react-image-hotspots
Version:
React component for rendering images with hotspots
466 lines (465 loc) • 67.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return _default;
}
});
const _react = /*#__PURE__*/ _interop_require_default(require("react"));
const _proptypes = /*#__PURE__*/ _interop_require_default(require("prop-types"));
const _Hotspot = /*#__PURE__*/ _interop_require_default(require("./Hotspot"));
function _define_property(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
class ImageHotspots extends _react.default.Component {
constructor(props){
super(props), _define_property(this, "componentDidMount", ()=>{
const { hideFullscreenControl, hideZoomControls, hideMinimap, hotspots, background } = this.props;
const { offsetWidth: width, offsetHeight: height } = this.container.current;
const orientation = width > height ? 'landscape' : 'portrait';
const ratio = orientation === 'landscape' ? width / height : height / width;
this.setState({
container: {
width,
height,
ratio,
orientation,
background
},
hideFullscreenControl,
hideZoomControls,
hideMinimap,
hotspots
});
window.addEventListener('resize', this.onWindowResize);
}), _define_property(this, "componentWillUnmount", ()=>{
window.removeEventListener('resize', this.onWindowResize);
}), _define_property(this, "startDrag", (event, element)=>{
const cursorX = event.clientX;
const cursorY = event.clientY;
if (element === 'image') {
this.setState((state)=>({
...state,
cursorX,
cursorY,
dragging: true
}));
} else if (element === 'guide') {
// TODO
}
event.preventDefault();
}), _define_property(this, "whileDrag", (event)=>{
const { image, minimap } = this.state;
const cursorX = event.clientX;
const cursorY = event.clientY;
const deltaX = cursorX - this.state.cursorX;
const deltaY = cursorY - this.state.cursorY;
const newOffsetX = image.offsetX + deltaX;
const newOffsetY = image.offsetY + deltaY;
this.setState((state)=>({
...state,
cursorX,
cursorY,
image: {
...image,
offsetX: newOffsetX,
offsetY: newOffsetY
},
minimap: {
...minimap,
offsetX: -(minimap.width / image.width * newOffsetX),
offsetY: -(minimap.height / image.height * newOffsetY)
}
}));
}), _define_property(this, "stopDrag", ()=>{
const { container, image, minimap } = this.state;
const deltaX = container.width - image.width - image.offsetX;
const deltaY = container.height - image.height - image.offsetY;
const offsetXMax = container.orientation === image.orientation ? -Math.abs(image.width - container.width) : -Math.abs(container.width - image.width);
const offsetYMax = container.orientation === image.orientation ? -Math.abs(container.height - image.height) : -Math.abs(image.height - container.height);
this.setState((state)=>({
...state,
image: {
...state.image,
offsetX: image.offsetX >= 0 ? 0 : deltaX >= 0 ? offsetXMax : image.offsetX,
offsetY: image.offsetY >= 0 ? container.height > image.height ? container.height / 2 - image.height / 2 : 0 : deltaY >= 0 ? container.height > image.height ? container.height / 2 - image.height / 2 : offsetYMax : image.offsetY
},
minimap: {
...state.minimap,
offsetX: image.offsetX >= 0 || image.width < container.width ? 0 : deltaX >= 0 ? -(minimap.height / image.height * offsetXMax) : -(minimap.height / image.height * image.offsetX),
offsetY: image.offsetY >= 0 || image.height < container.height ? 0 : deltaY >= 0 ? -(minimap.height / image.height * offsetYMax) : -(minimap.height / image.height * image.offsetY)
},
dragging: false
}));
}), _define_property(this, "handleOnImageLoad", ({ target: image })=>{
const { offsetWidth: initialWidth, offsetHeight: initialHeight } = image;
const { container, minimap, hideZoomControls, hideMinimap } = this.state;
const orientation = initialWidth > initialHeight ? 'landscape' : 'portrait';
const ratio = orientation === 'landscape' ? initialWidth / initialHeight : initialHeight / initialWidth;
const width = container.orientation === orientation ? orientation === 'landscape' ? ratio >= container.ratio ? container.width // landscape image bigger than landscape container
: container.height * ratio // landscape image smaller than landscape container
: ratio >= container.ratio ? container.height / ratio // portrait image bigger than portrait container
: container.width // portrait image smaller than portrait container
: orientation === 'landscape' ? container.width // landscape image and portrait container
: container.height / ratio // portrait image and landscape container
;
const height = container.orientation === orientation ? orientation === 'landscape' ? ratio >= container.ratio ? container.width / ratio // landscape image bigger than landscape container
: container.height // landscape image smaller than landscape container
: ratio >= container.ratio ? container.height // portrait image bigger than portrait container
: container.width * ratio // portrait image smaller than portrait container
: orientation === 'landscape' ? container.width / ratio // landscape image and portrait container
: container.height // portrait image and landscape container
;
const resizable = initialWidth > width || initialHeight > height;
this.setState((prevState)=>({
...prevState,
image: {
...prevState.image,
initialWidth,
initialHeight,
width,
height,
scale: 1,
ratio,
orientation,
offsetX: 0,
offsetY: container.height / 2 - height / 2
},
minimap: {
...minimap,
width: orientation === 'landscape' ? minimap.initialSize : minimap.initialSize / ratio,
height: orientation === 'portrait' ? minimap.initialSize : minimap.initialSize / ratio,
guideWidth: orientation === 'landscape' ? minimap.initialSize : minimap.initialSize / ratio,
guideHeight: orientation === 'portrait' ? minimap.initialSize : minimap.initialSize / ratio
},
hideZoomControls: hideZoomControls || !resizable,
hideMinimap: hideMinimap || !resizable,
resizable,
draggable: false
}));
}), _define_property(this, "onWindowResize", ()=>{
const { offsetWidth: width, offsetHeight: height } = this.container.current;
const orientation = width > height ? 'landscape' : 'portrait';
const ratio = orientation === 'landscape' ? width / height : height / width;
this.setState({
container: {
width,
height,
ratio,
orientation
}
});
this.zoom(this.state.image.scale);
}), _define_property(this, "toggleFullscreen", ()=>{
const { fullscreen } = this.state;
if (!fullscreen) {
this.requestFullscreen(this.container.current);
this.setState({
fullscreen: true
});
} else {
this.exitFullscreen();
this.setState({
fullscreen: false
});
}
}), _define_property(this, "zoom", (scale)=>{
if (scale > 0) {
const { container, image, minimap } = this.state;
const width = container.orientation === image.orientation ? image.orientation === 'landscape' ? image.ratio >= container.ratio ? container.width * scale // landscape image bigger than landscape container
: container.height * image.ratio * scale // landscape image smaller than landscape container
: image.ratio >= container.ratio ? container.height / image.ratio * scale // portrait image bigger than portrait container
: container.width * scale // portrait image smaller than portrait container
: image.orientation === 'landscape' ? container.width * scale // landscape image and portrait container
: container.height / image.ratio * scale // portrait image and landscape container
;
const height = container.orientation === image.orientation ? image.orientation === 'landscape' ? image.ratio >= container.ratio ? container.width / image.ratio * scale // landscape image bigger than landscape container
: container.height * scale // landscape image smaller than landscape container
: image.ratio >= container.ratio ? container.height * scale // portrait image bigger than portrait container
: container.width * image.ratio * scale // portrait image smaller than portrait container
: image.orientation === 'landscape' ? container.width / image.ratio * scale // landscape image and portrait container
: container.height * scale // portrait image and landscape container
;
const guideWidth = container.width >= width ? minimap.width : minimap.width / (width / container.width);
const guideHeight = container.height >= height ? minimap.height : minimap.height / (height / container.height);
const deltaX = Math.round(width - image.width);
const deltaY = Math.round(height - image.height);
const guideDeltaX = Math.round(guideWidth - minimap.guideWidth);
const guideDeltaY = Math.round(guideHeight - minimap.guideHeight);
const offsetX = image.offsetX - deltaX / 2;
const offsetY = image.offsetY - deltaY / 2;
const guideOffsetX = Math.round(minimap.offsetX - guideDeltaX / 2);
const guideOffsetY = Math.round(minimap.offsetY - guideDeltaY / 2);
const offsetXMax = -Math.abs(Math.round(container.width - width));
const offsetYMax = -Math.abs(Math.round(container.height - height));
const guideOffsetXMax = Math.round(minimap.width - guideWidth);
const guideOffsetYMax = Math.round(minimap.height - guideHeight);
if (image.initialWidth > width && image.initialHeight > height) {
this.setState((prevState)=>({
image: {
...prevState.image,
width,
height,
scale,
offsetX: offsetX >= 0 || container.width > width ? 0 : image.offsetX <= offsetXMax ? offsetXMax : offsetX,
offsetY: container.height > height ? container.height / 2 - height / 2 : offsetY >= 0 ? 0 : image.offsetY < offsetYMax ? offsetYMax : offsetY
},
minimap: {
...prevState.minimap,
guideWidth,
guideHeight,
offsetX: guideOffsetX <= 0 ? 0 : minimap.offsetX < guideOffsetXMax ? guideOffsetX : guideOffsetXMax,
offsetY: guideOffsetY <= 0 || height < container.height ? 0 : minimap.offsetY < guideOffsetYMax ? guideOffsetY : guideOffsetYMax
},
draggable: scale > 1
}));
}
// Reset image position
if (scale === 1) {
this.setState((prevState)=>({
image: {
...prevState.image,
offsetX: 0,
offsetY: container.height / 2 - height / 2
},
minimap: {
...prevState.minimap,
offsetX: 0,
offsetY: 0
}
}));
}
}
}), _define_property(this, "requestFullscreen", (element)=>{
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
}
}), _define_property(this, "exitFullscreen", ()=>{
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}), _define_property(this, "render", ()=>{
const { src, alt, hotspots, background } = this.props;
const { container, image, minimap, fullscreen, dragging, hideFullscreenControl, hideZoomControls, hideMinimap, draggable } = this.state;
const imageLoaded = image.initialWidth && image.initialHeight;
const containerStyle = {
width: '100%',
height: '100%',
position: 'relative',
overflow: 'hidden',
textAlign: 'center',
background: background || container.background
};
const imageStyle = {
position: 'relative',
left: image.offsetX,
top: image.offsetY
};
const hotspotsStyle = {
position: 'absolute',
top: image.offsetY,
left: image.offsetX,
right: image.offsetX >= 0 ? 0 : 'auto',
margin: 'auto',
pointerEvents: 'none'
};
const topControlsStyle = {
position: 'absolute',
top: 10,
right: 10,
pointerEvents: this.state.dragging ? 'none' : 'auto'
};
const bottomControlsStyle = {
position: 'absolute',
bottom: 10,
right: 10,
pointerEvents: this.state.dragging ? 'none' : 'auto'
};
const buttonStyle = {
width: '25px',
height: '25px',
border: 'none',
background: '#fff',
boxShadow: '0px 0px 2px 0px rgba(0,0,0,0.5)'
};
const minimapStyle = {
width: minimap.width,
height: minimap.height,
position: 'absolute',
display: 'block',
bottom: 10,
left: 10,
background: '#fff',
boxShadow: '0px 0px 2px 0px rgba(0,0,0,0.5)',
pointerEvents: 'none'
};
const guideStyle = {
width: minimap.guideWidth,
height: minimap.guideHeight,
position: 'absolute',
display: 'block',
left: minimap.offsetX,
top: minimap.offsetY,
border: '1px solid rgba(64, 139, 252, 0.8)',
background: 'rgba(64, 139, 252, 0.1)',
pointerEvents: 'none'
};
if (imageLoaded) {
if (container.orientation === 'landscape') {
imageStyle.height = image.height;
} else {
imageStyle.width = image.width;
}
if (image.orientation === 'landscape') {
hotspotsStyle.width = image.width;
hotspotsStyle.height = image.width / image.ratio;
} else {
hotspotsStyle.width = image.height / image.ratio;
hotspotsStyle.height = image.height;
}
}
return /*#__PURE__*/ _react.default.createElement("div", {
ref: this.container,
style: containerStyle,
onMouseOut: (event)=>{
if (dragging) {
this.stopDrag(event);
}
},
onBlur: (event)=>{
if (dragging) {
this.stopDrag(event);
}
}
}, src && /*#__PURE__*/ _react.default.createElement("img", {
src: src,
alt: alt,
onLoad: this.handleOnImageLoad,
style: imageStyle,
onMouseDown: (event)=>{
if (!hideZoomControls && draggable) {
this.startDrag(event, 'image');
}
},
onMouseMove: (event)=>{
if (!hideZoomControls && dragging) {
this.whileDrag(event);
}
},
onMouseUp: (event)=>{
if (dragging) {
this.stopDrag(event);
}
}
}), hotspots && /*#__PURE__*/ _react.default.createElement("div", {
style: hotspotsStyle
}, hotspots.map((hotspot, i)=>/*#__PURE__*/ _react.default.createElement(_Hotspot.default, {
key: i,
...hotspot
}))), !hideFullscreenControl && /*#__PURE__*/ _react.default.createElement("div", {
style: topControlsStyle
}, /*#__PURE__*/ _react.default.createElement("button", {
style: buttonStyle,
onClick: ()=>this.toggleFullscreen()
}, fullscreen ? 'X' : 'FS')), !hideZoomControls && /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement("div", {
style: bottomControlsStyle
}, draggable && /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement("button", {
style: buttonStyle,
onClick: ()=>this.zoom(1)
}, "Fit"), /*#__PURE__*/ _react.default.createElement("br", null), /*#__PURE__*/ _react.default.createElement("br", null)), /*#__PURE__*/ _react.default.createElement("button", {
style: buttonStyle,
onClick: ()=>this.zoom(image.scale + 1)
}, "+"), /*#__PURE__*/ _react.default.createElement("br", null), /*#__PURE__*/ _react.default.createElement("button", {
style: buttonStyle,
onClick: ()=>this.zoom(image.scale - 1)
}, "-")), !hideMinimap && /*#__PURE__*/ _react.default.createElement("div", {
style: minimapStyle
}, src && /*#__PURE__*/ _react.default.createElement("img", {
src: src,
width: minimapStyle.width,
height: minimapStyle.height
}), /*#__PURE__*/ _react.default.createElement("div", {
style: guideStyle
}))));
});
this.state = {
container: {
width: undefined,
height: undefined,
ratio: undefined,
orientation: undefined,
background: undefined
},
image: {
initialWidth: undefined,
initialHeight: undefined,
width: undefined,
height: undefined,
scale: undefined,
ratio: undefined,
orientation: undefined,
offsetX: undefined,
offsetY: undefined
},
minimap: {
initialSize: 100,
width: undefined,
height: undefined,
guideWidth: undefined,
guideHeight: undefined,
offsetX: 0,
offsetY: 0
},
hideFullscreenControl: false,
hideZoomControls: false,
hideMinimap: false,
resizable: undefined,
draggable: undefined,
cursorX: undefined,
cursorY: undefined,
mcursorX: undefined,
mcursorY: undefined,
dragging: undefined,
isGuideDragging: undefined,
hotspots: []
};
this.container = /*#__PURE__*/ _react.default.createRef();
}
}
ImageHotspots.propTypes = {
src: _proptypes.default.string,
alt: _proptypes.default.string,
hotspots: _proptypes.default.array
};
const _default = ImageHotspots;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9JbWFnZUhvdHNwb3RzLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCBQcm9wVHlwZXMgZnJvbSAncHJvcC10eXBlcydcbmltcG9ydCBIb3RzcG90IGZyb20gJy4vSG90c3BvdCdcblxuY2xhc3MgSW1hZ2VIb3RzcG90cyBleHRlbmRzIFJlYWN0LkNvbXBvbmVudCB7XG4gIGNvbnN0cnVjdG9yIChwcm9wcykge1xuICAgIHN1cGVyKHByb3BzKVxuXG4gICAgdGhpcy5zdGF0ZSA9IHtcbiAgICAgIGNvbnRhaW5lcjoge1xuICAgICAgICB3aWR0aDogdW5kZWZpbmVkLFxuICAgICAgICBoZWlnaHQ6IHVuZGVmaW5lZCxcbiAgICAgICAgcmF0aW86IHVuZGVmaW5lZCxcbiAgICAgICAgb3JpZW50YXRpb246IHVuZGVmaW5lZCxcbiAgICAgICAgYmFja2dyb3VuZDogdW5kZWZpbmVkXG4gICAgICB9LFxuICAgICAgaW1hZ2U6IHtcbiAgICAgICAgaW5pdGlhbFdpZHRoOiB1bmRlZmluZWQsXG4gICAgICAgIGluaXRpYWxIZWlnaHQ6IHVuZGVmaW5lZCxcbiAgICAgICAgd2lkdGg6IHVuZGVmaW5lZCxcbiAgICAgICAgaGVpZ2h0OiB1bmRlZmluZWQsXG4gICAgICAgIHNjYWxlOiB1bmRlZmluZWQsXG4gICAgICAgIHJhdGlvOiB1bmRlZmluZWQsXG4gICAgICAgIG9yaWVudGF0aW9uOiB1bmRlZmluZWQsXG4gICAgICAgIG9mZnNldFg6IHVuZGVmaW5lZCxcbiAgICAgICAgb2Zmc2V0WTogdW5kZWZpbmVkXG4gICAgICB9LFxuICAgICAgbWluaW1hcDoge1xuICAgICAgICBpbml0aWFsU2l6ZTogMTAwLFxuICAgICAgICB3aWR0aDogdW5kZWZpbmVkLFxuICAgICAgICBoZWlnaHQ6IHVuZGVmaW5lZCxcbiAgICAgICAgZ3VpZGVXaWR0aDogdW5kZWZpbmVkLFxuICAgICAgICBndWlkZUhlaWdodDogdW5kZWZpbmVkLFxuICAgICAgICBvZmZzZXRYOiAwLFxuICAgICAgICBvZmZzZXRZOiAwXG4gICAgICB9LFxuICAgICAgaGlkZUZ1bGxzY3JlZW5Db250cm9sOiBmYWxzZSxcbiAgICAgIGhpZGVab29tQ29udHJvbHM6IGZhbHNlLFxuICAgICAgaGlkZU1pbmltYXA6IGZhbHNlLFxuICAgICAgcmVzaXphYmxlOiB1bmRlZmluZWQsXG4gICAgICBkcmFnZ2FibGU6IHVuZGVmaW5lZCxcbiAgICAgIGN1cnNvclg6IHVuZGVmaW5lZCxcbiAgICAgIGN1cnNvclk6IHVuZGVmaW5lZCxcbiAgICAgIG1jdXJzb3JYOiB1bmRlZmluZWQsXG4gICAgICBtY3Vyc29yWTogdW5kZWZpbmVkLFxuICAgICAgZHJhZ2dpbmc6IHVuZGVmaW5lZCxcbiAgICAgIGlzR3VpZGVEcmFnZ2luZzogdW5kZWZpbmVkLFxuICAgICAgaG90c3BvdHM6IFtdXG4gICAgfVxuXG4gICAgdGhpcy5jb250YWluZXIgPSBSZWFjdC5jcmVhdGVSZWYoKVxuICB9XG5cbiAgY29tcG9uZW50RGlkTW91bnQgPSAoKSA9PiB7XG4gICAgY29uc3Qge1xuICAgICAgaGlkZUZ1bGxzY3JlZW5Db250cm9sLFxuICAgICAgaGlkZVpvb21Db250cm9scyxcbiAgICAgIGhpZGVNaW5pbWFwLFxuICAgICAgaG90c3BvdHMsXG4gICAgICBiYWNrZ3JvdW5kXG4gICAgfSA9IHRoaXMucHJvcHNcbiAgICBjb25zdCB7IG9mZnNldFdpZHRoOiB3aWR0aCwgb2Zmc2V0SGVpZ2h0OiBoZWlnaHQgfSA9IHRoaXMuY29udGFpbmVyLmN1cnJlbnRcbiAgICBjb25zdCBvcmllbnRhdGlvbiA9ICh3aWR0aCA+IGhlaWdodCkgPyAnbGFuZHNjYXBlJyA6ICdwb3J0cmFpdCdcbiAgICBjb25zdCByYXRpbyA9IChvcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZScpID8gd2lkdGggLyBoZWlnaHQgOiBoZWlnaHQgLyB3aWR0aFxuXG4gICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICBjb250YWluZXI6IHsgd2lkdGgsIGhlaWdodCwgcmF0aW8sIG9yaWVudGF0aW9uLCBiYWNrZ3JvdW5kIH0sXG4gICAgICBoaWRlRnVsbHNjcmVlbkNvbnRyb2wsXG4gICAgICBoaWRlWm9vbUNvbnRyb2xzLFxuICAgICAgaGlkZU1pbmltYXAsXG4gICAgICBob3RzcG90c1xuICAgIH0pXG5cbiAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigncmVzaXplJywgdGhpcy5vbldpbmRvd1Jlc2l6ZSlcbiAgfVxuXG4gIGNvbXBvbmVudFdpbGxVbm1vdW50ID0gKCkgPT4ge1xuICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdyZXNpemUnLCB0aGlzLm9uV2luZG93UmVzaXplKVxuICB9XG5cbiAgc3RhcnREcmFnID0gKGV2ZW50LCBlbGVtZW50KSA9PiB7XG4gICAgY29uc3QgY3Vyc29yWCA9IGV2ZW50LmNsaWVudFhcbiAgICBjb25zdCBjdXJzb3JZID0gZXZlbnQuY2xpZW50WVxuICAgIGlmIChlbGVtZW50ID09PSAnaW1hZ2UnKSB7XG4gICAgICB0aGlzLnNldFN0YXRlKHN0YXRlID0+ICh7XG4gICAgICAgIC4uLnN0YXRlLFxuICAgICAgICBjdXJzb3JYLFxuICAgICAgICBjdXJzb3JZLFxuICAgICAgICBkcmFnZ2luZzogdHJ1ZVxuICAgICAgfSkpXG4gICAgfSBlbHNlIGlmIChlbGVtZW50ID09PSAnZ3VpZGUnKSB7XG4gICAgICAvLyBUT0RPXG4gICAgfVxuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KClcbiAgfVxuXG4gIHdoaWxlRHJhZyA9IChldmVudCkgPT4ge1xuICAgIGNvbnN0IHsgaW1hZ2UsIG1pbmltYXAgfSA9IHRoaXMuc3RhdGVcbiAgICBjb25zdCBjdXJzb3JYID0gZXZlbnQuY2xpZW50WFxuICAgIGNvbnN0IGN1cnNvclkgPSBldmVudC5jbGllbnRZXG4gICAgY29uc3QgZGVsdGFYID0gY3Vyc29yWCAtIHRoaXMuc3RhdGUuY3Vyc29yWFxuICAgIGNvbnN0IGRlbHRhWSA9IGN1cnNvclkgLSB0aGlzLnN0YXRlLmN1cnNvcllcbiAgICBjb25zdCBuZXdPZmZzZXRYID0gaW1hZ2Uub2Zmc2V0WCArIGRlbHRhWFxuICAgIGNvbnN0IG5ld09mZnNldFkgPSBpbWFnZS5vZmZzZXRZICsgZGVsdGFZXG5cbiAgICB0aGlzLnNldFN0YXRlKHN0YXRlID0+ICh7XG4gICAgICAuLi5zdGF0ZSxcbiAgICAgIGN1cnNvclgsXG4gICAgICBjdXJzb3JZLFxuICAgICAgaW1hZ2U6IHtcbiAgICAgICAgLi4uaW1hZ2UsXG4gICAgICAgIG9mZnNldFg6IG5ld09mZnNldFgsXG4gICAgICAgIG9mZnNldFk6IG5ld09mZnNldFlcbiAgICAgIH0sXG4gICAgICBtaW5pbWFwOiB7XG4gICAgICAgIC4uLm1pbmltYXAsXG4gICAgICAgIG9mZnNldFg6IC0obWluaW1hcC53aWR0aCAvIGltYWdlLndpZHRoICogbmV3T2Zmc2V0WCksXG4gICAgICAgIG9mZnNldFk6IC0obWluaW1hcC5oZWlnaHQgLyBpbWFnZS5oZWlnaHQgKiBuZXdPZmZzZXRZKVxuICAgICAgfVxuICAgIH0pKVxuICB9XG5cbiAgc3RvcERyYWcgPSAoKSA9PiB7XG4gICAgY29uc3QgeyBjb250YWluZXIsIGltYWdlLCBtaW5pbWFwIH0gPSB0aGlzLnN0YXRlXG4gICAgY29uc3QgZGVsdGFYID0gY29udGFpbmVyLndpZHRoIC0gaW1hZ2Uud2lkdGggLSBpbWFnZS5vZmZzZXRYXG4gICAgY29uc3QgZGVsdGFZID0gY29udGFpbmVyLmhlaWdodCAtIGltYWdlLmhlaWdodCAtIGltYWdlLm9mZnNldFlcblxuICAgIGNvbnN0IG9mZnNldFhNYXggPSBjb250YWluZXIub3JpZW50YXRpb24gPT09IGltYWdlLm9yaWVudGF0aW9uXG4gICAgICA/IC1NYXRoLmFicyhpbWFnZS53aWR0aCAtIGNvbnRhaW5lci53aWR0aClcbiAgICAgIDogLU1hdGguYWJzKGNvbnRhaW5lci53aWR0aCAtIGltYWdlLndpZHRoKVxuICAgIGNvbnN0IG9mZnNldFlNYXggPSBjb250YWluZXIub3JpZW50YXRpb24gPT09IGltYWdlLm9yaWVudGF0aW9uXG4gICAgICA/IC1NYXRoLmFicyhjb250YWluZXIuaGVpZ2h0IC0gaW1hZ2UuaGVpZ2h0KVxuICAgICAgOiAtTWF0aC5hYnMoaW1hZ2UuaGVpZ2h0IC0gY29udGFpbmVyLmhlaWdodClcblxuICAgIHRoaXMuc2V0U3RhdGUoc3RhdGUgPT4gKHtcbiAgICAgIC4uLnN0YXRlLFxuICAgICAgaW1hZ2U6IHtcbiAgICAgICAgLi4uc3RhdGUuaW1hZ2UsXG4gICAgICAgIG9mZnNldFg6IGltYWdlLm9mZnNldFggPj0gMFxuICAgICAgICAgID8gMFxuICAgICAgICAgIDogZGVsdGFYID49IDBcbiAgICAgICAgICAgID8gb2Zmc2V0WE1heFxuICAgICAgICAgICAgOiBpbWFnZS5vZmZzZXRYLFxuICAgICAgICBvZmZzZXRZOiBpbWFnZS5vZmZzZXRZID49IDBcbiAgICAgICAgICA/IChjb250YWluZXIuaGVpZ2h0ID4gaW1hZ2UuaGVpZ2h0KVxuICAgICAgICAgICAgICA/IGNvbnRhaW5lci5oZWlnaHQgLyAyIC0gaW1hZ2UuaGVpZ2h0IC8gMlxuICAgICAgICAgICAgICA6IDBcbiAgICAgICAgICA6IGRlbHRhWSA+PSAwXG4gICAgICAgICAgICA/IChjb250YWluZXIuaGVpZ2h0ID4gaW1hZ2UuaGVpZ2h0KVxuICAgICAgICAgICAgICAgID8gY29udGFpbmVyLmhlaWdodCAvIDIgLSBpbWFnZS5oZWlnaHQgLyAyXG4gICAgICAgICAgICAgICAgOiBvZmZzZXRZTWF4XG4gICAgICAgICAgICA6IGltYWdlLm9mZnNldFlcbiAgICAgIH0sXG4gICAgICBtaW5pbWFwOiB7XG4gICAgICAgIC4uLnN0YXRlLm1pbmltYXAsXG4gICAgICAgIG9mZnNldFg6IGltYWdlLm9mZnNldFggPj0gMCB8fCBpbWFnZS53aWR0aCA8IGNvbnRhaW5lci53aWR0aFxuICAgICAgICAgID8gMFxuICAgICAgICAgIDogZGVsdGFYID49IDBcbiAgICAgICAgICAgID8gLShtaW5pbWFwLmhlaWdodCAvIGltYWdlLmhlaWdodCAqIG9mZnNldFhNYXgpXG4gICAgICAgICAgICA6IC0obWluaW1hcC5oZWlnaHQgLyBpbWFnZS5oZWlnaHQgKiBpbWFnZS5vZmZzZXRYKSxcbiAgICAgICAgb2Zmc2V0WTogaW1hZ2Uub2Zmc2V0WSA+PSAwIHx8IGltYWdlLmhlaWdodCA8IGNvbnRhaW5lci5oZWlnaHRcbiAgICAgICAgICA/IDBcbiAgICAgICAgICA6IGRlbHRhWSA+PSAwXG4gICAgICAgICAgICA/IC0obWluaW1hcC5oZWlnaHQgLyBpbWFnZS5oZWlnaHQgKiBvZmZzZXRZTWF4KVxuICAgICAgICAgICAgOiAtKG1pbmltYXAuaGVpZ2h0IC8gaW1hZ2UuaGVpZ2h0ICogaW1hZ2Uub2Zmc2V0WSlcbiAgICAgIH0sXG4gICAgICBkcmFnZ2luZzogZmFsc2VcbiAgICB9KSlcbiAgfVxuXG4gIGhhbmRsZU9uSW1hZ2VMb2FkID0gKHsgdGFyZ2V0OiBpbWFnZSB9KSA9PiB7XG4gICAgY29uc3QgeyBvZmZzZXRXaWR0aDogaW5pdGlhbFdpZHRoLCBvZmZzZXRIZWlnaHQ6IGluaXRpYWxIZWlnaHQgfSA9IGltYWdlXG4gICAgY29uc3QgeyBjb250YWluZXIsIG1pbmltYXAsIGhpZGVab29tQ29udHJvbHMsIGhpZGVNaW5pbWFwIH0gPSB0aGlzLnN0YXRlXG4gICAgY29uc3Qgb3JpZW50YXRpb24gPSAoaW5pdGlhbFdpZHRoID4gaW5pdGlhbEhlaWdodCkgPyAnbGFuZHNjYXBlJyA6ICdwb3J0cmFpdCdcbiAgICBjb25zdCByYXRpbyA9IChvcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZScpXG4gICAgICA/IGluaXRpYWxXaWR0aCAvIGluaXRpYWxIZWlnaHRcbiAgICAgIDogaW5pdGlhbEhlaWdodCAvIGluaXRpYWxXaWR0aFxuXG4gICAgY29uc3Qgd2lkdGggPSBjb250YWluZXIub3JpZW50YXRpb24gPT09IG9yaWVudGF0aW9uXG4gICAgICA/IG9yaWVudGF0aW9uID09PSAnbGFuZHNjYXBlJ1xuICAgICAgICAgID8gcmF0aW8gPj0gY29udGFpbmVyLnJhdGlvXG4gICAgICAgICAgICAgID8gY29udGFpbmVyLndpZHRoIC8vIGxhbmRzY2FwZSBpbWFnZSBiaWdnZXIgdGhhbiBsYW5kc2NhcGUgY29udGFpbmVyXG4gICAgICAgICAgICAgIDogY29udGFpbmVyLmhlaWdodCAqIHJhdGlvIC8vIGxhbmRzY2FwZSBpbWFnZSBzbWFsbGVyIHRoYW4gbGFuZHNjYXBlIGNvbnRhaW5lclxuICAgICAgICAgIDogcmF0aW8gPj0gY29udGFpbmVyLnJhdGlvXG4gICAgICAgICAgICA/IGNvbnRhaW5lci5oZWlnaHQgLyByYXRpbyAvLyBwb3J0cmFpdCBpbWFnZSBiaWdnZXIgdGhhbiBwb3J0cmFpdCBjb250YWluZXJcbiAgICAgICAgICAgIDogY29udGFpbmVyLndpZHRoIC8vIHBvcnRyYWl0IGltYWdlIHNtYWxsZXIgdGhhbiBwb3J0cmFpdCBjb250YWluZXJcbiAgICAgIDogb3JpZW50YXRpb24gPT09ICdsYW5kc2NhcGUnXG4gICAgICAgID8gY29udGFpbmVyLndpZHRoIC8vIGxhbmRzY2FwZSBpbWFnZSBhbmQgcG9ydHJhaXQgY29udGFpbmVyXG4gICAgICAgIDogY29udGFpbmVyLmhlaWdodCAvIHJhdGlvIC8vIHBvcnRyYWl0IGltYWdlIGFuZCBsYW5kc2NhcGUgY29udGFpbmVyXG5cbiAgICBjb25zdCBoZWlnaHQgPSBjb250YWluZXIub3JpZW50YXRpb24gPT09IG9yaWVudGF0aW9uXG4gICAgICA/IG9yaWVudGF0aW9uID09PSAnbGFuZHNjYXBlJ1xuICAgICAgICAgID8gcmF0aW8gPj0gY29udGFpbmVyLnJhdGlvXG4gICAgICAgICAgICAgID8gY29udGFpbmVyLndpZHRoIC8gcmF0aW8gLy8gbGFuZHNjYXBlIGltYWdlIGJpZ2dlciB0aGFuIGxhbmRzY2FwZSBjb250YWluZXJcbiAgICAgICAgICAgICAgOiBjb250YWluZXIuaGVpZ2h0IC8vIGxhbmRzY2FwZSBpbWFnZSBzbWFsbGVyIHRoYW4gbGFuZHNjYXBlIGNvbnRhaW5lclxuICAgICAgICAgIDogcmF0aW8gPj0gY29udGFpbmVyLnJhdGlvXG4gICAgICAgICAgICA/IGNvbnRhaW5lci5oZWlnaHQgLy8gcG9ydHJhaXQgaW1hZ2UgYmlnZ2VyIHRoYW4gcG9ydHJhaXQgY29udGFpbmVyXG4gICAgICAgICAgICA6IGNvbnRhaW5lci53aWR0aCAqIHJhdGlvIC8vIHBvcnRyYWl0IGltYWdlIHNtYWxsZXIgdGhhbiBwb3J0cmFpdCBjb250YWluZXJcbiAgICAgIDogb3JpZW50YXRpb24gPT09ICdsYW5kc2NhcGUnXG4gICAgICAgID8gY29udGFpbmVyLndpZHRoIC8gcmF0aW8gLy8gbGFuZHNjYXBlIGltYWdlIGFuZCBwb3J0cmFpdCBjb250YWluZXJcbiAgICAgICAgOiBjb250YWluZXIuaGVpZ2h0IC8vIHBvcnRyYWl0IGltYWdlIGFuZCBsYW5kc2NhcGUgY29udGFpbmVyXG5cbiAgICBjb25zdCByZXNpemFibGUgPSAoaW5pdGlhbFdpZHRoID4gd2lkdGgpIHx8IChpbml0aWFsSGVpZ2h0ID4gaGVpZ2h0KVxuXG4gICAgdGhpcy5zZXRTdGF0ZSgocHJldlN0YXRlKSA9PiAoe1xuICAgICAgLi4ucHJldlN0YXRlLFxuICAgICAgaW1hZ2U6IHtcbiAgICAgICAgLi4ucHJldlN0YXRlLmltYWdlLFxuICAgICAgICBpbml0aWFsV2lkdGgsXG4gICAgICAgIGluaXRpYWxIZWlnaHQsXG4gICAgICAgIHdpZHRoLFxuICAgICAgICBoZWlnaHQsXG4gICAgICAgIHNjYWxlOiAxLFxuICAgICAgICByYXRpbyxcbiAgICAgICAgb3JpZW50YXRpb24sXG4gICAgICAgIG9mZnNldFg6IDAsXG4gICAgICAgIG9mZnNldFk6IGNvbnRhaW5lci5oZWlnaHQgLyAyIC0gaGVpZ2h0IC8gMlxuICAgICAgfSxcbiAgICAgIG1pbmltYXA6IHtcbiAgICAgICAgLi4ubWluaW1hcCxcbiAgICAgICAgd2lkdGg6IG9yaWVudGF0aW9uID09PSAnbGFuZHNjYXBlJ1xuICAgICAgICAgID8gbWluaW1hcC5pbml0aWFsU2l6ZVxuICAgICAgICAgIDogbWluaW1hcC5pbml0aWFsU2l6ZSAvIHJhdGlvLFxuICAgICAgICBoZWlnaHQ6IG9yaWVudGF0aW9uID09PSAncG9ydHJhaXQnXG4gICAgICAgICAgPyBtaW5pbWFwLmluaXRpYWxTaXplXG4gICAgICAgICAgOiBtaW5pbWFwLmluaXRpYWxTaXplIC8gcmF0aW8sXG4gICAgICAgIGd1aWRlV2lkdGg6IG9yaWVudGF0aW9uID09PSAnbGFuZHNjYXBlJ1xuICAgICAgICAgID8gbWluaW1hcC5pbml0aWFsU2l6ZVxuICAgICAgICAgIDogbWluaW1hcC5pbml0aWFsU2l6ZSAvIHJhdGlvLFxuICAgICAgICBndWlkZUhlaWdodDogb3JpZW50YXRpb24gPT09ICdwb3J0cmFpdCdcbiAgICAgICAgICA/IG1pbmltYXAuaW5pdGlhbFNpemVcbiAgICAgICAgICA6IG1pbmltYXAuaW5pdGlhbFNpemUgLyByYXRpb1xuICAgICAgfSxcbiAgICAgIGhpZGVab29tQ29udHJvbHM6IGhpZGVab29tQ29udHJvbHMgfHwgIXJlc2l6YWJsZSxcbiAgICAgIGhpZGVNaW5pbWFwOiBoaWRlTWluaW1hcCB8fCAhcmVzaXphYmxlLFxuICAgICAgcmVzaXphYmxlLFxuICAgICAgZHJhZ2dhYmxlOiBmYWxzZVxuICAgIH0pKVxuICB9XG5cbiAgb25XaW5kb3dSZXNpemUgPSAoKSA9PiB7XG4gICAgY29uc3QgeyBvZmZzZXRXaWR0aDogd2lkdGgsIG9mZnNldEhlaWdodDogaGVpZ2h0IH0gPSB0aGlzLmNvbnRhaW5lci5jdXJyZW50XG4gICAgY29uc3Qgb3JpZW50YXRpb24gPSAod2lkdGggPiBoZWlnaHQpID8gJ2xhbmRzY2FwZScgOiAncG9ydHJhaXQnXG4gICAgY29uc3QgcmF0aW8gPSAob3JpZW50YXRpb24gPT09ICdsYW5kc2NhcGUnKSA/IHdpZHRoIC8gaGVpZ2h0IDogaGVpZ2h0IC8gd2lkdGhcblxuICAgIHRoaXMuc2V0U3RhdGUoeyBjb250YWluZXI6IHsgd2lkdGgsIGhlaWdodCwgcmF0aW8sIG9yaWVudGF0aW9uIH0gfSlcblxuICAgIHRoaXMuem9vbSh0aGlzLnN0YXRlLmltYWdlLnNjYWxlKVxuICB9XG5cbiAgdG9nZ2xlRnVsbHNjcmVlbiA9ICgpID0+IHtcbiAgICBjb25zdCB7IGZ1bGxzY3JlZW4gfSA9IHRoaXMuc3RhdGVcbiAgICBpZiAoIWZ1bGxzY3JlZW4pIHtcbiAgICAgIHRoaXMucmVxdWVzdEZ1bGxzY3JlZW4odGhpcy5jb250YWluZXIuY3VycmVudClcbiAgICAgIHRoaXMuc2V0U3RhdGUoeyBmdWxsc2NyZWVuOiB0cnVlIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZXhpdEZ1bGxzY3JlZW4oKVxuICAgICAgdGhpcy5zZXRTdGF0ZSh7IGZ1bGxzY3JlZW46IGZhbHNlIH0pXG4gICAgfVxuICB9XG5cbiAgem9vbSA9IChzY2FsZSkgPT4ge1xuICAgIGlmIChzY2FsZSA+IDApIHtcbiAgICAgIGNvbnN0IHsgY29udGFpbmVyLCBpbWFnZSwgbWluaW1hcCB9ID0gdGhpcy5zdGF0ZVxuXG4gICAgICBjb25zdCB3aWR0aCA9IGNvbnRhaW5lci5vcmllbnRhdGlvbiA9PT0gaW1hZ2Uub3JpZW50YXRpb25cbiAgICAgICAgPyBpbWFnZS5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZSdcbiAgICAgICAgICAgID8gaW1hZ2UucmF0aW8gPj0gY29udGFpbmVyLnJhdGlvXG4gICAgICAgICAgICAgICAgPyBjb250YWluZXIud2lkdGggKiBzY2FsZS8vIGxhbmRzY2FwZSBpbWFnZSBiaWdnZXIgdGhhbiBsYW5kc2NhcGUgY29udGFpbmVyXG4gICAgICAgICAgICAgICAgOiBjb250YWluZXIuaGVpZ2h0ICogaW1hZ2UucmF0aW8gKiBzY2FsZS8vIGxhbmRzY2FwZSBpbWFnZSBzbWFsbGVyIHRoYW4gbGFuZHNjYXBlIGNvbnRhaW5lclxuICAgICAgICAgICAgOiBpbWFnZS5yYXRpbyA+PSBjb250YWluZXIucmF0aW9cbiAgICAgICAgICAgICAgPyBjb250YWluZXIuaGVpZ2h0IC8gaW1hZ2UucmF0aW8gKiBzY2FsZS8vIHBvcnRyYWl0IGltYWdlIGJpZ2dlciB0aGFuIHBvcnRyYWl0IGNvbnRhaW5lclxuICAgICAgICAgICAgICA6IGNvbnRhaW5lci53aWR0aCAqIHNjYWxlLy8gcG9ydHJhaXQgaW1hZ2Ugc21hbGxlciB0aGFuIHBvcnRyYWl0IGNvbnRhaW5lclxuICAgICAgICA6IGltYWdlLm9yaWVudGF0aW9uID09PSAnbGFuZHNjYXBlJ1xuICAgICAgICAgID8gY29udGFpbmVyLndpZHRoICogc2NhbGUvLyBsYW5kc2NhcGUgaW1hZ2UgYW5kIHBvcnRyYWl0IGNvbnRhaW5lclxuICAgICAgICAgIDogY29udGFpbmVyLmhlaWdodCAvIGltYWdlLnJhdGlvICogc2NhbGUvLyBwb3J0cmFpdCBpbWFnZSBhbmQgbGFuZHNjYXBlIGNvbnRhaW5lclxuXG4gICAgICBjb25zdCBoZWlnaHQgPSBjb250YWluZXIub3JpZW50YXRpb24gPT09IGltYWdlLm9yaWVudGF0aW9uXG4gICAgICAgID8gaW1hZ2Uub3JpZW50YXRpb24gPT09ICdsYW5kc2NhcGUnXG4gICAgICAgICAgICA/IGltYWdlLnJhdGlvID49IGNvbnRhaW5lci5yYXRpb1xuICAgICAgICAgICAgICAgID8gY29udGFpbmVyLndpZHRoIC8gaW1hZ2UucmF0aW8gKiBzY2FsZS8vIGxhbmRzY2FwZSBpbWFnZSBiaWdnZXIgdGhhbiBsYW5kc2NhcGUgY29udGFpbmVyXG4gICAgICAgICAgICAgICAgOiBjb250YWluZXIuaGVpZ2h0ICogc2NhbGUvLyBsYW5kc2NhcGUgaW1hZ2Ugc21hbGxlciB0aGFuIGxhbmRzY2FwZSBjb250YWluZXJcbiAgICAgICAgICAgIDogaW1hZ2UucmF0aW8gPj0gY29udGFpbmVyLnJhdGlvXG4gICAgICAgICAgICAgID8gY29udGFpbmVyLmhlaWdodCAqIHNjYWxlLy8gcG9ydHJhaXQgaW1hZ2UgYmlnZ2VyIHRoYW4gcG9ydHJhaXQgY29udGFpbmVyXG4gICAgICAgICAgICAgIDogY29udGFpbmVyLndpZHRoICogaW1hZ2UucmF0aW8gKiBzY2FsZS8vIHBvcnRyYWl0IGltYWdlIHNtYWxsZXIgdGhhbiBwb3J0cmFpdCBjb250YWluZXJcbiAgICAgICAgOiBpbWFnZS5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZSdcbiAgICAgICAgICA/IGNvbnRhaW5lci53aWR0aCAvIGltYWdlLnJhdGlvICogc2NhbGUvLyBsYW5kc2NhcGUgaW1hZ2UgYW5kIHBvcnRyYWl0IGNvbnRhaW5lclxuICAgICAgICAgIDogY29udGFpbmVyLmhlaWdodCAqIHNjYWxlLy8gcG9ydHJhaXQgaW1hZ2UgYW5kIGxhbmRzY2FwZSBjb250YWluZXJcblxuICAgICAgY29uc3QgZ3VpZGVXaWR0aCA9IChjb250YWluZXIud2lkdGggPj0gd2lkdGgpXG4gICAgICAgID8gbWluaW1hcC53aWR0aFxuICAgICAgICA6IG1pbmltYXAud2lkdGggLyAod2lkdGggLyBjb250YWluZXIud2lkdGgpXG4gICAgICBjb25zdCBndWlkZUhlaWdodCA9IChjb250YWluZXIuaGVpZ2h0ID49IGhlaWdodClcbiAgICAgICAgPyBtaW5pbWFwLmhlaWdodFxuICAgICAgICA6IG1pbmltYXAuaGVpZ2h0IC8gKGhlaWdodCAvIGNvbnRhaW5lci5oZWlnaHQpXG5cbiAgICAgIGNvbnN0IGRlbHRhWCA9IE1hdGgucm91bmQod2lkdGggLSBpbWFnZS53aWR0aClcbiAgICAgIGNvbnN0IGRlbHRhWSA9IE1hdGgucm91bmQoaGVpZ2h0IC0gaW1hZ2UuaGVpZ2h0KVxuICAgICAgY29uc3QgZ3VpZGVEZWx0YVggPSBNYXRoLnJvdW5kKGd1aWRlV2lkdGggLSBtaW5pbWFwLmd1aWRlV2lkdGgpXG4gICAgICBjb25zdCBndWlkZURlbHRhWSA9IE1hdGgucm91bmQoZ3VpZGVIZWlnaHQgLSBtaW5pbWFwLmd1aWRlSGVpZ2h0KVxuXG4gICAgICBjb25zdCBvZmZzZXRYID0gaW1hZ2Uub2Zmc2V0WCAtIGRlbHRhWCAvIDJcbiAgICAgIGNvbnN0IG9mZnNldFkgPSBpbWFnZS5vZmZzZXRZIC0gZGVsdGFZIC8gMlxuICAgICAgY29uc3QgZ3VpZGVPZmZzZXRYID0gTWF0aC5yb3VuZChtaW5pbWFwLm9mZnNldFggLSBndWlkZURlbHRhWCAvIDIpXG4gICAgICBjb25zdCBndWlkZU9mZnNldFkgPSBNYXRoLnJvdW5kKG1pbmltYXAub2Zmc2V0WSAtIGd1aWRlRGVsdGFZIC8gMilcblxuICAgICAgY29uc3Qgb2Zmc2V0WE1heCA9IC1NYXRoLmFicyhNYXRoLnJvdW5kKGNvbnRhaW5lci53aWR0aCAtIHdpZHRoKSlcbiAgICAgIGNvbnN0IG9mZnNldFlNYXggPSAtTWF0aC5hYnMoTWF0aC5yb3VuZChjb250YWluZXIuaGVpZ2h0IC0gaGVpZ2h0KSlcbiAgICAgIGNvbnN0IGd1aWRlT2Zmc2V0WE1heCA9IE1hdGgucm91bmQobWluaW1hcC53aWR0aCAtIGd1aWRlV2lkdGgpXG4gICAgICBjb25zdCBndWlkZU9mZnNldFlNYXggPSBNYXRoLnJvdW5kKG1pbmltYXAuaGVpZ2h0IC0gZ3VpZGVIZWlnaHQpXG5cbiAgICAgIGlmIChpbWFnZS5pbml0aWFsV2lkdGggPiB3aWR0aCAmJiBpbWFnZS5pbml0aWFsSGVpZ2h0ID4gaGVpZ2h0KSB7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoKHByZXZTdGF0ZSkgPT4gKHtcbiAgICAgICAgICBpbWFnZToge1xuICAgICAgICAgICAgLi4ucHJldlN0YXRlLmltYWdlLFxuICAgICAgICAgICAgd2lkdGgsXG4gICAgICAgICAgICBoZWlnaHQsXG4gICAgICAgICAgICBzY2FsZSxcbiAgICAgICAgICAgIG9mZnNldFg6IG9mZnNldFggPj0gMCB8fCBjb250YWluZXIud2lkdGggPiB3aWR0aFxuICAgICAgICAgICAgICA/IDBcbiAgICAgICAgICAgICAgOiBpbWFnZS5vZmZzZXRYIDw9IG9mZnNldFhNYXhcbiAgICAgICAgICAgICAgICA/IG9mZnNldFhNYXhcbiAgICAgICAgICAgICAgICA6IG9mZnNldFgsXG4gICAgICAgICAgICBvZmZzZXRZOiAoY29udGFpbmVyLmhlaWdodCA+IGhlaWdodClcbiAgICAgICAgICAgICAgPyBjb250YWluZXIuaGVpZ2h0IC8gMiAtIGhlaWdodCAvIDJcbiAgICAgICAgICAgICAgOiBvZmZzZXRZID49IDBcbiAgICAgICAgICAgICAgICA/IDBcbiAgICAgICAgICAgICAgICA6IGltYWdlLm9mZnNldFkgPCBvZmZzZXRZTWF4XG4gICAgICAgICAgICAgICAgICA/IG9mZnNldFlNYXhcbiAgICAgICAgICAgICAgICAgIDogb2Zmc2V0WVxuICAgICAgICAgIH0sXG4gICAgICAgICAgbWluaW1hcDoge1xuICAgICAgICAgICAgLi4ucHJldlN0YXRlLm1pbmltYXAsXG4gICAgICAgICAgICBndWlkZVdpZHRoLFxuICAgICAgICAgICAgZ3VpZGVIZWlnaHQsXG4gICAgICAgICAgICBvZmZzZXRYOiBndWlkZU9mZnNldFggPD0gMFxuICAgICAgICAgICAgICA/IDBcbiAgICAgICAgICAgICAgOiBtaW5pbWFwLm9mZnNldFggPCBndWlkZU9mZnNldFhNYXhcbiAgICAgICAgICAgICAgICA/IGd1aWRlT2Zmc2V0WFxuICAgICAgICAgICAgICAgIDogZ3VpZGVPZmZzZXRYTWF4LFxuICAgICAgICAgICAgb2Zmc2V0WTogZ3VpZGVPZmZzZXRZIDw9IDAgfHwgaGVpZ2h0IDwgY29udGFpbmVyLmhlaWdodFxuICAgICAgICAgICAgICA/IDBcbiAgICAgICAgICAgICAgOiBtaW5pbWFwLm9mZnNldFkgPCBndWlkZU9mZnNldFlNYXhcbiAgICAgICAgICAgICAgICA/IGd1aWRlT2Zmc2V0WVxuICAgICAgICAgICAgICAgIDogZ3VpZGVPZmZzZXRZTWF4XG4gICAgICAgICAgfSxcbiAgICAgICAgICBkcmFnZ2FibGU6IHNjYWxlID4gMVxuICAgICAgICB9KSlcbiAgICAgIH1cblxuICAgICAgLy8gUmVzZXQgaW1hZ2UgcG9zaXRpb25cbiAgICAgIGlmIChzY2FsZSA9PT0gMSkge1xuICAgICAgICB0aGlzLnNldFN0YXRlKChwcmV2U3RhdGUpID0+ICh7XG4gICAgICAgICAgaW1hZ2U6IHtcbiAgICAgICAgICAgIC4uLnByZXZTdGF0ZS5pbWFnZSxcbiAgICAgICAgICAgIG9mZnNldFg6IDAsXG4gICAgICAgICAgICBvZmZzZXRZOiBjb250YWluZXIuaGVpZ2h0IC8gMiAtIGhlaWdodCAvIDJcbiAgICAgICAgICB9LFxuICAgICAgICAgIG1pbmltYXA6IHtcbiAgICAgICAgICAgIC4uLnByZXZTdGF0ZS5taW5pbWFwLFxuICAgICAgICAgICAgb2Zmc2V0WDogMCxcbiAgICAgICAgICAgIG9mZnNldFk6IDBcbiAgICAgICAgICB9XG4gICAgICAgIH0pKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJlcXVlc3RGdWxsc2NyZWVuID0gKGVsZW1lbnQpID0+IHtcbiAgICBpZiAoZWxlbWVudC5yZXF1ZXN0RnVsbHNjcmVlbikge1xuICAgICAgZWxlbWVudC5yZXF1ZXN0RnVsbHNjcmVlbigpXG4gICAgfSBlbHNlIGlmIChlbGVtZW50Lm1velJlcXVlc3RGdWxsU2NyZWVuKSB7XG4gICAgICBlbGVtZW50Lm1velJlcXVlc3RGdWxsU2NyZWVuKClcbiAgICB9IGVsc2UgaWYgKGVsZW1lbnQud2Via2l0UmVxdWVzdEZ1bGxzY3JlZW4pIHtcbiAgICAgIGVsZW1lbnQud2Via2l0UmVxdWVzdEZ1bGxzY3JlZW4oKVxuICAgIH0gZWxzZSBpZiAoZWxlbWVudC5tc1JlcXVlc3RGdWxsc2NyZWVuKSB7XG4gICAgICBlbGVtZW50Lm1zUmVxdWVzdEZ1bGxzY3JlZW4oKVxuICAgIH1cbiAgfVxuXG4gIGV4aXRGdWxsc2NyZWVuID0gKCkgPT4ge1xuICAgIGlmIChkb2N1bWVudC5leGl0RnVsbHNjcmVlbikge1xuICAgICAgZG9jdW1lbnQuZXhpdEZ1bGxzY3JlZW4oKVxuICAgIH0gZWxzZSBpZiAoZG9jdW1lbnQubW96Q2FuY2VsRnVsbFNjcmVlbikge1xuICAgICAgZG9jdW1lbnQubW96Q2FuY2VsRnVsbFNjcmVlbigpXG4gICAgfSBlbHNlIGlmIChkb2N1bWVudC53ZWJraXRFeGl0RnVsbHNjcmVlbikge1xuICAgICAgZG9jdW1lbnQud2Via2l0RXhpdEZ1bGxzY3JlZW4oKVxuICAgIH0gZWxzZSBpZiAoZG9jdW1lbnQubXNFeGl0RnVsbHNjcmVlbikge1xuICAgICAgZG9jdW1lbnQubXNFeGl0RnVsbHNjcmVlbigpXG4gICAgfVxuICB9XG5cbiAgcmVuZGVyID0gKCkgPT4ge1xuICAgIGNvbnN0IHsgc3JjLCBhbHQsIGhvdHNwb3RzLCBiYWNrZ3JvdW5kIH0gPSB0aGlzLnByb3BzXG4gICAgY29uc3Qge1xuICAgICAgY29udGFpbmVyLFxuICAgICAgaW1hZ2UsXG4gICAgICBtaW5pbWFwLFxuICAgICAgZnVsbHNjcmVlbixcbiAgICAgIGRyYWdnaW5nLFxuICAgICAgaGlkZUZ1bGxzY3JlZW5Db250cm9sLFxuICAgICAgaGlkZVpvb21Db250cm9scyxcbiAgICAgIGhpZGVNaW5pbWFwLFxuICAgICAgZHJhZ2dhYmxlXG4gICAgfSA9IHRoaXMuc3RhdGVcbiAgICBjb25zdCBpbWFnZUxvYWRlZCA9IGltYWdlLmluaXRpYWxXaWR0aCAmJiBpbWFnZS5pbml0aWFsSGVpZ2h0XG5cbiAgICBjb25zdCBjb250YWluZXJTdHlsZSA9IHtcbiAgICAgIHdpZHRoOiAnMTAwJScsXG4gICAgICBoZWlnaHQ6ICcxMDAlJyxcbiAgICAgIHBvc2l0aW9uOiAncmVsYXRpdmUnLFxuICAgICAgb3ZlcmZsb3c6ICdoaWRkZW4nLFxuICAgICAgdGV4dEFsaWduOiAnY2VudGVyJyxcbiAgICAgIGJhY2tncm91bmQ6IGJhY2tncm91bmQgfHwgY29udGFpbmVyLmJhY2tncm91bmRcbiAgICB9XG5cbiAgICBjb25zdCBpbWFnZVN0eWxlID0ge1xuICAgICAgcG9zaXRpb246ICdyZWxhdGl2ZScsXG4gICAgICBsZWZ0OiBpbWFnZS5vZmZzZXRYLFxuICAgICAgdG9wOiBpbWFnZS5vZmZzZXRZXG4gICAgfVxuXG4gICAgY29uc3QgaG90c3BvdHNTdHlsZSA9IHtcbiAgICAgIHBvc2l0aW9uOiAnYWJzb2x1dGUnLFxuICAgICAgdG9wOiBpbWFnZS5vZmZzZXRZLFxuICAgICAgbGVmdDogaW1hZ2Uub2Zmc2V0WCxcbiAgICAgIHJpZ2h0OiAoaW1hZ2Uub2Zmc2V0WCA+PSAwKSA/IDAgOiAnYXV0bycsXG4gICAgICBtYXJnaW46ICdhdXRvJyxcbiAgICAgIHBvaW50ZXJFdmVudHM6ICdub25lJ1xuICAgIH1cblxuICAgIGNvbnN0IHRvcENvbnRyb2xzU3R5bGUgPSB7XG4gICAgICBwb3NpdGlvbjogJ2Fic29sdXRlJyxcbiAgICAgIHRvcDogMTAsXG4gICAgICByaWdodDogMTAsXG4gICAgICBwb2ludGVyRXZlbnRzOiB0aGlzLnN0YXRlLmRyYWdnaW5nID8gJ25vbmUnIDogJ2F1dG8nXG4gICAgfVxuXG4gICAgY29uc3QgYm90dG9tQ29udHJvbHNTdHlsZSA9IHtcbiAgICAgIHBvc2l0aW9uOiAnYWJzb2x1dGUnLFxuICAgICAgYm90dG9tOiAxMCxcbiAgICAgIHJpZ2h0OiAxMCxcbiAgICAgIHBvaW50ZXJFdmVudHM6IHRoaXMuc3RhdGUuZHJhZ2dpbmcgPyAnbm9uZScgOiAnYXV0bydcbiAgICB9XG5cbiAgICBjb25zdCBidXR0b25TdHlsZSA9IHtcbiAgICAgIHdpZHRoOiAnMjVweCcsXG4gICAgICBoZWlnaHQ6ICcyNXB4JyxcbiAgICAgIGJvcmRlcjogJ25vbmUnLFxuICAgICAgYmFja2dyb3VuZDogJyNmZmYnLFxuICAgICAgYm94U2hhZG93OiAnMHB4IDBweCAycHggMHB4IHJnYmEoMCwwLDAsMC41KSdcbiAgICB9XG5cbiAgICBjb25zdCBtaW5pbWFwU3R5bGUgPSB7XG4gICAgICB3aWR0aDogbWluaW1hcC53aWR0aCxcbiAgICAgIGhlaWdodDogbWluaW1hcC5oZWlnaHQsXG4gICAgICBwb3NpdGlvbjogJ2Fic29sdXRlJyxcbiAgICAgIGRpc3BsYXk6ICdibG9jaycsXG4gICAgICBib3R0b206IDEwLFxuICAgICAgbGVmdDogMTAsXG4gICAgICBiYWNrZ3JvdW5kOiAnI2ZmZicsXG4gICAgICBib3hTaGFkb3c6ICcwcHggMHB4IDJweCAwcHggcmdiYSgwLDAsMCwwLjUpJyxcbiAgICAgIHBvaW50ZXJFdmVudHM6ICdub25lJ1xuICAgIH1cblxuICAgIGNvbnN0IGd1aWRlU3R5bGUgPSB7XG4gICAgICB3aWR0aDogbWluaW1hcC5ndWlkZVdpZHRoLFxuICAgICAgaGVpZ2h0OiBtaW5pbWFwLmd1aWRlSGVpZ2h0LFxuICAgICAgcG9zaXRpb246ICdhYnNvbHV0ZScsXG4gICAgICBkaXNwbGF5OiAnYmxvY2snLFxuICAgICAgbGVmdDogbWluaW1hcC5vZmZzZXRYLFxuICAgICAgdG9wOiBtaW5pbWFwLm9mZnNldFksXG4gICAgICBib3JkZXI6ICcxcHggc29saWQgcmdiYSg2NCwgMTM5LCAyNTIsIDAuOCknLFxuICAgICAgYmFja2dyb3VuZDogJ3JnYmEoNjQsIDEzOSwgMjUyLCAwLjEpJyxcbiAgICAgIHBvaW50ZXJFdmVudHM6ICdub25lJ1xuICAgIH1cblxuICAgIGlmIChpbWFnZUxvYWRlZCkge1xuICAgICAgaWYgKGNvbnRhaW5lci5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZScpIHtcbiAgICAgICAgaW1hZ2VTdHlsZS5oZWlnaHQgPSBpbWFnZS5oZWlnaHRcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGltYWdlU3R5bGUud2lkdGggPSBpbWFnZS53aWR0aFxuICAgICAgfVxuXG4gICAgICBpZiAoaW1hZ2Uub3JpZW50YXRpb24gPT09ICdsYW5kc2NhcGUnKSB7XG4gICAgICAgIGhvdHNwb3RzU3R5bGUud2lkdGggPSBpbWFnZS53aWR0aFxuICAgICAgICBob3RzcG90c1N0eWxlLmhlaWdodCA9IGltYWdlLndpZHRoIC8gaW1hZ2UucmF0aW9cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGhvdHNwb3RzU3R5bGUud2lkdGggPSBpbWFnZS5oZWlnaHQgLyBpbWFnZS5yYXRpb1xuICAgICAgICBob3RzcG90c1N0eWxlLmhlaWdodCA9IGltYWdlLmhlaWdodFxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiAoXG4gICAgICA8ZGl2XG4gICAgICAgIHJlZj17dGhpcy5jb250YWluZXJ9XG4gICAgICAgIHN0eWxlPXtjb250YWluZXJTdHlsZX1cbiAgICAgICAgb25Nb3VzZU91dD17ZXZlbnQgPT4ge1xuICAgICAgICAgIGlmIChkcmFnZ2luZykge1xuICAgICAgICAgICAgdGhpcy5zdG9wRHJhZyhldmVudClcbiAgICAgICAgICB9XG4gICAgICAgIH19XG4gICAgICAgIG9uQmx1cj17ZXZlbnQgPT4ge1xuICAgICAgICAgIGlmIChkcmFnZ2luZykge1xuICAgICAgICAgICAgdGhpcy5zdG9wRHJhZyhldmVudClcbiAgICAgICAgICB9XG4gICAgICAgIH19XG4gICAgICA+XG4gICAgICAgIHtcbiAgICAgICAgICBzcmMgJiZcbiAgICAgICAgICAgIDxpbWdcbiAgICAgICAgICAgICAgc3JjPXtzcmN9XG4gICAgICAgICAgICAgIGFsdD17YWx0fVxuICAgICAgICAgICAgICBvbkxvYWQ9e3RoaXMuaGFuZGxlT25JbWFnZUxvYWR9XG4gICAgICAgICAgICAgIHN0eWxlPXtpbWFnZVN0eWxlfVxuICAgICAgICAgICAgICBvbk1vdXNlRG93bj17ZXZlbnQgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghaGlkZVpvb21Db250cm9scyAmJiBkcmFnZ2FibGUpIHtcbiAgICAgICAgICAgICAgICAgIHRoaXMuc3RhcnREcmFnKGV2ZW50LCAnaW1hZ2UnKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgICAgb25Nb3VzZU1vdmU9e2V2ZW50ID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoIWhpZGVab29tQ29udHJvbHMgJiYgZHJhZ2dpbmcpIHtcbiAgICAgICAgICAgICAgICAgIHRoaXMud2hpbGVEcmFnKGV2ZW50KVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgICAgb25Nb3VzZVVwPXtldmVudCA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKGRyYWdnaW5nKSB7XG4gICAgICAgICAgICAgICAgICB0aGlzLnN0b3BEcmFnKGV2ZW50KVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgIC8+XG4gICAgICAgIH1cbiAgICAgICAge1xuICAgICAgICAgIGhvdHNwb3RzICYmXG4gICAgICAgICAgICA8ZGl2IHN0eWxlPXtob3RzcG90c1N0eWxlfT5cbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICBob3RzcG90cy5tYXAoKGhvdHNwb3QsIGkpID0+IDxIb3RzcG90IGtleT17aX0gey4uLmhvdHNwb3R9IC8+KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cbiAgICAgICAge1xuICAgICAgICAgICFoaWRlRnVsbHNjcmVlbkNvbnRyb2wgJiZcbiAgICAgICAgICAgIDxkaXYgc3R5bGU9e3RvcENvbnRyb2xzU3R5bGV9PlxuICAgICAgICAgICAgICA8YnV0dG9uIHN0eWxlPXtidXR0b25TdHlsZX0gb25DbGljaz17KCkgPT4gdGhpcy50b2dnbGVGdWxsc2NyZWVuKCl9PlxuICAgICAgICAgICAgICAgIHtmdWxsc2NyZWVuID8gJ1gnIDogJ0ZTJ31cbiAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuICAgICAgICB7XG4gICAgICAgICAgIWhpZGVab29tQ29udHJvbHMgJiZcbiAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgIDxkaXYgc3R5bGU9e2JvdHRvbUNvbnRyb2xzU3R5bGV9PlxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIGRyYWdnYWJsZSAmJlxuICAgICAgICAgICAgICAgICAgICA8PlxuICAgICAgICAgICAgICAgICAgICAgIDxidXR0b24gc3R5bGU9e2J1dHRvblN0eWxlfSBvbkNsaWNrPXsoKSA9PiB0aGlzLnpvb20oMSl9PkZpdDwvYnV0dG9uPlxuICAgICAgICAgICAgICAgICAgICAgIDxiciAvPlxuICAgICAgICAgICAgICAgICAgICAgIDxiciAvPlxuICAgICAgICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgPGJ1dHRvbiBzdHlsZT17YnV0dG9uU3R5bGV9IG9uQ2xpY2s9eygpID0+IHRoaXMuem9vbShpbWFnZS5zY2FsZSArIDEpfT4rPC9idXR0b24+XG4gICAgICAgICAgICAgICAgPGJyIC8+XG4gICAgICAgICAgICAgICAgPGJ1dHRvbiBzdHlsZT17YnV0dG9uU3R5bGV9IG9uQ2xpY2s9eygpID0+IHRoaXMuem9vbShpbWFnZS5zY2FsZSAtIDEpfT4tPC9idXR0b24+XG4gICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgIWhpZGVNaW5pbWFwICYmXG4gICAgICAgICAgICAgICAgICA8ZGl2IHN0eWxlPXttaW5pbWFwU3R5bGV9PlxuICAgICAgICAgICAgICAgICAgICB7c3JjICYmXG4gICAgICAgICAgICAgICAgICAgICAgPGltZyBzcmM9e3NyY30gd2lkdGg9e21pbmltYXBTdHlsZS53aWR0aH0gaGVpZ2h0PXttaW5pbWFwU3R5bGUuaGVpZ2h0fSAvPn1cbiAgICAgICAgICAgICAgICAgICAgPGRpdiBzdHlsZT17Z3VpZGVTdHlsZX0gLz5cbiAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA8Lz5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG4gICAgKVxuICB9XG59XG5cbkltYWdlSG90c3BvdHMucHJvcFR5cGVzID0ge1xuICBzcmM6IFByb3BUeXBlcy5zdHJpbmcsXG4gIGFsdDogUHJvcFR5cGVzLnN0cmluZyxcbiAgaG90c3BvdHM6IFByb3BUeXBlcy5hcnJheVxufVxuXG5leHBvcnQgZGVmYXVsdCBJbWFnZUhvdHNwb3RzXG4iXSwibmFtZXMiOlsiSW1hZ2VIb3RzcG90cyIsIlJlYWN0IiwiQ29tcG9uZW50IiwicHJvcHMiLCJjb21wb25lbnREaWRNb3VudCIsImhpZGVGdWxsc2NyZWVuQ29udHJvbCIsImhpZGVab29tQ29udHJvbHMiLCJoaWRlTWluaW1hcCIsImhvdHNwb3RzIiwiYmFja2dyb3VuZCIsIm9mZnNldFdpZHRoIiwid2lkdGgiLCJvZmZzZXRIZWlnaHQiLCJoZWlnaHQiLCJjb250YWluZXIiLCJjdXJyZW50Iiwib3JpZW50YXRpb24iLCJyYXRpbyIsInNldFN0YXRlIiwid2luZG93IiwiYWRkRXZlbnRMaXN0ZW5lciIsIm9uV2luZG93UmVzaXplIiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJyZW1vdmVFdmVudExpc3RlbmVyIiwic3RhcnREcmFnIiwiZXZlbnQiLCJlbGVtZW50IiwiY3Vyc29yWCIsImNsaWVudFgiLCJjdXJzb3JZIiwiY2xpZW50WSIsInN0YXRlIiwiZHJhZ2dpbmciLCJwcmV2ZW50RGVmYXVsdCIsIndoaWxlRHJhZyIsImltYWdlIiwibWluaW1hcCIsImRlbHRhWCIsImRlbHRhWSIsIm5ld09mZnNldFgiLCJvZmZzZXRYIiwibmV3T2Zmc2V0WSIsIm9mZnNldFkiLCJzdG9wRHJhZyIsIm9mZnNldFhNYXgiLCJNYXRoIiwiYWJzIiwib2Zmc2V0WU1heCIsImhhbmRsZU9uSW1hZ2VMb2FkIiwidGFyZ2V0IiwiaW5pdGlhbFdpZHRoIiwiaW5pdGlhbEhlaWdodCIsInJlc2l6YWJsZSIsInByZXZTdGF0ZSIsInNjYWxlIiwiaW5pdGlhbFNpemUiLCJndWlkZVdpZHRoIiwiZ3VpZGVIZWlnaHQiLCJkcmFnZ2FibGUiLCJ6b29tIiwidG9nZ2xlRnVsbHNjcmVlbiIsImZ1bGxzY3JlZW4iLCJyZXF1ZXN0RnVsbHNjcmVlbiIsImV4aXRGdWxsc2NyZWVuIiwicm91bmQiLCJndWlkZURlbHRhWCIsImd1aWRlRGVsdGFZIiwiZ3VpZGVPZmZzZXRYIiwiZ3VpZGVPZmZzZXRZIiwiZ3VpZGVPZmZzZXRYTWF4IiwiZ3VpZGVPZmZzZXRZTWF4IiwibW96UmVxdWVzdEZ1bGxTY3JlZW4iLCJ3ZWJraXRSZXF1ZXN0RnVsbHNjcmVlbiIsIm1zUmVxdWVzdEZ1bGxzY3JlZW4iLCJkb2N1bWVudCIsIm1vekNhbmNlbEZ1bGxTY3JlZW4iLCJ3ZWJraXRFeGl0RnVsbHNjcmVlbiIsIm1zRXhpdEZ1bGxzY3JlZW4iLCJyZW5kZXIiLCJzcmMiLCJhbHQiLCJpbWFnZUxvYWRlZCIsImNvbnRhaW5lclN0eWxlIiwicG9zaXRpb24iLCJvdmVyZmxvdyIsInRleHRBbGlnbiIsImltYWdlU3R5bGUiLCJsZWZ0IiwidG9wIiwiaG90c3BvdHNTdHlsZSIsInJpZ2h0IiwibWFyZ2luIiwicG9pbnRlckV2ZW50cyIsInRvcENvbnRyb2xzU3R5bGUiLCJib3R0b21D