UNPKG

@ux-aspects/ux-aspects

Version:

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

139 lines 39.4 kB
import { Component, ContentChildren, ElementRef, EventEmitter, inject, Input, Output, QueryList, TemplateRef, ViewChild } from '@angular/core'; import { takeUntil } from 'rxjs/operators'; import { TabbableListDirective } from '../../directives/accessibility'; import { ResizeService } from '../../directives/resize/index'; import { WizardComponent, WizardService } from '../wizard/index'; import { MarqueeWizardStepComponent } from './marquee-wizard-step.component'; 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/splitter/splitter-accessibility.directive"; import * as i4 from "../../directives/accessibility/tabbable-list/tabbable-list.directive"; import * as i5 from "../../directives/accessibility/tabbable-list/tabbable-list-item.directive"; import * as i6 from "@angular/common"; import * as i7 from "../icon/icon.component"; import * as i8 from "../tooltip/tooltip.directive"; import * as i9 from "angular-split"; export class MarqueeWizardComponent extends WizardComponent { get isTemplate() { return this.description && this.description instanceof TemplateRef; } constructor() { super(); this.wizardService = inject(WizardService); this._resizeService = inject(ResizeService); this._elementRef = inject(ElementRef); /** Initial set to default width to match 240px on left but can be changed with a percentage value */ this.sidePanelWidth = 25; /** Width of the splitter - default is 10 */ this.gutterSize = 10; /** If set to true the resizable splitter will be enabled and set to the default width **/ this.resizable = false; /** Emit the current width of the splitter*/ this.sidePanelWidthChange = new EventEmitter(); /** Access each step content component */ this.steps = new QueryList(); /** * If the wizard is in a modal it may initially have a size of 0 until the modal displays * in which case if we are using the splitter it will not render correctly. We use this * variable to only initialise the splitter when the content has a width. */ this._isInitialised = false; // set to true as default for Marquee Wizard only this.resetVisitedOnValidationError = true; // watch for changes to the size this._resizeService.addResizeListener(this._elementRef.nativeElement) .pipe(takeUntil(this._onDestroy)) .subscribe(this.onResize.bind(this)); } ngAfterViewChecked() { this.tabbableList?.setFirstItemTabbable(); } ngOnDestroy() { super.ngOnDestroy(); this._resizeService.removeResizeListener(this._elementRef.nativeElement); } /** * If the current step is valid, mark it as * complete and go to the next step */ async next() { // get the current step const step = this.getCurrentStep(); await super.next(); if (step && step.valid) { // mark this step as completed step.setCompleted(true); } else { this.stepError.next(this.step); } } /** * Emit the onFinishing event and if valid the onFinish event. * Also mark the final step as completed if it is valid */ async finish() { // get the current step const step = this.getCurrentStep(); await super.finish(); // if the step is valid indicate that it is now complete if (step.valid) { step.setCompleted(true); } else { this.stepError.next(this.step); } } onResize(event) { if (event.width !== 0 && event.height !== 0) { this._isInitialised = true; } } /** Whenever the drag event ends, update the internal value and emit the new size */ onDragEnd({ sizes }) { // we need to only get the size of the first panel which will be the side panel this.sidePanelWidth = sizes[0]; this.sidePanelWidthChange.emit(this.sidePanelWidth); } gotoStep(step) { const currentStep = this.getCurrentStep(); if (currentStep !== step) { if (!this.sequential) { currentStep.setCompleted(true); } super.gotoStep(step); } } setFutureStepsUnvisited(currentStep) { super.setFutureStepsUnvisited(currentStep); // Marquee wizard steps have an additional completed property which must also be changed. // The base class implementation only changes the visited state this.getFutureSteps(currentStep).forEach((step) => step.completed = false); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: MarqueeWizardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: MarqueeWizardComponent, selector: "ux-marquee-wizard", inputs: { description: "description", stepTemplate: "stepTemplate", sidePanelWidth: "sidePanelWidth", gutterSize: "gutterSize", resizable: "resizable" }, outputs: { sidePanelWidthChange: "sidePanelWidthChange" }, providers: [WizardService], queries: [{ propertyName: "steps", predicate: MarqueeWizardStepComponent }], viewQueries: [{ propertyName: "tabbableList", first: true, predicate: TabbableListDirective, descendants: true }], usesInheritance: true, ngImport: i0, template: "@if (resizable && _isInitialised) {\n <as-split direction=\"horizontal\"\n [gutterSize]=\"gutterSize\"\n (dragEnd)=\"onDragEnd($event)\">\n <as-split-area [size]=\"sidePanelWidth\">\n <ng-container [ngTemplateOutlet]=\"sidePanel\"></ng-container>\n </as-split-area>\n <as-split-area [size]=\"100 - sidePanelWidth\">\n <ng-container [ngTemplateOutlet]=\"mainContentPanel\"></ng-container>\n </as-split-area>\n </as-split>\n}\n\n@if (!resizable) {\n <ng-container [ngTemplateOutlet]=\"sidePanel\"></ng-container>\n <ng-container [ngTemplateOutlet]=\"mainContentPanel\"></ng-container>\n}\n\n<ng-template #sidePanel>\n <div class=\"marquee-wizard-side-panel\" [class.marquee-wizard-side-panel-resize]=\"resizable\">\n\n @if (description) {\n <div class=\"marquee-wizard-description-container\">\n <!-- If a template was provided display it -->\n @if (isTemplate) {\n <ng-container [ngTemplateOutlet]=\"$any(description)\"></ng-container>\n }\n <!-- Otherwise simply display the string -->\n @if (!isTemplate) {\n <p>{{ description }}</p>\n }\n </div>\n }\n\n <ul class=\"marquee-wizard-steps\"\n uxTabbableList\n direction=\"vertical\"\n role=\"tablist\"\n aria-orientation=\"vertical\">\n\n @for (step of steps; track step; let index = $index) {\n <li\n role=\"tab\"\n class=\"marquee-wizard-step\"\n [class.active]=\"step.active\"\n [class.visited]=\"step.visited\"\n [class.invalid]=\"!step.valid\"\n [attr.aria-posinset]=\"index + 1\"\n [attr.aria-setsize]=\"steps.length\"\n [attr.aria-selected]=\"step.active\"\n [attr.aria-controls]=\"id + '-step-' + index\"\n [attr.aria-labelledby]=\"id + '-step-' + index + '-label'\"\n [attr.aria-expanded]=\"step._active\"\n [id]=\"id + '-step-' + index + '-label'\"\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n uxTabbableListItem\n [disabled]=\"!step.visited\"\n (click)=\"gotoStep(step)\"\n (keydown.enter)=\"gotoStep(step)\">\n <ng-container [ngTemplateOutlet]=\"stepTemplate || defaultStepTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: step, index: index, context: step.context }\"></ng-container>\n </li>\n }\n\n </ul>\n </div>\n</ng-template>\n\n\n<ng-template #mainContentPanel>\n <div class=\"marquee-wizard-content-panel\" [class.marquee-wizard-content-panel-resize]=\"resizable\">\n <div class=\"marquee-wizard-content\">\n <ng-content></ng-content>\n </div>\n\n <div class=\"modal-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 marquee-wizard-previous-button\"\n [uxTooltip]=\"previousTooltip\"\n [attr.aria-label]=\"previousAriaLabel\"\n container=\"body\"\n [disabled]=\"previousDisabled || step === 0\"\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 marquee-wizard-next-button\"\n [uxTooltip]=\"nextTooltip\"\n [attr.aria-label]=\"nextAriaLabel\"\n container=\"body\"\n [disabled]=\"nextDisabled || isNextDisabled()\"\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 marquee-wizard-finish-button\"\n [uxTooltip]=\"finishTooltip\"\n [attr.aria-label]=\"finishAriaLabel\"\n container=\"body\"\n [disabled]=\"finishDisabled || isNextDisabled()\"\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 marquee-wizard-cancel-button\"\n [uxTooltip]=\"cancelTooltip\"\n [attr.aria-label]=\"cancelAriaLabel\"\n container=\"body\"\n [disabled]=\"cancelDisabled\"\n (click)=\"cancel(); tip.hide()\">\n {{ cancelText }}\n </button>\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #defaultStepTemplate let-step>\n\n <!-- Insert the icon -->\n @if (step._iconTemplate) {\n <div class=\"marquee-wizard-step-icon\">\n <ng-container [ngTemplateOutlet]=\"step._iconTemplate\"></ng-container>\n </div>\n }\n\n <span class=\"marquee-wizard-step-title\">{{ step.header }}</span>\n @if (step.completed && step.valid) {\n <ux-icon class=\"marquee-wizard-step-status\" name=\"checkmark\"></ux-icon>\n }\n @if (!step.valid) {\n <ux-icon class=\"marquee-wizard-step-status\" name=\"close\"></ux-icon>\n }\n</ng-template>\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.SplitterAccessibilityDirective, selector: "as-split", outputs: ["gutterKeydown"] }, { kind: "directive", type: i4.TabbableListDirective, selector: "[uxTabbableList]", inputs: ["direction", "wrap", "focusOnShow", "returnFocus", "hierarchy", "allowAltModifier", "allowCtrlModifier", "allowBoundaryKeys"], exportAs: ["ux-tabbable-list"] }, { kind: "directive", type: i5.TabbableListItemDirective, selector: "[uxTabbableListItem]", inputs: ["parent", "rank", "disabled", "expanded", "key"], outputs: ["expandedChange", "activated"], exportAs: ["ux-tabbable-list-item"] }, { kind: "directive", type: i6.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i7.IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: i8.TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }, { kind: "component", type: i9.SplitComponent, selector: "as-split", inputs: ["direction", "unit", "gutterSize", "gutterStep", "restrictMove", "useTransition", "disabled", "dir", "gutterDblClickDuration", "gutterClickDeltaPx", "gutterAriaLabel"], outputs: ["transitionEnd", "dragStart", "dragEnd", "gutterClick", "gutterDblClick"], exportAs: ["asSplit"] }, { kind: "directive", type: i9.SplitAreaDirective, selector: "as-split-area, [as-split-area]", inputs: ["order", "size", "minSize", "maxSize", "lockSize", "visible"], exportAs: ["asSplitArea"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: MarqueeWizardComponent, decorators: [{ type: Component, args: [{ selector: 'ux-marquee-wizard', providers: [WizardService], preserveWhitespaces: false, template: "@if (resizable && _isInitialised) {\n <as-split direction=\"horizontal\"\n [gutterSize]=\"gutterSize\"\n (dragEnd)=\"onDragEnd($event)\">\n <as-split-area [size]=\"sidePanelWidth\">\n <ng-container [ngTemplateOutlet]=\"sidePanel\"></ng-container>\n </as-split-area>\n <as-split-area [size]=\"100 - sidePanelWidth\">\n <ng-container [ngTemplateOutlet]=\"mainContentPanel\"></ng-container>\n </as-split-area>\n </as-split>\n}\n\n@if (!resizable) {\n <ng-container [ngTemplateOutlet]=\"sidePanel\"></ng-container>\n <ng-container [ngTemplateOutlet]=\"mainContentPanel\"></ng-container>\n}\n\n<ng-template #sidePanel>\n <div class=\"marquee-wizard-side-panel\" [class.marquee-wizard-side-panel-resize]=\"resizable\">\n\n @if (description) {\n <div class=\"marquee-wizard-description-container\">\n <!-- If a template was provided display it -->\n @if (isTemplate) {\n <ng-container [ngTemplateOutlet]=\"$any(description)\"></ng-container>\n }\n <!-- Otherwise simply display the string -->\n @if (!isTemplate) {\n <p>{{ description }}</p>\n }\n </div>\n }\n\n <ul class=\"marquee-wizard-steps\"\n uxTabbableList\n direction=\"vertical\"\n role=\"tablist\"\n aria-orientation=\"vertical\">\n\n @for (step of steps; track step; let index = $index) {\n <li\n role=\"tab\"\n class=\"marquee-wizard-step\"\n [class.active]=\"step.active\"\n [class.visited]=\"step.visited\"\n [class.invalid]=\"!step.valid\"\n [attr.aria-posinset]=\"index + 1\"\n [attr.aria-setsize]=\"steps.length\"\n [attr.aria-selected]=\"step.active\"\n [attr.aria-controls]=\"id + '-step-' + index\"\n [attr.aria-labelledby]=\"id + '-step-' + index + '-label'\"\n [attr.aria-expanded]=\"step._active\"\n [id]=\"id + '-step-' + index + '-label'\"\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n uxTabbableListItem\n [disabled]=\"!step.visited\"\n (click)=\"gotoStep(step)\"\n (keydown.enter)=\"gotoStep(step)\">\n <ng-container [ngTemplateOutlet]=\"stepTemplate || defaultStepTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: step, index: index, context: step.context }\"></ng-container>\n </li>\n }\n\n </ul>\n </div>\n</ng-template>\n\n\n<ng-template #mainContentPanel>\n <div class=\"marquee-wizard-content-panel\" [class.marquee-wizard-content-panel-resize]=\"resizable\">\n <div class=\"marquee-wizard-content\">\n <ng-content></ng-content>\n </div>\n\n <div class=\"modal-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 marquee-wizard-previous-button\"\n [uxTooltip]=\"previousTooltip\"\n [attr.aria-label]=\"previousAriaLabel\"\n container=\"body\"\n [disabled]=\"previousDisabled || step === 0\"\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 marquee-wizard-next-button\"\n [uxTooltip]=\"nextTooltip\"\n [attr.aria-label]=\"nextAriaLabel\"\n container=\"body\"\n [disabled]=\"nextDisabled || isNextDisabled()\"\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 marquee-wizard-finish-button\"\n [uxTooltip]=\"finishTooltip\"\n [attr.aria-label]=\"finishAriaLabel\"\n container=\"body\"\n [disabled]=\"finishDisabled || isNextDisabled()\"\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 marquee-wizard-cancel-button\"\n [uxTooltip]=\"cancelTooltip\"\n [attr.aria-label]=\"cancelAriaLabel\"\n container=\"body\"\n [disabled]=\"cancelDisabled\"\n (click)=\"cancel(); tip.hide()\">\n {{ cancelText }}\n </button>\n }\n </div>\n </div>\n</ng-template>\n\n<ng-template #defaultStepTemplate let-step>\n\n <!-- Insert the icon -->\n @if (step._iconTemplate) {\n <div class=\"marquee-wizard-step-icon\">\n <ng-container [ngTemplateOutlet]=\"step._iconTemplate\"></ng-container>\n </div>\n }\n\n <span class=\"marquee-wizard-step-title\">{{ step.header }}</span>\n @if (step.completed && step.valid) {\n <ux-icon class=\"marquee-wizard-step-status\" name=\"checkmark\"></ux-icon>\n }\n @if (!step.valid) {\n <ux-icon class=\"marquee-wizard-step-status\" name=\"close\"></ux-icon>\n }\n</ng-template>\n" }] }], ctorParameters: () => [], propDecorators: { tabbableList: [{ type: ViewChild, args: [TabbableListDirective] }], description: [{ type: Input }], stepTemplate: [{ type: Input }], sidePanelWidth: [{ type: Input }], gutterSize: [{ type: Input }], resizable: [{ type: Input }], sidePanelWidthChange: [{ type: Output }], steps: [{ type: ContentChildren, args: [MarqueeWizardStepComponent] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFycXVlZS13aXphcmQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbWFycXVlZS13aXphcmQvbWFycXVlZS13aXphcmQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbWFycXVlZS13aXphcmQvbWFycXVlZS13aXphcmQuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFvQixTQUFTLEVBQUUsZUFBZSxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBYSxNQUFNLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDNUssT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzNDLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ3ZFLE9BQU8sRUFBb0IsYUFBYSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDaEYsT0FBTyxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQXVCLE1BQU0saUJBQWlCLENBQUM7QUFDdEYsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0saUNBQWlDLENBQUM7Ozs7Ozs7Ozs7O0FBUTdFLE1BQU0sT0FBTyxzQkFBK0MsU0FBUSxlQUFlO0lBc0MvRSxJQUFJLFVBQVU7UUFDVixPQUFPLElBQUksQ0FBQyxXQUFXLElBQUksSUFBSSxDQUFDLFdBQVcsWUFBWSxXQUFXLENBQUM7SUFDdkUsQ0FBQztJQUVEO1FBQ0ksS0FBSyxFQUFFLENBQUM7UUExQ0gsa0JBQWEsR0FBRyxNQUFNLENBQTRDLGFBQWEsQ0FBQyxDQUFDO1FBRXpFLG1CQUFjLEdBQUcsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBRXZDLGdCQUFXLEdBQUcsTUFBTSxDQUEwQixVQUFVLENBQUMsQ0FBQztR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QUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUNuQixXQUFXLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ25DLENBQUM7WUFDRCxLQUFLLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pCLENBQUM7SUFDTCxDQUFDO0lBRVMsdUJBQXVCLENBQUMsV0FBdUM7UUFDckUsS0FBSyxDQUFDLHVCQUF1QixDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBRTNDLHlGQUF5RjtRQUN6RiwrREFBK0Q7UUFDL0QsSUFBSSxDQUFDLGNBQWMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFnQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQyxDQUFDO0lBQzNHLENBQUM7K0dBbElRLHNCQUFzQjttR0FBdEIsc0JBQXNCLGlRQUhwQixDQUFDLGFBQWEsQ0FBQyxnREFnQ1QsMEJBQTBCLDJFQXRCaEMscUJBQXFCLHVFQ3BCcEMsd3dLQTZKQTs7NEZEaEphLHNCQUFzQjtrQkFObEMsU0FBUzsrQkFDSSxtQkFBbUIsYUFFbEIsQ0FBQyxhQUFhLENBQUMsdUJBQ0wsS0FBSzt3REFVMUIsWUFBWTtzQkFEWCxTQUFTO3VCQUFDLHFCQUFxQjtnQkFJdkIsV0FBVztzQkFBbkIsS0FBSztnQkFHRyxZQUFZO3NCQUFwQixLQUFLO2dCQUdHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBR0csVUFBVTtzQkFBbEIsS0FBSztnQkFHRyxTQUFTO3NCQUFqQixLQUFLO2dCQUdJLG9CQUFvQjtzQkFBN0IsTUFBTTtnQkFHc0MsS0FBSztzQkFBakQsZUFBZTt1QkFBQywwQkFBMEIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBBZnRlclZpZXdDaGVja2VkLCBDb21wb25lbnQsIENvbnRlbnRDaGlsZHJlbiwgRWxlbWVudFJlZiwgRXZlbnRFbWl0dGVyLCBpbmplY3QsIElucHV0LCBPbkRlc3Ryb3ksIE91dHB1dCwgUXVlcnlMaXN0LCBUZW1wbGF0ZVJlZiwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyB0YWtlVW50aWwgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBUYWJiYWJsZUxpc3REaXJlY3RpdmUgfSBmcm9tICcuLi8uLi9kaXJlY3RpdmVzL2FjY2Vzc2liaWxpdHknO1xuaW1wb3J0IHsgUmVzaXplRGltZW5zaW9ucywgUmVzaXplU2VydmljZSB9IGZyb20gJy4uLy4uL2RpcmVjdGl2ZXMvcmVzaXplL2luZGV4JztcbmltcG9ydCB7IFdpemFyZENvbXBvbmVudCwgV2l6YXJkU2VydmljZSwgV2l6YXJkU3RlcENvbXBvbmVudCB9IGZyb20gJy4uL3dpemFyZC9pbmRleCc7XG5pbXBvcnQgeyBNYXJxdWVlV2l6YXJkU3RlcENvbXBvbmVudCB9IGZyb20gJy4vbWFycXVlZS13aXphcmQtc3RlcC5jb21wb25lbnQnO1xuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ3V4LW1hcnF1ZWUtd2l6YXJkJyxcbiAgICB0ZW1wbGF0ZVVybDogJy4vbWFycXVlZS13aXphcmQuY29tcG9uZW50Lmh0bWwnLFxuICAgIHByb3ZpZGVyczogW1dpemFyZFNlcnZpY2VdLFxuICAgIHByZXNlcnZlV2hpdGVzcGFjZXM6IGZhbHNlXG59KVxuZXhwb3J0IGNsYXNzIE1hcnF1ZWVXaXphcmRDb21wb25lbnQ8VFN0ZXBDb250ZXh0ID0gdW5rbm93bj4gZXh0ZW5kcyBXaXphcmRDb21wb25lbnQgaW1wbGVtZW50cyBPbkRlc3Ryb3ksIEFmdGVyVmlld0NoZWNrZWQge1xuICAgIHJlYWRvbmx5IHdpemFyZFNlcnZpY2UgPSBpbmplY3Q8V2l6YXJkU2VydmljZTxNYXJxdWVlV2l6YXJkU3RlcENvbXBvbmVudD4+KFdpemFyZFNlcnZpY2UpO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBfcmVzaXplU2VydmljZSA9IGluamVjdChSZXNpemVTZXJ2aWNlKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX2VsZW1lbnRSZWYgPSBpbmplY3Q8RWxlbWVudFJlZjxIVE1MRWxlbWVudD4+KEVsZW1lbnRSZWYpO1xuXG4gICAgQFZpZXdDaGlsZChUYWJiYWJsZUxpc3REaXJlY3RpdmUpXG4gICAgdGFiYmFibGVMaXN0OiBUYWJiYWJsZUxpc3REaXJlY3RpdmU7XG5cbiAgICAvKiogUHJvdmlkZSBhIGN1c3RvbSB0ZW1wbGF0ZSBmb3IgdGhlIGRlc2NyaXB0aW9uIGluIHRoZSBsZWZ0IHBhbmVsICovXG4gICAgQElucHV0KCkgZGVzY3JpcHRpb246IHN0cmluZyB8IFRlbXBsYXRlUmVmPHZvaWQ+O1xuXG4gICAgLyoqIFByb3ZpZGUgYSBjdXN0b20gdGVtcGxhdGUgZm9yIHRoZSBzdGVwIGluIHRoZSBsZWZ0IHBhbmVsICovXG4gICAgQElucHV0KCkgc3RlcFRlbXBsYXRlOiBUZW1wbGF0ZVJlZjxNYXJxdWVlV2l6YXJkU3RlcENvbnRleHQ8VFN0ZXBDb250ZXh0Pj47XG5cbiAgICAvKiogSW5pdGlhbCBzZXQgdG8gZGVmYXVsdCB3aWR0aCB0byBtYXRjaCAyNDBweCBvbiBsZWZ0IGJ1dCBjYW4gYmUgY2hhbmdlZCB3aXRoIGEgcGVyY2VudGFnZSB2YWx1ZSAqL1xuICAgIEBJbnB1dCgpIHNpZGVQYW5lbFdpZHRoOiBudW1iZXIgPSAyNTtcblxuICAgIC8qKiBXaWR0aCBvZiB0aGUgc3BsaXR0ZXIgLSBkZWZhdWx0IGlzIDEwICovXG4gICAgQElucHV0KCkgZ3V0dGVyU2l6ZTogbnVtYmVyID0gMTA7XG5cbiAgICAvKiogSWYgc2V0IHRvIHRydWUgdGhlIHJlc2l6YWJsZSBzcGxpdHRlciB3aWxsIGJlIGVuYWJsZWQgYW5kIHNldCB0byB0aGUgZGVmYXVsdCB3aWR0aCAqKi9cbiAgICBASW5wdXQoKSByZXNpemFibGU6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKiBFbWl0IHRoZSBjdXJyZW50IHdpZHRoIG9mIHRoZSBzcGxpdHRlciovXG4gICAgQE91dHB1dCgpIHNpZGVQYW5lbFdpZHRoQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG5cbiAgICAvKiogQWNjZXNzIGVhY2ggc3RlcCBjb250ZW50IGNvbXBvbmVudCAqL1xuICAgIEBDb250ZW50Q2hpbGRyZW4oTWFycXVlZVdpemFyZFN0ZXBDb21wb25lbnQpIHN0ZXBzID0gbmV3IFF1ZXJ5TGlzdDxNYXJxdWVlV2l6YXJkU3RlcENvbXBvbmVudD4oKTtcblxuICAgIC8qKlxuICAgICAqIElmIHRoZSB3aXphcmQgaXMgaW4gYSBtb2RhbCBpdCBtYXkgaW5pdGlhbGx5IGhhdmUgYSBzaXplIG9mIDAgdW50aWwgdGhlIG1vZGFsIGRpc3BsYXlzXG4gICAgICogaW4gd2hpY2ggY2FzZSBpZiB3ZSBhcmUgdXNpbmcgdGhlIHNwbGl0dGVyIGl0IHdpbGwgbm90IHJlbmRlciBjb3JyZWN0bHkuIFdlIHVzZSB0aGlzXG4gICAgICogdmFyaWFibGUgdG8gb25seSBpbml0aWFsaXNlIHRoZSBzcGxpdHRlciB3aGVuIHRoZSBjb250ZW50IGhhcyBhIHdpZHRoLlxuICAgICAqL1xuICAgIF9pc0luaXRpYWxpc2VkOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICBnZXQgaXNUZW1wbGF0ZSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGVzY3JpcHRpb24gJiYgdGhpcy5kZXNjcmlwdGlvbiBpbnN0YW5jZW9mIFRlbXBsYXRlUmVmO1xuICAgIH1cblxuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICBzdXBlcigpO1xuXG4gICAgICAgIC8vIHNldCB0byB0cnVlIGFzIGRlZmF1bHQgZm9yIE1hcnF1ZWUgV2l6YXJkIG9ubHlcbiAgICAgICAgdGhpcy5yZXNldFZpc2l0ZWRPblZhbGlkYXRpb25FcnJvciA9IHRydWU7XG5cbiAgICAgICAgLy8gd2F0Y2ggZm9yIGNoYW5nZXMgdG8gdGhlIHNpemVcbiAgICAgICAgdGhpcy5fcmVzaXplU2VydmljZS5hZGRSZXNpemVMaXN0ZW5lcih0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQpXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUodGhpcy5vblJlc2l6ZS5iaW5kKHRoaXMpKTtcbiAgICB9XG5cbiAgICBuZ0FmdGVyVmlld0NoZWNrZWQoKTogdm9pZCB7XG4gICAgICAgIHRoaXMudGFiYmFibGVMaXN0Py5zZXRGaXJzdEl0ZW1UYWJiYWJsZSgpO1xuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICBzdXBlci5uZ09uRGVzdHJveSgpO1xuICAgICAgICB0aGlzLl9yZXNpemVTZXJ2aWNlLnJlbW92ZVJlc2l6ZUxpc3RlbmVyKHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSWYgdGhlIGN1cnJlbnQgc3RlcCBpcyB2YWxpZCwgbWFyayBpdCBhc1xuICAgICAqIGNvbXBsZXRlIGFuZCBnbyB0byB0aGUgbmV4dCBzdGVwXG4gICAgICovXG4gICAgYXN5bmMgbmV4dCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gZ2V0IHRoZSBjdXJyZW50IHN0ZXBcbiAgICAgICAgY29uc3Qgc3RlcCA9IHRoaXMuZ2V0Q3VycmVudFN0ZXAoKSBhcyBNYXJxdWVlV2l6YXJkU3RlcENvbXBvbmVudDtcblxuICAgICAgICBhd2FpdCBzdXBlci5uZXh0KCk7XG5cbiAgICAgICAgaWYgKHN0ZXAgJiYgc3RlcC52YWxpZCkge1xuICAgICAgICAgICAgLy8gbWFyayB0aGlzIHN0ZXAgYXMgY29tcGxldGVkXG4gICAgICAgICAgICBzdGVwLnNldENvbXBsZXRlZCh0cnVlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc3RlcEVycm9yLm5leHQodGhpcy5zdGVwKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEVtaXQgdGhlIG9uRmluaXNoaW5nIGV2ZW50IGFuZCBpZiB2YWxpZCB0aGUgb25GaW5pc2ggZXZlbnQuXG4gICAgICogQWxzbyBtYXJrIHRoZSBmaW5hbCBzdGVwIGFzIGNvbXBsZXRlZCBpZiBpdCBpcyB2YWxpZFxuICAgICAqL1xuICAgIGFzeW5jIGZpbmlzaCgpOiBQcm9taXNlPHZvaWQ+IHtcblxuICAgICAgICAvLyBnZXQgdGhlIGN1cnJlbnQgc3RlcFxuICAgICAgICBjb25zdCBzdGVwID0gdGhpcy5nZXRDdXJyZW50U3RlcCgpIGFzIE1hcnF1ZWVXaXphcmRTdGVwQ29tcG9uZW50O1xuXG4gICAgICAgIGF3YWl0IHN1cGVyLmZpbmlzaCgpO1xuXG4gICAgICAgIC8vIGlmIHRoZSBzdGVwIGlzIHZhbGlkIGluZGljYXRlIHRoYXQgaXQgaXMgbm93IGNvbXBsZXRlXG4gICAgICAgIGlmIChzdGVwLnZhbGlkKSB7XG4gICAgICAgICAgICBzdGVwLnNldENvbXBsZXRlZCh0cnVlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc3RlcEVycm9yLm5leHQodGhpcy5zdGVwKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uUmVzaXplKGV2ZW50OiBSZXNpemVEaW1lbnNpb25zKTogdm9pZCB7XG4gICAgICAgIGlmIChldmVudC53aWR0aCAhPT0gMCAmJiBldmVudC5oZWlnaHQgIT09IDApIHtcbiAgICAgICAgICAgIHRoaXMuX2lzSW5pdGlhbGlzZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqIFdoZW5ldmVyIHRoZSBkcmFnIGV2ZW50IGVuZHMsIHVwZGF0ZSB0aGUgaW50ZXJuYWwgdmFsdWUgYW5kIGVtaXQgdGhlIG5ldyBzaXplICovXG4gICAgb25EcmFnRW5kKHsgc2l6ZXMgfTogU3BsaXREcmFnRW5kRXZlbnQpOiB2b2lkIHtcbiAgICAgICAgLy8gd2UgbmVlZCB0byBvbmx5IGdldCB0aGUgc2l6ZSBvZiB0aGUgZmlyc3QgcGFuZWwgd2hpY2ggd2lsbCBiZSB0aGUgc2lkZSBwYW5lbFxuICAgICAgICB0aGlzLnNpZGVQYW5lbFdpZHRoID0gc2l6ZXNbMF0gYXMgbnVtYmVyO1xuICAgICAgICB0aGlzLnNpZGVQYW5lbFdpZHRoQ2hhbmdlLmVtaXQodGhpcy5zaWRlUGFuZWxXaWR0aCk7XG4gICAgfVxuXG4gICAgZ290b1N0ZXAoc3RlcDogV2l6YXJkU3RlcENvbXBvbmVudCk6IHZvaWQge1xuICAgICAgICBjb25zdCBjdXJyZW50U3RlcCA9IHRoaXMuZ2V0Q3VycmVudFN0ZXAoKSBhcyBNYXJxdWVlV2l6YXJkU3RlcENvbXBvbmVudDtcblxuICAgICAgICBpZiAoY3VycmVudFN0ZXAgIT09IHN0ZXApIHtcbiAgICAgICAgICAgIGlmICghdGhpcy5zZXF1ZW50aWFsKSB7XG4gICAgICAgICAgICAgICAgY3VycmVudFN0ZXAuc2V0Q29tcGxldGVkKHRydWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc3VwZXIuZ290b1N0ZXAoc3RlcCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgc2V0RnV0dXJlU3RlcHNVbnZpc2l0ZWQoY3VycmVudFN0ZXA6IE1hcnF1ZWVXaXphcmRTdGVwQ29tcG9uZW50KTogdm9pZCB7XG4gICAgICAgIHN1cGVyLnNldEZ1dHVyZVN0ZXBzVW52aXNpdGVkKGN1cnJlbnRTdGVwKTtcblxuICAgICAgICAvLyBNYXJxdWVlIHdpemFyZCBzdGVwcyBoYXZlIGFuIGFkZGl0aW9uYWwgY29tcGxldGVkIHByb3BlcnR5IHdoaWNoIG11c3QgYWxzbyBiZSBjaGFuZ2VkLlxuICAgICAgICAvLyBUaGUgYmFzZSBjbGFzcyBpbXBsZW1lbnRhdGlvbiBvbmx5IGNoYW5nZXMgdGhlIHZpc2l0ZWQgc3RhdGVcbiAgICAgICAgdGhpcy5nZXRGdXR1cmVTdGVwcyhjdXJyZW50U3RlcCkuZm9yRWFjaCgoc3RlcDogTWFycXVlZVdpemFyZFN0ZXBDb21wb25lbnQpID0+IHN0ZXAuY29tcGxldGVkID0gZmFsc2UpO1xuICAgIH1cbn1cblxuLyoqIEFuZ3VsYXIgU3BsaXQgZG9lcyBub3QgZXhwb3J0IGEgdHlwZSBmb3IgdGhpcyBzbyB3ZSBjcmVhdGVkIG91ciBvd24gKi9cbmludGVyZmFjZSBTcGxpdERyYWdFbmRFdmVudCB7XG4gICAgZ3V0dGVyTnVtOiBudW1iZXI7XG4gICAgc2l6ZXM6IChudW1iZXIgfCAnKicpW107XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTWFycXVlZVdpemFyZFN0ZXBDb250ZXh0PFQ+IHtcbiAgICAkaW1wbGljaXQ6IE1hcnF1ZWVXaXphcmRTdGVwQ29tcG9uZW50PFQ+O1xuICAgIGluZGV4OiBudW1iZXI7XG4gICAgY29udGV4dDogVDtcbn1cbiIsIkBpZiAocmVzaXphYmxlICYmIF9pc0luaXRpYWxpc2VkKSB7XG4gIDxhcy1zcGxpdCBkaXJlY3Rpb249XCJob3Jpem9udGFsXCJcbiAgICBbZ3V0dGVyU2l6ZV09XCJndXR0ZXJTaXplXCJcbiAgICAoZHJhZ0VuZCk9XCJvbkRyYWdFbmQoJGV2ZW50KVwiPlxuICAgIDxhcy1zcGxpdC1hcmVhIFtzaXplXT1cInNpZGVQYW5lbFdpZHRoXCI+XG4gICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cInNpZGVQYW5lbFwiPjwvbmctY29udGFpbmVyPlxuICAgIDwvYXMtc3BsaXQtYXJlYT5cbiAgICA8YXMtc3BsaXQtYXJlYSBbc2l6ZV09XCIxMDAgLSBzaWRlUGFuZWxXaWR0aFwiPlxuICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJtYWluQ29udGVudFBhbmVsXCI+PC9uZy1jb250YWluZXI+XG4gICAgPC9hcy1zcGxpdC1hcmVhPlxuICA8L2FzLXNwbGl0PlxufVxuXG5AaWYgKCFyZXNpemFibGUpIHtcbiAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJzaWRlUGFuZWxcIj48L25nLWNvbnRhaW5lcj5cbiAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJtYWluQ29udGVudFBhbmVsXCI+PC9uZy1jb250YWluZXI+XG59XG5cbjxuZy10ZW1wbGF0ZSAjc2lkZVBhbmVsPlxuICA8ZGl2IGNsYXNzPVwibWFycXVlZS13aXphcmQtc2lkZS1wYW5lbFwiIFtjbGFzcy5tYXJxdWVlLXdpemFyZC1zaWRlLXBhbmVsLXJlc2l6ZV09XCJyZXNpemFibGVcIj5cblxuICAgIEBpZiAoZGVzY3JpcHRpb24pIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJtYXJxdWVlLXdpemFyZC1kZXNjcmlwdGlvbi1jb250YWluZXJcIj5cbiAgICAgICAgPCEtLSBJZiBhIHRlbXBsYXRlIHdhcyBwcm92aWRlZCBkaXNwbGF5IGl0IC0tPlxuICAgICAgICBAaWYgKGlzVGVtcGxhdGUpIHtcbiAgICAgICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIiRhbnkoZGVzY3JpcHRpb24pXCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIH1cbiAgICAgICAgPCEtLSBPdGhlcndpc2Ugc2ltcGx5IGRpc3BsYXkgdGhlIHN0cmluZyAtLT5cbiAgICAgICAgQGlmICghaXNUZW1wbGF0ZSkge1xuICAgICAgICAgIDxwPnt7IGRlc2NyaXB0aW9uIH19PC9wPlxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cbiAgICB9XG5cbiAgICA8dWwgY2xhc3M9XCJtYXJxdWVlLXdpemFyZC1zdGVwc1wiXG4gICAgICB1eFRhYmJhYmxlTGlzdFxuICAgICAgZGlyZWN0aW9uPVwidmVydGljYWxcIlxuICAgICAgcm9sZT1cInRhYmxpc3RcIlxuICAgICAgYXJpYS1vcmllbnRhdGlvbj1cInZlcnRpY2FsXCI+XG5cbiAgICAgIEBmb3IgKHN0ZXAgb2Ygc3RlcHM7IHRyYWNrIHN0ZXA7IGxldCBpbmRleCA9ICRpbmRleCkge1xuICAgICAgICA8bGlcbiAgICAgICAgICByb2xlPVwidGFiXCJcbiAgICAgICAgICBjbGFzcz1cIm1hcnF1ZWUtd2l6YXJkLXN0ZXBcIlxuICAgICAgICAgIFtjbGFzcy5hY3RpdmVdPVwic3RlcC5hY3RpdmVcIlxuICAgICAgICAgIFtjbGFzcy52aXNpdGVkXT1cInN0ZXAudmlzaXRlZFwiXG4gICAgICAgICAgW2NsYXNzLmludmFsaWRdPVwiIXN0ZXAudmFsaWRcIlxuICAgICAgICAgIFthdHRyLmFyaWEtcG9zaW5zZXRdPVwiaW5kZXggKyAxXCJcbiAgICAgICAgICBbYXR0ci5hcmlhLXNldHNpemVdPVwic3RlcHMubGVuZ3RoXCJcbiAgICAgICAgICBbYXR0ci5hcmlhLXNlbGVjdGVkXT1cInN0ZXAuYWN0aXZlXCJcbiAgICAgICAgICBbYXR0ci5hcmlhLWNvbnRyb2xzXT1cImlkICsgJy1zdGVwLScgKyBpbmRleFwiXG4gICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbGxlZGJ5XT1cImlkICsgJy1zdGVwLScgKyBpbmRleCArICctbGFiZWwnXCJcbiAgICAgICAgICBbYXR0ci5hcmlhLWV4cGFuZGVkXT1cInN0ZXAuX2FjdGl2ZVwiXG4gICAgICAgICAgW2lkXT1cImlkICsgJy1zdGVwLScgKyBpbmRleCArICctbGFiZWwnXCJcbiAgICAgICAgICB1eEZvY3VzSW5kaWNhdG9yXG4gICAgICAgICAgW3Byb2dyYW1tYXRpY0ZvY3VzSW5kaWNhdG9yXT1cInRydWVcIlxuICAgICAgICAgIHV4VGFiYmFibGVMaXN0SXRlbVxuICAgICAgICAgIFtkaXNhYmxlZF09XCIhc3RlcC52aXNpdGVkXCJcbiAgICAgICAgICAoY2xpY2spPVwiZ290b1N0ZXAoc3RlcClcIlxuICAgICAgICAgIChrZXlkb3duLmVudGVyKT1cImdvdG9TdGVwKHN0ZXApXCI+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJzdGVwVGVtcGxhdGUgfHwgZGVmYXVsdFN0ZXBUZW1wbGF0ZVwiXG4gICAgICAgICAgW25nVGVtcGxhdGVPdXRsZXRDb250ZXh0XT1cInsgJGltcGxpY2l0OiBzdGVwLCBpbmRleDogaW5kZXgsIGNvbnRleHQ6IHN0ZXAuY29udGV4dCB9XCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvbGk+XG4gICAgICB9XG5cbiAgICA8L3VsPlxuICA8L2Rpdj5cbjwvbmctdGVtcGxhdGU+XG5cblxuPG5nLXRlbXBsYXRlICNtYWluQ29udGVudFBhbmVsPlxuICA8ZGl2IGNsYXNzPVwibWFycXVlZS13aXphcmQtY29udGVudC1wYW5lbFwiIFtjbGFzcy5tYXJxdWVlLXdpemFyZC1jb250ZW50LXBhbmVsLXJlc2l6ZV09XCJyZXNpemFibGVcIj5cbiAgICA8ZGl2IGNsYXNzPVwibWFycXVlZS13aXphcmQtY29udGVudFwiPlxuICAgICAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuICAgIDwvZGl2PlxuXG4gICAgPGRpdiBjbGFzcz1cIm1vZGFsLWZvb3RlclwiPlxuXG4gICAgICBAaWYgKGZvb3RlclRlbXBsYXRlKSB7XG4gICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICBbbmdUZW1wbGF0ZU91dGxldF09XCJmb290ZXJUZW1wbGF0ZVwiXG4gICAgICAgICAgW25nVGVtcGxhdGVPdXRsZXRDb250ZXh0XT1cInsgc3RlcDogc3RlcCB9XCI+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgfVxuXG4gICAgICBAaWYgKHByZXZpb3VzVmlzaWJsZSkge1xuICAgICAgICA8YnV0dG9uICN0aXA9XCJ1eC10b29sdGlwXCJcbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICBjbGFzcz1cImJ0biBidXR0b24tc2Vjb25kYXJ5IG1hcnF1ZWUtd2l6YXJkLXByZXZpb3VzLWJ1dHRvblwiXG4gICAgICAgICAgW3V4VG9vbHRpcF09XCJwcmV2aW91c1Rvb2x0aXBcIlxuICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwicHJldmlvdXNBcmlhTGFiZWxcIlxuICAgICAgICAgIGNvbnRhaW5lcj1cImJvZHlcIlxuICAgICAgICAgIFtkaXNhYmxlZF09XCJwcmV2aW91c0Rpc2FibGVkIHx8IHN0ZXAgPT09IDBcIlxuICAgICAgICAgIChjbGljayk9XCJwcmV2aW91cygpOyB0aXAuaGlkZSgpXCI+XG4gICAgICAgICAge3sgcHJldmlvdXNUZXh0IH19XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgfVxuXG4gICAgICBAaWYgKG5leHRWaXNpYmxlICYmICFpc0xhc3RTdGVwKCkpIHtcbiAgICAgICAgPGJ1dHRvbiAjdGlwPVwidXgtdG9vbHRpcFwiXG4gICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgY2xhc3M9XCJidG4gYnV0dG9uLXByaW1hcnkgbWFycXVlZS13aXphcmQtbmV4dC1idXR0b25cIlxuICAgICAgICAgIFt1eFRvb2x0aXBdPVwibmV4dFRvb2x0aXBcIlxuICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwibmV4dEFyaWFMYWJlbFwiXG4gICAgICAgICAgY29udGFpbmVyPVwiYm9keVwiXG4gICAgICAgICAgW2Rpc2FibGVkXT1cIm5leHREaXNhYmxlZCB8fCBpc05leHREaXNhYmxlZCgpXCJcbiAgICAgICAgICAoY2xpY2spPVwibmV4dCgpOyB0aXAuaGlkZSgpXCI+XG4gICAgICAgICAge3sgbmV4dFRleHQgfX1cbiAgICAgICAgPC9idXR0b24+XG4gICAgICB9XG5cbiAgICAgIEBpZiAoZmluaXNoVmlzaWJsZSAmJiBpc0xhc3RTdGVwKCkgfHwgZmluaXNoQWx3YXlzVmlzaWJsZSkge1xuICAgICAgICA8YnV0dG9uICN0aXA9XCJ1eC10b29sdGlwXCJcbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICBjbGFzcz1cImJ0biBidXR0b24tcHJpbWFyeSBtYXJxdWVlLXdpemFyZC1maW5pc2gtYnV0dG9uXCJcbiAgICAgICAgICBbdXhUb29sdGlwXT1cImZpbmlzaFRvb2x0aXBcIlxuICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiZmluaXNoQXJpYUxhYmVsXCJcbiAgICAgICAgICBjb250YWluZXI9XCJib2R5XCJcbiAgICAgICAgICBbZGlzYWJsZWRdPVwiZmluaXNoRGlzYWJsZWQgfHwgaXNOZXh0RGlzYWJsZWQoKVwiXG4gICAgICAgICAgKGNsaWNrKT1cImZpbmlzaCgpOyB0aXAuaGlkZSgpXCI+XG4gICAgICAgICAge3sgZmluaXNoVGV4dCB9fVxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIH1cblxuICAgICAgQGlmIChjYW5jZWxWaXNpYmxlICYmICFpc0xhc3RTdGVwKCkgfHwgY2FuY2VsQWx3YXlzVmlzaWJsZSkge1xuICAgICAgICA8YnV0dG9uICN0aXA9XCJ1eC10b29sdGlwXCJcbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICBjbGFzcz1cImJ0biBidXR0b24tc2Vjb25kYXJ5IG1hcnF1ZWUtd2l6YXJkLWNhbmNlbC1idXR0b25cIlxuICAgICAgICAgIFt1eFRvb2x0aXBdPVwiY2FuY2VsVG9vbHRpcFwiXG4gICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCJjYW5jZWxBcmlhTGFiZWxcIlxuICAgICAgICAgIGNvbnRhaW5lcj1cImJvZHlcIlxuICAgICAgICAgIFtkaXNhYmxlZF09XCJjYW5jZWxEaXNhYmxlZFwiXG4gICAgICAgICAgKGNsaWNrKT1cImNhbmNlbCgpOyB0aXAuaGlkZSgpXCI+XG4gICAgICAgICAge3sgY2FuY2VsVGV4dCB9fVxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIH1cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG48L25nLXRlbXBsYXRlPlxuXG48bmctdGVtcGxhdGUgI2RlZmF1bHRTdGVwVGVtcGxhdGUgbGV0LXN0ZXA+XG5cbiAgPCEtLSBJbnNlcnQgdGhlIGljb24gLS0+XG4gIEBpZiAoc3RlcC5faWNvblRlbXBsYXRlKSB7XG4gICAgPGRpdiBjbGFzcz1cIm1hcnF1ZWUtd2l6YXJkLXN0ZXAtaWNvblwiPlxuICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJzdGVwLl9pY29uVGVtcGxhdGVcIj48L25nLWNvbnRhaW5lcj5cbiAgICA8L2Rpdj5cbiAgfVxuXG4gIDxzcGFuIGNsYXNzPVwibWFycXVlZS13aXphcmQtc3RlcC10aXRsZVwiPnt7IHN0ZXAuaGVhZGVyIH19PC9zcGFuPlxuICBAaWYgKHN0ZXAuY29tcGxldGVkICYmIHN0ZXAudmFsaWQpIHtcbiAgICA8dXgtaWNvbiBjbGFzcz1cIm1hcnF1ZWUtd2l6YXJkLXN0ZXAtc3RhdHVzXCIgbmFtZT1cImNoZWNrbWFya1wiPjwvdXgtaWNvbj5cbiAgfVxuICBAaWYgKCFzdGVwLnZhbGlkKSB7XG4gICAgPHV4LWljb24gY2xhc3M9XCJtYXJxdWVlLXdpemFyZC1zdGVwLXN0YXR1c1wiIG5hbWU9XCJjbG9zZVwiPjwvdXgtaWNvbj5cbiAgfVxuPC9uZy10ZW1wbGF0ZT5cbiJdfQ==