UNPKG

react-schedule-selector-custom

Version:

A mobile-friendly when2meet-style grid-based schedule selector

406 lines (335 loc) 49.2 kB
"use strict"; require("core-js/modules/web.dom-collections.iterator"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.preventScroll = exports.GridCell = void 0; var React = _interopRequireWildcard(require("react")); var _styledComponents = _interopRequireDefault(require("styled-components")); var _add_minutes = _interopRequireDefault(require("date-fns/add_minutes")); var _add_days = _interopRequireDefault(require("date-fns/add_days")); var _start_of_day = _interopRequireDefault(require("date-fns/start_of_day")); var _is_same_minute = _interopRequireDefault(require("date-fns/is_same_minute")); var _format = _interopRequireDefault(require("date-fns/format")); var _typography = require("./typography"); var _colors = _interopRequireDefault(require("./colors")); var _selectionSchemes = _interopRequireDefault(require("./selection-schemes")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // Import only the methods we need from date-fns in order to keep build size small const Wrapper = _styledComponents.default.div.withConfig({ displayName: "ScheduleSelector__Wrapper", componentId: "sc-1jag5hl-0" })(["display:flex;align-items:center;width:100%;user-select:none;"]); const Grid = _styledComponents.default.div.withConfig({ displayName: "ScheduleSelector__Grid", componentId: "sc-1jag5hl-1" })(["display:grid;grid-template-columns:auto repeat(", ",1fr);grid-template-rows:auto repeat(", ",1fr);column-gap:", ";row-gap:", ";width:100%;"], props => props.columns, props => props.rows, props => props.columnGap, props => props.rowGap); const GridCell = _styledComponents.default.div.withConfig({ displayName: "ScheduleSelector__GridCell", componentId: "sc-1jag5hl-2" })(["place-self:stretch;touch-action:none;"]); exports.GridCell = GridCell; const DateCell = _styledComponents.default.div.withConfig({ displayName: "ScheduleSelector__DateCell", componentId: "sc-1jag5hl-3" })(["width:100%;height:25px;background-color:", ";&:hover{background-color:", ";}"], props => props.selected ? props.selectedColor : props.unselectedColor, props => props.hoveredColor); const DateLabel = (0, _styledComponents.default)(_typography.Subtitle).withConfig({ displayName: "ScheduleSelector__DateLabel", componentId: "sc-1jag5hl-4" })(["@media (max-width:699px){font-size:12px;}margin:0;margin-bottom:4px;"]); const TimeText = (0, _styledComponents.default)(_typography.Text).withConfig({ displayName: "ScheduleSelector__TimeText", componentId: "sc-1jag5hl-5" })(["@media (max-width:699px){font-size:10px;}text-align:right;margin:0;margin-right:4px;"]); const preventScroll = e => { e.preventDefault(); }; exports.preventScroll = preventScroll; class ScheduleSelector extends React.Component { // documentMouseUpHandler: () => void = () => {} // endSelection: () => void = () => {} // handleTouchMoveEvent: (event: React.SyntheticTouchEvent<*>) => void // handleTouchEndEvent: () => void // handleMouseUpEvent: (date: Date) => void // handleMouseEnterEvent: (date: Date) => void // handleSelectionStartEvent: (date: Date) => void static getDerivedStateFromProps(props, state) { // As long as the user isn't in the process of selecting, allow prop changes to re-populate selection state if (state.selectionStart == null) { return { selectionDraft: [...props.selection], dates: ScheduleSelector.computeDatesMatrix(props) }; } return null; } static computeDatesMatrix(props) { const startTime = (0, _start_of_day.default)(props.startDate); const dates = []; for (let d = 0; d < props.numDays; d += 1) { const currentDay = []; let currentTime = (0, _start_of_day.default)((0, _add_days.default)(startTime, d)); // Start at the beginning of the day // Number of chunks in a day const numChunks = 24 * 60 / props.minutesChunks; while (currentDay.length < numChunks) { currentDay.push(new Date(currentTime)); currentTime = (0, _add_minutes.default)(currentTime, props.minutesChunks); // Increment by 'minutes' } dates.push(currentDay); } return dates; } constructor(props) { super(props); this.cellToDate = new Map(); this.gridRef = null; this.renderDateCellWrapper = time => { const startHandler = () => { this.handleSelectionStartEvent(time); }; const selected = Boolean(this.state.selectionDraft.find(a => (0, _is_same_minute.default)(a, time))); return /*#__PURE__*/React.createElement(GridCell, { className: "rgdp__grid-cell", role: "presentation", key: time.toISOString() // Mouse handlers , onMouseDown: startHandler, onMouseEnter: () => { this.handleMouseEnterEvent(time); }, onMouseUp: () => { this.handleMouseUpEvent(time); } // Touch handlers // Since touch events fire on the event where the touch-drag started, there's no point in passing // in the time parameter, instead these handlers will do their job using the default Event // parameters , onTouchStart: startHandler, onTouchMove: this.handleTouchMoveEvent, onTouchEnd: this.handleTouchEndEvent }, this.renderDateCell(time, selected)); }; this.renderDateCell = (time, selected) => { const refSetter = dateCell => { if (dateCell) { this.cellToDate.set(dateCell, time); } }; if (this.props.renderDateCell) { return this.props.renderDateCell(time, selected, refSetter); } else { return /*#__PURE__*/React.createElement(DateCell, { selected: selected, ref: refSetter, selectedColor: this.props.selectedColor, unselectedColor: this.props.unselectedColor, hoveredColor: this.props.hoveredColor }); } }; this.renderTimeLabel = time => { if (this.props.renderTimeLabel) { return this.props.renderTimeLabel(time); } else { return /*#__PURE__*/React.createElement(TimeText, null, (0, _format.default)(time, this.props.timeFormat)); } }; this.renderDateLabel = date => { if (this.props.renderDateLabel) { return this.props.renderDateLabel(date); } else { return /*#__PURE__*/React.createElement(DateLabel, null, (0, _format.default)(date, this.props.dateFormat)); } }; this.state = { selectionDraft: [...this.props.selection], // copy it over selectionType: null, selectionStart: null, isTouchDragging: false, dates: ScheduleSelector.computeDatesMatrix(props) }; this.selectionSchemeHandlers = { linear: _selectionSchemes.default.linear, square: _selectionSchemes.default.square }; this.endSelection = this.endSelection.bind(this); this.handleMouseUpEvent = this.handleMouseUpEvent.bind(this); this.handleMouseEnterEvent = this.handleMouseEnterEvent.bind(this); this.handleTouchMoveEvent = this.handleTouchMoveEvent.bind(this); this.handleTouchEndEvent = this.handleTouchEndEvent.bind(this); this.handleSelectionStartEvent = this.handleSelectionStartEvent.bind(this); } componentDidMount() { // We need to add the endSelection event listener to the document itself in order // to catch the cases where the users ends their mouse-click somewhere besides // the date cells (in which case none of the DateCell's onMouseUp handlers would fire) // // This isn't necessary for touch events since the `touchend` event fires on // the element where the touch/drag started so it's always caught. document.addEventListener('mouseup', this.endSelection); // Prevent page scrolling when user is dragging on the date cells this.cellToDate.forEach((value, dateCell) => { if (dateCell && dateCell.addEventListener) { // @ts-ignore dateCell.addEventListener('touchmove', preventScroll, { passive: false }); } }); } componentWillUnmount() { document.removeEventListener('mouseup', this.endSelection); this.cellToDate.forEach((value, dateCell) => { if (dateCell && dateCell.removeEventListener) { // @ts-ignore dateCell.removeEventListener('touchmove', preventScroll); } }); } // Performs a lookup into this.cellToDate to retrieve the Date that corresponds to // the cell where this touch event is right now. Note that this method will only work // if the event is a `touchmove` event since it's the only one that has a `touches` list. getTimeFromTouchEvent(event) { const { touches } = event; if (!touches || touches.length === 0) return null; const { clientX, clientY } = touches[0]; const targetElement = document.elementFromPoint(clientX, clientY); if (targetElement) { const cellTime = this.cellToDate.get(targetElement); return cellTime !== null && cellTime !== void 0 ? cellTime : null; } return null; } endSelection() { this.props.onChange(this.state.selectionDraft); this.setState({ selectionType: null, selectionStart: null }); } // Given an ending Date, determines all the dates that should be selected in this draft updateAvailabilityDraft(selectionEnd, callback) { const { selectionType, selectionStart } = this.state; if (selectionType === null || selectionStart === null) return; let newSelection = []; if (selectionStart && selectionEnd && selectionType) { newSelection = this.selectionSchemeHandlers[this.props.selectionScheme](selectionStart, selectionEnd, this.state.dates); } let nextDraft = [...this.props.selection]; if (selectionType === 'add') { nextDraft = Array.from(new Set([...nextDraft, ...newSelection])); } else if (selectionType === 'remove') { nextDraft = nextDraft.filter(a => !newSelection.find(b => (0, _is_same_minute.default)(a, b))); } this.setState({ selectionDraft: nextDraft }, callback); } // Isomorphic (mouse and touch) handler since starting a selection works the same way for both classes of user input handleSelectionStartEvent(startTime) { // Check if the startTime cell is selected/unselected to determine if this drag-select should // add values or remove values const timeSelected = this.props.selection.find(a => (0, _is_same_minute.default)(a, startTime)); this.setState({ selectionType: timeSelected ? 'remove' : 'add', selectionStart: startTime }); } handleMouseEnterEvent(time) { // Need to update selection draft on mouseup as well in order to catch the cases // where the user just clicks on a single cell (because no mouseenter events fire // in this scenario) this.updateAvailabilityDraft(time); } handleMouseUpEvent(time) { this.updateAvailabilityDraft(time); // Don't call this.endSelection() here because the document mouseup handler will do it } handleTouchMoveEvent(event) { this.setState({ isTouchDragging: true }); const cellTime = this.getTimeFromTouchEvent(event); if (cellTime) { this.updateAvailabilityDraft(cellTime); } } handleTouchEndEvent() { if (!this.state.isTouchDragging) { // Going down this branch means the user tapped but didn't drag -- which // means the availability draft hasn't yet been updated (since // handleTouchMoveEvent was never called) so we need to do it now this.updateAvailabilityDraft(null, () => { this.endSelection(); }); } else { this.endSelection(); } this.setState({ isTouchDragging: false }); } renderFullDateGrid() { let flattenedDates = []; const numDays = this.state.dates.length; const numTimes = this.state.dates[0].length; for (let j = 0; j < numTimes; j += 1) { for (let i = 0; i < numDays; i += 1) { flattenedDates.push(this.state.dates[i][j]); } } // Filter out any null dates or undefined dates flattenedDates = flattenedDates.filter(a => a !== null && a !== undefined); const dateGridElements = flattenedDates.map(this.renderDateCellWrapper); for (let i = 0; i < numTimes; i += 1) { const index = i * numDays; const time = this.state.dates[0][i]; // Inject the time label at the start of every row dateGridElements.splice(index + i, 0, this.renderTimeLabel(time)); } return [ /*#__PURE__*/ // Empty top left corner React.createElement("div", { key: "topleft" }), // Top row of dates ...this.state.dates.map((dayOfTimes, index) => /*#__PURE__*/React.cloneElement(this.renderDateLabel(dayOfTimes[0]), { key: "date-".concat(index) })), // Every row after that ...dateGridElements.map((element, index) => /*#__PURE__*/React.cloneElement(element, { key: "time-".concat(index) }))]; } render() { return /*#__PURE__*/React.createElement(Wrapper, null, /*#__PURE__*/React.createElement(Grid, { columns: this.state.dates.length, rows: this.state.dates[0].length, columnGap: this.props.columnGap, rowGap: this.props.rowGap, ref: el => { this.gridRef = el; } }, this.renderFullDateGrid())); } } exports.default = ScheduleSelector; ScheduleSelector.defaultProps = { selection: [], selectionScheme: 'square', numDays: 7, minutesChunks: 60, startDate: new Date(), timeFormat: 'ha', dateFormat: 'M/D', columnGap: '4px', rowGap: '4px', selectedColor: _colors.default.blue, unselectedColor: _colors.default.paleBlue, hoveredColor: _colors.default.lightBlue, onChange: () => {} }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvU2NoZWR1bGVTZWxlY3Rvci50c3giXSwibmFtZXMiOlsiV3JhcHBlciIsInN0eWxlZCIsImRpdiIsIkdyaWQiLCJwcm9wcyIsImNvbHVtbnMiLCJyb3dzIiwiY29sdW1uR2FwIiwicm93R2FwIiwiR3JpZENlbGwiLCJEYXRlQ2VsbCIsInNlbGVjdGVkIiwic2VsZWN0ZWRDb2xvciIsInVuc2VsZWN0ZWRDb2xvciIsImhvdmVyZWRDb2xvciIsIkRhdGVMYWJlbCIsIlN1YnRpdGxlIiwiVGltZVRleHQiLCJUZXh0IiwicHJldmVudFNjcm9sbCIsImUiLCJwcmV2ZW50RGVmYXVsdCIsIlNjaGVkdWxlU2VsZWN0b3IiLCJSZWFjdCIsIkNvbXBvbmVudCIsImdldERlcml2ZWRTdGF0ZUZyb21Qcm9wcyIsInN0YXRlIiwic2VsZWN0aW9uU3RhcnQiLCJzZWxlY3Rpb25EcmFmdCIsInNlbGVjdGlvbiIsImRhdGVzIiwiY29tcHV0ZURhdGVzTWF0cml4Iiwic3RhcnRUaW1lIiwic3RhcnREYXRlIiwiZCIsIm51bURheXMiLCJjdXJyZW50RGF5IiwiY3VycmVudFRpbWUiLCJudW1DaHVua3MiLCJtaW51dGVzQ2h1bmtzIiwibGVuZ3RoIiwicHVzaCIsIkRhdGUiLCJjb25zdHJ1Y3RvciIsImNlbGxUb0RhdGUiLCJNYXAiLCJncmlkUmVmIiwicmVuZGVyRGF0ZUNlbGxXcmFwcGVyIiwidGltZSIsInN0YXJ0SGFuZGxlciIsImhhbmRsZVNlbGVjdGlvblN0YXJ0RXZlbnQiLCJCb29sZWFuIiwiZmluZCIsImEiLCJ0b0lTT1N0cmluZyIsImhhbmRsZU1vdXNlRW50ZXJFdmVudCIsImhhbmRsZU1vdXNlVXBFdmVudCIsImhhbmRsZVRvdWNoTW92ZUV2ZW50IiwiaGFuZGxlVG91Y2hFbmRFdmVudCIsInJlbmRlckRhdGVDZWxsIiwicmVmU2V0dGVyIiwiZGF0ZUNlbGwiLCJzZXQiLCJyZW5kZXJUaW1lTGFiZWwiLCJ0aW1lRm9ybWF0IiwicmVuZGVyRGF0ZUxhYmVsIiwiZGF0ZSIsImRhdGVGb3JtYXQiLCJzZWxlY3Rpb25UeXBlIiwiaXNUb3VjaERyYWdnaW5nIiwic2VsZWN0aW9uU2NoZW1lSGFuZGxlcnMiLCJsaW5lYXIiLCJzZWxlY3Rpb25TY2hlbWVzIiwic3F1YXJlIiwiZW5kU2VsZWN0aW9uIiwiYmluZCIsImNvbXBvbmVudERpZE1vdW50IiwiZG9jdW1lbnQiLCJhZGRFdmVudExpc3RlbmVyIiwiZm9yRWFjaCIsInZhbHVlIiwicGFzc2l2ZSIsImNvbXBvbmVudFdpbGxVbm1vdW50IiwicmVtb3ZlRXZlbnRMaXN0ZW5lciIsImdldFRpbWVGcm9tVG91Y2hFdmVudCIsImV2ZW50IiwidG91Y2hlcyIsImNsaWVudFgiLCJjbGllbnRZIiwidGFyZ2V0RWxlbWVudCIsImVsZW1lbnRGcm9tUG9pbnQiLCJjZWxsVGltZSIsImdldCIsIm9uQ2hhbmdlIiwic2V0U3RhdGUiLCJ1cGRhdGVBdmFpbGFiaWxpdHlEcmFmdCIsInNlbGVjdGlvbkVuZCIsImNhbGxiYWNrIiwibmV3U2VsZWN0aW9uIiwic2VsZWN0aW9uU2NoZW1lIiwibmV4dERyYWZ0IiwiQXJyYXkiLCJmcm9tIiwiU2V0IiwiZmlsdGVyIiwiYiIsInRpbWVTZWxlY3RlZCIsInJlbmRlckZ1bGxEYXRlR3JpZCIsImZsYXR0ZW5lZERhdGVzIiwibnVtVGltZXMiLCJqIiwiaSIsInVuZGVmaW5lZCIsImRhdGVHcmlkRWxlbWVudHMiLCJtYXAiLCJpbmRleCIsInNwbGljZSIsImRheU9mVGltZXMiLCJjbG9uZUVsZW1lbnQiLCJrZXkiLCJlbGVtZW50IiwicmVuZGVyIiwiZWwiLCJkZWZhdWx0UHJvcHMiLCJjb2xvcnMiLCJibHVlIiwicGFsZUJsdWUiLCJsaWdodEJsdWUiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBOztBQUNBOztBQUdBOztBQUVBOztBQUNBOztBQUNBOztBQUNBOztBQUVBOztBQUNBOztBQUNBOzs7Ozs7OztBQVZBO0FBYUEsTUFBTUEsT0FBTyxHQUFHQywwQkFBT0Ms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QUFTLEVBQUUsS0FBS3BDLEtBQUwsQ0FBV0csU0FIeEI7QUFJRSxNQUFBLE1BQU0sRUFBRSxLQUFLSCxLQUFMLENBQVdJLE1BSnJCO0FBS0UsTUFBQSxHQUFHLEVBQUVrSCxFQUFFLElBQUk7QUFDVCxhQUFLNUUsT0FBTCxHQUFlNEUsRUFBZjtBQUNEO0FBUEgsT0FTRyxLQUFLZixrQkFBTCxFQVRILENBREYsQ0FERjtBQWVEOztBQTdUaUY7OztBQUEvRHJGLGdCLENBWVpxRyxZLEdBQW1DO0FBQ3hDOUYsRUFBQUEsU0FBUyxFQUFFLEVBRDZCO0FBRXhDc0UsRUFBQUEsZUFBZSxFQUFFLFFBRnVCO0FBR3hDaEUsRUFBQUEsT0FBTyxFQUFFLENBSCtCO0FBSXhDSSxFQUFBQSxhQUFhLEVBQUUsRUFKeUI7QUFLeENOLEVBQUFBLFNBQVMsRUFBRSxJQUFJUyxJQUFKLEVBTDZCO0FBTXhDc0IsRUFBQUEsVUFBVSxFQUFFLElBTjRCO0FBT3hDRyxFQUFBQSxVQUFVLEVBQUUsS0FQNEI7QUFReEM1RCxFQUFBQSxTQUFTLEVBQUUsS0FSNkI7QUFTeENDLEVBQUFBLE1BQU0sRUFBRSxLQVRnQztBQVV4Q0ksRUFBQUEsYUFBYSxFQUFFZ0gsZ0JBQU9DLElBVmtCO0FBV3hDaEgsRUFBQUEsZUFBZSxFQUFFK0csZ0JBQU9FLFFBWGdCO0FBWXhDaEgsRUFBQUEsWUFBWSxFQUFFOEcsZ0JBQU9HLFNBWm1CO0FBYXhDbEMsRUFBQUEsUUFBUSxFQUFFLE1BQU0sQ0FBRTtBQWJzQixDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgc3R5bGVkIGZyb20gJ3N0eWxlZC1jb21wb25lbnRzJ1xuXG4vLyBJbXBvcnQgb25seSB0aGUgbWV0aG9kcyB3ZSBuZWVkIGZyb20gZGF0ZS1mbnMgaW4gb3JkZXIgdG8ga2VlcCBidWlsZCBzaXplIHNtYWxsXG5pbXBvcnQgYWRkTWludXRlcyBmcm9tICdkYXRlLWZucy9hZGRfbWludXRlcydcbmltcG9ydCBhZGRIb3VycyBmcm9tICdkYXRlLWZucy9hZGRfaG91cnMnXG5pbXBvcnQgYWRkRGF5cyBmcm9tICdkYXRlLWZucy9hZGRfZGF5cydcbmltcG9ydCBzdGFydE9mRGF5IGZyb20gJ2RhdGUtZm5zL3N0YXJ0X29mX2RheSdcbmltcG9ydCBpc1NhbWVNaW51dGUgZnJvbSAnZGF0ZS1mbnMvaXNfc2FtZV9taW51dGUnXG5pbXBvcnQgZm9ybWF0RGF0ZSBmcm9tICdkYXRlLWZucy9mb3JtYXQnXG5cbmltcG9ydCB7IFRleHQsIFN1YnRpdGxlIH0gZnJvbSAnLi90eXBvZ3JhcGh5J1xuaW1wb3J0IGNvbG9ycyBmcm9tICcuL2NvbG9ycydcbmltcG9ydCBzZWxlY3Rpb25TY2hlbWVzLCB7IFNlbGVjdGlvblNjaGVtZVR5cGUsIFNlbGVjdGlvblR5cGUgfSBmcm9tICcuL3NlbGVjdGlvbi1zY2hlbWVzJ1xuaW1wb3J0IHsgZ2V0RGF5IH0gZnJvbSAnZGF0ZS1mbnMnXG5cbmNvbnN0IFdyYXBwZXIgPSBzdHlsZWQuZGl2YFxuICBkaXNwbGF5OiBmbGV4O1xuICBhbGlnbi1pdGVtczogY2VudGVyO1xuICB3aWR0aDogMTAwJTtcbiAgdXNlci1zZWxlY3Q6IG5vbmU7XG5gXG5cbmNvbnN0IEdyaWQgPSBzdHlsZWQuZGl2PHsgY29sdW1uczogbnVtYmVyOyByb3dzOiBudW1iZXI7IGNvbHVtbkdhcDogc3RyaW5nOyByb3dHYXA6IHN0cmluZyB9PmBcbiAgZGlzcGxheTogZ3JpZDtcbiAgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiBhdXRvIHJlcGVhdCgke3Byb3BzID0+IHByb3BzLmNvbHVtbnN9LCAxZnIpO1xuICBncmlkLXRlbXBsYXRlLXJvd3M6IGF1dG8gcmVwZWF0KCR7cHJvcHMgPT4gcHJvcHMucm93c30sIDFmcik7XG4gIGNvbHVtbi1nYXA6ICR7cHJvcHMgPT4gcHJvcHMuY29sdW1uR2FwfTtcbiAgcm93LWdhcDogJHtwcm9wcyA9PiBwcm9wcy5yb3dHYXB9O1xuICB3aWR0aDogMTAwJTtcbmBcblxuZXhwb3J0IGNvbnN0IEdyaWRDZWxsID0gc3R5bGVkLmRpdmBcbiAgcGxhY2Utc2VsZjogc3RyZXRjaDtcbiAgdG91Y2gtYWN0aW9uOiBub25lO1xuYFxuXG5jb25zdCBEYXRlQ2VsbCA9IHN0eWxlZC5kaXY8e1xuICBzZWxlY3RlZDogYm9vbGVhblxuICBzZWxlY3RlZENvbG9yOiBzdHJpbmdcbiAgdW5zZWxlY3RlZENvbG9yOiBzdHJpbmdcbiAgaG92ZXJlZENvbG9yOiBzdHJpbmdcbn0+YFxuICB3aWR0aDogMTAwJTtcbiAgaGVpZ2h0OiAyNXB4O1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAke3Byb3BzID0+IChwcm9wcy5zZWxlY3RlZCA/IHByb3BzLnNlbGVjdGVkQ29sb3IgOiBwcm9wcy51bnNlbGVjdGVkQ29sb3IpfTtcblxuICAmOmhvdmVyIHtcbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAke3Byb3BzID0+IHByb3BzLmhvdmVyZWRDb2xvcn07XG4gIH1cbmBcblxuY29uc3QgRGF0ZUxhYmVsID0gc3R5bGVkKFN1YnRpdGxlKWBcbiAgQG1lZGlhIChtYXgtd2lkdGg6IDY5OXB4KSB7XG4gICAgZm9udC1zaXplOiAxMnB4O1xuICB9XG4gIG1hcmdpbjogMDtcbiAgbWFyZ2luLWJvdHRvbTogNHB4O1xuYFxuXG5jb25zdCBUaW1lVGV4dCA9IHN0eWxlZChUZXh0KWBcbiAgQG1lZGlhIChtYXgtd2lkdGg6IDY5OXB4KSB7XG4gICAgZm9udC1zaXplOiAxMHB4O1xuICB9XG4gIHRleHQtYWxpZ246IHJpZ2h0O1xuICBtYXJnaW46IDA7XG4gIG1hcmdpbi1yaWdodDogNHB4O1xuYFxuXG50eXBlIFByb3BzVHlwZSA9IHtcbiAgc2VsZWN0aW9uOiBBcnJheTxEYXRlPlxuICBzZWxlY3Rpb25TY2hlbWU6IFNlbGVjdGlvblNjaGVtZVR5cGVcbiAgb25DaGFuZ2U6IChuZXdTZWxlY3Rpb246IEFycmF5PERhdGU+KSA9PiB2b2lkXG4gIHN0YXJ0RGF0ZTogRGF0ZVxuICBudW1EYXlzOiBudW1iZXJcbiAgbWludXRlc0NodW5rczogbnVtYmVyXG4gIGRhdGVGb3JtYXQ6IHN0cmluZ1xuICB0aW1lRm9ybWF0OiBzdHJpbmdcbiAgY29sdW1uR2FwOiBzdHJpbmdcbiAgcm93R2FwOiBzdHJpbmdcbiAgdW5zZWxlY3RlZENvbG9yOiBzdHJpbmdcbiAgc2VsZWN0ZWRDb2xvcjogc3RyaW5nXG4gIGhvdmVyZWRDb2xvcjogc3RyaW5nXG4gIHJlbmRlckRhdGVDZWxsPzogKGRhdGV0aW1lOiBEYXRlLCBzZWxlY3RlZDogYm9vbGVhbiwgcmVmU2V0dGVyOiAoZGF0ZUNlbGxFbGVtZW50OiBIVE1MRWxlbWVudCkgPT4gdm9pZCkgPT4gSlNYLkVsZW1lbnRcbiAgcmVuZGVyVGltZUxhYmVsPzogKHRpbWU6IERhdGUpID0+IEpTWC5FbGVtZW50XG4gIHJlbmRlckRhdGVMYWJlbD86IChkYXRlOiBEYXRlKSA9PiBKU1guRWxlbWVudFxufVxuXG50eXBlIFN0YXRlVHlwZSA9IHtcbiAgLy8gSW4gdGhlIGNhc2UgdGhhdCBhIHVzZXIgaXMgZHJhZy1zZWxlY3RpbmcsIHdlIGRvbid0IHdhbnQgdG8gY2FsbCB0aGlzLnByb3BzLm9uQ2hhbmdlKCkgdW50aWwgdGhleSBoYXZlIGNvbXBsZXRlZFxuICAvLyB0aGUgZHJhZy1zZWxlY3QuIHNlbGVjdGlvbkRyYWZ0IHNlcnZlcyBhcyBhIHRlbXBvcmFyeSBjb3B5IGR1cmluZyBkcmFnLXNlbGVjdHMuXG4gIHNlbGVjdGlvbkRyYWZ0OiBBcnJheTxEYXRlPlxuICBzZWxlY3Rpb25UeXBlOiBTZWxlY3Rpb25UeXBlIHwgbnVsbFxuICBzZWxlY3Rpb25TdGFydDogRGF0ZSB8IG51bGxcbiAgaXNUb3VjaERyYWdnaW5nOiBib29sZWFuXG4gIGRhdGVzOiBBcnJheTxBcnJheTxEYXRlPj5cbn1cblxuZXhwb3J0IGNvbnN0IHByZXZlbnRTY3JvbGwgPSAoZTogVG91Y2hFdmVudCkgPT4ge1xuICBlLnByZXZlbnREZWZhdWx0KClcbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU2NoZWR1bGVTZWxlY3RvciBleHRlbmRzIFJlYWN0LkNvbXBvbmVudDxQcm9wc1R5cGUsIFN0YXRlVHlwZT4ge1xuICBzZWxlY3Rpb25TY2hlbWVIYW5kbGVyczogeyBba2V5OiBzdHJpbmddOiAoc3RhcnREYXRlOiBEYXRlLCBlbmREYXRlOiBEYXRlLCBmb286IEFycmF5PEFycmF5PERhdGU+PikgPT4gRGF0ZVtdIH1cbiAgY2VsbFRvRGF0ZTogTWFwPEVsZW1lbnQsIERhdGU+ID0gbmV3IE1hcCgpXG4gIC8vIGRvY3VtZW50TW91c2VVcEhhbmRsZXI6ICgpID0+IHZvaWQgPSAoKSA9PiB7fVxuICAvLyBlbmRTZWxlY3Rpb246ICgpID0+IHZvaWQgPSAoKSA9PiB7fVxuICAvLyBoYW5kbGVUb3VjaE1vdmVFdmVudDogKGV2ZW50OiBSZWFjdC5TeW50aGV0aWNUb3VjaEV2ZW50PCo+KSA9PiB2b2lkXG4gIC8vIGhhbmRsZVRvdWNoRW5kRXZlbnQ6ICgpID0+IHZvaWRcbiAgLy8gaGFuZGxlTW91c2VVcEV2ZW50OiAoZGF0ZTogRGF0ZSkgPT4gdm9pZFxuICAvLyBoYW5kbGVNb3VzZUVudGVyRXZlbnQ6IChkYXRlOiBEYXRlKSA9PiB2b2lkXG4gIC8vIGhhbmRsZVNlbGVjdGlvblN0YXJ0RXZlbnQ6IChkYXRlOiBEYXRlKSA9PiB2b2lkXG4gIGdyaWRSZWY6IEhUTUxFbGVtZW50IHwgbnVsbCA9IG51bGxcblxuICBzdGF0aWMgZGVmYXVsdFByb3BzOiBQYXJ0aWFsPFByb3BzVHlwZT4gPSB7XG4gICAgc2VsZWN0aW9uOiBbXSxcbiAgICBzZWxlY3Rpb25TY2hlbWU6ICdzcXVhcmUnLFxuICAgIG51bURheXM6IDcsXG4gICAgbWludXRlc0NodW5rczogNjAsXG4gICAgc3RhcnREYXRlOiBuZXcgRGF0ZSgpLFxuICAgIHRpbWVGb3JtYXQ6ICdoYScsXG4gICAgZGF0ZUZvcm1hdDogJ00vRCcsXG4gICAgY29sdW1uR2FwOiAnNHB4JyxcbiAgICByb3dHYXA6ICc0cHgnLFxuICAgIHNlbGVjdGVkQ29sb3I6IGNvbG9ycy5ibHVlLFxuICAgIHVuc2VsZWN0ZWRDb2xvcjogY29sb3JzLnBhbGVCbHVlLFxuICAgIGhvdmVyZWRDb2xvcjogY29sb3JzLmxpZ2h0Qmx1ZSxcbiAgICBvbkNoYW5nZTogKCkgPT4ge31cbiAgfVxuXG4gIHN0YXRpYyBnZXREZXJpdmVkU3RhdGVGcm9tUHJvcHMocHJvcHM6IFByb3BzVHlwZSwgc3RhdGU6IFN0YXRlVHlwZSk6IFBhcnRpYWw8U3RhdGVUeXBlPiB8IG51bGwge1xuICAgIC8vIEFzIGxvbmcgYXMgdGhlIHVzZXIgaXNuJ3QgaW4gdGhlIHByb2Nlc3Mgb2Ygc2VsZWN0aW5nLCBhbGxvdyBwcm9wIGNoYW5nZXMgdG8gcmUtcG9wdWxhdGUgc2VsZWN0aW9uIHN0YXRlXG4gICAgaWYgKHN0YXRlLnNlbGVjdGlvblN0YXJ0ID09IG51bGwpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHNlbGVjdGlvbkRyYWZ0OiBbLi4ucHJvcHMuc2VsZWN0aW9uXSxcbiAgICAgICAgZGF0ZXM6IFNjaGVkdWxlU2VsZWN0b3IuY29tcHV0ZURhdGVzTWF0cml4KHByb3BzKVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgc3RhdGljIGNvbXB1dGVEYXRlc01hdHJpeChwcm9wczogUHJvcHNUeXBlKTogQXJyYXk8QXJyYXk8RGF0ZT4+IHtcbiAgICBjb25zdCBzdGFydFRpbWUgPSBzdGFydE9mRGF5KHByb3BzLnN0YXJ0RGF0ZSlcbiAgICBjb25zdCBkYXRlczogQXJyYXk8QXJyYXk8RGF0ZT4+ID0gW11cbiAgICBmb3IgKGxldCBkID0gMDsgZCA8IHByb3BzLm51bURheXM7IGQgKz0gMSkge1xuICAgICAgY29uc3QgY3VycmVudERheTogRGF0ZVtdID0gW11cbiAgICAgIGxldCBjdXJyZW50VGltZSA9IHN0YXJ0T2ZEYXkoYWRkRGF5cyhzdGFydFRpbWUsIGQpKSAvLyBTdGFydCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBkYXlcbiAgICAgIC8vIE51bWJlciBvZiBjaHVua3MgaW4gYSBkYXlcbiAgICAgIGNvbnN0IG51bUNodW5rcyA9ICgyNCAqIDYwKSAvIHByb3BzLm1pbnV0ZXNDaHVua3NcbiAgICAgIHdoaWxlIChjdXJyZW50RGF5Lmxlbmd0aCA8IG51bUNodW5rcykge1xuICAgICAgICBjdXJyZW50RGF5LnB1c2gobmV3IERhdGUoY3VycmVudFRpbWUpKVxuICAgICAgICBjdXJyZW50VGltZSA9IGFkZE1pbnV0ZXMoY3VycmVudFRpbWUsIHByb3BzLm1pbnV0ZXNDaHVua3MpIC8vIEluY3JlbWVudCBieSAnbWludXRlcydcbiAgICAgIH1cbiAgICAgIGRhdGVzLnB1c2goY3VycmVudERheSlcbiAgICB9XG4gICAgcmV0dXJuIGRhdGVzXG4gIH1cblxuICBjb25zdHJ1Y3Rvcihwcm9wczogUHJvcHNUeXBlKSB7XG4gICAgc3VwZXIocHJvcHMpXG5cbiAgICB0aGlzLnN0YXRlID0ge1xuICAgICAgc2VsZWN0aW9uRHJhZnQ6IFsuLi50aGlzLnByb3BzLnNlbGVjdGlvbl0sIC8vIGNvcHkgaXQgb3ZlclxuICAgICAgc2VsZWN0aW9uVHlwZTogbnVsbCxcbiAgICAgIHNlbGVjdGlvblN0YXJ0OiBudWxsLFxuICAgICAgaXNUb3VjaERyYWdnaW5nOiBmYWxzZSxcbiAgICAgIGRhdGVzOiBTY2hlZHVsZVNlbGVjdG9yLmNvbXB1dGVEYXRlc01hdHJpeChwcm9wcylcbiAgICB9XG5cbiAgICB0aGlzLnNlbGVjdGlvblNjaGVtZUhhbmRsZXJzID0ge1xuICAgICAgbGluZWFyOiBzZWxlY3Rpb25TY2hlbWVzLmxpbmVhcixcbiAgICAgIHNxdWFyZTogc2VsZWN0aW9uU2NoZW1lcy5zcXVhcmVcbiAgICB9XG5cbiAgICB0aGlzLmVuZFNlbGVjdGlvbiA9IHRoaXMuZW5kU2VsZWN0aW9uLmJpbmQodGhpcylcbiAgICB0aGlzLmhhbmRsZU1vdXNlVXBFdmVudCA9IHRoaXMuaGFuZGxlTW91c2VVcEV2ZW50LmJpbmQodGhpcylcbiAgICB0aGlzLmhhbmRsZU1vdXNlRW50ZXJFdmVudCA9IHRoaXMuaGFuZGxlTW91c2VFbnRlckV2ZW50LmJpbmQodGhpcylcbiAgICB0aGlzLmhhbmRsZVRvdWNoTW92ZUV2ZW50ID0gdGhpcy5oYW5kbGVUb3VjaE1vdmVFdmVudC5iaW5kKHRoaXMpXG4gICAgdGhpcy5oYW5kbGVUb3VjaEVuZEV2ZW50ID0gdGhpcy5oYW5kbGVUb3VjaEVuZEV2ZW50LmJpbmQodGhpcylcbiAgICB0aGlzLmhhbmRsZVNlbGVjdGlvblN0YXJ0RXZlbnQgPSB0aGlzLmhhbmRsZVNlbGVjdGlvblN0YXJ0RXZlbnQuYmluZCh0aGlzKVxuICB9XG5cbiAgY29tcG9uZW50RGlkTW91bnQoKSB7XG4gICAgLy8gV2UgbmVlZCB0byBhZGQgdGhlIGVuZFNlbGVjdGlvbiBldmVudCBsaXN0ZW5lciB0byB0aGUgZG9jdW1lbnQgaXRzZWxmIGluIG9yZGVyXG4gICAgLy8gdG8gY2F0Y2ggdGhlIGNhc2VzIHdoZXJlIHRoZSB1c2VycyBlbmRzIHRoZWlyIG1vdXNlLWNsaWNrIHNvbWV3aGVyZSBiZXNpZGVzXG4gICAgLy8gdGhlIGRhdGUgY2VsbHMgKGluIHdoaWNoIGNhc2Ugbm9uZSBvZiB0aGUgRGF0ZUNlbGwncyBvbk1vdXNlVXAgaGFuZGxlcnMgd291bGQgZmlyZSlcbiAgICAvL1xuICAgIC8vIFRoaXMgaXNuJ3QgbmVjZXNzYXJ5IGZvciB0b3VjaCBldmVudHMgc2luY2UgdGhlIGB0b3VjaGVuZGAgZXZlbnQgZmlyZXMgb25cbiAgICAvLyB0aGUgZWxlbWVudCB3aGVyZSB0aGUgdG91Y2gvZHJhZyBzdGFydGVkIHNvIGl0J3MgYWx3YXlzIGNhdWdodC5cbiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgdGhpcy5lbmRTZWxlY3Rpb24pXG5cbiAgICAvLyBQcmV2ZW50IHBhZ2Ugc2Nyb2xsaW5nIHdoZW4gdXNlciBpcyBkcmFnZ2luZyBvbiB0aGUgZGF0ZSBjZWxsc1xuICAgIHRoaXMuY2VsbFRvRGF0ZS5mb3JFYWNoKCh2YWx1ZSwgZGF0ZUNlbGwpID0+IHtcbiAgICAgIGlmIChkYXRlQ2VsbCAmJiBkYXRlQ2VsbC5hZGRFdmVudExpc3RlbmVyKSB7XG4gICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgZGF0ZUNlbGwuYWRkRXZlbnRMaXN0ZW5lcigndG91Y2htb3ZlJywgcHJldmVudFNjcm9sbCwgeyBwYXNzaXZlOiBmYWxzZSB9KVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICBjb21wb25lbnRXaWxsVW5tb3VudCgpIHtcbiAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgdGhpcy5lbmRTZWxlY3Rpb24pXG4gICAgdGhpcy5jZWxsVG9EYXRlLmZvckVhY2goKHZhbHVlLCBkYXRlQ2VsbCkgPT4ge1xuICAgICAgaWYgKGRhdGVDZWxsICYmIGRhdGVDZWxsLnJlbW92ZUV2ZW50TGlzdGVuZXIpIHtcbiAgICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgICBkYXRlQ2VsbC5yZW1vdmVFdmVudExpc3RlbmVyKCd0b3VjaG1vdmUnLCBwcmV2ZW50U2Nyb2xsKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICAvLyBQZXJmb3JtcyBhIGxvb2t1cCBpbnRvIHRoaXMuY2VsbFRvRGF0ZSB0byByZXRyaWV2ZSB0aGUgRGF0ZSB0aGF0IGNvcnJlc3BvbmRzIHRvXG4gIC8vIHRoZSBjZWxsIHdoZXJlIHRoaXMgdG91Y2ggZXZlbnQgaXMgcmlnaHQgbm93LiBOb3RlIHRoYXQgdGhpcyBtZXRob2Qgd2lsbCBvbmx5IHdvcmtcbiAgLy8gaWYgdGhlIGV2ZW50IGlzIGEgYHRvdWNobW92ZWAgZXZlbnQgc2luY2UgaXQncyB0aGUgb25seSBvbmUgdGhhdCBoYXMgYSBgdG91Y2hlc2AgbGlzdC5cbiAgZ2V0VGltZUZyb21Ub3VjaEV2ZW50KGV2ZW50OiBSZWFjdC5Ub3VjaEV2ZW50PGFueT4pOiBEYXRlIHwgbnVsbCB7XG4gICAgY29uc3QgeyB0b3VjaGVzIH0gPSBldmVudFxuICAgIGlmICghdG91Y2hlcyB8fCB0b3VjaGVzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIG51bGxcbiAgICBjb25zdCB7IGNsaWVudFgsIGNsaWVudFkgfSA9IHRvdWNoZXNbMF1cbiAgICBjb25zdCB0YXJnZXRFbGVtZW50ID0gZG9jdW1lbnQuZWxlbWVudEZyb21Qb2ludChjbGllbnRYLCBjbGllbnRZKVxuICAgIGlmICh0YXJnZXRFbGVtZW50KSB7XG4gICAgICBjb25zdCBjZWxsVGltZSA9IHRoaXMuY2VsbFRvRGF0ZS5nZXQodGFyZ2V0RWxlbWVudClcbiAgICAgIHJldHVybiBjZWxsVGltZSA/PyBudWxsXG4gICAgfVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBlbmRTZWxlY3Rpb24oKSB7XG4gICAgdGhpcy5wcm9wcy5vbkNoYW5nZSh0aGlzLnN0YXRlLnNlbGVjdGlvbkRyYWZ0KVxuICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgc2VsZWN0aW9uVHlwZTogbnVsbCxcbiAgICAgIHNlbGVjdGlvblN0YXJ0OiBudWxsXG4gICAgfSlcbiAgfVxuXG4gIC8vIEdpdmVuIGFuIGVuZGluZyBEYXRlLCBkZXRlcm1pbmVzIGFsbCB0aGUgZGF0ZXMgdGhhdCBzaG91bGQgYmUgc2VsZWN0ZWQgaW4gdGhpcyBkcmFmdFxuICB1cGRhdGVBdmFpbGFiaWxpdHlEcmFmdChzZWxlY3Rpb25FbmQ6IERhdGUgfCBudWxsLCBjYWxsYmFjaz86ICgpID0+IHZvaWQpIHtcbiAgICBjb25zdCB7IHNlbGVjdGlvblR5cGUsIHNlbGVjdGlvblN0YXJ0IH0gPSB0aGlzLnN0YXRlXG5cbiAgICBpZiAoc2VsZWN0aW9uVHlwZSA9PT0gbnVsbCB8fCBzZWxlY3Rpb25TdGFydCA9PT0gbnVsbCkgcmV0dXJuXG5cbiAgICBsZXQgbmV3U2VsZWN0aW9uOiBBcnJheTxEYXRlPiA9IFtdXG4gICAgaWYgKHNlbGVjdGlvblN0YXJ0ICYmIHNlbGVjdGlvbkVuZCAmJiBzZWxlY3Rpb25UeXBlKSB7XG4gICAgICBuZXdTZWxlY3Rpb24gPSB0aGlzLnNlbGVjdGlvblNjaGVtZUhhbmRsZXJzW3RoaXMucHJvcHMuc2VsZWN0aW9uU2NoZW1lXShcbiAgICAgICAgc2VsZWN0aW9uU3RhcnQsXG4gICAgICAgIHNlbGVjdGlvbkVuZCxcbiAgICAgICAgdGhpcy5zdGF0ZS5kYXRlc1xuICAgICAgKVxuICAgIH1cblxuICAgIGxldCBuZXh0RHJhZnQgPSBbLi4udGhpcy5wcm9wcy5zZWxlY3Rpb25dXG4gICAgaWYgKHNlbGVjdGlvblR5cGUgPT09ICdhZGQnKSB7XG4gICAgICBuZXh0RHJhZnQgPSBBcnJheS5mcm9tKG5ldyBTZXQoWy4uLm5leHREcmFmdCwgLi4ubmV3U2VsZWN0aW9uXSkpXG4gICAgfSBlbHNlIGlmIChzZWxlY3Rpb25UeXBlID09PSAncmVtb3ZlJykge1xuICAgICAgbmV4dERyYWZ0ID0gbmV4dERyYWZ0LmZpbHRlcihhID0+ICFuZXdTZWxlY3Rpb24uZmluZChiID0+IGlzU2FtZU1pbnV0ZShhLCBiKSkpXG4gICAgfVxuXG4gICAgdGhpcy5zZXRTdGF0ZSh7IHNlbGVjdGlvbkRyYWZ0OiBuZXh0RHJhZnQgfSwgY2FsbGJhY2spXG4gIH1cblxuICAvLyBJc29tb3JwaGljIChtb3VzZSBhbmQgdG91Y2gpIGhhbmRsZXIgc2luY2Ugc3RhcnRpbmcgYSBzZWxlY3Rpb24gd29ya3MgdGhlIHNhbWUgd2F5IGZvciBib3RoIGNsYXNzZXMgb2YgdXNlciBpbnB1dFxuICBoYW5kbGVTZWxlY3Rpb25TdGFydEV2ZW50KHN0YXJ0VGltZTogRGF0ZSkge1xuICAgIC8vIENoZWNrIGlmIHRoZSBzdGFydFRpbWUgY2VsbCBpcyBzZWxlY3RlZC91bnNlbGVjdGVkIHRvIGRldGVybWluZSBpZiB0aGlzIGRyYWctc2VsZWN0IHNob3VsZFxuICAgIC8vIGFkZCB2YWx1ZXMgb3IgcmVtb3ZlIHZhbHVlc1xuICAgIGNvbnN0IHRpbWVTZWxlY3RlZCA9IHRoaXMucHJvcHMuc2VsZWN0aW9uLmZpbmQoYSA9PiBpc1NhbWVNaW51dGUoYSwgc3RhcnRUaW1lKSlcbiAgICB0aGlzLnNldFN0YXRlKHtcbiAgICAgIHNlbGVjdGlvblR5cGU6IHRpbWVTZWxlY3RlZCA/ICdyZW1vdmUnIDogJ2FkZCcsXG4gICAgICBzZWxlY3Rpb25TdGFydDogc3RhcnRUaW1lXG4gICAgfSlcbiAgfVxuXG4gIGhhbmRsZU1vdXNlRW50ZXJFdmVudCh0aW1lOiBEYXRlKSB7XG4gICAgLy8gTmVlZCB0byB1cGRhdGUgc2VsZWN0aW9uIGRyYWZ0IG9uIG1vdXNldXAgYXMgd2VsbCBpbiBvcmRlciB0byBjYXRjaCB0aGUgY2FzZXNcbiAgICAvLyB3aGVyZSB0aGUgdXNlciBqdXN0IGNsaWNrcyBvbiBhIHNpbmdsZSBjZWxsIChiZWNhdXNlIG5vIG1vdXNlZW50ZXIgZXZlbnRzIGZpcmVcbiAgICAvLyBpbiB0aGlzIHNjZW5hcmlvKVxuICAgIHRoaXMudXBkYXRlQXZhaWxhYmlsaXR5RHJhZnQodGltZSlcbiAgfVxuXG4gIGhhbmRsZU1vdXNlVXBFdmVudCh0aW1lOiBEYXRlKSB7XG4gICAgdGhpcy51cGRhdGVBdmFpbGFiaWxpdHlEcmFmdCh0aW1lKVxuICAgIC8vIERvbid0IGNhbGwgdGhpcy5lbmRTZWxlY3Rpb24oKSBoZXJlIGJlY2F1c2UgdGhlIGRvY3VtZW50IG1vdXNldXAgaGFuZGxlciB3aWxsIGRvIGl0XG4gIH1cblxuICBoYW5kbGVUb3VjaE1vdmVFdmVudChldmVudDogUmVhY3QuVG91Y2hFdmVudCkge1xuICAgIHRoaXMuc2V0U3RhdGUoeyBpc1RvdWNoRHJhZ2dpbmc6IHRydWUgfSlcbiAgICBjb25zdCBjZWxsVGltZSA9IHRoaXMuZ2V0VGltZUZyb21Ub3VjaEV2ZW50KGV2ZW50KVxuICAgIGlmIChjZWxsVGltZSkge1xuICAgICAgdGhpcy51cGRhdGVBdmFpbGFiaWxpdHlEcmFmdChjZWxsVGltZSlcbiAgICB9XG4gIH1cblxuICBoYW5kbGVUb3VjaEVuZEV2ZW50KCkge1xuICAgIGlmICghdGhpcy5zdGF0ZS5pc1RvdWNoRHJhZ2dpbmcpIHtcbiAgICAgIC8vIEdvaW5nIGRvd24gdGhpcyBicmFuY2ggbWVhbnMgdGhlIHVzZXIgdGFwcGVkIGJ1dCBkaWRuJ3QgZHJhZyAtLSB3aGljaFxuICAgICAgLy8gbWVhbnMgdGhlIGF2YWlsYWJpbGl0eSBkcmFmdCBoYXNuJ3QgeWV0IGJlZW4gdXBkYXRlZCAoc2luY2VcbiAgICAgIC8vIGhhbmRsZVRvdWNoTW92ZUV2ZW50IHdhcyBuZXZlciBjYWxsZWQpIHNvIHdlIG5lZWQgdG8gZG8gaXQgbm93XG4gICAgICB0aGlzLnVwZGF0ZUF2YWlsYWJpbGl0eURyYWZ0KG51bGwsICgpID0+IHtcbiAgICAgICAgdGhpcy5lbmRTZWxlY3Rpb24oKVxuICAgICAgfSlcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5lbmRTZWxlY3Rpb24oKVxuICAgIH1cbiAgICB0aGlzLnNldFN0YXRlKHsgaXNUb3VjaERyYWdnaW5nOiBmYWxzZSB9KVxuICB9XG5cbiAgcmVuZGVyRGF0ZUNlbGxXcmFwcGVyID0gKHRpbWU6IERhdGUpOiBKU1guRWxlbWVudCA9PiB7XG4gICAgY29uc3Qgc3RhcnRIYW5kbGVyID0gKCkgPT4ge1xuICAgICAgdGhpcy5oYW5kbGVTZWxlY3Rpb25TdGFydEV2ZW50KHRpbWUpXG4gICAgfVxuXG4gICAgY29uc3Qgc2VsZWN0ZWQgPSBCb29sZWFuKHRoaXMuc3RhdGUuc2VsZWN0aW9uRHJhZnQuZmluZChhID0+IGlzU2FtZU1pbnV0ZShhLCB0aW1lKSkpXG5cbiAgICByZXR1cm4gKFxuICAgICAgPEdyaWRDZWxsXG4gICAgICAgIGNsYXNzTmFtZT1cInJnZHBfX2dyaWQtY2VsbFwiXG4gICAgICAgIHJvbGU9XCJwcmVzZW50YXRpb25cIlxuICAgICAgICBrZXk9e3RpbWUudG9JU09TdHJpbmcoKX1cbiAgICAgICAgLy8gTW91c2UgaGFuZGxlcnNcbiAgICAgICAgb25Nb3VzZURvd249e3N0YXJ0SGFuZGxlcn1cbiAgICAgICAgb25Nb3VzZUVudGVyPXsoKSA9PiB7XG4gICAgICAgICAgdGhpcy5oYW5kbGVNb3VzZUVudGVyRXZlbnQodGltZSlcbiAgICAgICAgfX1cbiAgICAgICAgb25Nb3VzZVVwPXsoKSA9PiB7XG4gICAgICAgICAgdGhpcy5oYW5kbGVNb3VzZVVwRXZlbnQodGltZSlcbiAgICAgICAgfX1cbiAgICAgICAgLy8gVG91Y2ggaGFuZGxlcnNcbiAgICAgICAgLy8gU2luY2UgdG91Y2ggZXZlbnRzIGZpcmUgb24gdGhlIGV2ZW50IHdoZXJlIHRoZSB0b3VjaC1kcmFnIHN0YXJ0ZWQsIHRoZXJlJ3Mgbm8gcG9pbnQgaW4gcGFzc2luZ1xuICAgICAgICAvLyBpbiB0aGUgdGltZSBwYXJhbWV0ZXIsIGluc3RlYWQgdGhlc2UgaGFuZGxlcnMgd2lsbCBkbyB0aGVpciBqb2IgdXNpbmcgdGhlIGRlZmF1bHQgRXZlbnRcbiAgICAgICAgLy8gcGFyYW1ldGVyc1xuICAgICAgICBvblRvdWNoU3RhcnQ9e3N0YXJ0SGFuZGxlcn1cbiAgICAgICAgb25Ub3VjaE1vdmU9e3RoaXMuaGFuZGxlVG91Y2hNb3ZlRXZlbnR9XG4gICAgICAgIG9uVG91Y2hFbmQ9e3RoaXMuaGFuZGxlVG91Y2hFbmRFdmVudH1cbiAgICAgID5cbiAgICAgICAge3RoaXMucmVuZGVyRGF0ZUNlbGwodGltZSwgc2VsZWN0ZWQpfVxuICAgICAgPC9HcmlkQ2VsbD5cbiAgICApXG4gIH1cblxuICByZW5kZXJEYXRlQ2VsbCA9ICh0aW1lOiBEYXRlLCBzZWxlY3RlZDogYm9vbGVhbik6IEpTWC5FbGVtZW50ID0+IHtcbiAgICBjb25zdCByZWZTZXR0ZXIgPSAoZGF0ZUNlbGw6IEhUTUxFbGVtZW50IHwgbnVsbCkgPT4ge1xuICAgICAgaWYgKGRhdGVDZWxsKSB7XG4gICAgICAgIHRoaXMuY2VsbFRvRGF0ZS5zZXQoZGF0ZUNlbGwsIHRpbWUpXG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aGlzLnByb3BzLnJlbmRlckRhdGVDZWxsKSB7XG4gICAgICByZXR1cm4gdGhpcy5wcm9wcy5yZW5kZXJEYXRlQ2VsbCh0aW1lLCBzZWxlY3RlZCwgcmVmU2V0dGVyKVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8RGF0ZUNlbGxcbiAgICAgICAgICBzZWxlY3RlZD17c2VsZWN0ZWR9XG4gICAgICAgICAgcmVmPXtyZWZTZXR0ZXJ9XG4gICAgICAgICAgc2VsZWN0ZWRDb2xvcj17dGhpcy5wcm9wcy5zZWxlY3RlZENvbG9yfVxuICAgICAgICAgIHVuc2VsZWN0ZWRDb2xvcj17dGhpcy5wcm9wcy51bnNlbGVjdGVkQ29sb3J9XG4gICAgICAgICAgaG92ZXJlZENvbG9yPXt0aGlzLnByb3BzLmhvdmVyZWRDb2xvcn1cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICB9XG4gIH1cblxuICByZW5kZXJUaW1lTGFiZWwgPSAodGltZTogRGF0ZSk6IEpTWC5FbGVtZW50ID0+IHtcbiAgICBpZiAodGhpcy5wcm9wcy5yZW5kZXJUaW1lTGFiZWwpIHtcbiAgICAgIHJldHVybiB0aGlzLnByb3BzLnJlbmRlclRpbWVMYWJlbCh0aW1lKVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gPFRpbWVUZXh0Pntmb3JtYXREYXRlKHRpbWUsIHRoaXMucHJvcHMudGltZUZvcm1hdCl9PC9UaW1lVGV4dD5cbiAgICB9XG4gIH1cblxuICByZW5kZXJEYXRlTGFiZWwgPSAoZGF0ZTogRGF0ZSk6IEpTWC5FbGVtZW50ID0+IHtcbiAgICBpZiAodGhpcy5wcm9wcy5yZW5kZXJEYXRlTGFiZWwpIHtcbiAgICAgIHJldHVybiB0aGlzLnByb3BzLnJlbmRlckRhdGVMYWJlbChkYXRlKVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gPERhdGVMYWJlbD57Zm9ybWF0RGF0ZShkYXRlLCB0aGlzLnByb3BzLmRhdGVGb3JtYXQpfTwvRGF0ZUxhYmVsPlxuICAgIH1cbiAgfVxuXG4gIHJlbmRlckZ1bGxEYXRlR3JpZCgpOiBBcnJheTxKU1guRWxlbWVudD4ge1xuICAgIGxldCBmbGF0dGVuZWREYXRlczogRGF0ZVtdID0gW11cbiAgICBjb25zdCBudW1EYXlzID0gdGhpcy5zdGF0ZS5kYXRlcy5sZW5ndGhcbiAgICBjb25zdCBudW1UaW1lcyA9IHRoaXMuc3RhdGUuZGF0ZXNbMF0ubGVuZ3RoXG4gICAgZm9yIChsZXQgaiA9IDA7IGogPCBudW1UaW1lczsgaiArPSAxKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG51bURheXM7IGkgKz0gMSkge1xuICAgICAgICBmbGF0dGVuZWREYXRlcy5wdXNoKHRoaXMuc3RhdGUuZGF0ZXNbaV1bal0pXG4gICAgICB9XG4gICAgfVxuICAgIC8vIEZpbHRlciBvdXQgYW55IG51bGwgZGF0ZXMgb3IgdW5kZWZpbmVkIGRhdGVzXG4gICAgZmxhdHRlbmVkRGF0ZXMgPSBmbGF0dGVuZWREYXRlcy5maWx0ZXIoYSA9PiBhICE9PSBudWxsICYmIGEgIT09IHVuZGVmaW5lZClcbiAgICBjb25zdCBkYXRlR3JpZEVsZW1lbnRzID0gZmxhdHRlbmVkRGF0ZXMubWFwKHRoaXMucmVuZGVyRGF0ZUNlbGxXcmFwcGVyKVxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbnVtVGltZXM7IGkgKz0gMSkge1xuICAgICAgY29uc3QgaW5kZXggPSBpICogbnVtRGF5c1xuICAgICAgY29uc3QgdGltZSA9IHRoaXMuc3RhdGUuZGF0ZXNbMF1baV1cbiAgICAgIC8vIEluamVjdCB0aGUgdGltZSBsYWJlbCBhdCB0aGUgc3RhcnQgb2YgZXZlcnkgcm93XG4gICAgICBkYXRlR3JpZEVsZW1lbnRzLnNwbGljZShpbmRleCArIGksIDAsIHRoaXMucmVuZGVyVGltZUxhYmVsKHRpbWUpKVxuICAgIH1cbiAgICByZXR1cm4gW1xuICAgICAgLy8gRW1wdHkgdG9wIGxlZnQgY29ybmVyXG4gICAgICA8ZGl2IGtleT1cInRvcGxlZnRcIiAvPixcbiAgICAgIC8vIFRvcCByb3cgb2YgZGF0ZXNcbiAgICAgIC4uLnRoaXMuc3RhdGUuZGF0ZXMubWFwKChkYXlPZlRpbWVzLCBpbmRleCkgPT5cbiAgICAgICAgUmVhY3QuY2xvbmVFbGVtZW50KHRoaXMucmVuZGVyRGF0ZUxhYmVsKGRheU9mVGltZXNbMF0pLCB7IGtleTogYGRhdGUtJHtpbmRleH1gIH0pXG4gICAgICApLFxuICAgICAgLy8gRXZlcnkgcm93IGFmdGVyIHRoYXRcbiAgICAgIC4uLmRhdGVHcmlkRWxlbWVudHMubWFwKChlbGVtZW50LCBpbmRleCkgPT4gUmVhY3QuY2xvbmVFbGVtZW50KGVsZW1lbnQsIHsga2V5OiBgdGltZS0ke2luZGV4fWAgfSkpXG4gICAgXVxuICB9XG5cbiAgcmVuZGVyKCk6IEpTWC5FbGVtZW50IHtcbiAgICByZXR1cm4gKFxuICAgICAgPFdyYXBwZXI+XG4gICAgICAgIDxHcmlkXG4gICAgICAgICAgY29sdW1ucz17dGhpcy5zdGF0ZS5kYXRlcy5sZW5ndGh9XG4gICAgICAgICAgcm93cz17dGhpcy5zdGF0ZS5kYXRlc1swXS5sZW5ndGh9XG4gICAgICAgICAgY29sdW1uR2FwPXt0aGlzLnByb3BzLmNvbHVtbkdhcH1cbiAgICAgICAgICByb3dHYXA9e3RoaXMucHJvcHMucm93R2FwfVxuICAgICAgICAgIHJlZj17ZWwgPT4ge1xuICAgICAgICAgICAgdGhpcy5ncmlkUmVmID0gZWxcbiAgICAgICAgICB9fVxuICAgICAgICA+XG4gICAgICAgICAge3RoaXMucmVuZGVyRnVsbERhdGVHcmlkKCl9XG4gICAgICAgIDwvR3JpZD5cbiAgICAgIDwvV3JhcHBlcj5cbiAgICApXG4gIH1cbn1cbiJdfQ==