From 652f63e63e51c90e115ceadc543ebbe0686ba70c Mon Sep 17 00:00:00 2001 From: Andrius Druzinis-Vitkus Date: Wed, 19 Apr 2017 18:00:22 +0200 Subject: [PATCH 01/46] add ability to specify custom rotation anchor point --- dist/L.Path.Transform-src.js | 19 +++++++++++++++---- dist/L.Path.Transform.js | 4 ++-- src/Path.Transform.js | 19 +++++++++++++++---- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/dist/L.Path.Transform-src.js b/dist/L.Path.Transform-src.js index 8923172..cc2e91c 100644 --- a/dist/L.Path.Transform-src.js +++ b/dist/L.Path.Transform-src.js @@ -867,6 +867,7 @@ L.Handler.PathTransform = L.Handler.extend({ options: { rotation: true, + rotationAnchor: null, scaling: true, uniformScaling: true, maxZoom: 22, @@ -1149,12 +1150,14 @@ L.Handler.PathTransform = L.Handler.extend({ this._path._transform(null); this._rect._transform(null); - this._transformPoints(this._path); - this._transformPoints(this._rect); - if (this.options.rotation) { this._handleLine._transform(null); - this._transformPoints(this._handleLine, this._angle, null, this._origin); + this._transformPoints(this._path, this._angle, null, this.options.rotationAnchor); + this._transformPoints(this._rect, this._angle, null, this.options.rotationAnchor); + this._transformPoints(this._handleLine, this._angle, null, this.options.rotationAnchor); + } else { + this._transformPoints(this._path); + this._transformPoints(this._rect); } }, @@ -1322,6 +1325,14 @@ L.Handler.PathTransform = L.Handler.extend({ * @return {L.LatLng} */ _getRotationOrigin: function() { + if (this.options.rotationAnchor) { + var anchor = this.options.rotationAnchor; + if (anchor.constructor === Array && anchor.length >= 2) { + return new L.LatLng(anchor[0], anchor[1]); + } else if (anchor instanceof L.LatLng) { + return anchor; + } + } var latlngs = this._rect._latlngs[0]; var lb = latlngs[0]; var rt = latlngs[2]; diff --git a/dist/L.Path.Transform.js b/dist/L.Path.Transform.js index c62c8a1..1a96412 100644 --- a/dist/L.Path.Transform.js +++ b/dist/L.Path.Transform.js @@ -3,11 +3,11 @@ * @author Alexander Milevski * @preserve */ -L.Path.include({_transform:function(t){if(this._renderer){if(t){this._renderer.transformPath(this,t)}else{this._renderer._resetTransformPath(this);this._update()}}return this},_onMouseClick:function(t){if(this.dragging&&this.dragging.moved()||this._map.dragging&&this._map.dragging.moved()){return}this._fireMouseEvent(t)}});L.Handler.PathDrag=L.Handler.extend({statics:{DRAGGING_CLS:"leaflet-path-draggable"},initialize:function(t){this._path=t;this._matrix=null;this._startPoint=null;this._dragStartPoint=null;this._mapDraggingWasEnabled=false},addHooks:function(){this._path.on("mousedown",this._onDragStart,this);this._path.options.className=this._path.options.className?this._path.options.className+" "+L.Handler.PathDrag.DRAGGING_CLS:L.Handler.PathDrag.DRAGGING_CLS;if(this._path._path){L.DomUtil.addClass(this._path._path,L.Handler.PathDrag.DRAGGING_CLS)}},removeHooks:function(){this._path.off("mousedown",this._onDragStart,this);this._path.options.className=this._path.options.className.replace(new RegExp("\\s+"+L.Handler.PathDrag.DRAGGING_CLS),"");if(this._path._path){L.DomUtil.removeClass(this._path._path,L.Handler.PathDrag.DRAGGING_CLS)}},moved:function(){return this._path._dragMoved},_onDragStart:function(t){var i=t.originalEvent._simulated?"touchstart":t.originalEvent.type;this._mapDraggingWasEnabled=false;this._startPoint=t.containerPoint.clone();this._dragStartPoint=t.containerPoint.clone();this._matrix=[1,0,0,1,0,0];L.DomEvent.stop(t.originalEvent);L.DomUtil.addClass(this._path._renderer._container,"leaflet-interactive");L.DomEvent.on(document,L.Draggable.MOVE[i],this._onDrag,this).on(document,L.Draggable.END[i],this._onDragEnd,this);if(this._path._map.dragging.enabled()){this._path._map.dragging.disable();this._mapDraggingWasEnabled=true}this._path._dragMoved=false;if(this._path._popup){this._path._popup._close()}this._replaceCoordGetters(t)},_onDrag:function(t){L.DomEvent.stop(t);var i=t.touches&&t.touches.length>=1?t.touches[0]:t;var a=this._path._map.mouseEventToContainerPoint(i);if(t.type==="touchmove"&&!this._path._dragMoved){var n=this._dragStartPoint.distanceTo(a);if(n<=this._path._map.options.tapTolerance){return}}var r=a.x;var e=a.y;var s=r-this._startPoint.x;var o=e-this._startPoint.y;if(s||o){if(!this._path._dragMoved){this._path._dragMoved=true;this._path.fire("dragstart",t);this._path.bringToFront()}this._matrix[4]+=s;this._matrix[5]+=o;this._startPoint.x=r;this._startPoint.y=e;this._path.fire("predrag",t);this._path._transform(this._matrix);this._path.fire("drag",t)}},_onDragEnd:function(t){var i=this._path._map.mouseEventToContainerPoint(t);var a=this.moved();if(a){this._transformPoints(this._matrix);this._path._updatePath();this._path._project();this._path._transform(null);L.DomEvent.stop(t)}L.DomEvent.off(document,"mousemove touchmove",this._onDrag,this).off(document,"mouseup touchend",this._onDragEnd,this);this._restoreCoordGetters();if(a){this._path.fire("dragend",{distance:Math.sqrt(L.LineUtil._sqDist(this._dragStartPoint,i))});var n=this._path._containsPoint;this._path._containsPoint=L.Util.falseFn;L.Util.requestAnimFrame(function(){L.DomEvent._skipped({type:"click"});this._path._containsPoint=n},this)}this._matrix=null;this._startPoint=null;this._dragStartPoint=null;this._path._dragMoved=false;if(this._mapDraggingWasEnabled){if(a)L.DomEvent._fakeStop({type:"click"});this._path._map.dragging.enable()}},_transformPoints:function(t,i){var a=this._path;var n,r,e;var s=L.point(t[4],t[5]);var o=a._map.options.crs;var h=o.transformation;var _=o.scale(a._map.getZoom());var l=o.projection;var d=h.untransform(s,_).subtract(h.untransform(L.point(0,0),_));var p=!i;a._bounds=new L.LatLngBounds;if(a._point){i=l.unproject(l.project(a._latlng)._add(d));if(p){a._latlng=i;a._point._add(s)}}else if(a._rings||a._parts){var g=a._rings||a._parts;var f=a._latlngs;i=i||f;if(!L.Util.isArray(f[0])){f=[f];i=[i]}for(n=0,r=g.length;n=1?t.touches[0]:t;var a=this._path._map.mouseEventToContainerPoint(i);if(t.type==="touchmove"&&!this._path._dragMoved){var n=this._dragStartPoint.distanceTo(a);if(n<=this._path._map.options.tapTolerance){return}}var r=a.x;var s=a.y;var e=r-this._startPoint.x;var o=s-this._startPoint.y;if(e||o){if(!this._path._dragMoved){this._path._dragMoved=true;this._path.fire("dragstart",t);this._path.bringToFront()}this._matrix[4]+=e;this._matrix[5]+=o;this._startPoint.x=r;this._startPoint.y=s;this._path.fire("predrag",t);this._path._transform(this._matrix);this._path.fire("drag",t)}},_onDragEnd:function(t){var i=this._path._map.mouseEventToContainerPoint(t);var a=this.moved();if(a){this._transformPoints(this._matrix);this._path._updatePath();this._path._project();this._path._transform(null);L.DomEvent.stop(t)}L.DomEvent.off(document,"mousemove touchmove",this._onDrag,this).off(document,"mouseup touchend",this._onDragEnd,this);this._restoreCoordGetters();if(a){this._path.fire("dragend",{distance:Math.sqrt(L.LineUtil._sqDist(this._dragStartPoint,i))});var n=this._path._containsPoint;this._path._containsPoint=L.Util.falseFn;L.Util.requestAnimFrame(function(){L.DomEvent._skipped({type:"click"});this._path._containsPoint=n},this)}this._matrix=null;this._startPoint=null;this._dragStartPoint=null;this._path._dragMoved=false;if(this._mapDraggingWasEnabled){if(a)L.DomEvent._fakeStop({type:"click"});this._path._map.dragging.enable()}},_transformPoints:function(t,i){var a=this._path;var n,r,s;var e=L.point(t[4],t[5]);var o=a._map.options.crs;var h=o.transformation;var _=o.scale(a._map.getZoom());var l=o.projection;var p=h.untransform(e,_).subtract(h.untransform(L.point(0,0),_));var d=!i;a._bounds=new L.LatLngBounds;if(a._point){i=l.unproject(l.project(a._latlng)._add(p));if(d){a._latlng=i;a._point._add(e)}}else if(a._rings||a._parts){var g=a._rings||a._parts;var f=a._latlngs;i=i||f;if(!L.Util.isArray(f[0])){f=[f];i=[i]}for(n=0,r=g.length;n * @license MIT * @preserve */ -L.LineUtil.pointOnLine=function(t,i,a){var n=1+a/t.distanceTo(i);return new L.Point(t.x+(i.x-t.x)*n,t.y+(i.y-t.y)*n)};L.Util.merge=function(){var t=1;var i,a;var n=arguments[t];function r(t){return Object.prototype.toString.call(t)==="[object Object]"}var e=arguments[0];while(n){n=arguments[t++];for(i in n){if(!n.hasOwnProperty(i)){continue}a=n[i];if(r(a)&&r(e[i])){e[i]=L.Util.merge(e[i],a)}else{e[i]=a}}}return e};L.Matrix=function(t,i,a,n,r,e){this._matrix=[t,i,a,n,r,e]};L.Matrix.prototype={transform:function(t){return this._transform(t.clone())},_transform:function(t){var i=this._matrix;var a=t.x,n=t.y;t.x=i[0]*a+i[1]*n+i[4];t.y=i[2]*a+i[3]*n+i[5];return t},untransform:function(t){var i=this._matrix;return new L.Point((t.x/i[0]-i[4])/i[0],(t.y/i[2]-i[5])/i[2])},clone:function(){var t=this._matrix;return new L.Matrix(t[0],t[1],t[2],t[3],t[4],t[5])},translate:function(t){if(t===undefined){return new L.Point(this._matrix[4],this._matrix[5])}var i,a;if(typeof t==="number"){i=a=t}else{i=t.x;a=t.y}return this._add(1,0,0,1,i,a)},scale:function(t,i){if(t===undefined){return new L.Point(this._matrix[0],this._matrix[3])}var a,n;i=i||L.point(0,0);if(typeof t==="number"){a=n=t}else{a=t.x;n=t.y}return this._add(a,0,0,n,i.x,i.y)._add(1,0,0,1,-i.x,-i.y)},rotate:function(t,i){var a=Math.cos(t);var n=Math.sin(t);i=i||new L.Point(0,0);return this._add(a,n,-n,a,i.x,i.y)._add(1,0,0,1,-i.x,-i.y)},flip:function(){this._matrix[1]*=-1;this._matrix[2]*=-1;return this},_add:function(t,i,a,n,r,e){var s=[[],[],[]];var o=this._matrix;var h=[[o[0],o[2],o[4]],[o[1],o[3],o[5]],[0,0,1]];var _=[[t,a,r],[i,n,e],[0,0,1]],l;if(t&&t instanceof L.Matrix){o=t._matrix;_=[[o[0],o[2],o[4]],[o[1],o[3],o[5]],[0,0,1]]}for(var d=0;d<3;d++){for(var p=0;p<3;p++){l=0;for(var g=0;g<3;g++){l+=h[d][g]*_[g][p]}s[d][p]=l}}this._matrix=[s[0][0],s[1][0],s[0][1],s[1][1],s[0][2],s[1][2]];return this}};L.matrix=function(t,i,a,n,r,e){return new L.Matrix(t,i,a,n,r,e)};L.PathTransform={};L.PathTransform.Handle=L.CircleMarker.extend({options:{className:"leaflet-path-transform-handler"},onAdd:function(t){L.CircleMarker.prototype.onAdd.call(this,t);if(this._path&&this.options.setCursor){this._path.style.cursor=L.PathTransform.Handle.CursorsByType[this.options.index]}}});L.PathTransform.Handle.CursorsByType=["nesw-resize","nwse-resize","nesw-resize","nwse-resize"];L.PathTransform.RotateHandle=L.PathTransform.Handle.extend({options:{className:"leaflet-path-transform-handler transform-handler--rotate"},onAdd:function(t){L.CircleMarker.prototype.onAdd.call(this,t);if(this._path&&this.options.setCursor){this._path.style.cursor="all-scroll"}}});L.Handler.PathTransform=L.Handler.extend({options:{rotation:true,scaling:true,uniformScaling:true,maxZoom:22,handlerOptions:{radius:5,fillColor:"#ffffff",color:"#202020",fillOpacity:1,weight:2,opacity:.7,setCursor:true},boundsOptions:{weight:1,opacity:1,dashArray:[3,3],fill:false},rotateHandleOptions:{weight:1,opacity:1,setCursor:true},handleLength:20,edgesCount:4,handleClass:L.PathTransform.Handle,rotateHandleClass:L.PathTransform.RotateHandle},initialize:function(t){this._path=t;this._map=null;this._activeMarker=null;this._originMarker=null;this._rotationMarker=null;this._rotationOrigin=null;this._scaleOrigin=null;this._angle=0;this._scale=L.point(1,1);this._initialDist=0;this._initialDistX=0;this._initialDistY=0;this._rotationStart=null;this._rotationOriginPt=null;this._matrix=new L.Matrix(1,0,0,1,0,0);this._projectedMatrix=new L.Matrix(1,0,0,1,0,0);this._handlersGroup=null;this._rect=null;this._handlers=[];this._handleLine=null},enable:function(t){if(this._path._map){this._map=this._path._map;if(t){this.setOptions(t)}L.Handler.prototype.enable.call(this)}},addHooks:function(){this._createHandlers();this._path.on("dragstart",this._onDragStart,this).on("dragend",this._onDragEnd,this)},removeHooks:function(){this._hideHandlers();this._path.off("dragstart",this._onDragStart,this).off("dragend",this._onDragEnd,this);this._handlersGroup=null;this._rect=null;this._handlers=[]},setOptions:function(t){var i=this._enabled;if(i){this.disable()}this.options=L.Util.merge({},L.Handler.PathTransform.prototype.options,t);if(i){this.enable()}return this},rotate:function(t,i){return this.transform(t,null,i)},scale:function(t,i){if(typeof t==="number"){t=L.point(t,t)}return this.transform(0,t,null,i)},transform:function(t,i,a,n){var r=this._path.getCenter();a=a||r;n=n||r;this._map=this._path._map;this._transformPoints(this._path,t,i,a,n);return this},_update:function(){var t=this._matrix;for(var i=0,a=this._handlers.length;i=0;i--){t.removeLayer(this._handlers[i])}this._createHandlers()},_transformGeometries:function(){this._path._transform(null);this._rect._transform(null);this._transformPoints(this._path);this._transformPoints(this._rect);if(this.options.rotation){this._handleLine._transform(null);this._transformPoints(this._handleLine,this._angle,null,this._origin)}},_getProjectedMatrix:function(t,i,a,n){var r=this._map;var e=r.getMaxZoom()||this.options.maxZoom;var s=L.matrix(1,0,0,1,0,0);var o;t=t||this._angle||0;i=i||this._scale||L.point(1,1);if(!(i.x===1&&i.y===1)){n=n||this._scaleOrigin;o=r.project(n,e);s=s._add(L.matrix(1,0,0,1,o.x,o.y))._add(L.matrix(i.x,0,0,i.y,0,0))._add(L.matrix(1,0,0,1,-o.x,-o.y))}if(t){a=a||this._rotationOrigin;o=r.project(a,e);s=s.rotate(t,o).flip()}return s},_transformPoint:function(t,i,a,n){return a.unproject(i.transform(a.project(t,n)),n)},_transformPoints:function(t,i,a,n,r){var e=t._map;var s=e.getMaxZoom()||this.options.maxZoom;var o,h;var _=this._projectedMatrix=this._getProjectedMatrix(i,a,n,r);if(t._point){t._latlng=this._transformPoint(t._latlng,_,e,s)}else if(t._rings||t._parts){var l=t._rings;var d=t._latlngs;t._bounds=new L.LatLngBounds;if(!L.Util.isArray(d[0])){d=[d]}for(o=0,h=l.length;o=0;i--){t.removeLayer(this._handlers[i])}this._createHandlers()},_transformGeometries:function(){this._path._transform(null);this._rect._transform(null);if(this.options.rotation){this._handleLine._transform(null);this._transformPoints(this._path,this._angle,null,this.options.rotationAnchor);this._transformPoints(this._rect,this._angle,null,this.options.rotationAnchor);this._transformPoints(this._handleLine,this._angle,null,this.options.rotationAnchor)}else{this._transformPoints(this._path);this._transformPoints(this._rect)}},_getProjectedMatrix:function(t,i,a,n){var r=this._map;var s=r.getMaxZoom()||this.options.maxZoom;var e=L.matrix(1,0,0,1,0,0);var o;t=t||this._angle||0;i=i||this._scale||L.point(1,1);if(!(i.x===1&&i.y===1)){n=n||this._scaleOrigin;o=r.project(n,s);e=e._add(L.matrix(1,0,0,1,o.x,o.y))._add(L.matrix(i.x,0,0,i.y,0,0))._add(L.matrix(1,0,0,1,-o.x,-o.y))}if(t){a=a||this._rotationOrigin;o=r.project(a,s);e=e.rotate(t,o).flip()}return e},_transformPoint:function(t,i,a,n){return a.unproject(i.transform(a.project(t,n)),n)},_transformPoints:function(t,i,a,n,r){var s=t._map;var e=s.getMaxZoom()||this.options.maxZoom;var o,h;var _=this._projectedMatrix=this._getProjectedMatrix(i,a,n,r);if(t._point){t._latlng=this._transformPoint(t._latlng,_,s,e)}else if(t._rings||t._parts){var l=t._rings;var p=t._latlngs;t._bounds=new L.LatLngBounds;if(!L.Util.isArray(p[0])){p=[p]}for(o=0,h=l.length;o=2){return new L.LatLng(t[0],t[1])}else if(t instanceof L.LatLng){return t}}var i=this._rect._latlngs[0];var a=i[0];var n=i[2];return new L.LatLng((a.lat+n.lat)/2,(a.lng+n.lng)/2)},_onRotateStart:function(t){var i=this._map;i.dragging.disable();this._originMarker=null;this._rotationOriginPt=i.latLngToLayerPoint(this._getRotationOrigin());this._rotationStart=t.layerPoint;this._initialMatrix=this._matrix.clone();this._angle=0;this._path._map.on("mousemove",this._onRotate,this).on("mouseup",this._onRotateEnd,this);this._cachePoints();this._path.fire("transformstart",{layer:this._path}).fire("rotatestart",{layer:this._path,rotation:0})},_onRotate:function(t){var i=t.layerPoint;var a=this._rotationStart;var n=this._rotationOriginPt;this._angle=Math.atan2(i.y-n.y,i.x-n.x)-Math.atan2(a.y-n.y,a.x-n.x);this._matrix=this._initialMatrix.clone().rotate(this._angle,n).flip();this._update();this._path.fire("rotate",{layer:this._path,rotation:this._angle})},_onRotateEnd:function(t){this._path._map.off("mousemove",this._onRotate,this).off("mouseup",this._onRotateEnd,this);this._apply();this._path.fire("rotateend",{layer:this._path,rotation:this._angle})},_onScaleStart:function(t){var i=t.target;var a=this._map;a.dragging.disable();this._activeMarker=i;this._originMarker=this._handlers[(i.options.index+2)%4];this._scaleOrigin=this._originMarker.getLatLng();this._initialMatrix=this._matrix.clone();this._cachePoints();this._map.on("mousemove",this._onScale,this).on("mouseup",this._onScaleEnd,this);this._initialDist=this._originMarker._point.distanceTo(this._activeMarker._point);this._initialDistX=this._originMarker._point.x-this._activeMarker._point.x;this._initialDistY=this._originMarker._point.y-this._activeMarker._point.y;this._path.fire("transformstart",{layer:this._path}).fire("scalestart",{layer:this._path,scale:L.point(1,1)});this._map.removeLayer(this._handleLine);this._map.removeLayer(this._rotationMarker)},_onScale:function(t){var i=this._originMarker._point;var a,n;if(this.options.uniformScaling){a=i.distanceTo(t.layerPoint)/this._initialDist;n=a}else{a=(i.x-t.layerPoint.x)/this._initialDistX;n=(i.y-t.layerPoint.y)/this._initialDistY}this._scale=new L.Point(a,n);this._matrix=this._initialMatrix.clone().scale(this._scale,i);this._update();this._path.fire("scale",{layer:this._path,scale:this._scale.clone()})},_onScaleEnd:function(t){this._map.off("mousemove",this._onScale,this).off("mouseup",this._onScaleEnd,this);this._map.addLayer(this._handleLine);this._map.addLayer(this._rotationMarker);this._apply();this._path.fire("scaleend",{layer:this._path,scale:this._scale.clone()})},_cachePoints:function(){this._handlersGroup.eachLayer(function(t){t.bringToFront()});for(var t=0,i=this._handlers.length;t= 2) { + return new L.LatLng(anchor[0], anchor[1]); + } else if (anchor instanceof L.LatLng) { + return anchor; + } + } var latlngs = this._rect._latlngs[0]; var lb = latlngs[0]; var rt = latlngs[2]; From f50bac9ea2da152f774144c9bd159c777d9269b4 Mon Sep 17 00:00:00 2001 From: Eero Kirjavainen Date: Sun, 4 Jun 2017 12:05:53 +0300 Subject: [PATCH 02/46] Safe removal of rotation handlers - skip removing handleline and rotationmarker if they are not defined (rotation is disabled) --- example/js/bundle.js | 2557 +++++++++++++++++++++-------------------- src/Path.Transform.js | 9 +- 2 files changed, 1288 insertions(+), 1278 deletions(-) diff --git a/example/js/bundle.js b/example/js/bundle.js index 342e707..9060ab8 100644 --- a/example/js/bundle.js +++ b/example/js/bundle.js @@ -1,262 +1,262 @@ (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;oOSM contributors' -}).addTo(map); - -//////////////////////////////////////////////////////////////////////////////// -function interpolateArr(array, insert) { - var res = []; - array.forEach(function(p, i, arr) { - res.push(p.concat()); - - if (i < arr.length - 1) { - var diff = [arr[i + 1][0] - p[0], arr[i + 1][1] - p[1]]; - for (var i = 1; i < insert; i++) { - res.push([p[0] + (diff[0] * i) / insert, p[1] + (diff[1] * i) / insert]); - } - } - }); - - return res; -} - -//////////////////////////////////////////////////////////////////////////////// -var polygon = global.polygon = new L.Polygon( - L.GeoJSON.coordsToLatLngs( - - // ~ 13 000 points - interpolateArr([ - [113.97697448730469, 22.403410892712124], - [113.98658752441405, 22.38373008592495], - [114.01268005371094, 22.369126397545887], - [114.02778625488281, 22.38563480185718], - [114.04701232910156, 22.395157990290755], - [114.06005859375, 22.413567638369805], - [114.06280517578125, 22.432609534876796], - [114.04838562011717, 22.444668051657157], - [114.04289245605469, 22.44847578656544], - [114.03259277343749, 22.444668051657157], - [114.01954650878906, 22.447206553211814], - [113.99620056152344, 22.436417600763114], - [113.98178100585938, 22.420549970290875], - [113.97697448730469, 22.403410892712124] - ], 1000) - ), { - color: '#f00', - interactive: true, - draggable: true, - transform: true - }).addTo(map); -//polygon.transform.enable(); - -var polyline = global.polyline = new L.Polyline( - L.GeoJSON.coordsToLatLngs([ - [114.14314270019531, 22.49479484975443], - [114.1534423828125, 22.485912942320958], - [114.15206909179688, 22.4732235144781], - [114.14932250976561, 22.459898363943893], - [114.15962219238281, 22.447206553211814], - [114.169921875, 22.447206553211814], - [114.19395446777344, 22.459898363943893], - [114.20631408691406, 22.46116748110935], - [114.21180725097655, 22.473858013487614], - [114.22416687011719, 22.471320000009992], - [114.23721313476562, 22.476395980457973], - [114.24201965332031, 22.49352604073722], - [114.2303466796875, 22.51572851830351], - [114.21798706054688, 22.524608511026262], - [114.20768737792969, 22.524608511026262], - [114.20768737792969, 22.536024805886974] - ]), { - weight: 15, - draggable: true, - transform: true - }) - .bindPopup("L.Polyline") - .addTo(map); -// polyline.transform.enable(); - - -var polygonWithHole = global.polygonWithHole = new L.Polygon( - [ - L.GeoJSON.coordsToLatLngs([ - [114.2749786376953, 22.412932863517717], - [114.28390502929688, 22.40087159030595], - [114.29008483886717, 22.38880927045556], - [114.30107116699219, 22.382460260815716], - [114.31892395019531, 22.391983666602783], - [114.32304382324219, 22.380555501421533], - [114.34295654296875, 22.372936203113838], - [114.334716796875, 22.384364994133303], - [114.33059692382812, 22.393888269511194], - [114.32167053222655, 22.40087159030595], - [114.32785034179688, 22.413567638369805], - [114.33197021484375, 22.42499308964722], - [114.32579040527344, 22.430705462748918], - [114.33197021484375, 22.43959090917266], - [114.33746337890624, 22.449110398886106], - [114.33540344238281, 22.461802035333992], - [114.32510375976562, 22.464340223177118], - [114.32922363281249, 22.472589012561954], - [114.32373046875, 22.477030464933307], - [114.31961059570312, 22.478933900916928], - [114.3017578125, 22.466243833549445], - [114.30244445800781, 22.457360094750083], - [114.29283142089844, 22.454821779075832], - [114.28390502929688, 22.45101421842269], - [114.2749786376953, 22.442764145001707], - [114.29077148437499, 22.428166659279615], - [114.27703857421875, 22.420549970290875], - [114.2749786376953, 22.412932863517717] - ]), - L.GeoJSON.coordsToLatLngs([ - [114.30107116699219, 22.43387890178297], - [114.29351806640625, 22.414202410321302], - [114.30587768554686, 22.408489358342635], - [114.32235717773438, 22.421184710331858], - [114.30107116699219, 22.43387890178297] - ]) - ], { - draggable: true, - transform: true - } - ) - .addTo(map); -//polygonWithHole.transform.enable(); - -var multiPolygon = global.multiPolygon = new L.Polygon([ - L.GeoJSON.coordsToLatLngs([ - [114.20562744140625, 22.32085984100593], - [114.21592712402344, 22.35261603551215], - [114.26467895507812, 22.351345926606957], - [114.2749786376953, 22.32403578584038], - [114.29214477539062, 22.32721165838893], - [114.3017578125, 22.311966810977616], - [114.29420471191406, 22.291002427735325], - [114.29351806640625, 22.272576585413475], - [114.28390502929688, 22.26177410097435], - [114.268798828125, 22.281472122783818], - [114.2749786376953, 22.294814367780518], - [114.26948547363281, 22.30243793590448], - [114.27017211914062, 22.31514295816939], - [114.2578125, 22.311966810977616], - [114.24751281738281, 22.299896792751927], - [114.24545288085938, 22.291002427735325], - [114.22966003417969, 22.307520083522476], - [114.22073364257812, 22.305614299837046], - [114.20562744140625, 22.32085984100593] - ]), - L.GeoJSON.coordsToLatLngs([ - [114.31549072265625, 22.33927931468312], - [114.32029724121094, 22.326576489662482], - [114.32991027832031, 22.326576489662482], - [114.33334350585938, 22.332292904091716], - [114.32304382324219, 22.3424548401465], - [114.31549072265625, 22.33927931468312] - ]), - L.GeoJSON.coordsToLatLngs([ - [114.27909851074219, 22.244615500323064], - [114.28115844726562, 22.251606295132948], - [114.28665161132812, 22.255419308858556], - [114.29969787597656, 22.26113863474449], - [114.2962646484375, 22.250970782750866], - [114.29489135742188, 22.24080219246335], - [114.29008483886717, 22.238895499613232], - [114.27909851074219, 22.244615500323064] - ]) -], { - draggable: true, - transform: true, - color: '#092' -}).bindPopup('MultiPolygon').addTo(map); -//multiPolygon.transform.enable(); - -var multiPolyline = global.multiPolyline = new L.Polyline([ - L.GeoJSON.coordsToLatLngs([ - [113.89869689941406, 22.399601921706953], - [113.89801025390625, 22.422454181709707], - [113.90350341796875, 22.43324421978117], - [113.90968322753906, 22.449110398886106], - [113.90693664550781, 22.478299425162852], - [113.9234161376953, 22.488450688325408], - [113.9337158203125, 22.483375149789623], - [113.9447021484375, 22.492257220085193], - [113.95225524902344, 22.51255695405145] - ]), - - L.GeoJSON.coordsToLatLngs([ - [113.8677978515625, 22.39261853713738], - [113.86917114257811, 22.42753195115699], - [113.9234161376953, 22.462436586653148], - [113.94813537597656, 22.473858013487614], - [113.9783477783203, 22.49923558968306], - [113.99688720703125, 22.51192263246886], - [114.01336669921875, 22.501138720300254], - [114.02503967285155, 22.508116641853675] - ]) -], { - draggable: true, - transform: true, - color: '#e90' -}).bindPopup('MultiPolyline').addTo(map); -// multiPolyline.transform.enable(); - -var layers = [polygon, polyline, multiPolyline, multiPolygon, polygonWithHole]; - -function update() { - L.Util.requestAnimFrame(function() { - var dragging = document.querySelector('#dragging').checked; - var scaling = document.querySelector('#scaling').checked; - var rotation = document.querySelector('#rotation').checked; - var uniform = document.querySelector('#uniform').checked; - - layers.forEach(function(layer) { - - if (layer.dragging) { - layer.dragging[dragging ? 'enable': 'disable'](); - } else { - layer.eachLayer(function(sublayer) { - sublayer.dragging[dragging ? 'enable': 'disable'](); - }); - } - - - layer.transform.setOptions({ - scaling: scaling, - rotation: rotation, - uniformScaling: uniform - }).enable(); - }); - }); -} - -[].slice.call(document.querySelectorAll('input[type=checkbox]')) -.forEach(function(checkbox) { - L.DomEvent.on(checkbox, 'change', update); -}); - -update(); +var L = require('leaflet'); +var DragHandler = require('../../index'); + +L.Icon.Default.imagePath = "http://cdn.leafletjs.com/leaflet-0.7/images"; + +//////////////////////////////////////////////////////////////////////////////// +var map = global.map = new L.Map('map', { + // crs: L.CRS.EPSG4326 // that was tested as well +}).setView([22.42658, 114.1952], 11); + +L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + attribution: '© ' + + 'OSM contributors' +}).addTo(map); + +//////////////////////////////////////////////////////////////////////////////// +function interpolateArr(array, insert) { + var res = []; + array.forEach(function(p, i, arr) { + res.push(p.concat()); + + if (i < arr.length - 1) { + var diff = [arr[i + 1][0] - p[0], arr[i + 1][1] - p[1]]; + for (var i = 1; i < insert; i++) { + res.push([p[0] + (diff[0] * i) / insert, p[1] + (diff[1] * i) / insert]); + } + } + }); + + return res; +} + +//////////////////////////////////////////////////////////////////////////////// +var polygon = global.polygon = new L.Polygon( + L.GeoJSON.coordsToLatLngs( + + // ~ 13 000 points + interpolateArr([ + [113.97697448730469, 22.403410892712124], + [113.98658752441405, 22.38373008592495], + [114.01268005371094, 22.369126397545887], + [114.02778625488281, 22.38563480185718], + [114.04701232910156, 22.395157990290755], + [114.06005859375, 22.413567638369805], + [114.06280517578125, 22.432609534876796], + [114.04838562011717, 22.444668051657157], + [114.04289245605469, 22.44847578656544], + [114.03259277343749, 22.444668051657157], + [114.01954650878906, 22.447206553211814], + [113.99620056152344, 22.436417600763114], + [113.98178100585938, 22.420549970290875], + [113.97697448730469, 22.403410892712124] + ], 1000) + ), { + color: '#f00', + interactive: true, + draggable: true, + transform: true + }).addTo(map); +//polygon.transform.enable(); + +var polyline = global.polyline = new L.Polyline( + L.GeoJSON.coordsToLatLngs([ + [114.14314270019531, 22.49479484975443], + [114.1534423828125, 22.485912942320958], + [114.15206909179688, 22.4732235144781], + [114.14932250976561, 22.459898363943893], + [114.15962219238281, 22.447206553211814], + [114.169921875, 22.447206553211814], + [114.19395446777344, 22.459898363943893], + [114.20631408691406, 22.46116748110935], + [114.21180725097655, 22.473858013487614], + [114.22416687011719, 22.471320000009992], + [114.23721313476562, 22.476395980457973], + [114.24201965332031, 22.49352604073722], + [114.2303466796875, 22.51572851830351], + [114.21798706054688, 22.524608511026262], + [114.20768737792969, 22.524608511026262], + [114.20768737792969, 22.536024805886974] + ]), { + weight: 15, + draggable: true, + transform: true + }) + .bindPopup("L.Polyline") + .addTo(map); +// polyline.transform.enable(); + + +var polygonWithHole = global.polygonWithHole = new L.Polygon( + [ + L.GeoJSON.coordsToLatLngs([ + [114.2749786376953, 22.412932863517717], + [114.28390502929688, 22.40087159030595], + [114.29008483886717, 22.38880927045556], + [114.30107116699219, 22.382460260815716], + [114.31892395019531, 22.391983666602783], + [114.32304382324219, 22.380555501421533], + [114.34295654296875, 22.372936203113838], + [114.334716796875, 22.384364994133303], + [114.33059692382812, 22.393888269511194], + [114.32167053222655, 22.40087159030595], + [114.32785034179688, 22.413567638369805], + [114.33197021484375, 22.42499308964722], + [114.32579040527344, 22.430705462748918], + [114.33197021484375, 22.43959090917266], + [114.33746337890624, 22.449110398886106], + [114.33540344238281, 22.461802035333992], + [114.32510375976562, 22.464340223177118], + [114.32922363281249, 22.472589012561954], + [114.32373046875, 22.477030464933307], + [114.31961059570312, 22.478933900916928], + [114.3017578125, 22.466243833549445], + [114.30244445800781, 22.457360094750083], + [114.29283142089844, 22.454821779075832], + [114.28390502929688, 22.45101421842269], + [114.2749786376953, 22.442764145001707], + [114.29077148437499, 22.428166659279615], + [114.27703857421875, 22.420549970290875], + [114.2749786376953, 22.412932863517717] + ]), + L.GeoJSON.coordsToLatLngs([ + [114.30107116699219, 22.43387890178297], + [114.29351806640625, 22.414202410321302], + [114.30587768554686, 22.408489358342635], + [114.32235717773438, 22.421184710331858], + [114.30107116699219, 22.43387890178297] + ]) + ], { + draggable: true, + transform: true + } + ) + .addTo(map); +//polygonWithHole.transform.enable(); + +var multiPolygon = global.multiPolygon = new L.Polygon([ + L.GeoJSON.coordsToLatLngs([ + [114.20562744140625, 22.32085984100593], + [114.21592712402344, 22.35261603551215], + [114.26467895507812, 22.351345926606957], + [114.2749786376953, 22.32403578584038], + [114.29214477539062, 22.32721165838893], + [114.3017578125, 22.311966810977616], + [114.29420471191406, 22.291002427735325], + [114.29351806640625, 22.272576585413475], + [114.28390502929688, 22.26177410097435], + [114.268798828125, 22.281472122783818], + [114.2749786376953, 22.294814367780518], + [114.26948547363281, 22.30243793590448], + [114.27017211914062, 22.31514295816939], + [114.2578125, 22.311966810977616], + [114.24751281738281, 22.299896792751927], + [114.24545288085938, 22.291002427735325], + [114.22966003417969, 22.307520083522476], + [114.22073364257812, 22.305614299837046], + [114.20562744140625, 22.32085984100593] + ]), + L.GeoJSON.coordsToLatLngs([ + [114.31549072265625, 22.33927931468312], + [114.32029724121094, 22.326576489662482], + [114.32991027832031, 22.326576489662482], + [114.33334350585938, 22.332292904091716], + [114.32304382324219, 22.3424548401465], + [114.31549072265625, 22.33927931468312] + ]), + L.GeoJSON.coordsToLatLngs([ + [114.27909851074219, 22.244615500323064], + [114.28115844726562, 22.251606295132948], + [114.28665161132812, 22.255419308858556], + [114.29969787597656, 22.26113863474449], + [114.2962646484375, 22.250970782750866], + [114.29489135742188, 22.24080219246335], + [114.29008483886717, 22.238895499613232], + [114.27909851074219, 22.244615500323064] + ]) +], { + draggable: true, + transform: true, + color: '#092' +}).bindPopup('MultiPolygon').addTo(map); +//multiPolygon.transform.enable(); + +var multiPolyline = global.multiPolyline = new L.Polyline([ + L.GeoJSON.coordsToLatLngs([ + [113.89869689941406, 22.399601921706953], + [113.89801025390625, 22.422454181709707], + [113.90350341796875, 22.43324421978117], + [113.90968322753906, 22.449110398886106], + [113.90693664550781, 22.478299425162852], + [113.9234161376953, 22.488450688325408], + [113.9337158203125, 22.483375149789623], + [113.9447021484375, 22.492257220085193], + [113.95225524902344, 22.51255695405145] + ]), + + L.GeoJSON.coordsToLatLngs([ + [113.8677978515625, 22.39261853713738], + [113.86917114257811, 22.42753195115699], + [113.9234161376953, 22.462436586653148], + [113.94813537597656, 22.473858013487614], + [113.9783477783203, 22.49923558968306], + [113.99688720703125, 22.51192263246886], + [114.01336669921875, 22.501138720300254], + [114.02503967285155, 22.508116641853675] + ]) +], { + draggable: true, + transform: true, + color: '#e90' +}).bindPopup('MultiPolyline').addTo(map); +// multiPolyline.transform.enable(); + +var layers = [polygon, polyline, multiPolyline, multiPolygon, polygonWithHole]; + +function update() { + L.Util.requestAnimFrame(function() { + var dragging = document.querySelector('#dragging').checked; + var scaling = document.querySelector('#scaling').checked; + var rotation = document.querySelector('#rotation').checked; + var uniform = document.querySelector('#uniform').checked; + + layers.forEach(function(layer) { + + if (layer.dragging) { + layer.dragging[dragging ? 'enable': 'disable'](); + } else { + layer.eachLayer(function(sublayer) { + sublayer.dragging[dragging ? 'enable': 'disable'](); + }); + } + + + layer.transform.setOptions({ + scaling: scaling, + rotation: rotation, + uniformScaling: uniform + }).enable(); + }); + }); +} + +[].slice.call(document.querySelectorAll('input[type=checkbox]')) +.forEach(function(checkbox) { + L.DomEvent.on(checkbox, 'change', update); +}); + +update(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"../../index":2,"leaflet":9}],2:[function(require,module,exports){ -var L = require('leaflet'); -require('leaflet-path-drag'); -require('./src/Util'); -require('./src/Matrix'); -require('./src/Path.Transform'); +var L = require('leaflet'); +require('leaflet-path-drag'); +require('./src/Util'); +require('./src/Matrix'); +require('./src/Path.Transform'); },{"./src/Matrix":10,"./src/Path.Transform":11,"./src/Util":12,"leaflet":9,"leaflet-path-drag":3}],3:[function(require,module,exports){ require('./src/SVG'); @@ -14081,1032 +14081,1037 @@ L.control.layers = function (baseLayers, overlays, options) { }(window, document)); },{}],10:[function(require,module,exports){ -/** - * @class L.Matrix - * - * @param {Number} a - * @param {Number} b - * @param {Number} c - * @param {Number} d - * @param {Number} e - * @param {Number} f - */ -L.Matrix = function(a, b, c, d, e, f) { - - /** - * @type {Array.} - */ - this._matrix = [a, b, c, d, e, f]; -}; - - -L.Matrix.prototype = { - - - /** - * @param {L.Point} point - * @return {L.Point} - */ - transform: function(point) { - return this._transform(point.clone()); - }, - - - /** - * Destructive - * - * [ x ] = [ a b tx ] [ x ] = [ a * x + b * y + tx ] - * [ y ] = [ c d ty ] [ y ] = [ c * x + d * y + ty ] - * - * @param {L.Point} point - * @return {L.Point} - */ - _transform: function(point) { - var matrix = this._matrix; - var x = point.x, y = point.y; - point.x = matrix[0] * x + matrix[1] * y + matrix[4]; - point.y = matrix[2] * x + matrix[3] * y + matrix[5]; - return point; - }, - - - /** - * @param {L.Point} point - * @return {L.Point} - */ - untransform: function (point) { - var matrix = this._matrix; - return new L.Point( - (point.x / matrix[0] - matrix[4]) / matrix[0], - (point.y / matrix[2] - matrix[5]) / matrix[2] - ); - }, - - - /** - * @return {L.Matrix} - */ - clone: function() { - var matrix = this._matrix; - return new L.Matrix( - matrix[0], matrix[1], matrix[2], - matrix[3], matrix[4], matrix[5] - ); - }, - - - /** - * @param {L.Point=|Number=} translate - * @return {L.Matrix|L.Point} - */ - translate: function(translate) { - if (translate === undefined) { - return new L.Point(this._matrix[4], this._matrix[5]); - } - - var translateX, translateY; - if (typeof translate === 'number') { - translateX = translateY = translate; - } else { - translateX = translate.x; - translateY = translate.y; - } - - return this._add(1, 0, 0, 1, translateX, translateY); - }, - - - /** - * @param {L.Point=|Number=} scale - * @return {L.Matrix|L.Point} - */ - scale: function(scale, origin) { - if (scale === undefined) { - return new L.Point(this._matrix[0], this._matrix[3]); - } - - var scaleX, scaleY; - origin = origin || L.point(0, 0); - if (typeof scale === 'number') { - scaleX = scaleY = scale; - } else { - scaleX = scale.x; - scaleY = scale.y; - } - - return this - ._add(scaleX, 0, 0, scaleY, origin.x, origin.y) - ._add(1, 0, 0, 1, -origin.x, -origin.y); - }, - - - /** - * m00 m01 x - m00 * x - m01 * y - * m10 m11 y - m10 * x - m11 * y - * @param {Number} angle - * @param {L.Point=} origin - * @return {L.Matrix} - */ - rotate: function(angle, origin) { - var cos = Math.cos(angle); - var sin = Math.sin(angle); - - origin = origin || new L.Point(0, 0); - - return this - ._add(cos, sin, -sin, cos, origin.x, origin.y) - ._add(1, 0, 0, 1, -origin.x, -origin.y); - }, - - - /** - * Invert rotation - * @return {L.Matrix} - */ - flip: function() { - this._matrix[1] *= -1; - this._matrix[2] *= -1; - return this; - }, - - - /** - * @param {Number|L.Matrix} a - * @param {Number} b - * @param {Number} c - * @param {Number} d - * @param {Number} e - * @param {Number} f - */ - _add: function(a, b, c, d, e, f) { - var result = [[], [], []]; - var src = this._matrix; - var m = [ - [src[0], src[2], src[4]], - [src[1], src[3], src[5]], - [ 0, 0, 1] - ]; - var other = [ - [a, c, e], - [b, d, f], - [0, 0, 1] - ], val; - - - if (a && a instanceof L.Matrix) { - src = a._matrix; - other = [ - [src[0], src[2], src[4]], - [src[1], src[3], src[5]], - [ 0, 0, 1]]; - } - - for (var i = 0; i < 3; i++) { - for (var j = 0; j < 3; j++) { - val = 0; - for (var k = 0; k < 3; k++) { - val += m[i][k] * other[k][j]; - } - result[i][j] = val; - } - } - - this._matrix = [ - result[0][0], result[1][0], result[0][1], - result[1][1], result[0][2], result[1][2] - ]; - return this; - } - - -}; - - -L.matrix = function(a, b, c, d, e, f) { - return new L.Matrix(a, b, c, d, e, f); -}; +/** + * @class L.Matrix + * + * @param {Number} a + * @param {Number} b + * @param {Number} c + * @param {Number} d + * @param {Number} e + * @param {Number} f + */ +L.Matrix = function(a, b, c, d, e, f) { + + /** + * @type {Array.} + */ + this._matrix = [a, b, c, d, e, f]; +}; + + +L.Matrix.prototype = { + + + /** + * @param {L.Point} point + * @return {L.Point} + */ + transform: function(point) { + return this._transform(point.clone()); + }, + + + /** + * Destructive + * + * [ x ] = [ a b tx ] [ x ] = [ a * x + b * y + tx ] + * [ y ] = [ c d ty ] [ y ] = [ c * x + d * y + ty ] + * + * @param {L.Point} point + * @return {L.Point} + */ + _transform: function(point) { + var matrix = this._matrix; + var x = point.x, y = point.y; + point.x = matrix[0] * x + matrix[1] * y + matrix[4]; + point.y = matrix[2] * x + matrix[3] * y + matrix[5]; + return point; + }, + + + /** + * @param {L.Point} point + * @return {L.Point} + */ + untransform: function (point) { + var matrix = this._matrix; + return new L.Point( + (point.x / matrix[0] - matrix[4]) / matrix[0], + (point.y / matrix[2] - matrix[5]) / matrix[2] + ); + }, + + + /** + * @return {L.Matrix} + */ + clone: function() { + var matrix = this._matrix; + return new L.Matrix( + matrix[0], matrix[1], matrix[2], + matrix[3], matrix[4], matrix[5] + ); + }, + + + /** + * @param {L.Point=|Number=} translate + * @return {L.Matrix|L.Point} + */ + translate: function(translate) { + if (translate === undefined) { + return new L.Point(this._matrix[4], this._matrix[5]); + } + + var translateX, translateY; + if (typeof translate === 'number') { + translateX = translateY = translate; + } else { + translateX = translate.x; + translateY = translate.y; + } + + return this._add(1, 0, 0, 1, translateX, translateY); + }, + + + /** + * @param {L.Point=|Number=} scale + * @return {L.Matrix|L.Point} + */ + scale: function(scale, origin) { + if (scale === undefined) { + return new L.Point(this._matrix[0], this._matrix[3]); + } + + var scaleX, scaleY; + origin = origin || L.point(0, 0); + if (typeof scale === 'number') { + scaleX = scaleY = scale; + } else { + scaleX = scale.x; + scaleY = scale.y; + } + + return this + ._add(scaleX, 0, 0, scaleY, origin.x, origin.y) + ._add(1, 0, 0, 1, -origin.x, -origin.y); + }, + + + /** + * m00 m01 x - m00 * x - m01 * y + * m10 m11 y - m10 * x - m11 * y + * @param {Number} angle + * @param {L.Point=} origin + * @return {L.Matrix} + */ + rotate: function(angle, origin) { + var cos = Math.cos(angle); + var sin = Math.sin(angle); + + origin = origin || new L.Point(0, 0); + + return this + ._add(cos, sin, -sin, cos, origin.x, origin.y) + ._add(1, 0, 0, 1, -origin.x, -origin.y); + }, + + + /** + * Invert rotation + * @return {L.Matrix} + */ + flip: function() { + this._matrix[1] *= -1; + this._matrix[2] *= -1; + return this; + }, + + + /** + * @param {Number|L.Matrix} a + * @param {Number} b + * @param {Number} c + * @param {Number} d + * @param {Number} e + * @param {Number} f + */ + _add: function(a, b, c, d, e, f) { + var result = [[], [], []]; + var src = this._matrix; + var m = [ + [src[0], src[2], src[4]], + [src[1], src[3], src[5]], + [ 0, 0, 1] + ]; + var other = [ + [a, c, e], + [b, d, f], + [0, 0, 1] + ], val; + + + if (a && a instanceof L.Matrix) { + src = a._matrix; + other = [ + [src[0], src[2], src[4]], + [src[1], src[3], src[5]], + [ 0, 0, 1]]; + } + + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 3; j++) { + val = 0; + for (var k = 0; k < 3; k++) { + val += m[i][k] * other[k][j]; + } + result[i][j] = val; + } + } + + this._matrix = [ + result[0][0], result[1][0], result[0][1], + result[1][1], result[0][2], result[1][2] + ]; + return this; + } + + +}; + + +L.matrix = function(a, b, c, d, e, f) { + return new L.Matrix(a, b, c, d, e, f); +}; },{}],11:[function(require,module,exports){ -/** - * @namespace - * @type {Object} - */ -L.PathTransform = {}; - - -/** - * Marker handler - * @extends {L.CircleMarker} - */ -L.PathTransform.Handle = L.CircleMarker.extend({ - options: { - className: 'leaflet-path-transform-handler' - }, - - onAdd: function (map) { - L.CircleMarker.prototype.onAdd.call(this, map); - if (this._path && this.options.setCursor) { // SVG/VML - this._path.style.cursor = L.PathTransform.Handle.CursorsByType[ - this.options.index - ]; - } - } -}); - - -/** - * @const - * @type {Array} - */ -L.PathTransform.Handle.CursorsByType = [ - 'nesw-resize', 'nwse-resize', 'nesw-resize', 'nwse-resize' -]; - - -/** - * @extends {L.Handler.PathTransform.Handle} - */ -L.PathTransform.RotateHandle = L.PathTransform.Handle.extend({ - options: { - className: 'leaflet-path-transform-handler transform-handler--rotate' - }, - - onAdd: function (map) { - L.CircleMarker.prototype.onAdd.call(this, map); - if (this._path && this.options.setCursor) { // SVG/VML - this._path.style.cursor = 'all-scroll'; - } - } -}); - -L.Handler.PathTransform = L.Handler.extend({ - - options: { - rotation: true, - scaling: true, - uniformScaling: true, - maxZoom: 22, - - // edge handlers - handlerOptions: { - radius: 5, - fillColor: '#ffffff', - color: '#202020', - fillOpacity: 1, - weight: 2, - opacity: 0.7, - setCursor: true - }, - - // rectangle - boundsOptions: { - weight: 1, - opacity: 1, - dashArray: [3, 3], - fill: false - }, - - // rotation handler - rotateHandleOptions: { - weight: 1, - opacity: 1, - setCursor: true - }, - // rotation handle length - handleLength: 20, - - // maybe I'll add skewing in the future - edgesCount: 4, - - handleClass: L.PathTransform.Handle, - rotateHandleClass: L.PathTransform.RotateHandle - }, - - - /** - * @class L.Handler.PathTransform - * @constructor - * @param {L.Path} path - */ - initialize: function(path) { - // references - this._path = path; - this._map = null; - - // handlers - this._activeMarker = null; - this._originMarker = null; - this._rotationMarker = null; - - // origins & temporary state - this._rotationOrigin = null; - this._scaleOrigin = null; - this._angle = 0; - this._scale = L.point(1, 1); - this._initialDist = 0; - this._initialDistX = 0; - this._initialDistY = 0; - this._rotationStart = null; - this._rotationOriginPt = null; - - // preview and transform matrix - this._matrix = new L.Matrix(1, 0, 0, 1, 0, 0); - this._projectedMatrix = new L.Matrix(1, 0, 0, 1, 0, 0); - - // ui elements - this._handlersGroup = null; - this._rect = null; - this._handlers = []; - this._handleLine = null; - }, - - - /** - * If the polygon is not rendered, you can transform it yourself - * in the coordinates, and do it properly. - * @param {Object=} options - */ - enable: function(options) { - if (this._path._map) { - this._map = this._path._map; - if (options) { - this.setOptions(options); - } - L.Handler.prototype.enable.call(this); - } - }, - - - /** - * Init interactions and handlers - */ - addHooks: function() { - this._createHandlers(); - this._path - .on('dragstart', this._onDragStart, this) - .on('dragend', this._onDragEnd, this); - }, - - - /** - * Remove handlers - */ - removeHooks: function() { - this._hideHandlers(); - this._path - .off('dragstart', this._onDragStart, this) - .off('dragend', this._onDragEnd, this); - this._handlersGroup = null; - this._rect = null; - this._handlers = []; - }, - - - /** - * Change editing options - * @param {Object} options - */ - setOptions: function(options) { - var enabled = this._enabled; - if (enabled) { - this.disable(); - } - - this.options = L.Util.merge({}, - L.Handler.PathTransform.prototype.options, - options); - - if (enabled) { - this.enable(); - } - - return this; - }, - - - /** - * @param {Number} angle - * @param {L.LatLng} origin - * @return {L.Handler.PathTransform} - */ - rotate: function(angle, origin) { - return this.transform(angle, null, origin); - }, - - - /** - * @param {L.Point|Number} scale - * @param {L.LatLng} origin - * @return {L.Handler.PathTransform} - */ - scale: function(scale, origin) { - if (typeof scale === 'number') { - scale = L.point(scale, scale); - } - return this.transform(0, scale, null, origin); - }, - - - /** - * @param {Number} angle - * @param {L.Point} scale - * @param {L.LatLng=} rotationOrigin - * @param {L.LatLng=} scaleOrigin - * @return {L.Handler.PathTransform} - */ - transform: function(angle, scale, rotationOrigin, scaleOrigin) { - var center = this._path.getCenter(); - rotationOrigin = rotationOrigin || center; - scaleOrigin = scaleOrigin || center; - this._map = this._path._map; - this._transformPoints(this._path, angle, scale, rotationOrigin, scaleOrigin); - return this; - }, - - - /** - * Update the polygon and handlers preview, no reprojection - */ - _update: function() { - var matrix = this._matrix; - - // update handlers - for (var i = 0, len = this._handlers.length; i < len; i++) { - var handler = this._handlers[i]; - if (handler !== this._originMarker) { - handler._point = matrix.transform(handler._initialPoint); - handler._updatePath(); - } - } - - matrix = matrix.clone().flip(); - - this._applyTransform(matrix); - this._path.fire('transform', { layer: this._path }); - }, - - - /** - * @param {L.Matrix} matrix - */ - _applyTransform: function(matrix) { - this._path._transform(matrix._matrix); - this._rect._transform(matrix._matrix); - - if (this.options.rotation) { - this._handleLine._transform(matrix._matrix); - } - }, - - - /** - * Apply final transformation - */ - _apply: function() { - //console.group('apply transform'); - var map = this._map; - var matrix = this._matrix.clone(); - var angle = this._angle; - var scale = this._scale.clone(); - - this._transformGeometries(); - - // update handlers - for (var i = 0, len = this._handlers.length; i < len; i++) { - var handler = this._handlers[i]; - handler._latlng = map.layerPointToLatLng(handler._point); - delete handler._initialPoint; - handler.redraw(); - } - - this._matrix = L.matrix(1, 0, 0, 1, 0, 0); - this._scale = L.point(1, 1); - this._angle = 0; - - this._updateHandlers(); - - map.dragging.enable(); - this._path.fire('transformed', { - matrix: matrix, - scale: scale, - rotation: angle, - // angle: angle * (180 / Math.PI), - layer: this._path - }); - // console.groupEnd('apply transform'); - }, - - - /** - * Recalculate rotation handlers position - */ - _updateHandlers: function() { - var handlersGroup = this._handlersGroup; - - this._rectShape = this._rect.toGeoJSON(); - - this._handlersGroup.removeLayer(this._handleLine); - this._handlersGroup.removeLayer(this._rotationMarker); - - this._handleLine = this._rotationMarker = null; - - for (var i = this._handlers.length - 1; i >= 0; i--) { - handlersGroup.removeLayer(this._handlers[i]); - } - - this._createHandlers(); - }, - - - /** - * Transform geometries separately - */ - _transformGeometries: function() { - this._path._transform(null); - this._rect._transform(null); - - this._transformPoints(this._path); - this._transformPoints(this._rect); - - if (this.options.rotation) { - this._handleLine._transform(null); - this._transformPoints(this._handleLine, this._angle, null, this._origin); - } - }, - - - /** - * @param {Number} angle - * @param {Number} scale - * @param {L.LatLng=} rotationOrigin - * @param {L.LatLng=} scaleOrigin - */ - _getProjectedMatrix: function(angle, scale, rotationOrigin, scaleOrigin) { - var map = this._map; - var zoom = map.getMaxZoom() || this.options.maxZoom; - var matrix = L.matrix(1, 0, 0, 1, 0, 0); - var origin; - - angle = angle || this._angle || 0; - scale = scale || this._scale || L.point(1, 1); - - if (!(scale.x === 1 && scale.y === 1)) { - scaleOrigin = scaleOrigin || this._scaleOrigin; - origin = map.project(scaleOrigin, zoom); - matrix = matrix - ._add(L.matrix(1, 0, 0, 1, origin.x, origin.y)) - ._add(L.matrix(scale.x, 0, 0, scale.y, 0, 0)) - ._add(L.matrix(1, 0, 0, 1, -origin.x, -origin.y)); - } - - if (angle) { - rotationOrigin = rotationOrigin || this._rotationOrigin; - origin = map.project(rotationOrigin, zoom); - matrix = matrix.rotate(angle, origin).flip(); - } - - return matrix; - }, - - - /** - * @param {L.LatLng} latlng - * @param {L.Matrix} matrix - * @param {L.Map} map - * @param {Number} zoom - * @return {L.LatLng} - */ - _transformPoint: function(latlng, matrix, map, zoom) { - return map.unproject(matrix.transform( - map.project(latlng, zoom)), zoom); - }, - - - /** - * Applies transformation, does it in one sweep for performance, - * so don't be surprised about the code repetition. - * - * @param {L.Path} path - * @param {Number=} angle - * @param {L.Point=} scale - * @param {L.LatLng=} rotationOrigin - * @param {L.LatLng=} scaleOrigin - */ - _transformPoints: function(path, angle, scale, rotationOrigin, scaleOrigin) { - var map = path._map; - var zoom = map.getMaxZoom() || this.options.maxZoom; - var i, len; - - var projectedMatrix = this._projectedMatrix = - this._getProjectedMatrix(angle, scale, rotationOrigin, scaleOrigin); - // console.time('transform'); - - // all shifts are in-place - if (path._point) { // L.Circle - path._latlng = this._transformPoint( - path._latlng, projectedMatrix, map, zoom); - } else if (path._rings || path._parts) { // everything else - var rings = path._rings; - var latlngs = path._latlngs; - path._bounds = new L.LatLngBounds(); - - if (!L.Util.isArray(latlngs[0])) { // polyline - latlngs = [latlngs]; - } - for (i = 0, len = rings.length; i < len; i++) { - for (var j = 0, jj = rings[i].length; j < jj; j++) { - latlngs[i][j] = this._transformPoint( - latlngs[i][j], projectedMatrix, map, zoom); - path._bounds.extend(latlngs[i][j]); - } - } - } - - path._reset(); - //console.timeEnd('transform'); - }, - - - /** - * Creates markers and handles - */ - _createHandlers: function() { - var map = this._map; - this._handlersGroup = this._handlersGroup || - new L.LayerGroup().addTo(map); - this._rect = this._rect || - this._getBoundingPolygon().addTo(this._handlersGroup); - - if (this.options.scaling) { - this._handlers = []; - for (var i = 0; i < this.options.edgesCount; i++) { - // TODO: add stretching - this._handlers.push( - this._createHandler(this._rect._latlngs[0][i], i * 2, i) - .addTo(this._handlersGroup)); - } - } - - // add bounds - if (this.options.rotation) { - //add rotation handler - this._createRotationHandlers(); - } - }, - - - /** - * Rotation marker and small connectin handle - */ - _createRotationHandlers: function() { - var map = this._map; - var latlngs = this._rect._latlngs[0]; - - var bottom = new L.LatLng( - (latlngs[0].lat + latlngs[3].lat) / 2, - (latlngs[0].lng + latlngs[3].lng) / 2); - // hehe, top is a reserved word - var topPoint = new L.LatLng( - (latlngs[1].lat + latlngs[2].lat) / 2, - (latlngs[1].lng + latlngs[2].lng) / 2); - - var handlerPosition = map.layerPointToLatLng( - L.LineUtil.pointOnLine( - map.latLngToLayerPoint(bottom), - map.latLngToLayerPoint(topPoint), - this.options.handleLength) - ); - - this._handleLine = new L.Polyline([topPoint, handlerPosition], - this.options.rotateHandleOptions).addTo(this._handlersGroup); - var RotateHandleClass = this.options.rotateHandleClass; - this._rotationMarker = new RotateHandleClass(handlerPosition, - this.options.handlerOptions) - .addTo(this._handlersGroup) - .on('mousedown', this._onRotateStart, this); - - this._rotationOrigin = new L.LatLng( - (topPoint.lat + bottom.lat) / 2, - (topPoint.lng + bottom.lng) / 2 - ); - - this._handlers.push(this._rotationMarker); - }, - - - /** - * @return {L.LatLng} - */ - _getRotationOrigin: function() { - var latlngs = this._rect._latlngs[0]; - var lb = latlngs[0]; - var rt = latlngs[2]; - - return new L.LatLng( - (lb.lat + rt.lat) / 2, - (lb.lng + rt.lng) / 2 - ); - }, - - - /** - * Secure the rotation origin - * @param {Event} evt - */ - _onRotateStart: function(evt) { - var map = this._map; - - map.dragging.disable(); - - this._originMarker = null; - this._rotationOriginPt = map.latLngToLayerPoint(this._getRotationOrigin()); - this._rotationStart = evt.layerPoint; - this._initialMatrix = this._matrix.clone(); - - this._angle = 0; - this._path._map - .on('mousemove', this._onRotate, this) - .on('mouseup', this._onRotateEnd, this); - - this._cachePoints(); - this._path - .fire('transformstart', { layer: this._path }) - .fire('rotatestart', { layer: this._path, rotation: 0 }); - }, - - - /** - * @param {Event} evt - */ - _onRotate: function(evt) { - var pos = evt.layerPoint; - var previous = this._rotationStart; - var origin = this._rotationOriginPt; - - // rotation step angle - this._angle = Math.atan2(pos.y - origin.y, pos.x - origin.x) - - Math.atan2(previous.y - origin.y, previous.x - origin.x); - - this._matrix = this._initialMatrix - .clone() - .rotate(this._angle, origin) - .flip(); - - this._update(); - this._path.fire('rotate', { layer: this._path, rotation: this._angle }); - }, - - - /** - * @param {Event} evt - */ - _onRotateEnd: function(evt) { - this._path._map - .off('mousemove', this._onRotate, this) - .off('mouseup', this._onRotateEnd, this); - - this._apply(); - this._path.fire('rotateend', { layer: this._path, rotation: this._angle }); - }, - - - /** - * @param {Event} evt - */ - _onScaleStart: function(evt) { - var marker = evt.target; - var map = this._map; - - map.dragging.disable(); - - this._activeMarker = marker; - - this._originMarker = this._handlers[(marker.options.index + 2) % 4]; - this._scaleOrigin = this._originMarker.getLatLng(); - - this._initialMatrix = this._matrix.clone(); - this._cachePoints(); - - this._map - .on('mousemove', this._onScale, this) - .on('mouseup', this._onScaleEnd, this); - this._initialDist = this._originMarker._point.distanceTo(this._activeMarker._point); - this._initialDistX = this._originMarker._point.x - this._activeMarker._point.x; - this._initialDistY = this._originMarker._point.y - this._activeMarker._point.y; - - this._path - .fire('transformstart', { layer: this._path }) - .fire('scalestart', { layer: this._path, scale: L.point(1, 1) }); - - this._map.removeLayer(this._handleLine); - this._map.removeLayer(this._rotationMarker); - - //this._handleLine = this._rotationMarker = null; - }, - - - /** - * @param {Event} evt - */ - _onScale: function(evt) { - var originPoint = this._originMarker._point; - var ratioX, ratioY; - if (this.options.uniformScaling) { - ratioX = originPoint.distanceTo(evt.layerPoint) / this._initialDist; - ratioY = ratioX; - } else { - ratioX = (originPoint.x - evt.layerPoint.x) / this._initialDistX; - ratioY = (originPoint.y - evt.layerPoint.y) / this._initialDistY; - } - - this._scale = new L.Point(ratioX, ratioY); - - // update matrix - this._matrix = this._initialMatrix - .clone() - .scale(this._scale, originPoint); - - this._update(); - this._path.fire('scale', { - layer: this._path, scale: this._scale.clone() }); - }, - - - /** - * Scaling complete - * @param {Event} evt - */ - _onScaleEnd: function(evt) { - this._map - .off('mousemove', this._onScale, this) - .off('mouseup', this._onScaleEnd, this); - - this._map.addLayer(this._handleLine); - this._map.addLayer(this._rotationMarker); - - this._apply(); - this._path.fire('scaleend', { - layer: this._path, scale: this._scale.clone() }); - }, - - - /** - * Cache current handlers positions - */ - _cachePoints: function() { - this._handlersGroup.eachLayer(function(layer) { - layer.bringToFront(); - }); - for (var i = 0, len = this._handlers.length; i < len; i++) { - var handler = this._handlers[i]; - handler._initialPoint = handler._point.clone(); - } - }, - - - /** - * Bounding polygon - * @return {L.Polygon} - */ - _getBoundingPolygon: function() { - if (this._rectShape) { - return L.GeoJSON.geometryToLayer( - this._rectShape, this.options.boundsOptions); - } else { - return new L.Rectangle( - this._path.getBounds(), this.options.boundsOptions); - } - }, - - - /** - * Create corner marker - * @param {L.LatLng} latlng - * @param {Number} type one of L.Handler.PathTransform.HandlerTypes - * @param {Number} index - * @return {L.Handler.PathTransform.Handle} - */ - _createHandler: function(latlng, type, index) { - var HandleClass = this.options.handleClass; - var marker = new HandleClass(latlng, - L.Util.extend({}, this.options.handlerOptions, { - className: 'leaflet-drag-transform-marker drag-marker--' + - index + ' drag-marker--' + type, - index: index, - type: type - }) - ); - - marker.on('mousedown', this._onScaleStart, this); - return marker; - }, - - - /** - * Hide(not remove) the handlers layer - */ - _hideHandlers: function() { - this._map.removeLayer(this._handlersGroup); - }, - - - /** - * Hide handlers and rectangle - */ - _onDragStart: function() { - this._hideHandlers(); - }, - - - /** - * Drag rectangle, re-create handlers - */ - _onDragEnd: function(evt) { - var rect = this._rect; - var matrix = (evt.layer ? evt.layer : this._path).dragging._matrix.slice(); - - if (!rect.dragging) { - rect.dragging = new L.Handler.PathDrag(rect); - } - rect.dragging.enable(); - this._map.addLayer(rect); - rect.dragging._transformPoints(matrix); - rect._updatePath(); - rect._project(); - rect.dragging.disable(); - - this._map.addLayer(this._handlersGroup); - this._updateHandlers(); - - this._path.fire('transformed', { - scale: L.point(1, 1), - rotation: 0, - matrix: L.matrix.apply(undefined, matrix), - translate: L.point(matrix[4], matrix[5]), - layer: this._path - }); - } -}); - - -L.Path.addInitHook(function() { - if (this.options.transform) { - this.transform = new L.Handler.PathTransform(this, this.options.transform); - } -}); +/** + * @namespace + * @type {Object} + */ +L.PathTransform = {}; + + +/** + * Marker handler + * @extends {L.CircleMarker} + */ +L.PathTransform.Handle = L.CircleMarker.extend({ + options: { + className: 'leaflet-path-transform-handler' + }, + + onAdd: function (map) { + L.CircleMarker.prototype.onAdd.call(this, map); + if (this._path && this.options.setCursor) { // SVG/VML + this._path.style.cursor = L.PathTransform.Handle.CursorsByType[ + this.options.index + ]; + } + } +}); + + +/** + * @const + * @type {Array} + */ +L.PathTransform.Handle.CursorsByType = [ + 'nesw-resize', 'nwse-resize', 'nesw-resize', 'nwse-resize' +]; + + +/** + * @extends {L.Handler.PathTransform.Handle} + */ +L.PathTransform.RotateHandle = L.PathTransform.Handle.extend({ + options: { + className: 'leaflet-path-transform-handler transform-handler--rotate' + }, + + onAdd: function (map) { + L.CircleMarker.prototype.onAdd.call(this, map); + if (this._path && this.options.setCursor) { // SVG/VML + this._path.style.cursor = 'all-scroll'; + } + } +}); + +L.Handler.PathTransform = L.Handler.extend({ + + options: { + rotation: true, + scaling: true, + uniformScaling: true, + maxZoom: 22, + + // edge handlers + handlerOptions: { + radius: 5, + fillColor: '#ffffff', + color: '#202020', + fillOpacity: 1, + weight: 2, + opacity: 0.7, + setCursor: true + }, + + // rectangle + boundsOptions: { + weight: 1, + opacity: 1, + dashArray: [3, 3], + fill: false + }, + + // rotation handler + rotateHandleOptions: { + weight: 1, + opacity: 1, + setCursor: true + }, + // rotation handle length + handleLength: 20, + + // maybe I'll add skewing in the future + edgesCount: 4, + + handleClass: L.PathTransform.Handle, + rotateHandleClass: L.PathTransform.RotateHandle + }, + + + /** + * @class L.Handler.PathTransform + * @constructor + * @param {L.Path} path + */ + initialize: function(path) { + // references + this._path = path; + this._map = null; + + // handlers + this._activeMarker = null; + this._originMarker = null; + this._rotationMarker = null; + + // origins & temporary state + this._rotationOrigin = null; + this._scaleOrigin = null; + this._angle = 0; + this._scale = L.point(1, 1); + this._initialDist = 0; + this._initialDistX = 0; + this._initialDistY = 0; + this._rotationStart = null; + this._rotationOriginPt = null; + + // preview and transform matrix + this._matrix = new L.Matrix(1, 0, 0, 1, 0, 0); + this._projectedMatrix = new L.Matrix(1, 0, 0, 1, 0, 0); + + // ui elements + this._handlersGroup = null; + this._rect = null; + this._handlers = []; + this._handleLine = null; + }, + + + /** + * If the polygon is not rendered, you can transform it yourself + * in the coordinates, and do it properly. + * @param {Object=} options + */ + enable: function(options) { + if (this._path._map) { + this._map = this._path._map; + if (options) { + this.setOptions(options); + } + L.Handler.prototype.enable.call(this); + } + }, + + + /** + * Init interactions and handlers + */ + addHooks: function() { + this._createHandlers(); + this._path + .on('dragstart', this._onDragStart, this) + .on('dragend', this._onDragEnd, this); + }, + + + /** + * Remove handlers + */ + removeHooks: function() { + this._hideHandlers(); + this._path + .off('dragstart', this._onDragStart, this) + .off('dragend', this._onDragEnd, this); + this._handlersGroup = null; + this._rect = null; + this._handlers = []; + }, + + + /** + * Change editing options + * @param {Object} options + */ + setOptions: function(options) { + var enabled = this._enabled; + if (enabled) { + this.disable(); + } + + this.options = L.Util.merge({}, + L.Handler.PathTransform.prototype.options, + options); + + if (enabled) { + this.enable(); + } + + return this; + }, + + + /** + * @param {Number} angle + * @param {L.LatLng} origin + * @return {L.Handler.PathTransform} + */ + rotate: function(angle, origin) { + return this.transform(angle, null, origin); + }, + + + /** + * @param {L.Point|Number} scale + * @param {L.LatLng} origin + * @return {L.Handler.PathTransform} + */ + scale: function(scale, origin) { + if (typeof scale === 'number') { + scale = L.point(scale, scale); + } + return this.transform(0, scale, null, origin); + }, + + + /** + * @param {Number} angle + * @param {L.Point} scale + * @param {L.LatLng=} rotationOrigin + * @param {L.LatLng=} scaleOrigin + * @return {L.Handler.PathTransform} + */ + transform: function(angle, scale, rotationOrigin, scaleOrigin) { + var center = this._path.getCenter(); + rotationOrigin = rotationOrigin || center; + scaleOrigin = scaleOrigin || center; + this._map = this._path._map; + this._transformPoints(this._path, angle, scale, rotationOrigin, scaleOrigin); + return this; + }, + + + /** + * Update the polygon and handlers preview, no reprojection + */ + _update: function() { + var matrix = this._matrix; + + // update handlers + for (var i = 0, len = this._handlers.length; i < len; i++) { + var handler = this._handlers[i]; + if (handler !== this._originMarker) { + handler._point = matrix.transform(handler._initialPoint); + handler._updatePath(); + } + } + + matrix = matrix.clone().flip(); + + this._applyTransform(matrix); + this._path.fire('transform', { layer: this._path }); + }, + + + /** + * @param {L.Matrix} matrix + */ + _applyTransform: function(matrix) { + this._path._transform(matrix._matrix); + this._rect._transform(matrix._matrix); + + if (this.options.rotation) { + this._handleLine._transform(matrix._matrix); + } + }, + + + /** + * Apply final transformation + */ + _apply: function() { + //console.group('apply transform'); + var map = this._map; + var matrix = this._matrix.clone(); + var angle = this._angle; + var scale = this._scale.clone(); + + this._transformGeometries(); + + // update handlers + for (var i = 0, len = this._handlers.length; i < len; i++) { + var handler = this._handlers[i]; + handler._latlng = map.layerPointToLatLng(handler._point); + delete handler._initialPoint; + handler.redraw(); + } + + this._matrix = L.matrix(1, 0, 0, 1, 0, 0); + this._scale = L.point(1, 1); + this._angle = 0; + + this._updateHandlers(); + + map.dragging.enable(); + this._path.fire('transformed', { + matrix: matrix, + scale: scale, + rotation: angle, + // angle: angle * (180 / Math.PI), + layer: this._path + }); + // console.groupEnd('apply transform'); + }, + + + /** + * Recalculate rotation handlers position + */ + _updateHandlers: function() { + var handlersGroup = this._handlersGroup; + + this._rectShape = this._rect.toGeoJSON(); + + if (this._handleLine) { + this._handlersGroup.removeLayer(this._handleLine); + } + + if (this._rotationMarker) { + this._handlersGroup.removeLayer(this._rotationMarker); + } + + this._handleLine = this._rotationMarker = null; + + for (var i = this._handlers.length - 1; i >= 0; i--) { + handlersGroup.removeLayer(this._handlers[i]); + } + + this._createHandlers(); + }, + + + /** + * Transform geometries separately + */ + _transformGeometries: function() { + this._path._transform(null); + this._rect._transform(null); + + this._transformPoints(this._path); + this._transformPoints(this._rect); + + if (this.options.rotation) { + this._handleLine._transform(null); + this._transformPoints(this._handleLine, this._angle, null, this._origin); + } + }, + + + /** + * @param {Number} angle + * @param {Number} scale + * @param {L.LatLng=} rotationOrigin + * @param {L.LatLng=} scaleOrigin + */ + _getProjectedMatrix: function(angle, scale, rotationOrigin, scaleOrigin) { + var map = this._map; + var zoom = map.getMaxZoom() || this.options.maxZoom; + var matrix = L.matrix(1, 0, 0, 1, 0, 0); + var origin; + + angle = angle || this._angle || 0; + scale = scale || this._scale || L.point(1, 1); + + if (!(scale.x === 1 && scale.y === 1)) { + scaleOrigin = scaleOrigin || this._scaleOrigin; + origin = map.project(scaleOrigin, zoom); + matrix = matrix + ._add(L.matrix(1, 0, 0, 1, origin.x, origin.y)) + ._add(L.matrix(scale.x, 0, 0, scale.y, 0, 0)) + ._add(L.matrix(1, 0, 0, 1, -origin.x, -origin.y)); + } + + if (angle) { + rotationOrigin = rotationOrigin || this._rotationOrigin; + origin = map.project(rotationOrigin, zoom); + matrix = matrix.rotate(angle, origin).flip(); + } + + return matrix; + }, + + + /** + * @param {L.LatLng} latlng + * @param {L.Matrix} matrix + * @param {L.Map} map + * @param {Number} zoom + * @return {L.LatLng} + */ + _transformPoint: function(latlng, matrix, map, zoom) { + return map.unproject(matrix.transform( + map.project(latlng, zoom)), zoom); + }, + + + /** + * Applies transformation, does it in one sweep for performance, + * so don't be surprised about the code repetition. + * + * @param {L.Path} path + * @param {Number=} angle + * @param {L.Point=} scale + * @param {L.LatLng=} rotationOrigin + * @param {L.LatLng=} scaleOrigin + */ + _transformPoints: function(path, angle, scale, rotationOrigin, scaleOrigin) { + var map = path._map; + var zoom = map.getMaxZoom() || this.options.maxZoom; + var i, len; + + var projectedMatrix = this._projectedMatrix = + this._getProjectedMatrix(angle, scale, rotationOrigin, scaleOrigin); + // console.time('transform'); + + // all shifts are in-place + if (path._point) { // L.Circle + path._latlng = this._transformPoint( + path._latlng, projectedMatrix, map, zoom); + } else if (path._rings || path._parts) { // everything else + var rings = path._rings; + var latlngs = path._latlngs; + path._bounds = new L.LatLngBounds(); + + if (!L.Util.isArray(latlngs[0])) { // polyline + latlngs = [latlngs]; + } + for (i = 0, len = rings.length; i < len; i++) { + for (var j = 0, jj = rings[i].length; j < jj; j++) { + latlngs[i][j] = this._transformPoint( + latlngs[i][j], projectedMatrix, map, zoom); + path._bounds.extend(latlngs[i][j]); + } + } + } + + path._reset(); + //console.timeEnd('transform'); + }, + + + /** + * Creates markers and handles + */ + _createHandlers: function() { + var map = this._map; + this._handlersGroup = this._handlersGroup || + new L.LayerGroup().addTo(map); + this._rect = this._rect || + this._getBoundingPolygon().addTo(this._handlersGroup); + + if (this.options.scaling) { + this._handlers = []; + for (var i = 0; i < this.options.edgesCount; i++) { + // TODO: add stretching + this._handlers.push( + this._createHandler(this._rect._latlngs[0][i], i * 2, i) + .addTo(this._handlersGroup)); + } + } + + // add bounds + if (this.options.rotation) { + //add rotation handler + this._createRotationHandlers(); + } + }, + + + /** + * Rotation marker and small connectin handle + */ + _createRotationHandlers: function() { + var map = this._map; + var latlngs = this._rect._latlngs[0]; + + var bottom = new L.LatLng( + (latlngs[0].lat + latlngs[3].lat) / 2, + (latlngs[0].lng + latlngs[3].lng) / 2); + // hehe, top is a reserved word + var topPoint = new L.LatLng( + (latlngs[1].lat + latlngs[2].lat) / 2, + (latlngs[1].lng + latlngs[2].lng) / 2); + + var handlerPosition = map.layerPointToLatLng( + L.LineUtil.pointOnLine( + map.latLngToLayerPoint(bottom), + map.latLngToLayerPoint(topPoint), + this.options.handleLength) + ); + + this._handleLine = new L.Polyline([topPoint, handlerPosition], + this.options.rotateHandleOptions).addTo(this._handlersGroup); + var RotateHandleClass = this.options.rotateHandleClass; + this._rotationMarker = new RotateHandleClass(handlerPosition, + this.options.handlerOptions) + .addTo(this._handlersGroup) + .on('mousedown', this._onRotateStart, this); + + this._rotationOrigin = new L.LatLng( + (topPoint.lat + bottom.lat) / 2, + (topPoint.lng + bottom.lng) / 2 + ); + + this._handlers.push(this._rotationMarker); + }, + + + /** + * @return {L.LatLng} + */ + _getRotationOrigin: function() { + var latlngs = this._rect._latlngs[0]; + var lb = latlngs[0]; + var rt = latlngs[2]; + + return new L.LatLng( + (lb.lat + rt.lat) / 2, + (lb.lng + rt.lng) / 2 + ); + }, + + + /** + * Secure the rotation origin + * @param {Event} evt + */ + _onRotateStart: function(evt) { + var map = this._map; + + map.dragging.disable(); + + this._originMarker = null; + this._rotationOriginPt = map.latLngToLayerPoint(this._getRotationOrigin()); + this._rotationStart = evt.layerPoint; + this._initialMatrix = this._matrix.clone(); + + this._angle = 0; + this._path._map + .on('mousemove', this._onRotate, this) + .on('mouseup', this._onRotateEnd, this); + + this._cachePoints(); + this._path + .fire('transformstart', { layer: this._path }) + .fire('rotatestart', { layer: this._path, rotation: 0 }); + }, + + + /** + * @param {Event} evt + */ + _onRotate: function(evt) { + var pos = evt.layerPoint; + var previous = this._rotationStart; + var origin = this._rotationOriginPt; + + // rotation step angle + this._angle = Math.atan2(pos.y - origin.y, pos.x - origin.x) - + Math.atan2(previous.y - origin.y, previous.x - origin.x); + + this._matrix = this._initialMatrix + .clone() + .rotate(this._angle, origin) + .flip(); + + this._update(); + this._path.fire('rotate', { layer: this._path, rotation: this._angle }); + }, + + + /** + * @param {Event} evt + */ + _onRotateEnd: function(evt) { + this._path._map + .off('mousemove', this._onRotate, this) + .off('mouseup', this._onRotateEnd, this); + + this._apply(); + this._path.fire('rotateend', { layer: this._path, rotation: this._angle }); + }, + + + /** + * @param {Event} evt + */ + _onScaleStart: function(evt) { + var marker = evt.target; + var map = this._map; + + map.dragging.disable(); + + this._activeMarker = marker; + + this._originMarker = this._handlers[(marker.options.index + 2) % 4]; + this._scaleOrigin = this._originMarker.getLatLng(); + + this._initialMatrix = this._matrix.clone(); + this._cachePoints(); + + this._map + .on('mousemove', this._onScale, this) + .on('mouseup', this._onScaleEnd, this); + this._initialDist = this._originMarker._point.distanceTo(this._activeMarker._point); + this._initialDistX = this._originMarker._point.x - this._activeMarker._point.x; + this._initialDistY = this._originMarker._point.y - this._activeMarker._point.y; + + this._path + .fire('transformstart', { layer: this._path }) + .fire('scalestart', { layer: this._path, scale: L.point(1, 1) }); + + this._map.removeLayer(this._handleLine); + this._map.removeLayer(this._rotationMarker); + + //this._handleLine = this._rotationMarker = null; + }, + + + /** + * @param {Event} evt + */ + _onScale: function(evt) { + var originPoint = this._originMarker._point; + var ratioX, ratioY; + if (this.options.uniformScaling) { + ratioX = originPoint.distanceTo(evt.layerPoint) / this._initialDist; + ratioY = ratioX; + } else { + ratioX = (originPoint.x - evt.layerPoint.x) / this._initialDistX; + ratioY = (originPoint.y - evt.layerPoint.y) / this._initialDistY; + } + + this._scale = new L.Point(ratioX, ratioY); + + // update matrix + this._matrix = this._initialMatrix + .clone() + .scale(this._scale, originPoint); + + this._update(); + this._path.fire('scale', { + layer: this._path, scale: this._scale.clone() }); + }, + + + /** + * Scaling complete + * @param {Event} evt + */ + _onScaleEnd: function(evt) { + this._map + .off('mousemove', this._onScale, this) + .off('mouseup', this._onScaleEnd, this); + + this._map.addLayer(this._handleLine); + this._map.addLayer(this._rotationMarker); + + this._apply(); + this._path.fire('scaleend', { + layer: this._path, scale: this._scale.clone() }); + }, + + + /** + * Cache current handlers positions + */ + _cachePoints: function() { + this._handlersGroup.eachLayer(function(layer) { + layer.bringToFront(); + }); + for (var i = 0, len = this._handlers.length; i < len; i++) { + var handler = this._handlers[i]; + handler._initialPoint = handler._point.clone(); + } + }, + + + /** + * Bounding polygon + * @return {L.Polygon} + */ + _getBoundingPolygon: function() { + if (this._rectShape) { + return L.GeoJSON.geometryToLayer( + this._rectShape, this.options.boundsOptions); + } else { + return new L.Rectangle( + this._path.getBounds(), this.options.boundsOptions); + } + }, + + + /** + * Create corner marker + * @param {L.LatLng} latlng + * @param {Number} type one of L.Handler.PathTransform.HandlerTypes + * @param {Number} index + * @return {L.Handler.PathTransform.Handle} + */ + _createHandler: function(latlng, type, index) { + var HandleClass = this.options.handleClass; + var marker = new HandleClass(latlng, + L.Util.extend({}, this.options.handlerOptions, { + className: 'leaflet-drag-transform-marker drag-marker--' + + index + ' drag-marker--' + type, + index: index, + type: type + }) + ); + + marker.on('mousedown', this._onScaleStart, this); + return marker; + }, + + + /** + * Hide(not remove) the handlers layer + */ + _hideHandlers: function() { + this._map.removeLayer(this._handlersGroup); + }, + + + /** + * Hide handlers and rectangle + */ + _onDragStart: function() { + this._hideHandlers(); + }, + + + /** + * Drag rectangle, re-create handlers + */ + _onDragEnd: function(evt) { + var rect = this._rect; + var matrix = (evt.layer ? evt.layer : this._path).dragging._matrix.slice(); + + if (!rect.dragging) { + rect.dragging = new L.Handler.PathDrag(rect); + } + rect.dragging.enable(); + this._map.addLayer(rect); + rect.dragging._transformPoints(matrix); + rect._updatePath(); + rect._project(); + rect.dragging.disable(); + + this._map.addLayer(this._handlersGroup); + this._updateHandlers(); + + this._path.fire('transformed', { + scale: L.point(1, 1), + rotation: 0, + matrix: L.matrix.apply(undefined, matrix), + translate: L.point(matrix[4], matrix[5]), + layer: this._path + }); + } +}); + + +L.Path.addInitHook(function() { + if (this.options.transform) { + this.transform = new L.Handler.PathTransform(this, this.options.transform); + } +}); },{}],12:[function(require,module,exports){ -/** - * Point on the line segment or its extention - * - * @param {L.Point} start - * @param {L.Point} final - * @param {Number} distPx - * @return {L.Point} - */ -L.LineUtil.pointOnLine = function(start, final, distPx) { - var ratio = 1 + distPx / start.distanceTo(final); - return new L.Point( - start.x + (final.x - start.x) * ratio, - start.y + (final.y - start.y) * ratio - ); -}; - - -/** - * Deep merge objects. - */ -L.Util.merge = function() { - var i = 1; - var key, val; - var obj = arguments[i]; - - function isObject(object) { - return Object.prototype.toString.call(object) === '[object Object]'; - } - - // make sure we don't modify source element and it's properties - // objects are passed by reference - var target = arguments[0]; - - while (obj) { - obj = arguments[i++]; - for (key in obj) { - if (!obj.hasOwnProperty(key)) { - continue; - } - - val = obj[key]; - - if (isObject(val) && isObject(target[key])){ - target[key] = L.Util.merge(target[key], val); - } else { - target[key] = val; - } - } - } - return target; -}; +/** + * Point on the line segment or its extention + * + * @param {L.Point} start + * @param {L.Point} final + * @param {Number} distPx + * @return {L.Point} + */ +L.LineUtil.pointOnLine = function(start, final, distPx) { + var ratio = 1 + distPx / start.distanceTo(final); + return new L.Point( + start.x + (final.x - start.x) * ratio, + start.y + (final.y - start.y) * ratio + ); +}; + + +/** + * Deep merge objects. + */ +L.Util.merge = function() { + var i = 1; + var key, val; + var obj = arguments[i]; + + function isObject(object) { + return Object.prototype.toString.call(object) === '[object Object]'; + } + + // make sure we don't modify source element and it's properties + // objects are passed by reference + var target = arguments[0]; + + while (obj) { + obj = arguments[i++]; + for (key in obj) { + if (!obj.hasOwnProperty(key)) { + continue; + } + + val = obj[key]; + + if (isObject(val) && isObject(target[key])){ + target[key] = L.Util.merge(target[key], val); + } else { + target[key] = val; + } + } + } + return target; +}; },{}]},{},[1]) -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJleGFtcGxlL2pzL2FwcC5qcyIsImluZGV4LmpzIiwibm9kZV9tb2R1bGVzL2xlYWZsZXQtcGF0aC1kcmFnL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL2xlYWZsZXQtcGF0aC1kcmFnL3NyYy9DYW52YXMuanMiLCJub2RlX21vZHVsZXMvbGVhZmxldC1wYXRoLWRyYWcvc3JjL1BhdGguRHJhZy5qcyIsIm5vZGVfbW9kdWxlcy9sZWFmbGV0LXBhdGgtZHJhZy9zcmMvUGF0aC5UcmFuc2Zvcm0uanMiLCJub2RlX21vZHVsZXMvbGVhZmxldC1wYXRoLWRyYWcvc3JjL1NWRy5WTUwuanMiLCJub2RlX21vZHVsZXMvbGVhZmxldC1wYXRoLWRyYWcvc3JjL1NWRy5qcyIsIm5vZGVfbW9kdWxlcy9sZWFmbGV0L2Rpc3QvbGVhZmxldC1zcmMuanMiLCJzcmMvTWF0cml4LmpzIiwic3JjL1BhdGguVHJhbnNmb3JtLmpzIiwic3JjL1V0aWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDeFBBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNMQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1BBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4VkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsOFpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzVNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDL3ZCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCJ2YXIgTCA9IHJlcXVpcmUoJ2xlYWZsZXQnKTtcbnZhciBEcmFnSGFuZGxlciA9IHJlcXVpcmUoJy4uLy4uL2luZGV4Jyk7XG5cbkwuSWNvbi5EZWZhdWx0LmltYWdlUGF0aCA9IFwiaHR0cDovL2Nkbi5sZWFmbGV0anMuY29tL2xlYWZsZXQtMC43L2ltYWdlc1wiO1xuXG4vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xudmFyIG1hcCA9IGdsb2JhbC5tYXAgPSBuZXcgTC5NYXAoJ21hcCcsIHtcbiAgLy8gY3JzOiBMLkNSUy5FUFNHNDMyNiAvLyB0aGF0IHdhcyB0ZXN0ZWQgYXMgd2VsbFxufSkuc2V0VmlldyhbMjIuNDI2NTgsIDExNC4xOTUyXSwgMTEpO1xuXG5MLnRpbGVMYXllcignaHR0cDovL3tzfS50aWxlLm9zbS5vcmcve3p9L3t4fS97eX0ucG5nJywge1xuICBhdHRyaWJ1dGlvbjogJyZjb3B5OyAnICtcbiAgICAnPGEgaHJlZj1cImh0dHA6Ly9vc20ub3JnL2NvcHlyaWdodFwiPk9TTTwvYT4gY29udHJpYnV0b3JzJ1xufSkuYWRkVG8obWFwKTtcblxuLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cbmZ1bmN0aW9uIGludGVycG9sYXRlQXJyKGFycmF5LCBpbnNlcnQpIHtcbiAgdmFyIHJlcyA9IFtdO1xuICBhcnJheS5mb3JFYWNoKGZ1bmN0aW9uKHAsIGksIGFycikge1xuICAgIHJlcy5wdXNoKHAuY29uY2F0KCkpO1xuXG4gICAgaWYgKGkgPCBhcnIubGVuZ3RoIC0gMSkge1xuICAgICAgdmFyIGRpZmYgPSBbYXJyW2kgKyAxXVswXSAtIHBbMF0sIGFycltpICsgMV1bMV0gLSBwWzFdXTtcbiAgICAgIGZvciAodmFyIGkgPSAxOyBpIDwgaW5zZXJ0OyBpKyspIHtcbiAgICAgICAgcmVzLnB1c2goW3BbMF0gKyAoZGlmZlswXSAqIGkpIC8gaW5zZXJ0LCBwWzFdICsgKGRpZmZbMV0gKiBpKSAvIGluc2VydF0pO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgcmV0dXJuIHJlcztcbn1cblxuLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cbnZhciBwb2x5Z29uID0gZ2xvYmFsLnBvbHlnb24gPSBuZXcgTC5Qb2x5Z29uKFxuICBMLkdlb0pTT04uY29vcmRzVG9MYXRMbmdzKFxuXG4gICAgLy8gfiAxMyAwMDAgcG9pbnRzXG4gICAgaW50ZXJwb2xhdGVBcnIoW1xuICAgICAgWzExMy45NzY5NzQ0ODczMDQ2OSwgMjIuNDAzNDEwODkyNzEyMTI0XSxcbiAgICAgIFsxMTMuOTg2NTg3NTI0NDE0MDUsIDIyLjM4MzczMDA4NTkyNDk1XSxcbiAgICAgIFsxMTQuMDEyNjgwMDUzNzEwOTQsIDIyLjM2OTEyNjM5NzU0NTg4N10sXG4gICAgICBbMTE0LjAyNzc4NjI1NDg4MjgxLCAyMi4zODU2MzQ4MDE4NTcxOF0sXG4gICAgICBbMTE0LjA0NzAxMjMyOTEwMTU2LCAyMi4zOTUxNTc5OTAyOTA3NTVdLFxuICAgICAgWzExNC4wNjAwNTg1OTM3NSwgICAgMjIuNDEzNTY3NjM4MzY5ODA1XSxcbiAgICAgIFsxMTQuMDYyODA1MTc1NzgxMjUsIDIyLjQzMjYwOTUzNDg3Njc5Nl0sXG4gICAgICBbMTE0LjA0ODM4NTYyMDExNzE3LCAyMi40NDQ2NjgwNTE2NTcxNTddLFxuICAgICAgWzExNC4wNDI4OTI0NTYwNTQ2OSwgMjIuNDQ4NDc1Nzg2NTY1NDRdLFxuICAgICAgWzExNC4wMzI1OTI3NzM0Mzc0OSwgMjIuNDQ0NjY4MDUxNjU3MTU3XSxcbiAgICAgIFsxMTQuMDE5NTQ2NTA4Nzg5MDYsIDIyLjQ0NzIwNjU1MzIxMTgxNF0sXG4gICAgICBbMTEzLjk5NjIwMDU2MTUyMzQ0LCAyMi40MzY0MTc2MDA3NjMxMTRdLFxuICAgICAgWzExMy45ODE3ODEwMDU4NTkzOCwgMjIuNDIwNTQ5OTcwMjkwODc1XSxcbiAgICAgIFsxMTMuOTc2OTc0NDg3MzA0NjksIDIyLjQwMzQxMDg5MjcxMjEyNF1cbiAgICBdLCAxMDAwKVxuICApLCB7XG4gICAgY29sb3I6ICcjZjAwJyxcbiAgICBpbnRlcmFjdGl2ZTogdHJ1ZSxcbiAgICBkcmFnZ2FibGU6IHRydWUsXG4gICAgdHJhbnNmb3JtOiB0cnVlXG4gIH0pLmFkZFRvKG1hcCk7XG4vL3BvbHlnb24udHJhbnNmb3JtLmVuYWJsZSgpO1xuXG52YXIgcG9seWxpbmUgPSBnbG9iYWwucG9seWxpbmUgPSBuZXcgTC5Qb2x5bGluZShcbiAgTC5HZW9KU09OLmNvb3Jkc1RvTGF0TG5ncyhbXG4gICAgWzExNC4xNDMxNDI3MDAxOTUzMSwgMjIuNDk0Nzk0ODQ5NzU0NDNdLFxuICAgIFsxMTQuMTUzNDQyMzgyODEyNSwgIDIyLjQ4NTkxMjk0MjMyMDk1OF0sXG4gICAgWzExNC4xNTIwNjkwOTE3OTY4OCwgMjIuNDczMjIzNTE0NDc4MV0sXG4gICAgWzExNC4xNDkzMjI1MDk3NjU2MSwgMjIuNDU5ODk4MzYzOTQzODkzXSxcbiAgICBbMTE0LjE1OTYyMjE5MjM4MjgxLCAyMi40NDcyMDY1NTMyMTE4MTRdLFxuICAgIFsxMTQuMTY5OTIxODc1LCAgICAgIDIyLjQ0NzIwNjU1MzIxMTgxNF0sXG4gICAgWzExNC4xOTM5NTQ0Njc3NzM0NCwgMjIuNDU5ODk4MzYzOTQzODkzXSxcbiAgICBbMTE0LjIwNjMxNDA4NjkxNDA2LCAyMi40NjExNjc0ODExMDkzNV0sXG4gICAgWzExNC4yMTE4MDcyNTA5NzY1NSwgMjIuNDczODU4MDEzNDg3NjE0XSxcbiAgICBbMTE0LjIyNDE2Njg3MDExNzE5LCAyMi40NzEzMjAwMDAwMDk5OTJdLFxuICAgIFsxMTQuMjM3MjEzMTM0NzY1NjIsIDIyLjQ3NjM5NTk4MDQ1Nzk3M10sXG4gICAgWzExNC4yNDIwMTk2NTMzMjAzMSwgMjIuNDkzNTI2MDQwNzM3MjJdLFxuICAgIFsxMTQuMjMwMzQ2Njc5Njg3NSwgIDIyLjUxNTcyODUxODMwMzUxXSxcbiAgICBbMTE0LjIxNzk4NzA2MDU0Njg4LCAyMi41MjQ2MDg1MTEwMjYyNjJdLFxuICAgIFsxMTQuMjA3Njg3Mzc3OTI5NjksIDIyLjUyNDYwODUxMTAyNjI2Ml0sXG4gICAgWzExNC4yMDc2ODczNzc5Mjk2OSwgMjIuNTM2MDI0ODA1ODg2OTc0XVxuICBdKSwge1xuICAgIHdlaWdodDogMTUsXG4gICAgZHJhZ2dhYmxlOiB0cnVlLFxuICAgIHRyYW5zZm9ybTogdHJ1ZVxuICB9KVxuICAuYmluZFBvcHVwKFwiTC5Qb2x5bGluZVwiKVxuICAuYWRkVG8obWFwKTtcbi8vIHBvbHlsaW5lLnRyYW5zZm9ybS5lbmFibGUoKTtcblxuXG52YXIgcG9seWdvbldpdGhIb2xlID0gZ2xvYmFsLnBvbHlnb25XaXRoSG9sZSA9IG5ldyBMLlBvbHlnb24oXG4gICAgW1xuICAgICAgTC5HZW9KU09OLmNvb3Jkc1RvTGF0TG5ncyhbXG4gICAgICAgIFsxMTQuMjc0OTc4NjM3Njk1MywgIDIyLjQxMjkzMjg2MzUxNzcxN10sXG4gICAgICAgIFsxMTQuMjgzOTA1MDI5Mjk2ODgsIDIyLjQwMDg3MTU5MDMwNTk1XSxcbiAgICAgICAgWzExNC4yOTAwODQ4Mzg4NjcxNywgMjIuMzg4ODA5MjcwNDU1NTZdLFxuICAgICAgICBbMTE0LjMwMTA3MTE2Njk5MjE5LCAyMi4zODI0NjAyNjA4MTU3MTZdLFxuICAgICAgICBbMTE0LjMxODkyMzk1MDE5NTMxLCAyMi4zOTE5ODM2NjY2MDI3ODNdLFxuICAgICAgICBbMTE0LjMyMzA0MzgyMzI0MjE5LCAyMi4zODA1NTU1MDE0MjE1MzNdLFxuICAgICAgICBbMTE0LjM0Mjk1NjU0Mjk2ODc1LCAyMi4zNzI5MzYyMDMxMTM4MzhdLFxuICAgICAgICBbMTE0LjMzNDcxNjc5Njg3NSwgICAyMi4zODQzNjQ5OTQxMzMzMDNdLFxuICAgICAgICBbMTE0LjMzMDU5NjkyMzgyODEyLCAyMi4zOTM4ODgyNjk1MTExOTRdLFxuICAgICAgICBbMTE0LjMyMTY3MDUzMjIyNjU1LCAyMi40MDA4NzE1OTAzMDU5NV0sXG4gICAgICAgIFsxMTQuMzI3ODUwMzQxNzk2ODgsIDIyLjQxMzU2NzYzODM2OTgwNV0sXG4gICAgICAgIFsxMTQuMzMxOTcwMjE0ODQzNzUsIDIyLjQyNDk5MzA4OTY0NzIyXSxcbiAgICAgICAgWzExNC4zMjU3OTA0MDUyNzM0NCwgMjIuNDMwNzA1NDYyNzQ4OTE4XSxcbiAgICAgICAgWzExNC4zMzE5NzAyMTQ4NDM3NSwgMjIuNDM5NTkwOTA5MTcyNjZdLFxuICAgICAgICBbMTE0LjMzNzQ2MzM3ODkwNjI0LCAyMi40NDkxMTAzOTg4ODYxMDZdLFxuICAgICAgICBbMTE0LjMzNTQwMzQ0MjM4MjgxLCAyMi40NjE4MDIwMzUzMzM5OTJdLFxuICAgICAgICBbMTE0LjMyNTEwMzc1OTc2NTYyLCAyMi40NjQzNDAyMjMxNzcxMThdLFxuICAgICAgICBbMTE0LjMyOTIyMzYzMjgxMjQ5LCAyMi40NzI1ODkwMTI1NjE5NTRdLFxuICAgICAgICBbMTE0LjMyMzczMDQ2ODc1LCAgICAyMi40NzcwMzA0NjQ5MzMzMDddLFxuICAgICAgICBbMTE0LjMxOTYxMDU5NTcwMzEyLCAyMi40Nzg5MzM5MDA5MTY5MjhdLFxuICAgICAgICBbMTE0LjMwMTc1NzgxMjUsICAgICAyMi40NjYyNDM4MzM1NDk0NDVdLFxuICAgICAgICBbMTE0LjMwMjQ0NDQ1ODAwNzgxLCAyMi40NTczNjAwOTQ3NTAwODNdLFxuICAgICAgICBbMTE0LjI5MjgzMTQyMDg5ODQ0LCAyMi40NTQ4MjE3NzkwNzU4MzJdLFxuICAgICAgICBbMTE0LjI4MzkwNTAyOTI5Njg4LCAyMi40NTEwMTQyMTg0MjI2OV0sXG4gICAgICAgIFsxMTQuMjc0OTc4NjM3Njk1MywgIDIyLjQ0Mjc2NDE0NTAwMTcwN10sXG4gICAgICAgIFsxMTQuMjkwNzcxNDg0Mzc0OTksIDIyLjQyODE2NjY1OTI3OTYxNV0sXG4gICAgICAgIFsxMTQuMjc3MDM4NTc0MjE4NzUsIDIyLjQyMDU0OTk3MDI5MDg3NV0sXG4gICAgICAgIFsxMTQuMjc0OTc4NjM3Njk1MywgIDIyLjQxMjkzMjg2MzUxNzcxN11cbiAgICAgIF0pLFxuICAgICAgTC5HZW9KU09OLmNvb3Jkc1RvTGF0TG5ncyhbXG4gICAgICAgIFsxMTQuMzAxMDcxMTY2OTkyMTksIDIyLjQzMzg3ODkwMTc4Mjk3XSxcbiAgICAgICAgWzExNC4yOTM1MTgwNjY0MDYyNSwgMjIuNDE0MjAyNDEwMzIxMzAyXSxcbiAgICAgICAgWzExNC4zMDU4Nzc2ODU1NDY4NiwgMjIuNDA4NDg5MzU4MzQyNjM1XSxcbiAgICAgICAgWzExNC4zMjIzNTcxNzc3MzQzOCwgMjIuNDIxMTg0NzEwMzMxODU4XSxcbiAgICAgICAgWzExNC4zMDEwNzExNjY5OTIxOSwgMjIuNDMzODc4OTAxNzgyOTddXG4gICAgICBdKVxuICAgIF0sIHtcbiAgICAgIGRyYWdnYWJsZTogdHJ1ZSxcbiAgICAgIHRyYW5zZm9ybTogdHJ1ZVxuICAgIH1cbiAgKVxuICAuYWRkVG8obWFwKTtcbi8vcG9seWdvbldpdGhIb2xlLnRyYW5zZm9ybS5lbmFibGUoKTtcblxudmFyIG11bHRpUG9seWdvbiA9IGdsb2JhbC5tdWx0aVBvbHlnb24gPSBuZXcgTC5Qb2x5Z29uKFtcbiAgTC5HZW9KU09OLmNvb3Jkc1RvTGF0TG5ncyhbXG4gICAgWzExNC4yMDU2Mjc0NDE0MDYyNSwgMjIuMzIwODU5ODQxMDA1OTNdLFxuICAgIFsxMTQuMjE1OTI3MTI0MDIzNDQsIDIyLjM1MjYxNjAzNTUxMjE1XSxcbiAgICBbMTE0LjI2NDY3ODk1NTA3ODEyLCAyMi4zNTEzNDU5MjY2MDY5NTddLFxuICAgIFsxMTQuMjc0OTc4NjM3Njk1MywgIDIyLjMyNDAzNTc4NTg0MDM4XSxcbiAgICBbMTE0LjI5MjE0NDc3NTM5MDYyLCAyMi4zMjcyMTE2NTgzODg5M10sXG4gICAgWzExNC4zMDE3NTc4MTI1LCAgICAgMjIuMzExOTY2ODEwOTc3NjE2XSxcbiAgICBbMTE0LjI5NDIwNDcxMTkxNDA2LCAyMi4yOTEwMDI0Mjc3MzUzMjVdLFxuICAgIFsxMTQuMjkzNTE4MDY2NDA2MjUsIDIyLjI3MjU3NjU4NTQxMzQ3NV0sXG4gICAgWzExNC4yODM5MDUwMjkyOTY4OCwgMjIuMjYxNzc0MTAwOTc0MzVdLFxuICAgIFsxMTQuMjY4Nzk4ODI4MTI1LCAgIDIyLjI4MTQ3MjEyMjc4MzgxOF0sXG4gICAgWzExNC4yNzQ5Nzg2Mzc2OTUzLCAgMjIuMjk0ODE0MzY3NzgwNTE4XSxcbiAgICBbMTE0LjI2OTQ4NTQ3MzYzMjgxLCAyMi4zMDI0Mzc5MzU5MDQ0OF0sXG4gICAgWzExNC4yNzAxNzIxMTkxNDA2MiwgMjIuMzE1MTQyOTU4MTY5MzldLFxuICAgIFsxMTQuMjU3ODEyNSwgICAgICAgIDIyLjMxMTk2NjgxMDk3NzYxNl0sXG4gICAgWzExNC4yNDc1MTI4MTczODI4MSwgMjIuMjk5ODk2NzkyNzUxOTI3XSxcbiAgICBbMTE0LjI0NTQ1Mjg4MDg1OTM4LCAyMi4yOTEwMDI0Mjc3MzUzMjVdLFxuICAgIFsxMTQuMjI5NjYwMDM0MTc5NjksIDIyLjMwNzUyMDA4MzUyMjQ3Nl0sXG4gICAgWzExNC4yMjA3MzM2NDI1NzgxMiwgMjIuMzA1NjE0Mjk5ODM3MDQ2XSxcbiAgICBbMTE0LjIwNTYyNzQ0MTQwNjI1LCAyMi4zMjA4NTk4NDEwMDU5M11cbiAgXSksXG4gIEwuR2VvSlNPTi5jb29yZHNUb0xhdExuZ3MoW1xuICAgIFsxMTQuMzE1NDkwNzIyNjU2MjUsIDIyLjMzOTI3OTMxNDY4MzEyXSxcbiAgICBbMTE0LjMyMDI5NzI0MTIxMDk0LCAyMi4zMjY1NzY0ODk2NjI0ODJdLFxuICAgIFsxMTQuMzI5OTEwMjc4MzIwMzEsIDIyLjMyNjU3NjQ4OTY2MjQ4Ml0sXG4gICAgWzExNC4zMzMzNDM1MDU4NTkzOCwgMjIuMzMyMjkyOTA0MDkxNzE2XSxcbiAgICBbMTE0LjMyMzA0MzgyMzI0MjE5LCAyMi4zNDI0NTQ4NDAxNDY1XSxcbiAgICBbMTE0LjMxNTQ5MDcyMjY1NjI1LCAyMi4zMzkyNzkzMTQ2ODMxMl1cbiAgXSksXG4gIEwuR2VvSlNPTi5jb29yZHNUb0xhdExuZ3MoW1xuICAgIFsxMTQuMjc5MDk4NTEwNzQyMTksIDIyLjI0NDYxNTUwMDMyMzA2NF0sXG4gICAgWzExNC4yODExNTg0NDcyNjU2MiwgMjIuMjUxNjA2Mjk1MTMyOTQ4XSxcbiAgICBbMTE0LjI4NjY1MTYxMTMyODEyLCAyMi4yNTU0MTkzMDg4NTg1NTZdLFxuICAgIFsxMTQuMjk5Njk3ODc1OTc2NTYsIDIyLjI2MTEzODYzNDc0NDQ5XSxcbiAgICBbMTE0LjI5NjI2NDY0ODQzNzUsICAyMi4yNTA5NzA3ODI3NTA4NjZdLFxuICAgIFsxMTQuMjk0ODkxMzU3NDIxODgsIDIyLjI0MDgwMjE5MjQ2MzM1XSxcbiAgICBbMTE0LjI5MDA4NDgzODg2NzE3LCAyMi4yMzg4OTU0OTk2MTMyMzJdLFxuICAgIFsxMTQuMjc5MDk4NTEwNzQyMTksIDIyLjI0NDYxNTUwMDMyMzA2NF1cbiAgXSlcbl0sIHtcbiAgZHJhZ2dhYmxlOiB0cnVlLFxuICB0cmFuc2Zvcm06IHRydWUsXG4gIGNvbG9yOiAnIzA5Midcbn0pLmJpbmRQb3B1cCgnTXVsdGlQb2x5Z29uJykuYWRkVG8obWFwKTtcbi8vbXVsdGlQb2x5Z29uLnRyYW5zZm9ybS5lbmFibGUoKTtcblxudmFyIG11bHRpUG9seWxpbmUgPSBnbG9iYWwubXVsdGlQb2x5bGluZSA9IG5ldyBMLlBvbHlsaW5lKFtcbiAgTC5HZW9KU09OLmNvb3Jkc1RvTGF0TG5ncyhbXG4gICAgWzExMy44OTg2OTY4OTk0MTQwNiwgMjIuMzk5NjAxOTIxNzA2OTUzXSxcbiAgICBbMTEzLjg5ODAxMDI1MzkwNjI1LCAyMi40MjI0NTQxODE3MDk3MDddLFxuICAgIFsxMTMuOTAzNTAzNDE3OTY4NzUsIDIyLjQzMzI0NDIxOTc4MTE3XSxcbiAgICBbMTEzLjkwOTY4MzIyNzUzOTA2LCAyMi40NDkxMTAzOTg4ODYxMDZdLFxuICAgIFsxMTMuOTA2OTM2NjQ1NTA3ODEsIDIyLjQ3ODI5OTQyNTE2Mjg1Ml0sXG4gICAgWzExMy45MjM0MTYxMzc2OTUzLCAgMjIuNDg4NDUwNjg4MzI1NDA4XSxcbiAgICBbMTEzLjkzMzcxNTgyMDMxMjUsICAyMi40ODMzNzUxNDk3ODk2MjNdLFxuICAgIFsxMTMuOTQ0NzAyMTQ4NDM3NSwgIDIyLjQ5MjI1NzIyMDA4NTE5M10sXG4gICAgWzExMy45NTIyNTUyNDkwMjM0NCwgMjIuNTEyNTU2OTU0MDUxNDVdXG4gIF0pLFxuXG4gIEwuR2VvSlNPTi5jb29yZHNUb0xhdExuZ3MoW1xuICAgIFsxMTMuODY3Nzk3ODUxNTYyNSwgIDIyLjM5MjYxODUzNzEzNzM4XSxcbiAgICBbMTEzLjg2OTE3MTE0MjU3ODExLCAyMi40Mjc1MzE5NTExNTY5OV0sXG4gICAgWzExMy45MjM0MTYxMzc2OTUzLCAgMjIuNDYyNDM2NTg2NjUzMTQ4XSxcbiAgICBbMTEzLjk0ODEzNTM3NTk3NjU2LCAyMi40NzM4NTgwMTM0ODc2MTRdLFxuICAgIFsxMTMuOTc4MzQ3Nzc4MzIwMywgIDIyLjQ5OTIzNTU4OTY4MzA2XSxcbiAgICBbMTEzLjk5Njg4NzIwNzAzMTI1LCAyMi41MTE5MjI2MzI0Njg4Nl0sXG4gICAgWzExNC4wMTMzNjY2OTkyMTg3NSwgMjIuNTAxMTM4NzIwMzAwMjU0XSxcbiAgICBbMTE0LjAyNTAzOTY3Mjg1MTU1LCAyMi41MDgxMTY2NDE4NTM2NzVdXG4gIF0pXG5dLCB7XG4gIGRyYWdnYWJsZTogdHJ1ZSxcbiAgdHJhbnNmb3JtOiB0cnVlLFxuICBjb2xvcjogJyNlOTAnXG59KS5iaW5kUG9wdXAoJ011bHRpUG9seWxpbmUnKS5hZGRUbyhtYXApO1xuLy8gbXVsdGlQb2x5bGluZS50cmFuc2Zvcm0uZW5hYmxlKCk7XG5cbnZhciBsYXllcnMgPSBbcG9seWdvbiwgcG9seWxpbmUsIG11bHRpUG9seWxpbmUsIG11bHRpUG9seWdvbiwgcG9seWdvbldpdGhIb2xlXTtcblxuZnVuY3Rpb24gdXBkYXRlKCkge1xuICBMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbigpIHtcbiAgICB2YXIgZHJhZ2dpbmcgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjZHJhZ2dpbmcnKS5jaGVja2VkO1xuICAgIHZhciBzY2FsaW5nID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI3NjYWxpbmcnKS5jaGVja2VkO1xuICAgIHZhciByb3RhdGlvbiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJyNyb3RhdGlvbicpLmNoZWNrZWQ7XG4gICAgdmFyIHVuaWZvcm0gID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI3VuaWZvcm0nKS5jaGVja2VkO1xuXG4gICAgbGF5ZXJzLmZvckVhY2goZnVuY3Rpb24obGF5ZXIpIHtcblxuICAgICAgaWYgKGxheWVyLmRyYWdnaW5nKSB7XG4gICAgICAgIGxheWVyLmRyYWdnaW5nW2RyYWdnaW5nID8gJ2VuYWJsZSc6ICdkaXNhYmxlJ10oKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxheWVyLmVhY2hMYXllcihmdW5jdGlvbihzdWJsYXllcikge1xuICAgICAgICAgIHN1YmxheWVyLmRyYWdnaW5nW2RyYWdnaW5nID8gJ2VuYWJsZSc6ICdkaXNhYmxlJ10oKTtcbiAgICAgICAgfSk7XG4gICAgICB9XG5cblxuICAgICAgbGF5ZXIudHJhbnNmb3JtLnNldE9wdGlvbnMoe1xuICAgICAgICBzY2FsaW5nOiBzY2FsaW5nLFxuICAgICAgICByb3RhdGlvbjogcm90YXRpb24sXG4gICAgICAgIHVuaWZvcm1TY2FsaW5nOiB1bmlmb3JtXG4gICAgICB9KS5lbmFibGUoKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cbltdLnNsaWNlLmNhbGwoZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnaW5wdXRbdHlwZT1jaGVja2JveF0nKSlcbi5mb3JFYWNoKGZ1bmN0aW9uKGNoZWNrYm94KSB7XG4gIEwuRG9tRXZlbnQub24oY2hlY2tib3gsICdjaGFuZ2UnLCB1cGRhdGUpO1xufSk7XG5cbnVwZGF0ZSgpO1xuIiwidmFyIEwgPSByZXF1aXJlKCdsZWFmbGV0Jyk7XG5yZXF1aXJlKCdsZWFmbGV0LXBhdGgtZHJhZycpO1xucmVxdWlyZSgnLi9zcmMvVXRpbCcpO1xucmVxdWlyZSgnLi9zcmMvTWF0cml4Jyk7XG5yZXF1aXJlKCcuL3NyYy9QYXRoLlRyYW5zZm9ybScpO1xuIiwicmVxdWlyZSgnLi9zcmMvU1ZHJyk7XG5yZXF1aXJlKCcuL3NyYy9TVkcuVk1MJyk7XG5yZXF1aXJlKCcuL3NyYy9DYW52YXMnKTtcbnJlcXVpcmUoJy4vc3JjL1BhdGguVHJhbnNmb3JtJyk7XG5yZXF1aXJlKCcuL3NyYy9QYXRoLkRyYWcnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBMLlBhdGguRHJhZztcbiIsIkwuVXRpbC50cnVlRm4gPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHRydWU7XG59O1xuXG5MLkNhbnZhcy5pbmNsdWRlKHtcblxuICAvKipcbiAgICogRG8gbm90aGluZ1xuICAgKiBAcGFyYW0gIHtMLlBhdGh9IGxheWVyXG4gICAqL1xuICBfcmVzZXRUcmFuc2Zvcm1QYXRoOiBmdW5jdGlvbihsYXllcikge1xuICAgIGlmICghdGhpcy5fY29udGFpbmVyQ29weSkgcmV0dXJuO1xuXG4gICAgZGVsZXRlIHRoaXMuX2NvbnRhaW5lckNvcHk7XG5cbiAgICBpZiAobGF5ZXIuX2NvbnRhaW5zUG9pbnRfKSB7XG4gICAgICBsYXllci5fY29udGFpbnNQb2ludCA9IGxheWVyLl9jb250YWluc1BvaW50XztcbiAgICAgIGRlbGV0ZSBsYXllci5fY29udGFpbnNQb2ludF87XG5cbiAgICAgIHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuICAgIH1cbiAgfSxcblxuXG4gIC8qKlxuICAgKiBBbGdvcml0aG0gb3V0bGluZTpcbiAgICpcbiAgICogMS4gcHJlLXRyYW5zZm9ybSAtIGNsZWFyIHRoZSBwYXRoIG91dCBvZiB0aGUgY2FudmFzLCBjb3B5IGNhbnZhcyBzdGF0ZVxuICAgKiAyLiBhdCBldmVyeSBmcmFtZTpcbiAgICogICAgMi4xLiBzYXZlXG4gICAqICAgIDIuMi4gcmVkcmF3IHRoZSBjYW52YXMgZnJvbSBzYXZlZCBvbmVcbiAgICogICAgMi4zLiB0cmFuc2Zvcm1cbiAgICogICAgMi40LiBkcmF3IHBhdGhcbiAgICogICAgMi41LiByZXN0b3JlXG4gICAqIDMuIFJlcGVhdFxuICAgKlxuICAgKiBAcGFyYW0gIHtMLlBhdGh9ICAgICAgICAgbGF5ZXJcbiAgICogQHBhcmFtICB7QXJyYXkuPE51bWJlcj59IG1hdHJpeFxuICAgKi9cbiAgdHJhbnNmb3JtUGF0aDogZnVuY3Rpb24obGF5ZXIsIG1hdHJpeCkge1xuICAgIHZhciBjb3B5ICAgPSB0aGlzLl9jb250YWluZXJDb3B5O1xuICAgIHZhciBjdHggICAgPSB0aGlzLl9jdHgsIGNvcHlDdHg7XG4gICAgdmFyIG0gICAgICA9IEwuQnJvd3Nlci5yZXRpbmEgPyAyIDogMTtcbiAgICB2YXIgYm91bmRzID0gdGhpcy5fYm91bmRzO1xuICAgIHZhciBzaXplICAgPSBib3VuZHMuZ2V0U2l6ZSgpO1xuICAgIHZhciBwb3MgICAgPSBib3VuZHMubWluO1xuXG4gICAgaWYgKCFjb3B5KSB7IC8vIGdldCBjb3B5IG9mIGFsbCByZW5kZXJlZCBsYXllcnNcbiAgICAgIGNvcHkgPSB0aGlzLl9jb250YWluZXJDb3B5ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgICBjb3B5Q3R4ID0gY29weS5nZXRDb250ZXh0KCcyZCcpO1xuICAgICAgLy8gZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChjb3B5KTtcblxuICAgICAgY29weS53aWR0aCAgPSBtICogc2l6ZS54O1xuICAgICAgY29weS5oZWlnaHQgPSBtICogc2l6ZS55O1xuXG4gICAgICB0aGlzLl9yZW1vdmVQYXRoKGxheWVyKTtcbiAgICAgIHRoaXMuX3JlZHJhdygpO1xuXG4gICAgICBjb3B5Q3R4LnRyYW5zbGF0ZShtICogYm91bmRzLm1pbi54LCBtICogYm91bmRzLm1pbi55KTtcbiAgICAgIGNvcHlDdHguZHJhd0ltYWdlKHRoaXMuX2NvbnRhaW5lciwgMCwgMCk7XG4gICAgICB0aGlzLl9pbml0UGF0aChsYXllcik7XG5cbiAgICAgIC8vIGF2b2lkIGZsaWNrZXJpbmcgYmVjYXVzZSBvZiB0aGUgJ21vdXNlb3ZlcidzXG4gICAgICBsYXllci5fY29udGFpbnNQb2ludF8gPSBsYXllci5fY29udGFpbnNQb2ludDtcbiAgICAgIGxheWVyLl9jb250YWluc1BvaW50ID0gTC5VdGlsLnRydWVGbjtcbiAgICB9XG5cbiAgICBjdHguc2F2ZSgpO1xuICAgIGN0eC5jbGVhclJlY3QocG9zLngsIHBvcy55LCBzaXplLnggKiBtLCBzaXplLnkgKiBtKTtcbiAgICBjdHguc2V0VHJhbnNmb3JtKDEsIDAsIDAsIDEsIDAsIDApO1xuICAgIGN0eC5yZXN0b3JlKCk7XG4gICAgY3R4LnNhdmUoKTtcblxuICAgIGN0eC5kcmF3SW1hZ2UodGhpcy5fY29udGFpbmVyQ29weSwgMCwgMCwgc2l6ZS54LCBzaXplLnkpO1xuICAgIGN0eC50cmFuc2Zvcm0uYXBwbHkoY3R4LCBtYXRyaXgpO1xuXG4gICAgLy8gbm93IGRyYXcgb25lIGxheWVyIG9ubHlcbiAgICB0aGlzLl9kcmF3aW5nID0gdHJ1ZTtcbiAgICBsYXllci5fdXBkYXRlUGF0aCgpO1xuICAgIHRoaXMuX2RyYXdpbmcgPSBmYWxzZTtcblxuICAgIGN0eC5yZXN0b3JlKCk7XG4gIH1cblxufSk7XG4iLCIvKipcbiAqIERyYWcgaGFuZGxlclxuICogQGNsYXNzIEwuUGF0aC5EcmFnXG4gKiBAZXh0ZW5kcyB7TC5IYW5kbGVyfVxuICovXG5MLkhhbmRsZXIuUGF0aERyYWcgPSBMLkhhbmRsZXIuZXh0ZW5kKCAvKiogQGxlbmRzICBMLlBhdGguRHJhZy5wcm90b3R5cGUgKi8ge1xuXG4gIHN0YXRpY3M6IHtcbiAgICBEUkFHR0lOR19DTFM6ICdsZWFmbGV0LXBhdGgtZHJhZ2dhYmxlJyxcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBAcGFyYW0gIHtMLlBhdGh9IHBhdGhcbiAgICogQGNvbnN0cnVjdG9yXG4gICAqL1xuICBpbml0aWFsaXplOiBmdW5jdGlvbihwYXRoKSB7XG5cbiAgICAvKipcbiAgICAgKiBAdHlwZSB7TC5QYXRofVxuICAgICAqL1xuICAgIHRoaXMuX3BhdGggPSBwYXRoO1xuXG4gICAgLyoqXG4gICAgICogQHR5cGUge0FycmF5LjxOdW1iZXI+fVxuICAgICAqL1xuICAgIHRoaXMuX21hdHJpeCA9IG51bGw7XG5cbiAgICAvKipcbiAgICAgKiBAdHlwZSB7TC5Qb2ludH1cbiAgICAgKi9cbiAgICB0aGlzLl9zdGFydFBvaW50ID0gbnVsbDtcblxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtMLlBvaW50fVxuICAgICAqL1xuICAgIHRoaXMuX2RyYWdTdGFydFBvaW50ID0gbnVsbDtcblxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtCb29sZWFufVxuICAgICAqL1xuICAgIHRoaXMuX21hcERyYWdnaW5nV2FzRW5hYmxlZCA9IGZhbHNlO1xuXG4gIH0sXG5cbiAgLyoqXG4gICAqIEVuYWJsZSBkcmFnZ2luZ1xuICAgKi9cbiAgYWRkSG9va3M6IGZ1bmN0aW9uKCkge1xuICAgIHRoaXMuX3BhdGgub24oJ21vdXNlZG93bicsIHRoaXMuX29uRHJhZ1N0YXJ0LCB0aGlzKTtcblxuICAgIHRoaXMuX3BhdGgub3B0aW9ucy5jbGFzc05hbWUgPSB0aGlzLl9wYXRoLm9wdGlvbnMuY2xhc3NOYW1lID9cbiAgICAgICAgKHRoaXMuX3BhdGgub3B0aW9ucy5jbGFzc05hbWUgKyAnICcgKyBMLkhhbmRsZXIuUGF0aERyYWcuRFJBR0dJTkdfQ0xTKSA6XG4gICAgICAgICBMLkhhbmRsZXIuUGF0aERyYWcuRFJBR0dJTkdfQ0xTO1xuXG4gICAgaWYgKHRoaXMuX3BhdGguX3BhdGgpIHtcbiAgICAgIEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9wYXRoLl9wYXRoLCBMLkhhbmRsZXIuUGF0aERyYWcuRFJBR0dJTkdfQ0xTKTtcbiAgICB9XG4gIH0sXG5cbiAgLyoqXG4gICAqIERpc2FibGUgZHJhZ2dpbmdcbiAgICovXG4gIHJlbW92ZUhvb2tzOiBmdW5jdGlvbigpIHtcbiAgICB0aGlzLl9wYXRoLm9mZignbW91c2Vkb3duJywgdGhpcy5fb25EcmFnU3RhcnQsIHRoaXMpO1xuXG4gICAgdGhpcy5fcGF0aC5vcHRpb25zLmNsYXNzTmFtZSA9IHRoaXMuX3BhdGgub3B0aW9ucy5jbGFzc05hbWVcbiAgICAgIC5yZXBsYWNlKG5ldyBSZWdFeHAoJ1xcXFxzKycgKyBMLkhhbmRsZXIuUGF0aERyYWcuRFJBR0dJTkdfQ0xTKSwgJycpO1xuICAgIGlmICh0aGlzLl9wYXRoLl9wYXRoKSB7XG4gICAgICBMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fcGF0aC5fcGF0aCwgTC5IYW5kbGVyLlBhdGhEcmFnLkRSQUdHSU5HX0NMUyk7XG4gICAgfVxuICB9LFxuXG4gIC8qKlxuICAgKiBAcmV0dXJuIHtCb29sZWFufVxuICAgKi9cbiAgbW92ZWQ6IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLl9wYXRoLl9kcmFnTW92ZWQ7XG4gIH0sXG5cbiAgLyoqXG4gICAqIFN0YXJ0IGRyYWdcbiAgICogQHBhcmFtICB7TC5Nb3VzZUV2ZW50fSBldnRcbiAgICovXG4gIF9vbkRyYWdTdGFydDogZnVuY3Rpb24oZXZ0KSB7XG4gICAgdmFyIGV2ZW50VHlwZSA9IGV2dC5vcmlnaW5hbEV2ZW50Ll9zaW11bGF0ZWQgPyAndG91Y2hzdGFydCcgOiBldnQub3JpZ2luYWxFdmVudC50eXBlO1xuXG4gICAgdGhpcy5fbWFwRHJhZ2dpbmdXYXNFbmFibGVkID0gZmFsc2U7XG4gICAgdGhpcy5fc3RhcnRQb2ludCA9IGV2dC5jb250YWluZXJQb2ludC5jbG9uZSgpO1xuICAgIHRoaXMuX2RyYWdTdGFydFBvaW50ID0gZXZ0LmNvbnRhaW5lclBvaW50LmNsb25lKCk7XG4gICAgdGhpcy5fbWF0cml4ID0gWzEsIDAsIDAsIDEsIDAsIDBdO1xuICAgIEwuRG9tRXZlbnQuc3RvcChldnQub3JpZ2luYWxFdmVudCk7XG5cbiAgICBMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fcGF0aC5fcmVuZGVyZXIuX2NvbnRhaW5lciwgJ2xlYWZsZXQtaW50ZXJhY3RpdmUnKTtcbiAgICBMLkRvbUV2ZW50XG4gICAgICAub24oZG9jdW1lbnQsIEwuRHJhZ2dhYmxlLk1PVkVbZXZlbnRUeXBlXSwgdGhpcy5fb25EcmFnLCAgICB0aGlzKVxuICAgICAgLm9uKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5FTkRbZXZlbnRUeXBlXSwgIHRoaXMuX29uRHJhZ0VuZCwgdGhpcyk7XG5cbiAgICBpZiAodGhpcy5fcGF0aC5fbWFwLmRyYWdnaW5nLmVuYWJsZWQoKSkge1xuICAgICAgLy8gSSBndWVzcyBpdCdzIHJlcXVpcmVkIGJlY2F1c2UgbW91c2Rvd24gZ2V0cyBzaW11bGF0ZWQgd2l0aCBhIGRlbGF5XG4gICAgICAvL3RoaXMuX3BhdGguX21hcC5kcmFnZ2luZy5fZHJhZ2dhYmxlLl9vblVwKGV2dCk7XG5cbiAgICAgIHRoaXMuX3BhdGguX21hcC5kcmFnZ2luZy5kaXNhYmxlKCk7XG4gICAgICB0aGlzLl9tYXBEcmFnZ2luZ1dhc0VuYWJsZWQgPSB0cnVlO1xuICAgIH1cbiAgICB0aGlzLl9wYXRoLl9kcmFnTW92ZWQgPSBmYWxzZTtcblxuICAgIGlmICh0aGlzLl9wYXRoLl9wb3B1cCkgeyAvLyB0aGF0IG1pZ2h0IGJlIGEgY2FzZSBvbiB0b3VjaCBkZXZpY2VzIGFzIHdlbGxcbiAgICAgIHRoaXMuX3BhdGguX3BvcHVwLl9jbG9zZSgpO1xuICAgIH1cblxuICAgIHRoaXMuX3JlcGxhY2VDb29yZEdldHRlcnMoZXZ0KTtcbiAgfSxcblxuICAvKipcbiAgICogRHJhZ2dpbmdcbiAgICogQHBhcmFtICB7TC5Nb3VzZUV2ZW50fSBldnRcbiAgICovXG4gIF9vbkRyYWc6IGZ1bmN0aW9uKGV2dCkge1xuICAgIEwuRG9tRXZlbnQuc3RvcChldnQpO1xuXG4gICAgdmFyIGZpcnN0ID0gKGV2dC50b3VjaGVzICYmIGV2dC50b3VjaGVzLmxlbmd0aCA+PSAxID8gZXZ0LnRvdWNoZXNbMF0gOiBldnQpO1xuICAgIHZhciBjb250YWluZXJQb2ludCA9IHRoaXMuX3BhdGguX21hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChmaXJzdCk7XG5cbiAgICAvLyBza2lwIHRhcHNcbiAgICBpZiAoZXZ0LnR5cGUgPT09ICd0b3VjaG1vdmUnICYmICF0aGlzLl9wYXRoLl9kcmFnTW92ZWQpIHtcbiAgICAgIHZhciB0b3RhbE1vdXNlRHJhZ0Rpc3RhbmNlID0gdGhpcy5fZHJhZ1N0YXJ0UG9pbnQuZGlzdGFuY2VUbyhjb250YWluZXJQb2ludCk7XG4gICAgICBpZiAodG90YWxNb3VzZURyYWdEaXN0YW5jZSA8PSB0aGlzLl9wYXRoLl9tYXAub3B0aW9ucy50YXBUb2xlcmFuY2UpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cblxuICAgIHZhciB4ID0gY29udGFpbmVyUG9pbnQueDtcbiAgICB2YXIgeSA9IGNvbnRhaW5lclBvaW50Lnk7XG5cbiAgICB2YXIgZHggPSB4IC0gdGhpcy5fc3RhcnRQb2ludC54O1xuICAgIHZhciBkeSA9IHkgLSB0aGlzLl9zdGFydFBvaW50Lnk7XG5cbiAgICAvLyBTZW5kIGV2ZW50cyBvbmx5IGlmIHBvaW50IHdhcyBtb3ZlZFxuICAgIGlmIChkeCB8fCBkeSkge1xuICAgICAgaWYgKCF0aGlzLl9wYXRoLl9kcmFnTW92ZWQpIHtcbiAgICAgICAgdGhpcy5fcGF0aC5fZHJhZ01vdmVkID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5fcGF0aC5maXJlKCdkcmFnc3RhcnQnLCBldnQpO1xuICAgICAgICAvLyB3ZSBkb24ndCB3YW50IHRoYXQgdG8gaGFwcGVuIG9uIGNsaWNrXG4gICAgICAgIHRoaXMuX3BhdGguYnJpbmdUb0Zyb250KCk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuX21hdHJpeFs0XSArPSBkeDtcbiAgICAgIHRoaXMuX21hdHJpeFs1XSArPSBkeTtcblxuICAgICAgdGhpcy5fc3RhcnRQb2ludC54ID0geDtcbiAgICAgIHRoaXMuX3N0YXJ0UG9pbnQueSA9IHk7XG5cbiAgICAgIHRoaXMuX3BhdGguZmlyZSgncHJlZHJhZycsIGV2dCk7XG4gICAgICB0aGlzLl9wYXRoLl90cmFuc2Zvcm0odGhpcy5fbWF0cml4KTtcbiAgICAgIHRoaXMuX3BhdGguZmlyZSgnZHJhZycsIGV2dCk7XG4gICAgfVxuICB9LFxuXG4gIC8qKlxuICAgKiBEcmFnZ2luZyBzdG9wcGVkLCBhcHBseVxuICAgKiBAcGFyYW0gIHtMLk1vdXNlRXZlbnR9IGV2dFxuICAgKi9cbiAgX29uRHJhZ0VuZDogZnVuY3Rpb24oZXZ0KSB7XG4gICAgdmFyIGNvbnRhaW5lclBvaW50ID0gdGhpcy5fcGF0aC5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGV2dCk7XG4gICAgdmFyIG1vdmVkID0gdGhpcy5tb3ZlZCgpO1xuXG4gICAgLy8gYXBwbHkgbWF0cml4XG4gICAgaWYgKG1vdmVkKSB7XG4gICAgICB0aGlzLl90cmFuc2Zvcm1Qb2ludHModGhpcy5fbWF0cml4KTtcbiAgICAgIHRoaXMuX3BhdGguX3VwZGF0ZVBhdGgoKTtcbiAgICAgIHRoaXMuX3BhdGguX3Byb2plY3QoKTtcbiAgICAgIHRoaXMuX3BhdGguX3RyYW5zZm9ybShudWxsKTtcblxuICAgICAgTC5Eb21FdmVudC5zdG9wKGV2dCk7XG4gICAgfVxuXG5cbiAgICBMLkRvbUV2ZW50XG4gICAgICAub2ZmKGRvY3VtZW50LCAnbW91c2Vtb3ZlIHRvdWNobW92ZScsIHRoaXMuX29uRHJhZywgdGhpcylcbiAgICAgIC5vZmYoZG9jdW1lbnQsICdtb3VzZXVwIHRvdWNoZW5kJywgICAgdGhpcy5fb25EcmFnRW5kLCB0aGlzKTtcblxuICAgIHRoaXMuX3Jlc3RvcmVDb29yZEdldHRlcnMoKTtcblxuICAgIC8vIGNvbnNpc3RlbmN5XG4gICAgaWYgKG1vdmVkKSB7XG4gICAgICB0aGlzLl9wYXRoLmZpcmUoJ2RyYWdlbmQnLCB7XG4gICAgICAgIGRpc3RhbmNlOiBNYXRoLnNxcnQoXG4gICAgICAgICAgTC5MaW5lVXRpbC5fc3FEaXN0KHRoaXMuX2RyYWdTdGFydFBvaW50LCBjb250YWluZXJQb2ludClcbiAgICAgICAgKVxuICAgICAgfSk7XG5cbiAgICAgIC8vIGhhY2sgZm9yIHNraXBwaW5nIHRoZSBjbGljayBpbiBjYW52YXMtcmVuZGVyZWQgbGF5ZXJzXG4gICAgICB2YXIgY29udGFpbnMgPSB0aGlzLl9wYXRoLl9jb250YWluc1BvaW50O1xuICAgICAgdGhpcy5fcGF0aC5fY29udGFpbnNQb2ludCA9IEwuVXRpbC5mYWxzZUZuO1xuICAgICAgTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUoZnVuY3Rpb24oKSB7XG4gICAgICAgIEwuRG9tRXZlbnQuX3NraXBwZWQoeyB0eXBlOiAnY2xpY2snIH0pO1xuICAgICAgICB0aGlzLl9wYXRoLl9jb250YWluc1BvaW50ID0gY29udGFpbnM7XG4gICAgICB9LCB0aGlzKTtcbiAgICB9XG5cbiAgICB0aGlzLl9tYXRyaXggICAgICAgICAgPSBudWxsO1xuICAgIHRoaXMuX3N0YXJ0UG9pbnQgICAgICA9IG51bGw7XG4gICAgdGhpcy5fZHJhZ1N0YXJ0UG9pbnQgID0gbnVsbDtcbiAgICB0aGlzLl9wYXRoLl9kcmFnTW92ZWQgPSBmYWxzZTtcblxuICAgIGlmICh0aGlzLl9tYXBEcmFnZ2luZ1dhc0VuYWJsZWQpIHtcbiAgICAgIGlmIChtb3ZlZCkgTC5Eb21FdmVudC5fZmFrZVN0b3AoeyB0eXBlOiAnY2xpY2snIH0pO1xuICAgICAgdGhpcy5fcGF0aC5fbWFwLmRyYWdnaW5nLmVuYWJsZSgpO1xuICAgIH1cbiAgfSxcblxuXG4gIC8qKlxuICAgKiBBcHBsaWVzIHRyYW5zZm9ybWF0aW9uLCBkb2VzIGl0IGluIG9uZSBzd2VlcCBmb3IgcGVyZm9ybWFuY2UsXG4gICAqIHNvIGRvbid0IGJlIHN1cnByaXNlZCBhYm91dCB0aGUgY29kZSByZXBldGl0aW9uLlxuICAgKlxuICAgKiBbIHggXSAgIFsgYSAgYiAgdHggXSBbIHggXSAgIFsgYSAqIHggKyBiICogeSArIHR4IF1cbiAgICogWyB5IF0gPSBbIGMgIGQgIHR5IF0gWyB5IF0gPSBbIGMgKiB4ICsgZCAqIHkgKyB0eSBdXG4gICAqXG4gICAqIEBwYXJhbSB7QXJyYXkuPE51bWJlcj59IG1hdHJpeFxuICAgKi9cbiAgX3RyYW5zZm9ybVBvaW50czogZnVuY3Rpb24obWF0cml4LCBkZXN0KSB7XG4gICAgdmFyIHBhdGggPSB0aGlzLl9wYXRoO1xuICAgIHZhciBpLCBsZW4sIGxhdGxuZztcblxuICAgIHZhciBweCA9IEwucG9pbnQobWF0cml4WzRdLCBtYXRyaXhbNV0pO1xuXG4gICAgdmFyIGNycyA9IHBhdGguX21hcC5vcHRpb25zLmNycztcbiAgICB2YXIgdHJhbnNmb3JtYXRpb24gPSBjcnMudHJhbnNmb3JtYXRpb247XG4gICAgdmFyIHNjYWxlID0gY3JzLnNjYWxlKHBhdGguX21hcC5nZXRab29tKCkpO1xuICAgIHZhciBwcm9qZWN0aW9uID0gY3JzLnByb2plY3Rpb247XG5cbiAgICB2YXIgZGlmZiA9IHRyYW5zZm9ybWF0aW9uLnVudHJhbnNmb3JtKHB4LCBzY2FsZSlcbiAgICAgIC5zdWJ0cmFjdCh0cmFuc2Zvcm1hdGlvbi51bnRyYW5zZm9ybShMLnBvaW50KDAsIDApLCBzY2FsZSkpO1xuICAgIHZhciBhcHBseVRyYW5zZm9ybSA9ICFkZXN0O1xuXG4gICAgcGF0aC5fYm91bmRzID0gbmV3IEwuTGF0TG5nQm91bmRzKCk7XG5cbiAgICAvLyBjb25zb2xlLnRpbWUoJ3RyYW5zZm9ybScpO1xuICAgIC8vIGFsbCBzaGlmdHMgYXJlIGluLXBsYWNlXG4gICAgaWYgKHBhdGguX3BvaW50KSB7IC8vIEwuQ2lyY2xlXG4gICAgICBkZXN0ID0gcHJvamVjdGlvbi51bnByb2plY3QoXG4gICAgICAgIHByb2plY3Rpb24ucHJvamVjdChwYXRoLl9sYXRsbmcpLl9hZGQoZGlmZikpO1xuICAgICAgaWYgKGFwcGx5VHJhbnNmb3JtKSB7XG4gICAgICAgIHBhdGguX2xhdGxuZyA9IGRlc3Q7XG4gICAgICAgIHBhdGguX3BvaW50Ll9hZGQocHgpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAocGF0aC5fcmluZ3MgfHwgcGF0aC5fcGFydHMpIHsgLy8gZXZlcnl0aGluZyBlbHNlXG4gICAgICB2YXIgcmluZ3MgICA9IHBhdGguX3JpbmdzIHx8IHBhdGguX3BhcnRzO1xuICAgICAgdmFyIGxhdGxuZ3MgPSBwYXRoLl9sYXRsbmdzO1xuICAgICAgZGVzdCA9IGRlc3QgfHwgbGF0bG5ncztcbiAgICAgIGlmICghTC5VdGlsLmlzQXJyYXkobGF0bG5nc1swXSkpIHsgLy8gcG9seWxpbmVcbiAgICAgICAgbGF0bG5ncyA9IFtsYXRsbmdzXTtcbiAgICAgICAgZGVzdCAgICA9IFtkZXN0XTtcbiAgICAgIH1cbiAgICAgIGZvciAoaSA9IDAsIGxlbiA9IHJpbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICAgIGRlc3RbaV0gPSBkZXN0W2ldIHx8IFtdO1xuICAgICAgICBmb3IgKHZhciBqID0gMCwgamogPSByaW5nc1tpXS5sZW5ndGg7IGogPCBqajsgaisrKSB7XG4gICAgICAgICAgbGF0bG5nICAgICA9IGxhdGxuZ3NbaV1bal07XG4gICAgICAgICAgZGVzdFtpXVtqXSA9IHByb2plY3Rpb25cbiAgICAgICAgICAgIC51bnByb2plY3QocHJvamVjdGlvbi5wcm9qZWN0KGxhdGxuZykuX2FkZChkaWZmKSk7XG4gICAgICAgICAgaWYgKGFwcGx5VHJhbnNmb3JtKSB7XG4gICAgICAgICAgICBwYXRoLl9ib3VuZHMuZXh0ZW5kKGxhdGxuZ3NbaV1bal0pO1xuICAgICAgICAgICAgcmluZ3NbaV1bal0uX2FkZChweCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBkZXN0O1xuICAgIC8vIGNvbnNvbGUudGltZUVuZCgndHJhbnNmb3JtJyk7XG4gIH0sXG5cblxuXG4gIC8qKlxuICAgKiBJZiB5b3Ugd2FudCB0byByZWFkIHRoZSBsYXRsbmdzIGR1cmluZyB0aGUgZHJhZyAtIHlvdXIgcmlnaHQsXG4gICAqIGJ1dCB0aGV5IGhhdmUgdG8gYmUgdHJhbnNmb3JtZWRcbiAgICovXG4gIF9yZXBsYWNlQ29vcmRHZXR0ZXJzOiBmdW5jdGlvbigpIHtcbiAgICBpZiAodGhpcy5fcGF0aC5nZXRMYXRMbmcpIHsgLy8gQ2lyY2xlLCBDaXJjbGVNYXJrZXJcbiAgICAgIHRoaXMuX3BhdGguZ2V0TGF0TG5nXyA9IHRoaXMuX3BhdGguZ2V0TGF0TG5nO1xuICAgICAgdGhpcy5fcGF0aC5nZXRMYXRMbmcgPSBMLlV0aWwuYmluZChmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZHJhZ2dpbmcuX3RyYW5zZm9ybVBvaW50cyh0aGlzLmRyYWdnaW5nLl9tYXRyaXgsIHt9KTtcbiAgICAgIH0sIHRoaXMuX3BhdGgpO1xuICAgIH0gZWxzZSBpZiAodGhpcy5fcGF0aC5nZXRMYXRMbmdzKSB7XG4gICAgICB0aGlzLl9wYXRoLmdldExhdExuZ3NfID0gdGhpcy5fcGF0aC5nZXRMYXRMbmdzO1xuICAgICAgdGhpcy5fcGF0aC5nZXRMYXRMbmdzID0gTC5VdGlsLmJpbmQoZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRyYWdnaW5nLl90cmFuc2Zvcm1Qb2ludHModGhpcy5kcmFnZ2luZy5fbWF0cml4LCBbXSk7XG4gICAgICB9LCB0aGlzLl9wYXRoKTtcbiAgICB9XG4gIH0sXG5cblxuICAvKipcbiAgICogUHV0IGJhY2sgdGhlIGdldHRlcnNcbiAgICovXG4gIF9yZXN0b3JlQ29vcmRHZXR0ZXJzOiBmdW5jdGlvbigpIHtcbiAgICBpZiAodGhpcy5fcGF0aC5nZXRMYXRMbmdfKSB7XG4gICAgICB0aGlzLl9wYXRoLmdldExhdExuZyA9IHRoaXMuX3BhdGguZ2V0TGF0TG5nXztcbiAgICAgIGRlbGV0ZSB0aGlzLl9wYXRoLmdldExhdExuZ187XG4gICAgfSBlbHNlIGlmICh0aGlzLl9wYXRoLmdldExhdExuZ3NfKSB7XG4gICAgICB0aGlzLl9wYXRoLmdldExhdExuZ3MgPSB0aGlzLl9wYXRoLmdldExhdExuZ3NfO1xuICAgICAgZGVsZXRlIHRoaXMuX3BhdGguZ2V0TGF0TG5nc187XG4gICAgfVxuICB9XG5cbn0pO1xuXG5cbi8qKlxuICogQHBhcmFtICB7TC5QYXRofSBsYXllclxuICogQHJldHVybiB7TC5QYXRofVxuICovXG5MLkhhbmRsZXIuUGF0aERyYWcubWFrZURyYWdnYWJsZSA9IGZ1bmN0aW9uKGxheWVyKSB7XG4gIGxheWVyLmRyYWdnaW5nID0gbmV3IEwuSGFuZGxlci5QYXRoRHJhZyhsYXllcik7XG4gIHJldHVybiBsYXllcjtcbn07XG5cblxuLyoqXG4gKiBBbHNvIGV4cG9zZSBhcyBhIG1ldGhvZFxuICogQHJldHVybiB7TC5QYXRofVxuICovXG5MLlBhdGgucHJvdG90eXBlLm1ha2VEcmFnZ2FibGUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIEwuSGFuZGxlci5QYXRoRHJhZy5tYWtlRHJhZ2dhYmxlKHRoaXMpO1xufTtcblxuXG5MLlBhdGguYWRkSW5pdEhvb2soZnVuY3Rpb24oKSB7XG4gIGlmICh0aGlzLm9wdGlvbnMuZHJhZ2dhYmxlKSB7XG4gICAgLy8gZW5zdXJlIGludGVyYWN0aXZlXG4gICAgdGhpcy5vcHRpb25zLmludGVyYWN0aXZlID0gdHJ1ZTtcblxuICAgIGlmICh0aGlzLmRyYWdnaW5nKSB7XG4gICAgICB0aGlzLmRyYWdnaW5nLmVuYWJsZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBMLkhhbmRsZXIuUGF0aERyYWcubWFrZURyYWdnYWJsZSh0aGlzKTtcbiAgICAgIHRoaXMuZHJhZ2dpbmcuZW5hYmxlKCk7XG4gICAgfVxuICB9IGVsc2UgaWYgKHRoaXMuZHJhZ2dpbmcpIHtcbiAgICB0aGlzLmRyYWdnaW5nLmRpc2FibGUoKTtcbiAgfVxufSk7XG4iLCIvKipcbiAqIExlYWZsZXQgdmVjdG9yIGZlYXR1cmVzIGRyYWcgZnVuY3Rpb25hbGl0eVxuICogQGF1dGhvciBBbGV4YW5kZXIgTWlsZXZza2kgPGluZm9AdzhyLm5hbWU+XG4gKiBAcHJlc2VydmVcbiAqL1xuXG4vKipcbiAqIE1hdHJpeCB0cmFuc2Zvcm0gcGF0aCBmb3IgU1ZHL1ZNTFxuICogUmVuZGVyZXItaW5kZXBlbmRlbnRcbiAqL1xuTC5QYXRoLmluY2x1ZGUoe1xuXG5cdC8qKlxuXHQgKiBBcHBsaWVzIG1hdHJpeCB0cmFuc2Zvcm1hdGlvbiB0byBTVkdcblx0ICogQHBhcmFtIHtBcnJheS48TnVtYmVyPj99IG1hdHJpeFxuXHQgKi9cblx0X3RyYW5zZm9ybTogZnVuY3Rpb24obWF0cml4KSB7XG5cdFx0aWYgKHRoaXMuX3JlbmRlcmVyKSB7XG5cdFx0XHRpZiAobWF0cml4KSB7XG5cdFx0XHRcdHRoaXMuX3JlbmRlcmVyLnRyYW5zZm9ybVBhdGgodGhpcywgbWF0cml4KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdC8vIHJlc2V0IHRyYW5zZm9ybSBtYXRyaXhcblx0XHRcdFx0dGhpcy5fcmVuZGVyZXIuX3Jlc2V0VHJhbnNmb3JtUGF0aCh0aGlzKTtcblx0XHRcdFx0dGhpcy5fdXBkYXRlKCk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDaGVjayBpZiB0aGUgZmVhdHVyZSB3YXMgZHJhZ2dlZCwgdGhhdCdsbCBzdXByZXNzIHRoZSBjbGljayBldmVudFxuXHQgKiBvbiBtb3VzZXVwLiBUaGF0IGZpeGVzIHBvcHVwcyBmb3IgZXhhbXBsZVxuXHQgKlxuXHQgKiBAcGFyYW0gIHtNb3VzZUV2ZW50fSBlXG5cdCAqL1xuXHRfb25Nb3VzZUNsaWNrOiBmdW5jdGlvbihlKSB7XG5cdFx0aWYgKCh0aGlzLmRyYWdnaW5nICYmIHRoaXMuZHJhZ2dpbmcubW92ZWQoKSkgfHxcblx0XHRcdCh0aGlzLl9tYXAuZHJhZ2dpbmcgJiYgdGhpcy5fbWFwLmRyYWdnaW5nLm1vdmVkKCkpKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dGhpcy5fZmlyZU1vdXNlRXZlbnQoZSk7XG5cdH1cblxufSk7XG4iLCJMLlNWRy5pbmNsdWRlKCFMLkJyb3dzZXIudm1sID8ge30gOiB7XG5cblx0LyoqXG5cdCAqIFJlc2V0IHRyYW5zZm9ybSBtYXRyaXhcblx0ICovXG5cdF9yZXNldFRyYW5zZm9ybVBhdGg6IGZ1bmN0aW9uKGxheWVyKSB7XG5cdFx0aWYgKGxheWVyLl9za2V3KSB7XG5cdFx0XHQvLyBzdXBlciBpbXBvcnRhbnQhIHdvcmthcm91bmQgZm9yIGEgJ2p1bXBpbmcnIGdsaXRjaDpcblx0XHRcdC8vIGRpc2FibGUgdHJhbnNmb3JtIGJlZm9yZSByZW1vdmluZyBpdFxuXHRcdFx0bGF5ZXIuX3NrZXcub24gPSBmYWxzZTtcblx0XHRcdGxheWVyLl9wYXRoLnJlbW92ZUNoaWxkKGxheWVyLl9za2V3KTtcblx0XHRcdGxheWVyLl9za2V3ID0gbnVsbDtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIEFwcGxpZXMgbWF0cml4IHRyYW5zZm9ybWF0aW9uIHRvIFZNTFxuXHQgKiBAcGFyYW0ge0wuUGF0aH0gICAgICAgICBsYXllclxuXHQgKiBAcGFyYW0ge0FycmF5LjxOdW1iZXI+fSBtYXRyaXhcblx0ICovXG5cdHRyYW5zZm9ybVBhdGg6IGZ1bmN0aW9uKGxheWVyLCBtYXRyaXgpIHtcblx0XHR2YXIgc2tldyA9IGxheWVyLl9za2V3O1xuXG5cdFx0aWYgKCFza2V3KSB7XG5cdFx0XHRza2V3ID0gTC5TVkcuY3JlYXRlKCdza2V3Jyk7XG5cdFx0XHRsYXllci5fcGF0aC5hcHBlbmRDaGlsZChza2V3KTtcblx0XHRcdHNrZXcuc3R5bGUuYmVoYXZpb3IgPSAndXJsKCNkZWZhdWx0I1ZNTCknO1xuXHRcdFx0bGF5ZXIuX3NrZXcgPSBza2V3O1xuXHRcdH1cblxuXHRcdC8vIGhhbmRsZSBza2V3L3RyYW5zbGF0ZSBzZXBhcmF0ZWx5LCBjYXVzZSBpdCdzIGJyb2tlblxuXHRcdHZhciBtdCA9IG1hdHJpeFswXS50b0ZpeGVkKDgpICsgJyAnICsgbWF0cml4WzFdLnRvRml4ZWQoOCkgKyAnICcgK1xuXHRcdFx0bWF0cml4WzJdLnRvRml4ZWQoOCkgKyAnICcgKyBtYXRyaXhbM10udG9GaXhlZCg4KSArICcgMCAwJztcblx0XHR2YXIgb2Zmc2V0ID0gTWF0aC5mbG9vcihtYXRyaXhbNF0pLnRvRml4ZWQoKSArICcsICcgK1xuXHRcdFx0TWF0aC5mbG9vcihtYXRyaXhbNV0pLnRvRml4ZWQoKSArICcnO1xuXG5cdFx0dmFyIHMgPSB0aGlzLl9wYXRoLnN0eWxlO1xuXHRcdHZhciBsID0gcGFyc2VGbG9hdChzLmxlZnQpO1xuXHRcdHZhciB0ID0gcGFyc2VGbG9hdChzLnRvcCk7XG5cdFx0dmFyIHcgPSBwYXJzZUZsb2F0KHMud2lkdGgpO1xuXHRcdHZhciBoID0gcGFyc2VGbG9hdChzLmhlaWdodCk7XG5cblx0XHRpZiAoaXNOYU4obCkpICAgICAgIGwgPSAwO1xuXHRcdGlmIChpc05hTih0KSkgICAgICAgdCA9IDA7XG5cdFx0aWYgKGlzTmFOKHcpIHx8ICF3KSB3ID0gMTtcblx0XHRpZiAoaXNOYU4oaCkgfHwgIWgpIGggPSAxO1xuXG5cdFx0dmFyIG9yaWdpbiA9ICgtbCAvIHcgLSAwLjUpLnRvRml4ZWQoOCkgKyAnICcgKyAoLXQgLyBoIC0gMC41KS50b0ZpeGVkKDgpO1xuXG5cdFx0c2tldy5vbiA9ICdmJztcblx0XHRza2V3Lm1hdHJpeCA9IG10O1xuXHRcdHNrZXcub3JpZ2luID0gb3JpZ2luO1xuXHRcdHNrZXcub2Zmc2V0ID0gb2Zmc2V0O1xuXHRcdHNrZXcub24gPSB0cnVlO1xuXHR9XG5cbn0pO1xuIiwiTC5TVkcuaW5jbHVkZSh7XG5cblx0LyoqXG5cdCAqIFJlc2V0IHRyYW5zZm9ybSBtYXRyaXhcblx0ICovXG5cdF9yZXNldFRyYW5zZm9ybVBhdGg6IGZ1bmN0aW9uKGxheWVyKSB7XG5cdFx0bGF5ZXIuX3BhdGguc2V0QXR0cmlidXRlTlMobnVsbCwgJ3RyYW5zZm9ybScsICcnKTtcblx0fSxcblxuXHQvKipcblx0ICogQXBwbGllcyBtYXRyaXggdHJhbnNmb3JtYXRpb24gdG8gU1ZHXG5cdCAqIEBwYXJhbSB7TC5QYXRofSAgICAgICAgIGxheWVyXG5cdCAqIEBwYXJhbSB7QXJyYXkuPE51bWJlcj59IG1hdHJpeFxuXHQgKi9cblx0dHJhbnNmb3JtUGF0aDogZnVuY3Rpb24obGF5ZXIsIG1hdHJpeCkge1xuXHRcdGxheWVyLl9wYXRoLnNldEF0dHJpYnV0ZU5TKG51bGwsICd0cmFuc2Zvcm0nLFxuXHRcdFx0J21hdHJpeCgnICsgbWF0cml4LmpvaW4oJyAnKSArICcpJyk7XG5cdH1cblxufSk7XG4iLCIvKlxuIExlYWZsZXQgMS4wLjMsIGEgSlMgbGlicmFyeSBmb3IgaW50ZXJhY3RpdmUgbWFwcy4gaHR0cDovL2xlYWZsZXRqcy5jb21cbiAoYykgMjAxMC0yMDE2IFZsYWRpbWlyIEFnYWZvbmtpbiwgKGMpIDIwMTAtMjAxMSBDbG91ZE1hZGVcbiovXG4oZnVuY3Rpb24gKHdpbmRvdywgZG9jdW1lbnQsIHVuZGVmaW5lZCkge1xyXG52YXIgTCA9IHtcclxuXHR2ZXJzaW9uOiBcIjEuMC4zXCJcclxufTtcclxuXHJcbmZ1bmN0aW9uIGV4cG9zZSgpIHtcclxuXHR2YXIgb2xkTCA9IHdpbmRvdy5MO1xyXG5cclxuXHRMLm5vQ29uZmxpY3QgPSBmdW5jdGlvbiAoKSB7XHJcblx0XHR3aW5kb3cuTCA9IG9sZEw7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9O1xyXG5cclxuXHR3aW5kb3cuTCA9IEw7XHJcbn1cclxuXHJcbi8vIGRlZmluZSBMZWFmbGV0IGZvciBOb2RlIG1vZHVsZSBwYXR0ZXJuIGxvYWRlcnMsIGluY2x1ZGluZyBCcm93c2VyaWZ5XHJcbmlmICh0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0JyAmJiB0eXBlb2YgbW9kdWxlLmV4cG9ydHMgPT09ICdvYmplY3QnKSB7XHJcblx0bW9kdWxlLmV4cG9ydHMgPSBMO1xyXG5cclxuLy8gZGVmaW5lIExlYWZsZXQgYXMgYW4gQU1EIG1vZHVsZVxyXG59IGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCkge1xyXG5cdGRlZmluZShMKTtcclxufVxyXG5cclxuLy8gZGVmaW5lIExlYWZsZXQgYXMgYSBnbG9iYWwgTCB2YXJpYWJsZSwgc2F2aW5nIHRoZSBvcmlnaW5hbCBMIHRvIHJlc3RvcmUgbGF0ZXIgaWYgbmVlZGVkXHJcbmlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJykge1xyXG5cdGV4cG9zZSgpO1xyXG59XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgVXRpbFxyXG4gKlxyXG4gKiBWYXJpb3VzIHV0aWxpdHkgZnVuY3Rpb25zLCB1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseS5cclxuICovXHJcblxyXG5MLlV0aWwgPSB7XHJcblxyXG5cdC8vIEBmdW5jdGlvbiBleHRlbmQoZGVzdDogT2JqZWN0LCBzcmM/OiBPYmplY3QpOiBPYmplY3RcclxuXHQvLyBNZXJnZXMgdGhlIHByb3BlcnRpZXMgb2YgdGhlIGBzcmNgIG9iamVjdCAob3IgbXVsdGlwbGUgb2JqZWN0cykgaW50byBgZGVzdGAgb2JqZWN0IGFuZCByZXR1cm5zIHRoZSBsYXR0ZXIuIEhhcyBhbiBgTC5leHRlbmRgIHNob3J0Y3V0LlxyXG5cdGV4dGVuZDogZnVuY3Rpb24gKGRlc3QpIHtcclxuXHRcdHZhciBpLCBqLCBsZW4sIHNyYztcclxuXHJcblx0XHRmb3IgKGogPSAxLCBsZW4gPSBhcmd1bWVudHMubGVuZ3RoOyBqIDwgbGVuOyBqKyspIHtcclxuXHRcdFx0c3JjID0gYXJndW1lbnRzW2pdO1xyXG5cdFx0XHRmb3IgKGkgaW4gc3JjKSB7XHJcblx0XHRcdFx0ZGVzdFtpXSA9IHNyY1tpXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGRlc3Q7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGNyZWF0ZShwcm90bzogT2JqZWN0LCBwcm9wZXJ0aWVzPzogT2JqZWN0KTogT2JqZWN0XHJcblx0Ly8gQ29tcGF0aWJpbGl0eSBwb2x5ZmlsbCBmb3IgW09iamVjdC5jcmVhdGVdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL09iamVjdC9jcmVhdGUpXHJcblx0Y3JlYXRlOiBPYmplY3QuY3JlYXRlIHx8IChmdW5jdGlvbiAoKSB7XHJcblx0XHRmdW5jdGlvbiBGKCkge31cclxuXHRcdHJldHVybiBmdW5jdGlvbiAocHJvdG8pIHtcclxuXHRcdFx0Ri5wcm90b3R5cGUgPSBwcm90bztcclxuXHRcdFx0cmV0dXJuIG5ldyBGKCk7XHJcblx0XHR9O1xyXG5cdH0pKCksXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBiaW5kKGZuOiBGdW5jdGlvbiwg4oCmKTogRnVuY3Rpb25cclxuXHQvLyBSZXR1cm5zIGEgbmV3IGZ1bmN0aW9uIGJvdW5kIHRvIHRoZSBhcmd1bWVudHMgcGFzc2VkLCBsaWtlIFtGdW5jdGlvbi5wcm90b3R5cGUuYmluZF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvRnVuY3Rpb24vYmluZCkuXHJcblx0Ly8gSGFzIGEgYEwuYmluZCgpYCBzaG9ydGN1dC5cclxuXHRiaW5kOiBmdW5jdGlvbiAoZm4sIG9iaikge1xyXG5cdFx0dmFyIHNsaWNlID0gQXJyYXkucHJvdG90eXBlLnNsaWNlO1xyXG5cclxuXHRcdGlmIChmbi5iaW5kKSB7XHJcblx0XHRcdHJldHVybiBmbi5iaW5kLmFwcGx5KGZuLCBzbGljZS5jYWxsKGFyZ3VtZW50cywgMSkpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBhcmdzID0gc2xpY2UuY2FsbChhcmd1bWVudHMsIDIpO1xyXG5cclxuXHRcdHJldHVybiBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHJldHVybiBmbi5hcHBseShvYmosIGFyZ3MubGVuZ3RoID8gYXJncy5jb25jYXQoc2xpY2UuY2FsbChhcmd1bWVudHMpKSA6IGFyZ3VtZW50cyk7XHJcblx0XHR9O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzdGFtcChvYmo6IE9iamVjdCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIHVuaXF1ZSBJRCBvZiBhbiBvYmplY3QsIGFzc2lnaW5nIGl0IG9uZSBpZiBpdCBkb2Vzbid0IGhhdmUgaXQuXHJcblx0c3RhbXA6IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdC8qZXNsaW50LWRpc2FibGUgKi9cclxuXHRcdG9iai5fbGVhZmxldF9pZCA9IG9iai5fbGVhZmxldF9pZCB8fCArK0wuVXRpbC5sYXN0SWQ7XHJcblx0XHRyZXR1cm4gb2JqLl9sZWFmbGV0X2lkO1xyXG5cdFx0Lyplc2xpbnQtZW5hYmxlICovXHJcblx0fSxcclxuXHJcblx0Ly8gQHByb3BlcnR5IGxhc3RJZDogTnVtYmVyXHJcblx0Ly8gTGFzdCB1bmlxdWUgSUQgdXNlZCBieSBbYHN0YW1wKClgXSgjdXRpbC1zdGFtcClcclxuXHRsYXN0SWQ6IDAsXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0aHJvdHRsZShmbjogRnVuY3Rpb24sIHRpbWU6IE51bWJlciwgY29udGV4dDogT2JqZWN0KTogRnVuY3Rpb25cclxuXHQvLyBSZXR1cm5zIGEgZnVuY3Rpb24gd2hpY2ggZXhlY3V0ZXMgZnVuY3Rpb24gYGZuYCB3aXRoIHRoZSBnaXZlbiBzY29wZSBgY29udGV4dGBcclxuXHQvLyAoc28gdGhhdCB0aGUgYHRoaXNgIGtleXdvcmQgcmVmZXJzIHRvIGBjb250ZXh0YCBpbnNpZGUgYGZuYCdzIGNvZGUpLiBUaGUgZnVuY3Rpb25cclxuXHQvLyBgZm5gIHdpbGwgYmUgY2FsbGVkIG5vIG1vcmUgdGhhbiBvbmUgdGltZSBwZXIgZ2l2ZW4gYW1vdW50IG9mIGB0aW1lYC4gVGhlIGFyZ3VtZW50c1xyXG5cdC8vIHJlY2VpdmVkIGJ5IHRoZSBib3VuZCBmdW5jdGlvbiB3aWxsIGJlIGFueSBhcmd1bWVudHMgcGFzc2VkIHdoZW4gYmluZGluZyB0aGVcclxuXHQvLyBmdW5jdGlvbiwgZm9sbG93ZWQgYnkgYW55IGFyZ3VtZW50cyBwYXNzZWQgd2hlbiBpbnZva2luZyB0aGUgYm91bmQgZnVuY3Rpb24uXHJcblx0Ly8gSGFzIGFuIGBMLmJpbmRgIHNob3J0Y3V0LlxyXG5cdHRocm90dGxlOiBmdW5jdGlvbiAoZm4sIHRpbWUsIGNvbnRleHQpIHtcclxuXHRcdHZhciBsb2NrLCBhcmdzLCB3cmFwcGVyRm4sIGxhdGVyO1xyXG5cclxuXHRcdGxhdGVyID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0XHQvLyByZXNldCBsb2NrIGFuZCBjYWxsIGlmIHF1ZXVlZFxyXG5cdFx0XHRsb2NrID0gZmFsc2U7XHJcblx0XHRcdGlmIChhcmdzKSB7XHJcblx0XHRcdFx0d3JhcHBlckZuLmFwcGx5KGNvbnRleHQsIGFyZ3MpO1xyXG5cdFx0XHRcdGFyZ3MgPSBmYWxzZTtcclxuXHRcdFx0fVxyXG5cdFx0fTtcclxuXHJcblx0XHR3cmFwcGVyRm4gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdGlmIChsb2NrKSB7XHJcblx0XHRcdFx0Ly8gY2FsbGVkIHRvbyBzb29uLCBxdWV1ZSB0byBjYWxsIGxhdGVyXHJcblx0XHRcdFx0YXJncyA9IGFyZ3VtZW50cztcclxuXHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0Ly8gY2FsbCBhbmQgbG9jayB1bnRpbCBsYXRlclxyXG5cdFx0XHRcdGZuLmFwcGx5KGNvbnRleHQsIGFyZ3VtZW50cyk7XHJcblx0XHRcdFx0c2V0VGltZW91dChsYXRlciwgdGltZSk7XHJcblx0XHRcdFx0bG9jayA9IHRydWU7XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblxyXG5cdFx0cmV0dXJuIHdyYXBwZXJGbjtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gd3JhcE51bShudW06IE51bWJlciwgcmFuZ2U6IE51bWJlcltdLCBpbmNsdWRlTWF4PzogQm9vbGVhbik6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIG51bWJlciBgbnVtYCBtb2R1bG8gYHJhbmdlYCBpbiBzdWNoIGEgd2F5IHNvIGl0IGxpZXMgd2l0aGluXHJcblx0Ly8gYHJhbmdlWzBdYCBhbmQgYHJhbmdlWzFdYC4gVGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgYWx3YXlzIHNtYWxsZXIgdGhhblxyXG5cdC8vIGByYW5nZVsxXWAgdW5sZXNzIGBpbmNsdWRlTWF4YCBpcyBzZXQgdG8gYHRydWVgLlxyXG5cdHdyYXBOdW06IGZ1bmN0aW9uICh4LCByYW5nZSwgaW5jbHVkZU1heCkge1xyXG5cdFx0dmFyIG1heCA9IHJhbmdlWzFdLFxyXG5cdFx0ICAgIG1pbiA9IHJhbmdlWzBdLFxyXG5cdFx0ICAgIGQgPSBtYXggLSBtaW47XHJcblx0XHRyZXR1cm4geCA9PT0gbWF4ICYmIGluY2x1ZGVNYXggPyB4IDogKCh4IC0gbWluKSAlIGQgKyBkKSAlIGQgKyBtaW47XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGZhbHNlRm4oKTogRnVuY3Rpb25cclxuXHQvLyBSZXR1cm5zIGEgZnVuY3Rpb24gd2hpY2ggYWx3YXlzIHJldHVybnMgYGZhbHNlYC5cclxuXHRmYWxzZUZuOiBmdW5jdGlvbiAoKSB7IHJldHVybiBmYWxzZTsgfSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGZvcm1hdE51bShudW06IE51bWJlciwgZGlnaXRzPzogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbnVtYmVyIGBudW1gIHJvdW5kZWQgdG8gYGRpZ2l0c2AgZGVjaW1hbHMsIG9yIHRvIDUgZGVjaW1hbHMgYnkgZGVmYXVsdC5cclxuXHRmb3JtYXROdW06IGZ1bmN0aW9uIChudW0sIGRpZ2l0cykge1xyXG5cdFx0dmFyIHBvdyA9IE1hdGgucG93KDEwLCBkaWdpdHMgfHwgNSk7XHJcblx0XHRyZXR1cm4gTWF0aC5yb3VuZChudW0gKiBwb3cpIC8gcG93O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0cmltKHN0cjogU3RyaW5nKTogU3RyaW5nXHJcblx0Ly8gQ29tcGF0aWJpbGl0eSBwb2x5ZmlsbCBmb3IgW1N0cmluZy5wcm90b3R5cGUudHJpbV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvU3RyaW5nL1RyaW0pXHJcblx0dHJpbTogZnVuY3Rpb24gKHN0cikge1xyXG5cdFx0cmV0dXJuIHN0ci50cmltID8gc3RyLnRyaW0oKSA6IHN0ci5yZXBsYWNlKC9eXFxzK3xcXHMrJC9nLCAnJyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNwbGl0V29yZHMoc3RyOiBTdHJpbmcpOiBTdHJpbmdbXVxyXG5cdC8vIFRyaW1zIGFuZCBzcGxpdHMgdGhlIHN0cmluZyBvbiB3aGl0ZXNwYWNlIGFuZCByZXR1cm5zIHRoZSBhcnJheSBvZiBwYXJ0cy5cclxuXHRzcGxpdFdvcmRzOiBmdW5jdGlvbiAoc3RyKSB7XHJcblx0XHRyZXR1cm4gTC5VdGlsLnRyaW0oc3RyKS5zcGxpdCgvXFxzKy8pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRPcHRpb25zKG9iajogT2JqZWN0LCBvcHRpb25zOiBPYmplY3QpOiBPYmplY3RcclxuXHQvLyBNZXJnZXMgdGhlIGdpdmVuIHByb3BlcnRpZXMgdG8gdGhlIGBvcHRpb25zYCBvZiB0aGUgYG9iamAgb2JqZWN0LCByZXR1cm5pbmcgdGhlIHJlc3VsdGluZyBvcHRpb25zLiBTZWUgYENsYXNzIG9wdGlvbnNgLiBIYXMgYW4gYEwuc2V0T3B0aW9uc2Agc2hvcnRjdXQuXHJcblx0c2V0T3B0aW9uczogZnVuY3Rpb24gKG9iaiwgb3B0aW9ucykge1xyXG5cdFx0aWYgKCFvYmouaGFzT3duUHJvcGVydHkoJ29wdGlvbnMnKSkge1xyXG5cdFx0XHRvYmoub3B0aW9ucyA9IG9iai5vcHRpb25zID8gTC5VdGlsLmNyZWF0ZShvYmoub3B0aW9ucykgOiB7fTtcclxuXHRcdH1cclxuXHRcdGZvciAodmFyIGkgaW4gb3B0aW9ucykge1xyXG5cdFx0XHRvYmoub3B0aW9uc1tpXSA9IG9wdGlvbnNbaV07XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gb2JqLm9wdGlvbnM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldFBhcmFtU3RyaW5nKG9iajogT2JqZWN0LCBleGlzdGluZ1VybD86IFN0cmluZywgdXBwZXJjYXNlPzogQm9vbGVhbik6IFN0cmluZ1xyXG5cdC8vIENvbnZlcnRzIGFuIG9iamVjdCBpbnRvIGEgcGFyYW1ldGVyIFVSTCBzdHJpbmcsIGUuZy4gYHthOiBcImZvb1wiLCBiOiBcImJhclwifWBcclxuXHQvLyB0cmFuc2xhdGVzIHRvIGAnP2E9Zm9vJmI9YmFyJ2AuIElmIGBleGlzdGluZ1VybGAgaXMgc2V0LCB0aGUgcGFyYW1ldGVycyB3aWxsXHJcblx0Ly8gYmUgYXBwZW5kZWQgYXQgdGhlIGVuZC4gSWYgYHVwcGVyY2FzZWAgaXMgYHRydWVgLCB0aGUgcGFyYW1ldGVyIG5hbWVzIHdpbGxcclxuXHQvLyBiZSB1cHBlcmNhc2VkIChlLmcuIGAnP0E9Zm9vJkI9YmFyJ2ApXHJcblx0Z2V0UGFyYW1TdHJpbmc6IGZ1bmN0aW9uIChvYmosIGV4aXN0aW5nVXJsLCB1cHBlcmNhc2UpIHtcclxuXHRcdHZhciBwYXJhbXMgPSBbXTtcclxuXHRcdGZvciAodmFyIGkgaW4gb2JqKSB7XHJcblx0XHRcdHBhcmFtcy5wdXNoKGVuY29kZVVSSUNvbXBvbmVudCh1cHBlcmNhc2UgPyBpLnRvVXBwZXJDYXNlKCkgOiBpKSArICc9JyArIGVuY29kZVVSSUNvbXBvbmVudChvYmpbaV0pKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiAoKCFleGlzdGluZ1VybCB8fCBleGlzdGluZ1VybC5pbmRleE9mKCc/JykgPT09IC0xKSA/ICc/JyA6ICcmJykgKyBwYXJhbXMuam9pbignJicpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0ZW1wbGF0ZShzdHI6IFN0cmluZywgZGF0YTogT2JqZWN0KTogU3RyaW5nXHJcblx0Ly8gU2ltcGxlIHRlbXBsYXRpbmcgZmFjaWxpdHksIGFjY2VwdHMgYSB0ZW1wbGF0ZSBzdHJpbmcgb2YgdGhlIGZvcm0gYCdIZWxsbyB7YX0sIHtifSdgXHJcblx0Ly8gYW5kIGEgZGF0YSBvYmplY3QgbGlrZSBge2E6ICdmb28nLCBiOiAnYmFyJ31gLCByZXR1cm5zIGV2YWx1YXRlZCBzdHJpbmdcclxuXHQvLyBgKCdIZWxsbyBmb28sIGJhcicpYC4gWW91IGNhbiBhbHNvIHNwZWNpZnkgZnVuY3Rpb25zIGluc3RlYWQgb2Ygc3RyaW5ncyBmb3JcclxuXHQvLyBkYXRhIHZhbHVlcyDigJQgdGhleSB3aWxsIGJlIGV2YWx1YXRlZCBwYXNzaW5nIGBkYXRhYCBhcyBhbiBhcmd1bWVudC5cclxuXHR0ZW1wbGF0ZTogZnVuY3Rpb24gKHN0ciwgZGF0YSkge1xyXG5cdFx0cmV0dXJuIHN0ci5yZXBsYWNlKEwuVXRpbC50ZW1wbGF0ZVJlLCBmdW5jdGlvbiAoc3RyLCBrZXkpIHtcclxuXHRcdFx0dmFyIHZhbHVlID0gZGF0YVtrZXldO1xyXG5cclxuXHRcdFx0aWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ05vIHZhbHVlIHByb3ZpZGVkIGZvciB2YXJpYWJsZSAnICsgc3RyKTtcclxuXHJcblx0XHRcdH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nKSB7XHJcblx0XHRcdFx0dmFsdWUgPSB2YWx1ZShkYXRhKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gdmFsdWU7XHJcblx0XHR9KTtcclxuXHR9LFxyXG5cclxuXHR0ZW1wbGF0ZVJlOiAvXFx7ICooW1xcd19cXC1dKykgKlxcfS9nLFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gaXNBcnJheShvYmopOiBCb29sZWFuXHJcblx0Ly8gQ29tcGF0aWJpbGl0eSBwb2x5ZmlsbCBmb3IgW0FycmF5LmlzQXJyYXldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2lzQXJyYXkpXHJcblx0aXNBcnJheTogQXJyYXkuaXNBcnJheSB8fCBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHRyZXR1cm4gKE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvYmopID09PSAnW29iamVjdCBBcnJheV0nKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gaW5kZXhPZihhcnJheTogQXJyYXksIGVsOiBPYmplY3QpOiBOdW1iZXJcclxuXHQvLyBDb21wYXRpYmlsaXR5IHBvbHlmaWxsIGZvciBbQXJyYXkucHJvdG90eXBlLmluZGV4T2ZdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2luZGV4T2YpXHJcblx0aW5kZXhPZjogZnVuY3Rpb24gKGFycmF5LCBlbCkge1xyXG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7IGkrKykge1xyXG5cdFx0XHRpZiAoYXJyYXlbaV0gPT09IGVsKSB7IHJldHVybiBpOyB9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gLTE7XHJcblx0fSxcclxuXHJcblx0Ly8gQHByb3BlcnR5IGVtcHR5SW1hZ2VVcmw6IFN0cmluZ1xyXG5cdC8vIERhdGEgVVJJIHN0cmluZyBjb250YWluaW5nIGEgYmFzZTY0LWVuY29kZWQgZW1wdHkgR0lGIGltYWdlLlxyXG5cdC8vIFVzZWQgYXMgYSBoYWNrIHRvIGZyZWUgbWVtb3J5IGZyb20gdW51c2VkIGltYWdlcyBvbiBXZWJLaXQtcG93ZXJlZFxyXG5cdC8vIG1vYmlsZSBkZXZpY2VzIChieSBzZXR0aW5nIGltYWdlIGBzcmNgIHRvIHRoaXMgc3RyaW5nKS5cclxuXHRlbXB0eUltYWdlVXJsOiAnZGF0YTppbWFnZS9naWY7YmFzZTY0LFIwbEdPRGxoQVFBQkFBRC9BQ3dBQUFBQUFRQUJBQUFDQURzPSdcclxufTtcclxuXHJcbihmdW5jdGlvbiAoKSB7XHJcblx0Ly8gaW5zcGlyZWQgYnkgaHR0cDovL3BhdWxpcmlzaC5jb20vMjAxMS9yZXF1ZXN0YW5pbWF0aW9uZnJhbWUtZm9yLXNtYXJ0LWFuaW1hdGluZy9cclxuXHJcblx0ZnVuY3Rpb24gZ2V0UHJlZml4ZWQobmFtZSkge1xyXG5cdFx0cmV0dXJuIHdpbmRvd1snd2Via2l0JyArIG5hbWVdIHx8IHdpbmRvd1snbW96JyArIG5hbWVdIHx8IHdpbmRvd1snbXMnICsgbmFtZV07XHJcblx0fVxyXG5cclxuXHR2YXIgbGFzdFRpbWUgPSAwO1xyXG5cclxuXHQvLyBmYWxsYmFjayBmb3IgSUUgNy04XHJcblx0ZnVuY3Rpb24gdGltZW91dERlZmVyKGZuKSB7XHJcblx0XHR2YXIgdGltZSA9ICtuZXcgRGF0ZSgpLFxyXG5cdFx0ICAgIHRpbWVUb0NhbGwgPSBNYXRoLm1heCgwLCAxNiAtICh0aW1lIC0gbGFzdFRpbWUpKTtcclxuXHJcblx0XHRsYXN0VGltZSA9IHRpbWUgKyB0aW1lVG9DYWxsO1xyXG5cdFx0cmV0dXJuIHdpbmRvdy5zZXRUaW1lb3V0KGZuLCB0aW1lVG9DYWxsKTtcclxuXHR9XHJcblxyXG5cdHZhciByZXF1ZXN0Rm4gPSB3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lIHx8IGdldFByZWZpeGVkKCdSZXF1ZXN0QW5pbWF0aW9uRnJhbWUnKSB8fCB0aW1lb3V0RGVmZXIsXHJcblx0ICAgIGNhbmNlbEZuID0gd2luZG93LmNhbmNlbEFuaW1hdGlvbkZyYW1lIHx8IGdldFByZWZpeGVkKCdDYW5jZWxBbmltYXRpb25GcmFtZScpIHx8XHJcblx0ICAgICAgICAgICAgICAgZ2V0UHJlZml4ZWQoJ0NhbmNlbFJlcXVlc3RBbmltYXRpb25GcmFtZScpIHx8IGZ1bmN0aW9uIChpZCkgeyB3aW5kb3cuY2xlYXJUaW1lb3V0KGlkKTsgfTtcclxuXHJcblxyXG5cdC8vIEBmdW5jdGlvbiByZXF1ZXN0QW5pbUZyYW1lKGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCwgaW1tZWRpYXRlPzogQm9vbGVhbik6IE51bWJlclxyXG5cdC8vIFNjaGVkdWxlcyBgZm5gIHRvIGJlIGV4ZWN1dGVkIHdoZW4gdGhlIGJyb3dzZXIgcmVwYWludHMuIGBmbmAgaXMgYm91bmQgdG9cclxuXHQvLyBgY29udGV4dGAgaWYgZ2l2ZW4uIFdoZW4gYGltbWVkaWF0ZWAgaXMgc2V0LCBgZm5gIGlzIGNhbGxlZCBpbW1lZGlhdGVseSBpZlxyXG5cdC8vIHRoZSBicm93c2VyIGRvZXNuJ3QgaGF2ZSBuYXRpdmUgc3VwcG9ydCBmb3JcclxuXHQvLyBbYHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWVgXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvd2luZG93L3JlcXVlc3RBbmltYXRpb25GcmFtZSksXHJcblx0Ly8gb3RoZXJ3aXNlIGl0J3MgZGVsYXllZC4gUmV0dXJucyBhIHJlcXVlc3QgSUQgdGhhdCBjYW4gYmUgdXNlZCB0byBjYW5jZWwgdGhlIHJlcXVlc3QuXHJcblx0TC5VdGlsLnJlcXVlc3RBbmltRnJhbWUgPSBmdW5jdGlvbiAoZm4sIGNvbnRleHQsIGltbWVkaWF0ZSkge1xyXG5cdFx0aWYgKGltbWVkaWF0ZSAmJiByZXF1ZXN0Rm4gPT09IHRpbWVvdXREZWZlcikge1xyXG5cdFx0XHRmbi5jYWxsKGNvbnRleHQpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0cmV0dXJuIHJlcXVlc3RGbi5jYWxsKHdpbmRvdywgTC5iaW5kKGZuLCBjb250ZXh0KSk7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGNhbmNlbEFuaW1GcmFtZShpZDogTnVtYmVyKTogdW5kZWZpbmVkXHJcblx0Ly8gQ2FuY2VscyBhIHByZXZpb3VzIGByZXF1ZXN0QW5pbUZyYW1lYC4gU2VlIGFsc28gW3dpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL3dpbmRvdy9jYW5jZWxBbmltYXRpb25GcmFtZSkuXHJcblx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSA9IGZ1bmN0aW9uIChpZCkge1xyXG5cdFx0aWYgKGlkKSB7XHJcblx0XHRcdGNhbmNlbEZuLmNhbGwod2luZG93LCBpZCk7XHJcblx0XHR9XHJcblx0fTtcclxufSkoKTtcclxuXHJcbi8vIHNob3J0Y3V0cyBmb3IgbW9zdCB1c2VkIHV0aWxpdHkgZnVuY3Rpb25zXHJcbkwuZXh0ZW5kID0gTC5VdGlsLmV4dGVuZDtcclxuTC5iaW5kID0gTC5VdGlsLmJpbmQ7XHJcbkwuc3RhbXAgPSBMLlV0aWwuc3RhbXA7XHJcbkwuc2V0T3B0aW9ucyA9IEwuVXRpbC5zZXRPcHRpb25zO1xyXG5cblxuXG5cclxuLy8gQGNsYXNzIENsYXNzXHJcbi8vIEBha2EgTC5DbGFzc1xyXG5cclxuLy8gQHNlY3Rpb25cclxuLy8gQHVuaW5oZXJpdGFibGVcclxuXHJcbi8vIFRoYW5rcyB0byBKb2huIFJlc2lnIGFuZCBEZWFuIEVkd2FyZHMgZm9yIGluc3BpcmF0aW9uIVxyXG5cclxuTC5DbGFzcyA9IGZ1bmN0aW9uICgpIHt9O1xyXG5cclxuTC5DbGFzcy5leHRlbmQgPSBmdW5jdGlvbiAocHJvcHMpIHtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGV4dGVuZChwcm9wczogT2JqZWN0KTogRnVuY3Rpb25cclxuXHQvLyBbRXh0ZW5kcyB0aGUgY3VycmVudCBjbGFzc10oI2NsYXNzLWluaGVyaXRhbmNlKSBnaXZlbiB0aGUgcHJvcGVydGllcyB0byBiZSBpbmNsdWRlZC5cclxuXHQvLyBSZXR1cm5zIGEgSmF2YXNjcmlwdCBmdW5jdGlvbiB0aGF0IGlzIGEgY2xhc3MgY29uc3RydWN0b3IgKHRvIGJlIGNhbGxlZCB3aXRoIGBuZXdgKS5cclxuXHR2YXIgTmV3Q2xhc3MgPSBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0Ly8gY2FsbCB0aGUgY29uc3RydWN0b3JcclxuXHRcdGlmICh0aGlzLmluaXRpYWxpemUpIHtcclxuXHRcdFx0dGhpcy5pbml0aWFsaXplLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gY2FsbCBhbGwgY29uc3RydWN0b3IgaG9va3NcclxuXHRcdHRoaXMuY2FsbEluaXRIb29rcygpO1xyXG5cdH07XHJcblxyXG5cdHZhciBwYXJlbnRQcm90byA9IE5ld0NsYXNzLl9fc3VwZXJfXyA9IHRoaXMucHJvdG90eXBlO1xyXG5cclxuXHR2YXIgcHJvdG8gPSBMLlV0aWwuY3JlYXRlKHBhcmVudFByb3RvKTtcclxuXHRwcm90by5jb25zdHJ1Y3RvciA9IE5ld0NsYXNzO1xyXG5cclxuXHROZXdDbGFzcy5wcm90b3R5cGUgPSBwcm90bztcclxuXHJcblx0Ly8gaW5oZXJpdCBwYXJlbnQncyBzdGF0aWNzXHJcblx0Zm9yICh2YXIgaSBpbiB0aGlzKSB7XHJcblx0XHRpZiAodGhpcy5oYXNPd25Qcm9wZXJ0eShpKSAmJiBpICE9PSAncHJvdG90eXBlJykge1xyXG5cdFx0XHROZXdDbGFzc1tpXSA9IHRoaXNbaV07XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHQvLyBtaXggc3RhdGljIHByb3BlcnRpZXMgaW50byB0aGUgY2xhc3NcclxuXHRpZiAocHJvcHMuc3RhdGljcykge1xyXG5cdFx0TC5leHRlbmQoTmV3Q2xhc3MsIHByb3BzLnN0YXRpY3MpO1xyXG5cdFx0ZGVsZXRlIHByb3BzLnN0YXRpY3M7XHJcblx0fVxyXG5cclxuXHQvLyBtaXggaW5jbHVkZXMgaW50byB0aGUgcHJvdG90eXBlXHJcblx0aWYgKHByb3BzLmluY2x1ZGVzKSB7XHJcblx0XHRMLlV0aWwuZXh0ZW5kLmFwcGx5KG51bGwsIFtwcm90b10uY29uY2F0KHByb3BzLmluY2x1ZGVzKSk7XHJcblx0XHRkZWxldGUgcHJvcHMuaW5jbHVkZXM7XHJcblx0fVxyXG5cclxuXHQvLyBtZXJnZSBvcHRpb25zXHJcblx0aWYgKHByb3RvLm9wdGlvbnMpIHtcclxuXHRcdHByb3BzLm9wdGlvbnMgPSBMLlV0aWwuZXh0ZW5kKEwuVXRpbC5jcmVhdGUocHJvdG8ub3B0aW9ucyksIHByb3BzLm9wdGlvbnMpO1xyXG5cdH1cclxuXHJcblx0Ly8gbWl4IGdpdmVuIHByb3BlcnRpZXMgaW50byB0aGUgcHJvdG90eXBlXHJcblx0TC5leHRlbmQocHJvdG8sIHByb3BzKTtcclxuXHJcblx0cHJvdG8uX2luaXRIb29rcyA9IFtdO1xyXG5cclxuXHQvLyBhZGQgbWV0aG9kIGZvciBjYWxsaW5nIGFsbCBob29rc1xyXG5cdHByb3RvLmNhbGxJbml0SG9va3MgPSBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2luaXRIb29rc0NhbGxlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAocGFyZW50UHJvdG8uY2FsbEluaXRIb29rcykge1xyXG5cdFx0XHRwYXJlbnRQcm90by5jYWxsSW5pdEhvb2tzLmNhbGwodGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdEhvb2tzQ2FsbGVkID0gdHJ1ZTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gcHJvdG8uX2luaXRIb29rcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRwcm90by5faW5pdEhvb2tzW2ldLmNhbGwodGhpcyk7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0cmV0dXJuIE5ld0NsYXNzO1xyXG59O1xyXG5cclxuXHJcbi8vIEBmdW5jdGlvbiBpbmNsdWRlKHByb3BlcnRpZXM6IE9iamVjdCk6IHRoaXNcclxuLy8gW0luY2x1ZGVzIGEgbWl4aW5dKCNjbGFzcy1pbmNsdWRlcykgaW50byB0aGUgY3VycmVudCBjbGFzcy5cclxuTC5DbGFzcy5pbmNsdWRlID0gZnVuY3Rpb24gKHByb3BzKSB7XHJcblx0TC5leHRlbmQodGhpcy5wcm90b3R5cGUsIHByb3BzKTtcclxuXHRyZXR1cm4gdGhpcztcclxufTtcclxuXHJcbi8vIEBmdW5jdGlvbiBtZXJnZU9wdGlvbnMob3B0aW9uczogT2JqZWN0KTogdGhpc1xyXG4vLyBbTWVyZ2VzIGBvcHRpb25zYF0oI2NsYXNzLW9wdGlvbnMpIGludG8gdGhlIGRlZmF1bHRzIG9mIHRoZSBjbGFzcy5cclxuTC5DbGFzcy5tZXJnZU9wdGlvbnMgPSBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdEwuZXh0ZW5kKHRoaXMucHJvdG90eXBlLm9wdGlvbnMsIG9wdGlvbnMpO1xyXG5cdHJldHVybiB0aGlzO1xyXG59O1xyXG5cclxuLy8gQGZ1bmN0aW9uIGFkZEluaXRIb29rKGZuOiBGdW5jdGlvbik6IHRoaXNcclxuLy8gQWRkcyBhIFtjb25zdHJ1Y3RvciBob29rXSgjY2xhc3MtY29uc3RydWN0b3ItaG9va3MpIHRvIHRoZSBjbGFzcy5cclxuTC5DbGFzcy5hZGRJbml0SG9vayA9IGZ1bmN0aW9uIChmbikgeyAvLyAoRnVuY3Rpb24pIHx8IChTdHJpbmcsIGFyZ3MuLi4pXHJcblx0dmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMsIDEpO1xyXG5cclxuXHR2YXIgaW5pdCA9IHR5cGVvZiBmbiA9PT0gJ2Z1bmN0aW9uJyA/IGZuIDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpc1tmbl0uYXBwbHkodGhpcywgYXJncyk7XHJcblx0fTtcclxuXHJcblx0dGhpcy5wcm90b3R5cGUuX2luaXRIb29rcyA9IHRoaXMucHJvdG90eXBlLl9pbml0SG9va3MgfHwgW107XHJcblx0dGhpcy5wcm90b3R5cGUuX2luaXRIb29rcy5wdXNoKGluaXQpO1xyXG5cdHJldHVybiB0aGlzO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgRXZlbnRlZFxyXG4gKiBAYWthIEwuRXZlbnRlZFxyXG4gKiBAaW5oZXJpdHMgQ2xhc3NcclxuICpcclxuICogQSBzZXQgb2YgbWV0aG9kcyBzaGFyZWQgYmV0d2VlbiBldmVudC1wb3dlcmVkIGNsYXNzZXMgKGxpa2UgYE1hcGAgYW5kIGBNYXJrZXJgKS4gR2VuZXJhbGx5LCBldmVudHMgYWxsb3cgeW91IHRvIGV4ZWN1dGUgc29tZSBmdW5jdGlvbiB3aGVuIHNvbWV0aGluZyBoYXBwZW5zIHdpdGggYW4gb2JqZWN0IChlLmcuIHRoZSB1c2VyIGNsaWNrcyBvbiB0aGUgbWFwLCBjYXVzaW5nIHRoZSBtYXAgdG8gZmlyZSBgJ2NsaWNrJ2AgZXZlbnQpLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBtYXAub24oJ2NsaWNrJywgZnVuY3Rpb24oZSkge1xyXG4gKiBcdGFsZXJ0KGUubGF0bG5nKTtcclxuICogfSApO1xyXG4gKiBgYGBcclxuICpcclxuICogTGVhZmxldCBkZWFscyB3aXRoIGV2ZW50IGxpc3RlbmVycyBieSByZWZlcmVuY2UsIHNvIGlmIHlvdSB3YW50IHRvIGFkZCBhIGxpc3RlbmVyIGFuZCB0aGVuIHJlbW92ZSBpdCwgZGVmaW5lIGl0IGFzIGEgZnVuY3Rpb246XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIGZ1bmN0aW9uIG9uQ2xpY2soZSkgeyAuLi4gfVxyXG4gKlxyXG4gKiBtYXAub24oJ2NsaWNrJywgb25DbGljayk7XHJcbiAqIG1hcC5vZmYoJ2NsaWNrJywgb25DbGljayk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcblxyXG5MLkV2ZW50ZWQgPSBMLkNsYXNzLmV4dGVuZCh7XHJcblxyXG5cdC8qIEBtZXRob2Qgb24odHlwZTogU3RyaW5nLCBmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0ICogQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIChgZm5gKSB0byBhIHBhcnRpY3VsYXIgZXZlbnQgdHlwZSBvZiB0aGUgb2JqZWN0LiBZb3UgY2FuIG9wdGlvbmFsbHkgc3BlY2lmeSB0aGUgY29udGV4dCBvZiB0aGUgbGlzdGVuZXIgKG9iamVjdCB0aGUgdGhpcyBrZXl3b3JkIHdpbGwgcG9pbnQgdG8pLiBZb3UgY2FuIGFsc28gcGFzcyBzZXZlcmFsIHNwYWNlLXNlcGFyYXRlZCB0eXBlcyAoZS5nLiBgJ2NsaWNrIGRibGNsaWNrJ2ApLlxyXG5cdCAqXHJcblx0ICogQGFsdGVybmF0aXZlXHJcblx0ICogQG1ldGhvZCBvbihldmVudE1hcDogT2JqZWN0KTogdGhpc1xyXG5cdCAqIEFkZHMgYSBzZXQgb2YgdHlwZS9saXN0ZW5lciBwYWlycywgZS5nLiBge2NsaWNrOiBvbkNsaWNrLCBtb3VzZW1vdmU6IG9uTW91c2VNb3ZlfWBcclxuXHQgKi9cclxuXHRvbjogZnVuY3Rpb24gKHR5cGVzLCBmbiwgY29udGV4dCkge1xyXG5cclxuXHRcdC8vIHR5cGVzIGNhbiBiZSBhIG1hcCBvZiB0eXBlcy9oYW5kbGVyc1xyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdC8vIHdlIGRvbid0IHByb2Nlc3Mgc3BhY2Utc2VwYXJhdGVkIGV2ZW50cyBoZXJlIGZvciBwZXJmb3JtYW5jZTtcclxuXHRcdFx0XHQvLyBpdCdzIGEgaG90IHBhdGggc2luY2UgTGF5ZXIgdXNlcyB0aGUgb24ob2JqKSBzeW50YXhcclxuXHRcdFx0XHR0aGlzLl9vbih0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0Ly8gdHlwZXMgY2FuIGJlIGEgc3RyaW5nIG9mIHNwYWNlLXNlcGFyYXRlZCB3b3Jkc1xyXG5cdFx0XHR0eXBlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0eXBlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuX29uKHR5cGVzW2ldLCBmbiwgY29udGV4dCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvKiBAbWV0aG9kIG9mZih0eXBlOiBTdHJpbmcsIGZuPzogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0ICogUmVtb3ZlcyBhIHByZXZpb3VzbHkgYWRkZWQgbGlzdGVuZXIgZnVuY3Rpb24uIElmIG5vIGZ1bmN0aW9uIGlzIHNwZWNpZmllZCwgaXQgd2lsbCByZW1vdmUgYWxsIHRoZSBsaXN0ZW5lcnMgb2YgdGhhdCBwYXJ0aWN1bGFyIGV2ZW50IGZyb20gdGhlIG9iamVjdC4gTm90ZSB0aGF0IGlmIHlvdSBwYXNzZWQgYSBjdXN0b20gY29udGV4dCB0byBgb25gLCB5b3UgbXVzdCBwYXNzIHRoZSBzYW1lIGNvbnRleHQgdG8gYG9mZmAgaW4gb3JkZXIgdG8gcmVtb3ZlIHRoZSBsaXN0ZW5lci5cclxuXHQgKlxyXG5cdCAqIEBhbHRlcm5hdGl2ZVxyXG5cdCAqIEBtZXRob2Qgb2ZmKGV2ZW50TWFwOiBPYmplY3QpOiB0aGlzXHJcblx0ICogUmVtb3ZlcyBhIHNldCBvZiB0eXBlL2xpc3RlbmVyIHBhaXJzLlxyXG5cdCAqXHJcblx0ICogQGFsdGVybmF0aXZlXHJcblx0ICogQG1ldGhvZCBvZmY6IHRoaXNcclxuXHQgKiBSZW1vdmVzIGFsbCBsaXN0ZW5lcnMgdG8gYWxsIGV2ZW50cyBvbiB0aGUgb2JqZWN0LlxyXG5cdCAqL1xyXG5cdG9mZjogZnVuY3Rpb24gKHR5cGVzLCBmbiwgY29udGV4dCkge1xyXG5cclxuXHRcdGlmICghdHlwZXMpIHtcclxuXHRcdFx0Ly8gY2xlYXIgYWxsIGxpc3RlbmVycyBpZiBjYWxsZWQgd2l0aG91dCBhcmd1bWVudHNcclxuXHRcdFx0ZGVsZXRlIHRoaXMuX2V2ZW50cztcclxuXHJcblx0XHR9IGVsc2UgaWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdHRoaXMuX29mZih0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0dHlwZXMgPSBMLlV0aWwuc3BsaXRXb3Jkcyh0eXBlcyk7XHJcblxyXG5cdFx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gdHlwZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHR0aGlzLl9vZmYodHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIGF0dGFjaCBsaXN0ZW5lciAod2l0aG91dCBzeW50YWN0aWMgc3VnYXIgbm93KVxyXG5cdF9vbjogZnVuY3Rpb24gKHR5cGUsIGZuLCBjb250ZXh0KSB7XHJcblx0XHR0aGlzLl9ldmVudHMgPSB0aGlzLl9ldmVudHMgfHwge307XHJcblxyXG5cdFx0LyogZ2V0L2luaXQgbGlzdGVuZXJzIGZvciB0eXBlICovXHJcblx0XHR2YXIgdHlwZUxpc3RlbmVycyA9IHRoaXMuX2V2ZW50c1t0eXBlXTtcclxuXHRcdGlmICghdHlwZUxpc3RlbmVycykge1xyXG5cdFx0XHR0eXBlTGlzdGVuZXJzID0gW107XHJcblx0XHRcdHRoaXMuX2V2ZW50c1t0eXBlXSA9IHR5cGVMaXN0ZW5lcnM7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGNvbnRleHQgPT09IHRoaXMpIHtcclxuXHRcdFx0Ly8gTGVzcyBtZW1vcnkgZm9vdHByaW50LlxyXG5cdFx0XHRjb250ZXh0ID0gdW5kZWZpbmVkO1xyXG5cdFx0fVxyXG5cdFx0dmFyIG5ld0xpc3RlbmVyID0ge2ZuOiBmbiwgY3R4OiBjb250ZXh0fSxcclxuXHRcdCAgICBsaXN0ZW5lcnMgPSB0eXBlTGlzdGVuZXJzO1xyXG5cclxuXHRcdC8vIGNoZWNrIGlmIGZuIGFscmVhZHkgdGhlcmVcclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0aWYgKGxpc3RlbmVyc1tpXS5mbiA9PT0gZm4gJiYgbGlzdGVuZXJzW2ldLmN0eCA9PT0gY29udGV4dCkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdGxpc3RlbmVycy5wdXNoKG5ld0xpc3RlbmVyKTtcclxuXHR9LFxyXG5cclxuXHRfb2ZmOiBmdW5jdGlvbiAodHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHRcdHZhciBsaXN0ZW5lcnMsXHJcblx0XHQgICAgaSxcclxuXHRcdCAgICBsZW47XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9ldmVudHMpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0bGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cclxuXHRcdGlmICghbGlzdGVuZXJzKSB7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIWZuKSB7XHJcblx0XHRcdC8vIFNldCBhbGwgcmVtb3ZlZCBsaXN0ZW5lcnMgdG8gbm9vcCBzbyB0aGV5IGFyZSBub3QgY2FsbGVkIGlmIHJlbW92ZSBoYXBwZW5zIGluIGZpcmVcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gbGlzdGVuZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0bGlzdGVuZXJzW2ldLmZuID0gTC5VdGlsLmZhbHNlRm47XHJcblx0XHRcdH1cclxuXHRcdFx0Ly8gY2xlYXIgYWxsIGxpc3RlbmVycyBmb3IgYSB0eXBlIGlmIGZ1bmN0aW9uIGlzbid0IHNwZWNpZmllZFxyXG5cdFx0XHRkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGNvbnRleHQgPT09IHRoaXMpIHtcclxuXHRcdFx0Y29udGV4dCA9IHVuZGVmaW5lZDtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAobGlzdGVuZXJzKSB7XHJcblxyXG5cdFx0XHQvLyBmaW5kIGZuIGFuZCByZW1vdmUgaXRcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gbGlzdGVuZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dmFyIGwgPSBsaXN0ZW5lcnNbaV07XHJcblx0XHRcdFx0aWYgKGwuY3R4ICE9PSBjb250ZXh0KSB7IGNvbnRpbnVlOyB9XHJcblx0XHRcdFx0aWYgKGwuZm4gPT09IGZuKSB7XHJcblxyXG5cdFx0XHRcdFx0Ly8gc2V0IHRoZSByZW1vdmVkIGxpc3RlbmVyIHRvIG5vb3Agc28gdGhhdCdzIG5vdCBjYWxsZWQgaWYgcmVtb3ZlIGhhcHBlbnMgaW4gZmlyZVxyXG5cdFx0XHRcdFx0bC5mbiA9IEwuVXRpbC5mYWxzZUZuO1xyXG5cclxuXHRcdFx0XHRcdGlmICh0aGlzLl9maXJpbmdDb3VudCkge1xyXG5cdFx0XHRcdFx0XHQvKiBjb3B5IGFycmF5IGluIGNhc2UgZXZlbnRzIGFyZSBiZWluZyBmaXJlZCAqL1xyXG5cdFx0XHRcdFx0XHR0aGlzLl9ldmVudHNbdHlwZV0gPSBsaXN0ZW5lcnMgPSBsaXN0ZW5lcnMuc2xpY2UoKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdGxpc3RlbmVycy5zcGxpY2UoaSwgMSk7XHJcblxyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZmlyZSh0eXBlOiBTdHJpbmcsIGRhdGE/OiBPYmplY3QsIHByb3BhZ2F0ZT86IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gRmlyZXMgYW4gZXZlbnQgb2YgdGhlIHNwZWNpZmllZCB0eXBlLiBZb3UgY2FuIG9wdGlvbmFsbHkgcHJvdmlkZSBhbiBkYXRhXHJcblx0Ly8gb2JqZWN0IOKAlCB0aGUgZmlyc3QgYXJndW1lbnQgb2YgdGhlIGxpc3RlbmVyIGZ1bmN0aW9uIHdpbGwgY29udGFpbiBpdHNcclxuXHQvLyBwcm9wZXJ0aWVzLiBUaGUgZXZlbnQgY2FuIG9wdGlvbmFsbHkgYmUgcHJvcGFnYXRlZCB0byBldmVudCBwYXJlbnRzLlxyXG5cdGZpcmU6IGZ1bmN0aW9uICh0eXBlLCBkYXRhLCBwcm9wYWdhdGUpIHtcclxuXHRcdGlmICghdGhpcy5saXN0ZW5zKHR5cGUsIHByb3BhZ2F0ZSkpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHR2YXIgZXZlbnQgPSBMLlV0aWwuZXh0ZW5kKHt9LCBkYXRhLCB7dHlwZTogdHlwZSwgdGFyZ2V0OiB0aGlzfSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2V2ZW50cykge1xyXG5cdFx0XHR2YXIgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cclxuXHRcdFx0aWYgKGxpc3RlbmVycykge1xyXG5cdFx0XHRcdHRoaXMuX2ZpcmluZ0NvdW50ID0gKHRoaXMuX2ZpcmluZ0NvdW50ICsgMSkgfHwgMTtcclxuXHRcdFx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGlzdGVuZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0XHR2YXIgbCA9IGxpc3RlbmVyc1tpXTtcclxuXHRcdFx0XHRcdGwuZm4uY2FsbChsLmN0eCB8fCB0aGlzLCBldmVudCk7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHR0aGlzLl9maXJpbmdDb3VudC0tO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHByb3BhZ2F0ZSkge1xyXG5cdFx0XHQvLyBwcm9wYWdhdGUgdGhlIGV2ZW50IHRvIHBhcmVudHMgKHNldCB3aXRoIGFkZEV2ZW50UGFyZW50KVxyXG5cdFx0XHR0aGlzLl9wcm9wYWdhdGVFdmVudChldmVudCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsaXN0ZW5zKHR5cGU6IFN0cmluZyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBhIHBhcnRpY3VsYXIgZXZlbnQgdHlwZSBoYXMgYW55IGxpc3RlbmVycyBhdHRhY2hlZCB0byBpdC5cclxuXHRsaXN0ZW5zOiBmdW5jdGlvbiAodHlwZSwgcHJvcGFnYXRlKSB7XHJcblx0XHR2YXIgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzICYmIHRoaXMuX2V2ZW50c1t0eXBlXTtcclxuXHRcdGlmIChsaXN0ZW5lcnMgJiYgbGlzdGVuZXJzLmxlbmd0aCkgeyByZXR1cm4gdHJ1ZTsgfVxyXG5cclxuXHRcdGlmIChwcm9wYWdhdGUpIHtcclxuXHRcdFx0Ly8gYWxzbyBjaGVjayBwYXJlbnRzIGZvciBsaXN0ZW5lcnMgaWYgZXZlbnQgcHJvcGFnYXRlc1xyXG5cdFx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9ldmVudFBhcmVudHMpIHtcclxuXHRcdFx0XHRpZiAodGhpcy5fZXZlbnRQYXJlbnRzW2lkXS5saXN0ZW5zKHR5cGUsIHByb3BhZ2F0ZSkpIHsgcmV0dXJuIHRydWU7IH1cclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGZhbHNlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgb25jZSjigKYpOiB0aGlzXHJcblx0Ly8gQmVoYXZlcyBhcyBbYG9uKOKApilgXSgjZXZlbnRlZC1vbiksIGV4Y2VwdCB0aGUgbGlzdGVuZXIgd2lsbCBvbmx5IGdldCBmaXJlZCBvbmNlIGFuZCB0aGVuIHJlbW92ZWQuXHJcblx0b25jZTogZnVuY3Rpb24gKHR5cGVzLCBmbiwgY29udGV4dCkge1xyXG5cclxuXHRcdGlmICh0eXBlb2YgdHlwZXMgPT09ICdvYmplY3QnKSB7XHJcblx0XHRcdGZvciAodmFyIHR5cGUgaW4gdHlwZXMpIHtcclxuXHRcdFx0XHR0aGlzLm9uY2UodHlwZSwgdHlwZXNbdHlwZV0sIGZuKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgaGFuZGxlciA9IEwuYmluZChmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXNcclxuXHRcdFx0ICAgIC5vZmYodHlwZXMsIGZuLCBjb250ZXh0KVxyXG5cdFx0XHQgICAgLm9mZih0eXBlcywgaGFuZGxlciwgY29udGV4dCk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHQvLyBhZGQgYSBsaXN0ZW5lciB0aGF0J3MgZXhlY3V0ZWQgb25jZSBhbmQgcmVtb3ZlZCBhZnRlciB0aGF0XHJcblx0XHRyZXR1cm4gdGhpc1xyXG5cdFx0ICAgIC5vbih0eXBlcywgZm4sIGNvbnRleHQpXHJcblx0XHQgICAgLm9uKHR5cGVzLCBoYW5kbGVyLCBjb250ZXh0KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZEV2ZW50UGFyZW50KG9iajogRXZlbnRlZCk6IHRoaXNcclxuXHQvLyBBZGRzIGFuIGV2ZW50IHBhcmVudCAtIGFuIGBFdmVudGVkYCB0aGF0IHdpbGwgcmVjZWl2ZSBwcm9wYWdhdGVkIGV2ZW50c1xyXG5cdGFkZEV2ZW50UGFyZW50OiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHR0aGlzLl9ldmVudFBhcmVudHMgPSB0aGlzLl9ldmVudFBhcmVudHMgfHwge307XHJcblx0XHR0aGlzLl9ldmVudFBhcmVudHNbTC5zdGFtcChvYmopXSA9IG9iajtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlRXZlbnRQYXJlbnQob2JqOiBFdmVudGVkKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYW4gZXZlbnQgcGFyZW50LCBzbyBpdCB3aWxsIHN0b3AgcmVjZWl2aW5nIHByb3BhZ2F0ZWQgZXZlbnRzXHJcblx0cmVtb3ZlRXZlbnRQYXJlbnQ6IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdGlmICh0aGlzLl9ldmVudFBhcmVudHMpIHtcclxuXHRcdFx0ZGVsZXRlIHRoaXMuX2V2ZW50UGFyZW50c1tMLnN0YW1wKG9iaildO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X3Byb3BhZ2F0ZUV2ZW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fZXZlbnRQYXJlbnRzKSB7XHJcblx0XHRcdHRoaXMuX2V2ZW50UGFyZW50c1tpZF0uZmlyZShlLnR5cGUsIEwuZXh0ZW5kKHtsYXllcjogZS50YXJnZXR9LCBlKSwgdHJ1ZSk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbnZhciBwcm90byA9IEwuRXZlbnRlZC5wcm90b3R5cGU7XHJcblxyXG4vLyBhbGlhc2VzOyB3ZSBzaG91bGQgZGl0Y2ggdGhvc2UgZXZlbnR1YWxseVxyXG5cclxuLy8gQG1ldGhvZCBhZGRFdmVudExpc3RlbmVyKOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BvbijigKYpYF0oI2V2ZW50ZWQtb24pXHJcbnByb3RvLmFkZEV2ZW50TGlzdGVuZXIgPSBwcm90by5vbjtcclxuXHJcbi8vIEBtZXRob2QgcmVtb3ZlRXZlbnRMaXN0ZW5lcijigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgb2ZmKOKApilgXSgjZXZlbnRlZC1vZmYpXHJcblxyXG4vLyBAbWV0aG9kIGNsZWFyQWxsRXZlbnRMaXN0ZW5lcnMo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYG9mZigpYF0oI2V2ZW50ZWQtb2ZmKVxyXG5wcm90by5yZW1vdmVFdmVudExpc3RlbmVyID0gcHJvdG8uY2xlYXJBbGxFdmVudExpc3RlbmVycyA9IHByb3RvLm9mZjtcclxuXHJcbi8vIEBtZXRob2QgYWRkT25lVGltZUV2ZW50TGlzdGVuZXIo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYG9uY2Uo4oCmKWBdKCNldmVudGVkLW9uY2UpXHJcbnByb3RvLmFkZE9uZVRpbWVFdmVudExpc3RlbmVyID0gcHJvdG8ub25jZTtcclxuXHJcbi8vIEBtZXRob2QgZmlyZUV2ZW50KOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BmaXJlKOKApilgXSgjZXZlbnRlZC1maXJlKVxyXG5wcm90by5maXJlRXZlbnQgPSBwcm90by5maXJlO1xyXG5cclxuLy8gQG1ldGhvZCBoYXNFdmVudExpc3RlbmVycyjigKYpOiBCb29sZWFuXHJcbi8vIEFsaWFzIHRvIFtgbGlzdGVucyjigKYpYF0oI2V2ZW50ZWQtbGlzdGVucylcclxucHJvdG8uaGFzRXZlbnRMaXN0ZW5lcnMgPSBwcm90by5saXN0ZW5zO1xyXG5cclxuTC5NaXhpbiA9IHtFdmVudHM6IHByb3RvfTtcclxuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBCcm93c2VyXHJcbiAqIEBha2EgTC5Ccm93c2VyXHJcbiAqXHJcbiAqIEEgbmFtZXNwYWNlIHdpdGggc3RhdGljIHByb3BlcnRpZXMgZm9yIGJyb3dzZXIvZmVhdHVyZSBkZXRlY3Rpb24gdXNlZCBieSBMZWFmbGV0IGludGVybmFsbHkuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIGlmIChMLkJyb3dzZXIuaWVsdDkpIHtcclxuICogICBhbGVydCgnVXBncmFkZSB5b3VyIGJyb3dzZXIsIGR1ZGUhJyk7XHJcbiAqIH1cclxuICogYGBgXHJcbiAqL1xyXG5cclxuKGZ1bmN0aW9uICgpIHtcclxuXHJcblx0dmFyIHVhID0gbmF2aWdhdG9yLnVzZXJBZ2VudC50b0xvd2VyQ2FzZSgpLFxyXG5cdCAgICBkb2MgPSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQsXHJcblxyXG5cdCAgICBpZSA9ICdBY3RpdmVYT2JqZWN0JyBpbiB3aW5kb3csXHJcblxyXG5cdCAgICB3ZWJraXQgICAgPSB1YS5pbmRleE9mKCd3ZWJraXQnKSAhPT0gLTEsXHJcblx0ICAgIHBoYW50b21qcyA9IHVhLmluZGV4T2YoJ3BoYW50b20nKSAhPT0gLTEsXHJcblx0ICAgIGFuZHJvaWQyMyA9IHVhLnNlYXJjaCgnYW5kcm9pZCBbMjNdJykgIT09IC0xLFxyXG5cdCAgICBjaHJvbWUgICAgPSB1YS5pbmRleE9mKCdjaHJvbWUnKSAhPT0gLTEsXHJcblx0ICAgIGdlY2tvICAgICA9IHVhLmluZGV4T2YoJ2dlY2tvJykgIT09IC0xICAmJiAhd2Via2l0ICYmICF3aW5kb3cub3BlcmEgJiYgIWllLFxyXG5cclxuXHQgICAgd2luID0gbmF2aWdhdG9yLnBsYXRmb3JtLmluZGV4T2YoJ1dpbicpID09PSAwLFxyXG5cclxuXHQgICAgbW9iaWxlID0gdHlwZW9mIG9yaWVudGF0aW9uICE9PSAndW5kZWZpbmVkJyB8fCB1YS5pbmRleE9mKCdtb2JpbGUnKSAhPT0gLTEsXHJcblx0ICAgIG1zUG9pbnRlciA9ICF3aW5kb3cuUG9pbnRlckV2ZW50ICYmIHdpbmRvdy5NU1BvaW50ZXJFdmVudCxcclxuXHQgICAgcG9pbnRlciA9IHdpbmRvdy5Qb2ludGVyRXZlbnQgfHwgbXNQb2ludGVyLFxyXG5cclxuXHQgICAgaWUzZCA9IGllICYmICgndHJhbnNpdGlvbicgaW4gZG9jLnN0eWxlKSxcclxuXHQgICAgd2Via2l0M2QgPSAoJ1dlYktpdENTU01hdHJpeCcgaW4gd2luZG93KSAmJiAoJ20xMScgaW4gbmV3IHdpbmRvdy5XZWJLaXRDU1NNYXRyaXgoKSkgJiYgIWFuZHJvaWQyMyxcclxuXHQgICAgZ2Vja28zZCA9ICdNb3pQZXJzcGVjdGl2ZScgaW4gZG9jLnN0eWxlLFxyXG5cdCAgICBvcGVyYTEyID0gJ09UcmFuc2l0aW9uJyBpbiBkb2Muc3R5bGU7XHJcblxyXG5cclxuXHR2YXIgdG91Y2ggPSAhd2luZG93LkxfTk9fVE9VQ0ggJiYgKHBvaW50ZXIgfHwgJ29udG91Y2hzdGFydCcgaW4gd2luZG93IHx8XHJcblx0XHRcdCh3aW5kb3cuRG9jdW1lbnRUb3VjaCAmJiBkb2N1bWVudCBpbnN0YW5jZW9mIHdpbmRvdy5Eb2N1bWVudFRvdWNoKSk7XHJcblxyXG5cdEwuQnJvd3NlciA9IHtcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgaWU6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIEludGVybmV0IEV4cGxvcmVyIHZlcnNpb25zIChub3QgRWRnZSkuXHJcblx0XHRpZTogaWUsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGllbHQ5OiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIEludGVybmV0IEV4cGxvcmVyIHZlcnNpb25zIGxlc3MgdGhhbiA5LlxyXG5cdFx0aWVsdDk6IGllICYmICFkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBlZGdlOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHRoZSBFZGdlIHdlYiBicm93c2VyLlxyXG5cdFx0ZWRnZTogJ21zTGF1bmNoVXJpJyBpbiBuYXZpZ2F0b3IgJiYgISgnZG9jdW1lbnRNb2RlJyBpbiBkb2N1bWVudCksXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHdlYmtpdDogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciB3ZWJraXQtYmFzZWQgYnJvd3NlcnMgbGlrZSBDaHJvbWUgYW5kIFNhZmFyaSAoaW5jbHVkaW5nIG1vYmlsZSB2ZXJzaW9ucykuXHJcblx0XHR3ZWJraXQ6IHdlYmtpdCxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgZ2Vja286IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgZ2Vja28tYmFzZWQgYnJvd3NlcnMgbGlrZSBGaXJlZm94LlxyXG5cdFx0Z2Vja286IGdlY2tvLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBhbmRyb2lkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFueSBicm93c2VyIHJ1bm5pbmcgb24gYW4gQW5kcm9pZCBwbGF0Zm9ybS5cclxuXHRcdGFuZHJvaWQ6IHVhLmluZGV4T2YoJ2FuZHJvaWQnKSAhPT0gLTEsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGFuZHJvaWQyMzogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBicm93c2VycyBydW5uaW5nIG9uIEFuZHJvaWQgMiBvciBBbmRyb2lkIDMuXHJcblx0XHRhbmRyb2lkMjM6IGFuZHJvaWQyMyxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgY2hyb21lOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHRoZSBDaHJvbWUgYnJvd3Nlci5cclxuXHRcdGNocm9tZTogY2hyb21lLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBzYWZhcmk6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIFNhZmFyaSBicm93c2VyLlxyXG5cdFx0c2FmYXJpOiAhY2hyb21lICYmIHVhLmluZGV4T2YoJ3NhZmFyaScpICE9PSAtMSxcclxuXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHdpbjogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIHdoZW4gdGhlIGJyb3dzZXIgaXMgcnVubmluZyBpbiBhIFdpbmRvd3MgcGxhdGZvcm1cclxuXHRcdHdpbjogd2luLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgaWUzZDogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgSW50ZXJuZXQgRXhwbG9yZXIgdmVyc2lvbnMgc3VwcG9ydGluZyBDU1MgdHJhbnNmb3Jtcy5cclxuXHRcdGllM2Q6IGllM2QsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHdlYmtpdDNkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHdlYmtpdC1iYXNlZCBicm93c2VycyBzdXBwb3J0aW5nIENTUyB0cmFuc2Zvcm1zLlxyXG5cdFx0d2Via2l0M2Q6IHdlYmtpdDNkLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBnZWNrbzNkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGdlY2tvLWJhc2VkIGJyb3dzZXJzIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcblx0XHRnZWNrbzNkOiBnZWNrbzNkLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBvcGVyYTEyOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHRoZSBPcGVyYSBicm93c2VyIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMgKHZlcnNpb24gMTIgb3IgbGF0ZXIpLlxyXG5cdFx0b3BlcmExMjogb3BlcmExMixcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgYW55M2Q6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIGJyb3dzZXJzIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcblx0XHRhbnkzZDogIXdpbmRvdy5MX0RJU0FCTEVfM0QgJiYgKGllM2QgfHwgd2Via2l0M2QgfHwgZ2Vja28zZCkgJiYgIW9wZXJhMTIgJiYgIXBoYW50b21qcyxcclxuXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG1vYmlsZTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgYnJvd3NlcnMgcnVubmluZyBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcblx0XHRtb2JpbGU6IG1vYmlsZSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbW9iaWxlV2Via2l0OiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCB3ZWJraXQtYmFzZWQgYnJvd3NlcnMgaW4gYSBtb2JpbGUgZGV2aWNlLlxyXG5cdFx0bW9iaWxlV2Via2l0OiBtb2JpbGUgJiYgd2Via2l0LFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtb2JpbGVXZWJraXQzZDogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgd2Via2l0LWJhc2VkIGJyb3dzZXJzIGluIGEgbW9iaWxlIGRldmljZSBzdXBwb3J0aW5nIENTUyB0cmFuc2Zvcm1zLlxyXG5cdFx0bW9iaWxlV2Via2l0M2Q6IG1vYmlsZSAmJiB3ZWJraXQzZCxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbW9iaWxlT3BlcmE6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIE9wZXJhIGJyb3dzZXIgaW4gYSBtb2JpbGUgZGV2aWNlLlxyXG5cdFx0bW9iaWxlT3BlcmE6IG1vYmlsZSAmJiB3aW5kb3cub3BlcmEsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG1vYmlsZUdlY2tvOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGdlY2tvLWJhc2VkIGJyb3dzZXJzIHJ1bm5pbmcgaW4gYSBtb2JpbGUgZGV2aWNlLlxyXG5cdFx0bW9iaWxlR2Vja286IG1vYmlsZSAmJiBnZWNrbyxcclxuXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHRvdWNoOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCBicm93c2VycyBzdXBwb3J0aW5nIFt0b3VjaCBldmVudHNdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9Ub3VjaF9ldmVudHMpLlxyXG5cdFx0Ly8gVGhpcyBkb2VzIG5vdCBuZWNlc3NhcmlseSBtZWFuIHRoYXQgdGhlIGJyb3dzZXIgaXMgcnVubmluZyBpbiBhIGNvbXB1dGVyIHdpdGhcclxuXHRcdC8vIGEgdG91Y2hzY3JlZW4sIGl0IG9ubHkgbWVhbnMgdGhhdCB0aGUgYnJvd3NlciBpcyBjYXBhYmxlIG9mIHVuZGVyc3RhbmRpbmdcclxuXHRcdC8vIHRvdWNoIGV2ZW50cy5cclxuXHRcdHRvdWNoOiAhIXRvdWNoLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtc1BvaW50ZXI6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYnJvd3NlcnMgaW1wbGVtZW50aW5nIHRoZSBNaWNyb3NvZnQgdG91Y2ggZXZlbnRzIG1vZGVsIChub3RhYmx5IElFMTApLlxyXG5cdFx0bXNQb2ludGVyOiAhIW1zUG9pbnRlcixcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgcG9pbnRlcjogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgYnJvd3NlcnMgc3VwcG9ydGluZyBbcG9pbnRlciBldmVudHNdKGh0dHBzOi8vbXNkbi5taWNyb3NvZnQuY29tL2VuLXVzL2xpYnJhcnkvZG40MzMyNDQlMjh2PXZzLjg1JTI5LmFzcHgpLlxyXG5cdFx0cG9pbnRlcjogISFwb2ludGVyLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgcmV0aW5hOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGJyb3dzZXJzIG9uIGEgaGlnaC1yZXNvbHV0aW9uIFwicmV0aW5hXCIgc2NyZWVuLlxyXG5cdFx0cmV0aW5hOiAod2luZG93LmRldmljZVBpeGVsUmF0aW8gfHwgKHdpbmRvdy5zY3JlZW4uZGV2aWNlWERQSSAvIHdpbmRvdy5zY3JlZW4ubG9naWNhbFhEUEkpKSA+IDFcclxuXHR9O1xyXG5cclxufSgpKTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIFBvaW50XHJcbiAqIEBha2EgTC5Qb2ludFxyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgcG9pbnQgd2l0aCBgeGAgYW5kIGB5YCBjb29yZGluYXRlcyBpbiBwaXhlbHMuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBwb2ludCA9IEwucG9pbnQoMjAwLCAzMDApO1xyXG4gKiBgYGBcclxuICpcclxuICogQWxsIExlYWZsZXQgbWV0aG9kcyBhbmQgb3B0aW9ucyB0aGF0IGFjY2VwdCBgUG9pbnRgIG9iamVjdHMgYWxzbyBhY2NlcHQgdGhlbSBpbiBhIHNpbXBsZSBBcnJheSBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlc2UgbGluZXMgYXJlIGVxdWl2YWxlbnQ6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIG1hcC5wYW5CeShbMjAwLCAzMDBdKTtcclxuICogbWFwLnBhbkJ5KEwucG9pbnQoMjAwLCAzMDApKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5Qb2ludCA9IGZ1bmN0aW9uICh4LCB5LCByb3VuZCkge1xyXG5cdC8vIEBwcm9wZXJ0eSB4OiBOdW1iZXI7IFRoZSBgeGAgY29vcmRpbmF0ZSBvZiB0aGUgcG9pbnRcclxuXHR0aGlzLnggPSAocm91bmQgPyBNYXRoLnJvdW5kKHgpIDogeCk7XHJcblx0Ly8gQHByb3BlcnR5IHk6IE51bWJlcjsgVGhlIGB5YCBjb29yZGluYXRlIG9mIHRoZSBwb2ludFxyXG5cdHRoaXMueSA9IChyb3VuZCA/IE1hdGgucm91bmQoeSkgOiB5KTtcclxufTtcclxuXHJcbkwuUG9pbnQucHJvdG90eXBlID0ge1xyXG5cclxuXHQvLyBAbWV0aG9kIGNsb25lKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIGNvcHkgb2YgdGhlIGN1cnJlbnQgcG9pbnQuXHJcblx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludCh0aGlzLngsIHRoaXMueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGQob3RoZXJQb2ludDogUG9pbnQpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHJlc3VsdCBvZiBhZGRpdGlvbiBvZiB0aGUgY3VycmVudCBhbmQgdGhlIGdpdmVuIHBvaW50cy5cclxuXHRhZGQ6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0Ly8gbm9uLWRlc3RydWN0aXZlLCByZXR1cm5zIGEgbmV3IHBvaW50XHJcblx0XHRyZXR1cm4gdGhpcy5jbG9uZSgpLl9hZGQoTC5wb2ludChwb2ludCkpO1xyXG5cdH0sXHJcblxyXG5cdF9hZGQ6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0Ly8gZGVzdHJ1Y3RpdmUsIHVzZWQgZGlyZWN0bHkgZm9yIHBlcmZvcm1hbmNlIGluIHNpdHVhdGlvbnMgd2hlcmUgaXQncyBzYWZlIHRvIG1vZGlmeSBleGlzdGluZyBwb2ludFxyXG5cdFx0dGhpcy54ICs9IHBvaW50Lng7XHJcblx0XHR0aGlzLnkgKz0gcG9pbnQueTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc3VidHJhY3Qob3RoZXJQb2ludDogUG9pbnQpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHJlc3VsdCBvZiBzdWJ0cmFjdGlvbiBvZiB0aGUgZ2l2ZW4gcG9pbnQgZnJvbSB0aGUgY3VycmVudC5cclxuXHRzdWJ0cmFjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gdGhpcy5jbG9uZSgpLl9zdWJ0cmFjdChMLnBvaW50KHBvaW50KSk7XHJcblx0fSxcclxuXHJcblx0X3N1YnRyYWN0OiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHRoaXMueCAtPSBwb2ludC54O1xyXG5cdFx0dGhpcy55IC09IHBvaW50Lnk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGRpdmlkZUJ5KG51bTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2YgZGl2aXNpb24gb2YgdGhlIGN1cnJlbnQgcG9pbnQgYnkgdGhlIGdpdmVuIG51bWJlci5cclxuXHRkaXZpZGVCeTogZnVuY3Rpb24gKG51bSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fZGl2aWRlQnkobnVtKTtcclxuXHR9LFxyXG5cclxuXHRfZGl2aWRlQnk6IGZ1bmN0aW9uIChudW0pIHtcclxuXHRcdHRoaXMueCAvPSBudW07XHJcblx0XHR0aGlzLnkgLz0gbnVtO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtdWx0aXBseUJ5KG51bTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2YgbXVsdGlwbGljYXRpb24gb2YgdGhlIGN1cnJlbnQgcG9pbnQgYnkgdGhlIGdpdmVuIG51bWJlci5cclxuXHRtdWx0aXBseUJ5OiBmdW5jdGlvbiAobnVtKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5jbG9uZSgpLl9tdWx0aXBseUJ5KG51bSk7XHJcblx0fSxcclxuXHJcblx0X211bHRpcGx5Qnk6IGZ1bmN0aW9uIChudW0pIHtcclxuXHRcdHRoaXMueCAqPSBudW07XHJcblx0XHR0aGlzLnkgKj0gbnVtO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzY2FsZUJ5KHNjYWxlOiBQb2ludCk6IFBvaW50XHJcblx0Ly8gTXVsdGlwbHkgZWFjaCBjb29yZGluYXRlIG9mIHRoZSBjdXJyZW50IHBvaW50IGJ5IGVhY2ggY29vcmRpbmF0ZSBvZlxyXG5cdC8vIGBzY2FsZWAuIEluIGxpbmVhciBhbGdlYnJhIHRlcm1zLCBtdWx0aXBseSB0aGUgcG9pbnQgYnkgdGhlXHJcblx0Ly8gW3NjYWxpbmcgbWF0cml4XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TY2FsaW5nXyUyOGdlb21ldHJ5JTI5I01hdHJpeF9yZXByZXNlbnRhdGlvbilcclxuXHQvLyBkZWZpbmVkIGJ5IGBzY2FsZWAuXHJcblx0c2NhbGVCeTogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy54ICogcG9pbnQueCwgdGhpcy55ICogcG9pbnQueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB1bnNjYWxlQnkoc2NhbGU6IFBvaW50KTogUG9pbnRcclxuXHQvLyBJbnZlcnNlIG9mIGBzY2FsZUJ5YC4gRGl2aWRlIGVhY2ggY29vcmRpbmF0ZSBvZiB0aGUgY3VycmVudCBwb2ludCBieVxyXG5cdC8vIGVhY2ggY29vcmRpbmF0ZSBvZiBgc2NhbGVgLlxyXG5cdHVuc2NhbGVCeTogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy54IC8gcG9pbnQueCwgdGhpcy55IC8gcG9pbnQueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByb3VuZCgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgYSBjb3B5IG9mIHRoZSBjdXJyZW50IHBvaW50IHdpdGggcm91bmRlZCBjb29yZGluYXRlcy5cclxuXHRyb3VuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fcm91bmQoKTtcclxuXHR9LFxyXG5cclxuXHRfcm91bmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMueCA9IE1hdGgucm91bmQodGhpcy54KTtcclxuXHRcdHRoaXMueSA9IE1hdGgucm91bmQodGhpcy55KTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZmxvb3IoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIGEgY29weSBvZiB0aGUgY3VycmVudCBwb2ludCB3aXRoIGZsb29yZWQgY29vcmRpbmF0ZXMgKHJvdW5kZWQgZG93bikuXHJcblx0Zmxvb3I6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX2Zsb29yKCk7XHJcblx0fSxcclxuXHJcblx0X2Zsb29yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnggPSBNYXRoLmZsb29yKHRoaXMueCk7XHJcblx0XHR0aGlzLnkgPSBNYXRoLmZsb29yKHRoaXMueSk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNlaWwoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIGEgY29weSBvZiB0aGUgY3VycmVudCBwb2ludCB3aXRoIGNlaWxlZCBjb29yZGluYXRlcyAocm91bmRlZCB1cCkuXHJcblx0Y2VpbDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fY2VpbCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jZWlsOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnggPSBNYXRoLmNlaWwodGhpcy54KTtcclxuXHRcdHRoaXMueSA9IE1hdGguY2VpbCh0aGlzLnkpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXN0YW5jZVRvKG90aGVyUG9pbnQ6IFBvaW50KTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgY2FydGVzaWFuIGRpc3RhbmNlIGJldHdlZW4gdGhlIGN1cnJlbnQgYW5kIHRoZSBnaXZlbiBwb2ludHMuXHJcblx0ZGlzdGFuY2VUbzogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRwb2ludCA9IEwucG9pbnQocG9pbnQpO1xyXG5cclxuXHRcdHZhciB4ID0gcG9pbnQueCAtIHRoaXMueCxcclxuXHRcdCAgICB5ID0gcG9pbnQueSAtIHRoaXMueTtcclxuXHJcblx0XHRyZXR1cm4gTWF0aC5zcXJ0KHggKiB4ICsgeSAqIHkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZXF1YWxzKG90aGVyUG9pbnQ6IFBvaW50KTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBnaXZlbiBwb2ludCBoYXMgdGhlIHNhbWUgY29vcmRpbmF0ZXMuXHJcblx0ZXF1YWxzOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHBvaW50ID0gTC5wb2ludChwb2ludCk7XHJcblxyXG5cdFx0cmV0dXJuIHBvaW50LnggPT09IHRoaXMueCAmJlxyXG5cdFx0ICAgICAgIHBvaW50LnkgPT09IHRoaXMueTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zKG90aGVyUG9pbnQ6IFBvaW50KTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIGJvdGggY29vcmRpbmF0ZXMgb2YgdGhlIGdpdmVuIHBvaW50IGFyZSBsZXNzIHRoYW4gdGhlIGNvcnJlc3BvbmRpbmcgY3VycmVudCBwb2ludCBjb29yZGluYXRlcyAoaW4gYWJzb2x1dGUgdmFsdWVzKS5cclxuXHRjb250YWluczogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRwb2ludCA9IEwucG9pbnQocG9pbnQpO1xyXG5cclxuXHRcdHJldHVybiBNYXRoLmFicyhwb2ludC54KSA8PSBNYXRoLmFicyh0aGlzLngpICYmXHJcblx0XHQgICAgICAgTWF0aC5hYnMocG9pbnQueSkgPD0gTWF0aC5hYnModGhpcy55KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHRvU3RyaW5nKCk6IFN0cmluZ1xyXG5cdC8vIFJldHVybnMgYSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIHBvaW50IGZvciBkZWJ1Z2dpbmcgcHVycG9zZXMuXHJcblx0dG9TdHJpbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAnUG9pbnQoJyArXHJcblx0XHQgICAgICAgIEwuVXRpbC5mb3JtYXROdW0odGhpcy54KSArICcsICcgK1xyXG5cdFx0ICAgICAgICBMLlV0aWwuZm9ybWF0TnVtKHRoaXMueSkgKyAnKSc7XHJcblx0fVxyXG59O1xyXG5cclxuLy8gQGZhY3RvcnkgTC5wb2ludCh4OiBOdW1iZXIsIHk6IE51bWJlciwgcm91bmQ/OiBCb29sZWFuKVxyXG4vLyBDcmVhdGVzIGEgUG9pbnQgb2JqZWN0IHdpdGggdGhlIGdpdmVuIGB4YCBhbmQgYHlgIGNvb3JkaW5hdGVzLiBJZiBvcHRpb25hbCBgcm91bmRgIGlzIHNldCB0byB0cnVlLCByb3VuZHMgdGhlIGB4YCBhbmQgYHlgIHZhbHVlcy5cclxuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLnBvaW50KGNvb3JkczogTnVtYmVyW10pXHJcbi8vIEV4cGVjdHMgYW4gYXJyYXkgb2YgdGhlIGZvcm0gYFt4LCB5XWAgaW5zdGVhZC5cclxuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLnBvaW50KGNvb3JkczogT2JqZWN0KVxyXG4vLyBFeHBlY3RzIGEgcGxhaW4gb2JqZWN0IG9mIHRoZSBmb3JtIGB7eDogTnVtYmVyLCB5OiBOdW1iZXJ9YCBpbnN0ZWFkLlxyXG5MLnBvaW50ID0gZnVuY3Rpb24gKHgsIHksIHJvdW5kKSB7XHJcblx0aWYgKHggaW5zdGFuY2VvZiBMLlBvaW50KSB7XHJcblx0XHRyZXR1cm4geDtcclxuXHR9XHJcblx0aWYgKEwuVXRpbC5pc0FycmF5KHgpKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoeFswXSwgeFsxXSk7XHJcblx0fVxyXG5cdGlmICh4ID09PSB1bmRlZmluZWQgfHwgeCA9PT0gbnVsbCkge1xyXG5cdFx0cmV0dXJuIHg7XHJcblx0fVxyXG5cdGlmICh0eXBlb2YgeCA9PT0gJ29iamVjdCcgJiYgJ3gnIGluIHggJiYgJ3knIGluIHgpIHtcclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludCh4LngsIHgueSk7XHJcblx0fVxyXG5cdHJldHVybiBuZXcgTC5Qb2ludCh4LCB5LCByb3VuZCk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBCb3VuZHNcclxuICogQGFrYSBMLkJvdW5kc1xyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgcmVjdGFuZ3VsYXIgYXJlYSBpbiBwaXhlbCBjb29yZGluYXRlcy5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIHAxID0gTC5wb2ludCgxMCwgMTApLFxyXG4gKiBwMiA9IEwucG9pbnQoNDAsIDYwKSxcclxuICogYm91bmRzID0gTC5ib3VuZHMocDEsIHAyKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIEFsbCBMZWFmbGV0IG1ldGhvZHMgdGhhdCBhY2NlcHQgYEJvdW5kc2Agb2JqZWN0cyBhbHNvIGFjY2VwdCB0aGVtIGluIGEgc2ltcGxlIEFycmF5IGZvcm0gKHVubGVzcyBub3RlZCBvdGhlcndpc2UpLCBzbyB0aGUgYm91bmRzIGV4YW1wbGUgYWJvdmUgY2FuIGJlIHBhc3NlZCBsaWtlIHRoaXM6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIG90aGVyQm91bmRzLmludGVyc2VjdHMoW1sxMCwgMTBdLCBbNDAsIDYwXV0pO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5MLkJvdW5kcyA9IGZ1bmN0aW9uIChhLCBiKSB7XHJcblx0aWYgKCFhKSB7IHJldHVybjsgfVxyXG5cclxuXHR2YXIgcG9pbnRzID0gYiA/IFthLCBiXSA6IGE7XHJcblxyXG5cdGZvciAodmFyIGkgPSAwLCBsZW4gPSBwb2ludHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdHRoaXMuZXh0ZW5kKHBvaW50c1tpXSk7XHJcblx0fVxyXG59O1xyXG5cclxuTC5Cb3VuZHMucHJvdG90eXBlID0ge1xyXG5cdC8vIEBtZXRob2QgZXh0ZW5kKHBvaW50OiBQb2ludCk6IHRoaXNcclxuXHQvLyBFeHRlbmRzIHRoZSBib3VuZHMgdG8gY29udGFpbiB0aGUgZ2l2ZW4gcG9pbnQuXHJcblx0ZXh0ZW5kOiBmdW5jdGlvbiAocG9pbnQpIHsgLy8gKFBvaW50KVxyXG5cdFx0cG9pbnQgPSBMLnBvaW50KHBvaW50KTtcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbWluOiBQb2ludFxyXG5cdFx0Ly8gVGhlIHRvcCBsZWZ0IGNvcm5lciBvZiB0aGUgcmVjdGFuZ2xlLlxyXG5cdFx0Ly8gQHByb3BlcnR5IG1heDogUG9pbnRcclxuXHRcdC8vIFRoZSBib3R0b20gcmlnaHQgY29ybmVyIG9mIHRoZSByZWN0YW5nbGUuXHJcblx0XHRpZiAoIXRoaXMubWluICYmICF0aGlzLm1heCkge1xyXG5cdFx0XHR0aGlzLm1pbiA9IHBvaW50LmNsb25lKCk7XHJcblx0XHRcdHRoaXMubWF4ID0gcG9pbnQuY2xvbmUoKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHRoaXMubWluLnggPSBNYXRoLm1pbihwb2ludC54LCB0aGlzLm1pbi54KTtcclxuXHRcdFx0dGhpcy5tYXgueCA9IE1hdGgubWF4KHBvaW50LngsIHRoaXMubWF4LngpO1xyXG5cdFx0XHR0aGlzLm1pbi55ID0gTWF0aC5taW4ocG9pbnQueSwgdGhpcy5taW4ueSk7XHJcblx0XHRcdHRoaXMubWF4LnkgPSBNYXRoLm1heChwb2ludC55LCB0aGlzLm1heC55KTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKHJvdW5kPzogQm9vbGVhbik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgY2VudGVyIHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAocm91bmQpIHtcclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludChcclxuXHRcdCAgICAgICAgKHRoaXMubWluLnggKyB0aGlzLm1heC54KSAvIDIsXHJcblx0XHQgICAgICAgICh0aGlzLm1pbi55ICsgdGhpcy5tYXgueSkgLyAyLCByb3VuZCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRCb3R0b21MZWZ0KCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgYm90dG9tLWxlZnQgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXRCb3R0b21MZWZ0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy5taW4ueCwgdGhpcy5tYXgueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRUb3BSaWdodCgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHRvcC1yaWdodCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldFRvcFJpZ2h0OiBmdW5jdGlvbiAoKSB7IC8vIC0+IFBvaW50XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy5tYXgueCwgdGhpcy5taW4ueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTaXplKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgc2l6ZSBvZiB0aGUgZ2l2ZW4gYm91bmRzXHJcblx0Z2V0U2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMubWF4LnN1YnRyYWN0KHRoaXMubWluKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zKG90aGVyQm91bmRzOiBCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBjb250YWlucyB0aGUgZ2l2ZW4gb25lLlxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2QgY29udGFpbnMocG9pbnQ6IFBvaW50KTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgY29udGFpbnMgdGhlIGdpdmVuIHBvaW50LlxyXG5cdGNvbnRhaW5zOiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHR2YXIgbWluLCBtYXg7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiBvYmpbMF0gPT09ICdudW1iZXInIHx8IG9iaiBpbnN0YW5jZW9mIEwuUG9pbnQpIHtcclxuXHRcdFx0b2JqID0gTC5wb2ludChvYmopO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0b2JqID0gTC5ib3VuZHMob2JqKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAob2JqIGluc3RhbmNlb2YgTC5Cb3VuZHMpIHtcclxuXHRcdFx0bWluID0gb2JqLm1pbjtcclxuXHRcdFx0bWF4ID0gb2JqLm1heDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG1pbiA9IG1heCA9IG9iajtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gKG1pbi54ID49IHRoaXMubWluLngpICYmXHJcblx0XHQgICAgICAgKG1heC54IDw9IHRoaXMubWF4LngpICYmXHJcblx0XHQgICAgICAgKG1pbi55ID49IHRoaXMubWluLnkpICYmXHJcblx0XHQgICAgICAgKG1heC55IDw9IHRoaXMubWF4LnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaW50ZXJzZWN0cyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgaW50ZXJzZWN0cyB0aGUgZ2l2ZW4gYm91bmRzLiBUd28gYm91bmRzXHJcblx0Ly8gaW50ZXJzZWN0IGlmIHRoZXkgaGF2ZSBhdCBsZWFzdCBvbmUgcG9pbnQgaW4gY29tbW9uLlxyXG5cdGludGVyc2VjdHM6IGZ1bmN0aW9uIChib3VuZHMpIHsgLy8gKEJvdW5kcykgLT4gQm9vbGVhblxyXG5cdFx0Ym91bmRzID0gTC5ib3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgbWluID0gdGhpcy5taW4sXHJcblx0XHQgICAgbWF4ID0gdGhpcy5tYXgsXHJcblx0XHQgICAgbWluMiA9IGJvdW5kcy5taW4sXHJcblx0XHQgICAgbWF4MiA9IGJvdW5kcy5tYXgsXHJcblx0XHQgICAgeEludGVyc2VjdHMgPSAobWF4Mi54ID49IG1pbi54KSAmJiAobWluMi54IDw9IG1heC54KSxcclxuXHRcdCAgICB5SW50ZXJzZWN0cyA9IChtYXgyLnkgPj0gbWluLnkpICYmIChtaW4yLnkgPD0gbWF4LnkpO1xyXG5cclxuXHRcdHJldHVybiB4SW50ZXJzZWN0cyAmJiB5SW50ZXJzZWN0cztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIG92ZXJsYXBzKG90aGVyQm91bmRzOiBCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBvdmVybGFwcyB0aGUgZ2l2ZW4gYm91bmRzLiBUd28gYm91bmRzXHJcblx0Ly8gb3ZlcmxhcCBpZiB0aGVpciBpbnRlcnNlY3Rpb24gaXMgYW4gYXJlYS5cclxuXHRvdmVybGFwczogZnVuY3Rpb24gKGJvdW5kcykgeyAvLyAoQm91bmRzKSAtPiBCb29sZWFuXHJcblx0XHRib3VuZHMgPSBMLmJvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHZhciBtaW4gPSB0aGlzLm1pbixcclxuXHRcdCAgICBtYXggPSB0aGlzLm1heCxcclxuXHRcdCAgICBtaW4yID0gYm91bmRzLm1pbixcclxuXHRcdCAgICBtYXgyID0gYm91bmRzLm1heCxcclxuXHRcdCAgICB4T3ZlcmxhcHMgPSAobWF4Mi54ID4gbWluLngpICYmIChtaW4yLnggPCBtYXgueCksXHJcblx0XHQgICAgeU92ZXJsYXBzID0gKG1heDIueSA+IG1pbi55KSAmJiAobWluMi55IDwgbWF4LnkpO1xyXG5cclxuXHRcdHJldHVybiB4T3ZlcmxhcHMgJiYgeU92ZXJsYXBzO1xyXG5cdH0sXHJcblxyXG5cdGlzVmFsaWQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhISh0aGlzLm1pbiAmJiB0aGlzLm1heCk7XHJcblx0fVxyXG59O1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwuYm91bmRzKHRvcExlZnQ6IFBvaW50LCBib3R0b21SaWdodDogUG9pbnQpXHJcbi8vIENyZWF0ZXMgYSBCb3VuZHMgb2JqZWN0IGZyb20gdHdvIGNvb3JkaW5hdGVzICh1c3VhbGx5IHRvcC1sZWZ0IGFuZCBib3R0b20tcmlnaHQgY29ybmVycykuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLmJvdW5kcyhwb2ludHM6IFBvaW50W10pXHJcbi8vIENyZWF0ZXMgYSBCb3VuZHMgb2JqZWN0IGZyb20gdGhlIHBvaW50cyBpdCBjb250YWluc1xyXG5MLmJvdW5kcyA9IGZ1bmN0aW9uIChhLCBiKSB7XHJcblx0aWYgKCFhIHx8IGEgaW5zdGFuY2VvZiBMLkJvdW5kcykge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdHJldHVybiBuZXcgTC5Cb3VuZHMoYSwgYik7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBUcmFuc2Zvcm1hdGlvblxyXG4gKiBAYWthIEwuVHJhbnNmb3JtYXRpb25cclxuICpcclxuICogUmVwcmVzZW50cyBhbiBhZmZpbmUgdHJhbnNmb3JtYXRpb246IGEgc2V0IG9mIGNvZWZmaWNpZW50cyBgYWAsIGBiYCwgYGNgLCBgZGBcclxuICogZm9yIHRyYW5zZm9ybWluZyBhIHBvaW50IG9mIGEgZm9ybSBgKHgsIHkpYCBpbnRvIGAoYSp4ICsgYiwgYyp5ICsgZClgIGFuZCBkb2luZ1xyXG4gKiB0aGUgcmV2ZXJzZS4gVXNlZCBieSBMZWFmbGV0IGluIGl0cyBwcm9qZWN0aW9ucyBjb2RlLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgdHJhbnNmb3JtYXRpb24gPSBuZXcgTC5UcmFuc2Zvcm1hdGlvbigyLCA1LCAtMSwgMTApLFxyXG4gKiBcdHAgPSBMLnBvaW50KDEsIDIpLFxyXG4gKiBcdHAyID0gdHJhbnNmb3JtYXRpb24udHJhbnNmb3JtKHApLCAvLyAgTC5wb2ludCg3LCA4KVxyXG4gKiBcdHAzID0gdHJhbnNmb3JtYXRpb24udW50cmFuc2Zvcm0ocDIpOyAvLyAgTC5wb2ludCgxLCAyKVxyXG4gKiBgYGBcclxuICovXHJcblxyXG5cclxuLy8gZmFjdG9yeSBuZXcgTC5UcmFuc2Zvcm1hdGlvbihhOiBOdW1iZXIsIGI6IE51bWJlciwgYzogTnVtYmVyLCBkOiBOdW1iZXIpXHJcbi8vIENyZWF0ZXMgYSBgVHJhbnNmb3JtYXRpb25gIG9iamVjdCB3aXRoIHRoZSBnaXZlbiBjb2VmZmljaWVudHMuXHJcbkwuVHJhbnNmb3JtYXRpb24gPSBmdW5jdGlvbiAoYSwgYiwgYywgZCkge1xyXG5cdHRoaXMuX2EgPSBhO1xyXG5cdHRoaXMuX2IgPSBiO1xyXG5cdHRoaXMuX2MgPSBjO1xyXG5cdHRoaXMuX2QgPSBkO1xyXG59O1xyXG5cclxuTC5UcmFuc2Zvcm1hdGlvbi5wcm90b3R5cGUgPSB7XHJcblx0Ly8gQG1ldGhvZCB0cmFuc2Zvcm0ocG9pbnQ6IFBvaW50LCBzY2FsZT86IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIHRyYW5zZm9ybWVkIHBvaW50LCBvcHRpb25hbGx5IG11bHRpcGxpZWQgYnkgdGhlIGdpdmVuIHNjYWxlLlxyXG5cdC8vIE9ubHkgYWNjZXB0cyBhY3R1YWwgYEwuUG9pbnRgIGluc3RhbmNlcywgbm90IGFycmF5cy5cclxuXHR0cmFuc2Zvcm06IGZ1bmN0aW9uIChwb2ludCwgc2NhbGUpIHsgLy8gKFBvaW50LCBOdW1iZXIpIC0+IFBvaW50XHJcblx0XHRyZXR1cm4gdGhpcy5fdHJhbnNmb3JtKHBvaW50LmNsb25lKCksIHNjYWxlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBkZXN0cnVjdGl2ZSB0cmFuc2Zvcm0gKGZhc3RlcilcclxuXHRfdHJhbnNmb3JtOiBmdW5jdGlvbiAocG9pbnQsIHNjYWxlKSB7XHJcblx0XHRzY2FsZSA9IHNjYWxlIHx8IDE7XHJcblx0XHRwb2ludC54ID0gc2NhbGUgKiAodGhpcy5fYSAqIHBvaW50LnggKyB0aGlzLl9iKTtcclxuXHRcdHBvaW50LnkgPSBzY2FsZSAqICh0aGlzLl9jICogcG9pbnQueSArIHRoaXMuX2QpO1xyXG5cdFx0cmV0dXJuIHBvaW50O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW50cmFuc2Zvcm0ocG9pbnQ6IFBvaW50LCBzY2FsZT86IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcmV2ZXJzZSB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgZ2l2ZW4gcG9pbnQsIG9wdGlvbmFsbHkgZGl2aWRlZFxyXG5cdC8vIGJ5IHRoZSBnaXZlbiBzY2FsZS4gT25seSBhY2NlcHRzIGFjdHVhbCBgTC5Qb2ludGAgaW5zdGFuY2VzLCBub3QgYXJyYXlzLlxyXG5cdHVudHJhbnNmb3JtOiBmdW5jdGlvbiAocG9pbnQsIHNjYWxlKSB7XHJcblx0XHRzY2FsZSA9IHNjYWxlIHx8IDE7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoXHJcblx0XHQgICAgICAgIChwb2ludC54IC8gc2NhbGUgLSB0aGlzLl9iKSAvIHRoaXMuX2EsXHJcblx0XHQgICAgICAgIChwb2ludC55IC8gc2NhbGUgLSB0aGlzLl9kKSAvIHRoaXMuX2MpO1xyXG5cdH1cclxufTtcclxuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBEb21VdGlsXHJcbiAqXHJcbiAqIFV0aWxpdHkgZnVuY3Rpb25zIHRvIHdvcmsgd2l0aCB0aGUgW0RPTV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0RvY3VtZW50X09iamVjdF9Nb2RlbClcclxuICogdHJlZSwgdXNlZCBieSBMZWFmbGV0IGludGVybmFsbHkuXHJcbiAqXHJcbiAqIE1vc3QgZnVuY3Rpb25zIGV4cGVjdGluZyBvciByZXR1cm5pbmcgYSBgSFRNTEVsZW1lbnRgIGFsc28gd29yayBmb3JcclxuICogU1ZHIGVsZW1lbnRzLiBUaGUgb25seSBkaWZmZXJlbmNlIGlzIHRoYXQgY2xhc3NlcyByZWZlciB0byBDU1MgY2xhc3Nlc1xyXG4gKiBpbiBIVE1MIGFuZCBTVkcgY2xhc3NlcyBpbiBTVkcuXHJcbiAqL1xyXG5cclxuTC5Eb21VdGlsID0ge1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0KGlkOiBTdHJpbmd8SFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudFxyXG5cdC8vIFJldHVybnMgYW4gZWxlbWVudCBnaXZlbiBpdHMgRE9NIGlkLCBvciByZXR1cm5zIHRoZSBlbGVtZW50IGl0c2VsZlxyXG5cdC8vIGlmIGl0IHdhcyBwYXNzZWQgZGlyZWN0bHkuXHJcblx0Z2V0OiBmdW5jdGlvbiAoaWQpIHtcclxuXHRcdHJldHVybiB0eXBlb2YgaWQgPT09ICdzdHJpbmcnID8gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoaWQpIDogaWQ7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldFN0eWxlKGVsOiBIVE1MRWxlbWVudCwgc3R5bGVBdHRyaWI6IFN0cmluZyk6IFN0cmluZ1xyXG5cdC8vIFJldHVybnMgdGhlIHZhbHVlIGZvciBhIGNlcnRhaW4gc3R5bGUgYXR0cmlidXRlIG9uIGFuIGVsZW1lbnQsXHJcblx0Ly8gaW5jbHVkaW5nIGNvbXB1dGVkIHZhbHVlcyBvciB2YWx1ZXMgc2V0IHRocm91Z2ggQ1NTLlxyXG5cdGdldFN0eWxlOiBmdW5jdGlvbiAoZWwsIHN0eWxlKSB7XHJcblxyXG5cdFx0dmFyIHZhbHVlID0gZWwuc3R5bGVbc3R5bGVdIHx8IChlbC5jdXJyZW50U3R5bGUgJiYgZWwuY3VycmVudFN0eWxlW3N0eWxlXSk7XHJcblxyXG5cdFx0aWYgKCghdmFsdWUgfHwgdmFsdWUgPT09ICdhdXRvJykgJiYgZG9jdW1lbnQuZGVmYXVsdFZpZXcpIHtcclxuXHRcdFx0dmFyIGNzcyA9IGRvY3VtZW50LmRlZmF1bHRWaWV3LmdldENvbXB1dGVkU3R5bGUoZWwsIG51bGwpO1xyXG5cdFx0XHR2YWx1ZSA9IGNzcyA/IGNzc1tzdHlsZV0gOiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB2YWx1ZSA9PT0gJ2F1dG8nID8gbnVsbCA6IHZhbHVlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBjcmVhdGUodGFnTmFtZTogU3RyaW5nLCBjbGFzc05hbWU/OiBTdHJpbmcsIGNvbnRhaW5lcj86IEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBDcmVhdGVzIGFuIEhUTUwgZWxlbWVudCB3aXRoIGB0YWdOYW1lYCwgc2V0cyBpdHMgY2xhc3MgdG8gYGNsYXNzTmFtZWAsIGFuZCBvcHRpb25hbGx5IGFwcGVuZHMgaXQgdG8gYGNvbnRhaW5lcmAgZWxlbWVudC5cclxuXHRjcmVhdGU6IGZ1bmN0aW9uICh0YWdOYW1lLCBjbGFzc05hbWUsIGNvbnRhaW5lcikge1xyXG5cclxuXHRcdHZhciBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQodGFnTmFtZSk7XHJcblx0XHRlbC5jbGFzc05hbWUgPSBjbGFzc05hbWUgfHwgJyc7XHJcblxyXG5cdFx0aWYgKGNvbnRhaW5lcikge1xyXG5cdFx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQoZWwpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBlbDtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gcmVtb3ZlKGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBSZW1vdmVzIGBlbGAgZnJvbSBpdHMgcGFyZW50IGVsZW1lbnRcclxuXHRyZW1vdmU6IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0dmFyIHBhcmVudCA9IGVsLnBhcmVudE5vZGU7XHJcblx0XHRpZiAocGFyZW50KSB7XHJcblx0XHRcdHBhcmVudC5yZW1vdmVDaGlsZChlbCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGVtcHR5KGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBSZW1vdmVzIGFsbCBvZiBgZWxgJ3MgY2hpbGRyZW4gZWxlbWVudHMgZnJvbSBgZWxgXHJcblx0ZW1wdHk6IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0d2hpbGUgKGVsLmZpcnN0Q2hpbGQpIHtcclxuXHRcdFx0ZWwucmVtb3ZlQ2hpbGQoZWwuZmlyc3RDaGlsZCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRvRnJvbnQoZWw6IEhUTUxFbGVtZW50KVxyXG5cdC8vIE1ha2VzIGBlbGAgdGhlIGxhc3QgY2hpbGRyZW4gb2YgaXRzIHBhcmVudCwgc28gaXQgcmVuZGVycyBpbiBmcm9udCBvZiB0aGUgb3RoZXIgY2hpbGRyZW4uXHJcblx0dG9Gcm9udDogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHRlbC5wYXJlbnROb2RlLmFwcGVuZENoaWxkKGVsKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gdG9CYWNrKGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBNYWtlcyBgZWxgIHRoZSBmaXJzdCBjaGlsZHJlbiBvZiBpdHMgcGFyZW50LCBzbyBpdCByZW5kZXJzIGJhY2sgZnJvbSB0aGUgb3RoZXIgY2hpbGRyZW4uXHJcblx0dG9CYWNrOiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdHZhciBwYXJlbnQgPSBlbC5wYXJlbnROb2RlO1xyXG5cdFx0cGFyZW50Lmluc2VydEJlZm9yZShlbCwgcGFyZW50LmZpcnN0Q2hpbGQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBoYXNDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbWU6IFN0cmluZyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZWxlbWVudCdzIGNsYXNzIGF0dHJpYnV0ZSBjb250YWlucyBgbmFtZWAuXHJcblx0aGFzQ2xhc3M6IGZ1bmN0aW9uIChlbCwgbmFtZSkge1xyXG5cdFx0aWYgKGVsLmNsYXNzTGlzdCAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdHJldHVybiBlbC5jbGFzc0xpc3QuY29udGFpbnMobmFtZSk7XHJcblx0XHR9XHJcblx0XHR2YXIgY2xhc3NOYW1lID0gTC5Eb21VdGlsLmdldENsYXNzKGVsKTtcclxuXHRcdHJldHVybiBjbGFzc05hbWUubGVuZ3RoID4gMCAmJiBuZXcgUmVnRXhwKCcoXnxcXFxccyknICsgbmFtZSArICcoXFxcXHN8JCknKS50ZXN0KGNsYXNzTmFtZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGFkZENsYXNzKGVsOiBIVE1MRWxlbWVudCwgbmFtZTogU3RyaW5nKVxyXG5cdC8vIEFkZHMgYG5hbWVgIHRvIHRoZSBlbGVtZW50J3MgY2xhc3MgYXR0cmlidXRlLlxyXG5cdGFkZENsYXNzOiBmdW5jdGlvbiAoZWwsIG5hbWUpIHtcclxuXHRcdGlmIChlbC5jbGFzc0xpc3QgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR2YXIgY2xhc3NlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKG5hbWUpO1xyXG5cdFx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gY2xhc3Nlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdGVsLmNsYXNzTGlzdC5hZGQoY2xhc3Nlc1tpXSk7XHJcblx0XHRcdH1cclxuXHRcdH0gZWxzZSBpZiAoIUwuRG9tVXRpbC5oYXNDbGFzcyhlbCwgbmFtZSkpIHtcclxuXHRcdFx0dmFyIGNsYXNzTmFtZSA9IEwuRG9tVXRpbC5nZXRDbGFzcyhlbCk7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRDbGFzcyhlbCwgKGNsYXNzTmFtZSA/IGNsYXNzTmFtZSArICcgJyA6ICcnKSArIG5hbWUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiByZW1vdmVDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbWU6IFN0cmluZylcclxuXHQvLyBSZW1vdmVzIGBuYW1lYCBmcm9tIHRoZSBlbGVtZW50J3MgY2xhc3MgYXR0cmlidXRlLlxyXG5cdHJlbW92ZUNsYXNzOiBmdW5jdGlvbiAoZWwsIG5hbWUpIHtcclxuXHRcdGlmIChlbC5jbGFzc0xpc3QgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRlbC5jbGFzc0xpc3QucmVtb3ZlKG5hbWUpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0TC5Eb21VdGlsLnNldENsYXNzKGVsLCBMLlV0aWwudHJpbSgoJyAnICsgTC5Eb21VdGlsLmdldENsYXNzKGVsKSArICcgJykucmVwbGFjZSgnICcgKyBuYW1lICsgJyAnLCAnICcpKSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNldENsYXNzKGVsOiBIVE1MRWxlbWVudCwgbmFtZTogU3RyaW5nKVxyXG5cdC8vIFNldHMgdGhlIGVsZW1lbnQncyBjbGFzcy5cclxuXHRzZXRDbGFzczogZnVuY3Rpb24gKGVsLCBuYW1lKSB7XHJcblx0XHRpZiAoZWwuY2xhc3NOYW1lLmJhc2VWYWwgPT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRlbC5jbGFzc05hbWUgPSBuYW1lO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0Ly8gaW4gY2FzZSBvZiBTVkcgZWxlbWVudFxyXG5cdFx0XHRlbC5jbGFzc05hbWUuYmFzZVZhbCA9IG5hbWU7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldENsYXNzKGVsOiBIVE1MRWxlbWVudCk6IFN0cmluZ1xyXG5cdC8vIFJldHVybnMgdGhlIGVsZW1lbnQncyBjbGFzcy5cclxuXHRnZXRDbGFzczogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHRyZXR1cm4gZWwuY2xhc3NOYW1lLmJhc2VWYWwgPT09IHVuZGVmaW5lZCA/IGVsLmNsYXNzTmFtZSA6IGVsLmNsYXNzTmFtZS5iYXNlVmFsO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRPcGFjaXR5KGVsOiBIVE1MRWxlbWVudCwgb3BhY2l0eTogTnVtYmVyKVxyXG5cdC8vIFNldCB0aGUgb3BhY2l0eSBvZiBhbiBlbGVtZW50IChpbmNsdWRpbmcgb2xkIElFIHN1cHBvcnQpLlxyXG5cdC8vIGBvcGFjaXR5YCBtdXN0IGJlIGEgbnVtYmVyIGZyb20gYDBgIHRvIGAxYC5cclxuXHRzZXRPcGFjaXR5OiBmdW5jdGlvbiAoZWwsIHZhbHVlKSB7XHJcblxyXG5cdFx0aWYgKCdvcGFjaXR5JyBpbiBlbC5zdHlsZSkge1xyXG5cdFx0XHRlbC5zdHlsZS5vcGFjaXR5ID0gdmFsdWU7XHJcblxyXG5cdFx0fSBlbHNlIGlmICgnZmlsdGVyJyBpbiBlbC5zdHlsZSkge1xyXG5cdFx0XHRMLkRvbVV0aWwuX3NldE9wYWNpdHlJRShlbCwgdmFsdWUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9zZXRPcGFjaXR5SUU6IGZ1bmN0aW9uIChlbCwgdmFsdWUpIHtcclxuXHRcdHZhciBmaWx0ZXIgPSBmYWxzZSxcclxuXHRcdCAgICBmaWx0ZXJOYW1lID0gJ0RYSW1hZ2VUcmFuc2Zvcm0uTWljcm9zb2Z0LkFscGhhJztcclxuXHJcblx0XHQvLyBmaWx0ZXJzIGNvbGxlY3Rpb24gdGhyb3dzIGFuIGVycm9yIGlmIHdlIHRyeSB0byByZXRyaWV2ZSBhIGZpbHRlciB0aGF0IGRvZXNuJ3QgZXhpc3RcclxuXHRcdHRyeSB7XHJcblx0XHRcdGZpbHRlciA9IGVsLmZpbHRlcnMuaXRlbShmaWx0ZXJOYW1lKTtcclxuXHRcdH0gY2F0Y2ggKGUpIHtcclxuXHRcdFx0Ly8gZG9uJ3Qgc2V0IG9wYWNpdHkgdG8gMSBpZiB3ZSBoYXZlbid0IGFscmVhZHkgc2V0IGFuIG9wYWNpdHksXHJcblx0XHRcdC8vIGl0IGlzbid0IG5lZWRlZCBhbmQgYnJlYWtzIHRyYW5zcGFyZW50IHBuZ3MuXHJcblx0XHRcdGlmICh2YWx1ZSA9PT0gMSkgeyByZXR1cm47IH1cclxuXHRcdH1cclxuXHJcblx0XHR2YWx1ZSA9IE1hdGgucm91bmQodmFsdWUgKiAxMDApO1xyXG5cclxuXHRcdGlmIChmaWx0ZXIpIHtcclxuXHRcdFx0ZmlsdGVyLkVuYWJsZWQgPSAodmFsdWUgIT09IDEwMCk7XHJcblx0XHRcdGZpbHRlci5PcGFjaXR5ID0gdmFsdWU7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRlbC5zdHlsZS5maWx0ZXIgKz0gJyBwcm9naWQ6JyArIGZpbHRlck5hbWUgKyAnKG9wYWNpdHk9JyArIHZhbHVlICsgJyknO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0ZXN0UHJvcChwcm9wczogU3RyaW5nW10pOiBTdHJpbmd8ZmFsc2VcclxuXHQvLyBHb2VzIHRocm91Z2ggdGhlIGFycmF5IG9mIHN0eWxlIG5hbWVzIGFuZCByZXR1cm5zIHRoZSBmaXJzdCBuYW1lXHJcblx0Ly8gdGhhdCBpcyBhIHZhbGlkIHN0eWxlIG5hbWUgZm9yIGFuIGVsZW1lbnQuIElmIG5vIHN1Y2ggbmFtZSBpcyBmb3VuZCxcclxuXHQvLyBpdCByZXR1cm5zIGZhbHNlLiBVc2VmdWwgZm9yIHZlbmRvci1wcmVmaXhlZCBzdHlsZXMgbGlrZSBgdHJhbnNmb3JtYC5cclxuXHR0ZXN0UHJvcDogZnVuY3Rpb24gKHByb3BzKSB7XHJcblxyXG5cdFx0dmFyIHN0eWxlID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnN0eWxlO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwOyBpIDwgcHJvcHMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0aWYgKHByb3BzW2ldIGluIHN0eWxlKSB7XHJcblx0XHRcdFx0cmV0dXJuIHByb3BzW2ldO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gZmFsc2U7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNldFRyYW5zZm9ybShlbDogSFRNTEVsZW1lbnQsIG9mZnNldDogUG9pbnQsIHNjYWxlPzogTnVtYmVyKVxyXG5cdC8vIFJlc2V0cyB0aGUgM0QgQ1NTIHRyYW5zZm9ybSBvZiBgZWxgIHNvIGl0IGlzIHRyYW5zbGF0ZWQgYnkgYG9mZnNldGAgcGl4ZWxzXHJcblx0Ly8gYW5kIG9wdGlvbmFsbHkgc2NhbGVkIGJ5IGBzY2FsZWAuIERvZXMgbm90IGhhdmUgYW4gZWZmZWN0IGlmIHRoZVxyXG5cdC8vIGJyb3dzZXIgZG9lc24ndCBzdXBwb3J0IDNEIENTUyB0cmFuc2Zvcm1zLlxyXG5cdHNldFRyYW5zZm9ybTogZnVuY3Rpb24gKGVsLCBvZmZzZXQsIHNjYWxlKSB7XHJcblx0XHR2YXIgcG9zID0gb2Zmc2V0IHx8IG5ldyBMLlBvaW50KDAsIDApO1xyXG5cclxuXHRcdGVsLnN0eWxlW0wuRG9tVXRpbC5UUkFOU0ZPUk1dID1cclxuXHRcdFx0KEwuQnJvd3Nlci5pZTNkID9cclxuXHRcdFx0XHQndHJhbnNsYXRlKCcgKyBwb3MueCArICdweCwnICsgcG9zLnkgKyAncHgpJyA6XHJcblx0XHRcdFx0J3RyYW5zbGF0ZTNkKCcgKyBwb3MueCArICdweCwnICsgcG9zLnkgKyAncHgsMCknKSArXHJcblx0XHRcdChzY2FsZSA/ICcgc2NhbGUoJyArIHNjYWxlICsgJyknIDogJycpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRQb3NpdGlvbihlbDogSFRNTEVsZW1lbnQsIHBvc2l0aW9uOiBQb2ludClcclxuXHQvLyBTZXRzIHRoZSBwb3NpdGlvbiBvZiBgZWxgIHRvIGNvb3JkaW5hdGVzIHNwZWNpZmllZCBieSBgcG9zaXRpb25gLFxyXG5cdC8vIHVzaW5nIENTUyB0cmFuc2xhdGUgb3IgdG9wL2xlZnQgcG9zaXRpb25pbmcgZGVwZW5kaW5nIG9uIHRoZSBicm93c2VyXHJcblx0Ly8gKHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5IHRvIHBvc2l0aW9uIGl0cyBsYXllcnMpLlxyXG5cdHNldFBvc2l0aW9uOiBmdW5jdGlvbiAoZWwsIHBvaW50KSB7IC8vIChIVE1MRWxlbWVudCwgUG9pbnRbLCBCb29sZWFuXSlcclxuXHJcblx0XHQvKmVzbGludC1kaXNhYmxlICovXHJcblx0XHRlbC5fbGVhZmxldF9wb3MgPSBwb2ludDtcclxuXHRcdC8qZXNsaW50LWVuYWJsZSAqL1xyXG5cclxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QpIHtcclxuXHRcdFx0TC5Eb21VdGlsLnNldFRyYW5zZm9ybShlbCwgcG9pbnQpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZWwuc3R5bGUubGVmdCA9IHBvaW50LnggKyAncHgnO1xyXG5cdFx0XHRlbC5zdHlsZS50b3AgPSBwb2ludC55ICsgJ3B4JztcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0UG9zaXRpb24oZWw6IEhUTUxFbGVtZW50KTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBjb29yZGluYXRlcyBvZiBhbiBlbGVtZW50IHByZXZpb3VzbHkgcG9zaXRpb25lZCB3aXRoIHNldFBvc2l0aW9uLlxyXG5cdGdldFBvc2l0aW9uOiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdC8vIHRoaXMgbWV0aG9kIGlzIG9ubHkgdXNlZCBmb3IgZWxlbWVudHMgcHJldmlvdXNseSBwb3NpdGlvbmVkIHVzaW5nIHNldFBvc2l0aW9uLFxyXG5cdFx0Ly8gc28gaXQncyBzYWZlIHRvIGNhY2hlIHRoZSBwb3NpdGlvbiBmb3IgcGVyZm9ybWFuY2VcclxuXHJcblx0XHRyZXR1cm4gZWwuX2xlYWZsZXRfcG9zIHx8IG5ldyBMLlBvaW50KDAsIDApO1xyXG5cdH1cclxufTtcclxuXHJcblxyXG4oZnVuY3Rpb24gKCkge1xyXG5cdC8vIHByZWZpeCBzdHlsZSBwcm9wZXJ0eSBuYW1lc1xyXG5cclxuXHQvLyBAcHJvcGVydHkgVFJBTlNGT1JNOiBTdHJpbmdcclxuXHQvLyBWZW5kb3ItcHJlZml4ZWQgZnJhbnNmb3JtIHN0eWxlIG5hbWUgKGUuZy4gYCd3ZWJraXRUcmFuc2Zvcm0nYCBmb3IgV2ViS2l0KS5cclxuXHRMLkRvbVV0aWwuVFJBTlNGT1JNID0gTC5Eb21VdGlsLnRlc3RQcm9wKFxyXG5cdFx0XHRbJ3RyYW5zZm9ybScsICdXZWJraXRUcmFuc2Zvcm0nLCAnT1RyYW5zZm9ybScsICdNb3pUcmFuc2Zvcm0nLCAnbXNUcmFuc2Zvcm0nXSk7XHJcblxyXG5cclxuXHQvLyB3ZWJraXRUcmFuc2l0aW9uIGNvbWVzIGZpcnN0IGJlY2F1c2Ugc29tZSBicm93c2VyIHZlcnNpb25zIHRoYXQgZHJvcCB2ZW5kb3IgcHJlZml4IGRvbid0IGRvXHJcblx0Ly8gdGhlIHNhbWUgZm9yIHRoZSB0cmFuc2l0aW9uZW5kIGV2ZW50LCBpbiBwYXJ0aWN1bGFyIHRoZSBBbmRyb2lkIDQuMSBzdG9jayBicm93c2VyXHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBUUkFOU0lUSU9OOiBTdHJpbmdcclxuXHQvLyBWZW5kb3ItcHJlZml4ZWQgdHJhbnNmb3JtIHN0eWxlIG5hbWUuXHJcblx0dmFyIHRyYW5zaXRpb24gPSBMLkRvbVV0aWwuVFJBTlNJVElPTiA9IEwuRG9tVXRpbC50ZXN0UHJvcChcclxuXHRcdFx0Wyd3ZWJraXRUcmFuc2l0aW9uJywgJ3RyYW5zaXRpb24nLCAnT1RyYW5zaXRpb24nLCAnTW96VHJhbnNpdGlvbicsICdtc1RyYW5zaXRpb24nXSk7XHJcblxyXG5cdEwuRG9tVXRpbC5UUkFOU0lUSU9OX0VORCA9XHJcblx0XHRcdHRyYW5zaXRpb24gPT09ICd3ZWJraXRUcmFuc2l0aW9uJyB8fCB0cmFuc2l0aW9uID09PSAnT1RyYW5zaXRpb24nID8gdHJhbnNpdGlvbiArICdFbmQnIDogJ3RyYW5zaXRpb25lbmQnO1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gZGlzYWJsZVRleHRTZWxlY3Rpb24oKVxyXG5cdC8vIFByZXZlbnRzIHRoZSB1c2VyIGZyb20gZ2VuZXJhdGluZyBgc2VsZWN0c3RhcnRgIERPTSBldmVudHMsIHVzdWFsbHkgZ2VuZXJhdGVkXHJcblx0Ly8gd2hlbiB0aGUgdXNlciBkcmFncyB0aGUgbW91c2UgdGhyb3VnaCBhIHBhZ2Ugd2l0aCB0ZXh0LiBVc2VkIGludGVybmFsbHlcclxuXHQvLyBieSBMZWFmbGV0IHRvIG92ZXJyaWRlIHRoZSBiZWhhdmlvdXIgb2YgYW55IGNsaWNrLWFuZC1kcmFnIGludGVyYWN0aW9uIG9uXHJcblx0Ly8gdGhlIG1hcC4gQWZmZWN0cyBkcmFnIGludGVyYWN0aW9ucyBvbiB0aGUgd2hvbGUgZG9jdW1lbnQuXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBlbmFibGVUZXh0U2VsZWN0aW9uKClcclxuXHQvLyBDYW5jZWxzIHRoZSBlZmZlY3RzIG9mIGEgcHJldmlvdXMgW2BMLkRvbVV0aWwuZGlzYWJsZVRleHRTZWxlY3Rpb25gXSgjZG9tdXRpbC1kaXNhYmxldGV4dHNlbGVjdGlvbikuXHJcblx0aWYgKCdvbnNlbGVjdHN0YXJ0JyBpbiBkb2N1bWVudCkge1xyXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRMLkRvbUV2ZW50Lm9uKHdpbmRvdywgJ3NlbGVjdHN0YXJ0JywgTC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdCk7XHJcblx0XHR9O1xyXG5cdFx0TC5Eb21VdGlsLmVuYWJsZVRleHRTZWxlY3Rpb24gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdEwuRG9tRXZlbnQub2ZmKHdpbmRvdywgJ3NlbGVjdHN0YXJ0JywgTC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdCk7XHJcblx0XHR9O1xyXG5cclxuXHR9IGVsc2Uge1xyXG5cdFx0dmFyIHVzZXJTZWxlY3RQcm9wZXJ0eSA9IEwuRG9tVXRpbC50ZXN0UHJvcChcclxuXHRcdFx0Wyd1c2VyU2VsZWN0JywgJ1dlYmtpdFVzZXJTZWxlY3QnLCAnT1VzZXJTZWxlY3QnLCAnTW96VXNlclNlbGVjdCcsICdtc1VzZXJTZWxlY3QnXSk7XHJcblxyXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRpZiAodXNlclNlbGVjdFByb3BlcnR5KSB7XHJcblx0XHRcdFx0dmFyIHN0eWxlID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnN0eWxlO1xyXG5cdFx0XHRcdHRoaXMuX3VzZXJTZWxlY3QgPSBzdHlsZVt1c2VyU2VsZWN0UHJvcGVydHldO1xyXG5cdFx0XHRcdHN0eWxlW3VzZXJTZWxlY3RQcm9wZXJ0eV0gPSAnbm9uZSc7XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblx0XHRMLkRvbVV0aWwuZW5hYmxlVGV4dFNlbGVjdGlvbiA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0aWYgKHVzZXJTZWxlY3RQcm9wZXJ0eSkge1xyXG5cdFx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZVt1c2VyU2VsZWN0UHJvcGVydHldID0gdGhpcy5fdXNlclNlbGVjdDtcclxuXHRcdFx0XHRkZWxldGUgdGhpcy5fdXNlclNlbGVjdDtcclxuXHRcdFx0fVxyXG5cdFx0fTtcclxuXHR9XHJcblxyXG5cdC8vIEBmdW5jdGlvbiBkaXNhYmxlSW1hZ2VEcmFnKClcclxuXHQvLyBBcyBbYEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbmBdKCNkb211dGlsLWRpc2FibGV0ZXh0c2VsZWN0aW9uKSwgYnV0XHJcblx0Ly8gZm9yIGBkcmFnc3RhcnRgIERPTSBldmVudHMsIHVzdWFsbHkgZ2VuZXJhdGVkIHdoZW4gdGhlIHVzZXIgZHJhZ3MgYW4gaW1hZ2UuXHJcblx0TC5Eb21VdGlsLmRpc2FibGVJbWFnZURyYWcgPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRMLkRvbUV2ZW50Lm9uKHdpbmRvdywgJ2RyYWdzdGFydCcsIEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQpO1xyXG5cdH07XHJcblxyXG5cdC8vIEBmdW5jdGlvbiBlbmFibGVJbWFnZURyYWcoKVxyXG5cdC8vIENhbmNlbHMgdGhlIGVmZmVjdHMgb2YgYSBwcmV2aW91cyBbYEwuRG9tVXRpbC5kaXNhYmxlSW1hZ2VEcmFnYF0oI2RvbXV0aWwtZGlzYWJsZXRleHRzZWxlY3Rpb24pLlxyXG5cdEwuRG9tVXRpbC5lbmFibGVJbWFnZURyYWcgPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRMLkRvbUV2ZW50Lm9mZih3aW5kb3csICdkcmFnc3RhcnQnLCBMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KTtcclxuXHR9O1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gcHJldmVudE91dGxpbmUoZWw6IEhUTUxFbGVtZW50KVxyXG5cdC8vIE1ha2VzIHRoZSBbb3V0bGluZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQ1NTL291dGxpbmUpXHJcblx0Ly8gb2YgdGhlIGVsZW1lbnQgYGVsYCBpbnZpc2libGUuIFVzZWQgaW50ZXJuYWxseSBieSBMZWFmbGV0IHRvIHByZXZlbnRcclxuXHQvLyBmb2N1c2FibGUgZWxlbWVudHMgZnJvbSBkaXNwbGF5aW5nIGFuIG91dGxpbmUgd2hlbiB0aGUgdXNlciBwZXJmb3JtcyBhXHJcblx0Ly8gZHJhZyBpbnRlcmFjdGlvbiBvbiB0aGVtLlxyXG5cdEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZSA9IGZ1bmN0aW9uIChlbGVtZW50KSB7XHJcblx0XHR3aGlsZSAoZWxlbWVudC50YWJJbmRleCA9PT0gLTEpIHtcclxuXHRcdFx0ZWxlbWVudCA9IGVsZW1lbnQucGFyZW50Tm9kZTtcclxuXHRcdH1cclxuXHRcdGlmICghZWxlbWVudCB8fCAhZWxlbWVudC5zdHlsZSkgeyByZXR1cm47IH1cclxuXHRcdEwuRG9tVXRpbC5yZXN0b3JlT3V0bGluZSgpO1xyXG5cdFx0dGhpcy5fb3V0bGluZUVsZW1lbnQgPSBlbGVtZW50O1xyXG5cdFx0dGhpcy5fb3V0bGluZVN0eWxlID0gZWxlbWVudC5zdHlsZS5vdXRsaW5lO1xyXG5cdFx0ZWxlbWVudC5zdHlsZS5vdXRsaW5lID0gJ25vbmUnO1xyXG5cdFx0TC5Eb21FdmVudC5vbih3aW5kb3csICdrZXlkb3duJywgTC5Eb21VdGlsLnJlc3RvcmVPdXRsaW5lLCB0aGlzKTtcclxuXHR9O1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gcmVzdG9yZU91dGxpbmUoKVxyXG5cdC8vIENhbmNlbHMgdGhlIGVmZmVjdHMgb2YgYSBwcmV2aW91cyBbYEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZWBdKCkuXHJcblx0TC5Eb21VdGlsLnJlc3RvcmVPdXRsaW5lID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9vdXRsaW5lRWxlbWVudCkgeyByZXR1cm47IH1cclxuXHRcdHRoaXMuX291dGxpbmVFbGVtZW50LnN0eWxlLm91dGxpbmUgPSB0aGlzLl9vdXRsaW5lU3R5bGU7XHJcblx0XHRkZWxldGUgdGhpcy5fb3V0bGluZUVsZW1lbnQ7XHJcblx0XHRkZWxldGUgdGhpcy5fb3V0bGluZVN0eWxlO1xyXG5cdFx0TC5Eb21FdmVudC5vZmYod2luZG93LCAna2V5ZG93bicsIEwuRG9tVXRpbC5yZXN0b3JlT3V0bGluZSwgdGhpcyk7XHJcblx0fTtcclxufSkoKTtcclxuXG5cblxuLyogQGNsYXNzIExhdExuZ1xyXG4gKiBAYWthIEwuTGF0TG5nXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYSBnZW9ncmFwaGljYWwgcG9pbnQgd2l0aCBhIGNlcnRhaW4gbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZS5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBgXHJcbiAqIHZhciBsYXRsbmcgPSBMLmxhdExuZyg1MC41LCAzMC41KTtcclxuICogYGBgXHJcbiAqXHJcbiAqIEFsbCBMZWFmbGV0IG1ldGhvZHMgdGhhdCBhY2NlcHQgTGF0TG5nIG9iamVjdHMgYWxzbyBhY2NlcHQgdGhlbSBpbiBhIHNpbXBsZSBBcnJheSBmb3JtIGFuZCBzaW1wbGUgb2JqZWN0IGZvcm0gKHVubGVzcyBub3RlZCBvdGhlcndpc2UpLCBzbyB0aGVzZSBsaW5lcyBhcmUgZXF1aXZhbGVudDpcclxuICpcclxuICogYGBgXHJcbiAqIG1hcC5wYW5UbyhbNTAsIDMwXSk7XHJcbiAqIG1hcC5wYW5Ubyh7bG9uOiAzMCwgbGF0OiA1MH0pO1xyXG4gKiBtYXAucGFuVG8oe2xhdDogNTAsIGxuZzogMzB9KTtcclxuICogbWFwLnBhblRvKEwubGF0TG5nKDUwLCAzMCkpO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5MLkxhdExuZyA9IGZ1bmN0aW9uIChsYXQsIGxuZywgYWx0KSB7XHJcblx0aWYgKGlzTmFOKGxhdCkgfHwgaXNOYU4obG5nKSkge1xyXG5cdFx0dGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIExhdExuZyBvYmplY3Q6ICgnICsgbGF0ICsgJywgJyArIGxuZyArICcpJyk7XHJcblx0fVxyXG5cclxuXHQvLyBAcHJvcGVydHkgbGF0OiBOdW1iZXJcclxuXHQvLyBMYXRpdHVkZSBpbiBkZWdyZWVzXHJcblx0dGhpcy5sYXQgPSArbGF0O1xyXG5cclxuXHQvLyBAcHJvcGVydHkgbG5nOiBOdW1iZXJcclxuXHQvLyBMb25naXR1ZGUgaW4gZGVncmVlc1xyXG5cdHRoaXMubG5nID0gK2xuZztcclxuXHJcblx0Ly8gQHByb3BlcnR5IGFsdDogTnVtYmVyXHJcblx0Ly8gQWx0aXR1ZGUgaW4gbWV0ZXJzIChvcHRpb25hbClcclxuXHRpZiAoYWx0ICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdHRoaXMuYWx0ID0gK2FsdDtcclxuXHR9XHJcbn07XHJcblxyXG5MLkxhdExuZy5wcm90b3R5cGUgPSB7XHJcblx0Ly8gQG1ldGhvZCBlcXVhbHMob3RoZXJMYXRMbmc6IExhdExuZywgbWF4TWFyZ2luPzogTnVtYmVyKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBnaXZlbiBgTGF0TG5nYCBwb2ludCBpcyBhdCB0aGUgc2FtZSBwb3NpdGlvbiAod2l0aGluIGEgc21hbGwgbWFyZ2luIG9mIGVycm9yKS4gVGhlIG1hcmdpbiBvZiBlcnJvciBjYW4gYmUgb3ZlcnJpZGVuIGJ5IHNldHRpbmcgYG1heE1hcmdpbmAgdG8gYSBzbWFsbCBudW1iZXIuXHJcblx0ZXF1YWxzOiBmdW5jdGlvbiAob2JqLCBtYXhNYXJnaW4pIHtcclxuXHRcdGlmICghb2JqKSB7IHJldHVybiBmYWxzZTsgfVxyXG5cclxuXHRcdG9iaiA9IEwubGF0TG5nKG9iaik7XHJcblxyXG5cdFx0dmFyIG1hcmdpbiA9IE1hdGgubWF4KFxyXG5cdFx0ICAgICAgICBNYXRoLmFicyh0aGlzLmxhdCAtIG9iai5sYXQpLFxyXG5cdFx0ICAgICAgICBNYXRoLmFicyh0aGlzLmxuZyAtIG9iai5sbmcpKTtcclxuXHJcblx0XHRyZXR1cm4gbWFyZ2luIDw9IChtYXhNYXJnaW4gPT09IHVuZGVmaW5lZCA/IDEuMEUtOSA6IG1heE1hcmdpbik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b1N0cmluZygpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwb2ludCAoZm9yIGRlYnVnZ2luZyBwdXJwb3NlcykuXHJcblx0dG9TdHJpbmc6IGZ1bmN0aW9uIChwcmVjaXNpb24pIHtcclxuXHRcdHJldHVybiAnTGF0TG5nKCcgK1xyXG5cdFx0ICAgICAgICBMLlV0aWwuZm9ybWF0TnVtKHRoaXMubGF0LCBwcmVjaXNpb24pICsgJywgJyArXHJcblx0XHQgICAgICAgIEwuVXRpbC5mb3JtYXROdW0odGhpcy5sbmcsIHByZWNpc2lvbikgKyAnKSc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXN0YW5jZVRvKG90aGVyTGF0TG5nOiBMYXRMbmcpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBkaXN0YW5jZSAoaW4gbWV0ZXJzKSB0byB0aGUgZ2l2ZW4gYExhdExuZ2AgY2FsY3VsYXRlZCB1c2luZyB0aGUgW0hhdmVyc2luZSBmb3JtdWxhXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0hhdmVyc2luZV9mb3JtdWxhKS5cclxuXHRkaXN0YW5jZVRvOiBmdW5jdGlvbiAob3RoZXIpIHtcclxuXHRcdHJldHVybiBMLkNSUy5FYXJ0aC5kaXN0YW5jZSh0aGlzLCBMLmxhdExuZyhvdGhlcikpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgd3JhcCgpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIGEgbmV3IGBMYXRMbmdgIG9iamVjdCB3aXRoIHRoZSBsb25naXR1ZGUgd3JhcHBlZCBzbyBpdCdzIGFsd2F5cyBiZXR3ZWVuIC0xODAgYW5kICsxODAgZGVncmVlcy5cclxuXHR3cmFwOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gTC5DUlMuRWFydGgud3JhcExhdExuZyh0aGlzKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHRvQm91bmRzKHNpemVJbk1ldGVyczogTnVtYmVyKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyBhIG5ldyBgTGF0TG5nQm91bmRzYCBvYmplY3QgaW4gd2hpY2ggZWFjaCBib3VuZGFyeSBpcyBgc2l6ZUluTWV0ZXJzLzJgIG1ldGVycyBhcGFydCBmcm9tIHRoZSBgTGF0TG5nYC5cclxuXHR0b0JvdW5kczogZnVuY3Rpb24gKHNpemVJbk1ldGVycykge1xyXG5cdFx0dmFyIGxhdEFjY3VyYWN5ID0gMTgwICogc2l6ZUluTWV0ZXJzIC8gNDAwNzUwMTcsXHJcblx0XHQgICAgbG5nQWNjdXJhY3kgPSBsYXRBY2N1cmFjeSAvIE1hdGguY29zKChNYXRoLlBJIC8gMTgwKSAqIHRoaXMubGF0KTtcclxuXHJcblx0XHRyZXR1cm4gTC5sYXRMbmdCb3VuZHMoXHJcblx0XHQgICAgICAgIFt0aGlzLmxhdCAtIGxhdEFjY3VyYWN5LCB0aGlzLmxuZyAtIGxuZ0FjY3VyYWN5XSxcclxuXHRcdCAgICAgICAgW3RoaXMubGF0ICsgbGF0QWNjdXJhY3ksIHRoaXMubG5nICsgbG5nQWNjdXJhY3ldKTtcclxuXHR9LFxyXG5cclxuXHRjbG9uZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyh0aGlzLmxhdCwgdGhpcy5sbmcsIHRoaXMuYWx0KTtcclxuXHR9XHJcbn07XHJcblxyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nKGxhdGl0dWRlOiBOdW1iZXIsIGxvbmdpdHVkZTogTnVtYmVyLCBhbHRpdHVkZT86IE51bWJlcik6IExhdExuZ1xyXG4vLyBDcmVhdGVzIGFuIG9iamVjdCByZXByZXNlbnRpbmcgYSBnZW9ncmFwaGljYWwgcG9pbnQgd2l0aCB0aGUgZ2l2ZW4gbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSAoYW5kIG9wdGlvbmFsbHkgYWx0aXR1ZGUpLlxyXG5cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nKGNvb3JkczogQXJyYXkpOiBMYXRMbmdcclxuLy8gRXhwZWN0cyBhbiBhcnJheSBvZiB0aGUgZm9ybSBgW051bWJlciwgTnVtYmVyXWAgb3IgYFtOdW1iZXIsIE51bWJlciwgTnVtYmVyXWAgaW5zdGVhZC5cclxuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLmxhdExuZyhjb29yZHM6IE9iamVjdCk6IExhdExuZ1xyXG4vLyBFeHBlY3RzIGFuIHBsYWluIG9iamVjdCBvZiB0aGUgZm9ybSBge2xhdDogTnVtYmVyLCBsbmc6IE51bWJlcn1gIG9yIGB7bGF0OiBOdW1iZXIsIGxuZzogTnVtYmVyLCBhbHQ6IE51bWJlcn1gIGluc3RlYWQuXHJcblxyXG5MLmxhdExuZyA9IGZ1bmN0aW9uIChhLCBiLCBjKSB7XHJcblx0aWYgKGEgaW5zdGFuY2VvZiBMLkxhdExuZykge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdGlmIChMLlV0aWwuaXNBcnJheShhKSAmJiB0eXBlb2YgYVswXSAhPT0gJ29iamVjdCcpIHtcclxuXHRcdGlmIChhLmxlbmd0aCA9PT0gMykge1xyXG5cdFx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKGFbMF0sIGFbMV0sIGFbMl0pO1xyXG5cdFx0fVxyXG5cdFx0aWYgKGEubGVuZ3RoID09PSAyKSB7XHJcblx0XHRcdHJldHVybiBuZXcgTC5MYXRMbmcoYVswXSwgYVsxXSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gbnVsbDtcclxuXHR9XHJcblx0aWYgKGEgPT09IHVuZGVmaW5lZCB8fCBhID09PSBudWxsKSB7XHJcblx0XHRyZXR1cm4gYTtcclxuXHR9XHJcblx0aWYgKHR5cGVvZiBhID09PSAnb2JqZWN0JyAmJiAnbGF0JyBpbiBhKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKGEubGF0LCAnbG5nJyBpbiBhID8gYS5sbmcgOiBhLmxvbiwgYS5hbHQpO1xyXG5cdH1cclxuXHRpZiAoYiA9PT0gdW5kZWZpbmVkKSB7XHJcblx0XHRyZXR1cm4gbnVsbDtcclxuXHR9XHJcblx0cmV0dXJuIG5ldyBMLkxhdExuZyhhLCBiLCBjKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIExhdExuZ0JvdW5kc1xyXG4gKiBAYWthIEwuTGF0TG5nQm91bmRzXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYSByZWN0YW5ndWxhciBnZW9ncmFwaGljYWwgYXJlYSBvbiBhIG1hcC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIGNvcm5lcjEgPSBMLmxhdExuZyg0MC43MTIsIC03NC4yMjcpLFxyXG4gKiBjb3JuZXIyID0gTC5sYXRMbmcoNDAuNzc0LCAtNzQuMTI1KSxcclxuICogYm91bmRzID0gTC5sYXRMbmdCb3VuZHMoY29ybmVyMSwgY29ybmVyMik7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBBbGwgTGVhZmxldCBtZXRob2RzIHRoYXQgYWNjZXB0IExhdExuZ0JvdW5kcyBvYmplY3RzIGFsc28gYWNjZXB0IHRoZW0gaW4gYSBzaW1wbGUgQXJyYXkgZm9ybSAodW5sZXNzIG5vdGVkIG90aGVyd2lzZSksIHNvIHRoZSBib3VuZHMgZXhhbXBsZSBhYm92ZSBjYW4gYmUgcGFzc2VkIGxpa2UgdGhpczpcclxuICpcclxuICogYGBganNcclxuICogbWFwLmZpdEJvdW5kcyhbXHJcbiAqIFx0WzQwLjcxMiwgLTc0LjIyN10sXHJcbiAqIFx0WzQwLjc3NCwgLTc0LjEyNV1cclxuICogXSk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBDYXV0aW9uOiBpZiB0aGUgYXJlYSBjcm9zc2VzIHRoZSBhbnRpbWVyaWRpYW4gKG9mdGVuIGNvbmZ1c2VkIHdpdGggdGhlIEludGVybmF0aW9uYWwgRGF0ZSBMaW5lKSwgeW91IG11c3Qgc3BlY2lmeSBjb3JuZXJzIF9vdXRzaWRlXyB0aGUgWy0xODAsIDE4MF0gZGVncmVlcyBsb25naXR1ZGUgcmFuZ2UuXHJcbiAqL1xyXG5cclxuTC5MYXRMbmdCb3VuZHMgPSBmdW5jdGlvbiAoY29ybmVyMSwgY29ybmVyMikgeyAvLyAoTGF0TG5nLCBMYXRMbmcpIG9yIChMYXRMbmdbXSlcclxuXHRpZiAoIWNvcm5lcjEpIHsgcmV0dXJuOyB9XHJcblxyXG5cdHZhciBsYXRsbmdzID0gY29ybmVyMiA/IFtjb3JuZXIxLCBjb3JuZXIyXSA6IGNvcm5lcjE7XHJcblxyXG5cdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsYXRsbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHR0aGlzLmV4dGVuZChsYXRsbmdzW2ldKTtcclxuXHR9XHJcbn07XHJcblxyXG5MLkxhdExuZ0JvdW5kcy5wcm90b3R5cGUgPSB7XHJcblxyXG5cdC8vIEBtZXRob2QgZXh0ZW5kKGxhdGxuZzogTGF0TG5nKTogdGhpc1xyXG5cdC8vIEV4dGVuZCB0aGUgYm91bmRzIHRvIGNvbnRhaW4gdGhlIGdpdmVuIHBvaW50XHJcblxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2QgZXh0ZW5kKG90aGVyQm91bmRzOiBMYXRMbmdCb3VuZHMpOiB0aGlzXHJcblx0Ly8gRXh0ZW5kIHRoZSBib3VuZHMgdG8gY29udGFpbiB0aGUgZ2l2ZW4gYm91bmRzXHJcblx0ZXh0ZW5kOiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgc3cyLCBuZTI7XHJcblxyXG5cdFx0aWYgKG9iaiBpbnN0YW5jZW9mIEwuTGF0TG5nKSB7XHJcblx0XHRcdHN3MiA9IG9iajtcclxuXHRcdFx0bmUyID0gb2JqO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAob2JqIGluc3RhbmNlb2YgTC5MYXRMbmdCb3VuZHMpIHtcclxuXHRcdFx0c3cyID0gb2JqLl9zb3V0aFdlc3Q7XHJcblx0XHRcdG5lMiA9IG9iai5fbm9ydGhFYXN0O1xyXG5cclxuXHRcdFx0aWYgKCFzdzIgfHwgIW5lMikgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHJldHVybiBvYmogPyB0aGlzLmV4dGVuZChMLmxhdExuZyhvYmopIHx8IEwubGF0TG5nQm91bmRzKG9iaikpIDogdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXN3ICYmICFuZSkge1xyXG5cdFx0XHR0aGlzLl9zb3V0aFdlc3QgPSBuZXcgTC5MYXRMbmcoc3cyLmxhdCwgc3cyLmxuZyk7XHJcblx0XHRcdHRoaXMuX25vcnRoRWFzdCA9IG5ldyBMLkxhdExuZyhuZTIubGF0LCBuZTIubG5nKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHN3LmxhdCA9IE1hdGgubWluKHN3Mi5sYXQsIHN3LmxhdCk7XHJcblx0XHRcdHN3LmxuZyA9IE1hdGgubWluKHN3Mi5sbmcsIHN3LmxuZyk7XHJcblx0XHRcdG5lLmxhdCA9IE1hdGgubWF4KG5lMi5sYXQsIG5lLmxhdCk7XHJcblx0XHRcdG5lLmxuZyA9IE1hdGgubWF4KG5lMi5sbmcsIG5lLmxuZyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwYWQoYnVmZmVyUmF0aW86IE51bWJlcik6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgYmlnZ2VyIGJvdW5kcyBjcmVhdGVkIGJ5IGV4dGVuZGluZyB0aGUgY3VycmVudCBib3VuZHMgYnkgYSBnaXZlbiBwZXJjZW50YWdlIGluIGVhY2ggZGlyZWN0aW9uLlxyXG5cdHBhZDogZnVuY3Rpb24gKGJ1ZmZlclJhdGlvKSB7XHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgaGVpZ2h0QnVmZmVyID0gTWF0aC5hYnMoc3cubGF0IC0gbmUubGF0KSAqIGJ1ZmZlclJhdGlvLFxyXG5cdFx0ICAgIHdpZHRoQnVmZmVyID0gTWF0aC5hYnMoc3cubG5nIC0gbmUubG5nKSAqIGJ1ZmZlclJhdGlvO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmdCb3VuZHMoXHJcblx0XHQgICAgICAgIG5ldyBMLkxhdExuZyhzdy5sYXQgLSBoZWlnaHRCdWZmZXIsIHN3LmxuZyAtIHdpZHRoQnVmZmVyKSxcclxuXHRcdCAgICAgICAgbmV3IEwuTGF0TG5nKG5lLmxhdCArIGhlaWdodEJ1ZmZlciwgbmUubG5nICsgd2lkdGhCdWZmZXIpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENlbnRlcigpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBjZW50ZXIgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXRDZW50ZXI6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcoXHJcblx0XHQgICAgICAgICh0aGlzLl9zb3V0aFdlc3QubGF0ICsgdGhpcy5fbm9ydGhFYXN0LmxhdCkgLyAyLFxyXG5cdFx0ICAgICAgICAodGhpcy5fc291dGhXZXN0LmxuZyArIHRoaXMuX25vcnRoRWFzdC5sbmcpIC8gMik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTb3V0aFdlc3QoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgc291dGgtd2VzdCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldFNvdXRoV2VzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3NvdXRoV2VzdDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldE5vcnRoRWFzdCgpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBub3J0aC1lYXN0IHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0Tm9ydGhFYXN0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbm9ydGhFYXN0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Tm9ydGhXZXN0KCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIG5vcnRoLXdlc3QgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXROb3J0aFdlc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcodGhpcy5nZXROb3J0aCgpLCB0aGlzLmdldFdlc3QoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTb3V0aEVhc3QoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgc291dGgtZWFzdCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldFNvdXRoRWFzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyh0aGlzLmdldFNvdXRoKCksIHRoaXMuZ2V0RWFzdCgpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFdlc3QoKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgd2VzdCBsb25naXR1ZGUgb2YgdGhlIGJvdW5kc1xyXG5cdGdldFdlc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9zb3V0aFdlc3QubG5nO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0U291dGgoKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgc291dGggbGF0aXR1ZGUgb2YgdGhlIGJvdW5kc1xyXG5cdGdldFNvdXRoOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fc291dGhXZXN0LmxhdDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldEVhc3QoKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZWFzdCBsb25naXR1ZGUgb2YgdGhlIGJvdW5kc1xyXG5cdGdldEVhc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9ub3J0aEVhc3QubG5nO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Tm9ydGgoKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbm9ydGggbGF0aXR1ZGUgb2YgdGhlIGJvdW5kc1xyXG5cdGdldE5vcnRoOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbm9ydGhFYXN0LmxhdDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zKG90aGVyQm91bmRzOiBMYXRMbmdCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBjb250YWlucyB0aGUgZ2l2ZW4gb25lLlxyXG5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zIChsYXRsbmc6IExhdExuZyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGNvbnRhaW5zIHRoZSBnaXZlbiBwb2ludC5cclxuXHRjb250YWluczogZnVuY3Rpb24gKG9iaikgeyAvLyAoTGF0TG5nQm91bmRzKSBvciAoTGF0TG5nKSAtPiBCb29sZWFuXHJcblx0XHRpZiAodHlwZW9mIG9ialswXSA9PT0gJ251bWJlcicgfHwgb2JqIGluc3RhbmNlb2YgTC5MYXRMbmcgfHwgJ2xhdCcgaW4gb2JqKSB7XHJcblx0XHRcdG9iaiA9IEwubGF0TG5nKG9iaik7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRvYmogPSBMLmxhdExuZ0JvdW5kcyhvYmopO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBzdzIsIG5lMjtcclxuXHJcblx0XHRpZiAob2JqIGluc3RhbmNlb2YgTC5MYXRMbmdCb3VuZHMpIHtcclxuXHRcdFx0c3cyID0gb2JqLmdldFNvdXRoV2VzdCgpO1xyXG5cdFx0XHRuZTIgPSBvYmouZ2V0Tm9ydGhFYXN0KCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRzdzIgPSBuZTIgPSBvYmo7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIChzdzIubGF0ID49IHN3LmxhdCkgJiYgKG5lMi5sYXQgPD0gbmUubGF0KSAmJlxyXG5cdFx0ICAgICAgIChzdzIubG5nID49IHN3LmxuZykgJiYgKG5lMi5sbmcgPD0gbmUubG5nKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGludGVyc2VjdHMob3RoZXJCb3VuZHM6IExhdExuZ0JvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGludGVyc2VjdHMgdGhlIGdpdmVuIGJvdW5kcy4gVHdvIGJvdW5kcyBpbnRlcnNlY3QgaWYgdGhleSBoYXZlIGF0IGxlYXN0IG9uZSBwb2ludCBpbiBjb21tb24uXHJcblx0aW50ZXJzZWN0czogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0Ym91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgc3cyID0gYm91bmRzLmdldFNvdXRoV2VzdCgpLFxyXG5cdFx0ICAgIG5lMiA9IGJvdW5kcy5nZXROb3J0aEVhc3QoKSxcclxuXHJcblx0XHQgICAgbGF0SW50ZXJzZWN0cyA9IChuZTIubGF0ID49IHN3LmxhdCkgJiYgKHN3Mi5sYXQgPD0gbmUubGF0KSxcclxuXHRcdCAgICBsbmdJbnRlcnNlY3RzID0gKG5lMi5sbmcgPj0gc3cubG5nKSAmJiAoc3cyLmxuZyA8PSBuZS5sbmcpO1xyXG5cclxuXHRcdHJldHVybiBsYXRJbnRlcnNlY3RzICYmIGxuZ0ludGVyc2VjdHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBvdmVybGFwcyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgb3ZlcmxhcHMgdGhlIGdpdmVuIGJvdW5kcy4gVHdvIGJvdW5kcyBvdmVybGFwIGlmIHRoZWlyIGludGVyc2VjdGlvbiBpcyBhbiBhcmVhLlxyXG5cdG92ZXJsYXBzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHRib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBzdzIgPSBib3VuZHMuZ2V0U291dGhXZXN0KCksXHJcblx0XHQgICAgbmUyID0gYm91bmRzLmdldE5vcnRoRWFzdCgpLFxyXG5cclxuXHRcdCAgICBsYXRPdmVybGFwcyA9IChuZTIubGF0ID4gc3cubGF0KSAmJiAoc3cyLmxhdCA8IG5lLmxhdCksXHJcblx0XHQgICAgbG5nT3ZlcmxhcHMgPSAobmUyLmxuZyA+IHN3LmxuZykgJiYgKHN3Mi5sbmcgPCBuZS5sbmcpO1xyXG5cclxuXHRcdHJldHVybiBsYXRPdmVybGFwcyAmJiBsbmdPdmVybGFwcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHRvQkJveFN0cmluZygpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIGEgc3RyaW5nIHdpdGggYm91bmRpbmcgYm94IGNvb3JkaW5hdGVzIGluIGEgJ3NvdXRod2VzdF9sbmcsc291dGh3ZXN0X2xhdCxub3J0aGVhc3RfbG5nLG5vcnRoZWFzdF9sYXQnIGZvcm1hdC4gVXNlZnVsIGZvciBzZW5kaW5nIHJlcXVlc3RzIHRvIHdlYiBzZXJ2aWNlcyB0aGF0IHJldHVybiBnZW8gZGF0YS5cclxuXHR0b0JCb3hTdHJpbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBbdGhpcy5nZXRXZXN0KCksIHRoaXMuZ2V0U291dGgoKSwgdGhpcy5nZXRFYXN0KCksIHRoaXMuZ2V0Tm9ydGgoKV0uam9pbignLCcpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZXF1YWxzKG90aGVyQm91bmRzOiBMYXRMbmdCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBpcyBlcXVpdmFsZW50ICh3aXRoaW4gYSBzbWFsbCBtYXJnaW4gb2YgZXJyb3IpIHRvIHRoZSBnaXZlbiBib3VuZHMuXHJcblx0ZXF1YWxzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHRpZiAoIWJvdW5kcykgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHRib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzLl9zb3V0aFdlc3QuZXF1YWxzKGJvdW5kcy5nZXRTb3V0aFdlc3QoKSkgJiZcclxuXHRcdCAgICAgICB0aGlzLl9ub3J0aEVhc3QuZXF1YWxzKGJvdW5kcy5nZXROb3J0aEVhc3QoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpc1ZhbGlkKCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgYm91bmRzIGFyZSBwcm9wZXJseSBpbml0aWFsaXplZC5cclxuXHRpc1ZhbGlkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gISEodGhpcy5fc291dGhXZXN0ICYmIHRoaXMuX25vcnRoRWFzdCk7XHJcblx0fVxyXG59O1xyXG5cclxuLy8gVE9ETyBJbnRlcm5hdGlvbmFsIGRhdGUgbGluZT9cclxuXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nQm91bmRzKGNvcm5lcjE6IExhdExuZywgY29ybmVyMjogTGF0TG5nKVxyXG4vLyBDcmVhdGVzIGEgYExhdExuZ0JvdW5kc2Agb2JqZWN0IGJ5IGRlZmluaW5nIHR3byBkaWFnb25hbGx5IG9wcG9zaXRlIGNvcm5lcnMgb2YgdGhlIHJlY3RhbmdsZS5cclxuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLmxhdExuZ0JvdW5kcyhsYXRsbmdzOiBMYXRMbmdbXSlcclxuLy8gQ3JlYXRlcyBhIGBMYXRMbmdCb3VuZHNgIG9iamVjdCBkZWZpbmVkIGJ5IHRoZSBnZW9ncmFwaGljYWwgcG9pbnRzIGl0IGNvbnRhaW5zLiBWZXJ5IHVzZWZ1bCBmb3Igem9vbWluZyB0aGUgbWFwIHRvIGZpdCBhIHBhcnRpY3VsYXIgc2V0IG9mIGxvY2F0aW9ucyB3aXRoIFtgZml0Qm91bmRzYF0oI21hcC1maXRib3VuZHMpLlxyXG5MLmxhdExuZ0JvdW5kcyA9IGZ1bmN0aW9uIChhLCBiKSB7XHJcblx0aWYgKGEgaW5zdGFuY2VvZiBMLkxhdExuZ0JvdW5kcykge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdHJldHVybiBuZXcgTC5MYXRMbmdCb3VuZHMoYSwgYik7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgUHJvamVjdGlvblxyXG4gKiBAc2VjdGlvblxyXG4gKiBMZWFmbGV0IGNvbWVzIHdpdGggYSBzZXQgb2YgYWxyZWFkeSBkZWZpbmVkIFByb2plY3Rpb25zIG91dCBvZiB0aGUgYm94OlxyXG4gKlxyXG4gKiBAcHJvamVjdGlvbiBMLlByb2plY3Rpb24uTG9uTGF0XHJcbiAqXHJcbiAqIEVxdWlyZWN0YW5ndWxhciwgb3IgUGxhdGUgQ2FycmVlIHByb2plY3Rpb24g4oCUIHRoZSBtb3N0IHNpbXBsZSBwcm9qZWN0aW9uLFxyXG4gKiBtb3N0bHkgdXNlZCBieSBHSVMgZW50aHVzaWFzdHMuIERpcmVjdGx5IG1hcHMgYHhgIGFzIGxvbmdpdHVkZSwgYW5kIGB5YCBhc1xyXG4gKiBsYXRpdHVkZS4gQWxzbyBzdWl0YWJsZSBmb3IgZmxhdCB3b3JsZHMsIGUuZy4gZ2FtZSBtYXBzLiBVc2VkIGJ5IHRoZVxyXG4gKiBgRVBTRzozMzk1YCBhbmQgYFNpbXBsZWAgQ1JTLlxyXG4gKi9cclxuXHJcbkwuUHJvamVjdGlvbiA9IHt9O1xyXG5cclxuTC5Qcm9qZWN0aW9uLkxvbkxhdCA9IHtcclxuXHRwcm9qZWN0OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQobGF0bG5nLmxuZywgbGF0bG5nLmxhdCk7XHJcblx0fSxcclxuXHJcblx0dW5wcm9qZWN0OiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcocG9pbnQueSwgcG9pbnQueCk7XHJcblx0fSxcclxuXHJcblx0Ym91bmRzOiBMLmJvdW5kcyhbLTE4MCwgLTkwXSwgWzE4MCwgOTBdKVxyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIFByb2plY3Rpb25cclxuICogQHByb2plY3Rpb24gTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yXHJcbiAqXHJcbiAqIFNwaGVyaWNhbCBNZXJjYXRvciBwcm9qZWN0aW9uIOKAlCB0aGUgbW9zdCBjb21tb24gcHJvamVjdGlvbiBmb3Igb25saW5lIG1hcHMsXHJcbiAqIHVzZWQgYnkgYWxtb3N0IGFsbCBmcmVlIGFuZCBjb21tZXJjaWFsIHRpbGUgcHJvdmlkZXJzLiBBc3N1bWVzIHRoYXQgRWFydGggaXNcclxuICogYSBzcGhlcmUuIFVzZWQgYnkgdGhlIGBFUFNHOjM4NTdgIENSUy5cclxuICovXHJcblxyXG5MLlByb2plY3Rpb24uU3BoZXJpY2FsTWVyY2F0b3IgPSB7XHJcblxyXG5cdFI6IDYzNzgxMzcsXHJcblx0TUFYX0xBVElUVURFOiA4NS4wNTExMjg3Nzk4LFxyXG5cclxuXHRwcm9qZWN0OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHR2YXIgZCA9IE1hdGguUEkgLyAxODAsXHJcblx0XHQgICAgbWF4ID0gdGhpcy5NQVhfTEFUSVRVREUsXHJcblx0XHQgICAgbGF0ID0gTWF0aC5tYXgoTWF0aC5taW4obWF4LCBsYXRsbmcubGF0KSwgLW1heCksXHJcblx0XHQgICAgc2luID0gTWF0aC5zaW4obGF0ICogZCk7XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KFxyXG5cdFx0XHRcdHRoaXMuUiAqIGxhdGxuZy5sbmcgKiBkLFxyXG5cdFx0XHRcdHRoaXMuUiAqIE1hdGgubG9nKCgxICsgc2luKSAvICgxIC0gc2luKSkgLyAyKTtcclxuXHR9LFxyXG5cclxuXHR1bnByb2plY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0dmFyIGQgPSAxODAgLyBNYXRoLlBJO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcoXHJcblx0XHRcdCgyICogTWF0aC5hdGFuKE1hdGguZXhwKHBvaW50LnkgLyB0aGlzLlIpKSAtIChNYXRoLlBJIC8gMikpICogZCxcclxuXHRcdFx0cG9pbnQueCAqIGQgLyB0aGlzLlIpO1xyXG5cdH0sXHJcblxyXG5cdGJvdW5kczogKGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBkID0gNjM3ODEzNyAqIE1hdGguUEk7XHJcblx0XHRyZXR1cm4gTC5ib3VuZHMoWy1kLCAtZF0sIFtkLCBkXSk7XHJcblx0fSkoKVxyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQ1JTXHJcbiAqIEBha2EgTC5DUlNcclxuICogQWJzdHJhY3QgY2xhc3MgdGhhdCBkZWZpbmVzIGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbXMgZm9yIHByb2plY3RpbmdcclxuICogZ2VvZ3JhcGhpY2FsIHBvaW50cyBpbnRvIHBpeGVsIChzY3JlZW4pIGNvb3JkaW5hdGVzIGFuZCBiYWNrIChhbmQgdG9cclxuICogY29vcmRpbmF0ZXMgaW4gb3RoZXIgdW5pdHMgZm9yIFtXTVNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dlYl9NYXBfU2VydmljZSkgc2VydmljZXMpLiBTZWVcclxuICogW3NwYXRpYWwgcmVmZXJlbmNlIHN5c3RlbV0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db29yZGluYXRlX3JlZmVyZW5jZV9zeXN0ZW0pLlxyXG4gKlxyXG4gKiBMZWFmbGV0IGRlZmluZXMgdGhlIG1vc3QgdXN1YWwgQ1JTcyBieSBkZWZhdWx0LiBJZiB5b3Ugd2FudCB0byB1c2UgYVxyXG4gKiBDUlMgbm90IGRlZmluZWQgYnkgZGVmYXVsdCwgdGFrZSBhIGxvb2sgYXQgdGhlXHJcbiAqIFtQcm9qNExlYWZsZXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9rYXJ0ZW5hL1Byb2o0TGVhZmxldCkgcGx1Z2luLlxyXG4gKi9cclxuXHJcbkwuQ1JTID0ge1xyXG5cdC8vIEBtZXRob2QgbGF0TG5nVG9Qb2ludChsYXRsbmc6IExhdExuZywgem9vbTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBQcm9qZWN0cyBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZXMgaW50byBwaXhlbCBjb29yZGluYXRlcyBmb3IgYSBnaXZlbiB6b29tLlxyXG5cdGxhdExuZ1RvUG9pbnQ6IGZ1bmN0aW9uIChsYXRsbmcsIHpvb20pIHtcclxuXHRcdHZhciBwcm9qZWN0ZWRQb2ludCA9IHRoaXMucHJvamVjdGlvbi5wcm9qZWN0KGxhdGxuZyksXHJcblx0XHQgICAgc2NhbGUgPSB0aGlzLnNjYWxlKHpvb20pO1xyXG5cclxuXHRcdHJldHVybiB0aGlzLnRyYW5zZm9ybWF0aW9uLl90cmFuc2Zvcm0ocHJvamVjdGVkUG9pbnQsIHNjYWxlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBvaW50VG9MYXRMbmcocG9pbnQ6IFBvaW50LCB6b29tOiBOdW1iZXIpOiBMYXRMbmdcclxuXHQvLyBUaGUgaW52ZXJzZSBvZiBgbGF0TG5nVG9Qb2ludGAuIFByb2plY3RzIHBpeGVsIGNvb3JkaW5hdGVzIG9uIGEgZ2l2ZW5cclxuXHQvLyB6b29tIGludG8gZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzLlxyXG5cdHBvaW50VG9MYXRMbmc6IGZ1bmN0aW9uIChwb2ludCwgem9vbSkge1xyXG5cdFx0dmFyIHNjYWxlID0gdGhpcy5zY2FsZSh6b29tKSxcclxuXHRcdCAgICB1bnRyYW5zZm9ybWVkUG9pbnQgPSB0aGlzLnRyYW5zZm9ybWF0aW9uLnVudHJhbnNmb3JtKHBvaW50LCBzY2FsZSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMucHJvamVjdGlvbi51bnByb2plY3QodW50cmFuc2Zvcm1lZFBvaW50KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHByb2plY3QobGF0bG5nOiBMYXRMbmcpOiBQb2ludFxyXG5cdC8vIFByb2plY3RzIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlcyBpbnRvIGNvb3JkaW5hdGVzIGluIHVuaXRzIGFjY2VwdGVkIGZvclxyXG5cdC8vIHRoaXMgQ1JTIChlLmcuIG1ldGVycyBmb3IgRVBTRzozODU3LCBmb3IgcGFzc2luZyBpdCB0byBXTVMgc2VydmljZXMpLlxyXG5cdHByb2plY3Q6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLnByb2plY3Rpb24ucHJvamVjdChsYXRsbmcpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW5wcm9qZWN0KHBvaW50OiBQb2ludCk6IExhdExuZ1xyXG5cdC8vIEdpdmVuIGEgcHJvamVjdGVkIGNvb3JkaW5hdGUgcmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyBMYXRMbmcuXHJcblx0Ly8gVGhlIGludmVyc2Ugb2YgYHByb2plY3RgLlxyXG5cdHVucHJvamVjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0aW9uLnVucHJvamVjdChwb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzY2FsZSh6b29tOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBzY2FsZSB1c2VkIHdoZW4gdHJhbnNmb3JtaW5nIHByb2plY3RlZCBjb29yZGluYXRlcyBpbnRvXHJcblx0Ly8gcGl4ZWwgY29vcmRpbmF0ZXMgZm9yIGEgcGFydGljdWxhciB6b29tLiBGb3IgZXhhbXBsZSwgaXQgcmV0dXJuc1xyXG5cdC8vIGAyNTYgKiAyXnpvb21gIGZvciBNZXJjYXRvci1iYXNlZCBDUlMuXHJcblx0c2NhbGU6IGZ1bmN0aW9uICh6b29tKSB7XHJcblx0XHRyZXR1cm4gMjU2ICogTWF0aC5wb3coMiwgem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB6b29tKHNjYWxlOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBJbnZlcnNlIG9mIGBzY2FsZSgpYCwgcmV0dXJucyB0aGUgem9vbSBsZXZlbCBjb3JyZXNwb25kaW5nIHRvIGEgc2NhbGVcclxuXHQvLyBmYWN0b3Igb2YgYHNjYWxlYC5cclxuXHR6b29tOiBmdW5jdGlvbiAoc2NhbGUpIHtcclxuXHRcdHJldHVybiBNYXRoLmxvZyhzY2FsZSAvIDI1NikgLyBNYXRoLkxOMjtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFByb2plY3RlZEJvdW5kcyh6b29tOiBOdW1iZXIpOiBCb3VuZHNcclxuXHQvLyBSZXR1cm5zIHRoZSBwcm9qZWN0aW9uJ3MgYm91bmRzIHNjYWxlZCBhbmQgdHJhbnNmb3JtZWQgZm9yIHRoZSBwcm92aWRlZCBgem9vbWAuXHJcblx0Z2V0UHJvamVjdGVkQm91bmRzOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0aWYgKHRoaXMuaW5maW5pdGUpIHsgcmV0dXJuIG51bGw7IH1cclxuXHJcblx0XHR2YXIgYiA9IHRoaXMucHJvamVjdGlvbi5ib3VuZHMsXHJcblx0XHQgICAgcyA9IHRoaXMuc2NhbGUoem9vbSksXHJcblx0XHQgICAgbWluID0gdGhpcy50cmFuc2Zvcm1hdGlvbi50cmFuc2Zvcm0oYi5taW4sIHMpLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMudHJhbnNmb3JtYXRpb24udHJhbnNmb3JtKGIubWF4LCBzKTtcclxuXHJcblx0XHRyZXR1cm4gTC5ib3VuZHMobWluLCBtYXgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzdGFuY2UobGF0bG5nMTogTGF0TG5nLCBsYXRsbmcyOiBMYXRMbmcpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHR3byBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZXMuXHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBjb2RlOiBTdHJpbmdcclxuXHQvLyBTdGFuZGFyZCBjb2RlIG5hbWUgb2YgdGhlIENSUyBwYXNzZWQgaW50byBXTVMgc2VydmljZXMgKGUuZy4gYCdFUFNHOjM4NTcnYClcclxuXHQvL1xyXG5cdC8vIEBwcm9wZXJ0eSB3cmFwTG5nOiBOdW1iZXJbXVxyXG5cdC8vIEFuIGFycmF5IG9mIHR3byBudW1iZXJzIGRlZmluaW5nIHdoZXRoZXIgdGhlIGxvbmdpdHVkZSAoaG9yaXpvbnRhbCkgY29vcmRpbmF0ZVxyXG5cdC8vIGF4aXMgd3JhcHMgYXJvdW5kIGEgZ2l2ZW4gcmFuZ2UgYW5kIGhvdy4gRGVmYXVsdHMgdG8gYFstMTgwLCAxODBdYCBpbiBtb3N0XHJcblx0Ly8gZ2VvZ3JhcGhpY2FsIENSU3MuIElmIGB1bmRlZmluZWRgLCB0aGUgbG9uZ2l0dWRlIGF4aXMgZG9lcyBub3Qgd3JhcCBhcm91bmQuXHJcblx0Ly9cclxuXHQvLyBAcHJvcGVydHkgd3JhcExhdDogTnVtYmVyW11cclxuXHQvLyBMaWtlIGB3cmFwTG5nYCwgYnV0IGZvciB0aGUgbGF0aXR1ZGUgKHZlcnRpY2FsKSBheGlzLlxyXG5cclxuXHQvLyB3cmFwTG5nOiBbbWluLCBtYXhdLFxyXG5cdC8vIHdyYXBMYXQ6IFttaW4sIG1heF0sXHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBpbmZpbml0ZTogQm9vbGVhblxyXG5cdC8vIElmIHRydWUsIHRoZSBjb29yZGluYXRlIHNwYWNlIHdpbGwgYmUgdW5ib3VuZGVkIChpbmZpbml0ZSBpbiBib3RoIGF4ZXMpXHJcblx0aW5maW5pdGU6IGZhbHNlLFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXBMYXRMbmcobGF0bG5nOiBMYXRMbmcpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIGEgYExhdExuZ2Agd2hlcmUgbGF0IGFuZCBsbmcgaGFzIGJlZW4gd3JhcHBlZCBhY2NvcmRpbmcgdG8gdGhlXHJcblx0Ly8gQ1JTJ3MgYHdyYXBMYXRgIGFuZCBgd3JhcExuZ2AgcHJvcGVydGllcywgaWYgdGhleSBhcmUgb3V0c2lkZSB0aGUgQ1JTJ3MgYm91bmRzLlxyXG5cdC8vIE9ubHkgYWNjZXB0cyBhY3R1YWwgYEwuTGF0TG5nYCBpbnN0YW5jZXMsIG5vdCBhcnJheXMuXHJcblx0d3JhcExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIGxuZyA9IHRoaXMud3JhcExuZyA/IEwuVXRpbC53cmFwTnVtKGxhdGxuZy5sbmcsIHRoaXMud3JhcExuZywgdHJ1ZSkgOiBsYXRsbmcubG5nLFxyXG5cdFx0ICAgIGxhdCA9IHRoaXMud3JhcExhdCA/IEwuVXRpbC53cmFwTnVtKGxhdGxuZy5sYXQsIHRoaXMud3JhcExhdCwgdHJ1ZSkgOiBsYXRsbmcubGF0LFxyXG5cdFx0ICAgIGFsdCA9IGxhdGxuZy5hbHQ7XHJcblxyXG5cdFx0cmV0dXJuIEwubGF0TG5nKGxhdCwgbG5nLCBhbHQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgd3JhcExhdExuZ0JvdW5kcyhib3VuZHM6IExhdExuZ0JvdW5kcyk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgYSBgTGF0TG5nQm91bmRzYCB3aXRoIHRoZSBzYW1lIHNpemUgYXMgdGhlIGdpdmVuIG9uZSwgZW5zdXJpbmdcclxuXHQvLyB0aGF0IGl0cyBjZW50ZXIgaXMgd2l0aGluIHRoZSBDUlMncyBib3VuZHMuXHJcblx0Ly8gT25seSBhY2NlcHRzIGFjdHVhbCBgTC5MYXRMbmdCb3VuZHNgIGluc3RhbmNlcywgbm90IGFycmF5cy5cclxuXHR3cmFwTGF0TG5nQm91bmRzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHR2YXIgY2VudGVyID0gYm91bmRzLmdldENlbnRlcigpLFxyXG5cdFx0ICAgIG5ld0NlbnRlciA9IHRoaXMud3JhcExhdExuZyhjZW50ZXIpLFxyXG5cdFx0ICAgIGxhdFNoaWZ0ID0gY2VudGVyLmxhdCAtIG5ld0NlbnRlci5sYXQsXHJcblx0XHQgICAgbG5nU2hpZnQgPSBjZW50ZXIubG5nIC0gbmV3Q2VudGVyLmxuZztcclxuXHJcblx0XHRpZiAobGF0U2hpZnQgPT09IDAgJiYgbG5nU2hpZnQgPT09IDApIHtcclxuXHRcdFx0cmV0dXJuIGJvdW5kcztcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgc3cgPSBib3VuZHMuZ2V0U291dGhXZXN0KCksXHJcblx0XHQgICAgbmUgPSBib3VuZHMuZ2V0Tm9ydGhFYXN0KCksXHJcblx0XHQgICAgbmV3U3cgPSBMLmxhdExuZyh7bGF0OiBzdy5sYXQgLSBsYXRTaGlmdCwgbG5nOiBzdy5sbmcgLSBsbmdTaGlmdH0pLFxyXG5cdFx0ICAgIG5ld05lID0gTC5sYXRMbmcoe2xhdDogbmUubGF0IC0gbGF0U2hpZnQsIGxuZzogbmUubG5nIC0gbG5nU2hpZnR9KTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nQm91bmRzKG5ld1N3LCBuZXdOZSk7XHJcblx0fVxyXG59O1xyXG5cblxuXG4vKlxuICogQG5hbWVzcGFjZSBDUlNcbiAqIEBjcnMgTC5DUlMuU2ltcGxlXG4gKlxuICogQSBzaW1wbGUgQ1JTIHRoYXQgbWFwcyBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGludG8gYHhgIGFuZCBgeWAgZGlyZWN0bHkuXG4gKiBNYXkgYmUgdXNlZCBmb3IgbWFwcyBvZiBmbGF0IHN1cmZhY2VzIChlLmcuIGdhbWUgbWFwcykuIE5vdGUgdGhhdCB0aGUgYHlgXG4gKiBheGlzIHNob3VsZCBzdGlsbCBiZSBpbnZlcnRlZCAoZ29pbmcgZnJvbSBib3R0b20gdG8gdG9wKS4gYGRpc3RhbmNlKClgIHJldHVybnNcbiAqIHNpbXBsZSBldWNsaWRlYW4gZGlzdGFuY2UuXG4gKi9cblxuTC5DUlMuU2ltcGxlID0gTC5leHRlbmQoe30sIEwuQ1JTLCB7XG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5Mb25MYXQsXG5cdHRyYW5zZm9ybWF0aW9uOiBuZXcgTC5UcmFuc2Zvcm1hdGlvbigxLCAwLCAtMSwgMCksXG5cblx0c2NhbGU6IGZ1bmN0aW9uICh6b29tKSB7XG5cdFx0cmV0dXJuIE1hdGgucG93KDIsIHpvb20pO1xuXHR9LFxuXG5cdHpvb206IGZ1bmN0aW9uIChzY2FsZSkge1xuXHRcdHJldHVybiBNYXRoLmxvZyhzY2FsZSkgLyBNYXRoLkxOMjtcblx0fSxcblxuXHRkaXN0YW5jZTogZnVuY3Rpb24gKGxhdGxuZzEsIGxhdGxuZzIpIHtcblx0XHR2YXIgZHggPSBsYXRsbmcyLmxuZyAtIGxhdGxuZzEubG5nLFxuXHRcdCAgICBkeSA9IGxhdGxuZzIubGF0IC0gbGF0bG5nMS5sYXQ7XG5cblx0XHRyZXR1cm4gTWF0aC5zcXJ0KGR4ICogZHggKyBkeSAqIGR5KTtcblx0fSxcblxuXHRpbmZpbml0ZTogdHJ1ZVxufSk7XG5cblxuXG4vKlxuICogQG5hbWVzcGFjZSBDUlNcbiAqIEBjcnMgTC5DUlMuRWFydGhcbiAqXG4gKiBTZXJ2ZXMgYXMgdGhlIGJhc2UgZm9yIENSUyB0aGF0IGFyZSBnbG9iYWwgc3VjaCB0aGF0IHRoZXkgY292ZXIgdGhlIGVhcnRoLlxuICogQ2FuIG9ubHkgYmUgdXNlZCBhcyB0aGUgYmFzZSBmb3Igb3RoZXIgQ1JTIGFuZCBjYW5ub3QgYmUgdXNlZCBkaXJlY3RseSxcbiAqIHNpbmNlIGl0IGRvZXMgbm90IGhhdmUgYSBgY29kZWAsIGBwcm9qZWN0aW9uYCBvciBgdHJhbnNmb3JtYXRpb25gLiBgZGlzdGFuY2UoKWAgcmV0dXJuc1xuICogbWV0ZXJzLlxuICovXG5cbkwuQ1JTLkVhcnRoID0gTC5leHRlbmQoe30sIEwuQ1JTLCB7XG5cdHdyYXBMbmc6IFstMTgwLCAxODBdLFxuXG5cdC8vIE1lYW4gRWFydGggUmFkaXVzLCBhcyByZWNvbW1lbmRlZCBmb3IgdXNlIGJ5XG5cdC8vIHRoZSBJbnRlcm5hdGlvbmFsIFVuaW9uIG9mIEdlb2Rlc3kgYW5kIEdlb3BoeXNpY3MsXG5cdC8vIHNlZSBodHRwOi8vcm9zZXR0YWNvZGUub3JnL3dpa2kvSGF2ZXJzaW5lX2Zvcm11bGFcblx0UjogNjM3MTAwMCxcblxuXHQvLyBkaXN0YW5jZSBiZXR3ZWVuIHR3byBnZW9ncmFwaGljYWwgcG9pbnRzIHVzaW5nIHNwaGVyaWNhbCBsYXcgb2YgY29zaW5lcyBhcHByb3hpbWF0aW9uXG5cdGRpc3RhbmNlOiBmdW5jdGlvbiAobGF0bG5nMSwgbGF0bG5nMikge1xuXHRcdHZhciByYWQgPSBNYXRoLlBJIC8gMTgwLFxuXHRcdCAgICBsYXQxID0gbGF0bG5nMS5sYXQgKiByYWQsXG5cdFx0ICAgIGxhdDIgPSBsYXRsbmcyLmxhdCAqIHJhZCxcblx0XHQgICAgYSA9IE1hdGguc2luKGxhdDEpICogTWF0aC5zaW4obGF0MikgK1xuXHRcdCAgICAgICAgTWF0aC5jb3MobGF0MSkgKiBNYXRoLmNvcyhsYXQyKSAqIE1hdGguY29zKChsYXRsbmcyLmxuZyAtIGxhdGxuZzEubG5nKSAqIHJhZCk7XG5cblx0XHRyZXR1cm4gdGhpcy5SICogTWF0aC5hY29zKE1hdGgubWluKGEsIDEpKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIENSU1xyXG4gKiBAY3JzIEwuQ1JTLkVQU0czODU3XHJcbiAqXHJcbiAqIFRoZSBtb3N0IGNvbW1vbiBDUlMgZm9yIG9ubGluZSBtYXBzLCB1c2VkIGJ5IGFsbW9zdCBhbGwgZnJlZSBhbmQgY29tbWVyY2lhbFxyXG4gKiB0aWxlIHByb3ZpZGVycy4gVXNlcyBTcGhlcmljYWwgTWVyY2F0b3IgcHJvamVjdGlvbi4gU2V0IGluIGJ5IGRlZmF1bHQgaW5cclxuICogTWFwJ3MgYGNyc2Agb3B0aW9uLlxyXG4gKi9cclxuXHJcbkwuQ1JTLkVQU0czODU3ID0gTC5leHRlbmQoe30sIEwuQ1JTLkVhcnRoLCB7XHJcblx0Y29kZTogJ0VQU0c6Mzg1NycsXHJcblx0cHJvamVjdGlvbjogTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yLFxyXG5cclxuXHR0cmFuc2Zvcm1hdGlvbjogKGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBzY2FsZSA9IDAuNSAvIChNYXRoLlBJICogTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yLlIpO1xyXG5cdFx0cmV0dXJuIG5ldyBMLlRyYW5zZm9ybWF0aW9uKHNjYWxlLCAwLjUsIC1zY2FsZSwgMC41KTtcclxuXHR9KCkpXHJcbn0pO1xyXG5cclxuTC5DUlMuRVBTRzkwMDkxMyA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FUFNHMzg1Nywge1xyXG5cdGNvZGU6ICdFUFNHOjkwMDkxMydcclxufSk7XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgQ1JTXHJcbiAqIEBjcnMgTC5DUlMuRVBTRzQzMjZcclxuICpcclxuICogQSBjb21tb24gQ1JTIGFtb25nIEdJUyBlbnRodXNpYXN0cy4gVXNlcyBzaW1wbGUgRXF1aXJlY3Rhbmd1bGFyIHByb2plY3Rpb24uXHJcbiAqXHJcbiAqIExlYWZsZXQgMS4wLnggY29tcGxpZXMgd2l0aCB0aGUgW1RNUyBjb29yZGluYXRlIHNjaGVtZSBmb3IgRVBTRzo0MzI2XShodHRwczovL3dpa2kub3NnZW8ub3JnL3dpa2kvVGlsZV9NYXBfU2VydmljZV9TcGVjaWZpY2F0aW9uI2dsb2JhbC1nZW9kZXRpYyksXHJcbiAqIHdoaWNoIGlzIGEgYnJlYWtpbmcgY2hhbmdlIGZyb20gMC43LnggYmVoYXZpb3VyLiAgSWYgeW91IGFyZSB1c2luZyBhIGBUaWxlTGF5ZXJgXHJcbiAqIHdpdGggdGhpcyBDUlMsIGVuc3VyZSB0aGF0IHRoZXJlIGFyZSB0d28gMjU2eDI1NiBwaXhlbCB0aWxlcyBjb3ZlcmluZyB0aGVcclxuICogd2hvbGUgZWFydGggYXQgem9vbSBsZXZlbCB6ZXJvLCBhbmQgdGhhdCB0aGUgdGlsZSBjb29yZGluYXRlIG9yaWdpbiBpcyAoLTE4MCwrOTApLFxyXG4gKiBvciAoLTE4MCwtOTApIGZvciBgVGlsZUxheWVyYHMgd2l0aCBbdGhlIGB0bXNgIG9wdGlvbl0oI3RpbGVsYXllci10bXMpIHNldC5cclxuICovXHJcblxyXG5MLkNSUy5FUFNHNDMyNiA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FYXJ0aCwge1xyXG5cdGNvZGU6ICdFUFNHOjQzMjYnLFxyXG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5Mb25MYXQsXHJcblx0dHJhbnNmb3JtYXRpb246IG5ldyBMLlRyYW5zZm9ybWF0aW9uKDEgLyAxODAsIDEsIC0xIC8gMTgwLCAwLjUpXHJcbn0pO1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgTWFwXHJcbiAqIEBha2EgTC5NYXBcclxuICogQGluaGVyaXRzIEV2ZW50ZWRcclxuICpcclxuICogVGhlIGNlbnRyYWwgY2xhc3Mgb2YgdGhlIEFQSSDigJQgaXQgaXMgdXNlZCB0byBjcmVhdGUgYSBtYXAgb24gYSBwYWdlIGFuZCBtYW5pcHVsYXRlIGl0LlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiAvLyBpbml0aWFsaXplIHRoZSBtYXAgb24gdGhlIFwibWFwXCIgZGl2IHdpdGggYSBnaXZlbiBjZW50ZXIgYW5kIHpvb21cclxuICogdmFyIG1hcCA9IEwubWFwKCdtYXAnLCB7XHJcbiAqIFx0Y2VudGVyOiBbNTEuNTA1LCAtMC4wOV0sXHJcbiAqIFx0em9vbTogMTNcclxuICogfSk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKi9cclxuXHJcbkwuTWFwID0gTC5FdmVudGVkLmV4dGVuZCh7XHJcblxyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBzZWN0aW9uIE1hcCBTdGF0ZSBPcHRpb25zXHJcblx0XHQvLyBAb3B0aW9uIGNyczogQ1JTID0gTC5DUlMuRVBTRzM4NTdcclxuXHRcdC8vIFRoZSBbQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtXSgjY3JzKSB0byB1c2UuIERvbid0IGNoYW5nZSB0aGlzIGlmIHlvdSdyZSBub3RcclxuXHRcdC8vIHN1cmUgd2hhdCBpdCBtZWFucy5cclxuXHRcdGNyczogTC5DUlMuRVBTRzM4NTcsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjZW50ZXI6IExhdExuZyA9IHVuZGVmaW5lZFxyXG5cdFx0Ly8gSW5pdGlhbCBnZW9ncmFwaGljIGNlbnRlciBvZiB0aGUgbWFwXHJcblx0XHRjZW50ZXI6IHVuZGVmaW5lZCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb206IE51bWJlciA9IHVuZGVmaW5lZFxyXG5cdFx0Ly8gSW5pdGlhbCBtYXAgem9vbSBsZXZlbFxyXG5cdFx0em9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWluWm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXHJcblx0XHQvLyBNaW5pbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcC4gT3ZlcnJpZGVzIGFueSBgbWluWm9vbWAgb3B0aW9uIHNldCBvbiBtYXAgbGF5ZXJzLlxyXG5cdFx0bWluWm9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWF4Wm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcC4gT3ZlcnJpZGVzIGFueSBgbWF4Wm9vbWAgb3B0aW9uIHNldCBvbiBtYXAgbGF5ZXJzLlxyXG5cdFx0bWF4Wm9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbGF5ZXJzOiBMYXllcltdID0gW11cclxuXHRcdC8vIEFycmF5IG9mIGxheWVycyB0aGF0IHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1hcCBpbml0aWFsbHlcclxuXHRcdGxheWVyczogW10sXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhCb3VuZHM6IExhdExuZ0JvdW5kcyA9IG51bGxcclxuXHRcdC8vIFdoZW4gdGhpcyBvcHRpb24gaXMgc2V0LCB0aGUgbWFwIHJlc3RyaWN0cyB0aGUgdmlldyB0byB0aGUgZ2l2ZW5cclxuXHRcdC8vIGdlb2dyYXBoaWNhbCBib3VuZHMsIGJvdW5jaW5nIHRoZSB1c2VyIGJhY2sgaWYgdGhlIHVzZXIgdHJpZXMgdG8gcGFuXHJcblx0XHQvLyBvdXRzaWRlIHRoZSB2aWV3LiBUbyBzZXQgdGhlIHJlc3RyaWN0aW9uIGR5bmFtaWNhbGx5LCB1c2VcclxuXHRcdC8vIFtgc2V0TWF4Qm91bmRzYF0oI21hcC1zZXRtYXhib3VuZHMpIG1ldGhvZC5cclxuXHRcdG1heEJvdW5kczogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gcmVuZGVyZXI6IFJlbmRlcmVyID0gKlxyXG5cdFx0Ly8gVGhlIGRlZmF1bHQgbWV0aG9kIGZvciBkcmF3aW5nIHZlY3RvciBsYXllcnMgb24gdGhlIG1hcC4gYEwuU1ZHYFxyXG5cdFx0Ly8gb3IgYEwuQ2FudmFzYCBieSBkZWZhdWx0IGRlcGVuZGluZyBvbiBicm93c2VyIHN1cHBvcnQuXHJcblx0XHRyZW5kZXJlcjogdW5kZWZpbmVkLFxyXG5cclxuXHJcblx0XHQvLyBAc2VjdGlvbiBBbmltYXRpb24gT3B0aW9uc1xyXG5cdFx0Ly8gQG9wdGlvbiB6b29tQW5pbWF0aW9uOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFwIHpvb20gYW5pbWF0aW9uIGlzIGVuYWJsZWQuIEJ5IGRlZmF1bHQgaXQncyBlbmFibGVkXHJcblx0XHQvLyBpbiBhbGwgYnJvd3NlcnMgdGhhdCBzdXBwb3J0IENTUzMgVHJhbnNpdGlvbnMgZXhjZXB0IEFuZHJvaWQuXHJcblx0XHR6b29tQW5pbWF0aW9uOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbUFuaW1hdGlvblRocmVzaG9sZDogTnVtYmVyID0gNFxyXG5cdFx0Ly8gV29uJ3QgYW5pbWF0ZSB6b29tIGlmIHRoZSB6b29tIGRpZmZlcmVuY2UgZXhjZWVkcyB0aGlzIHZhbHVlLlxyXG5cdFx0em9vbUFuaW1hdGlvblRocmVzaG9sZDogNCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGZhZGVBbmltYXRpb246IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBXaGV0aGVyIHRoZSB0aWxlIGZhZGUgYW5pbWF0aW9uIGlzIGVuYWJsZWQuIEJ5IGRlZmF1bHQgaXQncyBlbmFibGVkXHJcblx0XHQvLyBpbiBhbGwgYnJvd3NlcnMgdGhhdCBzdXBwb3J0IENTUzMgVHJhbnNpdGlvbnMgZXhjZXB0IEFuZHJvaWQuXHJcblx0XHRmYWRlQW5pbWF0aW9uOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWFya2VyWm9vbUFuaW1hdGlvbjogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgbWFya2VycyBhbmltYXRlIHRoZWlyIHpvb20gd2l0aCB0aGUgem9vbSBhbmltYXRpb24sIGlmIGRpc2FibGVkXHJcblx0XHQvLyB0aGV5IHdpbGwgZGlzYXBwZWFyIGZvciB0aGUgbGVuZ3RoIG9mIHRoZSBhbmltYXRpb24uIEJ5IGRlZmF1bHQgaXQnc1xyXG5cdFx0Ly8gZW5hYmxlZCBpbiBhbGwgYnJvd3NlcnMgdGhhdCBzdXBwb3J0IENTUzMgVHJhbnNpdGlvbnMgZXhjZXB0IEFuZHJvaWQuXHJcblx0XHRtYXJrZXJab29tQW5pbWF0aW9uOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdHJhbnNmb3JtM0RMaW1pdDogTnVtYmVyID0gMl4yM1xyXG5cdFx0Ly8gRGVmaW5lcyB0aGUgbWF4aW11bSBzaXplIG9mIGEgQ1NTIHRyYW5zbGF0aW9uIHRyYW5zZm9ybS4gVGhlIGRlZmF1bHRcclxuXHRcdC8vIHZhbHVlIHNob3VsZCBub3QgYmUgY2hhbmdlZCB1bmxlc3MgYSB3ZWIgYnJvd3NlciBwb3NpdGlvbnMgbGF5ZXJzIGluXHJcblx0XHQvLyB0aGUgd3JvbmcgcGxhY2UgYWZ0ZXIgZG9pbmcgYSBsYXJnZSBgcGFuQnlgLlxyXG5cdFx0dHJhbnNmb3JtM0RMaW1pdDogODM4ODYwOCwgLy8gUHJlY2lzaW9uIGxpbWl0IG9mIGEgMzItYml0IGZsb2F0XHJcblxyXG5cdFx0Ly8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xyXG5cdFx0Ly8gQG9wdGlvbiB6b29tU25hcDogTnVtYmVyID0gMVxyXG5cdFx0Ly8gRm9yY2VzIHRoZSBtYXAncyB6b29tIGxldmVsIHRvIGFsd2F5cyBiZSBhIG11bHRpcGxlIG9mIHRoaXMsIHBhcnRpY3VsYXJseVxyXG5cdFx0Ly8gcmlnaHQgYWZ0ZXIgYSBbYGZpdEJvdW5kcygpYF0oI21hcC1maXRib3VuZHMpIG9yIGEgcGluY2gtem9vbS5cclxuXHRcdC8vIEJ5IGRlZmF1bHQsIHRoZSB6b29tIGxldmVsIHNuYXBzIHRvIHRoZSBuZWFyZXN0IGludGVnZXI7IGxvd2VyIHZhbHVlc1xyXG5cdFx0Ly8gKGUuZy4gYDAuNWAgb3IgYDAuMWApIGFsbG93IGZvciBncmVhdGVyIGdyYW51bGFyaXR5LiBBIHZhbHVlIG9mIGAwYFxyXG5cdFx0Ly8gbWVhbnMgdGhlIHpvb20gbGV2ZWwgd2lsbCBub3QgYmUgc25hcHBlZCBhZnRlciBgZml0Qm91bmRzYCBvciBhIHBpbmNoLXpvb20uXHJcblx0XHR6b29tU25hcDogMSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21EZWx0YTogTnVtYmVyID0gMVxyXG5cdFx0Ly8gQ29udHJvbHMgaG93IG11Y2ggdGhlIG1hcCdzIHpvb20gbGV2ZWwgd2lsbCBjaGFuZ2UgYWZ0ZXIgYVxyXG5cdFx0Ly8gW2B6b29tSW4oKWBdKCNtYXAtem9vbWluKSwgW2B6b29tT3V0KClgXSgjbWFwLXpvb21vdXQpLCBwcmVzc2luZyBgK2BcclxuXHRcdC8vIG9yIGAtYCBvbiB0aGUga2V5Ym9hcmQsIG9yIHVzaW5nIHRoZSBbem9vbSBjb250cm9sc10oI2NvbnRyb2wtem9vbSkuXHJcblx0XHQvLyBWYWx1ZXMgc21hbGxlciB0aGFuIGAxYCAoZS5nLiBgMC41YCkgYWxsb3cgZm9yIGdyZWF0ZXIgZ3JhbnVsYXJpdHkuXHJcblx0XHR6b29tRGVsdGE6IDEsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0cmFja1Jlc2l6ZTogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgdGhlIG1hcCBhdXRvbWF0aWNhbGx5IGhhbmRsZXMgYnJvd3NlciB3aW5kb3cgcmVzaXplIHRvIHVwZGF0ZSBpdHNlbGYuXHJcblx0XHR0cmFja1Jlc2l6ZTogdHJ1ZVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChpZCwgb3B0aW9ucykgeyAvLyAoSFRNTEVsZW1lbnQgb3IgU3RyaW5nLCBPYmplY3QpXHJcblx0XHRvcHRpb25zID0gTC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX2luaXRDb250YWluZXIoaWQpO1xyXG5cdFx0dGhpcy5faW5pdExheW91dCgpO1xyXG5cclxuXHRcdC8vIGhhY2sgZm9yIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzE5ODBcclxuXHRcdHRoaXMuX29uUmVzaXplID0gTC5iaW5kKHRoaXMuX29uUmVzaXplLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9pbml0RXZlbnRzKCk7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMubWF4Qm91bmRzKSB7XHJcblx0XHRcdHRoaXMuc2V0TWF4Qm91bmRzKG9wdGlvbnMubWF4Qm91bmRzKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAob3B0aW9ucy56b29tICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0dGhpcy5fem9vbSA9IHRoaXMuX2xpbWl0Wm9vbShvcHRpb25zLnpvb20pO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChvcHRpb25zLmNlbnRlciAmJiBvcHRpb25zLnpvb20gIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR0aGlzLnNldFZpZXcoTC5sYXRMbmcob3B0aW9ucy5jZW50ZXIpLCBvcHRpb25zLnpvb20sIHtyZXNldDogdHJ1ZX0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2hhbmRsZXJzID0gW107XHJcblx0XHR0aGlzLl9sYXllcnMgPSB7fTtcclxuXHRcdHRoaXMuX3pvb21Cb3VuZExheWVycyA9IHt9O1xyXG5cdFx0dGhpcy5fc2l6ZUNoYW5nZWQgPSB0cnVlO1xyXG5cclxuXHRcdHRoaXMuY2FsbEluaXRIb29rcygpO1xyXG5cclxuXHRcdC8vIGRvbid0IGFuaW1hdGUgb24gYnJvd3NlcnMgd2l0aG91dCBoYXJkd2FyZS1hY2NlbGVyYXRlZCB0cmFuc2l0aW9ucyBvciBvbGQgQW5kcm9pZC9PcGVyYVxyXG5cdFx0dGhpcy5fem9vbUFuaW1hdGVkID0gTC5Eb21VdGlsLlRSQU5TSVRJT04gJiYgTC5Ccm93c2VyLmFueTNkICYmICFMLkJyb3dzZXIubW9iaWxlT3BlcmEgJiZcclxuXHRcdFx0XHR0aGlzLm9wdGlvbnMuem9vbUFuaW1hdGlvbjtcclxuXHJcblx0XHQvLyB6b29tIHRyYW5zaXRpb25zIHJ1biB3aXRoIHRoZSBzYW1lIGR1cmF0aW9uIGZvciBhbGwgbGF5ZXJzLCBzbyBpZiBvbmUgb2YgdHJhbnNpdGlvbmVuZCBldmVudHNcclxuXHRcdC8vIGhhcHBlbnMgYWZ0ZXIgc3RhcnRpbmcgem9vbSBhbmltYXRpb24gKHByb3BhZ2F0aW5nIHRvIHRoZSBtYXAgcGFuZSksIHdlIGtub3cgdGhhdCBpdCBlbmRlZCBnbG9iYWxseVxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHR0aGlzLl9jcmVhdGVBbmltUHJveHkoKTtcclxuXHRcdFx0TC5Eb21FdmVudC5vbih0aGlzLl9wcm94eSwgTC5Eb21VdGlsLlRSQU5TSVRJT05fRU5ELCB0aGlzLl9jYXRjaFRyYW5zaXRpb25FbmQsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2FkZExheWVycyh0aGlzLm9wdGlvbnMubGF5ZXJzKTtcclxuXHR9LFxyXG5cclxuXHJcblx0Ly8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgbW9kaWZ5aW5nIG1hcCBzdGF0ZVxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFZpZXcoY2VudGVyOiBMYXRMbmcsIHpvb206IE51bWJlciwgb3B0aW9ucz86IFpvb20vcGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdmlldyBvZiB0aGUgbWFwIChnZW9ncmFwaGljYWwgY2VudGVyIGFuZCB6b29tKSB3aXRoIHRoZSBnaXZlblxyXG5cdC8vIGFuaW1hdGlvbiBvcHRpb25zLlxyXG5cdHNldFZpZXc6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIG9wdGlvbnMpIHtcclxuXHJcblx0XHR6b29tID0gem9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fem9vbSA6IHRoaXMuX2xpbWl0Wm9vbSh6b29tKTtcclxuXHRcdGNlbnRlciA9IHRoaXMuX2xpbWl0Q2VudGVyKEwubGF0TG5nKGNlbnRlciksIHpvb20sIHRoaXMub3B0aW9ucy5tYXhCb3VuZHMpO1xyXG5cdFx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcblxyXG5cdFx0dGhpcy5fc3RvcCgpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sb2FkZWQgJiYgIW9wdGlvbnMucmVzZXQgJiYgb3B0aW9ucyAhPT0gdHJ1ZSkge1xyXG5cclxuXHRcdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdFx0b3B0aW9ucy56b29tID0gTC5leHRlbmQoe2FuaW1hdGU6IG9wdGlvbnMuYW5pbWF0ZX0sIG9wdGlvbnMuem9vbSk7XHJcblx0XHRcdFx0b3B0aW9ucy5wYW4gPSBMLmV4dGVuZCh7YW5pbWF0ZTogb3B0aW9ucy5hbmltYXRlLCBkdXJhdGlvbjogb3B0aW9ucy5kdXJhdGlvbn0sIG9wdGlvbnMucGFuKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gdHJ5IGFuaW1hdGluZyBwYW4gb3Igem9vbVxyXG5cdFx0XHR2YXIgbW92ZWQgPSAodGhpcy5fem9vbSAhPT0gem9vbSkgP1xyXG5cdFx0XHRcdHRoaXMuX3RyeUFuaW1hdGVkWm9vbSAmJiB0aGlzLl90cnlBbmltYXRlZFpvb20oY2VudGVyLCB6b29tLCBvcHRpb25zLnpvb20pIDpcclxuXHRcdFx0XHR0aGlzLl90cnlBbmltYXRlZFBhbihjZW50ZXIsIG9wdGlvbnMucGFuKTtcclxuXHJcblx0XHRcdGlmIChtb3ZlZCkge1xyXG5cdFx0XHRcdC8vIHByZXZlbnQgcmVzaXplIGhhbmRsZXIgY2FsbCwgdGhlIHZpZXcgd2lsbCByZWZyZXNoIGFmdGVyIGFuaW1hdGlvbiBhbnl3YXlcclxuXHRcdFx0XHRjbGVhclRpbWVvdXQodGhpcy5fc2l6ZVRpbWVyKTtcclxuXHRcdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIGFuaW1hdGlvbiBkaWRuJ3Qgc3RhcnQsIGp1c3QgcmVzZXQgdGhlIG1hcCB2aWV3XHJcblx0XHR0aGlzLl9yZXNldFZpZXcoY2VudGVyLCB6b29tKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFpvb20oem9vbTogTnVtYmVyLCBvcHRpb25zOiBab29tL3BhbiBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHpvb20gb2YgdGhlIG1hcC5cclxuXHRzZXRab29tOiBmdW5jdGlvbiAoem9vbSwgb3B0aW9ucykge1xyXG5cdFx0aWYgKCF0aGlzLl9sb2FkZWQpIHtcclxuXHRcdFx0dGhpcy5fem9vbSA9IHpvb207XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Vmlldyh0aGlzLmdldENlbnRlcigpLCB6b29tLCB7em9vbTogb3B0aW9uc30pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgem9vbUluKGRlbHRhPzogTnVtYmVyLCBvcHRpb25zPzogWm9vbSBvcHRpb25zKTogdGhpc1xyXG5cdC8vIEluY3JlYXNlcyB0aGUgem9vbSBvZiB0aGUgbWFwIGJ5IGBkZWx0YWAgKFtgem9vbURlbHRhYF0oI21hcC16b29tZGVsdGEpIGJ5IGRlZmF1bHQpLlxyXG5cdHpvb21JbjogZnVuY3Rpb24gKGRlbHRhLCBvcHRpb25zKSB7XHJcblx0XHRkZWx0YSA9IGRlbHRhIHx8IChMLkJyb3dzZXIuYW55M2QgPyB0aGlzLm9wdGlvbnMuem9vbURlbHRhIDogMSk7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHRoaXMuX3pvb20gKyBkZWx0YSwgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB6b29tT3V0KGRlbHRhPzogTnVtYmVyLCBvcHRpb25zPzogWm9vbSBvcHRpb25zKTogdGhpc1xyXG5cdC8vIERlY3JlYXNlcyB0aGUgem9vbSBvZiB0aGUgbWFwIGJ5IGBkZWx0YWAgKFtgem9vbURlbHRhYF0oI21hcC16b29tZGVsdGEpIGJ5IGRlZmF1bHQpLlxyXG5cdHpvb21PdXQ6IGZ1bmN0aW9uIChkZWx0YSwgb3B0aW9ucykge1xyXG5cdFx0ZGVsdGEgPSBkZWx0YSB8fCAoTC5Ccm93c2VyLmFueTNkID8gdGhpcy5vcHRpb25zLnpvb21EZWx0YSA6IDEpO1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Wm9vbSh0aGlzLl96b29tIC0gZGVsdGEsIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0Wm9vbUFyb3VuZChsYXRsbmc6IExhdExuZywgem9vbTogTnVtYmVyLCBvcHRpb25zOiBab29tIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gWm9vbXMgdGhlIG1hcCB3aGlsZSBrZWVwaW5nIGEgc3BlY2lmaWVkIGdlb2dyYXBoaWNhbCBwb2ludCBvbiB0aGUgbWFwXHJcblx0Ly8gc3RhdGlvbmFyeSAoZS5nLiB1c2VkIGludGVybmFsbHkgZm9yIHNjcm9sbCB6b29tIGFuZCBkb3VibGUtY2xpY2sgem9vbSkuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCBzZXRab29tQXJvdW5kKG9mZnNldDogUG9pbnQsIHpvb206IE51bWJlciwgb3B0aW9uczogWm9vbSBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFpvb21zIHRoZSBtYXAgd2hpbGUga2VlcGluZyBhIHNwZWNpZmllZCBwaXhlbCBvbiB0aGUgbWFwIChyZWxhdGl2ZSB0byB0aGUgdG9wLWxlZnQgY29ybmVyKSBzdGF0aW9uYXJ5LlxyXG5cdHNldFpvb21Bcm91bmQ6IGZ1bmN0aW9uIChsYXRsbmcsIHpvb20sIG9wdGlvbnMpIHtcclxuXHRcdHZhciBzY2FsZSA9IHRoaXMuZ2V0Wm9vbVNjYWxlKHpvb20pLFxyXG5cdFx0ICAgIHZpZXdIYWxmID0gdGhpcy5nZXRTaXplKCkuZGl2aWRlQnkoMiksXHJcblx0XHQgICAgY29udGFpbmVyUG9pbnQgPSBsYXRsbmcgaW5zdGFuY2VvZiBMLlBvaW50ID8gbGF0bG5nIDogdGhpcy5sYXRMbmdUb0NvbnRhaW5lclBvaW50KGxhdGxuZyksXHJcblxyXG5cdFx0ICAgIGNlbnRlck9mZnNldCA9IGNvbnRhaW5lclBvaW50LnN1YnRyYWN0KHZpZXdIYWxmKS5tdWx0aXBseUJ5KDEgLSAxIC8gc2NhbGUpLFxyXG5cdFx0ICAgIG5ld0NlbnRlciA9IHRoaXMuY29udGFpbmVyUG9pbnRUb0xhdExuZyh2aWV3SGFsZi5hZGQoY2VudGVyT2Zmc2V0KSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMuc2V0VmlldyhuZXdDZW50ZXIsIHpvb20sIHt6b29tOiBvcHRpb25zfSk7XHJcblx0fSxcclxuXHJcblx0X2dldEJvdW5kc0NlbnRlclpvb206IGZ1bmN0aW9uIChib3VuZHMsIG9wdGlvbnMpIHtcclxuXHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHRcdGJvdW5kcyA9IGJvdW5kcy5nZXRCb3VuZHMgPyBib3VuZHMuZ2V0Qm91bmRzKCkgOiBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHZhciBwYWRkaW5nVEwgPSBMLnBvaW50KG9wdGlvbnMucGFkZGluZ1RvcExlZnQgfHwgb3B0aW9ucy5wYWRkaW5nIHx8IFswLCAwXSksXHJcblx0XHQgICAgcGFkZGluZ0JSID0gTC5wb2ludChvcHRpb25zLnBhZGRpbmdCb3R0b21SaWdodCB8fCBvcHRpb25zLnBhZGRpbmcgfHwgWzAsIDBdKSxcclxuXHJcblx0XHQgICAgem9vbSA9IHRoaXMuZ2V0Qm91bmRzWm9vbShib3VuZHMsIGZhbHNlLCBwYWRkaW5nVEwuYWRkKHBhZGRpbmdCUikpO1xyXG5cclxuXHRcdHpvb20gPSAodHlwZW9mIG9wdGlvbnMubWF4Wm9vbSA9PT0gJ251bWJlcicpID8gTWF0aC5taW4ob3B0aW9ucy5tYXhab29tLCB6b29tKSA6IHpvb207XHJcblxyXG5cdFx0dmFyIHBhZGRpbmdPZmZzZXQgPSBwYWRkaW5nQlIuc3VidHJhY3QocGFkZGluZ1RMKS5kaXZpZGVCeSgyKSxcclxuXHJcblx0XHQgICAgc3dQb2ludCA9IHRoaXMucHJvamVjdChib3VuZHMuZ2V0U291dGhXZXN0KCksIHpvb20pLFxyXG5cdFx0ICAgIG5lUG9pbnQgPSB0aGlzLnByb2plY3QoYm91bmRzLmdldE5vcnRoRWFzdCgpLCB6b29tKSxcclxuXHRcdCAgICBjZW50ZXIgPSB0aGlzLnVucHJvamVjdChzd1BvaW50LmFkZChuZVBvaW50KS5kaXZpZGVCeSgyKS5hZGQocGFkZGluZ09mZnNldCksIHpvb20pO1xyXG5cclxuXHRcdHJldHVybiB7XHJcblx0XHRcdGNlbnRlcjogY2VudGVyLFxyXG5cdFx0XHR6b29tOiB6b29tXHJcblx0XHR9O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZml0Qm91bmRzKGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogZml0Qm91bmRzIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyBhIG1hcCB2aWV3IHRoYXQgY29udGFpbnMgdGhlIGdpdmVuIGdlb2dyYXBoaWNhbCBib3VuZHMgd2l0aCB0aGVcclxuXHQvLyBtYXhpbXVtIHpvb20gbGV2ZWwgcG9zc2libGUuXHJcblx0Zml0Qm91bmRzOiBmdW5jdGlvbiAoYm91bmRzLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0Ym91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHRpZiAoIWJvdW5kcy5pc1ZhbGlkKCkpIHtcclxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdCb3VuZHMgYXJlIG5vdCB2YWxpZC4nKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgdGFyZ2V0ID0gdGhpcy5fZ2V0Qm91bmRzQ2VudGVyWm9vbShib3VuZHMsIG9wdGlvbnMpO1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Vmlldyh0YXJnZXQuY2VudGVyLCB0YXJnZXQuem9vbSwgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmaXRXb3JsZChvcHRpb25zPzogZml0Qm91bmRzIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyBhIG1hcCB2aWV3IHRoYXQgbW9zdGx5IGNvbnRhaW5zIHRoZSB3aG9sZSB3b3JsZCB3aXRoIHRoZSBtYXhpbXVtXHJcblx0Ly8gem9vbSBsZXZlbCBwb3NzaWJsZS5cclxuXHRmaXRXb3JsZDogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRcdHJldHVybiB0aGlzLmZpdEJvdW5kcyhbWy05MCwgLTE4MF0sIFs5MCwgMTgwXV0sIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcGFuVG8obGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBQYW4gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBQYW5zIHRoZSBtYXAgdG8gYSBnaXZlbiBjZW50ZXIuXHJcblx0cGFuVG86IGZ1bmN0aW9uIChjZW50ZXIsIG9wdGlvbnMpIHsgLy8gKExhdExuZylcclxuXHRcdHJldHVybiB0aGlzLnNldFZpZXcoY2VudGVyLCB0aGlzLl96b29tLCB7cGFuOiBvcHRpb25zfSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwYW5CeShvZmZzZXQ6IFBvaW50KTogdGhpc1xyXG5cdC8vIFBhbnMgdGhlIG1hcCBieSBhIGdpdmVuIG51bWJlciBvZiBwaXhlbHMgKGFuaW1hdGVkKS5cclxuXHRwYW5CeTogZnVuY3Rpb24gKG9mZnNldCwgb3B0aW9ucykge1xyXG5cdFx0b2Zmc2V0ID0gTC5wb2ludChvZmZzZXQpLnJvdW5kKCk7XHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHRpZiAoIW9mZnNldC54ICYmICFvZmZzZXQueSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0XHR9XHJcblx0XHQvLyBJZiB3ZSBwYW4gdG9vIGZhciwgQ2hyb21lIGdldHMgaXNzdWVzIHdpdGggdGlsZXNcclxuXHRcdC8vIGFuZCBtYWtlcyB0aGVtIGRpc2FwcGVhciBvciBhcHBlYXIgaW4gdGhlIHdyb25nIHBsYWNlIChzbGlnaHRseSBvZmZzZXQpICMyNjAyXHJcblx0XHRpZiAob3B0aW9ucy5hbmltYXRlICE9PSB0cnVlICYmICF0aGlzLmdldFNpemUoKS5jb250YWlucyhvZmZzZXQpKSB7XHJcblx0XHRcdHRoaXMuX3Jlc2V0Vmlldyh0aGlzLnVucHJvamVjdCh0aGlzLnByb2plY3QodGhpcy5nZXRDZW50ZXIoKSkuYWRkKG9mZnNldCkpLCB0aGlzLmdldFpvb20oKSk7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghdGhpcy5fcGFuQW5pbSkge1xyXG5cdFx0XHR0aGlzLl9wYW5BbmltID0gbmV3IEwuUG9zQW5pbWF0aW9uKCk7XHJcblxyXG5cdFx0XHR0aGlzLl9wYW5BbmltLm9uKHtcclxuXHRcdFx0XHQnc3RlcCc6IHRoaXMuX29uUGFuVHJhbnNpdGlvblN0ZXAsXHJcblx0XHRcdFx0J2VuZCc6IHRoaXMuX29uUGFuVHJhbnNpdGlvbkVuZFxyXG5cdFx0XHR9LCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBkb24ndCBmaXJlIG1vdmVzdGFydCBpZiBhbmltYXRpbmcgaW5lcnRpYVxyXG5cdFx0aWYgKCFvcHRpb25zLm5vTW92ZVN0YXJ0KSB7XHJcblx0XHRcdHRoaXMuZmlyZSgnbW92ZXN0YXJ0Jyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gYW5pbWF0ZSBwYW4gdW5sZXNzIGFuaW1hdGU6IGZhbHNlIHNwZWNpZmllZFxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gZmFsc2UpIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX21hcFBhbmUsICdsZWFmbGV0LXBhbi1hbmltJyk7XHJcblxyXG5cdFx0XHR2YXIgbmV3UG9zID0gdGhpcy5fZ2V0TWFwUGFuZVBvcygpLnN1YnRyYWN0KG9mZnNldCkucm91bmQoKTtcclxuXHRcdFx0dGhpcy5fcGFuQW5pbS5ydW4odGhpcy5fbWFwUGFuZSwgbmV3UG9zLCBvcHRpb25zLmR1cmF0aW9uIHx8IDAuMjUsIG9wdGlvbnMuZWFzZUxpbmVhcml0eSk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLl9yYXdQYW5CeShvZmZzZXQpO1xyXG5cdFx0XHR0aGlzLmZpcmUoJ21vdmUnKS5maXJlKCdtb3ZlZW5kJyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmbHlUbyhsYXRsbmc6IExhdExuZywgem9vbT86IE51bWJlciwgb3B0aW9ucz86IFpvb20vcGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdmlldyBvZiB0aGUgbWFwIChnZW9ncmFwaGljYWwgY2VudGVyIGFuZCB6b29tKSBwZXJmb3JtaW5nIGEgc21vb3RoXHJcblx0Ly8gcGFuLXpvb20gYW5pbWF0aW9uLlxyXG5cdGZseVRvOiBmdW5jdGlvbiAodGFyZ2V0Q2VudGVyLCB0YXJnZXRab29tLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcblx0XHRpZiAob3B0aW9ucy5hbmltYXRlID09PSBmYWxzZSB8fCAhTC5Ccm93c2VyLmFueTNkKSB7XHJcblx0XHRcdHJldHVybiB0aGlzLnNldFZpZXcodGFyZ2V0Q2VudGVyLCB0YXJnZXRab29tLCBvcHRpb25zKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9zdG9wKCk7XHJcblxyXG5cdFx0dmFyIGZyb20gPSB0aGlzLnByb2plY3QodGhpcy5nZXRDZW50ZXIoKSksXHJcblx0XHQgICAgdG8gPSB0aGlzLnByb2plY3QodGFyZ2V0Q2VudGVyKSxcclxuXHRcdCAgICBzaXplID0gdGhpcy5nZXRTaXplKCksXHJcblx0XHQgICAgc3RhcnRab29tID0gdGhpcy5fem9vbTtcclxuXHJcblx0XHR0YXJnZXRDZW50ZXIgPSBMLmxhdExuZyh0YXJnZXRDZW50ZXIpO1xyXG5cdFx0dGFyZ2V0Wm9vbSA9IHRhcmdldFpvb20gPT09IHVuZGVmaW5lZCA/IHN0YXJ0Wm9vbSA6IHRhcmdldFpvb207XHJcblxyXG5cdFx0dmFyIHcwID0gTWF0aC5tYXgoc2l6ZS54LCBzaXplLnkpLFxyXG5cdFx0ICAgIHcxID0gdzAgKiB0aGlzLmdldFpvb21TY2FsZShzdGFydFpvb20sIHRhcmdldFpvb20pLFxyXG5cdFx0ICAgIHUxID0gKHRvLmRpc3RhbmNlVG8oZnJvbSkpIHx8IDEsXHJcblx0XHQgICAgcmhvID0gMS40MixcclxuXHRcdCAgICByaG8yID0gcmhvICogcmhvO1xyXG5cclxuXHRcdGZ1bmN0aW9uIHIoaSkge1xyXG5cdFx0XHR2YXIgczEgPSBpID8gLTEgOiAxLFxyXG5cdFx0XHQgICAgczIgPSBpID8gdzEgOiB3MCxcclxuXHRcdFx0ICAgIHQxID0gdzEgKiB3MSAtIHcwICogdzAgKyBzMSAqIHJobzIgKiByaG8yICogdTEgKiB1MSxcclxuXHRcdFx0ICAgIGIxID0gMiAqIHMyICogcmhvMiAqIHUxLFxyXG5cdFx0XHQgICAgYiA9IHQxIC8gYjEsXHJcblx0XHRcdCAgICBzcSA9IE1hdGguc3FydChiICogYiArIDEpIC0gYjtcclxuXHJcblx0XHRcdCAgICAvLyB3b3JrYXJvdW5kIGZvciBmbG9hdGluZyBwb2ludCBwcmVjaXNpb24gYnVnIHdoZW4gc3EgPSAwLCBsb2cgPSAtSW5maW5pdGUsXHJcblx0XHRcdCAgICAvLyB0aHVzIHRyaWdnZXJpbmcgYW4gaW5maW5pdGUgbG9vcCBpbiBmbHlUb1xyXG5cdFx0XHQgICAgdmFyIGxvZyA9IHNxIDwgMC4wMDAwMDAwMDEgPyAtMTggOiBNYXRoLmxvZyhzcSk7XHJcblxyXG5cdFx0XHRyZXR1cm4gbG9nO1xyXG5cdFx0fVxyXG5cclxuXHRcdGZ1bmN0aW9uIHNpbmgobikgeyByZXR1cm4gKE1hdGguZXhwKG4pIC0gTWF0aC5leHAoLW4pKSAvIDI7IH1cclxuXHRcdGZ1bmN0aW9uIGNvc2gobikgeyByZXR1cm4gKE1hdGguZXhwKG4pICsgTWF0aC5leHAoLW4pKSAvIDI7IH1cclxuXHRcdGZ1bmN0aW9uIHRhbmgobikgeyByZXR1cm4gc2luaChuKSAvIGNvc2gobik7IH1cclxuXHJcblx0XHR2YXIgcjAgPSByKDApO1xyXG5cclxuXHRcdGZ1bmN0aW9uIHcocykgeyByZXR1cm4gdzAgKiAoY29zaChyMCkgLyBjb3NoKHIwICsgcmhvICogcykpOyB9XHJcblx0XHRmdW5jdGlvbiB1KHMpIHsgcmV0dXJuIHcwICogKGNvc2gocjApICogdGFuaChyMCArIHJobyAqIHMpIC0gc2luaChyMCkpIC8gcmhvMjsgfVxyXG5cclxuXHRcdGZ1bmN0aW9uIGVhc2VPdXQodCkgeyByZXR1cm4gMSAtIE1hdGgucG93KDEgLSB0LCAxLjUpOyB9XHJcblxyXG5cdFx0dmFyIHN0YXJ0ID0gRGF0ZS5ub3coKSxcclxuXHRcdCAgICBTID0gKHIoMSkgLSByMCkgLyByaG8sXHJcblx0XHQgICAgZHVyYXRpb24gPSBvcHRpb25zLmR1cmF0aW9uID8gMTAwMCAqIG9wdGlvbnMuZHVyYXRpb24gOiAxMDAwICogUyAqIDAuODtcclxuXHJcblx0XHRmdW5jdGlvbiBmcmFtZSgpIHtcclxuXHRcdFx0dmFyIHQgPSAoRGF0ZS5ub3coKSAtIHN0YXJ0KSAvIGR1cmF0aW9uLFxyXG5cdFx0XHQgICAgcyA9IGVhc2VPdXQodCkgKiBTO1xyXG5cclxuXHRcdFx0aWYgKHQgPD0gMSkge1xyXG5cdFx0XHRcdHRoaXMuX2ZseVRvRnJhbWUgPSBMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmcmFtZSwgdGhpcyk7XHJcblxyXG5cdFx0XHRcdHRoaXMuX21vdmUoXHJcblx0XHRcdFx0XHR0aGlzLnVucHJvamVjdChmcm9tLmFkZCh0by5zdWJ0cmFjdChmcm9tKS5tdWx0aXBseUJ5KHUocykgLyB1MSkpLCBzdGFydFpvb20pLFxyXG5cdFx0XHRcdFx0dGhpcy5nZXRTY2FsZVpvb20odzAgLyB3KHMpLCBzdGFydFpvb20pLFxyXG5cdFx0XHRcdFx0e2ZseVRvOiB0cnVlfSk7XHJcblxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHRoaXNcclxuXHRcdFx0XHRcdC5fbW92ZSh0YXJnZXRDZW50ZXIsIHRhcmdldFpvb20pXHJcblx0XHRcdFx0XHQuX21vdmVFbmQodHJ1ZSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9tb3ZlU3RhcnQodHJ1ZSk7XHJcblxyXG5cdFx0ZnJhbWUuY2FsbCh0aGlzKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZmx5VG9Cb3VuZHMoYm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM/OiBmaXRCb3VuZHMgb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSB2aWV3IG9mIHRoZSBtYXAgd2l0aCBhIHNtb290aCBhbmltYXRpb24gbGlrZSBbYGZseVRvYF0oI21hcC1mbHl0byksXHJcblx0Ly8gYnV0IHRha2VzIGEgYm91bmRzIHBhcmFtZXRlciBsaWtlIFtgZml0Qm91bmRzYF0oI21hcC1maXRib3VuZHMpLlxyXG5cdGZseVRvQm91bmRzOiBmdW5jdGlvbiAoYm91bmRzLCBvcHRpb25zKSB7XHJcblx0XHR2YXIgdGFyZ2V0ID0gdGhpcy5fZ2V0Qm91bmRzQ2VudGVyWm9vbShib3VuZHMsIG9wdGlvbnMpO1xyXG5cdFx0cmV0dXJuIHRoaXMuZmx5VG8odGFyZ2V0LmNlbnRlciwgdGFyZ2V0Lnpvb20sIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TWF4Qm91bmRzKGJvdW5kczogQm91bmRzKTogdGhpc1xyXG5cdC8vIFJlc3RyaWN0cyB0aGUgbWFwIHZpZXcgdG8gdGhlIGdpdmVuIGJvdW5kcyAoc2VlIHRoZSBbbWF4Qm91bmRzXSgjbWFwLW1heGJvdW5kcykgb3B0aW9uKS5cclxuXHRzZXRNYXhCb3VuZHM6IGZ1bmN0aW9uIChib3VuZHMpIHtcclxuXHRcdGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0aWYgKCFib3VuZHMuaXNWYWxpZCgpKSB7XHJcblx0XHRcdHRoaXMub3B0aW9ucy5tYXhCb3VuZHMgPSBudWxsO1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5vZmYoJ21vdmVlbmQnLCB0aGlzLl9wYW5JbnNpZGVNYXhCb3VuZHMpO1xyXG5cdFx0fSBlbHNlIGlmICh0aGlzLm9wdGlvbnMubWF4Qm91bmRzKSB7XHJcblx0XHRcdHRoaXMub2ZmKCdtb3ZlZW5kJywgdGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLm9wdGlvbnMubWF4Qm91bmRzID0gYm91bmRzO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sb2FkZWQpIHtcclxuXHRcdFx0dGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMub24oJ21vdmVlbmQnLCB0aGlzLl9wYW5JbnNpZGVNYXhCb3VuZHMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TWluWm9vbSh6b29tOiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgbG93ZXIgbGltaXQgZm9yIHRoZSBhdmFpbGFibGUgem9vbSBsZXZlbHMgKHNlZSB0aGUgW21pblpvb21dKCNtYXAtbWluem9vbSkgb3B0aW9uKS5cclxuXHRzZXRNaW5ab29tOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0dGhpcy5vcHRpb25zLm1pblpvb20gPSB6b29tO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sb2FkZWQgJiYgdGhpcy5nZXRab29tKCkgPCB0aGlzLm9wdGlvbnMubWluWm9vbSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHpvb20pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TWF4Wm9vbSh6b29tOiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdXBwZXIgbGltaXQgZm9yIHRoZSBhdmFpbGFibGUgem9vbSBsZXZlbHMgKHNlZSB0aGUgW21heFpvb21dKCNtYXAtbWF4em9vbSkgb3B0aW9uKS5cclxuXHRzZXRNYXhab29tOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0dGhpcy5vcHRpb25zLm1heFpvb20gPSB6b29tO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sb2FkZWQgJiYgKHRoaXMuZ2V0Wm9vbSgpID4gdGhpcy5vcHRpb25zLm1heFpvb20pKSB7XHJcblx0XHRcdHJldHVybiB0aGlzLnNldFpvb20oem9vbSk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwYW5JbnNpZGVCb3VuZHMoYm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM/OiBQYW4gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBQYW5zIHRoZSBtYXAgdG8gdGhlIGNsb3Nlc3QgdmlldyB0aGF0IHdvdWxkIGxpZSBpbnNpZGUgdGhlIGdpdmVuIGJvdW5kcyAoaWYgaXQncyBub3QgYWxyZWFkeSksIGNvbnRyb2xsaW5nIHRoZSBhbmltYXRpb24gdXNpbmcgdGhlIG9wdGlvbnMgc3BlY2lmaWMsIGlmIGFueS5cclxuXHRwYW5JbnNpZGVCb3VuZHM6IGZ1bmN0aW9uIChib3VuZHMsIG9wdGlvbnMpIHtcclxuXHRcdHRoaXMuX2VuZm9yY2luZ0JvdW5kcyA9IHRydWU7XHJcblx0XHR2YXIgY2VudGVyID0gdGhpcy5nZXRDZW50ZXIoKSxcclxuXHRcdCAgICBuZXdDZW50ZXIgPSB0aGlzLl9saW1pdENlbnRlcihjZW50ZXIsIHRoaXMuX3pvb20sIEwubGF0TG5nQm91bmRzKGJvdW5kcykpO1xyXG5cclxuXHRcdGlmICghY2VudGVyLmVxdWFscyhuZXdDZW50ZXIpKSB7XHJcblx0XHRcdHRoaXMucGFuVG8obmV3Q2VudGVyLCBvcHRpb25zKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9lbmZvcmNpbmdCb3VuZHMgPSBmYWxzZTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaW52YWxpZGF0ZVNpemUob3B0aW9uczogWm9vbS9QYW4gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBDaGVja3MgaWYgdGhlIG1hcCBjb250YWluZXIgc2l6ZSBjaGFuZ2VkIGFuZCB1cGRhdGVzIHRoZSBtYXAgaWYgc28g4oCUXHJcblx0Ly8gY2FsbCBpdCBhZnRlciB5b3UndmUgY2hhbmdlZCB0aGUgbWFwIHNpemUgZHluYW1pY2FsbHksIGFsc28gYW5pbWF0aW5nXHJcblx0Ly8gcGFuIGJ5IGRlZmF1bHQuIElmIGBvcHRpb25zLnBhbmAgaXMgYGZhbHNlYCwgcGFubmluZyB3aWxsIG5vdCBvY2N1ci5cclxuXHQvLyBJZiBgb3B0aW9ucy5kZWJvdW5jZU1vdmVlbmRgIGlzIGB0cnVlYCwgaXQgd2lsbCBkZWxheSBgbW92ZWVuZGAgZXZlbnQgc29cclxuXHQvLyB0aGF0IGl0IGRvZXNuJ3QgaGFwcGVuIG9mdGVuIGV2ZW4gaWYgdGhlIG1ldGhvZCBpcyBjYWxsZWQgbWFueVxyXG5cdC8vIHRpbWVzIGluIGEgcm93LlxyXG5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIGludmFsaWRhdGVTaXplKGFuaW1hdGU6IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gQ2hlY2tzIGlmIHRoZSBtYXAgY29udGFpbmVyIHNpemUgY2hhbmdlZCBhbmQgdXBkYXRlcyB0aGUgbWFwIGlmIHNvIOKAlFxyXG5cdC8vIGNhbGwgaXQgYWZ0ZXIgeW91J3ZlIGNoYW5nZWQgdGhlIG1hcCBzaXplIGR5bmFtaWNhbGx5LCBhbHNvIGFuaW1hdGluZ1xyXG5cdC8vIHBhbiBieSBkZWZhdWx0LlxyXG5cdGludmFsaWRhdGVTaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdFx0aWYgKCF0aGlzLl9sb2FkZWQpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRvcHRpb25zID0gTC5leHRlbmQoe1xyXG5cdFx0XHRhbmltYXRlOiBmYWxzZSxcclxuXHRcdFx0cGFuOiB0cnVlXHJcblx0XHR9LCBvcHRpb25zID09PSB0cnVlID8ge2FuaW1hdGU6IHRydWV9IDogb3B0aW9ucyk7XHJcblxyXG5cdFx0dmFyIG9sZFNpemUgPSB0aGlzLmdldFNpemUoKTtcclxuXHRcdHRoaXMuX3NpemVDaGFuZ2VkID0gdHJ1ZTtcclxuXHRcdHRoaXMuX2xhc3RDZW50ZXIgPSBudWxsO1xyXG5cclxuXHRcdHZhciBuZXdTaXplID0gdGhpcy5nZXRTaXplKCksXHJcblx0XHQgICAgb2xkQ2VudGVyID0gb2xkU2l6ZS5kaXZpZGVCeSgyKS5yb3VuZCgpLFxyXG5cdFx0ICAgIG5ld0NlbnRlciA9IG5ld1NpemUuZGl2aWRlQnkoMikucm91bmQoKSxcclxuXHRcdCAgICBvZmZzZXQgPSBvbGRDZW50ZXIuc3VidHJhY3QobmV3Q2VudGVyKTtcclxuXHJcblx0XHRpZiAoIW9mZnNldC54ICYmICFvZmZzZXQueSkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdGlmIChvcHRpb25zLmFuaW1hdGUgJiYgb3B0aW9ucy5wYW4pIHtcclxuXHRcdFx0dGhpcy5wYW5CeShvZmZzZXQpO1xyXG5cclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGlmIChvcHRpb25zLnBhbikge1xyXG5cdFx0XHRcdHRoaXMuX3Jhd1BhbkJ5KG9mZnNldCk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHRoaXMuZmlyZSgnbW92ZScpO1xyXG5cclxuXHRcdFx0aWYgKG9wdGlvbnMuZGVib3VuY2VNb3ZlZW5kKSB7XHJcblx0XHRcdFx0Y2xlYXJUaW1lb3V0KHRoaXMuX3NpemVUaW1lcik7XHJcblx0XHRcdFx0dGhpcy5fc2l6ZVRpbWVyID0gc2V0VGltZW91dChMLmJpbmQodGhpcy5maXJlLCB0aGlzLCAnbW92ZWVuZCcpLCAyMDApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHRoaXMuZmlyZSgnbW92ZWVuZCcpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQHNlY3Rpb24gTWFwIHN0YXRlIGNoYW5nZSBldmVudHNcclxuXHRcdC8vIEBldmVudCByZXNpemU6IFJlc2l6ZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaXMgcmVzaXplZC5cclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ3Jlc2l6ZScsIHtcclxuXHRcdFx0b2xkU2l6ZTogb2xkU2l6ZSxcclxuXHRcdFx0bmV3U2l6ZTogbmV3U2l6ZVxyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgbW9kaWZ5aW5nIG1hcCBzdGF0ZVxyXG5cdC8vIEBtZXRob2Qgc3RvcCgpOiB0aGlzXHJcblx0Ly8gU3RvcHMgdGhlIGN1cnJlbnRseSBydW5uaW5nIGBwYW5Ub2Agb3IgYGZseVRvYCBhbmltYXRpb24sIGlmIGFueS5cclxuXHRzdG9wOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnNldFpvb20odGhpcy5fbGltaXRab29tKHRoaXMuX3pvb20pKTtcclxuXHRcdGlmICghdGhpcy5vcHRpb25zLnpvb21TbmFwKSB7XHJcblx0XHRcdHRoaXMuZmlyZSgndmlld3Jlc2V0Jyk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5fc3RvcCgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIEdlb2xvY2F0aW9uIG1ldGhvZHNcclxuXHQvLyBAbWV0aG9kIGxvY2F0ZShvcHRpb25zPzogTG9jYXRlIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gVHJpZXMgdG8gbG9jYXRlIHRoZSB1c2VyIHVzaW5nIHRoZSBHZW9sb2NhdGlvbiBBUEksIGZpcmluZyBhIFtgbG9jYXRpb25mb3VuZGBdKCNtYXAtbG9jYXRpb25mb3VuZClcclxuXHQvLyBldmVudCB3aXRoIGxvY2F0aW9uIGRhdGEgb24gc3VjY2VzcyBvciBhIFtgbG9jYXRpb25lcnJvcmBdKCNtYXAtbG9jYXRpb25lcnJvcikgZXZlbnQgb24gZmFpbHVyZSxcclxuXHQvLyBhbmQgb3B0aW9uYWxseSBzZXRzIHRoZSBtYXAgdmlldyB0byB0aGUgdXNlcidzIGxvY2F0aW9uIHdpdGggcmVzcGVjdCB0b1xyXG5cdC8vIGRldGVjdGlvbiBhY2N1cmFjeSAob3IgdG8gdGhlIHdvcmxkIHZpZXcgaWYgZ2VvbG9jYXRpb24gZmFpbGVkKS5cclxuXHQvLyBOb3RlIHRoYXQsIGlmIHlvdXIgcGFnZSBkb2Vzbid0IHVzZSBIVFRQUywgdGhpcyBtZXRob2Qgd2lsbCBmYWlsIGluXHJcblx0Ly8gbW9kZXJuIGJyb3dzZXJzIChbQ2hyb21lIDUwIGFuZCBuZXdlcl0oaHR0cHM6Ly9zaXRlcy5nb29nbGUuY29tL2EvY2hyb21pdW0ub3JnL2Rldi9Ib21lL2Nocm9taXVtLXNlY3VyaXR5L2RlcHJlY2F0aW5nLXBvd2VyZnVsLWZlYXR1cmVzLW9uLWluc2VjdXJlLW9yaWdpbnMpKVxyXG5cdC8vIFNlZSBgTG9jYXRlIG9wdGlvbnNgIGZvciBtb3JlIGRldGFpbHMuXHJcblx0bG9jYXRlOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cclxuXHRcdG9wdGlvbnMgPSB0aGlzLl9sb2NhdGVPcHRpb25zID0gTC5leHRlbmQoe1xyXG5cdFx0XHR0aW1lb3V0OiAxMDAwMCxcclxuXHRcdFx0d2F0Y2g6IGZhbHNlXHJcblx0XHRcdC8vIHNldFZpZXc6IGZhbHNlXHJcblx0XHRcdC8vIG1heFpvb206IDxOdW1iZXI+XHJcblx0XHRcdC8vIG1heGltdW1BZ2U6IDBcclxuXHRcdFx0Ly8gZW5hYmxlSGlnaEFjY3VyYWN5OiBmYWxzZVxyXG5cdFx0fSwgb3B0aW9ucyk7XHJcblxyXG5cdFx0aWYgKCEoJ2dlb2xvY2F0aW9uJyBpbiBuYXZpZ2F0b3IpKSB7XHJcblx0XHRcdHRoaXMuX2hhbmRsZUdlb2xvY2F0aW9uRXJyb3Ioe1xyXG5cdFx0XHRcdGNvZGU6IDAsXHJcblx0XHRcdFx0bWVzc2FnZTogJ0dlb2xvY2F0aW9uIG5vdCBzdXBwb3J0ZWQuJ1xyXG5cdFx0XHR9KTtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIG9uUmVzcG9uc2UgPSBMLmJpbmQodGhpcy5faGFuZGxlR2VvbG9jYXRpb25SZXNwb25zZSwgdGhpcyksXHJcblx0XHQgICAgb25FcnJvciA9IEwuYmluZCh0aGlzLl9oYW5kbGVHZW9sb2NhdGlvbkVycm9yLCB0aGlzKTtcclxuXHJcblx0XHRpZiAob3B0aW9ucy53YXRjaCkge1xyXG5cdFx0XHR0aGlzLl9sb2NhdGlvbldhdGNoSWQgPVxyXG5cdFx0XHQgICAgICAgIG5hdmlnYXRvci5nZW9sb2NhdGlvbi53YXRjaFBvc2l0aW9uKG9uUmVzcG9uc2UsIG9uRXJyb3IsIG9wdGlvbnMpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0bmF2aWdhdG9yLmdlb2xvY2F0aW9uLmdldEN1cnJlbnRQb3NpdGlvbihvblJlc3BvbnNlLCBvbkVycm9yLCBvcHRpb25zKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc3RvcExvY2F0ZSgpOiB0aGlzXHJcblx0Ly8gU3RvcHMgd2F0Y2hpbmcgbG9jYXRpb24gcHJldmlvdXNseSBpbml0aWF0ZWQgYnkgYG1hcC5sb2NhdGUoe3dhdGNoOiB0cnVlfSlgXHJcblx0Ly8gYW5kIGFib3J0cyByZXNldHRpbmcgdGhlIG1hcCB2aWV3IGlmIG1hcC5sb2NhdGUgd2FzIGNhbGxlZCB3aXRoXHJcblx0Ly8gYHtzZXRWaWV3OiB0cnVlfWAuXHJcblx0c3RvcExvY2F0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKG5hdmlnYXRvci5nZW9sb2NhdGlvbiAmJiBuYXZpZ2F0b3IuZ2VvbG9jYXRpb24uY2xlYXJXYXRjaCkge1xyXG5cdFx0XHRuYXZpZ2F0b3IuZ2VvbG9jYXRpb24uY2xlYXJXYXRjaCh0aGlzLl9sb2NhdGlvbldhdGNoSWQpO1xyXG5cdFx0fVxyXG5cdFx0aWYgKHRoaXMuX2xvY2F0ZU9wdGlvbnMpIHtcclxuXHRcdFx0dGhpcy5fbG9jYXRlT3B0aW9ucy5zZXRWaWV3ID0gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfaGFuZGxlR2VvbG9jYXRpb25FcnJvcjogZnVuY3Rpb24gKGVycm9yKSB7XHJcblx0XHR2YXIgYyA9IGVycm9yLmNvZGUsXHJcblx0XHQgICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2UgfHxcclxuXHRcdCAgICAgICAgICAgIChjID09PSAxID8gJ3Blcm1pc3Npb24gZGVuaWVkJyA6XHJcblx0XHQgICAgICAgICAgICAoYyA9PT0gMiA/ICdwb3NpdGlvbiB1bmF2YWlsYWJsZScgOiAndGltZW91dCcpKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9jYXRlT3B0aW9ucy5zZXRWaWV3ICYmICF0aGlzLl9sb2FkZWQpIHtcclxuXHRcdFx0dGhpcy5maXRXb3JsZCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEBzZWN0aW9uIExvY2F0aW9uIGV2ZW50c1xyXG5cdFx0Ly8gQGV2ZW50IGxvY2F0aW9uZXJyb3I6IEVycm9yRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gZ2VvbG9jYXRpb24gKHVzaW5nIHRoZSBbYGxvY2F0ZWBdKCNtYXAtbG9jYXRlKSBtZXRob2QpIGZhaWxlZC5cclxuXHRcdHRoaXMuZmlyZSgnbG9jYXRpb25lcnJvcicsIHtcclxuXHRcdFx0Y29kZTogYyxcclxuXHRcdFx0bWVzc2FnZTogJ0dlb2xvY2F0aW9uIGVycm9yOiAnICsgbWVzc2FnZSArICcuJ1xyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0X2hhbmRsZUdlb2xvY2F0aW9uUmVzcG9uc2U6IGZ1bmN0aW9uIChwb3MpIHtcclxuXHRcdHZhciBsYXQgPSBwb3MuY29vcmRzLmxhdGl0dWRlLFxyXG5cdFx0ICAgIGxuZyA9IHBvcy5jb29yZHMubG9uZ2l0dWRlLFxyXG5cdFx0ICAgIGxhdGxuZyA9IG5ldyBMLkxhdExuZyhsYXQsIGxuZyksXHJcblx0XHQgICAgYm91bmRzID0gbGF0bG5nLnRvQm91bmRzKHBvcy5jb29yZHMuYWNjdXJhY3kpLFxyXG5cdFx0ICAgIG9wdGlvbnMgPSB0aGlzLl9sb2NhdGVPcHRpb25zO1xyXG5cclxuXHRcdGlmIChvcHRpb25zLnNldFZpZXcpIHtcclxuXHRcdFx0dmFyIHpvb20gPSB0aGlzLmdldEJvdW5kc1pvb20oYm91bmRzKTtcclxuXHRcdFx0dGhpcy5zZXRWaWV3KGxhdGxuZywgb3B0aW9ucy5tYXhab29tID8gTWF0aC5taW4oem9vbSwgb3B0aW9ucy5tYXhab29tKSA6IHpvb20pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBkYXRhID0ge1xyXG5cdFx0XHRsYXRsbmc6IGxhdGxuZyxcclxuXHRcdFx0Ym91bmRzOiBib3VuZHMsXHJcblx0XHRcdHRpbWVzdGFtcDogcG9zLnRpbWVzdGFtcFxyXG5cdFx0fTtcclxuXHJcblx0XHRmb3IgKHZhciBpIGluIHBvcy5jb29yZHMpIHtcclxuXHRcdFx0aWYgKHR5cGVvZiBwb3MuY29vcmRzW2ldID09PSAnbnVtYmVyJykge1xyXG5cdFx0XHRcdGRhdGFbaV0gPSBwb3MuY29vcmRzW2ldO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxvY2F0aW9uZm91bmQ6IExvY2F0aW9uRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gZ2VvbG9jYXRpb24gKHVzaW5nIHRoZSBbYGxvY2F0ZWBdKCNtYXAtbG9jYXRlKSBtZXRob2QpXHJcblx0XHQvLyB3ZW50IHN1Y2Nlc3NmdWxseS5cclxuXHRcdHRoaXMuZmlyZSgnbG9jYXRpb25mb3VuZCcsIGRhdGEpO1xyXG5cdH0sXHJcblxyXG5cdC8vIFRPRE8gaGFuZGxlci5hZGRUb1xyXG5cdC8vIFRPRE8gQXBwcm9waWF0ZSBkb2NzIHNlY3Rpb24/XHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cdC8vIEBtZXRob2QgYWRkSGFuZGxlcihuYW1lOiBTdHJpbmcsIEhhbmRsZXJDbGFzczogRnVuY3Rpb24pOiB0aGlzXHJcblx0Ly8gQWRkcyBhIG5ldyBgSGFuZGxlcmAgdG8gdGhlIG1hcCwgZ2l2ZW4gaXRzIG5hbWUgYW5kIGNvbnN0cnVjdG9yIGZ1bmN0aW9uLlxyXG5cdGFkZEhhbmRsZXI6IGZ1bmN0aW9uIChuYW1lLCBIYW5kbGVyQ2xhc3MpIHtcclxuXHRcdGlmICghSGFuZGxlckNsYXNzKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0dmFyIGhhbmRsZXIgPSB0aGlzW25hbWVdID0gbmV3IEhhbmRsZXJDbGFzcyh0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9oYW5kbGVycy5wdXNoKGhhbmRsZXIpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnNbbmFtZV0pIHtcclxuXHRcdFx0aGFuZGxlci5lbmFibGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZSgpOiB0aGlzXHJcblx0Ly8gRGVzdHJveXMgdGhlIG1hcCBhbmQgY2xlYXJzIGFsbCByZWxhdGVkIGV2ZW50IGxpc3RlbmVycy5cclxuXHRyZW1vdmU6IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHR0aGlzLl9pbml0RXZlbnRzKHRydWUpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9jb250YWluZXJJZCAhPT0gdGhpcy5fY29udGFpbmVyLl9sZWFmbGV0X2lkKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignTWFwIGNvbnRhaW5lciBpcyBiZWluZyByZXVzZWQgYnkgYW5vdGhlciBpbnN0YW5jZScpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRyeSB7XHJcblx0XHRcdC8vIHRocm93cyBlcnJvciBpbiBJRTYtOFxyXG5cdFx0XHRkZWxldGUgdGhpcy5fY29udGFpbmVyLl9sZWFmbGV0X2lkO1xyXG5cdFx0XHRkZWxldGUgdGhpcy5fY29udGFpbmVySWQ7XHJcblx0XHR9IGNhdGNoIChlKSB7XHJcblx0XHRcdC8qZXNsaW50LWRpc2FibGUgKi9cclxuXHRcdFx0dGhpcy5fY29udGFpbmVyLl9sZWFmbGV0X2lkID0gdW5kZWZpbmVkO1xyXG5cdFx0XHQvKmVzbGludC1lbmFibGUgKi9cclxuXHRcdFx0dGhpcy5fY29udGFpbmVySWQgPSB1bmRlZmluZWQ7XHJcblx0XHR9XHJcblxyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9tYXBQYW5lKTtcclxuXHJcblx0XHRpZiAodGhpcy5fY2xlYXJDb250cm9sUG9zKSB7XHJcblx0XHRcdHRoaXMuX2NsZWFyQ29udHJvbFBvcygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2NsZWFySGFuZGxlcnMoKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdC8vIEBzZWN0aW9uIE1hcCBzdGF0ZSBjaGFuZ2UgZXZlbnRzXHJcblx0XHRcdC8vIEBldmVudCB1bmxvYWQ6IEV2ZW50XHJcblx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcCBpcyBkZXN0cm95ZWQgd2l0aCBbcmVtb3ZlXSgjbWFwLXJlbW92ZSkgbWV0aG9kLlxyXG5cdFx0XHR0aGlzLmZpcmUoJ3VubG9hZCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMuX2xheWVyc1tpXS5yZW1vdmUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAc2VjdGlvbiBPdGhlciBNZXRob2RzXHJcblx0Ly8gQG1ldGhvZCBjcmVhdGVQYW5lKG5hbWU6IFN0cmluZywgY29udGFpbmVyPzogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudFxyXG5cdC8vIENyZWF0ZXMgYSBuZXcgW21hcCBwYW5lXSgjbWFwLXBhbmUpIHdpdGggdGhlIGdpdmVuIG5hbWUgaWYgaXQgZG9lc24ndCBleGlzdCBhbHJlYWR5LFxyXG5cdC8vIHRoZW4gcmV0dXJucyBpdC4gVGhlIHBhbmUgaXMgY3JlYXRlZCBhcyBhIGNoaWxkcmVuIG9mIGBjb250YWluZXJgLCBvclxyXG5cdC8vIGFzIGEgY2hpbGRyZW4gb2YgdGhlIG1haW4gbWFwIHBhbmUgaWYgbm90IHNldC5cclxuXHRjcmVhdGVQYW5lOiBmdW5jdGlvbiAobmFtZSwgY29udGFpbmVyKSB7XHJcblx0XHR2YXIgY2xhc3NOYW1lID0gJ2xlYWZsZXQtcGFuZScgKyAobmFtZSA/ICcgbGVhZmxldC0nICsgbmFtZS5yZXBsYWNlKCdQYW5lJywgJycpICsgJy1wYW5lJyA6ICcnKSxcclxuXHRcdCAgICBwYW5lID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lLCBjb250YWluZXIgfHwgdGhpcy5fbWFwUGFuZSk7XHJcblxyXG5cdFx0aWYgKG5hbWUpIHtcclxuXHRcdFx0dGhpcy5fcGFuZXNbbmFtZV0gPSBwYW5lO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHBhbmU7XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgR2V0dGluZyBNYXAgU3RhdGVcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRDZW50ZXIoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgZ2VvZ3JhcGhpY2FsIGNlbnRlciBvZiB0aGUgbWFwIHZpZXdcclxuXHRnZXRDZW50ZXI6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX2NoZWNrSWZMb2FkZWQoKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbGFzdENlbnRlciAmJiAhdGhpcy5fbW92ZWQoKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5fbGFzdENlbnRlcjtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLl9nZXRDZW50ZXJMYXllclBvaW50KCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Wm9vbSgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBjdXJyZW50IHpvb20gbGV2ZWwgb2YgdGhlIG1hcCB2aWV3XHJcblx0Z2V0Wm9vbTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3pvb207XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHMoKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgZ2VvZ3JhcGhpY2FsIGJvdW5kcyB2aXNpYmxlIGluIHRoZSBjdXJyZW50IG1hcCB2aWV3XHJcblx0Z2V0Qm91bmRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgYm91bmRzID0gdGhpcy5nZXRQaXhlbEJvdW5kcygpLFxyXG5cdFx0ICAgIHN3ID0gdGhpcy51bnByb2plY3QoYm91bmRzLmdldEJvdHRvbUxlZnQoKSksXHJcblx0XHQgICAgbmUgPSB0aGlzLnVucHJvamVjdChib3VuZHMuZ2V0VG9wUmlnaHQoKSk7XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZ0JvdW5kcyhzdywgbmUpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TWluWm9vbSgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBtaW5pbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcCAoaWYgc2V0IGluIHRoZSBgbWluWm9vbWAgb3B0aW9uIG9mIHRoZSBtYXAgb3Igb2YgYW55IGxheWVycyksIG9yIGAwYCBieSBkZWZhdWx0LlxyXG5cdGdldE1pblpvb206IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMubWluWm9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fbGF5ZXJzTWluWm9vbSB8fCAwIDogdGhpcy5vcHRpb25zLm1pblpvb207XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRNYXhab29tKCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIG1heGltdW0gem9vbSBsZXZlbCBvZiB0aGUgbWFwIChpZiBzZXQgaW4gdGhlIGBtYXhab29tYCBvcHRpb24gb2YgdGhlIG1hcCBvciBvZiBhbnkgbGF5ZXJzKS5cclxuXHRnZXRNYXhab29tOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLm1heFpvb20gPT09IHVuZGVmaW5lZCA/XHJcblx0XHRcdCh0aGlzLl9sYXllcnNNYXhab29tID09PSB1bmRlZmluZWQgPyBJbmZpbml0eSA6IHRoaXMuX2xheWVyc01heFpvb20pIDpcclxuXHRcdFx0dGhpcy5vcHRpb25zLm1heFpvb207XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHNab29tKGJvdW5kczogTGF0TG5nQm91bmRzLCBpbnNpZGU/OiBCb29sZWFuKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbWF4aW11bSB6b29tIGxldmVsIG9uIHdoaWNoIHRoZSBnaXZlbiBib3VuZHMgZml0IHRvIHRoZSBtYXBcclxuXHQvLyB2aWV3IGluIGl0cyBlbnRpcmV0eS4gSWYgYGluc2lkZWAgKG9wdGlvbmFsKSBpcyBzZXQgdG8gYHRydWVgLCB0aGUgbWV0aG9kXHJcblx0Ly8gaW5zdGVhZCByZXR1cm5zIHRoZSBtaW5pbXVtIHpvb20gbGV2ZWwgb24gd2hpY2ggdGhlIG1hcCB2aWV3IGZpdHMgaW50b1xyXG5cdC8vIHRoZSBnaXZlbiBib3VuZHMgaW4gaXRzIGVudGlyZXR5LlxyXG5cdGdldEJvdW5kc1pvb206IGZ1bmN0aW9uIChib3VuZHMsIGluc2lkZSwgcGFkZGluZykgeyAvLyAoTGF0TG5nQm91bmRzWywgQm9vbGVhbiwgUG9pbnRdKSAtPiBOdW1iZXJcclxuXHRcdGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblx0XHRwYWRkaW5nID0gTC5wb2ludChwYWRkaW5nIHx8IFswLCAwXSk7XHJcblxyXG5cdFx0dmFyIHpvb20gPSB0aGlzLmdldFpvb20oKSB8fCAwLFxyXG5cdFx0ICAgIG1pbiA9IHRoaXMuZ2V0TWluWm9vbSgpLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMuZ2V0TWF4Wm9vbSgpLFxyXG5cdFx0ICAgIG53ID0gYm91bmRzLmdldE5vcnRoV2VzdCgpLFxyXG5cdFx0ICAgIHNlID0gYm91bmRzLmdldFNvdXRoRWFzdCgpLFxyXG5cdFx0ICAgIHNpemUgPSB0aGlzLmdldFNpemUoKS5zdWJ0cmFjdChwYWRkaW5nKSxcclxuXHRcdCAgICBib3VuZHNTaXplID0gTC5ib3VuZHModGhpcy5wcm9qZWN0KHNlLCB6b29tKSwgdGhpcy5wcm9qZWN0KG53LCB6b29tKSkuZ2V0U2l6ZSgpLFxyXG5cdFx0ICAgIHNuYXAgPSBMLkJyb3dzZXIuYW55M2QgPyB0aGlzLm9wdGlvbnMuem9vbVNuYXAgOiAxO1xyXG5cclxuXHRcdHZhciBzY2FsZSA9IE1hdGgubWluKHNpemUueCAvIGJvdW5kc1NpemUueCwgc2l6ZS55IC8gYm91bmRzU2l6ZS55KTtcclxuXHRcdHpvb20gPSB0aGlzLmdldFNjYWxlWm9vbShzY2FsZSwgem9vbSk7XHJcblxyXG5cdFx0aWYgKHNuYXApIHtcclxuXHRcdFx0em9vbSA9IE1hdGgucm91bmQoem9vbSAvIChzbmFwIC8gMTAwKSkgKiAoc25hcCAvIDEwMCk7IC8vIGRvbid0IGp1bXAgaWYgd2l0aGluIDElIG9mIGEgc25hcCBsZXZlbFxyXG5cdFx0XHR6b29tID0gaW5zaWRlID8gTWF0aC5jZWlsKHpvb20gLyBzbmFwKSAqIHNuYXAgOiBNYXRoLmZsb29yKHpvb20gLyBzbmFwKSAqIHNuYXA7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIE1hdGgubWF4KG1pbiwgTWF0aC5taW4obWF4LCB6b29tKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTaXplKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCBzaXplIG9mIHRoZSBtYXAgY29udGFpbmVyIChpbiBwaXhlbHMpLlxyXG5cdGdldFNpemU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fc2l6ZSB8fCB0aGlzLl9zaXplQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLl9zaXplID0gbmV3IEwuUG9pbnQoXHJcblx0XHRcdFx0dGhpcy5fY29udGFpbmVyLmNsaWVudFdpZHRoIHx8IDAsXHJcblx0XHRcdFx0dGhpcy5fY29udGFpbmVyLmNsaWVudEhlaWdodCB8fCAwKTtcclxuXHJcblx0XHRcdHRoaXMuX3NpemVDaGFuZ2VkID0gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5fc2l6ZS5jbG9uZSgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UGl4ZWxCb3VuZHMoKTogQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgYm91bmRzIG9mIHRoZSBjdXJyZW50IG1hcCB2aWV3IGluIHByb2plY3RlZCBwaXhlbFxyXG5cdC8vIGNvb3JkaW5hdGVzIChzb21ldGltZXMgdXNlZnVsIGluIGxheWVyIGFuZCBvdmVybGF5IGltcGxlbWVudGF0aW9ucykuXHJcblx0Z2V0UGl4ZWxCb3VuZHM6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcclxuXHRcdHZhciB0b3BMZWZ0UG9pbnQgPSB0aGlzLl9nZXRUb3BMZWZ0UG9pbnQoY2VudGVyLCB6b29tKTtcclxuXHRcdHJldHVybiBuZXcgTC5Cb3VuZHModG9wTGVmdFBvaW50LCB0b3BMZWZ0UG9pbnQuYWRkKHRoaXMuZ2V0U2l6ZSgpKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gVE9ETzogQ2hlY2sgc2VtYW50aWNzIC0gaXNuJ3QgdGhlIHBpeGVsIG9yaWdpbiB0aGUgMCwwIGNvb3JkIHJlbGF0aXZlIHRvXHJcblx0Ly8gdGhlIG1hcCBwYW5lPyBcImxlZnQgcG9pbnQgb2YgdGhlIG1hcCBsYXllclwiIGNhbiBiZSBjb25mdXNpbmcsIHNwZWNpYWxseVxyXG5cdC8vIHNpbmNlIHRoZXJlIGNhbiBiZSBuZWdhdGl2ZSBvZmZzZXRzLlxyXG5cdC8vIEBtZXRob2QgZ2V0UGl4ZWxPcmlnaW4oKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBwcm9qZWN0ZWQgcGl4ZWwgY29vcmRpbmF0ZXMgb2YgdGhlIHRvcCBsZWZ0IHBvaW50IG9mXHJcblx0Ly8gdGhlIG1hcCBsYXllciAodXNlZnVsIGluIGN1c3RvbSBsYXllciBhbmQgb3ZlcmxheSBpbXBsZW1lbnRhdGlvbnMpLlxyXG5cdGdldFBpeGVsT3JpZ2luOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9jaGVja0lmTG9hZGVkKCk7XHJcblx0XHRyZXR1cm4gdGhpcy5fcGl4ZWxPcmlnaW47XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRQaXhlbFdvcmxkQm91bmRzKHpvb20/OiBOdW1iZXIpOiBCb3VuZHNcclxuXHQvLyBSZXR1cm5zIHRoZSB3b3JsZCdzIGJvdW5kcyBpbiBwaXhlbCBjb29yZGluYXRlcyBmb3Igem9vbSBsZXZlbCBgem9vbWAuXHJcblx0Ly8gSWYgYHpvb21gIGlzIG9taXR0ZWQsIHRoZSBtYXAncyBjdXJyZW50IHpvb20gbGV2ZWwgaXMgdXNlZC5cclxuXHRnZXRQaXhlbFdvcmxkQm91bmRzOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMuZ2V0UHJvamVjdGVkQm91bmRzKHpvb20gPT09IHVuZGVmaW5lZCA/IHRoaXMuZ2V0Wm9vbSgpIDogem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cclxuXHQvLyBAbWV0aG9kIGdldFBhbmUocGFuZTogU3RyaW5nfEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIGEgW21hcCBwYW5lXSgjbWFwLXBhbmUpLCBnaXZlbiBpdHMgbmFtZSBvciBpdHMgSFRNTCBlbGVtZW50IChpdHMgaWRlbnRpdHkpLlxyXG5cdGdldFBhbmU6IGZ1bmN0aW9uIChwYW5lKSB7XHJcblx0XHRyZXR1cm4gdHlwZW9mIHBhbmUgPT09ICdzdHJpbmcnID8gdGhpcy5fcGFuZXNbcGFuZV0gOiBwYW5lO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UGFuZXMoKTogT2JqZWN0XHJcblx0Ly8gUmV0dXJucyBhIHBsYWluIG9iamVjdCBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhbGwgW3BhbmVzXSgjbWFwLXBhbmUpIGFzIGtleXMgYW5kXHJcblx0Ly8gdGhlIHBhbmVzIGFzIHZhbHVlcy5cclxuXHRnZXRQYW5lczogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3BhbmVzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q29udGFpbmVyOiBIVE1MRWxlbWVudFxyXG5cdC8vIFJldHVybnMgdGhlIEhUTUwgZWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSBtYXAuXHJcblx0Z2V0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBAc2VjdGlvbiBDb252ZXJzaW9uIE1ldGhvZHNcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRab29tU2NhbGUodG9ab29tOiBOdW1iZXIsIGZyb21ab29tOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBzY2FsZSBmYWN0b3IgdG8gYmUgYXBwbGllZCB0byBhIG1hcCB0cmFuc2l0aW9uIGZyb20gem9vbSBsZXZlbFxyXG5cdC8vIGBmcm9tWm9vbWAgdG8gYHRvWm9vbWAuIFVzZWQgaW50ZXJuYWxseSB0byBoZWxwIHdpdGggem9vbSBhbmltYXRpb25zLlxyXG5cdGdldFpvb21TY2FsZTogZnVuY3Rpb24gKHRvWm9vbSwgZnJvbVpvb20pIHtcclxuXHRcdC8vIFRPRE8gcmVwbGFjZSB3aXRoIHVuaXZlcnNhbCBpbXBsZW1lbnRhdGlvbiBhZnRlciByZWZhY3RvcmluZyBwcm9qZWN0aW9uc1xyXG5cdFx0dmFyIGNycyA9IHRoaXMub3B0aW9ucy5jcnM7XHJcblx0XHRmcm9tWm9vbSA9IGZyb21ab29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogZnJvbVpvb207XHJcblx0XHRyZXR1cm4gY3JzLnNjYWxlKHRvWm9vbSkgLyBjcnMuc2NhbGUoZnJvbVpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0U2NhbGVab29tKHNjYWxlOiBOdW1iZXIsIGZyb21ab29tOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSB6b29tIGxldmVsIHRoYXQgdGhlIG1hcCB3b3VsZCBlbmQgdXAgYXQsIGlmIGl0IGlzIGF0IGBmcm9tWm9vbWBcclxuXHQvLyBsZXZlbCBhbmQgZXZlcnl0aGluZyBpcyBzY2FsZWQgYnkgYSBmYWN0b3Igb2YgYHNjYWxlYC4gSW52ZXJzZSBvZlxyXG5cdC8vIFtgZ2V0Wm9vbVNjYWxlYF0oI21hcC1nZXRab29tU2NhbGUpLlxyXG5cdGdldFNjYWxlWm9vbTogZnVuY3Rpb24gKHNjYWxlLCBmcm9tWm9vbSkge1xyXG5cdFx0dmFyIGNycyA9IHRoaXMub3B0aW9ucy5jcnM7XHJcblx0XHRmcm9tWm9vbSA9IGZyb21ab29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogZnJvbVpvb207XHJcblx0XHR2YXIgem9vbSA9IGNycy56b29tKHNjYWxlICogY3JzLnNjYWxlKGZyb21ab29tKSk7XHJcblx0XHRyZXR1cm4gaXNOYU4oem9vbSkgPyBJbmZpbml0eSA6IHpvb207XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwcm9qZWN0KGxhdGxuZzogTGF0TG5nLCB6b29tOiBOdW1iZXIpOiBQb2ludFxyXG5cdC8vIFByb2plY3RzIGEgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUgYExhdExuZ2AgYWNjb3JkaW5nIHRvIHRoZSBwcm9qZWN0aW9uXHJcblx0Ly8gb2YgdGhlIG1hcCdzIENSUywgdGhlbiBzY2FsZXMgaXQgYWNjb3JkaW5nIHRvIGB6b29tYCBhbmQgdGhlIENSUydzXHJcblx0Ly8gYFRyYW5zZm9ybWF0aW9uYC4gVGhlIHJlc3VsdCBpcyBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvXHJcblx0Ly8gdGhlIENSUyBvcmlnaW4uXHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZywgem9vbSkge1xyXG5cdFx0em9vbSA9IHpvb20gPT09IHVuZGVmaW5lZCA/IHRoaXMuX3pvb20gOiB6b29tO1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMubGF0TG5nVG9Qb2ludChMLmxhdExuZyhsYXRsbmcpLCB6b29tKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVucHJvamVjdChwb2ludDogUG9pbnQsIHpvb206IE51bWJlcik6IExhdExuZ1xyXG5cdC8vIEludmVyc2Ugb2YgW2Bwcm9qZWN0YF0oI21hcC1wcm9qZWN0KS5cclxuXHR1bnByb2plY3Q6IGZ1bmN0aW9uIChwb2ludCwgem9vbSkge1xyXG5cdFx0em9vbSA9IHpvb20gPT09IHVuZGVmaW5lZCA/IHRoaXMuX3pvb20gOiB6b29tO1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMucG9pbnRUb0xhdExuZyhMLnBvaW50KHBvaW50KSwgem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsYXllclBvaW50VG9MYXRMbmcocG9pbnQ6IFBvaW50KTogTGF0TG5nXHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKSxcclxuXHQvLyByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIChmb3IgdGhlIGN1cnJlbnQgem9vbSBsZXZlbCkuXHJcblx0bGF5ZXJQb2ludFRvTGF0TG5nOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHZhciBwcm9qZWN0ZWRQb2ludCA9IEwucG9pbnQocG9pbnQpLmFkZCh0aGlzLmdldFBpeGVsT3JpZ2luKCkpO1xyXG5cdFx0cmV0dXJuIHRoaXMudW5wcm9qZWN0KHByb2plY3RlZFBvaW50KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmc6IExhdExuZyk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZSwgcmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyBwaXhlbCBjb29yZGluYXRlXHJcblx0Ly8gcmVsYXRpdmUgdG8gdGhlIFtvcmlnaW4gcGl4ZWxdKCNtYXAtZ2V0cGl4ZWxvcmlnaW4pLlxyXG5cdGxhdExuZ1RvTGF5ZXJQb2ludDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIHByb2plY3RlZFBvaW50ID0gdGhpcy5wcm9qZWN0KEwubGF0TG5nKGxhdGxuZykpLl9yb3VuZCgpO1xyXG5cdFx0cmV0dXJuIHByb2plY3RlZFBvaW50Ll9zdWJ0cmFjdCh0aGlzLmdldFBpeGVsT3JpZ2luKCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgd3JhcExhdExuZyhsYXRsbmc6IExhdExuZyk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgYSBgTGF0TG5nYCB3aGVyZSBgbGF0YCBhbmQgYGxuZ2AgaGFzIGJlZW4gd3JhcHBlZCBhY2NvcmRpbmcgdG8gdGhlXHJcblx0Ly8gbWFwJ3MgQ1JTJ3MgYHdyYXBMYXRgIGFuZCBgd3JhcExuZ2AgcHJvcGVydGllcywgaWYgdGhleSBhcmUgb3V0c2lkZSB0aGVcclxuXHQvLyBDUlMncyBib3VuZHMuXHJcblx0Ly8gQnkgZGVmYXVsdCB0aGlzIG1lYW5zIGxvbmdpdHVkZSBpcyB3cmFwcGVkIGFyb3VuZCB0aGUgZGF0ZWxpbmUgc28gaXRzXHJcblx0Ly8gdmFsdWUgaXMgYmV0d2VlbiAtMTgwIGFuZCArMTgwIGRlZ3JlZXMuXHJcblx0d3JhcExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMud3JhcExhdExuZyhMLmxhdExuZyhsYXRsbmcpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXBMYXRMbmdCb3VuZHMoYm91bmRzOiBMYXRMbmdCb3VuZHMpOiBMYXRMbmdCb3VuZHNcclxuXHQvLyBSZXR1cm5zIGEgYExhdExuZ0JvdW5kc2Agd2l0aCB0aGUgc2FtZSBzaXplIGFzIHRoZSBnaXZlbiBvbmUsIGVuc3VyaW5nIHRoYXRcclxuXHQvLyBpdHMgY2VudGVyIGlzIHdpdGhpbiB0aGUgQ1JTJ3MgYm91bmRzLlxyXG5cdC8vIEJ5IGRlZmF1bHQgdGhpcyBtZWFucyB0aGUgY2VudGVyIGxvbmdpdHVkZSBpcyB3cmFwcGVkIGFyb3VuZCB0aGUgZGF0ZWxpbmUgc28gaXRzXHJcblx0Ly8gdmFsdWUgaXMgYmV0d2VlbiAtMTgwIGFuZCArMTgwIGRlZ3JlZXMsIGFuZCB0aGUgbWFqb3JpdHkgb2YgdGhlIGJvdW5kc1xyXG5cdC8vIG92ZXJsYXBzIHRoZSBDUlMncyBib3VuZHMuXHJcblx0d3JhcExhdExuZ0JvdW5kczogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMud3JhcExhdExuZ0JvdW5kcyhMLmxhdExuZ0JvdW5kcyhsYXRsbmcpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGRpc3RhbmNlKGxhdGxuZzE6IExhdExuZywgbGF0bG5nMjogTGF0TG5nKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0d28gZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzIGFjY29yZGluZyB0b1xyXG5cdC8vIHRoZSBtYXAncyBDUlMuIEJ5IGRlZmF1bHQgdGhpcyBtZWFzdXJlcyBkaXN0YW5jZSBpbiBtZXRlcnMuXHJcblx0ZGlzdGFuY2U6IGZ1bmN0aW9uIChsYXRsbmcxLCBsYXRsbmcyKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLmNycy5kaXN0YW5jZShMLmxhdExuZyhsYXRsbmcxKSwgTC5sYXRMbmcobGF0bG5nMikpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQocG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBHaXZlbiBhIHBpeGVsIGNvb3JkaW5hdGUgcmVsYXRpdmUgdG8gdGhlIG1hcCBjb250YWluZXIsIHJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmdcclxuXHQvLyBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKS5cclxuXHRjb250YWluZXJQb2ludFRvTGF5ZXJQb2ludDogZnVuY3Rpb24gKHBvaW50KSB7IC8vIChQb2ludClcclxuXHRcdHJldHVybiBMLnBvaW50KHBvaW50KS5zdWJ0cmFjdCh0aGlzLl9nZXRNYXBQYW5lUG9zKCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbGF5ZXJQb2ludFRvQ29udGFpbmVyUG9pbnQocG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBHaXZlbiBhIHBpeGVsIGNvb3JkaW5hdGUgcmVsYXRpdmUgdG8gdGhlIFtvcmlnaW4gcGl4ZWxdKCNtYXAtZ2V0cGl4ZWxvcmlnaW4pLFxyXG5cdC8vIHJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmcgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGUgbWFwIGNvbnRhaW5lci5cclxuXHRsYXllclBvaW50VG9Db250YWluZXJQb2ludDogZnVuY3Rpb24gKHBvaW50KSB7IC8vIChQb2ludClcclxuXHRcdHJldHVybiBMLnBvaW50KHBvaW50KS5hZGQodGhpcy5fZ2V0TWFwUGFuZVBvcygpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5lclBvaW50VG9MYXRMbmcocG9pbnQ6IFBvaW50KTogTGF0TG5nXHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBtYXAgY29udGFpbmVyLCByZXR1cm5zXHJcblx0Ly8gdGhlIGNvcnJlc3BvbmRpbmcgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUgKGZvciB0aGUgY3VycmVudCB6b29tIGxldmVsKS5cclxuXHRjb250YWluZXJQb2ludFRvTGF0TG5nOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHZhciBsYXllclBvaW50ID0gdGhpcy5jb250YWluZXJQb2ludFRvTGF5ZXJQb2ludChMLnBvaW50KHBvaW50KSk7XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9MYXRMbmcobGF5ZXJQb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsYXRMbmdUb0NvbnRhaW5lclBvaW50KGxhdGxuZzogTGF0TG5nKTogUG9pbnRcclxuXHQvLyBHaXZlbiBhIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlLCByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIHBpeGVsIGNvb3JkaW5hdGVcclxuXHQvLyByZWxhdGl2ZSB0byB0aGUgbWFwIGNvbnRhaW5lci5cclxuXHRsYXRMbmdUb0NvbnRhaW5lclBvaW50OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9Db250YWluZXJQb2ludCh0aGlzLmxhdExuZ1RvTGF5ZXJQb2ludChMLmxhdExuZyhsYXRsbmcpKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChldjogTW91c2VFdmVudCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBNb3VzZUV2ZW50IG9iamVjdCwgcmV0dXJucyB0aGUgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGVcclxuXHQvLyBtYXAgY29udGFpbmVyIHdoZXJlIHRoZSBldmVudCB0b29rIHBsYWNlLlxyXG5cdG1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnQuZ2V0TW91c2VQb3NpdGlvbihlLCB0aGlzLl9jb250YWluZXIpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbW91c2VFdmVudFRvTGF5ZXJQb2ludChldjogTW91c2VFdmVudCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBNb3VzZUV2ZW50IG9iamVjdCwgcmV0dXJucyB0aGUgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKSB3aGVyZSB0aGUgZXZlbnQgdG9vayBwbGFjZS5cclxuXHRtb3VzZUV2ZW50VG9MYXllclBvaW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQodGhpcy5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtb3VzZUV2ZW50VG9MYXRMbmcoZXY6IE1vdXNlRXZlbnQpOiBMYXRMbmdcclxuXHQvLyBHaXZlbiBhIE1vdXNlRXZlbnQgb2JqZWN0LCByZXR1cm5zIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIHdoZXJlIHRoZVxyXG5cdC8vIGV2ZW50IHRvb2sgcGxhY2UuXHJcblx0bW91c2VFdmVudFRvTGF0TG5nOiBmdW5jdGlvbiAoZSkgeyAvLyAoTW91c2VFdmVudClcclxuXHRcdHJldHVybiB0aGlzLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLm1vdXNlRXZlbnRUb0xheWVyUG9pbnQoZSkpO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBtYXAgaW5pdGlhbGl6YXRpb24gbWV0aG9kc1xyXG5cclxuXHRfaW5pdENvbnRhaW5lcjogZnVuY3Rpb24gKGlkKSB7XHJcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmdldChpZCk7XHJcblxyXG5cdFx0aWYgKCFjb250YWluZXIpIHtcclxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdNYXAgY29udGFpbmVyIG5vdCBmb3VuZC4nKTtcclxuXHRcdH0gZWxzZSBpZiAoY29udGFpbmVyLl9sZWFmbGV0X2lkKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignTWFwIGNvbnRhaW5lciBpcyBhbHJlYWR5IGluaXRpYWxpemVkLicpO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIoY29udGFpbmVyLCAnc2Nyb2xsJywgdGhpcy5fb25TY3JvbGwsIHRoaXMpO1xyXG5cdFx0dGhpcy5fY29udGFpbmVySWQgPSBMLlV0aWwuc3RhbXAoY29udGFpbmVyKTtcclxuXHR9LFxyXG5cclxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcjtcclxuXHJcblx0XHR0aGlzLl9mYWRlQW5pbWF0ZWQgPSB0aGlzLm9wdGlvbnMuZmFkZUFuaW1hdGlvbiAmJiBMLkJyb3dzZXIuYW55M2Q7XHJcblxyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtY29udGFpbmVyJyArXHJcblx0XHRcdChMLkJyb3dzZXIudG91Y2ggPyAnIGxlYWZsZXQtdG91Y2gnIDogJycpICtcclxuXHRcdFx0KEwuQnJvd3Nlci5yZXRpbmEgPyAnIGxlYWZsZXQtcmV0aW5hJyA6ICcnKSArXHJcblx0XHRcdChMLkJyb3dzZXIuaWVsdDkgPyAnIGxlYWZsZXQtb2xkaWUnIDogJycpICtcclxuXHRcdFx0KEwuQnJvd3Nlci5zYWZhcmkgPyAnIGxlYWZsZXQtc2FmYXJpJyA6ICcnKSArXHJcblx0XHRcdCh0aGlzLl9mYWRlQW5pbWF0ZWQgPyAnIGxlYWZsZXQtZmFkZS1hbmltJyA6ICcnKSk7XHJcblxyXG5cdFx0dmFyIHBvc2l0aW9uID0gTC5Eb21VdGlsLmdldFN0eWxlKGNvbnRhaW5lciwgJ3Bvc2l0aW9uJyk7XHJcblxyXG5cdFx0aWYgKHBvc2l0aW9uICE9PSAnYWJzb2x1dGUnICYmIHBvc2l0aW9uICE9PSAncmVsYXRpdmUnICYmIHBvc2l0aW9uICE9PSAnZml4ZWQnKSB7XHJcblx0XHRcdGNvbnRhaW5lci5zdHlsZS5wb3NpdGlvbiA9ICdyZWxhdGl2ZSc7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdFBhbmVzKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2luaXRDb250cm9sUG9zKSB7XHJcblx0XHRcdHRoaXMuX2luaXRDb250cm9sUG9zKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2luaXRQYW5lczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBhbmVzID0gdGhpcy5fcGFuZXMgPSB7fTtcclxuXHRcdHRoaXMuX3BhbmVSZW5kZXJlcnMgPSB7fTtcclxuXHJcblx0XHQvLyBAc2VjdGlvblxyXG5cdFx0Ly9cclxuXHRcdC8vIFBhbmVzIGFyZSBET00gZWxlbWVudHMgdXNlZCB0byBjb250cm9sIHRoZSBvcmRlcmluZyBvZiBsYXllcnMgb24gdGhlIG1hcC4gWW91XHJcblx0XHQvLyBjYW4gYWNjZXNzIHBhbmVzIHdpdGggW2BtYXAuZ2V0UGFuZWBdKCNtYXAtZ2V0cGFuZSkgb3JcclxuXHRcdC8vIFtgbWFwLmdldFBhbmVzYF0oI21hcC1nZXRwYW5lcykgbWV0aG9kcy4gTmV3IHBhbmVzIGNhbiBiZSBjcmVhdGVkIHdpdGggdGhlXHJcblx0XHQvLyBbYG1hcC5jcmVhdGVQYW5lYF0oI21hcC1jcmVhdGVwYW5lKSBtZXRob2QuXHJcblx0XHQvL1xyXG5cdFx0Ly8gRXZlcnkgbWFwIGhhcyB0aGUgZm9sbG93aW5nIGRlZmF1bHQgcGFuZXMgdGhhdCBkaWZmZXIgb25seSBpbiB6SW5kZXguXHJcblx0XHQvL1xyXG5cdFx0Ly8gQHBhbmUgbWFwUGFuZTogSFRNTEVsZW1lbnQgPSAnYXV0bydcclxuXHRcdC8vIFBhbmUgdGhhdCBjb250YWlucyBhbGwgb3RoZXIgbWFwIHBhbmVzXHJcblxyXG5cdFx0dGhpcy5fbWFwUGFuZSA9IHRoaXMuY3JlYXRlUGFuZSgnbWFwUGFuZScsIHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSwgbmV3IEwuUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdC8vIEBwYW5lIHRpbGVQYW5lOiBIVE1MRWxlbWVudCA9IDIwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYEdyaWRMYXllcmBzIGFuZCBgVGlsZUxheWVyYHNcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgndGlsZVBhbmUnKTtcclxuXHRcdC8vIEBwYW5lIG92ZXJsYXlQYW5lOiBIVE1MRWxlbWVudCA9IDQwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgdmVjdG9yIG92ZXJsYXlzIChgUGF0aGBzKSwgbGlrZSBgUG9seWxpbmVgcyBhbmQgYFBvbHlnb25gc1xyXG5cdFx0dGhpcy5jcmVhdGVQYW5lKCdzaGFkb3dQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSBzaGFkb3dQYW5lOiBIVE1MRWxlbWVudCA9IDUwMFxyXG5cdFx0Ly8gUGFuZSBmb3Igb3ZlcmxheSBzaGFkb3dzIChlLmcuIGBNYXJrZXJgIHNoYWRvd3MpXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ292ZXJsYXlQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSBtYXJrZXJQYW5lOiBIVE1MRWxlbWVudCA9IDYwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYEljb25gcyBvZiBgTWFya2VyYHNcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgnbWFya2VyUGFuZScpO1xyXG5cdFx0Ly8gQHBhbmUgdG9vbHRpcFBhbmU6IEhUTUxFbGVtZW50ID0gNjUwXHJcblx0XHQvLyBQYW5lIGZvciB0b29sdGlwLlxyXG5cdFx0dGhpcy5jcmVhdGVQYW5lKCd0b29sdGlwUGFuZScpO1xyXG5cdFx0Ly8gQHBhbmUgcG9wdXBQYW5lOiBIVE1MRWxlbWVudCA9IDcwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYFBvcHVwYHMuXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ3BvcHVwUGFuZScpO1xyXG5cclxuXHRcdGlmICghdGhpcy5vcHRpb25zLm1hcmtlclpvb21BbmltYXRpb24pIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHBhbmVzLm1hcmtlclBhbmUsICdsZWFmbGV0LXpvb20taGlkZScpO1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MocGFuZXMuc2hhZG93UGFuZSwgJ2xlYWZsZXQtem9vbS1oaWRlJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIHByaXZhdGUgbWV0aG9kcyB0aGF0IG1vZGlmeSBtYXAgc3RhdGVcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWFwIHN0YXRlIGNoYW5nZSBldmVudHNcclxuXHRfcmVzZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSwgbmV3IEwuUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdHZhciBsb2FkaW5nID0gIXRoaXMuX2xvYWRlZDtcclxuXHRcdHRoaXMuX2xvYWRlZCA9IHRydWU7XHJcblx0XHR6b29tID0gdGhpcy5fbGltaXRab29tKHpvb20pO1xyXG5cclxuXHRcdHRoaXMuZmlyZSgndmlld3ByZXJlc2V0Jyk7XHJcblxyXG5cdFx0dmFyIHpvb21DaGFuZ2VkID0gdGhpcy5fem9vbSAhPT0gem9vbTtcclxuXHRcdHRoaXNcclxuXHRcdFx0Ll9tb3ZlU3RhcnQoem9vbUNoYW5nZWQpXHJcblx0XHRcdC5fbW92ZShjZW50ZXIsIHpvb20pXHJcblx0XHRcdC5fbW92ZUVuZCh6b29tQ2hhbmdlZCk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IHZpZXdyZXNldDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcCBuZWVkcyB0byByZWRyYXcgaXRzIGNvbnRlbnQgKHRoaXMgdXN1YWxseSBoYXBwZW5zXHJcblx0XHQvLyBvbiBtYXAgem9vbSBvciBsb2FkKS4gVmVyeSB1c2VmdWwgZm9yIGNyZWF0aW5nIGN1c3RvbSBvdmVybGF5cy5cclxuXHRcdHRoaXMuZmlyZSgndmlld3Jlc2V0Jyk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxvYWQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaXMgaW5pdGlhbGl6ZWQgKHdoZW4gaXRzIGNlbnRlciBhbmQgem9vbSBhcmUgc2V0XHJcblx0XHQvLyBmb3IgdGhlIGZpcnN0IHRpbWUpLlxyXG5cdFx0aWYgKGxvYWRpbmcpIHtcclxuXHRcdFx0dGhpcy5maXJlKCdsb2FkJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X21vdmVTdGFydDogZnVuY3Rpb24gKHpvb21DaGFuZ2VkKSB7XHJcblx0XHQvLyBAZXZlbnQgem9vbXN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIHpvb20gaXMgYWJvdXQgdG8gY2hhbmdlIChlLmcuIGJlZm9yZSB6b29tIGFuaW1hdGlvbikuXHJcblx0XHQvLyBAZXZlbnQgbW92ZXN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdmlldyBvZiB0aGUgbWFwIHN0YXJ0cyBjaGFuZ2luZyAoZS5nLiB1c2VyIHN0YXJ0cyBkcmFnZ2luZyB0aGUgbWFwKS5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb21zdGFydCcpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZXN0YXJ0Jyk7XHJcblx0fSxcclxuXHJcblx0X21vdmU6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIGRhdGEpIHtcclxuXHRcdGlmICh6b29tID09PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0em9vbSA9IHRoaXMuX3pvb207XHJcblx0XHR9XHJcblx0XHR2YXIgem9vbUNoYW5nZWQgPSB0aGlzLl96b29tICE9PSB6b29tO1xyXG5cclxuXHRcdHRoaXMuX3pvb20gPSB6b29tO1xyXG5cdFx0dGhpcy5fbGFzdENlbnRlciA9IGNlbnRlcjtcclxuXHRcdHRoaXMuX3BpeGVsT3JpZ2luID0gdGhpcy5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyKTtcclxuXHJcblx0XHQvLyBAZXZlbnQgem9vbTogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHJlcGVhdGVkbHkgZHVyaW5nIGFueSBjaGFuZ2UgaW4gem9vbSBsZXZlbCwgaW5jbHVkaW5nIHpvb21cclxuXHRcdC8vIGFuZCBmbHkgYW5pbWF0aW9ucy5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCB8fCAoZGF0YSAmJiBkYXRhLnBpbmNoKSkge1x0Ly8gQWx3YXlzIGZpcmUgJ3pvb20nIGlmIHBpbmNoaW5nIGJlY2F1c2UgIzM1MzBcclxuXHRcdFx0dGhpcy5maXJlKCd6b29tJywgZGF0YSk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQGV2ZW50IG1vdmU6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCByZXBlYXRlZGx5IGR1cmluZyBhbnkgbW92ZW1lbnQgb2YgdGhlIG1hcCwgaW5jbHVkaW5nIHBhbiBhbmRcclxuXHRcdC8vIGZseSBhbmltYXRpb25zLlxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZScsIGRhdGEpO1xyXG5cdH0sXHJcblxyXG5cdF9tb3ZlRW5kOiBmdW5jdGlvbiAoem9vbUNoYW5nZWQpIHtcclxuXHRcdC8vIEBldmVudCB6b29tZW5kOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIGhhcyBjaGFuZ2VkLCBhZnRlciBhbnkgYW5pbWF0aW9ucy5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb21lbmQnKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgbW92ZWVuZDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIGNlbnRlciBvZiB0aGUgbWFwIHN0b3BzIGNoYW5naW5nIChlLmcuIHVzZXIgc3RvcHBlZFxyXG5cdFx0Ly8gZHJhZ2dpbmcgdGhlIG1hcCkuXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0fSxcclxuXHJcblx0X3N0b3A6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fZmx5VG9GcmFtZSk7XHJcblx0XHRpZiAodGhpcy5fcGFuQW5pbSkge1xyXG5cdFx0XHR0aGlzLl9wYW5BbmltLnN0b3AoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9yYXdQYW5CeTogZnVuY3Rpb24gKG9mZnNldCkge1xyXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX21hcFBhbmUsIHRoaXMuX2dldE1hcFBhbmVQb3MoKS5zdWJ0cmFjdChvZmZzZXQpKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0Wm9vbVNwYW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmdldE1heFpvb20oKSAtIHRoaXMuZ2V0TWluWm9vbSgpO1xyXG5cdH0sXHJcblxyXG5cdF9wYW5JbnNpZGVNYXhCb3VuZHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fZW5mb3JjaW5nQm91bmRzKSB7XHJcblx0XHRcdHRoaXMucGFuSW5zaWRlQm91bmRzKHRoaXMub3B0aW9ucy5tYXhCb3VuZHMpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9jaGVja0lmTG9hZGVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ1NldCBtYXAgY2VudGVyIGFuZCB6b29tIGZpcnN0LicpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIERPTSBldmVudCBoYW5kbGluZ1xyXG5cclxuXHQvLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBldmVudHNcclxuXHRfaW5pdEV2ZW50czogZnVuY3Rpb24gKHJlbW92ZSkge1xyXG5cdFx0aWYgKCFMLkRvbUV2ZW50KSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHRoaXMuX3RhcmdldHMgPSB7fTtcclxuXHRcdHRoaXMuX3RhcmdldHNbTC5zdGFtcCh0aGlzLl9jb250YWluZXIpXSA9IHRoaXM7XHJcblxyXG5cdFx0dmFyIG9uT2ZmID0gcmVtb3ZlID8gJ29mZicgOiAnb24nO1xyXG5cclxuXHRcdC8vIEBldmVudCBjbGljazogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciBjbGlja3MgKG9yIHRhcHMpIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgZGJsY2xpY2s6IE1vdXNlRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgZG91YmxlLWNsaWNrcyAob3IgZG91YmxlLXRhcHMpIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2Vkb3duOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHB1c2hlcyB0aGUgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2V1cDogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciByZWxlYXNlcyB0aGUgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2VvdmVyOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtb3VzZSBlbnRlcnMgdGhlIG1hcC5cclxuXHRcdC8vIEBldmVudCBtb3VzZW91dDogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbW91c2UgbGVhdmVzIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2Vtb3ZlOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGlsZSB0aGUgbW91c2UgbW92ZXMgb3ZlciB0aGUgbWFwLlxyXG5cdFx0Ly8gQGV2ZW50IGNvbnRleHRtZW51OiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHB1c2hlcyB0aGUgcmlnaHQgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAsIHByZXZlbnRzXHJcblx0XHQvLyBkZWZhdWx0IGJyb3dzZXIgY29udGV4dCBtZW51IGZyb20gc2hvd2luZyBpZiB0aGVyZSBhcmUgbGlzdGVuZXJzIG9uXHJcblx0XHQvLyB0aGlzIGV2ZW50LiBBbHNvIGZpcmVkIG9uIG1vYmlsZSB3aGVuIHRoZSB1c2VyIGhvbGRzIGEgc2luZ2xlIHRvdWNoXHJcblx0XHQvLyBmb3IgYSBzZWNvbmQgKGFsc28gY2FsbGVkIGxvbmcgcHJlc3MpLlxyXG5cdFx0Ly8gQGV2ZW50IGtleXByZXNzOiBLZXlib2FyZEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHByZXNzZXMgYSBrZXkgZnJvbSB0aGUga2V5Ym9hcmQgd2hpbGUgdGhlIG1hcCBpcyBmb2N1c2VkLlxyXG5cdFx0TC5Eb21FdmVudFtvbk9mZl0odGhpcy5fY29udGFpbmVyLCAnY2xpY2sgZGJsY2xpY2sgbW91c2Vkb3duIG1vdXNldXAgJyArXHJcblx0XHRcdCdtb3VzZW92ZXIgbW91c2VvdXQgbW91c2Vtb3ZlIGNvbnRleHRtZW51IGtleXByZXNzJywgdGhpcy5faGFuZGxlRE9NRXZlbnQsIHRoaXMpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMudHJhY2tSZXNpemUpIHtcclxuXHRcdFx0TC5Eb21FdmVudFtvbk9mZl0od2luZG93LCAncmVzaXplJywgdGhpcy5fb25SZXNpemUsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QgJiYgdGhpcy5vcHRpb25zLnRyYW5zZm9ybTNETGltaXQpIHtcclxuXHRcdFx0dGhpc1tvbk9mZl0oJ21vdmVlbmQnLCB0aGlzLl9vbk1vdmVFbmQpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9vblJlc2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9yZXNpemVSZXF1ZXN0KTtcclxuXHRcdHRoaXMuX3Jlc2l6ZVJlcXVlc3QgPSBMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShcclxuXHRcdCAgICAgICAgZnVuY3Rpb24gKCkgeyB0aGlzLmludmFsaWRhdGVTaXplKHtkZWJvdW5jZU1vdmVlbmQ6IHRydWV9KTsgfSwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0X29uU2Nyb2xsOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9jb250YWluZXIuc2Nyb2xsVG9wICA9IDA7XHJcblx0XHR0aGlzLl9jb250YWluZXIuc2Nyb2xsTGVmdCA9IDA7XHJcblx0fSxcclxuXHJcblx0X29uTW92ZUVuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX2dldE1hcFBhbmVQb3MoKTtcclxuXHRcdGlmIChNYXRoLm1heChNYXRoLmFicyhwb3MueCksIE1hdGguYWJzKHBvcy55KSkgPj0gdGhpcy5vcHRpb25zLnRyYW5zZm9ybTNETGltaXQpIHtcclxuXHRcdFx0Ly8gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTIwMzg3MyBidXQgV2Via2l0IGFsc28gaGF2ZVxyXG5cdFx0XHQvLyBhIHBpeGVsIG9mZnNldCBvbiB2ZXJ5IGhpZ2ggdmFsdWVzLCBzZWU6IGh0dHA6Ly9qc2ZpZGRsZS5uZXQvZGc2cjVoaGIvXHJcblx0XHRcdHRoaXMuX3Jlc2V0Vmlldyh0aGlzLmdldENlbnRlcigpLCB0aGlzLmdldFpvb20oKSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2ZpbmRFdmVudFRhcmdldHM6IGZ1bmN0aW9uIChlLCB0eXBlKSB7XHJcblx0XHR2YXIgdGFyZ2V0cyA9IFtdLFxyXG5cdFx0ICAgIHRhcmdldCxcclxuXHRcdCAgICBpc0hvdmVyID0gdHlwZSA9PT0gJ21vdXNlb3V0JyB8fCB0eXBlID09PSAnbW91c2VvdmVyJyxcclxuXHRcdCAgICBzcmMgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQsXHJcblx0XHQgICAgZHJhZ2dpbmcgPSBmYWxzZTtcclxuXHJcblx0XHR3aGlsZSAoc3JjKSB7XHJcblx0XHRcdHRhcmdldCA9IHRoaXMuX3RhcmdldHNbTC5zdGFtcChzcmMpXTtcclxuXHRcdFx0aWYgKHRhcmdldCAmJiAodHlwZSA9PT0gJ2NsaWNrJyB8fCB0eXBlID09PSAncHJlY2xpY2snKSAmJiAhZS5fc2ltdWxhdGVkICYmIHRoaXMuX2RyYWdnYWJsZU1vdmVkKHRhcmdldCkpIHtcclxuXHRcdFx0XHQvLyBQcmV2ZW50IGZpcmluZyBjbGljayBhZnRlciB5b3UganVzdCBkcmFnZ2VkIGFuIG9iamVjdC5cclxuXHRcdFx0XHRkcmFnZ2luZyA9IHRydWU7XHJcblx0XHRcdFx0YnJlYWs7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKHRhcmdldCAmJiB0YXJnZXQubGlzdGVucyh0eXBlLCB0cnVlKSkge1xyXG5cdFx0XHRcdGlmIChpc0hvdmVyICYmICFMLkRvbUV2ZW50Ll9pc0V4dGVybmFsVGFyZ2V0KHNyYywgZSkpIHsgYnJlYWs7IH1cclxuXHRcdFx0XHR0YXJnZXRzLnB1c2godGFyZ2V0KTtcclxuXHRcdFx0XHRpZiAoaXNIb3ZlcikgeyBicmVhazsgfVxyXG5cdFx0XHR9XHJcblx0XHRcdGlmIChzcmMgPT09IHRoaXMuX2NvbnRhaW5lcikgeyBicmVhazsgfVxyXG5cdFx0XHRzcmMgPSBzcmMucGFyZW50Tm9kZTtcclxuXHRcdH1cclxuXHRcdGlmICghdGFyZ2V0cy5sZW5ndGggJiYgIWRyYWdnaW5nICYmICFpc0hvdmVyICYmIEwuRG9tRXZlbnQuX2lzRXh0ZXJuYWxUYXJnZXQoc3JjLCBlKSkge1xyXG5cdFx0XHR0YXJnZXRzID0gW3RoaXNdO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRhcmdldHM7XHJcblx0fSxcclxuXHJcblx0X2hhbmRsZURPTUV2ZW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9sb2FkZWQgfHwgTC5Eb21FdmVudC5fc2tpcHBlZChlKSkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgdHlwZSA9IGUudHlwZSA9PT0gJ2tleXByZXNzJyAmJiBlLmtleUNvZGUgPT09IDEzID8gJ2NsaWNrJyA6IGUudHlwZTtcclxuXHJcblx0XHRpZiAodHlwZSA9PT0gJ21vdXNlZG93bicpIHtcclxuXHRcdFx0Ly8gcHJldmVudHMgb3V0bGluZSB3aGVuIGNsaWNraW5nIG9uIGtleWJvYXJkLWZvY3VzYWJsZSBlbGVtZW50XHJcblx0XHRcdEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZShlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2ZpcmVET01FdmVudChlLCB0eXBlKTtcclxuXHR9LFxyXG5cclxuXHRfZmlyZURPTUV2ZW50OiBmdW5jdGlvbiAoZSwgdHlwZSwgdGFyZ2V0cykge1xyXG5cclxuXHRcdGlmIChlLnR5cGUgPT09ICdjbGljaycpIHtcclxuXHRcdFx0Ly8gRmlyZSBhIHN5bnRoZXRpYyAncHJlY2xpY2snIGV2ZW50IHdoaWNoIHByb3BhZ2F0ZXMgdXAgKG1haW5seSBmb3IgY2xvc2luZyBwb3B1cHMpLlxyXG5cdFx0XHQvLyBAZXZlbnQgcHJlY2xpY2s6IE1vdXNlRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgYmVmb3JlIG1vdXNlIGNsaWNrIG9uIHRoZSBtYXAgKHNvbWV0aW1lcyB1c2VmdWwgd2hlbiB5b3VcclxuXHRcdFx0Ly8gd2FudCBzb21ldGhpbmcgdG8gaGFwcGVuIG9uIGNsaWNrIGJlZm9yZSBhbnkgZXhpc3RpbmcgY2xpY2tcclxuXHRcdFx0Ly8gaGFuZGxlcnMgc3RhcnQgcnVubmluZykuXHJcblx0XHRcdHZhciBzeW50aCA9IEwuVXRpbC5leHRlbmQoe30sIGUpO1xyXG5cdFx0XHRzeW50aC50eXBlID0gJ3ByZWNsaWNrJztcclxuXHRcdFx0dGhpcy5fZmlyZURPTUV2ZW50KHN5bnRoLCBzeW50aC50eXBlLCB0YXJnZXRzKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoZS5fc3RvcHBlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHQvLyBGaW5kIHRoZSBsYXllciB0aGUgZXZlbnQgaXMgcHJvcGFnYXRpbmcgZnJvbSBhbmQgaXRzIHBhcmVudHMuXHJcblx0XHR0YXJnZXRzID0gKHRhcmdldHMgfHwgW10pLmNvbmNhdCh0aGlzLl9maW5kRXZlbnRUYXJnZXRzKGUsIHR5cGUpKTtcclxuXHJcblx0XHRpZiAoIXRhcmdldHMubGVuZ3RoKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciB0YXJnZXQgPSB0YXJnZXRzWzBdO1xyXG5cdFx0aWYgKHR5cGUgPT09ICdjb250ZXh0bWVudScgJiYgdGFyZ2V0Lmxpc3RlbnModHlwZSwgdHJ1ZSkpIHtcclxuXHRcdFx0TC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdChlKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0b3JpZ2luYWxFdmVudDogZVxyXG5cdFx0fTtcclxuXHJcblx0XHRpZiAoZS50eXBlICE9PSAna2V5cHJlc3MnKSB7XHJcblx0XHRcdHZhciBpc01hcmtlciA9IHRhcmdldCBpbnN0YW5jZW9mIEwuTWFya2VyO1xyXG5cdFx0XHRkYXRhLmNvbnRhaW5lclBvaW50ID0gaXNNYXJrZXIgP1xyXG5cdFx0XHRcdFx0dGhpcy5sYXRMbmdUb0NvbnRhaW5lclBvaW50KHRhcmdldC5nZXRMYXRMbmcoKSkgOiB0aGlzLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xyXG5cdFx0XHRkYXRhLmxheWVyUG9pbnQgPSB0aGlzLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KGRhdGEuY29udGFpbmVyUG9pbnQpO1xyXG5cdFx0XHRkYXRhLmxhdGxuZyA9IGlzTWFya2VyID8gdGFyZ2V0LmdldExhdExuZygpIDogdGhpcy5sYXllclBvaW50VG9MYXRMbmcoZGF0YS5sYXllclBvaW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHRhcmdldHMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGFyZ2V0c1tpXS5maXJlKHR5cGUsIGRhdGEsIHRydWUpO1xyXG5cdFx0XHRpZiAoZGF0YS5vcmlnaW5hbEV2ZW50Ll9zdG9wcGVkIHx8XHJcblx0XHRcdFx0KHRhcmdldHNbaV0ub3B0aW9ucy5ub25CdWJibGluZ0V2ZW50cyAmJiBMLlV0aWwuaW5kZXhPZih0YXJnZXRzW2ldLm9wdGlvbnMubm9uQnViYmxpbmdFdmVudHMsIHR5cGUpICE9PSAtMSkpIHsgcmV0dXJuOyB9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2RyYWdnYWJsZU1vdmVkOiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHRvYmogPSBvYmouZHJhZ2dpbmcgJiYgb2JqLmRyYWdnaW5nLmVuYWJsZWQoKSA/IG9iaiA6IHRoaXM7XHJcblx0XHRyZXR1cm4gKG9iai5kcmFnZ2luZyAmJiBvYmouZHJhZ2dpbmcubW92ZWQoKSkgfHwgKHRoaXMuYm94Wm9vbSAmJiB0aGlzLmJveFpvb20ubW92ZWQoKSk7XHJcblx0fSxcclxuXHJcblx0X2NsZWFySGFuZGxlcnM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0aGlzLl9oYW5kbGVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHR0aGlzLl9oYW5kbGVyc1tpXS5kaXNhYmxlKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cclxuXHQvLyBAbWV0aG9kIHdoZW5SZWFkeShmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gUnVucyB0aGUgZ2l2ZW4gZnVuY3Rpb24gYGZuYCB3aGVuIHRoZSBtYXAgZ2V0cyBpbml0aWFsaXplZCB3aXRoXHJcblx0Ly8gYSB2aWV3IChjZW50ZXIgYW5kIHpvb20pIGFuZCBhdCBsZWFzdCBvbmUgbGF5ZXIsIG9yIGltbWVkaWF0ZWx5XHJcblx0Ly8gaWYgaXQncyBhbHJlYWR5IGluaXRpYWxpemVkLCBvcHRpb25hbGx5IHBhc3NpbmcgYSBmdW5jdGlvbiBjb250ZXh0LlxyXG5cdHdoZW5SZWFkeTogZnVuY3Rpb24gKGNhbGxiYWNrLCBjb250ZXh0KSB7XHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdGNhbGxiYWNrLmNhbGwoY29udGV4dCB8fCB0aGlzLCB7dGFyZ2V0OiB0aGlzfSk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLm9uKCdsb2FkJywgY2FsbGJhY2ssIGNvbnRleHQpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIHByaXZhdGUgbWV0aG9kcyBmb3IgZ2V0dGluZyBtYXAgc3RhdGVcclxuXHJcblx0X2dldE1hcFBhbmVQb3M6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBMLkRvbVV0aWwuZ2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSkgfHwgbmV3IEwuUG9pbnQoMCwgMCk7XHJcblx0fSxcclxuXHJcblx0X21vdmVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgcG9zID0gdGhpcy5fZ2V0TWFwUGFuZVBvcygpO1xyXG5cdFx0cmV0dXJuIHBvcyAmJiAhcG9zLmVxdWFscyhbMCwgMF0pO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRUb3BMZWZ0UG9pbnQ6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcclxuXHRcdHZhciBwaXhlbE9yaWdpbiA9IGNlbnRlciAmJiB6b29tICE9PSB1bmRlZmluZWQgP1xyXG5cdFx0XHR0aGlzLl9nZXROZXdQaXhlbE9yaWdpbihjZW50ZXIsIHpvb20pIDpcclxuXHRcdFx0dGhpcy5nZXRQaXhlbE9yaWdpbigpO1xyXG5cdFx0cmV0dXJuIHBpeGVsT3JpZ2luLnN1YnRyYWN0KHRoaXMuX2dldE1hcFBhbmVQb3MoKSk7XHJcblx0fSxcclxuXHJcblx0X2dldE5ld1BpeGVsT3JpZ2luOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHR2YXIgdmlld0hhbGYgPSB0aGlzLmdldFNpemUoKS5fZGl2aWRlQnkoMik7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0KGNlbnRlciwgem9vbSkuX3N1YnRyYWN0KHZpZXdIYWxmKS5fYWRkKHRoaXMuX2dldE1hcFBhbmVQb3MoKSkuX3JvdW5kKCk7XHJcblx0fSxcclxuXHJcblx0X2xhdExuZ1RvTmV3TGF5ZXJQb2ludDogZnVuY3Rpb24gKGxhdGxuZywgem9vbSwgY2VudGVyKSB7XHJcblx0XHR2YXIgdG9wTGVmdCA9IHRoaXMuX2dldE5ld1BpeGVsT3JpZ2luKGNlbnRlciwgem9vbSk7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0KGxhdGxuZywgem9vbSkuX3N1YnRyYWN0KHRvcExlZnQpO1xyXG5cdH0sXHJcblxyXG5cdF9sYXRMbmdCb3VuZHNUb05ld0xheWVyQm91bmRzOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzLCB6b29tLCBjZW50ZXIpIHtcclxuXHRcdHZhciB0b3BMZWZ0ID0gdGhpcy5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyLCB6b29tKTtcclxuXHRcdHJldHVybiBMLmJvdW5kcyhbXHJcblx0XHRcdHRoaXMucHJvamVjdChsYXRMbmdCb3VuZHMuZ2V0U291dGhXZXN0KCksIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KSxcclxuXHRcdFx0dGhpcy5wcm9qZWN0KGxhdExuZ0JvdW5kcy5nZXROb3J0aFdlc3QoKSwgem9vbSkuX3N1YnRyYWN0KHRvcExlZnQpLFxyXG5cdFx0XHR0aGlzLnByb2plY3QobGF0TG5nQm91bmRzLmdldFNvdXRoRWFzdCgpLCB6b29tKS5fc3VidHJhY3QodG9wTGVmdCksXHJcblx0XHRcdHRoaXMucHJvamVjdChsYXRMbmdCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KVxyXG5cdFx0XSk7XHJcblx0fSxcclxuXHJcblx0Ly8gbGF5ZXIgcG9pbnQgb2YgdGhlIGN1cnJlbnQgY2VudGVyXHJcblx0X2dldENlbnRlckxheWVyUG9pbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KHRoaXMuZ2V0U2l6ZSgpLl9kaXZpZGVCeSgyKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gb2Zmc2V0IG9mIHRoZSBzcGVjaWZpZWQgcGxhY2UgdG8gdGhlIGN1cnJlbnQgY2VudGVyIGluIHBpeGVsc1xyXG5cdF9nZXRDZW50ZXJPZmZzZXQ6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLmxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmcpLnN1YnRyYWN0KHRoaXMuX2dldENlbnRlckxheWVyUG9pbnQoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gYWRqdXN0IGNlbnRlciBmb3IgdmlldyB0byBnZXQgaW5zaWRlIGJvdW5kc1xyXG5cdF9saW1pdENlbnRlcjogZnVuY3Rpb24gKGNlbnRlciwgem9vbSwgYm91bmRzKSB7XHJcblxyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIGNlbnRlcjsgfVxyXG5cclxuXHRcdHZhciBjZW50ZXJQb2ludCA9IHRoaXMucHJvamVjdChjZW50ZXIsIHpvb20pLFxyXG5cdFx0ICAgIHZpZXdIYWxmID0gdGhpcy5nZXRTaXplKCkuZGl2aWRlQnkoMiksXHJcblx0XHQgICAgdmlld0JvdW5kcyA9IG5ldyBMLkJvdW5kcyhjZW50ZXJQb2ludC5zdWJ0cmFjdCh2aWV3SGFsZiksIGNlbnRlclBvaW50LmFkZCh2aWV3SGFsZikpLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX2dldEJvdW5kc09mZnNldCh2aWV3Qm91bmRzLCBib3VuZHMsIHpvb20pO1xyXG5cclxuXHRcdC8vIElmIG9mZnNldCBpcyBsZXNzIHRoYW4gYSBwaXhlbCwgaWdub3JlLlxyXG5cdFx0Ly8gVGhpcyBwcmV2ZW50cyB1bnN0YWJsZSBwcm9qZWN0aW9ucyBmcm9tIGdldHRpbmcgaW50b1xyXG5cdFx0Ly8gYW4gaW5maW5pdGUgbG9vcCBvZiB0aW55IG9mZnNldHMuXHJcblx0XHRpZiAob2Zmc2V0LnJvdW5kKCkuZXF1YWxzKFswLCAwXSkpIHtcclxuXHRcdFx0cmV0dXJuIGNlbnRlcjtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcy51bnByb2plY3QoY2VudGVyUG9pbnQuYWRkKG9mZnNldCksIHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIGFkanVzdCBvZmZzZXQgZm9yIHZpZXcgdG8gZ2V0IGluc2lkZSBib3VuZHNcclxuXHRfbGltaXRPZmZzZXQ6IGZ1bmN0aW9uIChvZmZzZXQsIGJvdW5kcykge1xyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIG9mZnNldDsgfVxyXG5cclxuXHRcdHZhciB2aWV3Qm91bmRzID0gdGhpcy5nZXRQaXhlbEJvdW5kcygpLFxyXG5cdFx0ICAgIG5ld0JvdW5kcyA9IG5ldyBMLkJvdW5kcyh2aWV3Qm91bmRzLm1pbi5hZGQob2Zmc2V0KSwgdmlld0JvdW5kcy5tYXguYWRkKG9mZnNldCkpO1xyXG5cclxuXHRcdHJldHVybiBvZmZzZXQuYWRkKHRoaXMuX2dldEJvdW5kc09mZnNldChuZXdCb3VuZHMsIGJvdW5kcykpO1xyXG5cdH0sXHJcblxyXG5cdC8vIHJldHVybnMgb2Zmc2V0IG5lZWRlZCBmb3IgcHhCb3VuZHMgdG8gZ2V0IGluc2lkZSBtYXhCb3VuZHMgYXQgYSBzcGVjaWZpZWQgem9vbVxyXG5cdF9nZXRCb3VuZHNPZmZzZXQ6IGZ1bmN0aW9uIChweEJvdW5kcywgbWF4Qm91bmRzLCB6b29tKSB7XHJcblx0XHR2YXIgcHJvamVjdGVkTWF4Qm91bmRzID0gTC5ib3VuZHMoXHJcblx0XHQgICAgICAgIHRoaXMucHJvamVjdChtYXhCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLFxyXG5cdFx0ICAgICAgICB0aGlzLnByb2plY3QobWF4Qm91bmRzLmdldFNvdXRoV2VzdCgpLCB6b29tKVxyXG5cdFx0ICAgICksXHJcblx0XHQgICAgbWluT2Zmc2V0ID0gcHJvamVjdGVkTWF4Qm91bmRzLm1pbi5zdWJ0cmFjdChweEJvdW5kcy5taW4pLFxyXG5cdFx0ICAgIG1heE9mZnNldCA9IHByb2plY3RlZE1heEJvdW5kcy5tYXguc3VidHJhY3QocHhCb3VuZHMubWF4KSxcclxuXHJcblx0XHQgICAgZHggPSB0aGlzLl9yZWJvdW5kKG1pbk9mZnNldC54LCAtbWF4T2Zmc2V0LngpLFxyXG5cdFx0ICAgIGR5ID0gdGhpcy5fcmVib3VuZChtaW5PZmZzZXQueSwgLW1heE9mZnNldC55KTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoZHgsIGR5KTtcclxuXHR9LFxyXG5cclxuXHRfcmVib3VuZDogZnVuY3Rpb24gKGxlZnQsIHJpZ2h0KSB7XHJcblx0XHRyZXR1cm4gbGVmdCArIHJpZ2h0ID4gMCA/XHJcblx0XHRcdE1hdGgucm91bmQobGVmdCAtIHJpZ2h0KSAvIDIgOlxyXG5cdFx0XHRNYXRoLm1heCgwLCBNYXRoLmNlaWwobGVmdCkpIC0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcihyaWdodCkpO1xyXG5cdH0sXHJcblxyXG5cdF9saW1pdFpvb206IGZ1bmN0aW9uICh6b29tKSB7XHJcblx0XHR2YXIgbWluID0gdGhpcy5nZXRNaW5ab29tKCksXHJcblx0XHQgICAgbWF4ID0gdGhpcy5nZXRNYXhab29tKCksXHJcblx0XHQgICAgc25hcCA9IEwuQnJvd3Nlci5hbnkzZCA/IHRoaXMub3B0aW9ucy56b29tU25hcCA6IDE7XHJcblx0XHRpZiAoc25hcCkge1xyXG5cdFx0XHR6b29tID0gTWF0aC5yb3VuZCh6b29tIC8gc25hcCkgKiBzbmFwO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIE1hdGgubWF4KG1pbiwgTWF0aC5taW4obWF4LCB6b29tKSk7XHJcblx0fSxcclxuXHJcblx0X29uUGFuVHJhbnNpdGlvblN0ZXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuZmlyZSgnbW92ZScpO1xyXG5cdH0sXHJcblxyXG5cdF9vblBhblRyYW5zaXRpb25FbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC1wYW4tYW5pbScpO1xyXG5cdFx0dGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0fSxcclxuXHJcblx0X3RyeUFuaW1hdGVkUGFuOiBmdW5jdGlvbiAoY2VudGVyLCBvcHRpb25zKSB7XHJcblx0XHQvLyBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG5ldyBhbmQgY3VycmVudCBjZW50ZXJzIGluIHBpeGVsc1xyXG5cdFx0dmFyIG9mZnNldCA9IHRoaXMuX2dldENlbnRlck9mZnNldChjZW50ZXIpLl9mbG9vcigpO1xyXG5cclxuXHRcdC8vIGRvbid0IGFuaW1hdGUgdG9vIGZhciB1bmxlc3MgYW5pbWF0ZTogdHJ1ZSBzcGVjaWZpZWQgaW4gb3B0aW9uc1xyXG5cdFx0aWYgKChvcHRpb25zICYmIG9wdGlvbnMuYW5pbWF0ZSkgIT09IHRydWUgJiYgIXRoaXMuZ2V0U2l6ZSgpLmNvbnRhaW5zKG9mZnNldCkpIHsgcmV0dXJuIGZhbHNlOyB9XHJcblxyXG5cdFx0dGhpcy5wYW5CeShvZmZzZXQsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHJldHVybiB0cnVlO1xyXG5cdH0sXHJcblxyXG5cdF9jcmVhdGVBbmltUHJveHk6IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHR2YXIgcHJveHkgPSB0aGlzLl9wcm94eSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXByb3h5IGxlYWZsZXQtem9vbS1hbmltYXRlZCcpO1xyXG5cdFx0dGhpcy5fcGFuZXMubWFwUGFuZS5hcHBlbmRDaGlsZChwcm94eSk7XHJcblxyXG5cdFx0dGhpcy5vbignem9vbWFuaW0nLCBmdW5jdGlvbiAoZSkge1xyXG5cdFx0XHR2YXIgcHJvcCA9IEwuRG9tVXRpbC5UUkFOU0ZPUk0sXHJcblx0XHRcdCAgICB0cmFuc2Zvcm0gPSBwcm94eS5zdHlsZVtwcm9wXTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0ocHJveHksIHRoaXMucHJvamVjdChlLmNlbnRlciwgZS56b29tKSwgdGhpcy5nZXRab29tU2NhbGUoZS56b29tLCAxKSk7XHJcblxyXG5cdFx0XHQvLyB3b3JrYXJvdW5kIGZvciBjYXNlIHdoZW4gdHJhbnNmb3JtIGlzIHRoZSBzYW1lIGFuZCBzbyB0cmFuc2l0aW9uZW5kIGV2ZW50IGlzIG5vdCBmaXJlZFxyXG5cdFx0XHRpZiAodHJhbnNmb3JtID09PSBwcm94eS5zdHlsZVtwcm9wXSAmJiB0aGlzLl9hbmltYXRpbmdab29tKSB7XHJcblx0XHRcdFx0dGhpcy5fb25ab29tVHJhbnNpdGlvbkVuZCgpO1xyXG5cdFx0XHR9XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLm9uKCdsb2FkIG1vdmVlbmQnLCBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHZhciBjID0gdGhpcy5nZXRDZW50ZXIoKSxcclxuXHRcdFx0ICAgIHogPSB0aGlzLmdldFpvb20oKTtcclxuXHRcdFx0TC5Eb21VdGlsLnNldFRyYW5zZm9ybShwcm94eSwgdGhpcy5wcm9qZWN0KGMsIHopLCB0aGlzLmdldFpvb21TY2FsZSh6LCAxKSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRfY2F0Y2hUcmFuc2l0aW9uRW5kOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20gJiYgZS5wcm9wZXJ0eU5hbWUuaW5kZXhPZigndHJhbnNmb3JtJykgPj0gMCkge1xyXG5cdFx0XHR0aGlzLl9vblpvb21UcmFuc2l0aW9uRW5kKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X25vdGhpbmdUb0FuaW1hdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhdGhpcy5fY29udGFpbmVyLmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcpLmxlbmd0aDtcclxuXHR9LFxyXG5cclxuXHRfdHJ5QW5pbWF0ZWRab29tOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuIHRydWU7IH1cclxuXHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIGRpc2FibGVkLCBub3Qgc3VwcG9ydGVkIG9yIHpvb20gZGlmZmVyZW5jZSBpcyB0b28gbGFyZ2VcclxuXHRcdGlmICghdGhpcy5fem9vbUFuaW1hdGVkIHx8IG9wdGlvbnMuYW5pbWF0ZSA9PT0gZmFsc2UgfHwgdGhpcy5fbm90aGluZ1RvQW5pbWF0ZSgpIHx8XHJcblx0XHQgICAgICAgIE1hdGguYWJzKHpvb20gLSB0aGlzLl96b29tKSA+IHRoaXMub3B0aW9ucy56b29tQW5pbWF0aW9uVGhyZXNob2xkKSB7IHJldHVybiBmYWxzZTsgfVxyXG5cclxuXHRcdC8vIG9mZnNldCBpcyB0aGUgcGl4ZWwgY29vcmRzIG9mIHRoZSB6b29tIG9yaWdpbiByZWxhdGl2ZSB0byB0aGUgY3VycmVudCBjZW50ZXJcclxuXHRcdHZhciBzY2FsZSA9IHRoaXMuZ2V0Wm9vbVNjYWxlKHpvb20pLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX2dldENlbnRlck9mZnNldChjZW50ZXIpLl9kaXZpZGVCeSgxIC0gMSAvIHNjYWxlKTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIHRoZSB6b29tIG9yaWdpbiBpc24ndCB3aXRoaW4gb25lIHNjcmVlbiBmcm9tIHRoZSBjdXJyZW50IGNlbnRlciwgdW5sZXNzIGZvcmNlZFxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gdHJ1ZSAmJiAhdGhpcy5nZXRTaXplKCkuY29udGFpbnMob2Zmc2V0KSkgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXNcclxuXHRcdFx0ICAgIC5fbW92ZVN0YXJ0KHRydWUpXHJcblx0XHRcdCAgICAuX2FuaW1hdGVab29tKGNlbnRlciwgem9vbSwgdHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHRyZXR1cm4gdHJ1ZTtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIHN0YXJ0QW5pbSwgbm9VcGRhdGUpIHtcclxuXHRcdGlmIChzdGFydEFuaW0pIHtcclxuXHRcdFx0dGhpcy5fYW5pbWF0aW5nWm9vbSA9IHRydWU7XHJcblxyXG5cdFx0XHQvLyByZW1lbWJlciB3aGF0IGNlbnRlci96b29tIHRvIHNldCBhZnRlciBhbmltYXRpb25cclxuXHRcdFx0dGhpcy5fYW5pbWF0ZVRvQ2VudGVyID0gY2VudGVyO1xyXG5cdFx0XHR0aGlzLl9hbmltYXRlVG9ab29tID0gem9vbTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC16b29tLWFuaW0nKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgem9vbWFuaW06IFpvb21BbmltRXZlbnRcclxuXHRcdC8vIEZpcmVkIG9uIGV2ZXJ5IGZyYW1lIG9mIGEgem9vbSBhbmltYXRpb25cclxuXHRcdHRoaXMuZmlyZSgnem9vbWFuaW0nLCB7XHJcblx0XHRcdGNlbnRlcjogY2VudGVyLFxyXG5cdFx0XHR6b29tOiB6b29tLFxyXG5cdFx0XHRub1VwZGF0ZTogbm9VcGRhdGVcclxuXHRcdH0pO1xyXG5cclxuXHRcdC8vIFdvcmsgYXJvdW5kIHdlYmtpdCBub3QgZmlyaW5nICd0cmFuc2l0aW9uZW5kJywgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzM2ODksIDI2OTNcclxuXHRcdHNldFRpbWVvdXQoTC5iaW5kKHRoaXMuX29uWm9vbVRyYW5zaXRpb25FbmQsIHRoaXMpLCAyNTApO1xyXG5cdH0sXHJcblxyXG5cdF9vblpvb21UcmFuc2l0aW9uRW5kOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcFBhbmUsICdsZWFmbGV0LXpvb20tYW5pbScpO1xyXG5cclxuXHRcdHRoaXMuX2FuaW1hdGluZ1pvb20gPSBmYWxzZTtcclxuXHJcblx0XHR0aGlzLl9tb3ZlKHRoaXMuX2FuaW1hdGVUb0NlbnRlciwgdGhpcy5fYW5pbWF0ZVRvWm9vbSk7XHJcblxyXG5cdFx0Ly8gVGhpcyBhbmltIGZyYW1lIHNob3VsZCBwcmV2ZW50IGFuIG9ic2N1cmUgaU9TIHdlYmtpdCB0aWxlIGxvYWRpbmcgcmFjZSBjb25kaXRpb24uXHJcblx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXMuX21vdmVFbmQodHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQHNlY3Rpb25cclxuXHJcbi8vIEBmYWN0b3J5IEwubWFwKGlkOiBTdHJpbmcsIG9wdGlvbnM/OiBNYXAgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGEgbWFwIG9iamVjdCBnaXZlbiB0aGUgRE9NIElEIG9mIGEgYDxkaXY+YCBlbGVtZW50XHJcbi8vIGFuZCBvcHRpb25hbGx5IGFuIG9iamVjdCBsaXRlcmFsIHdpdGggYE1hcCBvcHRpb25zYC5cclxuLy9cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubWFwKGVsOiBIVE1MRWxlbWVudCwgb3B0aW9ucz86IE1hcCBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYSBtYXAgb2JqZWN0IGdpdmVuIGFuIGluc3RhbmNlIG9mIGEgYDxkaXY+YCBIVE1MIGVsZW1lbnRcclxuLy8gYW5kIG9wdGlvbmFsbHkgYW4gb2JqZWN0IGxpdGVyYWwgd2l0aCBgTWFwIG9wdGlvbnNgLlxyXG5MLm1hcCA9IGZ1bmN0aW9uIChpZCwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5NYXAoaWQsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG5cbi8qXG4gKiBAY2xhc3MgTGF5ZXJcbiAqIEBpbmhlcml0cyBFdmVudGVkXG4gKiBAYWthIEwuTGF5ZXJcbiAqIEBha2EgSUxheWVyXG4gKlxuICogQSBzZXQgb2YgbWV0aG9kcyBmcm9tIHRoZSBMYXllciBiYXNlIGNsYXNzIHRoYXQgYWxsIExlYWZsZXQgbGF5ZXJzIHVzZS5cbiAqIEluaGVyaXRzIGFsbCBtZXRob2RzLCBvcHRpb25zIGFuZCBldmVudHMgZnJvbSBgTC5FdmVudGVkYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbGF5ZXIgPSBMLk1hcmtlcihsYXRsbmcpLmFkZFRvKG1hcCk7XG4gKiBsYXllci5hZGRUbyhtYXApO1xuICogbGF5ZXIucmVtb3ZlKCk7XG4gKiBgYGBcbiAqXG4gKiBAZXZlbnQgYWRkOiBFdmVudFxuICogRmlyZWQgYWZ0ZXIgdGhlIGxheWVyIGlzIGFkZGVkIHRvIGEgbWFwXG4gKlxuICogQGV2ZW50IHJlbW92ZTogRXZlbnRcbiAqIEZpcmVkIGFmdGVyIHRoZSBsYXllciBpcyByZW1vdmVkIGZyb20gYSBtYXBcbiAqL1xuXG5cbkwuTGF5ZXIgPSBMLkV2ZW50ZWQuZXh0ZW5kKHtcblxuXHQvLyBDbGFzc2VzIGV4dGVuZGluZyBgTC5MYXllcmAgd2lsbCBpbmhlcml0IHRoZSBmb2xsb3dpbmcgb3B0aW9uczpcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gcGFuZTogU3RyaW5nID0gJ292ZXJsYXlQYW5lJ1xuXHRcdC8vIEJ5IGRlZmF1bHQgdGhlIGxheWVyIHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1hcCdzIFtvdmVybGF5IHBhbmVdKCNtYXAtb3ZlcmxheXBhbmUpLiBPdmVycmlkaW5nIHRoaXMgb3B0aW9uIHdpbGwgY2F1c2UgdGhlIGxheWVyIHRvIGJlIHBsYWNlZCBvbiBhbm90aGVyIHBhbmUgYnkgZGVmYXVsdC5cblx0XHRwYW5lOiAnb3ZlcmxheVBhbmUnLFxuXHRcdG5vbkJ1YmJsaW5nRXZlbnRzOiBbXSwgIC8vIEFycmF5IG9mIGV2ZW50cyB0aGF0IHNob3VsZCBub3QgYmUgYnViYmxlZCB0byBET00gcGFyZW50cyAobGlrZSB0aGUgbWFwKSxcblxuXHRcdC8vIEBvcHRpb24gYXR0cmlidXRpb246IFN0cmluZyA9IG51bGxcblx0XHQvLyBTdHJpbmcgdG8gYmUgc2hvd24gaW4gdGhlIGF0dHJpYnV0aW9uIGNvbnRyb2wsIGRlc2NyaWJlcyB0aGUgbGF5ZXIgZGF0YSwgZS5nLiBcIsKpIE1hcGJveFwiLlxuXHRcdGF0dHJpYnV0aW9uOiBudWxsXG5cdH0sXG5cblx0LyogQHNlY3Rpb25cblx0ICogQ2xhc3NlcyBleHRlbmRpbmcgYEwuTGF5ZXJgIHdpbGwgaW5oZXJpdCB0aGUgZm9sbG93aW5nIG1ldGhvZHM6XG5cdCAqXG5cdCAqIEBtZXRob2QgYWRkVG8obWFwOiBNYXApOiB0aGlzXG5cdCAqIEFkZHMgdGhlIGxheWVyIHRvIHRoZSBnaXZlbiBtYXBcblx0ICovXG5cdGFkZFRvOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0bWFwLmFkZExheWVyKHRoaXMpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVtb3ZlOiB0aGlzXG5cdC8vIFJlbW92ZXMgdGhlIGxheWVyIGZyb20gdGhlIG1hcCBpdCBpcyBjdXJyZW50bHkgYWN0aXZlIG9uLlxuXHRyZW1vdmU6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5yZW1vdmVGcm9tKHRoaXMuX21hcCB8fCB0aGlzLl9tYXBUb0FkZCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCByZW1vdmVGcm9tKG1hcDogTWFwKTogdGhpc1xuXHQvLyBSZW1vdmVzIHRoZSBsYXllciBmcm9tIHRoZSBnaXZlbiBtYXBcblx0cmVtb3ZlRnJvbTogZnVuY3Rpb24gKG9iaikge1xuXHRcdGlmIChvYmopIHtcblx0XHRcdG9iai5yZW1vdmVMYXllcih0aGlzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRQYW5lKG5hbWU/IDogU3RyaW5nKTogSFRNTEVsZW1lbnRcblx0Ly8gUmV0dXJucyB0aGUgYEhUTUxFbGVtZW50YCByZXByZXNlbnRpbmcgdGhlIG5hbWVkIHBhbmUgb24gdGhlIG1hcC4gSWYgYG5hbWVgIGlzIG9taXR0ZWQsIHJldHVybnMgdGhlIHBhbmUgZm9yIHRoaXMgbGF5ZXIuXG5cdGdldFBhbmU6IGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0cmV0dXJuIHRoaXMuX21hcC5nZXRQYW5lKG5hbWUgPyAodGhpcy5vcHRpb25zW25hbWVdIHx8IG5hbWUpIDogdGhpcy5vcHRpb25zLnBhbmUpO1xuXHR9LFxuXG5cdGFkZEludGVyYWN0aXZlVGFyZ2V0OiBmdW5jdGlvbiAodGFyZ2V0RWwpIHtcblx0XHR0aGlzLl9tYXAuX3RhcmdldHNbTC5zdGFtcCh0YXJnZXRFbCldID0gdGhpcztcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRyZW1vdmVJbnRlcmFjdGl2ZVRhcmdldDogZnVuY3Rpb24gKHRhcmdldEVsKSB7XG5cdFx0ZGVsZXRlIHRoaXMuX21hcC5fdGFyZ2V0c1tMLnN0YW1wKHRhcmdldEVsKV07XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRBdHRyaWJ1dGlvbjogU3RyaW5nXG5cdC8vIFVzZWQgYnkgdGhlIGBhdHRyaWJ1dGlvbiBjb250cm9sYCwgcmV0dXJucyB0aGUgW2F0dHJpYnV0aW9uIG9wdGlvbl0oI2dyaWRsYXllci1hdHRyaWJ1dGlvbikuXG5cdGdldEF0dHJpYnV0aW9uOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5hdHRyaWJ1dGlvbjtcblx0fSxcblxuXHRfbGF5ZXJBZGQ6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIG1hcCA9IGUudGFyZ2V0O1xuXG5cdFx0Ly8gY2hlY2sgaW4gY2FzZSBsYXllciBnZXRzIGFkZGVkIGFuZCB0aGVuIHJlbW92ZWQgYmVmb3JlIHRoZSBtYXAgaXMgcmVhZHlcblx0XHRpZiAoIW1hcC5oYXNMYXllcih0aGlzKSkgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX21hcCA9IG1hcDtcblx0XHR0aGlzLl96b29tQW5pbWF0ZWQgPSBtYXAuX3pvb21BbmltYXRlZDtcblxuXHRcdGlmICh0aGlzLmdldEV2ZW50cykge1xuXHRcdFx0dmFyIGV2ZW50cyA9IHRoaXMuZ2V0RXZlbnRzKCk7XG5cdFx0XHRtYXAub24oZXZlbnRzLCB0aGlzKTtcblx0XHRcdHRoaXMub25jZSgncmVtb3ZlJywgZnVuY3Rpb24gKCkge1xuXHRcdFx0XHRtYXAub2ZmKGV2ZW50cywgdGhpcyk7XG5cdFx0XHR9LCB0aGlzKTtcblx0XHR9XG5cblx0XHR0aGlzLm9uQWRkKG1hcCk7XG5cblx0XHRpZiAodGhpcy5nZXRBdHRyaWJ1dGlvbiAmJiBtYXAuYXR0cmlidXRpb25Db250cm9sKSB7XG5cdFx0XHRtYXAuYXR0cmlidXRpb25Db250cm9sLmFkZEF0dHJpYnV0aW9uKHRoaXMuZ2V0QXR0cmlidXRpb24oKSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5maXJlKCdhZGQnKTtcblx0XHRtYXAuZmlyZSgnbGF5ZXJhZGQnLCB7bGF5ZXI6IHRoaXN9KTtcblx0fVxufSk7XG5cbi8qIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXG4gKiBAdW5pbmhlcml0YWJsZVxuICpcbiAqIEV2ZXJ5IGxheWVyIHNob3VsZCBleHRlbmQgZnJvbSBgTC5MYXllcmAgYW5kIChyZS0paW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgbWV0aG9kcy5cbiAqXG4gKiBAbWV0aG9kIG9uQWRkKG1hcDogTWFwKTogdGhpc1xuICogU2hvdWxkIGNvbnRhaW4gY29kZSB0aGF0IGNyZWF0ZXMgRE9NIGVsZW1lbnRzIGZvciB0aGUgbGF5ZXIsIGFkZHMgdGhlbSB0byBgbWFwIHBhbmVzYCB3aGVyZSB0aGV5IHNob3VsZCBiZWxvbmcgYW5kIHB1dHMgbGlzdGVuZXJzIG9uIHJlbGV2YW50IG1hcCBldmVudHMuIENhbGxlZCBvbiBbYG1hcC5hZGRMYXllcihsYXllcilgXSgjbWFwLWFkZGxheWVyKS5cbiAqXG4gKiBAbWV0aG9kIG9uUmVtb3ZlKG1hcDogTWFwKTogdGhpc1xuICogU2hvdWxkIGNvbnRhaW4gYWxsIGNsZWFuIHVwIGNvZGUgdGhhdCByZW1vdmVzIHRoZSBsYXllcidzIGVsZW1lbnRzIGZyb20gdGhlIERPTSBhbmQgcmVtb3ZlcyBsaXN0ZW5lcnMgcHJldmlvdXNseSBhZGRlZCBpbiBbYG9uQWRkYF0oI2xheWVyLW9uYWRkKS4gQ2FsbGVkIG9uIFtgbWFwLnJlbW92ZUxheWVyKGxheWVyKWBdKCNtYXAtcmVtb3ZlbGF5ZXIpLlxuICpcbiAqIEBtZXRob2QgZ2V0RXZlbnRzKCk6IE9iamVjdFxuICogVGhpcyBvcHRpb25hbCBtZXRob2Qgc2hvdWxkIHJldHVybiBhbiBvYmplY3QgbGlrZSBgeyB2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0IH1gIGZvciBbYGFkZEV2ZW50TGlzdGVuZXJgXSgjZXZlbnRlZC1hZGRldmVudGxpc3RlbmVyKS4gVGhlIGV2ZW50IGhhbmRsZXJzIGluIHRoaXMgb2JqZWN0IHdpbGwgYmUgYXV0b21hdGljYWxseSBhZGRlZCBhbmQgcmVtb3ZlZCBmcm9tIHRoZSBtYXAgd2l0aCB5b3VyIGxheWVyLlxuICpcbiAqIEBtZXRob2QgZ2V0QXR0cmlidXRpb24oKTogU3RyaW5nXG4gKiBUaGlzIG9wdGlvbmFsIG1ldGhvZCBzaG91bGQgcmV0dXJuIGEgc3RyaW5nIGNvbnRhaW5pbmcgSFRNTCB0byBiZSBzaG93biBvbiB0aGUgYEF0dHJpYnV0aW9uIGNvbnRyb2xgIHdoZW5ldmVyIHRoZSBsYXllciBpcyB2aXNpYmxlLlxuICpcbiAqIEBtZXRob2QgYmVmb3JlQWRkKG1hcDogTWFwKTogdGhpc1xuICogT3B0aW9uYWwgbWV0aG9kLiBDYWxsZWQgb24gW2BtYXAuYWRkTGF5ZXIobGF5ZXIpYF0oI21hcC1hZGRsYXllciksIGJlZm9yZSB0aGUgbGF5ZXIgaXMgYWRkZWQgdG8gdGhlIG1hcCwgYmVmb3JlIGV2ZW50cyBhcmUgaW5pdGlhbGl6ZWQsIHdpdGhvdXQgd2FpdGluZyB1bnRpbCB0aGUgbWFwIGlzIGluIGEgdXNhYmxlIHN0YXRlLiBVc2UgZm9yIGVhcmx5IGluaXRpYWxpemF0aW9uIG9ubHkuXG4gKi9cblxuXG4vKiBAbmFtZXNwYWNlIE1hcFxuICogQHNlY3Rpb24gTGF5ZXIgZXZlbnRzXG4gKlxuICogQGV2ZW50IGxheWVyYWRkOiBMYXllckV2ZW50XG4gKiBGaXJlZCB3aGVuIGEgbmV3IGxheWVyIGlzIGFkZGVkIHRvIHRoZSBtYXAuXG4gKlxuICogQGV2ZW50IGxheWVycmVtb3ZlOiBMYXllckV2ZW50XG4gKiBGaXJlZCB3aGVuIHNvbWUgbGF5ZXIgaXMgcmVtb3ZlZCBmcm9tIHRoZSBtYXBcbiAqXG4gKiBAc2VjdGlvbiBNZXRob2RzIGZvciBMYXllcnMgYW5kIENvbnRyb2xzXG4gKi9cbkwuTWFwLmluY2x1ZGUoe1xuXHQvLyBAbWV0aG9kIGFkZExheWVyKGxheWVyOiBMYXllcik6IHRoaXNcblx0Ly8gQWRkcyB0aGUgZ2l2ZW4gbGF5ZXIgdG8gdGhlIG1hcFxuXHRhZGRMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGlkID0gTC5zdGFtcChsYXllcik7XG5cdFx0aWYgKHRoaXMuX2xheWVyc1tpZF0pIHsgcmV0dXJuIHRoaXM7IH1cblx0XHR0aGlzLl9sYXllcnNbaWRdID0gbGF5ZXI7XG5cblx0XHRsYXllci5fbWFwVG9BZGQgPSB0aGlzO1xuXG5cdFx0aWYgKGxheWVyLmJlZm9yZUFkZCkge1xuXHRcdFx0bGF5ZXIuYmVmb3JlQWRkKHRoaXMpO1xuXHRcdH1cblxuXHRcdHRoaXMud2hlblJlYWR5KGxheWVyLl9sYXllckFkZCwgbGF5ZXIpO1xuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXG5cdC8vIFJlbW92ZXMgdGhlIGdpdmVuIGxheWVyIGZyb20gdGhlIG1hcC5cblx0cmVtb3ZlTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBpZCA9IEwuc3RhbXAobGF5ZXIpO1xuXG5cdFx0aWYgKCF0aGlzLl9sYXllcnNbaWRdKSB7IHJldHVybiB0aGlzOyB9XG5cblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XG5cdFx0XHRsYXllci5vblJlbW92ZSh0aGlzKTtcblx0XHR9XG5cblx0XHRpZiAobGF5ZXIuZ2V0QXR0cmlidXRpb24gJiYgdGhpcy5hdHRyaWJ1dGlvbkNvbnRyb2wpIHtcblx0XHRcdHRoaXMuYXR0cmlidXRpb25Db250cm9sLnJlbW92ZUF0dHJpYnV0aW9uKGxheWVyLmdldEF0dHJpYnV0aW9uKCkpO1xuXHRcdH1cblxuXHRcdGRlbGV0ZSB0aGlzLl9sYXllcnNbaWRdO1xuXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCkge1xuXHRcdFx0dGhpcy5maXJlKCdsYXllcnJlbW92ZScsIHtsYXllcjogbGF5ZXJ9KTtcblx0XHRcdGxheWVyLmZpcmUoJ3JlbW92ZScpO1xuXHRcdH1cblxuXHRcdGxheWVyLl9tYXAgPSBsYXllci5fbWFwVG9BZGQgPSBudWxsO1xuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBoYXNMYXllcihsYXllcjogTGF5ZXIpOiBCb29sZWFuXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBnaXZlbiBsYXllciBpcyBjdXJyZW50bHkgYWRkZWQgdG8gdGhlIG1hcFxuXHRoYXNMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0cmV0dXJuICEhbGF5ZXIgJiYgKEwuc3RhbXAobGF5ZXIpIGluIHRoaXMuX2xheWVycyk7XG5cdH0sXG5cblx0LyogQG1ldGhvZCBlYWNoTGF5ZXIoZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xuXHQgKiBJdGVyYXRlcyBvdmVyIHRoZSBsYXllcnMgb2YgdGhlIG1hcCwgb3B0aW9uYWxseSBzcGVjaWZ5aW5nIGNvbnRleHQgb2YgdGhlIGl0ZXJhdG9yIGZ1bmN0aW9uLlxuXHQgKiBgYGBcblx0ICogbWFwLmVhY2hMYXllcihmdW5jdGlvbihsYXllcil7XG5cdCAqICAgICBsYXllci5iaW5kUG9wdXAoJ0hlbGxvJyk7XG5cdCAqIH0pO1xuXHQgKiBgYGBcblx0ICovXG5cdGVhY2hMYXllcjogZnVuY3Rpb24gKG1ldGhvZCwgY29udGV4dCkge1xuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHRtZXRob2QuY2FsbChjb250ZXh0LCB0aGlzLl9sYXllcnNbaV0pO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRfYWRkTGF5ZXJzOiBmdW5jdGlvbiAobGF5ZXJzKSB7XG5cdFx0bGF5ZXJzID0gbGF5ZXJzID8gKEwuVXRpbC5pc0FycmF5KGxheWVycykgPyBsYXllcnMgOiBbbGF5ZXJzXSkgOiBbXTtcblxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsYXllcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdHRoaXMuYWRkTGF5ZXIobGF5ZXJzW2ldKTtcblx0XHR9XG5cdH0sXG5cblx0X2FkZFpvb21MaW1pdDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0aWYgKGlzTmFOKGxheWVyLm9wdGlvbnMubWF4Wm9vbSkgfHwgIWlzTmFOKGxheWVyLm9wdGlvbnMubWluWm9vbSkpIHtcblx0XHRcdHRoaXMuX3pvb21Cb3VuZExheWVyc1tMLnN0YW1wKGxheWVyKV0gPSBsYXllcjtcblx0XHRcdHRoaXMuX3VwZGF0ZVpvb21MZXZlbHMoKTtcblx0XHR9XG5cdH0sXG5cblx0X3JlbW92ZVpvb21MaW1pdDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGlkID0gTC5zdGFtcChsYXllcik7XG5cblx0XHRpZiAodGhpcy5fem9vbUJvdW5kTGF5ZXJzW2lkXSkge1xuXHRcdFx0ZGVsZXRlIHRoaXMuX3pvb21Cb3VuZExheWVyc1tpZF07XG5cdFx0XHR0aGlzLl91cGRhdGVab29tTGV2ZWxzKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVab29tTGV2ZWxzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIG1pblpvb20gPSBJbmZpbml0eSxcblx0XHQgICAgbWF4Wm9vbSA9IC1JbmZpbml0eSxcblx0XHQgICAgb2xkWm9vbVNwYW4gPSB0aGlzLl9nZXRab29tU3BhbigpO1xuXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl96b29tQm91bmRMYXllcnMpIHtcblx0XHRcdHZhciBvcHRpb25zID0gdGhpcy5fem9vbUJvdW5kTGF5ZXJzW2ldLm9wdGlvbnM7XG5cblx0XHRcdG1pblpvb20gPSBvcHRpb25zLm1pblpvb20gPT09IHVuZGVmaW5lZCA/IG1pblpvb20gOiBNYXRoLm1pbihtaW5ab29tLCBvcHRpb25zLm1pblpvb20pO1xuXHRcdFx0bWF4Wm9vbSA9IG9wdGlvbnMubWF4Wm9vbSA9PT0gdW5kZWZpbmVkID8gbWF4Wm9vbSA6IE1hdGgubWF4KG1heFpvb20sIG9wdGlvbnMubWF4Wm9vbSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fbGF5ZXJzTWF4Wm9vbSA9IG1heFpvb20gPT09IC1JbmZpbml0eSA/IHVuZGVmaW5lZCA6IG1heFpvb207XG5cdFx0dGhpcy5fbGF5ZXJzTWluWm9vbSA9IG1pblpvb20gPT09IEluZmluaXR5ID8gdW5kZWZpbmVkIDogbWluWm9vbTtcblxuXHRcdC8vIEBzZWN0aW9uIE1hcCBzdGF0ZSBjaGFuZ2UgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IHpvb21sZXZlbHNjaGFuZ2U6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbnVtYmVyIG9mIHpvb21sZXZlbHMgb24gdGhlIG1hcCBpcyBjaGFuZ2VkIGR1ZVxuXHRcdC8vIHRvIGFkZGluZyBvciByZW1vdmluZyBhIGxheWVyLlxuXHRcdGlmIChvbGRab29tU3BhbiAhPT0gdGhpcy5fZ2V0Wm9vbVNwYW4oKSkge1xuXHRcdFx0dGhpcy5maXJlKCd6b29tbGV2ZWxzY2hhbmdlJyk7XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5tYXhab29tID09PSB1bmRlZmluZWQgJiYgdGhpcy5fbGF5ZXJzTWF4Wm9vbSAmJiB0aGlzLmdldFpvb20oKSA+IHRoaXMuX2xheWVyc01heFpvb20pIHtcblx0XHRcdHRoaXMuc2V0Wm9vbSh0aGlzLl9sYXllcnNNYXhab29tKTtcblx0XHR9XG5cdFx0aWYgKHRoaXMub3B0aW9ucy5taW5ab29tID09PSB1bmRlZmluZWQgJiYgdGhpcy5fbGF5ZXJzTWluWm9vbSAmJiB0aGlzLmdldFpvb20oKSA8IHRoaXMuX2xheWVyc01pblpvb20pIHtcblx0XHRcdHRoaXMuc2V0Wm9vbSh0aGlzLl9sYXllcnNNaW5ab29tKTtcblx0XHR9XG5cdH1cbn0pO1xuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBEb21FdmVudFxyXG4gKiBVdGlsaXR5IGZ1bmN0aW9ucyB0byB3b3JrIHdpdGggdGhlIFtET00gZXZlbnRzXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvRXZlbnQpLCB1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseS5cclxuICovXHJcblxyXG4vLyBJbnNwaXJlZCBieSBKb2huIFJlc2lnLCBEZWFuIEVkd2FyZHMgYW5kIFlVSSBhZGRFdmVudCBpbXBsZW1lbnRhdGlvbnMuXHJcblxyXG5cclxuXHJcbnZhciBldmVudHNLZXkgPSAnX2xlYWZsZXRfZXZlbnRzJztcclxuXHJcbkwuRG9tRXZlbnQgPSB7XHJcblxyXG5cdC8vIEBmdW5jdGlvbiBvbihlbDogSFRNTEVsZW1lbnQsIHR5cGVzOiBTdHJpbmcsIGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcclxuXHQvLyBBZGRzIGEgbGlzdGVuZXIgZnVuY3Rpb24gKGBmbmApIHRvIGEgcGFydGljdWxhciBET00gZXZlbnQgdHlwZSBvZiB0aGVcclxuXHQvLyBlbGVtZW50IGBlbGAuIFlvdSBjYW4gb3B0aW9uYWxseSBzcGVjaWZ5IHRoZSBjb250ZXh0IG9mIHRoZSBsaXN0ZW5lclxyXG5cdC8vIChvYmplY3QgdGhlIGB0aGlzYCBrZXl3b3JkIHdpbGwgcG9pbnQgdG8pLiBZb3UgY2FuIGFsc28gcGFzcyBzZXZlcmFsXHJcblx0Ly8gc3BhY2Utc2VwYXJhdGVkIHR5cGVzIChlLmcuIGAnY2xpY2sgZGJsY2xpY2snYCkuXHJcblxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBmdW5jdGlvbiBvbihlbDogSFRNTEVsZW1lbnQsIGV2ZW50TWFwOiBPYmplY3QsIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gQWRkcyBhIHNldCBvZiB0eXBlL2xpc3RlbmVyIHBhaXJzLCBlLmcuIGB7Y2xpY2s6IG9uQ2xpY2ssIG1vdXNlbW92ZTogb25Nb3VzZU1vdmV9YFxyXG5cdG9uOiBmdW5jdGlvbiAob2JqLCB0eXBlcywgZm4sIGNvbnRleHQpIHtcclxuXHJcblx0XHRpZiAodHlwZW9mIHR5cGVzID09PSAnb2JqZWN0Jykge1xyXG5cdFx0XHRmb3IgKHZhciB0eXBlIGluIHR5cGVzKSB7XHJcblx0XHRcdFx0dGhpcy5fb24ob2JqLCB0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0eXBlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0eXBlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuX29uKG9iaiwgdHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBvZmYoZWw6IEhUTUxFbGVtZW50LCB0eXBlczogU3RyaW5nLCBmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyBhIHByZXZpb3VzbHkgYWRkZWQgbGlzdGVuZXIgZnVuY3Rpb24uIElmIG5vIGZ1bmN0aW9uIGlzIHNwZWNpZmllZCxcclxuXHQvLyBpdCB3aWxsIHJlbW92ZSBhbGwgdGhlIGxpc3RlbmVycyBvZiB0aGF0IHBhcnRpY3VsYXIgRE9NIGV2ZW50IGZyb20gdGhlIGVsZW1lbnQuXHJcblx0Ly8gTm90ZSB0aGF0IGlmIHlvdSBwYXNzZWQgYSBjdXN0b20gY29udGV4dCB0byBvbiwgeW91IG11c3QgcGFzcyB0aGUgc2FtZVxyXG5cdC8vIGNvbnRleHQgdG8gYG9mZmAgaW4gb3JkZXIgdG8gcmVtb3ZlIHRoZSBsaXN0ZW5lci5cclxuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQGZ1bmN0aW9uIG9mZihlbDogSFRNTEVsZW1lbnQsIGV2ZW50TWFwOiBPYmplY3QsIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyBhIHNldCBvZiB0eXBlL2xpc3RlbmVyIHBhaXJzLCBlLmcuIGB7Y2xpY2s6IG9uQ2xpY2ssIG1vdXNlbW92ZTogb25Nb3VzZU1vdmV9YFxyXG5cdG9mZjogZnVuY3Rpb24gKG9iaiwgdHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdHRoaXMuX29mZihvYmosIHR5cGUsIHR5cGVzW3R5cGVdLCBmbik7XHJcblx0XHRcdH1cclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHR5cGVzID0gTC5VdGlsLnNwbGl0V29yZHModHlwZXMpO1xyXG5cclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHR5cGVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dGhpcy5fb2ZmKG9iaiwgdHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9vbjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHRcdHZhciBpZCA9IHR5cGUgKyBMLnN0YW1wKGZuKSArIChjb250ZXh0ID8gJ18nICsgTC5zdGFtcChjb250ZXh0KSA6ICcnKTtcclxuXHJcblx0XHRpZiAob2JqW2V2ZW50c0tleV0gJiYgb2JqW2V2ZW50c0tleV1baWRdKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0dmFyIGhhbmRsZXIgPSBmdW5jdGlvbiAoZSkge1xyXG5cdFx0XHRyZXR1cm4gZm4uY2FsbChjb250ZXh0IHx8IG9iaiwgZSB8fCB3aW5kb3cuZXZlbnQpO1xyXG5cdFx0fTtcclxuXHJcblx0XHR2YXIgb3JpZ2luYWxIYW5kbGVyID0gaGFuZGxlcjtcclxuXHJcblx0XHRpZiAoTC5Ccm93c2VyLnBvaW50ZXIgJiYgdHlwZS5pbmRleE9mKCd0b3VjaCcpID09PSAwKSB7XHJcblx0XHRcdHRoaXMuYWRkUG9pbnRlckxpc3RlbmVyKG9iaiwgdHlwZSwgaGFuZGxlciwgaWQpO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoTC5Ccm93c2VyLnRvdWNoICYmICh0eXBlID09PSAnZGJsY2xpY2snKSAmJiB0aGlzLmFkZERvdWJsZVRhcExpc3RlbmVyICYmXHJcblx0XHQgICAgICAgICAgICEoTC5Ccm93c2VyLnBvaW50ZXIgJiYgTC5Ccm93c2VyLmNocm9tZSkpIHtcclxuXHRcdFx0Ly8gQ2hyb21lID41NSBkb2VzIG5vdCBuZWVkIHRoZSBzeW50aGV0aWMgZGJsY2xpY2tzIGZyb20gYWRkRG91YmxlVGFwTGlzdGVuZXJcclxuXHRcdFx0Ly8gU2VlICM1MTgwXHJcblx0XHRcdHRoaXMuYWRkRG91YmxlVGFwTGlzdGVuZXIob2JqLCBoYW5kbGVyLCBpZCk7XHJcblxyXG5cdFx0fSBlbHNlIGlmICgnYWRkRXZlbnRMaXN0ZW5lcicgaW4gb2JqKSB7XHJcblxyXG5cdFx0XHRpZiAodHlwZSA9PT0gJ21vdXNld2hlZWwnKSB7XHJcblx0XHRcdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIoJ29ud2hlZWwnIGluIG9iaiA/ICd3aGVlbCcgOiAnbW91c2V3aGVlbCcsIGhhbmRsZXIsIGZhbHNlKTtcclxuXHJcblx0XHRcdH0gZWxzZSBpZiAoKHR5cGUgPT09ICdtb3VzZWVudGVyJykgfHwgKHR5cGUgPT09ICdtb3VzZWxlYXZlJykpIHtcclxuXHRcdFx0XHRoYW5kbGVyID0gZnVuY3Rpb24gKGUpIHtcclxuXHRcdFx0XHRcdGUgPSBlIHx8IHdpbmRvdy5ldmVudDtcclxuXHRcdFx0XHRcdGlmIChMLkRvbUV2ZW50Ll9pc0V4dGVybmFsVGFyZ2V0KG9iaiwgZSkpIHtcclxuXHRcdFx0XHRcdFx0b3JpZ2luYWxIYW5kbGVyKGUpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH07XHJcblx0XHRcdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodHlwZSA9PT0gJ21vdXNlZW50ZXInID8gJ21vdXNlb3ZlcicgOiAnbW91c2VvdXQnLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGlmICh0eXBlID09PSAnY2xpY2snICYmIEwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdFx0XHRoYW5kbGVyID0gZnVuY3Rpb24gKGUpIHtcclxuXHRcdFx0XHRcdFx0cmV0dXJuIEwuRG9tRXZlbnQuX2ZpbHRlckNsaWNrKGUsIG9yaWdpbmFsSGFuZGxlcik7XHJcblx0XHRcdFx0XHR9O1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcih0eXBlLCBoYW5kbGVyLCBmYWxzZSk7XHJcblx0XHRcdH1cclxuXHJcblx0XHR9IGVsc2UgaWYgKCdhdHRhY2hFdmVudCcgaW4gb2JqKSB7XHJcblx0XHRcdG9iai5hdHRhY2hFdmVudCgnb24nICsgdHlwZSwgaGFuZGxlcik7XHJcblx0XHR9XHJcblxyXG5cdFx0b2JqW2V2ZW50c0tleV0gPSBvYmpbZXZlbnRzS2V5XSB8fCB7fTtcclxuXHRcdG9ialtldmVudHNLZXldW2lkXSA9IGhhbmRsZXI7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X29mZjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHJcblx0XHR2YXIgaWQgPSB0eXBlICsgTC5zdGFtcChmbikgKyAoY29udGV4dCA/ICdfJyArIEwuc3RhbXAoY29udGV4dCkgOiAnJyksXHJcblx0XHQgICAgaGFuZGxlciA9IG9ialtldmVudHNLZXldICYmIG9ialtldmVudHNLZXldW2lkXTtcclxuXHJcblx0XHRpZiAoIWhhbmRsZXIpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRpZiAoTC5Ccm93c2VyLnBvaW50ZXIgJiYgdHlwZS5pbmRleE9mKCd0b3VjaCcpID09PSAwKSB7XHJcblx0XHRcdHRoaXMucmVtb3ZlUG9pbnRlckxpc3RlbmVyKG9iaiwgdHlwZSwgaWQpO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoTC5Ccm93c2VyLnRvdWNoICYmICh0eXBlID09PSAnZGJsY2xpY2snKSAmJiB0aGlzLnJlbW92ZURvdWJsZVRhcExpc3RlbmVyKSB7XHJcblx0XHRcdHRoaXMucmVtb3ZlRG91YmxlVGFwTGlzdGVuZXIob2JqLCBpZCk7XHJcblxyXG5cdFx0fSBlbHNlIGlmICgncmVtb3ZlRXZlbnRMaXN0ZW5lcicgaW4gb2JqKSB7XHJcblxyXG5cdFx0XHRpZiAodHlwZSA9PT0gJ21vdXNld2hlZWwnKSB7XHJcblx0XHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ29ud2hlZWwnIGluIG9iaiA/ICd3aGVlbCcgOiAnbW91c2V3aGVlbCcsIGhhbmRsZXIsIGZhbHNlKTtcclxuXHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIoXHJcblx0XHRcdFx0XHR0eXBlID09PSAnbW91c2VlbnRlcicgPyAnbW91c2VvdmVyJyA6XHJcblx0XHRcdFx0XHR0eXBlID09PSAnbW91c2VsZWF2ZScgPyAnbW91c2VvdXQnIDogdHlwZSwgaGFuZGxlciwgZmFsc2UpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0fSBlbHNlIGlmICgnZGV0YWNoRXZlbnQnIGluIG9iaikge1xyXG5cdFx0XHRvYmouZGV0YWNoRXZlbnQoJ29uJyArIHR5cGUsIGhhbmRsZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdG9ialtldmVudHNLZXldW2lkXSA9IG51bGw7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHN0b3BQcm9wYWdhdGlvbihldjogRE9NRXZlbnQpOiB0aGlzXHJcblx0Ly8gU3RvcCB0aGUgZ2l2ZW4gZXZlbnQgZnJvbSBwcm9wYWdhdGlvbiB0byBwYXJlbnQgZWxlbWVudHMuIFVzZWQgaW5zaWRlIHRoZSBsaXN0ZW5lciBmdW5jdGlvbnM6XHJcblx0Ly8gYGBganNcclxuXHQvLyBMLkRvbUV2ZW50Lm9uKGRpdiwgJ2NsaWNrJywgZnVuY3Rpb24gKGV2KSB7XHJcblx0Ly8gXHRMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbihldik7XHJcblx0Ly8gfSk7XHJcblx0Ly8gYGBgXHJcblx0c3RvcFByb3BhZ2F0aW9uOiBmdW5jdGlvbiAoZSkge1xyXG5cclxuXHRcdGlmIChlLnN0b3BQcm9wYWdhdGlvbikge1xyXG5cdFx0XHRlLnN0b3BQcm9wYWdhdGlvbigpO1xyXG5cdFx0fSBlbHNlIGlmIChlLm9yaWdpbmFsRXZlbnQpIHsgIC8vIEluIGNhc2Ugb2YgTGVhZmxldCBldmVudC5cclxuXHRcdFx0ZS5vcmlnaW5hbEV2ZW50Ll9zdG9wcGVkID0gdHJ1ZTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGUuY2FuY2VsQnViYmxlID0gdHJ1ZTtcclxuXHRcdH1cclxuXHRcdEwuRG9tRXZlbnQuX3NraXBwZWQoZSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGRpc2FibGVTY3JvbGxQcm9wYWdhdGlvbihlbDogSFRNTEVsZW1lbnQpOiB0aGlzXHJcblx0Ly8gQWRkcyBgc3RvcFByb3BhZ2F0aW9uYCB0byB0aGUgZWxlbWVudCdzIGAnbW91c2V3aGVlbCdgIGV2ZW50cyAocGx1cyBicm93c2VyIHZhcmlhbnRzKS5cclxuXHRkaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb246IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnQub24oZWwsICdtb3VzZXdoZWVsJywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBkaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbihlbDogSFRNTEVsZW1lbnQpOiB0aGlzXHJcblx0Ly8gQWRkcyBgc3RvcFByb3BhZ2F0aW9uYCB0byB0aGUgZWxlbWVudCdzIGAnY2xpY2snYCwgYCdkb3VibGVjbGljaydgLFxyXG5cdC8vIGAnbW91c2Vkb3duJ2AgYW5kIGAndG91Y2hzdGFydCdgIGV2ZW50cyAocGx1cyBicm93c2VyIHZhcmlhbnRzKS5cclxuXHRkaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbjogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHR2YXIgc3RvcCA9IEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uO1xyXG5cclxuXHRcdEwuRG9tRXZlbnQub24oZWwsIEwuRHJhZ2dhYmxlLlNUQVJULmpvaW4oJyAnKSwgc3RvcCk7XHJcblxyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnQub24oZWwsIHtcclxuXHRcdFx0Y2xpY2s6IEwuRG9tRXZlbnQuX2Zha2VTdG9wLFxyXG5cdFx0XHRkYmxjbGljazogc3RvcFxyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHByZXZlbnREZWZhdWx0KGV2OiBET01FdmVudCk6IHRoaXNcclxuXHQvLyBQcmV2ZW50cyB0aGUgZGVmYXVsdCBhY3Rpb24gb2YgdGhlIERPTSBFdmVudCBgZXZgIGZyb20gaGFwcGVuaW5nIChzdWNoIGFzXHJcblx0Ly8gZm9sbG93aW5nIGEgbGluayBpbiB0aGUgaHJlZiBvZiB0aGUgYSBlbGVtZW50LCBvciBkb2luZyBhIFBPU1QgcmVxdWVzdFxyXG5cdC8vIHdpdGggcGFnZSByZWxvYWQgd2hlbiBhIGA8Zm9ybT5gIGlzIHN1Ym1pdHRlZCkuXHJcblx0Ly8gVXNlIGl0IGluc2lkZSBsaXN0ZW5lciBmdW5jdGlvbnMuXHJcblx0cHJldmVudERlZmF1bHQ6IGZ1bmN0aW9uIChlKSB7XHJcblxyXG5cdFx0aWYgKGUucHJldmVudERlZmF1bHQpIHtcclxuXHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZS5yZXR1cm5WYWx1ZSA9IGZhbHNlO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHN0b3AoZXYpOiB0aGlzXHJcblx0Ly8gRG9lcyBgc3RvcFByb3BhZ2F0aW9uYCBhbmQgYHByZXZlbnREZWZhdWx0YCBhdCB0aGUgc2FtZSB0aW1lLlxyXG5cdHN0b3A6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRyZXR1cm4gTC5Eb21FdmVudFxyXG5cdFx0XHQucHJldmVudERlZmF1bHQoZSlcclxuXHRcdFx0LnN0b3BQcm9wYWdhdGlvbihlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0TW91c2VQb3NpdGlvbihldjogRE9NRXZlbnQsIGNvbnRhaW5lcj86IEhUTUxFbGVtZW50KTogUG9pbnRcclxuXHQvLyBHZXRzIG5vcm1hbGl6ZWQgbW91c2UgcG9zaXRpb24gZnJvbSBhIERPTSBldmVudCByZWxhdGl2ZSB0byB0aGVcclxuXHQvLyBgY29udGFpbmVyYCBvciB0byB0aGUgd2hvbGUgcGFnZSBpZiBub3Qgc3BlY2lmaWVkLlxyXG5cdGdldE1vdXNlUG9zaXRpb246IGZ1bmN0aW9uIChlLCBjb250YWluZXIpIHtcclxuXHRcdGlmICghY29udGFpbmVyKSB7XHJcblx0XHRcdHJldHVybiBuZXcgTC5Qb2ludChlLmNsaWVudFgsIGUuY2xpZW50WSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHJlY3QgPSBjb250YWluZXIuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KFxyXG5cdFx0XHRlLmNsaWVudFggLSByZWN0LmxlZnQgLSBjb250YWluZXIuY2xpZW50TGVmdCxcclxuXHRcdFx0ZS5jbGllbnRZIC0gcmVjdC50b3AgLSBjb250YWluZXIuY2xpZW50VG9wKTtcclxuXHR9LFxyXG5cclxuXHQvLyBDaHJvbWUgb24gV2luIHNjcm9sbHMgZG91YmxlIHRoZSBwaXhlbHMgYXMgaW4gb3RoZXIgcGxhdGZvcm1zIChzZWUgIzQ1MzgpLFxyXG5cdC8vIGFuZCBGaXJlZm94IHNjcm9sbHMgZGV2aWNlIHBpeGVscywgbm90IENTUyBwaXhlbHNcclxuXHRfd2hlZWxQeEZhY3RvcjogKEwuQnJvd3Nlci53aW4gJiYgTC5Ccm93c2VyLmNocm9tZSkgPyAyIDpcclxuXHQgICAgICAgICAgICAgICAgTC5Ccm93c2VyLmdlY2tvID8gd2luZG93LmRldmljZVBpeGVsUmF0aW8gOlxyXG5cdCAgICAgICAgICAgICAgICAxLFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0V2hlZWxEZWx0YShldjogRE9NRXZlbnQpOiBOdW1iZXJcclxuXHQvLyBHZXRzIG5vcm1hbGl6ZWQgd2hlZWwgZGVsdGEgZnJvbSBhIG1vdXNld2hlZWwgRE9NIGV2ZW50LCBpbiB2ZXJ0aWNhbFxyXG5cdC8vIHBpeGVscyBzY3JvbGxlZCAobmVnYXRpdmUgaWYgc2Nyb2xsaW5nIGRvd24pLlxyXG5cdC8vIEV2ZW50cyBmcm9tIHBvaW50aW5nIGRldmljZXMgd2l0aG91dCBwcmVjaXNlIHNjcm9sbGluZyBhcmUgbWFwcGVkIHRvXHJcblx0Ly8gYSBiZXN0IGd1ZXNzIG9mIDYwIHBpeGVscy5cclxuXHRnZXRXaGVlbERlbHRhOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIChMLkJyb3dzZXIuZWRnZSkgPyBlLndoZWVsRGVsdGFZIC8gMiA6IC8vIERvbid0IHRydXN0IHdpbmRvdy1nZW9tZXRyeS1iYXNlZCBkZWx0YVxyXG5cdFx0ICAgICAgIChlLmRlbHRhWSAmJiBlLmRlbHRhTW9kZSA9PT0gMCkgPyAtZS5kZWx0YVkgLyBMLkRvbUV2ZW50Ll93aGVlbFB4RmFjdG9yIDogLy8gUGl4ZWxzXHJcblx0XHQgICAgICAgKGUuZGVsdGFZICYmIGUuZGVsdGFNb2RlID09PSAxKSA/IC1lLmRlbHRhWSAqIDIwIDogLy8gTGluZXNcclxuXHRcdCAgICAgICAoZS5kZWx0YVkgJiYgZS5kZWx0YU1vZGUgPT09IDIpID8gLWUuZGVsdGFZICogNjAgOiAvLyBQYWdlc1xyXG5cdFx0ICAgICAgIChlLmRlbHRhWCB8fCBlLmRlbHRhWikgPyAwIDpcdC8vIFNraXAgaG9yaXpvbnRhbC9kZXB0aCB3aGVlbCBldmVudHNcclxuXHRcdCAgICAgICBlLndoZWVsRGVsdGEgPyAoZS53aGVlbERlbHRhWSB8fCBlLndoZWVsRGVsdGEpIC8gMiA6IC8vIExlZ2FjeSBJRSBwaXhlbHNcclxuXHRcdCAgICAgICAoZS5kZXRhaWwgJiYgTWF0aC5hYnMoZS5kZXRhaWwpIDwgMzI3NjUpID8gLWUuZGV0YWlsICogMjAgOiAvLyBMZWdhY3kgTW96IGxpbmVzXHJcblx0XHQgICAgICAgZS5kZXRhaWwgPyBlLmRldGFpbCAvIC0zMjc2NSAqIDYwIDogLy8gTGVnYWN5IE1veiBwYWdlc1xyXG5cdFx0ICAgICAgIDA7XHJcblx0fSxcclxuXHJcblx0X3NraXBFdmVudHM6IHt9LFxyXG5cclxuXHRfZmFrZVN0b3A6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHQvLyBmYWtlcyBzdG9wUHJvcGFnYXRpb24gYnkgc2V0dGluZyBhIHNwZWNpYWwgZXZlbnQgZmxhZywgY2hlY2tlZC9yZXNldCB3aXRoIEwuRG9tRXZlbnQuX3NraXBwZWQoZSlcclxuXHRcdEwuRG9tRXZlbnQuX3NraXBFdmVudHNbZS50eXBlXSA9IHRydWU7XHJcblx0fSxcclxuXHJcblx0X3NraXBwZWQ6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR2YXIgc2tpcHBlZCA9IHRoaXMuX3NraXBFdmVudHNbZS50eXBlXTtcclxuXHRcdC8vIHJlc2V0IHdoZW4gY2hlY2tpbmcsIGFzIGl0J3Mgb25seSB1c2VkIGluIG1hcCBjb250YWluZXIgYW5kIHByb3BhZ2F0ZXMgb3V0c2lkZSBvZiB0aGUgbWFwXHJcblx0XHR0aGlzLl9za2lwRXZlbnRzW2UudHlwZV0gPSBmYWxzZTtcclxuXHRcdHJldHVybiBza2lwcGVkO1xyXG5cdH0sXHJcblxyXG5cdC8vIGNoZWNrIGlmIGVsZW1lbnQgcmVhbGx5IGxlZnQvZW50ZXJlZCB0aGUgZXZlbnQgdGFyZ2V0IChmb3IgbW91c2VlbnRlci9tb3VzZWxlYXZlKVxyXG5cdF9pc0V4dGVybmFsVGFyZ2V0OiBmdW5jdGlvbiAoZWwsIGUpIHtcclxuXHJcblx0XHR2YXIgcmVsYXRlZCA9IGUucmVsYXRlZFRhcmdldDtcclxuXHJcblx0XHRpZiAoIXJlbGF0ZWQpIHsgcmV0dXJuIHRydWU7IH1cclxuXHJcblx0XHR0cnkge1xyXG5cdFx0XHR3aGlsZSAocmVsYXRlZCAmJiAocmVsYXRlZCAhPT0gZWwpKSB7XHJcblx0XHRcdFx0cmVsYXRlZCA9IHJlbGF0ZWQucGFyZW50Tm9kZTtcclxuXHRcdFx0fVxyXG5cdFx0fSBjYXRjaCAoZXJyKSB7XHJcblx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdH1cclxuXHRcdHJldHVybiAocmVsYXRlZCAhPT0gZWwpO1xyXG5cdH0sXHJcblxyXG5cdC8vIHRoaXMgaXMgYSBob3JyaWJsZSB3b3JrYXJvdW5kIGZvciBhIGJ1ZyBpbiBBbmRyb2lkIHdoZXJlIGEgc2luZ2xlIHRvdWNoIHRyaWdnZXJzIHR3byBjbGljayBldmVudHNcclxuXHRfZmlsdGVyQ2xpY2s6IGZ1bmN0aW9uIChlLCBoYW5kbGVyKSB7XHJcblx0XHR2YXIgdGltZVN0YW1wID0gKGUudGltZVN0YW1wIHx8IChlLm9yaWdpbmFsRXZlbnQgJiYgZS5vcmlnaW5hbEV2ZW50LnRpbWVTdGFtcCkpLFxyXG5cdFx0ICAgIGVsYXBzZWQgPSBMLkRvbUV2ZW50Ll9sYXN0Q2xpY2sgJiYgKHRpbWVTdGFtcCAtIEwuRG9tRXZlbnQuX2xhc3RDbGljayk7XHJcblxyXG5cdFx0Ly8gYXJlIHRoZXkgY2xvc2VyIHRvZ2V0aGVyIHRoYW4gNTAwbXMgeWV0IG1vcmUgdGhhbiAxMDBtcz9cclxuXHRcdC8vIEFuZHJvaWQgdHlwaWNhbGx5IHRyaWdnZXJzIHRoZW0gfjMwMG1zIGFwYXJ0IHdoaWxlIG11bHRpcGxlIGxpc3RlbmVyc1xyXG5cdFx0Ly8gb24gdGhlIHNhbWUgZXZlbnQgc2hvdWxkIGJlIHRyaWdnZXJlZCBmYXIgZmFzdGVyO1xyXG5cdFx0Ly8gb3IgY2hlY2sgaWYgY2xpY2sgaXMgc2ltdWxhdGVkIG9uIHRoZSBlbGVtZW50LCBhbmQgaWYgaXQgaXMsIHJlamVjdCBhbnkgbm9uLXNpbXVsYXRlZCBldmVudHNcclxuXHJcblx0XHRpZiAoKGVsYXBzZWQgJiYgZWxhcHNlZCA+IDEwMCAmJiBlbGFwc2VkIDwgNTAwKSB8fCAoZS50YXJnZXQuX3NpbXVsYXRlZENsaWNrICYmICFlLl9zaW11bGF0ZWQpKSB7XHJcblx0XHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cdFx0TC5Eb21FdmVudC5fbGFzdENsaWNrID0gdGltZVN0YW1wO1xyXG5cclxuXHRcdGhhbmRsZXIoZSk7XHJcblx0fVxyXG59O1xyXG5cclxuLy8gQGZ1bmN0aW9uIGFkZExpc3RlbmVyKOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BMLkRvbUV2ZW50Lm9uYF0oI2RvbWV2ZW50LW9uKVxyXG5MLkRvbUV2ZW50LmFkZExpc3RlbmVyID0gTC5Eb21FdmVudC5vbjtcclxuXHJcbi8vIEBmdW5jdGlvbiByZW1vdmVMaXN0ZW5lcijigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgTC5Eb21FdmVudC5vZmZgXSgjZG9tZXZlbnQtb2ZmKVxyXG5MLkRvbUV2ZW50LnJlbW92ZUxpc3RlbmVyID0gTC5Eb21FdmVudC5vZmY7XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgUG9zQW5pbWF0aW9uXG4gKiBAYWthIEwuUG9zQW5pbWF0aW9uXG4gKiBAaW5oZXJpdHMgRXZlbnRlZFxuICogVXNlZCBpbnRlcm5hbGx5IGZvciBwYW5uaW5nIGFuaW1hdGlvbnMsIHV0aWxpemluZyBDU1MzIFRyYW5zaXRpb25zIGZvciBtb2Rlcm4gYnJvd3NlcnMgYW5kIGEgdGltZXIgZmFsbGJhY2sgZm9yIElFNi05LlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGBqc1xuICogdmFyIGZ4ID0gbmV3IEwuUG9zQW5pbWF0aW9uKCk7XG4gKiBmeC5ydW4oZWwsIFszMDAsIDUwMF0sIDAuNSk7XG4gKiBgYGBcbiAqXG4gKiBAY29uc3RydWN0b3IgTC5Qb3NBbmltYXRpb24oKVxuICogQ3JlYXRlcyBhIGBQb3NBbmltYXRpb25gIG9iamVjdC5cbiAqXG4gKi9cblxuTC5Qb3NBbmltYXRpb24gPSBMLkV2ZW50ZWQuZXh0ZW5kKHtcblxuXHQvLyBAbWV0aG9kIHJ1bihlbDogSFRNTEVsZW1lbnQsIG5ld1BvczogUG9pbnQsIGR1cmF0aW9uPzogTnVtYmVyLCBlYXNlTGluZWFyaXR5PzogTnVtYmVyKVxuXHQvLyBSdW4gYW4gYW5pbWF0aW9uIG9mIGEgZ2l2ZW4gZWxlbWVudCB0byBhIG5ldyBwb3NpdGlvbiwgb3B0aW9uYWxseSBzZXR0aW5nXG5cdC8vIGR1cmF0aW9uIGluIHNlY29uZHMgKGAwLjI1YCBieSBkZWZhdWx0KSBhbmQgZWFzaW5nIGxpbmVhcml0eSBmYWN0b3IgKDNyZFxuXHQvLyBhcmd1bWVudCBvZiB0aGUgW2N1YmljIGJlemllciBjdXJ2ZV0oaHR0cDovL2N1YmljLWJlemllci5jb20vIzAsMCwuNSwxKSxcblx0Ly8gYDAuNWAgYnkgZGVmYXVsdCkuXG5cdHJ1bjogZnVuY3Rpb24gKGVsLCBuZXdQb3MsIGR1cmF0aW9uLCBlYXNlTGluZWFyaXR5KSB7XG5cdFx0dGhpcy5zdG9wKCk7XG5cblx0XHR0aGlzLl9lbCA9IGVsO1xuXHRcdHRoaXMuX2luUHJvZ3Jlc3MgPSB0cnVlO1xuXHRcdHRoaXMuX2R1cmF0aW9uID0gZHVyYXRpb24gfHwgMC4yNTtcblx0XHR0aGlzLl9lYXNlT3V0UG93ZXIgPSAxIC8gTWF0aC5tYXgoZWFzZUxpbmVhcml0eSB8fCAwLjUsIDAuMik7XG5cblx0XHR0aGlzLl9zdGFydFBvcyA9IEwuRG9tVXRpbC5nZXRQb3NpdGlvbihlbCk7XG5cdFx0dGhpcy5fb2Zmc2V0ID0gbmV3UG9zLnN1YnRyYWN0KHRoaXMuX3N0YXJ0UG9zKTtcblx0XHR0aGlzLl9zdGFydFRpbWUgPSArbmV3IERhdGUoKTtcblxuXHRcdC8vIEBldmVudCBzdGFydDogRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBhbmltYXRpb24gc3RhcnRzXG5cdFx0dGhpcy5maXJlKCdzdGFydCcpO1xuXG5cdFx0dGhpcy5fYW5pbWF0ZSgpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc3RvcCgpXG5cdC8vIFN0b3BzIHRoZSBhbmltYXRpb24gKGlmIGN1cnJlbnRseSBydW5uaW5nKS5cblx0c3RvcDogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5faW5Qcm9ncmVzcykgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX3N0ZXAodHJ1ZSk7XG5cdFx0dGhpcy5fY29tcGxldGUoKTtcblx0fSxcblxuXHRfYW5pbWF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdC8vIGFuaW1hdGlvbiBsb29wXG5cdFx0dGhpcy5fYW5pbUlkID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fYW5pbWF0ZSwgdGhpcyk7XG5cdFx0dGhpcy5fc3RlcCgpO1xuXHR9LFxuXG5cdF9zdGVwOiBmdW5jdGlvbiAocm91bmQpIHtcblx0XHR2YXIgZWxhcHNlZCA9ICgrbmV3IERhdGUoKSkgLSB0aGlzLl9zdGFydFRpbWUsXG5cdFx0ICAgIGR1cmF0aW9uID0gdGhpcy5fZHVyYXRpb24gKiAxMDAwO1xuXG5cdFx0aWYgKGVsYXBzZWQgPCBkdXJhdGlvbikge1xuXHRcdFx0dGhpcy5fcnVuRnJhbWUodGhpcy5fZWFzZU91dChlbGFwc2VkIC8gZHVyYXRpb24pLCByb3VuZCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX3J1bkZyYW1lKDEpO1xuXHRcdFx0dGhpcy5fY29tcGxldGUoKTtcblx0XHR9XG5cdH0sXG5cblx0X3J1bkZyYW1lOiBmdW5jdGlvbiAocHJvZ3Jlc3MsIHJvdW5kKSB7XG5cdFx0dmFyIHBvcyA9IHRoaXMuX3N0YXJ0UG9zLmFkZCh0aGlzLl9vZmZzZXQubXVsdGlwbHlCeShwcm9ncmVzcykpO1xuXHRcdGlmIChyb3VuZCkge1xuXHRcdFx0cG9zLl9yb3VuZCgpO1xuXHRcdH1cblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fZWwsIHBvcyk7XG5cblx0XHQvLyBAZXZlbnQgc3RlcDogRXZlbnRcblx0XHQvLyBGaXJlZCBjb250aW51b3VzbHkgZHVyaW5nIHRoZSBhbmltYXRpb24uXG5cdFx0dGhpcy5maXJlKCdzdGVwJyk7XG5cdH0sXG5cblx0X2NvbXBsZXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9hbmltSWQpO1xuXG5cdFx0dGhpcy5faW5Qcm9ncmVzcyA9IGZhbHNlO1xuXHRcdC8vIEBldmVudCBlbmQ6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYW5pbWF0aW9uIGVuZHMuXG5cdFx0dGhpcy5maXJlKCdlbmQnKTtcblx0fSxcblxuXHRfZWFzZU91dDogZnVuY3Rpb24gKHQpIHtcblx0XHRyZXR1cm4gMSAtIE1hdGgucG93KDEgLSB0LCB0aGlzLl9lYXNlT3V0UG93ZXIpO1xuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgUHJvamVjdGlvblxyXG4gKiBAcHJvamVjdGlvbiBMLlByb2plY3Rpb24uTWVyY2F0b3JcclxuICpcclxuICogRWxsaXB0aWNhbCBNZXJjYXRvciBwcm9qZWN0aW9uIOKAlCBtb3JlIGNvbXBsZXggdGhhbiBTcGhlcmljYWwgTWVyY2F0b3IuIFRha2VzIGludG8gYWNjb3VudCB0aGF0IEVhcnRoIGlzIGEgZ2VvaWQsIG5vdCBhIHBlcmZlY3Qgc3BoZXJlLiBVc2VkIGJ5IHRoZSBFUFNHOjMzOTUgQ1JTLlxyXG4gKi9cclxuXHJcbkwuUHJvamVjdGlvbi5NZXJjYXRvciA9IHtcclxuXHRSOiA2Mzc4MTM3LFxyXG5cdFJfTUlOT1I6IDYzNTY3NTIuMzE0MjQ1MTc5LFxyXG5cclxuXHRib3VuZHM6IEwuYm91bmRzKFstMjAwMzc1MDguMzQyNzksIC0xNTQ5NjU3MC43Mzk3Ml0sIFsyMDAzNzUwOC4zNDI3OSwgMTg3NjQ2NTYuMjMxMzhdKSxcclxuXHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIGQgPSBNYXRoLlBJIC8gMTgwLFxyXG5cdFx0ICAgIHIgPSB0aGlzLlIsXHJcblx0XHQgICAgeSA9IGxhdGxuZy5sYXQgKiBkLFxyXG5cdFx0ICAgIHRtcCA9IHRoaXMuUl9NSU5PUiAvIHIsXHJcblx0XHQgICAgZSA9IE1hdGguc3FydCgxIC0gdG1wICogdG1wKSxcclxuXHRcdCAgICBjb24gPSBlICogTWF0aC5zaW4oeSk7XHJcblxyXG5cdFx0dmFyIHRzID0gTWF0aC50YW4oTWF0aC5QSSAvIDQgLSB5IC8gMikgLyBNYXRoLnBvdygoMSAtIGNvbikgLyAoMSArIGNvbiksIGUgLyAyKTtcclxuXHRcdHkgPSAtciAqIE1hdGgubG9nKE1hdGgubWF4KHRzLCAxRS0xMCkpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludChsYXRsbmcubG5nICogZCAqIHIsIHkpO1xyXG5cdH0sXHJcblxyXG5cdHVucHJvamVjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHR2YXIgZCA9IDE4MCAvIE1hdGguUEksXHJcblx0XHQgICAgciA9IHRoaXMuUixcclxuXHRcdCAgICB0bXAgPSB0aGlzLlJfTUlOT1IgLyByLFxyXG5cdFx0ICAgIGUgPSBNYXRoLnNxcnQoMSAtIHRtcCAqIHRtcCksXHJcblx0XHQgICAgdHMgPSBNYXRoLmV4cCgtcG9pbnQueSAvIHIpLFxyXG5cdFx0ICAgIHBoaSA9IE1hdGguUEkgLyAyIC0gMiAqIE1hdGguYXRhbih0cyk7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGRwaGkgPSAwLjEsIGNvbjsgaSA8IDE1ICYmIE1hdGguYWJzKGRwaGkpID4gMWUtNzsgaSsrKSB7XHJcblx0XHRcdGNvbiA9IGUgKiBNYXRoLnNpbihwaGkpO1xyXG5cdFx0XHRjb24gPSBNYXRoLnBvdygoMSAtIGNvbikgLyAoMSArIGNvbiksIGUgLyAyKTtcclxuXHRcdFx0ZHBoaSA9IE1hdGguUEkgLyAyIC0gMiAqIE1hdGguYXRhbih0cyAqIGNvbikgLSBwaGk7XHJcblx0XHRcdHBoaSArPSBkcGhpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcocGhpICogZCwgcG9pbnQueCAqIGQgLyByKTtcclxuXHR9XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgQ1JTXHJcbiAqIEBjcnMgTC5DUlMuRVBTRzMzOTVcclxuICpcclxuICogUmFyZWx5IHVzZWQgYnkgc29tZSBjb21tZXJjaWFsIHRpbGUgcHJvdmlkZXJzLiBVc2VzIEVsbGlwdGljYWwgTWVyY2F0b3IgcHJvamVjdGlvbi5cclxuICovXHJcblxyXG5MLkNSUy5FUFNHMzM5NSA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FYXJ0aCwge1xyXG5cdGNvZGU6ICdFUFNHOjMzOTUnLFxyXG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5NZXJjYXRvcixcclxuXHJcblx0dHJhbnNmb3JtYXRpb246IChmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgc2NhbGUgPSAwLjUgLyAoTWF0aC5QSSAqIEwuUHJvamVjdGlvbi5NZXJjYXRvci5SKTtcclxuXHRcdHJldHVybiBuZXcgTC5UcmFuc2Zvcm1hdGlvbihzY2FsZSwgMC41LCAtc2NhbGUsIDAuNSk7XHJcblx0fSgpKVxyXG59KTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBHcmlkTGF5ZXJcbiAqIEBpbmhlcml0cyBMYXllclxuICogQGFrYSBMLkdyaWRMYXllclxuICpcbiAqIEdlbmVyaWMgY2xhc3MgZm9yIGhhbmRsaW5nIGEgdGlsZWQgZ3JpZCBvZiBIVE1MIGVsZW1lbnRzLiBUaGlzIGlzIHRoZSBiYXNlIGNsYXNzIGZvciBhbGwgdGlsZSBsYXllcnMgYW5kIHJlcGxhY2VzIGBUaWxlTGF5ZXIuQ2FudmFzYC5cbiAqIEdyaWRMYXllciBjYW4gYmUgZXh0ZW5kZWQgdG8gY3JlYXRlIGEgdGlsZWQgZ3JpZCBvZiBIVE1MIGVsZW1lbnRzIGxpa2UgYDxjYW52YXM+YCwgYDxpbWc+YCBvciBgPGRpdj5gLiBHcmlkTGF5ZXIgd2lsbCBoYW5kbGUgY3JlYXRpbmcgYW5kIGFuaW1hdGluZyB0aGVzZSBET00gZWxlbWVudHMgZm9yIHlvdS5cbiAqXG4gKlxuICogQHNlY3Rpb24gU3luY2hyb25vdXMgdXNhZ2VcbiAqIEBleGFtcGxlXG4gKlxuICogVG8gY3JlYXRlIGEgY3VzdG9tIGxheWVyLCBleHRlbmQgR3JpZExheWVyIGFuZCBpbXBsZW1lbnQgdGhlIGBjcmVhdGVUaWxlKClgIG1ldGhvZCwgd2hpY2ggd2lsbCBiZSBwYXNzZWQgYSBgUG9pbnRgIG9iamVjdCB3aXRoIHRoZSBgeGAsIGB5YCwgYW5kIGB6YCAoem9vbSBsZXZlbCkgY29vcmRpbmF0ZXMgdG8gZHJhdyB5b3VyIHRpbGUuXG4gKlxuICogYGBganNcbiAqIHZhciBDYW52YXNMYXllciA9IEwuR3JpZExheWVyLmV4dGVuZCh7XG4gKiAgICAgY3JlYXRlVGlsZTogZnVuY3Rpb24oY29vcmRzKXtcbiAqICAgICAgICAgLy8gY3JlYXRlIGEgPGNhbnZhcz4gZWxlbWVudCBmb3IgZHJhd2luZ1xuICogICAgICAgICB2YXIgdGlsZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2NhbnZhcycsICdsZWFmbGV0LXRpbGUnKTtcbiAqXG4gKiAgICAgICAgIC8vIHNldHVwIHRpbGUgd2lkdGggYW5kIGhlaWdodCBhY2NvcmRpbmcgdG8gdGhlIG9wdGlvbnNcbiAqICAgICAgICAgdmFyIHNpemUgPSB0aGlzLmdldFRpbGVTaXplKCk7XG4gKiAgICAgICAgIHRpbGUud2lkdGggPSBzaXplLng7XG4gKiAgICAgICAgIHRpbGUuaGVpZ2h0ID0gc2l6ZS55O1xuICpcbiAqICAgICAgICAgLy8gZ2V0IGEgY2FudmFzIGNvbnRleHQgYW5kIGRyYXcgc29tZXRoaW5nIG9uIGl0IHVzaW5nIGNvb3Jkcy54LCBjb29yZHMueSBhbmQgY29vcmRzLnpcbiAqICAgICAgICAgdmFyIGN0eCA9IHRpbGUuZ2V0Q29udGV4dCgnMmQnKTtcbiAqXG4gKiAgICAgICAgIC8vIHJldHVybiB0aGUgdGlsZSBzbyBpdCBjYW4gYmUgcmVuZGVyZWQgb24gc2NyZWVuXG4gKiAgICAgICAgIHJldHVybiB0aWxlO1xuICogICAgIH1cbiAqIH0pO1xuICogYGBgXG4gKlxuICogQHNlY3Rpb24gQXN5bmNocm9ub3VzIHVzYWdlXG4gKiBAZXhhbXBsZVxuICpcbiAqIFRpbGUgY3JlYXRpb24gY2FuIGFsc28gYmUgYXN5bmNocm9ub3VzLCB0aGlzIGlzIHVzZWZ1bCB3aGVuIHVzaW5nIGEgdGhpcmQtcGFydHkgZHJhd2luZyBsaWJyYXJ5LiBPbmNlIHRoZSB0aWxlIGlzIGZpbmlzaGVkIGRyYXdpbmcgaXQgY2FuIGJlIHBhc3NlZCB0byB0aGUgYGRvbmUoKWAgY2FsbGJhY2suXG4gKlxuICogYGBganNcbiAqIHZhciBDYW52YXNMYXllciA9IEwuR3JpZExheWVyLmV4dGVuZCh7XG4gKiAgICAgY3JlYXRlVGlsZTogZnVuY3Rpb24oY29vcmRzLCBkb25lKXtcbiAqICAgICAgICAgdmFyIGVycm9yO1xuICpcbiAqICAgICAgICAgLy8gY3JlYXRlIGEgPGNhbnZhcz4gZWxlbWVudCBmb3IgZHJhd2luZ1xuICogICAgICAgICB2YXIgdGlsZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2NhbnZhcycsICdsZWFmbGV0LXRpbGUnKTtcbiAqXG4gKiAgICAgICAgIC8vIHNldHVwIHRpbGUgd2lkdGggYW5kIGhlaWdodCBhY2NvcmRpbmcgdG8gdGhlIG9wdGlvbnNcbiAqICAgICAgICAgdmFyIHNpemUgPSB0aGlzLmdldFRpbGVTaXplKCk7XG4gKiAgICAgICAgIHRpbGUud2lkdGggPSBzaXplLng7XG4gKiAgICAgICAgIHRpbGUuaGVpZ2h0ID0gc2l6ZS55O1xuICpcbiAqICAgICAgICAgLy8gZHJhdyBzb21ldGhpbmcgYXN5bmNocm9ub3VzbHkgYW5kIHBhc3MgdGhlIHRpbGUgdG8gdGhlIGRvbmUoKSBjYWxsYmFja1xuICogICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICogICAgICAgICAgICAgZG9uZShlcnJvciwgdGlsZSk7XG4gKiAgICAgICAgIH0sIDEwMDApO1xuICpcbiAqICAgICAgICAgcmV0dXJuIHRpbGU7XG4gKiAgICAgfVxuICogfSk7XG4gKiBgYGBcbiAqXG4gKiBAc2VjdGlvblxuICovXG5cblxuTC5HcmlkTGF5ZXIgPSBMLkxheWVyLmV4dGVuZCh7XG5cblx0Ly8gQHNlY3Rpb25cblx0Ly8gQGFrYSBHcmlkTGF5ZXIgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiB0aWxlU2l6ZTogTnVtYmVyfFBvaW50ID0gMjU2XG5cdFx0Ly8gV2lkdGggYW5kIGhlaWdodCBvZiB0aWxlcyBpbiB0aGUgZ3JpZC4gVXNlIGEgbnVtYmVyIGlmIHdpZHRoIGFuZCBoZWlnaHQgYXJlIGVxdWFsLCBvciBgTC5wb2ludCh3aWR0aCwgaGVpZ2h0KWAgb3RoZXJ3aXNlLlxuXHRcdHRpbGVTaXplOiAyNTYsXG5cblx0XHQvLyBAb3B0aW9uIG9wYWNpdHk6IE51bWJlciA9IDEuMFxuXHRcdC8vIE9wYWNpdHkgb2YgdGhlIHRpbGVzLiBDYW4gYmUgdXNlZCBpbiB0aGUgYGNyZWF0ZVRpbGUoKWAgZnVuY3Rpb24uXG5cdFx0b3BhY2l0eTogMSxcblxuXHRcdC8vIEBvcHRpb24gdXBkYXRlV2hlbklkbGU6IEJvb2xlYW4gPSBkZXBlbmRzXG5cdFx0Ly8gSWYgYGZhbHNlYCwgbmV3IHRpbGVzIGFyZSBsb2FkZWQgZHVyaW5nIHBhbm5pbmcsIG90aGVyd2lzZSBvbmx5IGFmdGVyIGl0IChmb3IgYmV0dGVyIHBlcmZvcm1hbmNlKS4gYHRydWVgIGJ5IGRlZmF1bHQgb24gbW9iaWxlIGJyb3dzZXJzLCBvdGhlcndpc2UgYGZhbHNlYC5cblx0XHR1cGRhdGVXaGVuSWRsZTogTC5Ccm93c2VyLm1vYmlsZSxcblxuXHRcdC8vIEBvcHRpb24gdXBkYXRlV2hlblpvb21pbmc6IEJvb2xlYW4gPSB0cnVlXG5cdFx0Ly8gQnkgZGVmYXVsdCwgYSBzbW9vdGggem9vbSBhbmltYXRpb24gKGR1cmluZyBhIFt0b3VjaCB6b29tXSgjbWFwLXRvdWNoem9vbSkgb3IgYSBbYGZseVRvKClgXSgjbWFwLWZseXRvKSkgd2lsbCB1cGRhdGUgZ3JpZCBsYXllcnMgZXZlcnkgaW50ZWdlciB6b29tIGxldmVsLiBTZXR0aW5nIHRoaXMgb3B0aW9uIHRvIGBmYWxzZWAgd2lsbCB1cGRhdGUgdGhlIGdyaWQgbGF5ZXIgb25seSB3aGVuIHRoZSBzbW9vdGggYW5pbWF0aW9uIGVuZHMuXG5cdFx0dXBkYXRlV2hlblpvb21pbmc6IHRydWUsXG5cblx0XHQvLyBAb3B0aW9uIHVwZGF0ZUludGVydmFsOiBOdW1iZXIgPSAyMDBcblx0XHQvLyBUaWxlcyB3aWxsIG5vdCB1cGRhdGUgbW9yZSB0aGFuIG9uY2UgZXZlcnkgYHVwZGF0ZUludGVydmFsYCBtaWxsaXNlY29uZHMgd2hlbiBwYW5uaW5nLlxuXHRcdHVwZGF0ZUludGVydmFsOiAyMDAsXG5cblx0XHQvLyBAb3B0aW9uIHpJbmRleDogTnVtYmVyID0gMVxuXHRcdC8vIFRoZSBleHBsaWNpdCB6SW5kZXggb2YgdGhlIHRpbGUgbGF5ZXIuXG5cdFx0ekluZGV4OiAxLFxuXG5cdFx0Ly8gQG9wdGlvbiBib3VuZHM6IExhdExuZ0JvdW5kcyA9IHVuZGVmaW5lZFxuXHRcdC8vIElmIHNldCwgdGlsZXMgd2lsbCBvbmx5IGJlIGxvYWRlZCBpbnNpZGUgdGhlIHNldCBgTGF0TG5nQm91bmRzYC5cblx0XHRib3VuZHM6IG51bGwsXG5cblx0XHQvLyBAb3B0aW9uIG1pblpvb206IE51bWJlciA9IDBcblx0XHQvLyBUaGUgbWluaW11bSB6b29tIGxldmVsIHRoYXQgdGlsZXMgd2lsbCBiZSBsb2FkZWQgYXQuIEJ5IGRlZmF1bHQgdGhlIGVudGlyZSBtYXAuXG5cdFx0bWluWm9vbTogMCxcblxuXHRcdC8vIEBvcHRpb24gbWF4Wm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXG5cdFx0Ly8gVGhlIG1heGltdW0gem9vbSBsZXZlbCB0aGF0IHRpbGVzIHdpbGwgYmUgbG9hZGVkIGF0LlxuXHRcdG1heFpvb206IHVuZGVmaW5lZCxcblxuXHRcdC8vIEBvcHRpb24gbm9XcmFwOiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBXaGV0aGVyIHRoZSBsYXllciBpcyB3cmFwcGVkIGFyb3VuZCB0aGUgYW50aW1lcmlkaWFuLiBJZiBgdHJ1ZWAsIHRoZVxuXHRcdC8vIEdyaWRMYXllciB3aWxsIG9ubHkgYmUgZGlzcGxheWVkIG9uY2UgYXQgbG93IHpvb20gbGV2ZWxzLiBIYXMgbm9cblx0XHQvLyBlZmZlY3Qgd2hlbiB0aGUgW21hcCBDUlNdKCNtYXAtY3JzKSBkb2Vzbid0IHdyYXAgYXJvdW5kLiBDYW4gYmUgdXNlZFxuXHRcdC8vIGluIGNvbWJpbmF0aW9uIHdpdGggW2Bib3VuZHNgXSgjZ3JpZGxheWVyLWJvdW5kcykgdG8gcHJldmVudCByZXF1ZXN0aW5nXG5cdFx0Ly8gdGlsZXMgb3V0c2lkZSB0aGUgQ1JTIGxpbWl0cy5cblx0XHRub1dyYXA6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAndGlsZVBhbmUnXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgZ3JpZCBsYXllciB3aWxsIGJlIGFkZGVkLlxuXHRcdHBhbmU6ICd0aWxlUGFuZScsXG5cblx0XHQvLyBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gJydcblx0XHQvLyBBIGN1c3RvbSBjbGFzcyBuYW1lIHRvIGFzc2lnbiB0byB0aGUgdGlsZSBsYXllci4gRW1wdHkgYnkgZGVmYXVsdC5cblx0XHRjbGFzc05hbWU6ICcnLFxuXG5cdFx0Ly8gQG9wdGlvbiBrZWVwQnVmZmVyOiBOdW1iZXIgPSAyXG5cdFx0Ly8gV2hlbiBwYW5uaW5nIHRoZSBtYXAsIGtlZXAgdGhpcyBtYW55IHJvd3MgYW5kIGNvbHVtbnMgb2YgdGlsZXMgYmVmb3JlIHVubG9hZGluZyB0aGVtLlxuXHRcdGtlZXBCdWZmZXI6IDJcblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2luaXRDb250YWluZXIoKTtcblxuXHRcdHRoaXMuX2xldmVscyA9IHt9O1xuXHRcdHRoaXMuX3RpbGVzID0ge307XG5cblx0XHR0aGlzLl9yZXNldFZpZXcoKTtcblx0XHR0aGlzLl91cGRhdGUoKTtcblx0fSxcblxuXHRiZWZvcmVBZGQ6IGZ1bmN0aW9uIChtYXApIHtcblx0XHRtYXAuX2FkZFpvb21MaW1pdCh0aGlzKTtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHRoaXMuX3JlbW92ZUFsbFRpbGVzKCk7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xuXHRcdG1hcC5fcmVtb3ZlWm9vbUxpbWl0KHRoaXMpO1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IG51bGw7XG5cdFx0dGhpcy5fdGlsZVpvb20gPSBudWxsO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250OiB0aGlzXG5cdC8vIEJyaW5ncyB0aGUgdGlsZSBsYXllciB0byB0aGUgdG9wIG9mIGFsbCB0aWxlIGxheWVycy5cblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0TC5Eb21VdGlsLnRvRnJvbnQodGhpcy5fY29udGFpbmVyKTtcblx0XHRcdHRoaXMuX3NldEF1dG9aSW5kZXgoTWF0aC5tYXgpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGJyaW5nVG9CYWNrOiB0aGlzXG5cdC8vIEJyaW5ncyB0aGUgdGlsZSBsYXllciB0byB0aGUgYm90dG9tIG9mIGFsbCB0aWxlIGxheWVycy5cblx0YnJpbmdUb0JhY2s6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHRMLkRvbVV0aWwudG9CYWNrKHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHR0aGlzLl9zZXRBdXRvWkluZGV4KE1hdGgubWluKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRDb250YWluZXI6IEhUTUxFbGVtZW50XG5cdC8vIFJldHVybnMgdGhlIEhUTUwgZWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSB0aWxlcyBmb3IgdGhpcyBsYXllci5cblx0Z2V0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBbb3BhY2l0eV0oI2dyaWRsYXllci1vcGFjaXR5KSBvZiB0aGUgZ3JpZCBsYXllci5cblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKG9wYWNpdHkpIHtcblx0XHR0aGlzLm9wdGlvbnMub3BhY2l0eSA9IG9wYWNpdHk7XG5cdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0WkluZGV4KHpJbmRleDogTnVtYmVyKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBbekluZGV4XSgjZ3JpZGxheWVyLXppbmRleCkgb2YgdGhlIGdyaWQgbGF5ZXIuXG5cdHNldFpJbmRleDogZnVuY3Rpb24gKHpJbmRleCkge1xuXHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSB6SW5kZXg7XG5cdFx0dGhpcy5fdXBkYXRlWkluZGV4KCk7XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGlzTG9hZGluZzogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBhbnkgdGlsZSBpbiB0aGUgZ3JpZCBsYXllciBoYXMgbm90IGZpbmlzaGVkIGxvYWRpbmcuXG5cdGlzTG9hZGluZzogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sb2FkaW5nO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVkcmF3OiB0aGlzXG5cdC8vIENhdXNlcyB0aGUgbGF5ZXIgdG8gY2xlYXIgYWxsIHRoZSB0aWxlcyBhbmQgcmVxdWVzdCB0aGVtIGFnYWluLlxuXHRyZWRyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0dGhpcy5fdXBkYXRlKCk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSB7XG5cdFx0XHR2aWV3cHJlcmVzZXQ6IHRoaXMuX2ludmFsaWRhdGVBbGwsXG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdHpvb206IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdG1vdmVlbmQ6IHRoaXMuX29uTW92ZUVuZFxuXHRcdH07XG5cblx0XHRpZiAoIXRoaXMub3B0aW9ucy51cGRhdGVXaGVuSWRsZSkge1xuXHRcdFx0Ly8gdXBkYXRlIHRpbGVzIG9uIG1vdmUsIGJ1dCBub3QgbW9yZSBvZnRlbiB0aGFuIG9uY2UgcGVyIGdpdmVuIGludGVydmFsXG5cdFx0XHRpZiAoIXRoaXMuX29uTW92ZSkge1xuXHRcdFx0XHR0aGlzLl9vbk1vdmUgPSBMLlV0aWwudGhyb3R0bGUodGhpcy5fb25Nb3ZlRW5kLCB0aGlzLm9wdGlvbnMudXBkYXRlSW50ZXJ2YWwsIHRoaXMpO1xuXHRcdFx0fVxuXG5cdFx0XHRldmVudHMubW92ZSA9IHRoaXMuX29uTW92ZTtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9hbmltYXRlWm9vbTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXG5cdC8vIExheWVycyBleHRlbmRpbmcgYEdyaWRMYXllcmAgc2hhbGwgcmVpbXBsZW1lbnQgdGhlIGZvbGxvd2luZyBtZXRob2QuXG5cdC8vIEBtZXRob2QgY3JlYXRlVGlsZShjb29yZHM6IE9iamVjdCwgZG9uZT86IEZ1bmN0aW9uKTogSFRNTEVsZW1lbnRcblx0Ly8gQ2FsbGVkIG9ubHkgaW50ZXJuYWxseSwgbXVzdCBiZSBvdmVycmlkZW4gYnkgY2xhc3NlcyBleHRlbmRpbmcgYEdyaWRMYXllcmAuXG5cdC8vIFJldHVybnMgdGhlIGBIVE1MRWxlbWVudGAgY29ycmVzcG9uZGluZyB0byB0aGUgZ2l2ZW4gYGNvb3Jkc2AuIElmIHRoZSBgZG9uZWAgY2FsbGJhY2tcblx0Ly8gaXMgc3BlY2lmaWVkLCBpdCBtdXN0IGJlIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBmaW5pc2hlZCBsb2FkaW5nIGFuZCBkcmF3aW5nLlxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuXHR9LFxuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVNpemU6IFBvaW50XG5cdC8vIE5vcm1hbGl6ZXMgdGhlIFt0aWxlU2l6ZSBvcHRpb25dKCNncmlkbGF5ZXItdGlsZXNpemUpIGludG8gYSBwb2ludC4gVXNlZCBieSB0aGUgYGNyZWF0ZVRpbGUoKWAgbWV0aG9kLlxuXHRnZXRUaWxlU2l6ZTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBzID0gdGhpcy5vcHRpb25zLnRpbGVTaXplO1xuXHRcdHJldHVybiBzIGluc3RhbmNlb2YgTC5Qb2ludCA/IHMgOiBuZXcgTC5Qb2ludChzLCBzKTtcblx0fSxcblxuXHRfdXBkYXRlWkluZGV4OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lciAmJiB0aGlzLm9wdGlvbnMuekluZGV4ICE9PSB1bmRlZmluZWQgJiYgdGhpcy5vcHRpb25zLnpJbmRleCAhPT0gbnVsbCkge1xuXHRcdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLnpJbmRleCA9IHRoaXMub3B0aW9ucy56SW5kZXg7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRBdXRvWkluZGV4OiBmdW5jdGlvbiAoY29tcGFyZSkge1xuXHRcdC8vIGdvIHRocm91Z2ggYWxsIG90aGVyIGxheWVycyBvZiB0aGUgc2FtZSBwYW5lLCBzZXQgekluZGV4IHRvIG1heCArIDEgKGZyb250KSBvciBtaW4gLSAxIChiYWNrKVxuXG5cdFx0dmFyIGxheWVycyA9IHRoaXMuZ2V0UGFuZSgpLmNoaWxkcmVuLFxuXHRcdCAgICBlZGdlWkluZGV4ID0gLWNvbXBhcmUoLUluZmluaXR5LCBJbmZpbml0eSk7IC8vIC1JbmZpbml0eSBmb3IgbWF4LCBJbmZpbml0eSBmb3IgbWluXG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF5ZXJzLmxlbmd0aCwgekluZGV4OyBpIDwgbGVuOyBpKyspIHtcblxuXHRcdFx0ekluZGV4ID0gbGF5ZXJzW2ldLnN0eWxlLnpJbmRleDtcblxuXHRcdFx0aWYgKGxheWVyc1tpXSAhPT0gdGhpcy5fY29udGFpbmVyICYmIHpJbmRleCkge1xuXHRcdFx0XHRlZGdlWkluZGV4ID0gY29tcGFyZShlZGdlWkluZGV4LCArekluZGV4KTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoaXNGaW5pdGUoZWRnZVpJbmRleCkpIHtcblx0XHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSBlZGdlWkluZGV4ICsgY29tcGFyZSgtMSwgMSk7XG5cdFx0XHR0aGlzLl91cGRhdGVaSW5kZXgoKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZU9wYWNpdHk6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cblxuXHRcdC8vIElFIGRvZXNuJ3QgaW5oZXJpdCBmaWx0ZXIgb3BhY2l0eSBwcm9wZXJseSwgc28gd2UncmUgZm9yY2VkIHRvIHNldCBpdCBvbiB0aWxlc1xuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkpIHsgcmV0dXJuOyB9XG5cblx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblxuXHRcdHZhciBub3cgPSArbmV3IERhdGUoKSxcblx0XHQgICAgbmV4dEZyYW1lID0gZmFsc2UsXG5cdFx0ICAgIHdpbGxQcnVuZSA9IGZhbHNlO1xuXG5cdFx0Zm9yICh2YXIga2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR2YXIgdGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0XHRpZiAoIXRpbGUuY3VycmVudCB8fCAhdGlsZS5sb2FkZWQpIHsgY29udGludWU7IH1cblxuXHRcdFx0dmFyIGZhZGUgPSBNYXRoLm1pbigxLCAobm93IC0gdGlsZS5sb2FkZWQpIC8gMjAwKTtcblxuXHRcdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGlsZS5lbCwgZmFkZSk7XG5cdFx0XHRpZiAoZmFkZSA8IDEpIHtcblx0XHRcdFx0bmV4dEZyYW1lID0gdHJ1ZTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGlmICh0aWxlLmFjdGl2ZSkgeyB3aWxsUHJ1bmUgPSB0cnVlOyB9XG5cdFx0XHRcdHRpbGUuYWN0aXZlID0gdHJ1ZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAod2lsbFBydW5lICYmICF0aGlzLl9ub1BydW5lKSB7IHRoaXMuX3BydW5lVGlsZXMoKTsgfVxuXG5cdFx0aWYgKG5leHRGcmFtZSkge1xuXHRcdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9mYWRlRnJhbWUpO1xuXHRcdFx0dGhpcy5fZmFkZUZyYW1lID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fdXBkYXRlT3BhY2l0eSwgdGhpcyk7XG5cdFx0fVxuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lcikgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWxheWVyICcgKyAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSB8fCAnJykpO1xuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgpO1xuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5vcGFjaXR5IDwgMSkge1xuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2NvbnRhaW5lcik7XG5cdH0sXG5cblx0X3VwZGF0ZUxldmVsczogZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHpvb20gPSB0aGlzLl90aWxlWm9vbSxcblx0XHQgICAgbWF4Wm9vbSA9IHRoaXMub3B0aW9ucy5tYXhab29tO1xuXG5cdFx0aWYgKHpvb20gPT09IHVuZGVmaW5lZCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9XG5cblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0aWYgKHRoaXMuX2xldmVsc1t6XS5lbC5jaGlsZHJlbi5sZW5ndGggfHwgeiA9PT0gem9vbSkge1xuXHRcdFx0XHR0aGlzLl9sZXZlbHNbel0uZWwuc3R5bGUuekluZGV4ID0gbWF4Wm9vbSAtIE1hdGguYWJzKHpvb20gLSB6KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fbGV2ZWxzW3pdLmVsKTtcblx0XHRcdFx0dGhpcy5fcmVtb3ZlVGlsZXNBdFpvb20oeik7XG5cdFx0XHRcdGRlbGV0ZSB0aGlzLl9sZXZlbHNbel07XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dmFyIGxldmVsID0gdGhpcy5fbGV2ZWxzW3pvb21dLFxuXHRcdCAgICBtYXAgPSB0aGlzLl9tYXA7XG5cblx0XHRpZiAoIWxldmVsKSB7XG5cdFx0XHRsZXZlbCA9IHRoaXMuX2xldmVsc1t6b29tXSA9IHt9O1xuXG5cdFx0XHRsZXZlbC5lbCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXRpbGUtY29udGFpbmVyIGxlYWZsZXQtem9vbS1hbmltYXRlZCcsIHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHRsZXZlbC5lbC5zdHlsZS56SW5kZXggPSBtYXhab29tO1xuXG5cdFx0XHRsZXZlbC5vcmlnaW4gPSBtYXAucHJvamVjdChtYXAudW5wcm9qZWN0KG1hcC5nZXRQaXhlbE9yaWdpbigpKSwgem9vbSkucm91bmQoKTtcblx0XHRcdGxldmVsLnpvb20gPSB6b29tO1xuXG5cdFx0XHR0aGlzLl9zZXRab29tVHJhbnNmb3JtKGxldmVsLCBtYXAuZ2V0Q2VudGVyKCksIG1hcC5nZXRab29tKCkpO1xuXG5cdFx0XHQvLyBmb3JjZSB0aGUgYnJvd3NlciB0byBjb25zaWRlciB0aGUgbmV3bHkgYWRkZWQgZWxlbWVudCBmb3IgdHJhbnNpdGlvblxuXHRcdFx0TC5VdGlsLmZhbHNlRm4obGV2ZWwuZWwub2Zmc2V0V2lkdGgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2xldmVsID0gbGV2ZWw7XG5cblx0XHRyZXR1cm4gbGV2ZWw7XG5cdH0sXG5cblx0X3BydW5lVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBrZXksIHRpbGU7XG5cblx0XHR2YXIgem9vbSA9IHRoaXMuX21hcC5nZXRab29tKCk7XG5cdFx0aWYgKHpvb20gPiB0aGlzLm9wdGlvbnMubWF4Wm9vbSB8fFxuXHRcdFx0em9vbSA8IHRoaXMub3B0aW9ucy5taW5ab29tKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdHRpbGUucmV0YWluID0gdGlsZS5jdXJyZW50O1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdGlmICh0aWxlLmN1cnJlbnQgJiYgIXRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdHZhciBjb29yZHMgPSB0aWxlLmNvb3Jkcztcblx0XHRcdFx0aWYgKCF0aGlzLl9yZXRhaW5QYXJlbnQoY29vcmRzLngsIGNvb3Jkcy55LCBjb29yZHMueiwgY29vcmRzLnogLSA1KSkge1xuXHRcdFx0XHRcdHRoaXMuX3JldGFpbkNoaWxkcmVuKGNvb3Jkcy54LCBjb29yZHMueSwgY29vcmRzLnosIGNvb3Jkcy56ICsgMik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRmb3IgKGtleSBpbiB0aGlzLl90aWxlcykge1xuXHRcdFx0aWYgKCF0aGlzLl90aWxlc1trZXldLnJldGFpbikge1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVUaWxlKGtleSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9yZW1vdmVUaWxlc0F0Wm9vbTogZnVuY3Rpb24gKHpvb20pIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdGlmICh0aGlzLl90aWxlc1trZXldLmNvb3Jkcy56ICE9PSB6b29tKSB7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fcmVtb3ZlVGlsZShrZXkpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVtb3ZlQWxsVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdHRoaXMuX3JlbW92ZVRpbGUoa2V5KTtcblx0XHR9XG5cdH0sXG5cblx0X2ludmFsaWRhdGVBbGw6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9sZXZlbHNbel0uZWwpO1xuXHRcdFx0ZGVsZXRlIHRoaXMuX2xldmVsc1t6XTtcblx0XHR9XG5cdFx0dGhpcy5fcmVtb3ZlQWxsVGlsZXMoKTtcblxuXHRcdHRoaXMuX3RpbGVab29tID0gbnVsbDtcblx0fSxcblxuXHRfcmV0YWluUGFyZW50OiBmdW5jdGlvbiAoeCwgeSwgeiwgbWluWm9vbSkge1xuXHRcdHZhciB4MiA9IE1hdGguZmxvb3IoeCAvIDIpLFxuXHRcdCAgICB5MiA9IE1hdGguZmxvb3IoeSAvIDIpLFxuXHRcdCAgICB6MiA9IHogLSAxLFxuXHRcdCAgICBjb29yZHMyID0gbmV3IEwuUG9pbnQoK3gyLCAreTIpO1xuXHRcdGNvb3JkczIueiA9ICt6MjtcblxuXHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzMiksXG5cdFx0ICAgIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0aWYgKHRpbGUgJiYgdGlsZS5hY3RpdmUpIHtcblx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdHJldHVybiB0cnVlO1xuXG5cdFx0fSBlbHNlIGlmICh0aWxlICYmIHRpbGUubG9hZGVkKSB7XG5cdFx0XHR0aWxlLnJldGFpbiA9IHRydWU7XG5cdFx0fVxuXG5cdFx0aWYgKHoyID4gbWluWm9vbSkge1xuXHRcdFx0cmV0dXJuIHRoaXMuX3JldGFpblBhcmVudCh4MiwgeTIsIHoyLCBtaW5ab29tKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0X3JldGFpbkNoaWxkcmVuOiBmdW5jdGlvbiAoeCwgeSwgeiwgbWF4Wm9vbSkge1xuXG5cdFx0Zm9yICh2YXIgaSA9IDIgKiB4OyBpIDwgMiAqIHggKyAyOyBpKyspIHtcblx0XHRcdGZvciAodmFyIGogPSAyICogeTsgaiA8IDIgKiB5ICsgMjsgaisrKSB7XG5cblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBMLlBvaW50KGksIGopO1xuXHRcdFx0XHRjb29yZHMueiA9IHogKyAxO1xuXG5cdFx0XHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKSxcblx0XHRcdFx0ICAgIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0XHRcdGlmICh0aWxlICYmIHRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdFx0dGlsZS5yZXRhaW4gPSB0cnVlO1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGlsZSAmJiB0aWxlLmxvYWRlZCkge1xuXHRcdFx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICh6ICsgMSA8IG1heFpvb20pIHtcblx0XHRcdFx0XHR0aGlzLl9yZXRhaW5DaGlsZHJlbihpLCBqLCB6ICsgMSwgbWF4Wm9vbSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0X3Jlc2V0VmlldzogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgYW5pbWF0aW5nID0gZSAmJiAoZS5waW5jaCB8fCBlLmZseVRvKTtcblx0XHR0aGlzLl9zZXRWaWV3KHRoaXMuX21hcC5nZXRDZW50ZXIoKSwgdGhpcy5fbWFwLmdldFpvb20oKSwgYW5pbWF0aW5nLCBhbmltYXRpbmcpO1xuXHR9LFxuXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKGUpIHtcblx0XHR0aGlzLl9zZXRWaWV3KGUuY2VudGVyLCBlLnpvb20sIHRydWUsIGUubm9VcGRhdGUpO1xuXHR9LFxuXG5cdF9zZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBub1BydW5lLCBub1VwZGF0ZSkge1xuXHRcdHZhciB0aWxlWm9vbSA9IE1hdGgucm91bmQoem9vbSk7XG5cdFx0aWYgKCh0aGlzLm9wdGlvbnMubWF4Wm9vbSAhPT0gdW5kZWZpbmVkICYmIHRpbGVab29tID4gdGhpcy5vcHRpb25zLm1heFpvb20pIHx8XG5cdFx0ICAgICh0aGlzLm9wdGlvbnMubWluWm9vbSAhPT0gdW5kZWZpbmVkICYmIHRpbGVab29tIDwgdGhpcy5vcHRpb25zLm1pblpvb20pKSB7XG5cdFx0XHR0aWxlWm9vbSA9IHVuZGVmaW5lZDtcblx0XHR9XG5cblx0XHR2YXIgdGlsZVpvb21DaGFuZ2VkID0gdGhpcy5vcHRpb25zLnVwZGF0ZVdoZW5ab29taW5nICYmICh0aWxlWm9vbSAhPT0gdGhpcy5fdGlsZVpvb20pO1xuXG5cdFx0aWYgKCFub1VwZGF0ZSB8fCB0aWxlWm9vbUNoYW5nZWQpIHtcblxuXHRcdFx0dGhpcy5fdGlsZVpvb20gPSB0aWxlWm9vbTtcblxuXHRcdFx0aWYgKHRoaXMuX2Fib3J0TG9hZGluZykge1xuXHRcdFx0XHR0aGlzLl9hYm9ydExvYWRpbmcoKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fdXBkYXRlTGV2ZWxzKCk7XG5cdFx0XHR0aGlzLl9yZXNldEdyaWQoKTtcblxuXHRcdFx0aWYgKHRpbGVab29tICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdFx0dGhpcy5fdXBkYXRlKGNlbnRlcik7XG5cdFx0XHR9XG5cblx0XHRcdGlmICghbm9QcnVuZSkge1xuXHRcdFx0XHR0aGlzLl9wcnVuZVRpbGVzKCk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEZsYWcgdG8gcHJldmVudCBfdXBkYXRlT3BhY2l0eSBmcm9tIHBydW5pbmcgdGlsZXMgZHVyaW5nXG5cdFx0XHQvLyBhIHpvb20gYW5pbSBvciBhIHBpbmNoIGdlc3R1cmVcblx0XHRcdHRoaXMuX25vUHJ1bmUgPSAhIW5vUHJ1bmU7XG5cdFx0fVxuXG5cdFx0dGhpcy5fc2V0Wm9vbVRyYW5zZm9ybXMoY2VudGVyLCB6b29tKTtcblx0fSxcblxuXHRfc2V0Wm9vbVRyYW5zZm9ybXM6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0dGhpcy5fc2V0Wm9vbVRyYW5zZm9ybSh0aGlzLl9sZXZlbHNbaV0sIGNlbnRlciwgem9vbSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRab29tVHJhbnNmb3JtOiBmdW5jdGlvbiAobGV2ZWwsIGNlbnRlciwgem9vbSkge1xuXHRcdHZhciBzY2FsZSA9IHRoaXMuX21hcC5nZXRab29tU2NhbGUoem9vbSwgbGV2ZWwuem9vbSksXG5cdFx0ICAgIHRyYW5zbGF0ZSA9IGxldmVsLm9yaWdpbi5tdWx0aXBseUJ5KHNjYWxlKVxuXHRcdCAgICAgICAgLnN1YnRyYWN0KHRoaXMuX21hcC5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyLCB6b29tKSkucm91bmQoKTtcblxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0obGV2ZWwuZWwsIHRyYW5zbGF0ZSwgc2NhbGUpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24obGV2ZWwuZWwsIHRyYW5zbGF0ZSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXNldEdyaWQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBjcnMgPSBtYXAub3B0aW9ucy5jcnMsXG5cdFx0ICAgIHRpbGVTaXplID0gdGhpcy5fdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCksXG5cdFx0ICAgIHRpbGVab29tID0gdGhpcy5fdGlsZVpvb207XG5cblx0XHR2YXIgYm91bmRzID0gdGhpcy5fbWFwLmdldFBpeGVsV29ybGRCb3VuZHModGhpcy5fdGlsZVpvb20pO1xuXHRcdGlmIChib3VuZHMpIHtcblx0XHRcdHRoaXMuX2dsb2JhbFRpbGVSYW5nZSA9IHRoaXMuX3B4Qm91bmRzVG9UaWxlUmFuZ2UoYm91bmRzKTtcblx0XHR9XG5cblx0XHR0aGlzLl93cmFwWCA9IGNycy53cmFwTG5nICYmICF0aGlzLm9wdGlvbnMubm9XcmFwICYmIFtcblx0XHRcdE1hdGguZmxvb3IobWFwLnByb2plY3QoWzAsIGNycy53cmFwTG5nWzBdXSwgdGlsZVpvb20pLnggLyB0aWxlU2l6ZS54KSxcblx0XHRcdE1hdGguY2VpbChtYXAucHJvamVjdChbMCwgY3JzLndyYXBMbmdbMV1dLCB0aWxlWm9vbSkueCAvIHRpbGVTaXplLnkpXG5cdFx0XTtcblx0XHR0aGlzLl93cmFwWSA9IGNycy53cmFwTGF0ICYmICF0aGlzLm9wdGlvbnMubm9XcmFwICYmIFtcblx0XHRcdE1hdGguZmxvb3IobWFwLnByb2plY3QoW2Nycy53cmFwTGF0WzBdLCAwXSwgdGlsZVpvb20pLnkgLyB0aWxlU2l6ZS54KSxcblx0XHRcdE1hdGguY2VpbChtYXAucHJvamVjdChbY3JzLndyYXBMYXRbMV0sIDBdLCB0aWxlWm9vbSkueSAvIHRpbGVTaXplLnkpXG5cdFx0XTtcblx0fSxcblxuXHRfb25Nb3ZlRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXAgfHwgdGhpcy5fbWFwLl9hbmltYXRpbmdab29tKSB7IHJldHVybjsgfVxuXG5cdFx0dGhpcy5fdXBkYXRlKCk7XG5cdH0sXG5cblx0X2dldFRpbGVkUGl4ZWxCb3VuZHM6IGZ1bmN0aW9uIChjZW50ZXIpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBtYXBab29tID0gbWFwLl9hbmltYXRpbmdab29tID8gTWF0aC5tYXgobWFwLl9hbmltYXRlVG9ab29tLCBtYXAuZ2V0Wm9vbSgpKSA6IG1hcC5nZXRab29tKCksXG5cdFx0ICAgIHNjYWxlID0gbWFwLmdldFpvb21TY2FsZShtYXBab29tLCB0aGlzLl90aWxlWm9vbSksXG5cdFx0ICAgIHBpeGVsQ2VudGVyID0gbWFwLnByb2plY3QoY2VudGVyLCB0aGlzLl90aWxlWm9vbSkuZmxvb3IoKSxcblx0XHQgICAgaGFsZlNpemUgPSBtYXAuZ2V0U2l6ZSgpLmRpdmlkZUJ5KHNjYWxlICogMik7XG5cblx0XHRyZXR1cm4gbmV3IEwuQm91bmRzKHBpeGVsQ2VudGVyLnN1YnRyYWN0KGhhbGZTaXplKSwgcGl4ZWxDZW50ZXIuYWRkKGhhbGZTaXplKSk7XG5cdH0sXG5cblx0Ly8gUHJpdmF0ZSBtZXRob2QgdG8gbG9hZCB0aWxlcyBpbiB0aGUgZ3JpZCdzIGFjdGl2ZSB6b29tIGxldmVsIGFjY29yZGluZyB0byBtYXAgYm91bmRzXG5cdF91cGRhdGU6IGZ1bmN0aW9uIChjZW50ZXIpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXHRcdGlmICghbWFwKSB7IHJldHVybjsgfVxuXHRcdHZhciB6b29tID0gbWFwLmdldFpvb20oKTtcblxuXHRcdGlmIChjZW50ZXIgPT09IHVuZGVmaW5lZCkgeyBjZW50ZXIgPSBtYXAuZ2V0Q2VudGVyKCk7IH1cblx0XHRpZiAodGhpcy5fdGlsZVpvb20gPT09IHVuZGVmaW5lZCkgeyByZXR1cm47IH1cdC8vIGlmIG91dCBvZiBtaW56b29tL21heHpvb21cblxuXHRcdHZhciBwaXhlbEJvdW5kcyA9IHRoaXMuX2dldFRpbGVkUGl4ZWxCb3VuZHMoY2VudGVyKSxcblx0XHQgICAgdGlsZVJhbmdlID0gdGhpcy5fcHhCb3VuZHNUb1RpbGVSYW5nZShwaXhlbEJvdW5kcyksXG5cdFx0ICAgIHRpbGVDZW50ZXIgPSB0aWxlUmFuZ2UuZ2V0Q2VudGVyKCksXG5cdFx0ICAgIHF1ZXVlID0gW10sXG5cdFx0ICAgIG1hcmdpbiA9IHRoaXMub3B0aW9ucy5rZWVwQnVmZmVyLFxuXHRcdCAgICBub1BydW5lUmFuZ2UgPSBuZXcgTC5Cb3VuZHModGlsZVJhbmdlLmdldEJvdHRvbUxlZnQoKS5zdWJ0cmFjdChbbWFyZ2luLCAtbWFyZ2luXSksXG5cdFx0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGlsZVJhbmdlLmdldFRvcFJpZ2h0KCkuYWRkKFttYXJnaW4sIC1tYXJnaW5dKSk7XG5cblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdHZhciBjID0gdGhpcy5fdGlsZXNba2V5XS5jb29yZHM7XG5cdFx0XHRpZiAoYy56ICE9PSB0aGlzLl90aWxlWm9vbSB8fCAhbm9QcnVuZVJhbmdlLmNvbnRhaW5zKEwucG9pbnQoYy54LCBjLnkpKSkge1xuXHRcdFx0XHR0aGlzLl90aWxlc1trZXldLmN1cnJlbnQgPSBmYWxzZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBfdXBkYXRlIGp1c3QgbG9hZHMgbW9yZSB0aWxlcy4gSWYgdGhlIHRpbGUgem9vbSBsZXZlbCBkaWZmZXJzIHRvbyBtdWNoXG5cdFx0Ly8gZnJvbSB0aGUgbWFwJ3MsIGxldCBfc2V0VmlldyByZXNldCBsZXZlbHMgYW5kIHBydW5lIG9sZCB0aWxlcy5cblx0XHRpZiAoTWF0aC5hYnMoem9vbSAtIHRoaXMuX3RpbGVab29tKSA+IDEpIHsgdGhpcy5fc2V0VmlldyhjZW50ZXIsIHpvb20pOyByZXR1cm47IH1cblxuXHRcdC8vIGNyZWF0ZSBhIHF1ZXVlIG9mIGNvb3JkaW5hdGVzIHRvIGxvYWQgdGlsZXMgZnJvbVxuXHRcdGZvciAodmFyIGogPSB0aWxlUmFuZ2UubWluLnk7IGogPD0gdGlsZVJhbmdlLm1heC55OyBqKyspIHtcblx0XHRcdGZvciAodmFyIGkgPSB0aWxlUmFuZ2UubWluLng7IGkgPD0gdGlsZVJhbmdlLm1heC54OyBpKyspIHtcblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBMLlBvaW50KGksIGopO1xuXHRcdFx0XHRjb29yZHMueiA9IHRoaXMuX3RpbGVab29tO1xuXG5cdFx0XHRcdGlmICghdGhpcy5faXNWYWxpZFRpbGUoY29vcmRzKSkgeyBjb250aW51ZTsgfVxuXG5cdFx0XHRcdHZhciB0aWxlID0gdGhpcy5fdGlsZXNbdGhpcy5fdGlsZUNvb3Jkc1RvS2V5KGNvb3JkcyldO1xuXHRcdFx0XHRpZiAodGlsZSkge1xuXHRcdFx0XHRcdHRpbGUuY3VycmVudCA9IHRydWU7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0cXVldWUucHVzaChjb29yZHMpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gc29ydCB0aWxlIHF1ZXVlIHRvIGxvYWQgdGlsZXMgaW4gb3JkZXIgb2YgdGhlaXIgZGlzdGFuY2UgdG8gY2VudGVyXG5cdFx0cXVldWUuc29ydChmdW5jdGlvbiAoYSwgYikge1xuXHRcdFx0cmV0dXJuIGEuZGlzdGFuY2VUbyh0aWxlQ2VudGVyKSAtIGIuZGlzdGFuY2VUbyh0aWxlQ2VudGVyKTtcblx0XHR9KTtcblxuXHRcdGlmIChxdWV1ZS5sZW5ndGggIT09IDApIHtcblx0XHRcdC8vIGlmIGl0J3MgdGhlIGZpcnN0IGJhdGNoIG9mIHRpbGVzIHRvIGxvYWRcblx0XHRcdGlmICghdGhpcy5fbG9hZGluZykge1xuXHRcdFx0XHR0aGlzLl9sb2FkaW5nID0gdHJ1ZTtcblx0XHRcdFx0Ly8gQGV2ZW50IGxvYWRpbmc6IEV2ZW50XG5cdFx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIGdyaWQgbGF5ZXIgc3RhcnRzIGxvYWRpbmcgdGlsZXMuXG5cdFx0XHRcdHRoaXMuZmlyZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBjcmVhdGUgRE9NIGZyYWdtZW50IHRvIGFwcGVuZCB0aWxlcyBpbiBvbmUgYmF0Y2hcblx0XHRcdHZhciBmcmFnbWVudCA9IGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtcblxuXHRcdFx0Zm9yIChpID0gMDsgaSA8IHF1ZXVlLmxlbmd0aDsgaSsrKSB7XG5cdFx0XHRcdHRoaXMuX2FkZFRpbGUocXVldWVbaV0sIGZyYWdtZW50KTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fbGV2ZWwuZWwuYXBwZW5kQ2hpbGQoZnJhZ21lbnQpO1xuXHRcdH1cblx0fSxcblxuXHRfaXNWYWxpZFRpbGU6IGZ1bmN0aW9uIChjb29yZHMpIHtcblx0XHR2YXIgY3JzID0gdGhpcy5fbWFwLm9wdGlvbnMuY3JzO1xuXG5cdFx0aWYgKCFjcnMuaW5maW5pdGUpIHtcblx0XHRcdC8vIGRvbid0IGxvYWQgdGlsZSBpZiBpdCdzIG91dCBvZiBib3VuZHMgYW5kIG5vdCB3cmFwcGVkXG5cdFx0XHR2YXIgYm91bmRzID0gdGhpcy5fZ2xvYmFsVGlsZVJhbmdlO1xuXHRcdFx0aWYgKCghY3JzLndyYXBMbmcgJiYgKGNvb3Jkcy54IDwgYm91bmRzLm1pbi54IHx8IGNvb3Jkcy54ID4gYm91bmRzLm1heC54KSkgfHxcblx0XHRcdCAgICAoIWNycy53cmFwTGF0ICYmIChjb29yZHMueSA8IGJvdW5kcy5taW4ueSB8fCBjb29yZHMueSA+IGJvdW5kcy5tYXgueSkpKSB7IHJldHVybiBmYWxzZTsgfVxuXHRcdH1cblxuXHRcdGlmICghdGhpcy5vcHRpb25zLmJvdW5kcykgeyByZXR1cm4gdHJ1ZTsgfVxuXG5cdFx0Ly8gZG9uJ3QgbG9hZCB0aWxlIGlmIGl0IGRvZXNuJ3QgaW50ZXJzZWN0IHRoZSBib3VuZHMgaW4gb3B0aW9uc1xuXHRcdHZhciB0aWxlQm91bmRzID0gdGhpcy5fdGlsZUNvb3Jkc1RvQm91bmRzKGNvb3Jkcyk7XG5cdFx0cmV0dXJuIEwubGF0TG5nQm91bmRzKHRoaXMub3B0aW9ucy5ib3VuZHMpLm92ZXJsYXBzKHRpbGVCb3VuZHMpO1xuXHR9LFxuXG5cdF9rZXlUb0JvdW5kczogZnVuY3Rpb24gKGtleSkge1xuXHRcdHJldHVybiB0aGlzLl90aWxlQ29vcmRzVG9Cb3VuZHModGhpcy5fa2V5VG9UaWxlQ29vcmRzKGtleSkpO1xuXHR9LFxuXG5cdC8vIGNvbnZlcnRzIHRpbGUgY29vcmRpbmF0ZXMgdG8gaXRzIGdlb2dyYXBoaWNhbCBib3VuZHNcblx0X3RpbGVDb29yZHNUb0JvdW5kczogZnVuY3Rpb24gKGNvb3Jkcykge1xuXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCksXG5cblx0XHQgICAgbndQb2ludCA9IGNvb3Jkcy5zY2FsZUJ5KHRpbGVTaXplKSxcblx0XHQgICAgc2VQb2ludCA9IG53UG9pbnQuYWRkKHRpbGVTaXplKSxcblxuXHRcdCAgICBudyA9IG1hcC51bnByb2plY3QobndQb2ludCwgY29vcmRzLnopLFxuXHRcdCAgICBzZSA9IG1hcC51bnByb2plY3Qoc2VQb2ludCwgY29vcmRzLnopLFxuXHRcdCAgICBib3VuZHMgPSBuZXcgTC5MYXRMbmdCb3VuZHMobncsIHNlKTtcblxuXHRcdGlmICghdGhpcy5vcHRpb25zLm5vV3JhcCkge1xuXHRcdFx0bWFwLndyYXBMYXRMbmdCb3VuZHMoYm91bmRzKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gYm91bmRzO1xuXHR9LFxuXG5cdC8vIGNvbnZlcnRzIHRpbGUgY29vcmRpbmF0ZXMgdG8ga2V5IGZvciB0aGUgdGlsZSBjYWNoZVxuXHRfdGlsZUNvb3Jkc1RvS2V5OiBmdW5jdGlvbiAoY29vcmRzKSB7XG5cdFx0cmV0dXJuIGNvb3Jkcy54ICsgJzonICsgY29vcmRzLnkgKyAnOicgKyBjb29yZHMuejtcblx0fSxcblxuXHQvLyBjb252ZXJ0cyB0aWxlIGNhY2hlIGtleSB0byBjb29yZGluYXRlc1xuXHRfa2V5VG9UaWxlQ29vcmRzOiBmdW5jdGlvbiAoa2V5KSB7XG5cdFx0dmFyIGsgPSBrZXkuc3BsaXQoJzonKSxcblx0XHQgICAgY29vcmRzID0gbmV3IEwuUG9pbnQoK2tbMF0sICtrWzFdKTtcblx0XHRjb29yZHMueiA9ICtrWzJdO1xuXHRcdHJldHVybiBjb29yZHM7XG5cdH0sXG5cblx0X3JlbW92ZVRpbGU6IGZ1bmN0aW9uIChrZXkpIHtcblx0XHR2YXIgdGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0aWYgKCF0aWxlKSB7IHJldHVybjsgfVxuXG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aWxlLmVsKTtcblxuXHRcdGRlbGV0ZSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0Ly8gQGV2ZW50IHRpbGV1bmxvYWQ6IFRpbGVFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gYSB0aWxlIGlzIHJlbW92ZWQgKGUuZy4gd2hlbiBhIHRpbGUgZ29lcyBvZmYgdGhlIHNjcmVlbikuXG5cdFx0dGhpcy5maXJlKCd0aWxldW5sb2FkJywge1xuXHRcdFx0dGlsZTogdGlsZS5lbCxcblx0XHRcdGNvb3JkczogdGhpcy5fa2V5VG9UaWxlQ29vcmRzKGtleSlcblx0XHR9KTtcblx0fSxcblxuXHRfaW5pdFRpbGU6IGZ1bmN0aW9uICh0aWxlKSB7XG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRpbGUsICdsZWFmbGV0LXRpbGUnKTtcblxuXHRcdHZhciB0aWxlU2l6ZSA9IHRoaXMuZ2V0VGlsZVNpemUoKTtcblx0XHR0aWxlLnN0eWxlLndpZHRoID0gdGlsZVNpemUueCArICdweCc7XG5cdFx0dGlsZS5zdHlsZS5oZWlnaHQgPSB0aWxlU2l6ZS55ICsgJ3B4JztcblxuXHRcdHRpbGUub25zZWxlY3RzdGFydCA9IEwuVXRpbC5mYWxzZUZuO1xuXHRcdHRpbGUub25tb3VzZW1vdmUgPSBMLlV0aWwuZmFsc2VGbjtcblxuXHRcdC8vIHVwZGF0ZSBvcGFjaXR5IG9uIHRpbGVzIGluIElFNy04IGJlY2F1c2Ugb2YgZmlsdGVyIGluaGVyaXRhbmNlIHByb2JsZW1zXG5cdFx0aWYgKEwuQnJvd3Nlci5pZWx0OSAmJiB0aGlzLm9wdGlvbnMub3BhY2l0eSA8IDEpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRpbGUsIHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblx0XHR9XG5cblx0XHQvLyB3aXRob3V0IHRoaXMgaGFjaywgdGlsZXMgZGlzYXBwZWFyIGFmdGVyIHpvb20gb24gQ2hyb21lIGZvciBBbmRyb2lkXG5cdFx0Ly8gaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMjA3OFxuXHRcdGlmIChMLkJyb3dzZXIuYW5kcm9pZCAmJiAhTC5Ccm93c2VyLmFuZHJvaWQyMykge1xuXHRcdFx0dGlsZS5zdHlsZS5XZWJraXRCYWNrZmFjZVZpc2liaWxpdHkgPSAnaGlkZGVuJztcblx0XHR9XG5cdH0sXG5cblx0X2FkZFRpbGU6IGZ1bmN0aW9uIChjb29yZHMsIGNvbnRhaW5lcikge1xuXHRcdHZhciB0aWxlUG9zID0gdGhpcy5fZ2V0VGlsZVBvcyhjb29yZHMpLFxuXHRcdCAgICBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKTtcblxuXHRcdHZhciB0aWxlID0gdGhpcy5jcmVhdGVUaWxlKHRoaXMuX3dyYXBDb29yZHMoY29vcmRzKSwgTC5iaW5kKHRoaXMuX3RpbGVSZWFkeSwgdGhpcywgY29vcmRzKSk7XG5cblx0XHR0aGlzLl9pbml0VGlsZSh0aWxlKTtcblxuXHRcdC8vIGlmIGNyZWF0ZVRpbGUgaXMgZGVmaW5lZCB3aXRoIGEgc2Vjb25kIGFyZ3VtZW50IChcImRvbmVcIiBjYWxsYmFjayksXG5cdFx0Ly8gd2Uga25vdyB0aGF0IHRpbGUgaXMgYXN5bmMgYW5kIHdpbGwgYmUgcmVhZHkgbGF0ZXI7IG90aGVyd2lzZVxuXHRcdGlmICh0aGlzLmNyZWF0ZVRpbGUubGVuZ3RoIDwgMikge1xuXHRcdFx0Ly8gbWFyayB0aWxlIGFzIHJlYWR5LCBidXQgZGVsYXkgb25lIGZyYW1lIGZvciBvcGFjaXR5IGFuaW1hdGlvbiB0byBoYXBwZW5cblx0XHRcdEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKEwuYmluZCh0aGlzLl90aWxlUmVhZHksIHRoaXMsIGNvb3JkcywgbnVsbCwgdGlsZSkpO1xuXHRcdH1cblxuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aWxlLCB0aWxlUG9zKTtcblxuXHRcdC8vIHNhdmUgdGlsZSBpbiBjYWNoZVxuXHRcdHRoaXMuX3RpbGVzW2tleV0gPSB7XG5cdFx0XHRlbDogdGlsZSxcblx0XHRcdGNvb3JkczogY29vcmRzLFxuXHRcdFx0Y3VycmVudDogdHJ1ZVxuXHRcdH07XG5cblx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQodGlsZSk7XG5cdFx0Ly8gQGV2ZW50IHRpbGVsb2Fkc3RhcnQ6IFRpbGVFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gYSB0aWxlIGlzIHJlcXVlc3RlZCBhbmQgc3RhcnRzIGxvYWRpbmcuXG5cdFx0dGhpcy5maXJlKCd0aWxlbG9hZHN0YXJ0Jywge1xuXHRcdFx0dGlsZTogdGlsZSxcblx0XHRcdGNvb3JkczogY29vcmRzXG5cdFx0fSk7XG5cdH0sXG5cblx0X3RpbGVSZWFkeTogZnVuY3Rpb24gKGNvb3JkcywgZXJyLCB0aWxlKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XG5cblx0XHRpZiAoZXJyKSB7XG5cdFx0XHQvLyBAZXZlbnQgdGlsZWVycm9yOiBUaWxlRXJyb3JFdmVudFxuXHRcdFx0Ly8gRmlyZWQgd2hlbiB0aGVyZSBpcyBhbiBlcnJvciBsb2FkaW5nIGEgdGlsZS5cblx0XHRcdHRoaXMuZmlyZSgndGlsZWVycm9yJywge1xuXHRcdFx0XHRlcnJvcjogZXJyLFxuXHRcdFx0XHR0aWxlOiB0aWxlLFxuXHRcdFx0XHRjb29yZHM6IGNvb3Jkc1xuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0dmFyIGtleSA9IHRoaXMuX3RpbGVDb29yZHNUb0tleShjb29yZHMpO1xuXG5cdFx0dGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0aWYgKCF0aWxlKSB7IHJldHVybjsgfVxuXG5cdFx0dGlsZS5sb2FkZWQgPSArbmV3IERhdGUoKTtcblx0XHRpZiAodGhpcy5fbWFwLl9mYWRlQW5pbWF0ZWQpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRpbGUuZWwsIDApO1xuXHRcdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9mYWRlRnJhbWUpO1xuXHRcdFx0dGhpcy5fZmFkZUZyYW1lID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fdXBkYXRlT3BhY2l0eSwgdGhpcyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRpbGUuYWN0aXZlID0gdHJ1ZTtcblx0XHRcdHRoaXMuX3BydW5lVGlsZXMoKTtcblx0XHR9XG5cblx0XHRpZiAoIWVycikge1xuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRpbGUuZWwsICdsZWFmbGV0LXRpbGUtbG9hZGVkJyk7XG5cblx0XHRcdC8vIEBldmVudCB0aWxlbG9hZDogVGlsZUV2ZW50XG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgdGlsZSBsb2Fkcy5cblx0XHRcdHRoaXMuZmlyZSgndGlsZWxvYWQnLCB7XG5cdFx0XHRcdHRpbGU6IHRpbGUuZWwsXG5cdFx0XHRcdGNvb3JkczogY29vcmRzXG5cdFx0XHR9KTtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5fbm9UaWxlc1RvTG9hZCgpKSB7XG5cdFx0XHR0aGlzLl9sb2FkaW5nID0gZmFsc2U7XG5cdFx0XHQvLyBAZXZlbnQgbG9hZDogRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIGdyaWQgbGF5ZXIgbG9hZGVkIGFsbCB2aXNpYmxlIHRpbGVzLlxuXHRcdFx0dGhpcy5maXJlKCdsb2FkJyk7XG5cblx0XHRcdGlmIChMLkJyb3dzZXIuaWVsdDkgfHwgIXRoaXMuX21hcC5fZmFkZUFuaW1hdGVkKSB7XG5cdFx0XHRcdEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX3BydW5lVGlsZXMsIHRoaXMpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Ly8gV2FpdCBhIGJpdCBtb3JlIHRoYW4gMC4yIHNlY3MgKHRoZSBkdXJhdGlvbiBvZiB0aGUgdGlsZSBmYWRlLWluKVxuXHRcdFx0XHQvLyB0byB0cmlnZ2VyIGEgcHJ1bmluZy5cblx0XHRcdFx0c2V0VGltZW91dChMLmJpbmQodGhpcy5fcHJ1bmVUaWxlcywgdGhpcyksIDI1MCk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9nZXRUaWxlUG9zOiBmdW5jdGlvbiAoY29vcmRzKSB7XG5cdFx0cmV0dXJuIGNvb3Jkcy5zY2FsZUJ5KHRoaXMuZ2V0VGlsZVNpemUoKSkuc3VidHJhY3QodGhpcy5fbGV2ZWwub3JpZ2luKTtcblx0fSxcblxuXHRfd3JhcENvb3JkczogZnVuY3Rpb24gKGNvb3Jkcykge1xuXHRcdHZhciBuZXdDb29yZHMgPSBuZXcgTC5Qb2ludChcblx0XHRcdHRoaXMuX3dyYXBYID8gTC5VdGlsLndyYXBOdW0oY29vcmRzLngsIHRoaXMuX3dyYXBYKSA6IGNvb3Jkcy54LFxuXHRcdFx0dGhpcy5fd3JhcFkgPyBMLlV0aWwud3JhcE51bShjb29yZHMueSwgdGhpcy5fd3JhcFkpIDogY29vcmRzLnkpO1xuXHRcdG5ld0Nvb3Jkcy56ID0gY29vcmRzLno7XG5cdFx0cmV0dXJuIG5ld0Nvb3Jkcztcblx0fSxcblxuXHRfcHhCb3VuZHNUb1RpbGVSYW5nZTogZnVuY3Rpb24gKGJvdW5kcykge1xuXHRcdHZhciB0aWxlU2l6ZSA9IHRoaXMuZ2V0VGlsZVNpemUoKTtcblx0XHRyZXR1cm4gbmV3IEwuQm91bmRzKFxuXHRcdFx0Ym91bmRzLm1pbi51bnNjYWxlQnkodGlsZVNpemUpLmZsb29yKCksXG5cdFx0XHRib3VuZHMubWF4LnVuc2NhbGVCeSh0aWxlU2l6ZSkuY2VpbCgpLnN1YnRyYWN0KFsxLCAxXSkpO1xuXHR9LFxuXG5cdF9ub1RpbGVzVG9Mb2FkOiBmdW5jdGlvbiAoKSB7XG5cdFx0Zm9yICh2YXIga2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHRpZiAoIXRoaXMuX3RpbGVzW2tleV0ubG9hZGVkKSB7IHJldHVybiBmYWxzZTsgfVxuXHRcdH1cblx0XHRyZXR1cm4gdHJ1ZTtcblx0fVxufSk7XG5cbi8vIEBmYWN0b3J5IEwuZ3JpZExheWVyKG9wdGlvbnM/OiBHcmlkTGF5ZXIgb3B0aW9ucylcbi8vIENyZWF0ZXMgYSBuZXcgaW5zdGFuY2Ugb2YgR3JpZExheWVyIHdpdGggdGhlIHN1cHBsaWVkIG9wdGlvbnMuXG5MLmdyaWRMYXllciA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5HcmlkTGF5ZXIob3B0aW9ucyk7XG59O1xuXG5cblxuLypcclxuICogQGNsYXNzIFRpbGVMYXllclxyXG4gKiBAaW5oZXJpdHMgR3JpZExheWVyXHJcbiAqIEBha2EgTC5UaWxlTGF5ZXJcclxuICogVXNlZCB0byBsb2FkIGFuZCBkaXNwbGF5IHRpbGUgbGF5ZXJzIG9uIHRoZSBtYXAuIEV4dGVuZHMgYEdyaWRMYXllcmAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIEwudGlsZUxheWVyKCdodHRwOi8ve3N9LnRpbGUub3BlbnN0cmVldG1hcC5vcmcve3p9L3t4fS97eX0ucG5nP3tmb299Jywge2ZvbzogJ2Jhcid9KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICpcclxuICogQHNlY3Rpb24gVVJMIHRlbXBsYXRlXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIEEgc3RyaW5nIG9mIHRoZSBmb2xsb3dpbmcgZm9ybTpcclxuICpcclxuICogYGBgXHJcbiAqICdodHRwOi8ve3N9LnNvbWVkb21haW4uY29tL2JsYWJsYS97en0ve3h9L3t5fXtyfS5wbmcnXHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBge3N9YCBtZWFucyBvbmUgb2YgdGhlIGF2YWlsYWJsZSBzdWJkb21haW5zICh1c2VkIHNlcXVlbnRpYWxseSB0byBoZWxwIHdpdGggYnJvd3NlciBwYXJhbGxlbCByZXF1ZXN0cyBwZXIgZG9tYWluIGxpbWl0YXRpb247IHN1YmRvbWFpbiB2YWx1ZXMgYXJlIHNwZWNpZmllZCBpbiBvcHRpb25zOyBgYWAsIGBiYCBvciBgY2AgYnkgZGVmYXVsdCwgY2FuIGJlIG9taXR0ZWQpLCBge3p9YCDigJQgem9vbSBsZXZlbCwgYHt4fWAgYW5kIGB7eX1gIOKAlCB0aWxlIGNvb3JkaW5hdGVzLiBge3J9YCBjYW4gYmUgdXNlZCB0byBhZGQgQDJ4IHRvIHRoZSBVUkwgdG8gbG9hZCByZXRpbmEgdGlsZXMuXHJcbiAqXHJcbiAqIFlvdSBjYW4gdXNlIGN1c3RvbSBrZXlzIGluIHRoZSB0ZW1wbGF0ZSwgd2hpY2ggd2lsbCBiZSBbZXZhbHVhdGVkXSgjdXRpbC10ZW1wbGF0ZSkgZnJvbSBUaWxlTGF5ZXIgb3B0aW9ucywgbGlrZSB0aGlzOlxyXG4gKlxyXG4gKiBgYGBcclxuICogTC50aWxlTGF5ZXIoJ2h0dHA6Ly97c30uc29tZWRvbWFpbi5jb20ve2Zvb30ve3p9L3t4fS97eX0ucG5nJywge2ZvbzogJ2Jhcid9KTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuXHJcbkwuVGlsZUxheWVyID0gTC5HcmlkTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFRpbGVMYXllciBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBtaW5ab29tOiBOdW1iZXIgPSAwXHJcblx0XHQvLyBNaW5pbXVtIHpvb20gbnVtYmVyLlxyXG5cdFx0bWluWm9vbTogMCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1heFpvb206IE51bWJlciA9IDE4XHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbnVtYmVyLlxyXG5cdFx0bWF4Wm9vbTogMTgsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhOYXRpdmVab29tOiBOdW1iZXIgPSBudWxsXHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbnVtYmVyIHRoZSB0aWxlIHNvdXJjZSBoYXMgYXZhaWxhYmxlLiBJZiBpdCBpcyBzcGVjaWZpZWQsXHJcblx0XHQvLyB0aGUgdGlsZXMgb24gYWxsIHpvb20gbGV2ZWxzIGhpZ2hlciB0aGFuIGBtYXhOYXRpdmVab29tYCB3aWxsIGJlIGxvYWRlZFxyXG5cdFx0Ly8gZnJvbSBgbWF4TmF0aXZlWm9vbWAgbGV2ZWwgYW5kIGF1dG8tc2NhbGVkLlxyXG5cdFx0bWF4TmF0aXZlWm9vbTogbnVsbCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1pbk5hdGl2ZVpvb206IE51bWJlciA9IG51bGxcclxuXHRcdC8vIE1pbmltdW0gem9vbSBudW1iZXIgdGhlIHRpbGUgc291cmNlIGhhcyBhdmFpbGFibGUuIElmIGl0IGlzIHNwZWNpZmllZCxcclxuXHRcdC8vIHRoZSB0aWxlcyBvbiBhbGwgem9vbSBsZXZlbHMgbG93ZXIgdGhhbiBgbWluTmF0aXZlWm9vbWAgd2lsbCBiZSBsb2FkZWRcclxuXHRcdC8vIGZyb20gYG1pbk5hdGl2ZVpvb21gIGxldmVsIGFuZCBhdXRvLXNjYWxlZC5cclxuXHRcdG1pbk5hdGl2ZVpvb206IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBzdWJkb21haW5zOiBTdHJpbmd8U3RyaW5nW10gPSAnYWJjJ1xyXG5cdFx0Ly8gU3ViZG9tYWlucyBvZiB0aGUgdGlsZSBzZXJ2aWNlLiBDYW4gYmUgcGFzc2VkIGluIHRoZSBmb3JtIG9mIG9uZSBzdHJpbmcgKHdoZXJlIGVhY2ggbGV0dGVyIGlzIGEgc3ViZG9tYWluIG5hbWUpIG9yIGFuIGFycmF5IG9mIHN0cmluZ3MuXHJcblx0XHRzdWJkb21haW5zOiAnYWJjJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGVycm9yVGlsZVVybDogU3RyaW5nID0gJydcclxuXHRcdC8vIFVSTCB0byB0aGUgdGlsZSBpbWFnZSB0byBzaG93IGluIHBsYWNlIG9mIHRoZSB0aWxlIHRoYXQgZmFpbGVkIHRvIGxvYWQuXHJcblx0XHRlcnJvclRpbGVVcmw6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbU9mZnNldDogTnVtYmVyID0gMFxyXG5cdFx0Ly8gVGhlIHpvb20gbnVtYmVyIHVzZWQgaW4gdGlsZSBVUkxzIHdpbGwgYmUgb2Zmc2V0IHdpdGggdGhpcyB2YWx1ZS5cclxuXHRcdHpvb21PZmZzZXQ6IDAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0bXM6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCBpbnZlcnNlcyBZIGF4aXMgbnVtYmVyaW5nIGZvciB0aWxlcyAodHVybiB0aGlzIG9uIGZvciBbVE1TXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9UaWxlX01hcF9TZXJ2aWNlKSBzZXJ2aWNlcykuXHJcblx0XHR0bXM6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbVJldmVyc2U6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgc2V0IHRvIHRydWUsIHRoZSB6b29tIG51bWJlciB1c2VkIGluIHRpbGUgVVJMcyB3aWxsIGJlIHJldmVyc2VkIChgbWF4Wm9vbSAtIHpvb21gIGluc3RlYWQgb2YgYHpvb21gKVxyXG5cdFx0em9vbVJldmVyc2U6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gZGV0ZWN0UmV0aW5hOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCBhbmQgdXNlciBpcyBvbiBhIHJldGluYSBkaXNwbGF5LCBpdCB3aWxsIHJlcXVlc3QgZm91ciB0aWxlcyBvZiBoYWxmIHRoZSBzcGVjaWZpZWQgc2l6ZSBhbmQgYSBiaWdnZXIgem9vbSBsZXZlbCBpbiBwbGFjZSBvZiBvbmUgdG8gdXRpbGl6ZSB0aGUgaGlnaCByZXNvbHV0aW9uLlxyXG5cdFx0ZGV0ZWN0UmV0aW5hOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNyb3NzT3JpZ2luOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIHRydWUsIGFsbCB0aWxlcyB3aWxsIGhhdmUgdGhlaXIgY3Jvc3NPcmlnaW4gYXR0cmlidXRlIHNldCB0byAnJy4gVGhpcyBpcyBuZWVkZWQgaWYgeW91IHdhbnQgdG8gYWNjZXNzIHRpbGUgcGl4ZWwgZGF0YS5cclxuXHRcdGNyb3NzT3JpZ2luOiBmYWxzZVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblxyXG5cdFx0b3B0aW9ucyA9IEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHJcblx0XHQvLyBkZXRlY3RpbmcgcmV0aW5hIGRpc3BsYXlzLCBhZGp1c3RpbmcgdGlsZVNpemUgYW5kIHpvb20gbGV2ZWxzXHJcblx0XHRpZiAob3B0aW9ucy5kZXRlY3RSZXRpbmEgJiYgTC5Ccm93c2VyLnJldGluYSAmJiBvcHRpb25zLm1heFpvb20gPiAwKSB7XHJcblxyXG5cdFx0XHRvcHRpb25zLnRpbGVTaXplID0gTWF0aC5mbG9vcihvcHRpb25zLnRpbGVTaXplIC8gMik7XHJcblxyXG5cdFx0XHRpZiAoIW9wdGlvbnMuem9vbVJldmVyc2UpIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb21PZmZzZXQrKztcclxuXHRcdFx0XHRvcHRpb25zLm1heFpvb20tLTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb21PZmZzZXQtLTtcclxuXHRcdFx0XHRvcHRpb25zLm1pblpvb20rKztcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0b3B0aW9ucy5taW5ab29tID0gTWF0aC5tYXgoMCwgb3B0aW9ucy5taW5ab29tKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodHlwZW9mIG9wdGlvbnMuc3ViZG9tYWlucyA9PT0gJ3N0cmluZycpIHtcclxuXHRcdFx0b3B0aW9ucy5zdWJkb21haW5zID0gb3B0aW9ucy5zdWJkb21haW5zLnNwbGl0KCcnKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBmb3IgaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMTM3XHJcblx0XHRpZiAoIUwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdHRoaXMub24oJ3RpbGV1bmxvYWQnLCB0aGlzLl9vblRpbGVSZW1vdmUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0VXJsKHVybDogU3RyaW5nLCBub1JlZHJhdz86IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gVXBkYXRlcyB0aGUgbGF5ZXIncyBVUkwgdGVtcGxhdGUgYW5kIHJlZHJhd3MgaXQgKHVubGVzcyBgbm9SZWRyYXdgIGlzIHNldCB0byBgdHJ1ZWApLlxyXG5cdHNldFVybDogZnVuY3Rpb24gKHVybCwgbm9SZWRyYXcpIHtcclxuXHRcdHRoaXMuX3VybCA9IHVybDtcclxuXHJcblx0XHRpZiAoIW5vUmVkcmF3KSB7XHJcblx0XHRcdHRoaXMucmVkcmF3KCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNyZWF0ZVRpbGUoY29vcmRzOiBPYmplY3QsIGRvbmU/OiBGdW5jdGlvbik6IEhUTUxFbGVtZW50XHJcblx0Ly8gQ2FsbGVkIG9ubHkgaW50ZXJuYWxseSwgb3ZlcnJpZGVzIEdyaWRMYXllcidzIFtgY3JlYXRlVGlsZSgpYF0oI2dyaWRsYXllci1jcmVhdGV0aWxlKVxyXG5cdC8vIHRvIHJldHVybiBhbiBgPGltZz5gIEhUTUwgZWxlbWVudCB3aXRoIHRoZSBhcHByb3BpYXRlIGltYWdlIFVSTCBnaXZlbiBgY29vcmRzYC4gVGhlIGBkb25lYFxyXG5cdC8vIGNhbGxiYWNrIGlzIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBiZWVuIGxvYWRlZC5cclxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoY29vcmRzLCBkb25lKSB7XHJcblx0XHR2YXIgdGlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnQub24odGlsZSwgJ2xvYWQnLCBMLmJpbmQodGhpcy5fdGlsZU9uTG9hZCwgdGhpcywgZG9uZSwgdGlsZSkpO1xyXG5cdFx0TC5Eb21FdmVudC5vbih0aWxlLCAnZXJyb3InLCBMLmJpbmQodGhpcy5fdGlsZU9uRXJyb3IsIHRoaXMsIGRvbmUsIHRpbGUpKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmNyb3NzT3JpZ2luKSB7XHJcblx0XHRcdHRpbGUuY3Jvc3NPcmlnaW4gPSAnJztcclxuXHRcdH1cclxuXHJcblx0XHQvKlxyXG5cdFx0IEFsdCB0YWcgaXMgc2V0IHRvIGVtcHR5IHN0cmluZyB0byBrZWVwIHNjcmVlbiByZWFkZXJzIGZyb20gcmVhZGluZyBVUkwgYW5kIGZvciBjb21wbGlhbmNlIHJlYXNvbnNcclxuXHRcdCBodHRwOi8vd3d3LnczLm9yZy9UUi9XQ0FHMjAtVEVDSFMvSDY3XHJcblx0XHQqL1xyXG5cdFx0dGlsZS5hbHQgPSAnJztcclxuXHJcblx0XHQvKlxyXG5cdFx0IFNldCByb2xlPVwicHJlc2VudGF0aW9uXCIgdG8gZm9yY2Ugc2NyZWVuIHJlYWRlcnMgdG8gaWdub3JlIHRoaXNcclxuXHRcdCBodHRwczovL3d3dy53My5vcmcvVFIvd2FpLWFyaWEvcm9sZXMjdGV4dGFsdGVybmF0aXZlY29tcHV0YXRpb25cclxuXHRcdCovXHJcblx0XHR0aWxlLnNldEF0dHJpYnV0ZSgncm9sZScsICdwcmVzZW50YXRpb24nKTtcclxuXHJcblx0XHR0aWxlLnNyYyA9IHRoaXMuZ2V0VGlsZVVybChjb29yZHMpO1xyXG5cclxuXHRcdHJldHVybiB0aWxlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXHJcblx0Ly8gQHVuaW5oZXJpdGFibGVcclxuXHQvLyBMYXllcnMgZXh0ZW5kaW5nIGBUaWxlTGF5ZXJgIG1pZ2h0IHJlaW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgbWV0aG9kLlxyXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVVybChjb29yZHM6IE9iamVjdCk6IFN0cmluZ1xyXG5cdC8vIENhbGxlZCBvbmx5IGludGVybmFsbHksIHJldHVybnMgdGhlIFVSTCBmb3IgYSB0aWxlIGdpdmVuIGl0cyBjb29yZGluYXRlcy5cclxuXHQvLyBDbGFzc2VzIGV4dGVuZGluZyBgVGlsZUxheWVyYCBjYW4gb3ZlcnJpZGUgdGhpcyBmdW5jdGlvbiB0byBwcm92aWRlIGN1c3RvbSB0aWxlIFVSTCBuYW1pbmcgc2NoZW1lcy5cclxuXHRnZXRUaWxlVXJsOiBmdW5jdGlvbiAoY29vcmRzKSB7XHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0cjogTC5Ccm93c2VyLnJldGluYSA/ICdAMngnIDogJycsXHJcblx0XHRcdHM6IHRoaXMuX2dldFN1YmRvbWFpbihjb29yZHMpLFxyXG5cdFx0XHR4OiBjb29yZHMueCxcclxuXHRcdFx0eTogY29vcmRzLnksXHJcblx0XHRcdHo6IHRoaXMuX2dldFpvb21Gb3JVcmwoKVxyXG5cdFx0fTtcclxuXHRcdGlmICh0aGlzLl9tYXAgJiYgIXRoaXMuX21hcC5vcHRpb25zLmNycy5pbmZpbml0ZSkge1xyXG5cdFx0XHR2YXIgaW52ZXJ0ZWRZID0gdGhpcy5fZ2xvYmFsVGlsZVJhbmdlLm1heC55IC0gY29vcmRzLnk7XHJcblx0XHRcdGlmICh0aGlzLm9wdGlvbnMudG1zKSB7XHJcblx0XHRcdFx0ZGF0YVsneSddID0gaW52ZXJ0ZWRZO1xyXG5cdFx0XHR9XHJcblx0XHRcdGRhdGFbJy15J10gPSBpbnZlcnRlZFk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIEwuVXRpbC50ZW1wbGF0ZSh0aGlzLl91cmwsIEwuZXh0ZW5kKGRhdGEsIHRoaXMub3B0aW9ucykpO1xyXG5cdH0sXHJcblxyXG5cdF90aWxlT25Mb2FkOiBmdW5jdGlvbiAoZG9uZSwgdGlsZSkge1xyXG5cdFx0Ly8gRm9yIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzMzMzJcclxuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkpIHtcclxuXHRcdFx0c2V0VGltZW91dChMLmJpbmQoZG9uZSwgdGhpcywgbnVsbCwgdGlsZSksIDApO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZG9uZShudWxsLCB0aWxlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfdGlsZU9uRXJyb3I6IGZ1bmN0aW9uIChkb25lLCB0aWxlLCBlKSB7XHJcblx0XHR2YXIgZXJyb3JVcmwgPSB0aGlzLm9wdGlvbnMuZXJyb3JUaWxlVXJsO1xyXG5cdFx0aWYgKGVycm9yVXJsICYmIHRpbGUuc3JjICE9PSBlcnJvclVybCkge1xyXG5cdFx0XHR0aWxlLnNyYyA9IGVycm9yVXJsO1xyXG5cdFx0fVxyXG5cdFx0ZG9uZShlLCB0aWxlKTtcclxuXHR9LFxyXG5cclxuXHRnZXRUaWxlU2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcclxuXHRcdHRpbGVTaXplID0gTC5HcmlkTGF5ZXIucHJvdG90eXBlLmdldFRpbGVTaXplLmNhbGwodGhpcyksXHJcblx0XHR6b29tID0gdGhpcy5fdGlsZVpvb20gKyB0aGlzLm9wdGlvbnMuem9vbU9mZnNldCxcclxuXHRcdG1pbk5hdGl2ZVpvb20gPSB0aGlzLm9wdGlvbnMubWluTmF0aXZlWm9vbSxcclxuXHRcdG1heE5hdGl2ZVpvb20gPSB0aGlzLm9wdGlvbnMubWF4TmF0aXZlWm9vbTtcclxuXHJcblx0XHQvLyBkZWNyZWFzZSB0aWxlIHNpemUgd2hlbiBzY2FsaW5nIGJlbG93IG1pbk5hdGl2ZVpvb21cclxuXHRcdGlmIChtaW5OYXRpdmVab29tICE9PSBudWxsICYmIHpvb20gPCBtaW5OYXRpdmVab29tKSB7XHJcblx0XHRcdHJldHVybiB0aWxlU2l6ZS5kaXZpZGVCeShtYXAuZ2V0Wm9vbVNjYWxlKG1pbk5hdGl2ZVpvb20sIHpvb20pKS5yb3VuZCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIGluY3JlYXNlIHRpbGUgc2l6ZSB3aGVuIHNjYWxpbmcgYWJvdmUgbWF4TmF0aXZlWm9vbVxyXG5cdFx0aWYgKG1heE5hdGl2ZVpvb20gIT09IG51bGwgJiYgem9vbSA+IG1heE5hdGl2ZVpvb20pIHtcclxuXHRcdFx0cmV0dXJuIHRpbGVTaXplLmRpdmlkZUJ5KG1hcC5nZXRab29tU2NhbGUobWF4TmF0aXZlWm9vbSwgem9vbSkpLnJvdW5kKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRpbGVTaXplO1xyXG5cdH0sXHJcblxyXG5cdF9vblRpbGVSZW1vdmU6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRlLnRpbGUub25sb2FkID0gbnVsbDtcclxuXHR9LFxyXG5cclxuXHRfZ2V0Wm9vbUZvclVybDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHpvb20gPSB0aGlzLl90aWxlWm9vbSxcclxuXHRcdG1heFpvb20gPSB0aGlzLm9wdGlvbnMubWF4Wm9vbSxcclxuXHRcdHpvb21SZXZlcnNlID0gdGhpcy5vcHRpb25zLnpvb21SZXZlcnNlLFxyXG5cdFx0em9vbU9mZnNldCA9IHRoaXMub3B0aW9ucy56b29tT2Zmc2V0LFxyXG5cdFx0bWluTmF0aXZlWm9vbSA9IHRoaXMub3B0aW9ucy5taW5OYXRpdmVab29tLFxyXG5cdFx0bWF4TmF0aXZlWm9vbSA9IHRoaXMub3B0aW9ucy5tYXhOYXRpdmVab29tO1xyXG5cclxuXHRcdGlmICh6b29tUmV2ZXJzZSkge1xyXG5cdFx0XHR6b29tID0gbWF4Wm9vbSAtIHpvb207XHJcblx0XHR9XHJcblxyXG5cdFx0em9vbSArPSB6b29tT2Zmc2V0O1xyXG5cclxuXHRcdGlmIChtaW5OYXRpdmVab29tICE9PSBudWxsICYmIHpvb20gPCBtaW5OYXRpdmVab29tKSB7XHJcblx0XHRcdHJldHVybiBtaW5OYXRpdmVab29tO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChtYXhOYXRpdmVab29tICE9PSBudWxsICYmIHpvb20gPiBtYXhOYXRpdmVab29tKSB7XHJcblx0XHRcdHJldHVybiBtYXhOYXRpdmVab29tO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB6b29tO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRTdWJkb21haW46IGZ1bmN0aW9uICh0aWxlUG9pbnQpIHtcclxuXHRcdHZhciBpbmRleCA9IE1hdGguYWJzKHRpbGVQb2ludC54ICsgdGlsZVBvaW50LnkpICUgdGhpcy5vcHRpb25zLnN1YmRvbWFpbnMubGVuZ3RoO1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5zdWJkb21haW5zW2luZGV4XTtcclxuXHR9LFxyXG5cclxuXHQvLyBzdG9wcyBsb2FkaW5nIGFsbCB0aWxlcyBpbiB0aGUgYmFja2dyb3VuZCBsYXllclxyXG5cdF9hYm9ydExvYWRpbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBpLCB0aWxlO1xyXG5cdFx0Zm9yIChpIGluIHRoaXMuX3RpbGVzKSB7XHJcblx0XHRcdGlmICh0aGlzLl90aWxlc1tpXS5jb29yZHMueiAhPT0gdGhpcy5fdGlsZVpvb20pIHtcclxuXHRcdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNbaV0uZWw7XHJcblxyXG5cdFx0XHRcdHRpbGUub25sb2FkID0gTC5VdGlsLmZhbHNlRm47XHJcblx0XHRcdFx0dGlsZS5vbmVycm9yID0gTC5VdGlsLmZhbHNlRm47XHJcblxyXG5cdFx0XHRcdGlmICghdGlsZS5jb21wbGV0ZSkge1xyXG5cdFx0XHRcdFx0dGlsZS5zcmMgPSBMLlV0aWwuZW1wdHlJbWFnZVVybDtcclxuXHRcdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGlsZSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcblxyXG4vLyBAZmFjdG9yeSBMLnRpbGVsYXllcih1cmxUZW1wbGF0ZTogU3RyaW5nLCBvcHRpb25zPzogVGlsZUxheWVyIG9wdGlvbnMpXHJcbi8vIEluc3RhbnRpYXRlcyBhIHRpbGUgbGF5ZXIgb2JqZWN0IGdpdmVuIGEgYFVSTCB0ZW1wbGF0ZWAgYW5kIG9wdGlvbmFsbHkgYW4gb3B0aW9ucyBvYmplY3QuXHJcblxyXG5MLnRpbGVMYXllciA9IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuVGlsZUxheWVyKHVybCwgb3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBUaWxlTGF5ZXIuV01TXHJcbiAqIEBpbmhlcml0cyBUaWxlTGF5ZXJcclxuICogQGFrYSBMLlRpbGVMYXllci5XTVNcclxuICogVXNlZCB0byBkaXNwbGF5IFtXTVNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dlYl9NYXBfU2VydmljZSkgc2VydmljZXMgYXMgdGlsZSBsYXllcnMgb24gdGhlIG1hcC4gRXh0ZW5kcyBgVGlsZUxheWVyYC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIG5leHJhZCA9IEwudGlsZUxheWVyLndtcyhcImh0dHA6Ly9tZXNvbmV0LmFncm9uLmlhc3RhdGUuZWR1L2NnaS1iaW4vd21zL25leHJhZC9uMHIuY2dpXCIsIHtcclxuICogXHRsYXllcnM6ICduZXhyYWQtbjByLTkwMDkxMycsXHJcbiAqIFx0Zm9ybWF0OiAnaW1hZ2UvcG5nJyxcclxuICogXHR0cmFuc3BhcmVudDogdHJ1ZSxcclxuICogXHRhdHRyaWJ1dGlvbjogXCJXZWF0aGVyIGRhdGEgwqkgMjAxMiBJRU0gTmV4cmFkXCJcclxuICogfSk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuVGlsZUxheWVyLldNUyA9IEwuVGlsZUxheWVyLmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBUaWxlTGF5ZXIuV01TIG9wdGlvbnNcclxuXHQvLyBJZiBhbnkgY3VzdG9tIG9wdGlvbnMgbm90IGRvY3VtZW50ZWQgaGVyZSBhcmUgdXNlZCwgdGhleSB3aWxsIGJlIHNlbnQgdG8gdGhlXHJcblx0Ly8gV01TIHNlcnZlciBhcyBleHRyYSBwYXJhbWV0ZXJzIGluIGVhY2ggcmVxdWVzdCBVUkwuIFRoaXMgY2FuIGJlIHVzZWZ1bCBmb3JcclxuXHQvLyBbbm9uLXN0YW5kYXJkIHZlbmRvciBXTVMgcGFyYW1ldGVyc10oaHR0cDovL2RvY3MuZ2Vvc2VydmVyLm9yZy9zdGFibGUvZW4vdXNlci9zZXJ2aWNlcy93bXMvdmVuZG9yLmh0bWwpLlxyXG5cdGRlZmF1bHRXbXNQYXJhbXM6IHtcclxuXHRcdHNlcnZpY2U6ICdXTVMnLFxyXG5cdFx0cmVxdWVzdDogJ0dldE1hcCcsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBsYXllcnM6IFN0cmluZyA9ICcnXHJcblx0XHQvLyAqKihyZXF1aXJlZCkqKiBDb21tYS1zZXBhcmF0ZWQgbGlzdCBvZiBXTVMgbGF5ZXJzIHRvIHNob3cuXHJcblx0XHRsYXllcnM6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gc3R5bGVzOiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gQ29tbWEtc2VwYXJhdGVkIGxpc3Qgb2YgV01TIHN0eWxlcy5cclxuXHRcdHN0eWxlczogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBmb3JtYXQ6IFN0cmluZyA9ICdpbWFnZS9qcGVnJ1xyXG5cdFx0Ly8gV01TIGltYWdlIGZvcm1hdCAodXNlIGAnaW1hZ2UvcG5nJ2AgZm9yIGxheWVycyB3aXRoIHRyYW5zcGFyZW5jeSkuXHJcblx0XHRmb3JtYXQ6ICdpbWFnZS9qcGVnJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHRyYW5zcGFyZW50OiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIFdNUyBzZXJ2aWNlIHdpbGwgcmV0dXJuIGltYWdlcyB3aXRoIHRyYW5zcGFyZW5jeS5cclxuXHRcdHRyYW5zcGFyZW50OiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHZlcnNpb246IFN0cmluZyA9ICcxLjEuMSdcclxuXHRcdC8vIFZlcnNpb24gb2YgdGhlIFdNUyBzZXJ2aWNlIHRvIHVzZVxyXG5cdFx0dmVyc2lvbjogJzEuMS4xJ1xyXG5cdH0sXHJcblxyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gY3JzOiBDUlMgPSBudWxsXHJcblx0XHQvLyBDb29yZGluYXRlIFJlZmVyZW5jZSBTeXN0ZW0gdG8gdXNlIGZvciB0aGUgV01TIHJlcXVlc3RzLCBkZWZhdWx0cyB0b1xyXG5cdFx0Ly8gbWFwIENSUy4gRG9uJ3QgY2hhbmdlIHRoaXMgaWYgeW91J3JlIG5vdCBzdXJlIHdoYXQgaXQgbWVhbnMuXHJcblx0XHRjcnM6IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB1cHBlcmNhc2U6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCBXTVMgcmVxdWVzdCBwYXJhbWV0ZXIga2V5cyB3aWxsIGJlIHVwcGVyY2FzZS5cclxuXHRcdHVwcGVyY2FzZTogZmFsc2VcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAodXJsLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0dGhpcy5fdXJsID0gdXJsO1xyXG5cclxuXHRcdHZhciB3bXNQYXJhbXMgPSBMLmV4dGVuZCh7fSwgdGhpcy5kZWZhdWx0V21zUGFyYW1zKTtcclxuXHJcblx0XHQvLyBhbGwga2V5cyB0aGF0IGFyZSBub3QgVGlsZUxheWVyIG9wdGlvbnMgZ28gdG8gV01TIHBhcmFtc1xyXG5cdFx0Zm9yICh2YXIgaSBpbiBvcHRpb25zKSB7XHJcblx0XHRcdGlmICghKGkgaW4gdGhpcy5vcHRpb25zKSkge1xyXG5cdFx0XHRcdHdtc1BhcmFtc1tpXSA9IG9wdGlvbnNbaV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRvcHRpb25zID0gTC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHdtc1BhcmFtcy53aWR0aCA9IHdtc1BhcmFtcy5oZWlnaHQgPSBvcHRpb25zLnRpbGVTaXplICogKG9wdGlvbnMuZGV0ZWN0UmV0aW5hICYmIEwuQnJvd3Nlci5yZXRpbmEgPyAyIDogMSk7XHJcblxyXG5cdFx0dGhpcy53bXNQYXJhbXMgPSB3bXNQYXJhbXM7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHJcblx0XHR0aGlzLl9jcnMgPSB0aGlzLm9wdGlvbnMuY3JzIHx8IG1hcC5vcHRpb25zLmNycztcclxuXHRcdHRoaXMuX3dtc1ZlcnNpb24gPSBwYXJzZUZsb2F0KHRoaXMud21zUGFyYW1zLnZlcnNpb24pO1xyXG5cclxuXHRcdHZhciBwcm9qZWN0aW9uS2V5ID0gdGhpcy5fd21zVmVyc2lvbiA+PSAxLjMgPyAnY3JzJyA6ICdzcnMnO1xyXG5cdFx0dGhpcy53bXNQYXJhbXNbcHJvamVjdGlvbktleV0gPSB0aGlzLl9jcnMuY29kZTtcclxuXHJcblx0XHRMLlRpbGVMYXllci5wcm90b3R5cGUub25BZGQuY2FsbCh0aGlzLCBtYXApO1xyXG5cdH0sXHJcblxyXG5cdGdldFRpbGVVcmw6IGZ1bmN0aW9uIChjb29yZHMpIHtcclxuXHJcblx0XHR2YXIgdGlsZUJvdW5kcyA9IHRoaXMuX3RpbGVDb29yZHNUb0JvdW5kcyhjb29yZHMpLFxyXG5cdFx0ICAgIG53ID0gdGhpcy5fY3JzLnByb2plY3QodGlsZUJvdW5kcy5nZXROb3J0aFdlc3QoKSksXHJcblx0XHQgICAgc2UgPSB0aGlzLl9jcnMucHJvamVjdCh0aWxlQm91bmRzLmdldFNvdXRoRWFzdCgpKSxcclxuXHJcblx0XHQgICAgYmJveCA9ICh0aGlzLl93bXNWZXJzaW9uID49IDEuMyAmJiB0aGlzLl9jcnMgPT09IEwuQ1JTLkVQU0c0MzI2ID9cclxuXHRcdFx0ICAgIFtzZS55LCBudy54LCBudy55LCBzZS54XSA6XHJcblx0XHRcdCAgICBbbncueCwgc2UueSwgc2UueCwgbncueV0pLmpvaW4oJywnKSxcclxuXHJcblx0XHQgICAgdXJsID0gTC5UaWxlTGF5ZXIucHJvdG90eXBlLmdldFRpbGVVcmwuY2FsbCh0aGlzLCBjb29yZHMpO1xyXG5cclxuXHRcdHJldHVybiB1cmwgK1xyXG5cdFx0XHRMLlV0aWwuZ2V0UGFyYW1TdHJpbmcodGhpcy53bXNQYXJhbXMsIHVybCwgdGhpcy5vcHRpb25zLnVwcGVyY2FzZSkgK1xyXG5cdFx0XHQodGhpcy5vcHRpb25zLnVwcGVyY2FzZSA/ICcmQkJPWD0nIDogJyZiYm94PScpICsgYmJveDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFBhcmFtcyhwYXJhbXM6IE9iamVjdCwgbm9SZWRyYXc/OiBCb29sZWFuKTogdGhpc1xyXG5cdC8vIE1lcmdlcyBhbiBvYmplY3Qgd2l0aCB0aGUgbmV3IHBhcmFtZXRlcnMgYW5kIHJlLXJlcXVlc3RzIHRpbGVzIG9uIHRoZSBjdXJyZW50IHNjcmVlbiAodW5sZXNzIGBub1JlZHJhd2Agd2FzIHNldCB0byB0cnVlKS5cclxuXHRzZXRQYXJhbXM6IGZ1bmN0aW9uIChwYXJhbXMsIG5vUmVkcmF3KSB7XHJcblxyXG5cdFx0TC5leHRlbmQodGhpcy53bXNQYXJhbXMsIHBhcmFtcyk7XHJcblxyXG5cdFx0aWYgKCFub1JlZHJhdykge1xyXG5cdFx0XHR0aGlzLnJlZHJhdygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC50aWxlTGF5ZXIud21zKGJhc2VVcmw6IFN0cmluZywgb3B0aW9uczogVGlsZUxheWVyLldNUyBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYSBXTVMgdGlsZSBsYXllciBvYmplY3QgZ2l2ZW4gYSBiYXNlIFVSTCBvZiB0aGUgV01TIHNlcnZpY2UgYW5kIGEgV01TIHBhcmFtZXRlcnMvb3B0aW9ucyBvYmplY3QuXHJcbkwudGlsZUxheWVyLndtcyA9IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuVGlsZUxheWVyLldNUyh1cmwsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgSW1hZ2VPdmVybGF5XHJcbiAqIEBha2EgTC5JbWFnZU92ZXJsYXlcclxuICogQGluaGVyaXRzIEludGVyYWN0aXZlIGxheWVyXHJcbiAqXHJcbiAqIFVzZWQgdG8gbG9hZCBhbmQgZGlzcGxheSBhIHNpbmdsZSBpbWFnZSBvdmVyIHNwZWNpZmljIGJvdW5kcyBvZiB0aGUgbWFwLiBFeHRlbmRzIGBMYXllcmAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBpbWFnZVVybCA9ICdodHRwOi8vd3d3LmxpYi51dGV4YXMuZWR1L21hcHMvaGlzdG9yaWNhbC9uZXdhcmtfbmpfMTkyMi5qcGcnLFxyXG4gKiBcdGltYWdlQm91bmRzID0gW1s0MC43MTIyMTYsIC03NC4yMjY1NV0sIFs0MC43NzM5NDEsIC03NC4xMjU0NF1dO1xyXG4gKiBMLmltYWdlT3ZlcmxheShpbWFnZVVybCwgaW1hZ2VCb3VuZHMpLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuSW1hZ2VPdmVybGF5ID0gTC5MYXllci5leHRlbmQoe1xyXG5cclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgSW1hZ2VPdmVybGF5IG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIG9wYWNpdHk6IE51bWJlciA9IDEuMFxyXG5cdFx0Ly8gVGhlIG9wYWNpdHkgb2YgdGhlIGltYWdlIG92ZXJsYXkuXHJcblx0XHRvcGFjaXR5OiAxLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYWx0OiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gVGV4dCBmb3IgdGhlIGBhbHRgIGF0dHJpYnV0ZSBvZiB0aGUgaW1hZ2UgKHVzZWZ1bCBmb3IgYWNjZXNzaWJpbGl0eSkuXHJcblx0XHRhbHQ6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gaW50ZXJhY3RpdmU6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCB0aGUgaW1hZ2Ugb3ZlcmxheSB3aWxsIGVtaXQgW21vdXNlIGV2ZW50c10oI2ludGVyYWN0aXZlLWxheWVyKSB3aGVuIGNsaWNrZWQgb3IgaG92ZXJlZC5cclxuXHRcdGludGVyYWN0aXZlOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNyb3NzT3JpZ2luOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIHRydWUsIHRoZSBpbWFnZSB3aWxsIGhhdmUgaXRzIGNyb3NzT3JpZ2luIGF0dHJpYnV0ZSBzZXQgdG8gJycuIFRoaXMgaXMgbmVlZGVkIGlmIHlvdSB3YW50IHRvIGFjY2VzcyBpbWFnZSBwaXhlbCBkYXRhLlxyXG5cdFx0Y3Jvc3NPcmlnaW46IGZhbHNlXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKHVybCwgYm91bmRzLCBvcHRpb25zKSB7IC8vIChTdHJpbmcsIExhdExuZ0JvdW5kcywgT2JqZWN0KVxyXG5cdFx0dGhpcy5fdXJsID0gdXJsO1xyXG5cdFx0dGhpcy5fYm91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5faW1hZ2UpIHtcclxuXHRcdFx0dGhpcy5faW5pdEltYWdlKCk7XHJcblxyXG5cdFx0XHRpZiAodGhpcy5vcHRpb25zLm9wYWNpdHkgPCAxKSB7XHJcblx0XHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5pbnRlcmFjdGl2ZSkge1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5faW1hZ2UsICdsZWFmbGV0LWludGVyYWN0aXZlJyk7XHJcblx0XHRcdHRoaXMuYWRkSW50ZXJhY3RpdmVUYXJnZXQodGhpcy5faW1hZ2UpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2ltYWdlKTtcclxuXHRcdHRoaXMuX3Jlc2V0KCk7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5faW1hZ2UpO1xyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5pbnRlcmFjdGl2ZSkge1xyXG5cdFx0XHR0aGlzLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX2ltYWdlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIG9wYWNpdHkgb2YgdGhlIG92ZXJsYXkuXHJcblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKG9wYWNpdHkpIHtcclxuXHRcdHRoaXMub3B0aW9ucy5vcGFjaXR5ID0gb3BhY2l0eTtcclxuXHJcblx0XHRpZiAodGhpcy5faW1hZ2UpIHtcclxuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0c2V0U3R5bGU6IGZ1bmN0aW9uIChzdHlsZU9wdHMpIHtcclxuXHRcdGlmIChzdHlsZU9wdHMub3BhY2l0eSkge1xyXG5cdFx0XHR0aGlzLnNldE9wYWNpdHkoc3R5bGVPcHRzLm9wYWNpdHkpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvRnJvbnQoKTogdGhpc1xyXG5cdC8vIEJyaW5ncyB0aGUgbGF5ZXIgdG8gdGhlIHRvcCBvZiBhbGwgb3ZlcmxheXMuXHJcblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdEwuRG9tVXRpbC50b0Zyb250KHRoaXMuX2ltYWdlKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYnJpbmdUb0JhY2soKTogdGhpc1xyXG5cdC8vIEJyaW5ncyB0aGUgbGF5ZXIgdG8gdGhlIGJvdHRvbSBvZiBhbGwgb3ZlcmxheXMuXHJcblx0YnJpbmdUb0JhY2s6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0TC5Eb21VdGlsLnRvQmFjayh0aGlzLl9pbWFnZSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFVybCh1cmw6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBDaGFuZ2VzIHRoZSBVUkwgb2YgdGhlIGltYWdlLlxyXG5cdHNldFVybDogZnVuY3Rpb24gKHVybCkge1xyXG5cdFx0dGhpcy5fdXJsID0gdXJsO1xyXG5cclxuXHRcdGlmICh0aGlzLl9pbWFnZSkge1xyXG5cdFx0XHR0aGlzLl9pbWFnZS5zcmMgPSB1cmw7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldEJvdW5kcyhib3VuZHM6IExhdExuZ0JvdW5kcyk6IHRoaXNcclxuXHQvLyBVcGRhdGUgdGhlIGJvdW5kcyB0aGF0IHRoaXMgSW1hZ2VPdmVybGF5IGNvdmVyc1xyXG5cdHNldEJvdW5kczogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0dGhpcy5fYm91bmRzID0gYm91bmRzO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fcmVzZXQoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGV2ZW50cyA9IHtcclxuXHRcdFx0em9vbTogdGhpcy5fcmVzZXQsXHJcblx0XHRcdHZpZXdyZXNldDogdGhpcy5fcmVzZXRcclxuXHRcdH07XHJcblxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9hbmltYXRlWm9vbTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gZXZlbnRzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIEdldCB0aGUgYm91bmRzIHRoYXQgdGhpcyBJbWFnZU92ZXJsYXkgY292ZXJzXHJcblx0Z2V0Qm91bmRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fYm91bmRzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0RWxlbWVudCgpOiBIVE1MRWxlbWVudFxyXG5cdC8vIEdldCB0aGUgaW1nIGVsZW1lbnQgdGhhdCByZXByZXNlbnRzIHRoZSBJbWFnZU92ZXJsYXkgb24gdGhlIG1hcFxyXG5cdGdldEVsZW1lbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9pbWFnZTtcclxuXHR9LFxyXG5cclxuXHRfaW5pdEltYWdlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgaW1nID0gdGhpcy5faW1hZ2UgPSBMLkRvbVV0aWwuY3JlYXRlKCdpbWcnLFxyXG5cdFx0XHRcdCdsZWFmbGV0LWltYWdlLWxheWVyICcgKyAodGhpcy5fem9vbUFuaW1hdGVkID8gJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcgOiAnJykpO1xyXG5cclxuXHRcdGltZy5vbnNlbGVjdHN0YXJ0ID0gTC5VdGlsLmZhbHNlRm47XHJcblx0XHRpbWcub25tb3VzZW1vdmUgPSBMLlV0aWwuZmFsc2VGbjtcclxuXHJcblx0XHRpbWcub25sb2FkID0gTC5iaW5kKHRoaXMuZmlyZSwgdGhpcywgJ2xvYWQnKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmNyb3NzT3JpZ2luKSB7XHJcblx0XHRcdGltZy5jcm9zc09yaWdpbiA9ICcnO1xyXG5cdFx0fVxyXG5cclxuXHRcdGltZy5zcmMgPSB0aGlzLl91cmw7XHJcblx0XHRpbWcuYWx0ID0gdGhpcy5vcHRpb25zLmFsdDtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR2YXIgc2NhbGUgPSB0aGlzLl9tYXAuZ2V0Wm9vbVNjYWxlKGUuem9vbSksXHJcblx0XHQgICAgb2Zmc2V0ID0gdGhpcy5fbWFwLl9sYXRMbmdCb3VuZHNUb05ld0xheWVyQm91bmRzKHRoaXMuX2JvdW5kcywgZS56b29tLCBlLmNlbnRlcikubWluO1xyXG5cclxuXHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0odGhpcy5faW1hZ2UsIG9mZnNldCwgc2NhbGUpO1xyXG5cdH0sXHJcblxyXG5cdF9yZXNldDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGltYWdlID0gdGhpcy5faW1hZ2UsXHJcblx0XHQgICAgYm91bmRzID0gbmV3IEwuQm91bmRzKFxyXG5cdFx0ICAgICAgICB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2JvdW5kcy5nZXROb3J0aFdlc3QoKSksXHJcblx0XHQgICAgICAgIHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fYm91bmRzLmdldFNvdXRoRWFzdCgpKSksXHJcblx0XHQgICAgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XHJcblxyXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKGltYWdlLCBib3VuZHMubWluKTtcclxuXHJcblx0XHRpbWFnZS5zdHlsZS53aWR0aCAgPSBzaXplLnggKyAncHgnO1xyXG5cdFx0aW1hZ2Uuc3R5bGUuaGVpZ2h0ID0gc2l6ZS55ICsgJ3B4JztcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlT3BhY2l0eTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGhpcy5faW1hZ2UsIHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQGZhY3RvcnkgTC5pbWFnZU92ZXJsYXkoaW1hZ2VVcmw6IFN0cmluZywgYm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM/OiBJbWFnZU92ZXJsYXkgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGFuIGltYWdlIG92ZXJsYXkgb2JqZWN0IGdpdmVuIHRoZSBVUkwgb2YgdGhlIGltYWdlIGFuZCB0aGVcclxuLy8gZ2VvZ3JhcGhpY2FsIGJvdW5kcyBpdCBpcyB0aWVkIHRvLlxyXG5MLmltYWdlT3ZlcmxheSA9IGZ1bmN0aW9uICh1cmwsIGJvdW5kcywgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5JbWFnZU92ZXJsYXkodXJsLCBib3VuZHMsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgSWNvblxyXG4gKiBAYWthIEwuSWNvblxyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcclxuICpcclxuICogUmVwcmVzZW50cyBhbiBpY29uIHRvIHByb3ZpZGUgd2hlbiBjcmVhdGluZyBhIG1hcmtlci5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIG15SWNvbiA9IEwuaWNvbih7XHJcbiAqICAgICBpY29uVXJsOiAnbXktaWNvbi5wbmcnLFxyXG4gKiAgICAgaWNvblJldGluYVVybDogJ215LWljb25AMngucG5nJyxcclxuICogICAgIGljb25TaXplOiBbMzgsIDk1XSxcclxuICogICAgIGljb25BbmNob3I6IFsyMiwgOTRdLFxyXG4gKiAgICAgcG9wdXBBbmNob3I6IFstMywgLTc2XSxcclxuICogICAgIHNoYWRvd1VybDogJ215LWljb24tc2hhZG93LnBuZycsXHJcbiAqICAgICBzaGFkb3dSZXRpbmFVcmw6ICdteS1pY29uLXNoYWRvd0AyeC5wbmcnLFxyXG4gKiAgICAgc2hhZG93U2l6ZTogWzY4LCA5NV0sXHJcbiAqICAgICBzaGFkb3dBbmNob3I6IFsyMiwgOTRdXHJcbiAqIH0pO1xyXG4gKlxyXG4gKiBMLm1hcmtlcihbNTAuNTA1LCAzMC41N10sIHtpY29uOiBteUljb259KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICpcclxuICogYEwuSWNvbi5EZWZhdWx0YCBleHRlbmRzIGBMLkljb25gIGFuZCBpcyB0aGUgYmx1ZSBpY29uIExlYWZsZXQgdXNlcyBmb3IgbWFya2VycyBieSBkZWZhdWx0LlxyXG4gKlxyXG4gKi9cclxuXHJcbkwuSWNvbiA9IEwuQ2xhc3MuZXh0ZW5kKHtcclxuXHJcblx0LyogQHNlY3Rpb25cclxuXHQgKiBAYWthIEljb24gb3B0aW9uc1xyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBpY29uVXJsOiBTdHJpbmcgPSBudWxsXHJcblx0ICogKioocmVxdWlyZWQpKiogVGhlIFVSTCB0byB0aGUgaWNvbiBpbWFnZSAoYWJzb2x1dGUgb3IgcmVsYXRpdmUgdG8geW91ciBzY3JpcHQgcGF0aCkuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIGljb25SZXRpbmFVcmw6IFN0cmluZyA9IG51bGxcclxuXHQgKiBUaGUgVVJMIHRvIGEgcmV0aW5hIHNpemVkIHZlcnNpb24gb2YgdGhlIGljb24gaW1hZ2UgKGFic29sdXRlIG9yIHJlbGF0aXZlIHRvIHlvdXJcclxuXHQgKiBzY3JpcHQgcGF0aCkuIFVzZWQgZm9yIFJldGluYSBzY3JlZW4gZGV2aWNlcy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gaWNvblNpemU6IFBvaW50ID0gbnVsbFxyXG5cdCAqIFNpemUgb2YgdGhlIGljb24gaW1hZ2UgaW4gcGl4ZWxzLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBpY29uQW5jaG9yOiBQb2ludCA9IG51bGxcclxuXHQgKiBUaGUgY29vcmRpbmF0ZXMgb2YgdGhlIFwidGlwXCIgb2YgdGhlIGljb24gKHJlbGF0aXZlIHRvIGl0cyB0b3AgbGVmdCBjb3JuZXIpLiBUaGUgaWNvblxyXG5cdCAqIHdpbGwgYmUgYWxpZ25lZCBzbyB0aGF0IHRoaXMgcG9pbnQgaXMgYXQgdGhlIG1hcmtlcidzIGdlb2dyYXBoaWNhbCBsb2NhdGlvbi4gQ2VudGVyZWRcclxuXHQgKiBieSBkZWZhdWx0IGlmIHNpemUgaXMgc3BlY2lmaWVkLCBhbHNvIGNhbiBiZSBzZXQgaW4gQ1NTIHdpdGggbmVnYXRpdmUgbWFyZ2lucy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gcG9wdXBBbmNob3I6IFBvaW50ID0gbnVsbFxyXG5cdCAqIFRoZSBjb29yZGluYXRlcyBvZiB0aGUgcG9pbnQgZnJvbSB3aGljaCBwb3B1cHMgd2lsbCBcIm9wZW5cIiwgcmVsYXRpdmUgdG8gdGhlIGljb24gYW5jaG9yLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBzaGFkb3dVcmw6IFN0cmluZyA9IG51bGxcclxuXHQgKiBUaGUgVVJMIHRvIHRoZSBpY29uIHNoYWRvdyBpbWFnZS4gSWYgbm90IHNwZWNpZmllZCwgbm8gc2hhZG93IGltYWdlIHdpbGwgYmUgY3JlYXRlZC5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gc2hhZG93UmV0aW5hVXJsOiBTdHJpbmcgPSBudWxsXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHNoYWRvd1NpemU6IFBvaW50ID0gbnVsbFxyXG5cdCAqIFNpemUgb2YgdGhlIHNoYWRvdyBpbWFnZSBpbiBwaXhlbHMuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHNoYWRvd0FuY2hvcjogUG9pbnQgPSBudWxsXHJcblx0ICogVGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBcInRpcFwiIG9mIHRoZSBzaGFkb3cgKHJlbGF0aXZlIHRvIGl0cyB0b3AgbGVmdCBjb3JuZXIpICh0aGUgc2FtZVxyXG5cdCAqIGFzIGljb25BbmNob3IgaWYgbm90IHNwZWNpZmllZCkuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gJydcclxuXHQgKiBBIGN1c3RvbSBjbGFzcyBuYW1lIHRvIGFzc2lnbiB0byBib3RoIGljb24gYW5kIHNoYWRvdyBpbWFnZXMuIEVtcHR5IGJ5IGRlZmF1bHQuXHJcblx0ICovXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjcmVhdGVJY29uKG9sZEljb24/OiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gQ2FsbGVkIGludGVybmFsbHkgd2hlbiB0aGUgaWNvbiBoYXMgdG8gYmUgc2hvd24sIHJldHVybnMgYSBgPGltZz5gIEhUTUwgZWxlbWVudFxyXG5cdC8vIHN0eWxlZCBhY2NvcmRpbmcgdG8gdGhlIG9wdGlvbnMuXHJcblx0Y3JlYXRlSWNvbjogZnVuY3Rpb24gKG9sZEljb24pIHtcclxuXHRcdHJldHVybiB0aGlzLl9jcmVhdGVJY29uKCdpY29uJywgb2xkSWNvbik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjcmVhdGVTaGFkb3cob2xkSWNvbj86IEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBBcyBgY3JlYXRlSWNvbmAsIGJ1dCBmb3IgdGhlIHNoYWRvdyBiZW5lYXRoIGl0LlxyXG5cdGNyZWF0ZVNoYWRvdzogZnVuY3Rpb24gKG9sZEljb24pIHtcclxuXHRcdHJldHVybiB0aGlzLl9jcmVhdGVJY29uKCdzaGFkb3cnLCBvbGRJY29uKTtcclxuXHR9LFxyXG5cclxuXHRfY3JlYXRlSWNvbjogZnVuY3Rpb24gKG5hbWUsIG9sZEljb24pIHtcclxuXHRcdHZhciBzcmMgPSB0aGlzLl9nZXRJY29uVXJsKG5hbWUpO1xyXG5cclxuXHRcdGlmICghc3JjKSB7XHJcblx0XHRcdGlmIChuYW1lID09PSAnaWNvbicpIHtcclxuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ2ljb25Vcmwgbm90IHNldCBpbiBJY29uIG9wdGlvbnMgKHNlZSB0aGUgZG9jcykuJyk7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIG51bGw7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIGltZyA9IHRoaXMuX2NyZWF0ZUltZyhzcmMsIG9sZEljb24gJiYgb2xkSWNvbi50YWdOYW1lID09PSAnSU1HJyA/IG9sZEljb24gOiBudWxsKTtcclxuXHRcdHRoaXMuX3NldEljb25TdHlsZXMoaW1nLCBuYW1lKTtcclxuXHJcblx0XHRyZXR1cm4gaW1nO1xyXG5cdH0sXHJcblxyXG5cdF9zZXRJY29uU3R5bGVzOiBmdW5jdGlvbiAoaW1nLCBuYW1lKSB7XHJcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcclxuXHRcdHZhciBzaXplT3B0aW9uID0gb3B0aW9uc1tuYW1lICsgJ1NpemUnXTtcclxuXHJcblx0XHRpZiAodHlwZW9mIHNpemVPcHRpb24gPT09ICdudW1iZXInKSB7XHJcblx0XHRcdHNpemVPcHRpb24gPSBbc2l6ZU9wdGlvbiwgc2l6ZU9wdGlvbl07XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHNpemUgPSBMLnBvaW50KHNpemVPcHRpb24pLFxyXG5cdFx0ICAgIGFuY2hvciA9IEwucG9pbnQobmFtZSA9PT0gJ3NoYWRvdycgJiYgb3B0aW9ucy5zaGFkb3dBbmNob3IgfHwgb3B0aW9ucy5pY29uQW5jaG9yIHx8XHJcblx0XHQgICAgICAgICAgICBzaXplICYmIHNpemUuZGl2aWRlQnkoMiwgdHJ1ZSkpO1xyXG5cclxuXHRcdGltZy5jbGFzc05hbWUgPSAnbGVhZmxldC1tYXJrZXItJyArIG5hbWUgKyAnICcgKyAob3B0aW9ucy5jbGFzc05hbWUgfHwgJycpO1xyXG5cclxuXHRcdGlmIChhbmNob3IpIHtcclxuXHRcdFx0aW1nLnN0eWxlLm1hcmdpbkxlZnQgPSAoLWFuY2hvci54KSArICdweCc7XHJcblx0XHRcdGltZy5zdHlsZS5tYXJnaW5Ub3AgID0gKC1hbmNob3IueSkgKyAncHgnO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChzaXplKSB7XHJcblx0XHRcdGltZy5zdHlsZS53aWR0aCAgPSBzaXplLnggKyAncHgnO1xyXG5cdFx0XHRpbWcuc3R5bGUuaGVpZ2h0ID0gc2l6ZS55ICsgJ3B4JztcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfY3JlYXRlSW1nOiBmdW5jdGlvbiAoc3JjLCBlbCkge1xyXG5cdFx0ZWwgPSBlbCB8fCBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTtcclxuXHRcdGVsLnNyYyA9IHNyYztcclxuXHRcdHJldHVybiBlbDtcclxuXHR9LFxyXG5cclxuXHRfZ2V0SWNvblVybDogZnVuY3Rpb24gKG5hbWUpIHtcclxuXHRcdHJldHVybiBMLkJyb3dzZXIucmV0aW5hICYmIHRoaXMub3B0aW9uc1tuYW1lICsgJ1JldGluYVVybCddIHx8IHRoaXMub3B0aW9uc1tuYW1lICsgJ1VybCddO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5pY29uKG9wdGlvbnM6IEljb24gb3B0aW9ucylcclxuLy8gQ3JlYXRlcyBhbiBpY29uIGluc3RhbmNlIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXHJcbkwuaWNvbiA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkljb24ob3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAbWluaWNsYXNzIEljb24uRGVmYXVsdCAoSWNvbilcbiAqIEBha2EgTC5JY29uLkRlZmF1bHRcbiAqIEBzZWN0aW9uXG4gKlxuICogQSB0cml2aWFsIHN1YmNsYXNzIG9mIGBJY29uYCwgcmVwcmVzZW50cyB0aGUgaWNvbiB0byB1c2UgaW4gYE1hcmtlcmBzIHdoZW5cbiAqIG5vIGljb24gaXMgc3BlY2lmaWVkLiBQb2ludHMgdG8gdGhlIGJsdWUgbWFya2VyIGltYWdlIGRpc3RyaWJ1dGVkIHdpdGggTGVhZmxldFxuICogcmVsZWFzZXMuXG4gKlxuICogSW4gb3JkZXIgdG8gY3VzdG9taXplIHRoZSBkZWZhdWx0IGljb24sIGp1c3QgY2hhbmdlIHRoZSBwcm9wZXJ0aWVzIG9mIGBMLkljb24uRGVmYXVsdC5wcm90b3R5cGUub3B0aW9uc2BcbiAqICh3aGljaCBpcyBhIHNldCBvZiBgSWNvbiBvcHRpb25zYCkuXG4gKlxuICogSWYgeW91IHdhbnQgdG8gX2NvbXBsZXRlbHlfIHJlcGxhY2UgdGhlIGRlZmF1bHQgaWNvbiwgb3ZlcnJpZGUgdGhlXG4gKiBgTC5NYXJrZXIucHJvdG90eXBlLm9wdGlvbnMuaWNvbmAgd2l0aCB5b3VyIG93biBpY29uIGluc3RlYWQuXG4gKi9cblxuTC5JY29uLkRlZmF1bHQgPSBMLkljb24uZXh0ZW5kKHtcblxuXHRvcHRpb25zOiB7XG5cdFx0aWNvblVybDogICAgICAgJ21hcmtlci1pY29uLnBuZycsXG5cdFx0aWNvblJldGluYVVybDogJ21hcmtlci1pY29uLTJ4LnBuZycsXG5cdFx0c2hhZG93VXJsOiAgICAgJ21hcmtlci1zaGFkb3cucG5nJyxcblx0XHRpY29uU2l6ZTogICAgWzI1LCA0MV0sXG5cdFx0aWNvbkFuY2hvcjogIFsxMiwgNDFdLFxuXHRcdHBvcHVwQW5jaG9yOiBbMSwgLTM0XSxcblx0XHR0b29sdGlwQW5jaG9yOiBbMTYsIC0yOF0sXG5cdFx0c2hhZG93U2l6ZTogIFs0MSwgNDFdXG5cdH0sXG5cblx0X2dldEljb25Vcmw6IGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0aWYgKCFMLkljb24uRGVmYXVsdC5pbWFnZVBhdGgpIHtcdC8vIERlcHJlY2F0ZWQsIGJhY2t3YXJkcy1jb21wYXRpYmlsaXR5IG9ubHlcblx0XHRcdEwuSWNvbi5EZWZhdWx0LmltYWdlUGF0aCA9IHRoaXMuX2RldGVjdEljb25QYXRoKCk7XG5cdFx0fVxuXG5cdFx0Ly8gQG9wdGlvbiBpbWFnZVBhdGg6IFN0cmluZ1xuXHRcdC8vIGBMLkljb24uRGVmYXVsdGAgd2lsbCB0cnkgdG8gYXV0by1kZXRlY3QgdGhlIGFic29sdXRlIGxvY2F0aW9uIG9mIHRoZVxuXHRcdC8vIGJsdWUgaWNvbiBpbWFnZXMuIElmIHlvdSBhcmUgcGxhY2luZyB0aGVzZSBpbWFnZXMgaW4gYSBub24tc3RhbmRhcmRcblx0XHQvLyB3YXksIHNldCB0aGlzIG9wdGlvbiB0byBwb2ludCB0byB0aGUgcmlnaHQgYWJzb2x1dGUgcGF0aC5cblx0XHRyZXR1cm4gKHRoaXMub3B0aW9ucy5pbWFnZVBhdGggfHwgTC5JY29uLkRlZmF1bHQuaW1hZ2VQYXRoKSArIEwuSWNvbi5wcm90b3R5cGUuX2dldEljb25VcmwuY2FsbCh0aGlzLCBuYW1lKTtcblx0fSxcblxuXHRfZGV0ZWN0SWNvblBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgZWwgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAgJ2xlYWZsZXQtZGVmYXVsdC1pY29uLXBhdGgnLCBkb2N1bWVudC5ib2R5KTtcblx0XHR2YXIgcGF0aCA9IEwuRG9tVXRpbC5nZXRTdHlsZShlbCwgJ2JhY2tncm91bmQtaW1hZ2UnKSB8fFxuXHRcdCAgICAgICAgICAgTC5Eb21VdGlsLmdldFN0eWxlKGVsLCAnYmFja2dyb3VuZEltYWdlJyk7XHQvLyBJRThcblxuXHRcdGRvY3VtZW50LmJvZHkucmVtb3ZlQ2hpbGQoZWwpO1xuXG5cdFx0cmV0dXJuIHBhdGguaW5kZXhPZigndXJsJykgPT09IDAgP1xuXHRcdFx0cGF0aC5yZXBsYWNlKC9edXJsXFwoW1xcXCJcXCddPy8sICcnKS5yZXBsYWNlKC9tYXJrZXItaWNvblxcLnBuZ1tcXFwiXFwnXT9cXCkkLywgJycpIDogJyc7XG5cdH1cbn0pO1xuXG5cblxuLypcclxuICogQGNsYXNzIE1hcmtlclxyXG4gKiBAaW5oZXJpdHMgSW50ZXJhY3RpdmUgbGF5ZXJcclxuICogQGFrYSBMLk1hcmtlclxyXG4gKiBMLk1hcmtlciBpcyB1c2VkIHRvIGRpc3BsYXkgY2xpY2thYmxlL2RyYWdnYWJsZSBpY29ucyBvbiB0aGUgbWFwLiBFeHRlbmRzIGBMYXllcmAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIEwubWFya2VyKFs1MC41LCAzMC41XSkuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5NYXJrZXIgPSBMLkxheWVyLmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBNYXJrZXIgb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gaWNvbjogSWNvbiA9ICpcclxuXHRcdC8vIEljb24gY2xhc3MgdG8gdXNlIGZvciByZW5kZXJpbmcgdGhlIG1hcmtlci4gU2VlIFtJY29uIGRvY3VtZW50YXRpb25dKCNMLkljb24pIGZvciBkZXRhaWxzIG9uIGhvdyB0byBjdXN0b21pemUgdGhlIG1hcmtlciBpY29uLiBJZiBub3Qgc3BlY2lmaWVkLCBhIG5ldyBgTC5JY29uLkRlZmF1bHRgIGlzIHVzZWQuXHJcblx0XHRpY29uOiBuZXcgTC5JY29uLkRlZmF1bHQoKSxcclxuXHJcblx0XHQvLyBPcHRpb24gaW5oZXJpdGVkIGZyb20gXCJJbnRlcmFjdGl2ZSBsYXllclwiIGFic3RyYWN0IGNsYXNzXHJcblx0XHRpbnRlcmFjdGl2ZTogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGRyYWdnYWJsZTogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBXaGV0aGVyIHRoZSBtYXJrZXIgaXMgZHJhZ2dhYmxlIHdpdGggbW91c2UvdG91Y2ggb3Igbm90LlxyXG5cdFx0ZHJhZ2dhYmxlOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGtleWJvYXJkOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFya2VyIGNhbiBiZSB0YWJiZWQgdG8gd2l0aCBhIGtleWJvYXJkIGFuZCBjbGlja2VkIGJ5IHByZXNzaW5nIGVudGVyLlxyXG5cdFx0a2V5Ym9hcmQ6IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0aXRsZTogU3RyaW5nID0gJydcclxuXHRcdC8vIFRleHQgZm9yIHRoZSBicm93c2VyIHRvb2x0aXAgdGhhdCBhcHBlYXIgb24gbWFya2VyIGhvdmVyIChubyB0b29sdGlwIGJ5IGRlZmF1bHQpLlxyXG5cdFx0dGl0bGU6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYWx0OiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gVGV4dCBmb3IgdGhlIGBhbHRgIGF0dHJpYnV0ZSBvZiB0aGUgaWNvbiBpbWFnZSAodXNlZnVsIGZvciBhY2Nlc3NpYmlsaXR5KS5cclxuXHRcdGFsdDogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6SW5kZXhPZmZzZXQ6IE51bWJlciA9IDBcclxuXHRcdC8vIEJ5IGRlZmF1bHQsIG1hcmtlciBpbWFnZXMgekluZGV4IGlzIHNldCBhdXRvbWF0aWNhbGx5IGJhc2VkIG9uIGl0cyBsYXRpdHVkZS4gVXNlIHRoaXMgb3B0aW9uIGlmIHlvdSB3YW50IHRvIHB1dCB0aGUgbWFya2VyIG9uIHRvcCBvZiBhbGwgb3RoZXJzIChvciBiZWxvdyksIHNwZWNpZnlpbmcgYSBoaWdoIHZhbHVlIGxpa2UgYDEwMDBgIChvciBoaWdoIG5lZ2F0aXZlIHZhbHVlLCByZXNwZWN0aXZlbHkpLlxyXG5cdFx0ekluZGV4T2Zmc2V0OiAwLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMS4wXHJcblx0XHQvLyBUaGUgb3BhY2l0eSBvZiB0aGUgbWFya2VyLlxyXG5cdFx0b3BhY2l0eTogMSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHJpc2VPbkhvdmVyOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIG1hcmtlciB3aWxsIGdldCBvbiB0b3Agb2Ygb3RoZXJzIHdoZW4geW91IGhvdmVyIHRoZSBtb3VzZSBvdmVyIGl0LlxyXG5cdFx0cmlzZU9uSG92ZXI6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gcmlzZU9mZnNldDogTnVtYmVyID0gMjUwXHJcblx0XHQvLyBUaGUgei1pbmRleCBvZmZzZXQgdXNlZCBmb3IgdGhlIGByaXNlT25Ib3ZlcmAgZmVhdHVyZS5cclxuXHRcdHJpc2VPZmZzZXQ6IDI1MCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHBhbmU6IFN0cmluZyA9ICdtYXJrZXJQYW5lJ1xyXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgbWFya2VycyBpY29uIHdpbGwgYmUgYWRkZWQuXHJcblx0XHRwYW5lOiAnbWFya2VyUGFuZScsXHJcblxyXG5cdFx0Ly8gRklYTUU6IHNoYWRvd1BhbmUgaXMgbm8gbG9uZ2VyIGEgdmFsaWQgb3B0aW9uXHJcblx0XHRub25CdWJibGluZ0V2ZW50czogWydjbGljaycsICdkYmxjbGljaycsICdtb3VzZW92ZXInLCAnbW91c2VvdXQnLCAnY29udGV4dG1lbnUnXVxyXG5cdH0sXHJcblxyXG5cdC8qIEBzZWN0aW9uXHJcblx0ICpcclxuXHQgKiBJbiBhZGRpdGlvbiB0byBbc2hhcmVkIGxheWVyIG1ldGhvZHNdKCNMYXllcikgbGlrZSBgYWRkVG8oKWAgYW5kIGByZW1vdmUoKWAgYW5kIFtwb3B1cCBtZXRob2RzXSgjUG9wdXApIGxpa2UgYmluZFBvcHVwKCkgeW91IGNhbiBhbHNvIHVzZSB0aGUgZm9sbG93aW5nIG1ldGhvZHM6XHJcblx0ICovXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcclxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHRcdHRoaXMuX2xhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHRoaXMuX3pvb21BbmltYXRlZCA9IHRoaXMuX3pvb21BbmltYXRlZCAmJiBtYXAub3B0aW9ucy5tYXJrZXJab29tQW5pbWF0aW9uO1xyXG5cclxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcclxuXHRcdFx0bWFwLm9uKCd6b29tYW5pbScsIHRoaXMuX2FuaW1hdGVab29tLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9pbml0SWNvbigpO1xyXG5cdFx0dGhpcy51cGRhdGUoKTtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0aWYgKHRoaXMuZHJhZ2dpbmcgJiYgdGhpcy5kcmFnZ2luZy5lbmFibGVkKCkpIHtcclxuXHRcdFx0dGhpcy5vcHRpb25zLmRyYWdnYWJsZSA9IHRydWU7XHJcblx0XHRcdHRoaXMuZHJhZ2dpbmcucmVtb3ZlSG9va3MoKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdG1hcC5vZmYoJ3pvb21hbmltJywgdGhpcy5fYW5pbWF0ZVpvb20sIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX3JlbW92ZUljb24oKTtcclxuXHRcdHRoaXMuX3JlbW92ZVNoYWRvdygpO1xyXG5cdH0sXHJcblxyXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHtcclxuXHRcdFx0em9vbTogdGhpcy51cGRhdGUsXHJcblx0XHRcdHZpZXdyZXNldDogdGhpcy51cGRhdGVcclxuXHRcdH07XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRMYXRMbmc6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgZ2VvZ3JhcGhpY2FsIHBvc2l0aW9uIG9mIHRoZSBtYXJrZXIuXHJcblx0Z2V0TGF0TG5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbGF0bG5nO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TGF0TG5nKGxhdGxuZzogTGF0TG5nKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgdGhlIG1hcmtlciBwb3NpdGlvbiB0byB0aGUgZ2l2ZW4gcG9pbnQuXHJcblx0c2V0TGF0TG5nOiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHR2YXIgb2xkTGF0TG5nID0gdGhpcy5fbGF0bG5nO1xyXG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcclxuXHRcdHRoaXMudXBkYXRlKCk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IG1vdmU6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXJrZXIgaXMgbW92ZWQgdmlhIFtgc2V0TGF0TG5nYF0oI21hcmtlci1zZXRsYXRsbmcpIG9yIGJ5IFtkcmFnZ2luZ10oI21hcmtlci1kcmFnZ2luZykuIE9sZCBhbmQgbmV3IGNvb3JkaW5hdGVzIGFyZSBpbmNsdWRlZCBpbiBldmVudCBhcmd1bWVudHMgYXMgYG9sZExhdExuZ2AsIGBsYXRsbmdgLlxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZScsIHtvbGRMYXRMbmc6IG9sZExhdExuZywgbGF0bG5nOiB0aGlzLl9sYXRsbmd9KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFpJbmRleE9mZnNldChvZmZzZXQ6IE51bWJlcik6IHRoaXNcclxuXHQvLyBDaGFuZ2VzIHRoZSBbekluZGV4IG9mZnNldF0oI21hcmtlci16aW5kZXhvZmZzZXQpIG9mIHRoZSBtYXJrZXIuXHJcblx0c2V0WkluZGV4T2Zmc2V0OiBmdW5jdGlvbiAob2Zmc2V0KSB7XHJcblx0XHR0aGlzLm9wdGlvbnMuekluZGV4T2Zmc2V0ID0gb2Zmc2V0O1xyXG5cdFx0cmV0dXJuIHRoaXMudXBkYXRlKCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRJY29uKGljb246IEljb24pOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyB0aGUgbWFya2VyIGljb24uXHJcblx0c2V0SWNvbjogZnVuY3Rpb24gKGljb24pIHtcclxuXHJcblx0XHR0aGlzLm9wdGlvbnMuaWNvbiA9IGljb247XHJcblxyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0aGlzLl9pbml0SWNvbigpO1xyXG5cdFx0XHR0aGlzLnVwZGF0ZSgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHR0aGlzLmJpbmRQb3B1cCh0aGlzLl9wb3B1cCwgdGhpcy5fcG9wdXAub3B0aW9ucyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Z2V0RWxlbWVudDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2ljb247XHJcblx0fSxcclxuXHJcblx0dXBkYXRlOiBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2ljb24pIHtcclxuXHRcdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fbGF0bG5nKS5yb3VuZCgpO1xyXG5cdFx0XHR0aGlzLl9zZXRQb3MocG9zKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfaW5pdEljb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBvcHRpb25zID0gdGhpcy5vcHRpb25zLFxyXG5cdFx0ICAgIGNsYXNzVG9BZGQgPSAnbGVhZmxldC16b29tLScgKyAodGhpcy5fem9vbUFuaW1hdGVkID8gJ2FuaW1hdGVkJyA6ICdoaWRlJyk7XHJcblxyXG5cdFx0dmFyIGljb24gPSBvcHRpb25zLmljb24uY3JlYXRlSWNvbih0aGlzLl9pY29uKSxcclxuXHRcdCAgICBhZGRJY29uID0gZmFsc2U7XHJcblxyXG5cdFx0Ly8gaWYgd2UncmUgbm90IHJldXNpbmcgdGhlIGljb24sIHJlbW92ZSB0aGUgb2xkIG9uZSBhbmQgaW5pdCBuZXcgb25lXHJcblx0XHRpZiAoaWNvbiAhPT0gdGhpcy5faWNvbikge1xyXG5cdFx0XHRpZiAodGhpcy5faWNvbikge1xyXG5cdFx0XHRcdHRoaXMuX3JlbW92ZUljb24oKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRhZGRJY29uID0gdHJ1ZTtcclxuXHJcblx0XHRcdGlmIChvcHRpb25zLnRpdGxlKSB7XHJcblx0XHRcdFx0aWNvbi50aXRsZSA9IG9wdGlvbnMudGl0bGU7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKG9wdGlvbnMuYWx0KSB7XHJcblx0XHRcdFx0aWNvbi5hbHQgPSBvcHRpb25zLmFsdDtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhpY29uLCBjbGFzc1RvQWRkKTtcclxuXHJcblx0XHRpZiAob3B0aW9ucy5rZXlib2FyZCkge1xyXG5cdFx0XHRpY29uLnRhYkluZGV4ID0gJzAnO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2ljb24gPSBpY29uO1xyXG5cclxuXHRcdGlmIChvcHRpb25zLnJpc2VPbkhvdmVyKSB7XHJcblx0XHRcdHRoaXMub24oe1xyXG5cdFx0XHRcdG1vdXNlb3ZlcjogdGhpcy5fYnJpbmdUb0Zyb250LFxyXG5cdFx0XHRcdG1vdXNlb3V0OiB0aGlzLl9yZXNldFpJbmRleFxyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgbmV3U2hhZG93ID0gb3B0aW9ucy5pY29uLmNyZWF0ZVNoYWRvdyh0aGlzLl9zaGFkb3cpLFxyXG5cdFx0ICAgIGFkZFNoYWRvdyA9IGZhbHNlO1xyXG5cclxuXHRcdGlmIChuZXdTaGFkb3cgIT09IHRoaXMuX3NoYWRvdykge1xyXG5cdFx0XHR0aGlzLl9yZW1vdmVTaGFkb3coKTtcclxuXHRcdFx0YWRkU2hhZG93ID0gdHJ1ZTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAobmV3U2hhZG93KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhuZXdTaGFkb3csIGNsYXNzVG9BZGQpO1xyXG5cdFx0XHRuZXdTaGFkb3cuYWx0ID0gJyc7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9zaGFkb3cgPSBuZXdTaGFkb3c7XHJcblxyXG5cclxuXHRcdGlmIChvcHRpb25zLm9wYWNpdHkgPCAxKSB7XHJcblx0XHRcdHRoaXMuX3VwZGF0ZU9wYWNpdHkoKTtcclxuXHRcdH1cclxuXHJcblxyXG5cdFx0aWYgKGFkZEljb24pIHtcclxuXHRcdFx0dGhpcy5nZXRQYW5lKCkuYXBwZW5kQ2hpbGQodGhpcy5faWNvbik7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9pbml0SW50ZXJhY3Rpb24oKTtcclxuXHRcdGlmIChuZXdTaGFkb3cgJiYgYWRkU2hhZG93KSB7XHJcblx0XHRcdHRoaXMuZ2V0UGFuZSgnc2hhZG93UGFuZScpLmFwcGVuZENoaWxkKHRoaXMuX3NoYWRvdyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3JlbW92ZUljb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLm9wdGlvbnMucmlzZU9uSG92ZXIpIHtcclxuXHRcdFx0dGhpcy5vZmYoe1xyXG5cdFx0XHRcdG1vdXNlb3ZlcjogdGhpcy5fYnJpbmdUb0Zyb250LFxyXG5cdFx0XHRcdG1vdXNlb3V0OiB0aGlzLl9yZXNldFpJbmRleFxyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwucmVtb3ZlKHRoaXMuX2ljb24pO1xyXG5cdFx0dGhpcy5yZW1vdmVJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl9pY29uKTtcclxuXHJcblx0XHR0aGlzLl9pY29uID0gbnVsbDtcclxuXHR9LFxyXG5cclxuXHRfcmVtb3ZlU2hhZG93OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fc2hhZG93KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fc2hhZG93KTtcclxuXHRcdH1cclxuXHRcdHRoaXMuX3NoYWRvdyA9IG51bGw7XHJcblx0fSxcclxuXHJcblx0X3NldFBvczogZnVuY3Rpb24gKHBvcykge1xyXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2ljb24sIHBvcyk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3NoYWRvdykge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fc2hhZG93LCBwb3MpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX3pJbmRleCA9IHBvcy55ICsgdGhpcy5vcHRpb25zLnpJbmRleE9mZnNldDtcclxuXHJcblx0XHR0aGlzLl9yZXNldFpJbmRleCgpO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVaSW5kZXg6IGZ1bmN0aW9uIChvZmZzZXQpIHtcclxuXHRcdHRoaXMuX2ljb24uc3R5bGUuekluZGV4ID0gdGhpcy5fekluZGV4ICsgb2Zmc2V0O1xyXG5cdH0sXHJcblxyXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKG9wdCkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5fbGF0TG5nVG9OZXdMYXllclBvaW50KHRoaXMuX2xhdGxuZywgb3B0Lnpvb20sIG9wdC5jZW50ZXIpLnJvdW5kKCk7XHJcblxyXG5cdFx0dGhpcy5fc2V0UG9zKHBvcyk7XHJcblx0fSxcclxuXHJcblx0X2luaXRJbnRlcmFjdGlvbjogZnVuY3Rpb24gKCkge1xyXG5cclxuXHRcdGlmICghdGhpcy5vcHRpb25zLmludGVyYWN0aXZlKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9pY29uLCAnbGVhZmxldC1pbnRlcmFjdGl2ZScpO1xyXG5cclxuXHRcdHRoaXMuYWRkSW50ZXJhY3RpdmVUYXJnZXQodGhpcy5faWNvbik7XHJcblxyXG5cdFx0aWYgKEwuSGFuZGxlci5NYXJrZXJEcmFnKSB7XHJcblx0XHRcdHZhciBkcmFnZ2FibGUgPSB0aGlzLm9wdGlvbnMuZHJhZ2dhYmxlO1xyXG5cdFx0XHRpZiAodGhpcy5kcmFnZ2luZykge1xyXG5cdFx0XHRcdGRyYWdnYWJsZSA9IHRoaXMuZHJhZ2dpbmcuZW5hYmxlZCgpO1xyXG5cdFx0XHRcdHRoaXMuZHJhZ2dpbmcuZGlzYWJsZSgpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR0aGlzLmRyYWdnaW5nID0gbmV3IEwuSGFuZGxlci5NYXJrZXJEcmFnKHRoaXMpO1xyXG5cclxuXHRcdFx0aWYgKGRyYWdnYWJsZSkge1xyXG5cdFx0XHRcdHRoaXMuZHJhZ2dpbmcuZW5hYmxlKCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgdGhlIG9wYWNpdHkgb2YgdGhlIG1hcmtlci5cclxuXHRzZXRPcGFjaXR5OiBmdW5jdGlvbiAob3BhY2l0eSkge1xyXG5cdFx0dGhpcy5vcHRpb25zLm9wYWNpdHkgPSBvcGFjaXR5O1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0aGlzLl91cGRhdGVPcGFjaXR5KCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZU9wYWNpdHk6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBvcGFjaXR5ID0gdGhpcy5vcHRpb25zLm9wYWNpdHk7XHJcblxyXG5cdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGhpcy5faWNvbiwgb3BhY2l0eSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3NoYWRvdykge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9zaGFkb3csIG9wYWNpdHkpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9icmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCh0aGlzLm9wdGlvbnMucmlzZU9mZnNldCk7XHJcblx0fSxcclxuXHJcblx0X3Jlc2V0WkluZGV4OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl91cGRhdGVaSW5kZXgoMCk7XHJcblx0fSxcclxuXHJcblx0X2dldFBvcHVwQW5jaG9yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLmljb24ub3B0aW9ucy5wb3B1cEFuY2hvciB8fCBbMCwgMF07XHJcblx0fSxcclxuXHJcblx0X2dldFRvb2x0aXBBbmNob3I6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuaWNvbi5vcHRpb25zLnRvb2x0aXBBbmNob3IgfHwgWzAsIDBdO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gZmFjdG9yeSBMLm1hcmtlcihsYXRsbmc6IExhdExuZywgb3B0aW9ucz8gOiBNYXJrZXIgb3B0aW9ucylcclxuXHJcbi8vIEBmYWN0b3J5IEwubWFya2VyKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPyA6IE1hcmtlciBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYSBNYXJrZXIgb2JqZWN0IGdpdmVuIGEgZ2VvZ3JhcGhpY2FsIHBvaW50IGFuZCBvcHRpb25hbGx5IGFuIG9wdGlvbnMgb2JqZWN0LlxyXG5MLm1hcmtlciA9IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuTWFya2VyKGxhdGxuZywgb3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgRGl2SWNvblxuICogQGFrYSBMLkRpdkljb25cbiAqIEBpbmhlcml0cyBJY29uXG4gKlxuICogUmVwcmVzZW50cyBhIGxpZ2h0d2VpZ2h0IGljb24gZm9yIG1hcmtlcnMgdGhhdCB1c2VzIGEgc2ltcGxlIGA8ZGl2PmBcbiAqIGVsZW1lbnQgaW5zdGVhZCBvZiBhbiBpbWFnZS4gSW5oZXJpdHMgZnJvbSBgSWNvbmAgYnV0IGlnbm9yZXMgdGhlIGBpY29uVXJsYCBhbmQgc2hhZG93IG9wdGlvbnMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYGpzXG4gKiB2YXIgbXlJY29uID0gTC5kaXZJY29uKHtjbGFzc05hbWU6ICdteS1kaXYtaWNvbid9KTtcbiAqIC8vIHlvdSBjYW4gc2V0IC5teS1kaXYtaWNvbiBzdHlsZXMgaW4gQ1NTXG4gKlxuICogTC5tYXJrZXIoWzUwLjUwNSwgMzAuNTddLCB7aWNvbjogbXlJY29ufSkuYWRkVG8obWFwKTtcbiAqIGBgYFxuICpcbiAqIEJ5IGRlZmF1bHQsIGl0IGhhcyBhICdsZWFmbGV0LWRpdi1pY29uJyBDU1MgY2xhc3MgYW5kIGlzIHN0eWxlZCBhcyBhIGxpdHRsZSB3aGl0ZSBzcXVhcmUgd2l0aCBhIHNoYWRvdy5cbiAqL1xuXG5MLkRpdkljb24gPSBMLkljb24uZXh0ZW5kKHtcblx0b3B0aW9uczoge1xuXHRcdC8vIEBzZWN0aW9uXG5cdFx0Ly8gQGFrYSBEaXZJY29uIG9wdGlvbnNcblx0XHRpY29uU2l6ZTogWzEyLCAxMl0sIC8vIGFsc28gY2FuIGJlIHNldCB0aHJvdWdoIENTU1xuXG5cdFx0Ly8gaWNvbkFuY2hvcjogKFBvaW50KSxcblx0XHQvLyBwb3B1cEFuY2hvcjogKFBvaW50KSxcblxuXHRcdC8vIEBvcHRpb24gaHRtbDogU3RyaW5nID0gJydcblx0XHQvLyBDdXN0b20gSFRNTCBjb2RlIHRvIHB1dCBpbnNpZGUgdGhlIGRpdiBlbGVtZW50LCBlbXB0eSBieSBkZWZhdWx0LlxuXHRcdGh0bWw6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBiZ1BvczogUG9pbnQgPSBbMCwgMF1cblx0XHQvLyBPcHRpb25hbCByZWxhdGl2ZSBwb3NpdGlvbiBvZiB0aGUgYmFja2dyb3VuZCwgaW4gcGl4ZWxzXG5cdFx0YmdQb3M6IG51bGwsXG5cblx0XHRjbGFzc05hbWU6ICdsZWFmbGV0LWRpdi1pY29uJ1xuXHR9LFxuXG5cdGNyZWF0ZUljb246IGZ1bmN0aW9uIChvbGRJY29uKSB7XG5cdFx0dmFyIGRpdiA9IChvbGRJY29uICYmIG9sZEljb24udGFnTmFtZSA9PT0gJ0RJVicpID8gb2xkSWNvbiA6IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpLFxuXHRcdCAgICBvcHRpb25zID0gdGhpcy5vcHRpb25zO1xuXG5cdFx0ZGl2LmlubmVySFRNTCA9IG9wdGlvbnMuaHRtbCAhPT0gZmFsc2UgPyBvcHRpb25zLmh0bWwgOiAnJztcblxuXHRcdGlmIChvcHRpb25zLmJnUG9zKSB7XG5cdFx0XHR2YXIgYmdQb3MgPSBMLnBvaW50KG9wdGlvbnMuYmdQb3MpO1xuXHRcdFx0ZGl2LnN0eWxlLmJhY2tncm91bmRQb3NpdGlvbiA9ICgtYmdQb3MueCkgKyAncHggJyArICgtYmdQb3MueSkgKyAncHgnO1xuXHRcdH1cblx0XHR0aGlzLl9zZXRJY29uU3R5bGVzKGRpdiwgJ2ljb24nKTtcblxuXHRcdHJldHVybiBkaXY7XG5cdH0sXG5cblx0Y3JlYXRlU2hhZG93OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cbn0pO1xuXG4vLyBAZmFjdG9yeSBMLmRpdkljb24ob3B0aW9uczogRGl2SWNvbiBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhIGBEaXZJY29uYCBpbnN0YW5jZSB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuTC5kaXZJY29uID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcblx0cmV0dXJuIG5ldyBMLkRpdkljb24ob3B0aW9ucyk7XG59O1xuXG5cblxuLypcclxuICogQGNsYXNzIERpdk92ZXJsYXlcclxuICogQGluaGVyaXRzIExheWVyXHJcbiAqIEBha2EgTC5EaXZPdmVybGF5XHJcbiAqIEJhc2UgbW9kZWwgZm9yIEwuUG9wdXAgYW5kIEwuVG9vbHRpcC4gSW5oZXJpdCBmcm9tIGl0IGZvciBjdXN0b20gcG9wdXAgbGlrZSBwbHVnaW5zLlxyXG4gKi9cclxuXHJcbi8vIEBuYW1lc3BhY2UgRGl2T3ZlcmxheVxyXG5MLkRpdk92ZXJsYXkgPSBMLkxheWVyLmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBEaXZPdmVybGF5IG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIG9mZnNldDogUG9pbnQgPSBQb2ludCgwLCA3KVxyXG5cdFx0Ly8gVGhlIG9mZnNldCBvZiB0aGUgcG9wdXAgcG9zaXRpb24uIFVzZWZ1bCB0byBjb250cm9sIHRoZSBhbmNob3JcclxuXHRcdC8vIG9mIHRoZSBwb3B1cCB3aGVuIG9wZW5pbmcgaXQgb24gc29tZSBvdmVybGF5cy5cclxuXHRcdG9mZnNldDogWzAsIDddLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY2xhc3NOYW1lOiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gQSBjdXN0b20gQ1NTIGNsYXNzIG5hbWUgdG8gYXNzaWduIHRvIHRoZSBwb3B1cC5cclxuXHRcdGNsYXNzTmFtZTogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAncG9wdXBQYW5lJ1xyXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgcG9wdXAgd2lsbCBiZSBhZGRlZC5cclxuXHRcdHBhbmU6ICdwb3B1cFBhbmUnXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG9wdGlvbnMsIHNvdXJjZSkge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX3NvdXJjZSA9IHNvdXJjZTtcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dGhpcy5fem9vbUFuaW1hdGVkID0gbWFwLl96b29tQW5pbWF0ZWQ7XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9jb250YWluZXIpIHtcclxuXHRcdFx0dGhpcy5faW5pdExheW91dCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChtYXAuX2ZhZGVBbmltYXRlZCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIDApO1xyXG5cdFx0fVxyXG5cclxuXHRcdGNsZWFyVGltZW91dCh0aGlzLl9yZW1vdmVUaW1lb3V0KTtcclxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHR0aGlzLnVwZGF0ZSgpO1xyXG5cclxuXHRcdGlmIChtYXAuX2ZhZGVBbmltYXRlZCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIDEpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuYnJpbmdUb0Zyb250KCk7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdGlmIChtYXAuX2ZhZGVBbmltYXRlZCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIDApO1xyXG5cdFx0XHR0aGlzLl9yZW1vdmVUaW1lb3V0ID0gc2V0VGltZW91dChMLmJpbmQoTC5Eb21VdGlsLnJlbW92ZSwgTC5Eb21VdGlsLCB0aGlzLl9jb250YWluZXIpLCAyMDApO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBuYW1lc3BhY2UgUG9wdXBcclxuXHQvLyBAbWV0aG9kIGdldExhdExuZzogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgZ2VvZ3JhcGhpY2FsIHBvaW50IG9mIHBvcHVwLlxyXG5cdGdldExhdExuZzogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2xhdGxuZztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldExhdExuZyhsYXRsbmc6IExhdExuZyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBnZW9ncmFwaGljYWwgcG9pbnQgd2hlcmUgdGhlIHBvcHVwIHdpbGwgb3Blbi5cclxuXHRzZXRMYXRMbmc6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHRoaXMuX2xhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdHRoaXMuX3VwZGF0ZVBvc2l0aW9uKCk7XHJcblx0XHRcdHRoaXMuX2FkanVzdFBhbigpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRDb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBjb250ZW50IG9mIHRoZSBwb3B1cC5cclxuXHRnZXRDb250ZW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGVudDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldENvbnRlbnQoaHRtbENvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxGdW5jdGlvbik6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBIVE1MIGNvbnRlbnQgb2YgdGhlIHBvcHVwLiBJZiBhIGZ1bmN0aW9uIGlzIHBhc3NlZCB0aGUgc291cmNlIGxheWVyIHdpbGwgYmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi4gVGhlIGZ1bmN0aW9uIHNob3VsZCByZXR1cm4gYSBgU3RyaW5nYCBvciBgSFRNTEVsZW1lbnRgIHRvIGJlIHVzZWQgaW4gdGhlIHBvcHVwLlxyXG5cdHNldENvbnRlbnQ6IGZ1bmN0aW9uIChjb250ZW50KSB7XHJcblx0XHR0aGlzLl9jb250ZW50ID0gY29udGVudDtcclxuXHRcdHRoaXMudXBkYXRlKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldEVsZW1lbnQ6IFN0cmluZ3xIVE1MRWxlbWVudFxyXG5cdC8vIEFsaWFzIGZvciBbZ2V0Q29udGVudCgpXSgjcG9wdXAtZ2V0Y29udGVudClcclxuXHRnZXRFbGVtZW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdXBkYXRlOiBudWxsXHJcblx0Ly8gVXBkYXRlcyB0aGUgcG9wdXAgY29udGVudCwgbGF5b3V0IGFuZCBwb3NpdGlvbi4gVXNlZnVsIGZvciB1cGRhdGluZyB0aGUgcG9wdXAgYWZ0ZXIgc29tZXRoaW5nIGluc2lkZSBjaGFuZ2VkLCBlLmcuIGltYWdlIGxvYWRlZC5cclxuXHR1cGRhdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fbWFwKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHRoaXMuX2NvbnRhaW5lci5zdHlsZS52aXNpYmlsaXR5ID0gJ2hpZGRlbic7XHJcblxyXG5cdFx0dGhpcy5fdXBkYXRlQ29udGVudCgpO1xyXG5cdFx0dGhpcy5fdXBkYXRlTGF5b3V0KCk7XHJcblx0XHR0aGlzLl91cGRhdGVQb3NpdGlvbigpO1xyXG5cclxuXHRcdHRoaXMuX2NvbnRhaW5lci5zdHlsZS52aXNpYmlsaXR5ID0gJyc7XHJcblxyXG5cdFx0dGhpcy5fYWRqdXN0UGFuKCk7XHJcblx0fSxcclxuXHJcblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZXZlbnRzID0ge1xyXG5cdFx0XHR6b29tOiB0aGlzLl91cGRhdGVQb3NpdGlvbixcclxuXHRcdFx0dmlld3Jlc2V0OiB0aGlzLl91cGRhdGVQb3NpdGlvblxyXG5cdFx0fTtcclxuXHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdGV2ZW50cy56b29tYW5pbSA9IHRoaXMuX2FuaW1hdGVab29tO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGV2ZW50cztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGlzT3BlbjogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIHdoZW4gdGhlIHBvcHVwIGlzIHZpc2libGUgb24gdGhlIG1hcC5cclxuXHRpc09wZW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhIXRoaXMuX21hcCAmJiB0aGlzLl9tYXAuaGFzTGF5ZXIodGhpcyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvRnJvbnQ6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhpcyBwb3B1cCBpbiBmcm9udCBvZiBvdGhlciBwb3B1cHMgKGluIHRoZSBzYW1lIG1hcCBwYW5lKS5cclxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0TC5Eb21VdGlsLnRvRnJvbnQodGhpcy5fY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYnJpbmdUb0JhY2s6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhpcyBwb3B1cCB0byB0aGUgYmFjayBvZiBvdGhlciBwb3B1cHMgKGluIHRoZSBzYW1lIG1hcCBwYW5lKS5cclxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHRMLkRvbVV0aWwudG9CYWNrKHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlQ29udGVudDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9jb250ZW50KSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciBub2RlID0gdGhpcy5fY29udGVudE5vZGU7XHJcblx0XHR2YXIgY29udGVudCA9ICh0eXBlb2YgdGhpcy5fY29udGVudCA9PT0gJ2Z1bmN0aW9uJykgPyB0aGlzLl9jb250ZW50KHRoaXMuX3NvdXJjZSB8fCB0aGlzKSA6IHRoaXMuX2NvbnRlbnQ7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiBjb250ZW50ID09PSAnc3RyaW5nJykge1xyXG5cdFx0XHRub2RlLmlubmVySFRNTCA9IGNvbnRlbnQ7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR3aGlsZSAobm9kZS5oYXNDaGlsZE5vZGVzKCkpIHtcclxuXHRcdFx0XHRub2RlLnJlbW92ZUNoaWxkKG5vZGUuZmlyc3RDaGlsZCk7XHJcblx0XHRcdH1cclxuXHRcdFx0bm9kZS5hcHBlbmRDaGlsZChjb250ZW50KTtcclxuXHRcdH1cclxuXHRcdHRoaXMuZmlyZSgnY29udGVudHVwZGF0ZScpO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVQb3NpdGlvbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fbGF0bG5nKSxcclxuXHRcdCAgICBvZmZzZXQgPSBMLnBvaW50KHRoaXMub3B0aW9ucy5vZmZzZXQpLFxyXG5cdFx0ICAgIGFuY2hvciA9IHRoaXMuX2dldEFuY2hvcigpO1xyXG5cclxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcclxuXHRcdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lciwgcG9zLmFkZChhbmNob3IpKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG9mZnNldCA9IG9mZnNldC5hZGQocG9zKS5hZGQoYW5jaG9yKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgYm90dG9tID0gdGhpcy5fY29udGFpbmVyQm90dG9tID0gLW9mZnNldC55LFxyXG5cdFx0ICAgIGxlZnQgPSB0aGlzLl9jb250YWluZXJMZWZ0ID0gLU1hdGgucm91bmQodGhpcy5fY29udGFpbmVyV2lkdGggLyAyKSArIG9mZnNldC54O1xyXG5cclxuXHRcdC8vIGJvdHRvbSBwb3NpdGlvbiB0aGUgcG9wdXAgaW4gY2FzZSB0aGUgaGVpZ2h0IG9mIHRoZSBwb3B1cCBjaGFuZ2VzIChpbWFnZXMgbG9hZGluZyBldGMpXHJcblx0XHR0aGlzLl9jb250YWluZXIuc3R5bGUuYm90dG9tID0gYm90dG9tICsgJ3B4JztcclxuXHRcdHRoaXMuX2NvbnRhaW5lci5zdHlsZS5sZWZ0ID0gbGVmdCArICdweCc7XHJcblx0fSxcclxuXHJcblx0X2dldEFuY2hvcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIFswLCAwXTtcclxuXHR9XHJcblxyXG59KTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIFBvcHVwXHJcbiAqIEBpbmhlcml0cyBEaXZPdmVybGF5XHJcbiAqIEBha2EgTC5Qb3B1cFxyXG4gKiBVc2VkIHRvIG9wZW4gcG9wdXBzIGluIGNlcnRhaW4gcGxhY2VzIG9mIHRoZSBtYXAuIFVzZSBbTWFwLm9wZW5Qb3B1cF0oI21hcC1vcGVucG9wdXApIHRvXHJcbiAqIG9wZW4gcG9wdXBzIHdoaWxlIG1ha2luZyBzdXJlIHRoYXQgb25seSBvbmUgcG9wdXAgaXMgb3BlbiBhdCBvbmUgdGltZVxyXG4gKiAocmVjb21tZW5kZWQgZm9yIHVzYWJpbGl0eSksIG9yIHVzZSBbTWFwLmFkZExheWVyXSgjbWFwLWFkZGxheWVyKSB0byBvcGVuIGFzIG1hbnkgYXMgeW91IHdhbnQuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIElmIHlvdSB3YW50IHRvIGp1c3QgYmluZCBhIHBvcHVwIHRvIG1hcmtlciBjbGljayBhbmQgdGhlbiBvcGVuIGl0LCBpdCdzIHJlYWxseSBlYXN5OlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBtYXJrZXIuYmluZFBvcHVwKHBvcHVwQ29udGVudCkub3BlblBvcHVwKCk7XHJcbiAqIGBgYFxyXG4gKiBQYXRoIG92ZXJsYXlzIGxpa2UgcG9seWxpbmVzIGFsc28gaGF2ZSBhIGBiaW5kUG9wdXBgIG1ldGhvZC5cclxuICogSGVyZSdzIGEgbW9yZSBjb21wbGljYXRlZCB3YXkgdG8gb3BlbiBhIHBvcHVwIG9uIGEgbWFwOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgcG9wdXAgPSBMLnBvcHVwKClcclxuICogXHQuc2V0TGF0TG5nKGxhdGxuZylcclxuICogXHQuc2V0Q29udGVudCgnPHA+SGVsbG8gd29ybGQhPGJyIC8+VGhpcyBpcyBhIG5pY2UgcG9wdXAuPC9wPicpXHJcbiAqIFx0Lm9wZW5PbihtYXApO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5cclxuLy8gQG5hbWVzcGFjZSBQb3B1cFxyXG5MLlBvcHVwID0gTC5EaXZPdmVybGF5LmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBQb3B1cCBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBtYXhXaWR0aDogTnVtYmVyID0gMzAwXHJcblx0XHQvLyBNYXggd2lkdGggb2YgdGhlIHBvcHVwLCBpbiBwaXhlbHMuXHJcblx0XHRtYXhXaWR0aDogMzAwLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWluV2lkdGg6IE51bWJlciA9IDUwXHJcblx0XHQvLyBNaW4gd2lkdGggb2YgdGhlIHBvcHVwLCBpbiBwaXhlbHMuXHJcblx0XHRtaW5XaWR0aDogNTAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhIZWlnaHQ6IE51bWJlciA9IG51bGxcclxuXHRcdC8vIElmIHNldCwgY3JlYXRlcyBhIHNjcm9sbGFibGUgY29udGFpbmVyIG9mIHRoZSBnaXZlbiBoZWlnaHRcclxuXHRcdC8vIGluc2lkZSBhIHBvcHVwIGlmIGl0cyBjb250ZW50IGV4Y2VlZHMgaXQuXHJcblx0XHRtYXhIZWlnaHQ6IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGBmYWxzZWAgaWYgeW91IGRvbid0IHdhbnQgdGhlIG1hcCB0byBkbyBwYW5uaW5nIGFuaW1hdGlvblxyXG5cdFx0Ly8gdG8gZml0IHRoZSBvcGVuZWQgcG9wdXAuXHJcblx0XHRhdXRvUGFuOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmdUb3BMZWZ0OiBQb2ludCA9IG51bGxcclxuXHRcdC8vIFRoZSBtYXJnaW4gYmV0d2VlbiB0aGUgcG9wdXAgYW5kIHRoZSB0b3AgbGVmdCBjb3JuZXIgb2YgdGhlIG1hcFxyXG5cdFx0Ly8gdmlldyBhZnRlciBhdXRvcGFubmluZyB3YXMgcGVyZm9ybWVkLlxyXG5cdFx0YXV0b1BhblBhZGRpbmdUb3BMZWZ0OiBudWxsLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmdCb3R0b21SaWdodDogUG9pbnQgPSBudWxsXHJcblx0XHQvLyBUaGUgbWFyZ2luIGJldHdlZW4gdGhlIHBvcHVwIGFuZCB0aGUgYm90dG9tIHJpZ2h0IGNvcm5lciBvZiB0aGUgbWFwXHJcblx0XHQvLyB2aWV3IGFmdGVyIGF1dG9wYW5uaW5nIHdhcyBwZXJmb3JtZWQuXHJcblx0XHRhdXRvUGFuUGFkZGluZ0JvdHRvbVJpZ2h0OiBudWxsLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmc6IFBvaW50ID0gUG9pbnQoNSwgNSlcclxuXHRcdC8vIEVxdWl2YWxlbnQgb2Ygc2V0dGluZyBib3RoIHRvcCBsZWZ0IGFuZCBib3R0b20gcmlnaHQgYXV0b3BhbiBwYWRkaW5nIHRvIHRoZSBzYW1lIHZhbHVlLlxyXG5cdFx0YXV0b1BhblBhZGRpbmc6IFs1LCA1XSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGtlZXBJblZpZXc6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGB0cnVlYCBpZiB5b3Ugd2FudCB0byBwcmV2ZW50IHVzZXJzIGZyb20gcGFubmluZyB0aGUgcG9wdXBcclxuXHRcdC8vIG9mZiBvZiB0aGUgc2NyZWVuIHdoaWxlIGl0IGlzIG9wZW4uXHJcblx0XHRrZWVwSW5WaWV3OiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNsb3NlQnV0dG9uOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gQ29udHJvbHMgdGhlIHByZXNlbmNlIG9mIGEgY2xvc2UgYnV0dG9uIGluIHRoZSBwb3B1cC5cclxuXHRcdGNsb3NlQnV0dG9uOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b0Nsb3NlOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGBmYWxzZWAgaWYgeW91IHdhbnQgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgYmVoYXZpb3Igb2ZcclxuXHRcdC8vIHRoZSBwb3B1cCBjbG9zaW5nIHdoZW4gdXNlciBjbGlja3MgdGhlIG1hcCAoc2V0IGdsb2JhbGx5IGJ5XHJcblx0XHQvLyB0aGUgTWFwJ3MgW2Nsb3NlUG9wdXBPbkNsaWNrXSgjbWFwLWNsb3NlcG9wdXBvbmNsaWNrKSBvcHRpb24pLlxyXG5cdFx0YXV0b0Nsb3NlOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY2xhc3NOYW1lOiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gQSBjdXN0b20gQ1NTIGNsYXNzIG5hbWUgdG8gYXNzaWduIHRvIHRoZSBwb3B1cC5cclxuXHRcdGNsYXNzTmFtZTogJydcclxuXHR9LFxyXG5cclxuXHQvLyBAbmFtZXNwYWNlIFBvcHVwXHJcblx0Ly8gQG1ldGhvZCBvcGVuT24obWFwOiBNYXApOiB0aGlzXHJcblx0Ly8gQWRkcyB0aGUgcG9wdXAgdG8gdGhlIG1hcCBhbmQgY2xvc2VzIHRoZSBwcmV2aW91cyBvbmUuIFRoZSBzYW1lIGFzIGBtYXAub3BlblBvcHVwKHBvcHVwKWAuXHJcblx0b3Blbk9uOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRtYXAub3BlblBvcHVwKHRoaXMpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdEwuRGl2T3ZlcmxheS5wcm90b3R5cGUub25BZGQuY2FsbCh0aGlzLCBtYXApO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdC8vIEBldmVudCBwb3B1cG9wZW46IFBvcHVwRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gYSBwb3B1cCBpcyBvcGVuZWQgaW4gdGhlIG1hcFxyXG5cdFx0bWFwLmZpcmUoJ3BvcHVwb3BlbicsIHtwb3B1cDogdGhpc30pO1xyXG5cclxuXHRcdGlmICh0aGlzLl9zb3VyY2UpIHtcclxuXHRcdFx0Ly8gQG5hbWVzcGFjZSBMYXllclxyXG5cdFx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdFx0Ly8gQGV2ZW50IHBvcHVwb3BlbjogUG9wdXBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllciBpcyBvcGVuZWRcclxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3BvcHVwb3BlbicsIHtwb3B1cDogdGhpc30sIHRydWUpO1xyXG5cdFx0XHQvLyBGb3Igbm9uLXBhdGggbGF5ZXJzLCB3ZSB0b2dnbGUgdGhlIHBvcHVwIHdoZW4gY2xpY2tpbmdcclxuXHRcdFx0Ly8gYWdhaW4gdGhlIGxheWVyLCBzbyBwcmV2ZW50IHRoZSBtYXAgdG8gcmVvcGVuIGl0LlxyXG5cdFx0XHRpZiAoISh0aGlzLl9zb3VyY2UgaW5zdGFuY2VvZiBMLlBhdGgpKSB7XHJcblx0XHRcdFx0dGhpcy5fc291cmNlLm9uKCdwcmVjbGljaycsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRMLkRpdk92ZXJsYXkucHJvdG90eXBlLm9uUmVtb3ZlLmNhbGwodGhpcywgbWFwKTtcclxuXHJcblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxyXG5cdFx0Ly8gQHNlY3Rpb24gUG9wdXAgZXZlbnRzXHJcblx0XHQvLyBAZXZlbnQgcG9wdXBjbG9zZTogUG9wdXBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhIHBvcHVwIGluIHRoZSBtYXAgaXMgY2xvc2VkXHJcblx0XHRtYXAuZmlyZSgncG9wdXBjbG9zZScsIHtwb3B1cDogdGhpc30pO1xyXG5cclxuXHRcdGlmICh0aGlzLl9zb3VyY2UpIHtcclxuXHRcdFx0Ly8gQG5hbWVzcGFjZSBMYXllclxyXG5cdFx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdFx0Ly8gQGV2ZW50IHBvcHVwY2xvc2U6IFBvcHVwRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgd2hlbiBhIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgY2xvc2VkXHJcblx0XHRcdHRoaXMuX3NvdXJjZS5maXJlKCdwb3B1cGNsb3NlJywge3BvcHVwOiB0aGlzfSwgdHJ1ZSk7XHJcblx0XHRcdGlmICghKHRoaXMuX3NvdXJjZSBpbnN0YW5jZW9mIEwuUGF0aCkpIHtcclxuXHRcdFx0XHR0aGlzLl9zb3VyY2Uub2ZmKCdwcmVjbGljaycsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGV2ZW50cyA9IEwuRGl2T3ZlcmxheS5wcm90b3R5cGUuZ2V0RXZlbnRzLmNhbGwodGhpcyk7XHJcblxyXG5cdFx0aWYgKCdjbG9zZU9uQ2xpY2snIGluIHRoaXMub3B0aW9ucyA/IHRoaXMub3B0aW9ucy5jbG9zZU9uQ2xpY2sgOiB0aGlzLl9tYXAub3B0aW9ucy5jbG9zZVBvcHVwT25DbGljaykge1xyXG5cdFx0XHRldmVudHMucHJlY2xpY2sgPSB0aGlzLl9jbG9zZTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmtlZXBJblZpZXcpIHtcclxuXHRcdFx0ZXZlbnRzLm1vdmVlbmQgPSB0aGlzLl9hZGp1c3RQYW47XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIGV2ZW50cztcclxuXHR9LFxyXG5cclxuXHRfY2xvc2U6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fbWFwLmNsb3NlUG9wdXAodGhpcyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2luaXRMYXlvdXQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBwcmVmaXggPSAnbGVhZmxldC1wb3B1cCcsXHJcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JyxcclxuXHRcdFx0cHJlZml4ICsgJyAnICsgKHRoaXMub3B0aW9ucy5jbGFzc05hbWUgfHwgJycpICtcclxuXHRcdFx0JyBsZWFmbGV0LXpvb20tYW5pbWF0ZWQnKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmNsb3NlQnV0dG9uKSB7XHJcblx0XHRcdHZhciBjbG9zZUJ1dHRvbiA9IHRoaXMuX2Nsb3NlQnV0dG9uID0gTC5Eb21VdGlsLmNyZWF0ZSgnYScsIHByZWZpeCArICctY2xvc2UtYnV0dG9uJywgY29udGFpbmVyKTtcclxuXHRcdFx0Y2xvc2VCdXR0b24uaHJlZiA9ICcjY2xvc2UnO1xyXG5cdFx0XHRjbG9zZUJ1dHRvbi5pbm5lckhUTUwgPSAnJiMyMTU7JztcclxuXHJcblx0XHRcdEwuRG9tRXZlbnQub24oY2xvc2VCdXR0b24sICdjbGljaycsIHRoaXMuX29uQ2xvc2VCdXR0b25DbGljaywgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHdyYXBwZXIgPSB0aGlzLl93cmFwcGVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgcHJlZml4ICsgJy1jb250ZW50LXdyYXBwZXInLCBjb250YWluZXIpO1xyXG5cdFx0dGhpcy5fY29udGVudE5vZGUgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBwcmVmaXggKyAnLWNvbnRlbnQnLCB3cmFwcGVyKTtcclxuXHJcblx0XHRMLkRvbUV2ZW50XHJcblx0XHRcdC5kaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbih3cmFwcGVyKVxyXG5cdFx0XHQuZGlzYWJsZVNjcm9sbFByb3BhZ2F0aW9uKHRoaXMuX2NvbnRlbnROb2RlKVxyXG5cdFx0XHQub24od3JhcHBlciwgJ2NvbnRleHRtZW51JywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pO1xyXG5cclxuXHRcdHRoaXMuX3RpcENvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIHByZWZpeCArICctdGlwLWNvbnRhaW5lcicsIGNvbnRhaW5lcik7XHJcblx0XHR0aGlzLl90aXAgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBwcmVmaXggKyAnLXRpcCcsIHRoaXMuX3RpcENvbnRhaW5lcik7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZUxheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRlbnROb2RlLFxyXG5cdFx0ICAgIHN0eWxlID0gY29udGFpbmVyLnN0eWxlO1xyXG5cclxuXHRcdHN0eWxlLndpZHRoID0gJyc7XHJcblx0XHRzdHlsZS53aGl0ZVNwYWNlID0gJ25vd3JhcCc7XHJcblxyXG5cdFx0dmFyIHdpZHRoID0gY29udGFpbmVyLm9mZnNldFdpZHRoO1xyXG5cdFx0d2lkdGggPSBNYXRoLm1pbih3aWR0aCwgdGhpcy5vcHRpb25zLm1heFdpZHRoKTtcclxuXHRcdHdpZHRoID0gTWF0aC5tYXgod2lkdGgsIHRoaXMub3B0aW9ucy5taW5XaWR0aCk7XHJcblxyXG5cdFx0c3R5bGUud2lkdGggPSAod2lkdGggKyAxKSArICdweCc7XHJcblx0XHRzdHlsZS53aGl0ZVNwYWNlID0gJyc7XHJcblxyXG5cdFx0c3R5bGUuaGVpZ2h0ID0gJyc7XHJcblxyXG5cdFx0dmFyIGhlaWdodCA9IGNvbnRhaW5lci5vZmZzZXRIZWlnaHQsXHJcblx0XHQgICAgbWF4SGVpZ2h0ID0gdGhpcy5vcHRpb25zLm1heEhlaWdodCxcclxuXHRcdCAgICBzY3JvbGxlZENsYXNzID0gJ2xlYWZsZXQtcG9wdXAtc2Nyb2xsZWQnO1xyXG5cclxuXHRcdGlmIChtYXhIZWlnaHQgJiYgaGVpZ2h0ID4gbWF4SGVpZ2h0KSB7XHJcblx0XHRcdHN0eWxlLmhlaWdodCA9IG1heEhlaWdodCArICdweCc7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhjb250YWluZXIsIHNjcm9sbGVkQ2xhc3MpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKGNvbnRhaW5lciwgc2Nyb2xsZWRDbGFzcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fY29udGFpbmVyV2lkdGggPSB0aGlzLl9jb250YWluZXIub2Zmc2V0V2lkdGg7XHJcblx0fSxcclxuXHJcblx0X2FuaW1hdGVab29tOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5fbGF0TG5nVG9OZXdMYXllclBvaW50KHRoaXMuX2xhdGxuZywgZS56b29tLCBlLmNlbnRlciksXHJcblx0XHQgICAgYW5jaG9yID0gdGhpcy5fZ2V0QW5jaG9yKCk7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fY29udGFpbmVyLCBwb3MuYWRkKGFuY2hvcikpO1xyXG5cdH0sXHJcblxyXG5cdF9hZGp1c3RQYW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5vcHRpb25zLmF1dG9QYW4gfHwgKHRoaXMuX21hcC5fcGFuQW5pbSAmJiB0aGlzLl9tYXAuX3BhbkFuaW0uX2luUHJvZ3Jlc3MpKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXAsXHJcblx0XHQgICAgbWFyZ2luQm90dG9tID0gcGFyc2VJbnQoTC5Eb21VdGlsLmdldFN0eWxlKHRoaXMuX2NvbnRhaW5lciwgJ21hcmdpbkJvdHRvbScpLCAxMCkgfHwgMCxcclxuXHRcdCAgICBjb250YWluZXJIZWlnaHQgPSB0aGlzLl9jb250YWluZXIub2Zmc2V0SGVpZ2h0ICsgbWFyZ2luQm90dG9tLFxyXG5cdFx0ICAgIGNvbnRhaW5lcldpZHRoID0gdGhpcy5fY29udGFpbmVyV2lkdGgsXHJcblx0XHQgICAgbGF5ZXJQb3MgPSBuZXcgTC5Qb2ludCh0aGlzLl9jb250YWluZXJMZWZ0LCAtY29udGFpbmVySGVpZ2h0IC0gdGhpcy5fY29udGFpbmVyQm90dG9tKTtcclxuXHJcblx0XHRsYXllclBvcy5fYWRkKEwuRG9tVXRpbC5nZXRQb3NpdGlvbih0aGlzLl9jb250YWluZXIpKTtcclxuXHJcblx0XHR2YXIgY29udGFpbmVyUG9zID0gbWFwLmxheWVyUG9pbnRUb0NvbnRhaW5lclBvaW50KGxheWVyUG9zKSxcclxuXHRcdCAgICBwYWRkaW5nID0gTC5wb2ludCh0aGlzLm9wdGlvbnMuYXV0b1BhblBhZGRpbmcpLFxyXG5cdFx0ICAgIHBhZGRpbmdUTCA9IEwucG9pbnQodGhpcy5vcHRpb25zLmF1dG9QYW5QYWRkaW5nVG9wTGVmdCB8fCBwYWRkaW5nKSxcclxuXHRcdCAgICBwYWRkaW5nQlIgPSBMLnBvaW50KHRoaXMub3B0aW9ucy5hdXRvUGFuUGFkZGluZ0JvdHRvbVJpZ2h0IHx8IHBhZGRpbmcpLFxyXG5cdFx0ICAgIHNpemUgPSBtYXAuZ2V0U2l6ZSgpLFxyXG5cdFx0ICAgIGR4ID0gMCxcclxuXHRcdCAgICBkeSA9IDA7XHJcblxyXG5cdFx0aWYgKGNvbnRhaW5lclBvcy54ICsgY29udGFpbmVyV2lkdGggKyBwYWRkaW5nQlIueCA+IHNpemUueCkgeyAvLyByaWdodFxyXG5cdFx0XHRkeCA9IGNvbnRhaW5lclBvcy54ICsgY29udGFpbmVyV2lkdGggLSBzaXplLnggKyBwYWRkaW5nQlIueDtcclxuXHRcdH1cclxuXHRcdGlmIChjb250YWluZXJQb3MueCAtIGR4IC0gcGFkZGluZ1RMLnggPCAwKSB7IC8vIGxlZnRcclxuXHRcdFx0ZHggPSBjb250YWluZXJQb3MueCAtIHBhZGRpbmdUTC54O1xyXG5cdFx0fVxyXG5cdFx0aWYgKGNvbnRhaW5lclBvcy55ICsgY29udGFpbmVySGVpZ2h0ICsgcGFkZGluZ0JSLnkgPiBzaXplLnkpIHsgLy8gYm90dG9tXHJcblx0XHRcdGR5ID0gY29udGFpbmVyUG9zLnkgKyBjb250YWluZXJIZWlnaHQgLSBzaXplLnkgKyBwYWRkaW5nQlIueTtcclxuXHRcdH1cclxuXHRcdGlmIChjb250YWluZXJQb3MueSAtIGR5IC0gcGFkZGluZ1RMLnkgPCAwKSB7IC8vIHRvcFxyXG5cdFx0XHRkeSA9IGNvbnRhaW5lclBvcy55IC0gcGFkZGluZ1RMLnk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQG5hbWVzcGFjZSBNYXBcclxuXHRcdC8vIEBzZWN0aW9uIFBvcHVwIGV2ZW50c1xyXG5cdFx0Ly8gQGV2ZW50IGF1dG9wYW5zdGFydDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcCBzdGFydHMgYXV0b3Bhbm5pbmcgd2hlbiBvcGVuaW5nIGEgcG9wdXAuXHJcblx0XHRpZiAoZHggfHwgZHkpIHtcclxuXHRcdFx0bWFwXHJcblx0XHRcdCAgICAuZmlyZSgnYXV0b3BhbnN0YXJ0JylcclxuXHRcdFx0ICAgIC5wYW5CeShbZHgsIGR5XSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X29uQ2xvc2VCdXR0b25DbGljazogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHRoaXMuX2Nsb3NlKCk7XHJcblx0XHRMLkRvbUV2ZW50LnN0b3AoZSk7XHJcblx0fSxcclxuXHJcblx0X2dldEFuY2hvcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gV2hlcmUgc2hvdWxkIHdlIGFuY2hvciB0aGUgcG9wdXAgb24gdGhlIHNvdXJjZSBsYXllcj9cclxuXHRcdHJldHVybiBMLnBvaW50KHRoaXMuX3NvdXJjZSAmJiB0aGlzLl9zb3VyY2UuX2dldFBvcHVwQW5jaG9yID8gdGhpcy5fc291cmNlLl9nZXRQb3B1cEFuY2hvcigpIDogWzAsIDBdKTtcclxuXHR9XHJcblxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgUG9wdXBcclxuLy8gQGZhY3RvcnkgTC5wb3B1cChvcHRpb25zPzogUG9wdXAgb3B0aW9ucywgc291cmNlPzogTGF5ZXIpXHJcbi8vIEluc3RhbnRpYXRlcyBhIGBQb3B1cGAgb2JqZWN0IGdpdmVuIGFuIG9wdGlvbmFsIGBvcHRpb25zYCBvYmplY3QgdGhhdCBkZXNjcmliZXMgaXRzIGFwcGVhcmFuY2UgYW5kIGxvY2F0aW9uIGFuZCBhbiBvcHRpb25hbCBgc291cmNlYCBvYmplY3QgdGhhdCBpcyB1c2VkIHRvIHRhZyB0aGUgcG9wdXAgd2l0aCBhIHJlZmVyZW5jZSB0byB0aGUgTGF5ZXIgdG8gd2hpY2ggaXQgcmVmZXJzLlxyXG5MLnBvcHVwID0gZnVuY3Rpb24gKG9wdGlvbnMsIHNvdXJjZSkge1xyXG5cdHJldHVybiBuZXcgTC5Qb3B1cChvcHRpb25zLCBzb3VyY2UpO1xyXG59O1xyXG5cclxuXHJcbi8qIEBuYW1lc3BhY2UgTWFwXHJcbiAqIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcclxuICogQG9wdGlvbiBjbG9zZVBvcHVwT25DbGljazogQm9vbGVhbiA9IHRydWVcclxuICogU2V0IGl0IHRvIGBmYWxzZWAgaWYgeW91IGRvbid0IHdhbnQgcG9wdXBzIHRvIGNsb3NlIHdoZW4gdXNlciBjbGlja3MgdGhlIG1hcC5cclxuICovXHJcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XHJcblx0Y2xvc2VQb3B1cE9uQ2xpY2s6IHRydWVcclxufSk7XHJcblxyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXBcclxuLy8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgTGF5ZXJzIGFuZCBDb250cm9sc1xyXG5MLk1hcC5pbmNsdWRlKHtcclxuXHQvLyBAbWV0aG9kIG9wZW5Qb3B1cChwb3B1cDogUG9wdXApOiB0aGlzXHJcblx0Ly8gT3BlbnMgdGhlIHNwZWNpZmllZCBwb3B1cCB3aGlsZSBjbG9zaW5nIHRoZSBwcmV2aW91c2x5IG9wZW5lZCAodG8gbWFrZSBzdXJlIG9ubHkgb25lIGlzIG9wZW5lZCBhdCBvbmUgdGltZSBmb3IgdXNhYmlsaXR5KS5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIG9wZW5Qb3B1cChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnQsIGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogUG9wdXAgb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBDcmVhdGVzIGEgcG9wdXAgd2l0aCB0aGUgc3BlY2lmaWVkIGNvbnRlbnQgYW5kIG9wdGlvbnMgYW5kIG9wZW5zIGl0IGluIHRoZSBnaXZlbiBwb2ludCBvbiBhIG1hcC5cclxuXHRvcGVuUG9wdXA6IGZ1bmN0aW9uIChwb3B1cCwgbGF0bG5nLCBvcHRpb25zKSB7XHJcblx0XHRpZiAoIShwb3B1cCBpbnN0YW5jZW9mIEwuUG9wdXApKSB7XHJcblx0XHRcdHBvcHVwID0gbmV3IEwuUG9wdXAob3B0aW9ucykuc2V0Q29udGVudChwb3B1cCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGxhdGxuZykge1xyXG5cdFx0XHRwb3B1cC5zZXRMYXRMbmcobGF0bG5nKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5oYXNMYXllcihwb3B1cCkpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHRoaXMuX3BvcHVwICYmIHRoaXMuX3BvcHVwLm9wdGlvbnMuYXV0b0Nsb3NlKSB7XHJcblx0XHRcdHRoaXMuY2xvc2VQb3B1cCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX3BvcHVwID0gcG9wdXA7XHJcblx0XHRyZXR1cm4gdGhpcy5hZGRMYXllcihwb3B1cCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjbG9zZVBvcHVwKHBvcHVwPzogUG9wdXApOiB0aGlzXHJcblx0Ly8gQ2xvc2VzIHRoZSBwb3B1cCBwcmV2aW91c2x5IG9wZW5lZCB3aXRoIFtvcGVuUG9wdXBdKCNtYXAtb3BlbnBvcHVwKSAob3IgdGhlIGdpdmVuIG9uZSkuXHJcblx0Y2xvc2VQb3B1cDogZnVuY3Rpb24gKHBvcHVwKSB7XHJcblx0XHRpZiAoIXBvcHVwIHx8IHBvcHVwID09PSB0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHRwb3B1cCA9IHRoaXMuX3BvcHVwO1xyXG5cdFx0XHR0aGlzLl9wb3B1cCA9IG51bGw7XHJcblx0XHR9XHJcblx0XHRpZiAocG9wdXApIHtcclxuXHRcdFx0dGhpcy5yZW1vdmVMYXllcihwb3B1cCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9XHJcbn0pO1xyXG5cclxuLypcclxuICogQG5hbWVzcGFjZSBMYXllclxyXG4gKiBAc2VjdGlvbiBQb3B1cCBtZXRob2RzIGV4YW1wbGVcclxuICpcclxuICogQWxsIGxheWVycyBzaGFyZSBhIHNldCBvZiBtZXRob2RzIGNvbnZlbmllbnQgZm9yIGJpbmRpbmcgcG9wdXBzIHRvIGl0LlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgbGF5ZXIgPSBMLlBvbHlnb24obGF0bG5ncykuYmluZFBvcHVwKCdIaSBUaGVyZSEnKS5hZGRUbyhtYXApO1xyXG4gKiBsYXllci5vcGVuUG9wdXAoKTtcclxuICogbGF5ZXIuY2xvc2VQb3B1cCgpO1xyXG4gKiBgYGBcclxuICpcclxuICogUG9wdXBzIHdpbGwgYWxzbyBiZSBhdXRvbWF0aWNhbGx5IG9wZW5lZCB3aGVuIHRoZSBsYXllciBpcyBjbGlja2VkIG9uIGFuZCBjbG9zZWQgd2hlbiB0aGUgbGF5ZXIgaXMgcmVtb3ZlZCBmcm9tIHRoZSBtYXAgb3IgYW5vdGhlciBwb3B1cCBpcyBvcGVuZWQuXHJcbiAqL1xyXG5cclxuLy8gQHNlY3Rpb24gUG9wdXAgbWV0aG9kc1xyXG5MLkxheWVyLmluY2x1ZGUoe1xyXG5cclxuXHQvLyBAbWV0aG9kIGJpbmRQb3B1cChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnR8RnVuY3Rpb258UG9wdXAsIG9wdGlvbnM/OiBQb3B1cCBvcHRpb25zKTogdGhpc1xyXG5cdC8vIEJpbmRzIGEgcG9wdXAgdG8gdGhlIGxheWVyIHdpdGggdGhlIHBhc3NlZCBgY29udGVudGAgYW5kIHNldHMgdXAgdGhlXHJcblx0Ly8gbmVjY2Vzc2FyeSBldmVudCBsaXN0ZW5lcnMuIElmIGEgYEZ1bmN0aW9uYCBpcyBwYXNzZWQgaXQgd2lsbCByZWNlaXZlXHJcblx0Ly8gdGhlIGxheWVyIGFzIHRoZSBmaXJzdCBhcmd1bWVudCBhbmQgc2hvdWxkIHJldHVybiBhIGBTdHJpbmdgIG9yIGBIVE1MRWxlbWVudGAuXHJcblx0YmluZFBvcHVwOiBmdW5jdGlvbiAoY29udGVudCwgb3B0aW9ucykge1xyXG5cclxuXHRcdGlmIChjb250ZW50IGluc3RhbmNlb2YgTC5Qb3B1cCkge1xyXG5cdFx0XHRMLnNldE9wdGlvbnMoY29udGVudCwgb3B0aW9ucyk7XHJcblx0XHRcdHRoaXMuX3BvcHVwID0gY29udGVudDtcclxuXHRcdFx0Y29udGVudC5fc291cmNlID0gdGhpcztcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGlmICghdGhpcy5fcG9wdXAgfHwgb3B0aW9ucykge1xyXG5cdFx0XHRcdHRoaXMuX3BvcHVwID0gbmV3IEwuUG9wdXAob3B0aW9ucywgdGhpcyk7XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5fcG9wdXAuc2V0Q29udGVudChjb250ZW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXRoaXMuX3BvcHVwSGFuZGxlcnNBZGRlZCkge1xyXG5cdFx0XHR0aGlzLm9uKHtcclxuXHRcdFx0XHRjbGljazogdGhpcy5fb3BlblBvcHVwLFxyXG5cdFx0XHRcdHJlbW92ZTogdGhpcy5jbG9zZVBvcHVwLFxyXG5cdFx0XHRcdG1vdmU6IHRoaXMuX21vdmVQb3B1cFxyXG5cdFx0XHR9KTtcclxuXHRcdFx0dGhpcy5fcG9wdXBIYW5kbGVyc0FkZGVkID0gdHJ1ZTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVuYmluZFBvcHVwKCk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIHRoZSBwb3B1cCBwcmV2aW91c2x5IGJvdW5kIHdpdGggYGJpbmRQb3B1cGAuXHJcblx0dW5iaW5kUG9wdXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHR0aGlzLm9mZih7XHJcblx0XHRcdFx0Y2xpY2s6IHRoaXMuX29wZW5Qb3B1cCxcclxuXHRcdFx0XHRyZW1vdmU6IHRoaXMuY2xvc2VQb3B1cCxcclxuXHRcdFx0XHRtb3ZlOiB0aGlzLl9tb3ZlUG9wdXBcclxuXHRcdFx0fSk7XHJcblx0XHRcdHRoaXMuX3BvcHVwSGFuZGxlcnNBZGRlZCA9IGZhbHNlO1xyXG5cdFx0XHR0aGlzLl9wb3B1cCA9IG51bGw7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIG9wZW5Qb3B1cChsYXRsbmc/OiBMYXRMbmcpOiB0aGlzXHJcblx0Ly8gT3BlbnMgdGhlIGJvdW5kIHBvcHVwIGF0IHRoZSBzcGVjaWZpY2VkIGBsYXRsbmdgIG9yIGF0IHRoZSBkZWZhdWx0IHBvcHVwIGFuY2hvciBpZiBubyBgbGF0bG5nYCBpcyBwYXNzZWQuXHJcblx0b3BlblBvcHVwOiBmdW5jdGlvbiAobGF5ZXIsIGxhdGxuZykge1xyXG5cdFx0aWYgKCEobGF5ZXIgaW5zdGFuY2VvZiBMLkxheWVyKSkge1xyXG5cdFx0XHRsYXRsbmcgPSBsYXllcjtcclxuXHRcdFx0bGF5ZXIgPSB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChsYXllciBpbnN0YW5jZW9mIEwuRmVhdHVyZUdyb3VwKSB7XHJcblx0XHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRcdGxheWVyID0gdGhpcy5fbGF5ZXJzW2lkXTtcclxuXHRcdFx0XHRicmVhaztcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghbGF0bG5nKSB7XHJcblx0XHRcdGxhdGxuZyA9IGxheWVyLmdldENlbnRlciA/IGxheWVyLmdldENlbnRlcigpIDogbGF5ZXIuZ2V0TGF0TG5nKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHRoaXMuX3BvcHVwICYmIHRoaXMuX21hcCkge1xyXG5cdFx0XHQvLyBzZXQgcG9wdXAgc291cmNlIHRvIHRoaXMgbGF5ZXJcclxuXHRcdFx0dGhpcy5fcG9wdXAuX3NvdXJjZSA9IGxheWVyO1xyXG5cclxuXHRcdFx0Ly8gdXBkYXRlIHRoZSBwb3B1cCAoY29udGVudCwgbGF5b3V0LCBlY3QuLi4pXHJcblx0XHRcdHRoaXMuX3BvcHVwLnVwZGF0ZSgpO1xyXG5cclxuXHRcdFx0Ly8gb3BlbiB0aGUgcG9wdXAgb24gdGhlIG1hcFxyXG5cdFx0XHR0aGlzLl9tYXAub3BlblBvcHVwKHRoaXMuX3BvcHVwLCBsYXRsbmcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY2xvc2VQb3B1cCgpOiB0aGlzXHJcblx0Ly8gQ2xvc2VzIHRoZSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyIGlmIGl0IGlzIG9wZW4uXHJcblx0Y2xvc2VQb3B1cDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdHRoaXMuX3BvcHVwLl9jbG9zZSgpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b2dnbGVQb3B1cCgpOiB0aGlzXHJcblx0Ly8gT3BlbnMgb3IgY2xvc2VzIHRoZSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyIGRlcGVuZGluZyBvbiBpdHMgY3VycmVudCBzdGF0ZS5cclxuXHR0b2dnbGVQb3B1cDogZnVuY3Rpb24gKHRhcmdldCkge1xyXG5cdFx0aWYgKHRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdGlmICh0aGlzLl9wb3B1cC5fbWFwKSB7XHJcblx0XHRcdFx0dGhpcy5jbG9zZVBvcHVwKCk7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0dGhpcy5vcGVuUG9wdXAodGFyZ2V0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpc1BvcHVwT3BlbigpOiBib29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgY3VycmVudGx5IG9wZW4uXHJcblx0aXNQb3B1cE9wZW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAodGhpcy5fcG9wdXAgPyB0aGlzLl9wb3B1cC5pc09wZW4oKSA6IGZhbHNlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFBvcHVwQ29udGVudChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnR8UG9wdXApOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgY29udGVudCBvZiB0aGUgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllci5cclxuXHRzZXRQb3B1cENvbnRlbnQ6IGZ1bmN0aW9uIChjb250ZW50KSB7XHJcblx0XHRpZiAodGhpcy5fcG9wdXApIHtcclxuXHRcdFx0dGhpcy5fcG9wdXAuc2V0Q29udGVudChjb250ZW50KTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UG9wdXAoKTogUG9wdXBcclxuXHQvLyBSZXR1cm5zIHRoZSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyLlxyXG5cdGdldFBvcHVwOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fcG9wdXA7XHJcblx0fSxcclxuXHJcblx0X29wZW5Qb3B1cDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHZhciBsYXllciA9IGUubGF5ZXIgfHwgZS50YXJnZXQ7XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9tYXApIHtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIHByZXZlbnQgbWFwIGNsaWNrXHJcblx0XHRMLkRvbUV2ZW50LnN0b3AoZSk7XHJcblxyXG5cdFx0Ly8gaWYgdGhpcyBpbmhlcml0cyBmcm9tIFBhdGggaXRzIGEgdmVjdG9yIGFuZCB3ZSBjYW4ganVzdFxyXG5cdFx0Ly8gb3BlbiB0aGUgcG9wdXAgYXQgdGhlIG5ldyBsb2NhdGlvblxyXG5cdFx0aWYgKGxheWVyIGluc3RhbmNlb2YgTC5QYXRoKSB7XHJcblx0XHRcdHRoaXMub3BlblBvcHVwKGUubGF5ZXIgfHwgZS50YXJnZXQsIGUubGF0bG5nKTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIG90aGVyd2lzZSB0cmVhdCBpdCBsaWtlIGEgbWFya2VyIGFuZCBmaWd1cmUgb3V0XHJcblx0XHQvLyBpZiB3ZSBzaG91bGQgdG9nZ2xlIGl0IG9wZW4vY2xvc2VkXHJcblx0XHRpZiAodGhpcy5fbWFwLmhhc0xheWVyKHRoaXMuX3BvcHVwKSAmJiB0aGlzLl9wb3B1cC5fc291cmNlID09PSBsYXllcikge1xyXG5cdFx0XHR0aGlzLmNsb3NlUG9wdXAoKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHRoaXMub3BlblBvcHVwKGxheWVyLCBlLmxhdGxuZyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X21vdmVQb3B1cDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHRoaXMuX3BvcHVwLnNldExhdExuZyhlLmxhdGxuZyk7XHJcblx0fVxyXG59KTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBUb29sdGlwXG4gKiBAaW5oZXJpdHMgRGl2T3ZlcmxheVxuICogQGFrYSBMLlRvb2x0aXBcbiAqIFVzZWQgdG8gZGlzcGxheSBzbWFsbCB0ZXh0cyBvbiB0b3Agb2YgbWFwIGxheWVycy5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiBtYXJrZXIuYmluZFRvb2x0aXAoXCJteSB0b29sdGlwIHRleHRcIikub3BlblRvb2x0aXAoKTtcbiAqIGBgYFxuICogTm90ZSBhYm91dCB0b29sdGlwIG9mZnNldC4gTGVhZmxldCB0YWtlcyB0d28gb3B0aW9ucyBpbiBjb25zaWRlcmF0aW9uXG4gKiBmb3IgY29tcHV0aW5nIHRvb2x0aXAgb2Zmc2V0aW5nOlxuICogLSB0aGUgYG9mZnNldGAgVG9vbHRpcCBvcHRpb246IGl0IGRlZmF1bHRzIHRvIFswLCAwXSwgYW5kIGl0J3Mgc3BlY2lmaWMgdG8gb25lIHRvb2x0aXAuXG4gKiAgIEFkZCBhIHBvc2l0aXZlIHggb2Zmc2V0IHRvIG1vdmUgdGhlIHRvb2x0aXAgdG8gdGhlIHJpZ2h0LCBhbmQgYSBwb3NpdGl2ZSB5IG9mZnNldCB0b1xuICogICBtb3ZlIGl0IHRvIHRoZSBib3R0b20uIE5lZ2F0aXZlcyB3aWxsIG1vdmUgdG8gdGhlIGxlZnQgYW5kIHRvcC5cbiAqIC0gdGhlIGB0b29sdGlwQW5jaG9yYCBJY29uIG9wdGlvbjogdGhpcyB3aWxsIG9ubHkgYmUgY29uc2lkZXJlZCBmb3IgTWFya2VyLiBZb3VcbiAqICAgc2hvdWxkIGFkYXB0IHRoaXMgdmFsdWUgaWYgeW91IHVzZSBhIGN1c3RvbSBpY29uLlxuICovXG5cblxuLy8gQG5hbWVzcGFjZSBUb29sdGlwXG5MLlRvb2x0aXAgPSBMLkRpdk92ZXJsYXkuZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFRvb2x0aXAgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAndG9vbHRpcFBhbmUnXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgdG9vbHRpcCB3aWxsIGJlIGFkZGVkLlxuXHRcdHBhbmU6ICd0b29sdGlwUGFuZScsXG5cblx0XHQvLyBAb3B0aW9uIG9mZnNldDogUG9pbnQgPSBQb2ludCgwLCAwKVxuXHRcdC8vIE9wdGlvbmFsIG9mZnNldCBvZiB0aGUgdG9vbHRpcCBwb3NpdGlvbi5cblx0XHRvZmZzZXQ6IFswLCAwXSxcblxuXHRcdC8vIEBvcHRpb24gZGlyZWN0aW9uOiBTdHJpbmcgPSAnYXV0bydcblx0XHQvLyBEaXJlY3Rpb24gd2hlcmUgdG8gb3BlbiB0aGUgdG9vbHRpcC4gUG9zc2libGUgdmFsdWVzIGFyZTogYHJpZ2h0YCwgYGxlZnRgLFxuXHRcdC8vIGB0b3BgLCBgYm90dG9tYCwgYGNlbnRlcmAsIGBhdXRvYC5cblx0XHQvLyBgYXV0b2Agd2lsbCBkeW5hbWljYWx5IHN3aXRjaCBiZXR3ZWVuIGByaWdodGAgYW5kIGBsZWZ0YCBhY2NvcmRpbmcgdG8gdGhlIHRvb2x0aXBcblx0XHQvLyBwb3NpdGlvbiBvbiB0aGUgbWFwLlxuXHRcdGRpcmVjdGlvbjogJ2F1dG8nLFxuXG5cdFx0Ly8gQG9wdGlvbiBwZXJtYW5lbnQ6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIFdoZXRoZXIgdG8gb3BlbiB0aGUgdG9vbHRpcCBwZXJtYW5lbnRseSBvciBvbmx5IG9uIG1vdXNlb3Zlci5cblx0XHRwZXJtYW5lbnQ6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBzdGlja3k6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIElmIHRydWUsIHRoZSB0b29sdGlwIHdpbGwgZm9sbG93IHRoZSBtb3VzZSBpbnN0ZWFkIG9mIGJlaW5nIGZpeGVkIGF0IHRoZSBmZWF0dXJlIGNlbnRlci5cblx0XHRzdGlja3k6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBpbnRlcmFjdGl2ZTogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gSWYgdHJ1ZSwgdGhlIHRvb2x0aXAgd2lsbCBsaXN0ZW4gdG8gdGhlIGZlYXR1cmUgZXZlbnRzLlxuXHRcdGludGVyYWN0aXZlOiBmYWxzZSxcblxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMC45XG5cdFx0Ly8gVG9vbHRpcCBjb250YWluZXIgb3BhY2l0eS5cblx0XHRvcGFjaXR5OiAwLjlcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xuXHRcdEwuRGl2T3ZlcmxheS5wcm90b3R5cGUub25BZGQuY2FsbCh0aGlzLCBtYXApO1xuXHRcdHRoaXMuc2V0T3BhY2l0eSh0aGlzLm9wdGlvbnMub3BhY2l0eSk7XG5cblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxuXHRcdC8vIEBzZWN0aW9uIFRvb2x0aXAgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IHRvb2x0aXBvcGVuOiBUb29sdGlwRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIGEgdG9vbHRpcCBpcyBvcGVuZWQgaW4gdGhlIG1hcC5cblx0XHRtYXAuZmlyZSgndG9vbHRpcG9wZW4nLCB7dG9vbHRpcDogdGhpc30pO1xuXG5cdFx0aWYgKHRoaXMuX3NvdXJjZSkge1xuXHRcdFx0Ly8gQG5hbWVzcGFjZSBMYXllclxuXHRcdFx0Ly8gQHNlY3Rpb24gVG9vbHRpcCBldmVudHNcblx0XHRcdC8vIEBldmVudCB0b29sdGlwb3BlbjogVG9vbHRpcEV2ZW50XG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyIGlzIG9wZW5lZC5cblx0XHRcdHRoaXMuX3NvdXJjZS5maXJlKCd0b29sdGlwb3BlbicsIHt0b29sdGlwOiB0aGlzfSwgdHJ1ZSk7XG5cdFx0fVxuXHR9LFxuXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0TC5EaXZPdmVybGF5LnByb3RvdHlwZS5vblJlbW92ZS5jYWxsKHRoaXMsIG1hcCk7XG5cblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxuXHRcdC8vIEBzZWN0aW9uIFRvb2x0aXAgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IHRvb2x0aXBjbG9zZTogVG9vbHRpcEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiBhIHRvb2x0aXAgaW4gdGhlIG1hcCBpcyBjbG9zZWQuXG5cdFx0bWFwLmZpcmUoJ3Rvb2x0aXBjbG9zZScsIHt0b29sdGlwOiB0aGlzfSk7XG5cblx0XHRpZiAodGhpcy5fc291cmNlKSB7XG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXG5cdFx0XHQvLyBAc2VjdGlvbiBUb29sdGlwIGV2ZW50c1xuXHRcdFx0Ly8gQGV2ZW50IHRvb2x0aXBjbG9zZTogVG9vbHRpcEV2ZW50XG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyIGlzIGNsb3NlZC5cblx0XHRcdHRoaXMuX3NvdXJjZS5maXJlKCd0b29sdGlwY2xvc2UnLCB7dG9vbHRpcDogdGhpc30sIHRydWUpO1xuXHRcdH1cblx0fSxcblxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgZXZlbnRzID0gTC5EaXZPdmVybGF5LnByb3RvdHlwZS5nZXRFdmVudHMuY2FsbCh0aGlzKTtcblxuXHRcdGlmIChMLkJyb3dzZXIudG91Y2ggJiYgIXRoaXMub3B0aW9ucy5wZXJtYW5lbnQpIHtcblx0XHRcdGV2ZW50cy5wcmVjbGljayA9IHRoaXMuX2Nsb3NlO1xuXHRcdH1cblxuXHRcdHJldHVybiBldmVudHM7XG5cdH0sXG5cblx0X2Nsb3NlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0dGhpcy5fbWFwLmNsb3NlVG9vbHRpcCh0aGlzKTtcblx0XHR9XG5cdH0sXG5cblx0X2luaXRMYXlvdXQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcHJlZml4ID0gJ2xlYWZsZXQtdG9vbHRpcCcsXG5cdFx0ICAgIGNsYXNzTmFtZSA9IHByZWZpeCArICcgJyArICh0aGlzLm9wdGlvbnMuY2xhc3NOYW1lIHx8ICcnKSArICcgbGVhZmxldC16b29tLScgKyAodGhpcy5fem9vbUFuaW1hdGVkID8gJ2FuaW1hdGVkJyA6ICdoaWRlJyk7XG5cblx0XHR0aGlzLl9jb250ZW50Tm9kZSA9IHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSk7XG5cdH0sXG5cblx0X3VwZGF0ZUxheW91dDogZnVuY3Rpb24gKCkge30sXG5cblx0X2FkanVzdFBhbjogZnVuY3Rpb24gKCkge30sXG5cblx0X3NldFBvc2l0aW9uOiBmdW5jdGlvbiAocG9zKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyLFxuXHRcdCAgICBjZW50ZXJQb2ludCA9IG1hcC5sYXRMbmdUb0NvbnRhaW5lclBvaW50KG1hcC5nZXRDZW50ZXIoKSksXG5cdFx0ICAgIHRvb2x0aXBQb2ludCA9IG1hcC5sYXllclBvaW50VG9Db250YWluZXJQb2ludChwb3MpLFxuXHRcdCAgICBkaXJlY3Rpb24gPSB0aGlzLm9wdGlvbnMuZGlyZWN0aW9uLFxuXHRcdCAgICB0b29sdGlwV2lkdGggPSBjb250YWluZXIub2Zmc2V0V2lkdGgsXG5cdFx0ICAgIHRvb2x0aXBIZWlnaHQgPSBjb250YWluZXIub2Zmc2V0SGVpZ2h0LFxuXHRcdCAgICBvZmZzZXQgPSBMLnBvaW50KHRoaXMub3B0aW9ucy5vZmZzZXQpLFxuXHRcdCAgICBhbmNob3IgPSB0aGlzLl9nZXRBbmNob3IoKTtcblxuXHRcdGlmIChkaXJlY3Rpb24gPT09ICd0b3AnKSB7XG5cdFx0XHRwb3MgPSBwb3MuYWRkKEwucG9pbnQoLXRvb2x0aXBXaWR0aCAvIDIgKyBvZmZzZXQueCwgLXRvb2x0aXBIZWlnaHQgKyBvZmZzZXQueSArIGFuY2hvci55LCB0cnVlKSk7XG5cdFx0fSBlbHNlIGlmIChkaXJlY3Rpb24gPT09ICdib3R0b20nKSB7XG5cdFx0XHRwb3MgPSBwb3Muc3VidHJhY3QoTC5wb2ludCh0b29sdGlwV2lkdGggLyAyIC0gb2Zmc2V0LngsIC1vZmZzZXQueSwgdHJ1ZSkpO1xuXHRcdH0gZWxzZSBpZiAoZGlyZWN0aW9uID09PSAnY2VudGVyJykge1xuXHRcdFx0cG9zID0gcG9zLnN1YnRyYWN0KEwucG9pbnQodG9vbHRpcFdpZHRoIC8gMiArIG9mZnNldC54LCB0b29sdGlwSGVpZ2h0IC8gMiAtIGFuY2hvci55ICsgb2Zmc2V0LnksIHRydWUpKTtcblx0XHR9IGVsc2UgaWYgKGRpcmVjdGlvbiA9PT0gJ3JpZ2h0JyB8fCBkaXJlY3Rpb24gPT09ICdhdXRvJyAmJiB0b29sdGlwUG9pbnQueCA8IGNlbnRlclBvaW50LngpIHtcblx0XHRcdGRpcmVjdGlvbiA9ICdyaWdodCc7XG5cdFx0XHRwb3MgPSBwb3MuYWRkKEwucG9pbnQob2Zmc2V0LnggKyBhbmNob3IueCwgYW5jaG9yLnkgLSB0b29sdGlwSGVpZ2h0IC8gMiArIG9mZnNldC55LCB0cnVlKSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGRpcmVjdGlvbiA9ICdsZWZ0Jztcblx0XHRcdHBvcyA9IHBvcy5zdWJ0cmFjdChMLnBvaW50KHRvb2x0aXBXaWR0aCArIGFuY2hvci54IC0gb2Zmc2V0LngsIHRvb2x0aXBIZWlnaHQgLyAyIC0gYW5jaG9yLnkgLSBvZmZzZXQueSwgdHJ1ZSkpO1xuXHRcdH1cblxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtcmlnaHQnKTtcblx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC10b29sdGlwLWxlZnQnKTtcblx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC10b29sdGlwLXRvcCcpO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtYm90dG9tJyk7XG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtdG9vbHRpcC0nICsgZGlyZWN0aW9uKTtcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24oY29udGFpbmVyLCBwb3MpO1xuXHR9LFxuXG5cdF91cGRhdGVQb3NpdGlvbjogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBwb3MgPSB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZyk7XG5cdFx0dGhpcy5fc2V0UG9zaXRpb24ocG9zKTtcblx0fSxcblxuXHRzZXRPcGFjaXR5OiBmdW5jdGlvbiAob3BhY2l0eSkge1xuXHRcdHRoaXMub3B0aW9ucy5vcGFjaXR5ID0gb3BhY2l0eTtcblxuXHRcdGlmICh0aGlzLl9jb250YWluZXIpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgb3BhY2l0eSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLl9sYXRMbmdUb05ld0xheWVyUG9pbnQodGhpcy5fbGF0bG5nLCBlLnpvb20sIGUuY2VudGVyKTtcblx0XHR0aGlzLl9zZXRQb3NpdGlvbihwb3MpO1xuXHR9LFxuXG5cdF9nZXRBbmNob3I6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBXaGVyZSBzaG91bGQgd2UgYW5jaG9yIHRoZSB0b29sdGlwIG9uIHRoZSBzb3VyY2UgbGF5ZXI/XG5cdFx0cmV0dXJuIEwucG9pbnQodGhpcy5fc291cmNlICYmIHRoaXMuX3NvdXJjZS5fZ2V0VG9vbHRpcEFuY2hvciAmJiAhdGhpcy5vcHRpb25zLnN0aWNreSA/IHRoaXMuX3NvdXJjZS5fZ2V0VG9vbHRpcEFuY2hvcigpIDogWzAsIDBdKTtcblx0fVxuXG59KTtcblxuLy8gQG5hbWVzcGFjZSBUb29sdGlwXG4vLyBAZmFjdG9yeSBMLnRvb2x0aXAob3B0aW9ucz86IFRvb2x0aXAgb3B0aW9ucywgc291cmNlPzogTGF5ZXIpXG4vLyBJbnN0YW50aWF0ZXMgYSBUb29sdGlwIG9iamVjdCBnaXZlbiBhbiBvcHRpb25hbCBgb3B0aW9uc2Agb2JqZWN0IHRoYXQgZGVzY3JpYmVzIGl0cyBhcHBlYXJhbmNlIGFuZCBsb2NhdGlvbiBhbmQgYW4gb3B0aW9uYWwgYHNvdXJjZWAgb2JqZWN0IHRoYXQgaXMgdXNlZCB0byB0YWcgdGhlIHRvb2x0aXAgd2l0aCBhIHJlZmVyZW5jZSB0byB0aGUgTGF5ZXIgdG8gd2hpY2ggaXQgcmVmZXJzLlxuTC50b29sdGlwID0gZnVuY3Rpb24gKG9wdGlvbnMsIHNvdXJjZSkge1xuXHRyZXR1cm4gbmV3IEwuVG9vbHRpcChvcHRpb25zLCBzb3VyY2UpO1xufTtcblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIE1ldGhvZHMgZm9yIExheWVycyBhbmQgQ29udHJvbHNcbkwuTWFwLmluY2x1ZGUoe1xuXG5cdC8vIEBtZXRob2Qgb3BlblRvb2x0aXAodG9vbHRpcDogVG9vbHRpcCk6IHRoaXNcblx0Ly8gT3BlbnMgdGhlIHNwZWNpZmllZCB0b29sdGlwLlxuXHQvLyBAYWx0ZXJuYXRpdmVcblx0Ly8gQG1ldGhvZCBvcGVuVG9vbHRpcChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnQsIGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogVG9vbHRpcCBvcHRpb25zKTogdGhpc1xuXHQvLyBDcmVhdGVzIGEgdG9vbHRpcCB3aXRoIHRoZSBzcGVjaWZpZWQgY29udGVudCBhbmQgb3B0aW9ucyBhbmQgb3BlbiBpdC5cblx0b3BlblRvb2x0aXA6IGZ1bmN0aW9uICh0b29sdGlwLCBsYXRsbmcsIG9wdGlvbnMpIHtcblx0XHRpZiAoISh0b29sdGlwIGluc3RhbmNlb2YgTC5Ub29sdGlwKSkge1xuXHRcdFx0dG9vbHRpcCA9IG5ldyBMLlRvb2x0aXAob3B0aW9ucykuc2V0Q29udGVudCh0b29sdGlwKTtcblx0XHR9XG5cblx0XHRpZiAobGF0bG5nKSB7XG5cdFx0XHR0b29sdGlwLnNldExhdExuZyhsYXRsbmcpO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLmhhc0xheWVyKHRvb2x0aXApKSB7XG5cdFx0XHRyZXR1cm4gdGhpcztcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcy5hZGRMYXllcih0b29sdGlwKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGNsb3NlVG9vbHRpcCh0b29sdGlwPzogVG9vbHRpcCk6IHRoaXNcblx0Ly8gQ2xvc2VzIHRoZSB0b29sdGlwIGdpdmVuIGFzIHBhcmFtZXRlci5cblx0Y2xvc2VUb29sdGlwOiBmdW5jdGlvbiAodG9vbHRpcCkge1xuXHRcdGlmICh0b29sdGlwKSB7XG5cdFx0XHR0aGlzLnJlbW92ZUxheWVyKHRvb2x0aXApO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG59KTtcblxuLypcbiAqIEBuYW1lc3BhY2UgTGF5ZXJcbiAqIEBzZWN0aW9uIFRvb2x0aXAgbWV0aG9kcyBleGFtcGxlXG4gKlxuICogQWxsIGxheWVycyBzaGFyZSBhIHNldCBvZiBtZXRob2RzIGNvbnZlbmllbnQgZm9yIGJpbmRpbmcgdG9vbHRpcHMgdG8gaXQuXG4gKlxuICogYGBganNcbiAqIHZhciBsYXllciA9IEwuUG9seWdvbihsYXRsbmdzKS5iaW5kVG9vbHRpcCgnSGkgVGhlcmUhJykuYWRkVG8obWFwKTtcbiAqIGxheWVyLm9wZW5Ub29sdGlwKCk7XG4gKiBsYXllci5jbG9zZVRvb2x0aXAoKTtcbiAqIGBgYFxuICovXG5cbi8vIEBzZWN0aW9uIFRvb2x0aXAgbWV0aG9kc1xuTC5MYXllci5pbmNsdWRlKHtcblxuXHQvLyBAbWV0aG9kIGJpbmRUb29sdGlwKGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxGdW5jdGlvbnxUb29sdGlwLCBvcHRpb25zPzogVG9vbHRpcCBvcHRpb25zKTogdGhpc1xuXHQvLyBCaW5kcyBhIHRvb2x0aXAgdG8gdGhlIGxheWVyIHdpdGggdGhlIHBhc3NlZCBgY29udGVudGAgYW5kIHNldHMgdXAgdGhlXG5cdC8vIG5lY2Nlc3NhcnkgZXZlbnQgbGlzdGVuZXJzLiBJZiBhIGBGdW5jdGlvbmAgaXMgcGFzc2VkIGl0IHdpbGwgcmVjZWl2ZVxuXHQvLyB0aGUgbGF5ZXIgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IGFuZCBzaG91bGQgcmV0dXJuIGEgYFN0cmluZ2Agb3IgYEhUTUxFbGVtZW50YC5cblx0YmluZFRvb2x0aXA6IGZ1bmN0aW9uIChjb250ZW50LCBvcHRpb25zKSB7XG5cblx0XHRpZiAoY29udGVudCBpbnN0YW5jZW9mIEwuVG9vbHRpcCkge1xuXHRcdFx0TC5zZXRPcHRpb25zKGNvbnRlbnQsIG9wdGlvbnMpO1xuXHRcdFx0dGhpcy5fdG9vbHRpcCA9IGNvbnRlbnQ7XG5cdFx0XHRjb250ZW50Ll9zb3VyY2UgPSB0aGlzO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRpZiAoIXRoaXMuX3Rvb2x0aXAgfHwgb3B0aW9ucykge1xuXHRcdFx0XHR0aGlzLl90b29sdGlwID0gTC50b29sdGlwKG9wdGlvbnMsIHRoaXMpO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fdG9vbHRpcC5zZXRDb250ZW50KGNvbnRlbnQpO1xuXG5cdFx0fVxuXG5cdFx0dGhpcy5faW5pdFRvb2x0aXBJbnRlcmFjdGlvbnMoKTtcblxuXHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMucGVybWFuZW50ICYmIHRoaXMuX21hcCAmJiB0aGlzLl9tYXAuaGFzTGF5ZXIodGhpcykpIHtcblx0XHRcdHRoaXMub3BlblRvb2x0aXAoKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHVuYmluZFRvb2x0aXAoKTogdGhpc1xuXHQvLyBSZW1vdmVzIHRoZSB0b29sdGlwIHByZXZpb3VzbHkgYm91bmQgd2l0aCBgYmluZFRvb2x0aXBgLlxuXHR1bmJpbmRUb29sdGlwOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXApIHtcblx0XHRcdHRoaXMuX2luaXRUb29sdGlwSW50ZXJhY3Rpb25zKHRydWUpO1xuXHRcdFx0dGhpcy5jbG9zZVRvb2x0aXAoKTtcblx0XHRcdHRoaXMuX3Rvb2x0aXAgPSBudWxsO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRfaW5pdFRvb2x0aXBJbnRlcmFjdGlvbnM6IGZ1bmN0aW9uIChyZW1vdmUpIHtcblx0XHRpZiAoIXJlbW92ZSAmJiB0aGlzLl90b29sdGlwSGFuZGxlcnNBZGRlZCkgeyByZXR1cm47IH1cblx0XHR2YXIgb25PZmYgPSByZW1vdmUgPyAnb2ZmJyA6ICdvbicsXG5cdFx0ICAgIGV2ZW50cyA9IHtcblx0XHRcdHJlbW92ZTogdGhpcy5jbG9zZVRvb2x0aXAsXG5cdFx0XHRtb3ZlOiB0aGlzLl9tb3ZlVG9vbHRpcFxuXHRcdCAgICB9O1xuXHRcdGlmICghdGhpcy5fdG9vbHRpcC5vcHRpb25zLnBlcm1hbmVudCkge1xuXHRcdFx0ZXZlbnRzLm1vdXNlb3ZlciA9IHRoaXMuX29wZW5Ub29sdGlwO1xuXHRcdFx0ZXZlbnRzLm1vdXNlb3V0ID0gdGhpcy5jbG9zZVRvb2x0aXA7XG5cdFx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLnN0aWNreSkge1xuXHRcdFx0XHRldmVudHMubW91c2Vtb3ZlID0gdGhpcy5fbW92ZVRvb2x0aXA7XG5cdFx0XHR9XG5cdFx0XHRpZiAoTC5Ccm93c2VyLnRvdWNoKSB7XG5cdFx0XHRcdGV2ZW50cy5jbGljayA9IHRoaXMuX29wZW5Ub29sdGlwO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRldmVudHMuYWRkID0gdGhpcy5fb3BlblRvb2x0aXA7XG5cdFx0fVxuXHRcdHRoaXNbb25PZmZdKGV2ZW50cyk7XG5cdFx0dGhpcy5fdG9vbHRpcEhhbmRsZXJzQWRkZWQgPSAhcmVtb3ZlO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgb3BlblRvb2x0aXAobGF0bG5nPzogTGF0TG5nKTogdGhpc1xuXHQvLyBPcGVucyB0aGUgYm91bmQgdG9vbHRpcCBhdCB0aGUgc3BlY2lmaWNlZCBgbGF0bG5nYCBvciBhdCB0aGUgZGVmYXVsdCB0b29sdGlwIGFuY2hvciBpZiBubyBgbGF0bG5nYCBpcyBwYXNzZWQuXG5cdG9wZW5Ub29sdGlwOiBmdW5jdGlvbiAobGF5ZXIsIGxhdGxuZykge1xuXHRcdGlmICghKGxheWVyIGluc3RhbmNlb2YgTC5MYXllcikpIHtcblx0XHRcdGxhdGxuZyA9IGxheWVyO1xuXHRcdFx0bGF5ZXIgPSB0aGlzO1xuXHRcdH1cblxuXHRcdGlmIChsYXllciBpbnN0YW5jZW9mIEwuRmVhdHVyZUdyb3VwKSB7XG5cdFx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9sYXllcnMpIHtcblx0XHRcdFx0bGF5ZXIgPSB0aGlzLl9sYXllcnNbaWRdO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIWxhdGxuZykge1xuXHRcdFx0bGF0bG5nID0gbGF5ZXIuZ2V0Q2VudGVyID8gbGF5ZXIuZ2V0Q2VudGVyKCkgOiBsYXllci5nZXRMYXRMbmcoKTtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5fdG9vbHRpcCAmJiB0aGlzLl9tYXApIHtcblxuXHRcdFx0Ly8gc2V0IHRvb2x0aXAgc291cmNlIHRvIHRoaXMgbGF5ZXJcblx0XHRcdHRoaXMuX3Rvb2x0aXAuX3NvdXJjZSA9IGxheWVyO1xuXG5cdFx0XHQvLyB1cGRhdGUgdGhlIHRvb2x0aXAgKGNvbnRlbnQsIGxheW91dCwgZWN0Li4uKVxuXHRcdFx0dGhpcy5fdG9vbHRpcC51cGRhdGUoKTtcblxuXHRcdFx0Ly8gb3BlbiB0aGUgdG9vbHRpcCBvbiB0aGUgbWFwXG5cdFx0XHR0aGlzLl9tYXAub3BlblRvb2x0aXAodGhpcy5fdG9vbHRpcCwgbGF0bG5nKTtcblxuXHRcdFx0Ly8gVG9vbHRpcCBjb250YWluZXIgbWF5IG5vdCBiZSBkZWZpbmVkIGlmIG5vdCBwZXJtYW5lbnQgYW5kIG5ldmVyXG5cdFx0XHQvLyBvcGVuZWQuXG5cdFx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLmludGVyYWN0aXZlICYmIHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcikge1xuXHRcdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fdG9vbHRpcC5fY29udGFpbmVyLCAnbGVhZmxldC1jbGlja2FibGUnKTtcblx0XHRcdFx0dGhpcy5hZGRJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl90b29sdGlwLl9jb250YWluZXIpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgY2xvc2VUb29sdGlwKCk6IHRoaXNcblx0Ly8gQ2xvc2VzIHRoZSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaWYgaXQgaXMgb3Blbi5cblx0Y2xvc2VUb29sdGlwOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXApIHtcblx0XHRcdHRoaXMuX3Rvb2x0aXAuX2Nsb3NlKCk7XG5cdFx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLmludGVyYWN0aXZlICYmIHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcikge1xuXHRcdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fdG9vbHRpcC5fY29udGFpbmVyLCAnbGVhZmxldC1jbGlja2FibGUnKTtcblx0XHRcdFx0dGhpcy5yZW1vdmVJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl90b29sdGlwLl9jb250YWluZXIpO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHRvZ2dsZVRvb2x0aXAoKTogdGhpc1xuXHQvLyBPcGVucyBvciBjbG9zZXMgdGhlIHRvb2x0aXAgYm91bmQgdG8gdGhpcyBsYXllciBkZXBlbmRpbmcgb24gaXRzIGN1cnJlbnQgc3RhdGUuXG5cdHRvZ2dsZVRvb2x0aXA6IGZ1bmN0aW9uICh0YXJnZXQpIHtcblx0XHRpZiAodGhpcy5fdG9vbHRpcCkge1xuXHRcdFx0aWYgKHRoaXMuX3Rvb2x0aXAuX21hcCkge1xuXHRcdFx0XHR0aGlzLmNsb3NlVG9vbHRpcCgpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dGhpcy5vcGVuVG9vbHRpcCh0YXJnZXQpO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGlzVG9vbHRpcE9wZW4oKTogYm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyIGlzIGN1cnJlbnRseSBvcGVuLlxuXHRpc1Rvb2x0aXBPcGVuOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3Rvb2x0aXAuaXNPcGVuKCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRUb29sdGlwQ29udGVudChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnR8VG9vbHRpcCk6IHRoaXNcblx0Ly8gU2V0cyB0aGUgY29udGVudCBvZiB0aGUgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyLlxuXHRzZXRUb29sdGlwQ29udGVudDogZnVuY3Rpb24gKGNvbnRlbnQpIHtcblx0XHRpZiAodGhpcy5fdG9vbHRpcCkge1xuXHRcdFx0dGhpcy5fdG9vbHRpcC5zZXRDb250ZW50KGNvbnRlbnQpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldFRvb2x0aXAoKTogVG9vbHRpcFxuXHQvLyBSZXR1cm5zIHRoZSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIuXG5cdGdldFRvb2x0aXA6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fdG9vbHRpcDtcblx0fSxcblxuXHRfb3BlblRvb2x0aXA6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGxheWVyID0gZS5sYXllciB8fCBlLnRhcmdldDtcblxuXHRcdGlmICghdGhpcy5fdG9vbHRpcCB8fCAhdGhpcy5fbWFwKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdHRoaXMub3BlblRvb2x0aXAobGF5ZXIsIHRoaXMuX3Rvb2x0aXAub3B0aW9ucy5zdGlja3kgPyBlLmxhdGxuZyA6IHVuZGVmaW5lZCk7XG5cdH0sXG5cblx0X21vdmVUb29sdGlwOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBsYXRsbmcgPSBlLmxhdGxuZywgY29udGFpbmVyUG9pbnQsIGxheWVyUG9pbnQ7XG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXAub3B0aW9ucy5zdGlja3kgJiYgZS5vcmlnaW5hbEV2ZW50KSB7XG5cdFx0XHRjb250YWluZXJQb2ludCA9IHRoaXMuX21hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLm9yaWdpbmFsRXZlbnQpO1xuXHRcdFx0bGF5ZXJQb2ludCA9IHRoaXMuX21hcC5jb250YWluZXJQb2ludFRvTGF5ZXJQb2ludChjb250YWluZXJQb2ludCk7XG5cdFx0XHRsYXRsbmcgPSB0aGlzLl9tYXAubGF5ZXJQb2ludFRvTGF0TG5nKGxheWVyUG9pbnQpO1xuXHRcdH1cblx0XHR0aGlzLl90b29sdGlwLnNldExhdExuZyhsYXRsbmcpO1xuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBMYXllckdyb3VwXHJcbiAqIEBha2EgTC5MYXllckdyb3VwXHJcbiAqIEBpbmhlcml0cyBMYXllclxyXG4gKlxyXG4gKiBVc2VkIHRvIGdyb3VwIHNldmVyYWwgbGF5ZXJzIGFuZCBoYW5kbGUgdGhlbSBhcyBvbmUuIElmIHlvdSBhZGQgaXQgdG8gdGhlIG1hcCxcclxuICogYW55IGxheWVycyBhZGRlZCBvciByZW1vdmVkIGZyb20gdGhlIGdyb3VwIHdpbGwgYmUgYWRkZWQvcmVtb3ZlZCBvbiB0aGUgbWFwIGFzXHJcbiAqIHdlbGwuIEV4dGVuZHMgYExheWVyYC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogTC5sYXllckdyb3VwKFttYXJrZXIxLCBtYXJrZXIyXSlcclxuICogXHQuYWRkTGF5ZXIocG9seWxpbmUpXHJcbiAqIFx0LmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuTGF5ZXJHcm91cCA9IEwuTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGxheWVycykge1xyXG5cdFx0dGhpcy5fbGF5ZXJzID0ge307XHJcblxyXG5cdFx0dmFyIGksIGxlbjtcclxuXHJcblx0XHRpZiAobGF5ZXJzKSB7XHJcblx0XHRcdGZvciAoaSA9IDAsIGxlbiA9IGxheWVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuYWRkTGF5ZXIobGF5ZXJzW2ldKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkTGF5ZXIobGF5ZXI6IExheWVyKTogdGhpc1xyXG5cdC8vIEFkZHMgdGhlIGdpdmVuIGxheWVyIHRvIHRoZSBncm91cC5cclxuXHRhZGRMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHR2YXIgaWQgPSB0aGlzLmdldExheWVySWQobGF5ZXIpO1xyXG5cclxuXHRcdHRoaXMuX2xheWVyc1tpZF0gPSBsYXllcjtcclxuXHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdHRoaXMuX21hcC5hZGRMYXllcihsYXllcik7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyB0aGUgZ2l2ZW4gbGF5ZXIgZnJvbSB0aGUgZ3JvdXAuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihpZDogTnVtYmVyKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIGxheWVyIHdpdGggdGhlIGdpdmVuIGludGVybmFsIElEIGZyb20gdGhlIGdyb3VwLlxyXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdHZhciBpZCA9IGxheWVyIGluIHRoaXMuX2xheWVycyA/IGxheWVyIDogdGhpcy5nZXRMYXllcklkKGxheWVyKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbWFwICYmIHRoaXMuX2xheWVyc1tpZF0pIHtcclxuXHRcdFx0dGhpcy5fbWFwLnJlbW92ZUxheWVyKHRoaXMuX2xheWVyc1tpZF0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdGRlbGV0ZSB0aGlzLl9sYXllcnNbaWRdO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaGFzTGF5ZXIobGF5ZXI6IExheWVyKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBnaXZlbiBsYXllciBpcyBjdXJyZW50bHkgYWRkZWQgdG8gdGhlIGdyb3VwLlxyXG5cdGhhc0xheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdHJldHVybiAhIWxheWVyICYmIChsYXllciBpbiB0aGlzLl9sYXllcnMgfHwgdGhpcy5nZXRMYXllcklkKGxheWVyKSBpbiB0aGlzLl9sYXllcnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY2xlYXJMYXllcnMoKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYWxsIHRoZSBsYXllcnMgZnJvbSB0aGUgZ3JvdXAuXHJcblx0Y2xlYXJMYXllcnM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMucmVtb3ZlTGF5ZXIodGhpcy5fbGF5ZXJzW2ldKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaW52b2tlKG1ldGhvZE5hbWU6IFN0cmluZywg4oCmKTogdGhpc1xyXG5cdC8vIENhbGxzIGBtZXRob2ROYW1lYCBvbiBldmVyeSBsYXllciBjb250YWluZWQgaW4gdGhpcyBncm91cCwgcGFzc2luZyBhbnlcclxuXHQvLyBhZGRpdGlvbmFsIHBhcmFtZXRlcnMuIEhhcyBubyBlZmZlY3QgaWYgdGhlIGxheWVycyBjb250YWluZWQgZG8gbm90XHJcblx0Ly8gaW1wbGVtZW50IGBtZXRob2ROYW1lYC5cclxuXHRpbnZva2U6IGZ1bmN0aW9uIChtZXRob2ROYW1lKSB7XHJcblx0XHR2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMSksXHJcblx0XHQgICAgaSwgbGF5ZXI7XHJcblxyXG5cdFx0Zm9yIChpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRsYXllciA9IHRoaXMuX2xheWVyc1tpXTtcclxuXHJcblx0XHRcdGlmIChsYXllclttZXRob2ROYW1lXSkge1xyXG5cdFx0XHRcdGxheWVyW21ldGhvZE5hbWVdLmFwcGx5KGxheWVyLCBhcmdzKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRtYXAuYWRkTGF5ZXIodGhpcy5fbGF5ZXJzW2ldKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0bWFwLnJlbW92ZUxheWVyKHRoaXMuX2xheWVyc1tpXSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBlYWNoTGF5ZXIoZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xyXG5cdC8vIEl0ZXJhdGVzIG92ZXIgdGhlIGxheWVycyBvZiB0aGUgZ3JvdXAsIG9wdGlvbmFsbHkgc3BlY2lmeWluZyBjb250ZXh0IG9mIHRoZSBpdGVyYXRvciBmdW5jdGlvbi5cclxuXHQvLyBgYGBqc1xyXG5cdC8vIGdyb3VwLmVhY2hMYXllcihmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHQvLyBcdGxheWVyLmJpbmRQb3B1cCgnSGVsbG8nKTtcclxuXHQvLyB9KTtcclxuXHQvLyBgYGBcclxuXHRlYWNoTGF5ZXI6IGZ1bmN0aW9uIChtZXRob2QsIGNvbnRleHQpIHtcclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdG1ldGhvZC5jYWxsKGNvbnRleHQsIHRoaXMuX2xheWVyc1tpXSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldExheWVyKGlkOiBOdW1iZXIpOiBMYXllclxyXG5cdC8vIFJldHVybnMgdGhlIGxheWVyIHdpdGggdGhlIGdpdmVuIGludGVybmFsIElELlxyXG5cdGdldExheWVyOiBmdW5jdGlvbiAoaWQpIHtcclxuXHRcdHJldHVybiB0aGlzLl9sYXllcnNbaWRdO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TGF5ZXJzKCk6IExheWVyW11cclxuXHQvLyBSZXR1cm5zIGFuIGFycmF5IG9mIGFsbCB0aGUgbGF5ZXJzIGFkZGVkIHRvIHRoZSBncm91cC5cclxuXHRnZXRMYXllcnM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBsYXllcnMgPSBbXTtcclxuXHJcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRsYXllcnMucHVzaCh0aGlzLl9sYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGxheWVycztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFpJbmRleCh6SW5kZXg6IE51bWJlcik6IHRoaXNcclxuXHQvLyBDYWxscyBgc2V0WkluZGV4YCBvbiBldmVyeSBsYXllciBjb250YWluZWQgaW4gdGhpcyBncm91cCwgcGFzc2luZyB0aGUgei1pbmRleC5cclxuXHRzZXRaSW5kZXg6IGZ1bmN0aW9uICh6SW5kZXgpIHtcclxuXHRcdHJldHVybiB0aGlzLmludm9rZSgnc2V0WkluZGV4JywgekluZGV4KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldExheWVySWQobGF5ZXI6IExheWVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgaW50ZXJuYWwgSUQgZm9yIGEgbGF5ZXJcclxuXHRnZXRMYXllcklkOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdHJldHVybiBMLnN0YW1wKGxheWVyKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwubGF5ZXJHcm91cChsYXllcnM6IExheWVyW10pXHJcbi8vIENyZWF0ZSBhIGxheWVyIGdyb3VwLCBvcHRpb25hbGx5IGdpdmVuIGFuIGluaXRpYWwgc2V0IG9mIGxheWVycy5cclxuTC5sYXllckdyb3VwID0gZnVuY3Rpb24gKGxheWVycykge1xyXG5cdHJldHVybiBuZXcgTC5MYXllckdyb3VwKGxheWVycyk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBGZWF0dXJlR3JvdXBcclxuICogQGFrYSBMLkZlYXR1cmVHcm91cFxyXG4gKiBAaW5oZXJpdHMgTGF5ZXJHcm91cFxyXG4gKlxyXG4gKiBFeHRlbmRlZCBgTGF5ZXJHcm91cGAgdGhhdCBtYWtlcyBpdCBlYXNpZXIgdG8gZG8gdGhlIHNhbWUgdGhpbmcgdG8gYWxsIGl0cyBtZW1iZXIgbGF5ZXJzOlxyXG4gKiAgKiBbYGJpbmRQb3B1cGBdKCNsYXllci1iaW5kcG9wdXApIGJpbmRzIGEgcG9wdXAgdG8gYWxsIG9mIHRoZSBsYXllcnMgYXQgb25jZSAobGlrZXdpc2Ugd2l0aCBbYGJpbmRUb29sdGlwYF0oI2xheWVyLWJpbmR0b29sdGlwKSlcclxuICogICogRXZlbnRzIGFyZSBwcm9wYWdhdGVkIHRvIHRoZSBgRmVhdHVyZUdyb3VwYCwgc28gaWYgdGhlIGdyb3VwIGhhcyBhbiBldmVudFxyXG4gKiBoYW5kbGVyLCBpdCB3aWxsIGhhbmRsZSBldmVudHMgZnJvbSBhbnkgb2YgdGhlIGxheWVycy4gVGhpcyBpbmNsdWRlcyBtb3VzZSBldmVudHNcclxuICogYW5kIGN1c3RvbSBldmVudHMuXHJcbiAqICAqIEhhcyBgbGF5ZXJhZGRgIGFuZCBgbGF5ZXJyZW1vdmVgIGV2ZW50c1xyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLmZlYXR1cmVHcm91cChbbWFya2VyMSwgbWFya2VyMiwgcG9seWxpbmVdKVxyXG4gKiBcdC5iaW5kUG9wdXAoJ0hlbGxvIHdvcmxkIScpXHJcbiAqIFx0Lm9uKCdjbGljaycsIGZ1bmN0aW9uKCkgeyBhbGVydCgnQ2xpY2tlZCBvbiBhIG1lbWJlciBvZiB0aGUgZ3JvdXAhJyk7IH0pXHJcbiAqIFx0LmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuRmVhdHVyZUdyb3VwID0gTC5MYXllckdyb3VwLmV4dGVuZCh7XHJcblxyXG5cdGFkZExheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdGlmICh0aGlzLmhhc0xheWVyKGxheWVyKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRsYXllci5hZGRFdmVudFBhcmVudCh0aGlzKTtcclxuXHJcblx0XHRMLkxheWVyR3JvdXAucHJvdG90eXBlLmFkZExheWVyLmNhbGwodGhpcywgbGF5ZXIpO1xyXG5cclxuXHRcdC8vIEBldmVudCBsYXllcmFkZDogTGF5ZXJFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhIGxheWVyIGlzIGFkZGVkIHRvIHRoaXMgYEZlYXR1cmVHcm91cGBcclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ2xheWVyYWRkJywge2xheWVyOiBsYXllcn0pO1xyXG5cdH0sXHJcblxyXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdGlmICghdGhpcy5oYXNMYXllcihsYXllcikpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblx0XHRpZiAobGF5ZXIgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdGxheWVyID0gdGhpcy5fbGF5ZXJzW2xheWVyXTtcclxuXHRcdH1cclxuXHJcblx0XHRsYXllci5yZW1vdmVFdmVudFBhcmVudCh0aGlzKTtcclxuXHJcblx0XHRMLkxheWVyR3JvdXAucHJvdG90eXBlLnJlbW92ZUxheWVyLmNhbGwodGhpcywgbGF5ZXIpO1xyXG5cclxuXHRcdC8vIEBldmVudCBsYXllcnJlbW92ZTogTGF5ZXJFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhIGxheWVyIGlzIHJlbW92ZWQgZnJvbSB0aGlzIGBGZWF0dXJlR3JvdXBgXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdsYXllcnJlbW92ZScsIHtsYXllcjogbGF5ZXJ9KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFN0eWxlKHN0eWxlOiBQYXRoIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgZ2l2ZW4gcGF0aCBvcHRpb25zIHRvIGVhY2ggbGF5ZXIgb2YgdGhlIGdyb3VwIHRoYXQgaGFzIGEgYHNldFN0eWxlYCBtZXRob2QuXHJcblx0c2V0U3R5bGU6IGZ1bmN0aW9uIChzdHlsZSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdzZXRTdHlsZScsIHN0eWxlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGJyaW5nVG9Gcm9udCgpOiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoZSBsYXllciBncm91cCB0byB0aGUgdG9wIG9mIGFsbCBvdGhlciBsYXllcnNcclxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmludm9rZSgnYnJpbmdUb0Zyb250Jyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvQmFjaygpOiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoZSBsYXllciBncm91cCB0byB0aGUgdG9wIG9mIGFsbCBvdGhlciBsYXllcnNcclxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdicmluZ1RvQmFjaycpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgdGhlIExhdExuZ0JvdW5kcyBvZiB0aGUgRmVhdHVyZSBHcm91cCAoY3JlYXRlZCBmcm9tIGJvdW5kcyBhbmQgY29vcmRpbmF0ZXMgb2YgaXRzIGNoaWxkcmVuKS5cclxuXHRnZXRCb3VuZHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBib3VuZHMgPSBuZXcgTC5MYXRMbmdCb3VuZHMoKTtcclxuXHJcblx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0dmFyIGxheWVyID0gdGhpcy5fbGF5ZXJzW2lkXTtcclxuXHRcdFx0Ym91bmRzLmV4dGVuZChsYXllci5nZXRCb3VuZHMgPyBsYXllci5nZXRCb3VuZHMoKSA6IGxheWVyLmdldExhdExuZygpKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBib3VuZHM7XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBmYWN0b3J5IEwuZmVhdHVyZUdyb3VwKGxheWVyczogTGF5ZXJbXSlcclxuLy8gQ3JlYXRlIGEgZmVhdHVyZSBncm91cCwgb3B0aW9uYWxseSBnaXZlbiBhbiBpbml0aWFsIHNldCBvZiBsYXllcnMuXHJcbkwuZmVhdHVyZUdyb3VwID0gZnVuY3Rpb24gKGxheWVycykge1xyXG5cdHJldHVybiBuZXcgTC5GZWF0dXJlR3JvdXAobGF5ZXJzKTtcclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBSZW5kZXJlclxuICogQGluaGVyaXRzIExheWVyXG4gKiBAYWthIEwuUmVuZGVyZXJcbiAqXG4gKiBCYXNlIGNsYXNzIGZvciB2ZWN0b3IgcmVuZGVyZXIgaW1wbGVtZW50YXRpb25zIChgU1ZHYCwgYENhbnZhc2ApLiBIYW5kbGVzIHRoZVxuICogRE9NIGNvbnRhaW5lciBvZiB0aGUgcmVuZGVyZXIsIGl0cyBib3VuZHMsIGFuZCBpdHMgem9vbSBhbmltYXRpb24uXG4gKlxuICogQSBgUmVuZGVyZXJgIHdvcmtzIGFzIGFuIGltcGxpY2l0IGxheWVyIGdyb3VwIGZvciBhbGwgYFBhdGhgcyAtIHRoZSByZW5kZXJlclxuICogaXRzZWxmIGNhbiBiZSBhZGRlZCBvciByZW1vdmVkIHRvIHRoZSBtYXAuIEFsbCBwYXRocyB1c2UgYSByZW5kZXJlciwgd2hpY2ggY2FuXG4gKiBiZSBpbXBsaWNpdCAodGhlIG1hcCB3aWxsIGRlY2lkZSB0aGUgdHlwZSBvZiByZW5kZXJlciBhbmQgdXNlIGl0IGF1dG9tYXRpY2FsbHkpXG4gKiBvciBleHBsaWNpdCAodXNpbmcgdGhlIFtgcmVuZGVyZXJgXSgjcGF0aC1yZW5kZXJlcikgb3B0aW9uIG9mIHRoZSBwYXRoKS5cbiAqXG4gKiBEbyBub3QgdXNlIHRoaXMgY2xhc3MgZGlyZWN0bHksIHVzZSBgU1ZHYCBhbmQgYENhbnZhc2AgaW5zdGVhZC5cbiAqXG4gKiBAZXZlbnQgdXBkYXRlOiBFdmVudFxuICogRmlyZWQgd2hlbiB0aGUgcmVuZGVyZXIgdXBkYXRlcyBpdHMgYm91bmRzLCBjZW50ZXIgYW5kIHpvb20sIGZvciBleGFtcGxlIHdoZW5cbiAqIGl0cyBtYXAgaGFzIG1vdmVkXG4gKi9cblxuTC5SZW5kZXJlciA9IEwuTGF5ZXIuZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFJlbmRlcmVyIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gcGFkZGluZzogTnVtYmVyID0gMC4xXG5cdFx0Ly8gSG93IG11Y2ggdG8gZXh0ZW5kIHRoZSBjbGlwIGFyZWEgYXJvdW5kIHRoZSBtYXAgdmlldyAocmVsYXRpdmUgdG8gaXRzIHNpemUpXG5cdFx0Ly8gZS5nLiAwLjEgd291bGQgYmUgMTAlIG9mIG1hcCB2aWV3IGluIGVhY2ggZGlyZWN0aW9uXG5cdFx0cGFkZGluZzogMC4xXG5cdH0sXG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdFx0TC5zdGFtcCh0aGlzKTtcblx0XHR0aGlzLl9sYXllcnMgPSB0aGlzLl9sYXllcnMgfHwge307XG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX2NvbnRhaW5lcikge1xuXHRcdFx0dGhpcy5faW5pdENvbnRhaW5lcigpOyAvLyBkZWZpbmVkIGJ5IHJlbmRlcmVyIGltcGxlbWVudGF0aW9uc1xuXG5cdFx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LXpvb20tYW5pbWF0ZWQnKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLmdldFBhbmUoKS5hcHBlbmRDaGlsZCh0aGlzLl9jb250YWluZXIpO1xuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHRcdHRoaXMub24oJ3VwZGF0ZScsIHRoaXMuX3VwZGF0ZVBhdGhzLCB0aGlzKTtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fY29udGFpbmVyKTtcblx0XHR0aGlzLm9mZigndXBkYXRlJywgdGhpcy5fdXBkYXRlUGF0aHMsIHRoaXMpO1xuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSB7XG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0LFxuXHRcdFx0em9vbTogdGhpcy5fb25ab29tLFxuXHRcdFx0bW92ZWVuZDogdGhpcy5fdXBkYXRlLFxuXHRcdFx0em9vbWVuZDogdGhpcy5fb25ab29tRW5kXG5cdFx0fTtcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9vbkFuaW1ab29tO1xuXHRcdH1cblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdF9vbkFuaW1ab29tOiBmdW5jdGlvbiAoZXYpIHtcblx0XHR0aGlzLl91cGRhdGVUcmFuc2Zvcm0oZXYuY2VudGVyLCBldi56b29tKTtcblx0fSxcblxuXHRfb25ab29tOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fdXBkYXRlVHJhbnNmb3JtKHRoaXMuX21hcC5nZXRDZW50ZXIoKSwgdGhpcy5fbWFwLmdldFpvb20oKSk7XG5cdH0sXG5cblx0X3VwZGF0ZVRyYW5zZm9ybTogZnVuY3Rpb24gKGNlbnRlciwgem9vbSkge1xuXHRcdHZhciBzY2FsZSA9IHRoaXMuX21hcC5nZXRab29tU2NhbGUoem9vbSwgdGhpcy5fem9vbSksXG5cdFx0ICAgIHBvc2l0aW9uID0gTC5Eb21VdGlsLmdldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lciksXG5cdFx0ICAgIHZpZXdIYWxmID0gdGhpcy5fbWFwLmdldFNpemUoKS5tdWx0aXBseUJ5KDAuNSArIHRoaXMub3B0aW9ucy5wYWRkaW5nKSxcblx0XHQgICAgY3VycmVudENlbnRlclBvaW50ID0gdGhpcy5fbWFwLnByb2plY3QodGhpcy5fY2VudGVyLCB6b29tKSxcblx0XHQgICAgZGVzdENlbnRlclBvaW50ID0gdGhpcy5fbWFwLnByb2plY3QoY2VudGVyLCB6b29tKSxcblx0XHQgICAgY2VudGVyT2Zmc2V0ID0gZGVzdENlbnRlclBvaW50LnN1YnRyYWN0KGN1cnJlbnRDZW50ZXJQb2ludCksXG5cblx0XHQgICAgdG9wTGVmdE9mZnNldCA9IHZpZXdIYWxmLm11bHRpcGx5QnkoLXNjYWxlKS5hZGQocG9zaXRpb24pLmFkZCh2aWV3SGFsZikuc3VidHJhY3QoY2VudGVyT2Zmc2V0KTtcblxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0odGhpcy5fY29udGFpbmVyLCB0b3BMZWZ0T2Zmc2V0LCBzY2FsZSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aGlzLl9jb250YWluZXIsIHRvcExlZnRPZmZzZXQpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVzZXQ6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl91cGRhdGUoKTtcblx0XHR0aGlzLl91cGRhdGVUcmFuc2Zvcm0odGhpcy5fY2VudGVyLCB0aGlzLl96b29tKTtcblxuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0dGhpcy5fbGF5ZXJzW2lkXS5fcmVzZXQoKTtcblx0XHR9XG5cdH0sXG5cblx0X29uWm9vbUVuZDogZnVuY3Rpb24gKCkge1xuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0dGhpcy5fbGF5ZXJzW2lkXS5fcHJvamVjdCgpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlUGF0aHM6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9sYXllcnMpIHtcblx0XHRcdHRoaXMuX2xheWVyc1tpZF0uX3VwZGF0ZSgpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gVXBkYXRlIHBpeGVsIGJvdW5kcyBvZiByZW5kZXJlciBjb250YWluZXIgKGZvciBwb3NpdGlvbmluZy9zaXppbmcvY2xpcHBpbmcgbGF0ZXIpXG5cdFx0Ly8gU3ViY2xhc3NlcyBhcmUgcmVzcG9uc2libGUgb2YgZmlyaW5nIHRoZSAndXBkYXRlJyBldmVudC5cblx0XHR2YXIgcCA9IHRoaXMub3B0aW9ucy5wYWRkaW5nLFxuXHRcdCAgICBzaXplID0gdGhpcy5fbWFwLmdldFNpemUoKSxcblx0XHQgICAgbWluID0gdGhpcy5fbWFwLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KHNpemUubXVsdGlwbHlCeSgtcCkpLnJvdW5kKCk7XG5cblx0XHR0aGlzLl9ib3VuZHMgPSBuZXcgTC5Cb3VuZHMobWluLCBtaW4uYWRkKHNpemUubXVsdGlwbHlCeSgxICsgcCAqIDIpKS5yb3VuZCgpKTtcblxuXHRcdHRoaXMuX2NlbnRlciA9IHRoaXMuX21hcC5nZXRDZW50ZXIoKTtcblx0XHR0aGlzLl96b29tID0gdGhpcy5fbWFwLmdldFpvb20oKTtcblx0fVxufSk7XG5cblxuTC5NYXAuaW5jbHVkZSh7XG5cdC8vIEBuYW1lc3BhY2UgTWFwOyBAbWV0aG9kIGdldFJlbmRlcmVyKGxheWVyOiBQYXRoKTogUmVuZGVyZXJcblx0Ly8gUmV0dXJucyB0aGUgaW5zdGFuY2Ugb2YgYFJlbmRlcmVyYCB0aGF0IHNob3VsZCBiZSB1c2VkIHRvIHJlbmRlciB0aGUgZ2l2ZW5cblx0Ly8gYFBhdGhgLiBJdCB3aWxsIGVuc3VyZSB0aGF0IHRoZSBgcmVuZGVyZXJgIG9wdGlvbnMgb2YgdGhlIG1hcCBhbmQgcGF0aHNcblx0Ly8gYXJlIHJlc3BlY3RlZCwgYW5kIHRoYXQgdGhlIHJlbmRlcmVycyBkbyBleGlzdCBvbiB0aGUgbWFwLlxuXHRnZXRSZW5kZXJlcjogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0Ly8gQG5hbWVzcGFjZSBQYXRoOyBAb3B0aW9uIHJlbmRlcmVyOiBSZW5kZXJlclxuXHRcdC8vIFVzZSB0aGlzIHNwZWNpZmljIGluc3RhbmNlIG9mIGBSZW5kZXJlcmAgZm9yIHRoaXMgcGF0aC4gVGFrZXNcblx0XHQvLyBwcmVjZWRlbmNlIG92ZXIgdGhlIG1hcCdzIFtkZWZhdWx0IHJlbmRlcmVyXSgjbWFwLXJlbmRlcmVyKS5cblx0XHR2YXIgcmVuZGVyZXIgPSBsYXllci5vcHRpb25zLnJlbmRlcmVyIHx8IHRoaXMuX2dldFBhbmVSZW5kZXJlcihsYXllci5vcHRpb25zLnBhbmUpIHx8IHRoaXMub3B0aW9ucy5yZW5kZXJlciB8fCB0aGlzLl9yZW5kZXJlcjtcblxuXHRcdGlmICghcmVuZGVyZXIpIHtcblx0XHRcdC8vIEBuYW1lc3BhY2UgTWFwOyBAb3B0aW9uIHByZWZlckNhbnZhczogQm9vbGVhbiA9IGZhbHNlXG5cdFx0XHQvLyBXaGV0aGVyIGBQYXRoYHMgc2hvdWxkIGJlIHJlbmRlcmVkIG9uIGEgYENhbnZhc2AgcmVuZGVyZXIuXG5cdFx0XHQvLyBCeSBkZWZhdWx0LCBhbGwgYFBhdGhgcyBhcmUgcmVuZGVyZWQgaW4gYSBgU1ZHYCByZW5kZXJlci5cblx0XHRcdHJlbmRlcmVyID0gdGhpcy5fcmVuZGVyZXIgPSAodGhpcy5vcHRpb25zLnByZWZlckNhbnZhcyAmJiBMLmNhbnZhcygpKSB8fCBMLnN2ZygpO1xuXHRcdH1cblxuXHRcdGlmICghdGhpcy5oYXNMYXllcihyZW5kZXJlcikpIHtcblx0XHRcdHRoaXMuYWRkTGF5ZXIocmVuZGVyZXIpO1xuXHRcdH1cblx0XHRyZXR1cm4gcmVuZGVyZXI7XG5cdH0sXG5cblx0X2dldFBhbmVSZW5kZXJlcjogZnVuY3Rpb24gKG5hbWUpIHtcblx0XHRpZiAobmFtZSA9PT0gJ292ZXJsYXlQYW5lJyB8fCBuYW1lID09PSB1bmRlZmluZWQpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHR2YXIgcmVuZGVyZXIgPSB0aGlzLl9wYW5lUmVuZGVyZXJzW25hbWVdO1xuXHRcdGlmIChyZW5kZXJlciA9PT0gdW5kZWZpbmVkKSB7XG5cdFx0XHRyZW5kZXJlciA9IChMLlNWRyAmJiBMLnN2Zyh7cGFuZTogbmFtZX0pKSB8fCAoTC5DYW52YXMgJiYgTC5jYW52YXMoe3BhbmU6IG5hbWV9KSk7XG5cdFx0XHR0aGlzLl9wYW5lUmVuZGVyZXJzW25hbWVdID0gcmVuZGVyZXI7XG5cdFx0fVxuXHRcdHJldHVybiByZW5kZXJlcjtcblx0fVxufSk7XG5cblxuXG4vKlxuICogQGNsYXNzIFBhdGhcbiAqIEBha2EgTC5QYXRoXG4gKiBAaW5oZXJpdHMgSW50ZXJhY3RpdmUgbGF5ZXJcbiAqXG4gKiBBbiBhYnN0cmFjdCBjbGFzcyB0aGF0IGNvbnRhaW5zIG9wdGlvbnMgYW5kIGNvbnN0YW50cyBzaGFyZWQgYmV0d2VlbiB2ZWN0b3JcbiAqIG92ZXJsYXlzIChQb2x5Z29uLCBQb2x5bGluZSwgQ2lyY2xlKS4gRG8gbm90IHVzZSBpdCBkaXJlY3RseS4gRXh0ZW5kcyBgTGF5ZXJgLlxuICovXG5cbkwuUGF0aCA9IEwuTGF5ZXIuZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFBhdGggb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBzdHJva2U6IEJvb2xlYW4gPSB0cnVlXG5cdFx0Ly8gV2hldGhlciB0byBkcmF3IHN0cm9rZSBhbG9uZyB0aGUgcGF0aC4gU2V0IGl0IHRvIGBmYWxzZWAgdG8gZGlzYWJsZSBib3JkZXJzIG9uIHBvbHlnb25zIG9yIGNpcmNsZXMuXG5cdFx0c3Ryb2tlOiB0cnVlLFxuXG5cdFx0Ly8gQG9wdGlvbiBjb2xvcjogU3RyaW5nID0gJyMzMzg4ZmYnXG5cdFx0Ly8gU3Ryb2tlIGNvbG9yXG5cdFx0Y29sb3I6ICcjMzM4OGZmJyxcblxuXHRcdC8vIEBvcHRpb24gd2VpZ2h0OiBOdW1iZXIgPSAzXG5cdFx0Ly8gU3Ryb2tlIHdpZHRoIGluIHBpeGVsc1xuXHRcdHdlaWdodDogMyxcblxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMS4wXG5cdFx0Ly8gU3Ryb2tlIG9wYWNpdHlcblx0XHRvcGFjaXR5OiAxLFxuXG5cdFx0Ly8gQG9wdGlvbiBsaW5lQ2FwOiBTdHJpbmc9ICdyb3VuZCdcblx0XHQvLyBBIHN0cmluZyB0aGF0IGRlZmluZXMgW3NoYXBlIHRvIGJlIHVzZWQgYXQgdGhlIGVuZF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtbGluZWNhcCkgb2YgdGhlIHN0cm9rZS5cblx0XHRsaW5lQ2FwOiAncm91bmQnLFxuXG5cdFx0Ly8gQG9wdGlvbiBsaW5lSm9pbjogU3RyaW5nID0gJ3JvdW5kJ1xuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyBbc2hhcGUgdG8gYmUgdXNlZCBhdCB0aGUgY29ybmVyc10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtbGluZWpvaW4pIG9mIHRoZSBzdHJva2UuXG5cdFx0bGluZUpvaW46ICdyb3VuZCcsXG5cblx0XHQvLyBAb3B0aW9uIGRhc2hBcnJheTogU3RyaW5nID0gbnVsbFxuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyB0aGUgc3Ryb2tlIFtkYXNoIHBhdHRlcm5dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRy9BdHRyaWJ1dGUvc3Ryb2tlLWRhc2hhcnJheSkuIERvZXNuJ3Qgd29yayBvbiBgQ2FudmFzYC1wb3dlcmVkIGxheWVycyBpbiBbc29tZSBvbGQgYnJvd3NlcnNdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9DYW52YXNSZW5kZXJpbmdDb250ZXh0MkQvc2V0TGluZURhc2gjQnJvd3Nlcl9jb21wYXRpYmlsaXR5KS5cblx0XHRkYXNoQXJyYXk6IG51bGwsXG5cblx0XHQvLyBAb3B0aW9uIGRhc2hPZmZzZXQ6IFN0cmluZyA9IG51bGxcblx0XHQvLyBBIHN0cmluZyB0aGF0IGRlZmluZXMgdGhlIFtkaXN0YW5jZSBpbnRvIHRoZSBkYXNoIHBhdHRlcm4gdG8gc3RhcnQgdGhlIGRhc2hdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRy9BdHRyaWJ1dGUvc3Ryb2tlLWRhc2hvZmZzZXQpLiBEb2Vzbid0IHdvcmsgb24gYENhbnZhc2AtcG93ZXJlZCBsYXllcnMgaW4gW3NvbWUgb2xkIGJyb3dzZXJzXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEL3NldExpbmVEYXNoI0Jyb3dzZXJfY29tcGF0aWJpbGl0eSkuXG5cdFx0ZGFzaE9mZnNldDogbnVsbCxcblxuXHRcdC8vIEBvcHRpb24gZmlsbDogQm9vbGVhbiA9IGRlcGVuZHNcblx0XHQvLyBXaGV0aGVyIHRvIGZpbGwgdGhlIHBhdGggd2l0aCBjb2xvci4gU2V0IGl0IHRvIGBmYWxzZWAgdG8gZGlzYWJsZSBmaWxsaW5nIG9uIHBvbHlnb25zIG9yIGNpcmNsZXMuXG5cdFx0ZmlsbDogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIGZpbGxDb2xvcjogU3RyaW5nID0gKlxuXHRcdC8vIEZpbGwgY29sb3IuIERlZmF1bHRzIHRvIHRoZSB2YWx1ZSBvZiB0aGUgW2Bjb2xvcmBdKCNwYXRoLWNvbG9yKSBvcHRpb25cblx0XHRmaWxsQ29sb3I6IG51bGwsXG5cblx0XHQvLyBAb3B0aW9uIGZpbGxPcGFjaXR5OiBOdW1iZXIgPSAwLjJcblx0XHQvLyBGaWxsIG9wYWNpdHkuXG5cdFx0ZmlsbE9wYWNpdHk6IDAuMixcblxuXHRcdC8vIEBvcHRpb24gZmlsbFJ1bGU6IFN0cmluZyA9ICdldmVub2RkJ1xuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyBbaG93IHRoZSBpbnNpZGUgb2YgYSBzaGFwZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9maWxsLXJ1bGUpIGlzIGRldGVybWluZWQuXG5cdFx0ZmlsbFJ1bGU6ICdldmVub2RkJyxcblxuXHRcdC8vIGNsYXNzTmFtZTogJycsXG5cblx0XHQvLyBPcHRpb24gaW5oZXJpdGVkIGZyb20gXCJJbnRlcmFjdGl2ZSBsYXllclwiIGFic3RyYWN0IGNsYXNzXG5cdFx0aW50ZXJhY3RpdmU6IHRydWVcblx0fSxcblxuXHRiZWZvcmVBZGQ6IGZ1bmN0aW9uIChtYXApIHtcblx0XHQvLyBSZW5kZXJlciBpcyBzZXQgaGVyZSBiZWNhdXNlIHdlIG5lZWQgdG8gY2FsbCByZW5kZXJlci5nZXRFdmVudHNcblx0XHQvLyBiZWZvcmUgdGhpcy5nZXRFdmVudHMuXG5cdFx0dGhpcy5fcmVuZGVyZXIgPSBtYXAuZ2V0UmVuZGVyZXIodGhpcyk7XG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5faW5pdFBhdGgodGhpcyk7XG5cdFx0dGhpcy5fcmVzZXQoKTtcblx0XHR0aGlzLl9yZW5kZXJlci5fYWRkUGF0aCh0aGlzKTtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbmRlcmVyLl9yZW1vdmVQYXRoKHRoaXMpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVkcmF3KCk6IHRoaXNcblx0Ly8gUmVkcmF3cyB0aGUgbGF5ZXIuIFNvbWV0aW1lcyB1c2VmdWwgYWZ0ZXIgeW91IGNoYW5nZWQgdGhlIGNvb3JkaW5hdGVzIHRoYXQgdGhlIHBhdGggdXNlcy5cblx0cmVkcmF3OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0dGhpcy5fcmVuZGVyZXIuX3VwZGF0ZVBhdGgodGhpcyk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0U3R5bGUoc3R5bGU6IFBhdGggb3B0aW9ucyk6IHRoaXNcblx0Ly8gQ2hhbmdlcyB0aGUgYXBwZWFyYW5jZSBvZiBhIFBhdGggYmFzZWQgb24gdGhlIG9wdGlvbnMgaW4gdGhlIGBQYXRoIG9wdGlvbnNgIG9iamVjdC5cblx0c2V0U3R5bGU6IGZ1bmN0aW9uIChzdHlsZSkge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBzdHlsZSk7XG5cdFx0aWYgKHRoaXMuX3JlbmRlcmVyKSB7XG5cdFx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlU3R5bGUodGhpcyk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250KCk6IHRoaXNcblx0Ly8gQnJpbmdzIHRoZSBsYXllciB0byB0aGUgdG9wIG9mIGFsbCBwYXRoIGxheWVycy5cblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX3JlbmRlcmVyKSB7XG5cdFx0XHR0aGlzLl9yZW5kZXJlci5fYnJpbmdUb0Zyb250KHRoaXMpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGJyaW5nVG9CYWNrKCk6IHRoaXNcblx0Ly8gQnJpbmdzIHRoZSBsYXllciB0byB0aGUgYm90dG9tIG9mIGFsbCBwYXRoIGxheWVycy5cblx0YnJpbmdUb0JhY2s6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fcmVuZGVyZXIpIHtcblx0XHRcdHRoaXMuX3JlbmRlcmVyLl9icmluZ1RvQmFjayh0aGlzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Z2V0RWxlbWVudDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9wYXRoO1xuXHR9LFxuXG5cdF9yZXNldDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIGRlZmluZWQgaW4gY2hpbGRyZW4gY2xhc3Nlc1xuXHRcdHRoaXMuX3Byb2plY3QoKTtcblx0XHR0aGlzLl91cGRhdGUoKTtcblx0fSxcblxuXHRfY2xpY2tUb2xlcmFuY2U6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyB1c2VkIHdoZW4gZG9pbmcgaGl0IGRldGVjdGlvbiBmb3IgQ2FudmFzIGxheWVyc1xuXHRcdHJldHVybiAodGhpcy5vcHRpb25zLnN0cm9rZSA/IHRoaXMub3B0aW9ucy53ZWlnaHQgLyAyIDogMCkgKyAoTC5Ccm93c2VyLnRvdWNoID8gMTAgOiAwKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIExpbmVVdGlsXHJcbiAqXHJcbiAqIFZhcmlvdXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIHBvbHlpbmUgcG9pbnRzIHByb2Nlc3NpbmcsIHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5IHRvIG1ha2UgcG9seWxpbmVzIGxpZ2h0bmluZy1mYXN0LlxyXG4gKi9cclxuXHJcbkwuTGluZVV0aWwgPSB7XHJcblxyXG5cdC8vIFNpbXBsaWZ5IHBvbHlsaW5lIHdpdGggdmVydGV4IHJlZHVjdGlvbiBhbmQgRG91Z2xhcy1QZXVja2VyIHNpbXBsaWZpY2F0aW9uLlxyXG5cdC8vIEltcHJvdmVzIHJlbmRlcmluZyBwZXJmb3JtYW5jZSBkcmFtYXRpY2FsbHkgYnkgbGVzc2VuaW5nIHRoZSBudW1iZXIgb2YgcG9pbnRzIHRvIGRyYXcuXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzaW1wbGlmeShwb2ludHM6IFBvaW50W10sIHRvbGVyYW5jZTogTnVtYmVyKTogUG9pbnRbXVxyXG5cdC8vIERyYW1hdGljYWxseSByZWR1Y2VzIHRoZSBudW1iZXIgb2YgcG9pbnRzIGluIGEgcG9seWxpbmUgd2hpbGUgcmV0YWluaW5nXHJcblx0Ly8gaXRzIHNoYXBlIGFuZCByZXR1cm5zIGEgbmV3IGFycmF5IG9mIHNpbXBsaWZpZWQgcG9pbnRzLCB1c2luZyB0aGVcclxuXHQvLyBbRG91Z2xhcy1QZXVja2VyIGFsZ29yaXRobV0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Eb3VnbGFzLVBldWNrZXJfYWxnb3JpdGhtKS5cclxuXHQvLyBVc2VkIGZvciBhIGh1Z2UgcGVyZm9ybWFuY2UgYm9vc3Qgd2hlbiBwcm9jZXNzaW5nL2Rpc3BsYXlpbmcgTGVhZmxldCBwb2x5bGluZXMgZm9yXHJcblx0Ly8gZWFjaCB6b29tIGxldmVsIGFuZCBhbHNvIHJlZHVjaW5nIHZpc3VhbCBub2lzZS4gdG9sZXJhbmNlIGFmZmVjdHMgdGhlIGFtb3VudCBvZlxyXG5cdC8vIHNpbXBsaWZpY2F0aW9uIChsZXNzZXIgdmFsdWUgbWVhbnMgaGlnaGVyIHF1YWxpdHkgYnV0IHNsb3dlciBhbmQgd2l0aCBtb3JlIHBvaW50cykuXHJcblx0Ly8gQWxzbyByZWxlYXNlZCBhcyBhIHNlcGFyYXRlZCBtaWNyby1saWJyYXJ5IFtTaW1wbGlmeS5qc10oaHR0cDovL21vdXJuZXIuZ2l0aHViLmNvbS9zaW1wbGlmeS1qcy8pLlxyXG5cdHNpbXBsaWZ5OiBmdW5jdGlvbiAocG9pbnRzLCB0b2xlcmFuY2UpIHtcclxuXHRcdGlmICghdG9sZXJhbmNlIHx8ICFwb2ludHMubGVuZ3RoKSB7XHJcblx0XHRcdHJldHVybiBwb2ludHMuc2xpY2UoKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgc3FUb2xlcmFuY2UgPSB0b2xlcmFuY2UgKiB0b2xlcmFuY2U7XHJcblxyXG5cdFx0Ly8gc3RhZ2UgMTogdmVydGV4IHJlZHVjdGlvblxyXG5cdFx0cG9pbnRzID0gdGhpcy5fcmVkdWNlUG9pbnRzKHBvaW50cywgc3FUb2xlcmFuY2UpO1xyXG5cclxuXHRcdC8vIHN0YWdlIDI6IERvdWdsYXMtUGV1Y2tlciBzaW1wbGlmaWNhdGlvblxyXG5cdFx0cG9pbnRzID0gdGhpcy5fc2ltcGxpZnlEUChwb2ludHMsIHNxVG9sZXJhbmNlKTtcclxuXHJcblx0XHRyZXR1cm4gcG9pbnRzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBwb2ludFRvU2VnbWVudERpc3RhbmNlKHA6IFBvaW50LCBwMTogUG9pbnQsIHAyOiBQb2ludCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGRpc3RhbmNlIGJldHdlZW4gcG9pbnQgYHBgIGFuZCBzZWdtZW50IGBwMWAgdG8gYHAyYC5cclxuXHRwb2ludFRvU2VnbWVudERpc3RhbmNlOiAgZnVuY3Rpb24gKHAsIHAxLCBwMikge1xyXG5cdFx0cmV0dXJuIE1hdGguc3FydCh0aGlzLl9zcUNsb3Nlc3RQb2ludE9uU2VnbWVudChwLCBwMSwgcDIsIHRydWUpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY2xvc2VzdFBvaW50T25TZWdtZW50KHA6IFBvaW50LCBwMTogUG9pbnQsIHAyOiBQb2ludCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGNsb3Nlc3QgcG9pbnQgZnJvbSBhIHBvaW50IGBwYCBvbiBhIHNlZ21lbnQgYHAxYCB0byBgcDJgLlxyXG5cdGNsb3Nlc3RQb2ludE9uU2VnbWVudDogZnVuY3Rpb24gKHAsIHAxLCBwMikge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50KHAsIHAxLCBwMik7XHJcblx0fSxcclxuXHJcblx0Ly8gRG91Z2xhcy1QZXVja2VyIHNpbXBsaWZpY2F0aW9uLCBzZWUgaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Eb3VnbGFzLVBldWNrZXJfYWxnb3JpdGhtXHJcblx0X3NpbXBsaWZ5RFA6IGZ1bmN0aW9uIChwb2ludHMsIHNxVG9sZXJhbmNlKSB7XHJcblxyXG5cdFx0dmFyIGxlbiA9IHBvaW50cy5sZW5ndGgsXHJcblx0XHQgICAgQXJyYXlDb25zdHJ1Y3RvciA9IHR5cGVvZiBVaW50OEFycmF5ICE9PSB1bmRlZmluZWQgKyAnJyA/IFVpbnQ4QXJyYXkgOiBBcnJheSxcclxuXHRcdCAgICBtYXJrZXJzID0gbmV3IEFycmF5Q29uc3RydWN0b3IobGVuKTtcclxuXHJcblx0XHRtYXJrZXJzWzBdID0gbWFya2Vyc1tsZW4gLSAxXSA9IDE7XHJcblxyXG5cdFx0dGhpcy5fc2ltcGxpZnlEUFN0ZXAocG9pbnRzLCBtYXJrZXJzLCBzcVRvbGVyYW5jZSwgMCwgbGVuIC0gMSk7XHJcblxyXG5cdFx0dmFyIGksXHJcblx0XHQgICAgbmV3UG9pbnRzID0gW107XHJcblxyXG5cdFx0Zm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdGlmIChtYXJrZXJzW2ldKSB7XHJcblx0XHRcdFx0bmV3UG9pbnRzLnB1c2gocG9pbnRzW2ldKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBuZXdQb2ludHM7XHJcblx0fSxcclxuXHJcblx0X3NpbXBsaWZ5RFBTdGVwOiBmdW5jdGlvbiAocG9pbnRzLCBtYXJrZXJzLCBzcVRvbGVyYW5jZSwgZmlyc3QsIGxhc3QpIHtcclxuXHJcblx0XHR2YXIgbWF4U3FEaXN0ID0gMCxcclxuXHRcdCAgICBpbmRleCwgaSwgc3FEaXN0O1xyXG5cclxuXHRcdGZvciAoaSA9IGZpcnN0ICsgMTsgaSA8PSBsYXN0IC0gMTsgaSsrKSB7XHJcblx0XHRcdHNxRGlzdCA9IHRoaXMuX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50KHBvaW50c1tpXSwgcG9pbnRzW2ZpcnN0XSwgcG9pbnRzW2xhc3RdLCB0cnVlKTtcclxuXHJcblx0XHRcdGlmIChzcURpc3QgPiBtYXhTcURpc3QpIHtcclxuXHRcdFx0XHRpbmRleCA9IGk7XHJcblx0XHRcdFx0bWF4U3FEaXN0ID0gc3FEaXN0O1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG1heFNxRGlzdCA+IHNxVG9sZXJhbmNlKSB7XHJcblx0XHRcdG1hcmtlcnNbaW5kZXhdID0gMTtcclxuXHJcblx0XHRcdHRoaXMuX3NpbXBsaWZ5RFBTdGVwKHBvaW50cywgbWFya2Vycywgc3FUb2xlcmFuY2UsIGZpcnN0LCBpbmRleCk7XHJcblx0XHRcdHRoaXMuX3NpbXBsaWZ5RFBTdGVwKHBvaW50cywgbWFya2Vycywgc3FUb2xlcmFuY2UsIGluZGV4LCBsYXN0KTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyByZWR1Y2UgcG9pbnRzIHRoYXQgYXJlIHRvbyBjbG9zZSB0byBlYWNoIG90aGVyIHRvIGEgc2luZ2xlIHBvaW50XHJcblx0X3JlZHVjZVBvaW50czogZnVuY3Rpb24gKHBvaW50cywgc3FUb2xlcmFuY2UpIHtcclxuXHRcdHZhciByZWR1Y2VkUG9pbnRzID0gW3BvaW50c1swXV07XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IDEsIHByZXYgPSAwLCBsZW4gPSBwb2ludHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0aWYgKHRoaXMuX3NxRGlzdChwb2ludHNbaV0sIHBvaW50c1twcmV2XSkgPiBzcVRvbGVyYW5jZSkge1xyXG5cdFx0XHRcdHJlZHVjZWRQb2ludHMucHVzaChwb2ludHNbaV0pO1xyXG5cdFx0XHRcdHByZXYgPSBpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRpZiAocHJldiA8IGxlbiAtIDEpIHtcclxuXHRcdFx0cmVkdWNlZFBvaW50cy5wdXNoKHBvaW50c1tsZW4gLSAxXSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gcmVkdWNlZFBvaW50cztcclxuXHR9LFxyXG5cclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGNsaXBTZWdtZW50KGE6IFBvaW50LCBiOiBQb2ludCwgYm91bmRzOiBCb3VuZHMsIHVzZUxhc3RDb2RlPzogQm9vbGVhbiwgcm91bmQ/OiBCb29sZWFuKTogUG9pbnRbXXxCb29sZWFuXHJcblx0Ly8gQ2xpcHMgdGhlIHNlZ21lbnQgYSB0byBiIGJ5IHJlY3Rhbmd1bGFyIGJvdW5kcyB3aXRoIHRoZVxyXG5cdC8vIFtDb2hlbi1TdXRoZXJsYW5kIGFsZ29yaXRobV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29oZW4lRTIlODAlOTNTdXRoZXJsYW5kX2FsZ29yaXRobSlcclxuXHQvLyAobW9kaWZ5aW5nIHRoZSBzZWdtZW50IHBvaW50cyBkaXJlY3RseSEpLiBVc2VkIGJ5IExlYWZsZXQgdG8gb25seSBzaG93IHBvbHlsaW5lXHJcblx0Ly8gcG9pbnRzIHRoYXQgYXJlIG9uIHRoZSBzY3JlZW4gb3IgbmVhciwgaW5jcmVhc2luZyBwZXJmb3JtYW5jZS5cclxuXHRjbGlwU2VnbWVudDogZnVuY3Rpb24gKGEsIGIsIGJvdW5kcywgdXNlTGFzdENvZGUsIHJvdW5kKSB7XHJcblx0XHR2YXIgY29kZUEgPSB1c2VMYXN0Q29kZSA/IHRoaXMuX2xhc3RDb2RlIDogdGhpcy5fZ2V0Qml0Q29kZShhLCBib3VuZHMpLFxyXG5cdFx0ICAgIGNvZGVCID0gdGhpcy5fZ2V0Qml0Q29kZShiLCBib3VuZHMpLFxyXG5cclxuXHRcdCAgICBjb2RlT3V0LCBwLCBuZXdDb2RlO1xyXG5cclxuXHRcdC8vIHNhdmUgMm5kIGNvZGUgdG8gYXZvaWQgY2FsY3VsYXRpbmcgaXQgb24gdGhlIG5leHQgc2VnbWVudFxyXG5cdFx0dGhpcy5fbGFzdENvZGUgPSBjb2RlQjtcclxuXHJcblx0XHR3aGlsZSAodHJ1ZSkge1xyXG5cdFx0XHQvLyBpZiBhLGIgaXMgaW5zaWRlIHRoZSBjbGlwIHdpbmRvdyAodHJpdmlhbCBhY2NlcHQpXHJcblx0XHRcdGlmICghKGNvZGVBIHwgY29kZUIpKSB7XHJcblx0XHRcdFx0cmV0dXJuIFthLCBiXTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gaWYgYSxiIGlzIG91dHNpZGUgdGhlIGNsaXAgd2luZG93ICh0cml2aWFsIHJlamVjdClcclxuXHRcdFx0aWYgKGNvZGVBICYgY29kZUIpIHtcclxuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIG90aGVyIGNhc2VzXHJcblx0XHRcdGNvZGVPdXQgPSBjb2RlQSB8fCBjb2RlQjtcclxuXHRcdFx0cCA9IHRoaXMuX2dldEVkZ2VJbnRlcnNlY3Rpb24oYSwgYiwgY29kZU91dCwgYm91bmRzLCByb3VuZCk7XHJcblx0XHRcdG5ld0NvZGUgPSB0aGlzLl9nZXRCaXRDb2RlKHAsIGJvdW5kcyk7XHJcblxyXG5cdFx0XHRpZiAoY29kZU91dCA9PT0gY29kZUEpIHtcclxuXHRcdFx0XHRhID0gcDtcclxuXHRcdFx0XHRjb2RlQSA9IG5ld0NvZGU7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0YiA9IHA7XHJcblx0XHRcdFx0Y29kZUIgPSBuZXdDb2RlO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2dldEVkZ2VJbnRlcnNlY3Rpb246IGZ1bmN0aW9uIChhLCBiLCBjb2RlLCBib3VuZHMsIHJvdW5kKSB7XHJcblx0XHR2YXIgZHggPSBiLnggLSBhLngsXHJcblx0XHQgICAgZHkgPSBiLnkgLSBhLnksXHJcblx0XHQgICAgbWluID0gYm91bmRzLm1pbixcclxuXHRcdCAgICBtYXggPSBib3VuZHMubWF4LFxyXG5cdFx0ICAgIHgsIHk7XHJcblxyXG5cdFx0aWYgKGNvZGUgJiA4KSB7IC8vIHRvcFxyXG5cdFx0XHR4ID0gYS54ICsgZHggKiAobWF4LnkgLSBhLnkpIC8gZHk7XHJcblx0XHRcdHkgPSBtYXgueTtcclxuXHJcblx0XHR9IGVsc2UgaWYgKGNvZGUgJiA0KSB7IC8vIGJvdHRvbVxyXG5cdFx0XHR4ID0gYS54ICsgZHggKiAobWluLnkgLSBhLnkpIC8gZHk7XHJcblx0XHRcdHkgPSBtaW4ueTtcclxuXHJcblx0XHR9IGVsc2UgaWYgKGNvZGUgJiAyKSB7IC8vIHJpZ2h0XHJcblx0XHRcdHggPSBtYXgueDtcclxuXHRcdFx0eSA9IGEueSArIGR5ICogKG1heC54IC0gYS54KSAvIGR4O1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoY29kZSAmIDEpIHsgLy8gbGVmdFxyXG5cdFx0XHR4ID0gbWluLng7XHJcblx0XHRcdHkgPSBhLnkgKyBkeSAqIChtaW4ueCAtIGEueCkgLyBkeDtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoeCwgeSwgcm91bmQpO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRCaXRDb2RlOiBmdW5jdGlvbiAocCwgYm91bmRzKSB7XHJcblx0XHR2YXIgY29kZSA9IDA7XHJcblxyXG5cdFx0aWYgKHAueCA8IGJvdW5kcy5taW4ueCkgeyAvLyBsZWZ0XHJcblx0XHRcdGNvZGUgfD0gMTtcclxuXHRcdH0gZWxzZSBpZiAocC54ID4gYm91bmRzLm1heC54KSB7IC8vIHJpZ2h0XHJcblx0XHRcdGNvZGUgfD0gMjtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAocC55IDwgYm91bmRzLm1pbi55KSB7IC8vIGJvdHRvbVxyXG5cdFx0XHRjb2RlIHw9IDQ7XHJcblx0XHR9IGVsc2UgaWYgKHAueSA+IGJvdW5kcy5tYXgueSkgeyAvLyB0b3BcclxuXHRcdFx0Y29kZSB8PSA4O1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBjb2RlO1xyXG5cdH0sXHJcblxyXG5cdC8vIHNxdWFyZSBkaXN0YW5jZSAodG8gYXZvaWQgdW5uZWNlc3NhcnkgTWF0aC5zcXJ0IGNhbGxzKVxyXG5cdF9zcURpc3Q6IGZ1bmN0aW9uIChwMSwgcDIpIHtcclxuXHRcdHZhciBkeCA9IHAyLnggLSBwMS54LFxyXG5cdFx0ICAgIGR5ID0gcDIueSAtIHAxLnk7XHJcblx0XHRyZXR1cm4gZHggKiBkeCArIGR5ICogZHk7XHJcblx0fSxcclxuXHJcblx0Ly8gcmV0dXJuIGNsb3Nlc3QgcG9pbnQgb24gc2VnbWVudCBvciBkaXN0YW5jZSB0byB0aGF0IHBvaW50XHJcblx0X3NxQ2xvc2VzdFBvaW50T25TZWdtZW50OiBmdW5jdGlvbiAocCwgcDEsIHAyLCBzcURpc3QpIHtcclxuXHRcdHZhciB4ID0gcDEueCxcclxuXHRcdCAgICB5ID0gcDEueSxcclxuXHRcdCAgICBkeCA9IHAyLnggLSB4LFxyXG5cdFx0ICAgIGR5ID0gcDIueSAtIHksXHJcblx0XHQgICAgZG90ID0gZHggKiBkeCArIGR5ICogZHksXHJcblx0XHQgICAgdDtcclxuXHJcblx0XHRpZiAoZG90ID4gMCkge1xyXG5cdFx0XHR0ID0gKChwLnggLSB4KSAqIGR4ICsgKHAueSAtIHkpICogZHkpIC8gZG90O1xyXG5cclxuXHRcdFx0aWYgKHQgPiAxKSB7XHJcblx0XHRcdFx0eCA9IHAyLng7XHJcblx0XHRcdFx0eSA9IHAyLnk7XHJcblx0XHRcdH0gZWxzZSBpZiAodCA+IDApIHtcclxuXHRcdFx0XHR4ICs9IGR4ICogdDtcclxuXHRcdFx0XHR5ICs9IGR5ICogdDtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdGR4ID0gcC54IC0geDtcclxuXHRcdGR5ID0gcC55IC0geTtcclxuXHJcblx0XHRyZXR1cm4gc3FEaXN0ID8gZHggKiBkeCArIGR5ICogZHkgOiBuZXcgTC5Qb2ludCh4LCB5KTtcclxuXHR9XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgUG9seWxpbmVcbiAqIEBha2EgTC5Qb2x5bGluZVxuICogQGluaGVyaXRzIFBhdGhcbiAqXG4gKiBBIGNsYXNzIGZvciBkcmF3aW5nIHBvbHlsaW5lIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBQYXRoYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiAvLyBjcmVhdGUgYSByZWQgcG9seWxpbmUgZnJvbSBhbiBhcnJheSBvZiBMYXRMbmcgcG9pbnRzXG4gKiB2YXIgbGF0bG5ncyA9IFtcbiAqIFx0WzQ1LjUxLCAtMTIyLjY4XSxcbiAqIFx0WzM3Ljc3LCAtMTIyLjQzXSxcbiAqIFx0WzM0LjA0LCAtMTE4LjJdXG4gKiBdO1xuICpcbiAqIHZhciBwb2x5bGluZSA9IEwucG9seWxpbmUobGF0bG5ncywge2NvbG9yOiAncmVkJ30pLmFkZFRvKG1hcCk7XG4gKlxuICogLy8gem9vbSB0aGUgbWFwIHRvIHRoZSBwb2x5bGluZVxuICogbWFwLmZpdEJvdW5kcyhwb2x5bGluZS5nZXRCb3VuZHMoKSk7XG4gKiBgYGBcbiAqXG4gKiBZb3UgY2FuIGFsc28gcGFzcyBhIG11bHRpLWRpbWVuc2lvbmFsIGFycmF5IHRvIHJlcHJlc2VudCBhIGBNdWx0aVBvbHlsaW5lYCBzaGFwZTpcbiAqXG4gKiBgYGBqc1xuICogLy8gY3JlYXRlIGEgcmVkIHBvbHlsaW5lIGZyb20gYW4gYXJyYXkgb2YgYXJyYXlzIG9mIExhdExuZyBwb2ludHNcbiAqIHZhciBsYXRsbmdzID0gW1xuICogXHRbWzQ1LjUxLCAtMTIyLjY4XSxcbiAqIFx0IFszNy43NywgLTEyMi40M10sXG4gKiBcdCBbMzQuMDQsIC0xMTguMl1dLFxuICogXHRbWzQwLjc4LCAtNzMuOTFdLFxuICogXHQgWzQxLjgzLCAtODcuNjJdLFxuICogXHQgWzMyLjc2LCAtOTYuNzJdXVxuICogXTtcbiAqIGBgYFxuICovXG5cbkwuUG9seWxpbmUgPSBMLlBhdGguZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFBvbHlsaW5lIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gc21vb3RoRmFjdG9yOiBOdW1iZXIgPSAxLjBcblx0XHQvLyBIb3cgbXVjaCB0byBzaW1wbGlmeSB0aGUgcG9seWxpbmUgb24gZWFjaCB6b29tIGxldmVsLiBNb3JlIG1lYW5zXG5cdFx0Ly8gYmV0dGVyIHBlcmZvcm1hbmNlIGFuZCBzbW9vdGhlciBsb29rLCBhbmQgbGVzcyBtZWFucyBtb3JlIGFjY3VyYXRlIHJlcHJlc2VudGF0aW9uLlxuXHRcdHNtb290aEZhY3RvcjogMS4wLFxuXG5cdFx0Ly8gQG9wdGlvbiBub0NsaXA6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIERpc2FibGUgcG9seWxpbmUgY2xpcHBpbmcuXG5cdFx0bm9DbGlwOiBmYWxzZVxuXHR9LFxuXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmdzLCBvcHRpb25zKSB7XG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuXHRcdHRoaXMuX3NldExhdExuZ3MobGF0bG5ncyk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRMYXRMbmdzKCk6IExhdExuZ1tdXG5cdC8vIFJldHVybnMgYW4gYXJyYXkgb2YgdGhlIHBvaW50cyBpbiB0aGUgcGF0aCwgb3IgbmVzdGVkIGFycmF5cyBvZiBwb2ludHMgaW4gY2FzZSBvZiBtdWx0aS1wb2x5bGluZS5cblx0Z2V0TGF0TG5nczogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sYXRsbmdzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0TGF0TG5ncyhsYXRsbmdzOiBMYXRMbmdbXSk6IHRoaXNcblx0Ly8gUmVwbGFjZXMgYWxsIHRoZSBwb2ludHMgaW4gdGhlIHBvbHlsaW5lIHdpdGggdGhlIGdpdmVuIGFycmF5IG9mIGdlb2dyYXBoaWNhbCBwb2ludHMuXG5cdHNldExhdExuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzKSB7XG5cdFx0dGhpcy5fc2V0TGF0TG5ncyhsYXRsbmdzKTtcblx0XHRyZXR1cm4gdGhpcy5yZWRyYXcoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGlzRW1wdHkoKTogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgUG9seWxpbmUgaGFzIG5vIExhdExuZ3MuXG5cdGlzRW1wdHk6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gIXRoaXMuX2xhdGxuZ3MubGVuZ3RoO1xuXHR9LFxuXG5cdGNsb3Nlc3RMYXllclBvaW50OiBmdW5jdGlvbiAocCkge1xuXHRcdHZhciBtaW5EaXN0YW5jZSA9IEluZmluaXR5LFxuXHRcdCAgICBtaW5Qb2ludCA9IG51bGwsXG5cdFx0ICAgIGNsb3Nlc3QgPSBMLkxpbmVVdGlsLl9zcUNsb3Nlc3RQb2ludE9uU2VnbWVudCxcblx0XHQgICAgcDEsIHAyO1xuXG5cdFx0Zm9yICh2YXIgaiA9IDAsIGpMZW4gPSB0aGlzLl9wYXJ0cy5sZW5ndGg7IGogPCBqTGVuOyBqKyspIHtcblx0XHRcdHZhciBwb2ludHMgPSB0aGlzLl9wYXJ0c1tqXTtcblxuXHRcdFx0Zm9yICh2YXIgaSA9IDEsIGxlbiA9IHBvaW50cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0XHRwMSA9IHBvaW50c1tpIC0gMV07XG5cdFx0XHRcdHAyID0gcG9pbnRzW2ldO1xuXG5cdFx0XHRcdHZhciBzcURpc3QgPSBjbG9zZXN0KHAsIHAxLCBwMiwgdHJ1ZSk7XG5cblx0XHRcdFx0aWYgKHNxRGlzdCA8IG1pbkRpc3RhbmNlKSB7XG5cdFx0XHRcdFx0bWluRGlzdGFuY2UgPSBzcURpc3Q7XG5cdFx0XHRcdFx0bWluUG9pbnQgPSBjbG9zZXN0KHAsIHAxLCBwMik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0aWYgKG1pblBvaW50KSB7XG5cdFx0XHRtaW5Qb2ludC5kaXN0YW5jZSA9IE1hdGguc3FydChtaW5EaXN0YW5jZSk7XG5cdFx0fVxuXHRcdHJldHVybiBtaW5Qb2ludDtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldENlbnRlcigpOiBMYXRMbmdcblx0Ly8gUmV0dXJucyB0aGUgY2VudGVyIChbY2VudHJvaWRdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ2VudHJvaWQpKSBvZiB0aGUgcG9seWxpbmUuXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHRocm93cyBlcnJvciB3aGVuIG5vdCB5ZXQgYWRkZWQgdG8gbWFwIGFzIHRoaXMgY2VudGVyIGNhbGN1bGF0aW9uIHJlcXVpcmVzIHByb2plY3RlZCBjb29yZGluYXRlc1xuXHRcdGlmICghdGhpcy5fbWFwKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ011c3QgYWRkIGxheWVyIHRvIG1hcCBiZWZvcmUgdXNpbmcgZ2V0Q2VudGVyKCknKTtcblx0XHR9XG5cblx0XHR2YXIgaSwgaGFsZkRpc3QsIHNlZ0Rpc3QsIGRpc3QsIHAxLCBwMiwgcmF0aW8sXG5cdFx0ICAgIHBvaW50cyA9IHRoaXMuX3JpbmdzWzBdLFxuXHRcdCAgICBsZW4gPSBwb2ludHMubGVuZ3RoO1xuXG5cdFx0aWYgKCFsZW4pIHsgcmV0dXJuIG51bGw7IH1cblxuXHRcdC8vIHBvbHlsaW5lIGNlbnRyb2lkIGFsZ29yaXRobTsgb25seSB1c2VzIHRoZSBmaXJzdCByaW5nIGlmIHRoZXJlIGFyZSBtdWx0aXBsZVxuXG5cdFx0Zm9yIChpID0gMCwgaGFsZkRpc3QgPSAwOyBpIDwgbGVuIC0gMTsgaSsrKSB7XG5cdFx0XHRoYWxmRGlzdCArPSBwb2ludHNbaV0uZGlzdGFuY2VUbyhwb2ludHNbaSArIDFdKSAvIDI7XG5cdFx0fVxuXG5cdFx0Ly8gVGhlIGxpbmUgaXMgc28gc21hbGwgaW4gdGhlIGN1cnJlbnQgdmlldyB0aGF0IGFsbCBwb2ludHMgYXJlIG9uIHRoZSBzYW1lIHBpeGVsLlxuXHRcdGlmIChoYWxmRGlzdCA9PT0gMCkge1xuXHRcdFx0cmV0dXJuIHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcocG9pbnRzWzBdKTtcblx0XHR9XG5cblx0XHRmb3IgKGkgPSAwLCBkaXN0ID0gMDsgaSA8IGxlbiAtIDE7IGkrKykge1xuXHRcdFx0cDEgPSBwb2ludHNbaV07XG5cdFx0XHRwMiA9IHBvaW50c1tpICsgMV07XG5cdFx0XHRzZWdEaXN0ID0gcDEuZGlzdGFuY2VUbyhwMik7XG5cdFx0XHRkaXN0ICs9IHNlZ0Rpc3Q7XG5cblx0XHRcdGlmIChkaXN0ID4gaGFsZkRpc3QpIHtcblx0XHRcdFx0cmF0aW8gPSAoZGlzdCAtIGhhbGZEaXN0KSAvIHNlZ0Rpc3Q7XG5cdFx0XHRcdHJldHVybiB0aGlzLl9tYXAubGF5ZXJQb2ludFRvTGF0TG5nKFtcblx0XHRcdFx0XHRwMi54IC0gcmF0aW8gKiAocDIueCAtIHAxLngpLFxuXHRcdFx0XHRcdHAyLnkgLSByYXRpbyAqIChwMi55IC0gcDEueSlcblx0XHRcdFx0XSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xuXHQvLyBSZXR1cm5zIHRoZSBgTGF0TG5nQm91bmRzYCBvZiB0aGUgcGF0aC5cblx0Z2V0Qm91bmRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2JvdW5kcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGFkZExhdExuZyhsYXRsbmc6IExhdExuZywgbGF0bG5ncz8gTGF0TG5nW10pOiB0aGlzXG5cdC8vIEFkZHMgYSBnaXZlbiBwb2ludCB0byB0aGUgcG9seWxpbmUuIEJ5IGRlZmF1bHQsIGFkZHMgdG8gdGhlIGZpcnN0IHJpbmcgb2Zcblx0Ly8gdGhlIHBvbHlsaW5lIGluIGNhc2Ugb2YgYSBtdWx0aS1wb2x5bGluZSwgYnV0IGNhbiBiZSBvdmVycmlkZGVuIGJ5IHBhc3Npbmdcblx0Ly8gYSBzcGVjaWZpYyByaW5nIGFzIGEgTGF0TG5nIGFycmF5ICh0aGF0IHlvdSBjYW4gZWFybGllciBhY2Nlc3Mgd2l0aCBbYGdldExhdExuZ3NgXSgjcG9seWxpbmUtZ2V0bGF0bG5ncykpLlxuXHRhZGRMYXRMbmc6IGZ1bmN0aW9uIChsYXRsbmcsIGxhdGxuZ3MpIHtcblx0XHRsYXRsbmdzID0gbGF0bG5ncyB8fCB0aGlzLl9kZWZhdWx0U2hhcGUoKTtcblx0XHRsYXRsbmcgPSBMLmxhdExuZyhsYXRsbmcpO1xuXHRcdGxhdGxuZ3MucHVzaChsYXRsbmcpO1xuXHRcdHRoaXMuX2JvdW5kcy5leHRlbmQobGF0bG5nKTtcblx0XHRyZXR1cm4gdGhpcy5yZWRyYXcoKTtcblx0fSxcblxuXHRfc2V0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHR0aGlzLl9ib3VuZHMgPSBuZXcgTC5MYXRMbmdCb3VuZHMoKTtcblx0XHR0aGlzLl9sYXRsbmdzID0gdGhpcy5fY29udmVydExhdExuZ3MobGF0bG5ncyk7XG5cdH0sXG5cblx0X2RlZmF1bHRTaGFwZTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiBMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3MpID8gdGhpcy5fbGF0bG5ncyA6IHRoaXMuX2xhdGxuZ3NbMF07XG5cdH0sXG5cblx0Ly8gcmVjdXJzaXZlbHkgY29udmVydCBsYXRsbmdzIGlucHV0IGludG8gYWN0dWFsIExhdExuZyBpbnN0YW5jZXM7IGNhbGN1bGF0ZSBib3VuZHMgYWxvbmcgdGhlIHdheVxuXHRfY29udmVydExhdExuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzKSB7XG5cdFx0dmFyIHJlc3VsdCA9IFtdLFxuXHRcdCAgICBmbGF0ID0gTC5Qb2x5bGluZS5fZmxhdChsYXRsbmdzKTtcblxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsYXRsbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRpZiAoZmxhdCkge1xuXHRcdFx0XHRyZXN1bHRbaV0gPSBMLmxhdExuZyhsYXRsbmdzW2ldKTtcblx0XHRcdFx0dGhpcy5fYm91bmRzLmV4dGVuZChyZXN1bHRbaV0pO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0cmVzdWx0W2ldID0gdGhpcy5fY29udmVydExhdExuZ3MobGF0bG5nc1tpXSk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHJlc3VsdDtcblx0fSxcblxuXHRfcHJvamVjdDogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBweEJvdW5kcyA9IG5ldyBMLkJvdW5kcygpO1xuXHRcdHRoaXMuX3JpbmdzID0gW107XG5cdFx0dGhpcy5fcHJvamVjdExhdGxuZ3ModGhpcy5fbGF0bG5ncywgdGhpcy5fcmluZ3MsIHB4Qm91bmRzKTtcblxuXHRcdHZhciB3ID0gdGhpcy5fY2xpY2tUb2xlcmFuY2UoKSxcblx0XHQgICAgcCA9IG5ldyBMLlBvaW50KHcsIHcpO1xuXG5cdFx0aWYgKHRoaXMuX2JvdW5kcy5pc1ZhbGlkKCkgJiYgcHhCb3VuZHMuaXNWYWxpZCgpKSB7XG5cdFx0XHRweEJvdW5kcy5taW4uX3N1YnRyYWN0KHApO1xuXHRcdFx0cHhCb3VuZHMubWF4Ll9hZGQocCk7XG5cdFx0XHR0aGlzLl9weEJvdW5kcyA9IHB4Qm91bmRzO1xuXHRcdH1cblx0fSxcblxuXHQvLyByZWN1cnNpdmVseSB0dXJucyBsYXRsbmdzIGludG8gYSBzZXQgb2YgcmluZ3Mgd2l0aCBwcm9qZWN0ZWQgY29vcmRpbmF0ZXNcblx0X3Byb2plY3RMYXRsbmdzOiBmdW5jdGlvbiAobGF0bG5ncywgcmVzdWx0LCBwcm9qZWN0ZWRCb3VuZHMpIHtcblx0XHR2YXIgZmxhdCA9IGxhdGxuZ3NbMF0gaW5zdGFuY2VvZiBMLkxhdExuZyxcblx0XHQgICAgbGVuID0gbGF0bG5ncy5sZW5ndGgsXG5cdFx0ICAgIGksIHJpbmc7XG5cblx0XHRpZiAoZmxhdCkge1xuXHRcdFx0cmluZyA9IFtdO1xuXHRcdFx0Zm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRcdHJpbmdbaV0gPSB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KGxhdGxuZ3NbaV0pO1xuXHRcdFx0XHRwcm9qZWN0ZWRCb3VuZHMuZXh0ZW5kKHJpbmdbaV0pO1xuXHRcdFx0fVxuXHRcdFx0cmVzdWx0LnB1c2gocmluZyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGZvciAoaSA9IDA7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0XHR0aGlzLl9wcm9qZWN0TGF0bG5ncyhsYXRsbmdzW2ldLCByZXN1bHQsIHByb2plY3RlZEJvdW5kcyk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdC8vIGNsaXAgcG9seWxpbmUgYnkgcmVuZGVyZXIgYm91bmRzIHNvIHRoYXQgd2UgaGF2ZSBsZXNzIHRvIHJlbmRlciBmb3IgcGVyZm9ybWFuY2Vcblx0X2NsaXBQb2ludHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgYm91bmRzID0gdGhpcy5fcmVuZGVyZXIuX2JvdW5kcztcblxuXHRcdHRoaXMuX3BhcnRzID0gW107XG5cdFx0aWYgKCF0aGlzLl9weEJvdW5kcyB8fCAhdGhpcy5fcHhCb3VuZHMuaW50ZXJzZWN0cyhib3VuZHMpKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5ub0NsaXApIHtcblx0XHRcdHRoaXMuX3BhcnRzID0gdGhpcy5fcmluZ3M7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dmFyIHBhcnRzID0gdGhpcy5fcGFydHMsXG5cdFx0ICAgIGksIGosIGssIGxlbiwgbGVuMiwgc2VnbWVudCwgcG9pbnRzO1xuXG5cdFx0Zm9yIChpID0gMCwgayA9IDAsIGxlbiA9IHRoaXMuX3JpbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRwb2ludHMgPSB0aGlzLl9yaW5nc1tpXTtcblxuXHRcdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBvaW50cy5sZW5ndGg7IGogPCBsZW4yIC0gMTsgaisrKSB7XG5cdFx0XHRcdHNlZ21lbnQgPSBMLkxpbmVVdGlsLmNsaXBTZWdtZW50KHBvaW50c1tqXSwgcG9pbnRzW2ogKyAxXSwgYm91bmRzLCBqLCB0cnVlKTtcblxuXHRcdFx0XHRpZiAoIXNlZ21lbnQpIHsgY29udGludWU7IH1cblxuXHRcdFx0XHRwYXJ0c1trXSA9IHBhcnRzW2tdIHx8IFtdO1xuXHRcdFx0XHRwYXJ0c1trXS5wdXNoKHNlZ21lbnRbMF0pO1xuXG5cdFx0XHRcdC8vIGlmIHNlZ21lbnQgZ29lcyBvdXQgb2Ygc2NyZWVuLCBvciBpdCdzIHRoZSBsYXN0IG9uZSwgaXQncyB0aGUgZW5kIG9mIHRoZSBsaW5lIHBhcnRcblx0XHRcdFx0aWYgKChzZWdtZW50WzFdICE9PSBwb2ludHNbaiArIDFdKSB8fCAoaiA9PT0gbGVuMiAtIDIpKSB7XG5cdFx0XHRcdFx0cGFydHNba10ucHVzaChzZWdtZW50WzFdKTtcblx0XHRcdFx0XHRrKys7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0Ly8gc2ltcGxpZnkgZWFjaCBjbGlwcGVkIHBhcnQgb2YgdGhlIHBvbHlsaW5lIGZvciBwZXJmb3JtYW5jZVxuXHRfc2ltcGxpZnlQb2ludHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcGFydHMgPSB0aGlzLl9wYXJ0cyxcblx0XHQgICAgdG9sZXJhbmNlID0gdGhpcy5vcHRpb25zLnNtb290aEZhY3RvcjtcblxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBwYXJ0cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0cGFydHNbaV0gPSBMLkxpbmVVdGlsLnNpbXBsaWZ5KHBhcnRzW2ldLCB0b2xlcmFuY2UpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9jbGlwUG9pbnRzKCk7XG5cdFx0dGhpcy5fc2ltcGxpZnlQb2ludHMoKTtcblx0XHR0aGlzLl91cGRhdGVQYXRoKCk7XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlUG9seSh0aGlzKTtcblx0fVxufSk7XG5cbi8vIEBmYWN0b3J5IEwucG9seWxpbmUobGF0bG5nczogTGF0TG5nW10sIG9wdGlvbnM/OiBQb2x5bGluZSBvcHRpb25zKVxuLy8gSW5zdGFudGlhdGVzIGEgcG9seWxpbmUgb2JqZWN0IGdpdmVuIGFuIGFycmF5IG9mIGdlb2dyYXBoaWNhbCBwb2ludHMgYW5kXG4vLyBvcHRpb25hbGx5IGFuIG9wdGlvbnMgb2JqZWN0LiBZb3UgY2FuIGNyZWF0ZSBhIGBQb2x5bGluZWAgb2JqZWN0IHdpdGhcbi8vIG11bHRpcGxlIHNlcGFyYXRlIGxpbmVzIChgTXVsdGlQb2x5bGluZWApIGJ5IHBhc3NpbmcgYW4gYXJyYXkgb2YgYXJyYXlzXG4vLyBvZiBnZW9ncmFwaGljIHBvaW50cy5cbkwucG9seWxpbmUgPSBmdW5jdGlvbiAobGF0bG5ncywgb3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuUG9seWxpbmUobGF0bG5ncywgb3B0aW9ucyk7XG59O1xuXG5MLlBvbHlsaW5lLl9mbGF0ID0gZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0Ly8gdHJ1ZSBpZiBpdCdzIGEgZmxhdCBhcnJheSBvZiBsYXRsbmdzOyBmYWxzZSBpZiBuZXN0ZWRcblx0cmV0dXJuICFMLlV0aWwuaXNBcnJheShsYXRsbmdzWzBdKSB8fCAodHlwZW9mIGxhdGxuZ3NbMF1bMF0gIT09ICdvYmplY3QnICYmIHR5cGVvZiBsYXRsbmdzWzBdWzBdICE9PSAndW5kZWZpbmVkJyk7XG59O1xuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBQb2x5VXRpbFxyXG4gKiBWYXJpb3VzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBwb2x5Z29uIGdlb21ldHJpZXMuXHJcbiAqL1xyXG5cclxuTC5Qb2x5VXRpbCA9IHt9O1xyXG5cclxuLyogQGZ1bmN0aW9uIGNsaXBQb2x5Z29uKHBvaW50czogUG9pbnRbXSwgYm91bmRzOiBCb3VuZHMsIHJvdW5kPzogQm9vbGVhbik6IFBvaW50W11cclxuICogQ2xpcHMgdGhlIHBvbHlnb24gZ2VvbWV0cnkgZGVmaW5lZCBieSB0aGUgZ2l2ZW4gYHBvaW50c2AgYnkgdGhlIGdpdmVuIGJvdW5kcyAodXNpbmcgdGhlIFtTdXRoZXJsYW5kLUhvZGdlbWFuIGFsZ29yaXRobV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU3V0aGVybGFuZCVFMiU4MCU5M0hvZGdtYW5fYWxnb3JpdGhtKSkuXHJcbiAqIFVzZWQgYnkgTGVhZmxldCB0byBvbmx5IHNob3cgcG9seWdvbiBwb2ludHMgdGhhdCBhcmUgb24gdGhlIHNjcmVlbiBvciBuZWFyLCBpbmNyZWFzaW5nXHJcbiAqIHBlcmZvcm1hbmNlLiBOb3RlIHRoYXQgcG9seWdvbiBwb2ludHMgbmVlZHMgZGlmZmVyZW50IGFsZ29yaXRobSBmb3IgY2xpcHBpbmdcclxuICogdGhhbiBwb2x5bGluZSwgc28gdGhlcmUncyBhIHNlcGVyYXRlIG1ldGhvZCBmb3IgaXQuXHJcbiAqL1xyXG5MLlBvbHlVdGlsLmNsaXBQb2x5Z29uID0gZnVuY3Rpb24gKHBvaW50cywgYm91bmRzLCByb3VuZCkge1xyXG5cdHZhciBjbGlwcGVkUG9pbnRzLFxyXG5cdCAgICBlZGdlcyA9IFsxLCA0LCAyLCA4XSxcclxuXHQgICAgaSwgaiwgayxcclxuXHQgICAgYSwgYixcclxuXHQgICAgbGVuLCBlZGdlLCBwLFxyXG5cdCAgICBsdSA9IEwuTGluZVV0aWw7XHJcblxyXG5cdGZvciAoaSA9IDAsIGxlbiA9IHBvaW50cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0cG9pbnRzW2ldLl9jb2RlID0gbHUuX2dldEJpdENvZGUocG9pbnRzW2ldLCBib3VuZHMpO1xyXG5cdH1cclxuXHJcblx0Ly8gZm9yIGVhY2ggZWRnZSAobGVmdCwgYm90dG9tLCByaWdodCwgdG9wKVxyXG5cdGZvciAoayA9IDA7IGsgPCA0OyBrKyspIHtcclxuXHRcdGVkZ2UgPSBlZGdlc1trXTtcclxuXHRcdGNsaXBwZWRQb2ludHMgPSBbXTtcclxuXHJcblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBwb2ludHMubGVuZ3RoLCBqID0gbGVuIC0gMTsgaSA8IGxlbjsgaiA9IGkrKykge1xyXG5cdFx0XHRhID0gcG9pbnRzW2ldO1xyXG5cdFx0XHRiID0gcG9pbnRzW2pdO1xyXG5cclxuXHRcdFx0Ly8gaWYgYSBpcyBpbnNpZGUgdGhlIGNsaXAgd2luZG93XHJcblx0XHRcdGlmICghKGEuX2NvZGUgJiBlZGdlKSkge1xyXG5cdFx0XHRcdC8vIGlmIGIgaXMgb3V0c2lkZSB0aGUgY2xpcCB3aW5kb3cgKGEtPmIgZ29lcyBvdXQgb2Ygc2NyZWVuKVxyXG5cdFx0XHRcdGlmIChiLl9jb2RlICYgZWRnZSkge1xyXG5cdFx0XHRcdFx0cCA9IGx1Ll9nZXRFZGdlSW50ZXJzZWN0aW9uKGIsIGEsIGVkZ2UsIGJvdW5kcywgcm91bmQpO1xyXG5cdFx0XHRcdFx0cC5fY29kZSA9IGx1Ll9nZXRCaXRDb2RlKHAsIGJvdW5kcyk7XHJcblx0XHRcdFx0XHRjbGlwcGVkUG9pbnRzLnB1c2gocCk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdGNsaXBwZWRQb2ludHMucHVzaChhKTtcclxuXHJcblx0XHRcdC8vIGVsc2UgaWYgYiBpcyBpbnNpZGUgdGhlIGNsaXAgd2luZG93IChhLT5iIGVudGVycyB0aGUgc2NyZWVuKVxyXG5cdFx0XHR9IGVsc2UgaWYgKCEoYi5fY29kZSAmIGVkZ2UpKSB7XHJcblx0XHRcdFx0cCA9IGx1Ll9nZXRFZGdlSW50ZXJzZWN0aW9uKGIsIGEsIGVkZ2UsIGJvdW5kcywgcm91bmQpO1xyXG5cdFx0XHRcdHAuX2NvZGUgPSBsdS5fZ2V0Qml0Q29kZShwLCBib3VuZHMpO1xyXG5cdFx0XHRcdGNsaXBwZWRQb2ludHMucHVzaChwKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cG9pbnRzID0gY2xpcHBlZFBvaW50cztcclxuXHR9XHJcblxyXG5cdHJldHVybiBwb2ludHM7XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgUG9seWdvblxuICogQGFrYSBMLlBvbHlnb25cbiAqIEBpbmhlcml0cyBQb2x5bGluZVxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgcG9seWdvbiBvdmVybGF5cyBvbiBhIG1hcC4gRXh0ZW5kcyBgUG9seWxpbmVgLlxuICpcbiAqIE5vdGUgdGhhdCBwb2ludHMgeW91IHBhc3Mgd2hlbiBjcmVhdGluZyBhIHBvbHlnb24gc2hvdWxkbid0IGhhdmUgYW4gYWRkaXRpb25hbCBsYXN0IHBvaW50IGVxdWFsIHRvIHRoZSBmaXJzdCBvbmUg4oCUIGl0J3MgYmV0dGVyIHRvIGZpbHRlciBvdXQgc3VjaCBwb2ludHMuXG4gKlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIC8vIGNyZWF0ZSBhIHJlZCBwb2x5Z29uIGZyb20gYW4gYXJyYXkgb2YgTGF0TG5nIHBvaW50c1xuICogdmFyIGxhdGxuZ3MgPSBbWzM3LCAtMTA5LjA1XSxbNDEsIC0xMDkuMDNdLFs0MSwgLTEwMi4wNV0sWzM3LCAtMTAyLjA0XV07XG4gKlxuICogdmFyIHBvbHlnb24gPSBMLnBvbHlnb24obGF0bG5ncywge2NvbG9yOiAncmVkJ30pLmFkZFRvKG1hcCk7XG4gKlxuICogLy8gem9vbSB0aGUgbWFwIHRvIHRoZSBwb2x5Z29uXG4gKiBtYXAuZml0Qm91bmRzKHBvbHlnb24uZ2V0Qm91bmRzKCkpO1xuICogYGBgXG4gKlxuICogWW91IGNhbiBhbHNvIHBhc3MgYW4gYXJyYXkgb2YgYXJyYXlzIG9mIGxhdGxuZ3MsIHdpdGggdGhlIGZpcnN0IGFycmF5IHJlcHJlc2VudGluZyB0aGUgb3V0ZXIgc2hhcGUgYW5kIHRoZSBvdGhlciBhcnJheXMgcmVwcmVzZW50aW5nIGhvbGVzIGluIHRoZSBvdXRlciBzaGFwZTpcbiAqXG4gKiBgYGBqc1xuICogdmFyIGxhdGxuZ3MgPSBbXG4gKiAgIFtbMzcsIC0xMDkuMDVdLFs0MSwgLTEwOS4wM10sWzQxLCAtMTAyLjA1XSxbMzcsIC0xMDIuMDRdXSwgLy8gb3V0ZXIgcmluZ1xuICogICBbWzM3LjI5LCAtMTA4LjU4XSxbNDAuNzEsIC0xMDguNThdLFs0MC43MSwgLTEwMi41MF0sWzM3LjI5LCAtMTAyLjUwXV0gLy8gaG9sZVxuICogXTtcbiAqIGBgYFxuICpcbiAqIEFkZGl0aW9uYWxseSwgeW91IGNhbiBwYXNzIGEgbXVsdGktZGltZW5zaW9uYWwgYXJyYXkgdG8gcmVwcmVzZW50IGEgTXVsdGlQb2x5Z29uIHNoYXBlLlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbGF0bG5ncyA9IFtcbiAqICAgWyAvLyBmaXJzdCBwb2x5Z29uXG4gKiAgICAgW1szNywgLTEwOS4wNV0sWzQxLCAtMTA5LjAzXSxbNDEsIC0xMDIuMDVdLFszNywgLTEwMi4wNF1dLCAvLyBvdXRlciByaW5nXG4gKiAgICAgW1szNy4yOSwgLTEwOC41OF0sWzQwLjcxLCAtMTA4LjU4XSxbNDAuNzEsIC0xMDIuNTBdLFszNy4yOSwgLTEwMi41MF1dIC8vIGhvbGVcbiAqICAgXSxcbiAqICAgWyAvLyBzZWNvbmQgcG9seWdvblxuICogICAgIFtbNDEsIC0xMTEuMDNdLFs0NSwgLTExMS4wNF0sWzQ1LCAtMTA0LjA1XSxbNDEsIC0xMDQuMDVdXVxuICogICBdXG4gKiBdO1xuICogYGBgXG4gKi9cblxuTC5Qb2x5Z29uID0gTC5Qb2x5bGluZS5leHRlbmQoe1xuXG5cdG9wdGlvbnM6IHtcblx0XHRmaWxsOiB0cnVlXG5cdH0sXG5cblx0aXNFbXB0eTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiAhdGhpcy5fbGF0bG5ncy5sZW5ndGggfHwgIXRoaXMuX2xhdGxuZ3NbMF0ubGVuZ3RoO1xuXHR9LFxuXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHRocm93cyBlcnJvciB3aGVuIG5vdCB5ZXQgYWRkZWQgdG8gbWFwIGFzIHRoaXMgY2VudGVyIGNhbGN1bGF0aW9uIHJlcXVpcmVzIHByb2plY3RlZCBjb29yZGluYXRlc1xuXHRcdGlmICghdGhpcy5fbWFwKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ011c3QgYWRkIGxheWVyIHRvIG1hcCBiZWZvcmUgdXNpbmcgZ2V0Q2VudGVyKCknKTtcblx0XHR9XG5cblx0XHR2YXIgaSwgaiwgcDEsIHAyLCBmLCBhcmVhLCB4LCB5LCBjZW50ZXIsXG5cdFx0ICAgIHBvaW50cyA9IHRoaXMuX3JpbmdzWzBdLFxuXHRcdCAgICBsZW4gPSBwb2ludHMubGVuZ3RoO1xuXG5cdFx0aWYgKCFsZW4pIHsgcmV0dXJuIG51bGw7IH1cblxuXHRcdC8vIHBvbHlnb24gY2VudHJvaWQgYWxnb3JpdGhtOyBvbmx5IHVzZXMgdGhlIGZpcnN0IHJpbmcgaWYgdGhlcmUgYXJlIG11bHRpcGxlXG5cblx0XHRhcmVhID0geCA9IHkgPSAwO1xuXG5cdFx0Zm9yIChpID0gMCwgaiA9IGxlbiAtIDE7IGkgPCBsZW47IGogPSBpKyspIHtcblx0XHRcdHAxID0gcG9pbnRzW2ldO1xuXHRcdFx0cDIgPSBwb2ludHNbal07XG5cblx0XHRcdGYgPSBwMS55ICogcDIueCAtIHAyLnkgKiBwMS54O1xuXHRcdFx0eCArPSAocDEueCArIHAyLngpICogZjtcblx0XHRcdHkgKz0gKHAxLnkgKyBwMi55KSAqIGY7XG5cdFx0XHRhcmVhICs9IGYgKiAzO1xuXHRcdH1cblxuXHRcdGlmIChhcmVhID09PSAwKSB7XG5cdFx0XHQvLyBQb2x5Z29uIGlzIHNvIHNtYWxsIHRoYXQgYWxsIHBvaW50cyBhcmUgb24gc2FtZSBwaXhlbC5cblx0XHRcdGNlbnRlciA9IHBvaW50c1swXTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Y2VudGVyID0gW3ggLyBhcmVhLCB5IC8gYXJlYV07XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzLl9tYXAubGF5ZXJQb2ludFRvTGF0TG5nKGNlbnRlcik7XG5cdH0sXG5cblx0X2NvbnZlcnRMYXRMbmdzOiBmdW5jdGlvbiAobGF0bG5ncykge1xuXHRcdHZhciByZXN1bHQgPSBMLlBvbHlsaW5lLnByb3RvdHlwZS5fY29udmVydExhdExuZ3MuY2FsbCh0aGlzLCBsYXRsbmdzKSxcblx0XHQgICAgbGVuID0gcmVzdWx0Lmxlbmd0aDtcblxuXHRcdC8vIHJlbW92ZSBsYXN0IHBvaW50IGlmIGl0IGVxdWFscyBmaXJzdCBvbmVcblx0XHRpZiAobGVuID49IDIgJiYgcmVzdWx0WzBdIGluc3RhbmNlb2YgTC5MYXRMbmcgJiYgcmVzdWx0WzBdLmVxdWFscyhyZXN1bHRbbGVuIC0gMV0pKSB7XG5cdFx0XHRyZXN1bHQucG9wKCk7XG5cdFx0fVxuXHRcdHJldHVybiByZXN1bHQ7XG5cdH0sXG5cblx0X3NldExhdExuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzKSB7XG5cdFx0TC5Qb2x5bGluZS5wcm90b3R5cGUuX3NldExhdExuZ3MuY2FsbCh0aGlzLCBsYXRsbmdzKTtcblx0XHRpZiAoTC5Qb2x5bGluZS5fZmxhdCh0aGlzLl9sYXRsbmdzKSkge1xuXHRcdFx0dGhpcy5fbGF0bG5ncyA9IFt0aGlzLl9sYXRsbmdzXTtcblx0XHR9XG5cdH0sXG5cblx0X2RlZmF1bHRTaGFwZTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiBMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3NbMF0pID8gdGhpcy5fbGF0bG5nc1swXSA6IHRoaXMuX2xhdGxuZ3NbMF1bMF07XG5cdH0sXG5cblx0X2NsaXBQb2ludHM6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBwb2x5Z29ucyBuZWVkIGEgZGlmZmVyZW50IGNsaXBwaW5nIGFsZ29yaXRobSBzbyB3ZSByZWRlZmluZSB0aGF0XG5cblx0XHR2YXIgYm91bmRzID0gdGhpcy5fcmVuZGVyZXIuX2JvdW5kcyxcblx0XHQgICAgdyA9IHRoaXMub3B0aW9ucy53ZWlnaHQsXG5cdFx0ICAgIHAgPSBuZXcgTC5Qb2ludCh3LCB3KTtcblxuXHRcdC8vIGluY3JlYXNlIGNsaXAgcGFkZGluZyBieSBzdHJva2Ugd2lkdGggdG8gYXZvaWQgc3Ryb2tlIG9uIGNsaXAgZWRnZXNcblx0XHRib3VuZHMgPSBuZXcgTC5Cb3VuZHMoYm91bmRzLm1pbi5zdWJ0cmFjdChwKSwgYm91bmRzLm1heC5hZGQocCkpO1xuXG5cdFx0dGhpcy5fcGFydHMgPSBbXTtcblx0XHRpZiAoIXRoaXMuX3B4Qm91bmRzIHx8ICF0aGlzLl9weEJvdW5kcy5pbnRlcnNlY3RzKGJvdW5kcykpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5vcHRpb25zLm5vQ2xpcCkge1xuXHRcdFx0dGhpcy5fcGFydHMgPSB0aGlzLl9yaW5ncztcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gdGhpcy5fcmluZ3MubGVuZ3RoLCBjbGlwcGVkOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGNsaXBwZWQgPSBMLlBvbHlVdGlsLmNsaXBQb2x5Z29uKHRoaXMuX3JpbmdzW2ldLCBib3VuZHMsIHRydWUpO1xuXHRcdFx0aWYgKGNsaXBwZWQubGVuZ3RoKSB7XG5cdFx0XHRcdHRoaXMuX3BhcnRzLnB1c2goY2xpcHBlZCk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVQYXRoOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcmVuZGVyZXIuX3VwZGF0ZVBvbHkodGhpcywgdHJ1ZSk7XG5cdH1cbn0pO1xuXG5cbi8vIEBmYWN0b3J5IEwucG9seWdvbihsYXRsbmdzOiBMYXRMbmdbXSwgb3B0aW9ucz86IFBvbHlsaW5lIG9wdGlvbnMpXG5MLnBvbHlnb24gPSBmdW5jdGlvbiAobGF0bG5ncywgb3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuUG9seWdvbihsYXRsbmdzLCBvcHRpb25zKTtcbn07XG5cblxuXG4vKlxuICogTC5SZWN0YW5nbGUgZXh0ZW5kcyBQb2x5Z29uIGFuZCBjcmVhdGVzIGEgcmVjdGFuZ2xlIHdoZW4gcGFzc2VkIGEgTGF0TG5nQm91bmRzIG9iamVjdC5cbiAqL1xuXG4vKlxuICogQGNsYXNzIFJlY3RhbmdsZVxuICogQGFrYSBMLlJldGFuZ2xlXG4gKiBAaW5oZXJpdHMgUG9seWdvblxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgcmVjdGFuZ2xlIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBQb2x5Z29uYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiAvLyBkZWZpbmUgcmVjdGFuZ2xlIGdlb2dyYXBoaWNhbCBib3VuZHNcbiAqIHZhciBib3VuZHMgPSBbWzU0LjU1OTMyMiwgLTUuNzY3ODIyXSwgWzU2LjEyMTA2MDQsIC0zLjAyMTI0MF1dO1xuICpcbiAqIC8vIGNyZWF0ZSBhbiBvcmFuZ2UgcmVjdGFuZ2xlXG4gKiBMLnJlY3RhbmdsZShib3VuZHMsIHtjb2xvcjogXCIjZmY3ODAwXCIsIHdlaWdodDogMX0pLmFkZFRvKG1hcCk7XG4gKlxuICogLy8gem9vbSB0aGUgbWFwIHRvIHRoZSByZWN0YW5nbGUgYm91bmRzXG4gKiBtYXAuZml0Qm91bmRzKGJvdW5kcyk7XG4gKiBgYGBcbiAqXG4gKi9cblxuXG5MLlJlY3RhbmdsZSA9IEwuUG9seWdvbi5leHRlbmQoe1xuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzLCBvcHRpb25zKSB7XG5cdFx0TC5Qb2x5Z29uLnByb3RvdHlwZS5pbml0aWFsaXplLmNhbGwodGhpcywgdGhpcy5fYm91bmRzVG9MYXRMbmdzKGxhdExuZ0JvdW5kcyksIG9wdGlvbnMpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0Qm91bmRzKGxhdExuZ0JvdW5kczogTGF0TG5nQm91bmRzKTogdGhpc1xuXHQvLyBSZWRyYXdzIHRoZSByZWN0YW5nbGUgd2l0aCB0aGUgcGFzc2VkIGJvdW5kcy5cblx0c2V0Qm91bmRzOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzKSB7XG5cdFx0cmV0dXJuIHRoaXMuc2V0TGF0TG5ncyh0aGlzLl9ib3VuZHNUb0xhdExuZ3MobGF0TG5nQm91bmRzKSk7XG5cdH0sXG5cblx0X2JvdW5kc1RvTGF0TG5nczogZnVuY3Rpb24gKGxhdExuZ0JvdW5kcykge1xuXHRcdGxhdExuZ0JvdW5kcyA9IEwubGF0TG5nQm91bmRzKGxhdExuZ0JvdW5kcyk7XG5cdFx0cmV0dXJuIFtcblx0XHRcdGxhdExuZ0JvdW5kcy5nZXRTb3V0aFdlc3QoKSxcblx0XHRcdGxhdExuZ0JvdW5kcy5nZXROb3J0aFdlc3QoKSxcblx0XHRcdGxhdExuZ0JvdW5kcy5nZXROb3J0aEVhc3QoKSxcblx0XHRcdGxhdExuZ0JvdW5kcy5nZXRTb3V0aEVhc3QoKVxuXHRcdF07XG5cdH1cbn0pO1xuXG5cbi8vIEBmYWN0b3J5IEwucmVjdGFuZ2xlKGxhdExuZ0JvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogUG9seWxpbmUgb3B0aW9ucylcbkwucmVjdGFuZ2xlID0gZnVuY3Rpb24gKGxhdExuZ0JvdW5kcywgb3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuUmVjdGFuZ2xlKGxhdExuZ0JvdW5kcywgb3B0aW9ucyk7XG59O1xuXG5cblxuLypcbiAqIEBjbGFzcyBDaXJjbGVNYXJrZXJcbiAqIEBha2EgTC5DaXJjbGVNYXJrZXJcbiAqIEBpbmhlcml0cyBQYXRoXG4gKlxuICogQSBjaXJjbGUgb2YgYSBmaXhlZCBzaXplIHdpdGggcmFkaXVzIHNwZWNpZmllZCBpbiBwaXhlbHMuIEV4dGVuZHMgYFBhdGhgLlxuICovXG5cbkwuQ2lyY2xlTWFya2VyID0gTC5QYXRoLmV4dGVuZCh7XG5cblx0Ly8gQHNlY3Rpb25cblx0Ly8gQGFrYSBDaXJjbGVNYXJrZXIgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0ZmlsbDogdHJ1ZSxcblxuXHRcdC8vIEBvcHRpb24gcmFkaXVzOiBOdW1iZXIgPSAxMFxuXHRcdC8vIFJhZGl1cyBvZiB0aGUgY2lyY2xlIG1hcmtlciwgaW4gcGl4ZWxzXG5cdFx0cmFkaXVzOiAxMFxuXHR9LFxuXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcblx0XHR0aGlzLl9yYWRpdXMgPSB0aGlzLm9wdGlvbnMucmFkaXVzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0TGF0TG5nKGxhdExuZzogTGF0TG5nKTogdGhpc1xuXHQvLyBTZXRzIHRoZSBwb3NpdGlvbiBvZiBhIGNpcmNsZSBtYXJrZXIgdG8gYSBuZXcgbG9jYXRpb24uXG5cdHNldExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xuXHRcdHRoaXMuX2xhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XG5cdFx0dGhpcy5yZWRyYXcoKTtcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlJywge2xhdGxuZzogdGhpcy5fbGF0bG5nfSk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRMYXRMbmcoKTogTGF0TG5nXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgZ2VvZ3JhcGhpY2FsIHBvc2l0aW9uIG9mIHRoZSBjaXJjbGUgbWFya2VyXG5cdGdldExhdExuZzogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sYXRsbmc7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRSYWRpdXMocmFkaXVzOiBOdW1iZXIpOiB0aGlzXG5cdC8vIFNldHMgdGhlIHJhZGl1cyBvZiBhIGNpcmNsZSBtYXJrZXIuIFVuaXRzIGFyZSBpbiBwaXhlbHMuXG5cdHNldFJhZGl1czogZnVuY3Rpb24gKHJhZGl1cykge1xuXHRcdHRoaXMub3B0aW9ucy5yYWRpdXMgPSB0aGlzLl9yYWRpdXMgPSByYWRpdXM7XG5cdFx0cmV0dXJuIHRoaXMucmVkcmF3KCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRSYWRpdXMoKTogTnVtYmVyXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgcmFkaXVzIG9mIHRoZSBjaXJjbGVcblx0Z2V0UmFkaXVzOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3JhZGl1cztcblx0fSxcblxuXHRzZXRTdHlsZSA6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdFx0dmFyIHJhZGl1cyA9IG9wdGlvbnMgJiYgb3B0aW9ucy5yYWRpdXMgfHwgdGhpcy5fcmFkaXVzO1xuXHRcdEwuUGF0aC5wcm90b3R5cGUuc2V0U3R5bGUuY2FsbCh0aGlzLCBvcHRpb25zKTtcblx0XHR0aGlzLnNldFJhZGl1cyhyYWRpdXMpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdF9wcm9qZWN0OiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcG9pbnQgPSB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZyk7XG5cdFx0dGhpcy5fdXBkYXRlQm91bmRzKCk7XG5cdH0sXG5cblx0X3VwZGF0ZUJvdW5kczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciByID0gdGhpcy5fcmFkaXVzLFxuXHRcdCAgICByMiA9IHRoaXMuX3JhZGl1c1kgfHwgcixcblx0XHQgICAgdyA9IHRoaXMuX2NsaWNrVG9sZXJhbmNlKCksXG5cdFx0ICAgIHAgPSBbciArIHcsIHIyICsgd107XG5cdFx0dGhpcy5fcHhCb3VuZHMgPSBuZXcgTC5Cb3VuZHModGhpcy5fcG9pbnQuc3VidHJhY3QocCksIHRoaXMuX3BvaW50LmFkZChwKSk7XG5cdH0sXG5cblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9tYXApIHtcblx0XHRcdHRoaXMuX3VwZGF0ZVBhdGgoKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlQ2lyY2xlKHRoaXMpO1xuXHR9LFxuXG5cdF9lbXB0eTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9yYWRpdXMgJiYgIXRoaXMuX3JlbmRlcmVyLl9ib3VuZHMuaW50ZXJzZWN0cyh0aGlzLl9weEJvdW5kcyk7XG5cdH1cbn0pO1xuXG5cbi8vIEBmYWN0b3J5IEwuY2lyY2xlTWFya2VyKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogQ2lyY2xlTWFya2VyIG9wdGlvbnMpXG4vLyBJbnN0YW50aWF0ZXMgYSBjaXJjbGUgbWFya2VyIG9iamVjdCBnaXZlbiBhIGdlb2dyYXBoaWNhbCBwb2ludCwgYW5kIGFuIG9wdGlvbmFsIG9wdGlvbnMgb2JqZWN0LlxuTC5jaXJjbGVNYXJrZXIgPSBmdW5jdGlvbiAobGF0bG5nLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5DaXJjbGVNYXJrZXIobGF0bG5nLCBvcHRpb25zKTtcbn07XG5cblxuXG4vKlxuICogQGNsYXNzIENpcmNsZVxuICogQGFrYSBMLkNpcmNsZVxuICogQGluaGVyaXRzIENpcmNsZU1hcmtlclxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgY2lyY2xlIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBDaXJjbGVNYXJrZXJgLlxuICpcbiAqIEl0J3MgYW4gYXBwcm94aW1hdGlvbiBhbmQgc3RhcnRzIHRvIGRpdmVyZ2UgZnJvbSBhIHJlYWwgY2lyY2xlIGNsb3NlciB0byBwb2xlcyAoZHVlIHRvIHByb2plY3Rpb24gZGlzdG9ydGlvbikuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBgYGBqc1xuICogTC5jaXJjbGUoWzUwLjUsIDMwLjVdLCB7cmFkaXVzOiAyMDB9KS5hZGRUbyhtYXApO1xuICogYGBgXG4gKi9cblxuTC5DaXJjbGUgPSBMLkNpcmNsZU1hcmtlci5leHRlbmQoe1xuXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMsIGxlZ2FjeU9wdGlvbnMpIHtcblx0XHRpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdudW1iZXInKSB7XG5cdFx0XHQvLyBCYWNrd2FyZHMgY29tcGF0aWJpbGl0eSB3aXRoIDAuNy54IGZhY3RvcnkgKGxhdGxuZywgcmFkaXVzLCBvcHRpb25zPylcblx0XHRcdG9wdGlvbnMgPSBMLmV4dGVuZCh7fSwgbGVnYWN5T3B0aW9ucywge3JhZGl1czogb3B0aW9uc30pO1xuXHRcdH1cblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcblxuXHRcdGlmIChpc05hTih0aGlzLm9wdGlvbnMucmFkaXVzKSkgeyB0aHJvdyBuZXcgRXJyb3IoJ0NpcmNsZSByYWRpdXMgY2Fubm90IGJlIE5hTicpOyB9XG5cblx0XHQvLyBAc2VjdGlvblxuXHRcdC8vIEBha2EgQ2lyY2xlIG9wdGlvbnNcblx0XHQvLyBAb3B0aW9uIHJhZGl1czogTnVtYmVyOyBSYWRpdXMgb2YgdGhlIGNpcmNsZSwgaW4gbWV0ZXJzLlxuXHRcdHRoaXMuX21SYWRpdXMgPSB0aGlzLm9wdGlvbnMucmFkaXVzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0UmFkaXVzKHJhZGl1czogTnVtYmVyKTogdGhpc1xuXHQvLyBTZXRzIHRoZSByYWRpdXMgb2YgYSBjaXJjbGUuIFVuaXRzIGFyZSBpbiBtZXRlcnMuXG5cdHNldFJhZGl1czogZnVuY3Rpb24gKHJhZGl1cykge1xuXHRcdHRoaXMuX21SYWRpdXMgPSByYWRpdXM7XG5cdFx0cmV0dXJuIHRoaXMucmVkcmF3KCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRSYWRpdXMoKTogTnVtYmVyXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgcmFkaXVzIG9mIGEgY2lyY2xlLiBVbml0cyBhcmUgaW4gbWV0ZXJzLlxuXHRnZXRSYWRpdXM6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fbVJhZGl1cztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldEJvdW5kcygpOiBMYXRMbmdCb3VuZHNcblx0Ly8gUmV0dXJucyB0aGUgYExhdExuZ0JvdW5kc2Agb2YgdGhlIHBhdGguXG5cdGdldEJvdW5kczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBoYWxmID0gW3RoaXMuX3JhZGl1cywgdGhpcy5fcmFkaXVzWSB8fCB0aGlzLl9yYWRpdXNdO1xuXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZ0JvdW5kcyhcblx0XHRcdHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcodGhpcy5fcG9pbnQuc3VidHJhY3QoaGFsZikpLFxuXHRcdFx0dGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLl9wb2ludC5hZGQoaGFsZikpKTtcblx0fSxcblxuXHRzZXRTdHlsZTogTC5QYXRoLnByb3RvdHlwZS5zZXRTdHlsZSxcblxuXHRfcHJvamVjdDogZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIGxuZyA9IHRoaXMuX2xhdGxuZy5sbmcsXG5cdFx0ICAgIGxhdCA9IHRoaXMuX2xhdGxuZy5sYXQsXG5cdFx0ICAgIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgY3JzID0gbWFwLm9wdGlvbnMuY3JzO1xuXG5cdFx0aWYgKGNycy5kaXN0YW5jZSA9PT0gTC5DUlMuRWFydGguZGlzdGFuY2UpIHtcblx0XHRcdHZhciBkID0gTWF0aC5QSSAvIDE4MCxcblx0XHRcdCAgICBsYXRSID0gKHRoaXMuX21SYWRpdXMgLyBMLkNSUy5FYXJ0aC5SKSAvIGQsXG5cdFx0XHQgICAgdG9wID0gbWFwLnByb2plY3QoW2xhdCArIGxhdFIsIGxuZ10pLFxuXHRcdFx0ICAgIGJvdHRvbSA9IG1hcC5wcm9qZWN0KFtsYXQgLSBsYXRSLCBsbmddKSxcblx0XHRcdCAgICBwID0gdG9wLmFkZChib3R0b20pLmRpdmlkZUJ5KDIpLFxuXHRcdFx0ICAgIGxhdDIgPSBtYXAudW5wcm9qZWN0KHApLmxhdCxcblx0XHRcdCAgICBsbmdSID0gTWF0aC5hY29zKChNYXRoLmNvcyhsYXRSICogZCkgLSBNYXRoLnNpbihsYXQgKiBkKSAqIE1hdGguc2luKGxhdDIgKiBkKSkgL1xuXHRcdFx0ICAgICAgICAgICAgKE1hdGguY29zKGxhdCAqIGQpICogTWF0aC5jb3MobGF0MiAqIGQpKSkgLyBkO1xuXG5cdFx0XHRpZiAoaXNOYU4obG5nUikgfHwgbG5nUiA9PT0gMCkge1xuXHRcdFx0XHRsbmdSID0gbGF0UiAvIE1hdGguY29zKE1hdGguUEkgLyAxODAgKiBsYXQpOyAvLyBGYWxsYmFjayBmb3IgZWRnZSBjYXNlLCAjMjQyNVxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLl9wb2ludCA9IHAuc3VidHJhY3QobWFwLmdldFBpeGVsT3JpZ2luKCkpO1xuXHRcdFx0dGhpcy5fcmFkaXVzID0gaXNOYU4obG5nUikgPyAwIDogTWF0aC5tYXgoTWF0aC5yb3VuZChwLnggLSBtYXAucHJvamVjdChbbGF0MiwgbG5nIC0gbG5nUl0pLngpLCAxKTtcblx0XHRcdHRoaXMuX3JhZGl1c1kgPSBNYXRoLm1heChNYXRoLnJvdW5kKHAueSAtIHRvcC55KSwgMSk7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0dmFyIGxhdGxuZzIgPSBjcnMudW5wcm9qZWN0KGNycy5wcm9qZWN0KHRoaXMuX2xhdGxuZykuc3VidHJhY3QoW3RoaXMuX21SYWRpdXMsIDBdKSk7XG5cblx0XHRcdHRoaXMuX3BvaW50ID0gbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0aGlzLl9sYXRsbmcpO1xuXHRcdFx0dGhpcy5fcmFkaXVzID0gdGhpcy5fcG9pbnQueCAtIG1hcC5sYXRMbmdUb0xheWVyUG9pbnQobGF0bG5nMikueDtcblx0XHR9XG5cblx0XHR0aGlzLl91cGRhdGVCb3VuZHMoKTtcblx0fVxufSk7XG5cbi8vIEBmYWN0b3J5IEwuY2lyY2xlKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogQ2lyY2xlIG9wdGlvbnMpXG4vLyBJbnN0YW50aWF0ZXMgYSBjaXJjbGUgb2JqZWN0IGdpdmVuIGEgZ2VvZ3JhcGhpY2FsIHBvaW50LCBhbmQgYW4gb3B0aW9ucyBvYmplY3Rcbi8vIHdoaWNoIGNvbnRhaW5zIHRoZSBjaXJjbGUgcmFkaXVzLlxuLy8gQGFsdGVybmF0aXZlXG4vLyBAZmFjdG9yeSBMLmNpcmNsZShsYXRsbmc6IExhdExuZywgcmFkaXVzOiBOdW1iZXIsIG9wdGlvbnM/OiBDaXJjbGUgb3B0aW9ucylcbi8vIE9ic29sZXRlIHdheSBvZiBpbnN0YW50aWF0aW5nIGEgY2lyY2xlLCBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIDAuNy54IGNvZGUuXG4vLyBEbyBub3QgdXNlIGluIG5ldyBhcHBsaWNhdGlvbnMgb3IgcGx1Z2lucy5cbkwuY2lyY2xlID0gZnVuY3Rpb24gKGxhdGxuZywgb3B0aW9ucywgbGVnYWN5T3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuQ2lyY2xlKGxhdGxuZywgb3B0aW9ucywgbGVnYWN5T3B0aW9ucyk7XG59O1xuXG5cblxuLypcbiAqIEBjbGFzcyBTVkdcbiAqIEBpbmhlcml0cyBSZW5kZXJlclxuICogQGFrYSBMLlNWR1xuICpcbiAqIEFsbG93cyB2ZWN0b3IgbGF5ZXJzIHRvIGJlIGRpc3BsYXllZCB3aXRoIFtTVkddKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRykuXG4gKiBJbmhlcml0cyBgUmVuZGVyZXJgLlxuICpcbiAqIER1ZSB0byBbdGVjaG5pY2FsIGxpbWl0YXRpb25zXShodHRwOi8vY2FuaXVzZS5jb20vI3NlYXJjaD1zdmcpLCBTVkcgaXMgbm90XG4gKiBhdmFpbGFibGUgaW4gYWxsIHdlYiBicm93c2Vycywgbm90YWJseSBBbmRyb2lkIDIueCBhbmQgMy54LlxuICpcbiAqIEFsdGhvdWdoIFNWRyBpcyBub3QgYXZhaWxhYmxlIG9uIElFNyBhbmQgSUU4LCB0aGVzZSBicm93c2VycyBzdXBwb3J0XG4gKiBbVk1MXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9WZWN0b3JfTWFya3VwX0xhbmd1YWdlKVxuICogKGEgbm93IGRlcHJlY2F0ZWQgdGVjaG5vbG9neSksIGFuZCB0aGUgU1ZHIHJlbmRlcmVyIHdpbGwgZmFsbCBiYWNrIHRvIFZNTCBpblxuICogdGhpcyBjYXNlLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogVXNlIFNWRyBieSBkZWZhdWx0IGZvciBhbGwgcGF0aHMgaW4gdGhlIG1hcDpcbiAqXG4gKiBgYGBqc1xuICogdmFyIG1hcCA9IEwubWFwKCdtYXAnLCB7XG4gKiBcdHJlbmRlcmVyOiBMLnN2ZygpXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqIFVzZSBhIFNWRyByZW5kZXJlciB3aXRoIGV4dHJhIHBhZGRpbmcgZm9yIHNwZWNpZmljIHZlY3RvciBnZW9tZXRyaWVzOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcpO1xuICogdmFyIG15UmVuZGVyZXIgPSBMLnN2Zyh7IHBhZGRpbmc6IDAuNSB9KTtcbiAqIHZhciBsaW5lID0gTC5wb2x5bGluZSggY29vcmRpbmF0ZXMsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogdmFyIGNpcmNsZSA9IEwuY2lyY2xlKCBjZW50ZXIsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogYGBgXG4gKi9cblxuTC5TVkcgPSBMLlJlbmRlcmVyLmV4dGVuZCh7XG5cblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGV2ZW50cyA9IEwuUmVuZGVyZXIucHJvdG90eXBlLmdldEV2ZW50cy5jYWxsKHRoaXMpO1xuXHRcdGV2ZW50cy56b29tc3RhcnQgPSB0aGlzLl9vblpvb21TdGFydDtcblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fY29udGFpbmVyID0gTC5TVkcuY3JlYXRlKCdzdmcnKTtcblxuXHRcdC8vIG1ha2VzIGl0IHBvc3NpYmxlIHRvIGNsaWNrIHRocm91Z2ggc3ZnIHJvb3Q7IHdlJ2xsIHJlc2V0IGl0IGJhY2sgaW4gaW5kaXZpZHVhbCBwYXRoc1xuXHRcdHRoaXMuX2NvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ3BvaW50ZXItZXZlbnRzJywgJ25vbmUnKTtcblxuXHRcdHRoaXMuX3Jvb3RHcm91cCA9IEwuU1ZHLmNyZWF0ZSgnZycpO1xuXHRcdHRoaXMuX2NvbnRhaW5lci5hcHBlbmRDaGlsZCh0aGlzLl9yb290R3JvdXApO1xuXHR9LFxuXG5cdF9vblpvb21TdGFydDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIERyYWctdGhlbi1waW5jaCBpbnRlcmFjdGlvbnMgbWlnaHQgbWVzcyB1cCB0aGUgY2VudGVyIGFuZCB6b29tLlxuXHRcdC8vIEluIHRoaXMgY2FzZSwgdGhlIGVhc2llc3Qgd2F5IHRvIHByZXZlbnQgdGhpcyBpcyByZS1kbyB0aGUgcmVuZGVyZXJcblx0XHQvLyAgIGJvdW5kcyBhbmQgcGFkZGluZyB3aGVuIHRoZSB6b29taW5nIHN0YXJ0cy5cblx0XHR0aGlzLl91cGRhdGUoKTtcblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcC5fYW5pbWF0aW5nWm9vbSAmJiB0aGlzLl9ib3VuZHMpIHsgcmV0dXJuOyB9XG5cblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5fdXBkYXRlLmNhbGwodGhpcyk7XG5cblx0XHR2YXIgYiA9IHRoaXMuX2JvdW5kcyxcblx0XHQgICAgc2l6ZSA9IGIuZ2V0U2l6ZSgpLFxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXI7XG5cblx0XHQvLyBzZXQgc2l6ZSBvZiBzdmctY29udGFpbmVyIGlmIGNoYW5nZWRcblx0XHRpZiAoIXRoaXMuX3N2Z1NpemUgfHwgIXRoaXMuX3N2Z1NpemUuZXF1YWxzKHNpemUpKSB7XG5cdFx0XHR0aGlzLl9zdmdTaXplID0gc2l6ZTtcblx0XHRcdGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ3dpZHRoJywgc2l6ZS54KTtcblx0XHRcdGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ2hlaWdodCcsIHNpemUueSk7XG5cdFx0fVxuXG5cdFx0Ly8gbW92ZW1lbnQ6IHVwZGF0ZSBjb250YWluZXIgdmlld0JveCBzbyB0aGF0IHdlIGRvbid0IGhhdmUgdG8gY2hhbmdlIGNvb3JkaW5hdGVzIG9mIGluZGl2aWR1YWwgbGF5ZXJzXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKGNvbnRhaW5lciwgYi5taW4pO1xuXHRcdGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ3ZpZXdCb3gnLCBbYi5taW4ueCwgYi5taW4ueSwgc2l6ZS54LCBzaXplLnldLmpvaW4oJyAnKSk7XG5cblx0XHR0aGlzLmZpcmUoJ3VwZGF0ZScpO1xuXHR9LFxuXG5cdC8vIG1ldGhvZHMgYmVsb3cgYXJlIGNhbGxlZCBieSB2ZWN0b3IgbGF5ZXJzIGltcGxlbWVudGF0aW9uc1xuXG5cdF9pbml0UGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHBhdGggPSBsYXllci5fcGF0aCA9IEwuU1ZHLmNyZWF0ZSgncGF0aCcpO1xuXG5cdFx0Ly8gQG5hbWVzcGFjZSBQYXRoXG5cdFx0Ly8gQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9IG51bGxcblx0XHQvLyBDdXN0b20gY2xhc3MgbmFtZSBzZXQgb24gYW4gZWxlbWVudC4gT25seSBmb3IgU1ZHIHJlbmRlcmVyLlxuXHRcdGlmIChsYXllci5vcHRpb25zLmNsYXNzTmFtZSkge1xuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHBhdGgsIGxheWVyLm9wdGlvbnMuY2xhc3NOYW1lKTtcblx0XHR9XG5cblx0XHRpZiAobGF5ZXIub3B0aW9ucy5pbnRlcmFjdGl2ZSkge1xuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHBhdGgsICdsZWFmbGV0LWludGVyYWN0aXZlJyk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fdXBkYXRlU3R5bGUobGF5ZXIpO1xuXHRcdHRoaXMuX2xheWVyc1tMLnN0YW1wKGxheWVyKV0gPSBsYXllcjtcblx0fSxcblxuXHRfYWRkUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dGhpcy5fcm9vdEdyb3VwLmFwcGVuZENoaWxkKGxheWVyLl9wYXRoKTtcblx0XHRsYXllci5hZGRJbnRlcmFjdGl2ZVRhcmdldChsYXllci5fcGF0aCk7XG5cdH0sXG5cblx0X3JlbW92ZVBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC5yZW1vdmUobGF5ZXIuX3BhdGgpO1xuXHRcdGxheWVyLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KGxheWVyLl9wYXRoKTtcblx0XHRkZWxldGUgdGhpcy5fbGF5ZXJzW0wuc3RhbXAobGF5ZXIpXTtcblx0fSxcblxuXHRfdXBkYXRlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0bGF5ZXIuX3Byb2plY3QoKTtcblx0XHRsYXllci5fdXBkYXRlKCk7XG5cdH0sXG5cblx0X3VwZGF0ZVN0eWxlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcGF0aCA9IGxheWVyLl9wYXRoLFxuXHRcdCAgICBvcHRpb25zID0gbGF5ZXIub3B0aW9ucztcblxuXHRcdGlmICghcGF0aCkgeyByZXR1cm47IH1cblxuXHRcdGlmIChvcHRpb25zLnN0cm9rZSkge1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZScsIG9wdGlvbnMuY29sb3IpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS1vcGFjaXR5Jywgb3B0aW9ucy5vcGFjaXR5KTtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdzdHJva2Utd2lkdGgnLCBvcHRpb25zLndlaWdodCk7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLWxpbmVjYXAnLCBvcHRpb25zLmxpbmVDYXApO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS1saW5lam9pbicsIG9wdGlvbnMubGluZUpvaW4pO1xuXG5cdFx0XHRpZiAob3B0aW9ucy5kYXNoQXJyYXkpIHtcblx0XHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS1kYXNoYXJyYXknLCBvcHRpb25zLmRhc2hBcnJheSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRwYXRoLnJlbW92ZUF0dHJpYnV0ZSgnc3Ryb2tlLWRhc2hhcnJheScpO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAob3B0aW9ucy5kYXNoT2Zmc2V0KSB7XG5cdFx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdzdHJva2UtZGFzaG9mZnNldCcsIG9wdGlvbnMuZGFzaE9mZnNldCk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRwYXRoLnJlbW92ZUF0dHJpYnV0ZSgnc3Ryb2tlLWRhc2hvZmZzZXQnKTtcblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZScsICdub25lJyk7XG5cdFx0fVxuXG5cdFx0aWYgKG9wdGlvbnMuZmlsbCkge1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwnLCBvcHRpb25zLmZpbGxDb2xvciB8fCBvcHRpb25zLmNvbG9yKTtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdmaWxsLW9wYWNpdHknLCBvcHRpb25zLmZpbGxPcGFjaXR5KTtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdmaWxsLXJ1bGUnLCBvcHRpb25zLmZpbGxSdWxlIHx8ICdldmVub2RkJyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdmaWxsJywgJ25vbmUnKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVBvbHk6IGZ1bmN0aW9uIChsYXllciwgY2xvc2VkKSB7XG5cdFx0dGhpcy5fc2V0UGF0aChsYXllciwgTC5TVkcucG9pbnRzVG9QYXRoKGxheWVyLl9wYXJ0cywgY2xvc2VkKSk7XG5cdH0sXG5cblx0X3VwZGF0ZUNpcmNsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHAgPSBsYXllci5fcG9pbnQsXG5cdFx0ICAgIHIgPSBsYXllci5fcmFkaXVzLFxuXHRcdCAgICByMiA9IGxheWVyLl9yYWRpdXNZIHx8IHIsXG5cdFx0ICAgIGFyYyA9ICdhJyArIHIgKyAnLCcgKyByMiArICcgMCAxLDAgJztcblxuXHRcdC8vIGRyYXdpbmcgYSBjaXJjbGUgd2l0aCB0d28gaGFsZi1hcmNzXG5cdFx0dmFyIGQgPSBsYXllci5fZW1wdHkoKSA/ICdNMCAwJyA6XG5cdFx0XHRcdCdNJyArIChwLnggLSByKSArICcsJyArIHAueSArXG5cdFx0XHRcdGFyYyArIChyICogMikgKyAnLDAgJyArXG5cdFx0XHRcdGFyYyArICgtciAqIDIpICsgJywwICc7XG5cblx0XHR0aGlzLl9zZXRQYXRoKGxheWVyLCBkKTtcblx0fSxcblxuXHRfc2V0UGF0aDogZnVuY3Rpb24gKGxheWVyLCBwYXRoKSB7XG5cdFx0bGF5ZXIuX3BhdGguc2V0QXR0cmlidXRlKCdkJywgcGF0aCk7XG5cdH0sXG5cblx0Ly8gU1ZHIGRvZXMgbm90IGhhdmUgdGhlIGNvbmNlcHQgb2YgekluZGV4IHNvIHdlIHJlc29ydCB0byBjaGFuZ2luZyB0aGUgRE9NIG9yZGVyIG9mIGVsZW1lbnRzXG5cdF9icmluZ1RvRnJvbnQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC50b0Zyb250KGxheWVyLl9wYXRoKTtcblx0fSxcblxuXHRfYnJpbmdUb0JhY2s6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC50b0JhY2sobGF5ZXIuX3BhdGgpO1xuXHR9XG59KTtcblxuXG4vLyBAbmFtZXNwYWNlIFNWRzsgQHNlY3Rpb25cbi8vIFRoZXJlIGFyZSBzZXZlcmFsIHN0YXRpYyBmdW5jdGlvbnMgd2hpY2ggY2FuIGJlIGNhbGxlZCB3aXRob3V0IGluc3RhbnRpYXRpbmcgTC5TVkc6XG5MLmV4dGVuZChMLlNWRywge1xuXHQvLyBAZnVuY3Rpb24gY3JlYXRlKG5hbWU6IFN0cmluZyk6IFNWR0VsZW1lbnRcblx0Ly8gUmV0dXJucyBhIGluc3RhbmNlIG9mIFtTVkdFbGVtZW50XShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvU1ZHRWxlbWVudCksXG5cdC8vIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGNsYXNzIG5hbWUgcGFzc2VkLiBGb3IgZXhhbXBsZSwgdXNpbmcgJ2xpbmUnIHdpbGwgcmV0dXJuXG5cdC8vIGFuIGluc3RhbmNlIG9mIFtTVkdMaW5lRWxlbWVudF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL1NWR0xpbmVFbGVtZW50KS5cblx0Y3JlYXRlOiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdHJldHVybiBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoJ2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJywgbmFtZSk7XG5cdH0sXG5cblx0Ly8gQGZ1bmN0aW9uIHBvaW50c1RvUGF0aChyaW5nczogUG9pbnRbXSwgY2xvc2VkOiBCb29sZWFuKTogU3RyaW5nXG5cdC8vIEdlbmVyYXRlcyBhIFNWRyBwYXRoIHN0cmluZyBmb3IgbXVsdGlwbGUgcmluZ3MsIHdpdGggZWFjaCByaW5nIHR1cm5pbmdcblx0Ly8gaW50byBcIk0uLkwuLkwuLlwiIGluc3RydWN0aW9uc1xuXHRwb2ludHNUb1BhdGg6IGZ1bmN0aW9uIChyaW5ncywgY2xvc2VkKSB7XG5cdFx0dmFyIHN0ciA9ICcnLFxuXHRcdCAgICBpLCBqLCBsZW4sIGxlbjIsIHBvaW50cywgcDtcblxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IHJpbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRwb2ludHMgPSByaW5nc1tpXTtcblxuXHRcdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBvaW50cy5sZW5ndGg7IGogPCBsZW4yOyBqKyspIHtcblx0XHRcdFx0cCA9IHBvaW50c1tqXTtcblx0XHRcdFx0c3RyICs9IChqID8gJ0wnIDogJ00nKSArIHAueCArICcgJyArIHAueTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gY2xvc2VzIHRoZSByaW5nIGZvciBwb2x5Z29uczsgXCJ4XCIgaXMgVk1MIHN5bnRheFxuXHRcdFx0c3RyICs9IGNsb3NlZCA/IChMLkJyb3dzZXIuc3ZnID8gJ3onIDogJ3gnKSA6ICcnO1xuXHRcdH1cblxuXHRcdC8vIFNWRyBjb21wbGFpbnMgYWJvdXQgZW1wdHkgcGF0aCBzdHJpbmdzXG5cdFx0cmV0dXJuIHN0ciB8fCAnTTAgMCc7XG5cdH1cbn0pO1xuXG4vLyBAbmFtZXNwYWNlIEJyb3dzZXI7IEBwcm9wZXJ0eSBzdmc6IEJvb2xlYW5cbi8vIGB0cnVlYCB3aGVuIHRoZSBicm93c2VyIHN1cHBvcnRzIFtTVkddKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRykuXG5MLkJyb3dzZXIuc3ZnID0gISEoZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TICYmIEwuU1ZHLmNyZWF0ZSgnc3ZnJykuY3JlYXRlU1ZHUmVjdCk7XG5cblxuLy8gQG5hbWVzcGFjZSBTVkdcbi8vIEBmYWN0b3J5IEwuc3ZnKG9wdGlvbnM/OiBSZW5kZXJlciBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhIFNWRyByZW5kZXJlciB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuTC5zdmcgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gTC5Ccm93c2VyLnN2ZyB8fCBMLkJyb3dzZXIudm1sID8gbmV3IEwuU1ZHKG9wdGlvbnMpIDogbnVsbDtcbn07XG5cblxuXG4vKlxuICogVGhhbmtzIHRvIERtaXRyeSBCYXJhbm92c2t5IGFuZCBoaXMgUmFwaGFlbCBsaWJyYXJ5IGZvciBpbnNwaXJhdGlvbiFcbiAqL1xuXG4vKlxuICogQGNsYXNzIFNWR1xuICpcbiAqIEFsdGhvdWdoIFNWRyBpcyBub3QgYXZhaWxhYmxlIG9uIElFNyBhbmQgSUU4LCB0aGVzZSBicm93c2VycyBzdXBwb3J0IFtWTUxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1ZlY3Rvcl9NYXJrdXBfTGFuZ3VhZ2UpLCBhbmQgdGhlIFNWRyByZW5kZXJlciB3aWxsIGZhbGwgYmFjayB0byBWTUwgaW4gdGhpcyBjYXNlLlxuICpcbiAqIFZNTCB3YXMgZGVwcmVjYXRlZCBpbiAyMDEyLCB3aGljaCBtZWFucyBWTUwgZnVuY3Rpb25hbGl0eSBleGlzdHMgb25seSBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHlcbiAqIHdpdGggb2xkIHZlcnNpb25zIG9mIEludGVybmV0IEV4cGxvcmVyLlxuICovXG5cbi8vIEBuYW1lc3BhY2UgQnJvd3NlcjsgQHByb3BlcnR5IHZtbDogQm9vbGVhblxuLy8gYHRydWVgIGlmIHRoZSBicm93c2VyIHN1cHBvcnRzIFtWTUxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1ZlY3Rvcl9NYXJrdXBfTGFuZ3VhZ2UpLlxuTC5Ccm93c2VyLnZtbCA9ICFMLkJyb3dzZXIuc3ZnICYmIChmdW5jdGlvbiAoKSB7XG5cdHRyeSB7XG5cdFx0dmFyIGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuXHRcdGRpdi5pbm5lckhUTUwgPSAnPHY6c2hhcGUgYWRqPVwiMVwiLz4nO1xuXG5cdFx0dmFyIHNoYXBlID0gZGl2LmZpcnN0Q2hpbGQ7XG5cdFx0c2hhcGUuc3R5bGUuYmVoYXZpb3IgPSAndXJsKCNkZWZhdWx0I1ZNTCknO1xuXG5cdFx0cmV0dXJuIHNoYXBlICYmICh0eXBlb2Ygc2hhcGUuYWRqID09PSAnb2JqZWN0Jyk7XG5cblx0fSBjYXRjaCAoZSkge1xuXHRcdHJldHVybiBmYWxzZTtcblx0fVxufSgpKTtcblxuLy8gcmVkZWZpbmUgc29tZSBTVkcgbWV0aG9kcyB0byBoYW5kbGUgVk1MIHN5bnRheCB3aGljaCBpcyBzaW1pbGFyIGJ1dCB3aXRoIHNvbWUgZGlmZmVyZW5jZXNcbkwuU1ZHLmluY2x1ZGUoIUwuQnJvd3Nlci52bWwgPyB7fSA6IHtcblxuXHRfaW5pdENvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXZtbC1jb250YWluZXInKTtcblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcC5fYW5pbWF0aW5nWm9vbSkgeyByZXR1cm47IH1cblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5fdXBkYXRlLmNhbGwodGhpcyk7XG5cdFx0dGhpcy5maXJlKCd1cGRhdGUnKTtcblx0fSxcblxuXHRfaW5pdFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBjb250YWluZXIgPSBsYXllci5fY29udGFpbmVyID0gTC5TVkcuY3JlYXRlKCdzaGFwZScpO1xuXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtdm1sLXNoYXBlICcgKyAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSB8fCAnJykpO1xuXG5cdFx0Y29udGFpbmVyLmNvb3Jkc2l6ZSA9ICcxIDEnO1xuXG5cdFx0bGF5ZXIuX3BhdGggPSBMLlNWRy5jcmVhdGUoJ3BhdGgnKTtcblx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQobGF5ZXIuX3BhdGgpO1xuXG5cdFx0dGhpcy5fdXBkYXRlU3R5bGUobGF5ZXIpO1xuXHRcdHRoaXMuX2xheWVyc1tMLnN0YW1wKGxheWVyKV0gPSBsYXllcjtcblx0fSxcblxuXHRfYWRkUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IGxheWVyLl9jb250YWluZXI7XG5cdFx0dGhpcy5fY29udGFpbmVyLmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XG5cblx0XHRpZiAobGF5ZXIub3B0aW9ucy5pbnRlcmFjdGl2ZSkge1xuXHRcdFx0bGF5ZXIuYWRkSW50ZXJhY3RpdmVUYXJnZXQoY29udGFpbmVyKTtcblx0XHR9XG5cdH0sXG5cblx0X3JlbW92ZVBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBjb250YWluZXIgPSBsYXllci5fY29udGFpbmVyO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmUoY29udGFpbmVyKTtcblx0XHRsYXllci5yZW1vdmVJbnRlcmFjdGl2ZVRhcmdldChjb250YWluZXIpO1xuXHRcdGRlbGV0ZSB0aGlzLl9sYXllcnNbTC5zdGFtcChsYXllcildO1xuXHR9LFxuXG5cdF91cGRhdGVTdHlsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHN0cm9rZSA9IGxheWVyLl9zdHJva2UsXG5cdFx0ICAgIGZpbGwgPSBsYXllci5fZmlsbCxcblx0XHQgICAgb3B0aW9ucyA9IGxheWVyLm9wdGlvbnMsXG5cdFx0ICAgIGNvbnRhaW5lciA9IGxheWVyLl9jb250YWluZXI7XG5cblx0XHRjb250YWluZXIuc3Ryb2tlZCA9ICEhb3B0aW9ucy5zdHJva2U7XG5cdFx0Y29udGFpbmVyLmZpbGxlZCA9ICEhb3B0aW9ucy5maWxsO1xuXG5cdFx0aWYgKG9wdGlvbnMuc3Ryb2tlKSB7XG5cdFx0XHRpZiAoIXN0cm9rZSkge1xuXHRcdFx0XHRzdHJva2UgPSBsYXllci5fc3Ryb2tlID0gTC5TVkcuY3JlYXRlKCdzdHJva2UnKTtcblx0XHRcdH1cblx0XHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChzdHJva2UpO1xuXHRcdFx0c3Ryb2tlLndlaWdodCA9IG9wdGlvbnMud2VpZ2h0ICsgJ3B4Jztcblx0XHRcdHN0cm9rZS5jb2xvciA9IG9wdGlvbnMuY29sb3I7XG5cdFx0XHRzdHJva2Uub3BhY2l0eSA9IG9wdGlvbnMub3BhY2l0eTtcblxuXHRcdFx0aWYgKG9wdGlvbnMuZGFzaEFycmF5KSB7XG5cdFx0XHRcdHN0cm9rZS5kYXNoU3R5bGUgPSBMLlV0aWwuaXNBcnJheShvcHRpb25zLmRhc2hBcnJheSkgP1xuXHRcdFx0XHQgICAgb3B0aW9ucy5kYXNoQXJyYXkuam9pbignICcpIDpcblx0XHRcdFx0ICAgIG9wdGlvbnMuZGFzaEFycmF5LnJlcGxhY2UoLyggKiwgKikvZywgJyAnKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHN0cm9rZS5kYXNoU3R5bGUgPSAnJztcblx0XHRcdH1cblx0XHRcdHN0cm9rZS5lbmRjYXAgPSBvcHRpb25zLmxpbmVDYXAucmVwbGFjZSgnYnV0dCcsICdmbGF0Jyk7XG5cdFx0XHRzdHJva2Uuam9pbnN0eWxlID0gb3B0aW9ucy5saW5lSm9pbjtcblxuXHRcdH0gZWxzZSBpZiAoc3Ryb2tlKSB7XG5cdFx0XHRjb250YWluZXIucmVtb3ZlQ2hpbGQoc3Ryb2tlKTtcblx0XHRcdGxheWVyLl9zdHJva2UgPSBudWxsO1xuXHRcdH1cblxuXHRcdGlmIChvcHRpb25zLmZpbGwpIHtcblx0XHRcdGlmICghZmlsbCkge1xuXHRcdFx0XHRmaWxsID0gbGF5ZXIuX2ZpbGwgPSBMLlNWRy5jcmVhdGUoJ2ZpbGwnKTtcblx0XHRcdH1cblx0XHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChmaWxsKTtcblx0XHRcdGZpbGwuY29sb3IgPSBvcHRpb25zLmZpbGxDb2xvciB8fCBvcHRpb25zLmNvbG9yO1xuXHRcdFx0ZmlsbC5vcGFjaXR5ID0gb3B0aW9ucy5maWxsT3BhY2l0eTtcblxuXHRcdH0gZWxzZSBpZiAoZmlsbCkge1xuXHRcdFx0Y29udGFpbmVyLnJlbW92ZUNoaWxkKGZpbGwpO1xuXHRcdFx0bGF5ZXIuX2ZpbGwgPSBudWxsO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlQ2lyY2xlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcCA9IGxheWVyLl9wb2ludC5yb3VuZCgpLFxuXHRcdCAgICByID0gTWF0aC5yb3VuZChsYXllci5fcmFkaXVzKSxcblx0XHQgICAgcjIgPSBNYXRoLnJvdW5kKGxheWVyLl9yYWRpdXNZIHx8IHIpO1xuXG5cdFx0dGhpcy5fc2V0UGF0aChsYXllciwgbGF5ZXIuX2VtcHR5KCkgPyAnTTAgMCcgOlxuXHRcdFx0XHQnQUwgJyArIHAueCArICcsJyArIHAueSArICcgJyArIHIgKyAnLCcgKyByMiArICcgMCwnICsgKDY1NTM1ICogMzYwKSk7XG5cdH0sXG5cblx0X3NldFBhdGg6IGZ1bmN0aW9uIChsYXllciwgcGF0aCkge1xuXHRcdGxheWVyLl9wYXRoLnYgPSBwYXRoO1xuXHR9LFxuXG5cdF9icmluZ1RvRnJvbnQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC50b0Zyb250KGxheWVyLl9jb250YWluZXIpO1xuXHR9LFxuXG5cdF9icmluZ1RvQmFjazogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0TC5Eb21VdGlsLnRvQmFjayhsYXllci5fY29udGFpbmVyKTtcblx0fVxufSk7XG5cbmlmIChMLkJyb3dzZXIudm1sKSB7XG5cdEwuU1ZHLmNyZWF0ZSA9IChmdW5jdGlvbiAoKSB7XG5cdFx0dHJ5IHtcblx0XHRcdGRvY3VtZW50Lm5hbWVzcGFjZXMuYWRkKCdsdm1sJywgJ3VybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206dm1sJyk7XG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gKG5hbWUpIHtcblx0XHRcdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJzxsdm1sOicgKyBuYW1lICsgJyBjbGFzcz1cImx2bWxcIj4nKTtcblx0XHRcdH07XG5cdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0XHRcdHJldHVybiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCc8JyArIG5hbWUgKyAnIHhtbG5zPVwidXJuOnNjaGVtYXMtbWljcm9zb2Z0LmNvbTp2bWxcIiBjbGFzcz1cImx2bWxcIj4nKTtcblx0XHRcdH07XG5cdFx0fVxuXHR9KSgpO1xufVxuXG5cblxuLypcbiAqIEBjbGFzcyBDYW52YXNcbiAqIEBpbmhlcml0cyBSZW5kZXJlclxuICogQGFrYSBMLkNhbnZhc1xuICpcbiAqIEFsbG93cyB2ZWN0b3IgbGF5ZXJzIHRvIGJlIGRpc3BsYXllZCB3aXRoIFtgPGNhbnZhcz5gXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvQ2FudmFzX0FQSSkuXG4gKiBJbmhlcml0cyBgUmVuZGVyZXJgLlxuICpcbiAqIER1ZSB0byBbdGVjaG5pY2FsIGxpbWl0YXRpb25zXShodHRwOi8vY2FuaXVzZS5jb20vI3NlYXJjaD1jYW52YXMpLCBDYW52YXMgaXMgbm90XG4gKiBhdmFpbGFibGUgaW4gYWxsIHdlYiBicm93c2Vycywgbm90YWJseSBJRTgsIGFuZCBvdmVybGFwcGluZyBnZW9tZXRyaWVzIG1pZ2h0XG4gKiBub3QgZGlzcGxheSBwcm9wZXJseSBpbiBzb21lIGVkZ2UgY2FzZXMuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBVc2UgQ2FudmFzIGJ5IGRlZmF1bHQgZm9yIGFsbCBwYXRocyBpbiB0aGUgbWFwOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcsIHtcbiAqIFx0cmVuZGVyZXI6IEwuY2FudmFzKClcbiAqIH0pO1xuICogYGBgXG4gKlxuICogVXNlIGEgQ2FudmFzIHJlbmRlcmVyIHdpdGggZXh0cmEgcGFkZGluZyBmb3Igc3BlY2lmaWMgdmVjdG9yIGdlb21ldHJpZXM6XG4gKlxuICogYGBganNcbiAqIHZhciBtYXAgPSBMLm1hcCgnbWFwJyk7XG4gKiB2YXIgbXlSZW5kZXJlciA9IEwuY2FudmFzKHsgcGFkZGluZzogMC41IH0pO1xuICogdmFyIGxpbmUgPSBMLnBvbHlsaW5lKCBjb29yZGluYXRlcywgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiB2YXIgY2lyY2xlID0gTC5jaXJjbGUoIGNlbnRlciwgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiBgYGBcbiAqL1xuXG5MLkNhbnZhcyA9IEwuUmVuZGVyZXIuZXh0ZW5kKHtcblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGV2ZW50cyA9IEwuUmVuZGVyZXIucHJvdG90eXBlLmdldEV2ZW50cy5jYWxsKHRoaXMpO1xuXHRcdGV2ZW50cy52aWV3cHJlcmVzZXQgPSB0aGlzLl9vblZpZXdQcmVSZXNldDtcblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdF9vblZpZXdQcmVSZXNldDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIFNldCBhIGZsYWcgc28gdGhhdCBhIHZpZXdwcmVyZXNldCttb3ZlZW5kK3ZpZXdyZXNldCBvbmx5IHVwZGF0ZXMmcmVkcmF3cyBvbmNlXG5cdFx0dGhpcy5fcG9zdHBvbmVVcGRhdGVQYXRocyA9IHRydWU7XG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMpO1xuXG5cdFx0Ly8gUmVkcmF3IHZlY3RvcnMgc2luY2UgY2FudmFzIGlzIGNsZWFyZWQgdXBvbiByZW1vdmFsLFxuXHRcdC8vIGluIGNhc2Ugb2YgcmVtb3ZpbmcgdGhlIHJlbmRlcmVyIGl0c2VsZiBmcm9tIHRoZSBtYXAuXG5cdFx0dGhpcy5fZHJhdygpO1xuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuXG5cdFx0TC5Eb21FdmVudFxuXHRcdFx0Lm9uKGNvbnRhaW5lciwgJ21vdXNlbW92ZScsIEwuVXRpbC50aHJvdHRsZSh0aGlzLl9vbk1vdXNlTW92ZSwgMzIsIHRoaXMpLCB0aGlzKVxuXHRcdFx0Lm9uKGNvbnRhaW5lciwgJ2NsaWNrIGRibGNsaWNrIG1vdXNlZG93biBtb3VzZXVwIGNvbnRleHRtZW51JywgdGhpcy5fb25DbGljaywgdGhpcylcblx0XHRcdC5vbihjb250YWluZXIsICdtb3VzZW91dCcsIHRoaXMuX2hhbmRsZU1vdXNlT3V0LCB0aGlzKTtcblxuXHRcdHRoaXMuX2N0eCA9IGNvbnRhaW5lci5nZXRDb250ZXh0KCcyZCcpO1xuXHR9LFxuXG5cdF91cGRhdGVQYXRoczogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9wb3N0cG9uZVVwZGF0ZVBhdGhzKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIGxheWVyO1xuXHRcdHRoaXMuX3JlZHJhd0JvdW5kcyA9IG51bGw7XG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHRsYXllciA9IHRoaXMuX2xheWVyc1tpZF07XG5cdFx0XHRsYXllci5fdXBkYXRlKCk7XG5cdFx0fVxuXHRcdHRoaXMuX3JlZHJhdygpO1xuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwLl9hbmltYXRpbmdab29tICYmIHRoaXMuX2JvdW5kcykgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2RyYXduTGF5ZXJzID0ge307XG5cblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5fdXBkYXRlLmNhbGwodGhpcyk7XG5cblx0XHR2YXIgYiA9IHRoaXMuX2JvdW5kcyxcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyLFxuXHRcdCAgICBzaXplID0gYi5nZXRTaXplKCksXG5cdFx0ICAgIG0gPSBMLkJyb3dzZXIucmV0aW5hID8gMiA6IDE7XG5cblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24oY29udGFpbmVyLCBiLm1pbik7XG5cblx0XHQvLyBzZXQgY2FudmFzIHNpemUgKGFsc28gY2xlYXJpbmcgaXQpOyB1c2UgZG91YmxlIHNpemUgb24gcmV0aW5hXG5cdFx0Y29udGFpbmVyLndpZHRoID0gbSAqIHNpemUueDtcblx0XHRjb250YWluZXIuaGVpZ2h0ID0gbSAqIHNpemUueTtcblx0XHRjb250YWluZXIuc3R5bGUud2lkdGggPSBzaXplLnggKyAncHgnO1xuXHRcdGNvbnRhaW5lci5zdHlsZS5oZWlnaHQgPSBzaXplLnkgKyAncHgnO1xuXG5cdFx0aWYgKEwuQnJvd3Nlci5yZXRpbmEpIHtcblx0XHRcdHRoaXMuX2N0eC5zY2FsZSgyLCAyKTtcblx0XHR9XG5cblx0XHQvLyB0cmFuc2xhdGUgc28gd2UgdXNlIHRoZSBzYW1lIHBhdGggY29vcmRpbmF0ZXMgYWZ0ZXIgY2FudmFzIGVsZW1lbnQgbW92ZXNcblx0XHR0aGlzLl9jdHgudHJhbnNsYXRlKC1iLm1pbi54LCAtYi5taW4ueSk7XG5cblx0XHQvLyBUZWxsIHBhdGhzIHRvIHJlZHJhdyB0aGVtc2VsdmVzXG5cdFx0dGhpcy5maXJlKCd1cGRhdGUnKTtcblx0fSxcblxuXHRfcmVzZXQ6IGZ1bmN0aW9uICgpIHtcblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5fcmVzZXQuY2FsbCh0aGlzKTtcblxuXHRcdGlmICh0aGlzLl9wb3N0cG9uZVVwZGF0ZVBhdGhzKSB7XG5cdFx0XHR0aGlzLl9wb3N0cG9uZVVwZGF0ZVBhdGhzID0gZmFsc2U7XG5cdFx0XHR0aGlzLl91cGRhdGVQYXRocygpO1xuXHRcdH1cblx0fSxcblxuXHRfaW5pdFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHRoaXMuX3VwZGF0ZURhc2hBcnJheShsYXllcik7XG5cdFx0dGhpcy5fbGF5ZXJzW0wuc3RhbXAobGF5ZXIpXSA9IGxheWVyO1xuXG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyID0ge1xuXHRcdFx0bGF5ZXI6IGxheWVyLFxuXHRcdFx0cHJldjogdGhpcy5fZHJhd0xhc3QsXG5cdFx0XHRuZXh0OiBudWxsXG5cdFx0fTtcblx0XHRpZiAodGhpcy5fZHJhd0xhc3QpIHsgdGhpcy5fZHJhd0xhc3QubmV4dCA9IG9yZGVyOyB9XG5cdFx0dGhpcy5fZHJhd0xhc3QgPSBvcmRlcjtcblx0XHR0aGlzLl9kcmF3Rmlyc3QgPSB0aGlzLl9kcmF3Rmlyc3QgfHwgdGhpcy5fZHJhd0xhc3Q7XG5cdH0sXG5cblx0X2FkZFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF9yZW1vdmVQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgb3JkZXIgPSBsYXllci5fb3JkZXI7XG5cdFx0dmFyIG5leHQgPSBvcmRlci5uZXh0O1xuXHRcdHZhciBwcmV2ID0gb3JkZXIucHJldjtcblxuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRuZXh0LnByZXYgPSBwcmV2O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9kcmF3TGFzdCA9IHByZXY7XG5cdFx0fVxuXHRcdGlmIChwcmV2KSB7XG5cdFx0XHRwcmV2Lm5leHQgPSBuZXh0O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9kcmF3Rmlyc3QgPSBuZXh0O1xuXHRcdH1cblxuXHRcdGRlbGV0ZSBsYXllci5fb3JkZXI7XG5cblx0XHRkZWxldGUgdGhpcy5fbGF5ZXJzW0wuc3RhbXAobGF5ZXIpXTtcblxuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF91cGRhdGVQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHQvLyBSZWRyYXcgdGhlIHVuaW9uIG9mIHRoZSBsYXllcidzIG9sZCBwaXhlbFxuXHRcdC8vIGJvdW5kcyBhbmQgdGhlIG5ldyBwaXhlbCBib3VuZHMuXG5cdFx0dGhpcy5fZXh0ZW5kUmVkcmF3Qm91bmRzKGxheWVyKTtcblx0XHRsYXllci5fcHJvamVjdCgpO1xuXHRcdGxheWVyLl91cGRhdGUoKTtcblx0XHQvLyBUaGUgcmVkcmF3IHdpbGwgZXh0ZW5kIHRoZSByZWRyYXcgYm91bmRzXG5cdFx0Ly8gd2l0aCB0aGUgbmV3IHBpeGVsIGJvdW5kcy5cblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fSxcblxuXHRfdXBkYXRlU3R5bGU6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHRoaXMuX3VwZGF0ZURhc2hBcnJheShsYXllcik7XG5cdFx0dGhpcy5fcmVxdWVzdFJlZHJhdyhsYXllcik7XG5cdH0sXG5cblx0X3VwZGF0ZURhc2hBcnJheTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0aWYgKGxheWVyLm9wdGlvbnMuZGFzaEFycmF5KSB7XG5cdFx0XHR2YXIgcGFydHMgPSBsYXllci5vcHRpb25zLmRhc2hBcnJheS5zcGxpdCgnLCcpLFxuXHRcdFx0ICAgIGRhc2hBcnJheSA9IFtdLFxuXHRcdFx0ICAgIGk7XG5cdFx0XHRmb3IgKGkgPSAwOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcblx0XHRcdFx0ZGFzaEFycmF5LnB1c2goTnVtYmVyKHBhcnRzW2ldKSk7XG5cdFx0XHR9XG5cdFx0XHRsYXllci5vcHRpb25zLl9kYXNoQXJyYXkgPSBkYXNoQXJyYXk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXF1ZXN0UmVkcmF3OiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2V4dGVuZFJlZHJhd0JvdW5kcyhsYXllcik7XG5cdFx0dGhpcy5fcmVkcmF3UmVxdWVzdCA9IHRoaXMuX3JlZHJhd1JlcXVlc3QgfHwgTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fcmVkcmF3LCB0aGlzKTtcblx0fSxcblxuXHRfZXh0ZW5kUmVkcmF3Qm91bmRzOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcGFkZGluZyA9IChsYXllci5vcHRpb25zLndlaWdodCB8fCAwKSArIDE7XG5cdFx0dGhpcy5fcmVkcmF3Qm91bmRzID0gdGhpcy5fcmVkcmF3Qm91bmRzIHx8IG5ldyBMLkJvdW5kcygpO1xuXHRcdHRoaXMuX3JlZHJhd0JvdW5kcy5leHRlbmQobGF5ZXIuX3B4Qm91bmRzLm1pbi5zdWJ0cmFjdChbcGFkZGluZywgcGFkZGluZ10pKTtcblx0XHR0aGlzLl9yZWRyYXdCb3VuZHMuZXh0ZW5kKGxheWVyLl9weEJvdW5kcy5tYXguYWRkKFtwYWRkaW5nLCBwYWRkaW5nXSkpO1xuXHR9LFxuXG5cdF9yZWRyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZWRyYXdSZXF1ZXN0ID0gbnVsbDtcblxuXHRcdGlmICh0aGlzLl9yZWRyYXdCb3VuZHMpIHtcblx0XHRcdHRoaXMuX3JlZHJhd0JvdW5kcy5taW4uX2Zsb29yKCk7XG5cdFx0XHR0aGlzLl9yZWRyYXdCb3VuZHMubWF4Ll9jZWlsKCk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fY2xlYXIoKTsgLy8gY2xlYXIgbGF5ZXJzIGluIHJlZHJhdyBib3VuZHNcblx0XHR0aGlzLl9kcmF3KCk7IC8vIGRyYXcgbGF5ZXJzXG5cblx0XHR0aGlzLl9yZWRyYXdCb3VuZHMgPSBudWxsO1xuXHR9LFxuXG5cdF9jbGVhcjogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBib3VuZHMgPSB0aGlzLl9yZWRyYXdCb3VuZHM7XG5cdFx0aWYgKGJvdW5kcykge1xuXHRcdFx0dmFyIHNpemUgPSBib3VuZHMuZ2V0U2l6ZSgpO1xuXHRcdFx0dGhpcy5fY3R4LmNsZWFyUmVjdChib3VuZHMubWluLngsIGJvdW5kcy5taW4ueSwgc2l6ZS54LCBzaXplLnkpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9jdHguY2xlYXJSZWN0KDAsIDAsIHRoaXMuX2NvbnRhaW5lci53aWR0aCwgdGhpcy5fY29udGFpbmVyLmhlaWdodCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9kcmF3OiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGxheWVyLCBib3VuZHMgPSB0aGlzLl9yZWRyYXdCb3VuZHM7XG5cdFx0dGhpcy5fY3R4LnNhdmUoKTtcblx0XHRpZiAoYm91bmRzKSB7XG5cdFx0XHR2YXIgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XG5cdFx0XHR0aGlzLl9jdHguYmVnaW5QYXRoKCk7XG5cdFx0XHR0aGlzLl9jdHgucmVjdChib3VuZHMubWluLngsIGJvdW5kcy5taW4ueSwgc2l6ZS54LCBzaXplLnkpO1xuXHRcdFx0dGhpcy5fY3R4LmNsaXAoKTtcblx0XHR9XG5cblx0XHR0aGlzLl9kcmF3aW5nID0gdHJ1ZTtcblxuXHRcdGZvciAodmFyIG9yZGVyID0gdGhpcy5fZHJhd0ZpcnN0OyBvcmRlcjsgb3JkZXIgPSBvcmRlci5uZXh0KSB7XG5cdFx0XHRsYXllciA9IG9yZGVyLmxheWVyO1xuXHRcdFx0aWYgKCFib3VuZHMgfHwgKGxheWVyLl9weEJvdW5kcyAmJiBsYXllci5fcHhCb3VuZHMuaW50ZXJzZWN0cyhib3VuZHMpKSkge1xuXHRcdFx0XHRsYXllci5fdXBkYXRlUGF0aCgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuX2RyYXdpbmcgPSBmYWxzZTtcblxuXHRcdHRoaXMuX2N0eC5yZXN0b3JlKCk7ICAvLyBSZXN0b3JlIHN0YXRlIGJlZm9yZSBjbGlwcGluZy5cblx0fSxcblxuXHRfdXBkYXRlUG9seTogZnVuY3Rpb24gKGxheWVyLCBjbG9zZWQpIHtcblx0XHRpZiAoIXRoaXMuX2RyYXdpbmcpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgaSwgaiwgbGVuMiwgcCxcblx0XHQgICAgcGFydHMgPSBsYXllci5fcGFydHMsXG5cdFx0ICAgIGxlbiA9IHBhcnRzLmxlbmd0aCxcblx0XHQgICAgY3R4ID0gdGhpcy5fY3R4O1xuXG5cdFx0aWYgKCFsZW4pIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9kcmF3bkxheWVyc1tsYXllci5fbGVhZmxldF9pZF0gPSBsYXllcjtcblxuXHRcdGN0eC5iZWdpblBhdGgoKTtcblxuXHRcdGlmIChjdHguc2V0TGluZURhc2gpIHtcblx0XHRcdGN0eC5zZXRMaW5lRGFzaChsYXllci5vcHRpb25zICYmIGxheWVyLm9wdGlvbnMuX2Rhc2hBcnJheSB8fCBbXSk7XG5cdFx0fVxuXG5cdFx0Zm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRmb3IgKGogPSAwLCBsZW4yID0gcGFydHNbaV0ubGVuZ3RoOyBqIDwgbGVuMjsgaisrKSB7XG5cdFx0XHRcdHAgPSBwYXJ0c1tpXVtqXTtcblx0XHRcdFx0Y3R4W2ogPyAnbGluZVRvJyA6ICdtb3ZlVG8nXShwLngsIHAueSk7XG5cdFx0XHR9XG5cdFx0XHRpZiAoY2xvc2VkKSB7XG5cdFx0XHRcdGN0eC5jbG9zZVBhdGgoKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLl9maWxsU3Ryb2tlKGN0eCwgbGF5ZXIpO1xuXG5cdFx0Ly8gVE9ETyBvcHRpbWl6YXRpb246IDEgZmlsbC9zdHJva2UgZm9yIGFsbCBmZWF0dXJlcyB3aXRoIGVxdWFsIHN0eWxlIGluc3RlYWQgb2YgMSBmb3IgZWFjaCBmZWF0dXJlXG5cdH0sXG5cblx0X3VwZGF0ZUNpcmNsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cblx0XHRpZiAoIXRoaXMuX2RyYXdpbmcgfHwgbGF5ZXIuX2VtcHR5KCkpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgcCA9IGxheWVyLl9wb2ludCxcblx0XHQgICAgY3R4ID0gdGhpcy5fY3R4LFxuXHRcdCAgICByID0gbGF5ZXIuX3JhZGl1cyxcblx0XHQgICAgcyA9IChsYXllci5fcmFkaXVzWSB8fCByKSAvIHI7XG5cblx0XHR0aGlzLl9kcmF3bkxheWVyc1tsYXllci5fbGVhZmxldF9pZF0gPSBsYXllcjtcblxuXHRcdGlmIChzICE9PSAxKSB7XG5cdFx0XHRjdHguc2F2ZSgpO1xuXHRcdFx0Y3R4LnNjYWxlKDEsIHMpO1xuXHRcdH1cblxuXHRcdGN0eC5iZWdpblBhdGgoKTtcblx0XHRjdHguYXJjKHAueCwgcC55IC8gcywgciwgMCwgTWF0aC5QSSAqIDIsIGZhbHNlKTtcblxuXHRcdGlmIChzICE9PSAxKSB7XG5cdFx0XHRjdHgucmVzdG9yZSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2ZpbGxTdHJva2UoY3R4LCBsYXllcik7XG5cdH0sXG5cblx0X2ZpbGxTdHJva2U6IGZ1bmN0aW9uIChjdHgsIGxheWVyKSB7XG5cdFx0dmFyIG9wdGlvbnMgPSBsYXllci5vcHRpb25zO1xuXG5cdFx0aWYgKG9wdGlvbnMuZmlsbCkge1xuXHRcdFx0Y3R4Lmdsb2JhbEFscGhhID0gb3B0aW9ucy5maWxsT3BhY2l0eTtcblx0XHRcdGN0eC5maWxsU3R5bGUgPSBvcHRpb25zLmZpbGxDb2xvciB8fCBvcHRpb25zLmNvbG9yO1xuXHRcdFx0Y3R4LmZpbGwob3B0aW9ucy5maWxsUnVsZSB8fCAnZXZlbm9kZCcpO1xuXHRcdH1cblxuXHRcdGlmIChvcHRpb25zLnN0cm9rZSAmJiBvcHRpb25zLndlaWdodCAhPT0gMCkge1xuXHRcdFx0Y3R4Lmdsb2JhbEFscGhhID0gb3B0aW9ucy5vcGFjaXR5O1xuXHRcdFx0Y3R4LmxpbmVXaWR0aCA9IG9wdGlvbnMud2VpZ2h0O1xuXHRcdFx0Y3R4LnN0cm9rZVN0eWxlID0gb3B0aW9ucy5jb2xvcjtcblx0XHRcdGN0eC5saW5lQ2FwID0gb3B0aW9ucy5saW5lQ2FwO1xuXHRcdFx0Y3R4LmxpbmVKb2luID0gb3B0aW9ucy5saW5lSm9pbjtcblx0XHRcdGN0eC5zdHJva2UoKTtcblx0XHR9XG5cdH0sXG5cblx0Ly8gQ2FudmFzIG9idmlvdXNseSBkb2Vzbid0IGhhdmUgbW91c2UgZXZlbnRzIGZvciBpbmRpdmlkdWFsIGRyYXduIG9iamVjdHMsXG5cdC8vIHNvIHdlIGVtdWxhdGUgdGhhdCBieSBjYWxjdWxhdGluZyB3aGF0J3MgdW5kZXIgdGhlIG1vdXNlIG9uIG1vdXNlbW92ZS9jbGljayBtYW51YWxseVxuXG5cdF9vbkNsaWNrOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBwb2ludCA9IHRoaXMuX21hcC5tb3VzZUV2ZW50VG9MYXllclBvaW50KGUpLCBsYXllciwgY2xpY2tlZExheWVyO1xuXG5cdFx0Zm9yICh2YXIgb3JkZXIgPSB0aGlzLl9kcmF3Rmlyc3Q7IG9yZGVyOyBvcmRlciA9IG9yZGVyLm5leHQpIHtcblx0XHRcdGxheWVyID0gb3JkZXIubGF5ZXI7XG5cdFx0XHRpZiAobGF5ZXIub3B0aW9ucy5pbnRlcmFjdGl2ZSAmJiBsYXllci5fY29udGFpbnNQb2ludChwb2ludCkgJiYgIXRoaXMuX21hcC5fZHJhZ2dhYmxlTW92ZWQobGF5ZXIpKSB7XG5cdFx0XHRcdGNsaWNrZWRMYXllciA9IGxheWVyO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAoY2xpY2tlZExheWVyKSAge1xuXHRcdFx0TC5Eb21FdmVudC5fZmFrZVN0b3AoZSk7XG5cdFx0XHR0aGlzLl9maXJlRXZlbnQoW2NsaWNrZWRMYXllcl0sIGUpO1xuXHRcdH1cblx0fSxcblxuXHRfb25Nb3VzZU1vdmU6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXAgfHwgdGhpcy5fbWFwLmRyYWdnaW5nLm1vdmluZygpIHx8IHRoaXMuX21hcC5fYW5pbWF0aW5nWm9vbSkgeyByZXR1cm47IH1cblxuXHRcdHZhciBwb2ludCA9IHRoaXMuX21hcC5tb3VzZUV2ZW50VG9MYXllclBvaW50KGUpO1xuXHRcdHRoaXMuX2hhbmRsZU1vdXNlSG92ZXIoZSwgcG9pbnQpO1xuXHR9LFxuXG5cblx0X2hhbmRsZU1vdXNlT3V0OiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBsYXllciA9IHRoaXMuX2hvdmVyZWRMYXllcjtcblx0XHRpZiAobGF5ZXIpIHtcblx0XHRcdC8vIGlmIHdlJ3JlIGxlYXZpbmcgdGhlIGxheWVyLCBmaXJlIG1vdXNlb3V0XG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1pbnRlcmFjdGl2ZScpO1xuXHRcdFx0dGhpcy5fZmlyZUV2ZW50KFtsYXllcl0sIGUsICdtb3VzZW91dCcpO1xuXHRcdFx0dGhpcy5faG92ZXJlZExheWVyID0gbnVsbDtcblx0XHR9XG5cdH0sXG5cblx0X2hhbmRsZU1vdXNlSG92ZXI6IGZ1bmN0aW9uIChlLCBwb2ludCkge1xuXHRcdHZhciBsYXllciwgY2FuZGlkYXRlSG92ZXJlZExheWVyO1xuXG5cdFx0Zm9yICh2YXIgb3JkZXIgPSB0aGlzLl9kcmF3Rmlyc3Q7IG9yZGVyOyBvcmRlciA9IG9yZGVyLm5leHQpIHtcblx0XHRcdGxheWVyID0gb3JkZXIubGF5ZXI7XG5cdFx0XHRpZiAobGF5ZXIub3B0aW9ucy5pbnRlcmFjdGl2ZSAmJiBsYXllci5fY29udGFpbnNQb2ludChwb2ludCkpIHtcblx0XHRcdFx0Y2FuZGlkYXRlSG92ZXJlZExheWVyID0gbGF5ZXI7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKGNhbmRpZGF0ZUhvdmVyZWRMYXllciAhPT0gdGhpcy5faG92ZXJlZExheWVyKSB7XG5cdFx0XHR0aGlzLl9oYW5kbGVNb3VzZU91dChlKTtcblxuXHRcdFx0aWYgKGNhbmRpZGF0ZUhvdmVyZWRMYXllcikge1xuXHRcdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1pbnRlcmFjdGl2ZScpOyAvLyBjaGFuZ2UgY3Vyc29yXG5cdFx0XHRcdHRoaXMuX2ZpcmVFdmVudChbY2FuZGlkYXRlSG92ZXJlZExheWVyXSwgZSwgJ21vdXNlb3ZlcicpO1xuXHRcdFx0XHR0aGlzLl9ob3ZlcmVkTGF5ZXIgPSBjYW5kaWRhdGVIb3ZlcmVkTGF5ZXI7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMuX2hvdmVyZWRMYXllcikge1xuXHRcdFx0dGhpcy5fZmlyZUV2ZW50KFt0aGlzLl9ob3ZlcmVkTGF5ZXJdLCBlKTtcblx0XHR9XG5cdH0sXG5cblx0X2ZpcmVFdmVudDogZnVuY3Rpb24gKGxheWVycywgZSwgdHlwZSkge1xuXHRcdHRoaXMuX21hcC5fZmlyZURPTUV2ZW50KGUsIHR5cGUgfHwgZS50eXBlLCBsYXllcnMpO1xuXHR9LFxuXG5cdF9icmluZ1RvRnJvbnQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBvcmRlciA9IGxheWVyLl9vcmRlcjtcblx0XHR2YXIgbmV4dCA9IG9yZGVyLm5leHQ7XG5cdFx0dmFyIHByZXYgPSBvcmRlci5wcmV2O1xuXG5cdFx0aWYgKG5leHQpIHtcblx0XHRcdG5leHQucHJldiA9IHByZXY7XG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIEFscmVhZHkgbGFzdFxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRpZiAocHJldikge1xuXHRcdFx0cHJldi5uZXh0ID0gbmV4dDtcblx0XHR9IGVsc2UgaWYgKG5leHQpIHtcblx0XHRcdC8vIFVwZGF0ZSBmaXJzdCBlbnRyeSB1bmxlc3MgdGhpcyBpcyB0aGVcblx0XHRcdC8vIHNpZ25sZSBlbnRyeVxuXHRcdFx0dGhpcy5fZHJhd0ZpcnN0ID0gbmV4dDtcblx0XHR9XG5cblx0XHRvcmRlci5wcmV2ID0gdGhpcy5fZHJhd0xhc3Q7XG5cdFx0dGhpcy5fZHJhd0xhc3QubmV4dCA9IG9yZGVyO1xuXG5cdFx0b3JkZXIubmV4dCA9IG51bGw7XG5cdFx0dGhpcy5fZHJhd0xhc3QgPSBvcmRlcjtcblxuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF9icmluZ1RvQmFjazogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyO1xuXHRcdHZhciBuZXh0ID0gb3JkZXIubmV4dDtcblx0XHR2YXIgcHJldiA9IG9yZGVyLnByZXY7XG5cblx0XHRpZiAocHJldikge1xuXHRcdFx0cHJldi5uZXh0ID0gbmV4dDtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gQWxyZWFkeSBmaXJzdFxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRpZiAobmV4dCkge1xuXHRcdFx0bmV4dC5wcmV2ID0gcHJldjtcblx0XHR9IGVsc2UgaWYgKHByZXYpIHtcblx0XHRcdC8vIFVwZGF0ZSBsYXN0IGVudHJ5IHVubGVzcyB0aGlzIGlzIHRoZVxuXHRcdFx0Ly8gc2lnbmxlIGVudHJ5XG5cdFx0XHR0aGlzLl9kcmF3TGFzdCA9IHByZXY7XG5cdFx0fVxuXG5cdFx0b3JkZXIucHJldiA9IG51bGw7XG5cblx0XHRvcmRlci5uZXh0ID0gdGhpcy5fZHJhd0ZpcnN0O1xuXHRcdHRoaXMuX2RyYXdGaXJzdC5wcmV2ID0gb3JkZXI7XG5cdFx0dGhpcy5fZHJhd0ZpcnN0ID0gb3JkZXI7XG5cblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fVxufSk7XG5cbi8vIEBuYW1lc3BhY2UgQnJvd3NlcjsgQHByb3BlcnR5IGNhbnZhczogQm9vbGVhblxuLy8gYHRydWVgIHdoZW4gdGhlIGJyb3dzZXIgc3VwcG9ydHMgW2A8Y2FudmFzPmBdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9DYW52YXNfQVBJKS5cbkwuQnJvd3Nlci5jYW52YXMgPSAoZnVuY3Rpb24gKCkge1xuXHRyZXR1cm4gISFkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKS5nZXRDb250ZXh0O1xufSgpKTtcblxuLy8gQG5hbWVzcGFjZSBDYW52YXNcbi8vIEBmYWN0b3J5IEwuY2FudmFzKG9wdGlvbnM/OiBSZW5kZXJlciBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhIENhbnZhcyByZW5kZXJlciB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuTC5jYW52YXMgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gTC5Ccm93c2VyLmNhbnZhcyA/IG5ldyBMLkNhbnZhcyhvcHRpb25zKSA6IG51bGw7XG59O1xuXG5MLlBvbHlsaW5lLnByb3RvdHlwZS5fY29udGFpbnNQb2ludCA9IGZ1bmN0aW9uIChwLCBjbG9zZWQpIHtcblx0dmFyIGksIGosIGssIGxlbiwgbGVuMiwgcGFydCxcblx0ICAgIHcgPSB0aGlzLl9jbGlja1RvbGVyYW5jZSgpO1xuXG5cdGlmICghdGhpcy5fcHhCb3VuZHMuY29udGFpbnMocCkpIHsgcmV0dXJuIGZhbHNlOyB9XG5cblx0Ly8gaGl0IGRldGVjdGlvbiBmb3IgcG9seWxpbmVzXG5cdGZvciAoaSA9IDAsIGxlbiA9IHRoaXMuX3BhcnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0cGFydCA9IHRoaXMuX3BhcnRzW2ldO1xuXG5cdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBhcnQubGVuZ3RoLCBrID0gbGVuMiAtIDE7IGogPCBsZW4yOyBrID0gaisrKSB7XG5cdFx0XHRpZiAoIWNsb3NlZCAmJiAoaiA9PT0gMCkpIHsgY29udGludWU7IH1cblxuXHRcdFx0aWYgKEwuTGluZVV0aWwucG9pbnRUb1NlZ21lbnREaXN0YW5jZShwLCBwYXJ0W2tdLCBwYXJ0W2pdKSA8PSB3KSB7XG5cdFx0XHRcdHJldHVybiB0cnVlO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXHRyZXR1cm4gZmFsc2U7XG59O1xuXG5MLlBvbHlnb24ucHJvdG90eXBlLl9jb250YWluc1BvaW50ID0gZnVuY3Rpb24gKHApIHtcblx0dmFyIGluc2lkZSA9IGZhbHNlLFxuXHQgICAgcGFydCwgcDEsIHAyLCBpLCBqLCBrLCBsZW4sIGxlbjI7XG5cblx0aWYgKCF0aGlzLl9weEJvdW5kcy5jb250YWlucyhwKSkgeyByZXR1cm4gZmFsc2U7IH1cblxuXHQvLyByYXkgY2FzdGluZyBhbGdvcml0aG0gZm9yIGRldGVjdGluZyBpZiBwb2ludCBpcyBpbiBwb2x5Z29uXG5cdGZvciAoaSA9IDAsIGxlbiA9IHRoaXMuX3BhcnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0cGFydCA9IHRoaXMuX3BhcnRzW2ldO1xuXG5cdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBhcnQubGVuZ3RoLCBrID0gbGVuMiAtIDE7IGogPCBsZW4yOyBrID0gaisrKSB7XG5cdFx0XHRwMSA9IHBhcnRbal07XG5cdFx0XHRwMiA9IHBhcnRba107XG5cblx0XHRcdGlmICgoKHAxLnkgPiBwLnkpICE9PSAocDIueSA+IHAueSkpICYmIChwLnggPCAocDIueCAtIHAxLngpICogKHAueSAtIHAxLnkpIC8gKHAyLnkgLSBwMS55KSArIHAxLngpKSB7XG5cdFx0XHRcdGluc2lkZSA9ICFpbnNpZGU7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0Ly8gYWxzbyBjaGVjayBpZiBpdCdzIG9uIHBvbHlnb24gc3Ryb2tlXG5cdHJldHVybiBpbnNpZGUgfHwgTC5Qb2x5bGluZS5wcm90b3R5cGUuX2NvbnRhaW5zUG9pbnQuY2FsbCh0aGlzLCBwLCB0cnVlKTtcbn07XG5cbkwuQ2lyY2xlTWFya2VyLnByb3RvdHlwZS5fY29udGFpbnNQb2ludCA9IGZ1bmN0aW9uIChwKSB7XG5cdHJldHVybiBwLmRpc3RhbmNlVG8odGhpcy5fcG9pbnQpIDw9IHRoaXMuX3JhZGl1cyArIHRoaXMuX2NsaWNrVG9sZXJhbmNlKCk7XG59O1xuXG5cblxuLypcclxuICogQGNsYXNzIEdlb0pTT05cclxuICogQGFrYSBMLkdlb0pTT05cclxuICogQGluaGVyaXRzIEZlYXR1cmVHcm91cFxyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgR2VvSlNPTiBvYmplY3Qgb3IgYW4gYXJyYXkgb2YgR2VvSlNPTiBvYmplY3RzLiBBbGxvd3MgeW91IHRvIHBhcnNlXHJcbiAqIEdlb0pTT04gZGF0YSBhbmQgZGlzcGxheSBpdCBvbiB0aGUgbWFwLiBFeHRlbmRzIGBGZWF0dXJlR3JvdXBgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLmdlb0pTT04oZGF0YSwge1xyXG4gKiBcdHN0eWxlOiBmdW5jdGlvbiAoZmVhdHVyZSkge1xyXG4gKiBcdFx0cmV0dXJuIHtjb2xvcjogZmVhdHVyZS5wcm9wZXJ0aWVzLmNvbG9yfTtcclxuICogXHR9XHJcbiAqIH0pLmJpbmRQb3B1cChmdW5jdGlvbiAobGF5ZXIpIHtcclxuICogXHRyZXR1cm4gbGF5ZXIuZmVhdHVyZS5wcm9wZXJ0aWVzLmRlc2NyaXB0aW9uO1xyXG4gKiB9KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5MLkdlb0pTT04gPSBMLkZlYXR1cmVHcm91cC5leHRlbmQoe1xyXG5cclxuXHQvKiBAc2VjdGlvblxyXG5cdCAqIEBha2EgR2VvSlNPTiBvcHRpb25zXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHBvaW50VG9MYXllcjogRnVuY3Rpb24gPSAqXHJcblx0ICogQSBgRnVuY3Rpb25gIGRlZmluaW5nIGhvdyBHZW9KU09OIHBvaW50cyBzcGF3biBMZWFmbGV0IGxheWVycy4gSXQgaXMgaW50ZXJuYWxseVxyXG5cdCAqIGNhbGxlZCB3aGVuIGRhdGEgaXMgYWRkZWQsIHBhc3NpbmcgdGhlIEdlb0pTT04gcG9pbnQgZmVhdHVyZSBhbmQgaXRzIGBMYXRMbmdgLlxyXG5cdCAqIFRoZSBkZWZhdWx0IGlzIHRvIHNwYXduIGEgZGVmYXVsdCBgTWFya2VyYDpcclxuXHQgKiBgYGBqc1xyXG5cdCAqIGZ1bmN0aW9uKGdlb0pzb25Qb2ludCwgbGF0bG5nKSB7XHJcblx0ICogXHRyZXR1cm4gTC5tYXJrZXIobGF0bG5nKTtcclxuXHQgKiB9XHJcblx0ICogYGBgXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHN0eWxlOiBGdW5jdGlvbiA9ICpcclxuXHQgKiBBIGBGdW5jdGlvbmAgZGVmaW5pbmcgdGhlIGBQYXRoIG9wdGlvbnNgIGZvciBzdHlsaW5nIEdlb0pTT04gbGluZXMgYW5kIHBvbHlnb25zLFxyXG5cdCAqIGNhbGxlZCBpbnRlcm5hbGx5IHdoZW4gZGF0YSBpcyBhZGRlZC5cclxuXHQgKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0byBub3Qgb3ZlcnJpZGUgYW55IGRlZmF1bHRzOlxyXG5cdCAqIGBgYGpzXHJcblx0ICogZnVuY3Rpb24gKGdlb0pzb25GZWF0dXJlKSB7XHJcblx0ICogXHRyZXR1cm4ge31cclxuXHQgKiB9XHJcblx0ICogYGBgXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIG9uRWFjaEZlYXR1cmU6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCB0aGF0IHdpbGwgYmUgY2FsbGVkIG9uY2UgZm9yIGVhY2ggY3JlYXRlZCBgRmVhdHVyZWAsIGFmdGVyIGl0IGhhc1xyXG5cdCAqIGJlZW4gY3JlYXRlZCBhbmQgc3R5bGVkLiBVc2VmdWwgZm9yIGF0dGFjaGluZyBldmVudHMgYW5kIHBvcHVwcyB0byBmZWF0dXJlcy5cclxuXHQgKiBUaGUgZGVmYXVsdCBpcyB0byBkbyBub3RoaW5nIHdpdGggdGhlIG5ld2x5IGNyZWF0ZWQgbGF5ZXJzOlxyXG5cdCAqIGBgYGpzXHJcblx0ICogZnVuY3Rpb24gKGZlYXR1cmUsIGxheWVyKSB7fVxyXG5cdCAqIGBgYFxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBmaWx0ZXI6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCB0aGF0IHdpbGwgYmUgdXNlZCB0byBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIGEgZmVhdHVyZSBvciBub3QuXHJcblx0ICogVGhlIGRlZmF1bHQgaXMgdG8gaW5jbHVkZSBhbGwgZmVhdHVyZXM6XHJcblx0ICogYGBganNcclxuXHQgKiBmdW5jdGlvbiAoZ2VvSnNvbkZlYXR1cmUpIHtcclxuXHQgKiBcdHJldHVybiB0cnVlO1xyXG5cdCAqIH1cclxuXHQgKiBgYGBcclxuXHQgKiBOb3RlOiBkeW5hbWljYWxseSBjaGFuZ2luZyB0aGUgYGZpbHRlcmAgb3B0aW9uIHdpbGwgaGF2ZSBlZmZlY3Qgb25seSBvbiBuZXdseVxyXG5cdCAqIGFkZGVkIGRhdGEuIEl0IHdpbGwgX25vdF8gcmUtZXZhbHVhdGUgYWxyZWFkeSBpbmNsdWRlZCBmZWF0dXJlcy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gY29vcmRzVG9MYXRMbmc6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCB0aGF0IHdpbGwgYmUgdXNlZCBmb3IgY29udmVydGluZyBHZW9KU09OIGNvb3JkaW5hdGVzIHRvIGBMYXRMbmdgcy5cclxuXHQgKiBUaGUgZGVmYXVsdCBpcyB0aGUgYGNvb3Jkc1RvTGF0TG5nYCBzdGF0aWMgbWV0aG9kLlxyXG5cdCAqL1xyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAoZ2VvanNvbiwgb3B0aW9ucykge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX2xheWVycyA9IHt9O1xyXG5cclxuXHRcdGlmIChnZW9qc29uKSB7XHJcblx0XHRcdHRoaXMuYWRkRGF0YShnZW9qc29uKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZERhdGEoIDxHZW9KU09OPiBkYXRhICk6IHRoaXNcclxuXHQvLyBBZGRzIGEgR2VvSlNPTiBvYmplY3QgdG8gdGhlIGxheWVyLlxyXG5cdGFkZERhdGE6IGZ1bmN0aW9uIChnZW9qc29uKSB7XHJcblx0XHR2YXIgZmVhdHVyZXMgPSBMLlV0aWwuaXNBcnJheShnZW9qc29uKSA/IGdlb2pzb24gOiBnZW9qc29uLmZlYXR1cmVzLFxyXG5cdFx0ICAgIGksIGxlbiwgZmVhdHVyZTtcclxuXHJcblx0XHRpZiAoZmVhdHVyZXMpIHtcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gZmVhdHVyZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHQvLyBvbmx5IGFkZCB0aGlzIGlmIGdlb21ldHJ5IG9yIGdlb21ldHJpZXMgYXJlIHNldCBhbmQgbm90IG51bGxcclxuXHRcdFx0XHRmZWF0dXJlID0gZmVhdHVyZXNbaV07XHJcblx0XHRcdFx0aWYgKGZlYXR1cmUuZ2VvbWV0cmllcyB8fCBmZWF0dXJlLmdlb21ldHJ5IHx8IGZlYXR1cmUuZmVhdHVyZXMgfHwgZmVhdHVyZS5jb29yZGluYXRlcykge1xyXG5cdFx0XHRcdFx0dGhpcy5hZGREYXRhKGZlYXR1cmUpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcclxuXHJcblx0XHRpZiAob3B0aW9ucy5maWx0ZXIgJiYgIW9wdGlvbnMuZmlsdGVyKGdlb2pzb24pKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0dmFyIGxheWVyID0gTC5HZW9KU09OLmdlb21ldHJ5VG9MYXllcihnZW9qc29uLCBvcHRpb25zKTtcclxuXHRcdGlmICghbGF5ZXIpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblx0XHRsYXllci5mZWF0dXJlID0gTC5HZW9KU09OLmFzRmVhdHVyZShnZW9qc29uKTtcclxuXHJcblx0XHRsYXllci5kZWZhdWx0T3B0aW9ucyA9IGxheWVyLm9wdGlvbnM7XHJcblx0XHR0aGlzLnJlc2V0U3R5bGUobGF5ZXIpO1xyXG5cclxuXHRcdGlmIChvcHRpb25zLm9uRWFjaEZlYXR1cmUpIHtcclxuXHRcdFx0b3B0aW9ucy5vbkVhY2hGZWF0dXJlKGdlb2pzb24sIGxheWVyKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcy5hZGRMYXllcihsYXllcik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZXNldFN0eWxlKCA8UGF0aD4gbGF5ZXIgKTogdGhpc1xyXG5cdC8vIFJlc2V0cyB0aGUgZ2l2ZW4gdmVjdG9yIGxheWVyJ3Mgc3R5bGUgdG8gdGhlIG9yaWdpbmFsIEdlb0pTT04gc3R5bGUsIHVzZWZ1bCBmb3IgcmVzZXR0aW5nIHN0eWxlIGFmdGVyIGhvdmVyIGV2ZW50cy5cclxuXHRyZXNldFN0eWxlOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdC8vIHJlc2V0IGFueSBjdXN0b20gc3R5bGVzXHJcblx0XHRsYXllci5vcHRpb25zID0gTC5VdGlsLmV4dGVuZCh7fSwgbGF5ZXIuZGVmYXVsdE9wdGlvbnMpO1xyXG5cdFx0dGhpcy5fc2V0TGF5ZXJTdHlsZShsYXllciwgdGhpcy5vcHRpb25zLnN0eWxlKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0U3R5bGUoIDxGdW5jdGlvbj4gc3R5bGUgKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgc3R5bGVzIG9mIEdlb0pTT04gdmVjdG9yIGxheWVycyB3aXRoIHRoZSBnaXZlbiBzdHlsZSBmdW5jdGlvbi5cclxuXHRzZXRTdHlsZTogZnVuY3Rpb24gKHN0eWxlKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5lYWNoTGF5ZXIoZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRcdHRoaXMuX3NldExheWVyU3R5bGUobGF5ZXIsIHN0eWxlKTtcclxuXHRcdH0sIHRoaXMpO1xyXG5cdH0sXHJcblxyXG5cdF9zZXRMYXllclN0eWxlOiBmdW5jdGlvbiAobGF5ZXIsIHN0eWxlKSB7XHJcblx0XHRpZiAodHlwZW9mIHN0eWxlID09PSAnZnVuY3Rpb24nKSB7XHJcblx0XHRcdHN0eWxlID0gc3R5bGUobGF5ZXIuZmVhdHVyZSk7XHJcblx0XHR9XHJcblx0XHRpZiAobGF5ZXIuc2V0U3R5bGUpIHtcclxuXHRcdFx0bGF5ZXIuc2V0U3R5bGUoc3R5bGUpO1xyXG5cdFx0fVxyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAc2VjdGlvblxyXG4vLyBUaGVyZSBhcmUgc2V2ZXJhbCBzdGF0aWMgZnVuY3Rpb25zIHdoaWNoIGNhbiBiZSBjYWxsZWQgd2l0aG91dCBpbnN0YW50aWF0aW5nIEwuR2VvSlNPTjpcclxuTC5leHRlbmQoTC5HZW9KU09OLCB7XHJcblx0Ly8gQGZ1bmN0aW9uIGdlb21ldHJ5VG9MYXllcihmZWF0dXJlRGF0YTogT2JqZWN0LCBvcHRpb25zPzogR2VvSlNPTiBvcHRpb25zKTogTGF5ZXJcclxuXHQvLyBDcmVhdGVzIGEgYExheWVyYCBmcm9tIGEgZ2l2ZW4gR2VvSlNPTiBmZWF0dXJlLiBDYW4gdXNlIGEgY3VzdG9tXHJcblx0Ly8gW2Bwb2ludFRvTGF5ZXJgXSgjZ2VvanNvbi1wb2ludHRvbGF5ZXIpIGFuZC9vciBbYGNvb3Jkc1RvTGF0TG5nYF0oI2dlb2pzb24tY29vcmRzdG9sYXRsbmcpXHJcblx0Ly8gZnVuY3Rpb25zIGlmIHByb3ZpZGVkIGFzIG9wdGlvbnMuXHJcblx0Z2VvbWV0cnlUb0xheWVyOiBmdW5jdGlvbiAoZ2VvanNvbiwgb3B0aW9ucykge1xyXG5cclxuXHRcdHZhciBnZW9tZXRyeSA9IGdlb2pzb24udHlwZSA9PT0gJ0ZlYXR1cmUnID8gZ2VvanNvbi5nZW9tZXRyeSA6IGdlb2pzb24sXHJcblx0XHQgICAgY29vcmRzID0gZ2VvbWV0cnkgPyBnZW9tZXRyeS5jb29yZGluYXRlcyA6IG51bGwsXHJcblx0XHQgICAgbGF5ZXJzID0gW10sXHJcblx0XHQgICAgcG9pbnRUb0xheWVyID0gb3B0aW9ucyAmJiBvcHRpb25zLnBvaW50VG9MYXllcixcclxuXHRcdCAgICBjb29yZHNUb0xhdExuZyA9IG9wdGlvbnMgJiYgb3B0aW9ucy5jb29yZHNUb0xhdExuZyB8fCB0aGlzLmNvb3Jkc1RvTGF0TG5nLFxyXG5cdFx0ICAgIGxhdGxuZywgbGF0bG5ncywgaSwgbGVuO1xyXG5cclxuXHRcdGlmICghY29vcmRzICYmICFnZW9tZXRyeSkge1xyXG5cdFx0XHRyZXR1cm4gbnVsbDtcclxuXHRcdH1cclxuXHJcblx0XHRzd2l0Y2ggKGdlb21ldHJ5LnR5cGUpIHtcclxuXHRcdGNhc2UgJ1BvaW50JzpcclxuXHRcdFx0bGF0bG5nID0gY29vcmRzVG9MYXRMbmcoY29vcmRzKTtcclxuXHRcdFx0cmV0dXJuIHBvaW50VG9MYXllciA/IHBvaW50VG9MYXllcihnZW9qc29uLCBsYXRsbmcpIDogbmV3IEwuTWFya2VyKGxhdGxuZyk7XHJcblxyXG5cdFx0Y2FzZSAnTXVsdGlQb2ludCc6XHJcblx0XHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvb3Jkcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdGxhdGxuZyA9IGNvb3Jkc1RvTGF0TG5nKGNvb3Jkc1tpXSk7XHJcblx0XHRcdFx0bGF5ZXJzLnB1c2gocG9pbnRUb0xheWVyID8gcG9pbnRUb0xheWVyKGdlb2pzb24sIGxhdGxuZykgOiBuZXcgTC5NYXJrZXIobGF0bG5nKSk7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIG5ldyBMLkZlYXR1cmVHcm91cChsYXllcnMpO1xyXG5cclxuXHRcdGNhc2UgJ0xpbmVTdHJpbmcnOlxyXG5cdFx0Y2FzZSAnTXVsdGlMaW5lU3RyaW5nJzpcclxuXHRcdFx0bGF0bG5ncyA9IHRoaXMuY29vcmRzVG9MYXRMbmdzKGNvb3JkcywgZ2VvbWV0cnkudHlwZSA9PT0gJ0xpbmVTdHJpbmcnID8gMCA6IDEsIGNvb3Jkc1RvTGF0TG5nKTtcclxuXHRcdFx0cmV0dXJuIG5ldyBMLlBvbHlsaW5lKGxhdGxuZ3MsIG9wdGlvbnMpO1xyXG5cclxuXHRcdGNhc2UgJ1BvbHlnb24nOlxyXG5cdFx0Y2FzZSAnTXVsdGlQb2x5Z29uJzpcclxuXHRcdFx0bGF0bG5ncyA9IHRoaXMuY29vcmRzVG9MYXRMbmdzKGNvb3JkcywgZ2VvbWV0cnkudHlwZSA9PT0gJ1BvbHlnb24nID8gMSA6IDIsIGNvb3Jkc1RvTGF0TG5nKTtcclxuXHRcdFx0cmV0dXJuIG5ldyBMLlBvbHlnb24obGF0bG5ncywgb3B0aW9ucyk7XHJcblxyXG5cdFx0Y2FzZSAnR2VvbWV0cnlDb2xsZWN0aW9uJzpcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gZ2VvbWV0cnkuZ2VvbWV0cmllcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHZhciBsYXllciA9IHRoaXMuZ2VvbWV0cnlUb0xheWVyKHtcclxuXHRcdFx0XHRcdGdlb21ldHJ5OiBnZW9tZXRyeS5nZW9tZXRyaWVzW2ldLFxyXG5cdFx0XHRcdFx0dHlwZTogJ0ZlYXR1cmUnLFxyXG5cdFx0XHRcdFx0cHJvcGVydGllczogZ2VvanNvbi5wcm9wZXJ0aWVzXHJcblx0XHRcdFx0fSwgb3B0aW9ucyk7XHJcblxyXG5cdFx0XHRcdGlmIChsYXllcikge1xyXG5cdFx0XHRcdFx0bGF5ZXJzLnB1c2gobGF5ZXIpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gbmV3IEwuRmVhdHVyZUdyb3VwKGxheWVycyk7XHJcblxyXG5cdFx0ZGVmYXVsdDpcclxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIEdlb0pTT04gb2JqZWN0LicpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBjb29yZHNUb0xhdExuZyhjb29yZHM6IEFycmF5KTogTGF0TG5nXHJcblx0Ly8gQ3JlYXRlcyBhIGBMYXRMbmdgIG9iamVjdCBmcm9tIGFuIGFycmF5IG9mIDIgbnVtYmVycyAobG9uZ2l0dWRlLCBsYXRpdHVkZSlcclxuXHQvLyBvciAzIG51bWJlcnMgKGxvbmdpdHVkZSwgbGF0aXR1ZGUsIGFsdGl0dWRlKSB1c2VkIGluIEdlb0pTT04gZm9yIHBvaW50cy5cclxuXHRjb29yZHNUb0xhdExuZzogZnVuY3Rpb24gKGNvb3Jkcykge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyhjb29yZHNbMV0sIGNvb3Jkc1swXSwgY29vcmRzWzJdKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY29vcmRzVG9MYXRMbmdzKGNvb3JkczogQXJyYXksIGxldmVsc0RlZXA/OiBOdW1iZXIsIGNvb3Jkc1RvTGF0TG5nPzogRnVuY3Rpb24pOiBBcnJheVxyXG5cdC8vIENyZWF0ZXMgYSBtdWx0aWRpbWVuc2lvbmFsIGFycmF5IG9mIGBMYXRMbmdgcyBmcm9tIGEgR2VvSlNPTiBjb29yZGluYXRlcyBhcnJheS5cclxuXHQvLyBgbGV2ZWxzRGVlcGAgc3BlY2lmaWVzIHRoZSBuZXN0aW5nIGxldmVsICgwIGlzIGZvciBhbiBhcnJheSBvZiBwb2ludHMsIDEgZm9yIGFuIGFycmF5IG9mIGFycmF5cyBvZiBwb2ludHMsIGV0Yy4sIDAgYnkgZGVmYXVsdCkuXHJcblx0Ly8gQ2FuIHVzZSBhIGN1c3RvbSBbYGNvb3Jkc1RvTGF0TG5nYF0oI2dlb2pzb24tY29vcmRzdG9sYXRsbmcpIGZ1bmN0aW9uLlxyXG5cdGNvb3Jkc1RvTGF0TG5nczogZnVuY3Rpb24gKGNvb3JkcywgbGV2ZWxzRGVlcCwgY29vcmRzVG9MYXRMbmcpIHtcclxuXHRcdHZhciBsYXRsbmdzID0gW107XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGNvb3Jkcy5sZW5ndGgsIGxhdGxuZzsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdGxhdGxuZyA9IGxldmVsc0RlZXAgP1xyXG5cdFx0XHQgICAgICAgIHRoaXMuY29vcmRzVG9MYXRMbmdzKGNvb3Jkc1tpXSwgbGV2ZWxzRGVlcCAtIDEsIGNvb3Jkc1RvTGF0TG5nKSA6XHJcblx0XHRcdCAgICAgICAgKGNvb3Jkc1RvTGF0TG5nIHx8IHRoaXMuY29vcmRzVG9MYXRMbmcpKGNvb3Jkc1tpXSk7XHJcblxyXG5cdFx0XHRsYXRsbmdzLnB1c2gobGF0bG5nKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gbGF0bG5ncztcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gbGF0TG5nVG9Db29yZHMobGF0bG5nOiBMYXRMbmcpOiBBcnJheVxyXG5cdC8vIFJldmVyc2Ugb2YgW2Bjb29yZHNUb0xhdExuZ2BdKCNnZW9qc29uLWNvb3Jkc3RvbGF0bG5nKVxyXG5cdGxhdExuZ1RvQ29vcmRzOiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gbGF0bG5nLmFsdCAhPT0gdW5kZWZpbmVkID9cclxuXHRcdFx0XHRbbGF0bG5nLmxuZywgbGF0bG5nLmxhdCwgbGF0bG5nLmFsdF0gOlxyXG5cdFx0XHRcdFtsYXRsbmcubG5nLCBsYXRsbmcubGF0XTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gbGF0TG5nc1RvQ29vcmRzKGxhdGxuZ3M6IEFycmF5LCBsZXZlbHNEZWVwPzogTnVtYmVyLCBjbG9zZWQ/OiBCb29sZWFuKTogQXJyYXlcclxuXHQvLyBSZXZlcnNlIG9mIFtgY29vcmRzVG9MYXRMbmdzYF0oI2dlb2pzb24tY29vcmRzdG9sYXRsbmdzKVxyXG5cdC8vIGBjbG9zZWRgIGRldGVybWluZXMgd2hldGhlciB0aGUgZmlyc3QgcG9pbnQgc2hvdWxkIGJlIGFwcGVuZGVkIHRvIHRoZSBlbmQgb2YgdGhlIGFycmF5IHRvIGNsb3NlIHRoZSBmZWF0dXJlLCBvbmx5IHVzZWQgd2hlbiBgbGV2ZWxzRGVlcGAgaXMgMC4gRmFsc2UgYnkgZGVmYXVsdC5cclxuXHRsYXRMbmdzVG9Db29yZHM6IGZ1bmN0aW9uIChsYXRsbmdzLCBsZXZlbHNEZWVwLCBjbG9zZWQpIHtcclxuXHRcdHZhciBjb29yZHMgPSBbXTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF0bG5ncy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRjb29yZHMucHVzaChsZXZlbHNEZWVwID9cclxuXHRcdFx0XHRMLkdlb0pTT04ubGF0TG5nc1RvQ29vcmRzKGxhdGxuZ3NbaV0sIGxldmVsc0RlZXAgLSAxLCBjbG9zZWQpIDpcclxuXHRcdFx0XHRMLkdlb0pTT04ubGF0TG5nVG9Db29yZHMobGF0bG5nc1tpXSkpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghbGV2ZWxzRGVlcCAmJiBjbG9zZWQpIHtcclxuXHRcdFx0Y29vcmRzLnB1c2goY29vcmRzWzBdKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gY29vcmRzO1xyXG5cdH0sXHJcblxyXG5cdGdldEZlYXR1cmU6IGZ1bmN0aW9uIChsYXllciwgbmV3R2VvbWV0cnkpIHtcclxuXHRcdHJldHVybiBsYXllci5mZWF0dXJlID9cclxuXHRcdFx0XHRMLmV4dGVuZCh7fSwgbGF5ZXIuZmVhdHVyZSwge2dlb21ldHJ5OiBuZXdHZW9tZXRyeX0pIDpcclxuXHRcdFx0XHRMLkdlb0pTT04uYXNGZWF0dXJlKG5ld0dlb21ldHJ5KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gYXNGZWF0dXJlKGdlb2pzb246IE9iamVjdCk6IE9iamVjdFxyXG5cdC8vIE5vcm1hbGl6ZSBHZW9KU09OIGdlb21ldHJpZXMvZmVhdHVyZXMgaW50byBHZW9KU09OIGZlYXR1cmVzLlxyXG5cdGFzRmVhdHVyZTogZnVuY3Rpb24gKGdlb2pzb24pIHtcclxuXHRcdGlmIChnZW9qc29uLnR5cGUgPT09ICdGZWF0dXJlJyB8fCBnZW9qc29uLnR5cGUgPT09ICdGZWF0dXJlQ29sbGVjdGlvbicpIHtcclxuXHRcdFx0cmV0dXJuIGdlb2pzb247XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHtcclxuXHRcdFx0dHlwZTogJ0ZlYXR1cmUnLFxyXG5cdFx0XHRwcm9wZXJ0aWVzOiB7fSxcclxuXHRcdFx0Z2VvbWV0cnk6IGdlb2pzb25cclxuXHRcdH07XHJcblx0fVxyXG59KTtcclxuXHJcbnZhciBQb2ludFRvR2VvSlNPTiA9IHtcclxuXHR0b0dlb0pTT046IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBMLkdlb0pTT04uZ2V0RmVhdHVyZSh0aGlzLCB7XHJcblx0XHRcdHR5cGU6ICdQb2ludCcsXHJcblx0XHRcdGNvb3JkaW5hdGVzOiBMLkdlb0pTT04ubGF0TG5nVG9Db29yZHModGhpcy5nZXRMYXRMbmcoKSlcclxuXHRcdH0pO1xyXG5cdH1cclxufTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgTWFya2VyXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG4vLyBSZXR1cm5zIGEgW2BHZW9KU09OYF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9KU09OKSByZXByZXNlbnRhdGlvbiBvZiB0aGUgbWFya2VyIChhcyBhIEdlb0pTT04gYFBvaW50YCBGZWF0dXJlKS5cclxuTC5NYXJrZXIuaW5jbHVkZShQb2ludFRvR2VvSlNPTik7XHJcblxyXG4vLyBAbmFtZXNwYWNlIENpcmNsZU1hcmtlclxyXG4vLyBAbWV0aG9kIHRvR2VvSlNPTigpOiBPYmplY3RcclxuLy8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNpcmNsZSBtYXJrZXIgKGFzIGEgR2VvSlNPTiBgUG9pbnRgIEZlYXR1cmUpLlxyXG5MLkNpcmNsZS5pbmNsdWRlKFBvaW50VG9HZW9KU09OKTtcclxuTC5DaXJjbGVNYXJrZXIuaW5jbHVkZShQb2ludFRvR2VvSlNPTik7XHJcblxyXG5cclxuLy8gQG5hbWVzcGFjZSBQb2x5bGluZVxyXG4vLyBAbWV0aG9kIHRvR2VvSlNPTigpOiBPYmplY3RcclxuLy8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIHBvbHlsaW5lIChhcyBhIEdlb0pTT04gYExpbmVTdHJpbmdgIG9yIGBNdWx0aUxpbmVTdHJpbmdgIEZlYXR1cmUpLlxyXG5MLlBvbHlsaW5lLnByb3RvdHlwZS50b0dlb0pTT04gPSBmdW5jdGlvbiAoKSB7XHJcblx0dmFyIG11bHRpID0gIUwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5ncyk7XHJcblxyXG5cdHZhciBjb29yZHMgPSBMLkdlb0pTT04ubGF0TG5nc1RvQ29vcmRzKHRoaXMuX2xhdGxuZ3MsIG11bHRpID8gMSA6IDApO1xyXG5cclxuXHRyZXR1cm4gTC5HZW9KU09OLmdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0dHlwZTogKG11bHRpID8gJ011bHRpJyA6ICcnKSArICdMaW5lU3RyaW5nJyxcclxuXHRcdGNvb3JkaW5hdGVzOiBjb29yZHNcclxuXHR9KTtcclxufTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgUG9seWdvblxyXG4vLyBAbWV0aG9kIHRvR2VvSlNPTigpOiBPYmplY3RcclxuLy8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIHBvbHlnb24gKGFzIGEgR2VvSlNPTiBgUG9seWdvbmAgb3IgYE11bHRpUG9seWdvbmAgRmVhdHVyZSkuXHJcbkwuUG9seWdvbi5wcm90b3R5cGUudG9HZW9KU09OID0gZnVuY3Rpb24gKCkge1xyXG5cdHZhciBob2xlcyA9ICFMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3MpLFxyXG5cdCAgICBtdWx0aSA9IGhvbGVzICYmICFMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3NbMF0pO1xyXG5cclxuXHR2YXIgY29vcmRzID0gTC5HZW9KU09OLmxhdExuZ3NUb0Nvb3Jkcyh0aGlzLl9sYXRsbmdzLCBtdWx0aSA/IDIgOiBob2xlcyA/IDEgOiAwLCB0cnVlKTtcclxuXHJcblx0aWYgKCFob2xlcykge1xyXG5cdFx0Y29vcmRzID0gW2Nvb3Jkc107XHJcblx0fVxyXG5cclxuXHRyZXR1cm4gTC5HZW9KU09OLmdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0dHlwZTogKG11bHRpID8gJ011bHRpJyA6ICcnKSArICdQb2x5Z29uJyxcclxuXHRcdGNvb3JkaW5hdGVzOiBjb29yZHNcclxuXHR9KTtcclxufTtcclxuXHJcblxyXG4vLyBAbmFtZXNwYWNlIExheWVyR3JvdXBcclxuTC5MYXllckdyb3VwLmluY2x1ZGUoe1xyXG5cdHRvTXVsdGlQb2ludDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNvb3JkcyA9IFtdO1xyXG5cclxuXHRcdHRoaXMuZWFjaExheWVyKGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0XHRjb29yZHMucHVzaChsYXllci50b0dlb0pTT04oKS5nZW9tZXRyeS5jb29yZGluYXRlcyk7XHJcblx0XHR9KTtcclxuXHJcblx0XHRyZXR1cm4gTC5HZW9KU09OLmdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0XHR0eXBlOiAnTXVsdGlQb2ludCcsXHJcblx0XHRcdGNvb3JkaW5hdGVzOiBjb29yZHNcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG5cdC8vIFJldHVybnMgYSBbYEdlb0pTT05gXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dlb0pTT04pIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBsYXllciBncm91cCAoYXMgYSBHZW9KU09OIGBHZW9tZXRyeUNvbGxlY3Rpb25gKS5cclxuXHR0b0dlb0pTT046IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHR2YXIgdHlwZSA9IHRoaXMuZmVhdHVyZSAmJiB0aGlzLmZlYXR1cmUuZ2VvbWV0cnkgJiYgdGhpcy5mZWF0dXJlLmdlb21ldHJ5LnR5cGU7XHJcblxyXG5cdFx0aWYgKHR5cGUgPT09ICdNdWx0aVBvaW50Jykge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy50b011bHRpUG9pbnQoKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgaXNHZW9tZXRyeUNvbGxlY3Rpb24gPSB0eXBlID09PSAnR2VvbWV0cnlDb2xsZWN0aW9uJyxcclxuXHRcdCAgICBqc29ucyA9IFtdO1xyXG5cclxuXHRcdHRoaXMuZWFjaExheWVyKGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0XHRpZiAobGF5ZXIudG9HZW9KU09OKSB7XHJcblx0XHRcdFx0dmFyIGpzb24gPSBsYXllci50b0dlb0pTT04oKTtcclxuXHRcdFx0XHRqc29ucy5wdXNoKGlzR2VvbWV0cnlDb2xsZWN0aW9uID8ganNvbi5nZW9tZXRyeSA6IEwuR2VvSlNPTi5hc0ZlYXR1cmUoanNvbikpO1xyXG5cdFx0XHR9XHJcblx0XHR9KTtcclxuXHJcblx0XHRpZiAoaXNHZW9tZXRyeUNvbGxlY3Rpb24pIHtcclxuXHRcdFx0cmV0dXJuIEwuR2VvSlNPTi5nZXRGZWF0dXJlKHRoaXMsIHtcclxuXHRcdFx0XHRnZW9tZXRyaWVzOiBqc29ucyxcclxuXHRcdFx0XHR0eXBlOiAnR2VvbWV0cnlDb2xsZWN0aW9uJ1xyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4ge1xyXG5cdFx0XHR0eXBlOiAnRmVhdHVyZUNvbGxlY3Rpb24nLFxyXG5cdFx0XHRmZWF0dXJlczoganNvbnNcclxuXHRcdH07XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgR2VvSlNPTlxyXG4vLyBAZmFjdG9yeSBMLmdlb0pTT04oZ2VvanNvbj86IE9iamVjdCwgb3B0aW9ucz86IEdlb0pTT04gb3B0aW9ucylcclxuLy8gQ3JlYXRlcyBhIEdlb0pTT04gbGF5ZXIuIE9wdGlvbmFsbHkgYWNjZXB0cyBhbiBvYmplY3QgaW5cclxuLy8gW0dlb0pTT04gZm9ybWF0XShodHRwOi8vZ2VvanNvbi5vcmcvZ2VvanNvbi1zcGVjLmh0bWwpIHRvIGRpc3BsYXkgb24gdGhlIG1hcFxyXG4vLyAoeW91IGNhbiBhbHRlcm5hdGl2ZWx5IGFkZCBpdCBsYXRlciB3aXRoIGBhZGREYXRhYCBtZXRob2QpIGFuZCBhbiBgb3B0aW9uc2Agb2JqZWN0LlxyXG5MLmdlb0pTT04gPSBmdW5jdGlvbiAoZ2VvanNvbiwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5HZW9KU09OKGdlb2pzb24sIG9wdGlvbnMpO1xyXG59O1xyXG4vLyBCYWNrd2FyZCBjb21wYXRpYmlsaXR5LlxyXG5MLmdlb0pzb24gPSBMLmdlb0pTT047XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBEcmFnZ2FibGVcclxuICogQGFrYSBMLkRyYWdnYWJsZVxyXG4gKiBAaW5oZXJpdHMgRXZlbnRlZFxyXG4gKlxyXG4gKiBBIGNsYXNzIGZvciBtYWtpbmcgRE9NIGVsZW1lbnRzIGRyYWdnYWJsZSAoaW5jbHVkaW5nIHRvdWNoIHN1cHBvcnQpLlxyXG4gKiBVc2VkIGludGVybmFsbHkgZm9yIG1hcCBhbmQgbWFya2VyIGRyYWdnaW5nLiBPbmx5IHdvcmtzIGZvciBlbGVtZW50c1xyXG4gKiB0aGF0IHdlcmUgcG9zaXRpb25lZCB3aXRoIFtgTC5Eb21VdGlsLnNldFBvc2l0aW9uYF0oI2RvbXV0aWwtc2V0cG9zaXRpb24pLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgZHJhZ2dhYmxlID0gbmV3IEwuRHJhZ2dhYmxlKGVsZW1lbnRUb0RyYWcpO1xyXG4gKiBkcmFnZ2FibGUuZW5hYmxlKCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuRHJhZ2dhYmxlID0gTC5FdmVudGVkLmV4dGVuZCh7XHJcblxyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gY2xpY2tUb2xlcmFuY2U6IE51bWJlciA9IDNcclxuXHRcdC8vIFRoZSBtYXggbnVtYmVyIG9mIHBpeGVscyBhIHVzZXIgY2FuIHNoaWZ0IHRoZSBtb3VzZSBwb2ludGVyIGR1cmluZyBhIGNsaWNrXHJcblx0XHQvLyBmb3IgaXQgdG8gYmUgY29uc2lkZXJlZCBhIHZhbGlkIGNsaWNrIChhcyBvcHBvc2VkIHRvIGEgbW91c2UgZHJhZykuXHJcblx0XHRjbGlja1RvbGVyYW5jZTogM1xyXG5cdH0sXHJcblxyXG5cdHN0YXRpY3M6IHtcclxuXHRcdFNUQVJUOiBMLkJyb3dzZXIudG91Y2ggPyBbJ3RvdWNoc3RhcnQnLCAnbW91c2Vkb3duJ10gOiBbJ21vdXNlZG93biddLFxyXG5cdFx0RU5EOiB7XHJcblx0XHRcdG1vdXNlZG93bjogJ21vdXNldXAnLFxyXG5cdFx0XHR0b3VjaHN0YXJ0OiAndG91Y2hlbmQnLFxyXG5cdFx0XHRwb2ludGVyZG93bjogJ3RvdWNoZW5kJyxcclxuXHRcdFx0TVNQb2ludGVyRG93bjogJ3RvdWNoZW5kJ1xyXG5cdFx0fSxcclxuXHRcdE1PVkU6IHtcclxuXHRcdFx0bW91c2Vkb3duOiAnbW91c2Vtb3ZlJyxcclxuXHRcdFx0dG91Y2hzdGFydDogJ3RvdWNobW92ZScsXHJcblx0XHRcdHBvaW50ZXJkb3duOiAndG91Y2htb3ZlJyxcclxuXHRcdFx0TVNQb2ludGVyRG93bjogJ3RvdWNobW92ZSdcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAY29uc3RydWN0b3IgTC5EcmFnZ2FibGUoZWw6IEhUTUxFbGVtZW50LCBkcmFnSGFuZGxlPzogSFRNTEVsZW1lbnQsIHByZXZlbnRPdXRsaW5lOiBCb29sZWFuKVxyXG5cdC8vIENyZWF0ZXMgYSBgRHJhZ2dhYmxlYCBvYmplY3QgZm9yIG1vdmluZyBgZWxgIHdoZW4geW91IHN0YXJ0IGRyYWdnaW5nIHRoZSBgZHJhZ0hhbmRsZWAgZWxlbWVudCAoZXF1YWxzIGBlbGAgaXRzZWxmIGJ5IGRlZmF1bHQpLlxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChlbGVtZW50LCBkcmFnU3RhcnRUYXJnZXQsIHByZXZlbnRPdXRsaW5lKSB7XHJcblx0XHR0aGlzLl9lbGVtZW50ID0gZWxlbWVudDtcclxuXHRcdHRoaXMuX2RyYWdTdGFydFRhcmdldCA9IGRyYWdTdGFydFRhcmdldCB8fCBlbGVtZW50O1xyXG5cdFx0dGhpcy5fcHJldmVudE91dGxpbmUgPSBwcmV2ZW50T3V0bGluZTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVuYWJsZSgpXHJcblx0Ly8gRW5hYmxlcyB0aGUgZHJhZ2dpbmcgYWJpbGl0eVxyXG5cdGVuYWJsZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0TC5Eb21FdmVudC5vbih0aGlzLl9kcmFnU3RhcnRUYXJnZXQsIEwuRHJhZ2dhYmxlLlNUQVJULmpvaW4oJyAnKSwgdGhpcy5fb25Eb3duLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9lbmFibGVkID0gdHJ1ZTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGRpc2FibGUoKVxyXG5cdC8vIERpc2FibGVzIHRoZSBkcmFnZ2luZyBhYmlsaXR5XHJcblx0ZGlzYWJsZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9lbmFibGVkKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdC8vIElmIHdlJ3JlIGN1cnJlbnRseSBkcmFnZ2luZyB0aGlzIGRyYWdnYWJsZSxcclxuXHRcdC8vIGRpc2FibGluZyBpdCBjb3VudHMgYXMgZmlyc3QgZW5kaW5nIHRoZSBkcmFnLlxyXG5cdFx0aWYgKEwuRHJhZ2dhYmxlLl9kcmFnZ2luZyA9PT0gdGhpcykge1xyXG5cdFx0XHR0aGlzLmZpbmlzaERyYWcoKTtcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9kcmFnU3RhcnRUYXJnZXQsIEwuRHJhZ2dhYmxlLlNUQVJULmpvaW4oJyAnKSwgdGhpcy5fb25Eb3duLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9lbmFibGVkID0gZmFsc2U7XHJcblx0XHR0aGlzLl9tb3ZlZCA9IGZhbHNlO1xyXG5cdH0sXHJcblxyXG5cdF9vbkRvd246IGZ1bmN0aW9uIChlKSB7XHJcblx0XHQvLyBJZ25vcmUgc2ltdWxhdGVkIGV2ZW50cywgc2luY2Ugd2UgaGFuZGxlIGJvdGggdG91Y2ggYW5kXHJcblx0XHQvLyBtb3VzZSBleHBsaWNpdGx5OyBvdGhlcndpc2Ugd2UgcmlzayBnZXR0aW5nIGR1cGxpY2F0ZXMgb2ZcclxuXHRcdC8vIHRvdWNoIGV2ZW50cywgc2VlICM0MzE1LlxyXG5cdFx0Ly8gQWxzbyBpZ25vcmUgdGhlIGV2ZW50IGlmIGRpc2FibGVkOyB0aGlzIGhhcHBlbnMgaW4gSUUxMVxyXG5cdFx0Ly8gdW5kZXIgc29tZSBjaXJjdW1zdGFuY2VzLCBzZWUgIzM2NjYuXHJcblx0XHRpZiAoZS5fc2ltdWxhdGVkIHx8ICF0aGlzLl9lbmFibGVkKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XHJcblxyXG5cdFx0aWYgKEwuRG9tVXRpbC5oYXNDbGFzcyh0aGlzLl9lbGVtZW50LCAnbGVhZmxldC16b29tLWFuaW0nKSkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAoTC5EcmFnZ2FibGUuX2RyYWdnaW5nIHx8IGUuc2hpZnRLZXkgfHwgKChlLndoaWNoICE9PSAxKSAmJiAoZS5idXR0b24gIT09IDEpICYmICFlLnRvdWNoZXMpKSB7IHJldHVybjsgfVxyXG5cdFx0TC5EcmFnZ2FibGUuX2RyYWdnaW5nID0gdGhpczsgIC8vIFByZXZlbnQgZHJhZ2dpbmcgbXVsdGlwbGUgb2JqZWN0cyBhdCBvbmNlLlxyXG5cclxuXHRcdGlmICh0aGlzLl9wcmV2ZW50T3V0bGluZSkge1xyXG5cdFx0XHRMLkRvbVV0aWwucHJldmVudE91dGxpbmUodGhpcy5fZWxlbWVudCk7XHJcblx0XHR9XHJcblxyXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVJbWFnZURyYWcoKTtcclxuXHRcdEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbigpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tb3ZpbmcpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGRvd246IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgZHJhZyBpcyBhYm91dCB0byBzdGFydC5cclxuXHRcdHRoaXMuZmlyZSgnZG93bicpO1xyXG5cclxuXHRcdHZhciBmaXJzdCA9IGUudG91Y2hlcyA/IGUudG91Y2hlc1swXSA6IGU7XHJcblxyXG5cdFx0dGhpcy5fc3RhcnRQb2ludCA9IG5ldyBMLlBvaW50KGZpcnN0LmNsaWVudFgsIGZpcnN0LmNsaWVudFkpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnRcclxuXHRcdFx0Lm9uKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5NT1ZFW2UudHlwZV0sIHRoaXMuX29uTW92ZSwgdGhpcylcclxuXHRcdFx0Lm9uKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5FTkRbZS50eXBlXSwgdGhpcy5fb25VcCwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0X29uTW92ZTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdC8vIElnbm9yZSBzaW11bGF0ZWQgZXZlbnRzLCBzaW5jZSB3ZSBoYW5kbGUgYm90aCB0b3VjaCBhbmRcclxuXHRcdC8vIG1vdXNlIGV4cGxpY2l0bHk7IG90aGVyd2lzZSB3ZSByaXNrIGdldHRpbmcgZHVwbGljYXRlcyBvZlxyXG5cdFx0Ly8gdG91Y2ggZXZlbnRzLCBzZWUgIzQzMTUuXHJcblx0XHQvLyBBbHNvIGlnbm9yZSB0aGUgZXZlbnQgaWYgZGlzYWJsZWQ7IHRoaXMgaGFwcGVucyBpbiBJRTExXHJcblx0XHQvLyB1bmRlciBzb21lIGNpcmN1bXN0YW5jZXMsIHNlZSAjMzY2Ni5cclxuXHRcdGlmIChlLl9zaW11bGF0ZWQgfHwgIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0aWYgKGUudG91Y2hlcyAmJiBlLnRvdWNoZXMubGVuZ3RoID4gMSkge1xyXG5cdFx0XHR0aGlzLl9tb3ZlZCA9IHRydWU7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZmlyc3QgPSAoZS50b3VjaGVzICYmIGUudG91Y2hlcy5sZW5ndGggPT09IDEgPyBlLnRvdWNoZXNbMF0gOiBlKSxcclxuXHRcdCAgICBuZXdQb2ludCA9IG5ldyBMLlBvaW50KGZpcnN0LmNsaWVudFgsIGZpcnN0LmNsaWVudFkpLFxyXG5cdFx0ICAgIG9mZnNldCA9IG5ld1BvaW50LnN1YnRyYWN0KHRoaXMuX3N0YXJ0UG9pbnQpO1xyXG5cclxuXHRcdGlmICghb2Zmc2V0LnggJiYgIW9mZnNldC55KSB7IHJldHVybjsgfVxyXG5cdFx0aWYgKE1hdGguYWJzKG9mZnNldC54KSArIE1hdGguYWJzKG9mZnNldC55KSA8IHRoaXMub3B0aW9ucy5jbGlja1RvbGVyYW5jZSkgeyByZXR1cm47IH1cclxuXHJcblx0XHRMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xyXG5cclxuXHRcdGlmICghdGhpcy5fbW92ZWQpIHtcclxuXHRcdFx0Ly8gQGV2ZW50IGRyYWdzdGFydDogRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgd2hlbiBhIGRyYWcgc3RhcnRzXHJcblx0XHRcdHRoaXMuZmlyZSgnZHJhZ3N0YXJ0Jyk7XHJcblxyXG5cdFx0XHR0aGlzLl9tb3ZlZCA9IHRydWU7XHJcblx0XHRcdHRoaXMuX3N0YXJ0UG9zID0gTC5Eb21VdGlsLmdldFBvc2l0aW9uKHRoaXMuX2VsZW1lbnQpLnN1YnRyYWN0KG9mZnNldCk7XHJcblxyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoZG9jdW1lbnQuYm9keSwgJ2xlYWZsZXQtZHJhZ2dpbmcnKTtcclxuXHJcblx0XHRcdHRoaXMuX2xhc3RUYXJnZXQgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQ7XHJcblx0XHRcdC8vIElFIGFuZCBFZGdlIGRvIG5vdCBnaXZlIHRoZSA8dXNlPiBlbGVtZW50LCBzbyBmZXRjaCBpdFxyXG5cdFx0XHQvLyBpZiBuZWNlc3NhcnlcclxuXHRcdFx0aWYgKCh3aW5kb3cuU1ZHRWxlbWVudEluc3RhbmNlKSAmJiAodGhpcy5fbGFzdFRhcmdldCBpbnN0YW5jZW9mIFNWR0VsZW1lbnRJbnN0YW5jZSkpIHtcclxuXHRcdFx0XHR0aGlzLl9sYXN0VGFyZ2V0ID0gdGhpcy5fbGFzdFRhcmdldC5jb3JyZXNwb25kaW5nVXNlRWxlbWVudDtcclxuXHRcdFx0fVxyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fbGFzdFRhcmdldCwgJ2xlYWZsZXQtZHJhZy10YXJnZXQnKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9uZXdQb3MgPSB0aGlzLl9zdGFydFBvcy5hZGQob2Zmc2V0KTtcclxuXHRcdHRoaXMuX21vdmluZyA9IHRydWU7XHJcblxyXG5cdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9hbmltUmVxdWVzdCk7XHJcblx0XHR0aGlzLl9sYXN0RXZlbnQgPSBlO1xyXG5cdFx0dGhpcy5fYW5pbVJlcXVlc3QgPSBMLlV0aWwucmVxdWVzdEFuaW1GcmFtZSh0aGlzLl91cGRhdGVQb3NpdGlvbiwgdGhpcywgdHJ1ZSk7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZVBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZSA9IHtvcmlnaW5hbEV2ZW50OiB0aGlzLl9sYXN0RXZlbnR9O1xyXG5cclxuXHRcdC8vIEBldmVudCBwcmVkcmFnOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgY29udGludW91c2x5IGR1cmluZyBkcmFnZ2luZyAqYmVmb3JlKiBlYWNoIGNvcnJlc3BvbmRpbmdcclxuXHRcdC8vIHVwZGF0ZSBvZiB0aGUgZWxlbWVudCdzIHBvc2l0aW9uLlxyXG5cdFx0dGhpcy5maXJlKCdwcmVkcmFnJywgZSk7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fZWxlbWVudCwgdGhpcy5fbmV3UG9zKTtcclxuXHJcblx0XHQvLyBAZXZlbnQgZHJhZzogRXZlbnRcclxuXHRcdC8vIEZpcmVkIGNvbnRpbnVvdXNseSBkdXJpbmcgZHJhZ2dpbmcuXHJcblx0XHR0aGlzLmZpcmUoJ2RyYWcnLCBlKTtcclxuXHR9LFxyXG5cclxuXHRfb25VcDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdC8vIElnbm9yZSBzaW11bGF0ZWQgZXZlbnRzLCBzaW5jZSB3ZSBoYW5kbGUgYm90aCB0b3VjaCBhbmRcclxuXHRcdC8vIG1vdXNlIGV4cGxpY2l0bHk7IG90aGVyd2lzZSB3ZSByaXNrIGdldHRpbmcgZHVwbGljYXRlcyBvZlxyXG5cdFx0Ly8gdG91Y2ggZXZlbnRzLCBzZWUgIzQzMTUuXHJcblx0XHQvLyBBbHNvIGlnbm9yZSB0aGUgZXZlbnQgaWYgZGlzYWJsZWQ7IHRoaXMgaGFwcGVucyBpbiBJRTExXHJcblx0XHQvLyB1bmRlciBzb21lIGNpcmN1bXN0YW5jZXMsIHNlZSAjMzY2Ni5cclxuXHRcdGlmIChlLl9zaW11bGF0ZWQgfHwgIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblx0XHR0aGlzLmZpbmlzaERyYWcoKTtcclxuXHR9LFxyXG5cclxuXHRmaW5pc2hEcmFnOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3MoZG9jdW1lbnQuYm9keSwgJ2xlYWZsZXQtZHJhZ2dpbmcnKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbGFzdFRhcmdldCkge1xyXG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fbGFzdFRhcmdldCwgJ2xlYWZsZXQtZHJhZy10YXJnZXQnKTtcclxuXHRcdFx0dGhpcy5fbGFzdFRhcmdldCA9IG51bGw7XHJcblx0XHR9XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiBMLkRyYWdnYWJsZS5NT1ZFKSB7XHJcblx0XHRcdEwuRG9tRXZlbnRcclxuXHRcdFx0XHQub2ZmKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5NT1ZFW2ldLCB0aGlzLl9vbk1vdmUsIHRoaXMpXHJcblx0XHRcdFx0Lm9mZihkb2N1bWVudCwgTC5EcmFnZ2FibGUuRU5EW2ldLCB0aGlzLl9vblVwLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwuZW5hYmxlSW1hZ2VEcmFnKCk7XHJcblx0XHRMLkRvbVV0aWwuZW5hYmxlVGV4dFNlbGVjdGlvbigpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tb3ZlZCAmJiB0aGlzLl9tb3ZpbmcpIHtcclxuXHRcdFx0Ly8gZW5zdXJlIGRyYWcgaXMgbm90IGZpcmVkIGFmdGVyIGRyYWdlbmRcclxuXHRcdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9hbmltUmVxdWVzdCk7XHJcblxyXG5cdFx0XHQvLyBAZXZlbnQgZHJhZ2VuZDogRHJhZ0VuZEV2ZW50XHJcblx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIGRyYWcgZW5kcy5cclxuXHRcdFx0dGhpcy5maXJlKCdkcmFnZW5kJywge1xyXG5cdFx0XHRcdGRpc3RhbmNlOiB0aGlzLl9uZXdQb3MuZGlzdGFuY2VUbyh0aGlzLl9zdGFydFBvcylcclxuXHRcdFx0fSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fbW92aW5nID0gZmFsc2U7XHJcblx0XHRMLkRyYWdnYWJsZS5fZHJhZ2dpbmcgPSBmYWxzZTtcclxuXHR9XHJcblxyXG59KTtcclxuXG5cblxuLypcblx0TC5IYW5kbGVyIGlzIGEgYmFzZSBjbGFzcyBmb3IgaGFuZGxlciBjbGFzc2VzIHRoYXQgYXJlIHVzZWQgaW50ZXJuYWxseSB0byBpbmplY3Rcblx0aW50ZXJhY3Rpb24gZmVhdHVyZXMgbGlrZSBkcmFnZ2luZyB0byBjbGFzc2VzIGxpa2UgTWFwIGFuZCBNYXJrZXIuXG4qL1xuXG4vLyBAY2xhc3MgSGFuZGxlclxuLy8gQGFrYSBMLkhhbmRsZXJcbi8vIEFic3RyYWN0IGNsYXNzIGZvciBtYXAgaW50ZXJhY3Rpb24gaGFuZGxlcnNcblxuTC5IYW5kbGVyID0gTC5DbGFzcy5leHRlbmQoe1xuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0dGhpcy5fbWFwID0gbWFwO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZW5hYmxlKCk6IHRoaXNcblx0Ly8gRW5hYmxlcyB0aGUgaGFuZGxlclxuXHRlbmFibGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fZW5hYmxlZCkgeyByZXR1cm4gdGhpczsgfVxuXG5cdFx0dGhpcy5fZW5hYmxlZCA9IHRydWU7XG5cdFx0dGhpcy5hZGRIb29rcygpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZGlzYWJsZSgpOiB0aGlzXG5cdC8vIERpc2FibGVzIHRoZSBoYW5kbGVyXG5cdGRpc2FibGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuIHRoaXM7IH1cblxuXHRcdHRoaXMuX2VuYWJsZWQgPSBmYWxzZTtcblx0XHR0aGlzLnJlbW92ZUhvb2tzKCk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBlbmFibGVkKCk6IEJvb2xlYW5cblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGhhbmRsZXIgaXMgZW5hYmxlZFxuXHRlbmFibGVkOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuICEhdGhpcy5fZW5hYmxlZDtcblx0fVxuXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXG5cdC8vIENsYXNzZXMgaW5oZXJpdGluZyBmcm9tIGBIYW5kbGVyYCBtdXN0IGltcGxlbWVudCB0aGUgdHdvIGZvbGxvd2luZyBtZXRob2RzOlxuXHQvLyBAbWV0aG9kIGFkZEhvb2tzKClcblx0Ly8gQ2FsbGVkIHdoZW4gdGhlIGhhbmRsZXIgaXMgZW5hYmxlZCwgc2hvdWxkIGFkZCBldmVudCBob29rcy5cblx0Ly8gQG1ldGhvZCByZW1vdmVIb29rcygpXG5cdC8vIENhbGxlZCB3aGVuIHRoZSBoYW5kbGVyIGlzIGRpc2FibGVkLCBzaG91bGQgcmVtb3ZlIHRoZSBldmVudCBob29rcyBhZGRlZCBwcmV2aW91c2x5LlxufSk7XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLk1hcERyYWcgaXMgdXNlZCB0byBtYWtlIHRoZSBtYXAgZHJhZ2dhYmxlICh3aXRoIHBhbm5pbmcgaW5lcnRpYSksIGVuYWJsZWQgYnkgZGVmYXVsdC5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuTC5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQG9wdGlvbiBkcmFnZ2luZzogQm9vbGVhbiA9IHRydWVcblx0Ly8gV2hldGhlciB0aGUgbWFwIGJlIGRyYWdnYWJsZSB3aXRoIG1vdXNlL3RvdWNoIG9yIG5vdC5cblx0ZHJhZ2dpbmc6IHRydWUsXG5cblx0Ly8gQHNlY3Rpb24gUGFubmluZyBJbmVydGlhIE9wdGlvbnNcblx0Ly8gQG9wdGlvbiBpbmVydGlhOiBCb29sZWFuID0gKlxuXHQvLyBJZiBlbmFibGVkLCBwYW5uaW5nIG9mIHRoZSBtYXAgd2lsbCBoYXZlIGFuIGluZXJ0aWEgZWZmZWN0IHdoZXJlXG5cdC8vIHRoZSBtYXAgYnVpbGRzIG1vbWVudHVtIHdoaWxlIGRyYWdnaW5nIGFuZCBjb250aW51ZXMgbW92aW5nIGluXG5cdC8vIHRoZSBzYW1lIGRpcmVjdGlvbiBmb3Igc29tZSB0aW1lLiBGZWVscyBlc3BlY2lhbGx5IG5pY2Ugb24gdG91Y2hcblx0Ly8gZGV2aWNlcy4gRW5hYmxlZCBieSBkZWZhdWx0IHVubGVzcyBydW5uaW5nIG9uIG9sZCBBbmRyb2lkIGRldmljZXMuXG5cdGluZXJ0aWE6ICFMLkJyb3dzZXIuYW5kcm9pZDIzLFxuXG5cdC8vIEBvcHRpb24gaW5lcnRpYURlY2VsZXJhdGlvbjogTnVtYmVyID0gMzAwMFxuXHQvLyBUaGUgcmF0ZSB3aXRoIHdoaWNoIHRoZSBpbmVydGlhbCBtb3ZlbWVudCBzbG93cyBkb3duLCBpbiBwaXhlbHMvc2Vjb25kwrIuXG5cdGluZXJ0aWFEZWNlbGVyYXRpb246IDM0MDAsIC8vIHB4L3NeMlxuXG5cdC8vIEBvcHRpb24gaW5lcnRpYU1heFNwZWVkOiBOdW1iZXIgPSBJbmZpbml0eVxuXHQvLyBNYXggc3BlZWQgb2YgdGhlIGluZXJ0aWFsIG1vdmVtZW50LCBpbiBwaXhlbHMvc2Vjb25kLlxuXHRpbmVydGlhTWF4U3BlZWQ6IEluZmluaXR5LCAvLyBweC9zXG5cblx0Ly8gQG9wdGlvbiBlYXNlTGluZWFyaXR5OiBOdW1iZXIgPSAwLjJcblx0ZWFzZUxpbmVhcml0eTogMC4yLFxuXG5cdC8vIFRPRE8gcmVmYWN0b3IsIG1vdmUgdG8gQ1JTXG5cdC8vIEBvcHRpb24gd29ybGRDb3B5SnVtcDogQm9vbGVhbiA9IGZhbHNlXG5cdC8vIFdpdGggdGhpcyBvcHRpb24gZW5hYmxlZCwgdGhlIG1hcCB0cmFja3Mgd2hlbiB5b3UgcGFuIHRvIGFub3RoZXIgXCJjb3B5XCJcblx0Ly8gb2YgdGhlIHdvcmxkIGFuZCBzZWFtbGVzc2x5IGp1bXBzIHRvIHRoZSBvcmlnaW5hbCBvbmUgc28gdGhhdCBhbGwgb3ZlcmxheXNcblx0Ly8gbGlrZSBtYXJrZXJzIGFuZCB2ZWN0b3IgbGF5ZXJzIGFyZSBzdGlsbCB2aXNpYmxlLlxuXHR3b3JsZENvcHlKdW1wOiBmYWxzZSxcblxuXHQvLyBAb3B0aW9uIG1heEJvdW5kc1Zpc2Nvc2l0eTogTnVtYmVyID0gMC4wXG5cdC8vIElmIGBtYXhCb3VuZHNgIGlzIHNldCwgdGhpcyBvcHRpb24gd2lsbCBjb250cm9sIGhvdyBzb2xpZCB0aGUgYm91bmRzXG5cdC8vIGFyZSB3aGVuIGRyYWdnaW5nIHRoZSBtYXAgYXJvdW5kLiBUaGUgZGVmYXVsdCB2YWx1ZSBvZiBgMC4wYCBhbGxvd3MgdGhlXG5cdC8vIHVzZXIgdG8gZHJhZyBvdXRzaWRlIHRoZSBib3VuZHMgYXQgbm9ybWFsIHNwZWVkLCBoaWdoZXIgdmFsdWVzIHdpbGxcblx0Ly8gc2xvdyBkb3duIG1hcCBkcmFnZ2luZyBvdXRzaWRlIGJvdW5kcywgYW5kIGAxLjBgIG1ha2VzIHRoZSBib3VuZHMgZnVsbHlcblx0Ly8gc29saWQsIHByZXZlbnRpbmcgdGhlIHVzZXIgZnJvbSBkcmFnZ2luZyBvdXRzaWRlIHRoZSBib3VuZHMuXG5cdG1heEJvdW5kc1Zpc2Nvc2l0eTogMC4wXG59KTtcblxuTC5NYXAuRHJhZyA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fZHJhZ2dhYmxlKSB7XG5cdFx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXG5cdFx0XHR0aGlzLl9kcmFnZ2FibGUgPSBuZXcgTC5EcmFnZ2FibGUobWFwLl9tYXBQYW5lLCBtYXAuX2NvbnRhaW5lcik7XG5cblx0XHRcdHRoaXMuX2RyYWdnYWJsZS5vbih7XG5cdFx0XHRcdGRvd246IHRoaXMuX29uRG93bixcblx0XHRcdFx0ZHJhZ3N0YXJ0OiB0aGlzLl9vbkRyYWdTdGFydCxcblx0XHRcdFx0ZHJhZzogdGhpcy5fb25EcmFnLFxuXHRcdFx0XHRkcmFnZW5kOiB0aGlzLl9vbkRyYWdFbmRcblx0XHRcdH0sIHRoaXMpO1xuXG5cdFx0XHR0aGlzLl9kcmFnZ2FibGUub24oJ3ByZWRyYWcnLCB0aGlzLl9vblByZURyYWdMaW1pdCwgdGhpcyk7XG5cdFx0XHRpZiAobWFwLm9wdGlvbnMud29ybGRDb3B5SnVtcCkge1xuXHRcdFx0XHR0aGlzLl9kcmFnZ2FibGUub24oJ3ByZWRyYWcnLCB0aGlzLl9vblByZURyYWdXcmFwLCB0aGlzKTtcblx0XHRcdFx0bWFwLm9uKCd6b29tZW5kJywgdGhpcy5fb25ab29tRW5kLCB0aGlzKTtcblxuXHRcdFx0XHRtYXAud2hlblJlYWR5KHRoaXMuX29uWm9vbUVuZCwgdGhpcyk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtZ3JhYiBsZWFmbGV0LXRvdWNoLWRyYWcnKTtcblx0XHR0aGlzLl9kcmFnZ2FibGUuZW5hYmxlKCk7XG5cdFx0dGhpcy5fcG9zaXRpb25zID0gW107XG5cdFx0dGhpcy5fdGltZXMgPSBbXTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtZ3JhYicpO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtdG91Y2gtZHJhZycpO1xuXHRcdHRoaXMuX2RyYWdnYWJsZS5kaXNhYmxlKCk7XG5cdH0sXG5cblx0bW92ZWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fZHJhZ2dhYmxlICYmIHRoaXMuX2RyYWdnYWJsZS5fbW92ZWQ7XG5cdH0sXG5cblx0bW92aW5nOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2RyYWdnYWJsZSAmJiB0aGlzLl9kcmFnZ2FibGUuX21vdmluZztcblx0fSxcblxuXHRfb25Eb3duOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fbWFwLl9zdG9wKCk7XG5cdH0sXG5cblx0X29uRHJhZ1N0YXJ0OiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcDtcblxuXHRcdGlmICh0aGlzLl9tYXAub3B0aW9ucy5tYXhCb3VuZHMgJiYgdGhpcy5fbWFwLm9wdGlvbnMubWF4Qm91bmRzVmlzY29zaXR5KSB7XG5cdFx0XHR2YXIgYm91bmRzID0gTC5sYXRMbmdCb3VuZHModGhpcy5fbWFwLm9wdGlvbnMubWF4Qm91bmRzKTtcblxuXHRcdFx0dGhpcy5fb2Zmc2V0TGltaXQgPSBMLmJvdW5kcyhcblx0XHRcdFx0dGhpcy5fbWFwLmxhdExuZ1RvQ29udGFpbmVyUG9pbnQoYm91bmRzLmdldE5vcnRoV2VzdCgpKS5tdWx0aXBseUJ5KC0xKSxcblx0XHRcdFx0dGhpcy5fbWFwLmxhdExuZ1RvQ29udGFpbmVyUG9pbnQoYm91bmRzLmdldFNvdXRoRWFzdCgpKS5tdWx0aXBseUJ5KC0xKVxuXHRcdFx0XHRcdC5hZGQodGhpcy5fbWFwLmdldFNpemUoKSkpO1xuXG5cdFx0XHR0aGlzLl92aXNjb3NpdHkgPSBNYXRoLm1pbigxLjAsIE1hdGgubWF4KDAuMCwgdGhpcy5fbWFwLm9wdGlvbnMubWF4Qm91bmRzVmlzY29zaXR5KSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX29mZnNldExpbWl0ID0gbnVsbDtcblx0XHR9XG5cblx0XHRtYXBcblx0XHQgICAgLmZpcmUoJ21vdmVzdGFydCcpXG5cdFx0ICAgIC5maXJlKCdkcmFnc3RhcnQnKTtcblxuXHRcdGlmIChtYXAub3B0aW9ucy5pbmVydGlhKSB7XG5cdFx0XHR0aGlzLl9wb3NpdGlvbnMgPSBbXTtcblx0XHRcdHRoaXMuX3RpbWVzID0gW107XG5cdFx0fVxuXHR9LFxuXG5cdF9vbkRyYWc6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKHRoaXMuX21hcC5vcHRpb25zLmluZXJ0aWEpIHtcblx0XHRcdHZhciB0aW1lID0gdGhpcy5fbGFzdFRpbWUgPSArbmV3IERhdGUoKSxcblx0XHRcdCAgICBwb3MgPSB0aGlzLl9sYXN0UG9zID0gdGhpcy5fZHJhZ2dhYmxlLl9hYnNQb3MgfHwgdGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3M7XG5cblx0XHRcdHRoaXMuX3Bvc2l0aW9ucy5wdXNoKHBvcyk7XG5cdFx0XHR0aGlzLl90aW1lcy5wdXNoKHRpbWUpO1xuXG5cdFx0XHRpZiAodGltZSAtIHRoaXMuX3RpbWVzWzBdID4gNTApIHtcblx0XHRcdFx0dGhpcy5fcG9zaXRpb25zLnNoaWZ0KCk7XG5cdFx0XHRcdHRoaXMuX3RpbWVzLnNoaWZ0KCk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dGhpcy5fbWFwXG5cdFx0ICAgIC5maXJlKCdtb3ZlJywgZSlcblx0XHQgICAgLmZpcmUoJ2RyYWcnLCBlKTtcblx0fSxcblxuXHRfb25ab29tRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIHB4Q2VudGVyID0gdGhpcy5fbWFwLmdldFNpemUoKS5kaXZpZGVCeSgyKSxcblx0XHQgICAgcHhXb3JsZENlbnRlciA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQoWzAsIDBdKTtcblxuXHRcdHRoaXMuX2luaXRpYWxXb3JsZE9mZnNldCA9IHB4V29ybGRDZW50ZXIuc3VidHJhY3QocHhDZW50ZXIpLng7XG5cdFx0dGhpcy5fd29ybGRXaWR0aCA9IHRoaXMuX21hcC5nZXRQaXhlbFdvcmxkQm91bmRzKCkuZ2V0U2l6ZSgpLng7XG5cdH0sXG5cblx0X3Zpc2NvdXNMaW1pdDogZnVuY3Rpb24gKHZhbHVlLCB0aHJlc2hvbGQpIHtcblx0XHRyZXR1cm4gdmFsdWUgLSAodmFsdWUgLSB0aHJlc2hvbGQpICogdGhpcy5fdmlzY29zaXR5O1xuXHR9LFxuXG5cdF9vblByZURyYWdMaW1pdDogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fdmlzY29zaXR5IHx8ICF0aGlzLl9vZmZzZXRMaW1pdCkgeyByZXR1cm47IH1cblxuXHRcdHZhciBvZmZzZXQgPSB0aGlzLl9kcmFnZ2FibGUuX25ld1Bvcy5zdWJ0cmFjdCh0aGlzLl9kcmFnZ2FibGUuX3N0YXJ0UG9zKTtcblxuXHRcdHZhciBsaW1pdCA9IHRoaXMuX29mZnNldExpbWl0O1xuXHRcdGlmIChvZmZzZXQueCA8IGxpbWl0Lm1pbi54KSB7IG9mZnNldC54ID0gdGhpcy5fdmlzY291c0xpbWl0KG9mZnNldC54LCBsaW1pdC5taW4ueCk7IH1cblx0XHRpZiAob2Zmc2V0LnkgPCBsaW1pdC5taW4ueSkgeyBvZmZzZXQueSA9IHRoaXMuX3Zpc2NvdXNMaW1pdChvZmZzZXQueSwgbGltaXQubWluLnkpOyB9XG5cdFx0aWYgKG9mZnNldC54ID4gbGltaXQubWF4LngpIHsgb2Zmc2V0LnggPSB0aGlzLl92aXNjb3VzTGltaXQob2Zmc2V0LngsIGxpbWl0Lm1heC54KTsgfVxuXHRcdGlmIChvZmZzZXQueSA+IGxpbWl0Lm1heC55KSB7IG9mZnNldC55ID0gdGhpcy5fdmlzY291c0xpbWl0KG9mZnNldC55LCBsaW1pdC5tYXgueSk7IH1cblxuXHRcdHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zID0gdGhpcy5fZHJhZ2dhYmxlLl9zdGFydFBvcy5hZGQob2Zmc2V0KTtcblx0fSxcblxuXHRfb25QcmVEcmFnV3JhcDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIFRPRE8gcmVmYWN0b3IgdG8gYmUgYWJsZSB0byBhZGp1c3QgbWFwIHBhbmUgcG9zaXRpb24gYWZ0ZXIgem9vbVxuXHRcdHZhciB3b3JsZFdpZHRoID0gdGhpcy5fd29ybGRXaWR0aCxcblx0XHQgICAgaGFsZldpZHRoID0gTWF0aC5yb3VuZCh3b3JsZFdpZHRoIC8gMiksXG5cdFx0ICAgIGR4ID0gdGhpcy5faW5pdGlhbFdvcmxkT2Zmc2V0LFxuXHRcdCAgICB4ID0gdGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3MueCxcblx0XHQgICAgbmV3WDEgPSAoeCAtIGhhbGZXaWR0aCArIGR4KSAlIHdvcmxkV2lkdGggKyBoYWxmV2lkdGggLSBkeCxcblx0XHQgICAgbmV3WDIgPSAoeCArIGhhbGZXaWR0aCArIGR4KSAlIHdvcmxkV2lkdGggLSBoYWxmV2lkdGggLSBkeCxcblx0XHQgICAgbmV3WCA9IE1hdGguYWJzKG5ld1gxICsgZHgpIDwgTWF0aC5hYnMobmV3WDIgKyBkeCkgPyBuZXdYMSA6IG5ld1gyO1xuXG5cdFx0dGhpcy5fZHJhZ2dhYmxlLl9hYnNQb3MgPSB0aGlzLl9kcmFnZ2FibGUuX25ld1Bvcy5jbG9uZSgpO1xuXHRcdHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zLnggPSBuZXdYO1xuXHR9LFxuXG5cdF9vbkRyYWdFbmQ6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgb3B0aW9ucyA9IG1hcC5vcHRpb25zLFxuXG5cdFx0ICAgIG5vSW5lcnRpYSA9ICFvcHRpb25zLmluZXJ0aWEgfHwgdGhpcy5fdGltZXMubGVuZ3RoIDwgMjtcblxuXHRcdG1hcC5maXJlKCdkcmFnZW5kJywgZSk7XG5cblx0XHRpZiAobm9JbmVydGlhKSB7XG5cdFx0XHRtYXAuZmlyZSgnbW92ZWVuZCcpO1xuXG5cdFx0fSBlbHNlIHtcblxuXHRcdFx0dmFyIGRpcmVjdGlvbiA9IHRoaXMuX2xhc3RQb3Muc3VidHJhY3QodGhpcy5fcG9zaXRpb25zWzBdKSxcblx0XHRcdCAgICBkdXJhdGlvbiA9ICh0aGlzLl9sYXN0VGltZSAtIHRoaXMuX3RpbWVzWzBdKSAvIDEwMDAsXG5cdFx0XHQgICAgZWFzZSA9IG9wdGlvbnMuZWFzZUxpbmVhcml0eSxcblxuXHRcdFx0ICAgIHNwZWVkVmVjdG9yID0gZGlyZWN0aW9uLm11bHRpcGx5QnkoZWFzZSAvIGR1cmF0aW9uKSxcblx0XHRcdCAgICBzcGVlZCA9IHNwZWVkVmVjdG9yLmRpc3RhbmNlVG8oWzAsIDBdKSxcblxuXHRcdFx0ICAgIGxpbWl0ZWRTcGVlZCA9IE1hdGgubWluKG9wdGlvbnMuaW5lcnRpYU1heFNwZWVkLCBzcGVlZCksXG5cdFx0XHQgICAgbGltaXRlZFNwZWVkVmVjdG9yID0gc3BlZWRWZWN0b3IubXVsdGlwbHlCeShsaW1pdGVkU3BlZWQgLyBzcGVlZCksXG5cblx0XHRcdCAgICBkZWNlbGVyYXRpb25EdXJhdGlvbiA9IGxpbWl0ZWRTcGVlZCAvIChvcHRpb25zLmluZXJ0aWFEZWNlbGVyYXRpb24gKiBlYXNlKSxcblx0XHRcdCAgICBvZmZzZXQgPSBsaW1pdGVkU3BlZWRWZWN0b3IubXVsdGlwbHlCeSgtZGVjZWxlcmF0aW9uRHVyYXRpb24gLyAyKS5yb3VuZCgpO1xuXG5cdFx0XHRpZiAoIW9mZnNldC54ICYmICFvZmZzZXQueSkge1xuXHRcdFx0XHRtYXAuZmlyZSgnbW92ZWVuZCcpO1xuXG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRvZmZzZXQgPSBtYXAuX2xpbWl0T2Zmc2V0KG9mZnNldCwgbWFwLm9wdGlvbnMubWF4Qm91bmRzKTtcblxuXHRcdFx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdFx0bWFwLnBhbkJ5KG9mZnNldCwge1xuXHRcdFx0XHRcdFx0ZHVyYXRpb246IGRlY2VsZXJhdGlvbkR1cmF0aW9uLFxuXHRcdFx0XHRcdFx0ZWFzZUxpbmVhcml0eTogZWFzZSxcblx0XHRcdFx0XHRcdG5vTW92ZVN0YXJ0OiB0cnVlLFxuXHRcdFx0XHRcdFx0YW5pbWF0ZTogdHJ1ZVxuXHRcdFx0XHRcdH0pO1xuXHRcdFx0XHR9KTtcblx0XHRcdH1cblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IGRyYWdnaW5nOiBIYW5kbGVyXG4vLyBNYXAgZHJhZ2dpbmcgaGFuZGxlciAoYnkgYm90aCBtb3VzZSBhbmQgdG91Y2gpLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAnZHJhZ2dpbmcnLCBMLk1hcC5EcmFnKTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuRG91YmxlQ2xpY2tab29tIGlzIHVzZWQgdG8gaGFuZGxlIGRvdWJsZS1jbGljayB6b29tIG9uIHRoZSBtYXAsIGVuYWJsZWQgYnkgZGVmYXVsdC5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAb3B0aW9uIGRvdWJsZUNsaWNrWm9vbTogQm9vbGVhbnxTdHJpbmcgPSB0cnVlXG5cdC8vIFdoZXRoZXIgdGhlIG1hcCBjYW4gYmUgem9vbWVkIGluIGJ5IGRvdWJsZSBjbGlja2luZyBvbiBpdCBhbmRcblx0Ly8gem9vbWVkIG91dCBieSBkb3VibGUgY2xpY2tpbmcgd2hpbGUgaG9sZGluZyBzaGlmdC4gSWYgcGFzc2VkXG5cdC8vIGAnY2VudGVyJ2AsIGRvdWJsZS1jbGljayB6b29tIHdpbGwgem9vbSB0byB0aGUgY2VudGVyIG9mIHRoZVxuXHQvLyAgdmlldyByZWdhcmRsZXNzIG9mIHdoZXJlIHRoZSBtb3VzZSB3YXMuXG5cdGRvdWJsZUNsaWNrWm9vbTogdHJ1ZVxufSk7XG5cbkwuTWFwLkRvdWJsZUNsaWNrWm9vbSA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX21hcC5vbignZGJsY2xpY2snLCB0aGlzLl9vbkRvdWJsZUNsaWNrLCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX21hcC5vZmYoJ2RibGNsaWNrJywgdGhpcy5fb25Eb3VibGVDbGljaywgdGhpcyk7XG5cdH0sXG5cblx0X29uRG91YmxlQ2xpY2s6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgb2xkWm9vbSA9IG1hcC5nZXRab29tKCksXG5cdFx0ICAgIGRlbHRhID0gbWFwLm9wdGlvbnMuem9vbURlbHRhLFxuXHRcdCAgICB6b29tID0gZS5vcmlnaW5hbEV2ZW50LnNoaWZ0S2V5ID8gb2xkWm9vbSAtIGRlbHRhIDogb2xkWm9vbSArIGRlbHRhO1xuXG5cdFx0aWYgKG1hcC5vcHRpb25zLmRvdWJsZUNsaWNrWm9vbSA9PT0gJ2NlbnRlcicpIHtcblx0XHRcdG1hcC5zZXRab29tKHpvb20pO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRtYXAuc2V0Wm9vbUFyb3VuZChlLmNvbnRhaW5lclBvaW50LCB6b29tKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy9cbi8vIE1hcCBwcm9wZXJ0aWVzIGluY2x1ZGUgaW50ZXJhY3Rpb24gaGFuZGxlcnMgdGhhdCBhbGxvdyB5b3UgdG8gY29udHJvbFxuLy8gaW50ZXJhY3Rpb24gYmVoYXZpb3IgaW4gcnVudGltZSwgZW5hYmxpbmcgb3IgZGlzYWJsaW5nIGNlcnRhaW4gZmVhdHVyZXMgc3VjaFxuLy8gYXMgZHJhZ2dpbmcgb3IgdG91Y2ggem9vbSAoc2VlIGBIYW5kbGVyYCBtZXRob2RzKS4gRm9yIGV4YW1wbGU6XG4vL1xuLy8gYGBganNcbi8vIG1hcC5kb3VibGVDbGlja1pvb20uZGlzYWJsZSgpO1xuLy8gYGBgXG4vL1xuLy8gQHByb3BlcnR5IGRvdWJsZUNsaWNrWm9vbTogSGFuZGxlclxuLy8gRG91YmxlIGNsaWNrIHpvb20gaGFuZGxlci5cbkwuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ2RvdWJsZUNsaWNrWm9vbScsIEwuTWFwLkRvdWJsZUNsaWNrWm9vbSk7XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLlNjcm9sbFdoZWVsWm9vbSBpcyB1c2VkIGJ5IEwuTWFwIHRvIGVuYWJsZSBtb3VzZSBzY3JvbGwgd2hlZWwgem9vbSBvbiB0aGUgbWFwLlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAc2VjdGlvbiBNb3VzZXdoZWVsIG9wdGlvbnNcblx0Ly8gQG9wdGlvbiBzY3JvbGxXaGVlbFpvb206IEJvb2xlYW58U3RyaW5nID0gdHJ1ZVxuXHQvLyBXaGV0aGVyIHRoZSBtYXAgY2FuIGJlIHpvb21lZCBieSB1c2luZyB0aGUgbW91c2Ugd2hlZWwuIElmIHBhc3NlZCBgJ2NlbnRlcidgLFxuXHQvLyBpdCB3aWxsIHpvb20gdG8gdGhlIGNlbnRlciBvZiB0aGUgdmlldyByZWdhcmRsZXNzIG9mIHdoZXJlIHRoZSBtb3VzZSB3YXMuXG5cdHNjcm9sbFdoZWVsWm9vbTogdHJ1ZSxcblxuXHQvLyBAb3B0aW9uIHdoZWVsRGVib3VuY2VUaW1lOiBOdW1iZXIgPSA0MFxuXHQvLyBMaW1pdHMgdGhlIHJhdGUgYXQgd2hpY2ggYSB3aGVlbCBjYW4gZmlyZSAoaW4gbWlsbGlzZWNvbmRzKS4gQnkgZGVmYXVsdFxuXHQvLyB1c2VyIGNhbid0IHpvb20gdmlhIHdoZWVsIG1vcmUgb2Z0ZW4gdGhhbiBvbmNlIHBlciA0MCBtcy5cblx0d2hlZWxEZWJvdW5jZVRpbWU6IDQwLFxuXG5cdC8vIEBvcHRpb24gd2hlZWxQeFBlclpvb21MZXZlbDogTnVtYmVyID0gNjBcblx0Ly8gSG93IG1hbnkgc2Nyb2xsIHBpeGVscyAoYXMgcmVwb3J0ZWQgYnkgW0wuRG9tRXZlbnQuZ2V0V2hlZWxEZWx0YV0oI2RvbWV2ZW50LWdldHdoZWVsZGVsdGEpKVxuXHQvLyBtZWFuIGEgY2hhbmdlIG9mIG9uZSBmdWxsIHpvb20gbGV2ZWwuIFNtYWxsZXIgdmFsdWVzIHdpbGwgbWFrZSB3aGVlbC16b29taW5nXG5cdC8vIGZhc3RlciAoYW5kIHZpY2UgdmVyc2EpLlxuXHR3aGVlbFB4UGVyWm9vbUxldmVsOiA2MFxufSk7XG5cbkwuTWFwLlNjcm9sbFdoZWVsWm9vbSA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub24odGhpcy5fbWFwLl9jb250YWluZXIsICdtb3VzZXdoZWVsJywgdGhpcy5fb25XaGVlbFNjcm9sbCwgdGhpcyk7XG5cblx0XHR0aGlzLl9kZWx0YSA9IDA7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ21vdXNld2hlZWwnLCB0aGlzLl9vbldoZWVsU2Nyb2xsLCB0aGlzKTtcblx0fSxcblxuXHRfb25XaGVlbFNjcm9sbDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgZGVsdGEgPSBMLkRvbUV2ZW50LmdldFdoZWVsRGVsdGEoZSk7XG5cblx0XHR2YXIgZGVib3VuY2UgPSB0aGlzLl9tYXAub3B0aW9ucy53aGVlbERlYm91bmNlVGltZTtcblxuXHRcdHRoaXMuX2RlbHRhICs9IGRlbHRhO1xuXHRcdHRoaXMuX2xhc3RNb3VzZVBvcyA9IHRoaXMuX21hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlKTtcblxuXHRcdGlmICghdGhpcy5fc3RhcnRUaW1lKSB7XG5cdFx0XHR0aGlzLl9zdGFydFRpbWUgPSArbmV3IERhdGUoKTtcblx0XHR9XG5cblx0XHR2YXIgbGVmdCA9IE1hdGgubWF4KGRlYm91bmNlIC0gKCtuZXcgRGF0ZSgpIC0gdGhpcy5fc3RhcnRUaW1lKSwgMCk7XG5cblx0XHRjbGVhclRpbWVvdXQodGhpcy5fdGltZXIpO1xuXHRcdHRoaXMuX3RpbWVyID0gc2V0VGltZW91dChMLmJpbmQodGhpcy5fcGVyZm9ybVpvb20sIHRoaXMpLCBsZWZ0KTtcblxuXHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcblx0fSxcblxuXHRfcGVyZm9ybVpvb206IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICB6b29tID0gbWFwLmdldFpvb20oKSxcblx0XHQgICAgc25hcCA9IHRoaXMuX21hcC5vcHRpb25zLnpvb21TbmFwIHx8IDA7XG5cblx0XHRtYXAuX3N0b3AoKTsgLy8gc3RvcCBwYW5uaW5nIGFuZCBmbHkgYW5pbWF0aW9ucyBpZiBhbnlcblxuXHRcdC8vIG1hcCB0aGUgZGVsdGEgd2l0aCBhIHNpZ21vaWQgZnVuY3Rpb24gdG8gLTQuLjQgcmFuZ2UgbGVhbmluZyBvbiAtMS4uMVxuXHRcdHZhciBkMiA9IHRoaXMuX2RlbHRhIC8gKHRoaXMuX21hcC5vcHRpb25zLndoZWVsUHhQZXJab29tTGV2ZWwgKiA0KSxcblx0XHQgICAgZDMgPSA0ICogTWF0aC5sb2coMiAvICgxICsgTWF0aC5leHAoLU1hdGguYWJzKGQyKSkpKSAvIE1hdGguTE4yLFxuXHRcdCAgICBkNCA9IHNuYXAgPyBNYXRoLmNlaWwoZDMgLyBzbmFwKSAqIHNuYXAgOiBkMyxcblx0XHQgICAgZGVsdGEgPSBtYXAuX2xpbWl0Wm9vbSh6b29tICsgKHRoaXMuX2RlbHRhID4gMCA/IGQ0IDogLWQ0KSkgLSB6b29tO1xuXG5cdFx0dGhpcy5fZGVsdGEgPSAwO1xuXHRcdHRoaXMuX3N0YXJ0VGltZSA9IG51bGw7XG5cblx0XHRpZiAoIWRlbHRhKSB7IHJldHVybjsgfVxuXG5cdFx0aWYgKG1hcC5vcHRpb25zLnNjcm9sbFdoZWVsWm9vbSA9PT0gJ2NlbnRlcicpIHtcblx0XHRcdG1hcC5zZXRab29tKHpvb20gKyBkZWx0YSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdG1hcC5zZXRab29tQXJvdW5kKHRoaXMuX2xhc3RNb3VzZVBvcywgem9vbSArIGRlbHRhKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IHNjcm9sbFdoZWVsWm9vbTogSGFuZGxlclxuLy8gU2Nyb2xsIHdoZWVsIHpvb20gaGFuZGxlci5cbkwuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ3Njcm9sbFdoZWVsWm9vbScsIEwuTWFwLlNjcm9sbFdoZWVsWm9vbSk7XG5cblxuXG4vKlxyXG4gKiBFeHRlbmRzIHRoZSBldmVudCBoYW5kbGluZyBjb2RlIHdpdGggZG91YmxlIHRhcCBzdXBwb3J0IGZvciBtb2JpbGUgYnJvd3NlcnMuXHJcbiAqL1xyXG5cclxuTC5leHRlbmQoTC5Eb21FdmVudCwge1xyXG5cclxuXHRfdG91Y2hzdGFydDogTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJEb3duJyA6IEwuQnJvd3Nlci5wb2ludGVyID8gJ3BvaW50ZXJkb3duJyA6ICd0b3VjaHN0YXJ0JyxcclxuXHRfdG91Y2hlbmQ6IEwuQnJvd3Nlci5tc1BvaW50ZXIgPyAnTVNQb2ludGVyVXAnIDogTC5Ccm93c2VyLnBvaW50ZXIgPyAncG9pbnRlcnVwJyA6ICd0b3VjaGVuZCcsXHJcblxyXG5cdC8vIGluc3BpcmVkIGJ5IFplcHRvIHRvdWNoIGNvZGUgYnkgVGhvbWFzIEZ1Y2hzXHJcblx0YWRkRG91YmxlVGFwTGlzdGVuZXI6IGZ1bmN0aW9uIChvYmosIGhhbmRsZXIsIGlkKSB7XHJcblx0XHR2YXIgbGFzdCwgdG91Y2gsXHJcblx0XHQgICAgZG91YmxlVGFwID0gZmFsc2UsXHJcblx0XHQgICAgZGVsYXkgPSAyNTA7XHJcblxyXG5cdFx0ZnVuY3Rpb24gb25Ub3VjaFN0YXJ0KGUpIHtcclxuXHRcdFx0dmFyIGNvdW50O1xyXG5cclxuXHRcdFx0aWYgKEwuQnJvd3Nlci5wb2ludGVyKSB7XHJcblx0XHRcdFx0aWYgKCghTC5Ccm93c2VyLmVkZ2UpIHx8IGUucG9pbnRlclR5cGUgPT09ICdtb3VzZScpIHsgcmV0dXJuOyB9XHJcblx0XHRcdFx0Y291bnQgPSBMLkRvbUV2ZW50Ll9wb2ludGVyc0NvdW50O1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGNvdW50ID0gZS50b3VjaGVzLmxlbmd0aDtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKGNvdW50ID4gMSkgeyByZXR1cm47IH1cclxuXHJcblx0XHRcdHZhciBub3cgPSBEYXRlLm5vdygpLFxyXG5cdFx0XHQgICAgZGVsdGEgPSBub3cgLSAobGFzdCB8fCBub3cpO1xyXG5cclxuXHRcdFx0dG91Y2ggPSBlLnRvdWNoZXMgPyBlLnRvdWNoZXNbMF0gOiBlO1xyXG5cdFx0XHRkb3VibGVUYXAgPSAoZGVsdGEgPiAwICYmIGRlbHRhIDw9IGRlbGF5KTtcclxuXHRcdFx0bGFzdCA9IG5vdztcclxuXHRcdH1cclxuXHJcblx0XHRmdW5jdGlvbiBvblRvdWNoRW5kKGUpIHtcclxuXHRcdFx0aWYgKGRvdWJsZVRhcCAmJiAhdG91Y2guY2FuY2VsQnViYmxlKSB7XHJcblx0XHRcdFx0aWYgKEwuQnJvd3Nlci5wb2ludGVyKSB7XHJcblx0XHRcdFx0XHRpZiAoKCFMLkJyb3dzZXIuZWRnZSkgfHwgZS5wb2ludGVyVHlwZSA9PT0gJ21vdXNlJykgeyByZXR1cm47IH1cclxuXHJcblx0XHRcdFx0XHQvLyB3b3JrIGFyb3VuZCAudHlwZSBiZWluZyByZWFkb25seSB3aXRoIE1TUG9pbnRlciogZXZlbnRzXHJcblx0XHRcdFx0XHR2YXIgbmV3VG91Y2ggPSB7fSxcclxuXHRcdFx0XHRcdCAgICBwcm9wLCBpO1xyXG5cclxuXHRcdFx0XHRcdGZvciAoaSBpbiB0b3VjaCkge1xyXG5cdFx0XHRcdFx0XHRwcm9wID0gdG91Y2hbaV07XHJcblx0XHRcdFx0XHRcdG5ld1RvdWNoW2ldID0gcHJvcCAmJiBwcm9wLmJpbmQgPyBwcm9wLmJpbmQodG91Y2gpIDogcHJvcDtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdHRvdWNoID0gbmV3VG91Y2g7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdHRvdWNoLnR5cGUgPSAnZGJsY2xpY2snO1xyXG5cdFx0XHRcdGhhbmRsZXIodG91Y2gpO1xyXG5cdFx0XHRcdGxhc3QgPSBudWxsO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHByZSA9ICdfbGVhZmxldF8nLFxyXG5cdFx0ICAgIHRvdWNoc3RhcnQgPSB0aGlzLl90b3VjaHN0YXJ0LFxyXG5cdFx0ICAgIHRvdWNoZW5kID0gdGhpcy5fdG91Y2hlbmQ7XHJcblxyXG5cdFx0b2JqW3ByZSArIHRvdWNoc3RhcnQgKyBpZF0gPSBvblRvdWNoU3RhcnQ7XHJcblx0XHRvYmpbcHJlICsgdG91Y2hlbmQgKyBpZF0gPSBvblRvdWNoRW5kO1xyXG5cdFx0b2JqW3ByZSArICdkYmxjbGljaycgKyBpZF0gPSBoYW5kbGVyO1xyXG5cclxuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRvdWNoc3RhcnQsIG9uVG91Y2hTdGFydCwgZmFsc2UpO1xyXG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodG91Y2hlbmQsIG9uVG91Y2hFbmQsIGZhbHNlKTtcclxuXHJcblx0XHQvLyBPbiBzb21lIHBsYXRmb3JtcyAobm90YWJseSwgY2hyb21lPDU1IG9uIHdpbjEwICsgdG91Y2hzY3JlZW4gKyBtb3VzZSksXHJcblx0XHQvLyB0aGUgYnJvd3NlciBkb2Vzbid0IGZpcmUgdG91Y2hlbmQvcG9pbnRlcnVwIGV2ZW50cyBidXQgZG9lcyBmaXJlXHJcblx0XHQvLyBuYXRpdmUgZGJsY2xpY2tzLiBTZWUgIzQxMjcuXHJcblx0XHQvLyBFZGdlIDE0IGFsc28gZmlyZXMgbmF0aXZlIGRibGNsaWNrcywgYnV0IG9ubHkgZm9yIHBvaW50ZXJUeXBlIG1vdXNlLCBzZWUgIzUxODAuXHJcblx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcignZGJsY2xpY2snLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0cmVtb3ZlRG91YmxlVGFwTGlzdGVuZXI6IGZ1bmN0aW9uIChvYmosIGlkKSB7XHJcblx0XHR2YXIgcHJlID0gJ19sZWFmbGV0XycsXHJcblx0XHQgICAgdG91Y2hzdGFydCA9IG9ialtwcmUgKyB0aGlzLl90b3VjaHN0YXJ0ICsgaWRdLFxyXG5cdFx0ICAgIHRvdWNoZW5kID0gb2JqW3ByZSArIHRoaXMuX3RvdWNoZW5kICsgaWRdLFxyXG5cdFx0ICAgIGRibGNsaWNrID0gb2JqW3ByZSArICdkYmxjbGljaycgKyBpZF07XHJcblxyXG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5fdG91Y2hzdGFydCwgdG91Y2hzdGFydCwgZmFsc2UpO1xyXG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5fdG91Y2hlbmQsIHRvdWNoZW5kLCBmYWxzZSk7XHJcblx0XHRpZiAoIUwuQnJvd3Nlci5lZGdlKSB7XHJcblx0XHRcdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGRibGNsaWNrLCBmYWxzZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fVxyXG59KTtcclxuXG5cblxuLypcbiAqIEV4dGVuZHMgTC5Eb21FdmVudCB0byBwcm92aWRlIHRvdWNoIHN1cHBvcnQgZm9yIEludGVybmV0IEV4cGxvcmVyIGFuZCBXaW5kb3dzLWJhc2VkIGRldmljZXMuXG4gKi9cblxuTC5leHRlbmQoTC5Eb21FdmVudCwge1xuXG5cdFBPSU5URVJfRE9XTjogICBMLkJyb3dzZXIubXNQb2ludGVyID8gJ01TUG9pbnRlckRvd24nICAgOiAncG9pbnRlcmRvd24nLFxuXHRQT0lOVEVSX01PVkU6ICAgTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJNb3ZlJyAgIDogJ3BvaW50ZXJtb3ZlJyxcblx0UE9JTlRFUl9VUDogICAgIEwuQnJvd3Nlci5tc1BvaW50ZXIgPyAnTVNQb2ludGVyVXAnICAgICA6ICdwb2ludGVydXAnLFxuXHRQT0lOVEVSX0NBTkNFTDogTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJDYW5jZWwnIDogJ3BvaW50ZXJjYW5jZWwnLFxuXHRUQUdfV0hJVEVfTElTVDogWydJTlBVVCcsICdTRUxFQ1QnLCAnT1BUSU9OJ10sXG5cblx0X3BvaW50ZXJzOiB7fSxcblx0X3BvaW50ZXJzQ291bnQ6IDAsXG5cblx0Ly8gUHJvdmlkZXMgYSB0b3VjaCBldmVudHMgd3JhcHBlciBmb3IgKG1zKXBvaW50ZXIgZXZlbnRzLlxuXHQvLyByZWYgaHR0cDovL3d3dy53My5vcmcvVFIvcG9pbnRlcmV2ZW50cy8gaHR0cHM6Ly93d3cudzMub3JnL0J1Z3MvUHVibGljL3Nob3dfYnVnLmNnaT9pZD0yMjg5MFxuXG5cdGFkZFBvaW50ZXJMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgaGFuZGxlciwgaWQpIHtcblxuXHRcdGlmICh0eXBlID09PSAndG91Y2hzdGFydCcpIHtcblx0XHRcdHRoaXMuX2FkZFBvaW50ZXJTdGFydChvYmosIGhhbmRsZXIsIGlkKTtcblxuXHRcdH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNobW92ZScpIHtcblx0XHRcdHRoaXMuX2FkZFBvaW50ZXJNb3ZlKG9iaiwgaGFuZGxlciwgaWQpO1xuXG5cdFx0fSBlbHNlIGlmICh0eXBlID09PSAndG91Y2hlbmQnKSB7XG5cdFx0XHR0aGlzLl9hZGRQb2ludGVyRW5kKG9iaiwgaGFuZGxlciwgaWQpO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdHJlbW92ZVBvaW50ZXJMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgaWQpIHtcblx0XHR2YXIgaGFuZGxlciA9IG9ialsnX2xlYWZsZXRfJyArIHR5cGUgKyBpZF07XG5cblx0XHRpZiAodHlwZSA9PT0gJ3RvdWNoc3RhcnQnKSB7XG5cdFx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfRE9XTiwgaGFuZGxlciwgZmFsc2UpO1xuXG5cdFx0fSBlbHNlIGlmICh0eXBlID09PSAndG91Y2htb3ZlJykge1xuXHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX01PVkUsIGhhbmRsZXIsIGZhbHNlKTtcblxuXHRcdH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNoZW5kJykge1xuXHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX1VQLCBoYW5kbGVyLCBmYWxzZSk7XG5cdFx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfQ0FOQ0VMLCBoYW5kbGVyLCBmYWxzZSk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X2FkZFBvaW50ZXJTdGFydDogZnVuY3Rpb24gKG9iaiwgaGFuZGxlciwgaWQpIHtcblx0XHR2YXIgb25Eb3duID0gTC5iaW5kKGZ1bmN0aW9uIChlKSB7XG5cdFx0XHRpZiAoZS5wb2ludGVyVHlwZSAhPT0gJ21vdXNlJyAmJiBlLk1TUE9JTlRFUl9UWVBFX01PVVNFICYmIGUucG9pbnRlclR5cGUgIT09IGUuTVNQT0lOVEVSX1RZUEVfTU9VU0UpIHtcblx0XHRcdFx0Ly8gSW4gSUUxMSwgc29tZSB0b3VjaCBldmVudHMgbmVlZHMgdG8gZmlyZSBmb3IgZm9ybSBjb250cm9scywgb3Jcblx0XHRcdFx0Ly8gdGhlIGNvbnRyb2xzIHdpbGwgc3RvcCB3b3JraW5nLiBXZSBrZWVwIGEgd2hpdGVsaXN0IG9mIHRhZyBuYW1lcyB0aGF0XG5cdFx0XHRcdC8vIG5lZWQgdGhlc2UgZXZlbnRzLiBGb3Igb3RoZXIgdGFyZ2V0IHRhZ3MsIHdlIHByZXZlbnQgZGVmYXVsdCBvbiB0aGUgZXZlbnQuXG5cdFx0XHRcdGlmICh0aGlzLlRBR19XSElURV9MSVNULmluZGV4T2YoZS50YXJnZXQudGFnTmFtZSkgPCAwKSB7XG5cdFx0XHRcdFx0TC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdChlKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5faGFuZGxlUG9pbnRlcihlLCBoYW5kbGVyKTtcblx0XHR9LCB0aGlzKTtcblxuXHRcdG9ialsnX2xlYWZsZXRfdG91Y2hzdGFydCcgKyBpZF0gPSBvbkRvd247XG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX0RPV04sIG9uRG93biwgZmFsc2UpO1xuXG5cdFx0Ly8gbmVlZCB0byBrZWVwIHRyYWNrIG9mIHdoYXQgcG9pbnRlcnMgYW5kIGhvdyBtYW55IGFyZSBhY3RpdmUgdG8gcHJvdmlkZSBlLnRvdWNoZXMgZW11bGF0aW9uXG5cdFx0aWYgKCF0aGlzLl9wb2ludGVyRG9jTGlzdGVuZXIpIHtcblx0XHRcdHZhciBwb2ludGVyVXAgPSBMLmJpbmQodGhpcy5fZ2xvYmFsUG9pbnRlclVwLCB0aGlzKTtcblxuXHRcdFx0Ly8gd2UgbGlzdGVuIGRvY3VtZW50RWxlbWVudCBhcyBhbnkgZHJhZ3MgdGhhdCBlbmQgYnkgbW92aW5nIHRoZSB0b3VjaCBvZmYgdGhlIHNjcmVlbiBnZXQgZmlyZWQgdGhlcmVcblx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9ET1dOLCBMLmJpbmQodGhpcy5fZ2xvYmFsUG9pbnRlckRvd24sIHRoaXMpLCB0cnVlKTtcblx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9NT1ZFLCBMLmJpbmQodGhpcy5fZ2xvYmFsUG9pbnRlck1vdmUsIHRoaXMpLCB0cnVlKTtcblx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9VUCwgcG9pbnRlclVwLCB0cnVlKTtcblx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9DQU5DRUwsIHBvaW50ZXJVcCwgdHJ1ZSk7XG5cblx0XHRcdHRoaXMuX3BvaW50ZXJEb2NMaXN0ZW5lciA9IHRydWU7XG5cdFx0fVxuXHR9LFxuXG5cdF9nbG9iYWxQb2ludGVyRG93bjogZnVuY3Rpb24gKGUpIHtcblx0XHR0aGlzLl9wb2ludGVyc1tlLnBvaW50ZXJJZF0gPSBlO1xuXHRcdHRoaXMuX3BvaW50ZXJzQ291bnQrKztcblx0fSxcblxuXHRfZ2xvYmFsUG9pbnRlck1vdmU6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKHRoaXMuX3BvaW50ZXJzW2UucG9pbnRlcklkXSkge1xuXHRcdFx0dGhpcy5fcG9pbnRlcnNbZS5wb2ludGVySWRdID0gZTtcblx0XHR9XG5cdH0sXG5cblx0X2dsb2JhbFBvaW50ZXJVcDogZnVuY3Rpb24gKGUpIHtcblx0XHRkZWxldGUgdGhpcy5fcG9pbnRlcnNbZS5wb2ludGVySWRdO1xuXHRcdHRoaXMuX3BvaW50ZXJzQ291bnQtLTtcblx0fSxcblxuXHRfaGFuZGxlUG9pbnRlcjogZnVuY3Rpb24gKGUsIGhhbmRsZXIpIHtcblx0XHRlLnRvdWNoZXMgPSBbXTtcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX3BvaW50ZXJzKSB7XG5cdFx0XHRlLnRvdWNoZXMucHVzaCh0aGlzLl9wb2ludGVyc1tpXSk7XG5cdFx0fVxuXHRcdGUuY2hhbmdlZFRvdWNoZXMgPSBbZV07XG5cblx0XHRoYW5kbGVyKGUpO1xuXHR9LFxuXG5cdF9hZGRQb2ludGVyTW92ZTogZnVuY3Rpb24gKG9iaiwgaGFuZGxlciwgaWQpIHtcblx0XHR2YXIgb25Nb3ZlID0gTC5iaW5kKGZ1bmN0aW9uIChlKSB7XG5cdFx0XHQvLyBkb24ndCBmaXJlIHRvdWNoIG1vdmVzIHdoZW4gbW91c2UgaXNuJ3QgZG93blxuXHRcdFx0aWYgKChlLnBvaW50ZXJUeXBlID09PSBlLk1TUE9JTlRFUl9UWVBFX01PVVNFIHx8IGUucG9pbnRlclR5cGUgPT09ICdtb3VzZScpICYmIGUuYnV0dG9ucyA9PT0gMCkgeyByZXR1cm47IH1cblxuXHRcdFx0dGhpcy5faGFuZGxlUG9pbnRlcihlLCBoYW5kbGVyKTtcblx0XHR9LCB0aGlzKTtcblxuXHRcdG9ialsnX2xlYWZsZXRfdG91Y2htb3ZlJyArIGlkXSA9IG9uTW92ZTtcblx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfTU9WRSwgb25Nb3ZlLCBmYWxzZSk7XG5cdH0sXG5cblx0X2FkZFBvaW50ZXJFbmQ6IGZ1bmN0aW9uIChvYmosIGhhbmRsZXIsIGlkKSB7XG5cdFx0dmFyIG9uVXAgPSBMLmJpbmQoZnVuY3Rpb24gKGUpIHtcblx0XHRcdHRoaXMuX2hhbmRsZVBvaW50ZXIoZSwgaGFuZGxlcik7XG5cdFx0fSwgdGhpcyk7XG5cblx0XHRvYmpbJ19sZWFmbGV0X3RvdWNoZW5kJyArIGlkXSA9IG9uVXA7XG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX1VQLCBvblVwLCBmYWxzZSk7XG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX0NBTkNFTCwgb25VcCwgZmFsc2UpO1xuXHR9XG59KTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuVG91Y2hab29tIGlzIHVzZWQgYnkgTC5NYXAgdG8gYWRkIHBpbmNoIHpvb20gb24gc3VwcG9ydGVkIG1vYmlsZSBicm93c2Vycy5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuTC5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQHNlY3Rpb24gVG91Y2ggaW50ZXJhY3Rpb24gb3B0aW9uc1xuXHQvLyBAb3B0aW9uIHRvdWNoWm9vbTogQm9vbGVhbnxTdHJpbmcgPSAqXG5cdC8vIFdoZXRoZXIgdGhlIG1hcCBjYW4gYmUgem9vbWVkIGJ5IHRvdWNoLWRyYWdnaW5nIHdpdGggdHdvIGZpbmdlcnMuIElmXG5cdC8vIHBhc3NlZCBgJ2NlbnRlcidgLCBpdCB3aWxsIHpvb20gdG8gdGhlIGNlbnRlciBvZiB0aGUgdmlldyByZWdhcmRsZXNzIG9mXG5cdC8vIHdoZXJlIHRoZSB0b3VjaCBldmVudHMgKGZpbmdlcnMpIHdlcmUuIEVuYWJsZWQgZm9yIHRvdWNoLWNhcGFibGUgd2ViXG5cdC8vIGJyb3dzZXJzIGV4Y2VwdCBmb3Igb2xkIEFuZHJvaWRzLlxuXHR0b3VjaFpvb206IEwuQnJvd3Nlci50b3VjaCAmJiAhTC5Ccm93c2VyLmFuZHJvaWQyMyxcblxuXHQvLyBAb3B0aW9uIGJvdW5jZUF0Wm9vbUxpbWl0czogQm9vbGVhbiA9IHRydWVcblx0Ly8gU2V0IGl0IHRvIGZhbHNlIGlmIHlvdSBkb24ndCB3YW50IHRoZSBtYXAgdG8gem9vbSBiZXlvbmQgbWluL21heCB6b29tXG5cdC8vIGFuZCB0aGVuIGJvdW5jZSBiYWNrIHdoZW4gcGluY2gtem9vbWluZy5cblx0Ym91bmNlQXRab29tTGltaXRzOiB0cnVlXG59KTtcblxuTC5NYXAuVG91Y2hab29tID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC10b3VjaC16b29tJyk7XG5cdFx0TC5Eb21FdmVudC5vbih0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ3RvdWNoc3RhcnQnLCB0aGlzLl9vblRvdWNoU3RhcnQsIHRoaXMpO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC10b3VjaC16b29tJyk7XG5cdFx0TC5Eb21FdmVudC5vZmYodGhpcy5fbWFwLl9jb250YWluZXIsICd0b3VjaHN0YXJ0JywgdGhpcy5fb25Ub3VjaFN0YXJ0LCB0aGlzKTtcblx0fSxcblxuXHRfb25Ub3VjaFN0YXJ0OiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXA7XG5cdFx0aWYgKCFlLnRvdWNoZXMgfHwgZS50b3VjaGVzLmxlbmd0aCAhPT0gMiB8fCBtYXAuX2FuaW1hdGluZ1pvb20gfHwgdGhpcy5fem9vbWluZykgeyByZXR1cm47IH1cblxuXHRcdHZhciBwMSA9IG1hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLnRvdWNoZXNbMF0pLFxuXHRcdCAgICBwMiA9IG1hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLnRvdWNoZXNbMV0pO1xuXG5cdFx0dGhpcy5fY2VudGVyUG9pbnQgPSBtYXAuZ2V0U2l6ZSgpLl9kaXZpZGVCeSgyKTtcblx0XHR0aGlzLl9zdGFydExhdExuZyA9IG1hcC5jb250YWluZXJQb2ludFRvTGF0TG5nKHRoaXMuX2NlbnRlclBvaW50KTtcblx0XHRpZiAobWFwLm9wdGlvbnMudG91Y2hab29tICE9PSAnY2VudGVyJykge1xuXHRcdFx0dGhpcy5fcGluY2hTdGFydExhdExuZyA9IG1hcC5jb250YWluZXJQb2ludFRvTGF0TG5nKHAxLmFkZChwMikuX2RpdmlkZUJ5KDIpKTtcblx0XHR9XG5cblx0XHR0aGlzLl9zdGFydERpc3QgPSBwMS5kaXN0YW5jZVRvKHAyKTtcblx0XHR0aGlzLl9zdGFydFpvb20gPSBtYXAuZ2V0Wm9vbSgpO1xuXG5cdFx0dGhpcy5fbW92ZWQgPSBmYWxzZTtcblx0XHR0aGlzLl96b29taW5nID0gdHJ1ZTtcblxuXHRcdG1hcC5fc3RvcCgpO1xuXG5cdFx0TC5Eb21FdmVudFxuXHRcdCAgICAub24oZG9jdW1lbnQsICd0b3VjaG1vdmUnLCB0aGlzLl9vblRvdWNoTW92ZSwgdGhpcylcblx0XHQgICAgLm9uKGRvY3VtZW50LCAndG91Y2hlbmQnLCB0aGlzLl9vblRvdWNoRW5kLCB0aGlzKTtcblxuXHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG5cdH0sXG5cblx0X29uVG91Y2hNb3ZlOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmICghZS50b3VjaGVzIHx8IGUudG91Y2hlcy5sZW5ndGggIT09IDIgfHwgIXRoaXMuX3pvb21pbmcpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBwMSA9IG1hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLnRvdWNoZXNbMF0pLFxuXHRcdCAgICBwMiA9IG1hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLnRvdWNoZXNbMV0pLFxuXHRcdCAgICBzY2FsZSA9IHAxLmRpc3RhbmNlVG8ocDIpIC8gdGhpcy5fc3RhcnREaXN0O1xuXG5cblx0XHR0aGlzLl96b29tID0gbWFwLmdldFNjYWxlWm9vbShzY2FsZSwgdGhpcy5fc3RhcnRab29tKTtcblxuXHRcdGlmICghbWFwLm9wdGlvbnMuYm91bmNlQXRab29tTGltaXRzICYmIChcblx0XHRcdCh0aGlzLl96b29tIDwgbWFwLmdldE1pblpvb20oKSAmJiBzY2FsZSA8IDEpIHx8XG5cdFx0XHQodGhpcy5fem9vbSA+IG1hcC5nZXRNYXhab29tKCkgJiYgc2NhbGUgPiAxKSkpIHtcblx0XHRcdHRoaXMuX3pvb20gPSBtYXAuX2xpbWl0Wm9vbSh0aGlzLl96b29tKTtcblx0XHR9XG5cblx0XHRpZiAobWFwLm9wdGlvbnMudG91Y2hab29tID09PSAnY2VudGVyJykge1xuXHRcdFx0dGhpcy5fY2VudGVyID0gdGhpcy5fc3RhcnRMYXRMbmc7XG5cdFx0XHRpZiAoc2NhbGUgPT09IDEpIHsgcmV0dXJuOyB9XG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIEdldCBkZWx0YSBmcm9tIHBpbmNoIHRvIGNlbnRlciwgc28gY2VudGVyTGF0TG5nIGlzIGRlbHRhIGFwcGxpZWQgdG8gaW5pdGlhbCBwaW5jaExhdExuZ1xuXHRcdFx0dmFyIGRlbHRhID0gcDEuX2FkZChwMikuX2RpdmlkZUJ5KDIpLl9zdWJ0cmFjdCh0aGlzLl9jZW50ZXJQb2ludCk7XG5cdFx0XHRpZiAoc2NhbGUgPT09IDEgJiYgZGVsdGEueCA9PT0gMCAmJiBkZWx0YS55ID09PSAwKSB7IHJldHVybjsgfVxuXHRcdFx0dGhpcy5fY2VudGVyID0gbWFwLnVucHJvamVjdChtYXAucHJvamVjdCh0aGlzLl9waW5jaFN0YXJ0TGF0TG5nLCB0aGlzLl96b29tKS5zdWJ0cmFjdChkZWx0YSksIHRoaXMuX3pvb20pO1xuXHRcdH1cblxuXHRcdGlmICghdGhpcy5fbW92ZWQpIHtcblx0XHRcdG1hcC5fbW92ZVN0YXJ0KHRydWUpO1xuXHRcdFx0dGhpcy5fbW92ZWQgPSB0cnVlO1xuXHRcdH1cblxuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fYW5pbVJlcXVlc3QpO1xuXG5cdFx0dmFyIG1vdmVGbiA9IEwuYmluZChtYXAuX21vdmUsIG1hcCwgdGhpcy5fY2VudGVyLCB0aGlzLl96b29tLCB7cGluY2g6IHRydWUsIHJvdW5kOiBmYWxzZX0pO1xuXHRcdHRoaXMuX2FuaW1SZXF1ZXN0ID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUobW92ZUZuLCB0aGlzLCB0cnVlKTtcblxuXHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG5cdH0sXG5cblx0X29uVG91Y2hFbmQ6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21vdmVkIHx8ICF0aGlzLl96b29taW5nKSB7XG5cdFx0XHR0aGlzLl96b29taW5nID0gZmFsc2U7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dGhpcy5fem9vbWluZyA9IGZhbHNlO1xuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fYW5pbVJlcXVlc3QpO1xuXG5cdFx0TC5Eb21FdmVudFxuXHRcdCAgICAub2ZmKGRvY3VtZW50LCAndG91Y2htb3ZlJywgdGhpcy5fb25Ub3VjaE1vdmUpXG5cdFx0ICAgIC5vZmYoZG9jdW1lbnQsICd0b3VjaGVuZCcsIHRoaXMuX29uVG91Y2hFbmQpO1xuXG5cdFx0Ly8gUGluY2ggdXBkYXRlcyBHcmlkTGF5ZXJzJyBsZXZlbHMgb25seSB3aGVuIHpvb21TbmFwIGlzIG9mZiwgc28gem9vbVNuYXAgYmVjb21lcyBub1VwZGF0ZS5cblx0XHRpZiAodGhpcy5fbWFwLm9wdGlvbnMuem9vbUFuaW1hdGlvbikge1xuXHRcdFx0dGhpcy5fbWFwLl9hbmltYXRlWm9vbSh0aGlzLl9jZW50ZXIsIHRoaXMuX21hcC5fbGltaXRab29tKHRoaXMuX3pvb20pLCB0cnVlLCB0aGlzLl9tYXAub3B0aW9ucy56b29tU25hcCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX21hcC5fcmVzZXRWaWV3KHRoaXMuX2NlbnRlciwgdGhpcy5fbWFwLl9saW1pdFpvb20odGhpcy5fem9vbSkpO1xuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgdG91Y2hab29tOiBIYW5kbGVyXG4vLyBUb3VjaCB6b29tIGhhbmRsZXIuXG5MLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICd0b3VjaFpvb20nLCBMLk1hcC5Ub3VjaFpvb20pO1xuXG5cblxuLypcbiAqIEwuTWFwLlRhcCBpcyB1c2VkIHRvIGVuYWJsZSBtb2JpbGUgaGFja3MgbGlrZSBxdWljayB0YXBzIGFuZCBsb25nIGhvbGQuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBzZWN0aW9uIFRvdWNoIGludGVyYWN0aW9uIG9wdGlvbnNcblx0Ly8gQG9wdGlvbiB0YXA6IEJvb2xlYW4gPSB0cnVlXG5cdC8vIEVuYWJsZXMgbW9iaWxlIGhhY2tzIGZvciBzdXBwb3J0aW5nIGluc3RhbnQgdGFwcyAoZml4aW5nIDIwMG1zIGNsaWNrXG5cdC8vIGRlbGF5IG9uIGlPUy9BbmRyb2lkKSBhbmQgdG91Y2ggaG9sZHMgKGZpcmVkIGFzIGBjb250ZXh0bWVudWAgZXZlbnRzKS5cblx0dGFwOiB0cnVlLFxuXG5cdC8vIEBvcHRpb24gdGFwVG9sZXJhbmNlOiBOdW1iZXIgPSAxNVxuXHQvLyBUaGUgbWF4IG51bWJlciBvZiBwaXhlbHMgYSB1c2VyIGNhbiBzaGlmdCBoaXMgZmluZ2VyIGR1cmluZyB0b3VjaFxuXHQvLyBmb3IgaXQgdG8gYmUgY29uc2lkZXJlZCBhIHZhbGlkIHRhcC5cblx0dGFwVG9sZXJhbmNlOiAxNVxufSk7XG5cbkwuTWFwLlRhcCA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub24odGhpcy5fbWFwLl9jb250YWluZXIsICd0b3VjaHN0YXJ0JywgdGhpcy5fb25Eb3duLCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX21hcC5fY29udGFpbmVyLCAndG91Y2hzdGFydCcsIHRoaXMuX29uRG93biwgdGhpcyk7XG5cdH0sXG5cblx0X29uRG93bjogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIWUudG91Y2hlcykgeyByZXR1cm47IH1cblxuXHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG5cblx0XHR0aGlzLl9maXJlQ2xpY2sgPSB0cnVlO1xuXG5cdFx0Ly8gZG9uJ3Qgc2ltdWxhdGUgY2xpY2sgb3IgdHJhY2sgbG9uZ3ByZXNzIGlmIG1vcmUgdGhhbiAxIHRvdWNoXG5cdFx0aWYgKGUudG91Y2hlcy5sZW5ndGggPiAxKSB7XG5cdFx0XHR0aGlzLl9maXJlQ2xpY2sgPSBmYWxzZTtcblx0XHRcdGNsZWFyVGltZW91dCh0aGlzLl9ob2xkVGltZW91dCk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dmFyIGZpcnN0ID0gZS50b3VjaGVzWzBdLFxuXHRcdCAgICBlbCA9IGZpcnN0LnRhcmdldDtcblxuXHRcdHRoaXMuX3N0YXJ0UG9zID0gdGhpcy5fbmV3UG9zID0gbmV3IEwuUG9pbnQoZmlyc3QuY2xpZW50WCwgZmlyc3QuY2xpZW50WSk7XG5cblx0XHQvLyBpZiB0b3VjaGluZyBhIGxpbmssIGhpZ2hsaWdodCBpdFxuXHRcdGlmIChlbC50YWdOYW1lICYmIGVsLnRhZ05hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2EnKSB7XG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoZWwsICdsZWFmbGV0LWFjdGl2ZScpO1xuXHRcdH1cblxuXHRcdC8vIHNpbXVsYXRlIGxvbmcgaG9sZCBidXQgc2V0dGluZyBhIHRpbWVvdXRcblx0XHR0aGlzLl9ob2xkVGltZW91dCA9IHNldFRpbWVvdXQoTC5iaW5kKGZ1bmN0aW9uICgpIHtcblx0XHRcdGlmICh0aGlzLl9pc1RhcFZhbGlkKCkpIHtcblx0XHRcdFx0dGhpcy5fZmlyZUNsaWNrID0gZmFsc2U7XG5cdFx0XHRcdHRoaXMuX29uVXAoKTtcblx0XHRcdFx0dGhpcy5fc2ltdWxhdGVFdmVudCgnY29udGV4dG1lbnUnLCBmaXJzdCk7XG5cdFx0XHR9XG5cdFx0fSwgdGhpcyksIDEwMDApO1xuXG5cdFx0dGhpcy5fc2ltdWxhdGVFdmVudCgnbW91c2Vkb3duJywgZmlyc3QpO1xuXG5cdFx0TC5Eb21FdmVudC5vbihkb2N1bWVudCwge1xuXHRcdFx0dG91Y2htb3ZlOiB0aGlzLl9vbk1vdmUsXG5cdFx0XHR0b3VjaGVuZDogdGhpcy5fb25VcFxuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vblVwOiBmdW5jdGlvbiAoZSkge1xuXHRcdGNsZWFyVGltZW91dCh0aGlzLl9ob2xkVGltZW91dCk7XG5cblx0XHRMLkRvbUV2ZW50Lm9mZihkb2N1bWVudCwge1xuXHRcdFx0dG91Y2htb3ZlOiB0aGlzLl9vbk1vdmUsXG5cdFx0XHR0b3VjaGVuZDogdGhpcy5fb25VcFxuXHRcdH0sIHRoaXMpO1xuXG5cdFx0aWYgKHRoaXMuX2ZpcmVDbGljayAmJiBlICYmIGUuY2hhbmdlZFRvdWNoZXMpIHtcblxuXHRcdFx0dmFyIGZpcnN0ID0gZS5jaGFuZ2VkVG91Y2hlc1swXSxcblx0XHRcdCAgICBlbCA9IGZpcnN0LnRhcmdldDtcblxuXHRcdFx0aWYgKGVsICYmIGVsLnRhZ05hbWUgJiYgZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpID09PSAnYScpIHtcblx0XHRcdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKGVsLCAnbGVhZmxldC1hY3RpdmUnKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fc2ltdWxhdGVFdmVudCgnbW91c2V1cCcsIGZpcnN0KTtcblxuXHRcdFx0Ly8gc2ltdWxhdGUgY2xpY2sgaWYgdGhlIHRvdWNoIGRpZG4ndCBtb3ZlIHRvbyBtdWNoXG5cdFx0XHRpZiAodGhpcy5faXNUYXBWYWxpZCgpKSB7XG5cdFx0XHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ2NsaWNrJywgZmlyc3QpO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRfaXNUYXBWYWxpZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9uZXdQb3MuZGlzdGFuY2VUbyh0aGlzLl9zdGFydFBvcykgPD0gdGhpcy5fbWFwLm9wdGlvbnMudGFwVG9sZXJhbmNlO1xuXHR9LFxuXG5cdF9vbk1vdmU6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGZpcnN0ID0gZS50b3VjaGVzWzBdO1xuXHRcdHRoaXMuX25ld1BvcyA9IG5ldyBMLlBvaW50KGZpcnN0LmNsaWVudFgsIGZpcnN0LmNsaWVudFkpO1xuXHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ21vdXNlbW92ZScsIGZpcnN0KTtcblx0fSxcblxuXHRfc2ltdWxhdGVFdmVudDogZnVuY3Rpb24gKHR5cGUsIGUpIHtcblx0XHR2YXIgc2ltdWxhdGVkRXZlbnQgPSBkb2N1bWVudC5jcmVhdGVFdmVudCgnTW91c2VFdmVudHMnKTtcblxuXHRcdHNpbXVsYXRlZEV2ZW50Ll9zaW11bGF0ZWQgPSB0cnVlO1xuXHRcdGUudGFyZ2V0Ll9zaW11bGF0ZWRDbGljayA9IHRydWU7XG5cblx0XHRzaW11bGF0ZWRFdmVudC5pbml0TW91c2VFdmVudChcblx0XHQgICAgICAgIHR5cGUsIHRydWUsIHRydWUsIHdpbmRvdywgMSxcblx0XHQgICAgICAgIGUuc2NyZWVuWCwgZS5zY3JlZW5ZLFxuXHRcdCAgICAgICAgZS5jbGllbnRYLCBlLmNsaWVudFksXG5cdFx0ICAgICAgICBmYWxzZSwgZmFsc2UsIGZhbHNlLCBmYWxzZSwgMCwgbnVsbCk7XG5cblx0XHRlLnRhcmdldC5kaXNwYXRjaEV2ZW50KHNpbXVsYXRlZEV2ZW50KTtcblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgdGFwOiBIYW5kbGVyXG4vLyBNb2JpbGUgdG91Y2ggaGFja3MgKHF1aWNrIHRhcCBhbmQgdG91Y2ggaG9sZCkgaGFuZGxlci5cbmlmIChMLkJyb3dzZXIudG91Y2ggJiYgIUwuQnJvd3Nlci5wb2ludGVyKSB7XG5cdEwuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ3RhcCcsIEwuTWFwLlRhcCk7XG59XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLkJveFpvb20gaXMgdXNlZCB0byBhZGQgc2hpZnQtZHJhZyB6b29tIGludGVyYWN0aW9uIHRvIHRoZSBtYXBcbiAqICh6b29tIHRvIGEgc2VsZWN0ZWQgYm91bmRpbmcgYm94KSwgZW5hYmxlZCBieSBkZWZhdWx0LlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAb3B0aW9uIGJveFpvb206IEJvb2xlYW4gPSB0cnVlXG5cdC8vIFdoZXRoZXIgdGhlIG1hcCBjYW4gYmUgem9vbWVkIHRvIGEgcmVjdGFuZ3VsYXIgYXJlYSBzcGVjaWZpZWQgYnlcblx0Ly8gZHJhZ2dpbmcgdGhlIG1vdXNlIHdoaWxlIHByZXNzaW5nIHRoZSBzaGlmdCBrZXkuXG5cdGJveFpvb206IHRydWVcbn0pO1xuXG5MLk1hcC5Cb3hab29tID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHR0aGlzLl9tYXAgPSBtYXA7XG5cdFx0dGhpcy5fY29udGFpbmVyID0gbWFwLl9jb250YWluZXI7XG5cdFx0dGhpcy5fcGFuZSA9IG1hcC5fcGFuZXMub3ZlcmxheVBhbmU7XG5cdH0sXG5cblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9uKHRoaXMuX2NvbnRhaW5lciwgJ21vdXNlZG93bicsIHRoaXMuX29uTW91c2VEb3duLCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX2NvbnRhaW5lciwgJ21vdXNlZG93bicsIHRoaXMuX29uTW91c2VEb3duLCB0aGlzKTtcblx0fSxcblxuXHRtb3ZlZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9tb3ZlZDtcblx0fSxcblxuXHRfcmVzZXRTdGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XG5cdH0sXG5cblx0X29uTW91c2VEb3duOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmICghZS5zaGlmdEtleSB8fCAoKGUud2hpY2ggIT09IDEpICYmIChlLmJ1dHRvbiAhPT0gMSkpKSB7IHJldHVybiBmYWxzZTsgfVxuXG5cdFx0dGhpcy5fcmVzZXRTdGF0ZSgpO1xuXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uKCk7XG5cdFx0TC5Eb21VdGlsLmRpc2FibGVJbWFnZURyYWcoKTtcblxuXHRcdHRoaXMuX3N0YXJ0UG9pbnQgPSB0aGlzLl9tYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZSk7XG5cblx0XHRMLkRvbUV2ZW50Lm9uKGRvY3VtZW50LCB7XG5cdFx0XHRjb250ZXh0bWVudTogTC5Eb21FdmVudC5zdG9wLFxuXHRcdFx0bW91c2Vtb3ZlOiB0aGlzLl9vbk1vdXNlTW92ZSxcblx0XHRcdG1vdXNldXA6IHRoaXMuX29uTW91c2VVcCxcblx0XHRcdGtleWRvd246IHRoaXMuX29uS2V5RG93blxuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbk1vdXNlTW92ZTogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIXRoaXMuX21vdmVkKSB7XG5cdFx0XHR0aGlzLl9tb3ZlZCA9IHRydWU7XG5cblx0XHRcdHRoaXMuX2JveCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXpvb20tYm94JywgdGhpcy5fY29udGFpbmVyKTtcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWNyb3NzaGFpcicpO1xuXG5cdFx0XHR0aGlzLl9tYXAuZmlyZSgnYm94em9vbXN0YXJ0Jyk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fcG9pbnQgPSB0aGlzLl9tYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZSk7XG5cblx0XHR2YXIgYm91bmRzID0gbmV3IEwuQm91bmRzKHRoaXMuX3BvaW50LCB0aGlzLl9zdGFydFBvaW50KSxcblx0XHQgICAgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XG5cblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fYm94LCBib3VuZHMubWluKTtcblxuXHRcdHRoaXMuX2JveC5zdHlsZS53aWR0aCAgPSBzaXplLnggKyAncHgnO1xuXHRcdHRoaXMuX2JveC5zdHlsZS5oZWlnaHQgPSBzaXplLnkgKyAncHgnO1xuXHR9LFxuXG5cdF9maW5pc2g6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbW92ZWQpIHtcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fYm94KTtcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWNyb3NzaGFpcicpO1xuXHRcdH1cblxuXHRcdEwuRG9tVXRpbC5lbmFibGVUZXh0U2VsZWN0aW9uKCk7XG5cdFx0TC5Eb21VdGlsLmVuYWJsZUltYWdlRHJhZygpO1xuXG5cdFx0TC5Eb21FdmVudC5vZmYoZG9jdW1lbnQsIHtcblx0XHRcdGNvbnRleHRtZW51OiBMLkRvbUV2ZW50LnN0b3AsXG5cdFx0XHRtb3VzZW1vdmU6IHRoaXMuX29uTW91c2VNb3ZlLFxuXHRcdFx0bW91c2V1cDogdGhpcy5fb25Nb3VzZVVwLFxuXHRcdFx0a2V5ZG93bjogdGhpcy5fb25LZXlEb3duXG5cdFx0fSwgdGhpcyk7XG5cdH0sXG5cblx0X29uTW91c2VVcDogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoKGUud2hpY2ggIT09IDEpICYmIChlLmJ1dHRvbiAhPT0gMSkpIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9maW5pc2goKTtcblxuXHRcdGlmICghdGhpcy5fbW92ZWQpIHsgcmV0dXJuOyB9XG5cdFx0Ly8gUG9zdHBvbmUgdG8gbmV4dCBKUyB0aWNrIHNvIGludGVybmFsIGNsaWNrIGV2ZW50IGhhbmRsaW5nXG5cdFx0Ly8gc3RpbGwgc2VlIGl0IGFzIFwibW92ZWRcIi5cblx0XHRzZXRUaW1lb3V0KEwuYmluZCh0aGlzLl9yZXNldFN0YXRlLCB0aGlzKSwgMCk7XG5cblx0XHR2YXIgYm91bmRzID0gbmV3IEwuTGF0TG5nQm91bmRzKFxuXHRcdCAgICAgICAgdGhpcy5fbWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcodGhpcy5fc3RhcnRQb2ludCksXG5cdFx0ICAgICAgICB0aGlzLl9tYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyh0aGlzLl9wb2ludCkpO1xuXG5cdFx0dGhpcy5fbWFwXG5cdFx0XHQuZml0Qm91bmRzKGJvdW5kcylcblx0XHRcdC5maXJlKCdib3h6b29tZW5kJywge2JveFpvb21Cb3VuZHM6IGJvdW5kc30pO1xuXHR9LFxuXG5cdF9vbktleURvd246IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKGUua2V5Q29kZSA9PT0gMjcpIHtcblx0XHRcdHRoaXMuX2ZpbmlzaCgpO1xuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgYm94Wm9vbTogSGFuZGxlclxuLy8gQm94IChzaGlmdC1kcmFnIHdpdGggbW91c2UpIHpvb20gaGFuZGxlci5cbkwuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ2JveFpvb20nLCBMLk1hcC5Cb3hab29tKTtcblxuXG5cbi8qXG4gKiBMLk1hcC5LZXlib2FyZCBpcyBoYW5kbGluZyBrZXlib2FyZCBpbnRlcmFjdGlvbiB3aXRoIHRoZSBtYXAsIGVuYWJsZWQgYnkgZGVmYXVsdC5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gS2V5Ym9hcmQgTmF2aWdhdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAb3B0aW9uIGtleWJvYXJkOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBNYWtlcyB0aGUgbWFwIGZvY3VzYWJsZSBhbmQgYWxsb3dzIHVzZXJzIHRvIG5hdmlnYXRlIHRoZSBtYXAgd2l0aCBrZXlib2FyZFxuXHQvLyBhcnJvd3MgYW5kIGArYC9gLWAga2V5cy5cblx0a2V5Ym9hcmQ6IHRydWUsXG5cblx0Ly8gQG9wdGlvbiBrZXlib2FyZFBhbkRlbHRhOiBOdW1iZXIgPSA4MFxuXHQvLyBBbW91bnQgb2YgcGl4ZWxzIHRvIHBhbiB3aGVuIHByZXNzaW5nIGFuIGFycm93IGtleS5cblx0a2V5Ym9hcmRQYW5EZWx0YTogODBcbn0pO1xuXG5MLk1hcC5LZXlib2FyZCA9IEwuSGFuZGxlci5leHRlbmQoe1xuXG5cdGtleUNvZGVzOiB7XG5cdFx0bGVmdDogICAgWzM3XSxcblx0XHRyaWdodDogICBbMzldLFxuXHRcdGRvd246ICAgIFs0MF0sXG5cdFx0dXA6ICAgICAgWzM4XSxcblx0XHR6b29tSW46ICBbMTg3LCAxMDcsIDYxLCAxNzFdLFxuXHRcdHpvb21PdXQ6IFsxODksIDEwOSwgNTQsIDE3M11cblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0dGhpcy5fbWFwID0gbWFwO1xuXG5cdFx0dGhpcy5fc2V0UGFuRGVsdGEobWFwLm9wdGlvbnMua2V5Ym9hcmRQYW5EZWx0YSk7XG5cdFx0dGhpcy5fc2V0Wm9vbURlbHRhKG1hcC5vcHRpb25zLnpvb21EZWx0YSk7XG5cdH0sXG5cblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fbWFwLl9jb250YWluZXI7XG5cblx0XHQvLyBtYWtlIHRoZSBjb250YWluZXIgZm9jdXNhYmxlIGJ5IHRhYmJpbmdcblx0XHRpZiAoY29udGFpbmVyLnRhYkluZGV4IDw9IDApIHtcblx0XHRcdGNvbnRhaW5lci50YWJJbmRleCA9ICcwJztcblx0XHR9XG5cblx0XHRMLkRvbUV2ZW50Lm9uKGNvbnRhaW5lciwge1xuXHRcdFx0Zm9jdXM6IHRoaXMuX29uRm9jdXMsXG5cdFx0XHRibHVyOiB0aGlzLl9vbkJsdXIsXG5cdFx0XHRtb3VzZWRvd246IHRoaXMuX29uTW91c2VEb3duXG5cdFx0fSwgdGhpcyk7XG5cblx0XHR0aGlzLl9tYXAub24oe1xuXHRcdFx0Zm9jdXM6IHRoaXMuX2FkZEhvb2tzLFxuXHRcdFx0Ymx1cjogdGhpcy5fcmVtb3ZlSG9va3Ncblx0XHR9LCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbW92ZUhvb2tzKCk7XG5cblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9tYXAuX2NvbnRhaW5lciwge1xuXHRcdFx0Zm9jdXM6IHRoaXMuX29uRm9jdXMsXG5cdFx0XHRibHVyOiB0aGlzLl9vbkJsdXIsXG5cdFx0XHRtb3VzZWRvd246IHRoaXMuX29uTW91c2VEb3duXG5cdFx0fSwgdGhpcyk7XG5cblx0XHR0aGlzLl9tYXAub2ZmKHtcblx0XHRcdGZvY3VzOiB0aGlzLl9hZGRIb29rcyxcblx0XHRcdGJsdXI6IHRoaXMuX3JlbW92ZUhvb2tzXG5cdFx0fSwgdGhpcyk7XG5cdH0sXG5cblx0X29uTW91c2VEb3duOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2ZvY3VzZWQpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgYm9keSA9IGRvY3VtZW50LmJvZHksXG5cdFx0ICAgIGRvY0VsID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LFxuXHRcdCAgICB0b3AgPSBib2R5LnNjcm9sbFRvcCB8fCBkb2NFbC5zY3JvbGxUb3AsXG5cdFx0ICAgIGxlZnQgPSBib2R5LnNjcm9sbExlZnQgfHwgZG9jRWwuc2Nyb2xsTGVmdDtcblxuXHRcdHRoaXMuX21hcC5fY29udGFpbmVyLmZvY3VzKCk7XG5cblx0XHR3aW5kb3cuc2Nyb2xsVG8obGVmdCwgdG9wKTtcblx0fSxcblxuXHRfb25Gb2N1czogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2ZvY3VzZWQgPSB0cnVlO1xuXHRcdHRoaXMuX21hcC5maXJlKCdmb2N1cycpO1xuXHR9LFxuXG5cdF9vbkJsdXI6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9mb2N1c2VkID0gZmFsc2U7XG5cdFx0dGhpcy5fbWFwLmZpcmUoJ2JsdXInKTtcblx0fSxcblxuXHRfc2V0UGFuRGVsdGE6IGZ1bmN0aW9uIChwYW5EZWx0YSkge1xuXHRcdHZhciBrZXlzID0gdGhpcy5fcGFuS2V5cyA9IHt9LFxuXHRcdCAgICBjb2RlcyA9IHRoaXMua2V5Q29kZXMsXG5cdFx0ICAgIGksIGxlbjtcblxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLmxlZnQubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMubGVmdFtpXV0gPSBbLTEgKiBwYW5EZWx0YSwgMF07XG5cdFx0fVxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLnJpZ2h0Lmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLnJpZ2h0W2ldXSA9IFtwYW5EZWx0YSwgMF07XG5cdFx0fVxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLmRvd24ubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMuZG93bltpXV0gPSBbMCwgcGFuRGVsdGFdO1xuXHRcdH1cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy51cC5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0a2V5c1tjb2Rlcy51cFtpXV0gPSBbMCwgLTEgKiBwYW5EZWx0YV07XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRab29tRGVsdGE6IGZ1bmN0aW9uICh6b29tRGVsdGEpIHtcblx0XHR2YXIga2V5cyA9IHRoaXMuX3pvb21LZXlzID0ge30sXG5cdFx0ICAgIGNvZGVzID0gdGhpcy5rZXlDb2Rlcyxcblx0XHQgICAgaSwgbGVuO1xuXG5cdFx0Zm9yIChpID0gMCwgbGVuID0gY29kZXMuem9vbUluLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLnpvb21JbltpXV0gPSB6b29tRGVsdGE7XG5cdFx0fVxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLnpvb21PdXQubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMuem9vbU91dFtpXV0gPSAtem9vbURlbHRhO1xuXHRcdH1cblx0fSxcblxuXHRfYWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9uKGRvY3VtZW50LCAna2V5ZG93bicsIHRoaXMuX29uS2V5RG93biwgdGhpcyk7XG5cdH0sXG5cblx0X3JlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21FdmVudC5vZmYoZG9jdW1lbnQsICdrZXlkb3duJywgdGhpcy5fb25LZXlEb3duLCB0aGlzKTtcblx0fSxcblxuXHRfb25LZXlEb3duOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmIChlLmFsdEtleSB8fCBlLmN0cmxLZXkgfHwgZS5tZXRhS2V5KSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIGtleSA9IGUua2V5Q29kZSxcblx0XHQgICAgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBvZmZzZXQ7XG5cblx0XHRpZiAoa2V5IGluIHRoaXMuX3BhbktleXMpIHtcblxuXHRcdFx0aWYgKG1hcC5fcGFuQW5pbSAmJiBtYXAuX3BhbkFuaW0uX2luUHJvZ3Jlc3MpIHsgcmV0dXJuOyB9XG5cblx0XHRcdG9mZnNldCA9IHRoaXMuX3BhbktleXNba2V5XTtcblx0XHRcdGlmIChlLnNoaWZ0S2V5KSB7XG5cdFx0XHRcdG9mZnNldCA9IEwucG9pbnQob2Zmc2V0KS5tdWx0aXBseUJ5KDMpO1xuXHRcdFx0fVxuXG5cdFx0XHRtYXAucGFuQnkob2Zmc2V0KTtcblxuXHRcdFx0aWYgKG1hcC5vcHRpb25zLm1heEJvdW5kcykge1xuXHRcdFx0XHRtYXAucGFuSW5zaWRlQm91bmRzKG1hcC5vcHRpb25zLm1heEJvdW5kcyk7XG5cdFx0XHR9XG5cblx0XHR9IGVsc2UgaWYgKGtleSBpbiB0aGlzLl96b29tS2V5cykge1xuXHRcdFx0bWFwLnNldFpvb20obWFwLmdldFpvb20oKSArIChlLnNoaWZ0S2V5ID8gMyA6IDEpICogdGhpcy5fem9vbUtleXNba2V5XSk7XG5cblx0XHR9IGVsc2UgaWYgKGtleSA9PT0gMjcpIHtcblx0XHRcdG1hcC5jbG9zZVBvcHVwKCk7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IGtleWJvYXJkOiBIYW5kbGVyXG4vLyBLZXlib2FyZCBuYXZpZ2F0aW9uIGhhbmRsZXIuXG5MLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICdrZXlib2FyZCcsIEwuTWFwLktleWJvYXJkKTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuTWFya2VyRHJhZyBpcyB1c2VkIGludGVybmFsbHkgYnkgTC5NYXJrZXIgdG8gbWFrZSB0aGUgbWFya2VycyBkcmFnZ2FibGUuXG4gKi9cblxuXG4vKiBAbmFtZXNwYWNlIE1hcmtlclxuICogQHNlY3Rpb24gSW50ZXJhY3Rpb24gaGFuZGxlcnNcbiAqXG4gKiBJbnRlcmFjdGlvbiBoYW5kbGVycyBhcmUgcHJvcGVydGllcyBvZiBhIG1hcmtlciBpbnN0YW5jZSB0aGF0IGFsbG93IHlvdSB0byBjb250cm9sIGludGVyYWN0aW9uIGJlaGF2aW9yIGluIHJ1bnRpbWUsIGVuYWJsaW5nIG9yIGRpc2FibGluZyBjZXJ0YWluIGZlYXR1cmVzIHN1Y2ggYXMgZHJhZ2dpbmcgKHNlZSBgSGFuZGxlcmAgbWV0aG9kcykuIEV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIG1hcmtlci5kcmFnZ2luZy5kaXNhYmxlKCk7XG4gKiBgYGBcbiAqXG4gKiBAcHJvcGVydHkgZHJhZ2dpbmc6IEhhbmRsZXJcbiAqIE1hcmtlciBkcmFnZ2luZyBoYW5kbGVyIChieSBib3RoIG1vdXNlIGFuZCB0b3VjaCkuXG4gKi9cblxuTC5IYW5kbGVyLk1hcmtlckRyYWcgPSBMLkhhbmRsZXIuZXh0ZW5kKHtcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG1hcmtlcikge1xuXHRcdHRoaXMuX21hcmtlciA9IG1hcmtlcjtcblx0fSxcblxuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBpY29uID0gdGhpcy5fbWFya2VyLl9pY29uO1xuXG5cdFx0aWYgKCF0aGlzLl9kcmFnZ2FibGUpIHtcblx0XHRcdHRoaXMuX2RyYWdnYWJsZSA9IG5ldyBMLkRyYWdnYWJsZShpY29uLCBpY29uLCB0cnVlKTtcblx0XHR9XG5cblx0XHR0aGlzLl9kcmFnZ2FibGUub24oe1xuXHRcdFx0ZHJhZ3N0YXJ0OiB0aGlzLl9vbkRyYWdTdGFydCxcblx0XHRcdGRyYWc6IHRoaXMuX29uRHJhZyxcblx0XHRcdGRyYWdlbmQ6IHRoaXMuX29uRHJhZ0VuZFxuXHRcdH0sIHRoaXMpLmVuYWJsZSgpO1xuXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGljb24sICdsZWFmbGV0LW1hcmtlci1kcmFnZ2FibGUnKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2RyYWdnYWJsZS5vZmYoe1xuXHRcdFx0ZHJhZ3N0YXJ0OiB0aGlzLl9vbkRyYWdTdGFydCxcblx0XHRcdGRyYWc6IHRoaXMuX29uRHJhZyxcblx0XHRcdGRyYWdlbmQ6IHRoaXMuX29uRHJhZ0VuZFxuXHRcdH0sIHRoaXMpLmRpc2FibGUoKTtcblxuXHRcdGlmICh0aGlzLl9tYXJrZXIuX2ljb24pIHtcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXJrZXIuX2ljb24sICdsZWFmbGV0LW1hcmtlci1kcmFnZ2FibGUnKTtcblx0XHR9XG5cdH0sXG5cblx0bW92ZWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fZHJhZ2dhYmxlICYmIHRoaXMuX2RyYWdnYWJsZS5fbW92ZWQ7XG5cdH0sXG5cblx0X29uRHJhZ1N0YXJ0OiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gQHNlY3Rpb24gRHJhZ2dpbmcgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IGRyYWdzdGFydDogRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHN0YXJ0cyBkcmFnZ2luZyB0aGUgbWFya2VyLlxuXG5cdFx0Ly8gQGV2ZW50IG1vdmVzdGFydDogRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXJrZXIgc3RhcnRzIG1vdmluZyAoYmVjYXVzZSBvZiBkcmFnZ2luZykuXG5cblx0XHR0aGlzLl9vbGRMYXRMbmcgPSB0aGlzLl9tYXJrZXIuZ2V0TGF0TG5nKCk7XG5cdFx0dGhpcy5fbWFya2VyXG5cdFx0ICAgIC5jbG9zZVBvcHVwKClcblx0XHQgICAgLmZpcmUoJ21vdmVzdGFydCcpXG5cdFx0ICAgIC5maXJlKCdkcmFnc3RhcnQnKTtcblx0fSxcblxuXHRfb25EcmFnOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBtYXJrZXIgPSB0aGlzLl9tYXJrZXIsXG5cdFx0ICAgIHNoYWRvdyA9IG1hcmtlci5fc2hhZG93LFxuXHRcdCAgICBpY29uUG9zID0gTC5Eb21VdGlsLmdldFBvc2l0aW9uKG1hcmtlci5faWNvbiksXG5cdFx0ICAgIGxhdGxuZyA9IG1hcmtlci5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyhpY29uUG9zKTtcblxuXHRcdC8vIHVwZGF0ZSBzaGFkb3cgcG9zaXRpb25cblx0XHRpZiAoc2hhZG93KSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24oc2hhZG93LCBpY29uUG9zKTtcblx0XHR9XG5cblx0XHRtYXJrZXIuX2xhdGxuZyA9IGxhdGxuZztcblx0XHRlLmxhdGxuZyA9IGxhdGxuZztcblx0XHRlLm9sZExhdExuZyA9IHRoaXMuX29sZExhdExuZztcblxuXHRcdC8vIEBldmVudCBkcmFnOiBFdmVudFxuXHRcdC8vIEZpcmVkIHJlcGVhdGVkbHkgd2hpbGUgdGhlIHVzZXIgZHJhZ3MgdGhlIG1hcmtlci5cblx0XHRtYXJrZXJcblx0XHQgICAgLmZpcmUoJ21vdmUnLCBlKVxuXHRcdCAgICAuZmlyZSgnZHJhZycsIGUpO1xuXHR9LFxuXG5cdF9vbkRyYWdFbmQ6IGZ1bmN0aW9uIChlKSB7XG5cdFx0Ly8gQGV2ZW50IGRyYWdlbmQ6IERyYWdFbmRFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgc3RvcHMgZHJhZ2dpbmcgdGhlIG1hcmtlci5cblxuXHRcdC8vIEBldmVudCBtb3ZlZW5kOiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcmtlciBzdG9wcyBtb3ZpbmcgKGJlY2F1c2Ugb2YgZHJhZ2dpbmcpLlxuXHRcdGRlbGV0ZSB0aGlzLl9vbGRMYXRMbmc7XG5cdFx0dGhpcy5fbWFya2VyXG5cdFx0ICAgIC5maXJlKCdtb3ZlZW5kJylcblx0XHQgICAgLmZpcmUoJ2RyYWdlbmQnLCBlKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQ29udHJvbFxyXG4gKiBAYWthIEwuQ29udHJvbFxyXG4gKiBAaW5oZXJpdHMgQ2xhc3NcclxuICpcclxuICogTC5Db250cm9sIGlzIGEgYmFzZSBjbGFzcyBmb3IgaW1wbGVtZW50aW5nIG1hcCBjb250cm9scy4gSGFuZGxlcyBwb3NpdGlvbmluZy5cclxuICogQWxsIG90aGVyIGNvbnRyb2xzIGV4dGVuZCBmcm9tIHRoaXMgY2xhc3MuXHJcbiAqL1xyXG5cclxuTC5Db250cm9sID0gTC5DbGFzcy5leHRlbmQoe1xyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBDb250cm9sIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIHBvc2l0aW9uOiBTdHJpbmcgPSAndG9wcmlnaHQnXHJcblx0XHQvLyBUaGUgcG9zaXRpb24gb2YgdGhlIGNvbnRyb2wgKG9uZSBvZiB0aGUgbWFwIGNvcm5lcnMpLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIGAndG9wbGVmdCdgLFxyXG5cdFx0Ly8gYCd0b3ByaWdodCdgLCBgJ2JvdHRvbWxlZnQnYCBvciBgJ2JvdHRvbXJpZ2h0J2BcclxuXHRcdHBvc2l0aW9uOiAndG9wcmlnaHQnXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvKiBAc2VjdGlvblxyXG5cdCAqIENsYXNzZXMgZXh0ZW5kaW5nIEwuQ29udHJvbCB3aWxsIGluaGVyaXQgdGhlIGZvbGxvd2luZyBtZXRob2RzOlxyXG5cdCAqXHJcblx0ICogQG1ldGhvZCBnZXRQb3NpdGlvbjogc3RyaW5nXHJcblx0ICogUmV0dXJucyB0aGUgcG9zaXRpb24gb2YgdGhlIGNvbnRyb2wuXHJcblx0ICovXHJcblx0Z2V0UG9zaXRpb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMucG9zaXRpb247XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRQb3NpdGlvbihwb3NpdGlvbjogc3RyaW5nKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHBvc2l0aW9uIG9mIHRoZSBjb250cm9sLlxyXG5cdHNldFBvc2l0aW9uOiBmdW5jdGlvbiAocG9zaXRpb24pIHtcclxuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXA7XHJcblxyXG5cdFx0aWYgKG1hcCkge1xyXG5cdFx0XHRtYXAucmVtb3ZlQ29udHJvbCh0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLm9wdGlvbnMucG9zaXRpb24gPSBwb3NpdGlvbjtcclxuXHJcblx0XHRpZiAobWFwKSB7XHJcblx0XHRcdG1hcC5hZGRDb250cm9sKHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q29udGFpbmVyOiBIVE1MRWxlbWVudFxyXG5cdC8vIFJldHVybnMgdGhlIEhUTUxFbGVtZW50IHRoYXQgY29udGFpbnMgdGhlIGNvbnRyb2wuXHJcblx0Z2V0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkVG8obWFwOiBNYXApOiB0aGlzXHJcblx0Ly8gQWRkcyB0aGUgY29udHJvbCB0byB0aGUgZ2l2ZW4gbWFwLlxyXG5cdGFkZFRvOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHR0aGlzLnJlbW92ZSgpO1xyXG5cdFx0dGhpcy5fbWFwID0gbWFwO1xyXG5cclxuXHRcdHZhciBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIgPSB0aGlzLm9uQWRkKG1hcCksXHJcblx0XHQgICAgcG9zID0gdGhpcy5nZXRQb3NpdGlvbigpLFxyXG5cdFx0ICAgIGNvcm5lciA9IG1hcC5fY29udHJvbENvcm5lcnNbcG9zXTtcclxuXHJcblx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC1jb250cm9sJyk7XHJcblxyXG5cdFx0aWYgKHBvcy5pbmRleE9mKCdib3R0b20nKSAhPT0gLTEpIHtcclxuXHRcdFx0Y29ybmVyLmluc2VydEJlZm9yZShjb250YWluZXIsIGNvcm5lci5maXJzdENoaWxkKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGNvcm5lci5hcHBlbmRDaGlsZChjb250YWluZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyB0aGUgY29udHJvbCBmcm9tIHRoZSBtYXAgaXQgaXMgY3VycmVudGx5IGFjdGl2ZSBvbi5cclxuXHRyZW1vdmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fbWFwKSB7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fY29udGFpbmVyKTtcclxuXHJcblx0XHRpZiAodGhpcy5vblJlbW92ZSkge1xyXG5cdFx0XHR0aGlzLm9uUmVtb3ZlKHRoaXMuX21hcCk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fbWFwID0gbnVsbDtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfcmVmb2N1c09uTWFwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gaWYgbWFwIGV4aXN0cyBhbmQgZXZlbnQgaXMgbm90IGEga2V5Ym9hcmQgZXZlbnRcclxuXHRcdGlmICh0aGlzLl9tYXAgJiYgZSAmJiBlLnNjcmVlblggPiAwICYmIGUuc2NyZWVuWSA+IDApIHtcclxuXHRcdFx0dGhpcy5fbWFwLmdldENvbnRhaW5lcigpLmZvY3VzKCk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbkwuY29udHJvbCA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkNvbnRyb2wob3B0aW9ucyk7XHJcbn07XHJcblxyXG4vKiBAc2VjdGlvbiBFeHRlbnNpb24gbWV0aG9kc1xyXG4gKiBAdW5pbmhlcml0YWJsZVxyXG4gKlxyXG4gKiBFdmVyeSBjb250cm9sIHNob3VsZCBleHRlbmQgZnJvbSBgTC5Db250cm9sYCBhbmQgKHJlLSlpbXBsZW1lbnQgdGhlIGZvbGxvd2luZyBtZXRob2RzLlxyXG4gKlxyXG4gKiBAbWV0aG9kIG9uQWRkKG1hcDogTWFwKTogSFRNTEVsZW1lbnRcclxuICogU2hvdWxkIHJldHVybiB0aGUgY29udGFpbmVyIERPTSBlbGVtZW50IGZvciB0aGUgY29udHJvbCBhbmQgYWRkIGxpc3RlbmVycyBvbiByZWxldmFudCBtYXAgZXZlbnRzLiBDYWxsZWQgb24gW2Bjb250cm9sLmFkZFRvKG1hcClgXSgjY29udHJvbC1hZGRUbykuXHJcbiAqXHJcbiAqIEBtZXRob2Qgb25SZW1vdmUobWFwOiBNYXApXHJcbiAqIE9wdGlvbmFsIG1ldGhvZC4gU2hvdWxkIGNvbnRhaW4gYWxsIGNsZWFuIHVwIGNvZGUgdGhhdCByZW1vdmVzIHRoZSBsaXN0ZW5lcnMgcHJldmlvdXNseSBhZGRlZCBpbiBbYG9uQWRkYF0oI2NvbnRyb2wtb25hZGQpLiBDYWxsZWQgb24gW2Bjb250cm9sLnJlbW92ZSgpYF0oI2NvbnRyb2wtcmVtb3ZlKS5cclxuICovXHJcblxyXG4vKiBAbmFtZXNwYWNlIE1hcFxyXG4gKiBAc2VjdGlvbiBNZXRob2RzIGZvciBMYXllcnMgYW5kIENvbnRyb2xzXHJcbiAqL1xyXG5MLk1hcC5pbmNsdWRlKHtcclxuXHQvLyBAbWV0aG9kIGFkZENvbnRyb2woY29udHJvbDogQ29udHJvbCk6IHRoaXNcclxuXHQvLyBBZGRzIHRoZSBnaXZlbiBjb250cm9sIHRvIHRoZSBtYXBcclxuXHRhZGRDb250cm9sOiBmdW5jdGlvbiAoY29udHJvbCkge1xyXG5cdFx0Y29udHJvbC5hZGRUbyh0aGlzKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlQ29udHJvbChjb250cm9sOiBDb250cm9sKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIGdpdmVuIGNvbnRyb2wgZnJvbSB0aGUgbWFwXHJcblx0cmVtb3ZlQ29udHJvbDogZnVuY3Rpb24gKGNvbnRyb2wpIHtcclxuXHRcdGNvbnRyb2wucmVtb3ZlKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfaW5pdENvbnRyb2xQb3M6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBjb3JuZXJzID0gdGhpcy5fY29udHJvbENvcm5lcnMgPSB7fSxcclxuXHRcdCAgICBsID0gJ2xlYWZsZXQtJyxcclxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250cm9sQ29udGFpbmVyID1cclxuXHRcdCAgICAgICAgICAgIEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGwgKyAnY29udHJvbC1jb250YWluZXInLCB0aGlzLl9jb250YWluZXIpO1xyXG5cclxuXHRcdGZ1bmN0aW9uIGNyZWF0ZUNvcm5lcih2U2lkZSwgaFNpZGUpIHtcclxuXHRcdFx0dmFyIGNsYXNzTmFtZSA9IGwgKyB2U2lkZSArICcgJyArIGwgKyBoU2lkZTtcclxuXHJcblx0XHRcdGNvcm5lcnNbdlNpZGUgKyBoU2lkZV0gPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUsIGNvbnRhaW5lcik7XHJcblx0XHR9XHJcblxyXG5cdFx0Y3JlYXRlQ29ybmVyKCd0b3AnLCAnbGVmdCcpO1xyXG5cdFx0Y3JlYXRlQ29ybmVyKCd0b3AnLCAncmlnaHQnKTtcclxuXHRcdGNyZWF0ZUNvcm5lcignYm90dG9tJywgJ2xlZnQnKTtcclxuXHRcdGNyZWF0ZUNvcm5lcignYm90dG9tJywgJ3JpZ2h0Jyk7XHJcblx0fSxcclxuXHJcblx0X2NsZWFyQ29udHJvbFBvczogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250cm9sQ29udGFpbmVyKTtcclxuXHR9XHJcbn0pO1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQ29udHJvbC5ab29tXHJcbiAqIEBha2EgTC5Db250cm9sLlpvb21cclxuICogQGluaGVyaXRzIENvbnRyb2xcclxuICpcclxuICogQSBiYXNpYyB6b29tIGNvbnRyb2wgd2l0aCB0d28gYnV0dG9ucyAoem9vbSBpbiBhbmQgem9vbSBvdXQpLiBJdCBpcyBwdXQgb24gdGhlIG1hcCBieSBkZWZhdWx0IHVubGVzcyB5b3Ugc2V0IGl0cyBbYHpvb21Db250cm9sYCBvcHRpb25dKCNtYXAtem9vbWNvbnRyb2wpIHRvIGBmYWxzZWAuIEV4dGVuZHMgYENvbnRyb2xgLlxyXG4gKi9cclxuXHJcbkwuQ29udHJvbC5ab29tID0gTC5Db250cm9sLmV4dGVuZCh7XHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIENvbnRyb2wuWm9vbSBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0cG9zaXRpb246ICd0b3BsZWZ0JyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21JblRleHQ6IFN0cmluZyA9ICcrJ1xyXG5cdFx0Ly8gVGhlIHRleHQgc2V0IG9uIHRoZSAnem9vbSBpbicgYnV0dG9uLlxyXG5cdFx0em9vbUluVGV4dDogJysnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbUluVGl0bGU6IFN0cmluZyA9ICdab29tIGluJ1xyXG5cdFx0Ly8gVGhlIHRpdGxlIHNldCBvbiB0aGUgJ3pvb20gaW4nIGJ1dHRvbi5cclxuXHRcdHpvb21JblRpdGxlOiAnWm9vbSBpbicsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tT3V0VGV4dDogU3RyaW5nID0gJy0nXHJcblx0XHQvLyBUaGUgdGV4dCBzZXQgb24gdGhlICd6b29tIG91dCcgYnV0dG9uLlxyXG5cdFx0em9vbU91dFRleHQ6ICctJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21PdXRUaXRsZTogU3RyaW5nID0gJ1pvb20gb3V0J1xyXG5cdFx0Ly8gVGhlIHRpdGxlIHNldCBvbiB0aGUgJ3pvb20gb3V0JyBidXR0b24uXHJcblx0XHR6b29tT3V0VGl0bGU6ICdab29tIG91dCdcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dmFyIHpvb21OYW1lID0gJ2xlYWZsZXQtY29udHJvbC16b29tJyxcclxuXHRcdCAgICBjb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCB6b29tTmFtZSArICcgbGVhZmxldC1iYXInKSxcclxuXHRcdCAgICBvcHRpb25zID0gdGhpcy5vcHRpb25zO1xyXG5cclxuXHRcdHRoaXMuX3pvb21JbkJ1dHRvbiAgPSB0aGlzLl9jcmVhdGVCdXR0b24ob3B0aW9ucy56b29tSW5UZXh0LCBvcHRpb25zLnpvb21JblRpdGxlLFxyXG5cdFx0ICAgICAgICB6b29tTmFtZSArICctaW4nLCAgY29udGFpbmVyLCB0aGlzLl96b29tSW4pO1xyXG5cdFx0dGhpcy5fem9vbU91dEJ1dHRvbiA9IHRoaXMuX2NyZWF0ZUJ1dHRvbihvcHRpb25zLnpvb21PdXRUZXh0LCBvcHRpb25zLnpvb21PdXRUaXRsZSxcclxuXHRcdCAgICAgICAgem9vbU5hbWUgKyAnLW91dCcsIGNvbnRhaW5lciwgdGhpcy5fem9vbU91dCk7XHJcblxyXG5cdFx0dGhpcy5fdXBkYXRlRGlzYWJsZWQoKTtcclxuXHRcdG1hcC5vbignem9vbWVuZCB6b29tbGV2ZWxzY2hhbmdlJywgdGhpcy5fdXBkYXRlRGlzYWJsZWQsIHRoaXMpO1xyXG5cclxuXHRcdHJldHVybiBjb250YWluZXI7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdG1hcC5vZmYoJ3pvb21lbmQgem9vbWxldmVsc2NoYW5nZScsIHRoaXMuX3VwZGF0ZURpc2FibGVkLCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRkaXNhYmxlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9kaXNhYmxlZCA9IHRydWU7XHJcblx0XHR0aGlzLl91cGRhdGVEaXNhYmxlZCgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0ZW5hYmxlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9kaXNhYmxlZCA9IGZhbHNlO1xyXG5cdFx0dGhpcy5fdXBkYXRlRGlzYWJsZWQoKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF96b29tSW46IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRpZiAoIXRoaXMuX2Rpc2FibGVkICYmIHRoaXMuX21hcC5fem9vbSA8IHRoaXMuX21hcC5nZXRNYXhab29tKCkpIHtcclxuXHRcdFx0dGhpcy5fbWFwLnpvb21Jbih0aGlzLl9tYXAub3B0aW9ucy56b29tRGVsdGEgKiAoZS5zaGlmdEtleSA/IDMgOiAxKSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3pvb21PdXQ6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRpZiAoIXRoaXMuX2Rpc2FibGVkICYmIHRoaXMuX21hcC5fem9vbSA+IHRoaXMuX21hcC5nZXRNaW5ab29tKCkpIHtcclxuXHRcdFx0dGhpcy5fbWFwLnpvb21PdXQodGhpcy5fbWFwLm9wdGlvbnMuem9vbURlbHRhICogKGUuc2hpZnRLZXkgPyAzIDogMSkpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9jcmVhdGVCdXR0b246IGZ1bmN0aW9uIChodG1sLCB0aXRsZSwgY2xhc3NOYW1lLCBjb250YWluZXIsIGZuKSB7XHJcblx0XHR2YXIgbGluayA9IEwuRG9tVXRpbC5jcmVhdGUoJ2EnLCBjbGFzc05hbWUsIGNvbnRhaW5lcik7XHJcblx0XHRsaW5rLmlubmVySFRNTCA9IGh0bWw7XHJcblx0XHRsaW5rLmhyZWYgPSAnIyc7XHJcblx0XHRsaW5rLnRpdGxlID0gdGl0bGU7XHJcblxyXG5cdFx0LypcclxuXHRcdCAqIFdpbGwgZm9yY2Ugc2NyZWVuIHJlYWRlcnMgbGlrZSBWb2ljZU92ZXIgdG8gcmVhZCB0aGlzIGFzIFwiWm9vbSBpbiAtIGJ1dHRvblwiXHJcblx0XHQgKi9cclxuXHRcdGxpbmsuc2V0QXR0cmlidXRlKCdyb2xlJywgJ2J1dHRvbicpO1xyXG5cdFx0bGluay5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCB0aXRsZSk7XHJcblxyXG5cdFx0TC5Eb21FdmVudFxyXG5cdFx0ICAgIC5vbihsaW5rLCAnbW91c2Vkb3duIGRibGNsaWNrJywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pXHJcblx0XHQgICAgLm9uKGxpbmssICdjbGljaycsIEwuRG9tRXZlbnQuc3RvcClcclxuXHRcdCAgICAub24obGluaywgJ2NsaWNrJywgZm4sIHRoaXMpXHJcblx0XHQgICAgLm9uKGxpbmssICdjbGljaycsIHRoaXMuX3JlZm9jdXNPbk1hcCwgdGhpcyk7XHJcblxyXG5cdFx0cmV0dXJuIGxpbms7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZURpc2FibGVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxyXG5cdFx0ICAgIGNsYXNzTmFtZSA9ICdsZWFmbGV0LWRpc2FibGVkJztcclxuXHJcblx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fem9vbUluQnV0dG9uLCBjbGFzc05hbWUpO1xyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX3pvb21PdXRCdXR0b24sIGNsYXNzTmFtZSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2Rpc2FibGVkIHx8IG1hcC5fem9vbSA9PT0gbWFwLmdldE1pblpvb20oKSkge1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fem9vbU91dEJ1dHRvbiwgY2xhc3NOYW1lKTtcclxuXHRcdH1cclxuXHRcdGlmICh0aGlzLl9kaXNhYmxlZCB8fCBtYXAuX3pvb20gPT09IG1hcC5nZXRNYXhab29tKCkpIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX3pvb21JbkJ1dHRvbiwgY2xhc3NOYW1lKTtcclxuXHRcdH1cclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXBcclxuLy8gQHNlY3Rpb24gQ29udHJvbCBvcHRpb25zXHJcbi8vIEBvcHRpb24gem9vbUNvbnRyb2w6IEJvb2xlYW4gPSB0cnVlXHJcbi8vIFdoZXRoZXIgYSBbem9vbSBjb250cm9sXSgjY29udHJvbC16b29tKSBpcyBhZGRlZCB0byB0aGUgbWFwIGJ5IGRlZmF1bHQuXHJcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XHJcblx0em9vbUNvbnRyb2w6IHRydWVcclxufSk7XHJcblxyXG5MLk1hcC5hZGRJbml0SG9vayhmdW5jdGlvbiAoKSB7XHJcblx0aWYgKHRoaXMub3B0aW9ucy56b29tQ29udHJvbCkge1xyXG5cdFx0dGhpcy56b29tQ29udHJvbCA9IG5ldyBMLkNvbnRyb2wuWm9vbSgpO1xyXG5cdFx0dGhpcy5hZGRDb250cm9sKHRoaXMuem9vbUNvbnRyb2wpO1xyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAbmFtZXNwYWNlIENvbnRyb2wuWm9vbVxyXG4vLyBAZmFjdG9yeSBMLmNvbnRyb2wuem9vbShvcHRpb25zOiBDb250cm9sLlpvb20gb3B0aW9ucylcclxuLy8gQ3JlYXRlcyBhIHpvb20gY29udHJvbFxyXG5MLmNvbnRyb2wuem9vbSA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkNvbnRyb2wuWm9vbShvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIENvbnRyb2wuQXR0cmlidXRpb25cclxuICogQGFrYSBMLkNvbnRyb2wuQXR0cmlidXRpb25cclxuICogQGluaGVyaXRzIENvbnRyb2xcclxuICpcclxuICogVGhlIGF0dHJpYnV0aW9uIGNvbnRyb2wgYWxsb3dzIHlvdSB0byBkaXNwbGF5IGF0dHJpYnV0aW9uIGRhdGEgaW4gYSBzbWFsbCB0ZXh0IGJveCBvbiBhIG1hcC4gSXQgaXMgcHV0IG9uIHRoZSBtYXAgYnkgZGVmYXVsdCB1bmxlc3MgeW91IHNldCBpdHMgW2BhdHRyaWJ1dGlvbkNvbnRyb2xgIG9wdGlvbl0oI21hcC1hdHRyaWJ1dGlvbmNvbnRyb2wpIHRvIGBmYWxzZWAsIGFuZCBpdCBmZXRjaGVzIGF0dHJpYnV0aW9uIHRleHRzIGZyb20gbGF5ZXJzIHdpdGggdGhlIFtgZ2V0QXR0cmlidXRpb25gIG1ldGhvZF0oI2xheWVyLWdldGF0dHJpYnV0aW9uKSBhdXRvbWF0aWNhbGx5LiBFeHRlbmRzIENvbnRyb2wuXHJcbiAqL1xyXG5cclxuTC5Db250cm9sLkF0dHJpYnV0aW9uID0gTC5Db250cm9sLmV4dGVuZCh7XHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIENvbnRyb2wuQXR0cmlidXRpb24gb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdHBvc2l0aW9uOiAnYm90dG9tcmlnaHQnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gcHJlZml4OiBTdHJpbmcgPSAnTGVhZmxldCdcclxuXHRcdC8vIFRoZSBIVE1MIHRleHQgc2hvd24gYmVmb3JlIHRoZSBhdHRyaWJ1dGlvbnMuIFBhc3MgYGZhbHNlYCB0byBkaXNhYmxlLlxyXG5cdFx0cHJlZml4OiAnPGEgaHJlZj1cImh0dHA6Ly9sZWFmbGV0anMuY29tXCIgdGl0bGU9XCJBIEpTIGxpYnJhcnkgZm9yIGludGVyYWN0aXZlIG1hcHNcIj5MZWFmbGV0PC9hPidcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX2F0dHJpYnV0aW9ucyA9IHt9O1xyXG5cdH0sXHJcblxyXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRtYXAuYXR0cmlidXRpb25Db250cm9sID0gdGhpcztcclxuXHRcdHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtYXR0cmlidXRpb24nKTtcclxuXHRcdGlmIChMLkRvbUV2ZW50KSB7XHJcblx0XHRcdEwuRG9tRXZlbnQuZGlzYWJsZUNsaWNrUHJvcGFnYXRpb24odGhpcy5fY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBUT0RPIHVnbHksIHJlZmFjdG9yXHJcblx0XHRmb3IgKHZhciBpIGluIG1hcC5fbGF5ZXJzKSB7XHJcblx0XHRcdGlmIChtYXAuX2xheWVyc1tpXS5nZXRBdHRyaWJ1dGlvbikge1xyXG5cdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRpb24obWFwLl9sYXllcnNbaV0uZ2V0QXR0cmlidXRpb24oKSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0UHJlZml4KHByZWZpeDogU3RyaW5nKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHRleHQgYmVmb3JlIHRoZSBhdHRyaWJ1dGlvbnMuXHJcblx0c2V0UHJlZml4OiBmdW5jdGlvbiAocHJlZml4KSB7XHJcblx0XHR0aGlzLm9wdGlvbnMucHJlZml4ID0gcHJlZml4O1xyXG5cdFx0dGhpcy5fdXBkYXRlKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZEF0dHJpYnV0aW9uKHRleHQ6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBBZGRzIGFuIGF0dHJpYnV0aW9uIHRleHQgKGUuZy4gYCdWZWN0b3IgZGF0YSAmY29weTsgTWFwYm94J2ApLlxyXG5cdGFkZEF0dHJpYnV0aW9uOiBmdW5jdGlvbiAodGV4dCkge1xyXG5cdFx0aWYgKCF0ZXh0KSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9hdHRyaWJ1dGlvbnNbdGV4dF0pIHtcclxuXHRcdFx0dGhpcy5fYXR0cmlidXRpb25zW3RleHRdID0gMDtcclxuXHRcdH1cclxuXHRcdHRoaXMuX2F0dHJpYnV0aW9uc1t0ZXh0XSsrO1xyXG5cclxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlQXR0cmlidXRpb24odGV4dDogU3RyaW5nKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYW4gYXR0cmlidXRpb24gdGV4dC5cclxuXHRyZW1vdmVBdHRyaWJ1dGlvbjogZnVuY3Rpb24gKHRleHQpIHtcclxuXHRcdGlmICghdGV4dCkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdGlmICh0aGlzLl9hdHRyaWJ1dGlvbnNbdGV4dF0pIHtcclxuXHRcdFx0dGhpcy5fYXR0cmlidXRpb25zW3RleHRdLS07XHJcblx0XHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fbWFwKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciBhdHRyaWJzID0gW107XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9hdHRyaWJ1dGlvbnMpIHtcclxuXHRcdFx0aWYgKHRoaXMuX2F0dHJpYnV0aW9uc1tpXSkge1xyXG5cdFx0XHRcdGF0dHJpYnMucHVzaChpKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBwcmVmaXhBbmRBdHRyaWJzID0gW107XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5wcmVmaXgpIHtcclxuXHRcdFx0cHJlZml4QW5kQXR0cmlicy5wdXNoKHRoaXMub3B0aW9ucy5wcmVmaXgpO1xyXG5cdFx0fVxyXG5cdFx0aWYgKGF0dHJpYnMubGVuZ3RoKSB7XHJcblx0XHRcdHByZWZpeEFuZEF0dHJpYnMucHVzaChhdHRyaWJzLmpvaW4oJywgJykpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2NvbnRhaW5lci5pbm5lckhUTUwgPSBwcmVmaXhBbmRBdHRyaWJzLmpvaW4oJyB8ICcpO1xyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAbmFtZXNwYWNlIE1hcFxyXG4vLyBAc2VjdGlvbiBDb250cm9sIG9wdGlvbnNcclxuLy8gQG9wdGlvbiBhdHRyaWJ1dGlvbkNvbnRyb2w6IEJvb2xlYW4gPSB0cnVlXHJcbi8vIFdoZXRoZXIgYSBbYXR0cmlidXRpb24gY29udHJvbF0oI2NvbnRyb2wtYXR0cmlidXRpb24pIGlzIGFkZGVkIHRvIHRoZSBtYXAgYnkgZGVmYXVsdC5cclxuTC5NYXAubWVyZ2VPcHRpb25zKHtcclxuXHRhdHRyaWJ1dGlvbkNvbnRyb2w6IHRydWVcclxufSk7XHJcblxyXG5MLk1hcC5hZGRJbml0SG9vayhmdW5jdGlvbiAoKSB7XHJcblx0aWYgKHRoaXMub3B0aW9ucy5hdHRyaWJ1dGlvbkNvbnRyb2wpIHtcclxuXHRcdG5ldyBMLkNvbnRyb2wuQXR0cmlidXRpb24oKS5hZGRUbyh0aGlzKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBDb250cm9sLkF0dHJpYnV0aW9uXHJcbi8vIEBmYWN0b3J5IEwuY29udHJvbC5hdHRyaWJ1dGlvbihvcHRpb25zOiBDb250cm9sLkF0dHJpYnV0aW9uIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYW4gYXR0cmlidXRpb24gY29udHJvbC5cclxuTC5jb250cm9sLmF0dHJpYnV0aW9uID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuQ29udHJvbC5BdHRyaWJ1dGlvbihvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBDb250cm9sLlNjYWxlXG4gKiBAYWthIEwuQ29udHJvbC5TY2FsZVxuICogQGluaGVyaXRzIENvbnRyb2xcbiAqXG4gKiBBIHNpbXBsZSBzY2FsZSBjb250cm9sIHRoYXQgc2hvd3MgdGhlIHNjYWxlIG9mIHRoZSBjdXJyZW50IGNlbnRlciBvZiBzY3JlZW4gaW4gbWV0cmljIChtL2ttKSBhbmQgaW1wZXJpYWwgKG1pL2Z0KSBzeXN0ZW1zLiBFeHRlbmRzIGBDb250cm9sYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiBMLmNvbnRyb2wuc2NhbGUoKS5hZGRUbyhtYXApO1xuICogYGBgXG4gKi9cblxuTC5Db250cm9sLlNjYWxlID0gTC5Db250cm9sLmV4dGVuZCh7XG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgQ29udHJvbC5TY2FsZSBvcHRpb25zXG5cdG9wdGlvbnM6IHtcblx0XHRwb3NpdGlvbjogJ2JvdHRvbWxlZnQnLFxuXG5cdFx0Ly8gQG9wdGlvbiBtYXhXaWR0aDogTnVtYmVyID0gMTAwXG5cdFx0Ly8gTWF4aW11bSB3aWR0aCBvZiB0aGUgY29udHJvbCBpbiBwaXhlbHMuIFRoZSB3aWR0aCBpcyBzZXQgZHluYW1pY2FsbHkgdG8gc2hvdyByb3VuZCB2YWx1ZXMgKGUuZy4gMTAwLCAyMDAsIDUwMCkuXG5cdFx0bWF4V2lkdGg6IDEwMCxcblxuXHRcdC8vIEBvcHRpb24gbWV0cmljOiBCb29sZWFuID0gVHJ1ZVxuXHRcdC8vIFdoZXRoZXIgdG8gc2hvdyB0aGUgbWV0cmljIHNjYWxlIGxpbmUgKG0va20pLlxuXHRcdG1ldHJpYzogdHJ1ZSxcblxuXHRcdC8vIEBvcHRpb24gaW1wZXJpYWw6IEJvb2xlYW4gPSBUcnVlXG5cdFx0Ly8gV2hldGhlciB0byBzaG93IHRoZSBpbXBlcmlhbCBzY2FsZSBsaW5lIChtaS9mdCkuXG5cdFx0aW1wZXJpYWw6IHRydWVcblxuXHRcdC8vIEBvcHRpb24gdXBkYXRlV2hlbklkbGU6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGNvbnRyb2wgaXMgdXBkYXRlZCBvbiBbYG1vdmVlbmRgXSgjbWFwLW1vdmVlbmQpLCBvdGhlcndpc2UgaXQncyBhbHdheXMgdXAtdG8tZGF0ZSAodXBkYXRlZCBvbiBbYG1vdmVgXSgjbWFwLW1vdmUpKS5cblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHZhciBjbGFzc05hbWUgPSAnbGVhZmxldC1jb250cm9sLXNjYWxlJyxcblx0XHQgICAgY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lKSxcblx0XHQgICAgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcblxuXHRcdHRoaXMuX2FkZFNjYWxlcyhvcHRpb25zLCBjbGFzc05hbWUgKyAnLWxpbmUnLCBjb250YWluZXIpO1xuXG5cdFx0bWFwLm9uKG9wdGlvbnMudXBkYXRlV2hlbklkbGUgPyAnbW92ZWVuZCcgOiAnbW92ZScsIHRoaXMuX3VwZGF0ZSwgdGhpcyk7XG5cdFx0bWFwLndoZW5SZWFkeSh0aGlzLl91cGRhdGUsIHRoaXMpO1xuXG5cdFx0cmV0dXJuIGNvbnRhaW5lcjtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdG1hcC5vZmYodGhpcy5vcHRpb25zLnVwZGF0ZVdoZW5JZGxlID8gJ21vdmVlbmQnIDogJ21vdmUnLCB0aGlzLl91cGRhdGUsIHRoaXMpO1xuXHR9LFxuXG5cdF9hZGRTY2FsZXM6IGZ1bmN0aW9uIChvcHRpb25zLCBjbGFzc05hbWUsIGNvbnRhaW5lcikge1xuXHRcdGlmIChvcHRpb25zLm1ldHJpYykge1xuXHRcdFx0dGhpcy5fbVNjYWxlID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lLCBjb250YWluZXIpO1xuXHRcdH1cblx0XHRpZiAob3B0aW9ucy5pbXBlcmlhbCkge1xuXHRcdFx0dGhpcy5faVNjYWxlID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lLCBjb250YWluZXIpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgeSA9IG1hcC5nZXRTaXplKCkueSAvIDI7XG5cblx0XHR2YXIgbWF4TWV0ZXJzID0gbWFwLmRpc3RhbmNlKFxuXHRcdFx0XHRtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyhbMCwgeV0pLFxuXHRcdFx0XHRtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyhbdGhpcy5vcHRpb25zLm1heFdpZHRoLCB5XSkpO1xuXG5cdFx0dGhpcy5fdXBkYXRlU2NhbGVzKG1heE1ldGVycyk7XG5cdH0sXG5cblx0X3VwZGF0ZVNjYWxlczogZnVuY3Rpb24gKG1heE1ldGVycykge1xuXHRcdGlmICh0aGlzLm9wdGlvbnMubWV0cmljICYmIG1heE1ldGVycykge1xuXHRcdFx0dGhpcy5fdXBkYXRlTWV0cmljKG1heE1ldGVycyk7XG5cdFx0fVxuXHRcdGlmICh0aGlzLm9wdGlvbnMuaW1wZXJpYWwgJiYgbWF4TWV0ZXJzKSB7XG5cdFx0XHR0aGlzLl91cGRhdGVJbXBlcmlhbChtYXhNZXRlcnMpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlTWV0cmljOiBmdW5jdGlvbiAobWF4TWV0ZXJzKSB7XG5cdFx0dmFyIG1ldGVycyA9IHRoaXMuX2dldFJvdW5kTnVtKG1heE1ldGVycyksXG5cdFx0ICAgIGxhYmVsID0gbWV0ZXJzIDwgMTAwMCA/IG1ldGVycyArICcgbScgOiAobWV0ZXJzIC8gMTAwMCkgKyAnIGttJztcblxuXHRcdHRoaXMuX3VwZGF0ZVNjYWxlKHRoaXMuX21TY2FsZSwgbGFiZWwsIG1ldGVycyAvIG1heE1ldGVycyk7XG5cdH0sXG5cblx0X3VwZGF0ZUltcGVyaWFsOiBmdW5jdGlvbiAobWF4TWV0ZXJzKSB7XG5cdFx0dmFyIG1heEZlZXQgPSBtYXhNZXRlcnMgKiAzLjI4MDgzOTksXG5cdFx0ICAgIG1heE1pbGVzLCBtaWxlcywgZmVldDtcblxuXHRcdGlmIChtYXhGZWV0ID4gNTI4MCkge1xuXHRcdFx0bWF4TWlsZXMgPSBtYXhGZWV0IC8gNTI4MDtcblx0XHRcdG1pbGVzID0gdGhpcy5fZ2V0Um91bmROdW0obWF4TWlsZXMpO1xuXHRcdFx0dGhpcy5fdXBkYXRlU2NhbGUodGhpcy5faVNjYWxlLCBtaWxlcyArICcgbWknLCBtaWxlcyAvIG1heE1pbGVzKTtcblxuXHRcdH0gZWxzZSB7XG5cdFx0XHRmZWV0ID0gdGhpcy5fZ2V0Um91bmROdW0obWF4RmVldCk7XG5cdFx0XHR0aGlzLl91cGRhdGVTY2FsZSh0aGlzLl9pU2NhbGUsIGZlZXQgKyAnIGZ0JywgZmVldCAvIG1heEZlZXQpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlU2NhbGU6IGZ1bmN0aW9uIChzY2FsZSwgdGV4dCwgcmF0aW8pIHtcblx0XHRzY2FsZS5zdHlsZS53aWR0aCA9IE1hdGgucm91bmQodGhpcy5vcHRpb25zLm1heFdpZHRoICogcmF0aW8pICsgJ3B4Jztcblx0XHRzY2FsZS5pbm5lckhUTUwgPSB0ZXh0O1xuXHR9LFxuXG5cdF9nZXRSb3VuZE51bTogZnVuY3Rpb24gKG51bSkge1xuXHRcdHZhciBwb3cxMCA9IE1hdGgucG93KDEwLCAoTWF0aC5mbG9vcihudW0pICsgJycpLmxlbmd0aCAtIDEpLFxuXHRcdCAgICBkID0gbnVtIC8gcG93MTA7XG5cblx0XHRkID0gZCA+PSAxMCA/IDEwIDpcblx0XHQgICAgZCA+PSA1ID8gNSA6XG5cdFx0ICAgIGQgPj0gMyA/IDMgOlxuXHRcdCAgICBkID49IDIgPyAyIDogMTtcblxuXHRcdHJldHVybiBwb3cxMCAqIGQ7XG5cdH1cbn0pO1xuXG5cbi8vIEBmYWN0b3J5IEwuY29udHJvbC5zY2FsZShvcHRpb25zPzogQ29udHJvbC5TY2FsZSBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhbiBzY2FsZSBjb250cm9sIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXG5MLmNvbnRyb2wuc2NhbGUgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuQ29udHJvbC5TY2FsZShvcHRpb25zKTtcbn07XG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQ29udHJvbC5MYXllcnNcclxuICogQGFrYSBMLkNvbnRyb2wuTGF5ZXJzXHJcbiAqIEBpbmhlcml0cyBDb250cm9sXHJcbiAqXHJcbiAqIFRoZSBsYXllcnMgY29udHJvbCBnaXZlcyB1c2VycyB0aGUgYWJpbGl0eSB0byBzd2l0Y2ggYmV0d2VlbiBkaWZmZXJlbnQgYmFzZSBsYXllcnMgYW5kIHN3aXRjaCBvdmVybGF5cyBvbi9vZmYgKGNoZWNrIG91dCB0aGUgW2RldGFpbGVkIGV4YW1wbGVdKGh0dHA6Ly9sZWFmbGV0anMuY29tL2V4YW1wbGVzL2xheWVycy1jb250cm9sLmh0bWwpKS4gRXh0ZW5kcyBgQ29udHJvbGAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBiYXNlTGF5ZXJzID0ge1xyXG4gKiBcdFwiTWFwYm94XCI6IG1hcGJveCxcclxuICogXHRcIk9wZW5TdHJlZXRNYXBcIjogb3NtXHJcbiAqIH07XHJcbiAqXHJcbiAqIHZhciBvdmVybGF5cyA9IHtcclxuICogXHRcIk1hcmtlclwiOiBtYXJrZXIsXHJcbiAqIFx0XCJSb2Fkc1wiOiByb2Fkc0xheWVyXHJcbiAqIH07XHJcbiAqXHJcbiAqIEwuY29udHJvbC5sYXllcnMoYmFzZUxheWVycywgb3ZlcmxheXMpLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBUaGUgYGJhc2VMYXllcnNgIGFuZCBgb3ZlcmxheXNgIHBhcmFtZXRlcnMgYXJlIG9iamVjdCBsaXRlcmFscyB3aXRoIGxheWVyIG5hbWVzIGFzIGtleXMgYW5kIGBMYXllcmAgb2JqZWN0cyBhcyB2YWx1ZXM6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHtcclxuICogICAgIFwiPHNvbWVOYW1lMT5cIjogbGF5ZXIxLFxyXG4gKiAgICAgXCI8c29tZU5hbWUyPlwiOiBsYXllcjJcclxuICogfVxyXG4gKiBgYGBcclxuICpcclxuICogVGhlIGxheWVyIG5hbWVzIGNhbiBjb250YWluIEhUTUwsIHdoaWNoIGFsbG93cyB5b3UgdG8gYWRkIGFkZGl0aW9uYWwgc3R5bGluZyB0byB0aGUgaXRlbXM6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHtcIjxpbWcgc3JjPSdteS1sYXllci1pY29uJyAvPiA8c3BhbiBjbGFzcz0nbXktbGF5ZXItaXRlbSc+TXkgTGF5ZXI8L3NwYW4+XCI6IG15TGF5ZXJ9XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcblxyXG5MLkNvbnRyb2wuTGF5ZXJzID0gTC5Db250cm9sLmV4dGVuZCh7XHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIENvbnRyb2wuTGF5ZXJzIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIGNvbGxhcHNlZDogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGNvbnRyb2wgd2lsbCBiZSBjb2xsYXBzZWQgaW50byBhbiBpY29uIGFuZCBleHBhbmRlZCBvbiBtb3VzZSBob3ZlciBvciB0b3VjaC5cclxuXHRcdGNvbGxhcHNlZDogdHJ1ZSxcclxuXHRcdHBvc2l0aW9uOiAndG9wcmlnaHQnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1pJbmRleDogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGNvbnRyb2wgd2lsbCBhc3NpZ24gekluZGV4ZXMgaW4gaW5jcmVhc2luZyBvcmRlciB0byBhbGwgb2YgaXRzIGxheWVycyBzbyB0aGF0IHRoZSBvcmRlciBpcyBwcmVzZXJ2ZWQgd2hlbiBzd2l0Y2hpbmcgdGhlbSBvbi9vZmYuXHJcblx0XHRhdXRvWkluZGV4OiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gaGlkZVNpbmdsZUJhc2U6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCB0aGUgYmFzZSBsYXllcnMgaW4gdGhlIGNvbnRyb2wgd2lsbCBiZSBoaWRkZW4gd2hlbiB0aGVyZSBpcyBvbmx5IG9uZS5cclxuXHRcdGhpZGVTaW5nbGVCYXNlOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHNvcnRMYXllcnM6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gV2hldGhlciB0byBzb3J0IHRoZSBsYXllcnMuIFdoZW4gYGZhbHNlYCwgbGF5ZXJzIHdpbGwga2VlcCB0aGUgb3JkZXJcclxuXHRcdC8vIGluIHdoaWNoIHRoZXkgd2VyZSBhZGRlZCB0byB0aGUgY29udHJvbC5cclxuXHRcdHNvcnRMYXllcnM6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gc29ydEZ1bmN0aW9uOiBGdW5jdGlvbiA9ICpcclxuXHRcdC8vIEEgW2NvbXBhcmUgZnVuY3Rpb25dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L3NvcnQpXHJcblx0XHQvLyB0aGF0IHdpbGwgYmUgdXNlZCBmb3Igc29ydGluZyB0aGUgbGF5ZXJzLCB3aGVuIGBzb3J0TGF5ZXJzYCBpcyBgdHJ1ZWAuXHJcblx0XHQvLyBUaGUgZnVuY3Rpb24gcmVjZWl2ZXMgYm90aCB0aGUgYEwuTGF5ZXJgIGluc3RhbmNlcyBhbmQgdGhlaXIgbmFtZXMsIGFzIGluXHJcblx0XHQvLyBgc29ydEZ1bmN0aW9uKGxheWVyQSwgbGF5ZXJCLCBuYW1lQSwgbmFtZUIpYC5cclxuXHRcdC8vIEJ5IGRlZmF1bHQsIGl0IHNvcnRzIGxheWVycyBhbHBoYWJldGljYWxseSBieSB0aGVpciBuYW1lLlxyXG5cdFx0c29ydEZ1bmN0aW9uOiBmdW5jdGlvbiAobGF5ZXJBLCBsYXllckIsIG5hbWVBLCBuYW1lQikge1xyXG5cdFx0XHRyZXR1cm4gbmFtZUEgPCBuYW1lQiA/IC0xIDogKG5hbWVCIDwgbmFtZUEgPyAxIDogMCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGJhc2VMYXllcnMsIG92ZXJsYXlzLCBvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJzID0gW107XHJcblx0XHR0aGlzLl9sYXN0WkluZGV4ID0gMDtcclxuXHRcdHRoaXMuX2hhbmRsaW5nQ2xpY2sgPSBmYWxzZTtcclxuXHJcblx0XHRmb3IgKHZhciBpIGluIGJhc2VMYXllcnMpIHtcclxuXHRcdFx0dGhpcy5fYWRkTGF5ZXIoYmFzZUxheWVyc1tpXSwgaSk7XHJcblx0XHR9XHJcblxyXG5cdFx0Zm9yIChpIGluIG92ZXJsYXlzKSB7XHJcblx0XHRcdHRoaXMuX2FkZExheWVyKG92ZXJsYXlzW2ldLCBpLCB0cnVlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dGhpcy5faW5pdExheW91dCgpO1xyXG5cdFx0dGhpcy5fdXBkYXRlKCk7XHJcblxyXG5cdFx0dGhpcy5fbWFwID0gbWFwO1xyXG5cdFx0bWFwLm9uKCd6b29tZW5kJywgdGhpcy5fY2hlY2tEaXNhYmxlZExheWVycywgdGhpcyk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fbWFwLm9mZignem9vbWVuZCcsIHRoaXMuX2NoZWNrRGlzYWJsZWRMYXllcnMsIHRoaXMpO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5fbGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdHRoaXMuX2xheWVyc1tpXS5sYXllci5vZmYoJ2FkZCByZW1vdmUnLCB0aGlzLl9vbkxheWVyQ2hhbmdlLCB0aGlzKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZEJhc2VMYXllcihsYXllcjogTGF5ZXIsIG5hbWU6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBBZGRzIGEgYmFzZSBsYXllciAocmFkaW8gYnV0dG9uIGVudHJ5KSB3aXRoIHRoZSBnaXZlbiBuYW1lIHRvIHRoZSBjb250cm9sLlxyXG5cdGFkZEJhc2VMYXllcjogZnVuY3Rpb24gKGxheWVyLCBuYW1lKSB7XHJcblx0XHR0aGlzLl9hZGRMYXllcihsYXllciwgbmFtZSk7XHJcblx0XHRyZXR1cm4gKHRoaXMuX21hcCkgPyB0aGlzLl91cGRhdGUoKSA6IHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRPdmVybGF5KGxheWVyOiBMYXllciwgbmFtZTogU3RyaW5nKTogdGhpc1xyXG5cdC8vIEFkZHMgYW4gb3ZlcmxheSAoY2hlY2tib3ggZW50cnkpIHdpdGggdGhlIGdpdmVuIG5hbWUgdG8gdGhlIGNvbnRyb2wuXHJcblx0YWRkT3ZlcmxheTogZnVuY3Rpb24gKGxheWVyLCBuYW1lKSB7XHJcblx0XHR0aGlzLl9hZGRMYXllcihsYXllciwgbmFtZSwgdHJ1ZSk7XHJcblx0XHRyZXR1cm4gKHRoaXMuX21hcCkgPyB0aGlzLl91cGRhdGUoKSA6IHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlIHRoZSBnaXZlbiBsYXllciBmcm9tIHRoZSBjb250cm9sLlxyXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdGxheWVyLm9mZignYWRkIHJlbW92ZScsIHRoaXMuX29uTGF5ZXJDaGFuZ2UsIHRoaXMpO1xyXG5cclxuXHRcdHZhciBvYmogPSB0aGlzLl9nZXRMYXllcihMLnN0YW1wKGxheWVyKSk7XHJcblx0XHRpZiAob2JqKSB7XHJcblx0XHRcdHRoaXMuX2xheWVycy5zcGxpY2UodGhpcy5fbGF5ZXJzLmluZGV4T2Yob2JqKSwgMSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gKHRoaXMuX21hcCkgPyB0aGlzLl91cGRhdGUoKSA6IHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBleHBhbmQoKTogdGhpc1xyXG5cdC8vIEV4cGFuZCB0aGUgY29udHJvbCBjb250YWluZXIgaWYgY29sbGFwc2VkLlxyXG5cdGV4cGFuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtZXhwYW5kZWQnKTtcclxuXHRcdHRoaXMuX2Zvcm0uc3R5bGUuaGVpZ2h0ID0gbnVsbDtcclxuXHRcdHZhciBhY2NlcHRhYmxlSGVpZ2h0ID0gdGhpcy5fbWFwLmdldFNpemUoKS55IC0gKHRoaXMuX2NvbnRhaW5lci5vZmZzZXRUb3AgKyA1MCk7XHJcblx0XHRpZiAoYWNjZXB0YWJsZUhlaWdodCA8IHRoaXMuX2Zvcm0uY2xpZW50SGVpZ2h0KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9mb3JtLCAnbGVhZmxldC1jb250cm9sLWxheWVycy1zY3JvbGxiYXInKTtcclxuXHRcdFx0dGhpcy5fZm9ybS5zdHlsZS5oZWlnaHQgPSBhY2NlcHRhYmxlSGVpZ2h0ICsgJ3B4JztcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9mb3JtLCAnbGVhZmxldC1jb250cm9sLWxheWVycy1zY3JvbGxiYXInKTtcclxuXHRcdH1cclxuXHRcdHRoaXMuX2NoZWNrRGlzYWJsZWRMYXllcnMoKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY29sbGFwc2UoKTogdGhpc1xyXG5cdC8vIENvbGxhcHNlIHRoZSBjb250cm9sIGNvbnRhaW5lciBpZiBleHBhbmRlZC5cclxuXHRjb2xsYXBzZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtZXhwYW5kZWQnKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9pbml0TGF5b3V0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY2xhc3NOYW1lID0gJ2xlYWZsZXQtY29udHJvbC1sYXllcnMnLFxyXG5cdFx0ICAgIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSksXHJcblx0XHQgICAgY29sbGFwc2VkID0gdGhpcy5vcHRpb25zLmNvbGxhcHNlZDtcclxuXHJcblx0XHQvLyBtYWtlcyB0aGlzIHdvcmsgb24gSUUgdG91Y2ggZGV2aWNlcyBieSBzdG9wcGluZyBpdCBmcm9tIGZpcmluZyBhIG1vdXNlb3V0IGV2ZW50IHdoZW4gdGhlIHRvdWNoIGlzIHJlbGVhc2VkXHJcblx0XHRjb250YWluZXIuc2V0QXR0cmlidXRlKCdhcmlhLWhhc3BvcHVwJywgdHJ1ZSk7XHJcblxyXG5cdFx0TC5Eb21FdmVudC5kaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbihjb250YWluZXIpO1xyXG5cdFx0aWYgKCFMLkJyb3dzZXIudG91Y2gpIHtcclxuXHRcdFx0TC5Eb21FdmVudC5kaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb24oY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZm9ybSA9IHRoaXMuX2Zvcm0gPSBMLkRvbVV0aWwuY3JlYXRlKCdmb3JtJywgY2xhc3NOYW1lICsgJy1saXN0Jyk7XHJcblxyXG5cdFx0aWYgKGNvbGxhcHNlZCkge1xyXG5cdFx0XHR0aGlzLl9tYXAub24oJ2NsaWNrJywgdGhpcy5jb2xsYXBzZSwgdGhpcyk7XHJcblxyXG5cdFx0XHRpZiAoIUwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdFx0TC5Eb21FdmVudC5vbihjb250YWluZXIsIHtcclxuXHRcdFx0XHRcdG1vdXNlZW50ZXI6IHRoaXMuZXhwYW5kLFxyXG5cdFx0XHRcdFx0bW91c2VsZWF2ZTogdGhpcy5jb2xsYXBzZVxyXG5cdFx0XHRcdH0sIHRoaXMpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIGxpbmsgPSB0aGlzLl9sYXllcnNMaW5rID0gTC5Eb21VdGlsLmNyZWF0ZSgnYScsIGNsYXNzTmFtZSArICctdG9nZ2xlJywgY29udGFpbmVyKTtcclxuXHRcdGxpbmsuaHJlZiA9ICcjJztcclxuXHRcdGxpbmsudGl0bGUgPSAnTGF5ZXJzJztcclxuXHJcblx0XHRpZiAoTC5Ccm93c2VyLnRvdWNoKSB7XHJcblx0XHRcdEwuRG9tRXZlbnRcclxuXHRcdFx0ICAgIC5vbihsaW5rLCAnY2xpY2snLCBMLkRvbUV2ZW50LnN0b3ApXHJcblx0XHRcdCAgICAub24obGluaywgJ2NsaWNrJywgdGhpcy5leHBhbmQsIHRoaXMpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0TC5Eb21FdmVudC5vbihsaW5rLCAnZm9jdXMnLCB0aGlzLmV4cGFuZCwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gd29yayBhcm91bmQgZm9yIEZpcmVmb3ggQW5kcm9pZCBpc3N1ZSBodHRwczovL2dpdGh1Yi5jb20vTGVhZmxldC9MZWFmbGV0L2lzc3Vlcy8yMDMzXHJcblx0XHRMLkRvbUV2ZW50Lm9uKGZvcm0sICdjbGljaycsIGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0c2V0VGltZW91dChMLmJpbmQodGhpcy5fb25JbnB1dENsaWNrLCB0aGlzKSwgMCk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHQvLyBUT0RPIGtleWJvYXJkIGFjY2Vzc2liaWxpdHlcclxuXHJcblx0XHRpZiAoIWNvbGxhcHNlZCkge1xyXG5cdFx0XHR0aGlzLmV4cGFuZCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2Jhc2VMYXllcnNMaXN0ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lICsgJy1iYXNlJywgZm9ybSk7XHJcblx0XHR0aGlzLl9zZXBhcmF0b3IgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUgKyAnLXNlcGFyYXRvcicsIGZvcm0pO1xyXG5cdFx0dGhpcy5fb3ZlcmxheXNMaXN0ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lICsgJy1vdmVybGF5cycsIGZvcm0pO1xyXG5cclxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChmb3JtKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0TGF5ZXI6IGZ1bmN0aW9uIChpZCkge1xyXG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLl9sYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHJcblx0XHRcdGlmICh0aGlzLl9sYXllcnNbaV0gJiYgTC5zdGFtcCh0aGlzLl9sYXllcnNbaV0ubGF5ZXIpID09PSBpZCkge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLl9sYXllcnNbaV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfYWRkTGF5ZXI6IGZ1bmN0aW9uIChsYXllciwgbmFtZSwgb3ZlcmxheSkge1xyXG5cdFx0bGF5ZXIub24oJ2FkZCByZW1vdmUnLCB0aGlzLl9vbkxheWVyQ2hhbmdlLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9sYXllcnMucHVzaCh7XHJcblx0XHRcdGxheWVyOiBsYXllcixcclxuXHRcdFx0bmFtZTogbmFtZSxcclxuXHRcdFx0b3ZlcmxheTogb3ZlcmxheVxyXG5cdFx0fSk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5zb3J0TGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMuX2xheWVycy5zb3J0KEwuYmluZChmdW5jdGlvbiAoYSwgYikge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLm9wdGlvbnMuc29ydEZ1bmN0aW9uKGEubGF5ZXIsIGIubGF5ZXIsIGEubmFtZSwgYi5uYW1lKTtcclxuXHRcdFx0fSwgdGhpcykpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuYXV0b1pJbmRleCAmJiBsYXllci5zZXRaSW5kZXgpIHtcclxuXHRcdFx0dGhpcy5fbGFzdFpJbmRleCsrO1xyXG5cdFx0XHRsYXllci5zZXRaSW5kZXgodGhpcy5fbGFzdFpJbmRleCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9jb250YWluZXIpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRMLkRvbVV0aWwuZW1wdHkodGhpcy5fYmFzZUxheWVyc0xpc3QpO1xyXG5cdFx0TC5Eb21VdGlsLmVtcHR5KHRoaXMuX292ZXJsYXlzTGlzdCk7XHJcblxyXG5cdFx0dmFyIGJhc2VMYXllcnNQcmVzZW50LCBvdmVybGF5c1ByZXNlbnQsIGksIG9iaiwgYmFzZUxheWVyc0NvdW50ID0gMDtcclxuXHJcblx0XHRmb3IgKGkgPSAwOyBpIDwgdGhpcy5fbGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdG9iaiA9IHRoaXMuX2xheWVyc1tpXTtcclxuXHRcdFx0dGhpcy5fYWRkSXRlbShvYmopO1xyXG5cdFx0XHRvdmVybGF5c1ByZXNlbnQgPSBvdmVybGF5c1ByZXNlbnQgfHwgb2JqLm92ZXJsYXk7XHJcblx0XHRcdGJhc2VMYXllcnNQcmVzZW50ID0gYmFzZUxheWVyc1ByZXNlbnQgfHwgIW9iai5vdmVybGF5O1xyXG5cdFx0XHRiYXNlTGF5ZXJzQ291bnQgKz0gIW9iai5vdmVybGF5ID8gMSA6IDA7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gSGlkZSBiYXNlIGxheWVycyBzZWN0aW9uIGlmIHRoZXJlJ3Mgb25seSBvbmUgbGF5ZXIuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmhpZGVTaW5nbGVCYXNlKSB7XHJcblx0XHRcdGJhc2VMYXllcnNQcmVzZW50ID0gYmFzZUxheWVyc1ByZXNlbnQgJiYgYmFzZUxheWVyc0NvdW50ID4gMTtcclxuXHRcdFx0dGhpcy5fYmFzZUxheWVyc0xpc3Quc3R5bGUuZGlzcGxheSA9IGJhc2VMYXllcnNQcmVzZW50ID8gJycgOiAnbm9uZSc7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fc2VwYXJhdG9yLnN0eWxlLmRpc3BsYXkgPSBvdmVybGF5c1ByZXNlbnQgJiYgYmFzZUxheWVyc1ByZXNlbnQgPyAnJyA6ICdub25lJztcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfb25MYXllckNoYW5nZTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdGlmICghdGhpcy5faGFuZGxpbmdDbGljaykge1xyXG5cdFx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgb2JqID0gdGhpcy5fZ2V0TGF5ZXIoTC5zdGFtcChlLnRhcmdldCkpO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBMYXllciBldmVudHNcclxuXHRcdC8vIEBldmVudCBiYXNlbGF5ZXJjaGFuZ2U6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYmFzZSBsYXllciBpcyBjaGFuZ2VkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlhZGQ6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIHNlbGVjdGVkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlyZW1vdmU6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIGRlc2VsZWN0ZWQgdGhyb3VnaCB0aGUgW2xheWVyIGNvbnRyb2xdKCNjb250cm9sLWxheWVycykuXHJcblx0XHQvLyBAbmFtZXNwYWNlIENvbnRyb2wuTGF5ZXJzXHJcblx0XHR2YXIgdHlwZSA9IG9iai5vdmVybGF5ID9cclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnb3ZlcmxheWFkZCcgOiAnb3ZlcmxheXJlbW92ZScpIDpcclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnYmFzZWxheWVyY2hhbmdlJyA6IG51bGwpO1xyXG5cclxuXHRcdGlmICh0eXBlKSB7XHJcblx0XHRcdHRoaXMuX21hcC5maXJlKHR5cGUsIG9iaik7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gSUU3IGJ1Z3Mgb3V0IGlmIHlvdSBjcmVhdGUgYSByYWRpbyBkeW5hbWljYWxseSwgc28geW91IGhhdmUgdG8gZG8gaXQgdGhpcyBoYWNreSB3YXkgKHNlZSBodHRwOi8vYml0Lmx5L1BxWUxCZSlcclxuXHRfY3JlYXRlUmFkaW9FbGVtZW50OiBmdW5jdGlvbiAobmFtZSwgY2hlY2tlZCkge1xyXG5cclxuXHRcdHZhciByYWRpb0h0bWwgPSAnPGlucHV0IHR5cGU9XCJyYWRpb1wiIGNsYXNzPVwibGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3RvclwiIG5hbWU9XCInICtcclxuXHRcdFx0XHRuYW1lICsgJ1wiJyArIChjaGVja2VkID8gJyBjaGVja2VkPVwiY2hlY2tlZFwiJyA6ICcnKSArICcvPic7XHJcblxyXG5cdFx0dmFyIHJhZGlvRnJhZ21lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHRcdHJhZGlvRnJhZ21lbnQuaW5uZXJIVE1MID0gcmFkaW9IdG1sO1xyXG5cclxuXHRcdHJldHVybiByYWRpb0ZyYWdtZW50LmZpcnN0Q2hpbGQ7XHJcblx0fSxcclxuXHJcblx0X2FkZEl0ZW06IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHZhciBsYWJlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xhYmVsJyksXHJcblx0XHQgICAgY2hlY2tlZCA9IHRoaXMuX21hcC5oYXNMYXllcihvYmoubGF5ZXIpLFxyXG5cdFx0ICAgIGlucHV0O1xyXG5cclxuXHRcdGlmIChvYmoub3ZlcmxheSkge1xyXG5cdFx0XHRpbnB1dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7XHJcblx0XHRcdGlucHV0LnR5cGUgPSAnY2hlY2tib3gnO1xyXG5cdFx0XHRpbnB1dC5jbGFzc05hbWUgPSAnbGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3Rvcic7XHJcblx0XHRcdGlucHV0LmRlZmF1bHRDaGVja2VkID0gY2hlY2tlZDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGlucHV0ID0gdGhpcy5fY3JlYXRlUmFkaW9FbGVtZW50KCdsZWFmbGV0LWJhc2UtbGF5ZXJzJywgY2hlY2tlZCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aW5wdXQubGF5ZXJJZCA9IEwuc3RhbXAob2JqLmxheWVyKTtcclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9uKGlucHV0LCAnY2xpY2snLCB0aGlzLl9vbklucHV0Q2xpY2ssIHRoaXMpO1xyXG5cclxuXHRcdHZhciBuYW1lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xyXG5cdFx0bmFtZS5pbm5lckhUTUwgPSAnICcgKyBvYmoubmFtZTtcclxuXHJcblx0XHQvLyBIZWxwcyBmcm9tIHByZXZlbnRpbmcgbGF5ZXIgY29udHJvbCBmbGlja2VyIHdoZW4gY2hlY2tib3hlcyBhcmUgZGlzYWJsZWRcclxuXHRcdC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzI3NzFcclxuXHRcdHZhciBob2xkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHJcblx0XHRsYWJlbC5hcHBlbmRDaGlsZChob2xkZXIpO1xyXG5cdFx0aG9sZGVyLmFwcGVuZENoaWxkKGlucHV0KTtcclxuXHRcdGhvbGRlci5hcHBlbmRDaGlsZChuYW1lKTtcclxuXHJcblx0XHR2YXIgY29udGFpbmVyID0gb2JqLm92ZXJsYXkgPyB0aGlzLl9vdmVybGF5c0xpc3QgOiB0aGlzLl9iYXNlTGF5ZXJzTGlzdDtcclxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChsYWJlbCk7XHJcblxyXG5cdFx0dGhpcy5fY2hlY2tEaXNhYmxlZExheWVycygpO1xyXG5cdFx0cmV0dXJuIGxhYmVsO1xyXG5cdH0sXHJcblxyXG5cdF9vbklucHV0Q2xpY2s6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBpbnB1dHMgPSB0aGlzLl9mb3JtLmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbnB1dCcpLFxyXG5cdFx0ICAgIGlucHV0LCBsYXllciwgaGFzTGF5ZXI7XHJcblx0XHR2YXIgYWRkZWRMYXllcnMgPSBbXSxcclxuXHRcdCAgICByZW1vdmVkTGF5ZXJzID0gW107XHJcblxyXG5cdFx0dGhpcy5faGFuZGxpbmdDbGljayA9IHRydWU7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IGlucHV0cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xyXG5cdFx0XHRpbnB1dCA9IGlucHV0c1tpXTtcclxuXHRcdFx0bGF5ZXIgPSB0aGlzLl9nZXRMYXllcihpbnB1dC5sYXllcklkKS5sYXllcjtcclxuXHRcdFx0aGFzTGF5ZXIgPSB0aGlzLl9tYXAuaGFzTGF5ZXIobGF5ZXIpO1xyXG5cclxuXHRcdFx0aWYgKGlucHV0LmNoZWNrZWQgJiYgIWhhc0xheWVyKSB7XHJcblx0XHRcdFx0YWRkZWRMYXllcnMucHVzaChsYXllcik7XHJcblxyXG5cdFx0XHR9IGVsc2UgaWYgKCFpbnB1dC5jaGVja2VkICYmIGhhc0xheWVyKSB7XHJcblx0XHRcdFx0cmVtb3ZlZExheWVycy5wdXNoKGxheWVyKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEJ1Z2ZpeCBpc3N1ZSAyMzE4OiBTaG91bGQgcmVtb3ZlIGFsbCBvbGQgbGF5ZXJzIGJlZm9yZSByZWFkZGluZyBuZXcgb25lc1xyXG5cdFx0Zm9yIChpID0gMDsgaSA8IHJlbW92ZWRMYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGhpcy5fbWFwLnJlbW92ZUxheWVyKHJlbW92ZWRMYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0Zm9yIChpID0gMDsgaSA8IGFkZGVkTGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdHRoaXMuX21hcC5hZGRMYXllcihhZGRlZExheWVyc1tpXSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faGFuZGxpbmdDbGljayA9IGZhbHNlO1xyXG5cclxuXHRcdHRoaXMuX3JlZm9jdXNPbk1hcCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jaGVja0Rpc2FibGVkTGF5ZXJzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgaW5wdXRzID0gdGhpcy5fZm9ybS5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaW5wdXQnKSxcclxuXHRcdCAgICBpbnB1dCxcclxuXHRcdCAgICBsYXllcixcclxuXHRcdCAgICB6b29tID0gdGhpcy5fbWFwLmdldFpvb20oKTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gaW5wdXRzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XHJcblx0XHRcdGlucHV0ID0gaW5wdXRzW2ldO1xyXG5cdFx0XHRsYXllciA9IHRoaXMuX2dldExheWVyKGlucHV0LmxheWVySWQpLmxheWVyO1xyXG5cdFx0XHRpbnB1dC5kaXNhYmxlZCA9IChsYXllci5vcHRpb25zLm1pblpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tIDwgbGF5ZXIub3B0aW9ucy5taW5ab29tKSB8fFxyXG5cdFx0XHQgICAgICAgICAgICAgICAgIChsYXllci5vcHRpb25zLm1heFpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tID4gbGF5ZXIub3B0aW9ucy5tYXhab29tKTtcclxuXHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2V4cGFuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gQmFja3dhcmQgY29tcGF0aWJpbGl0eSwgcmVtb3ZlIG1lIGluIDEuMS5cclxuXHRcdHJldHVybiB0aGlzLmV4cGFuZCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jb2xsYXBzZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gQmFja3dhcmQgY29tcGF0aWJpbGl0eSwgcmVtb3ZlIG1lIGluIDEuMS5cclxuXHRcdHJldHVybiB0aGlzLmNvbGxhcHNlKCk7XHJcblx0fVxyXG5cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5jb250cm9sLmxheWVycyhiYXNlbGF5ZXJzPzogT2JqZWN0LCBvdmVybGF5cz86IE9iamVjdCwgb3B0aW9ucz86IENvbnRyb2wuTGF5ZXJzIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYW4gYXR0cmlidXRpb24gY29udHJvbCB3aXRoIHRoZSBnaXZlbiBsYXllcnMuIEJhc2UgbGF5ZXJzIHdpbGwgYmUgc3dpdGNoZWQgd2l0aCByYWRpbyBidXR0b25zLCB3aGlsZSBvdmVybGF5cyB3aWxsIGJlIHN3aXRjaGVkIHdpdGggY2hlY2tib3hlcy4gTm90ZSB0aGF0IGFsbCBiYXNlIGxheWVycyBzaG91bGQgYmUgcGFzc2VkIGluIHRoZSBiYXNlIGxheWVycyBvYmplY3QsIGJ1dCBvbmx5IG9uZSBzaG91bGQgYmUgYWRkZWQgdG8gdGhlIG1hcCBkdXJpbmcgbWFwIGluc3RhbnRpYXRpb24uXHJcbkwuY29udHJvbC5sYXllcnMgPSBmdW5jdGlvbiAoYmFzZUxheWVycywgb3ZlcmxheXMsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuQ29udHJvbC5MYXllcnMoYmFzZUxheWVycywgb3ZlcmxheXMsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG59KHdpbmRvdywgZG9jdW1lbnQpKTtcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPWxlYWZsZXQtc3JjLm1hcCIsIi8qKlxuICogQGNsYXNzICBMLk1hdHJpeFxuICpcbiAqIEBwYXJhbSB7TnVtYmVyfSBhXG4gKiBAcGFyYW0ge051bWJlcn0gYlxuICogQHBhcmFtIHtOdW1iZXJ9IGNcbiAqIEBwYXJhbSB7TnVtYmVyfSBkXG4gKiBAcGFyYW0ge051bWJlcn0gZVxuICogQHBhcmFtIHtOdW1iZXJ9IGZcbiAqL1xuTC5NYXRyaXggPSBmdW5jdGlvbihhLCBiLCBjLCBkLCBlLCBmKSB7XG5cbiAgLyoqXG4gICAqIEB0eXBlIHtBcnJheS48TnVtYmVyPn1cbiAgICovXG4gIHRoaXMuX21hdHJpeCA9IFthLCBiLCBjLCBkLCBlLCBmXTtcbn07XG5cblxuTC5NYXRyaXgucHJvdG90eXBlID0ge1xuXG5cbiAgLyoqXG4gICAqIEBwYXJhbSAge0wuUG9pbnR9IHBvaW50XG4gICAqIEByZXR1cm4ge0wuUG9pbnR9XG4gICAqL1xuICB0cmFuc2Zvcm06IGZ1bmN0aW9uKHBvaW50KSB7XG4gICAgcmV0dXJuIHRoaXMuX3RyYW5zZm9ybShwb2ludC5jbG9uZSgpKTtcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBEZXN0cnVjdGl2ZVxuICAgKlxuICAgKiBbIHggXSA9IFsgYSAgYiAgdHggXSBbIHggXSA9IFsgYSAqIHggKyBiICogeSArIHR4IF1cbiAgICogWyB5IF0gPSBbIGMgIGQgIHR5IF0gWyB5IF0gPSBbIGMgKiB4ICsgZCAqIHkgKyB0eSBdXG4gICAqXG4gICAqIEBwYXJhbSAge0wuUG9pbnR9IHBvaW50XG4gICAqIEByZXR1cm4ge0wuUG9pbnR9XG4gICAqL1xuICBfdHJhbnNmb3JtOiBmdW5jdGlvbihwb2ludCkge1xuICAgIHZhciBtYXRyaXggPSB0aGlzLl9tYXRyaXg7XG4gICAgdmFyIHggPSBwb2ludC54LCB5ID0gcG9pbnQueTtcbiAgICBwb2ludC54ID0gbWF0cml4WzBdICogeCArIG1hdHJpeFsxXSAqIHkgKyBtYXRyaXhbNF07XG4gICAgcG9pbnQueSA9IG1hdHJpeFsyXSAqIHggKyBtYXRyaXhbM10gKiB5ICsgbWF0cml4WzVdO1xuICAgIHJldHVybiBwb2ludDtcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBAcGFyYW0gIHtMLlBvaW50fSBwb2ludFxuICAgKiBAcmV0dXJuIHtMLlBvaW50fVxuICAgKi9cbiAgdW50cmFuc2Zvcm06IGZ1bmN0aW9uIChwb2ludCkge1xuICAgIHZhciBtYXRyaXggPSB0aGlzLl9tYXRyaXg7XG4gICAgcmV0dXJuIG5ldyBMLlBvaW50KFxuICAgICAgKHBvaW50LnggLyBtYXRyaXhbMF0gLSBtYXRyaXhbNF0pIC8gbWF0cml4WzBdLFxuICAgICAgKHBvaW50LnkgLyBtYXRyaXhbMl0gLSBtYXRyaXhbNV0pIC8gbWF0cml4WzJdXG4gICAgKTtcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBAcmV0dXJuIHtMLk1hdHJpeH1cbiAgICovXG4gIGNsb25lOiBmdW5jdGlvbigpIHtcbiAgICB2YXIgbWF0cml4ID0gdGhpcy5fbWF0cml4O1xuICAgIHJldHVybiBuZXcgTC5NYXRyaXgoXG4gICAgICBtYXRyaXhbMF0sIG1hdHJpeFsxXSwgbWF0cml4WzJdLFxuICAgICAgbWF0cml4WzNdLCBtYXRyaXhbNF0sIG1hdHJpeFs1XVxuICAgICk7XG4gIH0sXG5cblxuICAvKipcbiAgICogQHBhcmFtIHtMLlBvaW50PXxOdW1iZXI9fSB0cmFuc2xhdGVcbiAgICogQHJldHVybiB7TC5NYXRyaXh8TC5Qb2ludH1cbiAgICovXG4gIHRyYW5zbGF0ZTogZnVuY3Rpb24odHJhbnNsYXRlKSB7XG4gICAgaWYgKHRyYW5zbGF0ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gbmV3IEwuUG9pbnQodGhpcy5fbWF0cml4WzRdLCB0aGlzLl9tYXRyaXhbNV0pO1xuICAgIH1cblxuICAgIHZhciB0cmFuc2xhdGVYLCB0cmFuc2xhdGVZO1xuICAgIGlmICh0eXBlb2YgdHJhbnNsYXRlID09PSAnbnVtYmVyJykge1xuICAgICAgdHJhbnNsYXRlWCA9IHRyYW5zbGF0ZVkgPSB0cmFuc2xhdGU7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRyYW5zbGF0ZVggPSB0cmFuc2xhdGUueDtcbiAgICAgIHRyYW5zbGF0ZVkgPSB0cmFuc2xhdGUueTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5fYWRkKDEsIDAsIDAsIDEsIHRyYW5zbGF0ZVgsIHRyYW5zbGF0ZVkpO1xuICB9LFxuXG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7TC5Qb2ludD18TnVtYmVyPX0gc2NhbGVcbiAgICogQHJldHVybiB7TC5NYXRyaXh8TC5Qb2ludH1cbiAgICovXG4gIHNjYWxlOiBmdW5jdGlvbihzY2FsZSwgb3JpZ2luKSB7XG4gICAgaWYgKHNjYWxlID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiBuZXcgTC5Qb2ludCh0aGlzLl9tYXRyaXhbMF0sIHRoaXMuX21hdHJpeFszXSk7XG4gICAgfVxuXG4gICAgdmFyIHNjYWxlWCwgc2NhbGVZO1xuICAgIG9yaWdpbiA9IG9yaWdpbiB8fCBMLnBvaW50KDAsIDApO1xuICAgIGlmICh0eXBlb2Ygc2NhbGUgPT09ICdudW1iZXInKSB7XG4gICAgICBzY2FsZVggPSBzY2FsZVkgPSBzY2FsZTtcbiAgICB9IGVsc2Uge1xuICAgICAgc2NhbGVYID0gc2NhbGUueDtcbiAgICAgIHNjYWxlWSA9IHNjYWxlLnk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXNcbiAgICAgIC5fYWRkKHNjYWxlWCwgMCwgMCwgc2NhbGVZLCBvcmlnaW4ueCwgb3JpZ2luLnkpXG4gICAgICAuX2FkZCgxLCAwLCAwLCAxLCAtb3JpZ2luLngsIC1vcmlnaW4ueSk7XG4gIH0sXG5cblxuICAvKipcbiAgICogbTAwICBtMDEgIHggLSBtMDAgKiB4IC0gbTAxICogeVxuICAgKiBtMTAgIG0xMSAgeSAtIG0xMCAqIHggLSBtMTEgKiB5XG4gICAqIEBwYXJhbSB7TnVtYmVyfSAgIGFuZ2xlXG4gICAqIEBwYXJhbSB7TC5Qb2ludD19IG9yaWdpblxuICAgKiBAcmV0dXJuIHtMLk1hdHJpeH1cbiAgICovXG4gIHJvdGF0ZTogZnVuY3Rpb24oYW5nbGUsIG9yaWdpbikge1xuICAgIHZhciBjb3MgPSBNYXRoLmNvcyhhbmdsZSk7XG4gICAgdmFyIHNpbiA9IE1hdGguc2luKGFuZ2xlKTtcblxuICAgIG9yaWdpbiA9IG9yaWdpbiB8fCBuZXcgTC5Qb2ludCgwLCAwKTtcblxuICAgIHJldHVybiB0aGlzXG4gICAgICAuX2FkZChjb3MsIHNpbiwgLXNpbiwgY29zLCBvcmlnaW4ueCwgb3JpZ2luLnkpXG4gICAgICAuX2FkZCgxLCAwLCAwLCAxLCAtb3JpZ2luLngsIC1vcmlnaW4ueSk7XG4gIH0sXG5cblxuICAvKipcbiAgICogSW52ZXJ0IHJvdGF0aW9uXG4gICAqIEByZXR1cm4ge0wuTWF0cml4fVxuICAgKi9cbiAgZmxpcDogZnVuY3Rpb24oKSB7XG4gICAgdGhpcy5fbWF0cml4WzFdICo9IC0xO1xuICAgIHRoaXMuX21hdHJpeFsyXSAqPSAtMTtcbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge051bWJlcnxMLk1hdHJpeH0gYVxuICAgKiBAcGFyYW0ge051bWJlcn0gYlxuICAgKiBAcGFyYW0ge051bWJlcn0gY1xuICAgKiBAcGFyYW0ge051bWJlcn0gZFxuICAgKiBAcGFyYW0ge051bWJlcn0gZVxuICAgKiBAcGFyYW0ge051bWJlcn0gZlxuICAgKi9cbiAgX2FkZDogZnVuY3Rpb24oYSwgYiwgYywgZCwgZSwgZikge1xuICAgIHZhciByZXN1bHQgPSBbW10sIFtdLCBbXV07XG4gICAgdmFyIHNyYyA9IHRoaXMuX21hdHJpeDtcbiAgICB2YXIgbSA9IFtcbiAgICAgIFtzcmNbMF0sIHNyY1syXSwgc3JjWzRdXSxcbiAgICAgIFtzcmNbMV0sIHNyY1szXSwgc3JjWzVdXSxcbiAgICAgIFsgICAgIDAsICAgICAgMCwgICAgIDFdXG4gICAgXTtcbiAgICB2YXIgb3RoZXIgPSBbXG4gICAgICBbYSwgYywgZV0sXG4gICAgICBbYiwgZCwgZl0sXG4gICAgICBbMCwgMCwgMV1cbiAgICBdLCB2YWw7XG5cblxuICAgIGlmIChhICYmIGEgaW5zdGFuY2VvZiBMLk1hdHJpeCkge1xuICAgICAgc3JjID0gYS5fbWF0cml4O1xuICAgICAgb3RoZXIgPSBbXG4gICAgICAgIFtzcmNbMF0sIHNyY1syXSwgc3JjWzRdXSxcbiAgICAgICAgW3NyY1sxXSwgc3JjWzNdLCBzcmNbNV1dLFxuICAgICAgICBbICAgICAwLCAgICAgIDAsICAgICAxXV07XG4gICAgfVxuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCAzOyBpKyspIHtcbiAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgMzsgaisrKSB7XG4gICAgICAgIHZhbCA9IDA7XG4gICAgICAgIGZvciAodmFyIGsgPSAwOyBrIDwgMzsgaysrKSB7XG4gICAgICAgICAgdmFsICs9IG1baV1ba10gKiBvdGhlcltrXVtqXTtcbiAgICAgICAgfVxuICAgICAgICByZXN1bHRbaV1bal0gPSB2YWw7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5fbWF0cml4ID0gW1xuICAgICAgcmVzdWx0WzBdWzBdLCByZXN1bHRbMV1bMF0sIHJlc3VsdFswXVsxXSxcbiAgICAgIHJlc3VsdFsxXVsxXSwgcmVzdWx0WzBdWzJdLCByZXN1bHRbMV1bMl1cbiAgICBdO1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cblxufTtcblxuXG5MLm1hdHJpeCA9IGZ1bmN0aW9uKGEsIGIsIGMsIGQsIGUsIGYpIHtcbiAgcmV0dXJuIG5ldyBMLk1hdHJpeChhLCBiLCBjLCBkLCBlLCBmKTtcbn07XG4iLCIvKipcbiAqIEBuYW1lc3BhY2VcbiAqIEB0eXBlIHtPYmplY3R9XG4gKi9cbkwuUGF0aFRyYW5zZm9ybSA9IHt9O1xuXG5cbi8qKlxuICogTWFya2VyIGhhbmRsZXJcbiAqIEBleHRlbmRzIHtMLkNpcmNsZU1hcmtlcn1cbiAqL1xuTC5QYXRoVHJhbnNmb3JtLkhhbmRsZSA9IEwuQ2lyY2xlTWFya2VyLmV4dGVuZCh7XG4gIG9wdGlvbnM6IHtcbiAgICBjbGFzc05hbWU6ICdsZWFmbGV0LXBhdGgtdHJhbnNmb3JtLWhhbmRsZXInXG4gIH0sXG5cbiAgb25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcbiAgICBMLkNpcmNsZU1hcmtlci5wcm90b3R5cGUub25BZGQuY2FsbCh0aGlzLCBtYXApO1xuICAgIGlmICh0aGlzLl9wYXRoICYmIHRoaXMub3B0aW9ucy5zZXRDdXJzb3IpIHsgLy8gU1ZHL1ZNTFxuICAgICAgdGhpcy5fcGF0aC5zdHlsZS5jdXJzb3IgPSBMLlBhdGhUcmFuc2Zvcm0uSGFuZGxlLkN1cnNvcnNCeVR5cGVbXG4gICAgICAgIHRoaXMub3B0aW9ucy5pbmRleFxuICAgICAgXTtcbiAgICB9XG4gIH1cbn0pO1xuXG5cbi8qKlxuICogQGNvbnN0XG4gKiBAdHlwZSB7QXJyYXl9XG4gKi9cbkwuUGF0aFRyYW5zZm9ybS5IYW5kbGUuQ3Vyc29yc0J5VHlwZSA9IFtcbiAgJ25lc3ctcmVzaXplJywgJ253c2UtcmVzaXplJywgJ25lc3ctcmVzaXplJywgJ253c2UtcmVzaXplJ1xuXTtcblxuXG4vKipcbiAqIEBleHRlbmRzIHtMLkhhbmRsZXIuUGF0aFRyYW5zZm9ybS5IYW5kbGV9XG4gKi9cbkwuUGF0aFRyYW5zZm9ybS5Sb3RhdGVIYW5kbGUgPSBMLlBhdGhUcmFuc2Zvcm0uSGFuZGxlLmV4dGVuZCh7XG4gIG9wdGlvbnM6IHtcbiAgICBjbGFzc05hbWU6ICdsZWFmbGV0LXBhdGgtdHJhbnNmb3JtLWhhbmRsZXIgdHJhbnNmb3JtLWhhbmRsZXItLXJvdGF0ZSdcbiAgfSxcblxuICBvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xuICAgIEwuQ2lyY2xlTWFya2VyLnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMsIG1hcCk7XG4gICAgaWYgKHRoaXMuX3BhdGggJiYgdGhpcy5vcHRpb25zLnNldEN1cnNvcikgeyAvLyBTVkcvVk1MXG4gICAgICB0aGlzLl9wYXRoLnN0eWxlLmN1cnNvciA9ICdhbGwtc2Nyb2xsJztcbiAgICB9XG4gIH1cbn0pO1xuXG5MLkhhbmRsZXIuUGF0aFRyYW5zZm9ybSA9IEwuSGFuZGxlci5leHRlbmQoe1xuXG4gIG9wdGlvbnM6IHtcbiAgICByb3RhdGlvbjogdHJ1ZSxcbiAgICBzY2FsaW5nOiAgdHJ1ZSxcbiAgICB1bmlmb3JtU2NhbGluZzogdHJ1ZSxcbiAgICBtYXhab29tOiAgMjIsXG5cbiAgICAvLyBlZGdlIGhhbmRsZXJzXG4gICAgaGFuZGxlck9wdGlvbnM6IHtcbiAgICAgIHJhZGl1czogICAgICA1LFxuICAgICAgZmlsbENvbG9yOiAgICcjZmZmZmZmJyxcbiAgICAgIGNvbG9yOiAgICAgICAnIzIwMjAyMCcsXG4gICAgICBmaWxsT3BhY2l0eTogMSxcbiAgICAgIHdlaWdodDogICAgICAyLFxuICAgICAgb3BhY2l0eTogICAgIDAuNyxcbiAgICAgIHNldEN1cnNvcjogICB0cnVlXG4gICAgfSxcblxuICAgIC8vIHJlY3RhbmdsZVxuICAgIGJvdW5kc09wdGlvbnM6IHtcbiAgICAgIHdlaWdodDogICAgMSxcbiAgICAgIG9wYWNpdHk6ICAgMSxcbiAgICAgIGRhc2hBcnJheTogWzMsIDNdLFxuICAgICAgZmlsbDogICAgICBmYWxzZVxuICAgIH0sXG5cbiAgICAvLyByb3RhdGlvbiBoYW5kbGVyXG4gICAgcm90YXRlSGFuZGxlT3B0aW9uczoge1xuICAgICAgd2VpZ2h0OiAgICAxLFxuICAgICAgb3BhY2l0eTogICAxLFxuICAgICAgc2V0Q3Vyc29yOiB0cnVlXG4gICAgfSxcbiAgICAvLyByb3RhdGlvbiBoYW5kbGUgbGVuZ3RoXG4gICAgaGFuZGxlTGVuZ3RoOiAyMCxcblxuICAgIC8vIG1heWJlIEknbGwgYWRkIHNrZXdpbmcgaW4gdGhlIGZ1dHVyZVxuICAgIGVkZ2VzQ291bnQ6ICAgNCxcblxuICAgIGhhbmRsZUNsYXNzOiAgICAgICBMLlBhdGhUcmFuc2Zvcm0uSGFuZGxlLFxuICAgIHJvdGF0ZUhhbmRsZUNsYXNzOiBMLlBhdGhUcmFuc2Zvcm0uUm90YXRlSGFuZGxlXG4gIH0sXG5cblxuICAvKipcbiAgICogQGNsYXNzIEwuSGFuZGxlci5QYXRoVHJhbnNmb3JtXG4gICAqIEBjb25zdHJ1Y3RvclxuICAgKiBAcGFyYW0gIHtMLlBhdGh9IHBhdGhcbiAgICovXG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uKHBhdGgpIHtcbiAgICAvLyByZWZlcmVuY2VzXG4gICAgdGhpcy5fcGF0aCA9IHBhdGg7XG4gICAgdGhpcy5fbWFwICA9IG51bGw7XG5cbiAgICAvLyBoYW5kbGVyc1xuICAgIHRoaXMuX2FjdGl2ZU1hcmtlciAgID0gbnVsbDtcbiAgICB0aGlzLl9vcmlnaW5NYXJrZXIgICA9IG51bGw7XG4gICAgdGhpcy5fcm90YXRpb25NYXJrZXIgPSBudWxsO1xuXG4gICAgLy8gb3JpZ2lucyAmIHRlbXBvcmFyeSBzdGF0ZVxuICAgIHRoaXMuX3JvdGF0aW9uT3JpZ2luICAgPSBudWxsO1xuICAgIHRoaXMuX3NjYWxlT3JpZ2luICAgICAgPSBudWxsO1xuICAgIHRoaXMuX2FuZ2xlICAgICAgICAgICAgPSAwO1xuICAgIHRoaXMuX3NjYWxlICAgICAgICAgICAgPSBMLnBvaW50KDEsIDEpO1xuICAgIHRoaXMuX2luaXRpYWxEaXN0ICAgICAgPSAwO1xuICAgIHRoaXMuX2luaXRpYWxEaXN0WCAgICAgPSAwO1xuICAgIHRoaXMuX2luaXRpYWxEaXN0WSAgICAgPSAwO1xuICAgIHRoaXMuX3JvdGF0aW9uU3RhcnQgICAgPSBudWxsO1xuICAgIHRoaXMuX3JvdGF0aW9uT3JpZ2luUHQgPSBudWxsO1xuXG4gICAgLy8gcHJldmlldyBhbmQgdHJhbnNmb3JtIG1hdHJpeFxuICAgIHRoaXMuX21hdHJpeCAgICAgICAgICA9IG5ldyBMLk1hdHJpeCgxLCAwLCAwLCAxLCAwLCAwKTtcbiAgICB0aGlzLl9wcm9qZWN0ZWRNYXRyaXggPSBuZXcgTC5NYXRyaXgoMSwgMCwgMCwgMSwgMCwgMCk7XG5cbiAgICAvLyB1aSBlbGVtZW50c1xuICAgIHRoaXMuX2hhbmRsZXJzR3JvdXAgID0gbnVsbDtcbiAgICB0aGlzLl9yZWN0ICAgICAgICAgICA9IG51bGw7XG4gICAgdGhpcy5faGFuZGxlcnMgICAgICAgPSBbXTtcbiAgICB0aGlzLl9oYW5kbGVMaW5lICAgICA9IG51bGw7XG4gIH0sXG5cblxuICAvKipcbiAgICogSWYgdGhlIHBvbHlnb24gaXMgbm90IHJlbmRlcmVkLCB5b3UgY2FuIHRyYW5zZm9ybSBpdCB5b3Vyc2VsZlxuICAgKiBpbiB0aGUgY29vcmRpbmF0ZXMsIGFuZCBkbyBpdCBwcm9wZXJseS5cbiAgICogQHBhcmFtIHtPYmplY3Q9fSBvcHRpb25zXG4gICAqL1xuICBlbmFibGU6IGZ1bmN0aW9uKG9wdGlvbnMpIHtcbiAgICBpZiAodGhpcy5fcGF0aC5fbWFwKSB7XG4gICAgICB0aGlzLl9tYXAgPSB0aGlzLl9wYXRoLl9tYXA7XG4gICAgICBpZiAob3B0aW9ucykge1xuICAgICAgICB0aGlzLnNldE9wdGlvbnMob3B0aW9ucyk7XG4gICAgICB9XG4gICAgICBMLkhhbmRsZXIucHJvdG90eXBlLmVuYWJsZS5jYWxsKHRoaXMpO1xuICAgIH1cbiAgfSxcblxuXG4gIC8qKlxuICAgKiBJbml0IGludGVyYWN0aW9ucyBhbmQgaGFuZGxlcnNcbiAgICovXG4gIGFkZEhvb2tzOiBmdW5jdGlvbigpIHtcbiAgICB0aGlzLl9jcmVhdGVIYW5kbGVycygpO1xuICAgIHRoaXMuX3BhdGhcbiAgICAgIC5vbignZHJhZ3N0YXJ0JywgdGhpcy5fb25EcmFnU3RhcnQsIHRoaXMpXG4gICAgICAub24oJ2RyYWdlbmQnLCAgIHRoaXMuX29uRHJhZ0VuZCwgICB0aGlzKTtcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBSZW1vdmUgaGFuZGxlcnNcbiAgICovXG4gIHJlbW92ZUhvb2tzOiBmdW5jdGlvbigpIHtcbiAgICB0aGlzLl9oaWRlSGFuZGxlcnMoKTtcbiAgICB0aGlzLl9wYXRoXG4gICAgICAub2ZmKCdkcmFnc3RhcnQnLCB0aGlzLl9vbkRyYWdTdGFydCwgdGhpcylcbiAgICAgIC5vZmYoJ2RyYWdlbmQnLCAgIHRoaXMuX29uRHJhZ0VuZCwgICB0aGlzKTtcbiAgICB0aGlzLl9oYW5kbGVyc0dyb3VwID0gbnVsbDtcbiAgICB0aGlzLl9yZWN0ID0gbnVsbDtcbiAgICB0aGlzLl9oYW5kbGVycyA9IFtdO1xuICB9LFxuXG5cbiAgLyoqXG4gICAqIENoYW5nZSBlZGl0aW5nIG9wdGlvbnNcbiAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnNcbiAgICovXG4gIHNldE9wdGlvbnM6IGZ1bmN0aW9uKG9wdGlvbnMpIHtcbiAgICB2YXIgZW5hYmxlZCA9IHRoaXMuX2VuYWJsZWQ7XG4gICAgaWYgKGVuYWJsZWQpIHtcbiAgICAgIHRoaXMuZGlzYWJsZSgpO1xuICAgIH1cblxuICAgIHRoaXMub3B0aW9ucyA9IEwuVXRpbC5tZXJnZSh7fSxcbiAgICAgIEwuSGFuZGxlci5QYXRoVHJhbnNmb3JtLnByb3RvdHlwZS5vcHRpb25zLFxuICAgICAgb3B0aW9ucyk7XG5cbiAgICBpZiAoZW5hYmxlZCkge1xuICAgICAgdGhpcy5lbmFibGUoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBAcGFyYW0gIHtOdW1iZXJ9ICAgYW5nbGVcbiAgICogQHBhcmFtICB7TC5MYXRMbmd9IG9yaWdpblxuICAgKiBAcmV0dXJuIHtMLkhhbmRsZXIuUGF0aFRyYW5zZm9ybX1cbiAgICovXG4gIHJvdGF0ZTogZnVuY3Rpb24oYW5nbGUsIG9yaWdpbikge1xuICAgIHJldHVybiB0aGlzLnRyYW5zZm9ybShhbmdsZSwgbnVsbCwgb3JpZ2luKTtcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBAcGFyYW0gIHtMLlBvaW50fE51bWJlcn0gc2NhbGVcbiAgICogQHBhcmFtICB7TC5MYXRMbmd9ICAgICAgIG9yaWdpblxuICAgKiBAcmV0dXJuIHtMLkhhbmRsZXIuUGF0aFRyYW5zZm9ybX1cbiAgICovXG4gIHNjYWxlOiBmdW5jdGlvbihzY2FsZSwgb3JpZ2luKSB7XG4gICAgaWYgKHR5cGVvZiBzY2FsZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIHNjYWxlID0gTC5wb2ludChzY2FsZSwgc2NhbGUpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy50cmFuc2Zvcm0oMCwgc2NhbGUsIG51bGwsIG9yaWdpbik7XG4gIH0sXG5cblxuICAvKipcbiAgICogQHBhcmFtICB7TnVtYmVyfSAgICBhbmdsZVxuICAgKiBAcGFyYW0gIHtMLlBvaW50fSAgIHNjYWxlXG4gICAqIEBwYXJhbSAge0wuTGF0TG5nPX0gcm90YXRpb25PcmlnaW5cbiAgICogQHBhcmFtICB7TC5MYXRMbmc9fSBzY2FsZU9yaWdpblxuICAgKiBAcmV0dXJuIHtMLkhhbmRsZXIuUGF0aFRyYW5zZm9ybX1cbiAgICovXG4gIHRyYW5zZm9ybTogZnVuY3Rpb24oYW5nbGUsIHNjYWxlLCByb3RhdGlvbk9yaWdpbiwgc2NhbGVPcmlnaW4pIHtcbiAgICB2YXIgY2VudGVyICAgICA9IHRoaXMuX3BhdGguZ2V0Q2VudGVyKCk7XG4gICAgcm90YXRpb25PcmlnaW4gPSByb3RhdGlvbk9yaWdpbiB8fCBjZW50ZXI7XG4gICAgc2NhbGVPcmlnaW4gICAgPSBzY2FsZU9yaWdpbiAgICB8fCBjZW50ZXI7XG4gICAgdGhpcy5fbWFwID0gdGhpcy5fcGF0aC5fbWFwO1xuICAgIHRoaXMuX3RyYW5zZm9ybVBvaW50cyh0aGlzLl9wYXRoLCBhbmdsZSwgc2NhbGUsIHJvdGF0aW9uT3JpZ2luLCBzY2FsZU9yaWdpbik7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH0sXG5cblxuICAvKipcbiAgICogVXBkYXRlIHRoZSBwb2x5Z29uIGFuZCBoYW5kbGVycyBwcmV2aWV3LCBubyByZXByb2plY3Rpb25cbiAgICovXG4gIF91cGRhdGU6IGZ1bmN0aW9uKCkge1xuICAgIHZhciBtYXRyaXggPSB0aGlzLl9tYXRyaXg7XG5cbiAgICAvLyB1cGRhdGUgaGFuZGxlcnNcbiAgICBmb3IgKHZhciBpID0gMCwgbGVuID0gdGhpcy5faGFuZGxlcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgIHZhciBoYW5kbGVyID0gdGhpcy5faGFuZGxlcnNbaV07XG4gICAgICBpZiAoaGFuZGxlciAhPT0gdGhpcy5fb3JpZ2luTWFya2VyKSB7XG4gICAgICAgIGhhbmRsZXIuX3BvaW50ID0gbWF0cml4LnRyYW5zZm9ybShoYW5kbGVyLl9pbml0aWFsUG9pbnQpO1xuICAgICAgICBoYW5kbGVyLl91cGRhdGVQYXRoKCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgbWF0cml4ID0gbWF0cml4LmNsb25lKCkuZmxpcCgpO1xuXG4gICAgdGhpcy5fYXBwbHlUcmFuc2Zvcm0obWF0cml4KTtcbiAgICB0aGlzLl9wYXRoLmZpcmUoJ3RyYW5zZm9ybScsIHsgbGF5ZXI6IHRoaXMuX3BhdGggfSk7XG4gIH0sXG5cblxuICAvKipcbiAgICogQHBhcmFtICB7TC5NYXRyaXh9IG1hdHJpeFxuICAgKi9cbiAgX2FwcGx5VHJhbnNmb3JtOiBmdW5jdGlvbihtYXRyaXgpIHtcbiAgICB0aGlzLl9wYXRoLl90cmFuc2Zvcm0obWF0cml4Ll9tYXRyaXgpO1xuICAgIHRoaXMuX3JlY3QuX3RyYW5zZm9ybShtYXRyaXguX21hdHJpeCk7XG5cbiAgICBpZiAodGhpcy5vcHRpb25zLnJvdGF0aW9uKSB7XG4gICAgICB0aGlzLl9oYW5kbGVMaW5lLl90cmFuc2Zvcm0obWF0cml4Ll9tYXRyaXgpO1xuICAgIH1cbiAgfSxcblxuXG4gIC8qKlxuICAgKiBBcHBseSBmaW5hbCB0cmFuc2Zvcm1hdGlvblxuICAgKi9cbiAgX2FwcGx5OiBmdW5jdGlvbigpIHtcbiAgICAvL2NvbnNvbGUuZ3JvdXAoJ2FwcGx5IHRyYW5zZm9ybScpO1xuICAgIHZhciBtYXAgPSB0aGlzLl9tYXA7XG4gICAgdmFyIG1hdHJpeCA9IHRoaXMuX21hdHJpeC5jbG9uZSgpO1xuICAgIHZhciBhbmdsZSA9IHRoaXMuX2FuZ2xlO1xuICAgIHZhciBzY2FsZSA9IHRoaXMuX3NjYWxlLmNsb25lKCk7XG5cbiAgICB0aGlzLl90cmFuc2Zvcm1HZW9tZXRyaWVzKCk7XG5cbiAgICAvLyB1cGRhdGUgaGFuZGxlcnNcbiAgICBmb3IgKHZhciBpID0gMCwgbGVuID0gdGhpcy5faGFuZGxlcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgIHZhciBoYW5kbGVyID0gdGhpcy5faGFuZGxlcnNbaV07XG4gICAgICBoYW5kbGVyLl9sYXRsbmcgPSBtYXAubGF5ZXJQb2ludFRvTGF0TG5nKGhhbmRsZXIuX3BvaW50KTtcbiAgICAgIGRlbGV0ZSBoYW5kbGVyLl9pbml0aWFsUG9pbnQ7XG4gICAgICBoYW5kbGVyLnJlZHJhdygpO1xuICAgIH1cblxuICAgIHRoaXMuX21hdHJpeCA9IEwubWF0cml4KDEsIDAsIDAsIDEsIDAsIDApO1xuICAgIHRoaXMuX3NjYWxlICA9IEwucG9pbnQoMSwgMSk7XG4gICAgdGhpcy5fYW5nbGUgID0gMDtcblxuICAgIHRoaXMuX3VwZGF0ZUhhbmRsZXJzKCk7XG5cbiAgICBtYXAuZHJhZ2dpbmcuZW5hYmxlKCk7XG4gICAgdGhpcy5fcGF0aC5maXJlKCd0cmFuc2Zvcm1lZCcsIHtcbiAgICAgIG1hdHJpeDogbWF0cml4LFxuICAgICAgc2NhbGU6IHNjYWxlLFxuICAgICAgcm90YXRpb246IGFuZ2xlLFxuICAgICAgLy8gYW5nbGU6IGFuZ2xlICogKDE4MCAvIE1hdGguUEkpLFxuICAgICAgbGF5ZXI6IHRoaXMuX3BhdGhcbiAgICB9KTtcbiAgICAvLyBjb25zb2xlLmdyb3VwRW5kKCdhcHBseSB0cmFuc2Zvcm0nKTtcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBSZWNhbGN1bGF0ZSByb3RhdGlvbiBoYW5kbGVycyBwb3NpdGlvblxuICAgKi9cbiAgX3VwZGF0ZUhhbmRsZXJzOiBmdW5jdGlvbigpIHtcbiAgICB2YXIgaGFuZGxlcnNHcm91cCA9IHRoaXMuX2hhbmRsZXJzR3JvdXA7XG5cbiAgICB0aGlzLl9yZWN0U2hhcGUgPSB0aGlzLl9yZWN0LnRvR2VvSlNPTigpO1xuXG4gICAgdGhpcy5faGFuZGxlcnNHcm91cC5yZW1vdmVMYXllcih0aGlzLl9oYW5kbGVMaW5lKTtcbiAgICB0aGlzLl9oYW5kbGVyc0dyb3VwLnJlbW92ZUxheWVyKHRoaXMuX3JvdGF0aW9uTWFya2VyKTtcblxuICAgIHRoaXMuX2hhbmRsZUxpbmUgPSB0aGlzLl9yb3RhdGlvbk1hcmtlciA9IG51bGw7XG5cbiAgICBmb3IgKHZhciBpID0gdGhpcy5faGFuZGxlcnMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgIGhhbmRsZXJzR3JvdXAucmVtb3ZlTGF5ZXIodGhpcy5faGFuZGxlcnNbaV0pO1xuICAgIH1cblxuICAgIHRoaXMuX2NyZWF0ZUhhbmRsZXJzKCk7XG4gIH0sXG5cblxuICAvKipcbiAgICogVHJhbnNmb3JtIGdlb21ldHJpZXMgc2VwYXJhdGVseVxuICAgKi9cbiAgX3RyYW5zZm9ybUdlb21ldHJpZXM6IGZ1bmN0aW9uKCkge1xuICAgIHRoaXMuX3BhdGguX3RyYW5zZm9ybShudWxsKTtcbiAgICB0aGlzLl9yZWN0Ll90cmFuc2Zvcm0obnVsbCk7XG5cbiAgICB0aGlzLl90cmFuc2Zvcm1Qb2ludHModGhpcy5fcGF0aCk7XG4gICAgdGhpcy5fdHJhbnNmb3JtUG9pbnRzKHRoaXMuX3JlY3QpO1xuXG4gICAgaWYgKHRoaXMub3B0aW9ucy5yb3RhdGlvbikge1xuICAgICAgdGhpcy5faGFuZGxlTGluZS5fdHJhbnNmb3JtKG51bGwpO1xuICAgICAgdGhpcy5fdHJhbnNmb3JtUG9pbnRzKHRoaXMuX2hhbmRsZUxpbmUsIHRoaXMuX2FuZ2xlLCBudWxsLCB0aGlzLl9vcmlnaW4pO1xuICAgIH1cbiAgfSxcblxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge051bWJlcn0gYW5nbGVcbiAgICogQHBhcmFtIHtOdW1iZXJ9IHNjYWxlXG4gICAqIEBwYXJhbSB7TC5MYXRMbmc9fSByb3RhdGlvbk9yaWdpblxuICAgKiBAcGFyYW0ge0wuTGF0TG5nPX0gc2NhbGVPcmlnaW5cbiAgICovXG4gIF9nZXRQcm9qZWN0ZWRNYXRyaXg6IGZ1bmN0aW9uKGFuZ2xlLCBzY2FsZSwgcm90YXRpb25PcmlnaW4sIHNjYWxlT3JpZ2luKSB7XG4gICAgdmFyIG1hcCAgICA9IHRoaXMuX21hcDtcbiAgICB2YXIgem9vbSAgID0gbWFwLmdldE1heFpvb20oKSB8fCB0aGlzLm9wdGlvbnMubWF4Wm9vbTtcbiAgICB2YXIgbWF0cml4ID0gTC5tYXRyaXgoMSwgMCwgMCwgMSwgMCwgMCk7XG4gICAgdmFyIG9yaWdpbjtcblxuICAgIGFuZ2xlID0gYW5nbGUgfHwgdGhpcy5fYW5nbGUgfHwgMDtcbiAgICBzY2FsZSA9IHNjYWxlIHx8IHRoaXMuX3NjYWxlIHx8IEwucG9pbnQoMSwgMSk7XG5cbiAgICBpZiAoIShzY2FsZS54ID09PSAxICYmIHNjYWxlLnkgPT09IDEpKSB7XG4gICAgICBzY2FsZU9yaWdpbiA9IHNjYWxlT3JpZ2luIHx8IHRoaXMuX3NjYWxlT3JpZ2luO1xuICAgICAgb3JpZ2luID0gbWFwLnByb2plY3Qoc2NhbGVPcmlnaW4sIHpvb20pO1xuICAgICAgbWF0cml4ID0gbWF0cml4XG4gICAgICAgIC5fYWRkKEwubWF0cml4KDEsIDAsIDAsIDEsIG9yaWdpbi54LCBvcmlnaW4ueSkpXG4gICAgICAgIC5fYWRkKEwubWF0cml4KHNjYWxlLngsIDAsIDAsIHNjYWxlLnksIDAsIDApKVxuICAgICAgICAuX2FkZChMLm1hdHJpeCgxLCAwLCAwLCAxLCAtb3JpZ2luLngsIC1vcmlnaW4ueSkpO1xuICAgIH1cblxuICAgIGlmIChhbmdsZSkge1xuICAgICAgcm90YXRpb25PcmlnaW4gPSByb3RhdGlvbk9yaWdpbiB8fCB0aGlzLl9yb3RhdGlvbk9yaWdpbjtcbiAgICAgIG9yaWdpbiA9IG1hcC5wcm9qZWN0KHJvdGF0aW9uT3JpZ2luLCB6b29tKTtcbiAgICAgIG1hdHJpeCA9IG1hdHJpeC5yb3RhdGUoYW5nbGUsIG9yaWdpbikuZmxpcCgpO1xuICAgIH1cblxuICAgIHJldHVybiBtYXRyaXg7XG4gIH0sXG5cblxuICAvKipcbiAgICogQHBhcmFtICB7TC5MYXRMbmd9IGxhdGxuZ1xuICAgKiBAcGFyYW0gIHtMLk1hdHJpeH0gbWF0cml4XG4gICAqIEBwYXJhbSAge0wuTWFwfSAgICBtYXBcbiAgICogQHBhcmFtICB7TnVtYmVyfSAgIHpvb21cbiAgICogQHJldHVybiB7TC5MYXRMbmd9XG4gICAqL1xuICBfdHJhbnNmb3JtUG9pbnQ6IGZ1bmN0aW9uKGxhdGxuZywgbWF0cml4LCBtYXAsIHpvb20pIHtcbiAgICByZXR1cm4gbWFwLnVucHJvamVjdChtYXRyaXgudHJhbnNmb3JtKFxuICAgICAgbWFwLnByb2plY3QobGF0bG5nLCB6b29tKSksIHpvb20pO1xuICB9LFxuXG5cbiAgLyoqXG4gICAqIEFwcGxpZXMgdHJhbnNmb3JtYXRpb24sIGRvZXMgaXQgaW4gb25lIHN3ZWVwIGZvciBwZXJmb3JtYW5jZSxcbiAgICogc28gZG9uJ3QgYmUgc3VycHJpc2VkIGFib3V0IHRoZSBjb2RlIHJlcGV0aXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSB7TC5QYXRofSAgICBwYXRoXG4gICAqIEBwYXJhbSB7TnVtYmVyPX0gICBhbmdsZVxuICAgKiBAcGFyYW0ge0wuUG9pbnQ9fSAgc2NhbGVcbiAgICogQHBhcmFtIHtMLkxhdExuZz19IHJvdGF0aW9uT3JpZ2luXG4gICAqIEBwYXJhbSB7TC5MYXRMbmc9fSBzY2FsZU9yaWdpblxuICAgKi9cbiAgX3RyYW5zZm9ybVBvaW50czogZnVuY3Rpb24ocGF0aCwgYW5nbGUsIHNjYWxlLCByb3RhdGlvbk9yaWdpbiwgc2NhbGVPcmlnaW4pIHtcbiAgICB2YXIgbWFwID0gcGF0aC5fbWFwO1xuICAgIHZhciB6b29tID0gbWFwLmdldE1heFpvb20oKSB8fCB0aGlzLm9wdGlvbnMubWF4Wm9vbTtcbiAgICB2YXIgaSwgbGVuO1xuXG4gICAgdmFyIHByb2plY3RlZE1hdHJpeCA9IHRoaXMuX3Byb2plY3RlZE1hdHJpeCA9XG4gICAgICB0aGlzLl9nZXRQcm9qZWN0ZWRNYXRyaXgoYW5nbGUsIHNjYWxlLCByb3RhdGlvbk9yaWdpbiwgc2NhbGVPcmlnaW4pO1xuICAgIC8vIGNvbnNvbGUudGltZSgndHJhbnNmb3JtJyk7XG5cbiAgICAvLyBhbGwgc2hpZnRzIGFyZSBpbi1wbGFjZVxuICAgIGlmIChwYXRoLl9wb2ludCkgeyAvLyBMLkNpcmNsZVxuICAgICAgcGF0aC5fbGF0bG5nID0gdGhpcy5fdHJhbnNmb3JtUG9pbnQoXG4gICAgICAgIHBhdGguX2xhdGxuZywgcHJvamVjdGVkTWF0cml4LCBtYXAsIHpvb20pO1xuICAgIH0gZWxzZSBpZiAocGF0aC5fcmluZ3MgfHwgcGF0aC5fcGFydHMpIHsgLy8gZXZlcnl0aGluZyBlbHNlXG4gICAgICB2YXIgcmluZ3MgPSBwYXRoLl9yaW5ncztcbiAgICAgIHZhciBsYXRsbmdzID0gcGF0aC5fbGF0bG5ncztcbiAgICAgIHBhdGguX2JvdW5kcyA9IG5ldyBMLkxhdExuZ0JvdW5kcygpO1xuXG4gICAgICBpZiAoIUwuVXRpbC5pc0FycmF5KGxhdGxuZ3NbMF0pKSB7IC8vIHBvbHlsaW5lXG4gICAgICAgIGxhdGxuZ3MgPSBbbGF0bG5nc107XG4gICAgICB9XG4gICAgICBmb3IgKGkgPSAwLCBsZW4gPSByaW5ncy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuICAgICAgICBmb3IgKHZhciBqID0gMCwgamogPSByaW5nc1tpXS5sZW5ndGg7IGogPCBqajsgaisrKSB7XG4gICAgICAgICAgbGF0bG5nc1tpXVtqXSA9IHRoaXMuX3RyYW5zZm9ybVBvaW50KFxuICAgICAgICAgICAgbGF0bG5nc1tpXVtqXSwgcHJvamVjdGVkTWF0cml4LCBtYXAsIHpvb20pO1xuICAgICAgICAgIHBhdGguX2JvdW5kcy5leHRlbmQobGF0bG5nc1tpXVtqXSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBwYXRoLl9yZXNldCgpO1xuICAgIC8vY29uc29sZS50aW1lRW5kKCd0cmFuc2Zvcm0nKTtcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIG1hcmtlcnMgYW5kIGhhbmRsZXNcbiAgICovXG4gIF9jcmVhdGVIYW5kbGVyczogZnVuY3Rpb24oKSB7XG4gICAgdmFyIG1hcCA9IHRoaXMuX21hcDtcbiAgICB0aGlzLl9oYW5kbGVyc0dyb3VwID0gdGhpcy5faGFuZGxlcnNHcm91cCB8fFxuICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcgTC5MYXllckdyb3VwKCkuYWRkVG8obWFwKTtcbiAgICB0aGlzLl9yZWN0ID0gdGhpcy5fcmVjdCB8fFxuICAgICAgICAgICAgICAgICB0aGlzLl9nZXRCb3VuZGluZ1BvbHlnb24oKS5hZGRUbyh0aGlzLl9oYW5kbGVyc0dyb3VwKTtcblxuICAgIGlmICh0aGlzLm9wdGlvbnMuc2NhbGluZykge1xuICAgICAgdGhpcy5faGFuZGxlcnMgPSBbXTtcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5vcHRpb25zLmVkZ2VzQ291bnQ7IGkrKykge1xuICAgICAgICAvLyBUT0RPOiBhZGQgc3RyZXRjaGluZ1xuICAgICAgICB0aGlzLl9oYW5kbGVycy5wdXNoKFxuICAgICAgICAgIHRoaXMuX2NyZWF0ZUhhbmRsZXIodGhpcy5fcmVjdC5fbGF0bG5nc1swXVtpXSwgaSAqIDIsIGkpXG4gICAgICAgICAgLmFkZFRvKHRoaXMuX2hhbmRsZXJzR3JvdXApKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBhZGQgYm91bmRzXG4gICAgaWYgKHRoaXMub3B0aW9ucy5yb3RhdGlvbikge1xuICAgICAgLy9hZGQgcm90YXRpb24gaGFuZGxlclxuICAgICAgdGhpcy5fY3JlYXRlUm90YXRpb25IYW5kbGVycygpO1xuICAgIH1cbiAgfSxcblxuXG4gIC8qKlxuICAgKiBSb3RhdGlvbiBtYXJrZXIgYW5kIHNtYWxsIGNvbm5lY3RpbiBoYW5kbGVcbiAgICovXG4gIF9jcmVhdGVSb3RhdGlvbkhhbmRsZXJzOiBmdW5jdGlvbigpIHtcbiAgICB2YXIgbWFwICAgICA9IHRoaXMuX21hcDtcbiAgICB2YXIgbGF0bG5ncyA9IHRoaXMuX3JlY3QuX2xhdGxuZ3NbMF07XG5cbiAgICB2YXIgYm90dG9tICAgPSBuZXcgTC5MYXRMbmcoXG4gICAgICAobGF0bG5nc1swXS5sYXQgKyBsYXRsbmdzWzNdLmxhdCkgLyAyLFxuICAgICAgKGxhdGxuZ3NbMF0ubG5nICsgbGF0bG5nc1szXS5sbmcpIC8gMik7XG4gICAgLy8gaGVoZSwgdG9wIGlzIGEgcmVzZXJ2ZWQgd29yZFxuICAgIHZhciB0b3BQb2ludCA9IG5ldyBMLkxhdExuZyhcbiAgICAgIChsYXRsbmdzWzFdLmxhdCArIGxhdGxuZ3NbMl0ubGF0KSAvIDIsXG4gICAgICAobGF0bG5nc1sxXS5sbmcgKyBsYXRsbmdzWzJdLmxuZykgLyAyKTtcblxuICAgIHZhciBoYW5kbGVyUG9zaXRpb24gPSBtYXAubGF5ZXJQb2ludFRvTGF0TG5nKFxuICAgICAgTC5MaW5lVXRpbC5wb2ludE9uTGluZShcbiAgICAgICAgbWFwLmxhdExuZ1RvTGF5ZXJQb2ludChib3R0b20pLFxuICAgICAgICBtYXAubGF0TG5nVG9MYXllclBvaW50KHRvcFBvaW50KSxcbiAgICAgICAgdGhpcy5vcHRpb25zLmhhbmRsZUxlbmd0aClcbiAgICApO1xuXG4gICAgdGhpcy5faGFuZGxlTGluZSA9IG5ldyBMLlBvbHlsaW5lKFt0b3BQb2ludCwgaGFuZGxlclBvc2l0aW9uXSxcbiAgICAgIHRoaXMub3B0aW9ucy5yb3RhdGVIYW5kbGVPcHRpb25zKS5hZGRUbyh0aGlzLl9oYW5kbGVyc0dyb3VwKTtcbiAgICB2YXIgUm90YXRlSGFuZGxlQ2xhc3MgPSB0aGlzLm9wdGlvbnMucm90YXRlSGFuZGxlQ2xhc3M7XG4gICAgdGhpcy5fcm90YXRpb25NYXJrZXIgPSBuZXcgUm90YXRlSGFuZGxlQ2xhc3MoaGFuZGxlclBvc2l0aW9uLFxuICAgICAgdGhpcy5vcHRpb25zLmhhbmRsZXJPcHRpb25zKVxuICAgICAgLmFkZFRvKHRoaXMuX2hhbmRsZXJzR3JvdXApXG4gICAgICAub24oJ21vdXNlZG93bicsIHRoaXMuX29uUm90YXRlU3RhcnQsIHRoaXMpO1xuXG4gICAgdGhpcy5fcm90YXRpb25PcmlnaW4gPSBuZXcgTC5MYXRMbmcoXG4gICAgICAodG9wUG9pbnQubGF0ICsgYm90dG9tLmxhdCkgLyAyLFxuICAgICAgKHRvcFBvaW50LmxuZyArIGJvdHRvbS5sbmcpIC8gMlxuICAgICk7XG5cbiAgICB0aGlzLl9oYW5kbGVycy5wdXNoKHRoaXMuX3JvdGF0aW9uTWFya2VyKTtcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBAcmV0dXJuIHtMLkxhdExuZ31cbiAgICovXG4gIF9nZXRSb3RhdGlvbk9yaWdpbjogZnVuY3Rpb24oKSB7XG4gICAgdmFyIGxhdGxuZ3MgPSB0aGlzLl9yZWN0Ll9sYXRsbmdzWzBdO1xuICAgIHZhciBsYiA9IGxhdGxuZ3NbMF07XG4gICAgdmFyIHJ0ID0gbGF0bG5nc1syXTtcblxuICAgIHJldHVybiBuZXcgTC5MYXRMbmcoXG4gICAgICAobGIubGF0ICsgcnQubGF0KSAvIDIsXG4gICAgICAobGIubG5nICsgcnQubG5nKSAvIDJcbiAgICApO1xuICB9LFxuXG5cbiAgLyoqXG4gICAqIFNlY3VyZSB0aGUgcm90YXRpb24gb3JpZ2luXG4gICAqIEBwYXJhbSAge0V2ZW50fSBldnRcbiAgICovXG4gIF9vblJvdGF0ZVN0YXJ0OiBmdW5jdGlvbihldnQpIHtcbiAgICB2YXIgbWFwID0gdGhpcy5fbWFwO1xuXG4gICAgbWFwLmRyYWdnaW5nLmRpc2FibGUoKTtcblxuICAgIHRoaXMuX29yaWdpbk1hcmtlciAgICAgPSBudWxsO1xuICAgIHRoaXMuX3JvdGF0aW9uT3JpZ2luUHQgPSBtYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2dldFJvdGF0aW9uT3JpZ2luKCkpO1xuICAgIHRoaXMuX3JvdGF0aW9uU3RhcnQgICAgPSBldnQubGF5ZXJQb2ludDtcbiAgICB0aGlzLl9pbml0aWFsTWF0cml4ICAgID0gdGhpcy5fbWF0cml4LmNsb25lKCk7XG5cbiAgICB0aGlzLl9hbmdsZSA9IDA7XG4gICAgdGhpcy5fcGF0aC5fbWFwXG4gICAgICAub24oJ21vdXNlbW92ZScsIHRoaXMuX29uUm90YXRlLCAgICAgdGhpcylcbiAgICAgIC5vbignbW91c2V1cCcsICAgdGhpcy5fb25Sb3RhdGVFbmQsIHRoaXMpO1xuXG4gICAgdGhpcy5fY2FjaGVQb2ludHMoKTtcbiAgICB0aGlzLl9wYXRoXG4gICAgICAuZmlyZSgndHJhbnNmb3Jtc3RhcnQnLCAgIHsgbGF5ZXI6IHRoaXMuX3BhdGggfSlcbiAgICAgIC5maXJlKCdyb3RhdGVzdGFydCcsIHsgbGF5ZXI6IHRoaXMuX3BhdGgsIHJvdGF0aW9uOiAwIH0pO1xuICB9LFxuXG5cbiAgLyoqXG4gICAqIEBwYXJhbSAge0V2ZW50fSBldnRcbiAgICovXG4gIF9vblJvdGF0ZTogZnVuY3Rpb24oZXZ0KSB7XG4gICAgdmFyIHBvcyA9IGV2dC5sYXllclBvaW50O1xuICAgIHZhciBwcmV2aW91cyA9IHRoaXMuX3JvdGF0aW9uU3RhcnQ7XG4gICAgdmFyIG9yaWdpbiAgID0gdGhpcy5fcm90YXRpb25PcmlnaW5QdDtcblxuICAgIC8vIHJvdGF0aW9uIHN0ZXAgYW5nbGVcbiAgICB0aGlzLl9hbmdsZSA9IE1hdGguYXRhbjIocG9zLnkgLSBvcmlnaW4ueSwgcG9zLnggLSBvcmlnaW4ueCkgLVxuICAgICAgICAgICAgICAgICAgTWF0aC5hdGFuMihwcmV2aW91cy55IC0gb3JpZ2luLnksIHByZXZpb3VzLnggLSBvcmlnaW4ueCk7XG5cbiAgICB0aGlzLl9tYXRyaXggPSB0aGlzLl9pbml0aWFsTWF0cml4XG4gICAgICAuY2xvbmUoKVxuICAgICAgLnJvdGF0ZSh0aGlzLl9hbmdsZSwgb3JpZ2luKVxuICAgICAgLmZsaXAoKTtcblxuICAgIHRoaXMuX3VwZGF0ZSgpO1xuICAgIHRoaXMuX3BhdGguZmlyZSgncm90YXRlJywgeyBsYXllcjogdGhpcy5fcGF0aCwgcm90YXRpb246IHRoaXMuX2FuZ2xlIH0pO1xuICB9LFxuXG5cbiAgLyoqXG4gICAqIEBwYXJhbSAge0V2ZW50fSBldnRcbiAgICovXG4gIF9vblJvdGF0ZUVuZDogZnVuY3Rpb24oZXZ0KSB7XG4gICAgdGhpcy5fcGF0aC5fbWFwXG4gICAgICAub2ZmKCdtb3VzZW1vdmUnLCB0aGlzLl9vblJvdGF0ZSwgdGhpcylcbiAgICAgIC5vZmYoJ21vdXNldXAnLCAgIHRoaXMuX29uUm90YXRlRW5kLCB0aGlzKTtcblxuICAgIHRoaXMuX2FwcGx5KCk7XG4gICAgdGhpcy5fcGF0aC5maXJlKCdyb3RhdGVlbmQnLCB7IGxheWVyOiB0aGlzLl9wYXRoLCByb3RhdGlvbjogdGhpcy5fYW5nbGUgfSk7XG4gIH0sXG5cblxuICAvKipcbiAgICogQHBhcmFtICB7RXZlbnR9IGV2dFxuICAgKi9cbiAgX29uU2NhbGVTdGFydDogZnVuY3Rpb24oZXZ0KSB7XG4gICAgdmFyIG1hcmtlciA9IGV2dC50YXJnZXQ7XG4gICAgdmFyIG1hcCA9IHRoaXMuX21hcDtcblxuICAgIG1hcC5kcmFnZ2luZy5kaXNhYmxlKCk7XG5cbiAgICB0aGlzLl9hY3RpdmVNYXJrZXIgPSBtYXJrZXI7XG5cbiAgICB0aGlzLl9vcmlnaW5NYXJrZXIgPSB0aGlzLl9oYW5kbGVyc1sobWFya2VyLm9wdGlvbnMuaW5kZXggKyAyKSAlIDRdO1xuICAgIHRoaXMuX3NjYWxlT3JpZ2luICA9IHRoaXMuX29yaWdpbk1hcmtlci5nZXRMYXRMbmcoKTtcblxuICAgIHRoaXMuX2luaXRpYWxNYXRyaXggPSB0aGlzLl9tYXRyaXguY2xvbmUoKTtcbiAgICB0aGlzLl9jYWNoZVBvaW50cygpO1xuXG4gICAgdGhpcy5fbWFwXG4gICAgICAub24oJ21vdXNlbW92ZScsIHRoaXMuX29uU2NhbGUsICAgIHRoaXMpXG4gICAgICAub24oJ21vdXNldXAnLCAgIHRoaXMuX29uU2NhbGVFbmQsIHRoaXMpO1xuICAgIHRoaXMuX2luaXRpYWxEaXN0ICA9IHRoaXMuX29yaWdpbk1hcmtlci5fcG9pbnQuZGlzdGFuY2VUbyh0aGlzLl9hY3RpdmVNYXJrZXIuX3BvaW50KTtcbiAgICB0aGlzLl9pbml0aWFsRGlzdFggPSB0aGlzLl9vcmlnaW5NYXJrZXIuX3BvaW50LnggLSB0aGlzLl9hY3RpdmVNYXJrZXIuX3BvaW50Lng7XG4gICAgdGhpcy5faW5pdGlhbERpc3RZID0gdGhpcy5fb3JpZ2luTWFya2VyLl9wb2ludC55IC0gdGhpcy5fYWN0aXZlTWFya2VyLl9wb2ludC55O1xuXG4gICAgdGhpcy5fcGF0aFxuICAgICAgLmZpcmUoJ3RyYW5zZm9ybXN0YXJ0JywgeyBsYXllcjogdGhpcy5fcGF0aCB9KVxuICAgICAgLmZpcmUoJ3NjYWxlc3RhcnQnLCB7IGxheWVyOiB0aGlzLl9wYXRoLCBzY2FsZTogTC5wb2ludCgxLCAxKSB9KTtcblxuICAgIHRoaXMuX21hcC5yZW1vdmVMYXllcih0aGlzLl9oYW5kbGVMaW5lKTtcbiAgICB0aGlzLl9tYXAucmVtb3ZlTGF5ZXIodGhpcy5fcm90YXRpb25NYXJrZXIpO1xuXG4gICAgLy90aGlzLl9oYW5kbGVMaW5lID0gdGhpcy5fcm90YXRpb25NYXJrZXIgPSBudWxsO1xuICB9LFxuXG5cbiAgLyoqXG4gICAqIEBwYXJhbSAge0V2ZW50fSBldnRcbiAgICovXG4gIF9vblNjYWxlOiBmdW5jdGlvbihldnQpIHtcbiAgICB2YXIgb3JpZ2luUG9pbnQgPSB0aGlzLl9vcmlnaW5NYXJrZXIuX3BvaW50O1xuICAgIHZhciByYXRpb1gsIHJhdGlvWTtcbiAgICBpZiAodGhpcy5vcHRpb25zLnVuaWZvcm1TY2FsaW5nKSB7XG4gICAgICByYXRpb1ggPSBvcmlnaW5Qb2ludC5kaXN0YW5jZVRvKGV2dC5sYXllclBvaW50KSAvIHRoaXMuX2luaXRpYWxEaXN0O1xuICAgICAgcmF0aW9ZID0gcmF0aW9YO1xuICAgIH0gZWxzZSB7XG4gICAgICByYXRpb1ggPSAob3JpZ2luUG9pbnQueCAtIGV2dC5sYXllclBvaW50LngpIC8gdGhpcy5faW5pdGlhbERpc3RYO1xuICAgICAgcmF0aW9ZID0gKG9yaWdpblBvaW50LnkgLSBldnQubGF5ZXJQb2ludC55KSAvIHRoaXMuX2luaXRpYWxEaXN0WTtcbiAgICB9XG5cbiAgICB0aGlzLl9zY2FsZSA9IG5ldyBMLlBvaW50KHJhdGlvWCwgcmF0aW9ZKTtcblxuICAgIC8vIHVwZGF0ZSBtYXRyaXhcbiAgICB0aGlzLl9tYXRyaXggPSB0aGlzLl9pbml0aWFsTWF0cml4XG4gICAgICAuY2xvbmUoKVxuICAgICAgLnNjYWxlKHRoaXMuX3NjYWxlLCBvcmlnaW5Qb2ludCk7XG5cbiAgICB0aGlzLl91cGRhdGUoKTtcbiAgICB0aGlzLl9wYXRoLmZpcmUoJ3NjYWxlJywge1xuICAgICAgbGF5ZXI6IHRoaXMuX3BhdGgsIHNjYWxlOiB0aGlzLl9zY2FsZS5jbG9uZSgpIH0pO1xuICB9LFxuXG5cbiAgLyoqXG4gICAqIFNjYWxpbmcgY29tcGxldGVcbiAgICogQHBhcmFtICB7RXZlbnR9IGV2dFxuICAgKi9cbiAgX29uU2NhbGVFbmQ6IGZ1bmN0aW9uKGV2dCkge1xuICAgIHRoaXMuX21hcFxuICAgICAgLm9mZignbW91c2Vtb3ZlJywgdGhpcy5fb25TY2FsZSwgICAgdGhpcylcbiAgICAgIC5vZmYoJ21vdXNldXAnLCAgIHRoaXMuX29uU2NhbGVFbmQsIHRoaXMpO1xuXG4gICAgdGhpcy5fbWFwLmFkZExheWVyKHRoaXMuX2hhbmRsZUxpbmUpO1xuICAgIHRoaXMuX21hcC5hZGRMYXllcih0aGlzLl9yb3RhdGlvbk1hcmtlcik7XG5cbiAgICB0aGlzLl9hcHBseSgpO1xuICAgIHRoaXMuX3BhdGguZmlyZSgnc2NhbGVlbmQnLCB7XG4gICAgICBsYXllcjogdGhpcy5fcGF0aCwgc2NhbGU6IHRoaXMuX3NjYWxlLmNsb25lKCkgfSk7XG4gIH0sXG5cblxuICAvKipcbiAgICogQ2FjaGUgY3VycmVudCBoYW5kbGVycyBwb3NpdGlvbnNcbiAgICovXG4gIF9jYWNoZVBvaW50czogZnVuY3Rpb24oKSB7XG4gICAgdGhpcy5faGFuZGxlcnNHcm91cC5lYWNoTGF5ZXIoZnVuY3Rpb24obGF5ZXIpIHtcbiAgICAgIGxheWVyLmJyaW5nVG9Gcm9udCgpO1xuICAgIH0pO1xuICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSB0aGlzLl9oYW5kbGVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuICAgICAgdmFyIGhhbmRsZXIgPSB0aGlzLl9oYW5kbGVyc1tpXTtcbiAgICAgIGhhbmRsZXIuX2luaXRpYWxQb2ludCA9IGhhbmRsZXIuX3BvaW50LmNsb25lKCk7XG4gICAgfVxuICB9LFxuXG5cbiAgLyoqXG4gICAqIEJvdW5kaW5nIHBvbHlnb25cbiAgICogQHJldHVybiB7TC5Qb2x5Z29ufVxuICAgKi9cbiAgX2dldEJvdW5kaW5nUG9seWdvbjogZnVuY3Rpb24oKSB7XG4gICAgaWYgKHRoaXMuX3JlY3RTaGFwZSkge1xuICAgICAgcmV0dXJuIEwuR2VvSlNPTi5nZW9tZXRyeVRvTGF5ZXIoXG4gICAgICAgIHRoaXMuX3JlY3RTaGFwZSwgdGhpcy5vcHRpb25zLmJvdW5kc09wdGlvbnMpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbmV3IEwuUmVjdGFuZ2xlKFxuICAgICAgICB0aGlzLl9wYXRoLmdldEJvdW5kcygpLCB0aGlzLm9wdGlvbnMuYm91bmRzT3B0aW9ucyk7XG4gICAgfVxuICB9LFxuXG5cbiAgLyoqXG4gICAqIENyZWF0ZSBjb3JuZXIgbWFya2VyXG4gICAqIEBwYXJhbSAge0wuTGF0TG5nfSBsYXRsbmdcbiAgICogQHBhcmFtICB7TnVtYmVyfSAgIHR5cGUgb25lIG9mIEwuSGFuZGxlci5QYXRoVHJhbnNmb3JtLkhhbmRsZXJUeXBlc1xuICAgKiBAcGFyYW0gIHtOdW1iZXJ9ICAgaW5kZXhcbiAgICogQHJldHVybiB7TC5IYW5kbGVyLlBhdGhUcmFuc2Zvcm0uSGFuZGxlfVxuICAgKi9cbiAgX2NyZWF0ZUhhbmRsZXI6IGZ1bmN0aW9uKGxhdGxuZywgdHlwZSwgaW5kZXgpIHtcbiAgICB2YXIgSGFuZGxlQ2xhc3MgPSB0aGlzLm9wdGlvbnMuaGFuZGxlQ2xhc3M7XG4gICAgdmFyIG1hcmtlciA9IG5ldyBIYW5kbGVDbGFzcyhsYXRsbmcsXG4gICAgICBMLlV0aWwuZXh0ZW5kKHt9LCB0aGlzLm9wdGlvbnMuaGFuZGxlck9wdGlvbnMsIHtcbiAgICAgICAgY2xhc3NOYW1lOiAnbGVhZmxldC1kcmFnLXRyYW5zZm9ybS1tYXJrZXIgZHJhZy1tYXJrZXItLScgK1xuICAgICAgICAgICAgICAgICAgIGluZGV4ICsgJyBkcmFnLW1hcmtlci0tJyArIHR5cGUsXG4gICAgICAgIGluZGV4OiAgICAgaW5kZXgsXG4gICAgICAgIHR5cGU6ICAgICAgdHlwZVxuICAgICAgfSlcbiAgICApO1xuXG4gICAgbWFya2VyLm9uKCdtb3VzZWRvd24nLCB0aGlzLl9vblNjYWxlU3RhcnQsIHRoaXMpO1xuICAgIHJldHVybiBtYXJrZXI7XG4gIH0sXG5cblxuICAvKipcbiAgICogSGlkZShub3QgcmVtb3ZlKSB0aGUgaGFuZGxlcnMgbGF5ZXJcbiAgICovXG4gIF9oaWRlSGFuZGxlcnM6IGZ1bmN0aW9uKCkge1xuICAgIHRoaXMuX21hcC5yZW1vdmVMYXllcih0aGlzLl9oYW5kbGVyc0dyb3VwKTtcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBIaWRlIGhhbmRsZXJzIGFuZCByZWN0YW5nbGVcbiAgICovXG4gIF9vbkRyYWdTdGFydDogZnVuY3Rpb24oKSB7XG4gICAgdGhpcy5faGlkZUhhbmRsZXJzKCk7XG4gIH0sXG5cblxuICAvKipcbiAgICogRHJhZyByZWN0YW5nbGUsIHJlLWNyZWF0ZSBoYW5kbGVyc1xuICAgKi9cbiAgX29uRHJhZ0VuZDogZnVuY3Rpb24oZXZ0KSB7XG4gICAgdmFyIHJlY3QgPSB0aGlzLl9yZWN0O1xuICAgIHZhciBtYXRyaXggPSAoZXZ0LmxheWVyID8gZXZ0LmxheWVyIDogdGhpcy5fcGF0aCkuZHJhZ2dpbmcuX21hdHJpeC5zbGljZSgpO1xuXG4gICAgaWYgKCFyZWN0LmRyYWdnaW5nKSB7XG4gICAgICByZWN0LmRyYWdnaW5nID0gbmV3IEwuSGFuZGxlci5QYXRoRHJhZyhyZWN0KTtcbiAgICB9XG4gICAgcmVjdC5kcmFnZ2luZy5lbmFibGUoKTtcbiAgICB0aGlzLl9tYXAuYWRkTGF5ZXIocmVjdCk7XG4gICAgcmVjdC5kcmFnZ2luZy5fdHJhbnNmb3JtUG9pbnRzKG1hdHJpeCk7XG4gICAgcmVjdC5fdXBkYXRlUGF0aCgpO1xuICAgIHJlY3QuX3Byb2plY3QoKTtcbiAgICByZWN0LmRyYWdnaW5nLmRpc2FibGUoKTtcblxuICAgIHRoaXMuX21hcC5hZGRMYXllcih0aGlzLl9oYW5kbGVyc0dyb3VwKTtcbiAgICB0aGlzLl91cGRhdGVIYW5kbGVycygpO1xuXG4gICAgdGhpcy5fcGF0aC5maXJlKCd0cmFuc2Zvcm1lZCcsIHtcbiAgICAgIHNjYWxlOiBMLnBvaW50KDEsIDEpLFxuICAgICAgcm90YXRpb246IDAsXG4gICAgICBtYXRyaXg6IEwubWF0cml4LmFwcGx5KHVuZGVmaW5lZCwgbWF0cml4KSxcbiAgICAgIHRyYW5zbGF0ZTogTC5wb2ludChtYXRyaXhbNF0sIG1hdHJpeFs1XSksXG4gICAgICBsYXllcjogdGhpcy5fcGF0aFxuICAgIH0pO1xuICB9XG59KTtcblxuXG5MLlBhdGguYWRkSW5pdEhvb2soZnVuY3Rpb24oKSB7XG4gIGlmICh0aGlzLm9wdGlvbnMudHJhbnNmb3JtKSB7XG4gICAgdGhpcy50cmFuc2Zvcm0gPSBuZXcgTC5IYW5kbGVyLlBhdGhUcmFuc2Zvcm0odGhpcywgdGhpcy5vcHRpb25zLnRyYW5zZm9ybSk7XG4gIH1cbn0pO1xuIiwiLyoqXG4gKiBQb2ludCBvbiB0aGUgbGluZSBzZWdtZW50IG9yIGl0cyBleHRlbnRpb25cbiAqXG4gKiBAcGFyYW0gIHtMLlBvaW50fSBzdGFydFxuICogQHBhcmFtICB7TC5Qb2ludH0gZmluYWxcbiAqIEBwYXJhbSAge051bWJlcn0gIGRpc3RQeFxuICogQHJldHVybiB7TC5Qb2ludH1cbiAqL1xuTC5MaW5lVXRpbC5wb2ludE9uTGluZSA9IGZ1bmN0aW9uKHN0YXJ0LCBmaW5hbCwgZGlzdFB4KSB7XG4gIHZhciByYXRpbyA9IDEgKyBkaXN0UHggLyBzdGFydC5kaXN0YW5jZVRvKGZpbmFsKTtcbiAgcmV0dXJuIG5ldyBMLlBvaW50KFxuICAgIHN0YXJ0LnggKyAoZmluYWwueCAtIHN0YXJ0LngpICogcmF0aW8sXG4gICAgc3RhcnQueSArIChmaW5hbC55IC0gc3RhcnQueSkgKiByYXRpb1xuICApO1xufTtcblxuXG4vKipcbiAqIERlZXAgbWVyZ2Ugb2JqZWN0cy5cbiAqL1xuTC5VdGlsLm1lcmdlID0gZnVuY3Rpb24oKSB7XG4gIHZhciBpID0gMTtcbiAgdmFyIGtleSwgdmFsO1xuICB2YXIgb2JqID0gYXJndW1lbnRzW2ldO1xuXG4gIGZ1bmN0aW9uIGlzT2JqZWN0KG9iamVjdCkge1xuICAgIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwob2JqZWN0KSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XG4gIH1cblxuICAvLyBtYWtlIHN1cmUgd2UgZG9uJ3QgbW9kaWZ5IHNvdXJjZSBlbGVtZW50IGFuZCBpdCdzIHByb3BlcnRpZXNcbiAgLy8gb2JqZWN0cyBhcmUgcGFzc2VkIGJ5IHJlZmVyZW5jZVxuICB2YXIgdGFyZ2V0ID0gYXJndW1lbnRzWzBdO1xuXG4gIHdoaWxlIChvYmopIHtcbiAgICBvYmogPSBhcmd1bWVudHNbaSsrXTtcbiAgICBmb3IgKGtleSBpbiBvYmopIHtcbiAgICAgIGlmICghb2JqLmhhc093blByb3BlcnR5KGtleSkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIHZhbCA9IG9ialtrZXldO1xuXG4gICAgICBpZiAoaXNPYmplY3QodmFsKSAmJiBpc09iamVjdCh0YXJnZXRba2V5XSkpe1xuICAgICAgICB0YXJnZXRba2V5XSA9IEwuVXRpbC5tZXJnZSh0YXJnZXRba2V5XSwgdmFsKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRhcmdldFtrZXldID0gdmFsO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gdGFyZ2V0O1xufTtcbiJdfQ== +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJleGFtcGxlL2pzL2FwcC5qcyIsImluZGV4LmpzIiwibm9kZV9tb2R1bGVzL2xlYWZsZXQtcGF0aC1kcmFnL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL2xlYWZsZXQtcGF0aC1kcmFnL3NyYy9DYW52YXMuanMiLCJub2RlX21vZHVsZXMvbGVhZmxldC1wYXRoLWRyYWcvc3JjL1BhdGguRHJhZy5qcyIsIm5vZGVfbW9kdWxlcy9sZWFmbGV0LXBhdGgtZHJhZy9zcmMvUGF0aC5UcmFuc2Zvcm0uanMiLCJub2RlX21vZHVsZXMvbGVhZmxldC1wYXRoLWRyYWcvc3JjL1NWRy5WTUwuanMiLCJub2RlX21vZHVsZXMvbGVhZmxldC1wYXRoLWRyYWcvc3JjL1NWRy5qcyIsIm5vZGVfbW9kdWxlcy9sZWFmbGV0L2Rpc3QvbGVhZmxldC1zcmMuanMiLCJzcmMvTWF0cml4LmpzIiwic3JjL1BhdGguVHJhbnNmb3JtLmpzIiwic3JjL1V0aWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDeFBBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNMQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ1BBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4VkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsOFpBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzVNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3B3QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwidmFyIEwgPSByZXF1aXJlKCdsZWFmbGV0Jyk7XHJcbnZhciBEcmFnSGFuZGxlciA9IHJlcXVpcmUoJy4uLy4uL2luZGV4Jyk7XHJcblxyXG5MLkljb24uRGVmYXVsdC5pbWFnZVBhdGggPSBcImh0dHA6Ly9jZG4ubGVhZmxldGpzLmNvbS9sZWFmbGV0LTAuNy9pbWFnZXNcIjtcclxuXHJcbi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXHJcbnZhciBtYXAgPSBnbG9iYWwubWFwID0gbmV3IEwuTWFwKCdtYXAnLCB7XHJcbiAgLy8gY3JzOiBMLkNSUy5FUFNHNDMyNiAvLyB0aGF0IHdhcyB0ZXN0ZWQgYXMgd2VsbFxyXG59KS5zZXRWaWV3KFsyMi40MjY1OCwgMTE0LjE5NTJdLCAxMSk7XHJcblxyXG5MLnRpbGVMYXllcignaHR0cDovL3tzfS50aWxlLm9zbS5vcmcve3p9L3t4fS97eX0ucG5nJywge1xyXG4gIGF0dHJpYnV0aW9uOiAnJmNvcHk7ICcgK1xyXG4gICAgJzxhIGhyZWY9XCJodHRwOi8vb3NtLm9yZy9jb3B5cmlnaHRcIj5PU008L2E+IGNvbnRyaWJ1dG9ycydcclxufSkuYWRkVG8obWFwKTtcclxuXHJcbi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXHJcbmZ1bmN0aW9uIGludGVycG9sYXRlQXJyKGFycmF5LCBpbnNlcnQpIHtcclxuICB2YXIgcmVzID0gW107XHJcbiAgYXJyYXkuZm9yRWFjaChmdW5jdGlvbihwLCBpLCBhcnIpIHtcclxuICAgIHJlcy5wdXNoKHAuY29uY2F0KCkpO1xyXG5cclxuICAgIGlmIChpIDwgYXJyLmxlbmd0aCAtIDEpIHtcclxuICAgICAgdmFyIGRpZmYgPSBbYXJyW2kgKyAxXVswXSAtIHBbMF0sIGFycltpICsgMV1bMV0gLSBwWzFdXTtcclxuICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPCBpbnNlcnQ7IGkrKykge1xyXG4gICAgICAgIHJlcy5wdXNoKFtwWzBdICsgKGRpZmZbMF0gKiBpKSAvIGluc2VydCwgcFsxXSArIChkaWZmWzFdICogaSkgLyBpbnNlcnRdKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICByZXR1cm4gcmVzO1xyXG59XHJcblxyXG4vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xyXG52YXIgcG9seWdvbiA9IGdsb2JhbC5wb2x5Z29uID0gbmV3IEwuUG9seWdvbihcclxuICBMLkdlb0pTT04uY29vcmRzVG9MYXRMbmdzKFxyXG5cclxuICAgIC8vIH4gMTMgMDAwIHBvaW50c1xyXG4gICAgaW50ZXJwb2xhdGVBcnIoW1xyXG4gICAgICBbMTEzLjk3Njk3NDQ4NzMwNDY5LCAyMi40MDM0MTA4OTI3MTIxMjRdLFxyXG4gICAgICBbMTEzLjk4NjU4NzUyNDQxNDA1LCAyMi4zODM3MzAwODU5MjQ5NV0sXHJcbiAgICAgIFsxMTQuMDEyNjgwMDUzNzEwOTQsIDIyLjM2OTEyNjM5NzU0NTg4N10sXHJcbiAgICAgIFsxMTQuMDI3Nzg2MjU0ODgyODEsIDIyLjM4NTYzNDgwMTg1NzE4XSxcclxuICAgICAgWzExNC4wNDcwMTIzMjkxMDE1NiwgMjIuMzk1MTU3OTkwMjkwNzU1XSxcclxuICAgICAgWzExNC4wNjAwNTg1OTM3NSwgICAgMjIuNDEzNTY3NjM4MzY5ODA1XSxcclxuICAgICAgWzExNC4wNjI4MDUxNzU3ODEyNSwgMjIuNDMyNjA5NTM0ODc2Nzk2XSxcclxuICAgICAgWzExNC4wNDgzODU2MjAxMTcxNywgMjIuNDQ0NjY4MDUxNjU3MTU3XSxcclxuICAgICAgWzExNC4wNDI4OTI0NTYwNTQ2OSwgMjIuNDQ4NDc1Nzg2NTY1NDRdLFxyXG4gICAgICBbMTE0LjAzMjU5Mjc3MzQzNzQ5LCAyMi40NDQ2NjgwNTE2NTcxNTddLFxyXG4gICAgICBbMTE0LjAxOTU0NjUwODc4OTA2LCAyMi40NDcyMDY1NTMyMTE4MTRdLFxyXG4gICAgICBbMTEzLjk5NjIwMDU2MTUyMzQ0LCAyMi40MzY0MTc2MDA3NjMxMTRdLFxyXG4gICAgICBbMTEzLjk4MTc4MTAwNTg1OTM4LCAyMi40MjA1NDk5NzAyOTA4NzVdLFxyXG4gICAgICBbMTEzLjk3Njk3NDQ4NzMwNDY5LCAyMi40MDM0MTA4OTI3MTIxMjRdXHJcbiAgICBdLCAxMDAwKVxyXG4gICksIHtcclxuICAgIGNvbG9yOiAnI2YwMCcsXHJcbiAgICBpbnRlcmFjdGl2ZTogdHJ1ZSxcclxuICAgIGRyYWdnYWJsZTogdHJ1ZSxcclxuICAgIHRyYW5zZm9ybTogdHJ1ZVxyXG4gIH0pLmFkZFRvKG1hcCk7XHJcbi8vcG9seWdvbi50cmFuc2Zvcm0uZW5hYmxlKCk7XHJcblxyXG52YXIgcG9seWxpbmUgPSBnbG9iYWwucG9seWxpbmUgPSBuZXcgTC5Qb2x5bGluZShcclxuICBMLkdlb0pTT04uY29vcmRzVG9MYXRMbmdzKFtcclxuICAgIFsxMTQuMTQzMTQyNzAwMTk1MzEsIDIyLjQ5NDc5NDg0OTc1NDQzXSxcclxuICAgIFsxMTQuMTUzNDQyMzgyODEyNSwgIDIyLjQ4NTkxMjk0MjMyMDk1OF0sXHJcbiAgICBbMTE0LjE1MjA2OTA5MTc5Njg4LCAyMi40NzMyMjM1MTQ0NzgxXSxcclxuICAgIFsxMTQuMTQ5MzIyNTA5NzY1NjEsIDIyLjQ1OTg5ODM2Mzk0Mzg5M10sXHJcbiAgICBbMTE0LjE1OTYyMjE5MjM4MjgxLCAyMi40NDcyMDY1NTMyMTE4MTRdLFxyXG4gICAgWzExNC4xNjk5MjE4NzUsICAgICAgMjIuNDQ3MjA2NTUzMjExODE0XSxcclxuICAgIFsxMTQuMTkzOTU0NDY3NzczNDQsIDIyLjQ1OTg5ODM2Mzk0Mzg5M10sXHJcbiAgICBbMTE0LjIwNjMxNDA4NjkxNDA2LCAyMi40NjExNjc0ODExMDkzNV0sXHJcbiAgICBbMTE0LjIxMTgwNzI1MDk3NjU1LCAyMi40NzM4NTgwMTM0ODc2MTRdLFxyXG4gICAgWzExNC4yMjQxNjY4NzAxMTcxOSwgMjIuNDcxMzIwMDAwMDA5OTkyXSxcclxuICAgIFsxMTQuMjM3MjEzMTM0NzY1NjIsIDIyLjQ3NjM5NTk4MDQ1Nzk3M10sXHJcbiAgICBbMTE0LjI0MjAxOTY1MzMyMDMxLCAyMi40OTM1MjYwNDA3MzcyMl0sXHJcbiAgICBbMTE0LjIzMDM0NjY3OTY4NzUsICAyMi41MTU3Mjg1MTgzMDM1MV0sXHJcbiAgICBbMTE0LjIxNzk4NzA2MDU0Njg4LCAyMi41MjQ2MDg1MTEwMjYyNjJdLFxyXG4gICAgWzExNC4yMDc2ODczNzc5Mjk2OSwgMjIuNTI0NjA4NTExMDI2MjYyXSxcclxuICAgIFsxMTQuMjA3Njg3Mzc3OTI5NjksIDIyLjUzNjAyNDgwNTg4Njk3NF1cclxuICBdKSwge1xyXG4gICAgd2VpZ2h0OiAxNSxcclxuICAgIGRyYWdnYWJsZTogdHJ1ZSxcclxuICAgIHRyYW5zZm9ybTogdHJ1ZVxyXG4gIH0pXHJcbiAgLmJpbmRQb3B1cChcIkwuUG9seWxpbmVcIilcclxuICAuYWRkVG8obWFwKTtcclxuLy8gcG9seWxpbmUudHJhbnNmb3JtLmVuYWJsZSgpO1xyXG5cclxuXHJcbnZhciBwb2x5Z29uV2l0aEhvbGUgPSBnbG9iYWwucG9seWdvbldpdGhIb2xlID0gbmV3IEwuUG9seWdvbihcclxuICAgIFtcclxuICAgICAgTC5HZW9KU09OLmNvb3Jkc1RvTGF0TG5ncyhbXHJcbiAgICAgICAgWzExNC4yNzQ5Nzg2Mzc2OTUzLCAgMjIuNDEyOTMyODYzNTE3NzE3XSxcclxuICAgICAgICBbMTE0LjI4MzkwNTAyOTI5Njg4LCAyMi40MDA4NzE1OTAzMDU5NV0sXHJcbiAgICAgICAgWzExNC4yOTAwODQ4Mzg4NjcxNywgMjIuMzg4ODA5MjcwNDU1NTZdLFxyXG4gICAgICAgIFsxMTQuMzAxMDcxMTY2OTkyMTksIDIyLjM4MjQ2MDI2MDgxNTcxNl0sXHJcbiAgICAgICAgWzExNC4zMTg5MjM5NTAxOTUzMSwgMjIuMzkxOTgzNjY2NjAyNzgzXSxcclxuICAgICAgICBbMTE0LjMyMzA0MzgyMzI0MjE5LCAyMi4zODA1NTU1MDE0MjE1MzNdLFxyXG4gICAgICAgIFsxMTQuMzQyOTU2NTQyOTY4NzUsIDIyLjM3MjkzNjIwMzExMzgzOF0sXHJcbiAgICAgICAgWzExNC4zMzQ3MTY3OTY4NzUsICAgMjIuMzg0MzY0OTk0MTMzMzAzXSxcclxuICAgICAgICBbMTE0LjMzMDU5NjkyMzgyODEyLCAyMi4zOTM4ODgyNjk1MTExOTRdLFxyXG4gICAgICAgIFsxMTQuMzIxNjcwNTMyMjI2NTUsIDIyLjQwMDg3MTU5MDMwNTk1XSxcclxuICAgICAgICBbMTE0LjMyNzg1MDM0MTc5Njg4LCAyMi40MTM1Njc2MzgzNjk4MDVdLFxyXG4gICAgICAgIFsxMTQuMzMxOTcwMjE0ODQzNzUsIDIyLjQyNDk5MzA4OTY0NzIyXSxcclxuICAgICAgICBbMTE0LjMyNTc5MDQwNTI3MzQ0LCAyMi40MzA3MDU0NjI3NDg5MThdLFxyXG4gICAgICAgIFsxMTQuMzMxOTcwMjE0ODQzNzUsIDIyLjQzOTU5MDkwOTE3MjY2XSxcclxuICAgICAgICBbMTE0LjMzNzQ2MzM3ODkwNjI0LCAyMi40NDkxMTAzOTg4ODYxMDZdLFxyXG4gICAgICAgIFsxMTQuMzM1NDAzNDQyMzgyODEsIDIyLjQ2MTgwMjAzNTMzMzk5Ml0sXHJcbiAgICAgICAgWzExNC4zMjUxMDM3NTk3NjU2MiwgMjIuNDY0MzQwMjIzMTc3MTE4XSxcclxuICAgICAgICBbMTE0LjMyOTIyMzYzMjgxMjQ5LCAyMi40NzI1ODkwMTI1NjE5NTRdLFxyXG4gICAgICAgIFsxMTQuMzIzNzMwNDY4NzUsICAgIDIyLjQ3NzAzMDQ2NDkzMzMwN10sXHJcbiAgICAgICAgWzExNC4zMTk2MTA1OTU3MDMxMiwgMjIuNDc4OTMzOTAwOTE2OTI4XSxcclxuICAgICAgICBbMTE0LjMwMTc1NzgxMjUsICAgICAyMi40NjYyNDM4MzM1NDk0NDVdLFxyXG4gICAgICAgIFsxMTQuMzAyNDQ0NDU4MDA3ODEsIDIyLjQ1NzM2MDA5NDc1MDA4M10sXHJcbiAgICAgICAgWzExNC4yOTI4MzE0MjA4OTg0NCwgMjIuNDU0ODIxNzc5MDc1ODMyXSxcclxuICAgICAgICBbMTE0LjI4MzkwNTAyOTI5Njg4LCAyMi40NTEwMTQyMTg0MjI2OV0sXHJcbiAgICAgICAgWzExNC4yNzQ5Nzg2Mzc2OTUzLCAgMjIuNDQyNzY0MTQ1MDAxNzA3XSxcclxuICAgICAgICBbMTE0LjI5MDc3MTQ4NDM3NDk5LCAyMi40MjgxNjY2NTkyNzk2MTVdLFxyXG4gICAgICAgIFsxMTQuMjc3MDM4NTc0MjE4NzUsIDIyLjQyMDU0OTk3MDI5MDg3NV0sXHJcbiAgICAgICAgWzExNC4yNzQ5Nzg2Mzc2OTUzLCAgMjIuNDEyOTMyODYzNTE3NzE3XVxyXG4gICAgICBdKSxcclxuICAgICAgTC5HZW9KU09OLmNvb3Jkc1RvTGF0TG5ncyhbXHJcbiAgICAgICAgWzExNC4zMDEwNzExNjY5OTIxOSwgMjIuNDMzODc4OTAxNzgyOTddLFxyXG4gICAgICAgIFsxMTQuMjkzNTE4MDY2NDA2MjUsIDIyLjQxNDIwMjQxMDMyMTMwMl0sXHJcbiAgICAgICAgWzExNC4zMDU4Nzc2ODU1NDY4NiwgMjIuNDA4NDg5MzU4MzQyNjM1XSxcclxuICAgICAgICBbMTE0LjMyMjM1NzE3NzczNDM4LCAyMi40MjExODQ3MTAzMzE4NThdLFxyXG4gICAgICAgIFsxMTQuMzAxMDcxMTY2OTkyMTksIDIyLjQzMzg3ODkwMTc4Mjk3XVxyXG4gICAgICBdKVxyXG4gICAgXSwge1xyXG4gICAgICBkcmFnZ2FibGU6IHRydWUsXHJcbiAgICAgIHRyYW5zZm9ybTogdHJ1ZVxyXG4gICAgfVxyXG4gIClcclxuICAuYWRkVG8obWFwKTtcclxuLy9wb2x5Z29uV2l0aEhvbGUudHJhbnNmb3JtLmVuYWJsZSgpO1xyXG5cclxudmFyIG11bHRpUG9seWdvbiA9IGdsb2JhbC5tdWx0aVBvbHlnb24gPSBuZXcgTC5Qb2x5Z29uKFtcclxuICBMLkdlb0pTT04uY29vcmRzVG9MYXRMbmdzKFtcclxuICAgIFsxMTQuMjA1NjI3NDQxNDA2MjUsIDIyLjMyMDg1OTg0MTAwNTkzXSxcclxuICAgIFsxMTQuMjE1OTI3MTI0MDIzNDQsIDIyLjM1MjYxNjAzNTUxMjE1XSxcclxuICAgIFsxMTQuMjY0Njc4OTU1MDc4MTIsIDIyLjM1MTM0NTkyNjYwNjk1N10sXHJcbiAgICBbMTE0LjI3NDk3ODYzNzY5NTMsICAyMi4zMjQwMzU3ODU4NDAzOF0sXHJcbiAgICBbMTE0LjI5MjE0NDc3NTM5MDYyLCAyMi4zMjcyMTE2NTgzODg5M10sXHJcbiAgICBbMTE0LjMwMTc1NzgxMjUsICAgICAyMi4zMTE5NjY4MTA5Nzc2MTZdLFxyXG4gICAgWzExNC4yOTQyMDQ3MTE5MTQwNiwgMjIuMjkxMDAyNDI3NzM1MzI1XSxcclxuICAgIFsxMTQuMjkzNTE4MDY2NDA2MjUsIDIyLjI3MjU3NjU4NTQxMzQ3NV0sXHJcbiAgICBbMTE0LjI4MzkwNTAyOTI5Njg4LCAyMi4yNjE3NzQxMDA5NzQzNV0sXHJcbiAgICBbMTE0LjI2ODc5ODgyODEyNSwgICAyMi4yODE0NzIxMjI3ODM4MThdLFxyXG4gICAgWzExNC4yNzQ5Nzg2Mzc2OTUzLCAgMjIuMjk0ODE0MzY3NzgwNTE4XSxcclxuICAgIFsxMTQuMjY5NDg1NDczNjMyODEsIDIyLjMwMjQzNzkzNTkwNDQ4XSxcclxuICAgIFsxMTQuMjcwMTcyMTE5MTQwNjIsIDIyLjMxNTE0Mjk1ODE2OTM5XSxcclxuICAgIFsxMTQuMjU3ODEyNSwgICAgICAgIDIyLjMxMTk2NjgxMDk3NzYxNl0sXHJcbiAgICBbMTE0LjI0NzUxMjgxNzM4MjgxLCAyMi4yOTk4OTY3OTI3NTE5MjddLFxyXG4gICAgWzExNC4yNDU0NTI4ODA4NTkzOCwgMjIuMjkxMDAyNDI3NzM1MzI1XSxcclxuICAgIFsxMTQuMjI5NjYwMDM0MTc5NjksIDIyLjMwNzUyMDA4MzUyMjQ3Nl0sXHJcbiAgICBbMTE0LjIyMDczMzY0MjU3ODEyLCAyMi4zMDU2MTQyOTk4MzcwNDZdLFxyXG4gICAgWzExNC4yMDU2Mjc0NDE0MDYyNSwgMjIuMzIwODU5ODQxMDA1OTNdXHJcbiAgXSksXHJcbiAgTC5HZW9KU09OLmNvb3Jkc1RvTGF0TG5ncyhbXHJcbiAgICBbMTE0LjMxNTQ5MDcyMjY1NjI1LCAyMi4zMzkyNzkzMTQ2ODMxMl0sXHJcbiAgICBbMTE0LjMyMDI5NzI0MTIxMDk0LCAyMi4zMjY1NzY0ODk2NjI0ODJdLFxyXG4gICAgWzExNC4zMjk5MTAyNzgzMjAzMSwgMjIuMzI2NTc2NDg5NjYyNDgyXSxcclxuICAgIFsxMTQuMzMzMzQzNTA1ODU5MzgsIDIyLjMzMjI5MjkwNDA5MTcxNl0sXHJcbiAgICBbMTE0LjMyMzA0MzgyMzI0MjE5LCAyMi4zNDI0NTQ4NDAxNDY1XSxcclxuICAgIFsxMTQuMzE1NDkwNzIyNjU2MjUsIDIyLjMzOTI3OTMxNDY4MzEyXVxyXG4gIF0pLFxyXG4gIEwuR2VvSlNPTi5jb29yZHNUb0xhdExuZ3MoW1xyXG4gICAgWzExNC4yNzkwOTg1MTA3NDIxOSwgMjIuMjQ0NjE1NTAwMzIzMDY0XSxcclxuICAgIFsxMTQuMjgxMTU4NDQ3MjY1NjIsIDIyLjI1MTYwNjI5NTEzMjk0OF0sXHJcbiAgICBbMTE0LjI4NjY1MTYxMTMyODEyLCAyMi4yNTU0MTkzMDg4NTg1NTZdLFxyXG4gICAgWzExNC4yOTk2OTc4NzU5NzY1NiwgMjIuMjYxMTM4NjM0NzQ0NDldLFxyXG4gICAgWzExNC4yOTYyNjQ2NDg0Mzc1LCAgMjIuMjUwOTcwNzgyNzUwODY2XSxcclxuICAgIFsxMTQuMjk0ODkxMzU3NDIxODgsIDIyLjI0MDgwMjE5MjQ2MzM1XSxcclxuICAgIFsxMTQuMjkwMDg0ODM4ODY3MTcsIDIyLjIzODg5NTQ5OTYxMzIzMl0sXHJcbiAgICBbMTE0LjI3OTA5ODUxMDc0MjE5LCAyMi4yNDQ2MTU1MDAzMjMwNjRdXHJcbiAgXSlcclxuXSwge1xyXG4gIGRyYWdnYWJsZTogdHJ1ZSxcclxuICB0cmFuc2Zvcm06IHRydWUsXHJcbiAgY29sb3I6ICcjMDkyJ1xyXG59KS5iaW5kUG9wdXAoJ011bHRpUG9seWdvbicpLmFkZFRvKG1hcCk7XHJcbi8vbXVsdGlQb2x5Z29uLnRyYW5zZm9ybS5lbmFibGUoKTtcclxuXHJcbnZhciBtdWx0aVBvbHlsaW5lID0gZ2xvYmFsLm11bHRpUG9seWxpbmUgPSBuZXcgTC5Qb2x5bGluZShbXHJcbiAgTC5HZW9KU09OLmNvb3Jkc1RvTGF0TG5ncyhbXHJcbiAgICBbMTEzLjg5ODY5Njg5OTQxNDA2LCAyMi4zOTk2MDE5MjE3MDY5NTNdLFxyXG4gICAgWzExMy44OTgwMTAyNTM5MDYyNSwgMjIuNDIyNDU0MTgxNzA5NzA3XSxcclxuICAgIFsxMTMuOTAzNTAzNDE3OTY4NzUsIDIyLjQzMzI0NDIxOTc4MTE3XSxcclxuICAgIFsxMTMuOTA5NjgzMjI3NTM5MDYsIDIyLjQ0OTExMDM5ODg4NjEwNl0sXHJcbiAgICBbMTEzLjkwNjkzNjY0NTUwNzgxLCAyMi40NzgyOTk0MjUxNjI4NTJdLFxyXG4gICAgWzExMy45MjM0MTYxMzc2OTUzLCAgMjIuNDg4NDUwNjg4MzI1NDA4XSxcclxuICAgIFsxMTMuOTMzNzE1ODIwMzEyNSwgIDIyLjQ4MzM3NTE0OTc4OTYyM10sXHJcbiAgICBbMTEzLjk0NDcwMjE0ODQzNzUsICAyMi40OTIyNTcyMjAwODUxOTNdLFxyXG4gICAgWzExMy45NTIyNTUyNDkwMjM0NCwgMjIuNTEyNTU2OTU0MDUxNDVdXHJcbiAgXSksXHJcblxyXG4gIEwuR2VvSlNPTi5jb29yZHNUb0xhdExuZ3MoW1xyXG4gICAgWzExMy44Njc3OTc4NTE1NjI1LCAgMjIuMzkyNjE4NTM3MTM3MzhdLFxyXG4gICAgWzExMy44NjkxNzExNDI1NzgxMSwgMjIuNDI3NTMxOTUxMTU2OTldLFxyXG4gICAgWzExMy45MjM0MTYxMzc2OTUzLCAgMjIuNDYyNDM2NTg2NjUzMTQ4XSxcclxuICAgIFsxMTMuOTQ4MTM1Mzc1OTc2NTYsIDIyLjQ3Mzg1ODAxMzQ4NzYxNF0sXHJcbiAgICBbMTEzLjk3ODM0Nzc3ODMyMDMsICAyMi40OTkyMzU1ODk2ODMwNl0sXHJcbiAgICBbMTEzLjk5Njg4NzIwNzAzMTI1LCAyMi41MTE5MjI2MzI0Njg4Nl0sXHJcbiAgICBbMTE0LjAxMzM2NjY5OTIxODc1LCAyMi41MDExMzg3MjAzMDAyNTRdLFxyXG4gICAgWzExNC4wMjUwMzk2NzI4NTE1NSwgMjIuNTA4MTE2NjQxODUzNjc1XVxyXG4gIF0pXHJcbl0sIHtcclxuICBkcmFnZ2FibGU6IHRydWUsXHJcbiAgdHJhbnNmb3JtOiB0cnVlLFxyXG4gIGNvbG9yOiAnI2U5MCdcclxufSkuYmluZFBvcHVwKCdNdWx0aVBvbHlsaW5lJykuYWRkVG8obWFwKTtcclxuLy8gbXVsdGlQb2x5bGluZS50cmFuc2Zvcm0uZW5hYmxlKCk7XHJcblxyXG52YXIgbGF5ZXJzID0gW3BvbHlnb24sIHBvbHlsaW5lLCBtdWx0aVBvbHlsaW5lLCBtdWx0aVBvbHlnb24sIHBvbHlnb25XaXRoSG9sZV07XHJcblxyXG5mdW5jdGlvbiB1cGRhdGUoKSB7XHJcbiAgTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUoZnVuY3Rpb24oKSB7XHJcbiAgICB2YXIgZHJhZ2dpbmcgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjZHJhZ2dpbmcnKS5jaGVja2VkO1xyXG4gICAgdmFyIHNjYWxpbmcgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjc2NhbGluZycpLmNoZWNrZWQ7XHJcbiAgICB2YXIgcm90YXRpb24gPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjcm90YXRpb24nKS5jaGVja2VkO1xyXG4gICAgdmFyIHVuaWZvcm0gID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI3VuaWZvcm0nKS5jaGVja2VkO1xyXG5cclxuICAgIGxheWVycy5mb3JFYWNoKGZ1bmN0aW9uKGxheWVyKSB7XHJcblxyXG4gICAgICBpZiAobGF5ZXIuZHJhZ2dpbmcpIHtcclxuICAgICAgICBsYXllci5kcmFnZ2luZ1tkcmFnZ2luZyA/ICdlbmFibGUnOiAnZGlzYWJsZSddKCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbGF5ZXIuZWFjaExheWVyKGZ1bmN0aW9uKHN1YmxheWVyKSB7XHJcbiAgICAgICAgICBzdWJsYXllci5kcmFnZ2luZ1tkcmFnZ2luZyA/ICdlbmFibGUnOiAnZGlzYWJsZSddKCk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuXHJcblxyXG4gICAgICBsYXllci50cmFuc2Zvcm0uc2V0T3B0aW9ucyh7XHJcbiAgICAgICAgc2NhbGluZzogc2NhbGluZyxcclxuICAgICAgICByb3RhdGlvbjogcm90YXRpb24sXHJcbiAgICAgICAgdW5pZm9ybVNjYWxpbmc6IHVuaWZvcm1cclxuICAgICAgfSkuZW5hYmxlKCk7XHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuW10uc2xpY2UuY2FsbChkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdpbnB1dFt0eXBlPWNoZWNrYm94XScpKVxyXG4uZm9yRWFjaChmdW5jdGlvbihjaGVja2JveCkge1xyXG4gIEwuRG9tRXZlbnQub24oY2hlY2tib3gsICdjaGFuZ2UnLCB1cGRhdGUpO1xyXG59KTtcclxuXHJcbnVwZGF0ZSgpO1xyXG4iLCJ2YXIgTCA9IHJlcXVpcmUoJ2xlYWZsZXQnKTtcclxucmVxdWlyZSgnbGVhZmxldC1wYXRoLWRyYWcnKTtcclxucmVxdWlyZSgnLi9zcmMvVXRpbCcpO1xyXG5yZXF1aXJlKCcuL3NyYy9NYXRyaXgnKTtcclxucmVxdWlyZSgnLi9zcmMvUGF0aC5UcmFuc2Zvcm0nKTtcclxuIiwicmVxdWlyZSgnLi9zcmMvU1ZHJyk7XG5yZXF1aXJlKCcuL3NyYy9TVkcuVk1MJyk7XG5yZXF1aXJlKCcuL3NyYy9DYW52YXMnKTtcbnJlcXVpcmUoJy4vc3JjL1BhdGguVHJhbnNmb3JtJyk7XG5yZXF1aXJlKCcuL3NyYy9QYXRoLkRyYWcnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBMLlBhdGguRHJhZztcbiIsIkwuVXRpbC50cnVlRm4gPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHRydWU7XG59O1xuXG5MLkNhbnZhcy5pbmNsdWRlKHtcblxuICAvKipcbiAgICogRG8gbm90aGluZ1xuICAgKiBAcGFyYW0gIHtMLlBhdGh9IGxheWVyXG4gICAqL1xuICBfcmVzZXRUcmFuc2Zvcm1QYXRoOiBmdW5jdGlvbihsYXllcikge1xuICAgIGlmICghdGhpcy5fY29udGFpbmVyQ29weSkgcmV0dXJuO1xuXG4gICAgZGVsZXRlIHRoaXMuX2NvbnRhaW5lckNvcHk7XG5cbiAgICBpZiAobGF5ZXIuX2NvbnRhaW5zUG9pbnRfKSB7XG4gICAgICBsYXllci5fY29udGFpbnNQb2ludCA9IGxheWVyLl9jb250YWluc1BvaW50XztcbiAgICAgIGRlbGV0ZSBsYXllci5fY29udGFpbnNQb2ludF87XG5cbiAgICAgIHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuICAgIH1cbiAgfSxcblxuXG4gIC8qKlxuICAgKiBBbGdvcml0aG0gb3V0bGluZTpcbiAgICpcbiAgICogMS4gcHJlLXRyYW5zZm9ybSAtIGNsZWFyIHRoZSBwYXRoIG91dCBvZiB0aGUgY2FudmFzLCBjb3B5IGNhbnZhcyBzdGF0ZVxuICAgKiAyLiBhdCBldmVyeSBmcmFtZTpcbiAgICogICAgMi4xLiBzYXZlXG4gICAqICAgIDIuMi4gcmVkcmF3IHRoZSBjYW52YXMgZnJvbSBzYXZlZCBvbmVcbiAgICogICAgMi4zLiB0cmFuc2Zvcm1cbiAgICogICAgMi40LiBkcmF3IHBhdGhcbiAgICogICAgMi41LiByZXN0b3JlXG4gICAqIDMuIFJlcGVhdFxuICAgKlxuICAgKiBAcGFyYW0gIHtMLlBhdGh9ICAgICAgICAgbGF5ZXJcbiAgICogQHBhcmFtICB7QXJyYXkuPE51bWJlcj59IG1hdHJpeFxuICAgKi9cbiAgdHJhbnNmb3JtUGF0aDogZnVuY3Rpb24obGF5ZXIsIG1hdHJpeCkge1xuICAgIHZhciBjb3B5ICAgPSB0aGlzLl9jb250YWluZXJDb3B5O1xuICAgIHZhciBjdHggICAgPSB0aGlzLl9jdHgsIGNvcHlDdHg7XG4gICAgdmFyIG0gICAgICA9IEwuQnJvd3Nlci5yZXRpbmEgPyAyIDogMTtcbiAgICB2YXIgYm91bmRzID0gdGhpcy5fYm91bmRzO1xuICAgIHZhciBzaXplICAgPSBib3VuZHMuZ2V0U2l6ZSgpO1xuICAgIHZhciBwb3MgICAgPSBib3VuZHMubWluO1xuXG4gICAgaWYgKCFjb3B5KSB7IC8vIGdldCBjb3B5IG9mIGFsbCByZW5kZXJlZCBsYXllcnNcbiAgICAgIGNvcHkgPSB0aGlzLl9jb250YWluZXJDb3B5ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgICBjb3B5Q3R4ID0gY29weS5nZXRDb250ZXh0KCcyZCcpO1xuICAgICAgLy8gZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChjb3B5KTtcblxuICAgICAgY29weS53aWR0aCAgPSBtICogc2l6ZS54O1xuICAgICAgY29weS5oZWlnaHQgPSBtICogc2l6ZS55O1xuXG4gICAgICB0aGlzLl9yZW1vdmVQYXRoKGxheWVyKTtcbiAgICAgIHRoaXMuX3JlZHJhdygpO1xuXG4gICAgICBjb3B5Q3R4LnRyYW5zbGF0ZShtICogYm91bmRzLm1pbi54LCBtICogYm91bmRzLm1pbi55KTtcbiAgICAgIGNvcHlDdHguZHJhd0ltYWdlKHRoaXMuX2NvbnRhaW5lciwgMCwgMCk7XG4gICAgICB0aGlzLl9pbml0UGF0aChsYXllcik7XG5cbiAgICAgIC8vIGF2b2lkIGZsaWNrZXJpbmcgYmVjYXVzZSBvZiB0aGUgJ21vdXNlb3ZlcidzXG4gICAgICBsYXllci5fY29udGFpbnNQb2ludF8gPSBsYXllci5fY29udGFpbnNQb2ludDtcbiAgICAgIGxheWVyLl9jb250YWluc1BvaW50ID0gTC5VdGlsLnRydWVGbjtcbiAgICB9XG5cbiAgICBjdHguc2F2ZSgpO1xuICAgIGN0eC5jbGVhclJlY3QocG9zLngsIHBvcy55LCBzaXplLnggKiBtLCBzaXplLnkgKiBtKTtcbiAgICBjdHguc2V0VHJhbnNmb3JtKDEsIDAsIDAsIDEsIDAsIDApO1xuICAgIGN0eC5yZXN0b3JlKCk7XG4gICAgY3R4LnNhdmUoKTtcblxuICAgIGN0eC5kcmF3SW1hZ2UodGhpcy5fY29udGFpbmVyQ29weSwgMCwgMCwgc2l6ZS54LCBzaXplLnkpO1xuICAgIGN0eC50cmFuc2Zvcm0uYXBwbHkoY3R4LCBtYXRyaXgpO1xuXG4gICAgLy8gbm93IGRyYXcgb25lIGxheWVyIG9ubHlcbiAgICB0aGlzLl9kcmF3aW5nID0gdHJ1ZTtcbiAgICBsYXllci5fdXBkYXRlUGF0aCgpO1xuICAgIHRoaXMuX2RyYXdpbmcgPSBmYWxzZTtcblxuICAgIGN0eC5yZXN0b3JlKCk7XG4gIH1cblxufSk7XG4iLCIvKipcbiAqIERyYWcgaGFuZGxlclxuICogQGNsYXNzIEwuUGF0aC5EcmFnXG4gKiBAZXh0ZW5kcyB7TC5IYW5kbGVyfVxuICovXG5MLkhhbmRsZXIuUGF0aERyYWcgPSBMLkhhbmRsZXIuZXh0ZW5kKCAvKiogQGxlbmRzICBMLlBhdGguRHJhZy5wcm90b3R5cGUgKi8ge1xuXG4gIHN0YXRpY3M6IHtcbiAgICBEUkFHR0lOR19DTFM6ICdsZWFmbGV0LXBhdGgtZHJhZ2dhYmxlJyxcbiAgfSxcblxuXG4gIC8qKlxuICAgKiBAcGFyYW0gIHtMLlBhdGh9IHBhdGhcbiAgICogQGNvbnN0cnVjdG9yXG4gICAqL1xuICBpbml0aWFsaXplOiBmdW5jdGlvbihwYXRoKSB7XG5cbiAgICAvKipcbiAgICAgKiBAdHlwZSB7TC5QYXRofVxuICAgICAqL1xuICAgIHRoaXMuX3BhdGggPSBwYXRoO1xuXG4gICAgLyoqXG4gICAgICogQHR5cGUge0FycmF5LjxOdW1iZXI+fVxuICAgICAqL1xuICAgIHRoaXMuX21hdHJpeCA9IG51bGw7XG5cbiAgICAvKipcbiAgICAgKiBAdHlwZSB7TC5Qb2ludH1cbiAgICAgKi9cbiAgICB0aGlzLl9zdGFydFBvaW50ID0gbnVsbDtcblxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtMLlBvaW50fVxuICAgICAqL1xuICAgIHRoaXMuX2RyYWdTdGFydFBvaW50ID0gbnVsbDtcblxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtCb29sZWFufVxuICAgICAqL1xuICAgIHRoaXMuX21hcERyYWdnaW5nV2FzRW5hYmxlZCA9IGZhbHNlO1xuXG4gIH0sXG5cbiAgLyoqXG4gICAqIEVuYWJsZSBkcmFnZ2luZ1xuICAgKi9cbiAgYWRkSG9va3M6IGZ1bmN0aW9uKCkge1xuICAgIHRoaXMuX3BhdGgub24oJ21vdXNlZG93bicsIHRoaXMuX29uRHJhZ1N0YXJ0LCB0aGlzKTtcblxuICAgIHRoaXMuX3BhdGgub3B0aW9ucy5jbGFzc05hbWUgPSB0aGlzLl9wYXRoLm9wdGlvbnMuY2xhc3NOYW1lID9cbiAgICAgICAgKHRoaXMuX3BhdGgub3B0aW9ucy5jbGFzc05hbWUgKyAnICcgKyBMLkhhbmRsZXIuUGF0aERyYWcuRFJBR0dJTkdfQ0xTKSA6XG4gICAgICAgICBMLkhhbmRsZXIuUGF0aERyYWcuRFJBR0dJTkdfQ0xTO1xuXG4gICAgaWYgKHRoaXMuX3BhdGguX3BhdGgpIHtcbiAgICAgIEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9wYXRoLl9wYXRoLCBMLkhhbmRsZXIuUGF0aERyYWcuRFJBR0dJTkdfQ0xTKTtcbiAgICB9XG4gIH0sXG5cbiAgLyoqXG4gICAqIERpc2FibGUgZHJhZ2dpbmdcbiAgICovXG4gIHJlbW92ZUhvb2tzOiBmdW5jdGlvbigpIHtcbiAgICB0aGlzLl9wYXRoLm9mZignbW91c2Vkb3duJywgdGhpcy5fb25EcmFnU3RhcnQsIHRoaXMpO1xuXG4gICAgdGhpcy5fcGF0aC5vcHRpb25zLmNsYXNzTmFtZSA9IHRoaXMuX3BhdGgub3B0aW9ucy5jbGFzc05hbWVcbiAgICAgIC5yZXBsYWNlKG5ldyBSZWdFeHAoJ1xcXFxzKycgKyBMLkhhbmRsZXIuUGF0aERyYWcuRFJBR0dJTkdfQ0xTKSwgJycpO1xuICAgIGlmICh0aGlzLl9wYXRoLl9wYXRoKSB7XG4gICAgICBMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fcGF0aC5fcGF0aCwgTC5IYW5kbGVyLlBhdGhEcmFnLkRSQUdHSU5HX0NMUyk7XG4gICAgfVxuICB9LFxuXG4gIC8qKlxuICAgKiBAcmV0dXJuIHtCb29sZWFufVxuICAgKi9cbiAgbW92ZWQ6IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLl9wYXRoLl9kcmFnTW92ZWQ7XG4gIH0sXG5cbiAgLyoqXG4gICAqIFN0YXJ0IGRyYWdcbiAgICogQHBhcmFtICB7TC5Nb3VzZUV2ZW50fSBldnRcbiAgICovXG4gIF9vbkRyYWdTdGFydDogZnVuY3Rpb24oZXZ0KSB7XG4gICAgdmFyIGV2ZW50VHlwZSA9IGV2dC5vcmlnaW5hbEV2ZW50Ll9zaW11bGF0ZWQgPyAndG91Y2hzdGFydCcgOiBldnQub3JpZ2luYWxFdmVudC50eXBlO1xuXG4gICAgdGhpcy5fbWFwRHJhZ2dpbmdXYXNFbmFibGVkID0gZmFsc2U7XG4gICAgdGhpcy5fc3RhcnRQb2ludCA9IGV2dC5jb250YWluZXJQb2ludC5jbG9uZSgpO1xuICAgIHRoaXMuX2RyYWdTdGFydFBvaW50ID0gZXZ0LmNvbnRhaW5lclBvaW50LmNsb25lKCk7XG4gICAgdGhpcy5fbWF0cml4ID0gWzEsIDAsIDAsIDEsIDAsIDBdO1xuICAgIEwuRG9tRXZlbnQuc3RvcChldnQub3JpZ2luYWxFdmVudCk7XG5cbiAgICBMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fcGF0aC5fcmVuZGVyZXIuX2NvbnRhaW5lciwgJ2xlYWZsZXQtaW50ZXJhY3RpdmUnKTtcbiAgICBMLkRvbUV2ZW50XG4gICAgICAub24oZG9jdW1lbnQsIEwuRHJhZ2dhYmxlLk1PVkVbZXZlbnRUeXBlXSwgdGhpcy5fb25EcmFnLCAgICB0aGlzKVxuICAgICAgLm9uKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5FTkRbZXZlbnRUeXBlXSwgIHRoaXMuX29uRHJhZ0VuZCwgdGhpcyk7XG5cbiAgICBpZiAodGhpcy5fcGF0aC5fbWFwLmRyYWdnaW5nLmVuYWJsZWQoKSkge1xuICAgICAgLy8gSSBndWVzcyBpdCdzIHJlcXVpcmVkIGJlY2F1c2UgbW91c2Rvd24gZ2V0cyBzaW11bGF0ZWQgd2l0aCBhIGRlbGF5XG4gICAgICAvL3RoaXMuX3BhdGguX21hcC5kcmFnZ2luZy5fZHJhZ2dhYmxlLl9vblVwKGV2dCk7XG5cbiAgICAgIHRoaXMuX3BhdGguX21hcC5kcmFnZ2luZy5kaXNhYmxlKCk7XG4gICAgICB0aGlzLl9tYXBEcmFnZ2luZ1dhc0VuYWJsZWQgPSB0cnVlO1xuICAgIH1cbiAgICB0aGlzLl9wYXRoLl9kcmFnTW92ZWQgPSBmYWxzZTtcblxuICAgIGlmICh0aGlzLl9wYXRoLl9wb3B1cCkgeyAvLyB0aGF0IG1pZ2h0IGJlIGEgY2FzZSBvbiB0b3VjaCBkZXZpY2VzIGFzIHdlbGxcbiAgICAgIHRoaXMuX3BhdGguX3BvcHVwLl9jbG9zZSgpO1xuICAgIH1cblxuICAgIHRoaXMuX3JlcGxhY2VDb29yZEdldHRlcnMoZXZ0KTtcbiAgfSxcblxuICAvKipcbiAgICogRHJhZ2dpbmdcbiAgICogQHBhcmFtICB7TC5Nb3VzZUV2ZW50fSBldnRcbiAgICovXG4gIF9vbkRyYWc6IGZ1bmN0aW9uKGV2dCkge1xuICAgIEwuRG9tRXZlbnQuc3RvcChldnQpO1xuXG4gICAgdmFyIGZpcnN0ID0gKGV2dC50b3VjaGVzICYmIGV2dC50b3VjaGVzLmxlbmd0aCA+PSAxID8gZXZ0LnRvdWNoZXNbMF0gOiBldnQpO1xuICAgIHZhciBjb250YWluZXJQb2ludCA9IHRoaXMuX3BhdGguX21hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChmaXJzdCk7XG5cbiAgICAvLyBza2lwIHRhcHNcbiAgICBpZiAoZXZ0LnR5cGUgPT09ICd0b3VjaG1vdmUnICYmICF0aGlzLl9wYXRoLl9kcmFnTW92ZWQpIHtcbiAgICAgIHZhciB0b3RhbE1vdXNlRHJhZ0Rpc3RhbmNlID0gdGhpcy5fZHJhZ1N0YXJ0UG9pbnQuZGlzdGFuY2VUbyhjb250YWluZXJQb2ludCk7XG4gICAgICBpZiAodG90YWxNb3VzZURyYWdEaXN0YW5jZSA8PSB0aGlzLl9wYXRoLl9tYXAub3B0aW9ucy50YXBUb2xlcmFuY2UpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cblxuICAgIHZhciB4ID0gY29udGFpbmVyUG9pbnQueDtcbiAgICB2YXIgeSA9IGNvbnRhaW5lclBvaW50Lnk7XG5cbiAgICB2YXIgZHggPSB4IC0gdGhpcy5fc3RhcnRQb2ludC54O1xuICAgIHZhciBkeSA9IHkgLSB0aGlzLl9zdGFydFBvaW50Lnk7XG5cbiAgICAvLyBTZW5kIGV2ZW50cyBvbmx5IGlmIHBvaW50IHdhcyBtb3ZlZFxuICAgIGlmIChkeCB8fCBkeSkge1xuICAgICAgaWYgKCF0aGlzLl9wYXRoLl9kcmFnTW92ZWQpIHtcbiAgICAgICAgdGhpcy5fcGF0aC5fZHJhZ01vdmVkID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5fcGF0aC5maXJlKCdkcmFnc3RhcnQnLCBldnQpO1xuICAgICAgICAvLyB3ZSBkb24ndCB3YW50IHRoYXQgdG8gaGFwcGVuIG9uIGNsaWNrXG4gICAgICAgIHRoaXMuX3BhdGguYnJpbmdUb0Zyb250KCk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuX21hdHJpeFs0XSArPSBkeDtcbiAgICAgIHRoaXMuX21hdHJpeFs1XSArPSBkeTtcblxuICAgICAgdGhpcy5fc3RhcnRQb2ludC54ID0geDtcbiAgICAgIHRoaXMuX3N0YXJ0UG9pbnQueSA9IHk7XG5cbiAgICAgIHRoaXMuX3BhdGguZmlyZSgncHJlZHJhZycsIGV2dCk7XG4gICAgICB0aGlzLl9wYXRoLl90cmFuc2Zvcm0odGhpcy5fbWF0cml4KTtcbiAgICAgIHRoaXMuX3BhdGguZmlyZSgnZHJhZycsIGV2dCk7XG4gICAgfVxuICB9LFxuXG4gIC8qKlxuICAgKiBEcmFnZ2luZyBzdG9wcGVkLCBhcHBseVxuICAgKiBAcGFyYW0gIHtMLk1vdXNlRXZlbnR9IGV2dFxuICAgKi9cbiAgX29uRHJhZ0VuZDogZnVuY3Rpb24oZXZ0KSB7XG4gICAgdmFyIGNvbnRhaW5lclBvaW50ID0gdGhpcy5fcGF0aC5fbWFwLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGV2dCk7XG4gICAgdmFyIG1vdmVkID0gdGhpcy5tb3ZlZCgpO1xuXG4gICAgLy8gYXBwbHkgbWF0cml4XG4gICAgaWYgKG1vdmVkKSB7XG4gICAgICB0aGlzLl90cmFuc2Zvcm1Qb2ludHModGhpcy5fbWF0cml4KTtcbiAgICAgIHRoaXMuX3BhdGguX3VwZGF0ZVBhdGgoKTtcbiAgICAgIHRoaXMuX3BhdGguX3Byb2plY3QoKTtcbiAgICAgIHRoaXMuX3BhdGguX3RyYW5zZm9ybShudWxsKTtcblxuICAgICAgTC5Eb21FdmVudC5zdG9wKGV2dCk7XG4gICAgfVxuXG5cbiAgICBMLkRvbUV2ZW50XG4gICAgICAub2ZmKGRvY3VtZW50LCAnbW91c2Vtb3ZlIHRvdWNobW92ZScsIHRoaXMuX29uRHJhZywgdGhpcylcbiAgICAgIC5vZmYoZG9jdW1lbnQsICdtb3VzZXVwIHRvdWNoZW5kJywgICAgdGhpcy5fb25EcmFnRW5kLCB0aGlzKTtcblxuICAgIHRoaXMuX3Jlc3RvcmVDb29yZEdldHRlcnMoKTtcblxuICAgIC8vIGNvbnNpc3RlbmN5XG4gICAgaWYgKG1vdmVkKSB7XG4gICAgICB0aGlzLl9wYXRoLmZpcmUoJ2RyYWdlbmQnLCB7XG4gICAgICAgIGRpc3RhbmNlOiBNYXRoLnNxcnQoXG4gICAgICAgICAgTC5MaW5lVXRpbC5fc3FEaXN0KHRoaXMuX2RyYWdTdGFydFBvaW50LCBjb250YWluZXJQb2ludClcbiAgICAgICAgKVxuICAgICAgfSk7XG5cbiAgICAgIC8vIGhhY2sgZm9yIHNraXBwaW5nIHRoZSBjbGljayBpbiBjYW52YXMtcmVuZGVyZWQgbGF5ZXJzXG4gICAgICB2YXIgY29udGFpbnMgPSB0aGlzLl9wYXRoLl9jb250YWluc1BvaW50O1xuICAgICAgdGhpcy5fcGF0aC5fY29udGFpbnNQb2ludCA9IEwuVXRpbC5mYWxzZUZuO1xuICAgICAgTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUoZnVuY3Rpb24oKSB7XG4gICAgICAgIEwuRG9tRXZlbnQuX3NraXBwZWQoeyB0eXBlOiAnY2xpY2snIH0pO1xuICAgICAgICB0aGlzLl9wYXRoLl9jb250YWluc1BvaW50ID0gY29udGFpbnM7XG4gICAgICB9LCB0aGlzKTtcbiAgICB9XG5cbiAgICB0aGlzLl9tYXRyaXggICAgICAgICAgPSBudWxsO1xuICAgIHRoaXMuX3N0YXJ0UG9pbnQgICAgICA9IG51bGw7XG4gICAgdGhpcy5fZHJhZ1N0YXJ0UG9pbnQgID0gbnVsbDtcbiAgICB0aGlzLl9wYXRoLl9kcmFnTW92ZWQgPSBmYWxzZTtcblxuICAgIGlmICh0aGlzLl9tYXBEcmFnZ2luZ1dhc0VuYWJsZWQpIHtcbiAgICAgIGlmIChtb3ZlZCkgTC5Eb21FdmVudC5fZmFrZVN0b3AoeyB0eXBlOiAnY2xpY2snIH0pO1xuICAgICAgdGhpcy5fcGF0aC5fbWFwLmRyYWdnaW5nLmVuYWJsZSgpO1xuICAgIH1cbiAgfSxcblxuXG4gIC8qKlxuICAgKiBBcHBsaWVzIHRyYW5zZm9ybWF0aW9uLCBkb2VzIGl0IGluIG9uZSBzd2VlcCBmb3IgcGVyZm9ybWFuY2UsXG4gICAqIHNvIGRvbid0IGJlIHN1cnByaXNlZCBhYm91dCB0aGUgY29kZSByZXBldGl0aW9uLlxuICAgKlxuICAgKiBbIHggXSAgIFsgYSAgYiAgdHggXSBbIHggXSAgIFsgYSAqIHggKyBiICogeSArIHR4IF1cbiAgICogWyB5IF0gPSBbIGMgIGQgIHR5IF0gWyB5IF0gPSBbIGMgKiB4ICsgZCAqIHkgKyB0eSBdXG4gICAqXG4gICAqIEBwYXJhbSB7QXJyYXkuPE51bWJlcj59IG1hdHJpeFxuICAgKi9cbiAgX3RyYW5zZm9ybVBvaW50czogZnVuY3Rpb24obWF0cml4LCBkZXN0KSB7XG4gICAgdmFyIHBhdGggPSB0aGlzLl9wYXRoO1xuICAgIHZhciBpLCBsZW4sIGxhdGxuZztcblxuICAgIHZhciBweCA9IEwucG9pbnQobWF0cml4WzRdLCBtYXRyaXhbNV0pO1xuXG4gICAgdmFyIGNycyA9IHBhdGguX21hcC5vcHRpb25zLmNycztcbiAgICB2YXIgdHJhbnNmb3JtYXRpb24gPSBjcnMudHJhbnNmb3JtYXRpb247XG4gICAgdmFyIHNjYWxlID0gY3JzLnNjYWxlKHBhdGguX21hcC5nZXRab29tKCkpO1xuICAgIHZhciBwcm9qZWN0aW9uID0gY3JzLnByb2plY3Rpb247XG5cbiAgICB2YXIgZGlmZiA9IHRyYW5zZm9ybWF0aW9uLnVudHJhbnNmb3JtKHB4LCBzY2FsZSlcbiAgICAgIC5zdWJ0cmFjdCh0cmFuc2Zvcm1hdGlvbi51bnRyYW5zZm9ybShMLnBvaW50KDAsIDApLCBzY2FsZSkpO1xuICAgIHZhciBhcHBseVRyYW5zZm9ybSA9ICFkZXN0O1xuXG4gICAgcGF0aC5fYm91bmRzID0gbmV3IEwuTGF0TG5nQm91bmRzKCk7XG5cbiAgICAvLyBjb25zb2xlLnRpbWUoJ3RyYW5zZm9ybScpO1xuICAgIC8vIGFsbCBzaGlmdHMgYXJlIGluLXBsYWNlXG4gICAgaWYgKHBhdGguX3BvaW50KSB7IC8vIEwuQ2lyY2xlXG4gICAgICBkZXN0ID0gcHJvamVjdGlvbi51bnByb2plY3QoXG4gICAgICAgIHByb2plY3Rpb24ucHJvamVjdChwYXRoLl9sYXRsbmcpLl9hZGQoZGlmZikpO1xuICAgICAgaWYgKGFwcGx5VHJhbnNmb3JtKSB7XG4gICAgICAgIHBhdGguX2xhdGxuZyA9IGRlc3Q7XG4gICAgICAgIHBhdGguX3BvaW50Ll9hZGQocHgpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAocGF0aC5fcmluZ3MgfHwgcGF0aC5fcGFydHMpIHsgLy8gZXZlcnl0aGluZyBlbHNlXG4gICAgICB2YXIgcmluZ3MgICA9IHBhdGguX3JpbmdzIHx8IHBhdGguX3BhcnRzO1xuICAgICAgdmFyIGxhdGxuZ3MgPSBwYXRoLl9sYXRsbmdzO1xuICAgICAgZGVzdCA9IGRlc3QgfHwgbGF0bG5ncztcbiAgICAgIGlmICghTC5VdGlsLmlzQXJyYXkobGF0bG5nc1swXSkpIHsgLy8gcG9seWxpbmVcbiAgICAgICAgbGF0bG5ncyA9IFtsYXRsbmdzXTtcbiAgICAgICAgZGVzdCAgICA9IFtkZXN0XTtcbiAgICAgIH1cbiAgICAgIGZvciAoaSA9IDAsIGxlbiA9IHJpbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICAgIGRlc3RbaV0gPSBkZXN0W2ldIHx8IFtdO1xuICAgICAgICBmb3IgKHZhciBqID0gMCwgamogPSByaW5nc1tpXS5sZW5ndGg7IGogPCBqajsgaisrKSB7XG4gICAgICAgICAgbGF0bG5nICAgICA9IGxhdGxuZ3NbaV1bal07XG4gICAgICAgICAgZGVzdFtpXVtqXSA9IHByb2plY3Rpb25cbiAgICAgICAgICAgIC51bnByb2plY3QocHJvamVjdGlvbi5wcm9qZWN0KGxhdGxuZykuX2FkZChkaWZmKSk7XG4gICAgICAgICAgaWYgKGFwcGx5VHJhbnNmb3JtKSB7XG4gICAgICAgICAgICBwYXRoLl9ib3VuZHMuZXh0ZW5kKGxhdGxuZ3NbaV1bal0pO1xuICAgICAgICAgICAgcmluZ3NbaV1bal0uX2FkZChweCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBkZXN0O1xuICAgIC8vIGNvbnNvbGUudGltZUVuZCgndHJhbnNmb3JtJyk7XG4gIH0sXG5cblxuXG4gIC8qKlxuICAgKiBJZiB5b3Ugd2FudCB0byByZWFkIHRoZSBsYXRsbmdzIGR1cmluZyB0aGUgZHJhZyAtIHlvdXIgcmlnaHQsXG4gICAqIGJ1dCB0aGV5IGhhdmUgdG8gYmUgdHJhbnNmb3JtZWRcbiAgICovXG4gIF9yZXBsYWNlQ29vcmRHZXR0ZXJzOiBmdW5jdGlvbigpIHtcbiAgICBpZiAodGhpcy5fcGF0aC5nZXRMYXRMbmcpIHsgLy8gQ2lyY2xlLCBDaXJjbGVNYXJrZXJcbiAgICAgIHRoaXMuX3BhdGguZ2V0TGF0TG5nXyA9IHRoaXMuX3BhdGguZ2V0TGF0TG5nO1xuICAgICAgdGhpcy5fcGF0aC5nZXRMYXRMbmcgPSBMLlV0aWwuYmluZChmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZHJhZ2dpbmcuX3RyYW5zZm9ybVBvaW50cyh0aGlzLmRyYWdnaW5nLl9tYXRyaXgsIHt9KTtcbiAgICAgIH0sIHRoaXMuX3BhdGgpO1xuICAgIH0gZWxzZSBpZiAodGhpcy5fcGF0aC5nZXRMYXRMbmdzKSB7XG4gICAgICB0aGlzLl9wYXRoLmdldExhdExuZ3NfID0gdGhpcy5fcGF0aC5nZXRMYXRMbmdzO1xuICAgICAgdGhpcy5fcGF0aC5nZXRMYXRMbmdzID0gTC5VdGlsLmJpbmQoZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRyYWdnaW5nLl90cmFuc2Zvcm1Qb2ludHModGhpcy5kcmFnZ2luZy5fbWF0cml4LCBbXSk7XG4gICAgICB9LCB0aGlzLl9wYXRoKTtcbiAgICB9XG4gIH0sXG5cblxuICAvKipcbiAgICogUHV0IGJhY2sgdGhlIGdldHRlcnNcbiAgICovXG4gIF9yZXN0b3JlQ29vcmRHZXR0ZXJzOiBmdW5jdGlvbigpIHtcbiAgICBpZiAodGhpcy5fcGF0aC5nZXRMYXRMbmdfKSB7XG4gICAgICB0aGlzLl9wYXRoLmdldExhdExuZyA9IHRoaXMuX3BhdGguZ2V0TGF0TG5nXztcbiAgICAgIGRlbGV0ZSB0aGlzLl9wYXRoLmdldExhdExuZ187XG4gICAgfSBlbHNlIGlmICh0aGlzLl9wYXRoLmdldExhdExuZ3NfKSB7XG4gICAgICB0aGlzLl9wYXRoLmdldExhdExuZ3MgPSB0aGlzLl9wYXRoLmdldExhdExuZ3NfO1xuICAgICAgZGVsZXRlIHRoaXMuX3BhdGguZ2V0TGF0TG5nc187XG4gICAgfVxuICB9XG5cbn0pO1xuXG5cbi8qKlxuICogQHBhcmFtICB7TC5QYXRofSBsYXllclxuICogQHJldHVybiB7TC5QYXRofVxuICovXG5MLkhhbmRsZXIuUGF0aERyYWcubWFrZURyYWdnYWJsZSA9IGZ1bmN0aW9uKGxheWVyKSB7XG4gIGxheWVyLmRyYWdnaW5nID0gbmV3IEwuSGFuZGxlci5QYXRoRHJhZyhsYXllcik7XG4gIHJldHVybiBsYXllcjtcbn07XG5cblxuLyoqXG4gKiBBbHNvIGV4cG9zZSBhcyBhIG1ldGhvZFxuICogQHJldHVybiB7TC5QYXRofVxuICovXG5MLlBhdGgucHJvdG90eXBlLm1ha2VEcmFnZ2FibGUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIEwuSGFuZGxlci5QYXRoRHJhZy5tYWtlRHJhZ2dhYmxlKHRoaXMpO1xufTtcblxuXG5MLlBhdGguYWRkSW5pdEhvb2soZnVuY3Rpb24oKSB7XG4gIGlmICh0aGlzLm9wdGlvbnMuZHJhZ2dhYmxlKSB7XG4gICAgLy8gZW5zdXJlIGludGVyYWN0aXZlXG4gICAgdGhpcy5vcHRpb25zLmludGVyYWN0aXZlID0gdHJ1ZTtcblxuICAgIGlmICh0aGlzLmRyYWdnaW5nKSB7XG4gICAgICB0aGlzLmRyYWdnaW5nLmVuYWJsZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBMLkhhbmRsZXIuUGF0aERyYWcubWFrZURyYWdnYWJsZSh0aGlzKTtcbiAgICAgIHRoaXMuZHJhZ2dpbmcuZW5hYmxlKCk7XG4gICAgfVxuICB9IGVsc2UgaWYgKHRoaXMuZHJhZ2dpbmcpIHtcbiAgICB0aGlzLmRyYWdnaW5nLmRpc2FibGUoKTtcbiAgfVxufSk7XG4iLCIvKipcbiAqIExlYWZsZXQgdmVjdG9yIGZlYXR1cmVzIGRyYWcgZnVuY3Rpb25hbGl0eVxuICogQGF1dGhvciBBbGV4YW5kZXIgTWlsZXZza2kgPGluZm9AdzhyLm5hbWU+XG4gKiBAcHJlc2VydmVcbiAqL1xuXG4vKipcbiAqIE1hdHJpeCB0cmFuc2Zvcm0gcGF0aCBmb3IgU1ZHL1ZNTFxuICogUmVuZGVyZXItaW5kZXBlbmRlbnRcbiAqL1xuTC5QYXRoLmluY2x1ZGUoe1xuXG5cdC8qKlxuXHQgKiBBcHBsaWVzIG1hdHJpeCB0cmFuc2Zvcm1hdGlvbiB0byBTVkdcblx0ICogQHBhcmFtIHtBcnJheS48TnVtYmVyPj99IG1hdHJpeFxuXHQgKi9cblx0X3RyYW5zZm9ybTogZnVuY3Rpb24obWF0cml4KSB7XG5cdFx0aWYgKHRoaXMuX3JlbmRlcmVyKSB7XG5cdFx0XHRpZiAobWF0cml4KSB7XG5cdFx0XHRcdHRoaXMuX3JlbmRlcmVyLnRyYW5zZm9ybVBhdGgodGhpcywgbWF0cml4KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdC8vIHJlc2V0IHRyYW5zZm9ybSBtYXRyaXhcblx0XHRcdFx0dGhpcy5fcmVuZGVyZXIuX3Jlc2V0VHJhbnNmb3JtUGF0aCh0aGlzKTtcblx0XHRcdFx0dGhpcy5fdXBkYXRlKCk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDaGVjayBpZiB0aGUgZmVhdHVyZSB3YXMgZHJhZ2dlZCwgdGhhdCdsbCBzdXByZXNzIHRoZSBjbGljayBldmVudFxuXHQgKiBvbiBtb3VzZXVwLiBUaGF0IGZpeGVzIHBvcHVwcyBmb3IgZXhhbXBsZVxuXHQgKlxuXHQgKiBAcGFyYW0gIHtNb3VzZUV2ZW50fSBlXG5cdCAqL1xuXHRfb25Nb3VzZUNsaWNrOiBmdW5jdGlvbihlKSB7XG5cdFx0aWYgKCh0aGlzLmRyYWdnaW5nICYmIHRoaXMuZHJhZ2dpbmcubW92ZWQoKSkgfHxcblx0XHRcdCh0aGlzLl9tYXAuZHJhZ2dpbmcgJiYgdGhpcy5fbWFwLmRyYWdnaW5nLm1vdmVkKCkpKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dGhpcy5fZmlyZU1vdXNlRXZlbnQoZSk7XG5cdH1cblxufSk7XG4iLCJMLlNWRy5pbmNsdWRlKCFMLkJyb3dzZXIudm1sID8ge30gOiB7XG5cblx0LyoqXG5cdCAqIFJlc2V0IHRyYW5zZm9ybSBtYXRyaXhcblx0ICovXG5cdF9yZXNldFRyYW5zZm9ybVBhdGg6IGZ1bmN0aW9uKGxheWVyKSB7XG5cdFx0aWYgKGxheWVyLl9za2V3KSB7XG5cdFx0XHQvLyBzdXBlciBpbXBvcnRhbnQhIHdvcmthcm91bmQgZm9yIGEgJ2p1bXBpbmcnIGdsaXRjaDpcblx0XHRcdC8vIGRpc2FibGUgdHJhbnNmb3JtIGJlZm9yZSByZW1vdmluZyBpdFxuXHRcdFx0bGF5ZXIuX3NrZXcub24gPSBmYWxzZTtcblx0XHRcdGxheWVyLl9wYXRoLnJlbW92ZUNoaWxkKGxheWVyLl9za2V3KTtcblx0XHRcdGxheWVyLl9za2V3ID0gbnVsbDtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIEFwcGxpZXMgbWF0cml4IHRyYW5zZm9ybWF0aW9uIHRvIFZNTFxuXHQgKiBAcGFyYW0ge0wuUGF0aH0gICAgICAgICBsYXllclxuXHQgKiBAcGFyYW0ge0FycmF5LjxOdW1iZXI+fSBtYXRyaXhcblx0ICovXG5cdHRyYW5zZm9ybVBhdGg6IGZ1bmN0aW9uKGxheWVyLCBtYXRyaXgpIHtcblx0XHR2YXIgc2tldyA9IGxheWVyLl9za2V3O1xuXG5cdFx0aWYgKCFza2V3KSB7XG5cdFx0XHRza2V3ID0gTC5TVkcuY3JlYXRlKCdza2V3Jyk7XG5cdFx0XHRsYXllci5fcGF0aC5hcHBlbmRDaGlsZChza2V3KTtcblx0XHRcdHNrZXcuc3R5bGUuYmVoYXZpb3IgPSAndXJsKCNkZWZhdWx0I1ZNTCknO1xuXHRcdFx0bGF5ZXIuX3NrZXcgPSBza2V3O1xuXHRcdH1cblxuXHRcdC8vIGhhbmRsZSBza2V3L3RyYW5zbGF0ZSBzZXBhcmF0ZWx5LCBjYXVzZSBpdCdzIGJyb2tlblxuXHRcdHZhciBtdCA9IG1hdHJpeFswXS50b0ZpeGVkKDgpICsgJyAnICsgbWF0cml4WzFdLnRvRml4ZWQoOCkgKyAnICcgK1xuXHRcdFx0bWF0cml4WzJdLnRvRml4ZWQoOCkgKyAnICcgKyBtYXRyaXhbM10udG9GaXhlZCg4KSArICcgMCAwJztcblx0XHR2YXIgb2Zmc2V0ID0gTWF0aC5mbG9vcihtYXRyaXhbNF0pLnRvRml4ZWQoKSArICcsICcgK1xuXHRcdFx0TWF0aC5mbG9vcihtYXRyaXhbNV0pLnRvRml4ZWQoKSArICcnO1xuXG5cdFx0dmFyIHMgPSB0aGlzLl9wYXRoLnN0eWxlO1xuXHRcdHZhciBsID0gcGFyc2VGbG9hdChzLmxlZnQpO1xuXHRcdHZhciB0ID0gcGFyc2VGbG9hdChzLnRvcCk7XG5cdFx0dmFyIHcgPSBwYXJzZUZsb2F0KHMud2lkdGgpO1xuXHRcdHZhciBoID0gcGFyc2VGbG9hdChzLmhlaWdodCk7XG5cblx0XHRpZiAoaXNOYU4obCkpICAgICAgIGwgPSAwO1xuXHRcdGlmIChpc05hTih0KSkgICAgICAgdCA9IDA7XG5cdFx0aWYgKGlzTmFOKHcpIHx8ICF3KSB3ID0gMTtcblx0XHRpZiAoaXNOYU4oaCkgfHwgIWgpIGggPSAxO1xuXG5cdFx0dmFyIG9yaWdpbiA9ICgtbCAvIHcgLSAwLjUpLnRvRml4ZWQoOCkgKyAnICcgKyAoLXQgLyBoIC0gMC41KS50b0ZpeGVkKDgpO1xuXG5cdFx0c2tldy5vbiA9ICdmJztcblx0XHRza2V3Lm1hdHJpeCA9IG10O1xuXHRcdHNrZXcub3JpZ2luID0gb3JpZ2luO1xuXHRcdHNrZXcub2Zmc2V0ID0gb2Zmc2V0O1xuXHRcdHNrZXcub24gPSB0cnVlO1xuXHR9XG5cbn0pO1xuIiwiTC5TVkcuaW5jbHVkZSh7XG5cblx0LyoqXG5cdCAqIFJlc2V0IHRyYW5zZm9ybSBtYXRyaXhcblx0ICovXG5cdF9yZXNldFRyYW5zZm9ybVBhdGg6IGZ1bmN0aW9uKGxheWVyKSB7XG5cdFx0bGF5ZXIuX3BhdGguc2V0QXR0cmlidXRlTlMobnVsbCwgJ3RyYW5zZm9ybScsICcnKTtcblx0fSxcblxuXHQvKipcblx0ICogQXBwbGllcyBtYXRyaXggdHJhbnNmb3JtYXRpb24gdG8gU1ZHXG5cdCAqIEBwYXJhbSB7TC5QYXRofSAgICAgICAgIGxheWVyXG5cdCAqIEBwYXJhbSB7QXJyYXkuPE51bWJlcj59IG1hdHJpeFxuXHQgKi9cblx0dHJhbnNmb3JtUGF0aDogZnVuY3Rpb24obGF5ZXIsIG1hdHJpeCkge1xuXHRcdGxheWVyLl9wYXRoLnNldEF0dHJpYnV0ZU5TKG51bGwsICd0cmFuc2Zvcm0nLFxuXHRcdFx0J21hdHJpeCgnICsgbWF0cml4LmpvaW4oJyAnKSArICcpJyk7XG5cdH1cblxufSk7XG4iLCIvKlxuIExlYWZsZXQgMS4wLjMsIGEgSlMgbGlicmFyeSBmb3IgaW50ZXJhY3RpdmUgbWFwcy4gaHR0cDovL2xlYWZsZXRqcy5jb21cbiAoYykgMjAxMC0yMDE2IFZsYWRpbWlyIEFnYWZvbmtpbiwgKGMpIDIwMTAtMjAxMSBDbG91ZE1hZGVcbiovXG4oZnVuY3Rpb24gKHdpbmRvdywgZG9jdW1lbnQsIHVuZGVmaW5lZCkge1xyXG52YXIgTCA9IHtcclxuXHR2ZXJzaW9uOiBcIjEuMC4zXCJcclxufTtcclxuXHJcbmZ1bmN0aW9uIGV4cG9zZSgpIHtcclxuXHR2YXIgb2xkTCA9IHdpbmRvdy5MO1xyXG5cclxuXHRMLm5vQ29uZmxpY3QgPSBmdW5jdGlvbiAoKSB7XHJcblx0XHR3aW5kb3cuTCA9IG9sZEw7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9O1xyXG5cclxuXHR3aW5kb3cuTCA9IEw7XHJcbn1cclxuXHJcbi8vIGRlZmluZSBMZWFmbGV0IGZvciBOb2RlIG1vZHVsZSBwYXR0ZXJuIGxvYWRlcnMsIGluY2x1ZGluZyBCcm93c2VyaWZ5XHJcbmlmICh0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0JyAmJiB0eXBlb2YgbW9kdWxlLmV4cG9ydHMgPT09ICdvYmplY3QnKSB7XHJcblx0bW9kdWxlLmV4cG9ydHMgPSBMO1xyXG5cclxuLy8gZGVmaW5lIExlYWZsZXQgYXMgYW4gQU1EIG1vZHVsZVxyXG59IGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCkge1xyXG5cdGRlZmluZShMKTtcclxufVxyXG5cclxuLy8gZGVmaW5lIExlYWZsZXQgYXMgYSBnbG9iYWwgTCB2YXJpYWJsZSwgc2F2aW5nIHRoZSBvcmlnaW5hbCBMIHRvIHJlc3RvcmUgbGF0ZXIgaWYgbmVlZGVkXHJcbmlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJykge1xyXG5cdGV4cG9zZSgpO1xyXG59XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgVXRpbFxyXG4gKlxyXG4gKiBWYXJpb3VzIHV0aWxpdHkgZnVuY3Rpb25zLCB1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseS5cclxuICovXHJcblxyXG5MLlV0aWwgPSB7XHJcblxyXG5cdC8vIEBmdW5jdGlvbiBleHRlbmQoZGVzdDogT2JqZWN0LCBzcmM/OiBPYmplY3QpOiBPYmplY3RcclxuXHQvLyBNZXJnZXMgdGhlIHByb3BlcnRpZXMgb2YgdGhlIGBzcmNgIG9iamVjdCAob3IgbXVsdGlwbGUgb2JqZWN0cykgaW50byBgZGVzdGAgb2JqZWN0IGFuZCByZXR1cm5zIHRoZSBsYXR0ZXIuIEhhcyBhbiBgTC5leHRlbmRgIHNob3J0Y3V0LlxyXG5cdGV4dGVuZDogZnVuY3Rpb24gKGRlc3QpIHtcclxuXHRcdHZhciBpLCBqLCBsZW4sIHNyYztcclxuXHJcblx0XHRmb3IgKGogPSAxLCBsZW4gPSBhcmd1bWVudHMubGVuZ3RoOyBqIDwgbGVuOyBqKyspIHtcclxuXHRcdFx0c3JjID0gYXJndW1lbnRzW2pdO1xyXG5cdFx0XHRmb3IgKGkgaW4gc3JjKSB7XHJcblx0XHRcdFx0ZGVzdFtpXSA9IHNyY1tpXTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGRlc3Q7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGNyZWF0ZShwcm90bzogT2JqZWN0LCBwcm9wZXJ0aWVzPzogT2JqZWN0KTogT2JqZWN0XHJcblx0Ly8gQ29tcGF0aWJpbGl0eSBwb2x5ZmlsbCBmb3IgW09iamVjdC5jcmVhdGVdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL09iamVjdC9jcmVhdGUpXHJcblx0Y3JlYXRlOiBPYmplY3QuY3JlYXRlIHx8IChmdW5jdGlvbiAoKSB7XHJcblx0XHRmdW5jdGlvbiBGKCkge31cclxuXHRcdHJldHVybiBmdW5jdGlvbiAocHJvdG8pIHtcclxuXHRcdFx0Ri5wcm90b3R5cGUgPSBwcm90bztcclxuXHRcdFx0cmV0dXJuIG5ldyBGKCk7XHJcblx0XHR9O1xyXG5cdH0pKCksXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBiaW5kKGZuOiBGdW5jdGlvbiwg4oCmKTogRnVuY3Rpb25cclxuXHQvLyBSZXR1cm5zIGEgbmV3IGZ1bmN0aW9uIGJvdW5kIHRvIHRoZSBhcmd1bWVudHMgcGFzc2VkLCBsaWtlIFtGdW5jdGlvbi5wcm90b3R5cGUuYmluZF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvRnVuY3Rpb24vYmluZCkuXHJcblx0Ly8gSGFzIGEgYEwuYmluZCgpYCBzaG9ydGN1dC5cclxuXHRiaW5kOiBmdW5jdGlvbiAoZm4sIG9iaikge1xyXG5cdFx0dmFyIHNsaWNlID0gQXJyYXkucHJvdG90eXBlLnNsaWNlO1xyXG5cclxuXHRcdGlmIChmbi5iaW5kKSB7XHJcblx0XHRcdHJldHVybiBmbi5iaW5kLmFwcGx5KGZuLCBzbGljZS5jYWxsKGFyZ3VtZW50cywgMSkpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBhcmdzID0gc2xpY2UuY2FsbChhcmd1bWVudHMsIDIpO1xyXG5cclxuXHRcdHJldHVybiBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHJldHVybiBmbi5hcHBseShvYmosIGFyZ3MubGVuZ3RoID8gYXJncy5jb25jYXQoc2xpY2UuY2FsbChhcmd1bWVudHMpKSA6IGFyZ3VtZW50cyk7XHJcblx0XHR9O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzdGFtcChvYmo6IE9iamVjdCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIHVuaXF1ZSBJRCBvZiBhbiBvYmplY3QsIGFzc2lnaW5nIGl0IG9uZSBpZiBpdCBkb2Vzbid0IGhhdmUgaXQuXHJcblx0c3RhbXA6IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdC8qZXNsaW50LWRpc2FibGUgKi9cclxuXHRcdG9iai5fbGVhZmxldF9pZCA9IG9iai5fbGVhZmxldF9pZCB8fCArK0wuVXRpbC5sYXN0SWQ7XHJcblx0XHRyZXR1cm4gb2JqLl9sZWFmbGV0X2lkO1xyXG5cdFx0Lyplc2xpbnQtZW5hYmxlICovXHJcblx0fSxcclxuXHJcblx0Ly8gQHByb3BlcnR5IGxhc3RJZDogTnVtYmVyXHJcblx0Ly8gTGFzdCB1bmlxdWUgSUQgdXNlZCBieSBbYHN0YW1wKClgXSgjdXRpbC1zdGFtcClcclxuXHRsYXN0SWQ6IDAsXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0aHJvdHRsZShmbjogRnVuY3Rpb24sIHRpbWU6IE51bWJlciwgY29udGV4dDogT2JqZWN0KTogRnVuY3Rpb25cclxuXHQvLyBSZXR1cm5zIGEgZnVuY3Rpb24gd2hpY2ggZXhlY3V0ZXMgZnVuY3Rpb24gYGZuYCB3aXRoIHRoZSBnaXZlbiBzY29wZSBgY29udGV4dGBcclxuXHQvLyAoc28gdGhhdCB0aGUgYHRoaXNgIGtleXdvcmQgcmVmZXJzIHRvIGBjb250ZXh0YCBpbnNpZGUgYGZuYCdzIGNvZGUpLiBUaGUgZnVuY3Rpb25cclxuXHQvLyBgZm5gIHdpbGwgYmUgY2FsbGVkIG5vIG1vcmUgdGhhbiBvbmUgdGltZSBwZXIgZ2l2ZW4gYW1vdW50IG9mIGB0aW1lYC4gVGhlIGFyZ3VtZW50c1xyXG5cdC8vIHJlY2VpdmVkIGJ5IHRoZSBib3VuZCBmdW5jdGlvbiB3aWxsIGJlIGFueSBhcmd1bWVudHMgcGFzc2VkIHdoZW4gYmluZGluZyB0aGVcclxuXHQvLyBmdW5jdGlvbiwgZm9sbG93ZWQgYnkgYW55IGFyZ3VtZW50cyBwYXNzZWQgd2hlbiBpbnZva2luZyB0aGUgYm91bmQgZnVuY3Rpb24uXHJcblx0Ly8gSGFzIGFuIGBMLmJpbmRgIHNob3J0Y3V0LlxyXG5cdHRocm90dGxlOiBmdW5jdGlvbiAoZm4sIHRpbWUsIGNvbnRleHQpIHtcclxuXHRcdHZhciBsb2NrLCBhcmdzLCB3cmFwcGVyRm4sIGxhdGVyO1xyXG5cclxuXHRcdGxhdGVyID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0XHQvLyByZXNldCBsb2NrIGFuZCBjYWxsIGlmIHF1ZXVlZFxyXG5cdFx0XHRsb2NrID0gZmFsc2U7XHJcblx0XHRcdGlmIChhcmdzKSB7XHJcblx0XHRcdFx0d3JhcHBlckZuLmFwcGx5KGNvbnRleHQsIGFyZ3MpO1xyXG5cdFx0XHRcdGFyZ3MgPSBmYWxzZTtcclxuXHRcdFx0fVxyXG5cdFx0fTtcclxuXHJcblx0XHR3cmFwcGVyRm4gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdGlmIChsb2NrKSB7XHJcblx0XHRcdFx0Ly8gY2FsbGVkIHRvbyBzb29uLCBxdWV1ZSB0byBjYWxsIGxhdGVyXHJcblx0XHRcdFx0YXJncyA9IGFyZ3VtZW50cztcclxuXHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0Ly8gY2FsbCBhbmQgbG9jayB1bnRpbCBsYXRlclxyXG5cdFx0XHRcdGZuLmFwcGx5KGNvbnRleHQsIGFyZ3VtZW50cyk7XHJcblx0XHRcdFx0c2V0VGltZW91dChsYXRlciwgdGltZSk7XHJcblx0XHRcdFx0bG9jayA9IHRydWU7XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblxyXG5cdFx0cmV0dXJuIHdyYXBwZXJGbjtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gd3JhcE51bShudW06IE51bWJlciwgcmFuZ2U6IE51bWJlcltdLCBpbmNsdWRlTWF4PzogQm9vbGVhbik6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIG51bWJlciBgbnVtYCBtb2R1bG8gYHJhbmdlYCBpbiBzdWNoIGEgd2F5IHNvIGl0IGxpZXMgd2l0aGluXHJcblx0Ly8gYHJhbmdlWzBdYCBhbmQgYHJhbmdlWzFdYC4gVGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgYWx3YXlzIHNtYWxsZXIgdGhhblxyXG5cdC8vIGByYW5nZVsxXWAgdW5sZXNzIGBpbmNsdWRlTWF4YCBpcyBzZXQgdG8gYHRydWVgLlxyXG5cdHdyYXBOdW06IGZ1bmN0aW9uICh4LCByYW5nZSwgaW5jbHVkZU1heCkge1xyXG5cdFx0dmFyIG1heCA9IHJhbmdlWzFdLFxyXG5cdFx0ICAgIG1pbiA9IHJhbmdlWzBdLFxyXG5cdFx0ICAgIGQgPSBtYXggLSBtaW47XHJcblx0XHRyZXR1cm4geCA9PT0gbWF4ICYmIGluY2x1ZGVNYXggPyB4IDogKCh4IC0gbWluKSAlIGQgKyBkKSAlIGQgKyBtaW47XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGZhbHNlRm4oKTogRnVuY3Rpb25cclxuXHQvLyBSZXR1cm5zIGEgZnVuY3Rpb24gd2hpY2ggYWx3YXlzIHJldHVybnMgYGZhbHNlYC5cclxuXHRmYWxzZUZuOiBmdW5jdGlvbiAoKSB7IHJldHVybiBmYWxzZTsgfSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGZvcm1hdE51bShudW06IE51bWJlciwgZGlnaXRzPzogTnVtYmVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbnVtYmVyIGBudW1gIHJvdW5kZWQgdG8gYGRpZ2l0c2AgZGVjaW1hbHMsIG9yIHRvIDUgZGVjaW1hbHMgYnkgZGVmYXVsdC5cclxuXHRmb3JtYXROdW06IGZ1bmN0aW9uIChudW0sIGRpZ2l0cykge1xyXG5cdFx0dmFyIHBvdyA9IE1hdGgucG93KDEwLCBkaWdpdHMgfHwgNSk7XHJcblx0XHRyZXR1cm4gTWF0aC5yb3VuZChudW0gKiBwb3cpIC8gcG93O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0cmltKHN0cjogU3RyaW5nKTogU3RyaW5nXHJcblx0Ly8gQ29tcGF0aWJpbGl0eSBwb2x5ZmlsbCBmb3IgW1N0cmluZy5wcm90b3R5cGUudHJpbV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvU3RyaW5nL1RyaW0pXHJcblx0dHJpbTogZnVuY3Rpb24gKHN0cikge1xyXG5cdFx0cmV0dXJuIHN0ci50cmltID8gc3RyLnRyaW0oKSA6IHN0ci5yZXBsYWNlKC9eXFxzK3xcXHMrJC9nLCAnJyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNwbGl0V29yZHMoc3RyOiBTdHJpbmcpOiBTdHJpbmdbXVxyXG5cdC8vIFRyaW1zIGFuZCBzcGxpdHMgdGhlIHN0cmluZyBvbiB3aGl0ZXNwYWNlIGFuZCByZXR1cm5zIHRoZSBhcnJheSBvZiBwYXJ0cy5cclxuXHRzcGxpdFdvcmRzOiBmdW5jdGlvbiAoc3RyKSB7XHJcblx0XHRyZXR1cm4gTC5VdGlsLnRyaW0oc3RyKS5zcGxpdCgvXFxzKy8pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRPcHRpb25zKG9iajogT2JqZWN0LCBvcHRpb25zOiBPYmplY3QpOiBPYmplY3RcclxuXHQvLyBNZXJnZXMgdGhlIGdpdmVuIHByb3BlcnRpZXMgdG8gdGhlIGBvcHRpb25zYCBvZiB0aGUgYG9iamAgb2JqZWN0LCByZXR1cm5pbmcgdGhlIHJlc3VsdGluZyBvcHRpb25zLiBTZWUgYENsYXNzIG9wdGlvbnNgLiBIYXMgYW4gYEwuc2V0T3B0aW9uc2Agc2hvcnRjdXQuXHJcblx0c2V0T3B0aW9uczogZnVuY3Rpb24gKG9iaiwgb3B0aW9ucykge1xyXG5cdFx0aWYgKCFvYmouaGFzT3duUHJvcGVydHkoJ29wdGlvbnMnKSkge1xyXG5cdFx0XHRvYmoub3B0aW9ucyA9IG9iai5vcHRpb25zID8gTC5VdGlsLmNyZWF0ZShvYmoub3B0aW9ucykgOiB7fTtcclxuXHRcdH1cclxuXHRcdGZvciAodmFyIGkgaW4gb3B0aW9ucykge1xyXG5cdFx0XHRvYmoub3B0aW9uc1tpXSA9IG9wdGlvbnNbaV07XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gb2JqLm9wdGlvbnM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldFBhcmFtU3RyaW5nKG9iajogT2JqZWN0LCBleGlzdGluZ1VybD86IFN0cmluZywgdXBwZXJjYXNlPzogQm9vbGVhbik6IFN0cmluZ1xyXG5cdC8vIENvbnZlcnRzIGFuIG9iamVjdCBpbnRvIGEgcGFyYW1ldGVyIFVSTCBzdHJpbmcsIGUuZy4gYHthOiBcImZvb1wiLCBiOiBcImJhclwifWBcclxuXHQvLyB0cmFuc2xhdGVzIHRvIGAnP2E9Zm9vJmI9YmFyJ2AuIElmIGBleGlzdGluZ1VybGAgaXMgc2V0LCB0aGUgcGFyYW1ldGVycyB3aWxsXHJcblx0Ly8gYmUgYXBwZW5kZWQgYXQgdGhlIGVuZC4gSWYgYHVwcGVyY2FzZWAgaXMgYHRydWVgLCB0aGUgcGFyYW1ldGVyIG5hbWVzIHdpbGxcclxuXHQvLyBiZSB1cHBlcmNhc2VkIChlLmcuIGAnP0E9Zm9vJkI9YmFyJ2ApXHJcblx0Z2V0UGFyYW1TdHJpbmc6IGZ1bmN0aW9uIChvYmosIGV4aXN0aW5nVXJsLCB1cHBlcmNhc2UpIHtcclxuXHRcdHZhciBwYXJhbXMgPSBbXTtcclxuXHRcdGZvciAodmFyIGkgaW4gb2JqKSB7XHJcblx0XHRcdHBhcmFtcy5wdXNoKGVuY29kZVVSSUNvbXBvbmVudCh1cHBlcmNhc2UgPyBpLnRvVXBwZXJDYXNlKCkgOiBpKSArICc9JyArIGVuY29kZVVSSUNvbXBvbmVudChvYmpbaV0pKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiAoKCFleGlzdGluZ1VybCB8fCBleGlzdGluZ1VybC5pbmRleE9mKCc/JykgPT09IC0xKSA/ICc/JyA6ICcmJykgKyBwYXJhbXMuam9pbignJicpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0ZW1wbGF0ZShzdHI6IFN0cmluZywgZGF0YTogT2JqZWN0KTogU3RyaW5nXHJcblx0Ly8gU2ltcGxlIHRlbXBsYXRpbmcgZmFjaWxpdHksIGFjY2VwdHMgYSB0ZW1wbGF0ZSBzdHJpbmcgb2YgdGhlIGZvcm0gYCdIZWxsbyB7YX0sIHtifSdgXHJcblx0Ly8gYW5kIGEgZGF0YSBvYmplY3QgbGlrZSBge2E6ICdmb28nLCBiOiAnYmFyJ31gLCByZXR1cm5zIGV2YWx1YXRlZCBzdHJpbmdcclxuXHQvLyBgKCdIZWxsbyBmb28sIGJhcicpYC4gWW91IGNhbiBhbHNvIHNwZWNpZnkgZnVuY3Rpb25zIGluc3RlYWQgb2Ygc3RyaW5ncyBmb3JcclxuXHQvLyBkYXRhIHZhbHVlcyDigJQgdGhleSB3aWxsIGJlIGV2YWx1YXRlZCBwYXNzaW5nIGBkYXRhYCBhcyBhbiBhcmd1bWVudC5cclxuXHR0ZW1wbGF0ZTogZnVuY3Rpb24gKHN0ciwgZGF0YSkge1xyXG5cdFx0cmV0dXJuIHN0ci5yZXBsYWNlKEwuVXRpbC50ZW1wbGF0ZVJlLCBmdW5jdGlvbiAoc3RyLCBrZXkpIHtcclxuXHRcdFx0dmFyIHZhbHVlID0gZGF0YVtrZXldO1xyXG5cclxuXHRcdFx0aWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ05vIHZhbHVlIHByb3ZpZGVkIGZvciB2YXJpYWJsZSAnICsgc3RyKTtcclxuXHJcblx0XHRcdH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nKSB7XHJcblx0XHRcdFx0dmFsdWUgPSB2YWx1ZShkYXRhKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gdmFsdWU7XHJcblx0XHR9KTtcclxuXHR9LFxyXG5cclxuXHR0ZW1wbGF0ZVJlOiAvXFx7ICooW1xcd19cXC1dKykgKlxcfS9nLFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gaXNBcnJheShvYmopOiBCb29sZWFuXHJcblx0Ly8gQ29tcGF0aWJpbGl0eSBwb2x5ZmlsbCBmb3IgW0FycmF5LmlzQXJyYXldKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2lzQXJyYXkpXHJcblx0aXNBcnJheTogQXJyYXkuaXNBcnJheSB8fCBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHRyZXR1cm4gKE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvYmopID09PSAnW29iamVjdCBBcnJheV0nKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gaW5kZXhPZihhcnJheTogQXJyYXksIGVsOiBPYmplY3QpOiBOdW1iZXJcclxuXHQvLyBDb21wYXRpYmlsaXR5IHBvbHlmaWxsIGZvciBbQXJyYXkucHJvdG90eXBlLmluZGV4T2ZdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L2luZGV4T2YpXHJcblx0aW5kZXhPZjogZnVuY3Rpb24gKGFycmF5LCBlbCkge1xyXG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7IGkrKykge1xyXG5cdFx0XHRpZiAoYXJyYXlbaV0gPT09IGVsKSB7IHJldHVybiBpOyB9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gLTE7XHJcblx0fSxcclxuXHJcblx0Ly8gQHByb3BlcnR5IGVtcHR5SW1hZ2VVcmw6IFN0cmluZ1xyXG5cdC8vIERhdGEgVVJJIHN0cmluZyBjb250YWluaW5nIGEgYmFzZTY0LWVuY29kZWQgZW1wdHkgR0lGIGltYWdlLlxyXG5cdC8vIFVzZWQgYXMgYSBoYWNrIHRvIGZyZWUgbWVtb3J5IGZyb20gdW51c2VkIGltYWdlcyBvbiBXZWJLaXQtcG93ZXJlZFxyXG5cdC8vIG1vYmlsZSBkZXZpY2VzIChieSBzZXR0aW5nIGltYWdlIGBzcmNgIHRvIHRoaXMgc3RyaW5nKS5cclxuXHRlbXB0eUltYWdlVXJsOiAnZGF0YTppbWFnZS9naWY7YmFzZTY0LFIwbEdPRGxoQVFBQkFBRC9BQ3dBQUFBQUFRQUJBQUFDQURzPSdcclxufTtcclxuXHJcbihmdW5jdGlvbiAoKSB7XHJcblx0Ly8gaW5zcGlyZWQgYnkgaHR0cDovL3BhdWxpcmlzaC5jb20vMjAxMS9yZXF1ZXN0YW5pbWF0aW9uZnJhbWUtZm9yLXNtYXJ0LWFuaW1hdGluZy9cclxuXHJcblx0ZnVuY3Rpb24gZ2V0UHJlZml4ZWQobmFtZSkge1xyXG5cdFx0cmV0dXJuIHdpbmRvd1snd2Via2l0JyArIG5hbWVdIHx8IHdpbmRvd1snbW96JyArIG5hbWVdIHx8IHdpbmRvd1snbXMnICsgbmFtZV07XHJcblx0fVxyXG5cclxuXHR2YXIgbGFzdFRpbWUgPSAwO1xyXG5cclxuXHQvLyBmYWxsYmFjayBmb3IgSUUgNy04XHJcblx0ZnVuY3Rpb24gdGltZW91dERlZmVyKGZuKSB7XHJcblx0XHR2YXIgdGltZSA9ICtuZXcgRGF0ZSgpLFxyXG5cdFx0ICAgIHRpbWVUb0NhbGwgPSBNYXRoLm1heCgwLCAxNiAtICh0aW1lIC0gbGFzdFRpbWUpKTtcclxuXHJcblx0XHRsYXN0VGltZSA9IHRpbWUgKyB0aW1lVG9DYWxsO1xyXG5cdFx0cmV0dXJuIHdpbmRvdy5zZXRUaW1lb3V0KGZuLCB0aW1lVG9DYWxsKTtcclxuXHR9XHJcblxyXG5cdHZhciByZXF1ZXN0Rm4gPSB3aW5kb3cucmVxdWVzdEFuaW1hdGlvbkZyYW1lIHx8IGdldFByZWZpeGVkKCdSZXF1ZXN0QW5pbWF0aW9uRnJhbWUnKSB8fCB0aW1lb3V0RGVmZXIsXHJcblx0ICAgIGNhbmNlbEZuID0gd2luZG93LmNhbmNlbEFuaW1hdGlvbkZyYW1lIHx8IGdldFByZWZpeGVkKCdDYW5jZWxBbmltYXRpb25GcmFtZScpIHx8XHJcblx0ICAgICAgICAgICAgICAgZ2V0UHJlZml4ZWQoJ0NhbmNlbFJlcXVlc3RBbmltYXRpb25GcmFtZScpIHx8IGZ1bmN0aW9uIChpZCkgeyB3aW5kb3cuY2xlYXJUaW1lb3V0KGlkKTsgfTtcclxuXHJcblxyXG5cdC8vIEBmdW5jdGlvbiByZXF1ZXN0QW5pbUZyYW1lKGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCwgaW1tZWRpYXRlPzogQm9vbGVhbik6IE51bWJlclxyXG5cdC8vIFNjaGVkdWxlcyBgZm5gIHRvIGJlIGV4ZWN1dGVkIHdoZW4gdGhlIGJyb3dzZXIgcmVwYWludHMuIGBmbmAgaXMgYm91bmQgdG9cclxuXHQvLyBgY29udGV4dGAgaWYgZ2l2ZW4uIFdoZW4gYGltbWVkaWF0ZWAgaXMgc2V0LCBgZm5gIGlzIGNhbGxlZCBpbW1lZGlhdGVseSBpZlxyXG5cdC8vIHRoZSBicm93c2VyIGRvZXNuJ3QgaGF2ZSBuYXRpdmUgc3VwcG9ydCBmb3JcclxuXHQvLyBbYHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWVgXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvd2luZG93L3JlcXVlc3RBbmltYXRpb25GcmFtZSksXHJcblx0Ly8gb3RoZXJ3aXNlIGl0J3MgZGVsYXllZC4gUmV0dXJucyBhIHJlcXVlc3QgSUQgdGhhdCBjYW4gYmUgdXNlZCB0byBjYW5jZWwgdGhlIHJlcXVlc3QuXHJcblx0TC5VdGlsLnJlcXVlc3RBbmltRnJhbWUgPSBmdW5jdGlvbiAoZm4sIGNvbnRleHQsIGltbWVkaWF0ZSkge1xyXG5cdFx0aWYgKGltbWVkaWF0ZSAmJiByZXF1ZXN0Rm4gPT09IHRpbWVvdXREZWZlcikge1xyXG5cdFx0XHRmbi5jYWxsKGNvbnRleHQpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0cmV0dXJuIHJlcXVlc3RGbi5jYWxsKHdpbmRvdywgTC5iaW5kKGZuLCBjb250ZXh0KSk7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGNhbmNlbEFuaW1GcmFtZShpZDogTnVtYmVyKTogdW5kZWZpbmVkXHJcblx0Ly8gQ2FuY2VscyBhIHByZXZpb3VzIGByZXF1ZXN0QW5pbUZyYW1lYC4gU2VlIGFsc28gW3dpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL3dpbmRvdy9jYW5jZWxBbmltYXRpb25GcmFtZSkuXHJcblx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSA9IGZ1bmN0aW9uIChpZCkge1xyXG5cdFx0aWYgKGlkKSB7XHJcblx0XHRcdGNhbmNlbEZuLmNhbGwod2luZG93LCBpZCk7XHJcblx0XHR9XHJcblx0fTtcclxufSkoKTtcclxuXHJcbi8vIHNob3J0Y3V0cyBmb3IgbW9zdCB1c2VkIHV0aWxpdHkgZnVuY3Rpb25zXHJcbkwuZXh0ZW5kID0gTC5VdGlsLmV4dGVuZDtcclxuTC5iaW5kID0gTC5VdGlsLmJpbmQ7XHJcbkwuc3RhbXAgPSBMLlV0aWwuc3RhbXA7XHJcbkwuc2V0T3B0aW9ucyA9IEwuVXRpbC5zZXRPcHRpb25zO1xyXG5cblxuXG5cclxuLy8gQGNsYXNzIENsYXNzXHJcbi8vIEBha2EgTC5DbGFzc1xyXG5cclxuLy8gQHNlY3Rpb25cclxuLy8gQHVuaW5oZXJpdGFibGVcclxuXHJcbi8vIFRoYW5rcyB0byBKb2huIFJlc2lnIGFuZCBEZWFuIEVkd2FyZHMgZm9yIGluc3BpcmF0aW9uIVxyXG5cclxuTC5DbGFzcyA9IGZ1bmN0aW9uICgpIHt9O1xyXG5cclxuTC5DbGFzcy5leHRlbmQgPSBmdW5jdGlvbiAocHJvcHMpIHtcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGV4dGVuZChwcm9wczogT2JqZWN0KTogRnVuY3Rpb25cclxuXHQvLyBbRXh0ZW5kcyB0aGUgY3VycmVudCBjbGFzc10oI2NsYXNzLWluaGVyaXRhbmNlKSBnaXZlbiB0aGUgcHJvcGVydGllcyB0byBiZSBpbmNsdWRlZC5cclxuXHQvLyBSZXR1cm5zIGEgSmF2YXNjcmlwdCBmdW5jdGlvbiB0aGF0IGlzIGEgY2xhc3MgY29uc3RydWN0b3IgKHRvIGJlIGNhbGxlZCB3aXRoIGBuZXdgKS5cclxuXHR2YXIgTmV3Q2xhc3MgPSBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0Ly8gY2FsbCB0aGUgY29uc3RydWN0b3JcclxuXHRcdGlmICh0aGlzLmluaXRpYWxpemUpIHtcclxuXHRcdFx0dGhpcy5pbml0aWFsaXplLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gY2FsbCBhbGwgY29uc3RydWN0b3IgaG9va3NcclxuXHRcdHRoaXMuY2FsbEluaXRIb29rcygpO1xyXG5cdH07XHJcblxyXG5cdHZhciBwYXJlbnRQcm90byA9IE5ld0NsYXNzLl9fc3VwZXJfXyA9IHRoaXMucHJvdG90eXBlO1xyXG5cclxuXHR2YXIgcHJvdG8gPSBMLlV0aWwuY3JlYXRlKHBhcmVudFByb3RvKTtcclxuXHRwcm90by5jb25zdHJ1Y3RvciA9IE5ld0NsYXNzO1xyXG5cclxuXHROZXdDbGFzcy5wcm90b3R5cGUgPSBwcm90bztcclxuXHJcblx0Ly8gaW5oZXJpdCBwYXJlbnQncyBzdGF0aWNzXHJcblx0Zm9yICh2YXIgaSBpbiB0aGlzKSB7XHJcblx0XHRpZiAodGhpcy5oYXNPd25Qcm9wZXJ0eShpKSAmJiBpICE9PSAncHJvdG90eXBlJykge1xyXG5cdFx0XHROZXdDbGFzc1tpXSA9IHRoaXNbaV07XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHQvLyBtaXggc3RhdGljIHByb3BlcnRpZXMgaW50byB0aGUgY2xhc3NcclxuXHRpZiAocHJvcHMuc3RhdGljcykge1xyXG5cdFx0TC5leHRlbmQoTmV3Q2xhc3MsIHByb3BzLnN0YXRpY3MpO1xyXG5cdFx0ZGVsZXRlIHByb3BzLnN0YXRpY3M7XHJcblx0fVxyXG5cclxuXHQvLyBtaXggaW5jbHVkZXMgaW50byB0aGUgcHJvdG90eXBlXHJcblx0aWYgKHByb3BzLmluY2x1ZGVzKSB7XHJcblx0XHRMLlV0aWwuZXh0ZW5kLmFwcGx5KG51bGwsIFtwcm90b10uY29uY2F0KHByb3BzLmluY2x1ZGVzKSk7XHJcblx0XHRkZWxldGUgcHJvcHMuaW5jbHVkZXM7XHJcblx0fVxyXG5cclxuXHQvLyBtZXJnZSBvcHRpb25zXHJcblx0aWYgKHByb3RvLm9wdGlvbnMpIHtcclxuXHRcdHByb3BzLm9wdGlvbnMgPSBMLlV0aWwuZXh0ZW5kKEwuVXRpbC5jcmVhdGUocHJvdG8ub3B0aW9ucyksIHByb3BzLm9wdGlvbnMpO1xyXG5cdH1cclxuXHJcblx0Ly8gbWl4IGdpdmVuIHByb3BlcnRpZXMgaW50byB0aGUgcHJvdG90eXBlXHJcblx0TC5leHRlbmQocHJvdG8sIHByb3BzKTtcclxuXHJcblx0cHJvdG8uX2luaXRIb29rcyA9IFtdO1xyXG5cclxuXHQvLyBhZGQgbWV0aG9kIGZvciBjYWxsaW5nIGFsbCBob29rc1xyXG5cdHByb3RvLmNhbGxJbml0SG9va3MgPSBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2luaXRIb29rc0NhbGxlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAocGFyZW50UHJvdG8uY2FsbEluaXRIb29rcykge1xyXG5cdFx0XHRwYXJlbnRQcm90by5jYWxsSW5pdEhvb2tzLmNhbGwodGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdEhvb2tzQ2FsbGVkID0gdHJ1ZTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gcHJvdG8uX2luaXRIb29rcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRwcm90by5faW5pdEhvb2tzW2ldLmNhbGwodGhpcyk7XHJcblx0XHR9XHJcblx0fTtcclxuXHJcblx0cmV0dXJuIE5ld0NsYXNzO1xyXG59O1xyXG5cclxuXHJcbi8vIEBmdW5jdGlvbiBpbmNsdWRlKHByb3BlcnRpZXM6IE9iamVjdCk6IHRoaXNcclxuLy8gW0luY2x1ZGVzIGEgbWl4aW5dKCNjbGFzcy1pbmNsdWRlcykgaW50byB0aGUgY3VycmVudCBjbGFzcy5cclxuTC5DbGFzcy5pbmNsdWRlID0gZnVuY3Rpb24gKHByb3BzKSB7XHJcblx0TC5leHRlbmQodGhpcy5wcm90b3R5cGUsIHByb3BzKTtcclxuXHRyZXR1cm4gdGhpcztcclxufTtcclxuXHJcbi8vIEBmdW5jdGlvbiBtZXJnZU9wdGlvbnMob3B0aW9uczogT2JqZWN0KTogdGhpc1xyXG4vLyBbTWVyZ2VzIGBvcHRpb25zYF0oI2NsYXNzLW9wdGlvbnMpIGludG8gdGhlIGRlZmF1bHRzIG9mIHRoZSBjbGFzcy5cclxuTC5DbGFzcy5tZXJnZU9wdGlvbnMgPSBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdEwuZXh0ZW5kKHRoaXMucHJvdG90eXBlLm9wdGlvbnMsIG9wdGlvbnMpO1xyXG5cdHJldHVybiB0aGlzO1xyXG59O1xyXG5cclxuLy8gQGZ1bmN0aW9uIGFkZEluaXRIb29rKGZuOiBGdW5jdGlvbik6IHRoaXNcclxuLy8gQWRkcyBhIFtjb25zdHJ1Y3RvciBob29rXSgjY2xhc3MtY29uc3RydWN0b3ItaG9va3MpIHRvIHRoZSBjbGFzcy5cclxuTC5DbGFzcy5hZGRJbml0SG9vayA9IGZ1bmN0aW9uIChmbikgeyAvLyAoRnVuY3Rpb24pIHx8IChTdHJpbmcsIGFyZ3MuLi4pXHJcblx0dmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMsIDEpO1xyXG5cclxuXHR2YXIgaW5pdCA9IHR5cGVvZiBmbiA9PT0gJ2Z1bmN0aW9uJyA/IGZuIDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpc1tmbl0uYXBwbHkodGhpcywgYXJncyk7XHJcblx0fTtcclxuXHJcblx0dGhpcy5wcm90b3R5cGUuX2luaXRIb29rcyA9IHRoaXMucHJvdG90eXBlLl9pbml0SG9va3MgfHwgW107XHJcblx0dGhpcy5wcm90b3R5cGUuX2luaXRIb29rcy5wdXNoKGluaXQpO1xyXG5cdHJldHVybiB0aGlzO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgRXZlbnRlZFxyXG4gKiBAYWthIEwuRXZlbnRlZFxyXG4gKiBAaW5oZXJpdHMgQ2xhc3NcclxuICpcclxuICogQSBzZXQgb2YgbWV0aG9kcyBzaGFyZWQgYmV0d2VlbiBldmVudC1wb3dlcmVkIGNsYXNzZXMgKGxpa2UgYE1hcGAgYW5kIGBNYXJrZXJgKS4gR2VuZXJhbGx5LCBldmVudHMgYWxsb3cgeW91IHRvIGV4ZWN1dGUgc29tZSBmdW5jdGlvbiB3aGVuIHNvbWV0aGluZyBoYXBwZW5zIHdpdGggYW4gb2JqZWN0IChlLmcuIHRoZSB1c2VyIGNsaWNrcyBvbiB0aGUgbWFwLCBjYXVzaW5nIHRoZSBtYXAgdG8gZmlyZSBgJ2NsaWNrJ2AgZXZlbnQpLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBtYXAub24oJ2NsaWNrJywgZnVuY3Rpb24oZSkge1xyXG4gKiBcdGFsZXJ0KGUubGF0bG5nKTtcclxuICogfSApO1xyXG4gKiBgYGBcclxuICpcclxuICogTGVhZmxldCBkZWFscyB3aXRoIGV2ZW50IGxpc3RlbmVycyBieSByZWZlcmVuY2UsIHNvIGlmIHlvdSB3YW50IHRvIGFkZCBhIGxpc3RlbmVyIGFuZCB0aGVuIHJlbW92ZSBpdCwgZGVmaW5lIGl0IGFzIGEgZnVuY3Rpb246XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIGZ1bmN0aW9uIG9uQ2xpY2soZSkgeyAuLi4gfVxyXG4gKlxyXG4gKiBtYXAub24oJ2NsaWNrJywgb25DbGljayk7XHJcbiAqIG1hcC5vZmYoJ2NsaWNrJywgb25DbGljayk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcblxyXG5MLkV2ZW50ZWQgPSBMLkNsYXNzLmV4dGVuZCh7XHJcblxyXG5cdC8qIEBtZXRob2Qgb24odHlwZTogU3RyaW5nLCBmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0ICogQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIChgZm5gKSB0byBhIHBhcnRpY3VsYXIgZXZlbnQgdHlwZSBvZiB0aGUgb2JqZWN0LiBZb3UgY2FuIG9wdGlvbmFsbHkgc3BlY2lmeSB0aGUgY29udGV4dCBvZiB0aGUgbGlzdGVuZXIgKG9iamVjdCB0aGUgdGhpcyBrZXl3b3JkIHdpbGwgcG9pbnQgdG8pLiBZb3UgY2FuIGFsc28gcGFzcyBzZXZlcmFsIHNwYWNlLXNlcGFyYXRlZCB0eXBlcyAoZS5nLiBgJ2NsaWNrIGRibGNsaWNrJ2ApLlxyXG5cdCAqXHJcblx0ICogQGFsdGVybmF0aXZlXHJcblx0ICogQG1ldGhvZCBvbihldmVudE1hcDogT2JqZWN0KTogdGhpc1xyXG5cdCAqIEFkZHMgYSBzZXQgb2YgdHlwZS9saXN0ZW5lciBwYWlycywgZS5nLiBge2NsaWNrOiBvbkNsaWNrLCBtb3VzZW1vdmU6IG9uTW91c2VNb3ZlfWBcclxuXHQgKi9cclxuXHRvbjogZnVuY3Rpb24gKHR5cGVzLCBmbiwgY29udGV4dCkge1xyXG5cclxuXHRcdC8vIHR5cGVzIGNhbiBiZSBhIG1hcCBvZiB0eXBlcy9oYW5kbGVyc1xyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdC8vIHdlIGRvbid0IHByb2Nlc3Mgc3BhY2Utc2VwYXJhdGVkIGV2ZW50cyBoZXJlIGZvciBwZXJmb3JtYW5jZTtcclxuXHRcdFx0XHQvLyBpdCdzIGEgaG90IHBhdGggc2luY2UgTGF5ZXIgdXNlcyB0aGUgb24ob2JqKSBzeW50YXhcclxuXHRcdFx0XHR0aGlzLl9vbih0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0Ly8gdHlwZXMgY2FuIGJlIGEgc3RyaW5nIG9mIHNwYWNlLXNlcGFyYXRlZCB3b3Jkc1xyXG5cdFx0XHR0eXBlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0eXBlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuX29uKHR5cGVzW2ldLCBmbiwgY29udGV4dCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvKiBAbWV0aG9kIG9mZih0eXBlOiBTdHJpbmcsIGZuPzogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0ICogUmVtb3ZlcyBhIHByZXZpb3VzbHkgYWRkZWQgbGlzdGVuZXIgZnVuY3Rpb24uIElmIG5vIGZ1bmN0aW9uIGlzIHNwZWNpZmllZCwgaXQgd2lsbCByZW1vdmUgYWxsIHRoZSBsaXN0ZW5lcnMgb2YgdGhhdCBwYXJ0aWN1bGFyIGV2ZW50IGZyb20gdGhlIG9iamVjdC4gTm90ZSB0aGF0IGlmIHlvdSBwYXNzZWQgYSBjdXN0b20gY29udGV4dCB0byBgb25gLCB5b3UgbXVzdCBwYXNzIHRoZSBzYW1lIGNvbnRleHQgdG8gYG9mZmAgaW4gb3JkZXIgdG8gcmVtb3ZlIHRoZSBsaXN0ZW5lci5cclxuXHQgKlxyXG5cdCAqIEBhbHRlcm5hdGl2ZVxyXG5cdCAqIEBtZXRob2Qgb2ZmKGV2ZW50TWFwOiBPYmplY3QpOiB0aGlzXHJcblx0ICogUmVtb3ZlcyBhIHNldCBvZiB0eXBlL2xpc3RlbmVyIHBhaXJzLlxyXG5cdCAqXHJcblx0ICogQGFsdGVybmF0aXZlXHJcblx0ICogQG1ldGhvZCBvZmY6IHRoaXNcclxuXHQgKiBSZW1vdmVzIGFsbCBsaXN0ZW5lcnMgdG8gYWxsIGV2ZW50cyBvbiB0aGUgb2JqZWN0LlxyXG5cdCAqL1xyXG5cdG9mZjogZnVuY3Rpb24gKHR5cGVzLCBmbiwgY29udGV4dCkge1xyXG5cclxuXHRcdGlmICghdHlwZXMpIHtcclxuXHRcdFx0Ly8gY2xlYXIgYWxsIGxpc3RlbmVycyBpZiBjYWxsZWQgd2l0aG91dCBhcmd1bWVudHNcclxuXHRcdFx0ZGVsZXRlIHRoaXMuX2V2ZW50cztcclxuXHJcblx0XHR9IGVsc2UgaWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdHRoaXMuX29mZih0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0dHlwZXMgPSBMLlV0aWwuc3BsaXRXb3Jkcyh0eXBlcyk7XHJcblxyXG5cdFx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gdHlwZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHR0aGlzLl9vZmYodHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIGF0dGFjaCBsaXN0ZW5lciAod2l0aG91dCBzeW50YWN0aWMgc3VnYXIgbm93KVxyXG5cdF9vbjogZnVuY3Rpb24gKHR5cGUsIGZuLCBjb250ZXh0KSB7XHJcblx0XHR0aGlzLl9ldmVudHMgPSB0aGlzLl9ldmVudHMgfHwge307XHJcblxyXG5cdFx0LyogZ2V0L2luaXQgbGlzdGVuZXJzIGZvciB0eXBlICovXHJcblx0XHR2YXIgdHlwZUxpc3RlbmVycyA9IHRoaXMuX2V2ZW50c1t0eXBlXTtcclxuXHRcdGlmICghdHlwZUxpc3RlbmVycykge1xyXG5cdFx0XHR0eXBlTGlzdGVuZXJzID0gW107XHJcblx0XHRcdHRoaXMuX2V2ZW50c1t0eXBlXSA9IHR5cGVMaXN0ZW5lcnM7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGNvbnRleHQgPT09IHRoaXMpIHtcclxuXHRcdFx0Ly8gTGVzcyBtZW1vcnkgZm9vdHByaW50LlxyXG5cdFx0XHRjb250ZXh0ID0gdW5kZWZpbmVkO1xyXG5cdFx0fVxyXG5cdFx0dmFyIG5ld0xpc3RlbmVyID0ge2ZuOiBmbiwgY3R4OiBjb250ZXh0fSxcclxuXHRcdCAgICBsaXN0ZW5lcnMgPSB0eXBlTGlzdGVuZXJzO1xyXG5cclxuXHRcdC8vIGNoZWNrIGlmIGZuIGFscmVhZHkgdGhlcmVcclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0aWYgKGxpc3RlbmVyc1tpXS5mbiA9PT0gZm4gJiYgbGlzdGVuZXJzW2ldLmN0eCA9PT0gY29udGV4dCkge1xyXG5cdFx0XHRcdHJldHVybjtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdGxpc3RlbmVycy5wdXNoKG5ld0xpc3RlbmVyKTtcclxuXHR9LFxyXG5cclxuXHRfb2ZmOiBmdW5jdGlvbiAodHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHRcdHZhciBsaXN0ZW5lcnMsXHJcblx0XHQgICAgaSxcclxuXHRcdCAgICBsZW47XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9ldmVudHMpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0bGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cclxuXHRcdGlmICghbGlzdGVuZXJzKSB7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIWZuKSB7XHJcblx0XHRcdC8vIFNldCBhbGwgcmVtb3ZlZCBsaXN0ZW5lcnMgdG8gbm9vcCBzbyB0aGV5IGFyZSBub3QgY2FsbGVkIGlmIHJlbW92ZSBoYXBwZW5zIGluIGZpcmVcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gbGlzdGVuZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0bGlzdGVuZXJzW2ldLmZuID0gTC5VdGlsLmZhbHNlRm47XHJcblx0XHRcdH1cclxuXHRcdFx0Ly8gY2xlYXIgYWxsIGxpc3RlbmVycyBmb3IgYSB0eXBlIGlmIGZ1bmN0aW9uIGlzbid0IHNwZWNpZmllZFxyXG5cdFx0XHRkZWxldGUgdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGNvbnRleHQgPT09IHRoaXMpIHtcclxuXHRcdFx0Y29udGV4dCA9IHVuZGVmaW5lZDtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAobGlzdGVuZXJzKSB7XHJcblxyXG5cdFx0XHQvLyBmaW5kIGZuIGFuZCByZW1vdmUgaXRcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gbGlzdGVuZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dmFyIGwgPSBsaXN0ZW5lcnNbaV07XHJcblx0XHRcdFx0aWYgKGwuY3R4ICE9PSBjb250ZXh0KSB7IGNvbnRpbnVlOyB9XHJcblx0XHRcdFx0aWYgKGwuZm4gPT09IGZuKSB7XHJcblxyXG5cdFx0XHRcdFx0Ly8gc2V0IHRoZSByZW1vdmVkIGxpc3RlbmVyIHRvIG5vb3Agc28gdGhhdCdzIG5vdCBjYWxsZWQgaWYgcmVtb3ZlIGhhcHBlbnMgaW4gZmlyZVxyXG5cdFx0XHRcdFx0bC5mbiA9IEwuVXRpbC5mYWxzZUZuO1xyXG5cclxuXHRcdFx0XHRcdGlmICh0aGlzLl9maXJpbmdDb3VudCkge1xyXG5cdFx0XHRcdFx0XHQvKiBjb3B5IGFycmF5IGluIGNhc2UgZXZlbnRzIGFyZSBiZWluZyBmaXJlZCAqL1xyXG5cdFx0XHRcdFx0XHR0aGlzLl9ldmVudHNbdHlwZV0gPSBsaXN0ZW5lcnMgPSBsaXN0ZW5lcnMuc2xpY2UoKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdGxpc3RlbmVycy5zcGxpY2UoaSwgMSk7XHJcblxyXG5cdFx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZmlyZSh0eXBlOiBTdHJpbmcsIGRhdGE/OiBPYmplY3QsIHByb3BhZ2F0ZT86IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gRmlyZXMgYW4gZXZlbnQgb2YgdGhlIHNwZWNpZmllZCB0eXBlLiBZb3UgY2FuIG9wdGlvbmFsbHkgcHJvdmlkZSBhbiBkYXRhXHJcblx0Ly8gb2JqZWN0IOKAlCB0aGUgZmlyc3QgYXJndW1lbnQgb2YgdGhlIGxpc3RlbmVyIGZ1bmN0aW9uIHdpbGwgY29udGFpbiBpdHNcclxuXHQvLyBwcm9wZXJ0aWVzLiBUaGUgZXZlbnQgY2FuIG9wdGlvbmFsbHkgYmUgcHJvcGFnYXRlZCB0byBldmVudCBwYXJlbnRzLlxyXG5cdGZpcmU6IGZ1bmN0aW9uICh0eXBlLCBkYXRhLCBwcm9wYWdhdGUpIHtcclxuXHRcdGlmICghdGhpcy5saXN0ZW5zKHR5cGUsIHByb3BhZ2F0ZSkpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHR2YXIgZXZlbnQgPSBMLlV0aWwuZXh0ZW5kKHt9LCBkYXRhLCB7dHlwZTogdHlwZSwgdGFyZ2V0OiB0aGlzfSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2V2ZW50cykge1xyXG5cdFx0XHR2YXIgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW3R5cGVdO1xyXG5cclxuXHRcdFx0aWYgKGxpc3RlbmVycykge1xyXG5cdFx0XHRcdHRoaXMuX2ZpcmluZ0NvdW50ID0gKHRoaXMuX2ZpcmluZ0NvdW50ICsgMSkgfHwgMTtcclxuXHRcdFx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGlzdGVuZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0XHR2YXIgbCA9IGxpc3RlbmVyc1tpXTtcclxuXHRcdFx0XHRcdGwuZm4uY2FsbChsLmN0eCB8fCB0aGlzLCBldmVudCk7XHJcblx0XHRcdFx0fVxyXG5cclxuXHRcdFx0XHR0aGlzLl9maXJpbmdDb3VudC0tO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHByb3BhZ2F0ZSkge1xyXG5cdFx0XHQvLyBwcm9wYWdhdGUgdGhlIGV2ZW50IHRvIHBhcmVudHMgKHNldCB3aXRoIGFkZEV2ZW50UGFyZW50KVxyXG5cdFx0XHR0aGlzLl9wcm9wYWdhdGVFdmVudChldmVudCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsaXN0ZW5zKHR5cGU6IFN0cmluZyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBhIHBhcnRpY3VsYXIgZXZlbnQgdHlwZSBoYXMgYW55IGxpc3RlbmVycyBhdHRhY2hlZCB0byBpdC5cclxuXHRsaXN0ZW5zOiBmdW5jdGlvbiAodHlwZSwgcHJvcGFnYXRlKSB7XHJcblx0XHR2YXIgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzICYmIHRoaXMuX2V2ZW50c1t0eXBlXTtcclxuXHRcdGlmIChsaXN0ZW5lcnMgJiYgbGlzdGVuZXJzLmxlbmd0aCkgeyByZXR1cm4gdHJ1ZTsgfVxyXG5cclxuXHRcdGlmIChwcm9wYWdhdGUpIHtcclxuXHRcdFx0Ly8gYWxzbyBjaGVjayBwYXJlbnRzIGZvciBsaXN0ZW5lcnMgaWYgZXZlbnQgcHJvcGFnYXRlc1xyXG5cdFx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9ldmVudFBhcmVudHMpIHtcclxuXHRcdFx0XHRpZiAodGhpcy5fZXZlbnRQYXJlbnRzW2lkXS5saXN0ZW5zKHR5cGUsIHByb3BhZ2F0ZSkpIHsgcmV0dXJuIHRydWU7IH1cclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGZhbHNlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgb25jZSjigKYpOiB0aGlzXHJcblx0Ly8gQmVoYXZlcyBhcyBbYG9uKOKApilgXSgjZXZlbnRlZC1vbiksIGV4Y2VwdCB0aGUgbGlzdGVuZXIgd2lsbCBvbmx5IGdldCBmaXJlZCBvbmNlIGFuZCB0aGVuIHJlbW92ZWQuXHJcblx0b25jZTogZnVuY3Rpb24gKHR5cGVzLCBmbiwgY29udGV4dCkge1xyXG5cclxuXHRcdGlmICh0eXBlb2YgdHlwZXMgPT09ICdvYmplY3QnKSB7XHJcblx0XHRcdGZvciAodmFyIHR5cGUgaW4gdHlwZXMpIHtcclxuXHRcdFx0XHR0aGlzLm9uY2UodHlwZSwgdHlwZXNbdHlwZV0sIGZuKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgaGFuZGxlciA9IEwuYmluZChmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXNcclxuXHRcdFx0ICAgIC5vZmYodHlwZXMsIGZuLCBjb250ZXh0KVxyXG5cdFx0XHQgICAgLm9mZih0eXBlcywgaGFuZGxlciwgY29udGV4dCk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHQvLyBhZGQgYSBsaXN0ZW5lciB0aGF0J3MgZXhlY3V0ZWQgb25jZSBhbmQgcmVtb3ZlZCBhZnRlciB0aGF0XHJcblx0XHRyZXR1cm4gdGhpc1xyXG5cdFx0ICAgIC5vbih0eXBlcywgZm4sIGNvbnRleHQpXHJcblx0XHQgICAgLm9uKHR5cGVzLCBoYW5kbGVyLCBjb250ZXh0KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZEV2ZW50UGFyZW50KG9iajogRXZlbnRlZCk6IHRoaXNcclxuXHQvLyBBZGRzIGFuIGV2ZW50IHBhcmVudCAtIGFuIGBFdmVudGVkYCB0aGF0IHdpbGwgcmVjZWl2ZSBwcm9wYWdhdGVkIGV2ZW50c1xyXG5cdGFkZEV2ZW50UGFyZW50OiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHR0aGlzLl9ldmVudFBhcmVudHMgPSB0aGlzLl9ldmVudFBhcmVudHMgfHwge307XHJcblx0XHR0aGlzLl9ldmVudFBhcmVudHNbTC5zdGFtcChvYmopXSA9IG9iajtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlRXZlbnRQYXJlbnQob2JqOiBFdmVudGVkKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYW4gZXZlbnQgcGFyZW50LCBzbyBpdCB3aWxsIHN0b3AgcmVjZWl2aW5nIHByb3BhZ2F0ZWQgZXZlbnRzXHJcblx0cmVtb3ZlRXZlbnRQYXJlbnQ6IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdGlmICh0aGlzLl9ldmVudFBhcmVudHMpIHtcclxuXHRcdFx0ZGVsZXRlIHRoaXMuX2V2ZW50UGFyZW50c1tMLnN0YW1wKG9iaildO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X3Byb3BhZ2F0ZUV2ZW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fZXZlbnRQYXJlbnRzKSB7XHJcblx0XHRcdHRoaXMuX2V2ZW50UGFyZW50c1tpZF0uZmlyZShlLnR5cGUsIEwuZXh0ZW5kKHtsYXllcjogZS50YXJnZXR9LCBlKSwgdHJ1ZSk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbnZhciBwcm90byA9IEwuRXZlbnRlZC5wcm90b3R5cGU7XHJcblxyXG4vLyBhbGlhc2VzOyB3ZSBzaG91bGQgZGl0Y2ggdGhvc2UgZXZlbnR1YWxseVxyXG5cclxuLy8gQG1ldGhvZCBhZGRFdmVudExpc3RlbmVyKOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BvbijigKYpYF0oI2V2ZW50ZWQtb24pXHJcbnByb3RvLmFkZEV2ZW50TGlzdGVuZXIgPSBwcm90by5vbjtcclxuXHJcbi8vIEBtZXRob2QgcmVtb3ZlRXZlbnRMaXN0ZW5lcijigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgb2ZmKOKApilgXSgjZXZlbnRlZC1vZmYpXHJcblxyXG4vLyBAbWV0aG9kIGNsZWFyQWxsRXZlbnRMaXN0ZW5lcnMo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYG9mZigpYF0oI2V2ZW50ZWQtb2ZmKVxyXG5wcm90by5yZW1vdmVFdmVudExpc3RlbmVyID0gcHJvdG8uY2xlYXJBbGxFdmVudExpc3RlbmVycyA9IHByb3RvLm9mZjtcclxuXHJcbi8vIEBtZXRob2QgYWRkT25lVGltZUV2ZW50TGlzdGVuZXIo4oCmKTogdGhpc1xyXG4vLyBBbGlhcyB0byBbYG9uY2Uo4oCmKWBdKCNldmVudGVkLW9uY2UpXHJcbnByb3RvLmFkZE9uZVRpbWVFdmVudExpc3RlbmVyID0gcHJvdG8ub25jZTtcclxuXHJcbi8vIEBtZXRob2QgZmlyZUV2ZW50KOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BmaXJlKOKApilgXSgjZXZlbnRlZC1maXJlKVxyXG5wcm90by5maXJlRXZlbnQgPSBwcm90by5maXJlO1xyXG5cclxuLy8gQG1ldGhvZCBoYXNFdmVudExpc3RlbmVycyjigKYpOiBCb29sZWFuXHJcbi8vIEFsaWFzIHRvIFtgbGlzdGVucyjigKYpYF0oI2V2ZW50ZWQtbGlzdGVucylcclxucHJvdG8uaGFzRXZlbnRMaXN0ZW5lcnMgPSBwcm90by5saXN0ZW5zO1xyXG5cclxuTC5NaXhpbiA9IHtFdmVudHM6IHByb3RvfTtcclxuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBCcm93c2VyXHJcbiAqIEBha2EgTC5Ccm93c2VyXHJcbiAqXHJcbiAqIEEgbmFtZXNwYWNlIHdpdGggc3RhdGljIHByb3BlcnRpZXMgZm9yIGJyb3dzZXIvZmVhdHVyZSBkZXRlY3Rpb24gdXNlZCBieSBMZWFmbGV0IGludGVybmFsbHkuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIGlmIChMLkJyb3dzZXIuaWVsdDkpIHtcclxuICogICBhbGVydCgnVXBncmFkZSB5b3VyIGJyb3dzZXIsIGR1ZGUhJyk7XHJcbiAqIH1cclxuICogYGBgXHJcbiAqL1xyXG5cclxuKGZ1bmN0aW9uICgpIHtcclxuXHJcblx0dmFyIHVhID0gbmF2aWdhdG9yLnVzZXJBZ2VudC50b0xvd2VyQ2FzZSgpLFxyXG5cdCAgICBkb2MgPSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQsXHJcblxyXG5cdCAgICBpZSA9ICdBY3RpdmVYT2JqZWN0JyBpbiB3aW5kb3csXHJcblxyXG5cdCAgICB3ZWJraXQgICAgPSB1YS5pbmRleE9mKCd3ZWJraXQnKSAhPT0gLTEsXHJcblx0ICAgIHBoYW50b21qcyA9IHVhLmluZGV4T2YoJ3BoYW50b20nKSAhPT0gLTEsXHJcblx0ICAgIGFuZHJvaWQyMyA9IHVhLnNlYXJjaCgnYW5kcm9pZCBbMjNdJykgIT09IC0xLFxyXG5cdCAgICBjaHJvbWUgICAgPSB1YS5pbmRleE9mKCdjaHJvbWUnKSAhPT0gLTEsXHJcblx0ICAgIGdlY2tvICAgICA9IHVhLmluZGV4T2YoJ2dlY2tvJykgIT09IC0xICAmJiAhd2Via2l0ICYmICF3aW5kb3cub3BlcmEgJiYgIWllLFxyXG5cclxuXHQgICAgd2luID0gbmF2aWdhdG9yLnBsYXRmb3JtLmluZGV4T2YoJ1dpbicpID09PSAwLFxyXG5cclxuXHQgICAgbW9iaWxlID0gdHlwZW9mIG9yaWVudGF0aW9uICE9PSAndW5kZWZpbmVkJyB8fCB1YS5pbmRleE9mKCdtb2JpbGUnKSAhPT0gLTEsXHJcblx0ICAgIG1zUG9pbnRlciA9ICF3aW5kb3cuUG9pbnRlckV2ZW50ICYmIHdpbmRvdy5NU1BvaW50ZXJFdmVudCxcclxuXHQgICAgcG9pbnRlciA9IHdpbmRvdy5Qb2ludGVyRXZlbnQgfHwgbXNQb2ludGVyLFxyXG5cclxuXHQgICAgaWUzZCA9IGllICYmICgndHJhbnNpdGlvbicgaW4gZG9jLnN0eWxlKSxcclxuXHQgICAgd2Via2l0M2QgPSAoJ1dlYktpdENTU01hdHJpeCcgaW4gd2luZG93KSAmJiAoJ20xMScgaW4gbmV3IHdpbmRvdy5XZWJLaXRDU1NNYXRyaXgoKSkgJiYgIWFuZHJvaWQyMyxcclxuXHQgICAgZ2Vja28zZCA9ICdNb3pQZXJzcGVjdGl2ZScgaW4gZG9jLnN0eWxlLFxyXG5cdCAgICBvcGVyYTEyID0gJ09UcmFuc2l0aW9uJyBpbiBkb2Muc3R5bGU7XHJcblxyXG5cclxuXHR2YXIgdG91Y2ggPSAhd2luZG93LkxfTk9fVE9VQ0ggJiYgKHBvaW50ZXIgfHwgJ29udG91Y2hzdGFydCcgaW4gd2luZG93IHx8XHJcblx0XHRcdCh3aW5kb3cuRG9jdW1lbnRUb3VjaCAmJiBkb2N1bWVudCBpbnN0YW5jZW9mIHdpbmRvdy5Eb2N1bWVudFRvdWNoKSk7XHJcblxyXG5cdEwuQnJvd3NlciA9IHtcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgaWU6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIEludGVybmV0IEV4cGxvcmVyIHZlcnNpb25zIChub3QgRWRnZSkuXHJcblx0XHRpZTogaWUsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGllbHQ5OiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIEludGVybmV0IEV4cGxvcmVyIHZlcnNpb25zIGxlc3MgdGhhbiA5LlxyXG5cdFx0aWVsdDk6IGllICYmICFkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBlZGdlOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHRoZSBFZGdlIHdlYiBicm93c2VyLlxyXG5cdFx0ZWRnZTogJ21zTGF1bmNoVXJpJyBpbiBuYXZpZ2F0b3IgJiYgISgnZG9jdW1lbnRNb2RlJyBpbiBkb2N1bWVudCksXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHdlYmtpdDogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciB3ZWJraXQtYmFzZWQgYnJvd3NlcnMgbGlrZSBDaHJvbWUgYW5kIFNhZmFyaSAoaW5jbHVkaW5nIG1vYmlsZSB2ZXJzaW9ucykuXHJcblx0XHR3ZWJraXQ6IHdlYmtpdCxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgZ2Vja286IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgZ2Vja28tYmFzZWQgYnJvd3NlcnMgbGlrZSBGaXJlZm94LlxyXG5cdFx0Z2Vja286IGdlY2tvLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBhbmRyb2lkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFueSBicm93c2VyIHJ1bm5pbmcgb24gYW4gQW5kcm9pZCBwbGF0Zm9ybS5cclxuXHRcdGFuZHJvaWQ6IHVhLmluZGV4T2YoJ2FuZHJvaWQnKSAhPT0gLTEsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IGFuZHJvaWQyMzogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBicm93c2VycyBydW5uaW5nIG9uIEFuZHJvaWQgMiBvciBBbmRyb2lkIDMuXHJcblx0XHRhbmRyb2lkMjM6IGFuZHJvaWQyMyxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgY2hyb21lOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHRoZSBDaHJvbWUgYnJvd3Nlci5cclxuXHRcdGNocm9tZTogY2hyb21lLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBzYWZhcmk6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIFNhZmFyaSBicm93c2VyLlxyXG5cdFx0c2FmYXJpOiAhY2hyb21lICYmIHVhLmluZGV4T2YoJ3NhZmFyaScpICE9PSAtMSxcclxuXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHdpbjogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIHdoZW4gdGhlIGJyb3dzZXIgaXMgcnVubmluZyBpbiBhIFdpbmRvd3MgcGxhdGZvcm1cclxuXHRcdHdpbjogd2luLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgaWUzZDogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgSW50ZXJuZXQgRXhwbG9yZXIgdmVyc2lvbnMgc3VwcG9ydGluZyBDU1MgdHJhbnNmb3Jtcy5cclxuXHRcdGllM2Q6IGllM2QsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHdlYmtpdDNkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHdlYmtpdC1iYXNlZCBicm93c2VycyBzdXBwb3J0aW5nIENTUyB0cmFuc2Zvcm1zLlxyXG5cdFx0d2Via2l0M2Q6IHdlYmtpdDNkLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBnZWNrbzNkOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGdlY2tvLWJhc2VkIGJyb3dzZXJzIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcblx0XHRnZWNrbzNkOiBnZWNrbzNkLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBvcGVyYTEyOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIHRoZSBPcGVyYSBicm93c2VyIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMgKHZlcnNpb24gMTIgb3IgbGF0ZXIpLlxyXG5cdFx0b3BlcmExMjogb3BlcmExMixcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgYW55M2Q6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYWxsIGJyb3dzZXJzIHN1cHBvcnRpbmcgQ1NTIHRyYW5zZm9ybXMuXHJcblx0XHRhbnkzZDogIXdpbmRvdy5MX0RJU0FCTEVfM0QgJiYgKGllM2QgfHwgd2Via2l0M2QgfHwgZ2Vja28zZCkgJiYgIW9wZXJhMTIgJiYgIXBoYW50b21qcyxcclxuXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG1vYmlsZTogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgYnJvd3NlcnMgcnVubmluZyBpbiBhIG1vYmlsZSBkZXZpY2UuXHJcblx0XHRtb2JpbGU6IG1vYmlsZSxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbW9iaWxlV2Via2l0OiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCB3ZWJraXQtYmFzZWQgYnJvd3NlcnMgaW4gYSBtb2JpbGUgZGV2aWNlLlxyXG5cdFx0bW9iaWxlV2Via2l0OiBtb2JpbGUgJiYgd2Via2l0LFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtb2JpbGVXZWJraXQzZDogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgd2Via2l0LWJhc2VkIGJyb3dzZXJzIGluIGEgbW9iaWxlIGRldmljZSBzdXBwb3J0aW5nIENTUyB0cmFuc2Zvcm1zLlxyXG5cdFx0bW9iaWxlV2Via2l0M2Q6IG1vYmlsZSAmJiB3ZWJraXQzZCxcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbW9iaWxlT3BlcmE6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgdGhlIE9wZXJhIGJyb3dzZXIgaW4gYSBtb2JpbGUgZGV2aWNlLlxyXG5cdFx0bW9iaWxlT3BlcmE6IG1vYmlsZSAmJiB3aW5kb3cub3BlcmEsXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IG1vYmlsZUdlY2tvOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGdlY2tvLWJhc2VkIGJyb3dzZXJzIHJ1bm5pbmcgaW4gYSBtb2JpbGUgZGV2aWNlLlxyXG5cdFx0bW9iaWxlR2Vja286IG1vYmlsZSAmJiBnZWNrbyxcclxuXHJcblxyXG5cdFx0Ly8gQHByb3BlcnR5IHRvdWNoOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGFsbCBicm93c2VycyBzdXBwb3J0aW5nIFt0b3VjaCBldmVudHNdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9Ub3VjaF9ldmVudHMpLlxyXG5cdFx0Ly8gVGhpcyBkb2VzIG5vdCBuZWNlc3NhcmlseSBtZWFuIHRoYXQgdGhlIGJyb3dzZXIgaXMgcnVubmluZyBpbiBhIGNvbXB1dGVyIHdpdGhcclxuXHRcdC8vIGEgdG91Y2hzY3JlZW4sIGl0IG9ubHkgbWVhbnMgdGhhdCB0aGUgYnJvd3NlciBpcyBjYXBhYmxlIG9mIHVuZGVyc3RhbmRpbmdcclxuXHRcdC8vIHRvdWNoIGV2ZW50cy5cclxuXHRcdHRvdWNoOiAhIXRvdWNoLFxyXG5cclxuXHRcdC8vIEBwcm9wZXJ0eSBtc1BvaW50ZXI6IEJvb2xlYW5cclxuXHRcdC8vIGB0cnVlYCBmb3IgYnJvd3NlcnMgaW1wbGVtZW50aW5nIHRoZSBNaWNyb3NvZnQgdG91Y2ggZXZlbnRzIG1vZGVsIChub3RhYmx5IElFMTApLlxyXG5cdFx0bXNQb2ludGVyOiAhIW1zUG9pbnRlcixcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgcG9pbnRlcjogQm9vbGVhblxyXG5cdFx0Ly8gYHRydWVgIGZvciBhbGwgYnJvd3NlcnMgc3VwcG9ydGluZyBbcG9pbnRlciBldmVudHNdKGh0dHBzOi8vbXNkbi5taWNyb3NvZnQuY29tL2VuLXVzL2xpYnJhcnkvZG40MzMyNDQlMjh2PXZzLjg1JTI5LmFzcHgpLlxyXG5cdFx0cG9pbnRlcjogISFwb2ludGVyLFxyXG5cclxuXHJcblx0XHQvLyBAcHJvcGVydHkgcmV0aW5hOiBCb29sZWFuXHJcblx0XHQvLyBgdHJ1ZWAgZm9yIGJyb3dzZXJzIG9uIGEgaGlnaC1yZXNvbHV0aW9uIFwicmV0aW5hXCIgc2NyZWVuLlxyXG5cdFx0cmV0aW5hOiAod2luZG93LmRldmljZVBpeGVsUmF0aW8gfHwgKHdpbmRvdy5zY3JlZW4uZGV2aWNlWERQSSAvIHdpbmRvdy5zY3JlZW4ubG9naWNhbFhEUEkpKSA+IDFcclxuXHR9O1xyXG5cclxufSgpKTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIFBvaW50XHJcbiAqIEBha2EgTC5Qb2ludFxyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgcG9pbnQgd2l0aCBgeGAgYW5kIGB5YCBjb29yZGluYXRlcyBpbiBwaXhlbHMuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBwb2ludCA9IEwucG9pbnQoMjAwLCAzMDApO1xyXG4gKiBgYGBcclxuICpcclxuICogQWxsIExlYWZsZXQgbWV0aG9kcyBhbmQgb3B0aW9ucyB0aGF0IGFjY2VwdCBgUG9pbnRgIG9iamVjdHMgYWxzbyBhY2NlcHQgdGhlbSBpbiBhIHNpbXBsZSBBcnJheSBmb3JtICh1bmxlc3Mgbm90ZWQgb3RoZXJ3aXNlKSwgc28gdGhlc2UgbGluZXMgYXJlIGVxdWl2YWxlbnQ6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIG1hcC5wYW5CeShbMjAwLCAzMDBdKTtcclxuICogbWFwLnBhbkJ5KEwucG9pbnQoMjAwLCAzMDApKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5Qb2ludCA9IGZ1bmN0aW9uICh4LCB5LCByb3VuZCkge1xyXG5cdC8vIEBwcm9wZXJ0eSB4OiBOdW1iZXI7IFRoZSBgeGAgY29vcmRpbmF0ZSBvZiB0aGUgcG9pbnRcclxuXHR0aGlzLnggPSAocm91bmQgPyBNYXRoLnJvdW5kKHgpIDogeCk7XHJcblx0Ly8gQHByb3BlcnR5IHk6IE51bWJlcjsgVGhlIGB5YCBjb29yZGluYXRlIG9mIHRoZSBwb2ludFxyXG5cdHRoaXMueSA9IChyb3VuZCA/IE1hdGgucm91bmQoeSkgOiB5KTtcclxufTtcclxuXHJcbkwuUG9pbnQucHJvdG90eXBlID0ge1xyXG5cclxuXHQvLyBAbWV0aG9kIGNsb25lKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIGNvcHkgb2YgdGhlIGN1cnJlbnQgcG9pbnQuXHJcblx0Y2xvbmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludCh0aGlzLngsIHRoaXMueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGQob3RoZXJQb2ludDogUG9pbnQpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHJlc3VsdCBvZiBhZGRpdGlvbiBvZiB0aGUgY3VycmVudCBhbmQgdGhlIGdpdmVuIHBvaW50cy5cclxuXHRhZGQ6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0Ly8gbm9uLWRlc3RydWN0aXZlLCByZXR1cm5zIGEgbmV3IHBvaW50XHJcblx0XHRyZXR1cm4gdGhpcy5jbG9uZSgpLl9hZGQoTC5wb2ludChwb2ludCkpO1xyXG5cdH0sXHJcblxyXG5cdF9hZGQ6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0Ly8gZGVzdHJ1Y3RpdmUsIHVzZWQgZGlyZWN0bHkgZm9yIHBlcmZvcm1hbmNlIGluIHNpdHVhdGlvbnMgd2hlcmUgaXQncyBzYWZlIHRvIG1vZGlmeSBleGlzdGluZyBwb2ludFxyXG5cdFx0dGhpcy54ICs9IHBvaW50Lng7XHJcblx0XHR0aGlzLnkgKz0gcG9pbnQueTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc3VidHJhY3Qob3RoZXJQb2ludDogUG9pbnQpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHJlc3VsdCBvZiBzdWJ0cmFjdGlvbiBvZiB0aGUgZ2l2ZW4gcG9pbnQgZnJvbSB0aGUgY3VycmVudC5cclxuXHRzdWJ0cmFjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gdGhpcy5jbG9uZSgpLl9zdWJ0cmFjdChMLnBvaW50KHBvaW50KSk7XHJcblx0fSxcclxuXHJcblx0X3N1YnRyYWN0OiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHRoaXMueCAtPSBwb2ludC54O1xyXG5cdFx0dGhpcy55IC09IHBvaW50Lnk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGRpdmlkZUJ5KG51bTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2YgZGl2aXNpb24gb2YgdGhlIGN1cnJlbnQgcG9pbnQgYnkgdGhlIGdpdmVuIG51bWJlci5cclxuXHRkaXZpZGVCeTogZnVuY3Rpb24gKG51bSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fZGl2aWRlQnkobnVtKTtcclxuXHR9LFxyXG5cclxuXHRfZGl2aWRlQnk6IGZ1bmN0aW9uIChudW0pIHtcclxuXHRcdHRoaXMueCAvPSBudW07XHJcblx0XHR0aGlzLnkgLz0gbnVtO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtdWx0aXBseUJ5KG51bTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSByZXN1bHQgb2YgbXVsdGlwbGljYXRpb24gb2YgdGhlIGN1cnJlbnQgcG9pbnQgYnkgdGhlIGdpdmVuIG51bWJlci5cclxuXHRtdWx0aXBseUJ5OiBmdW5jdGlvbiAobnVtKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5jbG9uZSgpLl9tdWx0aXBseUJ5KG51bSk7XHJcblx0fSxcclxuXHJcblx0X211bHRpcGx5Qnk6IGZ1bmN0aW9uIChudW0pIHtcclxuXHRcdHRoaXMueCAqPSBudW07XHJcblx0XHR0aGlzLnkgKj0gbnVtO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzY2FsZUJ5KHNjYWxlOiBQb2ludCk6IFBvaW50XHJcblx0Ly8gTXVsdGlwbHkgZWFjaCBjb29yZGluYXRlIG9mIHRoZSBjdXJyZW50IHBvaW50IGJ5IGVhY2ggY29vcmRpbmF0ZSBvZlxyXG5cdC8vIGBzY2FsZWAuIEluIGxpbmVhciBhbGdlYnJhIHRlcm1zLCBtdWx0aXBseSB0aGUgcG9pbnQgYnkgdGhlXHJcblx0Ly8gW3NjYWxpbmcgbWF0cml4XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TY2FsaW5nXyUyOGdlb21ldHJ5JTI5I01hdHJpeF9yZXByZXNlbnRhdGlvbilcclxuXHQvLyBkZWZpbmVkIGJ5IGBzY2FsZWAuXHJcblx0c2NhbGVCeTogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy54ICogcG9pbnQueCwgdGhpcy55ICogcG9pbnQueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB1bnNjYWxlQnkoc2NhbGU6IFBvaW50KTogUG9pbnRcclxuXHQvLyBJbnZlcnNlIG9mIGBzY2FsZUJ5YC4gRGl2aWRlIGVhY2ggY29vcmRpbmF0ZSBvZiB0aGUgY3VycmVudCBwb2ludCBieVxyXG5cdC8vIGVhY2ggY29vcmRpbmF0ZSBvZiBgc2NhbGVgLlxyXG5cdHVuc2NhbGVCeTogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy54IC8gcG9pbnQueCwgdGhpcy55IC8gcG9pbnQueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByb3VuZCgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgYSBjb3B5IG9mIHRoZSBjdXJyZW50IHBvaW50IHdpdGggcm91bmRlZCBjb29yZGluYXRlcy5cclxuXHRyb3VuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fcm91bmQoKTtcclxuXHR9LFxyXG5cclxuXHRfcm91bmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMueCA9IE1hdGgucm91bmQodGhpcy54KTtcclxuXHRcdHRoaXMueSA9IE1hdGgucm91bmQodGhpcy55KTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZmxvb3IoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIGEgY29weSBvZiB0aGUgY3VycmVudCBwb2ludCB3aXRoIGZsb29yZWQgY29vcmRpbmF0ZXMgKHJvdW5kZWQgZG93bikuXHJcblx0Zmxvb3I6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNsb25lKCkuX2Zsb29yKCk7XHJcblx0fSxcclxuXHJcblx0X2Zsb29yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnggPSBNYXRoLmZsb29yKHRoaXMueCk7XHJcblx0XHR0aGlzLnkgPSBNYXRoLmZsb29yKHRoaXMueSk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNlaWwoKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIGEgY29weSBvZiB0aGUgY3VycmVudCBwb2ludCB3aXRoIGNlaWxlZCBjb29yZGluYXRlcyAocm91bmRlZCB1cCkuXHJcblx0Y2VpbDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY2xvbmUoKS5fY2VpbCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jZWlsOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnggPSBNYXRoLmNlaWwodGhpcy54KTtcclxuXHRcdHRoaXMueSA9IE1hdGguY2VpbCh0aGlzLnkpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXN0YW5jZVRvKG90aGVyUG9pbnQ6IFBvaW50KTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgY2FydGVzaWFuIGRpc3RhbmNlIGJldHdlZW4gdGhlIGN1cnJlbnQgYW5kIHRoZSBnaXZlbiBwb2ludHMuXHJcblx0ZGlzdGFuY2VUbzogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRwb2ludCA9IEwucG9pbnQocG9pbnQpO1xyXG5cclxuXHRcdHZhciB4ID0gcG9pbnQueCAtIHRoaXMueCxcclxuXHRcdCAgICB5ID0gcG9pbnQueSAtIHRoaXMueTtcclxuXHJcblx0XHRyZXR1cm4gTWF0aC5zcXJ0KHggKiB4ICsgeSAqIHkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZXF1YWxzKG90aGVyUG9pbnQ6IFBvaW50KTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBnaXZlbiBwb2ludCBoYXMgdGhlIHNhbWUgY29vcmRpbmF0ZXMuXHJcblx0ZXF1YWxzOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHBvaW50ID0gTC5wb2ludChwb2ludCk7XHJcblxyXG5cdFx0cmV0dXJuIHBvaW50LnggPT09IHRoaXMueCAmJlxyXG5cdFx0ICAgICAgIHBvaW50LnkgPT09IHRoaXMueTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zKG90aGVyUG9pbnQ6IFBvaW50KTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIGJvdGggY29vcmRpbmF0ZXMgb2YgdGhlIGdpdmVuIHBvaW50IGFyZSBsZXNzIHRoYW4gdGhlIGNvcnJlc3BvbmRpbmcgY3VycmVudCBwb2ludCBjb29yZGluYXRlcyAoaW4gYWJzb2x1dGUgdmFsdWVzKS5cclxuXHRjb250YWluczogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRwb2ludCA9IEwucG9pbnQocG9pbnQpO1xyXG5cclxuXHRcdHJldHVybiBNYXRoLmFicyhwb2ludC54KSA8PSBNYXRoLmFicyh0aGlzLngpICYmXHJcblx0XHQgICAgICAgTWF0aC5hYnMocG9pbnQueSkgPD0gTWF0aC5hYnModGhpcy55KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHRvU3RyaW5nKCk6IFN0cmluZ1xyXG5cdC8vIFJldHVybnMgYSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIHBvaW50IGZvciBkZWJ1Z2dpbmcgcHVycG9zZXMuXHJcblx0dG9TdHJpbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAnUG9pbnQoJyArXHJcblx0XHQgICAgICAgIEwuVXRpbC5mb3JtYXROdW0odGhpcy54KSArICcsICcgK1xyXG5cdFx0ICAgICAgICBMLlV0aWwuZm9ybWF0TnVtKHRoaXMueSkgKyAnKSc7XHJcblx0fVxyXG59O1xyXG5cclxuLy8gQGZhY3RvcnkgTC5wb2ludCh4OiBOdW1iZXIsIHk6IE51bWJlciwgcm91bmQ/OiBCb29sZWFuKVxyXG4vLyBDcmVhdGVzIGEgUG9pbnQgb2JqZWN0IHdpdGggdGhlIGdpdmVuIGB4YCBhbmQgYHlgIGNvb3JkaW5hdGVzLiBJZiBvcHRpb25hbCBgcm91bmRgIGlzIHNldCB0byB0cnVlLCByb3VuZHMgdGhlIGB4YCBhbmQgYHlgIHZhbHVlcy5cclxuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLnBvaW50KGNvb3JkczogTnVtYmVyW10pXHJcbi8vIEV4cGVjdHMgYW4gYXJyYXkgb2YgdGhlIGZvcm0gYFt4LCB5XWAgaW5zdGVhZC5cclxuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLnBvaW50KGNvb3JkczogT2JqZWN0KVxyXG4vLyBFeHBlY3RzIGEgcGxhaW4gb2JqZWN0IG9mIHRoZSBmb3JtIGB7eDogTnVtYmVyLCB5OiBOdW1iZXJ9YCBpbnN0ZWFkLlxyXG5MLnBvaW50ID0gZnVuY3Rpb24gKHgsIHksIHJvdW5kKSB7XHJcblx0aWYgKHggaW5zdGFuY2VvZiBMLlBvaW50KSB7XHJcblx0XHRyZXR1cm4geDtcclxuXHR9XHJcblx0aWYgKEwuVXRpbC5pc0FycmF5KHgpKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoeFswXSwgeFsxXSk7XHJcblx0fVxyXG5cdGlmICh4ID09PSB1bmRlZmluZWQgfHwgeCA9PT0gbnVsbCkge1xyXG5cdFx0cmV0dXJuIHg7XHJcblx0fVxyXG5cdGlmICh0eXBlb2YgeCA9PT0gJ29iamVjdCcgJiYgJ3gnIGluIHggJiYgJ3knIGluIHgpIHtcclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludCh4LngsIHgueSk7XHJcblx0fVxyXG5cdHJldHVybiBuZXcgTC5Qb2ludCh4LCB5LCByb3VuZCk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBCb3VuZHNcclxuICogQGFrYSBMLkJvdW5kc1xyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgcmVjdGFuZ3VsYXIgYXJlYSBpbiBwaXhlbCBjb29yZGluYXRlcy5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIHAxID0gTC5wb2ludCgxMCwgMTApLFxyXG4gKiBwMiA9IEwucG9pbnQoNDAsIDYwKSxcclxuICogYm91bmRzID0gTC5ib3VuZHMocDEsIHAyKTtcclxuICogYGBgXHJcbiAqXHJcbiAqIEFsbCBMZWFmbGV0IG1ldGhvZHMgdGhhdCBhY2NlcHQgYEJvdW5kc2Agb2JqZWN0cyBhbHNvIGFjY2VwdCB0aGVtIGluIGEgc2ltcGxlIEFycmF5IGZvcm0gKHVubGVzcyBub3RlZCBvdGhlcndpc2UpLCBzbyB0aGUgYm91bmRzIGV4YW1wbGUgYWJvdmUgY2FuIGJlIHBhc3NlZCBsaWtlIHRoaXM6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIG90aGVyQm91bmRzLmludGVyc2VjdHMoW1sxMCwgMTBdLCBbNDAsIDYwXV0pO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5MLkJvdW5kcyA9IGZ1bmN0aW9uIChhLCBiKSB7XHJcblx0aWYgKCFhKSB7IHJldHVybjsgfVxyXG5cclxuXHR2YXIgcG9pbnRzID0gYiA/IFthLCBiXSA6IGE7XHJcblxyXG5cdGZvciAodmFyIGkgPSAwLCBsZW4gPSBwb2ludHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdHRoaXMuZXh0ZW5kKHBvaW50c1tpXSk7XHJcblx0fVxyXG59O1xyXG5cclxuTC5Cb3VuZHMucHJvdG90eXBlID0ge1xyXG5cdC8vIEBtZXRob2QgZXh0ZW5kKHBvaW50OiBQb2ludCk6IHRoaXNcclxuXHQvLyBFeHRlbmRzIHRoZSBib3VuZHMgdG8gY29udGFpbiB0aGUgZ2l2ZW4gcG9pbnQuXHJcblx0ZXh0ZW5kOiBmdW5jdGlvbiAocG9pbnQpIHsgLy8gKFBvaW50KVxyXG5cdFx0cG9pbnQgPSBMLnBvaW50KHBvaW50KTtcclxuXHJcblx0XHQvLyBAcHJvcGVydHkgbWluOiBQb2ludFxyXG5cdFx0Ly8gVGhlIHRvcCBsZWZ0IGNvcm5lciBvZiB0aGUgcmVjdGFuZ2xlLlxyXG5cdFx0Ly8gQHByb3BlcnR5IG1heDogUG9pbnRcclxuXHRcdC8vIFRoZSBib3R0b20gcmlnaHQgY29ybmVyIG9mIHRoZSByZWN0YW5nbGUuXHJcblx0XHRpZiAoIXRoaXMubWluICYmICF0aGlzLm1heCkge1xyXG5cdFx0XHR0aGlzLm1pbiA9IHBvaW50LmNsb25lKCk7XHJcblx0XHRcdHRoaXMubWF4ID0gcG9pbnQuY2xvbmUoKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHRoaXMubWluLnggPSBNYXRoLm1pbihwb2ludC54LCB0aGlzLm1pbi54KTtcclxuXHRcdFx0dGhpcy5tYXgueCA9IE1hdGgubWF4KHBvaW50LngsIHRoaXMubWF4LngpO1xyXG5cdFx0XHR0aGlzLm1pbi55ID0gTWF0aC5taW4ocG9pbnQueSwgdGhpcy5taW4ueSk7XHJcblx0XHRcdHRoaXMubWF4LnkgPSBNYXRoLm1heChwb2ludC55LCB0aGlzLm1heC55KTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q2VudGVyKHJvdW5kPzogQm9vbGVhbik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgY2VudGVyIHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0Q2VudGVyOiBmdW5jdGlvbiAocm91bmQpIHtcclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludChcclxuXHRcdCAgICAgICAgKHRoaXMubWluLnggKyB0aGlzLm1heC54KSAvIDIsXHJcblx0XHQgICAgICAgICh0aGlzLm1pbi55ICsgdGhpcy5tYXgueSkgLyAyLCByb3VuZCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRCb3R0b21MZWZ0KCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgYm90dG9tLWxlZnQgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXRCb3R0b21MZWZ0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy5taW4ueCwgdGhpcy5tYXgueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRUb3BSaWdodCgpOiBQb2ludFxyXG5cdC8vIFJldHVybnMgdGhlIHRvcC1yaWdodCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldFRvcFJpZ2h0OiBmdW5jdGlvbiAoKSB7IC8vIC0+IFBvaW50XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQodGhpcy5tYXgueCwgdGhpcy5taW4ueSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTaXplKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgc2l6ZSBvZiB0aGUgZ2l2ZW4gYm91bmRzXHJcblx0Z2V0U2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMubWF4LnN1YnRyYWN0KHRoaXMubWluKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zKG90aGVyQm91bmRzOiBCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBjb250YWlucyB0aGUgZ2l2ZW4gb25lLlxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2QgY29udGFpbnMocG9pbnQ6IFBvaW50KTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgY29udGFpbnMgdGhlIGdpdmVuIHBvaW50LlxyXG5cdGNvbnRhaW5zOiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHR2YXIgbWluLCBtYXg7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiBvYmpbMF0gPT09ICdudW1iZXInIHx8IG9iaiBpbnN0YW5jZW9mIEwuUG9pbnQpIHtcclxuXHRcdFx0b2JqID0gTC5wb2ludChvYmopO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0b2JqID0gTC5ib3VuZHMob2JqKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAob2JqIGluc3RhbmNlb2YgTC5Cb3VuZHMpIHtcclxuXHRcdFx0bWluID0gb2JqLm1pbjtcclxuXHRcdFx0bWF4ID0gb2JqLm1heDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG1pbiA9IG1heCA9IG9iajtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gKG1pbi54ID49IHRoaXMubWluLngpICYmXHJcblx0XHQgICAgICAgKG1heC54IDw9IHRoaXMubWF4LngpICYmXHJcblx0XHQgICAgICAgKG1pbi55ID49IHRoaXMubWluLnkpICYmXHJcblx0XHQgICAgICAgKG1heC55IDw9IHRoaXMubWF4LnkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaW50ZXJzZWN0cyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgaW50ZXJzZWN0cyB0aGUgZ2l2ZW4gYm91bmRzLiBUd28gYm91bmRzXHJcblx0Ly8gaW50ZXJzZWN0IGlmIHRoZXkgaGF2ZSBhdCBsZWFzdCBvbmUgcG9pbnQgaW4gY29tbW9uLlxyXG5cdGludGVyc2VjdHM6IGZ1bmN0aW9uIChib3VuZHMpIHsgLy8gKEJvdW5kcykgLT4gQm9vbGVhblxyXG5cdFx0Ym91bmRzID0gTC5ib3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgbWluID0gdGhpcy5taW4sXHJcblx0XHQgICAgbWF4ID0gdGhpcy5tYXgsXHJcblx0XHQgICAgbWluMiA9IGJvdW5kcy5taW4sXHJcblx0XHQgICAgbWF4MiA9IGJvdW5kcy5tYXgsXHJcblx0XHQgICAgeEludGVyc2VjdHMgPSAobWF4Mi54ID49IG1pbi54KSAmJiAobWluMi54IDw9IG1heC54KSxcclxuXHRcdCAgICB5SW50ZXJzZWN0cyA9IChtYXgyLnkgPj0gbWluLnkpICYmIChtaW4yLnkgPD0gbWF4LnkpO1xyXG5cclxuXHRcdHJldHVybiB4SW50ZXJzZWN0cyAmJiB5SW50ZXJzZWN0cztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIG92ZXJsYXBzKG90aGVyQm91bmRzOiBCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBvdmVybGFwcyB0aGUgZ2l2ZW4gYm91bmRzLiBUd28gYm91bmRzXHJcblx0Ly8gb3ZlcmxhcCBpZiB0aGVpciBpbnRlcnNlY3Rpb24gaXMgYW4gYXJlYS5cclxuXHRvdmVybGFwczogZnVuY3Rpb24gKGJvdW5kcykgeyAvLyAoQm91bmRzKSAtPiBCb29sZWFuXHJcblx0XHRib3VuZHMgPSBMLmJvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHZhciBtaW4gPSB0aGlzLm1pbixcclxuXHRcdCAgICBtYXggPSB0aGlzLm1heCxcclxuXHRcdCAgICBtaW4yID0gYm91bmRzLm1pbixcclxuXHRcdCAgICBtYXgyID0gYm91bmRzLm1heCxcclxuXHRcdCAgICB4T3ZlcmxhcHMgPSAobWF4Mi54ID4gbWluLngpICYmIChtaW4yLnggPCBtYXgueCksXHJcblx0XHQgICAgeU92ZXJsYXBzID0gKG1heDIueSA+IG1pbi55KSAmJiAobWluMi55IDwgbWF4LnkpO1xyXG5cclxuXHRcdHJldHVybiB4T3ZlcmxhcHMgJiYgeU92ZXJsYXBzO1xyXG5cdH0sXHJcblxyXG5cdGlzVmFsaWQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhISh0aGlzLm1pbiAmJiB0aGlzLm1heCk7XHJcblx0fVxyXG59O1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwuYm91bmRzKHRvcExlZnQ6IFBvaW50LCBib3R0b21SaWdodDogUG9pbnQpXHJcbi8vIENyZWF0ZXMgYSBCb3VuZHMgb2JqZWN0IGZyb20gdHdvIGNvb3JkaW5hdGVzICh1c3VhbGx5IHRvcC1sZWZ0IGFuZCBib3R0b20tcmlnaHQgY29ybmVycykuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLmJvdW5kcyhwb2ludHM6IFBvaW50W10pXHJcbi8vIENyZWF0ZXMgYSBCb3VuZHMgb2JqZWN0IGZyb20gdGhlIHBvaW50cyBpdCBjb250YWluc1xyXG5MLmJvdW5kcyA9IGZ1bmN0aW9uIChhLCBiKSB7XHJcblx0aWYgKCFhIHx8IGEgaW5zdGFuY2VvZiBMLkJvdW5kcykge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdHJldHVybiBuZXcgTC5Cb3VuZHMoYSwgYik7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBUcmFuc2Zvcm1hdGlvblxyXG4gKiBAYWthIEwuVHJhbnNmb3JtYXRpb25cclxuICpcclxuICogUmVwcmVzZW50cyBhbiBhZmZpbmUgdHJhbnNmb3JtYXRpb246IGEgc2V0IG9mIGNvZWZmaWNpZW50cyBgYWAsIGBiYCwgYGNgLCBgZGBcclxuICogZm9yIHRyYW5zZm9ybWluZyBhIHBvaW50IG9mIGEgZm9ybSBgKHgsIHkpYCBpbnRvIGAoYSp4ICsgYiwgYyp5ICsgZClgIGFuZCBkb2luZ1xyXG4gKiB0aGUgcmV2ZXJzZS4gVXNlZCBieSBMZWFmbGV0IGluIGl0cyBwcm9qZWN0aW9ucyBjb2RlLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgdHJhbnNmb3JtYXRpb24gPSBuZXcgTC5UcmFuc2Zvcm1hdGlvbigyLCA1LCAtMSwgMTApLFxyXG4gKiBcdHAgPSBMLnBvaW50KDEsIDIpLFxyXG4gKiBcdHAyID0gdHJhbnNmb3JtYXRpb24udHJhbnNmb3JtKHApLCAvLyAgTC5wb2ludCg3LCA4KVxyXG4gKiBcdHAzID0gdHJhbnNmb3JtYXRpb24udW50cmFuc2Zvcm0ocDIpOyAvLyAgTC5wb2ludCgxLCAyKVxyXG4gKiBgYGBcclxuICovXHJcblxyXG5cclxuLy8gZmFjdG9yeSBuZXcgTC5UcmFuc2Zvcm1hdGlvbihhOiBOdW1iZXIsIGI6IE51bWJlciwgYzogTnVtYmVyLCBkOiBOdW1iZXIpXHJcbi8vIENyZWF0ZXMgYSBgVHJhbnNmb3JtYXRpb25gIG9iamVjdCB3aXRoIHRoZSBnaXZlbiBjb2VmZmljaWVudHMuXHJcbkwuVHJhbnNmb3JtYXRpb24gPSBmdW5jdGlvbiAoYSwgYiwgYywgZCkge1xyXG5cdHRoaXMuX2EgPSBhO1xyXG5cdHRoaXMuX2IgPSBiO1xyXG5cdHRoaXMuX2MgPSBjO1xyXG5cdHRoaXMuX2QgPSBkO1xyXG59O1xyXG5cclxuTC5UcmFuc2Zvcm1hdGlvbi5wcm90b3R5cGUgPSB7XHJcblx0Ly8gQG1ldGhvZCB0cmFuc2Zvcm0ocG9pbnQ6IFBvaW50LCBzY2FsZT86IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyBhIHRyYW5zZm9ybWVkIHBvaW50LCBvcHRpb25hbGx5IG11bHRpcGxpZWQgYnkgdGhlIGdpdmVuIHNjYWxlLlxyXG5cdC8vIE9ubHkgYWNjZXB0cyBhY3R1YWwgYEwuUG9pbnRgIGluc3RhbmNlcywgbm90IGFycmF5cy5cclxuXHR0cmFuc2Zvcm06IGZ1bmN0aW9uIChwb2ludCwgc2NhbGUpIHsgLy8gKFBvaW50LCBOdW1iZXIpIC0+IFBvaW50XHJcblx0XHRyZXR1cm4gdGhpcy5fdHJhbnNmb3JtKHBvaW50LmNsb25lKCksIHNjYWxlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBkZXN0cnVjdGl2ZSB0cmFuc2Zvcm0gKGZhc3RlcilcclxuXHRfdHJhbnNmb3JtOiBmdW5jdGlvbiAocG9pbnQsIHNjYWxlKSB7XHJcblx0XHRzY2FsZSA9IHNjYWxlIHx8IDE7XHJcblx0XHRwb2ludC54ID0gc2NhbGUgKiAodGhpcy5fYSAqIHBvaW50LnggKyB0aGlzLl9iKTtcclxuXHRcdHBvaW50LnkgPSBzY2FsZSAqICh0aGlzLl9jICogcG9pbnQueSArIHRoaXMuX2QpO1xyXG5cdFx0cmV0dXJuIHBvaW50O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW50cmFuc2Zvcm0ocG9pbnQ6IFBvaW50LCBzY2FsZT86IE51bWJlcik6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgcmV2ZXJzZSB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgZ2l2ZW4gcG9pbnQsIG9wdGlvbmFsbHkgZGl2aWRlZFxyXG5cdC8vIGJ5IHRoZSBnaXZlbiBzY2FsZS4gT25seSBhY2NlcHRzIGFjdHVhbCBgTC5Qb2ludGAgaW5zdGFuY2VzLCBub3QgYXJyYXlzLlxyXG5cdHVudHJhbnNmb3JtOiBmdW5jdGlvbiAocG9pbnQsIHNjYWxlKSB7XHJcblx0XHRzY2FsZSA9IHNjYWxlIHx8IDE7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoXHJcblx0XHQgICAgICAgIChwb2ludC54IC8gc2NhbGUgLSB0aGlzLl9iKSAvIHRoaXMuX2EsXHJcblx0XHQgICAgICAgIChwb2ludC55IC8gc2NhbGUgLSB0aGlzLl9kKSAvIHRoaXMuX2MpO1xyXG5cdH1cclxufTtcclxuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBEb21VdGlsXHJcbiAqXHJcbiAqIFV0aWxpdHkgZnVuY3Rpb25zIHRvIHdvcmsgd2l0aCB0aGUgW0RPTV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL0RvY3VtZW50X09iamVjdF9Nb2RlbClcclxuICogdHJlZSwgdXNlZCBieSBMZWFmbGV0IGludGVybmFsbHkuXHJcbiAqXHJcbiAqIE1vc3QgZnVuY3Rpb25zIGV4cGVjdGluZyBvciByZXR1cm5pbmcgYSBgSFRNTEVsZW1lbnRgIGFsc28gd29yayBmb3JcclxuICogU1ZHIGVsZW1lbnRzLiBUaGUgb25seSBkaWZmZXJlbmNlIGlzIHRoYXQgY2xhc3NlcyByZWZlciB0byBDU1MgY2xhc3Nlc1xyXG4gKiBpbiBIVE1MIGFuZCBTVkcgY2xhc3NlcyBpbiBTVkcuXHJcbiAqL1xyXG5cclxuTC5Eb21VdGlsID0ge1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0KGlkOiBTdHJpbmd8SFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudFxyXG5cdC8vIFJldHVybnMgYW4gZWxlbWVudCBnaXZlbiBpdHMgRE9NIGlkLCBvciByZXR1cm5zIHRoZSBlbGVtZW50IGl0c2VsZlxyXG5cdC8vIGlmIGl0IHdhcyBwYXNzZWQgZGlyZWN0bHkuXHJcblx0Z2V0OiBmdW5jdGlvbiAoaWQpIHtcclxuXHRcdHJldHVybiB0eXBlb2YgaWQgPT09ICdzdHJpbmcnID8gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoaWQpIDogaWQ7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldFN0eWxlKGVsOiBIVE1MRWxlbWVudCwgc3R5bGVBdHRyaWI6IFN0cmluZyk6IFN0cmluZ1xyXG5cdC8vIFJldHVybnMgdGhlIHZhbHVlIGZvciBhIGNlcnRhaW4gc3R5bGUgYXR0cmlidXRlIG9uIGFuIGVsZW1lbnQsXHJcblx0Ly8gaW5jbHVkaW5nIGNvbXB1dGVkIHZhbHVlcyBvciB2YWx1ZXMgc2V0IHRocm91Z2ggQ1NTLlxyXG5cdGdldFN0eWxlOiBmdW5jdGlvbiAoZWwsIHN0eWxlKSB7XHJcblxyXG5cdFx0dmFyIHZhbHVlID0gZWwuc3R5bGVbc3R5bGVdIHx8IChlbC5jdXJyZW50U3R5bGUgJiYgZWwuY3VycmVudFN0eWxlW3N0eWxlXSk7XHJcblxyXG5cdFx0aWYgKCghdmFsdWUgfHwgdmFsdWUgPT09ICdhdXRvJykgJiYgZG9jdW1lbnQuZGVmYXVsdFZpZXcpIHtcclxuXHRcdFx0dmFyIGNzcyA9IGRvY3VtZW50LmRlZmF1bHRWaWV3LmdldENvbXB1dGVkU3R5bGUoZWwsIG51bGwpO1xyXG5cdFx0XHR2YWx1ZSA9IGNzcyA/IGNzc1tzdHlsZV0gOiBudWxsO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB2YWx1ZSA9PT0gJ2F1dG8nID8gbnVsbCA6IHZhbHVlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBjcmVhdGUodGFnTmFtZTogU3RyaW5nLCBjbGFzc05hbWU/OiBTdHJpbmcsIGNvbnRhaW5lcj86IEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBDcmVhdGVzIGFuIEhUTUwgZWxlbWVudCB3aXRoIGB0YWdOYW1lYCwgc2V0cyBpdHMgY2xhc3MgdG8gYGNsYXNzTmFtZWAsIGFuZCBvcHRpb25hbGx5IGFwcGVuZHMgaXQgdG8gYGNvbnRhaW5lcmAgZWxlbWVudC5cclxuXHRjcmVhdGU6IGZ1bmN0aW9uICh0YWdOYW1lLCBjbGFzc05hbWUsIGNvbnRhaW5lcikge1xyXG5cclxuXHRcdHZhciBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQodGFnTmFtZSk7XHJcblx0XHRlbC5jbGFzc05hbWUgPSBjbGFzc05hbWUgfHwgJyc7XHJcblxyXG5cdFx0aWYgKGNvbnRhaW5lcikge1xyXG5cdFx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQoZWwpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBlbDtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gcmVtb3ZlKGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBSZW1vdmVzIGBlbGAgZnJvbSBpdHMgcGFyZW50IGVsZW1lbnRcclxuXHRyZW1vdmU6IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0dmFyIHBhcmVudCA9IGVsLnBhcmVudE5vZGU7XHJcblx0XHRpZiAocGFyZW50KSB7XHJcblx0XHRcdHBhcmVudC5yZW1vdmVDaGlsZChlbCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGVtcHR5KGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBSZW1vdmVzIGFsbCBvZiBgZWxgJ3MgY2hpbGRyZW4gZWxlbWVudHMgZnJvbSBgZWxgXHJcblx0ZW1wdHk6IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0d2hpbGUgKGVsLmZpcnN0Q2hpbGQpIHtcclxuXHRcdFx0ZWwucmVtb3ZlQ2hpbGQoZWwuZmlyc3RDaGlsZCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHRvRnJvbnQoZWw6IEhUTUxFbGVtZW50KVxyXG5cdC8vIE1ha2VzIGBlbGAgdGhlIGxhc3QgY2hpbGRyZW4gb2YgaXRzIHBhcmVudCwgc28gaXQgcmVuZGVycyBpbiBmcm9udCBvZiB0aGUgb3RoZXIgY2hpbGRyZW4uXHJcblx0dG9Gcm9udDogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHRlbC5wYXJlbnROb2RlLmFwcGVuZENoaWxkKGVsKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gdG9CYWNrKGVsOiBIVE1MRWxlbWVudClcclxuXHQvLyBNYWtlcyBgZWxgIHRoZSBmaXJzdCBjaGlsZHJlbiBvZiBpdHMgcGFyZW50LCBzbyBpdCByZW5kZXJzIGJhY2sgZnJvbSB0aGUgb3RoZXIgY2hpbGRyZW4uXHJcblx0dG9CYWNrOiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdHZhciBwYXJlbnQgPSBlbC5wYXJlbnROb2RlO1xyXG5cdFx0cGFyZW50Lmluc2VydEJlZm9yZShlbCwgcGFyZW50LmZpcnN0Q2hpbGQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBoYXNDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbWU6IFN0cmluZyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgZWxlbWVudCdzIGNsYXNzIGF0dHJpYnV0ZSBjb250YWlucyBgbmFtZWAuXHJcblx0aGFzQ2xhc3M6IGZ1bmN0aW9uIChlbCwgbmFtZSkge1xyXG5cdFx0aWYgKGVsLmNsYXNzTGlzdCAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdHJldHVybiBlbC5jbGFzc0xpc3QuY29udGFpbnMobmFtZSk7XHJcblx0XHR9XHJcblx0XHR2YXIgY2xhc3NOYW1lID0gTC5Eb21VdGlsLmdldENsYXNzKGVsKTtcclxuXHRcdHJldHVybiBjbGFzc05hbWUubGVuZ3RoID4gMCAmJiBuZXcgUmVnRXhwKCcoXnxcXFxccyknICsgbmFtZSArICcoXFxcXHN8JCknKS50ZXN0KGNsYXNzTmFtZSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGFkZENsYXNzKGVsOiBIVE1MRWxlbWVudCwgbmFtZTogU3RyaW5nKVxyXG5cdC8vIEFkZHMgYG5hbWVgIHRvIHRoZSBlbGVtZW50J3MgY2xhc3MgYXR0cmlidXRlLlxyXG5cdGFkZENsYXNzOiBmdW5jdGlvbiAoZWwsIG5hbWUpIHtcclxuXHRcdGlmIChlbC5jbGFzc0xpc3QgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR2YXIgY2xhc3NlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKG5hbWUpO1xyXG5cdFx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gY2xhc3Nlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdGVsLmNsYXNzTGlzdC5hZGQoY2xhc3Nlc1tpXSk7XHJcblx0XHRcdH1cclxuXHRcdH0gZWxzZSBpZiAoIUwuRG9tVXRpbC5oYXNDbGFzcyhlbCwgbmFtZSkpIHtcclxuXHRcdFx0dmFyIGNsYXNzTmFtZSA9IEwuRG9tVXRpbC5nZXRDbGFzcyhlbCk7XHJcblx0XHRcdEwuRG9tVXRpbC5zZXRDbGFzcyhlbCwgKGNsYXNzTmFtZSA/IGNsYXNzTmFtZSArICcgJyA6ICcnKSArIG5hbWUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiByZW1vdmVDbGFzcyhlbDogSFRNTEVsZW1lbnQsIG5hbWU6IFN0cmluZylcclxuXHQvLyBSZW1vdmVzIGBuYW1lYCBmcm9tIHRoZSBlbGVtZW50J3MgY2xhc3MgYXR0cmlidXRlLlxyXG5cdHJlbW92ZUNsYXNzOiBmdW5jdGlvbiAoZWwsIG5hbWUpIHtcclxuXHRcdGlmIChlbC5jbGFzc0xpc3QgIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRlbC5jbGFzc0xpc3QucmVtb3ZlKG5hbWUpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0TC5Eb21VdGlsLnNldENsYXNzKGVsLCBMLlV0aWwudHJpbSgoJyAnICsgTC5Eb21VdGlsLmdldENsYXNzKGVsKSArICcgJykucmVwbGFjZSgnICcgKyBuYW1lICsgJyAnLCAnICcpKSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNldENsYXNzKGVsOiBIVE1MRWxlbWVudCwgbmFtZTogU3RyaW5nKVxyXG5cdC8vIFNldHMgdGhlIGVsZW1lbnQncyBjbGFzcy5cclxuXHRzZXRDbGFzczogZnVuY3Rpb24gKGVsLCBuYW1lKSB7XHJcblx0XHRpZiAoZWwuY2xhc3NOYW1lLmJhc2VWYWwgPT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHRlbC5jbGFzc05hbWUgPSBuYW1lO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0Ly8gaW4gY2FzZSBvZiBTVkcgZWxlbWVudFxyXG5cdFx0XHRlbC5jbGFzc05hbWUuYmFzZVZhbCA9IG5hbWU7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGdldENsYXNzKGVsOiBIVE1MRWxlbWVudCk6IFN0cmluZ1xyXG5cdC8vIFJldHVybnMgdGhlIGVsZW1lbnQncyBjbGFzcy5cclxuXHRnZXRDbGFzczogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHRyZXR1cm4gZWwuY2xhc3NOYW1lLmJhc2VWYWwgPT09IHVuZGVmaW5lZCA/IGVsLmNsYXNzTmFtZSA6IGVsLmNsYXNzTmFtZS5iYXNlVmFsO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRPcGFjaXR5KGVsOiBIVE1MRWxlbWVudCwgb3BhY2l0eTogTnVtYmVyKVxyXG5cdC8vIFNldCB0aGUgb3BhY2l0eSBvZiBhbiBlbGVtZW50IChpbmNsdWRpbmcgb2xkIElFIHN1cHBvcnQpLlxyXG5cdC8vIGBvcGFjaXR5YCBtdXN0IGJlIGEgbnVtYmVyIGZyb20gYDBgIHRvIGAxYC5cclxuXHRzZXRPcGFjaXR5OiBmdW5jdGlvbiAoZWwsIHZhbHVlKSB7XHJcblxyXG5cdFx0aWYgKCdvcGFjaXR5JyBpbiBlbC5zdHlsZSkge1xyXG5cdFx0XHRlbC5zdHlsZS5vcGFjaXR5ID0gdmFsdWU7XHJcblxyXG5cdFx0fSBlbHNlIGlmICgnZmlsdGVyJyBpbiBlbC5zdHlsZSkge1xyXG5cdFx0XHRMLkRvbVV0aWwuX3NldE9wYWNpdHlJRShlbCwgdmFsdWUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9zZXRPcGFjaXR5SUU6IGZ1bmN0aW9uIChlbCwgdmFsdWUpIHtcclxuXHRcdHZhciBmaWx0ZXIgPSBmYWxzZSxcclxuXHRcdCAgICBmaWx0ZXJOYW1lID0gJ0RYSW1hZ2VUcmFuc2Zvcm0uTWljcm9zb2Z0LkFscGhhJztcclxuXHJcblx0XHQvLyBmaWx0ZXJzIGNvbGxlY3Rpb24gdGhyb3dzIGFuIGVycm9yIGlmIHdlIHRyeSB0byByZXRyaWV2ZSBhIGZpbHRlciB0aGF0IGRvZXNuJ3QgZXhpc3RcclxuXHRcdHRyeSB7XHJcblx0XHRcdGZpbHRlciA9IGVsLmZpbHRlcnMuaXRlbShmaWx0ZXJOYW1lKTtcclxuXHRcdH0gY2F0Y2ggKGUpIHtcclxuXHRcdFx0Ly8gZG9uJ3Qgc2V0IG9wYWNpdHkgdG8gMSBpZiB3ZSBoYXZlbid0IGFscmVhZHkgc2V0IGFuIG9wYWNpdHksXHJcblx0XHRcdC8vIGl0IGlzbid0IG5lZWRlZCBhbmQgYnJlYWtzIHRyYW5zcGFyZW50IHBuZ3MuXHJcblx0XHRcdGlmICh2YWx1ZSA9PT0gMSkgeyByZXR1cm47IH1cclxuXHRcdH1cclxuXHJcblx0XHR2YWx1ZSA9IE1hdGgucm91bmQodmFsdWUgKiAxMDApO1xyXG5cclxuXHRcdGlmIChmaWx0ZXIpIHtcclxuXHRcdFx0ZmlsdGVyLkVuYWJsZWQgPSAodmFsdWUgIT09IDEwMCk7XHJcblx0XHRcdGZpbHRlci5PcGFjaXR5ID0gdmFsdWU7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRlbC5zdHlsZS5maWx0ZXIgKz0gJyBwcm9naWQ6JyArIGZpbHRlck5hbWUgKyAnKG9wYWNpdHk9JyArIHZhbHVlICsgJyknO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiB0ZXN0UHJvcChwcm9wczogU3RyaW5nW10pOiBTdHJpbmd8ZmFsc2VcclxuXHQvLyBHb2VzIHRocm91Z2ggdGhlIGFycmF5IG9mIHN0eWxlIG5hbWVzIGFuZCByZXR1cm5zIHRoZSBmaXJzdCBuYW1lXHJcblx0Ly8gdGhhdCBpcyBhIHZhbGlkIHN0eWxlIG5hbWUgZm9yIGFuIGVsZW1lbnQuIElmIG5vIHN1Y2ggbmFtZSBpcyBmb3VuZCxcclxuXHQvLyBpdCByZXR1cm5zIGZhbHNlLiBVc2VmdWwgZm9yIHZlbmRvci1wcmVmaXhlZCBzdHlsZXMgbGlrZSBgdHJhbnNmb3JtYC5cclxuXHR0ZXN0UHJvcDogZnVuY3Rpb24gKHByb3BzKSB7XHJcblxyXG5cdFx0dmFyIHN0eWxlID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnN0eWxlO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwOyBpIDwgcHJvcHMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0aWYgKHByb3BzW2ldIGluIHN0eWxlKSB7XHJcblx0XHRcdFx0cmV0dXJuIHByb3BzW2ldO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gZmFsc2U7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHNldFRyYW5zZm9ybShlbDogSFRNTEVsZW1lbnQsIG9mZnNldDogUG9pbnQsIHNjYWxlPzogTnVtYmVyKVxyXG5cdC8vIFJlc2V0cyB0aGUgM0QgQ1NTIHRyYW5zZm9ybSBvZiBgZWxgIHNvIGl0IGlzIHRyYW5zbGF0ZWQgYnkgYG9mZnNldGAgcGl4ZWxzXHJcblx0Ly8gYW5kIG9wdGlvbmFsbHkgc2NhbGVkIGJ5IGBzY2FsZWAuIERvZXMgbm90IGhhdmUgYW4gZWZmZWN0IGlmIHRoZVxyXG5cdC8vIGJyb3dzZXIgZG9lc24ndCBzdXBwb3J0IDNEIENTUyB0cmFuc2Zvcm1zLlxyXG5cdHNldFRyYW5zZm9ybTogZnVuY3Rpb24gKGVsLCBvZmZzZXQsIHNjYWxlKSB7XHJcblx0XHR2YXIgcG9zID0gb2Zmc2V0IHx8IG5ldyBMLlBvaW50KDAsIDApO1xyXG5cclxuXHRcdGVsLnN0eWxlW0wuRG9tVXRpbC5UUkFOU0ZPUk1dID1cclxuXHRcdFx0KEwuQnJvd3Nlci5pZTNkID9cclxuXHRcdFx0XHQndHJhbnNsYXRlKCcgKyBwb3MueCArICdweCwnICsgcG9zLnkgKyAncHgpJyA6XHJcblx0XHRcdFx0J3RyYW5zbGF0ZTNkKCcgKyBwb3MueCArICdweCwnICsgcG9zLnkgKyAncHgsMCknKSArXHJcblx0XHRcdChzY2FsZSA/ICcgc2NhbGUoJyArIHNjYWxlICsgJyknIDogJycpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzZXRQb3NpdGlvbihlbDogSFRNTEVsZW1lbnQsIHBvc2l0aW9uOiBQb2ludClcclxuXHQvLyBTZXRzIHRoZSBwb3NpdGlvbiBvZiBgZWxgIHRvIGNvb3JkaW5hdGVzIHNwZWNpZmllZCBieSBgcG9zaXRpb25gLFxyXG5cdC8vIHVzaW5nIENTUyB0cmFuc2xhdGUgb3IgdG9wL2xlZnQgcG9zaXRpb25pbmcgZGVwZW5kaW5nIG9uIHRoZSBicm93c2VyXHJcblx0Ly8gKHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5IHRvIHBvc2l0aW9uIGl0cyBsYXllcnMpLlxyXG5cdHNldFBvc2l0aW9uOiBmdW5jdGlvbiAoZWwsIHBvaW50KSB7IC8vIChIVE1MRWxlbWVudCwgUG9pbnRbLCBCb29sZWFuXSlcclxuXHJcblx0XHQvKmVzbGludC1kaXNhYmxlICovXHJcblx0XHRlbC5fbGVhZmxldF9wb3MgPSBwb2ludDtcclxuXHRcdC8qZXNsaW50LWVuYWJsZSAqL1xyXG5cclxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QpIHtcclxuXHRcdFx0TC5Eb21VdGlsLnNldFRyYW5zZm9ybShlbCwgcG9pbnQpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZWwuc3R5bGUubGVmdCA9IHBvaW50LnggKyAncHgnO1xyXG5cdFx0XHRlbC5zdHlsZS50b3AgPSBwb2ludC55ICsgJ3B4JztcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0UG9zaXRpb24oZWw6IEhUTUxFbGVtZW50KTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBjb29yZGluYXRlcyBvZiBhbiBlbGVtZW50IHByZXZpb3VzbHkgcG9zaXRpb25lZCB3aXRoIHNldFBvc2l0aW9uLlxyXG5cdGdldFBvc2l0aW9uOiBmdW5jdGlvbiAoZWwpIHtcclxuXHRcdC8vIHRoaXMgbWV0aG9kIGlzIG9ubHkgdXNlZCBmb3IgZWxlbWVudHMgcHJldmlvdXNseSBwb3NpdGlvbmVkIHVzaW5nIHNldFBvc2l0aW9uLFxyXG5cdFx0Ly8gc28gaXQncyBzYWZlIHRvIGNhY2hlIHRoZSBwb3NpdGlvbiBmb3IgcGVyZm9ybWFuY2VcclxuXHJcblx0XHRyZXR1cm4gZWwuX2xlYWZsZXRfcG9zIHx8IG5ldyBMLlBvaW50KDAsIDApO1xyXG5cdH1cclxufTtcclxuXHJcblxyXG4oZnVuY3Rpb24gKCkge1xyXG5cdC8vIHByZWZpeCBzdHlsZSBwcm9wZXJ0eSBuYW1lc1xyXG5cclxuXHQvLyBAcHJvcGVydHkgVFJBTlNGT1JNOiBTdHJpbmdcclxuXHQvLyBWZW5kb3ItcHJlZml4ZWQgZnJhbnNmb3JtIHN0eWxlIG5hbWUgKGUuZy4gYCd3ZWJraXRUcmFuc2Zvcm0nYCBmb3IgV2ViS2l0KS5cclxuXHRMLkRvbVV0aWwuVFJBTlNGT1JNID0gTC5Eb21VdGlsLnRlc3RQcm9wKFxyXG5cdFx0XHRbJ3RyYW5zZm9ybScsICdXZWJraXRUcmFuc2Zvcm0nLCAnT1RyYW5zZm9ybScsICdNb3pUcmFuc2Zvcm0nLCAnbXNUcmFuc2Zvcm0nXSk7XHJcblxyXG5cclxuXHQvLyB3ZWJraXRUcmFuc2l0aW9uIGNvbWVzIGZpcnN0IGJlY2F1c2Ugc29tZSBicm93c2VyIHZlcnNpb25zIHRoYXQgZHJvcCB2ZW5kb3IgcHJlZml4IGRvbid0IGRvXHJcblx0Ly8gdGhlIHNhbWUgZm9yIHRoZSB0cmFuc2l0aW9uZW5kIGV2ZW50LCBpbiBwYXJ0aWN1bGFyIHRoZSBBbmRyb2lkIDQuMSBzdG9jayBicm93c2VyXHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBUUkFOU0lUSU9OOiBTdHJpbmdcclxuXHQvLyBWZW5kb3ItcHJlZml4ZWQgdHJhbnNmb3JtIHN0eWxlIG5hbWUuXHJcblx0dmFyIHRyYW5zaXRpb24gPSBMLkRvbVV0aWwuVFJBTlNJVElPTiA9IEwuRG9tVXRpbC50ZXN0UHJvcChcclxuXHRcdFx0Wyd3ZWJraXRUcmFuc2l0aW9uJywgJ3RyYW5zaXRpb24nLCAnT1RyYW5zaXRpb24nLCAnTW96VHJhbnNpdGlvbicsICdtc1RyYW5zaXRpb24nXSk7XHJcblxyXG5cdEwuRG9tVXRpbC5UUkFOU0lUSU9OX0VORCA9XHJcblx0XHRcdHRyYW5zaXRpb24gPT09ICd3ZWJraXRUcmFuc2l0aW9uJyB8fCB0cmFuc2l0aW9uID09PSAnT1RyYW5zaXRpb24nID8gdHJhbnNpdGlvbiArICdFbmQnIDogJ3RyYW5zaXRpb25lbmQnO1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gZGlzYWJsZVRleHRTZWxlY3Rpb24oKVxyXG5cdC8vIFByZXZlbnRzIHRoZSB1c2VyIGZyb20gZ2VuZXJhdGluZyBgc2VsZWN0c3RhcnRgIERPTSBldmVudHMsIHVzdWFsbHkgZ2VuZXJhdGVkXHJcblx0Ly8gd2hlbiB0aGUgdXNlciBkcmFncyB0aGUgbW91c2UgdGhyb3VnaCBhIHBhZ2Ugd2l0aCB0ZXh0LiBVc2VkIGludGVybmFsbHlcclxuXHQvLyBieSBMZWFmbGV0IHRvIG92ZXJyaWRlIHRoZSBiZWhhdmlvdXIgb2YgYW55IGNsaWNrLWFuZC1kcmFnIGludGVyYWN0aW9uIG9uXHJcblx0Ly8gdGhlIG1hcC4gQWZmZWN0cyBkcmFnIGludGVyYWN0aW9ucyBvbiB0aGUgd2hvbGUgZG9jdW1lbnQuXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBlbmFibGVUZXh0U2VsZWN0aW9uKClcclxuXHQvLyBDYW5jZWxzIHRoZSBlZmZlY3RzIG9mIGEgcHJldmlvdXMgW2BMLkRvbVV0aWwuZGlzYWJsZVRleHRTZWxlY3Rpb25gXSgjZG9tdXRpbC1kaXNhYmxldGV4dHNlbGVjdGlvbikuXHJcblx0aWYgKCdvbnNlbGVjdHN0YXJ0JyBpbiBkb2N1bWVudCkge1xyXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRMLkRvbUV2ZW50Lm9uKHdpbmRvdywgJ3NlbGVjdHN0YXJ0JywgTC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdCk7XHJcblx0XHR9O1xyXG5cdFx0TC5Eb21VdGlsLmVuYWJsZVRleHRTZWxlY3Rpb24gPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdEwuRG9tRXZlbnQub2ZmKHdpbmRvdywgJ3NlbGVjdHN0YXJ0JywgTC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdCk7XHJcblx0XHR9O1xyXG5cclxuXHR9IGVsc2Uge1xyXG5cdFx0dmFyIHVzZXJTZWxlY3RQcm9wZXJ0eSA9IEwuRG9tVXRpbC50ZXN0UHJvcChcclxuXHRcdFx0Wyd1c2VyU2VsZWN0JywgJ1dlYmtpdFVzZXJTZWxlY3QnLCAnT1VzZXJTZWxlY3QnLCAnTW96VXNlclNlbGVjdCcsICdtc1VzZXJTZWxlY3QnXSk7XHJcblxyXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0XHRpZiAodXNlclNlbGVjdFByb3BlcnR5KSB7XHJcblx0XHRcdFx0dmFyIHN0eWxlID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnN0eWxlO1xyXG5cdFx0XHRcdHRoaXMuX3VzZXJTZWxlY3QgPSBzdHlsZVt1c2VyU2VsZWN0UHJvcGVydHldO1xyXG5cdFx0XHRcdHN0eWxlW3VzZXJTZWxlY3RQcm9wZXJ0eV0gPSAnbm9uZSc7XHJcblx0XHRcdH1cclxuXHRcdH07XHJcblx0XHRMLkRvbVV0aWwuZW5hYmxlVGV4dFNlbGVjdGlvbiA9IGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0aWYgKHVzZXJTZWxlY3RQcm9wZXJ0eSkge1xyXG5cdFx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZVt1c2VyU2VsZWN0UHJvcGVydHldID0gdGhpcy5fdXNlclNlbGVjdDtcclxuXHRcdFx0XHRkZWxldGUgdGhpcy5fdXNlclNlbGVjdDtcclxuXHRcdFx0fVxyXG5cdFx0fTtcclxuXHR9XHJcblxyXG5cdC8vIEBmdW5jdGlvbiBkaXNhYmxlSW1hZ2VEcmFnKClcclxuXHQvLyBBcyBbYEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbmBdKCNkb211dGlsLWRpc2FibGV0ZXh0c2VsZWN0aW9uKSwgYnV0XHJcblx0Ly8gZm9yIGBkcmFnc3RhcnRgIERPTSBldmVudHMsIHVzdWFsbHkgZ2VuZXJhdGVkIHdoZW4gdGhlIHVzZXIgZHJhZ3MgYW4gaW1hZ2UuXHJcblx0TC5Eb21VdGlsLmRpc2FibGVJbWFnZURyYWcgPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRMLkRvbUV2ZW50Lm9uKHdpbmRvdywgJ2RyYWdzdGFydCcsIEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQpO1xyXG5cdH07XHJcblxyXG5cdC8vIEBmdW5jdGlvbiBlbmFibGVJbWFnZURyYWcoKVxyXG5cdC8vIENhbmNlbHMgdGhlIGVmZmVjdHMgb2YgYSBwcmV2aW91cyBbYEwuRG9tVXRpbC5kaXNhYmxlSW1hZ2VEcmFnYF0oI2RvbXV0aWwtZGlzYWJsZXRleHRzZWxlY3Rpb24pLlxyXG5cdEwuRG9tVXRpbC5lbmFibGVJbWFnZURyYWcgPSBmdW5jdGlvbiAoKSB7XHJcblx0XHRMLkRvbUV2ZW50Lm9mZih3aW5kb3csICdkcmFnc3RhcnQnLCBMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KTtcclxuXHR9O1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gcHJldmVudE91dGxpbmUoZWw6IEhUTUxFbGVtZW50KVxyXG5cdC8vIE1ha2VzIHRoZSBbb3V0bGluZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQ1NTL291dGxpbmUpXHJcblx0Ly8gb2YgdGhlIGVsZW1lbnQgYGVsYCBpbnZpc2libGUuIFVzZWQgaW50ZXJuYWxseSBieSBMZWFmbGV0IHRvIHByZXZlbnRcclxuXHQvLyBmb2N1c2FibGUgZWxlbWVudHMgZnJvbSBkaXNwbGF5aW5nIGFuIG91dGxpbmUgd2hlbiB0aGUgdXNlciBwZXJmb3JtcyBhXHJcblx0Ly8gZHJhZyBpbnRlcmFjdGlvbiBvbiB0aGVtLlxyXG5cdEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZSA9IGZ1bmN0aW9uIChlbGVtZW50KSB7XHJcblx0XHR3aGlsZSAoZWxlbWVudC50YWJJbmRleCA9PT0gLTEpIHtcclxuXHRcdFx0ZWxlbWVudCA9IGVsZW1lbnQucGFyZW50Tm9kZTtcclxuXHRcdH1cclxuXHRcdGlmICghZWxlbWVudCB8fCAhZWxlbWVudC5zdHlsZSkgeyByZXR1cm47IH1cclxuXHRcdEwuRG9tVXRpbC5yZXN0b3JlT3V0bGluZSgpO1xyXG5cdFx0dGhpcy5fb3V0bGluZUVsZW1lbnQgPSBlbGVtZW50O1xyXG5cdFx0dGhpcy5fb3V0bGluZVN0eWxlID0gZWxlbWVudC5zdHlsZS5vdXRsaW5lO1xyXG5cdFx0ZWxlbWVudC5zdHlsZS5vdXRsaW5lID0gJ25vbmUnO1xyXG5cdFx0TC5Eb21FdmVudC5vbih3aW5kb3csICdrZXlkb3duJywgTC5Eb21VdGlsLnJlc3RvcmVPdXRsaW5lLCB0aGlzKTtcclxuXHR9O1xyXG5cclxuXHQvLyBAZnVuY3Rpb24gcmVzdG9yZU91dGxpbmUoKVxyXG5cdC8vIENhbmNlbHMgdGhlIGVmZmVjdHMgb2YgYSBwcmV2aW91cyBbYEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZWBdKCkuXHJcblx0TC5Eb21VdGlsLnJlc3RvcmVPdXRsaW5lID0gZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9vdXRsaW5lRWxlbWVudCkgeyByZXR1cm47IH1cclxuXHRcdHRoaXMuX291dGxpbmVFbGVtZW50LnN0eWxlLm91dGxpbmUgPSB0aGlzLl9vdXRsaW5lU3R5bGU7XHJcblx0XHRkZWxldGUgdGhpcy5fb3V0bGluZUVsZW1lbnQ7XHJcblx0XHRkZWxldGUgdGhpcy5fb3V0bGluZVN0eWxlO1xyXG5cdFx0TC5Eb21FdmVudC5vZmYod2luZG93LCAna2V5ZG93bicsIEwuRG9tVXRpbC5yZXN0b3JlT3V0bGluZSwgdGhpcyk7XHJcblx0fTtcclxufSkoKTtcclxuXG5cblxuLyogQGNsYXNzIExhdExuZ1xyXG4gKiBAYWthIEwuTGF0TG5nXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYSBnZW9ncmFwaGljYWwgcG9pbnQgd2l0aCBhIGNlcnRhaW4gbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZS5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBgXHJcbiAqIHZhciBsYXRsbmcgPSBMLmxhdExuZyg1MC41LCAzMC41KTtcclxuICogYGBgXHJcbiAqXHJcbiAqIEFsbCBMZWFmbGV0IG1ldGhvZHMgdGhhdCBhY2NlcHQgTGF0TG5nIG9iamVjdHMgYWxzbyBhY2NlcHQgdGhlbSBpbiBhIHNpbXBsZSBBcnJheSBmb3JtIGFuZCBzaW1wbGUgb2JqZWN0IGZvcm0gKHVubGVzcyBub3RlZCBvdGhlcndpc2UpLCBzbyB0aGVzZSBsaW5lcyBhcmUgZXF1aXZhbGVudDpcclxuICpcclxuICogYGBgXHJcbiAqIG1hcC5wYW5UbyhbNTAsIDMwXSk7XHJcbiAqIG1hcC5wYW5Ubyh7bG9uOiAzMCwgbGF0OiA1MH0pO1xyXG4gKiBtYXAucGFuVG8oe2xhdDogNTAsIGxuZzogMzB9KTtcclxuICogbWFwLnBhblRvKEwubGF0TG5nKDUwLCAzMCkpO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5MLkxhdExuZyA9IGZ1bmN0aW9uIChsYXQsIGxuZywgYWx0KSB7XHJcblx0aWYgKGlzTmFOKGxhdCkgfHwgaXNOYU4obG5nKSkge1xyXG5cdFx0dGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIExhdExuZyBvYmplY3Q6ICgnICsgbGF0ICsgJywgJyArIGxuZyArICcpJyk7XHJcblx0fVxyXG5cclxuXHQvLyBAcHJvcGVydHkgbGF0OiBOdW1iZXJcclxuXHQvLyBMYXRpdHVkZSBpbiBkZWdyZWVzXHJcblx0dGhpcy5sYXQgPSArbGF0O1xyXG5cclxuXHQvLyBAcHJvcGVydHkgbG5nOiBOdW1iZXJcclxuXHQvLyBMb25naXR1ZGUgaW4gZGVncmVlc1xyXG5cdHRoaXMubG5nID0gK2xuZztcclxuXHJcblx0Ly8gQHByb3BlcnR5IGFsdDogTnVtYmVyXHJcblx0Ly8gQWx0aXR1ZGUgaW4gbWV0ZXJzIChvcHRpb25hbClcclxuXHRpZiAoYWx0ICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdHRoaXMuYWx0ID0gK2FsdDtcclxuXHR9XHJcbn07XHJcblxyXG5MLkxhdExuZy5wcm90b3R5cGUgPSB7XHJcblx0Ly8gQG1ldGhvZCBlcXVhbHMob3RoZXJMYXRMbmc6IExhdExuZywgbWF4TWFyZ2luPzogTnVtYmVyKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBnaXZlbiBgTGF0TG5nYCBwb2ludCBpcyBhdCB0aGUgc2FtZSBwb3NpdGlvbiAod2l0aGluIGEgc21hbGwgbWFyZ2luIG9mIGVycm9yKS4gVGhlIG1hcmdpbiBvZiBlcnJvciBjYW4gYmUgb3ZlcnJpZGVuIGJ5IHNldHRpbmcgYG1heE1hcmdpbmAgdG8gYSBzbWFsbCBudW1iZXIuXHJcblx0ZXF1YWxzOiBmdW5jdGlvbiAob2JqLCBtYXhNYXJnaW4pIHtcclxuXHRcdGlmICghb2JqKSB7IHJldHVybiBmYWxzZTsgfVxyXG5cclxuXHRcdG9iaiA9IEwubGF0TG5nKG9iaik7XHJcblxyXG5cdFx0dmFyIG1hcmdpbiA9IE1hdGgubWF4KFxyXG5cdFx0ICAgICAgICBNYXRoLmFicyh0aGlzLmxhdCAtIG9iai5sYXQpLFxyXG5cdFx0ICAgICAgICBNYXRoLmFicyh0aGlzLmxuZyAtIG9iai5sbmcpKTtcclxuXHJcblx0XHRyZXR1cm4gbWFyZ2luIDw9IChtYXhNYXJnaW4gPT09IHVuZGVmaW5lZCA/IDEuMEUtOSA6IG1heE1hcmdpbik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b1N0cmluZygpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBwb2ludCAoZm9yIGRlYnVnZ2luZyBwdXJwb3NlcykuXHJcblx0dG9TdHJpbmc6IGZ1bmN0aW9uIChwcmVjaXNpb24pIHtcclxuXHRcdHJldHVybiAnTGF0TG5nKCcgK1xyXG5cdFx0ICAgICAgICBMLlV0aWwuZm9ybWF0TnVtKHRoaXMubGF0LCBwcmVjaXNpb24pICsgJywgJyArXHJcblx0XHQgICAgICAgIEwuVXRpbC5mb3JtYXROdW0odGhpcy5sbmcsIHByZWNpc2lvbikgKyAnKSc7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBkaXN0YW5jZVRvKG90aGVyTGF0TG5nOiBMYXRMbmcpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBkaXN0YW5jZSAoaW4gbWV0ZXJzKSB0byB0aGUgZ2l2ZW4gYExhdExuZ2AgY2FsY3VsYXRlZCB1c2luZyB0aGUgW0hhdmVyc2luZSBmb3JtdWxhXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0hhdmVyc2luZV9mb3JtdWxhKS5cclxuXHRkaXN0YW5jZVRvOiBmdW5jdGlvbiAob3RoZXIpIHtcclxuXHRcdHJldHVybiBMLkNSUy5FYXJ0aC5kaXN0YW5jZSh0aGlzLCBMLmxhdExuZyhvdGhlcikpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgd3JhcCgpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIGEgbmV3IGBMYXRMbmdgIG9iamVjdCB3aXRoIHRoZSBsb25naXR1ZGUgd3JhcHBlZCBzbyBpdCdzIGFsd2F5cyBiZXR3ZWVuIC0xODAgYW5kICsxODAgZGVncmVlcy5cclxuXHR3cmFwOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gTC5DUlMuRWFydGgud3JhcExhdExuZyh0aGlzKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHRvQm91bmRzKHNpemVJbk1ldGVyczogTnVtYmVyKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyBhIG5ldyBgTGF0TG5nQm91bmRzYCBvYmplY3QgaW4gd2hpY2ggZWFjaCBib3VuZGFyeSBpcyBgc2l6ZUluTWV0ZXJzLzJgIG1ldGVycyBhcGFydCBmcm9tIHRoZSBgTGF0TG5nYC5cclxuXHR0b0JvdW5kczogZnVuY3Rpb24gKHNpemVJbk1ldGVycykge1xyXG5cdFx0dmFyIGxhdEFjY3VyYWN5ID0gMTgwICogc2l6ZUluTWV0ZXJzIC8gNDAwNzUwMTcsXHJcblx0XHQgICAgbG5nQWNjdXJhY3kgPSBsYXRBY2N1cmFjeSAvIE1hdGguY29zKChNYXRoLlBJIC8gMTgwKSAqIHRoaXMubGF0KTtcclxuXHJcblx0XHRyZXR1cm4gTC5sYXRMbmdCb3VuZHMoXHJcblx0XHQgICAgICAgIFt0aGlzLmxhdCAtIGxhdEFjY3VyYWN5LCB0aGlzLmxuZyAtIGxuZ0FjY3VyYWN5XSxcclxuXHRcdCAgICAgICAgW3RoaXMubGF0ICsgbGF0QWNjdXJhY3ksIHRoaXMubG5nICsgbG5nQWNjdXJhY3ldKTtcclxuXHR9LFxyXG5cclxuXHRjbG9uZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyh0aGlzLmxhdCwgdGhpcy5sbmcsIHRoaXMuYWx0KTtcclxuXHR9XHJcbn07XHJcblxyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nKGxhdGl0dWRlOiBOdW1iZXIsIGxvbmdpdHVkZTogTnVtYmVyLCBhbHRpdHVkZT86IE51bWJlcik6IExhdExuZ1xyXG4vLyBDcmVhdGVzIGFuIG9iamVjdCByZXByZXNlbnRpbmcgYSBnZW9ncmFwaGljYWwgcG9pbnQgd2l0aCB0aGUgZ2l2ZW4gbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSAoYW5kIG9wdGlvbmFsbHkgYWx0aXR1ZGUpLlxyXG5cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nKGNvb3JkczogQXJyYXkpOiBMYXRMbmdcclxuLy8gRXhwZWN0cyBhbiBhcnJheSBvZiB0aGUgZm9ybSBgW051bWJlciwgTnVtYmVyXWAgb3IgYFtOdW1iZXIsIE51bWJlciwgTnVtYmVyXWAgaW5zdGVhZC5cclxuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLmxhdExuZyhjb29yZHM6IE9iamVjdCk6IExhdExuZ1xyXG4vLyBFeHBlY3RzIGFuIHBsYWluIG9iamVjdCBvZiB0aGUgZm9ybSBge2xhdDogTnVtYmVyLCBsbmc6IE51bWJlcn1gIG9yIGB7bGF0OiBOdW1iZXIsIGxuZzogTnVtYmVyLCBhbHQ6IE51bWJlcn1gIGluc3RlYWQuXHJcblxyXG5MLmxhdExuZyA9IGZ1bmN0aW9uIChhLCBiLCBjKSB7XHJcblx0aWYgKGEgaW5zdGFuY2VvZiBMLkxhdExuZykge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdGlmIChMLlV0aWwuaXNBcnJheShhKSAmJiB0eXBlb2YgYVswXSAhPT0gJ29iamVjdCcpIHtcclxuXHRcdGlmIChhLmxlbmd0aCA9PT0gMykge1xyXG5cdFx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKGFbMF0sIGFbMV0sIGFbMl0pO1xyXG5cdFx0fVxyXG5cdFx0aWYgKGEubGVuZ3RoID09PSAyKSB7XHJcblx0XHRcdHJldHVybiBuZXcgTC5MYXRMbmcoYVswXSwgYVsxXSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gbnVsbDtcclxuXHR9XHJcblx0aWYgKGEgPT09IHVuZGVmaW5lZCB8fCBhID09PSBudWxsKSB7XHJcblx0XHRyZXR1cm4gYTtcclxuXHR9XHJcblx0aWYgKHR5cGVvZiBhID09PSAnb2JqZWN0JyAmJiAnbGF0JyBpbiBhKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nKGEubGF0LCAnbG5nJyBpbiBhID8gYS5sbmcgOiBhLmxvbiwgYS5hbHQpO1xyXG5cdH1cclxuXHRpZiAoYiA9PT0gdW5kZWZpbmVkKSB7XHJcblx0XHRyZXR1cm4gbnVsbDtcclxuXHR9XHJcblx0cmV0dXJuIG5ldyBMLkxhdExuZyhhLCBiLCBjKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIExhdExuZ0JvdW5kc1xyXG4gKiBAYWthIEwuTGF0TG5nQm91bmRzXHJcbiAqXHJcbiAqIFJlcHJlc2VudHMgYSByZWN0YW5ndWxhciBnZW9ncmFwaGljYWwgYXJlYSBvbiBhIG1hcC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIGNvcm5lcjEgPSBMLmxhdExuZyg0MC43MTIsIC03NC4yMjcpLFxyXG4gKiBjb3JuZXIyID0gTC5sYXRMbmcoNDAuNzc0LCAtNzQuMTI1KSxcclxuICogYm91bmRzID0gTC5sYXRMbmdCb3VuZHMoY29ybmVyMSwgY29ybmVyMik7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBBbGwgTGVhZmxldCBtZXRob2RzIHRoYXQgYWNjZXB0IExhdExuZ0JvdW5kcyBvYmplY3RzIGFsc28gYWNjZXB0IHRoZW0gaW4gYSBzaW1wbGUgQXJyYXkgZm9ybSAodW5sZXNzIG5vdGVkIG90aGVyd2lzZSksIHNvIHRoZSBib3VuZHMgZXhhbXBsZSBhYm92ZSBjYW4gYmUgcGFzc2VkIGxpa2UgdGhpczpcclxuICpcclxuICogYGBganNcclxuICogbWFwLmZpdEJvdW5kcyhbXHJcbiAqIFx0WzQwLjcxMiwgLTc0LjIyN10sXHJcbiAqIFx0WzQwLjc3NCwgLTc0LjEyNV1cclxuICogXSk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBDYXV0aW9uOiBpZiB0aGUgYXJlYSBjcm9zc2VzIHRoZSBhbnRpbWVyaWRpYW4gKG9mdGVuIGNvbmZ1c2VkIHdpdGggdGhlIEludGVybmF0aW9uYWwgRGF0ZSBMaW5lKSwgeW91IG11c3Qgc3BlY2lmeSBjb3JuZXJzIF9vdXRzaWRlXyB0aGUgWy0xODAsIDE4MF0gZGVncmVlcyBsb25naXR1ZGUgcmFuZ2UuXHJcbiAqL1xyXG5cclxuTC5MYXRMbmdCb3VuZHMgPSBmdW5jdGlvbiAoY29ybmVyMSwgY29ybmVyMikgeyAvLyAoTGF0TG5nLCBMYXRMbmcpIG9yIChMYXRMbmdbXSlcclxuXHRpZiAoIWNvcm5lcjEpIHsgcmV0dXJuOyB9XHJcblxyXG5cdHZhciBsYXRsbmdzID0gY29ybmVyMiA/IFtjb3JuZXIxLCBjb3JuZXIyXSA6IGNvcm5lcjE7XHJcblxyXG5cdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsYXRsbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHR0aGlzLmV4dGVuZChsYXRsbmdzW2ldKTtcclxuXHR9XHJcbn07XHJcblxyXG5MLkxhdExuZ0JvdW5kcy5wcm90b3R5cGUgPSB7XHJcblxyXG5cdC8vIEBtZXRob2QgZXh0ZW5kKGxhdGxuZzogTGF0TG5nKTogdGhpc1xyXG5cdC8vIEV4dGVuZCB0aGUgYm91bmRzIHRvIGNvbnRhaW4gdGhlIGdpdmVuIHBvaW50XHJcblxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBtZXRob2QgZXh0ZW5kKG90aGVyQm91bmRzOiBMYXRMbmdCb3VuZHMpOiB0aGlzXHJcblx0Ly8gRXh0ZW5kIHRoZSBib3VuZHMgdG8gY29udGFpbiB0aGUgZ2l2ZW4gYm91bmRzXHJcblx0ZXh0ZW5kOiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgc3cyLCBuZTI7XHJcblxyXG5cdFx0aWYgKG9iaiBpbnN0YW5jZW9mIEwuTGF0TG5nKSB7XHJcblx0XHRcdHN3MiA9IG9iajtcclxuXHRcdFx0bmUyID0gb2JqO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAob2JqIGluc3RhbmNlb2YgTC5MYXRMbmdCb3VuZHMpIHtcclxuXHRcdFx0c3cyID0gb2JqLl9zb3V0aFdlc3Q7XHJcblx0XHRcdG5lMiA9IG9iai5fbm9ydGhFYXN0O1xyXG5cclxuXHRcdFx0aWYgKCFzdzIgfHwgIW5lMikgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHJldHVybiBvYmogPyB0aGlzLmV4dGVuZChMLmxhdExuZyhvYmopIHx8IEwubGF0TG5nQm91bmRzKG9iaikpIDogdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXN3ICYmICFuZSkge1xyXG5cdFx0XHR0aGlzLl9zb3V0aFdlc3QgPSBuZXcgTC5MYXRMbmcoc3cyLmxhdCwgc3cyLmxuZyk7XHJcblx0XHRcdHRoaXMuX25vcnRoRWFzdCA9IG5ldyBMLkxhdExuZyhuZTIubGF0LCBuZTIubG5nKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHN3LmxhdCA9IE1hdGgubWluKHN3Mi5sYXQsIHN3LmxhdCk7XHJcblx0XHRcdHN3LmxuZyA9IE1hdGgubWluKHN3Mi5sbmcsIHN3LmxuZyk7XHJcblx0XHRcdG5lLmxhdCA9IE1hdGgubWF4KG5lMi5sYXQsIG5lLmxhdCk7XHJcblx0XHRcdG5lLmxuZyA9IE1hdGgubWF4KG5lMi5sbmcsIG5lLmxuZyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwYWQoYnVmZmVyUmF0aW86IE51bWJlcik6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgYmlnZ2VyIGJvdW5kcyBjcmVhdGVkIGJ5IGV4dGVuZGluZyB0aGUgY3VycmVudCBib3VuZHMgYnkgYSBnaXZlbiBwZXJjZW50YWdlIGluIGVhY2ggZGlyZWN0aW9uLlxyXG5cdHBhZDogZnVuY3Rpb24gKGJ1ZmZlclJhdGlvKSB7XHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgaGVpZ2h0QnVmZmVyID0gTWF0aC5hYnMoc3cubGF0IC0gbmUubGF0KSAqIGJ1ZmZlclJhdGlvLFxyXG5cdFx0ICAgIHdpZHRoQnVmZmVyID0gTWF0aC5hYnMoc3cubG5nIC0gbmUubG5nKSAqIGJ1ZmZlclJhdGlvO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmdCb3VuZHMoXHJcblx0XHQgICAgICAgIG5ldyBMLkxhdExuZyhzdy5sYXQgLSBoZWlnaHRCdWZmZXIsIHN3LmxuZyAtIHdpZHRoQnVmZmVyKSxcclxuXHRcdCAgICAgICAgbmV3IEwuTGF0TG5nKG5lLmxhdCArIGhlaWdodEJ1ZmZlciwgbmUubG5nICsgd2lkdGhCdWZmZXIpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldENlbnRlcigpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBjZW50ZXIgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXRDZW50ZXI6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcoXHJcblx0XHQgICAgICAgICh0aGlzLl9zb3V0aFdlc3QubGF0ICsgdGhpcy5fbm9ydGhFYXN0LmxhdCkgLyAyLFxyXG5cdFx0ICAgICAgICAodGhpcy5fc291dGhXZXN0LmxuZyArIHRoaXMuX25vcnRoRWFzdC5sbmcpIC8gMik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTb3V0aFdlc3QoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgc291dGgtd2VzdCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldFNvdXRoV2VzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3NvdXRoV2VzdDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldE5vcnRoRWFzdCgpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIHRoZSBub3J0aC1lYXN0IHBvaW50IG9mIHRoZSBib3VuZHMuXHJcblx0Z2V0Tm9ydGhFYXN0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbm9ydGhFYXN0O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Tm9ydGhXZXN0KCk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIG5vcnRoLXdlc3QgcG9pbnQgb2YgdGhlIGJvdW5kcy5cclxuXHRnZXROb3J0aFdlc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcodGhpcy5nZXROb3J0aCgpLCB0aGlzLmdldFdlc3QoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTb3V0aEVhc3QoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgc291dGgtZWFzdCBwb2ludCBvZiB0aGUgYm91bmRzLlxyXG5cdGdldFNvdXRoRWFzdDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyh0aGlzLmdldFNvdXRoKCksIHRoaXMuZ2V0RWFzdCgpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFdlc3QoKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgd2VzdCBsb25naXR1ZGUgb2YgdGhlIGJvdW5kc1xyXG5cdGdldFdlc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9zb3V0aFdlc3QubG5nO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0U291dGgoKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgc291dGggbGF0aXR1ZGUgb2YgdGhlIGJvdW5kc1xyXG5cdGdldFNvdXRoOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fc291dGhXZXN0LmxhdDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldEVhc3QoKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZWFzdCBsb25naXR1ZGUgb2YgdGhlIGJvdW5kc1xyXG5cdGdldEVhc3Q6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9ub3J0aEVhc3QubG5nO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Tm9ydGgoKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbm9ydGggbGF0aXR1ZGUgb2YgdGhlIGJvdW5kc1xyXG5cdGdldE5vcnRoOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbm9ydGhFYXN0LmxhdDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zKG90aGVyQm91bmRzOiBMYXRMbmdCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBjb250YWlucyB0aGUgZ2l2ZW4gb25lLlxyXG5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5zIChsYXRsbmc6IExhdExuZyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGNvbnRhaW5zIHRoZSBnaXZlbiBwb2ludC5cclxuXHRjb250YWluczogZnVuY3Rpb24gKG9iaikgeyAvLyAoTGF0TG5nQm91bmRzKSBvciAoTGF0TG5nKSAtPiBCb29sZWFuXHJcblx0XHRpZiAodHlwZW9mIG9ialswXSA9PT0gJ251bWJlcicgfHwgb2JqIGluc3RhbmNlb2YgTC5MYXRMbmcgfHwgJ2xhdCcgaW4gb2JqKSB7XHJcblx0XHRcdG9iaiA9IEwubGF0TG5nKG9iaik7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRvYmogPSBMLmxhdExuZ0JvdW5kcyhvYmopO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBzdzIsIG5lMjtcclxuXHJcblx0XHRpZiAob2JqIGluc3RhbmNlb2YgTC5MYXRMbmdCb3VuZHMpIHtcclxuXHRcdFx0c3cyID0gb2JqLmdldFNvdXRoV2VzdCgpO1xyXG5cdFx0XHRuZTIgPSBvYmouZ2V0Tm9ydGhFYXN0KCk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRzdzIgPSBuZTIgPSBvYmo7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIChzdzIubGF0ID49IHN3LmxhdCkgJiYgKG5lMi5sYXQgPD0gbmUubGF0KSAmJlxyXG5cdFx0ICAgICAgIChzdzIubG5nID49IHN3LmxuZykgJiYgKG5lMi5sbmcgPD0gbmUubG5nKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGludGVyc2VjdHMob3RoZXJCb3VuZHM6IExhdExuZ0JvdW5kcyk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgcmVjdGFuZ2xlIGludGVyc2VjdHMgdGhlIGdpdmVuIGJvdW5kcy4gVHdvIGJvdW5kcyBpbnRlcnNlY3QgaWYgdGhleSBoYXZlIGF0IGxlYXN0IG9uZSBwb2ludCBpbiBjb21tb24uXHJcblx0aW50ZXJzZWN0czogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0Ym91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHR2YXIgc3cgPSB0aGlzLl9zb3V0aFdlc3QsXHJcblx0XHQgICAgbmUgPSB0aGlzLl9ub3J0aEVhc3QsXHJcblx0XHQgICAgc3cyID0gYm91bmRzLmdldFNvdXRoV2VzdCgpLFxyXG5cdFx0ICAgIG5lMiA9IGJvdW5kcy5nZXROb3J0aEVhc3QoKSxcclxuXHJcblx0XHQgICAgbGF0SW50ZXJzZWN0cyA9IChuZTIubGF0ID49IHN3LmxhdCkgJiYgKHN3Mi5sYXQgPD0gbmUubGF0KSxcclxuXHRcdCAgICBsbmdJbnRlcnNlY3RzID0gKG5lMi5sbmcgPj0gc3cubG5nKSAmJiAoc3cyLmxuZyA8PSBuZS5sbmcpO1xyXG5cclxuXHRcdHJldHVybiBsYXRJbnRlcnNlY3RzICYmIGxuZ0ludGVyc2VjdHM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBvdmVybGFwcyhvdGhlckJvdW5kczogQm91bmRzKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSByZWN0YW5nbGUgb3ZlcmxhcHMgdGhlIGdpdmVuIGJvdW5kcy4gVHdvIGJvdW5kcyBvdmVybGFwIGlmIHRoZWlyIGludGVyc2VjdGlvbiBpcyBhbiBhcmVhLlxyXG5cdG92ZXJsYXBzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHRib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHZhciBzdyA9IHRoaXMuX3NvdXRoV2VzdCxcclxuXHRcdCAgICBuZSA9IHRoaXMuX25vcnRoRWFzdCxcclxuXHRcdCAgICBzdzIgPSBib3VuZHMuZ2V0U291dGhXZXN0KCksXHJcblx0XHQgICAgbmUyID0gYm91bmRzLmdldE5vcnRoRWFzdCgpLFxyXG5cclxuXHRcdCAgICBsYXRPdmVybGFwcyA9IChuZTIubGF0ID4gc3cubGF0KSAmJiAoc3cyLmxhdCA8IG5lLmxhdCksXHJcblx0XHQgICAgbG5nT3ZlcmxhcHMgPSAobmUyLmxuZyA+IHN3LmxuZykgJiYgKHN3Mi5sbmcgPCBuZS5sbmcpO1xyXG5cclxuXHRcdHJldHVybiBsYXRPdmVybGFwcyAmJiBsbmdPdmVybGFwcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHRvQkJveFN0cmluZygpOiBTdHJpbmdcclxuXHQvLyBSZXR1cm5zIGEgc3RyaW5nIHdpdGggYm91bmRpbmcgYm94IGNvb3JkaW5hdGVzIGluIGEgJ3NvdXRod2VzdF9sbmcsc291dGh3ZXN0X2xhdCxub3J0aGVhc3RfbG5nLG5vcnRoZWFzdF9sYXQnIGZvcm1hdC4gVXNlZnVsIGZvciBzZW5kaW5nIHJlcXVlc3RzIHRvIHdlYiBzZXJ2aWNlcyB0aGF0IHJldHVybiBnZW8gZGF0YS5cclxuXHR0b0JCb3hTdHJpbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBbdGhpcy5nZXRXZXN0KCksIHRoaXMuZ2V0U291dGgoKSwgdGhpcy5nZXRFYXN0KCksIHRoaXMuZ2V0Tm9ydGgoKV0uam9pbignLCcpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZXF1YWxzKG90aGVyQm91bmRzOiBMYXRMbmdCb3VuZHMpOiBCb29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHJlY3RhbmdsZSBpcyBlcXVpdmFsZW50ICh3aXRoaW4gYSBzbWFsbCBtYXJnaW4gb2YgZXJyb3IpIHRvIHRoZSBnaXZlbiBib3VuZHMuXHJcblx0ZXF1YWxzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHRpZiAoIWJvdW5kcykgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHRib3VuZHMgPSBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzLl9zb3V0aFdlc3QuZXF1YWxzKGJvdW5kcy5nZXRTb3V0aFdlc3QoKSkgJiZcclxuXHRcdCAgICAgICB0aGlzLl9ub3J0aEVhc3QuZXF1YWxzKGJvdW5kcy5nZXROb3J0aEVhc3QoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpc1ZhbGlkKCk6IEJvb2xlYW5cclxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgYm91bmRzIGFyZSBwcm9wZXJseSBpbml0aWFsaXplZC5cclxuXHRpc1ZhbGlkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gISEodGhpcy5fc291dGhXZXN0ICYmIHRoaXMuX25vcnRoRWFzdCk7XHJcblx0fVxyXG59O1xyXG5cclxuLy8gVE9ETyBJbnRlcm5hdGlvbmFsIGRhdGUgbGluZT9cclxuXHJcbi8vIEBmYWN0b3J5IEwubGF0TG5nQm91bmRzKGNvcm5lcjE6IExhdExuZywgY29ybmVyMjogTGF0TG5nKVxyXG4vLyBDcmVhdGVzIGEgYExhdExuZ0JvdW5kc2Agb2JqZWN0IGJ5IGRlZmluaW5nIHR3byBkaWFnb25hbGx5IG9wcG9zaXRlIGNvcm5lcnMgb2YgdGhlIHJlY3RhbmdsZS5cclxuXHJcbi8vIEBhbHRlcm5hdGl2ZVxyXG4vLyBAZmFjdG9yeSBMLmxhdExuZ0JvdW5kcyhsYXRsbmdzOiBMYXRMbmdbXSlcclxuLy8gQ3JlYXRlcyBhIGBMYXRMbmdCb3VuZHNgIG9iamVjdCBkZWZpbmVkIGJ5IHRoZSBnZW9ncmFwaGljYWwgcG9pbnRzIGl0IGNvbnRhaW5zLiBWZXJ5IHVzZWZ1bCBmb3Igem9vbWluZyB0aGUgbWFwIHRvIGZpdCBhIHBhcnRpY3VsYXIgc2V0IG9mIGxvY2F0aW9ucyB3aXRoIFtgZml0Qm91bmRzYF0oI21hcC1maXRib3VuZHMpLlxyXG5MLmxhdExuZ0JvdW5kcyA9IGZ1bmN0aW9uIChhLCBiKSB7XHJcblx0aWYgKGEgaW5zdGFuY2VvZiBMLkxhdExuZ0JvdW5kcykge1xyXG5cdFx0cmV0dXJuIGE7XHJcblx0fVxyXG5cdHJldHVybiBuZXcgTC5MYXRMbmdCb3VuZHMoYSwgYik7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgUHJvamVjdGlvblxyXG4gKiBAc2VjdGlvblxyXG4gKiBMZWFmbGV0IGNvbWVzIHdpdGggYSBzZXQgb2YgYWxyZWFkeSBkZWZpbmVkIFByb2plY3Rpb25zIG91dCBvZiB0aGUgYm94OlxyXG4gKlxyXG4gKiBAcHJvamVjdGlvbiBMLlByb2plY3Rpb24uTG9uTGF0XHJcbiAqXHJcbiAqIEVxdWlyZWN0YW5ndWxhciwgb3IgUGxhdGUgQ2FycmVlIHByb2plY3Rpb24g4oCUIHRoZSBtb3N0IHNpbXBsZSBwcm9qZWN0aW9uLFxyXG4gKiBtb3N0bHkgdXNlZCBieSBHSVMgZW50aHVzaWFzdHMuIERpcmVjdGx5IG1hcHMgYHhgIGFzIGxvbmdpdHVkZSwgYW5kIGB5YCBhc1xyXG4gKiBsYXRpdHVkZS4gQWxzbyBzdWl0YWJsZSBmb3IgZmxhdCB3b3JsZHMsIGUuZy4gZ2FtZSBtYXBzLiBVc2VkIGJ5IHRoZVxyXG4gKiBgRVBTRzozMzk1YCBhbmQgYFNpbXBsZWAgQ1JTLlxyXG4gKi9cclxuXHJcbkwuUHJvamVjdGlvbiA9IHt9O1xyXG5cclxuTC5Qcm9qZWN0aW9uLkxvbkxhdCA9IHtcclxuXHRwcm9qZWN0OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQobGF0bG5nLmxuZywgbGF0bG5nLmxhdCk7XHJcblx0fSxcclxuXHJcblx0dW5wcm9qZWN0OiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcocG9pbnQueSwgcG9pbnQueCk7XHJcblx0fSxcclxuXHJcblx0Ym91bmRzOiBMLmJvdW5kcyhbLTE4MCwgLTkwXSwgWzE4MCwgOTBdKVxyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIFByb2plY3Rpb25cclxuICogQHByb2plY3Rpb24gTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yXHJcbiAqXHJcbiAqIFNwaGVyaWNhbCBNZXJjYXRvciBwcm9qZWN0aW9uIOKAlCB0aGUgbW9zdCBjb21tb24gcHJvamVjdGlvbiBmb3Igb25saW5lIG1hcHMsXHJcbiAqIHVzZWQgYnkgYWxtb3N0IGFsbCBmcmVlIGFuZCBjb21tZXJjaWFsIHRpbGUgcHJvdmlkZXJzLiBBc3N1bWVzIHRoYXQgRWFydGggaXNcclxuICogYSBzcGhlcmUuIFVzZWQgYnkgdGhlIGBFUFNHOjM4NTdgIENSUy5cclxuICovXHJcblxyXG5MLlByb2plY3Rpb24uU3BoZXJpY2FsTWVyY2F0b3IgPSB7XHJcblxyXG5cdFI6IDYzNzgxMzcsXHJcblx0TUFYX0xBVElUVURFOiA4NS4wNTExMjg3Nzk4LFxyXG5cclxuXHRwcm9qZWN0OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHR2YXIgZCA9IE1hdGguUEkgLyAxODAsXHJcblx0XHQgICAgbWF4ID0gdGhpcy5NQVhfTEFUSVRVREUsXHJcblx0XHQgICAgbGF0ID0gTWF0aC5tYXgoTWF0aC5taW4obWF4LCBsYXRsbmcubGF0KSwgLW1heCksXHJcblx0XHQgICAgc2luID0gTWF0aC5zaW4obGF0ICogZCk7XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KFxyXG5cdFx0XHRcdHRoaXMuUiAqIGxhdGxuZy5sbmcgKiBkLFxyXG5cdFx0XHRcdHRoaXMuUiAqIE1hdGgubG9nKCgxICsgc2luKSAvICgxIC0gc2luKSkgLyAyKTtcclxuXHR9LFxyXG5cclxuXHR1bnByb2plY3Q6IGZ1bmN0aW9uIChwb2ludCkge1xyXG5cdFx0dmFyIGQgPSAxODAgLyBNYXRoLlBJO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcoXHJcblx0XHRcdCgyICogTWF0aC5hdGFuKE1hdGguZXhwKHBvaW50LnkgLyB0aGlzLlIpKSAtIChNYXRoLlBJIC8gMikpICogZCxcclxuXHRcdFx0cG9pbnQueCAqIGQgLyB0aGlzLlIpO1xyXG5cdH0sXHJcblxyXG5cdGJvdW5kczogKGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBkID0gNjM3ODEzNyAqIE1hdGguUEk7XHJcblx0XHRyZXR1cm4gTC5ib3VuZHMoWy1kLCAtZF0sIFtkLCBkXSk7XHJcblx0fSkoKVxyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQ1JTXHJcbiAqIEBha2EgTC5DUlNcclxuICogQWJzdHJhY3QgY2xhc3MgdGhhdCBkZWZpbmVzIGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbXMgZm9yIHByb2plY3RpbmdcclxuICogZ2VvZ3JhcGhpY2FsIHBvaW50cyBpbnRvIHBpeGVsIChzY3JlZW4pIGNvb3JkaW5hdGVzIGFuZCBiYWNrIChhbmQgdG9cclxuICogY29vcmRpbmF0ZXMgaW4gb3RoZXIgdW5pdHMgZm9yIFtXTVNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dlYl9NYXBfU2VydmljZSkgc2VydmljZXMpLiBTZWVcclxuICogW3NwYXRpYWwgcmVmZXJlbmNlIHN5c3RlbV0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db29yZGluYXRlX3JlZmVyZW5jZV9zeXN0ZW0pLlxyXG4gKlxyXG4gKiBMZWFmbGV0IGRlZmluZXMgdGhlIG1vc3QgdXN1YWwgQ1JTcyBieSBkZWZhdWx0LiBJZiB5b3Ugd2FudCB0byB1c2UgYVxyXG4gKiBDUlMgbm90IGRlZmluZWQgYnkgZGVmYXVsdCwgdGFrZSBhIGxvb2sgYXQgdGhlXHJcbiAqIFtQcm9qNExlYWZsZXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9rYXJ0ZW5hL1Byb2o0TGVhZmxldCkgcGx1Z2luLlxyXG4gKi9cclxuXHJcbkwuQ1JTID0ge1xyXG5cdC8vIEBtZXRob2QgbGF0TG5nVG9Qb2ludChsYXRsbmc6IExhdExuZywgem9vbTogTnVtYmVyKTogUG9pbnRcclxuXHQvLyBQcm9qZWN0cyBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZXMgaW50byBwaXhlbCBjb29yZGluYXRlcyBmb3IgYSBnaXZlbiB6b29tLlxyXG5cdGxhdExuZ1RvUG9pbnQ6IGZ1bmN0aW9uIChsYXRsbmcsIHpvb20pIHtcclxuXHRcdHZhciBwcm9qZWN0ZWRQb2ludCA9IHRoaXMucHJvamVjdGlvbi5wcm9qZWN0KGxhdGxuZyksXHJcblx0XHQgICAgc2NhbGUgPSB0aGlzLnNjYWxlKHpvb20pO1xyXG5cclxuXHRcdHJldHVybiB0aGlzLnRyYW5zZm9ybWF0aW9uLl90cmFuc2Zvcm0ocHJvamVjdGVkUG9pbnQsIHNjYWxlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHBvaW50VG9MYXRMbmcocG9pbnQ6IFBvaW50LCB6b29tOiBOdW1iZXIpOiBMYXRMbmdcclxuXHQvLyBUaGUgaW52ZXJzZSBvZiBgbGF0TG5nVG9Qb2ludGAuIFByb2plY3RzIHBpeGVsIGNvb3JkaW5hdGVzIG9uIGEgZ2l2ZW5cclxuXHQvLyB6b29tIGludG8gZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzLlxyXG5cdHBvaW50VG9MYXRMbmc6IGZ1bmN0aW9uIChwb2ludCwgem9vbSkge1xyXG5cdFx0dmFyIHNjYWxlID0gdGhpcy5zY2FsZSh6b29tKSxcclxuXHRcdCAgICB1bnRyYW5zZm9ybWVkUG9pbnQgPSB0aGlzLnRyYW5zZm9ybWF0aW9uLnVudHJhbnNmb3JtKHBvaW50LCBzY2FsZSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMucHJvamVjdGlvbi51bnByb2plY3QodW50cmFuc2Zvcm1lZFBvaW50KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHByb2plY3QobGF0bG5nOiBMYXRMbmcpOiBQb2ludFxyXG5cdC8vIFByb2plY3RzIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlcyBpbnRvIGNvb3JkaW5hdGVzIGluIHVuaXRzIGFjY2VwdGVkIGZvclxyXG5cdC8vIHRoaXMgQ1JTIChlLmcuIG1ldGVycyBmb3IgRVBTRzozODU3LCBmb3IgcGFzc2luZyBpdCB0byBXTVMgc2VydmljZXMpLlxyXG5cdHByb2plY3Q6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLnByb2plY3Rpb24ucHJvamVjdChsYXRsbmcpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdW5wcm9qZWN0KHBvaW50OiBQb2ludCk6IExhdExuZ1xyXG5cdC8vIEdpdmVuIGEgcHJvamVjdGVkIGNvb3JkaW5hdGUgcmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyBMYXRMbmcuXHJcblx0Ly8gVGhlIGludmVyc2Ugb2YgYHByb2plY3RgLlxyXG5cdHVucHJvamVjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0aW9uLnVucHJvamVjdChwb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzY2FsZSh6b29tOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBzY2FsZSB1c2VkIHdoZW4gdHJhbnNmb3JtaW5nIHByb2plY3RlZCBjb29yZGluYXRlcyBpbnRvXHJcblx0Ly8gcGl4ZWwgY29vcmRpbmF0ZXMgZm9yIGEgcGFydGljdWxhciB6b29tLiBGb3IgZXhhbXBsZSwgaXQgcmV0dXJuc1xyXG5cdC8vIGAyNTYgKiAyXnpvb21gIGZvciBNZXJjYXRvci1iYXNlZCBDUlMuXHJcblx0c2NhbGU6IGZ1bmN0aW9uICh6b29tKSB7XHJcblx0XHRyZXR1cm4gMjU2ICogTWF0aC5wb3coMiwgem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB6b29tKHNjYWxlOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBJbnZlcnNlIG9mIGBzY2FsZSgpYCwgcmV0dXJucyB0aGUgem9vbSBsZXZlbCBjb3JyZXNwb25kaW5nIHRvIGEgc2NhbGVcclxuXHQvLyBmYWN0b3Igb2YgYHNjYWxlYC5cclxuXHR6b29tOiBmdW5jdGlvbiAoc2NhbGUpIHtcclxuXHRcdHJldHVybiBNYXRoLmxvZyhzY2FsZSAvIDI1NikgLyBNYXRoLkxOMjtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldFByb2plY3RlZEJvdW5kcyh6b29tOiBOdW1iZXIpOiBCb3VuZHNcclxuXHQvLyBSZXR1cm5zIHRoZSBwcm9qZWN0aW9uJ3MgYm91bmRzIHNjYWxlZCBhbmQgdHJhbnNmb3JtZWQgZm9yIHRoZSBwcm92aWRlZCBgem9vbWAuXHJcblx0Z2V0UHJvamVjdGVkQm91bmRzOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0aWYgKHRoaXMuaW5maW5pdGUpIHsgcmV0dXJuIG51bGw7IH1cclxuXHJcblx0XHR2YXIgYiA9IHRoaXMucHJvamVjdGlvbi5ib3VuZHMsXHJcblx0XHQgICAgcyA9IHRoaXMuc2NhbGUoem9vbSksXHJcblx0XHQgICAgbWluID0gdGhpcy50cmFuc2Zvcm1hdGlvbi50cmFuc2Zvcm0oYi5taW4sIHMpLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMudHJhbnNmb3JtYXRpb24udHJhbnNmb3JtKGIubWF4LCBzKTtcclxuXHJcblx0XHRyZXR1cm4gTC5ib3VuZHMobWluLCBtYXgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZGlzdGFuY2UobGF0bG5nMTogTGF0TG5nLCBsYXRsbmcyOiBMYXRMbmcpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHR3byBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZXMuXHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBjb2RlOiBTdHJpbmdcclxuXHQvLyBTdGFuZGFyZCBjb2RlIG5hbWUgb2YgdGhlIENSUyBwYXNzZWQgaW50byBXTVMgc2VydmljZXMgKGUuZy4gYCdFUFNHOjM4NTcnYClcclxuXHQvL1xyXG5cdC8vIEBwcm9wZXJ0eSB3cmFwTG5nOiBOdW1iZXJbXVxyXG5cdC8vIEFuIGFycmF5IG9mIHR3byBudW1iZXJzIGRlZmluaW5nIHdoZXRoZXIgdGhlIGxvbmdpdHVkZSAoaG9yaXpvbnRhbCkgY29vcmRpbmF0ZVxyXG5cdC8vIGF4aXMgd3JhcHMgYXJvdW5kIGEgZ2l2ZW4gcmFuZ2UgYW5kIGhvdy4gRGVmYXVsdHMgdG8gYFstMTgwLCAxODBdYCBpbiBtb3N0XHJcblx0Ly8gZ2VvZ3JhcGhpY2FsIENSU3MuIElmIGB1bmRlZmluZWRgLCB0aGUgbG9uZ2l0dWRlIGF4aXMgZG9lcyBub3Qgd3JhcCBhcm91bmQuXHJcblx0Ly9cclxuXHQvLyBAcHJvcGVydHkgd3JhcExhdDogTnVtYmVyW11cclxuXHQvLyBMaWtlIGB3cmFwTG5nYCwgYnV0IGZvciB0aGUgbGF0aXR1ZGUgKHZlcnRpY2FsKSBheGlzLlxyXG5cclxuXHQvLyB3cmFwTG5nOiBbbWluLCBtYXhdLFxyXG5cdC8vIHdyYXBMYXQ6IFttaW4sIG1heF0sXHJcblxyXG5cdC8vIEBwcm9wZXJ0eSBpbmZpbml0ZTogQm9vbGVhblxyXG5cdC8vIElmIHRydWUsIHRoZSBjb29yZGluYXRlIHNwYWNlIHdpbGwgYmUgdW5ib3VuZGVkIChpbmZpbml0ZSBpbiBib3RoIGF4ZXMpXHJcblx0aW5maW5pdGU6IGZhbHNlLFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXBMYXRMbmcobGF0bG5nOiBMYXRMbmcpOiBMYXRMbmdcclxuXHQvLyBSZXR1cm5zIGEgYExhdExuZ2Agd2hlcmUgbGF0IGFuZCBsbmcgaGFzIGJlZW4gd3JhcHBlZCBhY2NvcmRpbmcgdG8gdGhlXHJcblx0Ly8gQ1JTJ3MgYHdyYXBMYXRgIGFuZCBgd3JhcExuZ2AgcHJvcGVydGllcywgaWYgdGhleSBhcmUgb3V0c2lkZSB0aGUgQ1JTJ3MgYm91bmRzLlxyXG5cdC8vIE9ubHkgYWNjZXB0cyBhY3R1YWwgYEwuTGF0TG5nYCBpbnN0YW5jZXMsIG5vdCBhcnJheXMuXHJcblx0d3JhcExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIGxuZyA9IHRoaXMud3JhcExuZyA/IEwuVXRpbC53cmFwTnVtKGxhdGxuZy5sbmcsIHRoaXMud3JhcExuZywgdHJ1ZSkgOiBsYXRsbmcubG5nLFxyXG5cdFx0ICAgIGxhdCA9IHRoaXMud3JhcExhdCA/IEwuVXRpbC53cmFwTnVtKGxhdGxuZy5sYXQsIHRoaXMud3JhcExhdCwgdHJ1ZSkgOiBsYXRsbmcubGF0LFxyXG5cdFx0ICAgIGFsdCA9IGxhdGxuZy5hbHQ7XHJcblxyXG5cdFx0cmV0dXJuIEwubGF0TG5nKGxhdCwgbG5nLCBhbHQpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgd3JhcExhdExuZ0JvdW5kcyhib3VuZHM6IExhdExuZ0JvdW5kcyk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgYSBgTGF0TG5nQm91bmRzYCB3aXRoIHRoZSBzYW1lIHNpemUgYXMgdGhlIGdpdmVuIG9uZSwgZW5zdXJpbmdcclxuXHQvLyB0aGF0IGl0cyBjZW50ZXIgaXMgd2l0aGluIHRoZSBDUlMncyBib3VuZHMuXHJcblx0Ly8gT25seSBhY2NlcHRzIGFjdHVhbCBgTC5MYXRMbmdCb3VuZHNgIGluc3RhbmNlcywgbm90IGFycmF5cy5cclxuXHR3cmFwTGF0TG5nQm91bmRzOiBmdW5jdGlvbiAoYm91bmRzKSB7XHJcblx0XHR2YXIgY2VudGVyID0gYm91bmRzLmdldENlbnRlcigpLFxyXG5cdFx0ICAgIG5ld0NlbnRlciA9IHRoaXMud3JhcExhdExuZyhjZW50ZXIpLFxyXG5cdFx0ICAgIGxhdFNoaWZ0ID0gY2VudGVyLmxhdCAtIG5ld0NlbnRlci5sYXQsXHJcblx0XHQgICAgbG5nU2hpZnQgPSBjZW50ZXIubG5nIC0gbmV3Q2VudGVyLmxuZztcclxuXHJcblx0XHRpZiAobGF0U2hpZnQgPT09IDAgJiYgbG5nU2hpZnQgPT09IDApIHtcclxuXHRcdFx0cmV0dXJuIGJvdW5kcztcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgc3cgPSBib3VuZHMuZ2V0U291dGhXZXN0KCksXHJcblx0XHQgICAgbmUgPSBib3VuZHMuZ2V0Tm9ydGhFYXN0KCksXHJcblx0XHQgICAgbmV3U3cgPSBMLmxhdExuZyh7bGF0OiBzdy5sYXQgLSBsYXRTaGlmdCwgbG5nOiBzdy5sbmcgLSBsbmdTaGlmdH0pLFxyXG5cdFx0ICAgIG5ld05lID0gTC5sYXRMbmcoe2xhdDogbmUubGF0IC0gbGF0U2hpZnQsIGxuZzogbmUubG5nIC0gbG5nU2hpZnR9KTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuTGF0TG5nQm91bmRzKG5ld1N3LCBuZXdOZSk7XHJcblx0fVxyXG59O1xyXG5cblxuXG4vKlxuICogQG5hbWVzcGFjZSBDUlNcbiAqIEBjcnMgTC5DUlMuU2ltcGxlXG4gKlxuICogQSBzaW1wbGUgQ1JTIHRoYXQgbWFwcyBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGludG8gYHhgIGFuZCBgeWAgZGlyZWN0bHkuXG4gKiBNYXkgYmUgdXNlZCBmb3IgbWFwcyBvZiBmbGF0IHN1cmZhY2VzIChlLmcuIGdhbWUgbWFwcykuIE5vdGUgdGhhdCB0aGUgYHlgXG4gKiBheGlzIHNob3VsZCBzdGlsbCBiZSBpbnZlcnRlZCAoZ29pbmcgZnJvbSBib3R0b20gdG8gdG9wKS4gYGRpc3RhbmNlKClgIHJldHVybnNcbiAqIHNpbXBsZSBldWNsaWRlYW4gZGlzdGFuY2UuXG4gKi9cblxuTC5DUlMuU2ltcGxlID0gTC5leHRlbmQoe30sIEwuQ1JTLCB7XG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5Mb25MYXQsXG5cdHRyYW5zZm9ybWF0aW9uOiBuZXcgTC5UcmFuc2Zvcm1hdGlvbigxLCAwLCAtMSwgMCksXG5cblx0c2NhbGU6IGZ1bmN0aW9uICh6b29tKSB7XG5cdFx0cmV0dXJuIE1hdGgucG93KDIsIHpvb20pO1xuXHR9LFxuXG5cdHpvb206IGZ1bmN0aW9uIChzY2FsZSkge1xuXHRcdHJldHVybiBNYXRoLmxvZyhzY2FsZSkgLyBNYXRoLkxOMjtcblx0fSxcblxuXHRkaXN0YW5jZTogZnVuY3Rpb24gKGxhdGxuZzEsIGxhdGxuZzIpIHtcblx0XHR2YXIgZHggPSBsYXRsbmcyLmxuZyAtIGxhdGxuZzEubG5nLFxuXHRcdCAgICBkeSA9IGxhdGxuZzIubGF0IC0gbGF0bG5nMS5sYXQ7XG5cblx0XHRyZXR1cm4gTWF0aC5zcXJ0KGR4ICogZHggKyBkeSAqIGR5KTtcblx0fSxcblxuXHRpbmZpbml0ZTogdHJ1ZVxufSk7XG5cblxuXG4vKlxuICogQG5hbWVzcGFjZSBDUlNcbiAqIEBjcnMgTC5DUlMuRWFydGhcbiAqXG4gKiBTZXJ2ZXMgYXMgdGhlIGJhc2UgZm9yIENSUyB0aGF0IGFyZSBnbG9iYWwgc3VjaCB0aGF0IHRoZXkgY292ZXIgdGhlIGVhcnRoLlxuICogQ2FuIG9ubHkgYmUgdXNlZCBhcyB0aGUgYmFzZSBmb3Igb3RoZXIgQ1JTIGFuZCBjYW5ub3QgYmUgdXNlZCBkaXJlY3RseSxcbiAqIHNpbmNlIGl0IGRvZXMgbm90IGhhdmUgYSBgY29kZWAsIGBwcm9qZWN0aW9uYCBvciBgdHJhbnNmb3JtYXRpb25gLiBgZGlzdGFuY2UoKWAgcmV0dXJuc1xuICogbWV0ZXJzLlxuICovXG5cbkwuQ1JTLkVhcnRoID0gTC5leHRlbmQoe30sIEwuQ1JTLCB7XG5cdHdyYXBMbmc6IFstMTgwLCAxODBdLFxuXG5cdC8vIE1lYW4gRWFydGggUmFkaXVzLCBhcyByZWNvbW1lbmRlZCBmb3IgdXNlIGJ5XG5cdC8vIHRoZSBJbnRlcm5hdGlvbmFsIFVuaW9uIG9mIEdlb2Rlc3kgYW5kIEdlb3BoeXNpY3MsXG5cdC8vIHNlZSBodHRwOi8vcm9zZXR0YWNvZGUub3JnL3dpa2kvSGF2ZXJzaW5lX2Zvcm11bGFcblx0UjogNjM3MTAwMCxcblxuXHQvLyBkaXN0YW5jZSBiZXR3ZWVuIHR3byBnZW9ncmFwaGljYWwgcG9pbnRzIHVzaW5nIHNwaGVyaWNhbCBsYXcgb2YgY29zaW5lcyBhcHByb3hpbWF0aW9uXG5cdGRpc3RhbmNlOiBmdW5jdGlvbiAobGF0bG5nMSwgbGF0bG5nMikge1xuXHRcdHZhciByYWQgPSBNYXRoLlBJIC8gMTgwLFxuXHRcdCAgICBsYXQxID0gbGF0bG5nMS5sYXQgKiByYWQsXG5cdFx0ICAgIGxhdDIgPSBsYXRsbmcyLmxhdCAqIHJhZCxcblx0XHQgICAgYSA9IE1hdGguc2luKGxhdDEpICogTWF0aC5zaW4obGF0MikgK1xuXHRcdCAgICAgICAgTWF0aC5jb3MobGF0MSkgKiBNYXRoLmNvcyhsYXQyKSAqIE1hdGguY29zKChsYXRsbmcyLmxuZyAtIGxhdGxuZzEubG5nKSAqIHJhZCk7XG5cblx0XHRyZXR1cm4gdGhpcy5SICogTWF0aC5hY29zKE1hdGgubWluKGEsIDEpKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIENSU1xyXG4gKiBAY3JzIEwuQ1JTLkVQU0czODU3XHJcbiAqXHJcbiAqIFRoZSBtb3N0IGNvbW1vbiBDUlMgZm9yIG9ubGluZSBtYXBzLCB1c2VkIGJ5IGFsbW9zdCBhbGwgZnJlZSBhbmQgY29tbWVyY2lhbFxyXG4gKiB0aWxlIHByb3ZpZGVycy4gVXNlcyBTcGhlcmljYWwgTWVyY2F0b3IgcHJvamVjdGlvbi4gU2V0IGluIGJ5IGRlZmF1bHQgaW5cclxuICogTWFwJ3MgYGNyc2Agb3B0aW9uLlxyXG4gKi9cclxuXHJcbkwuQ1JTLkVQU0czODU3ID0gTC5leHRlbmQoe30sIEwuQ1JTLkVhcnRoLCB7XHJcblx0Y29kZTogJ0VQU0c6Mzg1NycsXHJcblx0cHJvamVjdGlvbjogTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yLFxyXG5cclxuXHR0cmFuc2Zvcm1hdGlvbjogKGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBzY2FsZSA9IDAuNSAvIChNYXRoLlBJICogTC5Qcm9qZWN0aW9uLlNwaGVyaWNhbE1lcmNhdG9yLlIpO1xyXG5cdFx0cmV0dXJuIG5ldyBMLlRyYW5zZm9ybWF0aW9uKHNjYWxlLCAwLjUsIC1zY2FsZSwgMC41KTtcclxuXHR9KCkpXHJcbn0pO1xyXG5cclxuTC5DUlMuRVBTRzkwMDkxMyA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FUFNHMzg1Nywge1xyXG5cdGNvZGU6ICdFUFNHOjkwMDkxMydcclxufSk7XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgQ1JTXHJcbiAqIEBjcnMgTC5DUlMuRVBTRzQzMjZcclxuICpcclxuICogQSBjb21tb24gQ1JTIGFtb25nIEdJUyBlbnRodXNpYXN0cy4gVXNlcyBzaW1wbGUgRXF1aXJlY3Rhbmd1bGFyIHByb2plY3Rpb24uXHJcbiAqXHJcbiAqIExlYWZsZXQgMS4wLnggY29tcGxpZXMgd2l0aCB0aGUgW1RNUyBjb29yZGluYXRlIHNjaGVtZSBmb3IgRVBTRzo0MzI2XShodHRwczovL3dpa2kub3NnZW8ub3JnL3dpa2kvVGlsZV9NYXBfU2VydmljZV9TcGVjaWZpY2F0aW9uI2dsb2JhbC1nZW9kZXRpYyksXHJcbiAqIHdoaWNoIGlzIGEgYnJlYWtpbmcgY2hhbmdlIGZyb20gMC43LnggYmVoYXZpb3VyLiAgSWYgeW91IGFyZSB1c2luZyBhIGBUaWxlTGF5ZXJgXHJcbiAqIHdpdGggdGhpcyBDUlMsIGVuc3VyZSB0aGF0IHRoZXJlIGFyZSB0d28gMjU2eDI1NiBwaXhlbCB0aWxlcyBjb3ZlcmluZyB0aGVcclxuICogd2hvbGUgZWFydGggYXQgem9vbSBsZXZlbCB6ZXJvLCBhbmQgdGhhdCB0aGUgdGlsZSBjb29yZGluYXRlIG9yaWdpbiBpcyAoLTE4MCwrOTApLFxyXG4gKiBvciAoLTE4MCwtOTApIGZvciBgVGlsZUxheWVyYHMgd2l0aCBbdGhlIGB0bXNgIG9wdGlvbl0oI3RpbGVsYXllci10bXMpIHNldC5cclxuICovXHJcblxyXG5MLkNSUy5FUFNHNDMyNiA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FYXJ0aCwge1xyXG5cdGNvZGU6ICdFUFNHOjQzMjYnLFxyXG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5Mb25MYXQsXHJcblx0dHJhbnNmb3JtYXRpb246IG5ldyBMLlRyYW5zZm9ybWF0aW9uKDEgLyAxODAsIDEsIC0xIC8gMTgwLCAwLjUpXHJcbn0pO1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgTWFwXHJcbiAqIEBha2EgTC5NYXBcclxuICogQGluaGVyaXRzIEV2ZW50ZWRcclxuICpcclxuICogVGhlIGNlbnRyYWwgY2xhc3Mgb2YgdGhlIEFQSSDigJQgaXQgaXMgdXNlZCB0byBjcmVhdGUgYSBtYXAgb24gYSBwYWdlIGFuZCBtYW5pcHVsYXRlIGl0LlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiAvLyBpbml0aWFsaXplIHRoZSBtYXAgb24gdGhlIFwibWFwXCIgZGl2IHdpdGggYSBnaXZlbiBjZW50ZXIgYW5kIHpvb21cclxuICogdmFyIG1hcCA9IEwubWFwKCdtYXAnLCB7XHJcbiAqIFx0Y2VudGVyOiBbNTEuNTA1LCAtMC4wOV0sXHJcbiAqIFx0em9vbTogMTNcclxuICogfSk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKi9cclxuXHJcbkwuTWFwID0gTC5FdmVudGVkLmV4dGVuZCh7XHJcblxyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBzZWN0aW9uIE1hcCBTdGF0ZSBPcHRpb25zXHJcblx0XHQvLyBAb3B0aW9uIGNyczogQ1JTID0gTC5DUlMuRVBTRzM4NTdcclxuXHRcdC8vIFRoZSBbQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtXSgjY3JzKSB0byB1c2UuIERvbid0IGNoYW5nZSB0aGlzIGlmIHlvdSdyZSBub3RcclxuXHRcdC8vIHN1cmUgd2hhdCBpdCBtZWFucy5cclxuXHRcdGNyczogTC5DUlMuRVBTRzM4NTcsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBjZW50ZXI6IExhdExuZyA9IHVuZGVmaW5lZFxyXG5cdFx0Ly8gSW5pdGlhbCBnZW9ncmFwaGljIGNlbnRlciBvZiB0aGUgbWFwXHJcblx0XHRjZW50ZXI6IHVuZGVmaW5lZCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb206IE51bWJlciA9IHVuZGVmaW5lZFxyXG5cdFx0Ly8gSW5pdGlhbCBtYXAgem9vbSBsZXZlbFxyXG5cdFx0em9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWluWm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXHJcblx0XHQvLyBNaW5pbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcC4gT3ZlcnJpZGVzIGFueSBgbWluWm9vbWAgb3B0aW9uIHNldCBvbiBtYXAgbGF5ZXJzLlxyXG5cdFx0bWluWm9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWF4Wm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcC4gT3ZlcnJpZGVzIGFueSBgbWF4Wm9vbWAgb3B0aW9uIHNldCBvbiBtYXAgbGF5ZXJzLlxyXG5cdFx0bWF4Wm9vbTogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbGF5ZXJzOiBMYXllcltdID0gW11cclxuXHRcdC8vIEFycmF5IG9mIGxheWVycyB0aGF0IHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1hcCBpbml0aWFsbHlcclxuXHRcdGxheWVyczogW10sXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhCb3VuZHM6IExhdExuZ0JvdW5kcyA9IG51bGxcclxuXHRcdC8vIFdoZW4gdGhpcyBvcHRpb24gaXMgc2V0LCB0aGUgbWFwIHJlc3RyaWN0cyB0aGUgdmlldyB0byB0aGUgZ2l2ZW5cclxuXHRcdC8vIGdlb2dyYXBoaWNhbCBib3VuZHMsIGJvdW5jaW5nIHRoZSB1c2VyIGJhY2sgaWYgdGhlIHVzZXIgdHJpZXMgdG8gcGFuXHJcblx0XHQvLyBvdXRzaWRlIHRoZSB2aWV3LiBUbyBzZXQgdGhlIHJlc3RyaWN0aW9uIGR5bmFtaWNhbGx5LCB1c2VcclxuXHRcdC8vIFtgc2V0TWF4Qm91bmRzYF0oI21hcC1zZXRtYXhib3VuZHMpIG1ldGhvZC5cclxuXHRcdG1heEJvdW5kczogdW5kZWZpbmVkLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gcmVuZGVyZXI6IFJlbmRlcmVyID0gKlxyXG5cdFx0Ly8gVGhlIGRlZmF1bHQgbWV0aG9kIGZvciBkcmF3aW5nIHZlY3RvciBsYXllcnMgb24gdGhlIG1hcC4gYEwuU1ZHYFxyXG5cdFx0Ly8gb3IgYEwuQ2FudmFzYCBieSBkZWZhdWx0IGRlcGVuZGluZyBvbiBicm93c2VyIHN1cHBvcnQuXHJcblx0XHRyZW5kZXJlcjogdW5kZWZpbmVkLFxyXG5cclxuXHJcblx0XHQvLyBAc2VjdGlvbiBBbmltYXRpb24gT3B0aW9uc1xyXG5cdFx0Ly8gQG9wdGlvbiB6b29tQW5pbWF0aW9uOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFwIHpvb20gYW5pbWF0aW9uIGlzIGVuYWJsZWQuIEJ5IGRlZmF1bHQgaXQncyBlbmFibGVkXHJcblx0XHQvLyBpbiBhbGwgYnJvd3NlcnMgdGhhdCBzdXBwb3J0IENTUzMgVHJhbnNpdGlvbnMgZXhjZXB0IEFuZHJvaWQuXHJcblx0XHR6b29tQW5pbWF0aW9uOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbUFuaW1hdGlvblRocmVzaG9sZDogTnVtYmVyID0gNFxyXG5cdFx0Ly8gV29uJ3QgYW5pbWF0ZSB6b29tIGlmIHRoZSB6b29tIGRpZmZlcmVuY2UgZXhjZWVkcyB0aGlzIHZhbHVlLlxyXG5cdFx0em9vbUFuaW1hdGlvblRocmVzaG9sZDogNCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGZhZGVBbmltYXRpb246IEJvb2xlYW4gPSB0cnVlXHJcblx0XHQvLyBXaGV0aGVyIHRoZSB0aWxlIGZhZGUgYW5pbWF0aW9uIGlzIGVuYWJsZWQuIEJ5IGRlZmF1bHQgaXQncyBlbmFibGVkXHJcblx0XHQvLyBpbiBhbGwgYnJvd3NlcnMgdGhhdCBzdXBwb3J0IENTUzMgVHJhbnNpdGlvbnMgZXhjZXB0IEFuZHJvaWQuXHJcblx0XHRmYWRlQW5pbWF0aW9uOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWFya2VyWm9vbUFuaW1hdGlvbjogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgbWFya2VycyBhbmltYXRlIHRoZWlyIHpvb20gd2l0aCB0aGUgem9vbSBhbmltYXRpb24sIGlmIGRpc2FibGVkXHJcblx0XHQvLyB0aGV5IHdpbGwgZGlzYXBwZWFyIGZvciB0aGUgbGVuZ3RoIG9mIHRoZSBhbmltYXRpb24uIEJ5IGRlZmF1bHQgaXQnc1xyXG5cdFx0Ly8gZW5hYmxlZCBpbiBhbGwgYnJvd3NlcnMgdGhhdCBzdXBwb3J0IENTUzMgVHJhbnNpdGlvbnMgZXhjZXB0IEFuZHJvaWQuXHJcblx0XHRtYXJrZXJab29tQW5pbWF0aW9uOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gdHJhbnNmb3JtM0RMaW1pdDogTnVtYmVyID0gMl4yM1xyXG5cdFx0Ly8gRGVmaW5lcyB0aGUgbWF4aW11bSBzaXplIG9mIGEgQ1NTIHRyYW5zbGF0aW9uIHRyYW5zZm9ybS4gVGhlIGRlZmF1bHRcclxuXHRcdC8vIHZhbHVlIHNob3VsZCBub3QgYmUgY2hhbmdlZCB1bmxlc3MgYSB3ZWIgYnJvd3NlciBwb3NpdGlvbnMgbGF5ZXJzIGluXHJcblx0XHQvLyB0aGUgd3JvbmcgcGxhY2UgYWZ0ZXIgZG9pbmcgYSBsYXJnZSBgcGFuQnlgLlxyXG5cdFx0dHJhbnNmb3JtM0RMaW1pdDogODM4ODYwOCwgLy8gUHJlY2lzaW9uIGxpbWl0IG9mIGEgMzItYml0IGZsb2F0XHJcblxyXG5cdFx0Ly8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xyXG5cdFx0Ly8gQG9wdGlvbiB6b29tU25hcDogTnVtYmVyID0gMVxyXG5cdFx0Ly8gRm9yY2VzIHRoZSBtYXAncyB6b29tIGxldmVsIHRvIGFsd2F5cyBiZSBhIG11bHRpcGxlIG9mIHRoaXMsIHBhcnRpY3VsYXJseVxyXG5cdFx0Ly8gcmlnaHQgYWZ0ZXIgYSBbYGZpdEJvdW5kcygpYF0oI21hcC1maXRib3VuZHMpIG9yIGEgcGluY2gtem9vbS5cclxuXHRcdC8vIEJ5IGRlZmF1bHQsIHRoZSB6b29tIGxldmVsIHNuYXBzIHRvIHRoZSBuZWFyZXN0IGludGVnZXI7IGxvd2VyIHZhbHVlc1xyXG5cdFx0Ly8gKGUuZy4gYDAuNWAgb3IgYDAuMWApIGFsbG93IGZvciBncmVhdGVyIGdyYW51bGFyaXR5LiBBIHZhbHVlIG9mIGAwYFxyXG5cdFx0Ly8gbWVhbnMgdGhlIHpvb20gbGV2ZWwgd2lsbCBub3QgYmUgc25hcHBlZCBhZnRlciBgZml0Qm91bmRzYCBvciBhIHBpbmNoLXpvb20uXHJcblx0XHR6b29tU25hcDogMSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21EZWx0YTogTnVtYmVyID0gMVxyXG5cdFx0Ly8gQ29udHJvbHMgaG93IG11Y2ggdGhlIG1hcCdzIHpvb20gbGV2ZWwgd2lsbCBjaGFuZ2UgYWZ0ZXIgYVxyXG5cdFx0Ly8gW2B6b29tSW4oKWBdKCNtYXAtem9vbWluKSwgW2B6b29tT3V0KClgXSgjbWFwLXpvb21vdXQpLCBwcmVzc2luZyBgK2BcclxuXHRcdC8vIG9yIGAtYCBvbiB0aGUga2V5Ym9hcmQsIG9yIHVzaW5nIHRoZSBbem9vbSBjb250cm9sc10oI2NvbnRyb2wtem9vbSkuXHJcblx0XHQvLyBWYWx1ZXMgc21hbGxlciB0aGFuIGAxYCAoZS5nLiBgMC41YCkgYWxsb3cgZm9yIGdyZWF0ZXIgZ3JhbnVsYXJpdHkuXHJcblx0XHR6b29tRGVsdGE6IDEsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0cmFja1Jlc2l6ZTogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIFdoZXRoZXIgdGhlIG1hcCBhdXRvbWF0aWNhbGx5IGhhbmRsZXMgYnJvd3NlciB3aW5kb3cgcmVzaXplIHRvIHVwZGF0ZSBpdHNlbGYuXHJcblx0XHR0cmFja1Jlc2l6ZTogdHJ1ZVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChpZCwgb3B0aW9ucykgeyAvLyAoSFRNTEVsZW1lbnQgb3IgU3RyaW5nLCBPYmplY3QpXHJcblx0XHRvcHRpb25zID0gTC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX2luaXRDb250YWluZXIoaWQpO1xyXG5cdFx0dGhpcy5faW5pdExheW91dCgpO1xyXG5cclxuXHRcdC8vIGhhY2sgZm9yIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzE5ODBcclxuXHRcdHRoaXMuX29uUmVzaXplID0gTC5iaW5kKHRoaXMuX29uUmVzaXplLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9pbml0RXZlbnRzKCk7XHJcblxyXG5cdFx0aWYgKG9wdGlvbnMubWF4Qm91bmRzKSB7XHJcblx0XHRcdHRoaXMuc2V0TWF4Qm91bmRzKG9wdGlvbnMubWF4Qm91bmRzKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAob3B0aW9ucy56b29tICE9PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0dGhpcy5fem9vbSA9IHRoaXMuX2xpbWl0Wm9vbShvcHRpb25zLnpvb20pO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChvcHRpb25zLmNlbnRlciAmJiBvcHRpb25zLnpvb20gIT09IHVuZGVmaW5lZCkge1xyXG5cdFx0XHR0aGlzLnNldFZpZXcoTC5sYXRMbmcob3B0aW9ucy5jZW50ZXIpLCBvcHRpb25zLnpvb20sIHtyZXNldDogdHJ1ZX0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2hhbmRsZXJzID0gW107XHJcblx0XHR0aGlzLl9sYXllcnMgPSB7fTtcclxuXHRcdHRoaXMuX3pvb21Cb3VuZExheWVycyA9IHt9O1xyXG5cdFx0dGhpcy5fc2l6ZUNoYW5nZWQgPSB0cnVlO1xyXG5cclxuXHRcdHRoaXMuY2FsbEluaXRIb29rcygpO1xyXG5cclxuXHRcdC8vIGRvbid0IGFuaW1hdGUgb24gYnJvd3NlcnMgd2l0aG91dCBoYXJkd2FyZS1hY2NlbGVyYXRlZCB0cmFuc2l0aW9ucyBvciBvbGQgQW5kcm9pZC9PcGVyYVxyXG5cdFx0dGhpcy5fem9vbUFuaW1hdGVkID0gTC5Eb21VdGlsLlRSQU5TSVRJT04gJiYgTC5Ccm93c2VyLmFueTNkICYmICFMLkJyb3dzZXIubW9iaWxlT3BlcmEgJiZcclxuXHRcdFx0XHR0aGlzLm9wdGlvbnMuem9vbUFuaW1hdGlvbjtcclxuXHJcblx0XHQvLyB6b29tIHRyYW5zaXRpb25zIHJ1biB3aXRoIHRoZSBzYW1lIGR1cmF0aW9uIGZvciBhbGwgbGF5ZXJzLCBzbyBpZiBvbmUgb2YgdHJhbnNpdGlvbmVuZCBldmVudHNcclxuXHRcdC8vIGhhcHBlbnMgYWZ0ZXIgc3RhcnRpbmcgem9vbSBhbmltYXRpb24gKHByb3BhZ2F0aW5nIHRvIHRoZSBtYXAgcGFuZSksIHdlIGtub3cgdGhhdCBpdCBlbmRlZCBnbG9iYWxseVxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHR0aGlzLl9jcmVhdGVBbmltUHJveHkoKTtcclxuXHRcdFx0TC5Eb21FdmVudC5vbih0aGlzLl9wcm94eSwgTC5Eb21VdGlsLlRSQU5TSVRJT05fRU5ELCB0aGlzLl9jYXRjaFRyYW5zaXRpb25FbmQsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2FkZExheWVycyh0aGlzLm9wdGlvbnMubGF5ZXJzKTtcclxuXHR9LFxyXG5cclxuXHJcblx0Ly8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgbW9kaWZ5aW5nIG1hcCBzdGF0ZVxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFZpZXcoY2VudGVyOiBMYXRMbmcsIHpvb206IE51bWJlciwgb3B0aW9ucz86IFpvb20vcGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdmlldyBvZiB0aGUgbWFwIChnZW9ncmFwaGljYWwgY2VudGVyIGFuZCB6b29tKSB3aXRoIHRoZSBnaXZlblxyXG5cdC8vIGFuaW1hdGlvbiBvcHRpb25zLlxyXG5cdHNldFZpZXc6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIG9wdGlvbnMpIHtcclxuXHJcblx0XHR6b29tID0gem9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fem9vbSA6IHRoaXMuX2xpbWl0Wm9vbSh6b29tKTtcclxuXHRcdGNlbnRlciA9IHRoaXMuX2xpbWl0Q2VudGVyKEwubGF0TG5nKGNlbnRlciksIHpvb20sIHRoaXMub3B0aW9ucy5tYXhCb3VuZHMpO1xyXG5cdFx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcblxyXG5cdFx0dGhpcy5fc3RvcCgpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sb2FkZWQgJiYgIW9wdGlvbnMucmVzZXQgJiYgb3B0aW9ucyAhPT0gdHJ1ZSkge1xyXG5cclxuXHRcdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gdW5kZWZpbmVkKSB7XHJcblx0XHRcdFx0b3B0aW9ucy56b29tID0gTC5leHRlbmQoe2FuaW1hdGU6IG9wdGlvbnMuYW5pbWF0ZX0sIG9wdGlvbnMuem9vbSk7XHJcblx0XHRcdFx0b3B0aW9ucy5wYW4gPSBMLmV4dGVuZCh7YW5pbWF0ZTogb3B0aW9ucy5hbmltYXRlLCBkdXJhdGlvbjogb3B0aW9ucy5kdXJhdGlvbn0sIG9wdGlvbnMucGFuKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gdHJ5IGFuaW1hdGluZyBwYW4gb3Igem9vbVxyXG5cdFx0XHR2YXIgbW92ZWQgPSAodGhpcy5fem9vbSAhPT0gem9vbSkgP1xyXG5cdFx0XHRcdHRoaXMuX3RyeUFuaW1hdGVkWm9vbSAmJiB0aGlzLl90cnlBbmltYXRlZFpvb20oY2VudGVyLCB6b29tLCBvcHRpb25zLnpvb20pIDpcclxuXHRcdFx0XHR0aGlzLl90cnlBbmltYXRlZFBhbihjZW50ZXIsIG9wdGlvbnMucGFuKTtcclxuXHJcblx0XHRcdGlmIChtb3ZlZCkge1xyXG5cdFx0XHRcdC8vIHByZXZlbnQgcmVzaXplIGhhbmRsZXIgY2FsbCwgdGhlIHZpZXcgd2lsbCByZWZyZXNoIGFmdGVyIGFuaW1hdGlvbiBhbnl3YXlcclxuXHRcdFx0XHRjbGVhclRpbWVvdXQodGhpcy5fc2l6ZVRpbWVyKTtcclxuXHRcdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIGFuaW1hdGlvbiBkaWRuJ3Qgc3RhcnQsIGp1c3QgcmVzZXQgdGhlIG1hcCB2aWV3XHJcblx0XHR0aGlzLl9yZXNldFZpZXcoY2VudGVyLCB6b29tKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFpvb20oem9vbTogTnVtYmVyLCBvcHRpb25zOiBab29tL3BhbiBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHpvb20gb2YgdGhlIG1hcC5cclxuXHRzZXRab29tOiBmdW5jdGlvbiAoem9vbSwgb3B0aW9ucykge1xyXG5cdFx0aWYgKCF0aGlzLl9sb2FkZWQpIHtcclxuXHRcdFx0dGhpcy5fem9vbSA9IHpvb207XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Vmlldyh0aGlzLmdldENlbnRlcigpLCB6b29tLCB7em9vbTogb3B0aW9uc30pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgem9vbUluKGRlbHRhPzogTnVtYmVyLCBvcHRpb25zPzogWm9vbSBvcHRpb25zKTogdGhpc1xyXG5cdC8vIEluY3JlYXNlcyB0aGUgem9vbSBvZiB0aGUgbWFwIGJ5IGBkZWx0YWAgKFtgem9vbURlbHRhYF0oI21hcC16b29tZGVsdGEpIGJ5IGRlZmF1bHQpLlxyXG5cdHpvb21JbjogZnVuY3Rpb24gKGRlbHRhLCBvcHRpb25zKSB7XHJcblx0XHRkZWx0YSA9IGRlbHRhIHx8IChMLkJyb3dzZXIuYW55M2QgPyB0aGlzLm9wdGlvbnMuem9vbURlbHRhIDogMSk7XHJcblx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHRoaXMuX3pvb20gKyBkZWx0YSwgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB6b29tT3V0KGRlbHRhPzogTnVtYmVyLCBvcHRpb25zPzogWm9vbSBvcHRpb25zKTogdGhpc1xyXG5cdC8vIERlY3JlYXNlcyB0aGUgem9vbSBvZiB0aGUgbWFwIGJ5IGBkZWx0YWAgKFtgem9vbURlbHRhYF0oI21hcC16b29tZGVsdGEpIGJ5IGRlZmF1bHQpLlxyXG5cdHpvb21PdXQ6IGZ1bmN0aW9uIChkZWx0YSwgb3B0aW9ucykge1xyXG5cdFx0ZGVsdGEgPSBkZWx0YSB8fCAoTC5Ccm93c2VyLmFueTNkID8gdGhpcy5vcHRpb25zLnpvb21EZWx0YSA6IDEpO1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Wm9vbSh0aGlzLl96b29tIC0gZGVsdGEsIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0Wm9vbUFyb3VuZChsYXRsbmc6IExhdExuZywgem9vbTogTnVtYmVyLCBvcHRpb25zOiBab29tIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gWm9vbXMgdGhlIG1hcCB3aGlsZSBrZWVwaW5nIGEgc3BlY2lmaWVkIGdlb2dyYXBoaWNhbCBwb2ludCBvbiB0aGUgbWFwXHJcblx0Ly8gc3RhdGlvbmFyeSAoZS5nLiB1c2VkIGludGVybmFsbHkgZm9yIHNjcm9sbCB6b29tIGFuZCBkb3VibGUtY2xpY2sgem9vbSkuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCBzZXRab29tQXJvdW5kKG9mZnNldDogUG9pbnQsIHpvb206IE51bWJlciwgb3B0aW9uczogWm9vbSBvcHRpb25zKTogdGhpc1xyXG5cdC8vIFpvb21zIHRoZSBtYXAgd2hpbGUga2VlcGluZyBhIHNwZWNpZmllZCBwaXhlbCBvbiB0aGUgbWFwIChyZWxhdGl2ZSB0byB0aGUgdG9wLWxlZnQgY29ybmVyKSBzdGF0aW9uYXJ5LlxyXG5cdHNldFpvb21Bcm91bmQ6IGZ1bmN0aW9uIChsYXRsbmcsIHpvb20sIG9wdGlvbnMpIHtcclxuXHRcdHZhciBzY2FsZSA9IHRoaXMuZ2V0Wm9vbVNjYWxlKHpvb20pLFxyXG5cdFx0ICAgIHZpZXdIYWxmID0gdGhpcy5nZXRTaXplKCkuZGl2aWRlQnkoMiksXHJcblx0XHQgICAgY29udGFpbmVyUG9pbnQgPSBsYXRsbmcgaW5zdGFuY2VvZiBMLlBvaW50ID8gbGF0bG5nIDogdGhpcy5sYXRMbmdUb0NvbnRhaW5lclBvaW50KGxhdGxuZyksXHJcblxyXG5cdFx0ICAgIGNlbnRlck9mZnNldCA9IGNvbnRhaW5lclBvaW50LnN1YnRyYWN0KHZpZXdIYWxmKS5tdWx0aXBseUJ5KDEgLSAxIC8gc2NhbGUpLFxyXG5cdFx0ICAgIG5ld0NlbnRlciA9IHRoaXMuY29udGFpbmVyUG9pbnRUb0xhdExuZyh2aWV3SGFsZi5hZGQoY2VudGVyT2Zmc2V0KSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMuc2V0VmlldyhuZXdDZW50ZXIsIHpvb20sIHt6b29tOiBvcHRpb25zfSk7XHJcblx0fSxcclxuXHJcblx0X2dldEJvdW5kc0NlbnRlclpvb206IGZ1bmN0aW9uIChib3VuZHMsIG9wdGlvbnMpIHtcclxuXHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHRcdGJvdW5kcyA9IGJvdW5kcy5nZXRCb3VuZHMgPyBib3VuZHMuZ2V0Qm91bmRzKCkgOiBMLmxhdExuZ0JvdW5kcyhib3VuZHMpO1xyXG5cclxuXHRcdHZhciBwYWRkaW5nVEwgPSBMLnBvaW50KG9wdGlvbnMucGFkZGluZ1RvcExlZnQgfHwgb3B0aW9ucy5wYWRkaW5nIHx8IFswLCAwXSksXHJcblx0XHQgICAgcGFkZGluZ0JSID0gTC5wb2ludChvcHRpb25zLnBhZGRpbmdCb3R0b21SaWdodCB8fCBvcHRpb25zLnBhZGRpbmcgfHwgWzAsIDBdKSxcclxuXHJcblx0XHQgICAgem9vbSA9IHRoaXMuZ2V0Qm91bmRzWm9vbShib3VuZHMsIGZhbHNlLCBwYWRkaW5nVEwuYWRkKHBhZGRpbmdCUikpO1xyXG5cclxuXHRcdHpvb20gPSAodHlwZW9mIG9wdGlvbnMubWF4Wm9vbSA9PT0gJ251bWJlcicpID8gTWF0aC5taW4ob3B0aW9ucy5tYXhab29tLCB6b29tKSA6IHpvb207XHJcblxyXG5cdFx0dmFyIHBhZGRpbmdPZmZzZXQgPSBwYWRkaW5nQlIuc3VidHJhY3QocGFkZGluZ1RMKS5kaXZpZGVCeSgyKSxcclxuXHJcblx0XHQgICAgc3dQb2ludCA9IHRoaXMucHJvamVjdChib3VuZHMuZ2V0U291dGhXZXN0KCksIHpvb20pLFxyXG5cdFx0ICAgIG5lUG9pbnQgPSB0aGlzLnByb2plY3QoYm91bmRzLmdldE5vcnRoRWFzdCgpLCB6b29tKSxcclxuXHRcdCAgICBjZW50ZXIgPSB0aGlzLnVucHJvamVjdChzd1BvaW50LmFkZChuZVBvaW50KS5kaXZpZGVCeSgyKS5hZGQocGFkZGluZ09mZnNldCksIHpvb20pO1xyXG5cclxuXHRcdHJldHVybiB7XHJcblx0XHRcdGNlbnRlcjogY2VudGVyLFxyXG5cdFx0XHR6b29tOiB6b29tXHJcblx0XHR9O1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZml0Qm91bmRzKGJvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogZml0Qm91bmRzIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyBhIG1hcCB2aWV3IHRoYXQgY29udGFpbnMgdGhlIGdpdmVuIGdlb2dyYXBoaWNhbCBib3VuZHMgd2l0aCB0aGVcclxuXHQvLyBtYXhpbXVtIHpvb20gbGV2ZWwgcG9zc2libGUuXHJcblx0Zml0Qm91bmRzOiBmdW5jdGlvbiAoYm91bmRzLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0Ym91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHRpZiAoIWJvdW5kcy5pc1ZhbGlkKCkpIHtcclxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdCb3VuZHMgYXJlIG5vdCB2YWxpZC4nKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgdGFyZ2V0ID0gdGhpcy5fZ2V0Qm91bmRzQ2VudGVyWm9vbShib3VuZHMsIG9wdGlvbnMpO1xyXG5cdFx0cmV0dXJuIHRoaXMuc2V0Vmlldyh0YXJnZXQuY2VudGVyLCB0YXJnZXQuem9vbSwgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmaXRXb3JsZChvcHRpb25zPzogZml0Qm91bmRzIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyBhIG1hcCB2aWV3IHRoYXQgbW9zdGx5IGNvbnRhaW5zIHRoZSB3aG9sZSB3b3JsZCB3aXRoIHRoZSBtYXhpbXVtXHJcblx0Ly8gem9vbSBsZXZlbCBwb3NzaWJsZS5cclxuXHRmaXRXb3JsZDogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRcdHJldHVybiB0aGlzLmZpdEJvdW5kcyhbWy05MCwgLTE4MF0sIFs5MCwgMTgwXV0sIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcGFuVG8obGF0bG5nOiBMYXRMbmcsIG9wdGlvbnM/OiBQYW4gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBQYW5zIHRoZSBtYXAgdG8gYSBnaXZlbiBjZW50ZXIuXHJcblx0cGFuVG86IGZ1bmN0aW9uIChjZW50ZXIsIG9wdGlvbnMpIHsgLy8gKExhdExuZylcclxuXHRcdHJldHVybiB0aGlzLnNldFZpZXcoY2VudGVyLCB0aGlzLl96b29tLCB7cGFuOiBvcHRpb25zfSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwYW5CeShvZmZzZXQ6IFBvaW50KTogdGhpc1xyXG5cdC8vIFBhbnMgdGhlIG1hcCBieSBhIGdpdmVuIG51bWJlciBvZiBwaXhlbHMgKGFuaW1hdGVkKS5cclxuXHRwYW5CeTogZnVuY3Rpb24gKG9mZnNldCwgb3B0aW9ucykge1xyXG5cdFx0b2Zmc2V0ID0gTC5wb2ludChvZmZzZXQpLnJvdW5kKCk7XHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHRpZiAoIW9mZnNldC54ICYmICFvZmZzZXQueSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0XHR9XHJcblx0XHQvLyBJZiB3ZSBwYW4gdG9vIGZhciwgQ2hyb21lIGdldHMgaXNzdWVzIHdpdGggdGlsZXNcclxuXHRcdC8vIGFuZCBtYWtlcyB0aGVtIGRpc2FwcGVhciBvciBhcHBlYXIgaW4gdGhlIHdyb25nIHBsYWNlIChzbGlnaHRseSBvZmZzZXQpICMyNjAyXHJcblx0XHRpZiAob3B0aW9ucy5hbmltYXRlICE9PSB0cnVlICYmICF0aGlzLmdldFNpemUoKS5jb250YWlucyhvZmZzZXQpKSB7XHJcblx0XHRcdHRoaXMuX3Jlc2V0Vmlldyh0aGlzLnVucHJvamVjdCh0aGlzLnByb2plY3QodGhpcy5nZXRDZW50ZXIoKSkuYWRkKG9mZnNldCkpLCB0aGlzLmdldFpvb20oKSk7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghdGhpcy5fcGFuQW5pbSkge1xyXG5cdFx0XHR0aGlzLl9wYW5BbmltID0gbmV3IEwuUG9zQW5pbWF0aW9uKCk7XHJcblxyXG5cdFx0XHR0aGlzLl9wYW5BbmltLm9uKHtcclxuXHRcdFx0XHQnc3RlcCc6IHRoaXMuX29uUGFuVHJhbnNpdGlvblN0ZXAsXHJcblx0XHRcdFx0J2VuZCc6IHRoaXMuX29uUGFuVHJhbnNpdGlvbkVuZFxyXG5cdFx0XHR9LCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBkb24ndCBmaXJlIG1vdmVzdGFydCBpZiBhbmltYXRpbmcgaW5lcnRpYVxyXG5cdFx0aWYgKCFvcHRpb25zLm5vTW92ZVN0YXJ0KSB7XHJcblx0XHRcdHRoaXMuZmlyZSgnbW92ZXN0YXJ0Jyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gYW5pbWF0ZSBwYW4gdW5sZXNzIGFuaW1hdGU6IGZhbHNlIHNwZWNpZmllZFxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gZmFsc2UpIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX21hcFBhbmUsICdsZWFmbGV0LXBhbi1hbmltJyk7XHJcblxyXG5cdFx0XHR2YXIgbmV3UG9zID0gdGhpcy5fZ2V0TWFwUGFuZVBvcygpLnN1YnRyYWN0KG9mZnNldCkucm91bmQoKTtcclxuXHRcdFx0dGhpcy5fcGFuQW5pbS5ydW4odGhpcy5fbWFwUGFuZSwgbmV3UG9zLCBvcHRpb25zLmR1cmF0aW9uIHx8IDAuMjUsIG9wdGlvbnMuZWFzZUxpbmVhcml0eSk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLl9yYXdQYW5CeShvZmZzZXQpO1xyXG5cdFx0XHR0aGlzLmZpcmUoJ21vdmUnKS5maXJlKCdtb3ZlZW5kJyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBmbHlUbyhsYXRsbmc6IExhdExuZywgem9vbT86IE51bWJlciwgb3B0aW9ucz86IFpvb20vcGFuIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdmlldyBvZiB0aGUgbWFwIChnZW9ncmFwaGljYWwgY2VudGVyIGFuZCB6b29tKSBwZXJmb3JtaW5nIGEgc21vb3RoXHJcblx0Ly8gcGFuLXpvb20gYW5pbWF0aW9uLlxyXG5cdGZseVRvOiBmdW5jdGlvbiAodGFyZ2V0Q2VudGVyLCB0YXJnZXRab29tLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcblx0XHRpZiAob3B0aW9ucy5hbmltYXRlID09PSBmYWxzZSB8fCAhTC5Ccm93c2VyLmFueTNkKSB7XHJcblx0XHRcdHJldHVybiB0aGlzLnNldFZpZXcodGFyZ2V0Q2VudGVyLCB0YXJnZXRab29tLCBvcHRpb25zKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9zdG9wKCk7XHJcblxyXG5cdFx0dmFyIGZyb20gPSB0aGlzLnByb2plY3QodGhpcy5nZXRDZW50ZXIoKSksXHJcblx0XHQgICAgdG8gPSB0aGlzLnByb2plY3QodGFyZ2V0Q2VudGVyKSxcclxuXHRcdCAgICBzaXplID0gdGhpcy5nZXRTaXplKCksXHJcblx0XHQgICAgc3RhcnRab29tID0gdGhpcy5fem9vbTtcclxuXHJcblx0XHR0YXJnZXRDZW50ZXIgPSBMLmxhdExuZyh0YXJnZXRDZW50ZXIpO1xyXG5cdFx0dGFyZ2V0Wm9vbSA9IHRhcmdldFpvb20gPT09IHVuZGVmaW5lZCA/IHN0YXJ0Wm9vbSA6IHRhcmdldFpvb207XHJcblxyXG5cdFx0dmFyIHcwID0gTWF0aC5tYXgoc2l6ZS54LCBzaXplLnkpLFxyXG5cdFx0ICAgIHcxID0gdzAgKiB0aGlzLmdldFpvb21TY2FsZShzdGFydFpvb20sIHRhcmdldFpvb20pLFxyXG5cdFx0ICAgIHUxID0gKHRvLmRpc3RhbmNlVG8oZnJvbSkpIHx8IDEsXHJcblx0XHQgICAgcmhvID0gMS40MixcclxuXHRcdCAgICByaG8yID0gcmhvICogcmhvO1xyXG5cclxuXHRcdGZ1bmN0aW9uIHIoaSkge1xyXG5cdFx0XHR2YXIgczEgPSBpID8gLTEgOiAxLFxyXG5cdFx0XHQgICAgczIgPSBpID8gdzEgOiB3MCxcclxuXHRcdFx0ICAgIHQxID0gdzEgKiB3MSAtIHcwICogdzAgKyBzMSAqIHJobzIgKiByaG8yICogdTEgKiB1MSxcclxuXHRcdFx0ICAgIGIxID0gMiAqIHMyICogcmhvMiAqIHUxLFxyXG5cdFx0XHQgICAgYiA9IHQxIC8gYjEsXHJcblx0XHRcdCAgICBzcSA9IE1hdGguc3FydChiICogYiArIDEpIC0gYjtcclxuXHJcblx0XHRcdCAgICAvLyB3b3JrYXJvdW5kIGZvciBmbG9hdGluZyBwb2ludCBwcmVjaXNpb24gYnVnIHdoZW4gc3EgPSAwLCBsb2cgPSAtSW5maW5pdGUsXHJcblx0XHRcdCAgICAvLyB0aHVzIHRyaWdnZXJpbmcgYW4gaW5maW5pdGUgbG9vcCBpbiBmbHlUb1xyXG5cdFx0XHQgICAgdmFyIGxvZyA9IHNxIDwgMC4wMDAwMDAwMDEgPyAtMTggOiBNYXRoLmxvZyhzcSk7XHJcblxyXG5cdFx0XHRyZXR1cm4gbG9nO1xyXG5cdFx0fVxyXG5cclxuXHRcdGZ1bmN0aW9uIHNpbmgobikgeyByZXR1cm4gKE1hdGguZXhwKG4pIC0gTWF0aC5leHAoLW4pKSAvIDI7IH1cclxuXHRcdGZ1bmN0aW9uIGNvc2gobikgeyByZXR1cm4gKE1hdGguZXhwKG4pICsgTWF0aC5leHAoLW4pKSAvIDI7IH1cclxuXHRcdGZ1bmN0aW9uIHRhbmgobikgeyByZXR1cm4gc2luaChuKSAvIGNvc2gobik7IH1cclxuXHJcblx0XHR2YXIgcjAgPSByKDApO1xyXG5cclxuXHRcdGZ1bmN0aW9uIHcocykgeyByZXR1cm4gdzAgKiAoY29zaChyMCkgLyBjb3NoKHIwICsgcmhvICogcykpOyB9XHJcblx0XHRmdW5jdGlvbiB1KHMpIHsgcmV0dXJuIHcwICogKGNvc2gocjApICogdGFuaChyMCArIHJobyAqIHMpIC0gc2luaChyMCkpIC8gcmhvMjsgfVxyXG5cclxuXHRcdGZ1bmN0aW9uIGVhc2VPdXQodCkgeyByZXR1cm4gMSAtIE1hdGgucG93KDEgLSB0LCAxLjUpOyB9XHJcblxyXG5cdFx0dmFyIHN0YXJ0ID0gRGF0ZS5ub3coKSxcclxuXHRcdCAgICBTID0gKHIoMSkgLSByMCkgLyByaG8sXHJcblx0XHQgICAgZHVyYXRpb24gPSBvcHRpb25zLmR1cmF0aW9uID8gMTAwMCAqIG9wdGlvbnMuZHVyYXRpb24gOiAxMDAwICogUyAqIDAuODtcclxuXHJcblx0XHRmdW5jdGlvbiBmcmFtZSgpIHtcclxuXHRcdFx0dmFyIHQgPSAoRGF0ZS5ub3coKSAtIHN0YXJ0KSAvIGR1cmF0aW9uLFxyXG5cdFx0XHQgICAgcyA9IGVhc2VPdXQodCkgKiBTO1xyXG5cclxuXHRcdFx0aWYgKHQgPD0gMSkge1xyXG5cdFx0XHRcdHRoaXMuX2ZseVRvRnJhbWUgPSBMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmcmFtZSwgdGhpcyk7XHJcblxyXG5cdFx0XHRcdHRoaXMuX21vdmUoXHJcblx0XHRcdFx0XHR0aGlzLnVucHJvamVjdChmcm9tLmFkZCh0by5zdWJ0cmFjdChmcm9tKS5tdWx0aXBseUJ5KHUocykgLyB1MSkpLCBzdGFydFpvb20pLFxyXG5cdFx0XHRcdFx0dGhpcy5nZXRTY2FsZVpvb20odzAgLyB3KHMpLCBzdGFydFpvb20pLFxyXG5cdFx0XHRcdFx0e2ZseVRvOiB0cnVlfSk7XHJcblxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHRoaXNcclxuXHRcdFx0XHRcdC5fbW92ZSh0YXJnZXRDZW50ZXIsIHRhcmdldFpvb20pXHJcblx0XHRcdFx0XHQuX21vdmVFbmQodHJ1ZSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9tb3ZlU3RhcnQodHJ1ZSk7XHJcblxyXG5cdFx0ZnJhbWUuY2FsbCh0aGlzKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZmx5VG9Cb3VuZHMoYm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM/OiBmaXRCb3VuZHMgb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSB2aWV3IG9mIHRoZSBtYXAgd2l0aCBhIHNtb290aCBhbmltYXRpb24gbGlrZSBbYGZseVRvYF0oI21hcC1mbHl0byksXHJcblx0Ly8gYnV0IHRha2VzIGEgYm91bmRzIHBhcmFtZXRlciBsaWtlIFtgZml0Qm91bmRzYF0oI21hcC1maXRib3VuZHMpLlxyXG5cdGZseVRvQm91bmRzOiBmdW5jdGlvbiAoYm91bmRzLCBvcHRpb25zKSB7XHJcblx0XHR2YXIgdGFyZ2V0ID0gdGhpcy5fZ2V0Qm91bmRzQ2VudGVyWm9vbShib3VuZHMsIG9wdGlvbnMpO1xyXG5cdFx0cmV0dXJuIHRoaXMuZmx5VG8odGFyZ2V0LmNlbnRlciwgdGFyZ2V0Lnpvb20sIG9wdGlvbnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TWF4Qm91bmRzKGJvdW5kczogQm91bmRzKTogdGhpc1xyXG5cdC8vIFJlc3RyaWN0cyB0aGUgbWFwIHZpZXcgdG8gdGhlIGdpdmVuIGJvdW5kcyAoc2VlIHRoZSBbbWF4Qm91bmRzXSgjbWFwLW1heGJvdW5kcykgb3B0aW9uKS5cclxuXHRzZXRNYXhCb3VuZHM6IGZ1bmN0aW9uIChib3VuZHMpIHtcclxuXHRcdGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblxyXG5cdFx0aWYgKCFib3VuZHMuaXNWYWxpZCgpKSB7XHJcblx0XHRcdHRoaXMub3B0aW9ucy5tYXhCb3VuZHMgPSBudWxsO1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5vZmYoJ21vdmVlbmQnLCB0aGlzLl9wYW5JbnNpZGVNYXhCb3VuZHMpO1xyXG5cdFx0fSBlbHNlIGlmICh0aGlzLm9wdGlvbnMubWF4Qm91bmRzKSB7XHJcblx0XHRcdHRoaXMub2ZmKCdtb3ZlZW5kJywgdGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLm9wdGlvbnMubWF4Qm91bmRzID0gYm91bmRzO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sb2FkZWQpIHtcclxuXHRcdFx0dGhpcy5fcGFuSW5zaWRlTWF4Qm91bmRzKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMub24oJ21vdmVlbmQnLCB0aGlzLl9wYW5JbnNpZGVNYXhCb3VuZHMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TWluWm9vbSh6b29tOiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgbG93ZXIgbGltaXQgZm9yIHRoZSBhdmFpbGFibGUgem9vbSBsZXZlbHMgKHNlZSB0aGUgW21pblpvb21dKCNtYXAtbWluem9vbSkgb3B0aW9uKS5cclxuXHRzZXRNaW5ab29tOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0dGhpcy5vcHRpb25zLm1pblpvb20gPSB6b29tO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sb2FkZWQgJiYgdGhpcy5nZXRab29tKCkgPCB0aGlzLm9wdGlvbnMubWluWm9vbSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5zZXRab29tKHpvb20pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TWF4Wm9vbSh6b29tOiBOdW1iZXIpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgdXBwZXIgbGltaXQgZm9yIHRoZSBhdmFpbGFibGUgem9vbSBsZXZlbHMgKHNlZSB0aGUgW21heFpvb21dKCNtYXAtbWF4em9vbSkgb3B0aW9uKS5cclxuXHRzZXRNYXhab29tOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0dGhpcy5vcHRpb25zLm1heFpvb20gPSB6b29tO1xyXG5cclxuXHRcdGlmICh0aGlzLl9sb2FkZWQgJiYgKHRoaXMuZ2V0Wm9vbSgpID4gdGhpcy5vcHRpb25zLm1heFpvb20pKSB7XHJcblx0XHRcdHJldHVybiB0aGlzLnNldFpvb20oem9vbSk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwYW5JbnNpZGVCb3VuZHMoYm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM/OiBQYW4gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBQYW5zIHRoZSBtYXAgdG8gdGhlIGNsb3Nlc3QgdmlldyB0aGF0IHdvdWxkIGxpZSBpbnNpZGUgdGhlIGdpdmVuIGJvdW5kcyAoaWYgaXQncyBub3QgYWxyZWFkeSksIGNvbnRyb2xsaW5nIHRoZSBhbmltYXRpb24gdXNpbmcgdGhlIG9wdGlvbnMgc3BlY2lmaWMsIGlmIGFueS5cclxuXHRwYW5JbnNpZGVCb3VuZHM6IGZ1bmN0aW9uIChib3VuZHMsIG9wdGlvbnMpIHtcclxuXHRcdHRoaXMuX2VuZm9yY2luZ0JvdW5kcyA9IHRydWU7XHJcblx0XHR2YXIgY2VudGVyID0gdGhpcy5nZXRDZW50ZXIoKSxcclxuXHRcdCAgICBuZXdDZW50ZXIgPSB0aGlzLl9saW1pdENlbnRlcihjZW50ZXIsIHRoaXMuX3pvb20sIEwubGF0TG5nQm91bmRzKGJvdW5kcykpO1xyXG5cclxuXHRcdGlmICghY2VudGVyLmVxdWFscyhuZXdDZW50ZXIpKSB7XHJcblx0XHRcdHRoaXMucGFuVG8obmV3Q2VudGVyLCBvcHRpb25zKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9lbmZvcmNpbmdCb3VuZHMgPSBmYWxzZTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaW52YWxpZGF0ZVNpemUob3B0aW9uczogWm9vbS9QYW4gb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBDaGVja3MgaWYgdGhlIG1hcCBjb250YWluZXIgc2l6ZSBjaGFuZ2VkIGFuZCB1cGRhdGVzIHRoZSBtYXAgaWYgc28g4oCUXHJcblx0Ly8gY2FsbCBpdCBhZnRlciB5b3UndmUgY2hhbmdlZCB0aGUgbWFwIHNpemUgZHluYW1pY2FsbHksIGFsc28gYW5pbWF0aW5nXHJcblx0Ly8gcGFuIGJ5IGRlZmF1bHQuIElmIGBvcHRpb25zLnBhbmAgaXMgYGZhbHNlYCwgcGFubmluZyB3aWxsIG5vdCBvY2N1ci5cclxuXHQvLyBJZiBgb3B0aW9ucy5kZWJvdW5jZU1vdmVlbmRgIGlzIGB0cnVlYCwgaXQgd2lsbCBkZWxheSBgbW92ZWVuZGAgZXZlbnQgc29cclxuXHQvLyB0aGF0IGl0IGRvZXNuJ3QgaGFwcGVuIG9mdGVuIGV2ZW4gaWYgdGhlIG1ldGhvZCBpcyBjYWxsZWQgbWFueVxyXG5cdC8vIHRpbWVzIGluIGEgcm93LlxyXG5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIGludmFsaWRhdGVTaXplKGFuaW1hdGU6IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gQ2hlY2tzIGlmIHRoZSBtYXAgY29udGFpbmVyIHNpemUgY2hhbmdlZCBhbmQgdXBkYXRlcyB0aGUgbWFwIGlmIHNvIOKAlFxyXG5cdC8vIGNhbGwgaXQgYWZ0ZXIgeW91J3ZlIGNoYW5nZWQgdGhlIG1hcCBzaXplIGR5bmFtaWNhbGx5LCBhbHNvIGFuaW1hdGluZ1xyXG5cdC8vIHBhbiBieSBkZWZhdWx0LlxyXG5cdGludmFsaWRhdGVTaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdFx0aWYgKCF0aGlzLl9sb2FkZWQpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRvcHRpb25zID0gTC5leHRlbmQoe1xyXG5cdFx0XHRhbmltYXRlOiBmYWxzZSxcclxuXHRcdFx0cGFuOiB0cnVlXHJcblx0XHR9LCBvcHRpb25zID09PSB0cnVlID8ge2FuaW1hdGU6IHRydWV9IDogb3B0aW9ucyk7XHJcblxyXG5cdFx0dmFyIG9sZFNpemUgPSB0aGlzLmdldFNpemUoKTtcclxuXHRcdHRoaXMuX3NpemVDaGFuZ2VkID0gdHJ1ZTtcclxuXHRcdHRoaXMuX2xhc3RDZW50ZXIgPSBudWxsO1xyXG5cclxuXHRcdHZhciBuZXdTaXplID0gdGhpcy5nZXRTaXplKCksXHJcblx0XHQgICAgb2xkQ2VudGVyID0gb2xkU2l6ZS5kaXZpZGVCeSgyKS5yb3VuZCgpLFxyXG5cdFx0ICAgIG5ld0NlbnRlciA9IG5ld1NpemUuZGl2aWRlQnkoMikucm91bmQoKSxcclxuXHRcdCAgICBvZmZzZXQgPSBvbGRDZW50ZXIuc3VidHJhY3QobmV3Q2VudGVyKTtcclxuXHJcblx0XHRpZiAoIW9mZnNldC54ICYmICFvZmZzZXQueSkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdGlmIChvcHRpb25zLmFuaW1hdGUgJiYgb3B0aW9ucy5wYW4pIHtcclxuXHRcdFx0dGhpcy5wYW5CeShvZmZzZXQpO1xyXG5cclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGlmIChvcHRpb25zLnBhbikge1xyXG5cdFx0XHRcdHRoaXMuX3Jhd1BhbkJ5KG9mZnNldCk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdHRoaXMuZmlyZSgnbW92ZScpO1xyXG5cclxuXHRcdFx0aWYgKG9wdGlvbnMuZGVib3VuY2VNb3ZlZW5kKSB7XHJcblx0XHRcdFx0Y2xlYXJUaW1lb3V0KHRoaXMuX3NpemVUaW1lcik7XHJcblx0XHRcdFx0dGhpcy5fc2l6ZVRpbWVyID0gc2V0VGltZW91dChMLmJpbmQodGhpcy5maXJlLCB0aGlzLCAnbW92ZWVuZCcpLCAyMDApO1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdHRoaXMuZmlyZSgnbW92ZWVuZCcpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQHNlY3Rpb24gTWFwIHN0YXRlIGNoYW5nZSBldmVudHNcclxuXHRcdC8vIEBldmVudCByZXNpemU6IFJlc2l6ZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaXMgcmVzaXplZC5cclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ3Jlc2l6ZScsIHtcclxuXHRcdFx0b2xkU2l6ZTogb2xkU2l6ZSxcclxuXHRcdFx0bmV3U2l6ZTogbmV3U2l6ZVxyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgbW9kaWZ5aW5nIG1hcCBzdGF0ZVxyXG5cdC8vIEBtZXRob2Qgc3RvcCgpOiB0aGlzXHJcblx0Ly8gU3RvcHMgdGhlIGN1cnJlbnRseSBydW5uaW5nIGBwYW5Ub2Agb3IgYGZseVRvYCBhbmltYXRpb24sIGlmIGFueS5cclxuXHRzdG9wOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLnNldFpvb20odGhpcy5fbGltaXRab29tKHRoaXMuX3pvb20pKTtcclxuXHRcdGlmICghdGhpcy5vcHRpb25zLnpvb21TbmFwKSB7XHJcblx0XHRcdHRoaXMuZmlyZSgndmlld3Jlc2V0Jyk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5fc3RvcCgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIEdlb2xvY2F0aW9uIG1ldGhvZHNcclxuXHQvLyBAbWV0aG9kIGxvY2F0ZShvcHRpb25zPzogTG9jYXRlIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gVHJpZXMgdG8gbG9jYXRlIHRoZSB1c2VyIHVzaW5nIHRoZSBHZW9sb2NhdGlvbiBBUEksIGZpcmluZyBhIFtgbG9jYXRpb25mb3VuZGBdKCNtYXAtbG9jYXRpb25mb3VuZClcclxuXHQvLyBldmVudCB3aXRoIGxvY2F0aW9uIGRhdGEgb24gc3VjY2VzcyBvciBhIFtgbG9jYXRpb25lcnJvcmBdKCNtYXAtbG9jYXRpb25lcnJvcikgZXZlbnQgb24gZmFpbHVyZSxcclxuXHQvLyBhbmQgb3B0aW9uYWxseSBzZXRzIHRoZSBtYXAgdmlldyB0byB0aGUgdXNlcidzIGxvY2F0aW9uIHdpdGggcmVzcGVjdCB0b1xyXG5cdC8vIGRldGVjdGlvbiBhY2N1cmFjeSAob3IgdG8gdGhlIHdvcmxkIHZpZXcgaWYgZ2VvbG9jYXRpb24gZmFpbGVkKS5cclxuXHQvLyBOb3RlIHRoYXQsIGlmIHlvdXIgcGFnZSBkb2Vzbid0IHVzZSBIVFRQUywgdGhpcyBtZXRob2Qgd2lsbCBmYWlsIGluXHJcblx0Ly8gbW9kZXJuIGJyb3dzZXJzIChbQ2hyb21lIDUwIGFuZCBuZXdlcl0oaHR0cHM6Ly9zaXRlcy5nb29nbGUuY29tL2EvY2hyb21pdW0ub3JnL2Rldi9Ib21lL2Nocm9taXVtLXNlY3VyaXR5L2RlcHJlY2F0aW5nLXBvd2VyZnVsLWZlYXR1cmVzLW9uLWluc2VjdXJlLW9yaWdpbnMpKVxyXG5cdC8vIFNlZSBgTG9jYXRlIG9wdGlvbnNgIGZvciBtb3JlIGRldGFpbHMuXHJcblx0bG9jYXRlOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cclxuXHRcdG9wdGlvbnMgPSB0aGlzLl9sb2NhdGVPcHRpb25zID0gTC5leHRlbmQoe1xyXG5cdFx0XHR0aW1lb3V0OiAxMDAwMCxcclxuXHRcdFx0d2F0Y2g6IGZhbHNlXHJcblx0XHRcdC8vIHNldFZpZXc6IGZhbHNlXHJcblx0XHRcdC8vIG1heFpvb206IDxOdW1iZXI+XHJcblx0XHRcdC8vIG1heGltdW1BZ2U6IDBcclxuXHRcdFx0Ly8gZW5hYmxlSGlnaEFjY3VyYWN5OiBmYWxzZVxyXG5cdFx0fSwgb3B0aW9ucyk7XHJcblxyXG5cdFx0aWYgKCEoJ2dlb2xvY2F0aW9uJyBpbiBuYXZpZ2F0b3IpKSB7XHJcblx0XHRcdHRoaXMuX2hhbmRsZUdlb2xvY2F0aW9uRXJyb3Ioe1xyXG5cdFx0XHRcdGNvZGU6IDAsXHJcblx0XHRcdFx0bWVzc2FnZTogJ0dlb2xvY2F0aW9uIG5vdCBzdXBwb3J0ZWQuJ1xyXG5cdFx0XHR9KTtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIG9uUmVzcG9uc2UgPSBMLmJpbmQodGhpcy5faGFuZGxlR2VvbG9jYXRpb25SZXNwb25zZSwgdGhpcyksXHJcblx0XHQgICAgb25FcnJvciA9IEwuYmluZCh0aGlzLl9oYW5kbGVHZW9sb2NhdGlvbkVycm9yLCB0aGlzKTtcclxuXHJcblx0XHRpZiAob3B0aW9ucy53YXRjaCkge1xyXG5cdFx0XHR0aGlzLl9sb2NhdGlvbldhdGNoSWQgPVxyXG5cdFx0XHQgICAgICAgIG5hdmlnYXRvci5nZW9sb2NhdGlvbi53YXRjaFBvc2l0aW9uKG9uUmVzcG9uc2UsIG9uRXJyb3IsIG9wdGlvbnMpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0bmF2aWdhdG9yLmdlb2xvY2F0aW9uLmdldEN1cnJlbnRQb3NpdGlvbihvblJlc3BvbnNlLCBvbkVycm9yLCBvcHRpb25zKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc3RvcExvY2F0ZSgpOiB0aGlzXHJcblx0Ly8gU3RvcHMgd2F0Y2hpbmcgbG9jYXRpb24gcHJldmlvdXNseSBpbml0aWF0ZWQgYnkgYG1hcC5sb2NhdGUoe3dhdGNoOiB0cnVlfSlgXHJcblx0Ly8gYW5kIGFib3J0cyByZXNldHRpbmcgdGhlIG1hcCB2aWV3IGlmIG1hcC5sb2NhdGUgd2FzIGNhbGxlZCB3aXRoXHJcblx0Ly8gYHtzZXRWaWV3OiB0cnVlfWAuXHJcblx0c3RvcExvY2F0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKG5hdmlnYXRvci5nZW9sb2NhdGlvbiAmJiBuYXZpZ2F0b3IuZ2VvbG9jYXRpb24uY2xlYXJXYXRjaCkge1xyXG5cdFx0XHRuYXZpZ2F0b3IuZ2VvbG9jYXRpb24uY2xlYXJXYXRjaCh0aGlzLl9sb2NhdGlvbldhdGNoSWQpO1xyXG5cdFx0fVxyXG5cdFx0aWYgKHRoaXMuX2xvY2F0ZU9wdGlvbnMpIHtcclxuXHRcdFx0dGhpcy5fbG9jYXRlT3B0aW9ucy5zZXRWaWV3ID0gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfaGFuZGxlR2VvbG9jYXRpb25FcnJvcjogZnVuY3Rpb24gKGVycm9yKSB7XHJcblx0XHR2YXIgYyA9IGVycm9yLmNvZGUsXHJcblx0XHQgICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2UgfHxcclxuXHRcdCAgICAgICAgICAgIChjID09PSAxID8gJ3Blcm1pc3Npb24gZGVuaWVkJyA6XHJcblx0XHQgICAgICAgICAgICAoYyA9PT0gMiA/ICdwb3NpdGlvbiB1bmF2YWlsYWJsZScgOiAndGltZW91dCcpKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9jYXRlT3B0aW9ucy5zZXRWaWV3ICYmICF0aGlzLl9sb2FkZWQpIHtcclxuXHRcdFx0dGhpcy5maXRXb3JsZCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEBzZWN0aW9uIExvY2F0aW9uIGV2ZW50c1xyXG5cdFx0Ly8gQGV2ZW50IGxvY2F0aW9uZXJyb3I6IEVycm9yRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gZ2VvbG9jYXRpb24gKHVzaW5nIHRoZSBbYGxvY2F0ZWBdKCNtYXAtbG9jYXRlKSBtZXRob2QpIGZhaWxlZC5cclxuXHRcdHRoaXMuZmlyZSgnbG9jYXRpb25lcnJvcicsIHtcclxuXHRcdFx0Y29kZTogYyxcclxuXHRcdFx0bWVzc2FnZTogJ0dlb2xvY2F0aW9uIGVycm9yOiAnICsgbWVzc2FnZSArICcuJ1xyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0X2hhbmRsZUdlb2xvY2F0aW9uUmVzcG9uc2U6IGZ1bmN0aW9uIChwb3MpIHtcclxuXHRcdHZhciBsYXQgPSBwb3MuY29vcmRzLmxhdGl0dWRlLFxyXG5cdFx0ICAgIGxuZyA9IHBvcy5jb29yZHMubG9uZ2l0dWRlLFxyXG5cdFx0ICAgIGxhdGxuZyA9IG5ldyBMLkxhdExuZyhsYXQsIGxuZyksXHJcblx0XHQgICAgYm91bmRzID0gbGF0bG5nLnRvQm91bmRzKHBvcy5jb29yZHMuYWNjdXJhY3kpLFxyXG5cdFx0ICAgIG9wdGlvbnMgPSB0aGlzLl9sb2NhdGVPcHRpb25zO1xyXG5cclxuXHRcdGlmIChvcHRpb25zLnNldFZpZXcpIHtcclxuXHRcdFx0dmFyIHpvb20gPSB0aGlzLmdldEJvdW5kc1pvb20oYm91bmRzKTtcclxuXHRcdFx0dGhpcy5zZXRWaWV3KGxhdGxuZywgb3B0aW9ucy5tYXhab29tID8gTWF0aC5taW4oem9vbSwgb3B0aW9ucy5tYXhab29tKSA6IHpvb20pO1xyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBkYXRhID0ge1xyXG5cdFx0XHRsYXRsbmc6IGxhdGxuZyxcclxuXHRcdFx0Ym91bmRzOiBib3VuZHMsXHJcblx0XHRcdHRpbWVzdGFtcDogcG9zLnRpbWVzdGFtcFxyXG5cdFx0fTtcclxuXHJcblx0XHRmb3IgKHZhciBpIGluIHBvcy5jb29yZHMpIHtcclxuXHRcdFx0aWYgKHR5cGVvZiBwb3MuY29vcmRzW2ldID09PSAnbnVtYmVyJykge1xyXG5cdFx0XHRcdGRhdGFbaV0gPSBwb3MuY29vcmRzW2ldO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxvY2F0aW9uZm91bmQ6IExvY2F0aW9uRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gZ2VvbG9jYXRpb24gKHVzaW5nIHRoZSBbYGxvY2F0ZWBdKCNtYXAtbG9jYXRlKSBtZXRob2QpXHJcblx0XHQvLyB3ZW50IHN1Y2Nlc3NmdWxseS5cclxuXHRcdHRoaXMuZmlyZSgnbG9jYXRpb25mb3VuZCcsIGRhdGEpO1xyXG5cdH0sXHJcblxyXG5cdC8vIFRPRE8gaGFuZGxlci5hZGRUb1xyXG5cdC8vIFRPRE8gQXBwcm9waWF0ZSBkb2NzIHNlY3Rpb24/XHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cdC8vIEBtZXRob2QgYWRkSGFuZGxlcihuYW1lOiBTdHJpbmcsIEhhbmRsZXJDbGFzczogRnVuY3Rpb24pOiB0aGlzXHJcblx0Ly8gQWRkcyBhIG5ldyBgSGFuZGxlcmAgdG8gdGhlIG1hcCwgZ2l2ZW4gaXRzIG5hbWUgYW5kIGNvbnN0cnVjdG9yIGZ1bmN0aW9uLlxyXG5cdGFkZEhhbmRsZXI6IGZ1bmN0aW9uIChuYW1lLCBIYW5kbGVyQ2xhc3MpIHtcclxuXHRcdGlmICghSGFuZGxlckNsYXNzKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0dmFyIGhhbmRsZXIgPSB0aGlzW25hbWVdID0gbmV3IEhhbmRsZXJDbGFzcyh0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9oYW5kbGVycy5wdXNoKGhhbmRsZXIpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnNbbmFtZV0pIHtcclxuXHRcdFx0aGFuZGxlci5lbmFibGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHJlbW92ZSgpOiB0aGlzXHJcblx0Ly8gRGVzdHJveXMgdGhlIG1hcCBhbmQgY2xlYXJzIGFsbCByZWxhdGVkIGV2ZW50IGxpc3RlbmVycy5cclxuXHRyZW1vdmU6IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHR0aGlzLl9pbml0RXZlbnRzKHRydWUpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9jb250YWluZXJJZCAhPT0gdGhpcy5fY29udGFpbmVyLl9sZWFmbGV0X2lkKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignTWFwIGNvbnRhaW5lciBpcyBiZWluZyByZXVzZWQgYnkgYW5vdGhlciBpbnN0YW5jZScpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRyeSB7XHJcblx0XHRcdC8vIHRocm93cyBlcnJvciBpbiBJRTYtOFxyXG5cdFx0XHRkZWxldGUgdGhpcy5fY29udGFpbmVyLl9sZWFmbGV0X2lkO1xyXG5cdFx0XHRkZWxldGUgdGhpcy5fY29udGFpbmVySWQ7XHJcblx0XHR9IGNhdGNoIChlKSB7XHJcblx0XHRcdC8qZXNsaW50LWRpc2FibGUgKi9cclxuXHRcdFx0dGhpcy5fY29udGFpbmVyLl9sZWFmbGV0X2lkID0gdW5kZWZpbmVkO1xyXG5cdFx0XHQvKmVzbGludC1lbmFibGUgKi9cclxuXHRcdFx0dGhpcy5fY29udGFpbmVySWQgPSB1bmRlZmluZWQ7XHJcblx0XHR9XHJcblxyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9tYXBQYW5lKTtcclxuXHJcblx0XHRpZiAodGhpcy5fY2xlYXJDb250cm9sUG9zKSB7XHJcblx0XHRcdHRoaXMuX2NsZWFyQ29udHJvbFBvcygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2NsZWFySGFuZGxlcnMoKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdC8vIEBzZWN0aW9uIE1hcCBzdGF0ZSBjaGFuZ2UgZXZlbnRzXHJcblx0XHRcdC8vIEBldmVudCB1bmxvYWQ6IEV2ZW50XHJcblx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcCBpcyBkZXN0cm95ZWQgd2l0aCBbcmVtb3ZlXSgjbWFwLXJlbW92ZSkgbWV0aG9kLlxyXG5cdFx0XHR0aGlzLmZpcmUoJ3VubG9hZCcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMuX2xheWVyc1tpXS5yZW1vdmUoKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAc2VjdGlvbiBPdGhlciBNZXRob2RzXHJcblx0Ly8gQG1ldGhvZCBjcmVhdGVQYW5lKG5hbWU6IFN0cmluZywgY29udGFpbmVyPzogSFRNTEVsZW1lbnQpOiBIVE1MRWxlbWVudFxyXG5cdC8vIENyZWF0ZXMgYSBuZXcgW21hcCBwYW5lXSgjbWFwLXBhbmUpIHdpdGggdGhlIGdpdmVuIG5hbWUgaWYgaXQgZG9lc24ndCBleGlzdCBhbHJlYWR5LFxyXG5cdC8vIHRoZW4gcmV0dXJucyBpdC4gVGhlIHBhbmUgaXMgY3JlYXRlZCBhcyBhIGNoaWxkcmVuIG9mIGBjb250YWluZXJgLCBvclxyXG5cdC8vIGFzIGEgY2hpbGRyZW4gb2YgdGhlIG1haW4gbWFwIHBhbmUgaWYgbm90IHNldC5cclxuXHRjcmVhdGVQYW5lOiBmdW5jdGlvbiAobmFtZSwgY29udGFpbmVyKSB7XHJcblx0XHR2YXIgY2xhc3NOYW1lID0gJ2xlYWZsZXQtcGFuZScgKyAobmFtZSA/ICcgbGVhZmxldC0nICsgbmFtZS5yZXBsYWNlKCdQYW5lJywgJycpICsgJy1wYW5lJyA6ICcnKSxcclxuXHRcdCAgICBwYW5lID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lLCBjb250YWluZXIgfHwgdGhpcy5fbWFwUGFuZSk7XHJcblxyXG5cdFx0aWYgKG5hbWUpIHtcclxuXHRcdFx0dGhpcy5fcGFuZXNbbmFtZV0gPSBwYW5lO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHBhbmU7XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgR2V0dGluZyBNYXAgU3RhdGVcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRDZW50ZXIoKTogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgZ2VvZ3JhcGhpY2FsIGNlbnRlciBvZiB0aGUgbWFwIHZpZXdcclxuXHRnZXRDZW50ZXI6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX2NoZWNrSWZMb2FkZWQoKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbGFzdENlbnRlciAmJiAhdGhpcy5fbW92ZWQoKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy5fbGFzdENlbnRlcjtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLl9nZXRDZW50ZXJMYXllclBvaW50KCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Wm9vbSgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBjdXJyZW50IHpvb20gbGV2ZWwgb2YgdGhlIG1hcCB2aWV3XHJcblx0Z2V0Wm9vbTogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3pvb207XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHMoKTogTGF0TG5nQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgZ2VvZ3JhcGhpY2FsIGJvdW5kcyB2aXNpYmxlIGluIHRoZSBjdXJyZW50IG1hcCB2aWV3XHJcblx0Z2V0Qm91bmRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgYm91bmRzID0gdGhpcy5nZXRQaXhlbEJvdW5kcygpLFxyXG5cdFx0ICAgIHN3ID0gdGhpcy51bnByb2plY3QoYm91bmRzLmdldEJvdHRvbUxlZnQoKSksXHJcblx0XHQgICAgbmUgPSB0aGlzLnVucHJvamVjdChib3VuZHMuZ2V0VG9wUmlnaHQoKSk7XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZ0JvdW5kcyhzdywgbmUpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TWluWm9vbSgpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBtaW5pbXVtIHpvb20gbGV2ZWwgb2YgdGhlIG1hcCAoaWYgc2V0IGluIHRoZSBgbWluWm9vbWAgb3B0aW9uIG9mIHRoZSBtYXAgb3Igb2YgYW55IGxheWVycyksIG9yIGAwYCBieSBkZWZhdWx0LlxyXG5cdGdldE1pblpvb206IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMubWluWm9vbSA9PT0gdW5kZWZpbmVkID8gdGhpcy5fbGF5ZXJzTWluWm9vbSB8fCAwIDogdGhpcy5vcHRpb25zLm1pblpvb207XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRNYXhab29tKCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIG1heGltdW0gem9vbSBsZXZlbCBvZiB0aGUgbWFwIChpZiBzZXQgaW4gdGhlIGBtYXhab29tYCBvcHRpb24gb2YgdGhlIG1hcCBvciBvZiBhbnkgbGF5ZXJzKS5cclxuXHRnZXRNYXhab29tOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLm1heFpvb20gPT09IHVuZGVmaW5lZCA/XHJcblx0XHRcdCh0aGlzLl9sYXllcnNNYXhab29tID09PSB1bmRlZmluZWQgPyBJbmZpbml0eSA6IHRoaXMuX2xheWVyc01heFpvb20pIDpcclxuXHRcdFx0dGhpcy5vcHRpb25zLm1heFpvb207XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRCb3VuZHNab29tKGJvdW5kczogTGF0TG5nQm91bmRzLCBpbnNpZGU/OiBCb29sZWFuKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgbWF4aW11bSB6b29tIGxldmVsIG9uIHdoaWNoIHRoZSBnaXZlbiBib3VuZHMgZml0IHRvIHRoZSBtYXBcclxuXHQvLyB2aWV3IGluIGl0cyBlbnRpcmV0eS4gSWYgYGluc2lkZWAgKG9wdGlvbmFsKSBpcyBzZXQgdG8gYHRydWVgLCB0aGUgbWV0aG9kXHJcblx0Ly8gaW5zdGVhZCByZXR1cm5zIHRoZSBtaW5pbXVtIHpvb20gbGV2ZWwgb24gd2hpY2ggdGhlIG1hcCB2aWV3IGZpdHMgaW50b1xyXG5cdC8vIHRoZSBnaXZlbiBib3VuZHMgaW4gaXRzIGVudGlyZXR5LlxyXG5cdGdldEJvdW5kc1pvb206IGZ1bmN0aW9uIChib3VuZHMsIGluc2lkZSwgcGFkZGluZykgeyAvLyAoTGF0TG5nQm91bmRzWywgQm9vbGVhbiwgUG9pbnRdKSAtPiBOdW1iZXJcclxuXHRcdGJvdW5kcyA9IEwubGF0TG5nQm91bmRzKGJvdW5kcyk7XHJcblx0XHRwYWRkaW5nID0gTC5wb2ludChwYWRkaW5nIHx8IFswLCAwXSk7XHJcblxyXG5cdFx0dmFyIHpvb20gPSB0aGlzLmdldFpvb20oKSB8fCAwLFxyXG5cdFx0ICAgIG1pbiA9IHRoaXMuZ2V0TWluWm9vbSgpLFxyXG5cdFx0ICAgIG1heCA9IHRoaXMuZ2V0TWF4Wm9vbSgpLFxyXG5cdFx0ICAgIG53ID0gYm91bmRzLmdldE5vcnRoV2VzdCgpLFxyXG5cdFx0ICAgIHNlID0gYm91bmRzLmdldFNvdXRoRWFzdCgpLFxyXG5cdFx0ICAgIHNpemUgPSB0aGlzLmdldFNpemUoKS5zdWJ0cmFjdChwYWRkaW5nKSxcclxuXHRcdCAgICBib3VuZHNTaXplID0gTC5ib3VuZHModGhpcy5wcm9qZWN0KHNlLCB6b29tKSwgdGhpcy5wcm9qZWN0KG53LCB6b29tKSkuZ2V0U2l6ZSgpLFxyXG5cdFx0ICAgIHNuYXAgPSBMLkJyb3dzZXIuYW55M2QgPyB0aGlzLm9wdGlvbnMuem9vbVNuYXAgOiAxO1xyXG5cclxuXHRcdHZhciBzY2FsZSA9IE1hdGgubWluKHNpemUueCAvIGJvdW5kc1NpemUueCwgc2l6ZS55IC8gYm91bmRzU2l6ZS55KTtcclxuXHRcdHpvb20gPSB0aGlzLmdldFNjYWxlWm9vbShzY2FsZSwgem9vbSk7XHJcblxyXG5cdFx0aWYgKHNuYXApIHtcclxuXHRcdFx0em9vbSA9IE1hdGgucm91bmQoem9vbSAvIChzbmFwIC8gMTAwKSkgKiAoc25hcCAvIDEwMCk7IC8vIGRvbid0IGp1bXAgaWYgd2l0aGluIDElIG9mIGEgc25hcCBsZXZlbFxyXG5cdFx0XHR6b29tID0gaW5zaWRlID8gTWF0aC5jZWlsKHpvb20gLyBzbmFwKSAqIHNuYXAgOiBNYXRoLmZsb29yKHpvb20gLyBzbmFwKSAqIHNuYXA7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIE1hdGgubWF4KG1pbiwgTWF0aC5taW4obWF4LCB6b29tKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRTaXplKCk6IFBvaW50XHJcblx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCBzaXplIG9mIHRoZSBtYXAgY29udGFpbmVyIChpbiBwaXhlbHMpLlxyXG5cdGdldFNpemU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fc2l6ZSB8fCB0aGlzLl9zaXplQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLl9zaXplID0gbmV3IEwuUG9pbnQoXHJcblx0XHRcdFx0dGhpcy5fY29udGFpbmVyLmNsaWVudFdpZHRoIHx8IDAsXHJcblx0XHRcdFx0dGhpcy5fY29udGFpbmVyLmNsaWVudEhlaWdodCB8fCAwKTtcclxuXHJcblx0XHRcdHRoaXMuX3NpemVDaGFuZ2VkID0gZmFsc2U7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcy5fc2l6ZS5jbG9uZSgpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UGl4ZWxCb3VuZHMoKTogQm91bmRzXHJcblx0Ly8gUmV0dXJucyB0aGUgYm91bmRzIG9mIHRoZSBjdXJyZW50IG1hcCB2aWV3IGluIHByb2plY3RlZCBwaXhlbFxyXG5cdC8vIGNvb3JkaW5hdGVzIChzb21ldGltZXMgdXNlZnVsIGluIGxheWVyIGFuZCBvdmVybGF5IGltcGxlbWVudGF0aW9ucykuXHJcblx0Z2V0UGl4ZWxCb3VuZHM6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcclxuXHRcdHZhciB0b3BMZWZ0UG9pbnQgPSB0aGlzLl9nZXRUb3BMZWZ0UG9pbnQoY2VudGVyLCB6b29tKTtcclxuXHRcdHJldHVybiBuZXcgTC5Cb3VuZHModG9wTGVmdFBvaW50LCB0b3BMZWZ0UG9pbnQuYWRkKHRoaXMuZ2V0U2l6ZSgpKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gVE9ETzogQ2hlY2sgc2VtYW50aWNzIC0gaXNuJ3QgdGhlIHBpeGVsIG9yaWdpbiB0aGUgMCwwIGNvb3JkIHJlbGF0aXZlIHRvXHJcblx0Ly8gdGhlIG1hcCBwYW5lPyBcImxlZnQgcG9pbnQgb2YgdGhlIG1hcCBsYXllclwiIGNhbiBiZSBjb25mdXNpbmcsIHNwZWNpYWxseVxyXG5cdC8vIHNpbmNlIHRoZXJlIGNhbiBiZSBuZWdhdGl2ZSBvZmZzZXRzLlxyXG5cdC8vIEBtZXRob2QgZ2V0UGl4ZWxPcmlnaW4oKTogUG9pbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBwcm9qZWN0ZWQgcGl4ZWwgY29vcmRpbmF0ZXMgb2YgdGhlIHRvcCBsZWZ0IHBvaW50IG9mXHJcblx0Ly8gdGhlIG1hcCBsYXllciAodXNlZnVsIGluIGN1c3RvbSBsYXllciBhbmQgb3ZlcmxheSBpbXBsZW1lbnRhdGlvbnMpLlxyXG5cdGdldFBpeGVsT3JpZ2luOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9jaGVja0lmTG9hZGVkKCk7XHJcblx0XHRyZXR1cm4gdGhpcy5fcGl4ZWxPcmlnaW47XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRQaXhlbFdvcmxkQm91bmRzKHpvb20/OiBOdW1iZXIpOiBCb3VuZHNcclxuXHQvLyBSZXR1cm5zIHRoZSB3b3JsZCdzIGJvdW5kcyBpbiBwaXhlbCBjb29yZGluYXRlcyBmb3Igem9vbSBsZXZlbCBgem9vbWAuXHJcblx0Ly8gSWYgYHpvb21gIGlzIG9taXR0ZWQsIHRoZSBtYXAncyBjdXJyZW50IHpvb20gbGV2ZWwgaXMgdXNlZC5cclxuXHRnZXRQaXhlbFdvcmxkQm91bmRzOiBmdW5jdGlvbiAoem9vbSkge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMuZ2V0UHJvamVjdGVkQm91bmRzKHpvb20gPT09IHVuZGVmaW5lZCA/IHRoaXMuZ2V0Wm9vbSgpIDogem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cclxuXHQvLyBAbWV0aG9kIGdldFBhbmUocGFuZTogU3RyaW5nfEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIGEgW21hcCBwYW5lXSgjbWFwLXBhbmUpLCBnaXZlbiBpdHMgbmFtZSBvciBpdHMgSFRNTCBlbGVtZW50IChpdHMgaWRlbnRpdHkpLlxyXG5cdGdldFBhbmU6IGZ1bmN0aW9uIChwYW5lKSB7XHJcblx0XHRyZXR1cm4gdHlwZW9mIHBhbmUgPT09ICdzdHJpbmcnID8gdGhpcy5fcGFuZXNbcGFuZV0gOiBwYW5lO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UGFuZXMoKTogT2JqZWN0XHJcblx0Ly8gUmV0dXJucyBhIHBsYWluIG9iamVjdCBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhbGwgW3BhbmVzXSgjbWFwLXBhbmUpIGFzIGtleXMgYW5kXHJcblx0Ly8gdGhlIHBhbmVzIGFzIHZhbHVlcy5cclxuXHRnZXRQYW5lczogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3BhbmVzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q29udGFpbmVyOiBIVE1MRWxlbWVudFxyXG5cdC8vIFJldHVybnMgdGhlIEhUTUwgZWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSBtYXAuXHJcblx0Z2V0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBAc2VjdGlvbiBDb252ZXJzaW9uIE1ldGhvZHNcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRab29tU2NhbGUodG9ab29tOiBOdW1iZXIsIGZyb21ab29tOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSBzY2FsZSBmYWN0b3IgdG8gYmUgYXBwbGllZCB0byBhIG1hcCB0cmFuc2l0aW9uIGZyb20gem9vbSBsZXZlbFxyXG5cdC8vIGBmcm9tWm9vbWAgdG8gYHRvWm9vbWAuIFVzZWQgaW50ZXJuYWxseSB0byBoZWxwIHdpdGggem9vbSBhbmltYXRpb25zLlxyXG5cdGdldFpvb21TY2FsZTogZnVuY3Rpb24gKHRvWm9vbSwgZnJvbVpvb20pIHtcclxuXHRcdC8vIFRPRE8gcmVwbGFjZSB3aXRoIHVuaXZlcnNhbCBpbXBsZW1lbnRhdGlvbiBhZnRlciByZWZhY3RvcmluZyBwcm9qZWN0aW9uc1xyXG5cdFx0dmFyIGNycyA9IHRoaXMub3B0aW9ucy5jcnM7XHJcblx0XHRmcm9tWm9vbSA9IGZyb21ab29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogZnJvbVpvb207XHJcblx0XHRyZXR1cm4gY3JzLnNjYWxlKHRvWm9vbSkgLyBjcnMuc2NhbGUoZnJvbVpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0U2NhbGVab29tKHNjYWxlOiBOdW1iZXIsIGZyb21ab29tOiBOdW1iZXIpOiBOdW1iZXJcclxuXHQvLyBSZXR1cm5zIHRoZSB6b29tIGxldmVsIHRoYXQgdGhlIG1hcCB3b3VsZCBlbmQgdXAgYXQsIGlmIGl0IGlzIGF0IGBmcm9tWm9vbWBcclxuXHQvLyBsZXZlbCBhbmQgZXZlcnl0aGluZyBpcyBzY2FsZWQgYnkgYSBmYWN0b3Igb2YgYHNjYWxlYC4gSW52ZXJzZSBvZlxyXG5cdC8vIFtgZ2V0Wm9vbVNjYWxlYF0oI21hcC1nZXRab29tU2NhbGUpLlxyXG5cdGdldFNjYWxlWm9vbTogZnVuY3Rpb24gKHNjYWxlLCBmcm9tWm9vbSkge1xyXG5cdFx0dmFyIGNycyA9IHRoaXMub3B0aW9ucy5jcnM7XHJcblx0XHRmcm9tWm9vbSA9IGZyb21ab29tID09PSB1bmRlZmluZWQgPyB0aGlzLl96b29tIDogZnJvbVpvb207XHJcblx0XHR2YXIgem9vbSA9IGNycy56b29tKHNjYWxlICogY3JzLnNjYWxlKGZyb21ab29tKSk7XHJcblx0XHRyZXR1cm4gaXNOYU4oem9vbSkgPyBJbmZpbml0eSA6IHpvb207XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBwcm9qZWN0KGxhdGxuZzogTGF0TG5nLCB6b29tOiBOdW1iZXIpOiBQb2ludFxyXG5cdC8vIFByb2plY3RzIGEgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUgYExhdExuZ2AgYWNjb3JkaW5nIHRvIHRoZSBwcm9qZWN0aW9uXHJcblx0Ly8gb2YgdGhlIG1hcCdzIENSUywgdGhlbiBzY2FsZXMgaXQgYWNjb3JkaW5nIHRvIGB6b29tYCBhbmQgdGhlIENSUydzXHJcblx0Ly8gYFRyYW5zZm9ybWF0aW9uYC4gVGhlIHJlc3VsdCBpcyBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvXHJcblx0Ly8gdGhlIENSUyBvcmlnaW4uXHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZywgem9vbSkge1xyXG5cdFx0em9vbSA9IHpvb20gPT09IHVuZGVmaW5lZCA/IHRoaXMuX3pvb20gOiB6b29tO1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMubGF0TG5nVG9Qb2ludChMLmxhdExuZyhsYXRsbmcpLCB6b29tKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVucHJvamVjdChwb2ludDogUG9pbnQsIHpvb206IE51bWJlcik6IExhdExuZ1xyXG5cdC8vIEludmVyc2Ugb2YgW2Bwcm9qZWN0YF0oI21hcC1wcm9qZWN0KS5cclxuXHR1bnByb2plY3Q6IGZ1bmN0aW9uIChwb2ludCwgem9vbSkge1xyXG5cdFx0em9vbSA9IHpvb20gPT09IHVuZGVmaW5lZCA/IHRoaXMuX3pvb20gOiB6b29tO1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMucG9pbnRUb0xhdExuZyhMLnBvaW50KHBvaW50KSwgem9vbSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsYXllclBvaW50VG9MYXRMbmcocG9pbnQ6IFBvaW50KTogTGF0TG5nXHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKSxcclxuXHQvLyByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIChmb3IgdGhlIGN1cnJlbnQgem9vbSBsZXZlbCkuXHJcblx0bGF5ZXJQb2ludFRvTGF0TG5nOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHZhciBwcm9qZWN0ZWRQb2ludCA9IEwucG9pbnQocG9pbnQpLmFkZCh0aGlzLmdldFBpeGVsT3JpZ2luKCkpO1xyXG5cdFx0cmV0dXJuIHRoaXMudW5wcm9qZWN0KHByb2plY3RlZFBvaW50KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmc6IExhdExuZyk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBnZW9ncmFwaGljYWwgY29vcmRpbmF0ZSwgcmV0dXJucyB0aGUgY29ycmVzcG9uZGluZyBwaXhlbCBjb29yZGluYXRlXHJcblx0Ly8gcmVsYXRpdmUgdG8gdGhlIFtvcmlnaW4gcGl4ZWxdKCNtYXAtZ2V0cGl4ZWxvcmlnaW4pLlxyXG5cdGxhdExuZ1RvTGF5ZXJQb2ludDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIHByb2plY3RlZFBvaW50ID0gdGhpcy5wcm9qZWN0KEwubGF0TG5nKGxhdGxuZykpLl9yb3VuZCgpO1xyXG5cdFx0cmV0dXJuIHByb2plY3RlZFBvaW50Ll9zdWJ0cmFjdCh0aGlzLmdldFBpeGVsT3JpZ2luKCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgd3JhcExhdExuZyhsYXRsbmc6IExhdExuZyk6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgYSBgTGF0TG5nYCB3aGVyZSBgbGF0YCBhbmQgYGxuZ2AgaGFzIGJlZW4gd3JhcHBlZCBhY2NvcmRpbmcgdG8gdGhlXHJcblx0Ly8gbWFwJ3MgQ1JTJ3MgYHdyYXBMYXRgIGFuZCBgd3JhcExuZ2AgcHJvcGVydGllcywgaWYgdGhleSBhcmUgb3V0c2lkZSB0aGVcclxuXHQvLyBDUlMncyBib3VuZHMuXHJcblx0Ly8gQnkgZGVmYXVsdCB0aGlzIG1lYW5zIGxvbmdpdHVkZSBpcyB3cmFwcGVkIGFyb3VuZCB0aGUgZGF0ZWxpbmUgc28gaXRzXHJcblx0Ly8gdmFsdWUgaXMgYmV0d2VlbiAtMTgwIGFuZCArMTgwIGRlZ3JlZXMuXHJcblx0d3JhcExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMud3JhcExhdExuZyhMLmxhdExuZyhsYXRsbmcpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHdyYXBMYXRMbmdCb3VuZHMoYm91bmRzOiBMYXRMbmdCb3VuZHMpOiBMYXRMbmdCb3VuZHNcclxuXHQvLyBSZXR1cm5zIGEgYExhdExuZ0JvdW5kc2Agd2l0aCB0aGUgc2FtZSBzaXplIGFzIHRoZSBnaXZlbiBvbmUsIGVuc3VyaW5nIHRoYXRcclxuXHQvLyBpdHMgY2VudGVyIGlzIHdpdGhpbiB0aGUgQ1JTJ3MgYm91bmRzLlxyXG5cdC8vIEJ5IGRlZmF1bHQgdGhpcyBtZWFucyB0aGUgY2VudGVyIGxvbmdpdHVkZSBpcyB3cmFwcGVkIGFyb3VuZCB0aGUgZGF0ZWxpbmUgc28gaXRzXHJcblx0Ly8gdmFsdWUgaXMgYmV0d2VlbiAtMTgwIGFuZCArMTgwIGRlZ3JlZXMsIGFuZCB0aGUgbWFqb3JpdHkgb2YgdGhlIGJvdW5kc1xyXG5cdC8vIG92ZXJsYXBzIHRoZSBDUlMncyBib3VuZHMuXHJcblx0d3JhcExhdExuZ0JvdW5kczogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5jcnMud3JhcExhdExuZ0JvdW5kcyhMLmxhdExuZ0JvdW5kcyhsYXRsbmcpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGRpc3RhbmNlKGxhdGxuZzE6IExhdExuZywgbGF0bG5nMjogTGF0TG5nKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0d28gZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGVzIGFjY29yZGluZyB0b1xyXG5cdC8vIHRoZSBtYXAncyBDUlMuIEJ5IGRlZmF1bHQgdGhpcyBtZWFzdXJlcyBkaXN0YW5jZSBpbiBtZXRlcnMuXHJcblx0ZGlzdGFuY2U6IGZ1bmN0aW9uIChsYXRsbmcxLCBsYXRsbmcyKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLmNycy5kaXN0YW5jZShMLmxhdExuZyhsYXRsbmcxKSwgTC5sYXRMbmcobGF0bG5nMikpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQocG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBHaXZlbiBhIHBpeGVsIGNvb3JkaW5hdGUgcmVsYXRpdmUgdG8gdGhlIG1hcCBjb250YWluZXIsIHJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmdcclxuXHQvLyBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKS5cclxuXHRjb250YWluZXJQb2ludFRvTGF5ZXJQb2ludDogZnVuY3Rpb24gKHBvaW50KSB7IC8vIChQb2ludClcclxuXHRcdHJldHVybiBMLnBvaW50KHBvaW50KS5zdWJ0cmFjdCh0aGlzLl9nZXRNYXBQYW5lUG9zKCkpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbGF5ZXJQb2ludFRvQ29udGFpbmVyUG9pbnQocG9pbnQ6IFBvaW50KTogUG9pbnRcclxuXHQvLyBHaXZlbiBhIHBpeGVsIGNvb3JkaW5hdGUgcmVsYXRpdmUgdG8gdGhlIFtvcmlnaW4gcGl4ZWxdKCNtYXAtZ2V0cGl4ZWxvcmlnaW4pLFxyXG5cdC8vIHJldHVybnMgdGhlIGNvcnJlc3BvbmRpbmcgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGUgbWFwIGNvbnRhaW5lci5cclxuXHRsYXllclBvaW50VG9Db250YWluZXJQb2ludDogZnVuY3Rpb24gKHBvaW50KSB7IC8vIChQb2ludClcclxuXHRcdHJldHVybiBMLnBvaW50KHBvaW50KS5hZGQodGhpcy5fZ2V0TWFwUGFuZVBvcygpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNvbnRhaW5lclBvaW50VG9MYXRMbmcocG9pbnQ6IFBvaW50KTogTGF0TG5nXHJcblx0Ly8gR2l2ZW4gYSBwaXhlbCBjb29yZGluYXRlIHJlbGF0aXZlIHRvIHRoZSBtYXAgY29udGFpbmVyLCByZXR1cm5zXHJcblx0Ly8gdGhlIGNvcnJlc3BvbmRpbmcgZ2VvZ3JhcGhpY2FsIGNvb3JkaW5hdGUgKGZvciB0aGUgY3VycmVudCB6b29tIGxldmVsKS5cclxuXHRjb250YWluZXJQb2ludFRvTGF0TG5nOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuXHRcdHZhciBsYXllclBvaW50ID0gdGhpcy5jb250YWluZXJQb2ludFRvTGF5ZXJQb2ludChMLnBvaW50KHBvaW50KSk7XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9MYXRMbmcobGF5ZXJQb2ludCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBsYXRMbmdUb0NvbnRhaW5lclBvaW50KGxhdGxuZzogTGF0TG5nKTogUG9pbnRcclxuXHQvLyBHaXZlbiBhIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlLCByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIHBpeGVsIGNvb3JkaW5hdGVcclxuXHQvLyByZWxhdGl2ZSB0byB0aGUgbWFwIGNvbnRhaW5lci5cclxuXHRsYXRMbmdUb0NvbnRhaW5lclBvaW50OiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5sYXllclBvaW50VG9Db250YWluZXJQb2ludCh0aGlzLmxhdExuZ1RvTGF5ZXJQb2ludChMLmxhdExuZyhsYXRsbmcpKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChldjogTW91c2VFdmVudCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBNb3VzZUV2ZW50IG9iamVjdCwgcmV0dXJucyB0aGUgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0byB0aGVcclxuXHQvLyBtYXAgY29udGFpbmVyIHdoZXJlIHRoZSBldmVudCB0b29rIHBsYWNlLlxyXG5cdG1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnQuZ2V0TW91c2VQb3NpdGlvbihlLCB0aGlzLl9jb250YWluZXIpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgbW91c2VFdmVudFRvTGF5ZXJQb2ludChldjogTW91c2VFdmVudCk6IFBvaW50XHJcblx0Ly8gR2l2ZW4gYSBNb3VzZUV2ZW50IG9iamVjdCwgcmV0dXJucyB0aGUgcGl4ZWwgY29vcmRpbmF0ZSByZWxhdGl2ZSB0b1xyXG5cdC8vIHRoZSBbb3JpZ2luIHBpeGVsXSgjbWFwLWdldHBpeGVsb3JpZ2luKSB3aGVyZSB0aGUgZXZlbnQgdG9vayBwbGFjZS5cclxuXHRtb3VzZUV2ZW50VG9MYXllclBvaW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuY29udGFpbmVyUG9pbnRUb0xheWVyUG9pbnQodGhpcy5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBtb3VzZUV2ZW50VG9MYXRMbmcoZXY6IE1vdXNlRXZlbnQpOiBMYXRMbmdcclxuXHQvLyBHaXZlbiBhIE1vdXNlRXZlbnQgb2JqZWN0LCByZXR1cm5zIGdlb2dyYXBoaWNhbCBjb29yZGluYXRlIHdoZXJlIHRoZVxyXG5cdC8vIGV2ZW50IHRvb2sgcGxhY2UuXHJcblx0bW91c2VFdmVudFRvTGF0TG5nOiBmdW5jdGlvbiAoZSkgeyAvLyAoTW91c2VFdmVudClcclxuXHRcdHJldHVybiB0aGlzLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLm1vdXNlRXZlbnRUb0xheWVyUG9pbnQoZSkpO1xyXG5cdH0sXHJcblxyXG5cclxuXHQvLyBtYXAgaW5pdGlhbGl6YXRpb24gbWV0aG9kc1xyXG5cclxuXHRfaW5pdENvbnRhaW5lcjogZnVuY3Rpb24gKGlkKSB7XHJcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmdldChpZCk7XHJcblxyXG5cdFx0aWYgKCFjb250YWluZXIpIHtcclxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdNYXAgY29udGFpbmVyIG5vdCBmb3VuZC4nKTtcclxuXHRcdH0gZWxzZSBpZiAoY29udGFpbmVyLl9sZWFmbGV0X2lkKSB7XHJcblx0XHRcdHRocm93IG5ldyBFcnJvcignTWFwIGNvbnRhaW5lciBpcyBhbHJlYWR5IGluaXRpYWxpemVkLicpO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tRXZlbnQuYWRkTGlzdGVuZXIoY29udGFpbmVyLCAnc2Nyb2xsJywgdGhpcy5fb25TY3JvbGwsIHRoaXMpO1xyXG5cdFx0dGhpcy5fY29udGFpbmVySWQgPSBMLlV0aWwuc3RhbXAoY29udGFpbmVyKTtcclxuXHR9LFxyXG5cclxuXHRfaW5pdExheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lcjtcclxuXHJcblx0XHR0aGlzLl9mYWRlQW5pbWF0ZWQgPSB0aGlzLm9wdGlvbnMuZmFkZUFuaW1hdGlvbiAmJiBMLkJyb3dzZXIuYW55M2Q7XHJcblxyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtY29udGFpbmVyJyArXHJcblx0XHRcdChMLkJyb3dzZXIudG91Y2ggPyAnIGxlYWZsZXQtdG91Y2gnIDogJycpICtcclxuXHRcdFx0KEwuQnJvd3Nlci5yZXRpbmEgPyAnIGxlYWZsZXQtcmV0aW5hJyA6ICcnKSArXHJcblx0XHRcdChMLkJyb3dzZXIuaWVsdDkgPyAnIGxlYWZsZXQtb2xkaWUnIDogJycpICtcclxuXHRcdFx0KEwuQnJvd3Nlci5zYWZhcmkgPyAnIGxlYWZsZXQtc2FmYXJpJyA6ICcnKSArXHJcblx0XHRcdCh0aGlzLl9mYWRlQW5pbWF0ZWQgPyAnIGxlYWZsZXQtZmFkZS1hbmltJyA6ICcnKSk7XHJcblxyXG5cdFx0dmFyIHBvc2l0aW9uID0gTC5Eb21VdGlsLmdldFN0eWxlKGNvbnRhaW5lciwgJ3Bvc2l0aW9uJyk7XHJcblxyXG5cdFx0aWYgKHBvc2l0aW9uICE9PSAnYWJzb2x1dGUnICYmIHBvc2l0aW9uICE9PSAncmVsYXRpdmUnICYmIHBvc2l0aW9uICE9PSAnZml4ZWQnKSB7XHJcblx0XHRcdGNvbnRhaW5lci5zdHlsZS5wb3NpdGlvbiA9ICdyZWxhdGl2ZSc7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faW5pdFBhbmVzKCk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2luaXRDb250cm9sUG9zKSB7XHJcblx0XHRcdHRoaXMuX2luaXRDb250cm9sUG9zKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2luaXRQYW5lczogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBhbmVzID0gdGhpcy5fcGFuZXMgPSB7fTtcclxuXHRcdHRoaXMuX3BhbmVSZW5kZXJlcnMgPSB7fTtcclxuXHJcblx0XHQvLyBAc2VjdGlvblxyXG5cdFx0Ly9cclxuXHRcdC8vIFBhbmVzIGFyZSBET00gZWxlbWVudHMgdXNlZCB0byBjb250cm9sIHRoZSBvcmRlcmluZyBvZiBsYXllcnMgb24gdGhlIG1hcC4gWW91XHJcblx0XHQvLyBjYW4gYWNjZXNzIHBhbmVzIHdpdGggW2BtYXAuZ2V0UGFuZWBdKCNtYXAtZ2V0cGFuZSkgb3JcclxuXHRcdC8vIFtgbWFwLmdldFBhbmVzYF0oI21hcC1nZXRwYW5lcykgbWV0aG9kcy4gTmV3IHBhbmVzIGNhbiBiZSBjcmVhdGVkIHdpdGggdGhlXHJcblx0XHQvLyBbYG1hcC5jcmVhdGVQYW5lYF0oI21hcC1jcmVhdGVwYW5lKSBtZXRob2QuXHJcblx0XHQvL1xyXG5cdFx0Ly8gRXZlcnkgbWFwIGhhcyB0aGUgZm9sbG93aW5nIGRlZmF1bHQgcGFuZXMgdGhhdCBkaWZmZXIgb25seSBpbiB6SW5kZXguXHJcblx0XHQvL1xyXG5cdFx0Ly8gQHBhbmUgbWFwUGFuZTogSFRNTEVsZW1lbnQgPSAnYXV0bydcclxuXHRcdC8vIFBhbmUgdGhhdCBjb250YWlucyBhbGwgb3RoZXIgbWFwIHBhbmVzXHJcblxyXG5cdFx0dGhpcy5fbWFwUGFuZSA9IHRoaXMuY3JlYXRlUGFuZSgnbWFwUGFuZScsIHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSwgbmV3IEwuUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdC8vIEBwYW5lIHRpbGVQYW5lOiBIVE1MRWxlbWVudCA9IDIwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYEdyaWRMYXllcmBzIGFuZCBgVGlsZUxheWVyYHNcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgndGlsZVBhbmUnKTtcclxuXHRcdC8vIEBwYW5lIG92ZXJsYXlQYW5lOiBIVE1MRWxlbWVudCA9IDQwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgdmVjdG9yIG92ZXJsYXlzIChgUGF0aGBzKSwgbGlrZSBgUG9seWxpbmVgcyBhbmQgYFBvbHlnb25gc1xyXG5cdFx0dGhpcy5jcmVhdGVQYW5lKCdzaGFkb3dQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSBzaGFkb3dQYW5lOiBIVE1MRWxlbWVudCA9IDUwMFxyXG5cdFx0Ly8gUGFuZSBmb3Igb3ZlcmxheSBzaGFkb3dzIChlLmcuIGBNYXJrZXJgIHNoYWRvd3MpXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ292ZXJsYXlQYW5lJyk7XHJcblx0XHQvLyBAcGFuZSBtYXJrZXJQYW5lOiBIVE1MRWxlbWVudCA9IDYwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYEljb25gcyBvZiBgTWFya2VyYHNcclxuXHRcdHRoaXMuY3JlYXRlUGFuZSgnbWFya2VyUGFuZScpO1xyXG5cdFx0Ly8gQHBhbmUgdG9vbHRpcFBhbmU6IEhUTUxFbGVtZW50ID0gNjUwXHJcblx0XHQvLyBQYW5lIGZvciB0b29sdGlwLlxyXG5cdFx0dGhpcy5jcmVhdGVQYW5lKCd0b29sdGlwUGFuZScpO1xyXG5cdFx0Ly8gQHBhbmUgcG9wdXBQYW5lOiBIVE1MRWxlbWVudCA9IDcwMFxyXG5cdFx0Ly8gUGFuZSBmb3IgYFBvcHVwYHMuXHJcblx0XHR0aGlzLmNyZWF0ZVBhbmUoJ3BvcHVwUGFuZScpO1xyXG5cclxuXHRcdGlmICghdGhpcy5vcHRpb25zLm1hcmtlclpvb21BbmltYXRpb24pIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHBhbmVzLm1hcmtlclBhbmUsICdsZWFmbGV0LXpvb20taGlkZScpO1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MocGFuZXMuc2hhZG93UGFuZSwgJ2xlYWZsZXQtem9vbS1oaWRlJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIHByaXZhdGUgbWV0aG9kcyB0aGF0IG1vZGlmeSBtYXAgc3RhdGVcclxuXHJcblx0Ly8gQHNlY3Rpb24gTWFwIHN0YXRlIGNoYW5nZSBldmVudHNcclxuXHRfcmVzZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSwgbmV3IEwuUG9pbnQoMCwgMCkpO1xyXG5cclxuXHRcdHZhciBsb2FkaW5nID0gIXRoaXMuX2xvYWRlZDtcclxuXHRcdHRoaXMuX2xvYWRlZCA9IHRydWU7XHJcblx0XHR6b29tID0gdGhpcy5fbGltaXRab29tKHpvb20pO1xyXG5cclxuXHRcdHRoaXMuZmlyZSgndmlld3ByZXJlc2V0Jyk7XHJcblxyXG5cdFx0dmFyIHpvb21DaGFuZ2VkID0gdGhpcy5fem9vbSAhPT0gem9vbTtcclxuXHRcdHRoaXNcclxuXHRcdFx0Ll9tb3ZlU3RhcnQoem9vbUNoYW5nZWQpXHJcblx0XHRcdC5fbW92ZShjZW50ZXIsIHpvb20pXHJcblx0XHRcdC5fbW92ZUVuZCh6b29tQ2hhbmdlZCk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IHZpZXdyZXNldDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcCBuZWVkcyB0byByZWRyYXcgaXRzIGNvbnRlbnQgKHRoaXMgdXN1YWxseSBoYXBwZW5zXHJcblx0XHQvLyBvbiBtYXAgem9vbSBvciBsb2FkKS4gVmVyeSB1c2VmdWwgZm9yIGNyZWF0aW5nIGN1c3RvbSBvdmVybGF5cy5cclxuXHRcdHRoaXMuZmlyZSgndmlld3Jlc2V0Jyk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGxvYWQ6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXAgaXMgaW5pdGlhbGl6ZWQgKHdoZW4gaXRzIGNlbnRlciBhbmQgem9vbSBhcmUgc2V0XHJcblx0XHQvLyBmb3IgdGhlIGZpcnN0IHRpbWUpLlxyXG5cdFx0aWYgKGxvYWRpbmcpIHtcclxuXHRcdFx0dGhpcy5maXJlKCdsb2FkJyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X21vdmVTdGFydDogZnVuY3Rpb24gKHpvb21DaGFuZ2VkKSB7XHJcblx0XHQvLyBAZXZlbnQgem9vbXN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIHpvb20gaXMgYWJvdXQgdG8gY2hhbmdlIChlLmcuIGJlZm9yZSB6b29tIGFuaW1hdGlvbikuXHJcblx0XHQvLyBAZXZlbnQgbW92ZXN0YXJ0OiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdmlldyBvZiB0aGUgbWFwIHN0YXJ0cyBjaGFuZ2luZyAoZS5nLiB1c2VyIHN0YXJ0cyBkcmFnZ2luZyB0aGUgbWFwKS5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb21zdGFydCcpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZXN0YXJ0Jyk7XHJcblx0fSxcclxuXHJcblx0X21vdmU6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIGRhdGEpIHtcclxuXHRcdGlmICh6b29tID09PSB1bmRlZmluZWQpIHtcclxuXHRcdFx0em9vbSA9IHRoaXMuX3pvb207XHJcblx0XHR9XHJcblx0XHR2YXIgem9vbUNoYW5nZWQgPSB0aGlzLl96b29tICE9PSB6b29tO1xyXG5cclxuXHRcdHRoaXMuX3pvb20gPSB6b29tO1xyXG5cdFx0dGhpcy5fbGFzdENlbnRlciA9IGNlbnRlcjtcclxuXHRcdHRoaXMuX3BpeGVsT3JpZ2luID0gdGhpcy5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyKTtcclxuXHJcblx0XHQvLyBAZXZlbnQgem9vbTogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHJlcGVhdGVkbHkgZHVyaW5nIGFueSBjaGFuZ2UgaW4gem9vbSBsZXZlbCwgaW5jbHVkaW5nIHpvb21cclxuXHRcdC8vIGFuZCBmbHkgYW5pbWF0aW9ucy5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCB8fCAoZGF0YSAmJiBkYXRhLnBpbmNoKSkge1x0Ly8gQWx3YXlzIGZpcmUgJ3pvb20nIGlmIHBpbmNoaW5nIGJlY2F1c2UgIzM1MzBcclxuXHRcdFx0dGhpcy5maXJlKCd6b29tJywgZGF0YSk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQGV2ZW50IG1vdmU6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCByZXBlYXRlZGx5IGR1cmluZyBhbnkgbW92ZW1lbnQgb2YgdGhlIG1hcCwgaW5jbHVkaW5nIHBhbiBhbmRcclxuXHRcdC8vIGZseSBhbmltYXRpb25zLlxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZScsIGRhdGEpO1xyXG5cdH0sXHJcblxyXG5cdF9tb3ZlRW5kOiBmdW5jdGlvbiAoem9vbUNoYW5nZWQpIHtcclxuXHRcdC8vIEBldmVudCB6b29tZW5kOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbWFwIGhhcyBjaGFuZ2VkLCBhZnRlciBhbnkgYW5pbWF0aW9ucy5cclxuXHRcdGlmICh6b29tQ2hhbmdlZCkge1xyXG5cdFx0XHR0aGlzLmZpcmUoJ3pvb21lbmQnKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgbW92ZWVuZDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIGNlbnRlciBvZiB0aGUgbWFwIHN0b3BzIGNoYW5naW5nIChlLmcuIHVzZXIgc3RvcHBlZFxyXG5cdFx0Ly8gZHJhZ2dpbmcgdGhlIG1hcCkuXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0fSxcclxuXHJcblx0X3N0b3A6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fZmx5VG9GcmFtZSk7XHJcblx0XHRpZiAodGhpcy5fcGFuQW5pbSkge1xyXG5cdFx0XHR0aGlzLl9wYW5BbmltLnN0b3AoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9yYXdQYW5CeTogZnVuY3Rpb24gKG9mZnNldCkge1xyXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX21hcFBhbmUsIHRoaXMuX2dldE1hcFBhbmVQb3MoKS5zdWJ0cmFjdChvZmZzZXQpKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0Wm9vbVNwYW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmdldE1heFpvb20oKSAtIHRoaXMuZ2V0TWluWm9vbSgpO1xyXG5cdH0sXHJcblxyXG5cdF9wYW5JbnNpZGVNYXhCb3VuZHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fZW5mb3JjaW5nQm91bmRzKSB7XHJcblx0XHRcdHRoaXMucGFuSW5zaWRlQm91bmRzKHRoaXMub3B0aW9ucy5tYXhCb3VuZHMpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9jaGVja0lmTG9hZGVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2xvYWRlZCkge1xyXG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ1NldCBtYXAgY2VudGVyIGFuZCB6b29tIGZpcnN0LicpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIERPTSBldmVudCBoYW5kbGluZ1xyXG5cclxuXHQvLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBldmVudHNcclxuXHRfaW5pdEV2ZW50czogZnVuY3Rpb24gKHJlbW92ZSkge1xyXG5cdFx0aWYgKCFMLkRvbUV2ZW50KSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHRoaXMuX3RhcmdldHMgPSB7fTtcclxuXHRcdHRoaXMuX3RhcmdldHNbTC5zdGFtcCh0aGlzLl9jb250YWluZXIpXSA9IHRoaXM7XHJcblxyXG5cdFx0dmFyIG9uT2ZmID0gcmVtb3ZlID8gJ29mZicgOiAnb24nO1xyXG5cclxuXHRcdC8vIEBldmVudCBjbGljazogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciBjbGlja3MgKG9yIHRhcHMpIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgZGJsY2xpY2s6IE1vdXNlRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgZG91YmxlLWNsaWNrcyAob3IgZG91YmxlLXRhcHMpIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2Vkb3duOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHB1c2hlcyB0aGUgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2V1cDogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgdXNlciByZWxlYXNlcyB0aGUgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2VvdmVyOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtb3VzZSBlbnRlcnMgdGhlIG1hcC5cclxuXHRcdC8vIEBldmVudCBtb3VzZW91dDogTW91c2VFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbW91c2UgbGVhdmVzIHRoZSBtYXAuXHJcblx0XHQvLyBAZXZlbnQgbW91c2Vtb3ZlOiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGlsZSB0aGUgbW91c2UgbW92ZXMgb3ZlciB0aGUgbWFwLlxyXG5cdFx0Ly8gQGV2ZW50IGNvbnRleHRtZW51OiBNb3VzZUV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHB1c2hlcyB0aGUgcmlnaHQgbW91c2UgYnV0dG9uIG9uIHRoZSBtYXAsIHByZXZlbnRzXHJcblx0XHQvLyBkZWZhdWx0IGJyb3dzZXIgY29udGV4dCBtZW51IGZyb20gc2hvd2luZyBpZiB0aGVyZSBhcmUgbGlzdGVuZXJzIG9uXHJcblx0XHQvLyB0aGlzIGV2ZW50LiBBbHNvIGZpcmVkIG9uIG1vYmlsZSB3aGVuIHRoZSB1c2VyIGhvbGRzIGEgc2luZ2xlIHRvdWNoXHJcblx0XHQvLyBmb3IgYSBzZWNvbmQgKGFsc28gY2FsbGVkIGxvbmcgcHJlc3MpLlxyXG5cdFx0Ly8gQGV2ZW50IGtleXByZXNzOiBLZXlib2FyZEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHByZXNzZXMgYSBrZXkgZnJvbSB0aGUga2V5Ym9hcmQgd2hpbGUgdGhlIG1hcCBpcyBmb2N1c2VkLlxyXG5cdFx0TC5Eb21FdmVudFtvbk9mZl0odGhpcy5fY29udGFpbmVyLCAnY2xpY2sgZGJsY2xpY2sgbW91c2Vkb3duIG1vdXNldXAgJyArXHJcblx0XHRcdCdtb3VzZW92ZXIgbW91c2VvdXQgbW91c2Vtb3ZlIGNvbnRleHRtZW51IGtleXByZXNzJywgdGhpcy5faGFuZGxlRE9NRXZlbnQsIHRoaXMpO1xyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMudHJhY2tSZXNpemUpIHtcclxuXHRcdFx0TC5Eb21FdmVudFtvbk9mZl0od2luZG93LCAncmVzaXplJywgdGhpcy5fb25SZXNpemUsIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QgJiYgdGhpcy5vcHRpb25zLnRyYW5zZm9ybTNETGltaXQpIHtcclxuXHRcdFx0dGhpc1tvbk9mZl0oJ21vdmVlbmQnLCB0aGlzLl9vbk1vdmVFbmQpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9vblJlc2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9yZXNpemVSZXF1ZXN0KTtcclxuXHRcdHRoaXMuX3Jlc2l6ZVJlcXVlc3QgPSBMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShcclxuXHRcdCAgICAgICAgZnVuY3Rpb24gKCkgeyB0aGlzLmludmFsaWRhdGVTaXplKHtkZWJvdW5jZU1vdmVlbmQ6IHRydWV9KTsgfSwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0X29uU2Nyb2xsOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9jb250YWluZXIuc2Nyb2xsVG9wICA9IDA7XHJcblx0XHR0aGlzLl9jb250YWluZXIuc2Nyb2xsTGVmdCA9IDA7XHJcblx0fSxcclxuXHJcblx0X29uTW92ZUVuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX2dldE1hcFBhbmVQb3MoKTtcclxuXHRcdGlmIChNYXRoLm1heChNYXRoLmFicyhwb3MueCksIE1hdGguYWJzKHBvcy55KSkgPj0gdGhpcy5vcHRpb25zLnRyYW5zZm9ybTNETGltaXQpIHtcclxuXHRcdFx0Ly8gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTIwMzg3MyBidXQgV2Via2l0IGFsc28gaGF2ZVxyXG5cdFx0XHQvLyBhIHBpeGVsIG9mZnNldCBvbiB2ZXJ5IGhpZ2ggdmFsdWVzLCBzZWU6IGh0dHA6Ly9qc2ZpZGRsZS5uZXQvZGc2cjVoaGIvXHJcblx0XHRcdHRoaXMuX3Jlc2V0Vmlldyh0aGlzLmdldENlbnRlcigpLCB0aGlzLmdldFpvb20oKSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2ZpbmRFdmVudFRhcmdldHM6IGZ1bmN0aW9uIChlLCB0eXBlKSB7XHJcblx0XHR2YXIgdGFyZ2V0cyA9IFtdLFxyXG5cdFx0ICAgIHRhcmdldCxcclxuXHRcdCAgICBpc0hvdmVyID0gdHlwZSA9PT0gJ21vdXNlb3V0JyB8fCB0eXBlID09PSAnbW91c2VvdmVyJyxcclxuXHRcdCAgICBzcmMgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQsXHJcblx0XHQgICAgZHJhZ2dpbmcgPSBmYWxzZTtcclxuXHJcblx0XHR3aGlsZSAoc3JjKSB7XHJcblx0XHRcdHRhcmdldCA9IHRoaXMuX3RhcmdldHNbTC5zdGFtcChzcmMpXTtcclxuXHRcdFx0aWYgKHRhcmdldCAmJiAodHlwZSA9PT0gJ2NsaWNrJyB8fCB0eXBlID09PSAncHJlY2xpY2snKSAmJiAhZS5fc2ltdWxhdGVkICYmIHRoaXMuX2RyYWdnYWJsZU1vdmVkKHRhcmdldCkpIHtcclxuXHRcdFx0XHQvLyBQcmV2ZW50IGZpcmluZyBjbGljayBhZnRlciB5b3UganVzdCBkcmFnZ2VkIGFuIG9iamVjdC5cclxuXHRcdFx0XHRkcmFnZ2luZyA9IHRydWU7XHJcblx0XHRcdFx0YnJlYWs7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKHRhcmdldCAmJiB0YXJnZXQubGlzdGVucyh0eXBlLCB0cnVlKSkge1xyXG5cdFx0XHRcdGlmIChpc0hvdmVyICYmICFMLkRvbUV2ZW50Ll9pc0V4dGVybmFsVGFyZ2V0KHNyYywgZSkpIHsgYnJlYWs7IH1cclxuXHRcdFx0XHR0YXJnZXRzLnB1c2godGFyZ2V0KTtcclxuXHRcdFx0XHRpZiAoaXNIb3ZlcikgeyBicmVhazsgfVxyXG5cdFx0XHR9XHJcblx0XHRcdGlmIChzcmMgPT09IHRoaXMuX2NvbnRhaW5lcikgeyBicmVhazsgfVxyXG5cdFx0XHRzcmMgPSBzcmMucGFyZW50Tm9kZTtcclxuXHRcdH1cclxuXHRcdGlmICghdGFyZ2V0cy5sZW5ndGggJiYgIWRyYWdnaW5nICYmICFpc0hvdmVyICYmIEwuRG9tRXZlbnQuX2lzRXh0ZXJuYWxUYXJnZXQoc3JjLCBlKSkge1xyXG5cdFx0XHR0YXJnZXRzID0gW3RoaXNdO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRhcmdldHM7XHJcblx0fSxcclxuXHJcblx0X2hhbmRsZURPTUV2ZW50OiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKCF0aGlzLl9sb2FkZWQgfHwgTC5Eb21FdmVudC5fc2tpcHBlZChlKSkgeyByZXR1cm47IH1cclxuXHJcblx0XHR2YXIgdHlwZSA9IGUudHlwZSA9PT0gJ2tleXByZXNzJyAmJiBlLmtleUNvZGUgPT09IDEzID8gJ2NsaWNrJyA6IGUudHlwZTtcclxuXHJcblx0XHRpZiAodHlwZSA9PT0gJ21vdXNlZG93bicpIHtcclxuXHRcdFx0Ly8gcHJldmVudHMgb3V0bGluZSB3aGVuIGNsaWNraW5nIG9uIGtleWJvYXJkLWZvY3VzYWJsZSBlbGVtZW50XHJcblx0XHRcdEwuRG9tVXRpbC5wcmV2ZW50T3V0bGluZShlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2ZpcmVET01FdmVudChlLCB0eXBlKTtcclxuXHR9LFxyXG5cclxuXHRfZmlyZURPTUV2ZW50OiBmdW5jdGlvbiAoZSwgdHlwZSwgdGFyZ2V0cykge1xyXG5cclxuXHRcdGlmIChlLnR5cGUgPT09ICdjbGljaycpIHtcclxuXHRcdFx0Ly8gRmlyZSBhIHN5bnRoZXRpYyAncHJlY2xpY2snIGV2ZW50IHdoaWNoIHByb3BhZ2F0ZXMgdXAgKG1haW5seSBmb3IgY2xvc2luZyBwb3B1cHMpLlxyXG5cdFx0XHQvLyBAZXZlbnQgcHJlY2xpY2s6IE1vdXNlRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgYmVmb3JlIG1vdXNlIGNsaWNrIG9uIHRoZSBtYXAgKHNvbWV0aW1lcyB1c2VmdWwgd2hlbiB5b3VcclxuXHRcdFx0Ly8gd2FudCBzb21ldGhpbmcgdG8gaGFwcGVuIG9uIGNsaWNrIGJlZm9yZSBhbnkgZXhpc3RpbmcgY2xpY2tcclxuXHRcdFx0Ly8gaGFuZGxlcnMgc3RhcnQgcnVubmluZykuXHJcblx0XHRcdHZhciBzeW50aCA9IEwuVXRpbC5leHRlbmQoe30sIGUpO1xyXG5cdFx0XHRzeW50aC50eXBlID0gJ3ByZWNsaWNrJztcclxuXHRcdFx0dGhpcy5fZmlyZURPTUV2ZW50KHN5bnRoLCBzeW50aC50eXBlLCB0YXJnZXRzKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoZS5fc3RvcHBlZCkgeyByZXR1cm47IH1cclxuXHJcblx0XHQvLyBGaW5kIHRoZSBsYXllciB0aGUgZXZlbnQgaXMgcHJvcGFnYXRpbmcgZnJvbSBhbmQgaXRzIHBhcmVudHMuXHJcblx0XHR0YXJnZXRzID0gKHRhcmdldHMgfHwgW10pLmNvbmNhdCh0aGlzLl9maW5kRXZlbnRUYXJnZXRzKGUsIHR5cGUpKTtcclxuXHJcblx0XHRpZiAoIXRhcmdldHMubGVuZ3RoKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciB0YXJnZXQgPSB0YXJnZXRzWzBdO1xyXG5cdFx0aWYgKHR5cGUgPT09ICdjb250ZXh0bWVudScgJiYgdGFyZ2V0Lmxpc3RlbnModHlwZSwgdHJ1ZSkpIHtcclxuXHRcdFx0TC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdChlKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0b3JpZ2luYWxFdmVudDogZVxyXG5cdFx0fTtcclxuXHJcblx0XHRpZiAoZS50eXBlICE9PSAna2V5cHJlc3MnKSB7XHJcblx0XHRcdHZhciBpc01hcmtlciA9IHRhcmdldCBpbnN0YW5jZW9mIEwuTWFya2VyO1xyXG5cdFx0XHRkYXRhLmNvbnRhaW5lclBvaW50ID0gaXNNYXJrZXIgP1xyXG5cdFx0XHRcdFx0dGhpcy5sYXRMbmdUb0NvbnRhaW5lclBvaW50KHRhcmdldC5nZXRMYXRMbmcoKSkgOiB0aGlzLm1vdXNlRXZlbnRUb0NvbnRhaW5lclBvaW50KGUpO1xyXG5cdFx0XHRkYXRhLmxheWVyUG9pbnQgPSB0aGlzLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KGRhdGEuY29udGFpbmVyUG9pbnQpO1xyXG5cdFx0XHRkYXRhLmxhdGxuZyA9IGlzTWFya2VyID8gdGFyZ2V0LmdldExhdExuZygpIDogdGhpcy5sYXllclBvaW50VG9MYXRMbmcoZGF0YS5sYXllclBvaW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRmb3IgKHZhciBpID0gMDsgaSA8IHRhcmdldHMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGFyZ2V0c1tpXS5maXJlKHR5cGUsIGRhdGEsIHRydWUpO1xyXG5cdFx0XHRpZiAoZGF0YS5vcmlnaW5hbEV2ZW50Ll9zdG9wcGVkIHx8XHJcblx0XHRcdFx0KHRhcmdldHNbaV0ub3B0aW9ucy5ub25CdWJibGluZ0V2ZW50cyAmJiBMLlV0aWwuaW5kZXhPZih0YXJnZXRzW2ldLm9wdGlvbnMubm9uQnViYmxpbmdFdmVudHMsIHR5cGUpICE9PSAtMSkpIHsgcmV0dXJuOyB9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2RyYWdnYWJsZU1vdmVkOiBmdW5jdGlvbiAob2JqKSB7XHJcblx0XHRvYmogPSBvYmouZHJhZ2dpbmcgJiYgb2JqLmRyYWdnaW5nLmVuYWJsZWQoKSA/IG9iaiA6IHRoaXM7XHJcblx0XHRyZXR1cm4gKG9iai5kcmFnZ2luZyAmJiBvYmouZHJhZ2dpbmcubW92ZWQoKSkgfHwgKHRoaXMuYm94Wm9vbSAmJiB0aGlzLmJveFpvb20ubW92ZWQoKSk7XHJcblx0fSxcclxuXHJcblx0X2NsZWFySGFuZGxlcnM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0aGlzLl9oYW5kbGVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHR0aGlzLl9oYW5kbGVyc1tpXS5kaXNhYmxlKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQHNlY3Rpb24gT3RoZXIgTWV0aG9kc1xyXG5cclxuXHQvLyBAbWV0aG9kIHdoZW5SZWFkeShmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gUnVucyB0aGUgZ2l2ZW4gZnVuY3Rpb24gYGZuYCB3aGVuIHRoZSBtYXAgZ2V0cyBpbml0aWFsaXplZCB3aXRoXHJcblx0Ly8gYSB2aWV3IChjZW50ZXIgYW5kIHpvb20pIGFuZCBhdCBsZWFzdCBvbmUgbGF5ZXIsIG9yIGltbWVkaWF0ZWx5XHJcblx0Ly8gaWYgaXQncyBhbHJlYWR5IGluaXRpYWxpemVkLCBvcHRpb25hbGx5IHBhc3NpbmcgYSBmdW5jdGlvbiBjb250ZXh0LlxyXG5cdHdoZW5SZWFkeTogZnVuY3Rpb24gKGNhbGxiYWNrLCBjb250ZXh0KSB7XHJcblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XHJcblx0XHRcdGNhbGxiYWNrLmNhbGwoY29udGV4dCB8fCB0aGlzLCB7dGFyZ2V0OiB0aGlzfSk7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0aGlzLm9uKCdsb2FkJywgY2FsbGJhY2ssIGNvbnRleHQpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblxyXG5cdC8vIHByaXZhdGUgbWV0aG9kcyBmb3IgZ2V0dGluZyBtYXAgc3RhdGVcclxuXHJcblx0X2dldE1hcFBhbmVQb3M6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBMLkRvbVV0aWwuZ2V0UG9zaXRpb24odGhpcy5fbWFwUGFuZSkgfHwgbmV3IEwuUG9pbnQoMCwgMCk7XHJcblx0fSxcclxuXHJcblx0X21vdmVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgcG9zID0gdGhpcy5fZ2V0TWFwUGFuZVBvcygpO1xyXG5cdFx0cmV0dXJuIHBvcyAmJiAhcG9zLmVxdWFscyhbMCwgMF0pO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRUb3BMZWZ0UG9pbnQ6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcclxuXHRcdHZhciBwaXhlbE9yaWdpbiA9IGNlbnRlciAmJiB6b29tICE9PSB1bmRlZmluZWQgP1xyXG5cdFx0XHR0aGlzLl9nZXROZXdQaXhlbE9yaWdpbihjZW50ZXIsIHpvb20pIDpcclxuXHRcdFx0dGhpcy5nZXRQaXhlbE9yaWdpbigpO1xyXG5cdFx0cmV0dXJuIHBpeGVsT3JpZ2luLnN1YnRyYWN0KHRoaXMuX2dldE1hcFBhbmVQb3MoKSk7XHJcblx0fSxcclxuXHJcblx0X2dldE5ld1BpeGVsT3JpZ2luOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tKSB7XHJcblx0XHR2YXIgdmlld0hhbGYgPSB0aGlzLmdldFNpemUoKS5fZGl2aWRlQnkoMik7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0KGNlbnRlciwgem9vbSkuX3N1YnRyYWN0KHZpZXdIYWxmKS5fYWRkKHRoaXMuX2dldE1hcFBhbmVQb3MoKSkuX3JvdW5kKCk7XHJcblx0fSxcclxuXHJcblx0X2xhdExuZ1RvTmV3TGF5ZXJQb2ludDogZnVuY3Rpb24gKGxhdGxuZywgem9vbSwgY2VudGVyKSB7XHJcblx0XHR2YXIgdG9wTGVmdCA9IHRoaXMuX2dldE5ld1BpeGVsT3JpZ2luKGNlbnRlciwgem9vbSk7XHJcblx0XHRyZXR1cm4gdGhpcy5wcm9qZWN0KGxhdGxuZywgem9vbSkuX3N1YnRyYWN0KHRvcExlZnQpO1xyXG5cdH0sXHJcblxyXG5cdF9sYXRMbmdCb3VuZHNUb05ld0xheWVyQm91bmRzOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzLCB6b29tLCBjZW50ZXIpIHtcclxuXHRcdHZhciB0b3BMZWZ0ID0gdGhpcy5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyLCB6b29tKTtcclxuXHRcdHJldHVybiBMLmJvdW5kcyhbXHJcblx0XHRcdHRoaXMucHJvamVjdChsYXRMbmdCb3VuZHMuZ2V0U291dGhXZXN0KCksIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KSxcclxuXHRcdFx0dGhpcy5wcm9qZWN0KGxhdExuZ0JvdW5kcy5nZXROb3J0aFdlc3QoKSwgem9vbSkuX3N1YnRyYWN0KHRvcExlZnQpLFxyXG5cdFx0XHR0aGlzLnByb2plY3QobGF0TG5nQm91bmRzLmdldFNvdXRoRWFzdCgpLCB6b29tKS5fc3VidHJhY3QodG9wTGVmdCksXHJcblx0XHRcdHRoaXMucHJvamVjdChsYXRMbmdCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLl9zdWJ0cmFjdCh0b3BMZWZ0KVxyXG5cdFx0XSk7XHJcblx0fSxcclxuXHJcblx0Ly8gbGF5ZXIgcG9pbnQgb2YgdGhlIGN1cnJlbnQgY2VudGVyXHJcblx0X2dldENlbnRlckxheWVyUG9pbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KHRoaXMuZ2V0U2l6ZSgpLl9kaXZpZGVCeSgyKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gb2Zmc2V0IG9mIHRoZSBzcGVjaWZpZWQgcGxhY2UgdG8gdGhlIGN1cnJlbnQgY2VudGVyIGluIHBpeGVsc1xyXG5cdF9nZXRDZW50ZXJPZmZzZXQ6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHJldHVybiB0aGlzLmxhdExuZ1RvTGF5ZXJQb2ludChsYXRsbmcpLnN1YnRyYWN0KHRoaXMuX2dldENlbnRlckxheWVyUG9pbnQoKSk7XHJcblx0fSxcclxuXHJcblx0Ly8gYWRqdXN0IGNlbnRlciBmb3IgdmlldyB0byBnZXQgaW5zaWRlIGJvdW5kc1xyXG5cdF9saW1pdENlbnRlcjogZnVuY3Rpb24gKGNlbnRlciwgem9vbSwgYm91bmRzKSB7XHJcblxyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIGNlbnRlcjsgfVxyXG5cclxuXHRcdHZhciBjZW50ZXJQb2ludCA9IHRoaXMucHJvamVjdChjZW50ZXIsIHpvb20pLFxyXG5cdFx0ICAgIHZpZXdIYWxmID0gdGhpcy5nZXRTaXplKCkuZGl2aWRlQnkoMiksXHJcblx0XHQgICAgdmlld0JvdW5kcyA9IG5ldyBMLkJvdW5kcyhjZW50ZXJQb2ludC5zdWJ0cmFjdCh2aWV3SGFsZiksIGNlbnRlclBvaW50LmFkZCh2aWV3SGFsZikpLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX2dldEJvdW5kc09mZnNldCh2aWV3Qm91bmRzLCBib3VuZHMsIHpvb20pO1xyXG5cclxuXHRcdC8vIElmIG9mZnNldCBpcyBsZXNzIHRoYW4gYSBwaXhlbCwgaWdub3JlLlxyXG5cdFx0Ly8gVGhpcyBwcmV2ZW50cyB1bnN0YWJsZSBwcm9qZWN0aW9ucyBmcm9tIGdldHRpbmcgaW50b1xyXG5cdFx0Ly8gYW4gaW5maW5pdGUgbG9vcCBvZiB0aW55IG9mZnNldHMuXHJcblx0XHRpZiAob2Zmc2V0LnJvdW5kKCkuZXF1YWxzKFswLCAwXSkpIHtcclxuXHRcdFx0cmV0dXJuIGNlbnRlcjtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcy51bnByb2plY3QoY2VudGVyUG9pbnQuYWRkKG9mZnNldCksIHpvb20pO1xyXG5cdH0sXHJcblxyXG5cdC8vIGFkanVzdCBvZmZzZXQgZm9yIHZpZXcgdG8gZ2V0IGluc2lkZSBib3VuZHNcclxuXHRfbGltaXRPZmZzZXQ6IGZ1bmN0aW9uIChvZmZzZXQsIGJvdW5kcykge1xyXG5cdFx0aWYgKCFib3VuZHMpIHsgcmV0dXJuIG9mZnNldDsgfVxyXG5cclxuXHRcdHZhciB2aWV3Qm91bmRzID0gdGhpcy5nZXRQaXhlbEJvdW5kcygpLFxyXG5cdFx0ICAgIG5ld0JvdW5kcyA9IG5ldyBMLkJvdW5kcyh2aWV3Qm91bmRzLm1pbi5hZGQob2Zmc2V0KSwgdmlld0JvdW5kcy5tYXguYWRkKG9mZnNldCkpO1xyXG5cclxuXHRcdHJldHVybiBvZmZzZXQuYWRkKHRoaXMuX2dldEJvdW5kc09mZnNldChuZXdCb3VuZHMsIGJvdW5kcykpO1xyXG5cdH0sXHJcblxyXG5cdC8vIHJldHVybnMgb2Zmc2V0IG5lZWRlZCBmb3IgcHhCb3VuZHMgdG8gZ2V0IGluc2lkZSBtYXhCb3VuZHMgYXQgYSBzcGVjaWZpZWQgem9vbVxyXG5cdF9nZXRCb3VuZHNPZmZzZXQ6IGZ1bmN0aW9uIChweEJvdW5kcywgbWF4Qm91bmRzLCB6b29tKSB7XHJcblx0XHR2YXIgcHJvamVjdGVkTWF4Qm91bmRzID0gTC5ib3VuZHMoXHJcblx0XHQgICAgICAgIHRoaXMucHJvamVjdChtYXhCb3VuZHMuZ2V0Tm9ydGhFYXN0KCksIHpvb20pLFxyXG5cdFx0ICAgICAgICB0aGlzLnByb2plY3QobWF4Qm91bmRzLmdldFNvdXRoV2VzdCgpLCB6b29tKVxyXG5cdFx0ICAgICksXHJcblx0XHQgICAgbWluT2Zmc2V0ID0gcHJvamVjdGVkTWF4Qm91bmRzLm1pbi5zdWJ0cmFjdChweEJvdW5kcy5taW4pLFxyXG5cdFx0ICAgIG1heE9mZnNldCA9IHByb2plY3RlZE1heEJvdW5kcy5tYXguc3VidHJhY3QocHhCb3VuZHMubWF4KSxcclxuXHJcblx0XHQgICAgZHggPSB0aGlzLl9yZWJvdW5kKG1pbk9mZnNldC54LCAtbWF4T2Zmc2V0LngpLFxyXG5cdFx0ICAgIGR5ID0gdGhpcy5fcmVib3VuZChtaW5PZmZzZXQueSwgLW1heE9mZnNldC55KTtcclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoZHgsIGR5KTtcclxuXHR9LFxyXG5cclxuXHRfcmVib3VuZDogZnVuY3Rpb24gKGxlZnQsIHJpZ2h0KSB7XHJcblx0XHRyZXR1cm4gbGVmdCArIHJpZ2h0ID4gMCA/XHJcblx0XHRcdE1hdGgucm91bmQobGVmdCAtIHJpZ2h0KSAvIDIgOlxyXG5cdFx0XHRNYXRoLm1heCgwLCBNYXRoLmNlaWwobGVmdCkpIC0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcihyaWdodCkpO1xyXG5cdH0sXHJcblxyXG5cdF9saW1pdFpvb206IGZ1bmN0aW9uICh6b29tKSB7XHJcblx0XHR2YXIgbWluID0gdGhpcy5nZXRNaW5ab29tKCksXHJcblx0XHQgICAgbWF4ID0gdGhpcy5nZXRNYXhab29tKCksXHJcblx0XHQgICAgc25hcCA9IEwuQnJvd3Nlci5hbnkzZCA/IHRoaXMub3B0aW9ucy56b29tU25hcCA6IDE7XHJcblx0XHRpZiAoc25hcCkge1xyXG5cdFx0XHR6b29tID0gTWF0aC5yb3VuZCh6b29tIC8gc25hcCkgKiBzbmFwO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIE1hdGgubWF4KG1pbiwgTWF0aC5taW4obWF4LCB6b29tKSk7XHJcblx0fSxcclxuXHJcblx0X29uUGFuVHJhbnNpdGlvblN0ZXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuZmlyZSgnbW92ZScpO1xyXG5cdH0sXHJcblxyXG5cdF9vblBhblRyYW5zaXRpb25FbmQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC1wYW4tYW5pbScpO1xyXG5cdFx0dGhpcy5maXJlKCdtb3ZlZW5kJyk7XHJcblx0fSxcclxuXHJcblx0X3RyeUFuaW1hdGVkUGFuOiBmdW5jdGlvbiAoY2VudGVyLCBvcHRpb25zKSB7XHJcblx0XHQvLyBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG5ldyBhbmQgY3VycmVudCBjZW50ZXJzIGluIHBpeGVsc1xyXG5cdFx0dmFyIG9mZnNldCA9IHRoaXMuX2dldENlbnRlck9mZnNldChjZW50ZXIpLl9mbG9vcigpO1xyXG5cclxuXHRcdC8vIGRvbid0IGFuaW1hdGUgdG9vIGZhciB1bmxlc3MgYW5pbWF0ZTogdHJ1ZSBzcGVjaWZpZWQgaW4gb3B0aW9uc1xyXG5cdFx0aWYgKChvcHRpb25zICYmIG9wdGlvbnMuYW5pbWF0ZSkgIT09IHRydWUgJiYgIXRoaXMuZ2V0U2l6ZSgpLmNvbnRhaW5zKG9mZnNldCkpIHsgcmV0dXJuIGZhbHNlOyB9XHJcblxyXG5cdFx0dGhpcy5wYW5CeShvZmZzZXQsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHJldHVybiB0cnVlO1xyXG5cdH0sXHJcblxyXG5cdF9jcmVhdGVBbmltUHJveHk6IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHR2YXIgcHJveHkgPSB0aGlzLl9wcm94eSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXByb3h5IGxlYWZsZXQtem9vbS1hbmltYXRlZCcpO1xyXG5cdFx0dGhpcy5fcGFuZXMubWFwUGFuZS5hcHBlbmRDaGlsZChwcm94eSk7XHJcblxyXG5cdFx0dGhpcy5vbignem9vbWFuaW0nLCBmdW5jdGlvbiAoZSkge1xyXG5cdFx0XHR2YXIgcHJvcCA9IEwuRG9tVXRpbC5UUkFOU0ZPUk0sXHJcblx0XHRcdCAgICB0cmFuc2Zvcm0gPSBwcm94eS5zdHlsZVtwcm9wXTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0ocHJveHksIHRoaXMucHJvamVjdChlLmNlbnRlciwgZS56b29tKSwgdGhpcy5nZXRab29tU2NhbGUoZS56b29tLCAxKSk7XHJcblxyXG5cdFx0XHQvLyB3b3JrYXJvdW5kIGZvciBjYXNlIHdoZW4gdHJhbnNmb3JtIGlzIHRoZSBzYW1lIGFuZCBzbyB0cmFuc2l0aW9uZW5kIGV2ZW50IGlzIG5vdCBmaXJlZFxyXG5cdFx0XHRpZiAodHJhbnNmb3JtID09PSBwcm94eS5zdHlsZVtwcm9wXSAmJiB0aGlzLl9hbmltYXRpbmdab29tKSB7XHJcblx0XHRcdFx0dGhpcy5fb25ab29tVHJhbnNpdGlvbkVuZCgpO1xyXG5cdFx0XHR9XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLm9uKCdsb2FkIG1vdmVlbmQnLCBmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHZhciBjID0gdGhpcy5nZXRDZW50ZXIoKSxcclxuXHRcdFx0ICAgIHogPSB0aGlzLmdldFpvb20oKTtcclxuXHRcdFx0TC5Eb21VdGlsLnNldFRyYW5zZm9ybShwcm94eSwgdGhpcy5wcm9qZWN0KGMsIHopLCB0aGlzLmdldFpvb21TY2FsZSh6LCAxKSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRfY2F0Y2hUcmFuc2l0aW9uRW5kOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20gJiYgZS5wcm9wZXJ0eU5hbWUuaW5kZXhPZigndHJhbnNmb3JtJykgPj0gMCkge1xyXG5cdFx0XHR0aGlzLl9vblpvb21UcmFuc2l0aW9uRW5kKCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X25vdGhpbmdUb0FuaW1hdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhdGhpcy5fY29udGFpbmVyLmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcpLmxlbmd0aDtcclxuXHR9LFxyXG5cclxuXHRfdHJ5QW5pbWF0ZWRab29tOiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuIHRydWU7IH1cclxuXHJcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIGRpc2FibGVkLCBub3Qgc3VwcG9ydGVkIG9yIHpvb20gZGlmZmVyZW5jZSBpcyB0b28gbGFyZ2VcclxuXHRcdGlmICghdGhpcy5fem9vbUFuaW1hdGVkIHx8IG9wdGlvbnMuYW5pbWF0ZSA9PT0gZmFsc2UgfHwgdGhpcy5fbm90aGluZ1RvQW5pbWF0ZSgpIHx8XHJcblx0XHQgICAgICAgIE1hdGguYWJzKHpvb20gLSB0aGlzLl96b29tKSA+IHRoaXMub3B0aW9ucy56b29tQW5pbWF0aW9uVGhyZXNob2xkKSB7IHJldHVybiBmYWxzZTsgfVxyXG5cclxuXHRcdC8vIG9mZnNldCBpcyB0aGUgcGl4ZWwgY29vcmRzIG9mIHRoZSB6b29tIG9yaWdpbiByZWxhdGl2ZSB0byB0aGUgY3VycmVudCBjZW50ZXJcclxuXHRcdHZhciBzY2FsZSA9IHRoaXMuZ2V0Wm9vbVNjYWxlKHpvb20pLFxyXG5cdFx0ICAgIG9mZnNldCA9IHRoaXMuX2dldENlbnRlck9mZnNldChjZW50ZXIpLl9kaXZpZGVCeSgxIC0gMSAvIHNjYWxlKTtcclxuXHJcblx0XHQvLyBkb24ndCBhbmltYXRlIGlmIHRoZSB6b29tIG9yaWdpbiBpc24ndCB3aXRoaW4gb25lIHNjcmVlbiBmcm9tIHRoZSBjdXJyZW50IGNlbnRlciwgdW5sZXNzIGZvcmNlZFxyXG5cdFx0aWYgKG9wdGlvbnMuYW5pbWF0ZSAhPT0gdHJ1ZSAmJiAhdGhpcy5nZXRTaXplKCkuY29udGFpbnMob2Zmc2V0KSkgeyByZXR1cm4gZmFsc2U7IH1cclxuXHJcblx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXNcclxuXHRcdFx0ICAgIC5fbW92ZVN0YXJ0KHRydWUpXHJcblx0XHRcdCAgICAuX2FuaW1hdGVab29tKGNlbnRlciwgem9vbSwgdHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHRyZXR1cm4gdHJ1ZTtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20sIHN0YXJ0QW5pbSwgbm9VcGRhdGUpIHtcclxuXHRcdGlmIChzdGFydEFuaW0pIHtcclxuXHRcdFx0dGhpcy5fYW5pbWF0aW5nWm9vbSA9IHRydWU7XHJcblxyXG5cdFx0XHQvLyByZW1lbWJlciB3aGF0IGNlbnRlci96b29tIHRvIHNldCBhZnRlciBhbmltYXRpb25cclxuXHRcdFx0dGhpcy5fYW5pbWF0ZVRvQ2VudGVyID0gY2VudGVyO1xyXG5cdFx0XHR0aGlzLl9hbmltYXRlVG9ab29tID0gem9vbTtcclxuXHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXBQYW5lLCAnbGVhZmxldC16b29tLWFuaW0nKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBAZXZlbnQgem9vbWFuaW06IFpvb21BbmltRXZlbnRcclxuXHRcdC8vIEZpcmVkIG9uIGV2ZXJ5IGZyYW1lIG9mIGEgem9vbSBhbmltYXRpb25cclxuXHRcdHRoaXMuZmlyZSgnem9vbWFuaW0nLCB7XHJcblx0XHRcdGNlbnRlcjogY2VudGVyLFxyXG5cdFx0XHR6b29tOiB6b29tLFxyXG5cdFx0XHRub1VwZGF0ZTogbm9VcGRhdGVcclxuXHRcdH0pO1xyXG5cclxuXHRcdC8vIFdvcmsgYXJvdW5kIHdlYmtpdCBub3QgZmlyaW5nICd0cmFuc2l0aW9uZW5kJywgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzM2ODksIDI2OTNcclxuXHRcdHNldFRpbWVvdXQoTC5iaW5kKHRoaXMuX29uWm9vbVRyYW5zaXRpb25FbmQsIHRoaXMpLCAyNTApO1xyXG5cdH0sXHJcblxyXG5cdF9vblpvb21UcmFuc2l0aW9uRW5kOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAoIXRoaXMuX2FuaW1hdGluZ1pvb20pIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcFBhbmUsICdsZWFmbGV0LXpvb20tYW5pbScpO1xyXG5cclxuXHRcdHRoaXMuX2FuaW1hdGluZ1pvb20gPSBmYWxzZTtcclxuXHJcblx0XHR0aGlzLl9tb3ZlKHRoaXMuX2FuaW1hdGVUb0NlbnRlciwgdGhpcy5fYW5pbWF0ZVRvWm9vbSk7XHJcblxyXG5cdFx0Ly8gVGhpcyBhbmltIGZyYW1lIHNob3VsZCBwcmV2ZW50IGFuIG9ic2N1cmUgaU9TIHdlYmtpdCB0aWxlIGxvYWRpbmcgcmFjZSBjb25kaXRpb24uXHJcblx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XHJcblx0XHRcdHRoaXMuX21vdmVFbmQodHJ1ZSk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQHNlY3Rpb25cclxuXHJcbi8vIEBmYWN0b3J5IEwubWFwKGlkOiBTdHJpbmcsIG9wdGlvbnM/OiBNYXAgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGEgbWFwIG9iamVjdCBnaXZlbiB0aGUgRE9NIElEIG9mIGEgYDxkaXY+YCBlbGVtZW50XHJcbi8vIGFuZCBvcHRpb25hbGx5IGFuIG9iamVjdCBsaXRlcmFsIHdpdGggYE1hcCBvcHRpb25zYC5cclxuLy9cclxuLy8gQGFsdGVybmF0aXZlXHJcbi8vIEBmYWN0b3J5IEwubWFwKGVsOiBIVE1MRWxlbWVudCwgb3B0aW9ucz86IE1hcCBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYSBtYXAgb2JqZWN0IGdpdmVuIGFuIGluc3RhbmNlIG9mIGEgYDxkaXY+YCBIVE1MIGVsZW1lbnRcclxuLy8gYW5kIG9wdGlvbmFsbHkgYW4gb2JqZWN0IGxpdGVyYWwgd2l0aCBgTWFwIG9wdGlvbnNgLlxyXG5MLm1hcCA9IGZ1bmN0aW9uIChpZCwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5NYXAoaWQsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG5cbi8qXG4gKiBAY2xhc3MgTGF5ZXJcbiAqIEBpbmhlcml0cyBFdmVudGVkXG4gKiBAYWthIEwuTGF5ZXJcbiAqIEBha2EgSUxheWVyXG4gKlxuICogQSBzZXQgb2YgbWV0aG9kcyBmcm9tIHRoZSBMYXllciBiYXNlIGNsYXNzIHRoYXQgYWxsIExlYWZsZXQgbGF5ZXJzIHVzZS5cbiAqIEluaGVyaXRzIGFsbCBtZXRob2RzLCBvcHRpb25zIGFuZCBldmVudHMgZnJvbSBgTC5FdmVudGVkYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbGF5ZXIgPSBMLk1hcmtlcihsYXRsbmcpLmFkZFRvKG1hcCk7XG4gKiBsYXllci5hZGRUbyhtYXApO1xuICogbGF5ZXIucmVtb3ZlKCk7XG4gKiBgYGBcbiAqXG4gKiBAZXZlbnQgYWRkOiBFdmVudFxuICogRmlyZWQgYWZ0ZXIgdGhlIGxheWVyIGlzIGFkZGVkIHRvIGEgbWFwXG4gKlxuICogQGV2ZW50IHJlbW92ZTogRXZlbnRcbiAqIEZpcmVkIGFmdGVyIHRoZSBsYXllciBpcyByZW1vdmVkIGZyb20gYSBtYXBcbiAqL1xuXG5cbkwuTGF5ZXIgPSBMLkV2ZW50ZWQuZXh0ZW5kKHtcblxuXHQvLyBDbGFzc2VzIGV4dGVuZGluZyBgTC5MYXllcmAgd2lsbCBpbmhlcml0IHRoZSBmb2xsb3dpbmcgb3B0aW9uczpcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gcGFuZTogU3RyaW5nID0gJ292ZXJsYXlQYW5lJ1xuXHRcdC8vIEJ5IGRlZmF1bHQgdGhlIGxheWVyIHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1hcCdzIFtvdmVybGF5IHBhbmVdKCNtYXAtb3ZlcmxheXBhbmUpLiBPdmVycmlkaW5nIHRoaXMgb3B0aW9uIHdpbGwgY2F1c2UgdGhlIGxheWVyIHRvIGJlIHBsYWNlZCBvbiBhbm90aGVyIHBhbmUgYnkgZGVmYXVsdC5cblx0XHRwYW5lOiAnb3ZlcmxheVBhbmUnLFxuXHRcdG5vbkJ1YmJsaW5nRXZlbnRzOiBbXSwgIC8vIEFycmF5IG9mIGV2ZW50cyB0aGF0IHNob3VsZCBub3QgYmUgYnViYmxlZCB0byBET00gcGFyZW50cyAobGlrZSB0aGUgbWFwKSxcblxuXHRcdC8vIEBvcHRpb24gYXR0cmlidXRpb246IFN0cmluZyA9IG51bGxcblx0XHQvLyBTdHJpbmcgdG8gYmUgc2hvd24gaW4gdGhlIGF0dHJpYnV0aW9uIGNvbnRyb2wsIGRlc2NyaWJlcyB0aGUgbGF5ZXIgZGF0YSwgZS5nLiBcIsKpIE1hcGJveFwiLlxuXHRcdGF0dHJpYnV0aW9uOiBudWxsXG5cdH0sXG5cblx0LyogQHNlY3Rpb25cblx0ICogQ2xhc3NlcyBleHRlbmRpbmcgYEwuTGF5ZXJgIHdpbGwgaW5oZXJpdCB0aGUgZm9sbG93aW5nIG1ldGhvZHM6XG5cdCAqXG5cdCAqIEBtZXRob2QgYWRkVG8obWFwOiBNYXApOiB0aGlzXG5cdCAqIEFkZHMgdGhlIGxheWVyIHRvIHRoZSBnaXZlbiBtYXBcblx0ICovXG5cdGFkZFRvOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0bWFwLmFkZExheWVyKHRoaXMpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVtb3ZlOiB0aGlzXG5cdC8vIFJlbW92ZXMgdGhlIGxheWVyIGZyb20gdGhlIG1hcCBpdCBpcyBjdXJyZW50bHkgYWN0aXZlIG9uLlxuXHRyZW1vdmU6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5yZW1vdmVGcm9tKHRoaXMuX21hcCB8fCB0aGlzLl9tYXBUb0FkZCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCByZW1vdmVGcm9tKG1hcDogTWFwKTogdGhpc1xuXHQvLyBSZW1vdmVzIHRoZSBsYXllciBmcm9tIHRoZSBnaXZlbiBtYXBcblx0cmVtb3ZlRnJvbTogZnVuY3Rpb24gKG9iaikge1xuXHRcdGlmIChvYmopIHtcblx0XHRcdG9iai5yZW1vdmVMYXllcih0aGlzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRQYW5lKG5hbWU/IDogU3RyaW5nKTogSFRNTEVsZW1lbnRcblx0Ly8gUmV0dXJucyB0aGUgYEhUTUxFbGVtZW50YCByZXByZXNlbnRpbmcgdGhlIG5hbWVkIHBhbmUgb24gdGhlIG1hcC4gSWYgYG5hbWVgIGlzIG9taXR0ZWQsIHJldHVybnMgdGhlIHBhbmUgZm9yIHRoaXMgbGF5ZXIuXG5cdGdldFBhbmU6IGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0cmV0dXJuIHRoaXMuX21hcC5nZXRQYW5lKG5hbWUgPyAodGhpcy5vcHRpb25zW25hbWVdIHx8IG5hbWUpIDogdGhpcy5vcHRpb25zLnBhbmUpO1xuXHR9LFxuXG5cdGFkZEludGVyYWN0aXZlVGFyZ2V0OiBmdW5jdGlvbiAodGFyZ2V0RWwpIHtcblx0XHR0aGlzLl9tYXAuX3RhcmdldHNbTC5zdGFtcCh0YXJnZXRFbCldID0gdGhpcztcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRyZW1vdmVJbnRlcmFjdGl2ZVRhcmdldDogZnVuY3Rpb24gKHRhcmdldEVsKSB7XG5cdFx0ZGVsZXRlIHRoaXMuX21hcC5fdGFyZ2V0c1tMLnN0YW1wKHRhcmdldEVsKV07XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRBdHRyaWJ1dGlvbjogU3RyaW5nXG5cdC8vIFVzZWQgYnkgdGhlIGBhdHRyaWJ1dGlvbiBjb250cm9sYCwgcmV0dXJucyB0aGUgW2F0dHJpYnV0aW9uIG9wdGlvbl0oI2dyaWRsYXllci1hdHRyaWJ1dGlvbikuXG5cdGdldEF0dHJpYnV0aW9uOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5hdHRyaWJ1dGlvbjtcblx0fSxcblxuXHRfbGF5ZXJBZGQ6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIG1hcCA9IGUudGFyZ2V0O1xuXG5cdFx0Ly8gY2hlY2sgaW4gY2FzZSBsYXllciBnZXRzIGFkZGVkIGFuZCB0aGVuIHJlbW92ZWQgYmVmb3JlIHRoZSBtYXAgaXMgcmVhZHlcblx0XHRpZiAoIW1hcC5oYXNMYXllcih0aGlzKSkgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX21hcCA9IG1hcDtcblx0XHR0aGlzLl96b29tQW5pbWF0ZWQgPSBtYXAuX3pvb21BbmltYXRlZDtcblxuXHRcdGlmICh0aGlzLmdldEV2ZW50cykge1xuXHRcdFx0dmFyIGV2ZW50cyA9IHRoaXMuZ2V0RXZlbnRzKCk7XG5cdFx0XHRtYXAub24oZXZlbnRzLCB0aGlzKTtcblx0XHRcdHRoaXMub25jZSgncmVtb3ZlJywgZnVuY3Rpb24gKCkge1xuXHRcdFx0XHRtYXAub2ZmKGV2ZW50cywgdGhpcyk7XG5cdFx0XHR9LCB0aGlzKTtcblx0XHR9XG5cblx0XHR0aGlzLm9uQWRkKG1hcCk7XG5cblx0XHRpZiAodGhpcy5nZXRBdHRyaWJ1dGlvbiAmJiBtYXAuYXR0cmlidXRpb25Db250cm9sKSB7XG5cdFx0XHRtYXAuYXR0cmlidXRpb25Db250cm9sLmFkZEF0dHJpYnV0aW9uKHRoaXMuZ2V0QXR0cmlidXRpb24oKSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5maXJlKCdhZGQnKTtcblx0XHRtYXAuZmlyZSgnbGF5ZXJhZGQnLCB7bGF5ZXI6IHRoaXN9KTtcblx0fVxufSk7XG5cbi8qIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXG4gKiBAdW5pbmhlcml0YWJsZVxuICpcbiAqIEV2ZXJ5IGxheWVyIHNob3VsZCBleHRlbmQgZnJvbSBgTC5MYXllcmAgYW5kIChyZS0paW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgbWV0aG9kcy5cbiAqXG4gKiBAbWV0aG9kIG9uQWRkKG1hcDogTWFwKTogdGhpc1xuICogU2hvdWxkIGNvbnRhaW4gY29kZSB0aGF0IGNyZWF0ZXMgRE9NIGVsZW1lbnRzIGZvciB0aGUgbGF5ZXIsIGFkZHMgdGhlbSB0byBgbWFwIHBhbmVzYCB3aGVyZSB0aGV5IHNob3VsZCBiZWxvbmcgYW5kIHB1dHMgbGlzdGVuZXJzIG9uIHJlbGV2YW50IG1hcCBldmVudHMuIENhbGxlZCBvbiBbYG1hcC5hZGRMYXllcihsYXllcilgXSgjbWFwLWFkZGxheWVyKS5cbiAqXG4gKiBAbWV0aG9kIG9uUmVtb3ZlKG1hcDogTWFwKTogdGhpc1xuICogU2hvdWxkIGNvbnRhaW4gYWxsIGNsZWFuIHVwIGNvZGUgdGhhdCByZW1vdmVzIHRoZSBsYXllcidzIGVsZW1lbnRzIGZyb20gdGhlIERPTSBhbmQgcmVtb3ZlcyBsaXN0ZW5lcnMgcHJldmlvdXNseSBhZGRlZCBpbiBbYG9uQWRkYF0oI2xheWVyLW9uYWRkKS4gQ2FsbGVkIG9uIFtgbWFwLnJlbW92ZUxheWVyKGxheWVyKWBdKCNtYXAtcmVtb3ZlbGF5ZXIpLlxuICpcbiAqIEBtZXRob2QgZ2V0RXZlbnRzKCk6IE9iamVjdFxuICogVGhpcyBvcHRpb25hbCBtZXRob2Qgc2hvdWxkIHJldHVybiBhbiBvYmplY3QgbGlrZSBgeyB2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0IH1gIGZvciBbYGFkZEV2ZW50TGlzdGVuZXJgXSgjZXZlbnRlZC1hZGRldmVudGxpc3RlbmVyKS4gVGhlIGV2ZW50IGhhbmRsZXJzIGluIHRoaXMgb2JqZWN0IHdpbGwgYmUgYXV0b21hdGljYWxseSBhZGRlZCBhbmQgcmVtb3ZlZCBmcm9tIHRoZSBtYXAgd2l0aCB5b3VyIGxheWVyLlxuICpcbiAqIEBtZXRob2QgZ2V0QXR0cmlidXRpb24oKTogU3RyaW5nXG4gKiBUaGlzIG9wdGlvbmFsIG1ldGhvZCBzaG91bGQgcmV0dXJuIGEgc3RyaW5nIGNvbnRhaW5pbmcgSFRNTCB0byBiZSBzaG93biBvbiB0aGUgYEF0dHJpYnV0aW9uIGNvbnRyb2xgIHdoZW5ldmVyIHRoZSBsYXllciBpcyB2aXNpYmxlLlxuICpcbiAqIEBtZXRob2QgYmVmb3JlQWRkKG1hcDogTWFwKTogdGhpc1xuICogT3B0aW9uYWwgbWV0aG9kLiBDYWxsZWQgb24gW2BtYXAuYWRkTGF5ZXIobGF5ZXIpYF0oI21hcC1hZGRsYXllciksIGJlZm9yZSB0aGUgbGF5ZXIgaXMgYWRkZWQgdG8gdGhlIG1hcCwgYmVmb3JlIGV2ZW50cyBhcmUgaW5pdGlhbGl6ZWQsIHdpdGhvdXQgd2FpdGluZyB1bnRpbCB0aGUgbWFwIGlzIGluIGEgdXNhYmxlIHN0YXRlLiBVc2UgZm9yIGVhcmx5IGluaXRpYWxpemF0aW9uIG9ubHkuXG4gKi9cblxuXG4vKiBAbmFtZXNwYWNlIE1hcFxuICogQHNlY3Rpb24gTGF5ZXIgZXZlbnRzXG4gKlxuICogQGV2ZW50IGxheWVyYWRkOiBMYXllckV2ZW50XG4gKiBGaXJlZCB3aGVuIGEgbmV3IGxheWVyIGlzIGFkZGVkIHRvIHRoZSBtYXAuXG4gKlxuICogQGV2ZW50IGxheWVycmVtb3ZlOiBMYXllckV2ZW50XG4gKiBGaXJlZCB3aGVuIHNvbWUgbGF5ZXIgaXMgcmVtb3ZlZCBmcm9tIHRoZSBtYXBcbiAqXG4gKiBAc2VjdGlvbiBNZXRob2RzIGZvciBMYXllcnMgYW5kIENvbnRyb2xzXG4gKi9cbkwuTWFwLmluY2x1ZGUoe1xuXHQvLyBAbWV0aG9kIGFkZExheWVyKGxheWVyOiBMYXllcik6IHRoaXNcblx0Ly8gQWRkcyB0aGUgZ2l2ZW4gbGF5ZXIgdG8gdGhlIG1hcFxuXHRhZGRMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGlkID0gTC5zdGFtcChsYXllcik7XG5cdFx0aWYgKHRoaXMuX2xheWVyc1tpZF0pIHsgcmV0dXJuIHRoaXM7IH1cblx0XHR0aGlzLl9sYXllcnNbaWRdID0gbGF5ZXI7XG5cblx0XHRsYXllci5fbWFwVG9BZGQgPSB0aGlzO1xuXG5cdFx0aWYgKGxheWVyLmJlZm9yZUFkZCkge1xuXHRcdFx0bGF5ZXIuYmVmb3JlQWRkKHRoaXMpO1xuXHRcdH1cblxuXHRcdHRoaXMud2hlblJlYWR5KGxheWVyLl9sYXllckFkZCwgbGF5ZXIpO1xuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXG5cdC8vIFJlbW92ZXMgdGhlIGdpdmVuIGxheWVyIGZyb20gdGhlIG1hcC5cblx0cmVtb3ZlTGF5ZXI6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBpZCA9IEwuc3RhbXAobGF5ZXIpO1xuXG5cdFx0aWYgKCF0aGlzLl9sYXllcnNbaWRdKSB7IHJldHVybiB0aGlzOyB9XG5cblx0XHRpZiAodGhpcy5fbG9hZGVkKSB7XG5cdFx0XHRsYXllci5vblJlbW92ZSh0aGlzKTtcblx0XHR9XG5cblx0XHRpZiAobGF5ZXIuZ2V0QXR0cmlidXRpb24gJiYgdGhpcy5hdHRyaWJ1dGlvbkNvbnRyb2wpIHtcblx0XHRcdHRoaXMuYXR0cmlidXRpb25Db250cm9sLnJlbW92ZUF0dHJpYnV0aW9uKGxheWVyLmdldEF0dHJpYnV0aW9uKCkpO1xuXHRcdH1cblxuXHRcdGRlbGV0ZSB0aGlzLl9sYXllcnNbaWRdO1xuXG5cdFx0aWYgKHRoaXMuX2xvYWRlZCkge1xuXHRcdFx0dGhpcy5maXJlKCdsYXllcnJlbW92ZScsIHtsYXllcjogbGF5ZXJ9KTtcblx0XHRcdGxheWVyLmZpcmUoJ3JlbW92ZScpO1xuXHRcdH1cblxuXHRcdGxheWVyLl9tYXAgPSBsYXllci5fbWFwVG9BZGQgPSBudWxsO1xuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBoYXNMYXllcihsYXllcjogTGF5ZXIpOiBCb29sZWFuXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBnaXZlbiBsYXllciBpcyBjdXJyZW50bHkgYWRkZWQgdG8gdGhlIG1hcFxuXHRoYXNMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0cmV0dXJuICEhbGF5ZXIgJiYgKEwuc3RhbXAobGF5ZXIpIGluIHRoaXMuX2xheWVycyk7XG5cdH0sXG5cblx0LyogQG1ldGhvZCBlYWNoTGF5ZXIoZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xuXHQgKiBJdGVyYXRlcyBvdmVyIHRoZSBsYXllcnMgb2YgdGhlIG1hcCwgb3B0aW9uYWxseSBzcGVjaWZ5aW5nIGNvbnRleHQgb2YgdGhlIGl0ZXJhdG9yIGZ1bmN0aW9uLlxuXHQgKiBgYGBcblx0ICogbWFwLmVhY2hMYXllcihmdW5jdGlvbihsYXllcil7XG5cdCAqICAgICBsYXllci5iaW5kUG9wdXAoJ0hlbGxvJyk7XG5cdCAqIH0pO1xuXHQgKiBgYGBcblx0ICovXG5cdGVhY2hMYXllcjogZnVuY3Rpb24gKG1ldGhvZCwgY29udGV4dCkge1xuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHRtZXRob2QuY2FsbChjb250ZXh0LCB0aGlzLl9sYXllcnNbaV0pO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRfYWRkTGF5ZXJzOiBmdW5jdGlvbiAobGF5ZXJzKSB7XG5cdFx0bGF5ZXJzID0gbGF5ZXJzID8gKEwuVXRpbC5pc0FycmF5KGxheWVycykgPyBsYXllcnMgOiBbbGF5ZXJzXSkgOiBbXTtcblxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsYXllcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdHRoaXMuYWRkTGF5ZXIobGF5ZXJzW2ldKTtcblx0XHR9XG5cdH0sXG5cblx0X2FkZFpvb21MaW1pdDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0aWYgKGlzTmFOKGxheWVyLm9wdGlvbnMubWF4Wm9vbSkgfHwgIWlzTmFOKGxheWVyLm9wdGlvbnMubWluWm9vbSkpIHtcblx0XHRcdHRoaXMuX3pvb21Cb3VuZExheWVyc1tMLnN0YW1wKGxheWVyKV0gPSBsYXllcjtcblx0XHRcdHRoaXMuX3VwZGF0ZVpvb21MZXZlbHMoKTtcblx0XHR9XG5cdH0sXG5cblx0X3JlbW92ZVpvb21MaW1pdDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGlkID0gTC5zdGFtcChsYXllcik7XG5cblx0XHRpZiAodGhpcy5fem9vbUJvdW5kTGF5ZXJzW2lkXSkge1xuXHRcdFx0ZGVsZXRlIHRoaXMuX3pvb21Cb3VuZExheWVyc1tpZF07XG5cdFx0XHR0aGlzLl91cGRhdGVab29tTGV2ZWxzKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVab29tTGV2ZWxzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIG1pblpvb20gPSBJbmZpbml0eSxcblx0XHQgICAgbWF4Wm9vbSA9IC1JbmZpbml0eSxcblx0XHQgICAgb2xkWm9vbVNwYW4gPSB0aGlzLl9nZXRab29tU3BhbigpO1xuXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl96b29tQm91bmRMYXllcnMpIHtcblx0XHRcdHZhciBvcHRpb25zID0gdGhpcy5fem9vbUJvdW5kTGF5ZXJzW2ldLm9wdGlvbnM7XG5cblx0XHRcdG1pblpvb20gPSBvcHRpb25zLm1pblpvb20gPT09IHVuZGVmaW5lZCA/IG1pblpvb20gOiBNYXRoLm1pbihtaW5ab29tLCBvcHRpb25zLm1pblpvb20pO1xuXHRcdFx0bWF4Wm9vbSA9IG9wdGlvbnMubWF4Wm9vbSA9PT0gdW5kZWZpbmVkID8gbWF4Wm9vbSA6IE1hdGgubWF4KG1heFpvb20sIG9wdGlvbnMubWF4Wm9vbSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fbGF5ZXJzTWF4Wm9vbSA9IG1heFpvb20gPT09IC1JbmZpbml0eSA/IHVuZGVmaW5lZCA6IG1heFpvb207XG5cdFx0dGhpcy5fbGF5ZXJzTWluWm9vbSA9IG1pblpvb20gPT09IEluZmluaXR5ID8gdW5kZWZpbmVkIDogbWluWm9vbTtcblxuXHRcdC8vIEBzZWN0aW9uIE1hcCBzdGF0ZSBjaGFuZ2UgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IHpvb21sZXZlbHNjaGFuZ2U6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgbnVtYmVyIG9mIHpvb21sZXZlbHMgb24gdGhlIG1hcCBpcyBjaGFuZ2VkIGR1ZVxuXHRcdC8vIHRvIGFkZGluZyBvciByZW1vdmluZyBhIGxheWVyLlxuXHRcdGlmIChvbGRab29tU3BhbiAhPT0gdGhpcy5fZ2V0Wm9vbVNwYW4oKSkge1xuXHRcdFx0dGhpcy5maXJlKCd6b29tbGV2ZWxzY2hhbmdlJyk7XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5tYXhab29tID09PSB1bmRlZmluZWQgJiYgdGhpcy5fbGF5ZXJzTWF4Wm9vbSAmJiB0aGlzLmdldFpvb20oKSA+IHRoaXMuX2xheWVyc01heFpvb20pIHtcblx0XHRcdHRoaXMuc2V0Wm9vbSh0aGlzLl9sYXllcnNNYXhab29tKTtcblx0XHR9XG5cdFx0aWYgKHRoaXMub3B0aW9ucy5taW5ab29tID09PSB1bmRlZmluZWQgJiYgdGhpcy5fbGF5ZXJzTWluWm9vbSAmJiB0aGlzLmdldFpvb20oKSA8IHRoaXMuX2xheWVyc01pblpvb20pIHtcblx0XHRcdHRoaXMuc2V0Wm9vbSh0aGlzLl9sYXllcnNNaW5ab29tKTtcblx0XHR9XG5cdH1cbn0pO1xuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBEb21FdmVudFxyXG4gKiBVdGlsaXR5IGZ1bmN0aW9ucyB0byB3b3JrIHdpdGggdGhlIFtET00gZXZlbnRzXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvRXZlbnQpLCB1c2VkIGJ5IExlYWZsZXQgaW50ZXJuYWxseS5cclxuICovXHJcblxyXG4vLyBJbnNwaXJlZCBieSBKb2huIFJlc2lnLCBEZWFuIEVkd2FyZHMgYW5kIFlVSSBhZGRFdmVudCBpbXBsZW1lbnRhdGlvbnMuXHJcblxyXG5cclxuXHJcbnZhciBldmVudHNLZXkgPSAnX2xlYWZsZXRfZXZlbnRzJztcclxuXHJcbkwuRG9tRXZlbnQgPSB7XHJcblxyXG5cdC8vIEBmdW5jdGlvbiBvbihlbDogSFRNTEVsZW1lbnQsIHR5cGVzOiBTdHJpbmcsIGZuOiBGdW5jdGlvbiwgY29udGV4dD86IE9iamVjdCk6IHRoaXNcclxuXHQvLyBBZGRzIGEgbGlzdGVuZXIgZnVuY3Rpb24gKGBmbmApIHRvIGEgcGFydGljdWxhciBET00gZXZlbnQgdHlwZSBvZiB0aGVcclxuXHQvLyBlbGVtZW50IGBlbGAuIFlvdSBjYW4gb3B0aW9uYWxseSBzcGVjaWZ5IHRoZSBjb250ZXh0IG9mIHRoZSBsaXN0ZW5lclxyXG5cdC8vIChvYmplY3QgdGhlIGB0aGlzYCBrZXl3b3JkIHdpbGwgcG9pbnQgdG8pLiBZb3UgY2FuIGFsc28gcGFzcyBzZXZlcmFsXHJcblx0Ly8gc3BhY2Utc2VwYXJhdGVkIHR5cGVzIChlLmcuIGAnY2xpY2sgZGJsY2xpY2snYCkuXHJcblxyXG5cdC8vIEBhbHRlcm5hdGl2ZVxyXG5cdC8vIEBmdW5jdGlvbiBvbihlbDogSFRNTEVsZW1lbnQsIGV2ZW50TWFwOiBPYmplY3QsIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gQWRkcyBhIHNldCBvZiB0eXBlL2xpc3RlbmVyIHBhaXJzLCBlLmcuIGB7Y2xpY2s6IG9uQ2xpY2ssIG1vdXNlbW92ZTogb25Nb3VzZU1vdmV9YFxyXG5cdG9uOiBmdW5jdGlvbiAob2JqLCB0eXBlcywgZm4sIGNvbnRleHQpIHtcclxuXHJcblx0XHRpZiAodHlwZW9mIHR5cGVzID09PSAnb2JqZWN0Jykge1xyXG5cdFx0XHRmb3IgKHZhciB0eXBlIGluIHR5cGVzKSB7XHJcblx0XHRcdFx0dGhpcy5fb24ob2JqLCB0eXBlLCB0eXBlc1t0eXBlXSwgZm4pO1xyXG5cdFx0XHR9XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR0eXBlcyA9IEwuVXRpbC5zcGxpdFdvcmRzKHR5cGVzKTtcclxuXHJcblx0XHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSB0eXBlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuX29uKG9iaiwgdHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBvZmYoZWw6IEhUTUxFbGVtZW50LCB0eXBlczogU3RyaW5nLCBmbjogRnVuY3Rpb24sIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyBhIHByZXZpb3VzbHkgYWRkZWQgbGlzdGVuZXIgZnVuY3Rpb24uIElmIG5vIGZ1bmN0aW9uIGlzIHNwZWNpZmllZCxcclxuXHQvLyBpdCB3aWxsIHJlbW92ZSBhbGwgdGhlIGxpc3RlbmVycyBvZiB0aGF0IHBhcnRpY3VsYXIgRE9NIGV2ZW50IGZyb20gdGhlIGVsZW1lbnQuXHJcblx0Ly8gTm90ZSB0aGF0IGlmIHlvdSBwYXNzZWQgYSBjdXN0b20gY29udGV4dCB0byBvbiwgeW91IG11c3QgcGFzcyB0aGUgc2FtZVxyXG5cdC8vIGNvbnRleHQgdG8gYG9mZmAgaW4gb3JkZXIgdG8gcmVtb3ZlIHRoZSBsaXN0ZW5lci5cclxuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQGZ1bmN0aW9uIG9mZihlbDogSFRNTEVsZW1lbnQsIGV2ZW50TWFwOiBPYmplY3QsIGNvbnRleHQ/OiBPYmplY3QpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyBhIHNldCBvZiB0eXBlL2xpc3RlbmVyIHBhaXJzLCBlLmcuIGB7Y2xpY2s6IG9uQ2xpY2ssIG1vdXNlbW92ZTogb25Nb3VzZU1vdmV9YFxyXG5cdG9mZjogZnVuY3Rpb24gKG9iaiwgdHlwZXMsIGZuLCBjb250ZXh0KSB7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiB0eXBlcyA9PT0gJ29iamVjdCcpIHtcclxuXHRcdFx0Zm9yICh2YXIgdHlwZSBpbiB0eXBlcykge1xyXG5cdFx0XHRcdHRoaXMuX29mZihvYmosIHR5cGUsIHR5cGVzW3R5cGVdLCBmbik7XHJcblx0XHRcdH1cclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHR5cGVzID0gTC5VdGlsLnNwbGl0V29yZHModHlwZXMpO1xyXG5cclxuXHRcdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IHR5cGVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdFx0dGhpcy5fb2ZmKG9iaiwgdHlwZXNbaV0sIGZuLCBjb250ZXh0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9vbjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHRcdHZhciBpZCA9IHR5cGUgKyBMLnN0YW1wKGZuKSArIChjb250ZXh0ID8gJ18nICsgTC5zdGFtcChjb250ZXh0KSA6ICcnKTtcclxuXHJcblx0XHRpZiAob2JqW2V2ZW50c0tleV0gJiYgb2JqW2V2ZW50c0tleV1baWRdKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0dmFyIGhhbmRsZXIgPSBmdW5jdGlvbiAoZSkge1xyXG5cdFx0XHRyZXR1cm4gZm4uY2FsbChjb250ZXh0IHx8IG9iaiwgZSB8fCB3aW5kb3cuZXZlbnQpO1xyXG5cdFx0fTtcclxuXHJcblx0XHR2YXIgb3JpZ2luYWxIYW5kbGVyID0gaGFuZGxlcjtcclxuXHJcblx0XHRpZiAoTC5Ccm93c2VyLnBvaW50ZXIgJiYgdHlwZS5pbmRleE9mKCd0b3VjaCcpID09PSAwKSB7XHJcblx0XHRcdHRoaXMuYWRkUG9pbnRlckxpc3RlbmVyKG9iaiwgdHlwZSwgaGFuZGxlciwgaWQpO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoTC5Ccm93c2VyLnRvdWNoICYmICh0eXBlID09PSAnZGJsY2xpY2snKSAmJiB0aGlzLmFkZERvdWJsZVRhcExpc3RlbmVyICYmXHJcblx0XHQgICAgICAgICAgICEoTC5Ccm93c2VyLnBvaW50ZXIgJiYgTC5Ccm93c2VyLmNocm9tZSkpIHtcclxuXHRcdFx0Ly8gQ2hyb21lID41NSBkb2VzIG5vdCBuZWVkIHRoZSBzeW50aGV0aWMgZGJsY2xpY2tzIGZyb20gYWRkRG91YmxlVGFwTGlzdGVuZXJcclxuXHRcdFx0Ly8gU2VlICM1MTgwXHJcblx0XHRcdHRoaXMuYWRkRG91YmxlVGFwTGlzdGVuZXIob2JqLCBoYW5kbGVyLCBpZCk7XHJcblxyXG5cdFx0fSBlbHNlIGlmICgnYWRkRXZlbnRMaXN0ZW5lcicgaW4gb2JqKSB7XHJcblxyXG5cdFx0XHRpZiAodHlwZSA9PT0gJ21vdXNld2hlZWwnKSB7XHJcblx0XHRcdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIoJ29ud2hlZWwnIGluIG9iaiA/ICd3aGVlbCcgOiAnbW91c2V3aGVlbCcsIGhhbmRsZXIsIGZhbHNlKTtcclxuXHJcblx0XHRcdH0gZWxzZSBpZiAoKHR5cGUgPT09ICdtb3VzZWVudGVyJykgfHwgKHR5cGUgPT09ICdtb3VzZWxlYXZlJykpIHtcclxuXHRcdFx0XHRoYW5kbGVyID0gZnVuY3Rpb24gKGUpIHtcclxuXHRcdFx0XHRcdGUgPSBlIHx8IHdpbmRvdy5ldmVudDtcclxuXHRcdFx0XHRcdGlmIChMLkRvbUV2ZW50Ll9pc0V4dGVybmFsVGFyZ2V0KG9iaiwgZSkpIHtcclxuXHRcdFx0XHRcdFx0b3JpZ2luYWxIYW5kbGVyKGUpO1xyXG5cdFx0XHRcdFx0fVxyXG5cdFx0XHRcdH07XHJcblx0XHRcdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodHlwZSA9PT0gJ21vdXNlZW50ZXInID8gJ21vdXNlb3ZlcicgOiAnbW91c2VvdXQnLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGlmICh0eXBlID09PSAnY2xpY2snICYmIEwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdFx0XHRoYW5kbGVyID0gZnVuY3Rpb24gKGUpIHtcclxuXHRcdFx0XHRcdFx0cmV0dXJuIEwuRG9tRXZlbnQuX2ZpbHRlckNsaWNrKGUsIG9yaWdpbmFsSGFuZGxlcik7XHJcblx0XHRcdFx0XHR9O1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcih0eXBlLCBoYW5kbGVyLCBmYWxzZSk7XHJcblx0XHRcdH1cclxuXHJcblx0XHR9IGVsc2UgaWYgKCdhdHRhY2hFdmVudCcgaW4gb2JqKSB7XHJcblx0XHRcdG9iai5hdHRhY2hFdmVudCgnb24nICsgdHlwZSwgaGFuZGxlcik7XHJcblx0XHR9XHJcblxyXG5cdFx0b2JqW2V2ZW50c0tleV0gPSBvYmpbZXZlbnRzS2V5XSB8fCB7fTtcclxuXHRcdG9ialtldmVudHNLZXldW2lkXSA9IGhhbmRsZXI7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X29mZjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgZm4sIGNvbnRleHQpIHtcclxuXHJcblx0XHR2YXIgaWQgPSB0eXBlICsgTC5zdGFtcChmbikgKyAoY29udGV4dCA/ICdfJyArIEwuc3RhbXAoY29udGV4dCkgOiAnJyksXHJcblx0XHQgICAgaGFuZGxlciA9IG9ialtldmVudHNLZXldICYmIG9ialtldmVudHNLZXldW2lkXTtcclxuXHJcblx0XHRpZiAoIWhhbmRsZXIpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRpZiAoTC5Ccm93c2VyLnBvaW50ZXIgJiYgdHlwZS5pbmRleE9mKCd0b3VjaCcpID09PSAwKSB7XHJcblx0XHRcdHRoaXMucmVtb3ZlUG9pbnRlckxpc3RlbmVyKG9iaiwgdHlwZSwgaWQpO1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoTC5Ccm93c2VyLnRvdWNoICYmICh0eXBlID09PSAnZGJsY2xpY2snKSAmJiB0aGlzLnJlbW92ZURvdWJsZVRhcExpc3RlbmVyKSB7XHJcblx0XHRcdHRoaXMucmVtb3ZlRG91YmxlVGFwTGlzdGVuZXIob2JqLCBpZCk7XHJcblxyXG5cdFx0fSBlbHNlIGlmICgncmVtb3ZlRXZlbnRMaXN0ZW5lcicgaW4gb2JqKSB7XHJcblxyXG5cdFx0XHRpZiAodHlwZSA9PT0gJ21vdXNld2hlZWwnKSB7XHJcblx0XHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ29ud2hlZWwnIGluIG9iaiA/ICd3aGVlbCcgOiAnbW91c2V3aGVlbCcsIGhhbmRsZXIsIGZhbHNlKTtcclxuXHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIoXHJcblx0XHRcdFx0XHR0eXBlID09PSAnbW91c2VlbnRlcicgPyAnbW91c2VvdmVyJyA6XHJcblx0XHRcdFx0XHR0eXBlID09PSAnbW91c2VsZWF2ZScgPyAnbW91c2VvdXQnIDogdHlwZSwgaGFuZGxlciwgZmFsc2UpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0fSBlbHNlIGlmICgnZGV0YWNoRXZlbnQnIGluIG9iaikge1xyXG5cdFx0XHRvYmouZGV0YWNoRXZlbnQoJ29uJyArIHR5cGUsIGhhbmRsZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdG9ialtldmVudHNLZXldW2lkXSA9IG51bGw7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHN0b3BQcm9wYWdhdGlvbihldjogRE9NRXZlbnQpOiB0aGlzXHJcblx0Ly8gU3RvcCB0aGUgZ2l2ZW4gZXZlbnQgZnJvbSBwcm9wYWdhdGlvbiB0byBwYXJlbnQgZWxlbWVudHMuIFVzZWQgaW5zaWRlIHRoZSBsaXN0ZW5lciBmdW5jdGlvbnM6XHJcblx0Ly8gYGBganNcclxuXHQvLyBMLkRvbUV2ZW50Lm9uKGRpdiwgJ2NsaWNrJywgZnVuY3Rpb24gKGV2KSB7XHJcblx0Ly8gXHRMLkRvbUV2ZW50LnN0b3BQcm9wYWdhdGlvbihldik7XHJcblx0Ly8gfSk7XHJcblx0Ly8gYGBgXHJcblx0c3RvcFByb3BhZ2F0aW9uOiBmdW5jdGlvbiAoZSkge1xyXG5cclxuXHRcdGlmIChlLnN0b3BQcm9wYWdhdGlvbikge1xyXG5cdFx0XHRlLnN0b3BQcm9wYWdhdGlvbigpO1xyXG5cdFx0fSBlbHNlIGlmIChlLm9yaWdpbmFsRXZlbnQpIHsgIC8vIEluIGNhc2Ugb2YgTGVhZmxldCBldmVudC5cclxuXHRcdFx0ZS5vcmlnaW5hbEV2ZW50Ll9zdG9wcGVkID0gdHJ1ZTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGUuY2FuY2VsQnViYmxlID0gdHJ1ZTtcclxuXHRcdH1cclxuXHRcdEwuRG9tRXZlbnQuX3NraXBwZWQoZSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGRpc2FibGVTY3JvbGxQcm9wYWdhdGlvbihlbDogSFRNTEVsZW1lbnQpOiB0aGlzXHJcblx0Ly8gQWRkcyBgc3RvcFByb3BhZ2F0aW9uYCB0byB0aGUgZWxlbWVudCdzIGAnbW91c2V3aGVlbCdgIGV2ZW50cyAocGx1cyBicm93c2VyIHZhcmlhbnRzKS5cclxuXHRkaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb246IGZ1bmN0aW9uIChlbCkge1xyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnQub24oZWwsICdtb3VzZXdoZWVsJywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBkaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbihlbDogSFRNTEVsZW1lbnQpOiB0aGlzXHJcblx0Ly8gQWRkcyBgc3RvcFByb3BhZ2F0aW9uYCB0byB0aGUgZWxlbWVudCdzIGAnY2xpY2snYCwgYCdkb3VibGVjbGljaydgLFxyXG5cdC8vIGAnbW91c2Vkb3duJ2AgYW5kIGAndG91Y2hzdGFydCdgIGV2ZW50cyAocGx1cyBicm93c2VyIHZhcmlhbnRzKS5cclxuXHRkaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbjogZnVuY3Rpb24gKGVsKSB7XHJcblx0XHR2YXIgc3RvcCA9IEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uO1xyXG5cclxuXHRcdEwuRG9tRXZlbnQub24oZWwsIEwuRHJhZ2dhYmxlLlNUQVJULmpvaW4oJyAnKSwgc3RvcCk7XHJcblxyXG5cdFx0cmV0dXJuIEwuRG9tRXZlbnQub24oZWwsIHtcclxuXHRcdFx0Y2xpY2s6IEwuRG9tRXZlbnQuX2Zha2VTdG9wLFxyXG5cdFx0XHRkYmxjbGljazogc3RvcFxyXG5cdFx0fSk7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHByZXZlbnREZWZhdWx0KGV2OiBET01FdmVudCk6IHRoaXNcclxuXHQvLyBQcmV2ZW50cyB0aGUgZGVmYXVsdCBhY3Rpb24gb2YgdGhlIERPTSBFdmVudCBgZXZgIGZyb20gaGFwcGVuaW5nIChzdWNoIGFzXHJcblx0Ly8gZm9sbG93aW5nIGEgbGluayBpbiB0aGUgaHJlZiBvZiB0aGUgYSBlbGVtZW50LCBvciBkb2luZyBhIFBPU1QgcmVxdWVzdFxyXG5cdC8vIHdpdGggcGFnZSByZWxvYWQgd2hlbiBhIGA8Zm9ybT5gIGlzIHN1Ym1pdHRlZCkuXHJcblx0Ly8gVXNlIGl0IGluc2lkZSBsaXN0ZW5lciBmdW5jdGlvbnMuXHJcblx0cHJldmVudERlZmF1bHQ6IGZ1bmN0aW9uIChlKSB7XHJcblxyXG5cdFx0aWYgKGUucHJldmVudERlZmF1bHQpIHtcclxuXHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZS5yZXR1cm5WYWx1ZSA9IGZhbHNlO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQGZ1bmN0aW9uIHN0b3AoZXYpOiB0aGlzXHJcblx0Ly8gRG9lcyBgc3RvcFByb3BhZ2F0aW9uYCBhbmQgYHByZXZlbnREZWZhdWx0YCBhdCB0aGUgc2FtZSB0aW1lLlxyXG5cdHN0b3A6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRyZXR1cm4gTC5Eb21FdmVudFxyXG5cdFx0XHQucHJldmVudERlZmF1bHQoZSlcclxuXHRcdFx0LnN0b3BQcm9wYWdhdGlvbihlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0TW91c2VQb3NpdGlvbihldjogRE9NRXZlbnQsIGNvbnRhaW5lcj86IEhUTUxFbGVtZW50KTogUG9pbnRcclxuXHQvLyBHZXRzIG5vcm1hbGl6ZWQgbW91c2UgcG9zaXRpb24gZnJvbSBhIERPTSBldmVudCByZWxhdGl2ZSB0byB0aGVcclxuXHQvLyBgY29udGFpbmVyYCBvciB0byB0aGUgd2hvbGUgcGFnZSBpZiBub3Qgc3BlY2lmaWVkLlxyXG5cdGdldE1vdXNlUG9zaXRpb246IGZ1bmN0aW9uIChlLCBjb250YWluZXIpIHtcclxuXHRcdGlmICghY29udGFpbmVyKSB7XHJcblx0XHRcdHJldHVybiBuZXcgTC5Qb2ludChlLmNsaWVudFgsIGUuY2xpZW50WSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHJlY3QgPSBjb250YWluZXIuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcblxyXG5cdFx0cmV0dXJuIG5ldyBMLlBvaW50KFxyXG5cdFx0XHRlLmNsaWVudFggLSByZWN0LmxlZnQgLSBjb250YWluZXIuY2xpZW50TGVmdCxcclxuXHRcdFx0ZS5jbGllbnRZIC0gcmVjdC50b3AgLSBjb250YWluZXIuY2xpZW50VG9wKTtcclxuXHR9LFxyXG5cclxuXHQvLyBDaHJvbWUgb24gV2luIHNjcm9sbHMgZG91YmxlIHRoZSBwaXhlbHMgYXMgaW4gb3RoZXIgcGxhdGZvcm1zIChzZWUgIzQ1MzgpLFxyXG5cdC8vIGFuZCBGaXJlZm94IHNjcm9sbHMgZGV2aWNlIHBpeGVscywgbm90IENTUyBwaXhlbHNcclxuXHRfd2hlZWxQeEZhY3RvcjogKEwuQnJvd3Nlci53aW4gJiYgTC5Ccm93c2VyLmNocm9tZSkgPyAyIDpcclxuXHQgICAgICAgICAgICAgICAgTC5Ccm93c2VyLmdlY2tvID8gd2luZG93LmRldmljZVBpeGVsUmF0aW8gOlxyXG5cdCAgICAgICAgICAgICAgICAxLFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gZ2V0V2hlZWxEZWx0YShldjogRE9NRXZlbnQpOiBOdW1iZXJcclxuXHQvLyBHZXRzIG5vcm1hbGl6ZWQgd2hlZWwgZGVsdGEgZnJvbSBhIG1vdXNld2hlZWwgRE9NIGV2ZW50LCBpbiB2ZXJ0aWNhbFxyXG5cdC8vIHBpeGVscyBzY3JvbGxlZCAobmVnYXRpdmUgaWYgc2Nyb2xsaW5nIGRvd24pLlxyXG5cdC8vIEV2ZW50cyBmcm9tIHBvaW50aW5nIGRldmljZXMgd2l0aG91dCBwcmVjaXNlIHNjcm9sbGluZyBhcmUgbWFwcGVkIHRvXHJcblx0Ly8gYSBiZXN0IGd1ZXNzIG9mIDYwIHBpeGVscy5cclxuXHRnZXRXaGVlbERlbHRhOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0cmV0dXJuIChMLkJyb3dzZXIuZWRnZSkgPyBlLndoZWVsRGVsdGFZIC8gMiA6IC8vIERvbid0IHRydXN0IHdpbmRvdy1nZW9tZXRyeS1iYXNlZCBkZWx0YVxyXG5cdFx0ICAgICAgIChlLmRlbHRhWSAmJiBlLmRlbHRhTW9kZSA9PT0gMCkgPyAtZS5kZWx0YVkgLyBMLkRvbUV2ZW50Ll93aGVlbFB4RmFjdG9yIDogLy8gUGl4ZWxzXHJcblx0XHQgICAgICAgKGUuZGVsdGFZICYmIGUuZGVsdGFNb2RlID09PSAxKSA/IC1lLmRlbHRhWSAqIDIwIDogLy8gTGluZXNcclxuXHRcdCAgICAgICAoZS5kZWx0YVkgJiYgZS5kZWx0YU1vZGUgPT09IDIpID8gLWUuZGVsdGFZICogNjAgOiAvLyBQYWdlc1xyXG5cdFx0ICAgICAgIChlLmRlbHRhWCB8fCBlLmRlbHRhWikgPyAwIDpcdC8vIFNraXAgaG9yaXpvbnRhbC9kZXB0aCB3aGVlbCBldmVudHNcclxuXHRcdCAgICAgICBlLndoZWVsRGVsdGEgPyAoZS53aGVlbERlbHRhWSB8fCBlLndoZWVsRGVsdGEpIC8gMiA6IC8vIExlZ2FjeSBJRSBwaXhlbHNcclxuXHRcdCAgICAgICAoZS5kZXRhaWwgJiYgTWF0aC5hYnMoZS5kZXRhaWwpIDwgMzI3NjUpID8gLWUuZGV0YWlsICogMjAgOiAvLyBMZWdhY3kgTW96IGxpbmVzXHJcblx0XHQgICAgICAgZS5kZXRhaWwgPyBlLmRldGFpbCAvIC0zMjc2NSAqIDYwIDogLy8gTGVnYWN5IE1veiBwYWdlc1xyXG5cdFx0ICAgICAgIDA7XHJcblx0fSxcclxuXHJcblx0X3NraXBFdmVudHM6IHt9LFxyXG5cclxuXHRfZmFrZVN0b3A6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHQvLyBmYWtlcyBzdG9wUHJvcGFnYXRpb24gYnkgc2V0dGluZyBhIHNwZWNpYWwgZXZlbnQgZmxhZywgY2hlY2tlZC9yZXNldCB3aXRoIEwuRG9tRXZlbnQuX3NraXBwZWQoZSlcclxuXHRcdEwuRG9tRXZlbnQuX3NraXBFdmVudHNbZS50eXBlXSA9IHRydWU7XHJcblx0fSxcclxuXHJcblx0X3NraXBwZWQ6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR2YXIgc2tpcHBlZCA9IHRoaXMuX3NraXBFdmVudHNbZS50eXBlXTtcclxuXHRcdC8vIHJlc2V0IHdoZW4gY2hlY2tpbmcsIGFzIGl0J3Mgb25seSB1c2VkIGluIG1hcCBjb250YWluZXIgYW5kIHByb3BhZ2F0ZXMgb3V0c2lkZSBvZiB0aGUgbWFwXHJcblx0XHR0aGlzLl9za2lwRXZlbnRzW2UudHlwZV0gPSBmYWxzZTtcclxuXHRcdHJldHVybiBza2lwcGVkO1xyXG5cdH0sXHJcblxyXG5cdC8vIGNoZWNrIGlmIGVsZW1lbnQgcmVhbGx5IGxlZnQvZW50ZXJlZCB0aGUgZXZlbnQgdGFyZ2V0IChmb3IgbW91c2VlbnRlci9tb3VzZWxlYXZlKVxyXG5cdF9pc0V4dGVybmFsVGFyZ2V0OiBmdW5jdGlvbiAoZWwsIGUpIHtcclxuXHJcblx0XHR2YXIgcmVsYXRlZCA9IGUucmVsYXRlZFRhcmdldDtcclxuXHJcblx0XHRpZiAoIXJlbGF0ZWQpIHsgcmV0dXJuIHRydWU7IH1cclxuXHJcblx0XHR0cnkge1xyXG5cdFx0XHR3aGlsZSAocmVsYXRlZCAmJiAocmVsYXRlZCAhPT0gZWwpKSB7XHJcblx0XHRcdFx0cmVsYXRlZCA9IHJlbGF0ZWQucGFyZW50Tm9kZTtcclxuXHRcdFx0fVxyXG5cdFx0fSBjYXRjaCAoZXJyKSB7XHJcblx0XHRcdHJldHVybiBmYWxzZTtcclxuXHRcdH1cclxuXHRcdHJldHVybiAocmVsYXRlZCAhPT0gZWwpO1xyXG5cdH0sXHJcblxyXG5cdC8vIHRoaXMgaXMgYSBob3JyaWJsZSB3b3JrYXJvdW5kIGZvciBhIGJ1ZyBpbiBBbmRyb2lkIHdoZXJlIGEgc2luZ2xlIHRvdWNoIHRyaWdnZXJzIHR3byBjbGljayBldmVudHNcclxuXHRfZmlsdGVyQ2xpY2s6IGZ1bmN0aW9uIChlLCBoYW5kbGVyKSB7XHJcblx0XHR2YXIgdGltZVN0YW1wID0gKGUudGltZVN0YW1wIHx8IChlLm9yaWdpbmFsRXZlbnQgJiYgZS5vcmlnaW5hbEV2ZW50LnRpbWVTdGFtcCkpLFxyXG5cdFx0ICAgIGVsYXBzZWQgPSBMLkRvbUV2ZW50Ll9sYXN0Q2xpY2sgJiYgKHRpbWVTdGFtcCAtIEwuRG9tRXZlbnQuX2xhc3RDbGljayk7XHJcblxyXG5cdFx0Ly8gYXJlIHRoZXkgY2xvc2VyIHRvZ2V0aGVyIHRoYW4gNTAwbXMgeWV0IG1vcmUgdGhhbiAxMDBtcz9cclxuXHRcdC8vIEFuZHJvaWQgdHlwaWNhbGx5IHRyaWdnZXJzIHRoZW0gfjMwMG1zIGFwYXJ0IHdoaWxlIG11bHRpcGxlIGxpc3RlbmVyc1xyXG5cdFx0Ly8gb24gdGhlIHNhbWUgZXZlbnQgc2hvdWxkIGJlIHRyaWdnZXJlZCBmYXIgZmFzdGVyO1xyXG5cdFx0Ly8gb3IgY2hlY2sgaWYgY2xpY2sgaXMgc2ltdWxhdGVkIG9uIHRoZSBlbGVtZW50LCBhbmQgaWYgaXQgaXMsIHJlamVjdCBhbnkgbm9uLXNpbXVsYXRlZCBldmVudHNcclxuXHJcblx0XHRpZiAoKGVsYXBzZWQgJiYgZWxhcHNlZCA+IDEwMCAmJiBlbGFwc2VkIDwgNTAwKSB8fCAoZS50YXJnZXQuX3NpbXVsYXRlZENsaWNrICYmICFlLl9zaW11bGF0ZWQpKSB7XHJcblx0XHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cdFx0TC5Eb21FdmVudC5fbGFzdENsaWNrID0gdGltZVN0YW1wO1xyXG5cclxuXHRcdGhhbmRsZXIoZSk7XHJcblx0fVxyXG59O1xyXG5cclxuLy8gQGZ1bmN0aW9uIGFkZExpc3RlbmVyKOKApik6IHRoaXNcclxuLy8gQWxpYXMgdG8gW2BMLkRvbUV2ZW50Lm9uYF0oI2RvbWV2ZW50LW9uKVxyXG5MLkRvbUV2ZW50LmFkZExpc3RlbmVyID0gTC5Eb21FdmVudC5vbjtcclxuXHJcbi8vIEBmdW5jdGlvbiByZW1vdmVMaXN0ZW5lcijigKYpOiB0aGlzXHJcbi8vIEFsaWFzIHRvIFtgTC5Eb21FdmVudC5vZmZgXSgjZG9tZXZlbnQtb2ZmKVxyXG5MLkRvbUV2ZW50LnJlbW92ZUxpc3RlbmVyID0gTC5Eb21FdmVudC5vZmY7XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgUG9zQW5pbWF0aW9uXG4gKiBAYWthIEwuUG9zQW5pbWF0aW9uXG4gKiBAaW5oZXJpdHMgRXZlbnRlZFxuICogVXNlZCBpbnRlcm5hbGx5IGZvciBwYW5uaW5nIGFuaW1hdGlvbnMsIHV0aWxpemluZyBDU1MzIFRyYW5zaXRpb25zIGZvciBtb2Rlcm4gYnJvd3NlcnMgYW5kIGEgdGltZXIgZmFsbGJhY2sgZm9yIElFNi05LlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGBqc1xuICogdmFyIGZ4ID0gbmV3IEwuUG9zQW5pbWF0aW9uKCk7XG4gKiBmeC5ydW4oZWwsIFszMDAsIDUwMF0sIDAuNSk7XG4gKiBgYGBcbiAqXG4gKiBAY29uc3RydWN0b3IgTC5Qb3NBbmltYXRpb24oKVxuICogQ3JlYXRlcyBhIGBQb3NBbmltYXRpb25gIG9iamVjdC5cbiAqXG4gKi9cblxuTC5Qb3NBbmltYXRpb24gPSBMLkV2ZW50ZWQuZXh0ZW5kKHtcblxuXHQvLyBAbWV0aG9kIHJ1bihlbDogSFRNTEVsZW1lbnQsIG5ld1BvczogUG9pbnQsIGR1cmF0aW9uPzogTnVtYmVyLCBlYXNlTGluZWFyaXR5PzogTnVtYmVyKVxuXHQvLyBSdW4gYW4gYW5pbWF0aW9uIG9mIGEgZ2l2ZW4gZWxlbWVudCB0byBhIG5ldyBwb3NpdGlvbiwgb3B0aW9uYWxseSBzZXR0aW5nXG5cdC8vIGR1cmF0aW9uIGluIHNlY29uZHMgKGAwLjI1YCBieSBkZWZhdWx0KSBhbmQgZWFzaW5nIGxpbmVhcml0eSBmYWN0b3IgKDNyZFxuXHQvLyBhcmd1bWVudCBvZiB0aGUgW2N1YmljIGJlemllciBjdXJ2ZV0oaHR0cDovL2N1YmljLWJlemllci5jb20vIzAsMCwuNSwxKSxcblx0Ly8gYDAuNWAgYnkgZGVmYXVsdCkuXG5cdHJ1bjogZnVuY3Rpb24gKGVsLCBuZXdQb3MsIGR1cmF0aW9uLCBlYXNlTGluZWFyaXR5KSB7XG5cdFx0dGhpcy5zdG9wKCk7XG5cblx0XHR0aGlzLl9lbCA9IGVsO1xuXHRcdHRoaXMuX2luUHJvZ3Jlc3MgPSB0cnVlO1xuXHRcdHRoaXMuX2R1cmF0aW9uID0gZHVyYXRpb24gfHwgMC4yNTtcblx0XHR0aGlzLl9lYXNlT3V0UG93ZXIgPSAxIC8gTWF0aC5tYXgoZWFzZUxpbmVhcml0eSB8fCAwLjUsIDAuMik7XG5cblx0XHR0aGlzLl9zdGFydFBvcyA9IEwuRG9tVXRpbC5nZXRQb3NpdGlvbihlbCk7XG5cdFx0dGhpcy5fb2Zmc2V0ID0gbmV3UG9zLnN1YnRyYWN0KHRoaXMuX3N0YXJ0UG9zKTtcblx0XHR0aGlzLl9zdGFydFRpbWUgPSArbmV3IERhdGUoKTtcblxuXHRcdC8vIEBldmVudCBzdGFydDogRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBhbmltYXRpb24gc3RhcnRzXG5cdFx0dGhpcy5maXJlKCdzdGFydCcpO1xuXG5cdFx0dGhpcy5fYW5pbWF0ZSgpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc3RvcCgpXG5cdC8vIFN0b3BzIHRoZSBhbmltYXRpb24gKGlmIGN1cnJlbnRseSBydW5uaW5nKS5cblx0c3RvcDogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5faW5Qcm9ncmVzcykgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX3N0ZXAodHJ1ZSk7XG5cdFx0dGhpcy5fY29tcGxldGUoKTtcblx0fSxcblxuXHRfYW5pbWF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdC8vIGFuaW1hdGlvbiBsb29wXG5cdFx0dGhpcy5fYW5pbUlkID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fYW5pbWF0ZSwgdGhpcyk7XG5cdFx0dGhpcy5fc3RlcCgpO1xuXHR9LFxuXG5cdF9zdGVwOiBmdW5jdGlvbiAocm91bmQpIHtcblx0XHR2YXIgZWxhcHNlZCA9ICgrbmV3IERhdGUoKSkgLSB0aGlzLl9zdGFydFRpbWUsXG5cdFx0ICAgIGR1cmF0aW9uID0gdGhpcy5fZHVyYXRpb24gKiAxMDAwO1xuXG5cdFx0aWYgKGVsYXBzZWQgPCBkdXJhdGlvbikge1xuXHRcdFx0dGhpcy5fcnVuRnJhbWUodGhpcy5fZWFzZU91dChlbGFwc2VkIC8gZHVyYXRpb24pLCByb3VuZCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX3J1bkZyYW1lKDEpO1xuXHRcdFx0dGhpcy5fY29tcGxldGUoKTtcblx0XHR9XG5cdH0sXG5cblx0X3J1bkZyYW1lOiBmdW5jdGlvbiAocHJvZ3Jlc3MsIHJvdW5kKSB7XG5cdFx0dmFyIHBvcyA9IHRoaXMuX3N0YXJ0UG9zLmFkZCh0aGlzLl9vZmZzZXQubXVsdGlwbHlCeShwcm9ncmVzcykpO1xuXHRcdGlmIChyb3VuZCkge1xuXHRcdFx0cG9zLl9yb3VuZCgpO1xuXHRcdH1cblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fZWwsIHBvcyk7XG5cblx0XHQvLyBAZXZlbnQgc3RlcDogRXZlbnRcblx0XHQvLyBGaXJlZCBjb250aW51b3VzbHkgZHVyaW5nIHRoZSBhbmltYXRpb24uXG5cdFx0dGhpcy5maXJlKCdzdGVwJyk7XG5cdH0sXG5cblx0X2NvbXBsZXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9hbmltSWQpO1xuXG5cdFx0dGhpcy5faW5Qcm9ncmVzcyA9IGZhbHNlO1xuXHRcdC8vIEBldmVudCBlbmQ6IEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYW5pbWF0aW9uIGVuZHMuXG5cdFx0dGhpcy5maXJlKCdlbmQnKTtcblx0fSxcblxuXHRfZWFzZU91dDogZnVuY3Rpb24gKHQpIHtcblx0XHRyZXR1cm4gMSAtIE1hdGgucG93KDEgLSB0LCB0aGlzLl9lYXNlT3V0UG93ZXIpO1xuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgUHJvamVjdGlvblxyXG4gKiBAcHJvamVjdGlvbiBMLlByb2plY3Rpb24uTWVyY2F0b3JcclxuICpcclxuICogRWxsaXB0aWNhbCBNZXJjYXRvciBwcm9qZWN0aW9uIOKAlCBtb3JlIGNvbXBsZXggdGhhbiBTcGhlcmljYWwgTWVyY2F0b3IuIFRha2VzIGludG8gYWNjb3VudCB0aGF0IEVhcnRoIGlzIGEgZ2VvaWQsIG5vdCBhIHBlcmZlY3Qgc3BoZXJlLiBVc2VkIGJ5IHRoZSBFUFNHOjMzOTUgQ1JTLlxyXG4gKi9cclxuXHJcbkwuUHJvamVjdGlvbi5NZXJjYXRvciA9IHtcclxuXHRSOiA2Mzc4MTM3LFxyXG5cdFJfTUlOT1I6IDYzNTY3NTIuMzE0MjQ1MTc5LFxyXG5cclxuXHRib3VuZHM6IEwuYm91bmRzKFstMjAwMzc1MDguMzQyNzksIC0xNTQ5NjU3MC43Mzk3Ml0sIFsyMDAzNzUwOC4zNDI3OSwgMTg3NjQ2NTYuMjMxMzhdKSxcclxuXHJcblx0cHJvamVjdDogZnVuY3Rpb24gKGxhdGxuZykge1xyXG5cdFx0dmFyIGQgPSBNYXRoLlBJIC8gMTgwLFxyXG5cdFx0ICAgIHIgPSB0aGlzLlIsXHJcblx0XHQgICAgeSA9IGxhdGxuZy5sYXQgKiBkLFxyXG5cdFx0ICAgIHRtcCA9IHRoaXMuUl9NSU5PUiAvIHIsXHJcblx0XHQgICAgZSA9IE1hdGguc3FydCgxIC0gdG1wICogdG1wKSxcclxuXHRcdCAgICBjb24gPSBlICogTWF0aC5zaW4oeSk7XHJcblxyXG5cdFx0dmFyIHRzID0gTWF0aC50YW4oTWF0aC5QSSAvIDQgLSB5IC8gMikgLyBNYXRoLnBvdygoMSAtIGNvbikgLyAoMSArIGNvbiksIGUgLyAyKTtcclxuXHRcdHkgPSAtciAqIE1hdGgubG9nKE1hdGgubWF4KHRzLCAxRS0xMCkpO1xyXG5cclxuXHRcdHJldHVybiBuZXcgTC5Qb2ludChsYXRsbmcubG5nICogZCAqIHIsIHkpO1xyXG5cdH0sXHJcblxyXG5cdHVucHJvamVjdDogZnVuY3Rpb24gKHBvaW50KSB7XHJcblx0XHR2YXIgZCA9IDE4MCAvIE1hdGguUEksXHJcblx0XHQgICAgciA9IHRoaXMuUixcclxuXHRcdCAgICB0bXAgPSB0aGlzLlJfTUlOT1IgLyByLFxyXG5cdFx0ICAgIGUgPSBNYXRoLnNxcnQoMSAtIHRtcCAqIHRtcCksXHJcblx0XHQgICAgdHMgPSBNYXRoLmV4cCgtcG9pbnQueSAvIHIpLFxyXG5cdFx0ICAgIHBoaSA9IE1hdGguUEkgLyAyIC0gMiAqIE1hdGguYXRhbih0cyk7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGRwaGkgPSAwLjEsIGNvbjsgaSA8IDE1ICYmIE1hdGguYWJzKGRwaGkpID4gMWUtNzsgaSsrKSB7XHJcblx0XHRcdGNvbiA9IGUgKiBNYXRoLnNpbihwaGkpO1xyXG5cdFx0XHRjb24gPSBNYXRoLnBvdygoMSAtIGNvbikgLyAoMSArIGNvbiksIGUgLyAyKTtcclxuXHRcdFx0ZHBoaSA9IE1hdGguUEkgLyAyIC0gMiAqIE1hdGguYXRhbih0cyAqIGNvbikgLSBwaGk7XHJcblx0XHRcdHBoaSArPSBkcGhpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBuZXcgTC5MYXRMbmcocGhpICogZCwgcG9pbnQueCAqIGQgLyByKTtcclxuXHR9XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBuYW1lc3BhY2UgQ1JTXHJcbiAqIEBjcnMgTC5DUlMuRVBTRzMzOTVcclxuICpcclxuICogUmFyZWx5IHVzZWQgYnkgc29tZSBjb21tZXJjaWFsIHRpbGUgcHJvdmlkZXJzLiBVc2VzIEVsbGlwdGljYWwgTWVyY2F0b3IgcHJvamVjdGlvbi5cclxuICovXHJcblxyXG5MLkNSUy5FUFNHMzM5NSA9IEwuZXh0ZW5kKHt9LCBMLkNSUy5FYXJ0aCwge1xyXG5cdGNvZGU6ICdFUFNHOjMzOTUnLFxyXG5cdHByb2plY3Rpb246IEwuUHJvamVjdGlvbi5NZXJjYXRvcixcclxuXHJcblx0dHJhbnNmb3JtYXRpb246IChmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgc2NhbGUgPSAwLjUgLyAoTWF0aC5QSSAqIEwuUHJvamVjdGlvbi5NZXJjYXRvci5SKTtcclxuXHRcdHJldHVybiBuZXcgTC5UcmFuc2Zvcm1hdGlvbihzY2FsZSwgMC41LCAtc2NhbGUsIDAuNSk7XHJcblx0fSgpKVxyXG59KTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBHcmlkTGF5ZXJcbiAqIEBpbmhlcml0cyBMYXllclxuICogQGFrYSBMLkdyaWRMYXllclxuICpcbiAqIEdlbmVyaWMgY2xhc3MgZm9yIGhhbmRsaW5nIGEgdGlsZWQgZ3JpZCBvZiBIVE1MIGVsZW1lbnRzLiBUaGlzIGlzIHRoZSBiYXNlIGNsYXNzIGZvciBhbGwgdGlsZSBsYXllcnMgYW5kIHJlcGxhY2VzIGBUaWxlTGF5ZXIuQ2FudmFzYC5cbiAqIEdyaWRMYXllciBjYW4gYmUgZXh0ZW5kZWQgdG8gY3JlYXRlIGEgdGlsZWQgZ3JpZCBvZiBIVE1MIGVsZW1lbnRzIGxpa2UgYDxjYW52YXM+YCwgYDxpbWc+YCBvciBgPGRpdj5gLiBHcmlkTGF5ZXIgd2lsbCBoYW5kbGUgY3JlYXRpbmcgYW5kIGFuaW1hdGluZyB0aGVzZSBET00gZWxlbWVudHMgZm9yIHlvdS5cbiAqXG4gKlxuICogQHNlY3Rpb24gU3luY2hyb25vdXMgdXNhZ2VcbiAqIEBleGFtcGxlXG4gKlxuICogVG8gY3JlYXRlIGEgY3VzdG9tIGxheWVyLCBleHRlbmQgR3JpZExheWVyIGFuZCBpbXBsZW1lbnQgdGhlIGBjcmVhdGVUaWxlKClgIG1ldGhvZCwgd2hpY2ggd2lsbCBiZSBwYXNzZWQgYSBgUG9pbnRgIG9iamVjdCB3aXRoIHRoZSBgeGAsIGB5YCwgYW5kIGB6YCAoem9vbSBsZXZlbCkgY29vcmRpbmF0ZXMgdG8gZHJhdyB5b3VyIHRpbGUuXG4gKlxuICogYGBganNcbiAqIHZhciBDYW52YXNMYXllciA9IEwuR3JpZExheWVyLmV4dGVuZCh7XG4gKiAgICAgY3JlYXRlVGlsZTogZnVuY3Rpb24oY29vcmRzKXtcbiAqICAgICAgICAgLy8gY3JlYXRlIGEgPGNhbnZhcz4gZWxlbWVudCBmb3IgZHJhd2luZ1xuICogICAgICAgICB2YXIgdGlsZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2NhbnZhcycsICdsZWFmbGV0LXRpbGUnKTtcbiAqXG4gKiAgICAgICAgIC8vIHNldHVwIHRpbGUgd2lkdGggYW5kIGhlaWdodCBhY2NvcmRpbmcgdG8gdGhlIG9wdGlvbnNcbiAqICAgICAgICAgdmFyIHNpemUgPSB0aGlzLmdldFRpbGVTaXplKCk7XG4gKiAgICAgICAgIHRpbGUud2lkdGggPSBzaXplLng7XG4gKiAgICAgICAgIHRpbGUuaGVpZ2h0ID0gc2l6ZS55O1xuICpcbiAqICAgICAgICAgLy8gZ2V0IGEgY2FudmFzIGNvbnRleHQgYW5kIGRyYXcgc29tZXRoaW5nIG9uIGl0IHVzaW5nIGNvb3Jkcy54LCBjb29yZHMueSBhbmQgY29vcmRzLnpcbiAqICAgICAgICAgdmFyIGN0eCA9IHRpbGUuZ2V0Q29udGV4dCgnMmQnKTtcbiAqXG4gKiAgICAgICAgIC8vIHJldHVybiB0aGUgdGlsZSBzbyBpdCBjYW4gYmUgcmVuZGVyZWQgb24gc2NyZWVuXG4gKiAgICAgICAgIHJldHVybiB0aWxlO1xuICogICAgIH1cbiAqIH0pO1xuICogYGBgXG4gKlxuICogQHNlY3Rpb24gQXN5bmNocm9ub3VzIHVzYWdlXG4gKiBAZXhhbXBsZVxuICpcbiAqIFRpbGUgY3JlYXRpb24gY2FuIGFsc28gYmUgYXN5bmNocm9ub3VzLCB0aGlzIGlzIHVzZWZ1bCB3aGVuIHVzaW5nIGEgdGhpcmQtcGFydHkgZHJhd2luZyBsaWJyYXJ5LiBPbmNlIHRoZSB0aWxlIGlzIGZpbmlzaGVkIGRyYXdpbmcgaXQgY2FuIGJlIHBhc3NlZCB0byB0aGUgYGRvbmUoKWAgY2FsbGJhY2suXG4gKlxuICogYGBganNcbiAqIHZhciBDYW52YXNMYXllciA9IEwuR3JpZExheWVyLmV4dGVuZCh7XG4gKiAgICAgY3JlYXRlVGlsZTogZnVuY3Rpb24oY29vcmRzLCBkb25lKXtcbiAqICAgICAgICAgdmFyIGVycm9yO1xuICpcbiAqICAgICAgICAgLy8gY3JlYXRlIGEgPGNhbnZhcz4gZWxlbWVudCBmb3IgZHJhd2luZ1xuICogICAgICAgICB2YXIgdGlsZSA9IEwuRG9tVXRpbC5jcmVhdGUoJ2NhbnZhcycsICdsZWFmbGV0LXRpbGUnKTtcbiAqXG4gKiAgICAgICAgIC8vIHNldHVwIHRpbGUgd2lkdGggYW5kIGhlaWdodCBhY2NvcmRpbmcgdG8gdGhlIG9wdGlvbnNcbiAqICAgICAgICAgdmFyIHNpemUgPSB0aGlzLmdldFRpbGVTaXplKCk7XG4gKiAgICAgICAgIHRpbGUud2lkdGggPSBzaXplLng7XG4gKiAgICAgICAgIHRpbGUuaGVpZ2h0ID0gc2l6ZS55O1xuICpcbiAqICAgICAgICAgLy8gZHJhdyBzb21ldGhpbmcgYXN5bmNocm9ub3VzbHkgYW5kIHBhc3MgdGhlIHRpbGUgdG8gdGhlIGRvbmUoKSBjYWxsYmFja1xuICogICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICogICAgICAgICAgICAgZG9uZShlcnJvciwgdGlsZSk7XG4gKiAgICAgICAgIH0sIDEwMDApO1xuICpcbiAqICAgICAgICAgcmV0dXJuIHRpbGU7XG4gKiAgICAgfVxuICogfSk7XG4gKiBgYGBcbiAqXG4gKiBAc2VjdGlvblxuICovXG5cblxuTC5HcmlkTGF5ZXIgPSBMLkxheWVyLmV4dGVuZCh7XG5cblx0Ly8gQHNlY3Rpb25cblx0Ly8gQGFrYSBHcmlkTGF5ZXIgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiB0aWxlU2l6ZTogTnVtYmVyfFBvaW50ID0gMjU2XG5cdFx0Ly8gV2lkdGggYW5kIGhlaWdodCBvZiB0aWxlcyBpbiB0aGUgZ3JpZC4gVXNlIGEgbnVtYmVyIGlmIHdpZHRoIGFuZCBoZWlnaHQgYXJlIGVxdWFsLCBvciBgTC5wb2ludCh3aWR0aCwgaGVpZ2h0KWAgb3RoZXJ3aXNlLlxuXHRcdHRpbGVTaXplOiAyNTYsXG5cblx0XHQvLyBAb3B0aW9uIG9wYWNpdHk6IE51bWJlciA9IDEuMFxuXHRcdC8vIE9wYWNpdHkgb2YgdGhlIHRpbGVzLiBDYW4gYmUgdXNlZCBpbiB0aGUgYGNyZWF0ZVRpbGUoKWAgZnVuY3Rpb24uXG5cdFx0b3BhY2l0eTogMSxcblxuXHRcdC8vIEBvcHRpb24gdXBkYXRlV2hlbklkbGU6IEJvb2xlYW4gPSBkZXBlbmRzXG5cdFx0Ly8gSWYgYGZhbHNlYCwgbmV3IHRpbGVzIGFyZSBsb2FkZWQgZHVyaW5nIHBhbm5pbmcsIG90aGVyd2lzZSBvbmx5IGFmdGVyIGl0IChmb3IgYmV0dGVyIHBlcmZvcm1hbmNlKS4gYHRydWVgIGJ5IGRlZmF1bHQgb24gbW9iaWxlIGJyb3dzZXJzLCBvdGhlcndpc2UgYGZhbHNlYC5cblx0XHR1cGRhdGVXaGVuSWRsZTogTC5Ccm93c2VyLm1vYmlsZSxcblxuXHRcdC8vIEBvcHRpb24gdXBkYXRlV2hlblpvb21pbmc6IEJvb2xlYW4gPSB0cnVlXG5cdFx0Ly8gQnkgZGVmYXVsdCwgYSBzbW9vdGggem9vbSBhbmltYXRpb24gKGR1cmluZyBhIFt0b3VjaCB6b29tXSgjbWFwLXRvdWNoem9vbSkgb3IgYSBbYGZseVRvKClgXSgjbWFwLWZseXRvKSkgd2lsbCB1cGRhdGUgZ3JpZCBsYXllcnMgZXZlcnkgaW50ZWdlciB6b29tIGxldmVsLiBTZXR0aW5nIHRoaXMgb3B0aW9uIHRvIGBmYWxzZWAgd2lsbCB1cGRhdGUgdGhlIGdyaWQgbGF5ZXIgb25seSB3aGVuIHRoZSBzbW9vdGggYW5pbWF0aW9uIGVuZHMuXG5cdFx0dXBkYXRlV2hlblpvb21pbmc6IHRydWUsXG5cblx0XHQvLyBAb3B0aW9uIHVwZGF0ZUludGVydmFsOiBOdW1iZXIgPSAyMDBcblx0XHQvLyBUaWxlcyB3aWxsIG5vdCB1cGRhdGUgbW9yZSB0aGFuIG9uY2UgZXZlcnkgYHVwZGF0ZUludGVydmFsYCBtaWxsaXNlY29uZHMgd2hlbiBwYW5uaW5nLlxuXHRcdHVwZGF0ZUludGVydmFsOiAyMDAsXG5cblx0XHQvLyBAb3B0aW9uIHpJbmRleDogTnVtYmVyID0gMVxuXHRcdC8vIFRoZSBleHBsaWNpdCB6SW5kZXggb2YgdGhlIHRpbGUgbGF5ZXIuXG5cdFx0ekluZGV4OiAxLFxuXG5cdFx0Ly8gQG9wdGlvbiBib3VuZHM6IExhdExuZ0JvdW5kcyA9IHVuZGVmaW5lZFxuXHRcdC8vIElmIHNldCwgdGlsZXMgd2lsbCBvbmx5IGJlIGxvYWRlZCBpbnNpZGUgdGhlIHNldCBgTGF0TG5nQm91bmRzYC5cblx0XHRib3VuZHM6IG51bGwsXG5cblx0XHQvLyBAb3B0aW9uIG1pblpvb206IE51bWJlciA9IDBcblx0XHQvLyBUaGUgbWluaW11bSB6b29tIGxldmVsIHRoYXQgdGlsZXMgd2lsbCBiZSBsb2FkZWQgYXQuIEJ5IGRlZmF1bHQgdGhlIGVudGlyZSBtYXAuXG5cdFx0bWluWm9vbTogMCxcblxuXHRcdC8vIEBvcHRpb24gbWF4Wm9vbTogTnVtYmVyID0gdW5kZWZpbmVkXG5cdFx0Ly8gVGhlIG1heGltdW0gem9vbSBsZXZlbCB0aGF0IHRpbGVzIHdpbGwgYmUgbG9hZGVkIGF0LlxuXHRcdG1heFpvb206IHVuZGVmaW5lZCxcblxuXHRcdC8vIEBvcHRpb24gbm9XcmFwOiBCb29sZWFuID0gZmFsc2Vcblx0XHQvLyBXaGV0aGVyIHRoZSBsYXllciBpcyB3cmFwcGVkIGFyb3VuZCB0aGUgYW50aW1lcmlkaWFuLiBJZiBgdHJ1ZWAsIHRoZVxuXHRcdC8vIEdyaWRMYXllciB3aWxsIG9ubHkgYmUgZGlzcGxheWVkIG9uY2UgYXQgbG93IHpvb20gbGV2ZWxzLiBIYXMgbm9cblx0XHQvLyBlZmZlY3Qgd2hlbiB0aGUgW21hcCBDUlNdKCNtYXAtY3JzKSBkb2Vzbid0IHdyYXAgYXJvdW5kLiBDYW4gYmUgdXNlZFxuXHRcdC8vIGluIGNvbWJpbmF0aW9uIHdpdGggW2Bib3VuZHNgXSgjZ3JpZGxheWVyLWJvdW5kcykgdG8gcHJldmVudCByZXF1ZXN0aW5nXG5cdFx0Ly8gdGlsZXMgb3V0c2lkZSB0aGUgQ1JTIGxpbWl0cy5cblx0XHRub1dyYXA6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAndGlsZVBhbmUnXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgZ3JpZCBsYXllciB3aWxsIGJlIGFkZGVkLlxuXHRcdHBhbmU6ICd0aWxlUGFuZScsXG5cblx0XHQvLyBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gJydcblx0XHQvLyBBIGN1c3RvbSBjbGFzcyBuYW1lIHRvIGFzc2lnbiB0byB0aGUgdGlsZSBsYXllci4gRW1wdHkgYnkgZGVmYXVsdC5cblx0XHRjbGFzc05hbWU6ICcnLFxuXG5cdFx0Ly8gQG9wdGlvbiBrZWVwQnVmZmVyOiBOdW1iZXIgPSAyXG5cdFx0Ly8gV2hlbiBwYW5uaW5nIHRoZSBtYXAsIGtlZXAgdGhpcyBtYW55IHJvd3MgYW5kIGNvbHVtbnMgb2YgdGlsZXMgYmVmb3JlIHVubG9hZGluZyB0aGVtLlxuXHRcdGtlZXBCdWZmZXI6IDJcblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2luaXRDb250YWluZXIoKTtcblxuXHRcdHRoaXMuX2xldmVscyA9IHt9O1xuXHRcdHRoaXMuX3RpbGVzID0ge307XG5cblx0XHR0aGlzLl9yZXNldFZpZXcoKTtcblx0XHR0aGlzLl91cGRhdGUoKTtcblx0fSxcblxuXHRiZWZvcmVBZGQ6IGZ1bmN0aW9uIChtYXApIHtcblx0XHRtYXAuX2FkZFpvb21MaW1pdCh0aGlzKTtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHRoaXMuX3JlbW92ZUFsbFRpbGVzKCk7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xuXHRcdG1hcC5fcmVtb3ZlWm9vbUxpbWl0KHRoaXMpO1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IG51bGw7XG5cdFx0dGhpcy5fdGlsZVpvb20gPSBudWxsO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250OiB0aGlzXG5cdC8vIEJyaW5ncyB0aGUgdGlsZSBsYXllciB0byB0aGUgdG9wIG9mIGFsbCB0aWxlIGxheWVycy5cblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0TC5Eb21VdGlsLnRvRnJvbnQodGhpcy5fY29udGFpbmVyKTtcblx0XHRcdHRoaXMuX3NldEF1dG9aSW5kZXgoTWF0aC5tYXgpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGJyaW5nVG9CYWNrOiB0aGlzXG5cdC8vIEJyaW5ncyB0aGUgdGlsZSBsYXllciB0byB0aGUgYm90dG9tIG9mIGFsbCB0aWxlIGxheWVycy5cblx0YnJpbmdUb0JhY2s6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHRMLkRvbVV0aWwudG9CYWNrKHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHR0aGlzLl9zZXRBdXRvWkluZGV4KE1hdGgubWluKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRDb250YWluZXI6IEhUTUxFbGVtZW50XG5cdC8vIFJldHVybnMgdGhlIEhUTUwgZWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSB0aWxlcyBmb3IgdGhpcyBsYXllci5cblx0Z2V0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcblx0fSxcblxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBbb3BhY2l0eV0oI2dyaWRsYXllci1vcGFjaXR5KSBvZiB0aGUgZ3JpZCBsYXllci5cblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKG9wYWNpdHkpIHtcblx0XHR0aGlzLm9wdGlvbnMub3BhY2l0eSA9IG9wYWNpdHk7XG5cdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0WkluZGV4KHpJbmRleDogTnVtYmVyKTogdGhpc1xuXHQvLyBDaGFuZ2VzIHRoZSBbekluZGV4XSgjZ3JpZGxheWVyLXppbmRleCkgb2YgdGhlIGdyaWQgbGF5ZXIuXG5cdHNldFpJbmRleDogZnVuY3Rpb24gKHpJbmRleCkge1xuXHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSB6SW5kZXg7XG5cdFx0dGhpcy5fdXBkYXRlWkluZGV4KCk7XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGlzTG9hZGluZzogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiBhbnkgdGlsZSBpbiB0aGUgZ3JpZCBsYXllciBoYXMgbm90IGZpbmlzaGVkIGxvYWRpbmcuXG5cdGlzTG9hZGluZzogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sb2FkaW5nO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVkcmF3OiB0aGlzXG5cdC8vIENhdXNlcyB0aGUgbGF5ZXIgdG8gY2xlYXIgYWxsIHRoZSB0aWxlcyBhbmQgcmVxdWVzdCB0aGVtIGFnYWluLlxuXHRyZWRyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0dGhpcy5fdXBkYXRlKCk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSB7XG5cdFx0XHR2aWV3cHJlcmVzZXQ6IHRoaXMuX2ludmFsaWRhdGVBbGwsXG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdHpvb206IHRoaXMuX3Jlc2V0Vmlldyxcblx0XHRcdG1vdmVlbmQ6IHRoaXMuX29uTW92ZUVuZFxuXHRcdH07XG5cblx0XHRpZiAoIXRoaXMub3B0aW9ucy51cGRhdGVXaGVuSWRsZSkge1xuXHRcdFx0Ly8gdXBkYXRlIHRpbGVzIG9uIG1vdmUsIGJ1dCBub3QgbW9yZSBvZnRlbiB0aGFuIG9uY2UgcGVyIGdpdmVuIGludGVydmFsXG5cdFx0XHRpZiAoIXRoaXMuX29uTW92ZSkge1xuXHRcdFx0XHR0aGlzLl9vbk1vdmUgPSBMLlV0aWwudGhyb3R0bGUodGhpcy5fb25Nb3ZlRW5kLCB0aGlzLm9wdGlvbnMudXBkYXRlSW50ZXJ2YWwsIHRoaXMpO1xuXHRcdFx0fVxuXG5cdFx0XHRldmVudHMubW92ZSA9IHRoaXMuX29uTW92ZTtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9hbmltYXRlWm9vbTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXG5cdC8vIExheWVycyBleHRlbmRpbmcgYEdyaWRMYXllcmAgc2hhbGwgcmVpbXBsZW1lbnQgdGhlIGZvbGxvd2luZyBtZXRob2QuXG5cdC8vIEBtZXRob2QgY3JlYXRlVGlsZShjb29yZHM6IE9iamVjdCwgZG9uZT86IEZ1bmN0aW9uKTogSFRNTEVsZW1lbnRcblx0Ly8gQ2FsbGVkIG9ubHkgaW50ZXJuYWxseSwgbXVzdCBiZSBvdmVycmlkZW4gYnkgY2xhc3NlcyBleHRlbmRpbmcgYEdyaWRMYXllcmAuXG5cdC8vIFJldHVybnMgdGhlIGBIVE1MRWxlbWVudGAgY29ycmVzcG9uZGluZyB0byB0aGUgZ2l2ZW4gYGNvb3Jkc2AuIElmIHRoZSBgZG9uZWAgY2FsbGJhY2tcblx0Ly8gaXMgc3BlY2lmaWVkLCBpdCBtdXN0IGJlIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBmaW5pc2hlZCBsb2FkaW5nIGFuZCBkcmF3aW5nLlxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuXHR9LFxuXG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVNpemU6IFBvaW50XG5cdC8vIE5vcm1hbGl6ZXMgdGhlIFt0aWxlU2l6ZSBvcHRpb25dKCNncmlkbGF5ZXItdGlsZXNpemUpIGludG8gYSBwb2ludC4gVXNlZCBieSB0aGUgYGNyZWF0ZVRpbGUoKWAgbWV0aG9kLlxuXHRnZXRUaWxlU2l6ZTogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBzID0gdGhpcy5vcHRpb25zLnRpbGVTaXplO1xuXHRcdHJldHVybiBzIGluc3RhbmNlb2YgTC5Qb2ludCA/IHMgOiBuZXcgTC5Qb2ludChzLCBzKTtcblx0fSxcblxuXHRfdXBkYXRlWkluZGV4OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lciAmJiB0aGlzLm9wdGlvbnMuekluZGV4ICE9PSB1bmRlZmluZWQgJiYgdGhpcy5vcHRpb25zLnpJbmRleCAhPT0gbnVsbCkge1xuXHRcdFx0dGhpcy5fY29udGFpbmVyLnN0eWxlLnpJbmRleCA9IHRoaXMub3B0aW9ucy56SW5kZXg7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRBdXRvWkluZGV4OiBmdW5jdGlvbiAoY29tcGFyZSkge1xuXHRcdC8vIGdvIHRocm91Z2ggYWxsIG90aGVyIGxheWVycyBvZiB0aGUgc2FtZSBwYW5lLCBzZXQgekluZGV4IHRvIG1heCArIDEgKGZyb250KSBvciBtaW4gLSAxIChiYWNrKVxuXG5cdFx0dmFyIGxheWVycyA9IHRoaXMuZ2V0UGFuZSgpLmNoaWxkcmVuLFxuXHRcdCAgICBlZGdlWkluZGV4ID0gLWNvbXBhcmUoLUluZmluaXR5LCBJbmZpbml0eSk7IC8vIC1JbmZpbml0eSBmb3IgbWF4LCBJbmZpbml0eSBmb3IgbWluXG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF5ZXJzLmxlbmd0aCwgekluZGV4OyBpIDwgbGVuOyBpKyspIHtcblxuXHRcdFx0ekluZGV4ID0gbGF5ZXJzW2ldLnN0eWxlLnpJbmRleDtcblxuXHRcdFx0aWYgKGxheWVyc1tpXSAhPT0gdGhpcy5fY29udGFpbmVyICYmIHpJbmRleCkge1xuXHRcdFx0XHRlZGdlWkluZGV4ID0gY29tcGFyZShlZGdlWkluZGV4LCArekluZGV4KTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoaXNGaW5pdGUoZWRnZVpJbmRleCkpIHtcblx0XHRcdHRoaXMub3B0aW9ucy56SW5kZXggPSBlZGdlWkluZGV4ICsgY29tcGFyZSgtMSwgMSk7XG5cdFx0XHR0aGlzLl91cGRhdGVaSW5kZXgoKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZU9wYWNpdHk6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cblxuXHRcdC8vIElFIGRvZXNuJ3QgaW5oZXJpdCBmaWx0ZXIgb3BhY2l0eSBwcm9wZXJseSwgc28gd2UncmUgZm9yY2VkIHRvIHNldCBpdCBvbiB0aWxlc1xuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkpIHsgcmV0dXJuOyB9XG5cblx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblxuXHRcdHZhciBub3cgPSArbmV3IERhdGUoKSxcblx0XHQgICAgbmV4dEZyYW1lID0gZmFsc2UsXG5cdFx0ICAgIHdpbGxQcnVuZSA9IGZhbHNlO1xuXG5cdFx0Zm9yICh2YXIga2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR2YXIgdGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0XHRpZiAoIXRpbGUuY3VycmVudCB8fCAhdGlsZS5sb2FkZWQpIHsgY29udGludWU7IH1cblxuXHRcdFx0dmFyIGZhZGUgPSBNYXRoLm1pbigxLCAobm93IC0gdGlsZS5sb2FkZWQpIC8gMjAwKTtcblxuXHRcdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGlsZS5lbCwgZmFkZSk7XG5cdFx0XHRpZiAoZmFkZSA8IDEpIHtcblx0XHRcdFx0bmV4dEZyYW1lID0gdHJ1ZTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGlmICh0aWxlLmFjdGl2ZSkgeyB3aWxsUHJ1bmUgPSB0cnVlOyB9XG5cdFx0XHRcdHRpbGUuYWN0aXZlID0gdHJ1ZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAod2lsbFBydW5lICYmICF0aGlzLl9ub1BydW5lKSB7IHRoaXMuX3BydW5lVGlsZXMoKTsgfVxuXG5cdFx0aWYgKG5leHRGcmFtZSkge1xuXHRcdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9mYWRlRnJhbWUpO1xuXHRcdFx0dGhpcy5fZmFkZUZyYW1lID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fdXBkYXRlT3BhY2l0eSwgdGhpcyk7XG5cdFx0fVxuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2NvbnRhaW5lcikgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWxheWVyICcgKyAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSB8fCAnJykpO1xuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCgpO1xuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5vcGFjaXR5IDwgMSkge1xuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2NvbnRhaW5lcik7XG5cdH0sXG5cblx0X3VwZGF0ZUxldmVsczogZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIHpvb20gPSB0aGlzLl90aWxlWm9vbSxcblx0XHQgICAgbWF4Wm9vbSA9IHRoaXMub3B0aW9ucy5tYXhab29tO1xuXG5cdFx0aWYgKHpvb20gPT09IHVuZGVmaW5lZCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9XG5cblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0aWYgKHRoaXMuX2xldmVsc1t6XS5lbC5jaGlsZHJlbi5sZW5ndGggfHwgeiA9PT0gem9vbSkge1xuXHRcdFx0XHR0aGlzLl9sZXZlbHNbel0uZWwuc3R5bGUuekluZGV4ID0gbWF4Wm9vbSAtIE1hdGguYWJzKHpvb20gLSB6KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fbGV2ZWxzW3pdLmVsKTtcblx0XHRcdFx0dGhpcy5fcmVtb3ZlVGlsZXNBdFpvb20oeik7XG5cdFx0XHRcdGRlbGV0ZSB0aGlzLl9sZXZlbHNbel07XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dmFyIGxldmVsID0gdGhpcy5fbGV2ZWxzW3pvb21dLFxuXHRcdCAgICBtYXAgPSB0aGlzLl9tYXA7XG5cblx0XHRpZiAoIWxldmVsKSB7XG5cdFx0XHRsZXZlbCA9IHRoaXMuX2xldmVsc1t6b29tXSA9IHt9O1xuXG5cdFx0XHRsZXZlbC5lbCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXRpbGUtY29udGFpbmVyIGxlYWZsZXQtem9vbS1hbmltYXRlZCcsIHRoaXMuX2NvbnRhaW5lcik7XG5cdFx0XHRsZXZlbC5lbC5zdHlsZS56SW5kZXggPSBtYXhab29tO1xuXG5cdFx0XHRsZXZlbC5vcmlnaW4gPSBtYXAucHJvamVjdChtYXAudW5wcm9qZWN0KG1hcC5nZXRQaXhlbE9yaWdpbigpKSwgem9vbSkucm91bmQoKTtcblx0XHRcdGxldmVsLnpvb20gPSB6b29tO1xuXG5cdFx0XHR0aGlzLl9zZXRab29tVHJhbnNmb3JtKGxldmVsLCBtYXAuZ2V0Q2VudGVyKCksIG1hcC5nZXRab29tKCkpO1xuXG5cdFx0XHQvLyBmb3JjZSB0aGUgYnJvd3NlciB0byBjb25zaWRlciB0aGUgbmV3bHkgYWRkZWQgZWxlbWVudCBmb3IgdHJhbnNpdGlvblxuXHRcdFx0TC5VdGlsLmZhbHNlRm4obGV2ZWwuZWwub2Zmc2V0V2lkdGgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2xldmVsID0gbGV2ZWw7XG5cblx0XHRyZXR1cm4gbGV2ZWw7XG5cdH0sXG5cblx0X3BydW5lVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBrZXksIHRpbGU7XG5cblx0XHR2YXIgem9vbSA9IHRoaXMuX21hcC5nZXRab29tKCk7XG5cdFx0aWYgKHpvb20gPiB0aGlzLm9wdGlvbnMubWF4Wm9vbSB8fFxuXHRcdFx0em9vbSA8IHRoaXMub3B0aW9ucy5taW5ab29tKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVBbGxUaWxlcygpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdHRpbGUucmV0YWluID0gdGlsZS5jdXJyZW50O1xuXHRcdH1cblxuXHRcdGZvciAoa2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNba2V5XTtcblx0XHRcdGlmICh0aWxlLmN1cnJlbnQgJiYgIXRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdHZhciBjb29yZHMgPSB0aWxlLmNvb3Jkcztcblx0XHRcdFx0aWYgKCF0aGlzLl9yZXRhaW5QYXJlbnQoY29vcmRzLngsIGNvb3Jkcy55LCBjb29yZHMueiwgY29vcmRzLnogLSA1KSkge1xuXHRcdFx0XHRcdHRoaXMuX3JldGFpbkNoaWxkcmVuKGNvb3Jkcy54LCBjb29yZHMueSwgY29vcmRzLnosIGNvb3Jkcy56ICsgMik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRmb3IgKGtleSBpbiB0aGlzLl90aWxlcykge1xuXHRcdFx0aWYgKCF0aGlzLl90aWxlc1trZXldLnJldGFpbikge1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVUaWxlKGtleSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9yZW1vdmVUaWxlc0F0Wm9vbTogZnVuY3Rpb24gKHpvb20pIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdGlmICh0aGlzLl90aWxlc1trZXldLmNvb3Jkcy56ICE9PSB6b29tKSB7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fcmVtb3ZlVGlsZShrZXkpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVtb3ZlQWxsVGlsZXM6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdHRoaXMuX3JlbW92ZVRpbGUoa2V5KTtcblx0XHR9XG5cdH0sXG5cblx0X2ludmFsaWRhdGVBbGw6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciB6IGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9sZXZlbHNbel0uZWwpO1xuXHRcdFx0ZGVsZXRlIHRoaXMuX2xldmVsc1t6XTtcblx0XHR9XG5cdFx0dGhpcy5fcmVtb3ZlQWxsVGlsZXMoKTtcblxuXHRcdHRoaXMuX3RpbGVab29tID0gbnVsbDtcblx0fSxcblxuXHRfcmV0YWluUGFyZW50OiBmdW5jdGlvbiAoeCwgeSwgeiwgbWluWm9vbSkge1xuXHRcdHZhciB4MiA9IE1hdGguZmxvb3IoeCAvIDIpLFxuXHRcdCAgICB5MiA9IE1hdGguZmxvb3IoeSAvIDIpLFxuXHRcdCAgICB6MiA9IHogLSAxLFxuXHRcdCAgICBjb29yZHMyID0gbmV3IEwuUG9pbnQoK3gyLCAreTIpO1xuXHRcdGNvb3JkczIueiA9ICt6MjtcblxuXHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzMiksXG5cdFx0ICAgIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0aWYgKHRpbGUgJiYgdGlsZS5hY3RpdmUpIHtcblx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdHJldHVybiB0cnVlO1xuXG5cdFx0fSBlbHNlIGlmICh0aWxlICYmIHRpbGUubG9hZGVkKSB7XG5cdFx0XHR0aWxlLnJldGFpbiA9IHRydWU7XG5cdFx0fVxuXG5cdFx0aWYgKHoyID4gbWluWm9vbSkge1xuXHRcdFx0cmV0dXJuIHRoaXMuX3JldGFpblBhcmVudCh4MiwgeTIsIHoyLCBtaW5ab29tKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0X3JldGFpbkNoaWxkcmVuOiBmdW5jdGlvbiAoeCwgeSwgeiwgbWF4Wm9vbSkge1xuXG5cdFx0Zm9yICh2YXIgaSA9IDIgKiB4OyBpIDwgMiAqIHggKyAyOyBpKyspIHtcblx0XHRcdGZvciAodmFyIGogPSAyICogeTsgaiA8IDIgKiB5ICsgMjsgaisrKSB7XG5cblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBMLlBvaW50KGksIGopO1xuXHRcdFx0XHRjb29yZHMueiA9IHogKyAxO1xuXG5cdFx0XHRcdHZhciBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKSxcblx0XHRcdFx0ICAgIHRpbGUgPSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0XHRcdGlmICh0aWxlICYmIHRpbGUuYWN0aXZlKSB7XG5cdFx0XHRcdFx0dGlsZS5yZXRhaW4gPSB0cnVlO1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGlsZSAmJiB0aWxlLmxvYWRlZCkge1xuXHRcdFx0XHRcdHRpbGUucmV0YWluID0gdHJ1ZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICh6ICsgMSA8IG1heFpvb20pIHtcblx0XHRcdFx0XHR0aGlzLl9yZXRhaW5DaGlsZHJlbihpLCBqLCB6ICsgMSwgbWF4Wm9vbSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0X3Jlc2V0VmlldzogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgYW5pbWF0aW5nID0gZSAmJiAoZS5waW5jaCB8fCBlLmZseVRvKTtcblx0XHR0aGlzLl9zZXRWaWV3KHRoaXMuX21hcC5nZXRDZW50ZXIoKSwgdGhpcy5fbWFwLmdldFpvb20oKSwgYW5pbWF0aW5nLCBhbmltYXRpbmcpO1xuXHR9LFxuXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKGUpIHtcblx0XHR0aGlzLl9zZXRWaWV3KGUuY2VudGVyLCBlLnpvb20sIHRydWUsIGUubm9VcGRhdGUpO1xuXHR9LFxuXG5cdF9zZXRWaWV3OiBmdW5jdGlvbiAoY2VudGVyLCB6b29tLCBub1BydW5lLCBub1VwZGF0ZSkge1xuXHRcdHZhciB0aWxlWm9vbSA9IE1hdGgucm91bmQoem9vbSk7XG5cdFx0aWYgKCh0aGlzLm9wdGlvbnMubWF4Wm9vbSAhPT0gdW5kZWZpbmVkICYmIHRpbGVab29tID4gdGhpcy5vcHRpb25zLm1heFpvb20pIHx8XG5cdFx0ICAgICh0aGlzLm9wdGlvbnMubWluWm9vbSAhPT0gdW5kZWZpbmVkICYmIHRpbGVab29tIDwgdGhpcy5vcHRpb25zLm1pblpvb20pKSB7XG5cdFx0XHR0aWxlWm9vbSA9IHVuZGVmaW5lZDtcblx0XHR9XG5cblx0XHR2YXIgdGlsZVpvb21DaGFuZ2VkID0gdGhpcy5vcHRpb25zLnVwZGF0ZVdoZW5ab29taW5nICYmICh0aWxlWm9vbSAhPT0gdGhpcy5fdGlsZVpvb20pO1xuXG5cdFx0aWYgKCFub1VwZGF0ZSB8fCB0aWxlWm9vbUNoYW5nZWQpIHtcblxuXHRcdFx0dGhpcy5fdGlsZVpvb20gPSB0aWxlWm9vbTtcblxuXHRcdFx0aWYgKHRoaXMuX2Fib3J0TG9hZGluZykge1xuXHRcdFx0XHR0aGlzLl9hYm9ydExvYWRpbmcoKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fdXBkYXRlTGV2ZWxzKCk7XG5cdFx0XHR0aGlzLl9yZXNldEdyaWQoKTtcblxuXHRcdFx0aWYgKHRpbGVab29tICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdFx0dGhpcy5fdXBkYXRlKGNlbnRlcik7XG5cdFx0XHR9XG5cblx0XHRcdGlmICghbm9QcnVuZSkge1xuXHRcdFx0XHR0aGlzLl9wcnVuZVRpbGVzKCk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEZsYWcgdG8gcHJldmVudCBfdXBkYXRlT3BhY2l0eSBmcm9tIHBydW5pbmcgdGlsZXMgZHVyaW5nXG5cdFx0XHQvLyBhIHpvb20gYW5pbSBvciBhIHBpbmNoIGdlc3R1cmVcblx0XHRcdHRoaXMuX25vUHJ1bmUgPSAhIW5vUHJ1bmU7XG5cdFx0fVxuXG5cdFx0dGhpcy5fc2V0Wm9vbVRyYW5zZm9ybXMoY2VudGVyLCB6b29tKTtcblx0fSxcblxuXHRfc2V0Wm9vbVRyYW5zZm9ybXM6IGZ1bmN0aW9uIChjZW50ZXIsIHpvb20pIHtcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xldmVscykge1xuXHRcdFx0dGhpcy5fc2V0Wm9vbVRyYW5zZm9ybSh0aGlzLl9sZXZlbHNbaV0sIGNlbnRlciwgem9vbSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRab29tVHJhbnNmb3JtOiBmdW5jdGlvbiAobGV2ZWwsIGNlbnRlciwgem9vbSkge1xuXHRcdHZhciBzY2FsZSA9IHRoaXMuX21hcC5nZXRab29tU2NhbGUoem9vbSwgbGV2ZWwuem9vbSksXG5cdFx0ICAgIHRyYW5zbGF0ZSA9IGxldmVsLm9yaWdpbi5tdWx0aXBseUJ5KHNjYWxlKVxuXHRcdCAgICAgICAgLnN1YnRyYWN0KHRoaXMuX21hcC5fZ2V0TmV3UGl4ZWxPcmlnaW4oY2VudGVyLCB6b29tKSkucm91bmQoKTtcblxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0obGV2ZWwuZWwsIHRyYW5zbGF0ZSwgc2NhbGUpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24obGV2ZWwuZWwsIHRyYW5zbGF0ZSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXNldEdyaWQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBjcnMgPSBtYXAub3B0aW9ucy5jcnMsXG5cdFx0ICAgIHRpbGVTaXplID0gdGhpcy5fdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCksXG5cdFx0ICAgIHRpbGVab29tID0gdGhpcy5fdGlsZVpvb207XG5cblx0XHR2YXIgYm91bmRzID0gdGhpcy5fbWFwLmdldFBpeGVsV29ybGRCb3VuZHModGhpcy5fdGlsZVpvb20pO1xuXHRcdGlmIChib3VuZHMpIHtcblx0XHRcdHRoaXMuX2dsb2JhbFRpbGVSYW5nZSA9IHRoaXMuX3B4Qm91bmRzVG9UaWxlUmFuZ2UoYm91bmRzKTtcblx0XHR9XG5cblx0XHR0aGlzLl93cmFwWCA9IGNycy53cmFwTG5nICYmICF0aGlzLm9wdGlvbnMubm9XcmFwICYmIFtcblx0XHRcdE1hdGguZmxvb3IobWFwLnByb2plY3QoWzAsIGNycy53cmFwTG5nWzBdXSwgdGlsZVpvb20pLnggLyB0aWxlU2l6ZS54KSxcblx0XHRcdE1hdGguY2VpbChtYXAucHJvamVjdChbMCwgY3JzLndyYXBMbmdbMV1dLCB0aWxlWm9vbSkueCAvIHRpbGVTaXplLnkpXG5cdFx0XTtcblx0XHR0aGlzLl93cmFwWSA9IGNycy53cmFwTGF0ICYmICF0aGlzLm9wdGlvbnMubm9XcmFwICYmIFtcblx0XHRcdE1hdGguZmxvb3IobWFwLnByb2plY3QoW2Nycy53cmFwTGF0WzBdLCAwXSwgdGlsZVpvb20pLnkgLyB0aWxlU2l6ZS54KSxcblx0XHRcdE1hdGguY2VpbChtYXAucHJvamVjdChbY3JzLndyYXBMYXRbMV0sIDBdLCB0aWxlWm9vbSkueSAvIHRpbGVTaXplLnkpXG5cdFx0XTtcblx0fSxcblxuXHRfb25Nb3ZlRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXAgfHwgdGhpcy5fbWFwLl9hbmltYXRpbmdab29tKSB7IHJldHVybjsgfVxuXG5cdFx0dGhpcy5fdXBkYXRlKCk7XG5cdH0sXG5cblx0X2dldFRpbGVkUGl4ZWxCb3VuZHM6IGZ1bmN0aW9uIChjZW50ZXIpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBtYXBab29tID0gbWFwLl9hbmltYXRpbmdab29tID8gTWF0aC5tYXgobWFwLl9hbmltYXRlVG9ab29tLCBtYXAuZ2V0Wm9vbSgpKSA6IG1hcC5nZXRab29tKCksXG5cdFx0ICAgIHNjYWxlID0gbWFwLmdldFpvb21TY2FsZShtYXBab29tLCB0aGlzLl90aWxlWm9vbSksXG5cdFx0ICAgIHBpeGVsQ2VudGVyID0gbWFwLnByb2plY3QoY2VudGVyLCB0aGlzLl90aWxlWm9vbSkuZmxvb3IoKSxcblx0XHQgICAgaGFsZlNpemUgPSBtYXAuZ2V0U2l6ZSgpLmRpdmlkZUJ5KHNjYWxlICogMik7XG5cblx0XHRyZXR1cm4gbmV3IEwuQm91bmRzKHBpeGVsQ2VudGVyLnN1YnRyYWN0KGhhbGZTaXplKSwgcGl4ZWxDZW50ZXIuYWRkKGhhbGZTaXplKSk7XG5cdH0sXG5cblx0Ly8gUHJpdmF0ZSBtZXRob2QgdG8gbG9hZCB0aWxlcyBpbiB0aGUgZ3JpZCdzIGFjdGl2ZSB6b29tIGxldmVsIGFjY29yZGluZyB0byBtYXAgYm91bmRzXG5cdF91cGRhdGU6IGZ1bmN0aW9uIChjZW50ZXIpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXHRcdGlmICghbWFwKSB7IHJldHVybjsgfVxuXHRcdHZhciB6b29tID0gbWFwLmdldFpvb20oKTtcblxuXHRcdGlmIChjZW50ZXIgPT09IHVuZGVmaW5lZCkgeyBjZW50ZXIgPSBtYXAuZ2V0Q2VudGVyKCk7IH1cblx0XHRpZiAodGhpcy5fdGlsZVpvb20gPT09IHVuZGVmaW5lZCkgeyByZXR1cm47IH1cdC8vIGlmIG91dCBvZiBtaW56b29tL21heHpvb21cblxuXHRcdHZhciBwaXhlbEJvdW5kcyA9IHRoaXMuX2dldFRpbGVkUGl4ZWxCb3VuZHMoY2VudGVyKSxcblx0XHQgICAgdGlsZVJhbmdlID0gdGhpcy5fcHhCb3VuZHNUb1RpbGVSYW5nZShwaXhlbEJvdW5kcyksXG5cdFx0ICAgIHRpbGVDZW50ZXIgPSB0aWxlUmFuZ2UuZ2V0Q2VudGVyKCksXG5cdFx0ICAgIHF1ZXVlID0gW10sXG5cdFx0ICAgIG1hcmdpbiA9IHRoaXMub3B0aW9ucy5rZWVwQnVmZmVyLFxuXHRcdCAgICBub1BydW5lUmFuZ2UgPSBuZXcgTC5Cb3VuZHModGlsZVJhbmdlLmdldEJvdHRvbUxlZnQoKS5zdWJ0cmFjdChbbWFyZ2luLCAtbWFyZ2luXSksXG5cdFx0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGlsZVJhbmdlLmdldFRvcFJpZ2h0KCkuYWRkKFttYXJnaW4sIC1tYXJnaW5dKSk7XG5cblx0XHRmb3IgKHZhciBrZXkgaW4gdGhpcy5fdGlsZXMpIHtcblx0XHRcdHZhciBjID0gdGhpcy5fdGlsZXNba2V5XS5jb29yZHM7XG5cdFx0XHRpZiAoYy56ICE9PSB0aGlzLl90aWxlWm9vbSB8fCAhbm9QcnVuZVJhbmdlLmNvbnRhaW5zKEwucG9pbnQoYy54LCBjLnkpKSkge1xuXHRcdFx0XHR0aGlzLl90aWxlc1trZXldLmN1cnJlbnQgPSBmYWxzZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBfdXBkYXRlIGp1c3QgbG9hZHMgbW9yZSB0aWxlcy4gSWYgdGhlIHRpbGUgem9vbSBsZXZlbCBkaWZmZXJzIHRvbyBtdWNoXG5cdFx0Ly8gZnJvbSB0aGUgbWFwJ3MsIGxldCBfc2V0VmlldyByZXNldCBsZXZlbHMgYW5kIHBydW5lIG9sZCB0aWxlcy5cblx0XHRpZiAoTWF0aC5hYnMoem9vbSAtIHRoaXMuX3RpbGVab29tKSA+IDEpIHsgdGhpcy5fc2V0VmlldyhjZW50ZXIsIHpvb20pOyByZXR1cm47IH1cblxuXHRcdC8vIGNyZWF0ZSBhIHF1ZXVlIG9mIGNvb3JkaW5hdGVzIHRvIGxvYWQgdGlsZXMgZnJvbVxuXHRcdGZvciAodmFyIGogPSB0aWxlUmFuZ2UubWluLnk7IGogPD0gdGlsZVJhbmdlLm1heC55OyBqKyspIHtcblx0XHRcdGZvciAodmFyIGkgPSB0aWxlUmFuZ2UubWluLng7IGkgPD0gdGlsZVJhbmdlLm1heC54OyBpKyspIHtcblx0XHRcdFx0dmFyIGNvb3JkcyA9IG5ldyBMLlBvaW50KGksIGopO1xuXHRcdFx0XHRjb29yZHMueiA9IHRoaXMuX3RpbGVab29tO1xuXG5cdFx0XHRcdGlmICghdGhpcy5faXNWYWxpZFRpbGUoY29vcmRzKSkgeyBjb250aW51ZTsgfVxuXG5cdFx0XHRcdHZhciB0aWxlID0gdGhpcy5fdGlsZXNbdGhpcy5fdGlsZUNvb3Jkc1RvS2V5KGNvb3JkcyldO1xuXHRcdFx0XHRpZiAodGlsZSkge1xuXHRcdFx0XHRcdHRpbGUuY3VycmVudCA9IHRydWU7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0cXVldWUucHVzaChjb29yZHMpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gc29ydCB0aWxlIHF1ZXVlIHRvIGxvYWQgdGlsZXMgaW4gb3JkZXIgb2YgdGhlaXIgZGlzdGFuY2UgdG8gY2VudGVyXG5cdFx0cXVldWUuc29ydChmdW5jdGlvbiAoYSwgYikge1xuXHRcdFx0cmV0dXJuIGEuZGlzdGFuY2VUbyh0aWxlQ2VudGVyKSAtIGIuZGlzdGFuY2VUbyh0aWxlQ2VudGVyKTtcblx0XHR9KTtcblxuXHRcdGlmIChxdWV1ZS5sZW5ndGggIT09IDApIHtcblx0XHRcdC8vIGlmIGl0J3MgdGhlIGZpcnN0IGJhdGNoIG9mIHRpbGVzIHRvIGxvYWRcblx0XHRcdGlmICghdGhpcy5fbG9hZGluZykge1xuXHRcdFx0XHR0aGlzLl9sb2FkaW5nID0gdHJ1ZTtcblx0XHRcdFx0Ly8gQGV2ZW50IGxvYWRpbmc6IEV2ZW50XG5cdFx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIGdyaWQgbGF5ZXIgc3RhcnRzIGxvYWRpbmcgdGlsZXMuXG5cdFx0XHRcdHRoaXMuZmlyZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBjcmVhdGUgRE9NIGZyYWdtZW50IHRvIGFwcGVuZCB0aWxlcyBpbiBvbmUgYmF0Y2hcblx0XHRcdHZhciBmcmFnbWVudCA9IGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtcblxuXHRcdFx0Zm9yIChpID0gMDsgaSA8IHF1ZXVlLmxlbmd0aDsgaSsrKSB7XG5cdFx0XHRcdHRoaXMuX2FkZFRpbGUocXVldWVbaV0sIGZyYWdtZW50KTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fbGV2ZWwuZWwuYXBwZW5kQ2hpbGQoZnJhZ21lbnQpO1xuXHRcdH1cblx0fSxcblxuXHRfaXNWYWxpZFRpbGU6IGZ1bmN0aW9uIChjb29yZHMpIHtcblx0XHR2YXIgY3JzID0gdGhpcy5fbWFwLm9wdGlvbnMuY3JzO1xuXG5cdFx0aWYgKCFjcnMuaW5maW5pdGUpIHtcblx0XHRcdC8vIGRvbid0IGxvYWQgdGlsZSBpZiBpdCdzIG91dCBvZiBib3VuZHMgYW5kIG5vdCB3cmFwcGVkXG5cdFx0XHR2YXIgYm91bmRzID0gdGhpcy5fZ2xvYmFsVGlsZVJhbmdlO1xuXHRcdFx0aWYgKCghY3JzLndyYXBMbmcgJiYgKGNvb3Jkcy54IDwgYm91bmRzLm1pbi54IHx8IGNvb3Jkcy54ID4gYm91bmRzLm1heC54KSkgfHxcblx0XHRcdCAgICAoIWNycy53cmFwTGF0ICYmIChjb29yZHMueSA8IGJvdW5kcy5taW4ueSB8fCBjb29yZHMueSA+IGJvdW5kcy5tYXgueSkpKSB7IHJldHVybiBmYWxzZTsgfVxuXHRcdH1cblxuXHRcdGlmICghdGhpcy5vcHRpb25zLmJvdW5kcykgeyByZXR1cm4gdHJ1ZTsgfVxuXG5cdFx0Ly8gZG9uJ3QgbG9hZCB0aWxlIGlmIGl0IGRvZXNuJ3QgaW50ZXJzZWN0IHRoZSBib3VuZHMgaW4gb3B0aW9uc1xuXHRcdHZhciB0aWxlQm91bmRzID0gdGhpcy5fdGlsZUNvb3Jkc1RvQm91bmRzKGNvb3Jkcyk7XG5cdFx0cmV0dXJuIEwubGF0TG5nQm91bmRzKHRoaXMub3B0aW9ucy5ib3VuZHMpLm92ZXJsYXBzKHRpbGVCb3VuZHMpO1xuXHR9LFxuXG5cdF9rZXlUb0JvdW5kczogZnVuY3Rpb24gKGtleSkge1xuXHRcdHJldHVybiB0aGlzLl90aWxlQ29vcmRzVG9Cb3VuZHModGhpcy5fa2V5VG9UaWxlQ29vcmRzKGtleSkpO1xuXHR9LFxuXG5cdC8vIGNvbnZlcnRzIHRpbGUgY29vcmRpbmF0ZXMgdG8gaXRzIGdlb2dyYXBoaWNhbCBib3VuZHNcblx0X3RpbGVDb29yZHNUb0JvdW5kczogZnVuY3Rpb24gKGNvb3Jkcykge1xuXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgdGlsZVNpemUgPSB0aGlzLmdldFRpbGVTaXplKCksXG5cblx0XHQgICAgbndQb2ludCA9IGNvb3Jkcy5zY2FsZUJ5KHRpbGVTaXplKSxcblx0XHQgICAgc2VQb2ludCA9IG53UG9pbnQuYWRkKHRpbGVTaXplKSxcblxuXHRcdCAgICBudyA9IG1hcC51bnByb2plY3QobndQb2ludCwgY29vcmRzLnopLFxuXHRcdCAgICBzZSA9IG1hcC51bnByb2plY3Qoc2VQb2ludCwgY29vcmRzLnopLFxuXHRcdCAgICBib3VuZHMgPSBuZXcgTC5MYXRMbmdCb3VuZHMobncsIHNlKTtcblxuXHRcdGlmICghdGhpcy5vcHRpb25zLm5vV3JhcCkge1xuXHRcdFx0bWFwLndyYXBMYXRMbmdCb3VuZHMoYm91bmRzKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gYm91bmRzO1xuXHR9LFxuXG5cdC8vIGNvbnZlcnRzIHRpbGUgY29vcmRpbmF0ZXMgdG8ga2V5IGZvciB0aGUgdGlsZSBjYWNoZVxuXHRfdGlsZUNvb3Jkc1RvS2V5OiBmdW5jdGlvbiAoY29vcmRzKSB7XG5cdFx0cmV0dXJuIGNvb3Jkcy54ICsgJzonICsgY29vcmRzLnkgKyAnOicgKyBjb29yZHMuejtcblx0fSxcblxuXHQvLyBjb252ZXJ0cyB0aWxlIGNhY2hlIGtleSB0byBjb29yZGluYXRlc1xuXHRfa2V5VG9UaWxlQ29vcmRzOiBmdW5jdGlvbiAoa2V5KSB7XG5cdFx0dmFyIGsgPSBrZXkuc3BsaXQoJzonKSxcblx0XHQgICAgY29vcmRzID0gbmV3IEwuUG9pbnQoK2tbMF0sICtrWzFdKTtcblx0XHRjb29yZHMueiA9ICtrWzJdO1xuXHRcdHJldHVybiBjb29yZHM7XG5cdH0sXG5cblx0X3JlbW92ZVRpbGU6IGZ1bmN0aW9uIChrZXkpIHtcblx0XHR2YXIgdGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0aWYgKCF0aWxlKSB7IHJldHVybjsgfVxuXG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aWxlLmVsKTtcblxuXHRcdGRlbGV0ZSB0aGlzLl90aWxlc1trZXldO1xuXG5cdFx0Ly8gQGV2ZW50IHRpbGV1bmxvYWQ6IFRpbGVFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gYSB0aWxlIGlzIHJlbW92ZWQgKGUuZy4gd2hlbiBhIHRpbGUgZ29lcyBvZmYgdGhlIHNjcmVlbikuXG5cdFx0dGhpcy5maXJlKCd0aWxldW5sb2FkJywge1xuXHRcdFx0dGlsZTogdGlsZS5lbCxcblx0XHRcdGNvb3JkczogdGhpcy5fa2V5VG9UaWxlQ29vcmRzKGtleSlcblx0XHR9KTtcblx0fSxcblxuXHRfaW5pdFRpbGU6IGZ1bmN0aW9uICh0aWxlKSB7XG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRpbGUsICdsZWFmbGV0LXRpbGUnKTtcblxuXHRcdHZhciB0aWxlU2l6ZSA9IHRoaXMuZ2V0VGlsZVNpemUoKTtcblx0XHR0aWxlLnN0eWxlLndpZHRoID0gdGlsZVNpemUueCArICdweCc7XG5cdFx0dGlsZS5zdHlsZS5oZWlnaHQgPSB0aWxlU2l6ZS55ICsgJ3B4JztcblxuXHRcdHRpbGUub25zZWxlY3RzdGFydCA9IEwuVXRpbC5mYWxzZUZuO1xuXHRcdHRpbGUub25tb3VzZW1vdmUgPSBMLlV0aWwuZmFsc2VGbjtcblxuXHRcdC8vIHVwZGF0ZSBvcGFjaXR5IG9uIHRpbGVzIGluIElFNy04IGJlY2F1c2Ugb2YgZmlsdGVyIGluaGVyaXRhbmNlIHByb2JsZW1zXG5cdFx0aWYgKEwuQnJvd3Nlci5pZWx0OSAmJiB0aGlzLm9wdGlvbnMub3BhY2l0eSA8IDEpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRpbGUsIHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcblx0XHR9XG5cblx0XHQvLyB3aXRob3V0IHRoaXMgaGFjaywgdGlsZXMgZGlzYXBwZWFyIGFmdGVyIHpvb20gb24gQ2hyb21lIGZvciBBbmRyb2lkXG5cdFx0Ly8gaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMjA3OFxuXHRcdGlmIChMLkJyb3dzZXIuYW5kcm9pZCAmJiAhTC5Ccm93c2VyLmFuZHJvaWQyMykge1xuXHRcdFx0dGlsZS5zdHlsZS5XZWJraXRCYWNrZmFjZVZpc2liaWxpdHkgPSAnaGlkZGVuJztcblx0XHR9XG5cdH0sXG5cblx0X2FkZFRpbGU6IGZ1bmN0aW9uIChjb29yZHMsIGNvbnRhaW5lcikge1xuXHRcdHZhciB0aWxlUG9zID0gdGhpcy5fZ2V0VGlsZVBvcyhjb29yZHMpLFxuXHRcdCAgICBrZXkgPSB0aGlzLl90aWxlQ29vcmRzVG9LZXkoY29vcmRzKTtcblxuXHRcdHZhciB0aWxlID0gdGhpcy5jcmVhdGVUaWxlKHRoaXMuX3dyYXBDb29yZHMoY29vcmRzKSwgTC5iaW5kKHRoaXMuX3RpbGVSZWFkeSwgdGhpcywgY29vcmRzKSk7XG5cblx0XHR0aGlzLl9pbml0VGlsZSh0aWxlKTtcblxuXHRcdC8vIGlmIGNyZWF0ZVRpbGUgaXMgZGVmaW5lZCB3aXRoIGEgc2Vjb25kIGFyZ3VtZW50IChcImRvbmVcIiBjYWxsYmFjayksXG5cdFx0Ly8gd2Uga25vdyB0aGF0IHRpbGUgaXMgYXN5bmMgYW5kIHdpbGwgYmUgcmVhZHkgbGF0ZXI7IG90aGVyd2lzZVxuXHRcdGlmICh0aGlzLmNyZWF0ZVRpbGUubGVuZ3RoIDwgMikge1xuXHRcdFx0Ly8gbWFyayB0aWxlIGFzIHJlYWR5LCBidXQgZGVsYXkgb25lIGZyYW1lIGZvciBvcGFjaXR5IGFuaW1hdGlvbiB0byBoYXBwZW5cblx0XHRcdEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKEwuYmluZCh0aGlzLl90aWxlUmVhZHksIHRoaXMsIGNvb3JkcywgbnVsbCwgdGlsZSkpO1xuXHRcdH1cblxuXHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aWxlLCB0aWxlUG9zKTtcblxuXHRcdC8vIHNhdmUgdGlsZSBpbiBjYWNoZVxuXHRcdHRoaXMuX3RpbGVzW2tleV0gPSB7XG5cdFx0XHRlbDogdGlsZSxcblx0XHRcdGNvb3JkczogY29vcmRzLFxuXHRcdFx0Y3VycmVudDogdHJ1ZVxuXHRcdH07XG5cblx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQodGlsZSk7XG5cdFx0Ly8gQGV2ZW50IHRpbGVsb2Fkc3RhcnQ6IFRpbGVFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gYSB0aWxlIGlzIHJlcXVlc3RlZCBhbmQgc3RhcnRzIGxvYWRpbmcuXG5cdFx0dGhpcy5maXJlKCd0aWxlbG9hZHN0YXJ0Jywge1xuXHRcdFx0dGlsZTogdGlsZSxcblx0XHRcdGNvb3JkczogY29vcmRzXG5cdFx0fSk7XG5cdH0sXG5cblx0X3RpbGVSZWFkeTogZnVuY3Rpb24gKGNvb3JkcywgZXJyLCB0aWxlKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XG5cblx0XHRpZiAoZXJyKSB7XG5cdFx0XHQvLyBAZXZlbnQgdGlsZWVycm9yOiBUaWxlRXJyb3JFdmVudFxuXHRcdFx0Ly8gRmlyZWQgd2hlbiB0aGVyZSBpcyBhbiBlcnJvciBsb2FkaW5nIGEgdGlsZS5cblx0XHRcdHRoaXMuZmlyZSgndGlsZWVycm9yJywge1xuXHRcdFx0XHRlcnJvcjogZXJyLFxuXHRcdFx0XHR0aWxlOiB0aWxlLFxuXHRcdFx0XHRjb29yZHM6IGNvb3Jkc1xuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0dmFyIGtleSA9IHRoaXMuX3RpbGVDb29yZHNUb0tleShjb29yZHMpO1xuXG5cdFx0dGlsZSA9IHRoaXMuX3RpbGVzW2tleV07XG5cdFx0aWYgKCF0aWxlKSB7IHJldHVybjsgfVxuXG5cdFx0dGlsZS5sb2FkZWQgPSArbmV3IERhdGUoKTtcblx0XHRpZiAodGhpcy5fbWFwLl9mYWRlQW5pbWF0ZWQpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRpbGUuZWwsIDApO1xuXHRcdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9mYWRlRnJhbWUpO1xuXHRcdFx0dGhpcy5fZmFkZUZyYW1lID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fdXBkYXRlT3BhY2l0eSwgdGhpcyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRpbGUuYWN0aXZlID0gdHJ1ZTtcblx0XHRcdHRoaXMuX3BydW5lVGlsZXMoKTtcblx0XHR9XG5cblx0XHRpZiAoIWVycikge1xuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRpbGUuZWwsICdsZWFmbGV0LXRpbGUtbG9hZGVkJyk7XG5cblx0XHRcdC8vIEBldmVudCB0aWxlbG9hZDogVGlsZUV2ZW50XG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgdGlsZSBsb2Fkcy5cblx0XHRcdHRoaXMuZmlyZSgndGlsZWxvYWQnLCB7XG5cdFx0XHRcdHRpbGU6IHRpbGUuZWwsXG5cdFx0XHRcdGNvb3JkczogY29vcmRzXG5cdFx0XHR9KTtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5fbm9UaWxlc1RvTG9hZCgpKSB7XG5cdFx0XHR0aGlzLl9sb2FkaW5nID0gZmFsc2U7XG5cdFx0XHQvLyBAZXZlbnQgbG9hZDogRXZlbnRcblx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIGdyaWQgbGF5ZXIgbG9hZGVkIGFsbCB2aXNpYmxlIHRpbGVzLlxuXHRcdFx0dGhpcy5maXJlKCdsb2FkJyk7XG5cblx0XHRcdGlmIChMLkJyb3dzZXIuaWVsdDkgfHwgIXRoaXMuX21hcC5fZmFkZUFuaW1hdGVkKSB7XG5cdFx0XHRcdEwuVXRpbC5yZXF1ZXN0QW5pbUZyYW1lKHRoaXMuX3BydW5lVGlsZXMsIHRoaXMpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Ly8gV2FpdCBhIGJpdCBtb3JlIHRoYW4gMC4yIHNlY3MgKHRoZSBkdXJhdGlvbiBvZiB0aGUgdGlsZSBmYWRlLWluKVxuXHRcdFx0XHQvLyB0byB0cmlnZ2VyIGEgcHJ1bmluZy5cblx0XHRcdFx0c2V0VGltZW91dChMLmJpbmQodGhpcy5fcHJ1bmVUaWxlcywgdGhpcyksIDI1MCk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9nZXRUaWxlUG9zOiBmdW5jdGlvbiAoY29vcmRzKSB7XG5cdFx0cmV0dXJuIGNvb3Jkcy5zY2FsZUJ5KHRoaXMuZ2V0VGlsZVNpemUoKSkuc3VidHJhY3QodGhpcy5fbGV2ZWwub3JpZ2luKTtcblx0fSxcblxuXHRfd3JhcENvb3JkczogZnVuY3Rpb24gKGNvb3Jkcykge1xuXHRcdHZhciBuZXdDb29yZHMgPSBuZXcgTC5Qb2ludChcblx0XHRcdHRoaXMuX3dyYXBYID8gTC5VdGlsLndyYXBOdW0oY29vcmRzLngsIHRoaXMuX3dyYXBYKSA6IGNvb3Jkcy54LFxuXHRcdFx0dGhpcy5fd3JhcFkgPyBMLlV0aWwud3JhcE51bShjb29yZHMueSwgdGhpcy5fd3JhcFkpIDogY29vcmRzLnkpO1xuXHRcdG5ld0Nvb3Jkcy56ID0gY29vcmRzLno7XG5cdFx0cmV0dXJuIG5ld0Nvb3Jkcztcblx0fSxcblxuXHRfcHhCb3VuZHNUb1RpbGVSYW5nZTogZnVuY3Rpb24gKGJvdW5kcykge1xuXHRcdHZhciB0aWxlU2l6ZSA9IHRoaXMuZ2V0VGlsZVNpemUoKTtcblx0XHRyZXR1cm4gbmV3IEwuQm91bmRzKFxuXHRcdFx0Ym91bmRzLm1pbi51bnNjYWxlQnkodGlsZVNpemUpLmZsb29yKCksXG5cdFx0XHRib3VuZHMubWF4LnVuc2NhbGVCeSh0aWxlU2l6ZSkuY2VpbCgpLnN1YnRyYWN0KFsxLCAxXSkpO1xuXHR9LFxuXG5cdF9ub1RpbGVzVG9Mb2FkOiBmdW5jdGlvbiAoKSB7XG5cdFx0Zm9yICh2YXIga2V5IGluIHRoaXMuX3RpbGVzKSB7XG5cdFx0XHRpZiAoIXRoaXMuX3RpbGVzW2tleV0ubG9hZGVkKSB7IHJldHVybiBmYWxzZTsgfVxuXHRcdH1cblx0XHRyZXR1cm4gdHJ1ZTtcblx0fVxufSk7XG5cbi8vIEBmYWN0b3J5IEwuZ3JpZExheWVyKG9wdGlvbnM/OiBHcmlkTGF5ZXIgb3B0aW9ucylcbi8vIENyZWF0ZXMgYSBuZXcgaW5zdGFuY2Ugb2YgR3JpZExheWVyIHdpdGggdGhlIHN1cHBsaWVkIG9wdGlvbnMuXG5MLmdyaWRMYXllciA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5HcmlkTGF5ZXIob3B0aW9ucyk7XG59O1xuXG5cblxuLypcclxuICogQGNsYXNzIFRpbGVMYXllclxyXG4gKiBAaW5oZXJpdHMgR3JpZExheWVyXHJcbiAqIEBha2EgTC5UaWxlTGF5ZXJcclxuICogVXNlZCB0byBsb2FkIGFuZCBkaXNwbGF5IHRpbGUgbGF5ZXJzIG9uIHRoZSBtYXAuIEV4dGVuZHMgYEdyaWRMYXllcmAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIEwudGlsZUxheWVyKCdodHRwOi8ve3N9LnRpbGUub3BlbnN0cmVldG1hcC5vcmcve3p9L3t4fS97eX0ucG5nP3tmb299Jywge2ZvbzogJ2Jhcid9KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICpcclxuICogQHNlY3Rpb24gVVJMIHRlbXBsYXRlXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIEEgc3RyaW5nIG9mIHRoZSBmb2xsb3dpbmcgZm9ybTpcclxuICpcclxuICogYGBgXHJcbiAqICdodHRwOi8ve3N9LnNvbWVkb21haW4uY29tL2JsYWJsYS97en0ve3h9L3t5fXtyfS5wbmcnXHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBge3N9YCBtZWFucyBvbmUgb2YgdGhlIGF2YWlsYWJsZSBzdWJkb21haW5zICh1c2VkIHNlcXVlbnRpYWxseSB0byBoZWxwIHdpdGggYnJvd3NlciBwYXJhbGxlbCByZXF1ZXN0cyBwZXIgZG9tYWluIGxpbWl0YXRpb247IHN1YmRvbWFpbiB2YWx1ZXMgYXJlIHNwZWNpZmllZCBpbiBvcHRpb25zOyBgYWAsIGBiYCBvciBgY2AgYnkgZGVmYXVsdCwgY2FuIGJlIG9taXR0ZWQpLCBge3p9YCDigJQgem9vbSBsZXZlbCwgYHt4fWAgYW5kIGB7eX1gIOKAlCB0aWxlIGNvb3JkaW5hdGVzLiBge3J9YCBjYW4gYmUgdXNlZCB0byBhZGQgQDJ4IHRvIHRoZSBVUkwgdG8gbG9hZCByZXRpbmEgdGlsZXMuXHJcbiAqXHJcbiAqIFlvdSBjYW4gdXNlIGN1c3RvbSBrZXlzIGluIHRoZSB0ZW1wbGF0ZSwgd2hpY2ggd2lsbCBiZSBbZXZhbHVhdGVkXSgjdXRpbC10ZW1wbGF0ZSkgZnJvbSBUaWxlTGF5ZXIgb3B0aW9ucywgbGlrZSB0aGlzOlxyXG4gKlxyXG4gKiBgYGBcclxuICogTC50aWxlTGF5ZXIoJ2h0dHA6Ly97c30uc29tZWRvbWFpbi5jb20ve2Zvb30ve3p9L3t4fS97eX0ucG5nJywge2ZvbzogJ2Jhcid9KTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuXHJcbkwuVGlsZUxheWVyID0gTC5HcmlkTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIFRpbGVMYXllciBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBtaW5ab29tOiBOdW1iZXIgPSAwXHJcblx0XHQvLyBNaW5pbXVtIHpvb20gbnVtYmVyLlxyXG5cdFx0bWluWm9vbTogMCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1heFpvb206IE51bWJlciA9IDE4XHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbnVtYmVyLlxyXG5cdFx0bWF4Wm9vbTogMTgsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhOYXRpdmVab29tOiBOdW1iZXIgPSBudWxsXHJcblx0XHQvLyBNYXhpbXVtIHpvb20gbnVtYmVyIHRoZSB0aWxlIHNvdXJjZSBoYXMgYXZhaWxhYmxlLiBJZiBpdCBpcyBzcGVjaWZpZWQsXHJcblx0XHQvLyB0aGUgdGlsZXMgb24gYWxsIHpvb20gbGV2ZWxzIGhpZ2hlciB0aGFuIGBtYXhOYXRpdmVab29tYCB3aWxsIGJlIGxvYWRlZFxyXG5cdFx0Ly8gZnJvbSBgbWF4TmF0aXZlWm9vbWAgbGV2ZWwgYW5kIGF1dG8tc2NhbGVkLlxyXG5cdFx0bWF4TmF0aXZlWm9vbTogbnVsbCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIG1pbk5hdGl2ZVpvb206IE51bWJlciA9IG51bGxcclxuXHRcdC8vIE1pbmltdW0gem9vbSBudW1iZXIgdGhlIHRpbGUgc291cmNlIGhhcyBhdmFpbGFibGUuIElmIGl0IGlzIHNwZWNpZmllZCxcclxuXHRcdC8vIHRoZSB0aWxlcyBvbiBhbGwgem9vbSBsZXZlbHMgbG93ZXIgdGhhbiBgbWluTmF0aXZlWm9vbWAgd2lsbCBiZSBsb2FkZWRcclxuXHRcdC8vIGZyb20gYG1pbk5hdGl2ZVpvb21gIGxldmVsIGFuZCBhdXRvLXNjYWxlZC5cclxuXHRcdG1pbk5hdGl2ZVpvb206IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBzdWJkb21haW5zOiBTdHJpbmd8U3RyaW5nW10gPSAnYWJjJ1xyXG5cdFx0Ly8gU3ViZG9tYWlucyBvZiB0aGUgdGlsZSBzZXJ2aWNlLiBDYW4gYmUgcGFzc2VkIGluIHRoZSBmb3JtIG9mIG9uZSBzdHJpbmcgKHdoZXJlIGVhY2ggbGV0dGVyIGlzIGEgc3ViZG9tYWluIG5hbWUpIG9yIGFuIGFycmF5IG9mIHN0cmluZ3MuXHJcblx0XHRzdWJkb21haW5zOiAnYWJjJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGVycm9yVGlsZVVybDogU3RyaW5nID0gJydcclxuXHRcdC8vIFVSTCB0byB0aGUgdGlsZSBpbWFnZSB0byBzaG93IGluIHBsYWNlIG9mIHRoZSB0aWxlIHRoYXQgZmFpbGVkIHRvIGxvYWQuXHJcblx0XHRlcnJvclRpbGVVcmw6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbU9mZnNldDogTnVtYmVyID0gMFxyXG5cdFx0Ly8gVGhlIHpvb20gbnVtYmVyIHVzZWQgaW4gdGlsZSBVUkxzIHdpbGwgYmUgb2Zmc2V0IHdpdGggdGhpcyB2YWx1ZS5cclxuXHRcdHpvb21PZmZzZXQ6IDAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0bXM6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCBpbnZlcnNlcyBZIGF4aXMgbnVtYmVyaW5nIGZvciB0aWxlcyAodHVybiB0aGlzIG9uIGZvciBbVE1TXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9UaWxlX01hcF9TZXJ2aWNlKSBzZXJ2aWNlcykuXHJcblx0XHR0bXM6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbVJldmVyc2U6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgc2V0IHRvIHRydWUsIHRoZSB6b29tIG51bWJlciB1c2VkIGluIHRpbGUgVVJMcyB3aWxsIGJlIHJldmVyc2VkIChgbWF4Wm9vbSAtIHpvb21gIGluc3RlYWQgb2YgYHpvb21gKVxyXG5cdFx0em9vbVJldmVyc2U6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gZGV0ZWN0UmV0aW5hOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCBhbmQgdXNlciBpcyBvbiBhIHJldGluYSBkaXNwbGF5LCBpdCB3aWxsIHJlcXVlc3QgZm91ciB0aWxlcyBvZiBoYWxmIHRoZSBzcGVjaWZpZWQgc2l6ZSBhbmQgYSBiaWdnZXIgem9vbSBsZXZlbCBpbiBwbGFjZSBvZiBvbmUgdG8gdXRpbGl6ZSB0aGUgaGlnaCByZXNvbHV0aW9uLlxyXG5cdFx0ZGV0ZWN0UmV0aW5hOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNyb3NzT3JpZ2luOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIHRydWUsIGFsbCB0aWxlcyB3aWxsIGhhdmUgdGhlaXIgY3Jvc3NPcmlnaW4gYXR0cmlidXRlIHNldCB0byAnJy4gVGhpcyBpcyBuZWVkZWQgaWYgeW91IHdhbnQgdG8gYWNjZXNzIHRpbGUgcGl4ZWwgZGF0YS5cclxuXHRcdGNyb3NzT3JpZ2luOiBmYWxzZVxyXG5cdH0sXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHJcblx0XHR0aGlzLl91cmwgPSB1cmw7XHJcblxyXG5cdFx0b3B0aW9ucyA9IEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHJcblx0XHQvLyBkZXRlY3RpbmcgcmV0aW5hIGRpc3BsYXlzLCBhZGp1c3RpbmcgdGlsZVNpemUgYW5kIHpvb20gbGV2ZWxzXHJcblx0XHRpZiAob3B0aW9ucy5kZXRlY3RSZXRpbmEgJiYgTC5Ccm93c2VyLnJldGluYSAmJiBvcHRpb25zLm1heFpvb20gPiAwKSB7XHJcblxyXG5cdFx0XHRvcHRpb25zLnRpbGVTaXplID0gTWF0aC5mbG9vcihvcHRpb25zLnRpbGVTaXplIC8gMik7XHJcblxyXG5cdFx0XHRpZiAoIW9wdGlvbnMuem9vbVJldmVyc2UpIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb21PZmZzZXQrKztcclxuXHRcdFx0XHRvcHRpb25zLm1heFpvb20tLTtcclxuXHRcdFx0fSBlbHNlIHtcclxuXHRcdFx0XHRvcHRpb25zLnpvb21PZmZzZXQtLTtcclxuXHRcdFx0XHRvcHRpb25zLm1pblpvb20rKztcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0b3B0aW9ucy5taW5ab29tID0gTWF0aC5tYXgoMCwgb3B0aW9ucy5taW5ab29tKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodHlwZW9mIG9wdGlvbnMuc3ViZG9tYWlucyA9PT0gJ3N0cmluZycpIHtcclxuXHRcdFx0b3B0aW9ucy5zdWJkb21haW5zID0gb3B0aW9ucy5zdWJkb21haW5zLnNwbGl0KCcnKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBmb3IgaHR0cHM6Ly9naXRodWIuY29tL0xlYWZsZXQvTGVhZmxldC9pc3N1ZXMvMTM3XHJcblx0XHRpZiAoIUwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdHRoaXMub24oJ3RpbGV1bmxvYWQnLCB0aGlzLl9vblRpbGVSZW1vdmUpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0VXJsKHVybDogU3RyaW5nLCBub1JlZHJhdz86IEJvb2xlYW4pOiB0aGlzXHJcblx0Ly8gVXBkYXRlcyB0aGUgbGF5ZXIncyBVUkwgdGVtcGxhdGUgYW5kIHJlZHJhd3MgaXQgKHVubGVzcyBgbm9SZWRyYXdgIGlzIHNldCB0byBgdHJ1ZWApLlxyXG5cdHNldFVybDogZnVuY3Rpb24gKHVybCwgbm9SZWRyYXcpIHtcclxuXHRcdHRoaXMuX3VybCA9IHVybDtcclxuXHJcblx0XHRpZiAoIW5vUmVkcmF3KSB7XHJcblx0XHRcdHRoaXMucmVkcmF3KCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGNyZWF0ZVRpbGUoY29vcmRzOiBPYmplY3QsIGRvbmU/OiBGdW5jdGlvbik6IEhUTUxFbGVtZW50XHJcblx0Ly8gQ2FsbGVkIG9ubHkgaW50ZXJuYWxseSwgb3ZlcnJpZGVzIEdyaWRMYXllcidzIFtgY3JlYXRlVGlsZSgpYF0oI2dyaWRsYXllci1jcmVhdGV0aWxlKVxyXG5cdC8vIHRvIHJldHVybiBhbiBgPGltZz5gIEhUTUwgZWxlbWVudCB3aXRoIHRoZSBhcHByb3BpYXRlIGltYWdlIFVSTCBnaXZlbiBgY29vcmRzYC4gVGhlIGBkb25lYFxyXG5cdC8vIGNhbGxiYWNrIGlzIGNhbGxlZCB3aGVuIHRoZSB0aWxlIGhhcyBiZWVuIGxvYWRlZC5cclxuXHRjcmVhdGVUaWxlOiBmdW5jdGlvbiAoY29vcmRzLCBkb25lKSB7XHJcblx0XHR2YXIgdGlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnQub24odGlsZSwgJ2xvYWQnLCBMLmJpbmQodGhpcy5fdGlsZU9uTG9hZCwgdGhpcywgZG9uZSwgdGlsZSkpO1xyXG5cdFx0TC5Eb21FdmVudC5vbih0aWxlLCAnZXJyb3InLCBMLmJpbmQodGhpcy5fdGlsZU9uRXJyb3IsIHRoaXMsIGRvbmUsIHRpbGUpKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmNyb3NzT3JpZ2luKSB7XHJcblx0XHRcdHRpbGUuY3Jvc3NPcmlnaW4gPSAnJztcclxuXHRcdH1cclxuXHJcblx0XHQvKlxyXG5cdFx0IEFsdCB0YWcgaXMgc2V0IHRvIGVtcHR5IHN0cmluZyB0byBrZWVwIHNjcmVlbiByZWFkZXJzIGZyb20gcmVhZGluZyBVUkwgYW5kIGZvciBjb21wbGlhbmNlIHJlYXNvbnNcclxuXHRcdCBodHRwOi8vd3d3LnczLm9yZy9UUi9XQ0FHMjAtVEVDSFMvSDY3XHJcblx0XHQqL1xyXG5cdFx0dGlsZS5hbHQgPSAnJztcclxuXHJcblx0XHQvKlxyXG5cdFx0IFNldCByb2xlPVwicHJlc2VudGF0aW9uXCIgdG8gZm9yY2Ugc2NyZWVuIHJlYWRlcnMgdG8gaWdub3JlIHRoaXNcclxuXHRcdCBodHRwczovL3d3dy53My5vcmcvVFIvd2FpLWFyaWEvcm9sZXMjdGV4dGFsdGVybmF0aXZlY29tcHV0YXRpb25cclxuXHRcdCovXHJcblx0XHR0aWxlLnNldEF0dHJpYnV0ZSgncm9sZScsICdwcmVzZW50YXRpb24nKTtcclxuXHJcblx0XHR0aWxlLnNyYyA9IHRoaXMuZ2V0VGlsZVVybChjb29yZHMpO1xyXG5cclxuXHRcdHJldHVybiB0aWxlO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXHJcblx0Ly8gQHVuaW5oZXJpdGFibGVcclxuXHQvLyBMYXllcnMgZXh0ZW5kaW5nIGBUaWxlTGF5ZXJgIG1pZ2h0IHJlaW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgbWV0aG9kLlxyXG5cdC8vIEBtZXRob2QgZ2V0VGlsZVVybChjb29yZHM6IE9iamVjdCk6IFN0cmluZ1xyXG5cdC8vIENhbGxlZCBvbmx5IGludGVybmFsbHksIHJldHVybnMgdGhlIFVSTCBmb3IgYSB0aWxlIGdpdmVuIGl0cyBjb29yZGluYXRlcy5cclxuXHQvLyBDbGFzc2VzIGV4dGVuZGluZyBgVGlsZUxheWVyYCBjYW4gb3ZlcnJpZGUgdGhpcyBmdW5jdGlvbiB0byBwcm92aWRlIGN1c3RvbSB0aWxlIFVSTCBuYW1pbmcgc2NoZW1lcy5cclxuXHRnZXRUaWxlVXJsOiBmdW5jdGlvbiAoY29vcmRzKSB7XHJcblx0XHR2YXIgZGF0YSA9IHtcclxuXHRcdFx0cjogTC5Ccm93c2VyLnJldGluYSA/ICdAMngnIDogJycsXHJcblx0XHRcdHM6IHRoaXMuX2dldFN1YmRvbWFpbihjb29yZHMpLFxyXG5cdFx0XHR4OiBjb29yZHMueCxcclxuXHRcdFx0eTogY29vcmRzLnksXHJcblx0XHRcdHo6IHRoaXMuX2dldFpvb21Gb3JVcmwoKVxyXG5cdFx0fTtcclxuXHRcdGlmICh0aGlzLl9tYXAgJiYgIXRoaXMuX21hcC5vcHRpb25zLmNycy5pbmZpbml0ZSkge1xyXG5cdFx0XHR2YXIgaW52ZXJ0ZWRZID0gdGhpcy5fZ2xvYmFsVGlsZVJhbmdlLm1heC55IC0gY29vcmRzLnk7XHJcblx0XHRcdGlmICh0aGlzLm9wdGlvbnMudG1zKSB7XHJcblx0XHRcdFx0ZGF0YVsneSddID0gaW52ZXJ0ZWRZO1xyXG5cdFx0XHR9XHJcblx0XHRcdGRhdGFbJy15J10gPSBpbnZlcnRlZFk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIEwuVXRpbC50ZW1wbGF0ZSh0aGlzLl91cmwsIEwuZXh0ZW5kKGRhdGEsIHRoaXMub3B0aW9ucykpO1xyXG5cdH0sXHJcblxyXG5cdF90aWxlT25Mb2FkOiBmdW5jdGlvbiAoZG9uZSwgdGlsZSkge1xyXG5cdFx0Ly8gRm9yIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzMzMzJcclxuXHRcdGlmIChMLkJyb3dzZXIuaWVsdDkpIHtcclxuXHRcdFx0c2V0VGltZW91dChMLmJpbmQoZG9uZSwgdGhpcywgbnVsbCwgdGlsZSksIDApO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0ZG9uZShudWxsLCB0aWxlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfdGlsZU9uRXJyb3I6IGZ1bmN0aW9uIChkb25lLCB0aWxlLCBlKSB7XHJcblx0XHR2YXIgZXJyb3JVcmwgPSB0aGlzLm9wdGlvbnMuZXJyb3JUaWxlVXJsO1xyXG5cdFx0aWYgKGVycm9yVXJsICYmIHRpbGUuc3JjICE9PSBlcnJvclVybCkge1xyXG5cdFx0XHR0aWxlLnNyYyA9IGVycm9yVXJsO1xyXG5cdFx0fVxyXG5cdFx0ZG9uZShlLCB0aWxlKTtcclxuXHR9LFxyXG5cclxuXHRnZXRUaWxlU2l6ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcclxuXHRcdHRpbGVTaXplID0gTC5HcmlkTGF5ZXIucHJvdG90eXBlLmdldFRpbGVTaXplLmNhbGwodGhpcyksXHJcblx0XHR6b29tID0gdGhpcy5fdGlsZVpvb20gKyB0aGlzLm9wdGlvbnMuem9vbU9mZnNldCxcclxuXHRcdG1pbk5hdGl2ZVpvb20gPSB0aGlzLm9wdGlvbnMubWluTmF0aXZlWm9vbSxcclxuXHRcdG1heE5hdGl2ZVpvb20gPSB0aGlzLm9wdGlvbnMubWF4TmF0aXZlWm9vbTtcclxuXHJcblx0XHQvLyBkZWNyZWFzZSB0aWxlIHNpemUgd2hlbiBzY2FsaW5nIGJlbG93IG1pbk5hdGl2ZVpvb21cclxuXHRcdGlmIChtaW5OYXRpdmVab29tICE9PSBudWxsICYmIHpvb20gPCBtaW5OYXRpdmVab29tKSB7XHJcblx0XHRcdHJldHVybiB0aWxlU2l6ZS5kaXZpZGVCeShtYXAuZ2V0Wm9vbVNjYWxlKG1pbk5hdGl2ZVpvb20sIHpvb20pKS5yb3VuZCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIGluY3JlYXNlIHRpbGUgc2l6ZSB3aGVuIHNjYWxpbmcgYWJvdmUgbWF4TmF0aXZlWm9vbVxyXG5cdFx0aWYgKG1heE5hdGl2ZVpvb20gIT09IG51bGwgJiYgem9vbSA+IG1heE5hdGl2ZVpvb20pIHtcclxuXHRcdFx0cmV0dXJuIHRpbGVTaXplLmRpdmlkZUJ5KG1hcC5nZXRab29tU2NhbGUobWF4TmF0aXZlWm9vbSwgem9vbSkpLnJvdW5kKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRpbGVTaXplO1xyXG5cdH0sXHJcblxyXG5cdF9vblRpbGVSZW1vdmU6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRlLnRpbGUub25sb2FkID0gbnVsbDtcclxuXHR9LFxyXG5cclxuXHRfZ2V0Wm9vbUZvclVybDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIHpvb20gPSB0aGlzLl90aWxlWm9vbSxcclxuXHRcdG1heFpvb20gPSB0aGlzLm9wdGlvbnMubWF4Wm9vbSxcclxuXHRcdHpvb21SZXZlcnNlID0gdGhpcy5vcHRpb25zLnpvb21SZXZlcnNlLFxyXG5cdFx0em9vbU9mZnNldCA9IHRoaXMub3B0aW9ucy56b29tT2Zmc2V0LFxyXG5cdFx0bWluTmF0aXZlWm9vbSA9IHRoaXMub3B0aW9ucy5taW5OYXRpdmVab29tLFxyXG5cdFx0bWF4TmF0aXZlWm9vbSA9IHRoaXMub3B0aW9ucy5tYXhOYXRpdmVab29tO1xyXG5cclxuXHRcdGlmICh6b29tUmV2ZXJzZSkge1xyXG5cdFx0XHR6b29tID0gbWF4Wm9vbSAtIHpvb207XHJcblx0XHR9XHJcblxyXG5cdFx0em9vbSArPSB6b29tT2Zmc2V0O1xyXG5cclxuXHRcdGlmIChtaW5OYXRpdmVab29tICE9PSBudWxsICYmIHpvb20gPCBtaW5OYXRpdmVab29tKSB7XHJcblx0XHRcdHJldHVybiBtaW5OYXRpdmVab29tO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChtYXhOYXRpdmVab29tICE9PSBudWxsICYmIHpvb20gPiBtYXhOYXRpdmVab29tKSB7XHJcblx0XHRcdHJldHVybiBtYXhOYXRpdmVab29tO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB6b29tO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRTdWJkb21haW46IGZ1bmN0aW9uICh0aWxlUG9pbnQpIHtcclxuXHRcdHZhciBpbmRleCA9IE1hdGguYWJzKHRpbGVQb2ludC54ICsgdGlsZVBvaW50LnkpICUgdGhpcy5vcHRpb25zLnN1YmRvbWFpbnMubGVuZ3RoO1xyXG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5zdWJkb21haW5zW2luZGV4XTtcclxuXHR9LFxyXG5cclxuXHQvLyBzdG9wcyBsb2FkaW5nIGFsbCB0aWxlcyBpbiB0aGUgYmFja2dyb3VuZCBsYXllclxyXG5cdF9hYm9ydExvYWRpbmc6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBpLCB0aWxlO1xyXG5cdFx0Zm9yIChpIGluIHRoaXMuX3RpbGVzKSB7XHJcblx0XHRcdGlmICh0aGlzLl90aWxlc1tpXS5jb29yZHMueiAhPT0gdGhpcy5fdGlsZVpvb20pIHtcclxuXHRcdFx0XHR0aWxlID0gdGhpcy5fdGlsZXNbaV0uZWw7XHJcblxyXG5cdFx0XHRcdHRpbGUub25sb2FkID0gTC5VdGlsLmZhbHNlRm47XHJcblx0XHRcdFx0dGlsZS5vbmVycm9yID0gTC5VdGlsLmZhbHNlRm47XHJcblxyXG5cdFx0XHRcdGlmICghdGlsZS5jb21wbGV0ZSkge1xyXG5cdFx0XHRcdFx0dGlsZS5zcmMgPSBMLlV0aWwuZW1wdHlJbWFnZVVybDtcclxuXHRcdFx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGlsZSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcblxyXG4vLyBAZmFjdG9yeSBMLnRpbGVsYXllcih1cmxUZW1wbGF0ZTogU3RyaW5nLCBvcHRpb25zPzogVGlsZUxheWVyIG9wdGlvbnMpXHJcbi8vIEluc3RhbnRpYXRlcyBhIHRpbGUgbGF5ZXIgb2JqZWN0IGdpdmVuIGEgYFVSTCB0ZW1wbGF0ZWAgYW5kIG9wdGlvbmFsbHkgYW4gb3B0aW9ucyBvYmplY3QuXHJcblxyXG5MLnRpbGVMYXllciA9IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuVGlsZUxheWVyKHVybCwgb3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBUaWxlTGF5ZXIuV01TXHJcbiAqIEBpbmhlcml0cyBUaWxlTGF5ZXJcclxuICogQGFrYSBMLlRpbGVMYXllci5XTVNcclxuICogVXNlZCB0byBkaXNwbGF5IFtXTVNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dlYl9NYXBfU2VydmljZSkgc2VydmljZXMgYXMgdGlsZSBsYXllcnMgb24gdGhlIG1hcC4gRXh0ZW5kcyBgVGlsZUxheWVyYC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIG5leHJhZCA9IEwudGlsZUxheWVyLndtcyhcImh0dHA6Ly9tZXNvbmV0LmFncm9uLmlhc3RhdGUuZWR1L2NnaS1iaW4vd21zL25leHJhZC9uMHIuY2dpXCIsIHtcclxuICogXHRsYXllcnM6ICduZXhyYWQtbjByLTkwMDkxMycsXHJcbiAqIFx0Zm9ybWF0OiAnaW1hZ2UvcG5nJyxcclxuICogXHR0cmFuc3BhcmVudDogdHJ1ZSxcclxuICogXHRhdHRyaWJ1dGlvbjogXCJXZWF0aGVyIGRhdGEgwqkgMjAxMiBJRU0gTmV4cmFkXCJcclxuICogfSk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuVGlsZUxheWVyLldNUyA9IEwuVGlsZUxheWVyLmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBUaWxlTGF5ZXIuV01TIG9wdGlvbnNcclxuXHQvLyBJZiBhbnkgY3VzdG9tIG9wdGlvbnMgbm90IGRvY3VtZW50ZWQgaGVyZSBhcmUgdXNlZCwgdGhleSB3aWxsIGJlIHNlbnQgdG8gdGhlXHJcblx0Ly8gV01TIHNlcnZlciBhcyBleHRyYSBwYXJhbWV0ZXJzIGluIGVhY2ggcmVxdWVzdCBVUkwuIFRoaXMgY2FuIGJlIHVzZWZ1bCBmb3JcclxuXHQvLyBbbm9uLXN0YW5kYXJkIHZlbmRvciBXTVMgcGFyYW1ldGVyc10oaHR0cDovL2RvY3MuZ2Vvc2VydmVyLm9yZy9zdGFibGUvZW4vdXNlci9zZXJ2aWNlcy93bXMvdmVuZG9yLmh0bWwpLlxyXG5cdGRlZmF1bHRXbXNQYXJhbXM6IHtcclxuXHRcdHNlcnZpY2U6ICdXTVMnLFxyXG5cdFx0cmVxdWVzdDogJ0dldE1hcCcsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBsYXllcnM6IFN0cmluZyA9ICcnXHJcblx0XHQvLyAqKihyZXF1aXJlZCkqKiBDb21tYS1zZXBhcmF0ZWQgbGlzdCBvZiBXTVMgbGF5ZXJzIHRvIHNob3cuXHJcblx0XHRsYXllcnM6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gc3R5bGVzOiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gQ29tbWEtc2VwYXJhdGVkIGxpc3Qgb2YgV01TIHN0eWxlcy5cclxuXHRcdHN0eWxlczogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBmb3JtYXQ6IFN0cmluZyA9ICdpbWFnZS9qcGVnJ1xyXG5cdFx0Ly8gV01TIGltYWdlIGZvcm1hdCAodXNlIGAnaW1hZ2UvcG5nJ2AgZm9yIGxheWVycyB3aXRoIHRyYW5zcGFyZW5jeSkuXHJcblx0XHRmb3JtYXQ6ICdpbWFnZS9qcGVnJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHRyYW5zcGFyZW50OiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIFdNUyBzZXJ2aWNlIHdpbGwgcmV0dXJuIGltYWdlcyB3aXRoIHRyYW5zcGFyZW5jeS5cclxuXHRcdHRyYW5zcGFyZW50OiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHZlcnNpb246IFN0cmluZyA9ICcxLjEuMSdcclxuXHRcdC8vIFZlcnNpb24gb2YgdGhlIFdNUyBzZXJ2aWNlIHRvIHVzZVxyXG5cdFx0dmVyc2lvbjogJzEuMS4xJ1xyXG5cdH0sXHJcblxyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gY3JzOiBDUlMgPSBudWxsXHJcblx0XHQvLyBDb29yZGluYXRlIFJlZmVyZW5jZSBTeXN0ZW0gdG8gdXNlIGZvciB0aGUgV01TIHJlcXVlc3RzLCBkZWZhdWx0cyB0b1xyXG5cdFx0Ly8gbWFwIENSUy4gRG9uJ3QgY2hhbmdlIHRoaXMgaWYgeW91J3JlIG5vdCBzdXJlIHdoYXQgaXQgbWVhbnMuXHJcblx0XHRjcnM6IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB1cHBlcmNhc2U6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCBXTVMgcmVxdWVzdCBwYXJhbWV0ZXIga2V5cyB3aWxsIGJlIHVwcGVyY2FzZS5cclxuXHRcdHVwcGVyY2FzZTogZmFsc2VcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAodXJsLCBvcHRpb25zKSB7XHJcblxyXG5cdFx0dGhpcy5fdXJsID0gdXJsO1xyXG5cclxuXHRcdHZhciB3bXNQYXJhbXMgPSBMLmV4dGVuZCh7fSwgdGhpcy5kZWZhdWx0V21zUGFyYW1zKTtcclxuXHJcblx0XHQvLyBhbGwga2V5cyB0aGF0IGFyZSBub3QgVGlsZUxheWVyIG9wdGlvbnMgZ28gdG8gV01TIHBhcmFtc1xyXG5cdFx0Zm9yICh2YXIgaSBpbiBvcHRpb25zKSB7XHJcblx0XHRcdGlmICghKGkgaW4gdGhpcy5vcHRpb25zKSkge1xyXG5cdFx0XHRcdHdtc1BhcmFtc1tpXSA9IG9wdGlvbnNbaV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHRvcHRpb25zID0gTC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHdtc1BhcmFtcy53aWR0aCA9IHdtc1BhcmFtcy5oZWlnaHQgPSBvcHRpb25zLnRpbGVTaXplICogKG9wdGlvbnMuZGV0ZWN0UmV0aW5hICYmIEwuQnJvd3Nlci5yZXRpbmEgPyAyIDogMSk7XHJcblxyXG5cdFx0dGhpcy53bXNQYXJhbXMgPSB3bXNQYXJhbXM7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHJcblx0XHR0aGlzLl9jcnMgPSB0aGlzLm9wdGlvbnMuY3JzIHx8IG1hcC5vcHRpb25zLmNycztcclxuXHRcdHRoaXMuX3dtc1ZlcnNpb24gPSBwYXJzZUZsb2F0KHRoaXMud21zUGFyYW1zLnZlcnNpb24pO1xyXG5cclxuXHRcdHZhciBwcm9qZWN0aW9uS2V5ID0gdGhpcy5fd21zVmVyc2lvbiA+PSAxLjMgPyAnY3JzJyA6ICdzcnMnO1xyXG5cdFx0dGhpcy53bXNQYXJhbXNbcHJvamVjdGlvbktleV0gPSB0aGlzLl9jcnMuY29kZTtcclxuXHJcblx0XHRMLlRpbGVMYXllci5wcm90b3R5cGUub25BZGQuY2FsbCh0aGlzLCBtYXApO1xyXG5cdH0sXHJcblxyXG5cdGdldFRpbGVVcmw6IGZ1bmN0aW9uIChjb29yZHMpIHtcclxuXHJcblx0XHR2YXIgdGlsZUJvdW5kcyA9IHRoaXMuX3RpbGVDb29yZHNUb0JvdW5kcyhjb29yZHMpLFxyXG5cdFx0ICAgIG53ID0gdGhpcy5fY3JzLnByb2plY3QodGlsZUJvdW5kcy5nZXROb3J0aFdlc3QoKSksXHJcblx0XHQgICAgc2UgPSB0aGlzLl9jcnMucHJvamVjdCh0aWxlQm91bmRzLmdldFNvdXRoRWFzdCgpKSxcclxuXHJcblx0XHQgICAgYmJveCA9ICh0aGlzLl93bXNWZXJzaW9uID49IDEuMyAmJiB0aGlzLl9jcnMgPT09IEwuQ1JTLkVQU0c0MzI2ID9cclxuXHRcdFx0ICAgIFtzZS55LCBudy54LCBudy55LCBzZS54XSA6XHJcblx0XHRcdCAgICBbbncueCwgc2UueSwgc2UueCwgbncueV0pLmpvaW4oJywnKSxcclxuXHJcblx0XHQgICAgdXJsID0gTC5UaWxlTGF5ZXIucHJvdG90eXBlLmdldFRpbGVVcmwuY2FsbCh0aGlzLCBjb29yZHMpO1xyXG5cclxuXHRcdHJldHVybiB1cmwgK1xyXG5cdFx0XHRMLlV0aWwuZ2V0UGFyYW1TdHJpbmcodGhpcy53bXNQYXJhbXMsIHVybCwgdGhpcy5vcHRpb25zLnVwcGVyY2FzZSkgK1xyXG5cdFx0XHQodGhpcy5vcHRpb25zLnVwcGVyY2FzZSA/ICcmQkJPWD0nIDogJyZiYm94PScpICsgYmJveDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFBhcmFtcyhwYXJhbXM6IE9iamVjdCwgbm9SZWRyYXc/OiBCb29sZWFuKTogdGhpc1xyXG5cdC8vIE1lcmdlcyBhbiBvYmplY3Qgd2l0aCB0aGUgbmV3IHBhcmFtZXRlcnMgYW5kIHJlLXJlcXVlc3RzIHRpbGVzIG9uIHRoZSBjdXJyZW50IHNjcmVlbiAodW5sZXNzIGBub1JlZHJhd2Agd2FzIHNldCB0byB0cnVlKS5cclxuXHRzZXRQYXJhbXM6IGZ1bmN0aW9uIChwYXJhbXMsIG5vUmVkcmF3KSB7XHJcblxyXG5cdFx0TC5leHRlbmQodGhpcy53bXNQYXJhbXMsIHBhcmFtcyk7XHJcblxyXG5cdFx0aWYgKCFub1JlZHJhdykge1xyXG5cdFx0XHR0aGlzLnJlZHJhdygpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC50aWxlTGF5ZXIud21zKGJhc2VVcmw6IFN0cmluZywgb3B0aW9uczogVGlsZUxheWVyLldNUyBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYSBXTVMgdGlsZSBsYXllciBvYmplY3QgZ2l2ZW4gYSBiYXNlIFVSTCBvZiB0aGUgV01TIHNlcnZpY2UgYW5kIGEgV01TIHBhcmFtZXRlcnMvb3B0aW9ucyBvYmplY3QuXHJcbkwudGlsZUxheWVyLndtcyA9IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuVGlsZUxheWVyLldNUyh1cmwsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgSW1hZ2VPdmVybGF5XHJcbiAqIEBha2EgTC5JbWFnZU92ZXJsYXlcclxuICogQGluaGVyaXRzIEludGVyYWN0aXZlIGxheWVyXHJcbiAqXHJcbiAqIFVzZWQgdG8gbG9hZCBhbmQgZGlzcGxheSBhIHNpbmdsZSBpbWFnZSBvdmVyIHNwZWNpZmljIGJvdW5kcyBvZiB0aGUgbWFwLiBFeHRlbmRzIGBMYXllcmAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBpbWFnZVVybCA9ICdodHRwOi8vd3d3LmxpYi51dGV4YXMuZWR1L21hcHMvaGlzdG9yaWNhbC9uZXdhcmtfbmpfMTkyMi5qcGcnLFxyXG4gKiBcdGltYWdlQm91bmRzID0gW1s0MC43MTIyMTYsIC03NC4yMjY1NV0sIFs0MC43NzM5NDEsIC03NC4xMjU0NF1dO1xyXG4gKiBMLmltYWdlT3ZlcmxheShpbWFnZVVybCwgaW1hZ2VCb3VuZHMpLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuSW1hZ2VPdmVybGF5ID0gTC5MYXllci5leHRlbmQoe1xyXG5cclxuXHQvLyBAc2VjdGlvblxyXG5cdC8vIEBha2EgSW1hZ2VPdmVybGF5IG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIG9wYWNpdHk6IE51bWJlciA9IDEuMFxyXG5cdFx0Ly8gVGhlIG9wYWNpdHkgb2YgdGhlIGltYWdlIG92ZXJsYXkuXHJcblx0XHRvcGFjaXR5OiAxLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYWx0OiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gVGV4dCBmb3IgdGhlIGBhbHRgIGF0dHJpYnV0ZSBvZiB0aGUgaW1hZ2UgKHVzZWZ1bCBmb3IgYWNjZXNzaWJpbGl0eSkuXHJcblx0XHRhbHQ6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gaW50ZXJhY3RpdmU6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCB0aGUgaW1hZ2Ugb3ZlcmxheSB3aWxsIGVtaXQgW21vdXNlIGV2ZW50c10oI2ludGVyYWN0aXZlLWxheWVyKSB3aGVuIGNsaWNrZWQgb3IgaG92ZXJlZC5cclxuXHRcdGludGVyYWN0aXZlOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNyb3NzT3JpZ2luOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIHRydWUsIHRoZSBpbWFnZSB3aWxsIGhhdmUgaXRzIGNyb3NzT3JpZ2luIGF0dHJpYnV0ZSBzZXQgdG8gJycuIFRoaXMgaXMgbmVlZGVkIGlmIHlvdSB3YW50IHRvIGFjY2VzcyBpbWFnZSBwaXhlbCBkYXRhLlxyXG5cdFx0Y3Jvc3NPcmlnaW46IGZhbHNlXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKHVybCwgYm91bmRzLCBvcHRpb25zKSB7IC8vIChTdHJpbmcsIExhdExuZ0JvdW5kcywgT2JqZWN0KVxyXG5cdFx0dGhpcy5fdXJsID0gdXJsO1xyXG5cdFx0dGhpcy5fYm91bmRzID0gTC5sYXRMbmdCb3VuZHMoYm91bmRzKTtcclxuXHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5faW1hZ2UpIHtcclxuXHRcdFx0dGhpcy5faW5pdEltYWdlKCk7XHJcblxyXG5cdFx0XHRpZiAodGhpcy5vcHRpb25zLm9wYWNpdHkgPCAxKSB7XHJcblx0XHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5pbnRlcmFjdGl2ZSkge1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5faW1hZ2UsICdsZWFmbGV0LWludGVyYWN0aXZlJyk7XHJcblx0XHRcdHRoaXMuYWRkSW50ZXJhY3RpdmVUYXJnZXQodGhpcy5faW1hZ2UpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2ltYWdlKTtcclxuXHRcdHRoaXMuX3Jlc2V0KCk7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5faW1hZ2UpO1xyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5pbnRlcmFjdGl2ZSkge1xyXG5cdFx0XHR0aGlzLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KHRoaXMuX2ltYWdlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIG9wYWNpdHkgb2YgdGhlIG92ZXJsYXkuXHJcblx0c2V0T3BhY2l0eTogZnVuY3Rpb24gKG9wYWNpdHkpIHtcclxuXHRcdHRoaXMub3B0aW9ucy5vcGFjaXR5ID0gb3BhY2l0eTtcclxuXHJcblx0XHRpZiAodGhpcy5faW1hZ2UpIHtcclxuXHRcdFx0dGhpcy5fdXBkYXRlT3BhY2l0eSgpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0c2V0U3R5bGU6IGZ1bmN0aW9uIChzdHlsZU9wdHMpIHtcclxuXHRcdGlmIChzdHlsZU9wdHMub3BhY2l0eSkge1xyXG5cdFx0XHR0aGlzLnNldE9wYWNpdHkoc3R5bGVPcHRzLm9wYWNpdHkpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvRnJvbnQoKTogdGhpc1xyXG5cdC8vIEJyaW5ncyB0aGUgbGF5ZXIgdG8gdGhlIHRvcCBvZiBhbGwgb3ZlcmxheXMuXHJcblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdEwuRG9tVXRpbC50b0Zyb250KHRoaXMuX2ltYWdlKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYnJpbmdUb0JhY2soKTogdGhpc1xyXG5cdC8vIEJyaW5ncyB0aGUgbGF5ZXIgdG8gdGhlIGJvdHRvbSBvZiBhbGwgb3ZlcmxheXMuXHJcblx0YnJpbmdUb0JhY2s6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0TC5Eb21VdGlsLnRvQmFjayh0aGlzLl9pbWFnZSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFVybCh1cmw6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBDaGFuZ2VzIHRoZSBVUkwgb2YgdGhlIGltYWdlLlxyXG5cdHNldFVybDogZnVuY3Rpb24gKHVybCkge1xyXG5cdFx0dGhpcy5fdXJsID0gdXJsO1xyXG5cclxuXHRcdGlmICh0aGlzLl9pbWFnZSkge1xyXG5cdFx0XHR0aGlzLl9pbWFnZS5zcmMgPSB1cmw7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldEJvdW5kcyhib3VuZHM6IExhdExuZ0JvdW5kcyk6IHRoaXNcclxuXHQvLyBVcGRhdGUgdGhlIGJvdW5kcyB0aGF0IHRoaXMgSW1hZ2VPdmVybGF5IGNvdmVyc1xyXG5cdHNldEJvdW5kczogZnVuY3Rpb24gKGJvdW5kcykge1xyXG5cdFx0dGhpcy5fYm91bmRzID0gYm91bmRzO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fcmVzZXQoKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGV2ZW50cyA9IHtcclxuXHRcdFx0em9vbTogdGhpcy5fcmVzZXQsXHJcblx0XHRcdHZpZXdyZXNldDogdGhpcy5fcmVzZXRcclxuXHRcdH07XHJcblxyXG5cdFx0aWYgKHRoaXMuX3pvb21BbmltYXRlZCkge1xyXG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9hbmltYXRlWm9vbTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gZXZlbnRzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIEdldCB0aGUgYm91bmRzIHRoYXQgdGhpcyBJbWFnZU92ZXJsYXkgY292ZXJzXHJcblx0Z2V0Qm91bmRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fYm91bmRzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0RWxlbWVudCgpOiBIVE1MRWxlbWVudFxyXG5cdC8vIEdldCB0aGUgaW1nIGVsZW1lbnQgdGhhdCByZXByZXNlbnRzIHRoZSBJbWFnZU92ZXJsYXkgb24gdGhlIG1hcFxyXG5cdGdldEVsZW1lbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLl9pbWFnZTtcclxuXHR9LFxyXG5cclxuXHRfaW5pdEltYWdlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgaW1nID0gdGhpcy5faW1hZ2UgPSBMLkRvbVV0aWwuY3JlYXRlKCdpbWcnLFxyXG5cdFx0XHRcdCdsZWFmbGV0LWltYWdlLWxheWVyICcgKyAodGhpcy5fem9vbUFuaW1hdGVkID8gJ2xlYWZsZXQtem9vbS1hbmltYXRlZCcgOiAnJykpO1xyXG5cclxuXHRcdGltZy5vbnNlbGVjdHN0YXJ0ID0gTC5VdGlsLmZhbHNlRm47XHJcblx0XHRpbWcub25tb3VzZW1vdmUgPSBMLlV0aWwuZmFsc2VGbjtcclxuXHJcblx0XHRpbWcub25sb2FkID0gTC5iaW5kKHRoaXMuZmlyZSwgdGhpcywgJ2xvYWQnKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmNyb3NzT3JpZ2luKSB7XHJcblx0XHRcdGltZy5jcm9zc09yaWdpbiA9ICcnO1xyXG5cdFx0fVxyXG5cclxuXHRcdGltZy5zcmMgPSB0aGlzLl91cmw7XHJcblx0XHRpbWcuYWx0ID0gdGhpcy5vcHRpb25zLmFsdDtcclxuXHR9LFxyXG5cclxuXHRfYW5pbWF0ZVpvb206IGZ1bmN0aW9uIChlKSB7XHJcblx0XHR2YXIgc2NhbGUgPSB0aGlzLl9tYXAuZ2V0Wm9vbVNjYWxlKGUuem9vbSksXHJcblx0XHQgICAgb2Zmc2V0ID0gdGhpcy5fbWFwLl9sYXRMbmdCb3VuZHNUb05ld0xheWVyQm91bmRzKHRoaXMuX2JvdW5kcywgZS56b29tLCBlLmNlbnRlcikubWluO1xyXG5cclxuXHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0odGhpcy5faW1hZ2UsIG9mZnNldCwgc2NhbGUpO1xyXG5cdH0sXHJcblxyXG5cdF9yZXNldDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGltYWdlID0gdGhpcy5faW1hZ2UsXHJcblx0XHQgICAgYm91bmRzID0gbmV3IEwuQm91bmRzKFxyXG5cdFx0ICAgICAgICB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2JvdW5kcy5nZXROb3J0aFdlc3QoKSksXHJcblx0XHQgICAgICAgIHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fYm91bmRzLmdldFNvdXRoRWFzdCgpKSksXHJcblx0XHQgICAgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XHJcblxyXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKGltYWdlLCBib3VuZHMubWluKTtcclxuXHJcblx0XHRpbWFnZS5zdHlsZS53aWR0aCAgPSBzaXplLnggKyAncHgnO1xyXG5cdFx0aW1hZ2Uuc3R5bGUuaGVpZ2h0ID0gc2l6ZS55ICsgJ3B4JztcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlT3BhY2l0eTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGhpcy5faW1hZ2UsIHRoaXMub3B0aW9ucy5vcGFjaXR5KTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQGZhY3RvcnkgTC5pbWFnZU92ZXJsYXkoaW1hZ2VVcmw6IFN0cmluZywgYm91bmRzOiBMYXRMbmdCb3VuZHMsIG9wdGlvbnM/OiBJbWFnZU92ZXJsYXkgb3B0aW9ucylcclxuLy8gSW5zdGFudGlhdGVzIGFuIGltYWdlIG92ZXJsYXkgb2JqZWN0IGdpdmVuIHRoZSBVUkwgb2YgdGhlIGltYWdlIGFuZCB0aGVcclxuLy8gZ2VvZ3JhcGhpY2FsIGJvdW5kcyBpdCBpcyB0aWVkIHRvLlxyXG5MLmltYWdlT3ZlcmxheSA9IGZ1bmN0aW9uICh1cmwsIGJvdW5kcywgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5JbWFnZU92ZXJsYXkodXJsLCBib3VuZHMsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgSWNvblxyXG4gKiBAYWthIEwuSWNvblxyXG4gKiBAaW5oZXJpdHMgTGF5ZXJcclxuICpcclxuICogUmVwcmVzZW50cyBhbiBpY29uIHRvIHByb3ZpZGUgd2hlbiBjcmVhdGluZyBhIG1hcmtlci5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogdmFyIG15SWNvbiA9IEwuaWNvbih7XHJcbiAqICAgICBpY29uVXJsOiAnbXktaWNvbi5wbmcnLFxyXG4gKiAgICAgaWNvblJldGluYVVybDogJ215LWljb25AMngucG5nJyxcclxuICogICAgIGljb25TaXplOiBbMzgsIDk1XSxcclxuICogICAgIGljb25BbmNob3I6IFsyMiwgOTRdLFxyXG4gKiAgICAgcG9wdXBBbmNob3I6IFstMywgLTc2XSxcclxuICogICAgIHNoYWRvd1VybDogJ215LWljb24tc2hhZG93LnBuZycsXHJcbiAqICAgICBzaGFkb3dSZXRpbmFVcmw6ICdteS1pY29uLXNoYWRvd0AyeC5wbmcnLFxyXG4gKiAgICAgc2hhZG93U2l6ZTogWzY4LCA5NV0sXHJcbiAqICAgICBzaGFkb3dBbmNob3I6IFsyMiwgOTRdXHJcbiAqIH0pO1xyXG4gKlxyXG4gKiBMLm1hcmtlcihbNTAuNTA1LCAzMC41N10sIHtpY29uOiBteUljb259KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICpcclxuICogYEwuSWNvbi5EZWZhdWx0YCBleHRlbmRzIGBMLkljb25gIGFuZCBpcyB0aGUgYmx1ZSBpY29uIExlYWZsZXQgdXNlcyBmb3IgbWFya2VycyBieSBkZWZhdWx0LlxyXG4gKlxyXG4gKi9cclxuXHJcbkwuSWNvbiA9IEwuQ2xhc3MuZXh0ZW5kKHtcclxuXHJcblx0LyogQHNlY3Rpb25cclxuXHQgKiBAYWthIEljb24gb3B0aW9uc1xyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBpY29uVXJsOiBTdHJpbmcgPSBudWxsXHJcblx0ICogKioocmVxdWlyZWQpKiogVGhlIFVSTCB0byB0aGUgaWNvbiBpbWFnZSAoYWJzb2x1dGUgb3IgcmVsYXRpdmUgdG8geW91ciBzY3JpcHQgcGF0aCkuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIGljb25SZXRpbmFVcmw6IFN0cmluZyA9IG51bGxcclxuXHQgKiBUaGUgVVJMIHRvIGEgcmV0aW5hIHNpemVkIHZlcnNpb24gb2YgdGhlIGljb24gaW1hZ2UgKGFic29sdXRlIG9yIHJlbGF0aXZlIHRvIHlvdXJcclxuXHQgKiBzY3JpcHQgcGF0aCkuIFVzZWQgZm9yIFJldGluYSBzY3JlZW4gZGV2aWNlcy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gaWNvblNpemU6IFBvaW50ID0gbnVsbFxyXG5cdCAqIFNpemUgb2YgdGhlIGljb24gaW1hZ2UgaW4gcGl4ZWxzLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBpY29uQW5jaG9yOiBQb2ludCA9IG51bGxcclxuXHQgKiBUaGUgY29vcmRpbmF0ZXMgb2YgdGhlIFwidGlwXCIgb2YgdGhlIGljb24gKHJlbGF0aXZlIHRvIGl0cyB0b3AgbGVmdCBjb3JuZXIpLiBUaGUgaWNvblxyXG5cdCAqIHdpbGwgYmUgYWxpZ25lZCBzbyB0aGF0IHRoaXMgcG9pbnQgaXMgYXQgdGhlIG1hcmtlcidzIGdlb2dyYXBoaWNhbCBsb2NhdGlvbi4gQ2VudGVyZWRcclxuXHQgKiBieSBkZWZhdWx0IGlmIHNpemUgaXMgc3BlY2lmaWVkLCBhbHNvIGNhbiBiZSBzZXQgaW4gQ1NTIHdpdGggbmVnYXRpdmUgbWFyZ2lucy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gcG9wdXBBbmNob3I6IFBvaW50ID0gbnVsbFxyXG5cdCAqIFRoZSBjb29yZGluYXRlcyBvZiB0aGUgcG9pbnQgZnJvbSB3aGljaCBwb3B1cHMgd2lsbCBcIm9wZW5cIiwgcmVsYXRpdmUgdG8gdGhlIGljb24gYW5jaG9yLlxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBzaGFkb3dVcmw6IFN0cmluZyA9IG51bGxcclxuXHQgKiBUaGUgVVJMIHRvIHRoZSBpY29uIHNoYWRvdyBpbWFnZS4gSWYgbm90IHNwZWNpZmllZCwgbm8gc2hhZG93IGltYWdlIHdpbGwgYmUgY3JlYXRlZC5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gc2hhZG93UmV0aW5hVXJsOiBTdHJpbmcgPSBudWxsXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHNoYWRvd1NpemU6IFBvaW50ID0gbnVsbFxyXG5cdCAqIFNpemUgb2YgdGhlIHNoYWRvdyBpbWFnZSBpbiBwaXhlbHMuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHNoYWRvd0FuY2hvcjogUG9pbnQgPSBudWxsXHJcblx0ICogVGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBcInRpcFwiIG9mIHRoZSBzaGFkb3cgKHJlbGF0aXZlIHRvIGl0cyB0b3AgbGVmdCBjb3JuZXIpICh0aGUgc2FtZVxyXG5cdCAqIGFzIGljb25BbmNob3IgaWYgbm90IHNwZWNpZmllZCkuXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIGNsYXNzTmFtZTogU3RyaW5nID0gJydcclxuXHQgKiBBIGN1c3RvbSBjbGFzcyBuYW1lIHRvIGFzc2lnbiB0byBib3RoIGljb24gYW5kIHNoYWRvdyBpbWFnZXMuIEVtcHR5IGJ5IGRlZmF1bHQuXHJcblx0ICovXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjcmVhdGVJY29uKG9sZEljb24/OiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50XHJcblx0Ly8gQ2FsbGVkIGludGVybmFsbHkgd2hlbiB0aGUgaWNvbiBoYXMgdG8gYmUgc2hvd24sIHJldHVybnMgYSBgPGltZz5gIEhUTUwgZWxlbWVudFxyXG5cdC8vIHN0eWxlZCBhY2NvcmRpbmcgdG8gdGhlIG9wdGlvbnMuXHJcblx0Y3JlYXRlSWNvbjogZnVuY3Rpb24gKG9sZEljb24pIHtcclxuXHRcdHJldHVybiB0aGlzLl9jcmVhdGVJY29uKCdpY29uJywgb2xkSWNvbik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjcmVhdGVTaGFkb3cob2xkSWNvbj86IEhUTUxFbGVtZW50KTogSFRNTEVsZW1lbnRcclxuXHQvLyBBcyBgY3JlYXRlSWNvbmAsIGJ1dCBmb3IgdGhlIHNoYWRvdyBiZW5lYXRoIGl0LlxyXG5cdGNyZWF0ZVNoYWRvdzogZnVuY3Rpb24gKG9sZEljb24pIHtcclxuXHRcdHJldHVybiB0aGlzLl9jcmVhdGVJY29uKCdzaGFkb3cnLCBvbGRJY29uKTtcclxuXHR9LFxyXG5cclxuXHRfY3JlYXRlSWNvbjogZnVuY3Rpb24gKG5hbWUsIG9sZEljb24pIHtcclxuXHRcdHZhciBzcmMgPSB0aGlzLl9nZXRJY29uVXJsKG5hbWUpO1xyXG5cclxuXHRcdGlmICghc3JjKSB7XHJcblx0XHRcdGlmIChuYW1lID09PSAnaWNvbicpIHtcclxuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ2ljb25Vcmwgbm90IHNldCBpbiBJY29uIG9wdGlvbnMgKHNlZSB0aGUgZG9jcykuJyk7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIG51bGw7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIGltZyA9IHRoaXMuX2NyZWF0ZUltZyhzcmMsIG9sZEljb24gJiYgb2xkSWNvbi50YWdOYW1lID09PSAnSU1HJyA/IG9sZEljb24gOiBudWxsKTtcclxuXHRcdHRoaXMuX3NldEljb25TdHlsZXMoaW1nLCBuYW1lKTtcclxuXHJcblx0XHRyZXR1cm4gaW1nO1xyXG5cdH0sXHJcblxyXG5cdF9zZXRJY29uU3R5bGVzOiBmdW5jdGlvbiAoaW1nLCBuYW1lKSB7XHJcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcclxuXHRcdHZhciBzaXplT3B0aW9uID0gb3B0aW9uc1tuYW1lICsgJ1NpemUnXTtcclxuXHJcblx0XHRpZiAodHlwZW9mIHNpemVPcHRpb24gPT09ICdudW1iZXInKSB7XHJcblx0XHRcdHNpemVPcHRpb24gPSBbc2l6ZU9wdGlvbiwgc2l6ZU9wdGlvbl07XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHNpemUgPSBMLnBvaW50KHNpemVPcHRpb24pLFxyXG5cdFx0ICAgIGFuY2hvciA9IEwucG9pbnQobmFtZSA9PT0gJ3NoYWRvdycgJiYgb3B0aW9ucy5zaGFkb3dBbmNob3IgfHwgb3B0aW9ucy5pY29uQW5jaG9yIHx8XHJcblx0XHQgICAgICAgICAgICBzaXplICYmIHNpemUuZGl2aWRlQnkoMiwgdHJ1ZSkpO1xyXG5cclxuXHRcdGltZy5jbGFzc05hbWUgPSAnbGVhZmxldC1tYXJrZXItJyArIG5hbWUgKyAnICcgKyAob3B0aW9ucy5jbGFzc05hbWUgfHwgJycpO1xyXG5cclxuXHRcdGlmIChhbmNob3IpIHtcclxuXHRcdFx0aW1nLnN0eWxlLm1hcmdpbkxlZnQgPSAoLWFuY2hvci54KSArICdweCc7XHJcblx0XHRcdGltZy5zdHlsZS5tYXJnaW5Ub3AgID0gKC1hbmNob3IueSkgKyAncHgnO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChzaXplKSB7XHJcblx0XHRcdGltZy5zdHlsZS53aWR0aCAgPSBzaXplLnggKyAncHgnO1xyXG5cdFx0XHRpbWcuc3R5bGUuaGVpZ2h0ID0gc2l6ZS55ICsgJ3B4JztcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfY3JlYXRlSW1nOiBmdW5jdGlvbiAoc3JjLCBlbCkge1xyXG5cdFx0ZWwgPSBlbCB8fCBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTtcclxuXHRcdGVsLnNyYyA9IHNyYztcclxuXHRcdHJldHVybiBlbDtcclxuXHR9LFxyXG5cclxuXHRfZ2V0SWNvblVybDogZnVuY3Rpb24gKG5hbWUpIHtcclxuXHRcdHJldHVybiBMLkJyb3dzZXIucmV0aW5hICYmIHRoaXMub3B0aW9uc1tuYW1lICsgJ1JldGluYVVybCddIHx8IHRoaXMub3B0aW9uc1tuYW1lICsgJ1VybCddO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5pY29uKG9wdGlvbnM6IEljb24gb3B0aW9ucylcclxuLy8gQ3JlYXRlcyBhbiBpY29uIGluc3RhbmNlIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXHJcbkwuaWNvbiA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkljb24ob3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAbWluaWNsYXNzIEljb24uRGVmYXVsdCAoSWNvbilcbiAqIEBha2EgTC5JY29uLkRlZmF1bHRcbiAqIEBzZWN0aW9uXG4gKlxuICogQSB0cml2aWFsIHN1YmNsYXNzIG9mIGBJY29uYCwgcmVwcmVzZW50cyB0aGUgaWNvbiB0byB1c2UgaW4gYE1hcmtlcmBzIHdoZW5cbiAqIG5vIGljb24gaXMgc3BlY2lmaWVkLiBQb2ludHMgdG8gdGhlIGJsdWUgbWFya2VyIGltYWdlIGRpc3RyaWJ1dGVkIHdpdGggTGVhZmxldFxuICogcmVsZWFzZXMuXG4gKlxuICogSW4gb3JkZXIgdG8gY3VzdG9taXplIHRoZSBkZWZhdWx0IGljb24sIGp1c3QgY2hhbmdlIHRoZSBwcm9wZXJ0aWVzIG9mIGBMLkljb24uRGVmYXVsdC5wcm90b3R5cGUub3B0aW9uc2BcbiAqICh3aGljaCBpcyBhIHNldCBvZiBgSWNvbiBvcHRpb25zYCkuXG4gKlxuICogSWYgeW91IHdhbnQgdG8gX2NvbXBsZXRlbHlfIHJlcGxhY2UgdGhlIGRlZmF1bHQgaWNvbiwgb3ZlcnJpZGUgdGhlXG4gKiBgTC5NYXJrZXIucHJvdG90eXBlLm9wdGlvbnMuaWNvbmAgd2l0aCB5b3VyIG93biBpY29uIGluc3RlYWQuXG4gKi9cblxuTC5JY29uLkRlZmF1bHQgPSBMLkljb24uZXh0ZW5kKHtcblxuXHRvcHRpb25zOiB7XG5cdFx0aWNvblVybDogICAgICAgJ21hcmtlci1pY29uLnBuZycsXG5cdFx0aWNvblJldGluYVVybDogJ21hcmtlci1pY29uLTJ4LnBuZycsXG5cdFx0c2hhZG93VXJsOiAgICAgJ21hcmtlci1zaGFkb3cucG5nJyxcblx0XHRpY29uU2l6ZTogICAgWzI1LCA0MV0sXG5cdFx0aWNvbkFuY2hvcjogIFsxMiwgNDFdLFxuXHRcdHBvcHVwQW5jaG9yOiBbMSwgLTM0XSxcblx0XHR0b29sdGlwQW5jaG9yOiBbMTYsIC0yOF0sXG5cdFx0c2hhZG93U2l6ZTogIFs0MSwgNDFdXG5cdH0sXG5cblx0X2dldEljb25Vcmw6IGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0aWYgKCFMLkljb24uRGVmYXVsdC5pbWFnZVBhdGgpIHtcdC8vIERlcHJlY2F0ZWQsIGJhY2t3YXJkcy1jb21wYXRpYmlsaXR5IG9ubHlcblx0XHRcdEwuSWNvbi5EZWZhdWx0LmltYWdlUGF0aCA9IHRoaXMuX2RldGVjdEljb25QYXRoKCk7XG5cdFx0fVxuXG5cdFx0Ly8gQG9wdGlvbiBpbWFnZVBhdGg6IFN0cmluZ1xuXHRcdC8vIGBMLkljb24uRGVmYXVsdGAgd2lsbCB0cnkgdG8gYXV0by1kZXRlY3QgdGhlIGFic29sdXRlIGxvY2F0aW9uIG9mIHRoZVxuXHRcdC8vIGJsdWUgaWNvbiBpbWFnZXMuIElmIHlvdSBhcmUgcGxhY2luZyB0aGVzZSBpbWFnZXMgaW4gYSBub24tc3RhbmRhcmRcblx0XHQvLyB3YXksIHNldCB0aGlzIG9wdGlvbiB0byBwb2ludCB0byB0aGUgcmlnaHQgYWJzb2x1dGUgcGF0aC5cblx0XHRyZXR1cm4gKHRoaXMub3B0aW9ucy5pbWFnZVBhdGggfHwgTC5JY29uLkRlZmF1bHQuaW1hZ2VQYXRoKSArIEwuSWNvbi5wcm90b3R5cGUuX2dldEljb25VcmwuY2FsbCh0aGlzLCBuYW1lKTtcblx0fSxcblxuXHRfZGV0ZWN0SWNvblBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgZWwgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCAgJ2xlYWZsZXQtZGVmYXVsdC1pY29uLXBhdGgnLCBkb2N1bWVudC5ib2R5KTtcblx0XHR2YXIgcGF0aCA9IEwuRG9tVXRpbC5nZXRTdHlsZShlbCwgJ2JhY2tncm91bmQtaW1hZ2UnKSB8fFxuXHRcdCAgICAgICAgICAgTC5Eb21VdGlsLmdldFN0eWxlKGVsLCAnYmFja2dyb3VuZEltYWdlJyk7XHQvLyBJRThcblxuXHRcdGRvY3VtZW50LmJvZHkucmVtb3ZlQ2hpbGQoZWwpO1xuXG5cdFx0cmV0dXJuIHBhdGguaW5kZXhPZigndXJsJykgPT09IDAgP1xuXHRcdFx0cGF0aC5yZXBsYWNlKC9edXJsXFwoW1xcXCJcXCddPy8sICcnKS5yZXBsYWNlKC9tYXJrZXItaWNvblxcLnBuZ1tcXFwiXFwnXT9cXCkkLywgJycpIDogJyc7XG5cdH1cbn0pO1xuXG5cblxuLypcclxuICogQGNsYXNzIE1hcmtlclxyXG4gKiBAaW5oZXJpdHMgSW50ZXJhY3RpdmUgbGF5ZXJcclxuICogQGFrYSBMLk1hcmtlclxyXG4gKiBMLk1hcmtlciBpcyB1c2VkIHRvIGRpc3BsYXkgY2xpY2thYmxlL2RyYWdnYWJsZSBpY29ucyBvbiB0aGUgbWFwLiBFeHRlbmRzIGBMYXllcmAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIEwubWFya2VyKFs1MC41LCAzMC41XSkuYWRkVG8obWFwKTtcclxuICogYGBgXHJcbiAqL1xyXG5cclxuTC5NYXJrZXIgPSBMLkxheWVyLmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBNYXJrZXIgb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gaWNvbjogSWNvbiA9ICpcclxuXHRcdC8vIEljb24gY2xhc3MgdG8gdXNlIGZvciByZW5kZXJpbmcgdGhlIG1hcmtlci4gU2VlIFtJY29uIGRvY3VtZW50YXRpb25dKCNMLkljb24pIGZvciBkZXRhaWxzIG9uIGhvdyB0byBjdXN0b21pemUgdGhlIG1hcmtlciBpY29uLiBJZiBub3Qgc3BlY2lmaWVkLCBhIG5ldyBgTC5JY29uLkRlZmF1bHRgIGlzIHVzZWQuXHJcblx0XHRpY29uOiBuZXcgTC5JY29uLkRlZmF1bHQoKSxcclxuXHJcblx0XHQvLyBPcHRpb24gaW5oZXJpdGVkIGZyb20gXCJJbnRlcmFjdGl2ZSBsYXllclwiIGFic3RyYWN0IGNsYXNzXHJcblx0XHRpbnRlcmFjdGl2ZTogdHJ1ZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGRyYWdnYWJsZTogQm9vbGVhbiA9IGZhbHNlXHJcblx0XHQvLyBXaGV0aGVyIHRoZSBtYXJrZXIgaXMgZHJhZ2dhYmxlIHdpdGggbW91c2UvdG91Y2ggb3Igbm90LlxyXG5cdFx0ZHJhZ2dhYmxlOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGtleWJvYXJkOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gV2hldGhlciB0aGUgbWFya2VyIGNhbiBiZSB0YWJiZWQgdG8gd2l0aCBhIGtleWJvYXJkIGFuZCBjbGlja2VkIGJ5IHByZXNzaW5nIGVudGVyLlxyXG5cdFx0a2V5Ym9hcmQ6IHRydWUsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB0aXRsZTogU3RyaW5nID0gJydcclxuXHRcdC8vIFRleHQgZm9yIHRoZSBicm93c2VyIHRvb2x0aXAgdGhhdCBhcHBlYXIgb24gbWFya2VyIGhvdmVyIChubyB0b29sdGlwIGJ5IGRlZmF1bHQpLlxyXG5cdFx0dGl0bGU6ICcnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYWx0OiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gVGV4dCBmb3IgdGhlIGBhbHRgIGF0dHJpYnV0ZSBvZiB0aGUgaWNvbiBpbWFnZSAodXNlZnVsIGZvciBhY2Nlc3NpYmlsaXR5KS5cclxuXHRcdGFsdDogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6SW5kZXhPZmZzZXQ6IE51bWJlciA9IDBcclxuXHRcdC8vIEJ5IGRlZmF1bHQsIG1hcmtlciBpbWFnZXMgekluZGV4IGlzIHNldCBhdXRvbWF0aWNhbGx5IGJhc2VkIG9uIGl0cyBsYXRpdHVkZS4gVXNlIHRoaXMgb3B0aW9uIGlmIHlvdSB3YW50IHRvIHB1dCB0aGUgbWFya2VyIG9uIHRvcCBvZiBhbGwgb3RoZXJzIChvciBiZWxvdyksIHNwZWNpZnlpbmcgYSBoaWdoIHZhbHVlIGxpa2UgYDEwMDBgIChvciBoaWdoIG5lZ2F0aXZlIHZhbHVlLCByZXNwZWN0aXZlbHkpLlxyXG5cdFx0ekluZGV4T2Zmc2V0OiAwLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMS4wXHJcblx0XHQvLyBUaGUgb3BhY2l0eSBvZiB0aGUgbWFya2VyLlxyXG5cdFx0b3BhY2l0eTogMSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHJpc2VPbkhvdmVyOiBCb29sZWFuID0gZmFsc2VcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIG1hcmtlciB3aWxsIGdldCBvbiB0b3Agb2Ygb3RoZXJzIHdoZW4geW91IGhvdmVyIHRoZSBtb3VzZSBvdmVyIGl0LlxyXG5cdFx0cmlzZU9uSG92ZXI6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gcmlzZU9mZnNldDogTnVtYmVyID0gMjUwXHJcblx0XHQvLyBUaGUgei1pbmRleCBvZmZzZXQgdXNlZCBmb3IgdGhlIGByaXNlT25Ib3ZlcmAgZmVhdHVyZS5cclxuXHRcdHJpc2VPZmZzZXQ6IDI1MCxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHBhbmU6IFN0cmluZyA9ICdtYXJrZXJQYW5lJ1xyXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgbWFya2VycyBpY29uIHdpbGwgYmUgYWRkZWQuXHJcblx0XHRwYW5lOiAnbWFya2VyUGFuZScsXHJcblxyXG5cdFx0Ly8gRklYTUU6IHNoYWRvd1BhbmUgaXMgbm8gbG9uZ2VyIGEgdmFsaWQgb3B0aW9uXHJcblx0XHRub25CdWJibGluZ0V2ZW50czogWydjbGljaycsICdkYmxjbGljaycsICdtb3VzZW92ZXInLCAnbW91c2VvdXQnLCAnY29udGV4dG1lbnUnXVxyXG5cdH0sXHJcblxyXG5cdC8qIEBzZWN0aW9uXHJcblx0ICpcclxuXHQgKiBJbiBhZGRpdGlvbiB0byBbc2hhcmVkIGxheWVyIG1ldGhvZHNdKCNMYXllcikgbGlrZSBgYWRkVG8oKWAgYW5kIGByZW1vdmUoKWAgYW5kIFtwb3B1cCBtZXRob2RzXSgjUG9wdXApIGxpa2UgYmluZFBvcHVwKCkgeW91IGNhbiBhbHNvIHVzZSB0aGUgZm9sbG93aW5nIG1ldGhvZHM6XHJcblx0ICovXHJcblxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcclxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHRcdHRoaXMuX2xhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdHRoaXMuX3pvb21BbmltYXRlZCA9IHRoaXMuX3pvb21BbmltYXRlZCAmJiBtYXAub3B0aW9ucy5tYXJrZXJab29tQW5pbWF0aW9uO1xyXG5cclxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcclxuXHRcdFx0bWFwLm9uKCd6b29tYW5pbScsIHRoaXMuX2FuaW1hdGVab29tLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9pbml0SWNvbigpO1xyXG5cdFx0dGhpcy51cGRhdGUoKTtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0aWYgKHRoaXMuZHJhZ2dpbmcgJiYgdGhpcy5kcmFnZ2luZy5lbmFibGVkKCkpIHtcclxuXHRcdFx0dGhpcy5vcHRpb25zLmRyYWdnYWJsZSA9IHRydWU7XHJcblx0XHRcdHRoaXMuZHJhZ2dpbmcucmVtb3ZlSG9va3MoKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdG1hcC5vZmYoJ3pvb21hbmltJywgdGhpcy5fYW5pbWF0ZVpvb20sIHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX3JlbW92ZUljb24oKTtcclxuXHRcdHRoaXMuX3JlbW92ZVNoYWRvdygpO1xyXG5cdH0sXHJcblxyXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHtcclxuXHRcdFx0em9vbTogdGhpcy51cGRhdGUsXHJcblx0XHRcdHZpZXdyZXNldDogdGhpcy51cGRhdGVcclxuXHRcdH07XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRMYXRMbmc6IExhdExuZ1xyXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgZ2VvZ3JhcGhpY2FsIHBvc2l0aW9uIG9mIHRoZSBtYXJrZXIuXHJcblx0Z2V0TGF0TG5nOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fbGF0bG5nO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0TGF0TG5nKGxhdGxuZzogTGF0TG5nKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgdGhlIG1hcmtlciBwb3NpdGlvbiB0byB0aGUgZ2l2ZW4gcG9pbnQuXHJcblx0c2V0TGF0TG5nOiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHR2YXIgb2xkTGF0TG5nID0gdGhpcy5fbGF0bG5nO1xyXG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcclxuXHRcdHRoaXMudXBkYXRlKCk7XHJcblxyXG5cdFx0Ly8gQGV2ZW50IG1vdmU6IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXJrZXIgaXMgbW92ZWQgdmlhIFtgc2V0TGF0TG5nYF0oI21hcmtlci1zZXRsYXRsbmcpIG9yIGJ5IFtkcmFnZ2luZ10oI21hcmtlci1kcmFnZ2luZykuIE9sZCBhbmQgbmV3IGNvb3JkaW5hdGVzIGFyZSBpbmNsdWRlZCBpbiBldmVudCBhcmd1bWVudHMgYXMgYG9sZExhdExuZ2AsIGBsYXRsbmdgLlxyXG5cdFx0cmV0dXJuIHRoaXMuZmlyZSgnbW92ZScsIHtvbGRMYXRMbmc6IG9sZExhdExuZywgbGF0bG5nOiB0aGlzLl9sYXRsbmd9KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFpJbmRleE9mZnNldChvZmZzZXQ6IE51bWJlcik6IHRoaXNcclxuXHQvLyBDaGFuZ2VzIHRoZSBbekluZGV4IG9mZnNldF0oI21hcmtlci16aW5kZXhvZmZzZXQpIG9mIHRoZSBtYXJrZXIuXHJcblx0c2V0WkluZGV4T2Zmc2V0OiBmdW5jdGlvbiAob2Zmc2V0KSB7XHJcblx0XHR0aGlzLm9wdGlvbnMuekluZGV4T2Zmc2V0ID0gb2Zmc2V0O1xyXG5cdFx0cmV0dXJuIHRoaXMudXBkYXRlKCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRJY29uKGljb246IEljb24pOiB0aGlzXHJcblx0Ly8gQ2hhbmdlcyB0aGUgbWFya2VyIGljb24uXHJcblx0c2V0SWNvbjogZnVuY3Rpb24gKGljb24pIHtcclxuXHJcblx0XHR0aGlzLm9wdGlvbnMuaWNvbiA9IGljb247XHJcblxyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0aGlzLl9pbml0SWNvbigpO1xyXG5cdFx0XHR0aGlzLnVwZGF0ZSgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHR0aGlzLmJpbmRQb3B1cCh0aGlzLl9wb3B1cCwgdGhpcy5fcG9wdXAub3B0aW9ucyk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Z2V0RWxlbWVudDogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2ljb247XHJcblx0fSxcclxuXHJcblx0dXBkYXRlOiBmdW5jdGlvbiAoKSB7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2ljb24pIHtcclxuXHRcdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fbGF0bG5nKS5yb3VuZCgpO1xyXG5cdFx0XHR0aGlzLl9zZXRQb3MocG9zKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfaW5pdEljb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBvcHRpb25zID0gdGhpcy5vcHRpb25zLFxyXG5cdFx0ICAgIGNsYXNzVG9BZGQgPSAnbGVhZmxldC16b29tLScgKyAodGhpcy5fem9vbUFuaW1hdGVkID8gJ2FuaW1hdGVkJyA6ICdoaWRlJyk7XHJcblxyXG5cdFx0dmFyIGljb24gPSBvcHRpb25zLmljb24uY3JlYXRlSWNvbih0aGlzLl9pY29uKSxcclxuXHRcdCAgICBhZGRJY29uID0gZmFsc2U7XHJcblxyXG5cdFx0Ly8gaWYgd2UncmUgbm90IHJldXNpbmcgdGhlIGljb24sIHJlbW92ZSB0aGUgb2xkIG9uZSBhbmQgaW5pdCBuZXcgb25lXHJcblx0XHRpZiAoaWNvbiAhPT0gdGhpcy5faWNvbikge1xyXG5cdFx0XHRpZiAodGhpcy5faWNvbikge1xyXG5cdFx0XHRcdHRoaXMuX3JlbW92ZUljb24oKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRhZGRJY29uID0gdHJ1ZTtcclxuXHJcblx0XHRcdGlmIChvcHRpb25zLnRpdGxlKSB7XHJcblx0XHRcdFx0aWNvbi50aXRsZSA9IG9wdGlvbnMudGl0bGU7XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKG9wdGlvbnMuYWx0KSB7XHJcblx0XHRcdFx0aWNvbi5hbHQgPSBvcHRpb25zLmFsdDtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhpY29uLCBjbGFzc1RvQWRkKTtcclxuXHJcblx0XHRpZiAob3B0aW9ucy5rZXlib2FyZCkge1xyXG5cdFx0XHRpY29uLnRhYkluZGV4ID0gJzAnO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2ljb24gPSBpY29uO1xyXG5cclxuXHRcdGlmIChvcHRpb25zLnJpc2VPbkhvdmVyKSB7XHJcblx0XHRcdHRoaXMub24oe1xyXG5cdFx0XHRcdG1vdXNlb3ZlcjogdGhpcy5fYnJpbmdUb0Zyb250LFxyXG5cdFx0XHRcdG1vdXNlb3V0OiB0aGlzLl9yZXNldFpJbmRleFxyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgbmV3U2hhZG93ID0gb3B0aW9ucy5pY29uLmNyZWF0ZVNoYWRvdyh0aGlzLl9zaGFkb3cpLFxyXG5cdFx0ICAgIGFkZFNoYWRvdyA9IGZhbHNlO1xyXG5cclxuXHRcdGlmIChuZXdTaGFkb3cgIT09IHRoaXMuX3NoYWRvdykge1xyXG5cdFx0XHR0aGlzLl9yZW1vdmVTaGFkb3coKTtcclxuXHRcdFx0YWRkU2hhZG93ID0gdHJ1ZTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAobmV3U2hhZG93KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhuZXdTaGFkb3csIGNsYXNzVG9BZGQpO1xyXG5cdFx0XHRuZXdTaGFkb3cuYWx0ID0gJyc7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9zaGFkb3cgPSBuZXdTaGFkb3c7XHJcblxyXG5cclxuXHRcdGlmIChvcHRpb25zLm9wYWNpdHkgPCAxKSB7XHJcblx0XHRcdHRoaXMuX3VwZGF0ZU9wYWNpdHkoKTtcclxuXHRcdH1cclxuXHJcblxyXG5cdFx0aWYgKGFkZEljb24pIHtcclxuXHRcdFx0dGhpcy5nZXRQYW5lKCkuYXBwZW5kQ2hpbGQodGhpcy5faWNvbik7XHJcblx0XHR9XHJcblx0XHR0aGlzLl9pbml0SW50ZXJhY3Rpb24oKTtcclxuXHRcdGlmIChuZXdTaGFkb3cgJiYgYWRkU2hhZG93KSB7XHJcblx0XHRcdHRoaXMuZ2V0UGFuZSgnc2hhZG93UGFuZScpLmFwcGVuZENoaWxkKHRoaXMuX3NoYWRvdyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3JlbW92ZUljb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLm9wdGlvbnMucmlzZU9uSG92ZXIpIHtcclxuXHRcdFx0dGhpcy5vZmYoe1xyXG5cdFx0XHRcdG1vdXNlb3ZlcjogdGhpcy5fYnJpbmdUb0Zyb250LFxyXG5cdFx0XHRcdG1vdXNlb3V0OiB0aGlzLl9yZXNldFpJbmRleFxyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwucmVtb3ZlKHRoaXMuX2ljb24pO1xyXG5cdFx0dGhpcy5yZW1vdmVJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl9pY29uKTtcclxuXHJcblx0XHR0aGlzLl9pY29uID0gbnVsbDtcclxuXHR9LFxyXG5cclxuXHRfcmVtb3ZlU2hhZG93OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRpZiAodGhpcy5fc2hhZG93KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fc2hhZG93KTtcclxuXHRcdH1cclxuXHRcdHRoaXMuX3NoYWRvdyA9IG51bGw7XHJcblx0fSxcclxuXHJcblx0X3NldFBvczogZnVuY3Rpb24gKHBvcykge1xyXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2ljb24sIHBvcyk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3NoYWRvdykge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fc2hhZG93LCBwb3MpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX3pJbmRleCA9IHBvcy55ICsgdGhpcy5vcHRpb25zLnpJbmRleE9mZnNldDtcclxuXHJcblx0XHR0aGlzLl9yZXNldFpJbmRleCgpO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVaSW5kZXg6IGZ1bmN0aW9uIChvZmZzZXQpIHtcclxuXHRcdHRoaXMuX2ljb24uc3R5bGUuekluZGV4ID0gdGhpcy5fekluZGV4ICsgb2Zmc2V0O1xyXG5cdH0sXHJcblxyXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKG9wdCkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5fbGF0TG5nVG9OZXdMYXllclBvaW50KHRoaXMuX2xhdGxuZywgb3B0Lnpvb20sIG9wdC5jZW50ZXIpLnJvdW5kKCk7XHJcblxyXG5cdFx0dGhpcy5fc2V0UG9zKHBvcyk7XHJcblx0fSxcclxuXHJcblx0X2luaXRJbnRlcmFjdGlvbjogZnVuY3Rpb24gKCkge1xyXG5cclxuXHRcdGlmICghdGhpcy5vcHRpb25zLmludGVyYWN0aXZlKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9pY29uLCAnbGVhZmxldC1pbnRlcmFjdGl2ZScpO1xyXG5cclxuXHRcdHRoaXMuYWRkSW50ZXJhY3RpdmVUYXJnZXQodGhpcy5faWNvbik7XHJcblxyXG5cdFx0aWYgKEwuSGFuZGxlci5NYXJrZXJEcmFnKSB7XHJcblx0XHRcdHZhciBkcmFnZ2FibGUgPSB0aGlzLm9wdGlvbnMuZHJhZ2dhYmxlO1xyXG5cdFx0XHRpZiAodGhpcy5kcmFnZ2luZykge1xyXG5cdFx0XHRcdGRyYWdnYWJsZSA9IHRoaXMuZHJhZ2dpbmcuZW5hYmxlZCgpO1xyXG5cdFx0XHRcdHRoaXMuZHJhZ2dpbmcuZGlzYWJsZSgpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHR0aGlzLmRyYWdnaW5nID0gbmV3IEwuSGFuZGxlci5NYXJrZXJEcmFnKHRoaXMpO1xyXG5cclxuXHRcdFx0aWYgKGRyYWdnYWJsZSkge1xyXG5cdFx0XHRcdHRoaXMuZHJhZ2dpbmcuZW5hYmxlKCk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldE9wYWNpdHkob3BhY2l0eTogTnVtYmVyKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgdGhlIG9wYWNpdHkgb2YgdGhlIG1hcmtlci5cclxuXHRzZXRPcGFjaXR5OiBmdW5jdGlvbiAob3BhY2l0eSkge1xyXG5cdFx0dGhpcy5vcHRpb25zLm9wYWNpdHkgPSBvcGFjaXR5O1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHR0aGlzLl91cGRhdGVPcGFjaXR5KCk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZU9wYWNpdHk6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBvcGFjaXR5ID0gdGhpcy5vcHRpb25zLm9wYWNpdHk7XHJcblxyXG5cdFx0TC5Eb21VdGlsLnNldE9wYWNpdHkodGhpcy5faWNvbiwgb3BhY2l0eSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX3NoYWRvdykge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9zaGFkb3csIG9wYWNpdHkpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9icmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHRoaXMuX3VwZGF0ZVpJbmRleCh0aGlzLm9wdGlvbnMucmlzZU9mZnNldCk7XHJcblx0fSxcclxuXHJcblx0X3Jlc2V0WkluZGV4OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl91cGRhdGVaSW5kZXgoMCk7XHJcblx0fSxcclxuXHJcblx0X2dldFBvcHVwQW5jaG9yOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5vcHRpb25zLmljb24ub3B0aW9ucy5wb3B1cEFuY2hvciB8fCBbMCwgMF07XHJcblx0fSxcclxuXHJcblx0X2dldFRvb2x0aXBBbmNob3I6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMuaWNvbi5vcHRpb25zLnRvb2x0aXBBbmNob3IgfHwgWzAsIDBdO1xyXG5cdH1cclxufSk7XHJcblxyXG5cclxuLy8gZmFjdG9yeSBMLm1hcmtlcihsYXRsbmc6IExhdExuZywgb3B0aW9ucz8gOiBNYXJrZXIgb3B0aW9ucylcclxuXHJcbi8vIEBmYWN0b3J5IEwubWFya2VyKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPyA6IE1hcmtlciBvcHRpb25zKVxyXG4vLyBJbnN0YW50aWF0ZXMgYSBNYXJrZXIgb2JqZWN0IGdpdmVuIGEgZ2VvZ3JhcGhpY2FsIHBvaW50IGFuZCBvcHRpb25hbGx5IGFuIG9wdGlvbnMgb2JqZWN0LlxyXG5MLm1hcmtlciA9IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuTWFya2VyKGxhdGxuZywgb3B0aW9ucyk7XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgRGl2SWNvblxuICogQGFrYSBMLkRpdkljb25cbiAqIEBpbmhlcml0cyBJY29uXG4gKlxuICogUmVwcmVzZW50cyBhIGxpZ2h0d2VpZ2h0IGljb24gZm9yIG1hcmtlcnMgdGhhdCB1c2VzIGEgc2ltcGxlIGA8ZGl2PmBcbiAqIGVsZW1lbnQgaW5zdGVhZCBvZiBhbiBpbWFnZS4gSW5oZXJpdHMgZnJvbSBgSWNvbmAgYnV0IGlnbm9yZXMgdGhlIGBpY29uVXJsYCBhbmQgc2hhZG93IG9wdGlvbnMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYGpzXG4gKiB2YXIgbXlJY29uID0gTC5kaXZJY29uKHtjbGFzc05hbWU6ICdteS1kaXYtaWNvbid9KTtcbiAqIC8vIHlvdSBjYW4gc2V0IC5teS1kaXYtaWNvbiBzdHlsZXMgaW4gQ1NTXG4gKlxuICogTC5tYXJrZXIoWzUwLjUwNSwgMzAuNTddLCB7aWNvbjogbXlJY29ufSkuYWRkVG8obWFwKTtcbiAqIGBgYFxuICpcbiAqIEJ5IGRlZmF1bHQsIGl0IGhhcyBhICdsZWFmbGV0LWRpdi1pY29uJyBDU1MgY2xhc3MgYW5kIGlzIHN0eWxlZCBhcyBhIGxpdHRsZSB3aGl0ZSBzcXVhcmUgd2l0aCBhIHNoYWRvdy5cbiAqL1xuXG5MLkRpdkljb24gPSBMLkljb24uZXh0ZW5kKHtcblx0b3B0aW9uczoge1xuXHRcdC8vIEBzZWN0aW9uXG5cdFx0Ly8gQGFrYSBEaXZJY29uIG9wdGlvbnNcblx0XHRpY29uU2l6ZTogWzEyLCAxMl0sIC8vIGFsc28gY2FuIGJlIHNldCB0aHJvdWdoIENTU1xuXG5cdFx0Ly8gaWNvbkFuY2hvcjogKFBvaW50KSxcblx0XHQvLyBwb3B1cEFuY2hvcjogKFBvaW50KSxcblxuXHRcdC8vIEBvcHRpb24gaHRtbDogU3RyaW5nID0gJydcblx0XHQvLyBDdXN0b20gSFRNTCBjb2RlIHRvIHB1dCBpbnNpZGUgdGhlIGRpdiBlbGVtZW50LCBlbXB0eSBieSBkZWZhdWx0LlxuXHRcdGh0bWw6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBiZ1BvczogUG9pbnQgPSBbMCwgMF1cblx0XHQvLyBPcHRpb25hbCByZWxhdGl2ZSBwb3NpdGlvbiBvZiB0aGUgYmFja2dyb3VuZCwgaW4gcGl4ZWxzXG5cdFx0YmdQb3M6IG51bGwsXG5cblx0XHRjbGFzc05hbWU6ICdsZWFmbGV0LWRpdi1pY29uJ1xuXHR9LFxuXG5cdGNyZWF0ZUljb246IGZ1bmN0aW9uIChvbGRJY29uKSB7XG5cdFx0dmFyIGRpdiA9IChvbGRJY29uICYmIG9sZEljb24udGFnTmFtZSA9PT0gJ0RJVicpID8gb2xkSWNvbiA6IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpLFxuXHRcdCAgICBvcHRpb25zID0gdGhpcy5vcHRpb25zO1xuXG5cdFx0ZGl2LmlubmVySFRNTCA9IG9wdGlvbnMuaHRtbCAhPT0gZmFsc2UgPyBvcHRpb25zLmh0bWwgOiAnJztcblxuXHRcdGlmIChvcHRpb25zLmJnUG9zKSB7XG5cdFx0XHR2YXIgYmdQb3MgPSBMLnBvaW50KG9wdGlvbnMuYmdQb3MpO1xuXHRcdFx0ZGl2LnN0eWxlLmJhY2tncm91bmRQb3NpdGlvbiA9ICgtYmdQb3MueCkgKyAncHggJyArICgtYmdQb3MueSkgKyAncHgnO1xuXHRcdH1cblx0XHR0aGlzLl9zZXRJY29uU3R5bGVzKGRpdiwgJ2ljb24nKTtcblxuXHRcdHJldHVybiBkaXY7XG5cdH0sXG5cblx0Y3JlYXRlU2hhZG93OiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cbn0pO1xuXG4vLyBAZmFjdG9yeSBMLmRpdkljb24ob3B0aW9uczogRGl2SWNvbiBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhIGBEaXZJY29uYCBpbnN0YW5jZSB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuTC5kaXZJY29uID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcblx0cmV0dXJuIG5ldyBMLkRpdkljb24ob3B0aW9ucyk7XG59O1xuXG5cblxuLypcclxuICogQGNsYXNzIERpdk92ZXJsYXlcclxuICogQGluaGVyaXRzIExheWVyXHJcbiAqIEBha2EgTC5EaXZPdmVybGF5XHJcbiAqIEJhc2UgbW9kZWwgZm9yIEwuUG9wdXAgYW5kIEwuVG9vbHRpcC4gSW5oZXJpdCBmcm9tIGl0IGZvciBjdXN0b20gcG9wdXAgbGlrZSBwbHVnaW5zLlxyXG4gKi9cclxuXHJcbi8vIEBuYW1lc3BhY2UgRGl2T3ZlcmxheVxyXG5MLkRpdk92ZXJsYXkgPSBMLkxheWVyLmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBEaXZPdmVybGF5IG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIG9mZnNldDogUG9pbnQgPSBQb2ludCgwLCA3KVxyXG5cdFx0Ly8gVGhlIG9mZnNldCBvZiB0aGUgcG9wdXAgcG9zaXRpb24uIFVzZWZ1bCB0byBjb250cm9sIHRoZSBhbmNob3JcclxuXHRcdC8vIG9mIHRoZSBwb3B1cCB3aGVuIG9wZW5pbmcgaXQgb24gc29tZSBvdmVybGF5cy5cclxuXHRcdG9mZnNldDogWzAsIDddLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY2xhc3NOYW1lOiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gQSBjdXN0b20gQ1NTIGNsYXNzIG5hbWUgdG8gYXNzaWduIHRvIHRoZSBwb3B1cC5cclxuXHRcdGNsYXNzTmFtZTogJycsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAncG9wdXBQYW5lJ1xyXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgcG9wdXAgd2lsbCBiZSBhZGRlZC5cclxuXHRcdHBhbmU6ICdwb3B1cFBhbmUnXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG9wdGlvbnMsIHNvdXJjZSkge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX3NvdXJjZSA9IHNvdXJjZTtcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dGhpcy5fem9vbUFuaW1hdGVkID0gbWFwLl96b29tQW5pbWF0ZWQ7XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9jb250YWluZXIpIHtcclxuXHRcdFx0dGhpcy5faW5pdExheW91dCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChtYXAuX2ZhZGVBbmltYXRlZCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIDApO1xyXG5cdFx0fVxyXG5cclxuXHRcdGNsZWFyVGltZW91dCh0aGlzLl9yZW1vdmVUaW1lb3V0KTtcclxuXHRcdHRoaXMuZ2V0UGFuZSgpLmFwcGVuZENoaWxkKHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHR0aGlzLnVwZGF0ZSgpO1xyXG5cclxuXHRcdGlmIChtYXAuX2ZhZGVBbmltYXRlZCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIDEpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuYnJpbmdUb0Zyb250KCk7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdGlmIChtYXAuX2ZhZGVBbmltYXRlZCkge1xyXG5cdFx0XHRMLkRvbVV0aWwuc2V0T3BhY2l0eSh0aGlzLl9jb250YWluZXIsIDApO1xyXG5cdFx0XHR0aGlzLl9yZW1vdmVUaW1lb3V0ID0gc2V0VGltZW91dChMLmJpbmQoTC5Eb21VdGlsLnJlbW92ZSwgTC5Eb21VdGlsLCB0aGlzLl9jb250YWluZXIpLCAyMDApO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250YWluZXIpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBuYW1lc3BhY2UgUG9wdXBcclxuXHQvLyBAbWV0aG9kIGdldExhdExuZzogTGF0TG5nXHJcblx0Ly8gUmV0dXJucyB0aGUgZ2VvZ3JhcGhpY2FsIHBvaW50IG9mIHBvcHVwLlxyXG5cdGdldExhdExuZzogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuX2xhdGxuZztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldExhdExuZyhsYXRsbmc6IExhdExuZyk6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBnZW9ncmFwaGljYWwgcG9pbnQgd2hlcmUgdGhlIHBvcHVwIHdpbGwgb3Blbi5cclxuXHRzZXRMYXRMbmc6IGZ1bmN0aW9uIChsYXRsbmcpIHtcclxuXHRcdHRoaXMuX2xhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdHRoaXMuX3VwZGF0ZVBvc2l0aW9uKCk7XHJcblx0XHRcdHRoaXMuX2FkanVzdFBhbigpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBnZXRDb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnRcclxuXHQvLyBSZXR1cm5zIHRoZSBjb250ZW50IG9mIHRoZSBwb3B1cC5cclxuXHRnZXRDb250ZW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGVudDtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldENvbnRlbnQoaHRtbENvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxGdW5jdGlvbik6IHRoaXNcclxuXHQvLyBTZXRzIHRoZSBIVE1MIGNvbnRlbnQgb2YgdGhlIHBvcHVwLiBJZiBhIGZ1bmN0aW9uIGlzIHBhc3NlZCB0aGUgc291cmNlIGxheWVyIHdpbGwgYmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi4gVGhlIGZ1bmN0aW9uIHNob3VsZCByZXR1cm4gYSBgU3RyaW5nYCBvciBgSFRNTEVsZW1lbnRgIHRvIGJlIHVzZWQgaW4gdGhlIHBvcHVwLlxyXG5cdHNldENvbnRlbnQ6IGZ1bmN0aW9uIChjb250ZW50KSB7XHJcblx0XHR0aGlzLl9jb250ZW50ID0gY29udGVudDtcclxuXHRcdHRoaXMudXBkYXRlKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldEVsZW1lbnQ6IFN0cmluZ3xIVE1MRWxlbWVudFxyXG5cdC8vIEFsaWFzIGZvciBbZ2V0Q29udGVudCgpXSgjcG9wdXAtZ2V0Y29udGVudClcclxuXHRnZXRFbGVtZW50OiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdXBkYXRlOiBudWxsXHJcblx0Ly8gVXBkYXRlcyB0aGUgcG9wdXAgY29udGVudCwgbGF5b3V0IGFuZCBwb3NpdGlvbi4gVXNlZnVsIGZvciB1cGRhdGluZyB0aGUgcG9wdXAgYWZ0ZXIgc29tZXRoaW5nIGluc2lkZSBjaGFuZ2VkLCBlLmcuIGltYWdlIGxvYWRlZC5cclxuXHR1cGRhdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fbWFwKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHRoaXMuX2NvbnRhaW5lci5zdHlsZS52aXNpYmlsaXR5ID0gJ2hpZGRlbic7XHJcblxyXG5cdFx0dGhpcy5fdXBkYXRlQ29udGVudCgpO1xyXG5cdFx0dGhpcy5fdXBkYXRlTGF5b3V0KCk7XHJcblx0XHR0aGlzLl91cGRhdGVQb3NpdGlvbigpO1xyXG5cclxuXHRcdHRoaXMuX2NvbnRhaW5lci5zdHlsZS52aXNpYmlsaXR5ID0gJyc7XHJcblxyXG5cdFx0dGhpcy5fYWRqdXN0UGFuKCk7XHJcblx0fSxcclxuXHJcblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZXZlbnRzID0ge1xyXG5cdFx0XHR6b29tOiB0aGlzLl91cGRhdGVQb3NpdGlvbixcclxuXHRcdFx0dmlld3Jlc2V0OiB0aGlzLl91cGRhdGVQb3NpdGlvblxyXG5cdFx0fTtcclxuXHJcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XHJcblx0XHRcdGV2ZW50cy56b29tYW5pbSA9IHRoaXMuX2FuaW1hdGVab29tO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGV2ZW50cztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGlzT3BlbjogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIHdoZW4gdGhlIHBvcHVwIGlzIHZpc2libGUgb24gdGhlIG1hcC5cclxuXHRpc09wZW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAhIXRoaXMuX21hcCAmJiB0aGlzLl9tYXAuaGFzTGF5ZXIodGhpcyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvRnJvbnQ6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhpcyBwb3B1cCBpbiBmcm9udCBvZiBvdGhlciBwb3B1cHMgKGluIHRoZSBzYW1lIG1hcCBwYW5lKS5cclxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0TC5Eb21VdGlsLnRvRnJvbnQodGhpcy5fY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYnJpbmdUb0JhY2s6IHRoaXNcclxuXHQvLyBCcmluZ3MgdGhpcyBwb3B1cCB0byB0aGUgYmFjayBvZiBvdGhlciBwb3B1cHMgKGluIHRoZSBzYW1lIG1hcCBwYW5lKS5cclxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX21hcCkge1xyXG5cdFx0XHRMLkRvbVV0aWwudG9CYWNrKHRoaXMuX2NvbnRhaW5lcik7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfdXBkYXRlQ29udGVudDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9jb250ZW50KSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciBub2RlID0gdGhpcy5fY29udGVudE5vZGU7XHJcblx0XHR2YXIgY29udGVudCA9ICh0eXBlb2YgdGhpcy5fY29udGVudCA9PT0gJ2Z1bmN0aW9uJykgPyB0aGlzLl9jb250ZW50KHRoaXMuX3NvdXJjZSB8fCB0aGlzKSA6IHRoaXMuX2NvbnRlbnQ7XHJcblxyXG5cdFx0aWYgKHR5cGVvZiBjb250ZW50ID09PSAnc3RyaW5nJykge1xyXG5cdFx0XHRub2RlLmlubmVySFRNTCA9IGNvbnRlbnQ7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHR3aGlsZSAobm9kZS5oYXNDaGlsZE5vZGVzKCkpIHtcclxuXHRcdFx0XHRub2RlLnJlbW92ZUNoaWxkKG5vZGUuZmlyc3RDaGlsZCk7XHJcblx0XHRcdH1cclxuXHRcdFx0bm9kZS5hcHBlbmRDaGlsZChjb250ZW50KTtcclxuXHRcdH1cclxuXHRcdHRoaXMuZmlyZSgnY29udGVudHVwZGF0ZScpO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGVQb3NpdGlvbjogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQodGhpcy5fbGF0bG5nKSxcclxuXHRcdCAgICBvZmZzZXQgPSBMLnBvaW50KHRoaXMub3B0aW9ucy5vZmZzZXQpLFxyXG5cdFx0ICAgIGFuY2hvciA9IHRoaXMuX2dldEFuY2hvcigpO1xyXG5cclxuXHRcdGlmICh0aGlzLl96b29tQW5pbWF0ZWQpIHtcclxuXHRcdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lciwgcG9zLmFkZChhbmNob3IpKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdG9mZnNldCA9IG9mZnNldC5hZGQocG9zKS5hZGQoYW5jaG9yKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgYm90dG9tID0gdGhpcy5fY29udGFpbmVyQm90dG9tID0gLW9mZnNldC55LFxyXG5cdFx0ICAgIGxlZnQgPSB0aGlzLl9jb250YWluZXJMZWZ0ID0gLU1hdGgucm91bmQodGhpcy5fY29udGFpbmVyV2lkdGggLyAyKSArIG9mZnNldC54O1xyXG5cclxuXHRcdC8vIGJvdHRvbSBwb3NpdGlvbiB0aGUgcG9wdXAgaW4gY2FzZSB0aGUgaGVpZ2h0IG9mIHRoZSBwb3B1cCBjaGFuZ2VzIChpbWFnZXMgbG9hZGluZyBldGMpXHJcblx0XHR0aGlzLl9jb250YWluZXIuc3R5bGUuYm90dG9tID0gYm90dG9tICsgJ3B4JztcclxuXHRcdHRoaXMuX2NvbnRhaW5lci5zdHlsZS5sZWZ0ID0gbGVmdCArICdweCc7XHJcblx0fSxcclxuXHJcblx0X2dldEFuY2hvcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIFswLCAwXTtcclxuXHR9XHJcblxyXG59KTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIFBvcHVwXHJcbiAqIEBpbmhlcml0cyBEaXZPdmVybGF5XHJcbiAqIEBha2EgTC5Qb3B1cFxyXG4gKiBVc2VkIHRvIG9wZW4gcG9wdXBzIGluIGNlcnRhaW4gcGxhY2VzIG9mIHRoZSBtYXAuIFVzZSBbTWFwLm9wZW5Qb3B1cF0oI21hcC1vcGVucG9wdXApIHRvXHJcbiAqIG9wZW4gcG9wdXBzIHdoaWxlIG1ha2luZyBzdXJlIHRoYXQgb25seSBvbmUgcG9wdXAgaXMgb3BlbiBhdCBvbmUgdGltZVxyXG4gKiAocmVjb21tZW5kZWQgZm9yIHVzYWJpbGl0eSksIG9yIHVzZSBbTWFwLmFkZExheWVyXSgjbWFwLWFkZGxheWVyKSB0byBvcGVuIGFzIG1hbnkgYXMgeW91IHdhbnQuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIElmIHlvdSB3YW50IHRvIGp1c3QgYmluZCBhIHBvcHVwIHRvIG1hcmtlciBjbGljayBhbmQgdGhlbiBvcGVuIGl0LCBpdCdzIHJlYWxseSBlYXN5OlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBtYXJrZXIuYmluZFBvcHVwKHBvcHVwQ29udGVudCkub3BlblBvcHVwKCk7XHJcbiAqIGBgYFxyXG4gKiBQYXRoIG92ZXJsYXlzIGxpa2UgcG9seWxpbmVzIGFsc28gaGF2ZSBhIGBiaW5kUG9wdXBgIG1ldGhvZC5cclxuICogSGVyZSdzIGEgbW9yZSBjb21wbGljYXRlZCB3YXkgdG8gb3BlbiBhIHBvcHVwIG9uIGEgbWFwOlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgcG9wdXAgPSBMLnBvcHVwKClcclxuICogXHQuc2V0TGF0TG5nKGxhdGxuZylcclxuICogXHQuc2V0Q29udGVudCgnPHA+SGVsbG8gd29ybGQhPGJyIC8+VGhpcyBpcyBhIG5pY2UgcG9wdXAuPC9wPicpXHJcbiAqIFx0Lm9wZW5PbihtYXApO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5cclxuLy8gQG5hbWVzcGFjZSBQb3B1cFxyXG5MLlBvcHVwID0gTC5EaXZPdmVybGF5LmV4dGVuZCh7XHJcblxyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBQb3B1cCBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0Ly8gQG9wdGlvbiBtYXhXaWR0aDogTnVtYmVyID0gMzAwXHJcblx0XHQvLyBNYXggd2lkdGggb2YgdGhlIHBvcHVwLCBpbiBwaXhlbHMuXHJcblx0XHRtYXhXaWR0aDogMzAwLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gbWluV2lkdGg6IE51bWJlciA9IDUwXHJcblx0XHQvLyBNaW4gd2lkdGggb2YgdGhlIHBvcHVwLCBpbiBwaXhlbHMuXHJcblx0XHRtaW5XaWR0aDogNTAsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBtYXhIZWlnaHQ6IE51bWJlciA9IG51bGxcclxuXHRcdC8vIElmIHNldCwgY3JlYXRlcyBhIHNjcm9sbGFibGUgY29udGFpbmVyIG9mIHRoZSBnaXZlbiBoZWlnaHRcclxuXHRcdC8vIGluc2lkZSBhIHBvcHVwIGlmIGl0cyBjb250ZW50IGV4Y2VlZHMgaXQuXHJcblx0XHRtYXhIZWlnaHQ6IG51bGwsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiBhdXRvUGFuOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGBmYWxzZWAgaWYgeW91IGRvbid0IHdhbnQgdGhlIG1hcCB0byBkbyBwYW5uaW5nIGFuaW1hdGlvblxyXG5cdFx0Ly8gdG8gZml0IHRoZSBvcGVuZWQgcG9wdXAuXHJcblx0XHRhdXRvUGFuOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmdUb3BMZWZ0OiBQb2ludCA9IG51bGxcclxuXHRcdC8vIFRoZSBtYXJnaW4gYmV0d2VlbiB0aGUgcG9wdXAgYW5kIHRoZSB0b3AgbGVmdCBjb3JuZXIgb2YgdGhlIG1hcFxyXG5cdFx0Ly8gdmlldyBhZnRlciBhdXRvcGFubmluZyB3YXMgcGVyZm9ybWVkLlxyXG5cdFx0YXV0b1BhblBhZGRpbmdUb3BMZWZ0OiBudWxsLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmdCb3R0b21SaWdodDogUG9pbnQgPSBudWxsXHJcblx0XHQvLyBUaGUgbWFyZ2luIGJldHdlZW4gdGhlIHBvcHVwIGFuZCB0aGUgYm90dG9tIHJpZ2h0IGNvcm5lciBvZiB0aGUgbWFwXHJcblx0XHQvLyB2aWV3IGFmdGVyIGF1dG9wYW5uaW5nIHdhcyBwZXJmb3JtZWQuXHJcblx0XHRhdXRvUGFuUGFkZGluZ0JvdHRvbVJpZ2h0OiBudWxsLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1BhblBhZGRpbmc6IFBvaW50ID0gUG9pbnQoNSwgNSlcclxuXHRcdC8vIEVxdWl2YWxlbnQgb2Ygc2V0dGluZyBib3RoIHRvcCBsZWZ0IGFuZCBib3R0b20gcmlnaHQgYXV0b3BhbiBwYWRkaW5nIHRvIHRoZSBzYW1lIHZhbHVlLlxyXG5cdFx0YXV0b1BhblBhZGRpbmc6IFs1LCA1XSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGtlZXBJblZpZXc6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGB0cnVlYCBpZiB5b3Ugd2FudCB0byBwcmV2ZW50IHVzZXJzIGZyb20gcGFubmluZyB0aGUgcG9wdXBcclxuXHRcdC8vIG9mZiBvZiB0aGUgc2NyZWVuIHdoaWxlIGl0IGlzIG9wZW4uXHJcblx0XHRrZWVwSW5WaWV3OiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIGNsb3NlQnV0dG9uOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gQ29udHJvbHMgdGhlIHByZXNlbmNlIG9mIGEgY2xvc2UgYnV0dG9uIGluIHRoZSBwb3B1cC5cclxuXHRcdGNsb3NlQnV0dG9uOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b0Nsb3NlOiBCb29sZWFuID0gdHJ1ZVxyXG5cdFx0Ly8gU2V0IGl0IHRvIGBmYWxzZWAgaWYgeW91IHdhbnQgdG8gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgYmVoYXZpb3Igb2ZcclxuXHRcdC8vIHRoZSBwb3B1cCBjbG9zaW5nIHdoZW4gdXNlciBjbGlja3MgdGhlIG1hcCAoc2V0IGdsb2JhbGx5IGJ5XHJcblx0XHQvLyB0aGUgTWFwJ3MgW2Nsb3NlUG9wdXBPbkNsaWNrXSgjbWFwLWNsb3NlcG9wdXBvbmNsaWNrKSBvcHRpb24pLlxyXG5cdFx0YXV0b0Nsb3NlOiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gY2xhc3NOYW1lOiBTdHJpbmcgPSAnJ1xyXG5cdFx0Ly8gQSBjdXN0b20gQ1NTIGNsYXNzIG5hbWUgdG8gYXNzaWduIHRvIHRoZSBwb3B1cC5cclxuXHRcdGNsYXNzTmFtZTogJydcclxuXHR9LFxyXG5cclxuXHQvLyBAbmFtZXNwYWNlIFBvcHVwXHJcblx0Ly8gQG1ldGhvZCBvcGVuT24obWFwOiBNYXApOiB0aGlzXHJcblx0Ly8gQWRkcyB0aGUgcG9wdXAgdG8gdGhlIG1hcCBhbmQgY2xvc2VzIHRoZSBwcmV2aW91cyBvbmUuIFRoZSBzYW1lIGFzIGBtYXAub3BlblBvcHVwKHBvcHVwKWAuXHJcblx0b3Blbk9uOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRtYXAub3BlblBvcHVwKHRoaXMpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0b25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdEwuRGl2T3ZlcmxheS5wcm90b3R5cGUub25BZGQuY2FsbCh0aGlzLCBtYXApO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdC8vIEBldmVudCBwb3B1cG9wZW46IFBvcHVwRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gYSBwb3B1cCBpcyBvcGVuZWQgaW4gdGhlIG1hcFxyXG5cdFx0bWFwLmZpcmUoJ3BvcHVwb3BlbicsIHtwb3B1cDogdGhpc30pO1xyXG5cclxuXHRcdGlmICh0aGlzLl9zb3VyY2UpIHtcclxuXHRcdFx0Ly8gQG5hbWVzcGFjZSBMYXllclxyXG5cdFx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdFx0Ly8gQGV2ZW50IHBvcHVwb3BlbjogUG9wdXBFdmVudFxyXG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllciBpcyBvcGVuZWRcclxuXHRcdFx0dGhpcy5fc291cmNlLmZpcmUoJ3BvcHVwb3BlbicsIHtwb3B1cDogdGhpc30sIHRydWUpO1xyXG5cdFx0XHQvLyBGb3Igbm9uLXBhdGggbGF5ZXJzLCB3ZSB0b2dnbGUgdGhlIHBvcHVwIHdoZW4gY2xpY2tpbmdcclxuXHRcdFx0Ly8gYWdhaW4gdGhlIGxheWVyLCBzbyBwcmV2ZW50IHRoZSBtYXAgdG8gcmVvcGVuIGl0LlxyXG5cdFx0XHRpZiAoISh0aGlzLl9zb3VyY2UgaW5zdGFuY2VvZiBMLlBhdGgpKSB7XHJcblx0XHRcdFx0dGhpcy5fc291cmNlLm9uKCdwcmVjbGljaycsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRMLkRpdk92ZXJsYXkucHJvdG90eXBlLm9uUmVtb3ZlLmNhbGwodGhpcywgbWFwKTtcclxuXHJcblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxyXG5cdFx0Ly8gQHNlY3Rpb24gUG9wdXAgZXZlbnRzXHJcblx0XHQvLyBAZXZlbnQgcG9wdXBjbG9zZTogUG9wdXBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhIHBvcHVwIGluIHRoZSBtYXAgaXMgY2xvc2VkXHJcblx0XHRtYXAuZmlyZSgncG9wdXBjbG9zZScsIHtwb3B1cDogdGhpc30pO1xyXG5cclxuXHRcdGlmICh0aGlzLl9zb3VyY2UpIHtcclxuXHRcdFx0Ly8gQG5hbWVzcGFjZSBMYXllclxyXG5cdFx0XHQvLyBAc2VjdGlvbiBQb3B1cCBldmVudHNcclxuXHRcdFx0Ly8gQGV2ZW50IHBvcHVwY2xvc2U6IFBvcHVwRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgd2hlbiBhIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgY2xvc2VkXHJcblx0XHRcdHRoaXMuX3NvdXJjZS5maXJlKCdwb3B1cGNsb3NlJywge3BvcHVwOiB0aGlzfSwgdHJ1ZSk7XHJcblx0XHRcdGlmICghKHRoaXMuX3NvdXJjZSBpbnN0YW5jZW9mIEwuUGF0aCkpIHtcclxuXHRcdFx0XHR0aGlzLl9zb3VyY2Uub2ZmKCdwcmVjbGljaycsIEwuRG9tRXZlbnQuc3RvcFByb3BhZ2F0aW9uKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGV2ZW50cyA9IEwuRGl2T3ZlcmxheS5wcm90b3R5cGUuZ2V0RXZlbnRzLmNhbGwodGhpcyk7XHJcblxyXG5cdFx0aWYgKCdjbG9zZU9uQ2xpY2snIGluIHRoaXMub3B0aW9ucyA/IHRoaXMub3B0aW9ucy5jbG9zZU9uQ2xpY2sgOiB0aGlzLl9tYXAub3B0aW9ucy5jbG9zZVBvcHVwT25DbGljaykge1xyXG5cdFx0XHRldmVudHMucHJlY2xpY2sgPSB0aGlzLl9jbG9zZTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmtlZXBJblZpZXcpIHtcclxuXHRcdFx0ZXZlbnRzLm1vdmVlbmQgPSB0aGlzLl9hZGp1c3RQYW47XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIGV2ZW50cztcclxuXHR9LFxyXG5cclxuXHRfY2xvc2U6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9tYXApIHtcclxuXHRcdFx0dGhpcy5fbWFwLmNsb3NlUG9wdXAodGhpcyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2luaXRMYXlvdXQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBwcmVmaXggPSAnbGVhZmxldC1wb3B1cCcsXHJcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JyxcclxuXHRcdFx0cHJlZml4ICsgJyAnICsgKHRoaXMub3B0aW9ucy5jbGFzc05hbWUgfHwgJycpICtcclxuXHRcdFx0JyBsZWFmbGV0LXpvb20tYW5pbWF0ZWQnKTtcclxuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmNsb3NlQnV0dG9uKSB7XHJcblx0XHRcdHZhciBjbG9zZUJ1dHRvbiA9IHRoaXMuX2Nsb3NlQnV0dG9uID0gTC5Eb21VdGlsLmNyZWF0ZSgnYScsIHByZWZpeCArICctY2xvc2UtYnV0dG9uJywgY29udGFpbmVyKTtcclxuXHRcdFx0Y2xvc2VCdXR0b24uaHJlZiA9ICcjY2xvc2UnO1xyXG5cdFx0XHRjbG9zZUJ1dHRvbi5pbm5lckhUTUwgPSAnJiMyMTU7JztcclxuXHJcblx0XHRcdEwuRG9tRXZlbnQub24oY2xvc2VCdXR0b24sICdjbGljaycsIHRoaXMuX29uQ2xvc2VCdXR0b25DbGljaywgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHdyYXBwZXIgPSB0aGlzLl93cmFwcGVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgcHJlZml4ICsgJy1jb250ZW50LXdyYXBwZXInLCBjb250YWluZXIpO1xyXG5cdFx0dGhpcy5fY29udGVudE5vZGUgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBwcmVmaXggKyAnLWNvbnRlbnQnLCB3cmFwcGVyKTtcclxuXHJcblx0XHRMLkRvbUV2ZW50XHJcblx0XHRcdC5kaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbih3cmFwcGVyKVxyXG5cdFx0XHQuZGlzYWJsZVNjcm9sbFByb3BhZ2F0aW9uKHRoaXMuX2NvbnRlbnROb2RlKVxyXG5cdFx0XHQub24od3JhcHBlciwgJ2NvbnRleHRtZW51JywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pO1xyXG5cclxuXHRcdHRoaXMuX3RpcENvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIHByZWZpeCArICctdGlwLWNvbnRhaW5lcicsIGNvbnRhaW5lcik7XHJcblx0XHR0aGlzLl90aXAgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBwcmVmaXggKyAnLXRpcCcsIHRoaXMuX3RpcENvbnRhaW5lcik7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZUxheW91dDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRlbnROb2RlLFxyXG5cdFx0ICAgIHN0eWxlID0gY29udGFpbmVyLnN0eWxlO1xyXG5cclxuXHRcdHN0eWxlLndpZHRoID0gJyc7XHJcblx0XHRzdHlsZS53aGl0ZVNwYWNlID0gJ25vd3JhcCc7XHJcblxyXG5cdFx0dmFyIHdpZHRoID0gY29udGFpbmVyLm9mZnNldFdpZHRoO1xyXG5cdFx0d2lkdGggPSBNYXRoLm1pbih3aWR0aCwgdGhpcy5vcHRpb25zLm1heFdpZHRoKTtcclxuXHRcdHdpZHRoID0gTWF0aC5tYXgod2lkdGgsIHRoaXMub3B0aW9ucy5taW5XaWR0aCk7XHJcblxyXG5cdFx0c3R5bGUud2lkdGggPSAod2lkdGggKyAxKSArICdweCc7XHJcblx0XHRzdHlsZS53aGl0ZVNwYWNlID0gJyc7XHJcblxyXG5cdFx0c3R5bGUuaGVpZ2h0ID0gJyc7XHJcblxyXG5cdFx0dmFyIGhlaWdodCA9IGNvbnRhaW5lci5vZmZzZXRIZWlnaHQsXHJcblx0XHQgICAgbWF4SGVpZ2h0ID0gdGhpcy5vcHRpb25zLm1heEhlaWdodCxcclxuXHRcdCAgICBzY3JvbGxlZENsYXNzID0gJ2xlYWZsZXQtcG9wdXAtc2Nyb2xsZWQnO1xyXG5cclxuXHRcdGlmIChtYXhIZWlnaHQgJiYgaGVpZ2h0ID4gbWF4SGVpZ2h0KSB7XHJcblx0XHRcdHN0eWxlLmhlaWdodCA9IG1heEhlaWdodCArICdweCc7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyhjb250YWluZXIsIHNjcm9sbGVkQ2xhc3MpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKGNvbnRhaW5lciwgc2Nyb2xsZWRDbGFzcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fY29udGFpbmVyV2lkdGggPSB0aGlzLl9jb250YWluZXIub2Zmc2V0V2lkdGg7XHJcblx0fSxcclxuXHJcblx0X2FuaW1hdGVab29tOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0dmFyIHBvcyA9IHRoaXMuX21hcC5fbGF0TG5nVG9OZXdMYXllclBvaW50KHRoaXMuX2xhdGxuZywgZS56b29tLCBlLmNlbnRlciksXHJcblx0XHQgICAgYW5jaG9yID0gdGhpcy5fZ2V0QW5jaG9yKCk7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fY29udGFpbmVyLCBwb3MuYWRkKGFuY2hvcikpO1xyXG5cdH0sXHJcblxyXG5cdF9hZGp1c3RQYW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5vcHRpb25zLmF1dG9QYW4gfHwgKHRoaXMuX21hcC5fcGFuQW5pbSAmJiB0aGlzLl9tYXAuX3BhbkFuaW0uX2luUHJvZ3Jlc3MpKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXAsXHJcblx0XHQgICAgbWFyZ2luQm90dG9tID0gcGFyc2VJbnQoTC5Eb21VdGlsLmdldFN0eWxlKHRoaXMuX2NvbnRhaW5lciwgJ21hcmdpbkJvdHRvbScpLCAxMCkgfHwgMCxcclxuXHRcdCAgICBjb250YWluZXJIZWlnaHQgPSB0aGlzLl9jb250YWluZXIub2Zmc2V0SGVpZ2h0ICsgbWFyZ2luQm90dG9tLFxyXG5cdFx0ICAgIGNvbnRhaW5lcldpZHRoID0gdGhpcy5fY29udGFpbmVyV2lkdGgsXHJcblx0XHQgICAgbGF5ZXJQb3MgPSBuZXcgTC5Qb2ludCh0aGlzLl9jb250YWluZXJMZWZ0LCAtY29udGFpbmVySGVpZ2h0IC0gdGhpcy5fY29udGFpbmVyQm90dG9tKTtcclxuXHJcblx0XHRsYXllclBvcy5fYWRkKEwuRG9tVXRpbC5nZXRQb3NpdGlvbih0aGlzLl9jb250YWluZXIpKTtcclxuXHJcblx0XHR2YXIgY29udGFpbmVyUG9zID0gbWFwLmxheWVyUG9pbnRUb0NvbnRhaW5lclBvaW50KGxheWVyUG9zKSxcclxuXHRcdCAgICBwYWRkaW5nID0gTC5wb2ludCh0aGlzLm9wdGlvbnMuYXV0b1BhblBhZGRpbmcpLFxyXG5cdFx0ICAgIHBhZGRpbmdUTCA9IEwucG9pbnQodGhpcy5vcHRpb25zLmF1dG9QYW5QYWRkaW5nVG9wTGVmdCB8fCBwYWRkaW5nKSxcclxuXHRcdCAgICBwYWRkaW5nQlIgPSBMLnBvaW50KHRoaXMub3B0aW9ucy5hdXRvUGFuUGFkZGluZ0JvdHRvbVJpZ2h0IHx8IHBhZGRpbmcpLFxyXG5cdFx0ICAgIHNpemUgPSBtYXAuZ2V0U2l6ZSgpLFxyXG5cdFx0ICAgIGR4ID0gMCxcclxuXHRcdCAgICBkeSA9IDA7XHJcblxyXG5cdFx0aWYgKGNvbnRhaW5lclBvcy54ICsgY29udGFpbmVyV2lkdGggKyBwYWRkaW5nQlIueCA+IHNpemUueCkgeyAvLyByaWdodFxyXG5cdFx0XHRkeCA9IGNvbnRhaW5lclBvcy54ICsgY29udGFpbmVyV2lkdGggLSBzaXplLnggKyBwYWRkaW5nQlIueDtcclxuXHRcdH1cclxuXHRcdGlmIChjb250YWluZXJQb3MueCAtIGR4IC0gcGFkZGluZ1RMLnggPCAwKSB7IC8vIGxlZnRcclxuXHRcdFx0ZHggPSBjb250YWluZXJQb3MueCAtIHBhZGRpbmdUTC54O1xyXG5cdFx0fVxyXG5cdFx0aWYgKGNvbnRhaW5lclBvcy55ICsgY29udGFpbmVySGVpZ2h0ICsgcGFkZGluZ0JSLnkgPiBzaXplLnkpIHsgLy8gYm90dG9tXHJcblx0XHRcdGR5ID0gY29udGFpbmVyUG9zLnkgKyBjb250YWluZXJIZWlnaHQgLSBzaXplLnkgKyBwYWRkaW5nQlIueTtcclxuXHRcdH1cclxuXHRcdGlmIChjb250YWluZXJQb3MueSAtIGR5IC0gcGFkZGluZ1RMLnkgPCAwKSB7IC8vIHRvcFxyXG5cdFx0XHRkeSA9IGNvbnRhaW5lclBvcy55IC0gcGFkZGluZ1RMLnk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gQG5hbWVzcGFjZSBNYXBcclxuXHRcdC8vIEBzZWN0aW9uIFBvcHVwIGV2ZW50c1xyXG5cdFx0Ly8gQGV2ZW50IGF1dG9wYW5zdGFydDogRXZlbnRcclxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcCBzdGFydHMgYXV0b3Bhbm5pbmcgd2hlbiBvcGVuaW5nIGEgcG9wdXAuXHJcblx0XHRpZiAoZHggfHwgZHkpIHtcclxuXHRcdFx0bWFwXHJcblx0XHRcdCAgICAuZmlyZSgnYXV0b3BhbnN0YXJ0JylcclxuXHRcdFx0ICAgIC5wYW5CeShbZHgsIGR5XSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X29uQ2xvc2VCdXR0b25DbGljazogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHRoaXMuX2Nsb3NlKCk7XHJcblx0XHRMLkRvbUV2ZW50LnN0b3AoZSk7XHJcblx0fSxcclxuXHJcblx0X2dldEFuY2hvcjogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gV2hlcmUgc2hvdWxkIHdlIGFuY2hvciB0aGUgcG9wdXAgb24gdGhlIHNvdXJjZSBsYXllcj9cclxuXHRcdHJldHVybiBMLnBvaW50KHRoaXMuX3NvdXJjZSAmJiB0aGlzLl9zb3VyY2UuX2dldFBvcHVwQW5jaG9yID8gdGhpcy5fc291cmNlLl9nZXRQb3B1cEFuY2hvcigpIDogWzAsIDBdKTtcclxuXHR9XHJcblxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgUG9wdXBcclxuLy8gQGZhY3RvcnkgTC5wb3B1cChvcHRpb25zPzogUG9wdXAgb3B0aW9ucywgc291cmNlPzogTGF5ZXIpXHJcbi8vIEluc3RhbnRpYXRlcyBhIGBQb3B1cGAgb2JqZWN0IGdpdmVuIGFuIG9wdGlvbmFsIGBvcHRpb25zYCBvYmplY3QgdGhhdCBkZXNjcmliZXMgaXRzIGFwcGVhcmFuY2UgYW5kIGxvY2F0aW9uIGFuZCBhbiBvcHRpb25hbCBgc291cmNlYCBvYmplY3QgdGhhdCBpcyB1c2VkIHRvIHRhZyB0aGUgcG9wdXAgd2l0aCBhIHJlZmVyZW5jZSB0byB0aGUgTGF5ZXIgdG8gd2hpY2ggaXQgcmVmZXJzLlxyXG5MLnBvcHVwID0gZnVuY3Rpb24gKG9wdGlvbnMsIHNvdXJjZSkge1xyXG5cdHJldHVybiBuZXcgTC5Qb3B1cChvcHRpb25zLCBzb3VyY2UpO1xyXG59O1xyXG5cclxuXHJcbi8qIEBuYW1lc3BhY2UgTWFwXHJcbiAqIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcclxuICogQG9wdGlvbiBjbG9zZVBvcHVwT25DbGljazogQm9vbGVhbiA9IHRydWVcclxuICogU2V0IGl0IHRvIGBmYWxzZWAgaWYgeW91IGRvbid0IHdhbnQgcG9wdXBzIHRvIGNsb3NlIHdoZW4gdXNlciBjbGlja3MgdGhlIG1hcC5cclxuICovXHJcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XHJcblx0Y2xvc2VQb3B1cE9uQ2xpY2s6IHRydWVcclxufSk7XHJcblxyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXBcclxuLy8gQHNlY3Rpb24gTWV0aG9kcyBmb3IgTGF5ZXJzIGFuZCBDb250cm9sc1xyXG5MLk1hcC5pbmNsdWRlKHtcclxuXHQvLyBAbWV0aG9kIG9wZW5Qb3B1cChwb3B1cDogUG9wdXApOiB0aGlzXHJcblx0Ly8gT3BlbnMgdGhlIHNwZWNpZmllZCBwb3B1cCB3aGlsZSBjbG9zaW5nIHRoZSBwcmV2aW91c2x5IG9wZW5lZCAodG8gbWFrZSBzdXJlIG9ubHkgb25lIGlzIG9wZW5lZCBhdCBvbmUgdGltZSBmb3IgdXNhYmlsaXR5KS5cclxuXHQvLyBAYWx0ZXJuYXRpdmVcclxuXHQvLyBAbWV0aG9kIG9wZW5Qb3B1cChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnQsIGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogUG9wdXAgb3B0aW9ucyk6IHRoaXNcclxuXHQvLyBDcmVhdGVzIGEgcG9wdXAgd2l0aCB0aGUgc3BlY2lmaWVkIGNvbnRlbnQgYW5kIG9wdGlvbnMgYW5kIG9wZW5zIGl0IGluIHRoZSBnaXZlbiBwb2ludCBvbiBhIG1hcC5cclxuXHRvcGVuUG9wdXA6IGZ1bmN0aW9uIChwb3B1cCwgbGF0bG5nLCBvcHRpb25zKSB7XHJcblx0XHRpZiAoIShwb3B1cCBpbnN0YW5jZW9mIEwuUG9wdXApKSB7XHJcblx0XHRcdHBvcHVwID0gbmV3IEwuUG9wdXAob3B0aW9ucykuc2V0Q29udGVudChwb3B1cCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKGxhdGxuZykge1xyXG5cdFx0XHRwb3B1cC5zZXRMYXRMbmcobGF0bG5nKTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAodGhpcy5oYXNMYXllcihwb3B1cCkpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHRoaXMuX3BvcHVwICYmIHRoaXMuX3BvcHVwLm9wdGlvbnMuYXV0b0Nsb3NlKSB7XHJcblx0XHRcdHRoaXMuY2xvc2VQb3B1cCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX3BvcHVwID0gcG9wdXA7XHJcblx0XHRyZXR1cm4gdGhpcy5hZGRMYXllcihwb3B1cCk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBjbG9zZVBvcHVwKHBvcHVwPzogUG9wdXApOiB0aGlzXHJcblx0Ly8gQ2xvc2VzIHRoZSBwb3B1cCBwcmV2aW91c2x5IG9wZW5lZCB3aXRoIFtvcGVuUG9wdXBdKCNtYXAtb3BlbnBvcHVwKSAob3IgdGhlIGdpdmVuIG9uZSkuXHJcblx0Y2xvc2VQb3B1cDogZnVuY3Rpb24gKHBvcHVwKSB7XHJcblx0XHRpZiAoIXBvcHVwIHx8IHBvcHVwID09PSB0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHRwb3B1cCA9IHRoaXMuX3BvcHVwO1xyXG5cdFx0XHR0aGlzLl9wb3B1cCA9IG51bGw7XHJcblx0XHR9XHJcblx0XHRpZiAocG9wdXApIHtcclxuXHRcdFx0dGhpcy5yZW1vdmVMYXllcihwb3B1cCk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9XHJcbn0pO1xyXG5cclxuLypcclxuICogQG5hbWVzcGFjZSBMYXllclxyXG4gKiBAc2VjdGlvbiBQb3B1cCBtZXRob2RzIGV4YW1wbGVcclxuICpcclxuICogQWxsIGxheWVycyBzaGFyZSBhIHNldCBvZiBtZXRob2RzIGNvbnZlbmllbnQgZm9yIGJpbmRpbmcgcG9wdXBzIHRvIGl0LlxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgbGF5ZXIgPSBMLlBvbHlnb24obGF0bG5ncykuYmluZFBvcHVwKCdIaSBUaGVyZSEnKS5hZGRUbyhtYXApO1xyXG4gKiBsYXllci5vcGVuUG9wdXAoKTtcclxuICogbGF5ZXIuY2xvc2VQb3B1cCgpO1xyXG4gKiBgYGBcclxuICpcclxuICogUG9wdXBzIHdpbGwgYWxzbyBiZSBhdXRvbWF0aWNhbGx5IG9wZW5lZCB3aGVuIHRoZSBsYXllciBpcyBjbGlja2VkIG9uIGFuZCBjbG9zZWQgd2hlbiB0aGUgbGF5ZXIgaXMgcmVtb3ZlZCBmcm9tIHRoZSBtYXAgb3IgYW5vdGhlciBwb3B1cCBpcyBvcGVuZWQuXHJcbiAqL1xyXG5cclxuLy8gQHNlY3Rpb24gUG9wdXAgbWV0aG9kc1xyXG5MLkxheWVyLmluY2x1ZGUoe1xyXG5cclxuXHQvLyBAbWV0aG9kIGJpbmRQb3B1cChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnR8RnVuY3Rpb258UG9wdXAsIG9wdGlvbnM/OiBQb3B1cCBvcHRpb25zKTogdGhpc1xyXG5cdC8vIEJpbmRzIGEgcG9wdXAgdG8gdGhlIGxheWVyIHdpdGggdGhlIHBhc3NlZCBgY29udGVudGAgYW5kIHNldHMgdXAgdGhlXHJcblx0Ly8gbmVjY2Vzc2FyeSBldmVudCBsaXN0ZW5lcnMuIElmIGEgYEZ1bmN0aW9uYCBpcyBwYXNzZWQgaXQgd2lsbCByZWNlaXZlXHJcblx0Ly8gdGhlIGxheWVyIGFzIHRoZSBmaXJzdCBhcmd1bWVudCBhbmQgc2hvdWxkIHJldHVybiBhIGBTdHJpbmdgIG9yIGBIVE1MRWxlbWVudGAuXHJcblx0YmluZFBvcHVwOiBmdW5jdGlvbiAoY29udGVudCwgb3B0aW9ucykge1xyXG5cclxuXHRcdGlmIChjb250ZW50IGluc3RhbmNlb2YgTC5Qb3B1cCkge1xyXG5cdFx0XHRMLnNldE9wdGlvbnMoY29udGVudCwgb3B0aW9ucyk7XHJcblx0XHRcdHRoaXMuX3BvcHVwID0gY29udGVudDtcclxuXHRcdFx0Y29udGVudC5fc291cmNlID0gdGhpcztcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGlmICghdGhpcy5fcG9wdXAgfHwgb3B0aW9ucykge1xyXG5cdFx0XHRcdHRoaXMuX3BvcHVwID0gbmV3IEwuUG9wdXAob3B0aW9ucywgdGhpcyk7XHJcblx0XHRcdH1cclxuXHRcdFx0dGhpcy5fcG9wdXAuc2V0Q29udGVudChjb250ZW50KTtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAoIXRoaXMuX3BvcHVwSGFuZGxlcnNBZGRlZCkge1xyXG5cdFx0XHR0aGlzLm9uKHtcclxuXHRcdFx0XHRjbGljazogdGhpcy5fb3BlblBvcHVwLFxyXG5cdFx0XHRcdHJlbW92ZTogdGhpcy5jbG9zZVBvcHVwLFxyXG5cdFx0XHRcdG1vdmU6IHRoaXMuX21vdmVQb3B1cFxyXG5cdFx0XHR9KTtcclxuXHRcdFx0dGhpcy5fcG9wdXBIYW5kbGVyc0FkZGVkID0gdHJ1ZTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHVuYmluZFBvcHVwKCk6IHRoaXNcclxuXHQvLyBSZW1vdmVzIHRoZSBwb3B1cCBwcmV2aW91c2x5IGJvdW5kIHdpdGggYGJpbmRQb3B1cGAuXHJcblx0dW5iaW5kUG9wdXA6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICh0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHR0aGlzLm9mZih7XHJcblx0XHRcdFx0Y2xpY2s6IHRoaXMuX29wZW5Qb3B1cCxcclxuXHRcdFx0XHRyZW1vdmU6IHRoaXMuY2xvc2VQb3B1cCxcclxuXHRcdFx0XHRtb3ZlOiB0aGlzLl9tb3ZlUG9wdXBcclxuXHRcdFx0fSk7XHJcblx0XHRcdHRoaXMuX3BvcHVwSGFuZGxlcnNBZGRlZCA9IGZhbHNlO1xyXG5cdFx0XHR0aGlzLl9wb3B1cCA9IG51bGw7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIG9wZW5Qb3B1cChsYXRsbmc/OiBMYXRMbmcpOiB0aGlzXHJcblx0Ly8gT3BlbnMgdGhlIGJvdW5kIHBvcHVwIGF0IHRoZSBzcGVjaWZpY2VkIGBsYXRsbmdgIG9yIGF0IHRoZSBkZWZhdWx0IHBvcHVwIGFuY2hvciBpZiBubyBgbGF0bG5nYCBpcyBwYXNzZWQuXHJcblx0b3BlblBvcHVwOiBmdW5jdGlvbiAobGF5ZXIsIGxhdGxuZykge1xyXG5cdFx0aWYgKCEobGF5ZXIgaW5zdGFuY2VvZiBMLkxheWVyKSkge1xyXG5cdFx0XHRsYXRsbmcgPSBsYXllcjtcclxuXHRcdFx0bGF5ZXIgPSB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmIChsYXllciBpbnN0YW5jZW9mIEwuRmVhdHVyZUdyb3VwKSB7XHJcblx0XHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRcdGxheWVyID0gdGhpcy5fbGF5ZXJzW2lkXTtcclxuXHRcdFx0XHRicmVhaztcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghbGF0bG5nKSB7XHJcblx0XHRcdGxhdGxuZyA9IGxheWVyLmdldENlbnRlciA/IGxheWVyLmdldENlbnRlcigpIDogbGF5ZXIuZ2V0TGF0TG5nKCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKHRoaXMuX3BvcHVwICYmIHRoaXMuX21hcCkge1xyXG5cdFx0XHQvLyBzZXQgcG9wdXAgc291cmNlIHRvIHRoaXMgbGF5ZXJcclxuXHRcdFx0dGhpcy5fcG9wdXAuX3NvdXJjZSA9IGxheWVyO1xyXG5cclxuXHRcdFx0Ly8gdXBkYXRlIHRoZSBwb3B1cCAoY29udGVudCwgbGF5b3V0LCBlY3QuLi4pXHJcblx0XHRcdHRoaXMuX3BvcHVwLnVwZGF0ZSgpO1xyXG5cclxuXHRcdFx0Ly8gb3BlbiB0aGUgcG9wdXAgb24gdGhlIG1hcFxyXG5cdFx0XHR0aGlzLl9tYXAub3BlblBvcHVwKHRoaXMuX3BvcHVwLCBsYXRsbmcpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY2xvc2VQb3B1cCgpOiB0aGlzXHJcblx0Ly8gQ2xvc2VzIHRoZSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyIGlmIGl0IGlzIG9wZW4uXHJcblx0Y2xvc2VQb3B1cDogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdHRoaXMuX3BvcHVwLl9jbG9zZSgpO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCB0b2dnbGVQb3B1cCgpOiB0aGlzXHJcblx0Ly8gT3BlbnMgb3IgY2xvc2VzIHRoZSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyIGRlcGVuZGluZyBvbiBpdHMgY3VycmVudCBzdGF0ZS5cclxuXHR0b2dnbGVQb3B1cDogZnVuY3Rpb24gKHRhcmdldCkge1xyXG5cdFx0aWYgKHRoaXMuX3BvcHVwKSB7XHJcblx0XHRcdGlmICh0aGlzLl9wb3B1cC5fbWFwKSB7XHJcblx0XHRcdFx0dGhpcy5jbG9zZVBvcHVwKCk7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0dGhpcy5vcGVuUG9wdXAodGFyZ2V0KTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBpc1BvcHVwT3BlbigpOiBib29sZWFuXHJcblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHBvcHVwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaXMgY3VycmVudGx5IG9wZW4uXHJcblx0aXNQb3B1cE9wZW46IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiAodGhpcy5fcG9wdXAgPyB0aGlzLl9wb3B1cC5pc09wZW4oKSA6IGZhbHNlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFBvcHVwQ29udGVudChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnR8UG9wdXApOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgY29udGVudCBvZiB0aGUgcG9wdXAgYm91bmQgdG8gdGhpcyBsYXllci5cclxuXHRzZXRQb3B1cENvbnRlbnQ6IGZ1bmN0aW9uIChjb250ZW50KSB7XHJcblx0XHRpZiAodGhpcy5fcG9wdXApIHtcclxuXHRcdFx0dGhpcy5fcG9wdXAuc2V0Q29udGVudChjb250ZW50KTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0UG9wdXAoKTogUG9wdXBcclxuXHQvLyBSZXR1cm5zIHRoZSBwb3B1cCBib3VuZCB0byB0aGlzIGxheWVyLlxyXG5cdGdldFBvcHVwOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fcG9wdXA7XHJcblx0fSxcclxuXHJcblx0X29wZW5Qb3B1cDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHZhciBsYXllciA9IGUubGF5ZXIgfHwgZS50YXJnZXQ7XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9wb3B1cCkge1xyXG5cdFx0XHRyZXR1cm47XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9tYXApIHtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIHByZXZlbnQgbWFwIGNsaWNrXHJcblx0XHRMLkRvbUV2ZW50LnN0b3AoZSk7XHJcblxyXG5cdFx0Ly8gaWYgdGhpcyBpbmhlcml0cyBmcm9tIFBhdGggaXRzIGEgdmVjdG9yIGFuZCB3ZSBjYW4ganVzdFxyXG5cdFx0Ly8gb3BlbiB0aGUgcG9wdXAgYXQgdGhlIG5ldyBsb2NhdGlvblxyXG5cdFx0aWYgKGxheWVyIGluc3RhbmNlb2YgTC5QYXRoKSB7XHJcblx0XHRcdHRoaXMub3BlblBvcHVwKGUubGF5ZXIgfHwgZS50YXJnZXQsIGUubGF0bG5nKTtcclxuXHRcdFx0cmV0dXJuO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIG90aGVyd2lzZSB0cmVhdCBpdCBsaWtlIGEgbWFya2VyIGFuZCBmaWd1cmUgb3V0XHJcblx0XHQvLyBpZiB3ZSBzaG91bGQgdG9nZ2xlIGl0IG9wZW4vY2xvc2VkXHJcblx0XHRpZiAodGhpcy5fbWFwLmhhc0xheWVyKHRoaXMuX3BvcHVwKSAmJiB0aGlzLl9wb3B1cC5fc291cmNlID09PSBsYXllcikge1xyXG5cdFx0XHR0aGlzLmNsb3NlUG9wdXAoKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHRoaXMub3BlblBvcHVwKGxheWVyLCBlLmxhdGxuZyk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X21vdmVQb3B1cDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdHRoaXMuX3BvcHVwLnNldExhdExuZyhlLmxhdGxuZyk7XHJcblx0fVxyXG59KTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBUb29sdGlwXG4gKiBAaW5oZXJpdHMgRGl2T3ZlcmxheVxuICogQGFrYSBMLlRvb2x0aXBcbiAqIFVzZWQgdG8gZGlzcGxheSBzbWFsbCB0ZXh0cyBvbiB0b3Agb2YgbWFwIGxheWVycy5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiBtYXJrZXIuYmluZFRvb2x0aXAoXCJteSB0b29sdGlwIHRleHRcIikub3BlblRvb2x0aXAoKTtcbiAqIGBgYFxuICogTm90ZSBhYm91dCB0b29sdGlwIG9mZnNldC4gTGVhZmxldCB0YWtlcyB0d28gb3B0aW9ucyBpbiBjb25zaWRlcmF0aW9uXG4gKiBmb3IgY29tcHV0aW5nIHRvb2x0aXAgb2Zmc2V0aW5nOlxuICogLSB0aGUgYG9mZnNldGAgVG9vbHRpcCBvcHRpb246IGl0IGRlZmF1bHRzIHRvIFswLCAwXSwgYW5kIGl0J3Mgc3BlY2lmaWMgdG8gb25lIHRvb2x0aXAuXG4gKiAgIEFkZCBhIHBvc2l0aXZlIHggb2Zmc2V0IHRvIG1vdmUgdGhlIHRvb2x0aXAgdG8gdGhlIHJpZ2h0LCBhbmQgYSBwb3NpdGl2ZSB5IG9mZnNldCB0b1xuICogICBtb3ZlIGl0IHRvIHRoZSBib3R0b20uIE5lZ2F0aXZlcyB3aWxsIG1vdmUgdG8gdGhlIGxlZnQgYW5kIHRvcC5cbiAqIC0gdGhlIGB0b29sdGlwQW5jaG9yYCBJY29uIG9wdGlvbjogdGhpcyB3aWxsIG9ubHkgYmUgY29uc2lkZXJlZCBmb3IgTWFya2VyLiBZb3VcbiAqICAgc2hvdWxkIGFkYXB0IHRoaXMgdmFsdWUgaWYgeW91IHVzZSBhIGN1c3RvbSBpY29uLlxuICovXG5cblxuLy8gQG5hbWVzcGFjZSBUb29sdGlwXG5MLlRvb2x0aXAgPSBMLkRpdk92ZXJsYXkuZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFRvb2x0aXAgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBwYW5lOiBTdHJpbmcgPSAndG9vbHRpcFBhbmUnXG5cdFx0Ly8gYE1hcCBwYW5lYCB3aGVyZSB0aGUgdG9vbHRpcCB3aWxsIGJlIGFkZGVkLlxuXHRcdHBhbmU6ICd0b29sdGlwUGFuZScsXG5cblx0XHQvLyBAb3B0aW9uIG9mZnNldDogUG9pbnQgPSBQb2ludCgwLCAwKVxuXHRcdC8vIE9wdGlvbmFsIG9mZnNldCBvZiB0aGUgdG9vbHRpcCBwb3NpdGlvbi5cblx0XHRvZmZzZXQ6IFswLCAwXSxcblxuXHRcdC8vIEBvcHRpb24gZGlyZWN0aW9uOiBTdHJpbmcgPSAnYXV0bydcblx0XHQvLyBEaXJlY3Rpb24gd2hlcmUgdG8gb3BlbiB0aGUgdG9vbHRpcC4gUG9zc2libGUgdmFsdWVzIGFyZTogYHJpZ2h0YCwgYGxlZnRgLFxuXHRcdC8vIGB0b3BgLCBgYm90dG9tYCwgYGNlbnRlcmAsIGBhdXRvYC5cblx0XHQvLyBgYXV0b2Agd2lsbCBkeW5hbWljYWx5IHN3aXRjaCBiZXR3ZWVuIGByaWdodGAgYW5kIGBsZWZ0YCBhY2NvcmRpbmcgdG8gdGhlIHRvb2x0aXBcblx0XHQvLyBwb3NpdGlvbiBvbiB0aGUgbWFwLlxuXHRcdGRpcmVjdGlvbjogJ2F1dG8nLFxuXG5cdFx0Ly8gQG9wdGlvbiBwZXJtYW5lbnQ6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIFdoZXRoZXIgdG8gb3BlbiB0aGUgdG9vbHRpcCBwZXJtYW5lbnRseSBvciBvbmx5IG9uIG1vdXNlb3Zlci5cblx0XHRwZXJtYW5lbnQ6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBzdGlja3k6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIElmIHRydWUsIHRoZSB0b29sdGlwIHdpbGwgZm9sbG93IHRoZSBtb3VzZSBpbnN0ZWFkIG9mIGJlaW5nIGZpeGVkIGF0IHRoZSBmZWF0dXJlIGNlbnRlci5cblx0XHRzdGlja3k6IGZhbHNlLFxuXG5cdFx0Ly8gQG9wdGlvbiBpbnRlcmFjdGl2ZTogQm9vbGVhbiA9IGZhbHNlXG5cdFx0Ly8gSWYgdHJ1ZSwgdGhlIHRvb2x0aXAgd2lsbCBsaXN0ZW4gdG8gdGhlIGZlYXR1cmUgZXZlbnRzLlxuXHRcdGludGVyYWN0aXZlOiBmYWxzZSxcblxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMC45XG5cdFx0Ly8gVG9vbHRpcCBjb250YWluZXIgb3BhY2l0eS5cblx0XHRvcGFjaXR5OiAwLjlcblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xuXHRcdEwuRGl2T3ZlcmxheS5wcm90b3R5cGUub25BZGQuY2FsbCh0aGlzLCBtYXApO1xuXHRcdHRoaXMuc2V0T3BhY2l0eSh0aGlzLm9wdGlvbnMub3BhY2l0eSk7XG5cblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxuXHRcdC8vIEBzZWN0aW9uIFRvb2x0aXAgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IHRvb2x0aXBvcGVuOiBUb29sdGlwRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIGEgdG9vbHRpcCBpcyBvcGVuZWQgaW4gdGhlIG1hcC5cblx0XHRtYXAuZmlyZSgndG9vbHRpcG9wZW4nLCB7dG9vbHRpcDogdGhpc30pO1xuXG5cdFx0aWYgKHRoaXMuX3NvdXJjZSkge1xuXHRcdFx0Ly8gQG5hbWVzcGFjZSBMYXllclxuXHRcdFx0Ly8gQHNlY3Rpb24gVG9vbHRpcCBldmVudHNcblx0XHRcdC8vIEBldmVudCB0b29sdGlwb3BlbjogVG9vbHRpcEV2ZW50XG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyIGlzIG9wZW5lZC5cblx0XHRcdHRoaXMuX3NvdXJjZS5maXJlKCd0b29sdGlwb3BlbicsIHt0b29sdGlwOiB0aGlzfSwgdHJ1ZSk7XG5cdFx0fVxuXHR9LFxuXG5cdG9uUmVtb3ZlOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0TC5EaXZPdmVybGF5LnByb3RvdHlwZS5vblJlbW92ZS5jYWxsKHRoaXMsIG1hcCk7XG5cblx0XHQvLyBAbmFtZXNwYWNlIE1hcFxuXHRcdC8vIEBzZWN0aW9uIFRvb2x0aXAgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IHRvb2x0aXBjbG9zZTogVG9vbHRpcEV2ZW50XG5cdFx0Ly8gRmlyZWQgd2hlbiBhIHRvb2x0aXAgaW4gdGhlIG1hcCBpcyBjbG9zZWQuXG5cdFx0bWFwLmZpcmUoJ3Rvb2x0aXBjbG9zZScsIHt0b29sdGlwOiB0aGlzfSk7XG5cblx0XHRpZiAodGhpcy5fc291cmNlKSB7XG5cdFx0XHQvLyBAbmFtZXNwYWNlIExheWVyXG5cdFx0XHQvLyBAc2VjdGlvbiBUb29sdGlwIGV2ZW50c1xuXHRcdFx0Ly8gQGV2ZW50IHRvb2x0aXBjbG9zZTogVG9vbHRpcEV2ZW50XG5cdFx0XHQvLyBGaXJlZCB3aGVuIGEgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyIGlzIGNsb3NlZC5cblx0XHRcdHRoaXMuX3NvdXJjZS5maXJlKCd0b29sdGlwY2xvc2UnLCB7dG9vbHRpcDogdGhpc30sIHRydWUpO1xuXHRcdH1cblx0fSxcblxuXHRnZXRFdmVudHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgZXZlbnRzID0gTC5EaXZPdmVybGF5LnByb3RvdHlwZS5nZXRFdmVudHMuY2FsbCh0aGlzKTtcblxuXHRcdGlmIChMLkJyb3dzZXIudG91Y2ggJiYgIXRoaXMub3B0aW9ucy5wZXJtYW5lbnQpIHtcblx0XHRcdGV2ZW50cy5wcmVjbGljayA9IHRoaXMuX2Nsb3NlO1xuXHRcdH1cblxuXHRcdHJldHVybiBldmVudHM7XG5cdH0sXG5cblx0X2Nsb3NlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0dGhpcy5fbWFwLmNsb3NlVG9vbHRpcCh0aGlzKTtcblx0XHR9XG5cdH0sXG5cblx0X2luaXRMYXlvdXQ6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcHJlZml4ID0gJ2xlYWZsZXQtdG9vbHRpcCcsXG5cdFx0ICAgIGNsYXNzTmFtZSA9IHByZWZpeCArICcgJyArICh0aGlzLm9wdGlvbnMuY2xhc3NOYW1lIHx8ICcnKSArICcgbGVhZmxldC16b29tLScgKyAodGhpcy5fem9vbUFuaW1hdGVkID8gJ2FuaW1hdGVkJyA6ICdoaWRlJyk7XG5cblx0XHR0aGlzLl9jb250ZW50Tm9kZSA9IHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSk7XG5cdH0sXG5cblx0X3VwZGF0ZUxheW91dDogZnVuY3Rpb24gKCkge30sXG5cblx0X2FkanVzdFBhbjogZnVuY3Rpb24gKCkge30sXG5cblx0X3NldFBvc2l0aW9uOiBmdW5jdGlvbiAocG9zKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyLFxuXHRcdCAgICBjZW50ZXJQb2ludCA9IG1hcC5sYXRMbmdUb0NvbnRhaW5lclBvaW50KG1hcC5nZXRDZW50ZXIoKSksXG5cdFx0ICAgIHRvb2x0aXBQb2ludCA9IG1hcC5sYXllclBvaW50VG9Db250YWluZXJQb2ludChwb3MpLFxuXHRcdCAgICBkaXJlY3Rpb24gPSB0aGlzLm9wdGlvbnMuZGlyZWN0aW9uLFxuXHRcdCAgICB0b29sdGlwV2lkdGggPSBjb250YWluZXIub2Zmc2V0V2lkdGgsXG5cdFx0ICAgIHRvb2x0aXBIZWlnaHQgPSBjb250YWluZXIub2Zmc2V0SGVpZ2h0LFxuXHRcdCAgICBvZmZzZXQgPSBMLnBvaW50KHRoaXMub3B0aW9ucy5vZmZzZXQpLFxuXHRcdCAgICBhbmNob3IgPSB0aGlzLl9nZXRBbmNob3IoKTtcblxuXHRcdGlmIChkaXJlY3Rpb24gPT09ICd0b3AnKSB7XG5cdFx0XHRwb3MgPSBwb3MuYWRkKEwucG9pbnQoLXRvb2x0aXBXaWR0aCAvIDIgKyBvZmZzZXQueCwgLXRvb2x0aXBIZWlnaHQgKyBvZmZzZXQueSArIGFuY2hvci55LCB0cnVlKSk7XG5cdFx0fSBlbHNlIGlmIChkaXJlY3Rpb24gPT09ICdib3R0b20nKSB7XG5cdFx0XHRwb3MgPSBwb3Muc3VidHJhY3QoTC5wb2ludCh0b29sdGlwV2lkdGggLyAyIC0gb2Zmc2V0LngsIC1vZmZzZXQueSwgdHJ1ZSkpO1xuXHRcdH0gZWxzZSBpZiAoZGlyZWN0aW9uID09PSAnY2VudGVyJykge1xuXHRcdFx0cG9zID0gcG9zLnN1YnRyYWN0KEwucG9pbnQodG9vbHRpcFdpZHRoIC8gMiArIG9mZnNldC54LCB0b29sdGlwSGVpZ2h0IC8gMiAtIGFuY2hvci55ICsgb2Zmc2V0LnksIHRydWUpKTtcblx0XHR9IGVsc2UgaWYgKGRpcmVjdGlvbiA9PT0gJ3JpZ2h0JyB8fCBkaXJlY3Rpb24gPT09ICdhdXRvJyAmJiB0b29sdGlwUG9pbnQueCA8IGNlbnRlclBvaW50LngpIHtcblx0XHRcdGRpcmVjdGlvbiA9ICdyaWdodCc7XG5cdFx0XHRwb3MgPSBwb3MuYWRkKEwucG9pbnQob2Zmc2V0LnggKyBhbmNob3IueCwgYW5jaG9yLnkgLSB0b29sdGlwSGVpZ2h0IC8gMiArIG9mZnNldC55LCB0cnVlKSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGRpcmVjdGlvbiA9ICdsZWZ0Jztcblx0XHRcdHBvcyA9IHBvcy5zdWJ0cmFjdChMLnBvaW50KHRvb2x0aXBXaWR0aCArIGFuY2hvci54IC0gb2Zmc2V0LngsIHRvb2x0aXBIZWlnaHQgLyAyIC0gYW5jaG9yLnkgLSBvZmZzZXQueSwgdHJ1ZSkpO1xuXHRcdH1cblxuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtcmlnaHQnKTtcblx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC10b29sdGlwLWxlZnQnKTtcblx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC10b29sdGlwLXRvcCcpO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyhjb250YWluZXIsICdsZWFmbGV0LXRvb2x0aXAtYm90dG9tJyk7XG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtdG9vbHRpcC0nICsgZGlyZWN0aW9uKTtcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24oY29udGFpbmVyLCBwb3MpO1xuXHR9LFxuXG5cdF91cGRhdGVQb3NpdGlvbjogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBwb3MgPSB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZyk7XG5cdFx0dGhpcy5fc2V0UG9zaXRpb24ocG9zKTtcblx0fSxcblxuXHRzZXRPcGFjaXR5OiBmdW5jdGlvbiAob3BhY2l0eSkge1xuXHRcdHRoaXMub3B0aW9ucy5vcGFjaXR5ID0gb3BhY2l0eTtcblxuXHRcdGlmICh0aGlzLl9jb250YWluZXIpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRPcGFjaXR5KHRoaXMuX2NvbnRhaW5lciwgb3BhY2l0eSk7XG5cdFx0fVxuXHR9LFxuXG5cdF9hbmltYXRlWm9vbTogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgcG9zID0gdGhpcy5fbWFwLl9sYXRMbmdUb05ld0xheWVyUG9pbnQodGhpcy5fbGF0bG5nLCBlLnpvb20sIGUuY2VudGVyKTtcblx0XHR0aGlzLl9zZXRQb3NpdGlvbihwb3MpO1xuXHR9LFxuXG5cdF9nZXRBbmNob3I6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBXaGVyZSBzaG91bGQgd2UgYW5jaG9yIHRoZSB0b29sdGlwIG9uIHRoZSBzb3VyY2UgbGF5ZXI/XG5cdFx0cmV0dXJuIEwucG9pbnQodGhpcy5fc291cmNlICYmIHRoaXMuX3NvdXJjZS5fZ2V0VG9vbHRpcEFuY2hvciAmJiAhdGhpcy5vcHRpb25zLnN0aWNreSA/IHRoaXMuX3NvdXJjZS5fZ2V0VG9vbHRpcEFuY2hvcigpIDogWzAsIDBdKTtcblx0fVxuXG59KTtcblxuLy8gQG5hbWVzcGFjZSBUb29sdGlwXG4vLyBAZmFjdG9yeSBMLnRvb2x0aXAob3B0aW9ucz86IFRvb2x0aXAgb3B0aW9ucywgc291cmNlPzogTGF5ZXIpXG4vLyBJbnN0YW50aWF0ZXMgYSBUb29sdGlwIG9iamVjdCBnaXZlbiBhbiBvcHRpb25hbCBgb3B0aW9uc2Agb2JqZWN0IHRoYXQgZGVzY3JpYmVzIGl0cyBhcHBlYXJhbmNlIGFuZCBsb2NhdGlvbiBhbmQgYW4gb3B0aW9uYWwgYHNvdXJjZWAgb2JqZWN0IHRoYXQgaXMgdXNlZCB0byB0YWcgdGhlIHRvb2x0aXAgd2l0aCBhIHJlZmVyZW5jZSB0byB0aGUgTGF5ZXIgdG8gd2hpY2ggaXQgcmVmZXJzLlxuTC50b29sdGlwID0gZnVuY3Rpb24gKG9wdGlvbnMsIHNvdXJjZSkge1xuXHRyZXR1cm4gbmV3IEwuVG9vbHRpcChvcHRpb25zLCBzb3VyY2UpO1xufTtcblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIE1ldGhvZHMgZm9yIExheWVycyBhbmQgQ29udHJvbHNcbkwuTWFwLmluY2x1ZGUoe1xuXG5cdC8vIEBtZXRob2Qgb3BlblRvb2x0aXAodG9vbHRpcDogVG9vbHRpcCk6IHRoaXNcblx0Ly8gT3BlbnMgdGhlIHNwZWNpZmllZCB0b29sdGlwLlxuXHQvLyBAYWx0ZXJuYXRpdmVcblx0Ly8gQG1ldGhvZCBvcGVuVG9vbHRpcChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnQsIGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogVG9vbHRpcCBvcHRpb25zKTogdGhpc1xuXHQvLyBDcmVhdGVzIGEgdG9vbHRpcCB3aXRoIHRoZSBzcGVjaWZpZWQgY29udGVudCBhbmQgb3B0aW9ucyBhbmQgb3BlbiBpdC5cblx0b3BlblRvb2x0aXA6IGZ1bmN0aW9uICh0b29sdGlwLCBsYXRsbmcsIG9wdGlvbnMpIHtcblx0XHRpZiAoISh0b29sdGlwIGluc3RhbmNlb2YgTC5Ub29sdGlwKSkge1xuXHRcdFx0dG9vbHRpcCA9IG5ldyBMLlRvb2x0aXAob3B0aW9ucykuc2V0Q29udGVudCh0b29sdGlwKTtcblx0XHR9XG5cblx0XHRpZiAobGF0bG5nKSB7XG5cdFx0XHR0b29sdGlwLnNldExhdExuZyhsYXRsbmcpO1xuXHRcdH1cblxuXHRcdGlmICh0aGlzLmhhc0xheWVyKHRvb2x0aXApKSB7XG5cdFx0XHRyZXR1cm4gdGhpcztcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcy5hZGRMYXllcih0b29sdGlwKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGNsb3NlVG9vbHRpcCh0b29sdGlwPzogVG9vbHRpcCk6IHRoaXNcblx0Ly8gQ2xvc2VzIHRoZSB0b29sdGlwIGdpdmVuIGFzIHBhcmFtZXRlci5cblx0Y2xvc2VUb29sdGlwOiBmdW5jdGlvbiAodG9vbHRpcCkge1xuXHRcdGlmICh0b29sdGlwKSB7XG5cdFx0XHR0aGlzLnJlbW92ZUxheWVyKHRvb2x0aXApO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG59KTtcblxuLypcbiAqIEBuYW1lc3BhY2UgTGF5ZXJcbiAqIEBzZWN0aW9uIFRvb2x0aXAgbWV0aG9kcyBleGFtcGxlXG4gKlxuICogQWxsIGxheWVycyBzaGFyZSBhIHNldCBvZiBtZXRob2RzIGNvbnZlbmllbnQgZm9yIGJpbmRpbmcgdG9vbHRpcHMgdG8gaXQuXG4gKlxuICogYGBganNcbiAqIHZhciBsYXllciA9IEwuUG9seWdvbihsYXRsbmdzKS5iaW5kVG9vbHRpcCgnSGkgVGhlcmUhJykuYWRkVG8obWFwKTtcbiAqIGxheWVyLm9wZW5Ub29sdGlwKCk7XG4gKiBsYXllci5jbG9zZVRvb2x0aXAoKTtcbiAqIGBgYFxuICovXG5cbi8vIEBzZWN0aW9uIFRvb2x0aXAgbWV0aG9kc1xuTC5MYXllci5pbmNsdWRlKHtcblxuXHQvLyBAbWV0aG9kIGJpbmRUb29sdGlwKGNvbnRlbnQ6IFN0cmluZ3xIVE1MRWxlbWVudHxGdW5jdGlvbnxUb29sdGlwLCBvcHRpb25zPzogVG9vbHRpcCBvcHRpb25zKTogdGhpc1xuXHQvLyBCaW5kcyBhIHRvb2x0aXAgdG8gdGhlIGxheWVyIHdpdGggdGhlIHBhc3NlZCBgY29udGVudGAgYW5kIHNldHMgdXAgdGhlXG5cdC8vIG5lY2Nlc3NhcnkgZXZlbnQgbGlzdGVuZXJzLiBJZiBhIGBGdW5jdGlvbmAgaXMgcGFzc2VkIGl0IHdpbGwgcmVjZWl2ZVxuXHQvLyB0aGUgbGF5ZXIgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IGFuZCBzaG91bGQgcmV0dXJuIGEgYFN0cmluZ2Agb3IgYEhUTUxFbGVtZW50YC5cblx0YmluZFRvb2x0aXA6IGZ1bmN0aW9uIChjb250ZW50LCBvcHRpb25zKSB7XG5cblx0XHRpZiAoY29udGVudCBpbnN0YW5jZW9mIEwuVG9vbHRpcCkge1xuXHRcdFx0TC5zZXRPcHRpb25zKGNvbnRlbnQsIG9wdGlvbnMpO1xuXHRcdFx0dGhpcy5fdG9vbHRpcCA9IGNvbnRlbnQ7XG5cdFx0XHRjb250ZW50Ll9zb3VyY2UgPSB0aGlzO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRpZiAoIXRoaXMuX3Rvb2x0aXAgfHwgb3B0aW9ucykge1xuXHRcdFx0XHR0aGlzLl90b29sdGlwID0gTC50b29sdGlwKG9wdGlvbnMsIHRoaXMpO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fdG9vbHRpcC5zZXRDb250ZW50KGNvbnRlbnQpO1xuXG5cdFx0fVxuXG5cdFx0dGhpcy5faW5pdFRvb2x0aXBJbnRlcmFjdGlvbnMoKTtcblxuXHRcdGlmICh0aGlzLl90b29sdGlwLm9wdGlvbnMucGVybWFuZW50ICYmIHRoaXMuX21hcCAmJiB0aGlzLl9tYXAuaGFzTGF5ZXIodGhpcykpIHtcblx0XHRcdHRoaXMub3BlblRvb2x0aXAoKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHVuYmluZFRvb2x0aXAoKTogdGhpc1xuXHQvLyBSZW1vdmVzIHRoZSB0b29sdGlwIHByZXZpb3VzbHkgYm91bmQgd2l0aCBgYmluZFRvb2x0aXBgLlxuXHR1bmJpbmRUb29sdGlwOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXApIHtcblx0XHRcdHRoaXMuX2luaXRUb29sdGlwSW50ZXJhY3Rpb25zKHRydWUpO1xuXHRcdFx0dGhpcy5jbG9zZVRvb2x0aXAoKTtcblx0XHRcdHRoaXMuX3Rvb2x0aXAgPSBudWxsO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRfaW5pdFRvb2x0aXBJbnRlcmFjdGlvbnM6IGZ1bmN0aW9uIChyZW1vdmUpIHtcblx0XHRpZiAoIXJlbW92ZSAmJiB0aGlzLl90b29sdGlwSGFuZGxlcnNBZGRlZCkgeyByZXR1cm47IH1cblx0XHR2YXIgb25PZmYgPSByZW1vdmUgPyAnb2ZmJyA6ICdvbicsXG5cdFx0ICAgIGV2ZW50cyA9IHtcblx0XHRcdHJlbW92ZTogdGhpcy5jbG9zZVRvb2x0aXAsXG5cdFx0XHRtb3ZlOiB0aGlzLl9tb3ZlVG9vbHRpcFxuXHRcdCAgICB9O1xuXHRcdGlmICghdGhpcy5fdG9vbHRpcC5vcHRpb25zLnBlcm1hbmVudCkge1xuXHRcdFx0ZXZlbnRzLm1vdXNlb3ZlciA9IHRoaXMuX29wZW5Ub29sdGlwO1xuXHRcdFx0ZXZlbnRzLm1vdXNlb3V0ID0gdGhpcy5jbG9zZVRvb2x0aXA7XG5cdFx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLnN0aWNreSkge1xuXHRcdFx0XHRldmVudHMubW91c2Vtb3ZlID0gdGhpcy5fbW92ZVRvb2x0aXA7XG5cdFx0XHR9XG5cdFx0XHRpZiAoTC5Ccm93c2VyLnRvdWNoKSB7XG5cdFx0XHRcdGV2ZW50cy5jbGljayA9IHRoaXMuX29wZW5Ub29sdGlwO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRldmVudHMuYWRkID0gdGhpcy5fb3BlblRvb2x0aXA7XG5cdFx0fVxuXHRcdHRoaXNbb25PZmZdKGV2ZW50cyk7XG5cdFx0dGhpcy5fdG9vbHRpcEhhbmRsZXJzQWRkZWQgPSAhcmVtb3ZlO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgb3BlblRvb2x0aXAobGF0bG5nPzogTGF0TG5nKTogdGhpc1xuXHQvLyBPcGVucyB0aGUgYm91bmQgdG9vbHRpcCBhdCB0aGUgc3BlY2lmaWNlZCBgbGF0bG5nYCBvciBhdCB0aGUgZGVmYXVsdCB0b29sdGlwIGFuY2hvciBpZiBubyBgbGF0bG5nYCBpcyBwYXNzZWQuXG5cdG9wZW5Ub29sdGlwOiBmdW5jdGlvbiAobGF5ZXIsIGxhdGxuZykge1xuXHRcdGlmICghKGxheWVyIGluc3RhbmNlb2YgTC5MYXllcikpIHtcblx0XHRcdGxhdGxuZyA9IGxheWVyO1xuXHRcdFx0bGF5ZXIgPSB0aGlzO1xuXHRcdH1cblxuXHRcdGlmIChsYXllciBpbnN0YW5jZW9mIEwuRmVhdHVyZUdyb3VwKSB7XG5cdFx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9sYXllcnMpIHtcblx0XHRcdFx0bGF5ZXIgPSB0aGlzLl9sYXllcnNbaWRdO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIWxhdGxuZykge1xuXHRcdFx0bGF0bG5nID0gbGF5ZXIuZ2V0Q2VudGVyID8gbGF5ZXIuZ2V0Q2VudGVyKCkgOiBsYXllci5nZXRMYXRMbmcoKTtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5fdG9vbHRpcCAmJiB0aGlzLl9tYXApIHtcblxuXHRcdFx0Ly8gc2V0IHRvb2x0aXAgc291cmNlIHRvIHRoaXMgbGF5ZXJcblx0XHRcdHRoaXMuX3Rvb2x0aXAuX3NvdXJjZSA9IGxheWVyO1xuXG5cdFx0XHQvLyB1cGRhdGUgdGhlIHRvb2x0aXAgKGNvbnRlbnQsIGxheW91dCwgZWN0Li4uKVxuXHRcdFx0dGhpcy5fdG9vbHRpcC51cGRhdGUoKTtcblxuXHRcdFx0Ly8gb3BlbiB0aGUgdG9vbHRpcCBvbiB0aGUgbWFwXG5cdFx0XHR0aGlzLl9tYXAub3BlblRvb2x0aXAodGhpcy5fdG9vbHRpcCwgbGF0bG5nKTtcblxuXHRcdFx0Ly8gVG9vbHRpcCBjb250YWluZXIgbWF5IG5vdCBiZSBkZWZpbmVkIGlmIG5vdCBwZXJtYW5lbnQgYW5kIG5ldmVyXG5cdFx0XHQvLyBvcGVuZWQuXG5cdFx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLmludGVyYWN0aXZlICYmIHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcikge1xuXHRcdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fdG9vbHRpcC5fY29udGFpbmVyLCAnbGVhZmxldC1jbGlja2FibGUnKTtcblx0XHRcdFx0dGhpcy5hZGRJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl90b29sdGlwLl9jb250YWluZXIpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgY2xvc2VUb29sdGlwKCk6IHRoaXNcblx0Ly8gQ2xvc2VzIHRoZSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIgaWYgaXQgaXMgb3Blbi5cblx0Y2xvc2VUb29sdGlwOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXApIHtcblx0XHRcdHRoaXMuX3Rvb2x0aXAuX2Nsb3NlKCk7XG5cdFx0XHRpZiAodGhpcy5fdG9vbHRpcC5vcHRpb25zLmludGVyYWN0aXZlICYmIHRoaXMuX3Rvb2x0aXAuX2NvbnRhaW5lcikge1xuXHRcdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fdG9vbHRpcC5fY29udGFpbmVyLCAnbGVhZmxldC1jbGlja2FibGUnKTtcblx0XHRcdFx0dGhpcy5yZW1vdmVJbnRlcmFjdGl2ZVRhcmdldCh0aGlzLl90b29sdGlwLl9jb250YWluZXIpO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIHRvZ2dsZVRvb2x0aXAoKTogdGhpc1xuXHQvLyBPcGVucyBvciBjbG9zZXMgdGhlIHRvb2x0aXAgYm91bmQgdG8gdGhpcyBsYXllciBkZXBlbmRpbmcgb24gaXRzIGN1cnJlbnQgc3RhdGUuXG5cdHRvZ2dsZVRvb2x0aXA6IGZ1bmN0aW9uICh0YXJnZXQpIHtcblx0XHRpZiAodGhpcy5fdG9vbHRpcCkge1xuXHRcdFx0aWYgKHRoaXMuX3Rvb2x0aXAuX21hcCkge1xuXHRcdFx0XHR0aGlzLmNsb3NlVG9vbHRpcCgpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dGhpcy5vcGVuVG9vbHRpcCh0YXJnZXQpO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGlzVG9vbHRpcE9wZW4oKTogYm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyIGlzIGN1cnJlbnRseSBvcGVuLlxuXHRpc1Rvb2x0aXBPcGVuOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3Rvb2x0aXAuaXNPcGVuKCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRUb29sdGlwQ29udGVudChjb250ZW50OiBTdHJpbmd8SFRNTEVsZW1lbnR8VG9vbHRpcCk6IHRoaXNcblx0Ly8gU2V0cyB0aGUgY29udGVudCBvZiB0aGUgdG9vbHRpcCBib3VuZCB0byB0aGlzIGxheWVyLlxuXHRzZXRUb29sdGlwQ29udGVudDogZnVuY3Rpb24gKGNvbnRlbnQpIHtcblx0XHRpZiAodGhpcy5fdG9vbHRpcCkge1xuXHRcdFx0dGhpcy5fdG9vbHRpcC5zZXRDb250ZW50KGNvbnRlbnQpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldFRvb2x0aXAoKTogVG9vbHRpcFxuXHQvLyBSZXR1cm5zIHRoZSB0b29sdGlwIGJvdW5kIHRvIHRoaXMgbGF5ZXIuXG5cdGdldFRvb2x0aXA6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fdG9vbHRpcDtcblx0fSxcblxuXHRfb3BlblRvb2x0aXA6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGxheWVyID0gZS5sYXllciB8fCBlLnRhcmdldDtcblxuXHRcdGlmICghdGhpcy5fdG9vbHRpcCB8fCAhdGhpcy5fbWFwKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdHRoaXMub3BlblRvb2x0aXAobGF5ZXIsIHRoaXMuX3Rvb2x0aXAub3B0aW9ucy5zdGlja3kgPyBlLmxhdGxuZyA6IHVuZGVmaW5lZCk7XG5cdH0sXG5cblx0X21vdmVUb29sdGlwOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBsYXRsbmcgPSBlLmxhdGxuZywgY29udGFpbmVyUG9pbnQsIGxheWVyUG9pbnQ7XG5cdFx0aWYgKHRoaXMuX3Rvb2x0aXAub3B0aW9ucy5zdGlja3kgJiYgZS5vcmlnaW5hbEV2ZW50KSB7XG5cdFx0XHRjb250YWluZXJQb2ludCA9IHRoaXMuX21hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLm9yaWdpbmFsRXZlbnQpO1xuXHRcdFx0bGF5ZXJQb2ludCA9IHRoaXMuX21hcC5jb250YWluZXJQb2ludFRvTGF5ZXJQb2ludChjb250YWluZXJQb2ludCk7XG5cdFx0XHRsYXRsbmcgPSB0aGlzLl9tYXAubGF5ZXJQb2ludFRvTGF0TG5nKGxheWVyUG9pbnQpO1xuXHRcdH1cblx0XHR0aGlzLl90b29sdGlwLnNldExhdExuZyhsYXRsbmcpO1xuXHR9XG59KTtcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBMYXllckdyb3VwXHJcbiAqIEBha2EgTC5MYXllckdyb3VwXHJcbiAqIEBpbmhlcml0cyBMYXllclxyXG4gKlxyXG4gKiBVc2VkIHRvIGdyb3VwIHNldmVyYWwgbGF5ZXJzIGFuZCBoYW5kbGUgdGhlbSBhcyBvbmUuIElmIHlvdSBhZGQgaXQgdG8gdGhlIG1hcCxcclxuICogYW55IGxheWVycyBhZGRlZCBvciByZW1vdmVkIGZyb20gdGhlIGdyb3VwIHdpbGwgYmUgYWRkZWQvcmVtb3ZlZCBvbiB0aGUgbWFwIGFzXHJcbiAqIHdlbGwuIEV4dGVuZHMgYExheWVyYC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYGBganNcclxuICogTC5sYXllckdyb3VwKFttYXJrZXIxLCBtYXJrZXIyXSlcclxuICogXHQuYWRkTGF5ZXIocG9seWxpbmUpXHJcbiAqIFx0LmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuTGF5ZXJHcm91cCA9IEwuTGF5ZXIuZXh0ZW5kKHtcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGxheWVycykge1xyXG5cdFx0dGhpcy5fbGF5ZXJzID0ge307XHJcblxyXG5cdFx0dmFyIGksIGxlbjtcclxuXHJcblx0XHRpZiAobGF5ZXJzKSB7XHJcblx0XHRcdGZvciAoaSA9IDAsIGxlbiA9IGxheWVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHRoaXMuYWRkTGF5ZXIobGF5ZXJzW2ldKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkTGF5ZXIobGF5ZXI6IExheWVyKTogdGhpc1xyXG5cdC8vIEFkZHMgdGhlIGdpdmVuIGxheWVyIHRvIHRoZSBncm91cC5cclxuXHRhZGRMYXllcjogZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHR2YXIgaWQgPSB0aGlzLmdldExheWVySWQobGF5ZXIpO1xyXG5cclxuXHRcdHRoaXMuX2xheWVyc1tpZF0gPSBsYXllcjtcclxuXHJcblx0XHRpZiAodGhpcy5fbWFwKSB7XHJcblx0XHRcdHRoaXMuX21hcC5hZGRMYXllcihsYXllcik7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyB0aGUgZ2l2ZW4gbGF5ZXIgZnJvbSB0aGUgZ3JvdXAuXHJcblx0Ly8gQGFsdGVybmF0aXZlXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihpZDogTnVtYmVyKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIGxheWVyIHdpdGggdGhlIGdpdmVuIGludGVybmFsIElEIGZyb20gdGhlIGdyb3VwLlxyXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdHZhciBpZCA9IGxheWVyIGluIHRoaXMuX2xheWVycyA/IGxheWVyIDogdGhpcy5nZXRMYXllcklkKGxheWVyKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbWFwICYmIHRoaXMuX2xheWVyc1tpZF0pIHtcclxuXHRcdFx0dGhpcy5fbWFwLnJlbW92ZUxheWVyKHRoaXMuX2xheWVyc1tpZF0pO1xyXG5cdFx0fVxyXG5cclxuXHRcdGRlbGV0ZSB0aGlzLl9sYXllcnNbaWRdO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaGFzTGF5ZXIobGF5ZXI6IExheWVyKTogQm9vbGVhblxyXG5cdC8vIFJldHVybnMgYHRydWVgIGlmIHRoZSBnaXZlbiBsYXllciBpcyBjdXJyZW50bHkgYWRkZWQgdG8gdGhlIGdyb3VwLlxyXG5cdGhhc0xheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdHJldHVybiAhIWxheWVyICYmIChsYXllciBpbiB0aGlzLl9sYXllcnMgfHwgdGhpcy5nZXRMYXllcklkKGxheWVyKSBpbiB0aGlzLl9sYXllcnMpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY2xlYXJMYXllcnMoKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYWxsIHRoZSBsYXllcnMgZnJvbSB0aGUgZ3JvdXAuXHJcblx0Y2xlYXJMYXllcnM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMucmVtb3ZlTGF5ZXIodGhpcy5fbGF5ZXJzW2ldKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgaW52b2tlKG1ldGhvZE5hbWU6IFN0cmluZywg4oCmKTogdGhpc1xyXG5cdC8vIENhbGxzIGBtZXRob2ROYW1lYCBvbiBldmVyeSBsYXllciBjb250YWluZWQgaW4gdGhpcyBncm91cCwgcGFzc2luZyBhbnlcclxuXHQvLyBhZGRpdGlvbmFsIHBhcmFtZXRlcnMuIEhhcyBubyBlZmZlY3QgaWYgdGhlIGxheWVycyBjb250YWluZWQgZG8gbm90XHJcblx0Ly8gaW1wbGVtZW50IGBtZXRob2ROYW1lYC5cclxuXHRpbnZva2U6IGZ1bmN0aW9uIChtZXRob2ROYW1lKSB7XHJcblx0XHR2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMSksXHJcblx0XHQgICAgaSwgbGF5ZXI7XHJcblxyXG5cdFx0Zm9yIChpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRsYXllciA9IHRoaXMuX2xheWVyc1tpXTtcclxuXHJcblx0XHRcdGlmIChsYXllclttZXRob2ROYW1lXSkge1xyXG5cdFx0XHRcdGxheWVyW21ldGhvZE5hbWVdLmFwcGx5KGxheWVyLCBhcmdzKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRtYXAuYWRkTGF5ZXIodGhpcy5fbGF5ZXJzW2ldKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0bWFwLnJlbW92ZUxheWVyKHRoaXMuX2xheWVyc1tpXSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBlYWNoTGF5ZXIoZm46IEZ1bmN0aW9uLCBjb250ZXh0PzogT2JqZWN0KTogdGhpc1xyXG5cdC8vIEl0ZXJhdGVzIG92ZXIgdGhlIGxheWVycyBvZiB0aGUgZ3JvdXAsIG9wdGlvbmFsbHkgc3BlY2lmeWluZyBjb250ZXh0IG9mIHRoZSBpdGVyYXRvciBmdW5jdGlvbi5cclxuXHQvLyBgYGBqc1xyXG5cdC8vIGdyb3VwLmVhY2hMYXllcihmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHQvLyBcdGxheWVyLmJpbmRQb3B1cCgnSGVsbG8nKTtcclxuXHQvLyB9KTtcclxuXHQvLyBgYGBcclxuXHRlYWNoTGF5ZXI6IGZ1bmN0aW9uIChtZXRob2QsIGNvbnRleHQpIHtcclxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdG1ldGhvZC5jYWxsKGNvbnRleHQsIHRoaXMuX2xheWVyc1tpXSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldExheWVyKGlkOiBOdW1iZXIpOiBMYXllclxyXG5cdC8vIFJldHVybnMgdGhlIGxheWVyIHdpdGggdGhlIGdpdmVuIGludGVybmFsIElELlxyXG5cdGdldExheWVyOiBmdW5jdGlvbiAoaWQpIHtcclxuXHRcdHJldHVybiB0aGlzLl9sYXllcnNbaWRdO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0TGF5ZXJzKCk6IExheWVyW11cclxuXHQvLyBSZXR1cm5zIGFuIGFycmF5IG9mIGFsbCB0aGUgbGF5ZXJzIGFkZGVkIHRvIHRoZSBncm91cC5cclxuXHRnZXRMYXllcnM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBsYXllcnMgPSBbXTtcclxuXHJcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX2xheWVycykge1xyXG5cdFx0XHRsYXllcnMucHVzaCh0aGlzLl9sYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0cmV0dXJuIGxheWVycztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFpJbmRleCh6SW5kZXg6IE51bWJlcik6IHRoaXNcclxuXHQvLyBDYWxscyBgc2V0WkluZGV4YCBvbiBldmVyeSBsYXllciBjb250YWluZWQgaW4gdGhpcyBncm91cCwgcGFzc2luZyB0aGUgei1pbmRleC5cclxuXHRzZXRaSW5kZXg6IGZ1bmN0aW9uICh6SW5kZXgpIHtcclxuXHRcdHJldHVybiB0aGlzLmludm9rZSgnc2V0WkluZGV4JywgekluZGV4KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGdldExheWVySWQobGF5ZXI6IExheWVyKTogTnVtYmVyXHJcblx0Ly8gUmV0dXJucyB0aGUgaW50ZXJuYWwgSUQgZm9yIGEgbGF5ZXJcclxuXHRnZXRMYXllcklkOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdHJldHVybiBMLnN0YW1wKGxheWVyKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuXHJcbi8vIEBmYWN0b3J5IEwubGF5ZXJHcm91cChsYXllcnM6IExheWVyW10pXHJcbi8vIENyZWF0ZSBhIGxheWVyIGdyb3VwLCBvcHRpb25hbGx5IGdpdmVuIGFuIGluaXRpYWwgc2V0IG9mIGxheWVycy5cclxuTC5sYXllckdyb3VwID0gZnVuY3Rpb24gKGxheWVycykge1xyXG5cdHJldHVybiBuZXcgTC5MYXllckdyb3VwKGxheWVycyk7XHJcbn07XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBGZWF0dXJlR3JvdXBcclxuICogQGFrYSBMLkZlYXR1cmVHcm91cFxyXG4gKiBAaW5oZXJpdHMgTGF5ZXJHcm91cFxyXG4gKlxyXG4gKiBFeHRlbmRlZCBgTGF5ZXJHcm91cGAgdGhhdCBtYWtlcyBpdCBlYXNpZXIgdG8gZG8gdGhlIHNhbWUgdGhpbmcgdG8gYWxsIGl0cyBtZW1iZXIgbGF5ZXJzOlxyXG4gKiAgKiBbYGJpbmRQb3B1cGBdKCNsYXllci1iaW5kcG9wdXApIGJpbmRzIGEgcG9wdXAgdG8gYWxsIG9mIHRoZSBsYXllcnMgYXQgb25jZSAobGlrZXdpc2Ugd2l0aCBbYGJpbmRUb29sdGlwYF0oI2xheWVyLWJpbmR0b29sdGlwKSlcclxuICogICogRXZlbnRzIGFyZSBwcm9wYWdhdGVkIHRvIHRoZSBgRmVhdHVyZUdyb3VwYCwgc28gaWYgdGhlIGdyb3VwIGhhcyBhbiBldmVudFxyXG4gKiBoYW5kbGVyLCBpdCB3aWxsIGhhbmRsZSBldmVudHMgZnJvbSBhbnkgb2YgdGhlIGxheWVycy4gVGhpcyBpbmNsdWRlcyBtb3VzZSBldmVudHNcclxuICogYW5kIGN1c3RvbSBldmVudHMuXHJcbiAqICAqIEhhcyBgbGF5ZXJhZGRgIGFuZCBgbGF5ZXJyZW1vdmVgIGV2ZW50c1xyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLmZlYXR1cmVHcm91cChbbWFya2VyMSwgbWFya2VyMiwgcG9seWxpbmVdKVxyXG4gKiBcdC5iaW5kUG9wdXAoJ0hlbGxvIHdvcmxkIScpXHJcbiAqIFx0Lm9uKCdjbGljaycsIGZ1bmN0aW9uKCkgeyBhbGVydCgnQ2xpY2tlZCBvbiBhIG1lbWJlciBvZiB0aGUgZ3JvdXAhJyk7IH0pXHJcbiAqIFx0LmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuRmVhdHVyZUdyb3VwID0gTC5MYXllckdyb3VwLmV4dGVuZCh7XHJcblxyXG5cdGFkZExheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdGlmICh0aGlzLmhhc0xheWVyKGxheWVyKSkge1xyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHRsYXllci5hZGRFdmVudFBhcmVudCh0aGlzKTtcclxuXHJcblx0XHRMLkxheWVyR3JvdXAucHJvdG90eXBlLmFkZExheWVyLmNhbGwodGhpcywgbGF5ZXIpO1xyXG5cclxuXHRcdC8vIEBldmVudCBsYXllcmFkZDogTGF5ZXJFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhIGxheWVyIGlzIGFkZGVkIHRvIHRoaXMgYEZlYXR1cmVHcm91cGBcclxuXHRcdHJldHVybiB0aGlzLmZpcmUoJ2xheWVyYWRkJywge2xheWVyOiBsYXllcn0pO1xyXG5cdH0sXHJcblxyXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdGlmICghdGhpcy5oYXNMYXllcihsYXllcikpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblx0XHRpZiAobGF5ZXIgaW4gdGhpcy5fbGF5ZXJzKSB7XHJcblx0XHRcdGxheWVyID0gdGhpcy5fbGF5ZXJzW2xheWVyXTtcclxuXHRcdH1cclxuXHJcblx0XHRsYXllci5yZW1vdmVFdmVudFBhcmVudCh0aGlzKTtcclxuXHJcblx0XHRMLkxheWVyR3JvdXAucHJvdG90eXBlLnJlbW92ZUxheWVyLmNhbGwodGhpcywgbGF5ZXIpO1xyXG5cclxuXHRcdC8vIEBldmVudCBsYXllcnJlbW92ZTogTGF5ZXJFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhIGxheWVyIGlzIHJlbW92ZWQgZnJvbSB0aGlzIGBGZWF0dXJlR3JvdXBgXHJcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdsYXllcnJlbW92ZScsIHtsYXllcjogbGF5ZXJ9KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIHNldFN0eWxlKHN0eWxlOiBQYXRoIG9wdGlvbnMpOiB0aGlzXHJcblx0Ly8gU2V0cyB0aGUgZ2l2ZW4gcGF0aCBvcHRpb25zIHRvIGVhY2ggbGF5ZXIgb2YgdGhlIGdyb3VwIHRoYXQgaGFzIGEgYHNldFN0eWxlYCBtZXRob2QuXHJcblx0c2V0U3R5bGU6IGZ1bmN0aW9uIChzdHlsZSkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdzZXRTdHlsZScsIHN0eWxlKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGJyaW5nVG9Gcm9udCgpOiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoZSBsYXllciBncm91cCB0byB0aGUgdG9wIG9mIGFsbCBvdGhlciBsYXllcnNcclxuXHRicmluZ1RvRnJvbnQ6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLmludm9rZSgnYnJpbmdUb0Zyb250Jyk7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBicmluZ1RvQmFjaygpOiB0aGlzXHJcblx0Ly8gQnJpbmdzIHRoZSBsYXllciBncm91cCB0byB0aGUgdG9wIG9mIGFsbCBvdGhlciBsYXllcnNcclxuXHRicmluZ1RvQmFjazogZnVuY3Rpb24gKCkge1xyXG5cdFx0cmV0dXJuIHRoaXMuaW52b2tlKCdicmluZ1RvQmFjaycpO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xyXG5cdC8vIFJldHVybnMgdGhlIExhdExuZ0JvdW5kcyBvZiB0aGUgRmVhdHVyZSBHcm91cCAoY3JlYXRlZCBmcm9tIGJvdW5kcyBhbmQgY29vcmRpbmF0ZXMgb2YgaXRzIGNoaWxkcmVuKS5cclxuXHRnZXRCb3VuZHM6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBib3VuZHMgPSBuZXcgTC5MYXRMbmdCb3VuZHMoKTtcclxuXHJcblx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9sYXllcnMpIHtcclxuXHRcdFx0dmFyIGxheWVyID0gdGhpcy5fbGF5ZXJzW2lkXTtcclxuXHRcdFx0Ym91bmRzLmV4dGVuZChsYXllci5nZXRCb3VuZHMgPyBsYXllci5nZXRCb3VuZHMoKSA6IGxheWVyLmdldExhdExuZygpKTtcclxuXHRcdH1cclxuXHRcdHJldHVybiBib3VuZHM7XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBmYWN0b3J5IEwuZmVhdHVyZUdyb3VwKGxheWVyczogTGF5ZXJbXSlcclxuLy8gQ3JlYXRlIGEgZmVhdHVyZSBncm91cCwgb3B0aW9uYWxseSBnaXZlbiBhbiBpbml0aWFsIHNldCBvZiBsYXllcnMuXHJcbkwuZmVhdHVyZUdyb3VwID0gZnVuY3Rpb24gKGxheWVycykge1xyXG5cdHJldHVybiBuZXcgTC5GZWF0dXJlR3JvdXAobGF5ZXJzKTtcclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBSZW5kZXJlclxuICogQGluaGVyaXRzIExheWVyXG4gKiBAYWthIEwuUmVuZGVyZXJcbiAqXG4gKiBCYXNlIGNsYXNzIGZvciB2ZWN0b3IgcmVuZGVyZXIgaW1wbGVtZW50YXRpb25zIChgU1ZHYCwgYENhbnZhc2ApLiBIYW5kbGVzIHRoZVxuICogRE9NIGNvbnRhaW5lciBvZiB0aGUgcmVuZGVyZXIsIGl0cyBib3VuZHMsIGFuZCBpdHMgem9vbSBhbmltYXRpb24uXG4gKlxuICogQSBgUmVuZGVyZXJgIHdvcmtzIGFzIGFuIGltcGxpY2l0IGxheWVyIGdyb3VwIGZvciBhbGwgYFBhdGhgcyAtIHRoZSByZW5kZXJlclxuICogaXRzZWxmIGNhbiBiZSBhZGRlZCBvciByZW1vdmVkIHRvIHRoZSBtYXAuIEFsbCBwYXRocyB1c2UgYSByZW5kZXJlciwgd2hpY2ggY2FuXG4gKiBiZSBpbXBsaWNpdCAodGhlIG1hcCB3aWxsIGRlY2lkZSB0aGUgdHlwZSBvZiByZW5kZXJlciBhbmQgdXNlIGl0IGF1dG9tYXRpY2FsbHkpXG4gKiBvciBleHBsaWNpdCAodXNpbmcgdGhlIFtgcmVuZGVyZXJgXSgjcGF0aC1yZW5kZXJlcikgb3B0aW9uIG9mIHRoZSBwYXRoKS5cbiAqXG4gKiBEbyBub3QgdXNlIHRoaXMgY2xhc3MgZGlyZWN0bHksIHVzZSBgU1ZHYCBhbmQgYENhbnZhc2AgaW5zdGVhZC5cbiAqXG4gKiBAZXZlbnQgdXBkYXRlOiBFdmVudFxuICogRmlyZWQgd2hlbiB0aGUgcmVuZGVyZXIgdXBkYXRlcyBpdHMgYm91bmRzLCBjZW50ZXIgYW5kIHpvb20sIGZvciBleGFtcGxlIHdoZW5cbiAqIGl0cyBtYXAgaGFzIG1vdmVkXG4gKi9cblxuTC5SZW5kZXJlciA9IEwuTGF5ZXIuZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFJlbmRlcmVyIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gcGFkZGluZzogTnVtYmVyID0gMC4xXG5cdFx0Ly8gSG93IG11Y2ggdG8gZXh0ZW5kIHRoZSBjbGlwIGFyZWEgYXJvdW5kIHRoZSBtYXAgdmlldyAocmVsYXRpdmUgdG8gaXRzIHNpemUpXG5cdFx0Ly8gZS5nLiAwLjEgd291bGQgYmUgMTAlIG9mIG1hcCB2aWV3IGluIGVhY2ggZGlyZWN0aW9uXG5cdFx0cGFkZGluZzogMC4xXG5cdH0sXG5cblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdFx0TC5zdGFtcCh0aGlzKTtcblx0XHR0aGlzLl9sYXllcnMgPSB0aGlzLl9sYXllcnMgfHwge307XG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX2NvbnRhaW5lcikge1xuXHRcdFx0dGhpcy5faW5pdENvbnRhaW5lcigpOyAvLyBkZWZpbmVkIGJ5IHJlbmRlcmVyIGltcGxlbWVudGF0aW9uc1xuXG5cdFx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XG5cdFx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LXpvb20tYW5pbWF0ZWQnKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLmdldFBhbmUoKS5hcHBlbmRDaGlsZCh0aGlzLl9jb250YWluZXIpO1xuXHRcdHRoaXMuX3VwZGF0ZSgpO1xuXHRcdHRoaXMub24oJ3VwZGF0ZScsIHRoaXMuX3VwZGF0ZVBhdGhzLCB0aGlzKTtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fY29udGFpbmVyKTtcblx0XHR0aGlzLm9mZigndXBkYXRlJywgdGhpcy5fdXBkYXRlUGF0aHMsIHRoaXMpO1xuXHR9LFxuXG5cdGdldEV2ZW50czogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBldmVudHMgPSB7XG5cdFx0XHR2aWV3cmVzZXQ6IHRoaXMuX3Jlc2V0LFxuXHRcdFx0em9vbTogdGhpcy5fb25ab29tLFxuXHRcdFx0bW92ZWVuZDogdGhpcy5fdXBkYXRlLFxuXHRcdFx0em9vbWVuZDogdGhpcy5fb25ab29tRW5kXG5cdFx0fTtcblx0XHRpZiAodGhpcy5fem9vbUFuaW1hdGVkKSB7XG5cdFx0XHRldmVudHMuem9vbWFuaW0gPSB0aGlzLl9vbkFuaW1ab29tO1xuXHRcdH1cblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdF9vbkFuaW1ab29tOiBmdW5jdGlvbiAoZXYpIHtcblx0XHR0aGlzLl91cGRhdGVUcmFuc2Zvcm0oZXYuY2VudGVyLCBldi56b29tKTtcblx0fSxcblxuXHRfb25ab29tOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fdXBkYXRlVHJhbnNmb3JtKHRoaXMuX21hcC5nZXRDZW50ZXIoKSwgdGhpcy5fbWFwLmdldFpvb20oKSk7XG5cdH0sXG5cblx0X3VwZGF0ZVRyYW5zZm9ybTogZnVuY3Rpb24gKGNlbnRlciwgem9vbSkge1xuXHRcdHZhciBzY2FsZSA9IHRoaXMuX21hcC5nZXRab29tU2NhbGUoem9vbSwgdGhpcy5fem9vbSksXG5cdFx0ICAgIHBvc2l0aW9uID0gTC5Eb21VdGlsLmdldFBvc2l0aW9uKHRoaXMuX2NvbnRhaW5lciksXG5cdFx0ICAgIHZpZXdIYWxmID0gdGhpcy5fbWFwLmdldFNpemUoKS5tdWx0aXBseUJ5KDAuNSArIHRoaXMub3B0aW9ucy5wYWRkaW5nKSxcblx0XHQgICAgY3VycmVudENlbnRlclBvaW50ID0gdGhpcy5fbWFwLnByb2plY3QodGhpcy5fY2VudGVyLCB6b29tKSxcblx0XHQgICAgZGVzdENlbnRlclBvaW50ID0gdGhpcy5fbWFwLnByb2plY3QoY2VudGVyLCB6b29tKSxcblx0XHQgICAgY2VudGVyT2Zmc2V0ID0gZGVzdENlbnRlclBvaW50LnN1YnRyYWN0KGN1cnJlbnRDZW50ZXJQb2ludCksXG5cblx0XHQgICAgdG9wTGVmdE9mZnNldCA9IHZpZXdIYWxmLm11bHRpcGx5QnkoLXNjYWxlKS5hZGQocG9zaXRpb24pLmFkZCh2aWV3SGFsZikuc3VidHJhY3QoY2VudGVyT2Zmc2V0KTtcblxuXHRcdGlmIChMLkJyb3dzZXIuYW55M2QpIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRUcmFuc2Zvcm0odGhpcy5fY29udGFpbmVyLCB0b3BMZWZ0T2Zmc2V0LCBzY2FsZSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdEwuRG9tVXRpbC5zZXRQb3NpdGlvbih0aGlzLl9jb250YWluZXIsIHRvcExlZnRPZmZzZXQpO1xuXHRcdH1cblx0fSxcblxuXHRfcmVzZXQ6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl91cGRhdGUoKTtcblx0XHR0aGlzLl91cGRhdGVUcmFuc2Zvcm0odGhpcy5fY2VudGVyLCB0aGlzLl96b29tKTtcblxuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0dGhpcy5fbGF5ZXJzW2lkXS5fcmVzZXQoKTtcblx0XHR9XG5cdH0sXG5cblx0X29uWm9vbUVuZDogZnVuY3Rpb24gKCkge1xuXHRcdGZvciAodmFyIGlkIGluIHRoaXMuX2xheWVycykge1xuXHRcdFx0dGhpcy5fbGF5ZXJzW2lkXS5fcHJvamVjdCgpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlUGF0aHM6IGZ1bmN0aW9uICgpIHtcblx0XHRmb3IgKHZhciBpZCBpbiB0aGlzLl9sYXllcnMpIHtcblx0XHRcdHRoaXMuX2xheWVyc1tpZF0uX3VwZGF0ZSgpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gVXBkYXRlIHBpeGVsIGJvdW5kcyBvZiByZW5kZXJlciBjb250YWluZXIgKGZvciBwb3NpdGlvbmluZy9zaXppbmcvY2xpcHBpbmcgbGF0ZXIpXG5cdFx0Ly8gU3ViY2xhc3NlcyBhcmUgcmVzcG9uc2libGUgb2YgZmlyaW5nIHRoZSAndXBkYXRlJyBldmVudC5cblx0XHR2YXIgcCA9IHRoaXMub3B0aW9ucy5wYWRkaW5nLFxuXHRcdCAgICBzaXplID0gdGhpcy5fbWFwLmdldFNpemUoKSxcblx0XHQgICAgbWluID0gdGhpcy5fbWFwLmNvbnRhaW5lclBvaW50VG9MYXllclBvaW50KHNpemUubXVsdGlwbHlCeSgtcCkpLnJvdW5kKCk7XG5cblx0XHR0aGlzLl9ib3VuZHMgPSBuZXcgTC5Cb3VuZHMobWluLCBtaW4uYWRkKHNpemUubXVsdGlwbHlCeSgxICsgcCAqIDIpKS5yb3VuZCgpKTtcblxuXHRcdHRoaXMuX2NlbnRlciA9IHRoaXMuX21hcC5nZXRDZW50ZXIoKTtcblx0XHR0aGlzLl96b29tID0gdGhpcy5fbWFwLmdldFpvb20oKTtcblx0fVxufSk7XG5cblxuTC5NYXAuaW5jbHVkZSh7XG5cdC8vIEBuYW1lc3BhY2UgTWFwOyBAbWV0aG9kIGdldFJlbmRlcmVyKGxheWVyOiBQYXRoKTogUmVuZGVyZXJcblx0Ly8gUmV0dXJucyB0aGUgaW5zdGFuY2Ugb2YgYFJlbmRlcmVyYCB0aGF0IHNob3VsZCBiZSB1c2VkIHRvIHJlbmRlciB0aGUgZ2l2ZW5cblx0Ly8gYFBhdGhgLiBJdCB3aWxsIGVuc3VyZSB0aGF0IHRoZSBgcmVuZGVyZXJgIG9wdGlvbnMgb2YgdGhlIG1hcCBhbmQgcGF0aHNcblx0Ly8gYXJlIHJlc3BlY3RlZCwgYW5kIHRoYXQgdGhlIHJlbmRlcmVycyBkbyBleGlzdCBvbiB0aGUgbWFwLlxuXHRnZXRSZW5kZXJlcjogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0Ly8gQG5hbWVzcGFjZSBQYXRoOyBAb3B0aW9uIHJlbmRlcmVyOiBSZW5kZXJlclxuXHRcdC8vIFVzZSB0aGlzIHNwZWNpZmljIGluc3RhbmNlIG9mIGBSZW5kZXJlcmAgZm9yIHRoaXMgcGF0aC4gVGFrZXNcblx0XHQvLyBwcmVjZWRlbmNlIG92ZXIgdGhlIG1hcCdzIFtkZWZhdWx0IHJlbmRlcmVyXSgjbWFwLXJlbmRlcmVyKS5cblx0XHR2YXIgcmVuZGVyZXIgPSBsYXllci5vcHRpb25zLnJlbmRlcmVyIHx8IHRoaXMuX2dldFBhbmVSZW5kZXJlcihsYXllci5vcHRpb25zLnBhbmUpIHx8IHRoaXMub3B0aW9ucy5yZW5kZXJlciB8fCB0aGlzLl9yZW5kZXJlcjtcblxuXHRcdGlmICghcmVuZGVyZXIpIHtcblx0XHRcdC8vIEBuYW1lc3BhY2UgTWFwOyBAb3B0aW9uIHByZWZlckNhbnZhczogQm9vbGVhbiA9IGZhbHNlXG5cdFx0XHQvLyBXaGV0aGVyIGBQYXRoYHMgc2hvdWxkIGJlIHJlbmRlcmVkIG9uIGEgYENhbnZhc2AgcmVuZGVyZXIuXG5cdFx0XHQvLyBCeSBkZWZhdWx0LCBhbGwgYFBhdGhgcyBhcmUgcmVuZGVyZWQgaW4gYSBgU1ZHYCByZW5kZXJlci5cblx0XHRcdHJlbmRlcmVyID0gdGhpcy5fcmVuZGVyZXIgPSAodGhpcy5vcHRpb25zLnByZWZlckNhbnZhcyAmJiBMLmNhbnZhcygpKSB8fCBMLnN2ZygpO1xuXHRcdH1cblxuXHRcdGlmICghdGhpcy5oYXNMYXllcihyZW5kZXJlcikpIHtcblx0XHRcdHRoaXMuYWRkTGF5ZXIocmVuZGVyZXIpO1xuXHRcdH1cblx0XHRyZXR1cm4gcmVuZGVyZXI7XG5cdH0sXG5cblx0X2dldFBhbmVSZW5kZXJlcjogZnVuY3Rpb24gKG5hbWUpIHtcblx0XHRpZiAobmFtZSA9PT0gJ292ZXJsYXlQYW5lJyB8fCBuYW1lID09PSB1bmRlZmluZWQpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHR2YXIgcmVuZGVyZXIgPSB0aGlzLl9wYW5lUmVuZGVyZXJzW25hbWVdO1xuXHRcdGlmIChyZW5kZXJlciA9PT0gdW5kZWZpbmVkKSB7XG5cdFx0XHRyZW5kZXJlciA9IChMLlNWRyAmJiBMLnN2Zyh7cGFuZTogbmFtZX0pKSB8fCAoTC5DYW52YXMgJiYgTC5jYW52YXMoe3BhbmU6IG5hbWV9KSk7XG5cdFx0XHR0aGlzLl9wYW5lUmVuZGVyZXJzW25hbWVdID0gcmVuZGVyZXI7XG5cdFx0fVxuXHRcdHJldHVybiByZW5kZXJlcjtcblx0fVxufSk7XG5cblxuXG4vKlxuICogQGNsYXNzIFBhdGhcbiAqIEBha2EgTC5QYXRoXG4gKiBAaW5oZXJpdHMgSW50ZXJhY3RpdmUgbGF5ZXJcbiAqXG4gKiBBbiBhYnN0cmFjdCBjbGFzcyB0aGF0IGNvbnRhaW5zIG9wdGlvbnMgYW5kIGNvbnN0YW50cyBzaGFyZWQgYmV0d2VlbiB2ZWN0b3JcbiAqIG92ZXJsYXlzIChQb2x5Z29uLCBQb2x5bGluZSwgQ2lyY2xlKS4gRG8gbm90IHVzZSBpdCBkaXJlY3RseS4gRXh0ZW5kcyBgTGF5ZXJgLlxuICovXG5cbkwuUGF0aCA9IEwuTGF5ZXIuZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFBhdGggb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0Ly8gQG9wdGlvbiBzdHJva2U6IEJvb2xlYW4gPSB0cnVlXG5cdFx0Ly8gV2hldGhlciB0byBkcmF3IHN0cm9rZSBhbG9uZyB0aGUgcGF0aC4gU2V0IGl0IHRvIGBmYWxzZWAgdG8gZGlzYWJsZSBib3JkZXJzIG9uIHBvbHlnb25zIG9yIGNpcmNsZXMuXG5cdFx0c3Ryb2tlOiB0cnVlLFxuXG5cdFx0Ly8gQG9wdGlvbiBjb2xvcjogU3RyaW5nID0gJyMzMzg4ZmYnXG5cdFx0Ly8gU3Ryb2tlIGNvbG9yXG5cdFx0Y29sb3I6ICcjMzM4OGZmJyxcblxuXHRcdC8vIEBvcHRpb24gd2VpZ2h0OiBOdW1iZXIgPSAzXG5cdFx0Ly8gU3Ryb2tlIHdpZHRoIGluIHBpeGVsc1xuXHRcdHdlaWdodDogMyxcblxuXHRcdC8vIEBvcHRpb24gb3BhY2l0eTogTnVtYmVyID0gMS4wXG5cdFx0Ly8gU3Ryb2tlIG9wYWNpdHlcblx0XHRvcGFjaXR5OiAxLFxuXG5cdFx0Ly8gQG9wdGlvbiBsaW5lQ2FwOiBTdHJpbmc9ICdyb3VuZCdcblx0XHQvLyBBIHN0cmluZyB0aGF0IGRlZmluZXMgW3NoYXBlIHRvIGJlIHVzZWQgYXQgdGhlIGVuZF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtbGluZWNhcCkgb2YgdGhlIHN0cm9rZS5cblx0XHRsaW5lQ2FwOiAncm91bmQnLFxuXG5cdFx0Ly8gQG9wdGlvbiBsaW5lSm9pbjogU3RyaW5nID0gJ3JvdW5kJ1xuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyBbc2hhcGUgdG8gYmUgdXNlZCBhdCB0aGUgY29ybmVyc10oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9zdHJva2UtbGluZWpvaW4pIG9mIHRoZSBzdHJva2UuXG5cdFx0bGluZUpvaW46ICdyb3VuZCcsXG5cblx0XHQvLyBAb3B0aW9uIGRhc2hBcnJheTogU3RyaW5nID0gbnVsbFxuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyB0aGUgc3Ryb2tlIFtkYXNoIHBhdHRlcm5dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRy9BdHRyaWJ1dGUvc3Ryb2tlLWRhc2hhcnJheSkuIERvZXNuJ3Qgd29yayBvbiBgQ2FudmFzYC1wb3dlcmVkIGxheWVycyBpbiBbc29tZSBvbGQgYnJvd3NlcnNdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9DYW52YXNSZW5kZXJpbmdDb250ZXh0MkQvc2V0TGluZURhc2gjQnJvd3Nlcl9jb21wYXRpYmlsaXR5KS5cblx0XHRkYXNoQXJyYXk6IG51bGwsXG5cblx0XHQvLyBAb3B0aW9uIGRhc2hPZmZzZXQ6IFN0cmluZyA9IG51bGxcblx0XHQvLyBBIHN0cmluZyB0aGF0IGRlZmluZXMgdGhlIFtkaXN0YW5jZSBpbnRvIHRoZSBkYXNoIHBhdHRlcm4gdG8gc3RhcnQgdGhlIGRhc2hdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRy9BdHRyaWJ1dGUvc3Ryb2tlLWRhc2hvZmZzZXQpLiBEb2Vzbid0IHdvcmsgb24gYENhbnZhc2AtcG93ZXJlZCBsYXllcnMgaW4gW3NvbWUgb2xkIGJyb3dzZXJzXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEL3NldExpbmVEYXNoI0Jyb3dzZXJfY29tcGF0aWJpbGl0eSkuXG5cdFx0ZGFzaE9mZnNldDogbnVsbCxcblxuXHRcdC8vIEBvcHRpb24gZmlsbDogQm9vbGVhbiA9IGRlcGVuZHNcblx0XHQvLyBXaGV0aGVyIHRvIGZpbGwgdGhlIHBhdGggd2l0aCBjb2xvci4gU2V0IGl0IHRvIGBmYWxzZWAgdG8gZGlzYWJsZSBmaWxsaW5nIG9uIHBvbHlnb25zIG9yIGNpcmNsZXMuXG5cdFx0ZmlsbDogZmFsc2UsXG5cblx0XHQvLyBAb3B0aW9uIGZpbGxDb2xvcjogU3RyaW5nID0gKlxuXHRcdC8vIEZpbGwgY29sb3IuIERlZmF1bHRzIHRvIHRoZSB2YWx1ZSBvZiB0aGUgW2Bjb2xvcmBdKCNwYXRoLWNvbG9yKSBvcHRpb25cblx0XHRmaWxsQ29sb3I6IG51bGwsXG5cblx0XHQvLyBAb3B0aW9uIGZpbGxPcGFjaXR5OiBOdW1iZXIgPSAwLjJcblx0XHQvLyBGaWxsIG9wYWNpdHkuXG5cdFx0ZmlsbE9wYWNpdHk6IDAuMixcblxuXHRcdC8vIEBvcHRpb24gZmlsbFJ1bGU6IFN0cmluZyA9ICdldmVub2RkJ1xuXHRcdC8vIEEgc3RyaW5nIHRoYXQgZGVmaW5lcyBbaG93IHRoZSBpbnNpZGUgb2YgYSBzaGFwZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvU1ZHL0F0dHJpYnV0ZS9maWxsLXJ1bGUpIGlzIGRldGVybWluZWQuXG5cdFx0ZmlsbFJ1bGU6ICdldmVub2RkJyxcblxuXHRcdC8vIGNsYXNzTmFtZTogJycsXG5cblx0XHQvLyBPcHRpb24gaW5oZXJpdGVkIGZyb20gXCJJbnRlcmFjdGl2ZSBsYXllclwiIGFic3RyYWN0IGNsYXNzXG5cdFx0aW50ZXJhY3RpdmU6IHRydWVcblx0fSxcblxuXHRiZWZvcmVBZGQ6IGZ1bmN0aW9uIChtYXApIHtcblx0XHQvLyBSZW5kZXJlciBpcyBzZXQgaGVyZSBiZWNhdXNlIHdlIG5lZWQgdG8gY2FsbCByZW5kZXJlci5nZXRFdmVudHNcblx0XHQvLyBiZWZvcmUgdGhpcy5nZXRFdmVudHMuXG5cdFx0dGhpcy5fcmVuZGVyZXIgPSBtYXAuZ2V0UmVuZGVyZXIodGhpcyk7XG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5faW5pdFBhdGgodGhpcyk7XG5cdFx0dGhpcy5fcmVzZXQoKTtcblx0XHR0aGlzLl9yZW5kZXJlci5fYWRkUGF0aCh0aGlzKTtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbmRlcmVyLl9yZW1vdmVQYXRoKHRoaXMpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgcmVkcmF3KCk6IHRoaXNcblx0Ly8gUmVkcmF3cyB0aGUgbGF5ZXIuIFNvbWV0aW1lcyB1c2VmdWwgYWZ0ZXIgeW91IGNoYW5nZWQgdGhlIGNvb3JkaW5hdGVzIHRoYXQgdGhlIHBhdGggdXNlcy5cblx0cmVkcmF3OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcCkge1xuXHRcdFx0dGhpcy5fcmVuZGVyZXIuX3VwZGF0ZVBhdGgodGhpcyk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0U3R5bGUoc3R5bGU6IFBhdGggb3B0aW9ucyk6IHRoaXNcblx0Ly8gQ2hhbmdlcyB0aGUgYXBwZWFyYW5jZSBvZiBhIFBhdGggYmFzZWQgb24gdGhlIG9wdGlvbnMgaW4gdGhlIGBQYXRoIG9wdGlvbnNgIG9iamVjdC5cblx0c2V0U3R5bGU6IGZ1bmN0aW9uIChzdHlsZSkge1xuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBzdHlsZSk7XG5cdFx0aWYgKHRoaXMuX3JlbmRlcmVyKSB7XG5cdFx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlU3R5bGUodGhpcyk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgYnJpbmdUb0Zyb250KCk6IHRoaXNcblx0Ly8gQnJpbmdzIHRoZSBsYXllciB0byB0aGUgdG9wIG9mIGFsbCBwYXRoIGxheWVycy5cblx0YnJpbmdUb0Zyb250OiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX3JlbmRlcmVyKSB7XG5cdFx0XHR0aGlzLl9yZW5kZXJlci5fYnJpbmdUb0Zyb250KHRoaXMpO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGJyaW5nVG9CYWNrKCk6IHRoaXNcblx0Ly8gQnJpbmdzIHRoZSBsYXllciB0byB0aGUgYm90dG9tIG9mIGFsbCBwYXRoIGxheWVycy5cblx0YnJpbmdUb0JhY2s6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fcmVuZGVyZXIpIHtcblx0XHRcdHRoaXMuX3JlbmRlcmVyLl9icmluZ1RvQmFjayh0aGlzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Z2V0RWxlbWVudDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9wYXRoO1xuXHR9LFxuXG5cdF9yZXNldDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIGRlZmluZWQgaW4gY2hpbGRyZW4gY2xhc3Nlc1xuXHRcdHRoaXMuX3Byb2plY3QoKTtcblx0XHR0aGlzLl91cGRhdGUoKTtcblx0fSxcblxuXHRfY2xpY2tUb2xlcmFuY2U6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyB1c2VkIHdoZW4gZG9pbmcgaGl0IGRldGVjdGlvbiBmb3IgQ2FudmFzIGxheWVyc1xuXHRcdHJldHVybiAodGhpcy5vcHRpb25zLnN0cm9rZSA/IHRoaXMub3B0aW9ucy53ZWlnaHQgLyAyIDogMCkgKyAoTC5Ccm93c2VyLnRvdWNoID8gMTAgOiAwKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAbmFtZXNwYWNlIExpbmVVdGlsXHJcbiAqXHJcbiAqIFZhcmlvdXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIHBvbHlpbmUgcG9pbnRzIHByb2Nlc3NpbmcsIHVzZWQgYnkgTGVhZmxldCBpbnRlcm5hbGx5IHRvIG1ha2UgcG9seWxpbmVzIGxpZ2h0bmluZy1mYXN0LlxyXG4gKi9cclxuXHJcbkwuTGluZVV0aWwgPSB7XHJcblxyXG5cdC8vIFNpbXBsaWZ5IHBvbHlsaW5lIHdpdGggdmVydGV4IHJlZHVjdGlvbiBhbmQgRG91Z2xhcy1QZXVja2VyIHNpbXBsaWZpY2F0aW9uLlxyXG5cdC8vIEltcHJvdmVzIHJlbmRlcmluZyBwZXJmb3JtYW5jZSBkcmFtYXRpY2FsbHkgYnkgbGVzc2VuaW5nIHRoZSBudW1iZXIgb2YgcG9pbnRzIHRvIGRyYXcuXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBzaW1wbGlmeShwb2ludHM6IFBvaW50W10sIHRvbGVyYW5jZTogTnVtYmVyKTogUG9pbnRbXVxyXG5cdC8vIERyYW1hdGljYWxseSByZWR1Y2VzIHRoZSBudW1iZXIgb2YgcG9pbnRzIGluIGEgcG9seWxpbmUgd2hpbGUgcmV0YWluaW5nXHJcblx0Ly8gaXRzIHNoYXBlIGFuZCByZXR1cm5zIGEgbmV3IGFycmF5IG9mIHNpbXBsaWZpZWQgcG9pbnRzLCB1c2luZyB0aGVcclxuXHQvLyBbRG91Z2xhcy1QZXVja2VyIGFsZ29yaXRobV0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Eb3VnbGFzLVBldWNrZXJfYWxnb3JpdGhtKS5cclxuXHQvLyBVc2VkIGZvciBhIGh1Z2UgcGVyZm9ybWFuY2UgYm9vc3Qgd2hlbiBwcm9jZXNzaW5nL2Rpc3BsYXlpbmcgTGVhZmxldCBwb2x5bGluZXMgZm9yXHJcblx0Ly8gZWFjaCB6b29tIGxldmVsIGFuZCBhbHNvIHJlZHVjaW5nIHZpc3VhbCBub2lzZS4gdG9sZXJhbmNlIGFmZmVjdHMgdGhlIGFtb3VudCBvZlxyXG5cdC8vIHNpbXBsaWZpY2F0aW9uIChsZXNzZXIgdmFsdWUgbWVhbnMgaGlnaGVyIHF1YWxpdHkgYnV0IHNsb3dlciBhbmQgd2l0aCBtb3JlIHBvaW50cykuXHJcblx0Ly8gQWxzbyByZWxlYXNlZCBhcyBhIHNlcGFyYXRlZCBtaWNyby1saWJyYXJ5IFtTaW1wbGlmeS5qc10oaHR0cDovL21vdXJuZXIuZ2l0aHViLmNvbS9zaW1wbGlmeS1qcy8pLlxyXG5cdHNpbXBsaWZ5OiBmdW5jdGlvbiAocG9pbnRzLCB0b2xlcmFuY2UpIHtcclxuXHRcdGlmICghdG9sZXJhbmNlIHx8ICFwb2ludHMubGVuZ3RoKSB7XHJcblx0XHRcdHJldHVybiBwb2ludHMuc2xpY2UoKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgc3FUb2xlcmFuY2UgPSB0b2xlcmFuY2UgKiB0b2xlcmFuY2U7XHJcblxyXG5cdFx0Ly8gc3RhZ2UgMTogdmVydGV4IHJlZHVjdGlvblxyXG5cdFx0cG9pbnRzID0gdGhpcy5fcmVkdWNlUG9pbnRzKHBvaW50cywgc3FUb2xlcmFuY2UpO1xyXG5cclxuXHRcdC8vIHN0YWdlIDI6IERvdWdsYXMtUGV1Y2tlciBzaW1wbGlmaWNhdGlvblxyXG5cdFx0cG9pbnRzID0gdGhpcy5fc2ltcGxpZnlEUChwb2ludHMsIHNxVG9sZXJhbmNlKTtcclxuXHJcblx0XHRyZXR1cm4gcG9pbnRzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBwb2ludFRvU2VnbWVudERpc3RhbmNlKHA6IFBvaW50LCBwMTogUG9pbnQsIHAyOiBQb2ludCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGRpc3RhbmNlIGJldHdlZW4gcG9pbnQgYHBgIGFuZCBzZWdtZW50IGBwMWAgdG8gYHAyYC5cclxuXHRwb2ludFRvU2VnbWVudERpc3RhbmNlOiAgZnVuY3Rpb24gKHAsIHAxLCBwMikge1xyXG5cdFx0cmV0dXJuIE1hdGguc3FydCh0aGlzLl9zcUNsb3Nlc3RQb2ludE9uU2VnbWVudChwLCBwMSwgcDIsIHRydWUpKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY2xvc2VzdFBvaW50T25TZWdtZW50KHA6IFBvaW50LCBwMTogUG9pbnQsIHAyOiBQb2ludCk6IE51bWJlclxyXG5cdC8vIFJldHVybnMgdGhlIGNsb3Nlc3QgcG9pbnQgZnJvbSBhIHBvaW50IGBwYCBvbiBhIHNlZ21lbnQgYHAxYCB0byBgcDJgLlxyXG5cdGNsb3Nlc3RQb2ludE9uU2VnbWVudDogZnVuY3Rpb24gKHAsIHAxLCBwMikge1xyXG5cdFx0cmV0dXJuIHRoaXMuX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50KHAsIHAxLCBwMik7XHJcblx0fSxcclxuXHJcblx0Ly8gRG91Z2xhcy1QZXVja2VyIHNpbXBsaWZpY2F0aW9uLCBzZWUgaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Eb3VnbGFzLVBldWNrZXJfYWxnb3JpdGhtXHJcblx0X3NpbXBsaWZ5RFA6IGZ1bmN0aW9uIChwb2ludHMsIHNxVG9sZXJhbmNlKSB7XHJcblxyXG5cdFx0dmFyIGxlbiA9IHBvaW50cy5sZW5ndGgsXHJcblx0XHQgICAgQXJyYXlDb25zdHJ1Y3RvciA9IHR5cGVvZiBVaW50OEFycmF5ICE9PSB1bmRlZmluZWQgKyAnJyA/IFVpbnQ4QXJyYXkgOiBBcnJheSxcclxuXHRcdCAgICBtYXJrZXJzID0gbmV3IEFycmF5Q29uc3RydWN0b3IobGVuKTtcclxuXHJcblx0XHRtYXJrZXJzWzBdID0gbWFya2Vyc1tsZW4gLSAxXSA9IDE7XHJcblxyXG5cdFx0dGhpcy5fc2ltcGxpZnlEUFN0ZXAocG9pbnRzLCBtYXJrZXJzLCBzcVRvbGVyYW5jZSwgMCwgbGVuIC0gMSk7XHJcblxyXG5cdFx0dmFyIGksXHJcblx0XHQgICAgbmV3UG9pbnRzID0gW107XHJcblxyXG5cdFx0Zm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdGlmIChtYXJrZXJzW2ldKSB7XHJcblx0XHRcdFx0bmV3UG9pbnRzLnB1c2gocG9pbnRzW2ldKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBuZXdQb2ludHM7XHJcblx0fSxcclxuXHJcblx0X3NpbXBsaWZ5RFBTdGVwOiBmdW5jdGlvbiAocG9pbnRzLCBtYXJrZXJzLCBzcVRvbGVyYW5jZSwgZmlyc3QsIGxhc3QpIHtcclxuXHJcblx0XHR2YXIgbWF4U3FEaXN0ID0gMCxcclxuXHRcdCAgICBpbmRleCwgaSwgc3FEaXN0O1xyXG5cclxuXHRcdGZvciAoaSA9IGZpcnN0ICsgMTsgaSA8PSBsYXN0IC0gMTsgaSsrKSB7XHJcblx0XHRcdHNxRGlzdCA9IHRoaXMuX3NxQ2xvc2VzdFBvaW50T25TZWdtZW50KHBvaW50c1tpXSwgcG9pbnRzW2ZpcnN0XSwgcG9pbnRzW2xhc3RdLCB0cnVlKTtcclxuXHJcblx0XHRcdGlmIChzcURpc3QgPiBtYXhTcURpc3QpIHtcclxuXHRcdFx0XHRpbmRleCA9IGk7XHJcblx0XHRcdFx0bWF4U3FEaXN0ID0gc3FEaXN0O1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKG1heFNxRGlzdCA+IHNxVG9sZXJhbmNlKSB7XHJcblx0XHRcdG1hcmtlcnNbaW5kZXhdID0gMTtcclxuXHJcblx0XHRcdHRoaXMuX3NpbXBsaWZ5RFBTdGVwKHBvaW50cywgbWFya2Vycywgc3FUb2xlcmFuY2UsIGZpcnN0LCBpbmRleCk7XHJcblx0XHRcdHRoaXMuX3NpbXBsaWZ5RFBTdGVwKHBvaW50cywgbWFya2Vycywgc3FUb2xlcmFuY2UsIGluZGV4LCBsYXN0KTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyByZWR1Y2UgcG9pbnRzIHRoYXQgYXJlIHRvbyBjbG9zZSB0byBlYWNoIG90aGVyIHRvIGEgc2luZ2xlIHBvaW50XHJcblx0X3JlZHVjZVBvaW50czogZnVuY3Rpb24gKHBvaW50cywgc3FUb2xlcmFuY2UpIHtcclxuXHRcdHZhciByZWR1Y2VkUG9pbnRzID0gW3BvaW50c1swXV07XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IDEsIHByZXYgPSAwLCBsZW4gPSBwb2ludHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0aWYgKHRoaXMuX3NxRGlzdChwb2ludHNbaV0sIHBvaW50c1twcmV2XSkgPiBzcVRvbGVyYW5jZSkge1xyXG5cdFx0XHRcdHJlZHVjZWRQb2ludHMucHVzaChwb2ludHNbaV0pO1xyXG5cdFx0XHRcdHByZXYgPSBpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHRpZiAocHJldiA8IGxlbiAtIDEpIHtcclxuXHRcdFx0cmVkdWNlZFBvaW50cy5wdXNoKHBvaW50c1tsZW4gLSAxXSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gcmVkdWNlZFBvaW50cztcclxuXHR9LFxyXG5cclxuXHJcblx0Ly8gQGZ1bmN0aW9uIGNsaXBTZWdtZW50KGE6IFBvaW50LCBiOiBQb2ludCwgYm91bmRzOiBCb3VuZHMsIHVzZUxhc3RDb2RlPzogQm9vbGVhbiwgcm91bmQ/OiBCb29sZWFuKTogUG9pbnRbXXxCb29sZWFuXHJcblx0Ly8gQ2xpcHMgdGhlIHNlZ21lbnQgYSB0byBiIGJ5IHJlY3Rhbmd1bGFyIGJvdW5kcyB3aXRoIHRoZVxyXG5cdC8vIFtDb2hlbi1TdXRoZXJsYW5kIGFsZ29yaXRobV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29oZW4lRTIlODAlOTNTdXRoZXJsYW5kX2FsZ29yaXRobSlcclxuXHQvLyAobW9kaWZ5aW5nIHRoZSBzZWdtZW50IHBvaW50cyBkaXJlY3RseSEpLiBVc2VkIGJ5IExlYWZsZXQgdG8gb25seSBzaG93IHBvbHlsaW5lXHJcblx0Ly8gcG9pbnRzIHRoYXQgYXJlIG9uIHRoZSBzY3JlZW4gb3IgbmVhciwgaW5jcmVhc2luZyBwZXJmb3JtYW5jZS5cclxuXHRjbGlwU2VnbWVudDogZnVuY3Rpb24gKGEsIGIsIGJvdW5kcywgdXNlTGFzdENvZGUsIHJvdW5kKSB7XHJcblx0XHR2YXIgY29kZUEgPSB1c2VMYXN0Q29kZSA/IHRoaXMuX2xhc3RDb2RlIDogdGhpcy5fZ2V0Qml0Q29kZShhLCBib3VuZHMpLFxyXG5cdFx0ICAgIGNvZGVCID0gdGhpcy5fZ2V0Qml0Q29kZShiLCBib3VuZHMpLFxyXG5cclxuXHRcdCAgICBjb2RlT3V0LCBwLCBuZXdDb2RlO1xyXG5cclxuXHRcdC8vIHNhdmUgMm5kIGNvZGUgdG8gYXZvaWQgY2FsY3VsYXRpbmcgaXQgb24gdGhlIG5leHQgc2VnbWVudFxyXG5cdFx0dGhpcy5fbGFzdENvZGUgPSBjb2RlQjtcclxuXHJcblx0XHR3aGlsZSAodHJ1ZSkge1xyXG5cdFx0XHQvLyBpZiBhLGIgaXMgaW5zaWRlIHRoZSBjbGlwIHdpbmRvdyAodHJpdmlhbCBhY2NlcHQpXHJcblx0XHRcdGlmICghKGNvZGVBIHwgY29kZUIpKSB7XHJcblx0XHRcdFx0cmV0dXJuIFthLCBiXTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gaWYgYSxiIGlzIG91dHNpZGUgdGhlIGNsaXAgd2luZG93ICh0cml2aWFsIHJlamVjdClcclxuXHRcdFx0aWYgKGNvZGVBICYgY29kZUIpIHtcclxuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdC8vIG90aGVyIGNhc2VzXHJcblx0XHRcdGNvZGVPdXQgPSBjb2RlQSB8fCBjb2RlQjtcclxuXHRcdFx0cCA9IHRoaXMuX2dldEVkZ2VJbnRlcnNlY3Rpb24oYSwgYiwgY29kZU91dCwgYm91bmRzLCByb3VuZCk7XHJcblx0XHRcdG5ld0NvZGUgPSB0aGlzLl9nZXRCaXRDb2RlKHAsIGJvdW5kcyk7XHJcblxyXG5cdFx0XHRpZiAoY29kZU91dCA9PT0gY29kZUEpIHtcclxuXHRcdFx0XHRhID0gcDtcclxuXHRcdFx0XHRjb2RlQSA9IG5ld0NvZGU7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0YiA9IHA7XHJcblx0XHRcdFx0Y29kZUIgPSBuZXdDb2RlO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2dldEVkZ2VJbnRlcnNlY3Rpb246IGZ1bmN0aW9uIChhLCBiLCBjb2RlLCBib3VuZHMsIHJvdW5kKSB7XHJcblx0XHR2YXIgZHggPSBiLnggLSBhLngsXHJcblx0XHQgICAgZHkgPSBiLnkgLSBhLnksXHJcblx0XHQgICAgbWluID0gYm91bmRzLm1pbixcclxuXHRcdCAgICBtYXggPSBib3VuZHMubWF4LFxyXG5cdFx0ICAgIHgsIHk7XHJcblxyXG5cdFx0aWYgKGNvZGUgJiA4KSB7IC8vIHRvcFxyXG5cdFx0XHR4ID0gYS54ICsgZHggKiAobWF4LnkgLSBhLnkpIC8gZHk7XHJcblx0XHRcdHkgPSBtYXgueTtcclxuXHJcblx0XHR9IGVsc2UgaWYgKGNvZGUgJiA0KSB7IC8vIGJvdHRvbVxyXG5cdFx0XHR4ID0gYS54ICsgZHggKiAobWluLnkgLSBhLnkpIC8gZHk7XHJcblx0XHRcdHkgPSBtaW4ueTtcclxuXHJcblx0XHR9IGVsc2UgaWYgKGNvZGUgJiAyKSB7IC8vIHJpZ2h0XHJcblx0XHRcdHggPSBtYXgueDtcclxuXHRcdFx0eSA9IGEueSArIGR5ICogKG1heC54IC0gYS54KSAvIGR4O1xyXG5cclxuXHRcdH0gZWxzZSBpZiAoY29kZSAmIDEpIHsgLy8gbGVmdFxyXG5cdFx0XHR4ID0gbWluLng7XHJcblx0XHRcdHkgPSBhLnkgKyBkeSAqIChtaW4ueCAtIGEueCkgLyBkeDtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gbmV3IEwuUG9pbnQoeCwgeSwgcm91bmQpO1xyXG5cdH0sXHJcblxyXG5cdF9nZXRCaXRDb2RlOiBmdW5jdGlvbiAocCwgYm91bmRzKSB7XHJcblx0XHR2YXIgY29kZSA9IDA7XHJcblxyXG5cdFx0aWYgKHAueCA8IGJvdW5kcy5taW4ueCkgeyAvLyBsZWZ0XHJcblx0XHRcdGNvZGUgfD0gMTtcclxuXHRcdH0gZWxzZSBpZiAocC54ID4gYm91bmRzLm1heC54KSB7IC8vIHJpZ2h0XHJcblx0XHRcdGNvZGUgfD0gMjtcclxuXHRcdH1cclxuXHJcblx0XHRpZiAocC55IDwgYm91bmRzLm1pbi55KSB7IC8vIGJvdHRvbVxyXG5cdFx0XHRjb2RlIHw9IDQ7XHJcblx0XHR9IGVsc2UgaWYgKHAueSA+IGJvdW5kcy5tYXgueSkgeyAvLyB0b3BcclxuXHRcdFx0Y29kZSB8PSA4O1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiBjb2RlO1xyXG5cdH0sXHJcblxyXG5cdC8vIHNxdWFyZSBkaXN0YW5jZSAodG8gYXZvaWQgdW5uZWNlc3NhcnkgTWF0aC5zcXJ0IGNhbGxzKVxyXG5cdF9zcURpc3Q6IGZ1bmN0aW9uIChwMSwgcDIpIHtcclxuXHRcdHZhciBkeCA9IHAyLnggLSBwMS54LFxyXG5cdFx0ICAgIGR5ID0gcDIueSAtIHAxLnk7XHJcblx0XHRyZXR1cm4gZHggKiBkeCArIGR5ICogZHk7XHJcblx0fSxcclxuXHJcblx0Ly8gcmV0dXJuIGNsb3Nlc3QgcG9pbnQgb24gc2VnbWVudCBvciBkaXN0YW5jZSB0byB0aGF0IHBvaW50XHJcblx0X3NxQ2xvc2VzdFBvaW50T25TZWdtZW50OiBmdW5jdGlvbiAocCwgcDEsIHAyLCBzcURpc3QpIHtcclxuXHRcdHZhciB4ID0gcDEueCxcclxuXHRcdCAgICB5ID0gcDEueSxcclxuXHRcdCAgICBkeCA9IHAyLnggLSB4LFxyXG5cdFx0ICAgIGR5ID0gcDIueSAtIHksXHJcblx0XHQgICAgZG90ID0gZHggKiBkeCArIGR5ICogZHksXHJcblx0XHQgICAgdDtcclxuXHJcblx0XHRpZiAoZG90ID4gMCkge1xyXG5cdFx0XHR0ID0gKChwLnggLSB4KSAqIGR4ICsgKHAueSAtIHkpICogZHkpIC8gZG90O1xyXG5cclxuXHRcdFx0aWYgKHQgPiAxKSB7XHJcblx0XHRcdFx0eCA9IHAyLng7XHJcblx0XHRcdFx0eSA9IHAyLnk7XHJcblx0XHRcdH0gZWxzZSBpZiAodCA+IDApIHtcclxuXHRcdFx0XHR4ICs9IGR4ICogdDtcclxuXHRcdFx0XHR5ICs9IGR5ICogdDtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdGR4ID0gcC54IC0geDtcclxuXHRcdGR5ID0gcC55IC0geTtcclxuXHJcblx0XHRyZXR1cm4gc3FEaXN0ID8gZHggKiBkeCArIGR5ICogZHkgOiBuZXcgTC5Qb2ludCh4LCB5KTtcclxuXHR9XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgUG9seWxpbmVcbiAqIEBha2EgTC5Qb2x5bGluZVxuICogQGluaGVyaXRzIFBhdGhcbiAqXG4gKiBBIGNsYXNzIGZvciBkcmF3aW5nIHBvbHlsaW5lIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBQYXRoYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiAvLyBjcmVhdGUgYSByZWQgcG9seWxpbmUgZnJvbSBhbiBhcnJheSBvZiBMYXRMbmcgcG9pbnRzXG4gKiB2YXIgbGF0bG5ncyA9IFtcbiAqIFx0WzQ1LjUxLCAtMTIyLjY4XSxcbiAqIFx0WzM3Ljc3LCAtMTIyLjQzXSxcbiAqIFx0WzM0LjA0LCAtMTE4LjJdXG4gKiBdO1xuICpcbiAqIHZhciBwb2x5bGluZSA9IEwucG9seWxpbmUobGF0bG5ncywge2NvbG9yOiAncmVkJ30pLmFkZFRvKG1hcCk7XG4gKlxuICogLy8gem9vbSB0aGUgbWFwIHRvIHRoZSBwb2x5bGluZVxuICogbWFwLmZpdEJvdW5kcyhwb2x5bGluZS5nZXRCb3VuZHMoKSk7XG4gKiBgYGBcbiAqXG4gKiBZb3UgY2FuIGFsc28gcGFzcyBhIG11bHRpLWRpbWVuc2lvbmFsIGFycmF5IHRvIHJlcHJlc2VudCBhIGBNdWx0aVBvbHlsaW5lYCBzaGFwZTpcbiAqXG4gKiBgYGBqc1xuICogLy8gY3JlYXRlIGEgcmVkIHBvbHlsaW5lIGZyb20gYW4gYXJyYXkgb2YgYXJyYXlzIG9mIExhdExuZyBwb2ludHNcbiAqIHZhciBsYXRsbmdzID0gW1xuICogXHRbWzQ1LjUxLCAtMTIyLjY4XSxcbiAqIFx0IFszNy43NywgLTEyMi40M10sXG4gKiBcdCBbMzQuMDQsIC0xMTguMl1dLFxuICogXHRbWzQwLjc4LCAtNzMuOTFdLFxuICogXHQgWzQxLjgzLCAtODcuNjJdLFxuICogXHQgWzMyLjc2LCAtOTYuNzJdXVxuICogXTtcbiAqIGBgYFxuICovXG5cbkwuUG9seWxpbmUgPSBMLlBhdGguZXh0ZW5kKHtcblxuXHQvLyBAc2VjdGlvblxuXHQvLyBAYWthIFBvbHlsaW5lIG9wdGlvbnNcblx0b3B0aW9uczoge1xuXHRcdC8vIEBvcHRpb24gc21vb3RoRmFjdG9yOiBOdW1iZXIgPSAxLjBcblx0XHQvLyBIb3cgbXVjaCB0byBzaW1wbGlmeSB0aGUgcG9seWxpbmUgb24gZWFjaCB6b29tIGxldmVsLiBNb3JlIG1lYW5zXG5cdFx0Ly8gYmV0dGVyIHBlcmZvcm1hbmNlIGFuZCBzbW9vdGhlciBsb29rLCBhbmQgbGVzcyBtZWFucyBtb3JlIGFjY3VyYXRlIHJlcHJlc2VudGF0aW9uLlxuXHRcdHNtb290aEZhY3RvcjogMS4wLFxuXG5cdFx0Ly8gQG9wdGlvbiBub0NsaXA6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIERpc2FibGUgcG9seWxpbmUgY2xpcHBpbmcuXG5cdFx0bm9DbGlwOiBmYWxzZVxuXHR9LFxuXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmdzLCBvcHRpb25zKSB7XG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xuXHRcdHRoaXMuX3NldExhdExuZ3MobGF0bG5ncyk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRMYXRMbmdzKCk6IExhdExuZ1tdXG5cdC8vIFJldHVybnMgYW4gYXJyYXkgb2YgdGhlIHBvaW50cyBpbiB0aGUgcGF0aCwgb3IgbmVzdGVkIGFycmF5cyBvZiBwb2ludHMgaW4gY2FzZSBvZiBtdWx0aS1wb2x5bGluZS5cblx0Z2V0TGF0TG5nczogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sYXRsbmdzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0TGF0TG5ncyhsYXRsbmdzOiBMYXRMbmdbXSk6IHRoaXNcblx0Ly8gUmVwbGFjZXMgYWxsIHRoZSBwb2ludHMgaW4gdGhlIHBvbHlsaW5lIHdpdGggdGhlIGdpdmVuIGFycmF5IG9mIGdlb2dyYXBoaWNhbCBwb2ludHMuXG5cdHNldExhdExuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzKSB7XG5cdFx0dGhpcy5fc2V0TGF0TG5ncyhsYXRsbmdzKTtcblx0XHRyZXR1cm4gdGhpcy5yZWRyYXcoKTtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGlzRW1wdHkoKTogQm9vbGVhblxuXHQvLyBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgUG9seWxpbmUgaGFzIG5vIExhdExuZ3MuXG5cdGlzRW1wdHk6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gIXRoaXMuX2xhdGxuZ3MubGVuZ3RoO1xuXHR9LFxuXG5cdGNsb3Nlc3RMYXllclBvaW50OiBmdW5jdGlvbiAocCkge1xuXHRcdHZhciBtaW5EaXN0YW5jZSA9IEluZmluaXR5LFxuXHRcdCAgICBtaW5Qb2ludCA9IG51bGwsXG5cdFx0ICAgIGNsb3Nlc3QgPSBMLkxpbmVVdGlsLl9zcUNsb3Nlc3RQb2ludE9uU2VnbWVudCxcblx0XHQgICAgcDEsIHAyO1xuXG5cdFx0Zm9yICh2YXIgaiA9IDAsIGpMZW4gPSB0aGlzLl9wYXJ0cy5sZW5ndGg7IGogPCBqTGVuOyBqKyspIHtcblx0XHRcdHZhciBwb2ludHMgPSB0aGlzLl9wYXJ0c1tqXTtcblxuXHRcdFx0Zm9yICh2YXIgaSA9IDEsIGxlbiA9IHBvaW50cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0XHRwMSA9IHBvaW50c1tpIC0gMV07XG5cdFx0XHRcdHAyID0gcG9pbnRzW2ldO1xuXG5cdFx0XHRcdHZhciBzcURpc3QgPSBjbG9zZXN0KHAsIHAxLCBwMiwgdHJ1ZSk7XG5cblx0XHRcdFx0aWYgKHNxRGlzdCA8IG1pbkRpc3RhbmNlKSB7XG5cdFx0XHRcdFx0bWluRGlzdGFuY2UgPSBzcURpc3Q7XG5cdFx0XHRcdFx0bWluUG9pbnQgPSBjbG9zZXN0KHAsIHAxLCBwMik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0aWYgKG1pblBvaW50KSB7XG5cdFx0XHRtaW5Qb2ludC5kaXN0YW5jZSA9IE1hdGguc3FydChtaW5EaXN0YW5jZSk7XG5cdFx0fVxuXHRcdHJldHVybiBtaW5Qb2ludDtcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldENlbnRlcigpOiBMYXRMbmdcblx0Ly8gUmV0dXJucyB0aGUgY2VudGVyIChbY2VudHJvaWRdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ2VudHJvaWQpKSBvZiB0aGUgcG9seWxpbmUuXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHRocm93cyBlcnJvciB3aGVuIG5vdCB5ZXQgYWRkZWQgdG8gbWFwIGFzIHRoaXMgY2VudGVyIGNhbGN1bGF0aW9uIHJlcXVpcmVzIHByb2plY3RlZCBjb29yZGluYXRlc1xuXHRcdGlmICghdGhpcy5fbWFwKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ011c3QgYWRkIGxheWVyIHRvIG1hcCBiZWZvcmUgdXNpbmcgZ2V0Q2VudGVyKCknKTtcblx0XHR9XG5cblx0XHR2YXIgaSwgaGFsZkRpc3QsIHNlZ0Rpc3QsIGRpc3QsIHAxLCBwMiwgcmF0aW8sXG5cdFx0ICAgIHBvaW50cyA9IHRoaXMuX3JpbmdzWzBdLFxuXHRcdCAgICBsZW4gPSBwb2ludHMubGVuZ3RoO1xuXG5cdFx0aWYgKCFsZW4pIHsgcmV0dXJuIG51bGw7IH1cblxuXHRcdC8vIHBvbHlsaW5lIGNlbnRyb2lkIGFsZ29yaXRobTsgb25seSB1c2VzIHRoZSBmaXJzdCByaW5nIGlmIHRoZXJlIGFyZSBtdWx0aXBsZVxuXG5cdFx0Zm9yIChpID0gMCwgaGFsZkRpc3QgPSAwOyBpIDwgbGVuIC0gMTsgaSsrKSB7XG5cdFx0XHRoYWxmRGlzdCArPSBwb2ludHNbaV0uZGlzdGFuY2VUbyhwb2ludHNbaSArIDFdKSAvIDI7XG5cdFx0fVxuXG5cdFx0Ly8gVGhlIGxpbmUgaXMgc28gc21hbGwgaW4gdGhlIGN1cnJlbnQgdmlldyB0aGF0IGFsbCBwb2ludHMgYXJlIG9uIHRoZSBzYW1lIHBpeGVsLlxuXHRcdGlmIChoYWxmRGlzdCA9PT0gMCkge1xuXHRcdFx0cmV0dXJuIHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcocG9pbnRzWzBdKTtcblx0XHR9XG5cblx0XHRmb3IgKGkgPSAwLCBkaXN0ID0gMDsgaSA8IGxlbiAtIDE7IGkrKykge1xuXHRcdFx0cDEgPSBwb2ludHNbaV07XG5cdFx0XHRwMiA9IHBvaW50c1tpICsgMV07XG5cdFx0XHRzZWdEaXN0ID0gcDEuZGlzdGFuY2VUbyhwMik7XG5cdFx0XHRkaXN0ICs9IHNlZ0Rpc3Q7XG5cblx0XHRcdGlmIChkaXN0ID4gaGFsZkRpc3QpIHtcblx0XHRcdFx0cmF0aW8gPSAoZGlzdCAtIGhhbGZEaXN0KSAvIHNlZ0Rpc3Q7XG5cdFx0XHRcdHJldHVybiB0aGlzLl9tYXAubGF5ZXJQb2ludFRvTGF0TG5nKFtcblx0XHRcdFx0XHRwMi54IC0gcmF0aW8gKiAocDIueCAtIHAxLngpLFxuXHRcdFx0XHRcdHAyLnkgLSByYXRpbyAqIChwMi55IC0gcDEueSlcblx0XHRcdFx0XSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdC8vIEBtZXRob2QgZ2V0Qm91bmRzKCk6IExhdExuZ0JvdW5kc1xuXHQvLyBSZXR1cm5zIHRoZSBgTGF0TG5nQm91bmRzYCBvZiB0aGUgcGF0aC5cblx0Z2V0Qm91bmRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2JvdW5kcztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGFkZExhdExuZyhsYXRsbmc6IExhdExuZywgbGF0bG5ncz8gTGF0TG5nW10pOiB0aGlzXG5cdC8vIEFkZHMgYSBnaXZlbiBwb2ludCB0byB0aGUgcG9seWxpbmUuIEJ5IGRlZmF1bHQsIGFkZHMgdG8gdGhlIGZpcnN0IHJpbmcgb2Zcblx0Ly8gdGhlIHBvbHlsaW5lIGluIGNhc2Ugb2YgYSBtdWx0aS1wb2x5bGluZSwgYnV0IGNhbiBiZSBvdmVycmlkZGVuIGJ5IHBhc3Npbmdcblx0Ly8gYSBzcGVjaWZpYyByaW5nIGFzIGEgTGF0TG5nIGFycmF5ICh0aGF0IHlvdSBjYW4gZWFybGllciBhY2Nlc3Mgd2l0aCBbYGdldExhdExuZ3NgXSgjcG9seWxpbmUtZ2V0bGF0bG5ncykpLlxuXHRhZGRMYXRMbmc6IGZ1bmN0aW9uIChsYXRsbmcsIGxhdGxuZ3MpIHtcblx0XHRsYXRsbmdzID0gbGF0bG5ncyB8fCB0aGlzLl9kZWZhdWx0U2hhcGUoKTtcblx0XHRsYXRsbmcgPSBMLmxhdExuZyhsYXRsbmcpO1xuXHRcdGxhdGxuZ3MucHVzaChsYXRsbmcpO1xuXHRcdHRoaXMuX2JvdW5kcy5leHRlbmQobGF0bG5nKTtcblx0XHRyZXR1cm4gdGhpcy5yZWRyYXcoKTtcblx0fSxcblxuXHRfc2V0TGF0TG5nczogZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0XHR0aGlzLl9ib3VuZHMgPSBuZXcgTC5MYXRMbmdCb3VuZHMoKTtcblx0XHR0aGlzLl9sYXRsbmdzID0gdGhpcy5fY29udmVydExhdExuZ3MobGF0bG5ncyk7XG5cdH0sXG5cblx0X2RlZmF1bHRTaGFwZTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiBMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3MpID8gdGhpcy5fbGF0bG5ncyA6IHRoaXMuX2xhdGxuZ3NbMF07XG5cdH0sXG5cblx0Ly8gcmVjdXJzaXZlbHkgY29udmVydCBsYXRsbmdzIGlucHV0IGludG8gYWN0dWFsIExhdExuZyBpbnN0YW5jZXM7IGNhbGN1bGF0ZSBib3VuZHMgYWxvbmcgdGhlIHdheVxuXHRfY29udmVydExhdExuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzKSB7XG5cdFx0dmFyIHJlc3VsdCA9IFtdLFxuXHRcdCAgICBmbGF0ID0gTC5Qb2x5bGluZS5fZmxhdChsYXRsbmdzKTtcblxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBsYXRsbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRpZiAoZmxhdCkge1xuXHRcdFx0XHRyZXN1bHRbaV0gPSBMLmxhdExuZyhsYXRsbmdzW2ldKTtcblx0XHRcdFx0dGhpcy5fYm91bmRzLmV4dGVuZChyZXN1bHRbaV0pO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0cmVzdWx0W2ldID0gdGhpcy5fY29udmVydExhdExuZ3MobGF0bG5nc1tpXSk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHJlc3VsdDtcblx0fSxcblxuXHRfcHJvamVjdDogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBweEJvdW5kcyA9IG5ldyBMLkJvdW5kcygpO1xuXHRcdHRoaXMuX3JpbmdzID0gW107XG5cdFx0dGhpcy5fcHJvamVjdExhdGxuZ3ModGhpcy5fbGF0bG5ncywgdGhpcy5fcmluZ3MsIHB4Qm91bmRzKTtcblxuXHRcdHZhciB3ID0gdGhpcy5fY2xpY2tUb2xlcmFuY2UoKSxcblx0XHQgICAgcCA9IG5ldyBMLlBvaW50KHcsIHcpO1xuXG5cdFx0aWYgKHRoaXMuX2JvdW5kcy5pc1ZhbGlkKCkgJiYgcHhCb3VuZHMuaXNWYWxpZCgpKSB7XG5cdFx0XHRweEJvdW5kcy5taW4uX3N1YnRyYWN0KHApO1xuXHRcdFx0cHhCb3VuZHMubWF4Ll9hZGQocCk7XG5cdFx0XHR0aGlzLl9weEJvdW5kcyA9IHB4Qm91bmRzO1xuXHRcdH1cblx0fSxcblxuXHQvLyByZWN1cnNpdmVseSB0dXJucyBsYXRsbmdzIGludG8gYSBzZXQgb2YgcmluZ3Mgd2l0aCBwcm9qZWN0ZWQgY29vcmRpbmF0ZXNcblx0X3Byb2plY3RMYXRsbmdzOiBmdW5jdGlvbiAobGF0bG5ncywgcmVzdWx0LCBwcm9qZWN0ZWRCb3VuZHMpIHtcblx0XHR2YXIgZmxhdCA9IGxhdGxuZ3NbMF0gaW5zdGFuY2VvZiBMLkxhdExuZyxcblx0XHQgICAgbGVuID0gbGF0bG5ncy5sZW5ndGgsXG5cdFx0ICAgIGksIHJpbmc7XG5cblx0XHRpZiAoZmxhdCkge1xuXHRcdFx0cmluZyA9IFtdO1xuXHRcdFx0Zm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRcdHJpbmdbaV0gPSB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KGxhdGxuZ3NbaV0pO1xuXHRcdFx0XHRwcm9qZWN0ZWRCb3VuZHMuZXh0ZW5kKHJpbmdbaV0pO1xuXHRcdFx0fVxuXHRcdFx0cmVzdWx0LnB1c2gocmluZyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGZvciAoaSA9IDA7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0XHR0aGlzLl9wcm9qZWN0TGF0bG5ncyhsYXRsbmdzW2ldLCByZXN1bHQsIHByb2plY3RlZEJvdW5kcyk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdC8vIGNsaXAgcG9seWxpbmUgYnkgcmVuZGVyZXIgYm91bmRzIHNvIHRoYXQgd2UgaGF2ZSBsZXNzIHRvIHJlbmRlciBmb3IgcGVyZm9ybWFuY2Vcblx0X2NsaXBQb2ludHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgYm91bmRzID0gdGhpcy5fcmVuZGVyZXIuX2JvdW5kcztcblxuXHRcdHRoaXMuX3BhcnRzID0gW107XG5cdFx0aWYgKCF0aGlzLl9weEJvdW5kcyB8fCAhdGhpcy5fcHhCb3VuZHMuaW50ZXJzZWN0cyhib3VuZHMpKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5ub0NsaXApIHtcblx0XHRcdHRoaXMuX3BhcnRzID0gdGhpcy5fcmluZ3M7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dmFyIHBhcnRzID0gdGhpcy5fcGFydHMsXG5cdFx0ICAgIGksIGosIGssIGxlbiwgbGVuMiwgc2VnbWVudCwgcG9pbnRzO1xuXG5cdFx0Zm9yIChpID0gMCwgayA9IDAsIGxlbiA9IHRoaXMuX3JpbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRwb2ludHMgPSB0aGlzLl9yaW5nc1tpXTtcblxuXHRcdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBvaW50cy5sZW5ndGg7IGogPCBsZW4yIC0gMTsgaisrKSB7XG5cdFx0XHRcdHNlZ21lbnQgPSBMLkxpbmVVdGlsLmNsaXBTZWdtZW50KHBvaW50c1tqXSwgcG9pbnRzW2ogKyAxXSwgYm91bmRzLCBqLCB0cnVlKTtcblxuXHRcdFx0XHRpZiAoIXNlZ21lbnQpIHsgY29udGludWU7IH1cblxuXHRcdFx0XHRwYXJ0c1trXSA9IHBhcnRzW2tdIHx8IFtdO1xuXHRcdFx0XHRwYXJ0c1trXS5wdXNoKHNlZ21lbnRbMF0pO1xuXG5cdFx0XHRcdC8vIGlmIHNlZ21lbnQgZ29lcyBvdXQgb2Ygc2NyZWVuLCBvciBpdCdzIHRoZSBsYXN0IG9uZSwgaXQncyB0aGUgZW5kIG9mIHRoZSBsaW5lIHBhcnRcblx0XHRcdFx0aWYgKChzZWdtZW50WzFdICE9PSBwb2ludHNbaiArIDFdKSB8fCAoaiA9PT0gbGVuMiAtIDIpKSB7XG5cdFx0XHRcdFx0cGFydHNba10ucHVzaChzZWdtZW50WzFdKTtcblx0XHRcdFx0XHRrKys7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0Ly8gc2ltcGxpZnkgZWFjaCBjbGlwcGVkIHBhcnQgb2YgdGhlIHBvbHlsaW5lIGZvciBwZXJmb3JtYW5jZVxuXHRfc2ltcGxpZnlQb2ludHM6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgcGFydHMgPSB0aGlzLl9wYXJ0cyxcblx0XHQgICAgdG9sZXJhbmNlID0gdGhpcy5vcHRpb25zLnNtb290aEZhY3RvcjtcblxuXHRcdGZvciAodmFyIGkgPSAwLCBsZW4gPSBwYXJ0cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0cGFydHNbaV0gPSBMLkxpbmVVdGlsLnNpbXBsaWZ5KHBhcnRzW2ldLCB0b2xlcmFuY2UpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXApIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9jbGlwUG9pbnRzKCk7XG5cdFx0dGhpcy5fc2ltcGxpZnlQb2ludHMoKTtcblx0XHR0aGlzLl91cGRhdGVQYXRoKCk7XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlUG9seSh0aGlzKTtcblx0fVxufSk7XG5cbi8vIEBmYWN0b3J5IEwucG9seWxpbmUobGF0bG5nczogTGF0TG5nW10sIG9wdGlvbnM/OiBQb2x5bGluZSBvcHRpb25zKVxuLy8gSW5zdGFudGlhdGVzIGEgcG9seWxpbmUgb2JqZWN0IGdpdmVuIGFuIGFycmF5IG9mIGdlb2dyYXBoaWNhbCBwb2ludHMgYW5kXG4vLyBvcHRpb25hbGx5IGFuIG9wdGlvbnMgb2JqZWN0LiBZb3UgY2FuIGNyZWF0ZSBhIGBQb2x5bGluZWAgb2JqZWN0IHdpdGhcbi8vIG11bHRpcGxlIHNlcGFyYXRlIGxpbmVzIChgTXVsdGlQb2x5bGluZWApIGJ5IHBhc3NpbmcgYW4gYXJyYXkgb2YgYXJyYXlzXG4vLyBvZiBnZW9ncmFwaGljIHBvaW50cy5cbkwucG9seWxpbmUgPSBmdW5jdGlvbiAobGF0bG5ncywgb3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuUG9seWxpbmUobGF0bG5ncywgb3B0aW9ucyk7XG59O1xuXG5MLlBvbHlsaW5lLl9mbGF0ID0gZnVuY3Rpb24gKGxhdGxuZ3MpIHtcblx0Ly8gdHJ1ZSBpZiBpdCdzIGEgZmxhdCBhcnJheSBvZiBsYXRsbmdzOyBmYWxzZSBpZiBuZXN0ZWRcblx0cmV0dXJuICFMLlV0aWwuaXNBcnJheShsYXRsbmdzWzBdKSB8fCAodHlwZW9mIGxhdGxuZ3NbMF1bMF0gIT09ICdvYmplY3QnICYmIHR5cGVvZiBsYXRsbmdzWzBdWzBdICE9PSAndW5kZWZpbmVkJyk7XG59O1xuXG5cblxuLypcclxuICogQG5hbWVzcGFjZSBQb2x5VXRpbFxyXG4gKiBWYXJpb3VzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBwb2x5Z29uIGdlb21ldHJpZXMuXHJcbiAqL1xyXG5cclxuTC5Qb2x5VXRpbCA9IHt9O1xyXG5cclxuLyogQGZ1bmN0aW9uIGNsaXBQb2x5Z29uKHBvaW50czogUG9pbnRbXSwgYm91bmRzOiBCb3VuZHMsIHJvdW5kPzogQm9vbGVhbik6IFBvaW50W11cclxuICogQ2xpcHMgdGhlIHBvbHlnb24gZ2VvbWV0cnkgZGVmaW5lZCBieSB0aGUgZ2l2ZW4gYHBvaW50c2AgYnkgdGhlIGdpdmVuIGJvdW5kcyAodXNpbmcgdGhlIFtTdXRoZXJsYW5kLUhvZGdlbWFuIGFsZ29yaXRobV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU3V0aGVybGFuZCVFMiU4MCU5M0hvZGdtYW5fYWxnb3JpdGhtKSkuXHJcbiAqIFVzZWQgYnkgTGVhZmxldCB0byBvbmx5IHNob3cgcG9seWdvbiBwb2ludHMgdGhhdCBhcmUgb24gdGhlIHNjcmVlbiBvciBuZWFyLCBpbmNyZWFzaW5nXHJcbiAqIHBlcmZvcm1hbmNlLiBOb3RlIHRoYXQgcG9seWdvbiBwb2ludHMgbmVlZHMgZGlmZmVyZW50IGFsZ29yaXRobSBmb3IgY2xpcHBpbmdcclxuICogdGhhbiBwb2x5bGluZSwgc28gdGhlcmUncyBhIHNlcGVyYXRlIG1ldGhvZCBmb3IgaXQuXHJcbiAqL1xyXG5MLlBvbHlVdGlsLmNsaXBQb2x5Z29uID0gZnVuY3Rpb24gKHBvaW50cywgYm91bmRzLCByb3VuZCkge1xyXG5cdHZhciBjbGlwcGVkUG9pbnRzLFxyXG5cdCAgICBlZGdlcyA9IFsxLCA0LCAyLCA4XSxcclxuXHQgICAgaSwgaiwgayxcclxuXHQgICAgYSwgYixcclxuXHQgICAgbGVuLCBlZGdlLCBwLFxyXG5cdCAgICBsdSA9IEwuTGluZVV0aWw7XHJcblxyXG5cdGZvciAoaSA9IDAsIGxlbiA9IHBvaW50cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0cG9pbnRzW2ldLl9jb2RlID0gbHUuX2dldEJpdENvZGUocG9pbnRzW2ldLCBib3VuZHMpO1xyXG5cdH1cclxuXHJcblx0Ly8gZm9yIGVhY2ggZWRnZSAobGVmdCwgYm90dG9tLCByaWdodCwgdG9wKVxyXG5cdGZvciAoayA9IDA7IGsgPCA0OyBrKyspIHtcclxuXHRcdGVkZ2UgPSBlZGdlc1trXTtcclxuXHRcdGNsaXBwZWRQb2ludHMgPSBbXTtcclxuXHJcblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBwb2ludHMubGVuZ3RoLCBqID0gbGVuIC0gMTsgaSA8IGxlbjsgaiA9IGkrKykge1xyXG5cdFx0XHRhID0gcG9pbnRzW2ldO1xyXG5cdFx0XHRiID0gcG9pbnRzW2pdO1xyXG5cclxuXHRcdFx0Ly8gaWYgYSBpcyBpbnNpZGUgdGhlIGNsaXAgd2luZG93XHJcblx0XHRcdGlmICghKGEuX2NvZGUgJiBlZGdlKSkge1xyXG5cdFx0XHRcdC8vIGlmIGIgaXMgb3V0c2lkZSB0aGUgY2xpcCB3aW5kb3cgKGEtPmIgZ29lcyBvdXQgb2Ygc2NyZWVuKVxyXG5cdFx0XHRcdGlmIChiLl9jb2RlICYgZWRnZSkge1xyXG5cdFx0XHRcdFx0cCA9IGx1Ll9nZXRFZGdlSW50ZXJzZWN0aW9uKGIsIGEsIGVkZ2UsIGJvdW5kcywgcm91bmQpO1xyXG5cdFx0XHRcdFx0cC5fY29kZSA9IGx1Ll9nZXRCaXRDb2RlKHAsIGJvdW5kcyk7XHJcblx0XHRcdFx0XHRjbGlwcGVkUG9pbnRzLnB1c2gocCk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdGNsaXBwZWRQb2ludHMucHVzaChhKTtcclxuXHJcblx0XHRcdC8vIGVsc2UgaWYgYiBpcyBpbnNpZGUgdGhlIGNsaXAgd2luZG93IChhLT5iIGVudGVycyB0aGUgc2NyZWVuKVxyXG5cdFx0XHR9IGVsc2UgaWYgKCEoYi5fY29kZSAmIGVkZ2UpKSB7XHJcblx0XHRcdFx0cCA9IGx1Ll9nZXRFZGdlSW50ZXJzZWN0aW9uKGIsIGEsIGVkZ2UsIGJvdW5kcywgcm91bmQpO1xyXG5cdFx0XHRcdHAuX2NvZGUgPSBsdS5fZ2V0Qml0Q29kZShwLCBib3VuZHMpO1xyXG5cdFx0XHRcdGNsaXBwZWRQb2ludHMucHVzaChwKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cdFx0cG9pbnRzID0gY2xpcHBlZFBvaW50cztcclxuXHR9XHJcblxyXG5cdHJldHVybiBwb2ludHM7XHJcbn07XHJcblxuXG5cbi8qXG4gKiBAY2xhc3MgUG9seWdvblxuICogQGFrYSBMLlBvbHlnb25cbiAqIEBpbmhlcml0cyBQb2x5bGluZVxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgcG9seWdvbiBvdmVybGF5cyBvbiBhIG1hcC4gRXh0ZW5kcyBgUG9seWxpbmVgLlxuICpcbiAqIE5vdGUgdGhhdCBwb2ludHMgeW91IHBhc3Mgd2hlbiBjcmVhdGluZyBhIHBvbHlnb24gc2hvdWxkbid0IGhhdmUgYW4gYWRkaXRpb25hbCBsYXN0IHBvaW50IGVxdWFsIHRvIHRoZSBmaXJzdCBvbmUg4oCUIGl0J3MgYmV0dGVyIHRvIGZpbHRlciBvdXQgc3VjaCBwb2ludHMuXG4gKlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogYGBganNcbiAqIC8vIGNyZWF0ZSBhIHJlZCBwb2x5Z29uIGZyb20gYW4gYXJyYXkgb2YgTGF0TG5nIHBvaW50c1xuICogdmFyIGxhdGxuZ3MgPSBbWzM3LCAtMTA5LjA1XSxbNDEsIC0xMDkuMDNdLFs0MSwgLTEwMi4wNV0sWzM3LCAtMTAyLjA0XV07XG4gKlxuICogdmFyIHBvbHlnb24gPSBMLnBvbHlnb24obGF0bG5ncywge2NvbG9yOiAncmVkJ30pLmFkZFRvKG1hcCk7XG4gKlxuICogLy8gem9vbSB0aGUgbWFwIHRvIHRoZSBwb2x5Z29uXG4gKiBtYXAuZml0Qm91bmRzKHBvbHlnb24uZ2V0Qm91bmRzKCkpO1xuICogYGBgXG4gKlxuICogWW91IGNhbiBhbHNvIHBhc3MgYW4gYXJyYXkgb2YgYXJyYXlzIG9mIGxhdGxuZ3MsIHdpdGggdGhlIGZpcnN0IGFycmF5IHJlcHJlc2VudGluZyB0aGUgb3V0ZXIgc2hhcGUgYW5kIHRoZSBvdGhlciBhcnJheXMgcmVwcmVzZW50aW5nIGhvbGVzIGluIHRoZSBvdXRlciBzaGFwZTpcbiAqXG4gKiBgYGBqc1xuICogdmFyIGxhdGxuZ3MgPSBbXG4gKiAgIFtbMzcsIC0xMDkuMDVdLFs0MSwgLTEwOS4wM10sWzQxLCAtMTAyLjA1XSxbMzcsIC0xMDIuMDRdXSwgLy8gb3V0ZXIgcmluZ1xuICogICBbWzM3LjI5LCAtMTA4LjU4XSxbNDAuNzEsIC0xMDguNThdLFs0MC43MSwgLTEwMi41MF0sWzM3LjI5LCAtMTAyLjUwXV0gLy8gaG9sZVxuICogXTtcbiAqIGBgYFxuICpcbiAqIEFkZGl0aW9uYWxseSwgeW91IGNhbiBwYXNzIGEgbXVsdGktZGltZW5zaW9uYWwgYXJyYXkgdG8gcmVwcmVzZW50IGEgTXVsdGlQb2x5Z29uIHNoYXBlLlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbGF0bG5ncyA9IFtcbiAqICAgWyAvLyBmaXJzdCBwb2x5Z29uXG4gKiAgICAgW1szNywgLTEwOS4wNV0sWzQxLCAtMTA5LjAzXSxbNDEsIC0xMDIuMDVdLFszNywgLTEwMi4wNF1dLCAvLyBvdXRlciByaW5nXG4gKiAgICAgW1szNy4yOSwgLTEwOC41OF0sWzQwLjcxLCAtMTA4LjU4XSxbNDAuNzEsIC0xMDIuNTBdLFszNy4yOSwgLTEwMi41MF1dIC8vIGhvbGVcbiAqICAgXSxcbiAqICAgWyAvLyBzZWNvbmQgcG9seWdvblxuICogICAgIFtbNDEsIC0xMTEuMDNdLFs0NSwgLTExMS4wNF0sWzQ1LCAtMTA0LjA1XSxbNDEsIC0xMDQuMDVdXVxuICogICBdXG4gKiBdO1xuICogYGBgXG4gKi9cblxuTC5Qb2x5Z29uID0gTC5Qb2x5bGluZS5leHRlbmQoe1xuXG5cdG9wdGlvbnM6IHtcblx0XHRmaWxsOiB0cnVlXG5cdH0sXG5cblx0aXNFbXB0eTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiAhdGhpcy5fbGF0bG5ncy5sZW5ndGggfHwgIXRoaXMuX2xhdGxuZ3NbMF0ubGVuZ3RoO1xuXHR9LFxuXG5cdGdldENlbnRlcjogZnVuY3Rpb24gKCkge1xuXHRcdC8vIHRocm93cyBlcnJvciB3aGVuIG5vdCB5ZXQgYWRkZWQgdG8gbWFwIGFzIHRoaXMgY2VudGVyIGNhbGN1bGF0aW9uIHJlcXVpcmVzIHByb2plY3RlZCBjb29yZGluYXRlc1xuXHRcdGlmICghdGhpcy5fbWFwKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoJ011c3QgYWRkIGxheWVyIHRvIG1hcCBiZWZvcmUgdXNpbmcgZ2V0Q2VudGVyKCknKTtcblx0XHR9XG5cblx0XHR2YXIgaSwgaiwgcDEsIHAyLCBmLCBhcmVhLCB4LCB5LCBjZW50ZXIsXG5cdFx0ICAgIHBvaW50cyA9IHRoaXMuX3JpbmdzWzBdLFxuXHRcdCAgICBsZW4gPSBwb2ludHMubGVuZ3RoO1xuXG5cdFx0aWYgKCFsZW4pIHsgcmV0dXJuIG51bGw7IH1cblxuXHRcdC8vIHBvbHlnb24gY2VudHJvaWQgYWxnb3JpdGhtOyBvbmx5IHVzZXMgdGhlIGZpcnN0IHJpbmcgaWYgdGhlcmUgYXJlIG11bHRpcGxlXG5cblx0XHRhcmVhID0geCA9IHkgPSAwO1xuXG5cdFx0Zm9yIChpID0gMCwgaiA9IGxlbiAtIDE7IGkgPCBsZW47IGogPSBpKyspIHtcblx0XHRcdHAxID0gcG9pbnRzW2ldO1xuXHRcdFx0cDIgPSBwb2ludHNbal07XG5cblx0XHRcdGYgPSBwMS55ICogcDIueCAtIHAyLnkgKiBwMS54O1xuXHRcdFx0eCArPSAocDEueCArIHAyLngpICogZjtcblx0XHRcdHkgKz0gKHAxLnkgKyBwMi55KSAqIGY7XG5cdFx0XHRhcmVhICs9IGYgKiAzO1xuXHRcdH1cblxuXHRcdGlmIChhcmVhID09PSAwKSB7XG5cdFx0XHQvLyBQb2x5Z29uIGlzIHNvIHNtYWxsIHRoYXQgYWxsIHBvaW50cyBhcmUgb24gc2FtZSBwaXhlbC5cblx0XHRcdGNlbnRlciA9IHBvaW50c1swXTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Y2VudGVyID0gW3ggLyBhcmVhLCB5IC8gYXJlYV07XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzLl9tYXAubGF5ZXJQb2ludFRvTGF0TG5nKGNlbnRlcik7XG5cdH0sXG5cblx0X2NvbnZlcnRMYXRMbmdzOiBmdW5jdGlvbiAobGF0bG5ncykge1xuXHRcdHZhciByZXN1bHQgPSBMLlBvbHlsaW5lLnByb3RvdHlwZS5fY29udmVydExhdExuZ3MuY2FsbCh0aGlzLCBsYXRsbmdzKSxcblx0XHQgICAgbGVuID0gcmVzdWx0Lmxlbmd0aDtcblxuXHRcdC8vIHJlbW92ZSBsYXN0IHBvaW50IGlmIGl0IGVxdWFscyBmaXJzdCBvbmVcblx0XHRpZiAobGVuID49IDIgJiYgcmVzdWx0WzBdIGluc3RhbmNlb2YgTC5MYXRMbmcgJiYgcmVzdWx0WzBdLmVxdWFscyhyZXN1bHRbbGVuIC0gMV0pKSB7XG5cdFx0XHRyZXN1bHQucG9wKCk7XG5cdFx0fVxuXHRcdHJldHVybiByZXN1bHQ7XG5cdH0sXG5cblx0X3NldExhdExuZ3M6IGZ1bmN0aW9uIChsYXRsbmdzKSB7XG5cdFx0TC5Qb2x5bGluZS5wcm90b3R5cGUuX3NldExhdExuZ3MuY2FsbCh0aGlzLCBsYXRsbmdzKTtcblx0XHRpZiAoTC5Qb2x5bGluZS5fZmxhdCh0aGlzLl9sYXRsbmdzKSkge1xuXHRcdFx0dGhpcy5fbGF0bG5ncyA9IFt0aGlzLl9sYXRsbmdzXTtcblx0XHR9XG5cdH0sXG5cblx0X2RlZmF1bHRTaGFwZTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiBMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3NbMF0pID8gdGhpcy5fbGF0bG5nc1swXSA6IHRoaXMuX2xhdGxuZ3NbMF1bMF07XG5cdH0sXG5cblx0X2NsaXBQb2ludHM6IGZ1bmN0aW9uICgpIHtcblx0XHQvLyBwb2x5Z29ucyBuZWVkIGEgZGlmZmVyZW50IGNsaXBwaW5nIGFsZ29yaXRobSBzbyB3ZSByZWRlZmluZSB0aGF0XG5cblx0XHR2YXIgYm91bmRzID0gdGhpcy5fcmVuZGVyZXIuX2JvdW5kcyxcblx0XHQgICAgdyA9IHRoaXMub3B0aW9ucy53ZWlnaHQsXG5cdFx0ICAgIHAgPSBuZXcgTC5Qb2ludCh3LCB3KTtcblxuXHRcdC8vIGluY3JlYXNlIGNsaXAgcGFkZGluZyBieSBzdHJva2Ugd2lkdGggdG8gYXZvaWQgc3Ryb2tlIG9uIGNsaXAgZWRnZXNcblx0XHRib3VuZHMgPSBuZXcgTC5Cb3VuZHMoYm91bmRzLm1pbi5zdWJ0cmFjdChwKSwgYm91bmRzLm1heC5hZGQocCkpO1xuXG5cdFx0dGhpcy5fcGFydHMgPSBbXTtcblx0XHRpZiAoIXRoaXMuX3B4Qm91bmRzIHx8ICF0aGlzLl9weEJvdW5kcy5pbnRlcnNlY3RzKGJvdW5kcykpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5vcHRpb25zLm5vQ2xpcCkge1xuXHRcdFx0dGhpcy5fcGFydHMgPSB0aGlzLl9yaW5ncztcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gdGhpcy5fcmluZ3MubGVuZ3RoLCBjbGlwcGVkOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGNsaXBwZWQgPSBMLlBvbHlVdGlsLmNsaXBQb2x5Z29uKHRoaXMuX3JpbmdzW2ldLCBib3VuZHMsIHRydWUpO1xuXHRcdFx0aWYgKGNsaXBwZWQubGVuZ3RoKSB7XG5cdFx0XHRcdHRoaXMuX3BhcnRzLnB1c2goY2xpcHBlZCk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVQYXRoOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcmVuZGVyZXIuX3VwZGF0ZVBvbHkodGhpcywgdHJ1ZSk7XG5cdH1cbn0pO1xuXG5cbi8vIEBmYWN0b3J5IEwucG9seWdvbihsYXRsbmdzOiBMYXRMbmdbXSwgb3B0aW9ucz86IFBvbHlsaW5lIG9wdGlvbnMpXG5MLnBvbHlnb24gPSBmdW5jdGlvbiAobGF0bG5ncywgb3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuUG9seWdvbihsYXRsbmdzLCBvcHRpb25zKTtcbn07XG5cblxuXG4vKlxuICogTC5SZWN0YW5nbGUgZXh0ZW5kcyBQb2x5Z29uIGFuZCBjcmVhdGVzIGEgcmVjdGFuZ2xlIHdoZW4gcGFzc2VkIGEgTGF0TG5nQm91bmRzIG9iamVjdC5cbiAqL1xuXG4vKlxuICogQGNsYXNzIFJlY3RhbmdsZVxuICogQGFrYSBMLlJldGFuZ2xlXG4gKiBAaW5oZXJpdHMgUG9seWdvblxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgcmVjdGFuZ2xlIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBQb2x5Z29uYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiAvLyBkZWZpbmUgcmVjdGFuZ2xlIGdlb2dyYXBoaWNhbCBib3VuZHNcbiAqIHZhciBib3VuZHMgPSBbWzU0LjU1OTMyMiwgLTUuNzY3ODIyXSwgWzU2LjEyMTA2MDQsIC0zLjAyMTI0MF1dO1xuICpcbiAqIC8vIGNyZWF0ZSBhbiBvcmFuZ2UgcmVjdGFuZ2xlXG4gKiBMLnJlY3RhbmdsZShib3VuZHMsIHtjb2xvcjogXCIjZmY3ODAwXCIsIHdlaWdodDogMX0pLmFkZFRvKG1hcCk7XG4gKlxuICogLy8gem9vbSB0aGUgbWFwIHRvIHRoZSByZWN0YW5nbGUgYm91bmRzXG4gKiBtYXAuZml0Qm91bmRzKGJvdW5kcyk7XG4gKiBgYGBcbiAqXG4gKi9cblxuXG5MLlJlY3RhbmdsZSA9IEwuUG9seWdvbi5leHRlbmQoe1xuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzLCBvcHRpb25zKSB7XG5cdFx0TC5Qb2x5Z29uLnByb3RvdHlwZS5pbml0aWFsaXplLmNhbGwodGhpcywgdGhpcy5fYm91bmRzVG9MYXRMbmdzKGxhdExuZ0JvdW5kcyksIG9wdGlvbnMpO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0Qm91bmRzKGxhdExuZ0JvdW5kczogTGF0TG5nQm91bmRzKTogdGhpc1xuXHQvLyBSZWRyYXdzIHRoZSByZWN0YW5nbGUgd2l0aCB0aGUgcGFzc2VkIGJvdW5kcy5cblx0c2V0Qm91bmRzOiBmdW5jdGlvbiAobGF0TG5nQm91bmRzKSB7XG5cdFx0cmV0dXJuIHRoaXMuc2V0TGF0TG5ncyh0aGlzLl9ib3VuZHNUb0xhdExuZ3MobGF0TG5nQm91bmRzKSk7XG5cdH0sXG5cblx0X2JvdW5kc1RvTGF0TG5nczogZnVuY3Rpb24gKGxhdExuZ0JvdW5kcykge1xuXHRcdGxhdExuZ0JvdW5kcyA9IEwubGF0TG5nQm91bmRzKGxhdExuZ0JvdW5kcyk7XG5cdFx0cmV0dXJuIFtcblx0XHRcdGxhdExuZ0JvdW5kcy5nZXRTb3V0aFdlc3QoKSxcblx0XHRcdGxhdExuZ0JvdW5kcy5nZXROb3J0aFdlc3QoKSxcblx0XHRcdGxhdExuZ0JvdW5kcy5nZXROb3J0aEVhc3QoKSxcblx0XHRcdGxhdExuZ0JvdW5kcy5nZXRTb3V0aEVhc3QoKVxuXHRcdF07XG5cdH1cbn0pO1xuXG5cbi8vIEBmYWN0b3J5IEwucmVjdGFuZ2xlKGxhdExuZ0JvdW5kczogTGF0TG5nQm91bmRzLCBvcHRpb25zPzogUG9seWxpbmUgb3B0aW9ucylcbkwucmVjdGFuZ2xlID0gZnVuY3Rpb24gKGxhdExuZ0JvdW5kcywgb3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuUmVjdGFuZ2xlKGxhdExuZ0JvdW5kcywgb3B0aW9ucyk7XG59O1xuXG5cblxuLypcbiAqIEBjbGFzcyBDaXJjbGVNYXJrZXJcbiAqIEBha2EgTC5DaXJjbGVNYXJrZXJcbiAqIEBpbmhlcml0cyBQYXRoXG4gKlxuICogQSBjaXJjbGUgb2YgYSBmaXhlZCBzaXplIHdpdGggcmFkaXVzIHNwZWNpZmllZCBpbiBwaXhlbHMuIEV4dGVuZHMgYFBhdGhgLlxuICovXG5cbkwuQ2lyY2xlTWFya2VyID0gTC5QYXRoLmV4dGVuZCh7XG5cblx0Ly8gQHNlY3Rpb25cblx0Ly8gQGFrYSBDaXJjbGVNYXJrZXIgb3B0aW9uc1xuXHRvcHRpb25zOiB7XG5cdFx0ZmlsbDogdHJ1ZSxcblxuXHRcdC8vIEBvcHRpb24gcmFkaXVzOiBOdW1iZXIgPSAxMFxuXHRcdC8vIFJhZGl1cyBvZiB0aGUgY2lyY2xlIG1hcmtlciwgaW4gcGl4ZWxzXG5cdFx0cmFkaXVzOiAxMFxuXHR9LFxuXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMpIHtcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcblx0XHR0aGlzLl9yYWRpdXMgPSB0aGlzLm9wdGlvbnMucmFkaXVzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0TGF0TG5nKGxhdExuZzogTGF0TG5nKTogdGhpc1xuXHQvLyBTZXRzIHRoZSBwb3NpdGlvbiBvZiBhIGNpcmNsZSBtYXJrZXIgdG8gYSBuZXcgbG9jYXRpb24uXG5cdHNldExhdExuZzogZnVuY3Rpb24gKGxhdGxuZykge1xuXHRcdHRoaXMuX2xhdGxuZyA9IEwubGF0TG5nKGxhdGxuZyk7XG5cdFx0dGhpcy5yZWRyYXcoKTtcblx0XHRyZXR1cm4gdGhpcy5maXJlKCdtb3ZlJywge2xhdGxuZzogdGhpcy5fbGF0bG5nfSk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRMYXRMbmcoKTogTGF0TG5nXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgZ2VvZ3JhcGhpY2FsIHBvc2l0aW9uIG9mIHRoZSBjaXJjbGUgbWFya2VyXG5cdGdldExhdExuZzogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9sYXRsbmc7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBzZXRSYWRpdXMocmFkaXVzOiBOdW1iZXIpOiB0aGlzXG5cdC8vIFNldHMgdGhlIHJhZGl1cyBvZiBhIGNpcmNsZSBtYXJrZXIuIFVuaXRzIGFyZSBpbiBwaXhlbHMuXG5cdHNldFJhZGl1czogZnVuY3Rpb24gKHJhZGl1cykge1xuXHRcdHRoaXMub3B0aW9ucy5yYWRpdXMgPSB0aGlzLl9yYWRpdXMgPSByYWRpdXM7XG5cdFx0cmV0dXJuIHRoaXMucmVkcmF3KCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRSYWRpdXMoKTogTnVtYmVyXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgcmFkaXVzIG9mIHRoZSBjaXJjbGVcblx0Z2V0UmFkaXVzOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3JhZGl1cztcblx0fSxcblxuXHRzZXRTdHlsZSA6IGZ1bmN0aW9uIChvcHRpb25zKSB7XG5cdFx0dmFyIHJhZGl1cyA9IG9wdGlvbnMgJiYgb3B0aW9ucy5yYWRpdXMgfHwgdGhpcy5fcmFkaXVzO1xuXHRcdEwuUGF0aC5wcm90b3R5cGUuc2V0U3R5bGUuY2FsbCh0aGlzLCBvcHRpb25zKTtcblx0XHR0aGlzLnNldFJhZGl1cyhyYWRpdXMpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdF9wcm9qZWN0OiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fcG9pbnQgPSB0aGlzLl9tYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2xhdGxuZyk7XG5cdFx0dGhpcy5fdXBkYXRlQm91bmRzKCk7XG5cdH0sXG5cblx0X3VwZGF0ZUJvdW5kczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciByID0gdGhpcy5fcmFkaXVzLFxuXHRcdCAgICByMiA9IHRoaXMuX3JhZGl1c1kgfHwgcixcblx0XHQgICAgdyA9IHRoaXMuX2NsaWNrVG9sZXJhbmNlKCksXG5cdFx0ICAgIHAgPSBbciArIHcsIHIyICsgd107XG5cdFx0dGhpcy5fcHhCb3VuZHMgPSBuZXcgTC5Cb3VuZHModGhpcy5fcG9pbnQuc3VidHJhY3QocCksIHRoaXMuX3BvaW50LmFkZChwKSk7XG5cdH0sXG5cblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9tYXApIHtcblx0XHRcdHRoaXMuX3VwZGF0ZVBhdGgoKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVBhdGg6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZW5kZXJlci5fdXBkYXRlQ2lyY2xlKHRoaXMpO1xuXHR9LFxuXG5cdF9lbXB0eTogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9yYWRpdXMgJiYgIXRoaXMuX3JlbmRlcmVyLl9ib3VuZHMuaW50ZXJzZWN0cyh0aGlzLl9weEJvdW5kcyk7XG5cdH1cbn0pO1xuXG5cbi8vIEBmYWN0b3J5IEwuY2lyY2xlTWFya2VyKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogQ2lyY2xlTWFya2VyIG9wdGlvbnMpXG4vLyBJbnN0YW50aWF0ZXMgYSBjaXJjbGUgbWFya2VyIG9iamVjdCBnaXZlbiBhIGdlb2dyYXBoaWNhbCBwb2ludCwgYW5kIGFuIG9wdGlvbmFsIG9wdGlvbnMgb2JqZWN0LlxuTC5jaXJjbGVNYXJrZXIgPSBmdW5jdGlvbiAobGF0bG5nLCBvcHRpb25zKSB7XG5cdHJldHVybiBuZXcgTC5DaXJjbGVNYXJrZXIobGF0bG5nLCBvcHRpb25zKTtcbn07XG5cblxuXG4vKlxuICogQGNsYXNzIENpcmNsZVxuICogQGFrYSBMLkNpcmNsZVxuICogQGluaGVyaXRzIENpcmNsZU1hcmtlclxuICpcbiAqIEEgY2xhc3MgZm9yIGRyYXdpbmcgY2lyY2xlIG92ZXJsYXlzIG9uIGEgbWFwLiBFeHRlbmRzIGBDaXJjbGVNYXJrZXJgLlxuICpcbiAqIEl0J3MgYW4gYXBwcm94aW1hdGlvbiBhbmQgc3RhcnRzIHRvIGRpdmVyZ2UgZnJvbSBhIHJlYWwgY2lyY2xlIGNsb3NlciB0byBwb2xlcyAoZHVlIHRvIHByb2plY3Rpb24gZGlzdG9ydGlvbikuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBgYGBqc1xuICogTC5jaXJjbGUoWzUwLjUsIDMwLjVdLCB7cmFkaXVzOiAyMDB9KS5hZGRUbyhtYXApO1xuICogYGBgXG4gKi9cblxuTC5DaXJjbGUgPSBMLkNpcmNsZU1hcmtlci5leHRlbmQoe1xuXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChsYXRsbmcsIG9wdGlvbnMsIGxlZ2FjeU9wdGlvbnMpIHtcblx0XHRpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdudW1iZXInKSB7XG5cdFx0XHQvLyBCYWNrd2FyZHMgY29tcGF0aWJpbGl0eSB3aXRoIDAuNy54IGZhY3RvcnkgKGxhdGxuZywgcmFkaXVzLCBvcHRpb25zPylcblx0XHRcdG9wdGlvbnMgPSBMLmV4dGVuZCh7fSwgbGVnYWN5T3B0aW9ucywge3JhZGl1czogb3B0aW9uc30pO1xuXHRcdH1cblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XG5cdFx0dGhpcy5fbGF0bG5nID0gTC5sYXRMbmcobGF0bG5nKTtcblxuXHRcdGlmIChpc05hTih0aGlzLm9wdGlvbnMucmFkaXVzKSkgeyB0aHJvdyBuZXcgRXJyb3IoJ0NpcmNsZSByYWRpdXMgY2Fubm90IGJlIE5hTicpOyB9XG5cblx0XHQvLyBAc2VjdGlvblxuXHRcdC8vIEBha2EgQ2lyY2xlIG9wdGlvbnNcblx0XHQvLyBAb3B0aW9uIHJhZGl1czogTnVtYmVyOyBSYWRpdXMgb2YgdGhlIGNpcmNsZSwgaW4gbWV0ZXJzLlxuXHRcdHRoaXMuX21SYWRpdXMgPSB0aGlzLm9wdGlvbnMucmFkaXVzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2Qgc2V0UmFkaXVzKHJhZGl1czogTnVtYmVyKTogdGhpc1xuXHQvLyBTZXRzIHRoZSByYWRpdXMgb2YgYSBjaXJjbGUuIFVuaXRzIGFyZSBpbiBtZXRlcnMuXG5cdHNldFJhZGl1czogZnVuY3Rpb24gKHJhZGl1cykge1xuXHRcdHRoaXMuX21SYWRpdXMgPSByYWRpdXM7XG5cdFx0cmV0dXJuIHRoaXMucmVkcmF3KCk7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBnZXRSYWRpdXMoKTogTnVtYmVyXG5cdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgcmFkaXVzIG9mIGEgY2lyY2xlLiBVbml0cyBhcmUgaW4gbWV0ZXJzLlxuXHRnZXRSYWRpdXM6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fbVJhZGl1cztcblx0fSxcblxuXHQvLyBAbWV0aG9kIGdldEJvdW5kcygpOiBMYXRMbmdCb3VuZHNcblx0Ly8gUmV0dXJucyB0aGUgYExhdExuZ0JvdW5kc2Agb2YgdGhlIHBhdGguXG5cdGdldEJvdW5kczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBoYWxmID0gW3RoaXMuX3JhZGl1cywgdGhpcy5fcmFkaXVzWSB8fCB0aGlzLl9yYWRpdXNdO1xuXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZ0JvdW5kcyhcblx0XHRcdHRoaXMuX21hcC5sYXllclBvaW50VG9MYXRMbmcodGhpcy5fcG9pbnQuc3VidHJhY3QoaGFsZikpLFxuXHRcdFx0dGhpcy5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyh0aGlzLl9wb2ludC5hZGQoaGFsZikpKTtcblx0fSxcblxuXHRzZXRTdHlsZTogTC5QYXRoLnByb3RvdHlwZS5zZXRTdHlsZSxcblxuXHRfcHJvamVjdDogZnVuY3Rpb24gKCkge1xuXG5cdFx0dmFyIGxuZyA9IHRoaXMuX2xhdGxuZy5sbmcsXG5cdFx0ICAgIGxhdCA9IHRoaXMuX2xhdGxuZy5sYXQsXG5cdFx0ICAgIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgY3JzID0gbWFwLm9wdGlvbnMuY3JzO1xuXG5cdFx0aWYgKGNycy5kaXN0YW5jZSA9PT0gTC5DUlMuRWFydGguZGlzdGFuY2UpIHtcblx0XHRcdHZhciBkID0gTWF0aC5QSSAvIDE4MCxcblx0XHRcdCAgICBsYXRSID0gKHRoaXMuX21SYWRpdXMgLyBMLkNSUy5FYXJ0aC5SKSAvIGQsXG5cdFx0XHQgICAgdG9wID0gbWFwLnByb2plY3QoW2xhdCArIGxhdFIsIGxuZ10pLFxuXHRcdFx0ICAgIGJvdHRvbSA9IG1hcC5wcm9qZWN0KFtsYXQgLSBsYXRSLCBsbmddKSxcblx0XHRcdCAgICBwID0gdG9wLmFkZChib3R0b20pLmRpdmlkZUJ5KDIpLFxuXHRcdFx0ICAgIGxhdDIgPSBtYXAudW5wcm9qZWN0KHApLmxhdCxcblx0XHRcdCAgICBsbmdSID0gTWF0aC5hY29zKChNYXRoLmNvcyhsYXRSICogZCkgLSBNYXRoLnNpbihsYXQgKiBkKSAqIE1hdGguc2luKGxhdDIgKiBkKSkgL1xuXHRcdFx0ICAgICAgICAgICAgKE1hdGguY29zKGxhdCAqIGQpICogTWF0aC5jb3MobGF0MiAqIGQpKSkgLyBkO1xuXG5cdFx0XHRpZiAoaXNOYU4obG5nUikgfHwgbG5nUiA9PT0gMCkge1xuXHRcdFx0XHRsbmdSID0gbGF0UiAvIE1hdGguY29zKE1hdGguUEkgLyAxODAgKiBsYXQpOyAvLyBGYWxsYmFjayBmb3IgZWRnZSBjYXNlLCAjMjQyNVxuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLl9wb2ludCA9IHAuc3VidHJhY3QobWFwLmdldFBpeGVsT3JpZ2luKCkpO1xuXHRcdFx0dGhpcy5fcmFkaXVzID0gaXNOYU4obG5nUikgPyAwIDogTWF0aC5tYXgoTWF0aC5yb3VuZChwLnggLSBtYXAucHJvamVjdChbbGF0MiwgbG5nIC0gbG5nUl0pLngpLCAxKTtcblx0XHRcdHRoaXMuX3JhZGl1c1kgPSBNYXRoLm1heChNYXRoLnJvdW5kKHAueSAtIHRvcC55KSwgMSk7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0dmFyIGxhdGxuZzIgPSBjcnMudW5wcm9qZWN0KGNycy5wcm9qZWN0KHRoaXMuX2xhdGxuZykuc3VidHJhY3QoW3RoaXMuX21SYWRpdXMsIDBdKSk7XG5cblx0XHRcdHRoaXMuX3BvaW50ID0gbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0aGlzLl9sYXRsbmcpO1xuXHRcdFx0dGhpcy5fcmFkaXVzID0gdGhpcy5fcG9pbnQueCAtIG1hcC5sYXRMbmdUb0xheWVyUG9pbnQobGF0bG5nMikueDtcblx0XHR9XG5cblx0XHR0aGlzLl91cGRhdGVCb3VuZHMoKTtcblx0fVxufSk7XG5cbi8vIEBmYWN0b3J5IEwuY2lyY2xlKGxhdGxuZzogTGF0TG5nLCBvcHRpb25zPzogQ2lyY2xlIG9wdGlvbnMpXG4vLyBJbnN0YW50aWF0ZXMgYSBjaXJjbGUgb2JqZWN0IGdpdmVuIGEgZ2VvZ3JhcGhpY2FsIHBvaW50LCBhbmQgYW4gb3B0aW9ucyBvYmplY3Rcbi8vIHdoaWNoIGNvbnRhaW5zIHRoZSBjaXJjbGUgcmFkaXVzLlxuLy8gQGFsdGVybmF0aXZlXG4vLyBAZmFjdG9yeSBMLmNpcmNsZShsYXRsbmc6IExhdExuZywgcmFkaXVzOiBOdW1iZXIsIG9wdGlvbnM/OiBDaXJjbGUgb3B0aW9ucylcbi8vIE9ic29sZXRlIHdheSBvZiBpbnN0YW50aWF0aW5nIGEgY2lyY2xlLCBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIDAuNy54IGNvZGUuXG4vLyBEbyBub3QgdXNlIGluIG5ldyBhcHBsaWNhdGlvbnMgb3IgcGx1Z2lucy5cbkwuY2lyY2xlID0gZnVuY3Rpb24gKGxhdGxuZywgb3B0aW9ucywgbGVnYWN5T3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuQ2lyY2xlKGxhdGxuZywgb3B0aW9ucywgbGVnYWN5T3B0aW9ucyk7XG59O1xuXG5cblxuLypcbiAqIEBjbGFzcyBTVkdcbiAqIEBpbmhlcml0cyBSZW5kZXJlclxuICogQGFrYSBMLlNWR1xuICpcbiAqIEFsbG93cyB2ZWN0b3IgbGF5ZXJzIHRvIGJlIGRpc3BsYXllZCB3aXRoIFtTVkddKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRykuXG4gKiBJbmhlcml0cyBgUmVuZGVyZXJgLlxuICpcbiAqIER1ZSB0byBbdGVjaG5pY2FsIGxpbWl0YXRpb25zXShodHRwOi8vY2FuaXVzZS5jb20vI3NlYXJjaD1zdmcpLCBTVkcgaXMgbm90XG4gKiBhdmFpbGFibGUgaW4gYWxsIHdlYiBicm93c2Vycywgbm90YWJseSBBbmRyb2lkIDIueCBhbmQgMy54LlxuICpcbiAqIEFsdGhvdWdoIFNWRyBpcyBub3QgYXZhaWxhYmxlIG9uIElFNyBhbmQgSUU4LCB0aGVzZSBicm93c2VycyBzdXBwb3J0XG4gKiBbVk1MXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9WZWN0b3JfTWFya3VwX0xhbmd1YWdlKVxuICogKGEgbm93IGRlcHJlY2F0ZWQgdGVjaG5vbG9neSksIGFuZCB0aGUgU1ZHIHJlbmRlcmVyIHdpbGwgZmFsbCBiYWNrIHRvIFZNTCBpblxuICogdGhpcyBjYXNlLlxuICpcbiAqIEBleGFtcGxlXG4gKlxuICogVXNlIFNWRyBieSBkZWZhdWx0IGZvciBhbGwgcGF0aHMgaW4gdGhlIG1hcDpcbiAqXG4gKiBgYGBqc1xuICogdmFyIG1hcCA9IEwubWFwKCdtYXAnLCB7XG4gKiBcdHJlbmRlcmVyOiBMLnN2ZygpXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqIFVzZSBhIFNWRyByZW5kZXJlciB3aXRoIGV4dHJhIHBhZGRpbmcgZm9yIHNwZWNpZmljIHZlY3RvciBnZW9tZXRyaWVzOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcpO1xuICogdmFyIG15UmVuZGVyZXIgPSBMLnN2Zyh7IHBhZGRpbmc6IDAuNSB9KTtcbiAqIHZhciBsaW5lID0gTC5wb2x5bGluZSggY29vcmRpbmF0ZXMsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogdmFyIGNpcmNsZSA9IEwuY2lyY2xlKCBjZW50ZXIsIHsgcmVuZGVyZXI6IG15UmVuZGVyZXIgfSApO1xuICogYGBgXG4gKi9cblxuTC5TVkcgPSBMLlJlbmRlcmVyLmV4dGVuZCh7XG5cblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGV2ZW50cyA9IEwuUmVuZGVyZXIucHJvdG90eXBlLmdldEV2ZW50cy5jYWxsKHRoaXMpO1xuXHRcdGV2ZW50cy56b29tc3RhcnQgPSB0aGlzLl9vblpvb21TdGFydDtcblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fY29udGFpbmVyID0gTC5TVkcuY3JlYXRlKCdzdmcnKTtcblxuXHRcdC8vIG1ha2VzIGl0IHBvc3NpYmxlIHRvIGNsaWNrIHRocm91Z2ggc3ZnIHJvb3Q7IHdlJ2xsIHJlc2V0IGl0IGJhY2sgaW4gaW5kaXZpZHVhbCBwYXRoc1xuXHRcdHRoaXMuX2NvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ3BvaW50ZXItZXZlbnRzJywgJ25vbmUnKTtcblxuXHRcdHRoaXMuX3Jvb3RHcm91cCA9IEwuU1ZHLmNyZWF0ZSgnZycpO1xuXHRcdHRoaXMuX2NvbnRhaW5lci5hcHBlbmRDaGlsZCh0aGlzLl9yb290R3JvdXApO1xuXHR9LFxuXG5cdF9vblpvb21TdGFydDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIERyYWctdGhlbi1waW5jaCBpbnRlcmFjdGlvbnMgbWlnaHQgbWVzcyB1cCB0aGUgY2VudGVyIGFuZCB6b29tLlxuXHRcdC8vIEluIHRoaXMgY2FzZSwgdGhlIGVhc2llc3Qgd2F5IHRvIHByZXZlbnQgdGhpcyBpcyByZS1kbyB0aGUgcmVuZGVyZXJcblx0XHQvLyAgIGJvdW5kcyBhbmQgcGFkZGluZyB3aGVuIHRoZSB6b29taW5nIHN0YXJ0cy5cblx0XHR0aGlzLl91cGRhdGUoKTtcblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcC5fYW5pbWF0aW5nWm9vbSAmJiB0aGlzLl9ib3VuZHMpIHsgcmV0dXJuOyB9XG5cblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5fdXBkYXRlLmNhbGwodGhpcyk7XG5cblx0XHR2YXIgYiA9IHRoaXMuX2JvdW5kcyxcblx0XHQgICAgc2l6ZSA9IGIuZ2V0U2l6ZSgpLFxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXI7XG5cblx0XHQvLyBzZXQgc2l6ZSBvZiBzdmctY29udGFpbmVyIGlmIGNoYW5nZWRcblx0XHRpZiAoIXRoaXMuX3N2Z1NpemUgfHwgIXRoaXMuX3N2Z1NpemUuZXF1YWxzKHNpemUpKSB7XG5cdFx0XHR0aGlzLl9zdmdTaXplID0gc2l6ZTtcblx0XHRcdGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ3dpZHRoJywgc2l6ZS54KTtcblx0XHRcdGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ2hlaWdodCcsIHNpemUueSk7XG5cdFx0fVxuXG5cdFx0Ly8gbW92ZW1lbnQ6IHVwZGF0ZSBjb250YWluZXIgdmlld0JveCBzbyB0aGF0IHdlIGRvbid0IGhhdmUgdG8gY2hhbmdlIGNvb3JkaW5hdGVzIG9mIGluZGl2aWR1YWwgbGF5ZXJzXG5cdFx0TC5Eb21VdGlsLnNldFBvc2l0aW9uKGNvbnRhaW5lciwgYi5taW4pO1xuXHRcdGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoJ3ZpZXdCb3gnLCBbYi5taW4ueCwgYi5taW4ueSwgc2l6ZS54LCBzaXplLnldLmpvaW4oJyAnKSk7XG5cblx0XHR0aGlzLmZpcmUoJ3VwZGF0ZScpO1xuXHR9LFxuXG5cdC8vIG1ldGhvZHMgYmVsb3cgYXJlIGNhbGxlZCBieSB2ZWN0b3IgbGF5ZXJzIGltcGxlbWVudGF0aW9uc1xuXG5cdF9pbml0UGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHBhdGggPSBsYXllci5fcGF0aCA9IEwuU1ZHLmNyZWF0ZSgncGF0aCcpO1xuXG5cdFx0Ly8gQG5hbWVzcGFjZSBQYXRoXG5cdFx0Ly8gQG9wdGlvbiBjbGFzc05hbWU6IFN0cmluZyA9IG51bGxcblx0XHQvLyBDdXN0b20gY2xhc3MgbmFtZSBzZXQgb24gYW4gZWxlbWVudC4gT25seSBmb3IgU1ZHIHJlbmRlcmVyLlxuXHRcdGlmIChsYXllci5vcHRpb25zLmNsYXNzTmFtZSkge1xuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHBhdGgsIGxheWVyLm9wdGlvbnMuY2xhc3NOYW1lKTtcblx0XHR9XG5cblx0XHRpZiAobGF5ZXIub3B0aW9ucy5pbnRlcmFjdGl2ZSkge1xuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHBhdGgsICdsZWFmbGV0LWludGVyYWN0aXZlJyk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fdXBkYXRlU3R5bGUobGF5ZXIpO1xuXHRcdHRoaXMuX2xheWVyc1tMLnN0YW1wKGxheWVyKV0gPSBsYXllcjtcblx0fSxcblxuXHRfYWRkUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dGhpcy5fcm9vdEdyb3VwLmFwcGVuZENoaWxkKGxheWVyLl9wYXRoKTtcblx0XHRsYXllci5hZGRJbnRlcmFjdGl2ZVRhcmdldChsYXllci5fcGF0aCk7XG5cdH0sXG5cblx0X3JlbW92ZVBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC5yZW1vdmUobGF5ZXIuX3BhdGgpO1xuXHRcdGxheWVyLnJlbW92ZUludGVyYWN0aXZlVGFyZ2V0KGxheWVyLl9wYXRoKTtcblx0XHRkZWxldGUgdGhpcy5fbGF5ZXJzW0wuc3RhbXAobGF5ZXIpXTtcblx0fSxcblxuXHRfdXBkYXRlUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0bGF5ZXIuX3Byb2plY3QoKTtcblx0XHRsYXllci5fdXBkYXRlKCk7XG5cdH0sXG5cblx0X3VwZGF0ZVN0eWxlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcGF0aCA9IGxheWVyLl9wYXRoLFxuXHRcdCAgICBvcHRpb25zID0gbGF5ZXIub3B0aW9ucztcblxuXHRcdGlmICghcGF0aCkgeyByZXR1cm47IH1cblxuXHRcdGlmIChvcHRpb25zLnN0cm9rZSkge1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZScsIG9wdGlvbnMuY29sb3IpO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS1vcGFjaXR5Jywgb3B0aW9ucy5vcGFjaXR5KTtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdzdHJva2Utd2lkdGgnLCBvcHRpb25zLndlaWdodCk7XG5cdFx0XHRwYXRoLnNldEF0dHJpYnV0ZSgnc3Ryb2tlLWxpbmVjYXAnLCBvcHRpb25zLmxpbmVDYXApO1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS1saW5lam9pbicsIG9wdGlvbnMubGluZUpvaW4pO1xuXG5cdFx0XHRpZiAob3B0aW9ucy5kYXNoQXJyYXkpIHtcblx0XHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZS1kYXNoYXJyYXknLCBvcHRpb25zLmRhc2hBcnJheSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRwYXRoLnJlbW92ZUF0dHJpYnV0ZSgnc3Ryb2tlLWRhc2hhcnJheScpO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAob3B0aW9ucy5kYXNoT2Zmc2V0KSB7XG5cdFx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdzdHJva2UtZGFzaG9mZnNldCcsIG9wdGlvbnMuZGFzaE9mZnNldCk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRwYXRoLnJlbW92ZUF0dHJpYnV0ZSgnc3Ryb2tlLWRhc2hvZmZzZXQnKTtcblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ3N0cm9rZScsICdub25lJyk7XG5cdFx0fVxuXG5cdFx0aWYgKG9wdGlvbnMuZmlsbCkge1xuXHRcdFx0cGF0aC5zZXRBdHRyaWJ1dGUoJ2ZpbGwnLCBvcHRpb25zLmZpbGxDb2xvciB8fCBvcHRpb25zLmNvbG9yKTtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdmaWxsLW9wYWNpdHknLCBvcHRpb25zLmZpbGxPcGFjaXR5KTtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdmaWxsLXJ1bGUnLCBvcHRpb25zLmZpbGxSdWxlIHx8ICdldmVub2RkJyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHBhdGguc2V0QXR0cmlidXRlKCdmaWxsJywgJ25vbmUnKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVBvbHk6IGZ1bmN0aW9uIChsYXllciwgY2xvc2VkKSB7XG5cdFx0dGhpcy5fc2V0UGF0aChsYXllciwgTC5TVkcucG9pbnRzVG9QYXRoKGxheWVyLl9wYXJ0cywgY2xvc2VkKSk7XG5cdH0sXG5cblx0X3VwZGF0ZUNpcmNsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHAgPSBsYXllci5fcG9pbnQsXG5cdFx0ICAgIHIgPSBsYXllci5fcmFkaXVzLFxuXHRcdCAgICByMiA9IGxheWVyLl9yYWRpdXNZIHx8IHIsXG5cdFx0ICAgIGFyYyA9ICdhJyArIHIgKyAnLCcgKyByMiArICcgMCAxLDAgJztcblxuXHRcdC8vIGRyYXdpbmcgYSBjaXJjbGUgd2l0aCB0d28gaGFsZi1hcmNzXG5cdFx0dmFyIGQgPSBsYXllci5fZW1wdHkoKSA/ICdNMCAwJyA6XG5cdFx0XHRcdCdNJyArIChwLnggLSByKSArICcsJyArIHAueSArXG5cdFx0XHRcdGFyYyArIChyICogMikgKyAnLDAgJyArXG5cdFx0XHRcdGFyYyArICgtciAqIDIpICsgJywwICc7XG5cblx0XHR0aGlzLl9zZXRQYXRoKGxheWVyLCBkKTtcblx0fSxcblxuXHRfc2V0UGF0aDogZnVuY3Rpb24gKGxheWVyLCBwYXRoKSB7XG5cdFx0bGF5ZXIuX3BhdGguc2V0QXR0cmlidXRlKCdkJywgcGF0aCk7XG5cdH0sXG5cblx0Ly8gU1ZHIGRvZXMgbm90IGhhdmUgdGhlIGNvbmNlcHQgb2YgekluZGV4IHNvIHdlIHJlc29ydCB0byBjaGFuZ2luZyB0aGUgRE9NIG9yZGVyIG9mIGVsZW1lbnRzXG5cdF9icmluZ1RvRnJvbnQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC50b0Zyb250KGxheWVyLl9wYXRoKTtcblx0fSxcblxuXHRfYnJpbmdUb0JhY2s6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC50b0JhY2sobGF5ZXIuX3BhdGgpO1xuXHR9XG59KTtcblxuXG4vLyBAbmFtZXNwYWNlIFNWRzsgQHNlY3Rpb25cbi8vIFRoZXJlIGFyZSBzZXZlcmFsIHN0YXRpYyBmdW5jdGlvbnMgd2hpY2ggY2FuIGJlIGNhbGxlZCB3aXRob3V0IGluc3RhbnRpYXRpbmcgTC5TVkc6XG5MLmV4dGVuZChMLlNWRywge1xuXHQvLyBAZnVuY3Rpb24gY3JlYXRlKG5hbWU6IFN0cmluZyk6IFNWR0VsZW1lbnRcblx0Ly8gUmV0dXJucyBhIGluc3RhbmNlIG9mIFtTVkdFbGVtZW50XShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvU1ZHRWxlbWVudCksXG5cdC8vIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGNsYXNzIG5hbWUgcGFzc2VkLiBGb3IgZXhhbXBsZSwgdXNpbmcgJ2xpbmUnIHdpbGwgcmV0dXJuXG5cdC8vIGFuIGluc3RhbmNlIG9mIFtTVkdMaW5lRWxlbWVudF0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZG9jcy9XZWIvQVBJL1NWR0xpbmVFbGVtZW50KS5cblx0Y3JlYXRlOiBmdW5jdGlvbiAobmFtZSkge1xuXHRcdHJldHVybiBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoJ2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJywgbmFtZSk7XG5cdH0sXG5cblx0Ly8gQGZ1bmN0aW9uIHBvaW50c1RvUGF0aChyaW5nczogUG9pbnRbXSwgY2xvc2VkOiBCb29sZWFuKTogU3RyaW5nXG5cdC8vIEdlbmVyYXRlcyBhIFNWRyBwYXRoIHN0cmluZyBmb3IgbXVsdGlwbGUgcmluZ3MsIHdpdGggZWFjaCByaW5nIHR1cm5pbmdcblx0Ly8gaW50byBcIk0uLkwuLkwuLlwiIGluc3RydWN0aW9uc1xuXHRwb2ludHNUb1BhdGg6IGZ1bmN0aW9uIChyaW5ncywgY2xvc2VkKSB7XG5cdFx0dmFyIHN0ciA9ICcnLFxuXHRcdCAgICBpLCBqLCBsZW4sIGxlbjIsIHBvaW50cywgcDtcblxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IHJpbmdzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRwb2ludHMgPSByaW5nc1tpXTtcblxuXHRcdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBvaW50cy5sZW5ndGg7IGogPCBsZW4yOyBqKyspIHtcblx0XHRcdFx0cCA9IHBvaW50c1tqXTtcblx0XHRcdFx0c3RyICs9IChqID8gJ0wnIDogJ00nKSArIHAueCArICcgJyArIHAueTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gY2xvc2VzIHRoZSByaW5nIGZvciBwb2x5Z29uczsgXCJ4XCIgaXMgVk1MIHN5bnRheFxuXHRcdFx0c3RyICs9IGNsb3NlZCA/IChMLkJyb3dzZXIuc3ZnID8gJ3onIDogJ3gnKSA6ICcnO1xuXHRcdH1cblxuXHRcdC8vIFNWRyBjb21wbGFpbnMgYWJvdXQgZW1wdHkgcGF0aCBzdHJpbmdzXG5cdFx0cmV0dXJuIHN0ciB8fCAnTTAgMCc7XG5cdH1cbn0pO1xuXG4vLyBAbmFtZXNwYWNlIEJyb3dzZXI7IEBwcm9wZXJ0eSBzdmc6IEJvb2xlYW5cbi8vIGB0cnVlYCB3aGVuIHRoZSBicm93c2VyIHN1cHBvcnRzIFtTVkddKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL1NWRykuXG5MLkJyb3dzZXIuc3ZnID0gISEoZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TICYmIEwuU1ZHLmNyZWF0ZSgnc3ZnJykuY3JlYXRlU1ZHUmVjdCk7XG5cblxuLy8gQG5hbWVzcGFjZSBTVkdcbi8vIEBmYWN0b3J5IEwuc3ZnKG9wdGlvbnM/OiBSZW5kZXJlciBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhIFNWRyByZW5kZXJlciB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuTC5zdmcgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gTC5Ccm93c2VyLnN2ZyB8fCBMLkJyb3dzZXIudm1sID8gbmV3IEwuU1ZHKG9wdGlvbnMpIDogbnVsbDtcbn07XG5cblxuXG4vKlxuICogVGhhbmtzIHRvIERtaXRyeSBCYXJhbm92c2t5IGFuZCBoaXMgUmFwaGFlbCBsaWJyYXJ5IGZvciBpbnNwaXJhdGlvbiFcbiAqL1xuXG4vKlxuICogQGNsYXNzIFNWR1xuICpcbiAqIEFsdGhvdWdoIFNWRyBpcyBub3QgYXZhaWxhYmxlIG9uIElFNyBhbmQgSUU4LCB0aGVzZSBicm93c2VycyBzdXBwb3J0IFtWTUxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1ZlY3Rvcl9NYXJrdXBfTGFuZ3VhZ2UpLCBhbmQgdGhlIFNWRyByZW5kZXJlciB3aWxsIGZhbGwgYmFjayB0byBWTUwgaW4gdGhpcyBjYXNlLlxuICpcbiAqIFZNTCB3YXMgZGVwcmVjYXRlZCBpbiAyMDEyLCB3aGljaCBtZWFucyBWTUwgZnVuY3Rpb25hbGl0eSBleGlzdHMgb25seSBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHlcbiAqIHdpdGggb2xkIHZlcnNpb25zIG9mIEludGVybmV0IEV4cGxvcmVyLlxuICovXG5cbi8vIEBuYW1lc3BhY2UgQnJvd3NlcjsgQHByb3BlcnR5IHZtbDogQm9vbGVhblxuLy8gYHRydWVgIGlmIHRoZSBicm93c2VyIHN1cHBvcnRzIFtWTUxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1ZlY3Rvcl9NYXJrdXBfTGFuZ3VhZ2UpLlxuTC5Ccm93c2VyLnZtbCA9ICFMLkJyb3dzZXIuc3ZnICYmIChmdW5jdGlvbiAoKSB7XG5cdHRyeSB7XG5cdFx0dmFyIGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuXHRcdGRpdi5pbm5lckhUTUwgPSAnPHY6c2hhcGUgYWRqPVwiMVwiLz4nO1xuXG5cdFx0dmFyIHNoYXBlID0gZGl2LmZpcnN0Q2hpbGQ7XG5cdFx0c2hhcGUuc3R5bGUuYmVoYXZpb3IgPSAndXJsKCNkZWZhdWx0I1ZNTCknO1xuXG5cdFx0cmV0dXJuIHNoYXBlICYmICh0eXBlb2Ygc2hhcGUuYWRqID09PSAnb2JqZWN0Jyk7XG5cblx0fSBjYXRjaCAoZSkge1xuXHRcdHJldHVybiBmYWxzZTtcblx0fVxufSgpKTtcblxuLy8gcmVkZWZpbmUgc29tZSBTVkcgbWV0aG9kcyB0byBoYW5kbGUgVk1MIHN5bnRheCB3aGljaCBpcyBzaW1pbGFyIGJ1dCB3aXRoIHNvbWUgZGlmZmVyZW5jZXNcbkwuU1ZHLmluY2x1ZGUoIUwuQnJvd3Nlci52bWwgPyB7fSA6IHtcblxuXHRfaW5pdENvbnRhaW5lcjogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXZtbC1jb250YWluZXInKTtcblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX21hcC5fYW5pbWF0aW5nWm9vbSkgeyByZXR1cm47IH1cblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5fdXBkYXRlLmNhbGwodGhpcyk7XG5cdFx0dGhpcy5maXJlKCd1cGRhdGUnKTtcblx0fSxcblxuXHRfaW5pdFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBjb250YWluZXIgPSBsYXllci5fY29udGFpbmVyID0gTC5TVkcuY3JlYXRlKCdzaGFwZScpO1xuXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGNvbnRhaW5lciwgJ2xlYWZsZXQtdm1sLXNoYXBlICcgKyAodGhpcy5vcHRpb25zLmNsYXNzTmFtZSB8fCAnJykpO1xuXG5cdFx0Y29udGFpbmVyLmNvb3Jkc2l6ZSA9ICcxIDEnO1xuXG5cdFx0bGF5ZXIuX3BhdGggPSBMLlNWRy5jcmVhdGUoJ3BhdGgnKTtcblx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQobGF5ZXIuX3BhdGgpO1xuXG5cdFx0dGhpcy5fdXBkYXRlU3R5bGUobGF5ZXIpO1xuXHRcdHRoaXMuX2xheWVyc1tMLnN0YW1wKGxheWVyKV0gPSBsYXllcjtcblx0fSxcblxuXHRfYWRkUGF0aDogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IGxheWVyLl9jb250YWluZXI7XG5cdFx0dGhpcy5fY29udGFpbmVyLmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XG5cblx0XHRpZiAobGF5ZXIub3B0aW9ucy5pbnRlcmFjdGl2ZSkge1xuXHRcdFx0bGF5ZXIuYWRkSW50ZXJhY3RpdmVUYXJnZXQoY29udGFpbmVyKTtcblx0XHR9XG5cdH0sXG5cblx0X3JlbW92ZVBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBjb250YWluZXIgPSBsYXllci5fY29udGFpbmVyO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmUoY29udGFpbmVyKTtcblx0XHRsYXllci5yZW1vdmVJbnRlcmFjdGl2ZVRhcmdldChjb250YWluZXIpO1xuXHRcdGRlbGV0ZSB0aGlzLl9sYXllcnNbTC5zdGFtcChsYXllcildO1xuXHR9LFxuXG5cdF91cGRhdGVTdHlsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIHN0cm9rZSA9IGxheWVyLl9zdHJva2UsXG5cdFx0ICAgIGZpbGwgPSBsYXllci5fZmlsbCxcblx0XHQgICAgb3B0aW9ucyA9IGxheWVyLm9wdGlvbnMsXG5cdFx0ICAgIGNvbnRhaW5lciA9IGxheWVyLl9jb250YWluZXI7XG5cblx0XHRjb250YWluZXIuc3Ryb2tlZCA9ICEhb3B0aW9ucy5zdHJva2U7XG5cdFx0Y29udGFpbmVyLmZpbGxlZCA9ICEhb3B0aW9ucy5maWxsO1xuXG5cdFx0aWYgKG9wdGlvbnMuc3Ryb2tlKSB7XG5cdFx0XHRpZiAoIXN0cm9rZSkge1xuXHRcdFx0XHRzdHJva2UgPSBsYXllci5fc3Ryb2tlID0gTC5TVkcuY3JlYXRlKCdzdHJva2UnKTtcblx0XHRcdH1cblx0XHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChzdHJva2UpO1xuXHRcdFx0c3Ryb2tlLndlaWdodCA9IG9wdGlvbnMud2VpZ2h0ICsgJ3B4Jztcblx0XHRcdHN0cm9rZS5jb2xvciA9IG9wdGlvbnMuY29sb3I7XG5cdFx0XHRzdHJva2Uub3BhY2l0eSA9IG9wdGlvbnMub3BhY2l0eTtcblxuXHRcdFx0aWYgKG9wdGlvbnMuZGFzaEFycmF5KSB7XG5cdFx0XHRcdHN0cm9rZS5kYXNoU3R5bGUgPSBMLlV0aWwuaXNBcnJheShvcHRpb25zLmRhc2hBcnJheSkgP1xuXHRcdFx0XHQgICAgb3B0aW9ucy5kYXNoQXJyYXkuam9pbignICcpIDpcblx0XHRcdFx0ICAgIG9wdGlvbnMuZGFzaEFycmF5LnJlcGxhY2UoLyggKiwgKikvZywgJyAnKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHN0cm9rZS5kYXNoU3R5bGUgPSAnJztcblx0XHRcdH1cblx0XHRcdHN0cm9rZS5lbmRjYXAgPSBvcHRpb25zLmxpbmVDYXAucmVwbGFjZSgnYnV0dCcsICdmbGF0Jyk7XG5cdFx0XHRzdHJva2Uuam9pbnN0eWxlID0gb3B0aW9ucy5saW5lSm9pbjtcblxuXHRcdH0gZWxzZSBpZiAoc3Ryb2tlKSB7XG5cdFx0XHRjb250YWluZXIucmVtb3ZlQ2hpbGQoc3Ryb2tlKTtcblx0XHRcdGxheWVyLl9zdHJva2UgPSBudWxsO1xuXHRcdH1cblxuXHRcdGlmIChvcHRpb25zLmZpbGwpIHtcblx0XHRcdGlmICghZmlsbCkge1xuXHRcdFx0XHRmaWxsID0gbGF5ZXIuX2ZpbGwgPSBMLlNWRy5jcmVhdGUoJ2ZpbGwnKTtcblx0XHRcdH1cblx0XHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChmaWxsKTtcblx0XHRcdGZpbGwuY29sb3IgPSBvcHRpb25zLmZpbGxDb2xvciB8fCBvcHRpb25zLmNvbG9yO1xuXHRcdFx0ZmlsbC5vcGFjaXR5ID0gb3B0aW9ucy5maWxsT3BhY2l0eTtcblxuXHRcdH0gZWxzZSBpZiAoZmlsbCkge1xuXHRcdFx0Y29udGFpbmVyLnJlbW92ZUNoaWxkKGZpbGwpO1xuXHRcdFx0bGF5ZXIuX2ZpbGwgPSBudWxsO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlQ2lyY2xlOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcCA9IGxheWVyLl9wb2ludC5yb3VuZCgpLFxuXHRcdCAgICByID0gTWF0aC5yb3VuZChsYXllci5fcmFkaXVzKSxcblx0XHQgICAgcjIgPSBNYXRoLnJvdW5kKGxheWVyLl9yYWRpdXNZIHx8IHIpO1xuXG5cdFx0dGhpcy5fc2V0UGF0aChsYXllciwgbGF5ZXIuX2VtcHR5KCkgPyAnTTAgMCcgOlxuXHRcdFx0XHQnQUwgJyArIHAueCArICcsJyArIHAueSArICcgJyArIHIgKyAnLCcgKyByMiArICcgMCwnICsgKDY1NTM1ICogMzYwKSk7XG5cdH0sXG5cblx0X3NldFBhdGg6IGZ1bmN0aW9uIChsYXllciwgcGF0aCkge1xuXHRcdGxheWVyLl9wYXRoLnYgPSBwYXRoO1xuXHR9LFxuXG5cdF9icmluZ1RvRnJvbnQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdEwuRG9tVXRpbC50b0Zyb250KGxheWVyLl9jb250YWluZXIpO1xuXHR9LFxuXG5cdF9icmluZ1RvQmFjazogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0TC5Eb21VdGlsLnRvQmFjayhsYXllci5fY29udGFpbmVyKTtcblx0fVxufSk7XG5cbmlmIChMLkJyb3dzZXIudm1sKSB7XG5cdEwuU1ZHLmNyZWF0ZSA9IChmdW5jdGlvbiAoKSB7XG5cdFx0dHJ5IHtcblx0XHRcdGRvY3VtZW50Lm5hbWVzcGFjZXMuYWRkKCdsdm1sJywgJ3VybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206dm1sJyk7XG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24gKG5hbWUpIHtcblx0XHRcdFx0cmV0dXJuIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJzxsdm1sOicgKyBuYW1lICsgJyBjbGFzcz1cImx2bWxcIj4nKTtcblx0XHRcdH07XG5cdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uIChuYW1lKSB7XG5cdFx0XHRcdHJldHVybiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCc8JyArIG5hbWUgKyAnIHhtbG5zPVwidXJuOnNjaGVtYXMtbWljcm9zb2Z0LmNvbTp2bWxcIiBjbGFzcz1cImx2bWxcIj4nKTtcblx0XHRcdH07XG5cdFx0fVxuXHR9KSgpO1xufVxuXG5cblxuLypcbiAqIEBjbGFzcyBDYW52YXNcbiAqIEBpbmhlcml0cyBSZW5kZXJlclxuICogQGFrYSBMLkNhbnZhc1xuICpcbiAqIEFsbG93cyB2ZWN0b3IgbGF5ZXJzIHRvIGJlIGRpc3BsYXllZCB3aXRoIFtgPGNhbnZhcz5gXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9kb2NzL1dlYi9BUEkvQ2FudmFzX0FQSSkuXG4gKiBJbmhlcml0cyBgUmVuZGVyZXJgLlxuICpcbiAqIER1ZSB0byBbdGVjaG5pY2FsIGxpbWl0YXRpb25zXShodHRwOi8vY2FuaXVzZS5jb20vI3NlYXJjaD1jYW52YXMpLCBDYW52YXMgaXMgbm90XG4gKiBhdmFpbGFibGUgaW4gYWxsIHdlYiBicm93c2Vycywgbm90YWJseSBJRTgsIGFuZCBvdmVybGFwcGluZyBnZW9tZXRyaWVzIG1pZ2h0XG4gKiBub3QgZGlzcGxheSBwcm9wZXJseSBpbiBzb21lIGVkZ2UgY2FzZXMuXG4gKlxuICogQGV4YW1wbGVcbiAqXG4gKiBVc2UgQ2FudmFzIGJ5IGRlZmF1bHQgZm9yIGFsbCBwYXRocyBpbiB0aGUgbWFwOlxuICpcbiAqIGBgYGpzXG4gKiB2YXIgbWFwID0gTC5tYXAoJ21hcCcsIHtcbiAqIFx0cmVuZGVyZXI6IEwuY2FudmFzKClcbiAqIH0pO1xuICogYGBgXG4gKlxuICogVXNlIGEgQ2FudmFzIHJlbmRlcmVyIHdpdGggZXh0cmEgcGFkZGluZyBmb3Igc3BlY2lmaWMgdmVjdG9yIGdlb21ldHJpZXM6XG4gKlxuICogYGBganNcbiAqIHZhciBtYXAgPSBMLm1hcCgnbWFwJyk7XG4gKiB2YXIgbXlSZW5kZXJlciA9IEwuY2FudmFzKHsgcGFkZGluZzogMC41IH0pO1xuICogdmFyIGxpbmUgPSBMLnBvbHlsaW5lKCBjb29yZGluYXRlcywgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiB2YXIgY2lyY2xlID0gTC5jaXJjbGUoIGNlbnRlciwgeyByZW5kZXJlcjogbXlSZW5kZXJlciB9ICk7XG4gKiBgYGBcbiAqL1xuXG5MLkNhbnZhcyA9IEwuUmVuZGVyZXIuZXh0ZW5kKHtcblx0Z2V0RXZlbnRzOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGV2ZW50cyA9IEwuUmVuZGVyZXIucHJvdG90eXBlLmdldEV2ZW50cy5jYWxsKHRoaXMpO1xuXHRcdGV2ZW50cy52aWV3cHJlcmVzZXQgPSB0aGlzLl9vblZpZXdQcmVSZXNldDtcblx0XHRyZXR1cm4gZXZlbnRzO1xuXHR9LFxuXG5cdF9vblZpZXdQcmVSZXNldDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIFNldCBhIGZsYWcgc28gdGhhdCBhIHZpZXdwcmVyZXNldCttb3ZlZW5kK3ZpZXdyZXNldCBvbmx5IHVwZGF0ZXMmcmVkcmF3cyBvbmNlXG5cdFx0dGhpcy5fcG9zdHBvbmVVcGRhdGVQYXRocyA9IHRydWU7XG5cdH0sXG5cblx0b25BZGQ6IGZ1bmN0aW9uICgpIHtcblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMpO1xuXG5cdFx0Ly8gUmVkcmF3IHZlY3RvcnMgc2luY2UgY2FudmFzIGlzIGNsZWFyZWQgdXBvbiByZW1vdmFsLFxuXHRcdC8vIGluIGNhc2Ugb2YgcmVtb3ZpbmcgdGhlIHJlbmRlcmVyIGl0c2VsZiBmcm9tIHRoZSBtYXAuXG5cdFx0dGhpcy5fZHJhdygpO1xuXHR9LFxuXG5cdF9pbml0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuXG5cdFx0TC5Eb21FdmVudFxuXHRcdFx0Lm9uKGNvbnRhaW5lciwgJ21vdXNlbW92ZScsIEwuVXRpbC50aHJvdHRsZSh0aGlzLl9vbk1vdXNlTW92ZSwgMzIsIHRoaXMpLCB0aGlzKVxuXHRcdFx0Lm9uKGNvbnRhaW5lciwgJ2NsaWNrIGRibGNsaWNrIG1vdXNlZG93biBtb3VzZXVwIGNvbnRleHRtZW51JywgdGhpcy5fb25DbGljaywgdGhpcylcblx0XHRcdC5vbihjb250YWluZXIsICdtb3VzZW91dCcsIHRoaXMuX2hhbmRsZU1vdXNlT3V0LCB0aGlzKTtcblxuXHRcdHRoaXMuX2N0eCA9IGNvbnRhaW5lci5nZXRDb250ZXh0KCcyZCcpO1xuXHR9LFxuXG5cdF91cGRhdGVQYXRoczogZnVuY3Rpb24gKCkge1xuXHRcdGlmICh0aGlzLl9wb3N0cG9uZVVwZGF0ZVBhdGhzKSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIGxheWVyO1xuXHRcdHRoaXMuX3JlZHJhd0JvdW5kcyA9IG51bGw7XG5cdFx0Zm9yICh2YXIgaWQgaW4gdGhpcy5fbGF5ZXJzKSB7XG5cdFx0XHRsYXllciA9IHRoaXMuX2xheWVyc1tpZF07XG5cdFx0XHRsYXllci5fdXBkYXRlKCk7XG5cdFx0fVxuXHRcdHRoaXMuX3JlZHJhdygpO1xuXHR9LFxuXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbWFwLl9hbmltYXRpbmdab29tICYmIHRoaXMuX2JvdW5kcykgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2RyYXduTGF5ZXJzID0ge307XG5cblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5fdXBkYXRlLmNhbGwodGhpcyk7XG5cblx0XHR2YXIgYiA9IHRoaXMuX2JvdW5kcyxcblx0XHQgICAgY29udGFpbmVyID0gdGhpcy5fY29udGFpbmVyLFxuXHRcdCAgICBzaXplID0gYi5nZXRTaXplKCksXG5cdFx0ICAgIG0gPSBMLkJyb3dzZXIucmV0aW5hID8gMiA6IDE7XG5cblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24oY29udGFpbmVyLCBiLm1pbik7XG5cblx0XHQvLyBzZXQgY2FudmFzIHNpemUgKGFsc28gY2xlYXJpbmcgaXQpOyB1c2UgZG91YmxlIHNpemUgb24gcmV0aW5hXG5cdFx0Y29udGFpbmVyLndpZHRoID0gbSAqIHNpemUueDtcblx0XHRjb250YWluZXIuaGVpZ2h0ID0gbSAqIHNpemUueTtcblx0XHRjb250YWluZXIuc3R5bGUud2lkdGggPSBzaXplLnggKyAncHgnO1xuXHRcdGNvbnRhaW5lci5zdHlsZS5oZWlnaHQgPSBzaXplLnkgKyAncHgnO1xuXG5cdFx0aWYgKEwuQnJvd3Nlci5yZXRpbmEpIHtcblx0XHRcdHRoaXMuX2N0eC5zY2FsZSgyLCAyKTtcblx0XHR9XG5cblx0XHQvLyB0cmFuc2xhdGUgc28gd2UgdXNlIHRoZSBzYW1lIHBhdGggY29vcmRpbmF0ZXMgYWZ0ZXIgY2FudmFzIGVsZW1lbnQgbW92ZXNcblx0XHR0aGlzLl9jdHgudHJhbnNsYXRlKC1iLm1pbi54LCAtYi5taW4ueSk7XG5cblx0XHQvLyBUZWxsIHBhdGhzIHRvIHJlZHJhdyB0aGVtc2VsdmVzXG5cdFx0dGhpcy5maXJlKCd1cGRhdGUnKTtcblx0fSxcblxuXHRfcmVzZXQ6IGZ1bmN0aW9uICgpIHtcblx0XHRMLlJlbmRlcmVyLnByb3RvdHlwZS5fcmVzZXQuY2FsbCh0aGlzKTtcblxuXHRcdGlmICh0aGlzLl9wb3N0cG9uZVVwZGF0ZVBhdGhzKSB7XG5cdFx0XHR0aGlzLl9wb3N0cG9uZVVwZGF0ZVBhdGhzID0gZmFsc2U7XG5cdFx0XHR0aGlzLl91cGRhdGVQYXRocygpO1xuXHRcdH1cblx0fSxcblxuXHRfaW5pdFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHRoaXMuX3VwZGF0ZURhc2hBcnJheShsYXllcik7XG5cdFx0dGhpcy5fbGF5ZXJzW0wuc3RhbXAobGF5ZXIpXSA9IGxheWVyO1xuXG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyID0ge1xuXHRcdFx0bGF5ZXI6IGxheWVyLFxuXHRcdFx0cHJldjogdGhpcy5fZHJhd0xhc3QsXG5cdFx0XHRuZXh0OiBudWxsXG5cdFx0fTtcblx0XHRpZiAodGhpcy5fZHJhd0xhc3QpIHsgdGhpcy5fZHJhd0xhc3QubmV4dCA9IG9yZGVyOyB9XG5cdFx0dGhpcy5fZHJhd0xhc3QgPSBvcmRlcjtcblx0XHR0aGlzLl9kcmF3Rmlyc3QgPSB0aGlzLl9kcmF3Rmlyc3QgfHwgdGhpcy5fZHJhd0xhc3Q7XG5cdH0sXG5cblx0X2FkZFBhdGg6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF9yZW1vdmVQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgb3JkZXIgPSBsYXllci5fb3JkZXI7XG5cdFx0dmFyIG5leHQgPSBvcmRlci5uZXh0O1xuXHRcdHZhciBwcmV2ID0gb3JkZXIucHJldjtcblxuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRuZXh0LnByZXYgPSBwcmV2O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9kcmF3TGFzdCA9IHByZXY7XG5cdFx0fVxuXHRcdGlmIChwcmV2KSB7XG5cdFx0XHRwcmV2Lm5leHQgPSBuZXh0O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9kcmF3Rmlyc3QgPSBuZXh0O1xuXHRcdH1cblxuXHRcdGRlbGV0ZSBsYXllci5fb3JkZXI7XG5cblx0XHRkZWxldGUgdGhpcy5fbGF5ZXJzW0wuc3RhbXAobGF5ZXIpXTtcblxuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF91cGRhdGVQYXRoOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHQvLyBSZWRyYXcgdGhlIHVuaW9uIG9mIHRoZSBsYXllcidzIG9sZCBwaXhlbFxuXHRcdC8vIGJvdW5kcyBhbmQgdGhlIG5ldyBwaXhlbCBib3VuZHMuXG5cdFx0dGhpcy5fZXh0ZW5kUmVkcmF3Qm91bmRzKGxheWVyKTtcblx0XHRsYXllci5fcHJvamVjdCgpO1xuXHRcdGxheWVyLl91cGRhdGUoKTtcblx0XHQvLyBUaGUgcmVkcmF3IHdpbGwgZXh0ZW5kIHRoZSByZWRyYXcgYm91bmRzXG5cdFx0Ly8gd2l0aCB0aGUgbmV3IHBpeGVsIGJvdW5kcy5cblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fSxcblxuXHRfdXBkYXRlU3R5bGU6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHRoaXMuX3VwZGF0ZURhc2hBcnJheShsYXllcik7XG5cdFx0dGhpcy5fcmVxdWVzdFJlZHJhdyhsYXllcik7XG5cdH0sXG5cblx0X3VwZGF0ZURhc2hBcnJheTogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0aWYgKGxheWVyLm9wdGlvbnMuZGFzaEFycmF5KSB7XG5cdFx0XHR2YXIgcGFydHMgPSBsYXllci5vcHRpb25zLmRhc2hBcnJheS5zcGxpdCgnLCcpLFxuXHRcdFx0ICAgIGRhc2hBcnJheSA9IFtdLFxuXHRcdFx0ICAgIGk7XG5cdFx0XHRmb3IgKGkgPSAwOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcblx0XHRcdFx0ZGFzaEFycmF5LnB1c2goTnVtYmVyKHBhcnRzW2ldKSk7XG5cdFx0XHR9XG5cdFx0XHRsYXllci5vcHRpb25zLl9kYXNoQXJyYXkgPSBkYXNoQXJyYXk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZXF1ZXN0UmVkcmF3OiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHRpZiAoIXRoaXMuX21hcCkgeyByZXR1cm47IH1cblxuXHRcdHRoaXMuX2V4dGVuZFJlZHJhd0JvdW5kcyhsYXllcik7XG5cdFx0dGhpcy5fcmVkcmF3UmVxdWVzdCA9IHRoaXMuX3JlZHJhd1JlcXVlc3QgfHwgTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUodGhpcy5fcmVkcmF3LCB0aGlzKTtcblx0fSxcblxuXHRfZXh0ZW5kUmVkcmF3Qm91bmRzOiBmdW5jdGlvbiAobGF5ZXIpIHtcblx0XHR2YXIgcGFkZGluZyA9IChsYXllci5vcHRpb25zLndlaWdodCB8fCAwKSArIDE7XG5cdFx0dGhpcy5fcmVkcmF3Qm91bmRzID0gdGhpcy5fcmVkcmF3Qm91bmRzIHx8IG5ldyBMLkJvdW5kcygpO1xuXHRcdHRoaXMuX3JlZHJhd0JvdW5kcy5leHRlbmQobGF5ZXIuX3B4Qm91bmRzLm1pbi5zdWJ0cmFjdChbcGFkZGluZywgcGFkZGluZ10pKTtcblx0XHR0aGlzLl9yZWRyYXdCb3VuZHMuZXh0ZW5kKGxheWVyLl9weEJvdW5kcy5tYXguYWRkKFtwYWRkaW5nLCBwYWRkaW5nXSkpO1xuXHR9LFxuXG5cdF9yZWRyYXc6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9yZWRyYXdSZXF1ZXN0ID0gbnVsbDtcblxuXHRcdGlmICh0aGlzLl9yZWRyYXdCb3VuZHMpIHtcblx0XHRcdHRoaXMuX3JlZHJhd0JvdW5kcy5taW4uX2Zsb29yKCk7XG5cdFx0XHR0aGlzLl9yZWRyYXdCb3VuZHMubWF4Ll9jZWlsKCk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fY2xlYXIoKTsgLy8gY2xlYXIgbGF5ZXJzIGluIHJlZHJhdyBib3VuZHNcblx0XHR0aGlzLl9kcmF3KCk7IC8vIGRyYXcgbGF5ZXJzXG5cblx0XHR0aGlzLl9yZWRyYXdCb3VuZHMgPSBudWxsO1xuXHR9LFxuXG5cdF9jbGVhcjogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBib3VuZHMgPSB0aGlzLl9yZWRyYXdCb3VuZHM7XG5cdFx0aWYgKGJvdW5kcykge1xuXHRcdFx0dmFyIHNpemUgPSBib3VuZHMuZ2V0U2l6ZSgpO1xuXHRcdFx0dGhpcy5fY3R4LmNsZWFyUmVjdChib3VuZHMubWluLngsIGJvdW5kcy5taW4ueSwgc2l6ZS54LCBzaXplLnkpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9jdHguY2xlYXJSZWN0KDAsIDAsIHRoaXMuX2NvbnRhaW5lci53aWR0aCwgdGhpcy5fY29udGFpbmVyLmhlaWdodCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9kcmF3OiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIGxheWVyLCBib3VuZHMgPSB0aGlzLl9yZWRyYXdCb3VuZHM7XG5cdFx0dGhpcy5fY3R4LnNhdmUoKTtcblx0XHRpZiAoYm91bmRzKSB7XG5cdFx0XHR2YXIgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XG5cdFx0XHR0aGlzLl9jdHguYmVnaW5QYXRoKCk7XG5cdFx0XHR0aGlzLl9jdHgucmVjdChib3VuZHMubWluLngsIGJvdW5kcy5taW4ueSwgc2l6ZS54LCBzaXplLnkpO1xuXHRcdFx0dGhpcy5fY3R4LmNsaXAoKTtcblx0XHR9XG5cblx0XHR0aGlzLl9kcmF3aW5nID0gdHJ1ZTtcblxuXHRcdGZvciAodmFyIG9yZGVyID0gdGhpcy5fZHJhd0ZpcnN0OyBvcmRlcjsgb3JkZXIgPSBvcmRlci5uZXh0KSB7XG5cdFx0XHRsYXllciA9IG9yZGVyLmxheWVyO1xuXHRcdFx0aWYgKCFib3VuZHMgfHwgKGxheWVyLl9weEJvdW5kcyAmJiBsYXllci5fcHhCb3VuZHMuaW50ZXJzZWN0cyhib3VuZHMpKSkge1xuXHRcdFx0XHRsYXllci5fdXBkYXRlUGF0aCgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuX2RyYXdpbmcgPSBmYWxzZTtcblxuXHRcdHRoaXMuX2N0eC5yZXN0b3JlKCk7ICAvLyBSZXN0b3JlIHN0YXRlIGJlZm9yZSBjbGlwcGluZy5cblx0fSxcblxuXHRfdXBkYXRlUG9seTogZnVuY3Rpb24gKGxheWVyLCBjbG9zZWQpIHtcblx0XHRpZiAoIXRoaXMuX2RyYXdpbmcpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgaSwgaiwgbGVuMiwgcCxcblx0XHQgICAgcGFydHMgPSBsYXllci5fcGFydHMsXG5cdFx0ICAgIGxlbiA9IHBhcnRzLmxlbmd0aCxcblx0XHQgICAgY3R4ID0gdGhpcy5fY3R4O1xuXG5cdFx0aWYgKCFsZW4pIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9kcmF3bkxheWVyc1tsYXllci5fbGVhZmxldF9pZF0gPSBsYXllcjtcblxuXHRcdGN0eC5iZWdpblBhdGgoKTtcblxuXHRcdGlmIChjdHguc2V0TGluZURhc2gpIHtcblx0XHRcdGN0eC5zZXRMaW5lRGFzaChsYXllci5vcHRpb25zICYmIGxheWVyLm9wdGlvbnMuX2Rhc2hBcnJheSB8fCBbXSk7XG5cdFx0fVxuXG5cdFx0Zm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRmb3IgKGogPSAwLCBsZW4yID0gcGFydHNbaV0ubGVuZ3RoOyBqIDwgbGVuMjsgaisrKSB7XG5cdFx0XHRcdHAgPSBwYXJ0c1tpXVtqXTtcblx0XHRcdFx0Y3R4W2ogPyAnbGluZVRvJyA6ICdtb3ZlVG8nXShwLngsIHAueSk7XG5cdFx0XHR9XG5cdFx0XHRpZiAoY2xvc2VkKSB7XG5cdFx0XHRcdGN0eC5jbG9zZVBhdGgoKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLl9maWxsU3Ryb2tlKGN0eCwgbGF5ZXIpO1xuXG5cdFx0Ly8gVE9ETyBvcHRpbWl6YXRpb246IDEgZmlsbC9zdHJva2UgZm9yIGFsbCBmZWF0dXJlcyB3aXRoIGVxdWFsIHN0eWxlIGluc3RlYWQgb2YgMSBmb3IgZWFjaCBmZWF0dXJlXG5cdH0sXG5cblx0X3VwZGF0ZUNpcmNsZTogZnVuY3Rpb24gKGxheWVyKSB7XG5cblx0XHRpZiAoIXRoaXMuX2RyYXdpbmcgfHwgbGF5ZXIuX2VtcHR5KCkpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgcCA9IGxheWVyLl9wb2ludCxcblx0XHQgICAgY3R4ID0gdGhpcy5fY3R4LFxuXHRcdCAgICByID0gbGF5ZXIuX3JhZGl1cyxcblx0XHQgICAgcyA9IChsYXllci5fcmFkaXVzWSB8fCByKSAvIHI7XG5cblx0XHR0aGlzLl9kcmF3bkxheWVyc1tsYXllci5fbGVhZmxldF9pZF0gPSBsYXllcjtcblxuXHRcdGlmIChzICE9PSAxKSB7XG5cdFx0XHRjdHguc2F2ZSgpO1xuXHRcdFx0Y3R4LnNjYWxlKDEsIHMpO1xuXHRcdH1cblxuXHRcdGN0eC5iZWdpblBhdGgoKTtcblx0XHRjdHguYXJjKHAueCwgcC55IC8gcywgciwgMCwgTWF0aC5QSSAqIDIsIGZhbHNlKTtcblxuXHRcdGlmIChzICE9PSAxKSB7XG5cdFx0XHRjdHgucmVzdG9yZSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2ZpbGxTdHJva2UoY3R4LCBsYXllcik7XG5cdH0sXG5cblx0X2ZpbGxTdHJva2U6IGZ1bmN0aW9uIChjdHgsIGxheWVyKSB7XG5cdFx0dmFyIG9wdGlvbnMgPSBsYXllci5vcHRpb25zO1xuXG5cdFx0aWYgKG9wdGlvbnMuZmlsbCkge1xuXHRcdFx0Y3R4Lmdsb2JhbEFscGhhID0gb3B0aW9ucy5maWxsT3BhY2l0eTtcblx0XHRcdGN0eC5maWxsU3R5bGUgPSBvcHRpb25zLmZpbGxDb2xvciB8fCBvcHRpb25zLmNvbG9yO1xuXHRcdFx0Y3R4LmZpbGwob3B0aW9ucy5maWxsUnVsZSB8fCAnZXZlbm9kZCcpO1xuXHRcdH1cblxuXHRcdGlmIChvcHRpb25zLnN0cm9rZSAmJiBvcHRpb25zLndlaWdodCAhPT0gMCkge1xuXHRcdFx0Y3R4Lmdsb2JhbEFscGhhID0gb3B0aW9ucy5vcGFjaXR5O1xuXHRcdFx0Y3R4LmxpbmVXaWR0aCA9IG9wdGlvbnMud2VpZ2h0O1xuXHRcdFx0Y3R4LnN0cm9rZVN0eWxlID0gb3B0aW9ucy5jb2xvcjtcblx0XHRcdGN0eC5saW5lQ2FwID0gb3B0aW9ucy5saW5lQ2FwO1xuXHRcdFx0Y3R4LmxpbmVKb2luID0gb3B0aW9ucy5saW5lSm9pbjtcblx0XHRcdGN0eC5zdHJva2UoKTtcblx0XHR9XG5cdH0sXG5cblx0Ly8gQ2FudmFzIG9idmlvdXNseSBkb2Vzbid0IGhhdmUgbW91c2UgZXZlbnRzIGZvciBpbmRpdmlkdWFsIGRyYXduIG9iamVjdHMsXG5cdC8vIHNvIHdlIGVtdWxhdGUgdGhhdCBieSBjYWxjdWxhdGluZyB3aGF0J3MgdW5kZXIgdGhlIG1vdXNlIG9uIG1vdXNlbW92ZS9jbGljayBtYW51YWxseVxuXG5cdF9vbkNsaWNrOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBwb2ludCA9IHRoaXMuX21hcC5tb3VzZUV2ZW50VG9MYXllclBvaW50KGUpLCBsYXllciwgY2xpY2tlZExheWVyO1xuXG5cdFx0Zm9yICh2YXIgb3JkZXIgPSB0aGlzLl9kcmF3Rmlyc3Q7IG9yZGVyOyBvcmRlciA9IG9yZGVyLm5leHQpIHtcblx0XHRcdGxheWVyID0gb3JkZXIubGF5ZXI7XG5cdFx0XHRpZiAobGF5ZXIub3B0aW9ucy5pbnRlcmFjdGl2ZSAmJiBsYXllci5fY29udGFpbnNQb2ludChwb2ludCkgJiYgIXRoaXMuX21hcC5fZHJhZ2dhYmxlTW92ZWQobGF5ZXIpKSB7XG5cdFx0XHRcdGNsaWNrZWRMYXllciA9IGxheWVyO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAoY2xpY2tlZExheWVyKSAge1xuXHRcdFx0TC5Eb21FdmVudC5fZmFrZVN0b3AoZSk7XG5cdFx0XHR0aGlzLl9maXJlRXZlbnQoW2NsaWNrZWRMYXllcl0sIGUpO1xuXHRcdH1cblx0fSxcblxuXHRfb25Nb3VzZU1vdmU6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKCF0aGlzLl9tYXAgfHwgdGhpcy5fbWFwLmRyYWdnaW5nLm1vdmluZygpIHx8IHRoaXMuX21hcC5fYW5pbWF0aW5nWm9vbSkgeyByZXR1cm47IH1cblxuXHRcdHZhciBwb2ludCA9IHRoaXMuX21hcC5tb3VzZUV2ZW50VG9MYXllclBvaW50KGUpO1xuXHRcdHRoaXMuX2hhbmRsZU1vdXNlSG92ZXIoZSwgcG9pbnQpO1xuXHR9LFxuXG5cblx0X2hhbmRsZU1vdXNlT3V0OiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBsYXllciA9IHRoaXMuX2hvdmVyZWRMYXllcjtcblx0XHRpZiAobGF5ZXIpIHtcblx0XHRcdC8vIGlmIHdlJ3JlIGxlYXZpbmcgdGhlIGxheWVyLCBmaXJlIG1vdXNlb3V0XG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1pbnRlcmFjdGl2ZScpO1xuXHRcdFx0dGhpcy5fZmlyZUV2ZW50KFtsYXllcl0sIGUsICdtb3VzZW91dCcpO1xuXHRcdFx0dGhpcy5faG92ZXJlZExheWVyID0gbnVsbDtcblx0XHR9XG5cdH0sXG5cblx0X2hhbmRsZU1vdXNlSG92ZXI6IGZ1bmN0aW9uIChlLCBwb2ludCkge1xuXHRcdHZhciBsYXllciwgY2FuZGlkYXRlSG92ZXJlZExheWVyO1xuXG5cdFx0Zm9yICh2YXIgb3JkZXIgPSB0aGlzLl9kcmF3Rmlyc3Q7IG9yZGVyOyBvcmRlciA9IG9yZGVyLm5leHQpIHtcblx0XHRcdGxheWVyID0gb3JkZXIubGF5ZXI7XG5cdFx0XHRpZiAobGF5ZXIub3B0aW9ucy5pbnRlcmFjdGl2ZSAmJiBsYXllci5fY29udGFpbnNQb2ludChwb2ludCkpIHtcblx0XHRcdFx0Y2FuZGlkYXRlSG92ZXJlZExheWVyID0gbGF5ZXI7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKGNhbmRpZGF0ZUhvdmVyZWRMYXllciAhPT0gdGhpcy5faG92ZXJlZExheWVyKSB7XG5cdFx0XHR0aGlzLl9oYW5kbGVNb3VzZU91dChlKTtcblxuXHRcdFx0aWYgKGNhbmRpZGF0ZUhvdmVyZWRMYXllcikge1xuXHRcdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fY29udGFpbmVyLCAnbGVhZmxldC1pbnRlcmFjdGl2ZScpOyAvLyBjaGFuZ2UgY3Vyc29yXG5cdFx0XHRcdHRoaXMuX2ZpcmVFdmVudChbY2FuZGlkYXRlSG92ZXJlZExheWVyXSwgZSwgJ21vdXNlb3ZlcicpO1xuXHRcdFx0XHR0aGlzLl9ob3ZlcmVkTGF5ZXIgPSBjYW5kaWRhdGVIb3ZlcmVkTGF5ZXI7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMuX2hvdmVyZWRMYXllcikge1xuXHRcdFx0dGhpcy5fZmlyZUV2ZW50KFt0aGlzLl9ob3ZlcmVkTGF5ZXJdLCBlKTtcblx0XHR9XG5cdH0sXG5cblx0X2ZpcmVFdmVudDogZnVuY3Rpb24gKGxheWVycywgZSwgdHlwZSkge1xuXHRcdHRoaXMuX21hcC5fZmlyZURPTUV2ZW50KGUsIHR5cGUgfHwgZS50eXBlLCBsYXllcnMpO1xuXHR9LFxuXG5cdF9icmluZ1RvRnJvbnQ6IGZ1bmN0aW9uIChsYXllcikge1xuXHRcdHZhciBvcmRlciA9IGxheWVyLl9vcmRlcjtcblx0XHR2YXIgbmV4dCA9IG9yZGVyLm5leHQ7XG5cdFx0dmFyIHByZXYgPSBvcmRlci5wcmV2O1xuXG5cdFx0aWYgKG5leHQpIHtcblx0XHRcdG5leHQucHJldiA9IHByZXY7XG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIEFscmVhZHkgbGFzdFxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRpZiAocHJldikge1xuXHRcdFx0cHJldi5uZXh0ID0gbmV4dDtcblx0XHR9IGVsc2UgaWYgKG5leHQpIHtcblx0XHRcdC8vIFVwZGF0ZSBmaXJzdCBlbnRyeSB1bmxlc3MgdGhpcyBpcyB0aGVcblx0XHRcdC8vIHNpZ25sZSBlbnRyeVxuXHRcdFx0dGhpcy5fZHJhd0ZpcnN0ID0gbmV4dDtcblx0XHR9XG5cblx0XHRvcmRlci5wcmV2ID0gdGhpcy5fZHJhd0xhc3Q7XG5cdFx0dGhpcy5fZHJhd0xhc3QubmV4dCA9IG9yZGVyO1xuXG5cdFx0b3JkZXIubmV4dCA9IG51bGw7XG5cdFx0dGhpcy5fZHJhd0xhc3QgPSBvcmRlcjtcblxuXHRcdHRoaXMuX3JlcXVlc3RSZWRyYXcobGF5ZXIpO1xuXHR9LFxuXG5cdF9icmluZ1RvQmFjazogZnVuY3Rpb24gKGxheWVyKSB7XG5cdFx0dmFyIG9yZGVyID0gbGF5ZXIuX29yZGVyO1xuXHRcdHZhciBuZXh0ID0gb3JkZXIubmV4dDtcblx0XHR2YXIgcHJldiA9IG9yZGVyLnByZXY7XG5cblx0XHRpZiAocHJldikge1xuXHRcdFx0cHJldi5uZXh0ID0gbmV4dDtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gQWxyZWFkeSBmaXJzdFxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRpZiAobmV4dCkge1xuXHRcdFx0bmV4dC5wcmV2ID0gcHJldjtcblx0XHR9IGVsc2UgaWYgKHByZXYpIHtcblx0XHRcdC8vIFVwZGF0ZSBsYXN0IGVudHJ5IHVubGVzcyB0aGlzIGlzIHRoZVxuXHRcdFx0Ly8gc2lnbmxlIGVudHJ5XG5cdFx0XHR0aGlzLl9kcmF3TGFzdCA9IHByZXY7XG5cdFx0fVxuXG5cdFx0b3JkZXIucHJldiA9IG51bGw7XG5cblx0XHRvcmRlci5uZXh0ID0gdGhpcy5fZHJhd0ZpcnN0O1xuXHRcdHRoaXMuX2RyYXdGaXJzdC5wcmV2ID0gb3JkZXI7XG5cdFx0dGhpcy5fZHJhd0ZpcnN0ID0gb3JkZXI7XG5cblx0XHR0aGlzLl9yZXF1ZXN0UmVkcmF3KGxheWVyKTtcblx0fVxufSk7XG5cbi8vIEBuYW1lc3BhY2UgQnJvd3NlcjsgQHByb3BlcnR5IGNhbnZhczogQm9vbGVhblxuLy8gYHRydWVgIHdoZW4gdGhlIGJyb3dzZXIgc3VwcG9ydHMgW2A8Y2FudmFzPmBdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0FQSS9DYW52YXNfQVBJKS5cbkwuQnJvd3Nlci5jYW52YXMgPSAoZnVuY3Rpb24gKCkge1xuXHRyZXR1cm4gISFkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKS5nZXRDb250ZXh0O1xufSgpKTtcblxuLy8gQG5hbWVzcGFjZSBDYW52YXNcbi8vIEBmYWN0b3J5IEwuY2FudmFzKG9wdGlvbnM/OiBSZW5kZXJlciBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhIENhbnZhcyByZW5kZXJlciB3aXRoIHRoZSBnaXZlbiBvcHRpb25zLlxuTC5jYW52YXMgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gTC5Ccm93c2VyLmNhbnZhcyA/IG5ldyBMLkNhbnZhcyhvcHRpb25zKSA6IG51bGw7XG59O1xuXG5MLlBvbHlsaW5lLnByb3RvdHlwZS5fY29udGFpbnNQb2ludCA9IGZ1bmN0aW9uIChwLCBjbG9zZWQpIHtcblx0dmFyIGksIGosIGssIGxlbiwgbGVuMiwgcGFydCxcblx0ICAgIHcgPSB0aGlzLl9jbGlja1RvbGVyYW5jZSgpO1xuXG5cdGlmICghdGhpcy5fcHhCb3VuZHMuY29udGFpbnMocCkpIHsgcmV0dXJuIGZhbHNlOyB9XG5cblx0Ly8gaGl0IGRldGVjdGlvbiBmb3IgcG9seWxpbmVzXG5cdGZvciAoaSA9IDAsIGxlbiA9IHRoaXMuX3BhcnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0cGFydCA9IHRoaXMuX3BhcnRzW2ldO1xuXG5cdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBhcnQubGVuZ3RoLCBrID0gbGVuMiAtIDE7IGogPCBsZW4yOyBrID0gaisrKSB7XG5cdFx0XHRpZiAoIWNsb3NlZCAmJiAoaiA9PT0gMCkpIHsgY29udGludWU7IH1cblxuXHRcdFx0aWYgKEwuTGluZVV0aWwucG9pbnRUb1NlZ21lbnREaXN0YW5jZShwLCBwYXJ0W2tdLCBwYXJ0W2pdKSA8PSB3KSB7XG5cdFx0XHRcdHJldHVybiB0cnVlO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXHRyZXR1cm4gZmFsc2U7XG59O1xuXG5MLlBvbHlnb24ucHJvdG90eXBlLl9jb250YWluc1BvaW50ID0gZnVuY3Rpb24gKHApIHtcblx0dmFyIGluc2lkZSA9IGZhbHNlLFxuXHQgICAgcGFydCwgcDEsIHAyLCBpLCBqLCBrLCBsZW4sIGxlbjI7XG5cblx0aWYgKCF0aGlzLl9weEJvdW5kcy5jb250YWlucyhwKSkgeyByZXR1cm4gZmFsc2U7IH1cblxuXHQvLyByYXkgY2FzdGluZyBhbGdvcml0aG0gZm9yIGRldGVjdGluZyBpZiBwb2ludCBpcyBpbiBwb2x5Z29uXG5cdGZvciAoaSA9IDAsIGxlbiA9IHRoaXMuX3BhcnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0cGFydCA9IHRoaXMuX3BhcnRzW2ldO1xuXG5cdFx0Zm9yIChqID0gMCwgbGVuMiA9IHBhcnQubGVuZ3RoLCBrID0gbGVuMiAtIDE7IGogPCBsZW4yOyBrID0gaisrKSB7XG5cdFx0XHRwMSA9IHBhcnRbal07XG5cdFx0XHRwMiA9IHBhcnRba107XG5cblx0XHRcdGlmICgoKHAxLnkgPiBwLnkpICE9PSAocDIueSA+IHAueSkpICYmIChwLnggPCAocDIueCAtIHAxLngpICogKHAueSAtIHAxLnkpIC8gKHAyLnkgLSBwMS55KSArIHAxLngpKSB7XG5cdFx0XHRcdGluc2lkZSA9ICFpbnNpZGU7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0Ly8gYWxzbyBjaGVjayBpZiBpdCdzIG9uIHBvbHlnb24gc3Ryb2tlXG5cdHJldHVybiBpbnNpZGUgfHwgTC5Qb2x5bGluZS5wcm90b3R5cGUuX2NvbnRhaW5zUG9pbnQuY2FsbCh0aGlzLCBwLCB0cnVlKTtcbn07XG5cbkwuQ2lyY2xlTWFya2VyLnByb3RvdHlwZS5fY29udGFpbnNQb2ludCA9IGZ1bmN0aW9uIChwKSB7XG5cdHJldHVybiBwLmRpc3RhbmNlVG8odGhpcy5fcG9pbnQpIDw9IHRoaXMuX3JhZGl1cyArIHRoaXMuX2NsaWNrVG9sZXJhbmNlKCk7XG59O1xuXG5cblxuLypcclxuICogQGNsYXNzIEdlb0pTT05cclxuICogQGFrYSBMLkdlb0pTT05cclxuICogQGluaGVyaXRzIEZlYXR1cmVHcm91cFxyXG4gKlxyXG4gKiBSZXByZXNlbnRzIGEgR2VvSlNPTiBvYmplY3Qgb3IgYW4gYXJyYXkgb2YgR2VvSlNPTiBvYmplY3RzLiBBbGxvd3MgeW91IHRvIHBhcnNlXHJcbiAqIEdlb0pTT04gZGF0YSBhbmQgZGlzcGxheSBpdCBvbiB0aGUgbWFwLiBFeHRlbmRzIGBGZWF0dXJlR3JvdXBgLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKlxyXG4gKiBgYGBqc1xyXG4gKiBMLmdlb0pTT04oZGF0YSwge1xyXG4gKiBcdHN0eWxlOiBmdW5jdGlvbiAoZmVhdHVyZSkge1xyXG4gKiBcdFx0cmV0dXJuIHtjb2xvcjogZmVhdHVyZS5wcm9wZXJ0aWVzLmNvbG9yfTtcclxuICogXHR9XHJcbiAqIH0pLmJpbmRQb3B1cChmdW5jdGlvbiAobGF5ZXIpIHtcclxuICogXHRyZXR1cm4gbGF5ZXIuZmVhdHVyZS5wcm9wZXJ0aWVzLmRlc2NyaXB0aW9uO1xyXG4gKiB9KS5hZGRUbyhtYXApO1xyXG4gKiBgYGBcclxuICovXHJcblxyXG5MLkdlb0pTT04gPSBMLkZlYXR1cmVHcm91cC5leHRlbmQoe1xyXG5cclxuXHQvKiBAc2VjdGlvblxyXG5cdCAqIEBha2EgR2VvSlNPTiBvcHRpb25zXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHBvaW50VG9MYXllcjogRnVuY3Rpb24gPSAqXHJcblx0ICogQSBgRnVuY3Rpb25gIGRlZmluaW5nIGhvdyBHZW9KU09OIHBvaW50cyBzcGF3biBMZWFmbGV0IGxheWVycy4gSXQgaXMgaW50ZXJuYWxseVxyXG5cdCAqIGNhbGxlZCB3aGVuIGRhdGEgaXMgYWRkZWQsIHBhc3NpbmcgdGhlIEdlb0pTT04gcG9pbnQgZmVhdHVyZSBhbmQgaXRzIGBMYXRMbmdgLlxyXG5cdCAqIFRoZSBkZWZhdWx0IGlzIHRvIHNwYXduIGEgZGVmYXVsdCBgTWFya2VyYDpcclxuXHQgKiBgYGBqc1xyXG5cdCAqIGZ1bmN0aW9uKGdlb0pzb25Qb2ludCwgbGF0bG5nKSB7XHJcblx0ICogXHRyZXR1cm4gTC5tYXJrZXIobGF0bG5nKTtcclxuXHQgKiB9XHJcblx0ICogYGBgXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIHN0eWxlOiBGdW5jdGlvbiA9ICpcclxuXHQgKiBBIGBGdW5jdGlvbmAgZGVmaW5pbmcgdGhlIGBQYXRoIG9wdGlvbnNgIGZvciBzdHlsaW5nIEdlb0pTT04gbGluZXMgYW5kIHBvbHlnb25zLFxyXG5cdCAqIGNhbGxlZCBpbnRlcm5hbGx5IHdoZW4gZGF0YSBpcyBhZGRlZC5cclxuXHQgKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0byBub3Qgb3ZlcnJpZGUgYW55IGRlZmF1bHRzOlxyXG5cdCAqIGBgYGpzXHJcblx0ICogZnVuY3Rpb24gKGdlb0pzb25GZWF0dXJlKSB7XHJcblx0ICogXHRyZXR1cm4ge31cclxuXHQgKiB9XHJcblx0ICogYGBgXHJcblx0ICpcclxuXHQgKiBAb3B0aW9uIG9uRWFjaEZlYXR1cmU6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCB0aGF0IHdpbGwgYmUgY2FsbGVkIG9uY2UgZm9yIGVhY2ggY3JlYXRlZCBgRmVhdHVyZWAsIGFmdGVyIGl0IGhhc1xyXG5cdCAqIGJlZW4gY3JlYXRlZCBhbmQgc3R5bGVkLiBVc2VmdWwgZm9yIGF0dGFjaGluZyBldmVudHMgYW5kIHBvcHVwcyB0byBmZWF0dXJlcy5cclxuXHQgKiBUaGUgZGVmYXVsdCBpcyB0byBkbyBub3RoaW5nIHdpdGggdGhlIG5ld2x5IGNyZWF0ZWQgbGF5ZXJzOlxyXG5cdCAqIGBgYGpzXHJcblx0ICogZnVuY3Rpb24gKGZlYXR1cmUsIGxheWVyKSB7fVxyXG5cdCAqIGBgYFxyXG5cdCAqXHJcblx0ICogQG9wdGlvbiBmaWx0ZXI6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCB0aGF0IHdpbGwgYmUgdXNlZCB0byBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIGEgZmVhdHVyZSBvciBub3QuXHJcblx0ICogVGhlIGRlZmF1bHQgaXMgdG8gaW5jbHVkZSBhbGwgZmVhdHVyZXM6XHJcblx0ICogYGBganNcclxuXHQgKiBmdW5jdGlvbiAoZ2VvSnNvbkZlYXR1cmUpIHtcclxuXHQgKiBcdHJldHVybiB0cnVlO1xyXG5cdCAqIH1cclxuXHQgKiBgYGBcclxuXHQgKiBOb3RlOiBkeW5hbWljYWxseSBjaGFuZ2luZyB0aGUgYGZpbHRlcmAgb3B0aW9uIHdpbGwgaGF2ZSBlZmZlY3Qgb25seSBvbiBuZXdseVxyXG5cdCAqIGFkZGVkIGRhdGEuIEl0IHdpbGwgX25vdF8gcmUtZXZhbHVhdGUgYWxyZWFkeSBpbmNsdWRlZCBmZWF0dXJlcy5cclxuXHQgKlxyXG5cdCAqIEBvcHRpb24gY29vcmRzVG9MYXRMbmc6IEZ1bmN0aW9uID0gKlxyXG5cdCAqIEEgYEZ1bmN0aW9uYCB0aGF0IHdpbGwgYmUgdXNlZCBmb3IgY29udmVydGluZyBHZW9KU09OIGNvb3JkaW5hdGVzIHRvIGBMYXRMbmdgcy5cclxuXHQgKiBUaGUgZGVmYXVsdCBpcyB0aGUgYGNvb3Jkc1RvTGF0TG5nYCBzdGF0aWMgbWV0aG9kLlxyXG5cdCAqL1xyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAoZ2VvanNvbiwgb3B0aW9ucykge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX2xheWVycyA9IHt9O1xyXG5cclxuXHRcdGlmIChnZW9qc29uKSB7XHJcblx0XHRcdHRoaXMuYWRkRGF0YShnZW9qc29uKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZERhdGEoIDxHZW9KU09OPiBkYXRhICk6IHRoaXNcclxuXHQvLyBBZGRzIGEgR2VvSlNPTiBvYmplY3QgdG8gdGhlIGxheWVyLlxyXG5cdGFkZERhdGE6IGZ1bmN0aW9uIChnZW9qc29uKSB7XHJcblx0XHR2YXIgZmVhdHVyZXMgPSBMLlV0aWwuaXNBcnJheShnZW9qc29uKSA/IGdlb2pzb24gOiBnZW9qc29uLmZlYXR1cmVzLFxyXG5cdFx0ICAgIGksIGxlbiwgZmVhdHVyZTtcclxuXHJcblx0XHRpZiAoZmVhdHVyZXMpIHtcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gZmVhdHVyZXMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuXHRcdFx0XHQvLyBvbmx5IGFkZCB0aGlzIGlmIGdlb21ldHJ5IG9yIGdlb21ldHJpZXMgYXJlIHNldCBhbmQgbm90IG51bGxcclxuXHRcdFx0XHRmZWF0dXJlID0gZmVhdHVyZXNbaV07XHJcblx0XHRcdFx0aWYgKGZlYXR1cmUuZ2VvbWV0cmllcyB8fCBmZWF0dXJlLmdlb21ldHJ5IHx8IGZlYXR1cmUuZmVhdHVyZXMgfHwgZmVhdHVyZS5jb29yZGluYXRlcykge1xyXG5cdFx0XHRcdFx0dGhpcy5hZGREYXRhKGZlYXR1cmUpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gdGhpcztcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcclxuXHJcblx0XHRpZiAob3B0aW9ucy5maWx0ZXIgJiYgIW9wdGlvbnMuZmlsdGVyKGdlb2pzb24pKSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0dmFyIGxheWVyID0gTC5HZW9KU09OLmdlb21ldHJ5VG9MYXllcihnZW9qc29uLCBvcHRpb25zKTtcclxuXHRcdGlmICghbGF5ZXIpIHtcclxuXHRcdFx0cmV0dXJuIHRoaXM7XHJcblx0XHR9XHJcblx0XHRsYXllci5mZWF0dXJlID0gTC5HZW9KU09OLmFzRmVhdHVyZShnZW9qc29uKTtcclxuXHJcblx0XHRsYXllci5kZWZhdWx0T3B0aW9ucyA9IGxheWVyLm9wdGlvbnM7XHJcblx0XHR0aGlzLnJlc2V0U3R5bGUobGF5ZXIpO1xyXG5cclxuXHRcdGlmIChvcHRpb25zLm9uRWFjaEZlYXR1cmUpIHtcclxuXHRcdFx0b3B0aW9ucy5vbkVhY2hGZWF0dXJlKGdlb2pzb24sIGxheWVyKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gdGhpcy5hZGRMYXllcihsYXllcik7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZXNldFN0eWxlKCA8UGF0aD4gbGF5ZXIgKTogdGhpc1xyXG5cdC8vIFJlc2V0cyB0aGUgZ2l2ZW4gdmVjdG9yIGxheWVyJ3Mgc3R5bGUgdG8gdGhlIG9yaWdpbmFsIEdlb0pTT04gc3R5bGUsIHVzZWZ1bCBmb3IgcmVzZXR0aW5nIHN0eWxlIGFmdGVyIGhvdmVyIGV2ZW50cy5cclxuXHRyZXNldFN0eWxlOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdC8vIHJlc2V0IGFueSBjdXN0b20gc3R5bGVzXHJcblx0XHRsYXllci5vcHRpb25zID0gTC5VdGlsLmV4dGVuZCh7fSwgbGF5ZXIuZGVmYXVsdE9wdGlvbnMpO1xyXG5cdFx0dGhpcy5fc2V0TGF5ZXJTdHlsZShsYXllciwgdGhpcy5vcHRpb25zLnN0eWxlKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0U3R5bGUoIDxGdW5jdGlvbj4gc3R5bGUgKTogdGhpc1xyXG5cdC8vIENoYW5nZXMgc3R5bGVzIG9mIEdlb0pTT04gdmVjdG9yIGxheWVycyB3aXRoIHRoZSBnaXZlbiBzdHlsZSBmdW5jdGlvbi5cclxuXHRzZXRTdHlsZTogZnVuY3Rpb24gKHN0eWxlKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5lYWNoTGF5ZXIoZnVuY3Rpb24gKGxheWVyKSB7XHJcblx0XHRcdHRoaXMuX3NldExheWVyU3R5bGUobGF5ZXIsIHN0eWxlKTtcclxuXHRcdH0sIHRoaXMpO1xyXG5cdH0sXHJcblxyXG5cdF9zZXRMYXllclN0eWxlOiBmdW5jdGlvbiAobGF5ZXIsIHN0eWxlKSB7XHJcblx0XHRpZiAodHlwZW9mIHN0eWxlID09PSAnZnVuY3Rpb24nKSB7XHJcblx0XHRcdHN0eWxlID0gc3R5bGUobGF5ZXIuZmVhdHVyZSk7XHJcblx0XHR9XHJcblx0XHRpZiAobGF5ZXIuc2V0U3R5bGUpIHtcclxuXHRcdFx0bGF5ZXIuc2V0U3R5bGUoc3R5bGUpO1xyXG5cdFx0fVxyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAc2VjdGlvblxyXG4vLyBUaGVyZSBhcmUgc2V2ZXJhbCBzdGF0aWMgZnVuY3Rpb25zIHdoaWNoIGNhbiBiZSBjYWxsZWQgd2l0aG91dCBpbnN0YW50aWF0aW5nIEwuR2VvSlNPTjpcclxuTC5leHRlbmQoTC5HZW9KU09OLCB7XHJcblx0Ly8gQGZ1bmN0aW9uIGdlb21ldHJ5VG9MYXllcihmZWF0dXJlRGF0YTogT2JqZWN0LCBvcHRpb25zPzogR2VvSlNPTiBvcHRpb25zKTogTGF5ZXJcclxuXHQvLyBDcmVhdGVzIGEgYExheWVyYCBmcm9tIGEgZ2l2ZW4gR2VvSlNPTiBmZWF0dXJlLiBDYW4gdXNlIGEgY3VzdG9tXHJcblx0Ly8gW2Bwb2ludFRvTGF5ZXJgXSgjZ2VvanNvbi1wb2ludHRvbGF5ZXIpIGFuZC9vciBbYGNvb3Jkc1RvTGF0TG5nYF0oI2dlb2pzb24tY29vcmRzdG9sYXRsbmcpXHJcblx0Ly8gZnVuY3Rpb25zIGlmIHByb3ZpZGVkIGFzIG9wdGlvbnMuXHJcblx0Z2VvbWV0cnlUb0xheWVyOiBmdW5jdGlvbiAoZ2VvanNvbiwgb3B0aW9ucykge1xyXG5cclxuXHRcdHZhciBnZW9tZXRyeSA9IGdlb2pzb24udHlwZSA9PT0gJ0ZlYXR1cmUnID8gZ2VvanNvbi5nZW9tZXRyeSA6IGdlb2pzb24sXHJcblx0XHQgICAgY29vcmRzID0gZ2VvbWV0cnkgPyBnZW9tZXRyeS5jb29yZGluYXRlcyA6IG51bGwsXHJcblx0XHQgICAgbGF5ZXJzID0gW10sXHJcblx0XHQgICAgcG9pbnRUb0xheWVyID0gb3B0aW9ucyAmJiBvcHRpb25zLnBvaW50VG9MYXllcixcclxuXHRcdCAgICBjb29yZHNUb0xhdExuZyA9IG9wdGlvbnMgJiYgb3B0aW9ucy5jb29yZHNUb0xhdExuZyB8fCB0aGlzLmNvb3Jkc1RvTGF0TG5nLFxyXG5cdFx0ICAgIGxhdGxuZywgbGF0bG5ncywgaSwgbGVuO1xyXG5cclxuXHRcdGlmICghY29vcmRzICYmICFnZW9tZXRyeSkge1xyXG5cdFx0XHRyZXR1cm4gbnVsbDtcclxuXHRcdH1cclxuXHJcblx0XHRzd2l0Y2ggKGdlb21ldHJ5LnR5cGUpIHtcclxuXHRcdGNhc2UgJ1BvaW50JzpcclxuXHRcdFx0bGF0bG5nID0gY29vcmRzVG9MYXRMbmcoY29vcmRzKTtcclxuXHRcdFx0cmV0dXJuIHBvaW50VG9MYXllciA/IHBvaW50VG9MYXllcihnZW9qc29uLCBsYXRsbmcpIDogbmV3IEwuTWFya2VyKGxhdGxuZyk7XHJcblxyXG5cdFx0Y2FzZSAnTXVsdGlQb2ludCc6XHJcblx0XHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvb3Jkcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdGxhdGxuZyA9IGNvb3Jkc1RvTGF0TG5nKGNvb3Jkc1tpXSk7XHJcblx0XHRcdFx0bGF5ZXJzLnB1c2gocG9pbnRUb0xheWVyID8gcG9pbnRUb0xheWVyKGdlb2pzb24sIGxhdGxuZykgOiBuZXcgTC5NYXJrZXIobGF0bG5nKSk7XHJcblx0XHRcdH1cclxuXHRcdFx0cmV0dXJuIG5ldyBMLkZlYXR1cmVHcm91cChsYXllcnMpO1xyXG5cclxuXHRcdGNhc2UgJ0xpbmVTdHJpbmcnOlxyXG5cdFx0Y2FzZSAnTXVsdGlMaW5lU3RyaW5nJzpcclxuXHRcdFx0bGF0bG5ncyA9IHRoaXMuY29vcmRzVG9MYXRMbmdzKGNvb3JkcywgZ2VvbWV0cnkudHlwZSA9PT0gJ0xpbmVTdHJpbmcnID8gMCA6IDEsIGNvb3Jkc1RvTGF0TG5nKTtcclxuXHRcdFx0cmV0dXJuIG5ldyBMLlBvbHlsaW5lKGxhdGxuZ3MsIG9wdGlvbnMpO1xyXG5cclxuXHRcdGNhc2UgJ1BvbHlnb24nOlxyXG5cdFx0Y2FzZSAnTXVsdGlQb2x5Z29uJzpcclxuXHRcdFx0bGF0bG5ncyA9IHRoaXMuY29vcmRzVG9MYXRMbmdzKGNvb3JkcywgZ2VvbWV0cnkudHlwZSA9PT0gJ1BvbHlnb24nID8gMSA6IDIsIGNvb3Jkc1RvTGF0TG5nKTtcclxuXHRcdFx0cmV0dXJuIG5ldyBMLlBvbHlnb24obGF0bG5ncywgb3B0aW9ucyk7XHJcblxyXG5cdFx0Y2FzZSAnR2VvbWV0cnlDb2xsZWN0aW9uJzpcclxuXHRcdFx0Zm9yIChpID0gMCwgbGVuID0gZ2VvbWV0cnkuZ2VvbWV0cmllcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRcdHZhciBsYXllciA9IHRoaXMuZ2VvbWV0cnlUb0xheWVyKHtcclxuXHRcdFx0XHRcdGdlb21ldHJ5OiBnZW9tZXRyeS5nZW9tZXRyaWVzW2ldLFxyXG5cdFx0XHRcdFx0dHlwZTogJ0ZlYXR1cmUnLFxyXG5cdFx0XHRcdFx0cHJvcGVydGllczogZ2VvanNvbi5wcm9wZXJ0aWVzXHJcblx0XHRcdFx0fSwgb3B0aW9ucyk7XHJcblxyXG5cdFx0XHRcdGlmIChsYXllcikge1xyXG5cdFx0XHRcdFx0bGF5ZXJzLnB1c2gobGF5ZXIpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0fVxyXG5cdFx0XHRyZXR1cm4gbmV3IEwuRmVhdHVyZUdyb3VwKGxheWVycyk7XHJcblxyXG5cdFx0ZGVmYXVsdDpcclxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIEdlb0pTT04gb2JqZWN0LicpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdC8vIEBmdW5jdGlvbiBjb29yZHNUb0xhdExuZyhjb29yZHM6IEFycmF5KTogTGF0TG5nXHJcblx0Ly8gQ3JlYXRlcyBhIGBMYXRMbmdgIG9iamVjdCBmcm9tIGFuIGFycmF5IG9mIDIgbnVtYmVycyAobG9uZ2l0dWRlLCBsYXRpdHVkZSlcclxuXHQvLyBvciAzIG51bWJlcnMgKGxvbmdpdHVkZSwgbGF0aXR1ZGUsIGFsdGl0dWRlKSB1c2VkIGluIEdlb0pTT04gZm9yIHBvaW50cy5cclxuXHRjb29yZHNUb0xhdExuZzogZnVuY3Rpb24gKGNvb3Jkcykge1xyXG5cdFx0cmV0dXJuIG5ldyBMLkxhdExuZyhjb29yZHNbMV0sIGNvb3Jkc1swXSwgY29vcmRzWzJdKTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gY29vcmRzVG9MYXRMbmdzKGNvb3JkczogQXJyYXksIGxldmVsc0RlZXA/OiBOdW1iZXIsIGNvb3Jkc1RvTGF0TG5nPzogRnVuY3Rpb24pOiBBcnJheVxyXG5cdC8vIENyZWF0ZXMgYSBtdWx0aWRpbWVuc2lvbmFsIGFycmF5IG9mIGBMYXRMbmdgcyBmcm9tIGEgR2VvSlNPTiBjb29yZGluYXRlcyBhcnJheS5cclxuXHQvLyBgbGV2ZWxzRGVlcGAgc3BlY2lmaWVzIHRoZSBuZXN0aW5nIGxldmVsICgwIGlzIGZvciBhbiBhcnJheSBvZiBwb2ludHMsIDEgZm9yIGFuIGFycmF5IG9mIGFycmF5cyBvZiBwb2ludHMsIGV0Yy4sIDAgYnkgZGVmYXVsdCkuXHJcblx0Ly8gQ2FuIHVzZSBhIGN1c3RvbSBbYGNvb3Jkc1RvTGF0TG5nYF0oI2dlb2pzb24tY29vcmRzdG9sYXRsbmcpIGZ1bmN0aW9uLlxyXG5cdGNvb3Jkc1RvTGF0TG5nczogZnVuY3Rpb24gKGNvb3JkcywgbGV2ZWxzRGVlcCwgY29vcmRzVG9MYXRMbmcpIHtcclxuXHRcdHZhciBsYXRsbmdzID0gW107XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IDAsIGxlbiA9IGNvb3Jkcy5sZW5ndGgsIGxhdGxuZzsgaSA8IGxlbjsgaSsrKSB7XHJcblx0XHRcdGxhdGxuZyA9IGxldmVsc0RlZXAgP1xyXG5cdFx0XHQgICAgICAgIHRoaXMuY29vcmRzVG9MYXRMbmdzKGNvb3Jkc1tpXSwgbGV2ZWxzRGVlcCAtIDEsIGNvb3Jkc1RvTGF0TG5nKSA6XHJcblx0XHRcdCAgICAgICAgKGNvb3Jkc1RvTGF0TG5nIHx8IHRoaXMuY29vcmRzVG9MYXRMbmcpKGNvb3Jkc1tpXSk7XHJcblxyXG5cdFx0XHRsYXRsbmdzLnB1c2gobGF0bG5nKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gbGF0bG5ncztcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gbGF0TG5nVG9Db29yZHMobGF0bG5nOiBMYXRMbmcpOiBBcnJheVxyXG5cdC8vIFJldmVyc2Ugb2YgW2Bjb29yZHNUb0xhdExuZ2BdKCNnZW9qc29uLWNvb3Jkc3RvbGF0bG5nKVxyXG5cdGxhdExuZ1RvQ29vcmRzOiBmdW5jdGlvbiAobGF0bG5nKSB7XHJcblx0XHRyZXR1cm4gbGF0bG5nLmFsdCAhPT0gdW5kZWZpbmVkID9cclxuXHRcdFx0XHRbbGF0bG5nLmxuZywgbGF0bG5nLmxhdCwgbGF0bG5nLmFsdF0gOlxyXG5cdFx0XHRcdFtsYXRsbmcubG5nLCBsYXRsbmcubGF0XTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gbGF0TG5nc1RvQ29vcmRzKGxhdGxuZ3M6IEFycmF5LCBsZXZlbHNEZWVwPzogTnVtYmVyLCBjbG9zZWQ/OiBCb29sZWFuKTogQXJyYXlcclxuXHQvLyBSZXZlcnNlIG9mIFtgY29vcmRzVG9MYXRMbmdzYF0oI2dlb2pzb24tY29vcmRzdG9sYXRsbmdzKVxyXG5cdC8vIGBjbG9zZWRgIGRldGVybWluZXMgd2hldGhlciB0aGUgZmlyc3QgcG9pbnQgc2hvdWxkIGJlIGFwcGVuZGVkIHRvIHRoZSBlbmQgb2YgdGhlIGFycmF5IHRvIGNsb3NlIHRoZSBmZWF0dXJlLCBvbmx5IHVzZWQgd2hlbiBgbGV2ZWxzRGVlcGAgaXMgMC4gRmFsc2UgYnkgZGVmYXVsdC5cclxuXHRsYXRMbmdzVG9Db29yZHM6IGZ1bmN0aW9uIChsYXRsbmdzLCBsZXZlbHNEZWVwLCBjbG9zZWQpIHtcclxuXHRcdHZhciBjb29yZHMgPSBbXTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gMCwgbGVuID0gbGF0bG5ncy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG5cdFx0XHRjb29yZHMucHVzaChsZXZlbHNEZWVwID9cclxuXHRcdFx0XHRMLkdlb0pTT04ubGF0TG5nc1RvQ29vcmRzKGxhdGxuZ3NbaV0sIGxldmVsc0RlZXAgLSAxLCBjbG9zZWQpIDpcclxuXHRcdFx0XHRMLkdlb0pTT04ubGF0TG5nVG9Db29yZHMobGF0bG5nc1tpXSkpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICghbGV2ZWxzRGVlcCAmJiBjbG9zZWQpIHtcclxuXHRcdFx0Y29vcmRzLnB1c2goY29vcmRzWzBdKTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4gY29vcmRzO1xyXG5cdH0sXHJcblxyXG5cdGdldEZlYXR1cmU6IGZ1bmN0aW9uIChsYXllciwgbmV3R2VvbWV0cnkpIHtcclxuXHRcdHJldHVybiBsYXllci5mZWF0dXJlID9cclxuXHRcdFx0XHRMLmV4dGVuZCh7fSwgbGF5ZXIuZmVhdHVyZSwge2dlb21ldHJ5OiBuZXdHZW9tZXRyeX0pIDpcclxuXHRcdFx0XHRMLkdlb0pTT04uYXNGZWF0dXJlKG5ld0dlb21ldHJ5KTtcclxuXHR9LFxyXG5cclxuXHQvLyBAZnVuY3Rpb24gYXNGZWF0dXJlKGdlb2pzb246IE9iamVjdCk6IE9iamVjdFxyXG5cdC8vIE5vcm1hbGl6ZSBHZW9KU09OIGdlb21ldHJpZXMvZmVhdHVyZXMgaW50byBHZW9KU09OIGZlYXR1cmVzLlxyXG5cdGFzRmVhdHVyZTogZnVuY3Rpb24gKGdlb2pzb24pIHtcclxuXHRcdGlmIChnZW9qc29uLnR5cGUgPT09ICdGZWF0dXJlJyB8fCBnZW9qc29uLnR5cGUgPT09ICdGZWF0dXJlQ29sbGVjdGlvbicpIHtcclxuXHRcdFx0cmV0dXJuIGdlb2pzb247XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHtcclxuXHRcdFx0dHlwZTogJ0ZlYXR1cmUnLFxyXG5cdFx0XHRwcm9wZXJ0aWVzOiB7fSxcclxuXHRcdFx0Z2VvbWV0cnk6IGdlb2pzb25cclxuXHRcdH07XHJcblx0fVxyXG59KTtcclxuXHJcbnZhciBQb2ludFRvR2VvSlNPTiA9IHtcclxuXHR0b0dlb0pTT046IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiBMLkdlb0pTT04uZ2V0RmVhdHVyZSh0aGlzLCB7XHJcblx0XHRcdHR5cGU6ICdQb2ludCcsXHJcblx0XHRcdGNvb3JkaW5hdGVzOiBMLkdlb0pTT04ubGF0TG5nVG9Db29yZHModGhpcy5nZXRMYXRMbmcoKSlcclxuXHRcdH0pO1xyXG5cdH1cclxufTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgTWFya2VyXHJcbi8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG4vLyBSZXR1cm5zIGEgW2BHZW9KU09OYF0oaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HZW9KU09OKSByZXByZXNlbnRhdGlvbiBvZiB0aGUgbWFya2VyIChhcyBhIEdlb0pTT04gYFBvaW50YCBGZWF0dXJlKS5cclxuTC5NYXJrZXIuaW5jbHVkZShQb2ludFRvR2VvSlNPTik7XHJcblxyXG4vLyBAbmFtZXNwYWNlIENpcmNsZU1hcmtlclxyXG4vLyBAbWV0aG9kIHRvR2VvSlNPTigpOiBPYmplY3RcclxuLy8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNpcmNsZSBtYXJrZXIgKGFzIGEgR2VvSlNPTiBgUG9pbnRgIEZlYXR1cmUpLlxyXG5MLkNpcmNsZS5pbmNsdWRlKFBvaW50VG9HZW9KU09OKTtcclxuTC5DaXJjbGVNYXJrZXIuaW5jbHVkZShQb2ludFRvR2VvSlNPTik7XHJcblxyXG5cclxuLy8gQG5hbWVzcGFjZSBQb2x5bGluZVxyXG4vLyBAbWV0aG9kIHRvR2VvSlNPTigpOiBPYmplY3RcclxuLy8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIHBvbHlsaW5lIChhcyBhIEdlb0pTT04gYExpbmVTdHJpbmdgIG9yIGBNdWx0aUxpbmVTdHJpbmdgIEZlYXR1cmUpLlxyXG5MLlBvbHlsaW5lLnByb3RvdHlwZS50b0dlb0pTT04gPSBmdW5jdGlvbiAoKSB7XHJcblx0dmFyIG11bHRpID0gIUwuUG9seWxpbmUuX2ZsYXQodGhpcy5fbGF0bG5ncyk7XHJcblxyXG5cdHZhciBjb29yZHMgPSBMLkdlb0pTT04ubGF0TG5nc1RvQ29vcmRzKHRoaXMuX2xhdGxuZ3MsIG11bHRpID8gMSA6IDApO1xyXG5cclxuXHRyZXR1cm4gTC5HZW9KU09OLmdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0dHlwZTogKG11bHRpID8gJ011bHRpJyA6ICcnKSArICdMaW5lU3RyaW5nJyxcclxuXHRcdGNvb3JkaW5hdGVzOiBjb29yZHNcclxuXHR9KTtcclxufTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgUG9seWdvblxyXG4vLyBAbWV0aG9kIHRvR2VvSlNPTigpOiBPYmplY3RcclxuLy8gUmV0dXJucyBhIFtgR2VvSlNPTmBdKGh0dHA6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2VvSlNPTikgcmVwcmVzZW50YXRpb24gb2YgdGhlIHBvbHlnb24gKGFzIGEgR2VvSlNPTiBgUG9seWdvbmAgb3IgYE11bHRpUG9seWdvbmAgRmVhdHVyZSkuXHJcbkwuUG9seWdvbi5wcm90b3R5cGUudG9HZW9KU09OID0gZnVuY3Rpb24gKCkge1xyXG5cdHZhciBob2xlcyA9ICFMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3MpLFxyXG5cdCAgICBtdWx0aSA9IGhvbGVzICYmICFMLlBvbHlsaW5lLl9mbGF0KHRoaXMuX2xhdGxuZ3NbMF0pO1xyXG5cclxuXHR2YXIgY29vcmRzID0gTC5HZW9KU09OLmxhdExuZ3NUb0Nvb3Jkcyh0aGlzLl9sYXRsbmdzLCBtdWx0aSA/IDIgOiBob2xlcyA/IDEgOiAwLCB0cnVlKTtcclxuXHJcblx0aWYgKCFob2xlcykge1xyXG5cdFx0Y29vcmRzID0gW2Nvb3Jkc107XHJcblx0fVxyXG5cclxuXHRyZXR1cm4gTC5HZW9KU09OLmdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0dHlwZTogKG11bHRpID8gJ011bHRpJyA6ICcnKSArICdQb2x5Z29uJyxcclxuXHRcdGNvb3JkaW5hdGVzOiBjb29yZHNcclxuXHR9KTtcclxufTtcclxuXHJcblxyXG4vLyBAbmFtZXNwYWNlIExheWVyR3JvdXBcclxuTC5MYXllckdyb3VwLmluY2x1ZGUoe1xyXG5cdHRvTXVsdGlQb2ludDogZnVuY3Rpb24gKCkge1xyXG5cdFx0dmFyIGNvb3JkcyA9IFtdO1xyXG5cclxuXHRcdHRoaXMuZWFjaExheWVyKGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0XHRjb29yZHMucHVzaChsYXllci50b0dlb0pTT04oKS5nZW9tZXRyeS5jb29yZGluYXRlcyk7XHJcblx0XHR9KTtcclxuXHJcblx0XHRyZXR1cm4gTC5HZW9KU09OLmdldEZlYXR1cmUodGhpcywge1xyXG5cdFx0XHR0eXBlOiAnTXVsdGlQb2ludCcsXHJcblx0XHRcdGNvb3JkaW5hdGVzOiBjb29yZHNcclxuXHRcdH0pO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgdG9HZW9KU09OKCk6IE9iamVjdFxyXG5cdC8vIFJldHVybnMgYSBbYEdlb0pTT05gXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0dlb0pTT04pIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBsYXllciBncm91cCAoYXMgYSBHZW9KU09OIGBHZW9tZXRyeUNvbGxlY3Rpb25gKS5cclxuXHR0b0dlb0pTT046IGZ1bmN0aW9uICgpIHtcclxuXHJcblx0XHR2YXIgdHlwZSA9IHRoaXMuZmVhdHVyZSAmJiB0aGlzLmZlYXR1cmUuZ2VvbWV0cnkgJiYgdGhpcy5mZWF0dXJlLmdlb21ldHJ5LnR5cGU7XHJcblxyXG5cdFx0aWYgKHR5cGUgPT09ICdNdWx0aVBvaW50Jykge1xyXG5cdFx0XHRyZXR1cm4gdGhpcy50b011bHRpUG9pbnQoKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgaXNHZW9tZXRyeUNvbGxlY3Rpb24gPSB0eXBlID09PSAnR2VvbWV0cnlDb2xsZWN0aW9uJyxcclxuXHRcdCAgICBqc29ucyA9IFtdO1xyXG5cclxuXHRcdHRoaXMuZWFjaExheWVyKGZ1bmN0aW9uIChsYXllcikge1xyXG5cdFx0XHRpZiAobGF5ZXIudG9HZW9KU09OKSB7XHJcblx0XHRcdFx0dmFyIGpzb24gPSBsYXllci50b0dlb0pTT04oKTtcclxuXHRcdFx0XHRqc29ucy5wdXNoKGlzR2VvbWV0cnlDb2xsZWN0aW9uID8ganNvbi5nZW9tZXRyeSA6IEwuR2VvSlNPTi5hc0ZlYXR1cmUoanNvbikpO1xyXG5cdFx0XHR9XHJcblx0XHR9KTtcclxuXHJcblx0XHRpZiAoaXNHZW9tZXRyeUNvbGxlY3Rpb24pIHtcclxuXHRcdFx0cmV0dXJuIEwuR2VvSlNPTi5nZXRGZWF0dXJlKHRoaXMsIHtcclxuXHRcdFx0XHRnZW9tZXRyaWVzOiBqc29ucyxcclxuXHRcdFx0XHR0eXBlOiAnR2VvbWV0cnlDb2xsZWN0aW9uJ1xyXG5cdFx0XHR9KTtcclxuXHRcdH1cclxuXHJcblx0XHRyZXR1cm4ge1xyXG5cdFx0XHR0eXBlOiAnRmVhdHVyZUNvbGxlY3Rpb24nLFxyXG5cdFx0XHRmZWF0dXJlczoganNvbnNcclxuXHRcdH07XHJcblx0fVxyXG59KTtcclxuXHJcbi8vIEBuYW1lc3BhY2UgR2VvSlNPTlxyXG4vLyBAZmFjdG9yeSBMLmdlb0pTT04oZ2VvanNvbj86IE9iamVjdCwgb3B0aW9ucz86IEdlb0pTT04gb3B0aW9ucylcclxuLy8gQ3JlYXRlcyBhIEdlb0pTT04gbGF5ZXIuIE9wdGlvbmFsbHkgYWNjZXB0cyBhbiBvYmplY3QgaW5cclxuLy8gW0dlb0pTT04gZm9ybWF0XShodHRwOi8vZ2VvanNvbi5vcmcvZ2VvanNvbi1zcGVjLmh0bWwpIHRvIGRpc3BsYXkgb24gdGhlIG1hcFxyXG4vLyAoeW91IGNhbiBhbHRlcm5hdGl2ZWx5IGFkZCBpdCBsYXRlciB3aXRoIGBhZGREYXRhYCBtZXRob2QpIGFuZCBhbiBgb3B0aW9uc2Agb2JqZWN0LlxyXG5MLmdlb0pTT04gPSBmdW5jdGlvbiAoZ2VvanNvbiwgb3B0aW9ucykge1xyXG5cdHJldHVybiBuZXcgTC5HZW9KU09OKGdlb2pzb24sIG9wdGlvbnMpO1xyXG59O1xyXG4vLyBCYWNrd2FyZCBjb21wYXRpYmlsaXR5LlxyXG5MLmdlb0pzb24gPSBMLmdlb0pTT047XHJcblxuXG5cbi8qXHJcbiAqIEBjbGFzcyBEcmFnZ2FibGVcclxuICogQGFrYSBMLkRyYWdnYWJsZVxyXG4gKiBAaW5oZXJpdHMgRXZlbnRlZFxyXG4gKlxyXG4gKiBBIGNsYXNzIGZvciBtYWtpbmcgRE9NIGVsZW1lbnRzIGRyYWdnYWJsZSAoaW5jbHVkaW5nIHRvdWNoIHN1cHBvcnQpLlxyXG4gKiBVc2VkIGludGVybmFsbHkgZm9yIG1hcCBhbmQgbWFya2VyIGRyYWdnaW5nLiBPbmx5IHdvcmtzIGZvciBlbGVtZW50c1xyXG4gKiB0aGF0IHdlcmUgcG9zaXRpb25lZCB3aXRoIFtgTC5Eb21VdGlsLnNldFBvc2l0aW9uYF0oI2RvbXV0aWwtc2V0cG9zaXRpb24pLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKiBgYGBqc1xyXG4gKiB2YXIgZHJhZ2dhYmxlID0gbmV3IEwuRHJhZ2dhYmxlKGVsZW1lbnRUb0RyYWcpO1xyXG4gKiBkcmFnZ2FibGUuZW5hYmxlKCk7XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbkwuRHJhZ2dhYmxlID0gTC5FdmVudGVkLmV4dGVuZCh7XHJcblxyXG5cdG9wdGlvbnM6IHtcclxuXHRcdC8vIEBvcHRpb24gY2xpY2tUb2xlcmFuY2U6IE51bWJlciA9IDNcclxuXHRcdC8vIFRoZSBtYXggbnVtYmVyIG9mIHBpeGVscyBhIHVzZXIgY2FuIHNoaWZ0IHRoZSBtb3VzZSBwb2ludGVyIGR1cmluZyBhIGNsaWNrXHJcblx0XHQvLyBmb3IgaXQgdG8gYmUgY29uc2lkZXJlZCBhIHZhbGlkIGNsaWNrIChhcyBvcHBvc2VkIHRvIGEgbW91c2UgZHJhZykuXHJcblx0XHRjbGlja1RvbGVyYW5jZTogM1xyXG5cdH0sXHJcblxyXG5cdHN0YXRpY3M6IHtcclxuXHRcdFNUQVJUOiBMLkJyb3dzZXIudG91Y2ggPyBbJ3RvdWNoc3RhcnQnLCAnbW91c2Vkb3duJ10gOiBbJ21vdXNlZG93biddLFxyXG5cdFx0RU5EOiB7XHJcblx0XHRcdG1vdXNlZG93bjogJ21vdXNldXAnLFxyXG5cdFx0XHR0b3VjaHN0YXJ0OiAndG91Y2hlbmQnLFxyXG5cdFx0XHRwb2ludGVyZG93bjogJ3RvdWNoZW5kJyxcclxuXHRcdFx0TVNQb2ludGVyRG93bjogJ3RvdWNoZW5kJ1xyXG5cdFx0fSxcclxuXHRcdE1PVkU6IHtcclxuXHRcdFx0bW91c2Vkb3duOiAnbW91c2Vtb3ZlJyxcclxuXHRcdFx0dG91Y2hzdGFydDogJ3RvdWNobW92ZScsXHJcblx0XHRcdHBvaW50ZXJkb3duOiAndG91Y2htb3ZlJyxcclxuXHRcdFx0TVNQb2ludGVyRG93bjogJ3RvdWNobW92ZSdcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAY29uc3RydWN0b3IgTC5EcmFnZ2FibGUoZWw6IEhUTUxFbGVtZW50LCBkcmFnSGFuZGxlPzogSFRNTEVsZW1lbnQsIHByZXZlbnRPdXRsaW5lOiBCb29sZWFuKVxyXG5cdC8vIENyZWF0ZXMgYSBgRHJhZ2dhYmxlYCBvYmplY3QgZm9yIG1vdmluZyBgZWxgIHdoZW4geW91IHN0YXJ0IGRyYWdnaW5nIHRoZSBgZHJhZ0hhbmRsZWAgZWxlbWVudCAoZXF1YWxzIGBlbGAgaXRzZWxmIGJ5IGRlZmF1bHQpLlxyXG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChlbGVtZW50LCBkcmFnU3RhcnRUYXJnZXQsIHByZXZlbnRPdXRsaW5lKSB7XHJcblx0XHR0aGlzLl9lbGVtZW50ID0gZWxlbWVudDtcclxuXHRcdHRoaXMuX2RyYWdTdGFydFRhcmdldCA9IGRyYWdTdGFydFRhcmdldCB8fCBlbGVtZW50O1xyXG5cdFx0dGhpcy5fcHJldmVudE91dGxpbmUgPSBwcmV2ZW50T3V0bGluZTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGVuYWJsZSgpXHJcblx0Ly8gRW5hYmxlcyB0aGUgZHJhZ2dpbmcgYWJpbGl0eVxyXG5cdGVuYWJsZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKHRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0TC5Eb21FdmVudC5vbih0aGlzLl9kcmFnU3RhcnRUYXJnZXQsIEwuRHJhZ2dhYmxlLlNUQVJULmpvaW4oJyAnKSwgdGhpcy5fb25Eb3duLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9lbmFibGVkID0gdHJ1ZTtcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGRpc2FibGUoKVxyXG5cdC8vIERpc2FibGVzIHRoZSBkcmFnZ2luZyBhYmlsaXR5XHJcblx0ZGlzYWJsZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9lbmFibGVkKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdC8vIElmIHdlJ3JlIGN1cnJlbnRseSBkcmFnZ2luZyB0aGlzIGRyYWdnYWJsZSxcclxuXHRcdC8vIGRpc2FibGluZyBpdCBjb3VudHMgYXMgZmlyc3QgZW5kaW5nIHRoZSBkcmFnLlxyXG5cdFx0aWYgKEwuRHJhZ2dhYmxlLl9kcmFnZ2luZyA9PT0gdGhpcykge1xyXG5cdFx0XHR0aGlzLmZpbmlzaERyYWcoKTtcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9kcmFnU3RhcnRUYXJnZXQsIEwuRHJhZ2dhYmxlLlNUQVJULmpvaW4oJyAnKSwgdGhpcy5fb25Eb3duLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9lbmFibGVkID0gZmFsc2U7XHJcblx0XHR0aGlzLl9tb3ZlZCA9IGZhbHNlO1xyXG5cdH0sXHJcblxyXG5cdF9vbkRvd246IGZ1bmN0aW9uIChlKSB7XHJcblx0XHQvLyBJZ25vcmUgc2ltdWxhdGVkIGV2ZW50cywgc2luY2Ugd2UgaGFuZGxlIGJvdGggdG91Y2ggYW5kXHJcblx0XHQvLyBtb3VzZSBleHBsaWNpdGx5OyBvdGhlcndpc2Ugd2UgcmlzayBnZXR0aW5nIGR1cGxpY2F0ZXMgb2ZcclxuXHRcdC8vIHRvdWNoIGV2ZW50cywgc2VlICM0MzE1LlxyXG5cdFx0Ly8gQWxzbyBpZ25vcmUgdGhlIGV2ZW50IGlmIGRpc2FibGVkOyB0aGlzIGhhcHBlbnMgaW4gSUUxMVxyXG5cdFx0Ly8gdW5kZXIgc29tZSBjaXJjdW1zdGFuY2VzLCBzZWUgIzM2NjYuXHJcblx0XHRpZiAoZS5fc2ltdWxhdGVkIHx8ICF0aGlzLl9lbmFibGVkKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XHJcblxyXG5cdFx0aWYgKEwuRG9tVXRpbC5oYXNDbGFzcyh0aGlzLl9lbGVtZW50LCAnbGVhZmxldC16b29tLWFuaW0nKSkgeyByZXR1cm47IH1cclxuXHJcblx0XHRpZiAoTC5EcmFnZ2FibGUuX2RyYWdnaW5nIHx8IGUuc2hpZnRLZXkgfHwgKChlLndoaWNoICE9PSAxKSAmJiAoZS5idXR0b24gIT09IDEpICYmICFlLnRvdWNoZXMpKSB7IHJldHVybjsgfVxyXG5cdFx0TC5EcmFnZ2FibGUuX2RyYWdnaW5nID0gdGhpczsgIC8vIFByZXZlbnQgZHJhZ2dpbmcgbXVsdGlwbGUgb2JqZWN0cyBhdCBvbmNlLlxyXG5cclxuXHRcdGlmICh0aGlzLl9wcmV2ZW50T3V0bGluZSkge1xyXG5cdFx0XHRMLkRvbVV0aWwucHJldmVudE91dGxpbmUodGhpcy5fZWxlbWVudCk7XHJcblx0XHR9XHJcblxyXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVJbWFnZURyYWcoKTtcclxuXHRcdEwuRG9tVXRpbC5kaXNhYmxlVGV4dFNlbGVjdGlvbigpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tb3ZpbmcpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0Ly8gQGV2ZW50IGRvd246IEV2ZW50XHJcblx0XHQvLyBGaXJlZCB3aGVuIGEgZHJhZyBpcyBhYm91dCB0byBzdGFydC5cclxuXHRcdHRoaXMuZmlyZSgnZG93bicpO1xyXG5cclxuXHRcdHZhciBmaXJzdCA9IGUudG91Y2hlcyA/IGUudG91Y2hlc1swXSA6IGU7XHJcblxyXG5cdFx0dGhpcy5fc3RhcnRQb2ludCA9IG5ldyBMLlBvaW50KGZpcnN0LmNsaWVudFgsIGZpcnN0LmNsaWVudFkpO1xyXG5cclxuXHRcdEwuRG9tRXZlbnRcclxuXHRcdFx0Lm9uKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5NT1ZFW2UudHlwZV0sIHRoaXMuX29uTW92ZSwgdGhpcylcclxuXHRcdFx0Lm9uKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5FTkRbZS50eXBlXSwgdGhpcy5fb25VcCwgdGhpcyk7XHJcblx0fSxcclxuXHJcblx0X29uTW92ZTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdC8vIElnbm9yZSBzaW11bGF0ZWQgZXZlbnRzLCBzaW5jZSB3ZSBoYW5kbGUgYm90aCB0b3VjaCBhbmRcclxuXHRcdC8vIG1vdXNlIGV4cGxpY2l0bHk7IG90aGVyd2lzZSB3ZSByaXNrIGdldHRpbmcgZHVwbGljYXRlcyBvZlxyXG5cdFx0Ly8gdG91Y2ggZXZlbnRzLCBzZWUgIzQzMTUuXHJcblx0XHQvLyBBbHNvIGlnbm9yZSB0aGUgZXZlbnQgaWYgZGlzYWJsZWQ7IHRoaXMgaGFwcGVucyBpbiBJRTExXHJcblx0XHQvLyB1bmRlciBzb21lIGNpcmN1bXN0YW5jZXMsIHNlZSAjMzY2Ni5cclxuXHRcdGlmIChlLl9zaW11bGF0ZWQgfHwgIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblxyXG5cdFx0aWYgKGUudG91Y2hlcyAmJiBlLnRvdWNoZXMubGVuZ3RoID4gMSkge1xyXG5cdFx0XHR0aGlzLl9tb3ZlZCA9IHRydWU7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZmlyc3QgPSAoZS50b3VjaGVzICYmIGUudG91Y2hlcy5sZW5ndGggPT09IDEgPyBlLnRvdWNoZXNbMF0gOiBlKSxcclxuXHRcdCAgICBuZXdQb2ludCA9IG5ldyBMLlBvaW50KGZpcnN0LmNsaWVudFgsIGZpcnN0LmNsaWVudFkpLFxyXG5cdFx0ICAgIG9mZnNldCA9IG5ld1BvaW50LnN1YnRyYWN0KHRoaXMuX3N0YXJ0UG9pbnQpO1xyXG5cclxuXHRcdGlmICghb2Zmc2V0LnggJiYgIW9mZnNldC55KSB7IHJldHVybjsgfVxyXG5cdFx0aWYgKE1hdGguYWJzKG9mZnNldC54KSArIE1hdGguYWJzKG9mZnNldC55KSA8IHRoaXMub3B0aW9ucy5jbGlja1RvbGVyYW5jZSkgeyByZXR1cm47IH1cclxuXHJcblx0XHRMLkRvbUV2ZW50LnByZXZlbnREZWZhdWx0KGUpO1xyXG5cclxuXHRcdGlmICghdGhpcy5fbW92ZWQpIHtcclxuXHRcdFx0Ly8gQGV2ZW50IGRyYWdzdGFydDogRXZlbnRcclxuXHRcdFx0Ly8gRmlyZWQgd2hlbiBhIGRyYWcgc3RhcnRzXHJcblx0XHRcdHRoaXMuZmlyZSgnZHJhZ3N0YXJ0Jyk7XHJcblxyXG5cdFx0XHR0aGlzLl9tb3ZlZCA9IHRydWU7XHJcblx0XHRcdHRoaXMuX3N0YXJ0UG9zID0gTC5Eb21VdGlsLmdldFBvc2l0aW9uKHRoaXMuX2VsZW1lbnQpLnN1YnRyYWN0KG9mZnNldCk7XHJcblxyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoZG9jdW1lbnQuYm9keSwgJ2xlYWZsZXQtZHJhZ2dpbmcnKTtcclxuXHJcblx0XHRcdHRoaXMuX2xhc3RUYXJnZXQgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQ7XHJcblx0XHRcdC8vIElFIGFuZCBFZGdlIGRvIG5vdCBnaXZlIHRoZSA8dXNlPiBlbGVtZW50LCBzbyBmZXRjaCBpdFxyXG5cdFx0XHQvLyBpZiBuZWNlc3NhcnlcclxuXHRcdFx0aWYgKCh3aW5kb3cuU1ZHRWxlbWVudEluc3RhbmNlKSAmJiAodGhpcy5fbGFzdFRhcmdldCBpbnN0YW5jZW9mIFNWR0VsZW1lbnRJbnN0YW5jZSkpIHtcclxuXHRcdFx0XHR0aGlzLl9sYXN0VGFyZ2V0ID0gdGhpcy5fbGFzdFRhcmdldC5jb3JyZXNwb25kaW5nVXNlRWxlbWVudDtcclxuXHRcdFx0fVxyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fbGFzdFRhcmdldCwgJ2xlYWZsZXQtZHJhZy10YXJnZXQnKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl9uZXdQb3MgPSB0aGlzLl9zdGFydFBvcy5hZGQob2Zmc2V0KTtcclxuXHRcdHRoaXMuX21vdmluZyA9IHRydWU7XHJcblxyXG5cdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9hbmltUmVxdWVzdCk7XHJcblx0XHR0aGlzLl9sYXN0RXZlbnQgPSBlO1xyXG5cdFx0dGhpcy5fYW5pbVJlcXVlc3QgPSBMLlV0aWwucmVxdWVzdEFuaW1GcmFtZSh0aGlzLl91cGRhdGVQb3NpdGlvbiwgdGhpcywgdHJ1ZSk7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZVBvc2l0aW9uOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgZSA9IHtvcmlnaW5hbEV2ZW50OiB0aGlzLl9sYXN0RXZlbnR9O1xyXG5cclxuXHRcdC8vIEBldmVudCBwcmVkcmFnOiBFdmVudFxyXG5cdFx0Ly8gRmlyZWQgY29udGludW91c2x5IGR1cmluZyBkcmFnZ2luZyAqYmVmb3JlKiBlYWNoIGNvcnJlc3BvbmRpbmdcclxuXHRcdC8vIHVwZGF0ZSBvZiB0aGUgZWxlbWVudCdzIHBvc2l0aW9uLlxyXG5cdFx0dGhpcy5maXJlKCdwcmVkcmFnJywgZSk7XHJcblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fZWxlbWVudCwgdGhpcy5fbmV3UG9zKTtcclxuXHJcblx0XHQvLyBAZXZlbnQgZHJhZzogRXZlbnRcclxuXHRcdC8vIEZpcmVkIGNvbnRpbnVvdXNseSBkdXJpbmcgZHJhZ2dpbmcuXHJcblx0XHR0aGlzLmZpcmUoJ2RyYWcnLCBlKTtcclxuXHR9LFxyXG5cclxuXHRfb25VcDogZnVuY3Rpb24gKGUpIHtcclxuXHRcdC8vIElnbm9yZSBzaW11bGF0ZWQgZXZlbnRzLCBzaW5jZSB3ZSBoYW5kbGUgYm90aCB0b3VjaCBhbmRcclxuXHRcdC8vIG1vdXNlIGV4cGxpY2l0bHk7IG90aGVyd2lzZSB3ZSByaXNrIGdldHRpbmcgZHVwbGljYXRlcyBvZlxyXG5cdFx0Ly8gdG91Y2ggZXZlbnRzLCBzZWUgIzQzMTUuXHJcblx0XHQvLyBBbHNvIGlnbm9yZSB0aGUgZXZlbnQgaWYgZGlzYWJsZWQ7IHRoaXMgaGFwcGVucyBpbiBJRTExXHJcblx0XHQvLyB1bmRlciBzb21lIGNpcmN1bXN0YW5jZXMsIHNlZSAjMzY2Ni5cclxuXHRcdGlmIChlLl9zaW11bGF0ZWQgfHwgIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuOyB9XHJcblx0XHR0aGlzLmZpbmlzaERyYWcoKTtcclxuXHR9LFxyXG5cclxuXHRmaW5pc2hEcmFnOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3MoZG9jdW1lbnQuYm9keSwgJ2xlYWZsZXQtZHJhZ2dpbmcnKTtcclxuXHJcblx0XHRpZiAodGhpcy5fbGFzdFRhcmdldCkge1xyXG5cdFx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fbGFzdFRhcmdldCwgJ2xlYWZsZXQtZHJhZy10YXJnZXQnKTtcclxuXHRcdFx0dGhpcy5fbGFzdFRhcmdldCA9IG51bGw7XHJcblx0XHR9XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiBMLkRyYWdnYWJsZS5NT1ZFKSB7XHJcblx0XHRcdEwuRG9tRXZlbnRcclxuXHRcdFx0XHQub2ZmKGRvY3VtZW50LCBMLkRyYWdnYWJsZS5NT1ZFW2ldLCB0aGlzLl9vbk1vdmUsIHRoaXMpXHJcblx0XHRcdFx0Lm9mZihkb2N1bWVudCwgTC5EcmFnZ2FibGUuRU5EW2ldLCB0aGlzLl9vblVwLCB0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHRMLkRvbVV0aWwuZW5hYmxlSW1hZ2VEcmFnKCk7XHJcblx0XHRMLkRvbVV0aWwuZW5hYmxlVGV4dFNlbGVjdGlvbigpO1xyXG5cclxuXHRcdGlmICh0aGlzLl9tb3ZlZCAmJiB0aGlzLl9tb3ZpbmcpIHtcclxuXHRcdFx0Ly8gZW5zdXJlIGRyYWcgaXMgbm90IGZpcmVkIGFmdGVyIGRyYWdlbmRcclxuXHRcdFx0TC5VdGlsLmNhbmNlbEFuaW1GcmFtZSh0aGlzLl9hbmltUmVxdWVzdCk7XHJcblxyXG5cdFx0XHQvLyBAZXZlbnQgZHJhZ2VuZDogRHJhZ0VuZEV2ZW50XHJcblx0XHRcdC8vIEZpcmVkIHdoZW4gdGhlIGRyYWcgZW5kcy5cclxuXHRcdFx0dGhpcy5maXJlKCdkcmFnZW5kJywge1xyXG5cdFx0XHRcdGRpc3RhbmNlOiB0aGlzLl9uZXdQb3MuZGlzdGFuY2VUbyh0aGlzLl9zdGFydFBvcylcclxuXHRcdFx0fSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fbW92aW5nID0gZmFsc2U7XHJcblx0XHRMLkRyYWdnYWJsZS5fZHJhZ2dpbmcgPSBmYWxzZTtcclxuXHR9XHJcblxyXG59KTtcclxuXG5cblxuLypcblx0TC5IYW5kbGVyIGlzIGEgYmFzZSBjbGFzcyBmb3IgaGFuZGxlciBjbGFzc2VzIHRoYXQgYXJlIHVzZWQgaW50ZXJuYWxseSB0byBpbmplY3Rcblx0aW50ZXJhY3Rpb24gZmVhdHVyZXMgbGlrZSBkcmFnZ2luZyB0byBjbGFzc2VzIGxpa2UgTWFwIGFuZCBNYXJrZXIuXG4qL1xuXG4vLyBAY2xhc3MgSGFuZGxlclxuLy8gQGFrYSBMLkhhbmRsZXJcbi8vIEFic3RyYWN0IGNsYXNzIGZvciBtYXAgaW50ZXJhY3Rpb24gaGFuZGxlcnNcblxuTC5IYW5kbGVyID0gTC5DbGFzcy5leHRlbmQoe1xuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0dGhpcy5fbWFwID0gbWFwO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZW5hYmxlKCk6IHRoaXNcblx0Ly8gRW5hYmxlcyB0aGUgaGFuZGxlclxuXHRlbmFibGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fZW5hYmxlZCkgeyByZXR1cm4gdGhpczsgfVxuXG5cdFx0dGhpcy5fZW5hYmxlZCA9IHRydWU7XG5cdFx0dGhpcy5hZGRIb29rcygpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8vIEBtZXRob2QgZGlzYWJsZSgpOiB0aGlzXG5cdC8vIERpc2FibGVzIHRoZSBoYW5kbGVyXG5cdGRpc2FibGU6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX2VuYWJsZWQpIHsgcmV0dXJuIHRoaXM7IH1cblxuXHRcdHRoaXMuX2VuYWJsZWQgPSBmYWxzZTtcblx0XHR0aGlzLnJlbW92ZUhvb2tzKCk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Ly8gQG1ldGhvZCBlbmFibGVkKCk6IEJvb2xlYW5cblx0Ly8gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGhhbmRsZXIgaXMgZW5hYmxlZFxuXHRlbmFibGVkOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuICEhdGhpcy5fZW5hYmxlZDtcblx0fVxuXG5cdC8vIEBzZWN0aW9uIEV4dGVuc2lvbiBtZXRob2RzXG5cdC8vIENsYXNzZXMgaW5oZXJpdGluZyBmcm9tIGBIYW5kbGVyYCBtdXN0IGltcGxlbWVudCB0aGUgdHdvIGZvbGxvd2luZyBtZXRob2RzOlxuXHQvLyBAbWV0aG9kIGFkZEhvb2tzKClcblx0Ly8gQ2FsbGVkIHdoZW4gdGhlIGhhbmRsZXIgaXMgZW5hYmxlZCwgc2hvdWxkIGFkZCBldmVudCBob29rcy5cblx0Ly8gQG1ldGhvZCByZW1vdmVIb29rcygpXG5cdC8vIENhbGxlZCB3aGVuIHRoZSBoYW5kbGVyIGlzIGRpc2FibGVkLCBzaG91bGQgcmVtb3ZlIHRoZSBldmVudCBob29rcyBhZGRlZCBwcmV2aW91c2x5LlxufSk7XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLk1hcERyYWcgaXMgdXNlZCB0byBtYWtlIHRoZSBtYXAgZHJhZ2dhYmxlICh3aXRoIHBhbm5pbmcgaW5lcnRpYSksIGVuYWJsZWQgYnkgZGVmYXVsdC5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuTC5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQG9wdGlvbiBkcmFnZ2luZzogQm9vbGVhbiA9IHRydWVcblx0Ly8gV2hldGhlciB0aGUgbWFwIGJlIGRyYWdnYWJsZSB3aXRoIG1vdXNlL3RvdWNoIG9yIG5vdC5cblx0ZHJhZ2dpbmc6IHRydWUsXG5cblx0Ly8gQHNlY3Rpb24gUGFubmluZyBJbmVydGlhIE9wdGlvbnNcblx0Ly8gQG9wdGlvbiBpbmVydGlhOiBCb29sZWFuID0gKlxuXHQvLyBJZiBlbmFibGVkLCBwYW5uaW5nIG9mIHRoZSBtYXAgd2lsbCBoYXZlIGFuIGluZXJ0aWEgZWZmZWN0IHdoZXJlXG5cdC8vIHRoZSBtYXAgYnVpbGRzIG1vbWVudHVtIHdoaWxlIGRyYWdnaW5nIGFuZCBjb250aW51ZXMgbW92aW5nIGluXG5cdC8vIHRoZSBzYW1lIGRpcmVjdGlvbiBmb3Igc29tZSB0aW1lLiBGZWVscyBlc3BlY2lhbGx5IG5pY2Ugb24gdG91Y2hcblx0Ly8gZGV2aWNlcy4gRW5hYmxlZCBieSBkZWZhdWx0IHVubGVzcyBydW5uaW5nIG9uIG9sZCBBbmRyb2lkIGRldmljZXMuXG5cdGluZXJ0aWE6ICFMLkJyb3dzZXIuYW5kcm9pZDIzLFxuXG5cdC8vIEBvcHRpb24gaW5lcnRpYURlY2VsZXJhdGlvbjogTnVtYmVyID0gMzAwMFxuXHQvLyBUaGUgcmF0ZSB3aXRoIHdoaWNoIHRoZSBpbmVydGlhbCBtb3ZlbWVudCBzbG93cyBkb3duLCBpbiBwaXhlbHMvc2Vjb25kwrIuXG5cdGluZXJ0aWFEZWNlbGVyYXRpb246IDM0MDAsIC8vIHB4L3NeMlxuXG5cdC8vIEBvcHRpb24gaW5lcnRpYU1heFNwZWVkOiBOdW1iZXIgPSBJbmZpbml0eVxuXHQvLyBNYXggc3BlZWQgb2YgdGhlIGluZXJ0aWFsIG1vdmVtZW50LCBpbiBwaXhlbHMvc2Vjb25kLlxuXHRpbmVydGlhTWF4U3BlZWQ6IEluZmluaXR5LCAvLyBweC9zXG5cblx0Ly8gQG9wdGlvbiBlYXNlTGluZWFyaXR5OiBOdW1iZXIgPSAwLjJcblx0ZWFzZUxpbmVhcml0eTogMC4yLFxuXG5cdC8vIFRPRE8gcmVmYWN0b3IsIG1vdmUgdG8gQ1JTXG5cdC8vIEBvcHRpb24gd29ybGRDb3B5SnVtcDogQm9vbGVhbiA9IGZhbHNlXG5cdC8vIFdpdGggdGhpcyBvcHRpb24gZW5hYmxlZCwgdGhlIG1hcCB0cmFja3Mgd2hlbiB5b3UgcGFuIHRvIGFub3RoZXIgXCJjb3B5XCJcblx0Ly8gb2YgdGhlIHdvcmxkIGFuZCBzZWFtbGVzc2x5IGp1bXBzIHRvIHRoZSBvcmlnaW5hbCBvbmUgc28gdGhhdCBhbGwgb3ZlcmxheXNcblx0Ly8gbGlrZSBtYXJrZXJzIGFuZCB2ZWN0b3IgbGF5ZXJzIGFyZSBzdGlsbCB2aXNpYmxlLlxuXHR3b3JsZENvcHlKdW1wOiBmYWxzZSxcblxuXHQvLyBAb3B0aW9uIG1heEJvdW5kc1Zpc2Nvc2l0eTogTnVtYmVyID0gMC4wXG5cdC8vIElmIGBtYXhCb3VuZHNgIGlzIHNldCwgdGhpcyBvcHRpb24gd2lsbCBjb250cm9sIGhvdyBzb2xpZCB0aGUgYm91bmRzXG5cdC8vIGFyZSB3aGVuIGRyYWdnaW5nIHRoZSBtYXAgYXJvdW5kLiBUaGUgZGVmYXVsdCB2YWx1ZSBvZiBgMC4wYCBhbGxvd3MgdGhlXG5cdC8vIHVzZXIgdG8gZHJhZyBvdXRzaWRlIHRoZSBib3VuZHMgYXQgbm9ybWFsIHNwZWVkLCBoaWdoZXIgdmFsdWVzIHdpbGxcblx0Ly8gc2xvdyBkb3duIG1hcCBkcmFnZ2luZyBvdXRzaWRlIGJvdW5kcywgYW5kIGAxLjBgIG1ha2VzIHRoZSBib3VuZHMgZnVsbHlcblx0Ly8gc29saWQsIHByZXZlbnRpbmcgdGhlIHVzZXIgZnJvbSBkcmFnZ2luZyBvdXRzaWRlIHRoZSBib3VuZHMuXG5cdG1heEJvdW5kc1Zpc2Nvc2l0eTogMC4wXG59KTtcblxuTC5NYXAuRHJhZyA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fZHJhZ2dhYmxlKSB7XG5cdFx0XHR2YXIgbWFwID0gdGhpcy5fbWFwO1xuXG5cdFx0XHR0aGlzLl9kcmFnZ2FibGUgPSBuZXcgTC5EcmFnZ2FibGUobWFwLl9tYXBQYW5lLCBtYXAuX2NvbnRhaW5lcik7XG5cblx0XHRcdHRoaXMuX2RyYWdnYWJsZS5vbih7XG5cdFx0XHRcdGRvd246IHRoaXMuX29uRG93bixcblx0XHRcdFx0ZHJhZ3N0YXJ0OiB0aGlzLl9vbkRyYWdTdGFydCxcblx0XHRcdFx0ZHJhZzogdGhpcy5fb25EcmFnLFxuXHRcdFx0XHRkcmFnZW5kOiB0aGlzLl9vbkRyYWdFbmRcblx0XHRcdH0sIHRoaXMpO1xuXG5cdFx0XHR0aGlzLl9kcmFnZ2FibGUub24oJ3ByZWRyYWcnLCB0aGlzLl9vblByZURyYWdMaW1pdCwgdGhpcyk7XG5cdFx0XHRpZiAobWFwLm9wdGlvbnMud29ybGRDb3B5SnVtcCkge1xuXHRcdFx0XHR0aGlzLl9kcmFnZ2FibGUub24oJ3ByZWRyYWcnLCB0aGlzLl9vblByZURyYWdXcmFwLCB0aGlzKTtcblx0XHRcdFx0bWFwLm9uKCd6b29tZW5kJywgdGhpcy5fb25ab29tRW5kLCB0aGlzKTtcblxuXHRcdFx0XHRtYXAud2hlblJlYWR5KHRoaXMuX29uWm9vbUVuZCwgdGhpcyk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtZ3JhYiBsZWFmbGV0LXRvdWNoLWRyYWcnKTtcblx0XHR0aGlzLl9kcmFnZ2FibGUuZW5hYmxlKCk7XG5cdFx0dGhpcy5fcG9zaXRpb25zID0gW107XG5cdFx0dGhpcy5fdGltZXMgPSBbXTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtZ3JhYicpO1xuXHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ2xlYWZsZXQtdG91Y2gtZHJhZycpO1xuXHRcdHRoaXMuX2RyYWdnYWJsZS5kaXNhYmxlKCk7XG5cdH0sXG5cblx0bW92ZWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fZHJhZ2dhYmxlICYmIHRoaXMuX2RyYWdnYWJsZS5fbW92ZWQ7XG5cdH0sXG5cblx0bW92aW5nOiBmdW5jdGlvbiAoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2RyYWdnYWJsZSAmJiB0aGlzLl9kcmFnZ2FibGUuX21vdmluZztcblx0fSxcblxuXHRfb25Eb3duOiBmdW5jdGlvbiAoKSB7XG5cdFx0dGhpcy5fbWFwLl9zdG9wKCk7XG5cdH0sXG5cblx0X29uRHJhZ1N0YXJ0OiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcDtcblxuXHRcdGlmICh0aGlzLl9tYXAub3B0aW9ucy5tYXhCb3VuZHMgJiYgdGhpcy5fbWFwLm9wdGlvbnMubWF4Qm91bmRzVmlzY29zaXR5KSB7XG5cdFx0XHR2YXIgYm91bmRzID0gTC5sYXRMbmdCb3VuZHModGhpcy5fbWFwLm9wdGlvbnMubWF4Qm91bmRzKTtcblxuXHRcdFx0dGhpcy5fb2Zmc2V0TGltaXQgPSBMLmJvdW5kcyhcblx0XHRcdFx0dGhpcy5fbWFwLmxhdExuZ1RvQ29udGFpbmVyUG9pbnQoYm91bmRzLmdldE5vcnRoV2VzdCgpKS5tdWx0aXBseUJ5KC0xKSxcblx0XHRcdFx0dGhpcy5fbWFwLmxhdExuZ1RvQ29udGFpbmVyUG9pbnQoYm91bmRzLmdldFNvdXRoRWFzdCgpKS5tdWx0aXBseUJ5KC0xKVxuXHRcdFx0XHRcdC5hZGQodGhpcy5fbWFwLmdldFNpemUoKSkpO1xuXG5cdFx0XHR0aGlzLl92aXNjb3NpdHkgPSBNYXRoLm1pbigxLjAsIE1hdGgubWF4KDAuMCwgdGhpcy5fbWFwLm9wdGlvbnMubWF4Qm91bmRzVmlzY29zaXR5KSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX29mZnNldExpbWl0ID0gbnVsbDtcblx0XHR9XG5cblx0XHRtYXBcblx0XHQgICAgLmZpcmUoJ21vdmVzdGFydCcpXG5cdFx0ICAgIC5maXJlKCdkcmFnc3RhcnQnKTtcblxuXHRcdGlmIChtYXAub3B0aW9ucy5pbmVydGlhKSB7XG5cdFx0XHR0aGlzLl9wb3NpdGlvbnMgPSBbXTtcblx0XHRcdHRoaXMuX3RpbWVzID0gW107XG5cdFx0fVxuXHR9LFxuXG5cdF9vbkRyYWc6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKHRoaXMuX21hcC5vcHRpb25zLmluZXJ0aWEpIHtcblx0XHRcdHZhciB0aW1lID0gdGhpcy5fbGFzdFRpbWUgPSArbmV3IERhdGUoKSxcblx0XHRcdCAgICBwb3MgPSB0aGlzLl9sYXN0UG9zID0gdGhpcy5fZHJhZ2dhYmxlLl9hYnNQb3MgfHwgdGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3M7XG5cblx0XHRcdHRoaXMuX3Bvc2l0aW9ucy5wdXNoKHBvcyk7XG5cdFx0XHR0aGlzLl90aW1lcy5wdXNoKHRpbWUpO1xuXG5cdFx0XHRpZiAodGltZSAtIHRoaXMuX3RpbWVzWzBdID4gNTApIHtcblx0XHRcdFx0dGhpcy5fcG9zaXRpb25zLnNoaWZ0KCk7XG5cdFx0XHRcdHRoaXMuX3RpbWVzLnNoaWZ0KCk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dGhpcy5fbWFwXG5cdFx0ICAgIC5maXJlKCdtb3ZlJywgZSlcblx0XHQgICAgLmZpcmUoJ2RyYWcnLCBlKTtcblx0fSxcblxuXHRfb25ab29tRW5kOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIHB4Q2VudGVyID0gdGhpcy5fbWFwLmdldFNpemUoKS5kaXZpZGVCeSgyKSxcblx0XHQgICAgcHhXb3JsZENlbnRlciA9IHRoaXMuX21hcC5sYXRMbmdUb0xheWVyUG9pbnQoWzAsIDBdKTtcblxuXHRcdHRoaXMuX2luaXRpYWxXb3JsZE9mZnNldCA9IHB4V29ybGRDZW50ZXIuc3VidHJhY3QocHhDZW50ZXIpLng7XG5cdFx0dGhpcy5fd29ybGRXaWR0aCA9IHRoaXMuX21hcC5nZXRQaXhlbFdvcmxkQm91bmRzKCkuZ2V0U2l6ZSgpLng7XG5cdH0sXG5cblx0X3Zpc2NvdXNMaW1pdDogZnVuY3Rpb24gKHZhbHVlLCB0aHJlc2hvbGQpIHtcblx0XHRyZXR1cm4gdmFsdWUgLSAodmFsdWUgLSB0aHJlc2hvbGQpICogdGhpcy5fdmlzY29zaXR5O1xuXHR9LFxuXG5cdF9vblByZURyYWdMaW1pdDogZnVuY3Rpb24gKCkge1xuXHRcdGlmICghdGhpcy5fdmlzY29zaXR5IHx8ICF0aGlzLl9vZmZzZXRMaW1pdCkgeyByZXR1cm47IH1cblxuXHRcdHZhciBvZmZzZXQgPSB0aGlzLl9kcmFnZ2FibGUuX25ld1Bvcy5zdWJ0cmFjdCh0aGlzLl9kcmFnZ2FibGUuX3N0YXJ0UG9zKTtcblxuXHRcdHZhciBsaW1pdCA9IHRoaXMuX29mZnNldExpbWl0O1xuXHRcdGlmIChvZmZzZXQueCA8IGxpbWl0Lm1pbi54KSB7IG9mZnNldC54ID0gdGhpcy5fdmlzY291c0xpbWl0KG9mZnNldC54LCBsaW1pdC5taW4ueCk7IH1cblx0XHRpZiAob2Zmc2V0LnkgPCBsaW1pdC5taW4ueSkgeyBvZmZzZXQueSA9IHRoaXMuX3Zpc2NvdXNMaW1pdChvZmZzZXQueSwgbGltaXQubWluLnkpOyB9XG5cdFx0aWYgKG9mZnNldC54ID4gbGltaXQubWF4LngpIHsgb2Zmc2V0LnggPSB0aGlzLl92aXNjb3VzTGltaXQob2Zmc2V0LngsIGxpbWl0Lm1heC54KTsgfVxuXHRcdGlmIChvZmZzZXQueSA+IGxpbWl0Lm1heC55KSB7IG9mZnNldC55ID0gdGhpcy5fdmlzY291c0xpbWl0KG9mZnNldC55LCBsaW1pdC5tYXgueSk7IH1cblxuXHRcdHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zID0gdGhpcy5fZHJhZ2dhYmxlLl9zdGFydFBvcy5hZGQob2Zmc2V0KTtcblx0fSxcblxuXHRfb25QcmVEcmFnV3JhcDogZnVuY3Rpb24gKCkge1xuXHRcdC8vIFRPRE8gcmVmYWN0b3IgdG8gYmUgYWJsZSB0byBhZGp1c3QgbWFwIHBhbmUgcG9zaXRpb24gYWZ0ZXIgem9vbVxuXHRcdHZhciB3b3JsZFdpZHRoID0gdGhpcy5fd29ybGRXaWR0aCxcblx0XHQgICAgaGFsZldpZHRoID0gTWF0aC5yb3VuZCh3b3JsZFdpZHRoIC8gMiksXG5cdFx0ICAgIGR4ID0gdGhpcy5faW5pdGlhbFdvcmxkT2Zmc2V0LFxuXHRcdCAgICB4ID0gdGhpcy5fZHJhZ2dhYmxlLl9uZXdQb3MueCxcblx0XHQgICAgbmV3WDEgPSAoeCAtIGhhbGZXaWR0aCArIGR4KSAlIHdvcmxkV2lkdGggKyBoYWxmV2lkdGggLSBkeCxcblx0XHQgICAgbmV3WDIgPSAoeCArIGhhbGZXaWR0aCArIGR4KSAlIHdvcmxkV2lkdGggLSBoYWxmV2lkdGggLSBkeCxcblx0XHQgICAgbmV3WCA9IE1hdGguYWJzKG5ld1gxICsgZHgpIDwgTWF0aC5hYnMobmV3WDIgKyBkeCkgPyBuZXdYMSA6IG5ld1gyO1xuXG5cdFx0dGhpcy5fZHJhZ2dhYmxlLl9hYnNQb3MgPSB0aGlzLl9kcmFnZ2FibGUuX25ld1Bvcy5jbG9uZSgpO1xuXHRcdHRoaXMuX2RyYWdnYWJsZS5fbmV3UG9zLnggPSBuZXdYO1xuXHR9LFxuXG5cdF9vbkRyYWdFbmQ6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgb3B0aW9ucyA9IG1hcC5vcHRpb25zLFxuXG5cdFx0ICAgIG5vSW5lcnRpYSA9ICFvcHRpb25zLmluZXJ0aWEgfHwgdGhpcy5fdGltZXMubGVuZ3RoIDwgMjtcblxuXHRcdG1hcC5maXJlKCdkcmFnZW5kJywgZSk7XG5cblx0XHRpZiAobm9JbmVydGlhKSB7XG5cdFx0XHRtYXAuZmlyZSgnbW92ZWVuZCcpO1xuXG5cdFx0fSBlbHNlIHtcblxuXHRcdFx0dmFyIGRpcmVjdGlvbiA9IHRoaXMuX2xhc3RQb3Muc3VidHJhY3QodGhpcy5fcG9zaXRpb25zWzBdKSxcblx0XHRcdCAgICBkdXJhdGlvbiA9ICh0aGlzLl9sYXN0VGltZSAtIHRoaXMuX3RpbWVzWzBdKSAvIDEwMDAsXG5cdFx0XHQgICAgZWFzZSA9IG9wdGlvbnMuZWFzZUxpbmVhcml0eSxcblxuXHRcdFx0ICAgIHNwZWVkVmVjdG9yID0gZGlyZWN0aW9uLm11bHRpcGx5QnkoZWFzZSAvIGR1cmF0aW9uKSxcblx0XHRcdCAgICBzcGVlZCA9IHNwZWVkVmVjdG9yLmRpc3RhbmNlVG8oWzAsIDBdKSxcblxuXHRcdFx0ICAgIGxpbWl0ZWRTcGVlZCA9IE1hdGgubWluKG9wdGlvbnMuaW5lcnRpYU1heFNwZWVkLCBzcGVlZCksXG5cdFx0XHQgICAgbGltaXRlZFNwZWVkVmVjdG9yID0gc3BlZWRWZWN0b3IubXVsdGlwbHlCeShsaW1pdGVkU3BlZWQgLyBzcGVlZCksXG5cblx0XHRcdCAgICBkZWNlbGVyYXRpb25EdXJhdGlvbiA9IGxpbWl0ZWRTcGVlZCAvIChvcHRpb25zLmluZXJ0aWFEZWNlbGVyYXRpb24gKiBlYXNlKSxcblx0XHRcdCAgICBvZmZzZXQgPSBsaW1pdGVkU3BlZWRWZWN0b3IubXVsdGlwbHlCeSgtZGVjZWxlcmF0aW9uRHVyYXRpb24gLyAyKS5yb3VuZCgpO1xuXG5cdFx0XHRpZiAoIW9mZnNldC54ICYmICFvZmZzZXQueSkge1xuXHRcdFx0XHRtYXAuZmlyZSgnbW92ZWVuZCcpO1xuXG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRvZmZzZXQgPSBtYXAuX2xpbWl0T2Zmc2V0KG9mZnNldCwgbWFwLm9wdGlvbnMubWF4Qm91bmRzKTtcblxuXHRcdFx0XHRMLlV0aWwucmVxdWVzdEFuaW1GcmFtZShmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdFx0bWFwLnBhbkJ5KG9mZnNldCwge1xuXHRcdFx0XHRcdFx0ZHVyYXRpb246IGRlY2VsZXJhdGlvbkR1cmF0aW9uLFxuXHRcdFx0XHRcdFx0ZWFzZUxpbmVhcml0eTogZWFzZSxcblx0XHRcdFx0XHRcdG5vTW92ZVN0YXJ0OiB0cnVlLFxuXHRcdFx0XHRcdFx0YW5pbWF0ZTogdHJ1ZVxuXHRcdFx0XHRcdH0pO1xuXHRcdFx0XHR9KTtcblx0XHRcdH1cblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IGRyYWdnaW5nOiBIYW5kbGVyXG4vLyBNYXAgZHJhZ2dpbmcgaGFuZGxlciAoYnkgYm90aCBtb3VzZSBhbmQgdG91Y2gpLlxuTC5NYXAuYWRkSW5pdEhvb2soJ2FkZEhhbmRsZXInLCAnZHJhZ2dpbmcnLCBMLk1hcC5EcmFnKTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuRG91YmxlQ2xpY2tab29tIGlzIHVzZWQgdG8gaGFuZGxlIGRvdWJsZS1jbGljayB6b29tIG9uIHRoZSBtYXAsIGVuYWJsZWQgYnkgZGVmYXVsdC5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAb3B0aW9uIGRvdWJsZUNsaWNrWm9vbTogQm9vbGVhbnxTdHJpbmcgPSB0cnVlXG5cdC8vIFdoZXRoZXIgdGhlIG1hcCBjYW4gYmUgem9vbWVkIGluIGJ5IGRvdWJsZSBjbGlja2luZyBvbiBpdCBhbmRcblx0Ly8gem9vbWVkIG91dCBieSBkb3VibGUgY2xpY2tpbmcgd2hpbGUgaG9sZGluZyBzaGlmdC4gSWYgcGFzc2VkXG5cdC8vIGAnY2VudGVyJ2AsIGRvdWJsZS1jbGljayB6b29tIHdpbGwgem9vbSB0byB0aGUgY2VudGVyIG9mIHRoZVxuXHQvLyAgdmlldyByZWdhcmRsZXNzIG9mIHdoZXJlIHRoZSBtb3VzZSB3YXMuXG5cdGRvdWJsZUNsaWNrWm9vbTogdHJ1ZVxufSk7XG5cbkwuTWFwLkRvdWJsZUNsaWNrWm9vbSA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX21hcC5vbignZGJsY2xpY2snLCB0aGlzLl9vbkRvdWJsZUNsaWNrLCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX21hcC5vZmYoJ2RibGNsaWNrJywgdGhpcy5fb25Eb3VibGVDbGljaywgdGhpcyk7XG5cdH0sXG5cblx0X29uRG91YmxlQ2xpY2s6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgb2xkWm9vbSA9IG1hcC5nZXRab29tKCksXG5cdFx0ICAgIGRlbHRhID0gbWFwLm9wdGlvbnMuem9vbURlbHRhLFxuXHRcdCAgICB6b29tID0gZS5vcmlnaW5hbEV2ZW50LnNoaWZ0S2V5ID8gb2xkWm9vbSAtIGRlbHRhIDogb2xkWm9vbSArIGRlbHRhO1xuXG5cdFx0aWYgKG1hcC5vcHRpb25zLmRvdWJsZUNsaWNrWm9vbSA9PT0gJ2NlbnRlcicpIHtcblx0XHRcdG1hcC5zZXRab29tKHpvb20pO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRtYXAuc2V0Wm9vbUFyb3VuZChlLmNvbnRhaW5lclBvaW50LCB6b29tKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy9cbi8vIE1hcCBwcm9wZXJ0aWVzIGluY2x1ZGUgaW50ZXJhY3Rpb24gaGFuZGxlcnMgdGhhdCBhbGxvdyB5b3UgdG8gY29udHJvbFxuLy8gaW50ZXJhY3Rpb24gYmVoYXZpb3IgaW4gcnVudGltZSwgZW5hYmxpbmcgb3IgZGlzYWJsaW5nIGNlcnRhaW4gZmVhdHVyZXMgc3VjaFxuLy8gYXMgZHJhZ2dpbmcgb3IgdG91Y2ggem9vbSAoc2VlIGBIYW5kbGVyYCBtZXRob2RzKS4gRm9yIGV4YW1wbGU6XG4vL1xuLy8gYGBganNcbi8vIG1hcC5kb3VibGVDbGlja1pvb20uZGlzYWJsZSgpO1xuLy8gYGBgXG4vL1xuLy8gQHByb3BlcnR5IGRvdWJsZUNsaWNrWm9vbTogSGFuZGxlclxuLy8gRG91YmxlIGNsaWNrIHpvb20gaGFuZGxlci5cbkwuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ2RvdWJsZUNsaWNrWm9vbScsIEwuTWFwLkRvdWJsZUNsaWNrWm9vbSk7XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLlNjcm9sbFdoZWVsWm9vbSBpcyB1c2VkIGJ5IEwuTWFwIHRvIGVuYWJsZSBtb3VzZSBzY3JvbGwgd2hlZWwgem9vbSBvbiB0aGUgbWFwLlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAc2VjdGlvbiBNb3VzZXdoZWVsIG9wdGlvbnNcblx0Ly8gQG9wdGlvbiBzY3JvbGxXaGVlbFpvb206IEJvb2xlYW58U3RyaW5nID0gdHJ1ZVxuXHQvLyBXaGV0aGVyIHRoZSBtYXAgY2FuIGJlIHpvb21lZCBieSB1c2luZyB0aGUgbW91c2Ugd2hlZWwuIElmIHBhc3NlZCBgJ2NlbnRlcidgLFxuXHQvLyBpdCB3aWxsIHpvb20gdG8gdGhlIGNlbnRlciBvZiB0aGUgdmlldyByZWdhcmRsZXNzIG9mIHdoZXJlIHRoZSBtb3VzZSB3YXMuXG5cdHNjcm9sbFdoZWVsWm9vbTogdHJ1ZSxcblxuXHQvLyBAb3B0aW9uIHdoZWVsRGVib3VuY2VUaW1lOiBOdW1iZXIgPSA0MFxuXHQvLyBMaW1pdHMgdGhlIHJhdGUgYXQgd2hpY2ggYSB3aGVlbCBjYW4gZmlyZSAoaW4gbWlsbGlzZWNvbmRzKS4gQnkgZGVmYXVsdFxuXHQvLyB1c2VyIGNhbid0IHpvb20gdmlhIHdoZWVsIG1vcmUgb2Z0ZW4gdGhhbiBvbmNlIHBlciA0MCBtcy5cblx0d2hlZWxEZWJvdW5jZVRpbWU6IDQwLFxuXG5cdC8vIEBvcHRpb24gd2hlZWxQeFBlclpvb21MZXZlbDogTnVtYmVyID0gNjBcblx0Ly8gSG93IG1hbnkgc2Nyb2xsIHBpeGVscyAoYXMgcmVwb3J0ZWQgYnkgW0wuRG9tRXZlbnQuZ2V0V2hlZWxEZWx0YV0oI2RvbWV2ZW50LWdldHdoZWVsZGVsdGEpKVxuXHQvLyBtZWFuIGEgY2hhbmdlIG9mIG9uZSBmdWxsIHpvb20gbGV2ZWwuIFNtYWxsZXIgdmFsdWVzIHdpbGwgbWFrZSB3aGVlbC16b29taW5nXG5cdC8vIGZhc3RlciAoYW5kIHZpY2UgdmVyc2EpLlxuXHR3aGVlbFB4UGVyWm9vbUxldmVsOiA2MFxufSk7XG5cbkwuTWFwLlNjcm9sbFdoZWVsWm9vbSA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub24odGhpcy5fbWFwLl9jb250YWluZXIsICdtb3VzZXdoZWVsJywgdGhpcy5fb25XaGVlbFNjcm9sbCwgdGhpcyk7XG5cblx0XHR0aGlzLl9kZWx0YSA9IDA7XG5cdH0sXG5cblx0cmVtb3ZlSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ21vdXNld2hlZWwnLCB0aGlzLl9vbldoZWVsU2Nyb2xsLCB0aGlzKTtcblx0fSxcblxuXHRfb25XaGVlbFNjcm9sbDogZnVuY3Rpb24gKGUpIHtcblx0XHR2YXIgZGVsdGEgPSBMLkRvbUV2ZW50LmdldFdoZWVsRGVsdGEoZSk7XG5cblx0XHR2YXIgZGVib3VuY2UgPSB0aGlzLl9tYXAub3B0aW9ucy53aGVlbERlYm91bmNlVGltZTtcblxuXHRcdHRoaXMuX2RlbHRhICs9IGRlbHRhO1xuXHRcdHRoaXMuX2xhc3RNb3VzZVBvcyA9IHRoaXMuX21hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlKTtcblxuXHRcdGlmICghdGhpcy5fc3RhcnRUaW1lKSB7XG5cdFx0XHR0aGlzLl9zdGFydFRpbWUgPSArbmV3IERhdGUoKTtcblx0XHR9XG5cblx0XHR2YXIgbGVmdCA9IE1hdGgubWF4KGRlYm91bmNlIC0gKCtuZXcgRGF0ZSgpIC0gdGhpcy5fc3RhcnRUaW1lKSwgMCk7XG5cblx0XHRjbGVhclRpbWVvdXQodGhpcy5fdGltZXIpO1xuXHRcdHRoaXMuX3RpbWVyID0gc2V0VGltZW91dChMLmJpbmQodGhpcy5fcGVyZm9ybVpvb20sIHRoaXMpLCBsZWZ0KTtcblxuXHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcblx0fSxcblxuXHRfcGVyZm9ybVpvb206IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICB6b29tID0gbWFwLmdldFpvb20oKSxcblx0XHQgICAgc25hcCA9IHRoaXMuX21hcC5vcHRpb25zLnpvb21TbmFwIHx8IDA7XG5cblx0XHRtYXAuX3N0b3AoKTsgLy8gc3RvcCBwYW5uaW5nIGFuZCBmbHkgYW5pbWF0aW9ucyBpZiBhbnlcblxuXHRcdC8vIG1hcCB0aGUgZGVsdGEgd2l0aCBhIHNpZ21vaWQgZnVuY3Rpb24gdG8gLTQuLjQgcmFuZ2UgbGVhbmluZyBvbiAtMS4uMVxuXHRcdHZhciBkMiA9IHRoaXMuX2RlbHRhIC8gKHRoaXMuX21hcC5vcHRpb25zLndoZWVsUHhQZXJab29tTGV2ZWwgKiA0KSxcblx0XHQgICAgZDMgPSA0ICogTWF0aC5sb2coMiAvICgxICsgTWF0aC5leHAoLU1hdGguYWJzKGQyKSkpKSAvIE1hdGguTE4yLFxuXHRcdCAgICBkNCA9IHNuYXAgPyBNYXRoLmNlaWwoZDMgLyBzbmFwKSAqIHNuYXAgOiBkMyxcblx0XHQgICAgZGVsdGEgPSBtYXAuX2xpbWl0Wm9vbSh6b29tICsgKHRoaXMuX2RlbHRhID4gMCA/IGQ0IDogLWQ0KSkgLSB6b29tO1xuXG5cdFx0dGhpcy5fZGVsdGEgPSAwO1xuXHRcdHRoaXMuX3N0YXJ0VGltZSA9IG51bGw7XG5cblx0XHRpZiAoIWRlbHRhKSB7IHJldHVybjsgfVxuXG5cdFx0aWYgKG1hcC5vcHRpb25zLnNjcm9sbFdoZWVsWm9vbSA9PT0gJ2NlbnRlcicpIHtcblx0XHRcdG1hcC5zZXRab29tKHpvb20gKyBkZWx0YSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdG1hcC5zZXRab29tQXJvdW5kKHRoaXMuX2xhc3RNb3VzZVBvcywgem9vbSArIGRlbHRhKTtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IHNjcm9sbFdoZWVsWm9vbTogSGFuZGxlclxuLy8gU2Nyb2xsIHdoZWVsIHpvb20gaGFuZGxlci5cbkwuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ3Njcm9sbFdoZWVsWm9vbScsIEwuTWFwLlNjcm9sbFdoZWVsWm9vbSk7XG5cblxuXG4vKlxyXG4gKiBFeHRlbmRzIHRoZSBldmVudCBoYW5kbGluZyBjb2RlIHdpdGggZG91YmxlIHRhcCBzdXBwb3J0IGZvciBtb2JpbGUgYnJvd3NlcnMuXHJcbiAqL1xyXG5cclxuTC5leHRlbmQoTC5Eb21FdmVudCwge1xyXG5cclxuXHRfdG91Y2hzdGFydDogTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJEb3duJyA6IEwuQnJvd3Nlci5wb2ludGVyID8gJ3BvaW50ZXJkb3duJyA6ICd0b3VjaHN0YXJ0JyxcclxuXHRfdG91Y2hlbmQ6IEwuQnJvd3Nlci5tc1BvaW50ZXIgPyAnTVNQb2ludGVyVXAnIDogTC5Ccm93c2VyLnBvaW50ZXIgPyAncG9pbnRlcnVwJyA6ICd0b3VjaGVuZCcsXHJcblxyXG5cdC8vIGluc3BpcmVkIGJ5IFplcHRvIHRvdWNoIGNvZGUgYnkgVGhvbWFzIEZ1Y2hzXHJcblx0YWRkRG91YmxlVGFwTGlzdGVuZXI6IGZ1bmN0aW9uIChvYmosIGhhbmRsZXIsIGlkKSB7XHJcblx0XHR2YXIgbGFzdCwgdG91Y2gsXHJcblx0XHQgICAgZG91YmxlVGFwID0gZmFsc2UsXHJcblx0XHQgICAgZGVsYXkgPSAyNTA7XHJcblxyXG5cdFx0ZnVuY3Rpb24gb25Ub3VjaFN0YXJ0KGUpIHtcclxuXHRcdFx0dmFyIGNvdW50O1xyXG5cclxuXHRcdFx0aWYgKEwuQnJvd3Nlci5wb2ludGVyKSB7XHJcblx0XHRcdFx0aWYgKCghTC5Ccm93c2VyLmVkZ2UpIHx8IGUucG9pbnRlclR5cGUgPT09ICdtb3VzZScpIHsgcmV0dXJuOyB9XHJcblx0XHRcdFx0Y291bnQgPSBMLkRvbUV2ZW50Ll9wb2ludGVyc0NvdW50O1xyXG5cdFx0XHR9IGVsc2Uge1xyXG5cdFx0XHRcdGNvdW50ID0gZS50b3VjaGVzLmxlbmd0aDtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0aWYgKGNvdW50ID4gMSkgeyByZXR1cm47IH1cclxuXHJcblx0XHRcdHZhciBub3cgPSBEYXRlLm5vdygpLFxyXG5cdFx0XHQgICAgZGVsdGEgPSBub3cgLSAobGFzdCB8fCBub3cpO1xyXG5cclxuXHRcdFx0dG91Y2ggPSBlLnRvdWNoZXMgPyBlLnRvdWNoZXNbMF0gOiBlO1xyXG5cdFx0XHRkb3VibGVUYXAgPSAoZGVsdGEgPiAwICYmIGRlbHRhIDw9IGRlbGF5KTtcclxuXHRcdFx0bGFzdCA9IG5vdztcclxuXHRcdH1cclxuXHJcblx0XHRmdW5jdGlvbiBvblRvdWNoRW5kKGUpIHtcclxuXHRcdFx0aWYgKGRvdWJsZVRhcCAmJiAhdG91Y2guY2FuY2VsQnViYmxlKSB7XHJcblx0XHRcdFx0aWYgKEwuQnJvd3Nlci5wb2ludGVyKSB7XHJcblx0XHRcdFx0XHRpZiAoKCFMLkJyb3dzZXIuZWRnZSkgfHwgZS5wb2ludGVyVHlwZSA9PT0gJ21vdXNlJykgeyByZXR1cm47IH1cclxuXHJcblx0XHRcdFx0XHQvLyB3b3JrIGFyb3VuZCAudHlwZSBiZWluZyByZWFkb25seSB3aXRoIE1TUG9pbnRlciogZXZlbnRzXHJcblx0XHRcdFx0XHR2YXIgbmV3VG91Y2ggPSB7fSxcclxuXHRcdFx0XHRcdCAgICBwcm9wLCBpO1xyXG5cclxuXHRcdFx0XHRcdGZvciAoaSBpbiB0b3VjaCkge1xyXG5cdFx0XHRcdFx0XHRwcm9wID0gdG91Y2hbaV07XHJcblx0XHRcdFx0XHRcdG5ld1RvdWNoW2ldID0gcHJvcCAmJiBwcm9wLmJpbmQgPyBwcm9wLmJpbmQodG91Y2gpIDogcHJvcDtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHRcdHRvdWNoID0gbmV3VG91Y2g7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHRcdHRvdWNoLnR5cGUgPSAnZGJsY2xpY2snO1xyXG5cdFx0XHRcdGhhbmRsZXIodG91Y2gpO1xyXG5cdFx0XHRcdGxhc3QgPSBudWxsO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIHByZSA9ICdfbGVhZmxldF8nLFxyXG5cdFx0ICAgIHRvdWNoc3RhcnQgPSB0aGlzLl90b3VjaHN0YXJ0LFxyXG5cdFx0ICAgIHRvdWNoZW5kID0gdGhpcy5fdG91Y2hlbmQ7XHJcblxyXG5cdFx0b2JqW3ByZSArIHRvdWNoc3RhcnQgKyBpZF0gPSBvblRvdWNoU3RhcnQ7XHJcblx0XHRvYmpbcHJlICsgdG91Y2hlbmQgKyBpZF0gPSBvblRvdWNoRW5kO1xyXG5cdFx0b2JqW3ByZSArICdkYmxjbGljaycgKyBpZF0gPSBoYW5kbGVyO1xyXG5cclxuXHRcdG9iai5hZGRFdmVudExpc3RlbmVyKHRvdWNoc3RhcnQsIG9uVG91Y2hTdGFydCwgZmFsc2UpO1xyXG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodG91Y2hlbmQsIG9uVG91Y2hFbmQsIGZhbHNlKTtcclxuXHJcblx0XHQvLyBPbiBzb21lIHBsYXRmb3JtcyAobm90YWJseSwgY2hyb21lPDU1IG9uIHdpbjEwICsgdG91Y2hzY3JlZW4gKyBtb3VzZSksXHJcblx0XHQvLyB0aGUgYnJvd3NlciBkb2Vzbid0IGZpcmUgdG91Y2hlbmQvcG9pbnRlcnVwIGV2ZW50cyBidXQgZG9lcyBmaXJlXHJcblx0XHQvLyBuYXRpdmUgZGJsY2xpY2tzLiBTZWUgIzQxMjcuXHJcblx0XHQvLyBFZGdlIDE0IGFsc28gZmlyZXMgbmF0aXZlIGRibGNsaWNrcywgYnV0IG9ubHkgZm9yIHBvaW50ZXJUeXBlIG1vdXNlLCBzZWUgIzUxODAuXHJcblx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcignZGJsY2xpY2snLCBoYW5kbGVyLCBmYWxzZSk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0cmVtb3ZlRG91YmxlVGFwTGlzdGVuZXI6IGZ1bmN0aW9uIChvYmosIGlkKSB7XHJcblx0XHR2YXIgcHJlID0gJ19sZWFmbGV0XycsXHJcblx0XHQgICAgdG91Y2hzdGFydCA9IG9ialtwcmUgKyB0aGlzLl90b3VjaHN0YXJ0ICsgaWRdLFxyXG5cdFx0ICAgIHRvdWNoZW5kID0gb2JqW3ByZSArIHRoaXMuX3RvdWNoZW5kICsgaWRdLFxyXG5cdFx0ICAgIGRibGNsaWNrID0gb2JqW3ByZSArICdkYmxjbGljaycgKyBpZF07XHJcblxyXG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5fdG91Y2hzdGFydCwgdG91Y2hzdGFydCwgZmFsc2UpO1xyXG5cdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5fdG91Y2hlbmQsIHRvdWNoZW5kLCBmYWxzZSk7XHJcblx0XHRpZiAoIUwuQnJvd3Nlci5lZGdlKSB7XHJcblx0XHRcdG9iai5yZW1vdmVFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGRibGNsaWNrLCBmYWxzZSk7XHJcblx0XHR9XHJcblxyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fVxyXG59KTtcclxuXG5cblxuLypcbiAqIEV4dGVuZHMgTC5Eb21FdmVudCB0byBwcm92aWRlIHRvdWNoIHN1cHBvcnQgZm9yIEludGVybmV0IEV4cGxvcmVyIGFuZCBXaW5kb3dzLWJhc2VkIGRldmljZXMuXG4gKi9cblxuTC5leHRlbmQoTC5Eb21FdmVudCwge1xuXG5cdFBPSU5URVJfRE9XTjogICBMLkJyb3dzZXIubXNQb2ludGVyID8gJ01TUG9pbnRlckRvd24nICAgOiAncG9pbnRlcmRvd24nLFxuXHRQT0lOVEVSX01PVkU6ICAgTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJNb3ZlJyAgIDogJ3BvaW50ZXJtb3ZlJyxcblx0UE9JTlRFUl9VUDogICAgIEwuQnJvd3Nlci5tc1BvaW50ZXIgPyAnTVNQb2ludGVyVXAnICAgICA6ICdwb2ludGVydXAnLFxuXHRQT0lOVEVSX0NBTkNFTDogTC5Ccm93c2VyLm1zUG9pbnRlciA/ICdNU1BvaW50ZXJDYW5jZWwnIDogJ3BvaW50ZXJjYW5jZWwnLFxuXHRUQUdfV0hJVEVfTElTVDogWydJTlBVVCcsICdTRUxFQ1QnLCAnT1BUSU9OJ10sXG5cblx0X3BvaW50ZXJzOiB7fSxcblx0X3BvaW50ZXJzQ291bnQ6IDAsXG5cblx0Ly8gUHJvdmlkZXMgYSB0b3VjaCBldmVudHMgd3JhcHBlciBmb3IgKG1zKXBvaW50ZXIgZXZlbnRzLlxuXHQvLyByZWYgaHR0cDovL3d3dy53My5vcmcvVFIvcG9pbnRlcmV2ZW50cy8gaHR0cHM6Ly93d3cudzMub3JnL0J1Z3MvUHVibGljL3Nob3dfYnVnLmNnaT9pZD0yMjg5MFxuXG5cdGFkZFBvaW50ZXJMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgaGFuZGxlciwgaWQpIHtcblxuXHRcdGlmICh0eXBlID09PSAndG91Y2hzdGFydCcpIHtcblx0XHRcdHRoaXMuX2FkZFBvaW50ZXJTdGFydChvYmosIGhhbmRsZXIsIGlkKTtcblxuXHRcdH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNobW92ZScpIHtcblx0XHRcdHRoaXMuX2FkZFBvaW50ZXJNb3ZlKG9iaiwgaGFuZGxlciwgaWQpO1xuXG5cdFx0fSBlbHNlIGlmICh0eXBlID09PSAndG91Y2hlbmQnKSB7XG5cdFx0XHR0aGlzLl9hZGRQb2ludGVyRW5kKG9iaiwgaGFuZGxlciwgaWQpO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdHJlbW92ZVBvaW50ZXJMaXN0ZW5lcjogZnVuY3Rpb24gKG9iaiwgdHlwZSwgaWQpIHtcblx0XHR2YXIgaGFuZGxlciA9IG9ialsnX2xlYWZsZXRfJyArIHR5cGUgKyBpZF07XG5cblx0XHRpZiAodHlwZSA9PT0gJ3RvdWNoc3RhcnQnKSB7XG5cdFx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfRE9XTiwgaGFuZGxlciwgZmFsc2UpO1xuXG5cdFx0fSBlbHNlIGlmICh0eXBlID09PSAndG91Y2htb3ZlJykge1xuXHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX01PVkUsIGhhbmRsZXIsIGZhbHNlKTtcblxuXHRcdH0gZWxzZSBpZiAodHlwZSA9PT0gJ3RvdWNoZW5kJykge1xuXHRcdFx0b2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX1VQLCBoYW5kbGVyLCBmYWxzZSk7XG5cdFx0XHRvYmoucmVtb3ZlRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfQ0FOQ0VMLCBoYW5kbGVyLCBmYWxzZSk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X2FkZFBvaW50ZXJTdGFydDogZnVuY3Rpb24gKG9iaiwgaGFuZGxlciwgaWQpIHtcblx0XHR2YXIgb25Eb3duID0gTC5iaW5kKGZ1bmN0aW9uIChlKSB7XG5cdFx0XHRpZiAoZS5wb2ludGVyVHlwZSAhPT0gJ21vdXNlJyAmJiBlLk1TUE9JTlRFUl9UWVBFX01PVVNFICYmIGUucG9pbnRlclR5cGUgIT09IGUuTVNQT0lOVEVSX1RZUEVfTU9VU0UpIHtcblx0XHRcdFx0Ly8gSW4gSUUxMSwgc29tZSB0b3VjaCBldmVudHMgbmVlZHMgdG8gZmlyZSBmb3IgZm9ybSBjb250cm9scywgb3Jcblx0XHRcdFx0Ly8gdGhlIGNvbnRyb2xzIHdpbGwgc3RvcCB3b3JraW5nLiBXZSBrZWVwIGEgd2hpdGVsaXN0IG9mIHRhZyBuYW1lcyB0aGF0XG5cdFx0XHRcdC8vIG5lZWQgdGhlc2UgZXZlbnRzLiBGb3Igb3RoZXIgdGFyZ2V0IHRhZ3MsIHdlIHByZXZlbnQgZGVmYXVsdCBvbiB0aGUgZXZlbnQuXG5cdFx0XHRcdGlmICh0aGlzLlRBR19XSElURV9MSVNULmluZGV4T2YoZS50YXJnZXQudGFnTmFtZSkgPCAwKSB7XG5cdFx0XHRcdFx0TC5Eb21FdmVudC5wcmV2ZW50RGVmYXVsdChlKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0dGhpcy5faGFuZGxlUG9pbnRlcihlLCBoYW5kbGVyKTtcblx0XHR9LCB0aGlzKTtcblxuXHRcdG9ialsnX2xlYWZsZXRfdG91Y2hzdGFydCcgKyBpZF0gPSBvbkRvd247XG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX0RPV04sIG9uRG93biwgZmFsc2UpO1xuXG5cdFx0Ly8gbmVlZCB0byBrZWVwIHRyYWNrIG9mIHdoYXQgcG9pbnRlcnMgYW5kIGhvdyBtYW55IGFyZSBhY3RpdmUgdG8gcHJvdmlkZSBlLnRvdWNoZXMgZW11bGF0aW9uXG5cdFx0aWYgKCF0aGlzLl9wb2ludGVyRG9jTGlzdGVuZXIpIHtcblx0XHRcdHZhciBwb2ludGVyVXAgPSBMLmJpbmQodGhpcy5fZ2xvYmFsUG9pbnRlclVwLCB0aGlzKTtcblxuXHRcdFx0Ly8gd2UgbGlzdGVuIGRvY3VtZW50RWxlbWVudCBhcyBhbnkgZHJhZ3MgdGhhdCBlbmQgYnkgbW92aW5nIHRoZSB0b3VjaCBvZmYgdGhlIHNjcmVlbiBnZXQgZmlyZWQgdGhlcmVcblx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9ET1dOLCBMLmJpbmQodGhpcy5fZ2xvYmFsUG9pbnRlckRvd24sIHRoaXMpLCB0cnVlKTtcblx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9NT1ZFLCBMLmJpbmQodGhpcy5fZ2xvYmFsUG9pbnRlck1vdmUsIHRoaXMpLCB0cnVlKTtcblx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9VUCwgcG9pbnRlclVwLCB0cnVlKTtcblx0XHRcdGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHRoaXMuUE9JTlRFUl9DQU5DRUwsIHBvaW50ZXJVcCwgdHJ1ZSk7XG5cblx0XHRcdHRoaXMuX3BvaW50ZXJEb2NMaXN0ZW5lciA9IHRydWU7XG5cdFx0fVxuXHR9LFxuXG5cdF9nbG9iYWxQb2ludGVyRG93bjogZnVuY3Rpb24gKGUpIHtcblx0XHR0aGlzLl9wb2ludGVyc1tlLnBvaW50ZXJJZF0gPSBlO1xuXHRcdHRoaXMuX3BvaW50ZXJzQ291bnQrKztcblx0fSxcblxuXHRfZ2xvYmFsUG9pbnRlck1vdmU6IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKHRoaXMuX3BvaW50ZXJzW2UucG9pbnRlcklkXSkge1xuXHRcdFx0dGhpcy5fcG9pbnRlcnNbZS5wb2ludGVySWRdID0gZTtcblx0XHR9XG5cdH0sXG5cblx0X2dsb2JhbFBvaW50ZXJVcDogZnVuY3Rpb24gKGUpIHtcblx0XHRkZWxldGUgdGhpcy5fcG9pbnRlcnNbZS5wb2ludGVySWRdO1xuXHRcdHRoaXMuX3BvaW50ZXJzQ291bnQtLTtcblx0fSxcblxuXHRfaGFuZGxlUG9pbnRlcjogZnVuY3Rpb24gKGUsIGhhbmRsZXIpIHtcblx0XHRlLnRvdWNoZXMgPSBbXTtcblx0XHRmb3IgKHZhciBpIGluIHRoaXMuX3BvaW50ZXJzKSB7XG5cdFx0XHRlLnRvdWNoZXMucHVzaCh0aGlzLl9wb2ludGVyc1tpXSk7XG5cdFx0fVxuXHRcdGUuY2hhbmdlZFRvdWNoZXMgPSBbZV07XG5cblx0XHRoYW5kbGVyKGUpO1xuXHR9LFxuXG5cdF9hZGRQb2ludGVyTW92ZTogZnVuY3Rpb24gKG9iaiwgaGFuZGxlciwgaWQpIHtcblx0XHR2YXIgb25Nb3ZlID0gTC5iaW5kKGZ1bmN0aW9uIChlKSB7XG5cdFx0XHQvLyBkb24ndCBmaXJlIHRvdWNoIG1vdmVzIHdoZW4gbW91c2UgaXNuJ3QgZG93blxuXHRcdFx0aWYgKChlLnBvaW50ZXJUeXBlID09PSBlLk1TUE9JTlRFUl9UWVBFX01PVVNFIHx8IGUucG9pbnRlclR5cGUgPT09ICdtb3VzZScpICYmIGUuYnV0dG9ucyA9PT0gMCkgeyByZXR1cm47IH1cblxuXHRcdFx0dGhpcy5faGFuZGxlUG9pbnRlcihlLCBoYW5kbGVyKTtcblx0XHR9LCB0aGlzKTtcblxuXHRcdG9ialsnX2xlYWZsZXRfdG91Y2htb3ZlJyArIGlkXSA9IG9uTW92ZTtcblx0XHRvYmouYWRkRXZlbnRMaXN0ZW5lcih0aGlzLlBPSU5URVJfTU9WRSwgb25Nb3ZlLCBmYWxzZSk7XG5cdH0sXG5cblx0X2FkZFBvaW50ZXJFbmQ6IGZ1bmN0aW9uIChvYmosIGhhbmRsZXIsIGlkKSB7XG5cdFx0dmFyIG9uVXAgPSBMLmJpbmQoZnVuY3Rpb24gKGUpIHtcblx0XHRcdHRoaXMuX2hhbmRsZVBvaW50ZXIoZSwgaGFuZGxlcik7XG5cdFx0fSwgdGhpcyk7XG5cblx0XHRvYmpbJ19sZWFmbGV0X3RvdWNoZW5kJyArIGlkXSA9IG9uVXA7XG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX1VQLCBvblVwLCBmYWxzZSk7XG5cdFx0b2JqLmFkZEV2ZW50TGlzdGVuZXIodGhpcy5QT0lOVEVSX0NBTkNFTCwgb25VcCwgZmFsc2UpO1xuXHR9XG59KTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuVG91Y2hab29tIGlzIHVzZWQgYnkgTC5NYXAgdG8gYWRkIHBpbmNoIHpvb20gb24gc3VwcG9ydGVkIG1vYmlsZSBicm93c2Vycy5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gSW50ZXJhY3Rpb24gT3B0aW9uc1xuTC5NYXAubWVyZ2VPcHRpb25zKHtcblx0Ly8gQHNlY3Rpb24gVG91Y2ggaW50ZXJhY3Rpb24gb3B0aW9uc1xuXHQvLyBAb3B0aW9uIHRvdWNoWm9vbTogQm9vbGVhbnxTdHJpbmcgPSAqXG5cdC8vIFdoZXRoZXIgdGhlIG1hcCBjYW4gYmUgem9vbWVkIGJ5IHRvdWNoLWRyYWdnaW5nIHdpdGggdHdvIGZpbmdlcnMuIElmXG5cdC8vIHBhc3NlZCBgJ2NlbnRlcidgLCBpdCB3aWxsIHpvb20gdG8gdGhlIGNlbnRlciBvZiB0aGUgdmlldyByZWdhcmRsZXNzIG9mXG5cdC8vIHdoZXJlIHRoZSB0b3VjaCBldmVudHMgKGZpbmdlcnMpIHdlcmUuIEVuYWJsZWQgZm9yIHRvdWNoLWNhcGFibGUgd2ViXG5cdC8vIGJyb3dzZXJzIGV4Y2VwdCBmb3Igb2xkIEFuZHJvaWRzLlxuXHR0b3VjaFpvb206IEwuQnJvd3Nlci50b3VjaCAmJiAhTC5Ccm93c2VyLmFuZHJvaWQyMyxcblxuXHQvLyBAb3B0aW9uIGJvdW5jZUF0Wm9vbUxpbWl0czogQm9vbGVhbiA9IHRydWVcblx0Ly8gU2V0IGl0IHRvIGZhbHNlIGlmIHlvdSBkb24ndCB3YW50IHRoZSBtYXAgdG8gem9vbSBiZXlvbmQgbWluL21heCB6b29tXG5cdC8vIGFuZCB0aGVuIGJvdW5jZSBiYWNrIHdoZW4gcGluY2gtem9vbWluZy5cblx0Ym91bmNlQXRab29tTGltaXRzOiB0cnVlXG59KTtcblxuTC5NYXAuVG91Y2hab29tID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGFkZEhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC10b3VjaC16b29tJyk7XG5cdFx0TC5Eb21FdmVudC5vbih0aGlzLl9tYXAuX2NvbnRhaW5lciwgJ3RvdWNoc3RhcnQnLCB0aGlzLl9vblRvdWNoU3RhcnQsIHRoaXMpO1xuXHR9LFxuXG5cdHJlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX21hcC5fY29udGFpbmVyLCAnbGVhZmxldC10b3VjaC16b29tJyk7XG5cdFx0TC5Eb21FdmVudC5vZmYodGhpcy5fbWFwLl9jb250YWluZXIsICd0b3VjaHN0YXJ0JywgdGhpcy5fb25Ub3VjaFN0YXJ0LCB0aGlzKTtcblx0fSxcblxuXHRfb25Ub3VjaFN0YXJ0OiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXA7XG5cdFx0aWYgKCFlLnRvdWNoZXMgfHwgZS50b3VjaGVzLmxlbmd0aCAhPT0gMiB8fCBtYXAuX2FuaW1hdGluZ1pvb20gfHwgdGhpcy5fem9vbWluZykgeyByZXR1cm47IH1cblxuXHRcdHZhciBwMSA9IG1hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLnRvdWNoZXNbMF0pLFxuXHRcdCAgICBwMiA9IG1hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLnRvdWNoZXNbMV0pO1xuXG5cdFx0dGhpcy5fY2VudGVyUG9pbnQgPSBtYXAuZ2V0U2l6ZSgpLl9kaXZpZGVCeSgyKTtcblx0XHR0aGlzLl9zdGFydExhdExuZyA9IG1hcC5jb250YWluZXJQb2ludFRvTGF0TG5nKHRoaXMuX2NlbnRlclBvaW50KTtcblx0XHRpZiAobWFwLm9wdGlvbnMudG91Y2hab29tICE9PSAnY2VudGVyJykge1xuXHRcdFx0dGhpcy5fcGluY2hTdGFydExhdExuZyA9IG1hcC5jb250YWluZXJQb2ludFRvTGF0TG5nKHAxLmFkZChwMikuX2RpdmlkZUJ5KDIpKTtcblx0XHR9XG5cblx0XHR0aGlzLl9zdGFydERpc3QgPSBwMS5kaXN0YW5jZVRvKHAyKTtcblx0XHR0aGlzLl9zdGFydFpvb20gPSBtYXAuZ2V0Wm9vbSgpO1xuXG5cdFx0dGhpcy5fbW92ZWQgPSBmYWxzZTtcblx0XHR0aGlzLl96b29taW5nID0gdHJ1ZTtcblxuXHRcdG1hcC5fc3RvcCgpO1xuXG5cdFx0TC5Eb21FdmVudFxuXHRcdCAgICAub24oZG9jdW1lbnQsICd0b3VjaG1vdmUnLCB0aGlzLl9vblRvdWNoTW92ZSwgdGhpcylcblx0XHQgICAgLm9uKGRvY3VtZW50LCAndG91Y2hlbmQnLCB0aGlzLl9vblRvdWNoRW5kLCB0aGlzKTtcblxuXHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG5cdH0sXG5cblx0X29uVG91Y2hNb3ZlOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmICghZS50b3VjaGVzIHx8IGUudG91Y2hlcy5sZW5ndGggIT09IDIgfHwgIXRoaXMuX3pvb21pbmcpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBwMSA9IG1hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLnRvdWNoZXNbMF0pLFxuXHRcdCAgICBwMiA9IG1hcC5tb3VzZUV2ZW50VG9Db250YWluZXJQb2ludChlLnRvdWNoZXNbMV0pLFxuXHRcdCAgICBzY2FsZSA9IHAxLmRpc3RhbmNlVG8ocDIpIC8gdGhpcy5fc3RhcnREaXN0O1xuXG5cblx0XHR0aGlzLl96b29tID0gbWFwLmdldFNjYWxlWm9vbShzY2FsZSwgdGhpcy5fc3RhcnRab29tKTtcblxuXHRcdGlmICghbWFwLm9wdGlvbnMuYm91bmNlQXRab29tTGltaXRzICYmIChcblx0XHRcdCh0aGlzLl96b29tIDwgbWFwLmdldE1pblpvb20oKSAmJiBzY2FsZSA8IDEpIHx8XG5cdFx0XHQodGhpcy5fem9vbSA+IG1hcC5nZXRNYXhab29tKCkgJiYgc2NhbGUgPiAxKSkpIHtcblx0XHRcdHRoaXMuX3pvb20gPSBtYXAuX2xpbWl0Wm9vbSh0aGlzLl96b29tKTtcblx0XHR9XG5cblx0XHRpZiAobWFwLm9wdGlvbnMudG91Y2hab29tID09PSAnY2VudGVyJykge1xuXHRcdFx0dGhpcy5fY2VudGVyID0gdGhpcy5fc3RhcnRMYXRMbmc7XG5cdFx0XHRpZiAoc2NhbGUgPT09IDEpIHsgcmV0dXJuOyB9XG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIEdldCBkZWx0YSBmcm9tIHBpbmNoIHRvIGNlbnRlciwgc28gY2VudGVyTGF0TG5nIGlzIGRlbHRhIGFwcGxpZWQgdG8gaW5pdGlhbCBwaW5jaExhdExuZ1xuXHRcdFx0dmFyIGRlbHRhID0gcDEuX2FkZChwMikuX2RpdmlkZUJ5KDIpLl9zdWJ0cmFjdCh0aGlzLl9jZW50ZXJQb2ludCk7XG5cdFx0XHRpZiAoc2NhbGUgPT09IDEgJiYgZGVsdGEueCA9PT0gMCAmJiBkZWx0YS55ID09PSAwKSB7IHJldHVybjsgfVxuXHRcdFx0dGhpcy5fY2VudGVyID0gbWFwLnVucHJvamVjdChtYXAucHJvamVjdCh0aGlzLl9waW5jaFN0YXJ0TGF0TG5nLCB0aGlzLl96b29tKS5zdWJ0cmFjdChkZWx0YSksIHRoaXMuX3pvb20pO1xuXHRcdH1cblxuXHRcdGlmICghdGhpcy5fbW92ZWQpIHtcblx0XHRcdG1hcC5fbW92ZVN0YXJ0KHRydWUpO1xuXHRcdFx0dGhpcy5fbW92ZWQgPSB0cnVlO1xuXHRcdH1cblxuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fYW5pbVJlcXVlc3QpO1xuXG5cdFx0dmFyIG1vdmVGbiA9IEwuYmluZChtYXAuX21vdmUsIG1hcCwgdGhpcy5fY2VudGVyLCB0aGlzLl96b29tLCB7cGluY2g6IHRydWUsIHJvdW5kOiBmYWxzZX0pO1xuXHRcdHRoaXMuX2FuaW1SZXF1ZXN0ID0gTC5VdGlsLnJlcXVlc3RBbmltRnJhbWUobW92ZUZuLCB0aGlzLCB0cnVlKTtcblxuXHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG5cdH0sXG5cblx0X29uVG91Y2hFbmQ6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAoIXRoaXMuX21vdmVkIHx8ICF0aGlzLl96b29taW5nKSB7XG5cdFx0XHR0aGlzLl96b29taW5nID0gZmFsc2U7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dGhpcy5fem9vbWluZyA9IGZhbHNlO1xuXHRcdEwuVXRpbC5jYW5jZWxBbmltRnJhbWUodGhpcy5fYW5pbVJlcXVlc3QpO1xuXG5cdFx0TC5Eb21FdmVudFxuXHRcdCAgICAub2ZmKGRvY3VtZW50LCAndG91Y2htb3ZlJywgdGhpcy5fb25Ub3VjaE1vdmUpXG5cdFx0ICAgIC5vZmYoZG9jdW1lbnQsICd0b3VjaGVuZCcsIHRoaXMuX29uVG91Y2hFbmQpO1xuXG5cdFx0Ly8gUGluY2ggdXBkYXRlcyBHcmlkTGF5ZXJzJyBsZXZlbHMgb25seSB3aGVuIHpvb21TbmFwIGlzIG9mZiwgc28gem9vbVNuYXAgYmVjb21lcyBub1VwZGF0ZS5cblx0XHRpZiAodGhpcy5fbWFwLm9wdGlvbnMuem9vbUFuaW1hdGlvbikge1xuXHRcdFx0dGhpcy5fbWFwLl9hbmltYXRlWm9vbSh0aGlzLl9jZW50ZXIsIHRoaXMuX21hcC5fbGltaXRab29tKHRoaXMuX3pvb20pLCB0cnVlLCB0aGlzLl9tYXAub3B0aW9ucy56b29tU25hcCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX21hcC5fcmVzZXRWaWV3KHRoaXMuX2NlbnRlciwgdGhpcy5fbWFwLl9saW1pdFpvb20odGhpcy5fem9vbSkpO1xuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgdG91Y2hab29tOiBIYW5kbGVyXG4vLyBUb3VjaCB6b29tIGhhbmRsZXIuXG5MLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICd0b3VjaFpvb20nLCBMLk1hcC5Ub3VjaFpvb20pO1xuXG5cblxuLypcbiAqIEwuTWFwLlRhcCBpcyB1c2VkIHRvIGVuYWJsZSBtb2JpbGUgaGFja3MgbGlrZSBxdWljayB0YXBzIGFuZCBsb25nIGhvbGQuXG4gKi9cblxuLy8gQG5hbWVzcGFjZSBNYXBcbi8vIEBzZWN0aW9uIEludGVyYWN0aW9uIE9wdGlvbnNcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XG5cdC8vIEBzZWN0aW9uIFRvdWNoIGludGVyYWN0aW9uIG9wdGlvbnNcblx0Ly8gQG9wdGlvbiB0YXA6IEJvb2xlYW4gPSB0cnVlXG5cdC8vIEVuYWJsZXMgbW9iaWxlIGhhY2tzIGZvciBzdXBwb3J0aW5nIGluc3RhbnQgdGFwcyAoZml4aW5nIDIwMG1zIGNsaWNrXG5cdC8vIGRlbGF5IG9uIGlPUy9BbmRyb2lkKSBhbmQgdG91Y2ggaG9sZHMgKGZpcmVkIGFzIGBjb250ZXh0bWVudWAgZXZlbnRzKS5cblx0dGFwOiB0cnVlLFxuXG5cdC8vIEBvcHRpb24gdGFwVG9sZXJhbmNlOiBOdW1iZXIgPSAxNVxuXHQvLyBUaGUgbWF4IG51bWJlciBvZiBwaXhlbHMgYSB1c2VyIGNhbiBzaGlmdCBoaXMgZmluZ2VyIGR1cmluZyB0b3VjaFxuXHQvLyBmb3IgaXQgdG8gYmUgY29uc2lkZXJlZCBhIHZhbGlkIHRhcC5cblx0dGFwVG9sZXJhbmNlOiAxNVxufSk7XG5cbkwuTWFwLlRhcCA9IEwuSGFuZGxlci5leHRlbmQoe1xuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub24odGhpcy5fbWFwLl9jb250YWluZXIsICd0b3VjaHN0YXJ0JywgdGhpcy5fb25Eb3duLCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX21hcC5fY29udGFpbmVyLCAndG91Y2hzdGFydCcsIHRoaXMuX29uRG93biwgdGhpcyk7XG5cdH0sXG5cblx0X29uRG93bjogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIWUudG91Y2hlcykgeyByZXR1cm47IH1cblxuXHRcdEwuRG9tRXZlbnQucHJldmVudERlZmF1bHQoZSk7XG5cblx0XHR0aGlzLl9maXJlQ2xpY2sgPSB0cnVlO1xuXG5cdFx0Ly8gZG9uJ3Qgc2ltdWxhdGUgY2xpY2sgb3IgdHJhY2sgbG9uZ3ByZXNzIGlmIG1vcmUgdGhhbiAxIHRvdWNoXG5cdFx0aWYgKGUudG91Y2hlcy5sZW5ndGggPiAxKSB7XG5cdFx0XHR0aGlzLl9maXJlQ2xpY2sgPSBmYWxzZTtcblx0XHRcdGNsZWFyVGltZW91dCh0aGlzLl9ob2xkVGltZW91dCk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dmFyIGZpcnN0ID0gZS50b3VjaGVzWzBdLFxuXHRcdCAgICBlbCA9IGZpcnN0LnRhcmdldDtcblxuXHRcdHRoaXMuX3N0YXJ0UG9zID0gdGhpcy5fbmV3UG9zID0gbmV3IEwuUG9pbnQoZmlyc3QuY2xpZW50WCwgZmlyc3QuY2xpZW50WSk7XG5cblx0XHQvLyBpZiB0b3VjaGluZyBhIGxpbmssIGhpZ2hsaWdodCBpdFxuXHRcdGlmIChlbC50YWdOYW1lICYmIGVsLnRhZ05hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2EnKSB7XG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoZWwsICdsZWFmbGV0LWFjdGl2ZScpO1xuXHRcdH1cblxuXHRcdC8vIHNpbXVsYXRlIGxvbmcgaG9sZCBidXQgc2V0dGluZyBhIHRpbWVvdXRcblx0XHR0aGlzLl9ob2xkVGltZW91dCA9IHNldFRpbWVvdXQoTC5iaW5kKGZ1bmN0aW9uICgpIHtcblx0XHRcdGlmICh0aGlzLl9pc1RhcFZhbGlkKCkpIHtcblx0XHRcdFx0dGhpcy5fZmlyZUNsaWNrID0gZmFsc2U7XG5cdFx0XHRcdHRoaXMuX29uVXAoKTtcblx0XHRcdFx0dGhpcy5fc2ltdWxhdGVFdmVudCgnY29udGV4dG1lbnUnLCBmaXJzdCk7XG5cdFx0XHR9XG5cdFx0fSwgdGhpcyksIDEwMDApO1xuXG5cdFx0dGhpcy5fc2ltdWxhdGVFdmVudCgnbW91c2Vkb3duJywgZmlyc3QpO1xuXG5cdFx0TC5Eb21FdmVudC5vbihkb2N1bWVudCwge1xuXHRcdFx0dG91Y2htb3ZlOiB0aGlzLl9vbk1vdmUsXG5cdFx0XHR0b3VjaGVuZDogdGhpcy5fb25VcFxuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vblVwOiBmdW5jdGlvbiAoZSkge1xuXHRcdGNsZWFyVGltZW91dCh0aGlzLl9ob2xkVGltZW91dCk7XG5cblx0XHRMLkRvbUV2ZW50Lm9mZihkb2N1bWVudCwge1xuXHRcdFx0dG91Y2htb3ZlOiB0aGlzLl9vbk1vdmUsXG5cdFx0XHR0b3VjaGVuZDogdGhpcy5fb25VcFxuXHRcdH0sIHRoaXMpO1xuXG5cdFx0aWYgKHRoaXMuX2ZpcmVDbGljayAmJiBlICYmIGUuY2hhbmdlZFRvdWNoZXMpIHtcblxuXHRcdFx0dmFyIGZpcnN0ID0gZS5jaGFuZ2VkVG91Y2hlc1swXSxcblx0XHRcdCAgICBlbCA9IGZpcnN0LnRhcmdldDtcblxuXHRcdFx0aWYgKGVsICYmIGVsLnRhZ05hbWUgJiYgZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpID09PSAnYScpIHtcblx0XHRcdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKGVsLCAnbGVhZmxldC1hY3RpdmUnKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fc2ltdWxhdGVFdmVudCgnbW91c2V1cCcsIGZpcnN0KTtcblxuXHRcdFx0Ly8gc2ltdWxhdGUgY2xpY2sgaWYgdGhlIHRvdWNoIGRpZG4ndCBtb3ZlIHRvbyBtdWNoXG5cdFx0XHRpZiAodGhpcy5faXNUYXBWYWxpZCgpKSB7XG5cdFx0XHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ2NsaWNrJywgZmlyc3QpO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRfaXNUYXBWYWxpZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9uZXdQb3MuZGlzdGFuY2VUbyh0aGlzLl9zdGFydFBvcykgPD0gdGhpcy5fbWFwLm9wdGlvbnMudGFwVG9sZXJhbmNlO1xuXHR9LFxuXG5cdF9vbk1vdmU6IGZ1bmN0aW9uIChlKSB7XG5cdFx0dmFyIGZpcnN0ID0gZS50b3VjaGVzWzBdO1xuXHRcdHRoaXMuX25ld1BvcyA9IG5ldyBMLlBvaW50KGZpcnN0LmNsaWVudFgsIGZpcnN0LmNsaWVudFkpO1xuXHRcdHRoaXMuX3NpbXVsYXRlRXZlbnQoJ21vdXNlbW92ZScsIGZpcnN0KTtcblx0fSxcblxuXHRfc2ltdWxhdGVFdmVudDogZnVuY3Rpb24gKHR5cGUsIGUpIHtcblx0XHR2YXIgc2ltdWxhdGVkRXZlbnQgPSBkb2N1bWVudC5jcmVhdGVFdmVudCgnTW91c2VFdmVudHMnKTtcblxuXHRcdHNpbXVsYXRlZEV2ZW50Ll9zaW11bGF0ZWQgPSB0cnVlO1xuXHRcdGUudGFyZ2V0Ll9zaW11bGF0ZWRDbGljayA9IHRydWU7XG5cblx0XHRzaW11bGF0ZWRFdmVudC5pbml0TW91c2VFdmVudChcblx0XHQgICAgICAgIHR5cGUsIHRydWUsIHRydWUsIHdpbmRvdywgMSxcblx0XHQgICAgICAgIGUuc2NyZWVuWCwgZS5zY3JlZW5ZLFxuXHRcdCAgICAgICAgZS5jbGllbnRYLCBlLmNsaWVudFksXG5cdFx0ICAgICAgICBmYWxzZSwgZmFsc2UsIGZhbHNlLCBmYWxzZSwgMCwgbnVsbCk7XG5cblx0XHRlLnRhcmdldC5kaXNwYXRjaEV2ZW50KHNpbXVsYXRlZEV2ZW50KTtcblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgdGFwOiBIYW5kbGVyXG4vLyBNb2JpbGUgdG91Y2ggaGFja3MgKHF1aWNrIHRhcCBhbmQgdG91Y2ggaG9sZCkgaGFuZGxlci5cbmlmIChMLkJyb3dzZXIudG91Y2ggJiYgIUwuQnJvd3Nlci5wb2ludGVyKSB7XG5cdEwuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ3RhcCcsIEwuTWFwLlRhcCk7XG59XG5cblxuXG4vKlxuICogTC5IYW5kbGVyLkJveFpvb20gaXMgdXNlZCB0byBhZGQgc2hpZnQtZHJhZyB6b29tIGludGVyYWN0aW9uIHRvIHRoZSBtYXBcbiAqICh6b29tIHRvIGEgc2VsZWN0ZWQgYm91bmRpbmcgYm94KSwgZW5hYmxlZCBieSBkZWZhdWx0LlxuICovXG5cbi8vIEBuYW1lc3BhY2UgTWFwXG4vLyBAc2VjdGlvbiBJbnRlcmFjdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAb3B0aW9uIGJveFpvb206IEJvb2xlYW4gPSB0cnVlXG5cdC8vIFdoZXRoZXIgdGhlIG1hcCBjYW4gYmUgem9vbWVkIHRvIGEgcmVjdGFuZ3VsYXIgYXJlYSBzcGVjaWZpZWQgYnlcblx0Ly8gZHJhZ2dpbmcgdGhlIG1vdXNlIHdoaWxlIHByZXNzaW5nIHRoZSBzaGlmdCBrZXkuXG5cdGJveFpvb206IHRydWVcbn0pO1xuXG5MLk1hcC5Cb3hab29tID0gTC5IYW5kbGVyLmV4dGVuZCh7XG5cdGluaXRpYWxpemU6IGZ1bmN0aW9uIChtYXApIHtcblx0XHR0aGlzLl9tYXAgPSBtYXA7XG5cdFx0dGhpcy5fY29udGFpbmVyID0gbWFwLl9jb250YWluZXI7XG5cdFx0dGhpcy5fcGFuZSA9IG1hcC5fcGFuZXMub3ZlcmxheVBhbmU7XG5cdH0sXG5cblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9uKHRoaXMuX2NvbnRhaW5lciwgJ21vdXNlZG93bicsIHRoaXMuX29uTW91c2VEb3duLCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdEwuRG9tRXZlbnQub2ZmKHRoaXMuX2NvbnRhaW5lciwgJ21vdXNlZG93bicsIHRoaXMuX29uTW91c2VEb3duLCB0aGlzKTtcblx0fSxcblxuXHRtb3ZlZDogZnVuY3Rpb24gKCkge1xuXHRcdHJldHVybiB0aGlzLl9tb3ZlZDtcblx0fSxcblxuXHRfcmVzZXRTdGF0ZTogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX21vdmVkID0gZmFsc2U7XG5cdH0sXG5cblx0X29uTW91c2VEb3duOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmICghZS5zaGlmdEtleSB8fCAoKGUud2hpY2ggIT09IDEpICYmIChlLmJ1dHRvbiAhPT0gMSkpKSB7IHJldHVybiBmYWxzZTsgfVxuXG5cdFx0dGhpcy5fcmVzZXRTdGF0ZSgpO1xuXG5cdFx0TC5Eb21VdGlsLmRpc2FibGVUZXh0U2VsZWN0aW9uKCk7XG5cdFx0TC5Eb21VdGlsLmRpc2FibGVJbWFnZURyYWcoKTtcblxuXHRcdHRoaXMuX3N0YXJ0UG9pbnQgPSB0aGlzLl9tYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZSk7XG5cblx0XHRMLkRvbUV2ZW50Lm9uKGRvY3VtZW50LCB7XG5cdFx0XHRjb250ZXh0bWVudTogTC5Eb21FdmVudC5zdG9wLFxuXHRcdFx0bW91c2Vtb3ZlOiB0aGlzLl9vbk1vdXNlTW92ZSxcblx0XHRcdG1vdXNldXA6IHRoaXMuX29uTW91c2VVcCxcblx0XHRcdGtleWRvd246IHRoaXMuX29uS2V5RG93blxuXHRcdH0sIHRoaXMpO1xuXHR9LFxuXG5cdF9vbk1vdXNlTW92ZTogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoIXRoaXMuX21vdmVkKSB7XG5cdFx0XHR0aGlzLl9tb3ZlZCA9IHRydWU7XG5cblx0XHRcdHRoaXMuX2JveCA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LXpvb20tYm94JywgdGhpcy5fY29udGFpbmVyKTtcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWNyb3NzaGFpcicpO1xuXG5cdFx0XHR0aGlzLl9tYXAuZmlyZSgnYm94em9vbXN0YXJ0Jyk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fcG9pbnQgPSB0aGlzLl9tYXAubW91c2VFdmVudFRvQ29udGFpbmVyUG9pbnQoZSk7XG5cblx0XHR2YXIgYm91bmRzID0gbmV3IEwuQm91bmRzKHRoaXMuX3BvaW50LCB0aGlzLl9zdGFydFBvaW50KSxcblx0XHQgICAgc2l6ZSA9IGJvdW5kcy5nZXRTaXplKCk7XG5cblx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24odGhpcy5fYm94LCBib3VuZHMubWluKTtcblxuXHRcdHRoaXMuX2JveC5zdHlsZS53aWR0aCAgPSBzaXplLnggKyAncHgnO1xuXHRcdHRoaXMuX2JveC5zdHlsZS5oZWlnaHQgPSBzaXplLnkgKyAncHgnO1xuXHR9LFxuXG5cdF9maW5pc2g6IGZ1bmN0aW9uICgpIHtcblx0XHRpZiAodGhpcy5fbW92ZWQpIHtcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fYm94KTtcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9jb250YWluZXIsICdsZWFmbGV0LWNyb3NzaGFpcicpO1xuXHRcdH1cblxuXHRcdEwuRG9tVXRpbC5lbmFibGVUZXh0U2VsZWN0aW9uKCk7XG5cdFx0TC5Eb21VdGlsLmVuYWJsZUltYWdlRHJhZygpO1xuXG5cdFx0TC5Eb21FdmVudC5vZmYoZG9jdW1lbnQsIHtcblx0XHRcdGNvbnRleHRtZW51OiBMLkRvbUV2ZW50LnN0b3AsXG5cdFx0XHRtb3VzZW1vdmU6IHRoaXMuX29uTW91c2VNb3ZlLFxuXHRcdFx0bW91c2V1cDogdGhpcy5fb25Nb3VzZVVwLFxuXHRcdFx0a2V5ZG93bjogdGhpcy5fb25LZXlEb3duXG5cdFx0fSwgdGhpcyk7XG5cdH0sXG5cblx0X29uTW91c2VVcDogZnVuY3Rpb24gKGUpIHtcblx0XHRpZiAoKGUud2hpY2ggIT09IDEpICYmIChlLmJ1dHRvbiAhPT0gMSkpIHsgcmV0dXJuOyB9XG5cblx0XHR0aGlzLl9maW5pc2goKTtcblxuXHRcdGlmICghdGhpcy5fbW92ZWQpIHsgcmV0dXJuOyB9XG5cdFx0Ly8gUG9zdHBvbmUgdG8gbmV4dCBKUyB0aWNrIHNvIGludGVybmFsIGNsaWNrIGV2ZW50IGhhbmRsaW5nXG5cdFx0Ly8gc3RpbGwgc2VlIGl0IGFzIFwibW92ZWRcIi5cblx0XHRzZXRUaW1lb3V0KEwuYmluZCh0aGlzLl9yZXNldFN0YXRlLCB0aGlzKSwgMCk7XG5cblx0XHR2YXIgYm91bmRzID0gbmV3IEwuTGF0TG5nQm91bmRzKFxuXHRcdCAgICAgICAgdGhpcy5fbWFwLmNvbnRhaW5lclBvaW50VG9MYXRMbmcodGhpcy5fc3RhcnRQb2ludCksXG5cdFx0ICAgICAgICB0aGlzLl9tYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyh0aGlzLl9wb2ludCkpO1xuXG5cdFx0dGhpcy5fbWFwXG5cdFx0XHQuZml0Qm91bmRzKGJvdW5kcylcblx0XHRcdC5maXJlKCdib3h6b29tZW5kJywge2JveFpvb21Cb3VuZHM6IGJvdW5kc30pO1xuXHR9LFxuXG5cdF9vbktleURvd246IGZ1bmN0aW9uIChlKSB7XG5cdFx0aWYgKGUua2V5Q29kZSA9PT0gMjcpIHtcblx0XHRcdHRoaXMuX2ZpbmlzaCgpO1xuXHRcdH1cblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAcHJvcGVydHkgYm94Wm9vbTogSGFuZGxlclxuLy8gQm94IChzaGlmdC1kcmFnIHdpdGggbW91c2UpIHpvb20gaGFuZGxlci5cbkwuTWFwLmFkZEluaXRIb29rKCdhZGRIYW5kbGVyJywgJ2JveFpvb20nLCBMLk1hcC5Cb3hab29tKTtcblxuXG5cbi8qXG4gKiBMLk1hcC5LZXlib2FyZCBpcyBoYW5kbGluZyBrZXlib2FyZCBpbnRlcmFjdGlvbiB3aXRoIHRoZSBtYXAsIGVuYWJsZWQgYnkgZGVmYXVsdC5cbiAqL1xuXG4vLyBAbmFtZXNwYWNlIE1hcFxuLy8gQHNlY3Rpb24gS2V5Ym9hcmQgTmF2aWdhdGlvbiBPcHRpb25zXG5MLk1hcC5tZXJnZU9wdGlvbnMoe1xuXHQvLyBAb3B0aW9uIGtleWJvYXJkOiBCb29sZWFuID0gdHJ1ZVxuXHQvLyBNYWtlcyB0aGUgbWFwIGZvY3VzYWJsZSBhbmQgYWxsb3dzIHVzZXJzIHRvIG5hdmlnYXRlIHRoZSBtYXAgd2l0aCBrZXlib2FyZFxuXHQvLyBhcnJvd3MgYW5kIGArYC9gLWAga2V5cy5cblx0a2V5Ym9hcmQ6IHRydWUsXG5cblx0Ly8gQG9wdGlvbiBrZXlib2FyZFBhbkRlbHRhOiBOdW1iZXIgPSA4MFxuXHQvLyBBbW91bnQgb2YgcGl4ZWxzIHRvIHBhbiB3aGVuIHByZXNzaW5nIGFuIGFycm93IGtleS5cblx0a2V5Ym9hcmRQYW5EZWx0YTogODBcbn0pO1xuXG5MLk1hcC5LZXlib2FyZCA9IEwuSGFuZGxlci5leHRlbmQoe1xuXG5cdGtleUNvZGVzOiB7XG5cdFx0bGVmdDogICAgWzM3XSxcblx0XHRyaWdodDogICBbMzldLFxuXHRcdGRvd246ICAgIFs0MF0sXG5cdFx0dXA6ICAgICAgWzM4XSxcblx0XHR6b29tSW46ICBbMTg3LCAxMDcsIDYxLCAxNzFdLFxuXHRcdHpvb21PdXQ6IFsxODksIDEwOSwgNTQsIDE3M11cblx0fSxcblxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAobWFwKSB7XG5cdFx0dGhpcy5fbWFwID0gbWFwO1xuXG5cdFx0dGhpcy5fc2V0UGFuRGVsdGEobWFwLm9wdGlvbnMua2V5Ym9hcmRQYW5EZWx0YSk7XG5cdFx0dGhpcy5fc2V0Wm9vbURlbHRhKG1hcC5vcHRpb25zLnpvb21EZWx0YSk7XG5cdH0sXG5cblx0YWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHR2YXIgY29udGFpbmVyID0gdGhpcy5fbWFwLl9jb250YWluZXI7XG5cblx0XHQvLyBtYWtlIHRoZSBjb250YWluZXIgZm9jdXNhYmxlIGJ5IHRhYmJpbmdcblx0XHRpZiAoY29udGFpbmVyLnRhYkluZGV4IDw9IDApIHtcblx0XHRcdGNvbnRhaW5lci50YWJJbmRleCA9ICcwJztcblx0XHR9XG5cblx0XHRMLkRvbUV2ZW50Lm9uKGNvbnRhaW5lciwge1xuXHRcdFx0Zm9jdXM6IHRoaXMuX29uRm9jdXMsXG5cdFx0XHRibHVyOiB0aGlzLl9vbkJsdXIsXG5cdFx0XHRtb3VzZWRvd246IHRoaXMuX29uTW91c2VEb3duXG5cdFx0fSwgdGhpcyk7XG5cblx0XHR0aGlzLl9tYXAub24oe1xuXHRcdFx0Zm9jdXM6IHRoaXMuX2FkZEhvb2tzLFxuXHRcdFx0Ymx1cjogdGhpcy5fcmVtb3ZlSG9va3Ncblx0XHR9LCB0aGlzKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX3JlbW92ZUhvb2tzKCk7XG5cblx0XHRMLkRvbUV2ZW50Lm9mZih0aGlzLl9tYXAuX2NvbnRhaW5lciwge1xuXHRcdFx0Zm9jdXM6IHRoaXMuX29uRm9jdXMsXG5cdFx0XHRibHVyOiB0aGlzLl9vbkJsdXIsXG5cdFx0XHRtb3VzZWRvd246IHRoaXMuX29uTW91c2VEb3duXG5cdFx0fSwgdGhpcyk7XG5cblx0XHR0aGlzLl9tYXAub2ZmKHtcblx0XHRcdGZvY3VzOiB0aGlzLl9hZGRIb29rcyxcblx0XHRcdGJsdXI6IHRoaXMuX3JlbW92ZUhvb2tzXG5cdFx0fSwgdGhpcyk7XG5cdH0sXG5cblx0X29uTW91c2VEb3duOiBmdW5jdGlvbiAoKSB7XG5cdFx0aWYgKHRoaXMuX2ZvY3VzZWQpIHsgcmV0dXJuOyB9XG5cblx0XHR2YXIgYm9keSA9IGRvY3VtZW50LmJvZHksXG5cdFx0ICAgIGRvY0VsID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LFxuXHRcdCAgICB0b3AgPSBib2R5LnNjcm9sbFRvcCB8fCBkb2NFbC5zY3JvbGxUb3AsXG5cdFx0ICAgIGxlZnQgPSBib2R5LnNjcm9sbExlZnQgfHwgZG9jRWwuc2Nyb2xsTGVmdDtcblxuXHRcdHRoaXMuX21hcC5fY29udGFpbmVyLmZvY3VzKCk7XG5cblx0XHR3aW5kb3cuc2Nyb2xsVG8obGVmdCwgdG9wKTtcblx0fSxcblxuXHRfb25Gb2N1czogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2ZvY3VzZWQgPSB0cnVlO1xuXHRcdHRoaXMuX21hcC5maXJlKCdmb2N1cycpO1xuXHR9LFxuXG5cdF9vbkJsdXI6IGZ1bmN0aW9uICgpIHtcblx0XHR0aGlzLl9mb2N1c2VkID0gZmFsc2U7XG5cdFx0dGhpcy5fbWFwLmZpcmUoJ2JsdXInKTtcblx0fSxcblxuXHRfc2V0UGFuRGVsdGE6IGZ1bmN0aW9uIChwYW5EZWx0YSkge1xuXHRcdHZhciBrZXlzID0gdGhpcy5fcGFuS2V5cyA9IHt9LFxuXHRcdCAgICBjb2RlcyA9IHRoaXMua2V5Q29kZXMsXG5cdFx0ICAgIGksIGxlbjtcblxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLmxlZnQubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMubGVmdFtpXV0gPSBbLTEgKiBwYW5EZWx0YSwgMF07XG5cdFx0fVxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLnJpZ2h0Lmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLnJpZ2h0W2ldXSA9IFtwYW5EZWx0YSwgMF07XG5cdFx0fVxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLmRvd24ubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMuZG93bltpXV0gPSBbMCwgcGFuRGVsdGFdO1xuXHRcdH1cblx0XHRmb3IgKGkgPSAwLCBsZW4gPSBjb2Rlcy51cC5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuXHRcdFx0a2V5c1tjb2Rlcy51cFtpXV0gPSBbMCwgLTEgKiBwYW5EZWx0YV07XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRab29tRGVsdGE6IGZ1bmN0aW9uICh6b29tRGVsdGEpIHtcblx0XHR2YXIga2V5cyA9IHRoaXMuX3pvb21LZXlzID0ge30sXG5cdFx0ICAgIGNvZGVzID0gdGhpcy5rZXlDb2Rlcyxcblx0XHQgICAgaSwgbGVuO1xuXG5cdFx0Zm9yIChpID0gMCwgbGVuID0gY29kZXMuem9vbUluLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG5cdFx0XHRrZXlzW2NvZGVzLnpvb21JbltpXV0gPSB6b29tRGVsdGE7XG5cdFx0fVxuXHRcdGZvciAoaSA9IDAsIGxlbiA9IGNvZGVzLnpvb21PdXQubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcblx0XHRcdGtleXNbY29kZXMuem9vbU91dFtpXV0gPSAtem9vbURlbHRhO1xuXHRcdH1cblx0fSxcblxuXHRfYWRkSG9va3M6IGZ1bmN0aW9uICgpIHtcblx0XHRMLkRvbUV2ZW50Lm9uKGRvY3VtZW50LCAna2V5ZG93bicsIHRoaXMuX29uS2V5RG93biwgdGhpcyk7XG5cdH0sXG5cblx0X3JlbW92ZUhvb2tzOiBmdW5jdGlvbiAoKSB7XG5cdFx0TC5Eb21FdmVudC5vZmYoZG9jdW1lbnQsICdrZXlkb3duJywgdGhpcy5fb25LZXlEb3duLCB0aGlzKTtcblx0fSxcblxuXHRfb25LZXlEb3duOiBmdW5jdGlvbiAoZSkge1xuXHRcdGlmIChlLmFsdEtleSB8fCBlLmN0cmxLZXkgfHwgZS5tZXRhS2V5KSB7IHJldHVybjsgfVxuXG5cdFx0dmFyIGtleSA9IGUua2V5Q29kZSxcblx0XHQgICAgbWFwID0gdGhpcy5fbWFwLFxuXHRcdCAgICBvZmZzZXQ7XG5cblx0XHRpZiAoa2V5IGluIHRoaXMuX3BhbktleXMpIHtcblxuXHRcdFx0aWYgKG1hcC5fcGFuQW5pbSAmJiBtYXAuX3BhbkFuaW0uX2luUHJvZ3Jlc3MpIHsgcmV0dXJuOyB9XG5cblx0XHRcdG9mZnNldCA9IHRoaXMuX3BhbktleXNba2V5XTtcblx0XHRcdGlmIChlLnNoaWZ0S2V5KSB7XG5cdFx0XHRcdG9mZnNldCA9IEwucG9pbnQob2Zmc2V0KS5tdWx0aXBseUJ5KDMpO1xuXHRcdFx0fVxuXG5cdFx0XHRtYXAucGFuQnkob2Zmc2V0KTtcblxuXHRcdFx0aWYgKG1hcC5vcHRpb25zLm1heEJvdW5kcykge1xuXHRcdFx0XHRtYXAucGFuSW5zaWRlQm91bmRzKG1hcC5vcHRpb25zLm1heEJvdW5kcyk7XG5cdFx0XHR9XG5cblx0XHR9IGVsc2UgaWYgKGtleSBpbiB0aGlzLl96b29tS2V5cykge1xuXHRcdFx0bWFwLnNldFpvb20obWFwLmdldFpvb20oKSArIChlLnNoaWZ0S2V5ID8gMyA6IDEpICogdGhpcy5fem9vbUtleXNba2V5XSk7XG5cblx0XHR9IGVsc2UgaWYgKGtleSA9PT0gMjcpIHtcblx0XHRcdG1hcC5jbG9zZVBvcHVwKCk7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdEwuRG9tRXZlbnQuc3RvcChlKTtcblx0fVxufSk7XG5cbi8vIEBzZWN0aW9uIEhhbmRsZXJzXG4vLyBAc2VjdGlvbiBIYW5kbGVyc1xuLy8gQHByb3BlcnR5IGtleWJvYXJkOiBIYW5kbGVyXG4vLyBLZXlib2FyZCBuYXZpZ2F0aW9uIGhhbmRsZXIuXG5MLk1hcC5hZGRJbml0SG9vaygnYWRkSGFuZGxlcicsICdrZXlib2FyZCcsIEwuTWFwLktleWJvYXJkKTtcblxuXG5cbi8qXG4gKiBMLkhhbmRsZXIuTWFya2VyRHJhZyBpcyB1c2VkIGludGVybmFsbHkgYnkgTC5NYXJrZXIgdG8gbWFrZSB0aGUgbWFya2VycyBkcmFnZ2FibGUuXG4gKi9cblxuXG4vKiBAbmFtZXNwYWNlIE1hcmtlclxuICogQHNlY3Rpb24gSW50ZXJhY3Rpb24gaGFuZGxlcnNcbiAqXG4gKiBJbnRlcmFjdGlvbiBoYW5kbGVycyBhcmUgcHJvcGVydGllcyBvZiBhIG1hcmtlciBpbnN0YW5jZSB0aGF0IGFsbG93IHlvdSB0byBjb250cm9sIGludGVyYWN0aW9uIGJlaGF2aW9yIGluIHJ1bnRpbWUsIGVuYWJsaW5nIG9yIGRpc2FibGluZyBjZXJ0YWluIGZlYXR1cmVzIHN1Y2ggYXMgZHJhZ2dpbmcgKHNlZSBgSGFuZGxlcmAgbWV0aG9kcykuIEV4YW1wbGU6XG4gKlxuICogYGBganNcbiAqIG1hcmtlci5kcmFnZ2luZy5kaXNhYmxlKCk7XG4gKiBgYGBcbiAqXG4gKiBAcHJvcGVydHkgZHJhZ2dpbmc6IEhhbmRsZXJcbiAqIE1hcmtlciBkcmFnZ2luZyBoYW5kbGVyIChieSBib3RoIG1vdXNlIGFuZCB0b3VjaCkuXG4gKi9cblxuTC5IYW5kbGVyLk1hcmtlckRyYWcgPSBMLkhhbmRsZXIuZXh0ZW5kKHtcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG1hcmtlcikge1xuXHRcdHRoaXMuX21hcmtlciA9IG1hcmtlcjtcblx0fSxcblxuXHRhZGRIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHZhciBpY29uID0gdGhpcy5fbWFya2VyLl9pY29uO1xuXG5cdFx0aWYgKCF0aGlzLl9kcmFnZ2FibGUpIHtcblx0XHRcdHRoaXMuX2RyYWdnYWJsZSA9IG5ldyBMLkRyYWdnYWJsZShpY29uLCBpY29uLCB0cnVlKTtcblx0XHR9XG5cblx0XHR0aGlzLl9kcmFnZ2FibGUub24oe1xuXHRcdFx0ZHJhZ3N0YXJ0OiB0aGlzLl9vbkRyYWdTdGFydCxcblx0XHRcdGRyYWc6IHRoaXMuX29uRHJhZyxcblx0XHRcdGRyYWdlbmQ6IHRoaXMuX29uRHJhZ0VuZFxuXHRcdH0sIHRoaXMpLmVuYWJsZSgpO1xuXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKGljb24sICdsZWFmbGV0LW1hcmtlci1kcmFnZ2FibGUnKTtcblx0fSxcblxuXHRyZW1vdmVIb29rczogZnVuY3Rpb24gKCkge1xuXHRcdHRoaXMuX2RyYWdnYWJsZS5vZmYoe1xuXHRcdFx0ZHJhZ3N0YXJ0OiB0aGlzLl9vbkRyYWdTdGFydCxcblx0XHRcdGRyYWc6IHRoaXMuX29uRHJhZyxcblx0XHRcdGRyYWdlbmQ6IHRoaXMuX29uRHJhZ0VuZFxuXHRcdH0sIHRoaXMpLmRpc2FibGUoKTtcblxuXHRcdGlmICh0aGlzLl9tYXJrZXIuX2ljb24pIHtcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9tYXJrZXIuX2ljb24sICdsZWFmbGV0LW1hcmtlci1kcmFnZ2FibGUnKTtcblx0XHR9XG5cdH0sXG5cblx0bW92ZWQ6IGZ1bmN0aW9uICgpIHtcblx0XHRyZXR1cm4gdGhpcy5fZHJhZ2dhYmxlICYmIHRoaXMuX2RyYWdnYWJsZS5fbW92ZWQ7XG5cdH0sXG5cblx0X29uRHJhZ1N0YXJ0OiBmdW5jdGlvbiAoKSB7XG5cdFx0Ly8gQHNlY3Rpb24gRHJhZ2dpbmcgZXZlbnRzXG5cdFx0Ly8gQGV2ZW50IGRyYWdzdGFydDogRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSB1c2VyIHN0YXJ0cyBkcmFnZ2luZyB0aGUgbWFya2VyLlxuXG5cdFx0Ly8gQGV2ZW50IG1vdmVzdGFydDogRXZlbnRcblx0XHQvLyBGaXJlZCB3aGVuIHRoZSBtYXJrZXIgc3RhcnRzIG1vdmluZyAoYmVjYXVzZSBvZiBkcmFnZ2luZykuXG5cblx0XHR0aGlzLl9vbGRMYXRMbmcgPSB0aGlzLl9tYXJrZXIuZ2V0TGF0TG5nKCk7XG5cdFx0dGhpcy5fbWFya2VyXG5cdFx0ICAgIC5jbG9zZVBvcHVwKClcblx0XHQgICAgLmZpcmUoJ21vdmVzdGFydCcpXG5cdFx0ICAgIC5maXJlKCdkcmFnc3RhcnQnKTtcblx0fSxcblxuXHRfb25EcmFnOiBmdW5jdGlvbiAoZSkge1xuXHRcdHZhciBtYXJrZXIgPSB0aGlzLl9tYXJrZXIsXG5cdFx0ICAgIHNoYWRvdyA9IG1hcmtlci5fc2hhZG93LFxuXHRcdCAgICBpY29uUG9zID0gTC5Eb21VdGlsLmdldFBvc2l0aW9uKG1hcmtlci5faWNvbiksXG5cdFx0ICAgIGxhdGxuZyA9IG1hcmtlci5fbWFwLmxheWVyUG9pbnRUb0xhdExuZyhpY29uUG9zKTtcblxuXHRcdC8vIHVwZGF0ZSBzaGFkb3cgcG9zaXRpb25cblx0XHRpZiAoc2hhZG93KSB7XG5cdFx0XHRMLkRvbVV0aWwuc2V0UG9zaXRpb24oc2hhZG93LCBpY29uUG9zKTtcblx0XHR9XG5cblx0XHRtYXJrZXIuX2xhdGxuZyA9IGxhdGxuZztcblx0XHRlLmxhdGxuZyA9IGxhdGxuZztcblx0XHRlLm9sZExhdExuZyA9IHRoaXMuX29sZExhdExuZztcblxuXHRcdC8vIEBldmVudCBkcmFnOiBFdmVudFxuXHRcdC8vIEZpcmVkIHJlcGVhdGVkbHkgd2hpbGUgdGhlIHVzZXIgZHJhZ3MgdGhlIG1hcmtlci5cblx0XHRtYXJrZXJcblx0XHQgICAgLmZpcmUoJ21vdmUnLCBlKVxuXHRcdCAgICAuZmlyZSgnZHJhZycsIGUpO1xuXHR9LFxuXG5cdF9vbkRyYWdFbmQ6IGZ1bmN0aW9uIChlKSB7XG5cdFx0Ly8gQGV2ZW50IGRyYWdlbmQ6IERyYWdFbmRFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIHVzZXIgc3RvcHMgZHJhZ2dpbmcgdGhlIG1hcmtlci5cblxuXHRcdC8vIEBldmVudCBtb3ZlZW5kOiBFdmVudFxuXHRcdC8vIEZpcmVkIHdoZW4gdGhlIG1hcmtlciBzdG9wcyBtb3ZpbmcgKGJlY2F1c2Ugb2YgZHJhZ2dpbmcpLlxuXHRcdGRlbGV0ZSB0aGlzLl9vbGRMYXRMbmc7XG5cdFx0dGhpcy5fbWFya2VyXG5cdFx0ICAgIC5maXJlKCdtb3ZlZW5kJylcblx0XHQgICAgLmZpcmUoJ2RyYWdlbmQnLCBlKTtcblx0fVxufSk7XG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQ29udHJvbFxyXG4gKiBAYWthIEwuQ29udHJvbFxyXG4gKiBAaW5oZXJpdHMgQ2xhc3NcclxuICpcclxuICogTC5Db250cm9sIGlzIGEgYmFzZSBjbGFzcyBmb3IgaW1wbGVtZW50aW5nIG1hcCBjb250cm9scy4gSGFuZGxlcyBwb3NpdGlvbmluZy5cclxuICogQWxsIG90aGVyIGNvbnRyb2xzIGV4dGVuZCBmcm9tIHRoaXMgY2xhc3MuXHJcbiAqL1xyXG5cclxuTC5Db250cm9sID0gTC5DbGFzcy5leHRlbmQoe1xyXG5cdC8vIEBzZWN0aW9uXHJcblx0Ly8gQGFrYSBDb250cm9sIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIHBvc2l0aW9uOiBTdHJpbmcgPSAndG9wcmlnaHQnXHJcblx0XHQvLyBUaGUgcG9zaXRpb24gb2YgdGhlIGNvbnRyb2wgKG9uZSBvZiB0aGUgbWFwIGNvcm5lcnMpLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIGAndG9wbGVmdCdgLFxyXG5cdFx0Ly8gYCd0b3ByaWdodCdgLCBgJ2JvdHRvbWxlZnQnYCBvciBgJ2JvdHRvbXJpZ2h0J2BcclxuXHRcdHBvc2l0aW9uOiAndG9wcmlnaHQnXHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRcdEwuc2V0T3B0aW9ucyh0aGlzLCBvcHRpb25zKTtcclxuXHR9LFxyXG5cclxuXHQvKiBAc2VjdGlvblxyXG5cdCAqIENsYXNzZXMgZXh0ZW5kaW5nIEwuQ29udHJvbCB3aWxsIGluaGVyaXQgdGhlIGZvbGxvd2luZyBtZXRob2RzOlxyXG5cdCAqXHJcblx0ICogQG1ldGhvZCBnZXRQb3NpdGlvbjogc3RyaW5nXHJcblx0ICogUmV0dXJucyB0aGUgcG9zaXRpb24gb2YgdGhlIGNvbnRyb2wuXHJcblx0ICovXHJcblx0Z2V0UG9zaXRpb246IGZ1bmN0aW9uICgpIHtcclxuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMucG9zaXRpb247XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBzZXRQb3NpdGlvbihwb3NpdGlvbjogc3RyaW5nKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHBvc2l0aW9uIG9mIHRoZSBjb250cm9sLlxyXG5cdHNldFBvc2l0aW9uOiBmdW5jdGlvbiAocG9zaXRpb24pIHtcclxuXHRcdHZhciBtYXAgPSB0aGlzLl9tYXA7XHJcblxyXG5cdFx0aWYgKG1hcCkge1xyXG5cdFx0XHRtYXAucmVtb3ZlQ29udHJvbCh0aGlzKTtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLm9wdGlvbnMucG9zaXRpb24gPSBwb3NpdGlvbjtcclxuXHJcblx0XHRpZiAobWFwKSB7XHJcblx0XHRcdG1hcC5hZGRDb250cm9sKHRoaXMpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgZ2V0Q29udGFpbmVyOiBIVE1MRWxlbWVudFxyXG5cdC8vIFJldHVybnMgdGhlIEhUTUxFbGVtZW50IHRoYXQgY29udGFpbnMgdGhlIGNvbnRyb2wuXHJcblx0Z2V0Q29udGFpbmVyOiBmdW5jdGlvbiAoKSB7XHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgYWRkVG8obWFwOiBNYXApOiB0aGlzXHJcblx0Ly8gQWRkcyB0aGUgY29udHJvbCB0byB0aGUgZ2l2ZW4gbWFwLlxyXG5cdGFkZFRvOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHR0aGlzLnJlbW92ZSgpO1xyXG5cdFx0dGhpcy5fbWFwID0gbWFwO1xyXG5cclxuXHRcdHZhciBjb250YWluZXIgPSB0aGlzLl9jb250YWluZXIgPSB0aGlzLm9uQWRkKG1hcCksXHJcblx0XHQgICAgcG9zID0gdGhpcy5nZXRQb3NpdGlvbigpLFxyXG5cdFx0ICAgIGNvcm5lciA9IG1hcC5fY29udHJvbENvcm5lcnNbcG9zXTtcclxuXHJcblx0XHRMLkRvbVV0aWwuYWRkQ2xhc3MoY29udGFpbmVyLCAnbGVhZmxldC1jb250cm9sJyk7XHJcblxyXG5cdFx0aWYgKHBvcy5pbmRleE9mKCdib3R0b20nKSAhPT0gLTEpIHtcclxuXHRcdFx0Y29ybmVyLmluc2VydEJlZm9yZShjb250YWluZXIsIGNvcm5lci5maXJzdENoaWxkKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGNvcm5lci5hcHBlbmRDaGlsZChjb250YWluZXIpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlOiB0aGlzXHJcblx0Ly8gUmVtb3ZlcyB0aGUgY29udHJvbCBmcm9tIHRoZSBtYXAgaXQgaXMgY3VycmVudGx5IGFjdGl2ZSBvbi5cclxuXHRyZW1vdmU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fbWFwKSB7XHJcblx0XHRcdHJldHVybiB0aGlzO1xyXG5cdFx0fVxyXG5cclxuXHRcdEwuRG9tVXRpbC5yZW1vdmUodGhpcy5fY29udGFpbmVyKTtcclxuXHJcblx0XHRpZiAodGhpcy5vblJlbW92ZSkge1xyXG5cdFx0XHR0aGlzLm9uUmVtb3ZlKHRoaXMuX21hcCk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fbWFwID0gbnVsbDtcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfcmVmb2N1c09uTWFwOiBmdW5jdGlvbiAoZSkge1xyXG5cdFx0Ly8gaWYgbWFwIGV4aXN0cyBhbmQgZXZlbnQgaXMgbm90IGEga2V5Ym9hcmQgZXZlbnRcclxuXHRcdGlmICh0aGlzLl9tYXAgJiYgZSAmJiBlLnNjcmVlblggPiAwICYmIGUuc2NyZWVuWSA+IDApIHtcclxuXHRcdFx0dGhpcy5fbWFwLmdldENvbnRhaW5lcigpLmZvY3VzKCk7XHJcblx0XHR9XHJcblx0fVxyXG59KTtcclxuXHJcbkwuY29udHJvbCA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkNvbnRyb2wob3B0aW9ucyk7XHJcbn07XHJcblxyXG4vKiBAc2VjdGlvbiBFeHRlbnNpb24gbWV0aG9kc1xyXG4gKiBAdW5pbmhlcml0YWJsZVxyXG4gKlxyXG4gKiBFdmVyeSBjb250cm9sIHNob3VsZCBleHRlbmQgZnJvbSBgTC5Db250cm9sYCBhbmQgKHJlLSlpbXBsZW1lbnQgdGhlIGZvbGxvd2luZyBtZXRob2RzLlxyXG4gKlxyXG4gKiBAbWV0aG9kIG9uQWRkKG1hcDogTWFwKTogSFRNTEVsZW1lbnRcclxuICogU2hvdWxkIHJldHVybiB0aGUgY29udGFpbmVyIERPTSBlbGVtZW50IGZvciB0aGUgY29udHJvbCBhbmQgYWRkIGxpc3RlbmVycyBvbiByZWxldmFudCBtYXAgZXZlbnRzLiBDYWxsZWQgb24gW2Bjb250cm9sLmFkZFRvKG1hcClgXSgjY29udHJvbC1hZGRUbykuXHJcbiAqXHJcbiAqIEBtZXRob2Qgb25SZW1vdmUobWFwOiBNYXApXHJcbiAqIE9wdGlvbmFsIG1ldGhvZC4gU2hvdWxkIGNvbnRhaW4gYWxsIGNsZWFuIHVwIGNvZGUgdGhhdCByZW1vdmVzIHRoZSBsaXN0ZW5lcnMgcHJldmlvdXNseSBhZGRlZCBpbiBbYG9uQWRkYF0oI2NvbnRyb2wtb25hZGQpLiBDYWxsZWQgb24gW2Bjb250cm9sLnJlbW92ZSgpYF0oI2NvbnRyb2wtcmVtb3ZlKS5cclxuICovXHJcblxyXG4vKiBAbmFtZXNwYWNlIE1hcFxyXG4gKiBAc2VjdGlvbiBNZXRob2RzIGZvciBMYXllcnMgYW5kIENvbnRyb2xzXHJcbiAqL1xyXG5MLk1hcC5pbmNsdWRlKHtcclxuXHQvLyBAbWV0aG9kIGFkZENvbnRyb2woY29udHJvbDogQ29udHJvbCk6IHRoaXNcclxuXHQvLyBBZGRzIHRoZSBnaXZlbiBjb250cm9sIHRvIHRoZSBtYXBcclxuXHRhZGRDb250cm9sOiBmdW5jdGlvbiAoY29udHJvbCkge1xyXG5cdFx0Y29udHJvbC5hZGRUbyh0aGlzKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlQ29udHJvbChjb250cm9sOiBDb250cm9sKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgdGhlIGdpdmVuIGNvbnRyb2wgZnJvbSB0aGUgbWFwXHJcblx0cmVtb3ZlQ29udHJvbDogZnVuY3Rpb24gKGNvbnRyb2wpIHtcclxuXHRcdGNvbnRyb2wucmVtb3ZlKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfaW5pdENvbnRyb2xQb3M6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBjb3JuZXJzID0gdGhpcy5fY29udHJvbENvcm5lcnMgPSB7fSxcclxuXHRcdCAgICBsID0gJ2xlYWZsZXQtJyxcclxuXHRcdCAgICBjb250YWluZXIgPSB0aGlzLl9jb250cm9sQ29udGFpbmVyID1cclxuXHRcdCAgICAgICAgICAgIEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGwgKyAnY29udHJvbC1jb250YWluZXInLCB0aGlzLl9jb250YWluZXIpO1xyXG5cclxuXHRcdGZ1bmN0aW9uIGNyZWF0ZUNvcm5lcih2U2lkZSwgaFNpZGUpIHtcclxuXHRcdFx0dmFyIGNsYXNzTmFtZSA9IGwgKyB2U2lkZSArICcgJyArIGwgKyBoU2lkZTtcclxuXHJcblx0XHRcdGNvcm5lcnNbdlNpZGUgKyBoU2lkZV0gPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUsIGNvbnRhaW5lcik7XHJcblx0XHR9XHJcblxyXG5cdFx0Y3JlYXRlQ29ybmVyKCd0b3AnLCAnbGVmdCcpO1xyXG5cdFx0Y3JlYXRlQ29ybmVyKCd0b3AnLCAncmlnaHQnKTtcclxuXHRcdGNyZWF0ZUNvcm5lcignYm90dG9tJywgJ2xlZnQnKTtcclxuXHRcdGNyZWF0ZUNvcm5lcignYm90dG9tJywgJ3JpZ2h0Jyk7XHJcblx0fSxcclxuXHJcblx0X2NsZWFyQ29udHJvbFBvczogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZSh0aGlzLl9jb250cm9sQ29udGFpbmVyKTtcclxuXHR9XHJcbn0pO1xyXG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQ29udHJvbC5ab29tXHJcbiAqIEBha2EgTC5Db250cm9sLlpvb21cclxuICogQGluaGVyaXRzIENvbnRyb2xcclxuICpcclxuICogQSBiYXNpYyB6b29tIGNvbnRyb2wgd2l0aCB0d28gYnV0dG9ucyAoem9vbSBpbiBhbmQgem9vbSBvdXQpLiBJdCBpcyBwdXQgb24gdGhlIG1hcCBieSBkZWZhdWx0IHVubGVzcyB5b3Ugc2V0IGl0cyBbYHpvb21Db250cm9sYCBvcHRpb25dKCNtYXAtem9vbWNvbnRyb2wpIHRvIGBmYWxzZWAuIEV4dGVuZHMgYENvbnRyb2xgLlxyXG4gKi9cclxuXHJcbkwuQ29udHJvbC5ab29tID0gTC5Db250cm9sLmV4dGVuZCh7XHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIENvbnRyb2wuWm9vbSBvcHRpb25zXHJcblx0b3B0aW9uczoge1xyXG5cdFx0cG9zaXRpb246ICd0b3BsZWZ0JyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21JblRleHQ6IFN0cmluZyA9ICcrJ1xyXG5cdFx0Ly8gVGhlIHRleHQgc2V0IG9uIHRoZSAnem9vbSBpbicgYnV0dG9uLlxyXG5cdFx0em9vbUluVGV4dDogJysnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gem9vbUluVGl0bGU6IFN0cmluZyA9ICdab29tIGluJ1xyXG5cdFx0Ly8gVGhlIHRpdGxlIHNldCBvbiB0aGUgJ3pvb20gaW4nIGJ1dHRvbi5cclxuXHRcdHpvb21JblRpdGxlOiAnWm9vbSBpbicsXHJcblxyXG5cdFx0Ly8gQG9wdGlvbiB6b29tT3V0VGV4dDogU3RyaW5nID0gJy0nXHJcblx0XHQvLyBUaGUgdGV4dCBzZXQgb24gdGhlICd6b29tIG91dCcgYnV0dG9uLlxyXG5cdFx0em9vbU91dFRleHQ6ICctJyxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHpvb21PdXRUaXRsZTogU3RyaW5nID0gJ1pvb20gb3V0J1xyXG5cdFx0Ly8gVGhlIHRpdGxlIHNldCBvbiB0aGUgJ3pvb20gb3V0JyBidXR0b24uXHJcblx0XHR6b29tT3V0VGl0bGU6ICdab29tIG91dCdcclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dmFyIHpvb21OYW1lID0gJ2xlYWZsZXQtY29udHJvbC16b29tJyxcclxuXHRcdCAgICBjb250YWluZXIgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCB6b29tTmFtZSArICcgbGVhZmxldC1iYXInKSxcclxuXHRcdCAgICBvcHRpb25zID0gdGhpcy5vcHRpb25zO1xyXG5cclxuXHRcdHRoaXMuX3pvb21JbkJ1dHRvbiAgPSB0aGlzLl9jcmVhdGVCdXR0b24ob3B0aW9ucy56b29tSW5UZXh0LCBvcHRpb25zLnpvb21JblRpdGxlLFxyXG5cdFx0ICAgICAgICB6b29tTmFtZSArICctaW4nLCAgY29udGFpbmVyLCB0aGlzLl96b29tSW4pO1xyXG5cdFx0dGhpcy5fem9vbU91dEJ1dHRvbiA9IHRoaXMuX2NyZWF0ZUJ1dHRvbihvcHRpb25zLnpvb21PdXRUZXh0LCBvcHRpb25zLnpvb21PdXRUaXRsZSxcclxuXHRcdCAgICAgICAgem9vbU5hbWUgKyAnLW91dCcsIGNvbnRhaW5lciwgdGhpcy5fem9vbU91dCk7XHJcblxyXG5cdFx0dGhpcy5fdXBkYXRlRGlzYWJsZWQoKTtcclxuXHRcdG1hcC5vbignem9vbWVuZCB6b29tbGV2ZWxzY2hhbmdlJywgdGhpcy5fdXBkYXRlRGlzYWJsZWQsIHRoaXMpO1xyXG5cclxuXHRcdHJldHVybiBjb250YWluZXI7XHJcblx0fSxcclxuXHJcblx0b25SZW1vdmU6IGZ1bmN0aW9uIChtYXApIHtcclxuXHRcdG1hcC5vZmYoJ3pvb21lbmQgem9vbWxldmVsc2NoYW5nZScsIHRoaXMuX3VwZGF0ZURpc2FibGVkLCB0aGlzKTtcclxuXHR9LFxyXG5cclxuXHRkaXNhYmxlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9kaXNhYmxlZCA9IHRydWU7XHJcblx0XHR0aGlzLl91cGRhdGVEaXNhYmxlZCgpO1xyXG5cdFx0cmV0dXJuIHRoaXM7XHJcblx0fSxcclxuXHJcblx0ZW5hYmxlOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR0aGlzLl9kaXNhYmxlZCA9IGZhbHNlO1xyXG5cdFx0dGhpcy5fdXBkYXRlRGlzYWJsZWQoKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF96b29tSW46IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRpZiAoIXRoaXMuX2Rpc2FibGVkICYmIHRoaXMuX21hcC5fem9vbSA8IHRoaXMuX21hcC5nZXRNYXhab29tKCkpIHtcclxuXHRcdFx0dGhpcy5fbWFwLnpvb21Jbih0aGlzLl9tYXAub3B0aW9ucy56b29tRGVsdGEgKiAoZS5zaGlmdEtleSA/IDMgOiAxKSk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3pvb21PdXQ6IGZ1bmN0aW9uIChlKSB7XHJcblx0XHRpZiAoIXRoaXMuX2Rpc2FibGVkICYmIHRoaXMuX21hcC5fem9vbSA+IHRoaXMuX21hcC5nZXRNaW5ab29tKCkpIHtcclxuXHRcdFx0dGhpcy5fbWFwLnpvb21PdXQodGhpcy5fbWFwLm9wdGlvbnMuem9vbURlbHRhICogKGUuc2hpZnRLZXkgPyAzIDogMSkpO1xyXG5cdFx0fVxyXG5cdH0sXHJcblxyXG5cdF9jcmVhdGVCdXR0b246IGZ1bmN0aW9uIChodG1sLCB0aXRsZSwgY2xhc3NOYW1lLCBjb250YWluZXIsIGZuKSB7XHJcblx0XHR2YXIgbGluayA9IEwuRG9tVXRpbC5jcmVhdGUoJ2EnLCBjbGFzc05hbWUsIGNvbnRhaW5lcik7XHJcblx0XHRsaW5rLmlubmVySFRNTCA9IGh0bWw7XHJcblx0XHRsaW5rLmhyZWYgPSAnIyc7XHJcblx0XHRsaW5rLnRpdGxlID0gdGl0bGU7XHJcblxyXG5cdFx0LypcclxuXHRcdCAqIFdpbGwgZm9yY2Ugc2NyZWVuIHJlYWRlcnMgbGlrZSBWb2ljZU92ZXIgdG8gcmVhZCB0aGlzIGFzIFwiWm9vbSBpbiAtIGJ1dHRvblwiXHJcblx0XHQgKi9cclxuXHRcdGxpbmsuc2V0QXR0cmlidXRlKCdyb2xlJywgJ2J1dHRvbicpO1xyXG5cdFx0bGluay5zZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnLCB0aXRsZSk7XHJcblxyXG5cdFx0TC5Eb21FdmVudFxyXG5cdFx0ICAgIC5vbihsaW5rLCAnbW91c2Vkb3duIGRibGNsaWNrJywgTC5Eb21FdmVudC5zdG9wUHJvcGFnYXRpb24pXHJcblx0XHQgICAgLm9uKGxpbmssICdjbGljaycsIEwuRG9tRXZlbnQuc3RvcClcclxuXHRcdCAgICAub24obGluaywgJ2NsaWNrJywgZm4sIHRoaXMpXHJcblx0XHQgICAgLm9uKGxpbmssICdjbGljaycsIHRoaXMuX3JlZm9jdXNPbk1hcCwgdGhpcyk7XHJcblxyXG5cdFx0cmV0dXJuIGxpbms7XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZURpc2FibGVkOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgbWFwID0gdGhpcy5fbWFwLFxyXG5cdFx0ICAgIGNsYXNzTmFtZSA9ICdsZWFmbGV0LWRpc2FibGVkJztcclxuXHJcblx0XHRMLkRvbVV0aWwucmVtb3ZlQ2xhc3ModGhpcy5fem9vbUluQnV0dG9uLCBjbGFzc05hbWUpO1xyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX3pvb21PdXRCdXR0b24sIGNsYXNzTmFtZSk7XHJcblxyXG5cdFx0aWYgKHRoaXMuX2Rpc2FibGVkIHx8IG1hcC5fem9vbSA9PT0gbWFwLmdldE1pblpvb20oKSkge1xyXG5cdFx0XHRMLkRvbVV0aWwuYWRkQ2xhc3ModGhpcy5fem9vbU91dEJ1dHRvbiwgY2xhc3NOYW1lKTtcclxuXHRcdH1cclxuXHRcdGlmICh0aGlzLl9kaXNhYmxlZCB8fCBtYXAuX3pvb20gPT09IG1hcC5nZXRNYXhab29tKCkpIHtcclxuXHRcdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX3pvb21JbkJ1dHRvbiwgY2xhc3NOYW1lKTtcclxuXHRcdH1cclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBNYXBcclxuLy8gQHNlY3Rpb24gQ29udHJvbCBvcHRpb25zXHJcbi8vIEBvcHRpb24gem9vbUNvbnRyb2w6IEJvb2xlYW4gPSB0cnVlXHJcbi8vIFdoZXRoZXIgYSBbem9vbSBjb250cm9sXSgjY29udHJvbC16b29tKSBpcyBhZGRlZCB0byB0aGUgbWFwIGJ5IGRlZmF1bHQuXHJcbkwuTWFwLm1lcmdlT3B0aW9ucyh7XHJcblx0em9vbUNvbnRyb2w6IHRydWVcclxufSk7XHJcblxyXG5MLk1hcC5hZGRJbml0SG9vayhmdW5jdGlvbiAoKSB7XHJcblx0aWYgKHRoaXMub3B0aW9ucy56b29tQ29udHJvbCkge1xyXG5cdFx0dGhpcy56b29tQ29udHJvbCA9IG5ldyBMLkNvbnRyb2wuWm9vbSgpO1xyXG5cdFx0dGhpcy5hZGRDb250cm9sKHRoaXMuem9vbUNvbnRyb2wpO1xyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAbmFtZXNwYWNlIENvbnRyb2wuWm9vbVxyXG4vLyBAZmFjdG9yeSBMLmNvbnRyb2wuem9vbShvcHRpb25zOiBDb250cm9sLlpvb20gb3B0aW9ucylcclxuLy8gQ3JlYXRlcyBhIHpvb20gY29udHJvbFxyXG5MLmNvbnRyb2wuem9vbSA9IGZ1bmN0aW9uIChvcHRpb25zKSB7XHJcblx0cmV0dXJuIG5ldyBMLkNvbnRyb2wuWm9vbShvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcclxuICogQGNsYXNzIENvbnRyb2wuQXR0cmlidXRpb25cclxuICogQGFrYSBMLkNvbnRyb2wuQXR0cmlidXRpb25cclxuICogQGluaGVyaXRzIENvbnRyb2xcclxuICpcclxuICogVGhlIGF0dHJpYnV0aW9uIGNvbnRyb2wgYWxsb3dzIHlvdSB0byBkaXNwbGF5IGF0dHJpYnV0aW9uIGRhdGEgaW4gYSBzbWFsbCB0ZXh0IGJveCBvbiBhIG1hcC4gSXQgaXMgcHV0IG9uIHRoZSBtYXAgYnkgZGVmYXVsdCB1bmxlc3MgeW91IHNldCBpdHMgW2BhdHRyaWJ1dGlvbkNvbnRyb2xgIG9wdGlvbl0oI21hcC1hdHRyaWJ1dGlvbmNvbnRyb2wpIHRvIGBmYWxzZWAsIGFuZCBpdCBmZXRjaGVzIGF0dHJpYnV0aW9uIHRleHRzIGZyb20gbGF5ZXJzIHdpdGggdGhlIFtgZ2V0QXR0cmlidXRpb25gIG1ldGhvZF0oI2xheWVyLWdldGF0dHJpYnV0aW9uKSBhdXRvbWF0aWNhbGx5LiBFeHRlbmRzIENvbnRyb2wuXHJcbiAqL1xyXG5cclxuTC5Db250cm9sLkF0dHJpYnV0aW9uID0gTC5Db250cm9sLmV4dGVuZCh7XHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIENvbnRyb2wuQXR0cmlidXRpb24gb3B0aW9uc1xyXG5cdG9wdGlvbnM6IHtcclxuXHRcdHBvc2l0aW9uOiAnYm90dG9tcmlnaHQnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gcHJlZml4OiBTdHJpbmcgPSAnTGVhZmxldCdcclxuXHRcdC8vIFRoZSBIVE1MIHRleHQgc2hvd24gYmVmb3JlIHRoZSBhdHRyaWJ1dGlvbnMuIFBhc3MgYGZhbHNlYCB0byBkaXNhYmxlLlxyXG5cdFx0cHJlZml4OiAnPGEgaHJlZj1cImh0dHA6Ly9sZWFmbGV0anMuY29tXCIgdGl0bGU9XCJBIEpTIGxpYnJhcnkgZm9yIGludGVyYWN0aXZlIG1hcHNcIj5MZWFmbGV0PC9hPidcclxuXHR9LFxyXG5cclxuXHRpbml0aWFsaXplOiBmdW5jdGlvbiAob3B0aW9ucykge1xyXG5cdFx0TC5zZXRPcHRpb25zKHRoaXMsIG9wdGlvbnMpO1xyXG5cclxuXHRcdHRoaXMuX2F0dHJpYnV0aW9ucyA9IHt9O1xyXG5cdH0sXHJcblxyXG5cdG9uQWRkOiBmdW5jdGlvbiAobWFwKSB7XHJcblx0XHRtYXAuYXR0cmlidXRpb25Db250cm9sID0gdGhpcztcclxuXHRcdHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsICdsZWFmbGV0LWNvbnRyb2wtYXR0cmlidXRpb24nKTtcclxuXHRcdGlmIChMLkRvbUV2ZW50KSB7XHJcblx0XHRcdEwuRG9tRXZlbnQuZGlzYWJsZUNsaWNrUHJvcGFnYXRpb24odGhpcy5fY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHJcblx0XHQvLyBUT0RPIHVnbHksIHJlZmFjdG9yXHJcblx0XHRmb3IgKHZhciBpIGluIG1hcC5fbGF5ZXJzKSB7XHJcblx0XHRcdGlmIChtYXAuX2xheWVyc1tpXS5nZXRBdHRyaWJ1dGlvbikge1xyXG5cdFx0XHRcdHRoaXMuYWRkQXR0cmlidXRpb24obWFwLl9sYXllcnNbaV0uZ2V0QXR0cmlidXRpb24oKSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHJcblx0XHRyZXR1cm4gdGhpcy5fY29udGFpbmVyO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2Qgc2V0UHJlZml4KHByZWZpeDogU3RyaW5nKTogdGhpc1xyXG5cdC8vIFNldHMgdGhlIHRleHQgYmVmb3JlIHRoZSBhdHRyaWJ1dGlvbnMuXHJcblx0c2V0UHJlZml4OiBmdW5jdGlvbiAocHJlZml4KSB7XHJcblx0XHR0aGlzLm9wdGlvbnMucHJlZml4ID0gcHJlZml4O1xyXG5cdFx0dGhpcy5fdXBkYXRlKCk7XHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZEF0dHJpYnV0aW9uKHRleHQ6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBBZGRzIGFuIGF0dHJpYnV0aW9uIHRleHQgKGUuZy4gYCdWZWN0b3IgZGF0YSAmY29weTsgTWFwYm94J2ApLlxyXG5cdGFkZEF0dHJpYnV0aW9uOiBmdW5jdGlvbiAodGV4dCkge1xyXG5cdFx0aWYgKCF0ZXh0KSB7IHJldHVybiB0aGlzOyB9XHJcblxyXG5cdFx0aWYgKCF0aGlzLl9hdHRyaWJ1dGlvbnNbdGV4dF0pIHtcclxuXHRcdFx0dGhpcy5fYXR0cmlidXRpb25zW3RleHRdID0gMDtcclxuXHRcdH1cclxuXHRcdHRoaXMuX2F0dHJpYnV0aW9uc1t0ZXh0XSsrO1xyXG5cclxuXHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgcmVtb3ZlQXR0cmlidXRpb24odGV4dDogU3RyaW5nKTogdGhpc1xyXG5cdC8vIFJlbW92ZXMgYW4gYXR0cmlidXRpb24gdGV4dC5cclxuXHRyZW1vdmVBdHRyaWJ1dGlvbjogZnVuY3Rpb24gKHRleHQpIHtcclxuXHRcdGlmICghdGV4dCkgeyByZXR1cm4gdGhpczsgfVxyXG5cclxuXHRcdGlmICh0aGlzLl9hdHRyaWJ1dGlvbnNbdGV4dF0pIHtcclxuXHRcdFx0dGhpcy5fYXR0cmlidXRpb25zW3RleHRdLS07XHJcblx0XHRcdHRoaXMuX3VwZGF0ZSgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF91cGRhdGU6IGZ1bmN0aW9uICgpIHtcclxuXHRcdGlmICghdGhpcy5fbWFwKSB7IHJldHVybjsgfVxyXG5cclxuXHRcdHZhciBhdHRyaWJzID0gW107XHJcblxyXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLl9hdHRyaWJ1dGlvbnMpIHtcclxuXHRcdFx0aWYgKHRoaXMuX2F0dHJpYnV0aW9uc1tpXSkge1xyXG5cdFx0XHRcdGF0dHJpYnMucHVzaChpKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHZhciBwcmVmaXhBbmRBdHRyaWJzID0gW107XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5wcmVmaXgpIHtcclxuXHRcdFx0cHJlZml4QW5kQXR0cmlicy5wdXNoKHRoaXMub3B0aW9ucy5wcmVmaXgpO1xyXG5cdFx0fVxyXG5cdFx0aWYgKGF0dHJpYnMubGVuZ3RoKSB7XHJcblx0XHRcdHByZWZpeEFuZEF0dHJpYnMucHVzaChhdHRyaWJzLmpvaW4oJywgJykpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2NvbnRhaW5lci5pbm5lckhUTUwgPSBwcmVmaXhBbmRBdHRyaWJzLmpvaW4oJyB8ICcpO1xyXG5cdH1cclxufSk7XHJcblxyXG4vLyBAbmFtZXNwYWNlIE1hcFxyXG4vLyBAc2VjdGlvbiBDb250cm9sIG9wdGlvbnNcclxuLy8gQG9wdGlvbiBhdHRyaWJ1dGlvbkNvbnRyb2w6IEJvb2xlYW4gPSB0cnVlXHJcbi8vIFdoZXRoZXIgYSBbYXR0cmlidXRpb24gY29udHJvbF0oI2NvbnRyb2wtYXR0cmlidXRpb24pIGlzIGFkZGVkIHRvIHRoZSBtYXAgYnkgZGVmYXVsdC5cclxuTC5NYXAubWVyZ2VPcHRpb25zKHtcclxuXHRhdHRyaWJ1dGlvbkNvbnRyb2w6IHRydWVcclxufSk7XHJcblxyXG5MLk1hcC5hZGRJbml0SG9vayhmdW5jdGlvbiAoKSB7XHJcblx0aWYgKHRoaXMub3B0aW9ucy5hdHRyaWJ1dGlvbkNvbnRyb2wpIHtcclxuXHRcdG5ldyBMLkNvbnRyb2wuQXR0cmlidXRpb24oKS5hZGRUbyh0aGlzKTtcclxuXHR9XHJcbn0pO1xyXG5cclxuLy8gQG5hbWVzcGFjZSBDb250cm9sLkF0dHJpYnV0aW9uXHJcbi8vIEBmYWN0b3J5IEwuY29udHJvbC5hdHRyaWJ1dGlvbihvcHRpb25zOiBDb250cm9sLkF0dHJpYnV0aW9uIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYW4gYXR0cmlidXRpb24gY29udHJvbC5cclxuTC5jb250cm9sLmF0dHJpYnV0aW9uID0gZnVuY3Rpb24gKG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuQ29udHJvbC5BdHRyaWJ1dGlvbihvcHRpb25zKTtcclxufTtcclxuXG5cblxuLypcbiAqIEBjbGFzcyBDb250cm9sLlNjYWxlXG4gKiBAYWthIEwuQ29udHJvbC5TY2FsZVxuICogQGluaGVyaXRzIENvbnRyb2xcbiAqXG4gKiBBIHNpbXBsZSBzY2FsZSBjb250cm9sIHRoYXQgc2hvd3MgdGhlIHNjYWxlIG9mIHRoZSBjdXJyZW50IGNlbnRlciBvZiBzY3JlZW4gaW4gbWV0cmljIChtL2ttKSBhbmQgaW1wZXJpYWwgKG1pL2Z0KSBzeXN0ZW1zLiBFeHRlbmRzIGBDb250cm9sYC5cbiAqXG4gKiBAZXhhbXBsZVxuICpcbiAqIGBgYGpzXG4gKiBMLmNvbnRyb2wuc2NhbGUoKS5hZGRUbyhtYXApO1xuICogYGBgXG4gKi9cblxuTC5Db250cm9sLlNjYWxlID0gTC5Db250cm9sLmV4dGVuZCh7XG5cdC8vIEBzZWN0aW9uXG5cdC8vIEBha2EgQ29udHJvbC5TY2FsZSBvcHRpb25zXG5cdG9wdGlvbnM6IHtcblx0XHRwb3NpdGlvbjogJ2JvdHRvbWxlZnQnLFxuXG5cdFx0Ly8gQG9wdGlvbiBtYXhXaWR0aDogTnVtYmVyID0gMTAwXG5cdFx0Ly8gTWF4aW11bSB3aWR0aCBvZiB0aGUgY29udHJvbCBpbiBwaXhlbHMuIFRoZSB3aWR0aCBpcyBzZXQgZHluYW1pY2FsbHkgdG8gc2hvdyByb3VuZCB2YWx1ZXMgKGUuZy4gMTAwLCAyMDAsIDUwMCkuXG5cdFx0bWF4V2lkdGg6IDEwMCxcblxuXHRcdC8vIEBvcHRpb24gbWV0cmljOiBCb29sZWFuID0gVHJ1ZVxuXHRcdC8vIFdoZXRoZXIgdG8gc2hvdyB0aGUgbWV0cmljIHNjYWxlIGxpbmUgKG0va20pLlxuXHRcdG1ldHJpYzogdHJ1ZSxcblxuXHRcdC8vIEBvcHRpb24gaW1wZXJpYWw6IEJvb2xlYW4gPSBUcnVlXG5cdFx0Ly8gV2hldGhlciB0byBzaG93IHRoZSBpbXBlcmlhbCBzY2FsZSBsaW5lIChtaS9mdCkuXG5cdFx0aW1wZXJpYWw6IHRydWVcblxuXHRcdC8vIEBvcHRpb24gdXBkYXRlV2hlbklkbGU6IEJvb2xlYW4gPSBmYWxzZVxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGNvbnRyb2wgaXMgdXBkYXRlZCBvbiBbYG1vdmVlbmRgXSgjbWFwLW1vdmVlbmQpLCBvdGhlcndpc2UgaXQncyBhbHdheXMgdXAtdG8tZGF0ZSAodXBkYXRlZCBvbiBbYG1vdmVgXSgjbWFwLW1vdmUpKS5cblx0fSxcblxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xuXHRcdHZhciBjbGFzc05hbWUgPSAnbGVhZmxldC1jb250cm9sLXNjYWxlJyxcblx0XHQgICAgY29udGFpbmVyID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lKSxcblx0XHQgICAgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcblxuXHRcdHRoaXMuX2FkZFNjYWxlcyhvcHRpb25zLCBjbGFzc05hbWUgKyAnLWxpbmUnLCBjb250YWluZXIpO1xuXG5cdFx0bWFwLm9uKG9wdGlvbnMudXBkYXRlV2hlbklkbGUgPyAnbW92ZWVuZCcgOiAnbW92ZScsIHRoaXMuX3VwZGF0ZSwgdGhpcyk7XG5cdFx0bWFwLndoZW5SZWFkeSh0aGlzLl91cGRhdGUsIHRoaXMpO1xuXG5cdFx0cmV0dXJuIGNvbnRhaW5lcjtcblx0fSxcblxuXHRvblJlbW92ZTogZnVuY3Rpb24gKG1hcCkge1xuXHRcdG1hcC5vZmYodGhpcy5vcHRpb25zLnVwZGF0ZVdoZW5JZGxlID8gJ21vdmVlbmQnIDogJ21vdmUnLCB0aGlzLl91cGRhdGUsIHRoaXMpO1xuXHR9LFxuXG5cdF9hZGRTY2FsZXM6IGZ1bmN0aW9uIChvcHRpb25zLCBjbGFzc05hbWUsIGNvbnRhaW5lcikge1xuXHRcdGlmIChvcHRpb25zLm1ldHJpYykge1xuXHRcdFx0dGhpcy5fbVNjYWxlID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lLCBjb250YWluZXIpO1xuXHRcdH1cblx0XHRpZiAob3B0aW9ucy5pbXBlcmlhbCkge1xuXHRcdFx0dGhpcy5faVNjYWxlID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lLCBjb250YWluZXIpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlOiBmdW5jdGlvbiAoKSB7XG5cdFx0dmFyIG1hcCA9IHRoaXMuX21hcCxcblx0XHQgICAgeSA9IG1hcC5nZXRTaXplKCkueSAvIDI7XG5cblx0XHR2YXIgbWF4TWV0ZXJzID0gbWFwLmRpc3RhbmNlKFxuXHRcdFx0XHRtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyhbMCwgeV0pLFxuXHRcdFx0XHRtYXAuY29udGFpbmVyUG9pbnRUb0xhdExuZyhbdGhpcy5vcHRpb25zLm1heFdpZHRoLCB5XSkpO1xuXG5cdFx0dGhpcy5fdXBkYXRlU2NhbGVzKG1heE1ldGVycyk7XG5cdH0sXG5cblx0X3VwZGF0ZVNjYWxlczogZnVuY3Rpb24gKG1heE1ldGVycykge1xuXHRcdGlmICh0aGlzLm9wdGlvbnMubWV0cmljICYmIG1heE1ldGVycykge1xuXHRcdFx0dGhpcy5fdXBkYXRlTWV0cmljKG1heE1ldGVycyk7XG5cdFx0fVxuXHRcdGlmICh0aGlzLm9wdGlvbnMuaW1wZXJpYWwgJiYgbWF4TWV0ZXJzKSB7XG5cdFx0XHR0aGlzLl91cGRhdGVJbXBlcmlhbChtYXhNZXRlcnMpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlTWV0cmljOiBmdW5jdGlvbiAobWF4TWV0ZXJzKSB7XG5cdFx0dmFyIG1ldGVycyA9IHRoaXMuX2dldFJvdW5kTnVtKG1heE1ldGVycyksXG5cdFx0ICAgIGxhYmVsID0gbWV0ZXJzIDwgMTAwMCA/IG1ldGVycyArICcgbScgOiAobWV0ZXJzIC8gMTAwMCkgKyAnIGttJztcblxuXHRcdHRoaXMuX3VwZGF0ZVNjYWxlKHRoaXMuX21TY2FsZSwgbGFiZWwsIG1ldGVycyAvIG1heE1ldGVycyk7XG5cdH0sXG5cblx0X3VwZGF0ZUltcGVyaWFsOiBmdW5jdGlvbiAobWF4TWV0ZXJzKSB7XG5cdFx0dmFyIG1heEZlZXQgPSBtYXhNZXRlcnMgKiAzLjI4MDgzOTksXG5cdFx0ICAgIG1heE1pbGVzLCBtaWxlcywgZmVldDtcblxuXHRcdGlmIChtYXhGZWV0ID4gNTI4MCkge1xuXHRcdFx0bWF4TWlsZXMgPSBtYXhGZWV0IC8gNTI4MDtcblx0XHRcdG1pbGVzID0gdGhpcy5fZ2V0Um91bmROdW0obWF4TWlsZXMpO1xuXHRcdFx0dGhpcy5fdXBkYXRlU2NhbGUodGhpcy5faVNjYWxlLCBtaWxlcyArICcgbWknLCBtaWxlcyAvIG1heE1pbGVzKTtcblxuXHRcdH0gZWxzZSB7XG5cdFx0XHRmZWV0ID0gdGhpcy5fZ2V0Um91bmROdW0obWF4RmVldCk7XG5cdFx0XHR0aGlzLl91cGRhdGVTY2FsZSh0aGlzLl9pU2NhbGUsIGZlZXQgKyAnIGZ0JywgZmVldCAvIG1heEZlZXQpO1xuXHRcdH1cblx0fSxcblxuXHRfdXBkYXRlU2NhbGU6IGZ1bmN0aW9uIChzY2FsZSwgdGV4dCwgcmF0aW8pIHtcblx0XHRzY2FsZS5zdHlsZS53aWR0aCA9IE1hdGgucm91bmQodGhpcy5vcHRpb25zLm1heFdpZHRoICogcmF0aW8pICsgJ3B4Jztcblx0XHRzY2FsZS5pbm5lckhUTUwgPSB0ZXh0O1xuXHR9LFxuXG5cdF9nZXRSb3VuZE51bTogZnVuY3Rpb24gKG51bSkge1xuXHRcdHZhciBwb3cxMCA9IE1hdGgucG93KDEwLCAoTWF0aC5mbG9vcihudW0pICsgJycpLmxlbmd0aCAtIDEpLFxuXHRcdCAgICBkID0gbnVtIC8gcG93MTA7XG5cblx0XHRkID0gZCA+PSAxMCA/IDEwIDpcblx0XHQgICAgZCA+PSA1ID8gNSA6XG5cdFx0ICAgIGQgPj0gMyA/IDMgOlxuXHRcdCAgICBkID49IDIgPyAyIDogMTtcblxuXHRcdHJldHVybiBwb3cxMCAqIGQ7XG5cdH1cbn0pO1xuXG5cbi8vIEBmYWN0b3J5IEwuY29udHJvbC5zY2FsZShvcHRpb25zPzogQ29udHJvbC5TY2FsZSBvcHRpb25zKVxuLy8gQ3JlYXRlcyBhbiBzY2FsZSBjb250cm9sIHdpdGggdGhlIGdpdmVuIG9wdGlvbnMuXG5MLmNvbnRyb2wuc2NhbGUgPSBmdW5jdGlvbiAob3B0aW9ucykge1xuXHRyZXR1cm4gbmV3IEwuQ29udHJvbC5TY2FsZShvcHRpb25zKTtcbn07XG5cblxuXG4vKlxyXG4gKiBAY2xhc3MgQ29udHJvbC5MYXllcnNcclxuICogQGFrYSBMLkNvbnRyb2wuTGF5ZXJzXHJcbiAqIEBpbmhlcml0cyBDb250cm9sXHJcbiAqXHJcbiAqIFRoZSBsYXllcnMgY29udHJvbCBnaXZlcyB1c2VycyB0aGUgYWJpbGl0eSB0byBzd2l0Y2ggYmV0d2VlbiBkaWZmZXJlbnQgYmFzZSBsYXllcnMgYW5kIHN3aXRjaCBvdmVybGF5cyBvbi9vZmYgKGNoZWNrIG91dCB0aGUgW2RldGFpbGVkIGV4YW1wbGVdKGh0dHA6Ly9sZWFmbGV0anMuY29tL2V4YW1wbGVzL2xheWVycy1jb250cm9sLmh0bWwpKS4gRXh0ZW5kcyBgQ29udHJvbGAuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHZhciBiYXNlTGF5ZXJzID0ge1xyXG4gKiBcdFwiTWFwYm94XCI6IG1hcGJveCxcclxuICogXHRcIk9wZW5TdHJlZXRNYXBcIjogb3NtXHJcbiAqIH07XHJcbiAqXHJcbiAqIHZhciBvdmVybGF5cyA9IHtcclxuICogXHRcIk1hcmtlclwiOiBtYXJrZXIsXHJcbiAqIFx0XCJSb2Fkc1wiOiByb2Fkc0xheWVyXHJcbiAqIH07XHJcbiAqXHJcbiAqIEwuY29udHJvbC5sYXllcnMoYmFzZUxheWVycywgb3ZlcmxheXMpLmFkZFRvKG1hcCk7XHJcbiAqIGBgYFxyXG4gKlxyXG4gKiBUaGUgYGJhc2VMYXllcnNgIGFuZCBgb3ZlcmxheXNgIHBhcmFtZXRlcnMgYXJlIG9iamVjdCBsaXRlcmFscyB3aXRoIGxheWVyIG5hbWVzIGFzIGtleXMgYW5kIGBMYXllcmAgb2JqZWN0cyBhcyB2YWx1ZXM6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHtcclxuICogICAgIFwiPHNvbWVOYW1lMT5cIjogbGF5ZXIxLFxyXG4gKiAgICAgXCI8c29tZU5hbWUyPlwiOiBsYXllcjJcclxuICogfVxyXG4gKiBgYGBcclxuICpcclxuICogVGhlIGxheWVyIG5hbWVzIGNhbiBjb250YWluIEhUTUwsIHdoaWNoIGFsbG93cyB5b3UgdG8gYWRkIGFkZGl0aW9uYWwgc3R5bGluZyB0byB0aGUgaXRlbXM6XHJcbiAqXHJcbiAqIGBgYGpzXHJcbiAqIHtcIjxpbWcgc3JjPSdteS1sYXllci1pY29uJyAvPiA8c3BhbiBjbGFzcz0nbXktbGF5ZXItaXRlbSc+TXkgTGF5ZXI8L3NwYW4+XCI6IG15TGF5ZXJ9XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcblxyXG5MLkNvbnRyb2wuTGF5ZXJzID0gTC5Db250cm9sLmV4dGVuZCh7XHJcblx0Ly8gQHNlY3Rpb25cclxuXHQvLyBAYWthIENvbnRyb2wuTGF5ZXJzIG9wdGlvbnNcclxuXHRvcHRpb25zOiB7XHJcblx0XHQvLyBAb3B0aW9uIGNvbGxhcHNlZDogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGNvbnRyb2wgd2lsbCBiZSBjb2xsYXBzZWQgaW50byBhbiBpY29uIGFuZCBleHBhbmRlZCBvbiBtb3VzZSBob3ZlciBvciB0b3VjaC5cclxuXHRcdGNvbGxhcHNlZDogdHJ1ZSxcclxuXHRcdHBvc2l0aW9uOiAndG9wcmlnaHQnLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gYXV0b1pJbmRleDogQm9vbGVhbiA9IHRydWVcclxuXHRcdC8vIElmIGB0cnVlYCwgdGhlIGNvbnRyb2wgd2lsbCBhc3NpZ24gekluZGV4ZXMgaW4gaW5jcmVhc2luZyBvcmRlciB0byBhbGwgb2YgaXRzIGxheWVycyBzbyB0aGF0IHRoZSBvcmRlciBpcyBwcmVzZXJ2ZWQgd2hlbiBzd2l0Y2hpbmcgdGhlbSBvbi9vZmYuXHJcblx0XHRhdXRvWkluZGV4OiB0cnVlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gaGlkZVNpbmdsZUJhc2U6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gSWYgYHRydWVgLCB0aGUgYmFzZSBsYXllcnMgaW4gdGhlIGNvbnRyb2wgd2lsbCBiZSBoaWRkZW4gd2hlbiB0aGVyZSBpcyBvbmx5IG9uZS5cclxuXHRcdGhpZGVTaW5nbGVCYXNlOiBmYWxzZSxcclxuXHJcblx0XHQvLyBAb3B0aW9uIHNvcnRMYXllcnM6IEJvb2xlYW4gPSBmYWxzZVxyXG5cdFx0Ly8gV2hldGhlciB0byBzb3J0IHRoZSBsYXllcnMuIFdoZW4gYGZhbHNlYCwgbGF5ZXJzIHdpbGwga2VlcCB0aGUgb3JkZXJcclxuXHRcdC8vIGluIHdoaWNoIHRoZXkgd2VyZSBhZGRlZCB0byB0aGUgY29udHJvbC5cclxuXHRcdHNvcnRMYXllcnM6IGZhbHNlLFxyXG5cclxuXHRcdC8vIEBvcHRpb24gc29ydEZ1bmN0aW9uOiBGdW5jdGlvbiA9ICpcclxuXHRcdC8vIEEgW2NvbXBhcmUgZnVuY3Rpb25dKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0FycmF5L3NvcnQpXHJcblx0XHQvLyB0aGF0IHdpbGwgYmUgdXNlZCBmb3Igc29ydGluZyB0aGUgbGF5ZXJzLCB3aGVuIGBzb3J0TGF5ZXJzYCBpcyBgdHJ1ZWAuXHJcblx0XHQvLyBUaGUgZnVuY3Rpb24gcmVjZWl2ZXMgYm90aCB0aGUgYEwuTGF5ZXJgIGluc3RhbmNlcyBhbmQgdGhlaXIgbmFtZXMsIGFzIGluXHJcblx0XHQvLyBgc29ydEZ1bmN0aW9uKGxheWVyQSwgbGF5ZXJCLCBuYW1lQSwgbmFtZUIpYC5cclxuXHRcdC8vIEJ5IGRlZmF1bHQsIGl0IHNvcnRzIGxheWVycyBhbHBoYWJldGljYWxseSBieSB0aGVpciBuYW1lLlxyXG5cdFx0c29ydEZ1bmN0aW9uOiBmdW5jdGlvbiAobGF5ZXJBLCBsYXllckIsIG5hbWVBLCBuYW1lQikge1xyXG5cdFx0XHRyZXR1cm4gbmFtZUEgPCBuYW1lQiA/IC0xIDogKG5hbWVCIDwgbmFtZUEgPyAxIDogMCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0aW5pdGlhbGl6ZTogZnVuY3Rpb24gKGJhc2VMYXllcnMsIG92ZXJsYXlzLCBvcHRpb25zKSB7XHJcblx0XHRMLnNldE9wdGlvbnModGhpcywgb3B0aW9ucyk7XHJcblxyXG5cdFx0dGhpcy5fbGF5ZXJzID0gW107XHJcblx0XHR0aGlzLl9sYXN0WkluZGV4ID0gMDtcclxuXHRcdHRoaXMuX2hhbmRsaW5nQ2xpY2sgPSBmYWxzZTtcclxuXHJcblx0XHRmb3IgKHZhciBpIGluIGJhc2VMYXllcnMpIHtcclxuXHRcdFx0dGhpcy5fYWRkTGF5ZXIoYmFzZUxheWVyc1tpXSwgaSk7XHJcblx0XHR9XHJcblxyXG5cdFx0Zm9yIChpIGluIG92ZXJsYXlzKSB7XHJcblx0XHRcdHRoaXMuX2FkZExheWVyKG92ZXJsYXlzW2ldLCBpLCB0cnVlKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG5cdFx0dGhpcy5faW5pdExheW91dCgpO1xyXG5cdFx0dGhpcy5fdXBkYXRlKCk7XHJcblxyXG5cdFx0dGhpcy5fbWFwID0gbWFwO1xyXG5cdFx0bWFwLm9uKCd6b29tZW5kJywgdGhpcy5fY2hlY2tEaXNhYmxlZExheWVycywgdGhpcyk7XHJcblxyXG5cdFx0cmV0dXJuIHRoaXMuX2NvbnRhaW5lcjtcclxuXHR9LFxyXG5cclxuXHRvblJlbW92ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0dGhpcy5fbWFwLm9mZignem9vbWVuZCcsIHRoaXMuX2NoZWNrRGlzYWJsZWRMYXllcnMsIHRoaXMpO1xyXG5cclxuXHRcdGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5fbGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdHRoaXMuX2xheWVyc1tpXS5sYXllci5vZmYoJ2FkZCByZW1vdmUnLCB0aGlzLl9vbkxheWVyQ2hhbmdlLCB0aGlzKTtcclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHQvLyBAbWV0aG9kIGFkZEJhc2VMYXllcihsYXllcjogTGF5ZXIsIG5hbWU6IFN0cmluZyk6IHRoaXNcclxuXHQvLyBBZGRzIGEgYmFzZSBsYXllciAocmFkaW8gYnV0dG9uIGVudHJ5KSB3aXRoIHRoZSBnaXZlbiBuYW1lIHRvIHRoZSBjb250cm9sLlxyXG5cdGFkZEJhc2VMYXllcjogZnVuY3Rpb24gKGxheWVyLCBuYW1lKSB7XHJcblx0XHR0aGlzLl9hZGRMYXllcihsYXllciwgbmFtZSk7XHJcblx0XHRyZXR1cm4gKHRoaXMuX21hcCkgPyB0aGlzLl91cGRhdGUoKSA6IHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBhZGRPdmVybGF5KGxheWVyOiBMYXllciwgbmFtZTogU3RyaW5nKTogdGhpc1xyXG5cdC8vIEFkZHMgYW4gb3ZlcmxheSAoY2hlY2tib3ggZW50cnkpIHdpdGggdGhlIGdpdmVuIG5hbWUgdG8gdGhlIGNvbnRyb2wuXHJcblx0YWRkT3ZlcmxheTogZnVuY3Rpb24gKGxheWVyLCBuYW1lKSB7XHJcblx0XHR0aGlzLl9hZGRMYXllcihsYXllciwgbmFtZSwgdHJ1ZSk7XHJcblx0XHRyZXR1cm4gKHRoaXMuX21hcCkgPyB0aGlzLl91cGRhdGUoKSA6IHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCByZW1vdmVMYXllcihsYXllcjogTGF5ZXIpOiB0aGlzXHJcblx0Ly8gUmVtb3ZlIHRoZSBnaXZlbiBsYXllciBmcm9tIHRoZSBjb250cm9sLlxyXG5cdHJlbW92ZUxheWVyOiBmdW5jdGlvbiAobGF5ZXIpIHtcclxuXHRcdGxheWVyLm9mZignYWRkIHJlbW92ZScsIHRoaXMuX29uTGF5ZXJDaGFuZ2UsIHRoaXMpO1xyXG5cclxuXHRcdHZhciBvYmogPSB0aGlzLl9nZXRMYXllcihMLnN0YW1wKGxheWVyKSk7XHJcblx0XHRpZiAob2JqKSB7XHJcblx0XHRcdHRoaXMuX2xheWVycy5zcGxpY2UodGhpcy5fbGF5ZXJzLmluZGV4T2Yob2JqKSwgMSk7XHJcblx0XHR9XHJcblx0XHRyZXR1cm4gKHRoaXMuX21hcCkgPyB0aGlzLl91cGRhdGUoKSA6IHRoaXM7XHJcblx0fSxcclxuXHJcblx0Ly8gQG1ldGhvZCBleHBhbmQoKTogdGhpc1xyXG5cdC8vIEV4cGFuZCB0aGUgY29udHJvbCBjb250YWluZXIgaWYgY29sbGFwc2VkLlxyXG5cdGV4cGFuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLmFkZENsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtZXhwYW5kZWQnKTtcclxuXHRcdHRoaXMuX2Zvcm0uc3R5bGUuaGVpZ2h0ID0gbnVsbDtcclxuXHRcdHZhciBhY2NlcHRhYmxlSGVpZ2h0ID0gdGhpcy5fbWFwLmdldFNpemUoKS55IC0gKHRoaXMuX2NvbnRhaW5lci5vZmZzZXRUb3AgKyA1MCk7XHJcblx0XHRpZiAoYWNjZXB0YWJsZUhlaWdodCA8IHRoaXMuX2Zvcm0uY2xpZW50SGVpZ2h0KSB7XHJcblx0XHRcdEwuRG9tVXRpbC5hZGRDbGFzcyh0aGlzLl9mb3JtLCAnbGVhZmxldC1jb250cm9sLWxheWVycy1zY3JvbGxiYXInKTtcclxuXHRcdFx0dGhpcy5fZm9ybS5zdHlsZS5oZWlnaHQgPSBhY2NlcHRhYmxlSGVpZ2h0ICsgJ3B4JztcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdEwuRG9tVXRpbC5yZW1vdmVDbGFzcyh0aGlzLl9mb3JtLCAnbGVhZmxldC1jb250cm9sLWxheWVycy1zY3JvbGxiYXInKTtcclxuXHRcdH1cclxuXHRcdHRoaXMuX2NoZWNrRGlzYWJsZWRMYXllcnMoKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdC8vIEBtZXRob2QgY29sbGFwc2UoKTogdGhpc1xyXG5cdC8vIENvbGxhcHNlIHRoZSBjb250cm9sIGNvbnRhaW5lciBpZiBleHBhbmRlZC5cclxuXHRjb2xsYXBzZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0TC5Eb21VdGlsLnJlbW92ZUNsYXNzKHRoaXMuX2NvbnRhaW5lciwgJ2xlYWZsZXQtY29udHJvbC1sYXllcnMtZXhwYW5kZWQnKTtcclxuXHRcdHJldHVybiB0aGlzO1xyXG5cdH0sXHJcblxyXG5cdF9pbml0TGF5b3V0OiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgY2xhc3NOYW1lID0gJ2xlYWZsZXQtY29udHJvbC1sYXllcnMnLFxyXG5cdFx0ICAgIGNvbnRhaW5lciA9IHRoaXMuX2NvbnRhaW5lciA9IEwuRG9tVXRpbC5jcmVhdGUoJ2RpdicsIGNsYXNzTmFtZSksXHJcblx0XHQgICAgY29sbGFwc2VkID0gdGhpcy5vcHRpb25zLmNvbGxhcHNlZDtcclxuXHJcblx0XHQvLyBtYWtlcyB0aGlzIHdvcmsgb24gSUUgdG91Y2ggZGV2aWNlcyBieSBzdG9wcGluZyBpdCBmcm9tIGZpcmluZyBhIG1vdXNlb3V0IGV2ZW50IHdoZW4gdGhlIHRvdWNoIGlzIHJlbGVhc2VkXHJcblx0XHRjb250YWluZXIuc2V0QXR0cmlidXRlKCdhcmlhLWhhc3BvcHVwJywgdHJ1ZSk7XHJcblxyXG5cdFx0TC5Eb21FdmVudC5kaXNhYmxlQ2xpY2tQcm9wYWdhdGlvbihjb250YWluZXIpO1xyXG5cdFx0aWYgKCFMLkJyb3dzZXIudG91Y2gpIHtcclxuXHRcdFx0TC5Eb21FdmVudC5kaXNhYmxlU2Nyb2xsUHJvcGFnYXRpb24oY29udGFpbmVyKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgZm9ybSA9IHRoaXMuX2Zvcm0gPSBMLkRvbVV0aWwuY3JlYXRlKCdmb3JtJywgY2xhc3NOYW1lICsgJy1saXN0Jyk7XHJcblxyXG5cdFx0aWYgKGNvbGxhcHNlZCkge1xyXG5cdFx0XHR0aGlzLl9tYXAub24oJ2NsaWNrJywgdGhpcy5jb2xsYXBzZSwgdGhpcyk7XHJcblxyXG5cdFx0XHRpZiAoIUwuQnJvd3Nlci5hbmRyb2lkKSB7XHJcblx0XHRcdFx0TC5Eb21FdmVudC5vbihjb250YWluZXIsIHtcclxuXHRcdFx0XHRcdG1vdXNlZW50ZXI6IHRoaXMuZXhwYW5kLFxyXG5cdFx0XHRcdFx0bW91c2VsZWF2ZTogdGhpcy5jb2xsYXBzZVxyXG5cdFx0XHRcdH0sIHRoaXMpO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblxyXG5cdFx0dmFyIGxpbmsgPSB0aGlzLl9sYXllcnNMaW5rID0gTC5Eb21VdGlsLmNyZWF0ZSgnYScsIGNsYXNzTmFtZSArICctdG9nZ2xlJywgY29udGFpbmVyKTtcclxuXHRcdGxpbmsuaHJlZiA9ICcjJztcclxuXHRcdGxpbmsudGl0bGUgPSAnTGF5ZXJzJztcclxuXHJcblx0XHRpZiAoTC5Ccm93c2VyLnRvdWNoKSB7XHJcblx0XHRcdEwuRG9tRXZlbnRcclxuXHRcdFx0ICAgIC5vbihsaW5rLCAnY2xpY2snLCBMLkRvbUV2ZW50LnN0b3ApXHJcblx0XHRcdCAgICAub24obGluaywgJ2NsaWNrJywgdGhpcy5leHBhbmQsIHRoaXMpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0TC5Eb21FdmVudC5vbihsaW5rLCAnZm9jdXMnLCB0aGlzLmV4cGFuZCwgdGhpcyk7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gd29yayBhcm91bmQgZm9yIEZpcmVmb3ggQW5kcm9pZCBpc3N1ZSBodHRwczovL2dpdGh1Yi5jb20vTGVhZmxldC9MZWFmbGV0L2lzc3Vlcy8yMDMzXHJcblx0XHRMLkRvbUV2ZW50Lm9uKGZvcm0sICdjbGljaycsIGZ1bmN0aW9uICgpIHtcclxuXHRcdFx0c2V0VGltZW91dChMLmJpbmQodGhpcy5fb25JbnB1dENsaWNrLCB0aGlzKSwgMCk7XHJcblx0XHR9LCB0aGlzKTtcclxuXHJcblx0XHQvLyBUT0RPIGtleWJvYXJkIGFjY2Vzc2liaWxpdHlcclxuXHJcblx0XHRpZiAoIWNvbGxhcHNlZCkge1xyXG5cdFx0XHR0aGlzLmV4cGFuZCgpO1xyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuX2Jhc2VMYXllcnNMaXN0ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lICsgJy1iYXNlJywgZm9ybSk7XHJcblx0XHR0aGlzLl9zZXBhcmF0b3IgPSBMLkRvbVV0aWwuY3JlYXRlKCdkaXYnLCBjbGFzc05hbWUgKyAnLXNlcGFyYXRvcicsIGZvcm0pO1xyXG5cdFx0dGhpcy5fb3ZlcmxheXNMaXN0ID0gTC5Eb21VdGlsLmNyZWF0ZSgnZGl2JywgY2xhc3NOYW1lICsgJy1vdmVybGF5cycsIGZvcm0pO1xyXG5cclxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChmb3JtKTtcclxuXHR9LFxyXG5cclxuXHRfZ2V0TGF5ZXI6IGZ1bmN0aW9uIChpZCkge1xyXG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLl9sYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHJcblx0XHRcdGlmICh0aGlzLl9sYXllcnNbaV0gJiYgTC5zdGFtcCh0aGlzLl9sYXllcnNbaV0ubGF5ZXIpID09PSBpZCkge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLl9sYXllcnNbaV07XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9LFxyXG5cclxuXHRfYWRkTGF5ZXI6IGZ1bmN0aW9uIChsYXllciwgbmFtZSwgb3ZlcmxheSkge1xyXG5cdFx0bGF5ZXIub24oJ2FkZCByZW1vdmUnLCB0aGlzLl9vbkxheWVyQ2hhbmdlLCB0aGlzKTtcclxuXHJcblx0XHR0aGlzLl9sYXllcnMucHVzaCh7XHJcblx0XHRcdGxheWVyOiBsYXllcixcclxuXHRcdFx0bmFtZTogbmFtZSxcclxuXHRcdFx0b3ZlcmxheTogb3ZlcmxheVxyXG5cdFx0fSk7XHJcblxyXG5cdFx0aWYgKHRoaXMub3B0aW9ucy5zb3J0TGF5ZXJzKSB7XHJcblx0XHRcdHRoaXMuX2xheWVycy5zb3J0KEwuYmluZChmdW5jdGlvbiAoYSwgYikge1xyXG5cdFx0XHRcdHJldHVybiB0aGlzLm9wdGlvbnMuc29ydEZ1bmN0aW9uKGEubGF5ZXIsIGIubGF5ZXIsIGEubmFtZSwgYi5uYW1lKTtcclxuXHRcdFx0fSwgdGhpcykpO1xyXG5cdFx0fVxyXG5cclxuXHRcdGlmICh0aGlzLm9wdGlvbnMuYXV0b1pJbmRleCAmJiBsYXllci5zZXRaSW5kZXgpIHtcclxuXHRcdFx0dGhpcy5fbGFzdFpJbmRleCsrO1xyXG5cdFx0XHRsYXllci5zZXRaSW5kZXgodGhpcy5fbGFzdFpJbmRleCk7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X3VwZGF0ZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0aWYgKCF0aGlzLl9jb250YWluZXIpIHsgcmV0dXJuIHRoaXM7IH1cclxuXHJcblx0XHRMLkRvbVV0aWwuZW1wdHkodGhpcy5fYmFzZUxheWVyc0xpc3QpO1xyXG5cdFx0TC5Eb21VdGlsLmVtcHR5KHRoaXMuX292ZXJsYXlzTGlzdCk7XHJcblxyXG5cdFx0dmFyIGJhc2VMYXllcnNQcmVzZW50LCBvdmVybGF5c1ByZXNlbnQsIGksIG9iaiwgYmFzZUxheWVyc0NvdW50ID0gMDtcclxuXHJcblx0XHRmb3IgKGkgPSAwOyBpIDwgdGhpcy5fbGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdG9iaiA9IHRoaXMuX2xheWVyc1tpXTtcclxuXHRcdFx0dGhpcy5fYWRkSXRlbShvYmopO1xyXG5cdFx0XHRvdmVybGF5c1ByZXNlbnQgPSBvdmVybGF5c1ByZXNlbnQgfHwgb2JqLm92ZXJsYXk7XHJcblx0XHRcdGJhc2VMYXllcnNQcmVzZW50ID0gYmFzZUxheWVyc1ByZXNlbnQgfHwgIW9iai5vdmVybGF5O1xyXG5cdFx0XHRiYXNlTGF5ZXJzQ291bnQgKz0gIW9iai5vdmVybGF5ID8gMSA6IDA7XHJcblx0XHR9XHJcblxyXG5cdFx0Ly8gSGlkZSBiYXNlIGxheWVycyBzZWN0aW9uIGlmIHRoZXJlJ3Mgb25seSBvbmUgbGF5ZXIuXHJcblx0XHRpZiAodGhpcy5vcHRpb25zLmhpZGVTaW5nbGVCYXNlKSB7XHJcblx0XHRcdGJhc2VMYXllcnNQcmVzZW50ID0gYmFzZUxheWVyc1ByZXNlbnQgJiYgYmFzZUxheWVyc0NvdW50ID4gMTtcclxuXHRcdFx0dGhpcy5fYmFzZUxheWVyc0xpc3Quc3R5bGUuZGlzcGxheSA9IGJhc2VMYXllcnNQcmVzZW50ID8gJycgOiAnbm9uZSc7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5fc2VwYXJhdG9yLnN0eWxlLmRpc3BsYXkgPSBvdmVybGF5c1ByZXNlbnQgJiYgYmFzZUxheWVyc1ByZXNlbnQgPyAnJyA6ICdub25lJztcclxuXHJcblx0XHRyZXR1cm4gdGhpcztcclxuXHR9LFxyXG5cclxuXHRfb25MYXllckNoYW5nZTogZnVuY3Rpb24gKGUpIHtcclxuXHRcdGlmICghdGhpcy5faGFuZGxpbmdDbGljaykge1xyXG5cdFx0XHR0aGlzLl91cGRhdGUoKTtcclxuXHRcdH1cclxuXHJcblx0XHR2YXIgb2JqID0gdGhpcy5fZ2V0TGF5ZXIoTC5zdGFtcChlLnRhcmdldCkpO1xyXG5cclxuXHRcdC8vIEBuYW1lc3BhY2UgTWFwXHJcblx0XHQvLyBAc2VjdGlvbiBMYXllciBldmVudHNcclxuXHRcdC8vIEBldmVudCBiYXNlbGF5ZXJjaGFuZ2U6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiB0aGUgYmFzZSBsYXllciBpcyBjaGFuZ2VkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlhZGQ6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIHNlbGVjdGVkIHRocm91Z2ggdGhlIFtsYXllciBjb250cm9sXSgjY29udHJvbC1sYXllcnMpLlxyXG5cdFx0Ly8gQGV2ZW50IG92ZXJsYXlyZW1vdmU6IExheWVyc0NvbnRyb2xFdmVudFxyXG5cdFx0Ly8gRmlyZWQgd2hlbiBhbiBvdmVybGF5IGlzIGRlc2VsZWN0ZWQgdGhyb3VnaCB0aGUgW2xheWVyIGNvbnRyb2xdKCNjb250cm9sLWxheWVycykuXHJcblx0XHQvLyBAbmFtZXNwYWNlIENvbnRyb2wuTGF5ZXJzXHJcblx0XHR2YXIgdHlwZSA9IG9iai5vdmVybGF5ID9cclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnb3ZlcmxheWFkZCcgOiAnb3ZlcmxheXJlbW92ZScpIDpcclxuXHRcdFx0KGUudHlwZSA9PT0gJ2FkZCcgPyAnYmFzZWxheWVyY2hhbmdlJyA6IG51bGwpO1xyXG5cclxuXHRcdGlmICh0eXBlKSB7XHJcblx0XHRcdHRoaXMuX21hcC5maXJlKHR5cGUsIG9iaik7XHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0Ly8gSUU3IGJ1Z3Mgb3V0IGlmIHlvdSBjcmVhdGUgYSByYWRpbyBkeW5hbWljYWxseSwgc28geW91IGhhdmUgdG8gZG8gaXQgdGhpcyBoYWNreSB3YXkgKHNlZSBodHRwOi8vYml0Lmx5L1BxWUxCZSlcclxuXHRfY3JlYXRlUmFkaW9FbGVtZW50OiBmdW5jdGlvbiAobmFtZSwgY2hlY2tlZCkge1xyXG5cclxuXHRcdHZhciByYWRpb0h0bWwgPSAnPGlucHV0IHR5cGU9XCJyYWRpb1wiIGNsYXNzPVwibGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3RvclwiIG5hbWU9XCInICtcclxuXHRcdFx0XHRuYW1lICsgJ1wiJyArIChjaGVja2VkID8gJyBjaGVja2VkPVwiY2hlY2tlZFwiJyA6ICcnKSArICcvPic7XHJcblxyXG5cdFx0dmFyIHJhZGlvRnJhZ21lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHRcdHJhZGlvRnJhZ21lbnQuaW5uZXJIVE1MID0gcmFkaW9IdG1sO1xyXG5cclxuXHRcdHJldHVybiByYWRpb0ZyYWdtZW50LmZpcnN0Q2hpbGQ7XHJcblx0fSxcclxuXHJcblx0X2FkZEl0ZW06IGZ1bmN0aW9uIChvYmopIHtcclxuXHRcdHZhciBsYWJlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xhYmVsJyksXHJcblx0XHQgICAgY2hlY2tlZCA9IHRoaXMuX21hcC5oYXNMYXllcihvYmoubGF5ZXIpLFxyXG5cdFx0ICAgIGlucHV0O1xyXG5cclxuXHRcdGlmIChvYmoub3ZlcmxheSkge1xyXG5cdFx0XHRpbnB1dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2lucHV0Jyk7XHJcblx0XHRcdGlucHV0LnR5cGUgPSAnY2hlY2tib3gnO1xyXG5cdFx0XHRpbnB1dC5jbGFzc05hbWUgPSAnbGVhZmxldC1jb250cm9sLWxheWVycy1zZWxlY3Rvcic7XHJcblx0XHRcdGlucHV0LmRlZmF1bHRDaGVja2VkID0gY2hlY2tlZDtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdGlucHV0ID0gdGhpcy5fY3JlYXRlUmFkaW9FbGVtZW50KCdsZWFmbGV0LWJhc2UtbGF5ZXJzJywgY2hlY2tlZCk7XHJcblx0XHR9XHJcblxyXG5cdFx0aW5wdXQubGF5ZXJJZCA9IEwuc3RhbXAob2JqLmxheWVyKTtcclxuXHJcblx0XHRMLkRvbUV2ZW50Lm9uKGlucHV0LCAnY2xpY2snLCB0aGlzLl9vbklucHV0Q2xpY2ssIHRoaXMpO1xyXG5cclxuXHRcdHZhciBuYW1lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpO1xyXG5cdFx0bmFtZS5pbm5lckhUTUwgPSAnICcgKyBvYmoubmFtZTtcclxuXHJcblx0XHQvLyBIZWxwcyBmcm9tIHByZXZlbnRpbmcgbGF5ZXIgY29udHJvbCBmbGlja2VyIHdoZW4gY2hlY2tib3hlcyBhcmUgZGlzYWJsZWRcclxuXHRcdC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9MZWFmbGV0L0xlYWZsZXQvaXNzdWVzLzI3NzFcclxuXHRcdHZhciBob2xkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHJcblx0XHRsYWJlbC5hcHBlbmRDaGlsZChob2xkZXIpO1xyXG5cdFx0aG9sZGVyLmFwcGVuZENoaWxkKGlucHV0KTtcclxuXHRcdGhvbGRlci5hcHBlbmRDaGlsZChuYW1lKTtcclxuXHJcblx0XHR2YXIgY29udGFpbmVyID0gb2JqLm92ZXJsYXkgPyB0aGlzLl9vdmVybGF5c0xpc3QgOiB0aGlzLl9iYXNlTGF5ZXJzTGlzdDtcclxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZChsYWJlbCk7XHJcblxyXG5cdFx0dGhpcy5fY2hlY2tEaXNhYmxlZExheWVycygpO1xyXG5cdFx0cmV0dXJuIGxhYmVsO1xyXG5cdH0sXHJcblxyXG5cdF9vbklucHV0Q2xpY2s6IGZ1bmN0aW9uICgpIHtcclxuXHRcdHZhciBpbnB1dHMgPSB0aGlzLl9mb3JtLmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbnB1dCcpLFxyXG5cdFx0ICAgIGlucHV0LCBsYXllciwgaGFzTGF5ZXI7XHJcblx0XHR2YXIgYWRkZWRMYXllcnMgPSBbXSxcclxuXHRcdCAgICByZW1vdmVkTGF5ZXJzID0gW107XHJcblxyXG5cdFx0dGhpcy5faGFuZGxpbmdDbGljayA9IHRydWU7XHJcblxyXG5cdFx0Zm9yICh2YXIgaSA9IGlucHV0cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xyXG5cdFx0XHRpbnB1dCA9IGlucHV0c1tpXTtcclxuXHRcdFx0bGF5ZXIgPSB0aGlzLl9nZXRMYXllcihpbnB1dC5sYXllcklkKS5sYXllcjtcclxuXHRcdFx0aGFzTGF5ZXIgPSB0aGlzLl9tYXAuaGFzTGF5ZXIobGF5ZXIpO1xyXG5cclxuXHRcdFx0aWYgKGlucHV0LmNoZWNrZWQgJiYgIWhhc0xheWVyKSB7XHJcblx0XHRcdFx0YWRkZWRMYXllcnMucHVzaChsYXllcik7XHJcblxyXG5cdFx0XHR9IGVsc2UgaWYgKCFpbnB1dC5jaGVja2VkICYmIGhhc0xheWVyKSB7XHJcblx0XHRcdFx0cmVtb3ZlZExheWVycy5wdXNoKGxheWVyKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdC8vIEJ1Z2ZpeCBpc3N1ZSAyMzE4OiBTaG91bGQgcmVtb3ZlIGFsbCBvbGQgbGF5ZXJzIGJlZm9yZSByZWFkZGluZyBuZXcgb25lc1xyXG5cdFx0Zm9yIChpID0gMDsgaSA8IHJlbW92ZWRMYXllcnMubGVuZ3RoOyBpKyspIHtcclxuXHRcdFx0dGhpcy5fbWFwLnJlbW92ZUxheWVyKHJlbW92ZWRMYXllcnNbaV0pO1xyXG5cdFx0fVxyXG5cdFx0Zm9yIChpID0gMDsgaSA8IGFkZGVkTGF5ZXJzLmxlbmd0aDsgaSsrKSB7XHJcblx0XHRcdHRoaXMuX21hcC5hZGRMYXllcihhZGRlZExheWVyc1tpXSk7XHJcblx0XHR9XHJcblxyXG5cdFx0dGhpcy5faGFuZGxpbmdDbGljayA9IGZhbHNlO1xyXG5cclxuXHRcdHRoaXMuX3JlZm9jdXNPbk1hcCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jaGVja0Rpc2FibGVkTGF5ZXJzOiBmdW5jdGlvbiAoKSB7XHJcblx0XHR2YXIgaW5wdXRzID0gdGhpcy5fZm9ybS5nZXRFbGVtZW50c0J5VGFnTmFtZSgnaW5wdXQnKSxcclxuXHRcdCAgICBpbnB1dCxcclxuXHRcdCAgICBsYXllcixcclxuXHRcdCAgICB6b29tID0gdGhpcy5fbWFwLmdldFpvb20oKTtcclxuXHJcblx0XHRmb3IgKHZhciBpID0gaW5wdXRzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XHJcblx0XHRcdGlucHV0ID0gaW5wdXRzW2ldO1xyXG5cdFx0XHRsYXllciA9IHRoaXMuX2dldExheWVyKGlucHV0LmxheWVySWQpLmxheWVyO1xyXG5cdFx0XHRpbnB1dC5kaXNhYmxlZCA9IChsYXllci5vcHRpb25zLm1pblpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tIDwgbGF5ZXIub3B0aW9ucy5taW5ab29tKSB8fFxyXG5cdFx0XHQgICAgICAgICAgICAgICAgIChsYXllci5vcHRpb25zLm1heFpvb20gIT09IHVuZGVmaW5lZCAmJiB6b29tID4gbGF5ZXIub3B0aW9ucy5tYXhab29tKTtcclxuXHJcblx0XHR9XHJcblx0fSxcclxuXHJcblx0X2V4cGFuZDogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gQmFja3dhcmQgY29tcGF0aWJpbGl0eSwgcmVtb3ZlIG1lIGluIDEuMS5cclxuXHRcdHJldHVybiB0aGlzLmV4cGFuZCgpO1xyXG5cdH0sXHJcblxyXG5cdF9jb2xsYXBzZTogZnVuY3Rpb24gKCkge1xyXG5cdFx0Ly8gQmFja3dhcmQgY29tcGF0aWJpbGl0eSwgcmVtb3ZlIG1lIGluIDEuMS5cclxuXHRcdHJldHVybiB0aGlzLmNvbGxhcHNlKCk7XHJcblx0fVxyXG5cclxufSk7XHJcblxyXG5cclxuLy8gQGZhY3RvcnkgTC5jb250cm9sLmxheWVycyhiYXNlbGF5ZXJzPzogT2JqZWN0LCBvdmVybGF5cz86IE9iamVjdCwgb3B0aW9ucz86IENvbnRyb2wuTGF5ZXJzIG9wdGlvbnMpXHJcbi8vIENyZWF0ZXMgYW4gYXR0cmlidXRpb24gY29udHJvbCB3aXRoIHRoZSBnaXZlbiBsYXllcnMuIEJhc2UgbGF5ZXJzIHdpbGwgYmUgc3dpdGNoZWQgd2l0aCByYWRpbyBidXR0b25zLCB3aGlsZSBvdmVybGF5cyB3aWxsIGJlIHN3aXRjaGVkIHdpdGggY2hlY2tib3hlcy4gTm90ZSB0aGF0IGFsbCBiYXNlIGxheWVycyBzaG91bGQgYmUgcGFzc2VkIGluIHRoZSBiYXNlIGxheWVycyBvYmplY3QsIGJ1dCBvbmx5IG9uZSBzaG91bGQgYmUgYWRkZWQgdG8gdGhlIG1hcCBkdXJpbmcgbWFwIGluc3RhbnRpYXRpb24uXHJcbkwuY29udHJvbC5sYXllcnMgPSBmdW5jdGlvbiAoYmFzZUxheWVycywgb3ZlcmxheXMsIG9wdGlvbnMpIHtcclxuXHRyZXR1cm4gbmV3IEwuQ29udHJvbC5MYXllcnMoYmFzZUxheWVycywgb3ZlcmxheXMsIG9wdGlvbnMpO1xyXG59O1xyXG5cblxuXG59KHdpbmRvdywgZG9jdW1lbnQpKTtcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPWxlYWZsZXQtc3JjLm1hcCIsIi8qKlxyXG4gKiBAY2xhc3MgIEwuTWF0cml4XHJcbiAqXHJcbiAqIEBwYXJhbSB7TnVtYmVyfSBhXHJcbiAqIEBwYXJhbSB7TnVtYmVyfSBiXHJcbiAqIEBwYXJhbSB7TnVtYmVyfSBjXHJcbiAqIEBwYXJhbSB7TnVtYmVyfSBkXHJcbiAqIEBwYXJhbSB7TnVtYmVyfSBlXHJcbiAqIEBwYXJhbSB7TnVtYmVyfSBmXHJcbiAqL1xyXG5MLk1hdHJpeCA9IGZ1bmN0aW9uKGEsIGIsIGMsIGQsIGUsIGYpIHtcclxuXHJcbiAgLyoqXHJcbiAgICogQHR5cGUge0FycmF5LjxOdW1iZXI+fVxyXG4gICAqL1xyXG4gIHRoaXMuX21hdHJpeCA9IFthLCBiLCBjLCBkLCBlLCBmXTtcclxufTtcclxuXHJcblxyXG5MLk1hdHJpeC5wcm90b3R5cGUgPSB7XHJcblxyXG5cclxuICAvKipcclxuICAgKiBAcGFyYW0gIHtMLlBvaW50fSBwb2ludFxyXG4gICAqIEByZXR1cm4ge0wuUG9pbnR9XHJcbiAgICovXHJcbiAgdHJhbnNmb3JtOiBmdW5jdGlvbihwb2ludCkge1xyXG4gICAgcmV0dXJuIHRoaXMuX3RyYW5zZm9ybShwb2ludC5jbG9uZSgpKTtcclxuICB9LFxyXG5cclxuXHJcbiAgLyoqXHJcbiAgICogRGVzdHJ1Y3RpdmVcclxuICAgKlxyXG4gICAqIFsgeCBdID0gWyBhICBiICB0eCBdIFsgeCBdID0gWyBhICogeCArIGIgKiB5ICsgdHggXVxyXG4gICAqIFsgeSBdID0gWyBjICBkICB0eSBdIFsgeSBdID0gWyBjICogeCArIGQgKiB5ICsgdHkgXVxyXG4gICAqXHJcbiAgICogQHBhcmFtICB7TC5Qb2ludH0gcG9pbnRcclxuICAgKiBAcmV0dXJuIHtMLlBvaW50fVxyXG4gICAqL1xyXG4gIF90cmFuc2Zvcm06IGZ1bmN0aW9uKHBvaW50KSB7XHJcbiAgICB2YXIgbWF0cml4ID0gdGhpcy5fbWF0cml4O1xyXG4gICAgdmFyIHggPSBwb2ludC54LCB5ID0gcG9pbnQueTtcclxuICAgIHBvaW50LnggPSBtYXRyaXhbMF0gKiB4ICsgbWF0cml4WzFdICogeSArIG1hdHJpeFs0XTtcclxuICAgIHBvaW50LnkgPSBtYXRyaXhbMl0gKiB4ICsgbWF0cml4WzNdICogeSArIG1hdHJpeFs1XTtcclxuICAgIHJldHVybiBwb2ludDtcclxuICB9LFxyXG5cclxuXHJcbiAgLyoqXHJcbiAgICogQHBhcmFtICB7TC5Qb2ludH0gcG9pbnRcclxuICAgKiBAcmV0dXJuIHtMLlBvaW50fVxyXG4gICAqL1xyXG4gIHVudHJhbnNmb3JtOiBmdW5jdGlvbiAocG9pbnQpIHtcclxuICAgIHZhciBtYXRyaXggPSB0aGlzLl9tYXRyaXg7XHJcbiAgICByZXR1cm4gbmV3IEwuUG9pbnQoXHJcbiAgICAgIChwb2ludC54IC8gbWF0cml4WzBdIC0gbWF0cml4WzRdKSAvIG1hdHJpeFswXSxcclxuICAgICAgKHBvaW50LnkgLyBtYXRyaXhbMl0gLSBtYXRyaXhbNV0pIC8gbWF0cml4WzJdXHJcbiAgICApO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBAcmV0dXJuIHtMLk1hdHJpeH1cclxuICAgKi9cclxuICBjbG9uZTogZnVuY3Rpb24oKSB7XHJcbiAgICB2YXIgbWF0cml4ID0gdGhpcy5fbWF0cml4O1xyXG4gICAgcmV0dXJuIG5ldyBMLk1hdHJpeChcclxuICAgICAgbWF0cml4WzBdLCBtYXRyaXhbMV0sIG1hdHJpeFsyXSxcclxuICAgICAgbWF0cml4WzNdLCBtYXRyaXhbNF0sIG1hdHJpeFs1XVxyXG4gICAgKTtcclxuICB9LFxyXG5cclxuXHJcbiAgLyoqXHJcbiAgICogQHBhcmFtIHtMLlBvaW50PXxOdW1iZXI9fSB0cmFuc2xhdGVcclxuICAgKiBAcmV0dXJuIHtMLk1hdHJpeHxMLlBvaW50fVxyXG4gICAqL1xyXG4gIHRyYW5zbGF0ZTogZnVuY3Rpb24odHJhbnNsYXRlKSB7XHJcbiAgICBpZiAodHJhbnNsYXRlID09PSB1bmRlZmluZWQpIHtcclxuICAgICAgcmV0dXJuIG5ldyBMLlBvaW50KHRoaXMuX21hdHJpeFs0XSwgdGhpcy5fbWF0cml4WzVdKTtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgdHJhbnNsYXRlWCwgdHJhbnNsYXRlWTtcclxuICAgIGlmICh0eXBlb2YgdHJhbnNsYXRlID09PSAnbnVtYmVyJykge1xyXG4gICAgICB0cmFuc2xhdGVYID0gdHJhbnNsYXRlWSA9IHRyYW5zbGF0ZTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHRyYW5zbGF0ZVggPSB0cmFuc2xhdGUueDtcclxuICAgICAgdHJhbnNsYXRlWSA9IHRyYW5zbGF0ZS55O1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0aGlzLl9hZGQoMSwgMCwgMCwgMSwgdHJhbnNsYXRlWCwgdHJhbnNsYXRlWSk7XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIEBwYXJhbSB7TC5Qb2ludD18TnVtYmVyPX0gc2NhbGVcclxuICAgKiBAcmV0dXJuIHtMLk1hdHJpeHxMLlBvaW50fVxyXG4gICAqL1xyXG4gIHNjYWxlOiBmdW5jdGlvbihzY2FsZSwgb3JpZ2luKSB7XHJcbiAgICBpZiAoc2NhbGUgPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICByZXR1cm4gbmV3IEwuUG9pbnQodGhpcy5fbWF0cml4WzBdLCB0aGlzLl9tYXRyaXhbM10pO1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBzY2FsZVgsIHNjYWxlWTtcclxuICAgIG9yaWdpbiA9IG9yaWdpbiB8fCBMLnBvaW50KDAsIDApO1xyXG4gICAgaWYgKHR5cGVvZiBzY2FsZSA9PT0gJ251bWJlcicpIHtcclxuICAgICAgc2NhbGVYID0gc2NhbGVZID0gc2NhbGU7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBzY2FsZVggPSBzY2FsZS54O1xyXG4gICAgICBzY2FsZVkgPSBzY2FsZS55O1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0aGlzXHJcbiAgICAgIC5fYWRkKHNjYWxlWCwgMCwgMCwgc2NhbGVZLCBvcmlnaW4ueCwgb3JpZ2luLnkpXHJcbiAgICAgIC5fYWRkKDEsIDAsIDAsIDEsIC1vcmlnaW4ueCwgLW9yaWdpbi55KTtcclxuICB9LFxyXG5cclxuXHJcbiAgLyoqXHJcbiAgICogbTAwICBtMDEgIHggLSBtMDAgKiB4IC0gbTAxICogeVxyXG4gICAqIG0xMCAgbTExICB5IC0gbTEwICogeCAtIG0xMSAqIHlcclxuICAgKiBAcGFyYW0ge051bWJlcn0gICBhbmdsZVxyXG4gICAqIEBwYXJhbSB7TC5Qb2ludD19IG9yaWdpblxyXG4gICAqIEByZXR1cm4ge0wuTWF0cml4fVxyXG4gICAqL1xyXG4gIHJvdGF0ZTogZnVuY3Rpb24oYW5nbGUsIG9yaWdpbikge1xyXG4gICAgdmFyIGNvcyA9IE1hdGguY29zKGFuZ2xlKTtcclxuICAgIHZhciBzaW4gPSBNYXRoLnNpbihhbmdsZSk7XHJcblxyXG4gICAgb3JpZ2luID0gb3JpZ2luIHx8IG5ldyBMLlBvaW50KDAsIDApO1xyXG5cclxuICAgIHJldHVybiB0aGlzXHJcbiAgICAgIC5fYWRkKGNvcywgc2luLCAtc2luLCBjb3MsIG9yaWdpbi54LCBvcmlnaW4ueSlcclxuICAgICAgLl9hZGQoMSwgMCwgMCwgMSwgLW9yaWdpbi54LCAtb3JpZ2luLnkpO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBJbnZlcnQgcm90YXRpb25cclxuICAgKiBAcmV0dXJuIHtMLk1hdHJpeH1cclxuICAgKi9cclxuICBmbGlwOiBmdW5jdGlvbigpIHtcclxuICAgIHRoaXMuX21hdHJpeFsxXSAqPSAtMTtcclxuICAgIHRoaXMuX21hdHJpeFsyXSAqPSAtMTtcclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBAcGFyYW0ge051bWJlcnxMLk1hdHJpeH0gYVxyXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBiXHJcbiAgICogQHBhcmFtIHtOdW1iZXJ9IGNcclxuICAgKiBAcGFyYW0ge051bWJlcn0gZFxyXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBlXHJcbiAgICogQHBhcmFtIHtOdW1iZXJ9IGZcclxuICAgKi9cclxuICBfYWRkOiBmdW5jdGlvbihhLCBiLCBjLCBkLCBlLCBmKSB7XHJcbiAgICB2YXIgcmVzdWx0ID0gW1tdLCBbXSwgW11dO1xyXG4gICAgdmFyIHNyYyA9IHRoaXMuX21hdHJpeDtcclxuICAgIHZhciBtID0gW1xyXG4gICAgICBbc3JjWzBdLCBzcmNbMl0sIHNyY1s0XV0sXHJcbiAgICAgIFtzcmNbMV0sIHNyY1szXSwgc3JjWzVdXSxcclxuICAgICAgWyAgICAgMCwgICAgICAwLCAgICAgMV1cclxuICAgIF07XHJcbiAgICB2YXIgb3RoZXIgPSBbXHJcbiAgICAgIFthLCBjLCBlXSxcclxuICAgICAgW2IsIGQsIGZdLFxyXG4gICAgICBbMCwgMCwgMV1cclxuICAgIF0sIHZhbDtcclxuXHJcblxyXG4gICAgaWYgKGEgJiYgYSBpbnN0YW5jZW9mIEwuTWF0cml4KSB7XHJcbiAgICAgIHNyYyA9IGEuX21hdHJpeDtcclxuICAgICAgb3RoZXIgPSBbXHJcbiAgICAgICAgW3NyY1swXSwgc3JjWzJdLCBzcmNbNF1dLFxyXG4gICAgICAgIFtzcmNbMV0sIHNyY1szXSwgc3JjWzVdXSxcclxuICAgICAgICBbICAgICAwLCAgICAgIDAsICAgICAxXV07XHJcbiAgICB9XHJcblxyXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCAzOyBpKyspIHtcclxuICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCAzOyBqKyspIHtcclxuICAgICAgICB2YWwgPSAwO1xyXG4gICAgICAgIGZvciAodmFyIGsgPSAwOyBrIDwgMzsgaysrKSB7XHJcbiAgICAgICAgICB2YWwgKz0gbVtpXVtrXSAqIG90aGVyW2tdW2pdO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXN1bHRbaV1bal0gPSB2YWw7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB0aGlzLl9tYXRyaXggPSBbXHJcbiAgICAgIHJlc3VsdFswXVswXSwgcmVzdWx0WzFdWzBdLCByZXN1bHRbMF1bMV0sXHJcbiAgICAgIHJlc3VsdFsxXVsxXSwgcmVzdWx0WzBdWzJdLCByZXN1bHRbMV1bMl1cclxuICAgIF07XHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG5cclxufTtcclxuXHJcblxyXG5MLm1hdHJpeCA9IGZ1bmN0aW9uKGEsIGIsIGMsIGQsIGUsIGYpIHtcclxuICByZXR1cm4gbmV3IEwuTWF0cml4KGEsIGIsIGMsIGQsIGUsIGYpO1xyXG59O1xyXG4iLCIvKipcclxuICogQG5hbWVzcGFjZVxyXG4gKiBAdHlwZSB7T2JqZWN0fVxyXG4gKi9cclxuTC5QYXRoVHJhbnNmb3JtID0ge307XHJcblxyXG5cclxuLyoqXHJcbiAqIE1hcmtlciBoYW5kbGVyXHJcbiAqIEBleHRlbmRzIHtMLkNpcmNsZU1hcmtlcn1cclxuICovXHJcbkwuUGF0aFRyYW5zZm9ybS5IYW5kbGUgPSBMLkNpcmNsZU1hcmtlci5leHRlbmQoe1xyXG4gIG9wdGlvbnM6IHtcclxuICAgIGNsYXNzTmFtZTogJ2xlYWZsZXQtcGF0aC10cmFuc2Zvcm0taGFuZGxlcidcclxuICB9LFxyXG5cclxuICBvbkFkZDogZnVuY3Rpb24gKG1hcCkge1xyXG4gICAgTC5DaXJjbGVNYXJrZXIucHJvdG90eXBlLm9uQWRkLmNhbGwodGhpcywgbWFwKTtcclxuICAgIGlmICh0aGlzLl9wYXRoICYmIHRoaXMub3B0aW9ucy5zZXRDdXJzb3IpIHsgLy8gU1ZHL1ZNTFxyXG4gICAgICB0aGlzLl9wYXRoLnN0eWxlLmN1cnNvciA9IEwuUGF0aFRyYW5zZm9ybS5IYW5kbGUuQ3Vyc29yc0J5VHlwZVtcclxuICAgICAgICB0aGlzLm9wdGlvbnMuaW5kZXhcclxuICAgICAgXTtcclxuICAgIH1cclxuICB9XHJcbn0pO1xyXG5cclxuXHJcbi8qKlxyXG4gKiBAY29uc3RcclxuICogQHR5cGUge0FycmF5fVxyXG4gKi9cclxuTC5QYXRoVHJhbnNmb3JtLkhhbmRsZS5DdXJzb3JzQnlUeXBlID0gW1xyXG4gICduZXN3LXJlc2l6ZScsICdud3NlLXJlc2l6ZScsICduZXN3LXJlc2l6ZScsICdud3NlLXJlc2l6ZSdcclxuXTtcclxuXHJcblxyXG4vKipcclxuICogQGV4dGVuZHMge0wuSGFuZGxlci5QYXRoVHJhbnNmb3JtLkhhbmRsZX1cclxuICovXHJcbkwuUGF0aFRyYW5zZm9ybS5Sb3RhdGVIYW5kbGUgPSBMLlBhdGhUcmFuc2Zvcm0uSGFuZGxlLmV4dGVuZCh7XHJcbiAgb3B0aW9uczoge1xyXG4gICAgY2xhc3NOYW1lOiAnbGVhZmxldC1wYXRoLXRyYW5zZm9ybS1oYW5kbGVyIHRyYW5zZm9ybS1oYW5kbGVyLS1yb3RhdGUnXHJcbiAgfSxcclxuXHJcbiAgb25BZGQ6IGZ1bmN0aW9uIChtYXApIHtcclxuICAgIEwuQ2lyY2xlTWFya2VyLnByb3RvdHlwZS5vbkFkZC5jYWxsKHRoaXMsIG1hcCk7XHJcbiAgICBpZiAodGhpcy5fcGF0aCAmJiB0aGlzLm9wdGlvbnMuc2V0Q3Vyc29yKSB7IC8vIFNWRy9WTUxcclxuICAgICAgdGhpcy5fcGF0aC5zdHlsZS5jdXJzb3IgPSAnYWxsLXNjcm9sbCc7XHJcbiAgICB9XHJcbiAgfVxyXG59KTtcclxuXHJcbkwuSGFuZGxlci5QYXRoVHJhbnNmb3JtID0gTC5IYW5kbGVyLmV4dGVuZCh7XHJcblxyXG4gIG9wdGlvbnM6IHtcclxuICAgIHJvdGF0aW9uOiB0cnVlLFxyXG4gICAgc2NhbGluZzogIHRydWUsXHJcbiAgICB1bmlmb3JtU2NhbGluZzogdHJ1ZSxcclxuICAgIG1heFpvb206ICAyMixcclxuXHJcbiAgICAvLyBlZGdlIGhhbmRsZXJzXHJcbiAgICBoYW5kbGVyT3B0aW9uczoge1xyXG4gICAgICByYWRpdXM6ICAgICAgNSxcclxuICAgICAgZmlsbENvbG9yOiAgICcjZmZmZmZmJyxcclxuICAgICAgY29sb3I6ICAgICAgICcjMjAyMDIwJyxcclxuICAgICAgZmlsbE9wYWNpdHk6IDEsXHJcbiAgICAgIHdlaWdodDogICAgICAyLFxyXG4gICAgICBvcGFjaXR5OiAgICAgMC43LFxyXG4gICAgICBzZXRDdXJzb3I6ICAgdHJ1ZVxyXG4gICAgfSxcclxuXHJcbiAgICAvLyByZWN0YW5nbGVcclxuICAgIGJvdW5kc09wdGlvbnM6IHtcclxuICAgICAgd2VpZ2h0OiAgICAxLFxyXG4gICAgICBvcGFjaXR5OiAgIDEsXHJcbiAgICAgIGRhc2hBcnJheTogWzMsIDNdLFxyXG4gICAgICBmaWxsOiAgICAgIGZhbHNlXHJcbiAgICB9LFxyXG5cclxuICAgIC8vIHJvdGF0aW9uIGhhbmRsZXJcclxuICAgIHJvdGF0ZUhhbmRsZU9wdGlvbnM6IHtcclxuICAgICAgd2VpZ2h0OiAgICAxLFxyXG4gICAgICBvcGFjaXR5OiAgIDEsXHJcbiAgICAgIHNldEN1cnNvcjogdHJ1ZVxyXG4gICAgfSxcclxuICAgIC8vIHJvdGF0aW9uIGhhbmRsZSBsZW5ndGhcclxuICAgIGhhbmRsZUxlbmd0aDogMjAsXHJcblxyXG4gICAgLy8gbWF5YmUgSSdsbCBhZGQgc2tld2luZyBpbiB0aGUgZnV0dXJlXHJcbiAgICBlZGdlc0NvdW50OiAgIDQsXHJcblxyXG4gICAgaGFuZGxlQ2xhc3M6ICAgICAgIEwuUGF0aFRyYW5zZm9ybS5IYW5kbGUsXHJcbiAgICByb3RhdGVIYW5kbGVDbGFzczogTC5QYXRoVHJhbnNmb3JtLlJvdGF0ZUhhbmRsZVxyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBAY2xhc3MgTC5IYW5kbGVyLlBhdGhUcmFuc2Zvcm1cclxuICAgKiBAY29uc3RydWN0b3JcclxuICAgKiBAcGFyYW0gIHtMLlBhdGh9IHBhdGhcclxuICAgKi9cclxuICBpbml0aWFsaXplOiBmdW5jdGlvbihwYXRoKSB7XHJcbiAgICAvLyByZWZlcmVuY2VzXHJcbiAgICB0aGlzLl9wYXRoID0gcGF0aDtcclxuICAgIHRoaXMuX21hcCAgPSBudWxsO1xyXG5cclxuICAgIC8vIGhhbmRsZXJzXHJcbiAgICB0aGlzLl9hY3RpdmVNYXJrZXIgICA9IG51bGw7XHJcbiAgICB0aGlzLl9vcmlnaW5NYXJrZXIgICA9IG51bGw7XHJcbiAgICB0aGlzLl9yb3RhdGlvbk1hcmtlciA9IG51bGw7XHJcblxyXG4gICAgLy8gb3JpZ2lucyAmIHRlbXBvcmFyeSBzdGF0ZVxyXG4gICAgdGhpcy5fcm90YXRpb25PcmlnaW4gICA9IG51bGw7XHJcbiAgICB0aGlzLl9zY2FsZU9yaWdpbiAgICAgID0gbnVsbDtcclxuICAgIHRoaXMuX2FuZ2xlICAgICAgICAgICAgPSAwO1xyXG4gICAgdGhpcy5fc2NhbGUgICAgICAgICAgICA9IEwucG9pbnQoMSwgMSk7XHJcbiAgICB0aGlzLl9pbml0aWFsRGlzdCAgICAgID0gMDtcclxuICAgIHRoaXMuX2luaXRpYWxEaXN0WCAgICAgPSAwO1xyXG4gICAgdGhpcy5faW5pdGlhbERpc3RZICAgICA9IDA7XHJcbiAgICB0aGlzLl9yb3RhdGlvblN0YXJ0ICAgID0gbnVsbDtcclxuICAgIHRoaXMuX3JvdGF0aW9uT3JpZ2luUHQgPSBudWxsO1xyXG5cclxuICAgIC8vIHByZXZpZXcgYW5kIHRyYW5zZm9ybSBtYXRyaXhcclxuICAgIHRoaXMuX21hdHJpeCAgICAgICAgICA9IG5ldyBMLk1hdHJpeCgxLCAwLCAwLCAxLCAwLCAwKTtcclxuICAgIHRoaXMuX3Byb2plY3RlZE1hdHJpeCA9IG5ldyBMLk1hdHJpeCgxLCAwLCAwLCAxLCAwLCAwKTtcclxuXHJcbiAgICAvLyB1aSBlbGVtZW50c1xyXG4gICAgdGhpcy5faGFuZGxlcnNHcm91cCAgPSBudWxsO1xyXG4gICAgdGhpcy5fcmVjdCAgICAgICAgICAgPSBudWxsO1xyXG4gICAgdGhpcy5faGFuZGxlcnMgICAgICAgPSBbXTtcclxuICAgIHRoaXMuX2hhbmRsZUxpbmUgICAgID0gbnVsbDtcclxuICB9LFxyXG5cclxuXHJcbiAgLyoqXHJcbiAgICogSWYgdGhlIHBvbHlnb24gaXMgbm90IHJlbmRlcmVkLCB5b3UgY2FuIHRyYW5zZm9ybSBpdCB5b3Vyc2VsZlxyXG4gICAqIGluIHRoZSBjb29yZGluYXRlcywgYW5kIGRvIGl0IHByb3Blcmx5LlxyXG4gICAqIEBwYXJhbSB7T2JqZWN0PX0gb3B0aW9uc1xyXG4gICAqL1xyXG4gIGVuYWJsZTogZnVuY3Rpb24ob3B0aW9ucykge1xyXG4gICAgaWYgKHRoaXMuX3BhdGguX21hcCkge1xyXG4gICAgICB0aGlzLl9tYXAgPSB0aGlzLl9wYXRoLl9tYXA7XHJcbiAgICAgIGlmIChvcHRpb25zKSB7XHJcbiAgICAgICAgdGhpcy5zZXRPcHRpb25zKG9wdGlvbnMpO1xyXG4gICAgICB9XHJcbiAgICAgIEwuSGFuZGxlci5wcm90b3R5cGUuZW5hYmxlLmNhbGwodGhpcyk7XHJcbiAgICB9XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIEluaXQgaW50ZXJhY3Rpb25zIGFuZCBoYW5kbGVyc1xyXG4gICAqL1xyXG4gIGFkZEhvb2tzOiBmdW5jdGlvbigpIHtcclxuICAgIHRoaXMuX2NyZWF0ZUhhbmRsZXJzKCk7XHJcbiAgICB0aGlzLl9wYXRoXHJcbiAgICAgIC5vbignZHJhZ3N0YXJ0JywgdGhpcy5fb25EcmFnU3RhcnQsIHRoaXMpXHJcbiAgICAgIC5vbignZHJhZ2VuZCcsICAgdGhpcy5fb25EcmFnRW5kLCAgIHRoaXMpO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBSZW1vdmUgaGFuZGxlcnNcclxuICAgKi9cclxuICByZW1vdmVIb29rczogZnVuY3Rpb24oKSB7XHJcbiAgICB0aGlzLl9oaWRlSGFuZGxlcnMoKTtcclxuICAgIHRoaXMuX3BhdGhcclxuICAgICAgLm9mZignZHJhZ3N0YXJ0JywgdGhpcy5fb25EcmFnU3RhcnQsIHRoaXMpXHJcbiAgICAgIC5vZmYoJ2RyYWdlbmQnLCAgIHRoaXMuX29uRHJhZ0VuZCwgICB0aGlzKTtcclxuICAgIHRoaXMuX2hhbmRsZXJzR3JvdXAgPSBudWxsO1xyXG4gICAgdGhpcy5fcmVjdCA9IG51bGw7XHJcbiAgICB0aGlzLl9oYW5kbGVycyA9IFtdO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBDaGFuZ2UgZWRpdGluZyBvcHRpb25zXHJcbiAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnNcclxuICAgKi9cclxuICBzZXRPcHRpb25zOiBmdW5jdGlvbihvcHRpb25zKSB7XHJcbiAgICB2YXIgZW5hYmxlZCA9IHRoaXMuX2VuYWJsZWQ7XHJcbiAgICBpZiAoZW5hYmxlZCkge1xyXG4gICAgICB0aGlzLmRpc2FibGUoKTtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLm9wdGlvbnMgPSBMLlV0aWwubWVyZ2Uoe30sXHJcbiAgICAgIEwuSGFuZGxlci5QYXRoVHJhbnNmb3JtLnByb3RvdHlwZS5vcHRpb25zLFxyXG4gICAgICBvcHRpb25zKTtcclxuXHJcbiAgICBpZiAoZW5hYmxlZCkge1xyXG4gICAgICB0aGlzLmVuYWJsZSgpO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBAcGFyYW0gIHtOdW1iZXJ9ICAgYW5nbGVcclxuICAgKiBAcGFyYW0gIHtMLkxhdExuZ30gb3JpZ2luXHJcbiAgICogQHJldHVybiB7TC5IYW5kbGVyLlBhdGhUcmFuc2Zvcm19XHJcbiAgICovXHJcbiAgcm90YXRlOiBmdW5jdGlvbihhbmdsZSwgb3JpZ2luKSB7XHJcbiAgICByZXR1cm4gdGhpcy50cmFuc2Zvcm0oYW5nbGUsIG51bGwsIG9yaWdpbik7XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIEBwYXJhbSAge0wuUG9pbnR8TnVtYmVyfSBzY2FsZVxyXG4gICAqIEBwYXJhbSAge0wuTGF0TG5nfSAgICAgICBvcmlnaW5cclxuICAgKiBAcmV0dXJuIHtMLkhhbmRsZXIuUGF0aFRyYW5zZm9ybX1cclxuICAgKi9cclxuICBzY2FsZTogZnVuY3Rpb24oc2NhbGUsIG9yaWdpbikge1xyXG4gICAgaWYgKHR5cGVvZiBzY2FsZSA9PT0gJ251bWJlcicpIHtcclxuICAgICAgc2NhbGUgPSBMLnBvaW50KHNjYWxlLCBzY2FsZSk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gdGhpcy50cmFuc2Zvcm0oMCwgc2NhbGUsIG51bGwsIG9yaWdpbik7XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIEBwYXJhbSAge051bWJlcn0gICAgYW5nbGVcclxuICAgKiBAcGFyYW0gIHtMLlBvaW50fSAgIHNjYWxlXHJcbiAgICogQHBhcmFtICB7TC5MYXRMbmc9fSByb3RhdGlvbk9yaWdpblxyXG4gICAqIEBwYXJhbSAge0wuTGF0TG5nPX0gc2NhbGVPcmlnaW5cclxuICAgKiBAcmV0dXJuIHtMLkhhbmRsZXIuUGF0aFRyYW5zZm9ybX1cclxuICAgKi9cclxuICB0cmFuc2Zvcm06IGZ1bmN0aW9uKGFuZ2xlLCBzY2FsZSwgcm90YXRpb25PcmlnaW4sIHNjYWxlT3JpZ2luKSB7XHJcbiAgICB2YXIgY2VudGVyICAgICA9IHRoaXMuX3BhdGguZ2V0Q2VudGVyKCk7XHJcbiAgICByb3RhdGlvbk9yaWdpbiA9IHJvdGF0aW9uT3JpZ2luIHx8IGNlbnRlcjtcclxuICAgIHNjYWxlT3JpZ2luICAgID0gc2NhbGVPcmlnaW4gICAgfHwgY2VudGVyO1xyXG4gICAgdGhpcy5fbWFwID0gdGhpcy5fcGF0aC5fbWFwO1xyXG4gICAgdGhpcy5fdHJhbnNmb3JtUG9pbnRzKHRoaXMuX3BhdGgsIGFuZ2xlLCBzY2FsZSwgcm90YXRpb25PcmlnaW4sIHNjYWxlT3JpZ2luKTtcclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBVcGRhdGUgdGhlIHBvbHlnb24gYW5kIGhhbmRsZXJzIHByZXZpZXcsIG5vIHJlcHJvamVjdGlvblxyXG4gICAqL1xyXG4gIF91cGRhdGU6IGZ1bmN0aW9uKCkge1xyXG4gICAgdmFyIG1hdHJpeCA9IHRoaXMuX21hdHJpeDtcclxuXHJcbiAgICAvLyB1cGRhdGUgaGFuZGxlcnNcclxuICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSB0aGlzLl9oYW5kbGVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG4gICAgICB2YXIgaGFuZGxlciA9IHRoaXMuX2hhbmRsZXJzW2ldO1xyXG4gICAgICBpZiAoaGFuZGxlciAhPT0gdGhpcy5fb3JpZ2luTWFya2VyKSB7XHJcbiAgICAgICAgaGFuZGxlci5fcG9pbnQgPSBtYXRyaXgudHJhbnNmb3JtKGhhbmRsZXIuX2luaXRpYWxQb2ludCk7XHJcbiAgICAgICAgaGFuZGxlci5fdXBkYXRlUGF0aCgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgbWF0cml4ID0gbWF0cml4LmNsb25lKCkuZmxpcCgpO1xyXG5cclxuICAgIHRoaXMuX2FwcGx5VHJhbnNmb3JtKG1hdHJpeCk7XHJcbiAgICB0aGlzLl9wYXRoLmZpcmUoJ3RyYW5zZm9ybScsIHsgbGF5ZXI6IHRoaXMuX3BhdGggfSk7XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIEBwYXJhbSAge0wuTWF0cml4fSBtYXRyaXhcclxuICAgKi9cclxuICBfYXBwbHlUcmFuc2Zvcm06IGZ1bmN0aW9uKG1hdHJpeCkge1xyXG4gICAgdGhpcy5fcGF0aC5fdHJhbnNmb3JtKG1hdHJpeC5fbWF0cml4KTtcclxuICAgIHRoaXMuX3JlY3QuX3RyYW5zZm9ybShtYXRyaXguX21hdHJpeCk7XHJcblxyXG4gICAgaWYgKHRoaXMub3B0aW9ucy5yb3RhdGlvbikge1xyXG4gICAgICB0aGlzLl9oYW5kbGVMaW5lLl90cmFuc2Zvcm0obWF0cml4Ll9tYXRyaXgpO1xyXG4gICAgfVxyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBBcHBseSBmaW5hbCB0cmFuc2Zvcm1hdGlvblxyXG4gICAqL1xyXG4gIF9hcHBseTogZnVuY3Rpb24oKSB7XHJcbiAgICAvL2NvbnNvbGUuZ3JvdXAoJ2FwcGx5IHRyYW5zZm9ybScpO1xyXG4gICAgdmFyIG1hcCA9IHRoaXMuX21hcDtcclxuICAgIHZhciBtYXRyaXggPSB0aGlzLl9tYXRyaXguY2xvbmUoKTtcclxuICAgIHZhciBhbmdsZSA9IHRoaXMuX2FuZ2xlO1xyXG4gICAgdmFyIHNjYWxlID0gdGhpcy5fc2NhbGUuY2xvbmUoKTtcclxuXHJcbiAgICB0aGlzLl90cmFuc2Zvcm1HZW9tZXRyaWVzKCk7XHJcblxyXG4gICAgLy8gdXBkYXRlIGhhbmRsZXJzXHJcbiAgICBmb3IgKHZhciBpID0gMCwgbGVuID0gdGhpcy5faGFuZGxlcnMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuICAgICAgdmFyIGhhbmRsZXIgPSB0aGlzLl9oYW5kbGVyc1tpXTtcclxuICAgICAgaGFuZGxlci5fbGF0bG5nID0gbWFwLmxheWVyUG9pbnRUb0xhdExuZyhoYW5kbGVyLl9wb2ludCk7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVyLl9pbml0aWFsUG9pbnQ7XHJcbiAgICAgIGhhbmRsZXIucmVkcmF3KCk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhpcy5fbWF0cml4ID0gTC5tYXRyaXgoMSwgMCwgMCwgMSwgMCwgMCk7XHJcbiAgICB0aGlzLl9zY2FsZSAgPSBMLnBvaW50KDEsIDEpO1xyXG4gICAgdGhpcy5fYW5nbGUgID0gMDtcclxuXHJcbiAgICB0aGlzLl91cGRhdGVIYW5kbGVycygpO1xyXG5cclxuICAgIG1hcC5kcmFnZ2luZy5lbmFibGUoKTtcclxuICAgIHRoaXMuX3BhdGguZmlyZSgndHJhbnNmb3JtZWQnLCB7XHJcbiAgICAgIG1hdHJpeDogbWF0cml4LFxyXG4gICAgICBzY2FsZTogc2NhbGUsXHJcbiAgICAgIHJvdGF0aW9uOiBhbmdsZSxcclxuICAgICAgLy8gYW5nbGU6IGFuZ2xlICogKDE4MCAvIE1hdGguUEkpLFxyXG4gICAgICBsYXllcjogdGhpcy5fcGF0aFxyXG4gICAgfSk7XHJcbiAgICAvLyBjb25zb2xlLmdyb3VwRW5kKCdhcHBseSB0cmFuc2Zvcm0nKTtcclxuICB9LFxyXG5cclxuXHJcbiAgLyoqXHJcbiAgICogUmVjYWxjdWxhdGUgcm90YXRpb24gaGFuZGxlcnMgcG9zaXRpb25cclxuICAgKi9cclxuICBfdXBkYXRlSGFuZGxlcnM6IGZ1bmN0aW9uKCkge1xyXG4gICAgdmFyIGhhbmRsZXJzR3JvdXAgPSB0aGlzLl9oYW5kbGVyc0dyb3VwO1xyXG5cclxuICAgIHRoaXMuX3JlY3RTaGFwZSA9IHRoaXMuX3JlY3QudG9HZW9KU09OKCk7XHJcblxyXG4gICAgaWYgKHRoaXMuX2hhbmRsZUxpbmUpIHtcclxuICAgICAgdGhpcy5faGFuZGxlcnNHcm91cC5yZW1vdmVMYXllcih0aGlzLl9oYW5kbGVMaW5lKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAodGhpcy5fcm90YXRpb25NYXJrZXIpIHtcclxuICAgICAgdGhpcy5faGFuZGxlcnNHcm91cC5yZW1vdmVMYXllcih0aGlzLl9yb3RhdGlvbk1hcmtlcik7ICAgICAgICBcclxuICAgIH0gICAgICBcclxuXHJcbiAgICB0aGlzLl9oYW5kbGVMaW5lID0gdGhpcy5fcm90YXRpb25NYXJrZXIgPSBudWxsO1xyXG5cclxuICAgIGZvciAodmFyIGkgPSB0aGlzLl9oYW5kbGVycy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xyXG4gICAgICBoYW5kbGVyc0dyb3VwLnJlbW92ZUxheWVyKHRoaXMuX2hhbmRsZXJzW2ldKTtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLl9jcmVhdGVIYW5kbGVycygpO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBUcmFuc2Zvcm0gZ2VvbWV0cmllcyBzZXBhcmF0ZWx5XHJcbiAgICovXHJcbiAgX3RyYW5zZm9ybUdlb21ldHJpZXM6IGZ1bmN0aW9uKCkge1xyXG4gICAgdGhpcy5fcGF0aC5fdHJhbnNmb3JtKG51bGwpO1xyXG4gICAgdGhpcy5fcmVjdC5fdHJhbnNmb3JtKG51bGwpO1xyXG5cclxuICAgIHRoaXMuX3RyYW5zZm9ybVBvaW50cyh0aGlzLl9wYXRoKTtcclxuICAgIHRoaXMuX3RyYW5zZm9ybVBvaW50cyh0aGlzLl9yZWN0KTtcclxuXHJcbiAgICBpZiAodGhpcy5vcHRpb25zLnJvdGF0aW9uKSB7XHJcbiAgICAgIHRoaXMuX2hhbmRsZUxpbmUuX3RyYW5zZm9ybShudWxsKTtcclxuICAgICAgdGhpcy5fdHJhbnNmb3JtUG9pbnRzKHRoaXMuX2hhbmRsZUxpbmUsIHRoaXMuX2FuZ2xlLCBudWxsLCB0aGlzLl9vcmlnaW4pO1xyXG4gICAgfVxyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBAcGFyYW0ge051bWJlcn0gYW5nbGVcclxuICAgKiBAcGFyYW0ge051bWJlcn0gc2NhbGVcclxuICAgKiBAcGFyYW0ge0wuTGF0TG5nPX0gcm90YXRpb25PcmlnaW5cclxuICAgKiBAcGFyYW0ge0wuTGF0TG5nPX0gc2NhbGVPcmlnaW5cclxuICAgKi9cclxuICBfZ2V0UHJvamVjdGVkTWF0cml4OiBmdW5jdGlvbihhbmdsZSwgc2NhbGUsIHJvdGF0aW9uT3JpZ2luLCBzY2FsZU9yaWdpbikge1xyXG4gICAgdmFyIG1hcCAgICA9IHRoaXMuX21hcDtcclxuICAgIHZhciB6b29tICAgPSBtYXAuZ2V0TWF4Wm9vbSgpIHx8IHRoaXMub3B0aW9ucy5tYXhab29tO1xyXG4gICAgdmFyIG1hdHJpeCA9IEwubWF0cml4KDEsIDAsIDAsIDEsIDAsIDApO1xyXG4gICAgdmFyIG9yaWdpbjtcclxuXHJcbiAgICBhbmdsZSA9IGFuZ2xlIHx8IHRoaXMuX2FuZ2xlIHx8IDA7XHJcbiAgICBzY2FsZSA9IHNjYWxlIHx8IHRoaXMuX3NjYWxlIHx8IEwucG9pbnQoMSwgMSk7XHJcblxyXG4gICAgaWYgKCEoc2NhbGUueCA9PT0gMSAmJiBzY2FsZS55ID09PSAxKSkge1xyXG4gICAgICBzY2FsZU9yaWdpbiA9IHNjYWxlT3JpZ2luIHx8IHRoaXMuX3NjYWxlT3JpZ2luO1xyXG4gICAgICBvcmlnaW4gPSBtYXAucHJvamVjdChzY2FsZU9yaWdpbiwgem9vbSk7XHJcbiAgICAgIG1hdHJpeCA9IG1hdHJpeFxyXG4gICAgICAgIC5fYWRkKEwubWF0cml4KDEsIDAsIDAsIDEsIG9yaWdpbi54LCBvcmlnaW4ueSkpXHJcbiAgICAgICAgLl9hZGQoTC5tYXRyaXgoc2NhbGUueCwgMCwgMCwgc2NhbGUueSwgMCwgMCkpXHJcbiAgICAgICAgLl9hZGQoTC5tYXRyaXgoMSwgMCwgMCwgMSwgLW9yaWdpbi54LCAtb3JpZ2luLnkpKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoYW5nbGUpIHtcclxuICAgICAgcm90YXRpb25PcmlnaW4gPSByb3RhdGlvbk9yaWdpbiB8fCB0aGlzLl9yb3RhdGlvbk9yaWdpbjtcclxuICAgICAgb3JpZ2luID0gbWFwLnByb2plY3Qocm90YXRpb25PcmlnaW4sIHpvb20pO1xyXG4gICAgICBtYXRyaXggPSBtYXRyaXgucm90YXRlKGFuZ2xlLCBvcmlnaW4pLmZsaXAoKTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gbWF0cml4O1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBAcGFyYW0gIHtMLkxhdExuZ30gbGF0bG5nXHJcbiAgICogQHBhcmFtICB7TC5NYXRyaXh9IG1hdHJpeFxyXG4gICAqIEBwYXJhbSAge0wuTWFwfSAgICBtYXBcclxuICAgKiBAcGFyYW0gIHtOdW1iZXJ9ICAgem9vbVxyXG4gICAqIEByZXR1cm4ge0wuTGF0TG5nfVxyXG4gICAqL1xyXG4gIF90cmFuc2Zvcm1Qb2ludDogZnVuY3Rpb24obGF0bG5nLCBtYXRyaXgsIG1hcCwgem9vbSkge1xyXG4gICAgcmV0dXJuIG1hcC51bnByb2plY3QobWF0cml4LnRyYW5zZm9ybShcclxuICAgICAgbWFwLnByb2plY3QobGF0bG5nLCB6b29tKSksIHpvb20pO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBBcHBsaWVzIHRyYW5zZm9ybWF0aW9uLCBkb2VzIGl0IGluIG9uZSBzd2VlcCBmb3IgcGVyZm9ybWFuY2UsXHJcbiAgICogc28gZG9uJ3QgYmUgc3VycHJpc2VkIGFib3V0IHRoZSBjb2RlIHJlcGV0aXRpb24uXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge0wuUGF0aH0gICAgcGF0aFxyXG4gICAqIEBwYXJhbSB7TnVtYmVyPX0gICBhbmdsZVxyXG4gICAqIEBwYXJhbSB7TC5Qb2ludD19ICBzY2FsZVxyXG4gICAqIEBwYXJhbSB7TC5MYXRMbmc9fSByb3RhdGlvbk9yaWdpblxyXG4gICAqIEBwYXJhbSB7TC5MYXRMbmc9fSBzY2FsZU9yaWdpblxyXG4gICAqL1xyXG4gIF90cmFuc2Zvcm1Qb2ludHM6IGZ1bmN0aW9uKHBhdGgsIGFuZ2xlLCBzY2FsZSwgcm90YXRpb25PcmlnaW4sIHNjYWxlT3JpZ2luKSB7XHJcbiAgICB2YXIgbWFwID0gcGF0aC5fbWFwO1xyXG4gICAgdmFyIHpvb20gPSBtYXAuZ2V0TWF4Wm9vbSgpIHx8IHRoaXMub3B0aW9ucy5tYXhab29tO1xyXG4gICAgdmFyIGksIGxlbjtcclxuXHJcbiAgICB2YXIgcHJvamVjdGVkTWF0cml4ID0gdGhpcy5fcHJvamVjdGVkTWF0cml4ID1cclxuICAgICAgdGhpcy5fZ2V0UHJvamVjdGVkTWF0cml4KGFuZ2xlLCBzY2FsZSwgcm90YXRpb25PcmlnaW4sIHNjYWxlT3JpZ2luKTtcclxuICAgIC8vIGNvbnNvbGUudGltZSgndHJhbnNmb3JtJyk7XHJcblxyXG4gICAgLy8gYWxsIHNoaWZ0cyBhcmUgaW4tcGxhY2VcclxuICAgIGlmIChwYXRoLl9wb2ludCkgeyAvLyBMLkNpcmNsZVxyXG4gICAgICBwYXRoLl9sYXRsbmcgPSB0aGlzLl90cmFuc2Zvcm1Qb2ludChcclxuICAgICAgICBwYXRoLl9sYXRsbmcsIHByb2plY3RlZE1hdHJpeCwgbWFwLCB6b29tKTtcclxuICAgIH0gZWxzZSBpZiAocGF0aC5fcmluZ3MgfHwgcGF0aC5fcGFydHMpIHsgLy8gZXZlcnl0aGluZyBlbHNlXHJcbiAgICAgIHZhciByaW5ncyA9IHBhdGguX3JpbmdzO1xyXG4gICAgICB2YXIgbGF0bG5ncyA9IHBhdGguX2xhdGxuZ3M7XHJcbiAgICAgIHBhdGguX2JvdW5kcyA9IG5ldyBMLkxhdExuZ0JvdW5kcygpO1xyXG5cclxuICAgICAgaWYgKCFMLlV0aWwuaXNBcnJheShsYXRsbmdzWzBdKSkgeyAvLyBwb2x5bGluZVxyXG4gICAgICAgIGxhdGxuZ3MgPSBbbGF0bG5nc107XHJcbiAgICAgIH1cclxuICAgICAgZm9yIChpID0gMCwgbGVuID0gcmluZ3MubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcclxuICAgICAgICBmb3IgKHZhciBqID0gMCwgamogPSByaW5nc1tpXS5sZW5ndGg7IGogPCBqajsgaisrKSB7XHJcbiAgICAgICAgICBsYXRsbmdzW2ldW2pdID0gdGhpcy5fdHJhbnNmb3JtUG9pbnQoXHJcbiAgICAgICAgICAgIGxhdGxuZ3NbaV1bal0sIHByb2plY3RlZE1hdHJpeCwgbWFwLCB6b29tKTtcclxuICAgICAgICAgIHBhdGguX2JvdW5kcy5leHRlbmQobGF0bG5nc1tpXVtqXSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgcGF0aC5fcmVzZXQoKTtcclxuICAgIC8vY29uc29sZS50aW1lRW5kKCd0cmFuc2Zvcm0nKTtcclxuICB9LFxyXG5cclxuXHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBtYXJrZXJzIGFuZCBoYW5kbGVzXHJcbiAgICovXHJcbiAgX2NyZWF0ZUhhbmRsZXJzOiBmdW5jdGlvbigpIHtcclxuICAgIHZhciBtYXAgPSB0aGlzLl9tYXA7XHJcbiAgICB0aGlzLl9oYW5kbGVyc0dyb3VwID0gdGhpcy5faGFuZGxlcnNHcm91cCB8fFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBMLkxheWVyR3JvdXAoKS5hZGRUbyhtYXApO1xyXG4gICAgdGhpcy5fcmVjdCA9IHRoaXMuX3JlY3QgfHxcclxuICAgICAgICAgICAgICAgICB0aGlzLl9nZXRCb3VuZGluZ1BvbHlnb24oKS5hZGRUbyh0aGlzLl9oYW5kbGVyc0dyb3VwKTtcclxuXHJcbiAgICBpZiAodGhpcy5vcHRpb25zLnNjYWxpbmcpIHtcclxuICAgICAgdGhpcy5faGFuZGxlcnMgPSBbXTtcclxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLm9wdGlvbnMuZWRnZXNDb3VudDsgaSsrKSB7XHJcbiAgICAgICAgLy8gVE9ETzogYWRkIHN0cmV0Y2hpbmdcclxuICAgICAgICB0aGlzLl9oYW5kbGVycy5wdXNoKFxyXG4gICAgICAgICAgdGhpcy5fY3JlYXRlSGFuZGxlcih0aGlzLl9yZWN0Ll9sYXRsbmdzWzBdW2ldLCBpICogMiwgaSlcclxuICAgICAgICAgIC5hZGRUbyh0aGlzLl9oYW5kbGVyc0dyb3VwKSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBhZGQgYm91bmRzXHJcbiAgICBpZiAodGhpcy5vcHRpb25zLnJvdGF0aW9uKSB7XHJcbiAgICAgIC8vYWRkIHJvdGF0aW9uIGhhbmRsZXJcclxuICAgICAgdGhpcy5fY3JlYXRlUm90YXRpb25IYW5kbGVycygpO1xyXG4gICAgfVxyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBSb3RhdGlvbiBtYXJrZXIgYW5kIHNtYWxsIGNvbm5lY3RpbiBoYW5kbGVcclxuICAgKi9cclxuICBfY3JlYXRlUm90YXRpb25IYW5kbGVyczogZnVuY3Rpb24oKSB7XHJcbiAgICB2YXIgbWFwICAgICA9IHRoaXMuX21hcDtcclxuICAgIHZhciBsYXRsbmdzID0gdGhpcy5fcmVjdC5fbGF0bG5nc1swXTtcclxuXHJcbiAgICB2YXIgYm90dG9tICAgPSBuZXcgTC5MYXRMbmcoXHJcbiAgICAgIChsYXRsbmdzWzBdLmxhdCArIGxhdGxuZ3NbM10ubGF0KSAvIDIsXHJcbiAgICAgIChsYXRsbmdzWzBdLmxuZyArIGxhdGxuZ3NbM10ubG5nKSAvIDIpO1xyXG4gICAgLy8gaGVoZSwgdG9wIGlzIGEgcmVzZXJ2ZWQgd29yZFxyXG4gICAgdmFyIHRvcFBvaW50ID0gbmV3IEwuTGF0TG5nKFxyXG4gICAgICAobGF0bG5nc1sxXS5sYXQgKyBsYXRsbmdzWzJdLmxhdCkgLyAyLFxyXG4gICAgICAobGF0bG5nc1sxXS5sbmcgKyBsYXRsbmdzWzJdLmxuZykgLyAyKTtcclxuXHJcbiAgICB2YXIgaGFuZGxlclBvc2l0aW9uID0gbWFwLmxheWVyUG9pbnRUb0xhdExuZyhcclxuICAgICAgTC5MaW5lVXRpbC5wb2ludE9uTGluZShcclxuICAgICAgICBtYXAubGF0TG5nVG9MYXllclBvaW50KGJvdHRvbSksXHJcbiAgICAgICAgbWFwLmxhdExuZ1RvTGF5ZXJQb2ludCh0b3BQb2ludCksXHJcbiAgICAgICAgdGhpcy5vcHRpb25zLmhhbmRsZUxlbmd0aClcclxuICAgICk7XHJcblxyXG4gICAgdGhpcy5faGFuZGxlTGluZSA9IG5ldyBMLlBvbHlsaW5lKFt0b3BQb2ludCwgaGFuZGxlclBvc2l0aW9uXSxcclxuICAgICAgdGhpcy5vcHRpb25zLnJvdGF0ZUhhbmRsZU9wdGlvbnMpLmFkZFRvKHRoaXMuX2hhbmRsZXJzR3JvdXApO1xyXG4gICAgdmFyIFJvdGF0ZUhhbmRsZUNsYXNzID0gdGhpcy5vcHRpb25zLnJvdGF0ZUhhbmRsZUNsYXNzO1xyXG4gICAgdGhpcy5fcm90YXRpb25NYXJrZXIgPSBuZXcgUm90YXRlSGFuZGxlQ2xhc3MoaGFuZGxlclBvc2l0aW9uLFxyXG4gICAgICB0aGlzLm9wdGlvbnMuaGFuZGxlck9wdGlvbnMpXHJcbiAgICAgIC5hZGRUbyh0aGlzLl9oYW5kbGVyc0dyb3VwKVxyXG4gICAgICAub24oJ21vdXNlZG93bicsIHRoaXMuX29uUm90YXRlU3RhcnQsIHRoaXMpO1xyXG5cclxuICAgIHRoaXMuX3JvdGF0aW9uT3JpZ2luID0gbmV3IEwuTGF0TG5nKFxyXG4gICAgICAodG9wUG9pbnQubGF0ICsgYm90dG9tLmxhdCkgLyAyLFxyXG4gICAgICAodG9wUG9pbnQubG5nICsgYm90dG9tLmxuZykgLyAyXHJcbiAgICApO1xyXG5cclxuICAgIHRoaXMuX2hhbmRsZXJzLnB1c2godGhpcy5fcm90YXRpb25NYXJrZXIpO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBAcmV0dXJuIHtMLkxhdExuZ31cclxuICAgKi9cclxuICBfZ2V0Um90YXRpb25PcmlnaW46IGZ1bmN0aW9uKCkge1xyXG4gICAgdmFyIGxhdGxuZ3MgPSB0aGlzLl9yZWN0Ll9sYXRsbmdzWzBdO1xyXG4gICAgdmFyIGxiID0gbGF0bG5nc1swXTtcclxuICAgIHZhciBydCA9IGxhdGxuZ3NbMl07XHJcblxyXG4gICAgcmV0dXJuIG5ldyBMLkxhdExuZyhcclxuICAgICAgKGxiLmxhdCArIHJ0LmxhdCkgLyAyLFxyXG4gICAgICAobGIubG5nICsgcnQubG5nKSAvIDJcclxuICAgICk7XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIFNlY3VyZSB0aGUgcm90YXRpb24gb3JpZ2luXHJcbiAgICogQHBhcmFtICB7RXZlbnR9IGV2dFxyXG4gICAqL1xyXG4gIF9vblJvdGF0ZVN0YXJ0OiBmdW5jdGlvbihldnQpIHtcclxuICAgIHZhciBtYXAgPSB0aGlzLl9tYXA7XHJcblxyXG4gICAgbWFwLmRyYWdnaW5nLmRpc2FibGUoKTtcclxuXHJcbiAgICB0aGlzLl9vcmlnaW5NYXJrZXIgICAgID0gbnVsbDtcclxuICAgIHRoaXMuX3JvdGF0aW9uT3JpZ2luUHQgPSBtYXAubGF0TG5nVG9MYXllclBvaW50KHRoaXMuX2dldFJvdGF0aW9uT3JpZ2luKCkpO1xyXG4gICAgdGhpcy5fcm90YXRpb25TdGFydCAgICA9IGV2dC5sYXllclBvaW50O1xyXG4gICAgdGhpcy5faW5pdGlhbE1hdHJpeCAgICA9IHRoaXMuX21hdHJpeC5jbG9uZSgpO1xyXG5cclxuICAgIHRoaXMuX2FuZ2xlID0gMDtcclxuICAgIHRoaXMuX3BhdGguX21hcFxyXG4gICAgICAub24oJ21vdXNlbW92ZScsIHRoaXMuX29uUm90YXRlLCAgICAgdGhpcylcclxuICAgICAgLm9uKCdtb3VzZXVwJywgICB0aGlzLl9vblJvdGF0ZUVuZCwgdGhpcyk7XHJcblxyXG4gICAgdGhpcy5fY2FjaGVQb2ludHMoKTtcclxuICAgIHRoaXMuX3BhdGhcclxuICAgICAgLmZpcmUoJ3RyYW5zZm9ybXN0YXJ0JywgICB7IGxheWVyOiB0aGlzLl9wYXRoIH0pXHJcbiAgICAgIC5maXJlKCdyb3RhdGVzdGFydCcsIHsgbGF5ZXI6IHRoaXMuX3BhdGgsIHJvdGF0aW9uOiAwIH0pO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBAcGFyYW0gIHtFdmVudH0gZXZ0XHJcbiAgICovXHJcbiAgX29uUm90YXRlOiBmdW5jdGlvbihldnQpIHtcclxuICAgIHZhciBwb3MgPSBldnQubGF5ZXJQb2ludDtcclxuICAgIHZhciBwcmV2aW91cyA9IHRoaXMuX3JvdGF0aW9uU3RhcnQ7XHJcbiAgICB2YXIgb3JpZ2luICAgPSB0aGlzLl9yb3RhdGlvbk9yaWdpblB0O1xyXG5cclxuICAgIC8vIHJvdGF0aW9uIHN0ZXAgYW5nbGVcclxuICAgIHRoaXMuX2FuZ2xlID0gTWF0aC5hdGFuMihwb3MueSAtIG9yaWdpbi55LCBwb3MueCAtIG9yaWdpbi54KSAtXHJcbiAgICAgICAgICAgICAgICAgIE1hdGguYXRhbjIocHJldmlvdXMueSAtIG9yaWdpbi55LCBwcmV2aW91cy54IC0gb3JpZ2luLngpO1xyXG5cclxuICAgIHRoaXMuX21hdHJpeCA9IHRoaXMuX2luaXRpYWxNYXRyaXhcclxuICAgICAgLmNsb25lKClcclxuICAgICAgLnJvdGF0ZSh0aGlzLl9hbmdsZSwgb3JpZ2luKVxyXG4gICAgICAuZmxpcCgpO1xyXG5cclxuICAgIHRoaXMuX3VwZGF0ZSgpO1xyXG4gICAgdGhpcy5fcGF0aC5maXJlKCdyb3RhdGUnLCB7IGxheWVyOiB0aGlzLl9wYXRoLCByb3RhdGlvbjogdGhpcy5fYW5nbGUgfSk7XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIEBwYXJhbSAge0V2ZW50fSBldnRcclxuICAgKi9cclxuICBfb25Sb3RhdGVFbmQ6IGZ1bmN0aW9uKGV2dCkge1xyXG4gICAgdGhpcy5fcGF0aC5fbWFwXHJcbiAgICAgIC5vZmYoJ21vdXNlbW92ZScsIHRoaXMuX29uUm90YXRlLCB0aGlzKVxyXG4gICAgICAub2ZmKCdtb3VzZXVwJywgICB0aGlzLl9vblJvdGF0ZUVuZCwgdGhpcyk7XHJcblxyXG4gICAgdGhpcy5fYXBwbHkoKTtcclxuICAgIHRoaXMuX3BhdGguZmlyZSgncm90YXRlZW5kJywgeyBsYXllcjogdGhpcy5fcGF0aCwgcm90YXRpb246IHRoaXMuX2FuZ2xlIH0pO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBAcGFyYW0gIHtFdmVudH0gZXZ0XHJcbiAgICovXHJcbiAgX29uU2NhbGVTdGFydDogZnVuY3Rpb24oZXZ0KSB7XHJcbiAgICB2YXIgbWFya2VyID0gZXZ0LnRhcmdldDtcclxuICAgIHZhciBtYXAgPSB0aGlzLl9tYXA7XHJcblxyXG4gICAgbWFwLmRyYWdnaW5nLmRpc2FibGUoKTtcclxuXHJcbiAgICB0aGlzLl9hY3RpdmVNYXJrZXIgPSBtYXJrZXI7XHJcblxyXG4gICAgdGhpcy5fb3JpZ2luTWFya2VyID0gdGhpcy5faGFuZGxlcnNbKG1hcmtlci5vcHRpb25zLmluZGV4ICsgMikgJSA0XTtcclxuICAgIHRoaXMuX3NjYWxlT3JpZ2luICA9IHRoaXMuX29yaWdpbk1hcmtlci5nZXRMYXRMbmcoKTtcclxuXHJcbiAgICB0aGlzLl9pbml0aWFsTWF0cml4ID0gdGhpcy5fbWF0cml4LmNsb25lKCk7XHJcbiAgICB0aGlzLl9jYWNoZVBvaW50cygpO1xyXG5cclxuICAgIHRoaXMuX21hcFxyXG4gICAgICAub24oJ21vdXNlbW92ZScsIHRoaXMuX29uU2NhbGUsICAgIHRoaXMpXHJcbiAgICAgIC5vbignbW91c2V1cCcsICAgdGhpcy5fb25TY2FsZUVuZCwgdGhpcyk7XHJcbiAgICB0aGlzLl9pbml0aWFsRGlzdCAgPSB0aGlzLl9vcmlnaW5NYXJrZXIuX3BvaW50LmRpc3RhbmNlVG8odGhpcy5fYWN0aXZlTWFya2VyLl9wb2ludCk7XHJcbiAgICB0aGlzLl9pbml0aWFsRGlzdFggPSB0aGlzLl9vcmlnaW5NYXJrZXIuX3BvaW50LnggLSB0aGlzLl9hY3RpdmVNYXJrZXIuX3BvaW50Lng7XHJcbiAgICB0aGlzLl9pbml0aWFsRGlzdFkgPSB0aGlzLl9vcmlnaW5NYXJrZXIuX3BvaW50LnkgLSB0aGlzLl9hY3RpdmVNYXJrZXIuX3BvaW50Lnk7XHJcblxyXG4gICAgdGhpcy5fcGF0aFxyXG4gICAgICAuZmlyZSgndHJhbnNmb3Jtc3RhcnQnLCB7IGxheWVyOiB0aGlzLl9wYXRoIH0pXHJcbiAgICAgIC5maXJlKCdzY2FsZXN0YXJ0JywgeyBsYXllcjogdGhpcy5fcGF0aCwgc2NhbGU6IEwucG9pbnQoMSwgMSkgfSk7XHJcblxyXG4gICAgdGhpcy5fbWFwLnJlbW92ZUxheWVyKHRoaXMuX2hhbmRsZUxpbmUpO1xyXG4gICAgdGhpcy5fbWFwLnJlbW92ZUxheWVyKHRoaXMuX3JvdGF0aW9uTWFya2VyKTtcclxuXHJcbiAgICAvL3RoaXMuX2hhbmRsZUxpbmUgPSB0aGlzLl9yb3RhdGlvbk1hcmtlciA9IG51bGw7XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIEBwYXJhbSAge0V2ZW50fSBldnRcclxuICAgKi9cclxuICBfb25TY2FsZTogZnVuY3Rpb24oZXZ0KSB7XHJcbiAgICB2YXIgb3JpZ2luUG9pbnQgPSB0aGlzLl9vcmlnaW5NYXJrZXIuX3BvaW50O1xyXG4gICAgdmFyIHJhdGlvWCwgcmF0aW9ZO1xyXG4gICAgaWYgKHRoaXMub3B0aW9ucy51bmlmb3JtU2NhbGluZykge1xyXG4gICAgICByYXRpb1ggPSBvcmlnaW5Qb2ludC5kaXN0YW5jZVRvKGV2dC5sYXllclBvaW50KSAvIHRoaXMuX2luaXRpYWxEaXN0O1xyXG4gICAgICByYXRpb1kgPSByYXRpb1g7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICByYXRpb1ggPSAob3JpZ2luUG9pbnQueCAtIGV2dC5sYXllclBvaW50LngpIC8gdGhpcy5faW5pdGlhbERpc3RYO1xyXG4gICAgICByYXRpb1kgPSAob3JpZ2luUG9pbnQueSAtIGV2dC5sYXllclBvaW50LnkpIC8gdGhpcy5faW5pdGlhbERpc3RZO1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuX3NjYWxlID0gbmV3IEwuUG9pbnQocmF0aW9YLCByYXRpb1kpO1xyXG5cclxuICAgIC8vIHVwZGF0ZSBtYXRyaXhcclxuICAgIHRoaXMuX21hdHJpeCA9IHRoaXMuX2luaXRpYWxNYXRyaXhcclxuICAgICAgLmNsb25lKClcclxuICAgICAgLnNjYWxlKHRoaXMuX3NjYWxlLCBvcmlnaW5Qb2ludCk7XHJcblxyXG4gICAgdGhpcy5fdXBkYXRlKCk7XHJcbiAgICB0aGlzLl9wYXRoLmZpcmUoJ3NjYWxlJywge1xyXG4gICAgICBsYXllcjogdGhpcy5fcGF0aCwgc2NhbGU6IHRoaXMuX3NjYWxlLmNsb25lKCkgfSk7XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIFNjYWxpbmcgY29tcGxldGVcclxuICAgKiBAcGFyYW0gIHtFdmVudH0gZXZ0XHJcbiAgICovXHJcbiAgX29uU2NhbGVFbmQ6IGZ1bmN0aW9uKGV2dCkge1xyXG4gICAgdGhpcy5fbWFwXHJcbiAgICAgIC5vZmYoJ21vdXNlbW92ZScsIHRoaXMuX29uU2NhbGUsICAgIHRoaXMpXHJcbiAgICAgIC5vZmYoJ21vdXNldXAnLCAgIHRoaXMuX29uU2NhbGVFbmQsIHRoaXMpO1xyXG5cclxuICAgIHRoaXMuX21hcC5hZGRMYXllcih0aGlzLl9oYW5kbGVMaW5lKTtcclxuICAgIHRoaXMuX21hcC5hZGRMYXllcih0aGlzLl9yb3RhdGlvbk1hcmtlcik7XHJcblxyXG4gICAgdGhpcy5fYXBwbHkoKTtcclxuICAgIHRoaXMuX3BhdGguZmlyZSgnc2NhbGVlbmQnLCB7XHJcbiAgICAgIGxheWVyOiB0aGlzLl9wYXRoLCBzY2FsZTogdGhpcy5fc2NhbGUuY2xvbmUoKSB9KTtcclxuICB9LFxyXG5cclxuXHJcbiAgLyoqXHJcbiAgICogQ2FjaGUgY3VycmVudCBoYW5kbGVycyBwb3NpdGlvbnNcclxuICAgKi9cclxuICBfY2FjaGVQb2ludHM6IGZ1bmN0aW9uKCkge1xyXG4gICAgdGhpcy5faGFuZGxlcnNHcm91cC5lYWNoTGF5ZXIoZnVuY3Rpb24obGF5ZXIpIHtcclxuICAgICAgbGF5ZXIuYnJpbmdUb0Zyb250KCk7XHJcbiAgICB9KTtcclxuICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSB0aGlzLl9oYW5kbGVycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xyXG4gICAgICB2YXIgaGFuZGxlciA9IHRoaXMuX2hhbmRsZXJzW2ldO1xyXG4gICAgICBoYW5kbGVyLl9pbml0aWFsUG9pbnQgPSBoYW5kbGVyLl9wb2ludC5jbG9uZSgpO1xyXG4gICAgfVxyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBCb3VuZGluZyBwb2x5Z29uXHJcbiAgICogQHJldHVybiB7TC5Qb2x5Z29ufVxyXG4gICAqL1xyXG4gIF9nZXRCb3VuZGluZ1BvbHlnb246IGZ1bmN0aW9uKCkge1xyXG4gICAgaWYgKHRoaXMuX3JlY3RTaGFwZSkge1xyXG4gICAgICByZXR1cm4gTC5HZW9KU09OLmdlb21ldHJ5VG9MYXllcihcclxuICAgICAgICB0aGlzLl9yZWN0U2hhcGUsIHRoaXMub3B0aW9ucy5ib3VuZHNPcHRpb25zKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHJldHVybiBuZXcgTC5SZWN0YW5nbGUoXHJcbiAgICAgICAgdGhpcy5fcGF0aC5nZXRCb3VuZHMoKSwgdGhpcy5vcHRpb25zLmJvdW5kc09wdGlvbnMpO1xyXG4gICAgfVxyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBDcmVhdGUgY29ybmVyIG1hcmtlclxyXG4gICAqIEBwYXJhbSAge0wuTGF0TG5nfSBsYXRsbmdcclxuICAgKiBAcGFyYW0gIHtOdW1iZXJ9ICAgdHlwZSBvbmUgb2YgTC5IYW5kbGVyLlBhdGhUcmFuc2Zvcm0uSGFuZGxlclR5cGVzXHJcbiAgICogQHBhcmFtICB7TnVtYmVyfSAgIGluZGV4XHJcbiAgICogQHJldHVybiB7TC5IYW5kbGVyLlBhdGhUcmFuc2Zvcm0uSGFuZGxlfVxyXG4gICAqL1xyXG4gIF9jcmVhdGVIYW5kbGVyOiBmdW5jdGlvbihsYXRsbmcsIHR5cGUsIGluZGV4KSB7XHJcbiAgICB2YXIgSGFuZGxlQ2xhc3MgPSB0aGlzLm9wdGlvbnMuaGFuZGxlQ2xhc3M7XHJcbiAgICB2YXIgbWFya2VyID0gbmV3IEhhbmRsZUNsYXNzKGxhdGxuZyxcclxuICAgICAgTC5VdGlsLmV4dGVuZCh7fSwgdGhpcy5vcHRpb25zLmhhbmRsZXJPcHRpb25zLCB7XHJcbiAgICAgICAgY2xhc3NOYW1lOiAnbGVhZmxldC1kcmFnLXRyYW5zZm9ybS1tYXJrZXIgZHJhZy1tYXJrZXItLScgK1xyXG4gICAgICAgICAgICAgICAgICAgaW5kZXggKyAnIGRyYWctbWFya2VyLS0nICsgdHlwZSxcclxuICAgICAgICBpbmRleDogICAgIGluZGV4LFxyXG4gICAgICAgIHR5cGU6ICAgICAgdHlwZVxyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICBtYXJrZXIub24oJ21vdXNlZG93bicsIHRoaXMuX29uU2NhbGVTdGFydCwgdGhpcyk7XHJcbiAgICByZXR1cm4gbWFya2VyO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBIaWRlKG5vdCByZW1vdmUpIHRoZSBoYW5kbGVycyBsYXllclxyXG4gICAqL1xyXG4gIF9oaWRlSGFuZGxlcnM6IGZ1bmN0aW9uKCkge1xyXG4gICAgdGhpcy5fbWFwLnJlbW92ZUxheWVyKHRoaXMuX2hhbmRsZXJzR3JvdXApO1xyXG4gIH0sXHJcblxyXG5cclxuICAvKipcclxuICAgKiBIaWRlIGhhbmRsZXJzIGFuZCByZWN0YW5nbGVcclxuICAgKi9cclxuICBfb25EcmFnU3RhcnQ6IGZ1bmN0aW9uKCkge1xyXG4gICAgdGhpcy5faGlkZUhhbmRsZXJzKCk7XHJcbiAgfSxcclxuXHJcblxyXG4gIC8qKlxyXG4gICAqIERyYWcgcmVjdGFuZ2xlLCByZS1jcmVhdGUgaGFuZGxlcnNcclxuICAgKi9cclxuICBfb25EcmFnRW5kOiBmdW5jdGlvbihldnQpIHtcclxuICAgIHZhciByZWN0ID0gdGhpcy5fcmVjdDtcclxuICAgIHZhciBtYXRyaXggPSAoZXZ0LmxheWVyID8gZXZ0LmxheWVyIDogdGhpcy5fcGF0aCkuZHJhZ2dpbmcuX21hdHJpeC5zbGljZSgpO1xyXG5cclxuICAgIGlmICghcmVjdC5kcmFnZ2luZykge1xyXG4gICAgICByZWN0LmRyYWdnaW5nID0gbmV3IEwuSGFuZGxlci5QYXRoRHJhZyhyZWN0KTtcclxuICAgIH1cclxuICAgIHJlY3QuZHJhZ2dpbmcuZW5hYmxlKCk7XHJcbiAgICB0aGlzLl9tYXAuYWRkTGF5ZXIocmVjdCk7XHJcbiAgICByZWN0LmRyYWdnaW5nLl90cmFuc2Zvcm1Qb2ludHMobWF0cml4KTtcclxuICAgIHJlY3QuX3VwZGF0ZVBhdGgoKTtcclxuICAgIHJlY3QuX3Byb2plY3QoKTtcclxuICAgIHJlY3QuZHJhZ2dpbmcuZGlzYWJsZSgpO1xyXG5cclxuICAgIHRoaXMuX21hcC5hZGRMYXllcih0aGlzLl9oYW5kbGVyc0dyb3VwKTtcclxuICAgIHRoaXMuX3VwZGF0ZUhhbmRsZXJzKCk7XHJcblxyXG4gICAgdGhpcy5fcGF0aC5maXJlKCd0cmFuc2Zvcm1lZCcsIHtcclxuICAgICAgc2NhbGU6IEwucG9pbnQoMSwgMSksXHJcbiAgICAgIHJvdGF0aW9uOiAwLFxyXG4gICAgICBtYXRyaXg6IEwubWF0cml4LmFwcGx5KHVuZGVmaW5lZCwgbWF0cml4KSxcclxuICAgICAgdHJhbnNsYXRlOiBMLnBvaW50KG1hdHJpeFs0XSwgbWF0cml4WzVdKSxcclxuICAgICAgbGF5ZXI6IHRoaXMuX3BhdGhcclxuICAgIH0pO1xyXG4gIH1cclxufSk7XHJcblxyXG5cclxuTC5QYXRoLmFkZEluaXRIb29rKGZ1bmN0aW9uKCkge1xyXG4gIGlmICh0aGlzLm9wdGlvbnMudHJhbnNmb3JtKSB7XHJcbiAgICB0aGlzLnRyYW5zZm9ybSA9IG5ldyBMLkhhbmRsZXIuUGF0aFRyYW5zZm9ybSh0aGlzLCB0aGlzLm9wdGlvbnMudHJhbnNmb3JtKTtcclxuICB9XHJcbn0pO1xyXG4iLCIvKipcclxuICogUG9pbnQgb24gdGhlIGxpbmUgc2VnbWVudCBvciBpdHMgZXh0ZW50aW9uXHJcbiAqXHJcbiAqIEBwYXJhbSAge0wuUG9pbnR9IHN0YXJ0XHJcbiAqIEBwYXJhbSAge0wuUG9pbnR9IGZpbmFsXHJcbiAqIEBwYXJhbSAge051bWJlcn0gIGRpc3RQeFxyXG4gKiBAcmV0dXJuIHtMLlBvaW50fVxyXG4gKi9cclxuTC5MaW5lVXRpbC5wb2ludE9uTGluZSA9IGZ1bmN0aW9uKHN0YXJ0LCBmaW5hbCwgZGlzdFB4KSB7XHJcbiAgdmFyIHJhdGlvID0gMSArIGRpc3RQeCAvIHN0YXJ0LmRpc3RhbmNlVG8oZmluYWwpO1xyXG4gIHJldHVybiBuZXcgTC5Qb2ludChcclxuICAgIHN0YXJ0LnggKyAoZmluYWwueCAtIHN0YXJ0LngpICogcmF0aW8sXHJcbiAgICBzdGFydC55ICsgKGZpbmFsLnkgLSBzdGFydC55KSAqIHJhdGlvXHJcbiAgKTtcclxufTtcclxuXHJcblxyXG4vKipcclxuICogRGVlcCBtZXJnZSBvYmplY3RzLlxyXG4gKi9cclxuTC5VdGlsLm1lcmdlID0gZnVuY3Rpb24oKSB7XHJcbiAgdmFyIGkgPSAxO1xyXG4gIHZhciBrZXksIHZhbDtcclxuICB2YXIgb2JqID0gYXJndW1lbnRzW2ldO1xyXG5cclxuICBmdW5jdGlvbiBpc09iamVjdChvYmplY3QpIHtcclxuICAgIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwob2JqZWN0KSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XHJcbiAgfVxyXG5cclxuICAvLyBtYWtlIHN1cmUgd2UgZG9uJ3QgbW9kaWZ5IHNvdXJjZSBlbGVtZW50IGFuZCBpdCdzIHByb3BlcnRpZXNcclxuICAvLyBvYmplY3RzIGFyZSBwYXNzZWQgYnkgcmVmZXJlbmNlXHJcbiAgdmFyIHRhcmdldCA9IGFyZ3VtZW50c1swXTtcclxuXHJcbiAgd2hpbGUgKG9iaikge1xyXG4gICAgb2JqID0gYXJndW1lbnRzW2krK107XHJcbiAgICBmb3IgKGtleSBpbiBvYmopIHtcclxuICAgICAgaWYgKCFvYmouaGFzT3duUHJvcGVydHkoa2V5KSkge1xyXG4gICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICB2YWwgPSBvYmpba2V5XTtcclxuXHJcbiAgICAgIGlmIChpc09iamVjdCh2YWwpICYmIGlzT2JqZWN0KHRhcmdldFtrZXldKSl7XHJcbiAgICAgICAgdGFyZ2V0W2tleV0gPSBMLlV0aWwubWVyZ2UodGFyZ2V0W2tleV0sIHZhbCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGFyZ2V0W2tleV0gPSB2YWw7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcbiAgcmV0dXJuIHRhcmdldDtcclxufTtcclxuIl19 diff --git a/src/Path.Transform.js b/src/Path.Transform.js index e4cdc55..d276483 100644 --- a/src/Path.Transform.js +++ b/src/Path.Transform.js @@ -317,8 +317,13 @@ L.Handler.PathTransform = L.Handler.extend({ this._rectShape = this._rect.toGeoJSON(); - this._handlersGroup.removeLayer(this._handleLine); - this._handlersGroup.removeLayer(this._rotationMarker); + if (this._handleLine) { + this._handlersGroup.removeLayer(this._handleLine); + } + + if (this._rotationMarker) { + this._handlersGroup.removeLayer(this._rotationMarker); + } this._handleLine = this._rotationMarker = null; From f32f5a5ada1207167cabafd59ea5818b3841182c Mon Sep 17 00:00:00 2001 From: Alexander Milevski Date: Thu, 29 Jun 2017 22:26:46 +0200 Subject: [PATCH 03/46] leaflet 1.1.0 --- dist/L.Path.Transform-src.js | 836 +- dist/L.Path.Transform.js | 6 +- example/js/app.js | 12 +- example/js/bundle.js | 24296 +++++++++++++++++---------------- package-lock.json | 2674 ++++ package.json | 18 +- src/Path.Transform.js | 13 +- src/Util.js | 10 +- 8 files changed, 15479 insertions(+), 12386 deletions(-) create mode 100644 package-lock.json diff --git a/dist/L.Path.Transform-src.js b/dist/L.Path.Transform-src.js index cc2e91c..d8faa54 100644 --- a/dist/L.Path.Transform-src.js +++ b/dist/L.Path.Transform-src.js @@ -9,370 +9,403 @@ * Renderer-independent */ L.Path.include({ + /** + * Applies matrix transformation to SVG + * @param {Array.?} matrix + */ + _transform: function (matrix) { + if (this._renderer) { + if (matrix) { + this._renderer.transformPath(this, matrix); + } else { + // reset transform matrix + this._renderer._resetTransformPath(this); + this._update(); + } + } + return this; + }, - /** - * Applies matrix transformation to SVG - * @param {Array.?} matrix - */ - _transform: function(matrix) { - if (this._renderer) { - if (matrix) { - this._renderer.transformPath(this, matrix); - } else { - // reset transform matrix - this._renderer._resetTransformPath(this); - this._update(); - } - } - return this; - }, - - /** - * Check if the feature was dragged, that'll supress the click event - * on mouseup. That fixes popups for example - * - * @param {MouseEvent} e - */ - _onMouseClick: function(e) { - if ((this.dragging && this.dragging.moved()) || - (this._map.dragging && this._map.dragging.moved())) { - return; - } - - this._fireMouseEvent(e); - } + /** + * Check if the feature was dragged, that'll supress the click event + * on mouseup. That fixes popups for example + * + * @param {MouseEvent} e + */ + _onMouseClick: function (e) { + if ( + (this.dragging && this.dragging.moved()) || + (this._map.dragging && this._map.dragging.moved()) + ) { + return; + } + this._fireMouseEvent(e); + }, }); +var END = { + mousedown: 'mouseup', + touchstart: 'touchend', + pointerdown: 'touchend', + MSPointerDown: 'touchend', +}; + +var MOVE = { + mousedown: 'mousemove', + touchstart: 'touchmove', + pointerdown: 'touchmove', + MSPointerDown: 'touchmove', +}; + +function distance(a, b) { + var dx = a.x - b.x, + dy = a.y - b.y; + return Math.sqrt(dx * dx + dy * dy); +} + /** * Drag handler * @class L.Path.Drag * @extends {L.Handler} */ -L.Handler.PathDrag = L.Handler.extend( /** @lends L.Path.Drag.prototype */ { - - statics: { - DRAGGING_CLS: 'leaflet-path-draggable', - }, - - - /** - * @param {L.Path} path - * @constructor - */ - initialize: function(path) { +L.Handler.PathDrag = L.Handler.extend( + /** @lends L.Path.Drag.prototype */ { + statics: { + DRAGGING_CLS: 'leaflet-path-draggable', + }, /** - * @type {L.Path} + * @param {L.Path} path + * @constructor */ - this._path = path; + initialize: function (path) { + /** + * @type {L.Path} + */ + this._path = path; + + /** + * @type {Array.} + */ + this._matrix = null; + + /** + * @type {L.Point} + */ + this._startPoint = null; + + /** + * @type {L.Point} + */ + this._dragStartPoint = null; + + /** + * @type {Boolean} + */ + this._mapDraggingWasEnabled = false; + + this._path._dragMoved = false; + }, /** - * @type {Array.} + * Enable dragging */ - this._matrix = null; + addHooks: function () { + this._path.on('mousedown', this._onDragStart, this); + + this._path.options.className = this._path.options.className + ? this._path.options.className + ' ' + L.Handler.PathDrag.DRAGGING_CLS + : L.Handler.PathDrag.DRAGGING_CLS; + + if (this._path._path) { + L.DomUtil.addClass(this._path._path, L.Handler.PathDrag.DRAGGING_CLS); + } + }, /** - * @type {L.Point} + * Disable dragging */ - this._startPoint = null; + removeHooks: function () { + this._path.off('mousedown', this._onDragStart, this); + + this._path.options.className = this._path.options.className.replace( + new RegExp('\\s+' + L.Handler.PathDrag.DRAGGING_CLS), + '' + ); + if (this._path._path) { + L.DomUtil.removeClass( + this._path._path, + L.Handler.PathDrag.DRAGGING_CLS + ); + } + }, /** - * @type {L.Point} + * @return {Boolean} */ - this._dragStartPoint = null; + moved: function () { + return this._path._dragMoved; + }, /** - * @type {Boolean} + * Start drag + * @param {L.MouseEvent} evt */ - this._mapDraggingWasEnabled = false; - - }, + _onDragStart: function (evt) { + var eventType = evt.originalEvent._simulated + ? 'touchstart' + : evt.originalEvent.type; + + this._mapDraggingWasEnabled = false; + this._startPoint = evt.containerPoint.clone(); + + this._dragStartPoint = evt.containerPoint.clone(); + this._matrix = [1, 0, 0, 1, 0, 0]; + L.DomEvent.stop(evt.originalEvent); + + L.DomUtil.addClass( + this._path._renderer._container, + 'leaflet-interactive' + ); + L.DomEvent.on(document, MOVE[eventType], this._onDrag, this).on( + document, + END[eventType], + this._onDragEnd, + this + ); + + //L.DomEvent.disableClickPropagation(this._path._renderer._container); + + if (this._path._map.dragging.enabled()) { + // I guess it's required because mousdown gets simulated with a delay + //this._path._map.dragging._draggable._onUp(evt); + + this._path._map.dragging.disable(); + this._mapDraggingWasEnabled = true; + } + this._path._dragMoved = false; - /** - * Enable dragging - */ - addHooks: function() { - this._path.on('mousedown', this._onDragStart, this); + if (this._path._popup) { + // that might be a case on touch devices as well + this._path._popup.close(); + } - this._path.options.className = this._path.options.className ? - (this._path.options.className + ' ' + L.Handler.PathDrag.DRAGGING_CLS) : - L.Handler.PathDrag.DRAGGING_CLS; + this._replaceCoordGetters(evt); + }, - if (this._path._path) { - L.DomUtil.addClass(this._path._path, L.Handler.PathDrag.DRAGGING_CLS); - } - }, + /** + * Dragging + * @param {L.MouseEvent} evt + */ + _onDrag: function (evt) { + L.DomEvent.stop(evt); - /** - * Disable dragging - */ - removeHooks: function() { - this._path.off('mousedown', this._onDragStart, this); + var first = evt.touches && evt.touches.length >= 1 ? evt.touches[0] : evt; + var containerPoint = this._path._map.mouseEventToContainerPoint(first); - this._path.options.className = this._path.options.className - .replace(new RegExp('\\s+' + L.Handler.PathDrag.DRAGGING_CLS), ''); - if (this._path._path) { - L.DomUtil.removeClass(this._path._path, L.Handler.PathDrag.DRAGGING_CLS); - } - }, + // skip taps + if (evt.type === 'touchmove' && !this._path._dragMoved) { + var totalMouseDragDistance = + this._dragStartPoint.distanceTo(containerPoint); + if (totalMouseDragDistance <= this._path._map.options.tapTolerance) { + return; + } + } - /** - * @return {Boolean} - */ - moved: function() { - return this._path._dragMoved; - }, + var x = containerPoint.x; + var y = containerPoint.y; - /** - * Start drag - * @param {L.MouseEvent} evt - */ - _onDragStart: function(evt) { - var eventType = evt.originalEvent._simulated ? 'touchstart' : evt.originalEvent.type; - - this._mapDraggingWasEnabled = false; - this._startPoint = evt.containerPoint.clone(); - this._dragStartPoint = evt.containerPoint.clone(); - this._matrix = [1, 0, 0, 1, 0, 0]; - L.DomEvent.stop(evt.originalEvent); - - L.DomUtil.addClass(this._path._renderer._container, 'leaflet-interactive'); - L.DomEvent - .on(document, L.Draggable.MOVE[eventType], this._onDrag, this) - .on(document, L.Draggable.END[eventType], this._onDragEnd, this); - - if (this._path._map.dragging.enabled()) { - // I guess it's required because mousdown gets simulated with a delay - //this._path._map.dragging._draggable._onUp(evt); - - this._path._map.dragging.disable(); - this._mapDraggingWasEnabled = true; - } - this._path._dragMoved = false; + var dx = x - this._startPoint.x; + var dy = y - this._startPoint.y; - if (this._path._popup) { // that might be a case on touch devices as well - this._path._popup._close(); - } + // Send events only if point was moved + if (dx || dy) { + if (!this._path._dragMoved) { + this._path._dragMoved = true; + this._path.options.interactive = false; + this._path._map.dragging._draggable._moved = true; - this._replaceCoordGetters(evt); - }, + this._path.fire('dragstart', evt); + // we don't want that to happen on click + this._path.bringToFront(); + } - /** - * Dragging - * @param {L.MouseEvent} evt - */ - _onDrag: function(evt) { - L.DomEvent.stop(evt); + this._matrix[4] += dx; + this._matrix[5] += dy; - var first = (evt.touches && evt.touches.length >= 1 ? evt.touches[0] : evt); - var containerPoint = this._path._map.mouseEventToContainerPoint(first); + this._startPoint.x = x; + this._startPoint.y = y; - // skip taps - if (evt.type === 'touchmove' && !this._path._dragMoved) { - var totalMouseDragDistance = this._dragStartPoint.distanceTo(containerPoint); - if (totalMouseDragDistance <= this._path._map.options.tapTolerance) { - return; + this._path.fire('predrag', evt); + this._path._transform(this._matrix); + this._path.fire('drag', evt); } - } - - var x = containerPoint.x; - var y = containerPoint.y; - - var dx = x - this._startPoint.x; - var dy = y - this._startPoint.y; + }, - // Send events only if point was moved - if (dx || dy) { - if (!this._path._dragMoved) { - this._path._dragMoved = true; - this._path.fire('dragstart', evt); - // we don't want that to happen on click - this._path.bringToFront(); + /** + * Dragging stopped, apply + * @param {L.MouseEvent} evt + */ + _onDragEnd: function (evt) { + var containerPoint = this._path._map.mouseEventToContainerPoint(evt); + var moved = this.moved(); + + // apply matrix + if (moved) { + this._transformPoints(this._matrix); + this._path._updatePath(); + this._path._project(); + this._path._transform(null); + + L.DomEvent.stop(evt); } - this._matrix[4] += dx; - this._matrix[5] += dy; - - this._startPoint.x = x; - this._startPoint.y = y; - - this._path.fire('predrag', evt); - this._path._transform(this._matrix); - this._path.fire('drag', evt); - } - }, - - /** - * Dragging stopped, apply - * @param {L.MouseEvent} evt - */ - _onDragEnd: function(evt) { - var containerPoint = this._path._map.mouseEventToContainerPoint(evt); - var moved = this.moved(); + L.DomEvent.off(document, 'mousemove touchmove', this._onDrag, this); + L.DomEvent.off(document, 'mouseup touchend', this._onDragEnd, this); - // apply matrix - if (moved) { - this._transformPoints(this._matrix); - this._path._updatePath(); - this._path._project(); - this._path._transform(null); - - L.DomEvent.stop(evt); - } + this._restoreCoordGetters(); + // consistency + if (moved) { + this._path.fire('dragend', { + distance: distance(this._dragStartPoint, containerPoint), + }); - L.DomEvent - .off(document, 'mousemove touchmove', this._onDrag, this) - .off(document, 'mouseup touchend', this._onDragEnd, this); - - this._restoreCoordGetters(); - - // consistency - if (moved) { - this._path.fire('dragend', { - distance: Math.sqrt( - L.LineUtil._sqDist(this._dragStartPoint, containerPoint) - ) - }); - - // hack for skipping the click in canvas-rendered layers - var contains = this._path._containsPoint; - this._path._containsPoint = L.Util.falseFn; - L.Util.requestAnimFrame(function() { - L.DomEvent._skipped({ type: 'click' }); - this._path._containsPoint = contains; - }, this); - } - - this._matrix = null; - this._startPoint = null; - this._dragStartPoint = null; - this._path._dragMoved = false; + // hack for skipping the click in canvas-rendered layers + var contains = this._path._containsPoint; + this._path._containsPoint = L.Util.falseFn; - if (this._mapDraggingWasEnabled) { - if (moved) L.DomEvent._fakeStop({ type: 'click' }); - this._path._map.dragging.enable(); - } - }, + L.Util.requestAnimFrame(function () { + this._path._dragMoved = false; + this._path.options.interactive = true; + this._path._containsPoint = contains; + }, this); + } + if (this._mapDraggingWasEnabled) { + //if (moved) L.DomEvent.fakeStop({ type: "click" }); + this._path._map.dragging.enable(); + } + }, - /** - * Applies transformation, does it in one sweep for performance, - * so don't be surprised about the code repetition. - * - * [ x ] [ a b tx ] [ x ] [ a * x + b * y + tx ] - * [ y ] = [ c d ty ] [ y ] = [ c * x + d * y + ty ] - * - * @param {Array.} matrix - */ - _transformPoints: function(matrix, dest) { - var path = this._path; - var i, len, latlng; + /** + * Applies transformation, does it in one sweep for performance, + * so don't be surprised about the code repetition. + * + * [ x ] [ a b tx ] [ x ] [ a * x + b * y + tx ] + * [ y ] = [ c d ty ] [ y ] = [ c * x + d * y + ty ] + * + * @param {Array.} matrix + */ + _transformPoints: function (matrix, dest) { + var path = this._path; + var i, len, latlng; - var px = L.point(matrix[4], matrix[5]); + var px = L.point(matrix[4], matrix[5]); - var crs = path._map.options.crs; - var transformation = crs.transformation; - var scale = crs.scale(path._map.getZoom()); - var projection = crs.projection; + var crs = path._map.options.crs; + var transformation = crs.transformation; + var scale = crs.scale(path._map.getZoom()); + var projection = crs.projection; - var diff = transformation.untransform(px, scale) - .subtract(transformation.untransform(L.point(0, 0), scale)); - var applyTransform = !dest; + var diff = transformation + .untransform(px, scale) + .subtract(transformation.untransform(L.point(0, 0), scale)); + var applyTransform = !dest; - path._bounds = new L.LatLngBounds(); + path._bounds = new L.LatLngBounds(); - // console.time('transform'); - // all shifts are in-place - if (path._point) { // L.Circle - dest = projection.unproject( - projection.project(path._latlng)._add(diff)); - if (applyTransform) { - path._latlng = dest; - path._point._add(px); - } - } else if (path._rings || path._parts) { // everything else - var rings = path._rings || path._parts; - var latlngs = path._latlngs; - dest = dest || latlngs; - if (!L.Util.isArray(latlngs[0])) { // polyline - latlngs = [latlngs]; - dest = [dest]; - } - for (i = 0, len = rings.length; i < len; i++) { - dest[i] = dest[i] || []; - for (var j = 0, jj = rings[i].length; j < jj; j++) { - latlng = latlngs[i][j]; - dest[i][j] = projection - .unproject(projection.project(latlng)._add(diff)); - if (applyTransform) { - path._bounds.extend(latlngs[i][j]); - rings[i][j]._add(px); + // console.time('transform'); + // all shifts are in-place + if (path._point) { + // L.Circle + dest = projection.unproject( + projection.project(path._latlng)._add(diff) + ); + if (applyTransform) { + path._latlng = dest; + path._point._add(px); + } + } else if (path._rings || path._parts) { + // everything else + var rings = path._rings || path._parts; + var latlngs = path._latlngs; + dest = dest || latlngs; + if (!L.Util.isArray(latlngs[0])) { + // polyline + latlngs = [latlngs]; + dest = [dest]; + } + for (i = 0, len = rings.length; i < len; i++) { + dest[i] = dest[i] || []; + for (var j = 0, jj = rings[i].length; j < jj; j++) { + latlng = latlngs[i][j]; + dest[i][j] = projection.unproject( + projection.project(latlng)._add(diff) + ); + if (applyTransform) { + path._bounds.extend(latlngs[i][j]); + rings[i][j]._add(px); + } } } } - } - return dest; - // console.timeEnd('transform'); - }, - - - - /** - * If you want to read the latlngs during the drag - your right, - * but they have to be transformed - */ - _replaceCoordGetters: function() { - if (this._path.getLatLng) { // Circle, CircleMarker - this._path.getLatLng_ = this._path.getLatLng; - this._path.getLatLng = L.Util.bind(function() { - return this.dragging._transformPoints(this.dragging._matrix, {}); - }, this._path); - } else if (this._path.getLatLngs) { - this._path.getLatLngs_ = this._path.getLatLngs; - this._path.getLatLngs = L.Util.bind(function() { - return this.dragging._transformPoints(this.dragging._matrix, []); - }, this._path); - } - }, + return dest; + // console.timeEnd('transform'); + }, + /** + * If you want to read the latlngs during the drag - your right, + * but they have to be transformed + */ + _replaceCoordGetters: function () { + if (this._path.getLatLng) { + // Circle, CircleMarker + this._path.getLatLng_ = this._path.getLatLng; + this._path.getLatLng = L.Util.bind(function () { + return this.dragging._transformPoints(this.dragging._matrix, {}); + }, this._path); + } else if (this._path.getLatLngs) { + this._path.getLatLngs_ = this._path.getLatLngs; + this._path.getLatLngs = L.Util.bind(function () { + return this.dragging._transformPoints(this.dragging._matrix, []); + }, this._path); + } + }, - /** - * Put back the getters - */ - _restoreCoordGetters: function() { - if (this._path.getLatLng_) { - this._path.getLatLng = this._path.getLatLng_; - delete this._path.getLatLng_; - } else if (this._path.getLatLngs_) { - this._path.getLatLngs = this._path.getLatLngs_; - delete this._path.getLatLngs_; - } + /** + * Put back the getters + */ + _restoreCoordGetters: function () { + if (this._path.getLatLng_) { + this._path.getLatLng = this._path.getLatLng_; + delete this._path.getLatLng_; + } else if (this._path.getLatLngs_) { + this._path.getLatLngs = this._path.getLatLngs_; + delete this._path.getLatLngs_; + } + }, } - -}); - +); /** * @param {L.Path} layer * @return {L.Path} */ -L.Handler.PathDrag.makeDraggable = function(layer) { +L.Handler.PathDrag.makeDraggable = function (layer) { layer.dragging = new L.Handler.PathDrag(layer); return layer; }; - /** * Also expose as a method * @return {L.Path} */ -L.Path.prototype.makeDraggable = function() { +L.Path.prototype.makeDraggable = function () { return L.Handler.PathDrag.makeDraggable(this); }; - -L.Path.addInitHook(function() { +L.Path.addInitHook(function () { if (this.options.draggable) { // ensure interactive this.options.interactive = true; @@ -388,93 +421,106 @@ L.Path.addInitHook(function() { } }); L.SVG.include({ + /** + * Reset transform matrix + */ + _resetTransformPath: function (layer) { + layer._path.setAttributeNS(null, 'transform', ''); + }, - /** - * Reset transform matrix - */ - _resetTransformPath: function(layer) { - layer._path.setAttributeNS(null, 'transform', ''); - }, - - /** - * Applies matrix transformation to SVG - * @param {L.Path} layer - * @param {Array.} matrix - */ - transformPath: function(layer, matrix) { - layer._path.setAttributeNS(null, 'transform', - 'matrix(' + matrix.join(' ') + ')'); - } - + /** + * Applies matrix transformation to SVG + * @param {L.Path} layer + * @param {Array.} matrix + */ + transformPath: function (layer, matrix) { + layer._path.setAttributeNS( + null, + 'transform', + 'matrix(' + matrix.join(' ') + ')' + ); + }, }); -L.SVG.include(!L.Browser.vml ? {} : { - - /** - * Reset transform matrix - */ - _resetTransformPath: function(layer) { - if (layer._skew) { - // super important! workaround for a 'jumping' glitch: - // disable transform before removing it - layer._skew.on = false; - layer._path.removeChild(layer._skew); - layer._skew = null; - } - }, - - /** - * Applies matrix transformation to VML - * @param {L.Path} layer - * @param {Array.} matrix - */ - transformPath: function(layer, matrix) { - var skew = layer._skew; - - if (!skew) { - skew = L.SVG.create('skew'); - layer._path.appendChild(skew); - skew.style.behavior = 'url(#default#VML)'; - layer._skew = skew; - } - - // handle skew/translate separately, cause it's broken - var mt = matrix[0].toFixed(8) + ' ' + matrix[1].toFixed(8) + ' ' + - matrix[2].toFixed(8) + ' ' + matrix[3].toFixed(8) + ' 0 0'; - var offset = Math.floor(matrix[4]).toFixed() + ', ' + - Math.floor(matrix[5]).toFixed() + ''; - - var s = this._path.style; - var l = parseFloat(s.left); - var t = parseFloat(s.top); - var w = parseFloat(s.width); - var h = parseFloat(s.height); - - if (isNaN(l)) l = 0; - if (isNaN(t)) t = 0; - if (isNaN(w) || !w) w = 1; - if (isNaN(h) || !h) h = 1; - - var origin = (-l / w - 0.5).toFixed(8) + ' ' + (-t / h - 0.5).toFixed(8); - - skew.on = 'f'; - skew.matrix = mt; - skew.origin = origin; - skew.offset = offset; - skew.on = true; - } +L.SVG.include( + !L.Browser.vml + ? {} + : { + /** + * Reset transform matrix + */ + _resetTransformPath: function (layer) { + if (layer._skew) { + // super important! workaround for a 'jumping' glitch: + // disable transform before removing it + layer._skew.on = false; + layer._path.removeChild(layer._skew); + layer._skew = null; + } + }, + + /** + * Applies matrix transformation to VML + * @param {L.Path} layer + * @param {Array.} matrix + */ + transformPath: function (layer, matrix) { + var skew = layer._skew; + + if (!skew) { + skew = L.SVG.create('skew'); + layer._path.appendChild(skew); + skew.style.behavior = 'url(#default#VML)'; + layer._skew = skew; + } -}); -L.Util.trueFn = function() { + // handle skew/translate separately, cause it's broken + var mt = + matrix[0].toFixed(8) + + ' ' + + matrix[1].toFixed(8) + + ' ' + + matrix[2].toFixed(8) + + ' ' + + matrix[3].toFixed(8) + + ' 0 0'; + var offset = + Math.floor(matrix[4]).toFixed() + + ', ' + + Math.floor(matrix[5]).toFixed() + + ''; + + var s = this._path.style; + var l = parseFloat(s.left); + var t = parseFloat(s.top); + var w = parseFloat(s.width); + var h = parseFloat(s.height); + + if (isNaN(l)) l = 0; + if (isNaN(t)) t = 0; + if (isNaN(w) || !w) w = 1; + if (isNaN(h) || !h) h = 1; + + var origin = + (-l / w - 0.5).toFixed(8) + ' ' + (-t / h - 0.5).toFixed(8); + + skew.on = 'f'; + skew.matrix = mt; + skew.origin = origin; + skew.offset = offset; + skew.on = true; + }, + } +); +function TRUE_FN() { return true; -}; +} L.Canvas.include({ - /** * Do nothing * @param {L.Path} layer */ - _resetTransformPath: function(layer) { + _resetTransformPath: function (layer) { if (!this._containerCopy) return; delete this._containerCopy; @@ -487,7 +533,6 @@ L.Canvas.include({ } }, - /** * Algorithm outline: * @@ -503,20 +548,22 @@ L.Canvas.include({ * @param {L.Path} layer * @param {Array.} matrix */ - transformPath: function(layer, matrix) { - var copy = this._containerCopy; - var ctx = this._ctx, copyCtx; - var m = L.Browser.retina ? 2 : 1; + transformPath: function (layer, matrix) { + var copy = this._containerCopy; + var ctx = this._ctx, + copyCtx; + var m = L.Browser.retina ? 2 : 1; var bounds = this._bounds; - var size = bounds.getSize(); - var pos = bounds.min; + var size = bounds.getSize(); + var pos = bounds.min; - if (!copy) { // get copy of all rendered layers + if (!copy) { + // get copy of all rendered layers copy = this._containerCopy = document.createElement('canvas'); copyCtx = copy.getContext('2d'); // document.body.appendChild(copy); - copy.width = m * size.x; + copy.width = m * size.x; copy.height = m * size.y; this._removePath(layer); @@ -528,7 +575,7 @@ L.Canvas.include({ // avoid flickering because of the 'mouseover's layer._containsPoint_ = layer._containsPoint; - layer._containsPoint = L.Util.trueFn; + layer._containsPoint = TRUE_FN; } ctx.save(); @@ -546,8 +593,7 @@ L.Canvas.include({ this._drawing = false; ctx.restore(); - } - + }, }); /** * Drag/rotate/resize handler for [leaflet](http://leafletjs.com) vector features. @@ -556,6 +602,12 @@ L.Canvas.include({ * @license MIT * @preserve */ +/** + * @namespace + * @type {Object} + */ +L.PathTransform = {}; + /** * Point on the line segment or its extention * @@ -564,7 +616,7 @@ L.Canvas.include({ * @param {Number} distPx * @return {L.Point} */ -L.LineUtil.pointOnLine = function(start, final, distPx) { +L.PathTransform.pointOnLine = function(start, final, distPx) { var ratio = 1 + distPx / start.distanceTo(final); return new L.Point( start.x + (final.x - start.x) * ratio, @@ -576,7 +628,7 @@ L.LineUtil.pointOnLine = function(start, final, distPx) { /** * Deep merge objects. */ -L.Util.merge = function() { +L.PathTransform.merge = function() { var i = 1; var key, val; var obj = arguments[i]; @@ -811,11 +863,6 @@ L.Matrix.prototype = { L.matrix = function(a, b, c, d, e, f) { return new L.Matrix(a, b, c, d, e, f); }; -/** - * @namespace - * @type {Object} - */ -L.PathTransform = {}; /** @@ -997,7 +1044,7 @@ L.Handler.PathTransform = L.Handler.extend({ this.disable(); } - this.options = L.Util.merge({}, + this.options = L.PathTransform.merge({}, L.Handler.PathTransform.prototype.options, options); @@ -1130,8 +1177,13 @@ L.Handler.PathTransform = L.Handler.extend({ this._rectShape = this._rect.toGeoJSON(); - this._handlersGroup.removeLayer(this._handleLine); - this._handlersGroup.removeLayer(this._rotationMarker); + if (this._handleLine) { + this._handlersGroup.removeLayer(this._handleLine); + } + + if (this._rotationMarker) { + this._handlersGroup.removeLayer(this._rotationMarker); + } this._handleLine = this._rotationMarker = null; @@ -1298,7 +1350,7 @@ L.Handler.PathTransform = L.Handler.extend({ (latlngs[1].lng + latlngs[2].lng) / 2); var handlerPosition = map.layerPointToLatLng( - L.LineUtil.pointOnLine( + L.PathTransform.pointOnLine( map.latLngToLayerPoint(bottom), map.latLngToLayerPoint(topPoint), this.options.handleLength) diff --git a/dist/L.Path.Transform.js b/dist/L.Path.Transform.js index 1a96412..9754d7d 100644 --- a/dist/L.Path.Transform.js +++ b/dist/L.Path.Transform.js @@ -3,11 +3,11 @@ * @author Alexander Milevski * @preserve */ -L.Path.include({_transform:function(t){if(this._renderer){if(t){this._renderer.transformPath(this,t)}else{this._renderer._resetTransformPath(this);this._update()}}return this},_onMouseClick:function(t){if(this.dragging&&this.dragging.moved()||this._map.dragging&&this._map.dragging.moved()){return}this._fireMouseEvent(t)}});L.Handler.PathDrag=L.Handler.extend({statics:{DRAGGING_CLS:"leaflet-path-draggable"},initialize:function(t){this._path=t;this._matrix=null;this._startPoint=null;this._dragStartPoint=null;this._mapDraggingWasEnabled=false},addHooks:function(){this._path.on("mousedown",this._onDragStart,this);this._path.options.className=this._path.options.className?this._path.options.className+" "+L.Handler.PathDrag.DRAGGING_CLS:L.Handler.PathDrag.DRAGGING_CLS;if(this._path._path){L.DomUtil.addClass(this._path._path,L.Handler.PathDrag.DRAGGING_CLS)}},removeHooks:function(){this._path.off("mousedown",this._onDragStart,this);this._path.options.className=this._path.options.className.replace(new RegExp("\\s+"+L.Handler.PathDrag.DRAGGING_CLS),"");if(this._path._path){L.DomUtil.removeClass(this._path._path,L.Handler.PathDrag.DRAGGING_CLS)}},moved:function(){return this._path._dragMoved},_onDragStart:function(t){var i=t.originalEvent._simulated?"touchstart":t.originalEvent.type;this._mapDraggingWasEnabled=false;this._startPoint=t.containerPoint.clone();this._dragStartPoint=t.containerPoint.clone();this._matrix=[1,0,0,1,0,0];L.DomEvent.stop(t.originalEvent);L.DomUtil.addClass(this._path._renderer._container,"leaflet-interactive");L.DomEvent.on(document,L.Draggable.MOVE[i],this._onDrag,this).on(document,L.Draggable.END[i],this._onDragEnd,this);if(this._path._map.dragging.enabled()){this._path._map.dragging.disable();this._mapDraggingWasEnabled=true}this._path._dragMoved=false;if(this._path._popup){this._path._popup._close()}this._replaceCoordGetters(t)},_onDrag:function(t){L.DomEvent.stop(t);var i=t.touches&&t.touches.length>=1?t.touches[0]:t;var a=this._path._map.mouseEventToContainerPoint(i);if(t.type==="touchmove"&&!this._path._dragMoved){var n=this._dragStartPoint.distanceTo(a);if(n<=this._path._map.options.tapTolerance){return}}var r=a.x;var s=a.y;var e=r-this._startPoint.x;var o=s-this._startPoint.y;if(e||o){if(!this._path._dragMoved){this._path._dragMoved=true;this._path.fire("dragstart",t);this._path.bringToFront()}this._matrix[4]+=e;this._matrix[5]+=o;this._startPoint.x=r;this._startPoint.y=s;this._path.fire("predrag",t);this._path._transform(this._matrix);this._path.fire("drag",t)}},_onDragEnd:function(t){var i=this._path._map.mouseEventToContainerPoint(t);var a=this.moved();if(a){this._transformPoints(this._matrix);this._path._updatePath();this._path._project();this._path._transform(null);L.DomEvent.stop(t)}L.DomEvent.off(document,"mousemove touchmove",this._onDrag,this).off(document,"mouseup touchend",this._onDragEnd,this);this._restoreCoordGetters();if(a){this._path.fire("dragend",{distance:Math.sqrt(L.LineUtil._sqDist(this._dragStartPoint,i))});var n=this._path._containsPoint;this._path._containsPoint=L.Util.falseFn;L.Util.requestAnimFrame(function(){L.DomEvent._skipped({type:"click"});this._path._containsPoint=n},this)}this._matrix=null;this._startPoint=null;this._dragStartPoint=null;this._path._dragMoved=false;if(this._mapDraggingWasEnabled){if(a)L.DomEvent._fakeStop({type:"click"});this._path._map.dragging.enable()}},_transformPoints:function(t,i){var a=this._path;var n,r,s;var e=L.point(t[4],t[5]);var o=a._map.options.crs;var h=o.transformation;var _=o.scale(a._map.getZoom());var l=o.projection;var p=h.untransform(e,_).subtract(h.untransform(L.point(0,0),_));var d=!i;a._bounds=new L.LatLngBounds;if(a._point){i=l.unproject(l.project(a._latlng)._add(p));if(d){a._latlng=i;a._point._add(e)}}else if(a._rings||a._parts){var g=a._rings||a._parts;var f=a._latlngs;i=i||f;if(!L.Util.isArray(f[0])){f=[f];i=[i]}for(n=0,r=g.length;n=1?t.touches[0]:t;var a=this._path._map.mouseEventToContainerPoint(i);if(t.type==="touchmove"&&!this._path._dragMoved){var n=this._dragStartPoint.distanceTo(a);if(n<=this._path._map.options.tapTolerance){return}}var r=a.x;var s=a.y;var e=r-this._startPoint.x;var o=s-this._startPoint.y;if(e||o){if(!this._path._dragMoved){this._path._dragMoved=true;this._path.options.interactive=false;this._path._map.dragging._draggable._moved=true;this._path.fire("dragstart",t);this._path.bringToFront()}this._matrix[4]+=e;this._matrix[5]+=o;this._startPoint.x=r;this._startPoint.y=s;this._path.fire("predrag",t);this._path._transform(this._matrix);this._path.fire("drag",t)}},_onDragEnd:function(t){var i=this._path._map.mouseEventToContainerPoint(t);var a=this.moved();if(a){this._transformPoints(this._matrix);this._path._updatePath();this._path._project();this._path._transform(null);L.DomEvent.stop(t)}L.DomEvent.off(document,"mousemove touchmove",this._onDrag,this);L.DomEvent.off(document,"mouseup touchend",this._onDragEnd,this);this._restoreCoordGetters();if(a){this._path.fire("dragend",{distance:distance(this._dragStartPoint,i)});var n=this._path._containsPoint;this._path._containsPoint=L.Util.falseFn;L.Util.requestAnimFrame(function(){this._path._dragMoved=false;this._path.options.interactive=true;this._path._containsPoint=n},this)}if(this._mapDraggingWasEnabled){this._path._map.dragging.enable()}},_transformPoints:function(t,i){var a=this._path;var n,r,s;var e=L.point(t[4],t[5]);var o=a._map.options.crs;var h=o.transformation;var _=o.scale(a._map.getZoom());var l=o.projection;var d=h.untransform(e,_).subtract(h.untransform(L.point(0,0),_));var p=!i;a._bounds=new L.LatLngBounds;if(a._point){i=l.unproject(l.project(a._latlng)._add(d));if(p){a._latlng=i;a._point._add(e)}}else if(a._rings||a._parts){var f=a._rings||a._parts;var g=a._latlngs;i=i||g;if(!L.Util.isArray(g[0])){g=[g];i=[i]}for(n=0,r=f.length;n * @license MIT * @preserve - */ -L.LineUtil.pointOnLine=function(t,i,a){var n=1+a/t.distanceTo(i);return new L.Point(t.x+(i.x-t.x)*n,t.y+(i.y-t.y)*n)};L.Util.merge=function(){var t=1;var i,a;var n=arguments[t];function r(t){return Object.prototype.toString.call(t)==="[object Object]"}var s=arguments[0];while(n){n=arguments[t++];for(i in n){if(!n.hasOwnProperty(i)){continue}a=n[i];if(r(a)&&r(s[i])){s[i]=L.Util.merge(s[i],a)}else{s[i]=a}}}return s};L.Matrix=function(t,i,a,n,r,s){this._matrix=[t,i,a,n,r,s]};L.Matrix.prototype={transform:function(t){return this._transform(t.clone())},_transform:function(t){var i=this._matrix;var a=t.x,n=t.y;t.x=i[0]*a+i[1]*n+i[4];t.y=i[2]*a+i[3]*n+i[5];return t},untransform:function(t){var i=this._matrix;return new L.Point((t.x/i[0]-i[4])/i[0],(t.y/i[2]-i[5])/i[2])},clone:function(){var t=this._matrix;return new L.Matrix(t[0],t[1],t[2],t[3],t[4],t[5])},translate:function(t){if(t===undefined){return new L.Point(this._matrix[4],this._matrix[5])}var i,a;if(typeof t==="number"){i=a=t}else{i=t.x;a=t.y}return this._add(1,0,0,1,i,a)},scale:function(t,i){if(t===undefined){return new L.Point(this._matrix[0],this._matrix[3])}var a,n;i=i||L.point(0,0);if(typeof t==="number"){a=n=t}else{a=t.x;n=t.y}return this._add(a,0,0,n,i.x,i.y)._add(1,0,0,1,-i.x,-i.y)},rotate:function(t,i){var a=Math.cos(t);var n=Math.sin(t);i=i||new L.Point(0,0);return this._add(a,n,-n,a,i.x,i.y)._add(1,0,0,1,-i.x,-i.y)},flip:function(){this._matrix[1]*=-1;this._matrix[2]*=-1;return this},_add:function(t,i,a,n,r,s){var e=[[],[],[]];var o=this._matrix;var h=[[o[0],o[2],o[4]],[o[1],o[3],o[5]],[0,0,1]];var _=[[t,a,r],[i,n,s],[0,0,1]],l;if(t&&t instanceof L.Matrix){o=t._matrix;_=[[o[0],o[2],o[4]],[o[1],o[3],o[5]],[0,0,1]]}for(var p=0;p<3;p++){for(var d=0;d<3;d++){l=0;for(var g=0;g<3;g++){l+=h[p][g]*_[g][d]}e[p][d]=l}}this._matrix=[e[0][0],e[1][0],e[0][1],e[1][1],e[0][2],e[1][2]];return this}};L.matrix=function(t,i,a,n,r,s){return new L.Matrix(t,i,a,n,r,s)};L.PathTransform={};L.PathTransform.Handle=L.CircleMarker.extend({options:{className:"leaflet-path-transform-handler"},onAdd:function(t){L.CircleMarker.prototype.onAdd.call(this,t);if(this._path&&this.options.setCursor){this._path.style.cursor=L.PathTransform.Handle.CursorsByType[this.options.index]}}});L.PathTransform.Handle.CursorsByType=["nesw-resize","nwse-resize","nesw-resize","nwse-resize"];L.PathTransform.RotateHandle=L.PathTransform.Handle.extend({options:{className:"leaflet-path-transform-handler transform-handler--rotate"},onAdd:function(t){L.CircleMarker.prototype.onAdd.call(this,t);if(this._path&&this.options.setCursor){this._path.style.cursor="all-scroll"}}});L.Handler.PathTransform=L.Handler.extend({options:{rotation:true,rotationAnchor:null,scaling:true,uniformScaling:true,maxZoom:22,handlerOptions:{radius:5,fillColor:"#ffffff",color:"#202020",fillOpacity:1,weight:2,opacity:.7,setCursor:true},boundsOptions:{weight:1,opacity:1,dashArray:[3,3],fill:false},rotateHandleOptions:{weight:1,opacity:1,setCursor:true},handleLength:20,edgesCount:4,handleClass:L.PathTransform.Handle,rotateHandleClass:L.PathTransform.RotateHandle},initialize:function(t){this._path=t;this._map=null;this._activeMarker=null;this._originMarker=null;this._rotationMarker=null;this._rotationOrigin=null;this._scaleOrigin=null;this._angle=0;this._scale=L.point(1,1);this._initialDist=0;this._initialDistX=0;this._initialDistY=0;this._rotationStart=null;this._rotationOriginPt=null;this._matrix=new L.Matrix(1,0,0,1,0,0);this._projectedMatrix=new L.Matrix(1,0,0,1,0,0);this._handlersGroup=null;this._rect=null;this._handlers=[];this._handleLine=null},enable:function(t){if(this._path._map){this._map=this._path._map;if(t){this.setOptions(t)}L.Handler.prototype.enable.call(this)}},addHooks:function(){this._createHandlers();this._path.on("dragstart",this._onDragStart,this).on("dragend",this._onDragEnd,this)},removeHooks:function(){this._hideHandlers();this._path.off("dragstart",this._onDragStart,this).off("dragend",this._onDragEnd,this);this._handlersGroup=null;this._rect=null;this._handlers=[]},setOptions:function(t){var i=this._enabled;if(i){this.disable()}this.options=L.Util.merge({},L.Handler.PathTransform.prototype.options,t);if(i){this.enable()}return this},rotate:function(t,i){return this.transform(t,null,i)},scale:function(t,i){if(typeof t==="number"){t=L.point(t,t)}return this.transform(0,t,null,i)},transform:function(t,i,a,n){var r=this._path.getCenter();a=a||r;n=n||r;this._map=this._path._map;this._transformPoints(this._path,t,i,a,n);return this},_update:function(){var t=this._matrix;for(var i=0,a=this._handlers.length;i=0;i--){t.removeLayer(this._handlers[i])}this._createHandlers()},_transformGeometries:function(){this._path._transform(null);this._rect._transform(null);if(this.options.rotation){this._handleLine._transform(null);this._transformPoints(this._path,this._angle,null,this.options.rotationAnchor);this._transformPoints(this._rect,this._angle,null,this.options.rotationAnchor);this._transformPoints(this._handleLine,this._angle,null,this.options.rotationAnchor)}else{this._transformPoints(this._path);this._transformPoints(this._rect)}},_getProjectedMatrix:function(t,i,a,n){var r=this._map;var s=r.getMaxZoom()||this.options.maxZoom;var e=L.matrix(1,0,0,1,0,0);var o;t=t||this._angle||0;i=i||this._scale||L.point(1,1);if(!(i.x===1&&i.y===1)){n=n||this._scaleOrigin;o=r.project(n,s);e=e._add(L.matrix(1,0,0,1,o.x,o.y))._add(L.matrix(i.x,0,0,i.y,0,0))._add(L.matrix(1,0,0,1,-o.x,-o.y))}if(t){a=a||this._rotationOrigin;o=r.project(a,s);e=e.rotate(t,o).flip()}return e},_transformPoint:function(t,i,a,n){return a.unproject(i.transform(a.project(t,n)),n)},_transformPoints:function(t,i,a,n,r){var s=t._map;var e=s.getMaxZoom()||this.options.maxZoom;var o,h;var _=this._projectedMatrix=this._getProjectedMatrix(i,a,n,r);if(t._point){t._latlng=this._transformPoint(t._latlng,_,s,e)}else if(t._rings||t._parts){var l=t._rings;var p=t._latlngs;t._bounds=new L.LatLngBounds;if(!L.Util.isArray(p[0])){p=[p]}for(o=0,h=l.length;o=2){return new L.LatLng(t[0],t[1])}else if(t instanceof L.LatLng){return t}}var i=this._rect._latlngs[0];var a=i[0];var n=i[2];return new L.LatLng((a.lat+n.lat)/2,(a.lng+n.lng)/2)},_onRotateStart:function(t){var i=this._map;i.dragging.disable();this._originMarker=null;this._rotationOriginPt=i.latLngToLayerPoint(this._getRotationOrigin());this._rotationStart=t.layerPoint;this._initialMatrix=this._matrix.clone();this._angle=0;this._path._map.on("mousemove",this._onRotate,this).on("mouseup",this._onRotateEnd,this);this._cachePoints();this._path.fire("transformstart",{layer:this._path}).fire("rotatestart",{layer:this._path,rotation:0})},_onRotate:function(t){var i=t.layerPoint;var a=this._rotationStart;var n=this._rotationOriginPt;this._angle=Math.atan2(i.y-n.y,i.x-n.x)-Math.atan2(a.y-n.y,a.x-n.x);this._matrix=this._initialMatrix.clone().rotate(this._angle,n).flip();this._update();this._path.fire("rotate",{layer:this._path,rotation:this._angle})},_onRotateEnd:function(t){this._path._map.off("mousemove",this._onRotate,this).off("mouseup",this._onRotateEnd,this);this._apply();this._path.fire("rotateend",{layer:this._path,rotation:this._angle})},_onScaleStart:function(t){var i=t.target;var a=this._map;a.dragging.disable();this._activeMarker=i;this._originMarker=this._handlers[(i.options.index+2)%4];this._scaleOrigin=this._originMarker.getLatLng();this._initialMatrix=this._matrix.clone();this._cachePoints();this._map.on("mousemove",this._onScale,this).on("mouseup",this._onScaleEnd,this);this._initialDist=this._originMarker._point.distanceTo(this._activeMarker._point);this._initialDistX=this._originMarker._point.x-this._activeMarker._point.x;this._initialDistY=this._originMarker._point.y-this._activeMarker._point.y;this._path.fire("transformstart",{layer:this._path}).fire("scalestart",{layer:this._path,scale:L.point(1,1)});this._map.removeLayer(this._handleLine);this._map.removeLayer(this._rotationMarker)},_onScale:function(t){var i=this._originMarker._point;var a,n;if(this.options.uniformScaling){a=i.distanceTo(t.layerPoint)/this._initialDist;n=a}else{a=(i.x-t.layerPoint.x)/this._initialDistX;n=(i.y-t.layerPoint.y)/this._initialDistY}this._scale=new L.Point(a,n);this._matrix=this._initialMatrix.clone().scale(this._scale,i);this._update();this._path.fire("scale",{layer:this._path,scale:this._scale.clone()})},_onScaleEnd:function(t){this._map.off("mousemove",this._onScale,this).off("mouseup",this._onScaleEnd,this);this._map.addLayer(this._handleLine);this._map.addLayer(this._rotationMarker);this._apply();this._path.fire("scaleend",{layer:this._path,scale:this._scale.clone()})},_cachePoints:function(){this._handlersGroup.eachLayer(function(t){t.bringToFront()});for(var t=0,i=this._handlers.length;t=0;i--){t.removeLayer(this._handlers[i])}this._createHandlers()},_transformGeometries:function(){this._path._transform(null);this._rect._transform(null);if(this.options.rotation){this._handleLine._transform(null);this._transformPoints(this._path,this._angle,null,this.options.rotationAnchor);this._transformPoints(this._rect,this._angle,null,this.options.rotationAnchor);this._transformPoints(this._handleLine,this._angle,null,this.options.rotationAnchor)}else{this._transformPoints(this._path);this._transformPoints(this._rect)}},_getProjectedMatrix:function(t,i,a,n){var r=this._map;var s=r.getMaxZoom()||this.options.maxZoom;var e=L.matrix(1,0,0,1,0,0);var o;t=t||this._angle||0;i=i||this._scale||L.point(1,1);if(!(i.x===1&&i.y===1)){n=n||this._scaleOrigin;o=r.project(n,s);e=e._add(L.matrix(1,0,0,1,o.x,o.y))._add(L.matrix(i.x,0,0,i.y,0,0))._add(L.matrix(1,0,0,1,-o.x,-o.y))}if(t){a=a||this._rotationOrigin;o=r.project(a,s);e=e.rotate(t,o).flip()}return e},_transformPoint:function(t,i,a,n){return a.unproject(i.transform(a.project(t,n)),n)},_transformPoints:function(t,i,a,n,r){var s=t._map;var e=s.getMaxZoom()||this.options.maxZoom;var o,h;var _=this._projectedMatrix=this._getProjectedMatrix(i,a,n,r);if(t._point){t._latlng=this._transformPoint(t._latlng,_,s,e)}else if(t._rings||t._parts){var l=t._rings;var d=t._latlngs;t._bounds=new L.LatLngBounds;if(!L.Util.isArray(d[0])){d=[d]}for(o=0,h=l.length;o=2){return new L.LatLng(t[0],t[1])}else if(t instanceof L.LatLng){return t}}var i=this._rect._latlngs[0];var a=i[0];var n=i[2];return new L.LatLng((a.lat+n.lat)/2,(a.lng+n.lng)/2)},_onRotateStart:function(t){var i=this._map;i.dragging.disable();this._originMarker=null;this._rotationOriginPt=i.latLngToLayerPoint(this._getRotationOrigin());this._rotationStart=t.layerPoint;this._initialMatrix=this._matrix.clone();this._angle=0;this._path._map.on("mousemove",this._onRotate,this).on("mouseup",this._onRotateEnd,this);this._cachePoints();this._path.fire("transformstart",{layer:this._path}).fire("rotatestart",{layer:this._path,rotation:0})},_onRotate:function(t){var i=t.layerPoint;var a=this._rotationStart;var n=this._rotationOriginPt;this._angle=Math.atan2(i.y-n.y,i.x-n.x)-Math.atan2(a.y-n.y,a.x-n.x);this._matrix=this._initialMatrix.clone().rotate(this._angle,n).flip();this._update();this._path.fire("rotate",{layer:this._path,rotation:this._angle})},_onRotateEnd:function(t){this._path._map.off("mousemove",this._onRotate,this).off("mouseup",this._onRotateEnd,this);this._apply();this._path.fire("rotateend",{layer:this._path,rotation:this._angle})},_onScaleStart:function(t){var i=t.target;var a=this._map;a.dragging.disable();this._activeMarker=i;this._originMarker=this._handlers[(i.options.index+2)%4];this._scaleOrigin=this._originMarker.getLatLng();this._initialMatrix=this._matrix.clone();this._cachePoints();this._map.on("mousemove",this._onScale,this).on("mouseup",this._onScaleEnd,this);this._initialDist=this._originMarker._point.distanceTo(this._activeMarker._point);this._initialDistX=this._originMarker._point.x-this._activeMarker._point.x;this._initialDistY=this._originMarker._point.y-this._activeMarker._point.y;this._path.fire("transformstart",{layer:this._path}).fire("scalestart",{layer:this._path,scale:L.point(1,1)});this._map.removeLayer(this._handleLine);this._map.removeLayer(this._rotationMarker)},_onScale:function(t){var i=this._originMarker._point;var a,n;if(this.options.uniformScaling){a=i.distanceTo(t.layerPoint)/this._initialDist;n=a}else{a=(i.x-t.layerPoint.x)/this._initialDistX;n=(i.y-t.layerPoint.y)/this._initialDistY}this._scale=new L.Point(a,n);this._matrix=this._initialMatrix.clone().scale(this._scale,i);this._update();this._path.fire("scale",{layer:this._path,scale:this._scale.clone()})},_onScaleEnd:function(t){this._map.off("mousemove",this._onScale,this).off("mouseup",this._onScaleEnd,this);this._map.addLayer(this._handleLine);this._map.addLayer(this._rotationMarker);this._apply();this._path.fire("scaleend",{layer:this._path,scale:this._scale.clone()})},_cachePoints:function(){this._handlersGroup.eachLayer(function(t){t.bringToFront()});for(var t=0,i=this._handlers.length;tOSM contributors' -}).addTo(map); - -//////////////////////////////////////////////////////////////////////////////// -function interpolateArr(array, insert) { - var res = []; - array.forEach(function(p, i, arr) { - res.push(p.concat()); - - if (i < arr.length - 1) { - var diff = [arr[i + 1][0] - p[0], arr[i + 1][1] - p[1]]; - for (var i = 1; i < insert; i++) { - res.push([p[0] + (diff[0] * i) / insert, p[1] + (diff[1] * i) / insert]); - } - } - }); - - return res; -} - -//////////////////////////////////////////////////////////////////////////////// -var polygon = global.polygon = new L.Polygon( - L.GeoJSON.coordsToLatLngs( - - // ~ 13 000 points - interpolateArr([ - [113.97697448730469, 22.403410892712124], - [113.98658752441405, 22.38373008592495], - [114.01268005371094, 22.369126397545887], - [114.02778625488281, 22.38563480185718], - [114.04701232910156, 22.395157990290755], - [114.06005859375, 22.413567638369805], - [114.06280517578125, 22.432609534876796], - [114.04838562011717, 22.444668051657157], - [114.04289245605469, 22.44847578656544], - [114.03259277343749, 22.444668051657157], - [114.01954650878906, 22.447206553211814], - [113.99620056152344, 22.436417600763114], - [113.98178100585938, 22.420549970290875], - [113.97697448730469, 22.403410892712124] - ], 1000) - ), { - color: '#f00', - interactive: true, - draggable: true, - transform: true - }).addTo(map); -//polygon.transform.enable(); - -var polyline = global.polyline = new L.Polyline( - L.GeoJSON.coordsToLatLngs([ - [114.14314270019531, 22.49479484975443], - [114.1534423828125, 22.485912942320958], - [114.15206909179688, 22.4732235144781], - [114.14932250976561, 22.459898363943893], - [114.15962219238281, 22.447206553211814], - [114.169921875, 22.447206553211814], - [114.19395446777344, 22.459898363943893], - [114.20631408691406, 22.46116748110935], - [114.21180725097655, 22.473858013487614], - [114.22416687011719, 22.471320000009992], - [114.23721313476562, 22.476395980457973], - [114.24201965332031, 22.49352604073722], - [114.2303466796875, 22.51572851830351], - [114.21798706054688, 22.524608511026262], - [114.20768737792969, 22.524608511026262], - [114.20768737792969, 22.536024805886974] - ]), { - weight: 15, - draggable: true, - transform: true - }) - .bindPopup("L.Polyline") - .addTo(map); -// polyline.transform.enable(); - - -var polygonWithHole = global.polygonWithHole = new L.Polygon( - [ - L.GeoJSON.coordsToLatLngs([ - [114.2749786376953, 22.412932863517717], - [114.28390502929688, 22.40087159030595], - [114.29008483886717, 22.38880927045556], - [114.30107116699219, 22.382460260815716], - [114.31892395019531, 22.391983666602783], - [114.32304382324219, 22.380555501421533], - [114.34295654296875, 22.372936203113838], - [114.334716796875, 22.384364994133303], - [114.33059692382812, 22.393888269511194], - [114.32167053222655, 22.40087159030595], - [114.32785034179688, 22.413567638369805], - [114.33197021484375, 22.42499308964722], - [114.32579040527344, 22.430705462748918], - [114.33197021484375, 22.43959090917266], - [114.33746337890624, 22.449110398886106], - [114.33540344238281, 22.461802035333992], - [114.32510375976562, 22.464340223177118], - [114.32922363281249, 22.472589012561954], - [114.32373046875, 22.477030464933307], - [114.31961059570312, 22.478933900916928], - [114.3017578125, 22.466243833549445], - [114.30244445800781, 22.457360094750083], - [114.29283142089844, 22.454821779075832], - [114.28390502929688, 22.45101421842269], - [114.2749786376953, 22.442764145001707], - [114.29077148437499, 22.428166659279615], - [114.27703857421875, 22.420549970290875], - [114.2749786376953, 22.412932863517717] - ]), - L.GeoJSON.coordsToLatLngs([ - [114.30107116699219, 22.43387890178297], - [114.29351806640625, 22.414202410321302], - [114.30587768554686, 22.408489358342635], - [114.32235717773438, 22.421184710331858], - [114.30107116699219, 22.43387890178297] - ]) - ], { - draggable: true, - transform: true - } - ) - .addTo(map); -//polygonWithHole.transform.enable(); - -var multiPolygon = global.multiPolygon = new L.Polygon([ - L.GeoJSON.coordsToLatLngs([ - [114.20562744140625, 22.32085984100593], - [114.21592712402344, 22.35261603551215], - [114.26467895507812, 22.351345926606957], - [114.2749786376953, 22.32403578584038], - [114.29214477539062, 22.32721165838893], - [114.3017578125, 22.311966810977616], - [114.29420471191406, 22.291002427735325], - [114.29351806640625, 22.272576585413475], - [114.28390502929688, 22.26177410097435], - [114.268798828125, 22.281472122783818], - [114.2749786376953, 22.294814367780518], - [114.26948547363281, 22.30243793590448], - [114.27017211914062, 22.31514295816939], - [114.2578125, 22.311966810977616], - [114.24751281738281, 22.299896792751927], - [114.24545288085938, 22.291002427735325], - [114.22966003417969, 22.307520083522476], - [114.22073364257812, 22.305614299837046], - [114.20562744140625, 22.32085984100593] - ]), - L.GeoJSON.coordsToLatLngs([ - [114.31549072265625, 22.33927931468312], - [114.32029724121094, 22.326576489662482], - [114.32991027832031, 22.326576489662482], - [114.33334350585938, 22.332292904091716], - [114.32304382324219, 22.3424548401465], - [114.31549072265625, 22.33927931468312] - ]), - L.GeoJSON.coordsToLatLngs([ - [114.27909851074219, 22.244615500323064], - [114.28115844726562, 22.251606295132948], - [114.28665161132812, 22.255419308858556], - [114.29969787597656, 22.26113863474449], - [114.2962646484375, 22.250970782750866], - [114.29489135742188, 22.24080219246335], - [114.29008483886717, 22.238895499613232], - [114.27909851074219, 22.244615500323064] - ]) -], { - draggable: true, - transform: true, - color: '#092' -}).bindPopup('MultiPolygon').addTo(map); -//multiPolygon.transform.enable(); - -var multiPolyline = global.multiPolyline = new L.Polyline([ - L.GeoJSON.coordsToLatLngs([ - [113.89869689941406, 22.399601921706953], - [113.89801025390625, 22.422454181709707], - [113.90350341796875, 22.43324421978117], - [113.90968322753906, 22.449110398886106], - [113.90693664550781, 22.478299425162852], - [113.9234161376953, 22.488450688325408], - [113.9337158203125, 22.483375149789623], - [113.9447021484375, 22.492257220085193], - [113.95225524902344, 22.51255695405145] - ]), - - L.GeoJSON.coordsToLatLngs([ - [113.8677978515625, 22.39261853713738], - [113.86917114257811, 22.42753195115699], - [113.9234161376953, 22.462436586653148], - [113.94813537597656, 22.473858013487614], - [113.9783477783203, 22.49923558968306], - [113.99688720703125, 22.51192263246886], - [114.01336669921875, 22.501138720300254], - [114.02503967285155, 22.508116641853675] - ]) -], { - draggable: true, - transform: true, - color: '#e90' -}).bindPopup('MultiPolyline').addTo(map); -// multiPolyline.transform.enable(); - -var layers = [polygon, polyline, multiPolyline, multiPolygon, polygonWithHole]; - -function update() { - L.Util.requestAnimFrame(function() { - var dragging = document.querySelector('#dragging').checked; - var scaling = document.querySelector('#scaling').checked; - var rotation = document.querySelector('#rotation').checked; - var uniform = document.querySelector('#uniform').checked; - - layers.forEach(function(layer) { - - if (layer.dragging) { - layer.dragging[dragging ? 'enable': 'disable'](); - } else { - layer.eachLayer(function(sublayer) { - sublayer.dragging[dragging ? 'enable': 'disable'](); - }); - } - - - layer.transform.setOptions({ - scaling: scaling, - rotation: rotation, - uniformScaling: uniform - }).enable(); - }); - }); -} - -[].slice.call(document.querySelectorAll('input[type=checkbox]')) -.forEach(function(checkbox) { - L.DomEvent.on(checkbox, 'change', update); -}); - -update(); +var L = require('leaflet'); +var DragHandler = require('../../index'); + +L.Icon.Default.imagePath = "http://cdn.leafletjs.com/leaflet-0.7/images"; + +//////////////////////////////////////////////////////////////////////////////// +var map = global.map = new L.Map('map', { + // crs: L.CRS.EPSG4326 // that was tested as well +}).setView([22.42658, 114.1952], 11); + +L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + attribution: '© ' + + 'OSM contributors' +}).addTo(map); + +//////////////////////////////////////////////////////////////////////////////// +function interpolateArr(array, insert) { + var res = []; + array.forEach(function(p, i, arr) { + res.push(p.concat()); + + if (i < arr.length - 1) { + var diff = [arr[i + 1][0] - p[0], arr[i + 1][1] - p[1]]; + for (var i = 1; i < insert; i++) { + res.push([p[0] + (diff[0] * i) / insert, p[1] + (diff[1] * i) / insert]); + } + } + }); + + return res; +} + +//////////////////////////////////////////////////////////////////////////////// +var polygon = global.polygon = new L.Polygon( + L.GeoJSON.coordsToLatLngs( + + // ~ 13 000 points + interpolateArr([ + [113.97697448730469, 22.403410892712124], + [113.98658752441405, 22.38373008592495], + [114.01268005371094, 22.369126397545887], + [114.02778625488281, 22.38563480185718], + [114.04701232910156, 22.395157990290755], + [114.06005859375, 22.413567638369805], + [114.06280517578125, 22.432609534876796], + [114.04838562011717, 22.444668051657157], + [114.04289245605469, 22.44847578656544], + [114.03259277343749, 22.444668051657157], + [114.01954650878906, 22.447206553211814], + [113.99620056152344, 22.436417600763114], + [113.98178100585938, 22.420549970290875], + [113.97697448730469, 22.403410892712124] + ], 1000) + ), { + color: '#f00', + interactive: true, + draggable: true, + transform: true + }).addTo(map); +//polygon.transform.enable(); + +var polyline = global.polyline = new L.Polyline( + L.GeoJSON.coordsToLatLngs([ + [114.14314270019531, 22.49479484975443], + [114.1534423828125, 22.485912942320958], + [114.15206909179688, 22.4732235144781], + [114.14932250976561, 22.459898363943893], + [114.15962219238281, 22.447206553211814], + [114.169921875, 22.447206553211814], + [114.19395446777344, 22.459898363943893], + [114.20631408691406, 22.46116748110935], + [114.21180725097655, 22.473858013487614], + [114.22416687011719, 22.471320000009992], + [114.23721313476562, 22.476395980457973], + [114.24201965332031, 22.49352604073722], + [114.2303466796875, 22.51572851830351], + [114.21798706054688, 22.524608511026262], + [114.20768737792969, 22.524608511026262], + [114.20768737792969, 22.536024805886974] + ]), { + weight: 15, + draggable: true, + transform: true + }) + .bindPopup("L.Polyline") + .addTo(map); +// polyline.transform.enable(); + +var rectangle = global.rectangle = new L.Rectangle(L.latLngBounds([ + [22.334833457530486, 114.0154266357422], + [22.244615500323064, 114.14108276367189] +]), { + weight: 2, + draggable: true, + transform: true + }) + .bindPopup("L.Rectangle") + .addTo(map); + +var polygonWithHole = global.polygonWithHole = new L.Polygon( + [ + L.GeoJSON.coordsToLatLngs([ + [114.2749786376953, 22.412932863517717], + [114.28390502929688, 22.40087159030595], + [114.29008483886717, 22.38880927045556], + [114.30107116699219, 22.382460260815716], + [114.31892395019531, 22.391983666602783], + [114.32304382324219, 22.380555501421533], + [114.34295654296875, 22.372936203113838], + [114.334716796875, 22.384364994133303], + [114.33059692382812, 22.393888269511194], + [114.32167053222655, 22.40087159030595], + [114.32785034179688, 22.413567638369805], + [114.33197021484375, 22.42499308964722], + [114.32579040527344, 22.430705462748918], + [114.33197021484375, 22.43959090917266], + [114.33746337890624, 22.449110398886106], + [114.33540344238281, 22.461802035333992], + [114.32510375976562, 22.464340223177118], + [114.32922363281249, 22.472589012561954], + [114.32373046875, 22.477030464933307], + [114.31961059570312, 22.478933900916928], + [114.3017578125, 22.466243833549445], + [114.30244445800781, 22.457360094750083], + [114.29283142089844, 22.454821779075832], + [114.28390502929688, 22.45101421842269], + [114.2749786376953, 22.442764145001707], + [114.29077148437499, 22.428166659279615], + [114.27703857421875, 22.420549970290875], + [114.2749786376953, 22.412932863517717] + ]), + L.GeoJSON.coordsToLatLngs([ + [114.30107116699219, 22.43387890178297], + [114.29351806640625, 22.414202410321302], + [114.30587768554686, 22.408489358342635], + [114.32235717773438, 22.421184710331858], + [114.30107116699219, 22.43387890178297] + ]) + ], { + draggable: true, + transform: true + } + ) + .addTo(map); +//polygonWithHole.transform.enable(); + +var multiPolygon = global.multiPolygon = new L.Polygon([ + L.GeoJSON.coordsToLatLngs([ + [114.20562744140625, 22.32085984100593], + [114.21592712402344, 22.35261603551215], + [114.26467895507812, 22.351345926606957], + [114.2749786376953, 22.32403578584038], + [114.29214477539062, 22.32721165838893], + [114.3017578125, 22.311966810977616], + [114.29420471191406, 22.291002427735325], + [114.29351806640625, 22.272576585413475], + [114.28390502929688, 22.26177410097435], + [114.268798828125, 22.281472122783818], + [114.2749786376953, 22.294814367780518], + [114.26948547363281, 22.30243793590448], + [114.27017211914062, 22.31514295816939], + [114.2578125, 22.311966810977616], + [114.24751281738281, 22.299896792751927], + [114.24545288085938, 22.291002427735325], + [114.22966003417969, 22.307520083522476], + [114.22073364257812, 22.305614299837046], + [114.20562744140625, 22.32085984100593] + ]), + L.GeoJSON.coordsToLatLngs([ + [114.31549072265625, 22.33927931468312], + [114.32029724121094, 22.326576489662482], + [114.32991027832031, 22.326576489662482], + [114.33334350585938, 22.332292904091716], + [114.32304382324219, 22.3424548401465], + [114.31549072265625, 22.33927931468312] + ]), + L.GeoJSON.coordsToLatLngs([ + [114.27909851074219, 22.244615500323064], + [114.28115844726562, 22.251606295132948], + [114.28665161132812, 22.255419308858556], + [114.29969787597656, 22.26113863474449], + [114.2962646484375, 22.250970782750866], + [114.29489135742188, 22.24080219246335], + [114.29008483886717, 22.238895499613232], + [114.27909851074219, 22.244615500323064] + ]) +], { + draggable: true, + transform: true, + color: '#092' +}).bindPopup('MultiPolygon').addTo(map); +//multiPolygon.transform.enable(); + +var multiPolyline = global.multiPolyline = new L.Polyline([ + L.GeoJSON.coordsToLatLngs([ + [113.89869689941406, 22.399601921706953], + [113.89801025390625, 22.422454181709707], + [113.90350341796875, 22.43324421978117], + [113.90968322753906, 22.449110398886106], + [113.90693664550781, 22.478299425162852], + [113.9234161376953, 22.488450688325408], + [113.9337158203125, 22.483375149789623], + [113.9447021484375, 22.492257220085193], + [113.95225524902344, 22.51255695405145] + ]), + + L.GeoJSON.coordsToLatLngs([ + [113.8677978515625, 22.39261853713738], + [113.86917114257811, 22.42753195115699], + [113.9234161376953, 22.462436586653148], + [113.94813537597656, 22.473858013487614], + [113.9783477783203, 22.49923558968306], + [113.99688720703125, 22.51192263246886], + [114.01336669921875, 22.501138720300254], + [114.02503967285155, 22.508116641853675] + ]) +], { + draggable: true, + transform: true, + color: '#e90' +}).bindPopup('MultiPolyline').addTo(map); +// multiPolyline.transform.enable(); + +var layers = [polygon, polyline, rectangle, multiPolyline, multiPolygon, polygonWithHole]; + +function update() { + L.Util.requestAnimFrame(function() { + var dragging = document.querySelector('#dragging').checked; + var scaling = document.querySelector('#scaling').checked; + var rotation = document.querySelector('#rotation').checked; + var uniform = document.querySelector('#uniform').checked; + + layers.forEach(function(layer) { + + if (layer.dragging) { + layer.dragging[dragging ? 'enable': 'disable'](); + } else { + layer.eachLayer(function(sublayer) { + sublayer.dragging[dragging ? 'enable': 'disable'](); + }); + } + + + layer.transform.setOptions({ + scaling: scaling, + rotation: rotation, + uniformScaling: uniform + }).enable(); + }); + }); +} + +[].slice.call(document.querySelectorAll('input[type=checkbox]')) +.forEach(function(checkbox) { + L.DomEvent.on(checkbox, 'change', update); +}); + +update(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"../../index":2,"leaflet":9}],2:[function(require,module,exports){ -var L = require('leaflet'); -require('leaflet-path-drag'); -require('./src/Util'); -require('./src/Matrix'); -require('./src/Path.Transform'); +var L = require('leaflet'); +require('leaflet-path-drag'); +require('./src/Util'); +require('./src/Matrix'); +require('./src/Path.Transform'); },{"./src/Matrix":10,"./src/Path.Transform":11,"./src/Util":12,"leaflet":9,"leaflet-path-drag":3}],3:[function(require,module,exports){ require('./src/SVG'); @@ -268,9 +278,7 @@ require('./src/Path.Drag'); module.exports = L.Path.Drag; },{"./src/Canvas":4,"./src/Path.Drag":5,"./src/Path.Transform":6,"./src/SVG":8,"./src/SVG.VML":7}],4:[function(require,module,exports){ -L.Util.trueFn = function() { - return true; -}; +function TRUE_FN () { return true; } L.Canvas.include({ @@ -332,7 +340,7 @@ L.Canvas.include({ // avoid flickering because of the 'mouseover's layer._containsPoint_ = layer._containsPoint; - layer._containsPoint = L.Util.trueFn; + layer._containsPoint = TRUE_FN; } ctx.save(); @@ -355,6 +363,25 @@ L.Canvas.include({ }); },{}],5:[function(require,module,exports){ +var END = { + mousedown: 'mouseup', + touchstart: 'touchend', + pointerdown: 'touchend', + MSPointerDown: 'touchend' +}; + +var MOVE = { + mousedown: 'mousemove', + touchstart: 'touchmove', + pointerdown: 'touchmove', + MSPointerDown: 'touchmove' +}; + +function distance(a, b) { + var dx = a.x - b.x, dy = a.y - b.y; + return Math.sqrt(dx * dx + dy * dy); +} + /** * Drag handler * @class L.Path.Drag @@ -450,8 +477,8 @@ L.Handler.PathDrag = L.Handler.extend( /** @lends L.Path.Drag.prototype */ { L.DomUtil.addClass(this._path._renderer._container, 'leaflet-interactive'); L.DomEvent - .on(document, L.Draggable.MOVE[eventType], this._onDrag, this) - .on(document, L.Draggable.END[eventType], this._onDragEnd, this); + .on(document, MOVE[eventType], this._onDrag, this) + .on(document, END[eventType], this._onDragEnd, this); if (this._path._map.dragging.enabled()) { // I guess it's required because mousdown gets simulated with a delay @@ -533,25 +560,22 @@ L.Handler.PathDrag = L.Handler.extend( /** @lends L.Path.Drag.prototype */ { } - L.DomEvent - .off(document, 'mousemove touchmove', this._onDrag, this) - .off(document, 'mouseup touchend', this._onDragEnd, this); + L.DomEvent.off(document, 'mousemove touchmove', this._onDrag, this); + L.DomEvent.off(document, 'mouseup touchend', this._onDragEnd, this); this._restoreCoordGetters(); // consistency if (moved) { this._path.fire('dragend', { - distance: Math.sqrt( - L.LineUtil._sqDist(this._dragStartPoint, containerPoint) - ) + distance: distance(this._dragStartPoint, containerPoint) }); // hack for skipping the click in canvas-rendered layers var contains = this._path._containsPoint; this._path._containsPoint = L.Util.falseFn; L.Util.requestAnimFrame(function() { - L.DomEvent._skipped({ type: 'click' }); + L.DomEvent.skipped({ type: 'click' }); this._path._containsPoint = contains; }, this); } @@ -562,7 +586,7 @@ L.Handler.PathDrag = L.Handler.extend( /** @lends L.Path.Drag.prototype */ { this._path._dragMoved = false; if (this._mapDraggingWasEnabled) { - if (moved) L.DomEvent._fakeStop({ type: 'click' }); + if (moved) L.DomEvent.fakeStop({ type: 'click' }); this._path._map.dragging.enable(); } }, @@ -830,40 +854,16 @@ L.SVG.include({ },{}],9:[function(require,module,exports){ /* - Leaflet 1.0.3, a JS library for interactive maps. http://leafletjs.com - (c) 2010-2016 Vladimir Agafonkin, (c) 2010-2011 CloudMade -*/ -(function (window, document, undefined) { -var L = { - version: "1.0.3" -}; - -function expose() { - var oldL = window.L; - - L.noConflict = function () { - window.L = oldL; - return this; - }; - - window.L = L; -} - -// define Leaflet for Node module pattern loaders, including Browserify -if (typeof module === 'object' && typeof module.exports === 'object') { - module.exports = L; - -// define Leaflet as an AMD module -} else if (typeof define === 'function' && define.amd) { - define(L); -} - -// define Leaflet as a global L variable, saving the original L to restore later if needed -if (typeof window !== 'undefined') { - expose(); -} - + * Leaflet 1.1.0, a JS library for interactive maps. http://leafletjs.com + * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.L = global.L || {}))); +}(this, (function (exports) { 'use strict'; +var version = "1.1.0"; /* * @namespace Util @@ -871,254 +871,264 @@ if (typeof window !== 'undefined') { * Various utility functions, used by Leaflet internally. */ -L.Util = { - - // @function extend(dest: Object, src?: Object): Object - // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. - extend: function (dest) { - var i, j, len, src; +// @function extend(dest: Object, src?: Object): Object +// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. +function extend(dest) { + var i, j, len, src; - for (j = 1, len = arguments.length; j < len; j++) { - src = arguments[j]; - for (i in src) { - dest[i] = src[i]; - } + for (j = 1, len = arguments.length; j < len; j++) { + src = arguments[j]; + for (i in src) { + dest[i] = src[i]; } - return dest; - }, - - // @function create(proto: Object, properties?: Object): Object - // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) - create: Object.create || (function () { - function F() {} - return function (proto) { - F.prototype = proto; - return new F(); - }; - })(), + } + return dest; +} - // @function bind(fn: Function, …): Function - // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). - // Has a `L.bind()` shortcut. - bind: function (fn, obj) { - var slice = Array.prototype.slice; +// @function create(proto: Object, properties?: Object): Object +// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) +var create = Object.create || (function () { + function F() {} + return function (proto) { + F.prototype = proto; + return new F(); + }; +})(); - if (fn.bind) { - return fn.bind.apply(fn, slice.call(arguments, 1)); - } +// @function bind(fn: Function, …): Function +// Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). +// Has a `L.bind()` shortcut. +function bind(fn, obj) { + var slice = Array.prototype.slice; - var args = slice.call(arguments, 2); + if (fn.bind) { + return fn.bind.apply(fn, slice.call(arguments, 1)); + } - return function () { - return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); - }; - }, + var args = slice.call(arguments, 2); - // @function stamp(obj: Object): Number - // Returns the unique ID of an object, assiging it one if it doesn't have it. - stamp: function (obj) { - /*eslint-disable */ - obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId; - return obj._leaflet_id; - /*eslint-enable */ - }, - - // @property lastId: Number - // Last unique ID used by [`stamp()`](#util-stamp) - lastId: 0, - - // @function throttle(fn: Function, time: Number, context: Object): Function - // Returns a function which executes function `fn` with the given scope `context` - // (so that the `this` keyword refers to `context` inside `fn`'s code). The function - // `fn` will be called no more than one time per given amount of `time`. The arguments - // received by the bound function will be any arguments passed when binding the - // function, followed by any arguments passed when invoking the bound function. - // Has an `L.bind` shortcut. - throttle: function (fn, time, context) { - var lock, args, wrapperFn, later; - - later = function () { - // reset lock and call if queued - lock = false; - if (args) { - wrapperFn.apply(context, args); - args = false; - } - }; + return function () { + return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); + }; +} - wrapperFn = function () { - if (lock) { - // called too soon, queue to call later - args = arguments; +// @property lastId: Number +// Last unique ID used by [`stamp()`](#util-stamp) +var lastId = 0; + +// @function stamp(obj: Object): Number +// Returns the unique ID of an object, assiging it one if it doesn't have it. +function stamp(obj) { + /*eslint-disable */ + obj._leaflet_id = obj._leaflet_id || ++lastId; + return obj._leaflet_id; + /*eslint-enable */ +} - } else { - // call and lock until later - fn.apply(context, arguments); - setTimeout(later, time); - lock = true; - } - }; +// @function throttle(fn: Function, time: Number, context: Object): Function +// Returns a function which executes function `fn` with the given scope `context` +// (so that the `this` keyword refers to `context` inside `fn`'s code). The function +// `fn` will be called no more than one time per given amount of `time`. The arguments +// received by the bound function will be any arguments passed when binding the +// function, followed by any arguments passed when invoking the bound function. +// Has an `L.throttle` shortcut. +function throttle(fn, time, context) { + var lock, args, wrapperFn, later; + + later = function () { + // reset lock and call if queued + lock = false; + if (args) { + wrapperFn.apply(context, args); + args = false; + } + }; - return wrapperFn; - }, + wrapperFn = function () { + if (lock) { + // called too soon, queue to call later + args = arguments; - // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number - // Returns the number `num` modulo `range` in such a way so it lies within - // `range[0]` and `range[1]`. The returned value will be always smaller than - // `range[1]` unless `includeMax` is set to `true`. - wrapNum: function (x, range, includeMax) { - var max = range[1], - min = range[0], - d = max - min; - return x === max && includeMax ? x : ((x - min) % d + d) % d + min; - }, + } else { + // call and lock until later + fn.apply(context, arguments); + setTimeout(later, time); + lock = true; + } + }; - // @function falseFn(): Function - // Returns a function which always returns `false`. - falseFn: function () { return false; }, + return wrapperFn; +} - // @function formatNum(num: Number, digits?: Number): Number - // Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default. - formatNum: function (num, digits) { - var pow = Math.pow(10, digits || 5); - return Math.round(num * pow) / pow; - }, +// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number +// Returns the number `num` modulo `range` in such a way so it lies within +// `range[0]` and `range[1]`. The returned value will be always smaller than +// `range[1]` unless `includeMax` is set to `true`. +function wrapNum(x, range, includeMax) { + var max = range[1], + min = range[0], + d = max - min; + return x === max && includeMax ? x : ((x - min) % d + d) % d + min; +} - // @function trim(str: String): String - // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) - trim: function (str) { - return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); - }, +// @function falseFn(): Function +// Returns a function which always returns `false`. +function falseFn() { return false; } - // @function splitWords(str: String): String[] - // Trims and splits the string on whitespace and returns the array of parts. - splitWords: function (str) { - return L.Util.trim(str).split(/\s+/); - }, +// @function formatNum(num: Number, digits?: Number): Number +// Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default. +function formatNum(num, digits) { + var pow = Math.pow(10, digits || 5); + return Math.round(num * pow) / pow; +} - // @function setOptions(obj: Object, options: Object): Object - // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. - setOptions: function (obj, options) { - if (!obj.hasOwnProperty('options')) { - obj.options = obj.options ? L.Util.create(obj.options) : {}; - } - for (var i in options) { - obj.options[i] = options[i]; - } - return obj.options; - }, +// @function trim(str: String): String +// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) +function trim(str) { + return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); +} - // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String - // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` - // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will - // be appended at the end. If `uppercase` is `true`, the parameter names will - // be uppercased (e.g. `'?A=foo&B=bar'`) - getParamString: function (obj, existingUrl, uppercase) { - var params = []; - for (var i in obj) { - params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); - } - return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); - }, +// @function splitWords(str: String): String[] +// Trims and splits the string on whitespace and returns the array of parts. +function splitWords(str) { + return trim(str).split(/\s+/); +} - // @function template(str: String, data: Object): String - // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` - // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string - // `('Hello foo, bar')`. You can also specify functions instead of strings for - // data values — they will be evaluated passing `data` as an argument. - template: function (str, data) { - return str.replace(L.Util.templateRe, function (str, key) { - var value = data[key]; +// @function setOptions(obj: Object, options: Object): Object +// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. +function setOptions(obj, options) { + if (!obj.hasOwnProperty('options')) { + obj.options = obj.options ? create(obj.options) : {}; + } + for (var i in options) { + obj.options[i] = options[i]; + } + return obj.options; +} - if (value === undefined) { - throw new Error('No value provided for variable ' + str); +// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String +// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` +// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will +// be appended at the end. If `uppercase` is `true`, the parameter names will +// be uppercased (e.g. `'?A=foo&B=bar'`) +function getParamString(obj, existingUrl, uppercase) { + var params = []; + for (var i in obj) { + params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); + } + return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); +} - } else if (typeof value === 'function') { - value = value(data); - } - return value; - }); - }, +var templateRe = /\{ *([\w_\-]+) *\}/g; - templateRe: /\{ *([\w_\-]+) *\}/g, +// @function template(str: String, data: Object): String +// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` +// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string +// `('Hello foo, bar')`. You can also specify functions instead of strings for +// data values — they will be evaluated passing `data` as an argument. +function template(str, data) { + return str.replace(templateRe, function (str, key) { + var value = data[key]; - // @function isArray(obj): Boolean - // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) - isArray: Array.isArray || function (obj) { - return (Object.prototype.toString.call(obj) === '[object Array]'); - }, + if (value === undefined) { + throw new Error('No value provided for variable ' + str); - // @function indexOf(array: Array, el: Object): Number - // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) - indexOf: function (array, el) { - for (var i = 0; i < array.length; i++) { - if (array[i] === el) { return i; } + } else if (typeof value === 'function') { + value = value(data); } - return -1; - }, + return value; + }); +} - // @property emptyImageUrl: String - // Data URI string containing a base64-encoded empty GIF image. - // Used as a hack to free memory from unused images on WebKit-powered - // mobile devices (by setting image `src` to this string). - emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' +// @function isArray(obj): Boolean +// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) +var isArray = Array.isArray || function (obj) { + return (Object.prototype.toString.call(obj) === '[object Array]'); }; -(function () { - // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ - - function getPrefixed(name) { - return window['webkit' + name] || window['moz' + name] || window['ms' + name]; +// @function indexOf(array: Array, el: Object): Number +// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) +function indexOf(array, el) { + for (var i = 0; i < array.length; i++) { + if (array[i] === el) { return i; } } + return -1; +} - var lastTime = 0; +// @property emptyImageUrl: String +// Data URI string containing a base64-encoded empty GIF image. +// Used as a hack to free memory from unused images on WebKit-powered +// mobile devices (by setting image `src` to this string). +var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; - // fallback for IE 7-8 - function timeoutDefer(fn) { - var time = +new Date(), - timeToCall = Math.max(0, 16 - (time - lastTime)); +// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ - lastTime = time + timeToCall; - return window.setTimeout(fn, timeToCall); - } +function getPrefixed(name) { + return window['webkit' + name] || window['moz' + name] || window['ms' + name]; +} - var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer, - cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || - getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; +var lastTime = 0; +// fallback for IE 7-8 +function timeoutDefer(fn) { + var time = +new Date(), + timeToCall = Math.max(0, 16 - (time - lastTime)); - // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number - // Schedules `fn` to be executed when the browser repaints. `fn` is bound to - // `context` if given. When `immediate` is set, `fn` is called immediately if - // the browser doesn't have native support for - // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), - // otherwise it's delayed. Returns a request ID that can be used to cancel the request. - L.Util.requestAnimFrame = function (fn, context, immediate) { - if (immediate && requestFn === timeoutDefer) { - fn.call(context); - } else { - return requestFn.call(window, L.bind(fn, context)); - } - }; + lastTime = time + timeToCall; + return window.setTimeout(fn, timeToCall); +} - // @function cancelAnimFrame(id: Number): undefined - // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). - L.Util.cancelAnimFrame = function (id) { - if (id) { - cancelFn.call(window, id); - } - }; -})(); +var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer; +var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || + getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; + +// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number +// Schedules `fn` to be executed when the browser repaints. `fn` is bound to +// `context` if given. When `immediate` is set, `fn` is called immediately if +// the browser doesn't have native support for +// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), +// otherwise it's delayed. Returns a request ID that can be used to cancel the request. +function requestAnimFrame(fn, context, immediate) { + if (immediate && requestFn === timeoutDefer) { + fn.call(context); + } else { + return requestFn.call(window, bind(fn, context)); + } +} -// shortcuts for most used utility functions -L.extend = L.Util.extend; -L.bind = L.Util.bind; -L.stamp = L.Util.stamp; -L.setOptions = L.Util.setOptions; +// @function cancelAnimFrame(id: Number): undefined +// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). +function cancelAnimFrame(id) { + if (id) { + cancelFn.call(window, id); + } +} +var Util = (Object.freeze || Object)({ + extend: extend, + create: create, + bind: bind, + lastId: lastId, + stamp: stamp, + throttle: throttle, + wrapNum: wrapNum, + falseFn: falseFn, + formatNum: formatNum, + trim: trim, + splitWords: splitWords, + setOptions: setOptions, + getParamString: getParamString, + template: template, + isArray: isArray, + indexOf: indexOf, + emptyImageUrl: emptyImageUrl, + requestFn: requestFn, + cancelFn: cancelFn, + requestAnimFrame: requestAnimFrame, + cancelAnimFrame: cancelAnimFrame +}); - // @class Class // @aka L.Class @@ -1127,9 +1137,9 @@ L.setOptions = L.Util.setOptions; // Thanks to John Resig and Dean Edwards for inspiration! -L.Class = function () {}; +function Class() {} -L.Class.extend = function (props) { +Class.extend = function (props) { // @function extend(props: Object): Function // [Extends the current class](#class-inheritance) given the properties to be included. @@ -1147,37 +1157,38 @@ L.Class.extend = function (props) { var parentProto = NewClass.__super__ = this.prototype; - var proto = L.Util.create(parentProto); + var proto = create(parentProto); proto.constructor = NewClass; NewClass.prototype = proto; // inherit parent's statics for (var i in this) { - if (this.hasOwnProperty(i) && i !== 'prototype') { + if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') { NewClass[i] = this[i]; } } // mix static properties into the class if (props.statics) { - L.extend(NewClass, props.statics); + extend(NewClass, props.statics); delete props.statics; } // mix includes into the prototype if (props.includes) { - L.Util.extend.apply(null, [proto].concat(props.includes)); + checkDeprecatedMixinEvents(props.includes); + extend.apply(null, [proto].concat(props.includes)); delete props.includes; } // merge options if (proto.options) { - props.options = L.Util.extend(L.Util.create(proto.options), props.options); + props.options = extend(create(proto.options), props.options); } // mix given properties into the prototype - L.extend(proto, props); + extend(proto, props); proto._initHooks = []; @@ -1203,21 +1214,21 @@ L.Class.extend = function (props) { // @function include(properties: Object): this // [Includes a mixin](#class-includes) into the current class. -L.Class.include = function (props) { - L.extend(this.prototype, props); +Class.include = function (props) { + extend(this.prototype, props); return this; }; // @function mergeOptions(options: Object): this // [Merges `options`](#class-options) into the defaults of the class. -L.Class.mergeOptions = function (options) { - L.extend(this.prototype.options, options); +Class.mergeOptions = function (options) { + extend(this.prototype.options, options); return this; }; // @function addInitHook(fn: Function): this // Adds a [constructor hook](#class-constructor-hooks) to the class. -L.Class.addInitHook = function (fn) { // (Function) || (String, args...) +Class.addInitHook = function (fn) { // (Function) || (String, args...) var args = Array.prototype.slice.call(arguments, 1); var init = typeof fn === 'function' ? fn : function () { @@ -1228,8 +1239,20 @@ L.Class.addInitHook = function (fn) { // (Function) || (String, args...) this.prototype._initHooks.push(init); return this; }; - - + +function checkDeprecatedMixinEvents(includes) { + if (!L || !L.Mixin) { return; } + + includes = isArray(includes) ? includes : [includes]; + + for (var i = 0; i < includes.length; i++) { + if (includes[i] === L.Mixin.Events) { + console.warn('Deprecated include of L.Mixin.Events: ' + + 'this property will be removed in future releases, ' + + 'please inherit from L.Evented instead.', new Error().stack); + } + } +} /* * @class Evented @@ -1256,9 +1279,7 @@ L.Class.addInitHook = function (fn) { // (Function) || (String, args...) * ``` */ - -L.Evented = L.Class.extend({ - +var Events = { /* @method on(type: String, fn: Function, context?: Object): this * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`). * @@ -1278,7 +1299,7 @@ L.Evented = L.Class.extend({ } else { // types can be a string of space-separated words - types = L.Util.splitWords(types); + types = splitWords(types); for (var i = 0, len = types.length; i < len; i++) { this._on(types[i], fn, context); @@ -1311,7 +1332,7 @@ L.Evented = L.Class.extend({ } } else { - types = L.Util.splitWords(types); + types = splitWords(types); for (var i = 0, len = types.length; i < len; i++) { this._off(types[i], fn, context); @@ -1365,7 +1386,7 @@ L.Evented = L.Class.extend({ if (!fn) { // Set all removed listeners to noop so they are not called if remove happens in fire for (i = 0, len = listeners.length; i < len; i++) { - listeners[i].fn = L.Util.falseFn; + listeners[i].fn = falseFn; } // clear all listeners for a type if function isn't specified delete this._events[type]; @@ -1385,7 +1406,7 @@ L.Evented = L.Class.extend({ if (l.fn === fn) { // set the removed listener to noop so that's not called if remove happens in fire - l.fn = L.Util.falseFn; + l.fn = falseFn; if (this._firingCount) { /* copy array in case events are being fired */ @@ -1406,7 +1427,7 @@ L.Evented = L.Class.extend({ fire: function (type, data, propagate) { if (!this.listens(type, propagate)) { return this; } - var event = L.Util.extend({}, data, {type: type, target: this}); + var event = extend({}, data, {type: type, target: this}); if (this._events) { var listeners = this._events[type]; @@ -1456,7 +1477,7 @@ L.Evented = L.Class.extend({ return this; } - var handler = L.bind(function () { + var handler = bind(function () { this .off(types, fn, context) .off(types, handler, context); @@ -1472,7 +1493,7 @@ L.Evented = L.Class.extend({ // Adds an event parent - an `Evented` that will receive propagated events addEventParent: function (obj) { this._eventParents = this._eventParents || {}; - this._eventParents[L.stamp(obj)] = obj; + this._eventParents[stamp(obj)] = obj; return this; }, @@ -1480,202 +1501,44 @@ L.Evented = L.Class.extend({ // Removes an event parent, so it will stop receiving propagated events removeEventParent: function (obj) { if (this._eventParents) { - delete this._eventParents[L.stamp(obj)]; + delete this._eventParents[stamp(obj)]; } return this; }, _propagateEvent: function (e) { for (var id in this._eventParents) { - this._eventParents[id].fire(e.type, L.extend({layer: e.target}, e), true); + this._eventParents[id].fire(e.type, extend({layer: e.target}, e), true); } } -}); - -var proto = L.Evented.prototype; +}; // aliases; we should ditch those eventually // @method addEventListener(…): this // Alias to [`on(…)`](#evented-on) -proto.addEventListener = proto.on; +Events.addEventListener = Events.on; // @method removeEventListener(…): this // Alias to [`off(…)`](#evented-off) // @method clearAllEventListeners(…): this // Alias to [`off()`](#evented-off) -proto.removeEventListener = proto.clearAllEventListeners = proto.off; +Events.removeEventListener = Events.clearAllEventListeners = Events.off; // @method addOneTimeEventListener(…): this // Alias to [`once(…)`](#evented-once) -proto.addOneTimeEventListener = proto.once; +Events.addOneTimeEventListener = Events.once; // @method fireEvent(…): this // Alias to [`fire(…)`](#evented-fire) -proto.fireEvent = proto.fire; +Events.fireEvent = Events.fire; // @method hasEventListeners(…): Boolean // Alias to [`listens(…)`](#evented-listens) -proto.hasEventListeners = proto.listens; +Events.hasEventListeners = Events.listens; -L.Mixin = {Events: proto}; - - - -/* - * @namespace Browser - * @aka L.Browser - * - * A namespace with static properties for browser/feature detection used by Leaflet internally. - * - * @example - * - * ```js - * if (L.Browser.ielt9) { - * alert('Upgrade your browser, dude!'); - * } - * ``` - */ - -(function () { - - var ua = navigator.userAgent.toLowerCase(), - doc = document.documentElement, - - ie = 'ActiveXObject' in window, - - webkit = ua.indexOf('webkit') !== -1, - phantomjs = ua.indexOf('phantom') !== -1, - android23 = ua.search('android [23]') !== -1, - chrome = ua.indexOf('chrome') !== -1, - gecko = ua.indexOf('gecko') !== -1 && !webkit && !window.opera && !ie, - - win = navigator.platform.indexOf('Win') === 0, - - mobile = typeof orientation !== 'undefined' || ua.indexOf('mobile') !== -1, - msPointer = !window.PointerEvent && window.MSPointerEvent, - pointer = window.PointerEvent || msPointer, - - ie3d = ie && ('transition' in doc.style), - webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23, - gecko3d = 'MozPerspective' in doc.style, - opera12 = 'OTransition' in doc.style; - - - var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window || - (window.DocumentTouch && document instanceof window.DocumentTouch)); - - L.Browser = { - - // @property ie: Boolean - // `true` for all Internet Explorer versions (not Edge). - ie: ie, - - // @property ielt9: Boolean - // `true` for Internet Explorer versions less than 9. - ielt9: ie && !document.addEventListener, - - // @property edge: Boolean - // `true` for the Edge web browser. - edge: 'msLaunchUri' in navigator && !('documentMode' in document), - - // @property webkit: Boolean - // `true` for webkit-based browsers like Chrome and Safari (including mobile versions). - webkit: webkit, - - // @property gecko: Boolean - // `true` for gecko-based browsers like Firefox. - gecko: gecko, - - // @property android: Boolean - // `true` for any browser running on an Android platform. - android: ua.indexOf('android') !== -1, - - // @property android23: Boolean - // `true` for browsers running on Android 2 or Android 3. - android23: android23, - - // @property chrome: Boolean - // `true` for the Chrome browser. - chrome: chrome, - - // @property safari: Boolean - // `true` for the Safari browser. - safari: !chrome && ua.indexOf('safari') !== -1, - - - // @property win: Boolean - // `true` when the browser is running in a Windows platform - win: win, - - - // @property ie3d: Boolean - // `true` for all Internet Explorer versions supporting CSS transforms. - ie3d: ie3d, - - // @property webkit3d: Boolean - // `true` for webkit-based browsers supporting CSS transforms. - webkit3d: webkit3d, - - // @property gecko3d: Boolean - // `true` for gecko-based browsers supporting CSS transforms. - gecko3d: gecko3d, - - // @property opera12: Boolean - // `true` for the Opera browser supporting CSS transforms (version 12 or later). - opera12: opera12, - - // @property any3d: Boolean - // `true` for all browsers supporting CSS transforms. - any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantomjs, - - - // @property mobile: Boolean - // `true` for all browsers running in a mobile device. - mobile: mobile, - - // @property mobileWebkit: Boolean - // `true` for all webkit-based browsers in a mobile device. - mobileWebkit: mobile && webkit, - - // @property mobileWebkit3d: Boolean - // `true` for all webkit-based browsers in a mobile device supporting CSS transforms. - mobileWebkit3d: mobile && webkit3d, - - // @property mobileOpera: Boolean - // `true` for the Opera browser in a mobile device. - mobileOpera: mobile && window.opera, - - // @property mobileGecko: Boolean - // `true` for gecko-based browsers running in a mobile device. - mobileGecko: mobile && gecko, - - - // @property touch: Boolean - // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). - // This does not necessarily mean that the browser is running in a computer with - // a touchscreen, it only means that the browser is capable of understanding - // touch events. - touch: !!touch, - - // @property msPointer: Boolean - // `true` for browsers implementing the Microsoft touch events model (notably IE10). - msPointer: !!msPointer, - - // @property pointer: Boolean - // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). - pointer: !!pointer, - - - // @property retina: Boolean - // `true` for browsers on a high-resolution "retina" screen. - retina: (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1 - }; - -}()); - - +var Evented = Class.extend(Events); /* * @class Point @@ -1697,26 +1560,26 @@ L.Mixin = {Events: proto}; * ``` */ -L.Point = function (x, y, round) { +function Point(x, y, round) { // @property x: Number; The `x` coordinate of the point this.x = (round ? Math.round(x) : x); // @property y: Number; The `y` coordinate of the point this.y = (round ? Math.round(y) : y); -}; +} -L.Point.prototype = { +Point.prototype = { // @method clone(): Point // Returns a copy of the current point. clone: function () { - return new L.Point(this.x, this.y); + return new Point(this.x, this.y); }, // @method add(otherPoint: Point): Point // Returns the result of addition of the current and the given points. add: function (point) { // non-destructive, returns a new point - return this.clone()._add(L.point(point)); + return this.clone()._add(toPoint(point)); }, _add: function (point) { @@ -1729,7 +1592,7 @@ L.Point.prototype = { // @method subtract(otherPoint: Point): Point // Returns the result of subtraction of the given point from the current. subtract: function (point) { - return this.clone()._subtract(L.point(point)); + return this.clone()._subtract(toPoint(point)); }, _subtract: function (point) { @@ -1768,14 +1631,14 @@ L.Point.prototype = { // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) // defined by `scale`. scaleBy: function (point) { - return new L.Point(this.x * point.x, this.y * point.y); + return new Point(this.x * point.x, this.y * point.y); }, // @method unscaleBy(scale: Point): Point // Inverse of `scaleBy`. Divide each coordinate of the current point by // each coordinate of `scale`. unscaleBy: function (point) { - return new L.Point(this.x / point.x, this.y / point.y); + return new Point(this.x / point.x, this.y / point.y); }, // @method round(): Point @@ -1817,7 +1680,7 @@ L.Point.prototype = { // @method distanceTo(otherPoint: Point): Number // Returns the cartesian distance between the current and the given points. distanceTo: function (point) { - point = L.point(point); + point = toPoint(point); var x = point.x - this.x, y = point.y - this.y; @@ -1828,7 +1691,7 @@ L.Point.prototype = { // @method equals(otherPoint: Point): Boolean // Returns `true` if the given point has the same coordinates. equals: function (point) { - point = L.point(point); + point = toPoint(point); return point.x === this.x && point.y === this.y; @@ -1837,7 +1700,7 @@ L.Point.prototype = { // @method contains(otherPoint: Point): Boolean // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). contains: function (point) { - point = L.point(point); + point = toPoint(point); return Math.abs(point.x) <= Math.abs(this.x) && Math.abs(point.y) <= Math.abs(this.y); @@ -1847,8 +1710,8 @@ L.Point.prototype = { // Returns a string representation of the point for debugging purposes. toString: function () { return 'Point(' + - L.Util.formatNum(this.x) + ', ' + - L.Util.formatNum(this.y) + ')'; + formatNum(this.x) + ', ' + + formatNum(this.y) + ')'; } }; @@ -1862,23 +1725,21 @@ L.Point.prototype = { // @alternative // @factory L.point(coords: Object) // Expects a plain object of the form `{x: Number, y: Number}` instead. -L.point = function (x, y, round) { - if (x instanceof L.Point) { +function toPoint(x, y, round) { + if (x instanceof Point) { return x; } - if (L.Util.isArray(x)) { - return new L.Point(x[0], x[1]); + if (isArray(x)) { + return new Point(x[0], x[1]); } if (x === undefined || x === null) { return x; } if (typeof x === 'object' && 'x' in x && 'y' in x) { - return new L.Point(x.x, x.y); + return new Point(x.x, x.y); } - return new L.Point(x, y, round); -}; - - + return new Point(x, y, round); +} /* * @class Bounds @@ -1901,7 +1762,7 @@ L.point = function (x, y, round) { * ``` */ -L.Bounds = function (a, b) { +function Bounds(a, b) { if (!a) { return; } var points = b ? [a, b] : a; @@ -1909,13 +1770,13 @@ L.Bounds = function (a, b) { for (var i = 0, len = points.length; i < len; i++) { this.extend(points[i]); } -}; +} -L.Bounds.prototype = { +Bounds.prototype = { // @method extend(point: Point): this // Extends the bounds to contain the given point. extend: function (point) { // (Point) - point = L.point(point); + point = toPoint(point); // @property min: Point // The top left corner of the rectangle. @@ -1936,7 +1797,7 @@ L.Bounds.prototype = { // @method getCenter(round?: Boolean): Point // Returns the center point of the bounds. getCenter: function (round) { - return new L.Point( + return new Point( (this.min.x + this.max.x) / 2, (this.min.y + this.max.y) / 2, round); }, @@ -1944,13 +1805,25 @@ L.Bounds.prototype = { // @method getBottomLeft(): Point // Returns the bottom-left point of the bounds. getBottomLeft: function () { - return new L.Point(this.min.x, this.max.y); + return new Point(this.min.x, this.max.y); }, // @method getTopRight(): Point // Returns the top-right point of the bounds. getTopRight: function () { // -> Point - return new L.Point(this.max.x, this.min.y); + return new Point(this.max.x, this.min.y); + }, + + // @method getTopLeft(): Point + // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). + getTopLeft: function () { + return this.min; // left, top + }, + + // @method getBottomRight(): Point + // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). + getBottomRight: function () { + return this.max; // right, bottom }, // @method getSize(): Point @@ -1967,13 +1840,13 @@ L.Bounds.prototype = { contains: function (obj) { var min, max; - if (typeof obj[0] === 'number' || obj instanceof L.Point) { - obj = L.point(obj); + if (typeof obj[0] === 'number' || obj instanceof Point) { + obj = toPoint(obj); } else { - obj = L.bounds(obj); + obj = toBounds(obj); } - if (obj instanceof L.Bounds) { + if (obj instanceof Bounds) { min = obj.min; max = obj.max; } else { @@ -1990,7 +1863,7 @@ L.Bounds.prototype = { // Returns `true` if the rectangle intersects the given bounds. Two bounds // intersect if they have at least one point in common. intersects: function (bounds) { // (Bounds) -> Boolean - bounds = L.bounds(bounds); + bounds = toBounds(bounds); var min = this.min, max = this.max, @@ -2006,7 +1879,7 @@ L.Bounds.prototype = { // Returns `true` if the rectangle overlaps the given bounds. Two bounds // overlap if their intersection is an area. overlaps: function (bounds) { // (Bounds) -> Boolean - bounds = L.bounds(bounds); + bounds = toBounds(bounds); var min = this.min, max = this.max, @@ -2024,399 +1897,261 @@ L.Bounds.prototype = { }; -// @factory L.bounds(topLeft: Point, bottomRight: Point) -// Creates a Bounds object from two coordinates (usually top-left and bottom-right corners). +// @factory L.bounds(corner1: Point, corner2: Point) +// Creates a Bounds object from two corners coordinate pairs. // @alternative // @factory L.bounds(points: Point[]) -// Creates a Bounds object from the points it contains -L.bounds = function (a, b) { - if (!a || a instanceof L.Bounds) { +// Creates a Bounds object from the given array of points. +function toBounds(a, b) { + if (!a || a instanceof Bounds) { return a; } - return new L.Bounds(a, b); -}; - - + return new Bounds(a, b); +} /* - * @class Transformation - * @aka L.Transformation + * @class LatLngBounds + * @aka L.LatLngBounds * - * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` - * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing - * the reverse. Used by Leaflet in its projections code. + * Represents a rectangular geographical area on a map. * * @example * * ```js - * var transformation = new L.Transformation(2, 5, -1, 10), - * p = L.point(1, 2), - * p2 = transformation.transform(p), // L.point(7, 8) - * p3 = transformation.untransform(p2); // L.point(1, 2) + * var corner1 = L.latLng(40.712, -74.227), + * corner2 = L.latLng(40.774, -74.125), + * bounds = L.latLngBounds(corner1, corner2); + * ``` + * + * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: + * + * ```js + * map.fitBounds([ + * [40.712, -74.227], + * [40.774, -74.125] + * ]); * ``` + * + * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range. */ +function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) + if (!corner1) { return; } -// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) -// Creates a `Transformation` object with the given coefficients. -L.Transformation = function (a, b, c, d) { - this._a = a; - this._b = b; - this._c = c; - this._d = d; -}; + var latlngs = corner2 ? [corner1, corner2] : corner1; -L.Transformation.prototype = { - // @method transform(point: Point, scale?: Number): Point - // Returns a transformed point, optionally multiplied by the given scale. - // Only accepts actual `L.Point` instances, not arrays. - transform: function (point, scale) { // (Point, Number) -> Point - return this._transform(point.clone(), scale); - }, + for (var i = 0, len = latlngs.length; i < len; i++) { + this.extend(latlngs[i]); + } +} - // destructive transform (faster) - _transform: function (point, scale) { - scale = scale || 1; - point.x = scale * (this._a * point.x + this._b); - point.y = scale * (this._c * point.y + this._d); - return point; - }, +LatLngBounds.prototype = { - // @method untransform(point: Point, scale?: Number): Point - // Returns the reverse transformation of the given point, optionally divided - // by the given scale. Only accepts actual `L.Point` instances, not arrays. - untransform: function (point, scale) { - scale = scale || 1; - return new L.Point( - (point.x / scale - this._b) / this._a, - (point.y / scale - this._d) / this._c); - } -}; - - - -/* - * @namespace DomUtil - * - * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model) - * tree, used by Leaflet internally. - * - * Most functions expecting or returning a `HTMLElement` also work for - * SVG elements. The only difference is that classes refer to CSS classes - * in HTML and SVG classes in SVG. - */ + // @method extend(latlng: LatLng): this + // Extend the bounds to contain the given point -L.DomUtil = { + // @alternative + // @method extend(otherBounds: LatLngBounds): this + // Extend the bounds to contain the given bounds + extend: function (obj) { + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; - // @function get(id: String|HTMLElement): HTMLElement - // Returns an element given its DOM id, or returns the element itself - // if it was passed directly. - get: function (id) { - return typeof id === 'string' ? document.getElementById(id) : id; - }, + if (obj instanceof LatLng) { + sw2 = obj; + ne2 = obj; - // @function getStyle(el: HTMLElement, styleAttrib: String): String - // Returns the value for a certain style attribute on an element, - // including computed values or values set through CSS. - getStyle: function (el, style) { + } else if (obj instanceof LatLngBounds) { + sw2 = obj._southWest; + ne2 = obj._northEast; - var value = el.style[style] || (el.currentStyle && el.currentStyle[style]); + if (!sw2 || !ne2) { return this; } - if ((!value || value === 'auto') && document.defaultView) { - var css = document.defaultView.getComputedStyle(el, null); - value = css ? css[style] : null; + } else { + return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; } - return value === 'auto' ? null : value; - }, - - // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement - // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element. - create: function (tagName, className, container) { - - var el = document.createElement(tagName); - el.className = className || ''; - - if (container) { - container.appendChild(el); + if (!sw && !ne) { + this._southWest = new LatLng(sw2.lat, sw2.lng); + this._northEast = new LatLng(ne2.lat, ne2.lng); + } else { + sw.lat = Math.min(sw2.lat, sw.lat); + sw.lng = Math.min(sw2.lng, sw.lng); + ne.lat = Math.max(ne2.lat, ne.lat); + ne.lng = Math.max(ne2.lng, ne.lng); } - return el; + return this; }, - // @function remove(el: HTMLElement) - // Removes `el` from its parent element - remove: function (el) { - var parent = el.parentNode; - if (parent) { - parent.removeChild(el); - } - }, + // @method pad(bufferRatio: Number): LatLngBounds + // Returns bigger bounds created by extending the current bounds by a given percentage in each direction. + pad: function (bufferRatio) { + var sw = this._southWest, + ne = this._northEast, + heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, + widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; - // @function empty(el: HTMLElement) - // Removes all of `el`'s children elements from `el` - empty: function (el) { - while (el.firstChild) { - el.removeChild(el.firstChild); - } + return new LatLngBounds( + new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), + new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); }, - // @function toFront(el: HTMLElement) - // Makes `el` the last children of its parent, so it renders in front of the other children. - toFront: function (el) { - el.parentNode.appendChild(el); + // @method getCenter(): LatLng + // Returns the center point of the bounds. + getCenter: function () { + return new LatLng( + (this._southWest.lat + this._northEast.lat) / 2, + (this._southWest.lng + this._northEast.lng) / 2); }, - // @function toBack(el: HTMLElement) - // Makes `el` the first children of its parent, so it renders back from the other children. - toBack: function (el) { - var parent = el.parentNode; - parent.insertBefore(el, parent.firstChild); + // @method getSouthWest(): LatLng + // Returns the south-west point of the bounds. + getSouthWest: function () { + return this._southWest; }, - // @function hasClass(el: HTMLElement, name: String): Boolean - // Returns `true` if the element's class attribute contains `name`. - hasClass: function (el, name) { - if (el.classList !== undefined) { - return el.classList.contains(name); - } - var className = L.DomUtil.getClass(el); - return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className); + // @method getNorthEast(): LatLng + // Returns the north-east point of the bounds. + getNorthEast: function () { + return this._northEast; }, - // @function addClass(el: HTMLElement, name: String) - // Adds `name` to the element's class attribute. - addClass: function (el, name) { - if (el.classList !== undefined) { - var classes = L.Util.splitWords(name); - for (var i = 0, len = classes.length; i < len; i++) { - el.classList.add(classes[i]); - } - } else if (!L.DomUtil.hasClass(el, name)) { - var className = L.DomUtil.getClass(el); - L.DomUtil.setClass(el, (className ? className + ' ' : '') + name); - } + // @method getNorthWest(): LatLng + // Returns the north-west point of the bounds. + getNorthWest: function () { + return new LatLng(this.getNorth(), this.getWest()); }, - // @function removeClass(el: HTMLElement, name: String) - // Removes `name` from the element's class attribute. - removeClass: function (el, name) { - if (el.classList !== undefined) { - el.classList.remove(name); - } else { - L.DomUtil.setClass(el, L.Util.trim((' ' + L.DomUtil.getClass(el) + ' ').replace(' ' + name + ' ', ' '))); - } + // @method getSouthEast(): LatLng + // Returns the south-east point of the bounds. + getSouthEast: function () { + return new LatLng(this.getSouth(), this.getEast()); }, - // @function setClass(el: HTMLElement, name: String) - // Sets the element's class. - setClass: function (el, name) { - if (el.className.baseVal === undefined) { - el.className = name; - } else { - // in case of SVG element - el.className.baseVal = name; - } + // @method getWest(): Number + // Returns the west longitude of the bounds + getWest: function () { + return this._southWest.lng; }, - // @function getClass(el: HTMLElement): String - // Returns the element's class. - getClass: function (el) { - return el.className.baseVal === undefined ? el.className : el.className.baseVal; + // @method getSouth(): Number + // Returns the south latitude of the bounds + getSouth: function () { + return this._southWest.lat; }, - // @function setOpacity(el: HTMLElement, opacity: Number) - // Set the opacity of an element (including old IE support). - // `opacity` must be a number from `0` to `1`. - setOpacity: function (el, value) { - - if ('opacity' in el.style) { - el.style.opacity = value; + // @method getEast(): Number + // Returns the east longitude of the bounds + getEast: function () { + return this._northEast.lng; + }, - } else if ('filter' in el.style) { - L.DomUtil._setOpacityIE(el, value); - } + // @method getNorth(): Number + // Returns the north latitude of the bounds + getNorth: function () { + return this._northEast.lat; }, - _setOpacityIE: function (el, value) { - var filter = false, - filterName = 'DXImageTransform.Microsoft.Alpha'; + // @method contains(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle contains the given one. - // filters collection throws an error if we try to retrieve a filter that doesn't exist - try { - filter = el.filters.item(filterName); - } catch (e) { - // don't set opacity to 1 if we haven't already set an opacity, - // it isn't needed and breaks transparent pngs. - if (value === 1) { return; } + // @alternative + // @method contains (latlng: LatLng): Boolean + // Returns `true` if the rectangle contains the given point. + contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean + if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) { + obj = toLatLng(obj); + } else { + obj = toLatLngBounds(obj); } - value = Math.round(value * 100); + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; - if (filter) { - filter.Enabled = (value !== 100); - filter.Opacity = value; + if (obj instanceof LatLngBounds) { + sw2 = obj.getSouthWest(); + ne2 = obj.getNorthEast(); } else { - el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; + sw2 = ne2 = obj; } - }, - - // @function testProp(props: String[]): String|false - // Goes through the array of style names and returns the first name - // that is a valid style name for an element. If no such name is found, - // it returns false. Useful for vendor-prefixed styles like `transform`. - testProp: function (props) { - - var style = document.documentElement.style; - for (var i = 0; i < props.length; i++) { - if (props[i] in style) { - return props[i]; - } - } - return false; + return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && + (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); }, - // @function setTransform(el: HTMLElement, offset: Point, scale?: Number) - // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels - // and optionally scaled by `scale`. Does not have an effect if the - // browser doesn't support 3D CSS transforms. - setTransform: function (el, offset, scale) { - var pos = offset || new L.Point(0, 0); - - el.style[L.DomUtil.TRANSFORM] = - (L.Browser.ie3d ? - 'translate(' + pos.x + 'px,' + pos.y + 'px)' : - 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') + - (scale ? ' scale(' + scale + ')' : ''); - }, + // @method intersects(otherBounds: LatLngBounds): Boolean + // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. + intersects: function (bounds) { + bounds = toLatLngBounds(bounds); - // @function setPosition(el: HTMLElement, position: Point) - // Sets the position of `el` to coordinates specified by `position`, - // using CSS translate or top/left positioning depending on the browser - // (used by Leaflet internally to position its layers). - setPosition: function (el, point) { // (HTMLElement, Point[, Boolean]) + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), - /*eslint-disable */ - el._leaflet_pos = point; - /*eslint-enable */ + latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), + lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); - if (L.Browser.any3d) { - L.DomUtil.setTransform(el, point); - } else { - el.style.left = point.x + 'px'; - el.style.top = point.y + 'px'; - } + return latIntersects && lngIntersects; }, - // @function getPosition(el: HTMLElement): Point - // Returns the coordinates of an element previously positioned with setPosition. - getPosition: function (el) { - // this method is only used for elements previously positioned using setPosition, - // so it's safe to cache the position for performance - - return el._leaflet_pos || new L.Point(0, 0); - } -}; - - -(function () { - // prefix style property names + // @method overlaps(otherBounds: Bounds): Boolean + // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. + overlaps: function (bounds) { + bounds = toLatLngBounds(bounds); - // @property TRANSFORM: String - // Vendor-prefixed fransform style name (e.g. `'webkitTransform'` for WebKit). - L.DomUtil.TRANSFORM = L.DomUtil.testProp( - ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']); + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), + lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); - // webkitTransition comes first because some browser versions that drop vendor prefix don't do - // the same for the transitionend event, in particular the Android 4.1 stock browser + return latOverlaps && lngOverlaps; + }, - // @property TRANSITION: String - // Vendor-prefixed transform style name. - var transition = L.DomUtil.TRANSITION = L.DomUtil.testProp( - ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); + // @method toBBoxString(): String + // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data. + toBBoxString: function () { + return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); + }, - L.DomUtil.TRANSITION_END = - transition === 'webkitTransition' || transition === 'OTransition' ? transition + 'End' : 'transitionend'; + // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean + // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overriden by setting `maxMargin` to a small number. + equals: function (bounds, maxMargin) { + if (!bounds) { return false; } - // @function disableTextSelection() - // Prevents the user from generating `selectstart` DOM events, usually generated - // when the user drags the mouse through a page with text. Used internally - // by Leaflet to override the behaviour of any click-and-drag interaction on - // the map. Affects drag interactions on the whole document. + bounds = toLatLngBounds(bounds); - // @function enableTextSelection() - // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection). - if ('onselectstart' in document) { - L.DomUtil.disableTextSelection = function () { - L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault); - }; - L.DomUtil.enableTextSelection = function () { - L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault); - }; + return this._southWest.equals(bounds.getSouthWest(), maxMargin) && + this._northEast.equals(bounds.getNorthEast(), maxMargin); + }, - } else { - var userSelectProperty = L.DomUtil.testProp( - ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); - - L.DomUtil.disableTextSelection = function () { - if (userSelectProperty) { - var style = document.documentElement.style; - this._userSelect = style[userSelectProperty]; - style[userSelectProperty] = 'none'; - } - }; - L.DomUtil.enableTextSelection = function () { - if (userSelectProperty) { - document.documentElement.style[userSelectProperty] = this._userSelect; - delete this._userSelect; - } - }; + // @method isValid(): Boolean + // Returns `true` if the bounds are properly initialized. + isValid: function () { + return !!(this._southWest && this._northEast); } +}; - // @function disableImageDrag() - // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but - // for `dragstart` DOM events, usually generated when the user drags an image. - L.DomUtil.disableImageDrag = function () { - L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault); - }; - - // @function enableImageDrag() - // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection). - L.DomUtil.enableImageDrag = function () { - L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault); - }; +// TODO International date line? - // @function preventOutline(el: HTMLElement) - // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline) - // of the element `el` invisible. Used internally by Leaflet to prevent - // focusable elements from displaying an outline when the user performs a - // drag interaction on them. - L.DomUtil.preventOutline = function (element) { - while (element.tabIndex === -1) { - element = element.parentNode; - } - if (!element || !element.style) { return; } - L.DomUtil.restoreOutline(); - this._outlineElement = element; - this._outlineStyle = element.style.outline; - element.style.outline = 'none'; - L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this); - }; +// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) +// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. - // @function restoreOutline() - // Cancels the effects of a previous [`L.DomUtil.preventOutline`](). - L.DomUtil.restoreOutline = function () { - if (!this._outlineElement) { return; } - this._outlineElement.style.outline = this._outlineStyle; - delete this._outlineElement; - delete this._outlineStyle; - L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this); - }; -})(); - - +// @alternative +// @factory L.latLngBounds(latlngs: LatLng[]) +// Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds). +function toLatLngBounds(a, b) { + if (a instanceof LatLngBounds) { + return a; + } + return new LatLngBounds(a, b); +} /* @class LatLng * @aka L.LatLng @@ -2439,7 +2174,7 @@ L.DomUtil = { * ``` */ -L.LatLng = function (lat, lng, alt) { +function LatLng(lat, lng, alt) { if (isNaN(lat) || isNaN(lng)) { throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); } @@ -2457,15 +2192,15 @@ L.LatLng = function (lat, lng, alt) { if (alt !== undefined) { this.alt = +alt; } -}; +} -L.LatLng.prototype = { +LatLng.prototype = { // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number. equals: function (obj, maxMargin) { if (!obj) { return false; } - obj = L.latLng(obj); + obj = toLatLng(obj); var margin = Math.max( Math.abs(this.lat - obj.lat), @@ -2478,20 +2213,20 @@ L.LatLng.prototype = { // Returns a string representation of the point (for debugging purposes). toString: function (precision) { return 'LatLng(' + - L.Util.formatNum(this.lat, precision) + ', ' + - L.Util.formatNum(this.lng, precision) + ')'; + formatNum(this.lat, precision) + ', ' + + formatNum(this.lng, precision) + ')'; }, // @method distanceTo(otherLatLng: LatLng): Number // Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula). distanceTo: function (other) { - return L.CRS.Earth.distance(this, L.latLng(other)); + return Earth.distance(this, toLatLng(other)); }, // @method wrap(): LatLng // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. wrap: function () { - return L.CRS.Earth.wrapLatLng(this); + return Earth.wrapLatLng(this); }, // @method toBounds(sizeInMeters: Number): LatLngBounds @@ -2500,13 +2235,13 @@ L.LatLng.prototype = { var latAccuracy = 180 * sizeInMeters / 40075017, lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); - return L.latLngBounds( + return toLatLngBounds( [this.lat - latAccuracy, this.lng - lngAccuracy], [this.lat + latAccuracy, this.lng + lngAccuracy]); }, clone: function () { - return new L.LatLng(this.lat, this.lng, this.alt); + return new LatLng(this.lat, this.lng, this.alt); } }; @@ -2523,16 +2258,16 @@ L.LatLng.prototype = { // @factory L.latLng(coords: Object): LatLng // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead. -L.latLng = function (a, b, c) { - if (a instanceof L.LatLng) { +function toLatLng(a, b, c) { + if (a instanceof LatLng) { return a; } - if (L.Util.isArray(a) && typeof a[0] !== 'object') { + if (isArray(a) && typeof a[0] !== 'object') { if (a.length === 3) { - return new L.LatLng(a[0], a[1], a[2]); + return new LatLng(a[0], a[1], a[2]); } if (a.length === 2) { - return new L.LatLng(a[0], a[1]); + return new LatLng(a[0], a[1]); } return null; } @@ -2540,301 +2275,184 @@ L.latLng = function (a, b, c) { return a; } if (typeof a === 'object' && 'lat' in a) { - return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); + return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); } if (b === undefined) { return null; } - return new L.LatLng(a, b, c); -}; - - + return new LatLng(a, b, c); +} /* - * @class LatLngBounds - * @aka L.LatLngBounds - * - * Represents a rectangular geographical area on a map. - * - * @example - * - * ```js - * var corner1 = L.latLng(40.712, -74.227), - * corner2 = L.latLng(40.774, -74.125), - * bounds = L.latLngBounds(corner1, corner2); - * ``` - * - * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: - * - * ```js - * map.fitBounds([ - * [40.712, -74.227], - * [40.774, -74.125] - * ]); - * ``` + * @namespace CRS + * @crs L.CRS.Base + * Object that defines coordinate reference systems for projecting + * geographical points into pixel (screen) coordinates and back (and to + * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See + * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system). * - * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range. + * Leaflet defines the most usual CRSs by default. If you want to use a + * CRS not defined by default, take a look at the + * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. */ -L.LatLngBounds = function (corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) - if (!corner1) { return; } - - var latlngs = corner2 ? [corner1, corner2] : corner1; +var CRS = { + // @method latLngToPoint(latlng: LatLng, zoom: Number): Point + // Projects geographical coordinates into pixel coordinates for a given zoom. + latLngToPoint: function (latlng, zoom) { + var projectedPoint = this.projection.project(latlng), + scale = this.scale(zoom); - for (var i = 0, len = latlngs.length; i < len; i++) { - this.extend(latlngs[i]); - } -}; + return this.transformation._transform(projectedPoint, scale); + }, -L.LatLngBounds.prototype = { + // @method pointToLatLng(point: Point, zoom: Number): LatLng + // The inverse of `latLngToPoint`. Projects pixel coordinates on a given + // zoom into geographical coordinates. + pointToLatLng: function (point, zoom) { + var scale = this.scale(zoom), + untransformedPoint = this.transformation.untransform(point, scale); - // @method extend(latlng: LatLng): this - // Extend the bounds to contain the given point + return this.projection.unproject(untransformedPoint); + }, - // @alternative - // @method extend(otherBounds: LatLngBounds): this - // Extend the bounds to contain the given bounds - extend: function (obj) { - var sw = this._southWest, - ne = this._northEast, - sw2, ne2; - - if (obj instanceof L.LatLng) { - sw2 = obj; - ne2 = obj; - - } else if (obj instanceof L.LatLngBounds) { - sw2 = obj._southWest; - ne2 = obj._northEast; - - if (!sw2 || !ne2) { return this; } - - } else { - return obj ? this.extend(L.latLng(obj) || L.latLngBounds(obj)) : this; - } - - if (!sw && !ne) { - this._southWest = new L.LatLng(sw2.lat, sw2.lng); - this._northEast = new L.LatLng(ne2.lat, ne2.lng); - } else { - sw.lat = Math.min(sw2.lat, sw.lat); - sw.lng = Math.min(sw2.lng, sw.lng); - ne.lat = Math.max(ne2.lat, ne.lat); - ne.lng = Math.max(ne2.lng, ne.lng); - } - - return this; - }, - - // @method pad(bufferRatio: Number): LatLngBounds - // Returns bigger bounds created by extending the current bounds by a given percentage in each direction. - pad: function (bufferRatio) { - var sw = this._southWest, - ne = this._northEast, - heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, - widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; - - return new L.LatLngBounds( - new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), - new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); - }, - - // @method getCenter(): LatLng - // Returns the center point of the bounds. - getCenter: function () { - return new L.LatLng( - (this._southWest.lat + this._northEast.lat) / 2, - (this._southWest.lng + this._northEast.lng) / 2); - }, - - // @method getSouthWest(): LatLng - // Returns the south-west point of the bounds. - getSouthWest: function () { - return this._southWest; - }, - - // @method getNorthEast(): LatLng - // Returns the north-east point of the bounds. - getNorthEast: function () { - return this._northEast; - }, - - // @method getNorthWest(): LatLng - // Returns the north-west point of the bounds. - getNorthWest: function () { - return new L.LatLng(this.getNorth(), this.getWest()); - }, - - // @method getSouthEast(): LatLng - // Returns the south-east point of the bounds. - getSouthEast: function () { - return new L.LatLng(this.getSouth(), this.getEast()); - }, - - // @method getWest(): Number - // Returns the west longitude of the bounds - getWest: function () { - return this._southWest.lng; - }, - - // @method getSouth(): Number - // Returns the south latitude of the bounds - getSouth: function () { - return this._southWest.lat; + // @method project(latlng: LatLng): Point + // Projects geographical coordinates into coordinates in units accepted for + // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). + project: function (latlng) { + return this.projection.project(latlng); }, - // @method getEast(): Number - // Returns the east longitude of the bounds - getEast: function () { - return this._northEast.lng; + // @method unproject(point: Point): LatLng + // Given a projected coordinate returns the corresponding LatLng. + // The inverse of `project`. + unproject: function (point) { + return this.projection.unproject(point); }, - // @method getNorth(): Number - // Returns the north latitude of the bounds - getNorth: function () { - return this._northEast.lat; + // @method scale(zoom: Number): Number + // Returns the scale used when transforming projected coordinates into + // pixel coordinates for a particular zoom. For example, it returns + // `256 * 2^zoom` for Mercator-based CRS. + scale: function (zoom) { + return 256 * Math.pow(2, zoom); }, - // @method contains(otherBounds: LatLngBounds): Boolean - // Returns `true` if the rectangle contains the given one. - - // @alternative - // @method contains (latlng: LatLng): Boolean - // Returns `true` if the rectangle contains the given point. - contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean - if (typeof obj[0] === 'number' || obj instanceof L.LatLng || 'lat' in obj) { - obj = L.latLng(obj); - } else { - obj = L.latLngBounds(obj); - } - - var sw = this._southWest, - ne = this._northEast, - sw2, ne2; - - if (obj instanceof L.LatLngBounds) { - sw2 = obj.getSouthWest(); - ne2 = obj.getNorthEast(); - } else { - sw2 = ne2 = obj; - } - - return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && - (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); + // @method zoom(scale: Number): Number + // Inverse of `scale()`, returns the zoom level corresponding to a scale + // factor of `scale`. + zoom: function (scale) { + return Math.log(scale / 256) / Math.LN2; }, - // @method intersects(otherBounds: LatLngBounds): Boolean - // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. - intersects: function (bounds) { - bounds = L.latLngBounds(bounds); - - var sw = this._southWest, - ne = this._northEast, - sw2 = bounds.getSouthWest(), - ne2 = bounds.getNorthEast(), + // @method getProjectedBounds(zoom: Number): Bounds + // Returns the projection's bounds scaled and transformed for the provided `zoom`. + getProjectedBounds: function (zoom) { + if (this.infinite) { return null; } - latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), - lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); + var b = this.projection.bounds, + s = this.scale(zoom), + min = this.transformation.transform(b.min, s), + max = this.transformation.transform(b.max, s); - return latIntersects && lngIntersects; + return new Bounds(min, max); }, - // @method overlaps(otherBounds: Bounds): Boolean - // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. - overlaps: function (bounds) { - bounds = L.latLngBounds(bounds); - - var sw = this._southWest, - ne = this._northEast, - sw2 = bounds.getSouthWest(), - ne2 = bounds.getNorthEast(), - - latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), - lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates. - return latOverlaps && lngOverlaps; - }, + // @property code: String + // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) + // + // @property wrapLng: Number[] + // An array of two numbers defining whether the longitude (horizontal) coordinate + // axis wraps around a given range and how. Defaults to `[-180, 180]` in most + // geographical CRSs. If `undefined`, the longitude axis does not wrap around. + // + // @property wrapLat: Number[] + // Like `wrapLng`, but for the latitude (vertical) axis. - // @method toBBoxString(): String - // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data. - toBBoxString: function () { - return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); - }, + // wrapLng: [min, max], + // wrapLat: [min, max], - // @method equals(otherBounds: LatLngBounds): Boolean - // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. - equals: function (bounds) { - if (!bounds) { return false; } + // @property infinite: Boolean + // If true, the coordinate space will be unbounded (infinite in both axes) + infinite: false, - bounds = L.latLngBounds(bounds); + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where lat and lng has been wrapped according to the + // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. + wrapLatLng: function (latlng) { + var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, + lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, + alt = latlng.alt; - return this._southWest.equals(bounds.getSouthWest()) && - this._northEast.equals(bounds.getNorthEast()); + return new LatLng(lat, lng, alt); }, - // @method isValid(): Boolean - // Returns `true` if the bounds are properly initialized. - isValid: function () { - return !!(this._southWest && this._northEast); - } -}; + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring + // that its center is within the CRS's bounds. + // Only accepts actual `L.LatLngBounds` instances, not arrays. + wrapLatLngBounds: function (bounds) { + var center = bounds.getCenter(), + newCenter = this.wrapLatLng(center), + latShift = center.lat - newCenter.lat, + lngShift = center.lng - newCenter.lng; -// TODO International date line? + if (latShift === 0 && lngShift === 0) { + return bounds; + } -// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) -// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. + var sw = bounds.getSouthWest(), + ne = bounds.getNorthEast(), + newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), + newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); -// @alternative -// @factory L.latLngBounds(latlngs: LatLng[]) -// Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds). -L.latLngBounds = function (a, b) { - if (a instanceof L.LatLngBounds) { - return a; + return new LatLngBounds(newSw, newNe); } - return new L.LatLngBounds(a, b); -}; +}; + +/* + * @namespace CRS + * @crs L.CRS.Earth + * + * Serves as the base for CRS that are global such that they cover the earth. + * Can only be used as the base for other CRS and cannot be used directly, + * since it does not have a `code`, `projection` or `transformation`. `distance()` returns + * meters. + */ + +var Earth = extend({}, CRS, { + wrapLng: [-180, 180], + + // Mean Earth Radius, as recommended for use by + // the International Union of Geodesy and Geophysics, + // see http://rosettacode.org/wiki/Haversine_formula + R: 6371000, + // distance between two geographical points using spherical law of cosines approximation + distance: function (latlng1, latlng2) { + var rad = Math.PI / 180, + lat1 = latlng1.lat * rad, + lat2 = latlng2.lat * rad, + a = Math.sin(lat1) * Math.sin(lat2) + + Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad); + return this.R * Math.acos(Math.min(a, 1)); + } +}); /* * @namespace Projection - * @section - * Leaflet comes with a set of already defined Projections out of the box: - * - * @projection L.Projection.LonLat + * @projection L.Projection.SphericalMercator * - * Equirectangular, or Plate Carree projection — the most simple projection, - * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as - * latitude. Also suitable for flat worlds, e.g. game maps. Used by the - * `EPSG:3395` and `Simple` CRS. + * Spherical Mercator projection — the most common projection for online maps, + * used by almost all free and commercial tile providers. Assumes that Earth is + * a sphere. Used by the `EPSG:3857` CRS. */ -L.Projection = {}; - -L.Projection.LonLat = { - project: function (latlng) { - return new L.Point(latlng.lng, latlng.lat); - }, - - unproject: function (point) { - return new L.LatLng(point.y, point.x); - }, - - bounds: L.bounds([-180, -90], [180, 90]) -}; - - - -/* - * @namespace Projection - * @projection L.Projection.SphericalMercator - * - * Spherical Mercator projection — the most common projection for online maps, - * used by almost all free and commercial tile providers. Assumes that Earth is - * a sphere. Used by the `EPSG:3857` CRS. - */ - -L.Projection.SphericalMercator = { +var SphericalMercator = { R: 6378137, MAX_LATITUDE: 85.0511287798, @@ -2845,7 +2463,7 @@ L.Projection.SphericalMercator = { lat = Math.max(Math.min(max, latlng.lat), -max), sin = Math.sin(lat * d); - return new L.Point( + return new Point( this.R * latlng.lng * d, this.R * Math.log((1 + sin) / (1 - sin)) / 2); }, @@ -2853,12265 +2471,13005 @@ L.Projection.SphericalMercator = { unproject: function (point) { var d = 180 / Math.PI; - return new L.LatLng( + return new LatLng( (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, point.x * d / this.R); }, bounds: (function () { var d = 6378137 * Math.PI; - return L.bounds([-d, -d], [d, d]); + return new Bounds([-d, -d], [d, d]); })() +}; + +/* + * @class Transformation + * @aka L.Transformation + * + * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` + * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing + * the reverse. Used by Leaflet in its projections code. + * + * @example + * + * ```js + * var transformation = L.transformation(2, 5, -1, 10), + * p = L.point(1, 2), + * p2 = transformation.transform(p), // L.point(7, 8) + * p3 = transformation.untransform(p2); // L.point(1, 2) + * ``` + */ + + +// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) +// Creates a `Transformation` object with the given coefficients. +function Transformation(a, b, c, d) { + if (isArray(a)) { + // use array properties + this._a = a[0]; + this._b = a[1]; + this._c = a[2]; + this._d = a[3]; + return; + } + this._a = a; + this._b = b; + this._c = c; + this._d = d; +} + +Transformation.prototype = { + // @method transform(point: Point, scale?: Number): Point + // Returns a transformed point, optionally multiplied by the given scale. + // Only accepts actual `L.Point` instances, not arrays. + transform: function (point, scale) { // (Point, Number) -> Point + return this._transform(point.clone(), scale); + }, + + // destructive transform (faster) + _transform: function (point, scale) { + scale = scale || 1; + point.x = scale * (this._a * point.x + this._b); + point.y = scale * (this._c * point.y + this._d); + return point; + }, + + // @method untransform(point: Point, scale?: Number): Point + // Returns the reverse transformation of the given point, optionally divided + // by the given scale. Only accepts actual `L.Point` instances, not arrays. + untransform: function (point, scale) { + scale = scale || 1; + return new Point( + (point.x / scale - this._b) / this._a, + (point.y / scale - this._d) / this._c); + } }; + +// factory L.transformation(a: Number, b: Number, c: Number, d: Number) + +// @factory L.transformation(a: Number, b: Number, c: Number, d: Number) +// Instantiates a Transformation object with the given coefficients. + +// @alternative +// @factory L.transformation(coefficients: Array): Transformation +// Expects an coeficients array of the form +// `[a: Number, b: Number, c: Number, d: Number]`. + +function toTransformation(a, b, c, d) { + return new Transformation(a, b, c, d); +} + +/* + * @namespace CRS + * @crs L.CRS.EPSG3857 + * + * The most common CRS for online maps, used by almost all free and commercial + * tile providers. Uses Spherical Mercator projection. Set in by default in + * Map's `crs` option. + */ + +var EPSG3857 = extend({}, Earth, { + code: 'EPSG:3857', + projection: SphericalMercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * SphericalMercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) +}); + +var EPSG900913 = extend({}, EPSG3857, { + code: 'EPSG:900913' +}); + +// @namespace SVG; @section +// There are several static functions which can be called without instantiating L.SVG: + +// @function create(name: String): SVGElement +// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), +// corresponding to the class name passed. For example, using 'line' will return +// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). +function svgCreate(name) { + return document.createElementNS('http://www.w3.org/2000/svg', name); +} + +// @function pointsToPath(rings: Point[], closed: Boolean): String +// Generates a SVG path string for multiple rings, with each ring turning +// into "M..L..L.." instructions +function pointsToPath(rings, closed) { + var str = '', + i, j, len, len2, points, p; + + for (i = 0, len = rings.length; i < len; i++) { + points = rings[i]; + + for (j = 0, len2 = points.length; j < len2; j++) { + p = points[j]; + str += (j ? 'L' : 'M') + p.x + ' ' + p.y; + } + // closes the ring for polygons; "x" is VML syntax + str += closed ? (svg ? 'z' : 'x') : ''; + } + // SVG complains about empty path strings + return str || 'M0 0'; +} /* - * @class CRS - * @aka L.CRS - * Abstract class that defines coordinate reference systems for projecting - * geographical points into pixel (screen) coordinates and back (and to - * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See - * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system). + * @namespace Browser + * @aka L.Browser * - * Leaflet defines the most usual CRSs by default. If you want to use a - * CRS not defined by default, take a look at the - * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. + * A namespace with static properties for browser/feature detection used by Leaflet internally. + * + * @example + * + * ```js + * if (L.Browser.ielt9) { + * alert('Upgrade your browser, dude!'); + * } + * ``` */ -L.CRS = { - // @method latLngToPoint(latlng: LatLng, zoom: Number): Point - // Projects geographical coordinates into pixel coordinates for a given zoom. - latLngToPoint: function (latlng, zoom) { - var projectedPoint = this.projection.project(latlng), - scale = this.scale(zoom); +var style$1 = document.documentElement.style; - return this.transformation._transform(projectedPoint, scale); - }, +// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). +var ie = 'ActiveXObject' in window; - // @method pointToLatLng(point: Point, zoom: Number): LatLng - // The inverse of `latLngToPoint`. Projects pixel coordinates on a given - // zoom into geographical coordinates. - pointToLatLng: function (point, zoom) { - var scale = this.scale(zoom), - untransformedPoint = this.transformation.untransform(point, scale); +// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9. +var ielt9 = ie && !document.addEventListener; - return this.projection.unproject(untransformedPoint); - }, +// @property edge: Boolean; `true` for the Edge web browser. +var edge = 'msLaunchUri' in navigator && !('documentMode' in document); - // @method project(latlng: LatLng): Point - // Projects geographical coordinates into coordinates in units accepted for - // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). - project: function (latlng) { - return this.projection.project(latlng); - }, +// @property webkit: Boolean; +// `true` for webkit-based browsers like Chrome and Safari (including mobile versions). +var webkit = userAgentContains('webkit'); - // @method unproject(point: Point): LatLng - // Given a projected coordinate returns the corresponding LatLng. - // The inverse of `project`. - unproject: function (point) { - return this.projection.unproject(point); - }, +// @property android: Boolean +// `true` for any browser running on an Android platform. +var android = userAgentContains('android'); - // @method scale(zoom: Number): Number - // Returns the scale used when transforming projected coordinates into - // pixel coordinates for a particular zoom. For example, it returns - // `256 * 2^zoom` for Mercator-based CRS. - scale: function (zoom) { - return 256 * Math.pow(2, zoom); - }, +// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3. +var android23 = userAgentContains('android 2') || userAgentContains('android 3'); - // @method zoom(scale: Number): Number - // Inverse of `scale()`, returns the zoom level corresponding to a scale - // factor of `scale`. - zoom: function (scale) { - return Math.log(scale / 256) / Math.LN2; - }, +// @property opera: Boolean; `true` for the Opera browser +var opera = !!window.opera; - // @method getProjectedBounds(zoom: Number): Bounds - // Returns the projection's bounds scaled and transformed for the provided `zoom`. - getProjectedBounds: function (zoom) { - if (this.infinite) { return null; } +// @property chrome: Boolean; `true` for the Chrome browser. +var chrome = userAgentContains('chrome'); - var b = this.projection.bounds, - s = this.scale(zoom), - min = this.transformation.transform(b.min, s), - max = this.transformation.transform(b.max, s); +// @property gecko: Boolean; `true` for gecko-based browsers like Firefox. +var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; - return L.bounds(min, max); - }, +// @property safari: Boolean; `true` for the Safari browser. +var safari = !chrome && userAgentContains('safari'); - // @method distance(latlng1: LatLng, latlng2: LatLng): Number - // Returns the distance between two geographical coordinates. +var phantom = userAgentContains('phantom'); - // @property code: String - // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) - // - // @property wrapLng: Number[] - // An array of two numbers defining whether the longitude (horizontal) coordinate - // axis wraps around a given range and how. Defaults to `[-180, 180]` in most - // geographical CRSs. If `undefined`, the longitude axis does not wrap around. - // - // @property wrapLat: Number[] - // Like `wrapLng`, but for the latitude (vertical) axis. +// @property opera12: Boolean +// `true` for the Opera browser supporting CSS transforms (version 12 or later). +var opera12 = 'OTransition' in style$1; - // wrapLng: [min, max], - // wrapLat: [min, max], +// @property win: Boolean; `true` when the browser is running in a Windows platform +var win = navigator.platform.indexOf('Win') === 0; - // @property infinite: Boolean - // If true, the coordinate space will be unbounded (infinite in both axes) - infinite: false, +// @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms. +var ie3d = ie && ('transition' in style$1); - // @method wrapLatLng(latlng: LatLng): LatLng - // Returns a `LatLng` where lat and lng has been wrapped according to the - // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. - // Only accepts actual `L.LatLng` instances, not arrays. - wrapLatLng: function (latlng) { - var lng = this.wrapLng ? L.Util.wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, - lat = this.wrapLat ? L.Util.wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, - alt = latlng.alt; +// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. +var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; - return L.latLng(lat, lng, alt); - }, +// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. +var gecko3d = 'MozPerspective' in style$1; - // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds - // Returns a `LatLngBounds` with the same size as the given one, ensuring - // that its center is within the CRS's bounds. - // Only accepts actual `L.LatLngBounds` instances, not arrays. - wrapLatLngBounds: function (bounds) { - var center = bounds.getCenter(), - newCenter = this.wrapLatLng(center), - latShift = center.lat - newCenter.lat, - lngShift = center.lng - newCenter.lng; +// @property any3d: Boolean +// `true` for all browsers supporting CSS transforms. +var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; - if (latShift === 0 && lngShift === 0) { - return bounds; - } +// @property mobile: Boolean; `true` for all browsers running in a mobile device. +var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile'); - var sw = bounds.getSouthWest(), - ne = bounds.getNorthEast(), - newSw = L.latLng({lat: sw.lat - latShift, lng: sw.lng - lngShift}), - newNe = L.latLng({lat: ne.lat - latShift, lng: ne.lng - lngShift}); +// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device. +var mobileWebkit = mobile && webkit; + +// @property mobileWebkit3d: Boolean +// `true` for all webkit-based browsers in a mobile device supporting CSS transforms. +var mobileWebkit3d = mobile && webkit3d; + +// @property msPointer: Boolean +// `true` for browsers implementing the Microsoft touch events model (notably IE10). +var msPointer = !window.PointerEvent && window.MSPointerEvent; + +// @property pointer: Boolean +// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). +var pointer = !!(window.PointerEvent || msPointer); + +// @property touch: Boolean +// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). +// This does not necessarily mean that the browser is running in a computer with +// a touchscreen, it only means that the browser is capable of understanding +// touch events. +var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window || + (window.DocumentTouch && document instanceof window.DocumentTouch)); + +// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. +var mobileOpera = mobile && opera; + +// @property mobileGecko: Boolean +// `true` for gecko-based browsers running in a mobile device. +var mobileGecko = mobile && gecko; + +// @property retina: Boolean +// `true` for browsers on a high-resolution "retina" screen. +var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; - return new L.LatLngBounds(newSw, newNe); + +// @property canvas: Boolean +// `true` when the browser supports [``](https://developer.mozilla.org/docs/Web/API/Canvas_API). +var canvas = (function () { + return !!document.createElement('canvas').getContext; +}()); + +// @property svg: Boolean +// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). +var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect); + +// @property vml: Boolean +// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). +var vml = !svg && (function () { + try { + var div = document.createElement('div'); + div.innerHTML = ''; + + var shape = div.firstChild; + shape.style.behavior = 'url(#default#VML)'; + + return shape && (typeof shape.adj === 'object'); + + } catch (e) { + return false; } -}; +}()); + + +function userAgentContains(str) { + return navigator.userAgent.toLowerCase().indexOf(str) >= 0; +} +var Browser = (Object.freeze || Object)({ + ie: ie, + ielt9: ielt9, + edge: edge, + webkit: webkit, + android: android, + android23: android23, + opera: opera, + chrome: chrome, + gecko: gecko, + safari: safari, + phantom: phantom, + opera12: opera12, + win: win, + ie3d: ie3d, + webkit3d: webkit3d, + gecko3d: gecko3d, + any3d: any3d, + mobile: mobile, + mobileWebkit: mobileWebkit, + mobileWebkit3d: mobileWebkit3d, + msPointer: msPointer, + pointer: pointer, + touch: touch, + mobileOpera: mobileOpera, + mobileGecko: mobileGecko, + retina: retina, + canvas: canvas, + svg: svg, + vml: vml +}); /* - * @namespace CRS - * @crs L.CRS.Simple - * - * A simple CRS that maps longitude and latitude into `x` and `y` directly. - * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` - * axis should still be inverted (going from bottom to top). `distance()` returns - * simple euclidean distance. + * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. */ -L.CRS.Simple = L.extend({}, L.CRS, { - projection: L.Projection.LonLat, - transformation: new L.Transformation(1, 0, -1, 0), - scale: function (zoom) { - return Math.pow(2, zoom); - }, +var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown'; +var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove'; +var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup'; +var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel'; +var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION']; +var _pointers = {}; +var _pointerDocListener = false; - zoom: function (scale) { - return Math.log(scale) / Math.LN2; - }, +// DomEvent.DoubleTap needs to know about this +var _pointersCount = 0; - distance: function (latlng1, latlng2) { - var dx = latlng2.lng - latlng1.lng, - dy = latlng2.lat - latlng1.lat; +// Provides a touch events wrapper for (ms)pointer events. +// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 - return Math.sqrt(dx * dx + dy * dy); - }, +function addPointerListener(obj, type, handler, id) { + if (type === 'touchstart') { + _addPointerStart(obj, handler, id); - infinite: true -}); + } else if (type === 'touchmove') { + _addPointerMove(obj, handler, id); + } else if (type === 'touchend') { + _addPointerEnd(obj, handler, id); + } + return this; +} -/* - * @namespace CRS - * @crs L.CRS.Earth - * - * Serves as the base for CRS that are global such that they cover the earth. - * Can only be used as the base for other CRS and cannot be used directly, - * since it does not have a `code`, `projection` or `transformation`. `distance()` returns - * meters. - */ +function removePointerListener(obj, type, id) { + var handler = obj['_leaflet_' + type + id]; -L.CRS.Earth = L.extend({}, L.CRS, { - wrapLng: [-180, 180], + if (type === 'touchstart') { + obj.removeEventListener(POINTER_DOWN, handler, false); - // Mean Earth Radius, as recommended for use by - // the International Union of Geodesy and Geophysics, - // see http://rosettacode.org/wiki/Haversine_formula - R: 6371000, + } else if (type === 'touchmove') { + obj.removeEventListener(POINTER_MOVE, handler, false); - // distance between two geographical points using spherical law of cosines approximation - distance: function (latlng1, latlng2) { - var rad = Math.PI / 180, - lat1 = latlng1.lat * rad, - lat2 = latlng2.lat * rad, - a = Math.sin(lat1) * Math.sin(lat2) + - Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad); + } else if (type === 'touchend') { + obj.removeEventListener(POINTER_UP, handler, false); + obj.removeEventListener(POINTER_CANCEL, handler, false); + } - return this.R * Math.acos(Math.min(a, 1)); + return this; +} + +function _addPointerStart(obj, handler, id) { + var onDown = bind(function (e) { + if (e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + // In IE11, some touch events needs to fire for form controls, or + // the controls will stop working. We keep a whitelist of tag names that + // need these events. For other target tags, we prevent default on the event. + if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) { + preventDefault(e); + } else { + return; + } + } + + _handlePointer(e, handler); + }); + + obj['_leaflet_touchstart' + id] = onDown; + obj.addEventListener(POINTER_DOWN, onDown, false); + + // need to keep track of what pointers and how many are active to provide e.touches emulation + if (!_pointerDocListener) { + // we listen documentElement as any drags that end by moving the touch off the screen get fired there + document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true); + document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true); + document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true); + document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true); + + _pointerDocListener = true; } -}); +} +function _globalPointerDown(e) { + _pointers[e.pointerId] = e; + _pointersCount++; +} +function _globalPointerMove(e) { + if (_pointers[e.pointerId]) { + _pointers[e.pointerId] = e; + } +} -/* - * @namespace CRS - * @crs L.CRS.EPSG3857 - * - * The most common CRS for online maps, used by almost all free and commercial - * tile providers. Uses Spherical Mercator projection. Set in by default in - * Map's `crs` option. - */ - -L.CRS.EPSG3857 = L.extend({}, L.CRS.Earth, { - code: 'EPSG:3857', - projection: L.Projection.SphericalMercator, - - transformation: (function () { - var scale = 0.5 / (Math.PI * L.Projection.SphericalMercator.R); - return new L.Transformation(scale, 0.5, -scale, 0.5); - }()) -}); - -L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, { - code: 'EPSG:900913' -}); +function _globalPointerUp(e) { + delete _pointers[e.pointerId]; + _pointersCount--; +} +function _handlePointer(e, handler) { + e.touches = []; + for (var i in _pointers) { + e.touches.push(_pointers[i]); + } + e.changedTouches = [e]; + handler(e); +} -/* - * @namespace CRS - * @crs L.CRS.EPSG4326 - * - * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. - * - * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic), - * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer` - * with this CRS, ensure that there are two 256x256 pixel tiles covering the - * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90), - * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set. - */ - -L.CRS.EPSG4326 = L.extend({}, L.CRS.Earth, { - code: 'EPSG:4326', - projection: L.Projection.LonLat, - transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5) -}); +function _addPointerMove(obj, handler, id) { + var onMove = function (e) { + // don't fire touch moves when mouse isn't down + if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } + + _handlePointer(e, handler); + }; + + obj['_leaflet_touchmove' + id] = onMove; + obj.addEventListener(POINTER_MOVE, onMove, false); +} +function _addPointerEnd(obj, handler, id) { + var onUp = function (e) { + _handlePointer(e, handler); + }; + obj['_leaflet_touchend' + id] = onUp; + obj.addEventListener(POINTER_UP, onUp, false); + obj.addEventListener(POINTER_CANCEL, onUp, false); +} /* - * @class Map - * @aka L.Map - * @inherits Evented - * - * The central class of the API — it is used to create a map on a page and manipulate it. - * - * @example - * - * ```js - * // initialize the map on the "map" div with a given center and zoom - * var map = L.map('map', { - * center: [51.505, -0.09], - * zoom: 13 - * }); - * ``` - * + * Extends the event handling code with double tap support for mobile browsers. */ -L.Map = L.Evented.extend({ +var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart'; +var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend'; +var _pre = '_leaflet_'; - options: { - // @section Map State Options - // @option crs: CRS = L.CRS.EPSG3857 - // The [Coordinate Reference System](#crs) to use. Don't change this if you're not - // sure what it means. - crs: L.CRS.EPSG3857, +// inspired by Zepto touch code by Thomas Fuchs +function addDoubleTapListener(obj, handler, id) { + var last, touch$$1, + doubleTap = false, + delay = 250; - // @option center: LatLng = undefined - // Initial geographic center of the map - center: undefined, + function onTouchStart(e) { + var count; - // @option zoom: Number = undefined - // Initial map zoom level - zoom: undefined, + if (pointer) { + if ((!edge) || e.pointerType === 'mouse') { return; } + count = _pointersCount; + } else { + count = e.touches.length; + } - // @option minZoom: Number = undefined - // Minimum zoom level of the map. Overrides any `minZoom` option set on map layers. - minZoom: undefined, + if (count > 1) { return; } - // @option maxZoom: Number = undefined - // Maximum zoom level of the map. Overrides any `maxZoom` option set on map layers. - maxZoom: undefined, + var now = Date.now(), + delta = now - (last || now); - // @option layers: Layer[] = [] - // Array of layers that will be added to the map initially - layers: [], + touch$$1 = e.touches ? e.touches[0] : e; + doubleTap = (delta > 0 && delta <= delay); + last = now; + } - // @option maxBounds: LatLngBounds = null - // When this option is set, the map restricts the view to the given - // geographical bounds, bouncing the user back if the user tries to pan - // outside the view. To set the restriction dynamically, use - // [`setMaxBounds`](#map-setmaxbounds) method. - maxBounds: undefined, + function onTouchEnd(e) { + if (doubleTap && !touch$$1.cancelBubble) { + if (pointer) { + if ((!edge) || e.pointerType === 'mouse') { return; } + // work around .type being readonly with MSPointer* events + var newTouch = {}, + prop, i; + + for (i in touch$$1) { + prop = touch$$1[i]; + newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop; + } + touch$$1 = newTouch; + } + touch$$1.type = 'dblclick'; + handler(touch$$1); + last = null; + } + } - // @option renderer: Renderer = * - // The default method for drawing vector layers on the map. `L.SVG` - // or `L.Canvas` by default depending on browser support. - renderer: undefined, + obj[_pre + _touchstart + id] = onTouchStart; + obj[_pre + _touchend + id] = onTouchEnd; + obj[_pre + 'dblclick' + id] = handler; + obj.addEventListener(_touchstart, onTouchStart, false); + obj.addEventListener(_touchend, onTouchEnd, false); - // @section Animation Options - // @option zoomAnimation: Boolean = true - // Whether the map zoom animation is enabled. By default it's enabled - // in all browsers that support CSS3 Transitions except Android. - zoomAnimation: true, + // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse), + // the browser doesn't fire touchend/pointerup events but does fire + // native dblclicks. See #4127. + // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180. + obj.addEventListener('dblclick', handler, false); - // @option zoomAnimationThreshold: Number = 4 - // Won't animate zoom if the zoom difference exceeds this value. - zoomAnimationThreshold: 4, + return this; +} - // @option fadeAnimation: Boolean = true - // Whether the tile fade animation is enabled. By default it's enabled - // in all browsers that support CSS3 Transitions except Android. - fadeAnimation: true, +function removeDoubleTapListener(obj, id) { + var touchstart = obj[_pre + _touchstart + id], + touchend = obj[_pre + _touchend + id], + dblclick = obj[_pre + 'dblclick' + id]; - // @option markerZoomAnimation: Boolean = true - // Whether markers animate their zoom with the zoom animation, if disabled - // they will disappear for the length of the animation. By default it's - // enabled in all browsers that support CSS3 Transitions except Android. - markerZoomAnimation: true, + obj.removeEventListener(_touchstart, touchstart, false); + obj.removeEventListener(_touchend, touchend, false); + if (!edge) { + obj.removeEventListener('dblclick', dblclick, false); + } - // @option transform3DLimit: Number = 2^23 - // Defines the maximum size of a CSS translation transform. The default - // value should not be changed unless a web browser positions layers in - // the wrong place after doing a large `panBy`. - transform3DLimit: 8388608, // Precision limit of a 32-bit float + return this; +} + +/* + * @namespace DomEvent + * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally. + */ - // @section Interaction Options - // @option zoomSnap: Number = 1 - // Forces the map's zoom level to always be a multiple of this, particularly - // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. - // By default, the zoom level snaps to the nearest integer; lower values - // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` - // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. - zoomSnap: 1, +// Inspired by John Resig, Dean Edwards and YUI addEvent implementations. - // @option zoomDelta: Number = 1 - // Controls how much the map's zoom level will change after a - // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` - // or `-` on the keyboard, or using the [zoom controls](#control-zoom). - // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. - zoomDelta: 1, +// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this +// Adds a listener function (`fn`) to a particular DOM event type of the +// element `el`. You can optionally specify the context of the listener +// (object the `this` keyword will point to). You can also pass several +// space-separated types (e.g. `'click dblclick'`). - // @option trackResize: Boolean = true - // Whether the map automatically handles browser window resize to update itself. - trackResize: true - }, +// @alternative +// @function on(el: HTMLElement, eventMap: Object, context?: Object): this +// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` +function on(obj, types, fn, context) { - initialize: function (id, options) { // (HTMLElement or String, Object) - options = L.setOptions(this, options); + if (typeof types === 'object') { + for (var type in types) { + addOne(obj, type, types[type], fn); + } + } else { + types = splitWords(types); - this._initContainer(id); - this._initLayout(); + for (var i = 0, len = types.length; i < len; i++) { + addOne(obj, types[i], fn, context); + } + } - // hack for https://github.com/Leaflet/Leaflet/issues/1980 - this._onResize = L.bind(this._onResize, this); + return this; +} - this._initEvents(); +var eventsKey = '_leaflet_events'; - if (options.maxBounds) { - this.setMaxBounds(options.maxBounds); - } +// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this +// Removes a previously added listener function. If no function is specified, +// it will remove all the listeners of that particular DOM event from the element. +// Note that if you passed a custom context to on, you must pass the same +// context to `off` in order to remove the listener. - if (options.zoom !== undefined) { - this._zoom = this._limitZoom(options.zoom); +// @alternative +// @function off(el: HTMLElement, eventMap: Object, context?: Object): this +// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` + +// @alternative +// @function off(el: HTMLElement): this +// Removes all known event listeners +function off(obj, types, fn, context) { + + if (typeof types === 'object') { + for (var type in types) { + removeOne(obj, type, types[type], fn); } + } else if (types) { + types = splitWords(types); - if (options.center && options.zoom !== undefined) { - this.setView(L.latLng(options.center), options.zoom, {reset: true}); + for (var i = 0, len = types.length; i < len; i++) { + removeOne(obj, types[i], fn, context); + } + } else { + for (var j in obj[eventsKey]) { + removeOne(obj, j, obj[eventsKey][j]); } + delete obj[eventsKey]; + } +} - this._handlers = []; - this._layers = {}; - this._zoomBoundLayers = {}; - this._sizeChanged = true; +function addOne(obj, type, fn, context) { + var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''); - this.callInitHooks(); + if (obj[eventsKey] && obj[eventsKey][id]) { return this; } - // don't animate on browsers without hardware-accelerated transitions or old Android/Opera - this._zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera && - this.options.zoomAnimation; + var handler = function (e) { + return fn.call(context || obj, e || window.event); + }; - // zoom transitions run with the same duration for all layers, so if one of transitionend events - // happens after starting zoom animation (propagating to the map pane), we know that it ended globally - if (this._zoomAnimated) { - this._createAnimProxy(); - L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this); + var originalHandler = handler; + + if (pointer && type.indexOf('touch') === 0) { + // Needs DomEvent.Pointer.js + addPointerListener(obj, type, handler, id); + + } else if (touch && (type === 'dblclick') && addDoubleTapListener && + !(pointer && chrome)) { + // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener + // See #5180 + addDoubleTapListener(obj, handler, id); + + } else if ('addEventListener' in obj) { + + if (type === 'mousewheel') { + obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); + + } else if ((type === 'mouseenter') || (type === 'mouseleave')) { + handler = function (e) { + e = e || window.event; + if (isExternalTarget(obj, e)) { + originalHandler(e); + } + }; + obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false); + + } else { + if (type === 'click' && android) { + handler = function (e) { + filterClick(e, originalHandler); + }; + } + obj.addEventListener(type, handler, false); } - this._addLayers(this.options.layers); - }, + } else if ('attachEvent' in obj) { + obj.attachEvent('on' + type, handler); + } + obj[eventsKey] = obj[eventsKey] || {}; + obj[eventsKey][id] = handler; +} - // @section Methods for modifying map state +function removeOne(obj, type, fn, context) { - // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this - // Sets the view of the map (geographical center and zoom) with the given - // animation options. - setView: function (center, zoom, options) { + var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''), + handler = obj[eventsKey] && obj[eventsKey][id]; - zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); - center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds); - options = options || {}; + if (!handler) { return this; } - this._stop(); + if (pointer && type.indexOf('touch') === 0) { + removePointerListener(obj, type, id); - if (this._loaded && !options.reset && options !== true) { + } else if (touch && (type === 'dblclick') && removeDoubleTapListener) { + removeDoubleTapListener(obj, id); - if (options.animate !== undefined) { - options.zoom = L.extend({animate: options.animate}, options.zoom); - options.pan = L.extend({animate: options.animate, duration: options.duration}, options.pan); - } + } else if ('removeEventListener' in obj) { - // try animating pan or zoom - var moved = (this._zoom !== zoom) ? - this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : - this._tryAnimatedPan(center, options.pan); + if (type === 'mousewheel') { + obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); - if (moved) { - // prevent resize handler call, the view will refresh after animation anyway - clearTimeout(this._sizeTimer); - return this; - } + } else { + obj.removeEventListener( + type === 'mouseenter' ? 'mouseover' : + type === 'mouseleave' ? 'mouseout' : type, handler, false); } - // animation didn't start, just reset the map view - this._resetView(center, zoom); + } else if ('detachEvent' in obj) { + obj.detachEvent('on' + type, handler); + } - return this; - }, + obj[eventsKey][id] = null; +} - // @method setZoom(zoom: Number, options: Zoom/pan options): this - // Sets the zoom of the map. - setZoom: function (zoom, options) { - if (!this._loaded) { - this._zoom = zoom; - return this; - } - return this.setView(this.getCenter(), zoom, {zoom: options}); - }, +// @function stopPropagation(ev: DOMEvent): this +// Stop the given event from propagation to parent elements. Used inside the listener functions: +// ```js +// L.DomEvent.on(div, 'click', function (ev) { +// L.DomEvent.stopPropagation(ev); +// }); +// ``` +function stopPropagation(e) { + + if (e.stopPropagation) { + e.stopPropagation(); + } else if (e.originalEvent) { // In case of Leaflet event. + e.originalEvent._stopped = true; + } else { + e.cancelBubble = true; + } + skipped(e); - // @method zoomIn(delta?: Number, options?: Zoom options): this - // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). - zoomIn: function (delta, options) { - delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1); - return this.setZoom(this._zoom + delta, options); - }, + return this; +} - // @method zoomOut(delta?: Number, options?: Zoom options): this - // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). - zoomOut: function (delta, options) { - delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1); - return this.setZoom(this._zoom - delta, options); - }, +// @function disableScrollPropagation(el: HTMLElement): this +// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants). +function disableScrollPropagation(el) { + return addOne(el, 'mousewheel', stopPropagation); +} - // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this - // Zooms the map while keeping a specified geographical point on the map - // stationary (e.g. used internally for scroll zoom and double-click zoom). - // @alternative - // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this - // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. - setZoomAround: function (latlng, zoom, options) { - var scale = this.getZoomScale(zoom), - viewHalf = this.getSize().divideBy(2), - containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng), +// @function disableClickPropagation(el: HTMLElement): this +// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`, +// `'mousedown'` and `'touchstart'` events (plus browser variants). +function disableClickPropagation(el) { + on(el, 'mousedown touchstart dblclick', stopPropagation); + addOne(el, 'click', fakeStop); + return this; +} - centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), - newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); +// @function preventDefault(ev: DOMEvent): this +// Prevents the default action of the DOM Event `ev` from happening (such as +// following a link in the href of the a element, or doing a POST request +// with page reload when a `
` is submitted). +// Use it inside listener functions. +function preventDefault(e) { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return this; +} - return this.setView(newCenter, zoom, {zoom: options}); - }, +// @function stop(ev): this +// Does `stopPropagation` and `preventDefault` at the same time. +function stop(e) { + preventDefault(e); + stopPropagation(e); + return this; +} - _getBoundsCenterZoom: function (bounds, options) { +// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point +// Gets normalized mouse position from a DOM event relative to the +// `container` or to the whole page if not specified. +function getMousePosition(e, container) { + if (!container) { + return new Point(e.clientX, e.clientY); + } - options = options || {}; - bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds); + var rect = container.getBoundingClientRect(); - var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]), - paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]), + return new Point( + e.clientX - rect.left - container.clientLeft, + e.clientY - rect.top - container.clientTop); +} - zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); +// Chrome on Win scrolls double the pixels as in other platforms (see #4538), +// and Firefox scrolls device pixels, not CSS pixels +var wheelPxFactor = + (win && chrome) ? 2 * window.devicePixelRatio : + gecko ? window.devicePixelRatio : 1; + +// @function getWheelDelta(ev: DOMEvent): Number +// Gets normalized wheel delta from a mousewheel DOM event, in vertical +// pixels scrolled (negative if scrolling down). +// Events from pointing devices without precise scrolling are mapped to +// a best guess of 60 pixels. +function getWheelDelta(e) { + return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta + (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels + (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines + (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages + (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events + e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels + (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines + e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages + 0; +} - zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; +var skipEvents = {}; - var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), +function fakeStop(e) { + // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e) + skipEvents[e.type] = true; +} - swPoint = this.project(bounds.getSouthWest(), zoom), - nePoint = this.project(bounds.getNorthEast(), zoom), - center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); +function skipped(e) { + var events = skipEvents[e.type]; + // reset when checking, as it's only used in map container and propagates outside of the map + skipEvents[e.type] = false; + return events; +} - return { - center: center, - zoom: zoom - }; - }, +// check if element really left/entered the event target (for mouseenter/mouseleave) +function isExternalTarget(el, e) { - // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this - // Sets a map view that contains the given geographical bounds with the - // maximum zoom level possible. - fitBounds: function (bounds, options) { + var related = e.relatedTarget; - bounds = L.latLngBounds(bounds); + if (!related) { return true; } - if (!bounds.isValid()) { - throw new Error('Bounds are not valid.'); + try { + while (related && (related !== el)) { + related = related.parentNode; } + } catch (err) { + return false; + } + return (related !== el); +} - var target = this._getBoundsCenterZoom(bounds, options); - return this.setView(target.center, target.zoom, options); - }, - - // @method fitWorld(options?: fitBounds options): this - // Sets a map view that mostly contains the whole world with the maximum - // zoom level possible. - fitWorld: function (options) { - return this.fitBounds([[-90, -180], [90, 180]], options); - }, +var lastClick; - // @method panTo(latlng: LatLng, options?: Pan options): this - // Pans the map to a given center. - panTo: function (center, options) { // (LatLng) - return this.setView(center, this._zoom, {pan: options}); - }, +// this is a horrible workaround for a bug in Android where a single touch triggers two click events +function filterClick(e, handler) { + var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)), + elapsed = lastClick && (timeStamp - lastClick); - // @method panBy(offset: Point): this - // Pans the map by a given number of pixels (animated). - panBy: function (offset, options) { - offset = L.point(offset).round(); - options = options || {}; + // are they closer together than 500ms yet more than 100ms? + // Android typically triggers them ~300ms apart while multiple listeners + // on the same event should be triggered far faster; + // or check if click is simulated on the element, and if it is, reject any non-simulated events - if (!offset.x && !offset.y) { - return this.fire('moveend'); - } - // If we pan too far, Chrome gets issues with tiles - // and makes them disappear or appear in the wrong place (slightly offset) #2602 - if (options.animate !== true && !this.getSize().contains(offset)) { - this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); - return this; - } + if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { + stop(e); + return; + } + lastClick = timeStamp; - if (!this._panAnim) { - this._panAnim = new L.PosAnimation(); + handler(e); +} - this._panAnim.on({ - 'step': this._onPanTransitionStep, - 'end': this._onPanTransitionEnd - }, this); - } - // don't fire movestart if animating inertia - if (!options.noMoveStart) { - this.fire('movestart'); - } + + +var DomEvent = (Object.freeze || Object)({ + on: on, + off: off, + stopPropagation: stopPropagation, + disableScrollPropagation: disableScrollPropagation, + disableClickPropagation: disableClickPropagation, + preventDefault: preventDefault, + stop: stop, + getMousePosition: getMousePosition, + getWheelDelta: getWheelDelta, + fakeStop: fakeStop, + skipped: skipped, + isExternalTarget: isExternalTarget, + addListener: on, + removeListener: off +}); + +/* + * @namespace DomUtil + * + * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model) + * tree, used by Leaflet internally. + * + * Most functions expecting or returning a `HTMLElement` also work for + * SVG elements. The only difference is that classes refer to CSS classes + * in HTML and SVG classes in SVG. + */ - // animate pan unless animate: false specified - if (options.animate !== false) { - L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim'); - var newPos = this._getMapPanePos().subtract(offset).round(); - this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); - } else { - this._rawPanBy(offset); - this.fire('move').fire('moveend'); - } +// @property TRANSFORM: String +// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit). +var TRANSFORM = testProp( + ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']); - return this; - }, +// webkitTransition comes first because some browser versions that drop vendor prefix don't do +// the same for the transitionend event, in particular the Android 4.1 stock browser - // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this - // Sets the view of the map (geographical center and zoom) performing a smooth - // pan-zoom animation. - flyTo: function (targetCenter, targetZoom, options) { +// @property TRANSITION: String +// Vendor-prefixed transition style name. +var TRANSITION = testProp( + ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); - options = options || {}; - if (options.animate === false || !L.Browser.any3d) { - return this.setView(targetCenter, targetZoom, options); - } +// @property TRANSITION_END: String +// Vendor-prefixed transitionend event name. +var TRANSITION_END = + TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend'; - this._stop(); - var from = this.project(this.getCenter()), - to = this.project(targetCenter), - size = this.getSize(), - startZoom = this._zoom; +// @function get(id: String|HTMLElement): HTMLElement +// Returns an element given its DOM id, or returns the element itself +// if it was passed directly. +function get(id) { + return typeof id === 'string' ? document.getElementById(id) : id; +} - targetCenter = L.latLng(targetCenter); - targetZoom = targetZoom === undefined ? startZoom : targetZoom; +// @function getStyle(el: HTMLElement, styleAttrib: String): String +// Returns the value for a certain style attribute on an element, +// including computed values or values set through CSS. +function getStyle(el, style) { + var value = el.style[style] || (el.currentStyle && el.currentStyle[style]); - var w0 = Math.max(size.x, size.y), - w1 = w0 * this.getZoomScale(startZoom, targetZoom), - u1 = (to.distanceTo(from)) || 1, - rho = 1.42, - rho2 = rho * rho; + if ((!value || value === 'auto') && document.defaultView) { + var css = document.defaultView.getComputedStyle(el, null); + value = css ? css[style] : null; + } + return value === 'auto' ? null : value; +} - function r(i) { - var s1 = i ? -1 : 1, - s2 = i ? w1 : w0, - t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, - b1 = 2 * s2 * rho2 * u1, - b = t1 / b1, - sq = Math.sqrt(b * b + 1) - b; +// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement +// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element. +function create$1(tagName, className, container) { + var el = document.createElement(tagName); + el.className = className || ''; - // workaround for floating point precision bug when sq = 0, log = -Infinite, - // thus triggering an infinite loop in flyTo - var log = sq < 0.000000001 ? -18 : Math.log(sq); + if (container) { + container.appendChild(el); + } + return el; +} - return log; - } +// @function remove(el: HTMLElement) +// Removes `el` from its parent element +function remove(el) { + var parent = el.parentNode; + if (parent) { + parent.removeChild(el); + } +} - function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } - function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } - function tanh(n) { return sinh(n) / cosh(n); } +// @function empty(el: HTMLElement) +// Removes all of `el`'s children elements from `el` +function empty(el) { + while (el.firstChild) { + el.removeChild(el.firstChild); + } +} - var r0 = r(0); +// @function toFront(el: HTMLElement) +// Makes `el` the last child of its parent, so it renders in front of the other children. +function toFront(el) { + var parent = el.parentNode; + if (parent.lastChild !== el) { + parent.appendChild(el); + } +} - function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } - function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } +// @function toBack(el: HTMLElement) +// Makes `el` the first child of its parent, so it renders behind the other children. +function toBack(el) { + var parent = el.parentNode; + if (parent.firstChild !== el) { + parent.insertBefore(el, parent.firstChild); + } +} - function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } +// @function hasClass(el: HTMLElement, name: String): Boolean +// Returns `true` if the element's class attribute contains `name`. +function hasClass(el, name) { + if (el.classList !== undefined) { + return el.classList.contains(name); + } + var className = getClass(el); + return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className); +} - var start = Date.now(), - S = (r(1) - r0) / rho, - duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; +// @function addClass(el: HTMLElement, name: String) +// Adds `name` to the element's class attribute. +function addClass(el, name) { + if (el.classList !== undefined) { + var classes = splitWords(name); + for (var i = 0, len = classes.length; i < len; i++) { + el.classList.add(classes[i]); + } + } else if (!hasClass(el, name)) { + var className = getClass(el); + setClass(el, (className ? className + ' ' : '') + name); + } +} - function frame() { - var t = (Date.now() - start) / duration, - s = easeOut(t) * S; +// @function removeClass(el: HTMLElement, name: String) +// Removes `name` from the element's class attribute. +function removeClass(el, name) { + if (el.classList !== undefined) { + el.classList.remove(name); + } else { + setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' '))); + } +} - if (t <= 1) { - this._flyToFrame = L.Util.requestAnimFrame(frame, this); +// @function setClass(el: HTMLElement, name: String) +// Sets the element's class. +function setClass(el, name) { + if (el.className.baseVal === undefined) { + el.className = name; + } else { + // in case of SVG element + el.className.baseVal = name; + } +} - this._move( - this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), - this.getScaleZoom(w0 / w(s), startZoom), - {flyTo: true}); +// @function getClass(el: HTMLElement): String +// Returns the element's class. +function getClass(el) { + return el.className.baseVal === undefined ? el.className : el.className.baseVal; +} - } else { - this - ._move(targetCenter, targetZoom) - ._moveEnd(true); - } - } +// @function setOpacity(el: HTMLElement, opacity: Number) +// Set the opacity of an element (including old IE support). +// `opacity` must be a number from `0` to `1`. +function setOpacity(el, value) { + if ('opacity' in el.style) { + el.style.opacity = value; + } else if ('filter' in el.style) { + _setOpacityIE(el, value); + } +} - this._moveStart(true); +function _setOpacityIE(el, value) { + var filter = false, + filterName = 'DXImageTransform.Microsoft.Alpha'; + + // filters collection throws an error if we try to retrieve a filter that doesn't exist + try { + filter = el.filters.item(filterName); + } catch (e) { + // don't set opacity to 1 if we haven't already set an opacity, + // it isn't needed and breaks transparent pngs. + if (value === 1) { return; } + } - frame.call(this); - return this; - }, + value = Math.round(value * 100); - // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this - // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), - // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). - flyToBounds: function (bounds, options) { - var target = this._getBoundsCenterZoom(bounds, options); - return this.flyTo(target.center, target.zoom, options); - }, + if (filter) { + filter.Enabled = (value !== 100); + filter.Opacity = value; + } else { + el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; + } +} - // @method setMaxBounds(bounds: Bounds): this - // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). - setMaxBounds: function (bounds) { - bounds = L.latLngBounds(bounds); +// @function testProp(props: String[]): String|false +// Goes through the array of style names and returns the first name +// that is a valid style name for an element. If no such name is found, +// it returns false. Useful for vendor-prefixed styles like `transform`. +function testProp(props) { + var style = document.documentElement.style; - if (!bounds.isValid()) { - this.options.maxBounds = null; - return this.off('moveend', this._panInsideMaxBounds); - } else if (this.options.maxBounds) { - this.off('moveend', this._panInsideMaxBounds); + for (var i = 0; i < props.length; i++) { + if (props[i] in style) { + return props[i]; } + } + return false; +} - this.options.maxBounds = bounds; +// @function setTransform(el: HTMLElement, offset: Point, scale?: Number) +// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels +// and optionally scaled by `scale`. Does not have an effect if the +// browser doesn't support 3D CSS transforms. +function setTransform(el, offset, scale) { + var pos = offset || new Point(0, 0); + + el.style[TRANSFORM] = + (ie3d ? + 'translate(' + pos.x + 'px,' + pos.y + 'px)' : + 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') + + (scale ? ' scale(' + scale + ')' : ''); +} - if (this._loaded) { - this._panInsideMaxBounds(); - } +// @function setPosition(el: HTMLElement, position: Point) +// Sets the position of `el` to coordinates specified by `position`, +// using CSS translate or top/left positioning depending on the browser +// (used by Leaflet internally to position its layers). +function setPosition(el, point) { - return this.on('moveend', this._panInsideMaxBounds); - }, + /*eslint-disable */ + el._leaflet_pos = point; + /*eslint-enable */ - // @method setMinZoom(zoom: Number): this - // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). - setMinZoom: function (zoom) { - this.options.minZoom = zoom; + if (any3d) { + setTransform(el, point); + } else { + el.style.left = point.x + 'px'; + el.style.top = point.y + 'px'; + } +} - if (this._loaded && this.getZoom() < this.options.minZoom) { - return this.setZoom(zoom); - } +// @function getPosition(el: HTMLElement): Point +// Returns the coordinates of an element previously positioned with setPosition. +function getPosition(el) { + // this method is only used for elements previously positioned using setPosition, + // so it's safe to cache the position for performance - return this; - }, + return el._leaflet_pos || new Point(0, 0); +} - // @method setMaxZoom(zoom: Number): this - // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). - setMaxZoom: function (zoom) { - this.options.maxZoom = zoom; +// @function disableTextSelection() +// Prevents the user from generating `selectstart` DOM events, usually generated +// when the user drags the mouse through a page with text. Used internally +// by Leaflet to override the behaviour of any click-and-drag interaction on +// the map. Affects drag interactions on the whole document. + +// @function enableTextSelection() +// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection). +var disableTextSelection; +var enableTextSelection; +var _userSelect; +if ('onselectstart' in document) { + disableTextSelection = function () { + on(window, 'selectstart', preventDefault); + }; + enableTextSelection = function () { + off(window, 'selectstart', preventDefault); + }; +} else { + var userSelectProperty = testProp( + ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); - if (this._loaded && (this.getZoom() > this.options.maxZoom)) { - return this.setZoom(zoom); + disableTextSelection = function () { + if (userSelectProperty) { + var style = document.documentElement.style; + _userSelect = style[userSelectProperty]; + style[userSelectProperty] = 'none'; + } + }; + enableTextSelection = function () { + if (userSelectProperty) { + document.documentElement.style[userSelectProperty] = _userSelect; + _userSelect = undefined; } + }; +} - return this; - }, +// @function disableImageDrag() +// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but +// for `dragstart` DOM events, usually generated when the user drags an image. +function disableImageDrag() { + on(window, 'dragstart', preventDefault); +} - // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this - // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any. - panInsideBounds: function (bounds, options) { - this._enforcingBounds = true; - var center = this.getCenter(), - newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds)); +// @function enableImageDrag() +// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection). +function enableImageDrag() { + off(window, 'dragstart', preventDefault); +} - if (!center.equals(newCenter)) { - this.panTo(newCenter, options); - } +var _outlineElement; +var _outlineStyle; +// @function preventOutline(el: HTMLElement) +// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline) +// of the element `el` invisible. Used internally by Leaflet to prevent +// focusable elements from displaying an outline when the user performs a +// drag interaction on them. +function preventOutline(element) { + while (element.tabIndex === -1) { + element = element.parentNode; + } + if (!element.style) { return; } + restoreOutline(); + _outlineElement = element; + _outlineStyle = element.style.outline; + element.style.outline = 'none'; + on(window, 'keydown', restoreOutline); +} - this._enforcingBounds = false; - return this; - }, +// @function restoreOutline() +// Cancels the effects of a previous [`L.DomUtil.preventOutline`](). +function restoreOutline() { + if (!_outlineElement) { return; } + _outlineElement.style.outline = _outlineStyle; + _outlineElement = undefined; + _outlineStyle = undefined; + off(window, 'keydown', restoreOutline); +} + + +var DomUtil = (Object.freeze || Object)({ + TRANSFORM: TRANSFORM, + TRANSITION: TRANSITION, + TRANSITION_END: TRANSITION_END, + get: get, + getStyle: getStyle, + create: create$1, + remove: remove, + empty: empty, + toFront: toFront, + toBack: toBack, + hasClass: hasClass, + addClass: addClass, + removeClass: removeClass, + setClass: setClass, + getClass: getClass, + setOpacity: setOpacity, + testProp: testProp, + setTransform: setTransform, + setPosition: setPosition, + getPosition: getPosition, + disableTextSelection: disableTextSelection, + enableTextSelection: enableTextSelection, + disableImageDrag: disableImageDrag, + enableImageDrag: enableImageDrag, + preventOutline: preventOutline, + restoreOutline: restoreOutline +}); + +/* + * @class PosAnimation + * @aka L.PosAnimation + * @inherits Evented + * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. + * + * @example + * ```js + * var fx = new L.PosAnimation(); + * fx.run(el, [300, 500], 0.5); + * ``` + * + * @constructor L.PosAnimation() + * Creates a `PosAnimation` object. + * + */ + +var PosAnimation = Evented.extend({ + + // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) + // Run an animation of a given element to a new position, optionally setting + // duration in seconds (`0.25` by default) and easing linearity factor (3rd + // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1), + // `0.5` by default). + run: function (el, newPos, duration, easeLinearity) { + this.stop(); + + this._el = el; + this._inProgress = true; + this._duration = duration || 0.25; + this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); + + this._startPos = getPosition(el); + this._offset = newPos.subtract(this._startPos); + this._startTime = +new Date(); + + // @event start: Event + // Fired when the animation starts + this.fire('start'); + + this._animate(); + }, + + // @method stop() + // Stops the animation (if currently running). + stop: function () { + if (!this._inProgress) { return; } + + this._step(true); + this._complete(); + }, + + _animate: function () { + // animation loop + this._animId = requestAnimFrame(this._animate, this); + this._step(); + }, + + _step: function (round) { + var elapsed = (+new Date()) - this._startTime, + duration = this._duration * 1000; + + if (elapsed < duration) { + this._runFrame(this._easeOut(elapsed / duration), round); + } else { + this._runFrame(1); + this._complete(); + } + }, + + _runFrame: function (progress, round) { + var pos = this._startPos.add(this._offset.multiplyBy(progress)); + if (round) { + pos._round(); + } + setPosition(this._el, pos); + + // @event step: Event + // Fired continuously during the animation. + this.fire('step'); + }, + + _complete: function () { + cancelAnimFrame(this._animId); + + this._inProgress = false; + // @event end: Event + // Fired when the animation ends. + this.fire('end'); + }, + + _easeOut: function (t) { + return 1 - Math.pow(1 - t, this._easeOutPower); + } +}); + +/* + * @class Map + * @aka L.Map + * @inherits Evented + * + * The central class of the API — it is used to create a map on a page and manipulate it. + * + * @example + * + * ```js + * // initialize the map on the "map" div with a given center and zoom + * var map = L.map('map', { + * center: [51.505, -0.09], + * zoom: 13 + * }); + * ``` + * + */ - // @method invalidateSize(options: Zoom/Pan options): this - // Checks if the map container size changed and updates the map if so — - // call it after you've changed the map size dynamically, also animating - // pan by default. If `options.pan` is `false`, panning will not occur. - // If `options.debounceMoveend` is `true`, it will delay `moveend` event so - // that it doesn't happen often even if the method is called many - // times in a row. +var Map = Evented.extend({ - // @alternative - // @method invalidateSize(animate: Boolean): this - // Checks if the map container size changed and updates the map if so — - // call it after you've changed the map size dynamically, also animating - // pan by default. - invalidateSize: function (options) { - if (!this._loaded) { return this; } + options: { + // @section Map State Options + // @option crs: CRS = L.CRS.EPSG3857 + // The [Coordinate Reference System](#crs) to use. Don't change this if you're not + // sure what it means. + crs: EPSG3857, - options = L.extend({ - animate: false, - pan: true - }, options === true ? {animate: true} : options); + // @option center: LatLng = undefined + // Initial geographic center of the map + center: undefined, - var oldSize = this.getSize(); - this._sizeChanged = true; - this._lastCenter = null; + // @option zoom: Number = undefined + // Initial map zoom level + zoom: undefined, - var newSize = this.getSize(), - oldCenter = oldSize.divideBy(2).round(), - newCenter = newSize.divideBy(2).round(), - offset = oldCenter.subtract(newCenter); + // @option minZoom: Number = * + // Minimum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the lowest of their `minZoom` options will be used instead. + minZoom: undefined, - if (!offset.x && !offset.y) { return this; } + // @option maxZoom: Number = * + // Maximum zoom level of the map. + // If not specified and at least one `GridLayer` or `TileLayer` is in the map, + // the highest of their `maxZoom` options will be used instead. + maxZoom: undefined, - if (options.animate && options.pan) { - this.panBy(offset); + // @option layers: Layer[] = [] + // Array of layers that will be added to the map initially + layers: [], - } else { - if (options.pan) { - this._rawPanBy(offset); - } + // @option maxBounds: LatLngBounds = null + // When this option is set, the map restricts the view to the given + // geographical bounds, bouncing the user back if the user tries to pan + // outside the view. To set the restriction dynamically, use + // [`setMaxBounds`](#map-setmaxbounds) method. + maxBounds: undefined, - this.fire('move'); + // @option renderer: Renderer = * + // The default method for drawing vector layers on the map. `L.SVG` + // or `L.Canvas` by default depending on browser support. + renderer: undefined, - if (options.debounceMoveend) { - clearTimeout(this._sizeTimer); - this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200); - } else { - this.fire('moveend'); - } - } - // @section Map state change events - // @event resize: ResizeEvent - // Fired when the map is resized. - return this.fire('resize', { - oldSize: oldSize, - newSize: newSize - }); - }, + // @section Animation Options + // @option zoomAnimation: Boolean = true + // Whether the map zoom animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + zoomAnimation: true, - // @section Methods for modifying map state - // @method stop(): this - // Stops the currently running `panTo` or `flyTo` animation, if any. - stop: function () { - this.setZoom(this._limitZoom(this._zoom)); - if (!this.options.zoomSnap) { - this.fire('viewreset'); - } - return this._stop(); - }, + // @option zoomAnimationThreshold: Number = 4 + // Won't animate zoom if the zoom difference exceeds this value. + zoomAnimationThreshold: 4, - // @section Geolocation methods - // @method locate(options?: Locate options): this - // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) - // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, - // and optionally sets the map view to the user's location with respect to - // detection accuracy (or to the world view if geolocation failed). - // Note that, if your page doesn't use HTTPS, this method will fail in - // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) - // See `Locate options` for more details. - locate: function (options) { + // @option fadeAnimation: Boolean = true + // Whether the tile fade animation is enabled. By default it's enabled + // in all browsers that support CSS3 Transitions except Android. + fadeAnimation: true, - options = this._locateOptions = L.extend({ - timeout: 10000, - watch: false - // setView: false - // maxZoom: - // maximumAge: 0 - // enableHighAccuracy: false - }, options); + // @option markerZoomAnimation: Boolean = true + // Whether markers animate their zoom with the zoom animation, if disabled + // they will disappear for the length of the animation. By default it's + // enabled in all browsers that support CSS3 Transitions except Android. + markerZoomAnimation: true, - if (!('geolocation' in navigator)) { - this._handleGeolocationError({ - code: 0, - message: 'Geolocation not supported.' - }); - return this; - } + // @option transform3DLimit: Number = 2^23 + // Defines the maximum size of a CSS translation transform. The default + // value should not be changed unless a web browser positions layers in + // the wrong place after doing a large `panBy`. + transform3DLimit: 8388608, // Precision limit of a 32-bit float - var onResponse = L.bind(this._handleGeolocationResponse, this), - onError = L.bind(this._handleGeolocationError, this); + // @section Interaction Options + // @option zoomSnap: Number = 1 + // Forces the map's zoom level to always be a multiple of this, particularly + // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. + // By default, the zoom level snaps to the nearest integer; lower values + // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` + // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. + zoomSnap: 1, - if (options.watch) { - this._locationWatchId = - navigator.geolocation.watchPosition(onResponse, onError, options); - } else { - navigator.geolocation.getCurrentPosition(onResponse, onError, options); - } - return this; - }, + // @option zoomDelta: Number = 1 + // Controls how much the map's zoom level will change after a + // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` + // or `-` on the keyboard, or using the [zoom controls](#control-zoom). + // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. + zoomDelta: 1, - // @method stopLocate(): this - // Stops watching location previously initiated by `map.locate({watch: true})` - // and aborts resetting the map view if map.locate was called with - // `{setView: true}`. - stopLocate: function () { - if (navigator.geolocation && navigator.geolocation.clearWatch) { - navigator.geolocation.clearWatch(this._locationWatchId); - } - if (this._locateOptions) { - this._locateOptions.setView = false; - } - return this; + // @option trackResize: Boolean = true + // Whether the map automatically handles browser window resize to update itself. + trackResize: true }, - _handleGeolocationError: function (error) { - var c = error.code, - message = error.message || - (c === 1 ? 'permission denied' : - (c === 2 ? 'position unavailable' : 'timeout')); + initialize: function (id, options) { // (HTMLElement or String, Object) + options = setOptions(this, options); - if (this._locateOptions.setView && !this._loaded) { - this.fitWorld(); - } + this._initContainer(id); + this._initLayout(); - // @section Location events - // @event locationerror: ErrorEvent - // Fired when geolocation (using the [`locate`](#map-locate) method) failed. - this.fire('locationerror', { - code: c, - message: 'Geolocation error: ' + message + '.' - }); - }, + // hack for https://github.com/Leaflet/Leaflet/issues/1980 + this._onResize = bind(this._onResize, this); - _handleGeolocationResponse: function (pos) { - var lat = pos.coords.latitude, - lng = pos.coords.longitude, - latlng = new L.LatLng(lat, lng), - bounds = latlng.toBounds(pos.coords.accuracy), - options = this._locateOptions; + this._initEvents(); - if (options.setView) { - var zoom = this.getBoundsZoom(bounds); - this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); + if (options.maxBounds) { + this.setMaxBounds(options.maxBounds); } - var data = { - latlng: latlng, - bounds: bounds, - timestamp: pos.timestamp - }; - - for (var i in pos.coords) { - if (typeof pos.coords[i] === 'number') { - data[i] = pos.coords[i]; - } + if (options.zoom !== undefined) { + this._zoom = this._limitZoom(options.zoom); } - // @event locationfound: LocationEvent - // Fired when geolocation (using the [`locate`](#map-locate) method) - // went successfully. - this.fire('locationfound', data); - }, + if (options.center && options.zoom !== undefined) { + this.setView(toLatLng(options.center), options.zoom, {reset: true}); + } - // TODO handler.addTo - // TODO Appropiate docs section? - // @section Other Methods - // @method addHandler(name: String, HandlerClass: Function): this - // Adds a new `Handler` to the map, given its name and constructor function. - addHandler: function (name, HandlerClass) { - if (!HandlerClass) { return this; } + this._handlers = []; + this._layers = {}; + this._zoomBoundLayers = {}; + this._sizeChanged = true; - var handler = this[name] = new HandlerClass(this); + this.callInitHooks(); - this._handlers.push(handler); + // don't animate on browsers without hardware-accelerated transitions or old Android/Opera + this._zoomAnimated = TRANSITION && any3d && !mobileOpera && + this.options.zoomAnimation; - if (this.options[name]) { - handler.enable(); + // zoom transitions run with the same duration for all layers, so if one of transitionend events + // happens after starting zoom animation (propagating to the map pane), we know that it ended globally + if (this._zoomAnimated) { + this._createAnimProxy(); + on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this); } - return this; + this._addLayers(this.options.layers); }, - // @method remove(): this - // Destroys the map and clears all related event listeners. - remove: function () { - this._initEvents(true); + // @section Methods for modifying map state - if (this._containerId !== this._container._leaflet_id) { - throw new Error('Map container is being reused by another instance'); - } + // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) with the given + // animation options. + setView: function (center, zoom, options) { - try { - // throws error in IE6-8 - delete this._container._leaflet_id; - delete this._containerId; - } catch (e) { - /*eslint-disable */ - this._container._leaflet_id = undefined; - /*eslint-enable */ - this._containerId = undefined; - } + zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); + center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds); + options = options || {}; - L.DomUtil.remove(this._mapPane); + this._stop(); - if (this._clearControlPos) { - this._clearControlPos(); - } + if (this._loaded && !options.reset && options !== true) { - this._clearHandlers(); + if (options.animate !== undefined) { + options.zoom = extend({animate: options.animate}, options.zoom); + options.pan = extend({animate: options.animate, duration: options.duration}, options.pan); + } - if (this._loaded) { - // @section Map state change events - // @event unload: Event - // Fired when the map is destroyed with [remove](#map-remove) method. - this.fire('unload'); - } + // try animating pan or zoom + var moved = (this._zoom !== zoom) ? + this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : + this._tryAnimatedPan(center, options.pan); - for (var i in this._layers) { - this._layers[i].remove(); + if (moved) { + // prevent resize handler call, the view will refresh after animation anyway + clearTimeout(this._sizeTimer); + return this; + } } + // animation didn't start, just reset the map view + this._resetView(center, zoom); + return this; }, - // @section Other Methods - // @method createPane(name: String, container?: HTMLElement): HTMLElement - // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, - // then returns it. The pane is created as a children of `container`, or - // as a children of the main map pane if not set. - createPane: function (name, container) { - var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''), - pane = L.DomUtil.create('div', className, container || this._mapPane); - - if (name) { - this._panes[name] = pane; + // @method setZoom(zoom: Number, options?: Zoom/pan options): this + // Sets the zoom of the map. + setZoom: function (zoom, options) { + if (!this._loaded) { + this._zoom = zoom; + return this; } - return pane; + return this.setView(this.getCenter(), zoom, {zoom: options}); }, - // @section Methods for Getting Map State - - // @method getCenter(): LatLng - // Returns the geographical center of the map view - getCenter: function () { - this._checkIfLoaded(); - - if (this._lastCenter && !this._moved()) { - return this._lastCenter; - } - return this.layerPointToLatLng(this._getCenterLayerPoint()); + // @method zoomIn(delta?: Number, options?: Zoom options): this + // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomIn: function (delta, options) { + delta = delta || (any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom + delta, options); }, - // @method getZoom(): Number - // Returns the current zoom level of the map view - getZoom: function () { - return this._zoom; + // @method zoomOut(delta?: Number, options?: Zoom options): this + // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). + zoomOut: function (delta, options) { + delta = delta || (any3d ? this.options.zoomDelta : 1); + return this.setZoom(this._zoom - delta, options); }, - // @method getBounds(): LatLngBounds - // Returns the geographical bounds visible in the current map view - getBounds: function () { - var bounds = this.getPixelBounds(), - sw = this.unproject(bounds.getBottomLeft()), - ne = this.unproject(bounds.getTopRight()); + // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified geographical point on the map + // stationary (e.g. used internally for scroll zoom and double-click zoom). + // @alternative + // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this + // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. + setZoomAround: function (latlng, zoom, options) { + var scale = this.getZoomScale(zoom), + viewHalf = this.getSize().divideBy(2), + containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng), - return new L.LatLngBounds(sw, ne); - }, + centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), + newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); - // @method getMinZoom(): Number - // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default. - getMinZoom: function () { - return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom; + return this.setView(newCenter, zoom, {zoom: options}); }, - // @method getMaxZoom(): Number - // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). - getMaxZoom: function () { - return this.options.maxZoom === undefined ? - (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : - this.options.maxZoom; - }, + _getBoundsCenterZoom: function (bounds, options) { - // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number - // Returns the maximum zoom level on which the given bounds fit to the map - // view in its entirety. If `inside` (optional) is set to `true`, the method - // instead returns the minimum zoom level on which the map view fits into - // the given bounds in its entirety. - getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number - bounds = L.latLngBounds(bounds); - padding = L.point(padding || [0, 0]); + options = options || {}; + bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); - var zoom = this.getZoom() || 0, - min = this.getMinZoom(), - max = this.getMaxZoom(), - nw = bounds.getNorthWest(), - se = bounds.getSouthEast(), - size = this.getSize().subtract(padding), - boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), - snap = L.Browser.any3d ? this.options.zoomSnap : 1; + var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), + paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), - var scale = Math.min(size.x / boundsSize.x, size.y / boundsSize.y); - zoom = this.getScaleZoom(scale, zoom); + zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); - if (snap) { - zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level - zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; - } + zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; - return Math.max(min, Math.min(max, zoom)); - }, + if (zoom === Infinity) { + return { + center: bounds.getCenter(), + zoom: zoom + }; + } - // @method getSize(): Point - // Returns the current size of the map container (in pixels). - getSize: function () { - if (!this._size || this._sizeChanged) { - this._size = new L.Point( - this._container.clientWidth || 0, - this._container.clientHeight || 0); + var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), - this._sizeChanged = false; - } - return this._size.clone(); - }, + swPoint = this.project(bounds.getSouthWest(), zoom), + nePoint = this.project(bounds.getNorthEast(), zoom), + center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); - // @method getPixelBounds(): Bounds - // Returns the bounds of the current map view in projected pixel - // coordinates (sometimes useful in layer and overlay implementations). - getPixelBounds: function (center, zoom) { - var topLeftPoint = this._getTopLeftPoint(center, zoom); - return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); + return { + center: center, + zoom: zoom + }; }, - // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to - // the map pane? "left point of the map layer" can be confusing, specially - // since there can be negative offsets. - // @method getPixelOrigin(): Point - // Returns the projected pixel coordinates of the top left point of - // the map layer (useful in custom layer and overlay implementations). - getPixelOrigin: function () { - this._checkIfLoaded(); - return this._pixelOrigin; - }, + // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets a map view that contains the given geographical bounds with the + // maximum zoom level possible. + fitBounds: function (bounds, options) { - // @method getPixelWorldBounds(zoom?: Number): Bounds - // Returns the world's bounds in pixel coordinates for zoom level `zoom`. - // If `zoom` is omitted, the map's current zoom level is used. - getPixelWorldBounds: function (zoom) { - return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom); - }, + bounds = toLatLngBounds(bounds); - // @section Other Methods + if (!bounds.isValid()) { + throw new Error('Bounds are not valid.'); + } - // @method getPane(pane: String|HTMLElement): HTMLElement - // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). - getPane: function (pane) { - return typeof pane === 'string' ? this._panes[pane] : pane; + var target = this._getBoundsCenterZoom(bounds, options); + return this.setView(target.center, target.zoom, options); }, - // @method getPanes(): Object - // Returns a plain object containing the names of all [panes](#map-pane) as keys and - // the panes as values. - getPanes: function () { - return this._panes; + // @method fitWorld(options?: fitBounds options): this + // Sets a map view that mostly contains the whole world with the maximum + // zoom level possible. + fitWorld: function (options) { + return this.fitBounds([[-90, -180], [90, 180]], options); }, - // @method getContainer: HTMLElement - // Returns the HTML element that contains the map. - getContainer: function () { - return this._container; + // @method panTo(latlng: LatLng, options?: Pan options): this + // Pans the map to a given center. + panTo: function (center, options) { // (LatLng) + return this.setView(center, this._zoom, {pan: options}); }, + // @method panBy(offset: Point, options?: Pan options): this + // Pans the map by a given number of pixels (animated). + panBy: function (offset, options) { + offset = toPoint(offset).round(); + options = options || {}; - // @section Conversion Methods + if (!offset.x && !offset.y) { + return this.fire('moveend'); + } + // If we pan too far, Chrome gets issues with tiles + // and makes them disappear or appear in the wrong place (slightly offset) #2602 + if (options.animate !== true && !this.getSize().contains(offset)) { + this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); + return this; + } - // @method getZoomScale(toZoom: Number, fromZoom: Number): Number - // Returns the scale factor to be applied to a map transition from zoom level - // `fromZoom` to `toZoom`. Used internally to help with zoom animations. - getZoomScale: function (toZoom, fromZoom) { - // TODO replace with universal implementation after refactoring projections - var crs = this.options.crs; - fromZoom = fromZoom === undefined ? this._zoom : fromZoom; - return crs.scale(toZoom) / crs.scale(fromZoom); - }, + if (!this._panAnim) { + this._panAnim = new PosAnimation(); - // @method getScaleZoom(scale: Number, fromZoom: Number): Number - // Returns the zoom level that the map would end up at, if it is at `fromZoom` - // level and everything is scaled by a factor of `scale`. Inverse of - // [`getZoomScale`](#map-getZoomScale). - getScaleZoom: function (scale, fromZoom) { - var crs = this.options.crs; - fromZoom = fromZoom === undefined ? this._zoom : fromZoom; - var zoom = crs.zoom(scale * crs.scale(fromZoom)); - return isNaN(zoom) ? Infinity : zoom; - }, + this._panAnim.on({ + 'step': this._onPanTransitionStep, + 'end': this._onPanTransitionEnd + }, this); + } - // @method project(latlng: LatLng, zoom: Number): Point - // Projects a geographical coordinate `LatLng` according to the projection - // of the map's CRS, then scales it according to `zoom` and the CRS's - // `Transformation`. The result is pixel coordinate relative to - // the CRS origin. - project: function (latlng, zoom) { - zoom = zoom === undefined ? this._zoom : zoom; - return this.options.crs.latLngToPoint(L.latLng(latlng), zoom); - }, + // don't fire movestart if animating inertia + if (!options.noMoveStart) { + this.fire('movestart'); + } - // @method unproject(point: Point, zoom: Number): LatLng - // Inverse of [`project`](#map-project). - unproject: function (point, zoom) { - zoom = zoom === undefined ? this._zoom : zoom; - return this.options.crs.pointToLatLng(L.point(point), zoom); - }, + // animate pan unless animate: false specified + if (options.animate !== false) { + addClass(this._mapPane, 'leaflet-pan-anim'); - // @method layerPointToLatLng(point: Point): LatLng - // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), - // returns the corresponding geographical coordinate (for the current zoom level). - layerPointToLatLng: function (point) { - var projectedPoint = L.point(point).add(this.getPixelOrigin()); - return this.unproject(projectedPoint); - }, + var newPos = this._getMapPanePos().subtract(offset).round(); + this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); + } else { + this._rawPanBy(offset); + this.fire('move').fire('moveend'); + } - // @method latLngToLayerPoint(latlng: LatLng): Point - // Given a geographical coordinate, returns the corresponding pixel coordinate - // relative to the [origin pixel](#map-getpixelorigin). - latLngToLayerPoint: function (latlng) { - var projectedPoint = this.project(L.latLng(latlng))._round(); - return projectedPoint._subtract(this.getPixelOrigin()); + return this; }, - // @method wrapLatLng(latlng: LatLng): LatLng - // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the - // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the - // CRS's bounds. - // By default this means longitude is wrapped around the dateline so its - // value is between -180 and +180 degrees. - wrapLatLng: function (latlng) { - return this.options.crs.wrapLatLng(L.latLng(latlng)); - }, + // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this + // Sets the view of the map (geographical center and zoom) performing a smooth + // pan-zoom animation. + flyTo: function (targetCenter, targetZoom, options) { - // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds - // Returns a `LatLngBounds` with the same size as the given one, ensuring that - // its center is within the CRS's bounds. - // By default this means the center longitude is wrapped around the dateline so its - // value is between -180 and +180 degrees, and the majority of the bounds - // overlaps the CRS's bounds. - wrapLatLngBounds: function (latlng) { - return this.options.crs.wrapLatLngBounds(L.latLngBounds(latlng)); - }, + options = options || {}; + if (options.animate === false || !any3d) { + return this.setView(targetCenter, targetZoom, options); + } - // @method distance(latlng1: LatLng, latlng2: LatLng): Number - // Returns the distance between two geographical coordinates according to - // the map's CRS. By default this measures distance in meters. - distance: function (latlng1, latlng2) { - return this.options.crs.distance(L.latLng(latlng1), L.latLng(latlng2)); - }, + this._stop(); - // @method containerPointToLayerPoint(point: Point): Point - // Given a pixel coordinate relative to the map container, returns the corresponding - // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). - containerPointToLayerPoint: function (point) { // (Point) - return L.point(point).subtract(this._getMapPanePos()); - }, + var from = this.project(this.getCenter()), + to = this.project(targetCenter), + size = this.getSize(), + startZoom = this._zoom; - // @method layerPointToContainerPoint(point: Point): Point - // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), - // returns the corresponding pixel coordinate relative to the map container. - layerPointToContainerPoint: function (point) { // (Point) - return L.point(point).add(this._getMapPanePos()); - }, + targetCenter = toLatLng(targetCenter); + targetZoom = targetZoom === undefined ? startZoom : targetZoom; - // @method containerPointToLatLng(point: Point): LatLng - // Given a pixel coordinate relative to the map container, returns - // the corresponding geographical coordinate (for the current zoom level). - containerPointToLatLng: function (point) { - var layerPoint = this.containerPointToLayerPoint(L.point(point)); - return this.layerPointToLatLng(layerPoint); - }, + var w0 = Math.max(size.x, size.y), + w1 = w0 * this.getZoomScale(startZoom, targetZoom), + u1 = (to.distanceTo(from)) || 1, + rho = 1.42, + rho2 = rho * rho; - // @method latLngToContainerPoint(latlng: LatLng): Point - // Given a geographical coordinate, returns the corresponding pixel coordinate - // relative to the map container. - latLngToContainerPoint: function (latlng) { - return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng))); - }, + function r(i) { + var s1 = i ? -1 : 1, + s2 = i ? w1 : w0, + t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, + b1 = 2 * s2 * rho2 * u1, + b = t1 / b1, + sq = Math.sqrt(b * b + 1) - b; - // @method mouseEventToContainerPoint(ev: MouseEvent): Point - // Given a MouseEvent object, returns the pixel coordinate relative to the - // map container where the event took place. - mouseEventToContainerPoint: function (e) { - return L.DomEvent.getMousePosition(e, this._container); - }, + // workaround for floating point precision bug when sq = 0, log = -Infinite, + // thus triggering an infinite loop in flyTo + var log = sq < 0.000000001 ? -18 : Math.log(sq); - // @method mouseEventToLayerPoint(ev: MouseEvent): Point - // Given a MouseEvent object, returns the pixel coordinate relative to - // the [origin pixel](#map-getpixelorigin) where the event took place. - mouseEventToLayerPoint: function (e) { - return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); - }, + return log; + } - // @method mouseEventToLatLng(ev: MouseEvent): LatLng - // Given a MouseEvent object, returns geographical coordinate where the - // event took place. - mouseEventToLatLng: function (e) { // (MouseEvent) - return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); - }, + function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } + function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } + function tanh(n) { return sinh(n) / cosh(n); } + var r0 = r(0); - // map initialization methods - - _initContainer: function (id) { - var container = this._container = L.DomUtil.get(id); - - if (!container) { - throw new Error('Map container not found.'); - } else if (container._leaflet_id) { - throw new Error('Map container is already initialized.'); - } + function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } + function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } - L.DomEvent.addListener(container, 'scroll', this._onScroll, this); - this._containerId = L.Util.stamp(container); - }, + function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } - _initLayout: function () { - var container = this._container; + var start = Date.now(), + S = (r(1) - r0) / rho, + duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; - this._fadeAnimated = this.options.fadeAnimation && L.Browser.any3d; + function frame() { + var t = (Date.now() - start) / duration, + s = easeOut(t) * S; - L.DomUtil.addClass(container, 'leaflet-container' + - (L.Browser.touch ? ' leaflet-touch' : '') + - (L.Browser.retina ? ' leaflet-retina' : '') + - (L.Browser.ielt9 ? ' leaflet-oldie' : '') + - (L.Browser.safari ? ' leaflet-safari' : '') + - (this._fadeAnimated ? ' leaflet-fade-anim' : '')); + if (t <= 1) { + this._flyToFrame = requestAnimFrame(frame, this); - var position = L.DomUtil.getStyle(container, 'position'); + this._move( + this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), + this.getScaleZoom(w0 / w(s), startZoom), + {flyTo: true}); - if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { - container.style.position = 'relative'; + } else { + this + ._move(targetCenter, targetZoom) + ._moveEnd(true); + } } - this._initPanes(); + this._moveStart(true); - if (this._initControlPos) { - this._initControlPos(); - } + frame.call(this); + return this; }, - _initPanes: function () { - var panes = this._panes = {}; - this._paneRenderers = {}; + // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this + // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), + // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). + flyToBounds: function (bounds, options) { + var target = this._getBoundsCenterZoom(bounds, options); + return this.flyTo(target.center, target.zoom, options); + }, - // @section - // - // Panes are DOM elements used to control the ordering of layers on the map. You - // can access panes with [`map.getPane`](#map-getpane) or - // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the - // [`map.createPane`](#map-createpane) method. - // - // Every map has the following default panes that differ only in zIndex. - // - // @pane mapPane: HTMLElement = 'auto' - // Pane that contains all other map panes + // @method setMaxBounds(bounds: Bounds): this + // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). + setMaxBounds: function (bounds) { + bounds = toLatLngBounds(bounds); - this._mapPane = this.createPane('mapPane', this._container); - L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0)); + if (!bounds.isValid()) { + this.options.maxBounds = null; + return this.off('moveend', this._panInsideMaxBounds); + } else if (this.options.maxBounds) { + this.off('moveend', this._panInsideMaxBounds); + } - // @pane tilePane: HTMLElement = 200 - // Pane for `GridLayer`s and `TileLayer`s - this.createPane('tilePane'); - // @pane overlayPane: HTMLElement = 400 - // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s - this.createPane('shadowPane'); - // @pane shadowPane: HTMLElement = 500 - // Pane for overlay shadows (e.g. `Marker` shadows) - this.createPane('overlayPane'); - // @pane markerPane: HTMLElement = 600 - // Pane for `Icon`s of `Marker`s - this.createPane('markerPane'); - // @pane tooltipPane: HTMLElement = 650 - // Pane for tooltip. - this.createPane('tooltipPane'); - // @pane popupPane: HTMLElement = 700 - // Pane for `Popup`s. - this.createPane('popupPane'); + this.options.maxBounds = bounds; - if (!this.options.markerZoomAnimation) { - L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide'); - L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide'); + if (this._loaded) { + this._panInsideMaxBounds(); } + + return this.on('moveend', this._panInsideMaxBounds); }, + // @method setMinZoom(zoom: Number): this + // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). + setMinZoom: function (zoom) { + this.options.minZoom = zoom; - // private methods that modify map state + if (this._loaded && this.getZoom() < this.options.minZoom) { + return this.setZoom(zoom); + } - // @section Map state change events - _resetView: function (center, zoom) { - L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0)); + return this; + }, - var loading = !this._loaded; - this._loaded = true; - zoom = this._limitZoom(zoom); + // @method setMaxZoom(zoom: Number): this + // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). + setMaxZoom: function (zoom) { + this.options.maxZoom = zoom; - this.fire('viewprereset'); + if (this._loaded && (this.getZoom() > this.options.maxZoom)) { + return this.setZoom(zoom); + } - var zoomChanged = this._zoom !== zoom; - this - ._moveStart(zoomChanged) - ._move(center, zoom) - ._moveEnd(zoomChanged); + return this; + }, - // @event viewreset: Event - // Fired when the map needs to redraw its content (this usually happens - // on map zoom or load). Very useful for creating custom overlays. - this.fire('viewreset'); + // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this + // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any. + panInsideBounds: function (bounds, options) { + this._enforcingBounds = true; + var center = this.getCenter(), + newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds)); - // @event load: Event - // Fired when the map is initialized (when its center and zoom are set - // for the first time). - if (loading) { - this.fire('load'); + if (!center.equals(newCenter)) { + this.panTo(newCenter, options); } - }, - _moveStart: function (zoomChanged) { - // @event zoomstart: Event - // Fired when the map zoom is about to change (e.g. before zoom animation). - // @event movestart: Event - // Fired when the view of the map starts changing (e.g. user starts dragging the map). - if (zoomChanged) { - this.fire('zoomstart'); - } - return this.fire('movestart'); + this._enforcingBounds = false; + return this; }, - _move: function (center, zoom, data) { - if (zoom === undefined) { - zoom = this._zoom; - } - var zoomChanged = this._zoom !== zoom; + // @method invalidateSize(options: Zoom/Pan options): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. If `options.pan` is `false`, panning will not occur. + // If `options.debounceMoveend` is `true`, it will delay `moveend` event so + // that it doesn't happen often even if the method is called many + // times in a row. - this._zoom = zoom; - this._lastCenter = center; - this._pixelOrigin = this._getNewPixelOrigin(center); + // @alternative + // @method invalidateSize(animate: Boolean): this + // Checks if the map container size changed and updates the map if so — + // call it after you've changed the map size dynamically, also animating + // pan by default. + invalidateSize: function (options) { + if (!this._loaded) { return this; } - // @event zoom: Event - // Fired repeatedly during any change in zoom level, including zoom - // and fly animations. - if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530 - this.fire('zoom', data); - } + options = extend({ + animate: false, + pan: true + }, options === true ? {animate: true} : options); - // @event move: Event - // Fired repeatedly during any movement of the map, including pan and - // fly animations. - return this.fire('move', data); - }, + var oldSize = this.getSize(); + this._sizeChanged = true; + this._lastCenter = null; - _moveEnd: function (zoomChanged) { - // @event zoomend: Event - // Fired when the map has changed, after any animations. - if (zoomChanged) { - this.fire('zoomend'); - } + var newSize = this.getSize(), + oldCenter = oldSize.divideBy(2).round(), + newCenter = newSize.divideBy(2).round(), + offset = oldCenter.subtract(newCenter); - // @event moveend: Event - // Fired when the center of the map stops changing (e.g. user stopped - // dragging the map). - return this.fire('moveend'); - }, + if (!offset.x && !offset.y) { return this; } - _stop: function () { - L.Util.cancelAnimFrame(this._flyToFrame); - if (this._panAnim) { - this._panAnim.stop(); - } - return this; - }, + if (options.animate && options.pan) { + this.panBy(offset); - _rawPanBy: function (offset) { - L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); - }, + } else { + if (options.pan) { + this._rawPanBy(offset); + } - _getZoomSpan: function () { - return this.getMaxZoom() - this.getMinZoom(); - }, + this.fire('move'); - _panInsideMaxBounds: function () { - if (!this._enforcingBounds) { - this.panInsideBounds(this.options.maxBounds); + if (options.debounceMoveend) { + clearTimeout(this._sizeTimer); + this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200); + } else { + this.fire('moveend'); + } } + + // @section Map state change events + // @event resize: ResizeEvent + // Fired when the map is resized. + return this.fire('resize', { + oldSize: oldSize, + newSize: newSize + }); }, - _checkIfLoaded: function () { - if (!this._loaded) { - throw new Error('Set map center and zoom first.'); + // @section Methods for modifying map state + // @method stop(): this + // Stops the currently running `panTo` or `flyTo` animation, if any. + stop: function () { + this.setZoom(this._limitZoom(this._zoom)); + if (!this.options.zoomSnap) { + this.fire('viewreset'); } + return this._stop(); }, - // DOM event handling - - // @section Interaction events - _initEvents: function (remove) { - if (!L.DomEvent) { return; } + // @section Geolocation methods + // @method locate(options?: Locate options): this + // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) + // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, + // and optionally sets the map view to the user's location with respect to + // detection accuracy (or to the world view if geolocation failed). + // Note that, if your page doesn't use HTTPS, this method will fail in + // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) + // See `Locate options` for more details. + locate: function (options) { - this._targets = {}; - this._targets[L.stamp(this._container)] = this; + options = this._locateOptions = extend({ + timeout: 10000, + watch: false + // setView: false + // maxZoom: + // maximumAge: 0 + // enableHighAccuracy: false + }, options); - var onOff = remove ? 'off' : 'on'; + if (!('geolocation' in navigator)) { + this._handleGeolocationError({ + code: 0, + message: 'Geolocation not supported.' + }); + return this; + } - // @event click: MouseEvent - // Fired when the user clicks (or taps) the map. - // @event dblclick: MouseEvent - // Fired when the user double-clicks (or double-taps) the map. - // @event mousedown: MouseEvent - // Fired when the user pushes the mouse button on the map. - // @event mouseup: MouseEvent - // Fired when the user releases the mouse button on the map. - // @event mouseover: MouseEvent - // Fired when the mouse enters the map. - // @event mouseout: MouseEvent - // Fired when the mouse leaves the map. - // @event mousemove: MouseEvent - // Fired while the mouse moves over the map. - // @event contextmenu: MouseEvent - // Fired when the user pushes the right mouse button on the map, prevents - // default browser context menu from showing if there are listeners on - // this event. Also fired on mobile when the user holds a single touch - // for a second (also called long press). - // @event keypress: KeyboardEvent - // Fired when the user presses a key from the keyboard while the map is focused. - L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' + - 'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this); + var onResponse = bind(this._handleGeolocationResponse, this), + onError = bind(this._handleGeolocationError, this); - if (this.options.trackResize) { - L.DomEvent[onOff](window, 'resize', this._onResize, this); + if (options.watch) { + this._locationWatchId = + navigator.geolocation.watchPosition(onResponse, onError, options); + } else { + navigator.geolocation.getCurrentPosition(onResponse, onError, options); } + return this; + }, - if (L.Browser.any3d && this.options.transform3DLimit) { - this[onOff]('moveend', this._onMoveEnd); + // @method stopLocate(): this + // Stops watching location previously initiated by `map.locate({watch: true})` + // and aborts resetting the map view if map.locate was called with + // `{setView: true}`. + stopLocate: function () { + if (navigator.geolocation && navigator.geolocation.clearWatch) { + navigator.geolocation.clearWatch(this._locationWatchId); + } + if (this._locateOptions) { + this._locateOptions.setView = false; } + return this; }, - _onResize: function () { - L.Util.cancelAnimFrame(this._resizeRequest); - this._resizeRequest = L.Util.requestAnimFrame( - function () { this.invalidateSize({debounceMoveend: true}); }, this); - }, + _handleGeolocationError: function (error) { + var c = error.code, + message = error.message || + (c === 1 ? 'permission denied' : + (c === 2 ? 'position unavailable' : 'timeout')); - _onScroll: function () { - this._container.scrollTop = 0; - this._container.scrollLeft = 0; + if (this._locateOptions.setView && !this._loaded) { + this.fitWorld(); + } + + // @section Location events + // @event locationerror: ErrorEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) failed. + this.fire('locationerror', { + code: c, + message: 'Geolocation error: ' + message + '.' + }); }, - _onMoveEnd: function () { - var pos = this._getMapPanePos(); - if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { - // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have - // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/ - this._resetView(this.getCenter(), this.getZoom()); + _handleGeolocationResponse: function (pos) { + var lat = pos.coords.latitude, + lng = pos.coords.longitude, + latlng = new LatLng(lat, lng), + bounds = latlng.toBounds(pos.coords.accuracy), + options = this._locateOptions; + + if (options.setView) { + var zoom = this.getBoundsZoom(bounds); + this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); } - }, - _findEventTargets: function (e, type) { - var targets = [], - target, - isHover = type === 'mouseout' || type === 'mouseover', - src = e.target || e.srcElement, - dragging = false; + var data = { + latlng: latlng, + bounds: bounds, + timestamp: pos.timestamp + }; - while (src) { - target = this._targets[L.stamp(src)]; - if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) { - // Prevent firing click after you just dragged an object. - dragging = true; - break; - } - if (target && target.listens(type, true)) { - if (isHover && !L.DomEvent._isExternalTarget(src, e)) { break; } - targets.push(target); - if (isHover) { break; } + for (var i in pos.coords) { + if (typeof pos.coords[i] === 'number') { + data[i] = pos.coords[i]; } - if (src === this._container) { break; } - src = src.parentNode; - } - if (!targets.length && !dragging && !isHover && L.DomEvent._isExternalTarget(src, e)) { - targets = [this]; } - return targets; + + // @event locationfound: LocationEvent + // Fired when geolocation (using the [`locate`](#map-locate) method) + // went successfully. + this.fire('locationfound', data); }, - _handleDOMEvent: function (e) { - if (!this._loaded || L.DomEvent._skipped(e)) { return; } + // TODO handler.addTo + // TODO Appropiate docs section? + // @section Other Methods + // @method addHandler(name: String, HandlerClass: Function): this + // Adds a new `Handler` to the map, given its name and constructor function. + addHandler: function (name, HandlerClass) { + if (!HandlerClass) { return this; } - var type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type; + var handler = this[name] = new HandlerClass(this); - if (type === 'mousedown') { - // prevents outline when clicking on keyboard-focusable element - L.DomUtil.preventOutline(e.target || e.srcElement); + this._handlers.push(handler); + + if (this.options[name]) { + handler.enable(); } - this._fireDOMEvent(e, type); + return this; }, - _fireDOMEvent: function (e, type, targets) { + // @method remove(): this + // Destroys the map and clears all related event listeners. + remove: function () { - if (e.type === 'click') { - // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups). - // @event preclick: MouseEvent - // Fired before mouse click on the map (sometimes useful when you - // want something to happen on click before any existing click - // handlers start running). - var synth = L.Util.extend({}, e); - synth.type = 'preclick'; - this._fireDOMEvent(synth, synth.type, targets); - } + this._initEvents(true); - if (e._stopped) { return; } + if (this._containerId !== this._container._leaflet_id) { + throw new Error('Map container is being reused by another instance'); + } - // Find the layer the event is propagating from and its parents. - targets = (targets || []).concat(this._findEventTargets(e, type)); + try { + // throws error in IE6-8 + delete this._container._leaflet_id; + delete this._containerId; + } catch (e) { + /*eslint-disable */ + this._container._leaflet_id = undefined; + /*eslint-enable */ + this._containerId = undefined; + } - if (!targets.length) { return; } + remove(this._mapPane); - var target = targets[0]; - if (type === 'contextmenu' && target.listens(type, true)) { - L.DomEvent.preventDefault(e); + if (this._clearControlPos) { + this._clearControlPos(); } - var data = { - originalEvent: e - }; + this._clearHandlers(); - if (e.type !== 'keypress') { - var isMarker = target instanceof L.Marker; - data.containerPoint = isMarker ? - this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); - data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); - data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); + if (this._loaded) { + // @section Map state change events + // @event unload: Event + // Fired when the map is destroyed with [remove](#map-remove) method. + this.fire('unload'); } - for (var i = 0; i < targets.length; i++) { - targets[i].fire(type, data, true); - if (data.originalEvent._stopped || - (targets[i].options.nonBubblingEvents && L.Util.indexOf(targets[i].options.nonBubblingEvents, type) !== -1)) { return; } + var i; + for (i in this._layers) { + this._layers[i].remove(); + } + for (i in this._panes) { + remove(this._panes[i]); } - }, - _draggableMoved: function (obj) { - obj = obj.dragging && obj.dragging.enabled() ? obj : this; - return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); - }, + this._layers = []; + this._panes = []; + delete this._mapPane; + delete this._renderer; - _clearHandlers: function () { - for (var i = 0, len = this._handlers.length; i < len; i++) { - this._handlers[i].disable(); - } + return this; }, // @section Other Methods + // @method createPane(name: String, container?: HTMLElement): HTMLElement + // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, + // then returns it. The pane is created as a child of `container`, or + // as a child of the main map pane if not set. + createPane: function (name, container) { + var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''), + pane = create$1('div', className, container || this._mapPane); - // @method whenReady(fn: Function, context?: Object): this - // Runs the given function `fn` when the map gets initialized with - // a view (center and zoom) and at least one layer, or immediately - // if it's already initialized, optionally passing a function context. - whenReady: function (callback, context) { - if (this._loaded) { - callback.call(context || this, {target: this}); - } else { - this.on('load', callback, context); + if (name) { + this._panes[name] = pane; } - return this; + return pane; }, + // @section Methods for Getting Map State - // private methods for getting map state + // @method getCenter(): LatLng + // Returns the geographical center of the map view + getCenter: function () { + this._checkIfLoaded(); - _getMapPanePos: function () { - return L.DomUtil.getPosition(this._mapPane) || new L.Point(0, 0); + if (this._lastCenter && !this._moved()) { + return this._lastCenter; + } + return this.layerPointToLatLng(this._getCenterLayerPoint()); }, - _moved: function () { - var pos = this._getMapPanePos(); - return pos && !pos.equals([0, 0]); + // @method getZoom(): Number + // Returns the current zoom level of the map view + getZoom: function () { + return this._zoom; }, - _getTopLeftPoint: function (center, zoom) { - var pixelOrigin = center && zoom !== undefined ? - this._getNewPixelOrigin(center, zoom) : - this.getPixelOrigin(); - return pixelOrigin.subtract(this._getMapPanePos()); - }, - - _getNewPixelOrigin: function (center, zoom) { - var viewHalf = this.getSize()._divideBy(2); - return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); - }, - - _latLngToNewLayerPoint: function (latlng, zoom, center) { - var topLeft = this._getNewPixelOrigin(center, zoom); - return this.project(latlng, zoom)._subtract(topLeft); - }, + // @method getBounds(): LatLngBounds + // Returns the geographical bounds visible in the current map view + getBounds: function () { + var bounds = this.getPixelBounds(), + sw = this.unproject(bounds.getBottomLeft()), + ne = this.unproject(bounds.getTopRight()); - _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) { - var topLeft = this._getNewPixelOrigin(center, zoom); - return L.bounds([ - this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), - this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), - this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), - this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) - ]); + return new LatLngBounds(sw, ne); }, - // layer point of the current center - _getCenterLayerPoint: function () { - return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); + // @method getMinZoom(): Number + // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default. + getMinZoom: function () { + return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom; }, - // offset of the specified place to the current center in pixels - _getCenterOffset: function (latlng) { - return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); + // @method getMaxZoom(): Number + // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). + getMaxZoom: function () { + return this.options.maxZoom === undefined ? + (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : + this.options.maxZoom; }, - // adjust center for view to get inside bounds - _limitCenter: function (center, zoom, bounds) { + // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number + // Returns the maximum zoom level on which the given bounds fit to the map + // view in its entirety. If `inside` (optional) is set to `true`, the method + // instead returns the minimum zoom level on which the map view fits into + // the given bounds in its entirety. + getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number + bounds = toLatLngBounds(bounds); + padding = toPoint(padding || [0, 0]); - if (!bounds) { return center; } + var zoom = this.getZoom() || 0, + min = this.getMinZoom(), + max = this.getMaxZoom(), + nw = bounds.getNorthWest(), + se = bounds.getSouthEast(), + size = this.getSize().subtract(padding), + boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), + snap = any3d ? this.options.zoomSnap : 1, + scalex = size.x / boundsSize.x, + scaley = size.y / boundsSize.y, + scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); - var centerPoint = this.project(center, zoom), - viewHalf = this.getSize().divideBy(2), - viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), - offset = this._getBoundsOffset(viewBounds, bounds, zoom); + zoom = this.getScaleZoom(scale, zoom); - // If offset is less than a pixel, ignore. - // This prevents unstable projections from getting into - // an infinite loop of tiny offsets. - if (offset.round().equals([0, 0])) { - return center; + if (snap) { + zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level + zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; } - return this.unproject(centerPoint.add(offset), zoom); + return Math.max(min, Math.min(max, zoom)); }, - // adjust offset for view to get inside bounds - _limitOffset: function (offset, bounds) { - if (!bounds) { return offset; } - - var viewBounds = this.getPixelBounds(), - newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); + // @method getSize(): Point + // Returns the current size of the map container (in pixels). + getSize: function () { + if (!this._size || this._sizeChanged) { + this._size = new Point( + this._container.clientWidth || 0, + this._container.clientHeight || 0); - return offset.add(this._getBoundsOffset(newBounds, bounds)); + this._sizeChanged = false; + } + return this._size.clone(); }, - // returns offset needed for pxBounds to get inside maxBounds at a specified zoom - _getBoundsOffset: function (pxBounds, maxBounds, zoom) { - var projectedMaxBounds = L.bounds( - this.project(maxBounds.getNorthEast(), zoom), - this.project(maxBounds.getSouthWest(), zoom) - ), - minOffset = projectedMaxBounds.min.subtract(pxBounds.min), - maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), - - dx = this._rebound(minOffset.x, -maxOffset.x), - dy = this._rebound(minOffset.y, -maxOffset.y); - - return new L.Point(dx, dy); + // @method getPixelBounds(): Bounds + // Returns the bounds of the current map view in projected pixel + // coordinates (sometimes useful in layer and overlay implementations). + getPixelBounds: function (center, zoom) { + var topLeftPoint = this._getTopLeftPoint(center, zoom); + return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); }, - _rebound: function (left, right) { - return left + right > 0 ? - Math.round(left - right) / 2 : - Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); + // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to + // the map pane? "left point of the map layer" can be confusing, specially + // since there can be negative offsets. + // @method getPixelOrigin(): Point + // Returns the projected pixel coordinates of the top left point of + // the map layer (useful in custom layer and overlay implementations). + getPixelOrigin: function () { + this._checkIfLoaded(); + return this._pixelOrigin; }, - _limitZoom: function (zoom) { - var min = this.getMinZoom(), - max = this.getMaxZoom(), - snap = L.Browser.any3d ? this.options.zoomSnap : 1; - if (snap) { - zoom = Math.round(zoom / snap) * snap; - } - return Math.max(min, Math.min(max, zoom)); + // @method getPixelWorldBounds(zoom?: Number): Bounds + // Returns the world's bounds in pixel coordinates for zoom level `zoom`. + // If `zoom` is omitted, the map's current zoom level is used. + getPixelWorldBounds: function (zoom) { + return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom); }, - _onPanTransitionStep: function () { - this.fire('move'); + // @section Other Methods + + // @method getPane(pane: String|HTMLElement): HTMLElement + // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). + getPane: function (pane) { + return typeof pane === 'string' ? this._panes[pane] : pane; }, - _onPanTransitionEnd: function () { - L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim'); - this.fire('moveend'); + // @method getPanes(): Object + // Returns a plain object containing the names of all [panes](#map-pane) as keys and + // the panes as values. + getPanes: function () { + return this._panes; }, - _tryAnimatedPan: function (center, options) { - // difference between the new and current centers in pixels - var offset = this._getCenterOffset(center)._floor(); + // @method getContainer: HTMLElement + // Returns the HTML element that contains the map. + getContainer: function () { + return this._container; + }, - // don't animate too far unless animate: true specified in options - if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } - this.panBy(offset, options); + // @section Conversion Methods - return true; + // @method getZoomScale(toZoom: Number, fromZoom: Number): Number + // Returns the scale factor to be applied to a map transition from zoom level + // `fromZoom` to `toZoom`. Used internally to help with zoom animations. + getZoomScale: function (toZoom, fromZoom) { + // TODO replace with universal implementation after refactoring projections + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + return crs.scale(toZoom) / crs.scale(fromZoom); }, - _createAnimProxy: function () { + // @method getScaleZoom(scale: Number, fromZoom: Number): Number + // Returns the zoom level that the map would end up at, if it is at `fromZoom` + // level and everything is scaled by a factor of `scale`. Inverse of + // [`getZoomScale`](#map-getZoomScale). + getScaleZoom: function (scale, fromZoom) { + var crs = this.options.crs; + fromZoom = fromZoom === undefined ? this._zoom : fromZoom; + var zoom = crs.zoom(scale * crs.scale(fromZoom)); + return isNaN(zoom) ? Infinity : zoom; + }, - var proxy = this._proxy = L.DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated'); - this._panes.mapPane.appendChild(proxy); + // @method project(latlng: LatLng, zoom: Number): Point + // Projects a geographical coordinate `LatLng` according to the projection + // of the map's CRS, then scales it according to `zoom` and the CRS's + // `Transformation`. The result is pixel coordinate relative to + // the CRS origin. + project: function (latlng, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.latLngToPoint(toLatLng(latlng), zoom); + }, - this.on('zoomanim', function (e) { - var prop = L.DomUtil.TRANSFORM, - transform = proxy.style[prop]; + // @method unproject(point: Point, zoom: Number): LatLng + // Inverse of [`project`](#map-project). + unproject: function (point, zoom) { + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.pointToLatLng(toPoint(point), zoom); + }, - L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); + // @method layerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding geographical coordinate (for the current zoom level). + layerPointToLatLng: function (point) { + var projectedPoint = toPoint(point).add(this.getPixelOrigin()); + return this.unproject(projectedPoint); + }, - // workaround for case when transform is the same and so transitionend event is not fired - if (transform === proxy.style[prop] && this._animatingZoom) { - this._onZoomTransitionEnd(); - } - }, this); + // @method latLngToLayerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the [origin pixel](#map-getpixelorigin). + latLngToLayerPoint: function (latlng) { + var projectedPoint = this.project(toLatLng(latlng))._round(); + return projectedPoint._subtract(this.getPixelOrigin()); + }, - this.on('load moveend', function () { - var c = this.getCenter(), - z = this.getZoom(); - L.DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1)); - }, this); + // @method wrapLatLng(latlng: LatLng): LatLng + // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the + // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the + // CRS's bounds. + // By default this means longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees. + wrapLatLng: function (latlng) { + return this.options.crs.wrapLatLng(toLatLng(latlng)); }, - _catchTransitionEnd: function (e) { - if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { - this._onZoomTransitionEnd(); - } + // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds + // Returns a `LatLngBounds` with the same size as the given one, ensuring that + // its center is within the CRS's bounds. + // By default this means the center longitude is wrapped around the dateline so its + // value is between -180 and +180 degrees, and the majority of the bounds + // overlaps the CRS's bounds. + wrapLatLngBounds: function (latlng) { + return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng)); }, - _nothingToAnimate: function () { - return !this._container.getElementsByClassName('leaflet-zoom-animated').length; + // @method distance(latlng1: LatLng, latlng2: LatLng): Number + // Returns the distance between two geographical coordinates according to + // the map's CRS. By default this measures distance in meters. + distance: function (latlng1, latlng2) { + return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2)); }, - _tryAnimatedZoom: function (center, zoom, options) { + // @method containerPointToLayerPoint(point: Point): Point + // Given a pixel coordinate relative to the map container, returns the corresponding + // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). + containerPointToLayerPoint: function (point) { // (Point) + return toPoint(point).subtract(this._getMapPanePos()); + }, - if (this._animatingZoom) { return true; } + // @method layerPointToContainerPoint(point: Point): Point + // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), + // returns the corresponding pixel coordinate relative to the map container. + layerPointToContainerPoint: function (point) { // (Point) + return toPoint(point).add(this._getMapPanePos()); + }, - options = options || {}; + // @method containerPointToLatLng(point: Point): LatLng + // Given a pixel coordinate relative to the map container, returns + // the corresponding geographical coordinate (for the current zoom level). + containerPointToLatLng: function (point) { + var layerPoint = this.containerPointToLayerPoint(toPoint(point)); + return this.layerPointToLatLng(layerPoint); + }, - // don't animate if disabled, not supported or zoom difference is too large - if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || - Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } + // @method latLngToContainerPoint(latlng: LatLng): Point + // Given a geographical coordinate, returns the corresponding pixel coordinate + // relative to the map container. + latLngToContainerPoint: function (latlng) { + return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); + }, - // offset is the pixel coords of the zoom origin relative to the current center - var scale = this.getZoomScale(zoom), - offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); + // @method mouseEventToContainerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to the + // map container where the event took place. + mouseEventToContainerPoint: function (e) { + return getMousePosition(e, this._container); + }, - // don't animate if the zoom origin isn't within one screen from the current center, unless forced - if (options.animate !== true && !this.getSize().contains(offset)) { return false; } + // @method mouseEventToLayerPoint(ev: MouseEvent): Point + // Given a MouseEvent object, returns the pixel coordinate relative to + // the [origin pixel](#map-getpixelorigin) where the event took place. + mouseEventToLayerPoint: function (e) { + return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); + }, - L.Util.requestAnimFrame(function () { - this - ._moveStart(true) - ._animateZoom(center, zoom, true); - }, this); + // @method mouseEventToLatLng(ev: MouseEvent): LatLng + // Given a MouseEvent object, returns geographical coordinate where the + // event took place. + mouseEventToLatLng: function (e) { // (MouseEvent) + return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); + }, - return true; + + // map initialization methods + + _initContainer: function (id) { + var container = this._container = get(id); + + if (!container) { + throw new Error('Map container not found.'); + } else if (container._leaflet_id) { + throw new Error('Map container is already initialized.'); + } + + on(container, 'scroll', this._onScroll, this); + this._containerId = stamp(container); }, - _animateZoom: function (center, zoom, startAnim, noUpdate) { - if (startAnim) { - this._animatingZoom = true; + _initLayout: function () { + var container = this._container; - // remember what center/zoom to set after animation - this._animateToCenter = center; - this._animateToZoom = zoom; + this._fadeAnimated = this.options.fadeAnimation && any3d; - L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim'); + addClass(container, 'leaflet-container' + + (touch ? ' leaflet-touch' : '') + + (retina ? ' leaflet-retina' : '') + + (ielt9 ? ' leaflet-oldie' : '') + + (safari ? ' leaflet-safari' : '') + + (this._fadeAnimated ? ' leaflet-fade-anim' : '')); + + var position = getStyle(container, 'position'); + + if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { + container.style.position = 'relative'; } - // @event zoomanim: ZoomAnimEvent - // Fired on every frame of a zoom animation - this.fire('zoomanim', { - center: center, - zoom: zoom, - noUpdate: noUpdate - }); + this._initPanes(); - // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 - setTimeout(L.bind(this._onZoomTransitionEnd, this), 250); + if (this._initControlPos) { + this._initControlPos(); + } }, - _onZoomTransitionEnd: function () { - if (!this._animatingZoom) { return; } + _initPanes: function () { + var panes = this._panes = {}; + this._paneRenderers = {}; - L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim'); + // @section + // + // Panes are DOM elements used to control the ordering of layers on the map. You + // can access panes with [`map.getPane`](#map-getpane) or + // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the + // [`map.createPane`](#map-createpane) method. + // + // Every map has the following default panes that differ only in zIndex. + // + // @pane mapPane: HTMLElement = 'auto' + // Pane that contains all other map panes - this._animatingZoom = false; + this._mapPane = this.createPane('mapPane', this._container); + setPosition(this._mapPane, new Point(0, 0)); - this._move(this._animateToCenter, this._animateToZoom); + // @pane tilePane: HTMLElement = 200 + // Pane for `GridLayer`s and `TileLayer`s + this.createPane('tilePane'); + // @pane overlayPane: HTMLElement = 400 + // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s + this.createPane('shadowPane'); + // @pane shadowPane: HTMLElement = 500 + // Pane for overlay shadows (e.g. `Marker` shadows) + this.createPane('overlayPane'); + // @pane markerPane: HTMLElement = 600 + // Pane for `Icon`s of `Marker`s + this.createPane('markerPane'); + // @pane tooltipPane: HTMLElement = 650 + // Pane for tooltip. + this.createPane('tooltipPane'); + // @pane popupPane: HTMLElement = 700 + // Pane for `Popup`s. + this.createPane('popupPane'); - // This anim frame should prevent an obscure iOS webkit tile loading race condition. - L.Util.requestAnimFrame(function () { - this._moveEnd(true); - }, this); - } -}); - -// @section - -// @factory L.map(id: String, options?: Map options) -// Instantiates a map object given the DOM ID of a `
` element -// and optionally an object literal with `Map options`. -// -// @alternative -// @factory L.map(el: HTMLElement, options?: Map options) -// Instantiates a map object given an instance of a `
` HTML element -// and optionally an object literal with `Map options`. -L.map = function (id, options) { - return new L.Map(id, options); -}; - - - - -/* - * @class Layer - * @inherits Evented - * @aka L.Layer - * @aka ILayer - * - * A set of methods from the Layer base class that all Leaflet layers use. - * Inherits all methods, options and events from `L.Evented`. - * - * @example - * - * ```js - * var layer = L.Marker(latlng).addTo(map); - * layer.addTo(map); - * layer.remove(); - * ``` - * - * @event add: Event - * Fired after the layer is added to a map - * - * @event remove: Event - * Fired after the layer is removed from a map - */ - - -L.Layer = L.Evented.extend({ - - // Classes extending `L.Layer` will inherit the following options: - options: { - // @option pane: String = 'overlayPane' - // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default. - pane: 'overlayPane', - nonBubblingEvents: [], // Array of events that should not be bubbled to DOM parents (like the map), - - // @option attribution: String = null - // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox". - attribution: null - }, - - /* @section - * Classes extending `L.Layer` will inherit the following methods: - * - * @method addTo(map: Map): this - * Adds the layer to the given map - */ - addTo: function (map) { - map.addLayer(this); - return this; - }, - - // @method remove: this - // Removes the layer from the map it is currently active on. - remove: function () { - return this.removeFrom(this._map || this._mapToAdd); - }, - - // @method removeFrom(map: Map): this - // Removes the layer from the given map - removeFrom: function (obj) { - if (obj) { - obj.removeLayer(this); - } - return this; - }, - - // @method getPane(name? : String): HTMLElement - // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. - getPane: function (name) { - return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); - }, - - addInteractiveTarget: function (targetEl) { - this._map._targets[L.stamp(targetEl)] = this; - return this; - }, - - removeInteractiveTarget: function (targetEl) { - delete this._map._targets[L.stamp(targetEl)]; - return this; - }, - - // @method getAttribution: String - // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). - getAttribution: function () { - return this.options.attribution; - }, - - _layerAdd: function (e) { - var map = e.target; - - // check in case layer gets added and then removed before the map is ready - if (!map.hasLayer(this)) { return; } - - this._map = map; - this._zoomAnimated = map._zoomAnimated; - - if (this.getEvents) { - var events = this.getEvents(); - map.on(events, this); - this.once('remove', function () { - map.off(events, this); - }, this); - } - - this.onAdd(map); - - if (this.getAttribution && map.attributionControl) { - map.attributionControl.addAttribution(this.getAttribution()); - } - - this.fire('add'); - map.fire('layeradd', {layer: this}); - } -}); - -/* @section Extension methods - * @uninheritable - * - * Every layer should extend from `L.Layer` and (re-)implement the following methods. - * - * @method onAdd(map: Map): this - * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer). - * - * @method onRemove(map: Map): this - * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer). - * - * @method getEvents(): Object - * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer. - * - * @method getAttribution(): String - * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible. - * - * @method beforeAdd(map: Map): this - * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only. - */ - - -/* @namespace Map - * @section Layer events - * - * @event layeradd: LayerEvent - * Fired when a new layer is added to the map. - * - * @event layerremove: LayerEvent - * Fired when some layer is removed from the map - * - * @section Methods for Layers and Controls - */ -L.Map.include({ - // @method addLayer(layer: Layer): this - // Adds the given layer to the map - addLayer: function (layer) { - var id = L.stamp(layer); - if (this._layers[id]) { return this; } - this._layers[id] = layer; - - layer._mapToAdd = this; - - if (layer.beforeAdd) { - layer.beforeAdd(this); - } - - this.whenReady(layer._layerAdd, layer); - - return this; - }, - - // @method removeLayer(layer: Layer): this - // Removes the given layer from the map. - removeLayer: function (layer) { - var id = L.stamp(layer); - - if (!this._layers[id]) { return this; } - - if (this._loaded) { - layer.onRemove(this); - } - - if (layer.getAttribution && this.attributionControl) { - this.attributionControl.removeAttribution(layer.getAttribution()); - } - - delete this._layers[id]; - - if (this._loaded) { - this.fire('layerremove', {layer: layer}); - layer.fire('remove'); - } - - layer._map = layer._mapToAdd = null; - - return this; - }, - - // @method hasLayer(layer: Layer): Boolean - // Returns `true` if the given layer is currently added to the map - hasLayer: function (layer) { - return !!layer && (L.stamp(layer) in this._layers); - }, - - /* @method eachLayer(fn: Function, context?: Object): this - * Iterates over the layers of the map, optionally specifying context of the iterator function. - * ``` - * map.eachLayer(function(layer){ - * layer.bindPopup('Hello'); - * }); - * ``` - */ - eachLayer: function (method, context) { - for (var i in this._layers) { - method.call(context, this._layers[i]); - } - return this; - }, - - _addLayers: function (layers) { - layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : []; - - for (var i = 0, len = layers.length; i < len; i++) { - this.addLayer(layers[i]); - } - }, - - _addZoomLimit: function (layer) { - if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { - this._zoomBoundLayers[L.stamp(layer)] = layer; - this._updateZoomLevels(); - } - }, - - _removeZoomLimit: function (layer) { - var id = L.stamp(layer); - - if (this._zoomBoundLayers[id]) { - delete this._zoomBoundLayers[id]; - this._updateZoomLevels(); - } - }, - - _updateZoomLevels: function () { - var minZoom = Infinity, - maxZoom = -Infinity, - oldZoomSpan = this._getZoomSpan(); - - for (var i in this._zoomBoundLayers) { - var options = this._zoomBoundLayers[i].options; - - minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom); - maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom); - } - - this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom; - this._layersMinZoom = minZoom === Infinity ? undefined : minZoom; - - // @section Map state change events - // @event zoomlevelschange: Event - // Fired when the number of zoomlevels on the map is changed due - // to adding or removing a layer. - if (oldZoomSpan !== this._getZoomSpan()) { - this.fire('zoomlevelschange'); - } - - if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { - this.setZoom(this._layersMaxZoom); - } - if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { - this.setZoom(this._layersMinZoom); - } - } -}); - - - -/* - * @namespace DomEvent - * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally. - */ - -// Inspired by John Resig, Dean Edwards and YUI addEvent implementations. + if (!this.options.markerZoomAnimation) { + addClass(panes.markerPane, 'leaflet-zoom-hide'); + addClass(panes.shadowPane, 'leaflet-zoom-hide'); + } + }, + // private methods that modify map state -var eventsKey = '_leaflet_events'; + // @section Map state change events + _resetView: function (center, zoom) { + setPosition(this._mapPane, new Point(0, 0)); -L.DomEvent = { + var loading = !this._loaded; + this._loaded = true; + zoom = this._limitZoom(zoom); - // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this - // Adds a listener function (`fn`) to a particular DOM event type of the - // element `el`. You can optionally specify the context of the listener - // (object the `this` keyword will point to). You can also pass several - // space-separated types (e.g. `'click dblclick'`). + this.fire('viewprereset'); - // @alternative - // @function on(el: HTMLElement, eventMap: Object, context?: Object): this - // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` - on: function (obj, types, fn, context) { + var zoomChanged = this._zoom !== zoom; + this + ._moveStart(zoomChanged) + ._move(center, zoom) + ._moveEnd(zoomChanged); - if (typeof types === 'object') { - for (var type in types) { - this._on(obj, type, types[type], fn); - } - } else { - types = L.Util.splitWords(types); + // @event viewreset: Event + // Fired when the map needs to redraw its content (this usually happens + // on map zoom or load). Very useful for creating custom overlays. + this.fire('viewreset'); - for (var i = 0, len = types.length; i < len; i++) { - this._on(obj, types[i], fn, context); - } + // @event load: Event + // Fired when the map is initialized (when its center and zoom are set + // for the first time). + if (loading) { + this.fire('load'); } - - return this; }, - // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this - // Removes a previously added listener function. If no function is specified, - // it will remove all the listeners of that particular DOM event from the element. - // Note that if you passed a custom context to on, you must pass the same - // context to `off` in order to remove the listener. - - // @alternative - // @function off(el: HTMLElement, eventMap: Object, context?: Object): this - // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` - off: function (obj, types, fn, context) { - - if (typeof types === 'object') { - for (var type in types) { - this._off(obj, type, types[type], fn); - } - } else { - types = L.Util.splitWords(types); - - for (var i = 0, len = types.length; i < len; i++) { - this._off(obj, types[i], fn, context); - } + _moveStart: function (zoomChanged) { + // @event zoomstart: Event + // Fired when the map zoom is about to change (e.g. before zoom animation). + // @event movestart: Event + // Fired when the view of the map starts changing (e.g. user starts dragging the map). + if (zoomChanged) { + this.fire('zoomstart'); } - - return this; + return this.fire('movestart'); }, - _on: function (obj, type, fn, context) { - var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : ''); - - if (obj[eventsKey] && obj[eventsKey][id]) { return this; } - - var handler = function (e) { - return fn.call(context || obj, e || window.event); - }; - - var originalHandler = handler; - - if (L.Browser.pointer && type.indexOf('touch') === 0) { - this.addPointerListener(obj, type, handler, id); - - } else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener && - !(L.Browser.pointer && L.Browser.chrome)) { - // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener - // See #5180 - this.addDoubleTapListener(obj, handler, id); - - } else if ('addEventListener' in obj) { + _move: function (center, zoom, data) { + if (zoom === undefined) { + zoom = this._zoom; + } + var zoomChanged = this._zoom !== zoom; - if (type === 'mousewheel') { - obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); + this._zoom = zoom; + this._lastCenter = center; + this._pixelOrigin = this._getNewPixelOrigin(center); - } else if ((type === 'mouseenter') || (type === 'mouseleave')) { - handler = function (e) { - e = e || window.event; - if (L.DomEvent._isExternalTarget(obj, e)) { - originalHandler(e); - } - }; - obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false); + // @event zoom: Event + // Fired repeatedly during any change in zoom level, including zoom + // and fly animations. + if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530 + this.fire('zoom', data); + } - } else { - if (type === 'click' && L.Browser.android) { - handler = function (e) { - return L.DomEvent._filterClick(e, originalHandler); - }; - } - obj.addEventListener(type, handler, false); - } + // @event move: Event + // Fired repeatedly during any movement of the map, including pan and + // fly animations. + return this.fire('move', data); + }, - } else if ('attachEvent' in obj) { - obj.attachEvent('on' + type, handler); + _moveEnd: function (zoomChanged) { + // @event zoomend: Event + // Fired when the map has changed, after any animations. + if (zoomChanged) { + this.fire('zoomend'); } - obj[eventsKey] = obj[eventsKey] || {}; - obj[eventsKey][id] = handler; + // @event moveend: Event + // Fired when the center of the map stops changing (e.g. user stopped + // dragging the map). + return this.fire('moveend'); + }, + _stop: function () { + cancelAnimFrame(this._flyToFrame); + if (this._panAnim) { + this._panAnim.stop(); + } return this; }, - _off: function (obj, type, fn, context) { + _rawPanBy: function (offset) { + setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); + }, - var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : ''), - handler = obj[eventsKey] && obj[eventsKey][id]; + _getZoomSpan: function () { + return this.getMaxZoom() - this.getMinZoom(); + }, - if (!handler) { return this; } + _panInsideMaxBounds: function () { + if (!this._enforcingBounds) { + this.panInsideBounds(this.options.maxBounds); + } + }, - if (L.Browser.pointer && type.indexOf('touch') === 0) { - this.removePointerListener(obj, type, id); + _checkIfLoaded: function () { + if (!this._loaded) { + throw new Error('Set map center and zoom first.'); + } + }, - } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) { - this.removeDoubleTapListener(obj, id); + // DOM event handling - } else if ('removeEventListener' in obj) { + // @section Interaction events + _initEvents: function (remove$$1) { + this._targets = {}; + this._targets[stamp(this._container)] = this; - if (type === 'mousewheel') { - obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); + var onOff = remove$$1 ? off : on; - } else { - obj.removeEventListener( - type === 'mouseenter' ? 'mouseover' : - type === 'mouseleave' ? 'mouseout' : type, handler, false); - } + // @event click: MouseEvent + // Fired when the user clicks (or taps) the map. + // @event dblclick: MouseEvent + // Fired when the user double-clicks (or double-taps) the map. + // @event mousedown: MouseEvent + // Fired when the user pushes the mouse button on the map. + // @event mouseup: MouseEvent + // Fired when the user releases the mouse button on the map. + // @event mouseover: MouseEvent + // Fired when the mouse enters the map. + // @event mouseout: MouseEvent + // Fired when the mouse leaves the map. + // @event mousemove: MouseEvent + // Fired while the mouse moves over the map. + // @event contextmenu: MouseEvent + // Fired when the user pushes the right mouse button on the map, prevents + // default browser context menu from showing if there are listeners on + // this event. Also fired on mobile when the user holds a single touch + // for a second (also called long press). + // @event keypress: KeyboardEvent + // Fired when the user presses a key from the keyboard while the map is focused. + onOff(this._container, 'click dblclick mousedown mouseup ' + + 'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this); - } else if ('detachEvent' in obj) { - obj.detachEvent('on' + type, handler); + if (this.options.trackResize) { + onOff(window, 'resize', this._onResize, this); } - obj[eventsKey][id] = null; + if (any3d && this.options.transform3DLimit) { + (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd); + } + }, - return this; + _onResize: function () { + cancelAnimFrame(this._resizeRequest); + this._resizeRequest = requestAnimFrame( + function () { this.invalidateSize({debounceMoveend: true}); }, this); }, - // @function stopPropagation(ev: DOMEvent): this - // Stop the given event from propagation to parent elements. Used inside the listener functions: - // ```js - // L.DomEvent.on(div, 'click', function (ev) { - // L.DomEvent.stopPropagation(ev); - // }); - // ``` - stopPropagation: function (e) { + _onScroll: function () { + this._container.scrollTop = 0; + this._container.scrollLeft = 0; + }, - if (e.stopPropagation) { - e.stopPropagation(); - } else if (e.originalEvent) { // In case of Leaflet event. - e.originalEvent._stopped = true; - } else { - e.cancelBubble = true; + _onMoveEnd: function () { + var pos = this._getMapPanePos(); + if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { + // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have + // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/ + this._resetView(this.getCenter(), this.getZoom()); } - L.DomEvent._skipped(e); + }, - return this; + _findEventTargets: function (e, type) { + var targets = [], + target, + isHover = type === 'mouseout' || type === 'mouseover', + src = e.target || e.srcElement, + dragging = false; + + while (src) { + target = this._targets[stamp(src)]; + if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) { + // Prevent firing click after you just dragged an object. + dragging = true; + break; + } + if (target && target.listens(type, true)) { + if (isHover && !isExternalTarget(src, e)) { break; } + targets.push(target); + if (isHover) { break; } + } + if (src === this._container) { break; } + src = src.parentNode; + } + if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) { + targets = [this]; + } + return targets; }, - // @function disableScrollPropagation(el: HTMLElement): this - // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants). - disableScrollPropagation: function (el) { - return L.DomEvent.on(el, 'mousewheel', L.DomEvent.stopPropagation); + _handleDOMEvent: function (e) { + if (!this._loaded || skipped(e)) { return; } + + var type = e.type; + + if (type === 'mousedown' || type === 'keypress') { + // prevents outline when clicking on keyboard-focusable element + preventOutline(e.target || e.srcElement); + } + + this._fireDOMEvent(e, type); }, - // @function disableClickPropagation(el: HTMLElement): this - // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`, - // `'mousedown'` and `'touchstart'` events (plus browser variants). - disableClickPropagation: function (el) { - var stop = L.DomEvent.stopPropagation; + _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'], - L.DomEvent.on(el, L.Draggable.START.join(' '), stop); + _fireDOMEvent: function (e, type, targets) { - return L.DomEvent.on(el, { - click: L.DomEvent._fakeStop, - dblclick: stop - }); + if (e.type === 'click') { + // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups). + // @event preclick: MouseEvent + // Fired before mouse click on the map (sometimes useful when you + // want something to happen on click before any existing click + // handlers start running). + var synth = extend({}, e); + synth.type = 'preclick'; + this._fireDOMEvent(synth, synth.type, targets); + } + + if (e._stopped) { return; } + + // Find the layer the event is propagating from and its parents. + targets = (targets || []).concat(this._findEventTargets(e, type)); + + if (!targets.length) { return; } + + var target = targets[0]; + if (type === 'contextmenu' && target.listens(type, true)) { + preventDefault(e); + } + + var data = { + originalEvent: e + }; + + if (e.type !== 'keypress') { + var isMarker = (target.options && 'icon' in target.options); + data.containerPoint = isMarker ? + this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); + data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); + data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); + } + + for (var i = 0; i < targets.length; i++) { + targets[i].fire(type, data, true); + if (data.originalEvent._stopped || + (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; } + } + }, + + _draggableMoved: function (obj) { + obj = obj.dragging && obj.dragging.enabled() ? obj : this; + return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); + }, + + _clearHandlers: function () { + for (var i = 0, len = this._handlers.length; i < len; i++) { + this._handlers[i].disable(); + } }, - // @function preventDefault(ev: DOMEvent): this - // Prevents the default action of the DOM Event `ev` from happening (such as - // following a link in the href of the a element, or doing a POST request - // with page reload when a `` is submitted). - // Use it inside listener functions. - preventDefault: function (e) { + // @section Other Methods - if (e.preventDefault) { - e.preventDefault(); + // @method whenReady(fn: Function, context?: Object): this + // Runs the given function `fn` when the map gets initialized with + // a view (center and zoom) and at least one layer, or immediately + // if it's already initialized, optionally passing a function context. + whenReady: function (callback, context) { + if (this._loaded) { + callback.call(context || this, {target: this}); } else { - e.returnValue = false; + this.on('load', callback, context); } return this; }, - // @function stop(ev): this - // Does `stopPropagation` and `preventDefault` at the same time. - stop: function (e) { - return L.DomEvent - .preventDefault(e) - .stopPropagation(e); - }, - // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point - // Gets normalized mouse position from a DOM event relative to the - // `container` or to the whole page if not specified. - getMousePosition: function (e, container) { - if (!container) { - return new L.Point(e.clientX, e.clientY); - } + // private methods for getting map state + + _getMapPanePos: function () { + return getPosition(this._mapPane) || new Point(0, 0); + }, - var rect = container.getBoundingClientRect(); + _moved: function () { + var pos = this._getMapPanePos(); + return pos && !pos.equals([0, 0]); + }, - return new L.Point( - e.clientX - rect.left - container.clientLeft, - e.clientY - rect.top - container.clientTop); + _getTopLeftPoint: function (center, zoom) { + var pixelOrigin = center && zoom !== undefined ? + this._getNewPixelOrigin(center, zoom) : + this.getPixelOrigin(); + return pixelOrigin.subtract(this._getMapPanePos()); }, - // Chrome on Win scrolls double the pixels as in other platforms (see #4538), - // and Firefox scrolls device pixels, not CSS pixels - _wheelPxFactor: (L.Browser.win && L.Browser.chrome) ? 2 : - L.Browser.gecko ? window.devicePixelRatio : - 1, + _getNewPixelOrigin: function (center, zoom) { + var viewHalf = this.getSize()._divideBy(2); + return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); + }, - // @function getWheelDelta(ev: DOMEvent): Number - // Gets normalized wheel delta from a mousewheel DOM event, in vertical - // pixels scrolled (negative if scrolling down). - // Events from pointing devices without precise scrolling are mapped to - // a best guess of 60 pixels. - getWheelDelta: function (e) { - return (L.Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta - (e.deltaY && e.deltaMode === 0) ? -e.deltaY / L.DomEvent._wheelPxFactor : // Pixels - (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines - (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages - (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events - e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels - (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines - e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages - 0; + _latLngToNewLayerPoint: function (latlng, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return this.project(latlng, zoom)._subtract(topLeft); }, - _skipEvents: {}, + _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) { + var topLeft = this._getNewPixelOrigin(center, zoom); + return toBounds([ + this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), + this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), + this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) + ]); + }, - _fakeStop: function (e) { - // fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e) - L.DomEvent._skipEvents[e.type] = true; + // layer point of the current center + _getCenterLayerPoint: function () { + return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); }, - _skipped: function (e) { - var skipped = this._skipEvents[e.type]; - // reset when checking, as it's only used in map container and propagates outside of the map - this._skipEvents[e.type] = false; - return skipped; + // offset of the specified place to the current center in pixels + _getCenterOffset: function (latlng) { + return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); }, - // check if element really left/entered the event target (for mouseenter/mouseleave) - _isExternalTarget: function (el, e) { + // adjust center for view to get inside bounds + _limitCenter: function (center, zoom, bounds) { - var related = e.relatedTarget; + if (!bounds) { return center; } - if (!related) { return true; } + var centerPoint = this.project(center, zoom), + viewHalf = this.getSize().divideBy(2), + viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), + offset = this._getBoundsOffset(viewBounds, bounds, zoom); - try { - while (related && (related !== el)) { - related = related.parentNode; - } - } catch (err) { - return false; + // If offset is less than a pixel, ignore. + // This prevents unstable projections from getting into + // an infinite loop of tiny offsets. + if (offset.round().equals([0, 0])) { + return center; } - return (related !== el); + + return this.unproject(centerPoint.add(offset), zoom); }, - // this is a horrible workaround for a bug in Android where a single touch triggers two click events - _filterClick: function (e, handler) { - var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)), - elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick); + // adjust offset for view to get inside bounds + _limitOffset: function (offset, bounds) { + if (!bounds) { return offset; } - // are they closer together than 500ms yet more than 100ms? - // Android typically triggers them ~300ms apart while multiple listeners - // on the same event should be triggered far faster; - // or check if click is simulated on the element, and if it is, reject any non-simulated events + var viewBounds = this.getPixelBounds(), + newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); - if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { - L.DomEvent.stop(e); - return; + return offset.add(this._getBoundsOffset(newBounds, bounds)); + }, + + // returns offset needed for pxBounds to get inside maxBounds at a specified zoom + _getBoundsOffset: function (pxBounds, maxBounds, zoom) { + var projectedMaxBounds = toBounds( + this.project(maxBounds.getNorthEast(), zoom), + this.project(maxBounds.getSouthWest(), zoom) + ), + minOffset = projectedMaxBounds.min.subtract(pxBounds.min), + maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), + + dx = this._rebound(minOffset.x, -maxOffset.x), + dy = this._rebound(minOffset.y, -maxOffset.y); + + return new Point(dx, dy); + }, + + _rebound: function (left, right) { + return left + right > 0 ? + Math.round(left - right) / 2 : + Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); + }, + + _limitZoom: function (zoom) { + var min = this.getMinZoom(), + max = this.getMaxZoom(), + snap = any3d ? this.options.zoomSnap : 1; + if (snap) { + zoom = Math.round(zoom / snap) * snap; } - L.DomEvent._lastClick = timeStamp; + return Math.max(min, Math.min(max, zoom)); + }, - handler(e); - } -}; + _onPanTransitionStep: function () { + this.fire('move'); + }, -// @function addListener(…): this -// Alias to [`L.DomEvent.on`](#domevent-on) -L.DomEvent.addListener = L.DomEvent.on; + _onPanTransitionEnd: function () { + removeClass(this._mapPane, 'leaflet-pan-anim'); + this.fire('moveend'); + }, -// @function removeListener(…): this -// Alias to [`L.DomEvent.off`](#domevent-off) -L.DomEvent.removeListener = L.DomEvent.off; - - - -/* - * @class PosAnimation - * @aka L.PosAnimation - * @inherits Evented - * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. - * - * @example - * ```js - * var fx = new L.PosAnimation(); - * fx.run(el, [300, 500], 0.5); - * ``` - * - * @constructor L.PosAnimation() - * Creates a `PosAnimation` object. - * - */ - -L.PosAnimation = L.Evented.extend({ - - // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) - // Run an animation of a given element to a new position, optionally setting - // duration in seconds (`0.25` by default) and easing linearity factor (3rd - // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1), - // `0.5` by default). - run: function (el, newPos, duration, easeLinearity) { - this.stop(); - - this._el = el; - this._inProgress = true; - this._duration = duration || 0.25; - this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); - - this._startPos = L.DomUtil.getPosition(el); - this._offset = newPos.subtract(this._startPos); - this._startTime = +new Date(); - - // @event start: Event - // Fired when the animation starts - this.fire('start'); - - this._animate(); - }, - - // @method stop() - // Stops the animation (if currently running). - stop: function () { - if (!this._inProgress) { return; } - - this._step(true); - this._complete(); - }, - - _animate: function () { - // animation loop - this._animId = L.Util.requestAnimFrame(this._animate, this); - this._step(); - }, - - _step: function (round) { - var elapsed = (+new Date()) - this._startTime, - duration = this._duration * 1000; - - if (elapsed < duration) { - this._runFrame(this._easeOut(elapsed / duration), round); - } else { - this._runFrame(1); - this._complete(); - } - }, - - _runFrame: function (progress, round) { - var pos = this._startPos.add(this._offset.multiplyBy(progress)); - if (round) { - pos._round(); - } - L.DomUtil.setPosition(this._el, pos); - - // @event step: Event - // Fired continuously during the animation. - this.fire('step'); - }, - - _complete: function () { - L.Util.cancelAnimFrame(this._animId); - - this._inProgress = false; - // @event end: Event - // Fired when the animation ends. - this.fire('end'); - }, - - _easeOut: function (t) { - return 1 - Math.pow(1 - t, this._easeOutPower); - } -}); - - - -/* - * @namespace Projection - * @projection L.Projection.Mercator - * - * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS. - */ + _tryAnimatedPan: function (center, options) { + // difference between the new and current centers in pixels + var offset = this._getCenterOffset(center)._floor(); -L.Projection.Mercator = { - R: 6378137, - R_MINOR: 6356752.314245179, + // don't animate too far unless animate: true specified in options + if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } - bounds: L.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), + this.panBy(offset, options); - project: function (latlng) { - var d = Math.PI / 180, - r = this.R, - y = latlng.lat * d, - tmp = this.R_MINOR / r, - e = Math.sqrt(1 - tmp * tmp), - con = e * Math.sin(y); + return true; + }, - var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); - y = -r * Math.log(Math.max(ts, 1E-10)); + _createAnimProxy: function () { + + var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated'); + this._panes.mapPane.appendChild(proxy); + + this.on('zoomanim', function (e) { + var prop = TRANSFORM, + transform = this._proxy.style[prop]; + + setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); + + // workaround for case when transform is the same and so transitionend event is not fired + if (transform === this._proxy.style[prop] && this._animatingZoom) { + this._onZoomTransitionEnd(); + } + }, this); + + this.on('load moveend', function () { + var c = this.getCenter(), + z = this.getZoom(); + setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); + }, this); - return new L.Point(latlng.lng * d * r, y); + this._on('unload', this._destroyAnimProxy, this); }, - unproject: function (point) { - var d = 180 / Math.PI, - r = this.R, - tmp = this.R_MINOR / r, - e = Math.sqrt(1 - tmp * tmp), - ts = Math.exp(-point.y / r), - phi = Math.PI / 2 - 2 * Math.atan(ts); + _destroyAnimProxy: function () { + remove(this._proxy); + delete this._proxy; + }, - for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { - con = e * Math.sin(phi); - con = Math.pow((1 - con) / (1 + con), e / 2); - dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; - phi += dphi; + _catchTransitionEnd: function (e) { + if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { + this._onZoomTransitionEnd(); + } + }, + + _nothingToAnimate: function () { + return !this._container.getElementsByClassName('leaflet-zoom-animated').length; + }, + + _tryAnimatedZoom: function (center, zoom, options) { + + if (this._animatingZoom) { return true; } + + options = options || {}; + + // don't animate if disabled, not supported or zoom difference is too large + if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || + Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } + + // offset is the pixel coords of the zoom origin relative to the current center + var scale = this.getZoomScale(zoom), + offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); + + // don't animate if the zoom origin isn't within one screen from the current center, unless forced + if (options.animate !== true && !this.getSize().contains(offset)) { return false; } + + requestAnimFrame(function () { + this + ._moveStart(true) + ._animateZoom(center, zoom, true); + }, this); + + return true; + }, + + _animateZoom: function (center, zoom, startAnim, noUpdate) { + if (startAnim) { + this._animatingZoom = true; + + // remember what center/zoom to set after animation + this._animateToCenter = center; + this._animateToZoom = zoom; + + addClass(this._mapPane, 'leaflet-zoom-anim'); } - return new L.LatLng(phi * d, point.x * d / r); + // @event zoomanim: ZoomAnimEvent + // Fired on every frame of a zoom animation + this.fire('zoomanim', { + center: center, + zoom: zoom, + noUpdate: noUpdate + }); + + // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 + setTimeout(bind(this._onZoomTransitionEnd, this), 250); + }, + + _onZoomTransitionEnd: function () { + if (!this._animatingZoom) { return; } + + removeClass(this._mapPane, 'leaflet-zoom-anim'); + + this._animatingZoom = false; + + this._move(this._animateToCenter, this._animateToZoom); + + // This anim frame should prevent an obscure iOS webkit tile loading race condition. + requestAnimFrame(function () { + this._moveEnd(true); + }, this); } -}; - - +}); + +// @section + +// @factory L.map(id: String, options?: Map options) +// Instantiates a map object given the DOM ID of a `
` element +// and optionally an object literal with `Map options`. +// +// @alternative +// @factory L.map(el: HTMLElement, options?: Map options) +// Instantiates a map object given an instance of a `
` HTML element +// and optionally an object literal with `Map options`. +function createMap(id, options) { + return new Map(id, options); +} /* - * @namespace CRS - * @crs L.CRS.EPSG3395 + * @class Control + * @aka L.Control + * @inherits Class * - * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. + * L.Control is a base class for implementing map controls. Handles positioning. + * All other controls extend from this class. */ -L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, { - code: 'EPSG:3395', - projection: L.Projection.Mercator, +var Control = Class.extend({ + // @section + // @aka Control options + options: { + // @option position: String = 'topright' + // The position of the control (one of the map corners). Possible values are `'topleft'`, + // `'topright'`, `'bottomleft'` or `'bottomright'` + position: 'topright' + }, - transformation: (function () { - var scale = 0.5 / (Math.PI * L.Projection.Mercator.R); - return new L.Transformation(scale, 0.5, -scale, 0.5); - }()) -}); - - - -/* - * @class GridLayer - * @inherits Layer - * @aka L.GridLayer - * - * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`. - * GridLayer can be extended to create a tiled grid of HTML elements like ``, `` or `
`. GridLayer will handle creating and animating these DOM elements for you. - * - * - * @section Synchronous usage - * @example - * - * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile. - * - * ```js - * var CanvasLayer = L.GridLayer.extend({ - * createTile: function(coords){ - * // create a element for drawing - * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); - * - * // setup tile width and height according to the options - * var size = this.getTileSize(); - * tile.width = size.x; - * tile.height = size.y; - * - * // get a canvas context and draw something on it using coords.x, coords.y and coords.z - * var ctx = tile.getContext('2d'); - * - * // return the tile so it can be rendered on screen - * return tile; - * } - * }); - * ``` - * - * @section Asynchronous usage - * @example - * - * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback. - * - * ```js - * var CanvasLayer = L.GridLayer.extend({ - * createTile: function(coords, done){ - * var error; - * - * // create a element for drawing - * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); - * - * // setup tile width and height according to the options - * var size = this.getTileSize(); - * tile.width = size.x; - * tile.height = size.y; - * - * // draw something asynchronously and pass the tile to the done() callback - * setTimeout(function() { - * done(error, tile); - * }, 1000); - * - * return tile; - * } - * }); - * ``` - * - * @section - */ - - -L.GridLayer = L.Layer.extend({ - - // @section - // @aka GridLayer options - options: { - // @option tileSize: Number|Point = 256 - // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise. - tileSize: 256, - - // @option opacity: Number = 1.0 - // Opacity of the tiles. Can be used in the `createTile()` function. - opacity: 1, - - // @option updateWhenIdle: Boolean = depends - // If `false`, new tiles are loaded during panning, otherwise only after it (for better performance). `true` by default on mobile browsers, otherwise `false`. - updateWhenIdle: L.Browser.mobile, - - // @option updateWhenZooming: Boolean = true - // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends. - updateWhenZooming: true, - - // @option updateInterval: Number = 200 - // Tiles will not update more than once every `updateInterval` milliseconds when panning. - updateInterval: 200, - - // @option zIndex: Number = 1 - // The explicit zIndex of the tile layer. - zIndex: 1, - - // @option bounds: LatLngBounds = undefined - // If set, tiles will only be loaded inside the set `LatLngBounds`. - bounds: null, - - // @option minZoom: Number = 0 - // The minimum zoom level that tiles will be loaded at. By default the entire map. - minZoom: 0, - - // @option maxZoom: Number = undefined - // The maximum zoom level that tiles will be loaded at. - maxZoom: undefined, - - // @option noWrap: Boolean = false - // Whether the layer is wrapped around the antimeridian. If `true`, the - // GridLayer will only be displayed once at low zoom levels. Has no - // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used - // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting - // tiles outside the CRS limits. - noWrap: false, - - // @option pane: String = 'tilePane' - // `Map pane` where the grid layer will be added. - pane: 'tilePane', - - // @option className: String = '' - // A custom class name to assign to the tile layer. Empty by default. - className: '', - - // @option keepBuffer: Number = 2 - // When panning the map, keep this many rows and columns of tiles before unloading them. - keepBuffer: 2 - }, - - initialize: function (options) { - L.setOptions(this, options); - }, - - onAdd: function () { - this._initContainer(); - - this._levels = {}; - this._tiles = {}; - - this._resetView(); - this._update(); - }, - - beforeAdd: function (map) { - map._addZoomLimit(this); - }, - - onRemove: function (map) { - this._removeAllTiles(); - L.DomUtil.remove(this._container); - map._removeZoomLimit(this); - this._container = null; - this._tileZoom = null; - }, - - // @method bringToFront: this - // Brings the tile layer to the top of all tile layers. - bringToFront: function () { - if (this._map) { - L.DomUtil.toFront(this._container); - this._setAutoZIndex(Math.max); - } - return this; - }, - - // @method bringToBack: this - // Brings the tile layer to the bottom of all tile layers. - bringToBack: function () { - if (this._map) { - L.DomUtil.toBack(this._container); - this._setAutoZIndex(Math.min); - } - return this; - }, - - // @method getContainer: HTMLElement - // Returns the HTML element that contains the tiles for this layer. - getContainer: function () { - return this._container; - }, - - // @method setOpacity(opacity: Number): this - // Changes the [opacity](#gridlayer-opacity) of the grid layer. - setOpacity: function (opacity) { - this.options.opacity = opacity; - this._updateOpacity(); - return this; - }, - - // @method setZIndex(zIndex: Number): this - // Changes the [zIndex](#gridlayer-zindex) of the grid layer. - setZIndex: function (zIndex) { - this.options.zIndex = zIndex; - this._updateZIndex(); - - return this; - }, - - // @method isLoading: Boolean - // Returns `true` if any tile in the grid layer has not finished loading. - isLoading: function () { - return this._loading; - }, - - // @method redraw: this - // Causes the layer to clear all the tiles and request them again. - redraw: function () { - if (this._map) { - this._removeAllTiles(); - this._update(); - } - return this; - }, - - getEvents: function () { - var events = { - viewprereset: this._invalidateAll, - viewreset: this._resetView, - zoom: this._resetView, - moveend: this._onMoveEnd - }; - - if (!this.options.updateWhenIdle) { - // update tiles on move, but not more often than once per given interval - if (!this._onMove) { - this._onMove = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this); - } - - events.move = this._onMove; - } - - if (this._zoomAnimated) { - events.zoomanim = this._animateZoom; - } - - return events; - }, - - // @section Extension methods - // Layers extending `GridLayer` shall reimplement the following method. - // @method createTile(coords: Object, done?: Function): HTMLElement - // Called only internally, must be overriden by classes extending `GridLayer`. - // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback - // is specified, it must be called when the tile has finished loading and drawing. - createTile: function () { - return document.createElement('div'); - }, - - // @section - // @method getTileSize: Point - // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method. - getTileSize: function () { - var s = this.options.tileSize; - return s instanceof L.Point ? s : new L.Point(s, s); - }, - - _updateZIndex: function () { - if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) { - this._container.style.zIndex = this.options.zIndex; - } - }, - - _setAutoZIndex: function (compare) { - // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back) - - var layers = this.getPane().children, - edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min - - for (var i = 0, len = layers.length, zIndex; i < len; i++) { - - zIndex = layers[i].style.zIndex; - - if (layers[i] !== this._container && zIndex) { - edgeZIndex = compare(edgeZIndex, +zIndex); - } - } - - if (isFinite(edgeZIndex)) { - this.options.zIndex = edgeZIndex + compare(-1, 1); - this._updateZIndex(); - } - }, - - _updateOpacity: function () { - if (!this._map) { return; } - - // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles - if (L.Browser.ielt9) { return; } - - L.DomUtil.setOpacity(this._container, this.options.opacity); - - var now = +new Date(), - nextFrame = false, - willPrune = false; - - for (var key in this._tiles) { - var tile = this._tiles[key]; - if (!tile.current || !tile.loaded) { continue; } - - var fade = Math.min(1, (now - tile.loaded) / 200); - - L.DomUtil.setOpacity(tile.el, fade); - if (fade < 1) { - nextFrame = true; - } else { - if (tile.active) { willPrune = true; } - tile.active = true; - } - } - - if (willPrune && !this._noPrune) { this._pruneTiles(); } - - if (nextFrame) { - L.Util.cancelAnimFrame(this._fadeFrame); - this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this); - } - }, - - _initContainer: function () { - if (this._container) { return; } - - this._container = L.DomUtil.create('div', 'leaflet-layer ' + (this.options.className || '')); - this._updateZIndex(); - - if (this.options.opacity < 1) { - this._updateOpacity(); - } - - this.getPane().appendChild(this._container); - }, - - _updateLevels: function () { - - var zoom = this._tileZoom, - maxZoom = this.options.maxZoom; - - if (zoom === undefined) { return undefined; } - - for (var z in this._levels) { - if (this._levels[z].el.children.length || z === zoom) { - this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z); - } else { - L.DomUtil.remove(this._levels[z].el); - this._removeTilesAtZoom(z); - delete this._levels[z]; - } - } - - var level = this._levels[zoom], - map = this._map; - - if (!level) { - level = this._levels[zoom] = {}; - - level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container); - level.el.style.zIndex = maxZoom; - - level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round(); - level.zoom = zoom; - - this._setZoomTransform(level, map.getCenter(), map.getZoom()); - - // force the browser to consider the newly added element for transition - L.Util.falseFn(level.el.offsetWidth); - } - - this._level = level; - - return level; - }, - - _pruneTiles: function () { - if (!this._map) { - return; - } - - var key, tile; - - var zoom = this._map.getZoom(); - if (zoom > this.options.maxZoom || - zoom < this.options.minZoom) { - this._removeAllTiles(); - return; - } - - for (key in this._tiles) { - tile = this._tiles[key]; - tile.retain = tile.current; - } - - for (key in this._tiles) { - tile = this._tiles[key]; - if (tile.current && !tile.active) { - var coords = tile.coords; - if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) { - this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2); - } - } - } - - for (key in this._tiles) { - if (!this._tiles[key].retain) { - this._removeTile(key); - } - } - }, - - _removeTilesAtZoom: function (zoom) { - for (var key in this._tiles) { - if (this._tiles[key].coords.z !== zoom) { - continue; - } - this._removeTile(key); - } - }, - - _removeAllTiles: function () { - for (var key in this._tiles) { - this._removeTile(key); - } - }, - - _invalidateAll: function () { - for (var z in this._levels) { - L.DomUtil.remove(this._levels[z].el); - delete this._levels[z]; - } - this._removeAllTiles(); - - this._tileZoom = null; - }, - - _retainParent: function (x, y, z, minZoom) { - var x2 = Math.floor(x / 2), - y2 = Math.floor(y / 2), - z2 = z - 1, - coords2 = new L.Point(+x2, +y2); - coords2.z = +z2; - - var key = this._tileCoordsToKey(coords2), - tile = this._tiles[key]; - - if (tile && tile.active) { - tile.retain = true; - return true; - - } else if (tile && tile.loaded) { - tile.retain = true; - } - - if (z2 > minZoom) { - return this._retainParent(x2, y2, z2, minZoom); - } - - return false; - }, - - _retainChildren: function (x, y, z, maxZoom) { - - for (var i = 2 * x; i < 2 * x + 2; i++) { - for (var j = 2 * y; j < 2 * y + 2; j++) { - - var coords = new L.Point(i, j); - coords.z = z + 1; - - var key = this._tileCoordsToKey(coords), - tile = this._tiles[key]; - - if (tile && tile.active) { - tile.retain = true; - continue; - - } else if (tile && tile.loaded) { - tile.retain = true; - } - - if (z + 1 < maxZoom) { - this._retainChildren(i, j, z + 1, maxZoom); - } - } - } - }, - - _resetView: function (e) { - var animating = e && (e.pinch || e.flyTo); - this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating); - }, - - _animateZoom: function (e) { - this._setView(e.center, e.zoom, true, e.noUpdate); - }, - - _setView: function (center, zoom, noPrune, noUpdate) { - var tileZoom = Math.round(zoom); - if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) || - (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) { - tileZoom = undefined; - } - - var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom); - - if (!noUpdate || tileZoomChanged) { - - this._tileZoom = tileZoom; - - if (this._abortLoading) { - this._abortLoading(); - } - - this._updateLevels(); - this._resetGrid(); - - if (tileZoom !== undefined) { - this._update(center); - } - - if (!noPrune) { - this._pruneTiles(); - } - - // Flag to prevent _updateOpacity from pruning tiles during - // a zoom anim or a pinch gesture - this._noPrune = !!noPrune; - } - - this._setZoomTransforms(center, zoom); - }, - - _setZoomTransforms: function (center, zoom) { - for (var i in this._levels) { - this._setZoomTransform(this._levels[i], center, zoom); - } - }, - - _setZoomTransform: function (level, center, zoom) { - var scale = this._map.getZoomScale(zoom, level.zoom), - translate = level.origin.multiplyBy(scale) - .subtract(this._map._getNewPixelOrigin(center, zoom)).round(); - - if (L.Browser.any3d) { - L.DomUtil.setTransform(level.el, translate, scale); - } else { - L.DomUtil.setPosition(level.el, translate); - } - }, - - _resetGrid: function () { - var map = this._map, - crs = map.options.crs, - tileSize = this._tileSize = this.getTileSize(), - tileZoom = this._tileZoom; - - var bounds = this._map.getPixelWorldBounds(this._tileZoom); - if (bounds) { - this._globalTileRange = this._pxBoundsToTileRange(bounds); - } - - this._wrapX = crs.wrapLng && !this.options.noWrap && [ - Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x), - Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y) - ]; - this._wrapY = crs.wrapLat && !this.options.noWrap && [ - Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x), - Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y) - ]; - }, - - _onMoveEnd: function () { - if (!this._map || this._map._animatingZoom) { return; } - - this._update(); - }, - - _getTiledPixelBounds: function (center) { - var map = this._map, - mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(), - scale = map.getZoomScale(mapZoom, this._tileZoom), - pixelCenter = map.project(center, this._tileZoom).floor(), - halfSize = map.getSize().divideBy(scale * 2); - - return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize)); - }, - - // Private method to load tiles in the grid's active zoom level according to map bounds - _update: function (center) { - var map = this._map; - if (!map) { return; } - var zoom = map.getZoom(); - - if (center === undefined) { center = map.getCenter(); } - if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom - - var pixelBounds = this._getTiledPixelBounds(center), - tileRange = this._pxBoundsToTileRange(pixelBounds), - tileCenter = tileRange.getCenter(), - queue = [], - margin = this.options.keepBuffer, - noPruneRange = new L.Bounds(tileRange.getBottomLeft().subtract([margin, -margin]), - tileRange.getTopRight().add([margin, -margin])); - - for (var key in this._tiles) { - var c = this._tiles[key].coords; - if (c.z !== this._tileZoom || !noPruneRange.contains(L.point(c.x, c.y))) { - this._tiles[key].current = false; - } - } - - // _update just loads more tiles. If the tile zoom level differs too much - // from the map's, let _setView reset levels and prune old tiles. - if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; } - - // create a queue of coordinates to load tiles from - for (var j = tileRange.min.y; j <= tileRange.max.y; j++) { - for (var i = tileRange.min.x; i <= tileRange.max.x; i++) { - var coords = new L.Point(i, j); - coords.z = this._tileZoom; - - if (!this._isValidTile(coords)) { continue; } - - var tile = this._tiles[this._tileCoordsToKey(coords)]; - if (tile) { - tile.current = true; - } else { - queue.push(coords); - } - } - } - - // sort tile queue to load tiles in order of their distance to center - queue.sort(function (a, b) { - return a.distanceTo(tileCenter) - b.distanceTo(tileCenter); - }); - - if (queue.length !== 0) { - // if it's the first batch of tiles to load - if (!this._loading) { - this._loading = true; - // @event loading: Event - // Fired when the grid layer starts loading tiles. - this.fire('loading'); - } - - // create DOM fragment to append tiles in one batch - var fragment = document.createDocumentFragment(); - - for (i = 0; i < queue.length; i++) { - this._addTile(queue[i], fragment); - } - - this._level.el.appendChild(fragment); - } - }, - - _isValidTile: function (coords) { - var crs = this._map.options.crs; - - if (!crs.infinite) { - // don't load tile if it's out of bounds and not wrapped - var bounds = this._globalTileRange; - if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) || - (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; } - } - - if (!this.options.bounds) { return true; } - - // don't load tile if it doesn't intersect the bounds in options - var tileBounds = this._tileCoordsToBounds(coords); - return L.latLngBounds(this.options.bounds).overlaps(tileBounds); - }, - - _keyToBounds: function (key) { - return this._tileCoordsToBounds(this._keyToTileCoords(key)); - }, - - // converts tile coordinates to its geographical bounds - _tileCoordsToBounds: function (coords) { - - var map = this._map, - tileSize = this.getTileSize(), - - nwPoint = coords.scaleBy(tileSize), - sePoint = nwPoint.add(tileSize), - - nw = map.unproject(nwPoint, coords.z), - se = map.unproject(sePoint, coords.z), - bounds = new L.LatLngBounds(nw, se); - - if (!this.options.noWrap) { - map.wrapLatLngBounds(bounds); - } - - return bounds; - }, - - // converts tile coordinates to key for the tile cache - _tileCoordsToKey: function (coords) { - return coords.x + ':' + coords.y + ':' + coords.z; - }, - - // converts tile cache key to coordinates - _keyToTileCoords: function (key) { - var k = key.split(':'), - coords = new L.Point(+k[0], +k[1]); - coords.z = +k[2]; - return coords; - }, - - _removeTile: function (key) { - var tile = this._tiles[key]; - if (!tile) { return; } - - L.DomUtil.remove(tile.el); - - delete this._tiles[key]; - - // @event tileunload: TileEvent - // Fired when a tile is removed (e.g. when a tile goes off the screen). - this.fire('tileunload', { - tile: tile.el, - coords: this._keyToTileCoords(key) - }); - }, - - _initTile: function (tile) { - L.DomUtil.addClass(tile, 'leaflet-tile'); - - var tileSize = this.getTileSize(); - tile.style.width = tileSize.x + 'px'; - tile.style.height = tileSize.y + 'px'; - - tile.onselectstart = L.Util.falseFn; - tile.onmousemove = L.Util.falseFn; - - // update opacity on tiles in IE7-8 because of filter inheritance problems - if (L.Browser.ielt9 && this.options.opacity < 1) { - L.DomUtil.setOpacity(tile, this.options.opacity); - } - - // without this hack, tiles disappear after zoom on Chrome for Android - // https://github.com/Leaflet/Leaflet/issues/2078 - if (L.Browser.android && !L.Browser.android23) { - tile.style.WebkitBackfaceVisibility = 'hidden'; - } - }, - - _addTile: function (coords, container) { - var tilePos = this._getTilePos(coords), - key = this._tileCoordsToKey(coords); - - var tile = this.createTile(this._wrapCoords(coords), L.bind(this._tileReady, this, coords)); - - this._initTile(tile); - - // if createTile is defined with a second argument ("done" callback), - // we know that tile is async and will be ready later; otherwise - if (this.createTile.length < 2) { - // mark tile as ready, but delay one frame for opacity animation to happen - L.Util.requestAnimFrame(L.bind(this._tileReady, this, coords, null, tile)); - } - - L.DomUtil.setPosition(tile, tilePos); - - // save tile in cache - this._tiles[key] = { - el: tile, - coords: coords, - current: true - }; - - container.appendChild(tile); - // @event tileloadstart: TileEvent - // Fired when a tile is requested and starts loading. - this.fire('tileloadstart', { - tile: tile, - coords: coords - }); - }, - - _tileReady: function (coords, err, tile) { - if (!this._map) { return; } - - if (err) { - // @event tileerror: TileErrorEvent - // Fired when there is an error loading a tile. - this.fire('tileerror', { - error: err, - tile: tile, - coords: coords - }); - } - - var key = this._tileCoordsToKey(coords); - - tile = this._tiles[key]; - if (!tile) { return; } - - tile.loaded = +new Date(); - if (this._map._fadeAnimated) { - L.DomUtil.setOpacity(tile.el, 0); - L.Util.cancelAnimFrame(this._fadeFrame); - this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this); - } else { - tile.active = true; - this._pruneTiles(); - } - - if (!err) { - L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded'); - - // @event tileload: TileEvent - // Fired when a tile loads. - this.fire('tileload', { - tile: tile.el, - coords: coords - }); - } - - if (this._noTilesToLoad()) { - this._loading = false; - // @event load: Event - // Fired when the grid layer loaded all visible tiles. - this.fire('load'); - - if (L.Browser.ielt9 || !this._map._fadeAnimated) { - L.Util.requestAnimFrame(this._pruneTiles, this); - } else { - // Wait a bit more than 0.2 secs (the duration of the tile fade-in) - // to trigger a pruning. - setTimeout(L.bind(this._pruneTiles, this), 250); - } - } - }, - - _getTilePos: function (coords) { - return coords.scaleBy(this.getTileSize()).subtract(this._level.origin); - }, - - _wrapCoords: function (coords) { - var newCoords = new L.Point( - this._wrapX ? L.Util.wrapNum(coords.x, this._wrapX) : coords.x, - this._wrapY ? L.Util.wrapNum(coords.y, this._wrapY) : coords.y); - newCoords.z = coords.z; - return newCoords; - }, - - _pxBoundsToTileRange: function (bounds) { - var tileSize = this.getTileSize(); - return new L.Bounds( - bounds.min.unscaleBy(tileSize).floor(), - bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1])); - }, - - _noTilesToLoad: function () { - for (var key in this._tiles) { - if (!this._tiles[key].loaded) { return false; } - } - return true; - } -}); - -// @factory L.gridLayer(options?: GridLayer options) -// Creates a new instance of GridLayer with the supplied options. -L.gridLayer = function (options) { - return new L.GridLayer(options); -}; - - - -/* - * @class TileLayer - * @inherits GridLayer - * @aka L.TileLayer - * Used to load and display tile layers on the map. Extends `GridLayer`. - * - * @example - * - * ```js - * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map); - * ``` - * - * @section URL template - * @example - * - * A string of the following form: - * - * ``` - * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png' - * ``` - * - * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add @2x to the URL to load retina tiles. - * - * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this: - * - * ``` - * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'}); - * ``` - */ - - -L.TileLayer = L.GridLayer.extend({ - - // @section - // @aka TileLayer options - options: { - // @option minZoom: Number = 0 - // Minimum zoom number. - minZoom: 0, - - // @option maxZoom: Number = 18 - // Maximum zoom number. - maxZoom: 18, - - // @option maxNativeZoom: Number = null - // Maximum zoom number the tile source has available. If it is specified, - // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded - // from `maxNativeZoom` level and auto-scaled. - maxNativeZoom: null, - - // @option minNativeZoom: Number = null - // Minimum zoom number the tile source has available. If it is specified, - // the tiles on all zoom levels lower than `minNativeZoom` will be loaded - // from `minNativeZoom` level and auto-scaled. - minNativeZoom: null, - - // @option subdomains: String|String[] = 'abc' - // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings. - subdomains: 'abc', - - // @option errorTileUrl: String = '' - // URL to the tile image to show in place of the tile that failed to load. - errorTileUrl: '', - - // @option zoomOffset: Number = 0 - // The zoom number used in tile URLs will be offset with this value. - zoomOffset: 0, - - // @option tms: Boolean = false - // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services). - tms: false, - - // @option zoomReverse: Boolean = false - // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`) - zoomReverse: false, - - // @option detectRetina: Boolean = false - // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution. - detectRetina: false, - - // @option crossOrigin: Boolean = false - // If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data. - crossOrigin: false - }, - - initialize: function (url, options) { - - this._url = url; - - options = L.setOptions(this, options); - - // detecting retina displays, adjusting tileSize and zoom levels - if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) { - - options.tileSize = Math.floor(options.tileSize / 2); - - if (!options.zoomReverse) { - options.zoomOffset++; - options.maxZoom--; - } else { - options.zoomOffset--; - options.minZoom++; - } - - options.minZoom = Math.max(0, options.minZoom); - } - - if (typeof options.subdomains === 'string') { - options.subdomains = options.subdomains.split(''); - } - - // for https://github.com/Leaflet/Leaflet/issues/137 - if (!L.Browser.android) { - this.on('tileunload', this._onTileRemove); - } - }, - - // @method setUrl(url: String, noRedraw?: Boolean): this - // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`). - setUrl: function (url, noRedraw) { - this._url = url; - - if (!noRedraw) { - this.redraw(); - } - return this; - }, - - // @method createTile(coords: Object, done?: Function): HTMLElement - // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile) - // to return an `` HTML element with the appropiate image URL given `coords`. The `done` - // callback is called when the tile has been loaded. - createTile: function (coords, done) { - var tile = document.createElement('img'); - - L.DomEvent.on(tile, 'load', L.bind(this._tileOnLoad, this, done, tile)); - L.DomEvent.on(tile, 'error', L.bind(this._tileOnError, this, done, tile)); - - if (this.options.crossOrigin) { - tile.crossOrigin = ''; - } - - /* - Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons - http://www.w3.org/TR/WCAG20-TECHS/H67 - */ - tile.alt = ''; - - /* - Set role="presentation" to force screen readers to ignore this - https://www.w3.org/TR/wai-aria/roles#textalternativecomputation - */ - tile.setAttribute('role', 'presentation'); - - tile.src = this.getTileUrl(coords); - - return tile; - }, - - // @section Extension methods - // @uninheritable - // Layers extending `TileLayer` might reimplement the following method. - // @method getTileUrl(coords: Object): String - // Called only internally, returns the URL for a tile given its coordinates. - // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes. - getTileUrl: function (coords) { - var data = { - r: L.Browser.retina ? '@2x' : '', - s: this._getSubdomain(coords), - x: coords.x, - y: coords.y, - z: this._getZoomForUrl() - }; - if (this._map && !this._map.options.crs.infinite) { - var invertedY = this._globalTileRange.max.y - coords.y; - if (this.options.tms) { - data['y'] = invertedY; - } - data['-y'] = invertedY; - } - - return L.Util.template(this._url, L.extend(data, this.options)); - }, - - _tileOnLoad: function (done, tile) { - // For https://github.com/Leaflet/Leaflet/issues/3332 - if (L.Browser.ielt9) { - setTimeout(L.bind(done, this, null, tile), 0); - } else { - done(null, tile); - } - }, - - _tileOnError: function (done, tile, e) { - var errorUrl = this.options.errorTileUrl; - if (errorUrl && tile.src !== errorUrl) { - tile.src = errorUrl; - } - done(e, tile); - }, - - getTileSize: function () { - var map = this._map, - tileSize = L.GridLayer.prototype.getTileSize.call(this), - zoom = this._tileZoom + this.options.zoomOffset, - minNativeZoom = this.options.minNativeZoom, - maxNativeZoom = this.options.maxNativeZoom; - - // decrease tile size when scaling below minNativeZoom - if (minNativeZoom !== null && zoom < minNativeZoom) { - return tileSize.divideBy(map.getZoomScale(minNativeZoom, zoom)).round(); - } - - // increase tile size when scaling above maxNativeZoom - if (maxNativeZoom !== null && zoom > maxNativeZoom) { - return tileSize.divideBy(map.getZoomScale(maxNativeZoom, zoom)).round(); - } - - return tileSize; - }, - - _onTileRemove: function (e) { - e.tile.onload = null; - }, - - _getZoomForUrl: function () { - var zoom = this._tileZoom, - maxZoom = this.options.maxZoom, - zoomReverse = this.options.zoomReverse, - zoomOffset = this.options.zoomOffset, - minNativeZoom = this.options.minNativeZoom, - maxNativeZoom = this.options.maxNativeZoom; - - if (zoomReverse) { - zoom = maxZoom - zoom; - } - - zoom += zoomOffset; - - if (minNativeZoom !== null && zoom < minNativeZoom) { - return minNativeZoom; - } - - if (maxNativeZoom !== null && zoom > maxNativeZoom) { - return maxNativeZoom; - } - - return zoom; - }, - - _getSubdomain: function (tilePoint) { - var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length; - return this.options.subdomains[index]; - }, - - // stops loading all tiles in the background layer - _abortLoading: function () { - var i, tile; - for (i in this._tiles) { - if (this._tiles[i].coords.z !== this._tileZoom) { - tile = this._tiles[i].el; - - tile.onload = L.Util.falseFn; - tile.onerror = L.Util.falseFn; - - if (!tile.complete) { - tile.src = L.Util.emptyImageUrl; - L.DomUtil.remove(tile); - } - } - } - } -}); - - -// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options) -// Instantiates a tile layer object given a `URL template` and optionally an options object. - -L.tileLayer = function (url, options) { - return new L.TileLayer(url, options); -}; - - - -/* - * @class TileLayer.WMS - * @inherits TileLayer - * @aka L.TileLayer.WMS - * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`. - * - * @example - * - * ```js - * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", { - * layers: 'nexrad-n0r-900913', - * format: 'image/png', - * transparent: true, - * attribution: "Weather data © 2012 IEM Nexrad" - * }); - * ``` - */ - -L.TileLayer.WMS = L.TileLayer.extend({ - - // @section - // @aka TileLayer.WMS options - // If any custom options not documented here are used, they will be sent to the - // WMS server as extra parameters in each request URL. This can be useful for - // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html). - defaultWmsParams: { - service: 'WMS', - request: 'GetMap', - - // @option layers: String = '' - // **(required)** Comma-separated list of WMS layers to show. - layers: '', - - // @option styles: String = '' - // Comma-separated list of WMS styles. - styles: '', - - // @option format: String = 'image/jpeg' - // WMS image format (use `'image/png'` for layers with transparency). - format: 'image/jpeg', - - // @option transparent: Boolean = false - // If `true`, the WMS service will return images with transparency. - transparent: false, - - // @option version: String = '1.1.1' - // Version of the WMS service to use - version: '1.1.1' - }, - - options: { - // @option crs: CRS = null - // Coordinate Reference System to use for the WMS requests, defaults to - // map CRS. Don't change this if you're not sure what it means. - crs: null, - - // @option uppercase: Boolean = false - // If `true`, WMS request parameter keys will be uppercase. - uppercase: false - }, - - initialize: function (url, options) { - - this._url = url; - - var wmsParams = L.extend({}, this.defaultWmsParams); - - // all keys that are not TileLayer options go to WMS params - for (var i in options) { - if (!(i in this.options)) { - wmsParams[i] = options[i]; - } - } - - options = L.setOptions(this, options); - - wmsParams.width = wmsParams.height = options.tileSize * (options.detectRetina && L.Browser.retina ? 2 : 1); - - this.wmsParams = wmsParams; - }, - - onAdd: function (map) { - - this._crs = this.options.crs || map.options.crs; - this._wmsVersion = parseFloat(this.wmsParams.version); - - var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs'; - this.wmsParams[projectionKey] = this._crs.code; - - L.TileLayer.prototype.onAdd.call(this, map); - }, - - getTileUrl: function (coords) { - - var tileBounds = this._tileCoordsToBounds(coords), - nw = this._crs.project(tileBounds.getNorthWest()), - se = this._crs.project(tileBounds.getSouthEast()), - - bbox = (this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ? - [se.y, nw.x, nw.y, se.x] : - [nw.x, se.y, se.x, nw.y]).join(','), - - url = L.TileLayer.prototype.getTileUrl.call(this, coords); - - return url + - L.Util.getParamString(this.wmsParams, url, this.options.uppercase) + - (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox; - }, - - // @method setParams(params: Object, noRedraw?: Boolean): this - // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true). - setParams: function (params, noRedraw) { - - L.extend(this.wmsParams, params); - - if (!noRedraw) { - this.redraw(); - } - - return this; - } -}); - - -// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options) -// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object. -L.tileLayer.wms = function (url, options) { - return new L.TileLayer.WMS(url, options); -}; - - - -/* - * @class ImageOverlay - * @aka L.ImageOverlay - * @inherits Interactive layer - * - * Used to load and display a single image over specific bounds of the map. Extends `Layer`. - * - * @example - * - * ```js - * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg', - * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]]; - * L.imageOverlay(imageUrl, imageBounds).addTo(map); - * ``` - */ - -L.ImageOverlay = L.Layer.extend({ - - // @section - // @aka ImageOverlay options - options: { - // @option opacity: Number = 1.0 - // The opacity of the image overlay. - opacity: 1, - - // @option alt: String = '' - // Text for the `alt` attribute of the image (useful for accessibility). - alt: '', - - // @option interactive: Boolean = false - // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. - interactive: false, - - // @option crossOrigin: Boolean = false - // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data. - crossOrigin: false - }, - - initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) - this._url = url; - this._bounds = L.latLngBounds(bounds); - - L.setOptions(this, options); - }, - - onAdd: function () { - if (!this._image) { - this._initImage(); - - if (this.options.opacity < 1) { - this._updateOpacity(); - } - } - - if (this.options.interactive) { - L.DomUtil.addClass(this._image, 'leaflet-interactive'); - this.addInteractiveTarget(this._image); - } - - this.getPane().appendChild(this._image); - this._reset(); - }, - - onRemove: function () { - L.DomUtil.remove(this._image); - if (this.options.interactive) { - this.removeInteractiveTarget(this._image); - } - }, - - // @method setOpacity(opacity: Number): this - // Sets the opacity of the overlay. - setOpacity: function (opacity) { - this.options.opacity = opacity; - - if (this._image) { - this._updateOpacity(); - } - return this; - }, - - setStyle: function (styleOpts) { - if (styleOpts.opacity) { - this.setOpacity(styleOpts.opacity); - } - return this; - }, - - // @method bringToFront(): this - // Brings the layer to the top of all overlays. - bringToFront: function () { - if (this._map) { - L.DomUtil.toFront(this._image); - } - return this; - }, - - // @method bringToBack(): this - // Brings the layer to the bottom of all overlays. - bringToBack: function () { - if (this._map) { - L.DomUtil.toBack(this._image); - } - return this; - }, - - // @method setUrl(url: String): this - // Changes the URL of the image. - setUrl: function (url) { - this._url = url; - - if (this._image) { - this._image.src = url; - } - return this; - }, - - // @method setBounds(bounds: LatLngBounds): this - // Update the bounds that this ImageOverlay covers - setBounds: function (bounds) { - this._bounds = bounds; - - if (this._map) { - this._reset(); - } - return this; - }, - - getEvents: function () { - var events = { - zoom: this._reset, - viewreset: this._reset - }; - - if (this._zoomAnimated) { - events.zoomanim = this._animateZoom; - } - - return events; - }, - - // @method getBounds(): LatLngBounds - // Get the bounds that this ImageOverlay covers - getBounds: function () { - return this._bounds; - }, - - // @method getElement(): HTMLElement - // Get the img element that represents the ImageOverlay on the map - getElement: function () { - return this._image; - }, - - _initImage: function () { - var img = this._image = L.DomUtil.create('img', - 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : '')); - - img.onselectstart = L.Util.falseFn; - img.onmousemove = L.Util.falseFn; - - img.onload = L.bind(this.fire, this, 'load'); - - if (this.options.crossOrigin) { - img.crossOrigin = ''; - } - - img.src = this._url; - img.alt = this.options.alt; - }, - - _animateZoom: function (e) { - var scale = this._map.getZoomScale(e.zoom), - offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; - - L.DomUtil.setTransform(this._image, offset, scale); - }, - - _reset: function () { - var image = this._image, - bounds = new L.Bounds( - this._map.latLngToLayerPoint(this._bounds.getNorthWest()), - this._map.latLngToLayerPoint(this._bounds.getSouthEast())), - size = bounds.getSize(); - - L.DomUtil.setPosition(image, bounds.min); - - image.style.width = size.x + 'px'; - image.style.height = size.y + 'px'; - }, - - _updateOpacity: function () { - L.DomUtil.setOpacity(this._image, this.options.opacity); - } -}); - -// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options) -// Instantiates an image overlay object given the URL of the image and the -// geographical bounds it is tied to. -L.imageOverlay = function (url, bounds, options) { - return new L.ImageOverlay(url, bounds, options); -}; - - - -/* - * @class Icon - * @aka L.Icon - * @inherits Layer - * - * Represents an icon to provide when creating a marker. - * - * @example - * - * ```js - * var myIcon = L.icon({ - * iconUrl: 'my-icon.png', - * iconRetinaUrl: 'my-icon@2x.png', - * iconSize: [38, 95], - * iconAnchor: [22, 94], - * popupAnchor: [-3, -76], - * shadowUrl: 'my-icon-shadow.png', - * shadowRetinaUrl: 'my-icon-shadow@2x.png', - * shadowSize: [68, 95], - * shadowAnchor: [22, 94] - * }); - * - * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); - * ``` - * - * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default. - * - */ - -L.Icon = L.Class.extend({ - - /* @section - * @aka Icon options - * - * @option iconUrl: String = null - * **(required)** The URL to the icon image (absolute or relative to your script path). - * - * @option iconRetinaUrl: String = null - * The URL to a retina sized version of the icon image (absolute or relative to your - * script path). Used for Retina screen devices. - * - * @option iconSize: Point = null - * Size of the icon image in pixels. - * - * @option iconAnchor: Point = null - * The coordinates of the "tip" of the icon (relative to its top left corner). The icon - * will be aligned so that this point is at the marker's geographical location. Centered - * by default if size is specified, also can be set in CSS with negative margins. - * - * @option popupAnchor: Point = null - * The coordinates of the point from which popups will "open", relative to the icon anchor. - * - * @option shadowUrl: String = null - * The URL to the icon shadow image. If not specified, no shadow image will be created. - * - * @option shadowRetinaUrl: String = null - * - * @option shadowSize: Point = null - * Size of the shadow image in pixels. - * - * @option shadowAnchor: Point = null - * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same - * as iconAnchor if not specified). - * - * @option className: String = '' - * A custom class name to assign to both icon and shadow images. Empty by default. - */ - - initialize: function (options) { - L.setOptions(this, options); - }, - - // @method createIcon(oldIcon?: HTMLElement): HTMLElement - // Called internally when the icon has to be shown, returns a `` HTML element - // styled according to the options. - createIcon: function (oldIcon) { - return this._createIcon('icon', oldIcon); - }, - - // @method createShadow(oldIcon?: HTMLElement): HTMLElement - // As `createIcon`, but for the shadow beneath it. - createShadow: function (oldIcon) { - return this._createIcon('shadow', oldIcon); - }, - - _createIcon: function (name, oldIcon) { - var src = this._getIconUrl(name); - - if (!src) { - if (name === 'icon') { - throw new Error('iconUrl not set in Icon options (see the docs).'); - } - return null; - } - - var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null); - this._setIconStyles(img, name); - - return img; - }, - - _setIconStyles: function (img, name) { - var options = this.options; - var sizeOption = options[name + 'Size']; - - if (typeof sizeOption === 'number') { - sizeOption = [sizeOption, sizeOption]; - } - - var size = L.point(sizeOption), - anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor || - size && size.divideBy(2, true)); - - img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); - - if (anchor) { - img.style.marginLeft = (-anchor.x) + 'px'; - img.style.marginTop = (-anchor.y) + 'px'; - } - - if (size) { - img.style.width = size.x + 'px'; - img.style.height = size.y + 'px'; - } - }, - - _createImg: function (src, el) { - el = el || document.createElement('img'); - el.src = src; - return el; - }, - - _getIconUrl: function (name) { - return L.Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url']; - } -}); - - -// @factory L.icon(options: Icon options) -// Creates an icon instance with the given options. -L.icon = function (options) { - return new L.Icon(options); -}; - - - -/* - * @miniclass Icon.Default (Icon) - * @aka L.Icon.Default - * @section - * - * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when - * no icon is specified. Points to the blue marker image distributed with Leaflet - * releases. - * - * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options` - * (which is a set of `Icon options`). - * - * If you want to _completely_ replace the default icon, override the - * `L.Marker.prototype.options.icon` with your own icon instead. - */ - -L.Icon.Default = L.Icon.extend({ - - options: { - iconUrl: 'marker-icon.png', - iconRetinaUrl: 'marker-icon-2x.png', - shadowUrl: 'marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - tooltipAnchor: [16, -28], - shadowSize: [41, 41] - }, - - _getIconUrl: function (name) { - if (!L.Icon.Default.imagePath) { // Deprecated, backwards-compatibility only - L.Icon.Default.imagePath = this._detectIconPath(); - } - - // @option imagePath: String - // `L.Icon.Default` will try to auto-detect the absolute location of the - // blue icon images. If you are placing these images in a non-standard - // way, set this option to point to the right absolute path. - return (this.options.imagePath || L.Icon.Default.imagePath) + L.Icon.prototype._getIconUrl.call(this, name); - }, - - _detectIconPath: function () { - var el = L.DomUtil.create('div', 'leaflet-default-icon-path', document.body); - var path = L.DomUtil.getStyle(el, 'background-image') || - L.DomUtil.getStyle(el, 'backgroundImage'); // IE8 - - document.body.removeChild(el); - - return path.indexOf('url') === 0 ? - path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '') : ''; - } -}); - - - -/* - * @class Marker - * @inherits Interactive layer - * @aka L.Marker - * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`. - * - * @example - * - * ```js - * L.marker([50.5, 30.5]).addTo(map); - * ``` - */ - -L.Marker = L.Layer.extend({ - - // @section - // @aka Marker options - options: { - // @option icon: Icon = * - // Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. If not specified, a new `L.Icon.Default` is used. - icon: new L.Icon.Default(), - - // Option inherited from "Interactive layer" abstract class - interactive: true, - - // @option draggable: Boolean = false - // Whether the marker is draggable with mouse/touch or not. - draggable: false, - - // @option keyboard: Boolean = true - // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. - keyboard: true, - - // @option title: String = '' - // Text for the browser tooltip that appear on marker hover (no tooltip by default). - title: '', - - // @option alt: String = '' - // Text for the `alt` attribute of the icon image (useful for accessibility). - alt: '', - - // @option zIndexOffset: Number = 0 - // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively). - zIndexOffset: 0, - - // @option opacity: Number = 1.0 - // The opacity of the marker. - opacity: 1, - - // @option riseOnHover: Boolean = false - // If `true`, the marker will get on top of others when you hover the mouse over it. - riseOnHover: false, - - // @option riseOffset: Number = 250 - // The z-index offset used for the `riseOnHover` feature. - riseOffset: 250, - - // @option pane: String = 'markerPane' - // `Map pane` where the markers icon will be added. - pane: 'markerPane', - - // FIXME: shadowPane is no longer a valid option - nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'] - }, - - /* @section - * - * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: - */ - - initialize: function (latlng, options) { - L.setOptions(this, options); - this._latlng = L.latLng(latlng); - }, - - onAdd: function (map) { - this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation; - - if (this._zoomAnimated) { - map.on('zoomanim', this._animateZoom, this); - } - - this._initIcon(); - this.update(); - }, - - onRemove: function (map) { - if (this.dragging && this.dragging.enabled()) { - this.options.draggable = true; - this.dragging.removeHooks(); - } - - if (this._zoomAnimated) { - map.off('zoomanim', this._animateZoom, this); - } - - this._removeIcon(); - this._removeShadow(); - }, - - getEvents: function () { - return { - zoom: this.update, - viewreset: this.update - }; - }, - - // @method getLatLng: LatLng - // Returns the current geographical position of the marker. - getLatLng: function () { - return this._latlng; - }, - - // @method setLatLng(latlng: LatLng): this - // Changes the marker position to the given point. - setLatLng: function (latlng) { - var oldLatLng = this._latlng; - this._latlng = L.latLng(latlng); - this.update(); - - // @event move: Event - // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. - return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); - }, - - // @method setZIndexOffset(offset: Number): this - // Changes the [zIndex offset](#marker-zindexoffset) of the marker. - setZIndexOffset: function (offset) { - this.options.zIndexOffset = offset; - return this.update(); - }, - - // @method setIcon(icon: Icon): this - // Changes the marker icon. - setIcon: function (icon) { - - this.options.icon = icon; - - if (this._map) { - this._initIcon(); - this.update(); - } - - if (this._popup) { - this.bindPopup(this._popup, this._popup.options); - } - - return this; - }, - - getElement: function () { - return this._icon; - }, - - update: function () { - - if (this._icon) { - var pos = this._map.latLngToLayerPoint(this._latlng).round(); - this._setPos(pos); - } - - return this; - }, - - _initIcon: function () { - var options = this.options, - classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); - - var icon = options.icon.createIcon(this._icon), - addIcon = false; - - // if we're not reusing the icon, remove the old one and init new one - if (icon !== this._icon) { - if (this._icon) { - this._removeIcon(); - } - addIcon = true; - - if (options.title) { - icon.title = options.title; - } - if (options.alt) { - icon.alt = options.alt; - } - } - - L.DomUtil.addClass(icon, classToAdd); - - if (options.keyboard) { - icon.tabIndex = '0'; - } - - this._icon = icon; - - if (options.riseOnHover) { - this.on({ - mouseover: this._bringToFront, - mouseout: this._resetZIndex - }); - } - - var newShadow = options.icon.createShadow(this._shadow), - addShadow = false; - - if (newShadow !== this._shadow) { - this._removeShadow(); - addShadow = true; - } - - if (newShadow) { - L.DomUtil.addClass(newShadow, classToAdd); - newShadow.alt = ''; - } - this._shadow = newShadow; - - - if (options.opacity < 1) { - this._updateOpacity(); - } - - - if (addIcon) { - this.getPane().appendChild(this._icon); - } - this._initInteraction(); - if (newShadow && addShadow) { - this.getPane('shadowPane').appendChild(this._shadow); - } - }, - - _removeIcon: function () { - if (this.options.riseOnHover) { - this.off({ - mouseover: this._bringToFront, - mouseout: this._resetZIndex - }); - } - - L.DomUtil.remove(this._icon); - this.removeInteractiveTarget(this._icon); - - this._icon = null; - }, - - _removeShadow: function () { - if (this._shadow) { - L.DomUtil.remove(this._shadow); - } - this._shadow = null; - }, - - _setPos: function (pos) { - L.DomUtil.setPosition(this._icon, pos); - - if (this._shadow) { - L.DomUtil.setPosition(this._shadow, pos); - } - - this._zIndex = pos.y + this.options.zIndexOffset; - - this._resetZIndex(); - }, - - _updateZIndex: function (offset) { - this._icon.style.zIndex = this._zIndex + offset; - }, - - _animateZoom: function (opt) { - var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); - - this._setPos(pos); - }, - - _initInteraction: function () { - - if (!this.options.interactive) { return; } - - L.DomUtil.addClass(this._icon, 'leaflet-interactive'); - - this.addInteractiveTarget(this._icon); - - if (L.Handler.MarkerDrag) { - var draggable = this.options.draggable; - if (this.dragging) { - draggable = this.dragging.enabled(); - this.dragging.disable(); - } - - this.dragging = new L.Handler.MarkerDrag(this); - - if (draggable) { - this.dragging.enable(); - } - } - }, - - // @method setOpacity(opacity: Number): this - // Changes the opacity of the marker. - setOpacity: function (opacity) { - this.options.opacity = opacity; - if (this._map) { - this._updateOpacity(); - } - - return this; - }, - - _updateOpacity: function () { - var opacity = this.options.opacity; - - L.DomUtil.setOpacity(this._icon, opacity); - - if (this._shadow) { - L.DomUtil.setOpacity(this._shadow, opacity); - } - }, - - _bringToFront: function () { - this._updateZIndex(this.options.riseOffset); - }, - - _resetZIndex: function () { - this._updateZIndex(0); - }, - - _getPopupAnchor: function () { - return this.options.icon.options.popupAnchor || [0, 0]; - }, - - _getTooltipAnchor: function () { - return this.options.icon.options.tooltipAnchor || [0, 0]; - } -}); - - -// factory L.marker(latlng: LatLng, options? : Marker options) - -// @factory L.marker(latlng: LatLng, options? : Marker options) -// Instantiates a Marker object given a geographical point and optionally an options object. -L.marker = function (latlng, options) { - return new L.Marker(latlng, options); -}; - - - -/* - * @class DivIcon - * @aka L.DivIcon - * @inherits Icon - * - * Represents a lightweight icon for markers that uses a simple `
` - * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options. - * - * @example - * ```js - * var myIcon = L.divIcon({className: 'my-div-icon'}); - * // you can set .my-div-icon styles in CSS - * - * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); - * ``` - * - * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow. - */ - -L.DivIcon = L.Icon.extend({ - options: { - // @section - // @aka DivIcon options - iconSize: [12, 12], // also can be set through CSS - - // iconAnchor: (Point), - // popupAnchor: (Point), - - // @option html: String = '' - // Custom HTML code to put inside the div element, empty by default. - html: false, - - // @option bgPos: Point = [0, 0] - // Optional relative position of the background, in pixels - bgPos: null, - - className: 'leaflet-div-icon' - }, - - createIcon: function (oldIcon) { - var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'), - options = this.options; - - div.innerHTML = options.html !== false ? options.html : ''; - - if (options.bgPos) { - var bgPos = L.point(options.bgPos); - div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px'; - } - this._setIconStyles(div, 'icon'); - - return div; - }, - - createShadow: function () { - return null; - } -}); - -// @factory L.divIcon(options: DivIcon options) -// Creates a `DivIcon` instance with the given options. -L.divIcon = function (options) { - return new L.DivIcon(options); -}; - - - -/* - * @class DivOverlay - * @inherits Layer - * @aka L.DivOverlay - * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins. - */ - -// @namespace DivOverlay -L.DivOverlay = L.Layer.extend({ - - // @section - // @aka DivOverlay options - options: { - // @option offset: Point = Point(0, 7) - // The offset of the popup position. Useful to control the anchor - // of the popup when opening it on some overlays. - offset: [0, 7], - - // @option className: String = '' - // A custom CSS class name to assign to the popup. - className: '', - - // @option pane: String = 'popupPane' - // `Map pane` where the popup will be added. - pane: 'popupPane' - }, - - initialize: function (options, source) { - L.setOptions(this, options); - - this._source = source; - }, - - onAdd: function (map) { - this._zoomAnimated = map._zoomAnimated; - - if (!this._container) { - this._initLayout(); - } - - if (map._fadeAnimated) { - L.DomUtil.setOpacity(this._container, 0); - } - - clearTimeout(this._removeTimeout); - this.getPane().appendChild(this._container); - this.update(); - - if (map._fadeAnimated) { - L.DomUtil.setOpacity(this._container, 1); - } - - this.bringToFront(); - }, - - onRemove: function (map) { - if (map._fadeAnimated) { - L.DomUtil.setOpacity(this._container, 0); - this._removeTimeout = setTimeout(L.bind(L.DomUtil.remove, L.DomUtil, this._container), 200); - } else { - L.DomUtil.remove(this._container); - } - }, - - // @namespace Popup - // @method getLatLng: LatLng - // Returns the geographical point of popup. - getLatLng: function () { - return this._latlng; - }, - - // @method setLatLng(latlng: LatLng): this - // Sets the geographical point where the popup will open. - setLatLng: function (latlng) { - this._latlng = L.latLng(latlng); - if (this._map) { - this._updatePosition(); - this._adjustPan(); - } - return this; - }, - - // @method getContent: String|HTMLElement - // Returns the content of the popup. - getContent: function () { - return this._content; - }, - - // @method setContent(htmlContent: String|HTMLElement|Function): this - // Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup. - setContent: function (content) { - this._content = content; - this.update(); - return this; - }, - - // @method getElement: String|HTMLElement - // Alias for [getContent()](#popup-getcontent) - getElement: function () { - return this._container; - }, - - // @method update: null - // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded. - update: function () { - if (!this._map) { return; } - - this._container.style.visibility = 'hidden'; - - this._updateContent(); - this._updateLayout(); - this._updatePosition(); - - this._container.style.visibility = ''; - - this._adjustPan(); - }, - - getEvents: function () { - var events = { - zoom: this._updatePosition, - viewreset: this._updatePosition - }; - - if (this._zoomAnimated) { - events.zoomanim = this._animateZoom; - } - return events; - }, - - // @method isOpen: Boolean - // Returns `true` when the popup is visible on the map. - isOpen: function () { - return !!this._map && this._map.hasLayer(this); - }, - - // @method bringToFront: this - // Brings this popup in front of other popups (in the same map pane). - bringToFront: function () { - if (this._map) { - L.DomUtil.toFront(this._container); - } - return this; - }, - - // @method bringToBack: this - // Brings this popup to the back of other popups (in the same map pane). - bringToBack: function () { - if (this._map) { - L.DomUtil.toBack(this._container); - } - return this; - }, - - _updateContent: function () { - if (!this._content) { return; } - - var node = this._contentNode; - var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content; - - if (typeof content === 'string') { - node.innerHTML = content; - } else { - while (node.hasChildNodes()) { - node.removeChild(node.firstChild); - } - node.appendChild(content); - } - this.fire('contentupdate'); - }, - - _updatePosition: function () { - if (!this._map) { return; } - - var pos = this._map.latLngToLayerPoint(this._latlng), - offset = L.point(this.options.offset), - anchor = this._getAnchor(); - - if (this._zoomAnimated) { - L.DomUtil.setPosition(this._container, pos.add(anchor)); - } else { - offset = offset.add(pos).add(anchor); - } - - var bottom = this._containerBottom = -offset.y, - left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x; - - // bottom position the popup in case the height of the popup changes (images loading etc) - this._container.style.bottom = bottom + 'px'; - this._container.style.left = left + 'px'; - }, - - _getAnchor: function () { - return [0, 0]; - } - -}); - - - -/* - * @class Popup - * @inherits DivOverlay - * @aka L.Popup - * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to - * open popups while making sure that only one popup is open at one time - * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want. - * - * @example - * - * If you want to just bind a popup to marker click and then open it, it's really easy: - * - * ```js - * marker.bindPopup(popupContent).openPopup(); - * ``` - * Path overlays like polylines also have a `bindPopup` method. - * Here's a more complicated way to open a popup on a map: - * - * ```js - * var popup = L.popup() - * .setLatLng(latlng) - * .setContent('

Hello world!
This is a nice popup.

') - * .openOn(map); - * ``` - */ - - -// @namespace Popup -L.Popup = L.DivOverlay.extend({ - - // @section - // @aka Popup options - options: { - // @option maxWidth: Number = 300 - // Max width of the popup, in pixels. - maxWidth: 300, - - // @option minWidth: Number = 50 - // Min width of the popup, in pixels. - minWidth: 50, - - // @option maxHeight: Number = null - // If set, creates a scrollable container of the given height - // inside a popup if its content exceeds it. - maxHeight: null, - - // @option autoPan: Boolean = true - // Set it to `false` if you don't want the map to do panning animation - // to fit the opened popup. - autoPan: true, - - // @option autoPanPaddingTopLeft: Point = null - // The margin between the popup and the top left corner of the map - // view after autopanning was performed. - autoPanPaddingTopLeft: null, - - // @option autoPanPaddingBottomRight: Point = null - // The margin between the popup and the bottom right corner of the map - // view after autopanning was performed. - autoPanPaddingBottomRight: null, - - // @option autoPanPadding: Point = Point(5, 5) - // Equivalent of setting both top left and bottom right autopan padding to the same value. - autoPanPadding: [5, 5], - - // @option keepInView: Boolean = false - // Set it to `true` if you want to prevent users from panning the popup - // off of the screen while it is open. - keepInView: false, - - // @option closeButton: Boolean = true - // Controls the presence of a close button in the popup. - closeButton: true, - - // @option autoClose: Boolean = true - // Set it to `false` if you want to override the default behavior of - // the popup closing when user clicks the map (set globally by - // the Map's [closePopupOnClick](#map-closepopuponclick) option). - autoClose: true, - - // @option className: String = '' - // A custom CSS class name to assign to the popup. - className: '' - }, - - // @namespace Popup - // @method openOn(map: Map): this - // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`. - openOn: function (map) { - map.openPopup(this); - return this; - }, - - onAdd: function (map) { - L.DivOverlay.prototype.onAdd.call(this, map); - - // @namespace Map - // @section Popup events - // @event popupopen: PopupEvent - // Fired when a popup is opened in the map - map.fire('popupopen', {popup: this}); - - if (this._source) { - // @namespace Layer - // @section Popup events - // @event popupopen: PopupEvent - // Fired when a popup bound to this layer is opened - this._source.fire('popupopen', {popup: this}, true); - // For non-path layers, we toggle the popup when clicking - // again the layer, so prevent the map to reopen it. - if (!(this._source instanceof L.Path)) { - this._source.on('preclick', L.DomEvent.stopPropagation); - } - } - }, - - onRemove: function (map) { - L.DivOverlay.prototype.onRemove.call(this, map); - - // @namespace Map - // @section Popup events - // @event popupclose: PopupEvent - // Fired when a popup in the map is closed - map.fire('popupclose', {popup: this}); - - if (this._source) { - // @namespace Layer - // @section Popup events - // @event popupclose: PopupEvent - // Fired when a popup bound to this layer is closed - this._source.fire('popupclose', {popup: this}, true); - if (!(this._source instanceof L.Path)) { - this._source.off('preclick', L.DomEvent.stopPropagation); - } - } - }, - - getEvents: function () { - var events = L.DivOverlay.prototype.getEvents.call(this); - - if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) { - events.preclick = this._close; - } - - if (this.options.keepInView) { - events.moveend = this._adjustPan; - } - - return events; - }, - - _close: function () { - if (this._map) { - this._map.closePopup(this); - } - }, - - _initLayout: function () { - var prefix = 'leaflet-popup', - container = this._container = L.DomUtil.create('div', - prefix + ' ' + (this.options.className || '') + - ' leaflet-zoom-animated'); - - if (this.options.closeButton) { - var closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container); - closeButton.href = '#close'; - closeButton.innerHTML = '×'; - - L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this); - } - - var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container); - this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper); - - L.DomEvent - .disableClickPropagation(wrapper) - .disableScrollPropagation(this._contentNode) - .on(wrapper, 'contextmenu', L.DomEvent.stopPropagation); - - this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container); - this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer); - }, - - _updateLayout: function () { - var container = this._contentNode, - style = container.style; - - style.width = ''; - style.whiteSpace = 'nowrap'; - - var width = container.offsetWidth; - width = Math.min(width, this.options.maxWidth); - width = Math.max(width, this.options.minWidth); - - style.width = (width + 1) + 'px'; - style.whiteSpace = ''; - - style.height = ''; - - var height = container.offsetHeight, - maxHeight = this.options.maxHeight, - scrolledClass = 'leaflet-popup-scrolled'; - - if (maxHeight && height > maxHeight) { - style.height = maxHeight + 'px'; - L.DomUtil.addClass(container, scrolledClass); - } else { - L.DomUtil.removeClass(container, scrolledClass); - } - - this._containerWidth = this._container.offsetWidth; - }, - - _animateZoom: function (e) { - var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center), - anchor = this._getAnchor(); - L.DomUtil.setPosition(this._container, pos.add(anchor)); - }, - - _adjustPan: function () { - if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; } - - var map = this._map, - marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0, - containerHeight = this._container.offsetHeight + marginBottom, - containerWidth = this._containerWidth, - layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom); - - layerPos._add(L.DomUtil.getPosition(this._container)); - - var containerPos = map.layerPointToContainerPoint(layerPos), - padding = L.point(this.options.autoPanPadding), - paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding), - paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding), - size = map.getSize(), - dx = 0, - dy = 0; - - if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right - dx = containerPos.x + containerWidth - size.x + paddingBR.x; - } - if (containerPos.x - dx - paddingTL.x < 0) { // left - dx = containerPos.x - paddingTL.x; - } - if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom - dy = containerPos.y + containerHeight - size.y + paddingBR.y; - } - if (containerPos.y - dy - paddingTL.y < 0) { // top - dy = containerPos.y - paddingTL.y; - } - - // @namespace Map - // @section Popup events - // @event autopanstart: Event - // Fired when the map starts autopanning when opening a popup. - if (dx || dy) { - map - .fire('autopanstart') - .panBy([dx, dy]); - } - }, - - _onCloseButtonClick: function (e) { - this._close(); - L.DomEvent.stop(e); - }, - - _getAnchor: function () { - // Where should we anchor the popup on the source layer? - return L.point(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]); - } - -}); - -// @namespace Popup -// @factory L.popup(options?: Popup options, source?: Layer) -// Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers. -L.popup = function (options, source) { - return new L.Popup(options, source); -}; - - -/* @namespace Map - * @section Interaction Options - * @option closePopupOnClick: Boolean = true - * Set it to `false` if you don't want popups to close when user clicks the map. - */ -L.Map.mergeOptions({ - closePopupOnClick: true -}); - - -// @namespace Map -// @section Methods for Layers and Controls -L.Map.include({ - // @method openPopup(popup: Popup): this - // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability). - // @alternative - // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this - // Creates a popup with the specified content and options and opens it in the given point on a map. - openPopup: function (popup, latlng, options) { - if (!(popup instanceof L.Popup)) { - popup = new L.Popup(options).setContent(popup); - } - - if (latlng) { - popup.setLatLng(latlng); - } - - if (this.hasLayer(popup)) { - return this; - } - - if (this._popup && this._popup.options.autoClose) { - this.closePopup(); - } - - this._popup = popup; - return this.addLayer(popup); - }, - - // @method closePopup(popup?: Popup): this - // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one). - closePopup: function (popup) { - if (!popup || popup === this._popup) { - popup = this._popup; - this._popup = null; - } - if (popup) { - this.removeLayer(popup); - } - return this; - } -}); - -/* - * @namespace Layer - * @section Popup methods example - * - * All layers share a set of methods convenient for binding popups to it. - * - * ```js - * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map); - * layer.openPopup(); - * layer.closePopup(); - * ``` - * - * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened. - */ - -// @section Popup methods -L.Layer.include({ - - // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this - // Binds a popup to the layer with the passed `content` and sets up the - // neccessary event listeners. If a `Function` is passed it will receive - // the layer as the first argument and should return a `String` or `HTMLElement`. - bindPopup: function (content, options) { - - if (content instanceof L.Popup) { - L.setOptions(content, options); - this._popup = content; - content._source = this; - } else { - if (!this._popup || options) { - this._popup = new L.Popup(options, this); - } - this._popup.setContent(content); - } - - if (!this._popupHandlersAdded) { - this.on({ - click: this._openPopup, - remove: this.closePopup, - move: this._movePopup - }); - this._popupHandlersAdded = true; - } - - return this; - }, - - // @method unbindPopup(): this - // Removes the popup previously bound with `bindPopup`. - unbindPopup: function () { - if (this._popup) { - this.off({ - click: this._openPopup, - remove: this.closePopup, - move: this._movePopup - }); - this._popupHandlersAdded = false; - this._popup = null; - } - return this; - }, - - // @method openPopup(latlng?: LatLng): this - // Opens the bound popup at the specificed `latlng` or at the default popup anchor if no `latlng` is passed. - openPopup: function (layer, latlng) { - if (!(layer instanceof L.Layer)) { - latlng = layer; - layer = this; - } - - if (layer instanceof L.FeatureGroup) { - for (var id in this._layers) { - layer = this._layers[id]; - break; - } - } - - if (!latlng) { - latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng(); - } - - if (this._popup && this._map) { - // set popup source to this layer - this._popup._source = layer; - - // update the popup (content, layout, ect...) - this._popup.update(); - - // open the popup on the map - this._map.openPopup(this._popup, latlng); - } - - return this; - }, - - // @method closePopup(): this - // Closes the popup bound to this layer if it is open. - closePopup: function () { - if (this._popup) { - this._popup._close(); - } - return this; - }, - - // @method togglePopup(): this - // Opens or closes the popup bound to this layer depending on its current state. - togglePopup: function (target) { - if (this._popup) { - if (this._popup._map) { - this.closePopup(); - } else { - this.openPopup(target); - } - } - return this; - }, - - // @method isPopupOpen(): boolean - // Returns `true` if the popup bound to this layer is currently open. - isPopupOpen: function () { - return (this._popup ? this._popup.isOpen() : false); - }, - - // @method setPopupContent(content: String|HTMLElement|Popup): this - // Sets the content of the popup bound to this layer. - setPopupContent: function (content) { - if (this._popup) { - this._popup.setContent(content); - } - return this; - }, - - // @method getPopup(): Popup - // Returns the popup bound to this layer. - getPopup: function () { - return this._popup; - }, - - _openPopup: function (e) { - var layer = e.layer || e.target; - - if (!this._popup) { - return; - } - - if (!this._map) { - return; - } - - // prevent map click - L.DomEvent.stop(e); - - // if this inherits from Path its a vector and we can just - // open the popup at the new location - if (layer instanceof L.Path) { - this.openPopup(e.layer || e.target, e.latlng); - return; - } - - // otherwise treat it like a marker and figure out - // if we should toggle it open/closed - if (this._map.hasLayer(this._popup) && this._popup._source === layer) { - this.closePopup(); - } else { - this.openPopup(layer, e.latlng); - } - }, - - _movePopup: function (e) { - this._popup.setLatLng(e.latlng); - } -}); - - - -/* - * @class Tooltip - * @inherits DivOverlay - * @aka L.Tooltip - * Used to display small texts on top of map layers. - * - * @example - * - * ```js - * marker.bindTooltip("my tooltip text").openTooltip(); - * ``` - * Note about tooltip offset. Leaflet takes two options in consideration - * for computing tooltip offseting: - * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip. - * Add a positive x offset to move the tooltip to the right, and a positive y offset to - * move it to the bottom. Negatives will move to the left and top. - * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You - * should adapt this value if you use a custom icon. - */ - - -// @namespace Tooltip -L.Tooltip = L.DivOverlay.extend({ - - // @section - // @aka Tooltip options - options: { - // @option pane: String = 'tooltipPane' - // `Map pane` where the tooltip will be added. - pane: 'tooltipPane', - - // @option offset: Point = Point(0, 0) - // Optional offset of the tooltip position. - offset: [0, 0], - - // @option direction: String = 'auto' - // Direction where to open the tooltip. Possible values are: `right`, `left`, - // `top`, `bottom`, `center`, `auto`. - // `auto` will dynamicaly switch between `right` and `left` according to the tooltip - // position on the map. - direction: 'auto', - - // @option permanent: Boolean = false - // Whether to open the tooltip permanently or only on mouseover. - permanent: false, - - // @option sticky: Boolean = false - // If true, the tooltip will follow the mouse instead of being fixed at the feature center. - sticky: false, - - // @option interactive: Boolean = false - // If true, the tooltip will listen to the feature events. - interactive: false, - - // @option opacity: Number = 0.9 - // Tooltip container opacity. - opacity: 0.9 - }, - - onAdd: function (map) { - L.DivOverlay.prototype.onAdd.call(this, map); - this.setOpacity(this.options.opacity); - - // @namespace Map - // @section Tooltip events - // @event tooltipopen: TooltipEvent - // Fired when a tooltip is opened in the map. - map.fire('tooltipopen', {tooltip: this}); - - if (this._source) { - // @namespace Layer - // @section Tooltip events - // @event tooltipopen: TooltipEvent - // Fired when a tooltip bound to this layer is opened. - this._source.fire('tooltipopen', {tooltip: this}, true); - } - }, - - onRemove: function (map) { - L.DivOverlay.prototype.onRemove.call(this, map); - - // @namespace Map - // @section Tooltip events - // @event tooltipclose: TooltipEvent - // Fired when a tooltip in the map is closed. - map.fire('tooltipclose', {tooltip: this}); - - if (this._source) { - // @namespace Layer - // @section Tooltip events - // @event tooltipclose: TooltipEvent - // Fired when a tooltip bound to this layer is closed. - this._source.fire('tooltipclose', {tooltip: this}, true); - } - }, - - getEvents: function () { - var events = L.DivOverlay.prototype.getEvents.call(this); - - if (L.Browser.touch && !this.options.permanent) { - events.preclick = this._close; - } - - return events; - }, - - _close: function () { - if (this._map) { - this._map.closeTooltip(this); - } - }, - - _initLayout: function () { - var prefix = 'leaflet-tooltip', - className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); - - this._contentNode = this._container = L.DomUtil.create('div', className); - }, - - _updateLayout: function () {}, - - _adjustPan: function () {}, - - _setPosition: function (pos) { - var map = this._map, - container = this._container, - centerPoint = map.latLngToContainerPoint(map.getCenter()), - tooltipPoint = map.layerPointToContainerPoint(pos), - direction = this.options.direction, - tooltipWidth = container.offsetWidth, - tooltipHeight = container.offsetHeight, - offset = L.point(this.options.offset), - anchor = this._getAnchor(); - - if (direction === 'top') { - pos = pos.add(L.point(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true)); - } else if (direction === 'bottom') { - pos = pos.subtract(L.point(tooltipWidth / 2 - offset.x, -offset.y, true)); - } else if (direction === 'center') { - pos = pos.subtract(L.point(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true)); - } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) { - direction = 'right'; - pos = pos.add(L.point(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true)); - } else { - direction = 'left'; - pos = pos.subtract(L.point(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true)); - } - - L.DomUtil.removeClass(container, 'leaflet-tooltip-right'); - L.DomUtil.removeClass(container, 'leaflet-tooltip-left'); - L.DomUtil.removeClass(container, 'leaflet-tooltip-top'); - L.DomUtil.removeClass(container, 'leaflet-tooltip-bottom'); - L.DomUtil.addClass(container, 'leaflet-tooltip-' + direction); - L.DomUtil.setPosition(container, pos); - }, - - _updatePosition: function () { - var pos = this._map.latLngToLayerPoint(this._latlng); - this._setPosition(pos); - }, - - setOpacity: function (opacity) { - this.options.opacity = opacity; - - if (this._container) { - L.DomUtil.setOpacity(this._container, opacity); - } - }, - - _animateZoom: function (e) { - var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center); - this._setPosition(pos); - }, - - _getAnchor: function () { - // Where should we anchor the tooltip on the source layer? - return L.point(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]); - } - -}); - -// @namespace Tooltip -// @factory L.tooltip(options?: Tooltip options, source?: Layer) -// Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers. -L.tooltip = function (options, source) { - return new L.Tooltip(options, source); -}; - -// @namespace Map -// @section Methods for Layers and Controls -L.Map.include({ - - // @method openTooltip(tooltip: Tooltip): this - // Opens the specified tooltip. - // @alternative - // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this - // Creates a tooltip with the specified content and options and open it. - openTooltip: function (tooltip, latlng, options) { - if (!(tooltip instanceof L.Tooltip)) { - tooltip = new L.Tooltip(options).setContent(tooltip); - } - - if (latlng) { - tooltip.setLatLng(latlng); - } - - if (this.hasLayer(tooltip)) { - return this; - } - - return this.addLayer(tooltip); - }, - - // @method closeTooltip(tooltip?: Tooltip): this - // Closes the tooltip given as parameter. - closeTooltip: function (tooltip) { - if (tooltip) { - this.removeLayer(tooltip); - } - return this; - } - -}); - -/* - * @namespace Layer - * @section Tooltip methods example - * - * All layers share a set of methods convenient for binding tooltips to it. - * - * ```js - * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map); - * layer.openTooltip(); - * layer.closeTooltip(); - * ``` - */ - -// @section Tooltip methods -L.Layer.include({ - - // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this - // Binds a tooltip to the layer with the passed `content` and sets up the - // neccessary event listeners. If a `Function` is passed it will receive - // the layer as the first argument and should return a `String` or `HTMLElement`. - bindTooltip: function (content, options) { - - if (content instanceof L.Tooltip) { - L.setOptions(content, options); - this._tooltip = content; - content._source = this; - } else { - if (!this._tooltip || options) { - this._tooltip = L.tooltip(options, this); - } - this._tooltip.setContent(content); - - } - - this._initTooltipInteractions(); - - if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) { - this.openTooltip(); - } - - return this; - }, - - // @method unbindTooltip(): this - // Removes the tooltip previously bound with `bindTooltip`. - unbindTooltip: function () { - if (this._tooltip) { - this._initTooltipInteractions(true); - this.closeTooltip(); - this._tooltip = null; - } - return this; - }, - - _initTooltipInteractions: function (remove) { - if (!remove && this._tooltipHandlersAdded) { return; } - var onOff = remove ? 'off' : 'on', - events = { - remove: this.closeTooltip, - move: this._moveTooltip - }; - if (!this._tooltip.options.permanent) { - events.mouseover = this._openTooltip; - events.mouseout = this.closeTooltip; - if (this._tooltip.options.sticky) { - events.mousemove = this._moveTooltip; - } - if (L.Browser.touch) { - events.click = this._openTooltip; - } - } else { - events.add = this._openTooltip; - } - this[onOff](events); - this._tooltipHandlersAdded = !remove; - }, - - // @method openTooltip(latlng?: LatLng): this - // Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed. - openTooltip: function (layer, latlng) { - if (!(layer instanceof L.Layer)) { - latlng = layer; - layer = this; - } - - if (layer instanceof L.FeatureGroup) { - for (var id in this._layers) { - layer = this._layers[id]; - break; - } - } - - if (!latlng) { - latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng(); - } - - if (this._tooltip && this._map) { - - // set tooltip source to this layer - this._tooltip._source = layer; - - // update the tooltip (content, layout, ect...) - this._tooltip.update(); - - // open the tooltip on the map - this._map.openTooltip(this._tooltip, latlng); - - // Tooltip container may not be defined if not permanent and never - // opened. - if (this._tooltip.options.interactive && this._tooltip._container) { - L.DomUtil.addClass(this._tooltip._container, 'leaflet-clickable'); - this.addInteractiveTarget(this._tooltip._container); - } - } - - return this; - }, - - // @method closeTooltip(): this - // Closes the tooltip bound to this layer if it is open. - closeTooltip: function () { - if (this._tooltip) { - this._tooltip._close(); - if (this._tooltip.options.interactive && this._tooltip._container) { - L.DomUtil.removeClass(this._tooltip._container, 'leaflet-clickable'); - this.removeInteractiveTarget(this._tooltip._container); - } - } - return this; - }, - - // @method toggleTooltip(): this - // Opens or closes the tooltip bound to this layer depending on its current state. - toggleTooltip: function (target) { - if (this._tooltip) { - if (this._tooltip._map) { - this.closeTooltip(); - } else { - this.openTooltip(target); - } - } - return this; - }, - - // @method isTooltipOpen(): boolean - // Returns `true` if the tooltip bound to this layer is currently open. - isTooltipOpen: function () { - return this._tooltip.isOpen(); - }, - - // @method setTooltipContent(content: String|HTMLElement|Tooltip): this - // Sets the content of the tooltip bound to this layer. - setTooltipContent: function (content) { - if (this._tooltip) { - this._tooltip.setContent(content); - } - return this; - }, - - // @method getTooltip(): Tooltip - // Returns the tooltip bound to this layer. - getTooltip: function () { - return this._tooltip; - }, - - _openTooltip: function (e) { - var layer = e.layer || e.target; - - if (!this._tooltip || !this._map) { - return; - } - this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined); - }, - - _moveTooltip: function (e) { - var latlng = e.latlng, containerPoint, layerPoint; - if (this._tooltip.options.sticky && e.originalEvent) { - containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent); - layerPoint = this._map.containerPointToLayerPoint(containerPoint); - latlng = this._map.layerPointToLatLng(layerPoint); - } - this._tooltip.setLatLng(latlng); - } -}); - - + initialize: function (options) { + setOptions(this, options); + }, + + /* @section + * Classes extending L.Control will inherit the following methods: + * + * @method getPosition: string + * Returns the position of the control. + */ + getPosition: function () { + return this.options.position; + }, + + // @method setPosition(position: string): this + // Sets the position of the control. + setPosition: function (position) { + var map = this._map; + + if (map) { + map.removeControl(this); + } + + this.options.position = position; + + if (map) { + map.addControl(this); + } + + return this; + }, + + // @method getContainer: HTMLElement + // Returns the HTMLElement that contains the control. + getContainer: function () { + return this._container; + }, + + // @method addTo(map: Map): this + // Adds the control to the given map. + addTo: function (map) { + this.remove(); + this._map = map; + + var container = this._container = this.onAdd(map), + pos = this.getPosition(), + corner = map._controlCorners[pos]; + + addClass(container, 'leaflet-control'); + + if (pos.indexOf('bottom') !== -1) { + corner.insertBefore(container, corner.firstChild); + } else { + corner.appendChild(container); + } + + return this; + }, + + // @method remove: this + // Removes the control from the map it is currently active on. + remove: function () { + if (!this._map) { + return this; + } + + remove(this._container); + + if (this.onRemove) { + this.onRemove(this._map); + } + + this._map = null; + + return this; + }, + + _refocusOnMap: function (e) { + // if map exists and event is not a keyboard event + if (this._map && e && e.screenX > 0 && e.screenY > 0) { + this._map.getContainer().focus(); + } + } +}); + +var control = function (options) { + return new Control(options); +}; + +/* @section Extension methods + * @uninheritable + * + * Every control should extend from `L.Control` and (re-)implement the following methods. + * + * @method onAdd(map: Map): HTMLElement + * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo). + * + * @method onRemove(map: Map) + * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove). + */ + +/* @namespace Map + * @section Methods for Layers and Controls + */ +Map.include({ + // @method addControl(control: Control): this + // Adds the given control to the map + addControl: function (control) { + control.addTo(this); + return this; + }, + + // @method removeControl(control: Control): this + // Removes the given control from the map + removeControl: function (control) { + control.remove(); + return this; + }, + + _initControlPos: function () { + var corners = this._controlCorners = {}, + l = 'leaflet-', + container = this._controlContainer = + create$1('div', l + 'control-container', this._container); + + function createCorner(vSide, hSide) { + var className = l + vSide + ' ' + l + hSide; + + corners[vSide + hSide] = create$1('div', className, container); + } + + createCorner('top', 'left'); + createCorner('top', 'right'); + createCorner('bottom', 'left'); + createCorner('bottom', 'right'); + }, + + _clearControlPos: function () { + for (var i in this._controlCorners) { + remove(this._controlCorners[i]); + } + remove(this._controlContainer); + delete this._controlCorners; + delete this._controlContainer; + } +}); /* - * @class LayerGroup - * @aka L.LayerGroup - * @inherits Layer + * @class Control.Layers + * @aka L.Control.Layers + * @inherits Control * - * Used to group several layers and handle them as one. If you add it to the map, - * any layers added or removed from the group will be added/removed on the map as - * well. Extends `Layer`. + * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`. * * @example * * ```js - * L.layerGroup([marker1, marker2]) - * .addLayer(polyline) - * .addTo(map); + * var baseLayers = { + * "Mapbox": mapbox, + * "OpenStreetMap": osm + * }; + * + * var overlays = { + * "Marker": marker, + * "Roads": roadsLayer + * }; + * + * L.control.layers(baseLayers, overlays).addTo(map); + * ``` + * + * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values: + * + * ```js + * { + * "": layer1, + * "": layer2 + * } + * ``` + * + * The layer names can contain HTML, which allows you to add additional styling to the items: + * + * ```js + * {" My Layer": myLayer} * ``` */ -L.LayerGroup = L.Layer.extend({ +var Layers = Control.extend({ + // @section + // @aka Control.Layers options + options: { + // @option collapsed: Boolean = true + // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch. + collapsed: true, + position: 'topright', + + // @option autoZIndex: Boolean = true + // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off. + autoZIndex: true, + + // @option hideSingleBase: Boolean = false + // If `true`, the base layers in the control will be hidden when there is only one. + hideSingleBase: false, + + // @option sortLayers: Boolean = false + // Whether to sort the layers. When `false`, layers will keep the order + // in which they were added to the control. + sortLayers: false, + + // @option sortFunction: Function = * + // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) + // that will be used for sorting the layers, when `sortLayers` is `true`. + // The function receives both the `L.Layer` instances and their names, as in + // `sortFunction(layerA, layerB, nameA, nameB)`. + // By default, it sorts layers alphabetically by their name. + sortFunction: function (layerA, layerB, nameA, nameB) { + return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0); + } + }, + + initialize: function (baseLayers, overlays, options) { + setOptions(this, options); + + this._layerControlInputs = []; + this._layers = []; + this._lastZIndex = 0; + this._handlingClick = false; + + for (var i in baseLayers) { + this._addLayer(baseLayers[i], i); + } + + for (i in overlays) { + this._addLayer(overlays[i], i, true); + } + }, + + onAdd: function (map) { + this._initLayout(); + this._update(); + + this._map = map; + map.on('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.on('add remove', this._onLayerChange, this); + } + + return this._container; + }, + + addTo: function (map) { + Control.prototype.addTo.call(this, map); + // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height. + return this._expandIfNotCollapsed(); + }, + + onRemove: function () { + this._map.off('zoomend', this._checkDisabledLayers, this); + + for (var i = 0; i < this._layers.length; i++) { + this._layers[i].layer.off('add remove', this._onLayerChange, this); + } + }, + + // @method addBaseLayer(layer: Layer, name: String): this + // Adds a base layer (radio button entry) with the given name to the control. + addBaseLayer: function (layer, name) { + this._addLayer(layer, name); + return (this._map) ? this._update() : this; + }, + + // @method addOverlay(layer: Layer, name: String): this + // Adds an overlay (checkbox entry) with the given name to the control. + addOverlay: function (layer, name) { + this._addLayer(layer, name, true); + return (this._map) ? this._update() : this; + }, + + // @method removeLayer(layer: Layer): this + // Remove the given layer from the control. + removeLayer: function (layer) { + layer.off('add remove', this._onLayerChange, this); + + var obj = this._getLayer(stamp(layer)); + if (obj) { + this._layers.splice(this._layers.indexOf(obj), 1); + } + return (this._map) ? this._update() : this; + }, + + // @method expand(): this + // Expand the control container if collapsed. + expand: function () { + addClass(this._container, 'leaflet-control-layers-expanded'); + this._form.style.height = null; + var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); + if (acceptableHeight < this._form.clientHeight) { + addClass(this._form, 'leaflet-control-layers-scrollbar'); + this._form.style.height = acceptableHeight + 'px'; + } else { + removeClass(this._form, 'leaflet-control-layers-scrollbar'); + } + this._checkDisabledLayers(); + return this; + }, + + // @method collapse(): this + // Collapse the control container if expanded. + collapse: function () { + removeClass(this._container, 'leaflet-control-layers-expanded'); + return this; + }, + + _initLayout: function () { + var className = 'leaflet-control-layers', + container = this._container = create$1('div', className), + collapsed = this.options.collapsed; + + // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released + container.setAttribute('aria-haspopup', true); - initialize: function (layers) { - this._layers = {}; + disableClickPropagation(container); + disableScrollPropagation(container); - var i, len; + var form = this._form = create$1('form', className + '-list'); - if (layers) { - for (i = 0, len = layers.length; i < len; i++) { - this.addLayer(layers[i]); + if (collapsed) { + this._map.on('click', this.collapse, this); + + if (!android) { + on(container, { + mouseenter: this.expand, + mouseleave: this.collapse + }, this); } } + + var link = this._layersLink = create$1('a', className + '-toggle', container); + link.href = '#'; + link.title = 'Layers'; + + if (touch) { + on(link, 'click', stop); + on(link, 'click', this.expand, this); + } else { + on(link, 'focus', this.expand, this); + } + + // work around for Firefox Android issue https://github.com/Leaflet/Leaflet/issues/2033 + on(form, 'click', function () { + setTimeout(bind(this._onInputClick, this), 0); + }, this); + + // TODO keyboard accessibility + + if (!collapsed) { + this.expand(); + } + + this._baseLayersList = create$1('div', className + '-base', form); + this._separator = create$1('div', className + '-separator', form); + this._overlaysList = create$1('div', className + '-overlays', form); + + container.appendChild(form); }, - // @method addLayer(layer: Layer): this - // Adds the given layer to the group. - addLayer: function (layer) { - var id = this.getLayerId(layer); + _getLayer: function (id) { + for (var i = 0; i < this._layers.length; i++) { - this._layers[id] = layer; + if (this._layers[i] && stamp(this._layers[i].layer) === id) { + return this._layers[i]; + } + } + }, + _addLayer: function (layer, name, overlay) { if (this._map) { - this._map.addLayer(layer); + layer.on('add remove', this._onLayerChange, this); } - return this; + this._layers.push({ + layer: layer, + name: name, + overlay: overlay + }); + + if (this.options.sortLayers) { + this._layers.sort(L.bind(function (a, b) { + return this.options.sortFunction(a.layer, b.layer, a.name, b.name); + }, this)); + } + + if (this.options.autoZIndex && layer.setZIndex) { + this._lastZIndex++; + layer.setZIndex(this._lastZIndex); + } + + this._expandIfNotCollapsed(); }, - // @method removeLayer(layer: Layer): this - // Removes the given layer from the group. - // @alternative - // @method removeLayer(id: Number): this - // Removes the layer with the given internal ID from the group. - removeLayer: function (layer) { - var id = layer in this._layers ? layer : this.getLayerId(layer); + _update: function () { + if (!this._container) { return this; } - if (this._map && this._layers[id]) { - this._map.removeLayer(this._layers[id]); + empty(this._baseLayersList); + empty(this._overlaysList); + + this._layerControlInputs = []; + var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0; + + for (i = 0; i < this._layers.length; i++) { + obj = this._layers[i]; + this._addItem(obj); + overlaysPresent = overlaysPresent || obj.overlay; + baseLayersPresent = baseLayersPresent || !obj.overlay; + baseLayersCount += !obj.overlay ? 1 : 0; } - delete this._layers[id]; + // Hide base layers section if there's only one layer. + if (this.options.hideSingleBase) { + baseLayersPresent = baseLayersPresent && baseLayersCount > 1; + this._baseLayersList.style.display = baseLayersPresent ? '' : 'none'; + } + + this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; return this; }, - // @method hasLayer(layer: Layer): Boolean - // Returns `true` if the given layer is currently added to the group. - hasLayer: function (layer) { - return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers); - }, + _onLayerChange: function (e) { + if (!this._handlingClick) { + this._update(); + } - // @method clearLayers(): this - // Removes all the layers from the group. - clearLayers: function () { - for (var i in this._layers) { - this.removeLayer(this._layers[i]); + var obj = this._getLayer(stamp(e.target)); + + // @namespace Map + // @section Layer events + // @event baselayerchange: LayersControlEvent + // Fired when the base layer is changed through the [layer control](#control-layers). + // @event overlayadd: LayersControlEvent + // Fired when an overlay is selected through the [layer control](#control-layers). + // @event overlayremove: LayersControlEvent + // Fired when an overlay is deselected through the [layer control](#control-layers). + // @namespace Control.Layers + var type = obj.overlay ? + (e.type === 'add' ? 'overlayadd' : 'overlayremove') : + (e.type === 'add' ? 'baselayerchange' : null); + + if (type) { + this._map.fire(type, obj); } - return this; }, - // @method invoke(methodName: String, …): this - // Calls `methodName` on every layer contained in this group, passing any - // additional parameters. Has no effect if the layers contained do not - // implement `methodName`. - invoke: function (methodName) { - var args = Array.prototype.slice.call(arguments, 1), - i, layer; + // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) + _createRadioElement: function (name, checked) { - for (i in this._layers) { - layer = this._layers[i]; + var radioHtml = ''; - if (layer[methodName]) { - layer[methodName].apply(layer, args); - } - } + var radioFragment = document.createElement('div'); + radioFragment.innerHTML = radioHtml; - return this; + return radioFragment.firstChild; }, - onAdd: function (map) { - for (var i in this._layers) { - map.addLayer(this._layers[i]); + _addItem: function (obj) { + var label = document.createElement('label'), + checked = this._map.hasLayer(obj.layer), + input; + + if (obj.overlay) { + input = document.createElement('input'); + input.type = 'checkbox'; + input.className = 'leaflet-control-layers-selector'; + input.defaultChecked = checked; + } else { + input = this._createRadioElement('leaflet-base-layers', checked); } + + this._layerControlInputs.push(input); + input.layerId = stamp(obj.layer); + + on(input, 'click', this._onInputClick, this); + + var name = document.createElement('span'); + name.innerHTML = ' ' + obj.name; + + // Helps from preventing layer control flicker when checkboxes are disabled + // https://github.com/Leaflet/Leaflet/issues/2771 + var holder = document.createElement('div'); + + label.appendChild(holder); + holder.appendChild(input); + holder.appendChild(name); + + var container = obj.overlay ? this._overlaysList : this._baseLayersList; + container.appendChild(label); + + this._checkDisabledLayers(); + return label; }, - onRemove: function (map) { - for (var i in this._layers) { - map.removeLayer(this._layers[i]); + _onInputClick: function () { + var inputs = this._layerControlInputs, + input, layer, hasLayer; + var addedLayers = [], + removedLayers = []; + + this._handlingClick = true; + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + hasLayer = this._map.hasLayer(layer); + + if (input.checked && !hasLayer) { + addedLayers.push(layer); + + } else if (!input.checked && hasLayer) { + removedLayers.push(layer); + } } - }, - // @method eachLayer(fn: Function, context?: Object): this - // Iterates over the layers of the group, optionally specifying context of the iterator function. - // ```js - // group.eachLayer(function (layer) { - // layer.bindPopup('Hello'); - // }); - // ``` - eachLayer: function (method, context) { - for (var i in this._layers) { - method.call(context, this._layers[i]); + // Bugfix issue 2318: Should remove all old layers before readding new ones + for (i = 0; i < removedLayers.length; i++) { + this._map.removeLayer(removedLayers[i]); + } + for (i = 0; i < addedLayers.length; i++) { + this._map.addLayer(addedLayers[i]); } - return this; - }, - // @method getLayer(id: Number): Layer - // Returns the layer with the given internal ID. - getLayer: function (id) { - return this._layers[id]; + this._handlingClick = false; + + this._refocusOnMap(); }, - // @method getLayers(): Layer[] - // Returns an array of all the layers added to the group. - getLayers: function () { - var layers = []; + _checkDisabledLayers: function () { + var inputs = this._layerControlInputs, + input, + layer, + zoom = this._map.getZoom(); + + for (var i = inputs.length - 1; i >= 0; i--) { + input = inputs[i]; + layer = this._getLayer(input.layerId).layer; + input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) || + (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom); - for (var i in this._layers) { - layers.push(this._layers[i]); } - return layers; }, - // @method setZIndex(zIndex: Number): this - // Calls `setZIndex` on every layer contained in this group, passing the z-index. - setZIndex: function (zIndex) { - return this.invoke('setZIndex', zIndex); + _expandIfNotCollapsed: function () { + if (this._map && !this.options.collapsed) { + this.expand(); + } + return this; }, - // @method getLayerId(layer: Layer): Number - // Returns the internal ID for a layer - getLayerId: function (layer) { - return L.stamp(layer); + _expand: function () { + // Backward compatibility, remove me in 1.1. + return this.expand(); + }, + + _collapse: function () { + // Backward compatibility, remove me in 1.1. + return this.collapse(); } + }); -// @factory L.layerGroup(layers: Layer[]) -// Create a layer group, optionally given an initial set of layers. -L.layerGroup = function (layers) { - return new L.LayerGroup(layers); -}; - - +// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options) +// Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation. +var layers = function (baseLayers, overlays, options) { + return new Layers(baseLayers, overlays, options); +}; /* - * @class FeatureGroup - * @aka L.FeatureGroup - * @inherits LayerGroup - * - * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers: - * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip)) - * * Events are propagated to the `FeatureGroup`, so if the group has an event - * handler, it will handle events from any of the layers. This includes mouse events - * and custom events. - * * Has `layeradd` and `layerremove` events - * - * @example + * @class Control.Zoom + * @aka L.Control.Zoom + * @inherits Control * - * ```js - * L.featureGroup([marker1, marker2, polyline]) - * .bindPopup('Hello world!') - * .on('click', function() { alert('Clicked on a member of the group!'); }) - * .addTo(map); - * ``` + * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`. */ -L.FeatureGroup = L.LayerGroup.extend({ +var Zoom = Control.extend({ + // @section + // @aka Control.Zoom options + options: { + position: 'topleft', - addLayer: function (layer) { - if (this.hasLayer(layer)) { - return this; - } + // @option zoomInText: String = '+' + // The text set on the 'zoom in' button. + zoomInText: '+', - layer.addEventParent(this); + // @option zoomInTitle: String = 'Zoom in' + // The title set on the 'zoom in' button. + zoomInTitle: 'Zoom in', - L.LayerGroup.prototype.addLayer.call(this, layer); + // @option zoomOutText: String = '−' + // The text set on the 'zoom out' button. + zoomOutText: '−', - // @event layeradd: LayerEvent - // Fired when a layer is added to this `FeatureGroup` - return this.fire('layeradd', {layer: layer}); + // @option zoomOutTitle: String = 'Zoom out' + // The title set on the 'zoom out' button. + zoomOutTitle: 'Zoom out' }, - removeLayer: function (layer) { - if (!this.hasLayer(layer)) { - return this; - } - if (layer in this._layers) { - layer = this._layers[layer]; - } + onAdd: function (map) { + var zoomName = 'leaflet-control-zoom', + container = create$1('div', zoomName + ' leaflet-bar'), + options = this.options; - layer.removeEventParent(this); + this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, + zoomName + '-in', container, this._zoomIn); + this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, + zoomName + '-out', container, this._zoomOut); - L.LayerGroup.prototype.removeLayer.call(this, layer); + this._updateDisabled(); + map.on('zoomend zoomlevelschange', this._updateDisabled, this); - // @event layerremove: LayerEvent - // Fired when a layer is removed from this `FeatureGroup` - return this.fire('layerremove', {layer: layer}); + return container; }, - // @method setStyle(style: Path options): this - // Sets the given path options to each layer of the group that has a `setStyle` method. - setStyle: function (style) { - return this.invoke('setStyle', style); + onRemove: function (map) { + map.off('zoomend zoomlevelschange', this._updateDisabled, this); }, - // @method bringToFront(): this - // Brings the layer group to the top of all other layers - bringToFront: function () { - return this.invoke('bringToFront'); + disable: function () { + this._disabled = true; + this._updateDisabled(); + return this; }, - // @method bringToBack(): this - // Brings the layer group to the top of all other layers - bringToBack: function () { - return this.invoke('bringToBack'); + enable: function () { + this._disabled = false; + this._updateDisabled(); + return this; }, - // @method getBounds(): LatLngBounds - // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). - getBounds: function () { - var bounds = new L.LatLngBounds(); + _zoomIn: function (e) { + if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) { + this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, - for (var id in this._layers) { - var layer = this._layers[id]; - bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); + _zoomOut: function (e) { + if (!this._disabled && this._map._zoom > this._map.getMinZoom()) { + this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); + } + }, + + _createButton: function (html, title, className, container, fn) { + var link = create$1('a', className, container); + link.innerHTML = html; + link.href = '#'; + link.title = title; + + /* + * Will force screen readers like VoiceOver to read this as "Zoom in - button" + */ + link.setAttribute('role', 'button'); + link.setAttribute('aria-label', title); + + disableClickPropagation(link); + on(link, 'click', stop); + on(link, 'click', fn, this); + on(link, 'click', this._refocusOnMap, this); + + return link; + }, + + _updateDisabled: function () { + var map = this._map, + className = 'leaflet-disabled'; + + removeClass(this._zoomInButton, className); + removeClass(this._zoomOutButton, className); + + if (this._disabled || map._zoom === map.getMinZoom()) { + addClass(this._zoomOutButton, className); + } + if (this._disabled || map._zoom === map.getMaxZoom()) { + addClass(this._zoomInButton, className); } - return bounds; } }); -// @factory L.featureGroup(layers: Layer[]) -// Create a feature group, optionally given an initial set of layers. -L.featureGroup = function (layers) { - return new L.FeatureGroup(layers); -}; - - +// @namespace Map +// @section Control options +// @option zoomControl: Boolean = true +// Whether a [zoom control](#control-zoom) is added to the map by default. +Map.mergeOptions({ + zoomControl: true +}); + +Map.addInitHook(function () { + if (this.options.zoomControl) { + this.zoomControl = new Zoom(); + this.addControl(this.zoomControl); + } +}); + +// @namespace Control.Zoom +// @factory L.control.zoom(options: Control.Zoom options) +// Creates a zoom control +var zoom = function (options) { + return new Zoom(options); +}; /* - * @class Renderer - * @inherits Layer - * @aka L.Renderer - * - * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the - * DOM container of the renderer, its bounds, and its zoom animation. + * @class Control.Scale + * @aka L.Control.Scale + * @inherits Control * - * A `Renderer` works as an implicit layer group for all `Path`s - the renderer - * itself can be added or removed to the map. All paths use a renderer, which can - * be implicit (the map will decide the type of renderer and use it automatically) - * or explicit (using the [`renderer`](#path-renderer) option of the path). + * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`. * - * Do not use this class directly, use `SVG` and `Canvas` instead. + * @example * - * @event update: Event - * Fired when the renderer updates its bounds, center and zoom, for example when - * its map has moved + * ```js + * L.control.scale().addTo(map); + * ``` */ -L.Renderer = L.Layer.extend({ - +var Scale = Control.extend({ // @section - // @aka Renderer options + // @aka Control.Scale options options: { - // @option padding: Number = 0.1 - // How much to extend the clip area around the map view (relative to its size) - // e.g. 0.1 would be 10% of map view in each direction - padding: 0.1 - }, - - initialize: function (options) { - L.setOptions(this, options); - L.stamp(this); - this._layers = this._layers || {}; - }, - - onAdd: function () { - if (!this._container) { - this._initContainer(); // defined by renderer implementations - - if (this._zoomAnimated) { - L.DomUtil.addClass(this._container, 'leaflet-zoom-animated'); - } - } - - this.getPane().appendChild(this._container); - this._update(); - this.on('update', this._updatePaths, this); - }, + position: 'bottomleft', - onRemove: function () { - L.DomUtil.remove(this._container); - this.off('update', this._updatePaths, this); - }, + // @option maxWidth: Number = 100 + // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500). + maxWidth: 100, - getEvents: function () { - var events = { - viewreset: this._reset, - zoom: this._onZoom, - moveend: this._update, - zoomend: this._onZoomEnd - }; - if (this._zoomAnimated) { - events.zoomanim = this._onAnimZoom; - } - return events; - }, + // @option metric: Boolean = True + // Whether to show the metric scale line (m/km). + metric: true, - _onAnimZoom: function (ev) { - this._updateTransform(ev.center, ev.zoom); - }, + // @option imperial: Boolean = True + // Whether to show the imperial scale line (mi/ft). + imperial: true - _onZoom: function () { - this._updateTransform(this._map.getCenter(), this._map.getZoom()); + // @option updateWhenIdle: Boolean = false + // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)). }, - _updateTransform: function (center, zoom) { - var scale = this._map.getZoomScale(zoom, this._zoom), - position = L.DomUtil.getPosition(this._container), - viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding), - currentCenterPoint = this._map.project(this._center, zoom), - destCenterPoint = this._map.project(center, zoom), - centerOffset = destCenterPoint.subtract(currentCenterPoint), - - topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset); - - if (L.Browser.any3d) { - L.DomUtil.setTransform(this._container, topLeftOffset, scale); - } else { - L.DomUtil.setPosition(this._container, topLeftOffset); - } - }, + onAdd: function (map) { + var className = 'leaflet-control-scale', + container = create$1('div', className), + options = this.options; - _reset: function () { - this._update(); - this._updateTransform(this._center, this._zoom); + this._addScales(options, className + '-line', container); - for (var id in this._layers) { - this._layers[id]._reset(); - } + map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + map.whenReady(this._update, this); + + return container; }, - _onZoomEnd: function () { - for (var id in this._layers) { - this._layers[id]._project(); - } + onRemove: function (map) { + map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); }, - _updatePaths: function () { - for (var id in this._layers) { - this._layers[id]._update(); + _addScales: function (options, className, container) { + if (options.metric) { + this._mScale = create$1('div', className, container); + } + if (options.imperial) { + this._iScale = create$1('div', className, container); } }, _update: function () { - // Update pixel bounds of renderer container (for positioning/sizing/clipping later) - // Subclasses are responsible of firing the 'update' event. - var p = this.options.padding, - size = this._map.getSize(), - min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round(); - - this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round()); - - this._center = this._map.getCenter(); - this._zoom = this._map.getZoom(); - } -}); - - -L.Map.include({ - // @namespace Map; @method getRenderer(layer: Path): Renderer - // Returns the instance of `Renderer` that should be used to render the given - // `Path`. It will ensure that the `renderer` options of the map and paths - // are respected, and that the renderers do exist on the map. - getRenderer: function (layer) { - // @namespace Path; @option renderer: Renderer - // Use this specific instance of `Renderer` for this path. Takes - // precedence over the map's [default renderer](#map-renderer). - var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer; + var map = this._map, + y = map.getSize().y / 2; - if (!renderer) { - // @namespace Map; @option preferCanvas: Boolean = false - // Whether `Path`s should be rendered on a `Canvas` renderer. - // By default, all `Path`s are rendered in a `SVG` renderer. - renderer = this._renderer = (this.options.preferCanvas && L.canvas()) || L.svg(); - } + var maxMeters = map.distance( + map.containerPointToLatLng([0, y]), + map.containerPointToLatLng([this.options.maxWidth, y])); - if (!this.hasLayer(renderer)) { - this.addLayer(renderer); - } - return renderer; + this._updateScales(maxMeters); }, - _getPaneRenderer: function (name) { - if (name === 'overlayPane' || name === undefined) { - return false; + _updateScales: function (maxMeters) { + if (this.options.metric && maxMeters) { + this._updateMetric(maxMeters); } - - var renderer = this._paneRenderers[name]; - if (renderer === undefined) { - renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name})); - this._paneRenderers[name] = renderer; + if (this.options.imperial && maxMeters) { + this._updateImperial(maxMeters); } - return renderer; - } -}); - - - -/* - * @class Path - * @aka L.Path - * @inherits Interactive layer - * - * An abstract class that contains options and constants shared between vector - * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`. - */ + }, -L.Path = L.Layer.extend({ + _updateMetric: function (maxMeters) { + var meters = this._getRoundNum(maxMeters), + label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; - // @section - // @aka Path options - options: { - // @option stroke: Boolean = true - // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. - stroke: true, + this._updateScale(this._mScale, label, meters / maxMeters); + }, - // @option color: String = '#3388ff' - // Stroke color - color: '#3388ff', + _updateImperial: function (maxMeters) { + var maxFeet = maxMeters * 3.2808399, + maxMiles, miles, feet; - // @option weight: Number = 3 - // Stroke width in pixels - weight: 3, + if (maxFeet > 5280) { + maxMiles = maxFeet / 5280; + miles = this._getRoundNum(maxMiles); + this._updateScale(this._iScale, miles + ' mi', miles / maxMiles); - // @option opacity: Number = 1.0 - // Stroke opacity - opacity: 1, + } else { + feet = this._getRoundNum(maxFeet); + this._updateScale(this._iScale, feet + ' ft', feet / maxFeet); + } + }, - // @option lineCap: String= 'round' - // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. - lineCap: 'round', + _updateScale: function (scale, text, ratio) { + scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px'; + scale.innerHTML = text; + }, - // @option lineJoin: String = 'round' - // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. - lineJoin: 'round', + _getRoundNum: function (num) { + var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), + d = num / pow10; - // @option dashArray: String = null - // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). - dashArray: null, + d = d >= 10 ? 10 : + d >= 5 ? 5 : + d >= 3 ? 3 : + d >= 2 ? 2 : 1; - // @option dashOffset: String = null - // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). - dashOffset: null, + return pow10 * d; + } +}); - // @option fill: Boolean = depends - // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. - fill: false, - // @option fillColor: String = * - // Fill color. Defaults to the value of the [`color`](#path-color) option - fillColor: null, +// @factory L.control.scale(options?: Control.Scale options) +// Creates an scale control with the given options. +var scale = function (options) { + return new Scale(options); +}; - // @option fillOpacity: Number = 0.2 - // Fill opacity. - fillOpacity: 0.2, +/* + * @class Control.Attribution + * @aka L.Control.Attribution + * @inherits Control + * + * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control. + */ + +var Attribution = Control.extend({ + // @section + // @aka Control.Attribution options + options: { + position: 'bottomright', + + // @option prefix: String = 'Leaflet' + // The HTML text shown before the attributions. Pass `false` to disable. + prefix: 'Leaflet' + }, + + initialize: function (options) { + setOptions(this, options); + + this._attributions = {}; + }, + + onAdd: function (map) { + map.attributionControl = this; + this._container = create$1('div', 'leaflet-control-attribution'); + disableClickPropagation(this._container); + + // TODO ugly, refactor + for (var i in map._layers) { + if (map._layers[i].getAttribution) { + this.addAttribution(map._layers[i].getAttribution()); + } + } + + this._update(); + + return this._container; + }, + + // @method setPrefix(prefix: String): this + // Sets the text before the attributions. + setPrefix: function (prefix) { + this.options.prefix = prefix; + this._update(); + return this; + }, + + // @method addAttribution(text: String): this + // Adds an attribution text (e.g. `'Vector data © Mapbox'`). + addAttribution: function (text) { + if (!text) { return this; } + + if (!this._attributions[text]) { + this._attributions[text] = 0; + } + this._attributions[text]++; + + this._update(); + + return this; + }, + + // @method removeAttribution(text: String): this + // Removes an attribution text. + removeAttribution: function (text) { + if (!text) { return this; } + + if (this._attributions[text]) { + this._attributions[text]--; + this._update(); + } + + return this; + }, + + _update: function () { + if (!this._map) { return; } + + var attribs = []; + + for (var i in this._attributions) { + if (this._attributions[i]) { + attribs.push(i); + } + } + + var prefixAndAttribs = []; + + if (this.options.prefix) { + prefixAndAttribs.push(this.options.prefix); + } + if (attribs.length) { + prefixAndAttribs.push(attribs.join(', ')); + } + + this._container.innerHTML = prefixAndAttribs.join(' | '); + } +}); + +// @namespace Map +// @section Control options +// @option attributionControl: Boolean = true +// Whether a [attribution control](#control-attribution) is added to the map by default. +Map.mergeOptions({ + attributionControl: true +}); + +Map.addInitHook(function () { + if (this.options.attributionControl) { + new Attribution().addTo(this); + } +}); + +// @namespace Control.Attribution +// @factory L.control.attribution(options: Control.Attribution options) +// Creates an attribution control. +var attribution = function (options) { + return new Attribution(options); +}; - // @option fillRule: String = 'evenodd' - // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. - fillRule: 'evenodd', +Control.Layers = Layers; +Control.Zoom = Zoom; +Control.Scale = Scale; +Control.Attribution = Attribution; - // className: '', +control.layers = layers; +control.zoom = zoom; +control.scale = scale; +control.attribution = attribution; - // Option inherited from "Interactive layer" abstract class - interactive: true - }, +/* + L.Handler is a base class for handler classes that are used internally to inject + interaction features like dragging to classes like Map and Marker. +*/ - beforeAdd: function (map) { - // Renderer is set here because we need to call renderer.getEvents - // before this.getEvents. - this._renderer = map.getRenderer(this); - }, +// @class Handler +// @aka L.Handler +// Abstract class for map interaction handlers - onAdd: function () { - this._renderer._initPath(this); - this._reset(); - this._renderer._addPath(this); +var Handler = Class.extend({ + initialize: function (map) { + this._map = map; }, - onRemove: function () { - this._renderer._removePath(this); - }, + // @method enable(): this + // Enables the handler + enable: function () { + if (this._enabled) { return this; } - // @method redraw(): this - // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. - redraw: function () { - if (this._map) { - this._renderer._updatePath(this); - } + this._enabled = true; + this.addHooks(); return this; }, - // @method setStyle(style: Path options): this - // Changes the appearance of a Path based on the options in the `Path options` object. - setStyle: function (style) { - L.setOptions(this, style); - if (this._renderer) { - this._renderer._updateStyle(this); - } - return this; - }, + // @method disable(): this + // Disables the handler + disable: function () { + if (!this._enabled) { return this; } - // @method bringToFront(): this - // Brings the layer to the top of all path layers. - bringToFront: function () { - if (this._renderer) { - this._renderer._bringToFront(this); - } + this._enabled = false; + this.removeHooks(); return this; }, - // @method bringToBack(): this - // Brings the layer to the bottom of all path layers. - bringToBack: function () { - if (this._renderer) { - this._renderer._bringToBack(this); - } - return this; - }, + // @method enabled(): Boolean + // Returns `true` if the handler is enabled + enabled: function () { + return !!this._enabled; + } - getElement: function () { - return this._path; - }, + // @section Extension methods + // Classes inheriting from `Handler` must implement the two following methods: + // @method addHooks() + // Called when the handler is enabled, should add event hooks. + // @method removeHooks() + // Called when the handler is disabled, should remove the event hooks added previously. +}); - _reset: function () { - // defined in children classes - this._project(); - this._update(); - }, +var Mixin = {Events: Events}; - _clickTolerance: function () { - // used when doing hit detection for Canvas layers - return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0); - } +/* + * @class Draggable + * @aka L.Draggable + * @inherits Evented + * + * A class for making DOM elements draggable (including touch support). + * Used internally for map and marker dragging. Only works for elements + * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition). + * + * @example + * ```js + * var draggable = new L.Draggable(elementToDrag); + * draggable.enable(); + * ``` + */ + +var _dragging = false; +var START = touch ? 'touchstart mousedown' : 'mousedown'; +var END = { + mousedown: 'mouseup', + touchstart: 'touchend', + pointerdown: 'touchend', + MSPointerDown: 'touchend' +}; +var MOVE = { + mousedown: 'mousemove', + touchstart: 'touchmove', + pointerdown: 'touchmove', + MSPointerDown: 'touchmove' +}; + + +var Draggable = Evented.extend({ + + options: { + // @section + // @aka Draggable options + // @option clickTolerance: Number = 3 + // The max number of pixels a user can shift the mouse pointer during a click + // for it to be considered a valid click (as opposed to a mouse drag). + clickTolerance: 3 + }, + + // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options) + // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default). + initialize: function (element, dragStartTarget, preventOutline$$1, options) { + setOptions(this, options); + + this._element = element; + this._dragStartTarget = dragStartTarget || element; + this._preventOutline = preventOutline$$1; + }, + + // @method enable() + // Enables the dragging ability + enable: function () { + if (this._enabled) { return; } + + on(this._dragStartTarget, START, this._onDown, this); + + this._enabled = true; + }, + + // @method disable() + // Disables the dragging ability + disable: function () { + if (!this._enabled) { return; } + + // If we're currently dragging this draggable, + // disabling it counts as first ending the drag. + if (L.Draggable._dragging === this) { + this.finishDrag(); + } + + off(this._dragStartTarget, START, this._onDown, this); + + this._enabled = false; + this._moved = false; + }, + + _onDown: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + + this._moved = false; + + if (hasClass(this._element, 'leaflet-zoom-anim')) { return; } + + if (_dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } + _dragging = this; // Prevent dragging multiple objects at once. + + if (this._preventOutline) { + preventOutline(this._element); + } + + disableImageDrag(); + disableTextSelection(); + + if (this._moving) { return; } + + // @event down: Event + // Fired when a drag is about to start. + this.fire('down'); + + var first = e.touches ? e.touches[0] : e; + + this._startPoint = new Point(first.clientX, first.clientY); + + on(document, MOVE[e.type], this._onMove, this); + on(document, END[e.type], this._onUp, this); + }, + + _onMove: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + + if (e.touches && e.touches.length > 1) { + this._moved = true; + return; + } + + var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), + newPoint = new Point(first.clientX, first.clientY), + offset = newPoint.subtract(this._startPoint); + + if (!offset.x && !offset.y) { return; } + if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; } + + preventDefault(e); + + if (!this._moved) { + // @event dragstart: Event + // Fired when a drag starts + this.fire('dragstart'); + + this._moved = true; + this._startPos = getPosition(this._element).subtract(offset); + + addClass(document.body, 'leaflet-dragging'); + + this._lastTarget = e.target || e.srcElement; + // IE and Edge do not give the element, so fetch it + // if necessary + if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) { + this._lastTarget = this._lastTarget.correspondingUseElement; + } + addClass(this._lastTarget, 'leaflet-drag-target'); + } + + this._newPos = this._startPos.add(offset); + this._moving = true; + + cancelAnimFrame(this._animRequest); + this._lastEvent = e; + this._animRequest = requestAnimFrame(this._updatePosition, this, true); + }, + + _updatePosition: function () { + var e = {originalEvent: this._lastEvent}; + + // @event predrag: Event + // Fired continuously during dragging *before* each corresponding + // update of the element's position. + this.fire('predrag', e); + setPosition(this._element, this._newPos); + + // @event drag: Event + // Fired continuously during dragging. + this.fire('drag', e); + }, + + _onUp: function (e) { + // Ignore simulated events, since we handle both touch and + // mouse explicitly; otherwise we risk getting duplicates of + // touch events, see #4315. + // Also ignore the event if disabled; this happens in IE11 + // under some circumstances, see #3666. + if (e._simulated || !this._enabled) { return; } + this.finishDrag(); + }, + + finishDrag: function () { + removeClass(document.body, 'leaflet-dragging'); + + if (this._lastTarget) { + removeClass(this._lastTarget, 'leaflet-drag-target'); + this._lastTarget = null; + } + + for (var i in MOVE) { + off(document, MOVE[i], this._onMove, this); + off(document, END[i], this._onUp, this); + } + + enableImageDrag(); + enableTextSelection(); + + if (this._moved && this._moving) { + // ensure drag is not fired after dragend + cancelAnimFrame(this._animRequest); + + // @event dragend: DragEndEvent + // Fired when the drag ends. + this.fire('dragend', { + distance: this._newPos.distanceTo(this._startPos) + }); + } + + this._moving = false; + _dragging = false; + } + }); - - /* * @namespace LineUtil * * Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast. */ -L.LineUtil = { - - // Simplify polyline with vertex reduction and Douglas-Peucker simplification. - // Improves rendering performance dramatically by lessening the number of points to draw. - - // @function simplify(points: Point[], tolerance: Number): Point[] - // Dramatically reduces the number of points in a polyline while retaining - // its shape and returns a new array of simplified points, using the - // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm). - // Used for a huge performance boost when processing/displaying Leaflet polylines for - // each zoom level and also reducing visual noise. tolerance affects the amount of - // simplification (lesser value means higher quality but slower and with more points). - // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/). - simplify: function (points, tolerance) { - if (!tolerance || !points.length) { - return points.slice(); - } +// Simplify polyline with vertex reduction and Douglas-Peucker simplification. +// Improves rendering performance dramatically by lessening the number of points to draw. + +// @function simplify(points: Point[], tolerance: Number): Point[] +// Dramatically reduces the number of points in a polyline while retaining +// its shape and returns a new array of simplified points, using the +// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm). +// Used for a huge performance boost when processing/displaying Leaflet polylines for +// each zoom level and also reducing visual noise. tolerance affects the amount of +// simplification (lesser value means higher quality but slower and with more points). +// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/). +function simplify(points, tolerance) { + if (!tolerance || !points.length) { + return points.slice(); + } - var sqTolerance = tolerance * tolerance; + var sqTolerance = tolerance * tolerance; - // stage 1: vertex reduction - points = this._reducePoints(points, sqTolerance); + // stage 1: vertex reduction + points = _reducePoints(points, sqTolerance); - // stage 2: Douglas-Peucker simplification - points = this._simplifyDP(points, sqTolerance); + // stage 2: Douglas-Peucker simplification + points = _simplifyDP(points, sqTolerance); - return points; - }, + return points; +} - // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number - // Returns the distance between point `p` and segment `p1` to `p2`. - pointToSegmentDistance: function (p, p1, p2) { - return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true)); - }, +// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number +// Returns the distance between point `p` and segment `p1` to `p2`. +function pointToSegmentDistance(p, p1, p2) { + return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true)); +} - // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number - // Returns the closest point from a point `p` on a segment `p1` to `p2`. - closestPointOnSegment: function (p, p1, p2) { - return this._sqClosestPointOnSegment(p, p1, p2); - }, +// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number +// Returns the closest point from a point `p` on a segment `p1` to `p2`. +function closestPointOnSegment(p, p1, p2) { + return _sqClosestPointOnSegment(p, p1, p2); +} - // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm - _simplifyDP: function (points, sqTolerance) { +// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm +function _simplifyDP(points, sqTolerance) { - var len = points.length, - ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, - markers = new ArrayConstructor(len); + var len = points.length, + ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, + markers = new ArrayConstructor(len); - markers[0] = markers[len - 1] = 1; + markers[0] = markers[len - 1] = 1; - this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1); + _simplifyDPStep(points, markers, sqTolerance, 0, len - 1); - var i, - newPoints = []; + var i, + newPoints = []; - for (i = 0; i < len; i++) { - if (markers[i]) { - newPoints.push(points[i]); - } + for (i = 0; i < len; i++) { + if (markers[i]) { + newPoints.push(points[i]); } + } - return newPoints; - }, + return newPoints; +} - _simplifyDPStep: function (points, markers, sqTolerance, first, last) { +function _simplifyDPStep(points, markers, sqTolerance, first, last) { - var maxSqDist = 0, - index, i, sqDist; + var maxSqDist = 0, + index, i, sqDist; - for (i = first + 1; i <= last - 1; i++) { - sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true); + for (i = first + 1; i <= last - 1; i++) { + sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true); - if (sqDist > maxSqDist) { - index = i; - maxSqDist = sqDist; - } + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; } + } + + if (maxSqDist > sqTolerance) { + markers[index] = 1; + + _simplifyDPStep(points, markers, sqTolerance, first, index); + _simplifyDPStep(points, markers, sqTolerance, index, last); + } +} - if (maxSqDist > sqTolerance) { - markers[index] = 1; +// reduce points that are too close to each other to a single point +function _reducePoints(points, sqTolerance) { + var reducedPoints = [points[0]]; - this._simplifyDPStep(points, markers, sqTolerance, first, index); - this._simplifyDPStep(points, markers, sqTolerance, index, last); + for (var i = 1, prev = 0, len = points.length; i < len; i++) { + if (_sqDist(points[i], points[prev]) > sqTolerance) { + reducedPoints.push(points[i]); + prev = i; } - }, + } + if (prev < len - 1) { + reducedPoints.push(points[len - 1]); + } + return reducedPoints; +} - // reduce points that are too close to each other to a single point - _reducePoints: function (points, sqTolerance) { - var reducedPoints = [points[0]]; +var _lastCode; - for (var i = 1, prev = 0, len = points.length; i < len; i++) { - if (this._sqDist(points[i], points[prev]) > sqTolerance) { - reducedPoints.push(points[i]); - prev = i; - } +// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean +// Clips the segment a to b by rectangular bounds with the +// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm) +// (modifying the segment points directly!). Used by Leaflet to only show polyline +// points that are on the screen or near, increasing performance. +function clipSegment(a, b, bounds, useLastCode, round) { + var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), + codeB = _getBitCode(b, bounds), + + codeOut, p, newCode; + + // save 2nd code to avoid calculating it on the next segment + _lastCode = codeB; + + while (true) { + // if a,b is inside the clip window (trivial accept) + if (!(codeA | codeB)) { + return [a, b]; } - if (prev < len - 1) { - reducedPoints.push(points[len - 1]); + + // if a,b is outside the clip window (trivial reject) + if (codeA & codeB) { + return false; } - return reducedPoints; - }, + // other cases + codeOut = codeA || codeB; + p = _getEdgeIntersection(a, b, codeOut, bounds, round); + newCode = _getBitCode(p, bounds); + + if (codeOut === codeA) { + a = p; + codeA = newCode; + } else { + b = p; + codeB = newCode; + } + } +} - // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean - // Clips the segment a to b by rectangular bounds with the - // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm) - // (modifying the segment points directly!). Used by Leaflet to only show polyline - // points that are on the screen or near, increasing performance. - clipSegment: function (a, b, bounds, useLastCode, round) { - var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), - codeB = this._getBitCode(b, bounds), +function _getEdgeIntersection(a, b, code, bounds, round) { + var dx = b.x - a.x, + dy = b.y - a.y, + min = bounds.min, + max = bounds.max, + x, y; - codeOut, p, newCode; + if (code & 8) { // top + x = a.x + dx * (max.y - a.y) / dy; + y = max.y; - // save 2nd code to avoid calculating it on the next segment - this._lastCode = codeB; + } else if (code & 4) { // bottom + x = a.x + dx * (min.y - a.y) / dy; + y = min.y; - while (true) { - // if a,b is inside the clip window (trivial accept) - if (!(codeA | codeB)) { - return [a, b]; - } + } else if (code & 2) { // right + x = max.x; + y = a.y + dy * (max.x - a.x) / dx; - // if a,b is outside the clip window (trivial reject) - if (codeA & codeB) { - return false; - } + } else if (code & 1) { // left + x = min.x; + y = a.y + dy * (min.x - a.x) / dx; + } - // other cases - codeOut = codeA || codeB; - p = this._getEdgeIntersection(a, b, codeOut, bounds, round); - newCode = this._getBitCode(p, bounds); + return new Point(x, y, round); +} - if (codeOut === codeA) { - a = p; - codeA = newCode; - } else { - b = p; - codeB = newCode; - } - } - }, +function _getBitCode(p, bounds) { + var code = 0; - _getEdgeIntersection: function (a, b, code, bounds, round) { - var dx = b.x - a.x, - dy = b.y - a.y, - min = bounds.min, - max = bounds.max, - x, y; + if (p.x < bounds.min.x) { // left + code |= 1; + } else if (p.x > bounds.max.x) { // right + code |= 2; + } - if (code & 8) { // top - x = a.x + dx * (max.y - a.y) / dy; - y = max.y; + if (p.y < bounds.min.y) { // bottom + code |= 4; + } else if (p.y > bounds.max.y) { // top + code |= 8; + } - } else if (code & 4) { // bottom - x = a.x + dx * (min.y - a.y) / dy; - y = min.y; + return code; +} - } else if (code & 2) { // right - x = max.x; - y = a.y + dy * (max.x - a.x) / dx; +// square distance (to avoid unnecessary Math.sqrt calls) +function _sqDist(p1, p2) { + var dx = p2.x - p1.x, + dy = p2.y - p1.y; + return dx * dx + dy * dy; +} - } else if (code & 1) { // left - x = min.x; - y = a.y + dy * (min.x - a.x) / dx; +// return closest point on segment or distance to that point +function _sqClosestPointOnSegment(p, p1, p2, sqDist) { + var x = p1.x, + y = p1.y, + dx = p2.x - x, + dy = p2.y - y, + dot = dx * dx + dy * dy, + t; + + if (dot > 0) { + t = ((p.x - x) * dx + (p.y - y) * dy) / dot; + + if (t > 1) { + x = p2.x; + y = p2.y; + } else if (t > 0) { + x += dx * t; + y += dy * t; } + } - return new L.Point(x, y, round); - }, + dx = p.x - x; + dy = p.y - y; - _getBitCode: function (p, bounds) { - var code = 0; + return sqDist ? dx * dx + dy * dy : new Point(x, y); +} - if (p.x < bounds.min.x) { // left - code |= 1; - } else if (p.x > bounds.max.x) { // right - code |= 2; - } - if (p.y < bounds.min.y) { // bottom - code |= 4; - } else if (p.y > bounds.max.y) { // top - code |= 8; +function _flat(latlngs) { + // true if it's a flat array of latlngs; false if nested + return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); +} + + +var LineUtil = (Object.freeze || Object)({ + simplify: simplify, + pointToSegmentDistance: pointToSegmentDistance, + closestPointOnSegment: closestPointOnSegment, + clipSegment: clipSegment, + _getEdgeIntersection: _getEdgeIntersection, + _getBitCode: _getBitCode, + _sqClosestPointOnSegment: _sqClosestPointOnSegment, + _flat: _flat +}); + +/* + * @namespace PolyUtil + * Various utility functions for polygon geometries. + */ + +/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] + * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)). + * Used by Leaflet to only show polygon points that are on the screen or near, increasing + * performance. Note that polygon points needs different algorithm for clipping + * than polyline, so there's a seperate method for it. + */ +function clipPolygon(points, bounds, round) { + var clippedPoints, + edges = [1, 4, 2, 8], + i, j, k, + a, b, + len, edge, p; + + for (i = 0, len = points.length; i < len; i++) { + points[i]._code = _getBitCode(points[i], bounds); + } + + // for each edge (left, bottom, right, top) + for (k = 0; k < 4; k++) { + edge = edges[k]; + clippedPoints = []; + + for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { + a = points[i]; + b = points[j]; + + // if a is inside the clip window + if (!(a._code & edge)) { + // if b is outside the clip window (a->b goes out of screen) + if (b._code & edge) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } + clippedPoints.push(a); + + // else if b is inside the clip window (a->b enters the screen) + } else if (!(b._code & edge)) { + p = _getEdgeIntersection(b, a, edge, bounds, round); + p._code = _getBitCode(p, bounds); + clippedPoints.push(p); + } } + points = clippedPoints; + } + + return points; +} + + +var PolyUtil = (Object.freeze || Object)({ + clipPolygon: clipPolygon +}); + +/* + * @namespace Projection + * @section + * Leaflet comes with a set of already defined Projections out of the box: + * + * @projection L.Projection.LonLat + * + * Equirectangular, or Plate Carree projection — the most simple projection, + * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as + * latitude. Also suitable for flat worlds, e.g. game maps. Used by the + * `EPSG:4326` and `Simple` CRS. + */ - return code; +var LonLat = { + project: function (latlng) { + return new Point(latlng.lng, latlng.lat); }, - // square distance (to avoid unnecessary Math.sqrt calls) - _sqDist: function (p1, p2) { - var dx = p2.x - p1.x, - dy = p2.y - p1.y; - return dx * dx + dy * dy; + unproject: function (point) { + return new LatLng(point.y, point.x); }, - // return closest point on segment or distance to that point - _sqClosestPointOnSegment: function (p, p1, p2, sqDist) { - var x = p1.x, - y = p1.y, - dx = p2.x - x, - dy = p2.y - y, - dot = dx * dx + dy * dy, - t; + bounds: new Bounds([-180, -90], [180, 90]) +}; + +/* + * @namespace Projection + * @projection L.Projection.Mercator + * + * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS. + */ + +var Mercator = { + R: 6378137, + R_MINOR: 6356752.314245179, - if (dot > 0) { - t = ((p.x - x) * dx + (p.y - y) * dy) / dot; + bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), - if (t > 1) { - x = p2.x; - y = p2.y; - } else if (t > 0) { - x += dx * t; - y += dy * t; - } - } + project: function (latlng) { + var d = Math.PI / 180, + r = this.R, + y = latlng.lat * d, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + con = e * Math.sin(y); + + var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); + y = -r * Math.log(Math.max(ts, 1E-10)); + + return new Point(latlng.lng * d * r, y); + }, + + unproject: function (point) { + var d = 180 / Math.PI, + r = this.R, + tmp = this.R_MINOR / r, + e = Math.sqrt(1 - tmp * tmp), + ts = Math.exp(-point.y / r), + phi = Math.PI / 2 - 2 * Math.atan(ts); - dx = p.x - x; - dy = p.y - y; + for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { + con = e * Math.sin(phi); + con = Math.pow((1 - con) / (1 + con), e / 2); + dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; + phi += dphi; + } - return sqDist ? dx * dx + dy * dy : new L.Point(x, y); + return new LatLng(phi * d, point.x * d / r); } -}; +}; + +/* + * @class Projection + + * An object with methods for projecting geographical coordinates of the world onto + * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection). + + * @property bounds: Bounds + * The bounds (specified in CRS units) where the projection is valid + + * @method project(latlng: LatLng): Point + * Projects geographical coordinates into a 2D point. + * Only accepts actual `L.LatLng` instances, not arrays. + + * @method unproject(point: Point): LatLng + * The inverse of `project`. Projects a 2D point into a geographical location. + * Only accepts actual `L.Point` instances, not arrays. + */ + + + + +var index = (Object.freeze || Object)({ + LonLat: LonLat, + Mercator: Mercator, + SphericalMercator: SphericalMercator +}); + +/* + * @namespace CRS + * @crs L.CRS.EPSG3395 + * + * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. + */ +var EPSG3395 = extend({}, Earth, { + code: 'EPSG:3395', + projection: Mercator, + + transformation: (function () { + var scale = 0.5 / (Math.PI * Mercator.R); + return toTransformation(scale, 0.5, -scale, 0.5); + }()) +}); +/* + * @namespace CRS + * @crs L.CRS.EPSG4326 + * + * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. + * + * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic), + * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer` + * with this CRS, ensure that there are two 256x256 pixel tiles covering the + * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90), + * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set. + */ + +var EPSG4326 = extend({}, Earth, { + code: 'EPSG:4326', + projection: LonLat, + transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5) +}); /* - * @class Polyline - * @aka L.Polyline - * @inherits Path + * @namespace CRS + * @crs L.CRS.Simple + * + * A simple CRS that maps longitude and latitude into `x` and `y` directly. + * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` + * axis should still be inverted (going from bottom to top). `distance()` returns + * simple euclidean distance. + */ + +var Simple = extend({}, CRS, { + projection: LonLat, + transformation: toTransformation(1, 0, -1, 0), + + scale: function (zoom) { + return Math.pow(2, zoom); + }, + + zoom: function (scale) { + return Math.log(scale) / Math.LN2; + }, + + distance: function (latlng1, latlng2) { + var dx = latlng2.lng - latlng1.lng, + dy = latlng2.lat - latlng1.lat; + + return Math.sqrt(dx * dx + dy * dy); + }, + + infinite: true +}); + +CRS.Earth = Earth; +CRS.EPSG3395 = EPSG3395; +CRS.EPSG3857 = EPSG3857; +CRS.EPSG900913 = EPSG900913; +CRS.EPSG4326 = EPSG4326; +CRS.Simple = Simple; + +/* + * @class Layer + * @inherits Evented + * @aka L.Layer + * @aka ILayer * - * A class for drawing polyline overlays on a map. Extends `Path`. + * A set of methods from the Layer base class that all Leaflet layers use. + * Inherits all methods, options and events from `L.Evented`. * * @example * * ```js - * // create a red polyline from an array of LatLng points - * var latlngs = [ - * [45.51, -122.68], - * [37.77, -122.43], - * [34.04, -118.2] - * ]; - * - * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); - * - * // zoom the map to the polyline - * map.fitBounds(polyline.getBounds()); + * var layer = L.Marker(latlng).addTo(map); + * layer.addTo(map); + * layer.remove(); * ``` * - * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape: + * @event add: Event + * Fired after the layer is added to a map * - * ```js - * // create a red polyline from an array of arrays of LatLng points - * var latlngs = [ - * [[45.51, -122.68], - * [37.77, -122.43], - * [34.04, -118.2]], - * [[40.78, -73.91], - * [41.83, -87.62], - * [32.76, -96.72]] - * ]; - * ``` + * @event remove: Event + * Fired after the layer is removed from a map */ -L.Polyline = L.Path.extend({ - // @section - // @aka Polyline options +var Layer = Evented.extend({ + + // Classes extending `L.Layer` will inherit the following options: options: { - // @option smoothFactor: Number = 1.0 - // How much to simplify the polyline on each zoom level. More means - // better performance and smoother look, and less means more accurate representation. - smoothFactor: 1.0, + // @option pane: String = 'overlayPane' + // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default. + pane: 'overlayPane', - // @option noClip: Boolean = false - // Disable polyline clipping. - noClip: false - }, + // @option attribution: String = null + // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox". + attribution: null, - initialize: function (latlngs, options) { - L.setOptions(this, options); - this._setLatLngs(latlngs); + bubblingMouseEvents: true }, - // @method getLatLngs(): LatLng[] - // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. - getLatLngs: function () { - return this._latlngs; + /* @section + * Classes extending `L.Layer` will inherit the following methods: + * + * @method addTo(map: Map): this + * Adds the layer to the given map + */ + addTo: function (map) { + map.addLayer(this); + return this; }, - // @method setLatLngs(latlngs: LatLng[]): this - // Replaces all the points in the polyline with the given array of geographical points. - setLatLngs: function (latlngs) { - this._setLatLngs(latlngs); - return this.redraw(); + // @method remove: this + // Removes the layer from the map it is currently active on. + remove: function () { + return this.removeFrom(this._map || this._mapToAdd); }, - // @method isEmpty(): Boolean - // Returns `true` if the Polyline has no LatLngs. - isEmpty: function () { - return !this._latlngs.length; + // @method removeFrom(map: Map): this + // Removes the layer from the given map + removeFrom: function (obj) { + if (obj) { + obj.removeLayer(this); + } + return this; }, - closestLayerPoint: function (p) { - var minDistance = Infinity, - minPoint = null, - closest = L.LineUtil._sqClosestPointOnSegment, - p1, p2; - - for (var j = 0, jLen = this._parts.length; j < jLen; j++) { - var points = this._parts[j]; - - for (var i = 1, len = points.length; i < len; i++) { - p1 = points[i - 1]; - p2 = points[i]; - - var sqDist = closest(p, p1, p2, true); + // @method getPane(name? : String): HTMLElement + // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. + getPane: function (name) { + return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); + }, - if (sqDist < minDistance) { - minDistance = sqDist; - minPoint = closest(p, p1, p2); - } - } - } - if (minPoint) { - minPoint.distance = Math.sqrt(minDistance); - } - return minPoint; + addInteractiveTarget: function (targetEl) { + this._map._targets[stamp(targetEl)] = this; + return this; }, - // @method getCenter(): LatLng - // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline. - getCenter: function () { - // throws error when not yet added to map as this center calculation requires projected coordinates - if (!this._map) { - throw new Error('Must add layer to map before using getCenter()'); - } + removeInteractiveTarget: function (targetEl) { + delete this._map._targets[stamp(targetEl)]; + return this; + }, - var i, halfDist, segDist, dist, p1, p2, ratio, - points = this._rings[0], - len = points.length; + // @method getAttribution: String + // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). + getAttribution: function () { + return this.options.attribution; + }, - if (!len) { return null; } + _layerAdd: function (e) { + var map = e.target; - // polyline centroid algorithm; only uses the first ring if there are multiple + // check in case layer gets added and then removed before the map is ready + if (!map.hasLayer(this)) { return; } - for (i = 0, halfDist = 0; i < len - 1; i++) { - halfDist += points[i].distanceTo(points[i + 1]) / 2; - } + this._map = map; + this._zoomAnimated = map._zoomAnimated; - // The line is so small in the current view that all points are on the same pixel. - if (halfDist === 0) { - return this._map.layerPointToLatLng(points[0]); + if (this.getEvents) { + var events = this.getEvents(); + map.on(events, this); + this.once('remove', function () { + map.off(events, this); + }, this); } - for (i = 0, dist = 0; i < len - 1; i++) { - p1 = points[i]; - p2 = points[i + 1]; - segDist = p1.distanceTo(p2); - dist += segDist; + this.onAdd(map); - if (dist > halfDist) { - ratio = (dist - halfDist) / segDist; - return this._map.layerPointToLatLng([ - p2.x - ratio * (p2.x - p1.x), - p2.y - ratio * (p2.y - p1.y) - ]); - } + if (this.getAttribution && map.attributionControl) { + map.attributionControl.addAttribution(this.getAttribution()); } - }, - - // @method getBounds(): LatLngBounds - // Returns the `LatLngBounds` of the path. - getBounds: function () { - return this._bounds; - }, - - // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this - // Adds a given point to the polyline. By default, adds to the first ring of - // the polyline in case of a multi-polyline, but can be overridden by passing - // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). - addLatLng: function (latlng, latlngs) { - latlngs = latlngs || this._defaultShape(); - latlng = L.latLng(latlng); - latlngs.push(latlng); - this._bounds.extend(latlng); - return this.redraw(); - }, - _setLatLngs: function (latlngs) { - this._bounds = new L.LatLngBounds(); - this._latlngs = this._convertLatLngs(latlngs); - }, - - _defaultShape: function () { - return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0]; - }, - - // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way - _convertLatLngs: function (latlngs) { - var result = [], - flat = L.Polyline._flat(latlngs); + this.fire('add'); + map.fire('layeradd', {layer: this}); + } +}); - for (var i = 0, len = latlngs.length; i < len; i++) { - if (flat) { - result[i] = L.latLng(latlngs[i]); - this._bounds.extend(result[i]); - } else { - result[i] = this._convertLatLngs(latlngs[i]); - } - } +/* @section Extension methods + * @uninheritable + * + * Every layer should extend from `L.Layer` and (re-)implement the following methods. + * + * @method onAdd(map: Map): this + * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer). + * + * @method onRemove(map: Map): this + * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer). + * + * @method getEvents(): Object + * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer. + * + * @method getAttribution(): String + * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible. + * + * @method beforeAdd(map: Map): this + * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only. + */ - return result; - }, - _project: function () { - var pxBounds = new L.Bounds(); - this._rings = []; - this._projectLatlngs(this._latlngs, this._rings, pxBounds); +/* @namespace Map + * @section Layer events + * + * @event layeradd: LayerEvent + * Fired when a new layer is added to the map. + * + * @event layerremove: LayerEvent + * Fired when some layer is removed from the map + * + * @section Methods for Layers and Controls + */ +Map.include({ + // @method addLayer(layer: Layer): this + // Adds the given layer to the map + addLayer: function (layer) { + var id = stamp(layer); + if (this._layers[id]) { return this; } + this._layers[id] = layer; - var w = this._clickTolerance(), - p = new L.Point(w, w); + layer._mapToAdd = this; - if (this._bounds.isValid() && pxBounds.isValid()) { - pxBounds.min._subtract(p); - pxBounds.max._add(p); - this._pxBounds = pxBounds; + if (layer.beforeAdd) { + layer.beforeAdd(this); } - }, - // recursively turns latlngs into a set of rings with projected coordinates - _projectLatlngs: function (latlngs, result, projectedBounds) { - var flat = latlngs[0] instanceof L.LatLng, - len = latlngs.length, - i, ring; + this.whenReady(layer._layerAdd, layer); - if (flat) { - ring = []; - for (i = 0; i < len; i++) { - ring[i] = this._map.latLngToLayerPoint(latlngs[i]); - projectedBounds.extend(ring[i]); - } - result.push(ring); - } else { - for (i = 0; i < len; i++) { - this._projectLatlngs(latlngs[i], result, projectedBounds); - } - } + return this; }, - // clip polyline by renderer bounds so that we have less to render for performance - _clipPoints: function () { - var bounds = this._renderer._bounds; + // @method removeLayer(layer: Layer): this + // Removes the given layer from the map. + removeLayer: function (layer) { + var id = stamp(layer); - this._parts = []; - if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { - return; + if (!this._layers[id]) { return this; } + + if (this._loaded) { + layer.onRemove(this); } - if (this.options.noClip) { - this._parts = this._rings; - return; + if (layer.getAttribution && this.attributionControl) { + this.attributionControl.removeAttribution(layer.getAttribution()); } - var parts = this._parts, - i, j, k, len, len2, segment, points; + delete this._layers[id]; - for (i = 0, k = 0, len = this._rings.length; i < len; i++) { - points = this._rings[i]; + if (this._loaded) { + this.fire('layerremove', {layer: layer}); + layer.fire('remove'); + } - for (j = 0, len2 = points.length; j < len2 - 1; j++) { - segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j, true); + layer._map = layer._mapToAdd = null; - if (!segment) { continue; } + return this; + }, - parts[k] = parts[k] || []; - parts[k].push(segment[0]); + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the map + hasLayer: function (layer) { + return !!layer && (stamp(layer) in this._layers); + }, - // if segment goes out of screen, or it's the last one, it's the end of the line part - if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { - parts[k].push(segment[1]); - k++; - } - } + /* @method eachLayer(fn: Function, context?: Object): this + * Iterates over the layers of the map, optionally specifying context of the iterator function. + * ``` + * map.eachLayer(function(layer){ + * layer.bindPopup('Hello'); + * }); + * ``` + */ + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); } + return this; }, - // simplify each clipped part of the polyline for performance - _simplifyPoints: function () { - var parts = this._parts, - tolerance = this.options.smoothFactor; + _addLayers: function (layers) { + layers = layers ? (isArray(layers) ? layers : [layers]) : []; - for (var i = 0, len = parts.length; i < len; i++) { - parts[i] = L.LineUtil.simplify(parts[i], tolerance); + for (var i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); } }, - _update: function () { - if (!this._map) { return; } + _addZoomLimit: function (layer) { + if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { + this._zoomBoundLayers[stamp(layer)] = layer; + this._updateZoomLevels(); + } + }, - this._clipPoints(); - this._simplifyPoints(); - this._updatePath(); + _removeZoomLimit: function (layer) { + var id = stamp(layer); + + if (this._zoomBoundLayers[id]) { + delete this._zoomBoundLayers[id]; + this._updateZoomLevels(); + } }, - _updatePath: function () { - this._renderer._updatePoly(this); + _updateZoomLevels: function () { + var minZoom = Infinity, + maxZoom = -Infinity, + oldZoomSpan = this._getZoomSpan(); + + for (var i in this._zoomBoundLayers) { + var options = this._zoomBoundLayers[i].options; + + minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom); + maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom); + } + + this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom; + this._layersMinZoom = minZoom === Infinity ? undefined : minZoom; + + // @section Map state change events + // @event zoomlevelschange: Event + // Fired when the number of zoomlevels on the map is changed due + // to adding or removing a layer. + if (oldZoomSpan !== this._getZoomSpan()) { + this.fire('zoomlevelschange'); + } + + if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { + this.setZoom(this._layersMaxZoom); + } + if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { + this.setZoom(this._layersMinZoom); + } } }); -// @factory L.polyline(latlngs: LatLng[], options?: Polyline options) -// Instantiates a polyline object given an array of geographical points and -// optionally an options object. You can create a `Polyline` object with -// multiple separate lines (`MultiPolyline`) by passing an array of arrays -// of geographic points. -L.polyline = function (latlngs, options) { - return new L.Polyline(latlngs, options); +/* + * @class LayerGroup + * @aka L.LayerGroup + * @inherits Layer + * + * Used to group several layers and handle them as one. If you add it to the map, + * any layers added or removed from the group will be added/removed on the map as + * well. Extends `Layer`. + * + * @example + * + * ```js + * L.layerGroup([marker1, marker2]) + * .addLayer(polyline) + * .addTo(map); + * ``` + */ + +var LayerGroup = Layer.extend({ + + initialize: function (layers) { + this._layers = {}; + + var i, len; + + if (layers) { + for (i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + } + }, + + // @method addLayer(layer: Layer): this + // Adds the given layer to the group. + addLayer: function (layer) { + var id = this.getLayerId(layer); + + this._layers[id] = layer; + + if (this._map) { + this._map.addLayer(layer); + } + + return this; + }, + + // @method removeLayer(layer: Layer): this + // Removes the given layer from the group. + // @alternative + // @method removeLayer(id: Number): this + // Removes the layer with the given internal ID from the group. + removeLayer: function (layer) { + var id = layer in this._layers ? layer : this.getLayerId(layer); + + if (this._map && this._layers[id]) { + this._map.removeLayer(this._layers[id]); + } + + delete this._layers[id]; + + return this; + }, + + // @method hasLayer(layer: Layer): Boolean + // Returns `true` if the given layer is currently added to the group. + // @alternative + // @method hasLayer(id: Number): Boolean + // Returns `true` if the given internal ID is currently added to the group. + hasLayer: function (layer) { + return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers); + }, + + // @method clearLayers(): this + // Removes all the layers from the group. + clearLayers: function () { + for (var i in this._layers) { + this.removeLayer(this._layers[i]); + } + return this; + }, + + // @method invoke(methodName: String, …): this + // Calls `methodName` on every layer contained in this group, passing any + // additional parameters. Has no effect if the layers contained do not + // implement `methodName`. + invoke: function (methodName) { + var args = Array.prototype.slice.call(arguments, 1), + i, layer; + + for (i in this._layers) { + layer = this._layers[i]; + + if (layer[methodName]) { + layer[methodName].apply(layer, args); + } + } + + return this; + }, + + onAdd: function (map) { + for (var i in this._layers) { + map.addLayer(this._layers[i]); + } + }, + + onRemove: function (map) { + for (var i in this._layers) { + map.removeLayer(this._layers[i]); + } + }, + + // @method eachLayer(fn: Function, context?: Object): this + // Iterates over the layers of the group, optionally specifying context of the iterator function. + // ```js + // group.eachLayer(function (layer) { + // layer.bindPopup('Hello'); + // }); + // ``` + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + // @method getLayer(id: Number): Layer + // Returns the layer with the given internal ID. + getLayer: function (id) { + return this._layers[id]; + }, + + // @method getLayers(): Layer[] + // Returns an array of all the layers added to the group. + getLayers: function () { + var layers = []; + + for (var i in this._layers) { + layers.push(this._layers[i]); + } + return layers; + }, + + // @method setZIndex(zIndex: Number): this + // Calls `setZIndex` on every layer contained in this group, passing the z-index. + setZIndex: function (zIndex) { + return this.invoke('setZIndex', zIndex); + }, + + // @method getLayerId(layer: Layer): Number + // Returns the internal ID for a layer + getLayerId: function (layer) { + return stamp(layer); + } +}); + + +// @factory L.layerGroup(layers: Layer[]) +// Create a layer group, optionally given an initial set of layers. +var layerGroup = function (layers) { + return new LayerGroup(layers); }; -L.Polyline._flat = function (latlngs) { - // true if it's a flat array of latlngs; false if nested - return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); +/* + * @class FeatureGroup + * @aka L.FeatureGroup + * @inherits LayerGroup + * + * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers: + * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip)) + * * Events are propagated to the `FeatureGroup`, so if the group has an event + * handler, it will handle events from any of the layers. This includes mouse events + * and custom events. + * * Has `layeradd` and `layerremove` events + * + * @example + * + * ```js + * L.featureGroup([marker1, marker2, polyline]) + * .bindPopup('Hello world!') + * .on('click', function() { alert('Clicked on a member of the group!'); }) + * .addTo(map); + * ``` + */ + +var FeatureGroup = LayerGroup.extend({ + + addLayer: function (layer) { + if (this.hasLayer(layer)) { + return this; + } + + layer.addEventParent(this); + + LayerGroup.prototype.addLayer.call(this, layer); + + // @event layeradd: LayerEvent + // Fired when a layer is added to this `FeatureGroup` + return this.fire('layeradd', {layer: layer}); + }, + + removeLayer: function (layer) { + if (!this.hasLayer(layer)) { + return this; + } + if (layer in this._layers) { + layer = this._layers[layer]; + } + + layer.removeEventParent(this); + + LayerGroup.prototype.removeLayer.call(this, layer); + + // @event layerremove: LayerEvent + // Fired when a layer is removed from this `FeatureGroup` + return this.fire('layerremove', {layer: layer}); + }, + + // @method setStyle(style: Path options): this + // Sets the given path options to each layer of the group that has a `setStyle` method. + setStyle: function (style) { + return this.invoke('setStyle', style); + }, + + // @method bringToFront(): this + // Brings the layer group to the top of all other layers + bringToFront: function () { + return this.invoke('bringToFront'); + }, + + // @method bringToBack(): this + // Brings the layer group to the top of all other layers + bringToBack: function () { + return this.invoke('bringToBack'); + }, + + // @method getBounds(): LatLngBounds + // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). + getBounds: function () { + var bounds = new LatLngBounds(); + + for (var id in this._layers) { + var layer = this._layers[id]; + bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); + } + return bounds; + } +}); + +// @factory L.featureGroup(layers: Layer[]) +// Create a feature group, optionally given an initial set of layers. +var featureGroup = function (layers) { + return new FeatureGroup(layers); }; - - /* - * @namespace PolyUtil - * Various utility functions for polygon geometries. + * @class Icon + * @aka L.Icon + * + * Represents an icon to provide when creating a marker. + * + * @example + * + * ```js + * var myIcon = L.icon({ + * iconUrl: 'my-icon.png', + * iconRetinaUrl: 'my-icon@2x.png', + * iconSize: [38, 95], + * iconAnchor: [22, 94], + * popupAnchor: [-3, -76], + * shadowUrl: 'my-icon-shadow.png', + * shadowRetinaUrl: 'my-icon-shadow@2x.png', + * shadowSize: [68, 95], + * shadowAnchor: [22, 94] + * }); + * + * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); + * ``` + * + * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default. + * */ -L.PolyUtil = {}; +var Icon = Class.extend({ -/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] - * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)). - * Used by Leaflet to only show polygon points that are on the screen or near, increasing - * performance. Note that polygon points needs different algorithm for clipping - * than polyline, so there's a seperate method for it. - */ -L.PolyUtil.clipPolygon = function (points, bounds, round) { - var clippedPoints, - edges = [1, 4, 2, 8], - i, j, k, - a, b, - len, edge, p, - lu = L.LineUtil; + /* @section + * @aka Icon options + * + * @option iconUrl: String = null + * **(required)** The URL to the icon image (absolute or relative to your script path). + * + * @option iconRetinaUrl: String = null + * The URL to a retina sized version of the icon image (absolute or relative to your + * script path). Used for Retina screen devices. + * + * @option iconSize: Point = null + * Size of the icon image in pixels. + * + * @option iconAnchor: Point = null + * The coordinates of the "tip" of the icon (relative to its top left corner). The icon + * will be aligned so that this point is at the marker's geographical location. Centered + * by default if size is specified, also can be set in CSS with negative margins. + * + * @option popupAnchor: Point = null + * The coordinates of the point from which popups will "open", relative to the icon anchor. + * + * @option shadowUrl: String = null + * The URL to the icon shadow image. If not specified, no shadow image will be created. + * + * @option shadowRetinaUrl: String = null + * + * @option shadowSize: Point = null + * Size of the shadow image in pixels. + * + * @option shadowAnchor: Point = null + * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same + * as iconAnchor if not specified). + * + * @option className: String = '' + * A custom class name to assign to both icon and shadow images. Empty by default. + */ - for (i = 0, len = points.length; i < len; i++) { - points[i]._code = lu._getBitCode(points[i], bounds); - } + initialize: function (options) { + setOptions(this, options); + }, - // for each edge (left, bottom, right, top) - for (k = 0; k < 4; k++) { - edge = edges[k]; - clippedPoints = []; + // @method createIcon(oldIcon?: HTMLElement): HTMLElement + // Called internally when the icon has to be shown, returns a `` HTML element + // styled according to the options. + createIcon: function (oldIcon) { + return this._createIcon('icon', oldIcon); + }, - for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { - a = points[i]; - b = points[j]; + // @method createShadow(oldIcon?: HTMLElement): HTMLElement + // As `createIcon`, but for the shadow beneath it. + createShadow: function (oldIcon) { + return this._createIcon('shadow', oldIcon); + }, - // if a is inside the clip window - if (!(a._code & edge)) { - // if b is outside the clip window (a->b goes out of screen) - if (b._code & edge) { - p = lu._getEdgeIntersection(b, a, edge, bounds, round); - p._code = lu._getBitCode(p, bounds); - clippedPoints.push(p); - } - clippedPoints.push(a); + _createIcon: function (name, oldIcon) { + var src = this._getIconUrl(name); + + if (!src) { + if (name === 'icon') { + throw new Error('iconUrl not set in Icon options (see the docs).'); + } + return null; + } + + var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null); + this._setIconStyles(img, name); + + return img; + }, + + _setIconStyles: function (img, name) { + var options = this.options; + var sizeOption = options[name + 'Size']; + + if (typeof sizeOption === 'number') { + sizeOption = [sizeOption, sizeOption]; + } + + var size = toPoint(sizeOption), + anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor || + size && size.divideBy(2, true)); + + img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); + + if (anchor) { + img.style.marginLeft = (-anchor.x) + 'px'; + img.style.marginTop = (-anchor.y) + 'px'; + } - // else if b is inside the clip window (a->b enters the screen) - } else if (!(b._code & edge)) { - p = lu._getEdgeIntersection(b, a, edge, bounds, round); - p._code = lu._getBitCode(p, bounds); - clippedPoints.push(p); - } + if (size) { + img.style.width = size.x + 'px'; + img.style.height = size.y + 'px'; } - points = clippedPoints; + }, + + _createImg: function (src, el) { + el = el || document.createElement('img'); + el.src = src; + return el; + }, + + _getIconUrl: function (name) { + return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url']; } +}); - return points; -}; - - - -/* - * @class Polygon - * @aka L.Polygon - * @inherits Polyline - * - * A class for drawing polygon overlays on a map. Extends `Polyline`. - * - * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points. - * - * - * @example - * - * ```js - * // create a red polygon from an array of LatLng points - * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]; - * - * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map); - * - * // zoom the map to the polygon - * map.fitBounds(polygon.getBounds()); - * ``` - * - * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape: - * - * ```js - * var latlngs = [ - * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring - * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole - * ]; - * ``` - * - * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape. - * - * ```js - * var latlngs = [ - * [ // first polygon - * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring - * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole - * ], - * [ // second polygon - * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]] - * ] - * ]; - * ``` - */ - -L.Polygon = L.Polyline.extend({ - - options: { - fill: true - }, - - isEmpty: function () { - return !this._latlngs.length || !this._latlngs[0].length; - }, - - getCenter: function () { - // throws error when not yet added to map as this center calculation requires projected coordinates - if (!this._map) { - throw new Error('Must add layer to map before using getCenter()'); - } - - var i, j, p1, p2, f, area, x, y, center, - points = this._rings[0], - len = points.length; - - if (!len) { return null; } - - // polygon centroid algorithm; only uses the first ring if there are multiple - - area = x = y = 0; - - for (i = 0, j = len - 1; i < len; j = i++) { - p1 = points[i]; - p2 = points[j]; - - f = p1.y * p2.x - p2.y * p1.x; - x += (p1.x + p2.x) * f; - y += (p1.y + p2.y) * f; - area += f * 3; - } - - if (area === 0) { - // Polygon is so small that all points are on same pixel. - center = points[0]; - } else { - center = [x / area, y / area]; - } - return this._map.layerPointToLatLng(center); - }, - - _convertLatLngs: function (latlngs) { - var result = L.Polyline.prototype._convertLatLngs.call(this, latlngs), - len = result.length; - - // remove last point if it equals first one - if (len >= 2 && result[0] instanceof L.LatLng && result[0].equals(result[len - 1])) { - result.pop(); - } - return result; - }, - - _setLatLngs: function (latlngs) { - L.Polyline.prototype._setLatLngs.call(this, latlngs); - if (L.Polyline._flat(this._latlngs)) { - this._latlngs = [this._latlngs]; - } - }, - - _defaultShape: function () { - return L.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; - }, - - _clipPoints: function () { - // polygons need a different clipping algorithm so we redefine that - - var bounds = this._renderer._bounds, - w = this.options.weight, - p = new L.Point(w, w); - - // increase clip padding by stroke width to avoid stroke on clip edges - bounds = new L.Bounds(bounds.min.subtract(p), bounds.max.add(p)); - - this._parts = []; - if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { - return; - } - - if (this.options.noClip) { - this._parts = this._rings; - return; - } - - for (var i = 0, len = this._rings.length, clipped; i < len; i++) { - clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds, true); - if (clipped.length) { - this._parts.push(clipped); - } - } - }, - - _updatePath: function () { - this._renderer._updatePoly(this, true); - } -}); - - -// @factory L.polygon(latlngs: LatLng[], options?: Polyline options) -L.polygon = function (latlngs, options) { - return new L.Polygon(latlngs, options); -}; - - - -/* - * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. - */ - -/* - * @class Rectangle - * @aka L.Retangle - * @inherits Polygon - * - * A class for drawing rectangle overlays on a map. Extends `Polygon`. - * - * @example - * - * ```js - * // define rectangle geographical bounds - * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]]; - * - * // create an orange rectangle - * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map); - * - * // zoom the map to the rectangle bounds - * map.fitBounds(bounds); - * ``` - * - */ - - -L.Rectangle = L.Polygon.extend({ - initialize: function (latLngBounds, options) { - L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); - }, - - // @method setBounds(latLngBounds: LatLngBounds): this - // Redraws the rectangle with the passed bounds. - setBounds: function (latLngBounds) { - return this.setLatLngs(this._boundsToLatLngs(latLngBounds)); - }, - - _boundsToLatLngs: function (latLngBounds) { - latLngBounds = L.latLngBounds(latLngBounds); - return [ - latLngBounds.getSouthWest(), - latLngBounds.getNorthWest(), - latLngBounds.getNorthEast(), - latLngBounds.getSouthEast() - ]; - } -}); - - -// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options) -L.rectangle = function (latLngBounds, options) { - return new L.Rectangle(latLngBounds, options); -}; - - + +// @factory L.icon(options: Icon options) +// Creates an icon instance with the given options. +function icon(options) { + return new Icon(options); +} /* - * @class CircleMarker - * @aka L.CircleMarker - * @inherits Path + * @miniclass Icon.Default (Icon) + * @aka L.Icon.Default + * @section * - * A circle of a fixed size with radius specified in pixels. Extends `Path`. - */ - -L.CircleMarker = L.Path.extend({ - - // @section - // @aka CircleMarker options - options: { - fill: true, - - // @option radius: Number = 10 - // Radius of the circle marker, in pixels - radius: 10 - }, - - initialize: function (latlng, options) { - L.setOptions(this, options); - this._latlng = L.latLng(latlng); - this._radius = this.options.radius; - }, - - // @method setLatLng(latLng: LatLng): this - // Sets the position of a circle marker to a new location. - setLatLng: function (latlng) { - this._latlng = L.latLng(latlng); - this.redraw(); - return this.fire('move', {latlng: this._latlng}); - }, + * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when + * no icon is specified. Points to the blue marker image distributed with Leaflet + * releases. + * + * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options` + * (which is a set of `Icon options`). + * + * If you want to _completely_ replace the default icon, override the + * `L.Marker.prototype.options.icon` with your own icon instead. + */ - // @method getLatLng(): LatLng - // Returns the current geographical position of the circle marker - getLatLng: function () { - return this._latlng; - }, +var IconDefault = Icon.extend({ - // @method setRadius(radius: Number): this - // Sets the radius of a circle marker. Units are in pixels. - setRadius: function (radius) { - this.options.radius = this._radius = radius; - return this.redraw(); + options: { + iconUrl: 'marker-icon.png', + iconRetinaUrl: 'marker-icon-2x.png', + shadowUrl: 'marker-shadow.png', + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + tooltipAnchor: [16, -28], + shadowSize: [41, 41] }, - // @method getRadius(): Number - // Returns the current radius of the circle - getRadius: function () { - return this._radius; - }, + _getIconUrl: function (name) { + if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only + IconDefault.imagePath = this._detectIconPath(); + } - setStyle : function (options) { - var radius = options && options.radius || this._radius; - L.Path.prototype.setStyle.call(this, options); - this.setRadius(radius); - return this; + // @option imagePath: String + // `Icon.Default` will try to auto-detect the absolute location of the + // blue icon images. If you are placing these images in a non-standard + // way, set this option to point to the right absolute path. + return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name); }, - _project: function () { - this._point = this._map.latLngToLayerPoint(this._latlng); - this._updateBounds(); - }, + _detectIconPath: function () { + var el = create$1('div', 'leaflet-default-icon-path', document.body); + var path = getStyle(el, 'background-image') || + getStyle(el, 'backgroundImage'); // IE8 - _updateBounds: function () { - var r = this._radius, - r2 = this._radiusY || r, - w = this._clickTolerance(), - p = [r + w, r2 + w]; - this._pxBounds = new L.Bounds(this._point.subtract(p), this._point.add(p)); - }, + document.body.removeChild(el); - _update: function () { - if (this._map) { - this._updatePath(); + if (path === null || path.indexOf('url') !== 0) { + path = ''; + } else { + path = path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, ''); } - }, - - _updatePath: function () { - this._renderer._updateCircle(this); - }, - _empty: function () { - return this._radius && !this._renderer._bounds.intersects(this._pxBounds); + return path; } }); - -// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) -// Instantiates a circle marker object given a geographical point, and an optional options object. -L.circleMarker = function (latlng, options) { - return new L.CircleMarker(latlng, options); -}; - +/* + * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. + */ -/* - * @class Circle - * @aka L.Circle - * @inherits CircleMarker - * - * A class for drawing circle overlays on a map. Extends `CircleMarker`. - * - * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). +/* @namespace Marker + * @section Interaction handlers * - * @example + * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example: * * ```js - * L.circle([50.5, 30.5], {radius: 200}).addTo(map); + * marker.dragging.disable(); * ``` + * + * @property dragging: Handler + * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)). */ -L.Circle = L.CircleMarker.extend({ +var MarkerDrag = Handler.extend({ + initialize: function (marker) { + this._marker = marker; + }, - initialize: function (latlng, options, legacyOptions) { - if (typeof options === 'number') { - // Backwards compatibility with 0.7.x factory (latlng, radius, options?) - options = L.extend({}, legacyOptions, {radius: options}); + addHooks: function () { + var icon = this._marker._icon; + + if (!this._draggable) { + this._draggable = new Draggable(icon, icon, true); } - L.setOptions(this, options); - this._latlng = L.latLng(latlng); - if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } + this._draggable.on({ + dragstart: this._onDragStart, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).enable(); - // @section - // @aka Circle options - // @option radius: Number; Radius of the circle, in meters. - this._mRadius = this.options.radius; + addClass(icon, 'leaflet-marker-draggable'); }, - // @method setRadius(radius: Number): this - // Sets the radius of a circle. Units are in meters. - setRadius: function (radius) { - this._mRadius = radius; - return this.redraw(); - }, + removeHooks: function () { + this._draggable.off({ + dragstart: this._onDragStart, + drag: this._onDrag, + dragend: this._onDragEnd + }, this).disable(); - // @method getRadius(): Number - // Returns the current radius of a circle. Units are in meters. - getRadius: function () { - return this._mRadius; + if (this._marker._icon) { + removeClass(this._marker._icon, 'leaflet-marker-draggable'); + } }, - // @method getBounds(): LatLngBounds - // Returns the `LatLngBounds` of the path. - getBounds: function () { - var half = [this._radius, this._radiusY || this._radius]; - - return new L.LatLngBounds( - this._map.layerPointToLatLng(this._point.subtract(half)), - this._map.layerPointToLatLng(this._point.add(half))); + moved: function () { + return this._draggable && this._draggable._moved; }, - setStyle: L.Path.prototype.setStyle, + _onDragStart: function () { + // @section Dragging events + // @event dragstart: Event + // Fired when the user starts dragging the marker. - _project: function () { + // @event movestart: Event + // Fired when the marker starts moving (because of dragging). - var lng = this._latlng.lng, - lat = this._latlng.lat, - map = this._map, - crs = map.options.crs; + this._oldLatLng = this._marker.getLatLng(); + this._marker + .closePopup() + .fire('movestart') + .fire('dragstart'); + }, - if (crs.distance === L.CRS.Earth.distance) { - var d = Math.PI / 180, - latR = (this._mRadius / L.CRS.Earth.R) / d, - top = map.project([lat + latR, lng]), - bottom = map.project([lat - latR, lng]), - p = top.add(bottom).divideBy(2), - lat2 = map.unproject(p).lat, - lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / - (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; + _onDrag: function (e) { + var marker = this._marker, + shadow = marker._shadow, + iconPos = getPosition(marker._icon), + latlng = marker._map.layerPointToLatLng(iconPos); - if (isNaN(lngR) || lngR === 0) { - lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 - } + // update shadow position + if (shadow) { + setPosition(shadow, iconPos); + } - this._point = p.subtract(map.getPixelOrigin()); - this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1); - this._radiusY = Math.max(Math.round(p.y - top.y), 1); + marker._latlng = latlng; + e.latlng = latlng; + e.oldLatLng = this._oldLatLng; - } else { - var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); + // @event drag: Event + // Fired repeatedly while the user drags the marker. + marker + .fire('move', e) + .fire('drag', e); + }, - this._point = map.latLngToLayerPoint(this._latlng); - this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; - } + _onDragEnd: function (e) { + // @event dragend: DragEndEvent + // Fired when the user stops dragging the marker. - this._updateBounds(); + // @event moveend: Event + // Fired when the marker stops moving (because of dragging). + delete this._oldLatLng; + this._marker + .fire('moveend') + .fire('dragend', e); } }); -// @factory L.circle(latlng: LatLng, options?: Circle options) -// Instantiates a circle object given a geographical point, and an options object -// which contains the circle radius. -// @alternative -// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) -// Obsolete way of instantiating a circle, for compatibility with 0.7.x code. -// Do not use in new applications or plugins. -L.circle = function (latlng, options, legacyOptions) { - return new L.Circle(latlng, options, legacyOptions); -}; - - +/* + * @class Marker + * @inherits Interactive layer + * @aka L.Marker + * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`. + * + * @example + * + * ```js + * L.marker([50.5, 30.5]).addTo(map); + * ``` + */ + +var Marker = Layer.extend({ + + // @section + // @aka Marker options + options: { + // @option icon: Icon = * + // Icon instance to use for rendering the marker. + // See [Icon documentation](#L.Icon) for details on how to customize the marker icon. + // If not specified, a common instance of `L.Icon.Default` is used. + icon: new IconDefault(), + + // Option inherited from "Interactive layer" abstract class + interactive: true, + + // @option draggable: Boolean = false + // Whether the marker is draggable with mouse/touch or not. + draggable: false, + + // @option keyboard: Boolean = true + // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. + keyboard: true, + + // @option title: String = '' + // Text for the browser tooltip that appear on marker hover (no tooltip by default). + title: '', + + // @option alt: String = '' + // Text for the `alt` attribute of the icon image (useful for accessibility). + alt: '', + + // @option zIndexOffset: Number = 0 + // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively). + zIndexOffset: 0, + + // @option opacity: Number = 1.0 + // The opacity of the marker. + opacity: 1, + + // @option riseOnHover: Boolean = false + // If `true`, the marker will get on top of others when you hover the mouse over it. + riseOnHover: false, + + // @option riseOffset: Number = 250 + // The z-index offset used for the `riseOnHover` feature. + riseOffset: 250, + + // @option pane: String = 'markerPane' + // `Map pane` where the markers icon will be added. + pane: 'markerPane', + + // @option bubblingMouseEvents: Boolean = false + // When `true`, a mouse event on this marker will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: false + }, + + /* @section + * + * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: + */ + + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + }, + + onAdd: function (map) { + this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation; + + if (this._zoomAnimated) { + map.on('zoomanim', this._animateZoom, this); + } + + this._initIcon(); + this.update(); + }, + + onRemove: function (map) { + if (this.dragging && this.dragging.enabled()) { + this.options.draggable = true; + this.dragging.removeHooks(); + } + delete this.dragging; + + if (this._zoomAnimated) { + map.off('zoomanim', this._animateZoom, this); + } + + this._removeIcon(); + this._removeShadow(); + }, + + getEvents: function () { + return { + zoom: this.update, + viewreset: this.update + }; + }, + + // @method getLatLng: LatLng + // Returns the current geographical position of the marker. + getLatLng: function () { + return this._latlng; + }, + + // @method setLatLng(latlng: LatLng): this + // Changes the marker position to the given point. + setLatLng: function (latlng) { + var oldLatLng = this._latlng; + this._latlng = toLatLng(latlng); + this.update(); + + // @event move: Event + // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. + return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); + }, + + // @method setZIndexOffset(offset: Number): this + // Changes the [zIndex offset](#marker-zindexoffset) of the marker. + setZIndexOffset: function (offset) { + this.options.zIndexOffset = offset; + return this.update(); + }, + + // @method setIcon(icon: Icon): this + // Changes the marker icon. + setIcon: function (icon) { + + this.options.icon = icon; + + if (this._map) { + this._initIcon(); + this.update(); + } + + if (this._popup) { + this.bindPopup(this._popup, this._popup.options); + } + + return this; + }, + + getElement: function () { + return this._icon; + }, + + update: function () { + + if (this._icon) { + var pos = this._map.latLngToLayerPoint(this._latlng).round(); + this._setPos(pos); + } + + return this; + }, + + _initIcon: function () { + var options = this.options, + classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); + + var icon = options.icon.createIcon(this._icon), + addIcon = false; + + // if we're not reusing the icon, remove the old one and init new one + if (icon !== this._icon) { + if (this._icon) { + this._removeIcon(); + } + addIcon = true; + + if (options.title) { + icon.title = options.title; + } + if (options.alt) { + icon.alt = options.alt; + } + } + + addClass(icon, classToAdd); + + if (options.keyboard) { + icon.tabIndex = '0'; + } + + this._icon = icon; + + if (options.riseOnHover) { + this.on({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + var newShadow = options.icon.createShadow(this._shadow), + addShadow = false; + + if (newShadow !== this._shadow) { + this._removeShadow(); + addShadow = true; + } + + if (newShadow) { + addClass(newShadow, classToAdd); + newShadow.alt = ''; + } + this._shadow = newShadow; + + + if (options.opacity < 1) { + this._updateOpacity(); + } + + + if (addIcon) { + this.getPane().appendChild(this._icon); + } + this._initInteraction(); + if (newShadow && addShadow) { + this.getPane('shadowPane').appendChild(this._shadow); + } + }, + + _removeIcon: function () { + if (this.options.riseOnHover) { + this.off({ + mouseover: this._bringToFront, + mouseout: this._resetZIndex + }); + } + + remove(this._icon); + this.removeInteractiveTarget(this._icon); + + this._icon = null; + }, + + _removeShadow: function () { + if (this._shadow) { + remove(this._shadow); + } + this._shadow = null; + }, + + _setPos: function (pos) { + setPosition(this._icon, pos); + + if (this._shadow) { + setPosition(this._shadow, pos); + } + + this._zIndex = pos.y + this.options.zIndexOffset; + + this._resetZIndex(); + }, + + _updateZIndex: function (offset) { + this._icon.style.zIndex = this._zIndex + offset; + }, + + _animateZoom: function (opt) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); + + this._setPos(pos); + }, + + _initInteraction: function () { + + if (!this.options.interactive) { return; } + + addClass(this._icon, 'leaflet-interactive'); + + this.addInteractiveTarget(this._icon); + + if (MarkerDrag) { + var draggable = this.options.draggable; + if (this.dragging) { + draggable = this.dragging.enabled(); + this.dragging.disable(); + } + + this.dragging = new MarkerDrag(this); + + if (draggable) { + this.dragging.enable(); + } + } + }, + + // @method setOpacity(opacity: Number): this + // Changes the opacity of the marker. + setOpacity: function (opacity) { + this.options.opacity = opacity; + if (this._map) { + this._updateOpacity(); + } + + return this; + }, + + _updateOpacity: function () { + var opacity = this.options.opacity; + + setOpacity(this._icon, opacity); + + if (this._shadow) { + setOpacity(this._shadow, opacity); + } + }, + + _bringToFront: function () { + this._updateZIndex(this.options.riseOffset); + }, + + _resetZIndex: function () { + this._updateZIndex(0); + }, + + _getPopupAnchor: function () { + return this.options.icon.options.popupAnchor || [0, 0]; + }, + + _getTooltipAnchor: function () { + return this.options.icon.options.tooltipAnchor || [0, 0]; + } +}); + + +// factory L.marker(latlng: LatLng, options? : Marker options) + +// @factory L.marker(latlng: LatLng, options? : Marker options) +// Instantiates a Marker object given a geographical point and optionally an options object. +function marker(latlng, options) { + return new Marker(latlng, options); +} /* - * @class SVG - * @inherits Renderer - * @aka L.SVG - * - * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG). - * Inherits `Renderer`. - * - * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not - * available in all web browsers, notably Android 2.x and 3.x. - * - * Although SVG is not available on IE7 and IE8, these browsers support - * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language) - * (a now deprecated technology), and the SVG renderer will fall back to VML in - * this case. - * - * @example - * - * Use SVG by default for all paths in the map: - * - * ```js - * var map = L.map('map', { - * renderer: L.svg() - * }); - * ``` - * - * Use a SVG renderer with extra padding for specific vector geometries: + * @class Path + * @aka L.Path + * @inherits Interactive layer * - * ```js - * var map = L.map('map'); - * var myRenderer = L.svg({ padding: 0.5 }); - * var line = L.polyline( coordinates, { renderer: myRenderer } ); - * var circle = L.circle( center, { renderer: myRenderer } ); - * ``` + * An abstract class that contains options and constants shared between vector + * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`. */ -L.SVG = L.Renderer.extend({ - - getEvents: function () { - var events = L.Renderer.prototype.getEvents.call(this); - events.zoomstart = this._onZoomStart; - return events; - }, +var Path = Layer.extend({ - _initContainer: function () { - this._container = L.SVG.create('svg'); + // @section + // @aka Path options + options: { + // @option stroke: Boolean = true + // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. + stroke: true, - // makes it possible to click through svg root; we'll reset it back in individual paths - this._container.setAttribute('pointer-events', 'none'); + // @option color: String = '#3388ff' + // Stroke color + color: '#3388ff', - this._rootGroup = L.SVG.create('g'); - this._container.appendChild(this._rootGroup); - }, + // @option weight: Number = 3 + // Stroke width in pixels + weight: 3, - _onZoomStart: function () { - // Drag-then-pinch interactions might mess up the center and zoom. - // In this case, the easiest way to prevent this is re-do the renderer - // bounds and padding when the zooming starts. - this._update(); - }, + // @option opacity: Number = 1.0 + // Stroke opacity + opacity: 1, - _update: function () { - if (this._map._animatingZoom && this._bounds) { return; } + // @option lineCap: String= 'round' + // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. + lineCap: 'round', - L.Renderer.prototype._update.call(this); + // @option lineJoin: String = 'round' + // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. + lineJoin: 'round', - var b = this._bounds, - size = b.getSize(), - container = this._container; + // @option dashArray: String = null + // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashArray: null, - // set size of svg-container if changed - if (!this._svgSize || !this._svgSize.equals(size)) { - this._svgSize = size; - container.setAttribute('width', size.x); - container.setAttribute('height', size.y); - } + // @option dashOffset: String = null + // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). + dashOffset: null, - // movement: update container viewBox so that we don't have to change coordinates of individual layers - L.DomUtil.setPosition(container, b.min); - container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); + // @option fill: Boolean = depends + // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. + fill: false, - this.fire('update'); - }, + // @option fillColor: String = * + // Fill color. Defaults to the value of the [`color`](#path-color) option + fillColor: null, - // methods below are called by vector layers implementations + // @option fillOpacity: Number = 0.2 + // Fill opacity. + fillOpacity: 0.2, - _initPath: function (layer) { - var path = layer._path = L.SVG.create('path'); + // @option fillRule: String = 'evenodd' + // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. + fillRule: 'evenodd', - // @namespace Path - // @option className: String = null - // Custom class name set on an element. Only for SVG renderer. - if (layer.options.className) { - L.DomUtil.addClass(path, layer.options.className); - } + // className: '', - if (layer.options.interactive) { - L.DomUtil.addClass(path, 'leaflet-interactive'); - } + // Option inherited from "Interactive layer" abstract class + interactive: true, - this._updateStyle(layer); - this._layers[L.stamp(layer)] = layer; + // @option bubblingMouseEvents: Boolean = true + // When `true`, a mouse event on this path will trigger the same event on the map + // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). + bubblingMouseEvents: true }, - _addPath: function (layer) { - this._rootGroup.appendChild(layer._path); - layer.addInteractiveTarget(layer._path); + beforeAdd: function (map) { + // Renderer is set here because we need to call renderer.getEvents + // before this.getEvents. + this._renderer = map.getRenderer(this); }, - _removePath: function (layer) { - L.DomUtil.remove(layer._path); - layer.removeInteractiveTarget(layer._path); - delete this._layers[L.stamp(layer)]; + onAdd: function () { + this._renderer._initPath(this); + this._reset(); + this._renderer._addPath(this); }, - _updatePath: function (layer) { - layer._project(); - layer._update(); + onRemove: function () { + this._renderer._removePath(this); }, - _updateStyle: function (layer) { - var path = layer._path, - options = layer.options; - - if (!path) { return; } - - if (options.stroke) { - path.setAttribute('stroke', options.color); - path.setAttribute('stroke-opacity', options.opacity); - path.setAttribute('stroke-width', options.weight); - path.setAttribute('stroke-linecap', options.lineCap); - path.setAttribute('stroke-linejoin', options.lineJoin); - - if (options.dashArray) { - path.setAttribute('stroke-dasharray', options.dashArray); - } else { - path.removeAttribute('stroke-dasharray'); - } - - if (options.dashOffset) { - path.setAttribute('stroke-dashoffset', options.dashOffset); - } else { - path.removeAttribute('stroke-dashoffset'); - } - } else { - path.setAttribute('stroke', 'none'); - } - - if (options.fill) { - path.setAttribute('fill', options.fillColor || options.color); - path.setAttribute('fill-opacity', options.fillOpacity); - path.setAttribute('fill-rule', options.fillRule || 'evenodd'); - } else { - path.setAttribute('fill', 'none'); + // @method redraw(): this + // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. + redraw: function () { + if (this._map) { + this._renderer._updatePath(this); } + return this; }, - _updatePoly: function (layer, closed) { - this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed)); + // @method setStyle(style: Path options): this + // Changes the appearance of a Path based on the options in the `Path options` object. + setStyle: function (style) { + setOptions(this, style); + if (this._renderer) { + this._renderer._updateStyle(this); + } + return this; }, - _updateCircle: function (layer) { - var p = layer._point, - r = layer._radius, - r2 = layer._radiusY || r, - arc = 'a' + r + ',' + r2 + ' 0 1,0 '; - - // drawing a circle with two half-arcs - var d = layer._empty() ? 'M0 0' : - 'M' + (p.x - r) + ',' + p.y + - arc + (r * 2) + ',0 ' + - arc + (-r * 2) + ',0 '; - - this._setPath(layer, d); + // @method bringToFront(): this + // Brings the layer to the top of all path layers. + bringToFront: function () { + if (this._renderer) { + this._renderer._bringToFront(this); + } + return this; }, - _setPath: function (layer, path) { - layer._path.setAttribute('d', path); + // @method bringToBack(): this + // Brings the layer to the bottom of all path layers. + bringToBack: function () { + if (this._renderer) { + this._renderer._bringToBack(this); + } + return this; }, - // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements - _bringToFront: function (layer) { - L.DomUtil.toFront(layer._path); + getElement: function () { + return this._path; }, - _bringToBack: function (layer) { - L.DomUtil.toBack(layer._path); - } -}); - - -// @namespace SVG; @section -// There are several static functions which can be called without instantiating L.SVG: -L.extend(L.SVG, { - // @function create(name: String): SVGElement - // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), - // corresponding to the class name passed. For example, using 'line' will return - // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). - create: function (name) { - return document.createElementNS('http://www.w3.org/2000/svg', name); - }, - - // @function pointsToPath(rings: Point[], closed: Boolean): String - // Generates a SVG path string for multiple rings, with each ring turning - // into "M..L..L.." instructions - pointsToPath: function (rings, closed) { - var str = '', - i, j, len, len2, points, p; - - for (i = 0, len = rings.length; i < len; i++) { - points = rings[i]; - - for (j = 0, len2 = points.length; j < len2; j++) { - p = points[j]; - str += (j ? 'L' : 'M') + p.x + ' ' + p.y; - } - - // closes the ring for polygons; "x" is VML syntax - str += closed ? (L.Browser.svg ? 'z' : 'x') : ''; - } - - // SVG complains about empty path strings - return str || 'M0 0'; - } -}); - -// @namespace Browser; @property svg: Boolean -// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). -L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect); - - -// @namespace SVG -// @factory L.svg(options?: Renderer options) -// Creates a SVG renderer with the given options. -L.svg = function (options) { - return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null; -}; - - + _reset: function () { + // defined in child classes + this._project(); + this._update(); + }, -/* - * Thanks to Dmitry Baranovsky and his Raphael library for inspiration! - */ + _clickTolerance: function () { + // used when doing hit detection for Canvas layers + return (this.options.stroke ? this.options.weight / 2 : 0) + (touch ? 10 : 0); + } +}); /* - * @class SVG - * - * Although SVG is not available on IE7 and IE8, these browsers support [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language), and the SVG renderer will fall back to VML in this case. + * @class CircleMarker + * @aka L.CircleMarker + * @inherits Path * - * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility - * with old versions of Internet Explorer. + * A circle of a fixed size with radius specified in pixels. Extends `Path`. */ -// @namespace Browser; @property vml: Boolean -// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). -L.Browser.vml = !L.Browser.svg && (function () { - try { - var div = document.createElement('div'); - div.innerHTML = ''; - - var shape = div.firstChild; - shape.style.behavior = 'url(#default#VML)'; - - return shape && (typeof shape.adj === 'object'); - - } catch (e) { - return false; - } -}()); +var CircleMarker = Path.extend({ -// redefine some SVG methods to handle VML syntax which is similar but with some differences -L.SVG.include(!L.Browser.vml ? {} : { + // @section + // @aka CircleMarker options + options: { + fill: true, - _initContainer: function () { - this._container = L.DomUtil.create('div', 'leaflet-vml-container'); + // @option radius: Number = 10 + // Radius of the circle marker, in pixels + radius: 10 }, - _update: function () { - if (this._map._animatingZoom) { return; } - L.Renderer.prototype._update.call(this); - this.fire('update'); + initialize: function (latlng, options) { + setOptions(this, options); + this._latlng = toLatLng(latlng); + this._radius = this.options.radius; }, - _initPath: function (layer) { - var container = layer._container = L.SVG.create('shape'); - - L.DomUtil.addClass(container, 'leaflet-vml-shape ' + (this.options.className || '')); - - container.coordsize = '1 1'; - - layer._path = L.SVG.create('path'); - container.appendChild(layer._path); - - this._updateStyle(layer); - this._layers[L.stamp(layer)] = layer; + // @method setLatLng(latLng: LatLng): this + // Sets the position of a circle marker to a new location. + setLatLng: function (latlng) { + this._latlng = toLatLng(latlng); + this.redraw(); + return this.fire('move', {latlng: this._latlng}); }, - _addPath: function (layer) { - var container = layer._container; - this._container.appendChild(container); - - if (layer.options.interactive) { - layer.addInteractiveTarget(container); - } + // @method getLatLng(): LatLng + // Returns the current geographical position of the circle marker + getLatLng: function () { + return this._latlng; }, - _removePath: function (layer) { - var container = layer._container; - L.DomUtil.remove(container); - layer.removeInteractiveTarget(container); - delete this._layers[L.stamp(layer)]; + // @method setRadius(radius: Number): this + // Sets the radius of a circle marker. Units are in pixels. + setRadius: function (radius) { + this.options.radius = this._radius = radius; + return this.redraw(); }, - _updateStyle: function (layer) { - var stroke = layer._stroke, - fill = layer._fill, - options = layer.options, - container = layer._container; - - container.stroked = !!options.stroke; - container.filled = !!options.fill; - - if (options.stroke) { - if (!stroke) { - stroke = layer._stroke = L.SVG.create('stroke'); - } - container.appendChild(stroke); - stroke.weight = options.weight + 'px'; - stroke.color = options.color; - stroke.opacity = options.opacity; - - if (options.dashArray) { - stroke.dashStyle = L.Util.isArray(options.dashArray) ? - options.dashArray.join(' ') : - options.dashArray.replace(/( *, *)/g, ' '); - } else { - stroke.dashStyle = ''; - } - stroke.endcap = options.lineCap.replace('butt', 'flat'); - stroke.joinstyle = options.lineJoin; - - } else if (stroke) { - container.removeChild(stroke); - layer._stroke = null; - } + // @method getRadius(): Number + // Returns the current radius of the circle + getRadius: function () { + return this._radius; + }, - if (options.fill) { - if (!fill) { - fill = layer._fill = L.SVG.create('fill'); - } - container.appendChild(fill); - fill.color = options.fillColor || options.color; - fill.opacity = options.fillOpacity; + setStyle : function (options) { + var radius = options && options.radius || this._radius; + Path.prototype.setStyle.call(this, options); + this.setRadius(radius); + return this; + }, - } else if (fill) { - container.removeChild(fill); - layer._fill = null; - } + _project: function () { + this._point = this._map.latLngToLayerPoint(this._latlng); + this._updateBounds(); }, - _updateCircle: function (layer) { - var p = layer._point.round(), - r = Math.round(layer._radius), - r2 = Math.round(layer._radiusY || r); + _updateBounds: function () { + var r = this._radius, + r2 = this._radiusY || r, + w = this._clickTolerance(), + p = [r + w, r2 + w]; + this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p)); + }, - this._setPath(layer, layer._empty() ? 'M0 0' : - 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360)); + _update: function () { + if (this._map) { + this._updatePath(); + } }, - _setPath: function (layer, path) { - layer._path.v = path; + _updatePath: function () { + this._renderer._updateCircle(this); }, - _bringToFront: function (layer) { - L.DomUtil.toFront(layer._container); + _empty: function () { + return this._radius && !this._renderer._bounds.intersects(this._pxBounds); }, - _bringToBack: function (layer) { - L.DomUtil.toBack(layer._container); + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); } }); -if (L.Browser.vml) { - L.SVG.create = (function () { - try { - document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml'); - return function (name) { - return document.createElement(''); - }; - } catch (e) { - return function (name) { - return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">'); - }; - } - })(); -} - +// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) +// Instantiates a circle marker object given a geographical point, and an optional options object. +function circleMarker(latlng, options) { + return new CircleMarker(latlng, options); +} /* - * @class Canvas - * @inherits Renderer - * @aka L.Canvas + * @class Circle + * @aka L.Circle + * @inherits CircleMarker * - * Allows vector layers to be displayed with [``](https://developer.mozilla.org/docs/Web/API/Canvas_API). - * Inherits `Renderer`. + * A class for drawing circle overlays on a map. Extends `CircleMarker`. * - * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not - * available in all web browsers, notably IE8, and overlapping geometries might - * not display properly in some edge cases. + * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). * * @example * - * Use Canvas by default for all paths in the map: - * - * ```js - * var map = L.map('map', { - * renderer: L.canvas() - * }); - * ``` - * - * Use a Canvas renderer with extra padding for specific vector geometries: - * * ```js - * var map = L.map('map'); - * var myRenderer = L.canvas({ padding: 0.5 }); - * var line = L.polyline( coordinates, { renderer: myRenderer } ); - * var circle = L.circle( center, { renderer: myRenderer } ); + * L.circle([50.5, 30.5], {radius: 200}).addTo(map); * ``` */ -L.Canvas = L.Renderer.extend({ - getEvents: function () { - var events = L.Renderer.prototype.getEvents.call(this); - events.viewprereset = this._onViewPreReset; - return events; - }, +var Circle = CircleMarker.extend({ - _onViewPreReset: function () { - // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once - this._postponeUpdatePaths = true; - }, + initialize: function (latlng, options, legacyOptions) { + if (typeof options === 'number') { + // Backwards compatibility with 0.7.x factory (latlng, radius, options?) + options = extend({}, legacyOptions, {radius: options}); + } + setOptions(this, options); + this._latlng = toLatLng(latlng); - onAdd: function () { - L.Renderer.prototype.onAdd.call(this); + if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } - // Redraw vectors since canvas is cleared upon removal, - // in case of removing the renderer itself from the map. - this._draw(); + // @section + // @aka Circle options + // @option radius: Number; Radius of the circle, in meters. + this._mRadius = this.options.radius; }, - _initContainer: function () { - var container = this._container = document.createElement('canvas'); - - L.DomEvent - .on(container, 'mousemove', L.Util.throttle(this._onMouseMove, 32, this), this) - .on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this) - .on(container, 'mouseout', this._handleMouseOut, this); + // @method setRadius(radius: Number): this + // Sets the radius of a circle. Units are in meters. + setRadius: function (radius) { + this._mRadius = radius; + return this.redraw(); + }, - this._ctx = container.getContext('2d'); + // @method getRadius(): Number + // Returns the current radius of a circle. Units are in meters. + getRadius: function () { + return this._mRadius; }, - _updatePaths: function () { - if (this._postponeUpdatePaths) { return; } + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + var half = [this._radius, this._radiusY || this._radius]; - var layer; - this._redrawBounds = null; - for (var id in this._layers) { - layer = this._layers[id]; - layer._update(); - } - this._redraw(); + return new LatLngBounds( + this._map.layerPointToLatLng(this._point.subtract(half)), + this._map.layerPointToLatLng(this._point.add(half))); }, - _update: function () { - if (this._map._animatingZoom && this._bounds) { return; } + setStyle: Path.prototype.setStyle, - this._drawnLayers = {}; + _project: function () { + + var lng = this._latlng.lng, + lat = this._latlng.lat, + map = this._map, + crs = map.options.crs; - L.Renderer.prototype._update.call(this); + if (crs.distance === Earth.distance) { + var d = Math.PI / 180, + latR = (this._mRadius / Earth.R) / d, + top = map.project([lat + latR, lng]), + bottom = map.project([lat - latR, lng]), + p = top.add(bottom).divideBy(2), + lat2 = map.unproject(p).lat, + lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / + (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; - var b = this._bounds, - container = this._container, - size = b.getSize(), - m = L.Browser.retina ? 2 : 1; + if (isNaN(lngR) || lngR === 0) { + lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 + } - L.DomUtil.setPosition(container, b.min); + this._point = p.subtract(map.getPixelOrigin()); + this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1); + this._radiusY = Math.max(Math.round(p.y - top.y), 1); - // set canvas size (also clearing it); use double size on retina - container.width = m * size.x; - container.height = m * size.y; - container.style.width = size.x + 'px'; - container.style.height = size.y + 'px'; + } else { + var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); - if (L.Browser.retina) { - this._ctx.scale(2, 2); + this._point = map.latLngToLayerPoint(this._latlng); + this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; } - // translate so we use the same path coordinates after canvas element moves - this._ctx.translate(-b.min.x, -b.min.y); + this._updateBounds(); + } +}); - // Tell paths to redraw themselves - this.fire('update'); - }, +// @factory L.circle(latlng: LatLng, options?: Circle options) +// Instantiates a circle object given a geographical point, and an options object +// which contains the circle radius. +// @alternative +// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) +// Obsolete way of instantiating a circle, for compatibility with 0.7.x code. +// Do not use in new applications or plugins. +function circle(latlng, options, legacyOptions) { + return new Circle(latlng, options, legacyOptions); +} - _reset: function () { - L.Renderer.prototype._reset.call(this); +/* + * @class Polyline + * @aka L.Polyline + * @inherits Path + * + * A class for drawing polyline overlays on a map. Extends `Path`. + * + * @example + * + * ```js + * // create a red polyline from an array of LatLng points + * var latlngs = [ + * [45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2] + * ]; + * + * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polyline + * map.fitBounds(polyline.getBounds()); + * ``` + * + * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape: + * + * ```js + * // create a red polyline from an array of arrays of LatLng points + * var latlngs = [ + * [[45.51, -122.68], + * [37.77, -122.43], + * [34.04, -118.2]], + * [[40.78, -73.91], + * [41.83, -87.62], + * [32.76, -96.72]] + * ]; + * ``` + */ - if (this._postponeUpdatePaths) { - this._postponeUpdatePaths = false; - this._updatePaths(); - } - }, - _initPath: function (layer) { - this._updateDashArray(layer); - this._layers[L.stamp(layer)] = layer; +var Polyline = Path.extend({ - var order = layer._order = { - layer: layer, - prev: this._drawLast, - next: null - }; - if (this._drawLast) { this._drawLast.next = order; } - this._drawLast = order; - this._drawFirst = this._drawFirst || this._drawLast; + // @section + // @aka Polyline options + options: { + // @option smoothFactor: Number = 1.0 + // How much to simplify the polyline on each zoom level. More means + // better performance and smoother look, and less means more accurate representation. + smoothFactor: 1.0, + + // @option noClip: Boolean = false + // Disable polyline clipping. + noClip: false }, - _addPath: function (layer) { - this._requestRedraw(layer); + initialize: function (latlngs, options) { + setOptions(this, options); + this._setLatLngs(latlngs); }, - _removePath: function (layer) { - var order = layer._order; - var next = order.next; - var prev = order.prev; + // @method getLatLngs(): LatLng[] + // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. + getLatLngs: function () { + return this._latlngs; + }, - if (next) { - next.prev = prev; - } else { - this._drawLast = prev; - } - if (prev) { - prev.next = next; - } else { - this._drawFirst = next; - } + // @method setLatLngs(latlngs: LatLng[]): this + // Replaces all the points in the polyline with the given array of geographical points. + setLatLngs: function (latlngs) { + this._setLatLngs(latlngs); + return this.redraw(); + }, - delete layer._order; + // @method isEmpty(): Boolean + // Returns `true` if the Polyline has no LatLngs. + isEmpty: function () { + return !this._latlngs.length; + }, - delete this._layers[L.stamp(layer)]; + closestLayerPoint: function (p) { + var minDistance = Infinity, + minPoint = null, + closest = _sqClosestPointOnSegment, + p1, p2; - this._requestRedraw(layer); - }, + for (var j = 0, jLen = this._parts.length; j < jLen; j++) { + var points = this._parts[j]; - _updatePath: function (layer) { - // Redraw the union of the layer's old pixel - // bounds and the new pixel bounds. - this._extendRedrawBounds(layer); - layer._project(); - layer._update(); - // The redraw will extend the redraw bounds - // with the new pixel bounds. - this._requestRedraw(layer); - }, + for (var i = 1, len = points.length; i < len; i++) { + p1 = points[i - 1]; + p2 = points[i]; - _updateStyle: function (layer) { - this._updateDashArray(layer); - this._requestRedraw(layer); - }, + var sqDist = closest(p, p1, p2, true); - _updateDashArray: function (layer) { - if (layer.options.dashArray) { - var parts = layer.options.dashArray.split(','), - dashArray = [], - i; - for (i = 0; i < parts.length; i++) { - dashArray.push(Number(parts[i])); + if (sqDist < minDistance) { + minDistance = sqDist; + minPoint = closest(p, p1, p2); + } } - layer.options._dashArray = dashArray; } + if (minPoint) { + minPoint.distance = Math.sqrt(minDistance); + } + return minPoint; }, - _requestRedraw: function (layer) { - if (!this._map) { return; } + // @method getCenter(): LatLng + // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline. + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); + } - this._extendRedrawBounds(layer); - this._redrawRequest = this._redrawRequest || L.Util.requestAnimFrame(this._redraw, this); - }, + var i, halfDist, segDist, dist, p1, p2, ratio, + points = this._rings[0], + len = points.length; - _extendRedrawBounds: function (layer) { - var padding = (layer.options.weight || 0) + 1; - this._redrawBounds = this._redrawBounds || new L.Bounds(); - this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding])); - this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding])); - }, + if (!len) { return null; } - _redraw: function () { - this._redrawRequest = null; + // polyline centroid algorithm; only uses the first ring if there are multiple - if (this._redrawBounds) { - this._redrawBounds.min._floor(); - this._redrawBounds.max._ceil(); + for (i = 0, halfDist = 0; i < len - 1; i++) { + halfDist += points[i].distanceTo(points[i + 1]) / 2; } - this._clear(); // clear layers in redraw bounds - this._draw(); // draw layers + // The line is so small in the current view that all points are on the same pixel. + if (halfDist === 0) { + return this._map.layerPointToLatLng(points[0]); + } - this._redrawBounds = null; - }, + for (i = 0, dist = 0; i < len - 1; i++) { + p1 = points[i]; + p2 = points[i + 1]; + segDist = p1.distanceTo(p2); + dist += segDist; - _clear: function () { - var bounds = this._redrawBounds; - if (bounds) { - var size = bounds.getSize(); - this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y); - } else { - this._ctx.clearRect(0, 0, this._container.width, this._container.height); + if (dist > halfDist) { + ratio = (dist - halfDist) / segDist; + return this._map.layerPointToLatLng([ + p2.x - ratio * (p2.x - p1.x), + p2.y - ratio * (p2.y - p1.y) + ]); + } } }, - _draw: function () { - var layer, bounds = this._redrawBounds; - this._ctx.save(); - if (bounds) { - var size = bounds.getSize(); - this._ctx.beginPath(); - this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y); - this._ctx.clip(); - } - - this._drawing = true; + // @method getBounds(): LatLngBounds + // Returns the `LatLngBounds` of the path. + getBounds: function () { + return this._bounds; + }, - for (var order = this._drawFirst; order; order = order.next) { - layer = order.layer; - if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) { - layer._updatePath(); - } - } + // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this + // Adds a given point to the polyline. By default, adds to the first ring of + // the polyline in case of a multi-polyline, but can be overridden by passing + // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). + addLatLng: function (latlng, latlngs) { + latlngs = latlngs || this._defaultShape(); + latlng = toLatLng(latlng); + latlngs.push(latlng); + this._bounds.extend(latlng); + return this.redraw(); + }, - this._drawing = false; + _setLatLngs: function (latlngs) { + this._bounds = new LatLngBounds(); + this._latlngs = this._convertLatLngs(latlngs); + }, - this._ctx.restore(); // Restore state before clipping. + _defaultShape: function () { + return _flat(this._latlngs) ? this._latlngs : this._latlngs[0]; }, - _updatePoly: function (layer, closed) { - if (!this._drawing) { return; } + // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way + _convertLatLngs: function (latlngs) { + var result = [], + flat = _flat(latlngs); - var i, j, len2, p, - parts = layer._parts, - len = parts.length, - ctx = this._ctx; + for (var i = 0, len = latlngs.length; i < len; i++) { + if (flat) { + result[i] = toLatLng(latlngs[i]); + this._bounds.extend(result[i]); + } else { + result[i] = this._convertLatLngs(latlngs[i]); + } + } - if (!len) { return; } + return result; + }, - this._drawnLayers[layer._leaflet_id] = layer; + _project: function () { + var pxBounds = new Bounds(); + this._rings = []; + this._projectLatlngs(this._latlngs, this._rings, pxBounds); - ctx.beginPath(); + var w = this._clickTolerance(), + p = new Point(w, w); - if (ctx.setLineDash) { - ctx.setLineDash(layer.options && layer.options._dashArray || []); + if (this._bounds.isValid() && pxBounds.isValid()) { + pxBounds.min._subtract(p); + pxBounds.max._add(p); + this._pxBounds = pxBounds; } + }, - for (i = 0; i < len; i++) { - for (j = 0, len2 = parts[i].length; j < len2; j++) { - p = parts[i][j]; - ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y); + // recursively turns latlngs into a set of rings with projected coordinates + _projectLatlngs: function (latlngs, result, projectedBounds) { + var flat = latlngs[0] instanceof LatLng, + len = latlngs.length, + i, ring; + + if (flat) { + ring = []; + for (i = 0; i < len; i++) { + ring[i] = this._map.latLngToLayerPoint(latlngs[i]); + projectedBounds.extend(ring[i]); } - if (closed) { - ctx.closePath(); + result.push(ring); + } else { + for (i = 0; i < len; i++) { + this._projectLatlngs(latlngs[i], result, projectedBounds); } } - - this._fillStroke(ctx, layer); - - // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature }, - _updateCircle: function (layer) { - - if (!this._drawing || layer._empty()) { return; } - - var p = layer._point, - ctx = this._ctx, - r = layer._radius, - s = (layer._radiusY || r) / r; + // clip polyline by renderer bounds so that we have less to render for performance + _clipPoints: function () { + var bounds = this._renderer._bounds; - this._drawnLayers[layer._leaflet_id] = layer; + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } - if (s !== 1) { - ctx.save(); - ctx.scale(1, s); + if (this.options.noClip) { + this._parts = this._rings; + return; } - ctx.beginPath(); - ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false); + var parts = this._parts, + i, j, k, len, len2, segment, points; - if (s !== 1) { - ctx.restore(); - } + for (i = 0, k = 0, len = this._rings.length; i < len; i++) { + points = this._rings[i]; - this._fillStroke(ctx, layer); - }, + for (j = 0, len2 = points.length; j < len2 - 1; j++) { + segment = clipSegment(points[j], points[j + 1], bounds, j, true); - _fillStroke: function (ctx, layer) { - var options = layer.options; + if (!segment) { continue; } - if (options.fill) { - ctx.globalAlpha = options.fillOpacity; - ctx.fillStyle = options.fillColor || options.color; - ctx.fill(options.fillRule || 'evenodd'); - } + parts[k] = parts[k] || []; + parts[k].push(segment[0]); - if (options.stroke && options.weight !== 0) { - ctx.globalAlpha = options.opacity; - ctx.lineWidth = options.weight; - ctx.strokeStyle = options.color; - ctx.lineCap = options.lineCap; - ctx.lineJoin = options.lineJoin; - ctx.stroke(); + // if segment goes out of screen, or it's the last one, it's the end of the line part + if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { + parts[k].push(segment[1]); + k++; + } + } } }, - // Canvas obviously doesn't have mouse events for individual drawn objects, - // so we emulate that by calculating what's under the mouse on mousemove/click manually - - _onClick: function (e) { - var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer; + // simplify each clipped part of the polyline for performance + _simplifyPoints: function () { + var parts = this._parts, + tolerance = this.options.smoothFactor; - for (var order = this._drawFirst; order; order = order.next) { - layer = order.layer; - if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) { - clickedLayer = layer; - } - } - if (clickedLayer) { - L.DomEvent._fakeStop(e); - this._fireEvent([clickedLayer], e); + for (var i = 0, len = parts.length; i < len; i++) { + parts[i] = simplify(parts[i], tolerance); } }, - _onMouseMove: function (e) { - if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; } + _update: function () { + if (!this._map) { return; } - var point = this._map.mouseEventToLayerPoint(e); - this._handleMouseHover(e, point); + this._clipPoints(); + this._simplifyPoints(); + this._updatePath(); }, - - _handleMouseOut: function (e) { - var layer = this._hoveredLayer; - if (layer) { - // if we're leaving the layer, fire mouseout - L.DomUtil.removeClass(this._container, 'leaflet-interactive'); - this._fireEvent([layer], e, 'mouseout'); - this._hoveredLayer = null; - } + _updatePath: function () { + this._renderer._updatePoly(this); }, - _handleMouseHover: function (e, point) { - var layer, candidateHoveredLayer; + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p, closed) { + var i, j, k, len, len2, part, + w = this._clickTolerance(); - for (var order = this._drawFirst; order; order = order.next) { - layer = order.layer; - if (layer.options.interactive && layer._containsPoint(point)) { - candidateHoveredLayer = layer; - } - } + if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } - if (candidateHoveredLayer !== this._hoveredLayer) { - this._handleMouseOut(e); + // hit detection for polylines + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; - if (candidateHoveredLayer) { - L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor - this._fireEvent([candidateHoveredLayer], e, 'mouseover'); - this._hoveredLayer = candidateHoveredLayer; + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + if (!closed && (j === 0)) { continue; } + + if (pointToSegmentDistance(p, part[k], part[j]) <= w) { + return true; + } } } + return false; + } +}); - if (this._hoveredLayer) { - this._fireEvent([this._hoveredLayer], e); - } - }, +// @factory L.polyline(latlngs: LatLng[], options?: Polyline options) +// Instantiates a polyline object given an array of geographical points and +// optionally an options object. You can create a `Polyline` object with +// multiple separate lines (`MultiPolyline`) by passing an array of arrays +// of geographic points. +function polyline(latlngs, options) { + return new Polyline(latlngs, options); +} - _fireEvent: function (layers, e, type) { - this._map._fireDOMEvent(e, type || e.type, layers); +/* + * @class Polygon + * @aka L.Polygon + * @inherits Polyline + * + * A class for drawing polygon overlays on a map. Extends `Polyline`. + * + * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points. + * + * + * @example + * + * ```js + * // create a red polygon from an array of LatLng points + * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]; + * + * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map); + * + * // zoom the map to the polygon + * map.fitBounds(polygon.getBounds()); + * ``` + * + * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape: + * + * ```js + * var latlngs = [ + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ]; + * ``` + * + * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape. + * + * ```js + * var latlngs = [ + * [ // first polygon + * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring + * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole + * ], + * [ // second polygon + * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]] + * ] + * ]; + * ``` + */ + +var Polygon = Polyline.extend({ + + options: { + fill: true }, - _bringToFront: function (layer) { - var order = layer._order; - var next = order.next; - var prev = order.prev; + isEmpty: function () { + return !this._latlngs.length || !this._latlngs[0].length; + }, - if (next) { - next.prev = prev; - } else { - // Already last - return; - } - if (prev) { - prev.next = next; - } else if (next) { - // Update first entry unless this is the - // signle entry - this._drawFirst = next; + getCenter: function () { + // throws error when not yet added to map as this center calculation requires projected coordinates + if (!this._map) { + throw new Error('Must add layer to map before using getCenter()'); } - order.prev = this._drawLast; - this._drawLast.next = order; + var i, j, p1, p2, f, area, x, y, center, + points = this._rings[0], + len = points.length; + + if (!len) { return null; } - order.next = null; - this._drawLast = order; + // polygon centroid algorithm; only uses the first ring if there are multiple - this._requestRedraw(layer); - }, + area = x = y = 0; - _bringToBack: function (layer) { - var order = layer._order; - var next = order.next; - var prev = order.prev; + for (i = 0, j = len - 1; i < len; j = i++) { + p1 = points[i]; + p2 = points[j]; - if (prev) { - prev.next = next; - } else { - // Already first - return; + f = p1.y * p2.x - p2.y * p1.x; + x += (p1.x + p2.x) * f; + y += (p1.y + p2.y) * f; + area += f * 3; } - if (next) { - next.prev = prev; - } else if (prev) { - // Update last entry unless this is the - // signle entry - this._drawLast = prev; + + if (area === 0) { + // Polygon is so small that all points are on same pixel. + center = points[0]; + } else { + center = [x / area, y / area]; } + return this._map.layerPointToLatLng(center); + }, - order.prev = null; + _convertLatLngs: function (latlngs) { + var result = Polyline.prototype._convertLatLngs.call(this, latlngs), + len = result.length; - order.next = this._drawFirst; - this._drawFirst.prev = order; - this._drawFirst = order; + // remove last point if it equals first one + if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) { + result.pop(); + } + return result; + }, - this._requestRedraw(layer); - } -}); + _setLatLngs: function (latlngs) { + Polyline.prototype._setLatLngs.call(this, latlngs); + if (_flat(this._latlngs)) { + this._latlngs = [this._latlngs]; + } + }, -// @namespace Browser; @property canvas: Boolean -// `true` when the browser supports [``](https://developer.mozilla.org/docs/Web/API/Canvas_API). -L.Browser.canvas = (function () { - return !!document.createElement('canvas').getContext; -}()); + _defaultShape: function () { + return _flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; + }, -// @namespace Canvas -// @factory L.canvas(options?: Renderer options) -// Creates a Canvas renderer with the given options. -L.canvas = function (options) { - return L.Browser.canvas ? new L.Canvas(options) : null; -}; + _clipPoints: function () { + // polygons need a different clipping algorithm so we redefine that -L.Polyline.prototype._containsPoint = function (p, closed) { - var i, j, k, len, len2, part, - w = this._clickTolerance(); + var bounds = this._renderer._bounds, + w = this.options.weight, + p = new Point(w, w); - if (!this._pxBounds.contains(p)) { return false; } + // increase clip padding by stroke width to avoid stroke on clip edges + bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p)); - // hit detection for polylines - for (i = 0, len = this._parts.length; i < len; i++) { - part = this._parts[i]; + this._parts = []; + if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { + return; + } - for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { - if (!closed && (j === 0)) { continue; } + if (this.options.noClip) { + this._parts = this._rings; + return; + } - if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) { - return true; + for (var i = 0, len = this._rings.length, clipped; i < len; i++) { + clipped = clipPolygon(this._rings[i], bounds, true); + if (clipped.length) { + this._parts.push(clipped); } } - } - return false; -}; + }, + + _updatePath: function () { + this._renderer._updatePoly(this, true); + }, -L.Polygon.prototype._containsPoint = function (p) { - var inside = false, - part, p1, p2, i, j, k, len, len2; + // Needed by the `Canvas` renderer for interactivity + _containsPoint: function (p) { + var inside = false, + part, p1, p2, i, j, k, len, len2; - if (!this._pxBounds.contains(p)) { return false; } + if (!this._pxBounds.contains(p)) { return false; } - // ray casting algorithm for detecting if point is in polygon - for (i = 0, len = this._parts.length; i < len; i++) { - part = this._parts[i]; + // ray casting algorithm for detecting if point is in polygon + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; - for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { - p1 = part[j]; - p2 = part[k]; + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + p1 = part[j]; + p2 = part[k]; - if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { - inside = !inside; + if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { + inside = !inside; + } } } + + // also check if it's on polygon stroke + return inside || Polyline.prototype._containsPoint.call(this, p, true); } - // also check if it's on polygon stroke - return inside || L.Polyline.prototype._containsPoint.call(this, p, true); -}; +}); + + +// @factory L.polygon(latlngs: LatLng[], options?: Polyline options) +function polygon(latlngs, options) { + return new Polygon(latlngs, options); +} + +/* + * @class GeoJSON + * @aka L.GeoJSON + * @inherits FeatureGroup + * + * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse + * GeoJSON data and display it on the map. Extends `FeatureGroup`. + * + * @example + * + * ```js + * L.geoJSON(data, { + * style: function (feature) { + * return {color: feature.properties.color}; + * } + * }).bindPopup(function (layer) { + * return layer.feature.properties.description; + * }).addTo(map); + * ``` + */ + +var GeoJSON = FeatureGroup.extend({ + + /* @section + * @aka GeoJSON options + * + * @option pointToLayer: Function = * + * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally + * called when data is added, passing the GeoJSON point feature and its `LatLng`. + * The default is to spawn a default `Marker`: + * ```js + * function(geoJsonPoint, latlng) { + * return L.marker(latlng); + * } + * ``` + * + * @option style: Function = * + * A `Function` defining the `Path options` for styling GeoJSON lines and polygons, + * called internally when data is added. + * The default value is to not override any defaults: + * ```js + * function (geoJsonFeature) { + * return {} + * } + * ``` + * + * @option onEachFeature: Function = * + * A `Function` that will be called once for each created `Feature`, after it has + * been created and styled. Useful for attaching events and popups to features. + * The default is to do nothing with the newly created layers: + * ```js + * function (feature, layer) {} + * ``` + * + * @option filter: Function = * + * A `Function` that will be used to decide whether to include a feature or not. + * The default is to include all features: + * ```js + * function (geoJsonFeature) { + * return true; + * } + * ``` + * Note: dynamically changing the `filter` option will have effect only on newly + * added data. It will _not_ re-evaluate already included features. + * + * @option coordsToLatLng: Function = * + * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s. + * The default is the `coordsToLatLng` static method. + */ + + initialize: function (geojson, options) { + setOptions(this, options); + + this._layers = {}; + + if (geojson) { + this.addData(geojson); + } + }, + + // @method addData( data ): this + // Adds a GeoJSON object to the layer. + addData: function (geojson) { + var features = isArray(geojson) ? geojson : geojson.features, + i, len, feature; + + if (features) { + for (i = 0, len = features.length; i < len; i++) { + // only add this if geometry or geometries are set and not null + feature = features[i]; + if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { + this.addData(feature); + } + } + return this; + } + + var options = this.options; + + if (options.filter && !options.filter(geojson)) { return this; } + + var layer = geometryToLayer(geojson, options); + if (!layer) { + return this; + } + layer.feature = asFeature(geojson); + + layer.defaultOptions = layer.options; + this.resetStyle(layer); + + if (options.onEachFeature) { + options.onEachFeature(geojson, layer); + } + + return this.addLayer(layer); + }, + + // @method resetStyle( layer ): this + // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events. + resetStyle: function (layer) { + // reset any custom styles + layer.options = extend({}, layer.defaultOptions); + this._setLayerStyle(layer, this.options.style); + return this; + }, + + // @method setStyle( style ): this + // Changes styles of GeoJSON vector layers with the given style function. + setStyle: function (style) { + return this.eachLayer(function (layer) { + this._setLayerStyle(layer, style); + }, this); + }, + + _setLayerStyle: function (layer, style) { + if (typeof style === 'function') { + style = style(layer.feature); + } + if (layer.setStyle) { + layer.setStyle(style); + } + } +}); + +// @section +// There are several static functions which can be called without instantiating L.GeoJSON: + +// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer +// Creates a `Layer` from a given GeoJSON feature. Can use a custom +// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng) +// functions if provided as options. +function geometryToLayer(geojson, options) { + + var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, + coords = geometry ? geometry.coordinates : null, + layers = [], + pointToLayer = options && options.pointToLayer, + _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng, + latlng, latlngs, i, len; + + if (!coords && !geometry) { + return null; + } + + switch (geometry.type) { + case 'Point': + latlng = _coordsToLatLng(coords); + return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng); + + case 'MultiPoint': + for (i = 0, len = coords.length; i < len; i++) { + latlng = _coordsToLatLng(coords[i]); + layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng)); + } + return new FeatureGroup(layers); + + case 'LineString': + case 'MultiLineString': + latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng); + return new Polyline(latlngs, options); + + case 'Polygon': + case 'MultiPolygon': + latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng); + return new Polygon(latlngs, options); + + case 'GeometryCollection': + for (i = 0, len = geometry.geometries.length; i < len; i++) { + var layer = geometryToLayer({ + geometry: geometry.geometries[i], + type: 'Feature', + properties: geojson.properties + }, options); + + if (layer) { + layers.push(layer); + } + } + return new FeatureGroup(layers); + + default: + throw new Error('Invalid GeoJSON object.'); + } +} + +// @function coordsToLatLng(coords: Array): LatLng +// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude) +// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points. +function coordsToLatLng(coords) { + return new LatLng(coords[1], coords[0], coords[2]); +} + +// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array +// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array. +// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default). +// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function. +function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) { + var latlngs = []; + + for (var i = 0, len = coords.length, latlng; i < len; i++) { + latlng = levelsDeep ? + coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) : + (_coordsToLatLng || coordsToLatLng)(coords[i]); + + latlngs.push(latlng); + } + + return latlngs; +} + +// @function latLngToCoords(latlng: LatLng, precision?: Number): Array +// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng) +function latLngToCoords(latlng, precision) { + precision = typeof precision === 'number' ? precision : 6; + return latlng.alt !== undefined ? + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] : + [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)]; +} + +// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array +// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs) +// `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default. +function latLngsToCoords(latlngs, levelsDeep, closed, precision) { + var coords = []; + + for (var i = 0, len = latlngs.length; i < len; i++) { + coords.push(levelsDeep ? + latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) : + latLngToCoords(latlngs[i], precision)); + } + + if (!levelsDeep && closed) { + coords.push(coords[0]); + } + + return coords; +} + +function getFeature(layer, newGeometry) { + return layer.feature ? + extend({}, layer.feature, {geometry: newGeometry}) : + asFeature(newGeometry); +} + +// @function asFeature(geojson: Object): Object +// Normalize GeoJSON geometries/features into GeoJSON features. +function asFeature(geojson) { + if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') { + return geojson; + } + + return { + type: 'Feature', + properties: {}, + geometry: geojson + }; +} + +var PointToGeoJSON = { + toGeoJSON: function (precision) { + return getFeature(this, { + type: 'Point', + coordinates: latLngToCoords(this.getLatLng(), precision) + }); + } +}; + +// @namespace Marker +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature). +Marker.include(PointToGeoJSON); + +// @namespace CircleMarker +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature). +Circle.include(PointToGeoJSON); +CircleMarker.include(PointToGeoJSON); + + +// @namespace Polyline +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature). +Polyline.include({ + toGeoJSON: function (precision) { + var multi = !_flat(this._latlngs); + + var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision); + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'LineString', + coordinates: coords + }); + } +}); + +// @namespace Polygon +// @method toGeoJSON(): Object +// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature). +Polygon.include({ + toGeoJSON: function (precision) { + var holes = !_flat(this._latlngs), + multi = holes && !_flat(this._latlngs[0]); + + var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision); + + if (!holes) { + coords = [coords]; + } + + return getFeature(this, { + type: (multi ? 'Multi' : '') + 'Polygon', + coordinates: coords + }); + } +}); + + +// @namespace LayerGroup +LayerGroup.include({ + toMultiPoint: function (precision) { + var coords = []; + + this.eachLayer(function (layer) { + coords.push(layer.toGeoJSON(precision).geometry.coordinates); + }); + + return getFeature(this, { + type: 'MultiPoint', + coordinates: coords + }); + }, + + // @method toGeoJSON(): Object + // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`). + toGeoJSON: function (precision) { + + var type = this.feature && this.feature.geometry && this.feature.geometry.type; + + if (type === 'MultiPoint') { + return this.toMultiPoint(precision); + } + + var isGeometryCollection = type === 'GeometryCollection', + jsons = []; + + this.eachLayer(function (layer) { + if (layer.toGeoJSON) { + var json = layer.toGeoJSON(precision); + if (isGeometryCollection) { + jsons.push(json.geometry); + } else { + var feature = asFeature(json); + // Squash nested feature collections + if (feature.type === 'FeatureCollection') { + jsons.push.apply(jsons, feature.features); + } else { + jsons.push(feature); + } + } + } + }); + + if (isGeometryCollection) { + return getFeature(this, { + geometries: jsons, + type: 'GeometryCollection' + }); + } + + return { + type: 'FeatureCollection', + features: jsons + }; + } +}); + +// @namespace GeoJSON +// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options) +// Creates a GeoJSON layer. Optionally accepts an object in +// [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map +// (you can alternatively add it later with `addData` method) and an `options` object. +function geoJSON(geojson, options) { + return new GeoJSON(geojson, options); +} + +// Backward compatibility. +var geoJson = geoJSON; -L.CircleMarker.prototype._containsPoint = function (p) { - return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); +/* + * @class ImageOverlay + * @aka L.ImageOverlay + * @inherits Interactive layer + * + * Used to load and display a single image over specific bounds of the map. Extends `Layer`. + * + * @example + * + * ```js + * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg', + * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]]; + * L.imageOverlay(imageUrl, imageBounds).addTo(map); + * ``` + */ + +var ImageOverlay = Layer.extend({ + + // @section + // @aka ImageOverlay options + options: { + // @option opacity: Number = 1.0 + // The opacity of the image overlay. + opacity: 1, + + // @option alt: String = '' + // Text for the `alt` attribute of the image (useful for accessibility). + alt: '', + + // @option interactive: Boolean = false + // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. + interactive: false, + + // @option crossOrigin: Boolean = false + // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data. + crossOrigin: false, + + // @option errorOverlayUrl: String = '' + // URL to the overlay image to show in place of the overlay that failed to load. + errorOverlayUrl: '', + + // @option zIndex: Number = 1 + // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the tile layer. + zIndex: 1, + + // @option className: String = '' + // A custom class name to assign to the image. Empty by default. + className: '', + }, + + initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) + this._url = url; + this._bounds = toLatLngBounds(bounds); + + setOptions(this, options); + }, + + onAdd: function () { + if (!this._image) { + this._initImage(); + + if (this.options.opacity < 1) { + this._updateOpacity(); + } + } + + if (this.options.interactive) { + addClass(this._image, 'leaflet-interactive'); + this.addInteractiveTarget(this._image); + } + + this.getPane().appendChild(this._image); + this._reset(); + }, + + onRemove: function () { + remove(this._image); + if (this.options.interactive) { + this.removeInteractiveTarget(this._image); + } + }, + + // @method setOpacity(opacity: Number): this + // Sets the opacity of the overlay. + setOpacity: function (opacity) { + this.options.opacity = opacity; + + if (this._image) { + this._updateOpacity(); + } + return this; + }, + + setStyle: function (styleOpts) { + if (styleOpts.opacity) { + this.setOpacity(styleOpts.opacity); + } + return this; + }, + + // @method bringToFront(): this + // Brings the layer to the top of all overlays. + bringToFront: function () { + if (this._map) { + toFront(this._image); + } + return this; + }, + + // @method bringToBack(): this + // Brings the layer to the bottom of all overlays. + bringToBack: function () { + if (this._map) { + toBack(this._image); + } + return this; + }, + + // @method setUrl(url: String): this + // Changes the URL of the image. + setUrl: function (url) { + this._url = url; + + if (this._image) { + this._image.src = url; + } + return this; + }, + + // @method setBounds(bounds: LatLngBounds): this + // Update the bounds that this ImageOverlay covers + setBounds: function (bounds) { + this._bounds = bounds; + + if (this._map) { + this._reset(); + } + return this; + }, + + getEvents: function () { + var events = { + zoom: this._reset, + viewreset: this._reset + }; + + if (this._zoomAnimated) { + events.zoomanim = this._animateZoom; + } + + return events; + }, + + // @method: setZIndex(value: Number) : this + // Changes the [zIndex](#imageoverlay-zindex) of the image overlay. + setZIndex: function (value) { + this.options.zIndex = value; + this._updateZIndex(); + return this; + }, + + // @method getBounds(): LatLngBounds + // Get the bounds that this ImageOverlay covers + getBounds: function () { + return this._bounds; + }, + + // @method getElement(): HTMLElement + // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) + // used by this overlay. + getElement: function () { + return this._image; + }, + + _initImage: function () { + var img = this._image = create$1('img', + 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : '') + + (this.options.className || '')); + + img.onselectstart = falseFn; + img.onmousemove = falseFn; + + // @event load: Event + // Fired when the ImageOverlay layer has loaded its image + img.onload = bind(this.fire, this, 'load'); + img.onerror = bind(this._overlayOnError, this, 'error'); + + if (this.options.crossOrigin) { + img.crossOrigin = ''; + } + + if (this.options.zIndex) { + this._updateZIndex(); + } + + img.src = this._url; + img.alt = this.options.alt; + }, + + _animateZoom: function (e) { + var scale = this._map.getZoomScale(e.zoom), + offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; + + setTransform(this._image, offset, scale); + }, + + _reset: function () { + var image = this._image, + bounds = new Bounds( + this._map.latLngToLayerPoint(this._bounds.getNorthWest()), + this._map.latLngToLayerPoint(this._bounds.getSouthEast())), + size = bounds.getSize(); + + setPosition(image, bounds.min); + + image.style.width = size.x + 'px'; + image.style.height = size.y + 'px'; + }, + + _updateOpacity: function () { + setOpacity(this._image, this.options.opacity); + }, + + _updateZIndex: function () { + if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) { + this._image.style.zIndex = this.options.zIndex; + } + }, + + _overlayOnError: function () { + // @event error: Event + // Fired when the ImageOverlay layer has loaded its image + this.fire('error'); + + var errorUrl = this.options.errorOverlayUrl; + if (errorUrl && this._url !== errorUrl) { + this._url = errorUrl; + this._image.src = errorUrl; + } + } +}); + +// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options) +// Instantiates an image overlay object given the URL of the image and the +// geographical bounds it is tied to. +var imageOverlay = function (url, bounds, options) { + return new ImageOverlay(url, bounds, options); }; - - /* - * @class GeoJSON - * @aka L.GeoJSON - * @inherits FeatureGroup + * @class VideoOverlay + * @aka L.VideoOverlay + * @inherits ImageOverlay * - * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse - * GeoJSON data and display it on the map. Extends `FeatureGroup`. + * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`. + * + * A video overlay uses the [`