@libs-ui/services-grid-layout 0.2.357-20 → 0.2.357-22
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/canvas/grid-layout-canvas.component.d.ts +112 -4
- package/defines/collision.define.d.ts +56 -0
- package/drag-controller.d.ts +34 -0
- package/esm2022/canvas/grid-layout-canvas.component.mjs +387 -8
- package/esm2022/defines/collision.define.mjs +329 -0
- package/esm2022/defines/gridster-config.define.mjs +4 -5
- package/esm2022/drag-controller.mjs +89 -0
- package/esm2022/grid-layout.service.mjs +245 -3
- package/esm2022/index.mjs +3 -1
- package/esm2022/interfaces/grid-layout.interface.mjs +1 -1
- package/esm2022/tree.service.mjs +20 -1
- package/fesm2022/libs-ui-services-grid-layout.mjs +1066 -14
- package/fesm2022/libs-ui-services-grid-layout.mjs.map +1 -1
- package/grid-layout.service.d.ts +100 -0
- package/index.d.ts +2 -0
- package/interfaces/grid-layout.interface.d.ts +108 -0
- package/package.json +3 -3
- package/tree.service.d.ts +4 -0
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { ChangeDetectionStrategy, Component, ViewContainerRef, computed, inject, input, output,
|
|
1
|
+
import { ChangeDetectionStrategy, Component, HostListener, ViewContainerRef, computed, effect, inject, input, model, output, signal, viewChild, viewChildren, } from '@angular/core';
|
|
2
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
2
3
|
import { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';
|
|
3
4
|
import { GridsterComponent, GridsterModule } from 'gridster18';
|
|
5
|
+
import { TranslateModule } from '@ngx-translate/core';
|
|
4
6
|
import { emptyRootNode } from '../defines/gridster-config.define';
|
|
5
7
|
import { LibsUiGridLayoutService } from '../grid-layout.service';
|
|
8
|
+
import { calculateDropGhost, parseDropPayload } from '../defines/collision.define';
|
|
6
9
|
import * as i0 from "@angular/core";
|
|
7
10
|
import * as i1 from "gridster18";
|
|
11
|
+
import * as i2 from "@ngx-translate/core";
|
|
8
12
|
/**
|
|
9
13
|
* Mặt phẳng kéo thả. Đệ quy: khối có `children` thì ruột nó lại là một mặt phẳng nữa.
|
|
10
14
|
*
|
|
@@ -21,6 +25,23 @@ export class LibsUiGridLayoutCanvasComponent {
|
|
|
21
25
|
node = input();
|
|
22
26
|
/** @internal Chỉ mặt phẳng LỒNG dùng — thư viện tự truyền. */
|
|
23
27
|
depth = input(0);
|
|
28
|
+
/**
|
|
29
|
+
* Ẩn hẳn thanh công cụ mặc định (thêm-vào-trong · gộp/tách · xoá) của MỌI khối.
|
|
30
|
+
*
|
|
31
|
+
* Dùng khi sản phẩm gom các hành động đó vào chỗ khác — vd một nút ⋮ nằm trong chính nội dung
|
|
32
|
+
* khối. Ẩn rồi thì `outRemoveNode` · `outAddBlockInside` · `outToggleType` không còn nguồn phát
|
|
33
|
+
* từ thư viện; nơi dùng tự gọi `removeBlock` · `addBlock` · `toggleType` của service.
|
|
34
|
+
*/
|
|
35
|
+
hiddenToolbar = input(false, { transform: (value) => value ?? false });
|
|
36
|
+
/**
|
|
37
|
+
* Template tự vẽ thanh công cụ, thay cho bộ nút mặc định.
|
|
38
|
+
*
|
|
39
|
+
* Nhận context `{ $implicit: node, isContainer, canAddInside, canToggleType }` để nơi dùng vẽ đúng
|
|
40
|
+
* nút cho từng khối, rồi gọi ngược qua `outRemoveNode` · `outAddBlockInside` · `outToggleType`
|
|
41
|
+
* bằng chính các hàm trong context. Truyền cả `hiddenToolbar` và `templateToolbar` thì
|
|
42
|
+
* `hiddenToolbar` thắng — không vẽ gì.
|
|
43
|
+
*/
|
|
44
|
+
templateToolbar = input(undefined);
|
|
24
45
|
/**
|
|
25
46
|
* Bố cục vừa đổi do NGƯỜI DÙNG kéo hoặc đổi kích thước khối.
|
|
26
47
|
*
|
|
@@ -40,12 +61,71 @@ export class LibsUiGridLayoutCanvasComponent {
|
|
|
40
61
|
outAddBlockInside = output();
|
|
41
62
|
/** Người dùng bấm nút GỘP/TÁCH. Thư viện chưa làm gì — gọi `event.confirm()` để thực hiện. */
|
|
42
63
|
outToggleType = output();
|
|
64
|
+
/** Bật tính năng thu phóng (zoom) canvas. Chỉ áp dụng cho canvas gốc (depth 0). */
|
|
65
|
+
enableZoom = input(false, { transform: (value) => value ?? false });
|
|
66
|
+
/** Tỷ lệ thu phóng hiện tại (hỗ trợ two-way binding [(zoom)]="myZoom"). */
|
|
67
|
+
zoom = model(undefined);
|
|
68
|
+
/** Giới hạn thu phóng tối thiểu. Mặc định 0.4 (40%). */
|
|
69
|
+
zoomMin = input(0.4);
|
|
70
|
+
/** Giới hạn thu phóng tối đa. Mặc định 1.5 (150%). */
|
|
71
|
+
zoomMax = input(1.5);
|
|
72
|
+
/** Bước nhảy thu phóng mỗi lần bấm +/- hoặc lăn chuột. Mặc định 0.05 (5%). */
|
|
73
|
+
zoomStep = input(0.05);
|
|
74
|
+
/** Chiều rộng chuẩn của canvas stage tính bằng px. Mặc định 1668. */
|
|
75
|
+
stageWidth = input(1668);
|
|
76
|
+
/** Chiều cao tối thiểu của canvas stage tính bằng px. Mặc định 937. */
|
|
77
|
+
stageMinHeight = input(937);
|
|
78
|
+
/** Khóa lưu tỷ lệ thu phóng vào localStorage. Mặc định 'ocb_bi_canvas_zoom'. */
|
|
79
|
+
storageKey = input('ocb_bi_canvas_zoom');
|
|
80
|
+
/** Hiển thị thanh dock điều khiển thu phóng nổi ở góc dưới phải. Mặc định true. */
|
|
81
|
+
showZoomDock = input(true, { transform: (value) => value ?? true });
|
|
82
|
+
/** Tự động vừa màn hình khi tải trang lần đầu nếu khung nhìn nhỏ hơn stageWidth. Mặc định true. */
|
|
83
|
+
autoFitOnLoad = input(true, { transform: (value) => value ?? true });
|
|
84
|
+
/** Phát ra khi tỷ lệ thu phóng thay đổi. */
|
|
85
|
+
outZoomChange = output();
|
|
86
|
+
/** Bật tính năng kéo thả thẻ từ bên ngoài vào canvas (mặc định true) */
|
|
87
|
+
enableExternalDrop = input(true, { transform: (value) => value ?? true });
|
|
88
|
+
/** Luôn hiển thị đường kẻ lưới 24 cột */
|
|
89
|
+
showGridLines = input(false, { transform: (value) => value ?? false });
|
|
90
|
+
/** Mã id của khối đang được chọn */
|
|
91
|
+
selectedNodeId = model(undefined);
|
|
92
|
+
/** Phát ra khi thả một thẻ mới từ bên ngoài vào canvas */
|
|
93
|
+
outDropNode = output();
|
|
94
|
+
/** Phát ra khi người dùng click chọn một khối */
|
|
95
|
+
outSelectNode = output();
|
|
96
|
+
/** Phát ra khi người dùng bấm nút cấu hình trên thanh công cụ của khối */
|
|
97
|
+
outConfigNode = output();
|
|
43
98
|
gridsterRef = viewChild(GridsterComponent);
|
|
99
|
+
scrollAreaRef = viewChild('scrollArea');
|
|
100
|
+
zoomDockRef = viewChild('zoomDock');
|
|
44
101
|
/** mỗi ô một chỗ gắn riêng — cùng thứ tự với `children()` */
|
|
45
102
|
itemHosts = viewChildren('itemHost', { read: ViewContainerRef });
|
|
103
|
+
/** Khung xem trước vị trí thả thẻ từ bên ngoài (snapped ghost preview) */
|
|
104
|
+
dropGhost = signal(undefined);
|
|
105
|
+
activeHoveredEl;
|
|
106
|
+
rootGridsterEl;
|
|
107
|
+
/** Menu chọn preset thu phóng đang mở hay đóng */
|
|
108
|
+
isMenuOpen = signal(false);
|
|
109
|
+
/** Các mốc thu phóng định sẵn */
|
|
110
|
+
zoomPresets = [
|
|
111
|
+
{ label: 'i18n_zoom_preset_overview', value: 0.5 },
|
|
112
|
+
{ label: 'i18n_zoom_preset_small_laptop', value: 0.67 },
|
|
113
|
+
{ label: 'i18n_zoom_preset_laptop_1080p', value: 0.75 },
|
|
114
|
+
{ label: 'i18n_zoom_preset_screen_1080p', value: 0.85 },
|
|
115
|
+
{ label: 'i18n_zoom_preset_standard', value: 0.9 },
|
|
116
|
+
{ label: 'i18n_zoom_preset_original_size', value: 1 },
|
|
117
|
+
{ label: 'i18n_zoom_preset_large_screen', value: 1.1 },
|
|
118
|
+
{ label: 'i18n_zoom_preset_high_dpi_2k', value: 1.25 },
|
|
119
|
+
];
|
|
46
120
|
/** Sáu chấm của tay cầm kéo — chỉ để `@for` dựng đủ 6 thẻ, giá trị không dùng tới. */
|
|
47
121
|
dragBarDots = [0, 1, 2, 3, 4, 5];
|
|
48
122
|
service = inject(LibsUiGridLayoutService);
|
|
123
|
+
/** Chế độ thu phóng có đang hoạt động trên canvas này không (chỉ root canvas depth 0) */
|
|
124
|
+
isZoomActive = computed(() => {
|
|
125
|
+
return this.depth() === 0 && (this.enableZoom() || this.service.FunctionsControl.isZoomEnabled());
|
|
126
|
+
});
|
|
127
|
+
currentZoom = computed(() => this.service.Zoom());
|
|
128
|
+
zoomPercent = computed(() => Math.round(this.currentZoom() * 100));
|
|
49
129
|
/**
|
|
50
130
|
* Node THẬT của mặt phẳng này.
|
|
51
131
|
*
|
|
@@ -65,6 +145,21 @@ export class LibsUiGridLayoutCanvasComponent {
|
|
|
65
145
|
return this.service.Root() ?? emptyRootNode();
|
|
66
146
|
});
|
|
67
147
|
children = computed(() => this.currentNode().children ?? []);
|
|
148
|
+
/**
|
|
149
|
+
* Bối cảnh cho `templateToolbar`, dựng sẵn theo từng khối.
|
|
150
|
+
*
|
|
151
|
+
* Dựng ở `computed` chứ không gọi hàm trên template: gọi hàm trong template chạy lại mỗi vòng
|
|
152
|
+
* dò thay đổi (rule `fe-coding-convention-templates` mục 6).
|
|
153
|
+
*/
|
|
154
|
+
toolbarContexts = computed(() => this.children().map((item) => ({
|
|
155
|
+
$implicit: item,
|
|
156
|
+
isContainer: this.isContainer(item),
|
|
157
|
+
canAddInside: this.canAddInside(item),
|
|
158
|
+
canToggleType: this.canToggleType(item),
|
|
159
|
+
remove: () => this.outRemoveNode.emit({ nodeId: item.id, node: item, confirm: () => this.service.removeBlock(item.id) }),
|
|
160
|
+
addInside: () => this.emitAddInside(item),
|
|
161
|
+
toggleType: () => this.emitToggleType(item),
|
|
162
|
+
})));
|
|
68
163
|
isNestedCanvas = computed(() => this.depth() > 0);
|
|
69
164
|
/** trần lồng container — khối đã chạm trần thì không cho chuyển thành container */
|
|
70
165
|
maxContainerDepth = computed(() => this.service.FunctionsControl.maxContainerDepth());
|
|
@@ -102,8 +197,39 @@ export class LibsUiGridLayoutCanvasComponent {
|
|
|
102
197
|
this.service.FunctionsControl.attachNode(item, this.depth() + 1, host);
|
|
103
198
|
});
|
|
104
199
|
});
|
|
200
|
+
// Đồng bộ nếu model zoom bên ngoài thay đổi
|
|
201
|
+
effect(() => {
|
|
202
|
+
const z = this.zoom();
|
|
203
|
+
if (z !== undefined && Math.abs(z - this.service.Zoom()) > 0.001) {
|
|
204
|
+
this.service.FunctionsControl.setZoom(z, this.storageKey());
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
ngAfterViewInit() {
|
|
209
|
+
if (!this.isZoomActive() || !this.autoFitOnLoad()) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const key = this.storageKey();
|
|
213
|
+
let hasSaved = false;
|
|
214
|
+
if (key && typeof localStorage !== 'undefined') {
|
|
215
|
+
try {
|
|
216
|
+
hasSaved = Boolean(localStorage.getItem(key));
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
// ignore localStorage access error
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (!hasSaved) {
|
|
223
|
+
setTimeout(() => {
|
|
224
|
+
const scrollEl = this.scrollAreaRef()?.nativeElement;
|
|
225
|
+
if (scrollEl && scrollEl.clientWidth > 0 && scrollEl.clientWidth < this.stageWidth() + 64) {
|
|
226
|
+
this.handlerFitScreen();
|
|
227
|
+
}
|
|
228
|
+
}, 50);
|
|
229
|
+
}
|
|
105
230
|
}
|
|
106
231
|
ngOnDestroy() {
|
|
232
|
+
this.cleanupDragGrid();
|
|
107
233
|
for (const item of this.children()) {
|
|
108
234
|
this.service.FunctionsControl.detachNode(item.id);
|
|
109
235
|
}
|
|
@@ -112,8 +238,98 @@ export class LibsUiGridLayoutCanvasComponent {
|
|
|
112
238
|
get FunctionControl() {
|
|
113
239
|
return {
|
|
114
240
|
veLaiLuoi: () => this.gridsterRef()?.optionsChanged(),
|
|
241
|
+
zoomIn: () => this.handlerZoomIn(),
|
|
242
|
+
zoomOut: () => this.handlerZoomOut(),
|
|
243
|
+
resetZoom: () => this.handlerResetZoom(),
|
|
244
|
+
fitToScreen: () => this.handlerFitScreen(),
|
|
245
|
+
setZoom: (val) => {
|
|
246
|
+
this.service.FunctionsControl.setZoom(val, this.storageKey());
|
|
247
|
+
this.syncZoomOut();
|
|
248
|
+
},
|
|
249
|
+
// Vùng cuộn của mặt phẳng — nơi dùng cần nó để tự cuộn tới một khối, hoặc đo toạ độ khi kéo.
|
|
250
|
+
// Không mở ở đây thì nơi dùng phải dò bằng tên lớp CSS nội bộ, và tên đó đổi lúc nào không hay.
|
|
251
|
+
scrollArea: () => this.scrollAreaRef()?.nativeElement,
|
|
115
252
|
};
|
|
116
253
|
}
|
|
254
|
+
handlerZoomIn() {
|
|
255
|
+
this.service.FunctionsControl.zoomIn(this.zoomStep(), this.storageKey());
|
|
256
|
+
this.syncZoomOut();
|
|
257
|
+
}
|
|
258
|
+
handlerZoomOut() {
|
|
259
|
+
this.service.FunctionsControl.zoomOut(this.zoomStep(), this.storageKey());
|
|
260
|
+
this.syncZoomOut();
|
|
261
|
+
}
|
|
262
|
+
handlerResetZoom() {
|
|
263
|
+
this.service.FunctionsControl.resetZoom(this.storageKey());
|
|
264
|
+
this.syncZoomOut();
|
|
265
|
+
}
|
|
266
|
+
handlerFitScreen() {
|
|
267
|
+
this.isMenuOpen.set(false);
|
|
268
|
+
const scrollEl = this.scrollAreaRef()?.nativeElement;
|
|
269
|
+
const containerWidth = scrollEl?.clientWidth || (typeof window !== 'undefined' ? window.innerWidth - 300 : 1200);
|
|
270
|
+
this.service.FunctionsControl.fitToScreen(containerWidth, this.stageWidth(), 48, this.storageKey());
|
|
271
|
+
this.syncZoomOut();
|
|
272
|
+
}
|
|
273
|
+
handlerToggleMenu() {
|
|
274
|
+
this.isMenuOpen.update((open) => !open);
|
|
275
|
+
}
|
|
276
|
+
handlerSelectPreset(value) {
|
|
277
|
+
this.isMenuOpen.set(false);
|
|
278
|
+
this.service.FunctionsControl.setZoom(value, this.storageKey());
|
|
279
|
+
this.syncZoomOut();
|
|
280
|
+
}
|
|
281
|
+
isCurrentPreset(value) {
|
|
282
|
+
return Math.abs(this.currentZoom() - value) < 0.01;
|
|
283
|
+
}
|
|
284
|
+
syncZoomOut() {
|
|
285
|
+
const val = this.service.Zoom();
|
|
286
|
+
this.outZoomChange.emit(val);
|
|
287
|
+
if (this.zoom() !== val) {
|
|
288
|
+
this.zoom.set(val);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
handlerWheel(event) {
|
|
292
|
+
if (!this.isZoomActive()) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (event.ctrlKey || event.metaKey) {
|
|
296
|
+
event.preventDefault();
|
|
297
|
+
if (event.deltaY < 0) {
|
|
298
|
+
this.handlerZoomIn();
|
|
299
|
+
}
|
|
300
|
+
else if (event.deltaY > 0) {
|
|
301
|
+
this.handlerZoomOut();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
handlerDocumentKeyDown(event) {
|
|
306
|
+
if (!this.isZoomActive()) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (event.ctrlKey || event.metaKey) {
|
|
310
|
+
if (event.key === '=' || event.key === '+') {
|
|
311
|
+
event.preventDefault();
|
|
312
|
+
this.handlerZoomIn();
|
|
313
|
+
}
|
|
314
|
+
else if (event.key === '-' || event.key === '_') {
|
|
315
|
+
event.preventDefault();
|
|
316
|
+
this.handlerZoomOut();
|
|
317
|
+
}
|
|
318
|
+
else if (event.key === '0') {
|
|
319
|
+
event.preventDefault();
|
|
320
|
+
this.handlerResetZoom();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
handlerDocumentClick(event) {
|
|
325
|
+
if (!this.isMenuOpen()) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const dockEl = this.zoomDockRef()?.nativeElement;
|
|
329
|
+
if (dockEl && !dockEl.contains(event.target)) {
|
|
330
|
+
this.isMenuOpen.set(false);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
117
333
|
handlerTrackNode(_index, item) {
|
|
118
334
|
return item.id;
|
|
119
335
|
}
|
|
@@ -140,15 +356,32 @@ export class LibsUiGridLayoutCanvasComponent {
|
|
|
140
356
|
handlerAddInside(event, node) {
|
|
141
357
|
event.stopPropagation();
|
|
142
358
|
event.preventDefault();
|
|
359
|
+
this.emitAddInside(node);
|
|
360
|
+
}
|
|
361
|
+
handlerToggleType(event, node) {
|
|
362
|
+
event.stopPropagation();
|
|
363
|
+
event.preventDefault();
|
|
364
|
+
this.emitToggleType(node);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Chặn cú bấm trong thanh công cụ nổi lên gridster.
|
|
368
|
+
*
|
|
369
|
+
* Chỉ `stopPropagation`, KHÔNG `preventDefault`: nút bên trong template của nơi dùng vẫn cần
|
|
370
|
+
* hành vi mặc định của mình (mở dropdown, focus…).
|
|
371
|
+
*/
|
|
372
|
+
handlerStopEvent(event) {
|
|
373
|
+
event.stopPropagation();
|
|
374
|
+
}
|
|
375
|
+
/** Phát `outAddBlockInside` — dùng chung cho nút mặc định và `templateToolbar`. */
|
|
376
|
+
emitAddInside(node) {
|
|
143
377
|
this.outAddBlockInside.emit({
|
|
144
378
|
nodeId: node.id,
|
|
145
379
|
node,
|
|
146
380
|
confirm: (item) => this.service.addBlock(item, node.id),
|
|
147
381
|
});
|
|
148
382
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
event.preventDefault();
|
|
383
|
+
/** Phát `outToggleType` — dùng chung cho nút mặc định và `templateToolbar`. */
|
|
384
|
+
emitToggleType(node) {
|
|
152
385
|
this.outToggleType.emit({
|
|
153
386
|
nodeId: node.id,
|
|
154
387
|
node,
|
|
@@ -183,11 +416,157 @@ export class LibsUiGridLayoutCanvasComponent {
|
|
|
183
416
|
}
|
|
184
417
|
event.stopPropagation();
|
|
185
418
|
}
|
|
419
|
+
handlerSelectNode(node, event) {
|
|
420
|
+
if (event) {
|
|
421
|
+
const target = event.target;
|
|
422
|
+
if (target.closest('.libs-ui-grid-layout-toolbar') || target.closest('button')) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
this.selectedNodeId.set(node.id);
|
|
427
|
+
this.outSelectNode.emit(node);
|
|
428
|
+
}
|
|
429
|
+
handlerConfig(event, node) {
|
|
430
|
+
event.stopPropagation();
|
|
431
|
+
const actionEvent = {
|
|
432
|
+
nodeId: node.id,
|
|
433
|
+
node,
|
|
434
|
+
confirm: () => true,
|
|
435
|
+
};
|
|
436
|
+
this.outConfigNode.emit(actionEvent);
|
|
437
|
+
}
|
|
438
|
+
handlerDragOver(event) {
|
|
439
|
+
if (!this.enableExternalDrop() || this.depth() > 0) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
event.preventDefault();
|
|
443
|
+
if (event.dataTransfer) {
|
|
444
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
445
|
+
}
|
|
446
|
+
const container = this.scrollAreaRef()?.nativeElement;
|
|
447
|
+
const root = this.service.Root();
|
|
448
|
+
const payload = parseDropPayload(this.service.getDragPayload(), event.dataTransfer);
|
|
449
|
+
if (!container || !root || !payload) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const rootGridster = container.querySelector('gridster');
|
|
453
|
+
if (rootGridster) {
|
|
454
|
+
if (!rootGridster.classList.contains('display-grid')) {
|
|
455
|
+
rootGridster.classList.add('display-grid');
|
|
456
|
+
}
|
|
457
|
+
this.rootGridsterEl = rootGridster;
|
|
458
|
+
}
|
|
459
|
+
const ghost = calculateDropGhost(event, container, root, payload, this.service.totalColumns);
|
|
460
|
+
if (ghost?.isInsideGroup && ghost.hoveredGridster) {
|
|
461
|
+
if (this.activeHoveredEl !== ghost.hoveredGridster) {
|
|
462
|
+
this.cleanupHoveredGroup();
|
|
463
|
+
ghost.hoveredGridster.classList.add('display-grid');
|
|
464
|
+
this.activeHoveredEl = ghost.hoveredGridster;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
this.cleanupHoveredGroup();
|
|
469
|
+
}
|
|
470
|
+
this.dropGhost.set(ghost);
|
|
471
|
+
}
|
|
472
|
+
handlerDragLeave(event) {
|
|
473
|
+
if (this.depth() > 0) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (event.relatedTarget) {
|
|
477
|
+
const container = this.scrollAreaRef()?.nativeElement;
|
|
478
|
+
if (container && container.contains(event.relatedTarget)) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
this.dropGhost.set(undefined);
|
|
483
|
+
this.cleanupDragGrid();
|
|
484
|
+
}
|
|
485
|
+
handlerDrop(event) {
|
|
486
|
+
if (!this.enableExternalDrop() || this.depth() > 0) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
event.preventDefault();
|
|
490
|
+
const ghost = this.dropGhost();
|
|
491
|
+
const payload = parseDropPayload(this.service.getDragPayload(), event.dataTransfer);
|
|
492
|
+
this.dropGhost.set(undefined);
|
|
493
|
+
this.cleanupDragGrid();
|
|
494
|
+
this.service.clearDragPayload();
|
|
495
|
+
if (!ghost || !payload) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const targetParentId = ghost.isInsideGroup ? ghost.targetParentId : undefined;
|
|
499
|
+
const isInside = Boolean(targetParentId);
|
|
500
|
+
let itemToAdd;
|
|
501
|
+
const payloadAny = payload;
|
|
502
|
+
if (typeof payloadAny['resolveNode'] === 'function') {
|
|
503
|
+
const resolved = payloadAny['resolveNode'](isInside);
|
|
504
|
+
itemToAdd = resolved ?? {
|
|
505
|
+
cols: ghost.cols,
|
|
506
|
+
rows: ghost.rows,
|
|
507
|
+
data: payload.data ?? (payload.type ? { type: payload.type, title: payload.title } : undefined),
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
const newNodeData = payload.data ?? (payload.type ? { type: payload.type, title: payload.title } : undefined);
|
|
512
|
+
itemToAdd = {
|
|
513
|
+
cols: ghost.cols,
|
|
514
|
+
rows: ghost.rows,
|
|
515
|
+
data: newNodeData,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
const addedNode = this.service.insertBlockWithCollision(itemToAdd, ghost.col, ghost.row, targetParentId);
|
|
519
|
+
if (addedNode) {
|
|
520
|
+
this.selectedNodeId.set(addedNode.id);
|
|
521
|
+
this.outDropNode.emit({
|
|
522
|
+
node: addedNode,
|
|
523
|
+
parentId: targetParentId,
|
|
524
|
+
col: ghost.col,
|
|
525
|
+
row: ghost.row,
|
|
526
|
+
cols: ghost.cols,
|
|
527
|
+
rows: ghost.rows,
|
|
528
|
+
rawEvent: event,
|
|
529
|
+
payload,
|
|
530
|
+
});
|
|
531
|
+
const root = this.service.Root();
|
|
532
|
+
if (root) {
|
|
533
|
+
this.outChange.emit({
|
|
534
|
+
root,
|
|
535
|
+
nodeId: addedNode.id,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
cleanupHoveredGroup() {
|
|
541
|
+
if (this.activeHoveredEl) {
|
|
542
|
+
this.activeHoveredEl.classList.remove('display-grid');
|
|
543
|
+
this.activeHoveredEl = undefined;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
cleanupDragGrid() {
|
|
547
|
+
this.cleanupHoveredGroup();
|
|
548
|
+
if (this.rootGridsterEl && !this.showGridLines()) {
|
|
549
|
+
this.rootGridsterEl.classList.remove('display-grid');
|
|
550
|
+
this.rootGridsterEl = undefined;
|
|
551
|
+
}
|
|
552
|
+
const container = this.scrollAreaRef()?.nativeElement;
|
|
553
|
+
if (container && !this.showGridLines()) {
|
|
554
|
+
container.querySelectorAll('gridster.display-grid').forEach((el) => {
|
|
555
|
+
el.classList.remove('display-grid');
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
186
559
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiGridLayoutCanvasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
187
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LibsUiGridLayoutCanvasComponent, isStandalone: true, selector: "libs_ui-services-grid_layout-canvas", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: false, transformFunction: null }, depth: { classPropertyName: "depth", publicName: "depth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { outChange: "outChange", outRemoveNode: "outRemoveNode", outAddBlockInside: "outAddBlockInside", outToggleType: "outToggleType" }, viewQueries: [{ propertyName: "gridsterRef", first: true, predicate: GridsterComponent, descendants: true, isSignal: true }, { propertyName: "itemHosts", predicate: ["itemHost"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "<!-- \uD83D\uDD34 `relative` l\u00E0 B\u1EAET BU\u1ED8C, C\u1EA4M b\u1ECF.\n gridster \u0111\u1ED5i v\u1ECB tr\u00ED chu\u1ED9t th\u00E0nh \u00F4 l\u01B0\u1EDBi b\u1EB1ng `e.clientY + (el.scrollTop - el.offsetTop)` \u2014 c\u00F4ng\n th\u1EE9c \u0111\u00F3 ch\u1EC9 \u0111\u00FAng khi `offsetParent` c\u1EE7a <gridster> CH\u00CDNH L\u00C0 kh\u1ED1i cu\u1ED9n n\u00E0y. Thi\u1EBFu `relative` th\u00EC\n `offsetParent` nh\u1EA3y l\u00EAn kh\u1ED1i bao ngo\u00E0i: `offsetTop` tr\u1EA3 16 trong khi l\u1EC7ch th\u1EADt l\u00E0 65 \u2192 gridster\n t\u00EDnh \u00F4 sai 49px, \u00F4 ch\u1EDD \u0111\u1EB7t hi\u1EC7n l\u1EC7ch kh\u1ECFi con tr\u1ECF n\u00EAn k\u00E9o th\u1EA3 kh\u00F4ng \u0111\u1ED5i \u0111\u01B0\u1EE3c v\u1ECB tr\u00ED, v\u00E0 k\u00E9o m\u00E9p\n c\u0169ng t\u00EDnh sai \u0111i\u1EC3m d\u1EEBng. -->\n<div\n class=\"libs-ui-grid-layout-scroll-area relative h-full min-h-0 w-full min-w-0\"\n [class.overflow-y-auto]=\"!isNestedCanvas()\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\">\n <gridster class=\"h-full w-full\" [options]=\"gridOptions()\">\n @for (item of children(); track item.id) {\n <!-- \uD83D\uDD34 Ch\u1EB7n `mousedown` t\u1EA1i \u0110\u00C2Y cho l\u01B0\u1EDBi l\u1ED3ng \u2014 C\u1EA4M b\u1ECF v\u00E0 C\u1EA4M chuy\u1EC3n sang ch\u1ED7 kh\u00E1c.\n `ignoreContentClass` m\u1ED9t m\u00ECnh KH\u00D4NG \u0111\u1EE7: `delayStart: 160` l\u00E0m M\u1ED6I l\u01B0\u1EDBi l\u00EAn l\u1ECBch\n `dragStart` b\u1EB1ng `setTimeout` ngay t\u1EA1i `mousedown`, c\u00F2n `stopPropagation` m\u00E0 lib g\u1ECDi b\u00EAn\n trong `dragStart` ch\u1EA1y sau 160ms n\u00EAn qu\u00E1 mu\u1ED9n \u2014 l\u01B0\u1EDBi CHA \u0111\u00E3 k\u1ECBp nh\u1EADn (\u0111o 28/08/2026: k\u00E9o\n d\u1EA3i kh\u1ED1i con, chu\u1ED7i class duy\u1EC7t l\u00EAn \u0110\u00DANG m\u00E0 container v\u1EABn nh\u1EA3y 0,0 \u2192 0,1).\n G\u1EAFn \u1EDF `gridster-item` l\u00E0 \u0111\u00FAng bi\u00EAn: l\u01B0\u1EDBi con nghe tr\u00EAn ch\u00EDnh ph\u1EA7n t\u1EED n\u00E0y n\u00EAn \u0111\u00E3 nh\u1EADn xong,\n l\u01B0\u1EDBi cha \u1EDF ngo\u00E0i b\u1ECB ch\u1EB7n. -->\n <gridster-item\n [item]=\"item\"\n (mousedown)=\"handlerBlockParentDrag($event)\">\n <!-- \uD83D\uDD34 V\u1ECF n\u00E0y mang `dragHandleClass` \u2014 C\u1EA4M b\u1ECF.\n `ignoreContent: true` b\u1EAFt gridster CH\u1EC8 cho k\u00E9o khi ch\u1ED7 b\u1EA5m n\u1EB1m d\u01B0\u1EDBi m\u1ED9t ph\u1EA7n t\u1EED c\u00F3\n `dragHandleClass`. Kh\u00F4ng c\u00F3 v\u1ECF n\u00E0y th\u00EC component c\u1EE7a n\u01A1i d\u00F9ng (th\u01B0 vi\u1EC7n kh\u00F4ng ki\u1EC3m so\u00E1t\n \u0111\u01B0\u1EE3c class c\u1EE7a n\u00F3) s\u1EBD kh\u00F4ng c\u00F3 tay c\u1EA7m n\u00E0o, v\u00E0 KH\u00D4NG kh\u1ED1i n\u00E0o k\u00E9o \u0111\u01B0\u1EE3c \u2014 \u0111o 28/08/2026:\n k\u00E9o 320px, to\u1EA1 \u0111\u1ED9 kh\u1ED1i gi\u1EEF nguy\u00EAn x=3,y=0.\n T\u00EAn class theo c\u1EA5p: l\u01B0\u1EDBi trang v\u00E0 l\u01B0\u1EDBi l\u1ED3ng d\u00F9ng hai t\u00EAn KH\u00C1C nhau, n\u1EBFu kh\u00F4ng l\u01B0\u1EDBi cha\n c\u0169ng nh\u1EADn ra tay c\u1EA7m c\u1EE7a kh\u1ED1i con v\u00E0 nh\u1EA5c lu\u00F4n container. -->\n <!-- \uD83D\uDD34 V\u1ECF KH\u00D4NG mang `dragHandleClass` \u2014 C\u1EA4M th\u00EAm l\u1EA1i.\n K\u00E9o ch\u1EC9 \u0111\u01B0\u1EE3c ph\u00E9p b\u1EAFt \u0111\u1EA7u t\u1EEB D\u1EA2I XANH (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026: \"ph\u1EA3i hover v\u00E0o v\u00F9ng\n m\u00E0u xanh th\u00EC m\u1EDBi k\u00E9o th\u1EA3 \u0111\u01B0\u1EE3c cho \u0111\u1ED3ng nh\u1EA5t\"). Cho c\u1EA3 v\u1ECF l\u00E0m tay c\u1EA7m th\u00EC b\u1EA5m \u0111\u00E2u c\u0169ng\n k\u00E9o, v\u00E0 kh\u1ED1i con n\u1EB1m trong container s\u1EBD nh\u1EA5c lu\u00F4n container v\u00EC l\u01B0\u1EDBi cha duy\u1EC7t l\u00EAn g\u1EB7p\n tay c\u1EA7m c\u1EE7a v\u1ECF container tr\u01B0\u1EDBc.\n V\u1ECF kh\u1ED1i con v\u1EABn gi\u1EEF class CH\u1EB6N \u0111\u1EC3 l\u01B0\u1EDBi cha d\u1EEBng \u0111\u00FAng \u1EDF bi\u00EAn. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas()\">\n <!-- D\u1EA3i k\u00E9o: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i, ba ch\u1EA5m l\u00E0 d\u1EA5u hi\u1EC7u k\u00E9o quen thu\u1ED9c.\n Ng\u01B0\u1EDDi d\u00F9ng c\u1EA7n m\u1ED9t ch\u1ED7 b\u00E1m R\u00D5 R\u00C0NG thay v\u00EC \u0111o\u00E1n xem b\u1EA5m \u0111\u00E2u th\u00EC k\u00E9o \u0111\u01B0\u1EE3c\n (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng d\u1EA3i \u0111ang d\u00F9ng \u1EDF m\u00E0n Customer 360 c\u1EE7a mobio-web). -->\n @if (isEditMode()) {\n <!-- \uD83D\uDD34 D\u1EA3i PH\u1EA2I mang tay c\u1EA7m \u0110\u00DANG C\u1EA4P c\u1EE7a n\u00F3. D\u1EA3i nh\u1EADn chu\u1ED9t (`pointer-events: auto`),\n n\u00EAn n\u1EBFu kh\u00F4ng mang tay c\u1EA7m th\u00EC `checkDragHandleClass` duy\u1EC7t l\u00EAn g\u1EB7p tay c\u1EA7m c\u1EE7a\n container v\u00E0 nh\u1EA5c container thay v\u00EC kh\u1ED1i con (\u0111o 28/08/2026: k\u00E9o d\u1EA3i kh\u1ED1i con,\n container nh\u1EA3y 0,0 \u2192 0,1 c\u00F2n kh\u1ED1i con \u0111\u1EE9ng im).\n \uD83D\uDD34 D\u1EA3i CH\u1EC8 mang tay c\u1EA7m, C\u1EA4M mang th\u00EAm class ch\u1EB7n: `checkDragHandleClass` x\u00E9t hai\n class \u0111\u00F3 tr\u00EAn C\u00D9NG m\u1ED9t node n\u00EAn \u0111\u1EC3 chung l\u00E0 k\u00E9o h\u1ECFng (\u0111o 28/08/2026: b\u1EA5m d\u1EA3i kh\u1ED1i\n con k\u00E9o 140px, kh\u1ED1i \u0111\u1EE9ng im). Vi\u1EC7c ch\u1EB7n l\u01B0\u1EDBi cha do V\u1ECE KH\u1ED0I lo. -->\n <div\n class=\"libs-ui-grid-layout-drag-bar\"\n [class.libs-ui-grid-layout-drag-bar-child]=\"isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle]=\"!isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-drag-bar-dots\">\n @for (dot of dragBarDots; track dot) {\n <span class=\"libs-ui-grid-layout-drag-bar-dot\"></span>\n }\n </span>\n </div>\n }\n\n <!-- Khung ch\u1EE9a component c\u1EE7a n\u01A1i d\u00F9ng.\n \uD83D\uDD34 PH\u1EA2I c\u00F3 khung th\u1EADt (kh\u00F4ng ph\u1EA3i `ng-container` tr\u1ED1ng): n\u00F3 lo h\u1ED9 n\u01A1i d\u00F9ng ph\u1EA7n l\u1EA5p \u0111\u1EA7y\n \u00F4 v\u00E0 vi\u1EC1n b\u00E1o ch\u1EBF \u0111\u1ED9 s\u1EEDa. Kh\u00F4ng c\u00F3 khung th\u00EC m\u1ED7i component n\u1ED9i dung l\u1EA1i ph\u1EA3i t\u1EF1 vi\u1EBFt\n `:host { width/height: 100% }` + vi\u1EC1n \u0111\u1EE9t \u2014 \u0111\u00F3 l\u00E0 vi\u1EC7c c\u1EE7a th\u01B0 vi\u1EC7n, kh\u00F4ng ph\u1EA3i c\u1EE7a\n ng\u01B0\u1EDDi d\u00F9ng (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026).\n M\u1ED7i kh\u1ED1i m\u1ED9t ViewContainerRef ri\u00EAng n\u00EAn component n\u1EB1m \u0111\u00FAng \u00F4 c\u1EE7a n\u00F3. -->\n <div\n class=\"libs-ui-grid-layout-content-frame\"\n [class.libs-ui-grid-layout-content-frame-edit]=\"isEditMode() && !isContainer(item)\"\n [class.libs-ui-grid-layout-content-frame-empty]=\"isContainer(item)\">\n <ng-container #itemHost />\n </div>\n\n <!-- D\u1EA5u hi\u1EC7u CONTAINER: nh\u00E3n g\u00F3c tr\u00EAn-ph\u1EA3i + vi\u1EC1n \u0111\u1EE9t bao ru\u1ED9t. Kh\u00F4ng c\u00F3 d\u1EA5u hi\u1EC7u th\u00EC\n ng\u01B0\u1EDDi d\u00F9ng kh\u00F4ng bi\u1EBFt kh\u1ED1i n\u00E0o ch\u1EE9a \u0111\u01B0\u1EE3c kh\u1ED1i kh\u00E1c (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng c\u00E1ch\n m\u00E0n Customer 360 c\u1EE7a mobio-web \u0111ang l\u00E0m). -->\n @if (isEditMode() && isContainer(item)) {\n <div class=\"libs-ui-grid-layout-container-label\">Kh\u1ED1i gh\u00E9p</div>\n <div class=\"libs-ui-grid-layout-container-border\"></div>\n }\n\n <!-- Thanh c\u00F4ng c\u1EE5: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i.\n \uD83D\uDD34 D\u00F9ng `libs_ui-components-buttons-button` \u2014 C\u1EA4M t\u1EF1 d\u1EF1ng th\u1EBB `<button>`.\n Component n\u00E0y \u0111\u00E3 c\u00F3 s\u1EB5n ki\u1EC3u n\u00FAt, c\u1EE1 n\u00FAt v\u00E0 bong b\u00F3ng ch\u00FA th\u00EDch theo \u0111\u00FAng b\u1ED9 giao di\u1EC7n\n c\u1EE7a s\u1EA3n ph\u1EA9m; t\u1EF1 d\u1EF1ng l\u00E0 m\u1ED7i n\u01A1i m\u1ED9t ki\u1EC3u v\u00E0 m\u1EA5t lu\u00F4n tooltip. -->\n @if (isEditMode()) {\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\">\n @if (canAddInside(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-add'\"\n [popover]=\"{ config: { content: 'Th\u00EAm kh\u1ED1i v\u00E0o trong', zIndex: 1300 } }\"\n (outClick)=\"handlerAddInside($event, item)\" />\n }\n @if (canToggleType(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"isContainer(item) ? 'libs-ui-icon-split-cell' : 'libs-ui-icon-merge-cell'\"\n [popover]=\"{ config: { content: isContainer(item) ? '\u0110\u01B0a v\u1EC1 kh\u1ED1i \u0111\u01A1n' : 'Chuy\u1EC3n th\u00E0nh kh\u1ED1i gh\u00E9p', zIndex: 1300 } }\"\n (outClick)=\"handlerToggleType($event, item)\" />\n }\n <libs_ui-components-buttons-button\n [type]=\"'button-third-hover-danger'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-remove'\"\n [popover]=\"{ config: { content: isContainer(item) ? 'Xo\u00E1 c\u1EA3 kh\u1ED1i gh\u00E9p' : 'Xo\u00E1 kh\u1ED1i', zIndex: 1300 } }\"\n (outClick)=\"handlerRemove($event, item)\" />\n </div>\n }\n\n @if (item.children && item.children.length > 0) {\n <!-- Kh\u1ED1i c\u00F3 con \u2192 ru\u1ED9t n\u00F3 l\u1EA1i l\u00E0 m\u1ED9t m\u1EB7t ph\u1EB3ng n\u1EEFa. \u0110\u1EC7 quy \u1EDF \u0111\u00E2y, n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i lo.\n L\u1EC1 \u0111\u1EB7t \u1EDF \u0110\u00C2Y (v\u1ECF container), kh\u00F4ng \u0111\u1EB7t v\u00E0o l\u01B0\u1EDBi con \u2014 l\u01B0\u1EDBi con gi\u1EEF nguy\u00EAn khe 2px. -->\n <libs_ui-services-grid_layout-canvas\n class=\"block h-full w-full\"\n [style.padding]=\"containerPadding()\"\n [node]=\"item\"\n [depth]=\"depth() + 1\"\n (outChange)=\"outChange.emit($event)\"\n (outRemoveNode)=\"outRemoveNode.emit($event)\"\n (outAddBlockInside)=\"outAddBlockInside.emit($event)\"\n (outToggleType)=\"outToggleType.emit($event)\" />\n }\n </div>\n </gridster-item>\n }\n </gridster>\n</div>\n", styles: [":host{display:block;box-sizing:border-box;width:100%;height:100%;min-width:0;min-height:0}.libs-ui-grid-layout-block-shell{container-type:inline-size;display:block;box-sizing:border-box;height:100%}.libs-ui-grid-layout-drag-bar{position:absolute;top:2px;left:50%;z-index:15;display:flex;align-items:center;justify-content:center;width:64px;height:16px;background:transparent;transform:translate(-50%);opacity:1;transition:opacity .12s ease;pointer-events:auto;cursor:move}.libs-ui-grid-layout-drag-bar-dots{display:grid;grid-template-columns:repeat(3,3px);gap:3px;color:#9ca2ad}.libs-ui-grid-layout-drag-bar-dot{width:3px;height:3px;background:currentColor;border-radius:50%}.libs-ui-grid-layout-drag-bar:hover .libs-ui-grid-layout-drag-bar-dots{color:#3d6ef5}.libs-ui-grid-layout-drag-bar:not(.libs-ui-grid-layout-drag-bar-child){opacity:1}.libs-ui-grid-layout-drag-bar-child{top:auto;bottom:0;background:#d9f2e4;border-radius:4px 4px 0 0}.libs-ui-grid-layout-drag-bar-child .libs-ui-grid-layout-drag-bar-dots{color:#9ca2ad}.libs-ui-grid-layout-drag-bar-child:hover .libs-ui-grid-layout-drag-bar-dots{color:#00a757}.libs-ui-grid-layout-drag-bar-child{opacity:0}.libs-ui-grid-layout-block-shell:hover>.libs-ui-grid-layout-drag-bar-child{opacity:1}.libs-ui-grid-layout-block-shell:has(>.libs-ui-grid-layout-drag-bar:not(.libs-ui-grid-layout-drag-bar-child):hover){outline:2px solid #3d6ef5;outline-offset:-2px;border-radius:8px}.libs-ui-grid-layout-block-shell:has(>.libs-ui-grid-layout-drag-bar-child:hover){outline:2px solid #00a757;outline-offset:-2px;border-radius:8px}.libs-ui-grid-layout-container-label{position:absolute;left:6px;top:2px;display:flex;height:20px;align-items:center;z-index:3;padding:0 6px;color:#3d6ef5;font-weight:500;font-size:10px;line-height:14px;background:#dbe4ff;border-radius:4px;pointer-events:none}.libs-ui-grid-layout-content-frame{box-sizing:border-box;width:100%;height:100%;overflow:hidden;border-radius:6px}.libs-ui-grid-layout-content-frame ::ng-deep>*{display:block;box-sizing:border-box;width:100%;height:100%}.libs-ui-grid-layout-content-frame-empty{height:0}.libs-ui-grid-layout-content-frame-edit{border:1px dashed #c3cede}.libs-ui-grid-layout-container-border{position:absolute;inset:0;border:1px dashed #9db4f0;border-radius:8px;pointer-events:none}.libs-ui-grid-layout-toolbar{position:absolute;top:2px;right:8px;z-index:24;display:flex;gap:2px;align-items:center;height:20px;background:#fff;border-radius:4px;box-shadow:0 2px 8px #0716311f;opacity:0;transition:opacity .12s ease}.libs-ui-grid-layout-toolbar-child{top:50%;right:8px;left:auto;transform:translateY(-50%)}.libs-ui-grid-layout-block-shell:hover>.libs-ui-grid-layout-toolbar{opacity:1}:host ::ng-deep gridster{background:transparent}:host ::ng-deep .gridster-item-resizable-handler.handle-n,:host ::ng-deep .gridster-item-resizable-handler.handle-s{height:16px}:host ::ng-deep .gridster-item-resizable-handler.handle-e,:host ::ng-deep .gridster-item-resizable-handler.handle-w{width:16px}:host ::ng-deep .gridster-item-resizable-handler.handle-n{top:0}:host ::ng-deep .gridster-item-resizable-handler.handle-s{bottom:0}:host ::ng-deep .gridster-item-resizable-handler.handle-e{right:0}:host ::ng-deep .gridster-item-resizable-handler.handle-w{left:0}:host ::ng-deep gridster-item.gridster-item-moving,:host ::ng-deep gridster-item.gridster-item-resizing{z-index:30!important}:host ::ng-deep gridster-item.gridster-item-moving>.libs-ui-grid-layout-block-shell,:host ::ng-deep gridster-item.gridster-item-resizing>.libs-ui-grid-layout-block-shell{outline:2px solid #3d6ef5;outline-offset:-2px;border-radius:8px}:host ::ng-deep gridster gridster gridster-item.gridster-item-moving>.libs-ui-grid-layout-block-shell,:host ::ng-deep gridster gridster gridster-item.gridster-item-resizing>.libs-ui-grid-layout-block-shell{outline-color:#00a757}@container (max-width: 80px){.libs-ui-grid-layout-drag-bar{top:26px}}\n"], dependencies: [{ kind: "component", type: LibsUiGridLayoutCanvasComponent, selector: "libs_ui-services-grid_layout-canvas", inputs: ["node", "depth"], outputs: ["outChange", "outRemoveNode", "outAddBlockInside", "outToggleType"] }, { kind: "ngmodule", type: GridsterModule }, { kind: "component", type: i1.GridsterComponent, selector: "gridster", inputs: ["options"] }, { kind: "component", type: i1.GridsterItemComponent, selector: "gridster-item", inputs: ["item"], outputs: ["itemInit", "itemChange", "itemResize"] }, { 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
560
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LibsUiGridLayoutCanvasComponent, isStandalone: true, selector: "libs_ui-services-grid_layout-canvas", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: false, transformFunction: null }, depth: { classPropertyName: "depth", publicName: "depth", isSignal: true, isRequired: false, transformFunction: null }, hiddenToolbar: { classPropertyName: "hiddenToolbar", publicName: "hiddenToolbar", isSignal: true, isRequired: false, transformFunction: null }, templateToolbar: { classPropertyName: "templateToolbar", publicName: "templateToolbar", isSignal: true, isRequired: false, transformFunction: null }, enableZoom: { classPropertyName: "enableZoom", publicName: "enableZoom", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, zoomMin: { classPropertyName: "zoomMin", publicName: "zoomMin", isSignal: true, isRequired: false, transformFunction: null }, zoomMax: { classPropertyName: "zoomMax", publicName: "zoomMax", isSignal: true, isRequired: false, transformFunction: null }, zoomStep: { classPropertyName: "zoomStep", publicName: "zoomStep", isSignal: true, isRequired: false, transformFunction: null }, stageWidth: { classPropertyName: "stageWidth", publicName: "stageWidth", isSignal: true, isRequired: false, transformFunction: null }, stageMinHeight: { classPropertyName: "stageMinHeight", publicName: "stageMinHeight", isSignal: true, isRequired: false, transformFunction: null }, storageKey: { classPropertyName: "storageKey", publicName: "storageKey", isSignal: true, isRequired: false, transformFunction: null }, showZoomDock: { classPropertyName: "showZoomDock", publicName: "showZoomDock", isSignal: true, isRequired: false, transformFunction: null }, autoFitOnLoad: { classPropertyName: "autoFitOnLoad", publicName: "autoFitOnLoad", isSignal: true, isRequired: false, transformFunction: null }, enableExternalDrop: { classPropertyName: "enableExternalDrop", publicName: "enableExternalDrop", isSignal: true, isRequired: false, transformFunction: null }, showGridLines: { classPropertyName: "showGridLines", publicName: "showGridLines", isSignal: true, isRequired: false, transformFunction: null }, selectedNodeId: { classPropertyName: "selectedNodeId", publicName: "selectedNodeId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { outChange: "outChange", outRemoveNode: "outRemoveNode", outAddBlockInside: "outAddBlockInside", outToggleType: "outToggleType", zoom: "zoomChange", outZoomChange: "outZoomChange", selectedNodeId: "selectedNodeIdChange", outDropNode: "outDropNode", outSelectNode: "outSelectNode", outConfigNode: "outConfigNode" }, host: { listeners: { "document:keydown": "handlerDocumentKeyDown($event)", "document:click": "handlerDocumentClick($event)" } }, viewQueries: [{ propertyName: "gridsterRef", first: true, predicate: GridsterComponent, descendants: true, isSignal: true }, { propertyName: "scrollAreaRef", first: true, predicate: ["scrollArea"], descendants: true, isSignal: true }, { propertyName: "zoomDockRef", first: true, predicate: ["zoomDock"], descendants: true, isSignal: true }, { propertyName: "itemHosts", predicate: ["itemHost"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "@if (isZoomActive()) {\n <!-- Khu v\u1EF1c cu\u1ED9n ch\u1EE9a canvas stage thu ph\u00F3ng -->\n <div\n #scrollArea\n class=\"libs-ui-grid-layout-scroll-area relative h-full min-h-0 w-full min-w-0 overflow-auto p-6 flex justify-center items-start\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\"\n (wheel)=\"handlerWheel($event)\"\n (dragover)=\"handlerDragOver($event)\"\n (dragleave)=\"handlerDragLeave($event)\"\n (drop)=\"handlerDrop($event)\">\n <div\n #canvasStage\n id=\"bi-canvas-stage\"\n class=\"libs-ui-grid-layout-stage relative flex flex-col transition-[zoom] duration-150 origin-top shrink-0 bg-white/80 border border-slate-200/80 rounded-2xl shadow-sm p-4\"\n [style.zoom]=\"currentZoom()\"\n [style.width.px]=\"stageWidth()\"\n [style.min-width.px]=\"stageWidth()\"\n [style.min-height.px]=\"stageMinHeight()\">\n <!-- Snapped Drop Ghost Preview -->\n @if (dropGhost(); as ghost) {\n <div\n class=\"pointer-events-none absolute z-30 flex flex-col items-center justify-center overflow-hidden rounded-lg border-2 border-dashed border-blue-500 bg-blue-500/15 p-1 shadow-sm transition-all duration-75 ease-out backdrop-blur-[0.5px] select-none\"\n [style.left.px]=\"ghost.pixelLeft\"\n [style.top.px]=\"ghost.pixelTop\"\n [style.width.px]=\"ghost.pixelWidth\"\n [style.height.px]=\"ghost.pixelHeight\">\n <div class=\"inline-flex max-w-[95%] items-center gap-1 truncate rounded-md bg-blue-600 px-2 py-0.5 text-[11px] font-semibold text-white shadow-xs\">\n @if (ghost.cardIconClass) {\n <i [class]=\"ghost.cardIconClass + ' text-xs'\"></i>\n } @else if (ghost.cardIconText) {\n <span class=\"text-[10px] font-bold\">{{ ghost.cardIconText }}</span>\n } @else {\n <span class=\"text-xs font-bold\">{{ ghost.isInsideGroup ? '\u21B3' : '+' }}</span>\n }\n <span class=\"truncate\">{{ ghost.cardTitle }}</span>\n <span class=\"text-[9px] font-mono opacity-75\">({{ ghost.cols }}c)</span>\n </div>\n\n @if (ghost.pixelHeight >= 80) {\n <div class=\"mt-1 flex max-w-[95%] items-center gap-1.5 truncate rounded border border-blue-100 bg-white/95 px-2 py-0.5 text-[10px] font-medium text-blue-700 shadow-xs\">\n @if (ghost.isInsideGroup) {\n <span class=\"truncate font-bold text-blue-900\">\u21B3 {{ ghost.groupTitle }}</span>\n <span class=\"text-blue-300\">\u2022</span>\n }\n <span>C\u1ED9t {{ ghost.col + 1 }}-{{ ghost.col + ghost.cols }}</span>\n </div>\n }\n </div>\n }\n <gridster class=\"h-full w-full\" [class.display-grid]=\"showGridLines()\" [options]=\"gridOptions()\">\n @for (item of children(); track item.id; let itemIndex = $index) {\n <!-- \uD83D\uDD34 Ch\u1EB7n `mousedown` t\u1EA1i \u0110\u00C2Y cho l\u01B0\u1EDBi l\u1ED3ng \u2014 C\u1EA4M b\u1ECF v\u00E0 C\u1EA4M chuy\u1EC3n sang ch\u1ED7 kh\u00E1c.\n `ignoreContentClass` m\u1ED9t m\u00ECnh KH\u00D4NG \u0111\u1EE7: `delayStart: 160` l\u00E0m M\u1ED6I l\u01B0\u1EDBi l\u00EAn l\u1ECBch\n `dragStart` b\u1EB1ng `setTimeout` ngay t\u1EA1i `mousedown`, c\u00F2n `stopPropagation` m\u00E0 lib g\u1ECDi b\u00EAn\n trong `dragStart` ch\u1EA1y sau 160ms n\u00EAn qu\u00E1 mu\u1ED9n \u2014 l\u01B0\u1EDBi CHA \u0111\u00E3 k\u1ECBp nh\u1EADn (\u0111o 28/08/2026: k\u00E9o\n d\u1EA3i kh\u1ED1i con, chu\u1ED7i class duy\u1EC7t l\u00EAn \u0110\u00DANG m\u00E0 container v\u1EABn nh\u1EA3y 0,0 \u2192 0,1).\n G\u1EAFn \u1EDF `gridster-item` l\u00E0 \u0111\u00FAng bi\u00EAn: l\u01B0\u1EDBi con nghe tr\u00EAn ch\u00EDnh ph\u1EA7n t\u1EED n\u00E0y n\u00EAn \u0111\u00E3 nh\u1EADn xong,\n l\u01B0\u1EDBi cha \u1EDF ngo\u00E0i b\u1ECB ch\u1EB7n. -->\n <gridster-item\n [item]=\"item\"\n [class.libs-ui-grid-layout-selected]=\"selectedNodeId() === item.id\"\n (click)=\"handlerSelectNode(item, $event)\"\n (mousedown)=\"handlerBlockParentDrag($event)\">\n <!-- \uD83D\uDD34 V\u1ECF n\u00E0y mang `dragHandleClass` \u2014 C\u1EA4M b\u1ECF.\n `ignoreContent: true` b\u1EAFt gridster CH\u1EC8 cho k\u00E9o khi ch\u1ED7 b\u1EA5m n\u1EB1m d\u01B0\u1EDBi m\u1ED9t ph\u1EA7n t\u1EED c\u00F3\n `dragHandleClass`. Kh\u00F4ng c\u00F3 v\u1ECF n\u00E0y th\u00EC component c\u1EE7a n\u01A1i d\u00F9ng (th\u01B0 vi\u1EC7n kh\u00F4ng ki\u1EC3m so\u00E1t\n \u0111\u01B0\u1EE3c class c\u1EE7a n\u00F3) s\u1EBD kh\u00F4ng c\u00F3 tay c\u1EA7m n\u00E0o, v\u00E0 KH\u00D4NG kh\u1ED1i n\u00E0o k\u00E9o \u0111\u01B0\u1EE3c \u2014 \u0111o 28/08/2026:\n k\u00E9o 320px, to\u1EA1 \u0111\u1ED9 kh\u1ED1i gi\u1EEF nguy\u00EAn x=3,y=0.\n T\u00EAn class theo c\u1EA5p: l\u01B0\u1EDBi trang v\u00E0 l\u01B0\u1EDBi l\u1ED3ng d\u00F9ng hai t\u00EAn KH\u00C1C nhau, n\u1EBFu kh\u00F4ng l\u01B0\u1EDBi cha\n c\u0169ng nh\u1EADn ra tay c\u1EA7m c\u1EE7a kh\u1ED1i con v\u00E0 nh\u1EA5c lu\u00F4n container. -->\n <!-- \uD83D\uDD34 V\u1ECF KH\u00D4NG mang `dragHandleClass` \u2014 C\u1EA4M th\u00EAm l\u1EA1i.\n K\u00E9o ch\u1EC9 \u0111\u01B0\u1EE3c ph\u00E9p b\u1EAFt \u0111\u1EA7u t\u1EEB D\u1EA2I XANH (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026: \"ph\u1EA3i hover v\u00E0o v\u00F9ng\n m\u00E0u xanh th\u00EC m\u1EDBi k\u00E9o th\u1EA3 \u0111\u01B0\u1EE3c cho \u0111\u1ED3ng nh\u1EA5t\"). Cho c\u1EA3 v\u1ECF l\u00E0m tay c\u1EA7m th\u00EC b\u1EA5m \u0111\u00E2u c\u0169ng\n k\u00E9o, v\u00E0 kh\u1ED1i con n\u1EB1m trong container s\u1EBD nh\u1EA5c lu\u00F4n container v\u00EC l\u01B0\u1EDBi cha duy\u1EC7t l\u00EAn g\u1EB7p\n tay c\u1EA7m c\u1EE7a v\u1ECF container tr\u01B0\u1EDBc.\n V\u1ECF kh\u1ED1i con v\u1EABn gi\u1EEF class CH\u1EB6N \u0111\u1EC3 l\u01B0\u1EDBi cha d\u1EEBng \u0111\u00FAng \u1EDF bi\u00EAn. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas()\">\n <!-- D\u1EA3i k\u00E9o: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i, ba ch\u1EA5m l\u00E0 d\u1EA5u hi\u1EC7u k\u00E9o quen thu\u1ED9c.\n Ng\u01B0\u1EDDi d\u00F9ng c\u1EA7n m\u1ED9t ch\u1ED7 b\u00E1m R\u00D5 R\u00C0NG thay v\u00EC \u0111o\u00E1n xem b\u1EA5m \u0111\u00E2u th\u00EC k\u00E9o \u0111\u01B0\u1EE3c\n (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng d\u1EA3i \u0111ang d\u00F9ng \u1EDF m\u00E0n Customer 360 c\u1EE7a mobio-web). -->\n @if (isEditMode()) {\n <!-- \uD83D\uDD34 D\u1EA3i PH\u1EA2I mang tay c\u1EA7m \u0110\u00DANG C\u1EA4P c\u1EE7a n\u00F3. D\u1EA3i nh\u1EADn chu\u1ED9t (`pointer-events: auto`),\n n\u00EAn n\u1EBFu kh\u00F4ng mang tay c\u1EA7m th\u00EC `checkDragHandleClass` duy\u1EC7t l\u00EAn g\u1EB7p tay c\u1EA7m c\u1EE7a\n container v\u00E0 nh\u1EA5c container thay v\u00EC kh\u1ED1i con (\u0111o 28/08/2026: k\u00E9o d\u1EA3i kh\u1ED1i con,\n container nh\u1EA3y 0,0 \u2192 0,1 c\u00F2n kh\u1ED1i con \u0111\u1EE9ng im).\n \uD83D\uDD34 D\u1EA3i CH\u1EC8 mang tay c\u1EA7m, C\u1EA4M mang th\u00EAm class ch\u1EB7n: `checkDragHandleClass` x\u00E9t hai\n class \u0111\u00F3 tr\u00EAn C\u00D9NG m\u1ED9t node n\u00EAn \u0111\u1EC3 chung l\u00E0 k\u00E9o h\u1ECFng (\u0111o 28/08/2026: b\u1EA5m d\u1EA3i kh\u1ED1i\n con k\u00E9o 140px, kh\u1ED1i \u0111\u1EE9ng im). Vi\u1EC7c ch\u1EB7n l\u01B0\u1EDBi cha do V\u1ECE KH\u1ED0I lo. -->\n <div\n class=\"libs-ui-grid-layout-drag-bar\"\n [class.libs-ui-grid-layout-drag-bar-child]=\"isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle]=\"!isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-drag-bar-dots\">\n @for (dot of dragBarDots; track dot) {\n <span class=\"libs-ui-grid-layout-drag-bar-dot\"></span>\n }\n </span>\n </div>\n }\n\n <!-- Khung ch\u1EE9a component c\u1EE7a n\u01A1i d\u00F9ng.\n \uD83D\uDD34 PH\u1EA2I c\u00F3 khung th\u1EADt (kh\u00F4ng ph\u1EA3i `ng-container` tr\u1ED1ng): n\u00F3 lo h\u1ED9 n\u01A1i d\u00F9ng ph\u1EA7n l\u1EA5p \u0111\u1EA7y\n \u00F4 v\u00E0 vi\u1EC1n b\u00E1o ch\u1EBF \u0111\u1ED9 s\u1EEDa. Kh\u00F4ng c\u00F3 khung th\u00EC m\u1ED7i component n\u1ED9i dung l\u1EA1i ph\u1EA3i t\u1EF1 vi\u1EBFt\n `:host { width/height: 100% }` + vi\u1EC1n \u0111\u1EE9t \u2014 \u0111\u00F3 l\u00E0 vi\u1EC7c c\u1EE7a th\u01B0 vi\u1EC7n, kh\u00F4ng ph\u1EA3i c\u1EE7a\n ng\u01B0\u1EDDi d\u00F9ng (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026).\n M\u1ED7i kh\u1ED1i m\u1ED9t ViewContainerRef ri\u00EAng n\u00EAn component n\u1EB1m \u0111\u00FAng \u00F4 c\u1EE7a n\u00F3. -->\n <div\n class=\"libs-ui-grid-layout-content-frame\"\n [class.libs-ui-grid-layout-content-frame-edit]=\"isEditMode() && !isContainer(item)\"\n [class.libs-ui-grid-layout-content-frame-empty]=\"isContainer(item)\">\n <ng-container #itemHost />\n </div>\n\n <!-- D\u1EA5u hi\u1EC7u CONTAINER: nh\u00E3n g\u00F3c tr\u00EAn-ph\u1EA3i + vi\u1EC1n \u0111\u1EE9t bao ru\u1ED9t. Kh\u00F4ng c\u00F3 d\u1EA5u hi\u1EC7u th\u00EC\n ng\u01B0\u1EDDi d\u00F9ng kh\u00F4ng bi\u1EBFt kh\u1ED1i n\u00E0o ch\u1EE9a \u0111\u01B0\u1EE3c kh\u1ED1i kh\u00E1c (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng c\u00E1ch\n m\u00E0n Customer 360 c\u1EE7a mobio-web \u0111ang l\u00E0m). -->\n @if (isEditMode() && isContainer(item)) {\n <div class=\"libs-ui-grid-layout-container-label\">{{ 'i18n_container_block' | translate }}</div>\n <div class=\"libs-ui-grid-layout-container-border\"></div>\n }\n\n <!-- Thanh c\u00F4ng c\u1EE5: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i.\n \uD83D\uDD34 D\u00F9ng `libs_ui-components-buttons-button` \u2014 C\u1EA4M t\u1EF1 d\u1EF1ng th\u1EBB `<button>`.\n Component n\u00E0y \u0111\u00E3 c\u00F3 s\u1EB5n ki\u1EC3u n\u00FAt, c\u1EE1 n\u00FAt v\u00E0 bong b\u00F3ng ch\u00FA th\u00EDch theo \u0111\u00FAng b\u1ED9 giao di\u1EC7n\n c\u1EE7a s\u1EA3n ph\u1EA9m; t\u1EF1 d\u1EF1ng l\u00E0 m\u1ED7i n\u01A1i m\u1ED9t ki\u1EC3u v\u00E0 m\u1EA5t lu\u00F4n tooltip. -->\n @if (isEditMode() && !hiddenToolbar()) {\n @if (templateToolbar()) {\n <!-- N\u01A1i d\u00F9ng t\u1EF1 v\u1EBD thanh c\u00F4ng c\u1EE5: nh\u1EADn kh\u1ED1i + c\u00E1c h\u00E0m ph\u00E1t event qua context.\n\n \uD83D\uDD34 Kh\u1ED1i b\u1ECDc t\u1EF1 ch\u1EB7n `mousedown`/`click` n\u1ED5i l\u00EAn gridster. Thanh c\u00F4ng c\u1EE5 n\u1EB1m trong\n v\u00F9ng mang `dragHandleClass`; kh\u00F4ng ch\u1EB7n th\u00EC c\u00FA b\u1EA5m b\u1ECB hi\u1EC3u l\u00E0 b\u1EAFt \u0111\u1EA7u k\u00E9o v\u00E0\n `delayStart: 160` nu\u1ED1t lu\u00F4n `click` \u2014 n\u00FAt trong template ngo\u00E0i b\u1EA5m kh\u00F4ng \u0103n\n (\u0111o 09/09/2026: b\u1EA5m b\u1EB1ng `.click()` c\u1EE7a DOM th\u00EC m\u1EDF, b\u1EA5m chu\u1ED9t th\u1EADt th\u00EC kh\u00F4ng).\n Ch\u1EB7n \u1EDF \u0110\u00C2Y \u0111\u1EC3 n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i bi\u1EBFt b\u1EABy n\u00E0y. -->\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n role=\"toolbar\"\n tabindex=\"0\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\"\n (mousedown)=\"handlerStopEvent($event)\"\n (keydown)=\"handlerStopEvent($event)\"\n (click)=\"handlerStopEvent($event)\">\n <ng-container\n *ngTemplateOutlet=\"templateToolbar()!; context: toolbarContexts()[itemIndex]\" />\n </div>\n } @else {\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-dim-badge\">\n {{ item.rows }} h\u00E0ng \u00D7 {{ item.cols }} c\u1ED9t\n </span>\n @if (canAddInside(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-add'\"\n [popover]=\"{ config: { content: 'Th\u00EAm kh\u1ED1i v\u00E0o trong', zIndex: 1300 } }\"\n (outClick)=\"handlerAddInside($event, item)\" />\n }\n @if (canToggleType(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"isContainer(item) ? 'libs-ui-icon-split-cell' : 'libs-ui-icon-merge-cell'\"\n [popover]=\"{ config: { content: isContainer(item) ? '\u0110\u01B0a v\u1EC1 kh\u1ED1i \u0111\u01A1n' : 'Chuy\u1EC3n th\u00E0nh kh\u1ED1i gh\u00E9p', zIndex: 1300 } }\"\n (outClick)=\"handlerToggleType($event, item)\" />\n }\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-setting'\"\n [popover]=\"{ config: { content: 'C\u1EA5u h\u00ECnh th\u1EBB', zIndex: 1300 } }\"\n (outClick)=\"handlerConfig($event, item)\" />\n <libs_ui-components-buttons-button\n [type]=\"'button-third-hover-danger'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-remove'\"\n [popover]=\"{ config: { content: isContainer(item) ? 'Xo\u00E1 c\u1EA3 kh\u1ED1i gh\u00E9p' : 'Xo\u00E1 kh\u1ED1i', zIndex: 1300 } }\"\n (outClick)=\"handlerRemove($event, item)\" />\n </div>\n }\n }\n\n @if (item.children && item.children.length > 0) {\n <!-- Kh\u1ED1i c\u00F3 con \u2192 ru\u1ED9t n\u00F3 l\u1EA1i l\u00E0 m\u1ED9t m\u1EB7t ph\u1EB3ng n\u1EEFa. \u0110\u1EC7 quy \u1EDF \u0111\u00E2y, n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i lo.\n L\u1EC1 \u0111\u1EB7t \u1EDF \u0110\u00C2Y (v\u1ECF container), kh\u00F4ng \u0111\u1EB7t v\u00E0o l\u01B0\u1EDBi con \u2014 l\u01B0\u1EDBi con gi\u1EEF nguy\u00EAn khe 2px. -->\n <libs_ui-services-grid_layout-canvas\n class=\"block h-full w-full\"\n [style.padding]=\"containerPadding()\"\n [node]=\"item\"\n [depth]=\"depth() + 1\"\n [hiddenToolbar]=\"hiddenToolbar()\"\n [templateToolbar]=\"templateToolbar()\"\n [selectedNodeId]=\"selectedNodeId()\"\n (outSelectNode)=\"outSelectNode.emit($event)\"\n (outConfigNode)=\"outConfigNode.emit($event)\"\n (outChange)=\"outChange.emit($event)\"\n (outRemoveNode)=\"outRemoveNode.emit($event)\"\n (outAddBlockInside)=\"outAddBlockInside.emit($event)\"\n (outToggleType)=\"outToggleType.emit($event)\" />\n }\n </div>\n </gridster-item>\n }\n </gridster>\n </div>\n </div>\n\n <!-- Thanh dock \u0111i\u1EC1u khi\u1EC3n thu ph\u00F3ng n\u1ED5i \u1EDF g\u00F3c d\u01B0\u1EDBi ph\u1EA3i -->\n @if (showZoomDock()) {\n <div\n #zoomDock\n id=\"canvas-zoom-dock\"\n role=\"toolbar\"\n tabindex=\"0\"\n class=\"absolute bottom-4 right-6 z-30 flex items-center bg-white/95 backdrop-blur-md rounded-full shadow-lg border border-slate-300/80 p-1 space-x-1 select-none\"\n (mousedown)=\"$event.stopPropagation()\"\n (keydown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\">\n <!-- Thu nh\u1ECF (-) -->\n <button\n type=\"button\"\n class=\"w-7 h-7 flex items-center justify-center rounded-full text-slate-600 hover:text-slate-900 hover:bg-slate-100 disabled:opacity-30 disabled:hover:bg-transparent transition-colors cursor-pointer\"\n [disabled]=\"currentZoom() <= zoomMin()\"\n [title]=\"'i18n_zoom_out' | translate\"\n (click)=\"handlerZoomOut()\">\n <svg class=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/></svg>\n </button>\n\n <!-- T\u1EF7 l\u1EC7 % v\u00E0 menu preset -->\n <div class=\"relative\">\n <button\n type=\"button\"\n class=\"h-7 px-2 flex items-center gap-1 rounded-full text-xs font-semibold text-slate-700 hover:bg-slate-100 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_options' | translate\"\n (click)=\"handlerToggleMenu()\">\n <span>{{ zoomPercent() }}%</span>\n <svg class=\"w-3 h-3 text-slate-400 transition-transform duration-150\" [class.rotate-180]=\"isMenuOpen()\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n\n <!-- Menu dropdown c\u00E1c m\u1ED1c thu ph\u00F3ng -->\n @if (isMenuOpen()) {\n <div class=\"absolute bottom-full mb-2 right-0 w-64 bg-white rounded-xl shadow-xl border border-slate-200 py-1.5 z-40 text-xs select-none\">\n <div class=\"px-3 py-1 text-[11px] font-semibold text-slate-400 uppercase tracking-wider\">\n {{ 'i18n_zoom_canvas_width' | translate: { width: stageWidth() } }}\n </div>\n <button\n type=\"button\"\n class=\"w-full px-3 py-2 text-left flex items-center justify-between hover:bg-slate-50 transition-colors cursor-pointer\"\n (click)=\"handlerFitScreen()\">\n <span class=\"flex items-center gap-2 text-slate-700 font-medium\">\n <svg class=\"w-3.5 h-3.5 text-slate-500\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/></svg>\n {{ 'i18n_zoom_fit_screen' | translate }}\n </span>\n <span class=\"text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded font-medium\">{{ 'i18n_auto' | translate }}</span>\n </button>\n <div class=\"h-px bg-slate-100 my-1\"></div>\n @for (preset of zoomPresets; track preset.value) {\n <button\n type=\"button\"\n class=\"w-full px-3 py-1.5 text-left flex items-center justify-between hover:bg-slate-50 transition-colors cursor-pointer\"\n [class.text-blue-600]=\"isCurrentPreset(preset.value)\"\n [class.font-semibold]=\"isCurrentPreset(preset.value)\"\n [class.bg-blue-50]=\"isCurrentPreset(preset.value)\"\n (click)=\"handlerSelectPreset(preset.value)\">\n <span class=\"text-slate-700\" [class.text-blue-600]=\"isCurrentPreset(preset.value)\">\n {{ preset.label | translate }}\n </span>\n @if (isCurrentPreset(preset.value)) {\n <svg class=\"w-3.5 h-3.5 text-blue-600\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>\n }\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Ph\u00F3ng to (+) -->\n <button\n type=\"button\"\n class=\"w-7 h-7 flex items-center justify-center rounded-full text-slate-600 hover:text-slate-900 hover:bg-slate-100 disabled:opacity-30 disabled:hover:bg-transparent transition-colors cursor-pointer\"\n [disabled]=\"currentZoom() >= zoomMax()\"\n [title]=\"'i18n_zoom_in' | translate\"\n (click)=\"handlerZoomIn()\">\n <svg class=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/></svg>\n </button>\n\n <!-- Ph\u00E2n c\u00E1ch -->\n <div class=\"h-4 w-px bg-slate-200\"></div>\n\n <!-- N\u00FAt V\u1EEBa m\u00E0n h\u00ECnh nhanh -->\n <button\n type=\"button\"\n class=\"h-7 px-2.5 flex items-center gap-1 rounded-full text-xs text-slate-600 hover:text-slate-900 hover:bg-slate-100 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_fit_screen_tooltip' | translate\"\n (click)=\"handlerFitScreen()\">\n <svg class=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/></svg>\n <span class=\"font-medium\">{{ 'i18n_zoom_fit_screen' | translate }}</span>\n </button>\n\n <!-- N\u00FAt \u0110\u1EB7t l\u1EA1i 100% (ch\u1EC9 hi\u1EC7n khi kh\u00E1c 100%) -->\n @if (zoomPercent() !== 100) {\n <button\n type=\"button\"\n class=\"h-7 px-2 flex items-center gap-1 rounded-full text-xs font-semibold text-blue-600 hover:bg-blue-50 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_reset_tooltip' | translate: { width: stageWidth(), height: stageMinHeight() }\"\n (click)=\"handlerResetZoom()\">\n <svg class=\"w-3 h-3\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>\n <span>100%</span>\n </button>\n }\n </div>\n }\n} @else {\n <!-- \uD83D\uDD34 `relative` l\u00E0 B\u1EAET BU\u1ED8C, C\u1EA4M b\u1ECF.\n gridster \u0111\u1ED5i v\u1ECB tr\u00ED chu\u1ED9t th\u00E0nh \u00F4 l\u01B0\u1EDBi b\u1EB1ng `e.clientY + (el.scrollTop - el.offsetTop)` \u2014 c\u00F4ng\n th\u1EE9c \u0111\u00F3 ch\u1EC9 \u0111\u00FAng khi `offsetParent` c\u1EE7a <gridster> CH\u00CDNH L\u00C0 kh\u1ED1i cu\u1ED9n n\u00E0y. Thi\u1EBFu `relative` th\u00EC\n `offsetParent` nh\u1EA3y l\u00EAn kh\u1ED1i bao ngo\u00E0i: `offsetTop` tr\u1EA3 16 trong khi l\u1EC7ch th\u1EADt l\u00E0 65 \u2192 gridster\n t\u00EDnh \u00F4 sai 49px, \u00F4 ch\u1EDD \u0111\u1EB7t hi\u1EC7n l\u1EC7ch kh\u1ECFi con tr\u1ECF n\u00EAn k\u00E9o th\u1EA3 kh\u00F4ng \u0111\u1ED5i \u0111\u01B0\u1EE3c v\u1ECB tr\u00ED, v\u00E0 k\u00E9o m\u00E9p\n c\u0169ng t\u00EDnh sai \u0111i\u1EC3m d\u1EEBng. -->\n <div\n #scrollArea\n class=\"libs-ui-grid-layout-scroll-area relative h-full min-h-0 w-full min-w-0\"\n [class.overflow-y-auto]=\"!isNestedCanvas()\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\"\n (dragover)=\"handlerDragOver($event)\"\n (dragleave)=\"handlerDragLeave($event)\"\n (drop)=\"handlerDrop($event)\">\n <!-- Snapped Drop Ghost Preview -->\n @if (dropGhost(); as ghost) {\n <div\n class=\"pointer-events-none absolute z-30 flex flex-col items-center justify-center overflow-hidden rounded-lg border-2 border-dashed border-blue-500 bg-blue-500/15 p-1 shadow-sm transition-all duration-75 ease-out backdrop-blur-[0.5px] select-none\"\n [style.left.px]=\"ghost.pixelLeft\"\n [style.top.px]=\"ghost.pixelTop\"\n [style.width.px]=\"ghost.pixelWidth\"\n [style.height.px]=\"ghost.pixelHeight\">\n <div class=\"inline-flex max-w-[95%] items-center gap-1 truncate rounded-md bg-blue-600 px-2 py-0.5 text-[11px] font-semibold text-white shadow-xs\">\n @if (ghost.cardIconClass) {\n <i [class]=\"ghost.cardIconClass + ' text-xs'\"></i>\n } @else if (ghost.cardIconText) {\n <span class=\"text-[10px] font-bold\">{{ ghost.cardIconText }}</span>\n } @else {\n <span class=\"text-xs font-bold\">{{ ghost.isInsideGroup ? '\u21B3' : '+' }}</span>\n }\n <span class=\"truncate\">{{ ghost.cardTitle }}</span>\n <span class=\"text-[9px] font-mono opacity-75\">({{ ghost.cols }}c)</span>\n </div>\n\n @if (ghost.pixelHeight >= 80) {\n <div class=\"mt-1 flex max-w-[95%] items-center gap-1.5 truncate rounded border border-blue-100 bg-white/95 px-2 py-0.5 text-[10px] font-medium text-blue-700 shadow-xs\">\n @if (ghost.isInsideGroup) {\n <span class=\"truncate font-bold text-blue-900\">\u21B3 {{ ghost.groupTitle }}</span>\n <span class=\"text-blue-300\">\u2022</span>\n }\n <span>C\u1ED9t {{ ghost.col + 1 }}-{{ ghost.col + ghost.cols }}</span>\n </div>\n }\n </div>\n }\n <gridster class=\"h-full w-full\" [class.display-grid]=\"showGridLines()\" [options]=\"gridOptions()\">\n @for (item of children(); track item.id; let itemIndex = $index) {\n <!-- \uD83D\uDD34 Ch\u1EB7n `mousedown` t\u1EA1i \u0110\u00C2Y cho l\u01B0\u1EDBi l\u1ED3ng \u2014 C\u1EA4M b\u1ECF v\u00E0 C\u1EA4M chuy\u1EC3n sang ch\u1ED7 kh\u00E1c.\n `ignoreContentClass` m\u1ED9t m\u00ECnh KH\u00D4NG \u0111\u1EE7: `delayStart: 160` l\u00E0m M\u1ED6I l\u01B0\u1EDBi l\u00EAn l\u1ECBch\n `dragStart` b\u1EB1ng `setTimeout` ngay t\u1EA1i `mousedown`, c\u00F2n `stopPropagation` m\u00E0 lib g\u1ECDi b\u00EAn\n trong `dragStart` ch\u1EA1y sau 160ms n\u00EAn qu\u00E1 mu\u1ED9n \u2014 l\u01B0\u1EDBi CHA \u0111\u00E3 k\u1ECBp nh\u1EADn (\u0111o 28/08/2026: k\u00E9o\n d\u1EA3i kh\u1ED1i con, chu\u1ED7i class duy\u1EC7t l\u00EAn \u0110\u00DANG m\u00E0 container v\u1EABn nh\u1EA3y 0,0 \u2192 0,1).\n G\u1EAFn \u1EDF `gridster-item` l\u00E0 \u0111\u00FAng bi\u00EAn: l\u01B0\u1EDBi con nghe tr\u00EAn ch\u00EDnh ph\u1EA7n t\u1EED n\u00E0y n\u00EAn \u0111\u00E3 nh\u1EADn xong,\n l\u01B0\u1EDBi cha \u1EDF ngo\u00E0i b\u1ECB ch\u1EB7n. -->\n <gridster-item\n [item]=\"item\"\n [class.libs-ui-grid-layout-selected]=\"selectedNodeId() === item.id\"\n (click)=\"handlerSelectNode(item, $event)\"\n (mousedown)=\"handlerBlockParentDrag($event)\">\n <!-- \uD83D\uDD34 V\u1ECF n\u00E0y mang `dragHandleClass` \u2014 C\u1EA4M b\u1ECF.\n `ignoreContent: true` b\u1EAFt gridster CH\u1EC8 cho k\u00E9o khi ch\u1ED7 b\u1EA5m n\u1EB1m d\u01B0\u1EDBi m\u1ED9t ph\u1EA7n t\u1EED c\u00F3\n `dragHandleClass`. Kh\u00F4ng c\u00F3 v\u1ECF n\u00E0y th\u00EC component c\u1EE7a n\u01A1i d\u00F9ng (th\u01B0 vi\u1EC7n kh\u00F4ng ki\u1EC3m so\u00E1t\n \u0111\u01B0\u1EE3c class c\u1EE7a n\u00F3) s\u1EBD kh\u00F4ng c\u00F3 tay c\u1EA7m n\u00E0o, v\u00E0 KH\u00D4NG kh\u1ED1i n\u00E0o k\u00E9o \u0111\u01B0\u1EE3c \u2014 \u0111o 28/08/2026:\n k\u00E9o 320px, to\u1EA1 \u0111\u1ED9 kh\u1ED1i gi\u1EEF nguy\u00EAn x=3,y=0.\n T\u00EAn class theo c\u1EA5p: l\u01B0\u1EDBi trang v\u00E0 l\u01B0\u1EDBi l\u1ED3ng d\u00F9ng hai t\u00EAn KH\u00C1C nhau, n\u1EBFu kh\u00F4ng l\u01B0\u1EDBi cha\n c\u0169ng nh\u1EADn ra tay c\u1EA7m c\u1EE7a kh\u1ED1i con v\u00E0 nh\u1EA5c lu\u00F4n container. -->\n <!-- \uD83D\uDD34 V\u1ECF KH\u00D4NG mang `dragHandleClass` \u2014 C\u1EA4M th\u00EAm l\u1EA1i.\n K\u00E9o ch\u1EC9 \u0111\u01B0\u1EE3c ph\u00E9p b\u1EAFt \u0111\u1EA7u t\u1EEB D\u1EA2I XANH (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026: \"ph\u1EA3i hover v\u00E0o v\u00F9ng\n m\u00E0u xanh th\u00EC m\u1EDBi k\u00E9o th\u1EA3 \u0111\u01B0\u1EE3c cho \u0111\u1ED3ng nh\u1EA5t\"). Cho c\u1EA3 v\u1ECF l\u00E0m tay c\u1EA7m th\u00EC b\u1EA5m \u0111\u00E2u c\u0169ng\n k\u00E9o, v\u00E0 kh\u1ED1i con n\u1EB1m trong container s\u1EBD nh\u1EA5c lu\u00F4n container v\u00EC l\u01B0\u1EDBi cha duy\u1EC7t l\u00EAn g\u1EB7p\n tay c\u1EA7m c\u1EE7a v\u1ECF container tr\u01B0\u1EDBc.\n V\u1ECF kh\u1ED1i con v\u1EABn gi\u1EEF class CH\u1EB6N \u0111\u1EC3 l\u01B0\u1EDBi cha d\u1EEBng \u0111\u00FAng \u1EDF bi\u00EAn. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas()\">\n <!-- D\u1EA3i k\u00E9o: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i, ba ch\u1EA5m l\u00E0 d\u1EA5u hi\u1EC7u k\u00E9o quen thu\u1ED9c.\n Ng\u01B0\u1EDDi d\u00F9ng c\u1EA7n m\u1ED9t ch\u1ED7 b\u00E1m R\u00D5 R\u00C0NG thay v\u00EC \u0111o\u00E1n xem b\u1EA5m \u0111\u00E2u th\u00EC k\u00E9o \u0111\u01B0\u1EE3c\n (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng d\u1EA3i \u0111ang d\u00F9ng \u1EDF m\u00E0n Customer 360 c\u1EE7a mobio-web). -->\n @if (isEditMode()) {\n <!-- \uD83D\uDD34 D\u1EA3i PH\u1EA2I mang tay c\u1EA7m \u0110\u00DANG C\u1EA4P c\u1EE7a n\u00F3. D\u1EA3i nh\u1EADn chu\u1ED9t (`pointer-events: auto`),\n n\u00EAn n\u1EBFu kh\u00F4ng mang tay c\u1EA7m th\u00EC `checkDragHandleClass` duy\u1EC7t l\u00EAn g\u1EB7p tay c\u1EA7m c\u1EE7a\n container v\u00E0 nh\u1EA5c container thay v\u00EC kh\u1ED1i con (\u0111o 28/08/2026: k\u00E9o d\u1EA3i kh\u1ED1i con,\n container nh\u1EA3y 0,0 \u2192 0,1 c\u00F2n kh\u1ED1i con \u0111\u1EE9ng im).\n \uD83D\uDD34 D\u1EA3i CH\u1EC8 mang tay c\u1EA7m, C\u1EA4M mang th\u00EAm class ch\u1EB7n: `checkDragHandleClass` x\u00E9t hai\n class \u0111\u00F3 tr\u00EAn C\u00D9NG m\u1ED9t node n\u00EAn \u0111\u1EC3 chung l\u00E0 k\u00E9o h\u1ECFng (\u0111o 28/08/2026: b\u1EA5m d\u1EA3i kh\u1ED1i\n con k\u00E9o 140px, kh\u1ED1i \u0111\u1EE9ng im). Vi\u1EC7c ch\u1EB7n l\u01B0\u1EDBi cha do V\u1ECE KH\u1ED0I lo. -->\n <div\n class=\"libs-ui-grid-layout-drag-bar\"\n [class.libs-ui-grid-layout-drag-bar-child]=\"isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle]=\"!isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-drag-bar-dots\">\n @for (dot of dragBarDots; track dot) {\n <span class=\"libs-ui-grid-layout-drag-bar-dot\"></span>\n }\n </span>\n </div>\n }\n\n <!-- Khung ch\u1EE9a component c\u1EE7a n\u01A1i d\u00F9ng.\n \uD83D\uDD34 PH\u1EA2I c\u00F3 khung th\u1EADt (kh\u00F4ng ph\u1EA3i `ng-container` tr\u1ED1ng): n\u00F3 lo h\u1ED9 n\u01A1i d\u00F9ng ph\u1EA7n l\u1EA5p \u0111\u1EA7y\n \u00F4 v\u00E0 vi\u1EC1n b\u00E1o ch\u1EBF \u0111\u1ED9 s\u1EEDa. Kh\u00F4ng c\u00F3 khung th\u00EC m\u1ED7i component n\u1ED9i dung l\u1EA1i ph\u1EA3i t\u1EF1 vi\u1EBFt\n `:host { width/height: 100% }` + vi\u1EC1n \u0111\u1EE9t \u2014 \u0111\u00F3 l\u00E0 vi\u1EC7c c\u1EE7a th\u01B0 vi\u1EC7n, kh\u00F4ng ph\u1EA3i c\u1EE7a\n ng\u01B0\u1EDDi d\u00F9ng (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026).\n M\u1ED7i kh\u1ED1i m\u1ED9t ViewContainerRef ri\u00EAng n\u00EAn component n\u1EB1m \u0111\u00FAng \u00F4 c\u1EE7a n\u00F3. -->\n <div\n class=\"libs-ui-grid-layout-content-frame\"\n [class.libs-ui-grid-layout-content-frame-edit]=\"isEditMode() && !isContainer(item)\"\n [class.libs-ui-grid-layout-content-frame-empty]=\"isContainer(item)\">\n <ng-container #itemHost />\n </div>\n\n <!-- D\u1EA5u hi\u1EC7u CONTAINER: nh\u00E3n g\u00F3c tr\u00EAn-ph\u1EA3i + vi\u1EC1n \u0111\u1EE9t bao ru\u1ED9t. Kh\u00F4ng c\u00F3 d\u1EA5u hi\u1EC7u th\u00EC\n ng\u01B0\u1EDDi d\u00F9ng kh\u00F4ng bi\u1EBFt kh\u1ED1i n\u00E0o ch\u1EE9a \u0111\u01B0\u1EE3c kh\u1ED1i kh\u00E1c (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng c\u00E1ch\n m\u00E0n Customer 360 c\u1EE7a mobio-web \u0111ang l\u00E0m). -->\n @if (isEditMode() && isContainer(item)) {\n <div class=\"libs-ui-grid-layout-container-label\">{{ 'i18n_container_block' | translate }}</div>\n <div class=\"libs-ui-grid-layout-container-border\"></div>\n }\n\n <!-- Thanh c\u00F4ng c\u1EE5: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i.\n \uD83D\uDD34 D\u00F9ng `libs_ui-components-buttons-button` \u2014 C\u1EA4M t\u1EF1 d\u1EF1ng th\u1EBB `<button>`.\n Component n\u00E0y \u0111\u00E3 c\u00F3 s\u1EB5n ki\u1EC3u n\u00FAt, c\u1EE1 n\u00FAt v\u00E0 bong b\u00F3ng ch\u00FA th\u00EDch theo \u0111\u00FAng b\u1ED9 giao di\u1EC7n\n c\u1EE7a s\u1EA3n ph\u1EA9m; t\u1EF1 d\u1EF1ng l\u00E0 m\u1ED7i n\u01A1i m\u1ED9t ki\u1EC3u v\u00E0 m\u1EA5t lu\u00F4n tooltip. -->\n @if (isEditMode() && !hiddenToolbar()) {\n @if (templateToolbar()) {\n <!-- N\u01A1i d\u00F9ng t\u1EF1 v\u1EBD thanh c\u00F4ng c\u1EE5: nh\u1EADn kh\u1ED1i + c\u00E1c h\u00E0m ph\u00E1t event qua context.\n\n \uD83D\uDD34 Kh\u1ED1i b\u1ECDc t\u1EF1 ch\u1EB7n `mousedown`/`click` n\u1ED5i l\u00EAn gridster. Thanh c\u00F4ng c\u1EE5 n\u1EB1m trong\n v\u00F9ng mang `dragHandleClass`; kh\u00F4ng ch\u1EB7n th\u00EC c\u00FA b\u1EA5m b\u1ECB hi\u1EC3u l\u00E0 b\u1EAFt \u0111\u1EA7u k\u00E9o v\u00E0\n `delayStart: 160` nu\u1ED1t lu\u00F4n `click` \u2014 n\u00FAt trong template ngo\u00E0i b\u1EA5m kh\u00F4ng \u0103n\n (\u0111o 09/09/2026: b\u1EA5m b\u1EB1ng `.click()` c\u1EE7a DOM th\u00EC m\u1EDF, b\u1EA5m chu\u1ED9t th\u1EADt th\u00EC kh\u00F4ng).\n Ch\u1EB7n \u1EDF \u0110\u00C2Y \u0111\u1EC3 n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i bi\u1EBFt b\u1EABy n\u00E0y. -->\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n role=\"toolbar\"\n tabindex=\"0\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\"\n (mousedown)=\"handlerStopEvent($event)\"\n (keydown)=\"handlerStopEvent($event)\"\n (click)=\"handlerStopEvent($event)\">\n <ng-container\n *ngTemplateOutlet=\"templateToolbar()!; context: toolbarContexts()[itemIndex]\" />\n </div>\n } @else {\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-dim-badge\">\n {{ item.rows }} h\u00E0ng \u00D7 {{ item.cols }} c\u1ED9t\n </span>\n @if (canAddInside(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-add'\"\n [popover]=\"{ config: { content: 'Th\u00EAm kh\u1ED1i v\u00E0o trong', zIndex: 1300 } }\"\n (outClick)=\"handlerAddInside($event, item)\" />\n }\n @if (canToggleType(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"isContainer(item) ? 'libs-ui-icon-split-cell' : 'libs-ui-icon-merge-cell'\"\n [popover]=\"{ config: { content: isContainer(item) ? '\u0110\u01B0a v\u1EC1 kh\u1ED1i \u0111\u01A1n' : 'Chuy\u1EC3n th\u00E0nh kh\u1ED1i gh\u00E9p', zIndex: 1300 } }\"\n (outClick)=\"handlerToggleType($event, item)\" />\n }\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-setting'\"\n [popover]=\"{ config: { content: 'C\u1EA5u h\u00ECnh th\u1EBB', zIndex: 1300 } }\"\n (outClick)=\"handlerConfig($event, item)\" />\n <libs_ui-components-buttons-button\n [type]=\"'button-third-hover-danger'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-remove'\"\n [popover]=\"{ config: { content: isContainer(item) ? 'Xo\u00E1 c\u1EA3 kh\u1ED1i gh\u00E9p' : 'Xo\u00E1 kh\u1ED1i', zIndex: 1300 } }\"\n (outClick)=\"handlerRemove($event, item)\" />\n </div>\n }\n }\n\n @if (item.children && item.children.length > 0) {\n <!-- Kh\u1ED1i c\u00F3 con \u2192 ru\u1ED9t n\u00F3 l\u1EA1i l\u00E0 m\u1ED9t m\u1EB7t ph\u1EB3ng n\u1EEFa. \u0110\u1EC7 quy \u1EDF \u0111\u00E2y, n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i lo.\n L\u1EC1 \u0111\u1EB7t \u1EDF \u0110\u00C2Y (v\u1ECF container), kh\u00F4ng \u0111\u1EB7t v\u00E0o l\u01B0\u1EDBi con \u2014 l\u01B0\u1EDBi con gi\u1EEF nguy\u00EAn khe 2px. -->\n <libs_ui-services-grid_layout-canvas\n class=\"block h-full w-full\"\n [style.padding]=\"containerPadding()\"\n [node]=\"item\"\n [depth]=\"depth() + 1\"\n [hiddenToolbar]=\"hiddenToolbar()\"\n [templateToolbar]=\"templateToolbar()\"\n [selectedNodeId]=\"selectedNodeId()\"\n (outSelectNode)=\"outSelectNode.emit($event)\"\n (outConfigNode)=\"outConfigNode.emit($event)\"\n (outChange)=\"outChange.emit($event)\"\n (outRemoveNode)=\"outRemoveNode.emit($event)\"\n (outAddBlockInside)=\"outAddBlockInside.emit($event)\"\n (outToggleType)=\"outToggleType.emit($event)\" />\n }\n </div>\n </gridster-item>\n }\n </gridster>\n </div>\n}\n", styles: [":host{display:block;position:relative;box-sizing:border-box;width:100%;height:100%;min-width:0;min-height:0}.libs-ui-grid-layout-block-shell{container-type:inline-size;display:block;box-sizing:border-box;height:100%}.libs-ui-grid-layout-drag-bar{position:absolute;top:2px;left:50%;z-index:15;display:flex;align-items:center;justify-content:center;width:64px;height:16px;background:transparent;transform:translate(-50%);opacity:1;transition:opacity .12s ease;pointer-events:auto;cursor:move}.libs-ui-grid-layout-drag-bar-dots{display:grid;grid-template-columns:repeat(3,3px);gap:3px;color:#9ca2ad}.libs-ui-grid-layout-drag-bar-dot{width:3px;height:3px;background:currentColor;border-radius:50%}.libs-ui-grid-layout-drag-bar:hover .libs-ui-grid-layout-drag-bar-dots{color:#3d6ef5}.libs-ui-grid-layout-drag-bar:not(.libs-ui-grid-layout-drag-bar-child){opacity:1}.libs-ui-grid-layout-drag-bar-child{top:auto;bottom:0;background:#d9f2e4;border-radius:4px 4px 0 0}.libs-ui-grid-layout-drag-bar-child .libs-ui-grid-layout-drag-bar-dots{color:#9ca2ad}.libs-ui-grid-layout-drag-bar-child:hover .libs-ui-grid-layout-drag-bar-dots{color:#00a757}.libs-ui-grid-layout-drag-bar-child{opacity:0}.libs-ui-grid-layout-block-shell:hover>.libs-ui-grid-layout-drag-bar-child{opacity:1}.libs-ui-grid-layout-block-shell:has(>.libs-ui-grid-layout-drag-bar:not(.libs-ui-grid-layout-drag-bar-child):hover){outline:2px solid #3d6ef5;outline-offset:-2px;border-radius:8px}.libs-ui-grid-layout-block-shell:has(>.libs-ui-grid-layout-drag-bar-child:hover){outline:2px solid #00a757;outline-offset:-2px;border-radius:8px}.libs-ui-grid-layout-container-label{position:absolute;left:6px;top:2px;display:flex;height:20px;align-items:center;z-index:3;padding:0 6px;color:#3d6ef5;font-weight:500;font-size:10px;line-height:14px;background:#dbe4ff;border-radius:4px;pointer-events:none}.libs-ui-grid-layout-content-frame{box-sizing:border-box;width:100%;height:100%;overflow:hidden;border-radius:6px}.libs-ui-grid-layout-content-frame ::ng-deep>*{display:block;box-sizing:border-box;width:100%;height:100%}.libs-ui-grid-layout-content-frame-empty{height:0}.libs-ui-grid-layout-content-frame-edit{border:1px dashed #c3cede}.libs-ui-grid-layout-container-border{position:absolute;inset:0;border:1px dashed #9db4f0;border-radius:8px;pointer-events:none}.libs-ui-grid-layout-toolbar{position:absolute;top:6px;right:8px;z-index:24;display:flex;gap:2px;align-items:center;height:20px;background:#fff;border-radius:4px;box-shadow:0 2px 8px #0716311f;opacity:0;transition:opacity .12s ease}.libs-ui-grid-layout-toolbar-child{top:50%;right:8px;left:auto;transform:translateY(-50%)}.libs-ui-grid-layout-block-shell:hover>.libs-ui-grid-layout-toolbar{opacity:1}:host ::ng-deep gridster{background:transparent}:host ::ng-deep .gridster-item-resizable-handler.handle-n,:host ::ng-deep .gridster-item-resizable-handler.handle-s{height:16px}:host ::ng-deep .gridster-item-resizable-handler.handle-e,:host ::ng-deep .gridster-item-resizable-handler.handle-w{width:16px}:host ::ng-deep .gridster-item-resizable-handler.handle-n{top:0}:host ::ng-deep .gridster-item-resizable-handler.handle-s{bottom:0}:host ::ng-deep .gridster-item-resizable-handler.handle-e{right:0}:host ::ng-deep .gridster-item-resizable-handler.handle-w{left:0}:host ::ng-deep gridster-item.gridster-item-moving,:host ::ng-deep gridster-item.gridster-item-resizing{z-index:30!important}:host ::ng-deep gridster-item.gridster-item-moving>.libs-ui-grid-layout-block-shell,:host ::ng-deep gridster-item.gridster-item-resizing>.libs-ui-grid-layout-block-shell{outline:2px solid #3d6ef5;outline-offset:-2px;border-radius:8px}:host ::ng-deep gridster gridster gridster-item.gridster-item-moving>.libs-ui-grid-layout-block-shell,:host ::ng-deep gridster gridster gridster-item.gridster-item-resizing>.libs-ui-grid-layout-block-shell{outline-color:#00a757}:host ::ng-deep gridster-item.libs-ui-grid-layout-selected>.libs-ui-grid-layout-block-shell{outline:2px solid #2563eb!important;outline-offset:-2px!important;border-radius:8px!important;box-shadow:0 0 0 4px #2563eb26!important}:host ::ng-deep gridster.display-grid .gridster-column,:host ::ng-deep gridster gridster.display-grid .gridster-column{border-right:1px solid rgba(226,232,240,.7)!important;border-left:none!important;pointer-events:none!important;height:100%!important;min-height:100%!important}:host ::ng-deep gridster.display-grid .gridster-column:first-child,:host ::ng-deep gridster gridster.display-grid .gridster-column:first-child{border-left:1px solid rgba(226,232,240,.7)!important}:host ::ng-deep gridster.display-grid .gridster-row,:host ::ng-deep gridster gridster.display-grid .gridster-row{border-bottom:1px solid rgba(226,232,240,.7)!important;border-top:none!important;pointer-events:none!important}:host ::ng-deep gridster.display-grid .gridster-row:first-child,:host ::ng-deep gridster gridster.display-grid .gridster-row:first-child{border-top:1px solid rgba(226,232,240,.7)!important}:host ::ng-deep gridster gridster.display-grid{background-color:#f8fafc80!important}:host ::ng-deep gridster-preview{background:#2563eb1f!important;border:1.5px dashed #2563eb!important;border-radius:6px!important;z-index:25!important}.libs-ui-grid-layout-dim-badge{display:inline-flex;align-items:center;height:20px;padding:0 6px;background-color:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;color:#64748b;font-size:10px;font-weight:500;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;pointer-events:none;white-space:nowrap}@container (max-width: 80px){.libs-ui-grid-layout-drag-bar{top:26px}}.libs-ui-grid-layout-stage{box-sizing:border-box;transform-origin:top center;transition:zoom .15s ease-out}#canvas-zoom-dock{box-shadow:0 10px 25px -5px #0000001a,0 8px 10px -6px #0000001a}\n"], dependencies: [{ kind: "component", type: LibsUiGridLayoutCanvasComponent, selector: "libs_ui-services-grid_layout-canvas", inputs: ["node", "depth", "hiddenToolbar", "templateToolbar", "enableZoom", "zoom", "zoomMin", "zoomMax", "zoomStep", "stageWidth", "stageMinHeight", "storageKey", "showZoomDock", "autoFitOnLoad", "enableExternalDrop", "showGridLines", "selectedNodeId"], outputs: ["outChange", "outRemoveNode", "outAddBlockInside", "outToggleType", "zoomChange", "outZoomChange", "selectedNodeIdChange", "outDropNode", "outSelectNode", "outConfigNode"] }, { kind: "ngmodule", type: GridsterModule }, { kind: "component", type: i1.GridsterComponent, selector: "gridster", inputs: ["options"] }, { kind: "component", type: i1.GridsterItemComponent, selector: "gridster-item", inputs: ["item"], outputs: ["itemInit", "itemChange", "itemResize"] }, { 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: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
188
561
|
}
|
|
189
562
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiGridLayoutCanvasComponent, decorators: [{
|
|
190
563
|
type: Component,
|
|
191
|
-
args: [{ selector: 'libs_ui-services-grid_layout-canvas', standalone: true, imports: [GridsterModule, LibsUiComponentsButtonsButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- \uD83D\uDD34 `relative` l\u00E0 B\u1EAET BU\u1ED8C, C\u1EA4M b\u1ECF.\n gridster \u0111\u1ED5i v\u1ECB tr\u00ED chu\u1ED9t th\u00E0nh \u00F4 l\u01B0\u1EDBi b\u1EB1ng `e.clientY + (el.scrollTop - el.offsetTop)` \u2014 c\u00F4ng\n th\u1EE9c \u0111\u00F3 ch\u1EC9 \u0111\u00FAng khi `offsetParent` c\u1EE7a <gridster> CH\u00CDNH L\u00C0 kh\u1ED1i cu\u1ED9n n\u00E0y. Thi\u1EBFu `relative` th\u00EC\n `offsetParent` nh\u1EA3y l\u00EAn kh\u1ED1i bao ngo\u00E0i: `offsetTop` tr\u1EA3 16 trong khi l\u1EC7ch th\u1EADt l\u00E0 65 \u2192 gridster\n t\u00EDnh \u00F4 sai 49px, \u00F4 ch\u1EDD \u0111\u1EB7t hi\u1EC7n l\u1EC7ch kh\u1ECFi con tr\u1ECF n\u00EAn k\u00E9o th\u1EA3 kh\u00F4ng \u0111\u1ED5i \u0111\u01B0\u1EE3c v\u1ECB tr\u00ED, v\u00E0 k\u00E9o m\u00E9p\n c\u0169ng t\u00EDnh sai \u0111i\u1EC3m d\u1EEBng. -->\n<div\n class=\"libs-ui-grid-layout-scroll-area relative h-full min-h-0 w-full min-w-0\"\n [class.overflow-y-auto]=\"!isNestedCanvas()\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\">\n <gridster class=\"h-full w-full\" [options]=\"gridOptions()\">\n @for (item of children(); track item.id) {\n <!-- \uD83D\uDD34 Ch\u1EB7n `mousedown` t\u1EA1i \u0110\u00C2Y cho l\u01B0\u1EDBi l\u1ED3ng \u2014 C\u1EA4M b\u1ECF v\u00E0 C\u1EA4M chuy\u1EC3n sang ch\u1ED7 kh\u00E1c.\n `ignoreContentClass` m\u1ED9t m\u00ECnh KH\u00D4NG \u0111\u1EE7: `delayStart: 160` l\u00E0m M\u1ED6I l\u01B0\u1EDBi l\u00EAn l\u1ECBch\n `dragStart` b\u1EB1ng `setTimeout` ngay t\u1EA1i `mousedown`, c\u00F2n `stopPropagation` m\u00E0 lib g\u1ECDi b\u00EAn\n trong `dragStart` ch\u1EA1y sau 160ms n\u00EAn qu\u00E1 mu\u1ED9n \u2014 l\u01B0\u1EDBi CHA \u0111\u00E3 k\u1ECBp nh\u1EADn (\u0111o 28/08/2026: k\u00E9o\n d\u1EA3i kh\u1ED1i con, chu\u1ED7i class duy\u1EC7t l\u00EAn \u0110\u00DANG m\u00E0 container v\u1EABn nh\u1EA3y 0,0 \u2192 0,1).\n G\u1EAFn \u1EDF `gridster-item` l\u00E0 \u0111\u00FAng bi\u00EAn: l\u01B0\u1EDBi con nghe tr\u00EAn ch\u00EDnh ph\u1EA7n t\u1EED n\u00E0y n\u00EAn \u0111\u00E3 nh\u1EADn xong,\n l\u01B0\u1EDBi cha \u1EDF ngo\u00E0i b\u1ECB ch\u1EB7n. -->\n <gridster-item\n [item]=\"item\"\n (mousedown)=\"handlerBlockParentDrag($event)\">\n <!-- \uD83D\uDD34 V\u1ECF n\u00E0y mang `dragHandleClass` \u2014 C\u1EA4M b\u1ECF.\n `ignoreContent: true` b\u1EAFt gridster CH\u1EC8 cho k\u00E9o khi ch\u1ED7 b\u1EA5m n\u1EB1m d\u01B0\u1EDBi m\u1ED9t ph\u1EA7n t\u1EED c\u00F3\n `dragHandleClass`. Kh\u00F4ng c\u00F3 v\u1ECF n\u00E0y th\u00EC component c\u1EE7a n\u01A1i d\u00F9ng (th\u01B0 vi\u1EC7n kh\u00F4ng ki\u1EC3m so\u00E1t\n \u0111\u01B0\u1EE3c class c\u1EE7a n\u00F3) s\u1EBD kh\u00F4ng c\u00F3 tay c\u1EA7m n\u00E0o, v\u00E0 KH\u00D4NG kh\u1ED1i n\u00E0o k\u00E9o \u0111\u01B0\u1EE3c \u2014 \u0111o 28/08/2026:\n k\u00E9o 320px, to\u1EA1 \u0111\u1ED9 kh\u1ED1i gi\u1EEF nguy\u00EAn x=3,y=0.\n T\u00EAn class theo c\u1EA5p: l\u01B0\u1EDBi trang v\u00E0 l\u01B0\u1EDBi l\u1ED3ng d\u00F9ng hai t\u00EAn KH\u00C1C nhau, n\u1EBFu kh\u00F4ng l\u01B0\u1EDBi cha\n c\u0169ng nh\u1EADn ra tay c\u1EA7m c\u1EE7a kh\u1ED1i con v\u00E0 nh\u1EA5c lu\u00F4n container. -->\n <!-- \uD83D\uDD34 V\u1ECF KH\u00D4NG mang `dragHandleClass` \u2014 C\u1EA4M th\u00EAm l\u1EA1i.\n K\u00E9o ch\u1EC9 \u0111\u01B0\u1EE3c ph\u00E9p b\u1EAFt \u0111\u1EA7u t\u1EEB D\u1EA2I XANH (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026: \"ph\u1EA3i hover v\u00E0o v\u00F9ng\n m\u00E0u xanh th\u00EC m\u1EDBi k\u00E9o th\u1EA3 \u0111\u01B0\u1EE3c cho \u0111\u1ED3ng nh\u1EA5t\"). Cho c\u1EA3 v\u1ECF l\u00E0m tay c\u1EA7m th\u00EC b\u1EA5m \u0111\u00E2u c\u0169ng\n k\u00E9o, v\u00E0 kh\u1ED1i con n\u1EB1m trong container s\u1EBD nh\u1EA5c lu\u00F4n container v\u00EC l\u01B0\u1EDBi cha duy\u1EC7t l\u00EAn g\u1EB7p\n tay c\u1EA7m c\u1EE7a v\u1ECF container tr\u01B0\u1EDBc.\n V\u1ECF kh\u1ED1i con v\u1EABn gi\u1EEF class CH\u1EB6N \u0111\u1EC3 l\u01B0\u1EDBi cha d\u1EEBng \u0111\u00FAng \u1EDF bi\u00EAn. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas()\">\n <!-- D\u1EA3i k\u00E9o: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i, ba ch\u1EA5m l\u00E0 d\u1EA5u hi\u1EC7u k\u00E9o quen thu\u1ED9c.\n Ng\u01B0\u1EDDi d\u00F9ng c\u1EA7n m\u1ED9t ch\u1ED7 b\u00E1m R\u00D5 R\u00C0NG thay v\u00EC \u0111o\u00E1n xem b\u1EA5m \u0111\u00E2u th\u00EC k\u00E9o \u0111\u01B0\u1EE3c\n (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng d\u1EA3i \u0111ang d\u00F9ng \u1EDF m\u00E0n Customer 360 c\u1EE7a mobio-web). -->\n @if (isEditMode()) {\n <!-- \uD83D\uDD34 D\u1EA3i PH\u1EA2I mang tay c\u1EA7m \u0110\u00DANG C\u1EA4P c\u1EE7a n\u00F3. D\u1EA3i nh\u1EADn chu\u1ED9t (`pointer-events: auto`),\n n\u00EAn n\u1EBFu kh\u00F4ng mang tay c\u1EA7m th\u00EC `checkDragHandleClass` duy\u1EC7t l\u00EAn g\u1EB7p tay c\u1EA7m c\u1EE7a\n container v\u00E0 nh\u1EA5c container thay v\u00EC kh\u1ED1i con (\u0111o 28/08/2026: k\u00E9o d\u1EA3i kh\u1ED1i con,\n container nh\u1EA3y 0,0 \u2192 0,1 c\u00F2n kh\u1ED1i con \u0111\u1EE9ng im).\n \uD83D\uDD34 D\u1EA3i CH\u1EC8 mang tay c\u1EA7m, C\u1EA4M mang th\u00EAm class ch\u1EB7n: `checkDragHandleClass` x\u00E9t hai\n class \u0111\u00F3 tr\u00EAn C\u00D9NG m\u1ED9t node n\u00EAn \u0111\u1EC3 chung l\u00E0 k\u00E9o h\u1ECFng (\u0111o 28/08/2026: b\u1EA5m d\u1EA3i kh\u1ED1i\n con k\u00E9o 140px, kh\u1ED1i \u0111\u1EE9ng im). Vi\u1EC7c ch\u1EB7n l\u01B0\u1EDBi cha do V\u1ECE KH\u1ED0I lo. -->\n <div\n class=\"libs-ui-grid-layout-drag-bar\"\n [class.libs-ui-grid-layout-drag-bar-child]=\"isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle]=\"!isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-drag-bar-dots\">\n @for (dot of dragBarDots; track dot) {\n <span class=\"libs-ui-grid-layout-drag-bar-dot\"></span>\n }\n </span>\n </div>\n }\n\n <!-- Khung ch\u1EE9a component c\u1EE7a n\u01A1i d\u00F9ng.\n \uD83D\uDD34 PH\u1EA2I c\u00F3 khung th\u1EADt (kh\u00F4ng ph\u1EA3i `ng-container` tr\u1ED1ng): n\u00F3 lo h\u1ED9 n\u01A1i d\u00F9ng ph\u1EA7n l\u1EA5p \u0111\u1EA7y\n \u00F4 v\u00E0 vi\u1EC1n b\u00E1o ch\u1EBF \u0111\u1ED9 s\u1EEDa. Kh\u00F4ng c\u00F3 khung th\u00EC m\u1ED7i component n\u1ED9i dung l\u1EA1i ph\u1EA3i t\u1EF1 vi\u1EBFt\n `:host { width/height: 100% }` + vi\u1EC1n \u0111\u1EE9t \u2014 \u0111\u00F3 l\u00E0 vi\u1EC7c c\u1EE7a th\u01B0 vi\u1EC7n, kh\u00F4ng ph\u1EA3i c\u1EE7a\n ng\u01B0\u1EDDi d\u00F9ng (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026).\n M\u1ED7i kh\u1ED1i m\u1ED9t ViewContainerRef ri\u00EAng n\u00EAn component n\u1EB1m \u0111\u00FAng \u00F4 c\u1EE7a n\u00F3. -->\n <div\n class=\"libs-ui-grid-layout-content-frame\"\n [class.libs-ui-grid-layout-content-frame-edit]=\"isEditMode() && !isContainer(item)\"\n [class.libs-ui-grid-layout-content-frame-empty]=\"isContainer(item)\">\n <ng-container #itemHost />\n </div>\n\n <!-- D\u1EA5u hi\u1EC7u CONTAINER: nh\u00E3n g\u00F3c tr\u00EAn-ph\u1EA3i + vi\u1EC1n \u0111\u1EE9t bao ru\u1ED9t. Kh\u00F4ng c\u00F3 d\u1EA5u hi\u1EC7u th\u00EC\n ng\u01B0\u1EDDi d\u00F9ng kh\u00F4ng bi\u1EBFt kh\u1ED1i n\u00E0o ch\u1EE9a \u0111\u01B0\u1EE3c kh\u1ED1i kh\u00E1c (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng c\u00E1ch\n m\u00E0n Customer 360 c\u1EE7a mobio-web \u0111ang l\u00E0m). -->\n @if (isEditMode() && isContainer(item)) {\n <div class=\"libs-ui-grid-layout-container-label\">Kh\u1ED1i gh\u00E9p</div>\n <div class=\"libs-ui-grid-layout-container-border\"></div>\n }\n\n <!-- Thanh c\u00F4ng c\u1EE5: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i.\n \uD83D\uDD34 D\u00F9ng `libs_ui-components-buttons-button` \u2014 C\u1EA4M t\u1EF1 d\u1EF1ng th\u1EBB `<button>`.\n Component n\u00E0y \u0111\u00E3 c\u00F3 s\u1EB5n ki\u1EC3u n\u00FAt, c\u1EE1 n\u00FAt v\u00E0 bong b\u00F3ng ch\u00FA th\u00EDch theo \u0111\u00FAng b\u1ED9 giao di\u1EC7n\n c\u1EE7a s\u1EA3n ph\u1EA9m; t\u1EF1 d\u1EF1ng l\u00E0 m\u1ED7i n\u01A1i m\u1ED9t ki\u1EC3u v\u00E0 m\u1EA5t lu\u00F4n tooltip. -->\n @if (isEditMode()) {\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\">\n @if (canAddInside(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-add'\"\n [popover]=\"{ config: { content: 'Th\u00EAm kh\u1ED1i v\u00E0o trong', zIndex: 1300 } }\"\n (outClick)=\"handlerAddInside($event, item)\" />\n }\n @if (canToggleType(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"isContainer(item) ? 'libs-ui-icon-split-cell' : 'libs-ui-icon-merge-cell'\"\n [popover]=\"{ config: { content: isContainer(item) ? '\u0110\u01B0a v\u1EC1 kh\u1ED1i \u0111\u01A1n' : 'Chuy\u1EC3n th\u00E0nh kh\u1ED1i gh\u00E9p', zIndex: 1300 } }\"\n (outClick)=\"handlerToggleType($event, item)\" />\n }\n <libs_ui-components-buttons-button\n [type]=\"'button-third-hover-danger'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-remove'\"\n [popover]=\"{ config: { content: isContainer(item) ? 'Xo\u00E1 c\u1EA3 kh\u1ED1i gh\u00E9p' : 'Xo\u00E1 kh\u1ED1i', zIndex: 1300 } }\"\n (outClick)=\"handlerRemove($event, item)\" />\n </div>\n }\n\n @if (item.children && item.children.length > 0) {\n <!-- Kh\u1ED1i c\u00F3 con \u2192 ru\u1ED9t n\u00F3 l\u1EA1i l\u00E0 m\u1ED9t m\u1EB7t ph\u1EB3ng n\u1EEFa. \u0110\u1EC7 quy \u1EDF \u0111\u00E2y, n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i lo.\n L\u1EC1 \u0111\u1EB7t \u1EDF \u0110\u00C2Y (v\u1ECF container), kh\u00F4ng \u0111\u1EB7t v\u00E0o l\u01B0\u1EDBi con \u2014 l\u01B0\u1EDBi con gi\u1EEF nguy\u00EAn khe 2px. -->\n <libs_ui-services-grid_layout-canvas\n class=\"block h-full w-full\"\n [style.padding]=\"containerPadding()\"\n [node]=\"item\"\n [depth]=\"depth() + 1\"\n (outChange)=\"outChange.emit($event)\"\n (outRemoveNode)=\"outRemoveNode.emit($event)\"\n (outAddBlockInside)=\"outAddBlockInside.emit($event)\"\n (outToggleType)=\"outToggleType.emit($event)\" />\n }\n </div>\n </gridster-item>\n }\n </gridster>\n</div>\n", styles: [":host{display:block;box-sizing:border-box;width:100%;height:100%;min-width:0;min-height:0}.libs-ui-grid-layout-block-shell{container-type:inline-size;display:block;box-sizing:border-box;height:100%}.libs-ui-grid-layout-drag-bar{position:absolute;top:2px;left:50%;z-index:15;display:flex;align-items:center;justify-content:center;width:64px;height:16px;background:transparent;transform:translate(-50%);opacity:1;transition:opacity .12s ease;pointer-events:auto;cursor:move}.libs-ui-grid-layout-drag-bar-dots{display:grid;grid-template-columns:repeat(3,3px);gap:3px;color:#9ca2ad}.libs-ui-grid-layout-drag-bar-dot{width:3px;height:3px;background:currentColor;border-radius:50%}.libs-ui-grid-layout-drag-bar:hover .libs-ui-grid-layout-drag-bar-dots{color:#3d6ef5}.libs-ui-grid-layout-drag-bar:not(.libs-ui-grid-layout-drag-bar-child){opacity:1}.libs-ui-grid-layout-drag-bar-child{top:auto;bottom:0;background:#d9f2e4;border-radius:4px 4px 0 0}.libs-ui-grid-layout-drag-bar-child .libs-ui-grid-layout-drag-bar-dots{color:#9ca2ad}.libs-ui-grid-layout-drag-bar-child:hover .libs-ui-grid-layout-drag-bar-dots{color:#00a757}.libs-ui-grid-layout-drag-bar-child{opacity:0}.libs-ui-grid-layout-block-shell:hover>.libs-ui-grid-layout-drag-bar-child{opacity:1}.libs-ui-grid-layout-block-shell:has(>.libs-ui-grid-layout-drag-bar:not(.libs-ui-grid-layout-drag-bar-child):hover){outline:2px solid #3d6ef5;outline-offset:-2px;border-radius:8px}.libs-ui-grid-layout-block-shell:has(>.libs-ui-grid-layout-drag-bar-child:hover){outline:2px solid #00a757;outline-offset:-2px;border-radius:8px}.libs-ui-grid-layout-container-label{position:absolute;left:6px;top:2px;display:flex;height:20px;align-items:center;z-index:3;padding:0 6px;color:#3d6ef5;font-weight:500;font-size:10px;line-height:14px;background:#dbe4ff;border-radius:4px;pointer-events:none}.libs-ui-grid-layout-content-frame{box-sizing:border-box;width:100%;height:100%;overflow:hidden;border-radius:6px}.libs-ui-grid-layout-content-frame ::ng-deep>*{display:block;box-sizing:border-box;width:100%;height:100%}.libs-ui-grid-layout-content-frame-empty{height:0}.libs-ui-grid-layout-content-frame-edit{border:1px dashed #c3cede}.libs-ui-grid-layout-container-border{position:absolute;inset:0;border:1px dashed #9db4f0;border-radius:8px;pointer-events:none}.libs-ui-grid-layout-toolbar{position:absolute;top:2px;right:8px;z-index:24;display:flex;gap:2px;align-items:center;height:20px;background:#fff;border-radius:4px;box-shadow:0 2px 8px #0716311f;opacity:0;transition:opacity .12s ease}.libs-ui-grid-layout-toolbar-child{top:50%;right:8px;left:auto;transform:translateY(-50%)}.libs-ui-grid-layout-block-shell:hover>.libs-ui-grid-layout-toolbar{opacity:1}:host ::ng-deep gridster{background:transparent}:host ::ng-deep .gridster-item-resizable-handler.handle-n,:host ::ng-deep .gridster-item-resizable-handler.handle-s{height:16px}:host ::ng-deep .gridster-item-resizable-handler.handle-e,:host ::ng-deep .gridster-item-resizable-handler.handle-w{width:16px}:host ::ng-deep .gridster-item-resizable-handler.handle-n{top:0}:host ::ng-deep .gridster-item-resizable-handler.handle-s{bottom:0}:host ::ng-deep .gridster-item-resizable-handler.handle-e{right:0}:host ::ng-deep .gridster-item-resizable-handler.handle-w{left:0}:host ::ng-deep gridster-item.gridster-item-moving,:host ::ng-deep gridster-item.gridster-item-resizing{z-index:30!important}:host ::ng-deep gridster-item.gridster-item-moving>.libs-ui-grid-layout-block-shell,:host ::ng-deep gridster-item.gridster-item-resizing>.libs-ui-grid-layout-block-shell{outline:2px solid #3d6ef5;outline-offset:-2px;border-radius:8px}:host ::ng-deep gridster gridster gridster-item.gridster-item-moving>.libs-ui-grid-layout-block-shell,:host ::ng-deep gridster gridster gridster-item.gridster-item-resizing>.libs-ui-grid-layout-block-shell{outline-color:#00a757}@container (max-width: 80px){.libs-ui-grid-layout-drag-bar{top:26px}}\n"] }]
|
|
192
|
-
}], ctorParameters: () => []
|
|
193
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC1sYXlvdXQtY2FudmFzLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvc2VydmljZXMvZ3JpZC1sYXlvdXQvc3JjL2NhbnZhcy9ncmlkLWxheW91dC1jYW52YXMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy11aS9zZXJ2aWNlcy9ncmlkLWxheW91dC9zcmMvY2FudmFzL2dyaWQtbGF5b3V0LWNhbnZhcy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFFVCxnQkFBZ0IsRUFDaEIsUUFBUSxFQUNSLE1BQU0sRUFDTixLQUFLLEVBQ0wsTUFBTSxFQUNOLE1BQU0sRUFDTixTQUFTLEVBQ1QsWUFBWSxHQUNiLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzVGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxjQUFjLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDL0QsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ2xFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLHdCQUF3QixDQUFDOzs7QUFTakU7Ozs7O0dBS0c7QUFTSCxNQUFNLE9BQU8sK0JBQStCO0lBQzFDOzs7OztPQUtHO0lBQ00sSUFBSSxHQUFHLEtBQUssRUFBcUIsQ0FBQztJQUMzQyw4REFBOEQ7SUFDckQsS0FBSyxHQUFHLEtBQUssQ0FBUyxDQUFDLENBQUMsQ0FBQztJQUVsQzs7Ozs7T0FLRztJQUNNLFNBQVMsR0FBRyxNQUFNLEVBQTRCLENBQUM7SUFDeEQ7OztPQUdHO0lBQ00sYUFBYSxHQUFHLE1BQU0sRUFBNEIsQ0FBQztJQUM1RDs7O09BR0c7SUFDTSxpQkFBaUIsR0FBRyxNQUFNLEVBQXlCLENBQUM7SUFDN0QsOEZBQThGO0lBQ3JGLGFBQWEsR0FBRyxNQUFNLEVBQTRCLENBQUM7SUFHM0MsV0FBVyxHQUFHLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0lBQzVELDZEQUE2RDtJQUM1QyxTQUFTLEdBQUcsWUFBWSxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxnQkFBZ0IsRUFBRSxDQUFDLENBQUM7SUFFbEYsc0ZBQXNGO0lBQ25FLFdBQVcsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFFbkMsT0FBTyxHQUFHLE1BQU0sQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0lBRTNEOzs7Ozs7Ozs7O09BVUc7SUFDZ0IsV0FBVyxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUU7UUFDN0MsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUV2QixJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNyQixPQUFPLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxhQUFhLEVBQUUsQ0FBQztRQUN4QyxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxJQUFJLGFBQWEsRUFBRSxDQUFDO0lBQ2hELENBQUMsQ0FBQyxDQUFDO0lBRWdCLFFBQVEsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUU3RCxjQUFjLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUVyRSxtRkFBbUY7SUFDaEUsaUJBQWlCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDO0lBRXpHOzs7Ozs7T0FNRztJQUNnQixnQkFBZ0IsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLGdCQUFnQixFQUFFLElBQUksQ0FBQyxDQUFDO0lBRTlHLDBGQUEwRjtJQUN2RSxVQUFVLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUV2RCxXQUFXLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRTtRQUM3Qyx1RkFBdUY7UUFDdkYsOERBQThEO1FBQzlELElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUM7UUFFdkIsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzlILENBQUMsQ0FBQyxDQUFDO0lBRUg7UUFDRSxxRUFBcUU7UUFDckUsOEVBQThFO1FBQzlFLE1BQU0sQ0FBQyxHQUFHLEVBQUU7WUFDVixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDL0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBRTlCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxFQUFFLEVBQUU7Z0JBQ3hCLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFFdEIsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO29CQUNWLE9BQU87Z0JBQ1QsQ0FBQztnQkFFRCx3RkFBd0Y7Z0JBQ3hGLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztvQkFDckMsT0FBTztnQkFDVCxDQUFDO2dCQUVELElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQ3pFLENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsV0FBVztRQUNULEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUM7WUFDbkMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3BELENBQUM7SUFDSCxDQUFDO0lBRUQsbUZBQW1GO0lBQ25GLElBQUksZUFBZTtRQUNqQixPQUFPO1lBQ0wsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsRUFBRSxjQUFjLEVBQUU7U0FDdEQsQ0FBQztJQUNKLENBQUM7SUFFUyxnQkFBZ0IsQ0FBQyxNQUFjLEVBQUUsSUFBdUI7UUFDaEUsT0FBTyxJQUFJLENBQUMsRUFBRSxDQUFDO0lBQ2pCLENBQUM7SUFFRDs7O09BR0c7SUFDSDs7Ozs7T0FLRztJQUNIOzs7O09BSUc7SUFDTyxhQUFhLENBQUMsS0FBWSxFQUFFLElBQXVCO1FBQzNELEtBQUssQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUN4QixLQUFLLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDdkIsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDdkcsQ0FBQztJQUVTLGdCQUFnQixDQUFDLEtBQVksRUFBRSxJQUF1QjtRQUM5RCxLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7UUFDeEIsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUM7WUFDMUIsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFO1lBQ2YsSUFBSTtZQUNKLE9BQU8sRUFBRSxDQUFDLElBQTBCLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsRUFBRSxDQUFDO1NBQzlFLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFUyxpQkFBaUIsQ0FBQyxLQUFZLEVBQUUsSUFBdUI7UUFDL0QsS0FBSyxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ3hCLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQztZQUN0QixNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUU7WUFDZixJQUFJO1lBQ0osZ0ZBQWdGO1lBQ2hGLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1NBQzdHLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxxREFBcUQ7SUFDM0MsV0FBVyxDQUFDLElBQXVCO1FBQzNDLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDMUMsQ0FBQztJQUVELHdFQUF3RTtJQUM5RCxZQUFZLENBQUMsSUFBdUI7UUFDNUMsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUMzRSxDQUFDO0lBRUQsb0VBQW9FO0lBQzFELGFBQWEsQ0FBQyxJQUF1QjtRQUM3QyxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUMzQixPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUNqRCxDQUFDO0lBRVMsc0JBQXNCLENBQUMsS0FBWTtRQUMzQyxJQUFJLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxFQUFFLENBQUM7WUFDM0IsT0FBTztRQUNULENBQUM7UUFFRCxLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7SUFDMUIsQ0FBQztJQUVTLHlCQUF5QixDQUFDLEtBQVk7UUFDOUMsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDdkIsT0FBTztRQUNULENBQUM7UUFFRCxLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7SUFDMUIsQ0FBQzt3R0E3TVUsK0JBQStCOzRGQUEvQiwrQkFBK0Isa2hCQWdDRCxpQkFBaUIsc0hBRUksZ0JBQWdCLDZDQ3pFaEYsZzNXQXlJQSx3NEhEbEdhLCtCQUErQix5TEFMaEMsY0FBYyxvUkFBRSxzQ0FBc0M7OzRGQUtyRCwrQkFBK0I7a0JBUjNDLFNBQVM7K0JBQ0UscUNBQXFDLGNBQ25DLElBQUksV0FDUCxDQUFDLGNBQWMsRUFBRSxzQ0FBc0MsQ0FBQyxtQkFHaEQsdUJBQXVCLENBQUMsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICBDb21wb25lbnQsXG4gIE9uRGVzdHJveSxcbiAgVmlld0NvbnRhaW5lclJlZixcbiAgY29tcHV0ZWQsXG4gIGluamVjdCxcbiAgaW5wdXQsXG4gIG91dHB1dCxcbiAgZWZmZWN0LFxuICB2aWV3Q2hpbGQsXG4gIHZpZXdDaGlsZHJlbixcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBMaWJzVWlDb21wb25lbnRzQnV0dG9uc0J1dHRvbkNvbXBvbmVudCB9IGZyb20gJ0BsaWJzLXVpL2NvbXBvbmVudHMtYnV0dG9ucy1idXR0b24nO1xuaW1wb3J0IHsgR3JpZHN0ZXJDb21wb25lbnQsIEdyaWRzdGVyTW9kdWxlIH0gZnJvbSAnZ3JpZHN0ZXIxOCc7XG5pbXBvcnQgeyBlbXB0eVJvb3ROb2RlIH0gZnJvbSAnLi4vZGVmaW5lcy9ncmlkc3Rlci1jb25maWcuZGVmaW5lJztcbmltcG9ydCB7IExpYnNVaUdyaWRMYXlvdXRTZXJ2aWNlIH0gZnJvbSAnLi4vZ3JpZC1sYXlvdXQuc2VydmljZSc7XG5pbXBvcnQge1xuICBUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQsXG4gIFRfZ3JpZExheW91dF9hZGRFdmVudCxcbiAgVF9ncmlkTGF5b3V0X2NoYW5nZUV2ZW50LFxuICBUX2dyaWRMYXlvdXRfbmV3Tm9kZSxcbiAgVF9ncmlkTGF5b3V0X25vZGUsXG59IGZyb20gJy4uL2ludGVyZmFjZXMvZ3JpZC1sYXlvdXQuaW50ZXJmYWNlJztcblxuLyoqXG4gKiBN4bq3dCBwaOG6s25nIGvDqW8gdGjhuqMuIMSQ4buHIHF1eToga2jhu5FpIGPDsyBgY2hpbGRyZW5gIHRow6wgcnXhu5l0IG7DsyBs4bqhaSBsw6AgbeG7mXQgbeG6t3QgcGjhurNuZyBu4buvYS5cbiAqXG4gKiBOxqFpIGTDuW5nIEtIw5RORyBk4buxbmcgY29tcG9uZW50IG7DoHkgdHLhu7FjIHRp4bq/cCBjaG8gdOG7q25nIGto4buRaSDigJQgY2jhu4kgxJHhurd0IE3hu5hUIHRo4bq7IOG7nyBn4buRYyB2w6AgdHJ1eeG7gW5cbiAqIGBub2RlQ29tcG9uZW50VHlwZWAgcXVhIGBMaWJzVWlHcmlkTGF5b3V0U2VydmljZS5pbml0KClgLiBUaMawIHZp4buHbiB04buxIGfhuq9uIGNvbXBvbmVudCBjaG8gbeG7jWkga2jhu5FpLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdsaWJzX3VpLXNlcnZpY2VzLWdyaWRfbGF5b3V0LWNhbnZhcycsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtHcmlkc3Rlck1vZHVsZSwgTGlic1VpQ29tcG9uZW50c0J1dHRvbnNCdXR0b25Db21wb25lbnRdLFxuICB0ZW1wbGF0ZVVybDogJy4vZ3JpZC1sYXlvdXQtY2FudmFzLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmw6ICcuL2dyaWQtbGF5b3V0LWNhbnZhcy5jb21wb25lbnQuc2NzcycsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBMaWJzVWlHcmlkTGF5b3V0Q2FudmFzQ29tcG9uZW50IGltcGxlbWVudHMgT25EZXN0cm95IHtcbiAgLyoqXG4gICAqIEBpbnRlcm5hbCBDaOG7iSBt4bq3dCBwaOG6s25nIEzhu5JORyBkw7luZyDigJQgdGjGsCB2aeG7h24gdOG7sSB0cnV54buBbiBraGkgduG6vSBydeG7mXQgY29udGFpbmVyLlxuICAgKlxuICAgKiBOxqFpIGTDuW5nIEtIw5RORyB0cnV54buBbjogbeG6t3QgcGjhurNuZyBn4buRYyDEkeG7jWMgY8OieSB04burIHNlcnZpY2UgKMSRw6MgxJHGsGEgdsOgbyBxdWEgYGluaXQoKWApLCB0cnV54buBbiB0aMOqbVxuICAgKiDhu58gxJHDonkgbMOgIGtoYWkgaGFpIGzhuqduIGPDuW5nIG3hu5l0IHRo4bupIChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNikuXG4gICAqL1xuICByZWFkb25seSBub2RlID0gaW5wdXQ8VF9ncmlkTGF5b3V0X25vZGU+KCk7XG4gIC8qKiBAaW50ZXJuYWwgQ2jhu4kgbeG6t3QgcGjhurNuZyBM4buSTkcgZMO5bmcg4oCUIHRoxrAgdmnhu4duIHThu7EgdHJ1eeG7gW4uICovXG4gIHJlYWRvbmx5IGRlcHRoID0gaW5wdXQ8bnVtYmVyPigwKTtcblxuICAvKipcbiAgICogQuG7kSBj4bulYyB24burYSDEkeG7lWkgZG8gTkfGr+G7nEkgRMOZTkcga8OpbyBob+G6t2MgxJHhu5VpIGvDrWNoIHRoxrDhu5tjIGto4buRaS5cbiAgICpcbiAgICogTsahaSBkw7luZyB04buxIGfhu41pIGBhZGRCbG9ja2AgwrcgYHJlbW92ZUJsb2NrYCDCtyBgY29tcGFjdGAgdGjDrCBLSMOUTkcgYuG6r24g4oCUIGjhu40gduG7q2EgZ+G7jWkgdGjDrCDEkcOjIGJp4bq/dCxcbiAgICogYuG6r24gbmfGsOG7o2MgbOG6oWkgbMOgIHRo4burYSAobmfGsOG7nWkgZMO5bmcgY2jhu5F0IDI4LzA4LzIwMjYpLlxuICAgKi9cbiAgcmVhZG9ubHkgb3V0Q2hhbmdlID0gb3V0cHV0PFRfZ3JpZExheW91dF9jaGFuZ2VFdmVudD4oKTtcbiAgLyoqXG4gICAqIE5nxrDhu51pIGTDuW5nIGLhuqVtIG7DunQgWE/DgSB0csOqbiBt4buZdCBraOG7kWkuIFRoxrAgdmnhu4duIENIxq9BIHhvw6Eg4oCUIGfhu41pIGBldmVudC5jb25maXJtKClgIMSR4buDIHhvw6EgdGjhuq10LlxuICAgKiBD4bqnbiBo4buPaSB0csaw4bubYyB0aMOsIGjhu49pIHhvbmcgbeG7m2kgZ+G7jWk7IGtow7RuZyBjaG8gcGjDqXAgdGjDrCDEkeG7q25nIGfhu41pLlxuICAgKi9cbiAgcmVhZG9ubHkgb3V0UmVtb3ZlTm9kZSA9IG91dHB1dDxUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQ+KCk7XG4gIC8qKlxuICAgKiBOZ8aw4budaSBkw7luZyBi4bqlbSBuw7p0IFRIw4pNIHRyw6puIG3hu5l0IGNvbnRhaW5lci4gVGjGsCB2aeG7h24gS0jDlE5HIHThu7EgdGjDqm0g4oCUIG7DsyBraMO0bmcgYmnhur90IGLhuqFuIGPDs1xuICAgKiBuaOG7r25nIGxv4bqhaSBraOG7kWkgbsOgby4gTeG7nyBtZW51IGNo4buNbiBsb+G6oWkgcuG7k2kgZ+G7jWkgYGV2ZW50LmNvbmZpcm0oaXRlbSlgIHbhu5tpIGto4buRaSBi4bqhbiBtdeG7kW4gdGjDqm0uXG4gICAqL1xuICByZWFkb25seSBvdXRBZGRCbG9ja0luc2lkZSA9IG91dHB1dDxUX2dyaWRMYXlvdXRfYWRkRXZlbnQ+KCk7XG4gIC8qKiBOZ8aw4budaSBkw7luZyBi4bqlbSBuw7p0IEfhu5hQL1TDgUNILiBUaMawIHZp4buHbiBjaMawYSBsw6BtIGfDrCDigJQgZ+G7jWkgYGV2ZW50LmNvbmZpcm0oKWAgxJHhu4MgdGjhu7FjIGhp4buHbi4gKi9cbiAgcmVhZG9ubHkgb3V0VG9nZ2xlVHlwZSA9IG91dHB1dDxUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQ+KCk7XG5cblxuICBwcml2YXRlIHJlYWRvbmx5IGdyaWRzdGVyUmVmID0gdmlld0NoaWxkKEdyaWRzdGVyQ29tcG9uZW50KTtcbiAgLyoqIG3hu5dpIMO0IG3hu5l0IGNo4buXIGfhuq9uIHJpw6puZyDigJQgY8O5bmcgdGjhu6kgdOG7sSB24bubaSBgY2hpbGRyZW4oKWAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBpdGVtSG9zdHMgPSB2aWV3Q2hpbGRyZW4oJ2l0ZW1Ib3N0JywgeyByZWFkOiBWaWV3Q29udGFpbmVyUmVmIH0pO1xuXG4gIC8qKiBTw6F1IGNo4bqlbSBj4bunYSB0YXkgY+G6p20ga8OpbyDigJQgY2jhu4kgxJHhu4MgYEBmb3JgIGThu7FuZyDEkeG7pyA2IHRo4bq7LCBnacOhIHRy4buLIGtow7RuZyBkw7luZyB04bubaS4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGRyYWdCYXJEb3RzID0gWzAsIDEsIDIsIDMsIDQsIDVdO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgc2VydmljZSA9IGluamVjdChMaWJzVWlHcmlkTGF5b3V0U2VydmljZSk7XG5cbiAgLyoqXG4gICAqIE5vZGUgVEjhuqxUIGPhu6dhIG3hurd0IHBo4bqzbmcgbsOgeS5cbiAgICpcbiAgICog8J+UtCBN4bq3dCBwaOG6s25nIEfhu5BDIChkZXB0aCAwKSDEkeG7jWMgY8OieSB04burIFNFUlZJQ0UsIGtow7RuZyBkw7luZyBpbnB1dC5cbiAgICogTsahaSBkw7luZyB0aMaw4budbmcgc+G7rWEgY8OieSBU4bqgSSBDSOG7liAoYHRyZWVTZXJ2aWNlLmNvbXBhY3QodHJlZSlgKSBy4buTaSBn4buNaSBgcmVmcmVzaCgpYCwgbsOqbiBzaWduYWxcbiAgICogYHJvb3QoKWAgY+G7p2EgaOG7jSBnaeG7ryBuZ3V5w6puIHRoYW0gY2hp4bq/dSB2w6AgaW5wdXQgYG5vZGVgIGtow7RuZyBo4buBIMSR4buVaSDigJQgYEBmb3JgIGtow7RuZyBk4buxbmcgbOG6oWlcbiAgICogYGdyaWRzdGVyLWl0ZW1gLCBsxrDhu5tpIMSR4bupbmcgaW0gZMO5IGThu68gbGnhu4d1IMSRw6Mga2jDoWMgKMSRbyAyOC8wOC8yMDI2OiBi4bqlbSBcIlPhuq9wIGfhu41uXCIga2jDtG5nIHTDoWMgZOG7pW5nKS5cbiAgICogYHJlZnJlc2goKWAgY+G7p2Egc2VydmljZSB0aGF5IHRoYW0gY2hp4bq/dSBjw6J5LCBuw6puIMSR4buNYyB04burIMSRw7MgbeG7m2kgdGjhuqV5IHRoYXkgxJHhu5VpLlxuICAgKlxuICAgKiBN4bq3dCBwaOG6s25nIEzhu5JORyB24bqrbiBkw7luZyBpbnB1dDogbsOzIG5o4bqtbiBub2RlIGNvbiB04burIG3hurd0IHBo4bqzbmcgY2hhLCB24buRbiDEkcOjIGzDoCBvYmplY3QgbeG7m2kuXG4gICAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY3VycmVudE5vZGUgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgdGhpcy5zZXJ2aWNlLlZlcnNpb24oKTtcblxuICAgIGlmICh0aGlzLmRlcHRoKCkgPiAwKSB7XG4gICAgICByZXR1cm4gdGhpcy5ub2RlKCkgPz8gZW1wdHlSb290Tm9kZSgpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnNlcnZpY2UuUm9vdCgpID8/IGVtcHR5Um9vdE5vZGUoKTtcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNoaWxkcmVuID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jdXJyZW50Tm9kZSgpLmNoaWxkcmVuID8/IFtdKTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaXNOZXN0ZWRDYW52YXMgPSBjb21wdXRlZCgoKSA9PiB0aGlzLmRlcHRoKCkgPiAwKTtcblxuICAvKiogdHLhuqduIGzhu5NuZyBjb250YWluZXIg4oCUIGto4buRaSDEkcOjIGNo4bqhbSB0cuG6p24gdGjDrCBraMO0bmcgY2hvIGNodXnhu4NuIHRow6BuaCBjb250YWluZXIgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IG1heENvbnRhaW5lckRlcHRoID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wubWF4Q29udGFpbmVyRGVwdGgoKSk7XG5cbiAgLyoqXG4gICAqIEzhu4EgdHJvbmcgY+G7p2EgY29udGFpbmVyLCBweC5cbiAgICpcbiAgICog8J+UtCDEkOG6t3QgdHLDqm4gVuG7jiBjb250YWluZXIsIGtow7RuZyDEkeG6t3QgdsOgbyBsxrDhu5tpIGNvbi4gTMaw4bubaSBjb24gZ2nhu68gbmd1ecOqbiBgbWFyZ2luOiAyYCBuaMawIGPFqTsgbOG7gVxuICAgKiBuw6B5IGNo4buJIGNvIHJ14buZdCBs4bqhaSBraOG7j2kgYmnDqm4gduG7jywgbmjhu50gxJHDsyB0YXkgY+G6p20gxJHhu5VpIGvDrWNoIHRoxrDhu5tjIGPhu6dhIGto4buRaSBjb24gbHXDtG4gbuG6sW0gdHJvbmcgdOG6p21cbiAgICogY2h14buZdCAobmfGsOG7nWkgZMO5bmcgY2jhu5F0IDI4LzA4LzIwMjYpLlxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNvbnRhaW5lclBhZGRpbmcgPSBjb21wdXRlZCgoKSA9PiBgJHt0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5jb250YWluZXJQYWRkaW5nKCl9cHhgKTtcblxuICAvKiogROG6o2kga8OpbyBjaOG7iSBoaeG7h24g4bufIGNo4bq/IMSR4buZIHPhu61hIOKAlCBjaOG6vyDEkeG7mSB4ZW0ga2jDtG5nIGvDqW8gxJHGsOG7o2MgbsOqbiBoaeG7h24gZOG6o2kgbMOgIGfDonkgbmjhuqdtLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaXNFZGl0TW9kZSA9IGNvbXB1dGVkKCgpID0+IHRoaXMuc2VydmljZS5Jc0VkaXRNb2RlKCkpO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBncmlkT3B0aW9ucyA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICAvLyDEkOG7jWMgYFZlcnNpb25gIMSR4buDIGBpbml0KClgIGzhuqFpICjEkeG7lWkgY2jhur8gxJHhu5ksIGLhuq10L3Thuq90IGNo4buTbmcga2jhu5FpKSBzaW5oIHJhIG9wdGlvbnMgTeG7mkkg4oCUXG4gICAgLy8gZ3JpZHN0ZXIgY2jhu4kgw6FwIGThu6VuZyBj4bqldSBow6xuaCBraGkgdGhhbSBjaGnhur91IGBvcHRpb25zYCDEkeG7lWkuXG4gICAgdGhpcy5zZXJ2aWNlLlZlcnNpb24oKTtcblxuICAgIHJldHVybiB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5ncmlkc3Rlck9wdGlvbnModGhpcy5kZXB0aCgpLCAoKSA9PiB0aGlzLm91dENoYW5nZS5lbWl0KHsgcm9vdDogdGhpcy5jdXJyZW50Tm9kZSgpIH0pKTtcbiAgfSk7XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgLy8gR+G6r24gY29tcG9uZW50IGPhu6dhIG7GoWkgZMO5bmcgdsOgbyB04burbmcgw7QsIHbDoCBn4bqvbiBM4bqgSSBt4buXaSBraGkgY8OieSDEkeG7lWkuXG4gICAgLy8gRMO5bmcgZWZmZWN0IHbDrCBgaXRlbUhvc3RzKClgIGNo4buJIGPDsyBnacOhIHRy4buLIHNhdSBraGkgQW5ndWxhciBk4buxbmcgeG9uZyBAZm9yLlxuICAgIGVmZmVjdCgoKSA9PiB7XG4gICAgICBjb25zdCBob3N0cyA9IHRoaXMuaXRlbUhvc3RzKCk7XG4gICAgICBjb25zdCBpdGVtcyA9IHRoaXMuY2hpbGRyZW4oKTtcblxuICAgICAgaXRlbXMuZm9yRWFjaCgoaXRlbSwgaSkgPT4ge1xuICAgICAgICBjb25zdCBob3N0ID0gaG9zdHNbaV07XG5cbiAgICAgICAgaWYgKCFob3N0KSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gS2jhu5FpIGPDsyBjb24gbMOgIENPTlRBSU5FUjogcnXhu5l0IG7DsyBkbyBt4bq3dCBwaOG6s25nIGzhu5NuZyB24bq9LCBraMO0bmcgZ+G6r24gY29tcG9uZW50IG7hu5lpIGR1bmcuXG4gICAgICAgIGlmICgoaXRlbS5jaGlsZHJlbj8ubGVuZ3RoID8/IDApID4gMCkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmF0dGFjaE5vZGUoaXRlbSwgdGhpcy5kZXB0aCgpICsgMSwgaG9zdCk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIGZvciAoY29uc3QgaXRlbSBvZiB0aGlzLmNoaWxkcmVuKCkpIHtcbiAgICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmRldGFjaE5vZGUoaXRlbS5pZCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFbhur0gbOG6oWkgbMaw4bubaSDigJQgZ+G7jWkga2hpIGtodW5nIG5nb8OgaSDEkeG7lWkga8OtY2ggdGjGsOG7m2MgaG/hurdjIGPDonkgduG7q2EgxJHhu5VpIGRp4buHbiBy4buZbmcuICovXG4gIGdldCBGdW5jdGlvbkNvbnRyb2woKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHZlTGFpTHVvaTogKCkgPT4gdGhpcy5ncmlkc3RlclJlZigpPy5vcHRpb25zQ2hhbmdlZCgpLFxuICAgIH07XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlclRyYWNrTm9kZShfaW5kZXg6IG51bWJlciwgaXRlbTogVF9ncmlkTGF5b3V0X25vZGUpOiBzdHJpbmcge1xuICAgIHJldHVybiBpdGVtLmlkO1xuICB9XG5cbiAgLyoqXG4gICAqIPCflLQgQ2jhurduIGBtb3VzZWRvd25gIG7hu5VpIGzDqm4gbMaw4bubaSBDSEEga2hpIHRoYW8gdMOhYyB0cm9uZyBsxrDhu5tpIGNvbi5cbiAgICogS2jDtG5nIGNo4bq3biB0aMOsIGPhuqMgaGFpIGzGsOG7m2kgY8O5bmcgbmjhuq1uIG3hu5l0IGPDuiBi4bqlbToga8OpbyBraOG7kWkgY29uIGzDoCBuaOG6pWMgbHXDtG4gY29udGFpbmVyLlxuICAgKi9cbiAgLyoqXG4gICAqIENo4bq3biBgbW91c2Vkb3duYCBj4bunYSBraOG7kWkgdHJvbmcgbMaw4bubaSBM4buSTkcgbuG7lWkgbMOqbiBsxrDhu5tpIGNoYS5cbiAgICpcbiAgICogQ2jhu4kgY2jhurduIOG7nyBsxrDhu5tpIGzhu5NuZzogbMaw4bubaSBj4bqlcCB0cmFuZyBraMO0bmcgY8OzIGNoYSBuw6BvIMSR4buDIGNo4bq3biwgdsOgIGNo4bq3biDhu58gxJHDsyBz4bq9IG514buRdCBsdcO0biB0aGFvXG4gICAqIHTDoWMgY+G7p2EgY2jDrW5oIG7Dsy5cbiAgICovXG4gIC8qKlxuICAgKiDwn5S0IEJhIGhhbmRsZXIgZMaw4bubaSDEkcOieSBQSOG6okkgYHN0b3BQcm9wYWdhdGlvbmAuXG4gICAqIE7DunQgbuG6sW0gdHJvbmcgdsO5bmcgbWFuZyBgZHJhZ0hhbmRsZUNsYXNzYCwga2jDtG5nIGNo4bq3biB0aMOsIGPDuiBi4bqlbSBi4buLIGdyaWRzdGVyIGhp4buDdSBsw6AgYuG6r3QgxJHhuqd1IGvDqW9cbiAgICogdsOgIGBkZWxheVN0YXJ0OiAxNjBgIG514buRdCBsdcO0biBgY2xpY2tgIOKAlCBuZ8aw4budaSBkw7luZyB0aOG6pXkgbmjGsCBuw7p0IGNo4bq/dC5cbiAgICovXG4gIHByb3RlY3RlZCBoYW5kbGVyUmVtb3ZlKGV2ZW50OiBFdmVudCwgbm9kZTogVF9ncmlkTGF5b3V0X25vZGUpOiB2b2lkIHtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIHRoaXMub3V0UmVtb3ZlTm9kZS5lbWl0KHsgbm9kZUlkOiBub2RlLmlkLCBub2RlLCBjb25maXJtOiAoKSA9PiB0aGlzLnNlcnZpY2UucmVtb3ZlQmxvY2sobm9kZS5pZCkgfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckFkZEluc2lkZShldmVudDogRXZlbnQsIG5vZGU6IFRfZ3JpZExheW91dF9ub2RlKTogdm9pZCB7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICB0aGlzLm91dEFkZEJsb2NrSW5zaWRlLmVtaXQoe1xuICAgICAgbm9kZUlkOiBub2RlLmlkLFxuICAgICAgbm9kZSxcbiAgICAgIGNvbmZpcm06IChpdGVtOiBUX2dyaWRMYXlvdXRfbmV3Tm9kZSkgPT4gdGhpcy5zZXJ2aWNlLmFkZEJsb2NrKGl0ZW0sIG5vZGUuaWQpLFxuICAgIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJUb2dnbGVUeXBlKGV2ZW50OiBFdmVudCwgbm9kZTogVF9ncmlkTGF5b3V0X25vZGUpOiB2b2lkIHtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIHRoaXMub3V0VG9nZ2xlVHlwZS5lbWl0KHtcbiAgICAgIG5vZGVJZDogbm9kZS5pZCxcbiAgICAgIG5vZGUsXG4gICAgICAvLyBH4buZcCBoYXkgdMOhY2ggc3V5IHJhIHThu6sgY2jDrW5oIGto4buRaTogxJFhbmcgbMOgIGNvbnRhaW5lciB0aMOsIHTDoWNoLCBraMO0bmcgdGjDrCBn4buZcC5cbiAgICAgIGNvbmZpcm06ICgpID0+ICh0aGlzLmlzQ29udGFpbmVyKG5vZGUpID8gdGhpcy5zZXJ2aWNlLnRvU2luZ2xlKG5vZGUuaWQpIDogdGhpcy5zZXJ2aWNlLnRvQ29udGFpbmVyKG5vZGUuaWQpKSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBraOG7kWkgbsOgeSBjw7MgcGjhuqNpIGNvbnRhaW5lciBraMO0bmcgKGPDsyBraOG7kWkgY29uKSAqL1xuICBwcm90ZWN0ZWQgaXNDb250YWluZXIoaXRlbTogVF9ncmlkTGF5b3V0X25vZGUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKGl0ZW0uY2hpbGRyZW4/Lmxlbmd0aCA/PyAwKSA+IDA7XG4gIH1cblxuICAvKiogY29udGFpbmVyIMSRw6MgY2jhuqFtIHRy4bqnbiBs4buTbmcgdGjDrCBraMO0bmcgY2hvIHRow6ptIGto4buRaSB2w6BvIHRyb25nIG7hu69hICovXG4gIHByb3RlY3RlZCBjYW5BZGRJbnNpZGUoaXRlbTogVF9ncmlkTGF5b3V0X25vZGUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pc0NvbnRhaW5lcihpdGVtKSAmJiB0aGlzLmRlcHRoKCkgPCB0aGlzLm1heENvbnRhaW5lckRlcHRoKCk7XG4gIH1cblxuICAvKioga2jhu5FpIMSRxqFuIOG7nyBj4bqlcCBjaMawYSBjaOG6oW0gdHLhuqduIG3hu5tpIMSRxrDhu6NjIGNodXnhu4NuIHRow6BuaCBjb250YWluZXIgKi9cbiAgcHJvdGVjdGVkIGNhblRvZ2dsZVR5cGUoaXRlbTogVF9ncmlkTGF5b3V0X25vZGUpOiBib29sZWFuIHtcbiAgICBpZiAodGhpcy5pc0NvbnRhaW5lcihpdGVtKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuZGVwdGgoKSA8IHRoaXMubWF4Q29udGFpbmVyRGVwdGgoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyQmxvY2tQYXJlbnREcmFnKGV2ZW50OiBFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc05lc3RlZENhbnZhcygpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlck1vdXNlRG93blRyb25nTHVvaShldmVudDogRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5kZXB0aCgpID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gIH1cbn1cbiIsIjwhLS0g8J+UtCBgcmVsYXRpdmVgIGzDoCBC4bquVCBCVeG7mEMsIEPhuqRNIGLhu48uXG4gICAgIGdyaWRzdGVyIMSR4buVaSB24buLIHRyw60gY2h14buZdCB0aMOgbmggw7QgbMaw4bubaSBi4bqxbmcgYGUuY2xpZW50WSArIChlbC5zY3JvbGxUb3AgLSBlbC5vZmZzZXRUb3ApYCDigJQgY8O0bmdcbiAgICAgdGjhu6ljIMSRw7MgY2jhu4kgxJHDum5nIGtoaSBgb2Zmc2V0UGFyZW50YCBj4bunYSA8Z3JpZHN0ZXI+IENIw41OSCBMw4Aga2jhu5FpIGN14buZbiBuw6B5LiBUaGnhur91IGByZWxhdGl2ZWAgdGjDrFxuICAgICBgb2Zmc2V0UGFyZW50YCBuaOG6o3kgbMOqbiBraOG7kWkgYmFvIG5nb8OgaTogYG9mZnNldFRvcGAgdHLhuqMgMTYgdHJvbmcga2hpIGzhu4djaCB0aOG6rXQgbMOgIDY1IOKGkiBncmlkc3RlclxuICAgICB0w61uaCDDtCBzYWkgNDlweCwgw7QgY2jhu50gxJHhurd0IGhp4buHbiBs4buHY2gga2jhu49pIGNvbiB0cuG7jyBuw6puIGvDqW8gdGjhuqMga2jDtG5nIMSR4buVaSDEkcaw4bujYyB24buLIHRyw60sIHbDoCBrw6lvIG3DqXBcbiAgICAgY8WpbmcgdMOtbmggc2FpIMSRaeG7g20gZOG7q25nLiAtLT5cbjxkaXZcbiAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LXNjcm9sbC1hcmVhIHJlbGF0aXZlIGgtZnVsbCBtaW4taC0wIHctZnVsbCBtaW4tdy0wXCJcbiAgW2NsYXNzLm92ZXJmbG93LXktYXV0b109XCIhaXNOZXN0ZWRDYW52YXMoKVwiXG4gIChtb3VzZWRvd24pPVwiaGFuZGxlck1vdXNlRG93blRyb25nTHVvaSgkZXZlbnQpXCI+XG4gIDxncmlkc3RlciBjbGFzcz1cImgtZnVsbCB3LWZ1bGxcIiBbb3B0aW9uc109XCJncmlkT3B0aW9ucygpXCI+XG4gICAgQGZvciAoaXRlbSBvZiBjaGlsZHJlbigpOyB0cmFjayBpdGVtLmlkKSB7XG4gICAgICA8IS0tIPCflLQgQ2jhurduIGBtb3VzZWRvd25gIHThuqFpIMSQw4JZIGNobyBsxrDhu5tpIGzhu5NuZyDigJQgQ+G6pE0gYuG7jyB2w6AgQ+G6pE0gY2h1eeG7g24gc2FuZyBjaOG7lyBraMOhYy5cbiAgICAgICAgICAgYGlnbm9yZUNvbnRlbnRDbGFzc2AgbeG7mXQgbcOsbmggS0jDlE5HIMSR4bunOiBgZGVsYXlTdGFydDogMTYwYCBsw6BtIE3hu5ZJIGzGsOG7m2kgbMOqbiBs4buLY2hcbiAgICAgICAgICAgYGRyYWdTdGFydGAgYuG6sW5nIGBzZXRUaW1lb3V0YCBuZ2F5IHThuqFpIGBtb3VzZWRvd25gLCBjw7JuIGBzdG9wUHJvcGFnYXRpb25gIG3DoCBsaWIgZ+G7jWkgYsOqblxuICAgICAgICAgICB0cm9uZyBgZHJhZ1N0YXJ0YCBjaOG6oXkgc2F1IDE2MG1zIG7Dqm4gcXXDoSBtdeG7mW4g4oCUIGzGsOG7m2kgQ0hBIMSRw6Mga+G7i3Agbmjhuq1uICjEkW8gMjgvMDgvMjAyNjoga8Opb1xuICAgICAgICAgICBk4bqjaSBraOG7kWkgY29uLCBjaHXhu5dpIGNsYXNzIGR1eeG7h3QgbMOqbiDEkMOaTkcgbcOgIGNvbnRhaW5lciB24bqrbiBuaOG6o3kgMCwwIOKGkiAwLDEpLlxuICAgICAgICAgICBH4bqvbiDhu58gYGdyaWRzdGVyLWl0ZW1gIGzDoCDEkcO6bmcgYmnDqm46IGzGsOG7m2kgY29uIG5naGUgdHLDqm4gY2jDrW5oIHBo4bqnbiB04butIG7DoHkgbsOqbiDEkcOjIG5o4bqtbiB4b25nLFxuICAgICAgICAgICBsxrDhu5tpIGNoYSDhu58gbmdvw6BpIGLhu4sgY2jhurduLiAtLT5cbiAgICAgIDxncmlkc3Rlci1pdGVtXG4gICAgICAgIFtpdGVtXT1cIml0ZW1cIlxuICAgICAgICAobW91c2Vkb3duKT1cImhhbmRsZXJCbG9ja1BhcmVudERyYWcoJGV2ZW50KVwiPlxuICAgICAgICA8IS0tIPCflLQgVuG7jyBuw6B5IG1hbmcgYGRyYWdIYW5kbGVDbGFzc2Ag4oCUIEPhuqRNIGLhu48uXG4gICAgICAgICAgICAgYGlnbm9yZUNvbnRlbnQ6IHRydWVgIGLhuq90IGdyaWRzdGVyIENI4buIIGNobyBrw6lvIGtoaSBjaOG7lyBi4bqlbSBu4bqxbSBkxrDhu5tpIG3hu5l0IHBo4bqnbiB04butIGPDs1xuICAgICAgICAgICAgIGBkcmFnSGFuZGxlQ2xhc3NgLiBLaMO0bmcgY8OzIHbhu48gbsOgeSB0aMOsIGNvbXBvbmVudCBj4bunYSBuxqFpIGTDuW5nICh0aMawIHZp4buHbiBraMO0bmcga2nhu4NtIHNvw6F0XG4gICAgICAgICAgICAgxJHGsOG7o2MgY2xhc3MgY+G7p2EgbsOzKSBz4bq9IGtow7RuZyBjw7MgdGF5IGPhuqdtIG7DoG8sIHbDoCBLSMOUTkcga2jhu5FpIG7DoG8ga8OpbyDEkcaw4bujYyDigJQgxJFvIDI4LzA4LzIwMjY6XG4gICAgICAgICAgICAga8OpbyAzMjBweCwgdG/huqEgxJHhu5kga2jhu5FpIGdp4buvIG5ndXnDqm4geD0zLHk9MC5cbiAgICAgICAgICAgICBUw6puIGNsYXNzIHRoZW8gY+G6pXA6IGzGsOG7m2kgdHJhbmcgdsOgIGzGsOG7m2kgbOG7k25nIGTDuW5nIGhhaSB0w6puIEtIw4FDIG5oYXUsIG7hur91IGtow7RuZyBsxrDhu5tpIGNoYVxuICAgICAgICAgICAgIGPFqW5nIG5o4bqtbiByYSB0YXkgY+G6p20gY+G7p2Ega2jhu5FpIGNvbiB2w6AgbmjhuqVjIGx1w7RuIGNvbnRhaW5lci4gLS0+XG4gICAgICAgIDwhLS0g8J+UtCBW4buPIEtIw5RORyBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgIOKAlCBD4bqkTSB0aMOqbSBs4bqhaS5cbiAgICAgICAgICAgICBLw6lvIGNo4buJIMSRxrDhu6NjIHBow6lwIGLhuq90IMSR4bqndSB04burIEThuqJJIFhBTkggKG5nxrDhu51pIGTDuW5nIGNo4buRdCAyOC8wOC8yMDI2OiBcInBo4bqjaSBob3ZlciB2w6BvIHbDuW5nXG4gICAgICAgICAgICAgbcOgdSB4YW5oIHRow6wgbeG7m2kga8OpbyB0aOG6oyDEkcaw4bujYyBjaG8gxJHhu5NuZyBuaOG6pXRcIikuIENobyBj4bqjIHbhu48gbMOgbSB0YXkgY+G6p20gdGjDrCBi4bqlbSDEkcOidSBjxaluZ1xuICAgICAgICAgICAgIGvDqW8sIHbDoCBraOG7kWkgY29uIG7hurFtIHRyb25nIGNvbnRhaW5lciBz4bq9IG5o4bqlYyBsdcO0biBjb250YWluZXIgdsOsIGzGsOG7m2kgY2hhIGR1eeG7h3QgbMOqbiBn4bq3cFxuICAgICAgICAgICAgIHRheSBj4bqnbSBj4bunYSB24buPIGNvbnRhaW5lciB0csaw4bubYy5cbiAgICAgICAgICAgICBW4buPIGto4buRaSBjb24gduG6q24gZ2nhu68gY2xhc3MgQ0jhurZOIMSR4buDIGzGsOG7m2kgY2hhIGThu6tuZyDEkcO6bmcg4bufIGJpw6puLiAtLT5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1ibG9jay1zaGVsbCBncm91cCByZWxhdGl2ZSBoLWZ1bGwgdy1mdWxsIG1pbi13LTBcIlxuICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWJsb2NrLXBhcmVudC1kcmFnXT1cImlzTmVzdGVkQ2FudmFzKClcIj5cbiAgICAgICAgICA8IS0tIEThuqNpIGvDqW86IGhp4buHbiBraGkgcsOqIGNodeG7mXQgdsOgbyBraOG7kWksIGJhIGNo4bqlbSBsw6AgZOG6pXUgaGnhu4d1IGvDqW8gcXVlbiB0aHXhu5ljLlxuICAgICAgICAgICAgICAgTmfGsOG7nWkgZMO5bmcgY+G6p24gbeG7mXQgY2jhu5cgYsOhbSBSw5UgUsOATkcgdGhheSB2w6wgxJFvw6FuIHhlbSBi4bqlbSDEkcOidSB0aMOsIGvDqW8gxJHGsOG7o2NcbiAgICAgICAgICAgICAgIChjaOG7kXQgMjgvMDgvMjAyNiwgdGhlbyDEkcO6bmcgZOG6o2kgxJFhbmcgZMO5bmcg4bufIG3DoG4gQ3VzdG9tZXIgMzYwIGPhu6dhIG1vYmlvLXdlYikuIC0tPlxuICAgICAgICAgIEBpZiAoaXNFZGl0TW9kZSgpKSB7XG4gICAgICAgICAgICA8IS0tIPCflLQgROG6o2kgUEjhuqJJIG1hbmcgdGF5IGPhuqdtIMSQw5pORyBD4bqkUCBj4bunYSBuw7MuIEThuqNpIG5o4bqtbiBjaHXhu5l0IChgcG9pbnRlci1ldmVudHM6IGF1dG9gKSxcbiAgICAgICAgICAgICAgICAgbsOqbiBu4bq/dSBraMO0bmcgbWFuZyB0YXkgY+G6p20gdGjDrCBgY2hlY2tEcmFnSGFuZGxlQ2xhc3NgIGR1eeG7h3QgbMOqbiBn4bq3cCB0YXkgY+G6p20gY+G7p2FcbiAgICAgICAgICAgICAgICAgY29udGFpbmVyIHbDoCBuaOG6pWMgY29udGFpbmVyIHRoYXkgdsOsIGto4buRaSBjb24gKMSRbyAyOC8wOC8yMDI2OiBrw6lvIGThuqNpIGto4buRaSBjb24sXG4gICAgICAgICAgICAgICAgIGNvbnRhaW5lciBuaOG6o3kgMCwwIOKGkiAwLDEgY8OybiBraOG7kWkgY29uIMSR4bupbmcgaW0pLlxuICAgICAgICAgICAgICAgICDwn5S0IEThuqNpIENI4buIIG1hbmcgdGF5IGPhuqdtLCBD4bqkTSBtYW5nIHRow6ptIGNsYXNzIGNo4bq3bjogYGNoZWNrRHJhZ0hhbmRsZUNsYXNzYCB4w6l0IGhhaVxuICAgICAgICAgICAgICAgICBjbGFzcyDEkcOzIHRyw6puIEPDmU5HIG3hu5l0IG5vZGUgbsOqbiDEkeG7gyBjaHVuZyBsw6Aga8OpbyBo4buPbmcgKMSRbyAyOC8wOC8yMDI2OiBi4bqlbSBk4bqjaSBraOG7kWlcbiAgICAgICAgICAgICAgICAgY29uIGvDqW8gMTQwcHgsIGto4buRaSDEkeG7qW5nIGltKS4gVmnhu4djIGNo4bq3biBsxrDhu5tpIGNoYSBkbyBW4buOIEtI4buQSSBsby4gLS0+XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1kcmFnLWJhclwiXG4gICAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyLWNoaWxkXT1cImlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1kcmFnLWhhbmRsZV09XCIhaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctaGFuZGxlLWNoaWxkXT1cImlzTmVzdGVkQ2FudmFzKClcIj5cbiAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyLWRvdHNcIj5cbiAgICAgICAgICAgICAgICBAZm9yIChkb3Qgb2YgZHJhZ0JhckRvdHM7IHRyYWNrIGRvdCkge1xuICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyLWRvdFwiPjwvc3Bhbj5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cblxuICAgICAgICAgIDwhLS0gS2h1bmcgY2jhu6lhIGNvbXBvbmVudCBj4bunYSBuxqFpIGTDuW5nLlxuICAgICAgICAgICAgICAg8J+UtCBQSOG6okkgY8OzIGtodW5nIHRo4bqtdCAoa2jDtG5nIHBo4bqjaSBgbmctY29udGFpbmVyYCB0cuG7kW5nKTogbsOzIGxvIGjhu5kgbsahaSBkw7luZyBwaOG6p24gbOG6pXAgxJHhuqd5XG4gICAgICAgICAgICAgICDDtCB2w6Agdmnhu4FuIGLDoW8gY2jhur8gxJHhu5kgc+G7rWEuIEtow7RuZyBjw7Mga2h1bmcgdGjDrCBt4buXaSBjb21wb25lbnQgbuG7mWkgZHVuZyBs4bqhaSBwaOG6o2kgdOG7sSB2aeG6v3RcbiAgICAgICAgICAgICAgIGA6aG9zdCB7IHdpZHRoL2hlaWdodDogMTAwJSB9YCArIHZp4buBbiDEkeG7qXQg4oCUIMSRw7MgbMOgIHZp4buHYyBj4bunYSB0aMawIHZp4buHbiwga2jDtG5nIHBo4bqjaSBj4bunYVxuICAgICAgICAgICAgICAgbmfGsOG7nWkgZMO5bmcgKG5nxrDhu51pIGTDuW5nIGNo4buRdCAyOC8wOC8yMDI2KS5cbiAgICAgICAgICAgICAgIE3hu5dpIGto4buRaSBt4buZdCBWaWV3Q29udGFpbmVyUmVmIHJpw6puZyBuw6puIGNvbXBvbmVudCBu4bqxbSDEkcO6bmcgw7QgY+G7p2EgbsOzLiAtLT5cbiAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGVudC1mcmFtZVwiXG4gICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1jb250ZW50LWZyYW1lLWVkaXRdPVwiaXNFZGl0TW9kZSgpICYmICFpc0NvbnRhaW5lcihpdGVtKVwiXG4gICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1jb250ZW50LWZyYW1lLWVtcHR5XT1cImlzQ29udGFpbmVyKGl0ZW0pXCI+XG4gICAgICAgICAgICA8bmctY29udGFpbmVyICNpdGVtSG9zdCAvPlxuICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgPCEtLSBE4bqldSBoaeG7h3UgQ09OVEFJTkVSOiBuaMOjbiBnw7NjIHRyw6puLXBo4bqjaSArIHZp4buBbiDEkeG7qXQgYmFvIHJ14buZdC4gS2jDtG5nIGPDsyBk4bqldSBoaeG7h3UgdGjDrFxuICAgICAgICAgICAgICAgbmfGsOG7nWkgZMO5bmcga2jDtG5nIGJp4bq/dCBraOG7kWkgbsOgbyBjaOG7qWEgxJHGsOG7o2Mga2jhu5FpIGtow6FjIChjaOG7kXQgMjgvMDgvMjAyNiwgdGhlbyDEkcO6bmcgY8OhY2hcbiAgICAgICAgICAgICAgIG3DoG4gQ3VzdG9tZXIgMzYwIGPhu6dhIG1vYmlvLXdlYiDEkWFuZyBsw6BtKS4gLS0+XG4gICAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkgJiYgaXNDb250YWluZXIoaXRlbSkpIHtcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRhaW5lci1sYWJlbFwiPkto4buRaSBnaMOpcDwvZGl2PlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGFpbmVyLWJvcmRlclwiPjwvZGl2PlxuICAgICAgICAgIH1cblxuICAgICAgICAgIDwhLS0gVGhhbmggY8O0bmcgY+G7pTogaGnhu4duIGtoaSByw6ogY2h14buZdCB2w6BvIGto4buRaS5cbiAgICAgICAgICAgICAgIPCflLQgRMO5bmcgYGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvbmAg4oCUIEPhuqRNIHThu7EgZOG7sW5nIHRo4bq7IGA8YnV0dG9uPmAuXG4gICAgICAgICAgICAgICBDb21wb25lbnQgbsOgeSDEkcOjIGPDsyBz4bq1biBraeG7g3UgbsO6dCwgY+G7oSBuw7p0IHbDoCBib25nIGLDs25nIGNow7ogdGjDrWNoIHRoZW8gxJHDum5nIGLhu5kgZ2lhbyBkaeG7h25cbiAgICAgICAgICAgICAgIGPhu6dhIHPhuqNuIHBo4bqpbTsgdOG7sSBk4buxbmcgbMOgIG3hu5dpIG7GoWkgbeG7mXQga2nhu4N1IHbDoCBt4bqldCBsdcO0biB0b29sdGlwLiAtLT5cbiAgICAgICAgICBAaWYgKGlzRWRpdE1vZGUoKSkge1xuICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtdG9vbGJhclwiXG4gICAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiPlxuICAgICAgICAgICAgICBAaWYgKGNhbkFkZEluc2lkZShpdGVtKSkge1xuICAgICAgICAgICAgICAgIDxsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25cbiAgICAgICAgICAgICAgICAgIFt0eXBlXT1cIididXR0b24tdGhpcmQnXCJcbiAgICAgICAgICAgICAgICAgIFtpY29uT25seVR5cGVdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgICAgW2NsYXNzSWNvbkxlZnRdPVwiJ2xpYnMtdWktaWNvbi1hZGQnXCJcbiAgICAgICAgICAgICAgICAgIFtwb3BvdmVyXT1cInsgY29uZmlnOiB7IGNvbnRlbnQ6ICdUaMOqbSBraOG7kWkgdsOgbyB0cm9uZycsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgICAgKG91dENsaWNrKT1cImhhbmRsZXJBZGRJbnNpZGUoJGV2ZW50LCBpdGVtKVwiIC8+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgQGlmIChjYW5Ub2dnbGVUeXBlKGl0ZW0pKSB7XG4gICAgICAgICAgICAgICAgPGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvblxuICAgICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZCdcIlxuICAgICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIFtzaXplQnV0dG9uXT1cIidzbWFsbGVyJ1wiXG4gICAgICAgICAgICAgICAgICBbY2xhc3NJY29uTGVmdF09XCJpc0NvbnRhaW5lcihpdGVtKSA/ICdsaWJzLXVpLWljb24tc3BsaXQtY2VsbCcgOiAnbGlicy11aS1pY29uLW1lcmdlLWNlbGwnXCJcbiAgICAgICAgICAgICAgICAgIFtwb3BvdmVyXT1cInsgY29uZmlnOiB7IGNvbnRlbnQ6IGlzQ29udGFpbmVyKGl0ZW0pID8gJ8SQxrBhIHbhu4Ega2jhu5FpIMSRxqFuJyA6ICdDaHV54buDbiB0aMOgbmgga2jhu5FpIGdow6lwJywgekluZGV4OiAxMzAwIH0gfVwiXG4gICAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlclRvZ2dsZVR5cGUoJGV2ZW50LCBpdGVtKVwiIC8+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgPGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvblxuICAgICAgICAgICAgICAgIFt0eXBlXT1cIididXR0b24tdGhpcmQtaG92ZXItZGFuZ2VyJ1wiXG4gICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tcmVtb3ZlJ1wiXG4gICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogaXNDb250YWluZXIoaXRlbSkgPyAnWG/DoSBj4bqjIGto4buRaSBnaMOpcCcgOiAnWG/DoSBraOG7kWknLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlclJlbW92ZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cblxuICAgICAgICAgIEBpZiAoaXRlbS5jaGlsZHJlbiAmJiBpdGVtLmNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIDwhLS0gS2jhu5FpIGPDsyBjb24g4oaSIHJ14buZdCBuw7MgbOG6oWkgbMOgIG3hu5l0IG3hurd0IHBo4bqzbmcgbuG7r2EuIMSQ4buHIHF1eSDhu58gxJHDonksIG7GoWkgZMO5bmcga2jDtG5nIHBo4bqjaSBsby5cbiAgICAgICAgICAgICAgICAgTOG7gSDEkeG6t3Qg4bufIMSQw4JZICh24buPIGNvbnRhaW5lciksIGtow7RuZyDEkeG6t3QgdsOgbyBsxrDhu5tpIGNvbiDigJQgbMaw4bubaSBjb24gZ2nhu68gbmd1ecOqbiBraGUgMnB4LiAtLT5cbiAgICAgICAgICAgIDxsaWJzX3VpLXNlcnZpY2VzLWdyaWRfbGF5b3V0LWNhbnZhc1xuICAgICAgICAgICAgICBjbGFzcz1cImJsb2NrIGgtZnVsbCB3LWZ1bGxcIlxuICAgICAgICAgICAgICBbc3R5bGUucGFkZGluZ109XCJjb250YWluZXJQYWRkaW5nKClcIlxuICAgICAgICAgICAgICBbbm9kZV09XCJpdGVtXCJcbiAgICAgICAgICAgICAgW2RlcHRoXT1cImRlcHRoKCkgKyAxXCJcbiAgICAgICAgICAgICAgKG91dENoYW5nZSk9XCJvdXRDaGFuZ2UuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dFJlbW92ZU5vZGUpPVwib3V0UmVtb3ZlTm9kZS5lbWl0KCRldmVudClcIlxuICAgICAgICAgICAgICAob3V0QWRkQmxvY2tJbnNpZGUpPVwib3V0QWRkQmxvY2tJbnNpZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dFRvZ2dsZVR5cGUpPVwib3V0VG9nZ2xlVHlwZS5lbWl0KCRldmVudClcIiAvPlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2dyaWRzdGVyLWl0ZW0+XG4gICAgfVxuICA8L2dyaWRzdGVyPlxuPC9kaXY+XG4iXX0=
|
|
564
|
+
args: [{ selector: 'libs_ui-services-grid_layout-canvas', standalone: true, imports: [GridsterModule, LibsUiComponentsButtonsButtonComponent, NgTemplateOutlet, TranslateModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (isZoomActive()) {\n <!-- Khu v\u1EF1c cu\u1ED9n ch\u1EE9a canvas stage thu ph\u00F3ng -->\n <div\n #scrollArea\n class=\"libs-ui-grid-layout-scroll-area relative h-full min-h-0 w-full min-w-0 overflow-auto p-6 flex justify-center items-start\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\"\n (wheel)=\"handlerWheel($event)\"\n (dragover)=\"handlerDragOver($event)\"\n (dragleave)=\"handlerDragLeave($event)\"\n (drop)=\"handlerDrop($event)\">\n <div\n #canvasStage\n id=\"bi-canvas-stage\"\n class=\"libs-ui-grid-layout-stage relative flex flex-col transition-[zoom] duration-150 origin-top shrink-0 bg-white/80 border border-slate-200/80 rounded-2xl shadow-sm p-4\"\n [style.zoom]=\"currentZoom()\"\n [style.width.px]=\"stageWidth()\"\n [style.min-width.px]=\"stageWidth()\"\n [style.min-height.px]=\"stageMinHeight()\">\n <!-- Snapped Drop Ghost Preview -->\n @if (dropGhost(); as ghost) {\n <div\n class=\"pointer-events-none absolute z-30 flex flex-col items-center justify-center overflow-hidden rounded-lg border-2 border-dashed border-blue-500 bg-blue-500/15 p-1 shadow-sm transition-all duration-75 ease-out backdrop-blur-[0.5px] select-none\"\n [style.left.px]=\"ghost.pixelLeft\"\n [style.top.px]=\"ghost.pixelTop\"\n [style.width.px]=\"ghost.pixelWidth\"\n [style.height.px]=\"ghost.pixelHeight\">\n <div class=\"inline-flex max-w-[95%] items-center gap-1 truncate rounded-md bg-blue-600 px-2 py-0.5 text-[11px] font-semibold text-white shadow-xs\">\n @if (ghost.cardIconClass) {\n <i [class]=\"ghost.cardIconClass + ' text-xs'\"></i>\n } @else if (ghost.cardIconText) {\n <span class=\"text-[10px] font-bold\">{{ ghost.cardIconText }}</span>\n } @else {\n <span class=\"text-xs font-bold\">{{ ghost.isInsideGroup ? '\u21B3' : '+' }}</span>\n }\n <span class=\"truncate\">{{ ghost.cardTitle }}</span>\n <span class=\"text-[9px] font-mono opacity-75\">({{ ghost.cols }}c)</span>\n </div>\n\n @if (ghost.pixelHeight >= 80) {\n <div class=\"mt-1 flex max-w-[95%] items-center gap-1.5 truncate rounded border border-blue-100 bg-white/95 px-2 py-0.5 text-[10px] font-medium text-blue-700 shadow-xs\">\n @if (ghost.isInsideGroup) {\n <span class=\"truncate font-bold text-blue-900\">\u21B3 {{ ghost.groupTitle }}</span>\n <span class=\"text-blue-300\">\u2022</span>\n }\n <span>C\u1ED9t {{ ghost.col + 1 }}-{{ ghost.col + ghost.cols }}</span>\n </div>\n }\n </div>\n }\n <gridster class=\"h-full w-full\" [class.display-grid]=\"showGridLines()\" [options]=\"gridOptions()\">\n @for (item of children(); track item.id; let itemIndex = $index) {\n <!-- \uD83D\uDD34 Ch\u1EB7n `mousedown` t\u1EA1i \u0110\u00C2Y cho l\u01B0\u1EDBi l\u1ED3ng \u2014 C\u1EA4M b\u1ECF v\u00E0 C\u1EA4M chuy\u1EC3n sang ch\u1ED7 kh\u00E1c.\n `ignoreContentClass` m\u1ED9t m\u00ECnh KH\u00D4NG \u0111\u1EE7: `delayStart: 160` l\u00E0m M\u1ED6I l\u01B0\u1EDBi l\u00EAn l\u1ECBch\n `dragStart` b\u1EB1ng `setTimeout` ngay t\u1EA1i `mousedown`, c\u00F2n `stopPropagation` m\u00E0 lib g\u1ECDi b\u00EAn\n trong `dragStart` ch\u1EA1y sau 160ms n\u00EAn qu\u00E1 mu\u1ED9n \u2014 l\u01B0\u1EDBi CHA \u0111\u00E3 k\u1ECBp nh\u1EADn (\u0111o 28/08/2026: k\u00E9o\n d\u1EA3i kh\u1ED1i con, chu\u1ED7i class duy\u1EC7t l\u00EAn \u0110\u00DANG m\u00E0 container v\u1EABn nh\u1EA3y 0,0 \u2192 0,1).\n G\u1EAFn \u1EDF `gridster-item` l\u00E0 \u0111\u00FAng bi\u00EAn: l\u01B0\u1EDBi con nghe tr\u00EAn ch\u00EDnh ph\u1EA7n t\u1EED n\u00E0y n\u00EAn \u0111\u00E3 nh\u1EADn xong,\n l\u01B0\u1EDBi cha \u1EDF ngo\u00E0i b\u1ECB ch\u1EB7n. -->\n <gridster-item\n [item]=\"item\"\n [class.libs-ui-grid-layout-selected]=\"selectedNodeId() === item.id\"\n (click)=\"handlerSelectNode(item, $event)\"\n (mousedown)=\"handlerBlockParentDrag($event)\">\n <!-- \uD83D\uDD34 V\u1ECF n\u00E0y mang `dragHandleClass` \u2014 C\u1EA4M b\u1ECF.\n `ignoreContent: true` b\u1EAFt gridster CH\u1EC8 cho k\u00E9o khi ch\u1ED7 b\u1EA5m n\u1EB1m d\u01B0\u1EDBi m\u1ED9t ph\u1EA7n t\u1EED c\u00F3\n `dragHandleClass`. Kh\u00F4ng c\u00F3 v\u1ECF n\u00E0y th\u00EC component c\u1EE7a n\u01A1i d\u00F9ng (th\u01B0 vi\u1EC7n kh\u00F4ng ki\u1EC3m so\u00E1t\n \u0111\u01B0\u1EE3c class c\u1EE7a n\u00F3) s\u1EBD kh\u00F4ng c\u00F3 tay c\u1EA7m n\u00E0o, v\u00E0 KH\u00D4NG kh\u1ED1i n\u00E0o k\u00E9o \u0111\u01B0\u1EE3c \u2014 \u0111o 28/08/2026:\n k\u00E9o 320px, to\u1EA1 \u0111\u1ED9 kh\u1ED1i gi\u1EEF nguy\u00EAn x=3,y=0.\n T\u00EAn class theo c\u1EA5p: l\u01B0\u1EDBi trang v\u00E0 l\u01B0\u1EDBi l\u1ED3ng d\u00F9ng hai t\u00EAn KH\u00C1C nhau, n\u1EBFu kh\u00F4ng l\u01B0\u1EDBi cha\n c\u0169ng nh\u1EADn ra tay c\u1EA7m c\u1EE7a kh\u1ED1i con v\u00E0 nh\u1EA5c lu\u00F4n container. -->\n <!-- \uD83D\uDD34 V\u1ECF KH\u00D4NG mang `dragHandleClass` \u2014 C\u1EA4M th\u00EAm l\u1EA1i.\n K\u00E9o ch\u1EC9 \u0111\u01B0\u1EE3c ph\u00E9p b\u1EAFt \u0111\u1EA7u t\u1EEB D\u1EA2I XANH (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026: \"ph\u1EA3i hover v\u00E0o v\u00F9ng\n m\u00E0u xanh th\u00EC m\u1EDBi k\u00E9o th\u1EA3 \u0111\u01B0\u1EE3c cho \u0111\u1ED3ng nh\u1EA5t\"). Cho c\u1EA3 v\u1ECF l\u00E0m tay c\u1EA7m th\u00EC b\u1EA5m \u0111\u00E2u c\u0169ng\n k\u00E9o, v\u00E0 kh\u1ED1i con n\u1EB1m trong container s\u1EBD nh\u1EA5c lu\u00F4n container v\u00EC l\u01B0\u1EDBi cha duy\u1EC7t l\u00EAn g\u1EB7p\n tay c\u1EA7m c\u1EE7a v\u1ECF container tr\u01B0\u1EDBc.\n V\u1ECF kh\u1ED1i con v\u1EABn gi\u1EEF class CH\u1EB6N \u0111\u1EC3 l\u01B0\u1EDBi cha d\u1EEBng \u0111\u00FAng \u1EDF bi\u00EAn. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas()\">\n <!-- D\u1EA3i k\u00E9o: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i, ba ch\u1EA5m l\u00E0 d\u1EA5u hi\u1EC7u k\u00E9o quen thu\u1ED9c.\n Ng\u01B0\u1EDDi d\u00F9ng c\u1EA7n m\u1ED9t ch\u1ED7 b\u00E1m R\u00D5 R\u00C0NG thay v\u00EC \u0111o\u00E1n xem b\u1EA5m \u0111\u00E2u th\u00EC k\u00E9o \u0111\u01B0\u1EE3c\n (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng d\u1EA3i \u0111ang d\u00F9ng \u1EDF m\u00E0n Customer 360 c\u1EE7a mobio-web). -->\n @if (isEditMode()) {\n <!-- \uD83D\uDD34 D\u1EA3i PH\u1EA2I mang tay c\u1EA7m \u0110\u00DANG C\u1EA4P c\u1EE7a n\u00F3. D\u1EA3i nh\u1EADn chu\u1ED9t (`pointer-events: auto`),\n n\u00EAn n\u1EBFu kh\u00F4ng mang tay c\u1EA7m th\u00EC `checkDragHandleClass` duy\u1EC7t l\u00EAn g\u1EB7p tay c\u1EA7m c\u1EE7a\n container v\u00E0 nh\u1EA5c container thay v\u00EC kh\u1ED1i con (\u0111o 28/08/2026: k\u00E9o d\u1EA3i kh\u1ED1i con,\n container nh\u1EA3y 0,0 \u2192 0,1 c\u00F2n kh\u1ED1i con \u0111\u1EE9ng im).\n \uD83D\uDD34 D\u1EA3i CH\u1EC8 mang tay c\u1EA7m, C\u1EA4M mang th\u00EAm class ch\u1EB7n: `checkDragHandleClass` x\u00E9t hai\n class \u0111\u00F3 tr\u00EAn C\u00D9NG m\u1ED9t node n\u00EAn \u0111\u1EC3 chung l\u00E0 k\u00E9o h\u1ECFng (\u0111o 28/08/2026: b\u1EA5m d\u1EA3i kh\u1ED1i\n con k\u00E9o 140px, kh\u1ED1i \u0111\u1EE9ng im). Vi\u1EC7c ch\u1EB7n l\u01B0\u1EDBi cha do V\u1ECE KH\u1ED0I lo. -->\n <div\n class=\"libs-ui-grid-layout-drag-bar\"\n [class.libs-ui-grid-layout-drag-bar-child]=\"isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle]=\"!isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-drag-bar-dots\">\n @for (dot of dragBarDots; track dot) {\n <span class=\"libs-ui-grid-layout-drag-bar-dot\"></span>\n }\n </span>\n </div>\n }\n\n <!-- Khung ch\u1EE9a component c\u1EE7a n\u01A1i d\u00F9ng.\n \uD83D\uDD34 PH\u1EA2I c\u00F3 khung th\u1EADt (kh\u00F4ng ph\u1EA3i `ng-container` tr\u1ED1ng): n\u00F3 lo h\u1ED9 n\u01A1i d\u00F9ng ph\u1EA7n l\u1EA5p \u0111\u1EA7y\n \u00F4 v\u00E0 vi\u1EC1n b\u00E1o ch\u1EBF \u0111\u1ED9 s\u1EEDa. Kh\u00F4ng c\u00F3 khung th\u00EC m\u1ED7i component n\u1ED9i dung l\u1EA1i ph\u1EA3i t\u1EF1 vi\u1EBFt\n `:host { width/height: 100% }` + vi\u1EC1n \u0111\u1EE9t \u2014 \u0111\u00F3 l\u00E0 vi\u1EC7c c\u1EE7a th\u01B0 vi\u1EC7n, kh\u00F4ng ph\u1EA3i c\u1EE7a\n ng\u01B0\u1EDDi d\u00F9ng (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026).\n M\u1ED7i kh\u1ED1i m\u1ED9t ViewContainerRef ri\u00EAng n\u00EAn component n\u1EB1m \u0111\u00FAng \u00F4 c\u1EE7a n\u00F3. -->\n <div\n class=\"libs-ui-grid-layout-content-frame\"\n [class.libs-ui-grid-layout-content-frame-edit]=\"isEditMode() && !isContainer(item)\"\n [class.libs-ui-grid-layout-content-frame-empty]=\"isContainer(item)\">\n <ng-container #itemHost />\n </div>\n\n <!-- D\u1EA5u hi\u1EC7u CONTAINER: nh\u00E3n g\u00F3c tr\u00EAn-ph\u1EA3i + vi\u1EC1n \u0111\u1EE9t bao ru\u1ED9t. Kh\u00F4ng c\u00F3 d\u1EA5u hi\u1EC7u th\u00EC\n ng\u01B0\u1EDDi d\u00F9ng kh\u00F4ng bi\u1EBFt kh\u1ED1i n\u00E0o ch\u1EE9a \u0111\u01B0\u1EE3c kh\u1ED1i kh\u00E1c (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng c\u00E1ch\n m\u00E0n Customer 360 c\u1EE7a mobio-web \u0111ang l\u00E0m). -->\n @if (isEditMode() && isContainer(item)) {\n <div class=\"libs-ui-grid-layout-container-label\">{{ 'i18n_container_block' | translate }}</div>\n <div class=\"libs-ui-grid-layout-container-border\"></div>\n }\n\n <!-- Thanh c\u00F4ng c\u1EE5: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i.\n \uD83D\uDD34 D\u00F9ng `libs_ui-components-buttons-button` \u2014 C\u1EA4M t\u1EF1 d\u1EF1ng th\u1EBB `<button>`.\n Component n\u00E0y \u0111\u00E3 c\u00F3 s\u1EB5n ki\u1EC3u n\u00FAt, c\u1EE1 n\u00FAt v\u00E0 bong b\u00F3ng ch\u00FA th\u00EDch theo \u0111\u00FAng b\u1ED9 giao di\u1EC7n\n c\u1EE7a s\u1EA3n ph\u1EA9m; t\u1EF1 d\u1EF1ng l\u00E0 m\u1ED7i n\u01A1i m\u1ED9t ki\u1EC3u v\u00E0 m\u1EA5t lu\u00F4n tooltip. -->\n @if (isEditMode() && !hiddenToolbar()) {\n @if (templateToolbar()) {\n <!-- N\u01A1i d\u00F9ng t\u1EF1 v\u1EBD thanh c\u00F4ng c\u1EE5: nh\u1EADn kh\u1ED1i + c\u00E1c h\u00E0m ph\u00E1t event qua context.\n\n \uD83D\uDD34 Kh\u1ED1i b\u1ECDc t\u1EF1 ch\u1EB7n `mousedown`/`click` n\u1ED5i l\u00EAn gridster. Thanh c\u00F4ng c\u1EE5 n\u1EB1m trong\n v\u00F9ng mang `dragHandleClass`; kh\u00F4ng ch\u1EB7n th\u00EC c\u00FA b\u1EA5m b\u1ECB hi\u1EC3u l\u00E0 b\u1EAFt \u0111\u1EA7u k\u00E9o v\u00E0\n `delayStart: 160` nu\u1ED1t lu\u00F4n `click` \u2014 n\u00FAt trong template ngo\u00E0i b\u1EA5m kh\u00F4ng \u0103n\n (\u0111o 09/09/2026: b\u1EA5m b\u1EB1ng `.click()` c\u1EE7a DOM th\u00EC m\u1EDF, b\u1EA5m chu\u1ED9t th\u1EADt th\u00EC kh\u00F4ng).\n Ch\u1EB7n \u1EDF \u0110\u00C2Y \u0111\u1EC3 n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i bi\u1EBFt b\u1EABy n\u00E0y. -->\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n role=\"toolbar\"\n tabindex=\"0\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\"\n (mousedown)=\"handlerStopEvent($event)\"\n (keydown)=\"handlerStopEvent($event)\"\n (click)=\"handlerStopEvent($event)\">\n <ng-container\n *ngTemplateOutlet=\"templateToolbar()!; context: toolbarContexts()[itemIndex]\" />\n </div>\n } @else {\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-dim-badge\">\n {{ item.rows }} h\u00E0ng \u00D7 {{ item.cols }} c\u1ED9t\n </span>\n @if (canAddInside(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-add'\"\n [popover]=\"{ config: { content: 'Th\u00EAm kh\u1ED1i v\u00E0o trong', zIndex: 1300 } }\"\n (outClick)=\"handlerAddInside($event, item)\" />\n }\n @if (canToggleType(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"isContainer(item) ? 'libs-ui-icon-split-cell' : 'libs-ui-icon-merge-cell'\"\n [popover]=\"{ config: { content: isContainer(item) ? '\u0110\u01B0a v\u1EC1 kh\u1ED1i \u0111\u01A1n' : 'Chuy\u1EC3n th\u00E0nh kh\u1ED1i gh\u00E9p', zIndex: 1300 } }\"\n (outClick)=\"handlerToggleType($event, item)\" />\n }\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-setting'\"\n [popover]=\"{ config: { content: 'C\u1EA5u h\u00ECnh th\u1EBB', zIndex: 1300 } }\"\n (outClick)=\"handlerConfig($event, item)\" />\n <libs_ui-components-buttons-button\n [type]=\"'button-third-hover-danger'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-remove'\"\n [popover]=\"{ config: { content: isContainer(item) ? 'Xo\u00E1 c\u1EA3 kh\u1ED1i gh\u00E9p' : 'Xo\u00E1 kh\u1ED1i', zIndex: 1300 } }\"\n (outClick)=\"handlerRemove($event, item)\" />\n </div>\n }\n }\n\n @if (item.children && item.children.length > 0) {\n <!-- Kh\u1ED1i c\u00F3 con \u2192 ru\u1ED9t n\u00F3 l\u1EA1i l\u00E0 m\u1ED9t m\u1EB7t ph\u1EB3ng n\u1EEFa. \u0110\u1EC7 quy \u1EDF \u0111\u00E2y, n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i lo.\n L\u1EC1 \u0111\u1EB7t \u1EDF \u0110\u00C2Y (v\u1ECF container), kh\u00F4ng \u0111\u1EB7t v\u00E0o l\u01B0\u1EDBi con \u2014 l\u01B0\u1EDBi con gi\u1EEF nguy\u00EAn khe 2px. -->\n <libs_ui-services-grid_layout-canvas\n class=\"block h-full w-full\"\n [style.padding]=\"containerPadding()\"\n [node]=\"item\"\n [depth]=\"depth() + 1\"\n [hiddenToolbar]=\"hiddenToolbar()\"\n [templateToolbar]=\"templateToolbar()\"\n [selectedNodeId]=\"selectedNodeId()\"\n (outSelectNode)=\"outSelectNode.emit($event)\"\n (outConfigNode)=\"outConfigNode.emit($event)\"\n (outChange)=\"outChange.emit($event)\"\n (outRemoveNode)=\"outRemoveNode.emit($event)\"\n (outAddBlockInside)=\"outAddBlockInside.emit($event)\"\n (outToggleType)=\"outToggleType.emit($event)\" />\n }\n </div>\n </gridster-item>\n }\n </gridster>\n </div>\n </div>\n\n <!-- Thanh dock \u0111i\u1EC1u khi\u1EC3n thu ph\u00F3ng n\u1ED5i \u1EDF g\u00F3c d\u01B0\u1EDBi ph\u1EA3i -->\n @if (showZoomDock()) {\n <div\n #zoomDock\n id=\"canvas-zoom-dock\"\n role=\"toolbar\"\n tabindex=\"0\"\n class=\"absolute bottom-4 right-6 z-30 flex items-center bg-white/95 backdrop-blur-md rounded-full shadow-lg border border-slate-300/80 p-1 space-x-1 select-none\"\n (mousedown)=\"$event.stopPropagation()\"\n (keydown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\">\n <!-- Thu nh\u1ECF (-) -->\n <button\n type=\"button\"\n class=\"w-7 h-7 flex items-center justify-center rounded-full text-slate-600 hover:text-slate-900 hover:bg-slate-100 disabled:opacity-30 disabled:hover:bg-transparent transition-colors cursor-pointer\"\n [disabled]=\"currentZoom() <= zoomMin()\"\n [title]=\"'i18n_zoom_out' | translate\"\n (click)=\"handlerZoomOut()\">\n <svg class=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/></svg>\n </button>\n\n <!-- T\u1EF7 l\u1EC7 % v\u00E0 menu preset -->\n <div class=\"relative\">\n <button\n type=\"button\"\n class=\"h-7 px-2 flex items-center gap-1 rounded-full text-xs font-semibold text-slate-700 hover:bg-slate-100 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_options' | translate\"\n (click)=\"handlerToggleMenu()\">\n <span>{{ zoomPercent() }}%</span>\n <svg class=\"w-3 h-3 text-slate-400 transition-transform duration-150\" [class.rotate-180]=\"isMenuOpen()\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n\n <!-- Menu dropdown c\u00E1c m\u1ED1c thu ph\u00F3ng -->\n @if (isMenuOpen()) {\n <div class=\"absolute bottom-full mb-2 right-0 w-64 bg-white rounded-xl shadow-xl border border-slate-200 py-1.5 z-40 text-xs select-none\">\n <div class=\"px-3 py-1 text-[11px] font-semibold text-slate-400 uppercase tracking-wider\">\n {{ 'i18n_zoom_canvas_width' | translate: { width: stageWidth() } }}\n </div>\n <button\n type=\"button\"\n class=\"w-full px-3 py-2 text-left flex items-center justify-between hover:bg-slate-50 transition-colors cursor-pointer\"\n (click)=\"handlerFitScreen()\">\n <span class=\"flex items-center gap-2 text-slate-700 font-medium\">\n <svg class=\"w-3.5 h-3.5 text-slate-500\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/></svg>\n {{ 'i18n_zoom_fit_screen' | translate }}\n </span>\n <span class=\"text-[10px] bg-blue-50 text-blue-600 px-1.5 py-0.5 rounded font-medium\">{{ 'i18n_auto' | translate }}</span>\n </button>\n <div class=\"h-px bg-slate-100 my-1\"></div>\n @for (preset of zoomPresets; track preset.value) {\n <button\n type=\"button\"\n class=\"w-full px-3 py-1.5 text-left flex items-center justify-between hover:bg-slate-50 transition-colors cursor-pointer\"\n [class.text-blue-600]=\"isCurrentPreset(preset.value)\"\n [class.font-semibold]=\"isCurrentPreset(preset.value)\"\n [class.bg-blue-50]=\"isCurrentPreset(preset.value)\"\n (click)=\"handlerSelectPreset(preset.value)\">\n <span class=\"text-slate-700\" [class.text-blue-600]=\"isCurrentPreset(preset.value)\">\n {{ preset.label | translate }}\n </span>\n @if (isCurrentPreset(preset.value)) {\n <svg class=\"w-3.5 h-3.5 text-blue-600\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>\n }\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Ph\u00F3ng to (+) -->\n <button\n type=\"button\"\n class=\"w-7 h-7 flex items-center justify-center rounded-full text-slate-600 hover:text-slate-900 hover:bg-slate-100 disabled:opacity-30 disabled:hover:bg-transparent transition-colors cursor-pointer\"\n [disabled]=\"currentZoom() >= zoomMax()\"\n [title]=\"'i18n_zoom_in' | translate\"\n (click)=\"handlerZoomIn()\">\n <svg class=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/></svg>\n </button>\n\n <!-- Ph\u00E2n c\u00E1ch -->\n <div class=\"h-4 w-px bg-slate-200\"></div>\n\n <!-- N\u00FAt V\u1EEBa m\u00E0n h\u00ECnh nhanh -->\n <button\n type=\"button\"\n class=\"h-7 px-2.5 flex items-center gap-1 rounded-full text-xs text-slate-600 hover:text-slate-900 hover:bg-slate-100 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_fit_screen_tooltip' | translate\"\n (click)=\"handlerFitScreen()\">\n <svg class=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\"/><line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\"/></svg>\n <span class=\"font-medium\">{{ 'i18n_zoom_fit_screen' | translate }}</span>\n </button>\n\n <!-- N\u00FAt \u0110\u1EB7t l\u1EA1i 100% (ch\u1EC9 hi\u1EC7n khi kh\u00E1c 100%) -->\n @if (zoomPercent() !== 100) {\n <button\n type=\"button\"\n class=\"h-7 px-2 flex items-center gap-1 rounded-full text-xs font-semibold text-blue-600 hover:bg-blue-50 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_reset_tooltip' | translate: { width: stageWidth(), height: stageMinHeight() }\"\n (click)=\"handlerResetZoom()\">\n <svg class=\"w-3 h-3\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>\n <span>100%</span>\n </button>\n }\n </div>\n }\n} @else {\n <!-- \uD83D\uDD34 `relative` l\u00E0 B\u1EAET BU\u1ED8C, C\u1EA4M b\u1ECF.\n gridster \u0111\u1ED5i v\u1ECB tr\u00ED chu\u1ED9t th\u00E0nh \u00F4 l\u01B0\u1EDBi b\u1EB1ng `e.clientY + (el.scrollTop - el.offsetTop)` \u2014 c\u00F4ng\n th\u1EE9c \u0111\u00F3 ch\u1EC9 \u0111\u00FAng khi `offsetParent` c\u1EE7a <gridster> CH\u00CDNH L\u00C0 kh\u1ED1i cu\u1ED9n n\u00E0y. Thi\u1EBFu `relative` th\u00EC\n `offsetParent` nh\u1EA3y l\u00EAn kh\u1ED1i bao ngo\u00E0i: `offsetTop` tr\u1EA3 16 trong khi l\u1EC7ch th\u1EADt l\u00E0 65 \u2192 gridster\n t\u00EDnh \u00F4 sai 49px, \u00F4 ch\u1EDD \u0111\u1EB7t hi\u1EC7n l\u1EC7ch kh\u1ECFi con tr\u1ECF n\u00EAn k\u00E9o th\u1EA3 kh\u00F4ng \u0111\u1ED5i \u0111\u01B0\u1EE3c v\u1ECB tr\u00ED, v\u00E0 k\u00E9o m\u00E9p\n c\u0169ng t\u00EDnh sai \u0111i\u1EC3m d\u1EEBng. -->\n <div\n #scrollArea\n class=\"libs-ui-grid-layout-scroll-area relative h-full min-h-0 w-full min-w-0\"\n [class.overflow-y-auto]=\"!isNestedCanvas()\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\"\n (dragover)=\"handlerDragOver($event)\"\n (dragleave)=\"handlerDragLeave($event)\"\n (drop)=\"handlerDrop($event)\">\n <!-- Snapped Drop Ghost Preview -->\n @if (dropGhost(); as ghost) {\n <div\n class=\"pointer-events-none absolute z-30 flex flex-col items-center justify-center overflow-hidden rounded-lg border-2 border-dashed border-blue-500 bg-blue-500/15 p-1 shadow-sm transition-all duration-75 ease-out backdrop-blur-[0.5px] select-none\"\n [style.left.px]=\"ghost.pixelLeft\"\n [style.top.px]=\"ghost.pixelTop\"\n [style.width.px]=\"ghost.pixelWidth\"\n [style.height.px]=\"ghost.pixelHeight\">\n <div class=\"inline-flex max-w-[95%] items-center gap-1 truncate rounded-md bg-blue-600 px-2 py-0.5 text-[11px] font-semibold text-white shadow-xs\">\n @if (ghost.cardIconClass) {\n <i [class]=\"ghost.cardIconClass + ' text-xs'\"></i>\n } @else if (ghost.cardIconText) {\n <span class=\"text-[10px] font-bold\">{{ ghost.cardIconText }}</span>\n } @else {\n <span class=\"text-xs font-bold\">{{ ghost.isInsideGroup ? '\u21B3' : '+' }}</span>\n }\n <span class=\"truncate\">{{ ghost.cardTitle }}</span>\n <span class=\"text-[9px] font-mono opacity-75\">({{ ghost.cols }}c)</span>\n </div>\n\n @if (ghost.pixelHeight >= 80) {\n <div class=\"mt-1 flex max-w-[95%] items-center gap-1.5 truncate rounded border border-blue-100 bg-white/95 px-2 py-0.5 text-[10px] font-medium text-blue-700 shadow-xs\">\n @if (ghost.isInsideGroup) {\n <span class=\"truncate font-bold text-blue-900\">\u21B3 {{ ghost.groupTitle }}</span>\n <span class=\"text-blue-300\">\u2022</span>\n }\n <span>C\u1ED9t {{ ghost.col + 1 }}-{{ ghost.col + ghost.cols }}</span>\n </div>\n }\n </div>\n }\n <gridster class=\"h-full w-full\" [class.display-grid]=\"showGridLines()\" [options]=\"gridOptions()\">\n @for (item of children(); track item.id; let itemIndex = $index) {\n <!-- \uD83D\uDD34 Ch\u1EB7n `mousedown` t\u1EA1i \u0110\u00C2Y cho l\u01B0\u1EDBi l\u1ED3ng \u2014 C\u1EA4M b\u1ECF v\u00E0 C\u1EA4M chuy\u1EC3n sang ch\u1ED7 kh\u00E1c.\n `ignoreContentClass` m\u1ED9t m\u00ECnh KH\u00D4NG \u0111\u1EE7: `delayStart: 160` l\u00E0m M\u1ED6I l\u01B0\u1EDBi l\u00EAn l\u1ECBch\n `dragStart` b\u1EB1ng `setTimeout` ngay t\u1EA1i `mousedown`, c\u00F2n `stopPropagation` m\u00E0 lib g\u1ECDi b\u00EAn\n trong `dragStart` ch\u1EA1y sau 160ms n\u00EAn qu\u00E1 mu\u1ED9n \u2014 l\u01B0\u1EDBi CHA \u0111\u00E3 k\u1ECBp nh\u1EADn (\u0111o 28/08/2026: k\u00E9o\n d\u1EA3i kh\u1ED1i con, chu\u1ED7i class duy\u1EC7t l\u00EAn \u0110\u00DANG m\u00E0 container v\u1EABn nh\u1EA3y 0,0 \u2192 0,1).\n G\u1EAFn \u1EDF `gridster-item` l\u00E0 \u0111\u00FAng bi\u00EAn: l\u01B0\u1EDBi con nghe tr\u00EAn ch\u00EDnh ph\u1EA7n t\u1EED n\u00E0y n\u00EAn \u0111\u00E3 nh\u1EADn xong,\n l\u01B0\u1EDBi cha \u1EDF ngo\u00E0i b\u1ECB ch\u1EB7n. -->\n <gridster-item\n [item]=\"item\"\n [class.libs-ui-grid-layout-selected]=\"selectedNodeId() === item.id\"\n (click)=\"handlerSelectNode(item, $event)\"\n (mousedown)=\"handlerBlockParentDrag($event)\">\n <!-- \uD83D\uDD34 V\u1ECF n\u00E0y mang `dragHandleClass` \u2014 C\u1EA4M b\u1ECF.\n `ignoreContent: true` b\u1EAFt gridster CH\u1EC8 cho k\u00E9o khi ch\u1ED7 b\u1EA5m n\u1EB1m d\u01B0\u1EDBi m\u1ED9t ph\u1EA7n t\u1EED c\u00F3\n `dragHandleClass`. Kh\u00F4ng c\u00F3 v\u1ECF n\u00E0y th\u00EC component c\u1EE7a n\u01A1i d\u00F9ng (th\u01B0 vi\u1EC7n kh\u00F4ng ki\u1EC3m so\u00E1t\n \u0111\u01B0\u1EE3c class c\u1EE7a n\u00F3) s\u1EBD kh\u00F4ng c\u00F3 tay c\u1EA7m n\u00E0o, v\u00E0 KH\u00D4NG kh\u1ED1i n\u00E0o k\u00E9o \u0111\u01B0\u1EE3c \u2014 \u0111o 28/08/2026:\n k\u00E9o 320px, to\u1EA1 \u0111\u1ED9 kh\u1ED1i gi\u1EEF nguy\u00EAn x=3,y=0.\n T\u00EAn class theo c\u1EA5p: l\u01B0\u1EDBi trang v\u00E0 l\u01B0\u1EDBi l\u1ED3ng d\u00F9ng hai t\u00EAn KH\u00C1C nhau, n\u1EBFu kh\u00F4ng l\u01B0\u1EDBi cha\n c\u0169ng nh\u1EADn ra tay c\u1EA7m c\u1EE7a kh\u1ED1i con v\u00E0 nh\u1EA5c lu\u00F4n container. -->\n <!-- \uD83D\uDD34 V\u1ECF KH\u00D4NG mang `dragHandleClass` \u2014 C\u1EA4M th\u00EAm l\u1EA1i.\n K\u00E9o ch\u1EC9 \u0111\u01B0\u1EE3c ph\u00E9p b\u1EAFt \u0111\u1EA7u t\u1EEB D\u1EA2I XANH (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026: \"ph\u1EA3i hover v\u00E0o v\u00F9ng\n m\u00E0u xanh th\u00EC m\u1EDBi k\u00E9o th\u1EA3 \u0111\u01B0\u1EE3c cho \u0111\u1ED3ng nh\u1EA5t\"). Cho c\u1EA3 v\u1ECF l\u00E0m tay c\u1EA7m th\u00EC b\u1EA5m \u0111\u00E2u c\u0169ng\n k\u00E9o, v\u00E0 kh\u1ED1i con n\u1EB1m trong container s\u1EBD nh\u1EA5c lu\u00F4n container v\u00EC l\u01B0\u1EDBi cha duy\u1EC7t l\u00EAn g\u1EB7p\n tay c\u1EA7m c\u1EE7a v\u1ECF container tr\u01B0\u1EDBc.\n V\u1ECF kh\u1ED1i con v\u1EABn gi\u1EEF class CH\u1EB6N \u0111\u1EC3 l\u01B0\u1EDBi cha d\u1EEBng \u0111\u00FAng \u1EDF bi\u00EAn. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas()\">\n <!-- D\u1EA3i k\u00E9o: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i, ba ch\u1EA5m l\u00E0 d\u1EA5u hi\u1EC7u k\u00E9o quen thu\u1ED9c.\n Ng\u01B0\u1EDDi d\u00F9ng c\u1EA7n m\u1ED9t ch\u1ED7 b\u00E1m R\u00D5 R\u00C0NG thay v\u00EC \u0111o\u00E1n xem b\u1EA5m \u0111\u00E2u th\u00EC k\u00E9o \u0111\u01B0\u1EE3c\n (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng d\u1EA3i \u0111ang d\u00F9ng \u1EDF m\u00E0n Customer 360 c\u1EE7a mobio-web). -->\n @if (isEditMode()) {\n <!-- \uD83D\uDD34 D\u1EA3i PH\u1EA2I mang tay c\u1EA7m \u0110\u00DANG C\u1EA4P c\u1EE7a n\u00F3. D\u1EA3i nh\u1EADn chu\u1ED9t (`pointer-events: auto`),\n n\u00EAn n\u1EBFu kh\u00F4ng mang tay c\u1EA7m th\u00EC `checkDragHandleClass` duy\u1EC7t l\u00EAn g\u1EB7p tay c\u1EA7m c\u1EE7a\n container v\u00E0 nh\u1EA5c container thay v\u00EC kh\u1ED1i con (\u0111o 28/08/2026: k\u00E9o d\u1EA3i kh\u1ED1i con,\n container nh\u1EA3y 0,0 \u2192 0,1 c\u00F2n kh\u1ED1i con \u0111\u1EE9ng im).\n \uD83D\uDD34 D\u1EA3i CH\u1EC8 mang tay c\u1EA7m, C\u1EA4M mang th\u00EAm class ch\u1EB7n: `checkDragHandleClass` x\u00E9t hai\n class \u0111\u00F3 tr\u00EAn C\u00D9NG m\u1ED9t node n\u00EAn \u0111\u1EC3 chung l\u00E0 k\u00E9o h\u1ECFng (\u0111o 28/08/2026: b\u1EA5m d\u1EA3i kh\u1ED1i\n con k\u00E9o 140px, kh\u1ED1i \u0111\u1EE9ng im). Vi\u1EC7c ch\u1EB7n l\u01B0\u1EDBi cha do V\u1ECE KH\u1ED0I lo. -->\n <div\n class=\"libs-ui-grid-layout-drag-bar\"\n [class.libs-ui-grid-layout-drag-bar-child]=\"isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle]=\"!isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-drag-bar-dots\">\n @for (dot of dragBarDots; track dot) {\n <span class=\"libs-ui-grid-layout-drag-bar-dot\"></span>\n }\n </span>\n </div>\n }\n\n <!-- Khung ch\u1EE9a component c\u1EE7a n\u01A1i d\u00F9ng.\n \uD83D\uDD34 PH\u1EA2I c\u00F3 khung th\u1EADt (kh\u00F4ng ph\u1EA3i `ng-container` tr\u1ED1ng): n\u00F3 lo h\u1ED9 n\u01A1i d\u00F9ng ph\u1EA7n l\u1EA5p \u0111\u1EA7y\n \u00F4 v\u00E0 vi\u1EC1n b\u00E1o ch\u1EBF \u0111\u1ED9 s\u1EEDa. Kh\u00F4ng c\u00F3 khung th\u00EC m\u1ED7i component n\u1ED9i dung l\u1EA1i ph\u1EA3i t\u1EF1 vi\u1EBFt\n `:host { width/height: 100% }` + vi\u1EC1n \u0111\u1EE9t \u2014 \u0111\u00F3 l\u00E0 vi\u1EC7c c\u1EE7a th\u01B0 vi\u1EC7n, kh\u00F4ng ph\u1EA3i c\u1EE7a\n ng\u01B0\u1EDDi d\u00F9ng (ng\u01B0\u1EDDi d\u00F9ng ch\u1ED1t 28/08/2026).\n M\u1ED7i kh\u1ED1i m\u1ED9t ViewContainerRef ri\u00EAng n\u00EAn component n\u1EB1m \u0111\u00FAng \u00F4 c\u1EE7a n\u00F3. -->\n <div\n class=\"libs-ui-grid-layout-content-frame\"\n [class.libs-ui-grid-layout-content-frame-edit]=\"isEditMode() && !isContainer(item)\"\n [class.libs-ui-grid-layout-content-frame-empty]=\"isContainer(item)\">\n <ng-container #itemHost />\n </div>\n\n <!-- D\u1EA5u hi\u1EC7u CONTAINER: nh\u00E3n g\u00F3c tr\u00EAn-ph\u1EA3i + vi\u1EC1n \u0111\u1EE9t bao ru\u1ED9t. Kh\u00F4ng c\u00F3 d\u1EA5u hi\u1EC7u th\u00EC\n ng\u01B0\u1EDDi d\u00F9ng kh\u00F4ng bi\u1EBFt kh\u1ED1i n\u00E0o ch\u1EE9a \u0111\u01B0\u1EE3c kh\u1ED1i kh\u00E1c (ch\u1ED1t 28/08/2026, theo \u0111\u00FAng c\u00E1ch\n m\u00E0n Customer 360 c\u1EE7a mobio-web \u0111ang l\u00E0m). -->\n @if (isEditMode() && isContainer(item)) {\n <div class=\"libs-ui-grid-layout-container-label\">{{ 'i18n_container_block' | translate }}</div>\n <div class=\"libs-ui-grid-layout-container-border\"></div>\n }\n\n <!-- Thanh c\u00F4ng c\u1EE5: hi\u1EC7n khi r\u00EA chu\u1ED9t v\u00E0o kh\u1ED1i.\n \uD83D\uDD34 D\u00F9ng `libs_ui-components-buttons-button` \u2014 C\u1EA4M t\u1EF1 d\u1EF1ng th\u1EBB `<button>`.\n Component n\u00E0y \u0111\u00E3 c\u00F3 s\u1EB5n ki\u1EC3u n\u00FAt, c\u1EE1 n\u00FAt v\u00E0 bong b\u00F3ng ch\u00FA th\u00EDch theo \u0111\u00FAng b\u1ED9 giao di\u1EC7n\n c\u1EE7a s\u1EA3n ph\u1EA9m; t\u1EF1 d\u1EF1ng l\u00E0 m\u1ED7i n\u01A1i m\u1ED9t ki\u1EC3u v\u00E0 m\u1EA5t lu\u00F4n tooltip. -->\n @if (isEditMode() && !hiddenToolbar()) {\n @if (templateToolbar()) {\n <!-- N\u01A1i d\u00F9ng t\u1EF1 v\u1EBD thanh c\u00F4ng c\u1EE5: nh\u1EADn kh\u1ED1i + c\u00E1c h\u00E0m ph\u00E1t event qua context.\n\n \uD83D\uDD34 Kh\u1ED1i b\u1ECDc t\u1EF1 ch\u1EB7n `mousedown`/`click` n\u1ED5i l\u00EAn gridster. Thanh c\u00F4ng c\u1EE5 n\u1EB1m trong\n v\u00F9ng mang `dragHandleClass`; kh\u00F4ng ch\u1EB7n th\u00EC c\u00FA b\u1EA5m b\u1ECB hi\u1EC3u l\u00E0 b\u1EAFt \u0111\u1EA7u k\u00E9o v\u00E0\n `delayStart: 160` nu\u1ED1t lu\u00F4n `click` \u2014 n\u00FAt trong template ngo\u00E0i b\u1EA5m kh\u00F4ng \u0103n\n (\u0111o 09/09/2026: b\u1EA5m b\u1EB1ng `.click()` c\u1EE7a DOM th\u00EC m\u1EDF, b\u1EA5m chu\u1ED9t th\u1EADt th\u00EC kh\u00F4ng).\n Ch\u1EB7n \u1EDF \u0110\u00C2Y \u0111\u1EC3 n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i bi\u1EBFt b\u1EABy n\u00E0y. -->\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n role=\"toolbar\"\n tabindex=\"0\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\"\n (mousedown)=\"handlerStopEvent($event)\"\n (keydown)=\"handlerStopEvent($event)\"\n (click)=\"handlerStopEvent($event)\">\n <ng-container\n *ngTemplateOutlet=\"templateToolbar()!; context: toolbarContexts()[itemIndex]\" />\n </div>\n } @else {\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-dim-badge\">\n {{ item.rows }} h\u00E0ng \u00D7 {{ item.cols }} c\u1ED9t\n </span>\n @if (canAddInside(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-add'\"\n [popover]=\"{ config: { content: 'Th\u00EAm kh\u1ED1i v\u00E0o trong', zIndex: 1300 } }\"\n (outClick)=\"handlerAddInside($event, item)\" />\n }\n @if (canToggleType(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"isContainer(item) ? 'libs-ui-icon-split-cell' : 'libs-ui-icon-merge-cell'\"\n [popover]=\"{ config: { content: isContainer(item) ? '\u0110\u01B0a v\u1EC1 kh\u1ED1i \u0111\u01A1n' : 'Chuy\u1EC3n th\u00E0nh kh\u1ED1i gh\u00E9p', zIndex: 1300 } }\"\n (outClick)=\"handlerToggleType($event, item)\" />\n }\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-setting'\"\n [popover]=\"{ config: { content: 'C\u1EA5u h\u00ECnh th\u1EBB', zIndex: 1300 } }\"\n (outClick)=\"handlerConfig($event, item)\" />\n <libs_ui-components-buttons-button\n [type]=\"'button-third-hover-danger'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-remove'\"\n [popover]=\"{ config: { content: isContainer(item) ? 'Xo\u00E1 c\u1EA3 kh\u1ED1i gh\u00E9p' : 'Xo\u00E1 kh\u1ED1i', zIndex: 1300 } }\"\n (outClick)=\"handlerRemove($event, item)\" />\n </div>\n }\n }\n\n @if (item.children && item.children.length > 0) {\n <!-- Kh\u1ED1i c\u00F3 con \u2192 ru\u1ED9t n\u00F3 l\u1EA1i l\u00E0 m\u1ED9t m\u1EB7t ph\u1EB3ng n\u1EEFa. \u0110\u1EC7 quy \u1EDF \u0111\u00E2y, n\u01A1i d\u00F9ng kh\u00F4ng ph\u1EA3i lo.\n L\u1EC1 \u0111\u1EB7t \u1EDF \u0110\u00C2Y (v\u1ECF container), kh\u00F4ng \u0111\u1EB7t v\u00E0o l\u01B0\u1EDBi con \u2014 l\u01B0\u1EDBi con gi\u1EEF nguy\u00EAn khe 2px. -->\n <libs_ui-services-grid_layout-canvas\n class=\"block h-full w-full\"\n [style.padding]=\"containerPadding()\"\n [node]=\"item\"\n [depth]=\"depth() + 1\"\n [hiddenToolbar]=\"hiddenToolbar()\"\n [templateToolbar]=\"templateToolbar()\"\n [selectedNodeId]=\"selectedNodeId()\"\n (outSelectNode)=\"outSelectNode.emit($event)\"\n (outConfigNode)=\"outConfigNode.emit($event)\"\n (outChange)=\"outChange.emit($event)\"\n (outRemoveNode)=\"outRemoveNode.emit($event)\"\n (outAddBlockInside)=\"outAddBlockInside.emit($event)\"\n (outToggleType)=\"outToggleType.emit($event)\" />\n }\n </div>\n </gridster-item>\n }\n </gridster>\n </div>\n}\n", styles: [":host{display:block;position:relative;box-sizing:border-box;width:100%;height:100%;min-width:0;min-height:0}.libs-ui-grid-layout-block-shell{container-type:inline-size;display:block;box-sizing:border-box;height:100%}.libs-ui-grid-layout-drag-bar{position:absolute;top:2px;left:50%;z-index:15;display:flex;align-items:center;justify-content:center;width:64px;height:16px;background:transparent;transform:translate(-50%);opacity:1;transition:opacity .12s ease;pointer-events:auto;cursor:move}.libs-ui-grid-layout-drag-bar-dots{display:grid;grid-template-columns:repeat(3,3px);gap:3px;color:#9ca2ad}.libs-ui-grid-layout-drag-bar-dot{width:3px;height:3px;background:currentColor;border-radius:50%}.libs-ui-grid-layout-drag-bar:hover .libs-ui-grid-layout-drag-bar-dots{color:#3d6ef5}.libs-ui-grid-layout-drag-bar:not(.libs-ui-grid-layout-drag-bar-child){opacity:1}.libs-ui-grid-layout-drag-bar-child{top:auto;bottom:0;background:#d9f2e4;border-radius:4px 4px 0 0}.libs-ui-grid-layout-drag-bar-child .libs-ui-grid-layout-drag-bar-dots{color:#9ca2ad}.libs-ui-grid-layout-drag-bar-child:hover .libs-ui-grid-layout-drag-bar-dots{color:#00a757}.libs-ui-grid-layout-drag-bar-child{opacity:0}.libs-ui-grid-layout-block-shell:hover>.libs-ui-grid-layout-drag-bar-child{opacity:1}.libs-ui-grid-layout-block-shell:has(>.libs-ui-grid-layout-drag-bar:not(.libs-ui-grid-layout-drag-bar-child):hover){outline:2px solid #3d6ef5;outline-offset:-2px;border-radius:8px}.libs-ui-grid-layout-block-shell:has(>.libs-ui-grid-layout-drag-bar-child:hover){outline:2px solid #00a757;outline-offset:-2px;border-radius:8px}.libs-ui-grid-layout-container-label{position:absolute;left:6px;top:2px;display:flex;height:20px;align-items:center;z-index:3;padding:0 6px;color:#3d6ef5;font-weight:500;font-size:10px;line-height:14px;background:#dbe4ff;border-radius:4px;pointer-events:none}.libs-ui-grid-layout-content-frame{box-sizing:border-box;width:100%;height:100%;overflow:hidden;border-radius:6px}.libs-ui-grid-layout-content-frame ::ng-deep>*{display:block;box-sizing:border-box;width:100%;height:100%}.libs-ui-grid-layout-content-frame-empty{height:0}.libs-ui-grid-layout-content-frame-edit{border:1px dashed #c3cede}.libs-ui-grid-layout-container-border{position:absolute;inset:0;border:1px dashed #9db4f0;border-radius:8px;pointer-events:none}.libs-ui-grid-layout-toolbar{position:absolute;top:6px;right:8px;z-index:24;display:flex;gap:2px;align-items:center;height:20px;background:#fff;border-radius:4px;box-shadow:0 2px 8px #0716311f;opacity:0;transition:opacity .12s ease}.libs-ui-grid-layout-toolbar-child{top:50%;right:8px;left:auto;transform:translateY(-50%)}.libs-ui-grid-layout-block-shell:hover>.libs-ui-grid-layout-toolbar{opacity:1}:host ::ng-deep gridster{background:transparent}:host ::ng-deep .gridster-item-resizable-handler.handle-n,:host ::ng-deep .gridster-item-resizable-handler.handle-s{height:16px}:host ::ng-deep .gridster-item-resizable-handler.handle-e,:host ::ng-deep .gridster-item-resizable-handler.handle-w{width:16px}:host ::ng-deep .gridster-item-resizable-handler.handle-n{top:0}:host ::ng-deep .gridster-item-resizable-handler.handle-s{bottom:0}:host ::ng-deep .gridster-item-resizable-handler.handle-e{right:0}:host ::ng-deep .gridster-item-resizable-handler.handle-w{left:0}:host ::ng-deep gridster-item.gridster-item-moving,:host ::ng-deep gridster-item.gridster-item-resizing{z-index:30!important}:host ::ng-deep gridster-item.gridster-item-moving>.libs-ui-grid-layout-block-shell,:host ::ng-deep gridster-item.gridster-item-resizing>.libs-ui-grid-layout-block-shell{outline:2px solid #3d6ef5;outline-offset:-2px;border-radius:8px}:host ::ng-deep gridster gridster gridster-item.gridster-item-moving>.libs-ui-grid-layout-block-shell,:host ::ng-deep gridster gridster gridster-item.gridster-item-resizing>.libs-ui-grid-layout-block-shell{outline-color:#00a757}:host ::ng-deep gridster-item.libs-ui-grid-layout-selected>.libs-ui-grid-layout-block-shell{outline:2px solid #2563eb!important;outline-offset:-2px!important;border-radius:8px!important;box-shadow:0 0 0 4px #2563eb26!important}:host ::ng-deep gridster.display-grid .gridster-column,:host ::ng-deep gridster gridster.display-grid .gridster-column{border-right:1px solid rgba(226,232,240,.7)!important;border-left:none!important;pointer-events:none!important;height:100%!important;min-height:100%!important}:host ::ng-deep gridster.display-grid .gridster-column:first-child,:host ::ng-deep gridster gridster.display-grid .gridster-column:first-child{border-left:1px solid rgba(226,232,240,.7)!important}:host ::ng-deep gridster.display-grid .gridster-row,:host ::ng-deep gridster gridster.display-grid .gridster-row{border-bottom:1px solid rgba(226,232,240,.7)!important;border-top:none!important;pointer-events:none!important}:host ::ng-deep gridster.display-grid .gridster-row:first-child,:host ::ng-deep gridster gridster.display-grid .gridster-row:first-child{border-top:1px solid rgba(226,232,240,.7)!important}:host ::ng-deep gridster gridster.display-grid{background-color:#f8fafc80!important}:host ::ng-deep gridster-preview{background:#2563eb1f!important;border:1.5px dashed #2563eb!important;border-radius:6px!important;z-index:25!important}.libs-ui-grid-layout-dim-badge{display:inline-flex;align-items:center;height:20px;padding:0 6px;background-color:#f1f5f9;border:1px solid #e2e8f0;border-radius:4px;color:#64748b;font-size:10px;font-weight:500;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;pointer-events:none;white-space:nowrap}@container (max-width: 80px){.libs-ui-grid-layout-drag-bar{top:26px}}.libs-ui-grid-layout-stage{box-sizing:border-box;transform-origin:top center;transition:zoom .15s ease-out}#canvas-zoom-dock{box-shadow:0 10px 25px -5px #0000001a,0 8px 10px -6px #0000001a}\n"] }]
|
|
565
|
+
}], ctorParameters: () => [], propDecorators: { handlerDocumentKeyDown: [{
|
|
566
|
+
type: HostListener,
|
|
567
|
+
args: ['document:keydown', ['$event']]
|
|
568
|
+
}], handlerDocumentClick: [{
|
|
569
|
+
type: HostListener,
|
|
570
|
+
args: ['document:click', ['$event']]
|
|
571
|
+
}] } });
|
|
572
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC1sYXlvdXQtY2FudmFzLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvc2VydmljZXMvZ3JpZC1sYXlvdXQvc3JjL2NhbnZhcy9ncmlkLWxheW91dC1jYW52YXMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy11aS9zZXJ2aWNlcy9ncmlkLWxheW91dC9zcmMvY2FudmFzL2dyaWQtbGF5b3V0LWNhbnZhcy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFFVCxZQUFZLEVBR1osZ0JBQWdCLEVBQ2hCLFFBQVEsRUFDUixNQUFNLEVBQ04sTUFBTSxFQUNOLEtBQUssRUFDTCxLQUFLLEVBQ0wsTUFBTSxFQUNOLE1BQU0sRUFDTixTQUFTLEVBQ1QsWUFBWSxHQUNiLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzVGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxjQUFjLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDL0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNsRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQWFqRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQzs7OztBQUVuRjs7Ozs7R0FLRztBQVNILE1BQU0sT0FBTywrQkFBK0I7SUFDMUM7Ozs7O09BS0c7SUFDTSxJQUFJLEdBQUcsS0FBSyxFQUFxQixDQUFDO0lBQzNDLDhEQUE4RDtJQUNyRCxLQUFLLEdB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ckMsc0JBQXNCO3NCQUQvQixZQUFZO3VCQUFDLGtCQUFrQixFQUFFLENBQUMsUUFBUSxDQUFDO2dCQXFCbEMsb0JBQW9CO3NCQUQ3QixZQUFZO3VCQUFDLGdCQUFnQixFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENvbXBvbmVudCxcbiAgRWxlbWVudFJlZixcbiAgSG9zdExpc3RlbmVyLFxuICBPbkRlc3Ryb3ksXG4gIFRlbXBsYXRlUmVmLFxuICBWaWV3Q29udGFpbmVyUmVmLFxuICBjb21wdXRlZCxcbiAgZWZmZWN0LFxuICBpbmplY3QsXG4gIGlucHV0LFxuICBtb2RlbCxcbiAgb3V0cHV0LFxuICBzaWduYWwsXG4gIHZpZXdDaGlsZCxcbiAgdmlld0NoaWxkcmVuLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE5nVGVtcGxhdGVPdXRsZXQgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgTGlic1VpQ29tcG9uZW50c0J1dHRvbnNCdXR0b25Db21wb25lbnQgfSBmcm9tICdAbGlicy11aS9jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uJztcbmltcG9ydCB7IEdyaWRzdGVyQ29tcG9uZW50LCBHcmlkc3Rlck1vZHVsZSB9IGZyb20gJ2dyaWRzdGVyMTgnO1xuaW1wb3J0IHsgVHJhbnNsYXRlTW9kdWxlIH0gZnJvbSAnQG5neC10cmFuc2xhdGUvY29yZSc7XG5pbXBvcnQgeyBlbXB0eVJvb3ROb2RlIH0gZnJvbSAnLi4vZGVmaW5lcy9ncmlkc3Rlci1jb25maWcuZGVmaW5lJztcbmltcG9ydCB7IExpYnNVaUdyaWRMYXlvdXRTZXJ2aWNlIH0gZnJvbSAnLi4vZ3JpZC1sYXlvdXQuc2VydmljZSc7XG5pbXBvcnQge1xuICBUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQsXG4gIFRfZ3JpZExheW91dF9hZGRFdmVudCxcbiAgVF9ncmlkTGF5b3V0X2NoYW5nZUV2ZW50LFxuICBUX2dyaWRMYXlvdXRfZHJvcEV2ZW50LFxuICBUX2dyaWRMYXlvdXRfZHJvcEdob3N0LFxuICBUX2dyaWRMYXlvdXRfZXh0ZXJuYWxEcmFnUGF5bG9hZCxcbiAgVF9ncmlkTGF5b3V0X25ld05vZGUsXG4gIFRfZ3JpZExheW91dF9ub2RlLFxuICBUX2dyaWRMYXlvdXRfdG9vbGJhckNvbnRleHQsXG4gIFRfZ3JpZExheW91dF96b29tUHJlc2V0LFxufSBmcm9tICcuLi9pbnRlcmZhY2VzL2dyaWQtbGF5b3V0LmludGVyZmFjZSc7XG5pbXBvcnQgeyBjYWxjdWxhdGVEcm9wR2hvc3QsIHBhcnNlRHJvcFBheWxvYWQgfSBmcm9tICcuLi9kZWZpbmVzL2NvbGxpc2lvbi5kZWZpbmUnO1xuXG4vKipcbiAqIE3hurd0IHBo4bqzbmcga8OpbyB0aOG6oy4gxJDhu4cgcXV5OiBraOG7kWkgY8OzIGBjaGlsZHJlbmAgdGjDrCBydeG7mXQgbsOzIGzhuqFpIGzDoCBt4buZdCBt4bq3dCBwaOG6s25nIG7hu69hLlxuICpcbiAqIE7GoWkgZMO5bmcgS0jDlE5HIGThu7FuZyBjb21wb25lbnQgbsOgeSB0cuG7sWMgdGnhur9wIGNobyB04burbmcga2jhu5FpIOKAlCBjaOG7iSDEkeG6t3QgTeG7mFQgdGjhursg4bufIGfhu5FjIHbDoCB0cnV54buBblxuICogYG5vZGVDb21wb25lbnRUeXBlYCBxdWEgYExpYnNVaUdyaWRMYXlvdXRTZXJ2aWNlLmluaXQoKWAuIFRoxrAgdmnhu4duIHThu7EgZ+G6r24gY29tcG9uZW50IGNobyBt4buNaSBraOG7kWkuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2xpYnNfdWktc2VydmljZXMtZ3JpZF9sYXlvdXQtY2FudmFzJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW0dyaWRzdGVyTW9kdWxlLCBMaWJzVWlDb21wb25lbnRzQnV0dG9uc0J1dHRvbkNvbXBvbmVudCwgTmdUZW1wbGF0ZU91dGxldCwgVHJhbnNsYXRlTW9kdWxlXSxcbiAgdGVtcGxhdGVVcmw6ICcuL2dyaWQtbGF5b3V0LWNhbnZhcy5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsOiAnLi9ncmlkLWxheW91dC1jYW52YXMuY29tcG9uZW50LnNjc3MnLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgTGlic1VpR3JpZExheW91dENhbnZhc0NvbXBvbmVudCBpbXBsZW1lbnRzIEFmdGVyVmlld0luaXQsIE9uRGVzdHJveSB7XG4gIC8qKlxuICAgKiBAaW50ZXJuYWwgQ2jhu4kgbeG6t3QgcGjhurNuZyBM4buSTkcgZMO5bmcg4oCUIHRoxrAgdmnhu4duIHThu7EgdHJ1eeG7gW4ga2hpIHbhur0gcnXhu5l0IGNvbnRhaW5lci5cbiAgICpcbiAgICogTsahaSBkw7luZyBLSMOUTkcgdHJ1eeG7gW46IG3hurd0IHBo4bqzbmcgZ+G7kWMgxJHhu41jIGPDonkgdOG7qyBzZXJ2aWNlICjEkcOjIMSRxrBhIHbDoG8gcXVhIGBpbml0KClgKSwgdHJ1eeG7gW4gdGjDqm1cbiAgICog4bufIMSRw6J5IGzDoCBraGFpIGhhaSBs4bqnbiBjw7luZyBt4buZdCB0aOG7qSAobmfGsOG7nWkgZMO5bmcgY2jhu5F0IDI4LzA4LzIwMjYpLlxuICAgKi9cbiAgcmVhZG9ubHkgbm9kZSA9IGlucHV0PFRfZ3JpZExheW91dF9ub2RlPigpO1xuICAvKiogQGludGVybmFsIENo4buJIG3hurd0IHBo4bqzbmcgTOG7kk5HIGTDuW5nIOKAlCB0aMawIHZp4buHbiB04buxIHRydXnhu4FuLiAqL1xuICByZWFkb25seSBkZXB0aCA9IGlucHV0PG51bWJlcj4oMCk7XG5cbiAgLyoqXG4gICAqIOG6qG4gaOG6s24gdGhhbmggY8O0bmcgY+G7pSBt4bq3YyDEkeG7i25oICh0aMOqbS12w6BvLXRyb25nIMK3IGfhu5lwL3TDoWNoIMK3IHhvw6EpIGPhu6dhIE3hu4xJIGto4buRaS5cbiAgICpcbiAgICogRMO5bmcga2hpIHPhuqNuIHBo4bqpbSBnb20gY8OhYyBow6BuaCDEkeG7mW5nIMSRw7MgdsOgbyBjaOG7lyBraMOhYyDigJQgdmQgbeG7mXQgbsO6dCDii64gbuG6sW0gdHJvbmcgY2jDrW5oIG7hu5lpIGR1bmdcbiAgICoga2jhu5FpLiDhuqhuIHLhu5NpIHRow6wgYG91dFJlbW92ZU5vZGVgIMK3IGBvdXRBZGRCbG9ja0luc2lkZWAgwrcgYG91dFRvZ2dsZVR5cGVgIGtow7RuZyBjw7JuIG5ndeG7k24gcGjDoXRcbiAgICogdOG7qyB0aMawIHZp4buHbjsgbsahaSBkw7luZyB04buxIGfhu41pIGByZW1vdmVCbG9ja2AgwrcgYGFkZEJsb2NrYCDCtyBgdG9nZ2xlVHlwZWAgY+G7p2Egc2VydmljZS5cbiAgICovXG4gIHJlYWRvbmx5IGhpZGRlblRvb2xiYXIgPSBpbnB1dDxib29sZWFuLCBib29sZWFuIHwgdW5kZWZpbmVkPihmYWxzZSwgeyB0cmFuc2Zvcm06ICh2YWx1ZSkgPT4gdmFsdWUgPz8gZmFsc2UgfSk7XG5cbiAgLyoqXG4gICAqIFRlbXBsYXRlIHThu7EgduG6vSB0aGFuaCBjw7RuZyBj4bulLCB0aGF5IGNobyBi4buZIG7DunQgbeG6t2MgxJHhu4tuaC5cbiAgICpcbiAgICogTmjhuq1uIGNvbnRleHQgYHsgJGltcGxpY2l0OiBub2RlLCBpc0NvbnRhaW5lciwgY2FuQWRkSW5zaWRlLCBjYW5Ub2dnbGVUeXBlIH1gIMSR4buDIG7GoWkgZMO5bmcgduG6vSDEkcO6bmdcbiAgICogbsO6dCBjaG8gdOG7q25nIGto4buRaSwgcuG7k2kgZ+G7jWkgbmfGsOG7o2MgcXVhIGBvdXRSZW1vdmVOb2RlYCDCtyBgb3V0QWRkQmxvY2tJbnNpZGVgIMK3IGBvdXRUb2dnbGVUeXBlYFxuICAgKiBi4bqxbmcgY2jDrW5oIGPDoWMgaMOgbSB0cm9uZyBjb250ZXh0LiBUcnV54buBbiBj4bqjIGBoaWRkZW5Ub29sYmFyYCB2w6AgYHRlbXBsYXRlVG9vbGJhcmAgdGjDrFxuICAgKiBgaGlkZGVuVG9vbGJhcmAgdGjhuq9uZyDigJQga2jDtG5nIHbhur0gZ8OsLlxuICAgKi9cbiAgcmVhZG9ubHkgdGVtcGxhdGVUb29sYmFyID0gaW5wdXQ8VGVtcGxhdGVSZWY8VF9ncmlkTGF5b3V0X3Rvb2xiYXJDb250ZXh0PiB8IHVuZGVmaW5lZD4odW5kZWZpbmVkKTtcblxuICAvKipcbiAgICogQuG7kSBj4bulYyB24burYSDEkeG7lWkgZG8gTkfGr+G7nEkgRMOZTkcga8OpbyBob+G6t2MgxJHhu5VpIGvDrWNoIHRoxrDhu5tjIGto4buRaS5cbiAgICpcbiAgICogTsahaSBkw7luZyB04buxIGfhu41pIGBhZGRCbG9ja2AgwrcgYHJlbW92ZUJsb2NrYCDCtyBgY29tcGFjdGAgdGjDrCBLSMOUTkcgYuG6r24g4oCUIGjhu40gduG7q2EgZ+G7jWkgdGjDrCDEkcOjIGJp4bq/dCxcbiAgICogYuG6r24gbmfGsOG7o2MgbOG6oWkgbMOgIHRo4burYSAobmfGsOG7nWkgZMO5bmcgY2jhu5F0IDI4LzA4LzIwMjYpLlxuICAgKi9cbiAgcmVhZG9ubHkgb3V0Q2hhbmdlID0gb3V0cHV0PFRfZ3JpZExheW91dF9jaGFuZ2VFdmVudD4oKTtcbiAgLyoqXG4gICAqIE5nxrDhu51pIGTDuW5nIGLhuqVtIG7DunQgWE/DgSB0csOqbiBt4buZdCBraOG7kWkuIFRoxrAgdmnhu4duIENIxq9BIHhvw6Eg4oCUIGfhu41pIGBldmVudC5jb25maXJtKClgIMSR4buDIHhvw6EgdGjhuq10LlxuICAgKiBD4bqnbiBo4buPaSB0csaw4bubYyB0aMOsIGjhu49pIHhvbmcgbeG7m2kgZ+G7jWk7IGtow7RuZyBjaG8gcGjDqXAgdGjDrCDEkeG7q25nIGfhu41pLlxuICAgKi9cbiAgcmVhZG9ubHkgb3V0UmVtb3ZlTm9kZSA9IG91dHB1dDxUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQ+KCk7XG4gIC8qKlxuICAgKiBOZ8aw4budaSBkw7luZyBi4bqlbSBuw7p0IFRIw4pNIHRyw6puIG3hu5l0IGNvbnRhaW5lci4gVGjGsCB2aeG7h24gS0jDlE5HIHThu7EgdGjDqm0g4oCUIG7DsyBraMO0bmcgYmnhur90IGLhuqFuIGPDs1xuICAgKiBuaOG7r25nIGxv4bqhaSBraOG7kWkgbsOgby4gTeG7nyBtZW51IGNo4buNbiBsb+G6oWkgcuG7k2kgZ+G7jWkgYGV2ZW50LmNvbmZpcm0oaXRlbSlgIHbhu5tpIGto4buRaSBi4bqhbiBtdeG7kW4gdGjDqm0uXG4gICAqL1xuICByZWFkb25seSBvdXRBZGRCbG9ja0luc2lkZSA9IG91dHB1dDxUX2dyaWRMYXlvdXRfYWRkRXZlbnQ+KCk7XG4gIC8qKiBOZ8aw4budaSBkw7luZyBi4bqlbSBuw7p0IEfhu5hQL1TDgUNILiBUaMawIHZp4buHbiBjaMawYSBsw6BtIGfDrCDigJQgZ+G7jWkgYGV2ZW50LmNvbmZpcm0oKWAgxJHhu4MgdGjhu7FjIGhp4buHbi4gKi9cbiAgcmVhZG9ubHkgb3V0VG9nZ2xlVHlwZSA9IG91dHB1dDxUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQ+KCk7XG5cbiAgLyoqIELhuq10IHTDrW5oIG7Eg25nIHRodSBwaMOzbmcgKHpvb20pIGNhbnZhcy4gQ2jhu4kgw6FwIGThu6VuZyBjaG8gY2FudmFzIGfhu5FjIChkZXB0aCAwKS4gKi9cbiAgcmVhZG9ubHkgZW5hYmxlWm9vbSA9IGlucHV0PGJvb2xlYW4sIGJvb2xlYW4gfCB1bmRlZmluZWQ+KGZhbHNlLCB7IHRyYW5zZm9ybTogKHZhbHVlKSA9PiB2YWx1ZSA/PyBmYWxzZSB9KTtcblxuICAvKiogVOG7tyBs4buHIHRodSBwaMOzbmcgaGnhu4duIHThuqFpICho4buXIHRy4bujIHR3by13YXkgYmluZGluZyBbKHpvb20pXT1cIm15Wm9vbVwiKS4gKi9cbiAgcmVhZG9ubHkgem9vbSA9IG1vZGVsPG51bWJlciB8IHVuZGVmaW5lZD4odW5kZWZpbmVkKTtcblxuICAvKiogR2nhu5tpIGjhuqFuIHRodSBwaMOzbmcgdOG7kWkgdGhp4buDdS4gTeG6t2MgxJHhu4tuaCAwLjQgKDQwJSkuICovXG4gIHJlYWRvbmx5IHpvb21NaW4gPSBpbnB1dDxudW1iZXI+KDAuNCk7XG5cbiAgLyoqIEdp4bubaSBo4bqhbiB0aHUgcGjDs25nIHThu5FpIMSRYS4gTeG6t2MgxJHhu4tuaCAxLjUgKDE1MCUpLiAqL1xuICByZWFkb25seSB6b29tTWF4ID0gaW5wdXQ8bnVtYmVyPigxLjUpO1xuXG4gIC8qKiBCxrDhu5tjIG5o4bqjeSB0aHUgcGjDs25nIG3hu5dpIGzhuqduIGLhuqVtICsvLSBob+G6t2MgbMSDbiBjaHXhu5l0LiBN4bq3YyDEkeG7i25oIDAuMDUgKDUlKS4gKi9cbiAgcmVhZG9ubHkgem9vbVN0ZXAgPSBpbnB1dDxudW1iZXI+KDAuMDUpO1xuXG4gIC8qKiBDaGnhu4F1IHLhu5luZyBjaHXhuqluIGPhu6dhIGNhbnZhcyBzdGFnZSB0w61uaCBi4bqxbmcgcHguIE3hurdjIMSR4buLbmggMTY2OC4gKi9cbiAgcmVhZG9ubHkgc3RhZ2VXaWR0aCA9IGlucHV0PG51bWJlcj4oMTY2OCk7XG5cbiAgLyoqIENoaeG7gXUgY2FvIHThu5FpIHRoaeG7g3UgY+G7p2EgY2FudmFzIHN0YWdlIHTDrW5oIGLhurFuZyBweC4gTeG6t2MgxJHhu4tuaCA5MzcuICovXG4gIHJlYWRvbmx5IHN0YWdlTWluSGVpZ2h0ID0gaW5wdXQ8bnVtYmVyPig5MzcpO1xuXG4gIC8qKiBLaMOzYSBsxrB1IHThu7cgbOG7hyB0aHUgcGjDs25nIHbDoG8gbG9jYWxTdG9yYWdlLiBN4bq3YyDEkeG7i25oICdvY2JfYmlfY2FudmFzX3pvb20nLiAqL1xuICByZWFkb25seSBzdG9yYWdlS2V5ID0gaW5wdXQ8c3RyaW5nIHwgdW5kZWZpbmVkPignb2NiX2JpX2NhbnZhc196b29tJyk7XG5cbiAgLyoqIEhp4buDbiB0aOG7iyB0aGFuaCBkb2NrIMSRaeG7gXUga2hp4buDbiB0aHUgcGjDs25nIG7hu5VpIOG7nyBnw7NjIGTGsOG7m2kgcGjhuqNpLiBN4bq3YyDEkeG7i25oIHRydWUuICovXG4gIHJlYWRvbmx5IHNob3dab29tRG9jayA9IGlucHV0PGJvb2xlYW4sIGJvb2xlYW4gfCB1bmRlZmluZWQ+KHRydWUsIHsgdHJhbnNmb3JtOiAodmFsdWUpID0+IHZhbHVlID8/IHRydWUgfSk7XG5cbiAgLyoqIFThu7EgxJHhu5luZyB24burYSBtw6BuIGjDrG5oIGtoaSB04bqjaSB0cmFuZyBs4bqnbiDEkeG6p3UgbuG6v3Uga2h1bmcgbmjDrG4gbmjhu48gaMahbiBzdGFnZVdpZHRoLiBN4bq3YyDEkeG7i25oIHRydWUuICovXG4gIHJlYWRvbmx5IGF1dG9GaXRPbkxvYWQgPSBpbnB1dDxib29sZWFuLCBib29sZWFuIHwgdW5kZWZpbmVkPih0cnVlLCB7IHRyYW5zZm9ybTogKHZhbHVlKSA9PiB2YWx1ZSA/PyB0cnVlIH0pO1xuXG4gIC8qKiBQaMOhdCByYSBraGkgdOG7tyBs4buHIHRodSBwaMOzbmcgdGhheSDEkeG7lWkuICovXG4gIHJlYWRvbmx5IG91dFpvb21DaGFuZ2UgPSBvdXRwdXQ8bnVtYmVyPigpO1xuXG4gIC8qKiBC4bqtdCB0w61uaCBuxINuZyBrw6lvIHRo4bqjIHRo4bq7IHThu6sgYsOqbiBuZ2/DoGkgdsOgbyBjYW52YXMgKG3hurdjIMSR4buLbmggdHJ1ZSkgKi9cbiAgcmVhZG9ubHkgZW5hYmxlRXh0ZXJuYWxEcm9wID0gaW5wdXQ8Ym9vbGVhbiwgYm9vbGVhbiB8IHVuZGVmaW5lZD4odHJ1ZSwgeyB0cmFuc2Zvcm06ICh2YWx1ZSkgPT4gdmFsdWUgPz8gdHJ1ZSB9KTtcblxuICAvKiogTHXDtG4gaGnhu4NuIHRo4buLIMSRxrDhu51uZyBr4bq7IGzGsOG7m2kgMjQgY+G7mXQgKi9cbiAgcmVhZG9ubHkgc2hvd0dyaWRMaW5lcyA9IGlucHV0PGJvb2xlYW4sIGJvb2xlYW4gfCB1bmRlZmluZWQ+KGZhbHNlLCB7IHRyYW5zZm9ybTogKHZhbHVlKSA9PiB2YWx1ZSA/PyBmYWxzZSB9KTtcblxuICAvKiogTcOjIGlkIGPhu6dhIGto4buRaSDEkWFuZyDEkcaw4bujYyBjaOG7jW4gKi9cbiAgcmVhZG9ubHkgc2VsZWN0ZWROb2RlSWQgPSBtb2RlbDxzdHJpbmcgfCBudWxsIHwgdW5kZWZpbmVkPih1bmRlZmluZWQpO1xuXG4gIC8qKiBQaMOhdCByYSBraGkgdGjhuqMgbeG7mXQgdGjhursgbeG7m2kgdOG7qyBiw6puIG5nb8OgaSB2w6BvIGNhbnZhcyAqL1xuICByZWFkb25seSBvdXREcm9wTm9kZSA9IG91dHB1dDxUX2dyaWRMYXlvdXRfZHJvcEV2ZW50PigpO1xuXG4gIC8qKiBQaMOhdCByYSBraGkgbmfGsOG7nWkgZMO5bmcgY2xpY2sgY2jhu41uIG3hu5l0IGto4buRaSAqL1xuICByZWFkb25seSBvdXRTZWxlY3ROb2RlID0gb3V0cHV0PFRfZ3JpZExheW91dF9ub2RlPigpO1xuXG4gIC8qKiBQaMOhdCByYSBraGkgbmfGsOG7nWkgZMO5bmcgYuG6pW0gbsO6dCBj4bqldSBow6xuaCB0csOqbiB0aGFuaCBjw7RuZyBj4bulIGPhu6dhIGto4buRaSAqL1xuICByZWFkb25seSBvdXRDb25maWdOb2RlID0gb3V0cHV0PFRfZ3JpZExheW91dF9hY3Rpb25FdmVudD4oKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IGdyaWRzdGVyUmVmID0gdmlld0NoaWxkKEdyaWRzdGVyQ29tcG9uZW50KTtcbiAgcHJpdmF0ZSByZWFkb25seSBzY3JvbGxBcmVhUmVmID0gdmlld0NoaWxkPEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+Pignc2Nyb2xsQXJlYScpO1xuICBwcml2YXRlIHJlYWRvbmx5IHpvb21Eb2NrUmVmID0gdmlld0NoaWxkPEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+Pignem9vbURvY2snKTtcbiAgLyoqIG3hu5dpIMO0IG3hu5l0IGNo4buXIGfhuq9uIHJpw6puZyDigJQgY8O5bmcgdGjhu6kgdOG7sSB24bubaSBgY2hpbGRyZW4oKWAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBpdGVtSG9zdHMgPSB2aWV3Q2hpbGRyZW4oJ2l0ZW1Ib3N0JywgeyByZWFkOiBWaWV3Q29udGFpbmVyUmVmIH0pO1xuXG4gIC8qKiBLaHVuZyB4ZW0gdHLGsOG7m2MgduG7iyB0csOtIHRo4bqjIHRo4bq7IHThu6sgYsOqbiBuZ2/DoGkgKHNuYXBwZWQgZ2hvc3QgcHJldmlldykgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGRyb3BHaG9zdCA9IHNpZ25hbDxUX2dyaWRMYXlvdXRfZHJvcEdob3N0IHwgdW5kZWZpbmVkPih1bmRlZmluZWQpO1xuXG4gIHByaXZhdGUgYWN0aXZlSG92ZXJlZEVsPzogSFRNTEVsZW1lbnQ7XG4gIHByaXZhdGUgcm9vdEdyaWRzdGVyRWw/OiBIVE1MRWxlbWVudDtcblxuICAvKiogTWVudSBjaOG7jW4gcHJlc2V0IHRodSBwaMOzbmcgxJFhbmcgbeG7nyBoYXkgxJHDs25nICovXG4gIHByb3RlY3RlZCByZWFkb25seSBpc01lbnVPcGVuID0gc2lnbmFsPGJvb2xlYW4+KGZhbHNlKTtcblxuICAvKiogQ8OhYyBt4buRYyB0aHUgcGjDs25nIMSR4buLbmggc+G6tW4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHpvb21QcmVzZXRzOiBBcnJheTxUX2dyaWRMYXlvdXRfem9vbVByZXNldD4gPSBbXG4gICAgeyBsYWJlbDogJ2kxOG5fem9vbV9wcmVzZXRfb3ZlcnZpZXcnLCB2YWx1ZTogMC41IH0sXG4gICAgeyBsYWJlbDogJ2kxOG5fem9vbV9wcmVzZXRfc21hbGxfbGFwdG9wJywgdmFsdWU6IDAuNjcgfSxcbiAgICB7IGxhYmVsOiAnaTE4bl96b29tX3ByZXNldF9sYXB0b3BfMTA4MHAnLCB2YWx1ZTogMC43NSB9LFxuICAgIHsgbGFiZWw6ICdpMThuX3pvb21fcHJlc2V0X3NjcmVlbl8xMDgwcCcsIHZhbHVlOiAwLjg1IH0sXG4gICAgeyBsYWJlbDogJ2kxOG5fem9vbV9wcmVzZXRfc3RhbmRhcmQnLCB2YWx1ZTogMC45IH0sXG4gICAgeyBsYWJlbDogJ2kxOG5fem9vbV9wcmVzZXRfb3JpZ2luYWxfc2l6ZScsIHZhbHVlOiAxIH0sXG4gICAgeyBsYWJlbDogJ2kxOG5fem9vbV9wcmVzZXRfbGFyZ2Vfc2NyZWVuJywgdmFsdWU6IDEuMSB9LFxuICAgIHsgbGFiZWw6ICdpMThuX3pvb21fcHJlc2V0X2hpZ2hfZHBpXzJrJywgdmFsdWU6IDEuMjUgfSxcbiAgXTtcblxuICAvKiogU8OhdSBjaOG6pW0gY+G7p2EgdGF5IGPhuqdtIGvDqW8g4oCUIGNo4buJIMSR4buDIGBAZm9yYCBk4buxbmcgxJHhu6cgNiB0aOG6uywgZ2nDoSB0cuG7iyBraMO0bmcgZMO5bmcgdOG7m2kuICovXG4gIHByb3RlY3RlZCByZWFkb25seSBkcmFnQmFyRG90cyA9IFswLCAxLCAyLCAzLCA0LCA1XTtcblxuICBwcml2YXRlIHJlYWRvbmx5IHNlcnZpY2UgPSBpbmplY3QoTGlic1VpR3JpZExheW91dFNlcnZpY2UpO1xuXG4gIC8qKiBDaOG6vyDEkeG7mSB0aHUgcGjDs25nIGPDsyDEkWFuZyBob+G6oXQgxJHhu5luZyB0csOqbiBjYW52YXMgbsOgeSBraMO0bmcgKGNo4buJIHJvb3QgY2FudmFzIGRlcHRoIDApICovXG4gIHByb3RlY3RlZCByZWFkb25seSBpc1pvb21BY3RpdmUgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgcmV0dXJuIHRoaXMuZGVwdGgoKSA9PT0gMCAmJiAodGhpcy5lbmFibGVab29tKCkgfHwgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuaXNab29tRW5hYmxlZCgpKTtcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGN1cnJlbnRab29tID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5zZXJ2aWNlLlpvb20oKSk7XG4gIHByb3RlY3RlZCByZWFkb25seSB6b29tUGVyY2VudCA9IGNvbXB1dGVkKCgpID0+IE1hdGgucm91bmQodGhpcy5jdXJyZW50Wm9vbSgpICogMTAwKSk7XG5cbiAgLyoqXG4gICAqIE5vZGUgVEjhuqxUIGPhu6dhIG3hurd0IHBo4bqzbmcgbsOgeS5cbiAgICpcbiAgICog8J+UtCBN4bq3dCBwaOG6s25nIEfhu5BDIChkZXB0aCAwKSDEkeG7jWMgY8OieSB04burIFNFUlZJQ0UsIGtow7RuZyBkw7luZyBpbnB1dC5cbiAgICogTsahaSBkw7luZyB0aMaw4budbmcgc+G7rWEgY8OieSBU4bqgSSBDSOG7liAoYHRyZWVTZXJ2aWNlLmNvbXBhY3QodHJlZSlgKSBy4buTaSBn4buNaSBgcmVmcmVzaCgpYCwgbsOqbiBzaWduYWxcbiAgICogYHJvb3QoKWAgY+G7p2EgaOG7jSBnaeG7ryBuZ3V5w6puIHRoYW0gY2hp4bq/dSB2w6AgaW5wdXQgYG5vZGVgIGtow7RuZyBo4buBIMSR4buVaSDigJQgYEBmb3JgIGtow7RuZyBk4buxbmcgbOG6oWlcbiAgICogYGdyaWRzdGVyLWl0ZW1gLCBsxrDhu5tpIMSR4bupbmcgaW0gZMO5IGThu68gbGnhu4d1IMSRw6Mga2jDoWMgKMSRbyAyOC8wOC8yMDI2OiBi4bqlbSBcIlPhuq9wIGfhu41uXCIga2jDtG5nIHTDoWMgZOG7pW5nKS5cbiAgICogYHJlZnJlc2goKWAgY+G7p2Egc2VydmljZSB0aGF5IHRoYW0gY2hp4bq/dSBjw6J5LCBuw6puIMSR4buNYyB04burIMSRw7MgbeG7m2kgdGjhuqV5IHRoYXkgxJHhu5VpLlxuICAgKlxuICAgKiBN4bq3dCBwaOG6s25nIEzhu5JORyB24bqrbiBkw7luZyBpbnB1dDogbsOzIG5o4bqtbiBub2RlIGNvbiB04burIG3hurd0IHBo4bqzbmcgY2hhLCB24buRbiDEkcOjIGzDoCBvYmplY3QgbeG7m2kuXG4gICAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY3VycmVudE5vZGUgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgdGhpcy5zZXJ2aWNlLlZlcnNpb24oKTtcblxuICAgIGlmICh0aGlzLmRlcHRoKCkgPiAwKSB7XG4gICAgICByZXR1cm4gdGhpcy5ub2RlKCkgPz8gZW1wdHlSb290Tm9kZSgpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnNlcnZpY2UuUm9vdCgpID8/IGVtcHR5Um9vdE5vZGUoKTtcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNoaWxkcmVuID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jdXJyZW50Tm9kZSgpLmNoaWxkcmVuID8/IFtdKTtcblxuICAvKipcbiAgICogQuG7kWkgY+G6o25oIGNobyBgdGVtcGxhdGVUb29sYmFyYCwgZOG7sW5nIHPhurVuIHRoZW8gdOG7q25nIGto4buRaS5cbiAgICpcbiAgICogROG7sW5nIOG7nyBgY29tcHV0ZWRgIGNo4bupIGtow7RuZyBn4buNaSBow6BtIHRyw6puIHRlbXBsYXRlOiBn4buNaSBow6BtIHRyb25nIHRlbXBsYXRlIGNo4bqheSBs4bqhaSBt4buXaSB2w7JuZ1xuICAgKiBkw7IgdGhheSDEkeG7lWkgKHJ1bGUgYGZlLWNvZGluZy1jb252ZW50aW9uLXRlbXBsYXRlc2AgbeG7pWMgNikuXG4gICAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgdG9vbGJhckNvbnRleHRzID0gY29tcHV0ZWQ8QXJyYXk8VF9ncmlkTGF5b3V0X3Rvb2xiYXJDb250ZXh0Pj4oKCkgPT5cbiAgICB0aGlzLmNoaWxkcmVuKCkubWFwKChpdGVtKSA9PiAoe1xuICAgICAgJGltcGxpY2l0OiBpdGVtLFxuICAgICAgaXNDb250YWluZXI6IHRoaXMuaXNDb250YWluZXIoaXRlbSksXG4gICAgICBjYW5BZGRJbnNpZGU6IHRoaXMuY2FuQWRkSW5zaWRlKGl0ZW0pLFxuICAgICAgY2FuVG9nZ2xlVHlwZTogdGhpcy5jYW5Ub2dnbGVUeXBlKGl0ZW0pLFxuICAgICAgcmVtb3ZlOiAoKSA9PiB0aGlzLm91dFJlbW92ZU5vZGUuZW1pdCh7IG5vZGVJZDogaXRlbS5pZCwgbm9kZTogaXRlbSwgY29uZmlybTogKCkgPT4gdGhpcy5zZXJ2aWNlLnJlbW92ZUJsb2NrKGl0ZW0uaWQpIH0pLFxuICAgICAgYWRkSW5zaWRlOiAoKSA9PiB0aGlzLmVtaXRBZGRJbnNpZGUoaXRlbSksXG4gICAgICB0b2dnbGVUeXBlOiAoKSA9PiB0aGlzLmVtaXRUb2dnbGVUeXBlKGl0ZW0pLFxuICAgIH0pKVxuICApO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBpc05lc3RlZENhbnZhcyA9IGNvbXB1dGVkKCgpID0+IHRoaXMuZGVwdGgoKSA+IDApO1xuXG4gIC8qKiB0cuG6p24gbOG7k25nIGNvbnRhaW5lciDigJQga2jhu5FpIMSRw6MgY2jhuqFtIHRy4bqnbiB0aMOsIGtow7RuZyBjaG8gY2h1eeG7g24gdGjDoG5oIGNvbnRhaW5lciAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgbWF4Q29udGFpbmVyRGVwdGggPSBjb21wdXRlZCgoKSA9PiB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5tYXhDb250YWluZXJEZXB0aCgpKTtcblxuICAvKipcbiAgICogTOG7gSB0cm9uZyBj4bunYSBjb250YWluZXIsIHB4LlxuICAgKlxuICAgKiDwn5S0IMSQ4bq3dCB0csOqbiBW4buOIGNvbnRhaW5lciwga2jDtG5nIMSR4bq3dCB2w6BvIGzGsOG7m2kgY29uLiBMxrDhu5tpIGNvbiBnaeG7ryBuZ3V5w6puIGBtYXJnaW46IDJgIG5oxrAgY8WpOyBs4buBXG4gICAqIG7DoHkgY2jhu4kgY28gcnXhu5l0IGzhuqFpIGto4buPaSBiacOqbiB24buPLCBuaOG7nSDEkcOzIHRheSBj4bqnbSDEkeG7lWkga8OtY2ggdGjGsOG7m2MgY+G7p2Ega2jhu5FpIGNvbiBsdcO0biBu4bqxbSB0cm9uZyB04bqnbVxuICAgKiBjaHXhu5l0IChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNikuXG4gICAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY29udGFpbmVyUGFkZGluZyA9IGNvbXB1dGVkKCgpID0+IGAke3RoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmNvbnRhaW5lclBhZGRpbmcoKX1weGApO1xuXG4gIC8qKiBE4bqjaSBrw6lvIGNo4buJIGhp4buHbiDhu58gY2jhur8gxJHhu5kgc+G7rWEg4oCUIGNo4bq/IMSR4buZIHhlbSBraMO0bmcga8OpbyDEkcaw4bujYyBuw6puIGhp4buHbiBk4bqjaSBsw6AgZ8OieSBuaOG6p20uICovXG4gIHByb3RlY3RlZCByZWFkb25seSBpc0VkaXRNb2RlID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5zZXJ2aWNlLklzRWRpdE1vZGUoKSk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGdyaWRPcHRpb25zID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIC8vIMSQ4buNYyBgVmVyc2lvbmAgxJHhu4MgYGluaXQoKWAgbOG6oWkgKMSR4buVaSBjaOG6vyDEkeG7mSwgYuG6rXQvdOG6r3QgY2jhu5NuZyBraOG7kWkpIHNpbmggcmEgb3B0aW9ucyBN4buaSSDigJRcbiAgICAvLyBncmlkc3RlciBjaOG7iSDDoXAgZOG7pW5nIGPhuqV1IGjDrG5oIGtoaSB0aGFtIGNoaeG6v3UgYG9wdGlvbnNgIMSR4buVaS5cbiAgICB0aGlzLnNlcnZpY2UuVmVyc2lvbigpO1xuXG4gICAgcmV0dXJuIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmdyaWRzdGVyT3B0aW9ucyh0aGlzLmRlcHRoKCksICgpID0+IHRoaXMub3V0Q2hhbmdlLmVtaXQoeyByb290OiB0aGlzLmN1cnJlbnROb2RlKCkgfSkpO1xuICB9KTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICAvLyBH4bqvbiBjb21wb25lbnQgY+G7p2EgbsahaSBkw7luZyB2w6BvIHThu6tuZyDDtCwgdsOgIGfhuq9uIEzhuqBJIG3hu5dpIGtoaSBjw6J5IMSR4buVaS5cbiAgICAvLyBEw7luZyBlZmZlY3QgdsOsIGBpdGVtSG9zdHMoKWAgY2jhu4kgY8OzIGdpw6EgdHLhu4sgc2F1IGtoaSBBbmd1bGFyIGThu7FuZyB4b25nIEBmb3IuXG4gICAgZWZmZWN0KCgpID0+IHtcbiAgICAgIGNvbnN0IGhvc3RzID0gdGhpcy5pdGVtSG9zdHMoKTtcbiAgICAgIGNvbnN0IGl0ZW1zID0gdGhpcy5jaGlsZHJlbigpO1xuXG4gICAgICBpdGVtcy5mb3JFYWNoKChpdGVtLCBpKSA9PiB7XG4gICAgICAgIGNvbnN0IGhvc3QgPSBob3N0c1tpXTtcblxuICAgICAgICBpZiAoIWhvc3QpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBLaOG7kWkgY8OzIGNvbiBsw6AgQ09OVEFJTkVSOiBydeG7mXQgbsOzIGRvIG3hurd0IHBo4bqzbmcgbOG7k25nIHbhur0sIGtow7RuZyBn4bqvbiBjb21wb25lbnQgbuG7mWkgZHVuZy5cbiAgICAgICAgaWYgKChpdGVtLmNoaWxkcmVuPy5sZW5ndGggPz8gMCkgPiAwKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuYXR0YWNoTm9kZShpdGVtLCB0aGlzLmRlcHRoKCkgKyAxLCBob3N0KTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgLy8gxJDhu5NuZyBi4buZIG7hur91IG1vZGVsIHpvb20gYsOqbiBuZ2/DoGkgdGhheSDEkeG7lWlcbiAgICBlZmZlY3QoKCkgPT4ge1xuICAgICAgY29uc3QgeiA9IHRoaXMuem9vbSgpO1xuICAgICAgaWYgKHogIT09IHVuZGVmaW5lZCAmJiBNYXRoLmFicyh6IC0gdGhpcy5zZXJ2aWNlLlpvb20oKSkgPiAwLjAwMSkge1xuICAgICAgICB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5zZXRab29tKHosIHRoaXMuc3RvcmFnZUtleSgpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNab29tQWN0aXZlKCkgfHwgIXRoaXMuYXV0b0ZpdE9uTG9hZCgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3Qga2V5ID0gdGhpcy5zdG9yYWdlS2V5KCk7XG4gICAgbGV0IGhhc1NhdmVkID0gZmFsc2U7XG4gICAgaWYgKGtleSAmJiB0eXBlb2YgbG9jYWxTdG9yYWdlICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaGFzU2F2ZWQgPSBCb29sZWFuKGxvY2FsU3RvcmFnZS5nZXRJdGVtKGtleSkpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIGlnbm9yZSBsb2NhbFN0b3JhZ2UgYWNjZXNzIGVycm9yXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFoYXNTYXZlZCkge1xuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGNvbnN0IHNjcm9sbEVsID0gdGhpcy5zY3JvbGxBcmVhUmVmKCk/Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgICAgIGlmIChzY3JvbGxFbCAmJiBzY3JvbGxFbC5jbGllbnRXaWR0aCA+IDAgJiYgc2Nyb2xsRWwuY2xpZW50V2lkdGggPCB0aGlzLnN0YWdlV2lkdGgoKSArIDY0KSB7XG4gICAgICAgICAgdGhpcy5oYW5kbGVyRml0U2NyZWVuKCk7XG4gICAgICAgIH1cbiAgICAgIH0sIDUwKTtcbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLmNsZWFudXBEcmFnR3JpZCgpO1xuICAgIGZvciAoY29uc3QgaXRlbSBvZiB0aGlzLmNoaWxkcmVuKCkpIHtcbiAgICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmRldGFjaE5vZGUoaXRlbS5pZCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFbhur0gbOG6oWkgbMaw4bubaSDigJQgZ+G7jWkga2hpIGtodW5nIG5nb8OgaSDEkeG7lWkga8OtY2ggdGjGsOG7m2MgaG/hurdjIGPDonkgduG7q2EgxJHhu5VpIGRp4buHbiBy4buZbmcuICovXG4gIGdldCBGdW5jdGlvbkNvbnRyb2woKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHZlTGFpTHVvaTogKCkgPT4gdGhpcy5ncmlkc3RlclJlZigpPy5vcHRpb25zQ2hhbmdlZCgpLFxuICAgICAgem9vbUluOiAoKSA9PiB0aGlzLmhhbmRsZXJab29tSW4oKSxcbiAgICAgIHpvb21PdXQ6ICgpID0+IHRoaXMuaGFuZGxlclpvb21PdXQoKSxcbiAgICAgIHJlc2V0Wm9vbTogKCkgPT4gdGhpcy5oYW5kbGVyUmVzZXRab29tKCksXG4gICAgICBmaXRUb1NjcmVlbjogKCkgPT4gdGhpcy5oYW5kbGVyRml0U2NyZWVuKCksXG4gICAgICBzZXRab29tOiAodmFsOiBudW1iZXIpID0+IHtcbiAgICAgICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuc2V0Wm9vbSh2YWwsIHRoaXMuc3RvcmFnZUtleSgpKTtcbiAgICAgICAgdGhpcy5zeW5jWm9vbU91dCgpO1xuICAgICAgfSxcbiAgICAgIC8vIFbDuW5nIGN14buZbiBj4bunYSBt4bq3dCBwaOG6s25nIOKAlCBuxqFpIGTDuW5nIGPhuqduIG7DsyDEkeG7gyB04buxIGN14buZbiB04bubaSBt4buZdCBraOG7kWksIGhv4bq3YyDEkW8gdG/huqEgxJHhu5kga2hpIGvDqW8uXG4gICAgICAvLyBLaMO0bmcgbeG7nyDhu58gxJHDonkgdGjDrCBuxqFpIGTDuW5nIHBo4bqjaSBkw7IgYuG6sW5nIHTDqm4gbOG7m3AgQ1NTIG7hu5lpIGLhu5ksIHbDoCB0w6puIMSRw7MgxJHhu5VpIGzDumMgbsOgbyBraMO0bmcgaGF5LlxuICAgICAgc2Nyb2xsQXJlYTogKCk6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkID0+IHRoaXMuc2Nyb2xsQXJlYVJlZigpPy5uYXRpdmVFbGVtZW50LFxuICAgIH07XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlclpvb21JbigpOiB2b2lkIHtcbiAgICB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC56b29tSW4odGhpcy56b29tU3RlcCgpLCB0aGlzLnN0b3JhZ2VLZXkoKSk7XG4gICAgdGhpcy5zeW5jWm9vbU91dCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJab29tT3V0KCk6IHZvaWQge1xuICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLnpvb21PdXQodGhpcy56b29tU3RlcCgpLCB0aGlzLnN0b3JhZ2VLZXkoKSk7XG4gICAgdGhpcy5zeW5jWm9vbU91dCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJSZXNldFpvb20oKTogdm9pZCB7XG4gICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wucmVzZXRab29tKHRoaXMuc3RvcmFnZUtleSgpKTtcbiAgICB0aGlzLnN5bmNab29tT3V0KCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckZpdFNjcmVlbigpOiB2b2lkIHtcbiAgICB0aGlzLmlzTWVudU9wZW4uc2V0KGZhbHNlKTtcbiAgICBjb25zdCBzY3JvbGxFbCA9IHRoaXMuc2Nyb2xsQXJlYVJlZigpPy5uYXRpdmVFbGVtZW50O1xuICAgIGNvbnN0IGNvbnRhaW5lcldpZHRoID0gc2Nyb2xsRWw/LmNsaWVudFdpZHRoIHx8ICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdy5pbm5lcldpZHRoIC0gMzAwIDogMTIwMCk7XG4gICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuZml0VG9TY3JlZW4oY29udGFpbmVyV2lkdGgsIHRoaXMuc3RhZ2VXaWR0aCgpLCA0OCwgdGhpcy5zdG9yYWdlS2V5KCkpO1xuICAgIHRoaXMuc3luY1pvb21PdXQoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyVG9nZ2xlTWVudSgpOiB2b2lkIHtcbiAgICB0aGlzLmlzTWVudU9wZW4udXBkYXRlKChvcGVuKSA9PiAhb3Blbik7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlclNlbGVjdFByZXNldCh2YWx1ZTogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5pc01lbnVPcGVuLnNldChmYWxzZSk7XG4gICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuc2V0Wm9vbSh2YWx1ZSwgdGhpcy5zdG9yYWdlS2V5KCkpO1xuICAgIHRoaXMuc3luY1pvb21PdXQoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBpc0N1cnJlbnRQcmVzZXQodmFsdWU6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgIHJldHVybiBNYXRoLmFicyh0aGlzLmN1cnJlbnRab29tKCkgLSB2YWx1ZSkgPCAwLjAxO1xuICB9XG5cbiAgcHJpdmF0ZSBzeW5jWm9vbU91dCgpOiB2b2lkIHtcbiAgICBjb25zdCB2YWwgPSB0aGlzLnNlcnZpY2UuWm9vbSgpO1xuICAgIHRoaXMub3V0Wm9vbUNoYW5nZS5lbWl0KHZhbCk7XG4gICAgaWYgKHRoaXMuem9vbSgpICE9PSB2YWwpIHtcbiAgICAgIHRoaXMuem9vbS5zZXQodmFsKTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlcldoZWVsKGV2ZW50OiBXaGVlbEV2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmlzWm9vbUFjdGl2ZSgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSkge1xuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgIGlmIChldmVudC5kZWx0YVkgPCAwKSB7XG4gICAgICAgIHRoaXMuaGFuZGxlclpvb21JbigpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5kZWx0YVkgPiAwKSB7XG4gICAgICAgIHRoaXMuaGFuZGxlclpvb21PdXQoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkb2N1bWVudDprZXlkb3duJywgWyckZXZlbnQnXSlcbiAgcHJvdGVjdGVkIGhhbmRsZXJEb2N1bWVudEtleURvd24oZXZlbnQ6IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNab29tQWN0aXZlKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoZXZlbnQuY3RybEtleSB8fCBldmVudC5tZXRhS2V5KSB7XG4gICAgICBpZiAoZXZlbnQua2V5ID09PSAnPScgfHwgZXZlbnQua2V5ID09PSAnKycpIHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgdGhpcy5oYW5kbGVyWm9vbUluKCk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJy0nIHx8IGV2ZW50LmtleSA9PT0gJ18nKSB7XG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgIHRoaXMuaGFuZGxlclpvb21PdXQoKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnMCcpIHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgdGhpcy5oYW5kbGVyUmVzZXRab29tKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignZG9jdW1lbnQ6Y2xpY2snLCBbJyRldmVudCddKVxuICBwcm90ZWN0ZWQgaGFuZGxlckRvY3VtZW50Q2xpY2soZXZlbnQ6IE1vdXNlRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNNZW51T3BlbigpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgZG9ja0VsID0gdGhpcy56b29tRG9ja1JlZigpPy5uYXRpdmVFbGVtZW50O1xuICAgIGlmIChkb2NrRWwgJiYgIWRvY2tFbC5jb250YWlucyhldmVudC50YXJnZXQgYXMgTm9kZSkpIHtcbiAgICAgIHRoaXMuaXNNZW51T3Blbi5zZXQoZmFsc2UpO1xuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyVHJhY2tOb2RlKF9pbmRleDogbnVtYmVyLCBpdGVtOiBUX2dyaWRMYXlvdXRfbm9kZSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGl0ZW0uaWQ7XG4gIH1cblxuICAvKipcbiAgICog8J+UtCBDaOG6t24gYG1vdXNlZG93bmAgbuG7lWkgbMOqbiBsxrDhu5tpIENIQSBraGkgdGhhbyB0w6FjIHRyb25nIGzGsOG7m2kgY29uLlxuICAgKiBLaMO0bmcgY2jhurduIHRow6wgY+G6oyBoYWkgbMaw4bubaSBjw7luZyBuaOG6rW4gbeG7mXQgY8O6IGLhuqVtOiBrw6lvIGto4buRaSBjb24gbMOgIG5o4bqlYyBsdcO0biBjb250YWluZXIuXG4gICAqL1xuICAvKipcbiAgICogQ2jhurduIGBtb3VzZWRvd25gIGPhu6dhIGto4buRaSB0cm9uZyBsxrDhu5tpIEzhu5JORyBu4buVaSBsw6puIGzGsOG7m2kgY2hhLlxuICAgKlxuICAgKiBDaOG7iSBjaOG6t24g4bufIGzGsOG7m2kgbOG7k25nOiBsxrDhu5tpIGPhuqVwIHRyYW5nIGtow7RuZyBjw7MgY2hhIG7DoG8gxJHhu4MgY2jhurduLCB2w6AgY2jhurduIOG7nyDEkcOzIHPhur0gbnXhu5F0IGx1w7RuIHRoYW9cbiAgICogdMOhYyBj4bunYSBjaMOtbmggbsOzLlxuICAgKi9cbiAgLyoqXG4gICAqIPCflLQgQmEgaGFuZGxlciBkxrDhu5tpIMSRw6J5IFBI4bqiSSBgc3RvcFByb3BhZ2F0aW9uYC5cbiAgICogTsO6dCBu4bqxbSB0cm9uZyB2w7luZyBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgLCBraMO0bmcgY2jhurduIHRow6wgY8O6IGLhuqVtIGLhu4sgZ3JpZHN0ZXIgaGnhu4N1IGzDoCBi4bqvdCDEkeG6p3Uga8Opb1xuICAgKiB2w6AgYGRlbGF5U3RhcnQ6IDE2MGAgbnXhu5F0IGx1w7RuIGBjbGlja2Ag4oCUIG5nxrDhu51pIGTDuW5nIHRo4bqleSBuaMawIG7DunQgY2jhur90LlxuICAgKi9cbiAgcHJvdGVjdGVkIGhhbmRsZXJSZW1vdmUoZXZlbnQ6IEV2ZW50LCBub2RlOiBUX2dyaWRMYXlvdXRfbm9kZSk6IHZvaWQge1xuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgdGhpcy5vdXRSZW1vdmVOb2RlLmVtaXQoeyBub2RlSWQ6IG5vZGUuaWQsIG5vZGUsIGNvbmZpcm06ICgpID0+IHRoaXMuc2VydmljZS5yZW1vdmVCbG9jayhub2RlLmlkKSB9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyQWRkSW5zaWRlKGV2ZW50OiBFdmVudCwgbm9kZTogVF9ncmlkTGF5b3V0X25vZGUpOiB2b2lkIHtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIHRoaXMuZW1pdEFkZEluc2lkZShub2RlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyVG9nZ2xlVHlwZShldmVudDogRXZlbnQsIG5vZGU6IFRfZ3JpZExheW91dF9ub2RlKTogdm9pZCB7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICB0aGlzLmVtaXRUb2dnbGVUeXBlKG5vZGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIENo4bq3biBjw7ogYuG6pW0gdHJvbmcgdGhhbmggY8O0bmcgY+G7pSBu4buVaSBsw6puIGdyaWRzdGVyLlxuICAgKlxuICAgKiBDaOG7iSBgc3RvcFByb3BhZ2F0aW9uYCwgS0jDlE5HIGBwcmV2ZW50RGVmYXVsdGA6IG7DunQgYsOqbiB0cm9uZyB0ZW1wbGF0ZSBj4bunYSBuxqFpIGTDuW5nIHbhuqtuIGPhuqduXG4gICAqIGjDoG5oIHZpIG3hurdjIMSR4buLbmggY+G7p2EgbcOsbmggKG3hu58gZHJvcGRvd24sIGZvY3Vz4oCmKS5cbiAgICovXG4gIHByb3RlY3RlZCBoYW5kbGVyU3RvcEV2ZW50KGV2ZW50OiBFdmVudCk6IHZvaWQge1xuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICB9XG5cbiAgLyoqIFBow6F0IGBvdXRBZGRCbG9ja0luc2lkZWAg4oCUIGTDuW5nIGNodW5nIGNobyBuw7p0IG3hurdjIMSR4buLbmggdsOgIGB0ZW1wbGF0ZVRvb2xiYXJgLiAqL1xuICBwcml2YXRlIGVtaXRBZGRJbnNpZGUobm9kZTogVF9ncmlkTGF5b3V0X25vZGUpOiB2b2lkIHtcbiAgICB0aGlzLm91dEFkZEJsb2NrSW5zaWRlLmVtaXQoe1xuICAgICAgbm9kZUlkOiBub2RlLmlkLFxuICAgICAgbm9kZSxcbiAgICAgIGNvbmZpcm06IChpdGVtOiBUX2dyaWRMYXlvdXRfbmV3Tm9kZSkgPT4gdGhpcy5zZXJ2aWNlLmFkZEJsb2NrKGl0ZW0sIG5vZGUuaWQpLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIFBow6F0IGBvdXRUb2dnbGVUeXBlYCDigJQgZMO5bmcgY2h1bmcgY2hvIG7DunQgbeG6t2MgxJHhu4tuaCB2w6AgYHRlbXBsYXRlVG9vbGJhcmAuICovXG4gIHByaXZhdGUgZW1pdFRvZ2dsZVR5cGUobm9kZTogVF9ncmlkTGF5b3V0X25vZGUpOiB2b2lkIHtcbiAgICB0aGlzLm91dFRvZ2dsZVR5cGUuZW1pdCh7XG4gICAgICBub2RlSWQ6IG5vZGUuaWQsXG4gICAgICBub2RlLFxuICAgICAgLy8gR+G7mXAgaGF5IHTDoWNoIHN1eSByYSB04burIGNow61uaCBraOG7kWk6IMSRYW5nIGzDoCBjb250YWluZXIgdGjDrCB0w6FjaCwga2jDtG5nIHRow6wgZ+G7mXAuXG4gICAgICBjb25maXJtOiAoKSA9PiAodGhpcy5pc0NvbnRhaW5lcihub2RlKSA/IHRoaXMuc2VydmljZS50b1NpbmdsZShub2RlLmlkKSA6IHRoaXMuc2VydmljZS50b0NvbnRhaW5lcihub2RlLmlkKSksXG4gICAgfSk7XG4gIH1cblxuICAvKioga2jhu5FpIG7DoHkgY8OzIHBo4bqjaSBjb250YWluZXIga2jDtG5nIChjw7Mga2jhu5FpIGNvbikgKi9cbiAgcHJvdGVjdGVkIGlzQ29udGFpbmVyKGl0ZW06IFRfZ3JpZExheW91dF9ub2RlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChpdGVtLmNoaWxkcmVuPy5sZW5ndGggPz8gMCkgPiAwO1xuICB9XG5cbiAgLyoqIGNvbnRhaW5lciDEkcOjIGNo4bqhbSB0cuG6p24gbOG7k25nIHRow6wga2jDtG5nIGNobyB0aMOqbSBraOG7kWkgdsOgbyB0cm9uZyBu4buvYSAqL1xuICBwcm90ZWN0ZWQgY2FuQWRkSW5zaWRlKGl0ZW06IFRfZ3JpZExheW91dF9ub2RlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuaXNDb250YWluZXIoaXRlbSkgJiYgdGhpcy5kZXB0aCgpIDwgdGhpcy5tYXhDb250YWluZXJEZXB0aCgpO1xuICB9XG5cbiAgLyoqIGto4buRaSDEkcahbiDhu58gY+G6pXAgY2jGsGEgY2jhuqFtIHRy4bqnbiBt4bubaSDEkcaw4bujYyBjaHV54buDbiB0aMOgbmggY29udGFpbmVyICovXG4gIHByb3RlY3RlZCBjYW5Ub2dnbGVUeXBlKGl0ZW06IFRfZ3JpZExheW91dF9ub2RlKTogYm9vbGVhbiB7XG4gICAgaWYgKHRoaXMuaXNDb250YWluZXIoaXRlbSkpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLmRlcHRoKCkgPCB0aGlzLm1heENvbnRhaW5lckRlcHRoKCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckJsb2NrUGFyZW50RHJhZyhldmVudDogRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNOZXN0ZWRDYW52YXMoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJNb3VzZURvd25Ucm9uZ0x1b2koZXZlbnQ6IEV2ZW50KTogdm9pZCB7XG4gICAgaWYgKHRoaXMuZGVwdGgoKSA9PT0gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJTZWxlY3ROb2RlKG5vZGU6IFRfZ3JpZExheW91dF9ub2RlLCBldmVudD86IE1vdXNlRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoZXZlbnQpIHtcbiAgICAgIGNvbnN0IHRhcmdldCA9IGV2ZW50LnRhcmdldCBhcyBIVE1MRWxlbWVudDtcbiAgICAgIGlmICh0YXJnZXQuY2xvc2VzdCgnLmxpYnMtdWktZ3JpZC1sYXlvdXQtdG9vbGJhcicpIHx8IHRhcmdldC5jbG9zZXN0KCdidXR0b24nKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuc2VsZWN0ZWROb2RlSWQuc2V0KG5vZGUuaWQpO1xuICAgIHRoaXMub3V0U2VsZWN0Tm9kZS5lbWl0KG5vZGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJDb25maWcoZXZlbnQ6IEV2ZW50LCBub2RlOiBUX2dyaWRMYXlvdXRfbm9kZSk6IHZvaWQge1xuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIGNvbnN0IGFjdGlvbkV2ZW50OiBUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQgPSB7XG4gICAgICBub2RlSWQ6IG5vZGUuaWQsXG4gICAgICBub2RlLFxuICAgICAgY29uZmlybTogKCkgPT4gdHJ1ZSxcbiAgICB9O1xuICAgIHRoaXMub3V0Q29uZmlnTm9kZS5lbWl0KGFjdGlvbkV2ZW50KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyRHJhZ092ZXIoZXZlbnQ6IERyYWdFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5lbmFibGVFeHRlcm5hbERyb3AoKSB8fCB0aGlzLmRlcHRoKCkgPiAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgaWYgKGV2ZW50LmRhdGFUcmFuc2Zlcikge1xuICAgICAgZXZlbnQuZGF0YVRyYW5zZmVyLmRyb3BFZmZlY3QgPSAnY29weSc7XG4gICAgfVxuXG4gICAgY29uc3QgY29udGFpbmVyID0gdGhpcy5zY3JvbGxBcmVhUmVmKCk/Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgY29uc3Qgcm9vdCA9IHRoaXMuc2VydmljZS5Sb290KCk7XG4gICAgY29uc3QgcGF5bG9hZCA9IHBhcnNlRHJvcFBheWxvYWQ8VF9ncmlkTGF5b3V0X2V4dGVybmFsRHJhZ1BheWxvYWQ+KFxuICAgICAgdGhpcy5zZXJ2aWNlLmdldERyYWdQYXlsb2FkKCkgYXMgVF9ncmlkTGF5b3V0X2V4dGVybmFsRHJhZ1BheWxvYWQgfCB1bmRlZmluZWQsXG4gICAgICBldmVudC5kYXRhVHJhbnNmZXIsXG4gICAgKTtcblxuICAgIGlmICghY29udGFpbmVyIHx8ICFyb290IHx8ICFwYXlsb2FkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3Qgcm9vdEdyaWRzdGVyID0gY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJ2dyaWRzdGVyJykgYXMgSFRNTEVsZW1lbnQgfCBudWxsO1xuICAgIGlmIChyb290R3JpZHN0ZXIpIHtcbiAgICAgIGlmICghcm9vdEdyaWRzdGVyLmNsYXNzTGlzdC5jb250YWlucygnZGlzcGxheS1ncmlkJykpIHtcbiAgICAgICAgcm9vdEdyaWRzdGVyLmNsYXNzTGlzdC5hZGQoJ2Rpc3BsYXktZ3JpZCcpO1xuICAgICAgfVxuICAgICAgdGhpcy5yb290R3JpZHN0ZXJFbCA9IHJvb3RHcmlkc3RlcjtcbiAgICB9XG5cbiAgICBjb25zdCBnaG9zdCA9IGNhbGN1bGF0ZURyb3BHaG9zdChldmVudCwgY29udGFpbmVyLCByb290LCBwYXlsb2FkLCB0aGlzLnNlcnZpY2UudG90YWxDb2x1bW5zKTtcblxuICAgIGlmIChnaG9zdD8uaXNJbnNpZGVHcm91cCAmJiBnaG9zdC5ob3ZlcmVkR3JpZHN0ZXIpIHtcbiAgICAgIGlmICh0aGlzLmFjdGl2ZUhvdmVyZWRFbCAhPT0gZ2hvc3QuaG92ZXJlZEdyaWRzdGVyKSB7XG4gICAgICAgIHRoaXMuY2xlYW51cEhvdmVyZWRHcm91cCgpO1xuICAgICAgICBnaG9zdC5ob3ZlcmVkR3JpZHN0ZXIuY2xhc3NMaXN0LmFkZCgnZGlzcGxheS1ncmlkJyk7XG4gICAgICAgIHRoaXMuYWN0aXZlSG92ZXJlZEVsID0gZ2hvc3QuaG92ZXJlZEdyaWRzdGVyO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmNsZWFudXBIb3ZlcmVkR3JvdXAoKTtcbiAgICB9XG5cbiAgICB0aGlzLmRyb3BHaG9zdC5zZXQoZ2hvc3QpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJEcmFnTGVhdmUoZXZlbnQ6IERyYWdFdmVudCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmRlcHRoKCkgPiAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChldmVudC5yZWxhdGVkVGFyZ2V0KSB7XG4gICAgICBjb25zdCBjb250YWluZXIgPSB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudDtcbiAgICAgIGlmIChjb250YWluZXIgJiYgY29udGFpbmVyLmNvbnRhaW5zKGV2ZW50LnJlbGF0ZWRUYXJnZXQgYXMgTm9kZSkpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLmRyb3BHaG9zdC5zZXQodW5kZWZpbmVkKTtcbiAgICB0aGlzLmNsZWFudXBEcmFnR3JpZCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJEcm9wKGV2ZW50OiBEcmFnRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuZW5hYmxlRXh0ZXJuYWxEcm9wKCkgfHwgdGhpcy5kZXB0aCgpID4gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIGNvbnN0IGdob3N0ID0gdGhpcy5kcm9wR2hvc3QoKTtcbiAgICBjb25zdCBwYXlsb2FkID0gcGFyc2VEcm9wUGF5bG9hZDxUX2dyaWRMYXlvdXRfZXh0ZXJuYWxEcmFnUGF5bG9hZD4oXG4gICAgICB0aGlzLnNlcnZpY2UuZ2V0RHJhZ1BheWxvYWQoKSBhcyBUX2dyaWRMYXlvdXRfZXh0ZXJuYWxEcmFnUGF5bG9hZCB8IHVuZGVmaW5lZCxcbiAgICAgIGV2ZW50LmRhdGFUcmFuc2ZlcixcbiAgICApO1xuXG4gICAgdGhpcy5kcm9wR2hvc3Quc2V0KHVuZGVmaW5lZCk7XG4gICAgdGhpcy5jbGVhbnVwRHJhZ0dyaWQoKTtcbiAgICB0aGlzLnNlcnZpY2UuY2xlYXJEcmFnUGF5bG9hZCgpO1xuXG4gICAgaWYgKCFnaG9zdCB8fCAhcGF5bG9hZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHRhcmdldFBhcmVudElkID0gZ2hvc3QuaXNJbnNpZGVHcm91cCA/IGdob3N0LnRhcmdldFBhcmVudElkIDogdW5kZWZpbmVkO1xuICAgIGNvbnN0IGlzSW5zaWRlID0gQm9vbGVhbih0YXJnZXRQYXJlbnRJZCk7XG5cbiAgICBsZXQgaXRlbVRvQWRkOiBUX2dyaWRMYXlvdXRfbmV3Tm9kZTtcbiAgICBjb25zdCBwYXlsb2FkQW55ID0gcGF5bG9hZCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICBpZiAodHlwZW9mIHBheWxvYWRBbnlbJ3Jlc29sdmVOb2RlJ10gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIGNvbnN0IHJlc29sdmVkID0gKHBheWxvYWRBbnlbJ3Jlc29sdmVOb2RlJ10gYXMgKGluc2lkZTogYm9vbGVhbikgPT4gVF9ncmlkTGF5b3V0X25ld05vZGUgfCB1bmRlZmluZWQpKGlzSW5zaWRlKTtcbiAgICAgIGl0ZW1Ub0FkZCA9IHJlc29sdmVkID8/IHtcbiAgICAgICAgY29sczogZ2hvc3QuY29scyxcbiAgICAgICAgcm93czogZ2hvc3Qucm93cyxcbiAgICAgICAgZGF0YTogcGF5bG9hZC5kYXRhID8/IChwYXlsb2FkLnR5cGUgPyB7IHR5cGU6IHBheWxvYWQudHlwZSwgdGl0bGU6IHBheWxvYWQudGl0bGUgfSA6IHVuZGVmaW5lZCksXG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBuZXdOb2RlRGF0YSA9IHBheWxvYWQuZGF0YSA/PyAocGF5bG9hZC50eXBlID8geyB0eXBlOiBwYXlsb2FkLnR5cGUsIHRpdGxlOiBwYXlsb2FkLnRpdGxlIH0gOiB1bmRlZmluZWQpO1xuICAgICAgaXRlbVRvQWRkID0ge1xuICAgICAgICBjb2xzOiBnaG9zdC5jb2xzLFxuICAgICAgICByb3dzOiBnaG9zdC5yb3dzLFxuICAgICAgICBkYXRhOiBuZXdOb2RlRGF0YSxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgY29uc3QgYWRkZWROb2RlID0gdGhpcy5zZXJ2aWNlLmluc2VydEJsb2NrV2l0aENvbGxpc2lvbihcbiAgICAgIGl0ZW1Ub0FkZCxcbiAgICAgIGdob3N0LmNvbCxcbiAgICAgIGdob3N0LnJvdyxcbiAgICAgIHRhcmdldFBhcmVudElkLFxuICAgICk7XG5cbiAgICBpZiAoYWRkZWROb2RlKSB7XG4gICAgICB0aGlzLnNlbGVjdGVkTm9kZUlkLnNldChhZGRlZE5vZGUuaWQpO1xuICAgICAgdGhpcy5vdXREcm9wTm9kZS5lbWl0KHtcbiAgICAgICAgbm9kZTogYWRkZWROb2RlLFxuICAgICAgICBwYXJlbnRJZDogdGFyZ2V0UGFyZW50SWQsXG4gICAgICAgIGNvbDogZ2hvc3QuY29sLFxuICAgICAgICByb3c6IGdob3N0LnJvdyxcbiAgICAgICAgY29sczogZ2hvc3QuY29scyxcbiAgICAgICAgcm93czogZ2hvc3Qucm93cyxcbiAgICAgICAgcmF3RXZlbnQ6IGV2ZW50LFxuICAgICAgICBwYXlsb2FkLFxuICAgICAgfSk7XG4gICAgICBjb25zdCByb290ID0gdGhpcy5zZXJ2aWNlLlJvb3QoKTtcbiAgICAgIGlmIChyb290KSB7XG4gICAgICAgIHRoaXMub3V0Q2hhbmdlLmVtaXQoe1xuICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgbm9kZUlkOiBhZGRlZE5vZGUuaWQsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY2xlYW51cEhvdmVyZWRHcm91cCgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5hY3RpdmVIb3ZlcmVkRWwpIHtcbiAgICAgIHRoaXMuYWN0aXZlSG92ZXJlZEVsLmNsYXNzTGlzdC5yZW1vdmUoJ2Rpc3BsYXktZ3JpZCcpO1xuICAgICAgdGhpcy5hY3RpdmVIb3ZlcmVkRWwgPSB1bmRlZmluZWQ7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjbGVhbnVwRHJhZ0dyaWQoKTogdm9pZCB7XG4gICAgdGhpcy5jbGVhbnVwSG92ZXJlZEdyb3VwKCk7XG4gICAgaWYgKHRoaXMucm9vdEdyaWRzdGVyRWwgJiYgIXRoaXMuc2hvd0dyaWRMaW5lcygpKSB7XG4gICAgICB0aGlzLnJvb3RHcmlkc3RlckVsLmNsYXNzTGlzdC5yZW1vdmUoJ2Rpc3BsYXktZ3JpZCcpO1xuICAgICAgdGhpcy5yb290R3JpZHN0ZXJFbCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgY29uc3QgY29udGFpbmVyID0gdGhpcy5zY3JvbGxBcmVhUmVmKCk/Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgaWYgKGNvbnRhaW5lciAmJiAhdGhpcy5zaG93R3JpZExpbmVzKCkpIHtcbiAgICAgIGNvbnRhaW5lci5xdWVyeVNlbGVjdG9yQWxsKCdncmlkc3Rlci5kaXNwbGF5LWdyaWQnKS5mb3JFYWNoKChlbCkgPT4ge1xuICAgICAgICBlbC5jbGFzc0xpc3QucmVtb3ZlKCdkaXNwbGF5LWdyaWQnKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxufVxuIiwiQGlmIChpc1pvb21BY3RpdmUoKSkge1xuICA8IS0tIEtodSB24buxYyBjdeG7mW4gY2jhu6lhIGNhbnZhcyBzdGFnZSB0aHUgcGjDs25nIC0tPlxuICA8ZGl2XG4gICAgI3Njcm9sbEFyZWFcbiAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtc2Nyb2xsLWFyZWEgcmVsYXRpdmUgaC1mdWxsIG1pbi1oLTAgdy1mdWxsIG1pbi13LTAgb3ZlcmZsb3ctYXV0byBwLTYgZmxleCBqdXN0aWZ5LWNlbnRlciBpdGVtcy1zdGFydFwiXG4gICAgKG1vdXNlZG93bik9XCJoYW5kbGVyTW91c2VEb3duVHJvbmdMdW9pKCRldmVudClcIlxuICAgICh3aGVlbCk9XCJoYW5kbGVyV2hlZWwoJGV2ZW50KVwiXG4gICAgKGRyYWdvdmVyKT1cImhhbmRsZXJEcmFnT3ZlcigkZXZlbnQpXCJcbiAgICAoZHJhZ2xlYXZlKT1cImhhbmRsZXJEcmFnTGVhdmUoJGV2ZW50KVwiXG4gICAgKGRyb3ApPVwiaGFuZGxlckRyb3AoJGV2ZW50KVwiPlxuICAgIDxkaXZcbiAgICAgICNjYW52YXNTdGFnZVxuICAgICAgaWQ9XCJiaS1jYW52YXMtc3RhZ2VcIlxuICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LXN0YWdlIHJlbGF0aXZlIGZsZXggZmxleC1jb2wgdHJhbnNpdGlvbi1bem9vbV0gZHVyYXRpb24tMTUwIG9yaWdpbi10b3Agc2hyaW5rLTAgYmctd2hpdGUvODAgYm9yZGVyIGJvcmRlci1zbGF0ZS0yMDAvODAgcm91bmRlZC0yeGwgc2hhZG93LXNtIHAtNFwiXG4gICAgICBbc3R5bGUuem9vbV09XCJjdXJyZW50Wm9vbSgpXCJcbiAgICAgIFtzdHlsZS53aWR0aC5weF09XCJzdGFnZVdpZHRoKClcIlxuICAgICAgW3N0eWxlLm1pbi13aWR0aC5weF09XCJzdGFnZVdpZHRoKClcIlxuICAgICAgW3N0eWxlLm1pbi1oZWlnaHQucHhdPVwic3RhZ2VNaW5IZWlnaHQoKVwiPlxuICAgICAgPCEtLSBTbmFwcGVkIERyb3AgR2hvc3QgUHJldmlldyAtLT5cbiAgICAgIEBpZiAoZHJvcEdob3N0KCk7IGFzIGdob3N0KSB7XG4gICAgICAgIDxkaXZcbiAgICAgICAgICBjbGFzcz1cInBvaW50ZXItZXZlbnRzLW5vbmUgYWJzb2x1dGUgei0zMCBmbGV4IGZsZXgtY29sIGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciBvdmVyZmxvdy1oaWRkZW4gcm91bmRlZC1sZyBib3JkZXItMiBib3JkZXItZGFzaGVkIGJvcmRlci1ibHVlLTUwMCBiZy1ibHVlLTUwMC8xNSBwLTEgc2hhZG93LXNtIHRyYW5zaXRpb24tYWxsIGR1cmF0aW9uLTc1IGVhc2Utb3V0IGJhY2tkcm9wLWJsdXItWzAuNXB4XSBzZWxlY3Qtbm9uZVwiXG4gICAgICAgICAgW3N0eWxlLmxlZnQucHhdPVwiZ2hvc3QucGl4ZWxMZWZ0XCJcbiAgICAgICAgICBbc3R5bGUudG9wLnB4XT1cImdob3N0LnBpeGVsVG9wXCJcbiAgICAgICAgICBbc3R5bGUud2lkdGgucHhdPVwiZ2hvc3QucGl4ZWxXaWR0aFwiXG4gICAgICAgICAgW3N0eWxlLmhlaWdodC5weF09XCJnaG9zdC5waXhlbEhlaWdodFwiPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJpbmxpbmUtZmxleCBtYXgtdy1bOTUlXSBpdGVtcy1jZW50ZXIgZ2FwLTEgdHJ1bmNhdGUgcm91bmRlZC1tZCBiZy1ibHVlLTYwMCBweC0yIHB5LTAuNSB0ZXh0LVsxMXB4XSBmb250LXNlbWlib2xkIHRleHQtd2hpdGUgc2hhZG93LXhzXCI+XG4gICAgICAgICAgICBAaWYgKGdob3N0LmNhcmRJY29uQ2xhc3MpIHtcbiAgICAgICAgICAgICAgPGkgW2NsYXNzXT1cImdob3N0LmNhcmRJY29uQ2xhc3MgKyAnIHRleHQteHMnXCI+PC9pPlxuICAgICAgICAgICAgfSBAZWxzZSBpZiAoZ2hvc3QuY2FyZEljb25UZXh0KSB7XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1bMTBweF0gZm9udC1ib2xkXCI+e3sgZ2hvc3QuY2FyZEljb25UZXh0IH19PC9zcGFuPlxuICAgICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC14cyBmb250LWJvbGRcIj57eyBnaG9zdC5pc0luc2lkZUdyb3VwID8gJ+KGsycgOiAnKycgfX08L3NwYW4+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRydW5jYXRlXCI+e3sgZ2hvc3QuY2FyZFRpdGxlIH19PC9zcGFuPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LVs5cHhdIGZvbnQtbW9ubyBvcGFjaXR5LTc1XCI+KHt7IGdob3N0LmNvbHMgfX1jKTwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgIEBpZiAoZ2hvc3QucGl4ZWxIZWlnaHQgPj0gODApIHtcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJtdC0xIGZsZXggbWF4LXctWzk1JV0gaXRlbXMtY2VudGVyIGdhcC0xLjUgdHJ1bmNhdGUgcm91bmRlZCBib3JkZXIgYm9yZGVyLWJsdWUtMTAwIGJnLXdoaXRlLzk1IHB4LTIgcHktMC41IHRleHQtWzEwcHhdIGZvbnQtbWVkaXVtIHRleHQtYmx1ZS03MDAgc2hhZG93LXhzXCI+XG4gICAgICAgICAgICAgIEBpZiAoZ2hvc3QuaXNJbnNpZGVHcm91cCkge1xuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidHJ1bmNhdGUgZm9udC1ib2xkIHRleHQtYmx1ZS05MDBcIj7ihrMge3sgZ2hvc3QuZ3JvdXBUaXRsZSB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtYmx1ZS0zMDBcIj7igKI8L3NwYW4+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgPHNwYW4+Q+G7mXQge3sgZ2hvc3QuY29sICsgMSB9fS17eyBnaG9zdC5jb2wgKyBnaG9zdC5jb2xzIH19PC9zcGFuPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgfVxuICAgICAgICA8L2Rpdj5cbiAgICAgIH1cbiAgICAgIDxncmlkc3RlciBjbGFzcz1cImgtZnVsbCB3LWZ1bGxcIiBbY2xhc3MuZGlzcGxheS1ncmlkXT1cInNob3dHcmlkTGluZXMoKVwiIFtvcHRpb25zXT1cImdyaWRPcHRpb25zKClcIj5cbiAgICAgICAgICBAZm9yIChpdGVtIG9mIGNoaWxkcmVuKCk7IHRyYWNrIGl0ZW0uaWQ7IGxldCBpdGVtSW5kZXggPSAkaW5kZXgpIHtcbiAgICA8IS0tIPCflLQgQ2jhurduIGBtb3VzZWRvd25gIHThuqFpIMSQw4JZIGNobyBsxrDhu5tpIGzhu5NuZyDigJQgQ+G6pE0gYuG7jyB2w6AgQ+G6pE0gY2h1eeG7g24gc2FuZyBjaOG7lyBraMOhYy5cbiAgICAgICAgIGBpZ25vcmVDb250ZW50Q2xhc3NgIG3hu5l0IG3DrG5oIEtIw5RORyDEkeG7pzogYGRlbGF5U3RhcnQ6IDE2MGAgbMOgbSBN4buWSSBsxrDhu5tpIGzDqm4gbOG7i2NoXG4gICAgICAgICBgZHJhZ1N0YXJ0YCBi4bqxbmcgYHNldFRpbWVvdXRgIG5nYXkgdOG6oWkgYG1vdXNlZG93bmAsIGPDsm4gYHN0b3BQcm9wYWdhdGlvbmAgbcOgIGxpYiBn4buNaSBiw6puXG4gICAgICAgICB0cm9uZyBgZHJhZ1N0YXJ0YCBjaOG6oXkgc2F1IDE2MG1zIG7Dqm4gcXXDoSBtdeG7mW4g4oCUIGzGsOG7m2kgQ0hBIMSRw6Mga+G7i3Agbmjhuq1uICjEkW8gMjgvMDgvMjAyNjoga8Opb1xuICAgICAgICAgZOG6o2kga2jhu5FpIGNvbiwgY2h14buXaSBjbGFzcyBkdXnhu4d0IGzDqm4gxJDDmk5HIG3DoCBjb250YWluZXIgduG6q24gbmjhuqN5IDAsMCDihpIgMCwxKS5cbiAgICAgICAgIEfhuq9uIOG7nyBgZ3JpZHN0ZXItaXRlbWAgbMOgIMSRw7puZyBiacOqbjogbMaw4bubaSBjb24gbmdoZSB0csOqbiBjaMOtbmggcGjhuqduIHThu60gbsOgeSBuw6puIMSRw6Mgbmjhuq1uIHhvbmcsXG4gICAgICAgICBsxrDhu5tpIGNoYSDhu58gbmdvw6BpIGLhu4sgY2jhurduLiAtLT5cbiAgICA8Z3JpZHN0ZXItaXRlbVxuICAgICAgW2l0ZW1dPVwiaXRlbVwiXG4gICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1zZWxlY3RlZF09XCJzZWxlY3RlZE5vZGVJZCgpID09PSBpdGVtLmlkXCJcbiAgICAgIChjbGljayk9XCJoYW5kbGVyU2VsZWN0Tm9kZShpdGVtLCAkZXZlbnQpXCJcbiAgICAgIChtb3VzZWRvd24pPVwiaGFuZGxlckJsb2NrUGFyZW50RHJhZygkZXZlbnQpXCI+XG4gICAgICA8IS0tIPCflLQgVuG7jyBuw6B5IG1hbmcgYGRyYWdIYW5kbGVDbGFzc2Ag4oCUIEPhuqRNIGLhu48uXG4gICAgICAgICAgIGBpZ25vcmVDb250ZW50OiB0cnVlYCBi4bqvdCBncmlkc3RlciBDSOG7iCBjaG8ga8OpbyBraGkgY2jhu5cgYuG6pW0gbuG6sW0gZMaw4bubaSBt4buZdCBwaOG6p24gdOG7rSBjw7NcbiAgICAgICAgICAgYGRyYWdIYW5kbGVDbGFzc2AuIEtow7RuZyBjw7MgduG7jyBuw6B5IHRow6wgY29tcG9uZW50IGPhu6dhIG7GoWkgZMO5bmcgKHRoxrAgdmnhu4duIGtow7RuZyBraeG7g20gc2/DoXRcbiAgICAgICAgICAgxJHGsOG7o2MgY2xhc3MgY+G7p2EgbsOzKSBz4bq9IGtow7RuZyBjw7MgdGF5IGPhuqdtIG7DoG8sIHbDoCBLSMOUTkcga2jhu5FpIG7DoG8ga8OpbyDEkcaw4bujYyDigJQgxJFvIDI4LzA4LzIwMjY6XG4gICAgICAgICAgIGvDqW8gMzIwcHgsIHRv4bqhIMSR4buZIGto4buRaSBnaeG7ryBuZ3V5w6puIHg9Myx5PTAuXG4gICAgICAgICAgIFTDqm4gY2xhc3MgdGhlbyBj4bqlcDogbMaw4bubaSB0cmFuZyB2w6AgbMaw4bubaSBs4buTbmcgZMO5bmcgaGFpIHTDqm4gS0jDgUMgbmhhdSwgbuG6v3Uga2jDtG5nIGzGsOG7m2kgY2hhXG4gICAgICAgICAgIGPFqW5nIG5o4bqtbiByYSB0YXkgY+G6p20gY+G7p2Ega2jhu5FpIGNvbiB2w6AgbmjhuqVjIGx1w7RuIGNvbnRhaW5lci4gLS0+XG4gICAgICA8IS0tIPCflLQgVuG7jyBLSMOUTkcgbWFuZyBgZHJhZ0hhbmRsZUNsYXNzYCDigJQgQ+G6pE0gdGjDqm0gbOG6oWkuXG4gICAgICAgICAgIEvDqW8gY2jhu4kgxJHGsOG7o2MgcGjDqXAgYuG6r3QgxJHhuqd1IHThu6sgROG6okkgWEFOSCAobmfGsOG7nWkgZMO5bmcgY2jhu5F0IDI4LzA4LzIwMjY6IFwicGjhuqNpIGhvdmVyIHbDoG8gdsO5bmdcbiAgICAgICAgICAgbcOgdSB4YW5oIHRow6wgbeG7m2kga8OpbyB0aOG6oyDEkcaw4bujYyBjaG8gxJHhu5NuZyBuaOG6pXRcIikuIENobyBj4bqjIHbhu48gbMOgbSB0YXkgY+G6p20gdGjDrCBi4bqlbSDEkcOidSBjxaluZ1xuICAgICAgICAgICBrw6lvLCB2w6Aga2jhu5FpIGNvbiBu4bqxbSB0cm9uZyBjb250YWluZXIgc+G6vSBuaOG6pWMgbHXDtG4gY29udGFpbmVyIHbDrCBsxrDhu5tpIGNoYSBkdXnhu4d0IGzDqm4gZ+G6t3BcbiAgICAgICAgICAgdGF5IGPhuqdtIGPhu6dhIHbhu48gY29udGFpbmVyIHRyxrDhu5tjLlxuICAgICAgICAgICBW4buPIGto4buRaSBjb24gduG6q24gZ2nhu68gY2xhc3MgQ0jhurZOIMSR4buDIGzGsOG7m2kgY2hhIGThu6tuZyDEkcO6bmcg4bufIGJpw6puLiAtLT5cbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWJsb2NrLXNoZWxsIGdyb3VwIHJlbGF0aXZlIGgtZnVsbCB3LWZ1bGwgbWluLXctMFwiXG4gICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWJsb2NrLXBhcmVudC1kcmFnXT1cImlzTmVzdGVkQ2FudmFzKClcIj5cbiAgICAgICAgPCEtLSBE4bqjaSBrw6lvOiBoaeG7h24ga2hpIHLDqiBjaHXhu5l0IHbDoG8ga2jhu5FpLCBiYSBjaOG6pW0gbMOgIGThuqV1IGhp4buHdSBrw6lvIHF1ZW4gdGh14buZYy5cbiAgICAgICAgICAgICBOZ8aw4budaSBkw7luZyBj4bqnbiBt4buZdCBjaOG7lyBiw6FtIFLDlSBSw4BORyB0aGF5IHbDrCDEkW/DoW4geGVtIGLhuqVtIMSRw6J1IHRow6wga8OpbyDEkcaw4bujY1xuICAgICAgICAgICAgIChjaOG7kXQgMjgvMDgvMjAyNiwgdGhlbyDEkcO6bmcgZOG6o2kgxJFhbmcgZMO5bmcg4bufIG3DoG4gQ3VzdG9tZXIgMzYwIGPhu6dhIG1vYmlvLXdlYikuIC0tPlxuICAgICAgICBAaWYgKGlzRWRpdE1vZGUoKSkge1xuICAgICAgICAgIDwhLS0g8J+UtCBE4bqjaSBQSOG6okkgbWFuZyB0YXkgY+G6p20gxJDDmk5HIEPhuqRQIGPhu6dhIG7Dsy4gROG6o2kgbmjhuq1uIGNodeG7mXQgKGBwb2ludGVyLWV2ZW50czogYXV0b2ApLFxuICAgICAgICAgICAgICAgbsOqbiBu4bq/dSBraMO0bmcgbWFuZyB0YXkgY+G6p20gdGjDrCBgY2hlY2tEcmFnSGFuZGxlQ2xhc3NgIGR1eeG7h3QgbMOqbiBn4bq3cCB0YXkgY+G6p20gY+G7p2FcbiAgICAgICAgICAgICAgIGNvbnRhaW5lciB2w6AgbmjhuqVjIGNvbnRhaW5lciB0aGF5IHbDrCBraOG7kWkgY29uICjEkW8gMjgvMDgvMjAyNjoga8OpbyBk4bqjaSBraOG7kWkgY29uLFxuICAgICAgICAgICAgICAgY29udGFpbmVyIG5o4bqjeSAwLDAg4oaSIDAsMSBjw7JuIGto4buRaSBjb24gxJHhu6luZyBpbSkuXG4gICAgICAgICAgICAgICDwn5S0IEThuqNpIENI4buIIG1hbmcgdGF5IGPhuqdtLCBD4bqkTSBtYW5nIHRow6ptIGNsYXNzIGNo4bq3bjogYGNoZWNrRHJhZ0hhbmRsZUNsYXNzYCB4w6l0IGhhaVxuICAgICAgICAgICAgICAgY2xhc3MgxJHDsyB0csOqbiBDw5lORyBt4buZdCBub2RlIG7Dqm4gxJHhu4MgY2h1bmcgbMOgIGvDqW8gaOG7j25nICjEkW8gMjgvMDgvMjAyNjogYuG6pW0gZOG6o2kga2jhu5FpXG4gICAgICAgICAgICAgICBjb24ga8OpbyAxNDBweCwga2jhu5FpIMSR4bupbmcgaW0pLiBWaeG7h2MgY2jhurduIGzGsOG7m2kgY2hhIGRvIFbhu44gS0jhu5BJIGxvLiAtLT5cbiAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXJcIlxuICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1kcmFnLWhhbmRsZV09XCIhaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1kcmFnLWhhbmRsZS1jaGlsZF09XCJpc05lc3RlZENhbnZhcygpXCI+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXItZG90c1wiPlxuICAgICAgICAgICAgICBAZm9yIChkb3Qgb2YgZHJhZ0JhckRvdHM7IHRyYWNrIGRvdCkge1xuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1kcmFnLWJhci1kb3RcIj48L3NwYW4+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuXG4gICAgICAgIDwhLS0gS2h1bmcgY2jhu6lhIGNvbXBvbmVudCBj4bunYSBuxqFpIGTDuW5nLlxuICAgICAgICAgICAgIPCflLQgUEjhuqJJIGPDsyBraHVuZyB0aOG6rXQgKGtow7RuZyBwaOG6o2kgYG5nLWNvbnRhaW5lcmAgdHLhu5FuZyk6IG7DsyBsbyBo4buZIG7GoWkgZMO5bmcgcGjhuqduIGzhuqVwIMSR4bqneVxuICAgICAgICAgICAgIMO0IHbDoCB2aeG7gW4gYsOhbyBjaOG6vyDEkeG7mSBz4butYS4gS2jDtG5nIGPDsyBraHVuZyB0aMOsIG3hu5dpIGNvbXBvbmVudCBu4buZaSBkdW5nIGzhuqFpIHBo4bqjaSB04buxIHZp4bq/dFxuICAgICAgICAgICAgIGA6aG9zdCB7IHdpZHRoL2hlaWdodDogMTAwJSB9YCArIHZp4buBbiDEkeG7qXQg4oCUIMSRw7MgbMOgIHZp4buHYyBj4bunYSB0aMawIHZp4buHbiwga2jDtG5nIHBo4bqjaSBj4bunYVxuICAgICAgICAgICAgIG5nxrDhu51pIGTDuW5nIChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNikuXG4gICAgICAgICAgICAgTeG7l2kga2jhu5FpIG3hu5l0IFZpZXdDb250YWluZXJSZWYgcmnDqm5nIG7Dqm4gY29tcG9uZW50IG7hurFtIMSRw7puZyDDtCBj4bunYSBuw7MuIC0tPlxuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRlbnQtZnJhbWVcIlxuICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRlbnQtZnJhbWUtZWRpdF09XCJpc0VkaXRNb2RlKCkgJiYgIWlzQ29udGFpbmVyKGl0ZW0pXCJcbiAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1jb250ZW50LWZyYW1lLWVtcHR5XT1cImlzQ29udGFpbmVyKGl0ZW0pXCI+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciAjaXRlbUhvc3QgLz5cbiAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgPCEtLSBE4bqldSBoaeG7h3UgQ09OVEFJTkVSOiBuaMOjbiBnw7NjIHRyw6puLXBo4bqjaSArIHZp4buBbiDEkeG7qXQgYmFvIHJ14buZdC4gS2jDtG5nIGPDsyBk4bqldSBoaeG7h3UgdGjDrFxuICAgICAgICAgICAgIG5nxrDhu51pIGTDuW5nIGtow7RuZyBiaeG6v3Qga2jhu5FpIG7DoG8gY2jhu6lhIMSRxrDhu6NjIGto4buRaSBraMOhYyAoY2jhu5F0IDI4LzA4LzIwMjYsIHRoZW8gxJHDum5nIGPDoWNoXG4gICAgICAgICAgICAgbcOgbiBDdXN0b21lciAzNjAgY+G7p2EgbW9iaW8td2ViIMSRYW5nIGzDoG0pLiAtLT5cbiAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkgJiYgaXNDb250YWluZXIoaXRlbSkpIHtcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1jb250YWluZXItbGFiZWxcIj57eyAnaTE4bl9jb250YWluZXJfYmxvY2snIHwgdHJhbnNsYXRlIH19PC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGFpbmVyLWJvcmRlclwiPjwvZGl2PlxuICAgICAgICB9XG5cbiAgICAgICAgPCEtLSBUaGFuaCBjw7RuZyBj4bulOiBoaeG7h24ga2hpIHLDqiBjaHXhu5l0IHbDoG8ga2jhu5FpLlxuICAgICAgICAgICAgIPCflLQgRMO5bmcgYGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvbmAg4oCUIEPhuqRNIHThu7EgZOG7sW5nIHRo4bq7IGA8YnV0dG9uPmAuXG4gICAgICAgICAgICAgQ29tcG9uZW50IG7DoHkgxJHDoyBjw7Mgc+G6tW4ga2nhu4N1IG7DunQsIGPhu6EgbsO6dCB2w6AgYm9uZyBiw7NuZyBjaMO6IHRow61jaCB0aGVvIMSRw7puZyBi4buZIGdpYW8gZGnhu4duXG4gICAgICAgICAgICAgY+G7p2Egc+G6o24gcGjhuqltOyB04buxIGThu7FuZyBsw6AgbeG7l2kgbsahaSBt4buZdCBraeG7g3UgdsOgIG3huqV0IGx1w7RuIHRvb2x0aXAuIC0tPlxuICAgICAgICBAaWYgKGlzRWRpdE1vZGUoKSAmJiAhaGlkZGVuVG9vbGJhcigpKSB7XG4gICAgICAgICAgQGlmICh0ZW1wbGF0ZVRvb2xiYXIoKSkge1xuICAgICAgICAgICAgPCEtLSBOxqFpIGTDuW5nIHThu7EgduG6vSB0aGFuaCBjw7RuZyBj4bulOiBuaOG6rW4ga2jhu5FpICsgY8OhYyBow6BtIHBow6F0IGV2ZW50IHF1YSBjb250ZXh0LlxuXG4gICAgICAgICAgICAgICAgIPCflLQgS2jhu5FpIGLhu41jIHThu7EgY2jhurduIGBtb3VzZWRvd25gL2BjbGlja2AgbuG7lWkgbMOqbiBncmlkc3Rlci4gVGhhbmggY8O0bmcgY+G7pSBu4bqxbSB0cm9uZ1xuICAgICAgICAgICAgICAgICB2w7luZyBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgOyBraMO0bmcgY2jhurduIHRow6wgY8O6IGLhuqVtIGLhu4sgaGnhu4N1IGzDoCBi4bqvdCDEkeG6p3Uga8OpbyB2w6BcbiAgICAgICAgICAgICAgICAgYGRlbGF5U3RhcnQ6IDE2MGAgbnXhu5F0IGx1w7RuIGBjbGlja2Ag4oCUIG7DunQgdHJvbmcgdGVtcGxhdGUgbmdvw6BpIGLhuqVtIGtow7RuZyDEg25cbiAgICAgICAgICAgICAgICAgKMSRbyAwOS8wOS8yMDI2OiBi4bqlbSBi4bqxbmcgYC5jbGljaygpYCBj4bunYSBET00gdGjDrCBt4bufLCBi4bqlbSBjaHXhu5l0IHRo4bqtdCB0aMOsIGtow7RuZykuXG4gICAgICAgICAgICAgICAgIENo4bq3biDhu58gxJDDglkgxJHhu4MgbsahaSBkw7luZyBraMO0bmcgcGjhuqNpIGJp4bq/dCBi4bqreSBuw6B5LiAtLT5cbiAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXJcIlxuICAgICAgICAgICAgICByb2xlPVwidG9vbGJhclwiXG4gICAgICAgICAgICAgIHRhYmluZGV4PVwiMFwiXG4gICAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICAgIChtb3VzZWRvd24pPVwiaGFuZGxlclN0b3BFdmVudCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKGtleWRvd24pPVwiaGFuZGxlclN0b3BFdmVudCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJTdG9wRXZlbnQoJGV2ZW50KVwiPlxuICAgICAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAgICAgKm5nVGVtcGxhdGVPdXRsZXQ9XCJ0ZW1wbGF0ZVRvb2xiYXIoKSE7IGNvbnRleHQ6IHRvb2xiYXJDb250ZXh0cygpW2l0ZW1JbmRleF1cIiAvPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC10b29sYmFyXCJcbiAgICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtdG9vbGJhci1jaGlsZF09XCJpc05lc3RlZENhbnZhcygpXCI+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1kaW0tYmFkZ2VcIj5cbiAgICAgICAgICAgICAgICB7eyBpdGVtLnJvd3MgfX0gaMOgbmcgw5cge3sgaXRlbS5jb2xzIH19IGPhu5l0XG4gICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgQGlmIChjYW5BZGRJbnNpZGUoaXRlbSkpIHtcbiAgICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgICBbdHlwZV09XCInYnV0dG9uLXRoaXJkJ1wiXG4gICAgICAgICAgICAgICAgICBbaWNvbk9ubHlUeXBlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgW3NpemVCdXR0b25dPVwiJ3NtYWxsZXInXCJcbiAgICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tYWRkJ1wiXG4gICAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiAnVGjDqm0ga2jhu5FpIHbDoG8gdHJvbmcnLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyQWRkSW5zaWRlKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIEBpZiAoY2FuVG9nZ2xlVHlwZShpdGVtKSkge1xuICAgICAgICAgICAgICAgIDxsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25cbiAgICAgICAgICAgICAgICAgIFt0eXBlXT1cIididXR0b24tdGhpcmQnXCJcbiAgICAgICAgICAgICAgICAgIFtpY29uT25seVR5cGVdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgICAgW2NsYXNzSWNvbkxlZnRdPVwiaXNDb250YWluZXIoaXRlbSkgPyAnbGlicy11aS1pY29uLXNwbGl0LWNlbGwnIDogJ2xpYnMtdWktaWNvbi1tZXJnZS1jZWxsJ1wiXG4gICAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiBpc0NvbnRhaW5lcihpdGVtKSA/ICfEkMawYSB24buBIGto4buRaSDEkcahbicgOiAnQ2h1eeG7g24gdGjDoG5oIGto4buRaSBnaMOpcCcsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgICAgKG91dENsaWNrKT1cImhhbmRsZXJUb2dnbGVUeXBlKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDxsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25cbiAgICAgICAgICAgICAgICBbdHlwZV09XCInYnV0dG9uLXRoaXJkJ1wiXG4gICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tc2V0dGluZydcIlxuICAgICAgICAgICAgICAgIFtwb3BvdmVyXT1cInsgY29uZmlnOiB7IGNvbnRlbnQ6ICdD4bqldSBow6xuaCB0aOG6uycsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyQ29uZmlnKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZC1ob3Zlci1kYW5nZXInXCJcbiAgICAgICAgICAgICAgICBbaWNvbk9ubHlUeXBlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgIFtzaXplQnV0dG9uXT1cIidzbWFsbGVyJ1wiXG4gICAgICAgICAgICAgICAgW2NsYXNzSWNvbkxlZnRdPVwiJ2xpYnMtdWktaWNvbi1yZW1vdmUnXCJcbiAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiBpc0NvbnRhaW5lcihpdGVtKSA/ICdYb8OhIGPhuqMga2jhu5FpIGdow6lwJyA6ICdYb8OhIGto4buRaScsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyUmVtb3ZlKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgQGlmIChpdGVtLmNoaWxkcmVuICYmIGl0ZW0uY2hpbGRyZW4ubGVuZ3RoID4gMCkge1xuICAgICAgICAgIDwhLS0gS2jhu5FpIGPDsyBjb24g4oaSIHJ14buZdCBuw7MgbOG6oWkgbMOgIG3hu5l0IG3hurd0IHBo4bqzbmcgbuG7r2EuIMSQ4buHIHF1eSDhu58gxJHDonksIG7GoWkgZMO5bmcga2jDtG5nIHBo4bqjaSBsby5cbiAgICAgICAgICAgICAgIEzhu4EgxJHhurd0IOG7nyDEkMOCWSAoduG7jyBjb250YWluZXIpLCBraMO0bmcgxJHhurd0IHbDoG8gbMaw4bubaSBjb24g4oCUIGzGsOG7m2kgY29uIGdp4buvIG5ndXnDqm4ga2hlIDJweC4gLS0+XG4gICAgICAgICAgPGxpYnNfdWktc2VydmljZXMtZ3JpZF9sYXlvdXQtY2FudmFzXG4gICAgICAgICAgICBjbGFzcz1cImJsb2NrIGgtZnVsbCB3LWZ1bGxcIlxuICAgICAgICAgICAgW3N0eWxlLnBhZGRpbmddPVwiY29udGFpbmVyUGFkZGluZygpXCJcbiAgICAgICAgICAgIFtub2RlXT1cIml0ZW1cIlxuICAgICAgICAgICAgW2RlcHRoXT1cImRlcHRoKCkgKyAxXCJcbiAgICAgICAgICAgIFtoaWRkZW5Ub29sYmFyXT1cImhpZGRlblRvb2xiYXIoKVwiXG4gICAgICAgICAgICBbdGVtcGxhdGVUb29sYmFyXT1cInRlbXBsYXRlVG9vbGJhcigpXCJcbiAgICAgICAgICAgIFtzZWxlY3RlZE5vZGVJZF09XCJzZWxlY3RlZE5vZGVJZCgpXCJcbiAgICAgICAgICAgIChvdXRTZWxlY3ROb2RlKT1cIm91dFNlbGVjdE5vZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgIChvdXRDb25maWdOb2RlKT1cIm91dENvbmZpZ05vZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgIChvdXRDaGFuZ2UpPVwib3V0Q2hhbmdlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAob3V0UmVtb3ZlTm9kZSk9XCJvdXRSZW1vdmVOb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAob3V0QWRkQmxvY2tJbnNpZGUpPVwib3V0QWRkQmxvY2tJbnNpZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgIChvdXRUb2dnbGVUeXBlKT1cIm91dFRvZ2dsZVR5cGUuZW1pdCgkZXZlbnQpXCIgLz5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG4gICAgPC9ncmlkc3Rlci1pdGVtPlxuICB9XG4gICAgICA8L2dyaWRzdGVyPlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cblxuICA8IS0tIFRoYW5oIGRvY2sgxJFp4buBdSBraGnhu4NuIHRodSBwaMOzbmcgbuG7lWkg4bufIGfDs2MgZMaw4bubaSBwaOG6o2kgLS0+XG4gIEBpZiAoc2hvd1pvb21Eb2NrKCkpIHtcbiAgICA8ZGl2XG4gICAgICAjem9vbURvY2tcbiAgICAgIGlkPVwiY2FudmFzLXpvb20tZG9ja1wiXG4gICAgICByb2xlPVwidG9vbGJhclwiXG4gICAgICB0YWJpbmRleD1cIjBcIlxuICAgICAgY2xhc3M9XCJhYnNvbHV0ZSBib3R0b20tNCByaWdodC02IHotMzAgZmxleCBpdGVtcy1jZW50ZXIgYmctd2hpdGUvOTUgYmFja2Ryb3AtYmx1ci1tZCByb3VuZGVkLWZ1bGwgc2hhZG93LWxnIGJvcmRlciBib3JkZXItc2xhdGUtMzAwLzgwIHAtMSBzcGFjZS14LTEgc2VsZWN0LW5vbmVcIlxuICAgICAgKG1vdXNlZG93bik9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgKGtleWRvd24pPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgIChjbGljayk9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIj5cbiAgICAgIDwhLS0gVGh1IG5o4buPICgtKSAtLT5cbiAgICAgIDxidXR0b25cbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwidy03IGgtNyBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciByb3VuZGVkLWZ1bGwgdGV4dC1zbGF0ZS02MDAgaG92ZXI6dGV4dC1zbGF0ZS05MDAgaG92ZXI6Ymctc2xhdGUtMTAwIGRpc2FibGVkOm9wYWNpdHktMzAgZGlzYWJsZWQ6aG92ZXI6YmctdHJhbnNwYXJlbnQgdHJhbnNpdGlvbi1jb2xvcnMgY3Vyc29yLXBvaW50ZXJcIlxuICAgICAgICBbZGlzYWJsZWRdPVwiY3VycmVudFpvb20oKSA8PSB6b29tTWluKClcIlxuICAgICAgICBbdGl0bGVdPVwiJ2kxOG5fem9vbV9vdXQnIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJab29tT3V0KClcIj5cbiAgICAgICAgPHN2ZyBjbGFzcz1cInctMy41IGgtMy41XCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMi41XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiPjxsaW5lIHgxPVwiNVwiIHkxPVwiMTJcIiB4Mj1cIjE5XCIgeTI9XCIxMlwiLz48L3N2Zz5cbiAgICAgIDwvYnV0dG9uPlxuXG4gICAgICA8IS0tIFThu7cgbOG7hyAlIHbDoCBtZW51IHByZXNldCAtLT5cbiAgICAgIDxkaXYgY2xhc3M9XCJyZWxhdGl2ZVwiPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgY2xhc3M9XCJoLTcgcHgtMiBmbGV4IGl0ZW1zLWNlbnRlciBnYXAtMSByb3VuZGVkLWZ1bGwgdGV4dC14cyBmb250LXNlbWlib2xkIHRleHQtc2xhdGUtNzAwIGhvdmVyOmJnLXNsYXRlLTEwMCB0cmFuc2l0aW9uLWNvbG9ycyBjdXJzb3ItcG9pbnRlclwiXG4gICAgICAgICAgW3RpdGxlXT1cIidpMThuX3pvb21fb3B0aW9ucycgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgIChjbGljayk9XCJoYW5kbGVyVG9nZ2xlTWVudSgpXCI+XG4gICAgICAgICAgPHNwYW4+e3sgem9vbVBlcmNlbnQoKSB9fSU8L3NwYW4+XG4gICAgICAgICAgPHN2ZyBjbGFzcz1cInctMyBoLTMgdGV4dC1zbGF0ZS00MDAgdHJhbnNpdGlvbi10cmFuc2Zvcm0gZHVyYXRpb24tMTUwXCIgW2NsYXNzLnJvdGF0ZS0xODBdPVwiaXNNZW51T3BlbigpXCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMi41XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCI+PHBvbHlsaW5lIHBvaW50cz1cIjYgOSAxMiAxNSAxOCA5XCIvPjwvc3ZnPlxuICAgICAgICA8L2J1dHRvbj5cblxuICAgICAgICA8IS0tIE1lbnUgZHJvcGRvd24gY8OhYyBt4buRYyB0aHUgcGjDs25nIC0tPlxuICAgICAgICBAaWYgKGlzTWVudU9wZW4oKSkge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJhYnNvbHV0ZSBib3R0b20tZnVsbCBtYi0yIHJpZ2h0LTAgdy02NCBiZy13aGl0ZSByb3VuZGVkLXhsIHNoYWRvdy14bCBib3JkZXIgYm9yZGVyLXNsYXRlLTIwMCBweS0xLjUgei00MCB0ZXh0LXhzIHNlbGVjdC1ub25lXCI+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwicHgtMyBweS0xIHRleHQtWzExcHhdIGZvbnQtc2VtaWJvbGQgdGV4dC1zbGF0ZS00MDAgdXBwZXJjYXNlIHRyYWNraW5nLXdpZGVyXCI+XG4gICAgICAgICAgICAgIHt7ICdpMThuX3pvb21fY2FudmFzX3dpZHRoJyB8IHRyYW5zbGF0ZTogeyB3aWR0aDogc3RhZ2VXaWR0aCgpIH0gfX1cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgY2xhc3M9XCJ3LWZ1bGwgcHgtMyBweS0yIHRleHQtbGVmdCBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWJldHdlZW4gaG92ZXI6Ymctc2xhdGUtNTAgdHJhbnNpdGlvbi1jb2xvcnMgY3Vyc29yLXBvaW50ZXJcIlxuICAgICAgICAgICAgICAoY2xpY2spPVwiaGFuZGxlckZpdFNjcmVlbigpXCI+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTIgdGV4dC1zbGF0ZS03MDAgZm9udC1tZWRpdW1cIj5cbiAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPVwidy0zLjUgaC0zLjUgdGV4dC1zbGF0ZS01MDBcIiB2aWV3Qm94PVwiMCAwIDI0IDI0XCIgZmlsbD1cIm5vbmVcIiBzdHJva2U9XCJjdXJyZW50Q29sb3JcIiBzdHJva2Utd2lkdGg9XCIyXCI+PHBvbHlsaW5lIHBvaW50cz1cIjE1IDMgMjEgMyAyMSA5XCIvPjxwb2x5bGluZSBwb2ludHM9XCI5IDIxIDMgMjEgMyAxNVwiLz48bGluZSB4MT1cIjIxXCIgeTE9XCIzXCIgeDI9XCIxNFwiIHkyPVwiMTBcIi8+PGxpbmUgeDE9XCIzXCIgeTE9XCIyMVwiIHgyPVwiMTBcIiB5Mj1cIjE0XCIvPjwvc3ZnPlxuICAgICAgICAgICAgICAgIHt7ICdpMThuX3pvb21fZml0X3NjcmVlbicgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtWzEwcHhdIGJnLWJsdWUtNTAgdGV4dC1ibHVlLTYwMCBweC0xLjUgcHktMC41IHJvdW5kZWQgZm9udC1tZWRpdW1cIj57eyAnaTE4bl9hdXRvJyB8IHRyYW5zbGF0ZSB9fTwvc3Bhbj5cbiAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImgtcHggYmctc2xhdGUtMTAwIG15LTFcIj48L2Rpdj5cbiAgICAgICAgICAgIEBmb3IgKHByZXNldCBvZiB6b29tUHJlc2V0czsgdHJhY2sgcHJlc2V0LnZhbHVlKSB7XG4gICAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgICBjbGFzcz1cInctZnVsbCBweC0zIHB5LTEuNSB0ZXh0LWxlZnQgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1iZXR3ZWVuIGhvdmVyOmJnLXNsYXRlLTUwIHRyYW5zaXRpb24tY29sb3JzIGN1cnNvci1wb2ludGVyXCJcbiAgICAgICAgICAgICAgICBbY2xhc3MudGV4dC1ibHVlLTYwMF09XCJpc0N1cnJlbnRQcmVzZXQocHJlc2V0LnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgW2NsYXNzLmZvbnQtc2VtaWJvbGRdPVwiaXNDdXJyZW50UHJlc2V0KHByZXNldC52YWx1ZSlcIlxuICAgICAgICAgICAgICAgIFtjbGFzcy5iZy1ibHVlLTUwXT1cImlzQ3VycmVudFByZXNldChwcmVzZXQudmFsdWUpXCJcbiAgICAgICAgICAgICAgICAoY2xpY2spPVwiaGFuZGxlclNlbGVjdFByZXNldChwcmVzZXQudmFsdWUpXCI+XG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LXNsYXRlLTcwMFwiIFtjbGFzcy50ZXh0LWJsdWUtNjAwXT1cImlzQ3VycmVudFByZXNldChwcmVzZXQudmFsdWUpXCI+XG4gICAgICAgICAgICAgICAgICB7eyBwcmVzZXQubGFiZWwgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgQGlmIChpc0N1cnJlbnRQcmVzZXQocHJlc2V0LnZhbHVlKSkge1xuICAgICAgICAgICAgICAgICAgPHN2ZyBjbGFzcz1cInctMy41IGgtMy41IHRleHQtYmx1ZS02MDBcIiB2aWV3Qm94PVwiMCAwIDI0IDI0XCIgZmlsbD1cIm5vbmVcIiBzdHJva2U9XCJjdXJyZW50Q29sb3JcIiBzdHJva2Utd2lkdGg9XCIyLjVcIiBzdHJva2UtbGluZWNhcD1cInJvdW5kXCIgc3Ryb2tlLWxpbmVqb2luPVwicm91bmRcIj48cG9seWxpbmUgcG9pbnRzPVwiMjAgNiA5IDE3IDQgMTJcIi8+PC9zdmc+XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG5cbiAgICAgIDwhLS0gUGjDs25nIHRvICgrKSAtLT5cbiAgICAgIDxidXR0b25cbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwidy03IGgtNyBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciByb3VuZGVkLWZ1bGwgdGV4dC1zbGF0ZS02MDAgaG92ZXI6dGV4dC1zbGF0ZS05MDAgaG92ZXI6Ymctc2xhdGUtMTAwIGRpc2FibGVkOm9wYWNpdHktMzAgZGlzYWJsZWQ6aG92ZXI6YmctdHJhbnNwYXJlbnQgdHJhbnNpdGlvbi1jb2xvcnMgY3Vyc29yLXBvaW50ZXJcIlxuICAgICAgICBbZGlzYWJsZWRdPVwiY3VycmVudFpvb20oKSA+PSB6b29tTWF4KClcIlxuICAgICAgICBbdGl0bGVdPVwiJ2kxOG5fem9vbV9pbicgfCB0cmFuc2xhdGVcIlxuICAgICAgICAoY2xpY2spPVwiaGFuZGxlclpvb21JbigpXCI+XG4gICAgICAgIDxzdmcgY2xhc3M9XCJ3LTMuNSBoLTMuNVwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjIuNVwiIHN0cm9rZS1saW5lY2FwPVwicm91bmRcIj48bGluZSB4MT1cIjEyXCIgeTE9XCI1XCIgeDI9XCIxMlwiIHkyPVwiMTlcIi8+PGxpbmUgeDE9XCI1XCIgeTE9XCIxMlwiIHgyPVwiMTlcIiB5Mj1cIjEyXCIvPjwvc3ZnPlxuICAgICAgPC9idXR0b24+XG5cbiAgICAgIDwhLS0gUGjDom4gY8OhY2ggLS0+XG4gICAgICA8ZGl2IGNsYXNzPVwiaC00IHctcHggYmctc2xhdGUtMjAwXCI+PC9kaXY+XG5cbiAgICAgIDwhLS0gTsO6dCBW4burYSBtw6BuIGjDrG5oIG5oYW5oIC0tPlxuICAgICAgPGJ1dHRvblxuICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgY2xhc3M9XCJoLTcgcHgtMi41IGZsZXggaXRlbXMtY2VudGVyIGdhcC0xIHJvdW5kZWQtZnVsbCB0ZXh0LXhzIHRleHQtc2xhdGUtNjAwIGhvdmVyOnRleHQtc2xhdGUtOTAwIGhvdmVyOmJnLXNsYXRlLTEwMCB0cmFuc2l0aW9uLWNvbG9ycyBjdXJzb3ItcG9pbnRlclwiXG4gICAgICAgIFt0aXRsZV09XCInaTE4bl96b29tX2ZpdF9zY3JlZW5fdG9vbHRpcCcgfCB0cmFuc2xhdGVcIlxuICAgICAgICAoY2xpY2spPVwiaGFuZGxlckZpdFNjcmVlbigpXCI+XG4gICAgICAgIDxzdmcgY2xhc3M9XCJ3LTMuNSBoLTMuNVwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjJcIj48cG9seWxpbmUgcG9pbnRzPVwiMTUgMyAyMSAzIDIxIDlcIi8+PHBvbHlsaW5lIHBvaW50cz1cIjkgMjEgMyAyMSAzIDE1XCIvPjxsaW5lIHgxPVwiMjFcIiB5MT1cIjNcIiB4Mj1cIjE0XCIgeTI9XCIxMFwiLz48bGluZSB4MT1cIjNcIiB5MT1cIjIxXCIgeDI9XCIxMFwiIHkyPVwiMTRcIi8+PC9zdmc+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZm9udC1tZWRpdW1cIj57eyAnaTE4bl96b29tX2ZpdF9zY3JlZW4nIHwgdHJhbnNsYXRlIH19PC9zcGFuPlxuICAgICAgPC9idXR0b24+XG5cbiAgICAgIDwhLS0gTsO6dCDEkOG6t3QgbOG6oWkgMTAwJSAoY2jhu4kgaGnhu4duIGtoaSBraMOhYyAxMDAlKSAtLT5cbiAgICAgIEBpZiAoem9vbVBlcmNlbnQoKSAhPT0gMTAwKSB7XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICBjbGFzcz1cImgtNyBweC0yIGZsZXggaXRlbXMtY2VudGVyIGdhcC0xIHJvdW5kZWQtZnVsbCB0ZXh0LXhzIGZvbnQtc2VtaWJvbGQgdGV4dC1ibHVlLTYwMCBob3ZlcjpiZy1ibHVlLTUwIHRyYW5zaXRpb24tY29sb3JzIGN1cnNvci1wb2ludGVyXCJcbiAgICAgICAgICBbdGl0bGVdPVwiJ2kxOG5fem9vbV9yZXNldF90b29sdGlwJyB8IHRyYW5zbGF0ZTogeyB3aWR0aDogc3RhZ2VXaWR0aCgpLCBoZWlnaHQ6IHN0YWdlTWluSGVpZ2h0KCkgfVwiXG4gICAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJSZXNldFpvb20oKVwiPlxuICAgICAgICAgIDxzdmcgY2xhc3M9XCJ3LTMgaC0zXCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMlwiIHN0cm9rZS1saW5lY2FwPVwicm91bmRcIiBzdHJva2UtbGluZWpvaW49XCJyb3VuZFwiPjxwb2x5bGluZSBwb2ludHM9XCIxIDQgMSAxMCA3IDEwXCIvPjxwYXRoIGQ9XCJNMy41MSAxNWE5IDkgMCAxIDAgMi4xMy05LjM2TDEgMTBcIi8+PC9zdmc+XG4gICAgICAgICAgPHNwYW4+MTAwJTwvc3Bhbj5cbiAgICAgICAgPC9idXR0b24+XG4gICAgICB9XG4gICAgPC9kaXY+XG4gIH1cbn0gQGVsc2Uge1xuICA8IS0tIPCflLQgYHJlbGF0aXZlYCBsw6AgQuG6rlQgQlXhu5hDLCBD4bqkTSBi4buPLlxuICAgICAgIGdyaWRzdGVyIMSR4buVaSB24buLIHRyw60gY2h14buZdCB0aMOgbmggw7QgbMaw4bubaSBi4bqxbmcgYGUuY2xpZW50WSArIChlbC5zY3JvbGxUb3AgLSBlbC5vZmZzZXRUb3ApYCDigJQgY8O0bmdcbiAgICAgICB0aOG7qWMgxJHDsyBjaOG7iSDEkcO6bmcga2hpIGBvZmZzZXRQYXJlbnRgIGPhu6dhIDxncmlkc3Rlcj4gQ0jDjU5IIEzDgCBraOG7kWkgY3Xhu5luIG7DoHkuIFRoaeG6v3UgYHJlbGF0aXZlYCB0aMOsXG4gICAgICAgYG9mZnNldFBhcmVudGAgbmjhuqN5IGzDqm4ga2jhu5FpIGJhbyBuZ2/DoGk6IGBvZmZzZXRUb3BgIHRy4bqjIDE2IHRyb25nIGtoaSBs4buHY2ggdGjhuq10IGzDoCA2NSDihpIgZ3JpZHN0ZXJcbiAgICAgICB0w61uaCDDtCBzYWkgNDlweCwgw7QgY2jhu50gxJHhurd0IGhp4buHbiBs4buHY2gga2jhu49pIGNvbiB0cuG7jyBuw6puIGvDqW8gdGjhuqMga2jDtG5nIMSR4buVaSDEkcaw4bujYyB24buLIHRyw60sIHbDoCBrw6lvIG3DqXBcbiAgICAgICBjxaluZyB0w61uaCBzYWkgxJFp4buDbSBk4burbmcuIC0tPlxuICA8ZGl2XG4gICAgI3Njcm9sbEFyZWFcbiAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtc2Nyb2xsLWFyZWEgcmVsYXRpdmUgaC1mdWxsIG1pbi1oLTAgdy1mdWxsIG1pbi13LTBcIlxuICAgIFtjbGFzcy5vdmVyZmxvdy15LWF1dG9dPVwiIWlzTmVzdGVkQ2FudmFzKClcIlxuICAgIChtb3VzZWRvd24pPVwiaGFuZGxlck1vdXNlRG93blRyb25nTHVvaSgkZXZlbnQpXCJcbiAgICAoZHJhZ292ZXIpPVwiaGFuZGxlckRyYWdPdmVyKCRldmVudClcIlxuICAgIChkcmFnbGVhdmUpPVwiaGFuZGxlckRyYWdMZWF2ZSgkZXZlbnQpXCJcbiAgICAoZHJvcCk9XCJoYW5kbGVyRHJvcCgkZXZlbnQpXCI+XG4gICAgPCEtLSBTbmFwcGVkIERyb3AgR2hvc3QgUHJldmlldyAtLT5cbiAgICBAaWYgKGRyb3BHaG9zdCgpOyBhcyBnaG9zdCkge1xuICAgICAgPGRpdlxuICAgICAgICBjbGFzcz1cInBvaW50ZXItZXZlbnRzLW5vbmUgYWJzb2x1dGUgei0zMCBmbGV4IGZsZXgtY29sIGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciBvdmVyZmxvdy1oaWRkZW4gcm91bmRlZC1sZyBib3JkZXItMiBib3JkZXItZGFzaGVkIGJvcmRlci1ibHVlLTUwMCBiZy1ibHVlLTUwMC8xNSBwLTEgc2hhZG93LXNtIHRyYW5zaXRpb24tYWxsIGR1cmF0aW9uLTc1IGVhc2Utb3V0IGJhY2tkcm9wLWJsdXItWzAuNXB4XSBzZWxlY3Qtbm9uZVwiXG4gICAgICAgIFtzdHlsZS5sZWZ0LnB4XT1cImdob3N0LnBpeGVsTGVmdFwiXG4gICAgICAgIFtzdHlsZS50b3AucHhdPVwiZ2hvc3QucGl4ZWxUb3BcIlxuICAgICAgICBbc3R5bGUud2lkdGgucHhdPVwiZ2hvc3QucGl4ZWxXaWR0aFwiXG4gICAgICAgIFtzdHlsZS5oZWlnaHQucHhdPVwiZ2hvc3QucGl4ZWxIZWlnaHRcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImlubGluZS1mbGV4IG1heC13LVs5NSVdIGl0ZW1zLWNlbnRlciBnYXAtMSB0cnVuY2F0ZSByb3VuZGVkLW1kIGJnLWJsdWUtNjAwIHB4LTIgcHktMC41IHRleHQtWzExcHhdIGZvbnQtc2VtaWJvbGQgdGV4dC13aGl0ZSBzaGFkb3cteHNcIj5cbiAgICAgICAgICBAaWYgKGdob3N0LmNhcmRJY29uQ2xhc3MpIHtcbiAgICAgICAgICAgIDxpIFtjbGFzc109XCJnaG9zdC5jYXJkSWNvbkNsYXNzICsgJyB0ZXh0LXhzJ1wiPjwvaT5cbiAgICAgICAgICB9IEBlbHNlIGlmIChnaG9zdC5jYXJkSWNvblRleHQpIHtcbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1bMTBweF0gZm9udC1ib2xkXCI+e3sgZ2hvc3QuY2FyZEljb25UZXh0IH19PC9zcGFuPlxuICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LXhzIGZvbnQtYm9sZFwiPnt7IGdob3N0LmlzSW5zaWRlR3JvdXAgPyAn4oazJyA6ICcrJyB9fTwvc3Bhbj5cbiAgICAgICAgICB9XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0cnVuY2F0ZVwiPnt7IGdob3N0LmNhcmRUaXRsZSB9fTwvc3Bhbj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtWzlweF0gZm9udC1tb25vIG9wYWNpdHktNzVcIj4oe3sgZ2hvc3QuY29scyB9fWMpPC9zcGFuPlxuICAgICAgICA8L2Rpdj5cblxuICAgICAgICBAaWYgKGdob3N0LnBpeGVsSGVpZ2h0ID49IDgwKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm10LTEgZmxleCBtYXgtdy1bOTUlXSBpdGVtcy1jZW50ZXIgZ2FwLTEuNSB0cnVuY2F0ZSByb3VuZGVkIGJvcmRlciBib3JkZXItYmx1ZS0xMDAgYmctd2hpdGUvOTUgcHgtMiBweS0wLjUgdGV4dC1bMTBweF0gZm9udC1tZWRpdW0gdGV4dC1ibHVlLTcwMCBzaGFkb3cteHNcIj5cbiAgICAgICAgICAgIEBpZiAoZ2hvc3QuaXNJbnNpZGVHcm91cCkge1xuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRydW5jYXRlIGZvbnQtYm9sZCB0ZXh0LWJsdWUtOTAwXCI+4oazIHt7IGdob3N0Lmdyb3VwVGl0bGUgfX08L3NwYW4+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1ibHVlLTMwMFwiPuKAojwvc3Bhbj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDxzcGFuPkPhu5l0IHt7IGdob3N0LmNvbCArIDEgfX0te3sgZ2hvc3QuY29sICsgZ2hvc3QuY29scyB9fTwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG4gICAgfVxuICAgIDxncmlkc3RlciBjbGFzcz1cImgtZnVsbCB3LWZ1bGxcIiBbY2xhc3MuZGlzcGxheS1ncmlkXT1cInNob3dHcmlkTGluZXMoKVwiIFtvcHRpb25zXT1cImdyaWRPcHRpb25zKClcIj5cbiAgICAgICAgQGZvciAoaXRlbSBvZiBjaGlsZHJlbigpOyB0cmFjayBpdGVtLmlkOyBsZXQgaXRlbUluZGV4ID0gJGluZGV4KSB7XG4gICAgPCEtLSDwn5S0IENo4bq3biBgbW91c2Vkb3duYCB04bqhaSDEkMOCWSBjaG8gbMaw4bubaSBs4buTbmcg4oCUIEPhuqRNIGLhu48gdsOgIEPhuqRNIGNodXnhu4NuIHNhbmcgY2jhu5cga2jDoWMuXG4gICAgICAgICBgaWdub3JlQ29udGVudENsYXNzYCBt4buZdCBtw6xuaCBLSMOUTkcgxJHhu6c6IGBkZWxheVN0YXJ0OiAxNjBgIGzDoG0gTeG7lkkgbMaw4bubaSBsw6puIGzhu4tjaFxuICAgICAgICAgYGRyYWdTdGFydGAgYuG6sW5nIGBzZXRUaW1lb3V0YCBuZ2F5IHThuqFpIGBtb3VzZWRvd25gLCBjw7JuIGBzdG9wUHJvcGFnYXRpb25gIG3DoCBsaWIgZ+G7jWkgYsOqblxuICAgICAgICAgdHJvbmcgYGRyYWdTdGFydGAgY2jhuqF5IHNhdSAxNjBtcyBuw6puIHF1w6EgbXXhu5luIOKAlCBsxrDhu5tpIENIQSDEkcOjIGvhu4twIG5o4bqtbiAoxJFvIDI4LzA4LzIwMjY6IGvDqW9cbiAgICAgICAgIGThuqNpIGto4buRaSBjb24sIGNodeG7l2kgY2xhc3MgZHV54buHdCBsw6puIMSQw5pORyBtw6AgY29udGFpbmVyIHbhuqtuIG5o4bqjeSAwLDAg4oaSIDAsMSkuXG4gICAgICAgICBH4bqvbiDhu58gYGdyaWRzdGVyLWl0ZW1gIGzDoCDEkcO6bmcgYmnDqm46IGzGsOG7m2kgY29uIG5naGUgdHLDqm4gY2jDrW5oIHBo4bqnbiB04butIG7DoHkgbsOqbiDEkcOjIG5o4bqtbiB4b25nLFxuICAgICAgICAgbMaw4bubaSBjaGEg4bufIG5nb8OgaSBi4buLIGNo4bq3bi4gLS0+XG4gICAgPGdyaWRzdGVyLWl0ZW1cbiAgICAgIFtpdGVtXT1cIml0ZW1cIlxuICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtc2VsZWN0ZWRdPVwic2VsZWN0ZWROb2RlSWQoKSA9PT0gaXRlbS5pZFwiXG4gICAgICAoY2xpY2spPVwiaGFuZGxlclNlbGVjdE5vZGUoaXRlbSwgJGV2ZW50KVwiXG4gICAgICAobW91c2Vkb3duKT1cImhhbmRsZXJCbG9ja1BhcmVudERyYWcoJGV2ZW50KVwiPlxuICAgICAgPCEtLSDwn5S0IFbhu48gbsOgeSBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgIOKAlCBD4bqkTSBi4buPLlxuICAgICAgICAgICBgaWdub3JlQ29udGVudDogdHJ1ZWAgYuG6r3QgZ3JpZHN0ZXIgQ0jhu4ggY2hvIGvDqW8ga2hpIGNo4buXIGLhuqVtIG7hurFtIGTGsOG7m2kgbeG7mXQgcGjhuqduIHThu60gY8OzXG4gICAgICAgICAgIGBkcmFnSGFuZGxlQ2xhc3NgLiBLaMO0bmcgY8OzIHbhu48gbsOgeSB0aMOsIGNvbXBvbmVudCBj4bunYSBuxqFpIGTDuW5nICh0aMawIHZp4buHbiBraMO0bmcga2nhu4NtIHNvw6F0XG4gICAgICAgICAgIMSRxrDhu6NjIGNsYXNzIGPhu6dhIG7Dsykgc+G6vSBraMO0bmcgY8OzIHRheSBj4bqnbSBuw6BvLCB2w6AgS0jDlE5HIGto4buRaSBuw6BvIGvDqW8gxJHGsOG7o2Mg4oCUIMSRbyAyOC8wOC8yMDI2OlxuICAgICAgICAgICBrw6lvIDMyMHB4LCB0b+G6oSDEkeG7mSBraOG7kWkgZ2nhu68gbmd1ecOqbiB4PTMseT0wLlxuICAgICAgICAgICBUw6puIGNsYXNzIHRoZW8gY+G6pXA6IGzGsOG7m2kgdHJhbmcgdsOgIGzGsOG7m2kgbOG7k25nIGTDuW5nIGhhaSB0w6puIEtIw4FDIG5oYXUsIG7hur91IGtow7RuZyBsxrDhu5tpIGNoYVxuICAgICAgICAgICBjxaluZyBuaOG6rW4gcmEgdGF5IGPhuqdtIGPhu6dhIGto4buRaSBjb24gdsOgIG5o4bqlYyBsdcO0biBjb250YWluZXIuIC0tPlxuICAgICAgPCEtLSDwn5S0IFbhu48gS0jDlE5HIG1hbmcgYGRyYWdIYW5kbGVDbGFzc2Ag4oCUIEPhuqRNIHRow6ptIGzhuqFpLlxuICAgICAgICAgICBLw6lvIGNo4buJIMSRxrDhu6NjIHBow6lwIGLhuq90IMSR4bqndSB04burIEThuqJJIFhBTkggKG5nxrDhu51pIGTDuW5nIGNo4buRdCAyOC8wOC8yMDI2OiBcInBo4bqjaSBob3ZlciB2w6BvIHbDuW5nXG4gICAgICAgICAgIG3DoHUgeGFuaCB0aMOsIG3hu5tpIGvDqW8gdGjhuqMgxJHGsOG7o2MgY2hvIMSR4buTbmcgbmjhuqV0XCIpLiBDaG8gY+G6oyB24buPIGzDoG0gdGF5IGPhuqdtIHRow6wgYuG6pW0gxJHDonUgY8WpbmdcbiAgICAgICAgICAga8OpbywgdsOgIGto4buRaSBjb24gbuG6sW0gdHJvbmcgY29udGFpbmVyIHPhur0gbmjhuqVjIGx1w7RuIGNvbnRhaW5lciB2w6wgbMaw4bubaSBjaGEgZHV54buHdCBsw6puIGfhurdwXG4gICAgICAgICAgIHRheSBj4bqnbSBj4bunYSB24buPIGNvbnRhaW5lciB0csaw4bubYy5cbiAgICAgICAgICAgVuG7jyBraOG7kWkgY29uIHbhuqtuIGdp4buvIGNsYXNzIENI4bq2TiDEkeG7gyBsxrDhu5tpIGNoYSBk4burbmcgxJHDum5nIOG7nyBiacOqbi4gLS0+XG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1ibG9jay1zaGVsbCBncm91cCByZWxhdGl2ZSBoLWZ1bGwgdy1mdWxsIG1pbi13LTBcIlxuICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1ibG9jay1wYXJlbnQtZHJhZ109XCJpc05lc3RlZENhbnZhcygpXCI+XG4gICAgICAgIDwhLS0gROG6o2kga8OpbzogaGnhu4duIGtoaSByw6ogY2h14buZdCB2w6BvIGto4buRaSwgYmEgY2jhuqVtIGzDoCBk4bqldSBoaeG7h3Uga8OpbyBxdWVuIHRodeG7mWMuXG4gICAgICAgICAgICAgTmfGsOG7nWkgZMO5bmcgY+G6p24gbeG7mXQgY2jhu5cgYsOhbSBSw5UgUsOATkcgdGhheSB2w6wgxJFvw6FuIHhlbSBi4bqlbSDEkcOidSB0aMOsIGvDqW8gxJHGsOG7o2NcbiAgICAgICAgICAgICAoY2jhu5F0IDI4LzA4LzIwMjYsIHRoZW8gxJHDum5nIGThuqNpIMSRYW5nIGTDuW5nIOG7nyBtw6BuIEN1c3RvbWVyIDM2MCBj4bunYSBtb2Jpby13ZWIpLiAtLT5cbiAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkpIHtcbiAgICAgICAgICA8IS0tIPCflLQgROG6o2kgUEjhuqJJIG1hbmcgdGF5IGPhuqdtIMSQw5pORyBD4bqkUCBj4bunYSBuw7MuIEThuqNpIG5o4bqtbiBjaHXhu5l0IChgcG9pbnRlci1ldmVudHM6IGF1dG9gKSxcbiAgICAgICAgICAgICAgIG7Dqm4gbuG6v3Uga2jDtG5nIG1hbmcgdGF5IGPhuqdtIHRow6wgYGNoZWNrRHJhZ0hhbmRsZUNsYXNzYCBkdXnhu4d0IGzDqm4gZ+G6t3AgdGF5IGPhuqdtIGPhu6dhXG4gICAgICAgICAgICAgICBjb250YWluZXIgdsOgIG5o4bqlYyBjb250YWluZXIgdGhheSB2w6wga2jhu5FpIGNvbiAoxJFvIDI4LzA4LzIwMjY6IGvDqW8gZOG6o2kga2jhu5FpIGNvbixcbiAgICAgICAgICAgICAgIGNvbnRhaW5lciBuaOG6o3kgMCwwIOKGkiAwLDEgY8OybiBraOG7kWkgY29uIMSR4bupbmcgaW0pLlxuICAgICAgICAgICAgICAg8J+UtCBE4bqjaSBDSOG7iCBtYW5nIHRheSBj4bqnbSwgQ+G6pE0gbWFuZyB0aMOqbSBjbGFzcyBjaOG6t246IGBjaGVja0RyYWdIYW5kbGVDbGFzc2AgeMOpdCBoYWlcbiAgICAgICAgICAgICAgIGNsYXNzIMSRw7MgdHLDqm4gQ8OZTkcgbeG7mXQgbm9kZSBuw6puIMSR4buDIGNodW5nIGzDoCBrw6lvIGjhu49uZyAoxJFvIDI4LzA4LzIwMjY6IGLhuqVtIGThuqNpIGto4buRaVxuICAgICAgICAgICAgICAgY29uIGvDqW8gMTQwcHgsIGto4buRaSDEkeG7qW5nIGltKS4gVmnhu4djIGNo4bq3biBsxrDhu5tpIGNoYSBkbyBW4buOIEtI4buQSSBsby4gLS0+XG4gICAgICAgICAgPGRpdlxuICAgICAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyXCJcbiAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyLWNoaWxkXT1cImlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1oYW5kbGVdPVwiIWlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1oYW5kbGUtY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyLWRvdHNcIj5cbiAgICAgICAgICAgICAgQGZvciAoZG90IG9mIGRyYWdCYXJEb3RzOyB0cmFjayBkb3QpIHtcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXItZG90XCI+PC9zcGFuPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cblxuICAgICAgICA8IS0tIEtodW5nIGNo4bupYSBjb21wb25lbnQgY+G7p2EgbsahaSBkw7luZy5cbiAgICAgICAgICAgICDwn5S0IFBI4bqiSSBjw7Mga2h1bmcgdGjhuq10IChraMO0bmcgcGjhuqNpIGBuZy1jb250YWluZXJgIHRy4buRbmcpOiBuw7MgbG8gaOG7mSBuxqFpIGTDuW5nIHBo4bqnbiBs4bqlcCDEkeG6p3lcbiAgICAgICAgICAgICDDtCB2w6Agdmnhu4FuIGLDoW8gY2jhur8gxJHhu5kgc+G7rWEuIEtow7RuZyBjw7Mga2h1bmcgdGjDrCBt4buXaSBjb21wb25lbnQgbuG7mWkgZHVuZyBs4bqhaSBwaOG6o2kgdOG7sSB2aeG6v3RcbiAgICAgICAgICAgICBgOmhvc3QgeyB3aWR0aC9oZWlnaHQ6IDEwMCUgfWAgKyB2aeG7gW4gxJHhu6l0IOKAlCDEkcOzIGzDoCB2aeG7h2MgY+G7p2EgdGjGsCB2aeG7h24sIGtow7RuZyBwaOG6o2kgY+G7p2FcbiAgICAgICAgICAgICBuZ8aw4budaSBkw7luZyAobmfGsOG7nWkgZMO5bmcgY2jhu5F0IDI4LzA4LzIwMjYpLlxuICAgICAgICAgICAgIE3hu5dpIGto4buRaSBt4buZdCBWaWV3Q29udGFpbmVyUmVmIHJpw6puZyBuw6puIGNvbXBvbmVudCBu4bqxbSDEkcO6bmcgw7QgY+G7p2EgbsOzLiAtLT5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1jb250ZW50LWZyYW1lXCJcbiAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1jb250ZW50LWZyYW1lLWVkaXRdPVwiaXNFZGl0TW9kZSgpICYmICFpc0NvbnRhaW5lcihpdGVtKVwiXG4gICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGVudC1mcmFtZS1lbXB0eV09XCJpc0NvbnRhaW5lcihpdGVtKVwiPlxuICAgICAgICAgIDxuZy1jb250YWluZXIgI2l0ZW1Ib3N0IC8+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDwhLS0gROG6pXUgaGnhu4d1IENPTlRBSU5FUjogbmjDo24gZ8OzYyB0csOqbi1waOG6o2kgKyB2aeG7gW4gxJHhu6l0IGJhbyBydeG7mXQuIEtow7RuZyBjw7MgZOG6pXUgaGnhu4d1IHRow6xcbiAgICAgICAgICAgICBuZ8aw4budaSBkw7luZyBraMO0bmcgYmnhur90IGto4buRaSBuw6BvIGNo4bupYSDEkcaw4bujYyBraOG7kWkga2jDoWMgKGNo4buRdCAyOC8wOC8yMDI2LCB0aGVvIMSRw7puZyBjw6FjaFxuICAgICAgICAgICAgIG3DoG4gQ3VzdG9tZXIgMzYwIGPhu6dhIG1vYmlvLXdlYiDEkWFuZyBsw6BtKS4gLS0+XG4gICAgICAgIEBpZiAoaXNFZGl0TW9kZSgpICYmIGlzQ29udGFpbmVyKGl0ZW0pKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGFpbmVyLWxhYmVsXCI+e3sgJ2kxOG5fY29udGFpbmVyX2Jsb2NrJyB8IHRyYW5zbGF0ZSB9fTwvZGl2PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRhaW5lci1ib3JkZXJcIj48L2Rpdj5cbiAgICAgICAgfVxuXG4gICAgICAgIDwhLS0gVGhhbmggY8O0bmcgY+G7pTogaGnhu4duIGtoaSByw6ogY2h14buZdCB2w6BvIGto4buRaS5cbiAgICAgICAgICAgICDwn5S0IETDuW5nIGBsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25gIOKAlCBD4bqkTSB04buxIGThu7FuZyB0aOG6uyBgPGJ1dHRvbj5gLlxuICAgICAgICAgICAgIENvbXBvbmVudCBuw6B5IMSRw6MgY8OzIHPhurVuIGtp4buDdSBuw7p0LCBj4buhIG7DunQgdsOgIGJvbmcgYsOzbmcgY2jDuiB0aMOtY2ggdGhlbyDEkcO6bmcgYuG7mSBnaWFvIGRp4buHblxuICAgICAgICAgICAgIGPhu6dhIHPhuqNuIHBo4bqpbTsgdOG7sSBk4buxbmcgbMOgIG3hu5dpIG7GoWkgbeG7mXQga2nhu4N1IHbDoCBt4bqldCBsdcO0biB0b29sdGlwLiAtLT5cbiAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkgJiYgIWhpZGRlblRvb2xiYXIoKSkge1xuICAgICAgICAgIEBpZiAodGVtcGxhdGVUb29sYmFyKCkpIHtcbiAgICAgICAgICAgIDwhLS0gTsahaSBkw7luZyB04buxIHbhur0gdGhhbmggY8O0bmcgY+G7pTogbmjhuq1uIGto4buRaSArIGPDoWMgaMOgbSBwaMOhdCBldmVudCBxdWEgY29udGV4dC5cblxuICAgICAgICAgICAgICAgICDwn5S0IEto4buRaSBi4buNYyB04buxIGNo4bq3biBgbW91c2Vkb3duYC9gY2xpY2tgIG7hu5VpIGzDqm4gZ3JpZHN0ZXIuIFRoYW5oIGPDtG5nIGPhu6UgbuG6sW0gdHJvbmdcbiAgICAgICAgICAgICAgICAgdsO5bmcgbWFuZyBgZHJhZ0hhbmRsZUNsYXNzYDsga2jDtG5nIGNo4bq3biB0aMOsIGPDuiBi4bqlbSBi4buLIGhp4buDdSBsw6AgYuG6r3QgxJHhuqd1IGvDqW8gdsOgXG4gICAgICAgICAgICAgICAgIGBkZWxheVN0YXJ0OiAxNjBgIG514buRdCBsdcO0biBgY2xpY2tgIOKAlCBuw7p0IHRyb25nIHRlbXBsYXRlIG5nb8OgaSBi4bqlbSBraMO0bmcgxINuXG4gICAgICAgICAgICAgICAgICjEkW8gMDkvMDkvMjAyNjogYuG6pW0gYuG6sW5nIGAuY2xpY2soKWAgY+G7p2EgRE9NIHRow6wgbeG7nywgYuG6pW0gY2h14buZdCB0aOG6rXQgdGjDrCBraMO0bmcpLlxuICAgICAgICAgICAgICAgICBDaOG6t24g4bufIMSQw4JZIMSR4buDIG7GoWkgZMO5bmcga2jDtG5nIHBo4bqjaSBiaeG6v3QgYuG6q3kgbsOgeS4gLS0+XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC10b29sYmFyXCJcbiAgICAgICAgICAgICAgcm9sZT1cInRvb2xiYXJcIlxuICAgICAgICAgICAgICB0YWJpbmRleD1cIjBcIlxuICAgICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC10b29sYmFyLWNoaWxkXT1cImlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgICAobW91c2Vkb3duKT1cImhhbmRsZXJTdG9wRXZlbnQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChrZXlkb3duKT1cImhhbmRsZXJTdG9wRXZlbnQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJoYW5kbGVyU3RvcEV2ZW50KCRldmVudClcIj5cbiAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgICAgICpuZ1RlbXBsYXRlT3V0bGV0PVwidGVtcGxhdGVUb29sYmFyKCkhOyBjb250ZXh0OiB0b29sYmFyQ29udGV4dHMoKVtpdGVtSW5kZXhdXCIgLz5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtdG9vbGJhclwiXG4gICAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZGltLWJhZGdlXCI+XG4gICAgICAgICAgICAgICAge3sgaXRlbS5yb3dzIH19IGjDoG5nIMOXIHt7IGl0ZW0uY29scyB9fSBj4buZdFxuICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgIEBpZiAoY2FuQWRkSW5zaWRlKGl0ZW0pKSB7XG4gICAgICAgICAgICAgICAgPGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvblxuICAgICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZCdcIlxuICAgICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIFtzaXplQnV0dG9uXT1cIidzbWFsbGVyJ1wiXG4gICAgICAgICAgICAgICAgICBbY2xhc3NJY29uTGVmdF09XCInbGlicy11aS1pY29uLWFkZCdcIlxuICAgICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogJ1Row6ptIGto4buRaSB2w6BvIHRyb25nJywgekluZGV4OiAxMzAwIH0gfVwiXG4gICAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlckFkZEluc2lkZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBAaWYgKGNhblRvZ2dsZVR5cGUoaXRlbSkpIHtcbiAgICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgICBbdHlwZV09XCInYnV0dG9uLXRoaXJkJ1wiXG4gICAgICAgICAgICAgICAgICBbaWNvbk9ubHlUeXBlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgW3NpemVCdXR0b25dPVwiJ3NtYWxsZXInXCJcbiAgICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cImlzQ29udGFpbmVyKGl0ZW0pID8gJ2xpYnMtdWktaWNvbi1zcGxpdC1jZWxsJyA6ICdsaWJzLXVpLWljb24tbWVyZ2UtY2VsbCdcIlxuICAgICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogaXNDb250YWluZXIoaXRlbSkgPyAnxJDGsGEgduG7gSBraOG7kWkgxJHGoW4nIDogJ0NodXnhu4NuIHRow6BuaCBraOG7kWkgZ2jDqXAnLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyVG9nZ2xlVHlwZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZCdcIlxuICAgICAgICAgICAgICAgIFtpY29uT25seVR5cGVdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgW3NpemVCdXR0b25dPVwiJ3NtYWxsZXInXCJcbiAgICAgICAgICAgICAgICBbY2xhc3NJY29uTGVmdF09XCInbGlicy11aS1pY29uLXNldHRpbmcnXCJcbiAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiAnQ+G6pXUgaMOsbmggdGjhursnLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlckNvbmZpZygkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgICAgPGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvblxuICAgICAgICAgICAgICAgIFt0eXBlXT1cIididXR0b24tdGhpcmQtaG92ZXItZGFuZ2VyJ1wiXG4gICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tcmVtb3ZlJ1wiXG4gICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogaXNDb250YWluZXIoaXRlbSkgPyAnWG/DoSBj4bqjIGto4buRaSBnaMOpcCcgOiAnWG/DoSBraOG7kWknLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlclJlbW92ZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIEBpZiAoaXRlbS5jaGlsZHJlbiAmJiBpdGVtLmNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICA8IS0tIEto4buRaSBjw7MgY29uIOKGkiBydeG7mXQgbsOzIGzhuqFpIGzDoCBt4buZdCBt4bq3dCBwaOG6s25nIG7hu69hLiDEkOG7hyBxdXkg4bufIMSRw6J5LCBuxqFpIGTDuW5nIGtow7RuZyBwaOG6o2kgbG8uXG4gICAgICAgICAgICAgICBM4buBIMSR4bq3dCDhu58gxJDDglkgKHbhu48gY29udGFpbmVyKSwga2jDtG5nIMSR4bq3dCB2w6BvIGzGsOG7m2kgY29uIOKAlCBsxrDhu5tpIGNvbiBnaeG7ryBuZ3V5w6puIGtoZSAycHguIC0tPlxuICAgICAgICAgIDxsaWJzX3VpLXNlcnZpY2VzLWdyaWRfbGF5b3V0LWNhbnZhc1xuICAgICAgICAgICAgY2xhc3M9XCJibG9jayBoLWZ1bGwgdy1mdWxsXCJcbiAgICAgICAgICAgIFtzdHlsZS5wYWRkaW5nXT1cImNvbnRhaW5lclBhZGRpbmcoKVwiXG4gICAgICAgICAgICBbbm9kZV09XCJpdGVtXCJcbiAgICAgICAgICAgIFtkZXB0aF09XCJkZXB0aCgpICsgMVwiXG4gICAgICAgICAgICBbaGlkZGVuVG9vbGJhcl09XCJoaWRkZW5Ub29sYmFyKClcIlxuICAgICAgICAgICAgW3RlbXBsYXRlVG9vbGJhcl09XCJ0ZW1wbGF0ZVRvb2xiYXIoKVwiXG4gICAgICAgICAgICBbc2VsZWN0ZWROb2RlSWRdPVwic2VsZWN0ZWROb2RlSWQoKVwiXG4gICAgICAgICAgICAob3V0U2VsZWN0Tm9kZSk9XCJvdXRTZWxlY3ROb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAob3V0Q29uZmlnTm9kZSk9XCJvdXRDb25maWdOb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAob3V0Q2hhbmdlKT1cIm91dENoYW5nZS5lbWl0KCRldmVudClcIlxuICAgICAgICAgICAgKG91dFJlbW92ZU5vZGUpPVwib3V0UmVtb3ZlTm9kZS5lbWl0KCRldmVudClcIlxuICAgICAgICAgICAgKG91dEFkZEJsb2NrSW5zaWRlKT1cIm91dEFkZEJsb2NrSW5zaWRlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAob3V0VG9nZ2xlVHlwZSk9XCJvdXRUb2dnbGVUeXBlLmVtaXQoJGV2ZW50KVwiIC8+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIDwvZ3JpZHN0ZXItaXRlbT5cbiAgfVxuICAgIDwvZ3JpZHN0ZXI+XG4gIDwvZGl2PlxufVxuIl19
|