UNPKG

ngx-ui-hero

Version:

Simple, fast and reliable utilities for Angular.

348 lines 54.7 kB
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQtdXBsb2FkLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC11aS1oZXJvL3NyYy9saWIvaW5wdXQtZm9ybXMvY29tcG9uZW50cy9pbnB1dC11cGxvYWQvaW5wdXQtdXBsb2FkLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC11aS1oZXJvL3NyYy9saWIvaW5wdXQtZm9ybXMvY29tcG9uZW50cy9pbnB1dC11cGxvYWQvaW5wdXQtdXBsb2FkLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBWSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6RCxPQUFPLEVBQWMsR0FBRyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3ZDLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV2QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBVSxRQUFRLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBR2pHLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDOzs7Ozs7OztBQUV4RSxJQUFJLFVBQVUsR0FBRyxDQUFDLENBQUM7QUFPbkIsTUFBTSxPQUFPLG9CQUFvQjtJQTZDL0IsWUFDMEMsTUFBd0IsRUFDeEQsSUFBZ0I7UUFBaEIsU0FBSSxHQUFKLElBQUksQ0FBWTtRQTFDakIsZ0JBQVcsR0FBWSw0QkFBNEIsQ0FBQztRQUNwRCx3QkFBbUIsR0FBWSwrQkFBK0IsQ0FBQztRQUMvRCxhQUFRLEdBQWEsS0FBSyxDQUFDO1FBQzNCLGVBQVUsR0FBYSxJQUFJLENBQUM7UUFDNUIsaUJBQVksR0FBYSxLQUFLLENBQUM7UUFDL0IsY0FB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b29GQWdFQTs0RkQvQ2Esb0JBQW9CO2tCQUxoQyxTQUFTOytCQUNFLGNBQWM7OzBCQWtEckIsTUFBTTsyQkFBQyxrQkFBa0I7OzBCQUFHLFFBQVE7cUVBN0M5QixHQUFHO3NCQUFYLEtBQUs7Z0JBQ0csS0FBSztzQkFBYixLQUFLO2dCQUNHLElBQUk7c0JBQVosS0FBSztnQkFDRyxXQUFXO3NCQUFuQixLQUFLO2dCQUNHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBQ0csbUJBQW1CO3NCQUEzQixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csVUFBVTtzQkFBbEIsS0FBSztnQkFDRyxZQUFZO3NCQUFwQixLQUFLO2dCQUNHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBQ0csS0FBSztzQkFBYixLQUFLO2dCQUNHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBQ0csWUFBWTtzQkFBcEIsS0FBSztnQkFDRyw0QkFBNEI7c0JBQXBDLEtBQUs7Z0JBQ0csV0FBVztzQkFBbkIsS0FBSztnQkFDRyxlQUFlO3NCQUF2QixLQUFLO2dCQUNHLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFDRyxpQkFBaUI7c0JBQXpCLEtBQUs7Z0JBQ0csZ0JBQWdCO3NCQUF4QixLQUFLO2dCQUNHLGlCQUFpQjtzQkFBekIsS0FBSztnQkFDRyxtQkFBbUI7c0JBQTNCLEtBQUs7Z0JBQ0csaUJBQWlCO3NCQUF6QixLQUFLO2dCQUNHLG9CQUFvQjtzQkFBNUIsS0FBSztnQkFDRyxvQkFBb0I7c0JBQTVCLEtBQUs7Z0JBQ0csZ0JBQWdCO3NCQUF4QixLQUFLO2dCQUNHLHNCQUFzQjtzQkFBOUIsS0FBSztnQkFDSSxXQUFXO3NCQUFwQixNQUFNO2dCQUNHLGdCQUFnQjtzQkFBekIsTUFBTTtnQkFDRyxpQkFBaUI7c0JBQTFCLE1BQU07Z0JBQ0csT0FBTztzQkFBaEIsTUFBTTtnQkFDRyxPQUFPO3NCQUFoQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRmlsZUl0ZW0sIEZpbGVVcGxvYWRlciB9IGZyb20gJ25nMi1maWxlLXVwbG9hZCc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCB6aXAgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHJldHJ5IH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5pbXBvcnQgeyBIdHRwQ2xpZW50IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIEluamVjdCwgSW5wdXQsIE9uSW5pdCwgT3B0aW9uYWwsIE91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgeyBJbnB1dEZvcm1zQ29uZmlnIH0gZnJvbSAnLi4vLi4vaW5wdXQtZm9ybXMtY29uZmlnJztcbmltcG9ydCB7IElOUFVUX0ZPUk1TX0NPTkZJRyB9IGZyb20gJy4uLy4uL2lucHV0LWZvcm1zLWNvbmZpZy5jb25zdGFudHMnO1xuXG5sZXQgaWRlbnRpZmllciA9IDA7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2lucHV0LXVwbG9hZCcsXG4gIHRlbXBsYXRlVXJsOiAnaW5wdXQtdXBsb2FkLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJ2lucHV0LXVwbG9hZC5jb21wb25lbnQuc2NzcyddXG59KVxuZXhwb3J0IGNsYXNzIElucHV0VXBsb2FkQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0IHtcbiAgQElucHV0KCkgdXJsOiBzdHJpbmc7XG4gIEBJbnB1dCgpIGxhYmVsPzogc3RyaW5nO1xuICBASW5wdXQoKSBoZWxwOiBzdHJpbmc7XG4gIEBJbnB1dCgpIGRlc2NyaXB0aW9uOiBzdHJpbmc7XG4gIEBJbnB1dCgpIHBsYWNlaG9sZGVyPzogc3RyaW5nID0gJ1NlbGVjdCBhIGZpbGUgdG8gdXBsb2FkLi4uJztcbiAgQElucHV0KCkgZHJvcFpvbmVQbGFjZWhvbGRlcj86IHN0cmluZyA9ICdEcmFnICYgZHJvcCBhIGZpbGUgdG8gaW1wb3J0Lic7XG4gIEBJbnB1dCgpIGRpc2FibGVkPzogYm9vbGVhbiA9IGZhbHNlO1xuICBASW5wdXQoKSBhdXRvVXBsb2FkPzogYm9vbGVhbiA9IHRydWU7XG4gIEBJbnB1dCgpIHNob3dEcm9wWm9uZT86IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgc2hvd1F1ZXVlPzogYm9vbGVhbiA9IGZhbHNlO1xuICBASW5wdXQoKSBjaHVuaz86IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgY2h1bmtTaXplPzogbnVtYmVyID0gMTA0ODU3NjtcbiAgQElucHV0KCkgY2h1bmtSZXRyaWVzPzogbnVtYmVyID0gMztcbiAgQElucHV0KCkgY2h1bmtSZXF1ZXN0c0NvdW50SW5QYXJhbGxlbD86IG51bWJlciA9IDUwO1xuICBASW5wdXQoKSBtYXhGaWxlU2l6ZT86IG51bWJlciA9IDA7XG4gIEBJbnB1dCgpIHdpdGhDcmVkZW50aWFscz86IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgc2VsZWN0QnV0dG9uSWNvbj86IHN0cmluZyA9ICdmYSBmYS1mb2xkZXInO1xuICBASW5wdXQoKSBzZWxlY3RCdXR0b25MYWJlbD86IHN0cmluZyA9ICdTZWxlY3QnO1xuICBASW5wdXQoKSByZW1vdmVCdXR0b25JY29uPzogc3RyaW5nID0gJ2ZhIGZhLXRyYXNoJztcbiAgQElucHV0KCkgcmVtb3ZlQnV0dG9uTGFiZWw/OiBzdHJpbmcgPSAnUmVtb3ZlJztcbiAgQElucHV0KCkgcmVtb3ZlQnV0dG9uQWxsb3dlZD86IGJvb2xlYW4gPSB0cnVlO1xuICBASW5wdXQoKSBhbGxvd2VkRXh0ZW5zaW9ucz86IEFycmF5PHN0cmluZz47XG4gIEBJbnB1dCgpIGZpbGVUeXBlRXJyb3JNZXNzYWdlPzogc3RyaW5nID0gJ1RoZSBmaWxlIHR5cGUgW3tleHRlbnNpb259XSBpcyBub3QgYWxsb3dlZC4nO1xuICBASW5wdXQoKSBmaWxlU2l6ZUVycm9yTWVzc2FnZT86IHN0cmluZyA9ICdUaGlzIGZpbGUgZXhjZWVkcyB0aGUgbWF4IGZpbGUgc2l6ZSBhbGxvd2VkIG9mIHttYXhGaWxlU2l6ZX1NQi4nO1xuICBASW5wdXQoKSBtYXhGaWxlU2l6ZUxhYmVsPzogc3RyaW5nID0gJ01heCBmaWxlIHNpemU6JztcbiAgQElucHV0KCkgYWxsb3dlZEV4dGVuc2lvbnNMYWJlbD86IHN0cmluZyA9ICdBbGxvd2VkIGV4dGVuc2lvbnM6JztcbiAgQE91dHB1dCgpIG9uRmlsZUFkZGVkID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XG4gIEBPdXRwdXQoKSBvblVwbG9hZENvbXBsZXRlID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XG4gIEBPdXRwdXQoKSBvbkNodW5rRmlsZVVwbG9hZCA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuICBAT3V0cHV0KCkgb25FcnJvciA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuICBAT3V0cHV0KCkgb25DbGVhciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblxuICBwdWJsaWMgaWRlbnRpZmllciA9IGBpbnB1dC11cGxvYWQtJHtpZGVudGlmaWVyKyt9YDtcbiAgcHJpdmF0ZSBvblBhcmFsbGVsQ2h1bmtDb21wbGV0ZXMgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgc2VsZWN0ZWRGaWxlQmxvYjogYW55O1xuICBzZWxlY3RlZEZpbGVNb2RlbDogYW55O1xuICBzZWxlY3RlZEZpbGVOYW1lOiBzdHJpbmcgPSAnJztcbiAgZXJyb3JNZXNzYWdlOiBzdHJpbmc7XG4gIHVwbG9hZGVyOiBGaWxlVXBsb2FkZXI7XG4gIGhhc0Ryb3Bab25lT3ZlcjogYm9vbGVhbiA9IGZhbHNlO1xuICBjaHVua3M6IGFueVtdO1xuICBjaHVua1Byb2dyZXNzOiBudW1iZXIgPSAwO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIEBJbmplY3QoSU5QVVRfRk9STVNfQ09ORklHKSBAT3B0aW9uYWwoKSBjb25maWc6IElucHV0Rm9ybXNDb25maWcsXG4gICAgcHJpdmF0ZSBodHRwOiBIdHRwQ2xpZW50XG4gICkge1xuICAgIGlmIChjb25maWcgJiYgY29uZmlnLnVwbG9hZCkge1xuICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLCBjb25maWcudXBsb2FkKTtcbiAgICB9XG4gIH1cblxuICBuZ09uSW5pdCgpIHtcbiAgICB0aGlzLnVwbG9hZGVyID0gbmV3IEZpbGVVcGxvYWRlcih7XG4gICAgICB1cmw6IHRoaXMudXJsLFxuICAgICAgYXV0b1VwbG9hZDogZmFsc2UsXG4gICAgICBtYXhGaWxlU2l6ZTogdGhpcy5tYXhGaWxlU2l6ZSAqIDEwMDAwMDAsXG4gICAgfSk7XG5cbiAgICB0aGlzLmhhbmRsZVVwbG9hZGVyRXZlbnRzKCk7XG4gIH1cblxuICBDbGVhcigpOiB2b2lkIHtcbiAgICB0aGlzLmNodW5rUHJvZ3Jlc3MgPSAwO1xuICAgIHRoaXMuc2VsZWN0ZWRGaWxlTW9kZWwgPSBudWxsO1xuICAgIHRoaXMuc2VsZWN0ZWRGaWxlTmFtZSA9ICcnO1xuICAgIHRoaXMuZXJyb3JNZXNzYWdlID0gbnVsbDtcbiAgICB0aGlzLmNodW5rcyA9IG51bGw7XG4gICAgdGhpcy51cGxvYWRlci5jbGVhclF1ZXVlKCk7XG4gICAgdGhpcy51cGxvYWRlci5jYW5jZWxBbGwoKTtcbiAgICB0aGlzLm9uQ2xlYXIuZW1pdCgpO1xuICB9XG5cbiAgU3RhcnRVcGxvYWRNYW51YWxseSgpOiBQcm9taXNlPGFueT4ge1xuICAgIGxldCBwcm9taXNlID0gbmV3IFByb21pc2U8YW55PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBpZiAoIXRoaXMuc2VsZWN0ZWRGaWxlQmxvYikge1xuICAgICAgICByZWplY3QoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICB0aGlzLm9uVXBsb2FkQ29tcGxldGUuc3Vic2NyaWJlKHJlc3VsdCA9PiByZXNvbHZlKHJlc3VsdCksIGVycm9yID0+IHJlamVjdChlcnJvcikpO1xuICAgICAgdGhpcy5vbkVycm9yLnN1YnNjcmliZShyZXN1bHQgPT4gcmVqZWN0KHJlc3VsdCkpO1xuXG4gICAgICBpZiAodGhpcy5jaHVuayAmJiB0aGlzLmNodW5rcyAmJiB0aGlzLmNodW5rcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIHRoaXMuc3RhcnRDaHVua1VwbG9hZCgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5zdGFydFNpbmdsZVVwbG9hZCgpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgcmV0dXJuIHByb21pc2U7XG4gIH1cblxuICBTZXRTZWxlY3RlZEZpbGVOYW1lKGZpbGVOYW1lOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnNlbGVjdGVkRmlsZU5hbWUgPSBmaWxlTmFtZTtcbiAgfVxuXG4gIE9uRmlsZU92ZXIoZTogYW55KTogdm9pZCB7XG4gICAgdGhpcy5oYXNEcm9wWm9uZU92ZXIgPSBlO1xuICB9XG4gIE9uRmlsZUNoYW5nZShldmVudDogYW55KTogdm9pZCB7XG4gICAgaWYgKGV2ZW50LnRhcmdldC5maWxlc1swXSkge1xuICAgICAgdGhpcy5hZGRTZWxlY3RlZEZpbGVGb3JNYW51YWxVcGxvYWRpbmcoZXZlbnQudGFyZ2V0LmZpbGVzWzBdKTtcbiAgICB9XG4gIH1cbiAgT25GaWxlRHJvcChldmVudDogYW55KTogdm9pZCB7XG4gICAgaWYgKGV2ZW50WzBdKSB7XG4gICAgICB0aGlzLmFkZFNlbGVjdGVkRmlsZUZvck1hbnVhbFVwbG9hZGluZyhldmVudFswXSk7XG4gICAgfVxuICB9XG5cbiAgSGFzRmlsZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zZWxlY3RlZEZpbGVCbG9iICE9IG51bGwgJiYgdGhpcy5zZWxlY3RlZEZpbGVCbG9iICE9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIFJlc2V0U3RhdGUoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMudXBsb2FkZXIucXVldWUgJiYgdGhpcy51cGxvYWRlci5xdWV1ZS5sZW5ndGggPiAwKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMudXBsb2FkZXIucXVldWUubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdGhpcy51cGxvYWRlci5xdWV1ZVtpXS5pc0Vycm9yID0gZmFsc2U7XG4gICAgICAgIHRoaXMudXBsb2FkZXIucXVldWVbaV0uaXNVcGxvYWRlZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLnVwbG9hZGVyLnF1ZXVlW2ldLmlzU3VjY2VzcyA9IGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc3RhcnRTaW5nbGVVcGxvYWQoKTogdm9pZCB7XG4gICAgdGhpcy51cGxvYWRlci51cGxvYWRBbGwoKTtcbiAgfVxuICBwcml2YXRlIHN0YXJ0Q2h1bmtVcGxvYWQoKTogdm9pZCB7XG4gICAgdGhpcy5jaHVua1Byb2dyZXNzID0gMDtcblxuICAgIGlmICh0aGlzLmNodW5rUmVxdWVzdHNDb3VudEluUGFyYWxsZWwgPiAwICYmIHRoaXMuY2h1bmtzLmxlbmd0aCA+IHRoaXMuY2h1bmtSZXF1ZXN0c0NvdW50SW5QYXJhbGxlbCkge1xuICAgICAgdGhpcy5zZW5kR3JvdXBlZFByb21pc2VzU2VxdWVudGlhbGx5KCk7XG5cbiAgICAgIHRoaXMub25QYXJhbGxlbENodW5rQ29tcGxldGVzLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgIHRoaXMuY2h1bmtQcm9ncmVzcyA9IDEwMDtcbiAgICAgICAgdGhpcy5vblVwbG9hZENvbXBsZXRlLmVtaXQoeyBjaHVua0lkOiB0aGlzLmNodW5rc1swXS5pZCB9KTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgY2h1bmtzUHJvbWlzZXM6IEFycmF5PFByb21pc2U8dm9pZD4+ID0gW107XG4gICAgICBjaHVua3NQcm9taXNlcy5wdXNoKHRoaXMuc2VuZENodW5rcyh0aGlzLmNodW5rcykpO1xuXG4gICAgICBQcm9taXNlLmFsbChjaHVua3NQcm9taXNlcylcbiAgICAgICAgLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIHRoaXMuY2h1bmtQcm9ncmVzcyA9IDEwMDtcbiAgICAgICAgICB0aGlzLm9uVXBsb2FkQ29tcGxldGUuZW1pdCh7IGNodW5rSWQ6IHRoaXMuY2h1bmtzWzBdLmlkIH0pO1xuICAgICAgICB9KVxuICAgICAgICAuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICAgIHRoaXMub25FcnJvci5lbWl0KGVycm9yKTtcbiAgICAgICAgfSk7XG4gICAgfVxuICB9XG4gIHByaXZhdGUgc2VuZENodW5rcyhjaHVua3M6IGFueVtdKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbGV0IHByb21pc2UgPSBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBsZXQgcmVxdWVzdHM6IEFycmF5PE9ic2VydmFibGU8YW55Pj4gPSBbXTtcblxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjaHVua3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgbGV0IGZvcm1EYXRhID0gbmV3IEZvcm1EYXRhKCk7XG4gICAgICAgIGZvcm1EYXRhLmFwcGVuZChcImZpbGVcIiwgY2h1bmtzW2ldLmJsb2IsIGNodW5rc1tpXS5uYW1lKTtcblxuICAgICAgICByZXF1ZXN0cy5wdXNoKHRoaXMuaHR0cC5wb3N0KHRoaXMudXJsLCBmb3JtRGF0YSwgeyB3aXRoQ3JlZGVudGlhbHM6IHRoaXMud2l0aENyZWRlbnRpYWxzIH0pXG4gICAgICAgICAgLnBpcGUocmV0cnkodGhpcy5jaHVua1JldHJpZXMpKSk7XG4gICAgICB9XG5cbiAgICAgIHppcCguLi5yZXF1ZXN0cykuc3Vic2NyaWJlKFxuICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgdGhpcy5jaHVua1Byb2dyZXNzICs9ICgxMDAgLyB0aGlzLmNodW5rcy5sZW5ndGgpICogY2h1bmtzLmxlbmd0aDtcbiAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgIH0sXG4gICAgICAgIGVycm9yID0+IHJlamVjdChlcnJvcilcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gcHJvbWlzZTtcbiAgfVxuICBwcml2YXRlIHNlbmRHcm91cGVkUHJvbWlzZXNTZXF1ZW50aWFsbHkoaW5kZXg6IG51bWJlciA9IDApOiB2b2lkIHtcbiAgICBsZXQgc3RhcnQgPSBpbmRleCAqIHRoaXMuY2h1bmtSZXF1ZXN0c0NvdW50SW5QYXJhbGxlbDtcbiAgICBsZXQgZW5kID0gKChpbmRleCArIDEpICogdGhpcy5jaHVua1JlcXVlc3RzQ291bnRJblBhcmFsbGVsKSAtIDE7XG4gICAgbGV0IGxhc3RJbmRleCA9IGVuZCArIDEgPj0gdGhpcy5jaHVua3MubGVuZ3RoO1xuXG4gICAgaWYgKGVuZCA+IHRoaXMuY2h1bmtzLmxlbmd0aCAtIDEpIHtcbiAgICAgIGVuZCA9IHRoaXMuY2h1bmtzLmxlbmd0aCAtIDE7XG4gICAgfVxuXG4gICAgdGhpcy5zZW5kQ2h1bmtzKHRoaXMuY2h1bmtzLnNsaWNlKHN0YXJ0LCBlbmQgKyAxKSlcbiAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgaWYgKCFsYXN0SW5kZXgpIHtcbiAgICAgICAgICB0aGlzLnNlbmRHcm91cGVkUHJvbWlzZXNTZXF1ZW50aWFsbHkoaW5kZXggKyAxKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLm9uUGFyYWxsZWxDaHVua0NvbXBsZXRlcy5lbWl0KCk7XG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICAuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICB0aGlzLm9uRXJyb3IuZW1pdChlcnJvcik7XG4gICAgICAgIHRoaXMuQ2xlYXIoKTtcbiAgICAgIH0pO1xuICB9XG4gIHByaXZhdGUgc3BsaXRTZWxlY3RlZEZpbGVJbkNodW5rcygpOiB2b2lkIHtcbiAgICB0aGlzLmNodW5rcyA9IFtdO1xuICAgIGxldCBmaWxlOiBGaWxlID0gdGhpcy5zZWxlY3RlZEZpbGVCbG9iO1xuICAgIGxldCBmaWxlU2l6ZSA9IGZpbGUuc2l6ZTtcbiAgICBsZXQgc3RhcnQgPSAwO1xuICAgIGxldCBlbmQgPSB0aGlzLmNodW5rU2l6ZTtcbiAgICBsZXQgY2h1bmtzQ291bnQgPSAwO1xuICAgIGxldCBjaHVua0d1aWQgPSBNYXRoLnJhbmRvbSgpXG4gICAgICAudG9TdHJpbmcoKVxuICAgICAgLnJlcGxhY2UoXCIwLlwiLCBcIlwiKTtcblxuICAgIGlmIChmaWxlU2l6ZSAlIHRoaXMuY2h1bmtTaXplID09IDApIHtcbiAgICAgIGNodW5rc0NvdW50ID0gZmlsZVNpemUgLyB0aGlzLmNodW5rU2l6ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgY2h1bmtzQ291bnQgPSBNYXRoLmZsb29yKGZpbGVTaXplIC8gdGhpcy5jaHVua1NpemUpICsgMTtcbiAgICB9XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNodW5rc0NvdW50OyBpKyspIHtcbiAgICAgIHRoaXMuY2h1bmtzLnB1c2goe1xuICAgICAgICBpZDogY2h1bmtHdWlkLFxuICAgICAgICBuYW1lOiBgJHtjaHVua0d1aWR9XyR7aX1gLFxuICAgICAgICBibG9iOiBmaWxlLnNsaWNlKHN0YXJ0LCBlbmQpXG4gICAgICB9KTtcblxuICAgICAgc3RhcnQgPSBlbmQ7XG4gICAgICBlbmQgPSBzdGFydCArIHRoaXMuY2h1bmtTaXplO1xuICAgIH1cbiAgfVxuICBwcml2YXRlIGhhbmRsZVVwbG9hZGVyRXZlbnRzKCk6IHZvaWQge1xuICAgIHRoaXMudXBsb2FkZXIub25CZWZvcmVVcGxvYWRJdGVtID0gKGZpbGVJdGVtOiBGaWxlSXRlbSkgPT4ge1xuICAgICAgZmlsZUl0ZW0ud2l0aENyZWRlbnRpYWxzID0gdGhpcy53aXRoQ3JlZGVudGlhbHM7XG4gICAgfTtcbiAgICB0aGlzLnVwbG9hZGVyLm9uU3VjY2Vzc0l0ZW0gPSAoaXRlbTogYW55LCByZXNwb25zZTogYW55LCBzdGF0dXM6IGFueSwgaGVhZGVyczogYW55KSA9PiB7XG4gICAgICB0aGlzLmNodW5rUHJvZ3Jlc3MgPSAxMDA7XG4gICAgICB0aGlzLm9uVXBsb2FkQ29tcGxldGUuZW1pdCh7IGl0ZW0sIHJlc3BvbnNlIH0pO1xuICAgICAgdGhpcy5SZXNldFN0YXRlKCk7XG4gICAgfTtcbiAgICB0aGlzLnVwbG9hZGVyLm9uRXJyb3JJdGVtID0gKGl0ZW06IGFueSwgcmVzcG9uc2U6IGFueSwgc3RhdHVzOiBhbnksIGhlYWRlcnM6IGFueSkgPT4ge1xuICAgICAgdGhpcy5vbkVycm9yLmVtaXQoeyBpdGVtLCByZXNwb25zZSwgc3RhdHVzIH0pO1xuICAgICAgdGhpcy5SZXNldFN0YXRlKCk7XG4gICAgfTtcbiAgICB0aGlzLnVwbG9hZGVyLm9uV2hlbkFkZGluZ0ZpbGVGYWlsZWQgPSAoaXRlbTogYW55LCBmaWx0ZXI6IGFueSwgb3B0aW9uczogYW55KSA9PiB7XG4gICAgICB0aGlzLm9uRXJyb3IuZW1pdCh7IGl0ZW0sIGZpbHRlciwgb3B0aW9ucyB9KTtcbiAgICB9O1xuICB9XG4gIHByaXZhdGUgYWRkU2VsZWN0ZWRGaWxlRm9yTWFudWFsVXBsb2FkaW5nKGZpbGU6IGFueSk6IHZvaWQge1xuICAgIHRoaXMuc2VsZWN0ZWRGaWxlQmxvYiA9IG51bGw7XG5cbiAgICBpZiAoIWZpbGUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAodGhpcy52YWxpZGF0ZShmaWxlKSkge1xuICAgICAgdGhpcy5zZWxlY3RlZEZpbGVCbG9iID0gZmlsZTtcbiAgICAgIHRoaXMuU2V0U2VsZWN0ZWRGaWxlTmFtZShmaWxlLm5hbWUpO1xuXG4gICAgICBpZiAodGhpcy51cGxvYWRlci5xdWV1ZS5sZW5ndGggPiAxKSB7XG4gICAgICAgIHRoaXMudXBsb2FkZXIucmVtb3ZlRnJvbVF1ZXVlKHRoaXMudXBsb2FkZXIucXVldWVbMF0pO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5jaHVuaykge1xuICAgICAgICB0aGlzLnNwbGl0U2VsZWN0ZWRGaWxlSW5DaHVua3MoKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5vbkZpbGVBZGRlZC5lbWl0KGZpbGUpO1xuXG4gICAgICBpZiAodGhpcy5hdXRvVXBsb2FkKSB7XG4gICAgICAgIHRoaXMuU3RhcnRVcGxvYWRNYW51YWxseSgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBwcml2YXRlIHZhbGlkYXRlKGl0ZW06IGFueSk6IGJvb2xlYW4ge1xuICAgIHRoaXMuc2VsZWN0ZWRGaWxlTmFtZSA9IG51bGw7XG4gICAgdGhpcy5lcnJvck1lc3NhZ2UgPSBudWxsO1xuXG4gICAgaWYgKCF0aGlzLnZhbGlkYXRlRmlsZVR5cGUoaXRlbSkgfHwgIXRoaXMudmFsaWRhdGVGaWxlU2l6ZShpdGVtKSkge1xuICAgICAgdGhpcy5zZWxlY3RlZEZpbGVNb2RlbCA9IG51bGw7XG4gICAgICB0aGlzLnVwbG9hZGVyLmNsZWFyUXVldWUoKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICBwcml2YXRlIHZhbGlkYXRlRmlsZVR5cGUoZmlsZTogYW55KTogYm9vbGVhbiB7XG4gICAgaWYgKCF0aGlzLmFsbG93ZWRFeHRlbnNpb25zIHx8