@nova-ui/bits
Version:
SolarWinds Nova Framework
190 lines • 41.1 kB
JavaScript
// © 2022 SolarWinds Worldwide, LLC. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, Input, Output, } from "@angular/core";
import moment from "moment/moment";
import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
import { LoggerService } from "../../../services/log-service";
import { QuickPickerComponent } from "../../time-frame-picker/quick-picker/quick-picker.component";
import { TimeframeService } from "../../time-frame-picker/services/timeframe.service";
import { TimeFramePickerComponent } from "../../time-frame-picker/time-frame-picker.component";
import * as i0 from "@angular/core";
import * as i1 from "../../time-frame-picker/services/timeframe.service";
import * as i2 from "../../../services/log-service";
import * as i3 from "../../button/button.component";
import * as i4 from "@angular/common";
import * as i5 from "../../dialog/dialog-footer.component";
import * as i6 from "../../popover/popover.component";
import * as i7 from "../../tooltip/tooltip.directive";
/**
*
* <div class="card card-inverse card-warning mb-3">
* <h4 class="card-header">This is a Convenience Component!</h4>
* <div class="card-block">
* <p class="card-text">
* Please check the section under the <strong>Additional Documentation</strong> sub-menu for more details and usage examples.
* </p>
* </div>
* <div class="card-footer">
* <a href="../additional-documentation/convenience-components.html" class="card-link">Read more...</a>
* </div>
* </div>
*
*/
export class TimeFrameBarComponent {
constructor(timeframeService, logger, changeDetectorRef) {
this.timeframeService = timeframeService;
this.logger = logger;
this.changeDetectorRef = changeDetectorRef;
/** Current time frame value */
this.timeFrame = {
startDatetime: moment(undefined),
endDatetime: moment(undefined),
};
this.timeFrameChange = new EventEmitter();
/** Current index in zoom history */
this.historyIndex = 0;
/** Emits an event when "Zoom Out" is clicked */
this.undo = new EventEmitter();
/** Emits an event when "Clear" is clicked */
this.clear = new EventEmitter();
this.closePopoverSubject = new Subject();
this.changed = false;
this.isLeftmostRange = false;
this.isRightmostRange = false;
this.defaultPickerTitle = $localize `:Label indicating that the user can select a custom start date and/or end date:Custom Range`;
this.destroy$ = new Subject();
this.presets = this.timeframeService.getDefaultPresets();
}
ngAfterContentInit() {
if (!this.timeFramePicker) {
throw new Error("TimeFramePickerComponent must be present in 'timeFrameSelection' slot");
}
if (this.timeFramePicker.minDate ||
this.timeFramePicker.maxDate ||
this.timeFramePicker.startModel) {
this.logger
.warn(`minDate, maxDate, and startModel inputs set on the TimeFramePickerComponent will be ignored. Use the minDate,
maxDate, and timeFrame inputs on the TimeFrameBarComponent instead.`);
}
this.timeFramePicker.model = this.pickerTimeframe;
this.timeFramePicker.minDate = this.minDate;
this.timeFramePicker.maxDate = this.maxDate;
this.timeFramePicker.changed
.pipe(takeUntil(this.destroy$))
.subscribe((tf) => this.updatePickerTf(tf));
if (this.quickPicker) {
this.quickPicker.presets =
this.quickPicker.presets ||
this.timeframeService.getDefaultPresets();
this.quickPicker.pickerTitle =
undefined === this.quickPicker.pickerTitle
? this.defaultPickerTitle
: this.quickPicker.pickerTitle;
this.quickPicker.presetSelected
.pipe(takeUntil(this.destroy$))
.subscribe((presetKey) => this.handlePresetSelection(presetKey));
}
}
ngOnChanges(changes) {
if (changes["timeFrame"]) {
const value = changes["timeFrame"].currentValue;
this.isLeftmostRange =
this.minDate && this.minDate.diff(value.startDatetime) >= 0;
this.isRightmostRange =
this.maxDate && this.maxDate.diff(value.endDatetime) <= 0;
this.pickerTimeframe = TimeframeService.cloneTimeFrame(value);
this.humanizedTimeframe = this.timeframeService
.getDuration(this.timeFrame)
.humanize();
}
}
onPopoverShown() {
this.timeFramePicker.model = this.pickerTimeframe;
this.timeFramePicker.changeDetector.markForCheck();
if (this.quickPicker) {
this.quickPicker.selectedPreset = this.timeFrame.selectedPresetId;
this.quickPicker.changeDetector.markForCheck();
}
}
onUndo() {
this.undo.emit();
}
onClear() {
this.clear.emit();
}
updatePickerTf(value) {
this.pickerTimeframe = value;
this.changed = !this.timeframeService.isEqual(this.pickerTimeframe, this.timeFrame);
this.changeDetectorRef.markForCheck();
}
handlePresetSelection(presetKey) {
this.pickerTimeframe =
this.timeframeService.getTimeframeByPresetId(presetKey);
this.quickPicker.selectedPreset = presetKey;
this.quickPicker.changeDetector.markForCheck();
this.closePopover(true);
}
closePopover(confirmed = false) {
if (confirmed) {
this.changeTimeFrame(TimeframeService.cloneTimeFrame(this.pickerTimeframe));
}
this.closePopoverSubject.next();
this.changed = false;
}
shiftRange(factor) {
const tf = this.timeFrame;
const shiftDuration = moment.duration(factor * tf.endDatetime.diff(tf.startDatetime));
this.changeTimeFrame(this.timeframeService.shiftTimeFrame(tf, shiftDuration));
}
ngOnDestroy() {
this.destroy$.next();
}
changeTimeFrame(value) {
this.timeFrameChange.emit(value);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TimeFrameBarComponent, deps: [{ token: i1.TimeframeService }, { token: i2.LoggerService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: TimeFrameBarComponent, selector: "nui-time-frame-bar", inputs: { minDate: "minDate", maxDate: "maxDate", timeFrame: "timeFrame", historyIndex: "historyIndex" }, outputs: { timeFrameChange: "timeFrameChange", undo: "undo", clear: "clear" }, host: { attributes: { "role": "spinbutton" } }, queries: [{ propertyName: "timeFramePicker", first: true, predicate: TimeFramePickerComponent, descendants: true }, { propertyName: "quickPicker", first: true, predicate: QuickPickerComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"nui-time-frame-bar py-2\">\n <div class=\"left-content pr-4\">\n <ng-content select=\"[leftContent]\"></ng-content>\n </div>\n <div\n class=\"prev\"\n nuiTooltip=\"{{ humanizedTimeframe }} before\"\n i18n-nuiTooltip=\"e.g. '7 days before' or 'an hour before'\"\n >\n <button\n nui-button\n type=\"button\"\n icon=\"caret-left\"\n displayStyle=\"action\"\n [disabled]=\"isLeftmostRange\"\n (click)=\"shiftRange(-1)\"\n ></button>\n </div>\n <div class=\"picker-label d-inline-flex px-3\">\n <nui-popover\n trigger=\"click\"\n [template]=\"popoverTimeFramePicker\"\n [hasPadding]=\"false\"\n [closePopover]=\"closePopoverSubject\"\n [modal]=\"true\"\n (shown)=\"onPopoverShown()\"\n [unlimited]=\"true\"\n >\n <div\n class=\"picker-label-content d-inline-flex align-items-center nui-text-link\"\n >\n <ng-content></ng-content>\n </div>\n </nui-popover>\n </div>\n <div\n class=\"next\"\n nuiTooltip=\"{{ humanizedTimeframe }} later\"\n i18n-nuiTooltip=\"e.g. '7 days later' or 'an hour later'\"\n >\n <button\n nui-button\n type=\"button\"\n icon=\"caret-right\"\n displayStyle=\"action\"\n [disabled]=\"isRightmostRange\"\n (click)=\"shiftRange(1)\"\n ></button>\n </div>\n <div class=\"right-content d-flex pl-4\">\n <div class=\"pr-3\">\n <button\n nui-button\n class=\"undo ml-1\"\n type=\"button\"\n displayStyle=\"action\"\n icon=\"undo\"\n *ngIf=\"historyIndex > 1\"\n (click)=\"onUndo()\"\n nuiTooltip=\"Undo\"\n i18n-nuiTooltip\n ></button>\n <button\n nui-button\n class=\"clear ml-1\"\n type=\"button\"\n displayStyle=\"action\"\n icon=\"reset\"\n *ngIf=\"historyIndex > 0\"\n (click)=\"onClear()\"\n nuiTooltip=\"Reset to default\"\n i18n-nuiTooltip\n ></button>\n </div>\n <ng-content select=\"[rightContent]\"></ng-content>\n </div>\n</div>\n\n<ng-template #popoverTimeFramePicker>\n <div [ngClass]=\"{ 'px-3 py-2': !quickPicker }\">\n <ng-content select=\"[timeFrameSelection]\"></ng-content>\n </div>\n <nui-dialog-footer *ngIf=\"changed\">\n <button\n nui-button\n type=\"button\"\n displayStyle=\"action\"\n class=\"cancel mr-3\"\n (click)=\"closePopover()\"\n i18n\n >\n Cancel\n </button>\n <button\n nui-button\n type=\"button\"\n displayStyle=\"primary\"\n class=\"use\"\n (click)=\"closePopover(true)\"\n i18n=\"\n Imperative indicating that the user would like to apply, or use,\n the current value\n \"\n >\n Use\n </button>\n </nui-dialog-footer>\n</ng-template>\n", styles: [".nui-time-frame-bar{display:grid;justify-content:space-between;align-items:center;grid-gap:5px;grid-template-columns:1fr auto auto auto 1fr;grid-template-areas:\"left-content prev picker-label next right-content\"}.nui-time-frame-bar .left-content{grid-area:left-content;justify-self:start}.nui-time-frame-bar .prev{grid-area:prev}.nui-time-frame-bar .picker-label{grid-area:picker-label;cursor:pointer}.nui-time-frame-bar .next{grid-area:next}.nui-time-frame-bar .right-content{grid-area:right-content;justify-self:end}\n"], dependencies: [{ kind: "component", type: i3.ButtonComponent, selector: "[nui-button]", inputs: ["displayStyle", "icon", "iconColor", "iconRight", "isBusy", "isEmpty", "ariaLabel", "isRepeat", "size"] }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5.DialogFooterComponent, selector: "nui-dialog-footer" }, { kind: "component", type: i6.PopoverComponent, selector: "nui-popover", inputs: ["closePopover", "openPopover", "disabled", "trigger", "template", "placement", "popoverTitle", "icon", "modal", "container", "hasPadding", "preventClosing", "unlimited", "delay", "withGrowAfterOpen", "popoverOverlayPosition"], outputs: ["shown", "hidden"] }, { kind: "directive", type: i7.TooltipDirective, selector: "[nuiTooltip]", inputs: ["tooltipPlacement", "nuiTooltipDisabled", "nuiTooltipEllipsis", "nuiTooltip"], exportAs: ["nuiTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TimeFrameBarComponent, decorators: [{
type: Component,
args: [{ selector: "nui-time-frame-bar", changeDetection: ChangeDetectionStrategy.OnPush, host: { role: "spinbutton" }, template: "<div class=\"nui-time-frame-bar py-2\">\n <div class=\"left-content pr-4\">\n <ng-content select=\"[leftContent]\"></ng-content>\n </div>\n <div\n class=\"prev\"\n nuiTooltip=\"{{ humanizedTimeframe }} before\"\n i18n-nuiTooltip=\"e.g. '7 days before' or 'an hour before'\"\n >\n <button\n nui-button\n type=\"button\"\n icon=\"caret-left\"\n displayStyle=\"action\"\n [disabled]=\"isLeftmostRange\"\n (click)=\"shiftRange(-1)\"\n ></button>\n </div>\n <div class=\"picker-label d-inline-flex px-3\">\n <nui-popover\n trigger=\"click\"\n [template]=\"popoverTimeFramePicker\"\n [hasPadding]=\"false\"\n [closePopover]=\"closePopoverSubject\"\n [modal]=\"true\"\n (shown)=\"onPopoverShown()\"\n [unlimited]=\"true\"\n >\n <div\n class=\"picker-label-content d-inline-flex align-items-center nui-text-link\"\n >\n <ng-content></ng-content>\n </div>\n </nui-popover>\n </div>\n <div\n class=\"next\"\n nuiTooltip=\"{{ humanizedTimeframe }} later\"\n i18n-nuiTooltip=\"e.g. '7 days later' or 'an hour later'\"\n >\n <button\n nui-button\n type=\"button\"\n icon=\"caret-right\"\n displayStyle=\"action\"\n [disabled]=\"isRightmostRange\"\n (click)=\"shiftRange(1)\"\n ></button>\n </div>\n <div class=\"right-content d-flex pl-4\">\n <div class=\"pr-3\">\n <button\n nui-button\n class=\"undo ml-1\"\n type=\"button\"\n displayStyle=\"action\"\n icon=\"undo\"\n *ngIf=\"historyIndex > 1\"\n (click)=\"onUndo()\"\n nuiTooltip=\"Undo\"\n i18n-nuiTooltip\n ></button>\n <button\n nui-button\n class=\"clear ml-1\"\n type=\"button\"\n displayStyle=\"action\"\n icon=\"reset\"\n *ngIf=\"historyIndex > 0\"\n (click)=\"onClear()\"\n nuiTooltip=\"Reset to default\"\n i18n-nuiTooltip\n ></button>\n </div>\n <ng-content select=\"[rightContent]\"></ng-content>\n </div>\n</div>\n\n<ng-template #popoverTimeFramePicker>\n <div [ngClass]=\"{ 'px-3 py-2': !quickPicker }\">\n <ng-content select=\"[timeFrameSelection]\"></ng-content>\n </div>\n <nui-dialog-footer *ngIf=\"changed\">\n <button\n nui-button\n type=\"button\"\n displayStyle=\"action\"\n class=\"cancel mr-3\"\n (click)=\"closePopover()\"\n i18n\n >\n Cancel\n </button>\n <button\n nui-button\n type=\"button\"\n displayStyle=\"primary\"\n class=\"use\"\n (click)=\"closePopover(true)\"\n i18n=\"\n Imperative indicating that the user would like to apply, or use,\n the current value\n \"\n >\n Use\n </button>\n </nui-dialog-footer>\n</ng-template>\n", styles: [".nui-time-frame-bar{display:grid;justify-content:space-between;align-items:center;grid-gap:5px;grid-template-columns:1fr auto auto auto 1fr;grid-template-areas:\"left-content prev picker-label next right-content\"}.nui-time-frame-bar .left-content{grid-area:left-content;justify-self:start}.nui-time-frame-bar .prev{grid-area:prev}.nui-time-frame-bar .picker-label{grid-area:picker-label;cursor:pointer}.nui-time-frame-bar .next{grid-area:next}.nui-time-frame-bar .right-content{grid-area:right-content;justify-self:end}\n"] }]
}], ctorParameters: () => [{ type: i1.TimeframeService }, { type: i2.LoggerService }, { type: i0.ChangeDetectorRef }], propDecorators: { minDate: [{
type: Input
}], maxDate: [{
type: Input
}], timeFrame: [{
type: Input
}], timeFrameChange: [{
type: Output
}], historyIndex: [{
type: Input
}], undo: [{
type: Output
}], clear: [{
type: Output
}], timeFramePicker: [{
type: ContentChild,
args: [TimeFramePickerComponent]
}], quickPicker: [{
type: ContentChild,
args: [QuickPickerComponent]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZS1mcmFtZS1iYXIuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9jb252ZW5pZW5jZS90aW1lLWZyYW1lLWJhci90aW1lLWZyYW1lLWJhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbnZlbmllbmNlL3RpbWUtZnJhbWUtYmFyL3RpbWUtZnJhbWUtYmFyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHlEQUF5RDtBQUN6RCxFQUFFO0FBQ0YsK0VBQStFO0FBQy9FLDRFQUE0RTtBQUM1RSw4RUFBOEU7QUFDOUUsK0VBQStFO0FBQy9FLDhFQUE4RTtBQUM5RSw0REFBNEQ7QUFDNUQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSx1REFBdUQ7QUFDdkQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSw0RUFBNEU7QUFDNUUsK0VBQStFO0FBQy9FLDBFQUEwRTtBQUMxRSxpRkFBaUY7QUFDakYsNkVBQTZFO0FBQzdFLGlCQUFpQjtBQUVqQixPQUFPLEVBRUgsdUJBQXVCLEVBQ3ZCLGlCQUFpQixFQUNqQixTQUFTLEVBQ1QsWUFBWSxFQUNaLFlBQVksRUFDWixLQUFLLEVBR0wsTUFBTSxHQUVULE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sTUFBa0IsTUFBTSxlQUFlLENBQUM7QUFDL0MsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMvQixPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFM0MsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTlELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDZEQUE2RCxDQUFDO0FBQ25HLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLG9EQUFvRCxDQUFDO0FBQ3RGLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLHFEQUFxRCxDQUFD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Q0FDakQsQ0FBQztRQUNGLElBQUksQ0FBQyxlQUFlLENBQ2hCLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxjQUFjLENBQUMsRUFBRSxFQUFFLGFBQWEsQ0FBQyxDQUMxRCxDQUFDO0lBQ04sQ0FBQztJQUVNLFdBQVc7UUFDZCxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFTyxlQUFlLENBQUMsS0FBaUI7UUFDckMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDckMsQ0FBQzsrR0FuS1EscUJBQXFCO21HQUFyQixxQkFBcUIsZ1ZBdUJoQix3QkFBd0IsOEVBRXhCLG9CQUFvQixxRUMxRnRDLHcwR0E0R0E7OzRGRDNDYSxxQkFBcUI7a0JBUGpDLFNBQVM7K0JBQ0ksb0JBQW9CLG1CQUdiLHVCQUF1QixDQUFDLE1BQU0sUUFDekMsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFO2lKQU1uQixPQUFPO3NCQUFmLEtBQUs7Z0JBRUcsT0FBTztzQkFBZixLQUFLO2dCQUdHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBSUksZUFBZTtzQkFBeEIsTUFBTTtnQkFHRSxZQUFZO3NCQUFwQixLQUFLO2dCQUdJLElBQUk7c0JBQWIsTUFBTTtnQkFFRyxLQUFLO3NCQUFkLE1BQU07Z0JBR1AsZUFBZTtzQkFEZCxZQUFZO3VCQUFDLHdCQUF3QjtnQkFFRixXQUFXO3NCQUE5QyxZQUFZO3VCQUFDLG9CQUFvQiIsInNvdXJjZXNDb250ZW50IjpbIi8vIMKpIDIwMjIgU29sYXJXaW5kcyBXb3JsZHdpZGUsIExMQy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4vLyAgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG9cbi8vICBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZVxuLy8gIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vclxuLy8gIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4vLyAgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1Jcbi8vICBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbi8vICBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vICBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4vLyAgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbi8vICBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyAgVEhFIFNPRlRXQVJFLlxuXG5pbXBvcnQge1xuICAgIEFmdGVyQ29udGVudEluaXQsXG4gICAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gICAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgQ29tcG9uZW50LFxuICAgIENvbnRlbnRDaGlsZCxcbiAgICBFdmVudEVtaXR0ZXIsXG4gICAgSW5wdXQsXG4gICAgT25DaGFuZ2VzLFxuICAgIE9uRGVzdHJveSxcbiAgICBPdXRwdXQsXG4gICAgU2ltcGxlQ2hhbmdlcyxcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCBtb21lbnQsIHsgTW9tZW50IH0gZnJvbSBcIm1vbWVudC9tb21lbnRcIjtcbmltcG9ydCB7IFN1YmplY3QgfSBmcm9tIFwicnhqc1wiO1xuaW1wb3J0IHsgdGFrZVVudGlsIH0gZnJvbSBcInJ4anMvb3BlcmF0b3JzXCI7XG5cbmltcG9ydCB7IExvZ2dlclNlcnZpY2UgfSBmcm9tIFwiLi4vLi4vLi4vc2VydmljZXMvbG9nLXNlcnZpY2VcIjtcbmltcG9ydCB7IElUaW1lZnJhbWUgfSBmcm9tIFwiLi4vLi4vdGltZS1mcmFtZS1waWNrZXIvcHVibGljLWFwaVwiO1xuaW1wb3J0IHsgUXVpY2tQaWNrZXJDb21wb25lbnQgfSBmcm9tIFwiLi4vLi4vdGltZS1mcmFtZS1waWNrZXIvcXVpY2stcGlja2VyL3F1aWNrLXBpY2tlci5jb21wb25lbnRcIjtcbmltcG9ydCB7IFRpbWVmcmFtZVNlcnZpY2UgfSBmcm9tIFwiLi4vLi4vdGltZS1mcmFtZS1waWNrZXIvc2VydmljZXMvdGltZWZyYW1lLnNlcnZpY2VcIjtcbmltcG9ydCB7IFRpbWVGcmFtZVBpY2tlckNvbXBvbmVudCB9IGZyb20gXCIuLi8uLi90aW1lLWZyYW1lLXBpY2tlci90aW1lLWZyYW1lLXBpY2tlci5jb21wb25lbnRcIjtcblxuLyoqXG4gKlxuICogPGRpdiBjbGFzcz1cImNhcmQgY2FyZC1pbnZlcnNlIGNhcmQtd2FybmluZyBtYi0zXCI+XG4gKiAgIDxoNCBjbGFzcz1cImNhcmQtaGVhZGVyXCI+VGhpcyBpcyBhIENvbnZlbmllbmNlIENvbXBvbmVudCE8L2g0PlxuICogICA8ZGl2IGNsYXNzPVwiY2FyZC1ibG9ja1wiPlxuICogICAgIDxwIGNsYXNzPVwiY2FyZC10ZXh0XCI+XG4gKiAgICAgICBQbGVhc2UgY2hlY2sgdGhlIHNlY3Rpb24gdW5kZXIgdGhlIDxzdHJvbmc+QWRkaXRpb25hbCBEb2N1bWVudGF0aW9uPC9zdHJvbmc+IHN1Yi1tZW51IGZvciBtb3JlIGRldGFpbHMgYW5kIHVzYWdlIGV4YW1wbGVzLlxuICogICAgIDwvcD5cbiAqICAgPC9kaXY+XG4gKiAgIDxkaXYgY2xhc3M9XCJjYXJkLWZvb3RlclwiPlxuICogICAgIDxhIGhyZWY9XCIuLi9hZGRpdGlvbmFsLWRvY3VtZW50YXRpb24vY29udmVuaWVuY2UtY29tcG9uZW50cy5odG1sXCIgY2xhc3M9XCJjYXJkLWxpbmtcIj5SZWFkIG1vcmUuLi48L2E+XG4gKiAgIDwvZGl2PlxuICogPC9kaXY+XG4gKlxuICovXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogXCJudWktdGltZS1mcmFtZS1iYXJcIixcbiAgICB0ZW1wbGF0ZVVybDogXCIuL3RpbWUtZnJhbWUtYmFyLmNvbXBvbmVudC5odG1sXCIsXG4gICAgc3R5bGVVcmxzOiBbXCIuL3RpbWUtZnJhbWUtYmFyLmNvbXBvbmVudC5sZXNzXCJdLFxuICAgIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICAgIGhvc3Q6IHsgcm9sZTogXCJzcGluYnV0dG9uXCIgfSxcbn0pXG5leHBvcnQgY2xhc3MgVGltZUZyYW1lQmFyQ29tcG9uZW50XG4gICAgaW1wbGVtZW50cyBBZnRlckNvbnRlbnRJbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveVxue1xuICAgIC8qKiBFYXJsaWVzdCBzZWxlY3RhYmxlIGRhdGUgKi9cbiAgICBASW5wdXQoKSBtaW5EYXRlOiBNb21lbnQ7XG4gICAgLyoqIExhdGVzdCBzZWxlY3RhYmxlIGRhdGUgKi9cbiAgICBASW5wdXQoKSBtYXhEYXRlOiBNb21lbnQ7XG5cbiAgICAvKiogQ3VycmVudCB0aW1lIGZyYW1lIHZhbHVlICovXG4gICAgQElucHV0KCkgdGltZUZyYW1lOiBJVGltZWZyYW1lID0ge1xuICAgICAgICBzdGFydERhdGV0aW1lOiBtb21lbnQodW5kZWZpbmVkKSxcbiAgICAgICAgZW5kRGF0ZXRpbWU6IG1vbWVudCh1bmRlZmluZWQpLFxuICAgIH07XG4gICAgQE91dHB1dCgpIHRpbWVGcmFtZUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8SVRpbWVmcmFtZT4oKTtcblxuICAgIC8qKiBDdXJyZW50IGluZGV4IGluIHpvb20gaGlzdG9yeSAqL1xuICAgIEBJbnB1dCgpIGhpc3RvcnlJbmRleCA9IDA7XG5cbiAgICAvKiogRW1pdHMgYW4gZXZlbnQgd2hlbiBcIlpvb20gT3V0XCIgaXMgY2xpY2tlZCAqL1xuICAgIEBPdXRwdXQoKSB1bmRvID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuICAgIC8qKiBFbWl0cyBhbiBldmVudCB3aGVuIFwiQ2xlYXJcIiBpcyBjbGlja2VkICovXG4gICAgQE91dHB1dCgpIGNsZWFyID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuXG4gICAgQENvbnRlbnRDaGlsZChUaW1lRnJhbWVQaWNrZXJDb21wb25lbnQpXG4gICAgdGltZUZyYW1lUGlja2VyOiBUaW1lRnJhbWVQaWNrZXJDb21wb25lbnQ7XG4gICAgQENvbnRlbnRDaGlsZChRdWlja1BpY2tlckNvbXBvbmVudCkgcXVpY2tQaWNrZXI6IFF1aWNrUGlja2VyQ29tcG9uZW50O1xuXG4gICAgcHVibGljIHByZXNldHM7XG4gICAgcHVibGljIHBpY2tlclRpbWVmcmFtZTogSVRpbWVmcmFtZTtcbiAgICBwdWJsaWMgY2xvc2VQb3BvdmVyU3ViamVjdCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG4gICAgcHVibGljIGNoYW5nZWQ6IGJvb2xlYW4gPSBmYWxzZTtcbiAgICBwdWJsaWMgaXNMZWZ0bW9zdFJhbmdlID0gZmFsc2U7XG4gICAgcHVibGljIGlzUmlnaHRtb3N0UmFuZ2UgPSBmYWxzZTtcbiAgICBwdWJsaWMgaHVtYW5pemVkVGltZWZyYW1lOiBzdHJpbmc7XG4gICAgcHVibGljIHJlYWRvbmx5IGRlZmF1bHRQaWNrZXJUaXRsZSA9ICRsb2NhbGl6ZWA6TGFiZWwgaW5kaWNhdGluZyB0aGF0IHRoZSB1c2VyIGNhbiBzZWxlY3QgYSBjdXN0b20gc3RhcnQgZGF0ZSBhbmQvb3IgZW5kIGRhdGU6Q3VzdG9tIFJhbmdlYDtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHB1YmxpYyB0aW1lZnJhbWVTZXJ2aWNlOiBUaW1lZnJhbWVTZXJ2aWNlLFxuICAgICAgICBwcml2YXRlIGxvZ2dlcjogTG9nZ2VyU2VydmljZSxcbiAgICAgICAgcHJpdmF0ZSBjaGFuZ2VEZXRlY3RvclJlZjogQ2hhbmdlRGV0ZWN0b3JSZWZcbiAgICApIHtcbiAgICAgICAgdGhpcy5wcmVzZXRzID0gdGhpcy50aW1lZnJhbWVTZXJ2aWNlLmdldERlZmF1bHRQcmVzZXRzKCk7XG4gICAgfVxuXG4gICAgcHVibGljIG5nQWZ0ZXJDb250ZW50SW5pdCgpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLnRpbWVGcmFtZVBpY2tlcikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgIFwiVGltZUZyYW1lUGlja2VyQ29tcG9uZW50IG11c3QgYmUgcHJlc2VudCBpbiAndGltZUZyYW1lU2VsZWN0aW9uJyBzbG90XCJcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoXG4gICAgICAgICAgICB0aGlzLnRpbWVGcmFtZVBpY2tlci5taW5EYXRlIHx8XG4gICAgICAgICAgICB0aGlzLnRpbWVGcmFtZVBpY2tlci5tYXhEYXRlIHx8XG4gICAgICAgICAgICB0aGlzLnRpbWVGcmFtZVBpY2tlci5zdGFydE1vZGVsXG4gICAgICAgICkge1xuICAgICAgICAgICAgdGhpcy5sb2dnZXJcbiAgICAgICAgICAgICAgICAud2FybihgbWluRGF0ZSwgbWF4RGF0ZSwgYW5kIHN0YXJ0TW9kZWwgaW5wdXRzIHNldCBvbiB0aGUgVGltZUZyYW1lUGlja2VyQ29tcG9uZW50IHdpbGwgYmUgaWdub3JlZC4gVXNlIHRoZSBtaW5EYXRlLFxubWF4RGF0ZSwgYW5kIHRpbWVGcmFtZSBpbnB1dHMgb24gdGhlIFRpbWVGcmFtZUJhckNvbXBvbmVudCBpbnN0ZWFkLmApO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy50aW1lRnJhbWVQaWNrZXIubW9kZWwgPSB0aGlzLnBpY2tlclRpbWVmcmFtZTtcbiAgICAgICAgdGhpcy50aW1lRnJhbWVQaWNrZXIubWluRGF0ZSA9IHRoaXMubWluRGF0ZTtcbiAgICAgICAgdGhpcy50aW1lRnJhbWVQaWNrZXIubWF4RGF0ZSA9IHRoaXMubWF4RGF0ZTtcbiAgICAgICAgdGhpcy50aW1lRnJhbWVQaWNrZXIuY2hhbmdlZFxuICAgICAgICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgodGY6IElUaW1lZnJhbWUpID0+IHRoaXMudXBkYXRlUGlja2VyVGYodGYpKTtcblxuICAgICAgICBpZiAodGhpcy5xdWlja1BpY2tlcikge1xuICAgICAgICAgICAgdGhpcy5xdWlja1BpY2tlci5wcmVzZXRzID1cbiAgICAgICAgICAgICAgICB0aGlzLnF1aWNrUGlja2VyLnByZXNldHMgfHxcbiAgICAgICAgICAgICAgICB0aGlzLnRpbWVmcmFtZVNlcnZpY2UuZ2V0RGVmYXVsdFByZXNldHMoKTtcbiAgICAgICAgICAgIHRoaXMucXVpY2tQaWNrZXIucGlja2VyVGl0bGUgPVxuICAgICAgICAgICAgICAgIHVuZGVmaW5lZCA9PT0gdGhpcy5xdWlja1BpY2tlci5waWNrZXJUaXRsZVxuICAgICAgICAgICAgICAgICAgICA/IHRoaXMuZGVmYXVsdFBpY2tlclRpdGxlXG4gICAgICAgICAgICAgICAgICAgIDogdGhpcy5xdWlja1BpY2tlci5waWNrZXJUaXRsZTtcbiAgICAgICAgICAgIHRoaXMucXVpY2tQaWNrZXIucHJlc2V0U2VsZWN0ZWRcbiAgICAgICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAgICAgICAgICAgLnN1YnNjcmliZSgocHJlc2V0S2V5OiBzdHJpbmcpID0+XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlUHJlc2V0U2VsZWN0aW9uKHByZXNldEtleSlcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICAgICAgaWYgKGNoYW5nZXNbXCJ0aW1lRnJhbWVcIl0pIHtcbiAgICAgICAgICAgIGNvbnN0IHZhbHVlID0gY2hhbmdlc1tcInRpbWVGcmFtZVwiXS5jdXJyZW50VmFsdWU7XG4gICAgICAgICAgICB0aGlzLmlzTGVmdG1vc3RSYW5nZSA9XG4gICAgICAgICAgICAgICAgdGhpcy5taW5EYXRlICYmIHRoaXMubWluRGF0ZS5kaWZmKHZhbHVlLnN0YXJ0RGF0ZXRpbWUpID49IDA7XG4gICAgICAgICAgICB0aGlzLmlzUmlnaHRtb3N0UmFuZ2UgPVxuICAgICAgICAgICAgICAgIHRoaXMubWF4RGF0ZSAmJiB0aGlzLm1heERhdGUuZGlmZih2YWx1ZS5lbmREYXRldGltZSkgPD0gMDtcbiAgICAgICAgICAgIHRoaXMucGlja2VyVGltZWZyYW1lID0gVGltZWZyYW1lU2VydmljZS5jbG9uZVRpbWVGcmFtZSh2YWx1ZSk7XG4gICAgICAgICAgICB0aGlzLmh1bWFuaXplZFRpbWVmcmFtZSA9IHRoaXMudGltZWZyYW1lU2VydmljZVxuICAgICAgICAgICAgICAgIC5nZXREdXJhdGlvbih0aGlzLnRpbWVGcmFtZSlcbiAgICAgICAgICAgICAgICAuaHVtYW5pemUoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBvblBvcG92ZXJTaG93bigpOiB2b2lkIHtcbiAgICAgICAgdGhpcy50aW1lRnJhbWVQaWNrZXIubW9kZWwgPSB0aGlzLnBpY2tlclRpbWVmcmFtZTtcbiAgICAgICAgdGhpcy50aW1lRnJhbWVQaWNrZXIuY2hhbmdlRGV0ZWN0b3IubWFya0ZvckNoZWNrKCk7XG5cbiAgICAgICAgaWYgKHRoaXMucXVpY2tQaWNrZXIpIHtcbiAgICAgICAgICAgIHRoaXMucXVpY2tQaWNrZXIuc2VsZWN0ZWRQcmVzZXQgPSB0aGlzLnRpbWVGcmFtZS5zZWxlY3RlZFByZXNldElkO1xuICAgICAgICAgICAgdGhpcy5xdWlja1BpY2tlci5jaGFuZ2VEZXRlY3Rvci5tYXJrRm9yQ2hlY2soKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBvblVuZG8oKTogdm9pZCB7XG4gICAgICAgIHRoaXMudW5kby5lbWl0KCk7XG4gICAgfVxuXG4gICAgcHVibGljIG9uQ2xlYXIoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuY2xlYXIuZW1pdCgpO1xuICAgIH1cblxuICAgIHB1YmxpYyB1cGRhdGVQaWNrZXJUZih2YWx1ZTogSVRpbWVmcmFtZSk6IHZvaWQge1xuICAgICAgICB0aGlzLnBpY2tlclRpbWVmcmFtZSA9IHZhbHVlO1xuICAgICAgICB0aGlzLmNoYW5nZWQgPSAhdGhpcy50aW1lZnJhbWVTZXJ2aWNlLmlzRXF1YWwoXG4gICAgICAgICAgICB0aGlzLnBpY2tlclRpbWVmcmFtZSxcbiAgICAgICAgICAgIHRoaXMudGltZUZyYW1lXG4gICAgICAgICk7XG4gICAgICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYubWFya0ZvckNoZWNrKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGhhbmRsZVByZXNldFNlbGVjdGlvbihwcmVzZXRLZXk6IHN0cmluZyk6IHZvaWQge1xuICAgICAgICB0aGlzLnBpY2tlclRpbWVmcmFtZSA9XG4gICAgICAgICAgICB0aGlzLnRpbWVmcmFtZVNlcnZpY2UuZ2V0VGltZWZyYW1lQnlQcmVzZXRJZChwcmVzZXRLZXkpO1xuXG4gICAgICAgIHRoaXMucXVpY2tQaWNrZXIuc2VsZWN0ZWRQcmVzZXQgPSBwcmVzZXRLZXk7XG4gICAgICAgIHRoaXMucXVpY2tQaWNrZXIuY2hhbmdlRGV0ZWN0b3IubWFya0ZvckNoZWNrKCk7XG5cbiAgICAgICAgdGhpcy5jbG9zZVBvcG92ZXIodHJ1ZSk7XG4gICAgfVxuXG4gICAgcHVibGljIGNsb3NlUG9wb3Zlcihjb25maXJtZWQgPSBmYWxzZSk6IHZvaWQge1xuICAgICAgICBpZiAoY29uZmlybWVkKSB7XG4gICAgICAgICAgICB0aGlzLmNoYW5nZVRpbWVGcmFtZShcbiAgICAgICAgICAgICAgICBUaW1lZnJhbWVTZXJ2aWNlLmNsb25lVGltZUZyYW1lKHRoaXMucGlja2VyVGltZWZyYW1lKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmNsb3NlUG9wb3ZlclN1YmplY3QubmV4dCgpO1xuICAgICAgICB0aGlzLmNoYW5nZWQgPSBmYWxzZTtcbiAgICB9XG5cbiAgICBwdWJsaWMgc2hpZnRSYW5nZShmYWN0b3I6IG51bWJlcik6IHZvaWQge1xuICAgICAgICBjb25zdCB0ZiA9IHRoaXMudGltZUZyYW1lO1xuICAgICAgICBjb25zdCBzaGlmdER1cmF0aW9uID0gbW9tZW50LmR1cmF0aW9uKFxuICAgICAgICAgICAgZmFjdG9yICogdGYuZW5kRGF0ZXRpbWUuZGlmZih0Zi5zdGFydERhdGV0aW1lKVxuICAgICAgICApO1xuICAgICAgICB0aGlzLmNoYW5nZVRpbWVGcmFtZShcbiAgICAgICAgICAgIHRoaXMudGltZWZyYW1lU2VydmljZS5zaGlmdFRpbWVGcmFtZSh0Ziwgc2hpZnREdXJhdGlvbilcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZGVzdHJveSQubmV4dCgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgY2hhbmdlVGltZUZyYW1lKHZhbHVlOiBJVGltZWZyYW1lKSB7XG4gICAgICAgIHRoaXMudGltZUZyYW1lQ2hhbmdlLmVtaXQodmFsdWUpO1xuICAgIH1cbn1cbiIsIjxkaXYgY2xhc3M9XCJudWktdGltZS1mcmFtZS1iYXIgcHktMlwiPlxuICAgIDxkaXYgY2xhc3M9XCJsZWZ0LWNvbnRlbnQgcHItNFwiPlxuICAgICAgICA8bmctY29udGVudCBzZWxlY3Q9XCJbbGVmdENvbnRlbnRdXCI+PC9uZy1jb250ZW50PlxuICAgIDwvZGl2PlxuICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJwcmV2XCJcbiAgICAgICAgbnVpVG9vbHRpcD1cInt7IGh1bWFuaXplZFRpbWVmcmFtZSB9fSBiZWZvcmVcIlxuICAgICAgICBpMThuLW51aVRvb2x0aXA9XCJlLmcuICc3IGRheXMgYmVmb3JlJyBvciAnYW4gaG91ciBiZWZvcmUnXCJcbiAgICA+XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICAgIG51aS1idXR0b25cbiAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgaWNvbj1cImNhcmV0LWxlZnRcIlxuICAgICAgICAgICAgZGlzcGxheVN0eWxlPVwiYWN0aW9uXCJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCJpc0xlZnRtb3N0UmFuZ2VcIlxuICAgICAgICAgICAgKGNsaWNrKT1cInNoaWZ0UmFuZ2UoLTEpXCJcbiAgICAgICAgPjwvYnV0dG9uPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJwaWNrZXItbGFiZWwgZC1pbmxpbmUtZmxleCBweC0zXCI+XG4gICAgICAgIDxudWktcG9wb3ZlclxuICAgICAgICAgICAgdHJpZ2dlcj1cImNsaWNrXCJcbiAgICAgICAgICAgIFt0ZW1wbGF0ZV09XCJwb3BvdmVyVGltZUZyYW1lUGlja2VyXCJcbiAgICAgICAgICAgIFtoYXNQYWRkaW5nXT1cImZhbHNlXCJcbiAgICAgICAgICAgIFtjbG9zZVBvcG92ZXJdPVwiY2xvc2VQb3BvdmVyU3ViamVjdFwiXG4gICAgICAgICAgICBbbW9kYWxdPVwidHJ1ZVwiXG4gICAgICAgICAgICAoc2hvd24pPVwib25Qb3BvdmVyU2hvd24oKVwiXG4gICAgICAgICAgICBbdW5saW1pdGVkXT1cInRydWVcIlxuICAgICAgICA+XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgY2xhc3M9XCJwaWNrZXItbGFiZWwtY29udGVudCBkLWlubGluZS1mbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBudWktdGV4dC1saW5rXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9udWktcG9wb3Zlcj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwibmV4dFwiXG4gICAgICAgIG51aVRvb2x0aXA9XCJ7eyBodW1hbml6ZWRUaW1lZnJhbWUgfX0gbGF0ZXJcIlxuICAgICAgICBpMThuLW51aVRvb2x0aXA9XCJlLmcuICc3IGRheXMgbGF0ZXInIG9yICdhbiBob3VyIGxhdGVyJ1wiXG4gICAgPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICBudWktYnV0dG9uXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgIGljb249XCJjYXJldC1yaWdodFwiXG4gICAgICAgICAgICBkaXNwbGF5U3R5bGU9XCJhY3Rpb25cIlxuICAgICAgICAgICAgW2Rpc2FibGVkXT1cImlzUmlnaHRtb3N0UmFuZ2VcIlxuICAgICAgICAgICAgKGNsaWNrKT1cInNoaWZ0UmFuZ2UoMSlcIlxuICAgICAgICA+PC9idXR0b24+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInJpZ2h0LWNvbnRlbnQgZC1mbGV4IHBsLTRcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cInByLTNcIj5cbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgICBudWktYnV0dG9uXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJ1bmRvIG1sLTFcIlxuICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgIGRpc3BsYXlTdHlsZT1cImFjdGlvblwiXG4gICAgICAgICAgICAgICAgaWNvbj1cInVuZG9cIlxuICAgICAgICAgICAgICAgICpuZ0lmPVwiaGlzdG9yeUluZGV4ID4gMVwiXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uVW5kbygpXCJcbiAgICAgICAgICAgICAgICBudWlUb29sdGlwPVwiVW5kb1wiXG4gICAgICAgICAgICAgICAgaTE4bi1udWlUb29sdGlwXG4gICAgICAgICAgICA+PC9idXR0b24+XG4gICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgbnVpLWJ1dHRvblxuICAgICAgICAgICAgICAgIGNsYXNzPVwiY2xlYXIgbWwtMVwiXG4gICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgZGlzcGxheVN0eWxlPVwiYWN0aW9uXCJcbiAgICAgICAgICAgICAgICBpY29uPVwicmVzZXRcIlxuICAgICAgICAgICAgICAgICpuZ0lmPVwiaGlzdG9yeUluZGV4ID4gMFwiXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uQ2xlYXIoKVwiXG4gICAgICAgICAgICAgICAgbnVpVG9vbHRpcD1cIlJlc2V0IHRvIGRlZmF1bHRcIlxuICAgICAgICAgICAgICAgIGkxOG4tbnVpVG9vbHRpcFxuICAgICAgICAgICAgPjwvYnV0dG9uPlxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPG5nLWNvbnRlbnQgc2VsZWN0PVwiW3JpZ2h0Q29udGVudF1cIj48L25nLWNvbnRlbnQ+XG4gICAgPC9kaXY+XG48L2Rpdj5cblxuPG5nLXRlbXBsYXRlICNwb3BvdmVyVGltZUZyYW1lUGlja2VyPlxuICAgIDxkaXYgW25nQ2xhc3NdPVwieyAncHgtMyBweS0yJzogIXF1aWNrUGlja2VyIH1cIj5cbiAgICAgICAgPG5nLWNvbnRlbnQgc2VsZWN0PVwiW3RpbWVGcmFtZVNlbGVjdGlvbl1cIj48L25nLWNvbnRlbnQ+XG4gICAgPC9kaXY+XG4gICAgPG51aS1kaWFsb2ctZm9vdGVyICpuZ0lmPVwiY2hhbmdlZFwiPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICBudWktYnV0dG9uXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgIGRpc3BsYXlTdHlsZT1cImFjdGlvblwiXG4gICAgICAgICAgICBjbGFzcz1cImNhbmNlbCBtci0zXCJcbiAgICAgICAgICAgIChjbGljayk9XCJjbG9zZVBvcG92ZXIoKVwiXG4gICAgICAgICAgICBpMThuXG4gICAgICAgID5cbiAgICAgICAgICAgIENhbmNlbFxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgbnVpLWJ1dHRvblxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICBkaXNwbGF5U3R5bGU9XCJwcmltYXJ5XCJcbiAgICAgICAgICAgIGNsYXNzPVwidXNlXCJcbiAgICAgICAgICAgIChjbGljayk9XCJjbG9zZVBvcG92ZXIodHJ1ZSlcIlxuICAgICAgICAgICAgaTE4bj1cIlxuICAgICAgICAgICAgICAgIEltcGVyYXRpdmUgaW5kaWNhdGluZyB0aGF0IHRoZSB1c2VyIHdvdWxkIGxpa2UgdG8gYXBwbHksIG9yIHVzZSxcbiAgICAgICAgICAgICAgICB0aGUgY3VycmVudCB2YWx1ZVxuICAgICAgICAgICAgXCJcbiAgICAgICAgPlxuICAgICAgICAgICAgVXNlXG4gICAgICAgIDwvYnV0dG9uPlxuICAgIDwvbnVpLWRpYWxvZy1mb290ZXI+XG48L25nLXRlbXBsYXRlPlxuIl19