@libs-ui/services-grid-layout 0.2.357-19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -0
- package/canvas/grid-layout-canvas.component.d.ts +107 -0
- package/defines/gridster-config.define.d.ts +21 -0
- package/esm2022/canvas/grid-layout-canvas.component.mjs +193 -0
- package/esm2022/defines/gridster-config.define.mjs +123 -0
- package/esm2022/grid-layout.service.mjs +433 -0
- package/esm2022/index.mjs +10 -0
- package/esm2022/interfaces/grid-layout.interface.mjs +2 -0
- package/esm2022/libs-ui-services-grid-layout.mjs +5 -0
- package/esm2022/tree.service.mjs +180 -0
- package/fesm2022/libs-ui-services-grid-layout.mjs +933 -0
- package/fesm2022/libs-ui-services-grid-layout.mjs.map +1 -0
- package/grid-layout.service.d.ts +157 -0
- package/index.d.ts +3 -0
- package/interfaces/grid-layout.interface.d.ts +171 -0
- package/package.json +29 -0
- package/tree.service.d.ts +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# @libs-ui/services-grid-layout
|
|
2
|
+
|
|
3
|
+
Mặt phẳng kéo–thả. Thư viện lo toàn bộ việc **vẽ lưới, kéo thả, đổi kích thước, lồng container**;
|
|
4
|
+
nơi dùng chỉ đưa **component nội dung** và **cây dữ liệu**, thư viện tự gắn component lên từng khối.
|
|
5
|
+
|
|
6
|
+
Cùng mô hình với `@libs-ui/services-diagram-draw`: truyền `nodeComponentType`, thư viện `createComponent`.
|
|
7
|
+
|
|
8
|
+
## Cài
|
|
9
|
+
|
|
10
|
+
Lib dùng `angular-gridster2` qua **alias npm** để chạy song song nhiều bản (giống cách repo đang chạy
|
|
11
|
+
`quill` 1.3.7 và `quill2x` 2.0.3):
|
|
12
|
+
|
|
13
|
+
```jsonc
|
|
14
|
+
// package.json của nơi dùng
|
|
15
|
+
"gridster18": "npm:angular-gridster2@18.0.1"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Nơi dùng đang có `angular-gridster2` bản khác thì **không bị đụng** — hai bản nằm ở hai thư mục
|
|
19
|
+
`node_modules` riêng.
|
|
20
|
+
|
|
21
|
+
## Dùng
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
@Component({
|
|
25
|
+
imports: [LibsUiGridLayoutCanvasComponent],
|
|
26
|
+
// 🔴 Khai trong providers của CHÍNH component — service theo từng mặt phẳng, không phải root.
|
|
27
|
+
providers: [LibsUiGridLayoutService, LibsUiGridLayoutTreeService],
|
|
28
|
+
template: `<libs_ui-services-grid_layout-canvas [node]="root()" (outChange)="save($event)" />`,
|
|
29
|
+
})
|
|
30
|
+
export class MyPageComponent implements OnDestroy {
|
|
31
|
+
private readonly gridLayout = inject(LibsUiGridLayoutService);
|
|
32
|
+
|
|
33
|
+
protected readonly root = signal<T_gridLayout_node>(initialLayout());
|
|
34
|
+
|
|
35
|
+
constructor() {
|
|
36
|
+
this.gridLayout.init({
|
|
37
|
+
nodeComponentType: MyCardComponent, // component nội dung của BẠN
|
|
38
|
+
data: this.root(),
|
|
39
|
+
mode: E_gridLayout_mode_string.grid,
|
|
40
|
+
isEditMode: true,
|
|
41
|
+
allowOverlap: false,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ngOnDestroy(): void {
|
|
46
|
+
this.gridLayout.destroy(); // 🔴 BẮT BUỘC, nếu không là rò rỉ bộ nhớ
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Component nội dung **bắt buộc** khai đủ ba input — thư viện `setInput` đúng ba tên này:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
@Component({ changeDetection: ChangeDetectionStrategy.OnPush, /* … */ })
|
|
55
|
+
export class MyCardComponent {
|
|
56
|
+
readonly node = input.required<T_gridLayout_node>();
|
|
57
|
+
readonly isEditMode = input<boolean>(false);
|
|
58
|
+
readonly depth = input<number>(0);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Hai chế độ
|
|
63
|
+
|
|
64
|
+
| Chế độ | Khối bám lưới | Chồng lên nhau | Dùng khi |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| `luoi` | có | chỉ khi `allowOverlap` bật **và** hai khối khác `layerIndex` | dashboard, bố cục dạng bảng |
|
|
67
|
+
| `tuDo` | không, toạ độ px | tự do | dựng bố cục kiểu canvas |
|
|
68
|
+
|
|
69
|
+
## API
|
|
70
|
+
|
|
71
|
+
**`LibsUiGridLayoutService`** (một thực thể cho mỗi mặt phẳng)
|
|
72
|
+
|
|
73
|
+
| Thành viên | Việc |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `init(config)` | dựng mặt phẳng; gọi lại sẽ dọn sạch rồi dựng lại |
|
|
76
|
+
| `setEditMode(bat)` | bật/tắt chế độ sửa |
|
|
77
|
+
| `refresh(event?)` | báo cây vừa đổi từ bên ngoài |
|
|
78
|
+
| `destroy()` | dọn mọi component đã dựng — gọi trong `ngOnDestroy` |
|
|
79
|
+
| `Root` · `IsEditMode` · `Version` | signal chỉ đọc |
|
|
80
|
+
|
|
81
|
+
**`LibsUiGridLayoutTreeService`** — thao tác trên cây
|
|
82
|
+
|
|
83
|
+
`findById` · `findParent` · `addChild` · `removeById` · `canBecomeContainer` ·
|
|
84
|
+
`flattenOverDepthContainers` · `compact`
|
|
85
|
+
|
|
86
|
+
## Vì sao cấu hình gridster lại như vậy
|
|
87
|
+
|
|
88
|
+
`defines/gridster-config.define.ts` giữ toàn bộ cấu hình đã vá. Mỗi dòng có dấu 🔴 tương ứng một lỗi
|
|
89
|
+
đã đo được trên sản phẩm thật — đổi nó là lỗi sống lại:
|
|
90
|
+
|
|
91
|
+
| Cấu hình | Bỏ đi thì gặp lỗi gì |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `relative` trên khối cuộn | `offsetParent` sai → gridster tính ô lệch ~49px, ô chờ đặt không theo con trỏ |
|
|
94
|
+
| `compactType: None` | chỗ vừa nhường bị hút lấp lại → kéo mép bị hoàn nguyên, khối kéo treo lơ lửng |
|
|
95
|
+
| `pushItems: true` | khối lệch kích thước không swap được, cũng không nhường đường → bế tắc |
|
|
96
|
+
| `delayStart: 160` | bấm chọn một khối là nó nhích theo dù chuột đứng yên |
|
|
97
|
+
| `dragHandleClass` riêng cho lưới con | kéo khối con nhấc luôn container |
|
|
98
|
+
| `maxItemRows/Cols/Area` nới | khối bị chặn chiều cao (trần diện tích 2500 khó thấy nhất) |
|
|
99
|
+
| tay cầm resize ở `0`, không âm | `overflow: hidden` cắt mất nửa tay cầm → kéo mép không ăn |
|
|
100
|
+
| `setGridSize: false` ở lưới lồng | vòng lặp đo–ghi, lưới con co dần về 64×64px |
|
|
101
|
+
| `disableScroll*` ở lưới lồng | khối con bị đẩy ra ngoài container rồi kẹt |
|
|
102
|
+
|
|
103
|
+
## Kiểm
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npx nx lint services-grid-layout
|
|
107
|
+
npx nx test services-grid-layout
|
|
108
|
+
npx nx build services-grid-layout
|
|
109
|
+
```
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { OnDestroy } from '@angular/core';
|
|
2
|
+
import { T_gridLayout_actionEvent, T_gridLayout_addEvent, T_gridLayout_changeEvent, T_gridLayout_node } from '../interfaces/grid-layout.interface';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* 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.
|
|
6
|
+
*
|
|
7
|
+
* Nơi dùng KHÔNG dựng component này trực tiếp cho từng khối — chỉ đặt MỘT thẻ ở gốc và truyền
|
|
8
|
+
* `nodeComponentType` qua `LibsUiGridLayoutService.init()`. Thư viện tự gắn component cho mọi khối.
|
|
9
|
+
*/
|
|
10
|
+
export declare class LibsUiGridLayoutCanvasComponent implements OnDestroy {
|
|
11
|
+
/**
|
|
12
|
+
* @internal Chỉ mặt phẳng LỒNG dùng — thư viện tự truyền khi vẽ ruột container.
|
|
13
|
+
*
|
|
14
|
+
* Nơi dùng KHÔNG truyền: mặt phẳng gốc đọc cây từ service (đã đưa vào qua `init()`), truyền thêm
|
|
15
|
+
* ở đây là khai hai lần cùng một thứ (người dùng chốt 28/08/2026).
|
|
16
|
+
*/
|
|
17
|
+
readonly node: import("@angular/core").InputSignal<T_gridLayout_node | undefined>;
|
|
18
|
+
/** @internal Chỉ mặt phẳng LỒNG dùng — thư viện tự truyền. */
|
|
19
|
+
readonly depth: import("@angular/core").InputSignal<number>;
|
|
20
|
+
/**
|
|
21
|
+
* Bố cục vừa đổi do NGƯỜI DÙNG kéo hoặc đổi kích thước khối.
|
|
22
|
+
*
|
|
23
|
+
* Nơi dùng tự gọi `addBlock` · `removeBlock` · `compact` thì KHÔNG bắn — họ vừa gọi thì đã biết,
|
|
24
|
+
* bắn ngược lại là thừa (người dùng chốt 28/08/2026).
|
|
25
|
+
*/
|
|
26
|
+
readonly outChange: import("@angular/core").OutputEmitterRef<T_gridLayout_changeEvent>;
|
|
27
|
+
/**
|
|
28
|
+
* Người dùng bấm nút XOÁ trên một khối. Thư viện CHƯA xoá — gọi `event.confirm()` để xoá thật.
|
|
29
|
+
* Cần hỏi trước thì hỏi xong mới gọi; không cho phép thì đừng gọi.
|
|
30
|
+
*/
|
|
31
|
+
readonly outRemoveNode: import("@angular/core").OutputEmitterRef<T_gridLayout_actionEvent>;
|
|
32
|
+
/**
|
|
33
|
+
* Người dùng bấm nút THÊM trên một container. Thư viện KHÔNG tự thêm — nó không biết bạn có
|
|
34
|
+
* những loại khối nào. Mở menu chọn loại rồi gọi `event.confirm(item)` với khối bạn muốn thêm.
|
|
35
|
+
*/
|
|
36
|
+
readonly outAddBlockInside: import("@angular/core").OutputEmitterRef<T_gridLayout_addEvent>;
|
|
37
|
+
/** 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. */
|
|
38
|
+
readonly outToggleType: import("@angular/core").OutputEmitterRef<T_gridLayout_actionEvent>;
|
|
39
|
+
private readonly gridsterRef;
|
|
40
|
+
/** mỗi ô một chỗ gắn riêng — cùng thứ tự với `children()` */
|
|
41
|
+
private readonly itemHosts;
|
|
42
|
+
/** 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. */
|
|
43
|
+
protected readonly dragBarDots: number[];
|
|
44
|
+
private readonly service;
|
|
45
|
+
/**
|
|
46
|
+
* Node THẬT của mặt phẳng này.
|
|
47
|
+
*
|
|
48
|
+
* 🔴 Mặt phẳng GỐC (depth 0) đọc cây từ SERVICE, không dùng input.
|
|
49
|
+
* Nơi dùng thường sửa cây TẠI CHỖ (`treeService.compact(tree)`) rồi gọi `refresh()`, nên signal
|
|
50
|
+
* `root()` của họ giữ nguyên tham chiếu và input `node` không hề đổi — `@for` không dựng lại
|
|
51
|
+
* `gridster-item`, lưới đứng im dù dữ liệu đã khác (đo 28/08/2026: bấm "Sắp gọn" không tác dụng).
|
|
52
|
+
* `refresh()` của service thay tham chiếu cây, nên đọc từ đó mới thấy thay đổi.
|
|
53
|
+
*
|
|
54
|
+
* Mặt phẳng LỒNG vẫn dùng input: nó nhận node con từ mặt phẳng cha, vốn đã là object mới.
|
|
55
|
+
*/
|
|
56
|
+
protected readonly currentNode: import("@angular/core").Signal<T_gridLayout_node>;
|
|
57
|
+
protected readonly children: import("@angular/core").Signal<T_gridLayout_node[]>;
|
|
58
|
+
protected readonly isNestedCanvas: import("@angular/core").Signal<boolean>;
|
|
59
|
+
/** trần lồng container — khối đã chạm trần thì không cho chuyển thành container */
|
|
60
|
+
protected readonly maxContainerDepth: import("@angular/core").Signal<number>;
|
|
61
|
+
/**
|
|
62
|
+
* Lề trong của container, px.
|
|
63
|
+
*
|
|
64
|
+
* 🔴 Đặt trên VỎ container, không đặt vào lưới con. Lưới con giữ nguyên `margin: 2` như cũ; lề
|
|
65
|
+
* này chỉ co ruột lại khỏi biên vỏ, nhờ đó tay cầm đổi kích thước của khối con luôn nằm trong tầm
|
|
66
|
+
* chuột (người dùng chốt 28/08/2026).
|
|
67
|
+
*/
|
|
68
|
+
protected readonly containerPadding: import("@angular/core").Signal<string>;
|
|
69
|
+
/** Dải kéo chỉ hiện ở chế độ sửa — chế độ xem không kéo được nên hiện dải là gây nhầm. */
|
|
70
|
+
protected readonly isEditMode: import("@angular/core").Signal<boolean>;
|
|
71
|
+
protected readonly gridOptions: import("@angular/core").Signal<import("angular-gridster2").GridsterConfig>;
|
|
72
|
+
constructor();
|
|
73
|
+
ngOnDestroy(): void;
|
|
74
|
+
/** Vẽ lại lưới — gọi khi khung ngoài đổi kích thước hoặc cây vừa đổi diện rộng. */
|
|
75
|
+
get FunctionControl(): {
|
|
76
|
+
veLaiLuoi: () => void | undefined;
|
|
77
|
+
};
|
|
78
|
+
protected handlerTrackNode(_index: number, item: T_gridLayout_node): string;
|
|
79
|
+
/**
|
|
80
|
+
* 🔴 Chặn `mousedown` nổi lên lưới CHA khi thao tác trong lưới con.
|
|
81
|
+
* Không chặn thì cả hai lưới cùng nhận một cú bấm: kéo khối con là nhấc luôn container.
|
|
82
|
+
*/
|
|
83
|
+
/**
|
|
84
|
+
* Chặn `mousedown` của khối trong lưới LỒNG nổi lên lưới cha.
|
|
85
|
+
*
|
|
86
|
+
* Chỉ chặn ở lưới lồng: lưới cấp trang không có cha nào để chặn, và chặn ở đó sẽ nuốt luôn thao
|
|
87
|
+
* tác của chính nó.
|
|
88
|
+
*/
|
|
89
|
+
/**
|
|
90
|
+
* 🔴 Ba handler dưới đây PHẢI `stopPropagation`.
|
|
91
|
+
* Nút nằm trong vùng mang `dragHandleClass`, không chặn thì cú bấm bị gridster hiểu là bắt đầu kéo
|
|
92
|
+
* và `delayStart: 160` nuốt luôn `click` — người dùng thấy như nút chết.
|
|
93
|
+
*/
|
|
94
|
+
protected handlerRemove(event: Event, node: T_gridLayout_node): void;
|
|
95
|
+
protected handlerAddInside(event: Event, node: T_gridLayout_node): void;
|
|
96
|
+
protected handlerToggleType(event: Event, node: T_gridLayout_node): void;
|
|
97
|
+
/** khối này có phải container không (có khối con) */
|
|
98
|
+
protected isContainer(item: T_gridLayout_node): boolean;
|
|
99
|
+
/** container đã chạm trần lồng thì không cho thêm khối vào trong nữa */
|
|
100
|
+
protected canAddInside(item: T_gridLayout_node): boolean;
|
|
101
|
+
/** khối đơn ở cấp chưa chạm trần mới được chuyển thành container */
|
|
102
|
+
protected canToggleType(item: T_gridLayout_node): boolean;
|
|
103
|
+
protected handlerBlockParentDrag(event: Event): void;
|
|
104
|
+
protected handlerMouseDownTrongLuoi(event: Event): void;
|
|
105
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<LibsUiGridLayoutCanvasComponent, never>;
|
|
106
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<LibsUiGridLayoutCanvasComponent, "libs_ui-services-grid_layout-canvas", never, { "node": { "alias": "node"; "required": false; "isSignal": true; }; "depth": { "alias": "depth"; "required": false; "isSignal": true; }; }, { "outChange": "outChange"; "outRemoveNode": "outRemoveNode"; "outAddBlockInside": "outAddBlockInside"; "outToggleType": "outToggleType"; }, never, never, true, never>;
|
|
107
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GridsterConfig } from 'gridster18';
|
|
2
|
+
import { T_gridLayout_node } from '../interfaces/grid-layout.interface';
|
|
3
|
+
/**
|
|
4
|
+
* Cấu hình gridster cho lưới CẤP TRANG.
|
|
5
|
+
*
|
|
6
|
+
* Mọi giá trị dưới đây đều đến từ một lỗi đã đo được trên sản phẩm thật, không phải chọn theo cảm
|
|
7
|
+
* tính. Đổi bất kỳ dòng nào có dấu 🔴 sẽ làm sống lại đúng lỗi ghi trong comment.
|
|
8
|
+
*/
|
|
9
|
+
export declare const gridLayoutRootConfig: () => GridsterConfig;
|
|
10
|
+
/**
|
|
11
|
+
* Cấu hình cho lưới LỒNG (ruột một container).
|
|
12
|
+
*
|
|
13
|
+
* Kế thừa lưới trang rồi ghi đè phần khác biệt. Ba điểm quan trọng nhất: `setGridSize` phải TẮT,
|
|
14
|
+
* tay cầm phải mang TÊN RIÊNG, và tự-cuộn-khi-kéo phải TẮT.
|
|
15
|
+
*/
|
|
16
|
+
export declare const gridLayoutNestedConfig: () => GridsterConfig;
|
|
17
|
+
/**
|
|
18
|
+
* Cây rỗng dùng khi chưa có dữ liệu — mặt phẳng vẽ ra một lưới trống thay vì vỡ.
|
|
19
|
+
* Xảy ra trong nhịp giữa lúc component dựng và lúc `init()` chạy.
|
|
20
|
+
*/
|
|
21
|
+
export declare const emptyRootNode: () => T_gridLayout_node;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, ViewContainerRef, computed, inject, input, output, effect, viewChild, viewChildren, } from '@angular/core';
|
|
2
|
+
import { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';
|
|
3
|
+
import { GridsterComponent, GridsterModule } from 'gridster18';
|
|
4
|
+
import { emptyRootNode } from '../defines/gridster-config.define';
|
|
5
|
+
import { LibsUiGridLayoutService } from '../grid-layout.service';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
import * as i1 from "gridster18";
|
|
8
|
+
/**
|
|
9
|
+
* 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
|
+
*
|
|
11
|
+
* Nơi dùng KHÔNG dựng component này trực tiếp cho từng khối — chỉ đặt MỘT thẻ ở gốc và truyền
|
|
12
|
+
* `nodeComponentType` qua `LibsUiGridLayoutService.init()`. Thư viện tự gắn component cho mọi khối.
|
|
13
|
+
*/
|
|
14
|
+
export class LibsUiGridLayoutCanvasComponent {
|
|
15
|
+
/**
|
|
16
|
+
* @internal Chỉ mặt phẳng LỒNG dùng — thư viện tự truyền khi vẽ ruột container.
|
|
17
|
+
*
|
|
18
|
+
* Nơi dùng KHÔNG truyền: mặt phẳng gốc đọc cây từ service (đã đưa vào qua `init()`), truyền thêm
|
|
19
|
+
* ở đây là khai hai lần cùng một thứ (người dùng chốt 28/08/2026).
|
|
20
|
+
*/
|
|
21
|
+
node = input();
|
|
22
|
+
/** @internal Chỉ mặt phẳng LỒNG dùng — thư viện tự truyền. */
|
|
23
|
+
depth = input(0);
|
|
24
|
+
/**
|
|
25
|
+
* Bố cục vừa đổi do NGƯỜI DÙNG kéo hoặc đổi kích thước khối.
|
|
26
|
+
*
|
|
27
|
+
* Nơi dùng tự gọi `addBlock` · `removeBlock` · `compact` thì KHÔNG bắn — họ vừa gọi thì đã biết,
|
|
28
|
+
* bắn ngược lại là thừa (người dùng chốt 28/08/2026).
|
|
29
|
+
*/
|
|
30
|
+
outChange = output();
|
|
31
|
+
/**
|
|
32
|
+
* Người dùng bấm nút XOÁ trên một khối. Thư viện CHƯA xoá — gọi `event.confirm()` để xoá thật.
|
|
33
|
+
* Cần hỏi trước thì hỏi xong mới gọi; không cho phép thì đừng gọi.
|
|
34
|
+
*/
|
|
35
|
+
outRemoveNode = output();
|
|
36
|
+
/**
|
|
37
|
+
* Người dùng bấm nút THÊM trên một container. Thư viện KHÔNG tự thêm — nó không biết bạn có
|
|
38
|
+
* những loại khối nào. Mở menu chọn loại rồi gọi `event.confirm(item)` với khối bạn muốn thêm.
|
|
39
|
+
*/
|
|
40
|
+
outAddBlockInside = output();
|
|
41
|
+
/** 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
|
+
outToggleType = output();
|
|
43
|
+
gridsterRef = viewChild(GridsterComponent);
|
|
44
|
+
/** mỗi ô một chỗ gắn riêng — cùng thứ tự với `children()` */
|
|
45
|
+
itemHosts = viewChildren('itemHost', { read: ViewContainerRef });
|
|
46
|
+
/** 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
|
+
dragBarDots = [0, 1, 2, 3, 4, 5];
|
|
48
|
+
service = inject(LibsUiGridLayoutService);
|
|
49
|
+
/**
|
|
50
|
+
* Node THẬT của mặt phẳng này.
|
|
51
|
+
*
|
|
52
|
+
* 🔴 Mặt phẳng GỐC (depth 0) đọc cây từ SERVICE, không dùng input.
|
|
53
|
+
* Nơi dùng thường sửa cây TẠI CHỖ (`treeService.compact(tree)`) rồi gọi `refresh()`, nên signal
|
|
54
|
+
* `root()` của họ giữ nguyên tham chiếu và input `node` không hề đổi — `@for` không dựng lại
|
|
55
|
+
* `gridster-item`, lưới đứng im dù dữ liệu đã khác (đo 28/08/2026: bấm "Sắp gọn" không tác dụng).
|
|
56
|
+
* `refresh()` của service thay tham chiếu cây, nên đọc từ đó mới thấy thay đổi.
|
|
57
|
+
*
|
|
58
|
+
* Mặt phẳng LỒNG vẫn dùng input: nó nhận node con từ mặt phẳng cha, vốn đã là object mới.
|
|
59
|
+
*/
|
|
60
|
+
currentNode = computed(() => {
|
|
61
|
+
this.service.Version();
|
|
62
|
+
if (this.depth() > 0) {
|
|
63
|
+
return this.node() ?? emptyRootNode();
|
|
64
|
+
}
|
|
65
|
+
return this.service.Root() ?? emptyRootNode();
|
|
66
|
+
});
|
|
67
|
+
children = computed(() => this.currentNode().children ?? []);
|
|
68
|
+
isNestedCanvas = computed(() => this.depth() > 0);
|
|
69
|
+
/** trần lồng container — khối đã chạm trần thì không cho chuyển thành container */
|
|
70
|
+
maxContainerDepth = computed(() => this.service.FunctionsControl.maxContainerDepth());
|
|
71
|
+
/**
|
|
72
|
+
* Lề trong của container, px.
|
|
73
|
+
*
|
|
74
|
+
* 🔴 Đặt trên VỎ container, không đặt vào lưới con. Lưới con giữ nguyên `margin: 2` như cũ; lề
|
|
75
|
+
* này chỉ co ruột lại khỏi biên vỏ, nhờ đó tay cầm đổi kích thước của khối con luôn nằm trong tầm
|
|
76
|
+
* chuột (người dùng chốt 28/08/2026).
|
|
77
|
+
*/
|
|
78
|
+
containerPadding = computed(() => `${this.service.FunctionsControl.containerPadding()}px`);
|
|
79
|
+
/** Dải kéo chỉ hiện ở chế độ sửa — chế độ xem không kéo được nên hiện dải là gây nhầm. */
|
|
80
|
+
isEditMode = computed(() => this.service.IsEditMode());
|
|
81
|
+
gridOptions = computed(() => {
|
|
82
|
+
// Đọc `Version` để `init()` lại (đổi chế độ, bật/tắt chồng khối) sinh ra options MỚI —
|
|
83
|
+
// gridster chỉ áp dụng cấu hình khi tham chiếu `options` đổi.
|
|
84
|
+
this.service.Version();
|
|
85
|
+
return this.service.FunctionsControl.gridsterOptions(this.depth(), () => this.outChange.emit({ root: this.currentNode() }));
|
|
86
|
+
});
|
|
87
|
+
constructor() {
|
|
88
|
+
// Gắn component của nơi dùng vào từng ô, và gắn LẠI mỗi khi cây đổi.
|
|
89
|
+
// Dùng effect vì `itemHosts()` chỉ có giá trị sau khi Angular dựng xong @for.
|
|
90
|
+
effect(() => {
|
|
91
|
+
const hosts = this.itemHosts();
|
|
92
|
+
const items = this.children();
|
|
93
|
+
items.forEach((item, i) => {
|
|
94
|
+
const host = hosts[i];
|
|
95
|
+
if (!host) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// Khối có con là CONTAINER: ruột nó do mặt phẳng lồng vẽ, không gắn component nội dung.
|
|
99
|
+
if ((item.children?.length ?? 0) > 0) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
this.service.FunctionsControl.attachNode(item, this.depth() + 1, host);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
ngOnDestroy() {
|
|
107
|
+
for (const item of this.children()) {
|
|
108
|
+
this.service.FunctionsControl.detachNode(item.id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Vẽ lại lưới — gọi khi khung ngoài đổi kích thước hoặc cây vừa đổi diện rộng. */
|
|
112
|
+
get FunctionControl() {
|
|
113
|
+
return {
|
|
114
|
+
veLaiLuoi: () => this.gridsterRef()?.optionsChanged(),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
handlerTrackNode(_index, item) {
|
|
118
|
+
return item.id;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 🔴 Chặn `mousedown` nổi lên lưới CHA khi thao tác trong lưới con.
|
|
122
|
+
* Không chặn thì cả hai lưới cùng nhận một cú bấm: kéo khối con là nhấc luôn container.
|
|
123
|
+
*/
|
|
124
|
+
/**
|
|
125
|
+
* Chặn `mousedown` của khối trong lưới LỒNG nổi lên lưới cha.
|
|
126
|
+
*
|
|
127
|
+
* Chỉ chặn ở lưới lồng: lưới cấp trang không có cha nào để chặn, và chặn ở đó sẽ nuốt luôn thao
|
|
128
|
+
* tác của chính nó.
|
|
129
|
+
*/
|
|
130
|
+
/**
|
|
131
|
+
* 🔴 Ba handler dưới đây PHẢI `stopPropagation`.
|
|
132
|
+
* Nút nằm trong vùng mang `dragHandleClass`, không chặn thì cú bấm bị gridster hiểu là bắt đầu kéo
|
|
133
|
+
* và `delayStart: 160` nuốt luôn `click` — người dùng thấy như nút chết.
|
|
134
|
+
*/
|
|
135
|
+
handlerRemove(event, node) {
|
|
136
|
+
event.stopPropagation();
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
this.outRemoveNode.emit({ nodeId: node.id, node, confirm: () => this.service.removeBlock(node.id) });
|
|
139
|
+
}
|
|
140
|
+
handlerAddInside(event, node) {
|
|
141
|
+
event.stopPropagation();
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
this.outAddBlockInside.emit({
|
|
144
|
+
nodeId: node.id,
|
|
145
|
+
node,
|
|
146
|
+
confirm: (item) => this.service.addBlock(item, node.id),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
handlerToggleType(event, node) {
|
|
150
|
+
event.stopPropagation();
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
this.outToggleType.emit({
|
|
153
|
+
nodeId: node.id,
|
|
154
|
+
node,
|
|
155
|
+
// Gộp hay tách suy ra từ chính khối: đang là container thì tách, không thì gộp.
|
|
156
|
+
confirm: () => (this.isContainer(node) ? this.service.toSingle(node.id) : this.service.toContainer(node.id)),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/** khối này có phải container không (có khối con) */
|
|
160
|
+
isContainer(item) {
|
|
161
|
+
return (item.children?.length ?? 0) > 0;
|
|
162
|
+
}
|
|
163
|
+
/** container đã chạm trần lồng thì không cho thêm khối vào trong nữa */
|
|
164
|
+
canAddInside(item) {
|
|
165
|
+
return this.isContainer(item) && this.depth() < this.maxContainerDepth();
|
|
166
|
+
}
|
|
167
|
+
/** khối đơn ở cấp chưa chạm trần mới được chuyển thành container */
|
|
168
|
+
canToggleType(item) {
|
|
169
|
+
if (this.isContainer(item)) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
return this.depth() < this.maxContainerDepth();
|
|
173
|
+
}
|
|
174
|
+
handlerBlockParentDrag(event) {
|
|
175
|
+
if (!this.isNestedCanvas()) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
event.stopPropagation();
|
|
179
|
+
}
|
|
180
|
+
handlerMouseDownTrongLuoi(event) {
|
|
181
|
+
if (this.depth() === 0) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
event.stopPropagation();
|
|
185
|
+
}
|
|
186
|
+
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 });
|
|
188
|
+
}
|
|
189
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiGridLayoutCanvasComponent, decorators: [{
|
|
190
|
+
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC1sYXlvdXQtY2FudmFzLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvc2VydmljZXMvZ3JpZC1sYXlvdXQvc3JjL2NhbnZhcy9ncmlkLWxheW91dC1jYW52YXMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy11aS9zZXJ2aWNlcy9ncmlkLWxheW91dC9zcmMvY2FudmFzL2dyaWQtbGF5b3V0LWNhbnZhcy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFFVCxnQkFBZ0IsRUFDaEIsUUFBUSxFQUNSLE1BQU0sRUFDTixLQUFLLEVBQ0wsTUFBTSxFQUNOLE1BQU0sRUFDTixTQUFTLEVBQ1QsWUFBWSxHQUNiLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzVGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxjQUFjLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDL0QsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ2xFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLHdCQUF3QixDQUFDOzs7QUFTakU7Ozs7O0dBS0c7QUFTSCxNQUFNLE9BQU8sK0JBQStCO0lBQzFDOzs7OztPQUtHO0lBQ00sSUFBSSxHQUFHLEtBQUssRUFBcUIsQ0FBQztJQUMzQyw4REFBOEQ7SUFDckQsS0FBSyxHQUFHLEtBQUssQ0FBUyxDQUFDLENBQUMsQ0FBQztJQUVsQzs7Ozs7T0FLRztJQUNNLFNBQVMsR0FBRyxNQUFNLEVBQTRCLENBQUM7SUFDeEQ7OztPQUdHO0lBQ00sYUFBYSxHQUFHLE1BQU0sRUFBNEIsQ0FBQztJQUM1RDs7O09BR0c7SUFDTSxpQkFBaUIsR0FBRyxNQUFNLEVBQXlCLENBQUM7SUFDN0QsOEZBQThG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Q0FBQztJQUMzRSxDQUFDO0lBRUQsb0VBQW9FO0lBQzFELGFBQWEsQ0FBQyxJQUF1QjtRQUM3QyxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUMzQixPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUNqRCxDQUFDO0lBRVMsc0JBQXNCLENBQUMsS0FBWTtRQUMzQyxJQUFJLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxFQUFFLENBQUM7WUFDM0IsT0FBTztRQUNULENBQUM7UUFFRCxLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7SUFDMUIsQ0FBQztJQUVTLHlCQUF5QixDQUFDLEtBQVk7UUFDOUMsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDdkIsT0FBTztRQUNULENBQUM7UUFFRCxLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7SUFDMUIsQ0FBQzt3R0E3TVUsK0JBQStCOzRGQUEvQiwrQkFBK0Isa2hCQWdDRCxpQkFBaUIsc0hBRUksZ0JBQWdCLDZDQ3pFaEYsZzNXQXlJQSx3NEhEbEdhLCtCQUErQix5TEFMaEMsY0FBYyxvUkFBRSxzQ0FBc0M7OzRGQUtyRCwrQkFBK0I7a0JBUjNDLFNBQVM7K0JBQ0UscUNBQXFDLGNBQ25DLElBQUksV0FDUCxDQUFDLGNBQWMsRUFBRSxzQ0FBc0MsQ0FBQyxtQkFHaEQsdUJBQXVCLENBQUMsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICBDb21wb25lbnQsXG4gIE9uRGVzdHJveSxcbiAgVmlld0NvbnRhaW5lclJlZixcbiAgY29tcHV0ZWQsXG4gIGluamVjdCxcbiAgaW5wdXQsXG4gIG91dHB1dCxcbiAgZWZmZWN0LFxuICB2aWV3Q2hpbGQsXG4gIHZpZXdDaGlsZHJlbixcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBMaWJzVWlDb21wb25lbnRzQnV0dG9uc0J1dHRvbkNvbXBvbmVudCB9IGZyb20gJ0BsaWJzLXVpL2NvbXBvbmVudHMtYnV0dG9ucy1idXR0b24nO1xuaW1wb3J0IHsgR3JpZHN0ZXJDb21wb25lbnQsIEdyaWRzdGVyTW9kdWxlIH0gZnJvbSAnZ3JpZHN0ZXIxOCc7XG5pbXBvcnQgeyBlbXB0eVJvb3ROb2RlIH0gZnJvbSAnLi4vZGVmaW5lcy9ncmlkc3Rlci1jb25maWcuZGVmaW5lJztcbmltcG9ydCB7IExpYnNVaUdyaWRMYXlvdXRTZXJ2aWNlIH0gZnJvbSAnLi4vZ3JpZC1sYXlvdXQuc2VydmljZSc7XG5pbXBvcnQge1xuICBUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQsXG4gIFRfZ3JpZExheW91dF9hZGRFdmVudCxcbiAgVF9ncmlkTGF5b3V0X2NoYW5nZUV2ZW50LFxuICBUX2dyaWRMYXlvdXRfbmV3Tm9kZSxcbiAgVF9ncmlkTGF5b3V0X25vZGUsXG59IGZyb20gJy4uL2ludGVyZmFjZXMvZ3JpZC1sYXlvdXQuaW50ZXJmYWNlJztcblxuLyoqXG4gKiBN4bq3dCBwaOG6s25nIGvDqW8gdGjhuqMuIMSQ4buHIHF1eToga2jhu5FpIGPDsyBgY2hpbGRyZW5gIHRow6wgcnXhu5l0IG7DsyBs4bqhaSBsw6AgbeG7mXQgbeG6t3QgcGjhurNuZyBu4buvYS5cbiAqXG4gKiBOxqFpIGTDuW5nIEtIw5RORyBk4buxbmcgY29tcG9uZW50IG7DoHkgdHLhu7FjIHRp4bq/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=
|