ngx-image-upload
Version:
Angular 6 image upload.
556 lines (548 loc) • 57.5 kB
JavaScript
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { Injectable, Directive, EventEmitter, HostListener, Input, Output, defineInjectable, inject, Component, ViewChild, NgModule } from '@angular/core';
import { __awaiter, __generator } from 'tslib';
import { CommonModule } from '@angular/common';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var NgxImageUploadService = /** @class */ (function () {
function NgxImageUploadService(http) {
this.http = http;
}
/**
* @param {?} url
* @param {?} image
* @param {?=} headers
* @param {?=} partName
* @param {?=} customFormData
* @param {?=} withCredentials
* @return {?}
*/
NgxImageUploadService.prototype.postImage = /**
* @param {?} url
* @param {?} image
* @param {?=} headers
* @param {?=} partName
* @param {?=} customFormData
* @param {?=} withCredentials
* @return {?}
*/
function (url, image, headers, partName, customFormData, withCredentials) {
if (partName === void 0) { partName = 'image'; }
if (!url || url === '') {
throw new Error('请设置url参数');
}
// add custom form data
var /** @type {?} */ formData = new FormData();
for (var /** @type {?} */ key in customFormData) {
formData.append(key, customFormData[key]);
}
formData.append(partName, image);
return this.http.post(url, formData, { headers: headers, withCredentials: withCredentials });
};
NgxImageUploadService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] },
];
/** @nocollapse */
NgxImageUploadService.ctorParameters = function () { return [
{ type: HttpClient }
]; };
/** @nocollapse */ NgxImageUploadService.ngInjectableDef = defineInjectable({ factory: function NgxImageUploadService_Factory() { return new NgxImageUploadService(inject(HttpClient)); }, token: NgxImageUploadService, providedIn: "root" });
return NgxImageUploadService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var FileHolder = /** @class */ (function () {
function FileHolder(src, file) {
this.src = src;
this.file = file;
this.pending = false;
}
return FileHolder;
}());
var NgxImageUploadComponent = /** @class */ (function () {
function NgxImageUploadComponent(imageService) {
var _this = this;
this.imageService = imageService;
this.files = [];
this.fileCounter = 0;
this.fileOver = false;
this.showFileTooLargeMessage = false;
this.beforeUpload = function (data) { return data; };
this.buttonCaption = '添加图片';
this.disabled = false;
this.cssClass = 'img-ul';
this.clearButtonCaption = '清除';
this.dropBoxMessage = '拖拽图片到此处';
this.max = 100;
this.preview = true;
this.withCredentials = false;
this.uploadedFiles = [];
this.removed = new EventEmitter();
this.uploadStateChanged = new EventEmitter();
this.uploadFinished = new EventEmitter();
this.previewClicked = new EventEmitter();
this.pendingFilesCounter = 0;
this.onFileOver = function (isOver) { return _this.fileOver = isOver; };
this.countRemainingSlots = function () { return _this.max - _this.fileCounter; };
}
/**
* @return {?}
*/
NgxImageUploadComponent.prototype.ngOnInit = /**
* @return {?}
*/
function () {
if (!this.fileTooLargeMessage) {
this.fileTooLargeMessage = '图片太大无法上传.' + (this.maxFileSize ? (' 最大允许 ' + this.maxFileSize / 1024) + 'KiB.' : '');
}
this.supportedExtensions = this.supportedExtensions ? this.supportedExtensions.map(function (ext) { return 'image/' + ext; }) : ['image/*'];
};
/**
* @return {?}
*/
NgxImageUploadComponent.prototype.deleteAll = /**
* @return {?}
*/
function () {
var _this = this;
this.files.forEach(function (f) { return _this.removed.emit(f); });
this.files = [];
this.fileCounter = 0;
if (this.inputElement) {
this.inputElement.nativeElement.value = '';
}
};
/**
* @param {?} file
* @return {?}
*/
NgxImageUploadComponent.prototype.deleteFile = /**
* @param {?} file
* @return {?}
*/
function (file) {
var /** @type {?} */ index = this.files.indexOf(file);
this.files.splice(index, 1);
this.fileCounter--;
if (this.inputElement) {
this.inputElement.nativeElement.value = '';
}
this.removed.emit(file);
};
/**
* @param {?} file
* @return {?}
*/
NgxImageUploadComponent.prototype.previewFileClicked = /**
* @param {?} file
* @return {?}
*/
function (file) {
this.previewClicked.emit(file);
};
/**
* @param {?} changes
* @return {?}
*/
NgxImageUploadComponent.prototype.ngOnChanges = /**
* @param {?} changes
* @return {?}
*/
function (changes) {
if (changes.uploadedFiles && changes.uploadedFiles.currentValue && changes.uploadedFiles.currentValue.length > 0) {
this.processUploadedFiles();
}
};
/**
* @param {?} files
* @return {?}
*/
NgxImageUploadComponent.prototype.onFileChange = /**
* @param {?} files
* @return {?}
*/
function (files) {
if (this.disabled)
return;
// let remainingSlots = 0;
var /** @type {?} */ remainingSlots = this.countRemainingSlots();
var /** @type {?} */ filesToUploadNum = files.length > remainingSlots ? remainingSlots : files.length;
if (this.url && filesToUploadNum != 0) {
this.uploadStateChanged.emit(true);
}
this.fileCounter += filesToUploadNum;
this.showFileTooLargeMessage = false;
this.uploadFiles(files, filesToUploadNum);
};
/**
* @param {?} response
* @param {?} fileHolder
* @return {?}
*/
NgxImageUploadComponent.prototype.onResponse = /**
* @param {?} response
* @param {?} fileHolder
* @return {?}
*/
function (response, fileHolder) {
fileHolder.serverResponse = { status: response.status, response: response };
fileHolder.pending = false;
this.uploadFinished.emit(fileHolder);
if (--this.pendingFilesCounter == 0) {
this.uploadStateChanged.emit(false);
}
};
/**
* @return {?}
*/
NgxImageUploadComponent.prototype.processUploadedFiles = /**
* @return {?}
*/
function () {
if (typeof this.uploadedFiles === 'string') {
var /** @type {?} */ data = this.uploadedFiles;
this.fileCounter = 1;
this.max = 1;
var /** @type {?} */ fileBlob = void 0, /** @type {?} */
file = void 0, /** @type {?} */
fileUrl = void 0;
if (data instanceof Object) {
fileUrl = data.url;
fileBlob = (data.blob) ? data.blob : new Blob([data]);
file = new File([fileBlob], data.fileName);
}
else {
fileUrl = data;
fileBlob = new Blob([fileUrl]);
file = new File([fileBlob], fileUrl);
}
this.files = [];
this.files.push(new FileHolder(fileUrl, file));
}
else {
this.max = this.max - Object.keys(this.uploadedFiles).length;
for (var /** @type {?} */ i in this.uploadedFiles) {
var /** @type {?} */ data = this.uploadedFiles[i];
var /** @type {?} */ fileBlob = void 0, /** @type {?} */
file = void 0, /** @type {?} */
fileUrl = void 0;
if (data instanceof Object) {
fileUrl = data.url;
fileBlob = (data.blob) ? data.blob : new Blob([data]);
file = new File([fileBlob], data.fileName);
}
else {
fileUrl = data;
fileBlob = new Blob([fileUrl]);
file = new File([fileBlob], fileUrl);
}
this.files.push(new FileHolder(fileUrl, file));
}
}
};
/**
* @param {?} files
* @param {?} filesToUploadNum
* @return {?}
*/
NgxImageUploadComponent.prototype.uploadFiles = /**
* @param {?} files
* @param {?} filesToUploadNum
* @return {?}
*/
function (files, filesToUploadNum) {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
var _loop_1, this_1, i;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_loop_1 = function (i) {
var file, beforeUploadResult, img, reader;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
file = files[i];
if (this_1.maxFileSize && file.size > this_1.maxFileSize) {
this_1.fileCounter--;
this_1.inputElement.nativeElement.value = '';
this_1.showFileTooLargeMessage = true;
return [2 /*return*/, "continue"];
}
return [4 /*yield*/, this_1.beforeUpload({ file: file, url: this_1.url, abort: false })];
case 1:
beforeUploadResult = _a.sent();
if (beforeUploadResult.abort) {
this_1.fileCounter--;
this_1.inputElement.nativeElement.value = '';
return [2 /*return*/, "continue"];
}
img = document.createElement('img');
img.src = window.URL.createObjectURL(beforeUploadResult.file);
reader = new FileReader();
reader.addEventListener('load', function (event) {
var /** @type {?} */ fileHolder = new FileHolder(event.target.result, beforeUploadResult.file);
_this.uploadSingleFile(fileHolder, beforeUploadResult.url, beforeUploadResult.formData);
_this.files.push(fileHolder);
}, false);
reader.readAsDataURL(beforeUploadResult.file);
return [2 /*return*/];
}
});
};
this_1 = this;
i = 0;
_a.label = 1;
case 1:
if (!(i < filesToUploadNum)) return [3 /*break*/, 4];
return [5 /*yield**/, _loop_1(i)];
case 2:
_a.sent();
_a.label = 3;
case 3:
i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/];
}
});
});
};
/**
* @param {?} fileHolder
* @param {?=} url
* @param {?=} customForm
* @return {?}
*/
NgxImageUploadComponent.prototype.uploadSingleFile = /**
* @param {?} fileHolder
* @param {?=} url
* @param {?=} customForm
* @return {?}
*/
function (fileHolder, url, customForm) {
var _this = this;
if (url === void 0) { url = this.url; }
if (url) {
this.pendingFilesCounter++;
fileHolder.pending = true;
this.imageService
.postImage(url, fileHolder.file, this.headers, this.partName, customForm, this.withCredentials)
.subscribe(function (response) { return _this.onResponse(response, fileHolder); }, function (error) {
_this.onResponse(error, fileHolder);
_this.deleteFile(fileHolder);
});
}
else {
this.uploadFinished.emit(fileHolder);
}
};
NgxImageUploadComponent.decorators = [
{ type: Component, args: [{
selector: 'ds-ngx-image-upload',
template: "<div\n fileDrop\n [accept]=\"supportedExtensions\"\n (fileOver)=\"onFileOver($event)\"\n (fileDrop)=\"onFileChange($event)\"\n [ngClass]=\"cssClass\"\n [ngClass]=\"{'img-ul-file-is-over': fileOver}\" \n [ngStyle]=\"style?.layout\"\n>\n <p class=\"img-ul-file-too-large\" *ngIf=\"showFileTooLargeMessage\" [innerText]=\"fileTooLargeMessage\"></p>\n\n <div *ngIf=\"preview\" class=\"img-ul-container img-ul-hr-inline-group\" [ngStyle]=\"style?.previewPanel\">\n <!-- img -->\n <div\n class=\"img-ul-image\"\n *ngFor=\"let file of files\"\n (click)=\"previewFileClicked(file)\"\n >\n <!-- [ngStyle]=\"{'background-image': 'url('+ file.src +')'}\" -->\n <div class=\"img_div\"><img src={{file.src}} class=\"imgs\"></div>\n <div *ngIf=\"file.pending\" class=\"img-ul-loading-overlay\">\n <div class=\"img-ul-spinning-circle\"></div>\n </div>\n <div *ngIf=\"!file.pending\" \n [ngClass]=\"{'img-ul-disabled': disabled}\" \n class=\"img-ul-x-mark\" \n (click)=\"deleteFile(file)\">\n <span class=\"img-ul-close\"></span>\n </div>\n </div>\n </div>\n \n <div class=\"img-ul-file-upload img-ul-hr-inline-group\"> \n <label *ngIf=\"fileCounter != max\"\n class=\"img-ul-upload img-ul-button\" \n [ngStyle]=\"style?.selectButton\"\n [ngClass]=\"{'img-ul-disabled': disabled}\">\n <span [innerText]=\"buttonCaption\"></span>\n <input\n type=\"file\"\n [disabled]=\"disabled\"\n [accept]=\"supportedExtensions\"\n multiple (change)=\"onFileChange(input.files)\"\n #input>\n </label>\n </div>\n</div>",
styles: [".img-ul{--common-radius:3px;border-radius:var(--common-radius);position:relative;color:#9b9b9b;font-weight:400}.img-ul-x-mark{background-color:#000;border-radius:10px;color:#fff;cursor:pointer;height:20px;opacity:.7;text-align:center;width:20px;position:absolute;top:-8px;right:-10px}.img_div{position:relative;width:120px;height:120px;overflow:hidden}.imgs{max-width:200%;height:auto;position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);background:#000}.img-ul-file-is-over{border:var(--active-color) solid}.img-ul-hr-inline-group:after{clear:both;content:\"\";display:table}.img-ul-file-upload{display:inline-block;width:120px;height:120px;margin:10px;color:#000;text-align:center;line-height:120px}.img-ul-drag-box-msg{display:inline-block;font-weight:600;margin-left:12px}label.img-ul-button{border:1px dashed #d0d0d0;font-size:1em!important}label.img-ul-button input[type=file]{display:none;position:fixed;top:-99999px}.img-ul-clear{background-color:red;font-size:1em!important}.img-ul-clear:disabled{background-color:#ff6464;cursor:default}.img-ul-upload{background-color:var(--active-color);width:120px;height:120px}.img-ul-button{border:none;cursor:pointer;display:inline-block;float:left;font-size:1.25em;font-weight:500;text-transform:uppercase}.img-ul-button:active span{display:block;position:relative;top:1px}.img-ul-container{position:relative;display:inline-block}.img-ul-image{box-shadow:0 0 10px 2px #d0d0d0;background:center center/contain no-repeat;display:inline-block;float:left;height:120px;margin:6px;position:relative;width:120px}.img-ul-close{height:20px;opacity:.7;position:relative;width:20px}.img-ul-x-mark:hover .img-ul-close{opacity:1}.img-ul-close:after,.img-ul-close:before{background-color:#fff;border-radius:2px;content:'';height:10px;position:absolute;top:5px;width:2px}.img-ul-close:before{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.img-ul-close:after{-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.img-ul-x-mark.img-ul-disabled{display:none}.img-ul-loading-overlay{background-color:#000;bottom:0;left:0;opacity:.7;position:absolute;right:0;top:0}.img-ul-spinning-circle{height:30px;width:30px;margin:auto;position:absolute;top:0;left:0;bottom:0;right:0;border-radius:50%;border:3px solid rgba(255,255,255,0);border-top:3px solid #fff;border-right:3px solid #fff;-webkit-animation:2s cubic-bezier(.085,.625,.855,.36) infinite spinner;animation:2s cubic-bezier(.085,.625,.855,.36) infinite spinner}.img-ul-file-too-large{color:red}.img-ul-upload.img-ul-disabled{background-color:#86e9c9;cursor:default}.img-ul-upload.img-ul-disabled:active span{top:0}@-webkit-keyframes spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}"]
},] },
];
/** @nocollapse */
NgxImageUploadComponent.ctorParameters = function () { return [
{ type: NgxImageUploadService }
]; };
NgxImageUploadComponent.propDecorators = {
beforeUpload: [{ type: Input }],
buttonCaption: [{ type: Input }],
disabled: [{ type: Input }],
cssClass: [{ type: Input, args: ['class',] }],
clearButtonCaption: [{ type: Input }],
dropBoxMessage: [{ type: Input }],
fileTooLargeMessage: [{ type: Input }],
headers: [{ type: Input }],
max: [{ type: Input }],
maxFileSize: [{ type: Input }],
preview: [{ type: Input }],
partName: [{ type: Input }],
style: [{ type: Input }],
supportedExtensions: [{ type: Input, args: ['extensions',] }],
url: [{ type: Input }],
withCredentials: [{ type: Input }],
uploadedFiles: [{ type: Input }],
removed: [{ type: Output }],
uploadStateChanged: [{ type: Output }],
uploadFinished: [{ type: Output }],
previewClicked: [{ type: Output }],
inputElement: [{ type: ViewChild, args: ['input',] }]
};
return NgxImageUploadComponent;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var FileDropDirective = /** @class */ (function () {
function FileDropDirective() {
this.fileOver = new EventEmitter();
this.fileDrop = new EventEmitter();
}
/**
* @param {?} event
* @return {?}
*/
FileDropDirective.prototype.onDrop = /**
* @param {?} event
* @return {?}
*/
function (event) {
var /** @type {?} */ dataTransfer = FileDropDirective.getDataTransfer(event);
if (!FileDropDirective.hasFiles(dataTransfer.types)) {
return;
}
event.preventDefault();
var /** @type {?} */ files = this.filterFiles(dataTransfer.files);
event.preventDefault();
this.fileOver.emit(false);
this.fileDrop.emit(files);
};
/**
* @param {?} event
* @return {?}
*/
FileDropDirective.prototype.onDragLeave = /**
* @param {?} event
* @return {?}
*/
function (event) {
this.fileOver.emit(false);
};
/**
* @param {?} event
* @return {?}
*/
FileDropDirective.prototype.onDragOver = /**
* @param {?} event
* @return {?}
*/
function (event) {
var /** @type {?} */ dataTransfer = FileDropDirective.getDataTransfer(event);
if (!FileDropDirective.hasFiles(dataTransfer.types)) {
return;
}
dataTransfer.dropEffect = 'copy';
event.preventDefault();
this.fileOver.emit(true);
};
/**
* @param {?} files
* @return {?}
*/
FileDropDirective.prototype.filterFiles = /**
* @param {?} files
* @return {?}
*/
function (files) {
if (!this.accept || this.accept.length === 0) {
return files;
}
var /** @type {?} */ acceptedFiles = [];
for (var /** @type {?} */ i = 0; i < files.length; i++) {
for (var /** @type {?} */ j = 0; j < this.accept.length; j++) {
if (FileDropDirective.matchRule(this.accept[j], files[i].type)) {
acceptedFiles.push(files[i]);
break;
}
}
}
return acceptedFiles;
};
/**
* @param {?} event
* @return {?}
*/
FileDropDirective.getDataTransfer = /**
* @param {?} event
* @return {?}
*/
function (event) {
return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer;
};
/**
* @param {?} types
* @return {?}
*/
FileDropDirective.hasFiles = /**
* @param {?} types
* @return {?}
*/
function (types) {
if (!types) {
return false;
}
if (types.indexOf) {
return types.indexOf('Files') !== -1;
}
if (types.contains) {
return types.contains('Files');
}
return false;
};
/**
* @param {?} rule
* @param {?} candidate
* @return {?}
*/
FileDropDirective.matchRule = /**
* @param {?} rule
* @param {?} candidate
* @return {?}
*/
function (rule, candidate) {
return new RegExp("^" + rule.split("*").join(".*") + "$").test(candidate);
};
FileDropDirective.decorators = [
{ type: Directive, args: [{
selector: '[fileDrop]'
},] },
];
FileDropDirective.propDecorators = {
accept: [{ type: Input }],
fileOver: [{ type: Output }],
fileDrop: [{ type: Output }],
onDrop: [{ type: HostListener, args: ['drop', ['$event'],] }],
onDragLeave: [{ type: HostListener, args: ['dragleave', ['$event'],] }],
onDragOver: [{ type: HostListener, args: ['dragover', ['$event'],] }]
};
return FileDropDirective;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
var NgxImageUploadModule = /** @class */ (function () {
function NgxImageUploadModule() {
}
NgxImageUploadModule.decorators = [
{ type: NgModule, args: [{
imports: [
CommonModule,
HttpClientModule
],
declarations: [NgxImageUploadComponent, FileDropDirective],
exports: [NgxImageUploadComponent]
},] },
];
return NgxImageUploadModule;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
export { NgxImageUploadService, FileHolder, NgxImageUploadComponent, NgxImageUploadModule, FileDropDirective as ɵa };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWltYWdlLXVwbG9hZC5qcy5tYXAiLCJzb3VyY2VzIjpbIm5nOi8vbmd4LWltYWdlLXVwbG9hZC9saWIvbmd4LWltYWdlLXVwbG9hZC5zZXJ2aWNlLnRzIiwibmc6Ly9uZ3gtaW1hZ2UtdXBsb2FkL2xpYi9uZ3gtaW1hZ2UtdXBsb2FkLmNvbXBvbmVudC50cyIsIm5nOi8vbmd4LWltYWdlLXVwbG9hZC9maWxlLWRyb3AuZGlyZWN0aXZlLnRzIiwibmc6Ly9uZ3gtaW1hZ2UtdXBsb2FkL2xpYi9uZ3gtaW1hZ2UtdXBsb2FkLm1vZHVsZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBIdHRwQ2xpZW50LCBIdHRwSGVhZGVycyB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE9ic2VydmFibGUgfSBmcm9tICdyeGpzJztcblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgTmd4SW1hZ2VVcGxvYWRTZXJ2aWNlIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBodHRwOiBIdHRwQ2xpZW50KSB7XG4gIH1cblxuICBwdWJsaWMgcG9zdEltYWdlKHVybDogc3RyaW5nLCBpbWFnZTogRmlsZSwgaGVhZGVycz86IEh0dHBIZWFkZXJzIHwgeyBbbmFtZTogc3RyaW5nXTogYW55IH0sIHBhcnROYW1lOiBzdHJpbmcgPSAnaW1hZ2UnLCBjdXN0b21Gb3JtRGF0YT86IHsgW25hbWU6IHN0cmluZ106IGFueSB9LCB3aXRoQ3JlZGVudGlhbHM/OiBib29sZWFuKTogT2JzZXJ2YWJsZTxhbnk+IHtcbiAgICBpZiAoIXVybCB8fCB1cmwgPT09ICcnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ8Oowq/Ct8Oowq7CvsOnwr3CrnVybMOlwo/CgsOmwpXCsCcpO1xuICAgIH1cblxuICAgIC8vIGFkZCBjdXN0b20gZm9ybSBkYXRhXG4gICAgbGV0IGZvcm1EYXRhID0gbmV3IEZvcm1EYXRhKCk7XG4gICAgZm9yIChsZXQga2V5IGluIGN1c3RvbUZvcm1EYXRhKSB7XG4gICAgICBmb3JtRGF0YS5hcHBlbmQoa2V5LCBjdXN0b21Gb3JtRGF0YVtrZXldKTtcbiAgICB9XG4gICAgZm9ybURhdGEuYXBwZW5kKHBhcnROYW1lLCBpbWFnZSk7XG4gICAgcmV0dXJuIHRoaXMuaHR0cC5wb3N0KHVybCwgZm9ybURhdGEsIHsgaGVhZGVyczogaGVhZGVycywgd2l0aENyZWRlbnRpYWxzOiB3aXRoQ3JlZGVudGlhbHMgfSk7XG4gIH1cbn1cbiIsImltcG9ydCB7IENvbXBvbmVudCwgT25Jbml0LCBPbkNoYW5nZXMsIElucHV0LCBPdXRwdXQsIEV2ZW50RW1pdHRlciwgRWxlbWVudFJlZiwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7IFVwbG9hZE1ldGFkYXRhIH0gZnJvbSAnLi9iZWZvcmUtdXBsb2FkLmludGVyZmFjZSc7XG5pbXBvcnQgeyBOZ3hJbWFnZVVwbG9hZFNlcnZpY2UgfSBmcm9tICcuL25neC1pbWFnZS11cGxvYWQuc2VydmljZSc7XG5pbXBvcnQgeyBTdHlsZSB9IGZyb20gJy4vc3R5bGUnO1xuXG5leHBvcnQgY2xhc3MgRmlsZUhvbGRlciB7XG4gIHB1YmxpYyBwZW5kaW5nOiBib29sZWFuID0gZmFsc2U7XG4gIHB1YmxpYyBzZXJ2ZXJSZXNwb25zZTogeyBzdGF0dXM6IG51bWJlciwgcmVzcG9uc2U6IGFueSB9O1xuXG4gIGNvbnN0cnVjdG9yKHB1YmxpYyBzcmM6IHN0cmluZywgcHVibGljIGZpbGU6IEZpbGUpIHtcbiAgfVxufVxuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdkcy1uZ3gtaW1hZ2UtdXBsb2FkJyxcbiAgdGVtcGxhdGU6IGA8ZGl2XG4gICAgIGZpbGVEcm9wXG4gICAgIFthY2NlcHRdPVwic3VwcG9ydGVkRXh0ZW5zaW9uc1wiXG4gICAgIChmaWxlT3Zlcik9XCJvbkZpbGVPdmVyKCRldmVudClcIlxuICAgICAoZmlsZURyb3ApPVwib25GaWxlQ2hhbmdlKCRldmVudClcIlxuICAgICBbbmdDbGFzc109XCJjc3NDbGFzc1wiXG4gICAgIFtuZ0NsYXNzXT1cInsnaW1nLXVsLWZpbGUtaXMtb3Zlcic6IGZpbGVPdmVyfVwiICAgICBcbiAgICAgW25nU3R5bGVdPVwic3R5bGU/LmxheW91dFwiXG4+XG4gIDxwIGNsYXNzPVwiaW1nLXVsLWZpbGUtdG9vLWxhcmdlXCIgKm5nSWY9XCJzaG93RmlsZVRvb0xhcmdlTWVzc2FnZVwiIFtpbm5lclRleHRdPVwiZmlsZVRvb0xhcmdlTWVzc2FnZVwiPjwvcD5cblxuICA8ZGl2ICpuZ0lmPVwicHJldmlld1wiIGNsYXNzPVwiaW1nLXVsLWNvbnRhaW5lciBpbWctdWwtaHItaW5saW5lLWdyb3VwXCIgW25nU3R5bGVdPVwic3R5bGU/LnByZXZpZXdQYW5lbFwiPlxuICAgIDwhLS0gaW1nIC0tPlxuICAgIDxkaXZcbiAgICAgIGNsYXNzPVwiaW1nLXVsLWltYWdlXCJcbiAgICAgICpuZ0Zvcj1cImxldCBmaWxlIG9mIGZpbGVzXCJcbiAgICAgIChjbGljayk9XCJwcmV2aWV3RmlsZUNsaWNrZWQoZmlsZSlcIlxuICAgID5cbiAgICAgIDwhLS0gW25nU3R5bGVdPVwieydiYWNrZ3JvdW5kLWltYWdlJzogJ3VybCgnKyBmaWxlLnNyYyArJyknfVwiIC0tPlxuICAgICAgPGRpdiBjbGFzcz1cImltZ19kaXZcIj48aW1nIHNyYz17e2ZpbGUuc3JjfX0gY2xhc3M9XCJpbWdzXCI+PC9kaXY+XG4gICAgICA8ZGl2ICpuZ0lmPVwiZmlsZS5wZW5kaW5nXCIgY2xhc3M9XCJpbWctdWwtbG9hZGluZy1vdmVybGF5XCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJpbWctdWwtc3Bpbm5pbmctY2lyY2xlXCI+PC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXYgKm5nSWY9XCIhZmlsZS5wZW5kaW5nXCIgXG4gICAgICAgIFtuZ0NsYXNzXT1cInsnaW1nLXVsLWRpc2FibGVkJzogZGlzYWJsZWR9XCIgXG4gICAgICAgIGNsYXNzPVwiaW1nLXVsLXgtbWFya1wiIFxuICAgICAgICAoY2xpY2spPVwiZGVsZXRlRmlsZShmaWxlKVwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImltZy11bC1jbG9zZVwiPjwvc3Bhbj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cbiAgXG4gIDxkaXYgY2xhc3M9XCJpbWctdWwtZmlsZS11cGxvYWQgaW1nLXVsLWhyLWlubGluZS1ncm91cFwiPiAgICBcbiAgICA8bGFiZWwgKm5nSWY9XCJmaWxlQ291bnRlciAhPSBtYXhcIlxuICAgICAgY2xhc3M9XCJpbWctdWwtdXBsb2FkIGltZy11bC1idXR0b25cIiBcbiAgICAgIFtuZ1N0eWxlXT1cInN0eWxlPy5zZWxlY3RCdXR0b25cIlxuICAgICAgW25nQ2xhc3NdPVwieydpbWctdWwtZGlzYWJsZWQnOiBkaXNhYmxlZH1cIj5cbiAgICAgIDxzcGFuIFtpbm5lclRleHRdPVwiYnV0dG9uQ2FwdGlvblwiPjwvc3Bhbj5cbiAgICAgIDxpbnB1dFxuICAgICAgICB0eXBlPVwiZmlsZVwiXG4gICAgICAgIFtkaXNhYmxlZF09XCJkaXNhYmxlZFwiXG4gICAgICAgIFthY2NlcHRdPVwic3VwcG9ydGVkRXh0ZW5zaW9uc1wiXG4gICAgICAgIG11bHRpcGxlIChjaGFuZ2UpPVwib25GaWxlQ2hhbmdlKGlucHV0LmZpbGVzKVwiXG4gICAgICAgICNpbnB1dD5cbiAgICA8L2xhYmVsPlxuICA8L2Rpdj5cbjwvZGl2PmAsXG4gIHN0eWxlczogW2AuaW1nLXVsey0tY29tbW9uLXJhZGl1czozcHg7Ym9yZGVyLXJhZGl1czp2YXIoLS1jb21tb24tcmFkaXVzKTtwb3NpdGlvbjpyZWxhdGl2ZTtjb2xvcjojOWI5YjliO2ZvbnQtd2VpZ2h0OjQwMH0uaW1nLXVsLXgtbWFya3tiYWNrZ3JvdW5kLWNvbG9yOiMwMDA7Ym9yZGVyLXJhZGl1czoxMHB4O2NvbG9yOiNmZmY7Y3Vyc29yOnBvaW50ZXI7aGVpZ2h0OjIwcHg7b3BhY2l0eTouNzt0ZXh0LWFsaWduOmNlbnRlcjt3aWR0aDoyMHB4O3Bvc2l0aW9uOmFic29sdXRlO3RvcDotOHB4O3JpZ2h0Oi0xMHB4fS5pbWdfZGl2e3Bvc2l0aW9uOnJlbGF0aXZlO3dpZHRoOjEyMHB4O2hlaWdodDoxMjBweDtvdmVyZmxvdzpoaWRkZW59LmltZ3N7bWF4LXdpZHRoOjIwMCU7aGVpZ2h0OmF1dG87cG9zaXRpb246YWJzb2x1dGU7dG9wOjUwJTtsZWZ0OjUwJTstd2Via2l0LXRyYW5zZm9ybTp0cmFuc2xhdGUoLTUwJSwtNTAlKTt0cmFuc2Zvcm06dHJhbnNsYXRlKC01MCUsLTUwJSk7YmFja2dyb3VuZDojMDAwfS5pbWctdWwtZmlsZS1pcy1vdmVye2JvcmRlcjp2YXIoLS1hY3RpdmUtY29sb3IpIHNvbGlkfS5pbWctdWwtaHItaW5saW5lLWdyb3VwOmFmdGVye2NsZWFyOmJvdGg7Y29udGVudDpcIlwiO2Rpc3BsYXk6dGFibGV9LmltZy11bC1maWxlLXVwbG9hZHtkaXNwbGF5OmlubGluZS1ibG9jazt3aWR0aDoxMjBweDtoZWlnaHQ6MTIwcHg7bWFyZ2luOjEwcHg7Y29sb3I6IzAwMDt0ZXh0LWFsaWduOmNlbnRlcjtsaW5lLWhlaWdodDoxMjBweH0uaW1nLXVsLWRyYWctYm94LW1zZ3tkaXNwbGF5OmlubGluZS1ibG9jaztmb250LXdlaWdodDo2MDA7bWFyZ2luLWxlZnQ6MTJweH1sYWJlbC5pbWctdWwtYnV0dG9ue2JvcmRlcjoxcHggZGFzaGVkICNkMGQwZDA7Zm9udC1zaXplOjFlbSFpbXBvcnRhbnR9bGFiZWwuaW1nLXVsLWJ1dHRvbiBpbnB1dFt0eXBlPWZpbGVde2Rpc3BsYXk6bm9uZTtwb3NpdGlvbjpmaXhlZDt0b3A6LTk5OTk5cHh9LmltZy11bC1jbGVhcntiYWNrZ3JvdW5kLWNvbG9yOnJlZDtmb250LXNpemU6MWVtIWltcG9ydGFudH0uaW1nLXVsLWNsZWFyOmRpc2FibGVke2JhY2tncm91bmQtY29sb3I6I2ZmNjQ2NDtjdXJzb3I6ZGVmYXVsdH0uaW1nLXVsLXVwbG9hZHtiYWNrZ3JvdW5kLWNvbG9yOnZhcigtLWFjdGl2ZS1jb2xvcik7d2lkdGg6MTIwcHg7aGVpZ2h0OjEyMHB4fS5pbWctdWwtYnV0dG9ue2JvcmRlcjpub25lO2N1cnNvcjpwb2ludGVyO2Rpc3BsYXk6aW5saW5lLWJsb2NrO2Zsb2F0OmxlZnQ7Zm9udC1zaXplOjEuMjVlbTtmb250LXdlaWdodDo1MDA7dGV4dC10cmFuc2Zvcm06dXBwZXJjYXNlfS5pbWctdWwtYnV0dG9uOmFjdGl2ZSBzcGFue2Rpc3BsYXk6YmxvY2s7cG9zaXRpb246cmVsYXRpdmU7dG9wOjFweH0uaW1nLXVsLWNvbnRhaW5lcntwb3NpdGlvbjpyZWxhdGl2ZTtkaXNwbGF5OmlubGluZS1ibG9ja30uaW1nLXVsLWltYWdle2JveC1zaGFkb3c6MCAwIDEwcHggMnB4ICNkMGQwZDA7YmFja2dyb3VuZDpjZW50ZXIgY2VudGVyL2NvbnRhaW4gbm8tcmVwZWF0O2Rpc3BsYXk6aW5saW5lLWJsb2NrO2Zsb2F0OmxlZnQ7aGVpZ2h0OjEyMHB4O21hcmdpbjo2cHg7cG9zaXRpb246cmVsYXRpdmU7d2lkdGg6MTIwcHh9LmltZy11bC1jbG9zZXtoZWlnaHQ6MjBweDtvcGFjaXR5Oi43O3Bvc2l0aW9uOnJlbGF0aXZlO3dpZHRoOjIwcHh9LmltZy11bC14LW1hcms6aG92ZXIgLmltZy11bC1jbG9zZXtvcGFjaXR5OjF9LmltZy11bC1jbG9zZTphZnRlciwuaW1nLXVsLWNsb3NlOmJlZm9yZXtiYWNrZ3JvdW5kLWNvbG9yOiNmZmY7Ym9yZGVyLXJhZGl1czoycHg7Y29udGVudDonJztoZWlnaHQ6MTBweDtwb3NpdGlvbjphYnNvbHV0ZTt0b3A6NXB4O3dpZHRoOjJweH0uaW1nLXVsLWNsb3NlOmJlZm9yZXstd2Via2l0LXRyYW5zZm9ybTpyb3RhdGUoNDVkZWcpO3RyYW5zZm9ybTpyb3RhdGUoNDVkZWcpfS5pbWctdWwtY2xvc2U6YWZ0ZXJ7LXdlYmtpdC10cmFuc2Zvcm06cm90YXRlKC00NWRlZyk7dHJhbnNmb3JtOnJvdGF0ZSgtNDVkZWcpfS5pbWctdWwteC1tYXJrLmltZy11bC1kaXNhYmxlZHtkaXNwbGF5Om5vbmV9LmltZy11bC1sb2FkaW5nLW92ZXJsYXl7YmFja2dyb3VuZC1jb2xvcjojMDAwO2JvdHRvbTowO2xlZnQ6MDtvcGFjaXR5Oi43O3Bvc2l0aW9uOmFic29sdXRlO3JpZ2h0OjA7dG9wOjB9LmltZy11bC1zcGlubmluZy1jaXJjbGV7aGVpZ2h0OjMwcHg7d2lkdGg6MzBweDttYXJnaW46YXV0bztwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtsZWZ0OjA7Ym90dG9tOjA7cmlnaHQ6MDtib3JkZXItcmFkaXVzOjUwJTtib3JkZXI6M3B4IHNvbGlkIHJnYmEoMjU1LDI1NSwyNTUsMCk7Ym9yZGVyLXRvcDozcHggc29saWQgI2ZmZjtib3JkZXItcmlnaHQ6M3B4IHNvbGlkICNmZmY7LXdlYmtpdC1hbmltYXRpb246MnMgY3ViaWMtYmV6aWVyKC4wODUsLjYyNSwuODU1LC4zNikgaW5maW5pdGUgc3Bpbm5lcjthbmltYXRpb246MnMgY3ViaWMtYmV6aWVyKC4wODUsLjYyNSwuODU1LC4zNikgaW5maW5pdGUgc3Bpbm5lcn0uaW1nLXVsLWZpbGUtdG9vLWxhcmdle2NvbG9yOnJlZH0uaW1nLXVsLXVwbG9hZC5pbWctdWwtZGlzYWJsZWR7YmFja2dyb3VuZC1jb2xvcjojODZlOWM5O2N1cnNvcjpkZWZhdWx0fS5pbWctdWwtdXBsb2FkLmltZy11bC1kaXNhYmxlZDphY3RpdmUgc3Bhbnt0b3A6MH1ALXdlYmtpdC1rZXlmcmFtZXMgc3Bpbm5lcnswJXstd2Via2l0LXRyYW5zZm9ybTpyb3RhdGUoMCk7dHJhbnNmb3JtOnJvdGF0ZSgwKX0xMDAley13ZWJraXQtdHJhbnNmb3JtOnJvdGF0ZSgzNjBkZWcpO3RyYW5zZm9ybTpyb3RhdGUoMzYwZGVnKX19QGtleWZyYW1lcyBzcGlubmVyezAley13ZWJraXQtdHJhbnNmb3JtOnJvdGF0ZSgwKTt0cmFuc2Zvcm06cm90YXRlKDApfTEwMCV7LXdlYmtpdC10cmFuc2Zvcm06cm90YXRlKDM2MGRlZyk7dHJhbnNmb3JtOnJvdGF0ZSgzNjBkZWcpfX1gXVxufSlcbmV4cG9ydCBjbGFzcyBOZ3hJbWFnZVVwbG9hZENvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzIHtcblxuICBmaWxlczogRmlsZUhvbGRlcltdID0gW107XG4gIGZpbGVDb3VudGVyOiBudW1iZXIgPSAwO1xuICBmaWxlT3ZlcjogYm9vbGVhbiA9IGZhbHNlO1xuICBzaG93RmlsZVRvb0xhcmdlTWVzc2FnZTogYm9vbGVhbiA9IGZhbHNlO1xuXG4gIEBJbnB1dCgpIGJlZm9yZVVwbG9hZDogKHBhcmFtOiBVcGxvYWRNZXRhZGF0YSkgPT4gVXBsb2FkTWV0YWRhdGEgfCBQcm9taXNlPFVwbG9hZE1ldGFkYXRhPiA9IGRhdGEgPT4gZGF0YTtcbiAgQElucHV0KCkgYnV0dG9uQ2FwdGlvbiA9ICfDpsK3wrvDpcKKwqDDpcKbwr7Dp8KJwocnO1xuICBASW5wdXQoKSBkaXNhYmxlZCA9IGZhbHNlO1xuICBASW5wdXQoJ2NsYXNzJykgY3NzQ2xhc3MgPSAnaW1nLXVsJztcbiAgQElucHV0KCkgY2xlYXJCdXR0b25DYXB0aW9uID0gJ8OmwrjChcOpwpnCpCc7XG4gIEBJbnB1dCgpIGRyb3BCb3hNZXNzYWdlID0gJ8OmwovClsOmwovCvcOlwpvCvsOnwonCh8OlwojCsMOmwq3CpMOlwqTChCc7XG4gIEBJbnB1dCgpIGZpbGVUb29MYXJnZU1lc3NhZ2U7XG4gIEBJbnB1dCgpIGhlYWRlcnM6IEhlYWRlcnMgfCB7IFtuYW1lOiBzdHJpbmddOiBhbnkgfTtcbiAgQElucHV0KCkgbWF4ID0gMTAwO1xuICBASW5wdXQoKSBtYXhGaWxlU2l6ZTogbnVtYmVyO1xuICBASW5wdXQoKSBwcmV2aWV3ID0gdHJ1ZTtcbiAgQElucHV0KCkgcGFydE5hbWU6IHN0cmluZztcbiAgQElucHV0KCkgc3R5bGU6IFN0eWxlO1xuICBASW5wdXQoJ2V4dGVuc2lvbnMnKSBzdXBwb3J0ZWRFeHRlbnNpb25zOiBzdHJpbmdbXTtcbiAgQElucHV0KCkgdXJsOiBzdHJpbmc7XG4gIEBJbnB1dCgpIHdpdGhDcmVkZW50aWFscyA9IGZhbHNlO1xuICBASW5wdXQoKSB1cGxvYWRlZEZpbGVzOiBzdHJpbmdbXSB8IEFycmF5PHsgdXJsOiBzdHJpbmcsIGZpbGVOYW1lOiBzdHJpbmcsIGJsb2I/OiBCbG9iIH0+ID0gW107XG4gIEBPdXRwdXQoKSByZW1vdmVkID0gbmV3IEV2ZW50RW1pdHRlcjxGaWxlSG9sZGVyPigpO1xuICBAT3V0cHV0KCkgdXBsb2FkU3RhdGVDaGFuZ2VkID0gbmV3IEV2ZW50RW1pdHRlcjxib29sZWFuPigpO1xuICBAT3V0cHV0KCkgdXBsb2FkRmluaXNoZWQgPSBuZXcgRXZlbnRFbWl0dGVyPEZpbGVIb2xkZXI+KCk7XG4gIEBPdXRwdXQoKSBwcmV2aWV3Q2xpY2tlZCA9IG5ldyBFdmVudEVtaXR0ZXI8RmlsZUhvbGRlcj4oKTtcblxuICBAVmlld0NoaWxkKCdpbnB1dCcpXG4gIHByaXZhdGUgaW5wdXRFbGVtZW50OiBFbGVtZW50UmVmO1xuICBwcml2YXRlIHBlbmRpbmdGaWxlc0NvdW50ZXI6IG51bWJlciA9IDA7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBpbWFnZVNlcnZpY2U6IE5neEltYWdlVXBsb2FkU2VydmljZSkgeyB9XG5cbiAgbmdPbkluaXQoKSB7XG4gICAgaWYgKCF0aGlzLmZpbGVUb29MYXJnZU1lc3NhZ2UpIHtcbiAgICAgIHRoaXMuZmlsZVRvb0xhcmdlTWVzc2FnZSA9ICfDpcKbwr7Dp8KJwofDpcKkwqrDpcKkwqfDpsKXwqDDpsKzwpXDpMK4worDpMK8wqAuJyArICh0aGlzLm1heEZpbGVTaXplID8gKCcgw6bCnMKAw6XCpMKnw6XChcKBw6jCrsK4ICcgKyB0aGlzLm1heEZpbGVTaXplIC8gMTAyNCkgKyAnS2lCLicgOiAnJyk7XG4gICAgfVxuICAgIHRoaXMuc3VwcG9ydGVkRXh0ZW5zaW9ucyA9IHRoaXMuc3VwcG9ydGVkRXh0ZW5zaW9ucyA/IHRoaXMuc3VwcG9ydGVkRXh0ZW5zaW9ucy5tYXAoKGV4dCkgPT4gJ2ltYWdlLycgKyBleHQpIDogWydpbWFnZS8qJ107XG4gIH1cblxuICBkZWxldGVBbGwoKSB7XG4gICAgdGhpcy5maWxlcy5mb3JFYWNoKGYgPT4gdGhpcy5yZW1vdmVkLmVtaXQoZikpO1xuICAgIHRoaXMuZmlsZXMgPSBbXTtcbiAgICB0aGlzLmZpbGVDb3VudGVyID0gMDtcbiAgICBpZiAodGhpcy5pbnB1dEVsZW1lbnQpIHtcbiAgICAgIHRoaXMuaW5wdXRFbGVtZW50Lm5hdGl2ZUVsZW1lbnQudmFsdWUgPSAnJztcbiAgICB9XG4gIH1cblxuICBkZWxldGVGaWxlKGZpbGU6IEZpbGVIb2xkZXIpOiB2b2lkIHtcbiAgICBsZXQgaW5kZXggPSB0aGlzLmZpbGVzLmluZGV4T2YoZmlsZSk7XG4gICAgdGhpcy5maWxlcy5zcGxpY2UoaW5kZXgsIDEpO1xuICAgIHRoaXMuZmlsZUNvdW50ZXItLTtcblxuICAgIGlmICh0aGlzLmlucHV0RWxlbWVudCkge1xuICAgICAgdGhpcy5pbnB1dEVsZW1lbnQubmF0aXZlRWxlbWVudC52YWx1ZSA9ICcnO1xuICAgIH1cbiAgICB0aGlzLnJlbW92ZWQuZW1pdChmaWxlKTtcbiAgfVxuXG4gIHByZXZpZXdGaWxlQ2xpY2tlZChmaWxlOiBGaWxlSG9sZGVyKSB7XG4gICAgdGhpcy5wcmV2aWV3Q2xpY2tlZC5lbWl0KGZpbGUpO1xuICB9XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlcykge1xuICAgIGlmIChjaGFuZ2VzLnVwbG9hZGVkRmlsZXMgJiYgY2hhbmdlcy51cGxvYWRlZEZpbGVzLmN1cnJlbnRWYWx1ZSAmJiBjaGFuZ2VzLnVwbG9hZGVkRmlsZXMuY3VycmVudFZhbHVlLmxlbmd0aCA+IDApIHtcbiAgICAgIHRoaXMucHJvY2Vzc1VwbG9hZGVkRmlsZXMoKTtcbiAgICB9XG4gIH1cblxuICBvbkZpbGVDaGFuZ2UoZmlsZXM6IEZpbGVMaXN0KSB7XG4gICAgaWYgKHRoaXMuZGlzYWJsZWQpIHJldHVybjtcblxuICAgIC8vIGxldCByZW1haW5pbmdTbG90cyA9IDA7XG4gICAgbGV0IHJlbWFpbmluZ1Nsb3RzID0gdGhpcy5jb3VudFJlbWFpbmluZ1Nsb3RzKCk7XG4gICAgbGV0IGZpbGVzVG9VcGxvYWROdW0gPSBmaWxlcy5sZW5ndGggPiByZW1haW5pbmdTbG90cyA/IHJlbWFpbmluZ1Nsb3RzIDogZmlsZXMubGVuZ3RoO1xuXG4gICAgaWYgKHRoaXMudXJsICYmIGZpbGVzVG9VcGxvYWROdW0gIT0gMCkge1xuICAgICAgdGhpcy51cGxvYWRTdGF0ZUNoYW5nZWQuZW1pdCh0cnVlKTtcbiAgICB9XG5cbiAgICB0aGlzLmZpbGVDb3VudGVyICs9IGZpbGVzVG9VcGxvYWROdW07XG4gICAgdGhpcy5zaG93RmlsZVRvb0xhcmdlTWVzc2FnZSA9IGZhbHNlO1xuICAgIHRoaXMudXBsb2FkRmlsZXMoZmlsZXMsIGZpbGVzVG9VcGxvYWROdW0pO1xuICB9XG5cbiAgb25GaWxlT3ZlciA9IChpc092ZXIpID0+IHRoaXMuZmlsZU92ZXIgPSBpc092ZXI7XG5cbiAgcHJpdmF0ZSBjb3VudFJlbWFpbmluZ1Nsb3RzID0gKCkgPT4gdGhpcy5tYXggLSB0aGlzLmZpbGVDb3VudGVyO1xuXG4gIHByaXZhdGUgb25SZXNwb25zZShyZXNwb25zZTogUmVzcG9uc2UsIGZpbGVIb2xkZXI6IEZpbGVIb2xkZXIpIHtcbiAgICBmaWxlSG9sZGVyLnNlcnZlclJlc3BvbnNlID0geyBzdGF0dXM6IHJlc3BvbnNlLnN0YXR1cywgcmVzcG9uc2UgfTtcbiAgICBmaWxlSG9sZGVyLnBlbmRpbmcgPSBmYWxzZTtcblxuICAgIHRoaXMudXBsb2FkRmluaXNoZWQuZW1pdChmaWxlSG9sZGVyKTtcblxuICAgIGlmICgtLXRoaXMucGVuZGluZ0ZpbGVzQ291bnRlciA9PSAwKSB7XG4gICAgICB0aGlzLnVwbG9hZFN0YXRlQ2hhbmdlZC5lbWl0KGZhbHNlKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHByb2Nlc3NVcGxvYWRlZEZpbGVzKCkge1xuICAgIGlmICh0eXBlb2YgdGhpcy51cGxvYWRlZEZpbGVzID09PSAnc3RyaW5nJykge1xuICAgICAgbGV0IGRhdGE6IGFueSA9IHRoaXMudXBsb2FkZWRGaWxlcztcblxuICAgICAgdGhpcy5maWxlQ291bnRlciA9IDE7XG4gICAgICB0aGlzLm1heCA9IDE7XG5cbiAgICAgIGxldCBmaWxlQmxvYjogQmxvYixcbiAgICAgICAgZmlsZTogRmlsZSxcbiAgICAgICAgZmlsZVVybDogc3RyaW5nO1xuXG4gICAgICBpZiAoZGF0YSBpbnN0YW5jZW9mIE9iamVjdCkge1xuICAgICAgICBmaWxlVXJsID0gZGF0YS51cmw7XG4gICAgICAgIGZpbGVCbG9iID0gKGRhdGEuYmxvYikgPyBkYXRhLmJsb2IgOiBuZXcgQmxvYihbZGF0YV0pO1xuICAgICAgICBmaWxlID0gbmV3IEZpbGUoW2ZpbGVCbG9iXSwgZGF0YS5maWxlTmFtZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmaWxlVXJsID0gZGF0YTtcbiAgICAgICAgZmlsZUJsb2IgPSBuZXcgQmxvYihbZmlsZVVybF0pO1xuICAgICAgICBmaWxlID0gbmV3IEZpbGUoW2ZpbGVCbG9iXSwgZmlsZVVybCk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuZmlsZXMgPSBbXTtcbiAgICAgIHRoaXMuZmlsZXMucHVzaChuZXcgRmlsZUhvbGRlcihmaWxlVXJsLCBmaWxlKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMubWF4ID0gdGhpcy5tYXggLSBPYmplY3Qua2V5cyh0aGlzLnVwbG9hZGVkRmlsZXMpLmxlbmd0aDtcbiAgICAgIGZvciAoY29uc3QgaSBpbiB0aGlzLnVwbG9hZGVkRmlsZXMpIHtcbiAgICAgICAgbGV0IGRhdGE6IGFueSA9IHRoaXMudXBsb2FkZWRGaWxlc1tpXTtcblxuICAgICAgICBsZXQgZmlsZUJsb2I6IEJsb2IsXG4gICAgICAgICAgZmlsZTogRmlsZSxcbiAgICAgICAgICBmaWxlVXJsOiBzdHJpbmc7XG5cbiAgICAgICAgaWYgKGRhdGEgaW5zdGFuY2VvZiBPYmplY3QpIHtcbiAgICAgICAgICBmaWxlVXJsID0gZGF0YS51cmw7XG4gICAgICAgICAgZmlsZUJsb2IgPSAoZGF0YS5ibG9iKSA/IGRhdGEuYmxvYiA6IG5ldyBCbG9iKFtkYXRhXSk7XG4gICAgICAgICAgZmlsZSA9IG5ldyBGaWxlKFtmaWxlQmxvYl0sIGRhdGEuZmlsZU5hbWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGZpbGVVcmwgPSBkYXRhO1xuICAgICAgICAgIGZpbGVCbG9iID0gbmV3IEJsb2IoW2ZpbGVVcmxdKTtcbiAgICAgICAgICBmaWxlID0gbmV3IEZpbGUoW2ZpbGVCbG9iXSwgZmlsZVVybCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmZpbGVzLnB1c2gobmV3IEZpbGVIb2xkZXIoZmlsZVVybCwgZmlsZSkpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgdXBsb2FkRmlsZXMoZmlsZXM6IEZpbGVMaXN0LCBmaWxlc1RvVXBsb2FkTnVtOiBudW1iZXIpIHtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGZpbGVzVG9VcGxvYWROdW07IGkrKykge1xuICAgICAgY29uc3QgZmlsZSA9IGZpbGVzW2ldO1xuXG4gICAgICBpZiAodGhpcy5tYXhGaWxlU2l6ZSAmJiBmaWxlLnNpemUgPiB0aGlzLm1heEZpbGVTaXplKSB7XG4gICAgICAgIHRoaXMuZmlsZUNvdW50ZXItLTtcbiAgICAgICAgdGhpcy5pbnB1dEVsZW1lbnQubmF0aXZlRWxlbWVudC52YWx1ZSA9ICcnO1xuICAgICAgICB0aGlzLnNob3dGaWxlVG9vTGFyZ2VNZXNzYWdlID0gdHJ1ZTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGJlZm9yZVVwbG9hZFJlc3VsdDogVXBsb2FkTWV0YWRhdGEgPSBhd2FpdCB0aGlzLmJlZm9yZVVwbG9hZCh7IGZpbGUsIHVybDogdGhpcy51cmwsIGFib3J0OiBmYWxzZSB9KTtcblxuICAgICAgaWYgKGJlZm9yZVVwbG9hZFJlc3VsdC5hYm9ydCkge1xuICAgICAgICB0aGlzLmZpbGVDb3VudGVyLS07XG4gICAgICAgIHRoaXMuaW5wdXRFbGVtZW50Lm5hdGl2ZUVsZW1lbnQudmFsdWUgPSAnJztcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGltZyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2ltZycpO1xuICAgICAgaW1nLnNyYyA9IHdpbmRvdy5VUkwuY3JlYXRlT2JqZWN0VVJMKGJlZm9yZVVwbG9hZFJlc3VsdC5maWxlKTtcblxuICAgICAgY29uc3QgcmVhZGVyID0gbmV3IEZpbGVSZWFkZXIoKTtcbiAgICAgIHJlYWRlci5hZGRFdmVudExpc3RlbmVyKCdsb2FkJywgKGV2ZW50OiBhbnkpID0+IHtcbiAgICAgICAgY29uc3QgZmlsZUhvbGRlcjogRmlsZUhvbGRlciA9IG5ldyBGaWxlSG9sZGVyKGV2ZW50LnRhcmdldC5yZXN1bHQsIGJlZm9yZVVwbG9hZFJlc3VsdC5maWxlKTtcbiAgICAgICAgdGhpcy51cGxvYWRTaW5nbGVGaWxlKGZpbGVIb2xkZXIsIGJlZm9yZVVwbG9hZFJlc3VsdC51cmwsIGJlZm9yZVVwbG9hZFJlc3VsdC5mb3JtRGF0YSk7XG4gICAgICAgIHRoaXMuZmlsZXMucHVzaChmaWxlSG9sZGVyKTtcbiAgICAgIH0sIGZhbHNlKTtcbiAgICAgIHJlYWRlci5yZWFkQXNEYXRhVVJMKGJlZm9yZVVwbG9hZFJlc3VsdC5maWxlKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHVwbG9hZFNpbmdsZUZpbGUoZmlsZUhvbGRlcjogRmlsZUhvbGRlciwgdXJsID0gdGhpcy51cmwsIGN1c3RvbUZvcm0/OiB7IFtuYW1lOiBzdHJpbmddOiBhbnkgfSkge1xuICAgIGlmICh1cmwpIHtcbiAgICAgIHRoaXMucGVuZGluZ0ZpbGVzQ291bnRlcisrO1xuICAgICAgZmlsZUhvbGRlci5wZW5kaW5nID0gdHJ1ZTtcblxuICAgICAgdGhpcy5pbWFnZVNlcnZpY2VcbiAgICAgICAgLnBvc3RJbWFnZSh1cmwsIGZpbGVIb2xkZXIuZmlsZSwgdGhpcy5oZWFkZXJzLCB0aGlzLnBhcnROYW1lLCBjdXN0b21Gb3JtLCB0aGlzLndpdGhDcmVkZW50aWFscylcbiAgICAgICAgLnN1YnNjcmliZShcbiAgICAgICAgICByZXNwb25zZSA9PiB0aGlzLm9uUmVzcG9uc2UocmVzcG9uc2UsIGZpbGVIb2xkZXIpLFxuICAgICAgICAgIGVycm9yID0+IHtcbiAgICAgICAgICAgIHRoaXMub25SZXNwb25zZShlcnJvciwgZmlsZUhvbGRlcik7XG4gICAgICAgICAgICB0aGlzLmRlbGV0ZUZpbGUoZmlsZUhvbGRlcik7XG4gICAgICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMudXBsb2FkRmluaXNoZWQuZW1pdChmaWxlSG9sZGVyKTtcbiAgICB9XG4gIH1cblxufVxuIiwiaW1wb3J0IHsgRGlyZWN0aXZlLCBFdmVudEVtaXR0ZXIsIEhvc3RMaXN0ZW5lciwgSW5wdXQsIE91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5ARGlyZWN0aXZlKHtcbiAgc2VsZWN0b3I6ICdbZmlsZURyb3BdJ1xufSlcbmV4cG9ydCBjbGFzcyBGaWxlRHJvcERpcmVjdGl2ZSB7XG4gIEBJbnB1dCgpIGFjY2VwdDogc3RyaW5nW107XG4gIEBPdXRwdXQoKSBmaWxlT3ZlcjogRXZlbnRFbWl0dGVyPGJvb2xlYW4+ID0gbmV3IEV2ZW50RW1pdHRlcjxib29sZWFuPigpO1xuICBAT3V0cHV0KCkgZmlsZURyb3A6IEV2ZW50RW1pdHRlcjxGaWxlTGlzdD4gPSBuZXcgRXZlbnRFbWl0dGVyPEZpbGVMaXN0PigpO1xuXG4gIEBIb3N0TGlzdGVuZXIoJ2Ryb3AnLCBbJyRldmVudCddKVxuICBvbkRyb3AoZXZlbnQ6IGFueSkge1xuICAgIGxldCBkYXRhVHJhbnNmZXIgPSBGaWxlRHJvcERpcmVjdGl2ZS5nZXREYXRhVHJhbnNmZXIoZXZlbnQpO1xuXG4gICAgaWYgKCFGaWxlRHJvcERpcmVjdGl2ZS5oYXNGaWxlcyhkYXRhVHJhbnNmZXIudHlwZXMpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcblxuICAgIGxldCBmaWxlcyA9IHRoaXMuZmlsdGVyRmlsZXMoZGF0YVRyYW5zZmVyLmZpbGVzKTtcblxuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgdGhpcy5maWxlT3Zlci5lbWl0KGZhbHNlKTtcbiAgICB0aGlzLmZpbGVEcm9wLmVtaXQoZmlsZXMpO1xuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignZHJhZ2xlYXZlJywgWyckZXZlbnQnXSlcbiAgb25EcmFnTGVhdmUoZXZlbnQpIHtcbiAgICB0aGlzLmZpbGVPdmVyLmVtaXQoZmFsc2UpO1xuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignZHJhZ292ZXInLCBbJyRldmVudCddKVxuICBvbkRyYWdPdmVyKGV2ZW50OiBhbnkpIHtcbiAgICBsZXQgZGF0YVRyYW5zZmVyID0gRmlsZURyb3BEaXJlY3RpdmUuZ2V0RGF0YVRyYW5zZmVyKGV2ZW50KTtcblxuICAgIGlmICghRmlsZURyb3BEaXJlY3RpdmUuaGFzRmlsZXMoZGF0YVRyYW5zZmVyLnR5cGVzKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGRhdGFUcmFuc2Zlci5kcm9wRWZmZWN0ID0gJ2NvcHknO1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgdGhpcy5maWxlT3Zlci5lbWl0KHRydWUpO1xuICB9XG5cbiAgcHJpdmF0ZSBmaWx0ZXJGaWxlcyhmaWxlczogRmlsZUxpc3QpOiBhbnkge1xuICAgIGlmICghdGhpcy5hY2NlcHQgfHwgdGhpcy5hY2NlcHQubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4gZmlsZXM7XG4gICAgfVxuXG4gICAgbGV0IGFjY2VwdGVkRmlsZXM6IEZpbGVbXSA9IFtdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZmlsZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5hY2NlcHQubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgaWYgKEZpbGVEcm9wRGlyZWN0aXZlLm1hdGNoUnVsZSh0aGlzLmFjY2VwdFtqXSwgZmlsZXNbaV0udHlwZSkpIHtcbiAgICAgICAgICBhY2NlcHRlZEZpbGVzLnB1c2goZmlsZXNbaV0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGFjY2VwdGVkRmlsZXM7XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBnZXREYXRhVHJhbnNmZXIoZXZlbnQ6IGFueSk6IERhdGFUcmFuc2ZlciB7XG4gICAgcmV0dXJuIGV2ZW50LmRhdGFUcmFuc2ZlciA/IGV2ZW50LmRhdGFUcmFuc2ZlciA6IGV2ZW50Lm9yaWdpbmFsRXZlbnQuZGF0YVRyYW5zZmVyO1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgaGFzRmlsZXModHlwZXM6IGFueSk6IGJvb2xlYW4ge1xuICAgIGlmICghdHlwZXMpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBpZiAodHlwZXMuaW5kZXhPZikge1xuICAgICAgcmV0dXJuIHR5cGVzLmluZGV4T2YoJ0ZpbGVzJykgIT09IC0xO1xuICAgIH1cblxuICAgIGlmICh0eXBlcy5jb250YWlucykge1xuICAgICAgcmV0dXJuIHR5cGVzLmNvbnRhaW5zKCdGaWxlcycpO1xuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIG1hdGNoUnVsZShydWxlOiBzdHJpbmcsIGNhbmRpZGF0ZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIG5ldyBSZWdFeHAoXCJeXCIgKyBydWxlLnNwbGl0KFwiKlwiKS5qb2luKFwiLipcIikgKyBcIiRcIikudGVzdChjYW5kaWRhdGUpO1xuICB9XG59XG4iLCJpbXBvcnQgeyBIdHRwQ2xpZW50TW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgTmdNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBOZ3hJbWFnZVVwbG9hZENvbXBvbmVudCB9IGZyb20gJy4vbmd4LWltYWdlLXVwbG9hZC5jb21wb25lbnQnO1xuaW1wb3J0IHsgRmlsZURyb3BEaXJlY3RpdmUgfSBmcm9tICcuLi9maWxlLWRyb3AuZGlyZWN0aXZlJztcblxuQE5nTW9kdWxlKHtcbiAgaW1wb3J0czogW1xuICAgIENvbW1vbk1vZHVsZSxcbiAgICBIdHRwQ2xpZW50TW9kdWxlXG4gIF0sXG4gIGRlY2xhcmF0aW9uczogW05neEltYWdlVXBsb2FkQ29tcG9uZW50LCBGaWxlRHJvcERpcmVjdGl2ZV0sXG4gIGV4cG9ydHM6IFtOZ3hJbWFnZVVwbG9hZENvbXBvbmVudF1cbn0pXG5leHBvcnQgY2xhc3MgTmd4SW1hZ2VVcGxvYWRNb2R1bGUgeyB9XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUE7SUFRRSwrQkFBb0IsSUFBZ0I7UUFBaEIsU0FBSSxHQUFKLElBQUksQ0FBWTtLQUNuQzs7Ozs7Ozs7OztJQUVNLHlDQUFTOzs7Ozs7Ozs7Y0FBQyxHQUFXLEVBQUUsS0FBVyxFQUFFLE9BQStDLEVBQUUsUUFBMEIsRUFBRSxjQUF3QyxFQUFFLGVBQXlCO1FBQS9GLHlCQUFBLEVBQUEsa0JBQTBCO1FBQ3BILElBQUksQ0FBQyxHQUFHLElBQUksR0FBRyxLQUFLLEVBQUUsRUFBRTtZQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1NBQzdCOztRQUdELHFCQUFJLFFBQVEsR0FBRyxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQzlCLEtBQUsscUJBQUksR0FBRyxJQUFJLGNBQWMsRUFBRTtZQUM5QixRQUFRLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztTQUMzQztRQUNELFFBQVEsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ2pDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLFFBQVEsRUFBRSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsZUFBZSxFQUFFLGVBQWUsRUFBRSxDQUFDLENBQUM7OztnQkFsQmhHLFVBQVUsU0FBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkI7Ozs7Z0JBTlEsVUFBVTs7O2dDQUFuQjs7Ozs7OztJQ01BO0lBSUUsb0JBQW1CLEdBQVcsRUFBUyxJQUFVO1FBQTlCLFFBQUcsR0FBSCxHQUFHLENBQVE7UUFBUyxTQUFJLEdBQUosSUFBSSxDQUFNO3VCQUh2QixLQUFLO0tBSTlCO3FCQVhIO0lBWUMsQ0FBQTtBQU5EO0lBNEZFLGlDQUFvQixZQUFtQztRQUF2RCxpQkFBNEQ7UUFBeEMsaUJBQVksR0FBWixZQUFZLENBQXVCO3FCQS9CakMsRUFBRTsyQkFDRixDQUFDO3dCQUNILEtBQUs7dUNBQ1UsS0FBSzs0QkFFcUQsVUFBQSxJQUFJLElBQUksT0FBQSxJQUFJLEdBQUE7NkJBQ2hGLE1BQU07d0JBQ1gsS0FBSzt3QkFDRSxRQUFRO2tDQUNMLElBQUk7OEJBQ1IsU0FBUzttQkFHcEIsR0FBRzt1QkFFQyxJQUFJOytCQUtJLEtBQUs7NkJBQzJELEVBQUU7dUJBQ3pFLElBQUksWUFBWSxFQUFjO2tDQUNuQixJQUFJLFlBQVksRUFBVzs4QkFDL0IsSUFBSSxZQUFZLEVBQWM7OEJBQzlCLElBQUksWUFBWSxFQUFjO21DQUluQixDQUFDOzBCQXlEMUIsVUFBQyxNQUFNLElBQUssT0FBQSxLQUFJLENBQUMsUUFBUSxHQUFHLE1BQU0sR0FBQTttQ0FFakIsY0FBTSxPQUFBLEtBQUksQ0FBQyxHQUFHLEdBQUcsS0FBSSxDQUFDLFdBQVcsR0FBQTtLQXpESDs7OztJQUU1RCwwQ0FBUTs7O0lBQVI7UUFDRSxJQUFJLENBQUMsSUFBSSxDQUFDLG1CQUFtQixFQUFFO1lBQzdCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxXQUFXLElBQUksSUFBSSxDQUFDLFdBQVcsR0FBRyxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksSUFBSSxNQUFNLEdBQUcsRUFBRSxDQUFDLENBQUM7U0FDbEg7UUFDRCxJQUFJLENBQUMsbUJBQW1CLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsVUFBQyxHQUFHLElBQUssT0FBQSxRQUFRLEdBQUcsR0FBRyxHQUFBLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0tBQzNIOzs7O0lBRUQsMkNBQVM7OztJQUFUO1FBQUEsaUJBT0M7UUFOQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFBLENBQUMsSUFBSSxPQUFBLEtBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFBLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUMsS0FBSyxHQUFHLEVBQUUsQ0FBQztRQUNoQixJQUFJLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQztRQUNyQixJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUU7WUFDckIsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMsS0FBSyxHQUFHLEVBQUUsQ0FBQztTQUM1QztLQUNGOzs7OztJQUVELDRDQUFVOzs7O0lBQVYsVUFBVyxJQUFnQjtRQUN6QixxQkFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDckMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQzVCLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUVuQixJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUU7WUFDckIsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMsS0FBSyxHQUFHLEVBQUUsQ0FBQztTQUM1QztRQUNELElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0tBQ3pCOzs7OztJQUVELG9EQUFrQjs7OztJQUFsQixVQUFtQixJQUFnQjtRQUNqQyxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztLQUNoQzs7Ozs7SUFFRCw2Q0FBVzs7OztJQUFYLFVBQVksT0FBTztRQUNqQixJQUFJLE9BQU8sQ0FBQyxhQUFhLElBQUksT0FBTyxDQUFDLGFBQWEsQ0FBQyxZQUFZLElBQUksT0FBTyxDQUFDLGFBQWEsQ0FBQyxZQUFZLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtZQUNoSCxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztTQUM3QjtLQUNGOzs7OztJQUVELDhDQUFZOzs7O0lBQVosVUFBYSxLQUFlO1FBQzFCLElBQUksSUFBSSxDQUFDLFFBQVE7WUFBRSxPQUFPOztRQUcxQixxQkFBSSxjQUFjLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUM7UUFDaEQscUJBQUksZ0JBQWdCLEdBQUcsS0FBSyxDQUFDLE1BQU0sR0FBRyxjQUFjLEdBQUcsY0FBYyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFFckYsSUFBSSxJQUFJLENBQUMsR0FBRyxJQUFJLGdCQUFnQixJQUFJLENBQUMsRUFBRTtZQUNyQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQ3BDO1FBRUQsSUFBSSxDQUFDLFdBQVcsSUFBSSxnQkFBZ0IsQ0FBQztRQUNyQyxJQUFJLENBQUMsdUJBQXVCLEdBQUcsS0FBSyxDQUFDO1FBQ3JDLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLGdCQUFnQixDQUFDLENBQUM7S0FDM0M7Ozs7OztJQU1PLDRDQUFVOzs7OztjQUFDLFFBQWtCLEVBQUUsVUFBc0I7UUFDM0QsVUFBVSxDQUFDLGNBQWMsR0FBRyxFQUFFLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTSxFQUFFLFFBQVEsVUFBQSxFQUFFLENBQUM7UUFDbEUsVUFBVSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUM7UUFFM0IsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7UUFFckMsSUFBSSxFQUFFLElBQUksQ0FBQyxtQkFBbUIsSUFBSSxDQUFDLEVBQUU7WUFDbkMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztTQUNyQzs7Ozs7SUFHSyxzREFBb0I7Ozs7UUFDMUIsSUFBSSxPQUFPLElBQUksQ0FBQyxhQUFhLEtBQUssUUFBUSxFQUFFO1lBQzFDLHFCQUFJLElBQUksR0FBUSxJQUFJLENBQUMsYUFBYSxDQUFDO1lBRW5DLElBQUksQ0FBQyxXQUFXLEdBQUcsQ0FBQyxDQUFDO1lBQ3JCLElBQUksQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDO1lBRWIscUJBQUksUUFBUSxTQUFNO1lBQ2hCLElBQUksU0FBTTtZQUNWLE9BQU8sU0FBUSxDQUFDO1lBRWxCLElBQUksSUFBSSxZQUFZLE1BQU0sRUFBRTtnQkFDMUIsT0FBTyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7Z0JBQ25CLFFBQVEsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7Z0JBQ3RELElBQUksR0FBRyxJQUFJLElBQUksQ0FBQyxDQUFDLFFBQVEsQ0FBQyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQzthQUM1QztpQkFBTTtnQkFDTCxPQUFPLEdBQ