carbon-components-angular
Version:
Next generation components
471 lines (469 loc) • 45 kB
JavaScript
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" }, 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
}"
[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="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
}"
[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="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
}], 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGV4dGFyZWEtbGFiZWwuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2lucHV0L3RleHRhcmVhLWxhYmVsLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ04sU0FBUyxFQUNULEtBQUssRUFLTCxXQUFXLEVBQ1gsV0FBVyxFQUNYLFNBQVMsRUFDVCxZQUFZLEVBR1osTUFBTSxlQUFlLENBQUM7QUFFdkIsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLHVCQUF1QixDQUFDOzs7O0FBRWpEOzs7Ozs7Ozs7Ozs7Ozs7R0FlRztBQWlHSCxNQUFNLE9BQU8sc0JBQXNCO0lBNkdsQzs7T0FFRztJQUNILFlBQXNCLGlCQUFvQztRQUFwQyxzQkFBaUIsR0FBakIsaUJBQWlCLENBQW1CO1FBL0YxRDs7O1VBR0U7UUFDTyxpQkFBWSxHQUFHLGVBQWUsR0FBRyxzQkFBc0IsQ0FBQyxZQUFZLENBQUM7UUFFOUU7O1dBRUc7UUFDTSxhQUFRLEdBQUcsS0FBSyxDQUFDO1FBQzFCOztXQUVHO1FBQ00sYUFBUSxHQUFHLEtBQUssQ0FBQztRQWdCMUI7O1dBRUc7UUFDTSxZQUFPLEdBQUcsS0FBSyxDQUFDO1FBQ3pCOztZQUVJO1FBQ0ssU0FBSSxHQUFHLEtBQUssQ0FBQztRQVV0Qjs7V0FFRztRQUNNLFVBQUssR0FBRyxLQUFLLENBQUM7UUFFdkI7OztXQUdHO1FBQ00sY0FBUyxHQUFHLEtBQUssQ0FBQztRQUUzQjs7O1dBR0c7UUFDTSxrQkFBYSxHQUFHLEtBQUssQ0FBQztRQVEvQjs7OztXQUlHO1FBQ00sZ0JBQVcsR0FBeUIsV0FBVyxDQUFDO1FBRXpELGtFQUFrRTtRQUNsRSxjQUFTLEdBQUcsQ0FBQyxDQUFDO1FBUXVCLGVBQVUsR0FBRyxJQUFJLENBQUM7UUFFdkQseUVBQXlFO1FBQ2pFLHFCQUFnQixHQUErQixJQUFJLENBQUM7UUFDNUQsa0ZBQWtGO1FBQzFFLG1CQUFjLEdBQWdDLElBQUksQ0FBQztJQUtFLENBQUM7SUE5RzlELElBQTRELFVBQVU7UUFDckUsT0FBTyxJQUFJLENBQUMsT0FBTyxFQUFFLGFBQWEsQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLEVBQUUsUUFBUSxJQUFJLEtBQUssQ0FBQztJQUNqRixDQUFDO0lBRUQsSUFBZ0QsVUFBVTtRQUN6RCxPQUFPLElBQUksQ0FBQyxLQUFLLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3JDLENBQUM7SUFFRCxJQUEwRCxrQkFBa0I7UUFDM0UsT0FBTyxJQUFJLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUM7SUFDcEMsQ0FBQztJQXNHRDs7O09BR0c7SUFDSCxlQUFlO1FBQ2QsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFO1lBQ2pCLCtDQUErQztZQUMvQyxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDMUUsSUFBSSxZQUFZLEVBQUU7Z0JBQ2pCLGtFQUFrRTtnQkFDbEUsSUFBSSxZQUFZLENBQUMsRUFBRSxFQUFFO29CQUNwQixJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQyxFQUFFLENBQUM7b0JBQ3BDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxhQUFhLEVBQUUsQ0FBQztpQkFDdkM7Z0JBQ0QsWUFBWSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO2dCQUVuRCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsWUFBWSxDQUFDO2dCQUNyQyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7Z0JBRXRCLElBQUksSUFBSSxDQUFDLGFBQWEsRUFBRTtvQkFDdkIsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDLENBQUM7b0JBQzVELElBQUksQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO2lCQUM5QjtnQkFFRCxPQUFPO2FBQ1A7WUFFRCxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDbkUsSUFBSSxVQUFVLEVBQUU7Z0JBQ2YsSUFBSSxVQUFVLENBQUMsRUFBRSxFQUFFO29CQUNsQixJQUFJLENBQUMsWUFBWSxHQUFHLFVBQVUsQ0FBQyxFQUFFLENBQUM7b0JBQ2xDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxhQUFhLEVBQUUsQ0FBQztpQkFDdkM7Z0JBQ0QsVUFBVSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO2FBQ2pEO1NBQ0Q7SUFDRixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsV0FBVyxDQUFDLE9BQXNCO1FBQ2pDLElBQUksT0FBTyxDQUFDLGFBQWEsSUFBSSxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsV0FBVyxFQUFFO1lBQ2hFLElBQUksT0FBTyxDQUFDLGFBQWEsQ0FBQyxZQUFZLEVBQUU7Z0JBQ3ZDLElBQUksSUFBSSxDQUFDLGdCQUFnQixFQUFFO29CQUMxQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFDckUsSUFBSSxDQUFDLHNCQUFzQixFQUFFLENBQUM7b0JBQzlCLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxhQUFhLEVBQUUsQ0FBQztpQkFDdkM7YUFDRDtpQkFBTTtnQkFDTixJQUFJLENBQUMsc0JBQXNCLEVBQUUsQ0FBQzthQUM5QjtTQUNEO1FBRUQsSUFDQyxDQUFDLE9BQU8sQ0FBQyxRQUFRLElBQUksT0FBTyxDQUFDLFdBQVcsQ0FBQztZQUN6QyxDQUFDLENBQUMsT0FBTyxDQUFDLFFBQVEsRUFBRSxXQUFXLElBQUksT0FBTyxDQUFDLFdBQVcsRUFBRSxXQUFXLENBQUMsRUFDbkU7WUFDRCxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7U0FDdEI7SUFDRixDQUFDO0lBRUQsV0FBVztRQUNWLElBQUksQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO0lBQy9CLENBQUM7SUFFTSxVQUFVLENBQUMsS0FBSztRQUN0QixPQUFPLEtBQUssWUFBWSxXQUFXLENBQUM7SUFDckMsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ssY0FBYztRQUNyQixJQUFJLENBQUMsSUFBSSxDQUFDLGdCQUFnQixFQUFFO1lBQzNCLE9BQU87U0FDUDtRQUNELElBQUksSUFBSSxDQUFDLFdBQVcsS0FBSyxXQUFXLElBQUksSUFBSSxDQUFDLFFBQVEsSUFBSSxJQUFJLEVBQUU7WUFDOUQsSUFBSSxDQUFDLGdCQUFnQixDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1NBQ3ZFO2FBQU07WUFDTixJQUFJLENBQUMsZ0JBQWdCLENBQUMsZUFBZSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1NBQ25EO0lBQ0YsQ0FBQztJQUVEOztPQUVHO0lBQ0ssc0JBQXNCO1FBQzdCLElBQUksQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO1FBQzlCLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUU7WUFDM0IsT0FBTztTQUNQO1FBQ0QsSUFBSSxDQUFDLGNBQWMsR0FBRyxDQUFDLENBQVEsRUFBRSxFQUFFO1lBQ2xDLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQyxNQUE2QixDQUFDO1lBQzNDLHdFQUF3RTtZQUN4RSwyRUFBMkU7WUFDM0UsNERBQTREO1lBQzVELElBQUksSUFBSSxDQUFDLFdBQVcsS0FBSyxNQUFNLElBQUksSUFBSSxDQUFDLFFBQVEsSUFBSSxJQUFJLEVBQUU7Z0JBQ3pELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLENBQUMsS0FBSyxJQUFJLEVBQUUsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQ3pFLElBQUksT0FBTyxLQUFLLEVBQUUsQ0FBQyxLQUFLLEVBQUU7b0JBQ3pCLEVBQUUsQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDO2lCQUNuQjthQUNEO1lBQ0QsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDLENBQUM7UUFDbkQsQ0FBQyxDQUFDO1FBQ0YsSUFBSSxDQUFDLGdCQUFnQixDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7SUFDdEUsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxvQkFBb0IsQ0FBQyxLQUFhLEVBQUUsS0FBYTtRQUN4RCxJQUFJLFNBQVMsR0FBRyxDQUFDLENBQUM7UUFDbEIsb0RBQW9EO1FBQ3BELE1BQU0sV0FBVyxHQUFHLFVBQVUsQ0FBQztRQUMvQixJQUFJLEtBQTZCLENBQUM7UUFDbEMsSUFBSSxRQUFRLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQztRQUM1QixPQUFPLENBQUMsS0FBSyxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxJQUFJLEVBQUU7WUFDbEQsU0FBUyxFQUFFLENBQUM7WUFDWixJQUFJLFNBQVMsS0FBSyxLQUFLLEVBQUU7Z0JBQ3hCLHlFQUF5RTtnQkFDekUsUUFBUSxHQUFHLEtBQUssQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztnQkFDekMsTUFBTTthQUNOO1NBQ0Q7UUFDRCxPQUFPLFNBQVMsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDN0QsQ0FBQztJQUdEOztPQUVHO0lBQ0ssc0JBQXNCO1FBQzdCLElBQUksSUFBSSxDQUFDLGNBQWMsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLEVBQUU7WUFDakQsSUFBSSxDQUFDLGdCQUFnQixDQUFDLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7WUFDeEUsSUFBSSxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUM7U0FDM0I7SUFDRixDQUFDO0lBRU8sV0FBVyxDQUFDLEtBQWE7UUFDaEMsSUFBSSxJQUFJLENBQUMsV0FBVyxLQUFLLE1BQU0sRUFBRTtZQUNoQyxPQUFPLEtBQUssQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLEVBQUUsTUFBTSxJQUFJLENBQUMsQ0FBQztTQUM1QztRQUNELE9BQU8sS0FBSyxDQUFDLE1BQU0sQ0FBQztJQUNyQixDQUFDOztBQTNQRDs7R0FFRztBQUNJLG1DQUFZLEdBQUcsQ0FBQyxDQUFDO21IQWhCWixzQkFBc0I7dUdBQXRCLHNCQUFzQix5d0JBb0dwQixRQUFRLHlLQWxNWjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUE0RlQ7MkZBRVcsc0JBQXNCO2tCQWhHbEMsU0FBUzttQkFBQztvQkFDVixRQUFRLEVBQUUsd0NBQXdDO29CQUNsRCxRQUFRLEVBQUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0VBNEZUO2lCQUNEO3dHQUc0RCxVQUFVO3NCQUFyRSxXQUFXO3VCQUFDLHlDQUF5QztnQkFJTixVQUFVO3NCQUF6RCxXQUFXO3VCQUFDLDZCQUE2QjtnQkFJZ0Isa0JBQWtCO3NCQUEzRSxXQUFXO3VCQUFDLHVDQUF1QztnQkFXM0MsWUFBWTtzQkFBcEIsS0FBSztnQkFLRyxRQUFRO3NCQUFoQixLQUFLO2dCQUlHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBTUcsYUFBYTtzQkFBckIsS0FBSztnQkFDRyxnQkFBZ0I7c0JBQXhCLEtBQUs7Z0JBSUcsVUFBVTtzQkFBbEIsS0FBSztnQkFJRyxXQUFXO3NCQUFuQixLQUFLO2dCQUlHLE9BQU87c0JBQWYsS0FBSztnQkFJRyxJQUFJO3NCQUFaLEtBQUs7Z0JBSUcsUUFBUTtzQkFBaEIsS0FBSztnQkFJRyxTQUFTO3NCQUFqQixLQUFLO2dCQUtHLEtBQUs7c0JBQWIsS0FBSztnQkFNRyxTQUFTO3NCQUFqQixLQUFLO2dCQU1HLGFBQWE7c0JBQXJCLEtBQUs7Z0JBTUcsUUFBUTtzQkFBaEIsS0FBSztnQkFPRyxXQUFXO3NCQUFuQixLQUFLO2dCQU1tQyxPQUFPO3NCQUEvQyxTQUFTO3VCQUFDLFNBQVMsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUU7Z0JBR0ksUUFBUTtzQkFBbEQsWUFBWTt1QkFBQyxRQUFRLEVBQUUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFO2dCQUVKLFVBQVU7c0JBQTlDLFdBQVc7dUJBQUMsc0JBQXNCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcblx0Q29tcG9uZW50LFxuXHRJbnB1dCxcblx0QWZ0ZXJWaWV3SW5pdCxcblx0T25DaGFuZ2VzLFxuXHRPbkRlc3Ryb3ksXG5cdEVsZW1lbnRSZWYsXG5cdEhvc3RCaW5kaW5nLFxuXHRUZW1wbGF0ZVJlZixcblx0Vmlld0NoaWxkLFxuXHRDb250ZW50Q2hpbGQsXG5cdENoYW5nZURldGVjdG9yUmVmLFxuXHRTaW1wbGVDaGFuZ2VzXG59IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5cbmltcG9ydCB7IFRleHRBcmVhIH0gZnJvbSBcIi4vdGV4dC1hcmVhLmRpcmVjdGl2ZVwiO1xuXG4vKipcbiAqIEdldCBzdGFydGVkIHdpdGggaW1wb3J0aW5nIHRoZSBtb2R1bGU6XG4gKlxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgSW5wdXRNb2R1bGUgfSBmcm9tICdjYXJib24tY29tcG9uZW50cy1hbmd1bGFyJztcbiAqIGBgYFxuICpcbiAqIGBgYGh0bWxcbiAqIDxjZHMtdGV4dGFyZWEtbGFiZWw+XG4gKiBcdExhYmVsXG4gKiBcdDx0ZXh0YXJlYSBjZHNUZXh0QXJlYSBjbGFzcz1cInRleHRhcmVhLWZpZWxkXCI+XG4gKiA8L2Nkcy10ZXh0YXJlYS1sYWJlbD5cbiAqIGBgYFxuICpcbiAqIFtTZWUgZGVtb10oLi4vLi4vP3BhdGg9L3N0b3J5L2NvbXBvbmVudHMtaW5wdXQtdGV4dC1hcmVhLS1iYXNpYylcbiAqL1xuQENvbXBvbmVudCh7XG5cdHNlbGVjdG9yOiBcImNkcy10ZXh0YXJlYS1sYWJlbCwgaWJtLXRleHRhcmVhLWxhYmVsXCIsXG5cdHRlbXBsYXRlOiBgXG5cdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cInNrZWxldG9uXCI+XG5cdFx0XHQ8c3BhbiBjbGFzcz1cImNkcy0tbGFiZWwgY2RzLS1za2VsZXRvblwiPjwvc3Bhbj5cblx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXRleHQtYXJlYSBjZHMtLXNrZWxldG9uXCI+PC9kaXY+XG5cdFx0PC9uZy1jb250YWluZXI+XG5cdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFza2VsZXRvblwiPlxuXHRcdFx0PGRpdiBjbGFzcz1cImNkcy0tdGV4dC1hcmVhX19sYWJlbC13cmFwcGVyXCI+XG5cdFx0XHRcdDxsYWJlbFxuXHRcdFx0XHRcdFtmb3JdPVwibGFiZWxJbnB1dElEXCJcblx0XHRcdFx0XHRbYXR0ci5hcmlhLWxhYmVsXT1cImFyaWFMYWJlbFwiXG5cdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLWxhYmVsXCJcblx0XHRcdFx0XHRbbmdDbGFzc109XCJ7XG5cdFx0XHRcdFx0XHQnY2RzLS1sYWJlbC0tZGlzYWJsZWQnOiBkaXNhYmxlZCxcblx0XHRcdFx0XHRcdCdjZHMtLXZpc3VhbGx5LWhpZGRlbic6IGhpZGVMYWJlbFxuXHRcdFx0XHRcdH1cIj5cblx0XHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJsYWJlbFRlbXBsYXRlOyBlbHNlIGxhYmVsQ29udGVudFwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImxhYmVsVGVtcGxhdGVcIj48L25nLXRlbXBsYXRlPlxuXHRcdFx0XHRcdDxuZy10ZW1wbGF0ZSAjbGFiZWxDb250ZW50PlxuXHRcdFx0XHRcdFx0PG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuXHRcdFx0XHRcdDwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdDwvbGFiZWw+XG5cdFx0XHRcdDxzcGFuXG5cdFx0XHRcdFx0Km5nSWY9XCJlbmFibGVDb3VudGVyICYmIG1heENvdW50XCJcblx0XHRcdFx0XHRjbGFzcz1cImNkcy0tbGFiZWxcIlxuXHRcdFx0XHRcdFtuZ0NsYXNzXT1cInsnY2RzLS1sYWJlbC0tZGlzYWJsZWQnOiBkaXNhYmxlZH1cIlxuXHRcdFx0XHRcdGFyaWEtaGlkZGVuPVwidHJ1ZVwiPlxuXHRcdFx0XHRcdHt7dGV4dENvdW50fX0ve3ttYXhDb3VudH19XG5cdFx0XHRcdDwvc3Bhbj5cblx0XHRcdDwvZGl2PlxuXHRcdFx0PGRpdlxuXHRcdFx0XHRjbGFzcz1cImNkcy0tdGV4dC1hcmVhX193cmFwcGVyXCJcblx0XHRcdFx0W25nQ2xhc3NdPVwie1xuXHRcdFx0XHRcdCdjZHMtLXRleHQtYXJlYV9fd3JhcHBlci0td2Fybic6IHdhcm5cblx0XHRcdFx0fVwiXG5cdFx0XHRcdFthdHRyLmRhdGEtaW52YWxpZF09XCIoaW52YWxpZCA/IHRydWUgOiBudWxsKVwiXG5cdFx0XHRcdCN3cmFwcGVyPlxuXHRcdFx0XHQ8c3ZnXG5cdFx0XHRcdFx0Km5nSWY9XCIhZmx1aWQgJiYgaW52YWxpZFwiXG5cdFx0XHRcdFx0Y2RzSWNvbj1cIndhcm5pbmctLWZpbGxlZFwiXG5cdFx0XHRcdFx0c2l6ZT1cIjE2XCJcblx0XHRcdFx0XHRjbGFzcz1cImNkcy0tdGV4dC1hcmVhX19pbnZhbGlkLWljb25cIj5cblx0XHRcdFx0PC9zdmc+XG5cdFx0XHRcdDxzdmdcblx0XHRcdFx0XHQqbmdJZj1cIiFmbHVpZCAmJiAhaW52YWxpZCAmJiB3YXJuXCJcblx0XHRcdFx0XHRjZHNJY29uPVwid2FybmluZy0tYWx0LS1maWxsZWRcIlxuXHRcdFx0XHRcdHNpemU9XCIxNlwiXG5cdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXRleHQtYXJlYV9faW52YWxpZC1pY29uIGNkcy0tdGV4dC1hcmVhX19pbnZhbGlkLWljb24tLXdhcm5pbmdcIj5cblx0XHRcdFx0PC9zdmc+XG5cdFx0XHRcdDxuZy10ZW1wbGF0ZSAqbmdJZj1cInRleHRBcmVhVGVtcGxhdGU7IGVsc2UgdGV4dEFyZWFDb250ZW50XCIgW25nVGVtcGxhdGVPdXRsZXRdPVwidGV4dEFyZWFUZW1wbGF0ZVwiPjwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdDxuZy10ZW1wbGF0ZSAjdGV4dEFyZWFDb250ZW50PlxuXHRcdFx0XHRcdDxuZy1jb250ZW50IHNlbGVjdD1cIltjZHNUZXh0QXJlYV0sW2libVRleHRBcmVhXSx0ZXh0YXJlYVwiPjwvbmctY29udGVudD5cblx0XHRcdFx0PC9uZy10ZW1wbGF0ZT5cblxuXHRcdFx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwiZmx1aWRcIj5cblx0XHRcdFx0XHQ8aHIgY2xhc3M9XCJjZHMtLXRleHQtYXJlYV9fZGl2aWRlclwiIC8+XG5cdFx0XHRcdFx0PGRpdiAqbmdJZj1cImludmFsaWRcIiBjbGFzcz1cImNkcy0tZm9ybS1yZXF1aXJlbWVudFwiPlxuXHRcdFx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1RlbXBsYXRlKGludmFsaWRUZXh0KVwiPnt7aW52YWxpZFRleHR9fTwvbmctY29udGFpbmVyPlxuXHRcdFx0XHRcdFx0PG5nLXRlbXBsYXRlICpuZ0lmPVwiaXNUZW1wbGF0ZShpbnZhbGlkVGV4dClcIiBbbmdUZW1wbGF0ZU91dGxldF09XCJpbnZhbGlkVGV4dFwiPjwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdFx0XHQ8c3ZnXG5cdFx0XHRcdFx0XHRcdGNkc0ljb249XCJ3YXJuaW5nLS1maWxsZWRcIlxuXHRcdFx0XHRcdFx0XHRzaXplPVwiMTZcIlxuXHRcdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tdGV4dC1hcmVhX19pbnZhbGlkLWljb25cIj5cblx0XHRcdFx0XHRcdDwvc3ZnPlxuXHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdDxkaXYgKm5nSWY9XCIhaW52YWxpZCAmJiB3YXJuXCIgY2xhc3M9XCJjZHMtLWZvcm0tcmVxdWlyZW1lbnRcIj5cblx0XHRcdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhaXNUZW1wbGF0ZSh3YXJuVGV4dClcIj57e3dhcm5UZXh0fX08L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0XHRcdDxuZy10ZW1wbGF0ZSAqbmdJZj1cImlzVGVtcGxhdGUod2FyblRleHQpXCIgW25nVGVtcGxhdGVPdXRsZXRdPVwid2FyblRleHRcIj48L25nLXRlbXBsYXRlPlxuXHRcdFx0XHRcdFx0PHN2Z1xuXHRcdFx0XHRcdFx0XHRjZHNJY29uPVwid2FybmluZy0tYWx0LS1maWxsZWRcIlxuXHRcdFx0XHRcdFx0XHRzaXplPVwiMTZcIlxuXHRcdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tdGV4dC1hcmVhX19pbnZhbGlkLWljb24gY2RzLS10ZXh0LWFyZWFfX2ludmFsaWQtaWNvbi0td2FybmluZ1wiPlxuXHRcdFx0XHRcdFx0PC9zdmc+XG5cdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdDwvbmctY29udGFpbmVyPlxuXHRcdFx0PC9kaXY+XG5cdFx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwiIWZsdWlkXCI+XG5cdFx0XHRcdDxkaXZcblx0XHRcdFx0XHQqbmdJZj1cImhlbHBlclRleHQgJiYgIWludmFsaWQgJiYgIXdhcm5cIlxuXHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1mb3JtX19oZWxwZXItdGV4dFwiXG5cdFx0XHRcdFx0W25nQ2xhc3NdPVwieydjZHMtLWZvcm1fX2hlbHBlci10ZXh0LS1kaXNhYmxlZCc6IGRpc2FibGVkfVwiPlxuXHRcdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhaXNUZW1wbGF0ZShoZWxwZXJUZXh0KVwiPnt7aGVscGVyVGV4dH19PC9uZy1jb250YWluZXI+XG5cdFx0XHRcdFx0PG5nLXRlbXBsYXRlICpuZ0lmPVwiaXNUZW1wbGF0ZShoZWxwZXJUZXh0KVwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImhlbHBlclRleHRcIj48L25nLXRlbXBsYXRlPlxuXHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0PGRpdiAqbmdJZj1cImludmFsaWRcIiBjbGFzcz1cImNkcy0tZm9ybS1yZXF1aXJlbWVudFwiPlxuXHRcdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhaXNUZW1wbGF0ZShpbnZhbGlkVGV4dClcIj57e2ludmFsaWRUZXh0fX08L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJpc1RlbXBsYXRlKGludmFsaWRUZXh0KVwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImludmFsaWRUZXh0XCI+PC9uZy10ZW1wbGF0ZT5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdDxkaXYgKm5nSWY9XCIhaW52YWxpZCAmJiB3YXJuXCIgY2xhc3M9XCJjZHMtLWZvcm0tcmVxdWlyZW1lbnRcIj5cblx0XHRcdFx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwiIWlzVGVtcGxhdGUod2FyblRleHQpXCI+e3t3YXJuVGV4dH19PC9uZy1jb250YWluZXI+XG5cdFx0XHRcdFx0PG5nLXRlbXBsYXRlICpuZ0lmPVwiaXNUZW1wbGF0ZSh3YXJuVGV4dClcIiBbbmdUZW1wbGF0ZU91dGxldF09XCJ3YXJuVGV4dFwiPjwvbmctdGVtcGxhdGU+XG5cdFx0XHRcdDwvZGl2PlxuXHRcdFx0PC9uZy1jb250YWluZXI+XG5cdFx0PC9uZy1jb250YWluZXI+XG5cdGBcbn0pXG5leHBvcnQgY2xhc3MgVGV4dGFyZWFMYWJlbENvbXBvbmVudCBpbXBsZW1lbnRzIEFmdGVyVmlld0luaXQsIE9uQ2hhbmdlcywgT25EZXN0cm95IHtcblxuXHRASG9zdEJpbmRpbmcoXCJjbGFzcy5jZHMtLXRleHQtYXJlYV9fd3JhcHBlci0tcmVhZG9ubHlcIikgZ2V0IGlzUmVhZG9ubHkoKSB7XG5cdFx0cmV0dXJuIHRoaXMud3JhcHBlcj8ubmF0aXZlRWxlbWVudC5xdWVyeVNlbGVjdG9yKFwidGV4dGFyZWFcIik/LnJlYWRPbmx5ID8/IGZhbHNlO1xuXHR9XG5cblx0QEhvc3RCaW5kaW5nKFwiY2xhc3MuY2RzLS10ZXh0LWFyZWEtLWZsdWlkXCIpIGdldCBmbHVpZENsYXNzKCkge1xuXHRcdHJldHVybiB0aGlzLmZsdWlkICYmICF0aGlzLnNrZWxldG9uO1xuXHR9XG5cblx0QEhvc3RCaW5kaW5nKFwiY2xhc3MuY2RzLS10ZXh0LWFyZWEtLWZsdWlkX19za2VsZXRvblwiKSBnZXQgZmx1aWRTa2VsZXRvbkNsYXNzKCkge1xuXHRcdHJldHVybiB0aGlzLmZsdWlkICYmIHRoaXMuc2tlbGV0b247XG5cdH1cblx0LyoqXG5cdCAqIFVzZWQgdG8gYnVpbGQgdGhlIGlkIG9mIHRoZSBpbnB1dCBpdGVtIGFzc29jaWF0ZWQgd2l0aCB0aGUgYExhYmVsYC5cblx0ICovXG5cdHN0YXRpYyBsYWJlbENvdW50ZXIgPSAwO1xuXHQvKipcblx0ICogVGhlIGlkIG9mIHRoZSBpbnB1dCBpdGVtIGFzc29jaWF0ZWQgd2l0aCB0aGUgYExhYmVsYC4gVGhpcyB2YWx1ZSBpcyBhbHNvIHVzZWQgdG8gYXNzb2NpYXRlIHRoZSBgTGFiZWxgIHdpdGhcblx0ICogaXRzIGlucHV0IGNvdW50ZXJwYXJ0IHRocm91Z2ggdGhlICdmb3InIGF0dHJpYnV0ZS5cblx0Ki9cblx0QElucHV0KCkgbGFiZWxJbnB1dElEID0gXCJpYm0tdGV4dGFyZWEtXCIgKyBUZXh0YXJlYUxhYmVsQ29tcG9uZW50LmxhYmVsQ291bnRlcjtcblxuXHQvKipcblx0ICogU2V0IHRvIGB0cnVlYCBmb3IgYSBkaXNhYmxlZCBsYWJlbC5cblx0ICovXG5cdEBJbnB1dCgpIGRpc2FibGVkID0gZmFsc2U7XG5cdC8qKlxuXHQgKiBTZXQgdG8gYHRydWVgIGZvciBhIGxvYWRpbmcgbGFiZWwuXG5cdCAqL1xuXHRASW5wdXQoKSBza2VsZXRvbiA9IGZhbHNlO1xuXG5cdC8qKlxuXHQgKiBIZWxwZXIgaW5wdXQgcHJvcGVydHkgZm9yIGVhc2Ugb2YgbWlncmF0aW9uXG5cdCAqIFNpbmNlIHdlIGNhbm5vdCBwYXNzIG5nLWNvbnRlbnQgZG93biBlYXNpbHkgZnJvbSBsYWJlbCBjb21wb25lbnQsIHdlIHdpbGwgYWNjZXB0IHRoZSB0ZW1wbGF0ZXNcblx0ICovXG5cdEBJbnB1dCgpIGxhYmVsVGVtcGxhdGU6IFRlbXBsYXRlUmVmPGFueT47XG5cdEBJbnB1dCgpIHRleHRBcmVhVGVtcGxhdGU6IFRlbXBsYXRlUmVmPGFueT47XG5cdC8qKlxuXHQgKiBPcHRpb25hbCBoZWxwZXIgdGV4dCB0aGF0IGFwcGVhcnMgdW5kZXIgdGhlIGxhYmVsLlxuXHQgKi9cblx0QElucHV0KCkgaGVscGVyVGV4dDogc3RyaW5nIHwgVGVtcGxhdGVSZWY8YW55Pjtcblx0LyoqXG5cdCAqIFNldHMgdGhlIGludmFsaWQgdGV4dC5cblx0ICovXG5cdEBJbnB1dCgpIGludmFsaWRUZXh0OiBzdHJpbmcgfCBUZW1wbGF0ZVJlZjxhbnk+O1xuXHQvKipcblx0ICogU2V0IHRvIGB0cnVlYCBmb3IgYW4gaW52YWxpZCBsYWJlbCBjb21wb25lbnQuXG5cdCAqL1xuXHRASW5wdXQoKSBpbnZhbGlkID0gZmFsc2U7XG5cdC8qKlxuXHQgICogU2V0IHRvIGB0cnVlYCB0byBzaG93IGEgd2FybmluZyAoY29udGVudHMgc2V0IGJ5IHdhcm5pbmdUZXh0KVxuXHQgICovXG5cdEBJbnB1dCgpIHdhcm4gPSBmYWxzZTtcblx0LyoqXG5cdCAqIFNldHMgdGhlIHdhcm5pbmcgdGV4dFxuXHQgKi9cblx0QElucHV0KCkgd2FyblRleHQ6IHN0cmluZyB8IFRlbXBsYXRlUmVmPGFueT47XG5cdC8qKlxuXHQgKiBTZXQgdGhlIGFyaWFsYWJlbCBmb3IgbGFiZWxcblx0ICovXG5cdEBJbnB1dCgpIGFyaWFMYWJlbDogc3RyaW5nO1xuXG5cdC8qKlxuXHQgKiBFeHBlcmltZW50YWw6IGVuYWJsZSBmbHVpZCBzdGF0ZVxuXHQgKi9cblx0QElucHV0KCkgZmx1aWQgPSBmYWxzZTtcblxuXHQvKipcblx0ICogU2V0IHRvIGB0cnVlYCB0byBoaWRlIHRoZSBsYWJlbCB2aXN1YWxseSwgYnV0IGtlZXAgYWNjZXNzaWJsZSB0b1xuXHQgKiBzY3JlZW4gcmVhZGVycy5cblx0ICovXG5cdEBJbnB1dCgpIGhpZGVMYWJlbCA9IGZhbHNlO1xuXG5cdC8qKlxuXHQgKiBTZXQgdG8gYHRydWVgIChgbWF4Q291bnRgIG11c3QgYmUgc2V0KSB0byBkaXNwbGF5cyBhIGxpdmUgY2hhcmFjdGVyL3dvcmRcblx0ICogY291bnRlciBhbG9uZ3NpZGUgdGhlIGxhYmVsLlxuXHQgKi9cblx0QElucHV0KCkgZW5hYmxlQ291bnRlciA9IGZhbHNlO1xuXG5cdC8qKlxuXHQgKiBNYXhpbXVtIG51bWJlciBvZiBjaGFyYWN0ZXJzIChvciB3b3JkcykgYWxsb3dlZC4gUmVxdWlyZWQgZm9yIHRoZVxuXHQgKiBjb3VudGVyIHRvIGRpc3BsYXkuXG5cdCAqL1xuXHRASW5wdXQoKSBtYXhDb3VudDogbnVtYmVyO1xuXG5cdC8qKlxuXHQgKiBEZXRlcm1pbmVzIHdoZXRoZXIgdGhlIGNvdW50ZXIgY291bnRzIGNoYXJhY3RlcnMgb3Igd29yZHMuXG5cdCAqIFdoZW4gYFwid29yZFwiYCBhbmQgYG1heENvdW50YCBpcyBzZXQsIGlucHV0IGlzIGNsYW1wZWQgdG8gYG1heENvdW50YCB3b3Jkc1xuXHQgKiBvbiBlYWNoIGNoYW5nZS4gRXhjZXNzIHdvcmRzIGFyZSB0cmltbWVkIGZyb20gdGhlIGVuZCBvZiB0aGUgdmFsdWUuXG5cdCAqL1xuXHRASW5wdXQoKSBjb3VudGVyTW9kZTogXCJjaGFyYWN0ZXJcIiB8IFwid29yZFwiID0gXCJjaGFyYWN0ZXJcIjtcblxuXHQvLyAgVHJhY2tzIGN1cnJlbnQgY2hhcmFjdGVyIC8gd29yZCBjb3VudCBmb3IgdGhlIGNvdW50ZXIgZGlzcGxheS5cblx0dGV4dENvdW50ID0gMDtcblxuXHQvLyBAdHMtaWdub3JlXG5cdEBWaWV3Q2hpbGQoXCJ3cmFwcGVyXCIsIHsgc3RhdGljOiBmYWxzZSB9KSB3cmFwcGVyOiBFbGVtZW50UmVmPEhUTUxEaXZFbGVtZW50PjtcblxuXHQvLyBAdHMtaWdub3JlXG5cdEBDb250ZW50Q2hpbGQoVGV4dEFyZWEsIHsgc3RhdGljOiBmYWxzZSB9KSB0ZXh0QXJlYTogVGV4dEFyZWE7XG5cblx0QEhvc3RCaW5kaW5nKFwiY2xhc3MuY2RzLS1mb3JtLWl0ZW1cIikgbGFiZWxDbGFzcyA9IHRydWU7XG5cblx0Ly8gQ2FjaGVkIHJlZmVyZW5jZSB0byB0aGUgdGV4dGFyZWEgZWxlbWVudCwgc2V0IG9uY2UgaW4gbmdBZnRlclZpZXdJbml0LlxuXHRwcml2YXRlIF90ZXh0YXJlYUVsZW1lbnQ6IEhUTUxUZXh0QXJlYUVsZW1lbnQgfCBudWxsID0gbnVsbDtcblx0Ly8gQ2FjaGVkIGxpc3RlbmVyIHNvIGl0IGNhbiBiZSByZW1vdmVkIHByZWNpc2VseSAoYXZvaWRzIGFub255bW91cy1mdW5jdGlvbiBsZWFrKVxuXHRwcml2YXRlIF9pbnB1dExpc3RlbmVyOiAoKGU6IEV2ZW50KSA9PiB2b2lkKSB8IG51bGwgPSBudWxsO1xuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIExhYmVsLlxuXHQgKi9cblx0Y29uc3RydWN0b3IocHJvdGVjdGVkIGNoYW5nZURldGVjdG9yUmVmOiBDaGFuZ2VEZXRlY3RvclJlZikge31cblxuXHQvKipcblx0ICogU2V0cyB0aGUgaWQgb24gdGhlIGlucHV0IGl0ZW0gYXNzb2NpYXRlZCB3aXRoIHRoZSBgTGFiZWxgIGFuZCBhdHRhY2hlcyB0aGVcblx0ICogY291bnRlciBsaXN0ZW5lciB3aGVuIGBlbmFibGVDb3VudGVyYCBpcyBhbHJlYWR5IGB0cnVlYCBvbiBmaXJzdCByZW5kZXIuXG5cdCAqL1xuXHRuZ0FmdGVyVmlld0luaXQoKSB7XG5cdFx0aWYgKHRoaXMud3JhcHBlcikge1xuXHRcdFx0Ly8gUHJpb3JpdGl6ZSBzZXR0aW5nIGlkIHRvIGB0ZXh0YXJlYWAgb3ZlciBkaXZcblx0XHRcdGNvbnN0IGlucHV0RWxlbWVudCA9IHRoaXMud3JhcHBlci5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoXCJ0ZXh0YXJlYVwiKTtcblx0XHRcdGlmIChpbnB1dEVsZW1lbnQpIHtcblx0XHRcdFx0Ly8gYXZvaWQgb3ZlcnJpZGluZyBpZHMgYWxyZWFkeSBzZXQgYnkgdGhlIHVzZXIg4oCUIHJldXNlIGl0IGluc3RlYWRcblx0XHRcdFx0aWYgKGlucHV0RWxlbWVudC5pZCkge1xuXHRcdFx0XHRcdHRoaXMubGFiZWxJbnB1dElEID0gaW5wdXRFbGVtZW50LmlkO1xuXHRcdFx0XHRcdHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYuZGV0ZWN0Q2hhbmdlcygpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlucHV0RWxlbWVudC5zZXRBdHRyaWJ1dGUoXCJpZFwiLCB0aGlzLmxhYmVsSW5wdXRJRCk7XG5cblx0XHRcdFx0dGhpcy5fdGV4dGFyZWFFbGVtZW50ID0gaW5wdXRFbGVtZW50O1xuXHRcdFx0XHR0aGlzLl9zeW5jTWF4TGVuZ3RoKCk7XG5cblx0XHRcdFx0aWYgKHRoaXMuZW5hYmxlQ291bnRlcikge1xuXHRcdFx0XHRcdHRoaXMudGV4dENvdW50ID0gdGhpcy5fY291bnRWYWx1ZShpbnB1dEVsZW1lbnQudmFsdWUgfHwgXCJcIik7XG5cdFx0XHRcdFx0dGhpcy5fYXR0YWNoQ291bnRlckxpc3RlbmVyKCk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdGNvbnN0IGRpdkVsZW1lbnQgPSB0aGlzLndyYXBwZXIubmF0aXZlRWxlbWVudC5xdWVyeVNlbGVjdG9yKFwiZGl2XCIpO1xuXHRcdFx0aWYgKGRpdkVsZW1lbnQpIHtcblx0XHRcdFx0aWYgKGRpdkVsZW1lbnQuaWQpIHtcblx0XHRcdFx0XHR0aGlzLmxhYmVsSW5wdXRJRCA9IGRpdkVsZW1lbnQuaWQ7XG5cdFx0XHRcdFx0dGhpcy5jaGFuZ2VEZXRlY3RvclJlZi5kZXRlY3RDaGFuZ2VzKCk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZGl2RWxlbWVudC5zZXRBdHRyaWJ1dGUoXCJpZFwiLCB0aGlzLmxhYmVsSW5wdXRJRCk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIEF0dGFjaC9yZW1vdmUgbGlzdGVuZXIgYW5kIHNlZWQgYHRleHRDb3VudGAgZnJvbSB0aGUgdGV4dGFyZWEncyBjdXJyZW50IHZhbHVlLlxuXHQgKiBAcGFyYW0gY2hhbmdlc1xuXHQgKi9cblx0bmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcykge1xuXHRcdGlmIChjaGFuZ2VzLmVuYWJsZUNvdW50ZXIgJiYgIWNoYW5nZXMuZW5hYmxlQ291bnRlci5maXJzdENoYW5nZSkge1xuXHRcdFx0aWYgKGNoYW5nZXMuZW5hYmxlQ291bnRlci5jdXJyZW50VmFsdWUpIHtcblx0XHRcdFx0aWYgKHRoaXMuX3RleHRhcmVhRWxlbWVudCkge1xuXHRcdFx0XHRcdHRoaXMudGV4dENvdW50ID0gdGhpcy5fY291bnRWYWx1ZSh0aGlzLl90ZXh0YXJlYUVsZW1lbnQudmFsdWUgfHwgXCJcIik7XG5cdFx0XHRcdFx0dGhpcy5fYXR0YWNoQ291bnRlckxpc3RlbmVyKCk7XG5cdFx0XHRcdFx0dGhpcy5jaGFuZ2VEZXRlY3RvclJlZi5kZXRlY3RDaGFuZ2VzKCk7XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuX2RldGFjaENvdW50ZXJMaXN0ZW5lcigpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmIChcblx0XHRcdChjaGFuZ2VzLm1heENvdW50IHx8IGNoYW5nZXMuY291bnRlck1vZGUpICYmXG5cdFx0XHQhKGNoYW5nZXMubWF4Q291bnQ/LmZpcnN0Q2hhbmdlICYmIGNoYW5nZXMuY291bnRlck1vZGU/LmZpcnN0Q2hhbmdlKVxuXHRcdCkge1xuXHRcdFx0dGhpcy5fc3luY01heExlbmd0aCgpO1xuXHRcdH1cblx0fVxuXG5cdG5nT25EZXN0cm95KCkge1xuXHRcdHRoaXMuX2RldGFjaENvdW50ZXJMaXN0ZW5lcigpO1xuXHR9XG5cblx0cHVibGljIGlzVGVtcGxhdGUodmFsdWUpIHtcblx0XHRyZXR1cm4gdmFsdWUgaW5zdGFuY2VvZiBUZW1wbGF0ZVJlZjtcblx0fVxuXG5cdC8qKlxuXHQgKiBLZWVwcyB0aGUgdGV4dGFyZWEncyBgbWF4bGVuZ3RoYCBhdHRyaWJ1dGUgaW4gc3luYyB3aXRoIGBtYXhDb3VudGAuIFRoaXMgaXMgb25seSBzZXRcblx0ICogd2hlbiBjb3VudGVyTW9kZSBpcyBzZXQgdG8gYGNoYXJhY3RlcmAuIFdoZW4gY291bnRlck1vZGUgaXMgc2V0IHRvIGB3b3JkYCwgd2UgZW5mb3JjZSBsaW1pdCB2aWEgSlMuXG5cdCAqIElmIGBtYXhDb3VudGAgaXMgdW5zZXQgb3IgdGhlIG1vZGUgaXMgYFwid29yZFwiYCwgYW55IHByZXZpb3VzbHkgYXBwbGllZFxuXHQgKiBgbWF4bGVuZ3RoYCBpcyByZW1vdmVkIHNvIHRoZSB0ZXh0YXJlYSBpcyB1bnJlc3RyaWN0ZWQgYnkgdGhlIGF0dHJpYnV0ZS5cblx0ICovXG5cdHByaXZhdGUgX3N5bmNNYXhMZW5ndGgoKTogdm9pZCB7XG5cdFx0aWYgKCF0aGlzLl90ZXh0YXJlYUVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0aWYgKHRoaXMuY291bnRlck1vZGUgPT09IFwiY2hhcmFjdGVyXCIgJiYgdGhpcy5tYXhDb3VudCAhPSBudWxsKSB7XG5cdFx0XHR0aGlzLl90ZXh0YXJlYUVsZW1lbnQuc2V0QXR0cmlidXRlKFwibWF4bGVuZ3RoXCIsIFN0cmluZyh0aGlzLm1heENvdW50KSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX3RleHRhcmVhRWxlbWVudC5yZW1vdmVBdHRyaWJ1dGUoXCJtYXhsZW5ndGhcIik7XG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIEF0dGFjaGVzIHRoZSBpbnB1dCBldmVudCBsaXN0ZW5lciwgZW5zdXJpbmcgaXQgaXMgbmV2ZXIgYWRkZWQgdHdpY2UuXG5cdCAqL1xuXHRwcml2YXRlIF9hdHRhY2hDb3VudGVyTGlzdGVuZXIoKTogdm9pZCB7XG5cdFx0dGhpcy5fZGV0YWNoQ291bnRlckxpc3RlbmVyKCk7XG5cdFx0aWYgKCF0aGlzLl90ZXh0YXJlYUVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0dGhpcy5faW5wdXRMaXN0ZW5lciA9IChlOiBFdmVudCkgPT4ge1xuXHRcdFx0Y29uc3QgZWwgPSBlLnRhcmdldCBhcyBIVE1MVGV4dEFyZWFFbGVtZW50O1xuXHRcdFx0Ly8gV29yZC1tb2RlIGVuZm9yY2VtZW50OiBjbGFtcCB2YWx1ZSB0byBtYXhDb3VudCB3b3JkcyBvbiBlYWNoIGlucHV0IHNvXG5cdFx0XHQvLyB0aGUgdGV4dGFyZWEgbmV2ZXIgaG9sZHMgbW9yZSB3b3JkcyB0aGFuIGFsbG93ZWQuICBDaGFyYWN0ZXIgbW9kZSByZWxpZXNcblx0XHRcdC8vIG9uIHRoZSBuYXRpdmUgYG1heGxlbmd0aGAgYXR0cmlidXRlIHNldCBieSB0aGUgZGV2ZWxvcGVyLlxuXHRcdFx0aWYgKHRoaXMuY291bnRlck1vZGUgPT09IFwid29yZFwiICYmIHRoaXMubWF4Q291bnQgIT0gbnVsbCkge1xuXHRcdFx0XHRjb25zdCBjbGFtcGVkID0gdGhpcy5fdHJ1bmNhdGVUb1dvcmRMaW1pdChlbC52YWx1ZSB8fCBcIlwiLCB0aGlzLm1heENvdW50KTtcblx0XHRcdFx0aWYgKGNsYW1wZWQgIT09IGVsLnZhbHVlKSB7XG5cdFx0XHRcdFx0ZWwudmFsdWUgPSBjbGFtcGVkO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHR0aGlzLnRleHRDb3VudCA9IHRoaXMuX2NvdW50VmFsdWUoZWwudmFsdWUgfHwgXCJcIik7XG5cdFx0fTtcblx0XHR0aGlzLl90ZXh0YXJlYUVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcImlucHV0XCIsIHRoaXMuX2lucHV0TGlzdGVuZXIpO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRydW5jYXRlcyBgdmFsdWVgIHNvIGl0IGNvbnRhaW5zIGF0IG1vc3QgYGxpbWl0YCBVbmljb2RlIHdvcmRzLlxuXHQgKiBXaGl0ZXNwYWNlIGJldHdlZW4gYW5kIGFyb3VuZCB3b3JkcyBpcyBwcmVzZXJ2ZWQgdXAgdG8gdGhlIGxhc3QgYWxsb3dlZCB3b3JkO1xuXHQgKiBhbnkgdHJhaWxpbmcgY29udGVudCAocGFydGlhbCB3b3JkIG9yIHNwYWNlKSBiZXlvbmQgdGhlIGxpbWl0IGlzIGRyb3BwZWQuXG5cdCAqL1xuXHRwcml2YXRlIF90cnVuY2F0ZVRvV29yZExpbWl0KHZhbHVlOiBzdHJpbmcsIGxpbWl0OiBudW1iZXIpOiBzdHJpbmcge1xuXHRcdGxldCB3b3Jkc1NlZW4gPSAwO1xuXHRcdC8vIFdhbGsgdGhyb3VnaCB0aGUgc3RyaW5nIGNhcHR1cmluZyB3b3JkIGJvdW5kYXJpZXNcblx0XHRjb25zdCB3b3JkUGF0dGVybiA9IC9cXHB7TH0rL2d1O1xuXHRcdGxldCBtYXRjaDogUmVnRXhwRXhlY0FycmF5IHwgbnVsbDtcblx0XHRsZXQgY3V0SW5kZXggPSB2YWx1ZS5sZW5ndGg7XG5cdFx0d2hpbGUgKChtYXRjaCA9IHdvcmRQYXR0ZXJuLmV4ZWModmFsdWUpKSAhPT0gbnVsbCkge1xuXHRcdFx0d29yZHNTZWVuKys7XG5cdFx0XHRpZiAod29yZHNTZWVuID09PSBsaW1pdCkge1xuXHRcdFx0XHQvLyBBbGxvdyB0aGUgc3RyaW5nIHRvIGNvbnRpbnVlIHVwIHRvIChidXQgbm90IHBhc3QpIHRoZSBlbmQgb2YgdGhpcyB3b3JkXG5cdFx0XHRcdGN1dEluZGV4ID0gbWF0Y2guaW5kZXggKyBtYXRjaFswXS5sZW5ndGg7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gd29yZHNTZWVuIDwgbGltaXQgPyB2YWx1ZSA6IHZhbHVlLnNsaWNlKDAsIGN1dEluZGV4KTtcblx0fVxuXG5cblx0LyoqXG5cdCAqIFJlbW92ZXMgdGhlIGlucHV0IGV2ZW50IGxpc3RlbmVyIGFuZCBjbGVhcnMgdGhlIGNhY2hlZCByZWZlcmVuY2UuXG5cdCAqL1xuXHRwcml2YXRlIF9kZXRhY2hDb3VudGVyTGlzdGVuZXIoKTogdm9pZCB7XG5cdFx0aWYgKHRoaXMuX2lucHV0TGlzdGVuZXIgJiYgdGhpcy5fdGV4dGFyZWFFbGVtZW50KSB7XG5cdFx0XHR0aGlzLl90ZXh0YXJlYUVsZW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImlucHV0XCIsIHRoaXMuX2lucHV0TGlzdGVuZXIpO1xuXHRcdFx0dGhpcy5faW5wdXRMaXN0ZW5lciA9IG51bGw7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBfY291bnRWYWx1ZSh2YWx1ZTogc3RyaW5nKTogbnVtYmVyIHtcblx0XHRpZiAodGhpcy5jb3VudGVyTW9kZSA9PT0gXCJ3b3JkXCIpIHtcblx0XHRcdHJldHVybiB2YWx1ZS5tYXRjaCgvXFxwe0x9Ky9ndSk/Lmxlbmd0aCB8fCAwO1xuXHRcdH1cblx0XHRyZXR1cm4gdmFsdWUubGVuZ3RoO1xuXHR9XG59XG4iXX0=