UNPKG

carbon-components-angular

Version:
657 lines (655 loc) 66.1 kB
import { Component, HostBinding, Input, Output, EventEmitter, ViewChild, TemplateRef, ViewChildren } from "@angular/core"; import { NG_VALUE_ACCESSOR } from "@angular/forms"; import * as i0 from "@angular/core"; import * as i1 from "carbon-components-angular/utils"; import * as i2 from "@angular/common"; /** * Used to select from ranges of values. [See here](https://www.carbondesignsystem.com/components/slider/usage) for usage information. * * Get started with importing the module: * * ```typescript * import { SliderModule } from 'carbon-components-angular'; * ``` * * The simplest possible slider usage looks something like: * * ```html * <cds-slider></cds-slider> * ``` * * That will render a slider without labels or alternative value input. Labels can be provided by * elements with `[minLabel]` and `[maxLabel]` attributes, and an `input` (may use the `ibmInput` directive) can be supplied * for use as an alternative value field. * * ex: * * ```html * <!-- Full example --> * <cds-slider> * <span minLabel>0GB</span> * <span maxLabel>100GB</span> * <input/> * </cds-slider> * * <!-- with just an input --> * <cds-slider> * <input/> * </cds-slider> * * <!-- with just one label --> * <cds-slider> * <span maxLabel>Maximum</span> * </cds-slider> * ``` * * Slider supports `NgModel` by default, as well as two way binding to the `value` input. * * [See demo](../../?path=/story/components-slider--advanced) */ export class Slider { constructor(elementRef, eventService, changeDetection) { this.elementRef = elementRef; this.eventService = eventService; this.changeDetection = changeDetection; /** The interval for our range */ this.step = 1; /** Base ID for the slider. The min and max labels get IDs `${this.id}-bottom-range` and `${this.id}-top-range` respectively */ this.id = `slider-${Slider.count++}`; /** Value used to "multiply" the `step` when using arrow keys to select values */ this.shiftMultiplier = 4; /** Set to `true` for a loading slider */ this.skeleton = false; /** Set to `true` for a slider without arrow key interactions. */ this.disableArrowKeys = false; /** Emits every time a new value is selected */ this.valueChange = new EventEmitter(); this.hostClass = true; this.labelId = `${this.id}-label`; this.bottomRangeId = `${this.id}-bottom-range`; this.topRangeId = `${this.id}-top-range`; this.fractionComplete = 0; this.isMouseDown = false; this._min = 0; this._max = 100; this._value = [this.min]; this._previousValue = [this.min]; this._disabled = false; this._readonly = false; this._focusedThumbIndex = 0; /** Send changes back to the model */ this.propagateChange = (_) => { }; /** Callback to notify the model when our input has been touched */ this.onTouched = () => { }; } /** The lower bound of our range */ set min(v) { if (!v) { return; } this._min = v; // force the component to update this.value = this.value; } get min() { return this._min; } /** The upper bound of our range */ set max(v) { if (!v) { return; } this._max = v; // force the component to update this.value = this.value; } get max() { return this._max; } /** Set the initial value. Available for two way binding */ set value(v) { if (!v) { v = [this.min]; } if (typeof v === "number" || typeof v === "string") { v = [Number(v)]; } if (v[0] < this.min) { v[0] = this.min; } if (v[0] > this.max) { v[0] = this.max; } if (this.isRange()) { if (this._previousValue[0] !== v[0]) { // left moved if (v[0] > v[1] - this.step) { // stop the left handle if surpassing the right one v[0] = v[1] - this.step; } else if (v[0] > this.max) { v[0] = this.max; } else if (v[0] < this.min) { v[0] = this.min; } } if (this._previousValue[1] !== v[1]) { // right moved if (v[1] > this.max) { v[1] = this.max; } else if (v[1] < this._value[0] + this.step) { // stop the right handle if surpassing the left one v[1] = this._value[0] + this.step; } else if (v[1] < this.min) { v[1] = this.min; } } } this._previousValue = [...this._value]; // store a copy, enable detection which handle moved this._value = [...v]; // triggers change detection when ngModel value is an array (for range) if (this.isRange() && this.filledTrack) { this.updateTrackRangeWidth(); } else if (this.filledTrack) { this.filledTrack.nativeElement.style.transform = `translate(0%, -50%) ${this.scaleX(this.getFractionComplete(v[0]))}`; } if (this.inputs && this.inputs.length) { this.inputs.forEach((input, index) => { input.value = this._value[index].toString(); }); } const valueToEmit = this.isRange() ? v : v[0]; this.propagateChange(valueToEmit); this.valueChange.emit(valueToEmit); } get value() { if (this.isRange()) { return this._value; } return this._value[0]; } /** Disables the range visually and functionally */ set disabled(v) { this._disabled = v; // for some reason `this.input` never exists here, so we have to query for it here too const inputs = this.getInputs(); if (inputs && inputs.length > 0) { inputs.forEach(input => input.disabled = v); } } get disabled() { return this._disabled; } /** Set to `true` for a readonly state. */ set readonly(v) { this._readonly = v; // for some reason `this.input` never exists here, so we have to query for it here too const inputs = this.getInputs(); if (inputs && inputs.length > 0) { inputs.forEach(input => input.readOnly = v); } } get readonly() { return this._readonly; } ngAfterViewInit() { // bind mousemove and mouseup to the document so we don't have issues tracking the mouse this.eventService.onDocument("mousemove", this.onMouseMove.bind(this)); this.eventService.onDocument("mouseup", this.onMouseUp.bind(this)); // apply any values we got from before the view initialized this.changeDetection.detectChanges(); // TODO: ontouchstart/ontouchmove/ontouchend // set up the optional input this.inputs = this.getInputs(); if (this.inputs && this.inputs.length > 0) { this.inputs.forEach((input, index) => { input.type = "number"; input.classList.add("cds--slider-text-input"); input.classList.add("cds--text-input"); input.setAttribute("aria-labelledby", `${this.bottomRangeId} ${this.topRangeId}`); input.value = index < this._value.length ? this._value[index].toString() : this.max.toString(); // bind events on our optional input this.eventService.on(input, "change", event => this.onChange(event, index)); if (index === 0) { this.eventService.on(input, "focus", this.onFocus.bind(this)); } }); } } trackThumbsBy(index, item) { return index; } /** Register a change propagation function for `ControlValueAccessor` */ registerOnChange(fn) { this.propagateChange = fn; } /** Register a callback to notify when our input has been touched */ registerOnTouched(fn) { this.onTouched = fn; } /** Receives a value from the model */ writeValue(v) { this.value = v; } /** * Returns the amount of "completeness" of a value as a fraction of the total track width */ getFractionComplete(value) { if (!this.track) { return 0; } const trackWidth = this.track.nativeElement.getBoundingClientRect().width; return this.convertToPx(value) / trackWidth; } /** Helper function to return the CSS transform `scaleX` function */ scaleX(complete) { return `scaleX(${complete})`; } /** Converts a given px value to a "real" value in our range */ convertToValue(pxAmount) { // basic concept borrowed from carbon-components // https://github.com/carbon-design-system/carbon/blob/43bf3abdc2f8bdaa38aa84e0f733adde1e1e8894/src/components/slider/slider.js#L147-L151 const range = this.max - this.min; const trackWidth = this.track.nativeElement.getBoundingClientRect().width; const unrounded = pxAmount / trackWidth; const rounded = Math.round((range * unrounded) / this.step) * this.step; return rounded + this.min; } /** Converts a given "real" value to a px value we can update the view with */ convertToPx(value) { if (!this.track) { return 0; } const trackWidth = this.track.nativeElement.getBoundingClientRect().width; if (value >= this.max) { return trackWidth; } if (value <= this.min) { return 0; } // account for value shifting by subtracting min from value and max return Math.round(trackWidth * ((value - this.min) / (this.max - this.min))); } /** * Increments the value by the step value, or the step value multiplied by the `multiplier` argument. * * @argument multiplier Defaults to `1`, multiplied with the step value. */ incrementValue(multiplier = 1, index = 0) { this._value[index] = this._value[index] + (this.step * multiplier); this.value = this.value; // run the setter } /** * Decrements the value by the step value, or the step value multiplied by the `multiplier` argument. * * @argument multiplier Defaults to `1`, multiplied with the step value. */ decrementValue(multiplier = 1, index = 0) { this._value[index] = this._value[index] - (this.step * multiplier); this.value = this.value; // run the setter } /** * Determines if the slider is in range mode. */ isRange() { return this._value.length > 1; } /** * Range mode only. * Updates the track width to span from the low thumb to the high thumb */ updateTrackRangeWidth() { const fraction = this.getFractionComplete(this._value[0]); const fraction2 = this.getFractionComplete(this._value[1]); this.filledTrack.nativeElement.style.transform = `translate(${fraction * 100}%, -50%) ${this.scaleX(fraction2 - fraction)}`; } /** Change handler for the optional input */ onChange(event, index) { this._value[index] = Number(event.target.value); this.value = this.value; } /** * Handles clicks on the slider, and setting the value to it's "real" equivalent. * Will assign the value to the closest thumb if in range mode. * */ onClick(event) { if (this.disabled || this.readonly) { return; } const trackLeft = this.track.nativeElement.getBoundingClientRect().left; const trackValue = this.convertToValue(event.clientX - trackLeft); if (this.isRange()) { if (Math.abs(this._value[0] - trackValue) < Math.abs(this._value[1] - trackValue)) { this._value[0] = trackValue; } else { this._value[1] = trackValue; } } else { this._value[0] = trackValue; } this.value = this.value; } /** Focus handler for the optional input */ onFocus({ target }) { target.select(); } /** Mouse move handler. Responsible for updating the value and visual selection based on mouse movement */ onMouseMove(event) { if (this.disabled || this.readonly || !this.isMouseDown) { return; } const track = this.track.nativeElement.getBoundingClientRect(); let value; if (event.clientX - track.left <= track.width && event.clientX - track.left >= 0) { value = this.convertToValue(event.clientX - track.left); } // if the mouse is beyond the max, set the value to `max` if (event.clientX - track.left > track.width) { value = this.max; } // if the mouse is below the min, set the value to `min` if (event.clientX - track.left < 0) { value = this.min; } if (value !== undefined) { this._value[this._focusedThumbIndex] = value; this.value = this.value; } } /** * Enables the `onMouseMove` handler * * @param {boolean} thumb If true then `thumb` is clicked down, otherwise `thumb2` is clicked down. */ onMouseDown(event, index = 0) { event.preventDefault(); if (this.disabled || this.readonly) { return; } this._focusedThumbIndex = index; this.thumbs.toArray()[index].nativeElement.focus(); this.isMouseDown = true; } /** Disables the `onMouseMove` handler */ onMouseUp() { this.isMouseDown = false; } /** * Calls `incrementValue` for ArrowRight and ArrowUp, `decrementValue` for ArrowLeft and ArrowDown. * * @param {boolean} thumb If true then `thumb` is pressed down, otherwise `thumb2` is pressed down. */ onKeyDown(event, index = 0) { if (this.disableArrowKeys || this.readonly) { return; } const multiplier = event.shiftKey ? this.shiftMultiplier : 1; if (event.key === "ArrowLeft" || event.key === "ArrowDown") { this.decrementValue(multiplier, index); this.thumbs.toArray()[index].nativeElement.focus(); event.preventDefault(); } else if (event.key === "ArrowRight" || event.key === "ArrowUp") { this.incrementValue(multiplier, index); this.thumbs.toArray()[index].nativeElement.focus(); event.preventDefault(); } } isTemplate(value) { return value instanceof TemplateRef; } /** Get optional input fields */ getInputs() { return this.elementRef.nativeElement.querySelectorAll("input:not([type=range])"); } } /** Used to generate unique IDs */ Slider.count = 0; Slider.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Slider, deps: [{ token: i0.ElementRef }, { token: i1.EventService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); Slider.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: Slider, selector: "cds-slider, ibm-slider", inputs: { min: "min", max: "max", step: "step", value: "value", id: "id", shiftMultiplier: "shiftMultiplier", skeleton: "skeleton", label: "label", disableArrowKeys: "disableArrowKeys", disabled: "disabled", readonly: "readonly" }, outputs: { valueChange: "valueChange" }, host: { properties: { "class.cds--form-item": "this.hostClass" } }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: Slider, multi: true } ], viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true }, { propertyName: "filledTrack", first: true, predicate: ["filledTrack"], descendants: true }, { propertyName: "range", first: true, predicate: ["range"], descendants: true }, { propertyName: "thumbs", predicate: ["thumbs"], descendants: true }], ngImport: i0, template: ` <ng-container *ngIf="!skeleton; else skeletonTemplate"> <label *ngIf="label" [for]="id" [id]="labelId" class="cds--label" [ngClass]="{'cds--label--disabled': disabled}"> <ng-container *ngIf="!isTemplate(label)">{{label}}</ng-container> <ng-template *ngIf="isTemplate(label)" [ngTemplateOutlet]="label"></ng-template> </label> <div class="cds--slider-container" [ngClass]="{ 'cds--slider-container--readonly': readonly }"> <label [id]="bottomRangeId" class="cds--slider__range-label"> <ng-content select="[minLabel]"></ng-content> </label> <div class="cds--slider" (click)="onClick($event)" [ngClass]="{ 'cds--slider--disabled': disabled, 'cds--slider--readonly': readonly }"> <ng-container *ngIf="!isRange()"> <div class="cds--slider__thumb-wrapper" [ngStyle]="{insetInlineStart: getFractionComplete(value) * 100 + '%'}"> <div #thumbs role="slider" [id]="id" [attr.aria-labelledby]="labelId" class="cds--slider__thumb" tabindex="0" (mousedown)="onMouseDown($event)" (keydown)="onKeyDown($event)"> </div> </div> </ng-container> <ng-container *ngIf="isRange()"> <div class="cds--slider__thumb-wrapper" [ngStyle]="{insetInlineStart: getFractionComplete(thumb) * 100 + '%'}" *ngFor="let thumb of value; let i = index; trackBy: trackThumbsBy"> <div #thumbs role="slider" [id]="id + (i > 0 ? '-' + i : '')" [attr.aria-labelledby]="labelId" class="cds--slider__thumb" tabindex="0" (mousedown)="onMouseDown($event, i)" (keydown)="onKeyDown($event, i)"> </div> </div> </ng-container> <div #track class="cds--slider__track"> </div> <div #filledTrack class="cds--slider__filled-track"> </div> <input #range aria-label="slider" class="cds--slider__input" type="range" [step]="step" [min]="min" [max]="max" [value]="value.toString()"> </div> <label [id]="topRangeId" class="cds--slider__range-label"> <ng-content select="[maxLabel]"></ng-content> </label> <ng-content select="input"></ng-content> </div> </ng-container> <ng-template #skeletonTemplate> <label *ngIf="label" class="cds--label cds--skeleton"></label> <div class="cds--slider-container cds--skeleton"> <span class="cds--slider__range-label"></span> <div class="cds--slider"> <div class="cds--slider__thumb"></div> <div class="cds--slider__track"></div> <div class="cds--slider__filled-track"></div> </div> <span class="cds--slider__range-label"></span> </div> </ng-template> `, isInline: true, dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Slider, decorators: [{ type: Component, args: [{ selector: "cds-slider, ibm-slider", template: ` <ng-container *ngIf="!skeleton; else skeletonTemplate"> <label *ngIf="label" [for]="id" [id]="labelId" class="cds--label" [ngClass]="{'cds--label--disabled': disabled}"> <ng-container *ngIf="!isTemplate(label)">{{label}}</ng-container> <ng-template *ngIf="isTemplate(label)" [ngTemplateOutlet]="label"></ng-template> </label> <div class="cds--slider-container" [ngClass]="{ 'cds--slider-container--readonly': readonly }"> <label [id]="bottomRangeId" class="cds--slider__range-label"> <ng-content select="[minLabel]"></ng-content> </label> <div class="cds--slider" (click)="onClick($event)" [ngClass]="{ 'cds--slider--disabled': disabled, 'cds--slider--readonly': readonly }"> <ng-container *ngIf="!isRange()"> <div class="cds--slider__thumb-wrapper" [ngStyle]="{insetInlineStart: getFractionComplete(value) * 100 + '%'}"> <div #thumbs role="slider" [id]="id" [attr.aria-labelledby]="labelId" class="cds--slider__thumb" tabindex="0" (mousedown)="onMouseDown($event)" (keydown)="onKeyDown($event)"> </div> </div> </ng-container> <ng-container *ngIf="isRange()"> <div class="cds--slider__thumb-wrapper" [ngStyle]="{insetInlineStart: getFractionComplete(thumb) * 100 + '%'}" *ngFor="let thumb of value; let i = index; trackBy: trackThumbsBy"> <div #thumbs role="slider" [id]="id + (i > 0 ? '-' + i : '')" [attr.aria-labelledby]="labelId" class="cds--slider__thumb" tabindex="0" (mousedown)="onMouseDown($event, i)" (keydown)="onKeyDown($event, i)"> </div> </div> </ng-container> <div #track class="cds--slider__track"> </div> <div #filledTrack class="cds--slider__filled-track"> </div> <input #range aria-label="slider" class="cds--slider__input" type="range" [step]="step" [min]="min" [max]="max" [value]="value.toString()"> </div> <label [id]="topRangeId" class="cds--slider__range-label"> <ng-content select="[maxLabel]"></ng-content> </label> <ng-content select="input"></ng-content> </div> </ng-container> <ng-template #skeletonTemplate> <label *ngIf="label" class="cds--label cds--skeleton"></label> <div class="cds--slider-container cds--skeleton"> <span class="cds--slider__range-label"></span> <div class="cds--slider"> <div class="cds--slider__thumb"></div> <div class="cds--slider__track"></div> <div class="cds--slider__filled-track"></div> </div> <span class="cds--slider__range-label"></span> </div> </ng-template> `, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: Slider, multi: true } ] }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.EventService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { min: [{ type: Input }], max: [{ type: Input }], step: [{ type: Input }], value: [{ type: Input }], id: [{ type: Input }], shiftMultiplier: [{ type: Input }], skeleton: [{ type: Input }], label: [{ type: Input }], disableArrowKeys: [{ type: Input }], disabled: [{ type: Input }], readonly: [{ type: Input }], valueChange: [{ type: Output }], hostClass: [{ type: HostBinding, args: ["class.cds--form-item"] }], thumbs: [{ type: ViewChildren, args: ["thumbs"] }], track: [{ type: ViewChild, args: ["track"] }], filledTrack: [{ type: ViewChild, args: ["filledTrack"] }], range: [{ type: ViewChild, args: ["range"] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2xpZGVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zbGlkZXIvc2xpZGVyLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ04sU0FBUyxFQUNULFdBQVcsRUFDWCxLQUFLLEVBQ0wsTUFBTSxFQUNOLFlBQVksRUFFWixTQUFTLEVBRVQsV0FBVyxFQUNYLFlBQVksRUFHWixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQXdCLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7Ozs7QUFHekU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EyQ0c7QUF3R0gsTUFBTSxPQUFPLE1BQU07SUEySmxCLFlBQ1csVUFBc0IsRUFDdEIsWUFBMEIsRUFDNUIsZUFBa0M7UUFGaEMsZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQUN0QixpQkFBWSxHQUFaLFlBQVksQ0FBYztRQUM1QixvQkFBZSxHQUFmLGVBQWUsQ0FBbUI7UUFySTNDLGlDQUFpQztRQUN4QixTQUFJLEdBQUcsQ0FBQyxDQUFDO1FBc0VsQiwrSEFBK0g7UUFDdEgsT0FBRSxHQUFHLFVBQVUsTUFBTSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDekMsaUZBQWlGO1FBQ3hFLG9CQUFlLEdBQUcsQ0FBQyxDQUFDO1FBQzdCLHlDQUF5QztRQUNoQyxhQUFRLEdBQUcsS0FBSyxDQUFDO1FBRzFCLGlFQUFpRTtRQUN4RCxxQkFBZ0IsR0FBRyxLQUFLLENBQUM7UUEwQmxDLCtDQUErQztRQUNyQyxnQkFBVyxHQUFvQyxJQUFJLFlBQVksRUFBRSxDQUFDO1FBQ3ZDLGNBQVMsR0FBRyxJQUFJLENBQUM7UUFPL0MsWUFBTyxHQUFHLEdBQUcsSUFBSSxDQUFDLEVBQUUsUUFBUSxDQUFDO1FBQzdCLGtCQUFhLEdBQUcsR0FBRyxJQUFJLENBQUMsRUFBRSxlQUFlLENBQUM7UUFDMUMsZUFBVSxHQUFHLEdBQUcsSUFBSSxDQUFDLEVBQUUsWUFBWSxDQUFDO1FBQ3BDLHFCQUFnQixHQUFHLENBQUMsQ0FBQztRQUVsQixnQkFBVyxHQUFHLEtBQUssQ0FBQztRQUVwQixTQUFJLEdBQUcsQ0FBQyxDQUFDO1FBQ1QsU0FBSSxHQUFHLEdBQUcsQ0FBQztRQUNYLFdBQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNwQixtQkFBYyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzVCLGNBQVMsR0FBRyxLQUFLLENBQUM7UUFDbEIsY0FBUyxHQUFHLEtBQUssQ0FBQztRQUNsQix1QkFBa0IsR0FBRyxDQUFDLENBQUM7UUEwQ2pDLHFDQUFxQztRQUNyQyxvQkFBZSxHQUFHLENBQUMsQ0FBTSxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFPbEMsbUVBQW1FO1FBQ25FLGNBQVMsR0FBYyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUM7SUE3QzlCLENBQUM7SUEzSkosbUNBQW1DO0lBQ25DLElBQWEsR0FBRyxDQUFDLENBQUM7UUFDakIsSUFBSSxDQUFDLENBQUMsRUFBRTtZQUFFLE9BQU87U0FBRTtRQUNuQixJQUFJLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQztRQUNkLGdDQUFnQztRQUNoQyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7SUFDekIsQ0FBQztJQUNELElBQUksR0FBRztRQUNOLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQztJQUNsQixDQUFDO0lBQ0QsbUNBQW1DO0lBQ25DLElBQWEsR0FBRyxDQUFDLENBQUM7UUFDakIsSUFBSSxDQUFDLENBQUMsRUFBRTtZQUFFLE9BQU87U0FBRTtRQUNuQixJQUFJLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQztRQUNkLGdDQUFnQztRQUNoQyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7SUFDekIsQ0FBQztJQUVELElBQUksR0FBRztRQUNOLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQztJQUNsQixDQUFDO0lBR0QsMkRBQTJEO0lBQzNELElBQWEsS0FBSyxDQUFDLENBQUM7UUFDbkIsSUFBSSxDQUFDLENBQUMsRUFBRTtZQUNQLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztTQUNmO1FBRUQsSUFBSSxPQUFPLENBQUMsS0FBSyxRQUFRLElBQUksT0FBTyxDQUFDLEtBQUssUUFBUSxFQUFFO1lBQ25ELENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ2hCO1FBRUQsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUNwQixDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQztTQUNoQjtRQUVELElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDcEIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7U0FDaEI7UUFFRCxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUNuQixJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsYUFBYTtnQkFDbkQsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUU7b0JBQzVCLG1EQUFtRDtvQkFDbkQsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO2lCQUN4QjtxQkFBTSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFO29CQUMzQixDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQztpQkFDaEI7cUJBQU0sSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRTtvQkFDM0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7aUJBQ2hCO2FBQ0Q7WUFFRCxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsY0FBYztnQkFDcEQsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRTtvQkFDcEIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7aUJBQ2hCO3FCQUFNLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRTtvQkFDN0MsbURBQW1EO29CQUNuRCxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO2lCQUNsQztxQkFBTSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFO29CQUMzQixDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQztpQkFDaEI7YUFDRDtTQUNEO1FBRUQsSUFBSSxDQUFDLGNBQWMsR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsb0RBQW9EO1FBQzVGLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsdUVBQXVFO1FBRTdGLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxJQUFJLElBQUksQ0FBQyxXQUFXLEVBQUU7WUFDdkMsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7U0FDN0I7YUFBTSxJQUFJLElBQUksQ0FBQyxXQUFXLEVBQUU7WUFDNUIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLFNBQVMsR0FBRyx1QkFBdUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1NBQ3RIO1FBRUQsSUFBSSxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFO1lBQ3RDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxFQUFFO2dCQUNwQyxLQUFLLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDN0MsQ0FBQyxDQUFDLENBQUM7U0FDSDtRQUVELE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDOUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNsQyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBRUQsSUFBSSxLQUFLO1FBQ1IsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDbkIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDO1NBQ25CO1FBQ0QsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZCLENBQUM7SUFZRCxtREFBbUQ7SUFDbkQsSUFBYSxRQUFRLENBQUMsQ0FBQztRQUN0QixJQUFJLENBQUMsU0FBUyxHQUFHLENBQUMsQ0FBQztRQUNuQixzRkFBc0Y7UUFDdEYsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ2hDLElBQUksTUFBTSxJQUFJLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO1lBQ2hDLE1BQU0sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQzVDO0lBQ0YsQ0FBQztJQUVELElBQUksUUFBUTtRQUNYLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUN2QixDQUFDO0lBQ0QsMENBQTBDO0lBQzFDLElBQWEsUUFBUSxDQUFDLENBQVU7UUFDL0IsSUFBSSxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUM7UUFDbkIsc0ZBQXNGO1FBQ3RGLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNoQyxJQUFJLE1BQU0sSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtZQUNoQyxNQUFNLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLFFBQVEsR0FBRyxDQUFDLENBQUMsQ0FBQztTQUM1QztJQUNGLENBQUM7SUFDRCxJQUFJLFFBQVE7UUFDWCxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDdkIsQ0FBQztJQStCRCxlQUFlO1FBQ2Qsd0ZBQXdGO1FBQ3hGLElBQUksQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3ZFLElBQUksQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBRW5FLDJEQUEyRDtRQUMzRCxJQUFJLENBQUMsZUFBZSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBRXJDLDRDQUE0QztRQUU1Qyw0QkFBNEI7UUFDNUIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDL0IsSUFBSSxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtZQUMxQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsRUFBRTtnQkFDcEMsS0FBSyxDQUFDLElBQUksR0FBRyxRQUFRLENBQUM7Z0JBQ3RCLEtBQUssQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLHdCQUF3QixDQUFDLENBQUM7Z0JBQzlDLEtBQUssQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFDLENBQUM7Z0JBQ3ZDLEtBQUssQ0FBQyxZQUFZLENBQUMsaUJBQWlCLEVBQUUsR0FBRyxJQUFJLENBQUMsYUFBYSxJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDO2dCQUVsRixLQUFLLENBQUMsS0FBSyxHQUFHLEtBQUssR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztnQkFDL0Ysb0NBQW9DO2dCQUNwQyxJQUFJLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztnQkFFNUUsSUFBSSxLQUFLLEtBQUssQ0FBQyxFQUFFO29CQUNoQixJQUFJLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7aUJBQzlEO1lBQ0YsQ0FBQyxDQUFDLENBQUM7U0FDSDtJQUNGLENBQUM7SUFFRCxhQUFhLENBQUMsS0FBYSxFQUFFLElBQVM7UUFDckMsT0FBTyxLQUFLLENBQUM7SUFDZCxDQUFDO0lBS0Qsd0VBQXdFO0lBQ3hFLGdCQUFnQixDQUFDLEVBQU87UUFDdkIsSUFBSSxDQUFDLGVBQWUsR0FBRyxFQUFFLENBQUM7SUFDM0IsQ0FBQztJQUtELG9FQUFvRTtJQUNwRSxpQkFBaUIsQ0FBQyxFQUFPO1FBQ3hCLElBQUksQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDO0lBQ3JCLENBQUM7SUFFRCxzQ0FBc0M7SUFDdEMsVUFBVSxDQUFDLENBQU07UUFDaEIsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUM7SUFDaEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsbUJBQW1CLENBQUMsS0FBYTtRQUNoQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRTtZQUNoQixPQUFPLENBQUMsQ0FBQztTQUNUO1FBRUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMscUJBQXFCLEVBQUUsQ0FBQyxLQUFLLENBQUM7UUFDMUUsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxHQUFHLFVBQVUsQ0FBQztJQUM3QyxDQUFDO0lBRUQsb0VBQW9FO0lBQ3BFLE1BQU0sQ0FBQyxRQUFRO1FBQ2QsT0FBTyxVQUFVLFFBQVEsR0FBRyxDQUFDO0lBQzlCLENBQUM7SUFFRCwrREFBK0Q7SUFDL0QsY0FBYyxDQUFDLFFBQVE7UUFDdEIsZ0RBQWdEO1FBQ2hELHlJQUF5STtRQUN6SSxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7UUFDbEMsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMscUJBQXFCLEVBQUUsQ0FBQyxLQUFLLENBQUM7UUFDMUUsTUFBTSxTQUFTLEdBQUcsUUFBUSxHQUFHLFVBQVUsQ0FBQztRQUN4QyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxHQUFHLFNBQVMsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQ3hFLE9BQU8sT0FBTyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7SUFDM0IsQ0FBQztJQUVELDhFQUE4RTtJQUM5RSxXQUFXLENBQUMsS0FBSztRQUNoQixJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRTtZQUNoQixPQUFPLENBQUMsQ0FBQztTQUNUO1FBRUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMscUJBQXFCLEVBQUUsQ0FBQyxLQUFLLENBQUM7UUFDMUUsSUFBSSxLQUFLLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUN0QixPQUFPLFVBQVUsQ0FBQztTQUNsQjtRQUVELElBQUksS0FBSyxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDdEIsT0FBTyxDQUFDLENBQUM7U0FDVDtRQUVELG1FQUFtRTtRQUNuRSxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzlFLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsY0FBYyxDQUFDLFVBQVUsR0FBRyxDQUFDLEVBQUUsS0FBSyxHQUFHLENBQUM7UUFDdkMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxVQUFVLENBQUMsQ0FBQztRQUNuRSxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxpQkFBaUI7SUFDM0MsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxjQUFjLENBQUMsVUFBVSxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsQ0FBQztRQUN2QyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLFVBQVUsQ0FBQyxDQUFDO1FBQ25FLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLGlCQUFpQjtJQUMzQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxPQUFPO1FBQ04sT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDL0IsQ0FBQztJQUVEOzs7T0FHRztJQUNILHFCQUFxQjtRQUNwQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzFELE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDM0QsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLFNBQVMsR0FBRyxhQUFhLFFBQVEsR0FBRyxHQUFHLFlBQVksSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEdBQUcsUUFBUSxDQUFDLEVBQUUsQ0FBQztJQUM3SCxDQUFDO0lBRUQsNENBQTRDO0lBQzVDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsS0FBSztRQUNwQixJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2hELElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztJQUN6QixDQUFDO0lBRUQ7OztTQUdLO0lBQ0wsT0FBTyxDQUFDLEtBQUs7UUFDWixJQUFJLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUFFLE9BQU87U0FBRTtRQUMvQyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLElBQUksQ0FBQztRQUN4RSxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxPQUFPLEdBQUcsU0FBUyxDQUFDLENBQUM7UUFDbEUsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDbkIsSUFBSSxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEdBQUcsVUFBVSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxFQUFFO2dCQUNsRixJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQzthQUM1QjtpQkFBTTtnQkFDTixJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQzthQUM1QjtTQUNEO2FBQU07WUFDTixJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQztTQUM1QjtRQUVELElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztJQUN6QixDQUFDO0lBRUQsMkNBQTJDO0lBQzNDLE9BQU8sQ0FBQyxFQUFDLE1BQU0sRUFBQztRQUNmLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUNqQixDQUFDO0lBRUQsMEdBQTBHO0lBQzFHLFdBQVcsQ0FBQyxLQUFLO1FBQ2hCLElBQUksSUFBSSxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRTtZQUFFLE9BQU87U0FBRTtRQUNwRSxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBRS9ELElBQUksS0FBSyxDQUFDO1FBRVYsSUFDQyxLQUFLLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQyxJQUFJLElBQUksS0FBSyxDQUFDLEtBQUs7ZUFDdEMsS0FBSyxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUMsSUFBSSxJQUFJLENBQUMsRUFDakM7WUFDRCxLQUFLLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN4RDtRQUVELHlEQUF5RDtRQUN6RCxJQUFJLEtBQUssQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksR0FBRyxLQUFLLENBQUMsS0FBSyxFQUFFO1lBQzdDLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO1NBQ2pCO1FBRUQsd0RBQXdEO1FBQ3hELElBQUksS0FBSyxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUMsSUFBSSxHQUFHLENBQUMsRUFBRTtZQUNuQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQztTQUNqQjtRQUVELElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRTtZQUN4QixJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLEtBQUssQ0FBQztZQUM3QyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7U0FDeEI7SUFDRixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILFdBQVcsQ0FBQyxLQUFLLEVBQUUsS0FBSyxHQUFHLENBQUM7UUFDM0IsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3ZCLElBQUksSUFBSSxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFO1lBQUUsT0FBTztTQUFFO1FBQy9DLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxLQUFLLENBQUM7UUFDaEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDbkQsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7SUFDekIsQ0FBQztJQUVELHlDQUF5QztJQUN6QyxTQUFTO1FBQ1IsSUFBSSxDQUFDLFdBQVcsR0FBRyxLQUFLLENBQUM7SUFDMUIsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxTQUFTLENBQUMsS0FBb0IsRUFBRSxLQUFLLEdBQUcsQ0FBQztRQUN4QyxJQUFJLElBQUksQ0FBQyxnQkFBZ0IsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFO1lBQzNDLE9BQU87U0FDUDtRQUNELE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM3RCxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssV0FBVyxJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssV0FBVyxFQUFFO1lBQzNELElBQUksQ0FBQyxjQUFjLENBQUMsVUFBVSxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ3ZDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ25ELEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztTQUN2QjthQUFNLElBQUksS0FBSyxDQUFDLEdBQUcsS0FBSyxZQUFZLElBQUksS0FBSyxDQUFDLEdBQUcsS0FBSyxTQUFTLEVBQUU7WUFDakUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDdkMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDbkQsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO1NBQ3ZCO0lBQ0YsQ0FBQztJQUVNLFVBQVUsQ0FBQyxLQUFLO1FBQ3RCLE9BQU8sS0FBSyxZQUFZLFdBQVcsQ0FBQztJQUNyQyxDQUFDO0lBRUQsZ0NBQWdDO0lBQ3RCLFNBQVM7UUFDbEIsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0lBQ2xGLENBQUM7O0FBdlpELGtDQUFrQztBQUNuQixZQUFLLEdBQUcsQ0FBRSxDQUFBO21HQUZiLE1BQU07dUZBQU4sTUFBTSxzWUFSUDtRQUNWO1lBQ0MsT0FBTyxFQUFFLGlCQUFpQjtZQUMxQixXQUFXLEVBQUUsTUFBTTtZQUNuQixLQUFLLEVBQUUsSUFBSTtTQUNYO0tBQ0QsOFdBbkdTOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQTRGVDsyRkFTVyxNQUFNO2tCQXZHbEIsU0FBUzttQkFBQztvQkFDVixRQUFRLEVBQUUsd0JBQXdCO29CQUNsQyxRQUFRLEVBQUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0VBNEZUO29CQUNELFNBQVMsRUFBRTt3QkFDVjs0QkFDQyxPQUFPLEVBQUUsaUJBQWlCOzRCQUMxQixXQUFXLFFBQVE7NEJBQ25CLEtBQUssRUFBRSxJQUFJO3lCQUNYO3FCQUNEO2lCQUNEOzRKQU1hLEdBQUc7c0JBQWYsS0FBSztnQkFVTyxHQUFHO3NCQUFmLEtBQUs7Z0JBV0csSUFBSTtzQkFBWixLQUFLO2dCQUVPLEtBQUs7c0JBQWpCLEtBQUs7Z0JBcUVHLEVBQUU7c0JBQVYsS0FBSztnQkFFRyxlQUFlO3NCQUF2QixLQUFLO2dCQUVHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBRUcsS0FBSztzQkFBYixLQUFLO2dCQUVHLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFFTyxRQUFRO3NCQUFwQixLQUFLO2dCQWFPLFFBQVE7c0JBQXBCLEtBQUs7Z0JBWUksV0FBVztzQkFBcEIsTUFBTTtnQkFDOEIsU0FBUztzQkFBN0MsV0FBVzt1QkFBQyxzQkFBc0I7Z0JBQ1gsTUFBTTtzQkFBN0IsWUFBWTt1QkFBQyxRQUFRO2dCQUVGLEtBQUs7c0JBQXhCLFNBQVM7dUJBQUMsT0FBTztnQkFDUSxXQUFXO3NCQUFwQyxTQUFTO3VCQUFDLGFBQWE7Z0JBQ0osS0FBSztzQkFBeEIsU0FBUzt1QkFBQyxPQUFPIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcblx0Q29tcG9uZW50LFxuXHRIb3N0QmluZGluZyxcblx0SW5wdXQsXG5cdE91dHB1dCxcblx0RXZlbnRFbWl0dGVyLFxuXHRBZnRlclZpZXdJbml0LFxuXHRWaWV3Q2hpbGQsXG5cdEVsZW1lbnRSZWYsXG5cdFRlbXBsYXRlUmVmLFxuXHRWaWV3Q2hpbGRyZW4sXG5cdFF1ZXJ5TGlzdCxcblx0Q2hhbmdlRGV0ZWN0b3JSZWZcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7IENvbnRyb2xWYWx1ZUFjY2Vzc29yLCBOR19WQUxVRV9BQ0NFU1NPUiB9IGZyb20gXCJAYW5ndWxhci9mb3Jtc1wiO1xuaW1wb3J0IHsgRXZlbnRTZXJ2aWNlIH0gZnJvbSBcImNhcmJvbi1jb21wb25lbnRzLWFuZ3VsYXIvdXRpbHNcIjtcblxuLyoqXG4gKiBVc2VkIHRvIHNlbGVjdCBmcm9tIHJhbmdlcyBvZiB2YWx1ZXMuIFtTZWUgaGVyZV0oaHR0cHM6Ly93d3cuY2FyYm9uZGVzaWduc3lzdGVtLmNvbS9jb21wb25lbnRzL3NsaWRlci91c2FnZSkgZm9yIHVzYWdlIGluZm9ybWF0aW9uLlxuICpcbiAqIEdldCBzdGFydGVkIHdpdGggaW1wb3J0aW5nIHRoZSBtb2R1bGU6XG4gKlxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgU2xpZGVyTW9kdWxlIH0gZnJvbSAnY2FyYm9uLWNvbXBvbmVudHMtYW5ndWxhcic7XG4gKiBgYGBcbiAqXG4gKiBUaGUgc2ltcGxlc3QgcG9zc2libGUgc2xpZGVyIHVzYWdlIGxvb2tzIHNvbWV0aGluZyBsaWtlOlxuICpcbiAqIGBgYGh0bWxcbiAqXHQ8Y2RzLXNsaWRlcj48L2Nkcy1zbGlkZXI+XG4gKiBgYGBcbiAqXG4gKiBUaGF0IHdpbGwgcmVuZGVyIGEgc2xpZGVyIHdpdGhvdXQgbGFiZWxzIG9yIGFsdGVybmF0aXZlIHZhbHVlIGlucHV0LiBMYWJlbHMgY2FuIGJlIHByb3ZpZGVkIGJ5XG4gKiBlbGVtZW50cyB3aXRoIGBbbWluTGFiZWxdYCBhbmQgYFttYXhMYWJlbF1gIGF0dHJpYnV0ZXMsIGFuZCBhbiBgaW5wdXRgIChtYXkgdXNlIHRoZSBgaWJtSW5wdXRgIGRpcmVjdGl2ZSkgY2FuIGJlIHN1cHBsaWVkXG4gKiBmb3IgdXNlIGFzIGFuIGFsdGVybmF0aXZlIHZhbHVlIGZpZWxkLlxuICpcbiAqIGV4OlxuICpcbiAqIGBgYGh0bWxcbiAqIDwhLS0gRnVsbCBleGFtcGxlIC0tPlxuICogPGNkcy1zbGlkZXI+XG4gKlx0XHQ8c3BhbiBtaW5MYWJlbD4wR0I8L3NwYW4+XG4gKlx0XHQ8c3BhbiBtYXhMYWJlbD4xMDBHQjwvc3Bhbj5cbiAqXHRcdDxpbnB1dC8+XG4gKiA8L2Nkcy1zbGlkZXI+XG4gKlxuICogPCEtLSB3aXRoIGp1c3QgYW4gaW5wdXQgLS0+XG4gKiA8Y2RzLXNsaWRlcj5cbiAqXHRcdDxpbnB1dC8+XG4gKiA8L2Nkcy1zbGlkZXI+XG4gKlxuICogPCEtLSB3aXRoIGp1c3Qgb25lIGxhYmVsIC0tPlxuICogPGNkcy1zbGlkZXI+XG4gKlx0XHQ8c3BhbiBtYXhMYWJlbD5NYXhpbXVtPC9zcGFuPlxuICogPC9jZHMtc2xpZGVyPlxuICogYGBgXG4gKlxuICogU2xpZGVyIHN1cHBvcnRzIGBOZ01vZGVsYCBieSBkZWZhdWx0LCBhcyB3ZWxsIGFzIHR3byB3YXkgYmluZGluZyB0byB0aGUgYHZhbHVlYCBpbnB1dC5cbiAqXG4gKiBbU2VlIGRlbW9dKC4uLy4uLz9wYXRoPS9zdG9yeS9jb21wb25lbnRzLXNsaWRlci0tYWR2YW5jZWQpXG4gKi9cbkBDb21wb25lbnQoe1xuXHRzZWxlY3RvcjogXCJjZHMtc2xpZGVyLCBpYm0tc2xpZGVyXCIsXG5cdHRlbXBsYXRlOiBgXG5cdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFza2VsZXRvbjsgZWxzZSBza2VsZXRvblRlbXBsYXRlXCI+XG5cdFx0XHQ8bGFiZWxcblx0XHRcdFx0Km5nSWY9XCJsYWJlbFwiXG5cdFx0XHRcdFtmb3JdPVwiaWRcIlxuXHRcdFx0XHRbaWRdPVwibGFiZWxJZFwiXG5cdFx0XHRcdGNsYXNzPVwiY2RzLS1sYWJlbFwiXG5cdFx0XHRcdFtuZ0NsYXNzXT1cInsnY2RzLS1sYWJlbC0tZGlzYWJsZWQnOiBkaXNhYmxlZH1cIj5cblx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1RlbXBsYXRlKGxhYmVsKVwiPnt7bGFiZWx9fTwvbmctY29udGFpbmVyPlxuXHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJpc1RlbXBsYXRlKGxhYmVsKVwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImxhYmVsXCI+PC9uZy10ZW1wbGF0ZT5cblx0XHRcdDwvbGFiZWw+XG5cdFx0XHQ8ZGl2XG5cdFx0XHRcdGNsYXNzPVwiY2RzLS1zbGlkZXItY29udGFpbmVyXCJcblx0XHRcdFx0W25nQ2xhc3NdPVwieyAnY2RzLS1zbGlkZXItY29udGFpbmVyLS1yZWFkb25seSc6IHJlYWRvbmx5IH1cIj5cblx0XHRcdFx0PGxhYmVsIFtpZF09XCJib3R0b21SYW5nZUlkXCIgY2xhc3M9XCJjZHMtLXNsaWRlcl9fcmFuZ2UtbGFiZWxcIj5cblx0XHRcdFx0XHQ8bmctY29udGVudCBzZWxlY3Q9XCJbbWluTGFiZWxdXCI+PC9uZy1jb250ZW50PlxuXHRcdFx0XHQ8L2xhYmVsPlxuXHRcdFx0XHQ8ZGl2XG5cdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXNsaWRlclwiXG5cdFx0XHRcdFx0KGNsaWNrKT1cIm9uQ2xpY2soJGV2ZW50KVwiXG5cdFx0XHRcdFx0W25nQ2xhc3NdPVwie1xuXHRcdFx0XHRcdFx0J2Nkcy0tc2xpZGVyLS1kaXNhYmxlZCc6IGRpc2FibGVkLFxuXHRcdFx0XHRcdFx0J2Nkcy0tc2xpZGVyLS1yZWFkb25seSc6IHJlYWRvbmx5XG5cdFx0XHRcdFx0fVwiPlxuXHRcdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhaXNSYW5nZSgpXCI+XG5cdFx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS1zbGlkZXJfX3RodW1iLXdyYXBwZXJcIlxuXHRcdFx0XHRcdFx0XHRbbmdTdHlsZV09XCJ7aW5zZXRJbmxpbmVTdGFydDogZ2V0RnJhY3Rpb25Db21wbGV0ZSh2YWx1ZSkgKiAxMDAgKyAnJSd9XCI+XG5cdFx0XHRcdFx0XHRcdDxkaXZcblx0XHRcdFx0XHRcdFx0XHQjdGh1bWJzXG5cdFx0XHRcdFx0XHRcdFx0cm9sZT1cInNsaWRlclwiXG5cdFx0XHRcdFx0XHRcdFx0W2lkXT1cImlkXCJcblx0XHRcdFx0XHRcdFx0XHRbYXR0ci5hcmlhLWxhYmVsbGVkYnldPVwibGFiZWxJZFwiXG5cdFx0XHRcdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXNsaWRlcl9fdGh1bWJcIlxuXHRcdFx0XHRcdFx0XHRcdHRhYmluZGV4PVwiMFwiXG5cdFx0XHRcdFx0XHRcdFx0KG1vdXNlZG93bik9XCJvbk1vdXNlRG93bigkZXZlbnQpXCJcblx0XHRcdFx0XHRcdFx0XHQoa2V5ZG93bik9XCJvbktleURvd24oJGV2ZW50KVwiPlxuXHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdDwvbmctY29udGFpbmVyPlxuXHRcdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCJpc1JhbmdlKClcIj5cblx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXNsaWRlcl9fdGh1bWItd3JhcHBlclwiXG5cdFx0XHRcdFx0XHQgW25nU3R5bGVdPVwie2luc2V0SW5saW5lU3RhcnQ6IGdldEZyYWN0aW9uQ29tcGxldGUodGh1bWIpICogMTAwICsgJyUnfVwiXG5cdFx0XHRcdFx0XHQgKm5nRm9yPVwibGV0IHRodW1iIG9mIHZhbHVlOyBsZXQgaSA9IGluZGV4OyB0cmFja0J5OiB0cmFja1RodW1ic0J5XCI+XG5cdFx0XHRcdFx0XHRcdDxkaXZcblx0XHRcdFx0XHRcdFx0XHQjdGh1bWJzXG5cdFx0XHRcdFx0XHRcdFx0cm9sZT1cInNsaWRlclwiXG5cdFx0XHRcdFx0XHRcdFx0W2lkXT1cImlkICsgKGkgPiAwID8gJy0nICsgaSA6ICcnKVwiXG5cdFx0XHRcdFx0XHRcdFx0W2F0dHIuYXJpYS1sYWJlbGxlZGJ5XT1cImxhYmVsSWRcIlxuXHRcdFx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1zbGlkZXJfX3RodW1iXCJcblx0XHRcdFx0XHRcdFx0XHR0YWJpbmRleD1cIjBcIlxuXHRcdFx0XHRcdFx0XHRcdChtb3VzZWRvd24pPVwib25Nb3VzZURvd24oJGV2ZW50LCBpKVwiXG5cdFx0XHRcdFx0XHRcdFx0KGtleWRvd24pPVwib25LZXlEb3duKCRldmVudCwgaSlcIj5cblx0XHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHQ8L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0XHQ8ZGl2XG5cdFx0XHRcdFx0XHQjdHJhY2tcblx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1zbGlkZXJfX3RyYWNrXCI+XG5cdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0PGRpdlxuXHRcdFx0XHRcdFx0I2ZpbGxlZFRyYWNrXG5cdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tc2xpZGVyX19maWxsZWQtdHJhY2tcIj5cblx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHQ8aW5wdXRcblx0XHRcdFx0XHRcdCNyYW5nZVxuXHRcdFx0XHRcdFx0YXJpYS1sYWJlbD1cInNsaWRlclwiXG5cdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tc2xpZGVyX19pbnB1dFwiXG5cdFx0XHRcdFx0XHR0eXBlPVwicmFuZ2VcIlxuXHRcdFx0XHRcdFx0W3N0ZXBdPVwic3RlcFwiXG5cdFx0XHRcdFx0XHRbbWluXT1cIm1pblwiXG5cdFx0XHRcdFx0XHRbbWF4XT1cIm1heFwiXG5cdFx0XHRcdFx0XHRbdmFsdWVdPVwidmFsdWUudG9TdHJpbmcoKVwiPlxuXHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0PGxhYmVsIFtpZF09XCJ0b3BSYW5nZUlkXCIgY2xhc3M9XCJjZHMtLXNsaWRlcl9fcmFuZ2UtbGFiZWxcIj5cblx0XHRcdFx0XHQ8bmctY29udGVudCBzZWxlY3Q9XCJbbWF4TGFiZWxdXCI+PC9uZy1jb250ZW50PlxuXHRcdFx0XHQ8L2xhYmVsPlxuXHRcdFx0XHQ8bmctY29udGVudCBzZWxlY3Q9XCJpbnB1dFwiPjwvbmctY29udGVudD5cblx0XHRcdDwvZGl2PlxuXHRcdDwvbmctY29udGFpbmVyPlxuXG5cdFx0PG5nLXRlbXBsYXRlICNza2VsZXRvblRlbXBsYXRlPlxuXHRcdFx0PGxhYmVsICpuZ0lmPVwibGFiZWxcIiBjbGFzcz1cImNkcy0tbGFiZWwgY2RzLS1za2VsZXRvblwiPjwvbGFiZWw+XG5cdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS1zbGlkZXItY29udGFpbmVyIGNkcy0tc2tlbGV0b25cIj5cblx0XHRcdFx0PHNwYW4gY2xhc3M9XCJjZHMtLXNsaWRlcl9fcmFuZ2UtbGFiZWxcIj48L3NwYW4+XG5cdFx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXNsaWRlclwiPlxuXHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXNsaWRlcl9fdGh1bWJcIj48L2Rpdj5cblx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS1zbGlkZXJfX3RyYWNrXCI+PC9kaXY+XG5cdFx0XHRcdFx0PGRpdiBjbGFzcz1cImNkcy0tc2xpZGVyX19maWxsZWQtdHJhY2tcIj48L2Rpdj5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdDxzcGFuIGNsYXNzPVwiY2RzLS1zbGlkZXJfX3JhbmdlLWxhYmVsXCI+PC9zcGFuPlxuXHRcdFx0PC9kaXY+XG5cdFx0PC9uZy10ZW1wbGF0ZT5cblx0YCxcblx0cHJvdmlkZXJzOiBbXG5cdFx0e1xuXHRcdFx0cHJvdmlkZTogTkdfVkFMVUVfQUNDRVNTT1IsXG5cdFx0XHR1c2VFeGlzdGluZzogU2xpZGVyLFxuXHRcdFx0bXVsdGk6IHRydWVcblx0XHR9XG5cdF1cbn0pXG5leHBvcnQgY2xhc3MgU2xpZGVyIGltcGxlbWVudHMgQWZ0ZXJWaWV3SW5pdCwgQ29udHJvbFZhbHVlQWNjZXNzb3Ige1xuXHQvKiogVXNlZCB0byBnZW5lcmF0ZSB1bmlxdWUgSURzICovXG5cdHByaXZhdGUgc3RhdGljIGNvdW50ID0gMDtcblxuXHQvKiogVGhlIGxvd2VyIGJvdW5kIG9mIG91ciByYW5nZSAqL1xuXHRASW5wdXQoKSBzZXQgbWluKHYpIHtcblx0XHRpZiAoIXYpIHsgcmV0dXJuOyB9XG5cdFx0dGhpcy5fbWluID0gdjtcblx0XHQvLyBmb3JjZSB0aGUgY29tcG9uZW50IHRvIHVwZGF0ZVxuXHRcdHRoaXMudmFsdWUgPSB0aGlzLnZhbHVlO1xuXHR9XG5cdGdldCBtaW4oKSB7XG5cdFx0cmV0dXJuIHRoaXMuX21pbjtcblx0fVxuXHQvKiogVGhlIHVwcGVyIGJvdW5kIG9mIG91ciByYW5nZSAqL1xuXHRASW5wdXQoKSBzZXQgbWF4KHYpIHtcblx0XHRpZiAoIXYpIHsgcmV0dXJuOyB9XG5cdFx0dGhpcy5fbWF4ID0gdjtcblx0XHQvLyBmb3JjZSB0aGUgY29tcG9uZW50IHRvIHVwZGF0ZVxuXHRcdHRoaXMudmFsdWUgPSB0aGlzLnZhbHVlO1xuXHR9XG5cblx0Z2V0IG1heCgpIHtcblx0XHRyZXR1cm4gdGhpcy5fbWF4O1xuXHR9XG5cdC8qKiBUaGUgaW50ZXJ2YWwgZm9yIG91ciByYW5nZSAqL1xuXHRASW5wdXQoKSBzdGVwID0gMTtcblx0LyoqIFNldCB0aGUgaW5pdGlhbCB2YWx1ZS4gQXZhaWxhYmxlIGZvciB0d28gd2F5IGJpbmRpbmcgKi9cblx0QElucHV0KCkgc2V0IHZhbHVlKHYpIHtcblx0XHRpZiAoIXYpIHtcblx0XHRcdHYgPSBbdGhpcy5taW5dO1xuXHRcdH1cblxuXHRcdGlmICh0eXBlb2YgdiA9PT0gXCJudW1iZXJcIiB8fCB0eXBlb2YgdiA9PT0gXCJzdHJpbmdcIikge1xuXHRcdFx0diA9IFtOdW1iZXIodildO1xuXHRcdH1cblxuXHRcdGlmICh2WzBdIDwgdGhpcy5taW4pIHtcblx0XHRcdHZbMF0gPSB0aGlzLm1pbjtcblx0XHR9XG5cblx0XHRpZiAodlswXSA+IHRoaXMubWF4KSB7XG5cdFx0XHR2WzBdID0gdGhpcy5tYXg7XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMuaXNSYW5nZSgpKSB7XG5cdFx0XHRpZiAodGhpcy5fcHJldmlvdXNWYWx1ZVswXSAhPT0gdlswXSkgeyAvLyBsZWZ0IG1vdmVkXG5cdFx0XHRcdGlmICh2WzBdID4gdlsxXSAtIHRoaXMuc3RlcCkge1xuXHRcdFx0XHRcdC8vIHN0b3AgdGhlIGxlZnQgaGFuZGxlIGlmIHN1cnBhc3NpbmcgdGhlIHJpZ2h0IG9uZVxuXHRcdFx0XHRcdHZbMF0gPSB2W