UNPKG

ngx-input-color

Version:

Angular color input component and color picker (with HSL, HSV, RGB, CMYK, HEX, alpha, eye-dropper, etc)

176 lines 39.7 kB
import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, EventEmitter, forwardRef, Input, Output, ViewChild, } from '@angular/core'; import { NG_VALUE_ACCESSOR, NG_VALIDATORS, FormsModule, } from '@angular/forms'; import { RangeSliderComponent } from '../../range-slider/range-slider.component'; import { NgxInputColorModule } from '../../ngx-input-color.module'; import { buildGradientFromStops, generateRandomColor, isValidGradient, parseGradient, } from '../../utils/build-gradient'; import { DefaultGradients } from './default-gradients'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; import * as i2 from "@angular/forms"; import * as i3 from "../../directives/ngx-input-color.directive"; export class NgxInputGradientComponent { set setTheme(val) { if (!val || val == 'auto') { this.theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } else { this.theme = val; } } constructor(cd) { this.cd = cd; this.theme = 'auto'; this.change = new EventEmitter(); this.defaultGradients = []; this.resultGradient = ''; this.baseBg = ''; this.rangeValues = []; this.type = 'linear'; this.rotation = 90; this.rotationList = [0, 45, 90, 135, 180, 225, 270, 315, 360]; this.selectedIndex = 0; this.isDisabled = false; this._onChange = (value) => { }; this._onTouched = () => { }; this._onValidateChange = () => { }; } ngOnInit() { if (this.theme == 'auto') { this.theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } this.setDefaultGradients(); } ngOnDestroy() { } registerOnChange(fn) { this._onChange = fn; } registerOnTouched(fn) { this._onTouched = fn; } setDisabledState(disabled) { this.isDisabled = disabled; } registerOnValidatorChange(fn) { this._onValidateChange = fn; } validate(control) { if (!this.resultGradient) return { required: true }; const parsed = parseGradient(this.resultGradient); if (!parsed.valid) return { invalid: true }; if (parsed.stops.length < 2) return { stops: 'at least 2 color stops required' }; return null; } writeValue(value) { if (value && isValidGradient(value)) { const parsed = parseGradient(value); if (parsed.valid) { this.resultGradient = value; this.type = parsed.type; this.rotation = parsed.rotation; this.rangeValues = parsed.stops; } else { this.resultGradient = ''; this.rangeValues = [ { color: generateRandomColor(), value: 0, id: this.generateId() }, { color: generateRandomColor(), value: 100, id: this.generateId() }, ]; this.type = 'linear'; this.rotation = 90; } } else { this.resultGradient = ''; this.rangeValues = [ { color: generateRandomColor(), value: 0, id: this.generateId() }, { color: generateRandomColor(), value: 100, id: this.generateId() }, ]; this.type = 'linear'; this.rotation = 90; } this.generateGradient(); } generateId() { let id = 'ngx-thumb-' + Math.random().toString(36).substring(2, 9); if (this.rangeValues.findIndex((x) => x.id == id) >= 0) { return this.generateId(); } return id; } stopPropagation(ev) { ev.stopPropagation(); } remove() { if (this.rangeValues.length > 2) { this.rangeValues.splice(this.selectedIndex, 1); this.selectedIndex = 0; this.generateGradient(); } } generateGradient(ev) { if (ev && this.rangeValues[this.selectedIndex]) { this.rangeValues[this.selectedIndex].color = ev; } for (let item of this.rangeValues) { item.color ??= generateRandomColor(); } this.baseBg = buildGradientFromStops(this.rangeValues, 'linear', 90); this.resultGradient = buildGradientFromStops(this.rangeValues, this.type, this.rotation); this.emitChange(); } updateRangeSlider() { if (this.rangeSlider) { this.rangeSlider.writeValue(this.rangeValues); } } emitChange() { this._onChange(this.resultGradient); this.change.emit(this.resultGradient); } setDefaultGradients() { this.defaultGradients = []; for (let item of DefaultGradients) { this.defaultGradients.push(buildGradientFromStops(item, 'linear', 90)); } } onSelectDefault(item, i) { // console.log('onSelectDefault', item, i); this.writeValue(item); this.emitChange(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NgxInputGradientComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: NgxInputGradientComponent, isStandalone: true, selector: "ngx-input-gradient", inputs: { setTheme: ["theme", "setTheme"] }, outputs: { change: "change" }, host: { properties: { "class.dark": "theme==\"dark\"" } }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxInputGradientComponent), multi: true }, { provide: NG_VALIDATORS, multi: true, useExisting: NgxInputGradientComponent, }, ], viewQueries: [{ propertyName: "rangeSlider", first: true, predicate: ["rangeSlider"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"ngx-input-gradient-picker\" (click)=\"stopPropagation($event)\">\r\n <div class=\"ngx-color-preview\" [style.background]=\"resultGradient\"></div>\r\n <div class=\"inner\">\r\n <range-slider\r\n [(ngModel)]=\"rangeValues\"\r\n [min]=\"0\"\r\n [max]=\"100\"\r\n [step]=\"1\"\r\n [addNewRangeOnClick]=\"true\"\r\n [background]=\"baseBg\"\r\n [(selectedIndex)]=\"selectedIndex\"\r\n (change)=\"generateGradient()\"\r\n #rangeSlider></range-slider>\r\n\r\n <div class=\"text-end\">\r\n <button class=\"remove-btn\" type=\"button\" (click)=\"remove()\">remove</button>\r\n </div>\r\n <div class=\"ngx-row\" *ngIf=\"rangeValues[selectedIndex]\">\r\n <div class=\"ngx-input-group ngx-col-6\">\r\n <div class=\"label\">Color</div>\r\n <div class=\"input-color\">\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"rangeValues[selectedIndex].color\"\r\n name=\"color\"\r\n #ngxGradColor\r\n (ngModelChange)=\"generateGradient()\" />\r\n <span\r\n class=\"color\"\r\n [style.backgroundColor]=\"rangeValues[selectedIndex].color\"\r\n [ngxInputColor]=\"ngxGradColor\"\r\n [simpleMode]=\"true\"\r\n (change)=\"generateGradient($event)\"></span>\r\n </div>\r\n </div>\r\n <div class=\"ngx-col-6\">\r\n <div class=\"label\">Position</div>\r\n <input\r\n type=\"number\"\r\n [(ngModel)]=\"rangeValues[selectedIndex].value\"\r\n min=\"0\"\r\n max=\"100\"\r\n name=\"posiition\"\r\n (change)=\"generateGradient(); updateRangeSlider()\" />\r\n </div>\r\n <div class=\"ngx-col-6\">\r\n <div class=\"label\">Rotation</div>\r\n <select [(ngModel)]=\"rotation\" (change)=\"generateGradient()\" name=\"rotation\">\r\n <option [value]=\"item\" *ngFor=\"let item of rotationList\">{{ item + '\u00B0' }}</option>\r\n </select>\r\n </div>\r\n <div class=\"ngx-col-6\">\r\n <div class=\"label\">Type</div>\r\n <select [(ngModel)]=\"type\" (change)=\"generateGradient()\" name=\"type\">\r\n <option value=\"linear\">linear</option>\r\n <option value=\"radial\">radial</option>\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div class=\"default-list\">\r\n <span\r\n *ngFor=\"let item of defaultGradients; let i = index\"\r\n [style.background]=\"item\"\r\n (click)=\"onSelectDefault(item, i)\"></span>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [":host{--ngx-color-bg: #fff;--ngx-color-text: #000}:host-context(.dark){--ngx-color-bg: #222;--ngx-color-text: #fff}.ngx-input-gradient-picker{width:270px;max-width:100%;border:1px #bfbfbf solid;border-radius:15px;background:var(--ngx-color-bg);color:var(--ngx-color-text);overflow:hidden;box-shadow:0 0 20px #0000004f;direction:ltr;font-family:arial,tahoma}.ngx-input-gradient-picker .inner{padding:0 12px 12px}.ngx-input-gradient-picker *{box-sizing:border-box}.ngx-color-preview{min-height:100px;padding:10px 36px;line-height:1.6;font-size:14px;font-family:arial,tahoma;font-weight:700;position:relative;color:#353535}.ngx-color-preview:after{content:\" \";position:absolute;background:var(--ngx-color-bg);bottom:0;width:100%;left:0;right:0;height:15px;border-radius:18px 18px 0 0}.ngx-color-preview:before{content:\" \";background-image:linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%);background-size:16px 16px;background-position:0 0,0 8px,8px -8px,-8px 0px;position:absolute;inset:0;z-index:-1}.ngx-row{display:flex;flex-wrap:wrap;gap:12px}.ngx-row .ngx-col-6{width:47%}.ngx-row .input-color{position:relative}.ngx-row .input-color input{padding-right:26px}.ngx-row .input-color .color{position:absolute;top:4px;right:5px;cursor:pointer;display:inline-block;width:24px;height:24px;border-radius:4px;border:1px solid #ccc;transition:all .3s}.ngx-row .input-color .color:hover{border-color:#8db6e4}.ngx-row select,.ngx-row input{outline:none;border:1px solid #ececec;border-radius:4px;padding:8px 10px;background:none;width:100%;color:var(--ngx-color-text)}.ngx-row select:focus,.ngx-row input:focus{cursor:pointer;background:#e5f0ff;color:#06f}.ngx-row select option,.ngx-row input option{padding:10px 20px;background-color:#fff;transition:all .3s;background:var(--ngx-color-bg);color:var(--ngx-color-text)}.ngx-row select option:hover,.ngx-row input option:hover{background:#f0f0f0}.text-end{text-align:end}.remove-btn{background:none;border:none;outline:none;color:#07f;transition:color .3s}.remove-btn:hover{color:#0462cc}.default-list{display:flex;gap:6px;flex-wrap:wrap;margin:16px 0}.default-list span{width:36px;height:26px;border-radius:4px;cursor:pointer;transition:transform .3s}.default-list span:hover{transform:scale(1.2)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NgxInputColorModule }, { kind: "directive", type: i3.NgxInputColorDirective, selector: "[ngxInputColor]", inputs: ["setInputBackgroundColor", "defaultInspector", "simpleMode", "outputType", "theme", "ngxInputColor"], outputs: ["change"] }, { kind: "component", type: RangeSliderComponent, selector: "range-slider", inputs: ["step", "min", "max", "background", "isBgTransparent", "addNewRangeOnClick", "selectedIndex"], outputs: ["change", "selectedIndexChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NgxInputGradientComponent, decorators: [{ type: Component, args: [{ standalone: true, selector: 'ngx-input-gradient', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxInputGradientComponent), multi: true }, { provide: NG_VALIDATORS, multi: true, useExisting: NgxInputGradientComponent, }, ], host: { '[class.dark]': 'theme=="dark"', }, imports: [CommonModule, FormsModule, NgxInputColorModule, RangeSliderComponent], template: "<div class=\"ngx-input-gradient-picker\" (click)=\"stopPropagation($event)\">\r\n <div class=\"ngx-color-preview\" [style.background]=\"resultGradient\"></div>\r\n <div class=\"inner\">\r\n <range-slider\r\n [(ngModel)]=\"rangeValues\"\r\n [min]=\"0\"\r\n [max]=\"100\"\r\n [step]=\"1\"\r\n [addNewRangeOnClick]=\"true\"\r\n [background]=\"baseBg\"\r\n [(selectedIndex)]=\"selectedIndex\"\r\n (change)=\"generateGradient()\"\r\n #rangeSlider></range-slider>\r\n\r\n <div class=\"text-end\">\r\n <button class=\"remove-btn\" type=\"button\" (click)=\"remove()\">remove</button>\r\n </div>\r\n <div class=\"ngx-row\" *ngIf=\"rangeValues[selectedIndex]\">\r\n <div class=\"ngx-input-group ngx-col-6\">\r\n <div class=\"label\">Color</div>\r\n <div class=\"input-color\">\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"rangeValues[selectedIndex].color\"\r\n name=\"color\"\r\n #ngxGradColor\r\n (ngModelChange)=\"generateGradient()\" />\r\n <span\r\n class=\"color\"\r\n [style.backgroundColor]=\"rangeValues[selectedIndex].color\"\r\n [ngxInputColor]=\"ngxGradColor\"\r\n [simpleMode]=\"true\"\r\n (change)=\"generateGradient($event)\"></span>\r\n </div>\r\n </div>\r\n <div class=\"ngx-col-6\">\r\n <div class=\"label\">Position</div>\r\n <input\r\n type=\"number\"\r\n [(ngModel)]=\"rangeValues[selectedIndex].value\"\r\n min=\"0\"\r\n max=\"100\"\r\n name=\"posiition\"\r\n (change)=\"generateGradient(); updateRangeSlider()\" />\r\n </div>\r\n <div class=\"ngx-col-6\">\r\n <div class=\"label\">Rotation</div>\r\n <select [(ngModel)]=\"rotation\" (change)=\"generateGradient()\" name=\"rotation\">\r\n <option [value]=\"item\" *ngFor=\"let item of rotationList\">{{ item + '\u00B0' }}</option>\r\n </select>\r\n </div>\r\n <div class=\"ngx-col-6\">\r\n <div class=\"label\">Type</div>\r\n <select [(ngModel)]=\"type\" (change)=\"generateGradient()\" name=\"type\">\r\n <option value=\"linear\">linear</option>\r\n <option value=\"radial\">radial</option>\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div class=\"default-list\">\r\n <span\r\n *ngFor=\"let item of defaultGradients; let i = index\"\r\n [style.background]=\"item\"\r\n (click)=\"onSelectDefault(item, i)\"></span>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [":host{--ngx-color-bg: #fff;--ngx-color-text: #000}:host-context(.dark){--ngx-color-bg: #222;--ngx-color-text: #fff}.ngx-input-gradient-picker{width:270px;max-width:100%;border:1px #bfbfbf solid;border-radius:15px;background:var(--ngx-color-bg);color:var(--ngx-color-text);overflow:hidden;box-shadow:0 0 20px #0000004f;direction:ltr;font-family:arial,tahoma}.ngx-input-gradient-picker .inner{padding:0 12px 12px}.ngx-input-gradient-picker *{box-sizing:border-box}.ngx-color-preview{min-height:100px;padding:10px 36px;line-height:1.6;font-size:14px;font-family:arial,tahoma;font-weight:700;position:relative;color:#353535}.ngx-color-preview:after{content:\" \";position:absolute;background:var(--ngx-color-bg);bottom:0;width:100%;left:0;right:0;height:15px;border-radius:18px 18px 0 0}.ngx-color-preview:before{content:\" \";background-image:linear-gradient(45deg,#ccc 25%,transparent 25%),linear-gradient(-45deg,#ccc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ccc 75%),linear-gradient(-45deg,transparent 75%,#ccc 75%);background-size:16px 16px;background-position:0 0,0 8px,8px -8px,-8px 0px;position:absolute;inset:0;z-index:-1}.ngx-row{display:flex;flex-wrap:wrap;gap:12px}.ngx-row .ngx-col-6{width:47%}.ngx-row .input-color{position:relative}.ngx-row .input-color input{padding-right:26px}.ngx-row .input-color .color{position:absolute;top:4px;right:5px;cursor:pointer;display:inline-block;width:24px;height:24px;border-radius:4px;border:1px solid #ccc;transition:all .3s}.ngx-row .input-color .color:hover{border-color:#8db6e4}.ngx-row select,.ngx-row input{outline:none;border:1px solid #ececec;border-radius:4px;padding:8px 10px;background:none;width:100%;color:var(--ngx-color-text)}.ngx-row select:focus,.ngx-row input:focus{cursor:pointer;background:#e5f0ff;color:#06f}.ngx-row select option,.ngx-row input option{padding:10px 20px;background-color:#fff;transition:all .3s;background:var(--ngx-color-bg);color:var(--ngx-color-text)}.ngx-row select option:hover,.ngx-row input option:hover{background:#f0f0f0}.text-end{text-align:end}.remove-btn{background:none;border:none;outline:none;color:#07f;transition:color .3s}.remove-btn:hover{color:#0462cc}.default-list{display:flex;gap:6px;flex-wrap:wrap;margin:16px 0}.default-list span{width:36px;height:26px;border-radius:4px;cursor:pointer;transition:transform .3s}.default-list span:hover{transform:scale(1.2)}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { setTheme: [{ type: Input, args: ['theme'] }], change: [{ type: Output }], rangeSlider: [{ type: ViewChild, args: ['rangeSlider', { static: true }] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWlucHV0LWdyYWRpZW50LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1pbnB1dC1jb2xvci9zcmMvbGliL25neC1pbnB1dC1ncmFkaWVudC9uZ3gtaW5wdXQtZ3JhZGllbnQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWlucHV0LWNvbG9yL3NyYy9saWIvbmd4LWlucHV0LWdyYWRpZW50L25neC1pbnB1dC1ncmFkaWVudC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUNMLHVCQUF1QixFQUV2QixTQUFTLEVBQ1QsWUFBWSxFQUNaLFVBQVUsRUFDVixLQUFLLEVBR0wsTUFBTSxFQUNOLFNBQVMsR0FDVixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQ0wsaUJBQWlCLEVBQ2pCLGFBQWEsRUFLYixXQUFXLEdBQ1osTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQztBQUNqRixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUVuRSxPQUFPLEVBQ0wsc0JBQXNCLEVBQ3RCLG1CQUFtQixFQUNuQixlQUFlLEVBQ2YsYUFBYSxHQUNkLE1BQU0sNEJBQTRCLENBQUM7QUFDcEMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0scUJBQXFCLENBQUM7Ozs7O0FBcUJ2RCxNQUFNLE9BQU8seUJBQXlCO0lBRXBDLElBQW9CLFFBQVEsQ0FBQyxHQUE4QjtRQUN6RCxJQUFJLENBQUMsR0FBRyxJQUFJLEdBQUcsSUFBSSxNQUFNLEVBQUUsQ0FBQztZQUMxQixJQUFJLENBQUMsS0FBSyxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUMsOEJBQThCLENBQUMs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LEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRTtZQUNyRztnQkFDRSxPQUFPLEVBQUUsYUFBYTtnQkFDdEIsS0FBSyxFQUFFLElBQUk7Z0JBQ1gsV0FBVyxFQUFFLHlCQUF5QjthQUN2QztTQUNGLG9KQzlDSCxrbEZBb0VBLDQ0RURsQlksWUFBWSwrUEFBRSxXQUFXLGlpREFBRSxtQkFBbUIsd1BBQUUsb0JBQW9COzs0RkFFbkUseUJBQXlCO2tCQW5CckMsU0FBUztpQ0FDSSxJQUFJLFlBQ04sb0JBQW9CLG1CQUdiLHVCQUF1QixDQUFDLE1BQU0sYUFDcEM7d0JBQ1QsRUFBRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsV0FBVyxFQUFFLFVBQVUsQ0FBQyxHQUFHLEVBQUUsMEJBQTBCLENBQUMsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFO3dCQUNyRzs0QkFDRSxPQUFPLEVBQUUsYUFBYTs0QkFDdEIsS0FBSyxFQUFFLElBQUk7NEJBQ1gsV0FBVywyQkFBMkI7eUJBQ3ZDO3FCQUNGLFFBQ0s7d0JBQ0osY0FBYyxFQUFFLGVBQWU7cUJBQ2hDLFdBQ1EsQ0FBQyxZQUFZLEVBQUUsV0FBVyxFQUFFLG1CQUFtQixFQUFFLG9CQUFvQixDQUFDO3NGQUkzRCxRQUFRO3NCQUEzQixLQUFLO3VCQUFDLE9BQU87Z0JBT0osTUFBTTtzQkFBZixNQUFNO2dCQWlCcUMsV0FBVztzQkFBdEQsU0FBUzt1QkFBQyxhQUFhLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcclxuaW1wb3J0IHtcclxuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcclxuICBDaGFuZ2VEZXRlY3RvclJlZixcclxuICBDb21wb25lbnQsXHJcbiAgRXZlbnRFbWl0dGVyLFxyXG4gIGZvcndhcmRSZWYsXHJcbiAgSW5wdXQsXHJcbiAgT25EZXN0cm95LFxyXG4gIE9uSW5pdCxcclxuICBPdXRwdXQsXHJcbiAgVmlld0NoaWxkLFxyXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQge1xyXG4gIE5HX1ZBTFVFX0FDQ0VTU09SLFxyXG4gIE5HX1ZBTElEQVRPUlMsXHJcbiAgQ29udHJvbFZhbHVlQWNjZXNzb3IsXHJcbiAgVmFsaWRhdG9yLFxyXG4gIEFic3RyYWN0Q29udHJvbCxcclxuICBWYWxpZGF0aW9uRXJyb3JzLFxyXG4gIEZvcm1zTW9kdWxlLFxyXG59IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcclxuaW1wb3J0IHsgUmFuZ2VTbGlkZXJDb21wb25lbnQgfSBmcm9tICcuLi8uLi9yYW5nZS1zbGlkZXIvcmFuZ2Utc2xpZGVyLmNvbXBvbmVudCc7XHJcbmltcG9ydCB7IE5neElucHV0Q29sb3JNb2R1bGUgfSBmcm9tICcuLi8uLi9uZ3gtaW5wdXQtY29sb3IubW9kdWxlJztcclxuaW1wb3J0IHsgR3JhZGllbnRTdG9wLCBHcmFkaWVudFR5cGUgfSBmcm9tICcuLi8uLi9tb2RlbHMvR3JhZGllbnRTdG9wJztcclxuaW1wb3J0IHtcclxuICBidWlsZEdyYWRpZW50RnJvbVN0b3BzLFxyXG4gIGdlbmVyYXRlUmFuZG9tQ29sb3IsXHJcbiAgaXNWYWxpZEdyYWRpZW50LFxyXG4gIHBhcnNlR3JhZGllbnQsXHJcbn0gZnJvbSAnLi4vLi4vdXRpbHMvYnVpbGQtZ3JhZGllbnQnO1xyXG5pbXBvcnQgeyBEZWZhdWx0R3JhZGllbnRzIH0gZnJvbSAnLi9kZWZhdWx0LWdyYWRpZW50cyc7XHJcblxyXG5AQ29tcG9uZW50KHtcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIHNlbGVjdG9yOiAnbmd4LWlucHV0LWdyYWRpZW50JyxcclxuICB0ZW1wbGF0ZVVybDogJy4vbmd4LWlucHV0LWdyYWRpZW50LmNvbXBvbmVudC5odG1sJyxcclxuICBzdHlsZVVybHM6IFsnLi9uZ3gtaW5wdXQtZ3JhZGllbnQuY29tcG9uZW50LnNjc3MnXSxcclxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcclxuICBwcm92aWRlcnM6IFtcclxuICAgIHsgcHJvdmlkZTogTkdfVkFMVUVfQUNDRVNTT1IsIHVzZUV4aXN0aW5nOiBmb3J3YXJkUmVmKCgpID0+IE5neElucHV0R3JhZGllbnRDb21wb25lbnQpLCBtdWx0aTogdHJ1ZSB9LFxyXG4gICAge1xyXG4gICAgICBwcm92aWRlOiBOR19WQUxJREFUT1JTLFxyXG4gICAgICBtdWx0aTogdHJ1ZSxcclxuICAgICAgdXNlRXhpc3Rpbmc6IE5neElucHV0R3JhZGllbnRDb21wb25lbnQsXHJcbiAgICB9LFxyXG4gIF0sXHJcbiAgaG9zdDoge1xyXG4gICAgJ1tjbGFzcy5kYXJrXSc6ICd0aGVtZT09XCJkYXJrXCInLFxyXG4gIH0sXHJcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZSwgRm9ybXNNb2R1bGUsIE5neElucHV0Q29sb3JNb2R1bGUsIFJhbmdlU2xpZGVyQ29tcG9uZW50XSxcclxufSlcclxuZXhwb3J0IGNsYXNzIE5neElucHV0R3JhZGllbnRDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSwgQ29udHJvbFZhbHVlQWNjZXNzb3IsIFZhbGlkYXRvciB7XHJcbiAgdGhlbWU6ICdsaWdodCcgfCAnZGFyaycgfCAnYXV0bycgPSAnYXV0byc7XHJcbiAgQElucHV0KCd0aGVtZScpIHNldCBzZXRUaGVtZSh2YWw6ICdsaWdodCcgfCAnZGFyaycgfCAnYXV0bycpIHtcclxuICAgIGlmICghdmFsIHx8IHZhbCA9PSAnYXV0bycpIHtcclxuICAgICAgdGhpcy50aGVtZSA9IHdpbmRvdy5tYXRjaE1lZGlhKCcocHJlZmVycy1jb2xvci1zY2hlbWU6IGRhcmspJykubWF0Y2hlcyA/ICdkYXJrJyA6ICdsaWdodCc7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aGlzLnRoZW1lID0gdmFsO1xyXG4gICAgfVxyXG4gIH1cclxuICBAT3V0cHV0KCkgY2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XHJcblxyXG4gIGRlZmF1bHRHcmFkaWVudHM6IHN0cmluZ1tdID0gW107XHJcblxyXG4gIHJlc3VsdEdyYWRpZW50ID0gJyc7XHJcbiAgYmFzZUJnID0gJyc7XHJcbiAgcmFuZ2VWYWx1ZXM6IEdyYWRpZW50U3RvcFtdID0gW107XHJcbiAgdHlwZTogR3JhZGllbnRUeXBlID0gJ2xpbmVhcic7XHJcbiAgcm90YXRpb246IG51bWJlciA9IDkwO1xyXG4gIHJvdGF0aW9uTGlzdCA9IFswLCA0NSwgOTAsIDEzNSwgMTgwLCAyMjUsIDI3MCwgMzE1LCAzNjBdO1xyXG4gIHNlbGVjdGVkSW5kZXggPSAwO1xyXG5cclxuICBpc0Rpc2FibGVkID0gZmFsc2U7XHJcbiAgX29uQ2hhbmdlID0gKHZhbHVlOiBzdHJpbmcpID0+IHt9O1xyXG4gIF9vblRvdWNoZWQgPSAoKSA9PiB7fTtcclxuICBfb25WYWxpZGF0ZUNoYW5nZSA9ICgpID0+IHt9O1xyXG5cclxuICBAVmlld0NoaWxkKCdyYW5nZVNsaWRlcicsIHsgc3RhdGljOiB0cnVlIH0pIHJhbmdlU2xpZGVyPzogUmFuZ2VTbGlkZXJDb21wb25lbnQ7XHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBjZDogQ2hhbmdlRGV0ZWN0b3JSZWYpIHt9XHJcblxyXG4gIG5nT25Jbml0KCk6IHZvaWQge1xyXG4gICAgaWYgKHRoaXMudGhlbWUgPT0gJ2F1dG8nKSB7XHJcbiAgICAgIHRoaXMudGhlbWUgPSB3aW5kb3cubWF0Y2hNZWRpYSgnKHByZWZlcnMtY29sb3Itc2NoZW1lOiBkYXJrKScpLm1hdGNoZXMgPyAnZGFyaycgOiAnbGlnaHQnO1xyXG4gICAgfVxyXG4gICAgdGhpcy5zZXREZWZhdWx0R3JhZGllbnRzKCk7XHJcbiAgfVxyXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge31cclxuICByZWdpc3Rlck9uQ2hhbmdlKGZuOiBhbnkpOiB2b2lkIHtcclxuICAgIHRoaXMuX29uQ2hhbmdlID0gZm47XHJcbiAgfVxyXG4gIHJlZ2lzdGVyT25Ub3VjaGVkKGZuOiBhbnkpOiB2b2lkIHtcclxuICAgIHRoaXMuX29uVG91Y2hlZCA9IGZuO1xyXG4gIH1cclxuICBzZXREaXNhYmxlZFN0YXRlKGRpc2FibGVkOiBib29sZWFuKTogdm9pZCB7XHJcbiAgICB0aGlzLmlzRGlzYWJsZWQgPSBkaXNhYmxlZDtcclxuICB9XHJcbiAgcmVnaXN0ZXJPblZhbGlkYXRvckNoYW5nZShmbjogKCkgPT4gdm9pZCk6IHZvaWQge1xyXG4gICAgdGhpcy5fb25WYWxpZGF0ZUNoYW5nZSA9IGZuO1xyXG4gIH1cclxuICB2YWxpZGF0ZShjb250cm9sOiBBYnN0cmFjdENvbnRyb2wpOiBWYWxpZGF0aW9uRXJyb3JzIHwgbnVsbCB7XHJcbiAgICBpZiAoIXRoaXMucmVzdWx0R3JhZGllbnQpIHJldHVybiB7IHJlcXVpcmVkOiB0cnVlIH07XHJcbiAgICBjb25zdCBwYXJzZWQgPSBwYXJzZUdyYWRpZW50KHRoaXMucmVzdWx0R3JhZGllbnQpO1xyXG4gICAgaWYgKCFwYXJzZWQudmFsaWQpIHJldHVybiB7IGludmFsaWQ6IHRydWUgfTtcclxuICAgIGlmIChwYXJzZWQuc3RvcHMubGVuZ3RoIDwgMikgcmV0dXJuIHsgc3RvcHM6ICdhdCBsZWFzdCAyIGNvbG9yIHN0b3BzIHJlcXVpcmVkJyB9O1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICB3cml0ZVZhbHVlKHZhbHVlOiBhbnkpOiB2b2lkIHtcclxuICAgIGlmICh2YWx1ZSAmJiBpc1ZhbGlkR3JhZGllbnQodmFsdWUpKSB7XHJcbiAgICAgIGNvbnN0IHBhcnNlZCA9IHBhcnNlR3JhZGllbnQodmFsdWUpO1xyXG4gICAgICBpZiAocGFyc2VkLnZhbGlkKSB7XHJcbiAgICAgICAgdGhpcy5yZXN1bHRHcmFkaWVudCA9IHZhbHVlO1xyXG4gICAgICAgIHRoaXMudHlwZSA9IHBhcnNlZC50eXBlO1xyXG4gICAgICAgIHRoaXMucm90YXRpb24gPSBwYXJzZWQucm90YXRpb247XHJcbiAgICAgICAgdGhpcy5yYW5nZVZhbHVlcyA9IHBhcnNlZC5zdG9wcztcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aGlzLnJlc3VsdEdyYWRpZW50ID0gJyc7XHJcbiAgICAgICAgdGhpcy5yYW5nZVZhbHVlcyA9IFtcclxuICAgICAgICAgIHsgY29sb3I6IGdlbmVyYXRlUmFuZG9tQ29sb3IoKSwgdmFsdWU6IDAsIGlkOiB0aGlzLmdlbmVyYXRlSWQoKSB9LFxyXG4gICAgICAgICAgeyBjb2xvcjogZ2VuZXJhdGVSYW5kb21Db2xvcigpLCB2YWx1ZTogMTAwLCBpZDogdGhpcy5nZW5lcmF0ZUlkKCkgfSxcclxuICAgICAgICBdO1xyXG4gICAgICAgIHRoaXMudHlwZSA9ICdsaW5lYXInO1xyXG4gICAgICAgIHRoaXMucm90YXRpb24gPSA5MDtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdGhpcy5yZXN1bHRHcmFkaWVudCA9ICcnO1xyXG4gICAgICB0aGlzLnJhbmdlVmFsdWVzID0gW1xyXG4gICAgICAgIHsgY29sb3I6IGdlbmVyYXRlUmFuZG9tQ29sb3IoKSwgdmFsdWU6IDAsIGlkOiB0aGlzLmdlbmVyYXRlSWQoKSB9LFxyXG4gICAgICAgIHsgY29sb3I6IGdlbmVyYXRlUmFuZG9tQ29sb3IoKSwgdmFsdWU6IDEwMCwgaWQ6IHRoaXMuZ2VuZXJhdGVJZCgpIH0sXHJcbiAgICAgIF07XHJcbiAgICAgIHRoaXMudHlwZSA9ICdsaW5lYXInO1xyXG4gICAgICB0aGlzLnJvdGF0aW9uID0gOTA7XHJcbiAgICB9XHJcbiAgICB0aGlzLmdlbmVyYXRlR3JhZGllbnQoKTtcclxuICB9XHJcbiAgcHJpdmF0ZSBnZW5lcmF0ZUlkKCk6IHN0cmluZyB7XHJcbiAgICBsZXQgaWQgPSAnbmd4LXRodW1iLScgKyBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHJpbmcoMiwgOSk7XHJcbiAgICBpZiAodGhpcy5yYW5nZVZhbHVlcy5maW5kSW5kZXgoKHgpID0+IHguaWQgPT0gaWQpID49IDApIHtcclxuICAgICAgcmV0dXJuIHRoaXMuZ2VuZXJhdGVJZCgpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGlkO1xyXG4gIH1cclxuXHJcbiAgc3RvcFByb3BhZ2F0aW9uKGV2OiBFdmVudCkge1xyXG4gICAgZXYuc3RvcFByb3BhZ2F0aW9uKCk7XHJcbiAgfVxyXG5cclxuICByZW1vdmUoKSB7XHJcbiAgICBpZiAodGhpcy5yYW5nZVZhbHVlcy5sZW5ndGggPiAyKSB7XHJcbiAgICAgIHRoaXMucmFuZ2VWYWx1ZXMuc3BsaWNlKHRoaXMuc2VsZWN0ZWRJbmRleCwgMSk7XHJcbiAgICAgIHRoaXMuc2VsZWN0ZWRJbmRleCA9IDA7XHJcbiAgICAgIHRoaXMuZ2VuZXJhdGVHcmFkaWVudCgpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZ2VuZXJhdGVHcmFkaWVudChldj86IHN0cmluZykge1xyXG4gICAgaWYgKGV2ICYmIHRoaXMucmFuZ2VWYWx1ZXNbdGhpcy5zZWxlY3RlZEluZGV4XSkge1xyXG4gICAgICB0aGlzLnJhbmdlVmFsdWVzW3RoaXMuc2VsZWN0ZWRJbmRleF0uY29sb3IgPSBldjtcclxuICAgIH1cclxuICAgIGZvciAobGV0IGl0ZW0gb2YgdGhpcy5yYW5nZVZhbHVlcykge1xyXG4gICAgICBpdGVtLmNvbG9yID8/PSBnZW5lcmF0ZVJhbmRvbUNvbG9yKCk7XHJcbiAgICB9XHJcbiAgICB0aGlzLmJhc2VCZyA9IGJ1aWxkR3JhZGllbnRGcm9tU3RvcHModGhpcy5yYW5nZVZhbHVlcywgJ2xpbmVhcicsIDkwKTtcclxuICAgIHRoaXMucmVzdWx0R3JhZGllbnQgPSBidWlsZEdyYWRpZW50RnJvbVN0b3BzKHRoaXMucmFuZ2VWYWx1ZXMsIHRoaXMudHlwZSwgdGhpcy5yb3RhdGlvbik7XHJcbiAgICB0aGlzLmVtaXRDaGFuZ2UoKTtcclxuICB9XHJcblxyXG4gIHVwZGF0ZVJhbmdlU2xpZGVyKCkge1xyXG4gICAgaWYgKHRoaXMucmFuZ2VTbGlkZXIpIHtcclxuICAgICAgdGhpcy5yYW5nZVNsaWRlci53cml0ZVZhbHVlKHRoaXMucmFuZ2VWYWx1ZXMpO1xyXG4gICAgfVxyXG4gIH1cclxuICBlbWl0Q2hhbmdlKCkge1xyXG4gICAgdGhpcy5fb25DaGFuZ2UodGhpcy5yZXN1bHRHcmFkaWVudCk7XHJcbiAgICB0aGlzLmNoYW5nZS5lbWl0KHRoaXMucmVzdWx0R3JhZGllbnQpO1xyXG4gIH1cclxuXHJcbiAgc2V0RGVmYXVsdEdyYWRpZW50cygpIHtcclxuICAgIHRoaXMuZGVmYXVsdEdyYWRpZW50cyA9IFtdO1xyXG5cclxuICAgIGZvciAobGV0IGl0ZW0gb2YgRGVmYXVsdEdyYWRpZW50cykge1xyXG4gICAgICB0aGlzLmRlZmF1bHRHcmFkaWVudHMucHVzaChidWlsZEdyYWRpZW50RnJvbVN0b3BzKGl0ZW0sICdsaW5lYXInLCA5MCkpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgb25TZWxlY3REZWZhdWx0KGl0ZW06IHN0cmluZywgaTogbnVtYmVyKSB7XHJcbiAgICAvLyBjb25zb2xlLmxvZygnb25TZWxlY3REZWZhdWx0JywgaXRlbSwgaSk7XHJcbiAgICB0aGlzLndyaXRlVmFsdWUoaXRlbSk7XHJcbiAgICB0aGlzLmVtaXRDaGFuZ2UoKTtcclxuICB9XHJcbn1cclxuIiwiPGRpdiBjbGFzcz1cIm5neC1pbnB1dC1ncmFkaWVudC1waWNrZXJcIiAoY2xpY2spPVwic3RvcFByb3BhZ2F0aW9uKCRldmVudClcIj5cclxuICA8ZGl2IGNsYXNzPVwibmd4LWNvbG9yLXByZXZpZXdcIiBbc3R5bGUuYmFja2dyb3VuZF09XCJyZXN1bHRHcmFkaWVudFwiPjwvZGl2PlxyXG4gIDxkaXYgY2xhc3M9XCJpbm5lclwiPlxyXG4gICAgPHJhbmdlLXNsaWRlclxyXG4gICAgICBbKG5nTW9kZWwpXT1cInJhbmdlVmFsdWVzXCJcclxuICAgICAgW21pbl09XCIwXCJcclxuICAgICAgW21heF09XCIxMDBcIlxyXG4gICAgICBbc3RlcF09XCIxXCJcclxuICAgICAgW2FkZE5ld1JhbmdlT25DbGlja109XCJ0cnVlXCJcclxuICAgICAgW2JhY2tncm91bmRdPVwiYmFzZUJnXCJcclxuICAgICAgWyhzZWxlY3RlZEluZGV4KV09XCJzZWxlY3RlZEluZGV4XCJcclxuICAgICAgKGNoYW5nZSk9XCJnZW5lcmF0ZUdyYWRpZW50KClcIlxyXG4gICAgICAjcmFuZ2VTbGlkZXI+PC9yYW5nZS1zbGlkZXI+XHJcblxyXG4gICAgPGRpdiBjbGFzcz1cInRleHQtZW5kXCI+XHJcbiAgICAgIDxidXR0b24gY2xhc3M9XCJyZW1vdmUtYnRuXCIgdHlwZT1cImJ1dHRvblwiIChjbGljayk9XCJyZW1vdmUoKVwiPnJlbW92ZTwvYnV0dG9uPlxyXG4gICAgPC9kaXY+XHJcbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJvd1wiICpuZ0lmPVwicmFuZ2VWYWx1ZXNbc2VsZWN0ZWRJbmRleF1cIj5cclxuICAgICAgPGRpdiBjbGFzcz1cIm5neC1pbnB1dC1ncm91cCBuZ3gtY29sLTZcIj5cclxuICAgICAgICA8ZGl2IGNsYXNzPVwibGFiZWxcIj5Db2xvcjwvZGl2PlxyXG4gICAgICAgIDxkaXYgY2xhc3M9XCJpbnB1dC1jb2xvclwiPlxyXG4gICAgICAgICAgPGlucHV0XHJcbiAgICAgICAgICAgIHR5cGU9XCJ0ZXh0XCJcclxuICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJyYW5nZVZhbHVlc1tzZWxlY3RlZEluZGV4XS5jb2xvclwiXHJcbiAgICAgICAgICAgIG5hbWU9XCJjb2xvclwiXHJcbiAgICAgICAgICAgICNuZ3hHcmFkQ29sb3JcclxuICAgICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwiZ2VuZXJhdGVHcmFkaWVudCgpXCIgLz5cclxuICAgICAgICAgIDxzcGFuXHJcbiAgICAgICAgICAgIGNsYXNzPVwiY29sb3JcIlxyXG4gICAgICAgICAgICBbc3R5bGUuYmFja2dyb3VuZENvbG9yXT1cInJhbmdlVmFsdWVzW3NlbGVjdGVkSW5kZXhdLmNvbG9yXCJcclxuICAgICAgICAgICAgW25neElucHV0Q29sb3JdPVwibmd4R3JhZENvbG9yXCJcclxuICAgICAgICAgICAgW3NpbXBsZU1vZGVdPVwidHJ1ZVwiXHJcbiAgICAgICAgICAgIChjaGFuZ2UpPVwiZ2VuZXJhdGVHcmFkaWVudCgkZXZlbnQpXCI+PC9zcGFuPlxyXG4gICAgICAgIDwvZGl2PlxyXG4gICAgICA8L2Rpdj5cclxuICAgICAgPGRpdiBjbGFzcz1cIm5neC1jb2wtNlwiPlxyXG4gICAgICAgIDxkaXYgY2xhc3M9XCJsYWJlbFwiPlBvc2l0aW9uPC9kaXY+XHJcbiAgICAgICAgPGlucHV0XHJcbiAgICAgICAgICB0eXBlPVwibnVtYmVyXCJcclxuICAgICAgICAgIFsobmdNb2RlbCldPVwicmFuZ2VWYWx1ZXNbc2VsZWN0ZWRJbmRleF0udmFsdWVcIlxyXG4gICAgICAgICAgbWluPVwiMFwiXHJcbiAgICAgICAgICBtYXg9XCIxMDBcIlxyXG4gICAgICAgICAgbmFtZT1cInBvc2lpdGlvblwiXHJcbiAgICAgICAgICAoY2hhbmdlKT1cImdlbmVyYXRlR3JhZGllbnQoKTsgdXBkYXRlUmFuZ2VTbGlkZXIoKVwiIC8+XHJcbiAgICAgIDwvZGl2PlxyXG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LWNvbC02XCI+XHJcbiAgICAgICAgPGRpdiBjbGFzcz1cImxhYmVsXCI+Um90YXRpb248L2Rpdj5cclxuICAgICAgICA8c2VsZWN0IFsobmdNb2RlbCldPVwicm90YXRpb25cIiAoY2hhbmdlKT1cImdlbmVyYXRlR3JhZGllbnQoKVwiIG5hbWU9XCJyb3RhdGlvblwiPlxyXG4gICAgICAgICAgPG9wdGlvbiBbdmFsdWVdPVwiaXRlbVwiICpuZ0Zvcj1cImxldCBpdGVtIG9mIHJvdGF0aW9uTGlzdFwiPnt7IGl0ZW0gKyAnwrAnIH19PC9vcHRpb24+XHJcbiAgICAgICAgPC9zZWxlY3Q+XHJcbiAgICAgIDwvZGl2PlxyXG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LWNvbC02XCI+XHJcbiAgICAgICAgPGRpdiBjbGFzcz1cImxhYmVsXCI+VHlwZTwvZGl2PlxyXG4gICAgICAgIDxzZWxlY3QgWyhuZ01vZGVsKV09XCJ0eXBlXCIgKGNoYW5nZSk9XCJnZW5lcmF0ZUdyYWRpZW50KClcIiBuYW1lPVwidHlwZVwiPlxyXG4gICAgICAgICAgPG9wdGlvbiB2YWx1ZT1cImxpbmVhclwiPmxpbmVhcjwvb3B0aW9uPlxyXG4gICAgICAgICAgPG9wdGlvbiB2YWx1ZT1cInJhZGlhbFwiPnJhZGlhbDwvb3B0aW9uPlxyXG4gICAgICAgIDwvc2VsZWN0PlxyXG4gICAgICA8L2Rpdj5cclxuICAgIDwvZGl2PlxyXG5cclxuICAgIDxkaXYgY2xhc3M9XCJkZWZhdWx0LWxpc3RcIj5cclxuICAgICAgPHNwYW5cclxuICAgICAgICAqbmdGb3I9XCJsZXQgaXRlbSBvZiBkZWZhdWx0R3JhZGllbnRzOyBsZXQgaSA9IGluZGV4XCJcclxuICAgICAgICBbc3R5bGUuYmFja2dyb3VuZF09XCJpdGVtXCJcclxuICAgICAgICAoY2xpY2spPVwib25TZWxlY3REZWZhdWx0KGl0ZW0sIGkpXCI+PC9zcGFuPlxyXG4gICAgPC9kaXY+XHJcbiAgPC9kaXY+XHJcbjwvZGl2PlxyXG4iXX0=