@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
394 lines • 60.9 kB
JavaScript
import { Component, ContentChild, ContentChildren, EventEmitter, inject, Input, Output, QueryList } from '@angular/core';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { tick } from '../../common/index';
import { WizardStepComponent } from './wizard-step.component';
import { WizardService } from './wizard.service';
import * as i0 from "@angular/core";
import * as i1 from "../../directives/accessibility/focus-indicator/default-focus-indicator.directive";
import * as i2 from "../../directives/accessibility/focus-indicator/focus-indicator.directive";
import * as i3 from "../../directives/accessibility/tabbable-list/tabbable-list.directive";
import * as i4 from "../../directives/accessibility/tabbable-list/tabbable-list-item.directive";
import * as i5 from "@angular/common";
import * as i6 from "../icon/icon.component";
import * as i7 from "../tooltip/tooltip.directive";
let uniqueId = 0;
export class WizardComponent {
constructor() {
this._wizardService = inject(WizardService);
/** Defines whether or not the wizard should be displayed in a `horizontal` or `vertical` layout. */
this.orientation = 'horizontal';
/** Defines the text displayed in the 'Next' button. */
this.nextText = 'Next';
/** Defines the text displayed in the 'Previous' button. */
this.previousText = 'Previous';
/** Defines the text displayed in the 'Cancel' button. */
this.cancelText = 'Cancel';
/** Defines the text displayed in the 'Finish' button. */
this.finishText = 'Finish';
/** Defines the text displayed in the tooltip when the 'Next' button is hovered. */
this.nextTooltip = 'Go to the next step';
/** Defines the text displayed in the tooltip when the 'Previous' button is hovered. */
this.previousTooltip = 'Go to the previous step';
/** Defines the text displayed in the tooltip when the 'Cancel' button is hovered. */
this.cancelTooltip = 'Cancel the wizard';
/** Defines the text displayed in the tooltip when the 'Finish' button is hovered. */
this.finishTooltip = 'Finish the wizard';
/** Defines the text for the aria label on the 'Next' button. */
this.nextAriaLabel = 'Go to the next step';
/** Defines the text for the aria label on the 'Previous' button. */
this.previousAriaLabel = 'Go to the previous step';
/** Defines the text for the aria label on the 'Cancel' button. */
this.cancelAriaLabel = 'Cancel the wizard';
/** Defines the text for the aria label on the 'Finish' button. */
this.finishAriaLabel = 'Finish the wizard';
/** If set to `true` the 'Next' button will appear disabled and will not respond to clicks. */
this.nextDisabled = false;
/** If set to `true` the 'Previous' button will appear disabled and will not respond to clicks. */
this.previousDisabled = false;
/** If set to `true` the 'Cancel' button will appear disabled and will not respond to clicks. */
this.cancelDisabled = false;
/** If set to `true` the 'Finish' button will appear disabled and will not respond to clicks. */
this.finishDisabled = false;
/** If set to `false` the 'Next' button will be hidden. */
this.nextVisible = true;
/** If set to `false` the 'Previous' button will be hidden. */
this.previousVisible = true;
/** If set to `false` the 'Cancel' button will be hidden. */
this.cancelVisible = true;
/** If set to false the 'Finish' button will be hidden. */
this.finishVisible = true;
/** If set to `true` the 'Cancel' button will be visible even on the last step. By default it will be hidden on the final step. */
this.cancelAlwaysVisible = false;
/** If set to `true` the 'Finish' button will be visible on all steps of the wizard. By default this button will only be visible on the final step of the wizard. */
this.finishAlwaysVisible = false;
/** If set to `true` the 'Next' or 'Finish' button will become disabled when the current step is invalid. */
this.disableNextWhenInvalid = false;
/** Whether to set `visited` to false on subsequent steps after a validation fault. */
this.resetVisitedOnValidationError = false;
/** If set to false it will allow users to navigate to every step */
this.sequential = true;
/** Emits when the wizard has moved to the next step. It will receive the current step index as a parameter. */
this.onNext = new EventEmitter();
/** Emits when the wizard has moved to the previous step. It will receive the current step index as a parameter. */
this.onPrevious = new EventEmitter();
/** Emits when the 'Cancel' button has been pressed. */
this.onCancel = new EventEmitter();
/** Emits when the 'Finish' button is clicked, but before the finish event fires. This fires regardless of the validity of the final step. */
this.onFinishing = new EventEmitter();
/** Emits when the 'Finish' button has been pressed and the final step is valid. */
this.onFinish = new EventEmitter();
/** Emits before the current step changes. The event contains the current step index in the `from` property, and the requested step index in the `to` property. */
this.stepChanging = new EventEmitter();
/** Emits when the current step has changed. */
this.stepChange = new EventEmitter();
/** Emits when the user tries to continue but the current step is invalid. */
this.stepError = new EventEmitter();
this.steps = new QueryList();
this.id = `ux-wizard-${uniqueId++}`;
this.invalidIndicator = false;
this._step = 0;
this._onDestroy = new Subject();
}
/**
* The current active step. When the step changes an event will be emitted containing the index of the newly active step.
* If this is not specified the wizard will start on the first step.
*/
get step() {
return this._step;
}
set step(value) {
// only accept numbers as valid options
if (typeof value === 'number') {
// store the active step
this._step = value;
// update which steps should be active
this.update();
// reset the invalid state
this.invalidIndicator = false;
}
}
ngOnInit() {
// initially set the correct visibility of the steps
setTimeout(this.update.bind(this));
// watch for changes to valid subject
this._wizardService.validChange$.pipe(filter((event) => !event.valid), takeUntil(this._onDestroy)).subscribe((event) => this.setFutureStepsUnvisited(event.step));
}
ngAfterContentInit() {
this.steps.changes.pipe(tick(), takeUntil(this._onDestroy)).subscribe(this.update.bind(this));
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
/**
* Navigate to the next step
*/
async next() {
this.stepChanging.next(new StepChangingEvent(this.step, this.step + 1));
const step = this.getCurrentStep();
// Disable the button while waiting on validation
this.nextDisabled = true;
try {
// Fetch validation status
const validationResult = this.isStepValid();
step.valid = validationResult instanceof Promise ? await validationResult : validationResult;
}
finally {
// Re-enable button
this.nextDisabled = false;
}
// check if current step is invalid
if (!step.valid) {
this.invalidIndicator = true;
this.stepError.next(this.step);
return;
}
// check if we are currently on the last step
if ((this.step + 1) < this.steps.length) {
this.step++;
this.stepChange.emit(this.step);
// emit the current step
this.onNext.next(this.step);
}
}
/**
* Whether the Next or Finish button should be disabled.
*/
isNextDisabled() {
const step = this.getCurrentStep();
// ensure the step is not null before we try to access its properties. It may be null if an ngFor is being
// used and the steps haven't rendered yet
if (!step) {
return false;
}
// Use the `disableNextWhenInvalid` setting to determine whether to disable the Next/Finish button
// based on validation.
// If not defined on the WizardStepComponent, use the value from WizardComponent.
return (step.disableNextWhenInvalid === undefined ? this.disableNextWhenInvalid : step.disableNextWhenInvalid) && !step.valid;
}
/**
* Navigate to the previous step
*/
previous() {
this.stepChanging.next(new StepChangingEvent(this.step, this.step - 1));
// check if we are currently on the last step
if (this.step > 0) {
this.step--;
this.stepChange.emit(this.step);
// emit the current step
this.onPrevious.next(this.step);
}
}
/**
* Perform actions when the finish button is clicked
*/
async finish() {
// fires when the finish button is clicked always
this.onFinishing.next();
// Disable the button while waiting on validation
this.finishDisabled = true;
try {
// Fetch validation status
const validationResult = this.isStepValid();
this.getCurrentStep().valid = validationResult instanceof Promise ? await validationResult : validationResult;
}
finally {
// Re-enable button
this.finishDisabled = false;
}
/**
* This is required because we need to ensure change detection has run
* to determine whether or not we have the latest value for the 'valid' input
* on the current step. Unfortunately we can't use ChangeDetectorRef as we are looking to run
* on content children, and we cant use ApplicationRef.tick() as this does not work in a hybrid app, eg. our docs
*/
return new Promise(resolve => {
setTimeout(() => {
// only fires when the finish button is clicked and the step is valid
if (this.getCurrentStep().valid) {
this.onFinish.emit();
}
else {
this.stepError.next(this.step);
}
resolve();
});
});
}
/**
* Perform actions when the cancel button is clicked
*/
cancel() {
this.onCancel.next();
}
/**
* Update the active state of each step
*/
update() {
// update which steps should be active
this.steps.forEach((step, idx) => step.active = idx === this.step);
}
/**
* Jump to a specific step only if the step has previously been visited
*/
gotoStep(step) {
if (step.visited || !this.sequential) {
const stepIndex = this.steps.toArray().findIndex(stp => stp === step);
this.stepChanging.next(new StepChangingEvent(this.step, stepIndex));
this.step = stepIndex;
this.stepChange.emit(this.step);
}
}
/**
* Determine if the current step is the last step
*/
isLastStep() {
return this.step === (this.steps.length - 1);
}
/**
* Reset the wizard - goes to first step and resets visited state
*/
reset() {
// mark all steps as not visited
this.steps.forEach(step => step.setVisitedAndEmitChangeEvent(false));
// go to the first step
this.step = 0;
this.stepChange.emit(this.step);
}
/**
* Get the step at the current index
*/
getCurrentStep() {
return this.getStepAtIndex(this.step);
}
/**
* Return a step at a specific index
*/
getStepAtIndex(index) {
return this.steps.toArray()[index];
}
/**
* If a step in the wizard becomes invalid, all steps sequentially after
* it should become unvisited
*/
setFutureStepsUnvisited(currentStep) {
if (!this.resetVisitedOnValidationError) {
return;
}
this.getFutureSteps(currentStep).forEach(step => {
step.setVisitedAndEmitChangeEvent(false);
});
}
/**
* Get the currently active step and all steps beyond it
*/
getFutureSteps(currentStep) {
const currentIndex = this.steps.toArray().indexOf(currentStep);
return this.steps.toArray().slice(currentIndex + 1);
}
/**
* Returns the valid status of the current step, including the `validation` function (if provided).
*/
isStepValid() {
// get the current active step
const currentStep = this.getCurrentStep();
// if there is no validator then return the valid state
if (!currentStep.validator) {
return currentStep.valid;
}
// get the validator result
return currentStep.validator();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: WizardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: WizardComponent, selector: "ux-wizard", inputs: { orientation: "orientation", nextText: "nextText", previousText: "previousText", cancelText: "cancelText", finishText: "finishText", nextTooltip: "nextTooltip", previousTooltip: "previousTooltip", cancelTooltip: "cancelTooltip", finishTooltip: "finishTooltip", nextAriaLabel: "nextAriaLabel", previousAriaLabel: "previousAriaLabel", cancelAriaLabel: "cancelAriaLabel", finishAriaLabel: "finishAriaLabel", nextDisabled: "nextDisabled", previousDisabled: "previousDisabled", cancelDisabled: "cancelDisabled", finishDisabled: "finishDisabled", nextVisible: "nextVisible", previousVisible: "previousVisible", cancelVisible: "cancelVisible", finishVisible: "finishVisible", cancelAlwaysVisible: "cancelAlwaysVisible", finishAlwaysVisible: "finishAlwaysVisible", disableNextWhenInvalid: "disableNextWhenInvalid", resetVisitedOnValidationError: "resetVisitedOnValidationError", sequential: "sequential", step: "step" }, outputs: { onNext: "onNext", onPrevious: "onPrevious", onCancel: "onCancel", onFinishing: "onFinishing", onFinish: "onFinish", stepChanging: "stepChanging", stepChange: "stepChange", stepError: "stepError" }, host: { properties: { "class": "orientation" } }, providers: [WizardService], queries: [{ propertyName: "footerTemplate", first: true, predicate: ["footerTemplate"], descendants: true }, { propertyName: "steps", predicate: WizardStepComponent }], ngImport: i0, template: "<div class=\"wizard-body\">\n\n <div class=\"wizard-steps\"\n uxTabbableList\n [direction]=\"orientation\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation\">\n\n @for (stp of steps; track stp; let index = $index) {\n <div\n role=\"tab\"\n class=\"wizard-step\"\n [class.active]=\"stp._active\"\n [class.visited]=\"stp.visited\"\n [class.invalid]=\"!stp._valid && stp.visited\"\n [attr.aria-posinset]=\"index + 1\"\n [attr.aria-setsize]=\"steps.length\"\n [attr.aria-selected]=\"stp._active\"\n [attr.aria-controls]=\"id + '-step-' + index\"\n [attr.aria-labelledby]=\"id + '-step-' + index + '-label'\"\n [attr.aria-expanded]=\"stp._active\"\n [id]=\"id + '-step-' + index + '-label'\"\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n uxTabbableListItem\n [disabled]=\"index !== 0 && (!stp.visited || !sequential)\"\n (click)=\"gotoStep(stp)\"\n (keydown.enter)=\"gotoStep(stp)\">\n <span class=\"wizard-step-text\">{{ stp.header }}</span>\n @if (stp.visited && !stp._active) {\n <ux-icon class=\"wizard-step-icon\" name=\"checkmark\"></ux-icon>\n }\n </div>\n }\n\n </div>\n\n <div class=\"wizard-content\">\n <ng-content></ng-content>\n </div>\n\n</div>\n\n<div class=\"wizard-footer\">\n\n @if (footerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"footerTemplate\"\n [ngTemplateOutletContext]=\"{ step: step }\">\n </ng-container>\n }\n\n @if (previousVisible) {\n <button #tip=\"ux-tooltip\"\n type=\"button\"\n class=\"btn button-secondary\"\n [uxTooltip]=\"previousTooltip\"\n [disabled]=\"previousDisabled || step === 0\"\n [attr.aria-label]=\"previousAriaLabel\"\n (click)=\"previous(); tip.hide()\">\n {{ previousText }}\n </button>\n }\n\n @if (nextVisible && !isLastStep()) {\n <button #tip=\"ux-tooltip\"\n type=\"button\"\n class=\"btn button-primary\"\n [uxTooltip]=\"nextTooltip\"\n [disabled]=\"nextDisabled || isNextDisabled()\"\n [attr.aria-label]=\"nextAriaLabel\"\n (click)=\"next(); tip.hide()\">\n {{ nextText }}\n </button>\n }\n\n @if (finishVisible && isLastStep() || finishAlwaysVisible) {\n <button #tip=\"ux-tooltip\"\n type=\"button\"\n class=\"btn button-primary\"\n [uxTooltip]=\"finishTooltip\"\n [disabled]=\"finishDisabled || isNextDisabled()\"\n [attr.aria-label]=\"finishAriaLabel\"\n (click)=\"finish(); tip.hide()\">\n {{ finishText }}\n </button>\n }\n\n @if (cancelVisible && !isLastStep() || cancelAlwaysVisible) {\n <button #tip=\"ux-tooltip\"\n type=\"button\"\n class=\"btn button-secondary\"\n [uxTooltip]=\"cancelTooltip\"\n [disabled]=\"cancelDisabled\"\n [attr.aria-label]=\"cancelAriaLabel\"\n (click)=\"cancel(); tip.hide()\">\n {{ cancelText }}\n </button>\n }\n\n</div>\n", dependencies: [{ kind: "directive", type: i1.DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "directive", type: i2.FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i3.TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: i4.TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i6.IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: i7.TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: WizardComponent, decorators: [{
type: Component,
args: [{ selector: 'ux-wizard', providers: [WizardService], host: {
'[class]': 'orientation'
}, template: "<div class=\"wizard-body\">\n\n <div class=\"wizard-steps\"\n uxTabbableList\n [direction]=\"orientation\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation\">\n\n @for (stp of steps; track stp; let index = $index) {\n <div\n role=\"tab\"\n class=\"wizard-step\"\n [class.active]=\"stp._active\"\n [class.visited]=\"stp.visited\"\n [class.invalid]=\"!stp._valid && stp.visited\"\n [attr.aria-posinset]=\"index + 1\"\n [attr.aria-setsize]=\"steps.length\"\n [attr.aria-selected]=\"stp._active\"\n [attr.aria-controls]=\"id + '-step-' + index\"\n [attr.aria-labelledby]=\"id + '-step-' + index + '-label'\"\n [attr.aria-expanded]=\"stp._active\"\n [id]=\"id + '-step-' + index + '-label'\"\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n uxTabbableListItem\n [disabled]=\"index !== 0 && (!stp.visited || !sequential)\"\n (click)=\"gotoStep(stp)\"\n (keydown.enter)=\"gotoStep(stp)\">\n <span class=\"wizard-step-text\">{{ stp.header }}</span>\n @if (stp.visited && !stp._active) {\n <ux-icon class=\"wizard-step-icon\" name=\"checkmark\"></ux-icon>\n }\n </div>\n }\n\n </div>\n\n <div class=\"wizard-content\">\n <ng-content></ng-content>\n </div>\n\n</div>\n\n<div class=\"wizard-footer\">\n\n @if (footerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"footerTemplate\"\n [ngTemplateOutletContext]=\"{ step: step }\">\n </ng-container>\n }\n\n @if (previousVisible) {\n <button #tip=\"ux-tooltip\"\n type=\"button\"\n class=\"btn button-secondary\"\n [uxTooltip]=\"previousTooltip\"\n [disabled]=\"previousDisabled || step === 0\"\n [attr.aria-label]=\"previousAriaLabel\"\n (click)=\"previous(); tip.hide()\">\n {{ previousText }}\n </button>\n }\n\n @if (nextVisible && !isLastStep()) {\n <button #tip=\"ux-tooltip\"\n type=\"button\"\n class=\"btn button-primary\"\n [uxTooltip]=\"nextTooltip\"\n [disabled]=\"nextDisabled || isNextDisabled()\"\n [attr.aria-label]=\"nextAriaLabel\"\n (click)=\"next(); tip.hide()\">\n {{ nextText }}\n </button>\n }\n\n @if (finishVisible && isLastStep() || finishAlwaysVisible) {\n <button #tip=\"ux-tooltip\"\n type=\"button\"\n class=\"btn button-primary\"\n [uxTooltip]=\"finishTooltip\"\n [disabled]=\"finishDisabled || isNextDisabled()\"\n [attr.aria-label]=\"finishAriaLabel\"\n (click)=\"finish(); tip.hide()\">\n {{ finishText }}\n </button>\n }\n\n @if (cancelVisible && !isLastStep() || cancelAlwaysVisible) {\n <button #tip=\"ux-tooltip\"\n type=\"button\"\n class=\"btn button-secondary\"\n [uxTooltip]=\"cancelTooltip\"\n [disabled]=\"cancelDisabled\"\n [attr.aria-label]=\"cancelAriaLabel\"\n (click)=\"cancel(); tip.hide()\">\n {{ cancelText }}\n </button>\n }\n\n</div>\n" }]
}], propDecorators: { orientation: [{
type: Input
}], nextText: [{
type: Input
}], previousText: [{
type: Input
}], cancelText: [{
type: Input
}], finishText: [{
type: Input
}], nextTooltip: [{
type: Input
}], previousTooltip: [{
type: Input
}], cancelTooltip: [{
type: Input
}], finishTooltip: [{
type: Input
}], nextAriaLabel: [{
type: Input
}], previousAriaLabel: [{
type: Input
}], cancelAriaLabel: [{
type: Input
}], finishAriaLabel: [{
type: Input
}], nextDisabled: [{
type: Input
}], previousDisabled: [{
type: Input
}], cancelDisabled: [{
type: Input
}], finishDisabled: [{
type: Input
}], nextVisible: [{
type: Input
}], previousVisible: [{
type: Input
}], cancelVisible: [{
type: Input
}], finishVisible: [{
type: Input
}], cancelAlwaysVisible: [{
type: Input
}], finishAlwaysVisible: [{
type: Input
}], disableNextWhenInvalid: [{
type: Input
}], resetVisitedOnValidationError: [{
type: Input
}], sequential: [{
type: Input
}], onNext: [{
type: Output
}], onPrevious: [{
type: Output
}], onCancel: [{
type: Output
}], onFinishing: [{
type: Output
}], onFinish: [{
type: Output
}], stepChanging: [{
type: Output
}], stepChange: [{
type: Output
}], stepError: [{
type: Output
}], steps: [{
type: ContentChildren,
args: [WizardStepComponent]
}], footerTemplate: [{
type: ContentChild,
args: ['footerTemplate', { static: false }]
}], step: [{
type: Input
}] } });
export class StepChangingEvent {
constructor(from, to) {
this.from = from;
this.to = to;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l6YXJkLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3dpemFyZC93aXphcmQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvd2l6YXJkL3dpemFyZC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQW9CLFNBQVMsRUFBRSxZQUFZLEVBQUUsZUFBZSxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFxQixNQUFNLEVBQUUsU0FBUyxFQUFlLE1BQU0sZUFBZSxDQUFDO0FBQzNLLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDL0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNuRCxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDMUMsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxFQUFFLGFBQWEsRUFBb0IsTUFBTSxrQkFBa0IsQ0FBQzs7Ozs7Ozs7O0FBRW5FLElBQUksUUFBUSxHQUFXLENBQUMsQ0FBQztBQVV6QixNQUFNLE9BQU8sZUFBZTtJQVI1QjtRQVN1QixtQkFBYyxHQUFHLE1BQU0sQ0FBcUMsYUFBYSxDQUFDLENBQUM7UUFFOUYsb0dBQW9HO1FBQzNGLGdCQUFXLEdBQThCLFlBQVksQ0FBQztRQUUvRCx1REFBdUQ7UUFDOUMsYUFBUSxHQUFXLE1BQU0sQ0FBQztRQUVuQywyREFBMkQ7UUFDbEQsaUJBQVksR0FBVyxVQUFVLENBQUM7UUFFM0MseURBQXlEO1FBQ2hELGVBQVUsR0FBVyxRQUFRLENBQUM7UUFFdkMseURBQXlEO1FBQ2hELGVBQVUsR0FBVyxRQUFRLENBQUM7UUFFdkMsbUZBQW1GO1FBQzFFLGdCQUFXLEdBQVcscUJBQXFCLENBQUM7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YztzQkFBdEIsS0FBSztnQkFHRyxXQUFXO3NCQUFuQixLQUFLO2dCQUdHLGVBQWU7c0JBQXZCLEtBQUs7Z0JBR0csYUFBYTtzQkFBckIsS0FBSztnQkFHRyxhQUFhO3NCQUFyQixLQUFLO2dCQUdHLG1CQUFtQjtzQkFBM0IsS0FBSztnQkFHRyxtQkFBbUI7c0JBQTNCLEtBQUs7Z0JBR0csc0JBQXNCO3NCQUE5QixLQUFLO2dCQUdHLDZCQUE2QjtzQkFBckMsS0FBSztnQkFHRyxVQUFVO3NCQUFsQixLQUFLO2dCQUdJLE1BQU07c0JBQWYsTUFBTTtnQkFHRyxVQUFVO3NCQUFuQixNQUFNO2dCQUdHLFFBQVE7c0JBQWpCLE1BQU07Z0JBR0csV0FBVztzQkFBcEIsTUFBTTtnQkFHRyxRQUFRO3NCQUFqQixNQUFNO2dCQUdHLFlBQVk7c0JBQXJCLE1BQU07Z0JBR0csVUFBVTtzQkFBbkIsTUFBTTtnQkFHRyxTQUFTO3NCQUFsQixNQUFNO2dCQUUrQixLQUFLO3NCQUExQyxlQUFlO3VCQUFDLG1CQUFtQjtnQkFFZSxjQUFjO3NCQUFoRSxZQUFZO3VCQUFDLGdCQUFnQixFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRTtnQkFVN0MsSUFBSTtzQkFEUCxLQUFLOztBQXNRVixNQUFNLE9BQU8saUJBQWlCO0lBQzFCLFlBQW1CLElBQVksRUFBUyxFQUFVO1FBQS9CLFNBQUksR0FBSixJQUFJLENBQVE7UUFBUyxPQUFFLEdBQUYsRUFBRSxDQUFRO0lBQ2xELENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEFmdGVyQ29udGVudEluaXQsIENvbXBvbmVudCwgQ29udGVudENoaWxkLCBDb250ZW50Q2hpbGRyZW4sIEV2ZW50RW1pdHRlciwgaW5qZWN0LCBJbnB1dCwgT25EZXN0cm95LCBPbkluaXQsIE91dHB1dCwgUXVlcnlMaXN0LCBUZW1wbGF0ZVJlZiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZmlsdGVyLCB0YWtlVW50aWwgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyB0aWNrIH0gZnJvbSAnLi4vLi4vY29tbW9uL2luZGV4JztcbmltcG9ydCB7IFdpemFyZFN0ZXBDb21wb25lbnQgfSBmcm9tICcuL3dpemFyZC1zdGVwLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBXaXphcmRTZXJ2aWNlLCBXaXphcmRWYWxpZEV2ZW50IH0gZnJvbSAnLi93aXphcmQuc2VydmljZSc7XG5cbmxldCB1bmlxdWVJZDogbnVtYmVyID0gMDtcblxuQENvbXBvbmVudCh7XG4gICAgc2VsZWN0b3I6ICd1eC13aXphcmQnLFxuICAgIHRlbXBsYXRlVXJsOiAnLi93aXphcmQuY29tcG9uZW50Lmh0bWwnLFxuICAgIHByb3ZpZGVyczogW1dpemFyZFNlcnZpY2VdLFxuICAgIGhvc3Q6IHtcbiAgICAgICAgJ1tjbGFzc10nOiAnb3JpZW50YXRpb24nXG4gICAgfVxufSlcbmV4cG9ydCBjbGFzcyBXaXphcmRDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIEFmdGVyQ29udGVudEluaXQsIE9uRGVzdHJveSB7XG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IF93aXphcmRTZXJ2aWNlID0gaW5qZWN0PFdpemFyZFNlcnZpY2U8V2l6YXJkU3RlcENvbXBvbmVudD4+KFdpemFyZFNlcnZpY2UpO1xuXG4gICAgLyoqIERlZmluZXMgd2hldGhlciBvciBub3QgdGhlIHdpemFyZCBzaG91bGQgYmUgZGlzcGxheWVkIGluIGEgYGhvcml6b250YWxgIG9yIGB2ZXJ0aWNhbGAgbGF5b3V0LiAqL1xuICAgIEBJbnB1dCgpIG9yaWVudGF0aW9uOiAnaG9yaXpvbnRhbCcgfCAndmVydGljYWwnID0gJ2hvcml6b250YWwnO1xuXG4gICAgLyoqIERlZmluZXMgdGhlIHRleHQgZGlzcGxheWVkIGluIHRoZSAnTmV4dCcgYnV0dG9uLiAqL1xuICAgIEBJbnB1dCgpIG5leHRUZXh0OiBzdHJpbmcgPSAnTmV4dCc7XG5cbiAgICAvKiogRGVmaW5lcyB0aGUgdGV4dCBkaXNwbGF5ZWQgaW4gdGhlICdQcmV2aW91cycgYnV0dG9uLiAqL1xuICAgIEBJbnB1dCgpIHByZXZpb3VzVGV4dDogc3RyaW5nID0gJ1ByZXZpb3VzJztcblxuICAgIC8qKiBEZWZpbmVzIHRoZSB0ZXh0IGRpc3BsYXllZCBpbiB0aGUgJ0NhbmNlbCcgYnV0dG9uLiAqL1xuICAgIEBJbnB1dCgpIGNhbmNlbFRleHQ6IHN0cmluZyA9ICdDYW5jZWwnO1xuXG4gICAgLyoqIERlZmluZXMgdGhlIHRleHQgZGlzcGxheWVkIGluIHRoZSAnRmluaXNoJyBidXR0b24uICovXG4gICAgQElucHV0KCkgZmluaXNoVGV4dDogc3RyaW5nID0gJ0ZpbmlzaCc7XG5cbiAgICAvKiogRGVmaW5lcyB0aGUgdGV4dCBkaXNwbGF5ZWQgaW4gdGhlIHRvb2x0aXAgd2hlbiB0aGUgJ05leHQnIGJ1dHRvbiBpcyBob3ZlcmVkLiAqL1xuICAgIEBJbnB1dCgpIG5leHRUb29sdGlwOiBzdHJpbmcgPSAnR28gdG8gdGhlIG5leHQgc3RlcCc7XG5cbiAgICAvKiogRGVmaW5lcyB0aGUgdGV4dCBkaXNwbGF5ZWQgaW4gdGhlIHRvb2x0aXAgd2hlbiB0aGUgJ1ByZXZpb3VzJyBidXR0b24gaXMgaG92ZXJlZC4gKi9cbiAgICBASW5wdXQoKSBwcmV2aW91c1Rvb2x0aXA6IHN0cmluZyA9ICdHbyB0byB0aGUgcHJldmlvdXMgc3RlcCc7XG5cbiAgICAvKiogRGVmaW5lcyB0aGUgdGV4dCBkaXNwbGF5ZWQgaW4gdGhlIHRvb2x0aXAgd2hlbiB0aGUgJ0NhbmNlbCcgYnV0dG9uIGlzIGhvdmVyZWQuICovXG4gICAgQElucHV0KCkgY2FuY2VsVG9vbHRpcDogc3RyaW5nID0gJ0NhbmNlbCB0aGUgd2l6YXJkJztcblxuICAgIC8qKiBEZWZpbmVzIHRoZSB0ZXh0IGRpc3BsYXllZCBpbiB0aGUgdG9vbHRpcCB3aGVuIHRoZSAnRmluaXNoJyBidXR0b24gaXMgaG92ZXJlZC4gKi9cbiAgICBASW5wdXQoKSBmaW5pc2hUb29sdGlwOiBzdHJpbmcgPSAnRmluaXNoIHRoZSB3aXphcmQnO1xuXG4gICAgLyoqIERlZmluZXMgdGhlIHRleHQgZm9yIHRoZSBhcmlhIGxhYmVsIG9uIHRoZSAnTmV4dCcgYnV0dG9uLiAqL1xuICAgIEBJbnB1dCgpIG5leHRBcmlhTGFiZWw6IHN0cmluZyA9ICdHbyB0byB0aGUgbmV4dCBzdGVwJztcblxuICAgIC8qKiBEZWZpbmVzIHRoZSB0ZXh0IGZvciB0aGUgYXJpYSBsYWJlbCBvbiB0aGUgJ1ByZXZpb3VzJyBidXR0b24uICovXG4gICAgQElucHV0KCkgcHJldmlvdXNBcmlhTGFiZWw6IHN0cmluZyA9ICdHbyB0byB0aGUgcHJldmlvdXMgc3RlcCc7XG5cbiAgICAvKiogRGVmaW5lcyB0aGUgdGV4dCBmb3IgdGhlIGFyaWEgbGFiZWwgb24gdGhlICdDYW5jZWwnIGJ1dHRvbi4gKi9cbiAgICBASW5wdXQoKSBjYW5jZWxBcmlhTGFiZWw6IHN0cmluZyA9ICdDYW5jZWwgdGhlIHdpemFyZCc7XG5cbiAgICAvKiogRGVmaW5lcyB0aGUgdGV4dCBmb3IgdGhlIGFyaWEgbGFiZWwgb24gdGhlICdGaW5pc2gnIGJ1dHRvbi4gKi9cbiAgICBASW5wdXQoKSBmaW5pc2hBcmlhTGFiZWw6IHN0cmluZyA9ICdGaW5pc2ggdGhlIHdpemFyZCc7XG5cbiAgICAvKiogSWYgc2V0IHRvIGB0cnVlYCB0aGUgJ05leHQnIGJ1dHRvbiB3aWxsIGFwcGVhciBkaXNhYmxlZCBhbmQgd2lsbCBub3QgcmVzcG9uZCB0byBjbGlja3MuICovXG4gICAgQElucHV0KCkgbmV4dERpc2FibGVkOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKiogSWYgc2V0IHRvIGB0cnVlYCB0aGUgJ1ByZXZpb3VzJyBidXR0b24gd2lsbCBhcHBlYXIgZGlzYWJsZWQgYW5kIHdpbGwgbm90IHJlc3BvbmQgdG8gY2xpY2tzLiAqL1xuICAgIEBJbnB1dCgpIHByZXZpb3VzRGlzYWJsZWQ6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKiBJZiBzZXQgdG8gYHRydWVgIHRoZSAnQ2FuY2VsJyBidXR0b24gd2lsbCBhcHBlYXIgZGlzYWJsZWQgYW5kIHdpbGwgbm90IHJlc3BvbmQgdG8gY2xpY2tzLiAqL1xuICAgIEBJbnB1dCgpIGNhbmNlbERpc2FibGVkOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKiogSWYgc2V0IHRvIGB0cnVlYCB0aGUgJ0ZpbmlzaCcgYnV0dG9uIHdpbGwgYXBwZWFyIGRpc2FibGVkIGFuZCB3aWxsIG5vdCByZXNwb25kIHRvIGNsaWNrcy4gKi9cbiAgICBASW5wdXQoKSBmaW5pc2hEaXNhYmxlZDogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgLyoqIElmIHNldCB0byBgZmFsc2VgIHRoZSAnTmV4dCcgYnV0dG9uIHdpbGwgYmUgaGlkZGVuLiAqL1xuICAgIEBJbnB1dCgpIG5leHRWaXNpYmxlOiBib29sZWFuID0gdHJ1ZTtcblxuICAgIC8qKiBJZiBzZXQgdG8gYGZhbHNlYCB0aGUgJ1ByZXZpb3VzJyBidXR0b24gd2lsbCBiZSBoaWRkZW4uICovXG4gICAgQElucHV0KCkgcHJldmlvdXNWaXNpYmxlOiBib29sZWFuID0gdHJ1ZTtcblxuICAgIC8qKiBJZiBzZXQgdG8gYGZhbHNlYCB0aGUgJ0NhbmNlbCcgYnV0dG9uIHdpbGwgYmUgaGlkZGVuLiAqL1xuICAgIEBJbnB1dCgpIGNhbmNlbFZpc2libGU6IGJvb2xlYW4gPSB0cnVlO1xuXG4gICAgLyoqIElmIHNldCB0byBmYWxzZSB0aGUgJ0ZpbmlzaCcgYnV0dG9uIHdpbGwgYmUgaGlkZGVuLiAqL1xuICAgIEBJbnB1dCgpIGZpbmlzaFZpc2libGU6IGJvb2xlYW4gPSB0cnVlO1xuXG4gICAgLyoqIElmIHNldCB0byBgdHJ1ZWAgdGhlICdDYW5jZWwnIGJ1dHRvbiB3aWxsIGJlIHZpc2libGUgZXZlbiBvbiB0aGUgbGFzdCBzdGVwLiBCeSBkZWZhdWx0IGl0IHdpbGwgYmUgaGlkZGVuIG9uIHRoZSBmaW5hbCBzdGVwLiAqL1xuICAgIEBJbnB1dCgpIGNhbmNlbEFsd2F5c1Zpc2libGU6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKiBJZiBzZXQgdG8gYHRydWVgIHRoZSAnRmluaXNoJyBidXR0b24gd2lsbCBiZSB2aXNpYmxlIG9uIGFsbCBzdGVwcyBvZiB0aGUgd2l6YXJkLiBCeSBkZWZhdWx0IHRoaXMgYnV0dG9uIHdpbGwgb25seSBiZSB2aXNpYmxlIG9uIHRoZSBmaW5hbCBzdGVwIG9mIHRoZSB3aXphcmQuICovXG4gICAgQElucHV0KCkgZmluaXNoQWx3YXlzVmlzaWJsZTogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgLyoqIElmIHNldCB0byBgdHJ1ZWAgdGhlICdOZXh0JyBvciAnRmluaXNoJyBidXR0b24gd2lsbCBiZWNvbWUgZGlzYWJsZWQgd2hlbiB0aGUgY3VycmVudCBzdGVwIGlzIGludmFsaWQuICovXG4gICAgQElucHV0KCkgZGlzYWJsZU5leHRXaGVuSW52YWxpZDogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgLyoqIFdoZXRoZXIgdG8gc2V0IGB2aXNpdGVkYCB0byBmYWxzZSBvbiBzdWJzZXF1ZW50IHN0ZXBzIGFmdGVyIGEgdmFsaWRhdGlvbiBmYXVsdC4gKi9cbiAgICBASW5wdXQoKSByZXNldFZpc2l0ZWRPblZhbGlkYXRpb25FcnJvcjogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgLyoqIElmIHNldCB0byBmYWxzZSBpdCB3aWxsIGFsbG93IHVzZXJzIHRvIG5hdmlnYXRlIHRvIGV2ZXJ5IHN0ZXAgKi9cbiAgICBASW5wdXQoKSBzZXF1ZW50aWFsOiBib29sZWFuID0gdHJ1ZTtcblxuICAgIC8qKiBFbWl0cyB3aGVuIHRoZSB3aXphcmQgaGFzIG1vdmVkIHRvIHRoZSBuZXh0IHN0ZXAuIEl0IHdpbGwgcmVjZWl2ZSB0aGUgY3VycmVudCBzdGVwIGluZGV4IGFzIGEgcGFyYW1ldGVyLiAqL1xuICAgIEBPdXRwdXQoKSBvbk5leHQgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlcj4oKTtcblxuICAgIC8qKiBFbWl0cyB3aGVuIHRoZSB3aXphcmQgaGFzIG1vdmVkIHRvIHRoZSBwcmV2aW91cyBzdGVwLiBJdCB3aWxsIHJlY2VpdmUgdGhlIGN1cnJlbnQgc3RlcCBpbmRleCBhcyBhIHBhcmFtZXRlci4gKi9cbiAgICBAT3V0cHV0KCkgb25QcmV2aW91cyA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyPigpO1xuXG4gICAgLyoqIEVtaXRzIHdoZW4gdGhlICdDYW5jZWwnIGJ1dHRvbiBoYXMgYmVlbiBwcmVzc2VkLiAqL1xuICAgIEBPdXRwdXQoKSBvbkNhbmNlbCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcblxuICAgIC8qKiBFbWl0cyB3aGVuIHRoZSAnRmluaXNoJyBidXR0b24gaXMgY2xpY2tlZCwgYnV0IGJlZm9yZSB0aGUgZmluaXNoIGV2ZW50IGZpcmVzLiBUaGlzIGZpcmVzIHJlZ2FyZGxlc3Mgb2YgdGhlIHZhbGlkaXR5IG9mIHRoZSBmaW5hbCBzdGVwLiAqL1xuICAgIEBPdXRwdXQoKSBvbkZpbmlzaGluZyA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcblxuICAgIC8qKiBFbWl0cyB3aGVuIHRoZSAnRmluaXNoJyBidXR0b24gaGFzIGJlZW4gcHJlc3NlZCBhbmQgdGhlIGZpbmFsIHN0ZXAgaXMgdmFsaWQuICovXG4gICAgQE91dHB1dCgpIG9uRmluaXNoID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuXG4gICAgLyoqIEVtaXRzIGJlZm9yZSB0aGUgY3VycmVudCBzdGVwIGNoYW5nZXMuIFRoZSBldmVudCBjb250YWlucyB0aGUgY3VycmVudCBzdGVwIGluZGV4IGluIHRoZSBgZnJvbWAgcHJvcGVydHksIGFuZCB0aGUgcmVxdWVzdGVkIHN0ZXAgaW5kZXggaW4gdGhlIGB0b2AgcHJvcGVydHkuICovXG4gICAgQE91dHB1dCgpIHN0ZXBDaGFuZ2luZyA9IG5ldyBFdmVudEVtaXR0ZXI8U3RlcENoYW5naW5nRXZlbnQ+KCk7XG5cbiAgICAvKiogRW1pdHMgd2hlbiB0aGUgY3VycmVudCBzdGVwIGhhcyBjaGFuZ2VkLiAqL1xuICAgIEBPdXRwdXQoKSBzdGVwQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG5cbiAgICAvKiogRW1pdHMgd2hlbiB0aGUgdXNlciB0cmllcyB0byBjb250aW51ZSBidXQgdGhlIGN1cnJlbnQgc3RlcCBpcyBpbnZhbGlkLiAqL1xuICAgIEBPdXRwdXQoKSBzdGVwRXJyb3IgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlcj4oKTtcblxuICAgIEBDb250ZW50Q2hpbGRyZW4oV2l6YXJkU3RlcENvbXBvbmVudCkgc3RlcHMgPSBuZXcgUXVlcnlMaXN0PFdpemFyZFN0ZXBDb21wb25lbnQ+KCk7XG5cbiAgICBAQ29udGVudENoaWxkKCdmb290ZXJUZW1wbGF0ZScsIHsgc3RhdGljOiBmYWxzZSB9KSBmb290ZXJUZW1wbGF0ZTogVGVtcGxhdGVSZWY8V2l6YXJkRm9vdGVyQ29udGV4dD47XG5cbiAgICBpZDogc3RyaW5nID0gYHV4LXdpemFyZC0keyB1bmlxdWVJZCsrIH1gO1xuICAgIGludmFsaWRJbmRpY2F0b3I6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50IGFjdGl2ZSBzdGVwLiBXaGVuIHRoZSBzdGVwIGNoYW5nZXMgYW4gZXZlbnQgd2lsbCBiZSBlbWl0dGVkIGNvbnRhaW5pbmcgdGhlIGluZGV4IG9mIHRoZSBuZXdseSBhY3RpdmUgc3RlcC5cbiAgICAgKiBJZiB0aGlzIGlzIG5vdCBzcGVjaWZpZWQgdGhlIHdpemFyZCB3aWxsIHN0YXJ0IG9uIHRoZSBmaXJzdCBzdGVwLlxuICAgICAqL1xuICAgIEBJbnB1dCgpXG4gICAgZ2V0IHN0ZXAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9zdGVwO1xuICAgIH1cblxuICAgIHNldCBzdGVwKHZhbHVlOiBudW1iZXIpIHtcblxuICAgICAgICAvLyBvbmx5IGFjY2VwdCBudW1iZXJzIGFzIHZhbGlkIG9wdGlvbnNcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicpIHtcblxuICAgICAgICAgICAgLy8gc3RvcmUgdGhlIGFjdGl2ZSBzdGVwXG4gICAgICAgICAgICB0aGlzLl9zdGVwID0gdmFsdWU7XG5cbiAgICAgICAgICAgIC8vIHVwZGF0ZSB3aGljaCBzdGVwcyBzaG91bGQgYmUgYWN0aXZlXG4gICAgICAgICAgICB0aGlzLnVwZGF0ZSgpO1xuXG4gICAgICAgICAgICAvLyByZXNldCB0aGUgaW52YWxpZCBzdGF0ZVxuICAgICAgICAgICAgdGhpcy5pbnZhbGlkSW5kaWNhdG9yID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIF9zdGVwOiBudW1iZXIgPSAwO1xuICAgIHByb3RlY3RlZCByZWFkb25seSBfb25EZXN0cm95ID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICAgIG5nT25Jbml0KCk6IHZvaWQge1xuICAgICAgICAvLyBpbml0aWFsbHkgc2V0IHRoZSBjb3JyZWN0IHZpc2liaWxpdHkgb2YgdGhlIHN0ZXBzXG4gICAgICAgIHNldFRpbWVvdXQodGhpcy51cGRhdGUuYmluZCh0aGlzKSk7XG5cbiAgICAgICAgLy8gd2F0Y2ggZm9yIGNoYW5nZXMgdG8gdmFsaWQgc3ViamVjdFxuICAgICAgICB0aGlzLl93aXphcmRTZXJ2aWNlLnZhbGlkQ2hhbmdlJC5waXBlKFxuICAgICAgICAgICAgZmlsdGVyKChldmVudDogV2l6YXJkVmFsaWRFdmVudDxXaXphcmRTdGVwQ29tcG9uZW50PikgPT4gIWV2ZW50LnZhbGlkKSxcbiAgICAgICAgICAgIHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpXG4gICAgICAgICkuc3Vic2NyaWJlKChldmVudDogV2l6YXJkVmFsaWRFdmVudDxXaXphcmRTdGVwQ29tcG9uZW50PikgPT4gdGhpcy5zZXRGdXR1cmVTdGVwc1VudmlzaXRlZChldmVudC5zdGVwKSk7XG4gICAgfVxuXG4gICAgbmdBZnRlckNvbnRlbnRJbml0KCk6IHZvaWQge1xuICAgICAgICB0aGlzLnN0ZXBzLmNoYW5nZXMucGlwZSh0aWNrKCksIHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKS5zdWJzY3JpYmUodGhpcy51cGRhdGUuYmluZCh0aGlzKSk7XG4gICAgfVxuXG4gICAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuX29uRGVzdHJveS5uZXh0KCk7XG4gICAgICAgIHRoaXMuX29uRGVzdHJveS5jb21wbGV0ZSgpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE5hdmlnYXRlIHRvIHRoZSBuZXh0IHN0ZXBcbiAgICAgKi9cbiAgICBhc3luYyBuZXh0KCk6IFByb21pc2U8dm9pZD4ge1xuXG4gICAgICAgIHRoaXMuc3RlcENoYW5naW5nLm5leHQobmV3IFN0ZXBDaGFuZ2luZ0V2ZW50KHRoaXMuc3RlcCwgdGhpcy5zdGVwICsgMSkpO1xuXG4gICAgICAgIGNvbnN0IHN0ZXAgPSB0aGlzLmdldEN1cnJlbnRTdGVwKCk7XG5cbiAgICAgICAgLy8gRGlzYWJsZSB0aGUgYnV0dG9uIHdoaWxlIHdhaXRpbmcgb24gdmFsaWRhdGlvblxuICAgICAgICB0aGlzLm5leHREaXNhYmxlZCA9IHRydWU7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIEZldGNoIHZhbGlkYXRpb24gc3RhdHVzXG4gICAgICAgICAgICBjb25zdCB2YWxpZGF0aW9uUmVzdWx0ID0gdGhpcy5pc1N0ZXBWYWxpZCgpO1xuICAgICAgICAgICAgc3RlcC52YWxpZCA9IHZhbGlkYXRpb25SZXN1bHQgaW5zdGFuY2VvZiBQcm9taXNlID8gYXdhaXQgdmFsaWRhdGlvblJlc3VsdCA6IHZhbGlkYXRpb25SZXN1bHQ7XG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgICAvLyBSZS1lbmFibGUgYnV0dG9uXG4gICAgICAgICAgICB0aGlzLm5leHREaXNhYmxlZCA9IGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gY2hlY2sgaWYgY3VycmVudCBzdGVwIGlzIGludmFsaWRcbiAgICAgICAgaWYgKCFzdGVwLnZhbGlkKSB7XG4gICAgICAgICAgICB0aGlzLmludmFsaWRJbmRpY2F0b3IgPSB0cnVlO1xuICAgICAgICAgICAgdGhpcy5zdGVwRXJyb3IubmV4dCh0aGlzLnN0ZXApO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gY2hlY2sgaWYgd2UgYXJlIGN1cnJlbnRseSBvbiB0aGUgbGFzdCBzdGVwXG4gICAgICAgIGlmICgodGhpcy5zdGVwICsgMSkgPCB0aGlzLnN0ZXBzLmxlbmd0aCkge1xuICAgICAgICAgICAgdGhpcy5zdGVwKys7XG4gICAgICAgICAgICB0aGlzLnN0ZXBDaGFuZ2UuZW1pdCh0aGlzLnN0ZXApO1xuXG4gICAgICAgICAgICAvLyBlbWl0IHRoZSBjdXJyZW50IHN0ZXBcbiAgICAgICAgICAgIHRoaXMub25OZXh0Lm5leHQodGhpcy5zdGVwKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFdoZXRoZXIgdGhlIE5leHQgb3IgRmluaXNoIGJ1dHRvbiBzaG91bGQgYmUgZGlzYWJsZWQuXG4gICAgICovXG4gICAgaXNOZXh0RGlzYWJsZWQoKTogYm9vbGVhbiB7XG4gICAgICAgIGNvbnN0IHN0ZXAgPSB0aGlzLmdldEN1cnJlbnRTdGVwKCk7XG5cbiAgICAgICAgLy8gZW5zdXJlIHRoZSBzdGVwIGlzIG5vdCBudWxsIGJlZm9yZSB3ZSB0cnkgdG8gYWNjZXNzIGl0cyBwcm9wZXJ0aWVzLiBJdCBtYXkgYmUgbnVsbCBpZiBhbiBuZ0ZvciBpcyBiZWluZ1xuICAgICAgICAvLyB1c2VkIGFuZCB0aGUgc3RlcHMgaGF2ZW4ndCByZW5kZXJlZCB5ZXRcbiAgICAgICAgaWYgKCFzdGVwKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBVc2UgdGhlIGBkaXNhYmxlTmV4dFdoZW5JbnZhbGlkYCBzZXR0aW5nIHRvIGRldGVybWluZSB3aGV0aGVyIHRvIGRpc2FibGUgdGhlIE5leHQvRmluaXNoIGJ1dHRvblxuICAgICAgICAvLyBiYXNlZCBvbiB2YWxpZGF0aW9uLlxuICAgICAgICAvLyBJZiBub3QgZGVmaW5lZCBvbiB0aGUgV2l6YXJkU3RlcENvbXBvbmVudCwgdXNlIHRoZSB2YWx1ZSBmcm9tIFdpemFyZENvbXBvbmVudC5cbiAgICAgICAgcmV0dXJuIChzdGVwLmRpc2FibGVOZXh0V2hlbkludmFsaWQgPT09IHVuZGVmaW5lZCA/I