@libs-ui/components-gallery 0.2.356-9 → 0.2.357-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +234 -114
- package/esm2022/gallery.component.mjs +3 -3
- package/esm2022/viewer/viewer.component.mjs +8 -9
- package/fesm2022/libs-ui-components-gallery.mjs +10 -11
- package/fesm2022/libs-ui-components-gallery.mjs.map +1 -1
- package/gallery.component.d.ts +4 -4
- package/package.json +6 -7
- package/viewer/viewer.component.d.ts +11 -11
package/README.md
CHANGED
|
@@ -1,220 +1,340 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @libs-ui/components-gallery
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Component hiển thị gallery ảnh với lightbox viewer, hỗ trợ zoom, drag, fullscreen và keyboard navigation.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Giới thiệu
|
|
6
|
+
|
|
7
|
+
`@libs-ui/components-gallery` cung cấp component gallery ảnh dạng thumbnail grid, khi click vào thumbnail sẽ mở lightbox viewer fullscreen. Viewer hỗ trợ zoom in/out, kéo thả ảnh đã phóng to, chuyển ảnh bằng phím mũi tên, và chế độ fullscreen. Component cũng hỗ trợ nhận data dạng `Array<string>` và tự động convert sang định dạng nội bộ.
|
|
6
8
|
|
|
7
9
|
## Tính năng
|
|
8
10
|
|
|
9
|
-
- ✅ **Thumbnail Grid**: Hiển thị danh sách ảnh dạng thumbnail
|
|
10
|
-
- ✅ **Lightbox Viewer**: Viewer fullscreen
|
|
11
|
-
- ✅ **
|
|
12
|
-
- ✅ **
|
|
13
|
-
- ✅ **Keyboard Navigation**: Escape
|
|
14
|
-
- ✅ **
|
|
15
|
-
- ✅ **
|
|
16
|
-
- ✅ **FunctionsControl**:
|
|
17
|
-
- ✅ **
|
|
11
|
+
- ✅ **Thumbnail Grid**: Hiển thị danh sách ảnh dạng thumbnail, hỗ trợ cắt slice (`start`/`end`)
|
|
12
|
+
- ✅ **Lightbox Viewer**: Viewer fullscreen được inject động vào `document.body`
|
|
13
|
+
- ✅ **Zoom & Drag**: Zoom in/out và kéo thả ảnh khi đã phóng to vượt khung
|
|
14
|
+
- ✅ **Fullscreen Mode**: Mở rộng viewer chiếm toàn màn hình
|
|
15
|
+
- ✅ **Keyboard Navigation**: `Escape` đóng viewer, `ArrowLeft`/`ArrowRight` chuyển ảnh
|
|
16
|
+
- ✅ **String Array Support**: Tự động convert `Array<string>` sang object format qua `imageArrayStringConvert`
|
|
17
|
+
- ✅ **Overlay Count**: Hiển thị overlay "+N" trên thumbnail cuối khi ảnh nhiều hơn `end`
|
|
18
|
+
- ✅ **FunctionsControl**: Mở viewer từ bên ngoài component qua `outFunctionsControl`
|
|
19
|
+
- ✅ **Error Fallback**: Hiển thị icon placeholder khi ảnh load lỗi
|
|
20
|
+
- ✅ **Embed Frame Support**: Gửi postMessage tới parent khi chạy trong iframe
|
|
18
21
|
|
|
19
22
|
## Khi nào sử dụng
|
|
20
23
|
|
|
21
|
-
- Khi cần hiển thị danh sách ảnh dạng thumbnail grid
|
|
22
|
-
- Khi cần lightbox viewer với zoom, drag và fullscreen
|
|
23
|
-
- Khi
|
|
24
|
-
- Khi cần điều khiển mở viewer từ bên ngoài (
|
|
25
|
-
- Khi cần hiển thị
|
|
24
|
+
- Khi cần hiển thị danh sách ảnh dạng thumbnail grid trong card, form, hoặc detail page
|
|
25
|
+
- Khi cần lightbox viewer với zoom, drag và fullscreen cho ảnh tải lên từ người dùng
|
|
26
|
+
- Khi data ảnh là `Array<string>` (mảng URL thuần) thay vì array object
|
|
27
|
+
- Khi cần điều khiển mở viewer từ bên ngoài component (ví dụ: click nút "Xem ảnh")
|
|
28
|
+
- Khi cần giới hạn số thumbnail hiển thị và cho biết có bao nhiêu ảnh còn lại (+N)
|
|
26
29
|
|
|
27
|
-
##
|
|
30
|
+
## Cài đặt
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
```bash
|
|
33
|
+
npm install @libs-ui/components-gallery
|
|
34
|
+
```
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
## Import
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
```typescript
|
|
39
|
+
import {
|
|
40
|
+
LibsUiComponentsGalleryComponent,
|
|
41
|
+
LibsUiComponentsGalleryViewerComponent,
|
|
42
|
+
IGalleryFunctionsControlEvent,
|
|
43
|
+
IZoomDragHTMLElement,
|
|
44
|
+
} from '@libs-ui/components-gallery';
|
|
45
|
+
```
|
|
34
46
|
|
|
35
|
-
|
|
47
|
+
## Ví dụ sử dụng
|
|
36
48
|
|
|
37
|
-
|
|
49
|
+
### 1. Gallery cơ bản
|
|
38
50
|
|
|
39
|
-
|
|
51
|
+
```typescript
|
|
52
|
+
import { Component } from '@angular/core';
|
|
53
|
+
import { LibsUiComponentsGalleryComponent } from '@libs-ui/components-gallery';
|
|
40
54
|
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
@Component({
|
|
56
|
+
selector: 'app-product-images',
|
|
57
|
+
standalone: true,
|
|
58
|
+
imports: [LibsUiComponentsGalleryComponent],
|
|
59
|
+
template: `
|
|
60
|
+
<div class="w-[300px] h-[80px]">
|
|
61
|
+
<libs_ui-components-gallery
|
|
62
|
+
[images]="productImages"
|
|
63
|
+
[fieldDisplaySrcImage]="'url'"
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
`,
|
|
67
|
+
})
|
|
68
|
+
export class ProductImagesComponent {
|
|
69
|
+
productImages = [
|
|
70
|
+
{ url: 'https://example.com/product1.jpg' },
|
|
71
|
+
{ url: 'https://example.com/product2.jpg' },
|
|
72
|
+
{ url: 'https://example.com/product3.jpg' },
|
|
73
|
+
{ url: 'https://example.com/product4.jpg' },
|
|
74
|
+
{ url: 'https://example.com/product5.jpg' },
|
|
75
|
+
];
|
|
76
|
+
}
|
|
43
77
|
```
|
|
44
78
|
|
|
45
|
-
|
|
79
|
+
### 2. Gallery từ Array\<string\>
|
|
46
80
|
|
|
47
|
-
|
|
81
|
+
Khi data trả về từ API là mảng URL thuần, dùng `imageArrayStringConvert` để convert tự động:
|
|
48
82
|
|
|
49
83
|
```typescript
|
|
50
84
|
import { Component } from '@angular/core';
|
|
51
85
|
import { LibsUiComponentsGalleryComponent } from '@libs-ui/components-gallery';
|
|
52
86
|
|
|
53
87
|
@Component({
|
|
54
|
-
selector: 'app-
|
|
88
|
+
selector: 'app-attachment-gallery',
|
|
55
89
|
standalone: true,
|
|
56
90
|
imports: [LibsUiComponentsGalleryComponent],
|
|
57
91
|
template: `
|
|
58
92
|
<div class="w-[300px] h-[80px]">
|
|
59
93
|
<libs_ui-components-gallery
|
|
60
|
-
[images]="
|
|
61
|
-
[fieldDisplaySrcImage]="'
|
|
94
|
+
[images]="[]"
|
|
95
|
+
[fieldDisplaySrcImage]="'src'"
|
|
96
|
+
[imageArrayStringConvert]="attachmentUrls"
|
|
62
97
|
/>
|
|
63
98
|
</div>
|
|
64
99
|
`,
|
|
65
100
|
})
|
|
66
|
-
export class
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
101
|
+
export class AttachmentGalleryComponent {
|
|
102
|
+
attachmentUrls = [
|
|
103
|
+
'https://example.com/attach1.jpg',
|
|
104
|
+
'https://example.com/attach2.jpg',
|
|
105
|
+
'https://example.com/attach3.jpg',
|
|
71
106
|
];
|
|
72
107
|
}
|
|
73
108
|
```
|
|
74
109
|
|
|
75
|
-
###
|
|
110
|
+
### 3. Gallery với custom range và ẩn overlay count
|
|
76
111
|
|
|
77
112
|
```typescript
|
|
113
|
+
import { Component } from '@angular/core';
|
|
114
|
+
import { LibsUiComponentsGalleryComponent } from '@libs-ui/components-gallery';
|
|
115
|
+
|
|
78
116
|
@Component({
|
|
79
|
-
|
|
117
|
+
selector: 'app-preview-gallery',
|
|
118
|
+
standalone: true,
|
|
119
|
+
imports: [LibsUiComponentsGalleryComponent],
|
|
120
|
+
template: `
|
|
121
|
+
<div class="w-[400px] h-[80px]">
|
|
122
|
+
<libs_ui-components-gallery
|
|
123
|
+
[images]="photos"
|
|
124
|
+
[fieldDisplaySrcImage]="'photoUrl'"
|
|
125
|
+
[start]="0"
|
|
126
|
+
[end]="4"
|
|
127
|
+
[ignoreOverlayCountImage]="true"
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
`,
|
|
131
|
+
})
|
|
132
|
+
export class PreviewGalleryComponent {
|
|
133
|
+
photos = [
|
|
134
|
+
{ photoUrl: 'https://example.com/photo1.jpg', name: 'Photo 1' },
|
|
135
|
+
{ photoUrl: 'https://example.com/photo2.jpg', name: 'Photo 2' },
|
|
136
|
+
{ photoUrl: 'https://example.com/photo3.jpg', name: 'Photo 3' },
|
|
137
|
+
{ photoUrl: 'https://example.com/photo4.jpg', name: 'Photo 4' },
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 4. Mở viewer từ bên ngoài (FunctionsControl)
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { Component, signal } from '@angular/core';
|
|
146
|
+
import {
|
|
147
|
+
LibsUiComponentsGalleryComponent,
|
|
148
|
+
IGalleryFunctionsControlEvent,
|
|
149
|
+
} from '@libs-ui/components-gallery';
|
|
150
|
+
|
|
151
|
+
@Component({
|
|
152
|
+
selector: 'app-order-detail',
|
|
153
|
+
standalone: true,
|
|
154
|
+
imports: [LibsUiComponentsGalleryComponent],
|
|
80
155
|
template: `
|
|
81
156
|
<div class="w-[300px] h-[80px]">
|
|
82
157
|
<libs_ui-components-gallery
|
|
83
|
-
[images]="
|
|
158
|
+
[images]="orderImages"
|
|
84
159
|
[fieldDisplaySrcImage]="'url'"
|
|
85
|
-
|
|
160
|
+
(outFunctionsControl)="handlerFunctionsControl($event)"
|
|
161
|
+
(outViewerEvent)="handlerViewerEvent($event)"
|
|
86
162
|
/>
|
|
87
163
|
</div>
|
|
164
|
+
|
|
165
|
+
<button
|
|
166
|
+
class="mt-4 px-4 py-2 bg-blue-500 text-white rounded"
|
|
167
|
+
(click)="handlerOpenViewer($event)">
|
|
168
|
+
Xem ảnh đầu tiên
|
|
169
|
+
</button>
|
|
170
|
+
|
|
171
|
+
<span class="ml-2 text-sm text-gray-500">
|
|
172
|
+
Viewer: {{ viewerStatus() ?? 'đóng' }}
|
|
173
|
+
</span>
|
|
88
174
|
`,
|
|
89
175
|
})
|
|
90
|
-
export class
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
176
|
+
export class OrderDetailComponent {
|
|
177
|
+
protected galleryControls = signal<IGalleryFunctionsControlEvent | null>(null);
|
|
178
|
+
protected viewerStatus = signal<'show' | 'remove' | null>(null);
|
|
179
|
+
|
|
180
|
+
orderImages = [
|
|
181
|
+
{ url: 'https://example.com/order1.jpg' },
|
|
182
|
+
{ url: 'https://example.com/order2.jpg' },
|
|
183
|
+
{ url: 'https://example.com/order3.jpg' },
|
|
94
184
|
];
|
|
185
|
+
|
|
186
|
+
protected handlerFunctionsControl(event: IGalleryFunctionsControlEvent): void {
|
|
187
|
+
event.stopPropagation?.();
|
|
188
|
+
this.galleryControls.set(event);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
protected handlerViewerEvent(event: 'show' | 'remove'): void {
|
|
192
|
+
this.viewerStatus.set(event);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
protected handlerOpenViewer(event: Event): void {
|
|
196
|
+
event.stopPropagation();
|
|
197
|
+
this.galleryControls()?.open(this.orderImages[0]);
|
|
198
|
+
}
|
|
95
199
|
}
|
|
96
200
|
```
|
|
97
201
|
|
|
98
|
-
###
|
|
202
|
+
### 5. Single image (1 ảnh, viewer không có thumbnail strip)
|
|
99
203
|
|
|
100
204
|
```typescript
|
|
101
|
-
import { Component
|
|
102
|
-
import { LibsUiComponentsGalleryComponent
|
|
205
|
+
import { Component } from '@angular/core';
|
|
206
|
+
import { LibsUiComponentsGalleryComponent } from '@libs-ui/components-gallery';
|
|
103
207
|
|
|
104
208
|
@Component({
|
|
105
|
-
selector: 'app-
|
|
209
|
+
selector: 'app-avatar-preview',
|
|
106
210
|
standalone: true,
|
|
107
211
|
imports: [LibsUiComponentsGalleryComponent],
|
|
108
212
|
template: `
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
213
|
+
<div class="w-[100px] h-[80px]">
|
|
214
|
+
<libs_ui-components-gallery
|
|
215
|
+
[images]="[]"
|
|
216
|
+
[fieldDisplaySrcImage]="'url'"
|
|
217
|
+
[imageArrayStringConvert]="avatarUrl"
|
|
218
|
+
/>
|
|
219
|
+
</div>
|
|
115
220
|
`,
|
|
116
221
|
})
|
|
117
|
-
export class
|
|
118
|
-
|
|
119
|
-
images = [{ url: 'https://example.com/image1.jpg' }];
|
|
222
|
+
export class AvatarPreviewComponent {
|
|
223
|
+
avatarUrl = ['https://example.com/avatar.jpg'];
|
|
120
224
|
}
|
|
121
225
|
```
|
|
122
226
|
|
|
123
|
-
##
|
|
227
|
+
## @Input() — libs\_ui-components-gallery
|
|
124
228
|
|
|
125
|
-
|
|
229
|
+
| Input | Type | Default | Mô tả | Ví dụ |
|
|
230
|
+
|---|---|---|---|---|
|
|
231
|
+
| `[(images)]` | `Array<Record<string, any>>` | `required` | Danh sách ảnh (model — two-way binding). Component có thể ghi lại khi dùng `imageArrayStringConvert` | `[images]="photoList"` |
|
|
232
|
+
| `[end]` | `number` | `3` | Index kết thúc khi slice mảng ảnh để hiển thị thumbnails | `[end]="5"` |
|
|
233
|
+
| `[fieldDisplaySrcImage]` | `string` | `'url'` | Tên key trong object chứa URL ảnh | `[fieldDisplaySrcImage]="'photoUrl'"` |
|
|
234
|
+
| `[ignoreOverlayCountImage]` | `boolean` | `undefined` | Ẩn overlay đếm số ảnh còn lại (+N) trên thumbnail cuối | `[ignoreOverlayCountImage]="true"` |
|
|
235
|
+
| `[imageArrayStringConvert]` | `Array<string>` | `undefined` | Mảng URL dạng string — tự động convert sang `images` format theo `fieldDisplaySrcImage` | `[imageArrayStringConvert]="urlList"` |
|
|
236
|
+
| `[start]` | `number` | `0` | Index bắt đầu khi slice mảng ảnh để hiển thị thumbnails | `[start]="0"` |
|
|
237
|
+
| `[zIndex]` | `number` | `1200` | z-index của viewer overlay khi mở | `[zIndex]="1500"` |
|
|
126
238
|
|
|
127
|
-
|
|
239
|
+
## @Output() — libs\_ui-components-gallery
|
|
128
240
|
|
|
129
|
-
|
|
|
130
|
-
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `[ignoreOverlayCountImage]` | `boolean` | `false` | Ẩn overlay đếm số ảnh còn lại (+N) |
|
|
134
|
-
| `[imageArrayStringConvert]` | `Array<string>` | `undefined` | Array URL strings — tự động convert sang images format |
|
|
135
|
-
| `[(images)]` | `Array<Record<string, any>>` | `required` | Danh sách ảnh (model — two-way binding) |
|
|
136
|
-
| `[start]` | `number` | `0` | Index bắt đầu để hiển thị thumbnails (slice start) |
|
|
137
|
-
| `[zIndex]` | `number` | `1200` | z-index của viewer overlay |
|
|
241
|
+
| Output | Type | Mô tả | Handler TS | Binding HTML |
|
|
242
|
+
|---|---|---|---|---|
|
|
243
|
+
| `(outFunctionsControl)` | `IGalleryFunctionsControlEvent` | Emit ngay khi init và mỗi lần viewer mở/đóng — cung cấp method `open()` để điều khiển viewer từ bên ngoài | `handlerFunctionsControl(event: IGalleryFunctionsControlEvent): void { event.stopPropagation?.(); this.controls.set(event); }` | `(outFunctionsControl)="handlerFunctionsControl($event)"` |
|
|
244
|
+
| `(outViewerEvent)` | `'show' \| 'remove'` | Emit `'show'` khi viewer mở, `'remove'` khi viewer đóng | `handlerViewerEvent(event: 'show' \| 'remove'): void { this.viewerStatus.set(event); }` | `(outViewerEvent)="handlerViewerEvent($event)"` |
|
|
138
245
|
|
|
139
|
-
|
|
246
|
+
## Sub-component: Viewer
|
|
140
247
|
|
|
141
|
-
|
|
142
|
-
| ----------------------- | ------------------------------- | ---------------------------------------------------- |
|
|
143
|
-
| `(outFunctionsControl)` | `IGalleryFunctionsControlEvent` | Emit functions để điều khiển gallery từ bên ngoài |
|
|
144
|
-
| `(outViewerEvent)` | `'show' \| 'remove'` | Event khi viewer được mở hoặc đóng |
|
|
248
|
+
`libs_ui-components-gallery-viewer` là viewer fullscreen được tạo động và inject vào `document.body`. Thông thường không cần dùng trực tiếp — gallery component quản lý tự động.
|
|
145
249
|
|
|
146
|
-
|
|
250
|
+
### @Input() — libs\_ui-components-gallery-viewer
|
|
147
251
|
|
|
148
|
-
|
|
|
149
|
-
|
|
150
|
-
| `
|
|
151
|
-
| `
|
|
252
|
+
| Input | Type | Default | Mô tả | Ví dụ |
|
|
253
|
+
|---|---|---|---|---|
|
|
254
|
+
| `[classContainerInclude]` | `string` | `undefined` | CSS class bổ sung cho container viewer | `[classContainerInclude]="'custom-viewer'"` |
|
|
255
|
+
| `[fieldDisplaySrcImage]` | `string` | `required` | Key trong object chứa URL ảnh | `[fieldDisplaySrcImage]="'url'"` |
|
|
256
|
+
| `[(imageSelected)]` | `Record<string, any>` | `undefined` | Ảnh đang được chọn hiển thị (model — two-way binding) | `[(imageSelected)]="currentImage"` |
|
|
257
|
+
| `[images]` | `Array<Record<string, any>>` | `required` | Danh sách tất cả ảnh để hiển thị trong thumbnail strip | `[images]="allImages"` |
|
|
258
|
+
| `[removeBackdrop]` | `boolean` | `undefined` | Ẩn backdrop overlay phía sau viewer | `[removeBackdrop]="true"` |
|
|
259
|
+
| `[singleImage]` | `boolean` | `false` | Chế độ 1 ảnh — ẩn thumbnail strip, hiển thị ảnh đầu tiên trực tiếp | `[singleImage]="true"` |
|
|
260
|
+
| `[zIndex]` | `number` | `1200` | z-index của viewer | `[zIndex]="1500"` |
|
|
152
261
|
|
|
153
|
-
###
|
|
262
|
+
### @Output() — libs\_ui-components-gallery-viewer
|
|
154
263
|
|
|
155
|
-
|
|
264
|
+
| Output | Type | Mô tả | Handler TS | Binding HTML |
|
|
265
|
+
|---|---|---|---|---|
|
|
266
|
+
| `(outClose)` | `void` | Emit khi người dùng đóng viewer (click nút đóng, nhấn Escape) | `handlerCloseViewer(event: Event): void { event.stopPropagation(); this.viewerRef?.destroy(); }` | `(outClose)="handlerCloseViewer($event)"` |
|
|
156
267
|
|
|
157
|
-
|
|
268
|
+
## FunctionsControl API
|
|
158
269
|
|
|
159
|
-
|
|
160
|
-
| ------------------------ | ----------------------------- | ----------- | ------------------------------------ |
|
|
161
|
-
| `[classContainerInclude]`| `string` | `undefined` | CSS class thêm vào container |
|
|
162
|
-
| `[fieldDisplaySrcImage]` | `string` | `required` | Key trong object chứa URL ảnh |
|
|
163
|
-
| `[(imageSelected)]` | `Record<string, any>` | `undefined` | Ảnh đang được chọn (model) |
|
|
164
|
-
| `[images]` | `Array<Record<string, any>>` | `required` | Danh sách tất cả ảnh |
|
|
165
|
-
| `[removeBackdrop]` | `boolean` | `false` | Ẩn backdrop overlay |
|
|
166
|
-
| `[singleImage]` | `boolean` | `false` | Chế độ xem 1 ảnh (ẩn thumbnail list) |
|
|
167
|
-
| `[zIndex]` | `number` | `1200` | z-index của viewer |
|
|
270
|
+
`outFunctionsControl` emit một object `IGalleryFunctionsControlEvent` — dùng để điều khiển gallery từ code bên ngoài:
|
|
168
271
|
|
|
169
|
-
|
|
272
|
+
| Property | Kiểu | Mô tả |
|
|
273
|
+
|---|---|---|
|
|
274
|
+
| `open(imageSelected)` | `(imageSelected: Record<string, any>) => Promise<void>` | Mở viewer và hiển thị ảnh được truyền vào. `imageSelected` phải là object có cùng key `fieldDisplaySrcImage` với ảnh trong mảng `images` |
|
|
275
|
+
| `viewerRef` | `ComponentRef<LibsUiComponentsGalleryViewerComponent> \| undefined` | Tham chiếu đến `ComponentRef` của viewer đang mở. `undefined` khi viewer chưa mở |
|
|
170
276
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
277
|
+
```typescript
|
|
278
|
+
// Lấy controls qua outFunctionsControl
|
|
279
|
+
protected galleryControls = signal<IGalleryFunctionsControlEvent | null>(null);
|
|
280
|
+
|
|
281
|
+
// Mở viewer với ảnh cụ thể
|
|
282
|
+
protected handlerOpenFirstImage(event: Event): void {
|
|
283
|
+
event.stopPropagation();
|
|
284
|
+
const firstImage = this.images[0];
|
|
285
|
+
this.galleryControls()?.open(firstImage);
|
|
286
|
+
}
|
|
174
287
|
|
|
175
|
-
|
|
288
|
+
// Kiểm tra viewer có đang mở không
|
|
289
|
+
protected isViewerOpen(): boolean {
|
|
290
|
+
return !!this.galleryControls()?.viewerRef;
|
|
291
|
+
}
|
|
292
|
+
```
|
|
176
293
|
|
|
177
|
-
|
|
294
|
+
## Types & Interfaces
|
|
178
295
|
|
|
179
296
|
```typescript
|
|
297
|
+
import {
|
|
298
|
+
IGalleryFunctionsControlEvent,
|
|
299
|
+
IZoomDragHTMLElement,
|
|
300
|
+
} from '@libs-ui/components-gallery';
|
|
301
|
+
|
|
302
|
+
// Interface cho FunctionsControl (emit qua outFunctionsControl)
|
|
180
303
|
export interface IGalleryFunctionsControlEvent {
|
|
181
304
|
open: (imageSelected: Record<string, any>) => Promise<void>;
|
|
182
305
|
viewerRef: ComponentRef<any> | undefined;
|
|
183
306
|
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### IZoomDragHTMLElement
|
|
187
307
|
|
|
188
|
-
|
|
308
|
+
// Interface nội bộ cho element hỗ trợ drag khi zoom (internal — hiếm khi cần dùng trực tiếp)
|
|
189
309
|
export interface IZoomDragHTMLElement extends HTMLElement {
|
|
190
310
|
stopDraggableBehavior?(): IZoomDragHTMLElement | null;
|
|
191
311
|
}
|
|
192
312
|
```
|
|
193
313
|
|
|
194
|
-
##
|
|
314
|
+
## Lưu ý quan trọng
|
|
195
315
|
|
|
196
|
-
|
|
316
|
+
⚠️ **`[(images)]` là model (two-way binding)**: Khi dùng `imageArrayStringConvert`, component sẽ tự động ghi lại vào `images`. Truyền `[images]="[]"` khi chỉ dùng `imageArrayStringConvert`.
|
|
197
317
|
|
|
198
|
-
|
|
318
|
+
⚠️ **`imageArrayStringConvert` convert theo `fieldDisplaySrcImage`**: Mảng `['url1.jpg', 'url2.jpg']` sẽ được convert thành `[{ url: 'url1.jpg' }, { url: 'url2.jpg' }]` nếu `fieldDisplaySrcImage = 'url'`. Hai input này phải nhất quán với nhau.
|
|
199
319
|
|
|
200
|
-
|
|
320
|
+
⚠️ **Viewer inject vào `document.body`**: Viewer là dynamic component không nằm trong DOM tree của gallery component. `z-index` mặc định là `1200` — tăng lên nếu có modal/drawer bao bên ngoài có z-index cao hơn.
|
|
201
321
|
|
|
202
|
-
|
|
322
|
+
⚠️ **Keyboard navigation trong viewer**: `Escape` đóng viewer, `ArrowLeft` chuyển ảnh trước, `ArrowRight` chuyển ảnh tiếp. Event được lắng nghe từ `document` — hoạt động bất kể focus đang ở đâu.
|
|
203
323
|
|
|
204
|
-
|
|
324
|
+
⚠️ **Overlay "+N" hiển thị trên thumbnail cuối**: Khi `images.length > end`, thumbnail cuối hiển thị overlay `+N` (N = tổng ảnh - end). Viewer vẫn hiển thị toàn bộ ảnh. Tắt bằng `[ignoreOverlayCountImage]="true"`.
|
|
205
325
|
|
|
206
|
-
|
|
326
|
+
⚠️ **`outFunctionsControl` emit nhiều lần**: Event này emit khi ngOnInit, khi viewer mở và khi viewer đóng — không chỉ emit một lần. Luôn dùng giá trị mới nhất từ signal.
|
|
207
327
|
|
|
208
328
|
## Demo
|
|
209
329
|
|
|
210
|
-
|
|
330
|
+
```bash
|
|
331
|
+
npx nx serve core-ui
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Truy cập: http://localhost:4500/gallery
|
|
211
335
|
|
|
212
336
|
## Test
|
|
213
337
|
|
|
214
338
|
```bash
|
|
215
|
-
nx test components-gallery
|
|
339
|
+
npx nx test components-gallery
|
|
216
340
|
```
|
|
217
|
-
|
|
218
|
-
## License
|
|
219
|
-
|
|
220
|
-
MIT
|
|
@@ -126,10 +126,10 @@ export class LibsUiComponentsGalleryComponent {
|
|
|
126
126
|
this.closePopup();
|
|
127
127
|
}
|
|
128
128
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsGalleryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
129
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LibsUiComponentsGalleryComponent, isStandalone: true, selector: "libs_ui-components-gallery", inputs: { images: { classPropertyName: "images", publicName: "images", isSignal: true, isRequired: true, transformFunction: null }, start: { classPropertyName: "start", publicName: "start", isSignal: true, isRequired: false, transformFunction: null }, end: { classPropertyName: "end", publicName: "end", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, fieldDisplaySrcImage: { classPropertyName: "fieldDisplaySrcImage", publicName: "fieldDisplaySrcImage", isSignal: true, isRequired: false, transformFunction: null }, ignoreOverlayCountImage: { classPropertyName: "ignoreOverlayCountImage", publicName: "ignoreOverlayCountImage", isSignal: true, isRequired: false, transformFunction: null }, imageArrayStringConvert: { classPropertyName: "imageArrayStringConvert", publicName: "imageArrayStringConvert", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { images: "imagesChange", outFunctionsControl: "outFunctionsControl", outViewerEvent: "outViewerEvent" }, ngImport: i0, template: "@if (images()) {\n <div class=\"flex w-full h-full\">\n @for (image of imagesDisplay(); track $index) {\n <div\n #itemRef\n class=\"w-full h-full rounded-[4px] overflow-hidden relative cursor-pointer\"\n [class.mr-[8px]]=\"!$last\"\n (click)=\"handlerSelectImage($event, image)\">\n <div class=\"flex items-center justify-center w-full h-full\">\n @if (!image['isError']) {\n <img\n [src]=\"image[fieldDisplaySrcImage()]\"\n class=\"flex max-w-full max-h-full m-auto\"\n (error)=\"handlerImageError($event, image)\" />\n }\n @if (image['isError']) {\n <div *ngComponentOutlet=\"'image-default' | LibsUiIconsGetIconComponentPipe | async; inputs: { size: itemRef.clientWidth }\"></div>\n }\n </div>\n @if ($last && images().length > end() && !ignoreOverlayCountImage()) {\n <div class=\"flex w-full h-full absolute top-0 left-0 items-center justify-center bg-black/15\">\n <span class=\"libs-ui-font-h5m text-white\">+{{ images().length - end() }}</span>\n </div>\n }\n </div>\n }\n </div>\n}\n",
|
|
129
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LibsUiComponentsGalleryComponent, isStandalone: true, selector: "libs_ui-components-gallery", inputs: { images: { classPropertyName: "images", publicName: "images", isSignal: true, isRequired: true, transformFunction: null }, start: { classPropertyName: "start", publicName: "start", isSignal: true, isRequired: false, transformFunction: null }, end: { classPropertyName: "end", publicName: "end", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, fieldDisplaySrcImage: { classPropertyName: "fieldDisplaySrcImage", publicName: "fieldDisplaySrcImage", isSignal: true, isRequired: false, transformFunction: null }, ignoreOverlayCountImage: { classPropertyName: "ignoreOverlayCountImage", publicName: "ignoreOverlayCountImage", isSignal: true, isRequired: false, transformFunction: null }, imageArrayStringConvert: { classPropertyName: "imageArrayStringConvert", publicName: "imageArrayStringConvert", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { images: "imagesChange", outFunctionsControl: "outFunctionsControl", outViewerEvent: "outViewerEvent" }, ngImport: i0, template: "@if (images()) {\n <div class=\"flex w-full h-full\">\n @for (image of imagesDisplay(); track $index) {\n <div\n #itemRef\n class=\"w-full h-full rounded-[4px] overflow-hidden relative cursor-pointer\"\n [class.mr-[8px]]=\"!$last\"\n (click)=\"handlerSelectImage($event, image)\"\n (keydown.enter)=\"handlerSelectImage($event, image)\">\n <div class=\"flex items-center justify-center w-full h-full\">\n @if (!image['isError']) {\n <img\n [src]=\"image[fieldDisplaySrcImage()]\"\n class=\"flex max-w-full max-h-full m-auto\"\n alt=\"\"\n (error)=\"handlerImageError($event, image)\" />\n }\n @if (image['isError']) {\n <div *ngComponentOutlet=\"'image-default' | LibsUiIconsGetIconComponentPipe | async; inputs: { size: itemRef.clientWidth }\"></div>\n }\n </div>\n @if ($last && images().length > end() && !ignoreOverlayCountImage()) {\n <div class=\"flex w-full h-full absolute top-0 left-0 items-center justify-center bg-black/15\">\n <span class=\"libs-ui-font-h5m text-white\">+{{ images().length - end() }}</span>\n </div>\n }\n </div>\n }\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: LibsUiIconsGetIconComponentPipe, name: "LibsUiIconsGetIconComponentPipe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
130
130
|
}
|
|
131
131
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsGalleryComponent, decorators: [{
|
|
132
132
|
type: Component,
|
|
133
|
-
args: [{ selector: 'libs_ui-components-gallery', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgComponentOutlet, AsyncPipe, LibsUiIconsGetIconComponentPipe], template: "@if (images()) {\n <div class=\"flex w-full h-full\">\n @for (image of imagesDisplay(); track $index) {\n <div\n #itemRef\n class=\"w-full h-full rounded-[4px] overflow-hidden relative cursor-pointer\"\n [class.mr-[8px]]=\"!$last\"\n (click)=\"handlerSelectImage($event, image)\">\n <div class=\"flex items-center justify-center w-full h-full\">\n @if (!image['isError']) {\n <img\n [src]=\"image[fieldDisplaySrcImage()]\"\n class=\"flex max-w-full max-h-full m-auto\"\n (error)=\"handlerImageError($event, image)\" />\n }\n @if (image['isError']) {\n <div *ngComponentOutlet=\"'image-default' | LibsUiIconsGetIconComponentPipe | async; inputs: { size: itemRef.clientWidth }\"></div>\n }\n </div>\n @if ($last && images().length > end() && !ignoreOverlayCountImage()) {\n <div class=\"flex w-full h-full absolute top-0 left-0 items-center justify-center bg-black/15\">\n <span class=\"libs-ui-font-h5m text-white\">+{{ images().length - end() }}</span>\n </div>\n }\n </div>\n }\n </div>\n}\n" }]
|
|
133
|
+
args: [{ selector: 'libs_ui-components-gallery', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgComponentOutlet, AsyncPipe, LibsUiIconsGetIconComponentPipe], template: "@if (images()) {\n <div class=\"flex w-full h-full\">\n @for (image of imagesDisplay(); track $index) {\n <div\n #itemRef\n class=\"w-full h-full rounded-[4px] overflow-hidden relative cursor-pointer\"\n [class.mr-[8px]]=\"!$last\"\n (click)=\"handlerSelectImage($event, image)\"\n (keydown.enter)=\"handlerSelectImage($event, image)\">\n <div class=\"flex items-center justify-center w-full h-full\">\n @if (!image['isError']) {\n <img\n [src]=\"image[fieldDisplaySrcImage()]\"\n class=\"flex max-w-full max-h-full m-auto\"\n alt=\"\"\n (error)=\"handlerImageError($event, image)\" />\n }\n @if (image['isError']) {\n <div *ngComponentOutlet=\"'image-default' | LibsUiIconsGetIconComponentPipe | async; inputs: { size: itemRef.clientWidth }\"></div>\n }\n </div>\n @if ($last && images().length > end() && !ignoreOverlayCountImage()) {\n <div class=\"flex w-full h-full absolute top-0 left-0 items-center justify-center bg-black/15\">\n <span class=\"libs-ui-font-h5m text-white\">+{{ images().length - end() }}</span>\n </div>\n }\n </div>\n }\n </div>\n}\n" }]
|
|
134
134
|
}], ctorParameters: () => [] });
|
|
135
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2FsbGVyeS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvZ2FsbGVyeS9zcmMvZ2FsbGVyeS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvZ2FsbGVyeS9zcmMvZ2FsbGVyeS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx1REFBdUQ7QUFDdkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9ELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQWdCLFFBQVEsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQXFCLE1BQU0sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3ZLLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWpFLE9BQU8sRUFBRSw2QkFBNkIsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ3BGLE9BQU8sRUFBRSxRQUFRLEVBQUUsWUFBWSxFQUFFLHFCQUFxQixFQUFFLDhCQUE4QixFQUFFLElBQUksRUFBRSxNQUFNLGdCQUFnQixDQUFDOztBQWFySCxNQUFNLE9BQU8sZ0NBQWdDO0lBQzNDLG1CQUFtQjtJQUNULGFBQWEsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ3RDLE9BQU8sSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDdkQsQ0FBQyxDQUFDLENBQUM7SUFFSyxTQUFTLEdBQUcsTUFBTSxDQUFTLElBQUksRUFBRSxDQUFDLENBQUM7SUFDbkMsVUFBVSxHQUFHLE1BQU0sQ0FBcUIsU0FBUyxDQUFDLENBQUM7SUFDbkQs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QUksQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxhQUFrQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsU0FBUyxFQUFFLGFBQWEsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNoSyxDQUFDO0lBRUQsSUFBWSxTQUFTO1FBQ25CLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUN4QixDQUFDO0lBRUQsV0FBVztRQUNULElBQUksQ0FBQyxTQUFTLEVBQUUsT0FBTyxFQUFFLENBQUM7UUFDMUIsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQ3BCLENBQUM7d0dBckpVLGdDQUFnQzs0RkFBaEMsZ0NBQWdDLHFyQ0NuQjdDLDBxQ0E0QkEsMEREWFksaUJBQWlCLCtPQUFFLFNBQVMseUNBQUUsK0JBQStCOzs0RkFFNUQsZ0NBQWdDO2tCQVQ1QyxTQUFTOytCQUVFLDRCQUE0QixjQUcxQixJQUFJLG1CQUNDLHVCQUF1QixDQUFDLE1BQU0sV0FDdEMsQ0FBQyxpQkFBaUIsRUFBRSxTQUFTLEVBQUUsK0JBQStCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55ICovXG5pbXBvcnQgeyBBc3luY1BpcGUsIE5nQ29tcG9uZW50T3V0bGV0IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7IENoYW5nZURldGVjdGlvblN0cmF0ZWd5LCBDb21wb25lbnQsIENvbXBvbmVudFJlZiwgY29tcHV0ZWQsIGVmZmVjdCwgaW5qZWN0LCBpbnB1dCwgbW9kZWwsIE9uRGVzdHJveSwgT25Jbml0LCBvdXRwdXQsIHNpZ25hbCwgdW50cmFja2VkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBMaWJzVWlJY29uc0dldEljb25Db21wb25lbnRQaXBlIH0gZnJvbSAnQGxpYnMtdWkvaWNvbnMnO1xuaW1wb3J0IHsgSUV2ZW50IH0gZnJvbSAnQGxpYnMtdWkvaW50ZXJmYWNlcy10eXBlcyc7XG5pbXBvcnQgeyBMaWJzVWlEeW5hbWljQ29tcG9uZW50U2VydmljZSB9IGZyb20gJ0BsaWJzLXVpL3NlcnZpY2VzLWR5bmFtaWMtY29tcG9uZW50JztcbmltcG9ydCB7IGdldERheWpzLCBpc0VtYmVkRnJhbWUsIFV0aWxzQ29tbXVuaWNhdGVNaWNybywgVXRpbHNDb21tdW5pY2F0ZU1pY3JvS2V5R2xvYmFsLCB1dWlkIH0gZnJvbSAnQGxpYnMtdWkvdXRpbHMnO1xuaW1wb3J0IHsgSUdhbGxlcnlGdW5jdGlvbnNDb250cm9sRXZlbnQgfSBmcm9tICcuL2ludGVyZmFjZXMvZnVuY3Rpb25zLWNvbnRyb2wtZXZlbnQuaW50ZXJmYWNlJztcbmltcG9ydCB7IExpYnNVaUNvbXBvbmVudHNHYWxsZXJ5Vmlld2VyQ29tcG9uZW50IH0gZnJvbSAnLi92aWV3ZXIvdmlld2VyLmNvbXBvbmVudCc7XG5cbkBDb21wb25lbnQoe1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L2NvbXBvbmVudC1zZWxlY3RvclxuICBzZWxlY3RvcjogJ2xpYnNfdWktY29tcG9uZW50cy1nYWxsZXJ5JyxcbiAgdGVtcGxhdGVVcmw6ICcuL2dhbGxlcnkuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9nYWxsZXJ5LmNvbXBvbmVudC5zY3NzJ10sXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBpbXBvcnRzOiBbTmdDb21wb25lbnRPdXRsZXQsIEFzeW5jUGlwZSwgTGlic1VpSWNvbnNHZXRJY29uQ29tcG9uZW50UGlwZV0sXG59KVxuZXhwb3J0IGNsYXNzIExpYnNVaUNvbXBvbmVudHNHYWxsZXJ5Q29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuICAvLyAjcmVnaW9uIFBST1BFUlRZXG4gIHByb3RlY3RlZCBpbWFnZXNEaXNwbGF5ID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIHJldHVybiB0aGlzLmltYWdlcygpLnNsaWNlKHRoaXMuc3RhcnQoKSwgdGhpcy5lbmQoKSk7XG4gIH0pO1xuXG4gIHByaXZhdGUgaWRPdmVybGF5ID0gc2lnbmFsPHN0cmluZz4odXVpZCgpKTtcbiAgcHJpdmF0ZSBpZEludGVydmFsID0gc2lnbmFsPG51bWJlciB8IHVuZGVmaW5lZD4odW5kZWZpbmVkKTtcbiAgcHJpdmF0ZSB2aWV3ZXJSZWY6IENvbXBvbmVudFJlZjxMaWJzVWlDb21wb25lbnRzR2FsbGVyeVZpZXdlckNvbXBvbmVudD4gfCB1bmRlZmluZWQ7XG5cbiAgLy8gI3JlZ2lvbiBJTlBVVFxuICByZWFkb25seSBpbWFnZXMgPSBtb2RlbC5yZXF1aXJlZDxBcnJheTxSZWNvcmQ8c3RyaW5nLCBhbnk+Pj4oKTtcbiAgcmVhZG9ubHkgc3RhcnQgPSBpbnB1dDxudW1iZXI+KDApO1xuICByZWFkb25seSBlbmQgPSBpbnB1dDxudW1iZXI+KDMpO1xuICByZWFkb25seSB6SW5kZXggPSBpbnB1dDxudW1iZXI+KDEyMDApO1xuICByZWFkb25seSBmaWVsZERpc3BsYXlTcmNJbWFnZSA9IGlucHV0PHN0cmluZz4oJ3VybCcpO1xuICByZWFkb25seSBpZ25vcmVPdmVybGF5Q291bnRJbWFnZSA9IGlucHV0PGJvb2xlYW4+KCk7XG4gIHJlYWRvbmx5IGltYWdlQXJyYXlTdHJpbmdDb252ZXJ0ID0gaW5wdXQ8QXJyYXk8c3RyaW5nPj4oKTsgLy8gc+G7rSBk4bulbmcgdHJvbmcgMSBz4buRIHRyxrDhu51uZyBo4bujcCBpbWFnZXMgbMOgIEFycmF5PHN0cmluZz5cblxuICAvLyAjcmVnaW9uIE9VVFBVVFxuICByZWFkb25seSBvdXRGdW5jdGlvbnNDb250cm9sID0gb3V0cHV0PElHYWxsZXJ5RnVuY3Rpb25zQ29udHJvbEV2ZW50PigpO1xuICByZWFkb25seSBvdXRWaWV3ZXJFdmVudCA9IG91dHB1dDwnc2hvdycgfCAncmVtb3ZlJz4oKTtcblxuICAvLyAjcmVnaW9uIElOSkVDVFxuICBwcml2YXRlIGR5bmFtaWNDb21wb25lbnQgPSBpbmplY3QoTGlic1VpRHluYW1pY0NvbXBvbmVudFNlcnZpY2UpO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIGVmZmVjdCgoKSA9PiB7XG4gICAgICBjb25zdCBpbWdDb252ZXJ0ID0gdGhpcy5pbWFnZUFycmF5U3RyaW5nQ29udmVydCgpO1xuICAgICAgY29uc3QgZmllbGRHZXRTcmMgPSB0aGlzLmZpZWxkRGlzcGxheVNyY0ltYWdlKCk7XG5cbiAgICAgIGlmICghaW1nQ29udmVydD8ubGVuZ3RoIHx8ICFmaWVsZEdldFNyYykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB1bnRyYWNrZWQoKCkgPT4ge1xuICAgICAgICBjb25zdCBjb252ZXJ0ZWRJbWFnZXMgPVxuICAgICAgICAgIGltZ0NvbnZlcnQ/Lm1hcCgoaXRlbSkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgW2ZpZWxkR2V0U3JjXTogaXRlbSxcbiAgICAgICAgICAgIH0gYXMgUmVjb3JkPHN0cmluZywgYW55PjtcbiAgICAgICAgICB9KSB8fCBbXTtcblxuICAgICAgICB0aGlzLmltYWdlcy5zZXQoY29udmVydGVkSW1hZ2VzKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgbmdPbkluaXQoKSB7XG4gICAgdGhpcy5vdXRGdW5jdGlvbnNDb250cm9sLmVtaXQodGhpcy5GdW5jdGlvbnNDb250cm9sKTtcbiAgfVxuXG4gIC8qIEZVTkNUSU9OUyAqL1xuICBwdWJsaWMgZ2V0IEZ1bmN0aW9uc0NvbnRyb2woKTogSUdhbGxlcnlGdW5jdGlvbnNDb250cm9sRXZlbnQge1xuICAgIHJldHVybiB7XG4gICAgICBvcGVuOiAoaW1hZ2VTZWxlY3RlZDogUmVjb3JkPHN0cmluZywgYW55PikgPT4gdGhpcy5oYW5kbGVyU2VsZWN0SW1hZ2UodW5kZWZpbmVkLCBpbWFnZVNlbGVjdGVkKSxcbiAgICAgIHZpZXdlclJlZjogdGhpcy5WaWV3ZXJSZWYsXG4gICAgfTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhc3luYyBoYW5kbGVyU2VsZWN0SW1hZ2UoZXZlbnQ6IE1vdXNlRXZlbnQgfCB1bmRlZmluZWQsIGltYWdlU2VsZWN0ZWQ6IFJlY29yZDxzdHJpbmcsIGFueT4pIHtcbiAgICBldmVudD8uc3RvcFByb3BhZ2F0aW9uKCk7XG5cbiAgICBpZiAodGhpcy52aWV3ZXJSZWYpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCB2aWV3ZXJDb21wb25lbnQgPSBhd2FpdCBpbXBvcnQoJy4vdmlld2VyL3ZpZXdlci5jb21wb25lbnQnKS50aGVuKChjKSA9PiBjLkxpYnNVaUNvbXBvbmVudHNHYWxsZXJ5Vmlld2VyQ29tcG9uZW50KTtcblxuICAgIHRoaXMudmlld2VyUmVmID0gdGhpcy5keW5hbWljQ29tcG9uZW50LnJlc29sdmVDb21wb25lbnRGYWN0b3J5KHZpZXdlckNvbXBvbmVudCk7XG4gICAgdGhpcy5vdXRGdW5jdGlvbnNDb250cm9sLmVtaXQoeyB2aWV3ZXJSZWY6IHRoaXMuVmlld2VyUmVmLCBvcGVuOiAoaW1hZ2VTZWxlY3RlZDogUmVjb3JkPHN0cmluZywgYW55PikgPT4gdGhpcy5oYW5kbGVyU2VsZWN0SW1hZ2UodW5kZWZpbmVkLCBpbWFnZVNlbGVjdGVkKSB9KTtcblxuICAgIHRoaXMudmlld2VyUmVmLnNldElucHV0KCdmaWVsZERpc3BsYXlTcmNJbWFnZScsIHRoaXMuZmllbGREaXNwbGF5U3JjSW1hZ2UoKSk7XG4gICAgdGhpcy52aWV3ZXJSZWYuc2V0SW5wdXQoJ2ltYWdlcycsIHRoaXMuaW1hZ2VzKCkpO1xuICAgIHRoaXMudmlld2VyUmVmLnNldElucHV0KCdpbWFnZVNlbGVjdGVkJywgaW1hZ2VTZWxlY3RlZCk7XG4gICAgdGhpcy52aWV3ZXJSZWYuc2V0SW5wdXQoJ3pJbmRleCcsIHRoaXMuekluZGV4KCkpO1xuICAgIHRoaXMudmlld2VyUmVmLnNldElucHV0KCdzaW5nbGVJbWFnZScsIHRoaXMuaW1hZ2VzKCkubGVuZ3RoIDw9IDEpO1xuXG4gICAgY29uc3Qgc3ViID0gdGhpcy52aWV3ZXJSZWYuaW5zdGFuY2Uub3V0Q2xvc2Uuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIHN1Yi51bnN1YnNjcmliZSgpO1xuICAgICAgdGhpcy5jbG9zZVBvcHVwKCk7XG4gICAgfSk7XG5cbiAgICB0aGlzLmR5bmFtaWNDb21wb25lbnQuYWRkVG9Cb2R5KHRoaXMudmlld2VyUmVmKTtcbiAgICB0aGlzLm91dFZpZXdlckV2ZW50LmVtaXQoJ3Nob3cnKTtcblxuICAgIGlmIChpc0VtYmVkRnJhbWUoKSkge1xuICAgICAgY29uc3QgZGF0YSA9IHtcbiAgICAgICAgdHlwZTogVXRpbHNDb21tdW5pY2F0ZU1pY3JvS2V5R2xvYmFsLktFWV9NRVNTQUdFX01PREFMLFxuICAgICAgICByZXNwb25zZToge1xuICAgICAgICAgIG1lc3NhZ2U6ICdvcGVuJyxcbiAgICAgICAgICBzdGF0ZTogdHJ1ZSxcbiAgICAgICAgICBpZE92ZXJsYXk6IHRoaXMuaWRPdmVybGF5LFxuICAgICAgICAgIHRpbWVMaXZlVXBkYXRlOiBnZXREYXlqcygpLnVuaXgoKSxcbiAgICAgICAgICBpZ25vcmVJbnRlcnZhbFVwZGF0ZVRpbWVMaXZlRXZlbnQ6IFV0aWxzQ29tbXVuaWNhdGVNaWNyb0tleUdsb2JhbC5JR05PUkVfSU5URVJWQUxfVVBEQVRFX1RJTUVfTElWRV9FVkVOVF9NT0RBTCxcbiAgICAgICAgfSxcbiAgICAgIH07XG5cbiAgICAgIFV0aWxzQ29tbXVuaWNhdGVNaWNyby5Qb3N0TWVzc2FnZVRvUGFyZW50KGRhdGEpO1xuICAgICAgaWYgKFV0aWxzQ29tbXVuaWNhdGVNaWNyb0tleUdsb2JhbC5JR05PUkVfSU5URVJWQUxfVVBEQVRFX1RJTUVfTElWRV9FVkVOVF9NT0RBTCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIHRoaXMuaWRJbnRlcnZhbC5zZXQoXG4gICAgICAgIHNldEludGVydmFsKCgpID0+IHtcbiAgICAgICAgICBkYXRhLnJlc3BvbnNlLnRpbWVMaXZlVXBkYXRlID0gZ2V0RGF5anMoKS51bml4KCk7XG5cbiAgICAgICAgICBVdGlsc0NvbW11bmljYXRlTWljcm8uUG9zdE1lc3NhZ2VUb1BhcmVudChkYXRhKTtcbiAgICAgICAgfSwgMjAwMCkgYXMgdW5rbm93biBhcyBudW1iZXJcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJJbWFnZUVycm9yKGU6IEVycm9yRXZlbnQsIGltYWdlOiBSZWNvcmQ8c3RyaW5nLCBhbnk+KSB7XG4gICAgY29uc3QgZXZlbnQgPSBlIGFzIElFdmVudDtcblxuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuXG4gICAgaW1hZ2VbJ2lzRXJyb3InXSA9IHRydWU7XG4gIH1cblxuICBwcml2YXRlIGNsb3NlUG9wdXAoKSB7XG4gICAgY2xlYXJJbnRlcnZhbCh0aGlzLmlkSW50ZXJ2YWwoKSk7XG4gICAgaWYgKHRoaXMudmlld2VyUmVmKSB7XG4gICAgICB0aGlzLm91dFZpZXdlckV2ZW50LmVtaXQoJ3JlbW92ZScpO1xuICAgICAgaWYgKGlzRW1iZWRGcmFtZSgpKSB7XG4gICAgICAgIGNvbnN0IGRhdGEgPSB7XG4gICAgICAgICAgdHlwZTogVXRpbHNDb21tdW5pY2F0ZU1pY3JvS2V5R2xvYmFsLktFWV9NRVNTQUdFX01PREFMLFxuICAgICAgICAgIHJlc3BvbnNlOiB7XG4gICAgICAgICAgICBtZXNzYWdlOiAnY2xvc2UnLFxuICAgICAgICAgICAgc3RhdGU6IGZhbHNlLFxuICAgICAgICAgICAgaWRPdmVybGF5OiB0aGlzLmlkT3ZlcmxheSxcbiAgICAgICAgICB9LFxuICAgICAgICB9O1xuXG4gICAgICAgIFV0aWxzQ29tbXVuaWNhdGVNaWNyby5Qb3N0TWVzc2FnZVRvUGFyZW50KGRhdGEpO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLmR5bmFtaWNDb21wb25lbnQucmVtb3ZlKHRoaXMudmlld2VyUmVmKTtcbiAgICB0aGlzLnZpZXdlclJlZiA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLm91dEZ1bmN0aW9uc0NvbnRyb2wuZW1pdCh7IHZpZXdlclJlZjogdGhpcy5WaWV3ZXJSZWYsIG9wZW46IChpbWFnZVNlbGVjdGVkOiBSZWNvcmQ8c3RyaW5nLCBhbnk+KSA9PiB0aGlzLmhhbmRsZXJTZWxlY3RJbWFnZSh1bmRlZmluZWQsIGltYWdlU2VsZWN0ZWQpIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXQgVmlld2VyUmVmKCkge1xuICAgIHJldHVybiB0aGlzLnZpZXdlclJlZjtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCkge1xuICAgIHRoaXMudmlld2VyUmVmPy5kZXN0cm95KCk7XG4gICAgdGhpcy5jbG9zZVBvcHVwKCk7XG4gIH1cbn1cbiIsIkBpZiAoaW1hZ2VzKCkpIHtcbiAgPGRpdiBjbGFzcz1cImZsZXggdy1mdWxsIGgtZnVsbFwiPlxuICAgIEBmb3IgKGltYWdlIG9mIGltYWdlc0Rpc3BsYXkoKTsgdHJhY2sgJGluZGV4KSB7XG4gICAgICA8ZGl2XG4gICAgICAgICNpdGVtUmVmXG4gICAgICAgIGNsYXNzPVwidy1mdWxsIGgtZnVsbCByb3VuZGVkLVs0cHhdIG92ZXJmbG93LWhpZGRlbiByZWxhdGl2ZSBjdXJzb3ItcG9pbnRlclwiXG4gICAgICAgIFtjbGFzcy5tci1bOHB4XV09XCIhJGxhc3RcIlxuICAgICAgICAoY2xpY2spPVwiaGFuZGxlclNlbGVjdEltYWdlKCRldmVudCwgaW1hZ2UpXCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciB3LWZ1bGwgaC1mdWxsXCI+XG4gICAgICAgICAgQGlmICghaW1hZ2VbJ2lzRXJyb3InXSkge1xuICAgICAgICAgICAgPGltZ1xuICAgICAgICAgICAgICBbc3JjXT1cImltYWdlW2ZpZWxkRGlzcGxheVNyY0ltYWdlKCldXCJcbiAgICAgICAgICAgICAgY2xhc3M9XCJmbGV4IG1heC13LWZ1bGwgbWF4LWgtZnVsbCBtLWF1dG9cIlxuICAgICAgICAgICAgICAoZXJyb3IpPVwiaGFuZGxlckltYWdlRXJyb3IoJGV2ZW50LCBpbWFnZSlcIiAvPlxuICAgICAgICAgIH1cbiAgICAgICAgICBAaWYgKGltYWdlWydpc0Vycm9yJ10pIHtcbiAgICAgICAgICAgIDxkaXYgKm5nQ29tcG9uZW50T3V0bGV0PVwiJ2ltYWdlLWRlZmF1bHQnIHwgTGlic1VpSWNvbnNHZXRJY29uQ29tcG9uZW50UGlwZSB8IGFzeW5jOyBpbnB1dHM6IHsgc2l6ZTogaXRlbVJlZi5jbGllbnRXaWR0aCB9XCI+PC9kaXY+XG4gICAgICAgICAgfVxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgQGlmICgkbGFzdCAmJiBpbWFnZXMoKS5sZW5ndGggPiBlbmQoKSAmJiAhaWdub3JlT3ZlcmxheUNvdW50SW1hZ2UoKSkge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJmbGV4IHctZnVsbCBoLWZ1bGwgYWJzb2x1dGUgdG9wLTAgbGVmdC0wIGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciBiZy1ibGFjay8xNVwiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsaWJzLXVpLWZvbnQtaDVtIHRleHQtd2hpdGVcIj4re3sgaW1hZ2VzKCkubGVuZ3RoIC0gZW5kKCkgfX08L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgPC9kaXY+XG59XG4iXX0=
|
|
135
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2FsbGVyeS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvZ2FsbGVyeS9zcmMvZ2FsbGVyeS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvZ2FsbGVyeS9zcmMvZ2FsbGVyeS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx1REFBdUQ7QUFDdkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9ELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQWdCLFFBQVEsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQXFCLE1BQU0sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3ZLLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWpFLE9BQU8sRUFBRSw2QkFBNkIsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ3BGLE9BQU8sRUFBRSxRQUFRLEVBQUUsWUFBWSxFQUFFLHFCQUFxQixFQUFFLDhCQUE4QixFQUFFLElBQUksRUFBRSxNQUFNLGdCQUFnQixDQUFDOztBQVlySCxNQUFNLE9BQU8sZ0NBQWdDO0lBQzNDLG1CQUFtQjtJQUNULGFBQWEsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ3RDLE9BQU8sSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDdkQsQ0FBQyxDQUFDLENBQUM7SUFFYyxTQUFTLEdBQUcsTUFBTSxDQUFTLElBQUksRUFBRSxDQUFDLENBQUM7SUFDbkMsVUFBVSxHQUFHLE1BQU0sQ0FBcUIsU0FBUyxDQUFDLENBQUM7SUFDNUQs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QUksQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxhQUFrQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsU0FBUyxFQUFFLGFBQWEsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNoSyxDQUFDO0lBRUQsSUFBWSxTQUFTO1FBQ25CLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUN4QixDQUFDO0lBRUQsV0FBVztRQUNULElBQUksQ0FBQyxTQUFTLEVBQUUsT0FBTyxFQUFFLENBQUM7UUFDMUIsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQ3BCLENBQUM7d0dBckpVLGdDQUFnQzs0RkFBaEMsZ0NBQWdDLHFyQ0NsQjdDLGl3Q0E4QkEsNENEZFksaUJBQWlCLCtPQUFFLFNBQVMseUNBQUUsK0JBQStCOzs0RkFFNUQsZ0NBQWdDO2tCQVI1QyxTQUFTOytCQUVFLDRCQUE0QixjQUUxQixJQUFJLG1CQUNDLHVCQUF1QixDQUFDLE1BQU0sV0FDdEMsQ0FBQyxpQkFBaUIsRUFBRSxTQUFTLEVBQUUsK0JBQStCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55ICovXG5pbXBvcnQgeyBBc3luY1BpcGUsIE5nQ29tcG9uZW50T3V0bGV0IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7IENoYW5nZURldGVjdGlvblN0cmF0ZWd5LCBDb21wb25lbnQsIENvbXBvbmVudFJlZiwgY29tcHV0ZWQsIGVmZmVjdCwgaW5qZWN0LCBpbnB1dCwgbW9kZWwsIE9uRGVzdHJveSwgT25Jbml0LCBvdXRwdXQsIHNpZ25hbCwgdW50cmFja2VkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBMaWJzVWlJY29uc0dldEljb25Db21wb25lbnRQaXBlIH0gZnJvbSAnQGxpYnMtdWkvaWNvbnMnO1xuaW1wb3J0IHsgSUV2ZW50IH0gZnJvbSAnQGxpYnMtdWkvaW50ZXJmYWNlcy10eXBlcyc7XG5pbXBvcnQgeyBMaWJzVWlEeW5hbWljQ29tcG9uZW50U2VydmljZSB9IGZyb20gJ0BsaWJzLXVpL3NlcnZpY2VzLWR5bmFtaWMtY29tcG9uZW50JztcbmltcG9ydCB7IGdldERheWpzLCBpc0VtYmVkRnJhbWUsIFV0aWxzQ29tbXVuaWNhdGVNaWNybywgVXRpbHNDb21tdW5pY2F0ZU1pY3JvS2V5R2xvYmFsLCB1dWlkIH0gZnJvbSAnQGxpYnMtdWkvdXRpbHMnO1xuaW1wb3J0IHsgSUdhbGxlcnlGdW5jdGlvbnNDb250cm9sRXZlbnQgfSBmcm9tICcuL2ludGVyZmFjZXMvZnVuY3Rpb25zLWNvbnRyb2wtZXZlbnQuaW50ZXJmYWNlJztcbmltcG9ydCB7IExpYnNVaUNvbXBvbmVudHNHYWxsZXJ5Vmlld2VyQ29tcG9uZW50IH0gZnJvbSAnLi92aWV3ZXIvdmlld2VyLmNvbXBvbmVudCc7XG5cbkBDb21wb25lbnQoe1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L2NvbXBvbmVudC1zZWxlY3RvclxuICBzZWxlY3RvcjogJ2xpYnNfdWktY29tcG9uZW50cy1nYWxsZXJ5JyxcbiAgdGVtcGxhdGVVcmw6ICcuL2dhbGxlcnkuY29tcG9uZW50Lmh0bWwnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgaW1wb3J0czogW05nQ29tcG9uZW50T3V0bGV0LCBBc3luY1BpcGUsIExpYnNVaUljb25zR2V0SWNvbkNvbXBvbmVudFBpcGVdLFxufSlcbmV4cG9ydCBjbGFzcyBMaWJzVWlDb21wb25lbnRzR2FsbGVyeUNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcbiAgLy8gI3JlZ2lvbiBQUk9QRVJUWVxuICBwcm90ZWN0ZWQgaW1hZ2VzRGlzcGxheSA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICByZXR1cm4gdGhpcy5pbWFnZXMoKS5zbGljZSh0aGlzLnN0YXJ0KCksIHRoaXMuZW5kKCkpO1xuICB9KTtcblxuICBwcml2YXRlIHJlYWRvbmx5IGlkT3ZlcmxheSA9IHNpZ25hbDxzdHJpbmc+KHV1aWQoKSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgaWRJbnRlcnZhbCA9IHNpZ25hbDxudW1iZXIgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG4gIHByaXZhdGUgdmlld2VyUmVmOiBDb21wb25lbnRSZWY8TGlic1VpQ29tcG9uZW50c0dhbGxlcnlWaWV3ZXJDb21wb25lbnQ+IHwgdW5kZWZpbmVkO1xuXG4gIC8vICNyZWdpb24gSU5QVVRcbiAgcmVhZG9ubHkgaW1hZ2VzID0gbW9kZWwucmVxdWlyZWQ8QXJyYXk8UmVjb3JkPHN0cmluZywgYW55Pj4+KCk7XG4gIHJlYWRvbmx5IHN0YXJ0ID0gaW5wdXQ8bnVtYmVyPigwKTtcbiAgcmVhZG9ubHkgZW5kID0gaW5wdXQ8bnVtYmVyPigzKTtcbiAgcmVhZG9ubHkgekluZGV4ID0gaW5wdXQ8bnVtYmVyPigxMjAwKTtcbiAgcmVhZG9ubHkgZmllbGREaXNwbGF5U3JjSW1hZ2UgPSBpbnB1dDxzdHJpbmc+KCd1cmwnKTtcbiAgcmVhZG9ubHkgaWdub3JlT3ZlcmxheUNvdW50SW1hZ2UgPSBpbnB1dDxib29sZWFuPigpO1xuICByZWFkb25seSBpbWFnZUFycmF5U3RyaW5nQ29udmVydCA9IGlucHV0PEFycmF5PHN0cmluZz4+KCk7IC8vIHPhu60gZOG7pW5nIHRyb25nIDEgc+G7kSB0csaw4budbmcgaOG7o3AgaW1hZ2VzIGzDoCBBcnJheTxzdHJpbmc+XG5cbiAgLy8gI3JlZ2lvbiBPVVRQVVRcbiAgcmVhZG9ubHkgb3V0RnVuY3Rpb25zQ29udHJvbCA9IG91dHB1dDxJR2FsbGVyeUZ1bmN0aW9uc0NvbnRyb2xFdmVudD4oKTtcbiAgcmVhZG9ubHkgb3V0Vmlld2VyRXZlbnQgPSBvdXRwdXQ8J3Nob3cnIHwgJ3JlbW92ZSc+KCk7XG5cbiAgLy8gI3JlZ2lvbiBJTkpFQ1RcbiAgcHJpdmF0ZSByZWFkb25seSBkeW5hbWljQ29tcG9uZW50ID0gaW5qZWN0KExpYnNVaUR5bmFtaWNDb21wb25lbnRTZXJ2aWNlKTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICBlZmZlY3QoKCkgPT4ge1xuICAgICAgY29uc3QgaW1nQ29udmVydCA9IHRoaXMuaW1hZ2VBcnJheVN0cmluZ0NvbnZlcnQoKTtcbiAgICAgIGNvbnN0IGZpZWxkR2V0U3JjID0gdGhpcy5maWVsZERpc3BsYXlTcmNJbWFnZSgpO1xuXG4gICAgICBpZiAoIWltZ0NvbnZlcnQ/Lmxlbmd0aCB8fCAhZmllbGRHZXRTcmMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdW50cmFja2VkKCgpID0+IHtcbiAgICAgICAgY29uc3QgY29udmVydGVkSW1hZ2VzID1cbiAgICAgICAgICBpbWdDb252ZXJ0Py5tYXAoKGl0ZW0pID0+IHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIFtmaWVsZEdldFNyY106IGl0ZW0sXG4gICAgICAgICAgICB9IGFzIFJlY29yZDxzdHJpbmcsIGFueT47XG4gICAgICAgICAgfSkgfHwgW107XG5cbiAgICAgICAgdGhpcy5pbWFnZXMuc2V0KGNvbnZlcnRlZEltYWdlcyk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIG5nT25Jbml0KCkge1xuICAgIHRoaXMub3V0RnVuY3Rpb25zQ29udHJvbC5lbWl0KHRoaXMuRnVuY3Rpb25zQ29udHJvbCk7XG4gIH1cblxuICAvKiBGVU5DVElPTlMgKi9cbiAgcHVibGljIGdldCBGdW5jdGlvbnNDb250cm9sKCk6IElHYWxsZXJ5RnVuY3Rpb25zQ29udHJvbEV2ZW50IHtcbiAgICByZXR1cm4ge1xuICAgICAgb3BlbjogKGltYWdlU2VsZWN0ZWQ6IFJlY29yZDxzdHJpbmcsIGFueT4pID0+IHRoaXMuaGFuZGxlclNlbGVjdEltYWdlKHVuZGVmaW5lZCwgaW1hZ2VTZWxlY3RlZCksXG4gICAgICB2aWV3ZXJSZWY6IHRoaXMuVmlld2VyUmVmLFxuICAgIH07XG4gIH1cblxuICBwcm90ZWN0ZWQgYXN5bmMgaGFuZGxlclNlbGVjdEltYWdlKGV2ZW50OiBFdmVudCB8IHVuZGVmaW5lZCwgaW1hZ2VTZWxlY3RlZDogUmVjb3JkPHN0cmluZywgYW55Pikge1xuICAgIGV2ZW50Py5zdG9wUHJvcGFnYXRpb24oKTtcblxuICAgIGlmICh0aGlzLnZpZXdlclJlZikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHZpZXdlckNvbXBvbmVudCA9IGF3YWl0IGltcG9ydCgnLi92aWV3ZXIvdmlld2VyLmNvbXBvbmVudCcpLnRoZW4oKGMpID0+IGMuTGlic1VpQ29tcG9uZW50c0dhbGxlcnlWaWV3ZXJDb21wb25lbnQpO1xuXG4gICAgdGhpcy52aWV3ZXJSZWYgPSB0aGlzLmR5bmFtaWNDb21wb25lbnQucmVzb2x2ZUNvbXBvbmVudEZhY3Rvcnkodmlld2VyQ29tcG9uZW50KTtcbiAgICB0aGlzLm91dEZ1bmN0aW9uc0NvbnRyb2wuZW1pdCh7IHZpZXdlclJlZjogdGhpcy5WaWV3ZXJSZWYsIG9wZW46IChpbWFnZVNlbGVjdGVkOiBSZWNvcmQ8c3RyaW5nLCBhbnk+KSA9PiB0aGlzLmhhbmRsZXJTZWxlY3RJbWFnZSh1bmRlZmluZWQsIGltYWdlU2VsZWN0ZWQpIH0pO1xuXG4gICAgdGhpcy52aWV3ZXJSZWYuc2V0SW5wdXQoJ2ZpZWxkRGlzcGxheVNyY0ltYWdlJywgdGhpcy5maWVsZERpc3BsYXlTcmNJbWFnZSgpKTtcbiAgICB0aGlzLnZpZXdlclJlZi5zZXRJbnB1dCgnaW1hZ2VzJywgdGhpcy5pbWFnZXMoKSk7XG4gICAgdGhpcy52aWV3ZXJSZWYuc2V0SW5wdXQoJ2ltYWdlU2VsZWN0ZWQnLCBpbWFnZVNlbGVjdGVkKTtcbiAgICB0aGlzLnZpZXdlclJlZi5zZXRJbnB1dCgnekluZGV4JywgdGhpcy56SW5kZXgoKSk7XG4gICAgdGhpcy52aWV3ZXJSZWYuc2V0SW5wdXQoJ3NpbmdsZUltYWdlJywgdGhpcy5pbWFnZXMoKS5sZW5ndGggPD0gMSk7XG5cbiAgICBjb25zdCBzdWIgPSB0aGlzLnZpZXdlclJlZi5pbnN0YW5jZS5vdXRDbG9zZS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgc3ViLnVuc3Vic2NyaWJlKCk7XG4gICAgICB0aGlzLmNsb3NlUG9wdXAoKTtcbiAgICB9KTtcblxuICAgIHRoaXMuZHluYW1pY0NvbXBvbmVudC5hZGRUb0JvZHkodGhpcy52aWV3ZXJSZWYpO1xuICAgIHRoaXMub3V0Vmlld2VyRXZlbnQuZW1pdCgnc2hvdycpO1xuXG4gICAgaWYgKGlzRW1iZWRGcmFtZSgpKSB7XG4gICAgICBjb25zdCBkYXRhID0ge1xuICAgICAgICB0eXBlOiBVdGlsc0NvbW11bmljYXRlTWljcm9LZXlHbG9iYWwuS0VZX01FU1NBR0VfTU9EQUwsXG4gICAgICAgIHJlc3BvbnNlOiB7XG4gICAgICAgICAgbWVzc2FnZTogJ29wZW4nLFxuICAgICAgICAgIHN0YXRlOiB0cnVlLFxuICAgICAgICAgIGlkT3ZlcmxheTogdGhpcy5pZE92ZXJsYXksXG4gICAgICAgICAgdGltZUxpdmVVcGRhdGU6IGdldERheWpzKCkudW5peCgpLFxuICAgICAgICAgIGlnbm9yZUludGVydmFsVXBkYXRlVGltZUxpdmVFdmVudDogVXRpbHNDb21tdW5pY2F0ZU1pY3JvS2V5R2xvYmFsLklHTk9SRV9JTlRFUlZBTF9VUERBVEVfVElNRV9MSVZFX0VWRU5UX01PREFMLFxuICAgICAgICB9LFxuICAgICAgfTtcblxuICAgICAgVXRpbHNDb21tdW5pY2F0ZU1pY3JvLlBvc3RNZXNzYWdlVG9QYXJlbnQoZGF0YSk7XG4gICAgICBpZiAoVXRpbHNDb21tdW5pY2F0ZU1pY3JvS2V5R2xvYmFsLklHTk9SRV9JTlRFUlZBTF9VUERBVEVfVElNRV9MSVZFX0VWRU5UX01PREFMKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdGhpcy5pZEludGVydmFsLnNldChcbiAgICAgICAgc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgICAgIGRhdGEucmVzcG9uc2UudGltZUxpdmVVcGRhdGUgPSBnZXREYXlqcygpLnVuaXgoKTtcblxuICAgICAgICAgIFV0aWxzQ29tbXVuaWNhdGVNaWNyby5Qb3N0TWVzc2FnZVRvUGFyZW50KGRhdGEpO1xuICAgICAgICB9LCAyMDAwKSBhcyB1bmtub3duIGFzIG51bWJlclxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckltYWdlRXJyb3IoZTogRXJyb3JFdmVudCwgaW1hZ2U6IFJlY29yZDxzdHJpbmcsIGFueT4pIHtcbiAgICBjb25zdCBldmVudCA9IGUgYXMgSUV2ZW50O1xuXG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cbiAgICBpbWFnZVsnaXNFcnJvciddID0gdHJ1ZTtcbiAgfVxuXG4gIHByaXZhdGUgY2xvc2VQb3B1cCgpIHtcbiAgICBjbGVhckludGVydmFsKHRoaXMuaWRJbnRlcnZhbCgpKTtcbiAgICBpZiAodGhpcy52aWV3ZXJSZWYpIHtcbiAgICAgIHRoaXMub3V0Vmlld2VyRXZlbnQuZW1pdCgncmVtb3ZlJyk7XG4gICAgICBpZiAoaXNFbWJlZEZyYW1lKCkpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IHtcbiAgICAgICAgICB0eXBlOiBVdGlsc0NvbW11bmljYXRlTWljcm9LZXlHbG9iYWwuS0VZX01FU1NBR0VfTU9EQUwsXG4gICAgICAgICAgcmVzcG9uc2U6IHtcbiAgICAgICAgICAgIG1lc3NhZ2U6ICdjbG9zZScsXG4gICAgICAgICAgICBzdGF0ZTogZmFsc2UsXG4gICAgICAgICAgICBpZE92ZXJsYXk6IHRoaXMuaWRPdmVybGF5LFxuICAgICAgICAgIH0sXG4gICAgICAgIH07XG5cbiAgICAgICAgVXRpbHNDb21tdW5pY2F0ZU1pY3JvLlBvc3RNZXNzYWdlVG9QYXJlbnQoZGF0YSk7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuZHluYW1pY0NvbXBvbmVudC5yZW1vdmUodGhpcy52aWV3ZXJSZWYpO1xuICAgIHRoaXMudmlld2VyUmVmID0gdW5kZWZpbmVkO1xuICAgIHRoaXMub3V0RnVuY3Rpb25zQ29udHJvbC5lbWl0KHsgdmlld2VyUmVmOiB0aGlzLlZpZXdlclJlZiwgb3BlbjogKGltYWdlU2VsZWN0ZWQ6IFJlY29yZDxzdHJpbmcsIGFueT4pID0+IHRoaXMuaGFuZGxlclNlbGVjdEltYWdlKHVuZGVmaW5lZCwgaW1hZ2VTZWxlY3RlZCkgfSk7XG4gIH1cblxuICBwcml2YXRlIGdldCBWaWV3ZXJSZWYoKSB7XG4gICAgcmV0dXJuIHRoaXMudmlld2VyUmVmO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgdGhpcy52aWV3ZXJSZWY/LmRlc3Ryb3koKTtcbiAgICB0aGlzLmNsb3NlUG9wdXAoKTtcbiAgfVxufVxuIiwiQGlmIChpbWFnZXMoKSkge1xuICA8ZGl2IGNsYXNzPVwiZmxleCB3LWZ1bGwgaC1mdWxsXCI+XG4gICAgQGZvciAoaW1hZ2Ugb2YgaW1hZ2VzRGlzcGxheSgpOyB0cmFjayAkaW5kZXgpIHtcbiAgICAgIDxkaXZcbiAgICAgICAgI2l0ZW1SZWZcbiAgICAgICAgY2xhc3M9XCJ3LWZ1bGwgaC1mdWxsIHJvdW5kZWQtWzRweF0gb3ZlcmZsb3ctaGlkZGVuIHJlbGF0aXZlIGN1cnNvci1wb2ludGVyXCJcbiAgICAgICAgW2NsYXNzLm1yLVs4cHhdXT1cIiEkbGFzdFwiXG4gICAgICAgIChjbGljayk9XCJoYW5kbGVyU2VsZWN0SW1hZ2UoJGV2ZW50LCBpbWFnZSlcIlxuICAgICAgICAoa2V5ZG93bi5lbnRlcik9XCJoYW5kbGVyU2VsZWN0SW1hZ2UoJGV2ZW50LCBpbWFnZSlcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIHctZnVsbCBoLWZ1bGxcIj5cbiAgICAgICAgICBAaWYgKCFpbWFnZVsnaXNFcnJvciddKSB7XG4gICAgICAgICAgICA8aW1nXG4gICAgICAgICAgICAgIFtzcmNdPVwiaW1hZ2VbZmllbGREaXNwbGF5U3JjSW1hZ2UoKV1cIlxuICAgICAgICAgICAgICBjbGFzcz1cImZsZXggbWF4LXctZnVsbCBtYXgtaC1mdWxsIG0tYXV0b1wiXG4gICAgICAgICAgICAgIGFsdD1cIlwiXG4gICAgICAgICAgICAgIChlcnJvcik9XCJoYW5kbGVySW1hZ2VFcnJvcigkZXZlbnQsIGltYWdlKVwiIC8+XG4gICAgICAgICAgfVxuICAgICAgICAgIEBpZiAoaW1hZ2VbJ2lzRXJyb3InXSkge1xuICAgICAgICAgICAgPGRpdiAqbmdDb21wb25lbnRPdXRsZXQ9XCInaW1hZ2UtZGVmYXVsdCcgfCBMaWJzVWlJY29uc0dldEljb25Db21wb25lbnRQaXBlIHwgYXN5bmM7IGlucHV0czogeyBzaXplOiBpdGVtUmVmLmNsaWVudFdpZHRoIH1cIj48L2Rpdj5cbiAgICAgICAgICB9XG4gICAgICAgIDwvZGl2PlxuICAgICAgICBAaWYgKCRsYXN0ICYmIGltYWdlcygpLmxlbmd0aCA+IGVuZCgpICYmICFpZ25vcmVPdmVybGF5Q291bnRJbWFnZSgpKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImZsZXggdy1mdWxsIGgtZnVsbCBhYnNvbHV0ZSB0b3AtMCBsZWZ0LTAgaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIGJnLWJsYWNrLzE1XCI+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZm9udC1oNW0gdGV4dC13aGl0ZVwiPit7eyBpbWFnZXMoKS5sZW5ndGggLSBlbmQoKSB9fTwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG4gICAgfVxuICA8L2Rpdj5cbn1cbiJdfQ==
|