@libs-ui/components-image-editor 0.2.306 → 0.2.307-0
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.
- package/README.md +50 -45
- package/defines/image-editor.define.d.ts +1 -1
- package/demo/image-editor-demo.component.d.ts +2 -0
- package/esm2022/defines/image-editor.define.mjs +14 -14
- package/esm2022/demo/image-editor-demo.component.mjs +29 -26
- package/esm2022/image-editor.component.mjs +54 -39
- package/esm2022/index.mjs +1 -1
- package/esm2022/interfaces/function-control-event.interface.mjs +1 -1
- package/esm2022/interfaces/image-editor.interface.mjs +1 -1
- package/esm2022/resize/resize.component.mjs +21 -20
- package/fesm2022/libs-ui-components-image-editor.mjs +108 -89
- package/fesm2022/libs-ui-components-image-editor.mjs.map +1 -1
- package/image-editor.component.d.ts +1 -0
- package/package.json +9 -9
- package/resize/resize.component.d.ts +5 -5
package/README.md
CHANGED
|
@@ -53,18 +53,17 @@ import { LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-
|
|
|
53
53
|
[modeShowButton]="'save-file'"
|
|
54
54
|
[nameFile]="'image.jpg'"
|
|
55
55
|
(outSaveFile)="onSaveFile($event)"
|
|
56
|
-
(outClose)="onClose($event)">
|
|
57
|
-
|
|
58
|
-
`
|
|
56
|
+
(outClose)="onClose($event)"></libs_ui-components-image_editor>
|
|
57
|
+
`,
|
|
59
58
|
})
|
|
60
59
|
export class ExampleComponent {
|
|
61
60
|
imageSource = 'https://example.com/path/to/image.jpg';
|
|
62
61
|
|
|
63
|
-
onSaveFile(data: {file: Blob
|
|
62
|
+
onSaveFile(data: { file: Blob; url: string; mode: string }) {
|
|
64
63
|
console.log('File đã được lưu:', data);
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
onClose(data: {isClickButtonClose: boolean}) {
|
|
66
|
+
onClose(data: { isClickButtonClose: boolean }) {
|
|
68
67
|
console.log('Đã đóng trình chỉnh sửa ảnh');
|
|
69
68
|
}
|
|
70
69
|
}
|
|
@@ -81,16 +80,16 @@ import { LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-
|
|
|
81
80
|
selector: 'app-example',
|
|
82
81
|
standalone: true,
|
|
83
82
|
imports: [LibsUiComponentsImageEditorComponent],
|
|
84
|
-
templateUrl: './example.component.html'
|
|
83
|
+
templateUrl: './example.component.html',
|
|
85
84
|
})
|
|
86
85
|
export class ExampleComponent {
|
|
87
86
|
imageSource = 'https://example.com/path/to/image.jpg';
|
|
88
87
|
|
|
89
|
-
onSaveFile(data: {file: Blob
|
|
88
|
+
onSaveFile(data: { file: Blob; url: string; mode: string }) {
|
|
90
89
|
console.log('File đã được lưu:', data);
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
onClose(data: {isClickButtonClose: boolean}) {
|
|
92
|
+
onClose(data: { isClickButtonClose: boolean }) {
|
|
94
93
|
console.log('Đã đóng trình chỉnh sửa ảnh');
|
|
95
94
|
}
|
|
96
95
|
}
|
|
@@ -103,8 +102,7 @@ export class ExampleComponent {
|
|
|
103
102
|
[modeShowButton]="'save-file'"
|
|
104
103
|
[nameFile]="'image.jpg'"
|
|
105
104
|
(outSaveFile)="onSaveFile($event)"
|
|
106
|
-
(outClose)="onClose($event)">
|
|
107
|
-
</libs_ui-components-image_editor>
|
|
105
|
+
(outClose)="onClose($event)"></libs_ui-components-image_editor>
|
|
108
106
|
```
|
|
109
107
|
|
|
110
108
|
## Công nghệ sử dụng
|
|
@@ -117,29 +115,30 @@ export class ExampleComponent {
|
|
|
117
115
|
|
|
118
116
|
### Inputs
|
|
119
117
|
|
|
120
|
-
| Tên
|
|
121
|
-
|
|
122
|
-
| imgSrc
|
|
123
|
-
| originUrl
|
|
124
|
-
| nameFile
|
|
125
|
-
| modeShowButton
|
|
126
|
-
| mimetype
|
|
127
|
-
| zIndex
|
|
128
|
-
| hasZoom
|
|
129
|
-
| aspectRatio
|
|
130
|
-
| requiredCropFollowRatio | `boolean`
|
|
118
|
+
| Tên | Kiểu dữ liệu | Mặc định | Mô tả |
|
|
119
|
+
| ----------------------- | --------------------------- | ------------- | ------------------------------------------------ |
|
|
120
|
+
| imgSrc | `string` | Bắt buộc | Đường dẫn hoặc base64 của hình ảnh cần chỉnh sửa |
|
|
121
|
+
| originUrl | `string` | - | URL gốc của hình ảnh |
|
|
122
|
+
| nameFile | `string` | - | Tên file khi lưu |
|
|
123
|
+
| modeShowButton | `'save-file' \| 'save-api'` | `'save-file'` | Chế độ nút lưu |
|
|
124
|
+
| mimetype | `string` | - | Định dạng của hình ảnh |
|
|
125
|
+
| zIndex | `number` | 1200 | z-index của component |
|
|
126
|
+
| hasZoom | `boolean` | false | Cho phép phóng to/thu nhỏ hình ảnh |
|
|
127
|
+
| aspectRatio | `IAspectRatio` | - | Tỷ lệ khung hình mặc định |
|
|
128
|
+
| requiredCropFollowRatio | `boolean` | false | Bắt buộc cắt theo tỷ lệ đã chọn |
|
|
131
129
|
|
|
132
130
|
### Outputs
|
|
133
131
|
|
|
134
|
-
| Tên
|
|
135
|
-
|
|
136
|
-
| outClose
|
|
137
|
-
| outSaveFile
|
|
132
|
+
| Tên | Kiểu dữ liệu | Mô tả |
|
|
133
|
+
| ------------------- | ---------------------------------- | ---------------------------------- |
|
|
134
|
+
| outClose | `{isClickButtonClose: boolean}` | Sự kiện khi đóng trình chỉnh sửa |
|
|
135
|
+
| outSaveFile | `ISaveFile` | Sự kiện khi lưu file |
|
|
138
136
|
| outFunctionsControl | `IImageEditorFunctionControlEvent` | Sự kiện để kiểm soát các chức năng |
|
|
139
137
|
|
|
140
138
|
### Interfaces
|
|
141
139
|
|
|
142
140
|
#### ISaveFile
|
|
141
|
+
|
|
143
142
|
```typescript
|
|
144
143
|
export interface ISaveFile {
|
|
145
144
|
file: Blob;
|
|
@@ -149,6 +148,7 @@ export interface ISaveFile {
|
|
|
149
148
|
```
|
|
150
149
|
|
|
151
150
|
#### IImageEditorFunctionControlEvent
|
|
151
|
+
|
|
152
152
|
```typescript
|
|
153
153
|
export interface IImageEditorFunctionControlEvent {
|
|
154
154
|
cropImage: () => Promise<string>;
|
|
@@ -161,6 +161,7 @@ export interface IImageEditorFunctionControlEvent {
|
|
|
161
161
|
### Chỉnh sửa và lưu hình ảnh
|
|
162
162
|
|
|
163
163
|
**TypeScript (edit-image.component.ts):**
|
|
164
|
+
|
|
164
165
|
```typescript
|
|
165
166
|
import { Component } from '@angular/core';
|
|
166
167
|
import { ISaveFile, LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-editor';
|
|
@@ -169,7 +170,7 @@ import { ISaveFile, LibsUiComponentsImageEditorComponent } from '@libs-ui/compon
|
|
|
169
170
|
selector: 'app-edit-image',
|
|
170
171
|
standalone: true,
|
|
171
172
|
imports: [LibsUiComponentsImageEditorComponent],
|
|
172
|
-
templateUrl: './edit-image.component.html'
|
|
173
|
+
templateUrl: './edit-image.component.html',
|
|
173
174
|
})
|
|
174
175
|
export class EditImageComponent {
|
|
175
176
|
imageSource = 'https://example.com/path/to/image.jpg';
|
|
@@ -187,38 +188,42 @@ export class EditImageComponent {
|
|
|
187
188
|
console.log('Đã lưu file:', data.file);
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
onCloseEditor(data: {isClickButtonClose: boolean}) {
|
|
191
|
+
onCloseEditor(data: { isClickButtonClose: boolean }) {
|
|
191
192
|
this.showEditor = false;
|
|
192
193
|
}
|
|
193
194
|
}
|
|
194
195
|
```
|
|
195
196
|
|
|
196
197
|
**HTML (edit-image.component.html):**
|
|
198
|
+
|
|
197
199
|
```html
|
|
198
|
-
<button
|
|
200
|
+
<button
|
|
201
|
+
(click)="openEditor()"
|
|
202
|
+
class="px-4 py-2 bg-blue-500 text-white rounded">
|
|
199
203
|
Mở trình chỉnh sửa ảnh
|
|
200
204
|
</button>
|
|
201
205
|
|
|
202
206
|
@if (showEditor) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
207
|
+
<libs_ui-components-image_editor
|
|
208
|
+
[(imgSrc)]="imageSource"
|
|
209
|
+
[nameFile]="'my-image.jpg'"
|
|
210
|
+
(outSaveFile)="onSaveFile($event)"
|
|
211
|
+
(outClose)="onCloseEditor($event)"></libs_ui-components-image_editor>
|
|
212
|
+
} @if (editedImageUrl) {
|
|
213
|
+
<div class="mt-4">
|
|
214
|
+
<h3 class="text-lg font-semibold">Hình ảnh đã chỉnh sửa:</h3>
|
|
215
|
+
<img
|
|
216
|
+
[src]="editedImageUrl"
|
|
217
|
+
alt="Hình ảnh đã chỉnh sửa"
|
|
218
|
+
class="mt-2 max-w-full h-auto border rounded" />
|
|
219
|
+
</div>
|
|
216
220
|
}
|
|
217
221
|
```
|
|
218
222
|
|
|
219
223
|
### Sử dụng với tỷ lệ cố định
|
|
220
224
|
|
|
221
225
|
**TypeScript (fixed-ratio.component.ts):**
|
|
226
|
+
|
|
222
227
|
```typescript
|
|
223
228
|
import { Component } from '@angular/core';
|
|
224
229
|
import { LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-editor';
|
|
@@ -228,13 +233,13 @@ import { IAspectRatio } from '@libs-ui/interfaces-types';
|
|
|
228
233
|
selector: 'app-fixed-ratio',
|
|
229
234
|
standalone: true,
|
|
230
235
|
imports: [LibsUiComponentsImageEditorComponent],
|
|
231
|
-
templateUrl: './fixed-ratio.component.html'
|
|
236
|
+
templateUrl: './fixed-ratio.component.html',
|
|
232
237
|
})
|
|
233
238
|
export class FixedRatioComponent {
|
|
234
239
|
imageSource = 'https://example.com/path/to/image.jpg';
|
|
235
240
|
aspectRatio: IAspectRatio = {
|
|
236
241
|
key: '1:1',
|
|
237
|
-
value: 1
|
|
242
|
+
value: 1,
|
|
238
243
|
};
|
|
239
244
|
|
|
240
245
|
onSaveFile(data: any) {
|
|
@@ -244,11 +249,11 @@ export class FixedRatioComponent {
|
|
|
244
249
|
```
|
|
245
250
|
|
|
246
251
|
**HTML (fixed-ratio.component.html):**
|
|
252
|
+
|
|
247
253
|
```html
|
|
248
254
|
<libs_ui-components-image_editor
|
|
249
255
|
[(imgSrc)]="imageSource"
|
|
250
256
|
[aspectRatio]="aspectRatio"
|
|
251
257
|
[requiredCropFollowRatio]="true"
|
|
252
|
-
(outSaveFile)="onSaveFile($event)">
|
|
253
|
-
</libs_ui-components-image_editor>
|
|
258
|
+
(outSaveFile)="onSaveFile($event)"></libs_ui-components-image_editor>
|
|
254
259
|
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ICropRatioItem } from
|
|
1
|
+
import { ICropRatioItem } from '../interfaces/image-editor.interface';
|
|
2
2
|
export declare const cropRationItems: () => Array<ICropRatioItem>;
|
|
3
3
|
export declare const getDataUrl: (canvas: any, mimetype?: string, src?: string) => any;
|
|
4
4
|
export declare const getWidthHeightResizeCropFollow: (ratioValue: number | undefined, width: number, height: number, maxWidth: number, maxHeight: number, minWidth: number, minHeight: number) => number[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ElementRef } from '@angular/core';
|
|
2
2
|
import { ISaveFile } from '../interfaces/image-editor.interface';
|
|
3
|
+
import { IImageEditorFunctionControlEvent } from '../interfaces/function-control-event.interface';
|
|
3
4
|
import * as i0 from "@angular/core";
|
|
4
5
|
export declare class LibsUiComponentsImageEditorDemoComponent {
|
|
5
6
|
imageFileInput: ElementRef<HTMLInputElement>;
|
|
@@ -43,6 +44,7 @@ export declare class LibsUiComponentsImageEditorDemoComponent {
|
|
|
43
44
|
isClickButtonClose: boolean;
|
|
44
45
|
}): void;
|
|
45
46
|
downloadImage(): void;
|
|
47
|
+
handleFunctionControl(event: IImageEditorFunctionControlEvent): void;
|
|
46
48
|
static ɵfac: i0.ɵɵFactoryDeclaration<LibsUiComponentsImageEditorDemoComponent, never>;
|
|
47
49
|
static ɵcmp: i0.ɵɵComponentDeclaration<LibsUiComponentsImageEditorDemoComponent, "lib-image-editor-demo", never, {}, {}, never, never, true, never>;
|
|
48
50
|
}
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import { get } from
|
|
1
|
+
import { get } from '@libs-ui/utils';
|
|
2
2
|
export const cropRationItems = () => {
|
|
3
3
|
return [
|
|
4
4
|
{
|
|
5
5
|
key: 'free',
|
|
6
|
-
icon: 'libs-ui-icon-customize-image-outline'
|
|
6
|
+
icon: 'libs-ui-icon-customize-image-outline',
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
key: '1:1',
|
|
10
10
|
value: 1,
|
|
11
|
-
icon: 'libs-ui-icon-ratio-1-1'
|
|
11
|
+
icon: 'libs-ui-icon-ratio-1-1',
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
key: '2:3',
|
|
15
15
|
value: 2 / 3,
|
|
16
|
-
icon: 'libs-ui-icon-ratio-2-3'
|
|
16
|
+
icon: 'libs-ui-icon-ratio-2-3',
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
key: '3:2',
|
|
20
20
|
value: 3 / 2,
|
|
21
|
-
icon: 'libs-ui-icon-ratio-3-2'
|
|
21
|
+
icon: 'libs-ui-icon-ratio-3-2',
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
key: '3:4',
|
|
25
25
|
value: 3 / 4,
|
|
26
|
-
icon: 'libs-ui-icon-ratio-3-4'
|
|
26
|
+
icon: 'libs-ui-icon-ratio-3-4',
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
key: '4:3',
|
|
30
30
|
value: 4 / 3,
|
|
31
|
-
icon: 'libs-ui-icon-ratio-4-3'
|
|
31
|
+
icon: 'libs-ui-icon-ratio-4-3',
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
key: '9:16',
|
|
35
35
|
value: 9 / 16,
|
|
36
|
-
icon: 'libs-ui-icon-ratio-9-16'
|
|
36
|
+
icon: 'libs-ui-icon-ratio-9-16',
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
key: '16:9',
|
|
40
40
|
value: 16 / 9,
|
|
41
|
-
icon: 'libs-ui-icon-ratio-16-9'
|
|
42
|
-
}
|
|
41
|
+
icon: 'libs-ui-icon-ratio-16-9',
|
|
42
|
+
},
|
|
43
43
|
];
|
|
44
44
|
};
|
|
45
45
|
const getMimeTypeFromSrc = (src) => {
|
|
@@ -52,7 +52,7 @@ const getMimeTypeFromSrc = (src) => {
|
|
|
52
52
|
}
|
|
53
53
|
const srcSplit = src.split('.');
|
|
54
54
|
const mineType = srcSplit[srcSplit.length - 1];
|
|
55
|
-
return `image/${
|
|
55
|
+
return `image/${mineType.toLowerCase() === 'jpg' ? 'jpeg' : mineType}`;
|
|
56
56
|
};
|
|
57
57
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
58
|
export const getDataUrl = (canvas, mimetype, src) => {
|
|
@@ -80,10 +80,10 @@ export const getCropRectImage = (rectClip, imgWidth, imgHeight, scale) => {
|
|
|
80
80
|
left: Math.max(rectClip.left * scale, 0),
|
|
81
81
|
top: Math.max(rectClip.top * scale, 0),
|
|
82
82
|
width: rectClip.width * scale,
|
|
83
|
-
height: rectClip.height * scale
|
|
83
|
+
height: rectClip.height * scale,
|
|
84
84
|
};
|
|
85
85
|
};
|
|
86
86
|
export const getStylesOfElement = (element, fields) => {
|
|
87
|
-
return fields?.map(field => parseFloat(get(element, field) || '0'));
|
|
87
|
+
return fields?.map((field) => parseFloat(get(element, field) || '0'));
|
|
88
88
|
};
|
|
89
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
89
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2UtZWRpdG9yLmRlZmluZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvY29tcG9uZW50cy9pbWFnZS1lZGl0b3Ivc3JjL2RlZmluZXMvaW1hZ2UtZWRpdG9yLmRlZmluZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFHckMsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLEdBQTBCLEVBQUU7SUFDekQsT0FBTztRQUNMO1lBQ0UsR0FBRyxFQUFFLE1BQU07WUFDWCxJQUFJLEVBQUUsc0NBQXNDO1NBQzdDO1FBQ0Q7WUFDRSxHQUFHLEVBQUUsS0FBSztZQUNWLEtBQUssRUFBRSxDQUFDO1lBQ1IsSUFBSSxFQUFFLHdCQUF3QjtTQUMvQjtRQUNEO1lBQ0UsR0FBRyxFQUFFLEtBQUs7WUFDVixLQUFLLEVBQUUsQ0FBQyxHQUFHLENBQUM7WUFDWixJQUFJLEVBQUUsd0JBQXdCO1NBQy9CO1FBQ0Q7WUFDRSxHQUFHLEVBQUUsS0FBSztZQUNWLEtBQUssRUFBRSxDQUFDLEdBQUcsQ0FBQztZQUNaLElBQUksRUFBRSx3QkFBd0I7U0FDL0I7UUFDRDtZQUNFLEdBQUcsRUFBRSxLQUFLO1lBQ1YsS0FBSyxFQUFFLENBQUMsR0FBRyxDQUFDO1lBQ1osSUFBSSxFQUFFLHdCQUF3QjtTQUMvQjtRQUNEO1lBQ0UsR0FBRyxFQUFFLEtBQUs7WUFDVixLQUFLLEVBQUUsQ0FBQyxHQUFHLENBQUM7WUFDWixJQUFJLEVBQUUsd0JBQXdCO1NBQy9CO1FBQ0Q7WUFDRSxHQUFHLEVBQUUsTUFBTTtZQUNYLEtBQUssRUFBRSxDQUFDLEdBQUcsRUFBRTtZQUNiLElBQUksRUFBRSx5QkFBeUI7U0FDaEM7UUFDRDtZQUNFLEdBQUcsRUFBRSxNQUFNO1lBQ1gsS0FBSyxFQUFFLEVBQUUsR0FBRyxDQUFDO1lBQ2IsSUFBSSxFQUFFLHlCQUF5QjtTQUNo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QkFBa0IsR0FBRyxDQUFJLE9BQW9CLEVBQUUsTUFBcUIsRUFBWSxFQUFFO0lBQzdGLE9BQU8sTUFBTSxFQUFFLEdBQUcsQ0FBSSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLElBQUksR0FBRyxDQUFNLENBQUMsQ0FBQztBQUNoRixDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBnZXQgfSBmcm9tICdAbGlicy11aS91dGlscyc7XG5pbXBvcnQgeyBJQ3JvcFJhdGlvSXRlbSB9IGZyb20gJy4uL2ludGVyZmFjZXMvaW1hZ2UtZWRpdG9yLmludGVyZmFjZSc7XG5cbmV4cG9ydCBjb25zdCBjcm9wUmF0aW9uSXRlbXMgPSAoKTogQXJyYXk8SUNyb3BSYXRpb0l0ZW0+ID0+IHtcbiAgcmV0dXJuIFtcbiAgICB7XG4gICAgICBrZXk6ICdmcmVlJyxcbiAgICAgIGljb246ICdsaWJzLXVpLWljb24tY3VzdG9taXplLWltYWdlLW91dGxpbmUnLFxuICAgIH0sXG4gICAge1xuICAgICAga2V5OiAnMToxJyxcbiAgICAgIHZhbHVlOiAxLFxuICAgICAgaWNvbjogJ2xpYnMtdWktaWNvbi1yYXRpby0xLTEnLFxuICAgIH0sXG4gICAge1xuICAgICAga2V5OiAnMjozJyxcbiAgICAgIHZhbHVlOiAyIC8gMyxcbiAgICAgIGljb246ICdsaWJzLXVpLWljb24tcmF0aW8tMi0zJyxcbiAgICB9LFxuICAgIHtcbiAgICAgIGtleTogJzM6MicsXG4gICAgICB2YWx1ZTogMyAvIDIsXG4gICAgICBpY29uOiAnbGlicy11aS1pY29uLXJhdGlvLTMtMicsXG4gICAgfSxcbiAgICB7XG4gICAgICBrZXk6ICczOjQnLFxuICAgICAgdmFsdWU6IDMgLyA0LFxuICAgICAgaWNvbjogJ2xpYnMtdWktaWNvbi1yYXRpby0zLTQnLFxuICAgIH0sXG4gICAge1xuICAgICAga2V5OiAnNDozJyxcbiAgICAgIHZhbHVlOiA0IC8gMyxcbiAgICAgIGljb246ICdsaWJzLXVpLWljb24tcmF0aW8tNC0zJyxcbiAgICB9LFxuICAgIHtcbiAgICAgIGtleTogJzk6MTYnLFxuICAgICAgdmFsdWU6IDkgLyAxNixcbiAgICAgIGljb246ICdsaWJzLXVpLWljb24tcmF0aW8tOS0xNicsXG4gICAgfSxcbiAgICB7XG4gICAgICBrZXk6ICcxNjo5JyxcbiAgICAgIHZhbHVlOiAxNiAvIDksXG4gICAgICBpY29uOiAnbGlicy11aS1pY29uLXJhdGlvLTE2LTknLFxuICAgIH0sXG4gIF07XG59O1xuXG5jb25zdCBnZXRNaW1lVHlwZUZyb21TcmMgPSAoc3JjPzogc3RyaW5nKSA9PiB7XG4gIGlmICghc3JjKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuICBjb25zdCBtYXRjaCA9IC9eZGF0YTooLio/KTsvLmV4ZWMoc3JjKTtcblxuICBpZiAobWF0Y2gpIHtcbiAgICByZXR1cm4gbWF0Y2hbMV07XG4gIH1cbiAgY29uc3Qgc3JjU3BsaXQgPSBzcmMuc3BsaXQoJy4nKTtcbiAgY29uc3QgbWluZVR5cGUgPSBzcmNTcGxpdFtzcmNTcGxpdC5sZW5ndGggLSAxXTtcblxuICByZXR1cm4gYGltYWdlLyR7bWluZVR5cGUudG9Mb3dlckNhc2UoKSA9PT0gJ2pwZycgPyAnanBlZycgOiBtaW5lVHlwZX1gO1xufTtcblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbmV4cG9ydCBjb25zdCBnZXREYXRhVXJsID0gKGNhbnZhczogYW55LCBtaW1ldHlwZT86IHN0cmluZywgc3JjPzogc3RyaW5nKSA9PiB7XG4gIGNvbnN0IG1pbWVUeXBlQnlTcmMgPSBnZXRNaW1lVHlwZUZyb21TcmMoc3JjKTtcblxuICBpZiAobWltZXR5cGUgfHwgbWltZVR5cGVCeVNyYykge1xuICAgIHJldHVybiBjYW52YXMudG9EYXRhVVJMKG1pbWV0eXBlIHx8IG1pbWVUeXBlQnlTcmMpO1xuICB9XG5cbiAgcmV0dXJuIGNhbnZhcy50b0RhdGFVUkwoKTtcbn07XG5cbmV4cG9ydCBjb25zdCBnZXRXaWR0aEhlaWdodFJlc2l6ZUNyb3BGb2xsb3cgPSAocmF0aW9WYWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkLCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlciwgbWF4V2lkdGg6IG51bWJlciwgbWF4SGVpZ2h0OiBudW1iZXIsIG1pbldpZHRoOiBudW1iZXIsIG1pbkhlaWdodDogbnVtYmVyKSA9PiB7XG4gIGhlaWdodCA9IE1hdGgubWluKGhlaWdodCwgbWF4SGVpZ2h0KTtcbiAgaGVpZ2h0ID0gTWF0aC5tYXgobWluSGVpZ2h0LCBoZWlnaHQpO1xuICBpZiAocmF0aW9WYWx1ZSkge1xuICAgIHdpZHRoID0gaGVpZ2h0ICogcmF0aW9WYWx1ZTtcbiAgICB3aWR0aCA9IE1hdGgubWluKG1heFdpZHRoLCB3aWR0aCk7XG4gICAgd2lkdGggPSBNYXRoLm1heChtaW5XaWR0aCwgd2lkdGgpO1xuICAgIGhlaWdodCA9IHdpZHRoIC8gcmF0aW9WYWx1ZTtcbiAgfVxuICB3aWR0aCA9IE1hdGgubWluKG1heFdpZHRoLCB3aWR0aCk7XG4gIHdpZHRoID0gTWF0aC5tYXgobWluV2lkdGgsIHdpZHRoKTtcblxuICByZXR1cm4gW3dpZHRoLCBoZWlnaHRdO1xufTtcblxuZXhwb3J0IGNvbnN0IGdldENyb3BSZWN0SW1hZ2UgPSAocmVjdENsaXA6IHsgbGVmdDogbnVtYmVyOyB0b3A6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSwgaW1nV2lkdGg6IG51bWJlciwgaW1nSGVpZ2h0OiBudW1iZXIsIHNjYWxlOiBudW1iZXIpID0+IHtcbiAgcmV0dXJuIHtcbiAgICBsZWZ0OiBNYXRoLm1heChyZWN0Q2xpcC5sZWZ0ICogc2NhbGUsIDApLFxuICAgIHRvcDogTWF0aC5tYXgocmVjdENsaXAudG9wICogc2NhbGUsIDApLFxuICAgIHdpZHRoOiByZWN0Q2xpcC53aWR0aCAqIHNjYWxlLFxuICAgIGhlaWdodDogcmVjdENsaXAuaGVpZ2h0ICogc2NhbGUsXG4gIH07XG59O1xuXG5leHBvcnQgY29uc3QgZ2V0U3R5bGVzT2ZFbGVtZW50ID0gPFQ+KGVsZW1lbnQ6IEhUTUxFbGVtZW50LCBmaWVsZHM6IEFycmF5PHN0cmluZz4pOiBBcnJheTxUPiA9PiB7XG4gIHJldHVybiBmaWVsZHM/Lm1hcDxUPigoZmllbGQpID0+IHBhcnNlRmxvYXQoZ2V0KGVsZW1lbnQsIGZpZWxkKSB8fCAnMCcpIGFzIFQpO1xufTtcbiJdfQ==
|