UNPKG

react-image-hotspots

Version:
466 lines (465 loc) 67.2 kB
"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