@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
357 lines • 57.7 kB
JavaScript
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR, AbstractControl } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { get, map, some, min } from 'lodash-es';
import { BytesPipe } from '../common/bytes.pipe';
import { FilesService } from '../common/files.service';
import { gettext } from '../i18n/gettext';
import { CommonModule, IconDirective } from '../common';
import { C8yTranslatePipe } from '../i18n';
import { NgClass, NgIf } from '@angular/common';
import * as i0 from "@angular/core";
import * as i1 from "../common/files.service";
import * as i2 from "@ngx-translate/core";
import * as i3 from "../common/bytes.pipe";
import * as i4 from "../common/icon.directive";
import * as i5 from "../i18n/c8y-translate.directive";
import * as i6 from "@angular/common";
import * as i7 from "../common/loading.component";
import * as i8 from "../i18n/c8y-translate.pipe";
/**
* A drop-zone which is a file selector allowing users to select file(s) from their file system, either natively or by drag and drop.
*
* ```html
* <div>
* <c8y-drop-area
* (dropped)="uploadFile($event)"
* [icon]="'upload'"
* [accept]="'.zip,.7z,video'">
* </c8y-drop-area>
* </div>
* ```
*/
export class DropAreaComponent {
constructor(cd, filesService, translate, bytes, ref) {
this.cd = cd;
this.filesService = filesService;
this.translate = translate;
this.bytes = bytes;
this.ref = ref;
this.title = gettext('Upload file');
this.message = gettext('Drop file here');
this.icon = 'plus-square';
this.loadingMessage = gettext('Uploading…');
this.forceHideList = false;
/** Affects displaying both the drop zone and the list of dropped files. */
this.alwaysShow = false;
this.clickToOpen = true;
this.loading = false;
/**
* Current progress of the upload as a percentage. If not given a spinner will be displayed.
*/
this.progress = -1; // -1 = spinner
this.dropped = new EventEmitter();
this.maxAllowedFiles = Infinity;
this.isOver = false;
this.errors = false;
this.onChange = _ => undefined;
this.onTouched = () => undefined;
}
onkeyup(event) {
if (event.key === 'Enter') {
this.picker.nativeElement.click();
}
}
ngOnInit() {
this.acceptedExts = this.filesService.extractFileExtensions(this.accept).map(t => `.${t}`);
this.alwaysShow = this.alwaysShow || this.area.nativeElement.children.length === 0;
if (this.files && this.isFilesAnObjectOrArray() && this.files.length > 0) {
this.onFilesSelected(this.files);
}
}
ngAfterViewChecked() {
this.hasDropAreaSmallClass = this.ref.nativeElement.classList.contains('drop-area-sm');
}
/**
* Toggles the style of the drop zone element when a file is dragged over the component.
*/
toggle() {
this.zone.nativeElement.style.height = this.area.nativeElement.offsetHeight + 'px';
this.onOver();
}
/**
* Shows computer browser with files to drop into drop-area zone.
*/
showPicker($event) {
this.preventDefault($event);
this.picker.nativeElement.value = '';
this.picker.nativeElement.click();
}
/**
* Triggered when file is on over drop area, but not dropped.
*/
onOver() {
if (!this.isOver) {
this.isOver = true;
document.addEventListener('dragover', this.preventDefault);
document.addEventListener('drop', this.preventDefault);
}
}
/**
* Triggered when file is dropped.
*/
onPick($event) {
this.errors = false;
this.preventDefault($event);
this.onFilesSelected($event.target.files);
}
/**
* Handle file when it is dropped into drop-area.
*/
onDrop($event) {
this.preventDefault($event);
this.onFilesSelected($event.dataTransfer.files);
this.stopDragging();
}
/**
* Checks condition what should be displayed: drop-area zone or list of dropped files.
*/
shouldShowFilesList() {
return (this.isFilesAnObjectOrArray() &&
!this.forceHideList &&
this.alwaysShow &&
!this.isFilesArrayEmpty() &&
!this.hasEmptyFiles() &&
!this.isTooManyFiles());
}
/**
* Triggered when file is picked over web application.
*/
stopDragging() {
document.removeEventListener('dragover', this.preventDefault);
document.removeEventListener('drop', this.preventDefault);
this.isOver = false;
}
/**
* Delete files already dropped files.
*/
onDelete() {
delete this.files;
delete this.filesNameString;
this.clearErrors();
this.dropped.emit(null);
this.onChange(null);
this.onTouched();
this.cd.markForCheck();
}
writeValue(value) {
this.files = value;
if (!value) {
this.onDelete();
}
else {
this.filesNameString = this.getFilesNamesAsString(value);
}
this.cd.detectChanges();
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
async onFilesSelected(files) {
this.onTouched();
const hasValidNameLength = this.filesService.checkMaxLength(files);
if (!hasValidNameLength) {
this.onFileInvalidNameLength();
return;
}
const haveValidTypes = this.filesService.haveValidExtensions(files, this.accept);
if (!haveValidTypes) {
this.onFileInvalidType();
return;
}
const maxFileSizeInBytes = this.maxFileSizeInMegaBytes
? this.convertMegaBytesToBytes(this.maxFileSizeInMegaBytes)
: null;
const haveValidSizes = await this.filesService.haveValidSizes(files, maxFileSizeInBytes);
if (!haveValidSizes) {
await this.onFileInvalidSize();
return;
}
this.files = files;
this.filesNameString = this.getFilesNamesAsString(files);
this.errors = false;
if (this.isTooManyFiles()) {
this.errors = true;
this.formControl?.setErrors({ tooManyFiles: true });
this.errorMessage = gettext('Too many files selected.');
return;
}
if (this.hasEmptyFiles()) {
this.errors = true;
this.formControl?.setErrors({ emptyFiles: true });
this.errorMessage = gettext('File must not be empty, select another one.');
return;
}
const droppedFiles = this.compose(files);
this.dropped.emit(droppedFiles);
this.onChange(droppedFiles);
this.cd.markForCheck();
}
onFileInvalidNameLength() {
this.errors = true;
this.formControl?.setErrors({ invalidNameLength: true });
this.errorMessage = gettext('The filename is too long.');
}
onFileInvalidType() {
this.errors = true;
this.formControl?.setErrors({ invalidType: true });
this.errorMessage = gettext('The selected file is not supported.');
}
async onFileInvalidSize() {
const maxFileSizeInBytes = this.maxFileSizeInMegaBytes
? this.convertMegaBytesToBytes(this.maxFileSizeInMegaBytes)
: null;
const msg = gettext('The selected file is too large. The size limit is {{ limit }}.');
const limit = this.bytes.transform(min([maxFileSizeInBytes, await this.filesService.loadBytesSizeLimit()]));
this.errors = true;
this.formControl?.setErrors({ invalidSize: true });
this.errorMessage = this.translate.instant(msg, { limit });
}
convertMegaBytesToBytes(maxFileSizeInMegaBytes) {
return maxFileSizeInMegaBytes * 1_048_576;
}
getFilesNamesAsString(files) {
return map(files, ({ name }) => name).join(', ');
}
isFilesArrayEmpty() {
return get(this, 'files.length', 0) === 0;
}
isTooManyFiles() {
return get(this, 'files.length', 0) > this.maxAllowedFiles;
}
isFilesAnObjectOrArray() {
return typeof this.files === 'object';
}
hasEmptyFiles() {
let result = true;
if (!this.isFilesArrayEmpty()) {
result = this.isAnyFileEmpty();
}
return result;
}
isAnyFileEmpty() {
return some(Array.from(this.files), ['size', 0]);
}
clearErrors() {
delete this.errorMessage;
this.errors = false;
this.formControl?.setErrors(null);
}
preventDefault($event) {
if ($event) {
$event.preventDefault();
}
}
compose(files) {
return Array.from(files).map(file => ({
file,
readAsJson: async () => JSON.parse(await this.read(file, ReadAsType.TEXT)),
readAsText: async () => this.read(file, ReadAsType.TEXT),
readAsArrayBuffer: async () => this.read(file, ReadAsType.ARRAY_BUFFER),
readAsBinaryString: async () => this.read(file, ReadAsType.BINARY_STRING),
readAsDataURL: async () => this.read(file, ReadAsType.DATA_URL)
}));
}
async read(file, type) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
switch (type) {
case ReadAsType.TEXT: {
reader.readAsText(file);
break;
}
case ReadAsType.ARRAY_BUFFER: {
reader.readAsArrayBuffer(file);
break;
}
case ReadAsType.BINARY_STRING: {
reader.readAsBinaryString(file);
break;
}
case ReadAsType.DATA_URL: {
reader.readAsDataURL(file);
break;
}
}
reader.onload = () => this.onLoad(reader, resolve, reject);
});
}
onLoad(reader, resolve, reject) {
if (reader.readyState !== 2) {
return;
}
if (reader.error) {
reject(reader.error);
}
resolve(reader.result);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DropAreaComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.FilesService }, { token: i2.TranslateService }, { token: i3.BytesPipe }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DropAreaComponent, isStandalone: true, selector: "c8y-drop-area", inputs: { formControl: "formControl", title: "title", message: "message", icon: "icon", loadingMessage: "loadingMessage", forceHideList: "forceHideList", alwaysShow: "alwaysShow", clickToOpen: "clickToOpen", loading: "loading", progress: "progress", maxAllowedFiles: "maxAllowedFiles", files: "files", maxFileSizeInMegaBytes: "maxFileSizeInMegaBytes", accept: "accept" }, outputs: { dropped: "dropped" }, host: { listeners: { "keyup": "onkeyup($event)" } }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: DropAreaComponent, multi: true }], viewQueries: [{ propertyName: "area", first: true, predicate: ["area"], descendants: true, static: true }, { propertyName: "zone", first: true, predicate: ["zone"], descendants: true }, { propertyName: "picker", first: true, predicate: ["picker"], descendants: true }], ngImport: i0, template: "<div\n class=\"drop-zone\"\n [style.pointerEvents]=\"loading ? 'none' : 'auto'\"\n [style.display]=\"isOver || alwaysShow || loading ? 'block' : 'none'\"\n tabindex=\"0\"\n *ngIf=\"!shouldShowFilesList()\"\n [ngClass]=\"{ 'has-errors': errors, disabled: formControl?.disabled }\"\n #zone\n (dragleave)=\"stopDragging()\"\n (drop)=\"onDrop($event)\"\n (dragover)=\"onOver()\"\n (click)=\"showPicker($event)\"\n>\n <div\n class=\"file-placeholder\"\n data-cy=\"c8y-file-placeholder--drop-zone\"\n [ngClass]=\"{ 'drag-over': isOver }\"\n >\n <div\n class=\"d-flex d-col p-4 flex-center\"\n *ngIf=\"loading\"\n >\n <div\n class=\"progress progress-striped active m-0\"\n style=\"min-width: 50%\"\n *ngIf=\"progress !== -1\"\n >\n <div\n class=\"progress-bar\"\n [style.width]=\"progress + '%'\"\n [attr.aria-label]=\"progress + '%'\"\n aria-valuenow=\"0\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n role=\"progressbar\"\n ></div>\n </div>\n <div\n class=\"spinner-snake\"\n *ngIf=\"progress === -1\"\n ></div>\n <p\n class=\"m-t-auto m-b-auto m-r-8\"\n *ngIf=\"!hasDropAreaSmallClass\"\n >\n {{ loadingMessage | translate }}\n </p>\n </div>\n <div\n class=\"hint-placeholder pointer\"\n *ngIf=\"!loading\"\n data-cy=\"drop-zone--hint-placeholder\"\n >\n <i class=\"dlt-c8y-icon-{{ icon }}\"></i>\n <p *ngIf=\"!errors\">\n <b>{{ message | translate }}</b>\n <br />\n <span\n *ngIf=\"alwaysShow && clickToOpen\"\n translate\n ></span>\n </p>\n <div\n class=\"has-errors\"\n *ngIf=\"errors\"\n >\n <p class=\"form-control-feedback-message\">\n {{ errorMessage | translate }}\n </p>\n </div>\n </div>\n </div>\n</div>\n\n<div\n class=\"drop-zone\"\n [style.display]=\"isOver || alwaysShow || loading ? 'block' : 'none'\"\n tabindex=\"0\"\n *ngIf=\"shouldShowFilesList()\"\n>\n <div\n class=\"p-absolute p-4 fit-w fit-h d-flex d-col j-c-center a-i-center\"\n *ngIf=\"loading\"\n >\n <div\n class=\"progress progress-striped active m-0\"\n style=\"min-width: 80%\"\n *ngIf=\"progress !== -1\"\n >\n <div\n class=\"progress-bar\"\n [style.width]=\"progress + '%'\"\n [attr.aria-label]=\"progress + '%'\"\n aria-valuenow=\"0\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n role=\"progressbar\"\n ></div>\n </div>\n <div *ngIf=\"progress === -1\">\n <c8y-loading></c8y-loading>\n </div>\n <p\n class=\"m-b-8\"\n *ngIf=\"!hasDropAreaSmallClass\"\n >\n <strong>\n {{ loadingMessage | translate }}\n </strong>\n </p>\n </div>\n <div\n class=\"file-placeholder p-4\"\n *ngIf=\"!loading\"\n >\n <div class=\"d-flex p-4 a-i-center\">\n <i\n class=\"icon-20 m-r-8\"\n c8yIcon=\"file-o\"\n ></i>\n <span\n class=\"text-truncate\"\n title=\"{{ filesNameString }}\"\n >\n {{ filesNameString }}\n </span>\n <button\n class=\"btn btn-dot btn-dot--danger showOnHover m-l-auto\"\n title=\"{{ 'Remove' | translate }}\"\n [attr.aria-label]=\"'Remove' | translate\"\n type=\"button\"\n >\n <i\n c8yIcon=\"minus-circle\"\n (click)=\"onDelete()\"\n ></i>\n </button>\n </div>\n </div>\n</div>\n<label\n class=\"sr-only\"\n for=\"file\"\n>\n {{ 'Select file' | translate }}\n</label>\n<input\n class=\"hidden\"\n id=\"file\"\n type=\"file\"\n #picker\n *ngIf=\"clickToOpen\"\n (change)=\"onPick($event)\"\n (click)=\"picker.focus()\"\n (blur)=\"onTouched()\"\n [accept]=\"acceptedExts\"\n [multiple]=\"maxAllowedFiles > 1\"\n [disabled]=\"formControl?.disabled\"\n/>\n<div\n #area\n [hidden]=\"isOver || loading\"\n (dragover)=\"toggle()\"\n>\n <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i5.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i6.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i7.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "pipe", type: i8.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DropAreaComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-drop-area', providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: DropAreaComponent, multi: true }], standalone: true, imports: [CommonModule, C8yTranslatePipe, NgIf, NgClass, IconDirective], template: "<div\n class=\"drop-zone\"\n [style.pointerEvents]=\"loading ? 'none' : 'auto'\"\n [style.display]=\"isOver || alwaysShow || loading ? 'block' : 'none'\"\n tabindex=\"0\"\n *ngIf=\"!shouldShowFilesList()\"\n [ngClass]=\"{ 'has-errors': errors, disabled: formControl?.disabled }\"\n #zone\n (dragleave)=\"stopDragging()\"\n (drop)=\"onDrop($event)\"\n (dragover)=\"onOver()\"\n (click)=\"showPicker($event)\"\n>\n <div\n class=\"file-placeholder\"\n data-cy=\"c8y-file-placeholder--drop-zone\"\n [ngClass]=\"{ 'drag-over': isOver }\"\n >\n <div\n class=\"d-flex d-col p-4 flex-center\"\n *ngIf=\"loading\"\n >\n <div\n class=\"progress progress-striped active m-0\"\n style=\"min-width: 50%\"\n *ngIf=\"progress !== -1\"\n >\n <div\n class=\"progress-bar\"\n [style.width]=\"progress + '%'\"\n [attr.aria-label]=\"progress + '%'\"\n aria-valuenow=\"0\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n role=\"progressbar\"\n ></div>\n </div>\n <div\n class=\"spinner-snake\"\n *ngIf=\"progress === -1\"\n ></div>\n <p\n class=\"m-t-auto m-b-auto m-r-8\"\n *ngIf=\"!hasDropAreaSmallClass\"\n >\n {{ loadingMessage | translate }}\n </p>\n </div>\n <div\n class=\"hint-placeholder pointer\"\n *ngIf=\"!loading\"\n data-cy=\"drop-zone--hint-placeholder\"\n >\n <i class=\"dlt-c8y-icon-{{ icon }}\"></i>\n <p *ngIf=\"!errors\">\n <b>{{ message | translate }}</b>\n <br />\n <span\n *ngIf=\"alwaysShow && clickToOpen\"\n translate\n ></span>\n </p>\n <div\n class=\"has-errors\"\n *ngIf=\"errors\"\n >\n <p class=\"form-control-feedback-message\">\n {{ errorMessage | translate }}\n </p>\n </div>\n </div>\n </div>\n</div>\n\n<div\n class=\"drop-zone\"\n [style.display]=\"isOver || alwaysShow || loading ? 'block' : 'none'\"\n tabindex=\"0\"\n *ngIf=\"shouldShowFilesList()\"\n>\n <div\n class=\"p-absolute p-4 fit-w fit-h d-flex d-col j-c-center a-i-center\"\n *ngIf=\"loading\"\n >\n <div\n class=\"progress progress-striped active m-0\"\n style=\"min-width: 80%\"\n *ngIf=\"progress !== -1\"\n >\n <div\n class=\"progress-bar\"\n [style.width]=\"progress + '%'\"\n [attr.aria-label]=\"progress + '%'\"\n aria-valuenow=\"0\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n role=\"progressbar\"\n ></div>\n </div>\n <div *ngIf=\"progress === -1\">\n <c8y-loading></c8y-loading>\n </div>\n <p\n class=\"m-b-8\"\n *ngIf=\"!hasDropAreaSmallClass\"\n >\n <strong>\n {{ loadingMessage | translate }}\n </strong>\n </p>\n </div>\n <div\n class=\"file-placeholder p-4\"\n *ngIf=\"!loading\"\n >\n <div class=\"d-flex p-4 a-i-center\">\n <i\n class=\"icon-20 m-r-8\"\n c8yIcon=\"file-o\"\n ></i>\n <span\n class=\"text-truncate\"\n title=\"{{ filesNameString }}\"\n >\n {{ filesNameString }}\n </span>\n <button\n class=\"btn btn-dot btn-dot--danger showOnHover m-l-auto\"\n title=\"{{ 'Remove' | translate }}\"\n [attr.aria-label]=\"'Remove' | translate\"\n type=\"button\"\n >\n <i\n c8yIcon=\"minus-circle\"\n (click)=\"onDelete()\"\n ></i>\n </button>\n </div>\n </div>\n</div>\n<label\n class=\"sr-only\"\n for=\"file\"\n>\n {{ 'Select file' | translate }}\n</label>\n<input\n class=\"hidden\"\n id=\"file\"\n type=\"file\"\n #picker\n *ngIf=\"clickToOpen\"\n (change)=\"onPick($event)\"\n (click)=\"picker.focus()\"\n (blur)=\"onTouched()\"\n [accept]=\"acceptedExts\"\n [multiple]=\"maxAllowedFiles > 1\"\n [disabled]=\"formControl?.disabled\"\n/>\n<div\n #area\n [hidden]=\"isOver || loading\"\n (dragover)=\"toggle()\"\n>\n <ng-content></ng-content>\n</div>\n" }]
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.FilesService }, { type: i2.TranslateService }, { type: i3.BytesPipe }, { type: i0.ElementRef }], propDecorators: { formControl: [{
type: Input
}], title: [{
type: Input
}], message: [{
type: Input
}], icon: [{
type: Input
}], loadingMessage: [{
type: Input
}], forceHideList: [{
type: Input
}], alwaysShow: [{
type: Input
}], clickToOpen: [{
type: Input
}], loading: [{
type: Input
}], progress: [{
type: Input
}], dropped: [{
type: Output
}], maxAllowedFiles: [{
type: Input
}], files: [{
type: Input
}], maxFileSizeInMegaBytes: [{
type: Input
}], accept: [{
type: Input
}], area: [{
type: ViewChild,
args: ['area', { static: true }]
}], zone: [{
type: ViewChild,
args: ['zone', { static: false }]
}], picker: [{
type: ViewChild,
args: ['picker', { static: false }]
}], onkeyup: [{
type: HostListener,
args: ['keyup', ['$event']]
}] } });
var ReadAsType;
(function (ReadAsType) {
ReadAsType[ReadAsType["TEXT"] = 0] = "TEXT";
ReadAsType[ReadAsType["DATA_URL"] = 1] = "DATA_URL";
ReadAsType[ReadAsType["ARRAY_BUFFER"] = 2] = "ARRAY_BUFFER";
ReadAsType[ReadAsType["BINARY_STRING"] = 3] = "BINARY_STRING";
})(ReadAsType || (ReadAsType = {}));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcC1hcmVhLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2NvcmUvZHJvcC1hcmVhL2Ryb3AtYXJlYS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9jb3JlL2Ryb3AtYXJlYS9kcm9wLWFyZWEuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixTQUFTLEVBQ1QsVUFBVSxFQUNWLFlBQVksRUFDWixZQUFZLEVBQ1osS0FBSyxFQUVMLE1BQU0sRUFDTixTQUFTLEVBQ1YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUF3QixpQkFBaUIsRUFBRSxlQUFlLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUUxRixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN2RCxPQUFPLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ2hELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNqRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDdkQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3hELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUMzQyxPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLGlCQUFpQixDQUFDOzs7Ozs7Ozs7O0FBRWhEOzs7Ozs7Ozs7Ozs7R0FZRztBQVNILE1BQU0sT0FBTyxpQkFBaUI7SUFnRDVCLFlBQ1UsRUFBcUIsRUFDckIsWUFBMEIsRUFDMUIsU0FBMkIsRUFDM0IsS0FBZ0IsRUFDaEIsR0FBZTtRQUpmLE9BQUUsR0FBRixFQUFFLENBQW1CO1FBQ3JCLGlCQUFZLEdBQVosWUFBWSxDQUFjO1FBQzFCLGNBQVMsR0FBVCxTQUFTLENBQWtCO1FBQzNCLFVBQUssR0FBTCxLQUFLLENBQVc7UUFDaEIsUUFBRyxHQUFILEdBQUcsQ0FBWTtRQW5EaEIsVUFBSyxHQUFHLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUMvQixZQUFPLEdBQUcsT0FBTyxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDcEMsU0FBSSxHQUFHLGFBQWEsQ0FBQztRQUNyQixtQkFBYyxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUN2QyxrQkFBYSxHQUFHLEtBQUssQ0FBQztRQUMvQiwyRUFBMkU7UUFDbEUsZUFBVSxHQUFHLEtBQUssQ0FBQztRQUNuQixnQkFBVyxHQUFHLElBQUksQ0FBQztRQUNuQixZQUFPLEdBQUcsS0FBSyxDQUFDO1FBQ3pCOztXQUVHO1FBQ00sYUFBUSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsZUFBZTtRQUM3QixZQUFPLEdBQWdDLElBQUksWUFBWSxFQUFFLENBQUM7UUFDM0Qsb0JBQWUsR0FBRyxRQUFRLENBQUM7UUFxQnBDLFdBQU0sR0FBRyxLQUFLLENBQUM7UUFDZixXQUFNLEdBQUcsS0FBSyxDQUFDO1FBd0hmLGFBQVEsR0FBeUIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUM7UUFDaEQsY0FBUyxHQUFlLEdBQUcsRUFBRSxDQUFDLFNBQVMsQ0FBQztJQXpHckMsQ0FBQztJQUdKLE9BQU8sQ0FBQyxLQUFvQjtRQUMxQixJQUFJLEtBQUssQ0FBQyxHQUFHLEtBQUssT0FBTyxFQUFFLENBQUM7WUFDMUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDcEMsQ0FBQztJQUNILENBQUM7SUFFRCxRQUFRO1FBQ04sSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLHFCQUFxQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDM0YsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDO1FBRW5GLElBQUksSUFBSSxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsc0JBQXNCLEVBQUUsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN6RSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNuQyxDQUFDO0lBQ0gsQ0FBQztJQUVELGtCQUFrQjtRQUNoQixJQUFJLENBQUMscUJBQXFCLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUN6RixDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNO1FBQ0osSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDO1FBQ25GLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUNoQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxVQUFVLENBQUMsTUFBTztRQUNoQixJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzVCLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLEtBQUssR0FBRyxFQUFFLENBQUM7UUFDckMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDcEMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTTtRQUNKLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDakIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7WUFDbkIsUUFBUSxDQUFDLGdCQUFnQixDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7WUFDM0QsUUFBUSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDekQsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxNQUFNO1FBQ1gsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7UUFDcEIsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM1QixJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLE1BQU07UUFDWCxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzVCLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNoRCxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsbUJBQW1CO1FBQ2pCLE9BQU8sQ0FDTCxJQUFJLENBQUMsc0JBQXNCLEVBQUU7WUFDN0IsQ0FBQyxJQUFJLENBQUMsYUFBYTtZQUNuQixJQUFJLENBQUMsVUFBVTtZQUNmLENBQUMsSUFBSSxDQUFDLGlCQUFpQixFQUFFO1lBQ3pCLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRTtZQUNyQixDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FDdkIsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILFlBQVk7UUFDVixRQUFRLENBQUMsbUJBQW1CLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUM5RCxRQUFRLENBQUMsbUJBQW1CLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUMxRCxJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztJQUN0QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxRQUFRO1FBQ04sT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDO1FBQ2xCLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQztRQUM1QixJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDbkIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwQixJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDakIsSUFBSSxDQUFDLEVBQUUsQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUN6QixDQUFDO0lBS0QsVUFBVSxDQUFDLEtBQVU7UUFDbkIsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFDbkIsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ2xCLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMscUJBQXFCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDM0QsQ0FBQztRQUNELElBQUksQ0FBQyxFQUFFLENBQUMsYUFBYSxFQUFFLENBQUM7SUFDMUIsQ0FBQztJQUVELGdCQUFnQixDQUFDLEVBQU87UUFDdEIsSUFBSSxDQUFDLFFBQVEsR0FBRyxFQUFFLENBQUM7SUFDckIsQ0FBQztJQUVELGlCQUFpQixDQUFDLEVBQU87UUFDdkIsSUFBSSxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUVPLEtBQUssQ0FBQyxlQUFlLENBQUMsS0FBZTtRQUMzQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDakIsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNuRSxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztZQUN4QixJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztZQUMvQixPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsbUJBQW1CLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNqRixJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDcEIsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7WUFDekIsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLGtCQUFrQixHQUFHLElBQUksQ0FBQyxzQkFBc0I7WUFDcEQsQ0FBQyxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsc0JBQXNCLENBQUM7WUFDM0QsQ0FBQyxDQUFDLElBQUksQ0FBQztRQUNULE1BQU0sY0FBYyxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxjQUFjLENBQUMsS0FBSyxFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDekYsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3BCLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7WUFDL0IsT0FBTztRQUNULENBQUM7UUFFRCxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztRQUNuQixJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN6RCxJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztRQUVwQixJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsRUFBRSxDQUFDO1lBQzFCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1lBQ25CLElBQUksQ0FBQyxXQUFXLEVBQUUsU0FBUyxDQUFDLEVBQUUsWUFBWSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7WUFDcEQsSUFBSSxDQUFDLFlBQVksR0FBRyxPQUFPLENBQUMsMEJBQTBCLENBQUMsQ0FBQztZQUN4RCxPQUFPO1FBQ1QsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLGFBQWEsRUFBRSxFQUFFLENBQUM7WUFDekIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7WUFDbkIsSUFBSSxDQUFDLFdBQVcsRUFBRSxTQUFTLENBQUMsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztZQUNsRCxJQUFJLENBQUMsWUFBWSxHQUFHLE9BQU8sQ0FBQyw2Q0FBNkMsQ0FBQyxDQUFDO1lBQzNFLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxZQUFZLEdBQWtCLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDeEQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDaEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUM1QixJQUFJLENBQUMsRUFBRSxDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFTyx1QkFBdUI7UUFDN0IsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7UUFDbkIsSUFBSSxDQUFDLFdBQVcsRUFBRSxTQUFTLENBQUMsRUFBRSxpQkFBaUIsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3pELElBQUksQ0FBQyxZQUFZLEdBQUcsT0FBTyxDQUFDLDJCQUEyQixDQUFDLENBQUM7SUFDM0QsQ0FBQztJQUVPLGlCQUFpQjtRQUN2QixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNuQixJQUFJLENBQUMsV0FBVyxFQUFFLFNBQVMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ25ELElBQUksQ0FBQyxZQUFZLEdBQUcsT0FBTyxDQUFDLHFDQUFxQyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUVPLEtBQUssQ0FBQyxpQkFBaUI7UUFDN0IsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLENBQUMsc0JBQXNCO1lBQ3BELENBQUMsQ0FBQyxJQUFJLENBQUMsdUJBQXVCLENBQUMsSUFBSSxDQUFDLHNCQUFzQixDQUFDO1lBQzNELENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDVCxNQUFNLEdBQUcsR0FBRyxPQUFPLENBQUMsZ0VBQWdFLENBQUMsQ0FBQztRQUN0RixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FDaEMsR0FBRyxDQUFDLENBQUMsa0JBQWtCLEVBQUUsTUFBTSxJQUFJLENBQUMsWUFBWSxDQUFDLGtCQUFrQixFQUFFLENBQUMsQ0FBQyxDQUN4RSxDQUFDO1FBQ0YsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7UUFDbkIsSUFBSSxDQUFDLFdBQVcsRUFBRSxTQUFTLENBQUMsRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNuRCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7SUFDN0QsQ0FBQztJQUVPLHVCQUF1QixDQUFDLHNCQUF1QztRQUNyRSxPQUFPLHNCQUFzQixHQUFHLFNBQVMsQ0FBQztJQUM1QyxDQUFDO0lBRU8scUJBQXFCLENBQUMsS0FBZTtRQUMzQyxPQUFPLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUVPLGlCQUFpQjtRQUN2QixPQUFPLEdBQUcsQ0FBQyxJQUFJLEVBQUUsY0FBYyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRU8sY0FBYztRQUNwQixPQUFPLEdBQUcsQ0FBQyxJQUFJLEVBQUUsY0FBYyxFQUFFLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUM7SUFDN0QsQ0FBQztJQUVPLHNCQUFzQjtRQUM1QixPQUFPLE9BQU8sSUFBSSxDQUFDLEtBQUssS0FBSyxRQUFRLENBQUM7SUFDeEMsQ0FBQztJQUVPLGFBQWE7UUFDbkIsSUFBSSxNQUFNLEdBQUcsSUFBSSxDQUFDO1FBQ2xCLElBQUksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsRUFBRSxDQUFDO1lBQzlCLE1BQU0sR0FBRyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDakMsQ0FBQztRQUNELE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFTyxjQUFjO1FBQ3BCLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUVPLFdBQVc7UUFDakIsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBQ3BCLElBQUksQ0FBQyxXQUFXLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFTyxjQUFjLENBQUMsTUFBTztRQUM1QixJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQ1gsTUFBTSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQzFCLENBQUM7SUFDSCxDQUFDO0lBRU8sT0FBTyxDQUFDLEtBQWU7UUFDN0IsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDcEMsSUFBSTtZQUNKLFVBQVUsRUFBRSxLQUFLLElBQUksRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDMUUsVUFBVSxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsVUFBVSxDQUFDLElBQUksQ0FBQztZQUN4RCxpQkFBaUIsRUFBRSxLQUFLLElBQUksRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFVBQVUsQ0FBQyxZQUFZLENBQUM7WUFDdkUsa0JBQWtCLEVBQUUsS0FBSyxJQUFJLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxVQUFVLENBQUMsYUFBYSxDQUFDO1lBQ3pFLGFBQWEsRUFBRSxLQUFLLElBQUksRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFVBQVUsQ0FBQyxRQUFRLENBQUM7U0FDaEUsQ0FBQyxDQUFDLENBQUM7SUFDTixDQUFDO0lBRU8sS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBZ0I7UUFDdkMsT0FBTyxJQUFJLE9BQU8sQ0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUM3QyxNQUFNLE1BQU0sR0FBRyxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQ2hDLFFBQVEsSUFBSSxFQUFFLENBQUM7Z0JBQ2IsS0FBSyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztvQkFDckIsTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztvQkFDeEIsTUFBTTtnQkFDUixDQUFDO2dCQUNELEtBQUssVUFBVSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7b0JBQzdCLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztvQkFDL0IsTUFBTTtnQkFDUixDQUFDO2dCQUNELEtBQUssVUFBVSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7b0JBQzlCLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztvQkFDaEMsTUFBTTtnQkFDUixDQUFDO2dCQUNELEtBQUssVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7b0JBQ3pCLE1BQU0sQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLENBQUM7b0JBQzNCLE1BQU07Z0JBQ1IsQ0FBQztZQUNILENBQUM7WUFDRCxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUM3RCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTyxNQUFNLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNO1FBQ3BDLElBQUksTUFBTSxDQUFDLFVBQVUsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUM1QixPQUFPO1FBQ1QsQ0FBQztRQUNELElBQUksTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2pCLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDdkIsQ0FBQztRQUNELE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDekIsQ0FBQzsrR0FuVlUsaUJBQWlCO21HQUFqQixpQkFBaUIsc2dCQUpqQixDQUFDLEVBQUUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLFdBQVcsRUFBRSxpQkFBaUIsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsd1NDdkMxRix1aElBc0tBLDJDRDdIWSxZQUFZOzs0RkFFWCxpQkFBaUI7a0JBUDdCLFNBQVM7K0JBQ0UsZUFBZSxhQUVkLENBQUMsRUFBRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsV0FBVyxtQkFBbUIsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsY0FDNUUsSUFBSSxXQUNQLENBQUMsWUFBWSxFQUFFLGdCQUFnQixFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsYUFBYSxDQUFDO2lNQUc5RCxXQUFXO3NCQUFuQixLQUFLO2dCQUNHLEtBQUs7c0JBQWIsS0FBSztnQkFDRyxPQUFPO3NCQUFmLEtBQUs7Z0JBQ0csSUFBSTtzQkFBWixLQUFLO2dCQUNHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBQ0csYUFBYTtzQkFBckIsS0FBSztnQkFFRyxVQUFVO3NCQUFsQixLQUFLO2dCQUNHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUlHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0ksT0FBTztzQkFBaEIsTUFBTTtnQkFDRSxlQUFlO3NCQUF2QixLQUFLO2dCQUNHLEtBQUs7c0JBQWIsS0FBSztnQkFDRyxzQkFBc0I7c0JBQTlCLEtBQUs7Z0JBa0JHLE1BQU07c0JBQWQsS0FBSztnQkFRK0IsSUFBSTtzQkFBeEMsU0FBUzt1QkFBQyxNQUFNLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO2dCQUNHLElBQUk7c0JBQXpDLFNBQVM7dUJBQUMsTUFBTSxFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRTtnQkFDSSxNQUFNO3NCQUE3QyxTQUFTO3VCQUFDLFFBQVEsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUU7Z0JBV3RDLE9BQU87c0JBRE4sWUFBWTt1QkFBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUM7O0FBdVNuQyxJQUFLLFVBS0o7QUFMRCxXQUFLLFVBQVU7SUFDYiwyQ0FBSSxDQUFBO0lBQ0osbURBQVEsQ0FBQTtJQUNSLDJEQUFZLENBQUE7SUFDWiw2REFBYSxDQUFBO0FBQ2YsQ0FBQyxFQUxJLFVBQVUsS0FBVixVQUFVLFFBS2QiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDaGFuZ2VEZXRlY3RvclJlZixcbiAgQ29tcG9uZW50LFxuICBFbGVtZW50UmVmLFxuICBFdmVudEVtaXR0ZXIsXG4gIEhvc3RMaXN0ZW5lcixcbiAgSW5wdXQsXG4gIE9uSW5pdCxcbiAgT3V0cHV0LFxuICBWaWV3Q2hpbGRcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb250cm9sVmFsdWVBY2Nlc3NvciwgTkdfVkFMVUVfQUNDRVNTT1IsIEFic3RyYWN0Q29udHJvbCB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcblxuaW1wb3J0IHsgVHJhbnNsYXRlU2VydmljZSB9IGZyb20gJ0BuZ3gtdHJhbnNsYXRlL2NvcmUnO1xuaW1wb3J0IHsgZ2V0LCBtYXAsIHNvbWUsIG1pbiB9IGZyb20gJ2xvZGFzaC1lcyc7XG5pbXBvcnQgeyBCeXRlc1BpcGUgfSBmcm9tICcuLi9jb21tb24vYnl0ZXMucGlwZSc7XG5pbXBvcnQgeyBGaWxlc1NlcnZpY2UgfSBmcm9tICcuLi9jb21tb24vZmlsZXMuc2VydmljZSc7XG5pbXBvcnQgeyBnZXR0ZXh0IH0gZnJvbSAnLi4vaTE4bi9nZXR0ZXh0JztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSwgSWNvbkRpcmVjdGl2ZSB9IGZyb20gJy4uL2NvbW1vbic7XG5pbXBvcnQgeyBDOHlUcmFuc2xhdGVQaXBlIH0gZnJvbSAnLi4vaTE4bic7XG5pbXBvcnQgeyBOZ0NsYXNzLCBOZ0lmIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcblxuLyoqXG4gKiBBIGRyb3Atem9uZSB3aGljaCBpcyBhIGZpbGUgc2VsZWN0b3IgYWxsb3dpbmcgdXNlcnMgdG8gc2VsZWN0IGZpbGUocykgZnJvbSB0aGVpciBmaWxlIHN5c3RlbSwgZWl0aGVyIG5hdGl2ZWx5IG9yIGJ5IGRyYWcgYW5kIGRyb3AuXG4gKlxuICogYGBgaHRtbFxuICogIDxkaXY+XG4gKiAgICA8Yzh5LWRyb3AtYXJlYVxuICogICAgICAoZHJvcHBlZCk9XCJ1cGxvYWRGaWxlKCRldmVudClcIlxuICogICAgICBbaWNvbl09XCIndXBsb2FkJ1wiXG4gKiAgICAgIFthY2NlcHRdPVwiJy56aXAsLjd6LHZpZGVvJ1wiPlxuICogICAgPC9jOHktZHJvcC1hcmVhPlxuICogIDwvZGl2PlxuICogYGBgXG4gKi9cblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYzh5LWRyb3AtYXJlYScsXG4gIHRlbXBsYXRlVXJsOiAnLi9kcm9wLWFyZWEuY29tcG9uZW50Lmh0bWwnLFxuICBwcm92aWRlcnM6IFt7IHByb3ZpZGU6IE5HX1ZBTFVFX0FDQ0VTU09SLCB1c2VFeGlzdGluZzogRHJvcEFyZWFDb21wb25lbnQsIG11bHRpOiB0cnVlIH1dLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlLCBDOHlUcmFuc2xhdGVQaXBlLCBOZ0lmLCBOZ0NsYXNzLCBJY29uRGlyZWN0aXZlXVxufSlcbmV4cG9ydCBjbGFzcyBEcm9wQXJlYUNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgQ29udHJvbFZhbHVlQWNjZXNzb3Ige1xuICBASW5wdXQoKSBmb3JtQ29udHJvbDogQWJzdHJhY3RDb250cm9sPGFueSwgYW55PjtcbiAgQElucHV0KCkgdGl0bGUgPSBnZXR0ZXh0KCdVcGxvYWQgZmlsZScpO1xuICBASW5wdXQoKSBtZXNzYWdlID0gZ2V0dGV4dCgnRHJvcCBmaWxlIGhlcmUnKTtcbiAgQElucHV0KCkgaWNvbiA9ICdwbHVzLXNxdWFyZSc7XG4gIEBJbnB1dCgpIGxvYWRpbmdNZXNzYWdlID0gZ2V0dGV4dCgnVXBsb2FkaW5n4oCmJyk7XG4gIEBJbnB1dCgpIGZvcmNlSGlkZUxpc3QgPSBmYWxzZTtcbiAgLyoqIEFmZmVjdHMgZGlzcGxheWluZyBib3RoIHRoZSBkcm9wIHpvbmUgYW5kIHRoZSBsaXN0IG9mIGRyb3BwZWQgZmlsZXMuICovXG4gIEBJbnB1dCgpIGFsd2F5c1Nob3cgPSBmYWxzZTtcbiAgQElucHV0KCkgY2xpY2tUb09wZW4gPSB0cnVlO1xuICBASW5wdXQoKSBsb2FkaW5nID0gZmFsc2U7XG4gIC8qKlxuICAgKiBDdXJyZW50IHByb2dyZXNzIG9mIHRoZSB1cGxvYWQgYXMgYSBwZXJjZW50YWdlLiBJZiBub3QgZ2l2ZW4gYSBzcGlubmVyIHdpbGwgYmUgZGlzcGxheWVkLlxuICAgKi9cbiAgQElucHV0KCkgcHJvZ3Jlc3MgPSAtMTsgLy8gLTEgPSBzcGlubmVyXG4gIEBPdXRwdXQoKSBkcm9wcGVkOiBFdmVudEVtaXR0ZXI8RHJvcHBlZEZpbGVbXT4gPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG4gIEBJbnB1dCgpIG1heEFsbG93ZWRGaWxlcyA9IEluZmluaXR5O1xuICBASW5wdXQoKSBmaWxlczogRmlsZUxpc3Q7XG4gIEBJbnB1dCgpIG1heEZpbGVTaXplSW5NZWdhQnl0ZXM6IG51bWJlcjtcbiAgLyoqIFNwZWNpZmllcyBhIGZpbHRlciBmb3Igd2hhdCBmaWxlIHR5cGVzIHRoZSB1c2VyIGNhbiBwaWNrIGZyb20gdGhlIGZpbGUgaW5wdXQgZGlhbG9nIGJveC5cbiAgICpcbiAgICogU3BlY2lmeSBmaWxlIHR5cGVzIGJ5IGV4dGVuc2lvbnM6XG4gICAqIGBgYGh0bWxcbiAgICogIFthY2NlcHRdPVwiJy56aXAsLjd6J1wiXG4gICAqIGBgYFxuICAgKlxuICAgKiBTcGVjaWZ5IGZpbGUgdHlwZXMgYnkgZXh0ZW5zaW9ucyBhbmQgZ2VuZXJpYyB0eXBlcyBbR0VORVJJQ19GSUxFX1RZUEVde0BsaW5rIEdFTkVSSUNfRklMRV9UWVBFfTpcbiAgICogYGBgaHRtbFxuICAgKiAgW2FjY2VwdF09XCInLnBkZixhcmNoaXZlJ1wiXG4gICAqIGBgYFxuICAgKlxuICAgKiBTcGVjaWZ5IGZpbGUgdHlwZXMgYnkgZ2VuZXJpYyB0eXBlcyBbR0VORVJJQ19GSUxFX1RZUEVde0BsaW5rIEdFTkVSSUNfRklMRV9UWVBFfTpcbiAgICogYGBgaHRtbFxuICAgKiAgW2FjY2VwdF09XCInYXJjaGl2ZSx2aWRlbydcIlxuICAgKiBgYGBcbiAgICovXG4gIEBJbnB1dCgpIGFjY2VwdDogc3RyaW5nO1xuICBpc092ZXIgPSBmYWxzZTtcbiAgZXJyb3JzID0gZmFsc2U7XG4gIGVycm9yTWVzc2FnZTogc3RyaW5nO1xuICBmaWxlc05hbWVTdHJpbmc6IHN0cmluZztcbiAgYWNjZXB0ZWRFeHRzOiBzdHJpbmdbXTtcbiAgaGFzRHJvcEFyZWFTbWFsbENsYXNzOiBib29sZWFuO1xuXG4gIEBWaWV3Q2hpbGQoJ2FyZWEnLCB7IHN0YXRpYzogdHJ1ZSB9KSBhcmVhOiBFbGVtZW50UmVmO1xuICBAVmlld0NoaWxkKCd6b25lJywgeyBzdGF0aWM6IGZhbHNlIH0pIHpvbmU6IEVsZW1lbnRSZWY7XG4gIEBWaWV3Q2hpbGQoJ3BpY2tlcicsIHsgc3RhdGljOiBmYWxzZSB9KSBwaWNrZXI6IEVsZW1lbnRSZWY7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBjZDogQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgcHJpdmF0ZSBmaWxlc1NlcnZpY2U6IEZpbGVzU2VydmljZSxcbiAgICBwcml2YXRlIHRyYW5zbGF0ZTogVHJhbnNsYXRlU2VydmljZSxcbiAgICBwcml2YXRlIGJ5dGVzOiBCeXRlc1BpcGUsXG4gICAgcHJpdmF0ZSByZWY6IEVsZW1lbnRSZWZcbiAgKSB7fVxuXG4gIEBIb3N0TGlzdGVuZXIoJ2tleXVwJywgWyckZXZlbnQnXSlcbiAgb25rZXl1cChldmVudDogS2V5Ym9hcmRFdmVudCkge1xuICAgIGlmIChldmVudC5rZXkgPT09ICdFbnRlcicpIHtcbiAgICAgIHRoaXMucGlja2VyLm5hdGl2ZUVsZW1lbnQuY2xpY2soKTtcbiAgICB9XG4gIH1cblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLmFjY2VwdGVkRXh0cyA9IHRoaXMuZmlsZXNTZXJ2aWNlLmV4dHJhY3RGaWxlRXh0ZW5zaW9ucyh0aGlzLmFjY2VwdCkubWFwKHQgPT4gYC4ke3R9YCk7XG4gICAgdGhpcy5hbHdheXNTaG93ID0gdGhpcy5hbHdheXNTaG93IHx8IHRoaXMuYXJlYS5uYXRpdmVFbGVtZW50LmNoaWxkcmVuLmxlbmd0aCA9PT0gMDtcblxuICAgIGlmICh0aGlzLmZpbGVzICYmIHRoaXMuaXNGaWxlc0FuT2JqZWN0T3JBcnJheSgpICYmIHRoaXMuZmlsZXMubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5vbkZpbGVzU2VsZWN0ZWQodGhpcy5maWxlcyk7XG4gICAgfVxuICB9XG5cbiAgbmdBZnRlclZpZXdDaGVja2VkKCkge1xuICAgIHRoaXMuaGFzRHJvcEFyZWFTbWFsbENsYXNzID0gdGhpcy5yZWYubmF0aXZlRWxlbWVudC5jbGFzc0xpc3QuY29udGFpbnMoJ2Ryb3AtYXJlYS1zbScpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRvZ2dsZXMgdGhlIHN0eWxlIG9mIHRoZSBkcm9wIHpvbmUgZWxlbWVudCB3aGVuIGEgZmlsZSBpcyBkcmFnZ2VkIG92ZXIgdGhlIGNvbXBvbmVudC5cbiAgICovXG4gIHRvZ2dsZSgpOiB2b2lkIHtcbiAgICB0aGlzLnpvbmUubmF0aXZlRWxlbWVudC5zdHlsZS5oZWlnaHQgPSB0aGlzLmFyZWEubmF0aXZlRWxlbWVudC5vZmZzZXRIZWlnaHQgKyAncHgnO1xuICAgIHRoaXMub25PdmVyKCk7XG4gIH1cblxuICAvKipcbiAgICogU2hvd3MgY29tcHV0ZXIgYnJvd3NlciB3aXRoIGZpbGVzIHRvIGRyb3AgaW50byBkcm9wLWFyZWEgem9uZS5cbiAgICovXG4gIHNob3dQaWNrZXIoJGV2ZW50Pyk6IHZvaWQge1xuICAgIHRoaXMucHJldmVudERlZmF1bHQoJGV2ZW50KTtcbiAgICB0aGlzLnBpY2tlci5uYXRpdmVFbGVtZW50LnZhbHVlID0gJyc7XG4gICAgdGhpcy5waWNrZXIubmF0aXZlRWxlbWVudC5jbGljaygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyaWdnZXJlZCB3aGVuIGZpbGUgaXMgb24gb3ZlciBkcm9wIGFyZWEsIGJ1dCBub3QgZHJvcHBlZC5cbiAgICovXG4gIG9uT3ZlcigpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNPdmVyKSB7XG4gICAgICB0aGlzLmlzT3ZlciA9IHRydWU7XG4gICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkcmFnb3ZlcicsIHRoaXMucHJldmVudERlZmF1bHQpO1xuICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignZHJvcCcsIHRoaXMucHJldmVudERlZmF1bHQpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUcmlnZ2VyZWQgd2hlbiBmaWxlIGlzIGRyb3BwZWQuXG4gICAqL1xuICBvblBpY2soJGV2ZW50KTogdm9pZCB7XG4gICAgdGhpcy5lcnJvcnMgPSBmYWxzZTtcbiAgICB0aGlzLnByZXZlbnREZWZhdWx0KCRldmVudCk7XG4gICAgdGhpcy5vbkZpbGVzU2VsZWN0ZWQoJGV2ZW50LnRhcmdldC5maWxlcyk7XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlIGZpbGUgd2hlbiBpdCBpcyBkcm9wcGVkIGludG8gZHJvcC1hcmVhLlxuICAgKi9cbiAgb25Ecm9wKCRldmVudCk6IHZvaWQge1xuICAgIHRoaXMucHJldmVudERlZmF1bHQoJGV2ZW50KTtcbiAgICB0aGlzLm9uRmlsZXNTZWxlY3RlZCgkZXZlbnQuZGF0YVRyYW5zZmVyLmZpbGVzKTtcbiAgICB0aGlzLnN0b3BEcmFnZ2luZygpO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrcyBjb25kaXRpb24gd2hhdCBzaG91bGQgYmUgZGlzcGxheWVkOiBkcm9wLWFyZWEgem9uZSBvciBsaXN0IG9mIGRyb3BwZWQgZmlsZXMuXG4gICAqL1xuICBzaG91bGRTaG93RmlsZXNMaXN0KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0aGlzLmlzRmlsZXNBbk9iamVjdE9yQXJyYXkoKSAmJlxuICAgICAgIXRoaXMuZm9yY2VIaWRlTGlzdCAmJlxuICAgICAgdGhpcy5hbHdheXNTaG93ICYmXG4gICAgICAhdGhpcy5pc0ZpbGVzQXJyYXlFbXB0eSgpICYmXG4gICAgICAhdGhpcy5oYXNFbXB0eUZpbGVzKCkgJiZcbiAgICAgICF0aGlzLmlzVG9vTWFueUZpbGVzKClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyaWdnZXJlZCB3aGVuIGZpbGUgaXMgcGlja2VkIG92ZXIgd2ViIGFwcGxpY2F0aW9uLlxuICAgKi9cbiAgc3RvcERyYWdnaW5nKCk6IHZvaWQge1xuICAgIGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2RyYWdvdmVyJywgdGhpcy5wcmV2ZW50RGVmYXVsdCk7XG4gICAgZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcignZHJvcCcsIHRoaXMucHJldmVudERlZmF1bHQpO1xuICAgIHRoaXMuaXNPdmVyID0gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogRGVsZXRlIGZpbGVzIGFscmVhZHkgZHJvcHBlZCBmaWxlcy5cbiAgICovXG4gIG9uRGVsZXRlKCkge1xuICAgIGRlbGV0ZSB0aGlzLmZpbGVzO1xuICAgIGRlbGV0ZSB0aGlzLmZpbGVzTmFtZVN0cmluZztcbiAgICB0aGlzLmNsZWFyRXJyb3JzKCk7XG4gICAgdGhpcy5kcm9wcGVkLmVtaXQobnVsbCk7XG4gICAgdGhpcy5vbkNoYW5nZShudWxsKTtcbiAgICB0aGlzLm9uVG91Y2hlZCgpO1xuICAgIHRoaXMuY2QubWFya0ZvckNoZWNrKCk7XG4gIH1cblxuICBvbkNoYW5nZTogKHZhbHVlOiBhbnkpID0+IHZvaWQgPSBfID0+IHVuZGVmaW5lZDtcbiAgb25Ub3VjaGVkOiAoKSA9PiB2b2lkID0gKCkgPT4gdW5kZWZpbmVkO1xuXG4gIHdyaXRlVmFsdWUodmFsdWU6IGFueSkge1xuICAgIHRoaXMuZmlsZXMgPSB2YWx1ZTtcbiAgICBpZiAoIXZhbHVlKSB7XG4gICAgICB0aGlzLm9uRGVsZXRlKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZmlsZXNOYW1lU3RyaW5nID0gdGhpcy5nZXRGaWxlc05hbWVzQXNTdHJpbmcodmFsdWUpO1xuICAgIH1cbiAgICB0aGlzLmNkLmRldGVjdENoYW5nZXMoKTtcbiAgfVxuXG4gIHJlZ2lzdGVyT25DaGFuZ2UoZm46IGFueSkge1xuICAgIHRoaXMub25DaGFuZ2UgPSBmbjtcbiAgfVxuXG4gIHJlZ2lzdGVyT25Ub3VjaGVkKGZuOiBhbnkpIHtcbiAgICB0aGlzLm9uVG91Y2hlZCA9IGZuO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBvbkZpbGVzU2VsZWN0ZWQoZmlsZXM6IEZpbGVMaXN0KSB7XG4gICAgdGhpcy5vblRvdWNoZWQoKTtcbiAgICBjb25zdCBoYXNWYWxpZE5hbWVMZW5ndGggPSB0aGlzLmZpbGVzU2VydmljZS5jaGVja01heExlbmd0aChmaWxlcyk7XG4gICAgaWYgKCFoYXNWYWxpZE5hbWVMZW5ndGgpIHtcbiAgICAgIHRoaXMub25GaWxlSW52YWxpZE5hbWVMZW5ndGgoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBoYXZlVmFsaWRUeXBlcyA9IHRoaXMuZmlsZXNTZXJ2aWNlLmhhdmVWYWxpZEV4dGVuc2lvbnMoZmlsZXMsIHRoaXMuYWNjZXB0KTtcbiAgICBpZiAoIWhhdmVWYWxpZFR5cGVzKSB7XG4gICAgICB0aGlzLm9uRmlsZUludmFsaWRUeXBlKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgbWF4RmlsZVNpemVJbkJ5dGVzID0gdGhpcy5tYXhGaWxlU2l6ZUluTWVnYUJ5dGVzXG4gICAgICA/IHRoaXMuY29udmVydE1lZ2FCeXRlc1RvQnl0ZXModGhpcy5tYXhGaWxlU2l6ZUluTWVnYUJ5dGVzKVxuICAgICAgOiBudWxsO1xuICAgIGNvbnN0IGhhdmVWYWxpZFNpemVzID0gYXdhaXQgdGhpcy5maWxlc1NlcnZpY2UuaGF2ZVZhbGlkU2l6ZXMoZmlsZXMsIG1heEZpbGVTaXplSW5CeXRlcyk7XG4gICAgaWYgKCFoYXZlVmFsaWRTaXplcykge1xuICAgICAgYXdhaXQgdGhpcy5vbkZpbGVJbnZhbGlkU2l6ZSgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuZmlsZXMgPSBmaWxlcztcbiAgICB0aGlzLmZpbGVzTmFtZVN0cmluZyA9IHRoaXMuZ2V0RmlsZXNOYW1lc0FzU3RyaW5nKGZpbGVzKTtcbiAgICB0aGlzLmVycm9ycyA9IGZhbHNlO1xuXG4gICAgaWYgKHRoaXMuaXNUb29NYW55RmlsZXMoKSkge1xuICAgICAgdGhpcy5lcnJvcnMgPSB0cnVlO1xuICAgICAgdGhpcy5mb3JtQ29udHJvbD8uc2V0RXJyb3JzKHsgdG9vTWFueUZpbGVzOiB0cnVlIH0pO1xuICAgICAgdGhpcy5lcnJvck1lc3NhZ2UgPSBnZXR0ZXh0KCdUb28gbWFueSBmaWxlcyBzZWxlY3RlZC4nKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5oYXNFbXB0eUZpbGVzKCkpIHtcbiAgICAgIHRoaXMuZXJyb3JzID0gdHJ1ZTtcbiAgICAgIHRoaXMuZm9ybUNvbnRyb2w/LnNldEVycm9ycyh7IGVtcHR5RmlsZXM6IHRydWUgfSk7XG4gICAgICB0aGlzLmVycm9yTWVzc2FnZSA9IGdldHRleHQoJ0ZpbGUgbXVzdCBub3QgYmUgZW1wdHksIHNlbGVjdCBhbm90aGVyIG9uZS4nKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBkcm9wcGVkRmlsZXM6IERyb3BwZWRGaWxlW10gPSB0aGlzLmNvbXBvc2UoZmlsZXMpO1xuICAgIHRoaXMuZHJvcHBlZC5lbWl0KGRyb3BwZWRGaWxlcyk7XG4gICAgdGhpcy5vbkNoYW5nZShkcm9wcGVkRmlsZXMpO1xuICAgIHRoaXMuY2QubWFya0ZvckNoZWNrKCk7XG4gIH1cblxuICBwcml2YXRlIG9uRmlsZUludmFsaWROYW1lTGVuZ3RoKCkge1xuICAgIHRoaXMuZXJyb3JzID0gdHJ1ZTtcbiAgICB0aGlzLmZvcm1Db250cm9sPy5zZXRFcnJvcnMoeyBpbnZhbGlkTmFtZUxlbmd0aDogdHJ1ZSB9KTtcbiAgICB0aGlzLmVycm9yTWVzc2FnZSA9IGdldHRleHQoJ1RoZSBmaWxlbmFtZSBpcyB0b28gbG9uZy4nKTtcbiAgfVxuXG4gIHByaXZhdGUgb25GaWxlSW52YWxpZFR5cGUoKSB7XG4gICAgdGhpcy5lcnJvcnMgPSB0cnVlO1xuICAgIHRoaXMuZm9ybUNvbnRyb2w/LnNldEVycm9ycyh7IGludmFsaWRUeXBlOiB0cnVlIH0pO1xuICAgIHRoaXMuZXJyb3JNZXNzYWdlID0gZ2V0dGV4dCgnVGhlIHNlbGVjdGVkIGZpbGUgaXMgbm90IHN1cHBvcnRlZC4nKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgb25GaWxlSW52YWxpZFNpemUoKSB7XG4gICAgY29uc3QgbWF4RmlsZVNpemVJbkJ5dGVzID0gdGhpcy5tYXhGaWxlU2l6ZUluTWVnYUJ5dGVzXG4gICAgICA/IHRoaXMuY29udmVydE1lZ2FCeXRlc1RvQnl0ZXModGhpcy5tYXhGaWxlU2l6ZUluTWVnYUJ5dGVzKVxuICAgICAgOiBudWxsO1xuICAgIGNvbnN0IG1zZyA9IGdldHRleHQoJ1RoZSBzZWxlY3RlZCBmaWxlIGlzIHRvbyBsYXJnZS4gVGhlIHNpemUgbGltaXQgaXMge3sgbGltaXQgfX0uJyk7XG4gICAgY29uc3QgbGltaXQgPSB0aGlzLmJ5dGVzLnRyYW5zZm9ybShcbiAgICAgIG1pbihbbWF4RmlsZVNpemVJbkJ5dGVzLCBhd2FpdCB0aGlzLmZpbGVzU2VydmljZS5sb2FkQnl0ZXNTaXplTGltaXQoKV0pXG4gICAgKTtcbiAgICB0aGlzLmVycm9ycyA9IHRydWU7XG4gICAgdGhpcy5mb3JtQ29udHJvbD8uc2V0RXJyb3JzKHsgaW52YWxpZFNpemU6IHRydWUgfSk7XG4gICAgdGhpcy5lcnJvck1lc3NhZ2UgPSB0aGlzLnRyYW5zbGF0ZS5pbnN0YW50KG1zZywgeyBsaW1pdCB9KTtcbiAgfVxuXG4gIHByaXZhdGUgY29udmVydE1lZ2FCeXRlc1RvQnl0ZXMobWF4RmlsZVNpemVJbk1lZ2FCeXRlczogU2l6ZUluTWVnYUJ5dGVzKTogU2l6ZUluQnl0ZXMge1xuICAgIHJldHVybiBtYXhGaWxlU2l6ZUluTWVnYUJ5dGVzICogMV8wNDhfNTc2O1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRGaWxlc05hbWVzQXNTdHJpbmcoZmlsZXM6IEZpbGVMaXN0KTogc3RyaW5nIHtcbiAgICByZXR1cm4gbWFwKGZpbGVzLCAoeyBuYW1lIH0pID0+IG5hbWUpLmpvaW4oJywgJyk7XG4gIH1cblxuICBwcml2YXRlIGlzRmlsZXNBcnJheUVtcHR5KCkge1xuICAgIHJldHVybiBnZXQodGhpcywgJ2ZpbGVzLmxlbmd0aCcsIDApID09PSAwO1xuICB9XG5cbiAgcHJpdmF0ZSBpc1Rvb01hbnlGaWxlcygpIHtcbiAgICByZXR1cm4gZ2V0KHRoaXMsICdmaWxlcy5sZW5ndGgnLCAwKSA+IHRoaXMubWF4QWxsb3dlZEZpbGVzO1xuICB9XG5cbiAgcHJpdmF0ZSBpc0ZpbGVzQW5PYmplY3RPckFycmF5KCkge1xuICAgIHJldHVybiB0eXBlb2YgdGhpcy5maWxlcyA9PT0gJ29iamVjdCc7XG4gIH1cblxuICBwcml2YXRlIGhhc0VtcHR5RmlsZXMoKSB7XG4gICAgbGV0IHJlc3VsdCA9IHRydWU7XG4gICAgaWYgKCF0aGlzLmlzRmlsZXNBcnJheUVtcHR5KCkpIHtcbiAgICAgIHJlc3VsdCA9IHRoaXMuaXNBbnlGaWxlRW1wdHkoKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByaXZhdGUgaXNBbnlGaWxlRW1wdHkoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHNvbWUoQXJyYXkuZnJvbSh0aGlzLmZpbGVzKSwgWydzaXplJywgMF0pO1xuICB9XG5cbiAgcHJpdmF0ZSBjbGVhckVycm9ycygpIHtcbiAgICBkZWxldGUgdGhpcy5lcnJvck1lc3NhZ2U7XG4gICAgdGhpcy5lcnJvcnMgPSBmYWxzZTtcbiAgICB0aGlzLmZvcm1Db250cm9sPy5zZXRFcnJvcnMobnVsbCk7XG4gIH1cblxuICBwcml2YXRlIHByZXZlbnREZWZhdWx0KCRldmVudD8pIHtcbiAgICBpZiAoJGV2ZW50KSB7XG4gICAgICAkZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGNvbXBvc2UoZmlsZXM6IEZpbGVMaXN0KTogRHJvcHBlZEZpbGVbXSB7