carbon-components-angular
Version:
Next generation components
632 lines (630 loc) • 63.7 kB
JavaScript
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._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;
}
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) {
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.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) {
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) {
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" }, 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">
<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}">
<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">
<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}">
<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
}], 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2xpZGVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zbGlkZXIvc2xpZGVyLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ04sU0FBUyxFQUNULFdBQVcsRUFDWCxLQUFLLEVBQ0wsTUFBTSxFQUNOLFlBQVksRUFFWixTQUFTLEVBRVQsV0FBVyxFQUNYLFlBQVksRUFHWixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQXdCLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7Ozs7QUFHekU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EyQ0c7QUFtR0gsTUFBTSxPQUFPLE1BQU07SUE4SWxCLFlBQ1csVUFBc0IsRUFDdEIsWUFBMEIsRUFDNUIsZUFBa0M7UUFGaEMsZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQUN0QixpQkFBWSxHQUFaLFlBQVksQ0FBYztRQUM1QixvQkFBZSxHQUFmLGVBQWUsQ0FBbUI7UUF4SDNDLGlDQUFpQztRQUN4QixTQUFJLEdBQUcsQ0FBQyxDQUFDO1FBc0VsQiwrSEFBK0g7UUFDdEgsT0FBRSxHQUFHLFVBQVUsTUFBTSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDekMsaUZBQWlGO1FBQ3hFLG9CQUFlLEdBQUcsQ0FBQyxDQUFDO1FBQzdCLHlDQUF5QztRQUNoQyxhQUFRLEdBQUcsS0FBSyxDQUFDO1FBRzFCLGlFQUFpRTtRQUN4RCxxQkFBZ0IsR0FBRyxLQUFLLENBQUM7UUFjbEMsK0NBQStDO1FBQ3JDLGdCQUFXLEdBQW9DLElBQUksWUFBWSxFQUFFLENBQUM7UUFDdkMsY0FBUyxHQUFHLElBQUksQ0FBQztRQU8vQyxZQUFPLEdBQUcsR0FBRyxJQUFJLENBQUMsRUFBRSxRQUFRLENBQUM7UUFDN0Isa0JBQWEsR0FBRyxHQUFHLElBQUksQ0FBQyxFQUFFLGVBQWUsQ0FBQztRQUMxQyxlQUFVLEdB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Q0Q7NEpBTWEsR0FBRztzQkFBZixLQUFLO2dCQVVPLEdBQUc7c0JBQWYsS0FBSztnQkFXRyxJQUFJO3NCQUFaLEtBQUs7Z0JBRU8sS0FBSztzQkFBakIsS0FBSztnQkFxRUcsRUFBRTtzQkFBVixLQUFLO2dCQUVHLGVBQWU7c0JBQXZCLEtBQUs7Z0JBRUcsUUFBUTtzQkFBaEIsS0FBSztnQkFFRyxLQUFLO3NCQUFiLEtBQUs7Z0JBRUcsZ0JBQWdCO3NCQUF4QixLQUFLO2dCQUVPLFFBQVE7c0JBQXBCLEtBQUs7Z0JBYUksV0FBVztzQkFBcEIsTUFBTTtnQkFDOEIsU0FBUztzQkFBN0MsV0FBVzt1QkFBQyxzQkFBc0I7Z0JBQ1gsTUFBTTtzQkFBN0IsWUFBWTt1QkFBQyxRQUFRO2dCQUVGLEtBQUs7c0JBQXhCLFNBQVM7dUJBQUMsT0FBTztnQkFDUSxXQUFXO3NCQUFwQyxTQUFTO3VCQUFDLGFBQWE7Z0JBQ0osS0FBSztzQkFBeEIsU0FBUzt1QkFBQyxPQUFPIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcblx0Q29tcG9uZW50LFxuXHRIb3N0QmluZGluZyxcblx0SW5wdXQsXG5cdE91dHB1dCxcblx0RXZlbnRFbWl0dGVyLFxuXHRBZnRlclZpZXdJbml0LFxuXHRWaWV3Q2hpbGQsXG5cdEVsZW1lbnRSZWYsXG5cdFRlbXBsYXRlUmVmLFxuXHRWaWV3Q2hpbGRyZW4sXG5cdFF1ZXJ5TGlzdCxcblx0Q2hhbmdlRGV0ZWN0b3JSZWZcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7IENvbnRyb2xWYWx1ZUFjY2Vzc29yLCBOR19WQUxVRV9BQ0NFU1NPUiB9IGZyb20gXCJAYW5ndWxhci9mb3Jtc1wiO1xuaW1wb3J0IHsgRXZlbnRTZXJ2aWNlIH0gZnJvbSBcImNhcmJvbi1jb21wb25lbnRzLWFuZ3VsYXIvdXRpbHNcIjtcblxuLyoqXG4gKiBVc2VkIHRvIHNlbGVjdCBmcm9tIHJhbmdlcyBvZiB2YWx1ZXMuIFtTZWUgaGVyZV0oaHR0cHM6Ly93d3cuY2FyYm9uZGVzaWduc3lzdGVtLmNvbS9jb21wb25lbnRzL3NsaWRlci91c2FnZSkgZm9yIHVzYWdlIGluZm9ybWF0aW9uLlxuICpcbiAqIEdldCBzdGFydGVkIHdpdGggaW1wb3J0aW5nIHRoZSBtb2R1bGU6XG4gKlxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgU2xpZGVyTW9kdWxlIH0gZnJvbSAnY2FyYm9uLWNvbXBvbmVudHMtYW5ndWxhcic7XG4gKiBgYGBcbiAqXG4gKiBUaGUgc2ltcGxlc3QgcG9zc2libGUgc2xpZGVyIHVzYWdlIGxvb2tzIHNvbWV0aGluZyBsaWtlOlxuICpcbiAqIGBgYGh0bWxcbiAqXHQ8Y2RzLXNsaWRlcj48L2Nkcy1zbGlkZXI+XG4gKiBgYGBcbiAqXG4gKiBUaGF0IHdpbGwgcmVuZGVyIGEgc2xpZGVyIHdpdGhvdXQgbGFiZWxzIG9yIGFsdGVybmF0aXZlIHZhbHVlIGlucHV0LiBMYWJlbHMgY2FuIGJlIHByb3ZpZGVkIGJ5XG4gKiBlbGVtZW50cyB3aXRoIGBbbWluTGFiZWxdYCBhbmQgYFttYXhMYWJlbF1gIGF0dHJpYnV0ZXMsIGFuZCBhbiBgaW5wdXRgIChtYXkgdXNlIHRoZSBgaWJtSW5wdXRgIGRpcmVjdGl2ZSkgY2FuIGJlIHN1cHBsaWVkXG4gKiBmb3IgdXNlIGFzIGFuIGFsdGVybmF0aXZlIHZhbHVlIGZpZWxkLlxuICpcbiAqIGV4OlxuICpcbiAqIGBgYGh0bWxcbiAqIDwhLS0gRnVsbCBleGFtcGxlIC0tPlxuICogPGNkcy1zbGlkZXI+XG4gKlx0XHQ8c3BhbiBtaW5MYWJlbD4wR0I8L3NwYW4+XG4gKlx0XHQ8c3BhbiBtYXhMYWJlbD4xMDBHQjwvc3Bhbj5cbiAqXHRcdDxpbnB1dC8+XG4gKiA8L2Nkcy1zbGlkZXI+XG4gKlxuICogPCEtLSB3aXRoIGp1c3QgYW4gaW5wdXQgLS0+XG4gKiA8Y2RzLXNsaWRlcj5cbiAqXHRcdDxpbnB1dC8+XG4gKiA8L2Nkcy1zbGlkZXI+XG4gKlxuICogPCEtLSB3aXRoIGp1c3Qgb25lIGxhYmVsIC0tPlxuICogPGNkcy1zbGlkZXI+XG4gKlx0XHQ8c3BhbiBtYXhMYWJlbD5NYXhpbXVtPC9zcGFuPlxuICogPC9jZHMtc2xpZGVyPlxuICogYGBgXG4gKlxuICogU2xpZGVyIHN1cHBvcnRzIGBOZ01vZGVsYCBieSBkZWZhdWx0LCBhcyB3ZWxsIGFzIHR3byB3YXkgYmluZGluZyB0byB0aGUgYHZhbHVlYCBpbnB1dC5cbiAqXG4gKiBbU2VlIGRlbW9dKC4uLy4uLz9wYXRoPS9zdG9yeS9jb21wb25lbnRzLXNsaWRlci0tYWR2YW5jZWQpXG4gKi9cbkBDb21wb25lbnQoe1xuXHRzZWxlY3RvcjogXCJjZHMtc2xpZGVyLCBpYm0tc2xpZGVyXCIsXG5cdHRlbXBsYXRlOiBgXG5cdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFza2VsZXRvbjsgZWxzZSBza2VsZXRvblRlbXBsYXRlXCI+XG5cdFx0XHQ8bGFiZWxcblx0XHRcdFx0Km5nSWY9XCJsYWJlbFwiXG5cdFx0XHRcdFtmb3JdPVwiaWRcIlxuXHRcdFx0XHRbaWRdPVwibGFiZWxJZFwiXG5cdFx0XHRcdGNsYXNzPVwiY2RzLS1sYWJlbFwiXG5cdFx0XHRcdFtuZ0NsYXNzXT1cInsnY2RzLS1sYWJlbC0tZGlzYWJsZWQnOiBkaXNhYmxlZH1cIj5cblx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1RlbXBsYXRlKGxhYmVsKVwiPnt7bGFiZWx9fTwvbmctY29udGFpbmVyPlxuXHRcdFx0XHQ8bmctdGVtcGxhdGUgKm5nSWY9XCJpc1RlbXBsYXRlKGxhYmVsKVwiIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImxhYmVsXCI+PC9uZy10ZW1wbGF0ZT5cblx0XHRcdDwvbGFiZWw+XG5cdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS1zbGlkZXItY29udGFpbmVyXCI+XG5cdFx0XHRcdDxsYWJlbCBbaWRdPVwiYm90dG9tUmFuZ2VJZFwiIGNsYXNzPVwiY2RzLS1zbGlkZXJfX3JhbmdlLWxhYmVsXCI+XG5cdFx0XHRcdFx0PG5nLWNvbnRlbnQgc2VsZWN0PVwiW21pbkxhYmVsXVwiPjwvbmctY29udGVudD5cblx0XHRcdFx0PC9sYWJlbD5cblx0XHRcdFx0PGRpdlxuXHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1zbGlkZXJcIlxuXHRcdFx0XHRcdChjbGljayk9XCJvbkNsaWNrKCRldmVudClcIlxuXHRcdFx0XHRcdFtuZ0NsYXNzXT1cInsnY2RzLS1zbGlkZXItLWRpc2FibGVkJzogZGlzYWJsZWR9XCI+XG5cdFx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc1JhbmdlKClcIj5cblx0XHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXNsaWRlcl9fdGh1bWItd3JhcHBlclwiXG5cdFx0XHRcdFx0XHRcdFtuZ1N0eWxlXT1cIntpbnNldElubGluZVN0YXJ0OiBnZXRGcmFjdGlvbkNvbXBsZXRlKHZhbHVlKSAqIDEwMCArICclJ31cIj5cblx0XHRcdFx0XHRcdFx0PGRpdlxuXHRcdFx0XHRcdFx0XHRcdCN0aHVtYnNcblx0XHRcdFx0XHRcdFx0XHRyb2xlPVwic2xpZGVyXCJcblx0XHRcdFx0XHRcdFx0XHRbaWRdPVwiaWRcIlxuXHRcdFx0XHRcdFx0XHRcdFthdHRyLmFyaWEtbGFiZWxsZWRieV09XCJsYWJlbElkXCJcblx0XHRcdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tc2xpZGVyX190aHVtYlwiXG5cdFx0XHRcdFx0XHRcdFx0dGFiaW5kZXg9XCIwXCJcblx0XHRcdFx0XHRcdFx0XHQobW91c2Vkb3duKT1cIm9uTW91c2VEb3duKCRldmVudClcIlxuXHRcdFx0XHRcdFx0XHRcdChrZXlkb3duKT1cIm9uS2V5RG93bigkZXZlbnQpXCI+XG5cdFx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0PC9uZy1jb250YWluZXI+XG5cdFx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cImlzUmFuZ2UoKVwiPlxuXHRcdFx0XHRcdFx0PGRpdiBjbGFzcz1cImNkcy0tc2xpZGVyX190aHVtYi13cmFwcGVyXCJcblx0XHRcdFx0XHRcdCBbbmdTdHlsZV09XCJ7aW5zZXRJbmxpbmVTdGFydDogZ2V0RnJhY3Rpb25Db21wbGV0ZSh0aHVtYikgKiAxMDAgKyAnJSd9XCJcblx0XHRcdFx0XHRcdCAqbmdGb3I9XCJsZXQgdGh1bWIgb2YgdmFsdWU7IGxldCBpID0gaW5kZXg7IHRyYWNrQnk6IHRyYWNrVGh1bWJzQnlcIj5cblx0XHRcdFx0XHRcdFx0PGRpdlxuXHRcdFx0XHRcdFx0XHRcdCN0aHVtYnNcblx0XHRcdFx0XHRcdFx0XHRyb2xlPVwic2xpZGVyXCJcblx0XHRcdFx0XHRcdFx0XHRbaWRdPVwiaWQgKyAoaSA+IDAgPyAnLScgKyBpIDogJycpXCJcblx0XHRcdFx0XHRcdFx0XHRbYXR0ci5hcmlhLWxhYmVsbGVkYnldPVwibGFiZWxJZFwiXG5cdFx0XHRcdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXNsaWRlcl9fdGh1bWJcIlxuXHRcdFx0XHRcdFx0XHRcdHRhYmluZGV4PVwiMFwiXG5cdFx0XHRcdFx0XHRcdFx0KG1vdXNlZG93bik9XCJvbk1vdXNlRG93bigkZXZlbnQsIGkpXCJcblx0XHRcdFx0XHRcdFx0XHQoa2V5ZG93bik9XCJvbktleURvd24oJGV2ZW50LCBpKVwiPlxuXHRcdFx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdDwvbmctY29udGFpbmVyPlxuXHRcdFx0XHRcdDxkaXZcblx0XHRcdFx0XHRcdCN0cmFja1xuXHRcdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXNsaWRlcl9fdHJhY2tcIj5cblx0XHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0XHQ8ZGl2XG5cdFx0XHRcdFx0XHQjZmlsbGVkVHJhY2tcblx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1zbGlkZXJfX2ZpbGxlZC10cmFja1wiPlxuXHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdDxpbnB1dFxuXHRcdFx0XHRcdFx0I3JhbmdlXG5cdFx0XHRcdFx0XHRhcmlhLWxhYmVsPVwic2xpZGVyXCJcblx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1zbGlkZXJfX2lucHV0XCJcblx0XHRcdFx0XHRcdHR5cGU9XCJyYW5nZVwiXG5cdFx0XHRcdFx0XHRbc3RlcF09XCJzdGVwXCJcblx0XHRcdFx0XHRcdFttaW5dPVwibWluXCJcblx0XHRcdFx0XHRcdFttYXhdPVwibWF4XCJcblx0XHRcdFx0XHRcdFt2YWx1ZV09XCJ2YWx1ZS50b1N0cmluZygpXCI+XG5cdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHQ8bGFiZWwgW2lkXT1cInRvcFJhbmdlSWRcIiBjbGFzcz1cImNkcy0tc2xpZGVyX19yYW5nZS1sYWJlbFwiPlxuXHRcdFx0XHRcdDxuZy1jb250ZW50IHNlbGVjdD1cIlttYXhMYWJlbF1cIj48L25nLWNvbnRlbnQ+XG5cdFx0XHRcdDwvbGFiZWw+XG5cdFx0XHRcdDxuZy1jb250ZW50IHNlbGVjdD1cImlucHV0XCI+PC9uZy1jb250ZW50PlxuXHRcdFx0PC9kaXY+XG5cdFx0PC9uZy1jb250YWluZXI+XG5cblx0XHQ8bmctdGVtcGxhdGUgI3NrZWxldG9uVGVtcGxhdGU+XG5cdFx0XHQ8bGFiZWwgKm5nSWY9XCJsYWJlbFwiIGNsYXNzPVwiY2RzLS1sYWJlbCBjZHMtLXNrZWxldG9uXCI+PC9sYWJlbD5cblx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXNsaWRlci1jb250YWluZXIgY2RzLS1za2VsZXRvblwiPlxuXHRcdFx0XHQ8c3BhbiBjbGFzcz1cImNkcy0tc2xpZGVyX19yYW5nZS1sYWJlbFwiPjwvc3Bhbj5cblx0XHRcdFx0PGRpdiBjbGFzcz1cImNkcy0tc2xpZGVyXCI+XG5cdFx0XHRcdFx0PGRpdiBjbGFzcz1cImNkcy0tc2xpZGVyX190aHVtYlwiPjwvZGl2PlxuXHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXNsaWRlcl9fdHJhY2tcIj48L2Rpdj5cblx0XHRcdFx0XHQ8ZGl2IGNsYXNzPVwiY2RzLS1zbGlkZXJfX2ZpbGxlZC10cmFja1wiPjwvZGl2PlxuXHRcdFx0XHQ8L2Rpdj5cblx0XHRcdFx0PHNwYW4gY2xhc3M9XCJjZHMtLXNsaWRlcl9fcmFuZ2UtbGFiZWxcIj48L3NwYW4+XG5cdFx0XHQ8L2Rpdj5cblx0XHQ8L25nLXRlbXBsYXRlPlxuXHRgLFxuXHRwcm92aWRlcnM6IFtcblx0XHR7XG5cdFx0XHRwcm92aWRlOiBOR19WQUxVRV9BQ0NFU1NPUixcblx0XHRcdHVzZUV4aXN0aW5nOiBTbGlkZXIsXG5cdFx0XHRtdWx0aTogdHJ1ZVxuXHRcdH1cblx0XVxufSlcbmV4cG9ydCBjbGFzcyBTbGlkZXIgaW1wbGVtZW50cyBBZnRlclZpZXdJbml0LCBDb250cm9sVmFsdWVBY2Nlc3NvciB7XG5cdC8qKiBVc2VkIHRvIGdlbmVyYXRlIHVuaXF1ZSBJRHMgKi9cblx0cHJpdmF0ZSBzdGF0aWMgY291bnQgPSAwO1xuXG5cdC8qKiBUaGUgbG93ZXIgYm91bmQgb2Ygb3VyIHJhbmdlICovXG5cdEBJbnB1dCgpIHNldCBtaW4odikge1xuXHRcdGlmICghdikgeyByZXR1cm47IH1cblx0XHR0aGlzLl9taW4gPSB2O1xuXHRcdC8vIGZvcmNlIHRoZSBjb21wb25lbnQgdG8gdXBkYXRlXG5cdFx0dGhpcy52YWx1ZSA9IHRoaXMudmFsdWU7XG5cdH1cblx0Z2V0IG1pbigpIHtcblx0XHRyZXR1cm4gdGhpcy5fbWluO1xuXHR9XG5cdC8qKiBUaGUgdXBwZXIgYm91bmQgb2Ygb3VyIHJhbmdlICovXG5cdEBJbnB1dCgpIHNldCBtYXgodikge1xuXHRcdGlmICghdikgeyByZXR1cm47IH1cblx0XHR0aGlzLl9tYXggPSB2O1xuXHRcdC8vIGZvcmNlIHRoZSBjb21wb25lbnQgdG8gdXBkYXRlXG5cdFx0dGhpcy52YWx1ZSA9IHRoaXMudmFsdWU7XG5cdH1cblxuXHRnZXQgbWF4KCkge1xuXHRcdHJldHVybiB0aGlzLl9tYXg7XG5cdH1cblx0LyoqIFRoZSBpbnRlcnZhbCBmb3Igb3VyIHJhbmdlICovXG5cdEBJbnB1dCgpIHN0ZXAgPSAxO1xuXHQvKiogU2V0IHRoZSBpbml0aWFsIHZhbHVlLiBBdmFpbGFibGUgZm9yIHR3byB3YXkgYmluZGluZyAqL1xuXHRASW5wdXQoKSBzZXQgdmFsdWUodikge1xuXHRcdGlmICghdikge1xuXHRcdFx0diA9IFt0aGlzLm1pbl07XG5cdFx0fVxuXG5cdFx0aWYgKHR5cGVvZiB2ID09PSBcIm51bWJlclwiIHx8IHR5cGVvZiB2ID09PSBcInN0cmluZ1wiKSB7XG5cdFx0XHR2ID0gW051bWJlcih2KV07XG5cdFx0fVxuXG5cdFx0aWYgKHZbMF0gPCB0aGlzLm1pbikge1xuXHRcdFx0dlswXSA9IHRoaXMubWluO1xuXHRcdH1cblxuXHRcdGlmICh2WzBdID4gdGhpcy5tYXgpIHtcblx0XHRcdHZbMF0gPSB0aGlzLm1heDtcblx0XHR9XG5cblx0XHRpZiAodGhpcy5pc1JhbmdlKCkpIHtcblx0XHRcdGlmICh0aGlzLl9wcmV2aW91c1ZhbHVlWzBdICE9PSB2WzBdKSB7IC8vIGxlZnQgbW92ZWRcblx0XHRcdFx0aWYgKHZbMF0gPiB2WzFdIC0gdGhpcy5zdGVwKSB7XG5cdFx0XHRcdFx0Ly8gc3RvcCB0aGUgbGVmdCBoYW5kbGUgaWYgc3VycGFzc2luZyB0aGUgcmlnaHQgb25lXG5cdFx0XHRcdFx0dlswXSA9IHZbMV0gLSB0aGlzLnN0ZXA7XG5cdFx0XHRcdH0gZWxzZSBpZiAodlswXSA+IHRoaXMubWF4KSB7XG5cdFx0XHRcdFx0dlswXSA9IHRoaXMubWF4O1xuXHRcdFx0XHR9IGVsc2UgaWYgKHZbMF0gPCB0aGlzLm1pbikge1xuXHRcdFx0XHRcdHZbMF0gPSB0aGlzLm1pbjtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHRpZiAodGhpcy5fcHJldmlvdXNWYWx1ZVsxXSAhPT0gdlsxXSkgeyAvLyByaWdodCBtb3ZlZFxuXHRcdFx0XHRpZiAodlsxXSA+IHRoaXMubWF4KSB7XG5cdFx0XHRcdFx0dlsxXSA9IHRoaXMubWF4O1xuXHRcdFx0XHR9IGVsc2UgaWYgKHZbMV0gPCB0aGlzLl92YWx1ZVswXSArIHRoaXMuc3RlcCkge1xuXHRcdFx0XHRcdC8vIHN0b3AgdGhlIHJpZ2h0IGhhbmRsZSBpZiBzdXJwYXNzaW5nIHRoZSBsZWZ0IG9uZVxuXHRcdFx0XHRcdHZbMV0gPSB0aGlzLl92YWx1ZVswXSArIHRoaXMuc3RlcDtcblx0XHRcdFx0fSBlbHNlIGlmICh2WzFdIDwgdGhpcy5taW4pIHtcblx0XHRcdFx0XHR2WzFdID0gdGhpcy5taW47XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLl9wcmV2aW91c1ZhbHVlID0gWy4uLnRoaXMuX3ZhbHVlXTsgLy8gc3RvcmUgYSBjb3B5LCBlbmFibGUgZGV0ZWN0aW9uIHdoaWNoIGhhbmRsZSBtb3ZlZFxuXHRcdHRoaXMuX3ZhbHVlID0gWy4uLnZdOyAvLyB0cmlnZ2VycyBjaGFuZ2UgZGV0ZWN0aW9uIHdoZW4gbmdNb2RlbCB2YWx1ZSBpcyBhbiBhcnJheSAoZm9yIHJhbmdlKVxuXG5cdFx0aWYgKHRoaXMuaXNSYW5nZSgpICYmIHRoaXMuZmlsbGVkVHJhY2spIHtcblx0XHRcdHRoaXMudXBkYXRlVHJhY2tSYW5nZVdpZHRoKCk7XG5cdFx0fSBlbHNlIGlmICh0aGlzLmZpbGxlZFRyYWNrKSB7XG5cdFx0XHR0aGlzLmZpbGxlZFRyYWNrLm5hdGl2ZUVsZW1lbnQuc3R5bGUudHJhbnNmb3JtID0gYHRyYW5zbGF0ZSgwJSwgLTUwJSkgJHt0aGlzLnNjYWxlWCh0aGlzLmdldEZyYWN0aW9uQ29tcGxldGUodlswXSkpfWA7XG5cdFx0fVxuXG5cdFx0aWYgKHRoaXMuaW5wdXRzICYmIHRoaXMuaW5wdXRzLmxlbmd0aCkge1xuXHRcdFx0dGhpcy5pbnB1dHMuZm9yRWFjaCgoaW5wdXQsIGluZGV4KSA9PiB7XG5cdFx0XHRcdGlucHV0LnZhbHVlID0gdGhpcy5fdmFsdWVbaW5kZXhdLnRvU3RyaW5nKCk7XG5cdFx0XHR9KTtcblx0XHR9XG5cblx0XHRjb25zdCB2YWx1ZVRvRW1pdCA9IHRoaXMuaXNSYW5nZSgpID8gdiA6IHZbMF07XG5cdFx0dGhpcy5wcm9wYWdhdGVDaGFuZ2U