@libs-ui/components-image-editor 0.2.190 → 0.2.192

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 CHANGED
@@ -1,3 +1,254 @@
1
- # image-editor
1
+ # Image Editor
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ ## Giới thiệu
4
+
5
+ `image-editor` là một component mạnh mẽ dùng để chỉnh sửa hình ảnh trong ứng dụng Angular. Component này cho phép người dùng thực hiện nhiều thao tác chỉnh sửa hình ảnh như cắt, thay đổi kích thước, xoay, lật và áp dụng các tỷ lệ cố định.
6
+
7
+ ## Tính năng
8
+
9
+ - Cắt hình ảnh với nhiều lựa chọn tỷ lệ khác nhau
10
+ - Thay đổi kích thước hình ảnh
11
+ - Xoay và lật hình ảnh
12
+ - Phóng to/thu nhỏ hình ảnh
13
+ - Khôi phục hình ảnh gốc
14
+ - Lưu hình ảnh dưới dạng file hoặc gửi qua API
15
+
16
+ ## Cài đặt
17
+
18
+ ### Yêu cầu
19
+
20
+ - Angular 18.0.0 trở lên
21
+ - Tailwind CSS 3.3.0 trở lên
22
+
23
+ ### Hướng dẫn
24
+
25
+ Để cài đặt component `image-editor`, sử dụng npm hoặc yarn:
26
+
27
+ ```bash
28
+ npm install @libs-ui/components-image-editor
29
+ ```
30
+
31
+ hoặc
32
+
33
+ ```bash
34
+ yarn add @libs-ui/components-image-editor
35
+ ```
36
+
37
+ ## Sử dụng
38
+
39
+ ### Import component
40
+
41
+ ```typescript
42
+ // example.component.ts
43
+ import { Component } from '@angular/core';
44
+ import { LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-editor';
45
+
46
+ @Component({
47
+ selector: 'app-example',
48
+ standalone: true,
49
+ imports: [LibsUiComponentsImageEditorComponent],
50
+ template: `
51
+ <libs_ui-components-image_editor
52
+ [(imgSrc)]="imageSource"
53
+ [modeShowButton]="'save-file'"
54
+ [nameFile]="'image.jpg'"
55
+ (outSaveFile)="onSaveFile($event)"
56
+ (outClose)="onClose($event)">
57
+ </libs_ui-components-image_editor>
58
+ `
59
+ })
60
+ export class ExampleComponent {
61
+ imageSource = 'https://example.com/path/to/image.jpg';
62
+
63
+ onSaveFile(data: {file: Blob, url: string, mode: string}) {
64
+ console.log('File đã được lưu:', data);
65
+ }
66
+
67
+ onClose(data: {isClickButtonClose: boolean}) {
68
+ console.log('Đã đóng trình chỉnh sửa ảnh');
69
+ }
70
+ }
71
+ ```
72
+
73
+ #### Cách 2: Sử dụng file HTML riêng biệt
74
+
75
+ ```typescript
76
+ // example.component.ts
77
+ import { Component } from '@angular/core';
78
+ import { LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-editor';
79
+
80
+ @Component({
81
+ selector: 'app-example',
82
+ standalone: true,
83
+ imports: [LibsUiComponentsImageEditorComponent],
84
+ templateUrl: './example.component.html'
85
+ })
86
+ export class ExampleComponent {
87
+ imageSource = 'https://example.com/path/to/image.jpg';
88
+
89
+ onSaveFile(data: {file: Blob, url: string, mode: string}) {
90
+ console.log('File đã được lưu:', data);
91
+ }
92
+
93
+ onClose(data: {isClickButtonClose: boolean}) {
94
+ console.log('Đã đóng trình chỉnh sửa ảnh');
95
+ }
96
+ }
97
+ ```
98
+
99
+ ```html
100
+ <!-- example.component.html -->
101
+ <libs_ui-components-image_editor
102
+ [(imgSrc)]="imageSource"
103
+ [modeShowButton]="'save-file'"
104
+ [nameFile]="'image.jpg'"
105
+ (outSaveFile)="onSaveFile($event)"
106
+ (outClose)="onClose($event)">
107
+ </libs_ui-components-image_editor>
108
+ ```
109
+
110
+ ## Công nghệ sử dụng
111
+
112
+ - **Angular 18**: Sử dụng các tính năng mới nhất của Angular 18 như control flow (@if, @for), standalone components, và signals
113
+ - **Tailwind CSS**: Component được xây dựng với Tailwind CSS 3.3+ để quản lý style
114
+ - **File Structure**: HTML, CSS và TypeScript được tách riêng để dễ dàng bảo trì và cập nhật
115
+
116
+ ## API Reference
117
+
118
+ ### Inputs
119
+
120
+ | Tên | Kiểu dữ liệu | Mặc định | Mô tả |
121
+ |-----|--------------|----------|-------|
122
+ | imgSrc | `string` | Bắt buộc | Đường dẫn hoặc base64 của hình ảnh cần chỉnh sửa |
123
+ | originUrl | `string` | - | URL gốc của hình ảnh |
124
+ | nameFile | `string` | - | Tên file khi lưu |
125
+ | modeShowButton | `'save-file' \| 'save-api'` | `'save-file'` | Chế độ nút lưu |
126
+ | mimetype | `string` | - | Định dạng của hình ảnh |
127
+ | zIndex | `number` | 1200 | z-index của component |
128
+ | hasZoom | `boolean` | false | Cho phép phóng to/thu nhỏ hình ảnh |
129
+ | aspectRatio | `IAspectRatio` | - | Tỷ lệ khung hình mặc định |
130
+ | requiredCropFollowRatio | `boolean` | false | Bắt buộc cắt theo tỷ lệ đã chọn |
131
+
132
+ ### Outputs
133
+
134
+ | Tên | Kiểu dữ liệu | Mô tả |
135
+ |-----|--------------|-------|
136
+ | outClose | `{isClickButtonClose: boolean}` | Sự kiện khi đóng trình chỉnh sửa |
137
+ | outSaveFile | `ISaveFile` | Sự kiện khi lưu file |
138
+ | outFunctionsControl | `IImageEditorFunctionControlEvent` | Sự kiện để kiểm soát các chức năng |
139
+
140
+ ### Interfaces
141
+
142
+ #### ISaveFile
143
+ ```typescript
144
+ export interface ISaveFile {
145
+ file: Blob;
146
+ url: string;
147
+ mode: 'save-file' | 'save-api' | 'save-api-as-new-file';
148
+ }
149
+ ```
150
+
151
+ #### IImageEditorFunctionControlEvent
152
+ ```typescript
153
+ export interface IImageEditorFunctionControlEvent {
154
+ cropImage: () => Promise<string>;
155
+ setLoadingState: (loading: boolean) => void;
156
+ }
157
+ ```
158
+
159
+ ## Ví dụ
160
+
161
+ ### Chỉnh sửa và lưu hình ảnh
162
+
163
+ **TypeScript (edit-image.component.ts):**
164
+ ```typescript
165
+ import { Component } from '@angular/core';
166
+ import { ISaveFile, LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-editor';
167
+
168
+ @Component({
169
+ selector: 'app-edit-image',
170
+ standalone: true,
171
+ imports: [LibsUiComponentsImageEditorComponent],
172
+ templateUrl: './edit-image.component.html'
173
+ })
174
+ export class EditImageComponent {
175
+ imageSource = 'https://example.com/path/to/image.jpg';
176
+ showEditor = false;
177
+ editedImageUrl = '';
178
+
179
+ openEditor() {
180
+ this.showEditor = true;
181
+ }
182
+
183
+ onSaveFile(data: ISaveFile) {
184
+ this.editedImageUrl = data.url;
185
+ this.showEditor = false;
186
+ // Xử lý file đã lưu
187
+ console.log('Đã lưu file:', data.file);
188
+ }
189
+
190
+ onCloseEditor(data: {isClickButtonClose: boolean}) {
191
+ this.showEditor = false;
192
+ }
193
+ }
194
+ ```
195
+
196
+ **HTML (edit-image.component.html):**
197
+ ```html
198
+ <button (click)="openEditor()" class="px-4 py-2 bg-blue-500 text-white rounded">
199
+ Mở trình chỉnh sửa ảnh
200
+ </button>
201
+
202
+ @if (showEditor) {
203
+ <libs_ui-components-image_editor
204
+ [(imgSrc)]="imageSource"
205
+ [nameFile]="'my-image.jpg'"
206
+ (outSaveFile)="onSaveFile($event)"
207
+ (outClose)="onCloseEditor($event)">
208
+ </libs_ui-components-image_editor>
209
+ }
210
+
211
+ @if (editedImageUrl) {
212
+ <div class="mt-4">
213
+ <h3 class="text-lg font-semibold">Hình ảnh đã chỉnh sửa:</h3>
214
+ <img [src]="editedImageUrl" alt="Hình ảnh đã chỉnh sửa" class="mt-2 max-w-full h-auto border rounded" />
215
+ </div>
216
+ }
217
+ ```
218
+
219
+ ### Sử dụng với tỷ lệ cố định
220
+
221
+ **TypeScript (fixed-ratio.component.ts):**
222
+ ```typescript
223
+ import { Component } from '@angular/core';
224
+ import { LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-editor';
225
+ import { IAspectRatio } from '@libs-ui/interfaces-types';
226
+
227
+ @Component({
228
+ selector: 'app-fixed-ratio',
229
+ standalone: true,
230
+ imports: [LibsUiComponentsImageEditorComponent],
231
+ templateUrl: './fixed-ratio.component.html'
232
+ })
233
+ export class FixedRatioComponent {
234
+ imageSource = 'https://example.com/path/to/image.jpg';
235
+ aspectRatio: IAspectRatio = {
236
+ key: '1:1',
237
+ value: 1
238
+ };
239
+
240
+ onSaveFile(data: any) {
241
+ console.log('Đã lưu hình ảnh với tỷ lệ 1:1');
242
+ }
243
+ }
244
+ ```
245
+
246
+ **HTML (fixed-ratio.component.html):**
247
+ ```html
248
+ <libs_ui-components-image_editor
249
+ [(imgSrc)]="imageSource"
250
+ [aspectRatio]="aspectRatio"
251
+ [requiredCropFollowRatio]="true"
252
+ (outSaveFile)="onSaveFile($event)">
253
+ </libs_ui-components-image_editor>
254
+ ```
@@ -0,0 +1,48 @@
1
+ import { ElementRef } from '@angular/core';
2
+ import { ISaveFile } from '../interfaces/image-editor.interface';
3
+ import * as i0 from "@angular/core";
4
+ export declare class LibsUiComponentsImageEditorDemoComponent {
5
+ imageFileInput: ElementRef<HTMLInputElement>;
6
+ imageSource: string;
7
+ showEditor: boolean;
8
+ editedImageUrl: string;
9
+ selectedFile: File | null;
10
+ imageName: string;
11
+ inputsDoc: {
12
+ name: string;
13
+ type: string;
14
+ default: string;
15
+ description: string;
16
+ }[];
17
+ outputsDoc: {
18
+ name: string;
19
+ type: string;
20
+ description: string;
21
+ }[];
22
+ interfacesDoc: {
23
+ name: string;
24
+ code: string;
25
+ description: string;
26
+ }[];
27
+ features: {
28
+ id: number;
29
+ icon: string;
30
+ title: string;
31
+ description: string;
32
+ }[];
33
+ codeExamples: {
34
+ id: number;
35
+ title: string;
36
+ code: string;
37
+ }[];
38
+ copyToClipboard(text: string): void;
39
+ onFileSelected(event: Event): void;
40
+ uploadAndEdit(): void;
41
+ onSaveImage(data: ISaveFile): void;
42
+ onCloseEditor(data: {
43
+ isClickButtonClose: boolean;
44
+ }): void;
45
+ downloadImage(): void;
46
+ static ɵfac: i0.ɵɵFactoryDeclaration<LibsUiComponentsImageEditorDemoComponent, never>;
47
+ static ɵcmp: i0.ɵɵComponentDeclaration<LibsUiComponentsImageEditorDemoComponent, "lib-image-editor-demo", never, {}, {}, never, never, true, never>;
48
+ }
@@ -0,0 +1,154 @@
1
+ import { Component, ElementRef, ViewChild } from '@angular/core';
2
+ import { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';
3
+ import { LibsUiComponentsImageEditorComponent } from '../image-editor.component';
4
+ import * as i0 from "@angular/core";
5
+ export class LibsUiComponentsImageEditorDemoComponent {
6
+ imageFileInput;
7
+ imageSource = '';
8
+ showEditor = false;
9
+ editedImageUrl = '';
10
+ selectedFile = null;
11
+ imageName = '';
12
+ // Documentation data as regular properties
13
+ inputsDoc = [
14
+ { name: 'imgSrc', type: 'string', default: 'Bắt buộc', description: 'Đường dẫn hoặc base64 của hình ảnh cần chỉnh sửa' },
15
+ { name: 'originUrl', type: 'string', default: '-', description: 'URL gốc của hình ảnh' },
16
+ { name: 'nameFile', type: 'string', default: '-', description: 'Tên file khi lưu' },
17
+ { name: 'modeShowButton', type: '\'save-file\' | \'save-api\'', default: '\'save-file\'', description: 'Chế độ nút lưu' },
18
+ { name: 'mimetype', type: 'string', default: '-', description: 'Định dạng của hình ảnh' },
19
+ { name: 'zIndex', type: 'number', default: '1200', description: 'z-index của component' },
20
+ { name: 'hasZoom', type: 'boolean', default: 'false', description: 'Cho phép phóng to/thu nhỏ hình ảnh' },
21
+ { name: 'aspectRatio', type: 'IAspectRatio', default: '-', description: 'Tỷ lệ khung hình mặc định' },
22
+ { name: 'requiredCropFollowRatio', type: 'boolean', default: 'false', description: 'Bắt buộc cắt theo tỷ lệ đã chọn' }
23
+ ];
24
+ outputsDoc = [
25
+ { name: 'outClose', type: '{isClickButtonClose: boolean}', description: 'Sự kiện khi đóng trình chỉnh sửa' },
26
+ { name: 'outSaveFile', type: 'ISaveFile', description: 'Sự kiện khi lưu file' },
27
+ { name: 'outFunctionsControl', type: 'IImageEditorFunctionControlEvent', description: 'Sự kiện để kiểm soát các chức năng' }
28
+ ];
29
+ interfacesDoc = [
30
+ {
31
+ name: 'ISaveFile',
32
+ code: 'export interface ISaveFile {\n file: Blob;\n url: string;\n mode: \'save-file\' | \'save-api\' | \'save-api-as-new-file\';\n}',
33
+ description: 'Interface định nghĩa dữ liệu khi lưu file.'
34
+ },
35
+ {
36
+ name: 'IImageEditorFunctionControlEvent',
37
+ code: 'export interface IImageEditorFunctionControlEvent {\n cropImage: () => Promise<string>;\n setLoadingState: (loading: boolean) => void;\n}',
38
+ description: 'Interface định nghĩa các hàm điều khiển có thể được gọi từ bên ngoài component.'
39
+ },
40
+ {
41
+ name: 'IAspectRatio',
42
+ code: 'export interface IAspectRatio {\n key: string;\n value: number;\n}',
43
+ description: 'Interface định nghĩa tỷ lệ khung hình.'
44
+ }
45
+ ];
46
+ features = [
47
+ {
48
+ id: 1,
49
+ icon: '✂️',
50
+ title: 'Cắt ảnh',
51
+ description: 'Cắt hình ảnh theo khu vực tùy chỉnh hoặc dựa trên tỷ lệ định trước (1:1, 4:3, 16:9, v.v.).'
52
+ },
53
+ {
54
+ id: 2,
55
+ icon: '🔄',
56
+ title: 'Xoay và lật',
57
+ description: 'Xoay hình ảnh theo các góc khác nhau hoặc lật theo chiều ngang/dọc.'
58
+ },
59
+ {
60
+ id: 3,
61
+ icon: '📏',
62
+ title: 'Thay đổi kích thước',
63
+ description: 'Thay đổi kích thước hình ảnh theo tỷ lệ phần trăm hoặc kích thước cụ thể.'
64
+ },
65
+ {
66
+ id: 4,
67
+ icon: '💾',
68
+ title: 'Nhiều chế độ lưu',
69
+ description: 'Hỗ trợ nhiều chế độ lưu: tải xuống trực tiếp hoặc gửi đến API.'
70
+ },
71
+ {
72
+ id: 5,
73
+ icon: '🔍',
74
+ title: 'Phóng to/thu nhỏ',
75
+ description: 'Tính năng phóng to/thu nhỏ để xem chi tiết hình ảnh khi cần thiết.'
76
+ }
77
+ ];
78
+ codeExamples = [
79
+ {
80
+ id: 1,
81
+ title: 'Sử dụng cơ bản',
82
+ code: `import { Component } from '@angular/core';\nimport { LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-editor';\n\n@Component({\n selector: 'app-example',\n standalone: true,\n imports: [LibsUiComponentsImageEditorComponent],\n template: \`\n <libs_ui-components-image_editor\n [(imgSrc)]="imageSource"\n [modeShowButton]="'save-file'"\n [nameFile]="'image.jpg'"\n (outSaveFile)="onSaveFile($event)"\n (outClose)="onClose($event)">\n </libs_ui-components-image_editor>\n \`\n})\nexport class ExampleComponent {\n imageSource = 'https://example.com/path/to/image.jpg';\n\n onSaveFile(data: {file: Blob, url: string, mode: string}) {\n console.log('File đã được lưu:', data);\n }\n\n onClose(data: {isClickButtonClose: boolean}) {\n console.log('Đã đóng trình chỉnh sửa ảnh');\n }\n}`
83
+ },
84
+ {
85
+ id: 2,
86
+ title: 'Sử dụng với tỷ lệ cố định',
87
+ code: `import { Component } from '@angular/core';\nimport { LibsUiComponentsImageEditorComponent } from '@libs-ui/components-image-editor';\nimport { IAspectRatio } from '@libs-ui/interfaces-types';\n\n@Component({\n selector: 'app-example',\n standalone: true,\n imports: [LibsUiComponentsImageEditorComponent],\n template: \`\n <libs_ui-components-image_editor\n [(imgSrc)]="imageSource"\n [aspectRatio]="aspectRatio"\n [requiredCropFollowRatio]="true"\n (outSaveFile)="onSaveFile($event)">\n </libs_ui-components-image_editor>\n \`\n})\nexport class ExampleComponent {\n imageSource = 'https://example.com/path/to/image.jpg';\n aspectRatio: IAspectRatio = {\n key: '1:1',\n value: 1\n };\n\n onSaveFile(data: any) {\n console.log('Đã lưu hình ảnh với tỷ lệ 1:1');\n }\n}`
88
+ },
89
+ {
90
+ id: 3,
91
+ title: 'Sử dụng với phần điều khiển bên ngoài',
92
+ code: `import { Component, ViewChild } from '@angular/core';\nimport { LibsUiComponentsImageEditorComponent, IImageEditorFunctionControlEvent } from '@libs-ui/components-image-editor';\n\n@Component({\n selector: 'app-example',\n standalone: true,\n imports: [LibsUiComponentsImageEditorComponent],\n template: \`\n <button (click)="cropAndSave()">Cắt và lưu ngay</button>\n <libs_ui-components-image_editor\n [(imgSrc)]="imageSource"\n (outFunctionsControl)="onFunctionsControl($event)">\n </libs_ui-components-image_editor>\n \`\n})\nexport class ExampleComponent {\n imageSource = 'https://example.com/path/to/image.jpg';\n editorFunctions!: IImageEditorFunctionControlEvent;\n\n onFunctionsControl(event: IImageEditorFunctionControlEvent) {\n this.editorFunctions = event;\n }\n\n async cropAndSave() {\n if (this.editorFunctions) {\n this.editorFunctions.setLoadingState(true);\n try {\n const dataUrl = await this.editorFunctions.cropImage();\n console.log('Hình ảnh đã được cắt:', dataUrl);\n } finally {\n this.editorFunctions.setLoadingState(false);\n }\n }\n }\n}`
93
+ }
94
+ ];
95
+ copyToClipboard(text) {
96
+ navigator.clipboard.writeText(text).then(() => {
97
+ alert('Đã sao chép vào clipboard');
98
+ }).catch(err => {
99
+ console.error('Không thể sao chép: ', err);
100
+ });
101
+ }
102
+ onFileSelected(event) {
103
+ const input = event.target;
104
+ if (input.files && input.files.length > 0) {
105
+ this.selectedFile = input.files[0];
106
+ this.imageName = this.selectedFile.name;
107
+ console.log('File selected:', this.selectedFile.name);
108
+ }
109
+ }
110
+ uploadAndEdit() {
111
+ if (!this.selectedFile) {
112
+ alert('Vui lòng chọn một file hình ảnh trước');
113
+ return;
114
+ }
115
+ const reader = new FileReader();
116
+ reader.onload = (e) => {
117
+ if (e.target?.result) {
118
+ this.imageSource = e.target.result;
119
+ this.showEditor = true;
120
+ }
121
+ };
122
+ reader.readAsDataURL(this.selectedFile);
123
+ }
124
+ onSaveImage(data) {
125
+ console.log('Image saved:', data);
126
+ this.editedImageUrl = data.url;
127
+ this.showEditor = false;
128
+ }
129
+ onCloseEditor(data) {
130
+ console.log('Editor closed:', data);
131
+ this.showEditor = false;
132
+ }
133
+ downloadImage() {
134
+ if (this.editedImageUrl) {
135
+ const link = document.createElement('a');
136
+ link.href = this.editedImageUrl;
137
+ link.download = this.imageName || 'edited-image.jpg';
138
+ link.click();
139
+ }
140
+ }
141
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsImageEditorDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
142
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: LibsUiComponentsImageEditorDemoComponent, isStandalone: true, selector: "lib-image-editor-demo", viewQueries: [{ propertyName: "imageFileInput", first: true, predicate: ["imageFileInput"], descendants: true }], ngImport: i0, template: "<div class=\"max-w-6xl mx-auto p-5 font-sans text-gray-800\">\n <header class=\"text-center py-10 bg-white rounded-lg mb-8 shadow-sm\">\n <h1 class=\"text-4xl font-bold text-gray-800 mb-2\">Demo Tr\u00ECnh Ch\u1EC9nh S\u1EEDa H\u00ECnh \u1EA2nh</h1>\n <p class=\"text-xl text-gray-500\">Th\u01B0 vi\u1EC7n component cho Angular \u0111\u1EC3 ch\u1EC9nh s\u1EEDa h\u00ECnh \u1EA3nh</p>\n </header>\n\n <main>\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">Gi\u1EDBi thi\u1EC7u</h2>\n <p>\n <code class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">&#64;libs-ui/components-image-editor</code> l\u00E0 m\u1ED9t\n component Angular m\u1EA1nh m\u1EBD cho ph\u00E9p ng\u01B0\u1EDDi d\u00F9ng th\u1EF1c hi\u1EC7n c\u00E1c thao t\u00E1c ch\u1EC9nh s\u1EEDa h\u00ECnh \u1EA3nh nh\u01B0 c\u1EAFt, thay \u0111\u1ED5i k\u00EDch\n th\u01B0\u1EDBc, xoay, l\u1EADt v\u00E0 \u00E1p d\u1EE5ng t\u1EF7 l\u1EC7 c\u1ED1 \u0111\u1ECBnh.\n </p>\n </section>\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">C\u00E0i \u0111\u1EB7t</h2>\n\n <div class=\"mb-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Y\u00EAu c\u1EA7u</h3>\n <ul class=\"list-disc pl-5 space-y-2 text-gray-600\">\n <li><span class=\"font-semibold\">Angular</span>: 18.0.0 tr\u1EDF l\u00EAn</li>\n <li><span class=\"font-semibold\">Tailwind CSS</span>: 3.3.0 tr\u1EDF l\u00EAn</li>\n </ul>\n </div>\n\n <p class=\"mb-4\">\u0110\u1EC3 c\u00E0i \u0111\u1EB7t th\u01B0 vi\u1EC7n, s\u1EED d\u1EE5ng npm ho\u1EB7c yarn:</p>\n\n <div class=\"flex items-center bg-gray-100 p-4 rounded-lg mb-6\">\n <pre class=\"flex-1 text-sm overflow-x-auto\"><code>npm install &#64;libs-ui/components-image-editor</code></pre>\n <button class=\"ml-4 px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors\"\n (click)=\"copyToClipboard('npm install @libs-ui/components-image-editor')\">\n Sao ch\u00E9p\n </button>\n </div>\n\n <p class=\"mb-4\">Ho\u1EB7c v\u1EDBi yarn:</p>\n\n <div class=\"flex items-center bg-gray-100 p-4 rounded-lg mb-6\">\n <pre class=\"flex-1 text-sm overflow-x-auto\"><code>yarn add &#64;libs-ui/components-image-editor</code></pre>\n <button class=\"ml-4 px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors\"\n (click)=\"copyToClipboard('yarn add @libs-ui/components-image-editor')\">\n Sao ch\u00E9p\n </button>\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">Demo tr\u1EF1c ti\u1EBFp</h2>\n <div>\n <p class=\"mb-4\">T\u1EA3i l\u00EAn h\u00ECnh \u1EA3nh v\u00E0 ch\u1EC9nh s\u1EEDa ngay tr\u00EAn tr\u00ECnh duy\u1EC7t:</p>\n <div class=\"flex flex-wrap items-center gap-4 mb-8\">\n <input type=\"file\"\n #imageFileInput\n accept=\"image/*\"\n class=\"border border-gray-300 rounded px-3 py-2\"\n (change)=\"onFileSelected($event)\" />\n <libs_ui-components-buttons-button label=\"T\u1EA3i l\u00EAn v\u00E0 ch\u1EC9nh s\u1EEDa\"\n (click)=\"uploadAndEdit()\">\n </libs_ui-components-buttons-button>\n </div>\n\n @if (editedImageUrl) {\n <div class=\"mt-8 p-6 bg-gray-50 rounded-lg\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">K\u1EBFt qu\u1EA3</h3>\n <div class=\"max-w-xl mx-auto mb-4 border border-gray-200 rounded-lg overflow-hidden shadow-md\">\n <img [src]=\"editedImageUrl\"\n alt=\"H\u00ECnh \u1EA3nh \u0111\u00E3 ch\u1EC9nh s\u1EEDa\"\n class=\"w-full h-auto\" />\n </div>\n <div class=\"flex justify-center\">\n <libs_ui-components-buttons-button label=\"T\u1EA3i xu\u1ED1ng h\u00ECnh \u1EA3nh\"\n (click)=\"downloadImage()\">\n </libs_ui-components-buttons-button>\n </div>\n </div>\n }\n </div>\n </section>\n\n @if (showEditor) {\n <div class=\"fixed inset-0 z-50\">\n <libs_ui-components-image_editor [(imgSrc)]=\"imageSource\"\n [nameFile]=\"imageName || 'demo-image.jpg'\"\n [modeShowButton]=\"'save-file'\"\n [hasZoom]=\"true\"\n (outSaveFile)=\"onSaveImage($event)\"\n (outClose)=\"onCloseEditor($event)\" />\n </div>\n }\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">C\u00E1ch s\u1EED d\u1EE5ng</h2>\n\n <div class=\"mb-8\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">C\u00E1ch 1: S\u1EED d\u1EE5ng file HTML ri\u00EAng bi\u1EC7t</h3>\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-4\">\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">HTML (example.component.html)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>&lt;libs_ui-components-image_editor\n [(imgSrc)]=\"imageSource\"\n [nameFile]=\"'image.jpg'\"\n [modeShowButton]=\"'save-file'\"\n (outSaveFile)=\"onSaveFile($event)\"\n (outClose)=\"onClose($event)\"&gt;\n&lt;/libs_ui-components-image_editor&gt;</code></pre>\n </div>\n\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">TypeScript (example.component.ts)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>import &#123; Component &#125; from '&#64;angular/core';\nimport &#123; LibsUiComponentsImageEditorComponent &#125; from '&#64;libs-ui/components-image-editor';\n\n&#64;Component(&#123;\n selector: 'app-example',\n standalone: true,\n imports: [LibsUiComponentsImageEditorComponent],\n templateUrl: './example.component.html'\n&#125;)\nexport class ExampleComponent &#123;\n imageSource = 'https://example.com/path/to/image.jpg';\n\n onSaveFile(data: &#123;file: Blob, url: string, mode: string&#125;) &#123;\n console.log('File \u0111\u00E3 \u0111\u01B0\u1EE3c l\u01B0u:', data);\n &#125;\n\n onClose(data: &#123;isClickButtonClose: boolean&#125;) &#123;\n console.log('\u0110\u00E3 \u0111\u00F3ng tr\u00ECnh ch\u1EC9nh s\u1EEDa \u1EA3nh');\n &#125;\n&#125;</code></pre>\n </div>\n </div>\n </div>\n\n <div class=\"mb-8\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">C\u00E1ch 2: V\u1EDBi tr\u00ECnh ch\u1EC9nh s\u1EEDa c\u00F3 th\u1EC3 \u0111\u00F3ng/m\u1EDF</h3>\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-4\">\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">HTML (toggle-editor.component.html)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>&lt;button (click)=\"openEditor()\" class=\"px-4 py-2 bg-blue-500 text-white rounded\"&gt;\n M\u1EDF tr\u00ECnh ch\u1EC9nh s\u1EEDa \u1EA3nh\n&lt;/button&gt;\n\n&#64;if (showEditor) &#123;\n &lt;libs_ui-components-image_editor\n [(imgSrc)]=\"imageSource\"\n [nameFile]=\"'my-image.jpg'\"\n (outSaveFile)=\"onSaveFile($event)\"\n (outClose)=\"onCloseEditor($event)\"&gt;\n &lt;/libs_ui-components-image_editor&gt;\n&#125;\n\n&#64;if (editedImageUrl) &#123;\n &lt;div class=\"mt-4\"&gt;\n &lt;h3 class=\"text-lg font-semibold\"&gt;H\u00ECnh \u1EA3nh \u0111\u00E3 ch\u1EC9nh s\u1EEDa:&lt;/h3&gt;\n &lt;img [src]=\"editedImageUrl\" alt=\"H\u00ECnh \u1EA3nh \u0111\u00E3 ch\u1EC9nh s\u1EEDa\" class=\"mt-2 max-w-full h-auto\" /&gt;\n &lt;/div&gt;\n&#125;</code></pre>\n </div>\n\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">TypeScript (toggle-editor.component.ts)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>import &#123; Component &#125; from '&#64;angular/core';\nimport &#123; LibsUiComponentsImageEditorComponent &#125; from '&#64;libs-ui/components-image-editor';\nimport &#123; ISaveFile &#125; from '&#64;libs-ui/components-image-editor';\n\n&#64;Component(&#123;\n selector: 'app-toggle-editor',\n standalone: true,\n imports: [LibsUiComponentsImageEditorComponent],\n templateUrl: './toggle-editor.component.html'\n&#125;)\nexport class ToggleEditorComponent &#123;\n imageSource = 'https://example.com/path/to/image.jpg';\n showEditor = false;\n editedImageUrl = '';\n\n openEditor() &#123;\n this.showEditor = true;\n &#125;\n\n onSaveFile(data: ISaveFile) &#123;\n this.editedImageUrl = data.url;\n this.showEditor = false;\n console.log('\u0110\u00E3 l\u01B0u file:', data.file);\n &#125;\n\n onCloseEditor(data: &#123;isClickButtonClose: boolean&#125;) &#123;\n this.showEditor = false;\n &#125;\n&#125;</code></pre>\n </div>\n </div>\n </div>\n\n <div class=\"mb-8\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">C\u00E1ch 3: V\u1EDBi t\u1EF7 l\u1EC7 khung h\u00ECnh c\u1ED1 \u0111\u1ECBnh</h3>\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-4\">\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">HTML (fixed-ratio.component.html)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>&lt;libs_ui-components-image_editor\n [(imgSrc)]=\"imageSource\"\n [aspectRatio]=\"aspectRatio\"\n [requiredCropFollowRatio]=\"true\"\n (outSaveFile)=\"onSaveFile($event)\"&gt;\n&lt;/libs_ui-components-image_editor&gt;</code></pre>\n </div>\n\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">TypeScript (fixed-ratio.component.ts)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>import &#123; Component &#125; from '&#64;angular/core';\nimport &#123; LibsUiComponentsImageEditorComponent &#125; from '&#64;libs-ui/components-image-editor';\nimport &#123; IAspectRatio &#125; from '&#64;libs-ui/interfaces-types';\n\n&#64;Component(&#123;\n selector: 'app-fixed-ratio',\n standalone: true,\n imports: [LibsUiComponentsImageEditorComponent],\n templateUrl: './fixed-ratio.component.html'\n&#125;)\nexport class FixedRatioComponent &#123;\n imageSource = 'https://example.com/path/to/image.jpg';\n aspectRatio: IAspectRatio = &#123;\n key: '1:1',\n value: 1\n &#125;;\n\n onSaveFile(data: any) &#123;\n console.log('\u0110\u00E3 l\u01B0u h\u00ECnh \u1EA3nh v\u1EDBi t\u1EF7 l\u1EC7 1:1');\n &#125;\n&#125;</code></pre>\n </div>\n </div>\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">T\u00E0i li\u1EC7u API</h2>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">Inputs</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white border border-gray-200\">\n <thead>\n <tr>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">T\u00EAn</th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">Ki\u1EC3u d\u1EEF\n li\u1EC7u\n </th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">M\u1EB7c \u0111\u1ECBnh\n </th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">M\u00F4 t\u1EA3\n </th>\n </tr>\n </thead>\n <tbody>\n @for (input of inputsDoc; track input.name) {\n <tr>\n <td class=\"py-2 px-4 border-b border-gray-200\"><code\n class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">{{ input.name }}</code></td>\n <td class=\"py-2 px-4 border-b border-gray-200\"><code\n class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">{{ input.type }}</code></td>\n <td class=\"py-2 px-4 border-b border-gray-200\">{{ input.default }}</td>\n <td class=\"py-2 px-4 border-b border-gray-200\">{{ input.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">Outputs</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white border border-gray-200\">\n <thead>\n <tr>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">T\u00EAn</th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">Ki\u1EC3u d\u1EEF\n li\u1EC7u\n </th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">M\u00F4 t\u1EA3\n </th>\n </tr>\n </thead>\n <tbody>\n @for (output of outputsDoc; track output.name) {\n <tr>\n <td class=\"py-2 px-4 border-b border-gray-200\"><code\n class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">{{ output.name }}</code></td>\n <td class=\"py-2 px-4 border-b border-gray-200\"><code\n class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">{{ output.type }}</code></td>\n <td class=\"py-2 px-4 border-b border-gray-200\">{{ output.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">Interfaces</h3>\n <div class=\"space-y-6\">\n @for (interfaceItem of interfacesDoc; track interfaceItem.name) {\n <div class=\"bg-gray-50 p-6 rounded-lg\">\n <h4 class=\"text-lg font-semibold text-gray-700 mb-3\">{{ interfaceItem.name }}</h4>\n <pre\n class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm mb-3\"><code>{{ interfaceItem.code }}</code></pre>\n <p class=\"text-gray-600\">{{ interfaceItem.description }}</p>\n </div>\n }\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">T\u00EDnh n\u0103ng</h2>\n <ul class=\"space-y-6\">\n @for (feature of features; track feature.id) {\n <li class=\"flex items-start\">\n <span class=\"text-2xl text-blue-500 mr-4\">{{ feature.icon }}</span>\n <div>\n <h3 class=\"text-lg font-semibold text-gray-700 mb-1\">{{ feature.title }}</h3>\n <p class=\"text-gray-600\">{{ feature.description }}</p>\n </div>\n </li>\n }\n </ul>\n </section>\n </main>\n</div>\n", styles: ["pre code{@apply font-mono;}.hover\\:bg-blue-600{transition:background-color .2s ease-in-out}.overflow-x-auto{-webkit-overflow-scrolling:touch}pre::-webkit-scrollbar{@apply h-1.5 bg-gray-100;}pre::-webkit-scrollbar-thumb{@apply bg-gray-300 rounded;}button:focus,input:focus{@apply outline-none ring-2 ring-blue-400 ring-opacity-50;}input[type=file]{@apply text-sm text-gray-600;}\n"], dependencies: [{ kind: "component", type: LibsUiComponentsButtonsButtonComponent, selector: "libs_ui-components-buttons-button", inputs: ["flagMouse", "type", "buttonCustom", "sizeButton", "label", "disable", "isPending", "imageLeft", "classInclude", "classIconLeft", "classIconRight", "classLabel", "iconOnlyType", "popover", "ignoreStopPropagationEvent", "zIndex", "widthLabelPopover", "styleIconLeft", "styleButton", "ignoreFocusWhenInputTab", "ignoreSetClickWhenShowPopover", "ignorePointerEvent", "isActive", "isHandlerEnterDocumentClickButton"], outputs: ["outClick", "outPopoverEvent", "outFunctionsControl"] }, { kind: "component", type: LibsUiComponentsImageEditorComponent, selector: "libs_ui-components-image_editor", inputs: ["modeShowButton", "mimetype", "zIndex", "imgSrc", "originUrl", "nameFile", "hasZoom", "aspectRatio", "requiredCropFollowRatio"], outputs: ["imgSrcChange", "outClose", "outSaveFile", "outFunctionsControl"] }] });
143
+ }
144
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsImageEditorDemoComponent, decorators: [{
145
+ type: Component,
146
+ args: [{ selector: 'lib-image-editor-demo', standalone: true, imports: [
147
+ LibsUiComponentsButtonsButtonComponent,
148
+ LibsUiComponentsImageEditorComponent
149
+ ], template: "<div class=\"max-w-6xl mx-auto p-5 font-sans text-gray-800\">\n <header class=\"text-center py-10 bg-white rounded-lg mb-8 shadow-sm\">\n <h1 class=\"text-4xl font-bold text-gray-800 mb-2\">Demo Tr\u00ECnh Ch\u1EC9nh S\u1EEDa H\u00ECnh \u1EA2nh</h1>\n <p class=\"text-xl text-gray-500\">Th\u01B0 vi\u1EC7n component cho Angular \u0111\u1EC3 ch\u1EC9nh s\u1EEDa h\u00ECnh \u1EA3nh</p>\n </header>\n\n <main>\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">Gi\u1EDBi thi\u1EC7u</h2>\n <p>\n <code class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">&#64;libs-ui/components-image-editor</code> l\u00E0 m\u1ED9t\n component Angular m\u1EA1nh m\u1EBD cho ph\u00E9p ng\u01B0\u1EDDi d\u00F9ng th\u1EF1c hi\u1EC7n c\u00E1c thao t\u00E1c ch\u1EC9nh s\u1EEDa h\u00ECnh \u1EA3nh nh\u01B0 c\u1EAFt, thay \u0111\u1ED5i k\u00EDch\n th\u01B0\u1EDBc, xoay, l\u1EADt v\u00E0 \u00E1p d\u1EE5ng t\u1EF7 l\u1EC7 c\u1ED1 \u0111\u1ECBnh.\n </p>\n </section>\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">C\u00E0i \u0111\u1EB7t</h2>\n\n <div class=\"mb-6\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-3\">Y\u00EAu c\u1EA7u</h3>\n <ul class=\"list-disc pl-5 space-y-2 text-gray-600\">\n <li><span class=\"font-semibold\">Angular</span>: 18.0.0 tr\u1EDF l\u00EAn</li>\n <li><span class=\"font-semibold\">Tailwind CSS</span>: 3.3.0 tr\u1EDF l\u00EAn</li>\n </ul>\n </div>\n\n <p class=\"mb-4\">\u0110\u1EC3 c\u00E0i \u0111\u1EB7t th\u01B0 vi\u1EC7n, s\u1EED d\u1EE5ng npm ho\u1EB7c yarn:</p>\n\n <div class=\"flex items-center bg-gray-100 p-4 rounded-lg mb-6\">\n <pre class=\"flex-1 text-sm overflow-x-auto\"><code>npm install &#64;libs-ui/components-image-editor</code></pre>\n <button class=\"ml-4 px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors\"\n (click)=\"copyToClipboard('npm install @libs-ui/components-image-editor')\">\n Sao ch\u00E9p\n </button>\n </div>\n\n <p class=\"mb-4\">Ho\u1EB7c v\u1EDBi yarn:</p>\n\n <div class=\"flex items-center bg-gray-100 p-4 rounded-lg mb-6\">\n <pre class=\"flex-1 text-sm overflow-x-auto\"><code>yarn add &#64;libs-ui/components-image-editor</code></pre>\n <button class=\"ml-4 px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors\"\n (click)=\"copyToClipboard('yarn add @libs-ui/components-image-editor')\">\n Sao ch\u00E9p\n </button>\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">Demo tr\u1EF1c ti\u1EBFp</h2>\n <div>\n <p class=\"mb-4\">T\u1EA3i l\u00EAn h\u00ECnh \u1EA3nh v\u00E0 ch\u1EC9nh s\u1EEDa ngay tr\u00EAn tr\u00ECnh duy\u1EC7t:</p>\n <div class=\"flex flex-wrap items-center gap-4 mb-8\">\n <input type=\"file\"\n #imageFileInput\n accept=\"image/*\"\n class=\"border border-gray-300 rounded px-3 py-2\"\n (change)=\"onFileSelected($event)\" />\n <libs_ui-components-buttons-button label=\"T\u1EA3i l\u00EAn v\u00E0 ch\u1EC9nh s\u1EEDa\"\n (click)=\"uploadAndEdit()\">\n </libs_ui-components-buttons-button>\n </div>\n\n @if (editedImageUrl) {\n <div class=\"mt-8 p-6 bg-gray-50 rounded-lg\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">K\u1EBFt qu\u1EA3</h3>\n <div class=\"max-w-xl mx-auto mb-4 border border-gray-200 rounded-lg overflow-hidden shadow-md\">\n <img [src]=\"editedImageUrl\"\n alt=\"H\u00ECnh \u1EA3nh \u0111\u00E3 ch\u1EC9nh s\u1EEDa\"\n class=\"w-full h-auto\" />\n </div>\n <div class=\"flex justify-center\">\n <libs_ui-components-buttons-button label=\"T\u1EA3i xu\u1ED1ng h\u00ECnh \u1EA3nh\"\n (click)=\"downloadImage()\">\n </libs_ui-components-buttons-button>\n </div>\n </div>\n }\n </div>\n </section>\n\n @if (showEditor) {\n <div class=\"fixed inset-0 z-50\">\n <libs_ui-components-image_editor [(imgSrc)]=\"imageSource\"\n [nameFile]=\"imageName || 'demo-image.jpg'\"\n [modeShowButton]=\"'save-file'\"\n [hasZoom]=\"true\"\n (outSaveFile)=\"onSaveImage($event)\"\n (outClose)=\"onCloseEditor($event)\" />\n </div>\n }\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">C\u00E1ch s\u1EED d\u1EE5ng</h2>\n\n <div class=\"mb-8\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">C\u00E1ch 1: S\u1EED d\u1EE5ng file HTML ri\u00EAng bi\u1EC7t</h3>\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-4\">\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">HTML (example.component.html)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>&lt;libs_ui-components-image_editor\n [(imgSrc)]=\"imageSource\"\n [nameFile]=\"'image.jpg'\"\n [modeShowButton]=\"'save-file'\"\n (outSaveFile)=\"onSaveFile($event)\"\n (outClose)=\"onClose($event)\"&gt;\n&lt;/libs_ui-components-image_editor&gt;</code></pre>\n </div>\n\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">TypeScript (example.component.ts)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>import &#123; Component &#125; from '&#64;angular/core';\nimport &#123; LibsUiComponentsImageEditorComponent &#125; from '&#64;libs-ui/components-image-editor';\n\n&#64;Component(&#123;\n selector: 'app-example',\n standalone: true,\n imports: [LibsUiComponentsImageEditorComponent],\n templateUrl: './example.component.html'\n&#125;)\nexport class ExampleComponent &#123;\n imageSource = 'https://example.com/path/to/image.jpg';\n\n onSaveFile(data: &#123;file: Blob, url: string, mode: string&#125;) &#123;\n console.log('File \u0111\u00E3 \u0111\u01B0\u1EE3c l\u01B0u:', data);\n &#125;\n\n onClose(data: &#123;isClickButtonClose: boolean&#125;) &#123;\n console.log('\u0110\u00E3 \u0111\u00F3ng tr\u00ECnh ch\u1EC9nh s\u1EEDa \u1EA3nh');\n &#125;\n&#125;</code></pre>\n </div>\n </div>\n </div>\n\n <div class=\"mb-8\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">C\u00E1ch 2: V\u1EDBi tr\u00ECnh ch\u1EC9nh s\u1EEDa c\u00F3 th\u1EC3 \u0111\u00F3ng/m\u1EDF</h3>\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-4\">\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">HTML (toggle-editor.component.html)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>&lt;button (click)=\"openEditor()\" class=\"px-4 py-2 bg-blue-500 text-white rounded\"&gt;\n M\u1EDF tr\u00ECnh ch\u1EC9nh s\u1EEDa \u1EA3nh\n&lt;/button&gt;\n\n&#64;if (showEditor) &#123;\n &lt;libs_ui-components-image_editor\n [(imgSrc)]=\"imageSource\"\n [nameFile]=\"'my-image.jpg'\"\n (outSaveFile)=\"onSaveFile($event)\"\n (outClose)=\"onCloseEditor($event)\"&gt;\n &lt;/libs_ui-components-image_editor&gt;\n&#125;\n\n&#64;if (editedImageUrl) &#123;\n &lt;div class=\"mt-4\"&gt;\n &lt;h3 class=\"text-lg font-semibold\"&gt;H\u00ECnh \u1EA3nh \u0111\u00E3 ch\u1EC9nh s\u1EEDa:&lt;/h3&gt;\n &lt;img [src]=\"editedImageUrl\" alt=\"H\u00ECnh \u1EA3nh \u0111\u00E3 ch\u1EC9nh s\u1EEDa\" class=\"mt-2 max-w-full h-auto\" /&gt;\n &lt;/div&gt;\n&#125;</code></pre>\n </div>\n\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">TypeScript (toggle-editor.component.ts)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>import &#123; Component &#125; from '&#64;angular/core';\nimport &#123; LibsUiComponentsImageEditorComponent &#125; from '&#64;libs-ui/components-image-editor';\nimport &#123; ISaveFile &#125; from '&#64;libs-ui/components-image-editor';\n\n&#64;Component(&#123;\n selector: 'app-toggle-editor',\n standalone: true,\n imports: [LibsUiComponentsImageEditorComponent],\n templateUrl: './toggle-editor.component.html'\n&#125;)\nexport class ToggleEditorComponent &#123;\n imageSource = 'https://example.com/path/to/image.jpg';\n showEditor = false;\n editedImageUrl = '';\n\n openEditor() &#123;\n this.showEditor = true;\n &#125;\n\n onSaveFile(data: ISaveFile) &#123;\n this.editedImageUrl = data.url;\n this.showEditor = false;\n console.log('\u0110\u00E3 l\u01B0u file:', data.file);\n &#125;\n\n onCloseEditor(data: &#123;isClickButtonClose: boolean&#125;) &#123;\n this.showEditor = false;\n &#125;\n&#125;</code></pre>\n </div>\n </div>\n </div>\n\n <div class=\"mb-8\">\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">C\u00E1ch 3: V\u1EDBi t\u1EF7 l\u1EC7 khung h\u00ECnh c\u1ED1 \u0111\u1ECBnh</h3>\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-4\">\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">HTML (fixed-ratio.component.html)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>&lt;libs_ui-components-image_editor\n [(imgSrc)]=\"imageSource\"\n [aspectRatio]=\"aspectRatio\"\n [requiredCropFollowRatio]=\"true\"\n (outSaveFile)=\"onSaveFile($event)\"&gt;\n&lt;/libs_ui-components-image_editor&gt;</code></pre>\n </div>\n\n <div>\n <h4 class=\"font-semibold mb-2 text-gray-700\">TypeScript (fixed-ratio.component.ts)</h4>\n <pre class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm\"><code>import &#123; Component &#125; from '&#64;angular/core';\nimport &#123; LibsUiComponentsImageEditorComponent &#125; from '&#64;libs-ui/components-image-editor';\nimport &#123; IAspectRatio &#125; from '&#64;libs-ui/interfaces-types';\n\n&#64;Component(&#123;\n selector: 'app-fixed-ratio',\n standalone: true,\n imports: [LibsUiComponentsImageEditorComponent],\n templateUrl: './fixed-ratio.component.html'\n&#125;)\nexport class FixedRatioComponent &#123;\n imageSource = 'https://example.com/path/to/image.jpg';\n aspectRatio: IAspectRatio = &#123;\n key: '1:1',\n value: 1\n &#125;;\n\n onSaveFile(data: any) &#123;\n console.log('\u0110\u00E3 l\u01B0u h\u00ECnh \u1EA3nh v\u1EDBi t\u1EF7 l\u1EC7 1:1');\n &#125;\n&#125;</code></pre>\n </div>\n </div>\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">T\u00E0i li\u1EC7u API</h2>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">Inputs</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white border border-gray-200\">\n <thead>\n <tr>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">T\u00EAn</th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">Ki\u1EC3u d\u1EEF\n li\u1EC7u\n </th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">M\u1EB7c \u0111\u1ECBnh\n </th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">M\u00F4 t\u1EA3\n </th>\n </tr>\n </thead>\n <tbody>\n @for (input of inputsDoc; track input.name) {\n <tr>\n <td class=\"py-2 px-4 border-b border-gray-200\"><code\n class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">{{ input.name }}</code></td>\n <td class=\"py-2 px-4 border-b border-gray-200\"><code\n class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">{{ input.type }}</code></td>\n <td class=\"py-2 px-4 border-b border-gray-200\">{{ input.default }}</td>\n <td class=\"py-2 px-4 border-b border-gray-200\">{{ input.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">Outputs</h3>\n <div class=\"overflow-x-auto mb-8\">\n <table class=\"min-w-full bg-white border border-gray-200\">\n <thead>\n <tr>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">T\u00EAn</th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">Ki\u1EC3u d\u1EEF\n li\u1EC7u\n </th>\n <th class=\"py-3 px-4 border-b border-gray-200 bg-gray-100 text-left font-semibold text-gray-700\">M\u00F4 t\u1EA3\n </th>\n </tr>\n </thead>\n <tbody>\n @for (output of outputsDoc; track output.name) {\n <tr>\n <td class=\"py-2 px-4 border-b border-gray-200\"><code\n class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">{{ output.name }}</code></td>\n <td class=\"py-2 px-4 border-b border-gray-200\"><code\n class=\"text-sm bg-gray-100 px-1.5 py-0.5 rounded\">{{ output.type }}</code></td>\n <td class=\"py-2 px-4 border-b border-gray-200\">{{ output.description }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <h3 class=\"text-xl font-semibold text-gray-700 mb-4\">Interfaces</h3>\n <div class=\"space-y-6\">\n @for (interfaceItem of interfacesDoc; track interfaceItem.name) {\n <div class=\"bg-gray-50 p-6 rounded-lg\">\n <h4 class=\"text-lg font-semibold text-gray-700 mb-3\">{{ interfaceItem.name }}</h4>\n <pre\n class=\"bg-gray-100 p-4 rounded-lg overflow-x-auto text-sm mb-3\"><code>{{ interfaceItem.code }}</code></pre>\n <p class=\"text-gray-600\">{{ interfaceItem.description }}</p>\n </div>\n }\n </div>\n </section>\n\n <section class=\"bg-white rounded-lg p-8 mb-8 shadow-sm\">\n <h2 class=\"text-2xl font-bold text-gray-800 mb-5 pb-3 border-b border-gray-200\">T\u00EDnh n\u0103ng</h2>\n <ul class=\"space-y-6\">\n @for (feature of features; track feature.id) {\n <li class=\"flex items-start\">\n <span class=\"text-2xl text-blue-500 mr-4\">{{ feature.icon }}</span>\n <div>\n <h3 class=\"text-lg font-semibold text-gray-700 mb-1\">{{ feature.title }}</h3>\n <p class=\"text-gray-600\">{{ feature.description }}</p>\n </div>\n </li>\n }\n </ul>\n </section>\n </main>\n</div>\n", styles: ["pre code{@apply font-mono;}.hover\\:bg-blue-600{transition:background-color .2s ease-in-out}.overflow-x-auto{-webkit-overflow-scrolling:touch}pre::-webkit-scrollbar{@apply h-1.5 bg-gray-100;}pre::-webkit-scrollbar-thumb{@apply bg-gray-300 rounded;}button:focus,input:focus{@apply outline-none ring-2 ring-blue-400 ring-opacity-50;}input[type=file]{@apply text-sm text-gray-600;}\n"] }]
150
+ }], propDecorators: { imageFileInput: [{
151
+ type: ViewChild,
152
+ args: ['imageFileInput']
153
+ }] } });
154
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2UtZWRpdG9yLWRlbW8uY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy11aS9jb21wb25lbnRzL2ltYWdlLWVkaXRvci9zcmMvZGVtby9pbWFnZS1lZGl0b3ItZGVtby5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvaW1hZ2UtZWRpdG9yL3NyYy9kZW1vL2ltYWdlLWVkaXRvci1kZW1vLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNqRSxPQUFPLEVBQUUsc0NBQXNDLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUM1RixPQUFPLEVBQUUsb0NBQW9DLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQzs7QUFhakYsTUFBTSxPQUFPLHdDQUF3QztJQUN0QixjQUFjLENBQWdDO0lBRTNFLFdBQVcsR0FBRyxFQUFFLENBQUM7SUFDakIsVUFBVSxHQUFHLEtBQUssQ0FBQztJQUNuQixjQUFjLEdBQUcsRUFBRSxDQUFDO0lBQ3BCLFlBQVksR0FBZ0IsSUFBSSxDQUFDO0lBQ2pDLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFFZiwyQ0FBMkM7SUFDM0MsU0FBUyxHQUFHO1FBQ1YsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsa0RBQWtELEVBQUU7UUFDeEgsRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxXQUFXLEVBQUUsc0JBQXNCLEVBQUU7UUFDeEYsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxXQUFX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c0JBQTFDLFNBQVM7dUJBQUMsZ0JBQWdCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBFbGVtZW50UmVmLCBWaWV3Q2hpbGQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IExpYnNVaUNvbXBvbmVudHNCdXR0b25zQnV0dG9uQ29tcG9uZW50IH0gZnJvbSAnQGxpYnMtdWkvY29tcG9uZW50cy1idXR0b25zLWJ1dHRvbic7XG5pbXBvcnQgeyBMaWJzVWlDb21wb25lbnRzSW1hZ2VFZGl0b3JDb21wb25lbnQgfSBmcm9tICcuLi9pbWFnZS1lZGl0b3IuY29tcG9uZW50JztcbmltcG9ydCB7IElTYXZlRmlsZSB9IGZyb20gJy4uL2ludGVyZmFjZXMvaW1hZ2UtZWRpdG9yLmludGVyZmFjZSc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2xpYi1pbWFnZS1lZGl0b3ItZGVtbycsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBMaWJzVWlDb21wb25lbnRzQnV0dG9uc0J1dHRvbkNvbXBvbmVudCxcbiAgICBMaWJzVWlDb21wb25lbnRzSW1hZ2VFZGl0b3JDb21wb25lbnRcbiAgXSxcbiAgdGVtcGxhdGVVcmw6ICcuL2ltYWdlLWVkaXRvci1kZW1vLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmw6ICcuL2ltYWdlLWVkaXRvci1kZW1vLmNvbXBvbmVudC5zY3NzJ1xufSlcbmV4cG9ydCBjbGFzcyBMaWJzVWlDb21wb25lbnRzSW1hZ2VFZGl0b3JEZW1vQ29tcG9uZW50IHtcbiAgQFZpZXdDaGlsZCgnaW1hZ2VGaWxlSW5wdXQnKSBpbWFnZUZpbGVJbnB1dCE6IEVsZW1lbnRSZWY8SFRNTElucHV0RWxlbWVudD47XG5cbiAgaW1hZ2VTb3VyY2UgPSAnJztcbiAgc2hvd0VkaXRvciA9IGZhbHNlO1xuICBlZGl0ZWRJbWFnZVVybCA9ICcnO1xuICBzZWxlY3RlZEZpbGU6IEZpbGUgfCBudWxsID0gbnVsbDtcbiAgaW1hZ2VOYW1lID0gJyc7XG5cbiAgLy8gRG9jdW1lbnRhdGlvbiBkYXRhIGFzIHJlZ3VsYXIgcHJvcGVydGllc1xuICBpbnB1dHNEb2MgPSBbXG4gICAgeyBuYW1lOiAnaW1nU3JjJywgdHlwZTogJ3N0cmluZycsIGRlZmF1bHQ6ICdC4bqvdCBideG7mWMnLCBkZXNjcmlwdGlvbjogJ8SQxrDhu51uZyBk4bqrbiBob+G6t2MgYmFzZTY0IGPhu6dhIGjDrG5oIOG6o25oIGPhuqduIGNo4buJbmggc+G7rWEnIH0sXG4gICAgeyBuYW1lOiAnb3JpZ2luVXJsJywgdHlwZTogJ3N0cmluZycsIGRlZmF1bHQ6ICctJywgZGVzY3JpcHRpb246ICdVUkwgZ+G7kWMgY+G7p2EgaMOsbmgg4bqjbmgnIH0sXG4gICAgeyBuYW1lOiAnbmFtZUZpbGUnLCB0eXBlOiAnc3RyaW5nJywgZGVmYXVsdDogJy0nLCBkZXNjcmlwdGlvbjogJ1TDqm4gZmlsZSBraGkgbMawdScgfSxcbiAgICB7IG5hbWU6ICdtb2RlU2hvd0J1dHRvbicsIHR5cGU6ICdcXCdzYXZlLWZpbGVcXCcgfCBcXCdzYXZlLWFwaVxcJycsIGRlZmF1bHQ6ICdcXCdzYXZlLWZpbGVcXCcnLCBkZXNjcmlwdGlvbjogJ0No4bq/IMSR4buZIG7DunQgbMawdScgfSxcbiAgICB7IG5hbWU6ICdtaW1ldHlwZScsIHR5cGU6ICdzdHJpbmcnLCBkZWZhdWx0OiAnLScsIGRlc2NyaXB0aW9uOiAnxJDhu4tuaCBk4bqhbmcgY+G7p2EgaMOsbmgg4bqjbmgnIH0sXG4gICAgeyBuYW1lOiAnekluZGV4JywgdHlwZTogJ251bWJlcicsIGRlZmF1bHQ6ICcxMjAwJywgZGVzY3JpcHRpb246ICd6LWluZGV4IGPhu6dhIGNvbXBvbmVudCcgfSxcbiAgICB7IG5hbWU6ICdoYXNab29tJywgdHlwZTogJ2Jvb2xlYW4nLCBkZWZhdWx0OiAnZmFsc2UnLCBkZXNjcmlwdGlvbjogJ0NobyBwaMOpcCBwaMOzbmcgdG8vdGh1IG5o4buPIGjDrG5oIOG6o25oJyB9LFxuICAgIHsgbmFtZTogJ2FzcGVjdFJhdGlvJywgdHlwZTogJ0lBc3BlY3RSYXRpbycsIGRlZmF1bHQ6ICctJywgZGVzY3JpcHRpb246ICdU4bu3IGzhu4cga2h1bmcgaMOsbmggbeG6t2MgxJHhu4tuaCcgfSxcbiAgICB7IG5hbWU6ICdyZXF1aXJlZENyb3BGb2xsb3dSYXRpbycsIHR5cGU6ICdib29sZWFuJywgZGVmYXVsdDogJ2ZhbHNlJywgZGVzY3JpcHRpb246ICdC4bqvdCBideG7mWMgY+G6r3QgdGhlbyB04bu3IGzhu4cgxJHDoyBjaOG7jW4nIH1cbiAgXTtcblxuICBvdXRwdXRzRG9jID0gW1xuICAgIHsgbmFtZTogJ291dENsb3NlJywgdHlwZTogJ3tpc0NsaWNrQnV0dG9uQ2xvc2U6IGJvb2xlYW59JywgZGVzY3JpcHRpb246ICdT4buxIGtp4buHbiBraGkgxJHDs25nIHRyw6xuaCBjaOG7iW5oIHPhu61hJyB9LFxuICAgIHsgbmFtZTogJ291dFNhdmVGaWxlJywgdHlwZTogJ0lTYXZlRmlsZScsIGRlc2NyaXB0aW9uOiAnU+G7sSBraeG7h24ga2hpIGzGsHUgZmlsZScgfSxcbiAgICB7IG5hbWU6ICdvdXRGdW5jdGlvbnNDb250cm9sJywgdHlwZTogJ0lJbWFnZUVkaXRvckZ1bmN0aW9uQ29udHJvbEV2ZW50JywgZGVzY3JpcHRpb246ICdT4buxIGtp4buHbiDEkeG7gyBraeG7g20gc2/DoXQgY8OhYyBjaOG7qWMgbsSDbmcnIH1cbiAgXTtcblxuICBpbnRlcmZhY2VzRG9jID0gW1xuICAgIHtcbiAgICAgIG5hbWU6ICdJU2F2ZUZpbGUnLFxuICAgICAgY29kZTogJ2V4cG9ydCBpbnRlcmZhY2UgSVNhdmVGaWxlIHtcXG4gIGZpbGU6IEJsb2I7XFxuICB1cmw6IHN0cmluZztcXG4gIG1vZGU6IFxcJ3NhdmUtZmlsZVxcJyB8IFxcJ3NhdmUtYXBpXFwnIHwgXFwnc2F2ZS1hcGktYXMtbmV3LWZpbGVcXCc7XFxufScsXG4gICAgICBkZXNjcmlwdGlvbjogJ0ludGVyZmFjZSDEkeG7i25oIG5naMSpYSBk4buvIGxp4buHdSBraGkgbMawdSBmaWxlLidcbiAgICB9LFxuICAgIHtcbiAgICAgIG5hbWU6ICdJSW1hZ2VFZGl0b3JGdW5jdGlvbkNvbnRyb2xFdmVudCcsXG4gICAgICBjb2RlOiAnZXhwb3J0IGludGVyZmFjZSBJSW1hZ2VFZGl0b3JGdW5jdGlvbkNvbnRyb2xFdmVudCB7XFxuICBjcm9wSW1hZ2U6ICgpID0+IFByb21pc2U8c3RyaW5nPjtcXG4gIHNldExvYWRpbmdTdGF0ZTogKGxvYWRpbmc6IGJvb2xlYW4pID0+IHZvaWQ7XFxufScsXG4gICAgICBkZXNjcmlwdGlvbjogJ0ludGVyZmFjZSDEkeG7i25oIG5naMSpYSBjw6FjIGjDoG0gxJFp4buBdSBraGnhu4NuIGPDsyB0aOG7gyDEkcaw4bujYyBn4buNaSB04burIGLDqm4gbmdvw6BpIGNvbXBvbmVudC4nXG4gICAgfSxcbiAgICB7XG4gICAgICBuYW1lOiAnSUFzcGVjdFJhdGlvJyxcbiAgICAgIGNvZGU6ICdleHBvcnQgaW50ZXJmYWNlIElBc3BlY3RSYXRpbyB7XFxuICBrZXk6IHN0cmluZztcXG4gIHZhbHVlOiBudW1iZXI7XFxufScsXG4gICAgICBkZXNjcmlwdGlvbjogJ0ludGVyZmFjZSDEkeG7i25oIG5naMSpYSB04bu3IGzhu4cga2h1bmcgaMOsbmguJ1xuICAgIH1cbiAgXTtcblxuICBmZWF0dXJlcyA9IFtcbiAgICB7XG4gICAgICBpZDogMSxcbiAgICAgIGljb246ICfinILvuI8nLFxuICAgICAgdGl0bGU6ICdD4bqvdCDhuqNuaCcsXG4gICAgICBkZXNjcmlwdGlvbjogJ0Phuq90IGjDrG5oIOG6o25oIHRoZW8ga2h1IHbhu7FjIHTDuXkgY2jhu4luaCBob+G6t2MgZOG7sWEgdHLDqm4gdOG7tyBs4buHIMSR4buLbmggdHLGsOG7m2MgKDE6MSwgNDozLCAxNjo5LCB2LnYuKS4nXG4gICAgfSxcbiAgICB7XG4gICAgICBpZDogMixcbiAgICAgIGljb246ICfwn5SEJyxcbiAgICAgIHRpdGxlOiAnWG9heSB2w6AgbOG6rXQnLFxuICAgICAgZGVzY3JpcHRpb246ICdYb2F5IGjDrG5oIOG6o25oIHRoZW8gY8OhYyBnw7NjIGtow6FjIG5oYXUgaG/hurdjIGzhuq10IHRoZW8gY2hp4buBdSBuZ2FuZy9k4buNYy4nXG4gICAgfSxcbiAgICB7XG4gICAgICBpZDogMyxcbiAgICAgIGljb246ICfwn5OPJyxcbiAgICAgIHRpdGxlOiAnVGhheSDEkeG7lWkga8OtY2ggdGjGsOG7m2MnLFxuICAgICAgZGVzY3JpcHRpb246ICdUaGF5IMSR4buVaSBrw61jaCB0aMaw4bubYyBow6xuaCDhuqNuaCB0aGVvIHThu7cgbOG7hyBwaOG6p24gdHLEg20gaG/hurdjIGvDrWNoIHRoxrDhu5tjIGPhu6UgdGjhu4MuJ1xuICAgIH0sXG4gICAge1xuICAgICAgaWQ6IDQsXG4gICAgICBpY29uOiAn8J+SvicsXG4gICAgICB0aXRsZTogJ05oaeG7gXUgY2jhur8gxJHhu5kgbMawdScsXG4gICAgICBkZXNjcmlwdGlvbjogJ0jhu5cgdHLhu6Mgbmhp4buBdSBjaOG6vyDEkeG7mSBsxrB1OiB04bqjaSB4deG7kW5nIHRy4buxYyB0aeG6v3AgaG/hurdjIGfhu61pIMSR4bq/biBBUEkuJ1xuICAgIH0sXG4gICAge1xuICAgICAgaWQ6IDUsXG4gICAgICBpY29uOiAn8J+UjScsXG4gICAgICB0aXRsZTogJ1Bow7NuZyB0by90aHUgbmjhu48nLFxuICAgICAgZGVzY3JpcHRpb246ICdUw61uaCBuxINuZyBwaMOzbmcgdG8vdGh1IG5o4buPIMSR4buDIHhlbSBjaGkgdGnhur90IGjDrG5oIOG6o25oIGtoaSBj4bqnbiB0aGnhur90LidcbiAgICB9XG4gIF07XG5cbiAgY29kZUV4YW1wbGVzID0gW1xuICAgIHtcbiAgICAgIGlkOiAxLFxuICAgICAgdGl0bGU6ICdT4butIGThu6VuZyBjxqEgYuG6o24nLFxuICAgICAgY29kZTogYGltcG9ydCB7IENvbXBvbmVudCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xcbmltcG9ydCB7IExpYnNVaUNvbXBvbmVudHNJbWFnZUVkaXRvckNvbXBvbmVudCB9IGZyb20gJ0BsaWJzLXVpL2NvbXBvbmVudHMtaW1hZ2UtZWRpdG9yJztcXG5cXG5AQ29tcG9uZW50KHtcXG4gIHNlbGVjdG9yOiAnYXBwLWV4YW1wbGUnLFxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcXG4gIGltcG9ydHM6IFtMaWJzVWlDb21wb25lbnRzSW1hZ2VFZGl0b3JDb21wb25lbnRdLFxcbiAgdGVtcGxhdGU6IFxcYFxcbiAgICA8bGlic191aS1jb21wb25lbnRzLWltYWdlX2VkaXRvclxcbiAgICAgIFsoaW1nU3JjKV09XCJpbWFnZVNvdXJjZVwiXFxuICAgICAgW21vZGVTaG93QnV0dG9uXT1cIidzYXZlLWZpbGUnXCJcXG4gICAgICBbbmFtZUZpbGVdPVwiJ2ltYWdlLmpwZydcIlxcbiAgICAgIChvdXRTYXZlRmlsZSk9XCJvblNhdmVGaWxlKCRldmVudClcIlxcbiAgICAgIChvdXRDbG9zZSk9XCJvbkNsb3NlKCRldmVudClcIj5cXG4gICAgPC9saWJzX3VpLWNvbXBvbmVudHMtaW1hZ2VfZWRpdG9yPlxcbiAgXFxgXFxufSlcXG5leHBvcnQgY2xhc3MgRXhhbXBsZUNvbXBvbmVudCB7XFxuICBpbWFnZVNvdXJjZSA9ICdodHRwczovL2V4YW1wbGUuY29tL3BhdGgvdG8vaW1hZ2UuanBnJztcXG5cXG4gIG9uU2F2ZUZpbGUoZGF0YToge2ZpbGU6IEJsb2IsIHVybDogc3RyaW5nLCBtb2RlOiBzdHJpbmd9KSB7XFxuICAgIGNvbnNvbGUubG9nKCdGaWxlIMSRw6MgxJHGsOG7o2MgbMawdTonLCBkYXRhKTtcXG4gIH1cXG5cXG4gIG9uQ2xvc2UoZGF0YToge2lzQ2xpY2tCdXR0b25DbG9zZTogYm9vbGVhbn0pIHtcXG4gICAgY29uc29sZS5sb2coJ8SQw6MgxJHDs25nIHRyw6xuaCBjaOG7iW5oIHPhu61hIOG6o25oJyk7XFxuICB9XFxufWBcbiAgICB9LFxuICAgIHtcbiAgICAgIGlkOiAyLFxuICAgICAgdGl0bGU6ICdT4butIGThu6VuZyB24bubaSB04bu3IGzhu4cgY+G7kSDEkeG7i25oJyxcbiAgICAgIGNvZGU6IGBpbXBvcnQgeyBDb21wb25lbnQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcXG5pbXBvcnQgeyBMaWJzVWlDb21wb25lbnRzSW1hZ2VFZGl0b3JDb21wb25lbnQgfSBmcm9tICdAbGlicy11aS9jb21wb25lbnRzLWltYWdlLWVkaXRvcic7XFxuaW1wb3J0IHsgSUFzcGVjdFJhdGlvIH0gZnJvbSAnQGxpYnMtdWkvaW50ZXJmYWNlcy10eXBlcyc7XFxuXFxuQENvbXBvbmVudCh7XFxuICBzZWxlY3RvcjogJ2FwcC1leGFtcGxlJyxcXG4gIHN0YW5kYWxvbmU6IHRydWUsXFxuICBpbXBvcnRzOiBbTGlic1VpQ29tcG9uZW50c0ltYWdlRWRpdG9yQ29tcG9uZW50XSxcXG4gIHRlbXBsYXRlOiBcXGBcXG4gICAgPGxpYnNfdWktY29tcG9uZW50cy1pbWFnZV9lZGl0b3JcXG4gICAgICBbKGltZ1NyYyldPVwiaW1hZ2VTb3VyY2VcIlxcbiAgICAgIFthc3BlY3RSYXRpb109XCJhc3BlY3RSYXRpb1wiXFxuICAgICAgW3JlcXVpcmVkQ3JvcEZvbGxvd1JhdGlvXT1cInRydWVcIlxcbiAgICAgIChvdXRTYXZlRmlsZSk9XCJvblNhdmVGaWxlKCRldmVudClcIj5cXG4gICAgPC9saWJzX3VpLWNvbXBvbmVudHMtaW1hZ2VfZWRpdG9yPlxcbiAgXFxgXFxufSlcXG5leHBvcnQgY2xhc3MgRXhhbXBsZUNvbXBvbmVudCB7XFxuICBpbWFnZVNvdXJjZSA9ICdodHRwczovL2V4YW1wbGUuY29tL3BhdGgvdG8vaW1hZ2UuanBnJztcXG4gIGFzcGVjdFJhdGlvOiBJQXNwZWN0UmF0aW8gPSB7XFxuICAgIGtleTogJzE6MScsXFxuICAgIHZhbHVlOiAxXFxuICB9O1xcblxcbiAgb25TYXZlRmlsZShkYXRhOiBhbnkpIHtcXG4gICAgY29uc29sZS5sb2coJ8SQw6MgbMawdSBow6xuaCDhuqNuaCB24bubaSB04bu3IGzhu4cgMToxJyk7XFxuICB9XFxufWBcbiAgICB9LFxuICAgIHtcbiAgICAgIGlkOiAzLFxuICAgICAgdGl0bGU6ICdT4butIGThu6VuZyB24bubaSBwaOG6p24gxJFp4buBdSBraGnhu4NuIGLDqm4gbmdvw6BpJyxcbiAgICAgIGNvZGU6IGBpbXBvcnQgeyBDb21wb25lbnQsIFZpZXdDaGlsZCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xcbmltcG9ydCB7IExpYnNVaUNvbXBvbmVudHNJbWFnZUVkaXRvckNvbXBvbmVudCwgSUltYWdlRWRpdG9yRnVuY3Rpb25Db250cm9sRXZlbnQgfSBmcm9tICdAbGlicy11aS9jb21wb25lbnRzLWltYWdlLWVkaXRvcic7XFxuXFxuQENvbXBvbmVudCh7XFxuICBzZWxlY3RvcjogJ2FwcC1leGFtcGxlJyxcXG4gIHN0YW5kYWxvbmU6IHRydWUsXFxuICBpbXBvcnRzOiBbTGlic1VpQ29tcG9uZW50c0ltYWdlRWRpdG9yQ29tcG9uZW50XSxcXG4gIHRlbXBsYXRlOiBcXGBcXG4gICAgPGJ1dHRvbiAoY2xpY2spPVwiY3JvcEFuZFNhdmUoKVwiPkPhuq90IHbDoCBsxrB1IG5nYXk8L2J1dHRvbj5cXG4gICAgPGxpYnNfdWktY29tcG9uZW50cy1pbWFnZV9lZGl0b3JcXG4gICAgICBbKGltZ1NyYyldPVwiaW1hZ2VTb3VyY2VcIlxcbiAgICAgIChvdXRGdW5jdGlvbnNDb250cm9sKT1cIm9uRnVuY3Rpb25zQ29udHJvbCgkZXZlbnQpXCI+XFxuICAgIDwvbGlic191aS1jb21wb25lbnRzLWltYWdlX2VkaXRvcj5cXG4gIFxcYFxcbn0pXFxuZXhwb3J0IGNsYXNzIEV4YW1wbGVDb21wb25lbnQge1xcbiAgaW1hZ2VTb3VyY2UgPSAnaHR0cHM6Ly9leGFtcGxlLmNvbS9wYXRoL3RvL2ltYWdlLmpwZyc7XFxuICBlZGl0b3JGdW5jdGlvbnMhOiBJSW1hZ2VFZGl0b3JGdW5jdGlvbkNvbnRyb2xFdmVudDtcXG5cXG4gIG9uRnVuY3Rpb25zQ29udHJvbChldmVudDogSUltYWdlRWRpdG9yRnVuY3Rpb25Db250cm9sRXZlbnQpIHtcXG4gICAgdGhpcy5lZGl0b3JGdW5jdGlvbnMgPSBldmVudDtcXG4gIH1cXG5cXG4gIGFzeW5jIGNyb3BBbmRTYXZlKCkge1xcbiAgICBpZiAodGhpcy5lZGl0b3JGdW5jdGlvbnMpIHtcXG4gICAgICB0aGlzLmVkaXRvckZ1bmN0aW9ucy5zZXRMb2FkaW5nU3RhdGUodHJ1ZSk7XFxuICAgICAgdHJ5IHtcXG4gICAgICAgIGNvbnN0IGRhdGFVcmwgPSBhd2FpdCB0aGlzLmVkaXRvckZ1bmN0aW9ucy5jcm9wSW1hZ2UoKTtcXG4gICAgICAgIGNvbnNvbGUubG9nKCdIw6xuaCDhuqNuaCDEkcOjIMSRxrDhu6NjIGPhuq90OicsIGRhdGFVcmwpO1xcbiAgICAgIH0gZmluYWxseSB7XFxuICAgICAgICB0aGlzLmVkaXRvckZ1bmN0aW9ucy5zZXRMb2FkaW5nU3RhdGUoZmFsc2UpO1xcbiAgICAgIH1cXG4gICAgfVxcbiAgfVxcbn1gXG4gICAgfVxuICBdO1xuXG4gIGNvcHlUb0NsaXBib2FyZCh0ZXh0OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBuYXZpZ2F0b3IuY2xpcGJvYXJkLndyaXRlVGV4dCh0ZXh0KS50aGVuKCgpID0+IHtcbiAgICAgIGFsZXJ0KCfEkMOjIHNhbyBjaMOpcCB2w6BvIGNsaXBib2FyZCcpO1xuICAgIH0pLmNhdGNoKGVyciA9PiB7XG4gICAgICBjb25zb2xlLmVycm9yKCdLaMO0bmcgdGjhu4Mgc2FvIGNow6lwOiAnLCBlcnIpO1xuICAgIH0pO1xuICB9XG5cbiAgb25GaWxlU2VsZWN0ZWQoZXZlbnQ6IEV2ZW50KSB7XG4gICAgY29uc3QgaW5wdXQgPSBldmVudC50YXJnZXQgYXMgSFRNTElucHV0RWxlbWVudDtcbiAgICBpZiAoaW5wdXQuZmlsZXMgJiYgaW5wdXQuZmlsZXMubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5zZWxlY3RlZEZpbGUgPSBpbnB1dC5maWxlc1swXTtcbiAgICAgIHRoaXMuaW1hZ2VOYW1lID0gdGhpcy5zZWxlY3RlZEZpbGUubmFtZTtcbiAgICAgIGNvbnNvbGUubG9nKCdGaWxlIHNlbGVjdGVkOicsIHRoaXMuc2VsZWN0ZWRGaWxlLm5hbWUpO1xuICAgIH1cbiAgfVxuXG4gIHVwbG9hZEFuZEVkaXQoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnNlbGVjdGVkRmlsZSkge1xuICAgICAgYWxlcnQoJ1Z1aSBsw7JuZyBjaOG7jW4gbeG7mXQgZmlsZSBow6xuaCDhuqNuaCB0csaw4bubYycpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHJlYWRlciA9IG5ldyBGaWxlUmVhZGVyKCk7XG4gICAgcmVhZGVyLm9ubG9hZCA9IChlOiBQcm9ncmVzc0V2ZW50PEZpbGVSZWFkZXI+KSA9PiB7XG4gICAgICBpZiAoZS50YXJnZXQ/LnJlc3VsdCkge1xuICAgICAgICB0aGlzLmltYWdlU291cmNlID0gZS50YXJnZXQucmVzdWx0IGFzIHN0cmluZztcbiAgICAgICAgdGhpcy5zaG93RWRpdG9yID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHJlYWRlci5yZWFkQXNEYXRhVVJMKHRoaXMuc2VsZWN0ZWRGaWxlKTtcbiAgfVxuXG4gIG9uU2F2ZUltYWdlKGRhdGE6IElTYXZlRmlsZSk6IHZvaWQge1xuICAgIGNvbnNvbGUubG9nKCdJbWFnZSBzYXZlZDonLCBkYXRhKTtcbiAgICB0aGlzLmVkaXRlZEltYWdlVXJsID0gZGF0YS51cmw7XG4gICAgdGhpcy5zaG93RWRpdG9yID0gZmFsc2U7XG4gIH1cblxuICBvbkNsb3NlRWRpdG9yKGRhdGE6IHsgaXNDbGlja0J1dHRvbkNsb3NlOiBib29sZWFuIH0pOiB2b2lkIHtcbiAgICBjb25zb2xlLmxvZygnRWRpdG9yIGNsb3NlZDonLCBkYXRhKTtcbiAgICB0aGlzLnNob3dFZGl0b3IgPSBmYWxzZTtcbiAgfVxuXG4gIGRvd25sb2FkSW1hZ2UoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuZWRpdGVkSW1hZ2VVcmwpIHtcbiAgICAgIGNvbnN0IGxpbmsgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgICBsaW5rLmhyZWYgPSB0aGlzLmVkaXRlZEltYWdlVXJsO1xuICAgICAgbGluay5kb3dubG9hZCA9IHRoaXMuaW1hZ2VOYW1lIHx8ICdlZGl0ZWQtaW1hZ2UuanBnJztcbiAgICAgIGxpbmsuY2xpY2soKTtcbiAgICB9XG4gIH1cbn0gIiwiPGRpdiBjbGFzcz1cIm1heC13LTZ4bCBteC1hdXRvIHAtNSBmb250LXNhbnMgdGV4dC1ncmF5LTgwMFwiPlxuICA8aGVhZGVyIGNsYXNzPVwidGV4dC1jZW50ZXIgcHktMTAgYmctd2hpdGUgcm91bmRlZC1sZyBtYi04IHNoYWRvdy1zbVwiPlxuICAgIDxoMSBjbGFzcz1cInRleHQtNHhsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTJcIj5EZW1vIFRyw6xuaCBDaOG7iW5oIFPhu61hIEjDrG5oIOG6om5oPC9oMT5cbiAgICA8cCBjbGFzcz1cInRleHQteGwgdGV4dC1ncmF5LTUwMFwiPlRoxrAgdmnhu4duIGNvbXBvbmVudCBjaG8gQW5ndWxhciDEkeG7gyBjaOG7iW5oIHPhu61hIGjDrG5oIOG6o25oPC9wPlxuICA8L2hlYWRlcj5cblxuICA8bWFpbj5cbiAgICA8c2VjdGlvbiBjbGFzcz1cImJnLXdoaXRlIHJvdW5kZWQtbGcgcC04IG1iLTggc2hhZG93LXNtXCI+XG4gICAgICA8aDIgY2xhc3M9XCJ0ZXh0LTJ4bCBmb250LWJvbGQgdGV4dC1ncmF5LTgwMCBtYi01IHBiLTMgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwXCI+R2nhu5tpIHRoaeG7h3U8L2gyPlxuICAgICAgPHA+XG4gICAgICAgIDxjb2RlIGNsYXNzPVwidGV4dC1zbSBiZy1ncmF5LTEwMCBweC0xLjUgcHktMC41IHJvdW5kZWRcIj4mIzY0O2xpYnMtdWkvY29tcG9uZW50cy1pbWFnZS1lZGl0b3I8L2NvZGU+IGzDoCBt4buZdFxuICAgICAgICBjb21wb25lbnQgQW5ndWxhciBt4bqhbmggbeG6vSBjaG8gcGjDqXAgbmfGsOG7nWkgZMO5bmcgdGjhu7FjIGhp4buHbiBjw6FjIHRoYW8gdMOhYyBjaOG7iW5oIHPhu61hIGjDrG5oIOG6o25oIG5oxrAgY+G6r3QsIHRoYXkgxJHhu5VpIGvDrWNoXG4gICAgICAgIHRoxrDhu5tjLCB4b2F5LCBs4bqtdCB2w6Agw6FwIGThu6VuZyB04bu3IGzhu4cgY+G7kSDEkeG7i25oLlxuICAgICAgPC9wPlxuICAgIDwvc2VjdGlvbj5cblxuICAgIDxzZWN0aW9uIGNsYXNzPVwiYmctd2hpdGUgcm91bmRlZC1sZyBwLTggbWItOCBzaGFkb3ctc21cIj5cbiAgICAgIDxoMiBjbGFzcz1cInRleHQtMnhsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTUgcGItMyBib3JkZXItYiBib3JkZXItZ3JheS0yMDBcIj5Dw6BpIMSR4bq3dDwvaDI+XG5cbiAgICAgIDxkaXYgY2xhc3M9XCJtYi02XCI+XG4gICAgICAgIDxoMyBjbGFzcz1cInRleHQteGwgZm9udC1zZW1pYm9sZCB0ZXh0LWdyYXktNzAwIG1iLTNcIj5Zw6p1IGPhuqd1PC9oMz5cbiAgICAgICAgPHVsIGNsYXNzPVwibGlzdC1kaXNjIHBsLTUgc3BhY2UteS0yIHRleHQtZ3JheS02MDBcIj5cbiAgICAgICAgICA8bGk+PHNwYW4gY2xhc3M9XCJmb250LXNlbWlib2xkXCI+QW5ndWxhcjwvc3Bhbj46IDE4LjAuMCB0cuG7nyBsw6puPC9saT5cbiAgICAgICAgICA8bGk+PHNwYW4gY2xhc3M9XCJmb250LXNlbWlib2xkXCI+VGFpbHdpbmQgQ1NTPC9zcGFuPjogMy4zLjAgdHLhu58gbMOqbjwvbGk+XG4gICAgICAgIDwvdWw+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPHAgY2xhc3M9XCJtYi00XCI+xJDhu4MgY8OgaSDEkeG6t3QgdGjGsCB2aeG7h24sIHPhu60gZOG7pW5nIG5wbSBob+G6t2MgeWFybjo8L3A+XG5cbiAgICAgIDxkaXYgY2xhc3M9XCJmbGV4IGl0ZW1zLWNlbnRlciBiZy1ncmF5LTEwMCBwLTQgcm91bmRlZC1sZyBtYi02XCI+XG4gICAgICAgIDxwcmUgY2xhc3M9XCJmbGV4LTEgdGV4dC1zbSBvdmVyZmxvdy14LWF1dG9cIj48Y29kZT5ucG0gaW5zdGFsbCAmIzY0O2xpYnMtdWkvY29tcG9uZW50cy1pbWFnZS1lZGl0b3I8L2NvZGU+PC9wcmU+XG4gICAgICAgIDxidXR0b24gY2xhc3M9XCJtbC00IHB4LTMgcHktMSBiZy1ibHVlLTUwMCB0ZXh0LXdoaXRlIHJvdW5kZWQgaG92ZXI6YmctYmx1ZS02MDAgdHJhbnNpdGlvbi1jb2xvcnNcIlxuICAgICAgICAgIChjbGljayk9XCJjb3B5VG9DbGlwYm9hcmQoJ25wbSBpbnN0YWxsIEBsaWJzLXVpL2NvbXBvbmVudHMtaW1hZ2UtZWRpdG9yJylcIj5cbiAgICAgICAgICBTYW8gY2jDqXBcbiAgICAgICAgPC9idXR0b24+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPHAgY2xhc3M9XCJtYi00XCI+SG/hurdjIHbhu5tpIHlhcm46PC9wPlxuXG4gICAgICA8ZGl2IGNsYXNzPVwiZmxleCBpdGVtcy1jZW50ZXIgYmctZ3JheS0xMDAgcC00IHJvdW5kZWQtbGcgbWItNlwiPlxuICAgICAgICA8cHJlIGNsYXNzPVwiZmxleC0xIHRleHQtc20gb3ZlcmZsb3cteC1hdXRvXCI+PGNvZGU+eWFybiBhZGQgJiM2NDtsaWJzLXVpL2NvbXBvbmVudHMtaW1hZ2UtZWRpdG9yPC9jb2RlPjwvcHJlPlxuICAgICAgICA8YnV0dG9uIGNsYXNzPVwibWwtNCBweC0zIHB5LTEgYmctYmx1ZS01MDAgdGV4dC13aGl0ZSByb3VuZGVkIGhvdmVyOmJnLWJsdWUtNjAwIHRyYW5zaXRpb24tY29sb3JzXCJcbiAgICAgICAgICAoY2xpY2spPVwiY29weVRvQ2xpcGJvYXJkKCd5YXJuIGFkZCBAbGlicy11aS9jb21wb25lbnRzLWltYWdlLWVkaXRvcicpXCI+XG4gICAgICAgICAgU2FvIGNow6lwXG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgPC9kaXY+XG4gICAgPC9zZWN0aW9uPlxuXG4gICAgPHNlY3Rpb24gY2xhc3M9XCJiZy13aGl0ZSByb3VuZGVkLWxnIHAtOCBtYi04IHNoYWRvdy1zbVwiPlxuICAgICAgPGgyIGNsYXNzPVwidGV4dC0yeGwgZm9udC1ib2xkIHRleHQtZ3JheS04MDAgbWItNSBwYi0zIGJvcmRlci1iIGJvcmRlci1ncmF5LTIwMFwiPkRlbW8gdHLhu7FjIHRp4bq/cDwvaDI+XG4gICAgICA8ZGl2PlxuICAgICAgICA8cCBjbGFzcz1cIm1iLTRcIj5U4bqjaSBsw6puIGjDrG5oIOG6o25oIHbDoCBjaOG7iW5oIHPhu61hIG5nYXkgdHLDqm4gdHLDrG5oIGR1eeG7h3Q6PC9wPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiZmxleCBmbGV4LXdyYXAgaXRlbXMtY2VudGVyIGdhcC00IG1iLThcIj5cbiAgICAgICAgICA8aW5wdXQgdHlwZT1cImZpbGVcIlxuICAgICAgICAgICAgI2ltYWdlRmlsZUlucHV0XG4gICAgICAgICAgICBhY2NlcHQ9XCJpbWFnZS8qXCJcbiAgICAgICAgICAgIGNsYXNzPVwiYm9yZGVyIGJvcmRlci1ncmF5LTMwMCByb3VuZGVkIHB4LTMgcHktMlwiXG4gICAgICAgICAgICAoY2hhbmdlKT1cIm9uRmlsZVNlbGVjdGVkKCRldmVudClcIiAvPlxuICAgICAgICAgIDxsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b24gbGFiZWw9XCJU4bqjaSBsw6puIHbDoCBjaOG7iW5oIHPhu61hXCJcbiAgICAgICAgICAgIChjbGljayk9XCJ1cGxvYWRBbmRFZGl0KClcIj5cbiAgICAgICAgICA8L2xpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvbj5cbiAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgQGlmIChlZGl0ZWRJbWFnZVVybCkge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJtdC04IHAtNiBiZy1ncmF5LTUwIHJvdW5kZWQtbGdcIj5cbiAgICAgICAgICAgIDxoMyBjbGFzcz1cInRleHQteGwgZm9udC1zZW1pYm9sZCB0ZXh0LWdyYXktNzAwIG1iLTRcIj5L4bq/dCBxdeG6ozwvaDM+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWF4LXcteGwgbXgtYXV0byBtYi00IGJvcmRlciBib3JkZXItZ3JheS0yMDAgcm91bmRlZC1sZyBvdmVyZmxvdy1oaWRkZW4gc2hhZG93LW1kXCI+XG4gICAgICAgICAgICAgIDxpbWcgW3NyY109XCJlZGl0ZWRJbWFnZVVybFwiXG4gICAgICAgICAgICAgICAgYWx0PVwiSMOsbmgg4bqjbmggxJHDoyBjaOG7iW5oIHPhu61hXCJcbiAgICAgICAgICAgICAgICBjbGFzcz1cInctZnVsbCBoLWF1dG9cIiAvPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZmxleCBqdXN0aWZ5LWNlbnRlclwiPlxuICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uIGxhYmVsPVwiVOG6o2kgeHXhu5FuZyBow6xuaCDhuqNuaFwiXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cImRvd25sb2FkSW1hZ2UoKVwiPlxuICAgICAgICAgICAgICA8L2xpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvbj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cbiAgICA8L3NlY3Rpb24+XG5cbiAgICBAaWYgKHNob3dFZGl0b3IpIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJmaXhlZCBpbnNldC0wIHotNTBcIj5cbiAgICAgICAgPGxpYnNfdWktY29tcG9uZW50cy1pbWFnZV9lZGl0b3IgWyhpbWdTcmMpXT1cImltYWdlU291cmNlXCJcbiAgICAgICAgICBbbmFtZUZpbGVdPVwiaW1hZ2VOYW1lIHx8ICdkZW1vLWltYWdlLmpwZydcIlxuICAgICAgICAgIFttb2RlU2hvd0J1dHRvbl09XCInc2F2ZS1maWxlJ1wiXG4gICAgICAgICAgW2hhc1pvb21dPVwidHJ1ZVwiXG4gICAgICAgICAgKG91dFNhdmVGaWxlKT1cIm9uU2F2ZUltYWdlKCRldmVudClcIlxuICAgICAgICAgIChvdXRDbG9zZSk9XCJvbkNsb3NlRWRpdG9yKCRldmVudClcIiAvPlxuICAgICAgPC9kaXY+XG4gICAgfVxuXG4gICAgPHNlY3Rpb24gY2xhc3M9XCJiZy13aGl0ZSByb3VuZGVkLWxnIHAtOCBtYi04IHNoYWRvdy1zbVwiPlxuICAgICAgPGgyIGNsYXNzPVwidGV4dC0yeGwgZm9udC1ib2xkIHRleHQtZ3JheS04MDAgbWItNSBwYi0zIGJvcmRlci1iIGJvcmRlci1ncmF5LTIwMFwiPkPDoWNoIHPhu60gZOG7pW5nPC9oMj5cblxuICAgICAgPGRpdiBjbGFzcz1cIm1iLThcIj5cbiAgICAgICAgPGgzIGNsYXNzPVwidGV4dC14bCBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDAgbWItNFwiPkPDoWNoIDE6IFPhu60gZOG7pW5nIGZpbGUgSFRNTCByacOqbmcgYmnhu4d0PC9oMz5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwiZ3JpZCBncmlkLWNvbHMtMSBtZDpncmlkLWNvbHMtMiBnYXAtNiBtYi00XCI+XG4gICAgICAgICAgPGRpdj5cbiAgICAgICAgICAgIDxoNCBjbGFzcz1cImZvbnQtc2VtaWJvbGQgbWItMiB0ZXh0LWdyYXktNzAwXCI+SFRNTCAoZXhhbXBsZS5jb21wb25lbnQuaHRtbCk8L2g0PlxuICAgICAgICAgICAgPHByZSBjbGFzcz1cImJnLWdyYXktMTAwIHAtNCByb3VuZGVkLWxnIG92ZXJmbG93LXgtYXV0byB0ZXh0LXNtXCI+PGNvZGU+Jmx0O2xpYnNfdWktY29tcG9uZW50cy1pbWFnZV9lZGl0b3JcbiAgWyhpbWdTcmMpXT1cImltYWdlU291cmNlXCJcbiAgW25hbWVGaWxlXT1cIidpbWFnZS5qcGcnXCJcbiAgW21vZGVTaG93QnV0dG9uXT1cIidzYXZlLWZpbGUnXCJcbiAgKG91dFNhdmVGaWxlKT1cIm9uU2F2ZUZpbGUoJGV2ZW50KVwiXG4gIChvdXRDbG9zZSk9XCJvbkNsb3NlKCRldmVudClcIiZndDtcbiZsdDsvbGlic191aS1jb21wb25lbnRzLWltYWdlX2VkaXRvciZndDs8L2NvZGU+PC9wcmU+XG4gICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICA8ZGl2PlxuICAgICAgICAgICAgPGg0IGNsYXNzPVwiZm9udC1zZW1pYm9sZCBtYi0yIHRleHQtZ3JheS03MDBcIj5UeXBlU2NyaXB0IChleGFtcGxlLmNvbXBvbmVudC50cyk8L2g0PlxuICAgICAgICAgICAgPHByZSBjbGFzcz1cImJnLWdyYXktMTAwIHAtNCByb3VuZGVkLWxnIG92ZXJmbG93LXgtYXV0byB0ZXh0LXNtXCI+PGNvZGU+aW1wb3J0ICYjMTIzOyBDb21wb25lbnQgJiMxMjU7IGZyb20gJyYjNjQ7YW5ndWxhci9jb3JlJztcbmltcG9ydCAmIzEyMzsgTGlic1VpQ29tcG9uZW50c0ltYWdlRWRpdG9yQ29tcG9uZW50ICYjMTI1OyBmcm9tICcmIzY0O2xpYnMtdWkvY29tcG9uZW50cy1pbWFnZS1lZGl0b3InO1xuXG4mIzY0O0NvbXBvbmVudCgmIzEyMztcbiAgc2VsZWN0b3I6ICdhcHAtZXhhbXBsZScsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtMaWJzVWlDb21wb25lbnRzSW1hZ2VFZGl0b3JDb21wb25lbnRdLFxuICB0ZW1wbGF0ZVVybDogJy4vZXhhbXBsZS5jb21wb25lbnQuaHRtbCdcbiYjMTI1OylcbmV4cG9ydCBjbGFzcyBFeGFtcGxlQ29tcG9uZW50ICYjMTIzO1xuICBpbWFnZVNvdXJjZSA9ICdodHRwczovL2V4YW1wbGUuY29tL3BhdGgvdG8vaW1hZ2UuanBnJztcblxuICBvblNhdmVGaWxlKGRhdGE6ICYjMTIzO2ZpbGU6IEJsb2IsIHVybDogc3RyaW5nLCBtb2RlOiBzdHJpbmcmIzEyNTspICYjMTIzO1xuICAgIGNvbnNvbGUubG9nKCdGaWxlIMSRw6MgxJHGsOG7o2MgbMawdTonLCBkYXRhKTtcbiAgJiMxMjU7XG5cbiAgb25DbG9zZShkYXRhOiAmIzEyMztpc0NsaWNrQnV0dG9uQ2xvc2U6IGJvb2xlYW4mIzEyNTspICYjMTIzO1xuICAgIGNvbnNvbGUubG9nKCfEkMOjIMSRw7NuZyB0csOsbmggY2jhu4luaCBz4butYSDhuqNuaCcpO1xuICAmIzEyNTtcbiYjMTI1OzwvY29kZT48L3ByZT5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPGRpdiBjbGFzcz1cIm1iLThcIj5cbiAgICAgICAgPGgzIGNsYXNzPVwidGV4dC14bCBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDAgbWItNFwiPkPDoWNoIDI6IFbhu5tpIHRyw6xuaCBjaOG7iW5oIHPhu61hIGPDsyB0aOG7gyDEkcOzbmcvbeG7nzwvaDM+XG5cbiAgICAgICAgPGRpdiBjbGFzcz1cImdyaWQgZ3JpZC1jb2xzLTEgbWQ6Z3JpZC1jb2xzLTIgZ2FwLTYgbWItNFwiPlxuICAgICAgICAgIDxkaXY+XG4gICAgICAgICAgICA8aDQgY2xhc3M9XCJmb250LXNlbWlib2xkIG1iLTIgdGV4dC1ncmF5LTcwMFwiPkhUTUwgKHRvZ2dsZS1lZGl0b3IuY29tcG9uZW50Lmh0bWwpPC9oND5cbiAgICAgICAgICAgIDxwcmUgY2xhc3M9XCJiZy1ncmF5LTEwMCBwLTQgcm91bmRlZC1sZyBvdmVyZmxvdy14LWF1dG8gdGV4dC1zbVwiPjxjb2RlPiZsdDtidXR0b24gKGNsaWNrKT1cIm9wZW5FZGl0b3IoKVwiIGNsYXNzPVwicHgtNCBweS0yIGJnLWJsdWUtNTAwIHRleHQtd2hpdGUgcm91bmRlZFwiJmd0O1xuICBN4bufIHRyw6xuaCBjaOG7iW5oIHPhu61hIOG6o25oXG4mbHQ7L2J1dHRvbiZndDtcblxuJiM2NDtpZiAoc2hvd0VkaXRvcikgJiMxMjM7XG4gICZsdDtsaWJzX3VpLWNvbXBvbmVudHMtaW1hZ2VfZWRpdG9yXG4gICAgWyhpbWdTcmMpXT1cImltYWdlU291cmNlXCJcbiAgICBbbmFtZUZpbGVdPVwiJ215LWltYWdlLmpwZydcIlxuICAgIChvdXRTYXZlRmlsZSk9XCJvblNhdmVGaWxlKCRldmVudClcIlxuICAgIChvdXRDbG9zZSk9XCJvbkNsb3NlRWRpdG9yKCRldmVudClcIiZndDtcbiAgJmx0Oy9saWJzX3VpLWNvbXBvbmVudHMtaW1hZ2VfZWRpdG9yJmd0O1xuJiMxMjU7XG5cbiYjNjQ7aWYgKGVkaXRlZEltYWdlVXJsKSAmIzEyMztcbiAgJmx0O2RpdiBjbGFzcz1cIm10LTRcIiZndDtcbiAgICAmbHQ7aDMgY2xhc3M9XCJ0ZXh0LWxnIGZvbnQtc2VtaWJvbGRcIiZndDtIw6xuaCDhuqNuaCDEkcOjIGNo4buJbmggc+G7rWE6Jmx0Oy9oMyZndDtcbiAgICAmbHQ7aW1nIFtzcmNdPVwiZWRpdGVkSW1hZ2VVcmxcIiBhbHQ9XCJIw6xuaCDhuqNuaCDEkcOjIGNo4buJbmggc+G7rWFcIiBjbGFzcz1cIm10LTIgbWF4LXctZnVsbCBoLWF1dG9cIiAvJmd0O1xuICAmbHQ7L2RpdiZndDtcbiYjMTI1OzwvY29kZT48L3ByZT5cbiAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgIDxkaXY+XG4gICAgICAgICAgICA8aDQgY2xhc3M9XCJmb250LXNlbWlib2xkIG1iLTIgdGV4dC1ncmF5LTcwMFwiPlR5cGVTY3JpcHQgKHRvZ2dsZS1lZGl0b3IuY29tcG9uZW50LnRzKTwvaDQ+XG4gICAgICAgICAgICA8cHJlIGNsYXNzPVwiYmctZ3JheS0xMDAgcC00IHJvdW5kZWQtbGcgb3ZlcmZsb3cteC1hdXRvIHRleHQtc21cIj48Y29kZT5pbXBvcnQgJiMxMjM7IENvbXBvbmVudCAmIzEyNTsgZnJvbSAnJiM2NDthbmd1bGFyL2NvcmUnO1xuaW1wb3J0ICYjMTIzOyBMaWJzVWlDb21wb25lbnRzSW1hZ2VFZGl0b3JDb21wb25lbnQgJiMxMjU7IGZyb20gJyYjNjQ7bGlicy11aS9jb21wb25lbnRzLWltYWdlLWVkaXRvcic7XG5pbXBvcnQgJiMxMjM7IElTYXZlRmlsZSAmIzEyNTsgZnJvbSAnJiM2NDtsaWJzLXVpL2NvbXBvbmVudHMtaW1hZ2UtZWRpdG9yJztcblxuJiM2NDtDb21wb25lbnQoJiMxMjM7XG4gIHNlbGVjdG9yOiAnYXBwLXRvZ2dsZS1lZGl0b3InLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbTGlic1VpQ29tcG9uZW50c0ltYWdlRWRpdG9yQ29tcG9uZW50XSxcbiAgdGVtcGxhdGVVcmw6ICcuL3RvZ2dsZS1lZGl0b3IuY29tcG9uZW50Lmh0bWwnXG4mIzEyNTspXG5leHBvcnQgY2xhc3MgVG9nZ2xlRWRpdG9yQ29tcG9uZW50ICYjMTIzO1xuICBpbWFnZVNvdXJjZSA9ICdodHRwczovL2V4YW1wbGUuY29tL3BhdGgvdG8vaW1hZ2UuanBnJztcbiAgc2hvd0VkaXRvciA9IGZhbHNlO1xuICBlZGl0ZWRJbWFnZVVybCA9ICcnO1xuXG4gIG9wZW5FZGl0b3IoKSAmIzEyMztcbiAgICB0aGlzLnNob3dFZGl0b3IgPSB0cnVlO1xuICAmIzEyNTtcblxuICBvblNhdmVGaWxlKGRhdGE6IElTYXZlRmlsZSkgJiMxMjM7XG4gICAgdGhpcy5lZGl0ZWRJbWFnZVVybCA9IGRhdGEudXJsO1xuICAgIHRoaXMuc2hvd0VkaXRvciA9IGZhbHNlO1xuICAgIGNvbnNvbGUubG9nKCfEkMOjIGzGsHUgZmlsZTonLCBkYXRhLmZpbGUpO1xuICAmIzEyNTtcblxuICBvbkNsb3NlRWRpdG9yKGRhdGE6ICYjMTIzO2lzQ2xpY2tCdXR0b25DbG9zZTogYm9vbGVhbiYjMTI1OykgJiMxMjM7XG4gICAgdGhpcy5zaG93RWRpdG9yID0gZmFsc2U7XG4gICYjMTI1O1xuJiMxMjU7PC9jb2RlPjwvcHJlPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8ZGl2IGNsYXNzPVwibWItOFwiPlxuICAgICAgICA8aDMgY2xhc3M9XCJ0ZXh0LXhsIGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMCBtYi00XCI+Q8OhY2ggMzogVuG7m2kgdOG7tyBs4buHIGtodW5nIGjDrG5oIGPhu5EgxJHhu4tuaDwvaDM+XG5cbiAgICAgICAgPGRpdiBjbGFzcz1cImdyaWQgZ3JpZC1jb2xzLTEgbWQ6Z3JpZC1jb2xzLTIgZ2FwLTYgbWItNFwiPlxuICAgICAgICAgIDxkaXY+XG4gICAgICAgICAgICA8aDQgY2xhc3M9XCJmb250LXNlbWlib2xkIG1iLTIgdGV4dC1ncmF5LTcwMFwiPkhUTUwgKGZpeGVkLXJhdGlvLmNvbXBvbmVudC5odG1sKTwvaDQ+XG4gICAgICAgICAgICA8cHJlIGNsYXNzPVwiYmctZ3JheS0xMDAgcC00IHJvdW5kZWQtbGcgb3ZlcmZsb3cteC1hdXRvIHRleHQtc21cIj48Y29kZT4mbHQ7bGlic191aS1jb21wb25lbnRzLWltYWdlX2VkaXRvclxuICBbKGltZ1NyYyldPVwiaW1hZ2VTb3VyY2VcIlxuICBbYXNwZWN0UmF0aW9dPVwiYXNwZWN0UmF0aW9cIlxuICBbcmVxdWlyZWRDcm9wRm9sbG93UmF0aW9dPVwidHJ1ZVwiXG4gIChvdXRTYXZlRmlsZSk9XCJvblNhdmVGaWxlKCRldmVudClcIiZndDtcbiZsdDsvbGlic191aS1jb21wb25lbnRzLWltYWdlX2VkaXRvciZndDs8L2NvZGU+PC9wcmU+XG4gICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICA8ZGl2PlxuICAgICAgICAgICAgPGg0IGNsYXNzPVwiZm9udC1zZW1pYm9sZCBtYi0yIHRleHQtZ3JheS03MDBcIj5UeXBlU2NyaXB0IChmaXhlZC1yYXRpby5jb21wb25lbnQudHMpPC9oND5cbiAgICAgICAgICAgIDxwcmUgY2xhc3M9XCJiZy1ncmF5LTEwMCBwLTQgcm91bmRlZC1sZyBvdmVyZmxvdy14LWF1dG8gdGV4dC1zbVwiPjxjb2RlPmltcG9ydCAmIzEyMzsgQ29tcG9uZW50ICYjMTI1OyBmcm9tICcmIzY0O2FuZ3VsYXIvY29yZSc7XG5pbXBvcnQgJiMxMjM7IExpYnNVaUNvbXBvbmVudHNJbWFnZUVkaXRvckNvbXBvbmVudCAmIzEyNTsgZnJvbSAnJiM2NDtsaWJzLXVpL2NvbXBvbmVudHMtaW1hZ2UtZWRpdG9yJztcbmltcG9ydCAmIzEyMzsgSUFzcGVjdFJhdGlvICYjMTI1OyBmcm9tICcmIzY0O2xpYnMtdWkvaW50ZXJmYWNlcy10eXBlcyc7XG5cbiYjNjQ7Q29tcG9uZW50KCYjMTIzO1xuICBzZWxlY3RvcjogJ2FwcC1maXhlZC1yYXRpbycsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtMaWJzVWlDb21wb25lbnRzSW1hZ2VFZGl0b3JDb21wb25lbnRdLFxuICB0ZW1wbGF0ZVVybDogJy4vZml4ZWQtcmF0aW8uY29tcG9uZW50Lmh0bWwnXG4mIzEyNTspXG5leHBvcnQgY2xhc3MgRml4ZWRSYXRpb0NvbXBvbmVudCAmIzEyMztcbiAgaW1hZ2VTb3VyY2UgPSAnaHR0cHM6Ly9leGFtcGxlLmNvbS9wYXRoL3RvL2ltYWdlLmpwZyc7XG4gIGFzcGVjdFJhdGlvOiBJQXNwZWN0UmF0aW8gPSAmIzEyMztcbiAgICBrZXk6ICcxOjEnLFxuICAgIHZhbHVlOiAxXG4gICYjMTI1OztcblxuICBvblNhdmVGaWxlKGRhdGE6IGFueSkgJiMxMjM7XG4gICAgY29uc29sZS5sb2coJ8SQw6MgbMawdSBow6xuaCDhuqNuaCB24bubaSB04bu3IGzhu4cgMToxJyk7XG4gICYjMTI1O1xuJiMxMjU7PC9jb2RlPjwvcHJlPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvc2VjdGlvbj5cblxuICAgIDxzZWN0aW9uIGNsYXNzPVwiYmctd2hpdGUgcm91bmRlZC1sZyBwLTggbWItOCBzaGFkb3ctc21cIj5cbiAgICAgIDxoMiBjbGFzcz1cInRleHQtMnhsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTUgcGItMyBib3JkZXItYiBib3JkZXItZ3JheS0yMDBcIj5Uw6BpIGxp4buHdSBBUEk8L2gyPlxuXG4gICAgICA8aDMgY2xhc3M9XCJ0ZXh0LXhsIGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMCBtYi00XCI+SW5wdXRzPC9oMz5cbiAgICAgIDxkaXYgY2xhc3M9XCJvdmVyZmxvdy14LWF1dG8gbWItOFwiPlxuICAgICAgICA8dGFibGUgY2xhc3M9XCJtaW4tdy1mdWxsIGJnLXdoaXRlIGJvcmRlciBib3JkZXItZ3JheS0yMDBcIj5cbiAgICAgICAgICA8dGhlYWQ+XG4gICAgICAgICAgICA8dHI+XG4gICAgICAgICAgICAgIDx0aCBjbGFzcz1cInB5LTMgcHgtNCBib3JkZXItYiBib3JkZXItZ3JheS0yMDAgYmctZ3JheS0xMDAgdGV4dC1sZWZ0IGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMFwiPlTDqm48L3RoPlxuICAgICAgICAgICAgICA8dGggY2xhc3M9XCJweS0zIHB4LTQgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwIGJnLWdyYXktMTAwIHRleHQtbGVmdCBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDBcIj5LaeG7g3UgZOG7r1xuICAgICAgICAgICAgICAgIGxp4buHdVxuICAgICAgICAgICAgICA8L3RoPlxuICAgICAgICAgICAgICA8dGggY2xhc3M9XCJweS0zIHB4LTQgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwIGJnLWdyYXktMTAwIHRleHQtbGVmdCBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDBcIj5N4bq3YyDEkeG7i25oXG4gICAgICAgICAgICAgIDwvdGg+XG4gICAgICAgICAgICAgIDx0aCBjbGFzcz1cInB5LTMgcHgtNCBib3JkZXItYiBib3JkZXItZ3JheS0yMDAgYmctZ3JheS0xMDAgdGV4dC1sZWZ0IGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMFwiPk3DtCB04bqjXG4gICAgICAgICAgICAgIDwvdGg+XG4gICAgICAgICAgICA8L3RyPlxuICAgICAgICAgIDwvdGhlYWQ+XG4gICAgICAgICAgPHRib2R5PlxuICAgICAgICAgICAgQGZvciAoaW5wdXQgb2YgaW5wdXRzRG9jOyB0cmFjayBpbnB1dC5uYW1lKSB7XG4gICAgICAgICAgICAgIDx0cj5cbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJweS0yIHB4LTQgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwXCI+PGNvZGVcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJ0ZXh0LXNtIGJnLWdyYXktMTAwIHB4LTEuNSBweS0wLjUgcm91bmRlZFwiPnt7IGlucHV0Lm5hbWUgfX08L2NvZGU+PC90ZD5cbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJweS0yIHB4LTQgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwXCI+PGNvZGVcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJ0ZXh0LXNtIGJnLWdyYXktMTAwIHB4LTEuNSBweS0wLjUgcm91bmRlZFwiPnt7IGlucHV0LnR5cGUgfX08L2NvZGU+PC90ZD5cbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJweS0yIHB4LTQgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwXCI+e3sgaW5wdXQuZGVmYXVsdCB9fTwvdGQ+XG4gICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwicHktMiBweC00IGJvcmRlci1iIGJvcmRlci1ncmF5LTIwMFwiPnt7IGlucHV0LmRlc2NyaXB0aW9uIH19PC90ZD5cbiAgICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICA8L3Rib2R5PlxuICAgICAgICA8L3RhYmxlPlxuICAgICAgPC9kaXY+XG5cbiAgICAgIDxoMyBjbGFzcz1cInRleHQteGwgZm9udC1zZW1pYm9sZCB0ZXh0LWdyYXktNzAwIG1iLTRcIj5PdXRwdXRzPC9oMz5cbiAgICAgIDxkaXYgY2xhc3M9XCJvdmVyZmxvdy14LWF1dG8gbWItOFwiPlxuICAgICAgICA8dGFibGUgY2xhc3M9XCJtaW4tdy1mdWxsIGJnLXdoaXRlIGJvcmRlciBib3JkZXItZ3JheS0yMDBcIj5cbiAgICAgICAgICA8dGhlYWQ+XG4gICAgICAgICAgICA8dHI+XG4gICAgICAgICAgICAgIDx0aCBjbGFzcz1cInB5LTMgcHgtNCBib3JkZXItYiBib3JkZXItZ3JheS0yMDAgYmctZ3JheS0xMDAgdGV4dC1sZWZ0IGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMFwiPlTDqm48L3RoPlxuICAgICAgICAgICAgICA8dGggY2xhc3M9XCJweS0zIHB4LTQgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwIGJnLWdyYXktMTAwIHRleHQtbGVmdCBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDBcIj5LaeG7g3UgZOG7r1xuICAgICAgICAgICAgICAgIGxp4buHdVxuICAgICAgICAgICAgICA8L3RoPlxuICAgICAgICAgICAgICA8dGggY2xhc3M9XCJweS0zIHB4LTQgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwIGJnLWdyYXktMTAwIHRleHQtbGVmdCBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDBcIj5Nw7QgdOG6o1xuICAgICAgICAgICAgICA8L3RoPlxuICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICA8L3RoZWFkPlxuICAgICAgICAgIDx0Ym9keT5cbiAgICAgICAgICAgIEBmb3IgKG91dHB1dCBvZiBvdXRwdXRzRG9jOyB0cmFjayBvdXRwdXQubmFtZSkge1xuICAgICAgICAgICAgICA8dHI+XG4gICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwicHktMiBweC00IGJvcmRlci1iIGJvcmRlci1ncmF5LTIwMFwiPjxjb2RlXG4gICAgICAgICAgICAgICAgICAgIGNsYXNzPVwidGV4dC1zbSBiZy1ncmF5LTEwMCBweC0xLjUgcHktMC41IHJvdW5kZWRcIj57eyBvdXRwdXQubmFtZSB9fTwvY29kZT48L3RkPlxuICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInB5LTIgcHgtNCBib3JkZXItYiBib3JkZXItZ3JheS0yMDBcIj48Y29kZVxuICAgICAgICAgICAgICAgICAgICBjbGFzcz1cInRleHQtc20gYmctZ3JheS0xMDAgcHgtMS41IHB5LTAuNSByb3VuZGVkXCI+e3sgb3V0cHV0LnR5cGUgfX08L2NvZGU+PC90ZD5cbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJweS0yIHB4LTQgYm9yZGVyLWIgYm9yZGVyLWdyYXktMjAwXCI+e3sgb3V0cHV0LmRlc2NyaXB0aW9uIH19PC90ZD5cbiAgICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICA8L3Rib2R5PlxuICAgICAgICA8L3RhYmxlPlxuICAgICAgPC9kaXY+XG5cbiAgICAgIDxoMyBjbGFzcz1cInRleHQteGwgZm9udC1zZW1pYm9sZCB0ZXh0LWdyYXktNzAwIG1iLTRcIj5JbnRlcmZhY2VzPC9oMz5cbiAgICAgIDxkaXYgY2xhc3M9XCJzcGFjZS15LTZcIj5cbiAgICAgICAgQGZvciAoaW50ZXJmYWNlSXRlbSBvZiBpbnRlcmZhY2VzRG9jOyB0cmFjayBpbnRlcmZhY2VJdGVtLm5hbWUpIHtcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiYmctZ3JheS01MCBwLTYgcm91bmRlZC1sZ1wiPlxuICAgICAgICAgICAgPGg0IGNsYXNzPVwidGV4dC1sZyBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDAgbWItM1wiPnt7IGludGVyZmFjZUl0ZW0ubmFtZSB9fTwvaDQ+XG4gICAgICAgICAgICA8cHJlXG4gICAgICAgICAgICAgIGNsYXNzPVwiYmctZ3JheS0xMDAgcC00IHJvdW5kZWQtbGcgb3ZlcmZsb3cteC1hdXRvIHRleHQtc20gbWItM1wiPjxjb2RlPnt7IGludGVyZmFjZUl0ZW0uY29kZSB9fTwvY29kZT48L3ByZT5cbiAgICAgICAgICAgIDxwIGNsYXNzPVwidGV4dC1ncmF5LTYwMFwiPnt7IGludGVyZmFjZUl0ZW0uZGVzY3JpcHRpb24gfX08L3A+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIDwvc2VjdGlvbj5cblxuICAgIDxzZWN0aW9uIGNsYXNzPVwiYmctd2hpdGUgcm91bmRlZC1sZyBwLTggbWItOCBzaGFkb3ctc21cIj5cbiAgICAgIDxoMiBjbGFzcz1cInRleHQtMnhsIGZvbnQtYm9sZCB0ZXh0LWdyYXktODAwIG1iLTUgcGItMyBib3JkZXItYiBib3JkZXItZ3JheS0yMDBcIj5Uw61uaCBuxINuZzwvaDI+XG4gICAgICA8dWwgY2xhc3M9XCJzcGFjZS15LTZcIj5cbiAgICAgICAgQGZvciAoZmVhdHVyZSBvZiBmZWF0dXJlczsgdHJhY2sgZmVhdHVyZS5pZCkge1xuICAgICAgICAgIDxsaSBjbGFzcz1cImZsZXggaXRlbXMtc3RhcnRcIj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC0yeGwgdGV4dC1ibHVlLTUwMCBtci00XCI+e3sgZmVhdHVyZS5pY29uIH19PC9zcGFuPlxuICAgICAgICAgICAgPGRpdj5cbiAgICAgICAgICAgICAgPGgzIGNsYXNzPVwidGV4dC1sZyBmb250LXNlbWlib2xkIHRleHQtZ3JheS03MDAgbWItMVwiPnt7IGZlYXR1cmUudGl0bGUgfX08L2gzPlxuICAgICAgICAgICAgICA8cCBjbGFzcz1cInRleHQtZ3JheS02MDBcIj57eyBmZWF0dXJlLmRlc2NyaXB0aW9uIH19PC9wPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPC9saT5cbiAgICAgICAgfVxuICAgICAgPC91bD5cbiAgICA8L3NlY3Rpb24+XG4gIDwvbWFpbj5cbjwvZGl2PlxuIl19