UNPKG

carbon-components-angular

Version:
493 lines (485 loc) 43 kB
import { Component, HostBinding, Input, TemplateRef, ViewChild } from "@angular/core"; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; import * as i2 from "carbon-components-angular/icon"; /** * Get started with importing the module: * * ```typescript * import { InputModule } from 'carbon-components-angular'; * ``` * * ```html * <cds-text-label> * Label * <input cdsText type="text" class="input-field"> * </cds-text-label> * ``` * * [See demo](../../?path=/story/components-input--basic) */ export class TextInputLabelComponent { /** * Creates an instance of Label. */ constructor(changeDetectorRef) { this.changeDetectorRef = changeDetectorRef; /** * The id of the input item associated with the `Label`. This value is also used to associate the `Label` with * its input counterpart through the 'for' attribute. */ this.labelInputID = "ibm-text-input-" + TextInputLabelComponent.labelCounter++; /** * Set to `true` for a disabled label. */ this.disabled = false; /** * Set to `true` for a loading label. */ this.skeleton = false; /** * Set to `true` for an invalid label component. */ this.invalid = false; /** * Set to `true` to show a warning (contents set by warningText) */ this.warn = false; /** * Experimental: enable fluid state */ this.fluid = false; /** * Set to `true` to hide the label visually, but keep accessible to * screen readers. */ this.hideLabel = false; /** * Set to `true` to render the label and field side-by-side instead of stacked. */ this.inline = false; /** * The render size for the `TextInput`. Used to compute the INLINE label size * variant class (`cds--label--inline--{size}`). */ this.size = "md"; /** * Set to `true` (`maxCount` must be set) to displays a live character * counter alongside the label. */ this.enableCounter = false; // Tracks current character count for the counter display. this.textCount = 0; this.labelClass = true; this.textInputWrapper = true; // Cached reference to the input element, set once in ngAfterViewInit. this._inputElement = null; // Cached listener so it can be removed precisely (avoids anonymous-function leak). this._inputListener = null; } get isInlineWrapper() { return this.inline; } get isReadonly() { return this.wrapper?.nativeElement.querySelector("input")?.readOnly ?? false; } get fluidClass() { return this.fluid && !this.skeleton; } get fluidSkeletonClass() { return this.fluid && this.skeleton; } /** * Sets the id on the input item associated with the `Label` and attaches the * counter listener when `enableCounter` is already `true` on first render. */ ngAfterViewInit() { if (this.wrapper) { // Prioritize setting id to `input` over div const inputElement = this.wrapper.nativeElement.querySelector("input"); if (inputElement) { // avoid overriding ids already set by the user, reuse it instead if (inputElement.id) { this.labelInputID = inputElement.id; this.changeDetectorRef.detectChanges(); } inputElement.setAttribute("id", this.labelInputID); this._inputElement = inputElement; if (this.enableCounter) { this.textCount = inputElement.value?.length || 0; this._attachCounterListener(); } return; } const divElement = this.wrapper.nativeElement.querySelector("div"); if (divElement) { if (divElement.id) { this.labelInputID = divElement.id; this.changeDetectorRef.detectChanges(); } divElement.setAttribute("id", this.labelInputID); } } } /** * Attach/remove listener and seed `textCount` from the textarea's current value. * @param changes */ ngOnChanges(changes) { if (changes.enableCounter && !changes.enableCounter.firstChange) { if (changes.enableCounter.currentValue) { if (this._inputElement) { this.textCount = this._inputElement.value?.length || 0; this._attachCounterListener(); this.changeDetectorRef.detectChanges(); } } else { this._detachCounterListener(); } } } ngAfterContentInit() { this.changeDetectorRef.detectChanges(); } ngOnDestroy() { this._detachCounterListener(); } isTemplate(value) { return value instanceof TemplateRef; } /** * Attaches the input event listener, ensuring it is never added twice. */ _attachCounterListener() { this._detachCounterListener(); if (!this._inputElement) { return; } this._inputListener = (e) => { this.textCount = e.target.value?.length || 0; this.changeDetectorRef.detectChanges(); }; this._inputElement.addEventListener("input", this._inputListener); } /** * Removes the input event listener and clears the cached reference. */ _detachCounterListener() { if (this._inputListener && this._inputElement) { this._inputElement.removeEventListener("input", this._inputListener); this._inputListener = null; } } } /** * Used to build the id of the input item associated with the `Label`. */ TextInputLabelComponent.labelCounter = 0; TextInputLabelComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: TextInputLabelComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); TextInputLabelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: TextInputLabelComponent, selector: "cds-text-label, ibm-text-label", inputs: { labelInputID: "labelInputID", disabled: "disabled", skeleton: "skeleton", labelTemplate: "labelTemplate", textInputTemplate: "textInputTemplate", helperText: "helperText", invalidText: "invalidText", invalid: "invalid", warn: "warn", warnText: "warnText", ariaLabel: "ariaLabel", fluid: "fluid", decorator: "decorator", hideLabel: "hideLabel", inline: "inline", size: "size", enableCounter: "enableCounter", maxCount: "maxCount" }, host: { properties: { "class.cds--text-input-wrapper--inline": "this.isInlineWrapper", "class.cds--text-input-wrapper--readonly": "this.isReadonly", "class.cds--text-input--fluid": "this.fluidClass", "class.cds--text-input--fluid__skeleton": "this.fluidSkeletonClass", "class.cds--form-item": "this.labelClass", "class.cds--text-input-wrapper": "this.textInputWrapper" } }, viewQueries: [{ propertyName: "wrapper", first: true, predicate: ["wrapper"], descendants: true }], usesOnChanges: true, ngImport: i0, template: ` <ng-container *ngIf="skeleton"> <span class="cds--label cds--skeleton"></span> <div class="cds--text-input cds--skeleton"></div> </ng-container> <ng-container *ngIf="!skeleton"> <!-- non-inline: label-wrapper above field; inline: label+validation side-by-side --> <ng-container *ngIf="!inline; else inlineHeader"> <div class="cds--text-input__label-wrapper"> <label [for]="labelInputID" [attr.aria-label]="ariaLabel" class="cds--label" [ngClass]="{ 'cds--label--disabled': disabled, 'cds--visually-hidden': hideLabel }"> <ng-template *ngIf="labelTemplate; else labelContent" [ngTemplateOutlet]="labelTemplate"></ng-template> <ng-template #labelContent> <ng-content></ng-content> </ng-template> </label> <span *ngIf="enableCounter && maxCount" class="cds--label" [ngClass]="{'cds--label--disabled': disabled}" aria-hidden="true"> {{textCount}}/{{maxCount}} </span> </div> </ng-container> <ng-template #inlineHeader> <div class="cds--text-input__label-helper-wrapper"> <div class="cds--text-input__label-wrapper"> <label [for]="labelInputID" [attr.aria-label]="ariaLabel" class="cds--label" [ngClass]="{ 'cds--label--disabled': disabled, 'cds--visually-hidden': hideLabel, 'cds--label--inline': true, 'cds--label--inline--sm': size === 'sm', 'cds--label--inline--md': size === 'md', 'cds--label--inline--lg': size === 'lg' }"> <ng-template *ngIf="labelTemplate" [ngTemplateOutlet]="labelTemplate"></ng-template> </label> </div> <ng-container *ngIf="!fluid"> <ng-container [ngTemplateOutlet]="validationOrHelper"></ng-container> </ng-container> </div> </ng-template> <div class="cds--text-input__field-outer-wrapper" [ngClass]="{'cds--text-input__field-outer-wrapper--inline': inline}"> <div class="cds--text-input__field-wrapper" [ngClass]="{ 'cds--text-input__field-wrapper--warning': warn, 'cds--text-input__field-wrapper--decorator': !!decorator }" [attr.data-invalid]="(invalid ? true : null)" #wrapper> <svg *ngIf="invalid && !warn" cdsIcon="warning--filled" size="16" class="cds--text-input__invalid-icon"> </svg> <svg *ngIf="!invalid && warn" cdsIcon="warning--alt--filled" size="16" class="cds--text-input__invalid-icon cds--text-input__invalid-icon--warning"> </svg> <ng-template *ngIf="textInputTemplate; else textInputContent" [ngTemplateOutlet]="textInputTemplate"></ng-template> <ng-template #textInputContent> <ng-content select="[cdsText],[ibmText],input[type=text],div"></ng-content> </ng-template> <ng-container *ngIf="decorator"> <div class="cds--text-input__field-inner-wrapper--decorator"> <ng-template [ngTemplateOutlet]="decorator"></ng-template> </div> </ng-container> <ng-container *ngIf="fluid"> <hr class="cds--text-input__divider" /> <div *ngIf="invalid" class="cds--form-requirement"> <ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container> <ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template> </div> <div *ngIf="!invalid && warn" class="cds--form-requirement"> <ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container> <ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template> </div> </ng-container> </div> <ng-container *ngIf="!fluid && !inline"> <ng-container [ngTemplateOutlet]="validationOrHelper"></ng-container> </ng-container> </div> </ng-container> <ng-template #validationOrHelper> <div *ngIf="helperText && !invalid && !warn" class="cds--form__helper-text" [ngClass]="{'cds--form__helper-text--disabled': disabled, 'cds--form__helper-text--inline': inline}"> <ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container> <ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template> </div> <div *ngIf="invalid" class="cds--form-requirement"> <ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container> <ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template> </div> <div *ngIf="!invalid && warn" class="cds--form-requirement"> <ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container> <ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template> </div> </ng-template> `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: TextInputLabelComponent, decorators: [{ type: Component, args: [{ selector: "cds-text-label, ibm-text-label", template: ` <ng-container *ngIf="skeleton"> <span class="cds--label cds--skeleton"></span> <div class="cds--text-input cds--skeleton"></div> </ng-container> <ng-container *ngIf="!skeleton"> <!-- non-inline: label-wrapper above field; inline: label+validation side-by-side --> <ng-container *ngIf="!inline; else inlineHeader"> <div class="cds--text-input__label-wrapper"> <label [for]="labelInputID" [attr.aria-label]="ariaLabel" class="cds--label" [ngClass]="{ 'cds--label--disabled': disabled, 'cds--visually-hidden': hideLabel }"> <ng-template *ngIf="labelTemplate; else labelContent" [ngTemplateOutlet]="labelTemplate"></ng-template> <ng-template #labelContent> <ng-content></ng-content> </ng-template> </label> <span *ngIf="enableCounter && maxCount" class="cds--label" [ngClass]="{'cds--label--disabled': disabled}" aria-hidden="true"> {{textCount}}/{{maxCount}} </span> </div> </ng-container> <ng-template #inlineHeader> <div class="cds--text-input__label-helper-wrapper"> <div class="cds--text-input__label-wrapper"> <label [for]="labelInputID" [attr.aria-label]="ariaLabel" class="cds--label" [ngClass]="{ 'cds--label--disabled': disabled, 'cds--visually-hidden': hideLabel, 'cds--label--inline': true, 'cds--label--inline--sm': size === 'sm', 'cds--label--inline--md': size === 'md', 'cds--label--inline--lg': size === 'lg' }"> <ng-template *ngIf="labelTemplate" [ngTemplateOutlet]="labelTemplate"></ng-template> </label> </div> <ng-container *ngIf="!fluid"> <ng-container [ngTemplateOutlet]="validationOrHelper"></ng-container> </ng-container> </div> </ng-template> <div class="cds--text-input__field-outer-wrapper" [ngClass]="{'cds--text-input__field-outer-wrapper--inline': inline}"> <div class="cds--text-input__field-wrapper" [ngClass]="{ 'cds--text-input__field-wrapper--warning': warn, 'cds--text-input__field-wrapper--decorator': !!decorator }" [attr.data-invalid]="(invalid ? true : null)" #wrapper> <svg *ngIf="invalid && !warn" cdsIcon="warning--filled" size="16" class="cds--text-input__invalid-icon"> </svg> <svg *ngIf="!invalid && warn" cdsIcon="warning--alt--filled" size="16" class="cds--text-input__invalid-icon cds--text-input__invalid-icon--warning"> </svg> <ng-template *ngIf="textInputTemplate; else textInputContent" [ngTemplateOutlet]="textInputTemplate"></ng-template> <ng-template #textInputContent> <ng-content select="[cdsText],[ibmText],input[type=text],div"></ng-content> </ng-template> <ng-container *ngIf="decorator"> <div class="cds--text-input__field-inner-wrapper--decorator"> <ng-template [ngTemplateOutlet]="decorator"></ng-template> </div> </ng-container> <ng-container *ngIf="fluid"> <hr class="cds--text-input__divider" /> <div *ngIf="invalid" class="cds--form-requirement"> <ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container> <ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template> </div> <div *ngIf="!invalid && warn" class="cds--form-requirement"> <ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container> <ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template> </div> </ng-container> </div> <ng-container *ngIf="!fluid && !inline"> <ng-container [ngTemplateOutlet]="validationOrHelper"></ng-container> </ng-container> </div> </ng-container> <ng-template #validationOrHelper> <div *ngIf="helperText && !invalid && !warn" class="cds--form__helper-text" [ngClass]="{'cds--form__helper-text--disabled': disabled, 'cds--form__helper-text--inline': inline}"> <ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container> <ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template> </div> <div *ngIf="invalid" class="cds--form-requirement"> <ng-container *ngIf="!isTemplate(invalidText)">{{invalidText}}</ng-container> <ng-template *ngIf="isTemplate(invalidText)" [ngTemplateOutlet]="invalidText"></ng-template> </div> <div *ngIf="!invalid && warn" class="cds--form-requirement"> <ng-container *ngIf="!isTemplate(warnText)">{{warnText}}</ng-container> <ng-template *ngIf="isTemplate(warnText)" [ngTemplateOutlet]="warnText"></ng-template> </div> </ng-template> ` }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { isInlineWrapper: [{ type: HostBinding, args: ["class.cds--text-input-wrapper--inline"] }], isReadonly: [{ type: HostBinding, args: ["class.cds--text-input-wrapper--readonly"] }], fluidClass: [{ type: HostBinding, args: ["class.cds--text-input--fluid"] }], fluidSkeletonClass: [{ type: HostBinding, args: ["class.cds--text-input--fluid__skeleton"] }], labelInputID: [{ type: Input }], disabled: [{ type: Input }], skeleton: [{ type: Input }], labelTemplate: [{ type: Input }], textInputTemplate: [{ type: Input }], helperText: [{ type: Input }], invalidText: [{ type: Input }], invalid: [{ type: Input }], warn: [{ type: Input }], warnText: [{ type: Input }], ariaLabel: [{ type: Input }], fluid: [{ type: Input }], decorator: [{ type: Input }], hideLabel: [{ type: Input }], inline: [{ type: Input }], size: [{ type: Input }], enableCounter: [{ type: Input }], maxCount: [{ type: Input }], wrapper: [{ type: ViewChild, args: ["wrapper", { static: false }] }], labelClass: [{ type: HostBinding, args: ["class.cds--form-item"] }], textInputWrapper: [{ type: HostBinding, args: ["class.cds--text-input-wrapper"] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dC1pbnB1dC1sYWJlbC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaW5wdXQvdGV4dC1pbnB1dC1sYWJlbC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUlOLFNBQVMsRUFFVCxXQUFXLEVBQ1gsS0FBSyxFQUlMLFdBQVcsRUFDWCxTQUFTLEVBQ1QsTUFBTSxlQUFlLENBQUM7Ozs7QUFFdkI7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBaUlILE1BQU0sT0FBTyx1QkFBdUI7SUF5SG5DOztPQUVHO0lBQ0gsWUFBc0IsaUJBQW9DO1FBQXBDLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBbUI7UUF2RzFEOzs7VUFHRTtRQUNPLGlCQUFZLEdBQUcsaUJBQWlCLEdBQUcsdUJBQXVCLENBQUMsWUFBWSxFQUFFLENBQUM7UUFFbkY7O1dBRUc7UUFDTSxhQUFRLEdBQUcsS0FBSyxDQUFDO1FBQzFCOztXQUVHO1FBQ00sYUFBUSxHQUFHLEtBQUssQ0FBQztRQWdCMUI7O1dBRUc7UUFDTSxZQUFPLEdBQUcsS0FBSyxDQUFDO1FBQ3pCOztZQUVJO1FBQ0ssU0FBSSxHQUFHLEtBQUssQ0FBQztRQVV0Qjs7V0FFRztRQUNNLFVBQUssR0FBRyxLQUFLLENBQUM7UUFPdkI7OztXQUdHO1FBQ00sY0FBUyxHQUFHLEtBQUssQ0FBQztRQUUzQjs7V0FFRztRQUNNLFdBQU0sR0FBRyxLQUFLLENBQUM7UUFFeEI7OztXQUdHO1FBQ00sU0FBSSxHQUF1QixJQUFJLENBQUM7UUFFekM7OztXQUdHO1FBQ00sa0JBQWEsR0FBRyxLQUFLLENBQUM7UUFRL0IsMERBQTBEO1FBQzFELGNBQVMsR0FBRyxDQUFDLENBQUM7UUFLdUIsZUFBVSxHQUFHLElBQUksQ0FBQztRQUVULHFCQUFnQixHQUFHLElBQUksQ0FBQztRQUV0RSxzRUFBc0U7UUFDOUQsa0JBQWEsR0FBNEIsSUFBSSxDQUFDO1FBQ3RELG1GQUFt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Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQTRIVDtpQkFDRDt3R0FHMEQsZUFBZTtzQkFBeEUsV0FBVzt1QkFBQyx1Q0FBdUM7Z0JBSVEsVUFBVTtzQkFBckUsV0FBVzt1QkFBQyx5Q0FBeUM7Z0JBSUwsVUFBVTtzQkFBMUQsV0FBVzt1QkFBQyw4QkFBOEI7Z0JBSWdCLGtCQUFrQjtzQkFBNUUsV0FBVzt1QkFBQyx3Q0FBd0M7Z0JBVzVDLFlBQVk7c0JBQXBCLEtBQUs7Z0JBS0csUUFBUTtzQkFBaEIsS0FBSztnQkFJRyxRQUFRO3NCQUFoQixLQUFLO2dCQU1HLGFBQWE7c0JBQXJCLEtBQUs7Z0JBQ0csaUJBQWlCO3NCQUF6QixLQUFLO2dCQUlHLFVBQVU7c0JBQWxCLEtBQUs7Z0JBSUcsV0FBVztzQkFBbkIsS0FBSztnQkFJRyxPQUFPO3NCQUFmLEtBQUs7Z0JBSUcsSUFBSTtzQkFBWixLQUFLO2dCQUlHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBSUcsU0FBUztzQkFBakIsS0FBSztnQkFLRyxLQUFLO3NCQUFiLEtBQUs7Z0JBS0csU0FBUztzQkFBakIsS0FBSztnQkFNRyxTQUFTO3NCQUFqQixLQUFLO2dCQUtHLE1BQU07c0JBQWQsS0FBSztnQkFNRyxJQUFJO3NCQUFaLEtBQUs7Z0JBTUcsYUFBYTtzQkFBckIsS0FBSztnQkFNRyxRQUFRO3NCQUFoQixLQUFLO2dCQU1tQyxPQUFPO3NCQUEvQyxTQUFTO3VCQUFDLFNBQVMsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUU7Z0JBRUYsVUFBVTtzQkFBOUMsV0FBVzt1QkFBQyxzQkFBc0I7Z0JBRVcsZ0JBQWdCO3NCQUE3RCxXQUFXO3VCQUFDLCtCQUErQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG5cdEFmdGVyQ29udGVudEluaXQsXG5cdEFmdGVyVmlld0luaXQsXG5cdENoYW5nZURldGVjdG9yUmVmLFxuXHRDb21wb25lbnQsXG5cdEVsZW1lbnRSZWYsXG5cdEhvc3RCaW5kaW5nLFxuXHRJbnB1dCxcblx0T25DaGFuZ2VzLFxuXHRPbkRlc3Ryb3ksXG5cdFNpbXBsZUNoYW5nZXMsXG5cdFRlbXBsYXRlUmVmLFxuXHRWaWV3Q2hpbGRcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcblxuLyoqXG4gKiBHZXQgc3RhcnRlZCB3aXRoIGltcG9ydGluZyB0aGUgbW9kdWxlOlxuICpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGltcG9ydCB7IElucHV0TW9kdWxlIH0gZnJvbSAnY2FyYm9uLWNvbXBvbmVudHMtYW5ndWxhcic7XG4gKiBgYGBcbiAqXG4gKiBgYGBodG1sXG4gKiA8Y2RzLXRleHQtbGFiZWw+XG4gKiBcdExhYmVsXG4gKiBcdDxpbnB1dCBjZHNUZXh0IHR5cGU9XCJ0ZXh0XCIgY2xhc3M9XCJpbnB1dC1maWVsZFwiPlxuICogPC9jZHMtdGV4dC1sYWJlbD5cbiAqIGBgYFxuICpcbiAqIFtTZWUgZGVtb10oLi4vLi4vP3BhdGg9L3N0b3J5L2NvbXBvbmVudHMtaW5wdXQtLWJhc2ljKVxuICovXG5AQ29tcG9uZW50KHtcblx0c2VsZWN0b3I6IFwiY2RzLXRleHQtbGFiZWwsIGlibS10ZXh0LWxhYmVsXCIsXG5cdHRlbXBsYXRlOiBgXG5cdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cInNrZWxldG9uXCI+XG5cdFx0XHQ8c3BhbiBjbGFzcz1cImNkcy0tbGFiZWwgY2RzLS1za2VsZXRvblwiPjwvc3Bhbj5cblx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXRleHQtaW5wdXQgY2RzLS1za2VsZXRvblwiPjwvZGl2PlxuXHRcdDwvbmctY29udGFpbmVyPlxuXHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhc2tlbGV0b25cIj5cblx0XHRcdDwhLS0gbm9uLWlubGluZTogbGFiZWwtd3JhcHBlciBhYm92ZSBmaWVsZDsgaW5saW5lOiBsYWJlbCt2YWxpZGF0aW9uIHNpZGUtYnktc2lkZSAtLT5cblx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhaW5saW5lOyBlbHNlIGlubGluZUhlYWRlclwiPlxuXHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS10ZXh0LWlucHV0X19sYWJlbC13cmFwcGVyXCI+XG5cdFx0XHRcdFx0PGxhYmVsXG5cdFx0XHRcdFx0XHRbZm9yXT1cImxhYmVsSW5wdXRJRFwiXG5cdFx0XHRcdFx0XHRbYXR0ci5hcmlhLWxhYmVsXT1cImFyaWFMYWJlbFwiXG5cdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tbGFiZWxcIlxuXHRcdFx0XHRcdFx0W25nQ2xhc3NdPVwie1xuXHRcdFx0XHRcdFx0XHQnY2RzLS1sYWJlbC0tZGlzYWJsZWQnOiBkaXNhYmxlZCxcblx0XHRcdFx0XHRcdFx0J2Nkcy0tdmlzdWFsbHktaGlkZGVuJzogaGlkZUxhYmVsXG5cdFx0XHRcdFx0XHR9XCI+XG5cdFx0XHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJsYWJlbFRlbXBsYXRlOyBlbHNlIGxhYmVsQ29udGVudFwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImxhYmVsVGVtcGxhdGVcIj48L25nLXRlbXBsYXRlPlxuXHRcdFx0XHRcdFx0PG5nLXRlbXBsYXRlICNsYWJlbENvbnRlbnQ+XG5cdFx0XHRcdFx0XHRcdDxuZy1jb250ZW50PjwvbmctY29udGVudD5cblx0XHRcdFx0XHRcdDwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdFx0PC9sYWJlbD5cblx0XHRcdFx0XHQ8c3BhblxuXHRcdFx0XHRcdFx0Km5nSWY9XCJlbmFibGVDb3VudGVyICYmIG1heENvdW50XCJcblx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1sYWJlbFwiXG5cdFx0XHRcdFx0XHRbbmdDbGFzc109XCJ7J2Nkcy0tbGFiZWwtLWRpc2FibGVkJzogZGlzYWJsZWR9XCJcblx0XHRcdFx0XHRcdGFyaWEtaGlkZGVuPVwidHJ1ZVwiPlxuXHRcdFx0XHRcdFx0e3t0ZXh0Q291bnR9fS97e21heENvdW50fX1cblx0XHRcdFx0XHQ8L3NwYW4+XG5cdFx0XHRcdDwvZGl2PlxuXHRcdFx0PC9uZy1jb250YWluZXI+XG5cblx0XHRcdDxuZy10ZW1wbGF0ZSAjaW5saW5lSGVhZGVyPlxuXHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS10ZXh0LWlucHV0X19sYWJlbC1oZWxwZXItd3JhcHBlclwiPlxuXHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXRleHQtaW5wdXRfX2xhYmVsLXdyYXBwZXJcIj5cblx0XHRcdFx0XHRcdDxsYWJlbFxuXHRcdFx0XHRcdFx0XHRbZm9yXT1cImxhYmVsSW5wdXRJRFwiXG5cdFx0XHRcdFx0XHRcdFthdHRyLmFyaWEtbGFiZWxdPVwiYXJpYUxhYmVsXCJcblx0XHRcdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLWxhYmVsXCJcblx0XHRcdFx0XHRcdFx0W25nQ2xhc3NdPVwie1xuXHRcdFx0XHRcdFx0XHRcdCdjZHMtLWxhYmVsLS1kaXNhYmxlZCc6IGRpc2FibGVkLFxuXHRcdFx0XHRcdFx0XHRcdCdjZHMtLXZpc3VhbGx5LWhpZGRlbic6IGhpZGVMYWJlbCxcblx0XHRcdFx0XHRcdFx0XHQnY2RzLS1sYWJlbC0taW5saW5lJzogdHJ1ZSxcblx0XHRcdFx0XHRcdFx0XHQnY2RzLS1sYWJlbC0taW5saW5lLS1zbSc6IHNpemUgPT09ICdzbScsXG5cdFx0XHRcdFx0XHRcdFx0J2Nkcy0tbGFiZWwtLWlubGluZS0tbWQnOiBzaXplID09PSAnbWQnLFxuXHRcdFx0XHRcdFx0XHRcdCdjZHMtLWxhYmVsLS1pbmxpbmUtLWxnJzogc2l6ZSA9PT0gJ2xnJ1xuXHRcdFx0XHRcdFx0XHR9XCI+XG5cdFx0XHRcdFx0XHRcdDxuZy10ZW1wbGF0ZSAqbmdJZj1cImxhYmVsVGVtcGxhdGVcIiBbbmdUZW1wbGF0ZU91dGxldF09XCJsYWJlbFRlbXBsYXRlXCI+PC9uZy10ZW1wbGF0ZT5cblx0XHRcdFx0XHRcdDwvbGFiZWw+XG5cdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFmbHVpZFwiPlxuXHRcdFx0XHRcdFx0PG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJ2YWxpZGF0aW9uT3JIZWxwZXJcIj48L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0XHQ8L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHQ8L25nLXRlbXBsYXRlPlxuXG5cdFx0XHQ8ZGl2XG5cdFx0XHRcdGNsYXNzPVwiY2RzLS10ZXh0LWlucHV0X19maWVsZC1vdXRlci13cmFwcGVyXCJcblx0XHRcdFx0W25nQ2xhc3NdPVwieydjZHMtLXRleHQtaW5wdXRfX2ZpZWxkLW91dGVyLXdyYXBwZXItLWlubGluZSc6IGlubGluZX1cIj5cblx0XHRcdDxkaXZcblx0XHRcdFx0Y2xhc3M9XCJjZHMtLXRleHQtaW5wdXRfX2ZpZWxkLXdyYXBwZXJcIlxuXHRcdFx0XHRbbmdDbGFzc109XCJ7XG5cdFx0XHRcdFx0J2Nkcy0tdGV4dC1pbnB1dF9fZmllbGQtd3JhcHBlci0td2FybmluZyc6IHdhcm4sXG5cdFx0XHRcdFx0J2Nkcy0tdGV4dC1pbnB1dF9fZmllbGQtd3JhcHBlci0tZGVjb3JhdG9yJzogISFkZWNvcmF0b3Jcblx0XHRcdFx0fVwiXG5cdFx0XHRcdFx0W2F0dHIuZGF0YS1pbnZhbGlkXT1cIihpbnZhbGlkID8gdHJ1ZSA6IG51bGwpXCJcblx0XHRcdFx0XHQjd3JhcHBlcj5cblx0XHRcdFx0XHQ8c3ZnXG5cdFx0XHRcdFx0XHQqbmdJZj1cImludmFsaWQgJiYgIXdhcm5cIlxuXHRcdFx0XHRcdFx0Y2RzSWNvbj1cIndhcm5pbmctLWZpbGxlZFwiXG5cdFx0XHRcdFx0XHRzaXplPVwiMTZcIlxuXHRcdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXRleHQtaW5wdXRfX2ludmFsaWQtaWNvblwiPlxuXHRcdFx0XHRcdDwvc3ZnPlxuXHRcdFx0XHRcdDxzdmdcblx0XHRcdFx0XHRcdCpuZ0lmPVwiIWludmFsaWQgJiYgd2FyblwiXG5cdFx0XHRcdFx0XHRjZHNJY29uPVwid2FybmluZy0tYWx0LS1maWxsZWRcIlxuXHRcdFx0XHRcdFx0c2l6ZT1cIjE2XCJcblx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS10ZXh0LWlucHV0X19pbnZhbGlkLWljb24gY2RzLS10ZXh0LWlucHV0X19pbnZhbGlkLWljb24tLXdhcm5pbmdcIj5cblx0XHRcdFx0XHQ8L3N2Zz5cblx0XHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJ0ZXh0SW5wdXRUZW1wbGF0ZTsgZWxzZSB0ZXh0SW5wdXRDb250ZW50XCIgW25nVGVtcGxhdGVPdXRsZXRdPVwidGV4dElucHV0VGVtcGxhdGVcIj48L25nLXRlbXBsYXRlPlxuXHRcdFx0XHRcdDxuZy10ZW1wbGF0ZSAjdGV4dElucHV0Q29udGVudD5cblx0XHRcdFx0XHRcdDxuZy1jb250ZW50IHNlbGVjdD1cIltjZHNUZXh0XSxbaWJtVGV4dF0saW5wdXRbdHlwZT10ZXh0XSxkaXZcIj48L25nLWNvbnRlbnQ+XG5cdFx0XHRcdFx0PC9uZy10ZW1wbGF0ZT5cblx0XHRcdFx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwiZGVjb3JhdG9yXCI+XG5cdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS10ZXh0LWlucHV0X19maWVsZC1pbm5lci13cmFwcGVyLS1kZWNvcmF0b3JcIj5cblx0XHRcdFx0XHRcdFx0PG5nLXRlbXBsYXRlIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImRlY29yYXRvclwiPjwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHQ8L25nLWNvbnRhaW5lcj5cblxuXHRcdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCJmbHVpZFwiPlxuXHRcdFx0XHRcdFx0PGhyIGNsYXNzPVwiY2RzLS10ZXh0LWlucHV0X19kaXZpZGVyXCIgLz5cblx0XHRcdFx0XHRcdDxkaXYgKm5nSWY9XCJpbnZhbGlkXCIgY2xhc3M9XCJjZHMtLWZvcm0tcmVxdWlyZW1lbnRcIj5cblx0XHRcdFx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1RlbXBsYXRlKGludmFsaWRUZXh0KVwiPnt7aW52YWxpZFRleHR9fTwvbmctY29udGFpbmVyPlxuXHRcdFx0XHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJpc1RlbXBsYXRlKGludmFsaWRUZXh0KVwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImludmFsaWRUZXh0XCI+PC9uZy10ZW1wbGF0ZT5cblx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0PGRpdiAqbmdJZj1cIiFpbnZhbGlkICYmIHdhcm5cIiBjbGFzcz1cImNkcy0tZm9ybS1yZXF1aXJlbWVudFwiPlxuXHRcdFx0XHRcdFx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwiIWlzVGVtcGxhdGUod2FyblRleHQpXCI+e3t3YXJuVGV4dH19PC9uZy1jb250YWluZXI+XG5cdFx0XHRcdFx0XHRcdDxuZy10ZW1wbGF0ZSAqbmdJZj1cImlzVGVtcGxhdGUod2FyblRleHQpXCIgW25nVGVtcGxhdGVPdXRsZXRdPVwid2FyblRleHRcIj48L25nLXRlbXBsYXRlPlxuXHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0PC9uZy1jb250YWluZXI+XG5cdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwiIWZsdWlkICYmICFpbmxpbmVcIj5cblx0XHRcdFx0XHQ8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cInZhbGlkYXRpb25PckhlbHBlclwiPjwvbmctY29udGFpbmVyPlxuXHRcdFx0XHQ8L25nLWNvbnRhaW5lcj5cblx0XHRcdDwvZGl2PlxuXHRcdDwvbmctY29udGFpbmVyPlxuXG5cdFx0PG5nLXRlbXBsYXRlICN2YWxpZGF0aW9uT3JIZWxwZXI+XG5cdFx0XHQ8ZGl2XG5cdFx0XHRcdCpuZ0lmPVwiaGVscGVyVGV4dCAmJiAhaW52YWxpZCAmJiAhd2FyblwiXG5cdFx0XHRcdGNsYXNzPVwiY2RzLS1mb3JtX19oZWxwZXItdGV4dFwiXG5cdFx0XHRcdFtuZ0NsYXNzXT1cInsnY2RzLS1mb3JtX19oZWxwZXItdGV4dC0tZGlzYWJsZWQnOiBkaXNhYmxlZCwgJ2Nkcy0tZm9ybV9faGVscGVyLXRleHQtLWlubGluZSc6IGlubGluZX1cIj5cblx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1RlbXBsYXRlKGhlbHBlclRleHQpXCI+e3toZWxwZXJUZXh0fX08L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0PG5nLXRlbXBsYXRlICpuZ0lmPVwiaXNUZW1wbGF0ZShoZWxwZXJUZXh0KVwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImhlbHBlclRleHRcIj48L25nLXRlbXBsYXRlPlxuXHRcdFx0PC9kaXY+XG5cdFx0XHQ8ZGl2ICpuZ0lmPVwiaW52YWxpZFwiIGNsYXNzPVwiY2RzLS1mb3JtLXJlcXVpcmVtZW50XCI+XG5cdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhaXNUZW1wbGF0ZShpbnZhbGlkVGV4dClcIj57e2ludmFsaWRUZXh0fX08L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0PG5nLXRlbXBsYXRlICpuZ0lmPVwiaXNUZW1wbGF0ZShpbnZhbGlkVGV4dClcIiBbbmdUZW1wbGF0ZU91dGxldF09XCJpbnZhbGlkVGV4dFwiPjwvbmctdGVtcGxhdGU+XG5cdFx0XHQ8L2Rpdj5cblx0XHRcdDxkaXYgKm5nSWY9XCIhaW52YWxpZCAmJiB3YXJuXCIgY2xhc3M9XCJjZHMtLWZvcm0tcmVxdWlyZW1lbnRcIj5cblx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1RlbXBsYXRlKHdhcm5UZXh0KVwiPnt7d2FyblRleHR9fTwvbmctY29udGFpbmVyPlxuXHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJpc1RlbXBsYXRlKHdhcm5UZXh0KVwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIndhcm5UZXh0XCI+PC9uZy10ZW1wbGF0ZT5cblx0XHRcdDwvZGl2PlxuXHRcdDwvbmctdGVtcGxhdGU+XG5cdGBcbn0pXG5leHBvcnQgY2xhc3MgVGV4dElucHV0TGFiZWxDb21wb25lbnQgaW1wbGVtZW50cyBBZnRlclZpZXdJbml0LCBBZnRlckNvbnRlbnRJbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSB7XG5cblx0QEhvc3RCaW5kaW5nKFwiY2xhc3MuY2RzLS10ZXh0LWlucHV0LXdyYXBwZXItLWlubGluZVwiKSBnZXQgaXNJbmxpbmVXcmFwcGVyKCkge1xuXHRcdHJldHVybiB0aGlzLmlubGluZTtcblx0fVxuXG5cdEBIb3N0QmluZGluZyhcImNsYXNzLmNkcy0tdGV4dC1pbnB1dC13cmFwcGVyLS1yZWFkb25seVwiKSBnZXQgaXNSZWFkb25seSgpIHtcblx0XHRyZXR1cm4gdGhpcy53cmFwcGVyPy5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoXCJpbnB1dFwiKT8ucmVhZE9ubHkgPz8gZmFsc2U7XG5cdH1cblxuXHRASG9zdEJpbmRpbmcoXCJjbGFzcy5jZHMtLXRleHQtaW5wdXQtLWZsdWlkXCIpIGdldCBmbHVpZENsYXNzKCkge1xuXHRcdHJldHVybiB0aGlzLmZsdWlkICYmICF0aGlzLnNrZWxldG9uO1xuXHR9XG5cblx0QEhvc3RCaW5kaW5nKFwiY2xhc3MuY2RzLS10ZXh0LWlucHV0LS1mbHVpZF9fc2tlbGV0b25cIikgZ2V0IGZsdWlkU2tlbGV0b25DbGFzcygpIHtcblx0XHRyZXR1cm4gdGhpcy5mbHVpZCAmJiB0aGlzLnNrZWxldG9uO1xuXHR9XG5cdC8qKlxuXHQgKiBVc2VkIHRvIGJ1aWxkIHRoZSBpZCBvZiB0aGUgaW5wdXQgaXRlbSBhc3NvY2lhdGVkIHdpdGggdGhlIGBMYWJlbGAuXG5cdCAqL1xuXHRzdGF0aWMgbGFiZWxDb3VudGVyID0gMDtcblx0LyoqXG5cdCAqIFRoZSBpZCBvZiB0aGUgaW5wdXQgaXRlbSBhc3NvY2lhdGVkIHdpdGggdGhlIGBMYWJlbGAuIFRoaXMgdmFsdWUgaXMgYWxzbyB1c2VkIHRvIGFzc29jaWF0ZSB0aGUgYExhYmVsYCB3aXRoXG5cdCAqIGl0cyBpbnB1dCBjb3VudGVycGFydCB0aHJvdWdoIHRoZSAnZm9yJyBhdHRyaWJ1dGUuXG5cdCovXG5cdEBJbnB1dCgpIGxhYmVsSW5wdXRJRCA9IFwiaWJtLXRleHQtaW5wdXQtXCIgKyBUZXh0SW5wdXRMYWJlbENvbXBvbmVudC5sYWJlbENvdW50ZXIrKztcblxuXHQvKipcblx0ICogU2V0IHRvIGB0cnVlYCBmb3IgYSBkaXNhYmxlZCBsYWJlbC5cblx0ICovXG5cdEBJbnB1dCgpIGRpc2FibGVkID0gZmFsc2U7XG5cdC8qKlxuXHQgKiBTZXQgdG8gYHRydWVgIGZvciBhIGxvYWRpbmcgbGFiZWwuXG5cdCAqL1xuXHRASW5wdXQoKSBza2VsZXRvbiA9IGZhbHNlO1xuXG5cdC8qKlxuXHQgKiBIZWxwZXIgaW5wdXQgcHJvcGVydHkgZm9yIGVhc2Ugb2YgbWlncmF0aW9uXG5cdCAqIFNpbmNlIHdlIGNhbm5vdCBwYXNzIG5nLWNvbnRlbnQgZG93biBlYXNpbHkgZnJvbSBsYWJlbCBjb21wb25lbnQsIHdlIHdpbGwgYWNjZXB0IHRoZSB0ZW1wbGF0ZXNcblx0ICovXG5cdEBJbnB1dCgpIGxhYmVsVGVtcGxhdGU6IFRlbXBsYXRlUmVmPGFueT47XG5cdEBJbnB1dCgpIHRleHRJbnB1dFRlbXBsYXRlOiBUZW1wbGF0ZVJlZjxhbnk+O1xuXHQvKipcblx0ICogT3B0aW9uYWwgaGVscGVyIHRleHQgdGhhdCBhcHBlYXJzIHVuZGVyIHRoZSBsYWJlbC5cblx0ICovXG5cdEBJbnB1dCgpIGhlbHBlclRleHQ6IHN0cmluZyB8IFRlbXBsYXRlUmVmPGFueT47XG5cdC8qKlxuXHQgKiBTZXRzIHRoZSBpbnZhbGlkIHRleHQuXG5cdCAqL1xuXHRASW5wdXQoKSBpbnZhbGlkVGV4dDogc3RyaW5nIHwgVGVtcGxhdGVSZWY8YW55Pjtcblx0LyoqXG5cdCAqIFNldCB0byBgdHJ1ZWAgZm9yIGFuIGludmFsaWQgbGFiZWwgY29tcG9uZW50LlxuXHQgKi9cblx0QElucHV0KCkgaW52YWxpZCA9IGZhbHNlO1xuXHQvKipcblx0ICAqIFNldCB0byBgdHJ1ZWAgdG8gc2hvdyBhIHdhcm5pbmcgKGNvbnRlbnRzIHNldCBieSB3YXJuaW5nVGV4dClcblx0ICAqL1xuXHRASW5wdXQoKSB3YXJuID0gZmFsc2U7XG5cdC8qKlxuXHQgKiBTZXRzIHRoZSB3YXJuaW5nIHRleHRcblx0ICovXG5cdEBJbnB1dCgpIHdhcm5UZXh0OiBzdHJpbmcgfCBUZW1wbGF0ZVJlZjxhbnk+O1xuXHQvKipcblx0ICogU2V0IHRoZSBhcmlhbGFiZWwgZm9yIGxhYmVsXG5cdCAqL1xuXHRASW5wdXQoKSBhcmlhTGFiZWw6IHN0cmluZztcblxuXHQvKipcblx0ICogRXhwZXJpbWVudGFsOiBlbmFibGUgZmx1aWQgc3RhdGVcblx0ICovXG5cdEBJbnB1dCgpIGZsdWlkID0gZmFsc2U7XG5cblx0LyoqXG5cdCAqICoqRXhwZXJpbWVudGFsKio6IE9wdGlvbmFsIGRlY29yYXRvciAoZS5nLiBBSSBsYWJlbCkuXG5cdCAqL1xuXHRASW5wdXQoKSBkZWNvcmF0b3I6IFRlbXBsYXRlUmVmPGFueT47XG5cblx0LyoqXG5cdCAqIFNldCB0byBgdHJ1ZWAgdG8gaGlkZSB0aGUgbGFiZWwgdmlzdWFsbHksIGJ1dCBrZWVwIGFjY2Vzc2libGUgdG9cblx0ICogc2NyZWVuIHJlYWRlcnMuXG5cdCAqL1xuXHRASW5wdXQoKSBoaWRlTGFiZWwgPSBmYWxzZTtcblxuXHQvKipcblx0ICogU2V0IHRvIGB0cnVlYCB0byByZW5kZXIgdGhlIGxhYmVsIGFuZCBmaWVsZCBzaWRlLWJ5LXNpZGUgaW5zdGVhZCBvZiBzdGFja2VkLlxuXHQgKi9cblx0QElucHV0KCkgaW5saW5lID0gZmFsc2U7XG5cblx0LyoqXG5cdCAqIFRoZSByZW5kZXIgc2l6ZSBmb3IgdGhlIGBUZXh0SW5wdXRgLiBVc2VkIHRvIGNvbXB1dGUgdGhlIElOTElORSBsYWJlbCBzaXplXG5cdCAqIHZhcmlhbnQgY2xhc3MgKGBjZHMtLWxhYmVsLS1pbmxpbmUtLXtzaXplfWApLlxuXHQgKi9cblx0QElucHV0KCkgc2l6ZTogXCJzbVwiIHwgXCJtZFwiIHwgXCJsZ1wiID0gXCJtZFwiO1xuXG5cdC8qKlxuXHQgKiBTZXQgdG8gYHRydWVgIChgbWF4Q291bnRgIG11c3QgYmUgc2V0KSB0byBkaXNwbGF5cyBhIGxpdmUgY2hhcmFjdGVyXG5cdCAqIGNvdW50ZXIgYWxvbmdzaWRlIHRoZSBsYWJlbC5cblx0ICovXG5cdEBJbnB1dCgpIGVuYWJsZUNvdW50ZXIgPSBmYWxzZTtcblxuXHQvKipcblx0ICogTWF4aW11bSBudW1iZXIgb2YgY2hhcmFjdGVycyAob3Igd29yZHMpIGFsbG93ZWQuIFJlcXVpcmVkIGZvciB0aGVcblx0ICogY291bnRlciB0byBkaXNwbGF5LlxuXHQgKi9cblx0QElucHV0KCkgbWF4Q291bnQ6IG51bWJlcjtcblxuXHQvLyBUcmFja3MgY3VycmVudCBjaGFyYWN0ZXIgY291bnQgZm9yIHRoZSBjb3VudGVyIGRpc3BsYXkuXG5cdHRleHRDb3VudCA9IDA7XG5cblx0Ly8gQHRzLWlnbm9yZVxuXHRAVmlld0NoaWxkKFwid3JhcHBlclwiLCB7IHN0YXRpYzogZmFsc2UgfSkgd3JhcHBlcjogRWxlbWVudFJlZjxIVE1MRGl2RWxlbWVudD47XG5cblx0QEhvc3RCaW5kaW5nKFwiY2xhc3MuY2RzLS1mb3JtLWl0ZW1cIikgbGFiZWxDbGFzcyA9IHRydWU7XG5cblx0QEhvc3RCaW5kaW5nKFwiY2xhc3MuY2RzLS10ZXh0LWlucHV0LXdyYXBwZXJcIikgdGV4dElucHV0V3JhcHBlciA9IHRydWU7XG5cblx0Ly8gQ2FjaGVkIHJlZmVyZW5jZSB0byB0aGUgaW5wdXQgZWxlbWVudCwgc2V0IG9uY2UgaW4gbmdBZnRlclZpZXdJbml0LlxuXHRwcml2YXRlIF9pbnB1dEVsZW1lbnQ6IEhUTUxJbnB1dEVsZW1lbnQgfCBudWxsID0gbnVsbDtcblx0Ly8gQ2FjaGVkIGxpc3RlbmVyIHNvIGl0IGNhbiBiZSByZW1vdmVkIHByZWNpc2VseSAoYXZvaWRzIGFub255bW91cy1mdW5jdGlvbiBsZWFrKS5cblx0cHJpdmF0ZSBfaW5wdXRMaXN0ZW5lcjogKChlOiBFdmVudCkgPT4gdm9pZCkgfCBudWxsID0gbnVsbDtcblxuXHQvKipcblx0ICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBMYWJlbC5cblx0ICovXG5cdGNvbnN0cnVjdG9yKHByb3RlY3RlZCBjaGFuZ2VEZXRlY3RvclJlZjogQ2hhbmdlRGV0ZWN0b3JSZWYpIHt9XG5cblx0LyoqXG5cdCAqIFNldHMgdGhlIGlkIG9uIHRoZSBpbnB1dCBpdGVtIGFzc29jaWF0ZWQgd2l0aCB0aGUgYExhYmVsYCBhbmQgYXR0YWNoZXMgdGhlXG5cdCAqIGNvdW50ZXIgbGlzdGVuZXIgd2hlbiBgZW5hYmxlQ291bnRlcmAgaXMgYWxyZWFkeSBgdHJ1ZWAgb24gZmlyc3QgcmVuZGVyLlxuXHQgKi9cblx0bmdBZnRlclZpZXdJbml0KCkge1xuXHRcdGlmICh0aGlzLndyYXBwZXIpIHtcblx0XHRcdC8vIFByaW9yaXRpemUgc2V0dGluZyBpZCB0byBgaW5wdXRgIG92ZXIgZGl2XG5cdFx0XHRjb25zdCBpbnB1dEVsZW1lbnQgPSB0aGlzLndyYXBwZXIubmF0aXZlRWxlbWVudC5xdWVyeVNlbGVjdG9yKFwiaW5wdXRcIik7XG5cdFx0XHRpZiAoaW5wdXRFbGVtZW50KSB7XG5cdFx0XHRcdC8vIGF2b2lkIG92ZXJyaWRpbmcgaWRzIGFscmVhZHkgc2V0IGJ5IHRoZSB1c2VyLCByZXVzZSBpdCBpbnN0ZWFkXG5cdFx0XHRcdGlmIChpbnB1dEVsZW1lbnQuaWQpIHtcblx0XHRcdFx0XHR0aGlzLmxhYmVsSW5wdXRJRCA9IGlucHV0RWxlbWVudC5pZDtcblx0XHRcdFx0XHR0aGlzLmNoYW5nZURldGVjdG9yUmVmLmRldGVjdENoYW5nZXMoKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpbnB1dEVsZW1lbnQuc2V0QXR0cmlidXRlKFwiaWRcIiwgdGhpcy5sYWJlbElucHV0SUQpO1xuXG5cdFx0XHRcdHRoaXMuX2lucHV0RWxlbWVudCA9IGlucHV0RWxlbWVudDtcblxuXHRcdFx0XHRpZiAodGhpcy5lbmFibGVDb3VudGVyKSB7XG5cdFx0XHRcdFx0dGhpcy50ZXh0Q291bnQgPSBpbnB1dEVsZW1lbnQudmFsdWU/Lmxlbmd0aCB8fCAwO1xuXHRcdFx0XHRcdHRoaXMuX2F0dGFjaENvdW50ZXJMaXN0ZW5lcigpO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHRjb25zdCBkaXZFbGVtZW50ID0gdGhpcy53cmFwcGVyLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvcihcImRpdlwiKTtcblx0XHRcdGlmIChkaXZFbGVtZW50KSB7XG5cdFx0XHRcdGlmIChkaXZFbGVtZW50LmlkKSB7XG5cdFx0XHRcdFx0dGhpcy5sYWJlbElucHV0SUQgPSBkaXZFbGVtZW50LmlkO1xuXHRcdFx0XHRcdHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYuZGV0ZWN0Q2hhbmdlcygpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGRpdkVsZW1lbnQuc2V0QXR0cmlidXRlKFwiaWRcIiwgdGhpcy5sYWJlbElucHV0SUQpO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBBdHRhY2gvcmVtb3ZlIGxpc3RlbmVyIGFuZCBzZWVkIGB0ZXh0Q291bnRgIGZyb20gdGhlIHRleHRhcmVhJ3MgY3VycmVudCB2YWx1ZS5cblx0ICogQHBhcmFtIGNoYW5nZXNcblx0ICovXG5cdG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpIHtcblx0XHRpZiAoY2hhbmdlcy5lbmFibGVDb3VudGVyICYmICFjaGFuZ2VzLmVuYWJsZUNvdW50ZXIuZmlyc3RDaGFuZ2UpIHtcblx0XHRcdGlmIChjaGFuZ2VzLmVuYWJsZUNvdW50ZXIuY3VycmVudFZhbHVlKSB7XG5cdFx0XHRcdGlmICh0aGlzLl9pbnB1dEVsZW1lbnQpIHtcblx0XHRcdFx0XHR0aGlzLnRleHRDb3VudCA9IHRoaXMuX2lucHV0RWxlbWVudC52YWx1ZT8ubGVuZ3RoIHx8IDA7XG5cdFx0XHRcdFx0dGhpcy5fYXR0YWNoQ291bnRlckxpc3RlbmVyKCk7XG5cdFx0XHRcdFx0dGhpcy5jaGFuZ2VEZXRlY3RvclJlZi5kZXRlY3RDaGFuZ2VzKCk7XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuX2RldGFjaENvdW50ZXJMaXN0ZW5lcigpO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdG5nQWZ0ZXJDb250ZW50SW5pdCgpIHtcblx0XHR0aGlzLmNoYW5nZURldGVjdG9yUmVmLmRldGVjdENoYW5nZXMoKTtcblx0fVxuXG5cdG5nT25EZXN0cm95KCkge1xuXHRcdHRoaXMuX2RldGFjaENvdW50ZXJMaXN0ZW5lcigpO1xuXHR9XG5cblx0cHVibGljIGlzVGVtcGxhdGUodmFsdWUpIHtcblx0XHRyZXR1cm4gdmFsdWUgaW5zdGFuY2VvZiBUZW1wbGF0ZVJlZjtcblx0fVxuXG5cdC8qKlxuXHQgKiBBdHRhY2hlcyB0aGUgaW5wdXQgZXZlbnQgbGlzdGVuZXIsIGVuc3VyaW5nIGl0IGlzIG5ldmVyIGFkZGVkIHR3aWNlLlxuXHQgKi9cblx0cHJpdmF0ZSBfYXR0YWNoQ291bnRlckxpc3RlbmVyKCk6IHZvaWQge1xuXHRcdHRoaXMuX2RldGFjaENvdW50ZXJMaXN0ZW5lcigpO1xuXHRcdGlmICghdGhpcy5faW5wdXRFbGVtZW50KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdHRoaXMuX2lucHV0TGlzdGVuZXIgPSAoZTogRXZlbnQpID0+IHtcblx0XHRcdHRoaXMudGV4dENvdW50ID0gKGUudGFyZ2V0IGFzIEhUTUxJbnB1dEVsZW1lbnQpLnZhbHVlPy5sZW5ndGggfHwgMDtcblx0XHRcdHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYuZGV0ZWN0Q2hhbmdlcygpO1xuXHRcdH07XG5cdFx0dGhpcy5faW5wdXRFbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJpbnB1dFwiLCB0aGlzLl9pbnB1dExpc3RlbmVyKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBSZW1vdmVzIHRoZSBpbnB1dCBldmVudCBsaXN0ZW5lciBhbmQgY2xlYXJzIHRoZSBjYWNoZWQgcmVmZXJlbmNlLlxuXHQgKi9cblx0cHJpdmF0ZSBfZGV0YWNoQ291bnRlckxpc3RlbmVyKCk6IHZvaWQge1xuXHRcdGlmICh0aGlzLl9pbnB1dExpc3RlbmVyICYmIHRoaXMuX2lucHV0RWxlbWVudCkge1xuXHRcdFx0dGhpcy5faW5wdXRFbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJpbnB1dFwiLCB0aGlzLl9pbnB1dExpc3RlbmVyKTtcblx0XHRcdHRoaXMuX2lucHV0TGlzdGVuZXIgPSBudWxsO1xuXHRcdH1cblx0fVxufVxuIl19