UNPKG

@ux-aspects/ux-aspects

Version:

Open source user interface framework for building modern, responsive, mobile big data applications

209 lines 40.8 kB
import { WeekDay } from '@angular/common'; import { ChangeDetectionStrategy, Component, DestroyRef, EventEmitter, inject, Input, Output, } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { Subject } from 'rxjs'; import { debounceTime, filter } from 'rxjs/operators'; import { timezones as defaultTimezones, differenceBetweenDates, } from '../date-time-picker/date-time-picker.utils'; import { DateRangePicker, DateRangeService } from './date-range.service'; import * as i0 from "@angular/core"; import * as i1 from "../date-time-picker/date-time-picker.component"; import * as i2 from "../icon/icon.component"; import * as i3 from "./date-range-picker.directive"; import * as i4 from "../../pipes/date-formatter/date-formatter.pipe"; export class DateRangePickerComponent { /** The selected start date to be displayed in the component. */ set start(start) { this.rangeService.start = start; } /** The selected end date to be displayed in the component. */ set end(end) { this.rangeService.end = end; } /** Defines the aria label for the range start picker */ set startPickerAriaLabel(label) { this.rangeService.startPickerAriaLabel = label; } /** Defines the aria label for the range end picker */ set endPickerAriaLabel(label) { this.rangeService.endPickerAriaLabel = label; } /** Defines whether or not the time picker should be visible. */ set showTime(showTime) { this.rangeService.showTime = showTime; } get showTime() { return this.rangeService.showTime; } /** Calculate the number of days between the start and end date */ get _duration() { if (this.rangeService.start && this.rangeService.end) { return differenceBetweenDates(this.rangeService.start, this.rangeService.end, false); } if (this.rangeService.start && !this.rangeService.end && this.rangeService.hover) { // apply the time from the time picker const hoverDate = new Date(this.rangeService.hover); hoverDate.setHours(this.rangeService.endTime.hours, this.rangeService.endTime.minutes, this.rangeService.endTime.seconds); return this.rangeService.start.getTime() <= hoverDate.getTime() ? differenceBetweenDates(this.rangeService.start, hoverDate, false) : null; } // if we only have one selected date and have a hover date if (this.rangeService.end && !this.rangeService.start && this.rangeService.hover) { // apply the time from the time picker const hoverDate = new Date(this.rangeService.hover); hoverDate.setHours(this.rangeService.startTime.hours, this.rangeService.startTime.minutes, this.rangeService.startTime.seconds); return this.rangeService.end.getTime() >= hoverDate.getTime() ? differenceBetweenDates(this.rangeService.end, hoverDate, false) : null; } } constructor() { this.rangeService = inject(DateRangeService); this._destroyRef = inject(DestroyRef); /** Expose enum to the view */ this.DateRangePicker = DateRangePicker; /** Defines whether or not the time picker should allow the user to specify seconds. */ this.showSeconds = false; /** Defines whether or not the time picker should show an AM/PM button, or time should be represented in 24hr format instead. */ this.showMeridian = true; /** Defines whether or not the time picker should allow the user to select the time using spinners. */ this.showSpinners = true; /** If defined will override the weekday names displayed. */ this.weekdays = ['S', 'M', 'T', 'W', 'T', 'F', 'S']; /** Specify whether or not the show now button should be visible */ this.showNowBtn = false; /** Defines the title to display above the start picker. */ this.selectStartTitle = 'Select Start Date'; /** Defines the title to display above the end picker. */ this.selectEndTitle = 'Select End Date'; /** Define the aria label for the now button */ this.nowBtnAriaLabel = 'Set date to now'; /** * Defines the list of available timezones. The `DateTimePickerTimezone` interface specifies that each timezone should * be an object with a `name` property that represents the timezone, eg. `GMT+2`, and an `offset` property that represents * the number of minutes relative to GMT the timezone is. */ this.timezones = defaultTimezones; /** Will set the selected start timezone. */ this.startTimezone = this.getCurrentTimezone(); /** Will set the selected end timezone. */ this.endTimezone = this.getCurrentTimezone(); /** Defines the day of the week that should appear in the first column. `WeekDay` is an enumeration available in `@angular/common`. */ this.startOfWeek = WeekDay.Sunday; /** Define a function to return the number of days within the selected range */ this.durationTitle = this.getDurationTitle; /** Emit when the start date changes */ this.startChange = new EventEmitter(); /** Emit when the end date changes */ this.endChange = new EventEmitter(); /** Emit when the start timezone changes. */ this.startTimezoneChange = new EventEmitter(); /** Emit when the end timezone changes. */ this.endTimezoneChange = new EventEmitter(); /** Use an observable to debounce rapid start changes */ this.startChange$ = new Subject(); /** Use an observable to debounce rapid end changes */ this.endChange$ = new Subject(); /** Unsubscribe from all observables private */ this._onDestroy = new Subject(); this.startChange$ .pipe(debounceTime(0), filter(date => date !== undefined), takeUntilDestroyed(this._destroyRef)) .subscribe(date => this.onStartChange(date)); this.endChange$ .pipe(debounceTime(0), filter(date => date !== undefined), takeUntilDestroyed(this._destroyRef)) .subscribe(date => this.onEndChange(date)); } ngOnDestroy() { this._onDestroy.next(); this._onDestroy.complete(); } /** Clear the selected date range */ clear() { this.rangeService.clear(); } /** Get the timezone based on the machine timezone */ getCurrentTimezone() { return this.timezones.find(timezone => timezone.offset === new Date().getTimezoneOffset()); } onStartChange(date) { this.rangeService.setStartDate(date); this.startChange.emit(date); } onEndChange(date) { this.rangeService.setEndDate(date); this.endChange.emit(date); } /** Get the text to display to indicate the duration */ getDurationTitle(days) { return days + ' ' + (days > 1 ? 'days' : 'day'); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: DateRangePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: DateRangePickerComponent, selector: "ux-date-range-picker", inputs: { start: "start", end: "end", dateFormat: "dateFormat", timeFormat: "timeFormat", min: "min", max: "max", showTimezone: "showTimezone", showSeconds: "showSeconds", showMeridian: "showMeridian", showSpinners: "showSpinners", weekdays: "weekdays", months: "months", monthsShort: "monthsShort", meridians: "meridians", nowBtnText: "nowBtnText", showNowBtn: "showNowBtn", selectStartTitle: "selectStartTitle", selectEndTitle: "selectEndTitle", nowBtnAriaLabel: "nowBtnAriaLabel", startPickerAriaLabel: "startPickerAriaLabel", endPickerAriaLabel: "endPickerAriaLabel", showTime: "showTime", timezones: "timezones", startTimezone: "startTimezone", endTimezone: "endTimezone", startOfWeek: "startOfWeek", durationTitle: "durationTitle" }, outputs: { startChange: "startChange", endChange: "endChange", startTimezoneChange: "startTimezoneChange", endTimezoneChange: "endTimezoneChange" }, providers: [DateRangeService], ngImport: i0, template: "<div class=\"range-header\">\n\n <div class=\"header-section\">\n @if (!rangeService.start) {\n <div class=\"select-header\">{{ selectStartTitle }}</div>\n }\n @if (rangeService.start) {\n <div class=\"date-header\">\n {{ rangeService.start | formatDate: dateFormat || 'd MMMM y' }}\n </div>\n }\n <div [style.visibility]=\"rangeService.start && showTime ? 'visible' : 'hidden'\" class=\"time-header\">\n {{ rangeService.start | formatDate: timeFormat || (showMeridian ? 'shortTime' : 'HH:mm') }}\n </div>\n </div>\n\n <div class=\"header-separator\">\n <ux-icon name=\"link-next\"></ux-icon>\n <p class=\"duration\"\n [style.visibility]=\"_duration !== null && _duration !== undefined ? 'visible' : 'hidden'\">\n {{ durationTitle(_duration || 0) }}\n </p>\n </div>\n\n <div class=\"header-section\">\n @if (!rangeService.end) {\n <div class=\"select-header\">{{ selectEndTitle }}</div>\n }\n @if (rangeService.end) {\n <div class=\"date-header\">\n {{ rangeService.end | formatDate: dateFormat || 'd MMMM y'}}\n </div>\n }\n <div [style.visibility]=\"rangeService.end && showTime ? 'visible' : 'hidden'\" class=\"time-header\">\n {{ rangeService.end | formatDate: timeFormat || (showMeridian ? 'shortTime' : 'HH:mm') }}\n </div>\n </div>\n\n</div>\n\n<div class=\"content\">\n <ux-date-time-picker\n uxDateRangePicker\n [picker]=\"DateRangePicker.Start\"\n class=\"start-date-picker\"\n [date]=\"rangeService.start\"\n (dateChange)=\"startChange$.next($event)\"\n [min]=\"min\"\n [max]=\"max\"\n [weekdays]=\"weekdays\"\n [startOfWeek]=\"startOfWeek\"\n [showTime]=\"showTime\"\n [showTimezone]=\"showTimezone\"\n [showSeconds]=\"showSeconds\"\n [showMeridian]=\"showMeridian\"\n [showSpinners]=\"showSpinners\"\n [months]=\"months\"\n [monthsShort]=\"monthsShort\"\n [meridians]=\"meridians\"\n [nowBtnText]=\"nowBtnText\"\n [showNowBtn]=\"showNowBtn\"\n [nowBtnAriaLabel]=\"nowBtnAriaLabel\"\n [timezones]=\"timezones\"\n [(timezone)]=\"startTimezone\"\n (timezoneChange)=\"startTimezoneChange.emit($event)\">\n </ux-date-time-picker>\n\n <ux-date-time-picker\n uxDateRangePicker\n [picker]=\"DateRangePicker.End\"\n class=\"end-date-picker\"\n [date]=\"rangeService.end\"\n (dateChange)=\"endChange$.next($event)\"\n [min]=\"min\"\n [max]=\"max\"\n [weekdays]=\"weekdays\"\n [startOfWeek]=\"startOfWeek\"\n [showTime]=\"showTime\"\n [showTimezone]=\"showTimezone\"\n [showSeconds]=\"showSeconds\"\n [showMeridian]=\"showMeridian\"\n [showSpinners]=\"showSpinners\"\n [months]=\"months\"\n [monthsShort]=\"monthsShort\"\n [meridians]=\"meridians\"\n [nowBtnText]=\"nowBtnText\"\n [showNowBtn]=\"showNowBtn\"\n [nowBtnAriaLabel]=\"nowBtnAriaLabel\"\n [timezones]=\"timezones\"\n [(timezone)]=\"endTimezone\"\n (timezoneChange)=\"endTimezoneChange.emit($event)\">\n </ux-date-time-picker>\n</div>\n", dependencies: [{ kind: "component", type: i1.DateTimePickerComponent, selector: "ux-date-time-picker", inputs: ["showDate", "showTime", "showTimezone", "showSeconds", "showMeridian", "showSpinners", "weekdays", "months", "monthsShort", "meridians", "nowBtnText", "showNowBtn", "timezones", "startOfWeek", "nowBtnAriaLabel", "date", "timezone", "min", "max"], outputs: ["dateChange", "timezoneChange"] }, { kind: "component", type: i2.IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: i3.DateRangePickerDirective, selector: "[uxDateRangePicker]", inputs: ["picker"] }, { kind: "pipe", type: i4.DateFormatterPipe, name: "formatDate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: DateRangePickerComponent, decorators: [{ type: Component, args: [{ selector: 'ux-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, providers: [DateRangeService], template: "<div class=\"range-header\">\n\n <div class=\"header-section\">\n @if (!rangeService.start) {\n <div class=\"select-header\">{{ selectStartTitle }}</div>\n }\n @if (rangeService.start) {\n <div class=\"date-header\">\n {{ rangeService.start | formatDate: dateFormat || 'd MMMM y' }}\n </div>\n }\n <div [style.visibility]=\"rangeService.start && showTime ? 'visible' : 'hidden'\" class=\"time-header\">\n {{ rangeService.start | formatDate: timeFormat || (showMeridian ? 'shortTime' : 'HH:mm') }}\n </div>\n </div>\n\n <div class=\"header-separator\">\n <ux-icon name=\"link-next\"></ux-icon>\n <p class=\"duration\"\n [style.visibility]=\"_duration !== null && _duration !== undefined ? 'visible' : 'hidden'\">\n {{ durationTitle(_duration || 0) }}\n </p>\n </div>\n\n <div class=\"header-section\">\n @if (!rangeService.end) {\n <div class=\"select-header\">{{ selectEndTitle }}</div>\n }\n @if (rangeService.end) {\n <div class=\"date-header\">\n {{ rangeService.end | formatDate: dateFormat || 'd MMMM y'}}\n </div>\n }\n <div [style.visibility]=\"rangeService.end && showTime ? 'visible' : 'hidden'\" class=\"time-header\">\n {{ rangeService.end | formatDate: timeFormat || (showMeridian ? 'shortTime' : 'HH:mm') }}\n </div>\n </div>\n\n</div>\n\n<div class=\"content\">\n <ux-date-time-picker\n uxDateRangePicker\n [picker]=\"DateRangePicker.Start\"\n class=\"start-date-picker\"\n [date]=\"rangeService.start\"\n (dateChange)=\"startChange$.next($event)\"\n [min]=\"min\"\n [max]=\"max\"\n [weekdays]=\"weekdays\"\n [startOfWeek]=\"startOfWeek\"\n [showTime]=\"showTime\"\n [showTimezone]=\"showTimezone\"\n [showSeconds]=\"showSeconds\"\n [showMeridian]=\"showMeridian\"\n [showSpinners]=\"showSpinners\"\n [months]=\"months\"\n [monthsShort]=\"monthsShort\"\n [meridians]=\"meridians\"\n [nowBtnText]=\"nowBtnText\"\n [showNowBtn]=\"showNowBtn\"\n [nowBtnAriaLabel]=\"nowBtnAriaLabel\"\n [timezones]=\"timezones\"\n [(timezone)]=\"startTimezone\"\n (timezoneChange)=\"startTimezoneChange.emit($event)\">\n </ux-date-time-picker>\n\n <ux-date-time-picker\n uxDateRangePicker\n [picker]=\"DateRangePicker.End\"\n class=\"end-date-picker\"\n [date]=\"rangeService.end\"\n (dateChange)=\"endChange$.next($event)\"\n [min]=\"min\"\n [max]=\"max\"\n [weekdays]=\"weekdays\"\n [startOfWeek]=\"startOfWeek\"\n [showTime]=\"showTime\"\n [showTimezone]=\"showTimezone\"\n [showSeconds]=\"showSeconds\"\n [showMeridian]=\"showMeridian\"\n [showSpinners]=\"showSpinners\"\n [months]=\"months\"\n [monthsShort]=\"monthsShort\"\n [meridians]=\"meridians\"\n [nowBtnText]=\"nowBtnText\"\n [showNowBtn]=\"showNowBtn\"\n [nowBtnAriaLabel]=\"nowBtnAriaLabel\"\n [timezones]=\"timezones\"\n [(timezone)]=\"endTimezone\"\n (timezoneChange)=\"endTimezoneChange.emit($event)\">\n </ux-date-time-picker>\n</div>\n" }] }], ctorParameters: () => [], propDecorators: { start: [{ type: Input }], end: [{ type: Input }], dateFormat: [{ type: Input }], timeFormat: [{ type: Input }], min: [{ type: Input }], max: [{ type: Input }], showTimezone: [{ type: Input }], showSeconds: [{ type: Input }], showMeridian: [{ type: Input }], showSpinners: [{ type: Input }], weekdays: [{ type: Input }], months: [{ type: Input }], monthsShort: [{ type: Input }], meridians: [{ type: Input }], nowBtnText: [{ type: Input }], showNowBtn: [{ type: Input }], selectStartTitle: [{ type: Input }], selectEndTitle: [{ type: Input }], nowBtnAriaLabel: [{ type: Input }], startPickerAriaLabel: [{ type: Input }], endPickerAriaLabel: [{ type: Input }], showTime: [{ type: Input }], timezones: [{ type: Input }], startTimezone: [{ type: Input }], endTimezone: [{ type: Input }], startOfWeek: [{ type: Input }], durationTitle: [{ type: Input }], startChange: [{ type: Output }], endChange: [{ type: Output }], startTimezoneChange: [{ type: Output }], endTimezoneChange: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZS1yYW5nZS1waWNrZXIuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvZGF0ZS1yYW5nZS1waWNrZXIvZGF0ZS1yYW5nZS1waWNrZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvZGF0ZS1yYW5nZS1waWNrZXIvZGF0ZS1yYW5nZS1waWNrZXIuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzFDLE9BQU8sRUFDTCx1QkFBdUIsRUFDdkIsU0FBUyxFQUNULFVBQVUsRUFDVixZQUFZLEVBQ1osTUFBTSxFQUNOLEtBQUssRUFFTCxNQUFNLEdBQ1AsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDaEUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMvQixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXRELE9BQU8sRUFFTCxTQUFTLElBQUksZ0JBQWdCLEVBQzdCLHNCQUFzQixHQUN2QixNQUFNLDRDQUE0QyxDQUFDO0FBQ3BELE9BQU8sRUFBRSxlQUFlLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQzs7Ozs7O0FBUXpFLE1BQU0sT0FBTyx3QkFBd0I7SUFPbkMsZ0VBQWdFO0lBQ2hFLElBQWEsS0FBSyxDQUFDLEtBQVc7UUFDNUIsSUFBSSxDQUFDLFlBQVksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ2xDLENBQUM7SUFFRCw4REFBOEQ7SUFDOUQsSUFBYSxHQUFHLENBQUMsR0FBUztRQUN4QixJQUFJLENBQUMsWUFBWSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7SUFDOUIsQ0FBQztJQXFE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QWpCLEtBQUs7Z0JBR0csVUFBVTtzQkFBbEIsS0FBSztnQkFHRyxVQUFVO3NCQUFsQixLQUFLO2dCQUdHLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFHRyxjQUFjO3NCQUF0QixLQUFLO2dCQUdHLGVBQWU7c0JBQXZCLEtBQUs7Z0JBR08sb0JBQW9CO3NCQUFoQyxLQUFLO2dCQUtPLGtCQUFrQjtzQkFBOUIsS0FBSztnQkFLTyxRQUFRO3NCQUFwQixLQUFLO2dCQWFHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBR0csYUFBYTtzQkFBckIsS0FBSztnQkFHRyxXQUFXO3NCQUFuQixLQUFLO2dCQUdHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBR0csYUFBYTtzQkFBckIsS0FBSztnQkFHSSxXQUFXO3NCQUFwQixNQUFNO2dCQUdHLFNBQVM7c0JBQWxCLE1BQU07Z0JBR0csbUJBQW1CO3NCQUE1QixNQUFNO2dCQUlHLGlCQUFpQjtzQkFBMUIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFdlZWtEYXkgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENvbXBvbmVudCxcbiAgRGVzdHJveVJlZixcbiAgRXZlbnRFbWl0dGVyLFxuICBpbmplY3QsXG4gIElucHV0LFxuICBPbkRlc3Ryb3ksXG4gIE91dHB1dCxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyB0YWtlVW50aWxEZXN0cm95ZWQgfSBmcm9tICdAYW5ndWxhci9jb3JlL3J4anMtaW50ZXJvcCc7XG5pbXBvcnQgeyBTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBkZWJvdW5jZVRpbWUsIGZpbHRlciB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IERhdGVGb3JtYXR0ZXIgfSBmcm9tICcuLi8uLi9waXBlcy9kYXRlLWZvcm1hdHRlci9kYXRlLWZvcm1hdHRlci50eXBlJztcbmltcG9ydCB7XG4gIERhdGVUaW1lUGlja2VyVGltZXpvbmUsXG4gIHRpbWV6b25lcyBhcyBkZWZhdWx0VGltZXpvbmVzLFxuICBkaWZmZXJlbmNlQmV0d2VlbkRhdGVzLFxufSBmcm9tICcuLi9kYXRlLXRpbWUtcGlja2VyL2RhdGUtdGltZS1waWNrZXIudXRpbHMnO1xuaW1wb3J0IHsgRGF0ZVJhbmdlUGlja2VyLCBEYXRlUmFuZ2VTZXJ2aWNlIH0gZnJvbSAnLi9kYXRlLXJhbmdlLnNlcnZpY2UnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICd1eC1kYXRlLXJhbmdlLXBpY2tlcicsXG4gIHRlbXBsYXRlVXJsOiAnLi9kYXRlLXJhbmdlLXBpY2tlci5jb21wb25lbnQuaHRtbCcsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBwcm92aWRlcnM6IFtEYXRlUmFuZ2VTZXJ2aWNlXSxcbn0pXG5leHBvcnQgY2xhc3MgRGF0ZVJhbmdlUGlja2VyQ29tcG9uZW50IGltcGxlbWVudHMgT25EZXN0cm95IHtcbiAgcmVhZG9ubHkgcmFuZ2VTZXJ2aWNlID0gaW5qZWN0KERhdGVSYW5nZVNlcnZpY2UpO1xuICBwcml2YXRlIHJlYWRvbmx5IF9kZXN0cm95UmVmID0gaW5qZWN0KERlc3Ryb3lSZWYpO1xuXG4gIC8qKiBFeHBvc2UgZW51bSB0byB0aGUgdmlldyAqL1xuICBEYXRlUmFuZ2VQaWNrZXIgPSBEYXRlUmFuZ2VQaWNrZXI7XG5cbiAgLyoqIFRoZSBzZWxlY3RlZCBzdGFydCBkYXRlIHRvIGJlIGRpc3BsYXllZCBpbiB0aGUgY29tcG9uZW50LiAqL1xuICBASW5wdXQoKSBzZXQgc3RhcnQoc3RhcnQ6IERhdGUpIHtcbiAgICB0aGlzLnJhbmdlU2VydmljZS5zdGFydCA9IHN0YXJ0O1xuICB9XG5cbiAgLyoqIFRoZSBzZWxlY3RlZCBlbmQgZGF0ZSB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIGNvbXBvbmVudC4gKi9cbiAgQElucHV0KCkgc2V0IGVuZChlbmQ6IERhdGUpIHtcbiAgICB0aGlzLnJhbmdlU2VydmljZS5lbmQgPSBlbmQ7XG4gIH1cblxuICAvKiogRGVmaW5lIHRoZSBkYXRlIGxvY2FsaXphdGlvbiB0aGF0IHNob3VsZCBiZSB1c2VkIGNhbiBiZSBlaXRoZXIgYSBzdHJpbmcgb24gYSBmdW5jdGlvbiAqL1xuICBASW5wdXQoKSBkYXRlRm9ybWF0OiBzdHJpbmcgfCBEYXRlRm9ybWF0dGVyO1xuXG4gIC8qKiBEZWZpbmUgdGhlIHRpbWUgbG9jYWxpemF0aW9uIHRoYXQgc2hvdWxkIGJlIHVzZWQgY2FuIGJlIGVpdGhlciBhIHN0cmluZyBvbiBhIGZ1bmN0aW9uICovXG4gIEBJbnB1dCgpIHRpbWVGb3JtYXQ6IHN0cmluZyB8IERhdGVGb3JtYXR0ZXI7XG5cbiAgLyoqIFRoZSBlYXJsaWVzdCBzZWxlY3RhYmxlIGRhdGUuICovXG4gIEBJbnB1dCgpIG1pbjogRGF0ZTtcblxuICAvKiogVGhlIGxhdGVzdCBzZWxlY3RhYmxlIGRhdGUuICovXG4gIEBJbnB1dCgpIG1heDogRGF0ZTtcblxuICAvKiogRGVmaW5lcyB3aGV0aGVyIG9yIG5vdCB0aGUgdGltZSBwaWNrZXIgc2hvdWxkIGFsbG93IHRoZSB1c2VyIHRvIGNob29zZSBhIHRpbWV6b25lLiAqL1xuICBASW5wdXQoKSBzaG93VGltZXpvbmU6IGJvb2xlYW47XG5cbiAgLyoqIERlZmluZXMgd2hldGhlciBvciBub3QgdGhlIHRpbWUgcGlja2VyIHNob3VsZCBhbGxvdyB0aGUgdXNlciB0byBzcGVjaWZ5IHNlY29uZHMuICovXG4gIEBJbnB1dCgpIHNob3dTZWNvbmRzOiBib29sZWFuID0gZmFsc2U7XG5cbiAgLyoqIERlZmluZXMgd2hldGhlciBvciBub3QgdGhlIHRpbWUgcGlja2VyIHNob3VsZCBzaG93IGFuIEFNL1BNIGJ1dHRvbiwgb3IgdGltZSBzaG91bGQgYmUgcmVwcmVzZW50ZWQgaW4gMjRociBmb3JtYXQgaW5zdGVhZC4gKi9cbiAgQElucHV0KCkgc2hvd01lcmlkaWFuOiBib29sZWFuID0gdHJ1ZTtcblxuICAvKiogRGVmaW5lcyB3aGV0aGVyIG9yIG5vdCB0aGUgdGltZSBwaWNrZXIgc2hvdWxkIGFsbG93IHRoZSB1c2VyIHRvIHNlbGVjdCB0aGUgdGltZSB1c2luZyBzcGlubmVycy4gKi9cbiAgQElucHV0KCkgc2hvd1NwaW5uZXJzOiBib29sZWFuID0gdHJ1ZTtcblxuICAvKiogSWYgZGVmaW5lZCB3aWxsIG92ZXJyaWRlIHRoZSB3ZWVrZGF5IG5hbWVzIGRpc3BsYXllZC4gKi9cbiAgQElucHV0KCkgd2Vla2RheXM6IHN0cmluZ1tdID0gWydTJywgJ00nLCAnVCcsICdXJywgJ1QnLCAnRicsICdTJ107XG5cbiAgLyoqIERlZmluZXMgdGhlIG5hbWVzIG9mIHRoZSBtb250aHMuICovXG4gIEBJbnB1dCgpIG1vbnRoczogc3RyaW5nW107XG5cbiAgLyoqIERlZmluZXMgdGhlIHNob3J0IG5hbWVzIG9mIGVhY2ggbW9udGguICovXG4gIEBJbnB1dCgpIG1vbnRoc1Nob3J0OiBzdHJpbmdbXTtcblxuICAvKiogRGVmaW5lcyB0aGUgbGFiZWxzIHRvIHNob3cgaW4gdGhlIG1lcmlkaWFuIChBTS9QTSkgc2VsZWN0b3IuICovXG4gIEBJbnB1dCgpIG1lcmlkaWFuczogc3RyaW5nW107XG5cbiAgLyoqIERlZmluZXMgdGhlIHRleHQgdG8gYmUgZGlzcGxheWVkIGluIHRoZSBidXR0b24gdXNlZCB0byBzZXQgdGhlIHNlbGVjdGVkIHRpbWUgdG8gdGhlIGN1cnJlbnQgdGltZS4gKi9cbiAgQElucHV0KCkgbm93QnRuVGV4dDogc3RyaW5nO1xuXG4gIC8qKiBTcGVjaWZ5IHdoZXRoZXIgb3Igbm90IHRoZSBzaG93IG5vdyBidXR0b24gc2hvdWxkIGJlIHZpc2libGUgKi9cbiAgQElucHV0KCkgc2hvd05vd0J0bjogYm9vbGVhbiA9IGZhbHNlO1xuXG4gIC8qKiBEZWZpbmVzIHRoZSB0aXRsZSB0byBkaXNwbGF5IGFib3ZlIHRoZSBzdGFydCBwaWNrZXIuICovXG4gIEBJbnB1dCgpIHNlbGVjdFN0YXJ0VGl0bGU6IHN0cmluZyA9ICdTZWxlY3QgU3RhcnQgRGF0ZSc7XG5cbiAgLyoqIERlZmluZXMgdGhlIHRpdGxlIHRvIGRpc3BsYXkgYWJvdmUgdGhlIGVuZCBwaWNrZXIuICovXG4gIEBJbnB1dCgpIHNlbGVjdEVuZFRpdGxlOiBzdHJpbmcgPSAnU2VsZWN0IEVuZCBEYXRlJztcblxuICAvKiogRGVmaW5lIHRoZSBhcmlhIGxhYmVsIGZvciB0aGUgbm93IGJ1dHRvbiAqL1xuICBASW5wdXQoKSBub3dCdG5BcmlhTGFiZWw6IHN0cmluZyA9ICdTZXQgZGF0ZSB0byBub3cnO1xuXG4gIC8qKiBEZWZpbmVzIHRoZSBhcmlhIGxhYmVsIGZvciB0aGUgcmFuZ2Ugc3RhcnQgcGlja2VyICovXG4gIEBJbnB1dCgpIHNldCBzdGFydFBpY2tlckFyaWFMYWJlbChsYWJlbDogc3RyaW5nKSB7XG4gICAgdGhpcy5yYW5nZVNlcnZpY2Uuc3RhcnRQaWNrZXJBcmlhTGFiZWwgPSBsYWJlbDtcbiAgfVxuXG4gIC8qKiBEZWZpbmVzIHRoZSBhcmlhIGxhYmVsIGZvciB0aGUgcmFuZ2UgZW5kIHBpY2tlciAqL1xuICBASW5wdXQoKSBzZXQgZW5kUGlja2VyQXJpYUxhYmVsKGxhYmVsOiBzdHJpbmcpIHtcbiAgICB0aGlzLnJhbmdlU2VydmljZS5lbmRQaWNrZXJBcmlhTGFiZWwgPSBsYWJlbDtcbiAgfVxuXG4gIC8qKiBEZWZpbmVzIHdoZXRoZXIgb3Igbm90IHRoZSB0aW1lIHBpY2tlciBzaG91bGQgYmUgdmlzaWJsZS4gKi9cbiAgQElucHV0KCkgc2V0IHNob3dUaW1lKHNob3dUaW1lOiBib29sZWFuKSB7XG4gICAgdGhpcy5yYW5nZVNlcnZpY2Uuc2hvd1RpbWUgPSBzaG93VGltZTtcbiAgfVxuXG4gIGdldCBzaG93VGltZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5yYW5nZVNlcnZpY2Uuc2hvd1RpbWU7XG4gIH1cblxuICAvKipcbiAgICogRGVmaW5lcyB0aGUgbGlzdCBvZiBhdmFpbGFibGUgdGltZXpvbmVzLiBUaGUgYERhdGVUaW1lUGlja2VyVGltZXpvbmVgIGludGVyZmFjZSBzcGVjaWZpZXMgdGhhdCBlYWNoIHRpbWV6b25lIHNob3VsZFxuICAgKiBiZSBhbiBvYmplY3Qgd2l0aCBhIGBuYW1lYCBwcm9wZXJ0eSB0aGF0IHJlcHJlc2VudHMgdGhlIHRpbWV6b25lLCBlZy4gYEdNVCsyYCwgYW5kIGFuIGBvZmZzZXRgIHByb3BlcnR5IHRoYXQgcmVwcmVzZW50c1xuICAgKiB0aGUgbnVtYmVyIG9mIG1pbnV0ZXMgcmVsYXRpdmUgdG8gR01UIHRoZSB0aW1lem9uZSBpcy5cbiAgICovXG4gIEBJbnB1dCgpIHRpbWV6b25lczogRGF0ZVRpbWVQaWNrZXJUaW1lem9uZVtdID0gZGVmYXVsdFRpbWV6b25lcztcblxuICAvKiogV2lsbCBzZXQgdGhlIHNlbGVjdGVkIHN0YXJ0IHRpbWV6b25lLiAqL1xuICBASW5wdXQoKSBzdGFydFRpbWV6b25lOiBEYXRlVGltZVBpY2tlclRpbWV6b25lID0gdGhpcy5nZXRDdXJyZW50VGltZXpvbmUoKTtcblxuICAvKiogV2lsbCBzZXQgdGhlIHNlbGVjdGVkIGVuZCB0aW1lem9uZS4gKi9cbiAgQElucHV0KCkgZW5kVGltZXpvbmU6IERhdGVUaW1lUGlja2VyVGltZXpvbmUgPSB0aGlzLmdldEN1cnJlbnRUaW1lem9uZSgpO1xuXG4gIC8qKiBEZWZpbmVzIHRoZSBkYXkgb2YgdGhlIHdlZWsgdGhhdCBzaG91bGQgYXBwZWFyIGluIHRoZSBmaXJzdCBjb2x1bW4uIGBXZWVrRGF5YCBpcyBhbiBlbnVtZXJhdGlvbiBhdmFpbGFibGUgaW4gYEBhbmd1bGFyL2NvbW1vbmAuICovXG4gIEBJbnB1dCgpIHN0YXJ0T2ZXZWVrOiBXZWVrRGF5ID0gV2Vla0RheS5TdW5kYXk7XG5cbiAgLyoqIERlZmluZSBhIGZ1bmN0aW9uIHRvIHJldHVybiB0aGUgbnVtYmVyIG9mIGRheXMgd2l0aGluIHRoZSBzZWxlY3RlZCByYW5nZSAqL1xuICBASW5wdXQoKSBkdXJhdGlvblRpdGxlOiAoZGF5czogbnVtYmVyKSA9PiBzdHJpbmcgPSB0aGlzLmdldER1cmF0aW9uVGl0bGU7XG5cbiAgLyoqIEVtaXQgd2hlbiB0aGUgc3RhcnQgZGF0ZSBjaGFuZ2VzICovXG4gIEBPdXRwdXQoKSBzdGFydENoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8RGF0ZT4oKTtcblxuICAvKiogRW1pdCB3aGVuIHRoZSBlbmQgZGF0ZSBjaGFuZ2VzICovXG4gIEBPdXRwdXQoKSBlbmRDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPERhdGU+KCk7XG5cbiAgLyoqIEVtaXQgd2hlbiB0aGUgc3RhcnQgdGltZXpvbmUgY2hhbmdlcy4gKi9cbiAgQE91dHB1dCgpIHN0YXJ0VGltZXpvbmVDaGFuZ2U6IEV2ZW50RW1pdHRlcjxEYXRlVGltZVBpY2tlclRpbWV6b25lPiA9XG4gICAgbmV3IEV2ZW50RW1pdHRlcjxEYXRlVGltZVBpY2tlclRpbWV6b25lPigpO1xuXG4gIC8qKiBFbWl0IHdoZW4gdGhlIGVuZCB0aW1lem9uZSBjaGFuZ2VzLiAqL1xuICBAT3V0cHV0KCkgZW5kVGltZXpvbmVDaGFuZ2U6IEV2ZW50RW1pdHRlcjxEYXRlVGltZVBpY2tlclRpbWV6b25lPiA9XG4gICAgbmV3IEV2ZW50RW1pdHRlcjxEYXRlVGltZVBpY2tlclRpbWV6b25lPigpO1xuXG4gIC8qKiBDYWxjdWxhdGUgdGhlIG51bWJlciBvZiBkYXlzIGJldHdlZW4gdGhlIHN0YXJ0IGFuZCBlbmQgZGF0ZSAqL1xuICBnZXQgX2R1cmF0aW9uKCk6IG51bWJlciB8IG51bGwge1xuICAgIGlmICh0aGlzLnJhbmdlU2VydmljZS5zdGFydCAmJiB0aGlzLnJhbmdlU2VydmljZS5lbmQpIHtcbiAgICAgIHJldHVybiBkaWZmZXJlbmNlQmV0d2VlbkRhdGVzKHRoaXMucmFuZ2VTZXJ2aWNlLnN0YXJ0LCB0aGlzLnJhbmdlU2VydmljZS5lbmQsIGZhbHNlKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5yYW5nZVNlcnZpY2Uuc3RhcnQgJiYgIXRoaXMucmFuZ2VTZXJ2aWNlLmVuZCAmJiB0aGlzLnJhbmdlU2VydmljZS5ob3Zlcikge1xuICAgICAgLy8gYXBwbHkgdGhlIHRpbWUgZnJvbSB0aGUgdGltZSBwaWNrZXJcbiAgICAgIGNvbnN0IGhvdmVyRGF0ZSA9IG5ldyBEYXRlKHRoaXMucmFuZ2VTZXJ2aWNlLmhvdmVyKTtcbiAgICAgIGhvdmVyRGF0ZS5zZXRIb3VycyhcbiAgICAgICAgdGhpcy5yYW5nZVNlcnZpY2UuZW5kVGltZS5ob3VycyxcbiAgICAgICAgdGhpcy5yYW5nZVNlcnZpY2UuZW5kVGltZS5taW51dGVzLFxuICAgICAgICB0aGlzLnJhbmdlU2VydmljZS5lbmRUaW1lLnNlY29uZHNcbiAgICAgICk7XG4gICAgICByZXR1cm4gdGhpcy5yYW5nZVNlcnZpY2Uuc3RhcnQuZ2V0VGltZSgpIDw9IGhvdmVyRGF0ZS5nZXRUaW1lKClcbiAgICAgICAgPyBkaWZmZXJlbmNlQmV0d2VlbkRhdGVzKHRoaXMucmFuZ2VTZXJ2aWNlLnN0YXJ0LCBob3ZlckRhdGUsIGZhbHNlKVxuICAgICAgICA6IG51bGw7XG4gICAgfVxuXG4gICAgLy8gaWYgd2Ugb25seSBoYXZlIG9uZSBzZWxlY3RlZCBkYXRlIGFuZCBoYXZlIGEgaG92ZXIgZGF0ZVxuICAgIGlmICh0aGlzLnJhbmdlU2VydmljZS5lbmQgJiYgIXRoaXMucmFuZ2VTZXJ2aWNlLnN0YXJ0ICYmIHRoaXMucmFuZ2VTZXJ2aWNlLmhvdmVyKSB7XG4gICAgICAvLyBhcHBseSB0aGUgdGltZSBmcm9tIHRoZSB0aW1lIHBpY2tlclxuICAgICAgY29uc3QgaG92ZXJEYXRlID0gbmV3IERhdGUodGhpcy5yYW5nZVNlcnZpY2UuaG92ZXIpO1xuICAgICAgaG92ZXJEYXRlLnNldEhvdXJzKFxuICAgICAgICB0aGlzLnJhbmdlU2VydmljZS5zdGFydFRpbWUuaG91cnMsXG4gICAgICAgIHRoaXMucmFuZ2VTZXJ2aWNlLnN0YXJ0VGltZS5taW51dGVzLFxuICAgICAgICB0aGlzLnJhbmdlU2VydmljZS5zdGFydFRpbWUuc2Vjb25kc1xuICAgICAgKTtcbiAgICAgIHJldHVybiB0aGlzLnJhbmdlU2VydmljZS5lbmQuZ2V0VGltZSgpID49IGhvdmVyRGF0ZS5nZXRUaW1lKClcbiAgICAgICAgPyBkaWZmZXJlbmNlQmV0d2VlbkRhdGVzKHRoaXMucmFuZ2VTZXJ2aWNlLmVuZCwgaG92ZXJEYXRlLCBmYWxzZSlcbiAgICAgICAgOiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBVc2UgYW4gb2JzZXJ2YWJsZSB0byBkZWJvdW5jZSByYXBpZCBzdGFydCBjaGFuZ2VzICovXG4gIHN0YXJ0Q2hhbmdlJCA9IG5ldyBTdWJqZWN0PERhdGU+KCk7XG5cbiAgLyoqIFVzZSBhbiBvYnNlcnZhYmxlIHRvIGRlYm91bmNlIHJhcGlkIGVuZCBjaGFuZ2VzICovXG4gIGVuZENoYW5nZSQgPSBuZXcgU3ViamVjdDxEYXRlPigpO1xuXG4gIC8qKiBVbnN1YnNjcmliZSBmcm9tIGFsbCBvYnNlcnZhYmxlcyBwcml2YXRlICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IF9vbkRlc3Ryb3kgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMuc3RhcnRDaGFuZ2UkXG4gICAgICAucGlwZShcbiAgICAgICAgZGVib3VuY2VUaW1lKDApLFxuICAgICAgICBmaWx0ZXIoZGF0ZSA9PiBkYXRlICE9PSB1bmRlZmluZWQpLFxuICAgICAgICB0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZilcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoZGF0ZSA9PiB0aGlzLm9uU3RhcnRDaGFuZ2UoZGF0ZSkpO1xuICAgIHRoaXMuZW5kQ2hhbmdlJFxuICAgICAgLnBpcGUoXG4gICAgICAgIGRlYm91bmNlVGltZSgwKSxcbiAgICAgICAgZmlsdGVyKGRhdGUgPT4gZGF0ZSAhPT0gdW5kZWZpbmVkKSxcbiAgICAgICAgdGFrZVVudGlsRGVzdHJveWVkKHRoaXMuX2Rlc3Ryb3lSZWYpXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKGRhdGUgPT4gdGhpcy5vbkVuZENoYW5nZShkYXRlKSk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLl9vbkRlc3Ryb3kubmV4dCgpO1xuICAgIHRoaXMuX29uRGVzdHJveS5jb21wbGV0ZSgpO1xuICB9XG5cbiAgLyoqIENsZWFyIHRoZSBzZWxlY3RlZCBkYXRlIHJhbmdlICovXG4gIGNsZWFyKCk6IHZvaWQge1xuICAgIHRoaXMucmFuZ2VTZXJ2aWNlLmNsZWFyKCk7XG4gIH1cblxuICAvKiogR2V0IHRoZSB0aW1lem9uZSBiYXNlZCBvbiB0aGUgbWFjaGluZSB0aW1lem9uZSAqL1xuICBwcml2YXRlIGdldEN1cnJlbnRUaW1lem9uZSgpOiBEYXRlVGltZVBpY2tlclRpbWV6b25lIHtcbiAgICByZXR1cm4gdGhpcy50aW1lem9uZXMuZmluZCh0aW1lem9uZSA9PiB0aW1lem9uZS5vZmZzZXQgPT09IG5ldyBEYXRlKCkuZ2V0VGltZXpvbmVPZmZzZXQoKSk7XG4gIH1cblxuICBwcml2YXRlIG9uU3RhcnRDaGFuZ2UoZGF0ZTogRGF0ZSk6IHZvaWQge1xuICAgIHRoaXMucmFuZ2VTZXJ2aWNlLnNldFN0YXJ0RGF0ZShkYXRlKTtcbiAgICB0aGlzLnN0YXJ0Q2hhbmdlLmVtaXQoZGF0ZSk7XG4gIH1cblxuICBwcml2YXRlIG9uRW5kQ2hhbmdlKGRhdGU6IERhdGUpOiB2b2lkIHtcbiAgICB0aGlzLnJhbmdlU2VydmljZS5zZXRFbmREYXRlKGRhdGUpO1xuICAgIHRoaXMuZW5kQ2hhbmdlLmVtaXQoZGF0ZSk7XG4gIH1cblxuICAvKiogR2V0IHRoZSB0ZXh0IHRvIGRpc3BsYXkgdG8gaW5kaWNhdGUgdGhlIGR1cmF0aW9uICovXG4gIHByaXZhdGUgZ2V0RHVyYXRpb25UaXRsZShkYXlzOiBudW1iZXIpOiBzdHJpbmcge1xuICAgIHJldHVybiBkYXlzICsgJyAnICsgKGRheXMgPiAxID8gJ2RheXMnIDogJ2RheScpO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwicmFuZ2UtaGVhZGVyXCI+XG5cbiAgPGRpdiBjbGFzcz1cImhlYWRlci1zZWN0aW9uXCI+XG4gICAgQGlmICghcmFuZ2VTZXJ2aWNlLnN0YXJ0KSB7XG4gICAgICA8ZGl2IGNsYXNzPVwic2VsZWN0LWhlYWRlclwiPnt7IHNlbGVjdFN0YXJ0VGl0bGUgfX08L2Rpdj5cbiAgICB9XG4gICAgQGlmIChyYW5nZVNlcnZpY2Uuc3RhcnQpIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJkYXRlLWhlYWRlclwiPlxuICAgICAgICB7eyByYW5nZVNlcnZpY2Uuc3RhcnQgfCBmb3JtYXREYXRlOiBkYXRlRm9ybWF0IHx8ICdkIE1NTU0geScgfX1cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgICA8ZGl2IFtzdHlsZS52aXNpYmlsaXR5XT1cInJhbmdlU2VydmljZS5zdGFydCAmJiBzaG93VGltZSA/ICd2aXNpYmxlJyA6ICdoaWRkZW4nXCIgY2xhc3M9XCJ0aW1lLWhlYWRlclwiPlxuICAgICAge3sgcmFuZ2VTZXJ2aWNlLnN0YXJ0IHwgZm9ybWF0RGF0ZTogdGltZUZvcm1hdCB8fCAoc2hvd01lcmlkaWFuID8gJ3Nob3J0VGltZScgOiAnSEg6bW0nKSB9fVxuICAgIDwvZGl2PlxuICA8L2Rpdj5cblxuICA8ZGl2IGNsYXNzPVwiaGVhZGVyLXNlcGFyYXRvclwiPlxuICAgIDx1eC1pY29uIG5hbWU9XCJsaW5rLW5leHRcIj48L3V4LWljb24+XG4gICAgPHAgY2xhc3M9XCJkdXJhdGlvblwiXG4gICAgICBbc3R5bGUudmlzaWJpbGl0eV09XCJfZHVyYXRpb24gIT09IG51bGwgJiYgX2R1cmF0aW9uICE9PSB1bmRlZmluZWQgPyAndmlzaWJsZScgOiAnaGlkZGVuJ1wiPlxuICAgICAge3sgZHVyYXRpb25UaXRsZShfZHVyYXRpb24gfHwgMCkgfX1cbiAgICA8L3A+XG4gIDwvZGl2PlxuXG4gIDxkaXYgY2xhc3M9XCJoZWFkZXItc2VjdGlvblwiPlxuICAgIEBpZiAoIXJhbmdlU2VydmljZS5lbmQpIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJzZWxlY3QtaGVhZGVyXCI+e3sgc2VsZWN0RW5kVGl0bGUgfX08L2Rpdj5cbiAgICB9XG4gICAgQGlmIChyYW5nZVNlcnZpY2UuZW5kKSB7XG4gICAgICA8ZGl2IGNsYXNzPVwiZGF0ZS1oZWFkZXJcIj5cbiAgICAgICAge3sgcmFuZ2VTZXJ2aWNlLmVuZCB8IGZvcm1hdERhdGU6IGRhdGVGb3JtYXQgfHwgJ2QgTU1NTSB5J319XG4gICAgICA8L2Rpdj5cbiAgICB9XG4gICAgPGRpdiBbc3R5bGUudmlzaWJpbGl0eV09XCJyYW5nZVNlcnZpY2UuZW5kICYmIHNob3dUaW1lID8gJ3Zpc2libGUnIDogJ2hpZGRlbidcIiBjbGFzcz1cInRpbWUtaGVhZGVyXCI+XG4gICAgICB7eyByYW5nZVNlcnZpY2UuZW5kIHwgZm9ybWF0RGF0ZTogdGltZUZvcm1hdCB8fCAoc2hvd01lcmlkaWFuID8gJ3Nob3J0VGltZScgOiAnSEg6bW0nKSB9fVxuICAgIDwvZGl2PlxuICA8L2Rpdj5cblxuPC9kaXY+XG5cbjxkaXYgY2xhc3M9XCJjb250ZW50XCI+XG4gIDx1eC1kYXRlLXRpbWUtcGlja2VyXG4gICAgdXhEYXRlUmFuZ2VQaWNrZXJcbiAgICBbcGlja2VyXT1cIkRhdGVSYW5nZVBpY2tlci5TdGFydFwiXG4gICAgY2xhc3M9XCJzdGFydC1kYXRlLXBpY2tlclwiXG4gICAgW2RhdGVdPVwicmFuZ2VTZXJ2aWNlLnN0YXJ0XCJcbiAgICAoZGF0ZUNoYW5nZSk9XCJzdGFydENoYW5nZSQubmV4dCgkZXZlbnQpXCJcbiAgICBbbWluXT1cIm1pblwiXG4gICAgW21heF09XCJtYXhcIlxuICAgIFt3ZWVrZGF5c109XCJ3ZWVrZGF5c1wiXG4gICAgW3N0YXJ0T2ZXZWVrXT1cInN0YXJ0T2ZXZWVrXCJcbiAgICBbc2hvd1RpbWVdPVwic2hvd1RpbWVcIlxuICAgIFtzaG93VGltZXpvbmVdPVwic2hvd1RpbWV6b25lXCJcbiAgICBbc2hvd1NlY29uZHNdPVwic2hvd1NlY29uZHNcIlxuICAgIFtzaG93TWVyaWRpYW5dPVwic2hvd01lcmlkaWFuXCJcbiAgICBbc2hvd1NwaW5uZXJzXT1cInNob3dTcGlubmVyc1wiXG4gICAgW21vbnRoc109XCJtb250aHNcIlxuICAgIFttb250aHNTaG9ydF09XCJtb250aHNTaG9ydFwiXG4gICAgW21lcmlkaWFuc109XCJtZXJpZGlhbnNcIlxuICAgIFtub3dCdG5UZXh0XT1cIm5vd0J0blRleHRcIlxuICAgIFtzaG93Tm93QnRuXT1cInNob3dOb3dCdG5cIlxuICAgIFtub3dCdG5BcmlhTGFiZWxdPVwibm93QnRuQXJpYUxhYmVsXCJcbiAgICBbdGltZXpvbmVzXT1cInRpbWV6b25lc1wiXG4gICAgWyh0aW1lem9uZSldPVwic3RhcnRUaW1lem9uZVwiXG4gICAgKHRpbWV6b25lQ2hhbmdlKT1cInN0YXJ0VGltZXpvbmVDaGFuZ2UuZW1pdCgkZXZlbnQpXCI+XG4gIDwvdXgtZGF0ZS10aW1lLXBpY2tlcj5cblxuICA8dXgtZGF0ZS10aW1lLXBpY2tlclxuICAgIHV4RGF0ZVJhbmdlUGlja2VyXG4gICAgW3BpY2tlcl09XCJEYXRlUmFuZ2VQaWNrZXIuRW5kXCJcbiAgICBjbGFzcz1cImVuZC1kYXRlLXBpY2tlclwiXG4gICAgW2RhdGVdPVwicmFuZ2VTZXJ2aWNlLmVuZFwiXG4gICAgKGRhdGVDaGFuZ2UpPVwiZW5kQ2hhbmdlJC5uZXh0KCRldmVudClcIlxuICAgIFttaW5dPVwibWluXCJcbiAgICBbbWF4XT1cIm1heFwiXG4gICAgW3dlZWtkYXlzXT1cIndlZWtkYXlzXCJcbiAgICBbc3RhcnRPZldlZWtdPVwic3RhcnRPZldlZWtcIlxuICAgIFtzaG93VGltZV09XCJzaG93VGltZVwiXG4gICAgW3Nob3dUaW1lem9uZV09XCJzaG93VGltZXpvbmVcIlxuICAgIFtzaG93U2Vjb25kc109XCJzaG93U2Vjb25kc1wiXG4gICAgW3Nob3dNZXJpZGlhbl09XCJzaG93TWVyaWRpYW5cIlxuICAgIFtzaG93U3Bpbm5lcnNdPVwic2hvd1NwaW5uZXJzXCJcbiAgICBbbW9udGhzXT1cIm1vbnRoc1wiXG4gICAgW21vbnRoc1Nob3J0XT1cIm1vbnRoc1Nob3J0XCJcbiAgICBbbWVyaWRpYW5zXT1cIm1lcmlkaWFuc1wiXG4gICAgW25vd0J0blRleHRdPVwibm93QnRuVGV4dFwiXG4gICAgW3Nob3dOb3dCdG5dPVwic2hvd05vd0J0blwiXG4gICAgW25vd0J0bkFyaWFMYWJlbF09XCJub3dCdG5BcmlhTGFiZWxcIlxuICAgIFt0aW1lem9uZXNdPVwidGltZXpvbmVzXCJcbiAgICBbKHRpbWV6b25lKV09XCJlbmRUaW1lem9uZVwiXG4gICAgKHRpbWV6b25lQ2hhbmdlKT1cImVuZFRpbWV6b25lQ2hhbmdlLmVtaXQoJGV2ZW50KVwiPlxuICA8L3V4LWRhdGUtdGltZS1waWNrZXI+XG48L2Rpdj5cbiJdfQ==