ngx-ui-hero
Version:
Simple, fast and reliable utilities for Angular.
348 lines • 54.7 kB
JavaScript
import { FileUploader } from 'ng2-file-upload';
import { zip } from 'rxjs';
import { retry } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
import { Component, EventEmitter, Inject, Input, Optional, Output } from '@angular/core';
import { INPUT_FORMS_CONFIG } from '../../input-forms-config.constants';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common/http";
import * as i2 from "@angular/common";
import * as i3 from "@angular/forms";
import * as i4 from "ng2-file-upload";
import * as i5 from "ngx-bootstrap/progressbar";
import * as i6 from "ngx-bootstrap/tooltip";
let identifier = 0;
export class InputUploadComponent {
constructor(config, http) {
this.http = http;
this.placeholder = 'Select a file to upload...';
this.dropZonePlaceholder = 'Drag & drop a file to import.';
this.disabled = false;
this.autoUpload = true;
this.showDropZone = false;
this.showQueue = false;
this.chunk = false;
this.chunkSize = 1048576;
this.chunkRetries = 3;
this.chunkRequestsCountInParallel = 50;
this.maxFileSize = 0;
this.withCredentials = false;
this.selectButtonIcon = 'fa fa-folder';
this.selectButtonLabel = 'Select';
this.removeButtonIcon = 'fa fa-trash';
this.removeButtonLabel = 'Remove';
this.removeButtonAllowed = true;
this.fileTypeErrorMessage = 'The file type [{extension}] is not allowed.';
this.fileSizeErrorMessage = 'This file exceeds the max file size allowed of {maxFileSize}MB.';
this.maxFileSizeLabel = 'Max file size:';
this.allowedExtensionsLabel = 'Allowed extensions:';
this.onFileAdded = new EventEmitter();
this.onUploadComplete = new EventEmitter();
this.onChunkFileUpload = new EventEmitter();
this.onError = new EventEmitter();
this.onClear = new EventEmitter();
this.identifier = `input-upload-${identifier++}`;
this.onParallelChunkCompletes = new EventEmitter();
this.selectedFileName = '';
this.hasDropZoneOver = false;
this.chunkProgress = 0;
if (config && config.upload) {
Object.assign(this, config.upload);
}
}
ngOnInit() {
this.uploader = new FileUploader({
url: this.url,
autoUpload: false,
maxFileSize: this.maxFileSize * 1000000,
});
this.handleUploaderEvents();
}
Clear() {
this.chunkProgress = 0;
this.selectedFileModel = null;
this.selectedFileName = '';
this.errorMessage = null;
this.chunks = null;
this.uploader.clearQueue();
this.uploader.cancelAll();
this.onClear.emit();
}
StartUploadManually() {
let promise = new Promise((resolve, reject) => {
if (!this.selectedFileBlob) {
reject();
return;
}
this.onUploadComplete.subscribe(result => resolve(result), error => reject(error));
this.onError.subscribe(result => reject(result));
if (this.chunk && this.chunks && this.chunks.length > 0) {
this.startChunkUpload();
}
else {
this.startSingleUpload();
}
});
return promise;
}
SetSelectedFileName(fileName) {
this.selectedFileName = fileName;
}
OnFileOver(e) {
this.hasDropZoneOver = e;
}
OnFileChange(event) {
if (event.target.files[0]) {
this.addSelectedFileForManualUploading(event.target.files[0]);
}
}
OnFileDrop(event) {
if (event[0]) {
this.addSelectedFileForManualUploading(event[0]);
}
}
HasFile() {
return this.selectedFileBlob != null && this.selectedFileBlob != undefined;
}
ResetState() {
if (this.uploader.queue && this.uploader.queue.length > 0) {
for (let i = 0; i < this.uploader.queue.length; i++) {
this.uploader.queue[i].isError = false;
this.uploader.queue[i].isUploaded = false;
this.uploader.queue[i].isSuccess = false;
}
}
}
startSingleUpload() {
this.uploader.uploadAll();
}
startChunkUpload() {
this.chunkProgress = 0;
if (this.chunkRequestsCountInParallel > 0 && this.chunks.length > this.chunkRequestsCountInParallel) {
this.sendGroupedPromisesSequentially();
this.onParallelChunkCompletes.subscribe(() => {
this.chunkProgress = 100;
this.onUploadComplete.emit({ chunkId: this.chunks[0].id });
});
}
else {
let chunksPromises = [];
chunksPromises.push(this.sendChunks(this.chunks));
Promise.all(chunksPromises)
.then(() => {
this.chunkProgress = 100;
this.onUploadComplete.emit({ chunkId: this.chunks[0].id });
})
.catch(error => {
this.onError.emit(error);
});
}
}
sendChunks(chunks) {
let promise = new Promise((resolve, reject) => {
let requests = [];
for (let i = 0; i < chunks.length; i++) {
let formData = new FormData();
formData.append("file", chunks[i].blob, chunks[i].name);
requests.push(this.http.post(this.url, formData, { withCredentials: this.withCredentials })
.pipe(retry(this.chunkRetries)));
}
zip(...requests).subscribe(() => {
this.chunkProgress += (100 / this.chunks.length) * chunks.length;
resolve();
}, error => reject(error));
});
return promise;
}
sendGroupedPromisesSequentially(index = 0) {
let start = index * this.chunkRequestsCountInParallel;
let end = ((index + 1) * this.chunkRequestsCountInParallel) - 1;
let lastIndex = end + 1 >= this.chunks.length;
if (end > this.chunks.length - 1) {
end = this.chunks.length - 1;
}
this.sendChunks(this.chunks.slice(start, end + 1))
.then(() => {
if (!lastIndex) {
this.sendGroupedPromisesSequentially(index + 1);
}
else {
this.onParallelChunkCompletes.emit();
}
})
.catch(error => {
this.onError.emit(error);
this.Clear();
});
}
splitSelectedFileInChunks() {
this.chunks = [];
let file = this.selectedFileBlob;
let fileSize = file.size;
let start = 0;
let end = this.chunkSize;
let chunksCount = 0;
let chunkGuid = Math.random()
.toString()
.replace("0.", "");
if (fileSize % this.chunkSize == 0) {
chunksCount = fileSize / this.chunkSize;
}
else {
chunksCount = Math.floor(fileSize / this.chunkSize) + 1;
}
for (let i = 0; i < chunksCount; i++) {
this.chunks.push({
id: chunkGuid,
name: `${chunkGuid}_${i}`,
blob: file.slice(start, end)
});
start = end;
end = start + this.chunkSize;
}
}
handleUploaderEvents() {
this.uploader.onBeforeUploadItem = (fileItem) => {
fileItem.withCredentials = this.withCredentials;
};
this.uploader.onSuccessItem = (item, response, status, headers) => {
this.chunkProgress = 100;
this.onUploadComplete.emit({ item, response });
this.ResetState();
};
this.uploader.onErrorItem = (item, response, status, headers) => {
this.onError.emit({ item, response, status });
this.ResetState();
};
this.uploader.onWhenAddingFileFailed = (item, filter, options) => {
this.onError.emit({ item, filter, options });
};
}
addSelectedFileForManualUploading(file) {
this.selectedFileBlob = null;
if (!file) {
return;
}
if (this.validate(file)) {
this.selectedFileBlob = file;
this.SetSelectedFileName(file.name);
if (this.uploader.queue.length > 1) {
this.uploader.removeFromQueue(this.uploader.queue[0]);
}
if (this.chunk) {
this.splitSelectedFileInChunks();
}
this.onFileAdded.emit(file);
if (this.autoUpload) {
this.StartUploadManually();
}
}
}
validate(item) {
this.selectedFileName = null;
this.errorMessage = null;
if (!this.validateFileType(item) || !this.validateFileSize(item)) {
this.selectedFileModel = null;
this.uploader.clearQueue();
return false;
}
return true;
}
validateFileType(file) {
if (!this.allowedExtensions || this.allowedExtensions.length == 0) {
return true;
}
let extensionArray = file.name.split('.');
let extension = extensionArray[extensionArray.length - 1].toLowerCase();
let result = this.allowedExtensions.find(x => x == extension);
if (result == undefined || result == null) {
this.errorMessage = this.fileTypeErrorMessage.replace('{extension}', extension);
return false;
}
return true;
}
validateFileSize(file) {
if (this.maxFileSize == 0) {
return true;
}
if (file.size > this.maxFileSize * 1048576) {
this.errorMessage = this.fileSizeErrorMessage.replace('{maxFileSize}', `${this.maxFileSize}`);
return false;
}
return true;
}
}
InputUploadComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: InputUploadComponent, deps: [{ token: INPUT_FORMS_CONFIG, optional: true }, { token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
InputUploadComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: InputUploadComponent, selector: "input-upload", inputs: { url: "url", label: "label", help: "help", description: "description", placeholder: "placeholder", dropZonePlaceholder: "dropZonePlaceholder", disabled: "disabled", autoUpload: "autoUpload", showDropZone: "showDropZone", showQueue: "showQueue", chunk: "chunk", chunkSize: "chunkSize", chunkRetries: "chunkRetries", chunkRequestsCountInParallel: "chunkRequestsCountInParallel", maxFileSize: "maxFileSize", withCredentials: "withCredentials", selectButtonIcon: "selectButtonIcon", selectButtonLabel: "selectButtonLabel", removeButtonIcon: "removeButtonIcon", removeButtonLabel: "removeButtonLabel", removeButtonAllowed: "removeButtonAllowed", allowedExtensions: "allowedExtensions", fileTypeErrorMessage: "fileTypeErrorMessage", fileSizeErrorMessage: "fileSizeErrorMessage", maxFileSizeLabel: "maxFileSizeLabel", allowedExtensionsLabel: "allowedExtensionsLabel" }, outputs: { onFileAdded: "onFileAdded", onUploadComplete: "onUploadComplete", onChunkFileUpload: "onChunkFileUpload", onError: "onError", onClear: "onClear" }, ngImport: i0, template: "<label *ngIf=\"label\">\n {{label}}\n <i class=\"fa fa-question-circle ml-2\" tooltip=\"{{help}}\" container=\"body\" *ngIf=\"help\"></i>\n</label>\n\n<input type=\"file\" class=\"d-none\" [(ngModel)]=\"selectedFileModel\" (change)=\"OnFileChange($event)\" #fileInput\n ng2FileSelect [uploader]=\"uploader\" [disabled]=\"disabled\" id=\"{{identifier}}-0\" />\n\n<div class=\"input-group\">\n <input type=\"text\" class=\"form-control\" [value]=\"selectedFileName\" placeholder=\"{{placeholder}}\" id=\"{{identifier}}-1\"\n (click)=\"fileInput.click()\" readonly />\n <div class=\"input-group-append\">\n <button class=\"btn btn-primary\" type=\"button\" (click)=\"fileInput.click()\" [disabled]=\"disabled\"><i\n *ngIf=\"selectButtonIcon\" class=\"{{selectButtonIcon}}\"></i> {{selectButtonLabel}}</button>\n <button class=\"btn btn-outline-danger\" type=\"button\" *ngIf=\"selectedFileName && removeButtonAllowed\"\n (click)=\"Clear()\" [disabled]=\"disabled\"><i *ngIf=\"removeButtonIcon\" class=\"{{removeButtonIcon}}\"></i>\n {{removeButtonLabel}}</button>\n </div>\n</div>\n\n<p class=\"mb-0\" *ngIf=\"description\">\n <small class=\"text-muted\">{{description}}</small>\n</p>\n\n<small class=\"text-muted\" *ngIf=\"maxFileSize > 0\">\n {{maxFileSizeLabel}} <b>{{maxFileSize}}MB</b>.\n</small>\n<small class=\"text-muted\" *ngIf=\"allowedExtensions?.length > 0\">\n {{allowedExtensionsLabel}}\n <span *ngFor=\"let item of allowedExtensions; let first = first;\">\n <span *ngIf=\"!first\">, </span>\n <b>{{item}}</b>\n </span>\n <span>.</span>\n</small>\n\n<div *ngIf=\"errorMessage\">\n <small class=\"text-danger\">{{errorMessage}}</small>\n</div>\n\n<div class=\"chunks-queue\" *ngIf=\"chunk && chunks?.length > 0 && showQueue\">\n <div class=\"chunk\">\n <div class=\"d-flex\">\n <div class=\"icon mr-2\">\n <i class=\"fa fa-file fa-2x\"></i>\n </div>\n <div class=\"data flex-grow-1\">\n <div class=\"d-flex align-items-center\">\n <small class=\"file-name\"><b>{{selectedFileName}} ({{chunks.length}})</b></small>\n <small class=\"file-size ml-auto\">{{selectedFileBlob?.size / 1000 | number:'1.2-2'}}KB</small>\n </div>\n <progressbar [value]=\"chunkProgress\">\n {{chunkProgress}}%\n </progressbar>\n </div>\n </div>\n </div>\n</div>\n\n<div ng2FileDrop *ngIf=\"showDropZone\" [ngClass]=\"{'mouse-over': hasDropZoneOver}\" (fileOver)=\"OnFileOver($event)\"\n (onFileDrop)=\"OnFileDrop($event)\" [uploader]=\"uploader\" class=\"dropzone\">\n <i class=\"fa fa-cloud-download fa-4x\"></i><br />\n <span>{{dropZonePlaceholder}}</span>\n</div>\n", styles: [".dropzone{background:white;border-radius:5px;border:2px dashed #CCC;margin:10px 0;padding:40px;text-align:center}.dropzone i{color:#ccc}.dropzone span{color:#999}.dropzone,.dropzone>*{transition:color .35s ease-in-out,background-color .35s ease-in-out}.dropzone.mouse-over{background-color:#f0f0f0;border:2px solid #CCC;opacity:.5}.dropzone.mouse-over i,.dropzone.mouse-over span{color:#292929}.chunks-queue{border:1px solid #CCC;border-radius:6px;margin:10px 0}.chunks-queue .chunk{padding:10px;border-bottom:1px solid #CCC}.chunks-queue .chunk .icon i{font-size:2.2em}.chunks-queue .chunk .icon i,.chunks-queue .chunk .data .file-name{color:#00559a}.chunks-queue .chunk:last-child{border-bottom:0}.chunks-queue.scrollable{max-height:300px;overflow-y:scroll}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i4.FileDropDirective, selector: "[ng2FileDrop]", inputs: ["uploader"], outputs: ["fileOver", "onFileDrop"] }, { kind: "directive", type: i4.FileSelectDirective, selector: "[ng2FileSelect]", inputs: ["uploader"], outputs: ["onFileSelected"] }, { kind: "component", type: i5.ProgressbarComponent, selector: "progressbar", inputs: ["animate", "striped", "value", "max", "type"] }, { kind: "directive", type: i6.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["containerClass", "tooltipAnimation", "tooltipFadeDuration", "isOpen", "tooltipHtml", "tooltip", "tooltipPlacement", "placement", "tooltipIsOpen", "tooltipEnable", "isDisabled", "tooltipAppendToBody", "container", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "delay", "tooltipTrigger", "triggers", "adaptivePosition"], outputs: ["tooltipChange", "tooltipStateChanged", "onShown", "onHidden"], exportAs: ["bs-tooltip"] }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: InputUploadComponent, decorators: [{
type: Component,
args: [{ selector: 'input-upload', template: "<label *ngIf=\"label\">\n {{label}}\n <i class=\"fa fa-question-circle ml-2\" tooltip=\"{{help}}\" container=\"body\" *ngIf=\"help\"></i>\n</label>\n\n<input type=\"file\" class=\"d-none\" [(ngModel)]=\"selectedFileModel\" (change)=\"OnFileChange($event)\" #fileInput\n ng2FileSelect [uploader]=\"uploader\" [disabled]=\"disabled\" id=\"{{identifier}}-0\" />\n\n<div class=\"input-group\">\n <input type=\"text\" class=\"form-control\" [value]=\"selectedFileName\" placeholder=\"{{placeholder}}\" id=\"{{identifier}}-1\"\n (click)=\"fileInput.click()\" readonly />\n <div class=\"input-group-append\">\n <button class=\"btn btn-primary\" type=\"button\" (click)=\"fileInput.click()\" [disabled]=\"disabled\"><i\n *ngIf=\"selectButtonIcon\" class=\"{{selectButtonIcon}}\"></i> {{selectButtonLabel}}</button>\n <button class=\"btn btn-outline-danger\" type=\"button\" *ngIf=\"selectedFileName && removeButtonAllowed\"\n (click)=\"Clear()\" [disabled]=\"disabled\"><i *ngIf=\"removeButtonIcon\" class=\"{{removeButtonIcon}}\"></i>\n {{removeButtonLabel}}</button>\n </div>\n</div>\n\n<p class=\"mb-0\" *ngIf=\"description\">\n <small class=\"text-muted\">{{description}}</small>\n</p>\n\n<small class=\"text-muted\" *ngIf=\"maxFileSize > 0\">\n {{maxFileSizeLabel}} <b>{{maxFileSize}}MB</b>.\n</small>\n<small class=\"text-muted\" *ngIf=\"allowedExtensions?.length > 0\">\n {{allowedExtensionsLabel}}\n <span *ngFor=\"let item of allowedExtensions; let first = first;\">\n <span *ngIf=\"!first\">, </span>\n <b>{{item}}</b>\n </span>\n <span>.</span>\n</small>\n\n<div *ngIf=\"errorMessage\">\n <small class=\"text-danger\">{{errorMessage}}</small>\n</div>\n\n<div class=\"chunks-queue\" *ngIf=\"chunk && chunks?.length > 0 && showQueue\">\n <div class=\"chunk\">\n <div class=\"d-flex\">\n <div class=\"icon mr-2\">\n <i class=\"fa fa-file fa-2x\"></i>\n </div>\n <div class=\"data flex-grow-1\">\n <div class=\"d-flex align-items-center\">\n <small class=\"file-name\"><b>{{selectedFileName}} ({{chunks.length}})</b></small>\n <small class=\"file-size ml-auto\">{{selectedFileBlob?.size / 1000 | number:'1.2-2'}}KB</small>\n </div>\n <progressbar [value]=\"chunkProgress\">\n {{chunkProgress}}%\n </progressbar>\n </div>\n </div>\n </div>\n</div>\n\n<div ng2FileDrop *ngIf=\"showDropZone\" [ngClass]=\"{'mouse-over': hasDropZoneOver}\" (fileOver)=\"OnFileOver($event)\"\n (onFileDrop)=\"OnFileDrop($event)\" [uploader]=\"uploader\" class=\"dropzone\">\n <i class=\"fa fa-cloud-download fa-4x\"></i><br />\n <span>{{dropZonePlaceholder}}</span>\n</div>\n", styles: [".dropzone{background:white;border-radius:5px;border:2px dashed #CCC;margin:10px 0;padding:40px;text-align:center}.dropzone i{color:#ccc}.dropzone span{color:#999}.dropzone,.dropzone>*{transition:color .35s ease-in-out,background-color .35s ease-in-out}.dropzone.mouse-over{background-color:#f0f0f0;border:2px solid #CCC;opacity:.5}.dropzone.mouse-over i,.dropzone.mouse-over span{color:#292929}.chunks-queue{border:1px solid #CCC;border-radius:6px;margin:10px 0}.chunks-queue .chunk{padding:10px;border-bottom:1px solid #CCC}.chunks-queue .chunk .icon i{font-size:2.2em}.chunks-queue .chunk .icon i,.chunks-queue .chunk .data .file-name{color:#00559a}.chunks-queue .chunk:last-child{border-bottom:0}.chunks-queue.scrollable{max-height:300px;overflow-y:scroll}\n"] }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [INPUT_FORMS_CONFIG]
}, {
type: Optional
}] }, { type: i1.HttpClient }]; }, propDecorators: { url: [{
type: Input
}], label: [{
type: Input
}], help: [{
type: Input
}], description: [{
type: Input
}], placeholder: [{
type: Input
}], dropZonePlaceholder: [{
type: Input
}], disabled: [{
type: Input
}], autoUpload: [{
type: Input
}], showDropZone: [{
type: Input
}], showQueue: [{
type: Input
}], chunk: [{
type: Input
}], chunkSize: [{
type: Input
}], chunkRetries: [{
type: Input
}], chunkRequestsCountInParallel: [{
type: Input
}], maxFileSize: [{
type: Input
}], withCredentials: [{
type: Input
}], selectButtonIcon: [{
type: Input
}], selectButtonLabel: [{
type: Input
}], removeButtonIcon: [{
type: Input
}], removeButtonLabel: [{
type: Input
}], removeButtonAllowed: [{
type: Input
}], allowedExtensions: [{
type: Input
}], fileTypeErrorMessage: [{
type: Input
}], fileSizeErrorMessage: [{
type: Input
}], maxFileSizeLabel: [{
type: Input
}], allowedExtensionsLabel: [{
type: Input
}], onFileAdded: [{
type: Output
}], onUploadComplete: [{
type: Output
}], onChunkFileUpload: [{
type: Output
}], onError: [{
type: Output
}], onClear: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQtdXBsb2FkLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC11aS1oZXJvL3NyYy9saWIvaW5wdXQtZm9ybXMvY29tcG9uZW50cy9pbnB1dC11cGxvYWQvaW5wdXQtdXBsb2FkLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC11aS1oZXJvL3NyYy9saWIvaW5wdXQtZm9ybXMvY29tcG9uZW50cy9pbnB1dC11cGxvYWQvaW5wdXQtdXBsb2FkLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBWSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6RCxPQUFPLEVBQWMsR0FBRyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3ZDLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV2QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBVSxRQUFRLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBR2pHLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDOzs7Ozs7OztBQUV4RSxJQUFJLFVBQVUsR0FBRyxDQUFDLENBQUM7QUFPbkIsTUFBTSxPQUFPLG9CQUFvQjtJQTZDL0IsWUFDMEMsTUFBd0IsRUFDeEQsSUFBZ0I7UUFBaEIsU0FBSSxHQUFKLElBQUksQ0FBWTtRQTFDakIsZ0JBQVcsR0FBWSw0QkFBNEIsQ0FBQztRQUNwRCx3QkFBbUIsR0FBWSwrQkFBK0IsQ0FBQztRQUMvRCxhQUFRLEdBQWEsS0FBSyxDQUFDO1FBQzNCLGVBQVUsR0FBYSxJQUFJLENBQUM7UUFDNUIsaUJBQVksR0FBYSxLQUFLLENBQUM7UUFDL0IsY0FBUyxHQUFhLEtBQUssQ0FBQztRQUM1QixVQUFLLEdBQWEsS0FBSyxDQUFDO1FBQ3hCLGNBQVMsR0FBWSxPQUFPLENBQUM7UUFDN0IsaUJBQVksR0FBWSxDQUFDLENBQUM7UUFDMUIsaUNBQTRCLEdBQVksRUFBRSxDQUFDO1FBQzNDLGdCQUFXLEdBQVksQ0FBQyxDQUFDO1FBQ3pCLG9CQUFlLEdBQWEsS0FBSyxDQUFDO1FBQ2xDLHFCQUFnQixHQUFZLGNBQWMsQ0FBQztRQUMzQyxzQkFBaUIsR0FBWSxRQUFRLENBQUM7UUFDdEMscUJBQWdCLEdBQVksYUFBYSxDQUFDO1FBQzFDLHNCQUFpQixHQUFZLFFBQVEsQ0FBQztRQUN0Qyx3QkFBbUIsR0FBYSxJQUFJLENBQUM7UUFFckMseUJBQW9CLEdBQVksNkNBQTZDLENBQUM7UUFDOUUseUJBQW9CLEdBQVksaUVBQWlFLENBQUM7UUFDbEcscUJBQWdCLEdBQVksZ0JBQWdCLENBQUM7UUFDN0MsMkJBQXNCLEdBQVkscUJBQXFCLENBQUM7UUFDdkQsZ0JBQVcsR0FBRyxJQUFJLFlBQVksRUFBTyxDQUFDO1FBQ3RDLHFCQUFnQixHQUFHLElBQUksWUFBWSxFQUFPLENBQUM7UUFDM0Msc0JBQWlCLEdBQUcsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQUM1QyxZQUFPLEdBQUcsSUFBSSxZQUFZLEVBQU8sQ0FBQztRQUNsQyxZQUFPLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUVoQyxlQUFVLEdBQUcsZ0JBQWdCLFVBQVUsRUFBRSxFQUFFLENBQUM7UUFDM0MsNkJBQXdCLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUl0RCxxQkFBZ0IsR0FBVyxFQUFFLENBQUM7UUFHOUIsb0JBQWUsR0FBWSxLQUFLLENBQUM7UUFFakMsa0JBQWEsR0FBVyxDQUFDLENBQUM7UUFNeEIsSUFBSSxNQUFNLElBQUksTUFBTSxDQUFDLE1BQU0sRUFBRTtZQUMzQixNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7U0FDcEM7SUFDSCxDQUFDO0lBRUQsUUFBUTtRQUNOLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxZQUFZLENBQUM7WUFDL0IsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHO1lBQ2IsVUFBVSxFQUFFLEtBQUs7WUFDakIsV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFXLEdBQUcsT0FBTztTQUN4QyxDQUFDLENBQUM7UUFFSCxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztJQUM5QixDQUFDO0lBRUQsS0FBSztRQUNILElBQUksQ0FBQyxhQUFhLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxJQUFJLENBQUM7UUFDOUIsSUFBSSxDQUFDLGdCQUFnQixHQUFHLEVBQUUsQ0FBQztRQUMzQixJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQztRQUN6QixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNuQixJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQzNCLElBQUksQ0FBQyxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDMUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN0QixDQUFDO0lBRUQsbUJBQW1CO1FBQ2pCLElBQUksT0FBTyxHQUFHLElBQUksT0FBTyxDQUFNLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQ2pELElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUU7Z0JBQzFCLE1BQU0sRUFBRSxDQUFDO2dCQUNULE9BQU87YUFDUjtZQUVELElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsS0FBSyxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztZQUNuRixJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBRWpELElBQUksSUFBSSxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtnQkFDdkQsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7YUFDekI7aUJBQU07Z0JBQ0wsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7YUFDMUI7UUFDSCxDQUFDLENBQUMsQ0FBQztRQUVILE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFRCxtQkFBbUIsQ0FBQyxRQUFnQjtRQUNsQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsUUFBUSxDQUFDO0lBQ25DLENBQUM7SUFFRCxVQUFVLENBQUMsQ0FBTTtRQUNmLElBQUksQ0FBQyxlQUFlLEdBQUcsQ0FBQyxDQUFDO0lBQzNCLENBQUM7SUFDRCxZQUFZLENBQUMsS0FBVTtRQUNyQixJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQ3pCLElBQUksQ0FBQyxpQ0FBaUMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQy9EO0lBQ0gsQ0FBQztJQUNELFVBQVUsQ0FBQyxLQUFVO1FBQ25CLElBQUksS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQ1osSUFBSSxDQUFDLGlDQUFpQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ2xEO0lBQ0gsQ0FBQztJQUVELE9BQU87UUFDTCxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsSUFBSSxJQUFJLElBQUksSUFBSSxDQUFDLGdCQUFnQixJQUFJLFNBQVMsQ0FBQztJQUM3RSxDQUFDO0lBRUQsVUFBVTtRQUNSLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtZQUN6RCxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO2dCQUNuRCxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDO2dCQUN2QyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLEdBQUcsS0FBSyxDQUFDO2dCQUMxQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDO2FBQzFDO1NBQ0Y7SUFDSCxDQUFDO0lBRU8saUJBQWlCO1FBQ3ZCLElBQUksQ0FBQyxRQUFRLENBQUMsU0FBUyxFQUFFLENBQUM7SUFDNUIsQ0FBQztJQUNPLGdCQUFnQjtRQUN0QixJQUFJLENBQUMsYUFBYSxHQUFHLENBQUMsQ0FBQztRQUV2QixJQUFJLElBQUksQ0FBQyw0QkFBNEIsR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLDRCQUE0QixFQUFFO1lBQ25HLElBQUksQ0FBQywrQkFBK0IsRUFBRSxDQUFDO1lBRXZDLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFO2dCQUMzQyxJQUFJLENBQUMsYUFBYSxHQUFHLEdBQUcsQ0FBQztnQkFDekIsSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDN0QsQ0FBQyxDQUFDLENBQUM7U0FDSjthQUFNO1lBQ0wsSUFBSSxjQUFjLEdBQXlCLEVBQUUsQ0FBQztZQUM5QyxjQUFjLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7WUFFbEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUM7aUJBQ3hCLElBQUksQ0FBQyxHQUFHLEVBQUU7Z0JBQ1QsSUFBSSxDQUFDLGFBQWEsR0FBRyxHQUFHLENBQUM7Z0JBQ3pCLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQzdELENBQUMsQ0FBQztpQkFDRCxLQUFLLENBQUMsS0FBSyxDQUFDLEVBQUU7Z0JBQ2IsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDM0IsQ0FBQyxDQUFDLENBQUM7U0FDTjtJQUNILENBQUM7SUFDTyxVQUFVLENBQUMsTUFBYTtRQUM5QixJQUFJLE9BQU8sR0FBRyxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUNsRCxJQUFJLFFBQVEsR0FBMkIsRUFBRSxDQUFDO1lBRTFDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO2dCQUN0QyxJQUFJLFFBQVEsR0FBRyxJQUFJLFFBQVEsRUFBRSxDQUFDO2dCQUM5QixRQUFRLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFFeEQsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLFFBQVEsRUFBRSxFQUFFLGVBQWUsRUFBRSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7cUJBQ3hGLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUNwQztZQUVELEdBQUcsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxDQUFDLFNBQVMsQ0FDeEIsR0FBRyxFQUFFO2dCQUNILElBQUksQ0FBQyxhQUFhLElBQUksQ0FBQyxHQUFHLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO2dCQUNqRSxPQUFPLEVBQUUsQ0FBQztZQUNaLENBQUMsRUFDRCxLQUFLLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FDdkIsQ0FBQztRQUNKLENBQUMsQ0FBQyxDQUFDO1FBRUgsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztJQUNPLCtCQUErQixDQUFDLFFBQWdCLENBQUM7UUFDdkQsSUFBSSxLQUFLLEdBQUcsS0FBSyxHQUFHLElBQUksQ0FBQyw0QkFBNEIsQ0FBQztRQUN0RCxJQUFJLEdBQUcsR0FBRyxDQUFDLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyw0QkFBNEIsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNoRSxJQUFJLFNBQVMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDO1FBRTlDLElBQUksR0FBRyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtZQUNoQyxHQUFHLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO1NBQzlCO1FBRUQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDO2FBQy9DLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDVCxJQUFJLENBQUMsU0FBUyxFQUFFO2dCQUNkLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUM7YUFDakQ7aUJBQU07Z0JBQ0wsSUFBSSxDQUFDLHdCQUF3QixDQUFDLElBQUksRUFBRSxDQUFDO2FBQ3RDO1FBQ0gsQ0FBQyxDQUFDO2FBQ0QsS0FBSyxDQUFDLEtBQUssQ0FBQyxFQUFFO1lBQ2IsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDekIsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ2YsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBQ08seUJBQXlCO1FBQy9CLElBQUksQ0FBQyxNQUFNLEdBQUcsRUFBRSxDQUFDO1FBQ2pCLElBQUksSUFBSSxHQUFTLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQztRQUN2QyxJQUFJLFFBQVEsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQ3pCLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQztRQUNkLElBQUksR0FBRyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7UUFDekIsSUFBSSxXQUFXLEdBQUcsQ0FBQyxDQUFDO1FBQ3BCLElBQUksU0FBUyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUU7YUFDMUIsUUFBUSxFQUFFO2FBQ1YsT0FBTyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztRQUVyQixJQUFJLFFBQVEsR0FBRyxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsRUFBRTtZQUNsQyxXQUFXLEdBQUcsUUFBUSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7U0FDekM7YUFBTTtZQUNMLFdBQVcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1NBQ3pEO1FBRUQsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFdBQVcsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUNwQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztnQkFDZixFQUFFLEVBQUUsU0FBUztnQkFDYixJQUFJLEVBQUUsR0FBRyxTQUFTLElBQUksQ0FBQyxFQUFFO2dCQUN6QixJQUFJLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDO2FBQzdCLENBQUMsQ0FBQztZQUVILEtBQUssR0FBRyxHQUFHLENBQUM7WUFDWixHQUFHLEdBQUcsS0FBSyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7U0FDOUI7SUFDSCxDQUFDO0lBQ08sb0JBQW9CO1FBQzFCLElBQUksQ0FBQyxRQUFRLENBQUMsa0JBQWtCLEdBQUcsQ0FBQyxRQUFrQixFQUFFLEVBQUU7WUFDeEQsUUFBUSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDO1FBQ2xELENBQUMsQ0FBQztRQUNGLElBQUksQ0FBQyxRQUFRLENBQUMsYUFBYSxHQUFHLENBQUMsSUFBUyxFQUFFLFFBQWEsRUFBRSxNQUFXLEVBQUUsT0FBWSxFQUFFLEVBQUU7WUFDcEYsSUFBSSxDQUFDLGFBQWEsR0FBRyxHQUFHLENBQUM7WUFDekIsSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO1lBQy9DLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNwQixDQUFDLENBQUM7UUFDRixJQUFJLENBQUMsUUFBUSxDQUFDLFdBQVcsR0FBRyxDQUFDLElBQVMsRUFBRSxRQUFhLEVBQUUsTUFBVyxFQUFFLE9BQVksRUFBRSxFQUFFO1lBQ2xGLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQzlDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNwQixDQUFDLENBQUM7UUFDRixJQUFJLENBQUMsUUFBUSxDQUFDLHNCQUFzQixHQUFHLENBQUMsSUFBUyxFQUFFLE1BQVcsRUFBRSxPQUFZLEVBQUUsRUFBRTtZQUM5RSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUMvQyxDQUFDLENBQUM7SUFDSixDQUFDO0lBQ08saUNBQWlDLENBQUMsSUFBUztRQUNqRCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDO1FBRTdCLElBQUksQ0FBQyxJQUFJLEVBQUU7WUFDVCxPQUFPO1NBQ1I7UUFFRCxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDdkIsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQztZQUM3QixJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBRXBDLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtnQkFDbEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUN2RDtZQUVELElBQUksSUFBSSxDQUFDLEtBQUssRUFBRTtnQkFDZCxJQUFJLENBQUMseUJBQXlCLEVBQUUsQ0FBQzthQUNsQztZQUVELElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBRTVCLElBQUksSUFBSSxDQUFDLFVBQVUsRUFBRTtnQkFDbkIsSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUM7YUFDNUI7U0FDRjtJQUNILENBQUM7SUFDTyxRQUFRLENBQUMsSUFBUztRQUN4QixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDO1FBQzdCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDO1FBRXpCLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDaEUsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQztZQUM5QixJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQzNCLE9BQU8sS0FBSyxDQUFDO1NBQ2Q7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFDTyxnQkFBZ0IsQ0FBQyxJQUFTO1FBQ2hDLElBQUksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLElBQUksSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sSUFBSSxDQUFDLEVBQUU7WUFDakUsT0FBTyxJQUFJLENBQUM7U0FDYjtRQUVELElBQUksY0FBYyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzFDLElBQUksU0FBUyxHQUFXLGNBQWMsQ0FBQyxjQUFjLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2hGLElBQUksTUFBTSxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksU0FBUyxDQUFDLENBQUM7UUFFOUQsSUFBSSxNQUFNLElBQUksU0FBUyxJQUFJLE1BQU0sSUFBSSxJQUFJLEVBQUU7WUFDekMsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsb0JBQW9CLENBQUMsT0FBTyxDQUFDLGFBQWEsRUFBRSxTQUFTLENBQUMsQ0FBQztZQUNoRixPQUFPLEtBQUssQ0FBQztTQUNkO1FBRUQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQ08sZ0JBQWdCLENBQUMsSUFBUztRQUNoQyxJQUFJLElBQUksQ0FBQyxXQUFXLElBQUksQ0FBQyxFQUFFO1lBQ3pCLE9BQU8sSUFBSSxDQUFDO1NBQ2I7UUFFRCxJQUFJLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsR0FBRyxPQUFPLEVBQUU7WUFDMUMsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsb0JBQW9CLENBQUMsT0FBTyxDQUFDLGVBQWUsRUFBRSxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO1lBQzlGLE9BQU8sS0FBSyxDQUFDO1NBQ2Q7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7O2tIQXJUVSxvQkFBb0Isa0JBOENyQixrQkFBa0I7c0dBOUNqQixvQkFBb0IsMmpDQ2pCakMsb29GQWdFQTs0RkQvQ2Esb0JBQW9CO2tCQUxoQyxTQUFTOytCQUNFLGNBQWM7OzBCQWtEckIsTUFBTTsyQkFBQyxrQkFBa0I7OzBCQUFHLFFBQVE7cUVBN0M5QixHQUFHO3NCQUFYLEtBQUs7Z0JBQ0csS0FBSztzQkFBYixLQUFLO2dCQUNHLElBQUk7c0JBQVosS0FBSztnQkFDRyxXQUFXO3NCQUFuQixLQUFLO2dCQUNHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBQ0csbUJBQW1CO3NCQUEzQixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csVUFBVTtzQkFBbEIsS0FBSztnQkFDRyxZQUFZO3NCQUFwQixLQUFLO2dCQUNHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBQ0csS0FBSztzQkFBYixLQUFLO2dCQUNHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBQ0csWUFBWTtzQkFBcEIsS0FBSztnQkFDRyw0QkFBNEI7c0JBQXBDLEtBQUs7Z0JBQ0csV0FBVztzQkFBbkIsS0FBSztnQkFDRyxlQUFlO3NCQUF2QixLQUFLO2dCQUNHLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFDRyxpQkFBaUI7c0JBQXpCLEtBQUs7Z0JBQ0csZ0JBQWdCO3NCQUF4QixLQUFLO2dCQUNHLGlCQUFpQjtzQkFBekIsS0FBSztnQkFDRyxtQkFBbUI7c0JBQTNCLEtBQUs7Z0JBQ0csaUJBQWlCO3NCQUF6QixLQUFLO2dCQUNHLG9CQUFvQjtzQkFBNUIsS0FBSztnQkFDRyxvQkFBb0I7c0JBQTVCLEtBQUs7Z0JBQ0csZ0JBQWdCO3NCQUF4QixLQUFLO2dCQUNHLHNCQUFzQjtzQkFBOUIsS0FBSztnQkFDSSxXQUFXO3NCQUFwQixNQUFNO2dCQUNHLGdCQUFnQjtzQkFBekIsTUFBTTtnQkFDRyxpQkFBaUI7c0JBQTFCLE1BQU07Z0JBQ0csT0FBTztzQkFBaEIsTUFBTTtnQkFDRyxPQUFPO3NCQUFoQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRmlsZUl0ZW0sIEZpbGVVcGxvYWRlciB9IGZyb20gJ25nMi1maWxlLXVwbG9hZCc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCB6aXAgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHJldHJ5IH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5pbXBvcnQgeyBIdHRwQ2xpZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIEluamVjdCwgSW5wdXQsIE9uSW5pdCwgT3B0aW9uYWwsIE91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgeyBJbnB1dEZvcm1zQ29uZmlnIH0gZnJvbSAnLi4vLi4vaW5wdXQtZm9ybXMtY29uZmlnJztcbmltcG9ydCB7IElOUFVUX0ZPUk1TX0NPTkZJRyB9IGZyb20gJy4uLy4uL2lucHV0LWZvcm1zLWNvbmZpZy5jb25zdGFudHMnO1xuXG5sZXQgaWRlbnRpZmllciA9IDA7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2lucHV0LXVwbG9hZCcsXG4gIHRlbXBsYXRlVXJsOiAnaW5wdXQtdXBsb2FkLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJ2lucHV0LXVwbG9hZC5jb21wb25lbnQuc2NzcyddXG59KVxuZXhwb3J0IGNsYXNzIElucHV0VXBsb2FkQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0IHtcbiAgQElucHV0KCkgdXJsOiBzdHJpbmc7XG4gIEBJbnB1dCgpIGxhYmVsPzogc3RyaW5nO1xuICBASW5wdXQoKSBoZWxwOiBzdHJpbmc7XG4gIEBJbnB1dCgpIGRlc2NyaXB0aW9uOiBzdHJpbmc7XG4gIEBJbnB1dCgpIHBsYWNlaG9sZGVyPzogc3RyaW5nID0gJ1NlbGVjdCBhIGZpbGUgdG8gdXBsb2FkLi4uJztcbiAgQElucHV0KCkgZHJvcFpvbmVQbGFjZWhvbGRlcj86IHN0cmluZyA9ICdEcmFnICYgZHJvcCBhIGZpbGUgdG8gaW1wb3J0Lic7XG4gIEBJbnB1dCgpIGRpc2FibGVkPzogYm9vbGVhbiA9IGZhbHNlO1xuICBASW5wdXQoKSBhdXRvVXBsb2FkPzogYm9vbGVhbiA9IHRydWU7XG4gIEBJbnB1dCgpIHNob3dEcm9wWm9uZT86IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgc2hvd1F1ZXVlPzogYm9vbGVhbiA9IGZhbHNlO1xuICBASW5wdXQoKSBjaHVuaz86IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgY2h1bmtTaXplPzogbnVtYmVyID0gMTA0ODU3NjtcbiAgQElucHV0KCkgY2h1bmtSZXRyaWVzPzogbnVtYmVyID0gMztcbiAgQElucHV0KCkgY2h1bmtSZXF1ZXN0c0NvdW50SW5QYXJhbGxlbD86IG51bWJlciA9IDUwO1xuICBASW5wdXQoKSBtYXhGaWxlU2l6ZT86IG51bWJlciA9IDA7XG4gIEBJbnB1dCgpIHdpdGhDcmVkZW50aWFscz86IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgc2VsZWN0QnV0dG9uSWNvbj86IHN0cmluZyA9ICdmYSBmYS1mb2xkZXInO1xuICBASW5wdXQoKSBzZWxlY3RCdXR0b25MYWJlbD86IHN0cmluZyA9ICdTZWxlY3QnO1xuICBASW5wdXQoKSByZW1vdmVCdXR0b25JY29uPzogc3RyaW5nID0gJ2ZhIGZhLXRyYXNoJztcbiAgQElucHV0KCkgcmVtb3ZlQnV0dG9uTGFiZWw/OiBzdHJpbmcgPSAnUmVtb3ZlJztcbiAgQElucHV0KCkgcmVtb3ZlQnV0dG9uQWxsb3dlZD86IGJvb2xlYW4gPSB0cnVlO1xuICBASW5wdXQoKSBhbGxvd2VkRXh0ZW5zaW9ucz86IEFycmF5PHN0cmluZz47XG4gIEBJbnB1dCgpIGZpbGVUeXBlRXJyb3JNZXNzYWdlPzogc3RyaW5nID0gJ1RoZSBmaWxlIHR5cGUgW3tleHRlbnNpb259XSBpcyBub3QgYWxsb3dlZC4nO1xuICBASW5wdXQoKSBmaWxlU2l6ZUVycm9yTWVzc2FnZT86IHN0cmluZyA9ICdUaGlzIGZpbGUgZXhjZWVkcyB0aGUgbWF4IGZpbGUgc2l6ZSBhbGxvd2VkIG9mIHttYXhGaWxlU2l6ZX1NQi4nO1xuICBASW5wdXQoKSBtYXhGaWxlU2l6ZUxhYmVsPzogc3RyaW5nID0gJ01heCBmaWxlIHNpemU6JztcbiAgQElucHV0KCkgYWxsb3dlZEV4dGVuc2lvbnNMYWJlbD86IHN0cmluZyA9ICdBbGxvd2VkIGV4dGVuc2lvbnM6JztcbiAgQE91dHB1dCgpIG9uRmlsZUFkZGVkID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XG4gIEBPdXRwdXQoKSBvblVwbG9hZENvbXBsZXRlID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XG4gIEBPdXRwdXQoKSBvbkNodW5rRmlsZVVwbG9hZCA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuICBAT3V0cHV0KCkgb25FcnJvciA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuICBAT3V0cHV0KCkgb25DbGVhciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblxuICBwdWJsaWMgaWRlbnRpZmllciA9IGBpbnB1dC11cGxvYWQtJHtpZGVudGlmaWVyKyt9YDtcbiAgcHJpdmF0ZSBvblBhcmFsbGVsQ2h1bmtDb21wbGV0ZXMgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgc2VsZWN0ZWRGaWxlQmxvYjogYW55O1xuICBzZWxlY3RlZEZpbGVNb2RlbDogYW55O1xuICBzZWxlY3RlZEZpbGVOYW1lOiBzdHJpbmcgPSAnJztcbiAgZXJyb3JNZXNzYWdlOiBzdHJpbmc7XG4gIHVwbG9hZGVyOiBGaWxlVXBsb2FkZXI7XG4gIGhhc0Ryb3Bab25lT3ZlcjogYm9vbGVhbiA9IGZhbHNlO1xuICBjaHVua3M6IGFueVtdO1xuICBjaHVua1Byb2dyZXNzOiBudW1iZXIgPSAwO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIEBJbmplY3QoSU5QVVRfRk9STVNfQ09ORklHKSBAT3B0aW9uYWwoKSBjb25maWc6IElucHV0Rm9ybXNDb25maWcsXG4gICAgcHJpdmF0ZSBodHRwOiBIdHRwQ2xpZW50XG4gICkge1xuICAgIGlmIChjb25maWcgJiYgY29uZmlnLnVwbG9hZCkge1xuICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLCBjb25maWcudXBsb2FkKTtcbiAgICB9XG4gIH1cblxuICBuZ09uSW5pdCgpIHtcbiAgICB0aGlzLnVwbG9hZGVyID0gbmV3IEZpbGVVcGxvYWRlcih7XG4gICAgICB1cmw6IHRoaXMudXJsLFxuICAgICAgYXV0b1VwbG9hZDogZmFsc2UsXG4gICAgICBtYXhGaWxlU2l6ZTogdGhpcy5tYXhGaWxlU2l6ZSAqIDEwMDAwMDAsXG4gICAgfSk7XG5cbiAgICB0aGlzLmhhbmRsZVVwbG9hZGVyRXZlbnRzKCk7XG4gIH1cblxuICBDbGVhcigpOiB2b2lkIHtcbiAgICB0aGlzLmNodW5rUHJvZ3Jlc3MgPSAwO1xuICAgIHRoaXMuc2VsZWN0ZWRGaWxlTW9kZWwgPSBudWxsO1xuICAgIHRoaXMuc2VsZWN0ZWRGaWxlTmFtZSA9ICcnO1xuICAgIHRoaXMuZXJyb3JNZXNzYWdlID0gbnVsbDtcbiAgICB0aGlzLmNodW5rcyA9IG51bGw7XG4gICAgdGhpcy51cGxvYWRlci5jbGVhclF1ZXVlKCk7XG4gICAgdGhpcy51cGxvYWRlci5jYW5jZWxBbGwoKTtcbiAgICB0aGlzLm9uQ2xlYXIuZW1pdCgpO1xuICB9XG5cbiAgU3RhcnRVcGxvYWRNYW51YWxseSgpOiBQcm9taXNlPGFueT4ge1xuICAgIGxldCBwcm9taXNlID0gbmV3IFByb21pc2U8YW55PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBpZiAoIXRoaXMuc2VsZWN0ZWRGaWxlQmxvYikge1xuICAgICAgICByZWplY3QoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICB0aGlzLm9uVXBsb2FkQ29tcGxldGUuc3Vic2NyaWJlKHJlc3VsdCA9PiByZXNvbHZlKHJlc3VsdCksIGVycm9yID0+IHJlamVjdChlcnJvcikpO1xuICAgICAgdGhpcy5vbkVycm9yLnN1YnNjcmliZShyZXN1bHQgPT4gcmVqZWN0KHJlc3VsdCkpO1xuXG4gICAgICBpZiAodGhpcy5jaHVuayAmJiB0aGlzLmNodW5rcyAmJiB0aGlzLmNodW5rcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIHRoaXMuc3RhcnRDaHVua1VwbG9hZCgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5zdGFydFNpbmdsZVVwbG9hZCgpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgcmV0dXJuIHByb21pc2U7XG4gIH1cblxuICBTZXRTZWxlY3RlZEZpbGVOYW1lKGZpbGVOYW1lOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnNlbGVjdGVkRmlsZU5hbWUgPSBmaWxlTmFtZTtcbiAgfVxuXG4gIE9uRmlsZU92ZXIoZTogYW55KTogdm9pZCB7XG4gICAgdGhpcy5oYXNEcm9wWm9uZU92ZXIgPSBlO1xuICB9XG4gIE9uRmlsZUNoYW5nZShldmVudDogYW55KTogdm9pZCB7XG4gICAgaWYgKGV2ZW50LnRhcmdldC5maWxlc1swXSkge1xuICAgICAgdGhpcy5hZGRTZWxlY3RlZEZpbGVGb3JNYW51YWxVcGxvYWRpbmcoZXZlbnQudGFyZ2V0LmZpbGVzWzBdKTtcbiAgICB9XG4gIH1cbiAgT25GaWxlRHJvcChldmVudDogYW55KTogdm9pZCB7XG4gICAgaWYgKGV2ZW50WzBdKSB7XG4gICAgICB0aGlzLmFkZFNlbGVjdGVkRmlsZUZvck1hbnVhbFVwbG9hZGluZyhldmVudFswXSk7XG4gICAgfVxuICB9XG5cbiAgSGFzRmlsZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zZWxlY3RlZEZpbGVCbG9iICE9IG51bGwgJiYgdGhpcy5zZWxlY3RlZEZpbGVCbG9iICE9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIFJlc2V0U3RhdGUoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMudXBsb2FkZXIucXVldWUgJiYgdGhpcy51cGxvYWRlci5xdWV1ZS5sZW5ndGggPiAwKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMudXBsb2FkZXIucXVldWUubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdGhpcy51cGxvYWRlci5xdWV1ZVtpXS5pc0Vycm9yID0gZmFsc2U7XG4gICAgICAgIHRoaXMudXBsb2FkZXIucXVldWVbaV0uaXNVcGxvYWRlZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLnVwbG9hZGVyLnF1ZXVlW2ldLmlzU3VjY2VzcyA9IGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc3RhcnRTaW5nbGVVcGxvYWQoKTogdm9pZCB7XG4gICAgdGhpcy51cGxvYWRlci51cGxvYWRBbGwoKTtcbiAgfVxuICBwcml2YXRlIHN0YXJ0Q2h1bmtVcGxvYWQoKTogdm9pZCB7XG4gICAgdGhpcy5jaHVua1Byb2dyZXNzID0gMDtcblxuICAgIGlmICh0aGlzLmNodW5rUmVxdWVzdHNDb3VudEluUGFyYWxsZWwgPiAwICYmIHRoaXMuY2h1bmtzLmxlbmd0aCA+IHRoaXMuY2h1bmtSZXF1ZXN0c0NvdW50SW5QYXJhbGxlbCkge1xuICAgICAgdGhpcy5zZW5kR3JvdXBlZFByb21pc2VzU2VxdWVudGlhbGx5KCk7XG5cbiAgICAgIHRoaXMub25QYXJhbGxlbENodW5rQ29tcGxldGVzLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgIHRoaXMuY2h1bmtQcm9ncmVzcyA9IDEwMDtcbiAgICAgICAgdGhpcy5vblVwbG9hZENvbXBsZXRlLmVtaXQoeyBjaHVua0lkOiB0aGlzLmNodW5rc1swXS5pZCB9KTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgY2h1bmtzUHJvbWlzZXM6IEFycmF5PFByb21pc2U8dm9pZD4+ID0gW107XG4gICAgICBjaHVua3NQcm9taXNlcy5wdXNoKHRoaXMuc2VuZENodW5rcyh0aGlzLmNodW5rcykpO1xuXG4gICAgICBQcm9taXNlLmFsbChjaHVua3NQcm9taXNlcylcbiAgICAgICAgLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIHRoaXMuY2h1bmtQcm9ncmVzcyA9IDEwMDtcbiAgICAgICAgICB0aGlzLm9uVXBsb2FkQ29tcGxldGUuZW1pdCh7IGNodW5rSWQ6IHRoaXMuY2h1bmtzWzBdLmlkIH0pO1xuICAgICAgICB9KVxuICAgICAgICAuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICAgIHRoaXMub25FcnJvci5lbWl0KGVycm9yKTtcbiAgICAgICAgfSk7XG4gICAgfVxuICB9XG4gIHByaXZhdGUgc2VuZENodW5rcyhjaHVua3M6IGFueVtdKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbGV0IHByb21pc2UgPSBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBsZXQgcmVxdWVzdHM6IEFycmF5PE9ic2VydmFibGU8YW55Pj4gPSBbXTtcblxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjaHVua3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgbGV0IGZvcm1EYXRhID0gbmV3IEZvcm1EYXRhKCk7XG4gICAgICAgIGZvcm1EYXRhLmFwcGVuZChcImZpbGVcIiwgY2h1bmtzW2ldLmJsb2IsIGNodW5rc1tpXS5uYW1lKTtcblxuICAgICAgICByZXF1ZXN0cy5wdXNoKHRoaXMuaHR0cC5wb3N0KHRoaXMudXJsLCBmb3JtRGF0YSwgeyB3aXRoQ3JlZGVudGlhbHM6IHRoaXMud2l0aENyZWRlbnRpYWxzIH0pXG4gICAgICAgICAgLnBpcGUocmV0cnkodGhpcy5jaHVua1JldHJpZXMpKSk7XG4gICAgICB9XG5cbiAgICAgIHppcCguLi5yZXF1ZXN0cykuc3Vic2NyaWJlKFxuICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgdGhpcy5jaHVua1Byb2dyZXNzICs9ICgxMDAgLyB0aGlzLmNodW5rcy5sZW5ndGgpICogY2h1bmtzLmxlbmd0aDtcbiAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGVycm9yID0+IHJlamVjdChlcnJvcilcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gcHJvbWlzZTtcbiAgfVxuICBwcml2YXRlIHNlbmRHcm91cGVkUHJvbWlzZXNTZXF1ZW50aWFsbHkoaW5kZXg6IG51bWJlciA9IDApOiB2b2lkIHtcbiAgICBsZXQgc3RhcnQgPSBpbmRleCAqIHRoaXMuY2h1bmtSZXF1ZXN0c0NvdW50SW5QYXJhbGxlbDtcbiAgICBsZXQgZW5kID0gKChpbmRleCArIDEpICogdGhpcy5jaHVua1JlcXVlc3RzQ291bnRJblBhcmFsbGVsKSAtIDE7XG4gICAgbGV0IGxhc3RJbmRleCA9IGVuZCArIDEgPj0gdGhpcy5jaHVua3MubGVuZ3RoO1xuXG4gICAgaWYgKGVuZCA+IHRoaXMuY2h1bmtzLmxlbmd0aCAtIDEpIHtcbiAgICAgIGVuZCA9IHRoaXMuY2h1bmtzLmxlbmd0aCAtIDE7XG4gICAgfVxuXG4gICAgdGhpcy5zZW5kQ2h1bmtzKHRoaXMuY2h1bmtzLnNsaWNlKHN0YXJ0LCBlbmQgKyAxKSlcbiAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgaWYgKCFsYXN0SW5kZXgpIHtcbiAgICAgICAgICB0aGlzLnNlbmRHcm91cGVkUHJvbWlzZXNTZXF1ZW50aWFsbHkoaW5kZXggKyAxKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLm9uUGFyYWxsZWxDaHVua0NvbXBsZXRlcy5lbWl0KCk7XG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICAuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICB0aGlzLm9uRXJyb3IuZW1pdChlcnJvcik7XG4gICAgICAgIHRoaXMuQ2xlYXIoKTtcbiAgICAgIH0pO1xuICB9XG4gIHByaXZhdGUgc3BsaXRTZWxlY3RlZEZpbGVJbkNodW5rcygpOiB2b2lkIHtcbiAgICB0aGlzLmNodW5rcyA9IFtdO1xuICAgIGxldCBmaWxlOiBGaWxlID0gdGhpcy5zZWxlY3RlZEZpbGVCbG9iO1xuICAgIGxldCBmaWxlU2l6ZSA9IGZpbGUuc2l6ZTtcbiAgICBsZXQgc3RhcnQgPSAwO1xuICAgIGxldCBlbmQgPSB0aGlzLmNodW5rU2l6ZTtcbiAgICBsZXQgY2h1bmtzQ291bnQgPSAwO1xuICAgIGxldCBjaHVua0d1aWQgPSBNYXRoLnJhbmRvbSgpXG4gICAgICAudG9TdHJpbmcoKVxuICAgICAgLnJlcGxhY2UoXCIwLlwiLCBcIlwiKTtcblxuICAgIGlmIChmaWxlU2l6ZSAlIHRoaXMuY2h1bmtTaXplID09IDApIHtcbiAgICAgIGNodW5rc0NvdW50ID0gZmlsZVNpemUgLyB0aGlzLmNodW5rU2l6ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgY2h1bmtzQ291bnQgPSBNYXRoLmZsb29yKGZpbGVTaXplIC8gdGhpcy5jaHVua1NpemUpICsgMTtcbiAgICB9XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNodW5rc0NvdW50OyBpKyspIHtcbiAgICAgIHRoaXMuY2h1bmtzLnB1c2goe1xuICAgICAgICBpZDogY2h1bmtHdWlkLFxuICAgICAgICBuYW1lOiBgJHtjaHVua0d1aWR9XyR7aX1gLFxuICAgICAgICBibG9iOiBmaWxlLnNsaWNlKHN0YXJ0LCBlbmQpXG4gICAgICB9KTtcblxuICAgICAgc3RhcnQgPSBlbmQ7XG4gICAgICBlbmQgPSBzdGFydCArIHRoaXMuY2h1bmtTaXplO1xuICAgIH1cbiAgfVxuICBwcml2YXRlIGhhbmRsZVVwbG9hZGVyRXZlbnRzKCk6IHZvaWQge1xuICAgIHRoaXMudXBsb2FkZXIub25CZWZvcmVVcGxvYWRJdGVtID0gKGZpbGVJdGVtOiBGaWxlSXRlbSkgPT4ge1xuICAgICAgZmlsZUl0ZW0ud2l0aENyZWRlbnRpYWxzID0gdGhpcy53aXRoQ3JlZGVudGlhbHM7XG4gICAgfTtcbiAgICB0aGlzLnVwbG9hZGVyLm9uU3VjY2Vzc0l0ZW0gPSAoaXRlbTogYW55LCByZXNwb25zZTogYW55LCBzdGF0dXM6IGFueSwgaGVhZGVyczogYW55KSA9PiB7XG4gICAgICB0aGlzLmNodW5rUHJvZ3Jlc3MgPSAxMDA7XG4gICAgICB0aGlzLm9uVXBsb2FkQ29tcGxldGUuZW1pdCh7IGl0ZW0sIHJlc3BvbnNlIH0pO1xuICAgICAgdGhpcy5SZXNldFN0YXRlKCk7XG4gICAgfTtcbiAgICB0aGlzLnVwbG9hZGVyLm9uRXJyb3JJdGVtID0gKGl0ZW06IGFueSwgcmVzcG9uc2U6IGFueSwgc3RhdHVzOiBhbnksIGhlYWRlcnM6IGFueSkgPT4ge1xuICAgICAgdGhpcy5vbkVycm9yLmVtaXQoeyBpdGVtLCByZXNwb25zZSwgc3RhdHVzIH0pO1xuICAgICAgdGhpcy5SZXNldFN0YXRlKCk7XG4gICAgfTtcbiAgICB0aGlzLnVwbG9hZGVyLm9uV2hlbkFkZGluZ0ZpbGVGYWlsZWQgPSAoaXRlbTogYW55LCBmaWx0ZXI6IGFueSwgb3B0aW9uczogYW55KSA9PiB7XG4gICAgICB0aGlzLm9uRXJyb3IuZW1pdCh7IGl0ZW0sIGZpbHRlciwgb3B0aW9ucyB9KTtcbiAgICB9O1xuICB9XG4gIHByaXZhdGUgYWRkU2VsZWN0ZWRGaWxlRm9yTWFudWFsVXBsb2FkaW5nKGZpbGU6IGFueSk6IHZvaWQge1xuICAgIHRoaXMuc2VsZWN0ZWRGaWxlQmxvYiA9IG51bGw7XG5cbiAgICBpZiAoIWZpbGUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAodGhpcy52YWxpZGF0ZShmaWxlKSkge1xuICAgICAgdGhpcy5zZWxlY3RlZEZpbGVCbG9iID0gZmlsZTtcbiAgICAgIHRoaXMuU2V0U2VsZWN0ZWRGaWxlTmFtZShmaWxlLm5hbWUpO1xuXG4gICAgICBpZiAodGhpcy51cGxvYWRlci5xdWV1ZS5sZW5ndGggPiAxKSB7XG4gICAgICAgIHRoaXMudXBsb2FkZXIucmVtb3ZlRnJvbVF1ZXVlKHRoaXMudXBsb2FkZXIucXVldWVbMF0pO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5jaHVuaykge1xuICAgICAgICB0aGlzLnNwbGl0U2VsZWN0ZWRGaWxlSW5DaHVua3MoKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5vbkZpbGVBZGRlZC5lbWl0KGZpbGUpO1xuXG4gICAgICBpZiAodGhpcy5hdXRvVXBsb2FkKSB7XG4gICAgICAgIHRoaXMuU3RhcnRVcGxvYWRNYW51YWxseSgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBwcml2YXRlIHZhbGlkYXRlKGl0ZW06IGFueSk6IGJvb2xlYW4ge1xuICAgIHRoaXMuc2VsZWN0ZWRGaWxlTmFtZSA9IG51bGw7XG4gICAgdGhpcy5lcnJvck1lc3NhZ2UgPSBudWxsO1xuXG4gICAgaWYgKCF0aGlzLnZhbGlkYXRlRmlsZVR5cGUoaXRlbSkgfHwgIXRoaXMudmFsaWRhdGVGaWxlU2l6ZShpdGVtKSkge1xuICAgICAgdGhpcy5zZWxlY3RlZEZpbGVNb2RlbCA9IG51bGw7XG4gICAgICB0aGlzLnVwbG9hZGVyLmNsZWFyUXVldWUoKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICBwcml2YXRlIHZhbGlkYXRlRmlsZVR5cGUoZmlsZTogYW55KTogYm9vbGVhbiB7XG4gICAgaWYgKCF0aGlzLmFsbG93ZWRFeHRlbnNpb25zIHx8