@rlucan/ui 18.2.2 → 19.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/fesm2022/rlucan-ui.mjs +144 -140
  2. package/fesm2022/rlucan-ui.mjs.map +1 -1
  3. package/package.json +11 -13
  4. package/esm2022/lib/action-button/action-button.component.mjs +0 -99
  5. package/esm2022/lib/action-icon/action-icon.component.mjs +0 -37
  6. package/esm2022/lib/auto-complete/auto-complete.component.mjs +0 -405
  7. package/esm2022/lib/autocomplete/autocomplete.component.mjs +0 -391
  8. package/esm2022/lib/avatar/avatar.component.mjs +0 -34
  9. package/esm2022/lib/button/button.component.mjs +0 -57
  10. package/esm2022/lib/checkbox/checkbox.component.mjs +0 -39
  11. package/esm2022/lib/checkbox-group/checkbox-group.component.mjs +0 -91
  12. package/esm2022/lib/currency/currency.component.mjs +0 -151
  13. package/esm2022/lib/date/date.component.mjs +0 -68
  14. package/esm2022/lib/dialog/dialog.component.mjs +0 -37
  15. package/esm2022/lib/directives/force-visibility/force-visibility.directive.mjs +0 -104
  16. package/esm2022/lib/editor/editor.component.mjs +0 -119
  17. package/esm2022/lib/elements/burger/burger.component.mjs +0 -21
  18. package/esm2022/lib/elements/expander/expander.component.mjs +0 -28
  19. package/esm2022/lib/elements/validation-message/validation-message.component.mjs +0 -47
  20. package/esm2022/lib/file/file.component.mjs +0 -171
  21. package/esm2022/lib/file-uploader/ui-file-uploader.component.mjs +0 -424
  22. package/esm2022/lib/input/input.component.mjs +0 -265
  23. package/esm2022/lib/input-autocomplete/input-autocomplete.component.mjs +0 -277
  24. package/esm2022/lib/layouts/base/ui-base-layout.component.mjs +0 -22
  25. package/esm2022/lib/layouts/base/ui-base.component.mjs +0 -74
  26. package/esm2022/lib/layouts/simple/ui-simple-layout.component.mjs +0 -18
  27. package/esm2022/lib/layouts/simple/ui-simple.component.mjs +0 -166
  28. package/esm2022/lib/radio/radio.component.mjs +0 -21
  29. package/esm2022/lib/radio-group/radio-group.component.mjs +0 -58
  30. package/esm2022/lib/select/select.component.mjs +0 -126
  31. package/esm2022/lib/services/message-box.service.mjs +0 -149
  32. package/esm2022/lib/services/toast.service.mjs +0 -23
  33. package/esm2022/lib/services/ui-file.service.mjs +0 -72
  34. package/esm2022/lib/services/ui-translate.service.mjs +0 -32
  35. package/esm2022/lib/submit-button/submit-button.component.mjs +0 -72
  36. package/esm2022/lib/table/table.component.mjs +0 -97
  37. package/esm2022/lib/text-area/text-area.component.mjs +0 -46
  38. package/esm2022/lib/ui.model.mjs +0 -2
  39. package/esm2022/lib/ui.module.mjs +0 -274
  40. package/esm2022/public-api.mjs +0 -36
  41. package/esm2022/rlucan-ui.mjs +0 -5
@@ -1,424 +0,0 @@
1
- import { Component, EventEmitter, Host, Inject, Input, Optional, Output, SkipSelf } from '@angular/core';
2
- import { NG_VALUE_ACCESSOR } from '@angular/forms';
3
- import { UI_FILESERVICE } from '../services/ui-file.service';
4
- import { UiSimpleComponent } from '../layouts/simple/ui-simple.component';
5
- import { UI_TRANSLATESERVICE } from '../services/ui-translate.service';
6
- import * as i0 from "@angular/core";
7
- import * as i1 from "@angular/forms";
8
- import * as i2 from "../services/ui-file.service";
9
- import * as i3 from "@angular/common";
10
- import * as i4 from "@angular/material/icon";
11
- import * as i5 from "@angular/material/tooltip";
12
- import * as i6 from "ngx-uploader";
13
- import * as i7 from "@angular/material/progress-spinner";
14
- import * as i8 from "ngx-image-cropper";
15
- import * as i9 from "../file/file.component";
16
- export class UiFileUploaderComponent extends UiSimpleComponent {
17
- set fileSize(fs) {
18
- this._fileSize = fs;
19
- if (typeof fs === 'string') {
20
- const f = this.fileServiceConfig.imageSizes.find(n => n.name === fs);
21
- if (!f) {
22
- console.error('Missing filesize for ' + fs);
23
- }
24
- else {
25
- this.uiFileSize = f;
26
- }
27
- }
28
- else {
29
- this.uiFileSize = fs;
30
- }
31
- }
32
- get menuTemplate() {
33
- return this.templates?.menuTemplate;
34
- }
35
- get addFileTemplate() {
36
- return this.templates?.addFileTemplate;
37
- }
38
- get customContentTemplate() {
39
- return this.templates?.customContentTemplate;
40
- }
41
- get fileTemplate() {
42
- return this.templates?.fileTemplate;
43
- }
44
- constructor(fileServiceConfig, translateService, controlContainer, fileService) {
45
- super(null, controlContainer);
46
- this.fileServiceConfig = fileServiceConfig;
47
- this.translateService = translateService;
48
- this.controlContainer = controlContainer;
49
- this.fileService = fileService;
50
- this.maxFiles = 1;
51
- this.multiple = true;
52
- this.crop = false;
53
- this.cropRounded = false;
54
- this.customFileMenu = false;
55
- this.newFilePosition = 'last';
56
- this.templates = {};
57
- this.previewPosition = 'bottom';
58
- this.imageCropped = new EventEmitter();
59
- this.fileEvent = new EventEmitter();
60
- this.uploadOptions = {
61
- concurrency: this.fileServiceConfig.concurrentUploads
62
- };
63
- this.uploading = false;
64
- this.showCropper = false;
65
- this.showCropPreview = {};
66
- this.cropPreviewTimeouts = {};
67
- this.cropSourceImages = {};
68
- this.cropperImageLoaded = {};
69
- this.imagesDone = {};
70
- this.transform = {};
71
- this.canvasRotation = {};
72
- this.refreshAddInput = false;
73
- this.patchingValues = false;
74
- this.inputArray = false;
75
- this.dragging = false;
76
- this.uploaderId = Math.round(Math.random() * 1000000);
77
- this.croppedImages = {};
78
- this.fileService.uploadSubscription.next({ action: 'register', data: this });
79
- this.fileService.uploadSubscription.subscribe((v) => {
80
- switch (v.action) {
81
- case 'uploadStarted':
82
- this.uploading = true;
83
- break;
84
- case 'uploadFailed':
85
- case 'uploadSuccess':
86
- setTimeout(() => {
87
- this.uploading = false;
88
- });
89
- this.patchValues();
90
- }
91
- });
92
- }
93
- get uploadInput() {
94
- return this.fileService.uploadInput;
95
- }
96
- get editable() {
97
- return this.componentFormControl.enabled;
98
- }
99
- get ngAddStyle() {
100
- return {
101
- width: Math.abs(this.uiFileSize.width) + 'px',
102
- height: Math.abs(this.uiFileSize.height) + 'px',
103
- };
104
- }
105
- get aspectRatio() {
106
- return this.uiFileSize.width / this.uiFileSize.height;
107
- }
108
- get addFileImgSrc() {
109
- return this.fileServiceConfig.addFileImgSrc;
110
- }
111
- cropDone(f) {
112
- return !f.$newFile || this.imagesDone[f.$newFile.id];
113
- }
114
- // ngOnInit(): void {
115
- // super.ngOnInit();
116
- // }
117
- ngOnDestroy() {
118
- this.fileService.uploadSubscription.next({ action: 'destroy', data: this });
119
- }
120
- remove(f) {
121
- if (f.$newFile) {
122
- this.uploadInput.emit({ type: 'remove', id: f.$newFile.id });
123
- }
124
- this.files.splice(this.files.indexOf(f), 1);
125
- this.prepareResponse();
126
- // this.onChange(this.inputArray ? this.files : (this.files.length > 0 ? this.files[0] : null));
127
- this.fileEvent.emit({ message: 'fileRemoved', data: f });
128
- // this.patchValues();
129
- }
130
- patchValues() {
131
- this.patchingValues = true;
132
- // if (this.inputArray) {
133
- // this.componentFormControl.setValue(this.files.map(f => f.custom));
134
- // } else {
135
- // this.componentFormControl.setValue(this.files.length === 0 ? null : this.files[0].custom);
136
- // }
137
- this.patchingValues = false;
138
- }
139
- get hasFilesToUpload() {
140
- return !!this.files.find(f => f.$newFile && f.$newFile.progress.status !== 2);
141
- }
142
- canImagePreview(type) {
143
- return type.startsWith('image/') || type.startsWith('video/');
144
- }
145
- onUploadOutput($event, skipCustomValidation = false) {
146
- // console.log($event);
147
- switch ($event.type) {
148
- case 'start':
149
- break;
150
- case 'dragOver':
151
- this.dragging = true;
152
- break;
153
- case 'dragOut':
154
- case 'drop':
155
- this.dragging = false;
156
- break;
157
- case 'addedToQueue':
158
- if (this.crop && !$event.file.type.startsWith('image/')) {
159
- this.uploadInput.emit({ type: 'remove', id: $event.file.id });
160
- this.fileEvent.emit({ message: 'cropInvalidFileType', data: $event.file });
161
- return;
162
- }
163
- if (!skipCustomValidation && this.fileServiceConfig.fileValidation) {
164
- this.fileServiceConfig.fileValidation(this.uiFileSize, $event.file).then(valid => {
165
- if (valid) {
166
- this.onUploadOutput($event, true);
167
- }
168
- else {
169
- this.uploadInput.emit({ type: 'remove', id: $event.file.id });
170
- // this.fileEvent.emit({ message: 'cropInvalidFileType', data: $event.file });
171
- }
172
- });
173
- }
174
- else {
175
- let f;
176
- if (this.changeFileIndex !== undefined) {
177
- f = this.files[this.changeFileIndex];
178
- if (f.$newFile) {
179
- this.uploadInput.emit({ type: 'remove', id: f.$newFile.id });
180
- }
181
- f.$newFile = $event.file;
182
- f.custom = $event.file;
183
- this.changeFileIndex = undefined;
184
- }
185
- else {
186
- f = { custom: $event.file, $newFile: $event.file };
187
- if (this.newFilePosition === 'last') {
188
- this.files.push(f);
189
- }
190
- else {
191
- this.files.unshift(f);
192
- }
193
- }
194
- if (this.canImagePreview($event.file.type)) {
195
- if (this.crop) {
196
- this.canvasRotation[$event.file.id] = 0;
197
- this.cropSourceImages[$event.file.id] = $event.file.nativeFile;
198
- this.cropperImageLoaded[$event.file.id] = false;
199
- }
200
- }
201
- this.prepareResponse();
202
- this.fileEvent.emit({ message: 'fileAdded', data: f });
203
- }
204
- break;
205
- case 'done':
206
- const df = this.files.find(f => f.$newFile === $event.file);
207
- if (df) {
208
- if (df.$newFile.responseStatus === 200) {
209
- this.files[this.files.indexOf(df)].custom = df.$newFile.response;
210
- df.$newFile = null;
211
- }
212
- else {
213
- // console.log('error on upload', df.$event);
214
- }
215
- }
216
- if (!this.hasFilesToUpload) {
217
- this.prepareResponse(false);
218
- this.fileService.uploadSubscription.next({
219
- action: this.files.find(f => !!f.$newFile) ? 'uploadControlFailed' : 'uploadControlSuccess'
220
- });
221
- }
222
- break;
223
- }
224
- }
225
- prepareResponse(doemit = true) {
226
- if (this.inputArray) {
227
- if (!this.responseFiles) {
228
- this.responseFiles = [];
229
- }
230
- this.responseFiles.length = 0;
231
- this.files.forEach(f => this.responseFiles.push({ ...f.custom, $newFile: f.$newFile }));
232
- }
233
- else {
234
- if (this.files.length === 0) {
235
- this.responseFiles = null;
236
- }
237
- else {
238
- if (!this.responseFiles) {
239
- this.responseFiles = {};
240
- }
241
- Object.keys(this.files[0].custom).forEach(k => {
242
- this.responseFiles[k] = this.files[0].custom[k];
243
- });
244
- this.responseFiles.$newFile = this.files[0].$newFile;
245
- }
246
- }
247
- if (doemit) {
248
- this.onChange(this.responseFiles);
249
- }
250
- // console.log('emit', this.files[1], { ...this.files[1].custom, $newFile: this.files[1].$newFile });
251
- // this.onChange(this.inputArray ?
252
- // this.files.map(f => ({ ...f.custom, $newFile: f.$newFile})) :
253
- // (this.files.length > 0 ? { ...this.files[0].custom, $newFile: this.files[0].$newFile } : null));
254
- // this.onChange(this.inputArray ?
255
- // this.files :
256
- // (this.files.length > 0 ? this.files[0] : null));
257
- }
258
- beforeSave() {
259
- return new Promise((res) => {
260
- this.files.forEach(f => {
261
- if (f.$newFile && f.$newFile.progress.status === 2) {
262
- f.$newFile.progress.status = 0;
263
- }
264
- if (this.crop && f.$newFile && !this.imagesDone[f.$newFile.id]) {
265
- this.cropperDone(f.$newFile.id);
266
- }
267
- });
268
- setTimeout(() => {
269
- res('done');
270
- }, 250);
271
- });
272
- }
273
- fileChangeEvent(event, f) {
274
- this.changeFileIndex = this.files.indexOf(f);
275
- }
276
- startCropPreview(id) {
277
- clearTimeout(this.cropPreviewTimeouts[id]);
278
- this.showCropPreview[id] = !!this.cropSourceImages[id];
279
- }
280
- stopCropPreview(id) {
281
- this.cropPreviewTimeouts[id] = setTimeout(() => {
282
- this.showCropPreview[id] = false;
283
- }, 250);
284
- }
285
- zoomCroppedImage(dir, id) {
286
- let scale = this.transform[id].scale;
287
- scale += dir;
288
- if (scale < 0.1) {
289
- scale = 0.1;
290
- }
291
- this.transform[id] = {
292
- ...this.transform[id],
293
- scale
294
- };
295
- }
296
- rotateCroppedImage(dir, id) {
297
- this.canvasRotation[id] += dir;
298
- const flippedH = this.transform[id].flipH;
299
- const flippedV = this.transform[id].flipV;
300
- this.transform[id] = {
301
- ...this.transform[id],
302
- flipH: flippedV,
303
- flipV: flippedH
304
- };
305
- }
306
- onImageCropped(event, id) {
307
- this.croppedImages[id] = event;
308
- this.imageCropped.emit(event);
309
- }
310
- imageLoaded(id) {
311
- this.showCropper = true;
312
- setTimeout(() => {
313
- this.transform[id] = {
314
- scale: 1
315
- };
316
- });
317
- }
318
- cropperReady(sourceImageDimensions, id) {
319
- this.cropperImageLoaded[id] = true;
320
- }
321
- loadImageFailed() {
322
- }
323
- toggleMenuVisibility(f) {
324
- this.fileMenuOpened = f;
325
- }
326
- menuClick() {
327
- this.fileMenuOpened = undefined;
328
- }
329
- writeValue(obj) {
330
- this.inputArray = obj instanceof Array;
331
- if (this.inputArray) {
332
- this.files = obj.map(f => ({ custom: f, $newFile: null }));
333
- }
334
- else {
335
- this.files = obj ? [{ custom: obj, $newFile: null }] : [];
336
- }
337
- this.cropSourceImages = {};
338
- }
339
- cropperDone(id) {
340
- const f = this.files.find(fx => fx.$newFile && fx.$newFile.id === id);
341
- if (f) {
342
- // const url = this.croppedImages[id];
343
- // console.log(url);
344
- // fetch(url)
345
- // .then(res => res.blob())
346
- // .then(blob => {
347
- // f.$newFile.nativeFile = new File([ blob ], 'crop.png', {
348
- // type: 'image/png'
349
- // });
350
- // f.$newFile.name = 'crop.png';
351
- // f.$newFile.size = f.$newFile.nativeFile.size;
352
- // f.$newFile.type = f.$newFile.nativeFile.type;
353
- // this.imagesDone[id] = true;
354
- // console.log(f);
355
- // })
356
- const croppedImage = this.croppedImages[id];
357
- f.$newFile.nativeFile = new File([croppedImage.blob], 'crop.png', {
358
- type: 'image/png'
359
- });
360
- f.$newFile.name = 'crop.png';
361
- f.$newFile.size = f.$newFile.nativeFile.size;
362
- f.$newFile.type = f.$newFile.nativeFile.type;
363
- this.imagesDone[id] = true;
364
- }
365
- }
366
- uploadImgSrc() {
367
- return this.addFileImgSrc(this.uiFileSize);
368
- }
369
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: UiFileUploaderComponent, deps: [{ token: UI_FILESERVICE, optional: true }, { token: UI_TRANSLATESERVICE }, { token: i1.ControlContainer, host: true, optional: true, skipSelf: true }, { token: i2.UiFileService }], target: i0.ɵɵFactoryTarget.Component }); }
370
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.1", type: UiFileUploaderComponent, selector: "ui-file-uploader", inputs: { fileSize: "fileSize", maxFiles: "maxFiles", multiple: "multiple", crop: "crop", cropRounded: "cropRounded", cropMaxWidth: "cropMaxWidth", customFileMenu: "customFileMenu", addHint: "addHint", newFilePosition: "newFilePosition", templates: "templates", previewPosition: "previewPosition" }, outputs: { imageCropped: "imageCropped", fileEvent: "fileEvent" }, providers: [{
371
- provide: NG_VALUE_ACCESSOR,
372
- multi: true,
373
- useExisting: UiFileUploaderComponent,
374
- }], usesInheritance: true, ngImport: i0, template: "<div class=\"label\" *ngIf=\"label\">\r\n <div class=\"text-container\"\r\n [matTooltip]=\"required ? translateService.instant('ui.controls.validation.required') : undefined\"\r\n [matTooltipPosition]=\"'above'\">\r\n <div class=\"text\">{{ label }}</div>\r\n <div *ngIf=\"required\" class=\"required\">*</div>\r\n </div>\r\n</div>\r\n\r\n<div class=\"files-container\" [ngClass]=\"{dragging: dragging, disabled: uploading || !editable}\">\r\n\r\n <div class=\"existing-files\">\r\n\r\n <div class=\"file-container\" *ngFor=\"let f of files\">\r\n\r\n @if (customContentTemplate) {\r\n <ng-template [ngTemplateOutlet]=\"customContentTemplate\" [ngTemplateOutletContext]=\"{fileUploader: this, file: f}\"></ng-template>\r\n }\r\n\r\n <div class=\"file-menu\" (click)=\"toggleMenuVisibility(f)\" *ngIf=\"!customFileMenu && editable && !uploading\">\r\n <mat-icon>menu</mat-icon>\r\n </div>\r\n <div class=\"file-menu-container mat-elevation-z2\" [ngClass]=\"{visible: f === fileMenuOpened}\"\r\n (click)=\"menuClick()\" (mouseleave)=\"menuClick()\">\r\n <ng-container *ngIf=\"!menuTemplate\">\r\n <label class=\"menu-item\">{{ translateService.instant('ui.controls.ui-file-uploader.menu.change') }}\r\n <input *ngIf=\"!refreshAddInput\" style=\"display: none;\" type=\"file\" ngFileSelect\r\n [options]=\"uploadOptions\" (change)=\"fileChangeEvent($event, f)\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\">\r\n </label>\r\n <div class=\"menu-item\"\r\n (click)=\"remove(f)\">{{ translateService.instant('ui.controls.ui-file-uploader.menu.delete') }}\r\n </div>\r\n </ng-container>\r\n <ng-container *ngIf=\"menuTemplate\">\r\n <ng-template [ngTemplateOutlet]=\"menuTemplate\"\r\n [ngTemplateOutletContext]=\"{fileInput: fileInput}\"></ng-template>\r\n <ng-template #fileInput>\r\n <input style=\"display: none;\" type=\"file\" ngFileSelect [options]=\"uploadOptions\"\r\n (change)=\"fileChangeEvent($event, f)\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\">\r\n </ng-template>\r\n </ng-container>\r\n </div>\r\n\r\n <div *ngIf=\"crop && !cropDone(f)\" (mouseenter)=\"startCropPreview(f.$newFile.id)\"\r\n (mouseleave)=\"stopCropPreview(f.$newFile.id)\">\r\n <div class=\"crop-container\" [ngStyle]=\"ngAddStyle\">\r\n\r\n <ui-file *ngIf=\"false && files.length > 0 && !cropSourceImages\" [fileSize]=\"null\"\r\n [srcData]=\"files[0].custom\" [ngClass]=\"{rounded: cropRounded}\"></ui-file>\r\n\r\n <mat-spinner *ngIf=\"!cropperImageLoaded[f.$newFile.id] && cropSourceImages[f.$newFile.id]\"\r\n [diameter]=\"75\"></mat-spinner>\r\n\r\n <image-cropper *ngIf=\"cropSourceImages[f.$newFile.id]\"\r\n [imageFile]=\"cropSourceImages[f.$newFile.id]\"\r\n [maintainAspectRatio]=\"true\"\r\n [containWithinAspectRatio]=\"false\"\r\n [aspectRatio]=\"aspectRatio\"\r\n [resizeToWidth]=\"cropMaxWidth\"\r\n [onlyScaleDown]=\"false\"\r\n [roundCropper]=\"cropRounded\"\r\n [canvasRotation]=\"canvasRotation[f.$newFile.id]\"\r\n [transform]=\"transform[f.$newFile.id]\"\r\n [alignImage]=\"'center'\"\r\n [style.display]=\"showCropper ? null : 'none'\"\r\n [format]=\"'png'\"\r\n (imageCropped)=\"onImageCropped($event, f.$newFile.id)\"\r\n (imageLoaded)=\"imageLoaded(f.$newFile.id)\"\r\n (cropperReady)=\"cropperReady($event, f.$newFile.id)\"\r\n (loadImageFailed)=\"loadImageFailed()\"\r\n ></image-cropper>\r\n\r\n <div class=\"crop-preview mat-elevation-z2\" [class]=\"previewPosition\"\r\n *ngIf=\"true || (showCropPreview[f.$newFile.id] && cropperImageLoaded[f.$newFile.id])\">\r\n <div class=\"crop-menu\">\r\n <mat-icon (click)=\"zoomCroppedImage(.1, f.$newFile.id)\">zoom_in</mat-icon>\r\n <mat-icon (click)=\"zoomCroppedImage(- .1, f.$newFile.id)\">zoom_out</mat-icon>\r\n <mat-icon (click)=\"rotateCroppedImage(-1, f.$newFile.id)\">rotate_left</mat-icon>\r\n <mat-icon (click)=\"rotateCroppedImage(1, f.$newFile.id)\">rotate_right</mat-icon>\r\n <div style=\"flex: 1 1 100%\"></div>\r\n <label class=\"menu-item\">\r\n <mat-icon>upload_file</mat-icon>\r\n <input style=\"display: none;\" type=\"file\" ngFileSelect [options]=\"uploadOptions\"\r\n (change)=\"fileChangeEvent($event, f)\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\">\r\n </label>\r\n <mat-icon (click)=\"remove(f)\">delete_outline</mat-icon>\r\n <div style=\"flex: 1 1 100%\"></div>\r\n <mat-icon (click)=\"cropperDone(f.$newFile.id)\">done</mat-icon>\r\n </div>\r\n <img *ngIf=\"croppedImages[f.$newFile.id]\" [src]=\"croppedImages[f.$newFile.id].objectUrl\"\r\n [ngClass]=\"{rounded: cropRounded}\"/>\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n </div>\r\n\r\n <ng-container *ngIf=\"!crop || cropDone(f)\">\r\n <ng-container *ngIf=\"fileTemplate\">\r\n <ng-template [ngTemplateOutlet]=\"fileTemplate\"\r\n [ngTemplateOutletContext]=\"{file: f, fileInput: fileInput2}\"></ng-template>\r\n <ng-template #fileInput2>\r\n <input style=\"display: none;\" type=\"file\" ngFileSelect [options]=\"uploadOptions\"\r\n (change)=\"fileChangeEvent($event, f)\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\">\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ui-file *ngIf=\"!fileTemplate\" [fileSize]=\"_fileSize\" [srcData]=\"f.$newFile ? f.$newFile: f.custom\"\r\n [ngClass]=\"{rounded: crop && cropRounded}\">\r\n </ui-file>\r\n\r\n <ng-container *ngIf=\"f.$newFile\">\r\n <div *ngIf=\"f.$newFile.responseStatus && f.$newFile.responseStatus !== 200\"\r\n class=\"progress-container error\">\r\n {{ translateService.instant('ui.controls.ui-file-uploader.uploadError', f.$newFile.response) }}\r\n </div>\r\n <div *ngIf=\"!f.$newFile.responseStatus && f.$newFile.progress.status === 1\"\r\n class=\"progress-container\">\r\n <div class=\"progress\" [ngStyle]=\"{width: f.$newFile.progress.data.percentage + '%'}\"></div>\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n\r\n </div>\r\n\r\n <div class=\"file-container add\" ngFileDrop [options]=\"uploadOptions\" (uploadOutput)=\"onUploadOutput($event)\"\r\n [uploadInput]=\"uploadInput\" [ngClass]=\"{visible: files.length < maxFiles || maxFiles === 0}\"\r\n [ngStyle]=\"addFileTemplate ? {} : ngAddStyle\">\r\n <label>\r\n @if (addFileTemplate) {\r\n <ng-template [ngTemplateOutlet]=\"addFileTemplate\"></ng-template>\r\n } @else {\r\n <div class=\"upload-image-container\"></div>\r\n <img [src]=\"uploadImgSrc()\" [ngClass]=\"{'with-hint': addHint}\"/>\r\n <div *ngIf=\"addHint\" class=\"add-hint\">{{ addHint }}</div>\r\n }\r\n <input style=\"display: none;\" type=\"file\" ngFileSelect [options]=\"uploadOptions\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\"\r\n [multiple]=\"maxFiles === 0 && multiple\">\r\n </label>\r\n </div>\r\n\r\n</div>\r\n", styles: [":host{display:flex;flex-wrap:wrap}:host .label .text{font-size:.9em;line-height:1em;padding:6px 0 4px}:host .files-container{display:flex;flex-wrap:wrap;border-style:solid}:host .files-container .existing-files{display:flex;flex-wrap:wrap}:host .files-container.dragging .add label{border:2px dashed grey;background-color:#0000001a}:host .files-container.disabled .add{opacity:.5;pointer-events:none}:host .file-container{max-width:100%;max-height:100%;position:relative;display:flex;align-items:center;justify-content:center}:host .file-container .progress-container{position:absolute;bottom:0;left:1px;right:1px;height:20%;max-height:20px;background-color:#0000001a}:host .file-container .progress-container.error{background-color:red;color:#fff;display:flex;align-items:center;justify-content:center}:host .file-container .progress-container .progress{background-color:#0000004d;height:100%}:host .file-container.add{display:flex;align-items:center;flex-direction:column;transition:opacity .25s;cursor:pointer}:host .file-container.add:not(.visible){display:none}:host .file-container.add img{max-width:100%;max-height:100%}:host .file-container.add img.with-hint{max-height:75%}:host .file-container.add .add-hint{margin-top:8px;font-size:80%;text-align:center}:host .file-container.add label{cursor:pointer;z-index:10;display:flex;flex-direction:column;align-items:center;justify-content:center}:host .file-container.add .upload-image-container{display:none}:host .file-container .file-menu-container{display:none;position:absolute;left:calc(100% - 45px);top:27px;z-index:1000}:host .file-container .file-menu-container ::ng-deep .menu-item{position:relative;padding:12px 24px;cursor:pointer;display:block}:host .file-container .file-menu-container.visible{display:block}:host .file-container .file-menu{display:flex;position:absolute;flex-direction:column;align-items:center;justify-content:center;padding:12px;right:10px;top:10px;background-color:#fff9;border-radius:50%;cursor:pointer;transition:all .25s;z-index:999}:host .file-container:hover .file-menu{background-color:#fff;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}:host .file-container mat-progress-bar{position:absolute;bottom:0;height:6px;left:0;right:0;z-index:1000}:host image-cropper{padding:0;--cropper-outline-color: rgba(255, 255, 255, .8)}:host ui-file.rounded ::ng-deep img{border-radius:50%}:host .crop-container{display:flex;align-items:center;justify-content:center;position:relative}:host .crop-container mat-spinner{position:absolute;z-index:1}:host .crop-container .crop-preview{position:absolute;top:100%;left:0;right:0;padding:6px;border:1px solid transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:1000;max-width:350px;margin-right:auto;margin-left:auto}:host .crop-container .crop-preview.left{right:calc(100% + 6px);left:unset;top:0}:host .crop-container .crop-preview.above{bottom:100%;top:unset}:host .crop-container .crop-preview .crop-menu{width:100%;flex:0 0 auto;display:flex}:host .crop-container .crop-preview .crop-menu mat-icon{margin:0 3px;width:calc(1em + 6px);height:1em;font-size:2em;cursor:pointer;-webkit-user-select:none;user-select:none;flex:1 1 100%}:host .crop-container .crop-preview img{margin:16px 0;max-width:256px;max-height:256px}:host .crop-container .crop-preview img.rounded{border-radius:50%}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: i6.NgFileDropDirective, selector: "[ngFileDrop]", inputs: ["options", "uploadInput"], outputs: ["uploadOutput"] }, { kind: "directive", type: i6.NgFileSelectDirective, selector: "[ngFileSelect]", inputs: ["options", "uploadInput"], outputs: ["uploadOutput"] }, { kind: "component", type: i7.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: i8.ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "cropperFrameAriaLabel", "output", "format", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "autoCrop", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "cropper", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange"] }, { kind: "component", type: i9.FileComponent, selector: "ui-file", inputs: ["fileSize", "srcOptions", "srcData", "srcUrl"] }] }); }
375
- }
376
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.1", ngImport: i0, type: UiFileUploaderComponent, decorators: [{
377
- type: Component,
378
- args: [{ selector: 'ui-file-uploader', providers: [{
379
- provide: NG_VALUE_ACCESSOR,
380
- multi: true,
381
- useExisting: UiFileUploaderComponent,
382
- }], template: "<div class=\"label\" *ngIf=\"label\">\r\n <div class=\"text-container\"\r\n [matTooltip]=\"required ? translateService.instant('ui.controls.validation.required') : undefined\"\r\n [matTooltipPosition]=\"'above'\">\r\n <div class=\"text\">{{ label }}</div>\r\n <div *ngIf=\"required\" class=\"required\">*</div>\r\n </div>\r\n</div>\r\n\r\n<div class=\"files-container\" [ngClass]=\"{dragging: dragging, disabled: uploading || !editable}\">\r\n\r\n <div class=\"existing-files\">\r\n\r\n <div class=\"file-container\" *ngFor=\"let f of files\">\r\n\r\n @if (customContentTemplate) {\r\n <ng-template [ngTemplateOutlet]=\"customContentTemplate\" [ngTemplateOutletContext]=\"{fileUploader: this, file: f}\"></ng-template>\r\n }\r\n\r\n <div class=\"file-menu\" (click)=\"toggleMenuVisibility(f)\" *ngIf=\"!customFileMenu && editable && !uploading\">\r\n <mat-icon>menu</mat-icon>\r\n </div>\r\n <div class=\"file-menu-container mat-elevation-z2\" [ngClass]=\"{visible: f === fileMenuOpened}\"\r\n (click)=\"menuClick()\" (mouseleave)=\"menuClick()\">\r\n <ng-container *ngIf=\"!menuTemplate\">\r\n <label class=\"menu-item\">{{ translateService.instant('ui.controls.ui-file-uploader.menu.change') }}\r\n <input *ngIf=\"!refreshAddInput\" style=\"display: none;\" type=\"file\" ngFileSelect\r\n [options]=\"uploadOptions\" (change)=\"fileChangeEvent($event, f)\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\">\r\n </label>\r\n <div class=\"menu-item\"\r\n (click)=\"remove(f)\">{{ translateService.instant('ui.controls.ui-file-uploader.menu.delete') }}\r\n </div>\r\n </ng-container>\r\n <ng-container *ngIf=\"menuTemplate\">\r\n <ng-template [ngTemplateOutlet]=\"menuTemplate\"\r\n [ngTemplateOutletContext]=\"{fileInput: fileInput}\"></ng-template>\r\n <ng-template #fileInput>\r\n <input style=\"display: none;\" type=\"file\" ngFileSelect [options]=\"uploadOptions\"\r\n (change)=\"fileChangeEvent($event, f)\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\">\r\n </ng-template>\r\n </ng-container>\r\n </div>\r\n\r\n <div *ngIf=\"crop && !cropDone(f)\" (mouseenter)=\"startCropPreview(f.$newFile.id)\"\r\n (mouseleave)=\"stopCropPreview(f.$newFile.id)\">\r\n <div class=\"crop-container\" [ngStyle]=\"ngAddStyle\">\r\n\r\n <ui-file *ngIf=\"false && files.length > 0 && !cropSourceImages\" [fileSize]=\"null\"\r\n [srcData]=\"files[0].custom\" [ngClass]=\"{rounded: cropRounded}\"></ui-file>\r\n\r\n <mat-spinner *ngIf=\"!cropperImageLoaded[f.$newFile.id] && cropSourceImages[f.$newFile.id]\"\r\n [diameter]=\"75\"></mat-spinner>\r\n\r\n <image-cropper *ngIf=\"cropSourceImages[f.$newFile.id]\"\r\n [imageFile]=\"cropSourceImages[f.$newFile.id]\"\r\n [maintainAspectRatio]=\"true\"\r\n [containWithinAspectRatio]=\"false\"\r\n [aspectRatio]=\"aspectRatio\"\r\n [resizeToWidth]=\"cropMaxWidth\"\r\n [onlyScaleDown]=\"false\"\r\n [roundCropper]=\"cropRounded\"\r\n [canvasRotation]=\"canvasRotation[f.$newFile.id]\"\r\n [transform]=\"transform[f.$newFile.id]\"\r\n [alignImage]=\"'center'\"\r\n [style.display]=\"showCropper ? null : 'none'\"\r\n [format]=\"'png'\"\r\n (imageCropped)=\"onImageCropped($event, f.$newFile.id)\"\r\n (imageLoaded)=\"imageLoaded(f.$newFile.id)\"\r\n (cropperReady)=\"cropperReady($event, f.$newFile.id)\"\r\n (loadImageFailed)=\"loadImageFailed()\"\r\n ></image-cropper>\r\n\r\n <div class=\"crop-preview mat-elevation-z2\" [class]=\"previewPosition\"\r\n *ngIf=\"true || (showCropPreview[f.$newFile.id] && cropperImageLoaded[f.$newFile.id])\">\r\n <div class=\"crop-menu\">\r\n <mat-icon (click)=\"zoomCroppedImage(.1, f.$newFile.id)\">zoom_in</mat-icon>\r\n <mat-icon (click)=\"zoomCroppedImage(- .1, f.$newFile.id)\">zoom_out</mat-icon>\r\n <mat-icon (click)=\"rotateCroppedImage(-1, f.$newFile.id)\">rotate_left</mat-icon>\r\n <mat-icon (click)=\"rotateCroppedImage(1, f.$newFile.id)\">rotate_right</mat-icon>\r\n <div style=\"flex: 1 1 100%\"></div>\r\n <label class=\"menu-item\">\r\n <mat-icon>upload_file</mat-icon>\r\n <input style=\"display: none;\" type=\"file\" ngFileSelect [options]=\"uploadOptions\"\r\n (change)=\"fileChangeEvent($event, f)\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\">\r\n </label>\r\n <mat-icon (click)=\"remove(f)\">delete_outline</mat-icon>\r\n <div style=\"flex: 1 1 100%\"></div>\r\n <mat-icon (click)=\"cropperDone(f.$newFile.id)\">done</mat-icon>\r\n </div>\r\n <img *ngIf=\"croppedImages[f.$newFile.id]\" [src]=\"croppedImages[f.$newFile.id].objectUrl\"\r\n [ngClass]=\"{rounded: cropRounded}\"/>\r\n </div>\r\n\r\n </div>\r\n\r\n\r\n </div>\r\n\r\n <ng-container *ngIf=\"!crop || cropDone(f)\">\r\n <ng-container *ngIf=\"fileTemplate\">\r\n <ng-template [ngTemplateOutlet]=\"fileTemplate\"\r\n [ngTemplateOutletContext]=\"{file: f, fileInput: fileInput2}\"></ng-template>\r\n <ng-template #fileInput2>\r\n <input style=\"display: none;\" type=\"file\" ngFileSelect [options]=\"uploadOptions\"\r\n (change)=\"fileChangeEvent($event, f)\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\">\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ui-file *ngIf=\"!fileTemplate\" [fileSize]=\"_fileSize\" [srcData]=\"f.$newFile ? f.$newFile: f.custom\"\r\n [ngClass]=\"{rounded: crop && cropRounded}\">\r\n </ui-file>\r\n\r\n <ng-container *ngIf=\"f.$newFile\">\r\n <div *ngIf=\"f.$newFile.responseStatus && f.$newFile.responseStatus !== 200\"\r\n class=\"progress-container error\">\r\n {{ translateService.instant('ui.controls.ui-file-uploader.uploadError', f.$newFile.response) }}\r\n </div>\r\n <div *ngIf=\"!f.$newFile.responseStatus && f.$newFile.progress.status === 1\"\r\n class=\"progress-container\">\r\n <div class=\"progress\" [ngStyle]=\"{width: f.$newFile.progress.data.percentage + '%'}\"></div>\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n\r\n </div>\r\n\r\n <div class=\"file-container add\" ngFileDrop [options]=\"uploadOptions\" (uploadOutput)=\"onUploadOutput($event)\"\r\n [uploadInput]=\"uploadInput\" [ngClass]=\"{visible: files.length < maxFiles || maxFiles === 0}\"\r\n [ngStyle]=\"addFileTemplate ? {} : ngAddStyle\">\r\n <label>\r\n @if (addFileTemplate) {\r\n <ng-template [ngTemplateOutlet]=\"addFileTemplate\"></ng-template>\r\n } @else {\r\n <div class=\"upload-image-container\"></div>\r\n <img [src]=\"uploadImgSrc()\" [ngClass]=\"{'with-hint': addHint}\"/>\r\n <div *ngIf=\"addHint\" class=\"add-hint\">{{ addHint }}</div>\r\n }\r\n <input style=\"display: none;\" type=\"file\" ngFileSelect [options]=\"uploadOptions\"\r\n (uploadOutput)=\"onUploadOutput($event)\" [uploadInput]=\"uploadInput\"\r\n [multiple]=\"maxFiles === 0 && multiple\">\r\n </label>\r\n </div>\r\n\r\n</div>\r\n", styles: [":host{display:flex;flex-wrap:wrap}:host .label .text{font-size:.9em;line-height:1em;padding:6px 0 4px}:host .files-container{display:flex;flex-wrap:wrap;border-style:solid}:host .files-container .existing-files{display:flex;flex-wrap:wrap}:host .files-container.dragging .add label{border:2px dashed grey;background-color:#0000001a}:host .files-container.disabled .add{opacity:.5;pointer-events:none}:host .file-container{max-width:100%;max-height:100%;position:relative;display:flex;align-items:center;justify-content:center}:host .file-container .progress-container{position:absolute;bottom:0;left:1px;right:1px;height:20%;max-height:20px;background-color:#0000001a}:host .file-container .progress-container.error{background-color:red;color:#fff;display:flex;align-items:center;justify-content:center}:host .file-container .progress-container .progress{background-color:#0000004d;height:100%}:host .file-container.add{display:flex;align-items:center;flex-direction:column;transition:opacity .25s;cursor:pointer}:host .file-container.add:not(.visible){display:none}:host .file-container.add img{max-width:100%;max-height:100%}:host .file-container.add img.with-hint{max-height:75%}:host .file-container.add .add-hint{margin-top:8px;font-size:80%;text-align:center}:host .file-container.add label{cursor:pointer;z-index:10;display:flex;flex-direction:column;align-items:center;justify-content:center}:host .file-container.add .upload-image-container{display:none}:host .file-container .file-menu-container{display:none;position:absolute;left:calc(100% - 45px);top:27px;z-index:1000}:host .file-container .file-menu-container ::ng-deep .menu-item{position:relative;padding:12px 24px;cursor:pointer;display:block}:host .file-container .file-menu-container.visible{display:block}:host .file-container .file-menu{display:flex;position:absolute;flex-direction:column;align-items:center;justify-content:center;padding:12px;right:10px;top:10px;background-color:#fff9;border-radius:50%;cursor:pointer;transition:all .25s;z-index:999}:host .file-container:hover .file-menu{background-color:#fff;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}:host .file-container mat-progress-bar{position:absolute;bottom:0;height:6px;left:0;right:0;z-index:1000}:host image-cropper{padding:0;--cropper-outline-color: rgba(255, 255, 255, .8)}:host ui-file.rounded ::ng-deep img{border-radius:50%}:host .crop-container{display:flex;align-items:center;justify-content:center;position:relative}:host .crop-container mat-spinner{position:absolute;z-index:1}:host .crop-container .crop-preview{position:absolute;top:100%;left:0;right:0;padding:6px;border:1px solid transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:1000;max-width:350px;margin-right:auto;margin-left:auto}:host .crop-container .crop-preview.left{right:calc(100% + 6px);left:unset;top:0}:host .crop-container .crop-preview.above{bottom:100%;top:unset}:host .crop-container .crop-preview .crop-menu{width:100%;flex:0 0 auto;display:flex}:host .crop-container .crop-preview .crop-menu mat-icon{margin:0 3px;width:calc(1em + 6px);height:1em;font-size:2em;cursor:pointer;-webkit-user-select:none;user-select:none;flex:1 1 100%}:host .crop-container .crop-preview img{margin:16px 0;max-width:256px;max-height:256px}:host .crop-container .crop-preview img.rounded{border-radius:50%}\n"] }]
383
- }], ctorParameters: () => [{ type: undefined, decorators: [{
384
- type: Optional
385
- }, {
386
- type: Inject,
387
- args: [UI_FILESERVICE]
388
- }] }, { type: undefined, decorators: [{
389
- type: Inject,
390
- args: [UI_TRANSLATESERVICE]
391
- }] }, { type: i1.ControlContainer, decorators: [{
392
- type: Optional
393
- }, {
394
- type: Host
395
- }, {
396
- type: SkipSelf
397
- }] }, { type: i2.UiFileService }], propDecorators: { fileSize: [{
398
- type: Input
399
- }], maxFiles: [{
400
- type: Input
401
- }], multiple: [{
402
- type: Input
403
- }], crop: [{
404
- type: Input
405
- }], cropRounded: [{
406
- type: Input
407
- }], cropMaxWidth: [{
408
- type: Input
409
- }], customFileMenu: [{
410
- type: Input
411
- }], addHint: [{
412
- type: Input
413
- }], newFilePosition: [{
414
- type: Input
415
- }], templates: [{
416
- type: Input
417
- }], previewPosition: [{
418
- type: Input
419
- }], imageCropped: [{
420
- type: Output
421
- }], fileEvent: [{
422
- type: Output
423
- }] } });
424
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidWktZmlsZS11cGxvYWRlci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy91aS9zcmMvbGliL2ZpbGUtdXBsb2FkZXIvdWktZmlsZS11cGxvYWRlci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy91aS9zcmMvbGliL2ZpbGUtdXBsb2FkZXIvdWktZmlsZS11cGxvYWRlci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULFlBQVksRUFDWixJQUFJLEVBQ0osTUFBTSxFQUNOLEtBQUssRUFHTCxRQUFRLEVBQ1IsTUFBTSxFQUNOLFFBQVEsRUFDVCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQW9CLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFckUsT0FBTyxFQUdMLGNBQWMsRUFHZixNQUFNLDZCQUE2QixDQUFDO0FBRXJDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQzFFLE9BQU8sRUFBdUIsbUJBQW1CLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQzs7Ozs7Ozs7Ozs7QUEwQjVGLE1BQU0sT0FBTyx1QkFBd0IsU0FBUSxpQkFBaUI7SUFLNUQsSUFDSSxRQUFRLENBQUMsRUFBd0I7UUFDbkMsSUFBSSxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUM7UUFDcEIsSUFBSSxPQUFPLEVBQUUsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUMzQixNQUFNLENBQUMsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssRUFBRSxDQUFDLENBQUM7WUFDckUsSUFBSSxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUNQLE9BQU8sQ0FBQyxLQUFLLENBQUMsdUJBQXVCLEdBQUcsRUFBRSxD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bUI7OzBCQUMxQixRQUFROzswQkFBSSxJQUFJOzswQkFBSSxRQUFRO3FFQWpGckMsUUFBUTtzQkFEWCxLQUFLO2dCQWVHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDRyxJQUFJO3NCQUFaLEtBQUs7Z0JBQ0csV0FBVztzQkFBbkIsS0FBSztnQkFDRyxZQUFZO3NCQUFwQixLQUFLO2dCQUNHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUNHLGVBQWU7c0JBQXZCLEtBQUs7Z0JBRUcsU0FBUztzQkFBakIsS0FBSztnQkFrQkcsZUFBZTtzQkFBdkIsS0FBSztnQkFFSSxZQUFZO3NCQUFyQixNQUFNO2dCQUNHLFNBQVM7c0JBQWxCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xyXG4gIENvbXBvbmVudCxcclxuICBFdmVudEVtaXR0ZXIsXHJcbiAgSG9zdCxcclxuICBJbmplY3QsXHJcbiAgSW5wdXQsXHJcbiAgT25EZXN0cm95LFxyXG4gIE9uSW5pdCxcclxuICBPcHRpb25hbCxcclxuICBPdXRwdXQsXHJcbiAgU2tpcFNlbGZcclxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgQ29udHJvbENvbnRhaW5lciwgTkdfVkFMVUVfQUNDRVNTT1IgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XHJcbmltcG9ydCB7IFVwbG9hZGVyT3B0aW9ucywgVXBsb2FkRmlsZSwgVXBsb2FkSW5wdXQsIFVwbG9hZE91dHB1dCB9IGZyb20gJ25neC11cGxvYWRlcic7XHJcbmltcG9ydCB7XHJcbiAgSVVpRmlsZVNlcnZpY2UsXHJcbiAgSVVpRmlsZVNlcnZpY2VNZXNzYWdlcyxcclxuICBVSV9GSUxFU0VSVklDRSxcclxuICBVaUZpbGVTZXJ2aWNlLFxyXG4gIElVaUZpbGVTaXplXHJcbn0gZnJvbSAnLi4vc2VydmljZXMvdWktZmlsZS5zZXJ2aWNlJztcclxuaW1wb3J0IHsgRGltZW5zaW9ucywgSW1hZ2VDcm9wcGVkRXZlbnQsIEltYWdlVHJhbnNmb3JtIH0gZnJvbSAnbmd4LWltYWdlLWNyb3BwZXInO1xyXG5pbXBvcnQgeyBVaVNpbXBsZUNvbXBvbmVudCB9IGZyb20gJy4uL2xheW91dHMvc2ltcGxlL3VpLXNpbXBsZS5jb21wb25lbnQnO1xyXG5pbXBvcnQgeyBJVWlUcmFuc2xhdGVTZXJ2aWNlLCBVSV9UUkFOU0xBVEVTRVJWSUNFIH0gZnJvbSAnLi4vc2VydmljZXMvdWktdHJhbnNsYXRlLnNlcnZpY2UnO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBVcGxvYWRPdXRwdXRFeCBleHRlbmRzIFVwbG9hZE91dHB1dCB7XHJcbiAgZmlsZTogVXBsb2FkRmlsZUV4O1xyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFVwbG9hZEZpbGVFeCBleHRlbmRzIFVwbG9hZEZpbGUge1xyXG4gIGltYWdlUHJldmlldz86IGFueTtcclxuICBiYXNlNjQ/OiBhbnk7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgSVVpVXBsb2FkZXJVc2VyTWVzc2FnZSB7XHJcbiAgbWVzc2FnZTogSVVpRmlsZVNlcnZpY2VNZXNzYWdlcyxcclxuICBkYXRhOiBhbnlcclxufVxyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICd1aS1maWxlLXVwbG9hZGVyJyxcclxuICB0ZW1wbGF0ZVVybDogJy4vdWktZmlsZS11cGxvYWRlci5jb21wb25lbnQuaHRtbCcsXHJcbiAgc3R5bGVVcmxzOiBbICcuL3VpLWZpbGUtdXBsb2FkZXIuY29tcG9uZW50LnNjc3MnXSxcclxuICBwcm92aWRlcnM6IFt7XHJcbiAgICBwcm92aWRlOiBOR19WQUxVRV9BQ0NFU1NPUixcclxuICAgIG11bHRpOiB0cnVlLFxyXG4gICAgdXNlRXhpc3Rpbmc6IFVpRmlsZVVwbG9hZGVyQ29tcG9uZW50LFxyXG4gIH1dXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBVaUZpbGVVcGxvYWRlckNvbXBvbmVudCBleHRlbmRzIFVpU2ltcGxlQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xyXG5cclxuICBwcml2YXRlIHVpRmlsZVNpemU6IElVaUZpbGVTaXplO1xyXG4gIF9maWxlU2l6ZTogc3RyaW5nIHwgSVVpRmlsZVNpemU7XHJcblxyXG4gIEBJbnB1dCgpXHJcbiAgc2V0IGZpbGVTaXplKGZzOiBzdHJpbmcgfCBJVWlGaWxlU2l6ZSkge1xyXG4gICAgdGhpcy5fZmlsZVNpemUgPSBmcztcclxuICAgIGlmICh0eXBlb2YgZnMgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIGNvbnN0IGYgPSB0aGlzLmZpbGVTZXJ2aWNlQ29uZmlnLmltYWdlU2l6ZXMuZmluZChuID0+IG4ubmFtZSA9PT0gZnMpO1xyXG4gICAgICBpZiAoIWYpIHtcclxuICAgICAgICBjb25zb2xlLmVycm9yKCdNaXNzaW5nIGZpbGVzaXplIGZvciAnICsgZnMpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRoaXMudWlGaWxlU2l6ZSA9IGY7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHRoaXMudWlGaWxlU2l6ZSA9IGZzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgQElucHV0KCkgbWF4RmlsZXMgPSAxO1xyXG4gIEBJbnB1dCgpIG11bHRpcGxlID0gdHJ1ZTtcclxuICBASW5wdXQoKSBjcm9wID0gZmFsc2U7XHJcbiAgQElucHV0KCkgY3JvcFJvdW5kZWQgPSBmYWxzZTtcclxuICBASW5wdXQoKSBjcm9wTWF4V2lkdGg7XHJcbiAgQElucHV0KCkgY3VzdG9tRmlsZU1lbnUgPSBmYWxzZTtcclxuICBASW5wdXQoKSBhZGRIaW50O1xyXG4gIEBJbnB1dCgpIG5ld0ZpbGVQb3NpdGlvbjogJ2ZpcnN0JyB8ICdsYXN0JyA9ICdsYXN0JztcclxuXHJcbiAgQElucHV0KCkgdGVtcGxhdGVzOiB7IG1lbnVUZW1wbGF0ZT86IGFueSwgYWRkRmlsZVRlbXBsYXRlPzogYW55LCBmaWxlVGVtcGxhdGU/OiBhbnksIGN1c3RvbUNvbnRlbnRUZW1wbGF0ZT86IGFueSB9ID0ge31cclxuXHJcbiAgZ2V0IG1lbnVUZW1wbGF0ZSgpIHtcclxuICAgIHJldHVybiB0aGlzLnRlbXBsYXRlcz8ubWVudVRlbXBsYXRlO1xyXG4gIH1cclxuXHJcbiAgZ2V0IGFkZEZpbGVUZW1wbGF0ZSgpIHtcclxuICAgIHJldHVybiB0aGlzLnRlbXBsYXRlcz8uYWRkRmlsZVRlbXBsYXRlO1xyXG4gIH1cclxuXHJcbiAgZ2V0IGN1c3RvbUNvbnRlbnRUZW1wbGF0ZSgpIHtcclxuICAgIHJldHVybiB0aGlzLnRlbXBsYXRlcz8uY3VzdG9tQ29udGVudFRlbXBsYXRlO1xyXG4gIH1cclxuXHJcbiAgZ2V0IGZpbGVUZW1wbGF0ZSgpIHtcclxuICAgIHJldHVybiB0aGlzLnRlbXBsYXRlcz8uZmlsZVRlbXBsYXRlO1xyXG4gIH1cclxuXHJcbiAgQElucHV0KCkgcHJldmlld1Bvc2l0aW9uOiAnbGVmdCcgfCAnYm90dG9tJyB8ICdhYm92ZScgPSAnYm90dG9tJztcclxuXHJcbiAgQE91dHB1dCgpIGltYWdlQ3JvcHBlZCA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcclxuICBAT3V0cHV0KCkgZmlsZUV2ZW50ID0gbmV3IEV2ZW50RW1pdHRlcjxJVWlVcGxvYWRlclVzZXJNZXNzYWdlPigpO1xyXG5cclxuICB1cGxvYWRPcHRpb25zOiBVcGxvYWRlck9wdGlvbnMgPSB7XHJcbiAgICBjb25jdXJyZW5jeTogdGhpcy5maWxlU2VydmljZUNvbmZpZy5jb25jdXJyZW50VXBsb2Fkc1xyXG4gIH07XHJcblxyXG4gIHB1YmxpYyB1cGxvYWRpbmcgPSBmYWxzZTtcclxuXHJcbiAgY2hhbmdlRmlsZUluZGV4O1xyXG5cclxuICBzaG93Q3JvcHBlciA9IGZhbHNlO1xyXG4gIG1lbnVWaXNpYmxlOiBhbnk7XHJcbiAgc2hvd0Nyb3BQcmV2aWV3ID0ge307XHJcbiAgY3JvcFByZXZpZXdUaW1lb3V0cyA9IHt9O1xyXG4gIGNyb3BTb3VyY2VJbWFnZXMgPSB7fTtcclxuICBjcm9wcGVySW1hZ2VMb2FkZWQgPSB7fTtcclxuICBpbWFnZXNEb25lID0ge307XHJcbiAgdHJhbnNmb3JtOiBJbWFnZVRyYW5zZm9ybSA9IHt9O1xyXG4gIGNhbnZhc1JvdGF0aW9uID0ge31cclxuXHJcbiAgcHVibGljIGZpbGVzIDogeyBjdXN0b20/OiBhbnksICRuZXdGaWxlPzogVXBsb2FkRmlsZSB9W107XHJcblxyXG4gIHB1YmxpYyByZXNwb25zZUZpbGVzO1xyXG5cclxuICByZWZyZXNoQWRkSW5wdXQgPSBmYWxzZTtcclxuXHJcbiAgZmlsZU1lbnVPcGVuZWQ7XHJcblxyXG4gIHBhdGNoaW5nVmFsdWVzID0gZmFsc2U7XHJcblxyXG4gIGlucHV0QXJyYXkgPSBmYWxzZTtcclxuICBkcmFnZ2luZyA9IGZhbHNlO1xyXG5cclxuICB1cGxvYWRlcklkID0gTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogMTAwMDAwMCk7XHJcblxyXG4gIGNvbnN0cnVjdG9yKEBPcHRpb25hbCgpIEBJbmplY3QoVUlfRklMRVNFUlZJQ0UpIHByaXZhdGUgZmlsZVNlcnZpY2VDb25maWc6IElVaUZpbGVTZXJ2aWNlLFxyXG4gICAgICAgICAgICAgIEBJbmplY3QoVUlfVFJBTlNMQVRFU0VSVklDRSkgcHVibGljIHRyYW5zbGF0ZVNlcnZpY2U6IElVaVRyYW5zbGF0ZVNlcnZpY2UsXHJcbiAgICAgICAgICAgICAgQE9wdGlvbmFsKCkgQEhvc3QoKSBAU2tpcFNlbGYoKSBwcm90ZWN0ZWQgY29udHJvbENvbnRhaW5lcjogQ29udHJvbENvbnRhaW5lcixcclxuICAgICAgICAgICAgICBwcml2YXRlIGZpbGVTZXJ2aWNlOiBVaUZpbGVTZXJ2aWNlKSB7XHJcblxyXG4gICAgc3VwZXIobnVsbCwgY29udHJvbENvbnRhaW5lcik7XHJcblxyXG4gICAgdGhpcy5maWxlU2VydmljZS51cGxvYWRTdWJzY3JpcHRpb24ubmV4dCh7YWN0aW9uOiAncmVnaXN0ZXInLCBkYXRhOiB0aGlzfSk7XHJcblxyXG4gICAgdGhpcy5maWxlU2VydmljZS51cGxvYWRTdWJzY3JpcHRpb24uc3Vic2NyaWJlKCh2OiBhbnkpID0+IHtcclxuICAgICAgc3dpdGNoICh2LmFjdGlvbikge1xyXG4gICAgICAgIGNhc2UgJ3VwbG9hZFN0YXJ0ZWQnOiB0aGlzLnVwbG9hZGluZyA9IHRydWU7IGJyZWFrO1xyXG4gICAgICAgIGNhc2UgJ3VwbG9hZEZhaWxlZCc6XHJcbiAgICAgICAgY2FzZSAndXBsb2FkU3VjY2Vzcyc6XHJcbiAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgdGhpcy51cGxvYWRpbmcgPSBmYWxzZTtcclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgdGhpcy5wYXRjaFZhbHVlcygpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIGdldCB1cGxvYWRJbnB1dCgpOiBFdmVudEVtaXR0ZXI8VXBsb2FkSW5wdXQ+IHtcclxuICAgIHJldHVybiB0aGlzLmZpbGVTZXJ2aWNlLnVwbG9hZElucHV0O1xyXG4gIH1cclxuXHJcbiAgY3JvcHBlZEltYWdlcyA9IHt9O1xyXG5cclxuICBnZXQgZWRpdGFibGUoKSB7XHJcbiAgICByZXR1cm4gdGhpcy5jb21wb25lbnRGb3JtQ29udHJvbC5lbmFibGVkO1xyXG4gIH1cclxuXHJcbiAgZ2V0IG5nQWRkU3R5bGUoKSB7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB3aWR0aDogTWF0aC5hYnModGhpcy51aUZpbGVTaXplLndpZHRoKSArICdweCcsXHJcbiAgICAgIGhlaWdodDogTWF0aC5hYnModGhpcy51aUZpbGVTaXplLmhlaWdodCkgKyAncHgnLFxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZ2V0IGFzcGVjdFJhdGlvKCkge1xyXG4gICAgcmV0dXJuIHRoaXMudWlGaWxlU2l6ZS53aWR0aCAvIHRoaXMudWlGaWxlU2l6ZS5oZWlnaHQ7XHJcbiAgfVxyXG5cclxuICBnZXQgYWRkRmlsZUltZ1NyYygpIHtcclxuICAgIHJldHVybiB0aGlzLmZpbGVTZXJ2aWNlQ29uZmlnLmFkZEZpbGVJbWdTcmM7XHJcbiAgfVxyXG5cclxuICBjcm9wRG9uZShmKSB7XHJcbiAgICByZXR1cm4gIWYuJG5ld0ZpbGUgfHwgdGhpcy5pbWFnZXNEb25lW2YuJG5ld0ZpbGUuaWRdO1xyXG4gIH1cclxuXHJcbiAgLy8gbmdPbkluaXQoKTogdm9pZCB7XHJcbiAgLy8gICBzdXBlci5uZ09uSW5pdCgpO1xyXG4gIC8vIH1cclxuXHJcbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XHJcbiAgICB0aGlzLmZpbGVTZXJ2aWNlLnVwbG9hZFN1YnNjcmlwdGlvbi5uZXh0KHthY3Rpb246ICdkZXN0cm95JywgZGF0YTogdGhpc30pO1xyXG4gIH1cclxuXHJcbiAgcmVtb3ZlKGY6IGFueSk6IHZvaWQge1xyXG4gICAgaWYgKGYuJG5ld0ZpbGUpIHtcclxuICAgICAgdGhpcy51cGxvYWRJbnB1dC5lbWl0KHt0eXBlOiAncmVtb3ZlJywgaWQ6IGYuJG5ld0ZpbGUuaWR9KTtcclxuICAgIH1cclxuICAgIHRoaXMuZmlsZXMuc3BsaWNlKHRoaXMuZmlsZXMuaW5kZXhPZihmKSwgMSk7XHJcbiAgICB0aGlzLnByZXBhcmVSZXNwb25zZSgpO1xyXG4gICAgLy8gdGhpcy5vbkNoYW5nZSh0aGlzLmlucHV0QXJyYXkgPyB0aGlzLmZpbGVzIDogKHRoaXMuZmlsZXMubGVuZ3RoID4gMCA/IHRoaXMuZmlsZXNbMF0gOiBudWxsKSk7XHJcbiAgICB0aGlzLmZpbGVFdmVudC5lbWl0KHsgbWVzc2FnZTogJ2ZpbGVSZW1vdmVkJywgZGF0YTogZiB9KTtcclxuLy8gICAgIHRoaXMucGF0Y2hWYWx1ZXMoKTtcclxuICB9XHJcblxyXG4gIHBhdGNoVmFsdWVzKCk6IHZvaWQge1xyXG4gICAgdGhpcy5wYXRjaGluZ1ZhbHVlcyA9IHRydWU7XHJcbiAgICAvLyBpZiAodGhpcy5pbnB1dEFycmF5KSB7XHJcbiAgICAvLyAgIHRoaXMuY29tcG9uZW50Rm9ybUNvbnRyb2wuc2V0VmFsdWUodGhpcy5maWxlcy5tYXAoZiA9PiBmLmN1c3RvbSkpO1xyXG4gICAgLy8gfSBlbHNlIHtcclxuICAgIC8vICAgdGhpcy5jb21wb25lbnRGb3JtQ29udHJvbC5zZXRWYWx1ZSh0aGlzLmZpbGVzLmxlbmd0aCA9PT0gMCA/IG51bGwgOiB0aGlzLmZpbGVzWzBdLmN1c3RvbSk7XHJcbiAgICAvLyB9XHJcbiAgICB0aGlzLnBhdGNoaW5nVmFsdWVzID0gZmFsc2U7XHJcbiAgfVxyXG5cclxuICBnZXQgaGFzRmlsZXNUb1VwbG9hZCgpOiBib29sZWFuIHtcclxuICAgIHJldHVybiAhIXRoaXMuZmlsZXMuZmluZChmID0+IGYuJG5ld0ZpbGUgJiYgZi4kbmV3RmlsZS5wcm9ncmVzcy5zdGF0dXMgIT09IDIpO1xyXG4gIH1cclxuXHJcbiAgY2FuSW1hZ2VQcmV2aWV3KHR5cGU6IHN0cmluZykge1xyXG4gICAgcmV0dXJuIHR5cGUuc3RhcnRzV2l0aCgnaW1hZ2UvJykgfHwgdHlwZS5zdGFydHNXaXRoKCd2aWRlby8nKTtcclxuICB9XHJcblxyXG4gIG9uVXBsb2FkT3V0cHV0KCRldmVudDogVXBsb2FkT3V0cHV0LCBza2lwQ3VzdG9tVmFsaWRhdGlvbiA9IGZhbHNlKTogdm9pZCB7XHJcbiAgICAvLyBjb25zb2xlLmxvZygkZXZlbnQpO1xyXG4gICAgc3dpdGNoICgkZXZlbnQudHlwZSkge1xyXG4gICAgICBjYXNlICdzdGFydCc6XHJcbiAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICBjYXNlICdkcmFnT3Zlcic6XHJcbiAgICAgICAgdGhpcy5kcmFnZ2luZyA9IHRydWU7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ2RyYWdPdXQnOlxyXG4gICAgICBjYXNlICdkcm9wJzpcclxuICAgICAgICB0aGlzLmRyYWdnaW5nID0gZmFsc2U7XHJcbiAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICBjYXNlICdhZGRlZFRvUXVldWUnOlxyXG4gICAgICAgIGlmICh0aGlzLmNyb3AgJiYgISRldmVudC5maWxlLnR5cGUuc3RhcnRzV2l0aCgnaW1hZ2UvJykpIHtcclxuICAgICAgICAgIHRoaXMudXBsb2FkSW5wdXQuZW1pdCh7IHR5cGU6ICdyZW1vdmUnLCBpZDogJGV2ZW50LmZpbGUuaWQgfSk7XHJcbiAgICAgICAgICB0aGlzLmZpbGVFdmVudC5lbWl0KHsgbWVzc2FnZTogJ2Nyb3BJbnZhbGlkRmlsZVR5cGUnLCBkYXRhOiAkZXZlbnQuZmlsZSB9KTtcclxuICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICghc2tpcEN1c3RvbVZhbGlkYXRpb24gJiYgdGhpcy5maWxlU2VydmljZUNvbmZpZy5maWxlVmFsaWRhdGlvbikge1xyXG4gICAgICAgICAgdGhpcy5maWxlU2VydmljZUNvbmZpZy5maWxlVmFsaWRhdGlvbih0aGlzLnVpRmlsZVNpemUsICRldmVudC5maWxlKS50aGVuKHZhbGlkID0+IHtcclxuICAgICAgICAgICAgaWYgKHZhbGlkKSB7XHJcbiAgICAgICAgICAgICAgdGhpcy5vblVwbG9hZE91dHB1dCgkZXZlbnQsIHRydWUpXHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgdGhpcy51cGxvYWRJbnB1dC5lbWl0KHsgdHlwZTogJ3JlbW92ZScsIGlkOiAkZXZlbnQuZmlsZS5pZCB9KTtcclxuICAgICAgICAgICAgICAvLyB0aGlzLmZpbGVFdmVudC5lbWl0KHsgbWVzc2FnZTogJ2Nyb3BJbnZhbGlkRmlsZVR5cGUnLCBkYXRhOiAkZXZlbnQuZmlsZSB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSlcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgbGV0IGY7XHJcbiAgICAgICAgICBpZiAodGhpcy5jaGFuZ2VGaWxlSW5kZXggIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgICBmID0gdGhpcy5maWxlc1t0aGlzLmNoYW5nZUZpbGVJbmRleF07XHJcbiAgICAgICAgICAgIGlmIChmLiRuZXdGaWxlKSB7XHJcbiAgICAgICAgICAgICAgdGhpcy51cGxvYWRJbnB1dC5lbWl0KHsgdHlwZTogJ3JlbW92ZScsIGlkOiBmLiRuZXdGaWxlLmlkIH0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGYuJG5ld0ZpbGUgPSAkZXZlbnQuZmlsZTtcclxuICAgICAgICAgICAgZi5jdXN0b20gPSAkZXZlbnQuZmlsZTtcclxuICAgICAgICAgICAgdGhpcy5jaGFuZ2VGaWxlSW5kZXggPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBmID0geyBjdXN0b206ICRldmVudC5maWxlLCAkbmV3RmlsZTogJGV2ZW50LmZpbGUgfTtcclxuICAgICAgICAgICAgaWYgKHRoaXMubmV3RmlsZVBvc2l0aW9uID09PSAnbGFzdCcpIHtcclxuICAgICAgICAgICAgICB0aGlzLmZpbGVzLnB1c2goZik7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgdGhpcy5maWxlcy51bnNoaWZ0KGYpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICBpZiAodGhpcy5jYW5JbWFnZVByZXZpZXcoJGV2ZW50LmZpbGUudHlwZSkpIHtcclxuICAgICAgICAgICAgaWYgKHRoaXMuY3JvcCkge1xyXG4gICAgICAgICAgICAgIHRoaXMuY2FudmFzUm90YXRpb25bJGV2ZW50LmZpbGUuaWRdID0gMDtcclxuICAgICAgICAgICAgICB0aGlzLmNyb3BTb3VyY2VJbWFnZXNbJGV2ZW50LmZpbGUuaWRdID0gJGV2ZW50LmZpbGUubmF0aXZlRmlsZTtcclxuICAgICAgICAgICAgICB0aGlzLmNyb3BwZXJJbWFnZUxvYWRlZFskZXZlbnQuZmlsZS5pZF0gPSBmYWxzZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgdGhpcy5wcmVwYXJlUmVzcG9uc2UoKTtcclxuICAgICAgICAgIHRoaXMuZmlsZUV2ZW50LmVtaXQoeyBtZXNzYWdlOiAnZmlsZUFkZGVkJywgZGF0YTogZiB9KTtcclxuICAgICAgICB9XHJcbiAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICBjYXNlICdkb25lJzpcclxuICAgICAgICBjb25zdCBkZiA9IHRoaXMuZmlsZXMuZmluZChmID0+IGYuJG5ld0ZpbGUgPT09ICRldmVudC5maWxlKTtcclxuICAgICAgICBpZiAoZGYpIHtcclxuICAgICAgICAgIGlmIChkZi4kbmV3RmlsZS5yZXNwb25zZVN0YXR1cyA9PT0gMjAwKSB7XHJcbiAgICAgICAgICAgIHRoaXMuZmlsZXNbdGhpcy5maWxlcy5pbmRleE9mKGRmKV0uY3VzdG9tID0gZGYuJG5ld0ZpbGUucmVzcG9uc2U7XHJcbiAgICAgICAgICAgIGRmLiRuZXdGaWxlID0gbnVsbDtcclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKCdlcnJvciBvbiB1cGxvYWQnLCBkZi4kZXZlbnQpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoIXRoaXMuaGFzRmlsZXNUb1VwbG9hZCkge1xyXG4gICAgICAgICAgdGhpcy5wcmVwYXJlUmVzcG9uc2UoZmFsc2UpO1xyXG4gICAgICAgICAgdGhpcy5maWxlU2VydmljZS51cGxvYWRTdWJzY3JpcHRpb24ubmV4dCh7XHJcbiAgICAgICAgICAgIGFjdGlvbjogdGhpcy5maWxlcy5maW5kKGYgPT4gISFmLiRuZXdGaWxlKSA/ICd1cGxvYWRDb250cm9sRmFpbGVkJyA6ICd1cGxvYWRDb250cm9sU3VjY2VzcydcclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuICAgICAgICBicmVhaztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHByZXBhcmVSZXNwb25zZShkb2VtaXQgPSB0cnVlKSB7XHJcbiAgICBpZiAodGhpcy5pbnB1dEFycmF5KSB7XHJcbiAgICAgIGlmICghdGhpcy5yZXNwb25zZUZpbGVzKSB7XHJcbiAgICAgICAgdGhpcy5yZXNwb25zZUZpbGVzID0gW107XHJcbiAgICAgIH1cclxuICAgICAgdGhpcy5yZXNwb25zZUZpbGVzLmxlbmd0aCA9IDA7XHJcbiAgICAgIHRoaXMuZmlsZXMuZm9yRWFjaChmID0+IHRoaXMucmVzcG9uc2VGaWxlcy5wdXNoKHsuLi5mLmN1c3RvbSwgJG5ld0ZpbGU6IGYuJG5ld0ZpbGV9KSk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBpZiAodGhpcy5maWxlcy5sZW5ndGggPT09IDApIHtcclxuICAgICAgICB0aGlzLnJlc3BvbnNlRmlsZXMgPSBudWxsO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGlmICghdGhpcy5yZXNwb25zZUZpbGVzKSB7XHJcbiAgICAgICAgICB0aGlzLnJlc3BvbnNlRmlsZXMgPSB7fTtcclxuICAgICAgICB9XHJcbiAgICAgICAgT2JqZWN0LmtleXModGhpcy5maWxlc1swXS5jdXN0b20pLmZvckVhY2goayA9PiB7XHJcbiAgICAgICAgICB0aGlzLnJlc3BvbnNlRmlsZXNba10gPSB0aGlzLmZpbGVzWzBdLmN1c3RvbVtrXTtcclxuICAgICAgICB9KTtcclxuICAgICAgICB0aGlzLnJlc3BvbnNlRmlsZXMuJG5ld0ZpbGUgPSB0aGlzLmZpbGVzWzBdLiRuZXdGaWxlO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpZiAoZG9lbWl0KSB7XHJcbiAgICAgIHRoaXMub25DaGFuZ2UodGhpcy5yZXNwb25zZUZpbGVzKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBjb25zb2xlLmxvZygnZW1pdCcsIHRoaXMuZmlsZXNbMV0sIHsgLi4udGhpcy5maWxlc1sxXS5jdXN0b20sICRuZXdGaWxlOiB0aGlzLmZpbGVzWzFdLiRuZXdGaWxlIH0pO1xyXG4gICAgLy8gdGhpcy5vbkNoYW5nZSh0aGlzLmlucHV0QXJyYXkgP1xyXG4gICAgLy8gICB0aGlzLmZpbGVzLm1hcChmID0+ICh7IC4uLmYuY3VzdG9tLCAkbmV3RmlsZTogZi4kbmV3RmlsZX0pKSA6XHJcbiAgICAvLyAgICh0aGlzLmZpbGVzLmxlbmd0aCA+IDAgPyB7IC4uLnRoaXMuZmlsZXNbMF0uY3VzdG9tLCAkbmV3RmlsZTogdGhpcy5maWxlc1swXS4kbmV3RmlsZSB9IDogbnVsbCkpO1xyXG4gICAgLy8gdGhpcy5vbkNoYW5nZSh0aGlzLmlucHV0QXJyYXkgP1xyXG4gICAgLy8gICB0aGlzLmZpbGVzIDpcclxuICAgIC8vICAgKHRoaXMuZmlsZXMubGVuZ3RoID4gMCA/IHRoaXMuZmlsZXNbMF0gOiBudWxsKSk7XHJcbiAgfVxyXG5cclxuICBiZWZvcmVTYXZlKCkge1xyXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXMpID0+IHtcclxuICAgICAgdGhpcy5maWxlcy5mb3JFYWNoKGYgPT4ge1xyXG4gICAgICAgIGlmIChmLiRuZXdGaWxlICYmIGYuJG5ld0ZpbGUucHJvZ3Jlc3Muc3RhdHVzID09PSAyKSB7XHJcbiAgICAgICAgICBmLiRuZXdGaWxlLnByb2dyZXNzLnN0YXR1cyA9IDA7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmICh0aGlzLmNyb3AgJiYgZi4kbmV3RmlsZSAmJiAhdGhpcy5pbWFnZXNEb25lW2YuJG5ld0ZpbGUuaWRdKSB7XHJcbiAgICAgICAgICB0aGlzLmNyb3BwZXJEb25lKGYuJG5ld0ZpbGUuaWQpO1xyXG4gICAgICAgIH1cclxuICAgICAgfSlcclxuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgICAgcmVzKCdkb25lJyk7XHJcbiAgICAgIH0sIDI1MCk7XHJcbiAgICB9KVxyXG4gIH1cclxuXHJcbiAgZmlsZUNoYW5nZUV2ZW50KGV2ZW50OiBhbnksIGY6IGFueSk6IHZvaWQge1xyXG4gICAgdGhpcy5jaGFuZ2VGaWxlSW5kZXggPSB0aGlzLmZpbGVzLmluZGV4T2YoZik7XHJcbiAgfVxyXG5cclxuICBzdGFydENyb3BQcmV2aWV3KGlkKSB7XHJcbiAgICBjbGVhclRpbWVvdXQodGhpcy5jcm9wUHJldmlld1RpbWVvdXRzW2lkXSk7XHJcbiAgICB0aGlzLnNob3dDcm9wUHJldmlld1tpZF0gPSAhIXRoaXMuY3JvcFNvdXJjZUltYWdlc1tpZF07XHJcbiAgfVxyXG5cclxuICBzdG9wQ3JvcFByZXZpZXcoaWQpIHtcclxuICAgIHRoaXMuY3JvcFByZXZpZXdUaW1lb3V0c1tpZF0gPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgdGhpcy5zaG93Q3JvcFByZXZpZXdbaWRdID0gZmFsc2U7XHJcbiAgICB9LCAyNTApO1xyXG4gIH1cclxuXHJcbiAgem9vbUNyb3BwZWRJbWFnZShkaXIsIGlkKSB7XHJcbiAgICBsZXQgc2NhbGUgPSB0aGlzLnRyYW5zZm9ybVtpZF0uc2NhbGU7XHJcbiAgICBzY2FsZSArPSBkaXI7XHJcbiAgICBpZiAoc2NhbGUgPCAwLjEpIHtcclxuICAgICAgc2NhbGUgPSAwLjE7XHJcbiAgICB9XHJcbiAgICB0aGlzLnRyYW5zZm9ybVtpZF0gPSB7XHJcbiAgICAgIC4uLnRoaXMudHJhbnNmb3JtW2lkXSxcclxuICAgICAgc2NhbGVcclxuICAgIH07XHJcbiAgfVxyXG5cclxuICByb3RhdGVDcm9wcGVkSW1hZ2UoZGlyLCBpZCkge1xyXG4gICAgdGhpcy5jYW52YXNSb3RhdGlvbltpZF0gKz0gZGlyO1xyXG4gICAgY29uc3QgZmxpcHBlZEggPSB0aGlzLnRyYW5zZm9ybVtpZF0uZmxpcEg7XHJcbiAgICBjb25zdCBmbGlwcGVkViA9IHRoaXMudHJhbnNmb3JtW2lkXS5mbGlwVjtcclxuICAgIHRoaXMudHJhbnNmb3JtW2lkXSA9IHtcclxuICAgICAgLi4udGhpcy50cmFuc2Zvcm1baWRdLFxyXG4gICAgICBmbGlwSDogZmxpcHBlZFYsXHJcbiAgICAgIGZsaXBWOiBmbGlwcGVkSFxyXG4gICAgfTtcclxuICB9XHJcblxyXG4gIG9uSW1hZ2VDcm9wcGVkKGV2ZW50OiBJbWFnZUNyb3BwZWRFdmVudCwgaWQpIHtcclxuICAgIHRoaXMuY3JvcHBlZEltYWdlc1tpZF0gPSBldmVudDtcclxuICAgIHRoaXMuaW1hZ2VDcm9wcGVkLmVtaXQoZXZlbnQpO1xyXG4gIH1cclxuXHJcbiAgaW1hZ2VMb2FkZWQoaWQpIHtcclxuICAgIHRoaXMuc2hvd0Nyb3BwZXIgPSB0cnVlO1xyXG4gICAgc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgIHRoaXMudHJhbnNmb3JtW2lkXSA9IHtcclxuICAgICAgICBzY2FsZTogMVxyXG4gICAgICB9O1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBjcm9wcGVyUmVhZHkoc291cmNlSW1hZ2VEaW1lbnNpb25zOiBEaW1lbnNpb25zLCBpZCkge1xyXG4gICAgdGhpcy5jcm9wcGVySW1hZ2VMb2FkZWRbaWRdID0gdHJ1ZTtcclxuICB9XHJcblxyXG4gIGxvYWRJbWFnZUZhaWxlZCgpIHtcclxuICB9XHJcblxyXG4gIHRvZ2dsZU1lbnVWaXNpYmlsaXR5KGYpIHtcclxuICAgIHRoaXMuZmlsZU1lbnVPcGVuZWQgPSBmO1xyXG4gIH1cclxuXHJcbiAgbWVudUNsaWNrKCkge1xyXG4gICAgdGhpcy5maWxlTWVudU9wZW5lZCA9IHVuZGVmaW5lZDtcclxuICB9XHJcblxyXG5cclxuICB3cml0ZVZhbHVlKG9iajogYW55KTogdm9pZCB7XHJcbiAgICB0aGlzLmlucHV0QXJyYXkgPSBvYmogaW5zdGFuY2VvZiBBcnJheTtcclxuICAgIGlmICh0aGlzLmlucHV0QXJyYXkpIHtcclxuICAgICAgdGhpcy5maWxlcyA9IG9iai5tYXAoZiA9PiAoeyBjdXN0b206IGYsICRuZXdGaWxlOiBudWxsIH0pKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHRoaXMuZmlsZXMgPSBvYmogPyBbIHsgY3VzdG9tOm9iaiwgJG5ld0ZpbGU6IG51bGx9IF0gOiBbXTtcclxuICAgIH1cclxuICAgIHRoaXMuY3JvcFNvdXJjZUltYWdlcyA9IHt9O1xyXG4gIH1cclxuXHJcbiAgY3JvcHBlckRvbmUoaWQpIHtcclxuICAgIGNvbnN0IGYgPSB0aGlzLmZpbGVzLmZpbmQoZnggPT4gZnguJG5ld0ZpbGUgJiYgZnguJG5ld0ZpbGUuaWQgPT09IGlkKTtcclxuICAgIGlmIChmKSB7XHJcbiAgICAgIC8vIGNvbnN0IHVybCA9IHRoaXMuY3JvcHBlZEltYWdlc1tpZF07XHJcbiAgICAgIC8vIGNvbnNvbGUubG9nKHVybCk7XHJcbiAgICAgIC8vIGZldGNoKHVybClcclxuICAgICAgLy8gICAudGhlbihyZXMgPT4gcmVzLmJsb2IoKSlcclxuICAgICAgLy8gICAudGhlbihibG9iID0+IHtcclxuICAgICAgLy8gICAgIGYuJG5ld0ZpbGUubmF0aXZlRmlsZSA9IG5ldyBGaWxlKFsgYmxvYiBdLCAnY3JvcC5wbmcnLCB7XHJcbiAgICAgIC8vICAgICAgIHR5cGU6ICdpbWFnZS9wbmcnXHJcbiAgICAgIC8vICAgICB9KTtcclxuICAgICAgLy8gICAgIGYuJG5ld0ZpbGUubmFtZSA9ICdjcm9wLnBuZyc7XHJcbiAgICAgIC8vICAgICBmLiRuZXdGaWxlLnNpemUgPSBmLiRuZXdGaWxlLm5hdGl2ZUZpbGUuc2l6ZTtcclxuICAgICAgLy8gICAgIGYuJG5ld0ZpbGUudHlwZSA9IGYuJG5ld0ZpbGUubmF0aXZlRmlsZS50eXBlO1xyXG4gICAgICAvLyAgICAgdGhpcy5pbWFnZXNEb25lW2lkXSA9IHRydWU7XHJcbiAgICAgIC8vICAgICBjb25zb2xlLmxvZyhmKTtcclxuICAgICAgLy8gICB9KVxyXG4gICAgICBjb25zdCBjcm9wcGVkSW1hZ2UgPSB0aGlzLmNyb3BwZWRJbWFnZXNbaWRdO1xyXG4gICAgICBmLiRuZXdGaWxlLm5hdGl2ZUZpbGUgPSBuZXcgRmlsZShbIGNyb3BwZWRJbWFnZS5ibG9iIF0sICdjcm9wLnBuZycsIHtcclxuICAgICAgICB0eXBlOiAnaW1hZ2UvcG5nJ1xyXG4gICAgICB9KTtcclxuICAgICAgZi4kbmV3RmlsZS5uYW1lID0gJ2Nyb3AucG5nJztcclxuICAgICAgZi4kbmV3RmlsZS5zaXplID0gZi4kbmV3RmlsZS5uYXRpdmVGaWxlLnNpemU7XHJcbiAgICAgIGYuJG5ld0ZpbGUudHlwZSA9IGYuJG5ld0ZpbGUubmF0aXZlRmlsZS50eXBlO1xyXG4gICAgICB0aGlzLmltYWdlc0RvbmVbaWRdID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHVwbG9hZEltZ1NyYygpIHtcclxuICAgIHJldHVybiB0aGlzLmFkZEZpbGVJbWdTcmModGhpcy51aUZpbGVTaXplKVxyXG4gIH1cclxufVxyXG4iLCI8ZGl2IGNsYXNzPVwibGFiZWxcIiAqbmdJZj1cImxhYmVsXCI+XHJcbiAgICA8ZGl2IGNsYXNzPVwidGV4dC1jb250YWluZXJcIlxyXG4gICAgICAgICBbbWF0VG9vbHRpcF09XCJyZXF1aXJlZCA/IHRyYW5zbGF0ZVNlcnZpY2UuaW5zdGFudCgndWkuY29udHJvbHMudmFsaWRhdGlvbi5yZXF1aXJlZCcpIDogdW5kZWZpbmVkXCJcclxuICAgICAgICAgW21hdFRvb2x0aXBQb3NpdGlvbl09XCInYWJvdmUnXCI+XHJcbiAgICAgICAgPGRpdiBjbGFzcz1cInRleHRcIj57eyBsYWJlbCB9fTwvZGl2PlxyXG4gICAgICAgIDxkaXYgKm5nSWY9XCJyZXF1aXJlZFwiIGNsYXNzPVwicmVxdWlyZWRcIj4qPC9kaXY+XHJcbiAgICA8L2Rpdj5cclxuPC9kaXY+XHJcblxyXG48ZGl2IGNsYXNzPVwiZmlsZXMtY29udGFpbmVyXCIgW25nQ2xhc3NdPVwie2RyYWdnaW5nOiBkcmFnZ2luZywgZGlzYWJsZWQ6IHVwbG9hZGluZyB8fCAhZWRpdGFibGV9XCI+XHJcblxyXG4gICAgPGRpdiBjbGFzcz1cImV4aXN0aW5nLWZpbGVzXCI+XHJcblxyXG4gICAgICAgIDxkaXYgY2xhc3M9XCJmaWxlLWNvbnRhaW5lclwiICpuZ0Zvcj1cImxldCBmIG9mIGZpbGVzXCI+XHJcblxyXG4gICAgICAgICAgICBAaWYgKGN1c3RvbUNvbnRlbnRUZW1wbGF0ZSkge1xyXG4gICAgICAgICAgICAgICAgPG5nLXRlbXBsYXRlIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImN1c3RvbUNvbnRlbnRUZW1wbGF0ZVwiIFtuZ1RlbXBsYXRlT3V0bGV0Q29udGV4dF09XCJ7ZmlsZVVwbG9hZGVyOiB0aGlzLCBmaWxlOiBmfVwiPjwvbmctdGVtcGxhdGU+XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmaWxlLW1lbnVcIiAoY2xpY2spPVwidG9nZ2xlTWVudVZpc2liaWxpdHkoZilcIiAqbmdJZj1cIiFjdXN0b21GaWxlTWVudSAmJiBlZGl0YWJsZSAmJiAhdXBsb2FkaW5nXCI+XHJcbiAgICAgICAgICAgICAgICA8bWF0LWljb24+bWVudTwvbWF0LWljb24+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZmlsZS1tZW51LWNvbnRhaW5lciBtYXQtZWxldmF0aW9uLXoyXCIgW25nQ2xhc3NdPVwie3Zpc2libGU6IGYgPT09IGZpbGVNZW51T3BlbmVkfVwiXHJcbiAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm1lbnVDbGljaygpXCIgKG1vdXNlbGVhdmUpPVwibWVudUNsaWNrKClcIj5cclxuICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIhbWVudVRlbXBsYXRlXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzPVwibWVudS1pdGVtXCI+e3sgdHJhbnNsYXRlU2VydmljZS5pbnN0YW50KCd1aS5jb250cm9scy51aS1maWxlLXVwbG9hZGVyLm1lbnUuY2hhbmdlJykgfX1cclxuICAgICAgICAgICAgICAgICAgICAgICAgPGlucHV0ICpuZ0lmPVwiIXJlZnJlc2hBZGRJbnB1dFwiIHN0eWxlPVwiZGlzcGxheTogbm9uZTtcIiB0eXBlPVwiZmlsZVwiIG5nRmlsZVNlbGVjdFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW29wdGlvbnNdPVwidXBsb2FkT3B0aW9uc1wiIChjaGFuZ2UpPVwiZmlsZUNoYW5nZUV2ZW50KCRldmVudCwgZilcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHVwbG9hZE91dHB1dCk9XCJvblVwbG9hZE91dHB1dCgkZXZlbnQpXCIgW3VwbG9hZElucHV0XT1cInVwbG9hZElucHV0XCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9sYWJlbD5cclxuICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWVudS1pdGVtXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJyZW1vdmUoZilcIj57eyB0cmFuc2xhdGVTZXJ2aWNlLmluc3RhbnQoJ3VpLmNvbnRyb2xzLnVpLWZpbGUtdXBsb2FkZXIubWVudS5kZWxldGUnKSB9fVxyXG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XHJcbiAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwibWVudVRlbXBsYXRlXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPG5nLXRlbXBsYXRlIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIm1lbnVUZW1wbGF0ZVwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtuZ1RlbXBsYXRlT3V0bGV0Q29udGV4dF09XCJ7ZmlsZUlucHV0OiBmaWxlSW5wdXR9XCI+PC9uZy10ZW1wbGF0ZT5cclxuICAgICAgICAgICAgICAgICAgICA8bmctdGVtcGxhdGUgI2ZpbGVJbnB1dD5cclxuICAgICAgICAgICAgICAgICAgICAgICAgPGlucHV0IHN0eWxlPVwiZGlzcGxheTogbm9uZTtcIiB0eXBlPVwiZmlsZVwiIG5nRmlsZVNlbGVjdCBbb3B0aW9uc109XCJ1cGxvYWRPcHRpb25zXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChjaGFuZ2UpPVwiZmlsZUNoYW5nZUV2ZW50KCRldmVudCwgZilcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHVwbG9hZE91dHB1dCk9XCJvblVwbG9hZE91dHB1dCgkZXZlbnQpXCIgW3VwbG9hZElucHV0XT1cInVwbG9hZElucHV0XCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cclxuICAgICAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxyXG4gICAgICAgICAgICA8L2Rpdj5cclxuXHJcbiAgICAgICAgICAgIDxkaXYgKm5nSWY9XCJjcm9wICYmICFjcm9wRG9uZShmKVwiIChtb3VzZWVudGVyKT1cInN0YXJ0Q3JvcFByZXZpZXcoZi4kbmV3RmlsZS5pZClcIlxyXG4gICAgICAgICAgICAgICAgIChtb3VzZWxlYXZlKT1cInN0b3BDcm9wUHJldmlldyhmLiRuZXdGaWxlLmlkKVwiPlxyXG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImNyb3AtY29udGFpbmVyXCIgW25nU3R5bGVdPVwibmdBZGRTdHlsZVwiPlxyXG5cclxuICAgICAgICAgICAgICAgICAgICA8dWktZmlsZSAqbmdJZj1cImZhbHNlICYmIGZpbGVzLmxlbmd0aCA+IDAgJiYgIWNyb3BTb3VyY2VJbWFnZXNcIiBbZmlsZVNpemVdPVwibnVsbFwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW3NyY0RhdGFdPVwiZmlsZXNbMF0uY3VzdG9tXCIgW25nQ2xhc3NdPVwie3JvdW5kZWQ6IGNyb3BSb3VuZGVkfVwiPjwvdWktZmlsZT5cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgPG1hdC1zcGlubmVyICpuZ0lmPVwiIWNyb3BwZXJJbWFnZUxvYWRlZFtmLiRuZXdGaWxlLmlkXSAmJiBjcm9wU291cmNlSW1hZ2VzW2YuJG5ld0ZpbGUuaWRdXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2RpYW1ldGVyXT1cIjc1XCI+PC9tYXQtc3Bpbm5lcj5cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgPGltYWdlLWNyb3BwZXIgKm5nSWY9XCJjcm9wU291cmNlSW1hZ2VzW2YuJG5ld0ZpbGUuaWRdXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbaW1hZ2VGaWxlXT1cImNyb3BTb3VyY2VJbWFnZXNbZi4kbmV3RmlsZS5pZF1cIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFttYWludGFpbkFzcGVjdFJhdGlvXT1cInRydWVcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtjb250YWluV2l0aGluQXNwZWN0UmF0aW9dPVwiZmFsc2VcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFthc3BlY3RSYXRpb109XCJhc3BlY3RSYXRpb1wiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW3Jlc2l6ZVRvV2lkdGhdPVwiY3JvcE1heFdpZHRoXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbb25seVNjYWxlRG93bl09XCJmYWxzZVwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW3JvdW5kQ3JvcHBlcl09XCJjcm9wUm91bmRlZFwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2NhbnZhc1JvdGF0aW9uXT1cImNhbnZhc1JvdGF0aW9uW2YuJG5ld0ZpbGUuaWRdXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbdHJhbnNmb3JtXT1cInRyYW5zZm9ybVtmLiRuZXdGaWxlLmlkXVwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2FsaWduSW1hZ2VdPVwiJ2NlbnRlcidcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtzdHlsZS5kaXNwbGF5XT1cInNob3dDcm9wcGVyID8gbnVsbCA6ICdub25lJ1wiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2Zvcm1hdF09XCIncG5nJ1wiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGltYWdlQ3JvcHBlZCk9XCJvbkltYWdlQ3JvcHBlZCgkZXZlbnQsIGYuJG5ld0ZpbGUuaWQpXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoaW1hZ2VMb2FkZWQpPVwiaW1hZ2VMb2FkZWQoZi4kbmV3RmlsZS5pZClcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChjcm9wcGVyUmVhZHkpPVwiY3JvcHBlclJlYWR5KCRldmVudCwgZi4kbmV3RmlsZS5pZClcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChsb2FkSW1hZ2VGYWlsZWQpPVwibG9hZEltYWdlRmFpbGVkKClcIlxyXG4gICAgICAgICAgICAgICAgICAgID48L2ltYWdlLWNyb3BwZXI+XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjcm9wLXByZXZpZXcgbWF0LWVsZXZhdGlvbi16MlwiIFtjbGFzc109XCJwcmV2aWV3UG9zaXRpb25cIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgKm5nSWY9XCJ0cnVlIHx8IChzaG93Q3JvcFByZXZpZXdbZi4kbmV3RmlsZS5pZF0gJiYgY3JvcHBlckltYWdlTG9hZGVkW2YuJG5ld0ZpbGUuaWRdKVwiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiY3JvcC1tZW51XCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8bWF0LWljb24gKGNsaWNrKT1cInpvb21Dcm9wcGVkSW1hZ2UoLjEsIGYuJG5ld0ZpbGUuaWQpXCI+em9vbV9pbjwvbWF0LWljb24+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8bWF0LWljb24gKGNsaWNrKT1cInpvb21Dcm9wcGVkSW1hZ2UoLSAuMSwgZi4kbmV3RmlsZS5pZClcIj56b29tX291dDwvbWF0LWljb24+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8bWF0LWljb24gKGNsaWNrKT1cInJvdGF0ZUNyb3BwZWRJbWFnZSgtMSwgZi4kbmV3RmlsZS5pZClcIj5yb3RhdGVfbGVmdDwvbWF0LWljb24+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8bWF0LWljb24gKGNsaWNrKT1cInJvdGF0ZUNyb3BwZWRJbWFnZSgxLCBmLiRuZXdGaWxlLmlkKVwiPnJvdGF0ZV9yaWdodDwvbWF0LWljb24+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IHN0eWxlPVwiZmxleDogMSAxIDEwMCVcIj48L2Rpdj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsYWJlbCBjbGFzcz1cIm1lbnUtaXRlbVwiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxtYXQtaWNvbj51cGxvYWRfZmlsZTwvbWF0LWljb24+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGlucHV0IHN0eWxlPVwiZGlzcGxheTogbm9uZTtcIiB0eXBlPVwiZmlsZVwiIG5nRmlsZVNlbGVjdCBbb3B0aW9uc109XCJ1cGxvYWRPcHRpb25zXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGNoYW5nZSk9XCJmaWxlQ2hhbmdlRXZlbnQoJGV2ZW50LCBmKVwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICh1cGxvYWRPdXRwdXQpPVwib25VcGxvYWRPdXRwdXQoJGV2ZW50KVwiIFt1cGxvYWRJbnB1dF09XCJ1cGxvYWRJbnB1dFwiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sYWJlbD5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxtYXQtaWNvbiAoY2xpY2spPVwicmVtb3ZlKGYpXCI+ZGVsZXRlX291dGxpbmU8L21hdC1pY29uPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBzdHlsZT1cImZsZXg6IDEgMSAxMDAlXCI+PC9kaXY+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8bWF0LWljb24gKGNsaWNrKT1cImNyb3BwZXJEb25lKGYuJG5ld0ZpbGUuaWQpXCI+ZG9uZTwvbWF0LWljb24+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8aW1nICpuZ0lmPVwiY3JvcHBlZEltYWdlc1tmLiRuZXdGaWxlLmlkXVwiIFtzcmNdPVwiY3JvcHBlZEltYWdlc1tmLiRuZXdGaWxlLmlkXS5vYmplY3RVcmxcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtuZ0NsYXNzXT1cIntyb3VuZGVkOiBjcm9wUm91bmRlZH1cIi8+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9kaXY+XHJcblxyXG4gICAgICAgICAgICAgICAgPC9kaXY+XHJcblxyXG5cclxuICAgICAgICAgICAgPC9kaXY+XHJcblxyXG4gICAgICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiIWNyb3AgfHwgY3JvcERvbmUoZilcIj5cclxuICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJmaWxlVGVtcGxhdGVcIj5cclxuICAgICAgICAgICAgICAgICAgICA8bmctdGVtcGxhdGUgW25nVGVtcGxhdGVPdXRsZXRdPVwiZmlsZVRlbXBsYXRlXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW25nVGVtcGxhdGVPdXRsZXRDb250ZXh0XT1cIntmaWxlOiBmLCBmaWxlSW5wdXQ6IGZpbGVJbnB1dDJ9XCI+PC9uZy10ZW1wbGF0ZT5cclxuICAgICAgICAgICAgICAgICAgICA8bmctdGVtcGxhdGUgI2ZpbGVJbnB1dDI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDxpbnB1dCBzdHlsZT1cImRpc3BsYXk6IG5vbmU7XCIgdHlwZT1cImZpbGVcIiBuZ0ZpbGVTZWxlY3QgW29wdGlvbnNdPVwidXBsb2FkT3B0aW9uc1wiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoY2hhbmdlKT1cImZpbGVDaGFuZ2VFdmVudCgkZXZlbnQsIGYpXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICh1cGxvYWRPdXRwdXQpPVwib25VcGxvYWRPdXRwdXQoJGV2ZW50KVwiIFt1cGxvYWRJbnB1dF09XCJ1cGxvYWRJbnB1dFwiPlxyXG4gICAgICAgICAgICAgICAgICAgIDwvbmctdGVtcGxhdGU+XHJcbiAgICAgICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cclxuXHJcbiAgICAgICAgICAgICAgICA8dWktZmlsZSAqbmdJZj1cIiFmaWxlVGVtcGxhdGVcIiBbZmlsZVNpemVdPVwiX2ZpbGVTaXplXCIgW3NyY0RhdGFdPVwiZi4kbmV3RmlsZSA/IGYuJG5ld0ZpbGU6IGYuY3VzdG9tXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgIFtuZ0NsYXNzXT1cIntyb3VuZGVkOiBjcm9wICYmIGNyb3BSb3VuZGVkfVwiPlxyXG4gICAgICAgICAgICAgICAgPC91aS1maWxlPlxyXG5cclxuICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJmLiRuZXdGaWxlXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPGRpdiAqbmdJZj1cImYuJG5ld0ZpbGUucmVzcG9uc2VTdGF0dXMgJiYgZi4kbmV3RmlsZS5yZXNwb25zZVN0YXR1cyAhPT0gMjAwXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwicHJvZ3Jlc3MtY29udGFpbmVyIGVycm9yXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IHRyYW5zbGF0ZVNlcnZpY2UuaW5zdGFudCgndWkuY29udHJvbHMudWktZmlsZS11cGxvYWRlci51cGxvYWRFcnJvcicsIGYuJG5ld0ZpbGUucmVzcG9uc2UpIH19XHJcbiAgICAgICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgICAgICAgICAgPGRpdiAqbmdJZj1cIiFmLiRuZXdGaWxlLnJlc3BvbnNlU3RhdHVzICYmIGYuJG5ld0ZpbGUucHJvZ3Jlc3Muc3RhdHVzID09PSAxXCJcclxuICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwicHJvZ3Jlc3MtY29udGFpbmVyXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJwcm9ncmVzc1wiIFtuZ1N0eWxlXT1cInt3aWR0aDogZi4kbmV3RmlsZS5wcm9ncmVzcy5kYXRhLnBlcmNlbnRhZ2UgKyAnJSd9XCI+PC9kaXY+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cclxuICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XHJcbiAgICAgICAgPC9kaXY+XHJcblxyXG4gICAgPC9kaXY+XHJcblxyXG4gICAgPGRpdiBjbGFzcz1cImZpbGUtY29udGFpbmVyIGFkZFwiIG5nRmlsZURyb3AgW29wdGlvbnNdPVwidXBsb2FkT3B0aW9uc1wiICh1cGxvYWRPdXRwdXQpPVwib25VcGxvYWRPdXRwdXQoJGV2ZW50KVwiXHJcbiAgICAgICAgIFt1cGxvYWRJbnB1dF09XCJ1cGxvYWRJbnB1dFwiIFtuZ0NsYXNzXT1cInt2aXNpYmxlOiBmaWxlcy5sZW5ndGggPCBtYXhGaWxlcyB8fCBtYXhGaWxlcyA9PT0gMH1cIlxyXG4gICAgICAgICBbbmdTdHlsZV09XCJhZGRGaWxlVGVtcGxhdGUgPyB7fSA6IG5nQWRkU3R5bGVcIj5cclxuICAgICAgICA8bGFiZWw+XHJcbiAgICAgICAgICAgIEBpZiAoYWRkRmlsZVRlbXBsYXRlKSB7XHJcbiAgICAgICAgICAgICAgICA8bmctdGVtcGxhdGUgW25nVGVtcGxhdGVPdXRsZXRdPVwiYWRkRmlsZVRlbXBsYXRlXCI+PC9uZy10ZW1wbGF0ZT5cclxuICAgICAgICAgICAgfSBAZWxzZSB7XHJcbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidXBsb2FkLWltYWdlLWNvbnRhaW5lclwiPjwvZGl2PlxyXG4gICAgICAgICAgICAgICAgPGltZyBbc3JjXT1cInVwbG9hZEltZ1NyYygpXCIgW25nQ2xhc3NdPVwieyd3aXRoLWhpbnQnOiBhZGRIaW50fVwiLz5cclxuICAgICAgICAgICAgICAgIDxkaXYgKm5nSWY9XCJhZGRIaW50XCIgY2xhc3M9XCJhZGQtaGludFwiPnt7IGFkZEhpbnQgfX08L2Rpdj5cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA8aW5wdXQgc3R5bGU9XCJkaXNwbGF5OiBub25lO1wiIHR5cGU9XCJmaWxlXCIgbmdGaWxlU2VsZWN0IFtvcHRpb25zXT1cInVwbG9hZE9wdGlvbnNcIlxyXG4gICAgICAgICAgICAgICAgICAgKHVwbG9hZE91dHB1dCk9XCJvblVwbG9hZE91dHB1dCgkZXZlbnQpXCIgW3VwbG9hZElucHV0XT1cInVwbG9hZElucHV0XCJcclxuICAgICAgICAgICAgICAgICAgIFttdWx0aXBsZV09XCJtYXhGaWxlcyA9PT0gMCAmJiBtdWx0aXBsZVwiPlxyXG4gICAgICAgIDwvbGFiZWw+XHJcbiAgICA8L2Rpdj5cclxuXHJcbjwvZGl2PlxyXG4iXX0=