UNPKG

carbon-components-angular

Version:
485 lines (483 loc) 46.2 kB
import { Component, Input, HostBinding, TemplateRef, ViewChild, ContentChild } from "@angular/core"; import { TextArea } from "./text-area.directive"; 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-textarea-label> * Label * <textarea cdsTextArea class="textarea-field"> * </cds-textarea-label> * ``` * * [See demo](../../?path=/story/components-input-text-area--basic) */ export class TextareaLabelComponent { /** * 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-textarea-" + TextareaLabelComponent.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` (`maxCount` must be set) to displays a live character/word * counter alongside the label. */ this.enableCounter = false; /** * Determines whether the counter counts characters or words. * When `"word"` and `maxCount` is set, input is clamped to `maxCount` words * on each change. Excess words are trimmed from the end of the value. */ this.counterMode = "character"; // Tracks current character / word count for the counter display. this.textCount = 0; this.labelClass = true; // Cached reference to the textarea element, set once in ngAfterViewInit. this._textareaElement = null; // Cached listener so it can be removed precisely (avoids anonymous-function leak) this._inputListener = null; } get isReadonly() { return this.wrapper?.nativeElement.querySelector("textarea")?.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 `textarea` over div const inputElement = this.wrapper.nativeElement.querySelector("textarea"); 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._textareaElement = inputElement; this._syncMaxLength(); if (this.enableCounter) { this.textCount = this._countValue(inputElement.value || ""); 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._textareaElement) { this.textCount = this._countValue(this._textareaElement.value || ""); this._attachCounterListener(); this.changeDetectorRef.detectChanges(); } } else { this._detachCounterListener(); } } if ((changes.maxCount || changes.counterMode) && !(changes.maxCount?.firstChange && changes.counterMode?.firstChange)) { this._syncMaxLength(); } } ngOnDestroy() { this._detachCounterListener(); } isTemplate(value) { return value instanceof TemplateRef; } /** * Keeps the textarea's `maxlength` attribute in sync with `maxCount`. This is only set * when counterMode is set to `character`. When counterMode is set to `word`, we enforce limit via JS. * If `maxCount` is unset or the mode is `"word"`, any previously applied * `maxlength` is removed so the textarea is unrestricted by the attribute. */ _syncMaxLength() { if (!this._textareaElement) { return; } if (this.counterMode === "character" && this.maxCount != null) { this._textareaElement.setAttribute("maxlength", String(this.maxCount)); } else { this._textareaElement.removeAttribute("maxlength"); } } /** * Attaches the input event listener, ensuring it is never added twice. */ _attachCounterListener() { this._detachCounterListener(); if (!this._textareaElement) { return; } this._inputListener = (e) => { const el = e.target; // Word-mode enforcement: clamp value to maxCount words on each input so // the textarea never holds more words than allowed. Character mode relies // on the native `maxlength` attribute set by the developer. if (this.counterMode === "word" && this.maxCount != null) { const clamped = this._truncateToWordLimit(el.value || "", this.maxCount); if (clamped !== el.value) { el.value = clamped; } } this.textCount = this._countValue(el.value || ""); }; this._textareaElement.addEventListener("input", this._inputListener); } /** * Truncates `value` so it contains at most `limit` Unicode words. * Whitespace between and around words is preserved up to the last allowed word; * any trailing content (partial word or space) beyond the limit is dropped. */ _truncateToWordLimit(value, limit) { let wordsSeen = 0; // Walk through the string capturing word boundaries const wordPattern = /\p{L}+/gu; let match; let cutIndex = value.length; while ((match = wordPattern.exec(value)) !== null) { wordsSeen++; if (wordsSeen === limit) { // Allow the string to continue up to (but not past) the end of this word cutIndex = match.index + match[0].length; break; } } return wordsSeen < limit ? value : value.slice(0, cutIndex); } /** * Removes the input event listener and clears the cached reference. */ _detachCounterListener() { if (this._inputListener && this._textareaElement) { this._textareaElement.removeEventListener("input", this._inputListener); this._inputListener = null; } } _countValue(value) { if (this.counterMode === "word") { return value.match(/\p{L}+/gu)?.length || 0; } return value.length; } } /** * Used to build the id of the input item associated with the `Label`. */ TextareaLabelComponent.labelCounter = 0; TextareaLabelComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: TextareaLabelComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); TextareaLabelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: TextareaLabelComponent, selector: "cds-textarea-label, ibm-textarea-label", inputs: { labelInputID: "labelInputID", disabled: "disabled", skeleton: "skeleton", labelTemplate: "labelTemplate", textAreaTemplate: "textAreaTemplate", helperText: "helperText", invalidText: "invalidText", invalid: "invalid", warn: "warn", warnText: "warnText", ariaLabel: "ariaLabel", fluid: "fluid", hideLabel: "hideLabel", enableCounter: "enableCounter", maxCount: "maxCount", counterMode: "counterMode", decorator: "decorator" }, host: { properties: { "class.cds--text-area__wrapper--readonly": "this.isReadonly", "class.cds--text-area--fluid": "this.fluidClass", "class.cds--text-area--fluid__skeleton": "this.fluidSkeletonClass", "class.cds--form-item": "this.labelClass" } }, queries: [{ propertyName: "textArea", first: true, predicate: TextArea, descendants: true }], 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-area cds--skeleton"></div> </ng-container> <ng-container *ngIf="!skeleton"> <div class="cds--text-area__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> <div class="cds--text-area__wrapper" [ngClass]="{ 'cds--text-area__wrapper--warn': warn, 'cds--text-area__wrapper--decorator': !!decorator }" [attr.data-invalid]="(invalid ? true : null)" #wrapper> <svg *ngIf="!fluid && invalid" cdsIcon="warning--filled" size="16" class="cds--text-area__invalid-icon"> </svg> <svg *ngIf="!fluid && !invalid && warn" cdsIcon="warning--alt--filled" size="16" class="cds--text-area__invalid-icon cds--text-area__invalid-icon--warning"> </svg> <ng-template *ngIf="textAreaTemplate; else textAreaContent" [ngTemplateOutlet]="textAreaTemplate"></ng-template> <ng-template #textAreaContent> <ng-content select="[cdsTextArea],[ibmTextArea],textarea"></ng-content> </ng-template> <ng-container *ngIf="decorator"> <div class="cds--text-area__inner-wrapper--decorator"> <ng-template [ngTemplateOutlet]="decorator"></ng-template> </div> </ng-container> <ng-container *ngIf="fluid"> <hr class="cds--text-area__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> <svg cdsIcon="warning--filled" size="16" class="cds--text-area__invalid-icon"> </svg> </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> <svg cdsIcon="warning--alt--filled" size="16" class="cds--text-area__invalid-icon cds--text-area__invalid-icon--warning"> </svg> </div> </ng-container> </div> <ng-container *ngIf="!fluid"> <div *ngIf="helperText && !invalid && !warn" class="cds--form__helper-text" [ngClass]="{'cds--form__helper-text--disabled': disabled}"> <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-container> </ng-container> `, 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: TextareaLabelComponent, decorators: [{ type: Component, args: [{ selector: "cds-textarea-label, ibm-textarea-label", template: ` <ng-container *ngIf="skeleton"> <span class="cds--label cds--skeleton"></span> <div class="cds--text-area cds--skeleton"></div> </ng-container> <ng-container *ngIf="!skeleton"> <div class="cds--text-area__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> <div class="cds--text-area__wrapper" [ngClass]="{ 'cds--text-area__wrapper--warn': warn, 'cds--text-area__wrapper--decorator': !!decorator }" [attr.data-invalid]="(invalid ? true : null)" #wrapper> <svg *ngIf="!fluid && invalid" cdsIcon="warning--filled" size="16" class="cds--text-area__invalid-icon"> </svg> <svg *ngIf="!fluid && !invalid && warn" cdsIcon="warning--alt--filled" size="16" class="cds--text-area__invalid-icon cds--text-area__invalid-icon--warning"> </svg> <ng-template *ngIf="textAreaTemplate; else textAreaContent" [ngTemplateOutlet]="textAreaTemplate"></ng-template> <ng-template #textAreaContent> <ng-content select="[cdsTextArea],[ibmTextArea],textarea"></ng-content> </ng-template> <ng-container *ngIf="decorator"> <div class="cds--text-area__inner-wrapper--decorator"> <ng-template [ngTemplateOutlet]="decorator"></ng-template> </div> </ng-container> <ng-container *ngIf="fluid"> <hr class="cds--text-area__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> <svg cdsIcon="warning--filled" size="16" class="cds--text-area__invalid-icon"> </svg> </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> <svg cdsIcon="warning--alt--filled" size="16" class="cds--text-area__invalid-icon cds--text-area__invalid-icon--warning"> </svg> </div> </ng-container> </div> <ng-container *ngIf="!fluid"> <div *ngIf="helperText && !invalid && !warn" class="cds--form__helper-text" [ngClass]="{'cds--form__helper-text--disabled': disabled}"> <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-container> </ng-container> ` }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { isReadonly: [{ type: HostBinding, args: ["class.cds--text-area__wrapper--readonly"] }], fluidClass: [{ type: HostBinding, args: ["class.cds--text-area--fluid"] }], fluidSkeletonClass: [{ type: HostBinding, args: ["class.cds--text-area--fluid__skeleton"] }], labelInputID: [{ type: Input }], disabled: [{ type: Input }], skeleton: [{ type: Input }], labelTemplate: [{ type: Input }], textAreaTemplate: [{ type: Input }], helperText: [{ type: Input }], invalidText: [{ type: Input }], invalid: [{ type: Input }], warn: [{ type: Input }], warnText: [{ type: Input }], ariaLabel: [{ type: Input }], fluid: [{ type: Input }], hideLabel: [{ type: Input }], enableCounter: [{ type: Input }], maxCount: [{ type: Input }], counterMode: [{ type: Input }], decorator: [{ type: Input }], wrapper: [{ type: ViewChild, args: ["wrapper", { static: false }] }], textArea: [{ type: ContentChild, args: [TextArea, { static: false }] }], labelClass: [{ type: HostBinding, args: ["class.cds--form-item"] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dGFyZWEtbGFiZWwuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2lucHV0L3RleHRhcmVhLWxhYmVsLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ04sU0FBUyxFQUNULEtBQUssRUFLTCxXQUFXLEVBQ1gsV0FBVyxFQUNYLFNBQVMsRUFDVCxZQUFZLEVBR1osTUFBTSxlQUFlLENBQUM7QUFFdkIsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLHVCQUF1QixDQUFDOzs7O0FBRWpEOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQXVHSCxNQUFNLE9BQU8sc0JBQXNCO0lBa0hsQzs7T0FFRztJQUNILFlBQXNCLGlCQUFvQztRQUFwQyxzQkFBaUIsR0FBakIsaUJBQWlCLENBQW1CO1FBcEcxRDs7O1VBR0U7UUFDTyxpQkFBWSxHQUFHLGVBQWUsR0FBRyxzQkFBc0IsQ0FBQyxZQUFZLENBQUM7UUFFOUU7O1dBRUc7UUFDTSxhQUFRLEdBQUcsS0FBSyxDQUFDO1FBQzFCOztXQUVHO1FBQ00sYUFBUSxHQUFHLEtBQUssQ0FBQztRQWdCMUI7O1dBRUc7UUFDTSxZQUFPLEdBQUcsS0FBSyxDQUFDO1FBQ3pCOztZQUVJO1FBQ0ssU0FBSSxHQUFHLEtBQUssQ0FBQztRQVV0Qjs7V0FFRztRQUNNLFVBQUssR0FBRyxLQUFLLENBQUM7UUFFdkI7OztXQUdHO1FBQ00sY0FBUyxHQUFHLEtBQUssQ0FBQztRQUUzQjs7O1dBR0c7UUFDTSxrQkFBYSxHQUFHLEtBQUssQ0FBQztRQVEvQjs7OztXQUlHO1FBQ00sZ0JBQVcsR0FBeUIsV0FBVyxDQUFDO1FBT3pELGtFQUFrRTtRQUNsRSxjQUFTLEdBQUcsQ0FBQyxDQUFDO1FBUXVCLGVBQVUsR0FBRyxJQUFJLENBQUM7UUFFdkQseUVBQXlFO1FBQ2pFLHFCQUFnQixHQUErQixJQUFJLENBQUM7UUFDNUQsa0ZBQWtGO1FBQzFFLG1CQUFjLEdBQWdD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RyxJQUFJO3NCQUFaLEtBQUs7Z0JBSUcsUUFBUTtzQkFBaEIsS0FBSztnQkFJRyxTQUFTO3NCQUFqQixLQUFLO2dCQUtHLEtBQUs7c0JBQWIsS0FBSztnQkFNRyxTQUFTO3NCQUFqQixLQUFLO2dCQU1HLGFBQWE7c0JBQXJCLEtBQUs7Z0JBTUcsUUFBUTtzQkFBaEIsS0FBSztnQkFPRyxXQUFXO3NCQUFuQixLQUFLO2dCQUtHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBTW1DLE9BQU87c0JBQS9DLFNBQVM7dUJBQUMsU0FBUyxFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRTtnQkFHSSxRQUFRO3NCQUFsRCxZQUFZO3VCQUFDLFFBQVEsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUU7Z0JBRUosVUFBVTtzQkFBOUMsV0FBVzt1QkFBQyxzQkFBc0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuXHRDb21wb25lbnQsXG5cdElucHV0LFxuXHRBZnRlclZpZXdJbml0LFxuXHRPbkNoYW5nZXMsXG5cdE9uRGVzdHJveSxcblx0RWxlbWVudFJlZixcblx0SG9zdEJpbmRpbmcsXG5cdFRlbXBsYXRlUmVmLFxuXHRWaWV3Q2hpbGQsXG5cdENvbnRlbnRDaGlsZCxcblx0Q2hhbmdlRGV0ZWN0b3JSZWYsXG5cdFNpbXBsZUNoYW5nZXNcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcblxuaW1wb3J0IHsgVGV4dEFyZWEgfSBmcm9tIFwiLi90ZXh0LWFyZWEuZGlyZWN0aXZlXCI7XG5cbi8qKlxuICogR2V0IHN0YXJ0ZWQgd2l0aCBpbXBvcnRpbmcgdGhlIG1vZHVsZTpcbiAqXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBJbnB1dE1vZHVsZSB9IGZyb20gJ2NhcmJvbi1jb21wb25lbnRzLWFuZ3VsYXInO1xuICogYGBgXG4gKlxuICogYGBgaHRtbFxuICogPGNkcy10ZXh0YXJlYS1sYWJlbD5cbiAqIFx0TGFiZWxcbiAqIFx0PHRleHRhcmVhIGNkc1RleHRBcmVhIGNsYXNzPVwidGV4dGFyZWEtZmllbGRcIj5cbiAqIDwvY2RzLXRleHRhcmVhLWxhYmVsPlxuICogYGBgXG4gKlxuICogW1NlZSBkZW1vXSguLi8uLi8/cGF0aD0vc3RvcnkvY29tcG9uZW50cy1pbnB1dC10ZXh0LWFyZWEtLWJhc2ljKVxuICovXG5AQ29tcG9uZW50KHtcblx0c2VsZWN0b3I6IFwiY2RzLXRleHRhcmVhLWxhYmVsLCBpYm0tdGV4dGFyZWEtbGFiZWxcIixcblx0dGVtcGxhdGU6IGBcblx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwic2tlbGV0b25cIj5cblx0XHRcdDxzcGFuIGNsYXNzPVwiY2RzLS1sYWJlbCBjZHMtLXNrZWxldG9uXCI+PC9zcGFuPlxuXHRcdFx0PGRpdiBjbGFzcz1cImNkcy0tdGV4dC1hcmVhIGNkcy0tc2tlbGV0b25cIj48L2Rpdj5cblx0XHQ8L25nLWNvbnRhaW5lcj5cblx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwiIXNrZWxldG9uXCI+XG5cdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS10ZXh0LWFyZWFfX2xhYmVsLXdyYXBwZXJcIj5cblx0XHRcdFx0PGxhYmVsXG5cdFx0XHRcdFx0W2Zvcl09XCJsYWJlbElucHV0SURcIlxuXHRcdFx0XHRcdFthdHRyLmFyaWEtbGFiZWxdPVwiYXJpYUxhYmVsXCJcblx0XHRcdFx0XHRjbGFzcz1cImNkcy0tbGFiZWxcIlxuXHRcdFx0XHRcdFtuZ0NsYXNzXT1cIntcblx0XHRcdFx0XHRcdCdjZHMtLWxhYmVsLS1kaXNhYmxlZCc6IGRpc2FibGVkLFxuXHRcdFx0XHRcdFx0J2Nkcy0tdmlzdWFsbHktaGlkZGVuJzogaGlkZUxhYmVsXG5cdFx0XHRcdFx0fVwiPlxuXHRcdFx0XHRcdDxuZy10ZW1wbGF0ZSAqbmdJZj1cImxhYmVsVGVtcGxhdGU7IGVsc2UgbGFiZWxDb250ZW50XCIgW25nVGVtcGxhdGVPdXRsZXRdPVwibGFiZWxUZW1wbGF0ZVwiPjwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdFx0PG5nLXRlbXBsYXRlICNsYWJlbENvbnRlbnQ+XG5cdFx0XHRcdFx0XHQ8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG5cdFx0XHRcdFx0PC9uZy10ZW1wbGF0ZT5cblx0XHRcdFx0PC9sYWJlbD5cblx0XHRcdFx0PHNwYW5cblx0XHRcdFx0XHQqbmdJZj1cImVuYWJsZUNvdW50ZXIgJiYgbWF4Q291bnRcIlxuXHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1sYWJlbFwiXG5cdFx0XHRcdFx0W25nQ2xhc3NdPVwieydjZHMtLWxhYmVsLS1kaXNhYmxlZCc6IGRpc2FibGVkfVwiXG5cdFx0XHRcdFx0YXJpYS1oaWRkZW49XCJ0cnVlXCI+XG5cdFx0XHRcdFx0e3t0ZXh0Q291bnR9fS97e21heENvdW50fX1cblx0XHRcdFx0PC9zcGFuPlxuXHRcdFx0PC9kaXY+XG5cdFx0XHQ8ZGl2XG5cdFx0XHRcdGNsYXNzPVwiY2RzLS10ZXh0LWFyZWFfX3dyYXBwZXJcIlxuXHRcdFx0XHRbbmdDbGFzc109XCJ7XG5cdFx0XHRcdFx0J2Nkcy0tdGV4dC1hcmVhX193cmFwcGVyLS13YXJuJzogd2Fybixcblx0XHRcdFx0XHQnY2RzLS10ZXh0LWFyZWFfX3dyYXBwZXItLWRlY29yYXRvcic6ICEhZGVjb3JhdG9yXG5cdFx0XHRcdH1cIlxuXHRcdFx0XHRbYXR0ci5kYXRhLWludmFsaWRdPVwiKGludmFsaWQgPyB0cnVlIDogbnVsbClcIlxuXHRcdFx0XHQjd3JhcHBlcj5cblx0XHRcdFx0PHN2Z1xuXHRcdFx0XHRcdCpuZ0lmPVwiIWZsdWlkICYmIGludmFsaWRcIlxuXHRcdFx0XHRcdGNkc0ljb249XCJ3YXJuaW5nLS1maWxsZWRcIlxuXHRcdFx0XHRcdHNpemU9XCIxNlwiXG5cdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXRleHQtYXJlYV9faW52YWxpZC1pY29uXCI+XG5cdFx0XHRcdDwvc3ZnPlxuXHRcdFx0XHQ8c3ZnXG5cdFx0XHRcdFx0Km5nSWY9XCIhZmx1aWQgJiYgIWludmFsaWQgJiYgd2FyblwiXG5cdFx0XHRcdFx0Y2RzSWNvbj1cIndhcm5pbmctLWFsdC0tZmlsbGVkXCJcblx0XHRcdFx0XHRzaXplPVwiMTZcIlxuXHRcdFx0XHRcdGNsYXNzPVwiY2RzLS10ZXh0LWFyZWFfX2ludmFsaWQtaWNvbiBjZHMtLXRleHQtYXJlYV9faW52YWxpZC1pY29uLS13YXJuaW5nXCI+XG5cdFx0XHRcdDwvc3ZnPlxuXHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJ0ZXh0QXJlYVRlbXBsYXRlOyBlbHNlIHRleHRBcmVhQ29udGVudFwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cInRleHRBcmVhVGVtcGxhdGVcIj48L25nLXRlbXBsYXRlPlxuXHRcdFx0XHQ8bmctdGVtcGxhdGUgI3RleHRBcmVhQ29udGVudD5cblx0XHRcdFx0XHQ8bmctY29udGVudCBzZWxlY3Q9XCJbY2RzVGV4dEFyZWFdLFtpYm1UZXh0QXJlYV0sdGV4dGFyZWFcIj48L25nLWNvbnRlbnQ+XG5cdFx0XHRcdDwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCJkZWNvcmF0b3JcIj5cblx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS10ZXh0LWFyZWFfX2lubmVyLXdyYXBwZXItLWRlY29yYXRvclwiPlxuXHRcdFx0XHRcdFx0PG5nLXRlbXBsYXRlIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImRlY29yYXRvclwiPjwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdDwvbmctY29udGFpbmVyPlxuXG5cdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCJmbHVpZFwiPlxuXHRcdFx0XHRcdDxociBjbGFzcz1cImNkcy0tdGV4dC1hcmVhX19kaXZpZGVyXCIgLz5cblx0XHRcdFx0XHQ8ZGl2ICpuZ0lmPVwiaW52YWxpZFwiIGNsYXNzPVwiY2RzLS1mb3JtLXJlcXVpcmVtZW50XCI+XG5cdFx0XHRcdFx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwiIWlzVGVtcGxhdGUoaW52YWxpZFRleHQpXCI+e3tpbnZhbGlkVGV4dH19PC9uZy1jb250YWluZXI+XG5cdFx0XHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJpc1RlbXBsYXRlKGludmFsaWRUZXh0KVwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImludmFsaWRUZXh0XCI+PC9uZy10ZW1wbGF0ZT5cblx0XHRcdFx0XHRcdDxzdmdcblx0XHRcdFx0XHRcdFx0Y2RzSWNvbj1cIndhcm5pbmctLWZpbGxlZFwiXG5cdFx0XHRcdFx0XHRcdHNpemU9XCIxNlwiXG5cdFx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS10ZXh0LWFyZWFfX2ludmFsaWQtaWNvblwiPlxuXHRcdFx0XHRcdFx0PC9zdmc+XG5cdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0PGRpdiAqbmdJZj1cIiFpbnZhbGlkICYmIHdhcm5cIiBjbGFzcz1cImNkcy0tZm9ybS1yZXF1aXJlbWVudFwiPlxuXHRcdFx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1RlbXBsYXRlKHdhcm5UZXh0KVwiPnt7d2FyblRleHR9fTwvbmctY29udGFpbmVyPlxuXHRcdFx0XHRcdFx0PG5nLXRlbXBsYXRlICpuZ0lmPVwiaXNUZW1wbGF0ZSh3YXJuVGV4dClcIiBbbmdUZW1wbGF0ZU91dGxldF09XCJ3YXJuVGV4dFwiPjwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdFx0XHQ8c3ZnXG5cdFx0XHRcdFx0XHRcdGNkc0ljb249XCJ3YXJuaW5nLS1hbHQtLWZpbGxlZFwiXG5cdFx0XHRcdFx0XHRcdHNpemU9XCIxNlwiXG5cdFx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS10ZXh0LWFyZWFfX2ludmFsaWQtaWNvbiBjZHMtLXRleHQtYXJlYV9faW52YWxpZC1pY29uLS13YXJuaW5nXCI+XG5cdFx0XHRcdFx0XHQ8L3N2Zz5cblx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0PC9uZy1jb250YWluZXI+XG5cdFx0XHQ8L2Rpdj5cblx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhZmx1aWRcIj5cblx0XHRcdFx0PGRpdlxuXHRcdFx0XHRcdCpuZ0lmPVwiaGVscGVyVGV4dCAmJiAhaW52YWxpZCAmJiAhd2FyblwiXG5cdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLWZvcm1fX2hlbHBlci10ZXh0XCJcblx0XHRcdFx0XHRbbmdDbGFzc109XCJ7J2Nkcy0tZm9ybV9faGVscGVyLXRleHQtLWRpc2FibGVkJzogZGlzYWJsZWR9XCI+XG5cdFx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1RlbXBsYXRlKGhlbHBlclRleHQpXCI+e3toZWxwZXJUZXh0fX08L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJpc1RlbXBsYXRlKGhlbHBlclRleHQpXCIgW25nVGVtcGxhdGVPdXRsZXRdPVwiaGVscGVyVGV4dFwiPjwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHQ8ZGl2ICpuZ0lmPVwiaW52YWxpZFwiIGNsYXNzPVwiY2RzLS1mb3JtLXJlcXVpcmVtZW50XCI+XG5cdFx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1RlbXBsYXRlKGludmFsaWRUZXh0KVwiPnt7aW52YWxpZFRleHR9fTwvbmctY29udGFpbmVyPlxuXHRcdFx0XHRcdDxuZy10ZW1wbGF0ZSAqbmdJZj1cImlzVGVtcGxhdGUoaW52YWxpZFRleHQpXCIgW25nVGVtcGxhdGVPdXRsZXRdPVwiaW52YWxpZFRleHRcIj48L25nLXRlbXBsYXRlPlxuXHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0PGRpdiAqbmdJZj1cIiFpbnZhbGlkICYmIHdhcm5cIiBjbGFzcz1cImNkcy0tZm9ybS1yZXF1aXJlbWVudFwiPlxuXHRcdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhaXNUZW1wbGF0ZSh3YXJuVGV4dClcIj57e3dhcm5UZXh0fX08L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJpc1RlbXBsYXRlKHdhcm5UZXh0KVwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIndhcm5UZXh0XCI+PC9uZy10ZW1wbGF0ZT5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHQ8L25nLWNvbnRhaW5lcj5cblx0XHQ8L25nLWNvbnRhaW5lcj5cblx0YFxufSlcbmV4cG9ydCBjbGFzcyBUZXh0YXJlYUxhYmVsQ29tcG9uZW50IGltcGxlbWVudHMgQWZ0ZXJWaWV3SW5pdCwgT25DaGFuZ2VzLCBPbkRlc3Ryb3kge1xuXG5cdEBIb3N0QmluZGluZyhcImNsYXNzLmNkcy0tdGV4dC1hcmVhX193cmFwcGVyLS1yZWFkb25seVwiKSBnZXQgaXNSZWFkb25seSgpIHtcblx0XHRyZXR1cm4gdGhpcy53cmFwcGVyPy5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoXCJ0ZXh0YXJlYVwiKT8ucmVhZE9ubHkgPz8gZmFsc2U7XG5cdH1cblxuXHRASG9zdEJpbmRpbmcoXCJjbGFzcy5jZHMtLXRleHQtYXJlYS0tZmx1aWRcIikgZ2V0IGZsdWlkQ2xhc3MoKSB7XG5cdFx0cmV0dXJuIHRoaXMuZmx1aWQgJiYgIXRoaXMuc2tlbGV0b247XG5cdH1cblxuXHRASG9zdEJpbmRpbmcoXCJjbGFzcy5jZHMtLXRleHQtYXJlYS0tZmx1aWRfX3NrZWxldG9uXCIpIGdldCBmbHVpZFNrZWxldG9uQ2xhc3MoKSB7XG5cdFx0cmV0dXJuIHRoaXMuZmx1aWQgJiYgdGhpcy5za2VsZXRvbjtcblx0fVxuXHQvKipcblx0ICogVXNlZCB0byBidWlsZCB0aGUgaWQgb2YgdGhlIGlucHV0IGl0ZW0gYXNzb2NpYXRlZCB3aXRoIHRoZSBgTGFiZWxgLlxuXHQgKi9cblx0c3RhdGljIGxhYmVsQ291bnRlciA9IDA7XG5cdC8qKlxuXHQgKiBUaGUgaWQgb2YgdGhlIGlucHV0IGl0ZW0gYXNzb2NpYXRlZCB3aXRoIHRoZSBgTGFiZWxgLiBUaGlzIHZhbHVlIGlzIGFsc28gdXNlZCB0byBhc3NvY2lhdGUgdGhlIGBMYWJlbGAgd2l0aFxuXHQgKiBpdHMgaW5wdXQgY291bnRlcnBhcnQgdGhyb3VnaCB0aGUgJ2ZvcicgYXR0cmlidXRlLlxuXHQqL1xuXHRASW5wdXQoKSBsYWJlbElucHV0SUQgPSBcImlibS10ZXh0YXJlYS1cIiArIFRleHRhcmVhTGFiZWxDb21wb25lbnQubGFiZWxDb3VudGVyO1xuXG5cdC8qKlxuXHQgKiBTZXQgdG8gYHRydWVgIGZvciBhIGRpc2FibGVkIGxhYmVsLlxuXHQgKi9cblx0QElucHV0KCkgZGlzYWJsZWQgPSBmYWxzZTtcblx0LyoqXG5cdCAqIFNldCB0byBgdHJ1ZWAgZm9yIGEgbG9hZGluZyBsYWJlbC5cblx0ICovXG5cdEBJbnB1dCgpIHNrZWxldG9uID0gZmFsc2U7XG5cblx0LyoqXG5cdCAqIEhlbHBlciBpbnB1dCBwcm9wZXJ0eSBmb3IgZWFzZSBvZiBtaWdyYXRpb25cblx0ICogU2luY2Ugd2UgY2Fubm90IHBhc3MgbmctY29udGVudCBkb3duIGVhc2lseSBmcm9tIGxhYmVsIGNvbXBvbmVudCwgd2Ugd2lsbCBhY2NlcHQgdGhlIHRlbXBsYXRlc1xuXHQgKi9cblx0QElucHV0KCkgbGFiZWxUZW1wbGF0ZTogVGVtcGxhdGVSZWY8YW55Pjtcblx0QElucHV0KCkgdGV4dEFyZWFUZW1wbGF0ZTogVGVtcGxhdGVSZWY8YW55Pjtcblx0LyoqXG5cdCAqIE9wdGlvbmFsIGhlbHBlciB0ZXh0IHRoYXQgYXBwZWFycyB1bmRlciB0aGUgbGFiZWwuXG5cdCAqL1xuXHRASW5wdXQoKSBoZWxwZXJUZXh0OiBzdHJpbmcgfCBUZW1wbGF0ZVJlZjxhbnk+O1xuXHQvKipcblx0ICogU2V0cyB0aGUgaW52YWxpZCB0ZXh0LlxuXHQgKi9cblx0QElucHV0KCkgaW52YWxpZFRleHQ6IHN0cmluZyB8IFRlbXBsYXRlUmVmPGFueT47XG5cdC8qKlxuXHQgKiBTZXQgdG8gYHRydWVgIGZvciBhbiBpbnZhbGlkIGxhYmVsIGNvbXBvbmVudC5cblx0ICovXG5cdEBJbnB1dCgpIGludmFsaWQgPSBmYWxzZTtcblx0LyoqXG5cdCAgKiBTZXQgdG8gYHRydWVgIHRvIHNob3cgYSB3YXJuaW5nIChjb250ZW50cyBzZXQgYnkgd2FybmluZ1RleHQpXG5cdCAgKi9cblx0QElucHV0KCkgd2FybiA9IGZhbHNlO1xuXHQvKipcblx0ICogU2V0cyB0aGUgd2FybmluZyB0ZXh0XG5cdCAqL1xuXHRASW5wdXQoKSB3YXJuVGV4dDogc3RyaW5nIHwgVGVtcGxhdGVSZWY8YW55Pjtcblx0LyoqXG5cdCAqIFNldCB0aGUgYXJpYWxhYmVsIGZvciBsYWJlbFxuXHQgKi9cblx0QElucHV0KCkgYXJpYUxhYmVsOiBzdHJpbmc7XG5cblx0LyoqXG5cdCAqIEV4cGVyaW1lbnRhbDogZW5hYmxlIGZsdWlkIHN0YXRlXG5cdCAqL1xuXHRASW5wdXQoKSBmbHVpZCA9IGZhbHNlO1xuXG5cdC8qKlxuXHQgKiBTZXQgdG8gYHRydWVgIHRvIGhpZGUgdGhlIGxhYmVsIHZpc3VhbGx5LCBidXQga2VlcCBhY2Nlc3NpYmxlIHRvXG5cdCAqIHNjcmVlbiByZWFkZXJzLlxuXHQgKi9cblx0QElucHV0KCkgaGlkZUxhYmVsID0gZmFsc2U7XG5cblx0LyoqXG5cdCAqIFNldCB0byBgdHJ1ZWAgKGBtYXhDb3VudGAgbXVzdCBiZSBzZXQpIHRvIGRpc3BsYXlzIGEgbGl2ZSBjaGFyYWN0ZXIvd29yZFxuXHQgKiBjb3VudGVyIGFsb25nc2lkZSB0aGUgbGFiZWwuXG5cdCAqL1xuXHRASW5wdXQoKSBlbmFibGVDb3VudGVyID0gZmFsc2U7XG5cblx0LyoqXG5cdCAqIE1heGltdW0gbnVtYmVyIG9mIGNoYXJhY3RlcnMgKG9yIHdvcmRzKSBhbGxvd2VkLiBSZXF1aXJlZCBmb3IgdGhlXG5cdCAqIGNvdW50ZXIgdG8gZGlzcGxheS5cblx0ICovXG5cdEBJbnB1dCgpIG1heENvdW50OiBudW1iZXI7XG5cblx0LyoqXG5cdCAqIERldGVybWluZXMgd2hldGhlciB0aGUgY291bnRlciBjb3VudHMgY2hhcmFjdGVycyBvciB3b3Jkcy5cblx0ICogV2hlbiBgXCJ3b3JkXCJgIGFuZCBgbWF4Q291bnRgIGlzIHNldCwgaW5wdXQgaXMgY2xhbXBlZCB0byBgbWF4Q291bnRgIHdvcmRzXG5cdCAqIG9uIGVhY2ggY2hhbmdlLiBFeGNlc3Mgd29yZHMgYXJlIHRyaW1tZWQgZnJvbSB0aGUgZW5kIG9mIHRoZSB2YWx1ZS5cblx0ICovXG5cdEBJbnB1dCgpIGNvdW50ZXJNb2RlOiBcImNoYXJhY3RlclwiIHwgXCJ3b3JkXCIgPSBcImNoYXJhY3RlclwiO1xuXG5cdC8qKlxuXHQgKiAqKkV4cGVyaW1lbnRhbCoqOiBPcHRpb25hbCBkZWNvcmF0b3IgKGUuZy4gQUkgbGFiZWwpLlxuXHQgKi9cblx0QElucHV0KCkgZGVjb3JhdG9yOiBUZW1wbGF0ZVJlZjxhbnk+O1xuXG5cdC8vICBUcmFja3MgY3VycmVudCBjaGFyYWN0ZXIgLyB3b3JkIGNvdW50IGZvciB0aGUgY291bnRlciBkaXNwbGF5LlxuXHR0ZXh0Q291bnQgPSAwO1xuXG5cdC8vIEB0cy1pZ25vcmVcblx0QFZpZXdDaGlsZChcIndyYXBwZXJcIiwgeyBzdGF0aWM6IGZhbHNlIH0pIHdyYXBwZXI6IEVsZW1lbnRSZWY8SFRNTERpdkVsZW1lbnQ+O1xuXG5cdC8vIEB0cy1pZ25vcmVcblx0QENvbnRlbnRDaGlsZChUZXh0QXJlYSwgeyBzdGF0aWM6IGZhbHNlIH0pIHRleHRBcmVhOiBUZXh0QXJlYTtcblxuXHRASG9zdEJpbmRpbmcoXCJjbGFzcy5jZHMtLWZvcm0taXRlbVwiKSBsYWJlbENsYXNzID0gdHJ1ZTtcblxuXHQvLyBDYWNoZWQgcmVmZXJlbmNlIHRvIHRoZSB0ZXh0YXJlYSBlbGVtZW50LCBzZXQgb25jZSBpbiBuZ0FmdGVyVmlld0luaXQuXG5cdHByaXZhdGUgX3RleHRhcmVhRWxlbWVudDogSFRNTFRleHRBcmVhRWxlbWVudCB8IG51bGwgPSBudWxsO1xuXHQvLyBDYWNoZWQgbGlzdGVuZXIgc28gaXQgY2FuIGJlIHJlbW92ZWQgcHJlY2lzZWx5IChhdm9pZHMgYW5vbnltb3VzLWZ1bmN0aW9uIGxlYWspXG5cdHByaXZhdGUgX2lucHV0TGlzdGVuZXI6ICgoZTogRXZlbnQpID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG5cblx0LyoqXG5cdCAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgTGFiZWwuXG5cdCAqL1xuXHRjb25zdHJ1Y3Rvcihwcm90ZWN0ZWQgY2hhbmdlRGV0ZWN0b3JSZWY6IENoYW5nZURldGVjdG9yUmVmKSB7fVxuXG5cdC8qKlxuXHQgKiBTZXRzIHRoZSBpZCBvbiB0aGUgaW5wdXQgaXRlbSBhc3NvY2lhdGVkIHdpdGggdGhlIGBMYWJlbGAgYW5kIGF0dGFjaGVzIHRoZVxuXHQgKiBjb3VudGVyIGxpc3RlbmVyIHdoZW4gYGVuYWJsZUNvdW50ZXJgIGlzIGFscmVhZHkgYHRydWVgIG9uIGZpcnN0IHJlbmRlci5cblx0ICovXG5cdG5nQWZ0ZXJWaWV3SW5pdCgpIHtcblx0XHRpZiAodGhpcy53cmFwcGVyKSB7XG5cdFx0XHQvLyBQcmlvcml0aXplIHNldHRpbmcgaWQgdG8gYHRleHRhcmVhYCBvdmVyIGRpdlxuXHRcdFx0Y29uc3QgaW5wdXRFbGVtZW50ID0gdGhpcy53cmFwcGVyLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvcihcInRleHRhcmVhXCIpO1xuXHRcdFx0aWYgKGlucHV0RWxlbWVudCkge1xuXHRcdFx0XHQvLyBhdm9pZCBvdmVycmlkaW5nIGlkcyBhbHJlYWR5IHNldCBieSB0aGUgdXNlciDigJQgcmV1c2UgaXQgaW5zdGVhZFxuXHRcdFx0XHRpZiAoaW5wdXRFbGVtZW50LmlkKSB7XG5cdFx0XHRcdFx0dGhpcy5sYWJlbElucHV0SUQgPSBpbnB1dEVsZW1lbnQuaWQ7XG5cdFx0XHRcdFx0dGhpcy5jaGFuZ2VEZXRlY3RvclJlZi5kZXRlY3RDaGFuZ2VzKCk7XG5cdFx0XHRcdH1cblx0XHRcdFx0aW5wdXRFbGVtZW50LnNldEF0dHJpYnV0ZShcImlkXCIsIHRoaXMubGFiZWxJbnB1dElEKTtcblxuXHRcdFx0XHR0aGlzLl90ZXh0YXJlYUVsZW1lbnQgPSBpbnB1dEVsZW1lbnQ7XG5cdFx0XHRcdHRoaXMuX3N5bmNNYXhMZW5ndGgoKTtcblxuXHRcdFx0XHRpZiAodGhpcy5lbmFibGVDb3VudGVyKSB7XG5cdFx0XHRcdFx0dGhpcy50ZXh0Q291bnQgPSB0aGlzLl9jb3VudFZhbHVlKGlucHV0RWxlbWVudC52YWx1ZSB8fCBcIlwiKTtcblx0XHRcdFx0XHR0aGlzLl9hdHRhY2hDb3VudGVyTGlzdGVuZXIoKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0Y29uc3QgZGl2RWxlbWVudCA9IHRoaXMud3JhcHBlci5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoXCJkaXZcIik7XG5cdFx0XHRpZiAoZGl2RWxlbWVudCkge1xuXHRcdFx0XHRpZiAoZGl2RWxlbWVudC5pZCkge1xuXHRcdFx0XHRcdHRoaXMubGFiZWxJbnB1dElEID0gZGl2RWxlbWVudC5pZDtcblx0XHRcdFx0XHR0aGlzLmNoYW5nZURldGVjdG9yUmVmLmRldGVjdENoYW5nZXMoKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRkaXZFbGVtZW50LnNldEF0dHJpYnV0ZShcImlkXCIsIHRoaXMubGFiZWxJbnB1dElEKTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogQXR0YWNoL3JlbW92ZSBsaXN0ZW5lciBhbmQgc2VlZCBgdGV4dENvdW50YCBmcm9tIHRoZSB0ZXh0YXJlYSdzIGN1cnJlbnQgdmFsdWUuXG5cdCAqIEBwYXJhbSBjaGFuZ2VzXG5cdCAqL1xuXHRuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKSB7XG5cdFx0aWYgKGNoYW5nZXMuZW5hYmxlQ291bnRlciAmJiAhY2hhbmdlcy5lbmFibGVDb3VudGVyLmZpcnN0Q2hhbmdlKSB7XG5cdFx0XHRpZiAoY2hhbmdlcy5lbmFibGVDb3VudGVyLmN1cnJlbnRWYWx1ZSkge1xuXHRcdFx0XHRpZiAodGhpcy5fdGV4dGFyZWFFbGVtZW50KSB7XG5cdFx0XHRcdFx0dGhpcy50ZXh0Q291bnQgPSB0aGlzLl9jb3VudFZhbHVlKHRoaXMuX3RleHRhcmVhRWxlbWVudC52YWx1ZSB8fCBcIlwiKTtcblx0XHRcdFx0XHR0aGlzLl9hdHRhY2hDb3VudGVyTGlzdGVuZXIoKTtcblx0XHRcdFx0XHR0aGlzLmNoYW5nZURldGVjdG9yUmVmLmRldGVjdENoYW5nZXMoKTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dGhpcy5fZGV0YWNoQ291bnRlckxpc3RlbmVyKCk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKFxuXHRcdFx0KGNoYW5nZXMubWF4Q291bnQgfHwgY2hhbmdlcy5jb3VudGVyTW9kZSkgJiZcblx0XHRcdCEoY2hhbmdlcy5tYXhDb3VudD8uZmlyc3RDaGFuZ2UgJiYgY2hhbmdlcy5jb3VudGVyTW9kZT8uZmlyc3RDaGFuZ2UpXG5cdFx0KSB7XG5cdFx0XHR0aGlzLl9zeW5jTWF4TGVuZ3RoKCk7XG5cdFx0fVxuXHR9XG5cblx0bmdPbkRlc3Ryb3koKSB7XG5cdFx0dGhpcy5fZGV0YWNoQ291bnRlckxpc3RlbmVyKCk7XG5cdH1cblxuXHRwdWJsaWMgaXNUZW1wbGF0ZSh2YWx1ZSkge1xuXHRcdHJldHVybiB2YWx1ZSBpbnN0YW5jZW9mIFRlbXBsYXRlUmVmO1xuXHR9XG5cblx0LyoqXG5cdCAqIEtlZXBzIHRoZSB0ZXh0YXJlYSdzIGBtYXhsZW5ndGhgIGF0dHJpYnV0ZSBpbiBzeW5jIHdpdGggYG1heENvdW50YC4gVGhpcyBpcyBvbmx5IHNldFxuXHQgKiB3aGVuIGNvdW50ZXJNb2RlIGlzIHNldCB0byBgY2hhcmFjdGVyYC4gV2hlbiBjb3VudGVyTW9kZSBpcyBzZXQgdG8gYHdvcmRgLCB3ZSBlbmZvcmNlIGxpbWl0IHZpYSBKUy5cblx0ICogSWYgYG1heENvdW50YCBpcyB1bnNldCBvciB0aGUgbW9kZSBpcyBgXCJ3b3JkXCJgLCBhbnkgcHJldmlvdXNseSBhcHBsaWVkXG5cdCAqIGBtYXhsZW5ndGhgIGlzIHJlbW92ZWQgc28gdGhlIHRleHRhcmVhIGlzIHVucmVzdHJpY3RlZCBieSB0aGUgYXR0cmlidXRlLlxuXHQgKi9cblx0cHJpdmF0ZSBfc3luY01heExlbmd0aCgpOiB2b2lkIHtcblx0XHRpZiAoIXRoaXMuX3RleHRhcmVhRWxlbWVudCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRpZiAodGhpcy5jb3VudGVyTW9kZSA9PT0gXCJjaGFyYWN0ZXJcIiAmJiB0aGlzLm1heENvdW50ICE9IG51bGwpIHtcblx0XHRcdHRoaXMuX3RleHRhcmVhRWxlbWVudC5zZXRBdHRyaWJ1dGUoXCJtYXhsZW5ndGhcIiwgU3RyaW5nKHRoaXMubWF4Q291bnQpKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fdGV4dGFyZWFFbGVtZW50LnJlbW92ZUF0dHJpYnV0ZShcIm1heGxlbmd0aFwiKTtcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogQXR0YWNoZXMgdGhlIGlucHV0IGV2ZW50IGxpc3RlbmVyLCBlbnN1cmluZyBpdCBpcyBuZXZlciBhZGRlZCB0d2ljZS5cblx0ICovXG5cdHByaXZhdGUgX2F0dGFjaENvdW50ZXJMaXN0ZW5lcigpOiB2b2lkIHtcblx0XHR0aGlzLl9kZXRhY2hDb3VudGVyTGlzdGVuZXIoKTtcblx0XHRpZiAoIXRoaXMuX3RleHRhcmVhRWxlbWVudCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHR0aGlzLl9pbnB1dExpc3RlbmVyID0gKGU6IEV2ZW50KSA9PiB7XG5cdFx0XHRjb25zdCBlbCA9IGUudGFyZ2V0IGFzIEhUTUxUZXh0QXJlYUVsZW1lbnQ7XG5cdFx0XHQvLyBXb3JkLW1vZGUgZW5mb3JjZW1lbnQ6IGNsYW1wIHZhbHVlIHRvIG1heENvdW50IHdvcmRzIG9uIGVhY2ggaW5wdXQgc29cblx0XHRcdC8vIHRoZSB0ZXh0YXJlYSBuZXZlciBob2xkcyBtb3JlIHdvcmRzIHRoYW4gYWxsb3dlZC4gIENoYXJhY3RlciBtb2RlIHJlbGllc1xuXHRcdFx0Ly8gb24gdGhlIG5hdGl2ZSBgbWF4bGVuZ3RoYCBhdHRyaWJ1dGUgc2V0IGJ5IHRoZSBkZXZlbG9wZXIuXG5cdFx0XHRpZiAodGhpcy5jb3VudGVyTW9kZSA9PT0gXCJ3b3JkXCIgJiYgdGhpcy5tYXhDb3VudCAhPSBudWxsKSB7XG5cdFx0XHRcdGNvbnN0IGNsYW1wZWQgPSB0aGlzLl90cnVuY2F0ZVRvV29yZExpbWl0KGVsLnZhbHVlIHx8IFwiXCIsIHRoaXMubWF4Q291bnQpO1xuXHRcdFx0XHRpZiAoY2xhbXBlZCAhPT0gZWwudmFsdWUpIHtcblx0XHRcdFx0XHRlbC52YWx1ZSA9IGNsYW1wZWQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdHRoaXMudGV4dENvdW50ID0gdGhpcy5fY291bnRWYWx1ZShlbC52YWx1ZSB8fCBcIlwiKTtcblx0XHR9O1xuXHRcdHRoaXMuX3RleHRhcmVhRWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFwiaW5wdXRcIiwgdGhpcy5faW5wdXRMaXN0ZW5lcik7XG5cdH1cblxuXHQvKipcblx0ICogVHJ1bmNhdGVzIGB2YWx1ZWAgc28gaXQgY29udGFpbnMgYXQgbW9zdCBgbGltaXRgIFVuaWNvZGUgd29yZHMuXG5cdCAqIFdoaXRlc3BhY2UgYmV0d2VlbiBhbmQgYXJvdW5kIHdvcmRzIGlzIHByZXNlcnZlZCB1cCB0byB0aGUgbGFzdCBhbGxvd2VkIHdvcmQ7XG5cdCAqIGFueSB0cmFpbGluZyBjb250ZW50IChwYXJ0aWFsIHdvcmQgb3Igc3BhY2UpIGJleW9uZCB0aGUgbGltaXQgaXMgZHJvcHBlZC5cblx0ICovXG5cdHByaXZhdGUgX3RydW5jYXRlVG9Xb3JkTGltaXQodmFsdWU6IHN0cmluZywgbGltaXQ6IG51bWJlcik6IHN0cmluZyB7XG5cdFx0bGV0IHdvcmRzU2VlbiA9IDA7XG5cdFx0Ly8gV2FsayB0aHJvdWdoIHRoZSBzdHJpbmcgY2FwdHVyaW5nIHdvcmQgYm91bmRhcmllc1xuXHRcdGNvbnN0IHdvcmRQYXR0ZXJuID0gL1xccHtMfSsvZ3U7XG5cdFx0bGV0IG1hdGNoOiBSZWdFeHBFeGVjQXJyYXkgfCBudWxsO1xuXHRcdGxldCBjdXRJbmRleCA9IHZhbHVlLmxlbmd0aDtcblx0XHR3aGlsZSAoKG1hdGNoID0gd29yZFBhdHRlcm4uZXhlYyh2YWx1ZSkpICE9PSBudWxsKSB7XG5cdFx0XHR3b3Jkc1NlZW4rKztcblx0XHRcdGlmICh3b3Jkc1NlZW4gPT09IGxpbWl0KSB7XG5cdFx0XHRcdC8vIEFsbG93IHRoZSBzdHJpbmcgdG8gY29udGludWUgdXAgdG8gKGJ1dCBub3QgcGFzdCkgdGhlIGVuZCBvZiB0aGlzIHdvcmRcblx0XHRcdFx0Y3V0SW5kZXggPSBtYXRjaC5pbmRleCArIG1hdGNoWzBdLmxlbmd0aDtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiB3b3Jkc1NlZW4gPCBsaW1pdCA/IHZhbHVlIDogdmFsdWUuc2xpY2UoMCwgY3V0SW5kZXgpO1xuXHR9XG5cblxuXHQvKipcblx0ICogUmVtb3ZlcyB0aGUgaW5wdXQgZXZlbnQgbGlzdGVuZXIgYW5kIGNsZWFycyB0aGUgY2FjaGVkIHJlZmVyZW5jZS5cblx0ICovXG5cdHByaXZhdGUgX2RldGFjaENvdW50ZXJMaXN0ZW5lcigpOiB2b2lkIHtcblx0XHRpZiAodGhpcy5faW5wdXRMaXN0ZW5lciAmJiB0aGlzLl90ZXh0YXJlYUVsZW1lbnQpIHtcblx0XHRcdHRoaXMuX3RleHRhcmVhRWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKFwiaW5wdXRcIiwgdGhpcy5faW5wdXRMaXN0ZW5lcik7XG5cdFx0XHR0aGlzLl9pbnB1dExpc3RlbmVyID0gbnVsbDtcblx0XHR9XG5cdH1cblxuXHRwcml2YXRlIF9jb3VudFZhbHVlKHZhbHVlOiBzdHJpbmcpOiBudW1iZXIge1xuXHRcdGlmICh0aGlzLmNvdW50ZXJNb2RlID09PSBcIndvcmRcIikge1xuXHRcdFx0cmV0dXJuIHZhbHVlLm1hdGNoKC9cXHB7TH0rL2d1KT8ubGVuZ3RoIHx8IDA7XG5cdFx0fVxuXHRcdHJldHVybiB2YWx1ZS5sZW5ndGg7XG5cdH1cbn1cbiJdfQ==