@acrodata/gradient-picker
Version:
A powerful and beautiful gradient picker.
99 lines • 29.7 kB
JavaScript
import { booleanAttribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, inject, Input, ViewEncapsulation, } from '@angular/core';
import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
import { GradientCheckbox, GradientFormGroup, GradientIconButton, GradientInputField, GradientRadioButton, GradientUnitInput, } from './form-controls';
import { GradientStops } from './gradient-stops';
import { parseRadialGradient, stringifyRadialGradient } from './parser';
import { hueInterpolationMethods, lengthUnits, polarColorSpaces, positionXKeywords, positionYKeywords, rectangularColorSpaces, reverseColorStops, } from './utils';
import * as i0 from "@angular/core";
import * as i1 from "@angular/forms";
export class RadialGradientPicker {
cdr = inject(ChangeDetectorRef);
disabled = false;
radialGradient = {
repeating: false,
shape: 'ellipse',
size: [],
position: {
x: { type: 'keyword', value: 'center' },
y: { type: 'keyword', value: 'center' },
},
stops: [{ color: '#000000' }],
};
value = '';
sizeKeywords = ['farthest-corner', 'farthest-side', 'closest-corner', 'closest-side'];
lengthUnits = lengthUnits;
posXOptions = positionXKeywords;
posYOptions = positionYKeywords;
colorSpaceOptgroups = [
{ label: 'Rectangular', options: rectangularColorSpaces },
{ label: 'Polar', options: polarColorSpaces },
];
hueInterpolationMethodOptions = hueInterpolationMethods;
get isPolarColorSpace() {
return polarColorSpaces.includes(this.radialGradient.color?.space || '');
}
onChange = () => { };
onTouched = () => { };
writeValue(value) {
this.value = value || 'radial-gradient(transparent, #000000)';
this.radialGradient = parseRadialGradient(this.value);
this.cdr.markForCheck();
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
this.disabled = isDisabled;
this.cdr.markForCheck();
}
onGradientChange() {
this.value = stringifyRadialGradient(this.radialGradient);
this.onChange(this.value);
}
reverseStops() {
this.radialGradient.stops = reverseColorStops(this.radialGradient.stops);
this.onGradientChange();
}
onColorSpaceChange() {
if (!this.isPolarColorSpace && this.radialGradient.color) {
this.radialGradient.color.method = undefined;
}
this.onGradientChange();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: RadialGradientPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.7", type: RadialGradientPicker, isStandalone: true, selector: "radial-gradient-picker", inputs: { disabled: ["disabled", "disabled", booleanAttribute] }, host: { classAttribute: "radial-gradient-picker" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RadialGradientPicker),
multi: true,
},
], ngImport: i0, template: "<gradient-form-group label=\"Shape\">\n <gradient-input-field>\n <label gradientRadioButton>\n <input\n type=\"radio\"\n value=\"ellipse\"\n [(ngModel)]=\"radialGradient.shape\"\n (change)=\"onGradientChange()\"\n />\n <span>Ellipse</span>\n </label>\n <label gradientRadioButton>\n <input\n type=\"radio\"\n value=\"circle\"\n [(ngModel)]=\"radialGradient.shape\"\n (change)=\"onGradientChange()\"\n />\n <span>Circle</span>\n </label>\n </gradient-input-field>\n</gradient-form-group>\n\n<gradient-form-group label=\"Size\">\n @for (size of radialGradient.size; track $index) {\n @if (size.type === 'keyword') {\n <gradient-input-field>\n <select [(ngModel)]=\"size.value\" (change)=\"onGradientChange()\">\n @for (sk of sizeKeywords; track $index) {\n <option [value]=\"sk\">{{ sk }}</option>\n }\n </select>\n </gradient-input-field>\n } @else {\n <gradient-unit-input\n [units]=\"lengthUnits\"\n [(ngModel)]=\"size.value\"\n (change)=\"onGradientChange()\"\n />\n }\n }\n</gradient-form-group>\n\n<gradient-form-group label=\"Position\">\n @if (radialGradient.position.x.type === 'keyword') {\n <gradient-input-field>\n <select [(ngModel)]=\"radialGradient.position.x.value\" (change)=\"onGradientChange()\">\n @for (posX of posXOptions; track $index) {\n <option [value]=\"posX\">{{ posX }}</option>\n }\n </select>\n </gradient-input-field>\n } @else {\n <gradient-unit-input\n [units]=\"lengthUnits\"\n [(ngModel)]=\"radialGradient.position.x.value\"\n (change)=\"onGradientChange()\"\n />\n }\n @if (radialGradient.position.y.type === 'keyword') {\n <gradient-input-field>\n <select [(ngModel)]=\"radialGradient.position.y.value\" (change)=\"onGradientChange()\">\n @for (posY of posYOptions; track $index) {\n <option [value]=\"posY\">{{ posY }}</option>\n }\n </select>\n </gradient-input-field>\n } @else {\n <gradient-unit-input\n [units]=\"lengthUnits\"\n [(ngModel)]=\"radialGradient.position.y.value\"\n (change)=\"onGradientChange()\"\n />\n }\n</gradient-form-group>\n\n@if (radialGradient.color) {\n <gradient-form-group label=\"Color interpolation\">\n <gradient-input-field>\n <select [(ngModel)]=\"radialGradient.color.space\" (change)=\"onColorSpaceChange()\">\n @for (colorSpaceGroup of colorSpaceOptgroups; track $index) {\n <optgroup [label]=\"colorSpaceGroup.label\">\n @for (colorSpace of colorSpaceGroup.options; track $index) {\n <option [value]=\"colorSpace\">{{ colorSpace }}</option>\n }\n </optgroup>\n }\n </select>\n </gradient-input-field>\n @if (isPolarColorSpace) {\n <gradient-input-field>\n <select [(ngModel)]=\"radialGradient.color.method\" (change)=\"onGradientChange()\">\n @for (hueInterp of hueInterpolationMethodOptions; track $index) {\n <option [value]=\"hueInterp\">{{ hueInterp }}</option>\n }\n </select>\n </gradient-input-field>\n }\n </gradient-form-group>\n}\n\n<gradient-form-group>\n <label gradientCheckbox>\n <input type=\"checkbox\" [(ngModel)]=\"radialGradient.repeating\" (change)=\"onGradientChange()\" />\n <span>Repeat</span>\n </label>\n\n <gradient-icon-button>\n <button type=\"button\" (click)=\"reverseStops()\" title=\"Reverse stops\" aria-label=\"Reverse stops\">\n <svg viewBox=\"0 0 24 24\">\n <path\n fill=\"currentColor\"\n d=\"M8.354 6.354a.5.5 0 1 0-.708-.708l-2.5 2.5a.5.5 0 0 0 0 .708l2.5 2.5a.5.5 0 0 0 .708-.708L6.707 9H18.5a.5.5 0 0 0 0-1H6.707zm7.292 7a.5.5 0 0 1 .708-.708l2.5 2.5a.5.5 0 0 1 0 .708l-2.5 2.5a.5.5 0 0 1-.708-.708L17.293 16H5.5a.5.5 0 0 1 0-1h11.793z\"\n />\n </svg>\n </button>\n </gradient-icon-button>\n</gradient-form-group>\n\n<gradient-stops [ngModel]=\"radialGradient.stops\" (ngModelChange)=\"onGradientChange()\" />\n", styles: [""], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: GradientStops, selector: "gradient-stops", inputs: ["disabled", "colorStops"], outputs: ["colorStopsChange"] }, { kind: "component", type: GradientInputField, selector: "gradient-input-field" }, { kind: "component", type: GradientFormGroup, selector: "gradient-form-group", inputs: ["label"] }, { kind: "component", type: GradientUnitInput, selector: "gradient-unit-input", inputs: ["disabled", "units"] }, { kind: "component", type: GradientCheckbox, selector: "[gradientCheckbox]" }, { kind: "component", type: GradientRadioButton, selector: "[gradientRadioButton]" }, { kind: "component", type: GradientIconButton, selector: "gradient-icon-button" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: RadialGradientPicker, decorators: [{
type: Component,
args: [{ selector: 'radial-gradient-picker', standalone: true, imports: [
FormsModule,
GradientStops,
GradientInputField,
GradientFormGroup,
GradientUnitInput,
GradientCheckbox,
GradientRadioButton,
GradientIconButton,
], host: {
class: 'radial-gradient-picker',
}, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RadialGradientPicker),
multi: true,
},
], template: "<gradient-form-group label=\"Shape\">\n <gradient-input-field>\n <label gradientRadioButton>\n <input\n type=\"radio\"\n value=\"ellipse\"\n [(ngModel)]=\"radialGradient.shape\"\n (change)=\"onGradientChange()\"\n />\n <span>Ellipse</span>\n </label>\n <label gradientRadioButton>\n <input\n type=\"radio\"\n value=\"circle\"\n [(ngModel)]=\"radialGradient.shape\"\n (change)=\"onGradientChange()\"\n />\n <span>Circle</span>\n </label>\n </gradient-input-field>\n</gradient-form-group>\n\n<gradient-form-group label=\"Size\">\n @for (size of radialGradient.size; track $index) {\n @if (size.type === 'keyword') {\n <gradient-input-field>\n <select [(ngModel)]=\"size.value\" (change)=\"onGradientChange()\">\n @for (sk of sizeKeywords; track $index) {\n <option [value]=\"sk\">{{ sk }}</option>\n }\n </select>\n </gradient-input-field>\n } @else {\n <gradient-unit-input\n [units]=\"lengthUnits\"\n [(ngModel)]=\"size.value\"\n (change)=\"onGradientChange()\"\n />\n }\n }\n</gradient-form-group>\n\n<gradient-form-group label=\"Position\">\n @if (radialGradient.position.x.type === 'keyword') {\n <gradient-input-field>\n <select [(ngModel)]=\"radialGradient.position.x.value\" (change)=\"onGradientChange()\">\n @for (posX of posXOptions; track $index) {\n <option [value]=\"posX\">{{ posX }}</option>\n }\n </select>\n </gradient-input-field>\n } @else {\n <gradient-unit-input\n [units]=\"lengthUnits\"\n [(ngModel)]=\"radialGradient.position.x.value\"\n (change)=\"onGradientChange()\"\n />\n }\n @if (radialGradient.position.y.type === 'keyword') {\n <gradient-input-field>\n <select [(ngModel)]=\"radialGradient.position.y.value\" (change)=\"onGradientChange()\">\n @for (posY of posYOptions; track $index) {\n <option [value]=\"posY\">{{ posY }}</option>\n }\n </select>\n </gradient-input-field>\n } @else {\n <gradient-unit-input\n [units]=\"lengthUnits\"\n [(ngModel)]=\"radialGradient.position.y.value\"\n (change)=\"onGradientChange()\"\n />\n }\n</gradient-form-group>\n\n@if (radialGradient.color) {\n <gradient-form-group label=\"Color interpolation\">\n <gradient-input-field>\n <select [(ngModel)]=\"radialGradient.color.space\" (change)=\"onColorSpaceChange()\">\n @for (colorSpaceGroup of colorSpaceOptgroups; track $index) {\n <optgroup [label]=\"colorSpaceGroup.label\">\n @for (colorSpace of colorSpaceGroup.options; track $index) {\n <option [value]=\"colorSpace\">{{ colorSpace }}</option>\n }\n </optgroup>\n }\n </select>\n </gradient-input-field>\n @if (isPolarColorSpace) {\n <gradient-input-field>\n <select [(ngModel)]=\"radialGradient.color.method\" (change)=\"onGradientChange()\">\n @for (hueInterp of hueInterpolationMethodOptions; track $index) {\n <option [value]=\"hueInterp\">{{ hueInterp }}</option>\n }\n </select>\n </gradient-input-field>\n }\n </gradient-form-group>\n}\n\n<gradient-form-group>\n <label gradientCheckbox>\n <input type=\"checkbox\" [(ngModel)]=\"radialGradient.repeating\" (change)=\"onGradientChange()\" />\n <span>Repeat</span>\n </label>\n\n <gradient-icon-button>\n <button type=\"button\" (click)=\"reverseStops()\" title=\"Reverse stops\" aria-label=\"Reverse stops\">\n <svg viewBox=\"0 0 24 24\">\n <path\n fill=\"currentColor\"\n d=\"M8.354 6.354a.5.5 0 1 0-.708-.708l-2.5 2.5a.5.5 0 0 0 0 .708l2.5 2.5a.5.5 0 0 0 .708-.708L6.707 9H18.5a.5.5 0 0 0 0-1H6.707zm7.292 7a.5.5 0 0 1 .708-.708l2.5 2.5a.5.5 0 0 1 0 .708l-2.5 2.5a.5.5 0 0 1-.708-.708L17.293 16H5.5a.5.5 0 0 1 0-1h11.793z\"\n />\n </svg>\n </button>\n </gradient-icon-button>\n</gradient-form-group>\n\n<gradient-stops [ngModel]=\"radialGradient.stops\" (ngModelChange)=\"onGradientChange()\" />\n" }]
}], propDecorators: { disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmFkaWFsLWdyYWRpZW50LXBpY2tlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL2dyYWRpZW50LXBpY2tlci9zcmMvbGliL3JhZGlhbC1ncmFkaWVudC1waWNrZXIudHMiLCIuLi8uLi8uLi8uLi9wcm9qZWN0cy9ncmFkaWVudC1waWNrZXIvc3JjL2xpYi9yYWRpYWwtZ3JhZGllbnQtcGlja2VyLmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLGdCQUFnQixFQUNoQix1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxVQUFVLEVBQ1YsTUFBTSxFQUNOLEtBQUssRUFDTCxpQkFBaUIsR0FDbEIsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUF3QixXQUFXLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN0RixPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLGlCQUFpQixFQUNqQixrQkFBa0IsRUFDbEIsa0JBQWtCLEVBQ2xCLG1CQUFtQixFQUNuQixpQkFBaUIsR0FDbEIsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDakQsT0FBTyxFQUFFLG1CQUFtQixFQUF3Qix1QkFBdUIsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUM5RixPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFdBQVcsRUFDWCxnQkFBZ0IsRUFDaEIsaUJBQWlCLEVBQ2pCLGlCQUFpQixFQUNqQixzQkFBc0IsRUFDdEIsaUJBQWlCLEdBQ2xCLE1BQU0sU0FBUyxDQUFDOzs7QUE4QmpCLE1BQU0sT0FBTyxvQkFBb0I7SUFDdkIsR0FBRyxHQUFHLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0lBRUEsUUFBUSxHQUFHLEtBQUssQ0FBQztJQUV6RCxjQUFjLEdBQXlCO1FBQ3JDLFNBQVMsRUFBRSxLQUFLO1FBQ2hCLEtBQUssRUFB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O2dCQUNuRCxLQUFLLEVBQUUsSUFBSTthQUNaO1NBQ0YsMEJDekRILHlqSUF3SEEseUREckZJLFdBQVcsODRDQUNYLGFBQWEsOEhBQ2Isa0JBQWtCLGlFQUNsQixpQkFBaUIsbUZBQ2pCLGlCQUFpQiwrRkFDakIsZ0JBQWdCLCtEQUNoQixtQkFBbUIsa0VBQ25CLGtCQUFrQjs7MkZBaUJULG9CQUFvQjtrQkE1QmhDLFNBQVM7K0JBQ0Usd0JBQXdCLGNBQ3RCLElBQUksV0FDUDt3QkFDUCxXQUFXO3dCQUNYLGFBQWE7d0JBQ2Isa0JBQWtCO3dCQUNsQixpQkFBaUI7d0JBQ2pCLGlCQUFpQjt3QkFDakIsZ0JBQWdCO3dCQUNoQixtQkFBbUI7d0JBQ25CLGtCQUFrQjtxQkFDbkIsUUFHSzt3QkFDSixLQUFLLEVBQUUsd0JBQXdCO3FCQUNoQyxpQkFDYyxpQkFBaUIsQ0FBQyxJQUFJLG1CQUNwQix1QkFBdUIsQ0FBQyxNQUFNLGFBQ3BDO3dCQUNUOzRCQUNFLE9BQU8sRUFBRSxpQkFBaUI7NEJBQzFCLFdBQVcsRUFBRSxVQUFVLENBQUMsR0FBRyxFQUFFLHFCQUFxQixDQUFDOzRCQUNuRCxLQUFLLEVBQUUsSUFBSTt5QkFDWjtxQkFDRjs4QkFLdUMsUUFBUTtzQkFBL0MsS0FBSzt1QkFBQyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIGJvb2xlYW5BdHRyaWJ1dGUsXG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICBDaGFuZ2VEZXRlY3RvclJlZixcbiAgQ29tcG9uZW50LFxuICBmb3J3YXJkUmVmLFxuICBpbmplY3QsXG4gIElucHV0LFxuICBWaWV3RW5jYXBzdWxhdGlvbixcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb250cm9sVmFsdWVBY2Nlc3NvciwgRm9ybXNNb2R1bGUsIE5HX1ZBTFVFX0FDQ0VTU09SIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHtcbiAgR3JhZGllbnRDaGVja2JveCxcbiAgR3JhZGllbnRGb3JtR3JvdXAsXG4gIEdyYWRpZW50SWNvbkJ1dHRvbixcbiAgR3JhZGllbnRJbnB1dEZpZWxkLFxuICBHcmFkaWVudFJhZGlvQnV0dG9uLFxuICBHcmFkaWVudFVuaXRJbnB1dCxcbn0gZnJvbSAnLi9mb3JtLWNvbnRyb2xzJztcbmltcG9ydCB7IEdyYWRpZW50U3RvcHMgfSBmcm9tICcuL2dyYWRpZW50LXN0b3BzJztcbmltcG9ydCB7IHBhcnNlUmFkaWFsR3JhZGllbnQsIFJhZGlhbEdyYWRpZW50UmVzdWx0LCBzdHJpbmdpZnlSYWRpYWxHcmFkaWVudCB9IGZyb20gJy4vcGFyc2VyJztcbmltcG9ydCB7XG4gIGh1ZUludGVycG9sYXRpb25NZXRob2RzLFxuICBsZW5ndGhVbml0cyxcbiAgcG9sYXJDb2xvclNwYWNlcyxcbiAgcG9zaXRpb25YS2V5d29yZHMsXG4gIHBvc2l0aW9uWUtleXdvcmRzLFxuICByZWN0YW5ndWxhckNvbG9yU3BhY2VzLFxuICByZXZlcnNlQ29sb3JTdG9wcyxcbn0gZnJvbSAnLi91dGlscyc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3JhZGlhbC1ncmFkaWVudC1waWNrZXInLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgRm9ybXNNb2R1bGUsXG4gICAgR3JhZGllbnRTdG9wcyxcbiAgICBHcmFkaWVudElucHV0RmllbGQsXG4gICAgR3JhZGllbnRGb3JtR3JvdXAsXG4gICAgR3JhZGllbnRVbml0SW5wdXQsXG4gICAgR3JhZGllbnRDaGVja2JveCxcbiAgICBHcmFkaWVudFJhZGlvQnV0dG9uLFxuICAgIEdyYWRpZW50SWNvbkJ1dHRvbixcbiAgXSxcbiAgdGVtcGxhdGVVcmw6ICcuL3JhZGlhbC1ncmFkaWVudC1waWNrZXIuaHRtbCcsXG4gIHN0eWxlVXJsOiAnLi9yYWRpYWwtZ3JhZGllbnQtcGlja2VyLnNjc3MnLFxuICBob3N0OiB7XG4gICAgY2xhc3M6ICdyYWRpYWwtZ3JhZGllbnQtcGlja2VyJyxcbiAgfSxcbiAgZW5jYXBzdWxhdGlvbjogVmlld0VuY2Fwc3VsYXRpb24uTm9uZSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG4gIHByb3ZpZGVyczogW1xuICAgIHtcbiAgICAgIHByb3ZpZGU6IE5HX1ZBTFVFX0FDQ0VTU09SLFxuICAgICAgdXNlRXhpc3Rpbmc6IGZvcndhcmRSZWYoKCkgPT4gUmFkaWFsR3JhZGllbnRQaWNrZXIpLFxuICAgICAgbXVsdGk6IHRydWUsXG4gICAgfSxcbiAgXSxcbn0pXG5leHBvcnQgY2xhc3MgUmFkaWFsR3JhZGllbnRQaWNrZXIgaW1wbGVtZW50cyBDb250cm9sVmFsdWVBY2Nlc3NvciB7XG4gIHByaXZhdGUgY2RyID0gaW5qZWN0KENoYW5nZURldGVjdG9yUmVmKTtcblxuICBASW5wdXQoeyB0cmFuc2Zvcm06IGJvb2xlYW5BdHRyaWJ1dGUgfSkgZGlzYWJsZWQgPSBmYWxzZTtcblxuICByYWRpYWxHcmFkaWVudDogUmFkaWFsR3JhZGllbnRSZXN1bHQgPSB7XG4gICAgcmVwZWF0aW5nOiBmYWxzZSxcbiAgICBzaGFwZTogJ2VsbGlwc2UnLFxuICAgIHNpemU6IFtdLFxuICAgIHBvc2l0aW9uOiB7XG4gICAgICB4OiB7IHR5cGU6ICdrZXl3b3JkJywgdmFsdWU6ICdjZW50ZXInIH0sXG4gICAgICB5OiB7IHR5cGU6ICdrZXl3b3JkJywgdmFsdWU6ICdjZW50ZXInIH0sXG4gICAgfSxcbiAgICBzdG9wczogW3sgY29sb3I6ICcjMDAwMDAwJyB9XSxcbiAgfTtcblxuICB2YWx1ZSA9ICcnO1xuXG4gIHNpemVLZXl3b3JkcyA9IFsnZmFydGhlc3QtY29ybmVyJywgJ2ZhcnRoZXN0LXNpZGUnLCAnY2xvc2VzdC1jb3JuZXInLCAnY2xvc2VzdC1zaWRlJ107XG5cbiAgbGVuZ3RoVW5pdHMgPSBsZW5ndGhVbml0cztcblxuICBwb3NYT3B0aW9ucyA9IHBvc2l0aW9uWEtleXdvcmRzO1xuXG4gIHBvc1lPcHRpb25zID0gcG9zaXRpb25ZS2V5d29yZHM7XG5cbiAgY29sb3JTcGFjZU9wdGdyb3VwcyA9IFtcbiAgICB7IGxhYmVsOiAnUmVjdGFuZ3VsYXInLCBvcHRpb25zOiByZWN0YW5ndWxhckNvbG9yU3BhY2VzIH0sXG4gICAgeyBsYWJlbDogJ1BvbGFyJywgb3B0aW9uczogcG9sYXJDb2xvclNwYWNlcyB9LFxuICBdO1xuXG4gIGh1ZUludGVycG9sYXRpb25NZXRob2RPcHRpb25zID0gaHVlSW50ZXJwb2xhdGlvbk1ldGhvZHM7XG5cbiAgZ2V0IGlzUG9sYXJDb2xvclNwYWNlKCkge1xuICAgIHJldHVybiBwb2xhckNvbG9yU3BhY2VzLmluY2x1ZGVzKHRoaXMucmFkaWFsR3JhZGllbnQuY29sb3I/LnNwYWNlIHx8ICcnKTtcbiAgfVxuXG4gIHByaXZhdGUgb25DaGFuZ2U6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkID0gKCkgPT4ge307XG4gIHByaXZhdGUgb25Ub3VjaGVkOiAoKSA9PiB2b2lkID0gKCkgPT4ge307XG5cbiAgd3JpdGVWYWx1ZSh2YWx1ZTogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy52YWx1ZSA9IHZhbHVlIHx8ICdyYWRpYWwtZ3JhZGllbnQodHJhbnNwYXJlbnQsICMwMDAwMDApJztcbiAgICB0aGlzLnJhZGlhbEdyYWRpZW50ID0gcGFyc2VSYWRpYWxHcmFkaWVudCh0aGlzLnZhbHVlKTtcbiAgICB0aGlzLmNkci5tYXJrRm9yQ2hlY2soKTtcbiAgfVxuXG4gIHJlZ2lzdGVyT25DaGFuZ2UoZm46ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkKTogdm9pZCB7XG4gICAgdGhpcy5vbkNoYW5nZSA9IGZuO1xuICB9XG5cbiAgcmVnaXN0ZXJPblRvdWNoZWQoZm46ICgpID0+IHZvaWQpOiB2b2lkIHtcbiAgICB0aGlzLm9uVG91Y2hlZCA9IGZuO1xuICB9XG5cbiAgc2V0RGlzYWJsZWRTdGF0ZShpc0Rpc2FibGVkOiBib29sZWFuKSB7XG4gICAgdGhpcy5kaXNhYmxlZCA9IGlzRGlzYWJsZWQ7XG4gICAgdGhpcy5jZHIubWFya0ZvckNoZWNrKCk7XG4gIH1cblxuICBvbkdyYWRpZW50Q2hhbmdlKCkge1xuICAgIHRoaXMudmFsdWUgPSBzdHJpbmdpZnlSYWRpYWxHcmFkaWVudCh0aGlzLnJhZGlhbEdyYWRpZW50KTtcbiAgICB0aGlzLm9uQ2hhbmdlKHRoaXMudmFsdWUpO1xuICB9XG5cbiAgcmV2ZXJzZVN0b3BzKCkge1xuICAgIHRoaXMucmFkaWFsR3JhZGllbnQuc3RvcHMgPSByZXZlcnNlQ29sb3JTdG9wcyh0aGlzLnJhZGlhbEdyYWRpZW50LnN0b3BzKTtcbiAgICB0aGlzLm9uR3JhZGllbnRDaGFuZ2UoKTtcbiAgfVxuXG4gIG9uQ29sb3JTcGFjZUNoYW5nZSgpIHtcbiAgICBpZiAoIXRoaXMuaXNQb2xhckNvbG9yU3BhY2UgJiYgdGhpcy5yYWRpYWxHcmFkaWVudC5jb2xvcikge1xuICAgICAgdGhpcy5yYWRpYWxHcmFkaWVudC5jb2xvci5tZXRob2QgPSB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHRoaXMub25HcmFkaWVudENoYW5nZSgpO1xuICB9XG59XG4iLCI8Z3JhZGllbnQtZm9ybS1ncm91cCBsYWJlbD1cIlNoYXBlXCI+XG4gIDxncmFkaWVudC1pbnB1dC1maWVsZD5cbiAgICA8bGFiZWwgZ3JhZGllbnRSYWRpb0J1dHRvbj5cbiAgICAgIDxpbnB1dFxuICAgICAgICB0eXBlPVwicmFkaW9cIlxuICAgICAgICB2YWx1ZT1cImVsbGlwc2VcIlxuICAgICAgICBbKG5nTW9kZWwpXT1cInJhZGlhbEdyYWRpZW50LnNoYXBlXCJcbiAgICAgICAgKGNoYW5nZSk9XCJvbkdyYWRpZW50Q2hhbmdlKClcIlxuICAgICAgLz5cbiAgICAgIDxzcGFuPkVsbGlwc2U8L3NwYW4+XG4gICAgPC9sYWJlbD5cbiAgICA8bGFiZWwgZ3JhZGllbnRSYWRpb0J1dHRvbj5cbiAgICAgIDxpbnB1dFxuICAgICAgICB0eXBlPVwicmFkaW9cIlxuICAgICAgICB2YWx1ZT1cImNpcmNsZVwiXG4gICAgICAgIFsobmdNb2RlbCldPVwicmFkaWFsR3JhZGllbnQuc2hhcGVcIlxuICAgICAgICAoY2hhbmdlKT1cIm9uR3JhZGllbnRDaGFuZ2UoKVwiXG4gICAgICAvPlxuICAgICAgPHNwYW4+Q2lyY2xlPC9zcGFuPlxuICAgIDwvbGFiZWw+XG4gIDwvZ3JhZGllbnQtaW5wdXQtZmllbGQ+XG48L2dyYWRpZW50LWZvcm0tZ3JvdXA+XG5cbjxncmFkaWVudC1mb3JtLWdyb3VwIGxhYmVsPVwiU2l6ZVwiPlxuICBAZm9yIChzaXplIG9mIHJhZGlhbEdyYWRpZW50LnNpemU7IHRyYWNrICRpbmRleCkge1xuICAgIEBpZiAoc2l6ZS50eXBlID09PSAna2V5d29yZCcpIHtcbiAgICAgIDxncmFkaWVudC1pbnB1dC1maWVsZD5cbiAgICAgICAgPHNlbGVjdCBbKG5nTW9kZWwpXT1cInNpemUudmFsdWVcIiAoY2hhbmdlKT1cIm9uR3JhZGllbnRDaGFuZ2UoKVwiPlxuICAgICAgICAgIEBmb3IgKHNrIG9mIHNpemVLZXl3b3JkczsgdHJhY2sgJGluZGV4KSB7XG4gICAgICAgICAgICA8b3B0aW9uIFt2YWx1ZV09XCJza1wiPnt7IHNrIH19PC9vcHRpb24+XG4gICAgICAgICAgfVxuICAgICAgICA8L3NlbGVjdD5cbiAgICAgIDwvZ3JhZGllbnQtaW5wdXQtZmllbGQ+XG4gICAgfSBAZWxzZSB7XG4gICAgICA8Z3JhZGllbnQtdW5pdC1pbnB1dFxuICAgICAgICBbdW5pdHNdPVwibGVuZ3RoVW5pdHNcIlxuICAgICAgICBbKG5nTW9kZWwpXT1cInNpemUudmFsdWVcIlxuICAgICAgICAoY2hhbmdlKT1cIm9uR3JhZGllbnRDaGFuZ2UoKVwiXG4gICAgICAvPlxuICAgIH1cbiAgfVxuPC9ncmFkaWVudC1mb3JtLWdyb3VwPlxuXG48Z3JhZGllbnQtZm9ybS1ncm91cCBsYWJlbD1cIlBvc2l0aW9uXCI+XG4gIEBpZiAocmFkaWFsR3JhZGllbnQucG9zaXRpb24ueC50eXBlID09PSAna2V5d29yZCcpIHtcbiAgICA8Z3JhZGllbnQtaW5wdXQtZmllbGQ+XG4gICAgICA8c2VsZWN0IFsobmdNb2RlbCldPVwicmFkaWFsR3JhZGllbnQucG9zaXRpb24ueC52YWx1ZVwiIChjaGFuZ2UpPVwib25HcmFkaWVudENoYW5nZSgpXCI+XG4gICAgICAgIEBmb3IgKHBvc1ggb2YgcG9zWE9wdGlvbnM7IHRyYWNrICRpbmRleCkge1xuICAgICAgICAgIDxvcHRpb24gW3ZhbHVlXT1cInBvc1hcIj57eyBwb3NYIH19PC9vcHRpb24+XG4gICAgICAgIH1cbiAgICAgIDwvc2VsZWN0PlxuICAgIDwvZ3JhZGllbnQtaW5wdXQtZmllbGQ+XG4gIH0gQGVsc2Uge1xuICAgIDxncmFkaWVudC11bml0LWlucHV0XG4gICAgICBbdW5pdHNdPVwibGVuZ3RoVW5pdHNcIlxuICAgICAgWyhuZ01vZGVsKV09XCJyYWRpYWxHcmFkaWVudC5wb3NpdGlvbi54LnZhbHVlXCJcbiAgICAgIChjaGFuZ2UpPVwib25HcmFkaWVudENoYW5nZSgpXCJcbiAgICAvPlxuICB9XG4gIEBpZiAocmFkaWFsR3JhZGllbnQucG9zaXRpb24ueS50eXBlID09PSAna2V5d29yZCcpIHtcbiAgICA8Z3JhZGllbnQtaW5wdXQtZmllbGQ+XG4gICAgICA8c2VsZWN0IFsobmdNb2RlbCldPVwicmFkaWFsR3JhZGllbnQucG9zaXRpb24ueS52YWx1ZVwiIChjaGFuZ2UpPVwib25HcmFkaWVudENoYW5nZSgpXCI+XG4gICAgICAgIEBmb3IgKHBvc1kgb2YgcG9zWU9wdGlvbnM7IHRyYWNrICRpbmRleCkge1xuICAgICAgICAgIDxvcHRpb24gW3ZhbHVlXT1cInBvc1lcIj57eyBwb3NZIH19PC9vcHRpb24+XG4gICAgICAgIH1cbiAgICAgIDwvc2VsZWN0PlxuICAgIDwvZ3JhZGllbnQtaW5wdXQtZmllbGQ+XG4gIH0gQGVsc2Uge1xuICAgIDxncmFkaWVudC11bml0LWlucHV0XG4gICAgICBbdW5pdHNdPVwibGVuZ3RoVW5pdHNcIlxuICAgICAgWyhuZ01vZGVsKV09XCJyYWRpYWxHcmFkaWVudC5wb3NpdGlvbi55LnZhbHVlXCJcbiAgICAgIChjaGFuZ2UpPVwib25HcmFkaWVudENoYW5nZSgpXCJcbiAgICAvPlxuICB9XG48L2dyYWRpZW50LWZvcm0tZ3JvdXA+XG5cbkBpZiAocmFkaWFsR3JhZGllbnQuY29sb3IpIHtcbiAgPGdyYWRpZW50LWZvcm0tZ3JvdXAgbGFiZWw9XCJDb2xvciBpbnRlcnBvbGF0aW9uXCI+XG4gICAgPGdyYWRpZW50LWlucHV0LWZpZWxkPlxuICAgICAgPHNlbGVjdCBbKG5nTW9kZWwpXT1cInJhZGlhbEdyYWRpZW50LmNvbG9yLnNwYWNlXCIgKGNoYW5nZSk9XCJvbkNvbG9yU3BhY2VDaGFuZ2UoKVwiPlxuICAgICAgICBAZm9yIChjb2xvclNwYWNlR3JvdXAgb2YgY29sb3JTcGFjZU9wdGdyb3VwczsgdHJhY2sgJGluZGV4KSB7XG4gICAgICAgICAgPG9wdGdyb3VwIFtsYWJlbF09XCJjb2xvclNwYWNlR3JvdXAubGFiZWxcIj5cbiAgICAgICAgICAgIEBmb3IgKGNvbG9yU3BhY2Ugb2YgY29sb3JTcGFjZUdyb3VwLm9wdGlvbnM7IHRyYWNrICRpbmRleCkge1xuICAgICAgICAgICAgICA8b3B0aW9uIFt2YWx1ZV09XCJjb2xvclNwYWNlXCI+e3sgY29sb3JTcGFjZSB9fTwvb3B0aW9uPlxuICAgICAgICAgICAgfVxuICAgICAgICAgIDwvb3B0Z3JvdXA+XG4gICAgICAgIH1cbiAgICAgIDwvc2VsZWN0PlxuICAgIDwvZ3JhZGllbnQtaW5wdXQtZmllbGQ+XG4gICAgQGlmIChpc1BvbGFyQ29sb3JTcGFjZSkge1xuICAgICAgPGdyYWRpZW50LWlucHV0LWZpZWxkPlxuICAgICAgICA8c2VsZWN0IFsobmdNb2RlbCldPVwicmFkaWFsR3JhZGllbnQuY29sb3IubWV0aG9kXCIgKGNoYW5nZSk9XCJvbkdyYWRpZW50Q2hhbmdlKClcIj5cbiAgICAgICAgICBAZm9yIChodWVJbnRlcnAgb2YgaHVlSW50ZXJwb2xhdGlvbk1ldGhvZE9wdGlvbnM7IHRyYWNrICRpbmRleCkge1xuICAgICAgICAgICAgPG9wdGlvbiBbdmFsdWVdPVwiaHVlSW50ZXJwXCI+e3sgaHVlSW50ZXJwIH19PC9vcHRpb24+XG4gICAgICAgICAgfVxuICAgICAgICA8L3NlbGVjdD5cbiAgICAgIDwvZ3JhZGllbnQtaW5wdXQtZmllbGQ+XG4gICAgfVxuICA8L2dyYWRpZW50LWZvcm0tZ3JvdXA+XG59XG5cbjxncmFkaWVudC1mb3JtLWdyb3VwPlxuICA8bGFiZWwgZ3JhZGllbnRDaGVja2JveD5cbiAgICA8aW5wdXQgdHlwZT1cImNoZWNrYm94XCIgWyhuZ01vZGVsKV09XCJyYWRpYWxHcmFkaWVudC5yZXBlYXRpbmdcIiAoY2hhbmdlKT1cIm9uR3JhZGllbnRDaGFuZ2UoKVwiIC8+XG4gICAgPHNwYW4+UmVwZWF0PC9zcGFuPlxuICA8L2xhYmVsPlxuXG4gIDxncmFkaWVudC1pY29uLWJ1dHRvbj5cbiAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiAoY2xpY2spPVwicmV2ZXJzZVN0b3BzKClcIiB0aXRsZT1cIlJldmVyc2Ugc3RvcHNcIiBhcmlhLWxhYmVsPVwiUmV2ZXJzZSBzdG9wc1wiPlxuICAgICAgPHN2ZyB2aWV3Qm94PVwiMCAwIDI0IDI0XCI+XG4gICAgICAgIDxwYXRoXG4gICAgICAgICAgZmlsbD1cImN1cnJlbnRDb2xvclwiXG4gICAgICAgICAgZD1cIk04LjM1NCA2LjM1NGEuNS41IDAgMSAwLS43MDgtLjcwOGwtMi41IDIuNWEuNS41IDAgMCAwIDAgLjcwOGwyLjUgMi41YS41LjUgMCAwIDAgLjcwOC0uNzA4TDYuNzA3IDlIMTguNWEuNS41IDAgMCAwIDAtMUg2LjcwN3ptNy4yOTIgN2EuNS41IDAgMCAxIC43MDgtLjcwOGwyLjUgMi41YS41LjUgMCAwIDEgMCAuNzA4bC0yLjUgMi41YS41LjUgMCAwIDEtLjcwOC0uNzA4TDE3LjI5MyAxNkg1LjVhLjUuNSAwIDAgMSAwLTFoMTEuNzkzelwiXG4gICAgICAgIC8+XG4gICAgICA8L3N2Zz5cbiAgICA8L2J1dHRvbj5cbiAgPC9ncmFkaWVudC1pY29uLWJ1dHRvbj5cbjwvZ3JhZGllbnQtZm9ybS1ncm91cD5cblxuPGdyYWRpZW50LXN0b3BzIFtuZ01vZGVsXT1cInJhZGlhbEdyYWRpZW50LnN0b3BzXCIgKG5nTW9kZWxDaGFuZ2UpPVwib25HcmFkaWVudENoYW5nZSgpXCIgLz5cbiJdfQ==