@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
171 lines • 36 kB
JavaScript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostListener, inject, Output } from '@angular/core';
import { combineLatest, Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { DateRangeOptions } from '../../date-range-picker/date-range-picker.directive';
import { DateRangePicker, DateRangeService } from '../../date-range-picker/date-range.service';
import { DateTimePickerService } from '../date-time-picker.service';
import { compareDays } from '../date-time-picker.utils';
import * as i0 from "@angular/core";
import * as i1 from "@angular/forms";
import * as i2 from "../../spin-button/spin-button.component";
import * as i3 from "../../time-picker/time-picker.component";
import * as i4 from "@angular/common";
export class TimeViewComponent {
/** Determine if we are in range selection mode */
get _isRangeMode() {
return !!this._rangeOptions;
}
/** Determine if this picker is the start picker */
get _isRangeStart() {
return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.Start;
}
/** Determine if this picker is the end picker */
get _isRangeEnd() {
return this._isRangeMode && this._rangeOptions.picker === DateRangePicker.End;
}
get _rangeStart() {
return this._isRangeMode && this._rangeService ? this._rangeService.start : null;
}
get _rangeEnd() {
return this._isRangeMode && this._rangeService ? this._rangeService.end : null;
}
constructor() {
this.datepicker = inject(DateTimePickerService);
this._changeDetector = inject(ChangeDetectorRef);
this._rangeService = inject(DateRangeService, { optional: true });
this._rangeOptions = inject(DateRangeOptions, { optional: true });
/** Earliest time permitted on the time picker. */
this.min = null;
/** Latest time permitted on the time picker. */
this.max = null;
/** Emit when the timezone changes. */
this.timezoneChange = new EventEmitter();
this._onDestroy = new Subject();
// when the date changes we should update the value
this.datepicker.date$.pipe(filter(date => date && this.value instanceof Date), takeUntil(this._onDestroy)).subscribe(date => {
this.value = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds());
this._changeDetector.detectChanges();
});
if (!this._isRangeMode) {
this.datepicker.selected$.pipe(filter(date => !!date), takeUntil(this._onDestroy))
.subscribe(date => this.value = new Date(date));
}
if (this._isRangeMode && this._isRangeStart) {
this.value = new Date();
if (!this._rangeStart) {
this.value.setHours(0, 0, 0, 0);
}
else {
this.value.setHours(this._rangeStart.getHours(), this._rangeStart.getMinutes(), this._rangeStart.getSeconds());
}
}
if (this._isRangeMode && this._isRangeEnd) {
this.value = new Date();
if (!this._rangeEnd) {
this.value.setHours(23, 59, 59, 0);
}
else {
this.value.setHours(this._rangeEnd.getHours(), this._rangeEnd.getMinutes(), this._rangeEnd.getSeconds());
}
}
}
ngOnInit() {
// min should only apply if it's on the same day as the selected date
combineLatest(this.datepicker.min$, this.datepicker.date$).pipe(takeUntil(this._onDestroy)).subscribe(([min, date]) => {
this.min = (min && date && compareDays(date, min)) ? min : null;
this._changeDetector.detectChanges();
});
// max should only apply if it's on the same day as the selected date
combineLatest(this.datepicker.max$, this.datepicker.date$).pipe(takeUntil(this._onDestroy)).subscribe(([max, date]) => {
this.max = (max && date && compareDays(date, max)) ? max : null;
this._changeDetector.detectChanges();
});
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
onTimeChange(time) {
if (this._isRangeMode) {
this.datepicker.hours = time.getHours();
this.datepicker.minutes = time.getMinutes();
this.datepicker.seconds = time.getSeconds();
// update the time in the range picker service
if (this._isRangeStart) {
this._rangeService.startTime = { hours: time.getHours(), minutes: time.getMinutes(), seconds: time.getSeconds() };
}
else {
this._rangeService.endTime = { hours: time.getHours(), minutes: time.getMinutes(), seconds: time.getSeconds() };
}
// if a date is currently selected we should update it
if (this._isRangeStart && this._rangeStart) {
const start = new Date(this._rangeStart);
start.setHours(time.getHours(), time.getMinutes(), time.getSeconds());
this._rangeService.setStartDate(start);
}
if (this._isRangeEnd && this._rangeEnd) {
const end = new Date(this._rangeEnd);
end.setHours(time.getHours(), time.getMinutes(), time.getSeconds());
this._rangeService.setEndDate(end);
}
return;
}
// if the selected time is null then do nothing
if (!this.datepicker.selected$.value) {
return;
}
// otherwise set the time
const date = new Date(this.datepicker.selected$.value);
// update the time
date.setHours(time.getHours(), time.getMinutes(), time.getSeconds());
// emit the time
this.datepicker.selected$.next(date);
}
selectTimezone(name) {
const timezones = this.datepicker.timezones$.value;
// find matching timezone
const timezone = timezones.find(_timezone => _timezone.name === name);
if (timezone) {
this.timezoneChange.emit(timezone);
}
}
incrementTimezone() {
const timezone = this.datepicker.timezone$.value;
const timezones = this.datepicker.timezones$.value;
const currentZone = timezones.findIndex(zone => zone.name === timezone.name && zone.offset === timezone.offset);
// try to get the previous zone
this.timezoneChange.emit(timezones[currentZone + 1] ? timezones[currentZone + 1] : timezones[currentZone]);
}
decrementTimezone() {
const timezone = this.datepicker.timezone$.value;
const timezones = this.datepicker.timezones$.value;
const currentZone = timezones.findIndex(zone => zone.name === timezone.name && zone.offset === timezone.offset);
// try to get the previous zone
this.timezoneChange.emit(timezones[currentZone - 1] ? timezones[currentZone - 1] : timezones[currentZone]);
}
onFocusWithin() {
if (this._isRangeMode) {
this._rangeService.isChangingTime = true;
}
}
onFocusOut() {
if (this._isRangeMode) {
this._rangeService.isChangingTime = false;
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: TimeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: TimeViewComponent, selector: "ux-date-time-picker-time-view", outputs: { timezoneChange: "timezoneChange" }, host: { listeners: { "focusin": "onFocusWithin()", "focusout": "onFocusOut()" } }, ngImport: i0, template: "@if (datepicker.showTime$ | async) {\n <ux-time-picker\n [value]=\"value\"\n (valueChange)=\"onTimeChange($event)\"\n [showSeconds]=\"datepicker.showSeconds$ | async\"\n [showMeridian]=\"datepicker.showMeridian$ | async\"\n [showSpinners]=\"datepicker.showSpinners$ | async\"\n [meridians]=\"datepicker.meridians\"\n [min]=\"min\"\n [max]=\"max\">\n </ux-time-picker>\n}\n\n@if (datepicker.showTimezone$ | async) {\n @if (datepicker.showSpinners$ | async) {\n <div class=\"time-zone-picker\">\n <ux-spin-button\n class=\"time-zone-spinner\"\n [value]=\"(datepicker.timezone$ | async)?.name\"\n [readOnly]=\"true\"\n (increment)=\"incrementTimezone()\"\n (decrement)=\"decrementTimezone()\"\n inputAriaLabel=\"Time Zone\"\n incrementAriaLabel=\"Switch to the next time zone\"\n decrementAriaLabel=\"Switch to the previous time zone\">\n </ux-spin-button>\n </div>\n }\n @if ((datepicker.showSpinners$ | async) === false) {\n <div class=\"time-zone-picker\">\n <select class=\"form-control time-zone-select\"\n tabindex=\"0\"\n [ngModel]=\"(datepicker.timezone$ | async)?.name\"\n (ngModelChange)=\"selectTimezone($event)\"\n aria-label=\"Timezone\"\n [attr.aria-valuetext]=\"(datepicker.timezone$ | async)?.name\">\n @for (zone of datepicker.timezones$ | async; track zone) {\n <option\n [selected]=\"zone.name === (datepicker.timezone$ | async)?.name\"\n [value]=\"zone.name\">\n {{ zone?.name }}\n </option>\n }\n </select>\n </div>\n }\n}\n", dependencies: [{ kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2.SpinButtonComponent, selector: "ux-spin-button", inputs: ["value", "type", "min", "max", "placeholder", "disabled", "spinners", "readOnly", "scrolling", "arrowkeys", "maxLength", "incrementAriaLabel", "inputAriaLabel", "decrementAriaLabel"], outputs: ["valueChange", "increment", "decrement"] }, { kind: "component", type: i3.TimePickerComponent, selector: "ux-time-picker", inputs: ["arrowkeys", "mousewheel", "disabled", "readOnly", "showMeridian", "showHours", "showMinutes", "showSeconds", "showSpinners", "hourStep", "minuteStep", "secondStep", "min", "max", "meridians", "value"], outputs: ["valueChange", "isValid"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: TimeViewComponent, decorators: [{
type: Component,
args: [{ selector: 'ux-date-time-picker-time-view', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (datepicker.showTime$ | async) {\n <ux-time-picker\n [value]=\"value\"\n (valueChange)=\"onTimeChange($event)\"\n [showSeconds]=\"datepicker.showSeconds$ | async\"\n [showMeridian]=\"datepicker.showMeridian$ | async\"\n [showSpinners]=\"datepicker.showSpinners$ | async\"\n [meridians]=\"datepicker.meridians\"\n [min]=\"min\"\n [max]=\"max\">\n </ux-time-picker>\n}\n\n@if (datepicker.showTimezone$ | async) {\n @if (datepicker.showSpinners$ | async) {\n <div class=\"time-zone-picker\">\n <ux-spin-button\n class=\"time-zone-spinner\"\n [value]=\"(datepicker.timezone$ | async)?.name\"\n [readOnly]=\"true\"\n (increment)=\"incrementTimezone()\"\n (decrement)=\"decrementTimezone()\"\n inputAriaLabel=\"Time Zone\"\n incrementAriaLabel=\"Switch to the next time zone\"\n decrementAriaLabel=\"Switch to the previous time zone\">\n </ux-spin-button>\n </div>\n }\n @if ((datepicker.showSpinners$ | async) === false) {\n <div class=\"time-zone-picker\">\n <select class=\"form-control time-zone-select\"\n tabindex=\"0\"\n [ngModel]=\"(datepicker.timezone$ | async)?.name\"\n (ngModelChange)=\"selectTimezone($event)\"\n aria-label=\"Timezone\"\n [attr.aria-valuetext]=\"(datepicker.timezone$ | async)?.name\">\n @for (zone of datepicker.timezones$ | async; track zone) {\n <option\n [selected]=\"zone.name === (datepicker.timezone$ | async)?.name\"\n [value]=\"zone.name\">\n {{ zone?.name }}\n </option>\n }\n </select>\n </div>\n }\n}\n" }]
}], ctorParameters: () => [], propDecorators: { timezoneChange: [{
type: Output
}], onFocusWithin: [{
type: HostListener,
args: ['focusin']
}], onFocusOut: [{
type: HostListener,
args: ['focusout']
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZS12aWV3LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL2RhdGUtdGltZS1waWNrZXIvdGltZS12aWV3L3RpbWUtdmlldy5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy9kYXRlLXRpbWUtcGlja2VyL3RpbWUtdmlldy90aW1lLXZpZXcuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLHVCQUF1QixFQUFFLGlCQUFpQixFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBcUIsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3JKLE9BQU8sRUFBRSxhQUFhLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzlDLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0scURBQXFELENBQUM7QUFDdkYsT0FBTyxFQUFFLGVBQWUsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBQy9GLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxXQUFXLEVBQTBCLE1BQU0sMkJBQTJCLENBQUM7Ozs7OztBQU9oRixNQUFNLE9BQU8saUJBQWlCO0lBa0IxQixrREFBa0Q7SUFDbEQsSUFBSSxZQUFZO1FBQ1osT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQztJQUNoQyxDQUFDO0lBRUQsbURBQW1EO0lBQ25ELElBQUksYUFBYTtRQUNiLE9BQU8sSUFBSSxDQUFDLFlBQVksSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sS0FBSyxlQUFlLENBQUMsS0FBSyxDQUFDO0lBQ3BGLENBQUM7SUFFRCxp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QUNMLENBQUM7K0dBNUxRLGlCQUFpQjttR0FBakIsaUJBQWlCLHVNQ2I5Qiw4bkRBK0NBOzs0RkRsQ2EsaUJBQWlCO2tCQUw3QixTQUFTOytCQUNJLCtCQUErQixtQkFFeEIsdUJBQXVCLENBQUMsTUFBTTt3REE0Q3JDLGNBQWM7c0JBQXZCLE1BQU07Z0JBdUlQLGFBQWE7c0JBRFosWUFBWTt1QkFBQyxTQUFTO2dCQVF2QixVQUFVO3NCQURULFlBQVk7dUJBQUMsVUFBVSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENoYW5nZURldGVjdGlvblN0cmF0ZWd5LCBDaGFuZ2VEZXRlY3RvclJlZiwgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIEhvc3RMaXN0ZW5lciwgaW5qZWN0LCBPbkRlc3Ryb3ksIE9uSW5pdCwgT3V0cHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBjb21iaW5lTGF0ZXN0LCBTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBmaWx0ZXIsIHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IERhdGVSYW5nZU9wdGlvbnMgfSBmcm9tICcuLi8uLi9kYXRlLXJhbmdlLXBpY2tlci9kYXRlLXJhbmdlLXBpY2tlci5kaXJlY3RpdmUnO1xuaW1wb3J0IHsgRGF0ZVJhbmdlUGlja2VyLCBEYXRlUmFuZ2VTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vZGF0ZS1yYW5nZS1waWNrZXIvZGF0ZS1yYW5nZS5zZXJ2aWNlJztcbmltcG9ydCB7IERhdGVUaW1lUGlja2VyU2VydmljZSB9IGZyb20gJy4uL2RhdGUtdGltZS1waWNrZXIuc2VydmljZSc7XG5pbXBvcnQgeyBjb21wYXJlRGF5cywgRGF0ZVRpbWVQaWNrZXJUaW1lem9uZSB9IGZyb20gJy4uL2RhdGUtdGltZS1waWNrZXIudXRpbHMnO1xuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ3V4LWRhdGUtdGltZS1waWNrZXItdGltZS12aWV3JyxcbiAgICB0ZW1wbGF0ZVVybDogJy4vdGltZS12aWV3LmNvbXBvbmVudC5odG1sJyxcbiAgICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaFxufSlcbmV4cG9ydCBjbGFzcyBUaW1lVmlld0NvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcbiAgICByZWFkb25seSBkYXRlcGlja2VyID0gaW5qZWN0KERhdGVUaW1lUGlja2VyU2VydmljZSk7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9jaGFuZ2VEZXRlY3RvciA9IGluamVjdChDaGFuZ2VEZXRlY3RvclJlZik7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9yYW5nZVNlcnZpY2UgPSBpbmplY3QoRGF0ZVJhbmdlU2VydmljZSwgeyBvcHRpb25hbDogdHJ1ZSB9KTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX3JhbmdlT3B0aW9ucyA9IGluamVjdChEYXRlUmFuZ2VPcHRpb25zLCB7IG9wdGlvbmFsOiB0cnVlIH0pO1xuXG4gICAgLyoqIERvbnQgYmluZCBkaXJlY3RseSB0byB0aGUgc2VsZWN0ZWQgZGF0ZSBhcyBpZiBpdCdzIG51bGwgd2UgY2FuIGVuZCB1cCBpbiAxOTcwISAqL1xuICAgIHZhbHVlOiBEYXRlO1xuXG4gICAgLyoqIEVhcmxpZXN0IHRpbWUgcGVybWl0dGVkIG9uIHRoZSB0aW1lIHBpY2tlci4gKi9cbiAgICBtaW46IERhdGUgPSBudWxsO1xuXG4gICAgLyoqIExhdGVzdCB0aW1lIHBlcm1pdHRlZCBvbiB0aGUgdGltZSBwaWNrZXIuICovXG4gICAgbWF4OiBEYXRlID0gbnVsbDtcblxuICAgIC8qKiBEZXRlcm1pbmUgaWYgd2UgYXJlIGluIHJhbmdlIHNlbGVjdGlvbiBtb2RlICovXG4gICAgZ2V0IF9pc1JhbmdlTW9kZSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuICEhdGhpcy5fcmFuZ2VPcHRpb25zO1xuICAgIH1cblxuICAgIC8qKiBEZXRlcm1pbmUgaWYgdGhpcyBwaWNrZXIgaXMgdGhlIHN0YXJ0IHBpY2tlciAqL1xuICAgIGdldCBfaXNSYW5nZVN0YXJ0KCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5faXNSYW5nZU1vZGUgJiYgdGhpcy5fcmFuZ2VPcHRpb25zLnBpY2tlciA9PT0gRGF0ZVJhbmdlUGlja2VyLlN0YXJ0O1xuICAgIH1cblxuICAgIC8qKiBEZXRlcm1pbmUgaWYgdGhpcyBwaWNrZXIgaXMgdGhlIGVuZCBwaWNrZXIgKi9cbiAgICBnZXQgX2lzUmFuZ2VFbmQoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9pc1JhbmdlTW9kZSAmJiB0aGlzLl9yYW5nZU9wdGlvbnMucGlja2VyID09PSBEYXRlUmFuZ2VQaWNrZXIuRW5kO1xuICAgIH1cblxuICAgIGdldCBfcmFuZ2VTdGFydCgpOiBEYXRlIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB0aGlzLl9pc1JhbmdlTW9kZSAmJiB0aGlzLl9yYW5nZVNlcnZpY2UgPyB0aGlzLl9yYW5nZVNlcnZpY2Uuc3RhcnQgOiBudWxsO1xuICAgIH1cblxuICAgIGdldCBfcmFuZ2VFbmQoKTogRGF0ZSB8IG51bGwge1xuICAgICAgICByZXR1cm4gdGhpcy5faXNSYW5nZU1vZGUgJiYgdGhpcy5fcmFuZ2VTZXJ2aWNlID8gdGhpcy5fcmFuZ2VTZXJ2aWNlLmVuZCA6IG51bGw7XG4gICAgfVxuXG4gICAgLyoqIEVtaXQgd2hlbiB0aGUgdGltZXpvbmUgY2hhbmdlcy4gKi9cbiAgICBAT3V0cHV0KCkgdGltZXpvbmVDaGFuZ2U6IEV2ZW50RW1pdHRlcjxEYXRlVGltZVBpY2tlclRpbWV6b25lPiA9IG5ldyBFdmVudEVtaXR0ZXI8RGF0ZVRpbWVQaWNrZXJUaW1lem9uZT4oKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX29uRGVzdHJveSA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgICBjb25zdHJ1Y3RvcigpIHtcblxuICAgICAgICAvLyB3aGVuIHRoZSBkYXRlIGNoYW5nZXMgd2Ugc2hvdWxkIHVwZGF0ZSB0aGUgdmFsdWVcbiAgICAgICAgdGhpcy5kYXRlcGlja2VyLmRhdGUkLnBpcGUoZmlsdGVyKGRhdGUgPT4gZGF0ZSAmJiB0aGlzLnZhbHVlIGluc3RhbmNlb2YgRGF0ZSksIHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKS5zdWJzY3JpYmUoZGF0ZSA9PiB7XG5cbiAgICAgICAgICAgIHRoaXMudmFsdWUgPSBuZXcgRGF0ZShkYXRlLmdldEZ1bGxZZWFyKCksIGRhdGUuZ2V0TW9udGgoKSwgZGF0ZS5nZXREYXRlKCksIGRhdGUuZ2V0SG91cnMoKSwgZGF0ZS5nZXRNaW51dGVzKCksIGRhdGUuZ2V0U2Vjb25kcygpKTtcblxuICAgICAgICAgICAgdGhpcy5fY2hhbmdlRGV0ZWN0b3IuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoIXRoaXMuX2lzUmFuZ2VNb2RlKSB7XG4gICAgICAgICAgICB0aGlzLmRhdGVwaWNrZXIuc2VsZWN0ZWQkLnBpcGUoZmlsdGVyKGRhdGUgPT4gISFkYXRlKSwgdGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSkpXG4gICAgICAgICAgICAgICAgLnN1YnNjcmliZShkYXRlID0+IHRoaXMudmFsdWUgPSBuZXcgRGF0ZShkYXRlKSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5faXNSYW5nZU1vZGUgJiYgdGhpcy5faXNSYW5nZVN0YXJ0KSB7XG4gICAgICAgICAgICB0aGlzLnZhbHVlID0gbmV3IERhdGUoKTtcbiAgICAgICAgICAgIGlmICghdGhpcy5fcmFuZ2VTdGFydCkge1xuICAgICAgICAgICAgICAgIHRoaXMudmFsdWUuc2V0SG91cnMoMCwgMCwgMCwgMCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMudmFsdWUuc2V0SG91cnModGhpcy5fcmFuZ2VTdGFydC5nZXRIb3VycygpLCB0aGlzLl9yYW5nZVN0YXJ0LmdldE1pbnV0ZXMoKSwgdGhpcy5fcmFuZ2VTdGFydC5nZXRTZWNvbmRzKCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuX2lzUmFuZ2VNb2RlICYmIHRoaXMuX2lzUmFuZ2VFbmQpIHtcbiAgICAgICAgICAgIHRoaXMudmFsdWUgPSBuZXcgRGF0ZSgpO1xuICAgICAgICAgICAgaWYgKCF0aGlzLl9yYW5nZUVuZCkge1xuICAgICAgICAgICAgICAgIHRoaXMudmFsdWUuc2V0SG91cnMoMjMsIDU5LCA1OSwgMCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMudmFsdWUuc2V0SG91cnModGhpcy5fcmFuZ2VFbmQuZ2V0SG91cnMoKSwgdGhpcy5fcmFuZ2VFbmQuZ2V0TWludXRlcygpLCB0aGlzLl9yYW5nZUVuZC5nZXRTZWNvbmRzKCkpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgbmdPbkluaXQoKTogdm9pZCB7XG5cbiAgICAgICAgLy8gbWluIHNob3VsZCBvbmx5IGFwcGx5IGlmIGl0J3Mgb24gdGhlIHNhbWUgZGF5IGFzIHRoZSBzZWxlY3RlZCBkYXRlXG4gICAgICAgIGNvbWJpbmVMYXRlc3QodGhpcy5kYXRlcGlja2VyLm1pbiQsIHRoaXMuZGF0ZXBpY2tlci5kYXRlJCkucGlwZSh0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSkuc3Vic2NyaWJlKChbbWluLCBkYXRlXSkgPT4ge1xuICAgICAgICAgICAgdGhpcy5taW4gPSAobWluICYmIGRhdGUgJiYgY29tcGFyZURheXMoZGF0ZSwgbWluKSkgPyBtaW4gOiBudWxsO1xuICAgICAgICAgICAgdGhpcy5fY2hhbmdlRGV0ZWN0b3IuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgICB9KTtcblxuICAgICAgICAvLyBtYXggc2hvdWxkIG9ubHkgYXBwbHkgaWYgaXQncyBvbiB0aGUgc2FtZSBkYXkgYXMgdGhlIHNlbGVjdGVkIGRhdGVcbiAgICAgICAgY29tYmluZUxhdGVzdCh0aGlzLmRhdGVwaWNrZXIubWF4JCwgdGhpcy5kYXRlcGlja2VyLmRhdGUkKS5waXBlKHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKS5zdWJzY3JpYmUoKFttYXgsIGRhdGVdKSA9PiB7XG4gICAgICAgICAgICB0aGlzLm1heCA9IChtYXggJiYgZGF0ZSAmJiBjb21wYXJlRGF5cyhkYXRlLCBtYXgpKSA/IG1heCA6IG51bGw7XG4gICAgICAgICAgICB0aGlzLl9jaGFuZ2VEZXRlY3Rvci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kubmV4dCgpO1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICBvblRpbWVDaGFuZ2UodGltZTogRGF0ZSk6IHZvaWQge1xuXG4gICAgICAgIGlmICh0aGlzLl9pc1JhbmdlTW9kZSkge1xuICAgICAgICAgICAgdGhpcy5kYXRlcGlja2VyLmhvdXJzID0gdGltZS5nZXRIb3VycygpO1xuICAgICAgICAgICAgdGhpcy5kYXRlcGlja2VyLm1pbnV0ZXMgPSB0aW1lLmdldE1pbnV0ZXMoKTtcbiAgICAgICAgICAgIHRoaXMuZGF0ZXBpY2tlci5zZWNvbmRzID0gdGltZS5nZXRTZWNvbmRzKCk7XG5cbiAgICAgICAgICAgIC8vIHVwZGF0ZSB0aGUgdGltZSBpbiB0aGUgcmFuZ2UgcGlja2VyIHNlcnZpY2VcbiAgICAgICAgICAgIGlmICh0aGlzLl9pc1JhbmdlU3RhcnQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9yYW5nZVNlcnZpY2Uuc3RhcnRUaW1lID0geyBob3VyczogdGltZS5nZXRIb3VycygpLCBtaW51dGVzOiB0aW1lLmdldE1pbnV0ZXMoKSwgc2Vjb25kczogdGltZS5nZXRTZWNvbmRzKCkgfTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fcmFuZ2VTZXJ2aWNlLmVuZFRpbWUgPSB7IGhvdXJzOiB0aW1lLmdldEhvdXJzKCksIG1pbnV0ZXM6IHRpbWUuZ2V0TWludXRlcygpLCBzZWNvbmRzOiB0aW1lLmdldFNlY29uZHMoKSB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBpZiBhIGRhdGUgaXMgY3VycmVudGx5IHNlbGVjdGVkIHdlIHNob3VsZCB1cGRhdGUgaXRcbiAgICAgICAgICAgIGlmICh0aGlzLl9pc1JhbmdlU3RhcnQgJiYgdGhpcy5fcmFuZ2VTdGFydCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHN0YXJ0ID0gbmV3IERhdGUodGhpcy5fcmFuZ2VTdGFydCk7XG4gICAgICAgICAgICAgICAgc3RhcnQuc2V0SG91cnModGltZS5nZXRIb3VycygpLCB0aW1lLmdldE1pbnV0ZXMoKSwgdGltZS5nZXRTZWNvbmRzKCkpO1xuICAgICAgICAgICAgICAgIHRoaXMuX3JhbmdlU2VydmljZS5zZXRTdGFydERhdGUoc3RhcnQpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAodGhpcy5faXNSYW5nZUVuZCAmJiB0aGlzLl9yYW5nZUVuZCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGVuZCA9IG5ldyBEYXRlKHRoaXMuX3JhbmdlRW5kKTtcbiAgICAgICAgICAgICAgICBlbmQuc2V0SG91cnModGltZS5nZXRIb3VycygpLCB0aW1lLmdldE1pbnV0ZXMoKSwgdGltZS5nZXRTZWNvbmRzKCkpO1xuICAgICAgICAgICAgICAgIHRoaXMuX3JhbmdlU2VydmljZS5zZXRFbmREYXRlKGVuZCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGlmIHRoZSBzZWxlY3RlZCB0aW1lIGlzIG51bGwgdGhlbiBkbyBub3RoaW5nXG4gICAgICAgIGlmICghdGhpcy5kYXRlcGlja2VyLnNlbGVjdGVkJC52YWx1ZSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gb3RoZXJ3aXNlIHNldCB0aGUgdGltZVxuICAgICAgICBjb25zdCBkYXRlID0gbmV3IERhdGUodGhpcy5kYXRlcGlja2VyLnNlbGVjdGVkJC52YWx1ZSk7XG5cbiAgICAgICAgLy8gdXBkYXRlIHRoZSB0aW1lXG4gICAgICAgIGRhdGUuc2V0SG91cnModGltZS5nZXRIb3VycygpLCB0aW1lLmdldE1pbnV0ZXMoKSwgdGltZS5nZXRTZWNvbmRzKCkpO1xuXG4gICAgICAgIC8vIGVtaXQgdGhlIHRpbWVcbiAgICAgICAgdGhpcy5kYXRlcGlja2VyLnNlbGVjdGVkJC5uZXh0KGRhdGUpO1xuICAgIH1cblxuICAgIHNlbGVjdFRpbWV6b25lKG5hbWU6IHN0cmluZyk6IHZvaWQge1xuICAgICAgICBjb25zdCB0aW1lem9uZXMgPSB0aGlzLmRhdGVwaWNrZXIudGltZXpvbmVzJC52YWx1ZTtcblxuICAgICAgICAvLyBmaW5kIG1hdGNoaW5nIHRpbWV6b25lXG4gICAgICAgIGNvbnN0IHRpbWV6b25lID0gdGltZXpvbmVzLmZpbmQoX3RpbWV6b25lID0+IF90aW1lem9uZS5uYW1lID09PSBuYW1lKTtcblxuICAgICAgICBpZiAodGltZXpvbmUpIHtcbiAgICAgICAgICAgIHRoaXMudGltZXpvbmVDaGFuZ2UuZW1pdCh0aW1lem9uZSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBpbmNyZW1lbnRUaW1lem9uZSgpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgdGltZXpvbmUgPSB0aGlzLmRhdGVwaWNrZXIudGltZXpvbmUkLnZhbHVlO1xuICAgICAgICBjb25zdCB0aW1lem9uZXMgPSB0aGlzLmRhdGVwaWNrZXIudGltZXpvbmVzJC52YWx1ZTtcblxuICAgICAgICBjb25zdCBjdXJyZW50Wm9uZSA9IHRpbWV6b25lcy5maW5kSW5kZXgoem9uZSA9PiB6b25lLm5hbWUgPT09IHRpbWV6b25lLm5hbWUgJiYgem9uZS5vZmZzZXQgPT09IHRpbWV6b25lLm9mZnNldCk7XG5cbiAgICAgICAgLy8gdHJ5IHRvIGdldCB0aGUgcHJldmlvdXMgem9uZVxuICAgICAgICB0aGlzLnRpbWV6b25lQ2hhbmdlLmVtaXQodGltZXpvbmVzW2N1cnJlbnRab25lICsgMV0gPyB0aW1lem9uZXNbY3VycmVudFpvbmUgKyAxXSA6IHRpbWV6b25lc1tjdXJyZW50Wm9uZV0pO1xuICAgIH1cblxuICAgIGRlY3JlbWVudFRpbWV6b25lKCk6IHZvaWQge1xuICAgICAgICBjb25zdCB0aW1lem9uZSA9IHRoaXMuZGF0ZXBpY2tlci50aW1lem9uZSQudmFsdWU7XG4gICAgICAgIGNvbnN0IHRpbWV6b25lcyA9IHRoaXMuZGF0ZXBpY2tlci50aW1lem9uZXMkLnZhbHVlO1xuXG4gICAgICAgIGNvbnN0IGN1cnJlbnRab25lID0gdGltZXpvbmVzLmZpbmRJbmRleCh6b25lID0+IHpvbmUubmFtZSA9PT0gdGltZXpvbmUubmFtZSAmJiB6b25lLm9mZnNldCA9PT0gdGltZXpvbmUub2Zmc2V0KTtcblxuICAgICAgICAvLyB0cnkgdG8gZ2V0IHRoZSBwcmV2aW91cyB6b25lXG4gICAgICAgIHRoaXMudGltZXpvbmVDaGFuZ2UuZW1pdCh0aW1lem9uZXNbY3VycmVudFpvbmUgLSAxXSA/IHRpbWV6b25lc1tjdXJyZW50Wm9uZSAtIDFdIDogdGltZXpvbmVzW2N1cnJlbnRab25lXSk7XG4gICAgfVxuXG4gICAgQEhvc3RMaXN0ZW5lcignZm9jdXNpbicpXG4gICAgb25Gb2N1c1dpdGhpbigpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuX2lzUmFuZ2VNb2RlKSB7XG4gICAgICAgICAgICB0aGlzLl9yYW5nZVNlcnZpY2UuaXNDaGFuZ2luZ1RpbWUgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgQEhvc3RMaXN0ZW5lcignZm9jdXNvdXQnKVxuICAgIG9uRm9jdXNPdXQoKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLl9pc1JhbmdlTW9kZSkge1xuICAgICAgICAgICAgdGhpcy5fcmFuZ2VTZXJ2aWNlLmlzQ2hhbmdpbmdUaW1lID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9XG59XG4iLCJAaWYgKGRhdGVwaWNrZXIuc2hvd1RpbWUkIHwgYXN5bmMpIHtcbiAgPHV4LXRpbWUtcGlja2VyXG4gICAgW3ZhbHVlXT1cInZhbHVlXCJcbiAgICAodmFsdWVDaGFuZ2UpPVwib25UaW1lQ2hhbmdlKCRldmVudClcIlxuICAgIFtzaG93U2Vjb25kc109XCJkYXRlcGlja2VyLnNob3dTZWNvbmRzJCB8IGFzeW5jXCJcbiAgICBbc2hvd01lcmlkaWFuXT1cImRhdGVwaWNrZXIuc2hvd01lcmlkaWFuJCB8IGFzeW5jXCJcbiAgICBbc2hvd1NwaW5uZXJzXT1cImRhdGVwaWNrZXIuc2hvd1NwaW5uZXJzJCB8IGFzeW5jXCJcbiAgICBbbWVyaWRpYW5zXT1cImRhdGVwaWNrZXIubWVyaWRpYW5zXCJcbiAgICBbbWluXT1cIm1pblwiXG4gICAgW21heF09XCJtYXhcIj5cbiAgPC91eC10aW1lLXBpY2tlcj5cbn1cblxuQGlmIChkYXRlcGlja2VyLnNob3dUaW1lem9uZSQgfCBhc3luYykge1xuICBAaWYgKGRhdGVwaWNrZXIuc2hvd1NwaW5uZXJzJCB8IGFzeW5jKSB7XG4gICAgPGRpdiBjbGFzcz1cInRpbWUtem9uZS1waWNrZXJcIj5cbiAgICAgIDx1eC1zcGluLWJ1dHRvblxuICAgICAgICBjbGFzcz1cInRpbWUtem9uZS1zcGlubmVyXCJcbiAgICAgICAgW3ZhbHVlXT1cIihkYXRlcGlja2VyLnRpbWV6b25lJCB8IGFzeW5jKT8ubmFtZVwiXG4gICAgICAgIFtyZWFkT25seV09XCJ0cnVlXCJcbiAgICAgICAgKGluY3JlbWVudCk9XCJpbmNyZW1lbnRUaW1lem9uZSgpXCJcbiAgICAgICAgKGRlY3JlbWVudCk9XCJkZWNyZW1lbnRUaW1lem9uZSgpXCJcbiAgICAgICAgaW5wdXRBcmlhTGFiZWw9XCJUaW1lIFpvbmVcIlxuICAgICAgICBpbmNyZW1lbnRBcmlhTGFiZWw9XCJTd2l0Y2ggdG8gdGhlIG5leHQgdGltZSB6b25lXCJcbiAgICAgICAgZGVjcmVtZW50QXJpYUxhYmVsPVwiU3dpdGNoIHRvIHRoZSBwcmV2aW91cyB0aW1lIHpvbmVcIj5cbiAgICAgIDwvdXgtc3Bpbi1idXR0b24+XG4gICAgPC9kaXY+XG4gIH1cbiAgQGlmICgoZGF0ZXBpY2tlci5zaG93U3Bpbm5lcnMkIHwgYXN5bmMpID09PSBmYWxzZSkge1xuICAgIDxkaXYgY2xhc3M9XCJ0aW1lLXpvbmUtcGlja2VyXCI+XG4gICAgICA8c2VsZWN0IGNsYXNzPVwiZm9ybS1jb250cm9sIHRpbWUtem9uZS1zZWxlY3RcIlxuICAgICAgICB0YWJpbmRleD1cIjBcIlxuICAgICAgICBbbmdNb2RlbF09XCIoZGF0ZXBpY2tlci50aW1lem9uZSQgfCBhc3luYyk/Lm5hbWVcIlxuICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzZWxlY3RUaW1lem9uZSgkZXZlbnQpXCJcbiAgICAgICAgYXJpYS1sYWJlbD1cIlRpbWV6b25lXCJcbiAgICAgICAgW2F0dHIuYXJpYS12YWx1ZXRleHRdPVwiKGRhdGVwaWNrZXIudGltZXpvbmUkIHwgYXN5bmMpPy5uYW1lXCI+XG4gICAgICAgIEBmb3IgKHpvbmUgb2YgZGF0ZXBpY2tlci50aW1lem9uZXMkIHwgYXN5bmM7IHRyYWNrIHpvbmUpIHtcbiAgICAgICAgICA8b3B0aW9uXG4gICAgICAgICAgICBbc2VsZWN0ZWRdPVwiem9uZS5uYW1lID09PSAoZGF0ZXBpY2tlci50aW1lem9uZSQgfCBhc3luYyk/Lm5hbWVcIlxuICAgICAgICAgICAgW3ZhbHVlXT1cInpvbmUubmFtZVwiPlxuICAgICAgICAgICAge3sgem9uZT8ubmFtZSB9fVxuICAgICAgICAgIDwvb3B0aW9uPlxuICAgICAgICB9XG4gICAgICA8L3NlbGVjdD5cbiAgICA8L2Rpdj5cbiAgfVxufVxuIl19