@libs-ui/services-grid-layout 0.2.357-26 → 0.2.357-28

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.
@@ -1,11 +1,13 @@
1
1
  import { ChangeDetectionStrategy, Component, HostListener, ViewContainerRef, computed, effect, inject, input, model, output, signal, viewChild, viewChildren, } from '@angular/core';
2
2
  import { NgTemplateOutlet } from '@angular/common';
3
3
  import { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';
4
+ import { LibsUiComponentsScrollOverlayDirective } from '@libs-ui/components-scroll-overlay';
4
5
  import { GridsterComponent, GridsterModule } from 'gridster18';
5
6
  import { TranslateModule } from '@ngx-translate/core';
6
7
  import { emptyRootNode } from '../defines/gridster-config.define';
7
8
  import { LibsUiGridLayoutService } from '../grid-layout.service';
8
9
  import { calculateDropGhost, parseDropPayload } from '../defines/collision.define';
10
+ import { bridgeScrollBetweenElements, edgeScrollStep } from '../defines/scroll-bridge.define';
9
11
  import * as i0 from "@angular/core";
10
12
  import * as i1 from "gridster18";
11
13
  import * as i2 from "@ngx-translate/core";
@@ -97,7 +99,48 @@ export class LibsUiGridLayoutCanvasComponent {
97
99
  outConfigNode = output();
98
100
  gridsterRef = viewChild(GridsterComponent);
99
101
  scrollAreaRef = viewChild('scrollArea');
102
+ /**
103
+ * Cầm BẤT CỨ ĐÂU trên khối để kéo, thay cho dải kéo ba chấm.
104
+ *
105
+ * 🔴 Mặc định `false` — giữ nguyên luật đã chốt 28/08/2026 cho mọi sản phẩm: *"phải hover vào vùng
106
+ * màu xanh thì mới kéo thả được cho đồng nhất"*. Nơi dùng nào muốn kiểu "cầm vào thẻ là kéo" thì tự
107
+ * bật, không đổi hành vi của màn khác.
108
+ *
109
+ * Bật lên thì dải kéo biến mất và gridster chuyển sang `ignoreContent: false` — cho kéo từ mọi chỗ
110
+ * TRỪ phần tử mang `ignoreContentClass`. Nhờ vậy khối con nằm trong khối ghép vẫn không nhấc nhầm
111
+ * khối ghép: vỏ khối con đã mang sẵn class chặn đó.
112
+ */
113
+ dragFromAnywhere = input(false);
114
+ /**
115
+ * Nơi dùng đã đẩy vùng cuộn DỌC vào thẳng `<gridster>` (bằng CSS của chính nó).
116
+ *
117
+ * 🔴 Bật cờ này là trả `angular-gridster2` về đúng giả định gốc của nó — `dragMove` đọc
118
+ * `gridster.el.scrollTop`, `scroll()` đo `gridsterElement.offsetHeight` — nên thư viện KHÔNG bắc
119
+ * cầu `scrollTop` sang khung bọc nữa, và vòng tự cuộn khi kéo cũng cuộn thẳng vào gridster.
120
+ *
121
+ * 🔴 PHẢI là input, KHÔNG dò bằng `getComputedStyle`: thanh cuộn nổi của libs-ui mới là thứ đặt
122
+ * `overflow` lên phần tử, mà nó chạy SAU `ngAfterViewInit` — dò lúc đó luôn ra "không cuộn" và cầu
123
+ * nối vẫn cắm vào, nuốt sạch đoạn cuộn thật (đo 15/09/2026: `g.scrollTop = 300` xong đọc lại vẫn 0).
124
+ */
125
+ gridsterOwnsScroll = input(false);
126
+ /**
127
+ * Vẽ thanh cuộn nổi của libs-ui cho mặt phẳng LỒNG (ruột khối ghép).
128
+ *
129
+ * 🔴 Vùng cuộn của mặt phẳng lồng phải giấu thanh cuộn mặc định của trình duyệt — nó dày, xám, và
130
+ * ăn mất bề rộng của lưới con nên 24 cột hẹp lại mỗi khi nội dung vừa đủ tràn. Hệ quả là người
131
+ * dùng cuộn được nhưng KHÔNG THẤY có gì để cuộn: thẻ con nằm dưới đáy trông như biến mất (người
132
+ * dùng chỉ ra 16/09/2026). Bật cờ này để thư viện gắn thanh cuộn nổi — nhìn thấy được mà không
133
+ * chiếm chỗ.
134
+ */
135
+ nestedScrollbar = input(false);
100
136
  zoomDockRef = viewChild('zoomDock');
137
+ /** Gỡ cầu nối scroll đã cắm lên phần tử `<gridster>` — xem `bridgeScrollToGridster`. */
138
+ removeScrollBridge;
139
+ /** Gỡ vòng tự cuộn khi kéo — xem `startDragAutoScroll`. */
140
+ stopDragAutoScroll;
141
+ /** Khoảng cách tới mép vùng cuộn (px) bắt đầu tự cuộn, và bước cuộn mỗi khung hình. */
142
+ dragScrollEdge = 36;
143
+ dragScrollStep = 8;
101
144
  /** mỗi ô một chỗ gắn riêng — cùng thứ tự với `children()` */
102
145
  itemHosts = viewChildren('itemHost', { read: ViewContainerRef });
103
146
  /**
@@ -170,6 +213,13 @@ export class LibsUiGridLayoutCanvasComponent {
170
213
  toggleType: () => this.emitToggleType(item),
171
214
  })));
172
215
  isNestedCanvas = computed(() => this.depth() > 0);
216
+ /** Chỉ mặt phẳng LỒNG mới vẽ thanh cuộn nổi — mặt phẳng gốc do nơi dùng tự lo. */
217
+ showNestedScrollbar = computed(() => this.isNestedCanvas() && this.nestedScrollbar());
218
+ /**
219
+ * Chỉ cuộn DỌC: bề rộng khối con luôn bị gridster kẹp trong số cột của lưới nên không có gì để
220
+ * cuộn ngang.
221
+ */
222
+ nestedScrollOverlayOptions = { scrollX: 'hidden', scrollY: 'scroll' };
173
223
  /**
174
224
  * Khe nhìn thấy giữa các khối CẤP GỐC. Mặt phẳng lồng trả 0 — khối con là các dòng của cùng một
175
225
  * thẻ, tách chúng ra làm chúng trông như thẻ rời.
@@ -191,7 +241,17 @@ export class LibsUiGridLayoutCanvasComponent {
191
241
  // Đọc `Version` để `init()` lại (đổi chế độ, bật/tắt chồng khối) sinh ra options MỚI —
192
242
  // gridster chỉ áp dụng cấu hình khi tham chiếu `options` đổi.
193
243
  this.service.Version();
194
- return this.service.FunctionsControl.gridsterOptions(this.depth(), () => this.outChange.emit({ root: this.currentNode() }));
244
+ const options = this.service.FunctionsControl.gridsterOptions(this.depth(), () => this.outChange.emit({ root: this.currentNode() }));
245
+ if (!this.dragFromAnywhere()) {
246
+ return options;
247
+ }
248
+ // 🔴 `delayStart` PHẢI khác 0 ở chế độ này. Với tay cầm chuyên dụng thì `0` là đúng (bấm vào dải
249
+ // chỉ có thể là ý định kéo), nhưng khi cả thẻ là tay cầm thì `0` nuốt luôn mọi cú bấm vào nút và
250
+ // liên kết bên trong thẻ. 150ms: bấm nhanh vẫn ăn, giữ lại một nhịp mới là kéo.
251
+ return {
252
+ ...options,
253
+ draggable: { ...options.draggable, ignoreContent: false, delayStart: 150 },
254
+ };
195
255
  });
196
256
  constructor() {
197
257
  // Gắn component của nơi dùng vào từng ô, và gắn LẠI mỗi khi cây đổi.
@@ -230,6 +290,7 @@ export class LibsUiGridLayoutCanvasComponent {
230
290
  });
231
291
  }
232
292
  ngAfterViewInit() {
293
+ this.bridgeScrollToGridster();
233
294
  if (!this.isZoomActive() || !this.autoFitOnLoad()) {
234
295
  return;
235
296
  }
@@ -253,6 +314,8 @@ export class LibsUiGridLayoutCanvasComponent {
253
314
  }
254
315
  }
255
316
  ngOnDestroy() {
317
+ this.removeScrollBridge?.();
318
+ this.stopDragAutoScroll?.();
256
319
  this.cleanupDragGrid();
257
320
  for (const item of this.children()) {
258
321
  this.service.FunctionsControl.detachNode(item.id);
@@ -275,6 +338,110 @@ export class LibsUiGridLayoutCanvasComponent {
275
338
  scrollArea: () => this.scrollAreaRef()?.nativeElement,
276
339
  };
277
340
  }
341
+ /**
342
+ * Bắc CẦU NỐI để `<gridster>` báo cáo scroll của KHUNG CUỘN, thay vì scroll của chính nó.
343
+ *
344
+ * 🔴 Vì sao cần: gridster đổi vị trí chuột thành ô lưới bằng
345
+ * `e.clientY + (el.scrollTop - el.offsetTop)` (xem `calculateItemPositionWithoutScale` và
346
+ * `this.offsetTop = this.gridster.el.scrollTop - this.gridster.el.offsetTop` trong
347
+ * `angular-gridster2@18`). Công thức chỉ đúng khi CHÍNH `<gridster>` là phần tử cuộn — đúng như
348
+ * comment đã ghi ở template cho `offsetParent`. Ở nhánh THU PHÓNG, việc cuộn nằm ở `#scrollArea`
349
+ * còn `<gridster>` giữ đủ cỡ sân khấu bên trong một tầng `transform: scale`, nên
350
+ * `el.scrollTop` luôn bằng 0: mọi px người dùng cuộn đều vô hình với thư viện. Hệ quả đo được
351
+ * 15/09/2026 trên mặt phẳng cấp trang: tự cuộn lúc kéo không chạy, và nếu nơi dùng tự cuộn thì thẻ
352
+ * đứng im trong khi mặt phẳng trôi.
353
+ *
354
+ * 🔴 CHỈ vá `scrollTop`/`scrollLeft`. CẤM đụng `offsetWidth`/`offsetHeight`: `setGridSize()` của
355
+ * gridster đọc hai cái đó để tính `curColWidth`, đè vào là vỡ bề rộng cột của mọi lưới.
356
+ *
357
+ * 🔴 Chia cho mức phóng: `offsetTop` là px BỐ CỤC, còn `scrollTop` của khung cuộn là px đã nhân
358
+ * thu phóng. Không quy đổi thì ở mức phóng khác 100% khối trôi nhanh hơn hoặc chậm hơn con trỏ.
359
+ */
360
+ /** Phần tử thật sự cuộn theo chiều dọc — `<gridster>` nếu nơi dùng đã đẩy cuộn vào trong nó. */
361
+ vungCuonDoc() {
362
+ const gridsterEl = this.gridsterRef()?.el;
363
+ if (this.gridsterOwnsScroll() && gridsterEl) {
364
+ return gridsterEl;
365
+ }
366
+ return this.scrollAreaRef()?.nativeElement;
367
+ }
368
+ bridgeScrollToGridster() {
369
+ // `GridsterComponent.el` là HTMLElement thẳng, không phải `ElementRef` (xem `gridster.component.d.ts`).
370
+ const gridsterEl = this.gridsterRef()?.el;
371
+ const scrollEl = this.scrollAreaRef()?.nativeElement;
372
+ if (!this.isZoomActive() || !gridsterEl || !scrollEl || this.removeScrollBridge) {
373
+ return;
374
+ }
375
+ // 🔴 `<gridster>` đã tự cuộn thì CẤM bắc cầu: cầu nối ghi đè `scrollTop`/`scrollLeft` của phần tử
376
+ // bằng số của khung bọc, nên chính đoạn cuộn thật của gridster bị nuốt — đặt `scrollTop` không
377
+ // ăn, kéo khối là nó nhảy về đầu lưới.
378
+ if (this.gridsterOwnsScroll()) {
379
+ return;
380
+ }
381
+ this.removeScrollBridge = bridgeScrollBetweenElements(gridsterEl, scrollEl, () => this.service.Zoom());
382
+ }
383
+ /**
384
+ * Tự cuộn mặt phẳng khi người dùng kéo khối tới sát mép.
385
+ *
386
+ * 🔴 Tự cuộn sẵn có của gridster (`scrollSensitivity`) không dùng được ở nhánh thu phóng: hàm
387
+ * `scroll()` của nó đo bằng `gridsterElement.offsetHeight`, mà `<gridster>` cao bằng CẢ sân khấu
388
+ * nên con trỏ không bao giờ "tới sát mép" theo con mắt của nó.
389
+ *
390
+ * 🔴 Chỉ cuộn khi thư viện ĐANG kéo thật (`.gridster-item-moving`) — không có chốt này thì một cú
391
+ * bấm bình thường gần mép cũng làm mặt phẳng trôi.
392
+ */
393
+ handlerScrollAreaPointerDown() {
394
+ this.startDragAutoScroll();
395
+ }
396
+ startDragAutoScroll() {
397
+ this.stopDragAutoScroll?.();
398
+ const scrollEl = this.scrollAreaRef()?.nativeElement;
399
+ const cuonDoc = this.vungCuonDoc();
400
+ if (!scrollEl) {
401
+ return;
402
+ }
403
+ let viTri;
404
+ let khung;
405
+ const chay = () => {
406
+ if (viTri && scrollEl.querySelector('.gridster-item-moving')) {
407
+ const hop = scrollEl.getBoundingClientRect();
408
+ const buoc = { edge: this.dragScrollEdge, step: this.dragScrollStep };
409
+ // `edgeScrollStep` tự kẹp mép theo cạnh khung, nên khung nhỏ như ruột khối ghép vẫn đúng.
410
+ const doc = edgeScrollStep({ ...buoc, pos: viTri.y, min: hop.top, max: hop.bottom });
411
+ const ngang = edgeScrollStep({ ...buoc, pos: viTri.x, min: hop.left, max: hop.right });
412
+ if (doc !== 0 || ngang !== 0) {
413
+ // Hai chiều có thể nằm ở HAI phần tử khác nhau: nơi dùng được phép đẩy cuộn dọc vào trong
414
+ // `<gridster>` mà vẫn để cuộn ngang ở khung bọc (sân khấu rộng hơn khung khi phóng to).
415
+ (cuonDoc ?? scrollEl).scrollTop += doc;
416
+ scrollEl.scrollLeft += ngang;
417
+ // 🔴 Cuộn xong PHẢI bắn lại một nhịp `mousemove` ở đúng chỗ con trỏ đang đứng. gridster chỉ
418
+ // tính lại vị trí khối trong `dragMove`, tức chỉ khi có sự kiện chuột — cuộn bằng mã mà
419
+ // không bắn thì giữa hai lần người dùng rê chuột, mặt phẳng trôi còn khối đứng im rồi giật
420
+ // về chỗ đúng ở nhịp sau (người dùng chỉ ra 15/09/2026: "kéo xuống dưới bị giật giật").
421
+ // Bắn `mousemove` chứ không phải `pointermove`: draggable của gridster chỉ nghe `mousemove`
422
+ // và `touchmove`, nên cũng không dội ngược vào chính listener bên dưới.
423
+ document.dispatchEvent(new MouseEvent('mousemove', { clientX: viTri.x, clientY: viTri.y, bubbles: true, cancelable: true, view: window }));
424
+ }
425
+ }
426
+ khung = requestAnimationFrame(chay);
427
+ };
428
+ const theoChuot = (event) => {
429
+ viTri = { x: event.clientX, y: event.clientY };
430
+ };
431
+ this.stopDragAutoScroll = () => {
432
+ document.removeEventListener('pointermove', theoChuot);
433
+ document.removeEventListener('pointerup', dung);
434
+ if (khung !== undefined) {
435
+ cancelAnimationFrame(khung);
436
+ khung = undefined;
437
+ }
438
+ this.stopDragAutoScroll = undefined;
439
+ };
440
+ const dung = () => this.stopDragAutoScroll?.();
441
+ document.addEventListener('pointermove', theoChuot);
442
+ document.addEventListener('pointerup', dung);
443
+ chay();
444
+ }
278
445
  handlerZoomIn() {
279
446
  this.service.FunctionsControl.zoomIn(this.zoomStep(), this.storageKey());
280
447
  this.syncZoomOut();
@@ -452,6 +619,15 @@ export class LibsUiGridLayoutCanvasComponent {
452
619
  if (this.depth() === 0) {
453
620
  return;
454
621
  }
622
+ // 🔴 `dragFromAnywhere` bật thì KHÔNG chặn — CẤM bỏ nhánh này.
623
+ // Khối ghép không còn dải kéo riêng, nên chỗ DUY NHẤT còn lại để cầm nó là NỀN TRỐNG của mặt
624
+ // phẳng lồng bên trong. Chặn ở đây là bịt nốt đường đó: người dùng bấm vào đâu trong khối ghép
625
+ // cũng không kéo nó đi được (người dùng chỉ ra 15/09/2026).
626
+ // Khối CON vẫn an toàn: `gridster-item` của chúng tự chặn (xem `handlerBlockParentDrag`), nên
627
+ // chỉ sự kiện rơi vào nền trống mới nổi lên tới khối ghép.
628
+ if (this.dragFromAnywhere()) {
629
+ return;
630
+ }
455
631
  event.stopPropagation();
456
632
  }
457
633
  handlerSelectNode(node, event) {
@@ -598,11 +774,17 @@ export class LibsUiGridLayoutCanvasComponent {
598
774
  }
599
775
  }
600
776
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiGridLayoutCanvasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
601
- 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 }, { propertyName: "childCanvasTemplates", predicate: ["childCanvasTpl"], descendants: true, 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-start 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 mx-auto 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 [style.--libs-ui-grid-layout-visual-gap.px]=\"visualGap()\">\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 [attr.data-node-id]=\"item.id\"\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 <!-- \uD83D\uDD34 Container C\u00D3 `getComponentOutlet`: khung n\u00E0y l\u00E0 V\u1ECE, v\u00E0 canvas con \u0111\u01B0\u1EE3c \u0111\u01B0a V\u00C0O TRONG\n component v\u1ECF qua input `childCanvas` (m\u1ED9t `TemplateRef`) \u2014 xem `ng-template #childCanvasTpl`\n b\u00EAn d\u01B0\u1EDBi. V\u1ECF t\u1EF1 quy\u1EBFt \u0111\u1EB7t canvas \u1EDF \u0111\u00E2u gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a n\u00F3.\n Tr\u01B0\u1EDBc \u0111\u00E2y canvas con l\u00E0 ANH EM c\u1EE7a khung n\u00E0y, n\u00EAn v\u1ECF chi\u1EBFm tr\u1ECDn chi\u1EC1u cao r\u1ED3i \u0111\u1EA9y to\u00E0n b\u1ED9\n th\u1EBB con ra ngo\u00E0i v\u00F9ng nh\u00ECn th\u1EA5y (\u0111o 14/09/2026: m\u1ECDi kh\u1ED1i gh\u00E9p ch\u1EC9 c\u00F2n nh\u00E3n v\u00E0 ru\u1ED9t tr\u1EAFng). -->\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) && !item.getComponentOutlet\">\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\n \uD83D\uDD34 Khu\u00F4n \u0111\u1EB7t trong `ng-template` \u0111\u1EC3 \u0111i \u0111\u01B0\u1EE3c HAI \u0111\u01B0\u1EDDng:\n - Container KH\u00D4NG c\u00F3 v\u1ECF ri\u00EAng \u2192 v\u1EBD th\u1EB3ng t\u1EA1i \u0111\u00E2y, y nh\u01B0 tr\u01B0\u1EDBc.\n - Container C\u00D3 `getComponentOutlet` \u2192 th\u01B0 vi\u1EC7n truy\u1EC1n khu\u00F4n n\u00E0y xu\u1ED1ng component v\u1ECF qua\n input `childCanvas`, v\u1ECF t\u1EF1 \u0111\u1EB7t n\u00F3 v\u00E0o gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a m\u00ECnh. V\u1EBD th\u00EAm \u1EDF\n \u0111\u00E2y n\u1EEFa l\u00E0 hai m\u1EB7t ph\u1EB3ng l\u1ED3ng ch\u1ED3ng nhau. -->\n <ng-template #childCanvasTpl>\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 </ng-template>\n\n @if (!item.getComponentOutlet) {\n <ng-container [ngTemplateOutlet]=\"childCanvasTpl\" />\n }\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 tr\u00E1i -->\n @if (showZoomDock()) {\n <div\n #zoomDock\n id=\"canvas-zoom-dock\"\n role=\"toolbar\"\n tabindex=\"0\"\n class=\"absolute bottom-4 left-6 z-30 flex items-center bg-white rounded-full shadow-2xl border-2 border-slate-400 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-[28px] h-[28px] flex items-center justify-center rounded-full text-slate-700 hover:text-slate-900 hover:bg-slate-200 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-[28px] px-2 flex items-center gap-1 rounded-full text-xs font-semibold text-slate-700 hover:bg-slate-200 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_options' | translate\"\n (click)=\"handlerToggleMenu()\">\n <span>{{ zoomPercent() }}%</span>\n <svg class=\"w-[12px] h-[12px] 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 left-0 w-[256px] 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-600\" 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-[28px] h-[28px] flex items-center justify-center rounded-full text-slate-700 hover:text-slate-900 hover:bg-slate-200 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-[16px] 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-[28px] shrink-0 px-2.5 flex items-center gap-1 rounded-full text-xs text-slate-700 hover:text-slate-900 hover:bg-slate-200 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=\"whitespace-nowrap 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-[28px] 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-[12px] h-[12px]\" 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 [attr.data-node-id]=\"item.id\"\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 <!-- \uD83D\uDD34 Container C\u00D3 `getComponentOutlet`: khung n\u00E0y l\u00E0 V\u1ECE, v\u00E0 canvas con \u0111\u01B0\u1EE3c \u0111\u01B0a V\u00C0O TRONG\n component v\u1ECF qua input `childCanvas` (m\u1ED9t `TemplateRef`) \u2014 xem `ng-template #childCanvasTpl`\n b\u00EAn d\u01B0\u1EDBi. V\u1ECF t\u1EF1 quy\u1EBFt \u0111\u1EB7t canvas \u1EDF \u0111\u00E2u gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a n\u00F3.\n Tr\u01B0\u1EDBc \u0111\u00E2y canvas con l\u00E0 ANH EM c\u1EE7a khung n\u00E0y, n\u00EAn v\u1ECF chi\u1EBFm tr\u1ECDn chi\u1EC1u cao r\u1ED3i \u0111\u1EA9y to\u00E0n b\u1ED9\n th\u1EBB con ra ngo\u00E0i v\u00F9ng nh\u00ECn th\u1EA5y (\u0111o 14/09/2026: m\u1ECDi kh\u1ED1i gh\u00E9p ch\u1EC9 c\u00F2n nh\u00E3n v\u00E0 ru\u1ED9t tr\u1EAFng). -->\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) && !item.getComponentOutlet\">\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\n \uD83D\uDD34 Khu\u00F4n \u0111\u1EB7t trong `ng-template` \u0111\u1EC3 \u0111i \u0111\u01B0\u1EE3c HAI \u0111\u01B0\u1EDDng:\n - Container KH\u00D4NG c\u00F3 v\u1ECF ri\u00EAng \u2192 v\u1EBD th\u1EB3ng t\u1EA1i \u0111\u00E2y, y nh\u01B0 tr\u01B0\u1EDBc.\n - Container C\u00D3 `getComponentOutlet` \u2192 th\u01B0 vi\u1EC7n truy\u1EC1n khu\u00F4n n\u00E0y xu\u1ED1ng component v\u1ECF qua\n input `childCanvas`, v\u1ECF t\u1EF1 \u0111\u1EB7t n\u00F3 v\u00E0o gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a m\u00ECnh. V\u1EBD th\u00EAm \u1EDF\n \u0111\u00E2y n\u1EEFa l\u00E0 hai m\u1EB7t ph\u1EB3ng l\u1ED3ng ch\u1ED3ng nhau. -->\n <ng-template #childCanvasTpl>\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 </ng-template>\n\n @if (!item.getComponentOutlet) {\n <ng-container [ngTemplateOutlet]=\"childCanvasTpl\" />\n }\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}.libs-ui-grid-layout-stage>gridster>gridster-item>.libs-ui-grid-layout-block-shell{inset:calc(var(--libs-ui-grid-layout-visual-gap, 0px) / 2);width:calc(100% - var(--libs-ui-grid-layout-visual-gap, 0px));height:calc(100% - var(--libs-ui-grid-layout-visual-gap, 0px));box-sizing:border-box}\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 });
777
+ 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 }, dragFromAnywhere: { classPropertyName: "dragFromAnywhere", publicName: "dragFromAnywhere", isSignal: true, isRequired: false, transformFunction: null }, gridsterOwnsScroll: { classPropertyName: "gridsterOwnsScroll", publicName: "gridsterOwnsScroll", isSignal: true, isRequired: false, transformFunction: null }, nestedScrollbar: { classPropertyName: "nestedScrollbar", publicName: "nestedScrollbar", 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 }, { propertyName: "childCanvasTemplates", predicate: ["childCanvasTpl"], descendants: true, 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-start items-start\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\"\n (pointerdown)=\"handlerScrollAreaPointerDown()\"\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 mx-auto 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 [style.--libs-ui-grid-layout-visual-gap.px]=\"visualGap()\">\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\n class=\"h-full w-full\"\n [class.display-grid]=\"showGridLines()\"\n [class.libs-ui-grid-layout-drag-anywhere]=\"dragFromAnywhere()\"\n [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 <!-- \uD83D\uDD34 `dragFromAnywhere` b\u1EADt th\u00EC B\u1ECE class ch\u1EB7n \u2014 C\u1EA4M g\u1EAFn l\u1EA1i v\u00F4 \u0111i\u1EC1u ki\u1EC7n.\n `libs-ui-grid-layout-block-parent-drag` CH\u00CDNH L\u00C0 `ignoreContentClass` c\u1EE7a gridster (xem\n `gridster-config.define`). V\u1EDBi `ignoreContent: true` n\u00F3 ch\u1EC9 mang ngh\u0129a \"kh\u00F4ng ph\u1EA3i tay\n c\u1EA7m\"; nh\u01B0ng v\u1EDBi `ignoreContent: false` (ch\u1EBF \u0111\u1ED9 c\u1EA7m-c\u1EA3-th\u1EBB) gridster hi\u1EC3u l\u00E0 \"v\u00F9ng C\u1EA4M\n k\u00E9o\": duy\u1EC7t t\u1EEB ch\u1ED7 b\u1EA5m l\u00EAn g\u1EB7p class n\u00E0y l\u00E0 d\u1EEBng h\u1EB3n, n\u00EAn KH\u1ED0I CON trong kh\u1ED1i gh\u00E9p b\u1EA5m\n v\u00E0o \u0111\u00E2u c\u0169ng kh\u00F4ng nh\u00FAc nh\u00EDch (ng\u01B0\u1EDDi d\u00F9ng ch\u1EC9 ra 15/09/2026; \u0111o \u0111\u01B0\u1EE3c k\u00E9o 150px m\u00E0 kh\u1ED1i\n l\u1EC7ch 0px). Ch\u1EB7n l\u01B0\u1EDBi cha \u1EDF ch\u1EBF \u0111\u1ED9 n\u00E0y do `handlerBlockParentDrag` tr\u00EAn ch\u00EDnh\n `gridster-item` lo, kh\u00F4ng c\u1EA7n class n\u00E0y. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [attr.data-node-id]=\"item.id\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas() && !dragFromAnywhere()\">\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 <!-- \uD83D\uDD34 `dragFromAnywhere` b\u1EADt th\u00EC KH\u00D4NG v\u1EBD d\u1EA3i: c\u1EA3 th\u1EBB \u0111\u00E3 l\u00E0 tay c\u1EA7m, \u0111\u1EC3 l\u1EA1i d\u1EA3i ch\u1EC9 th\u1EEBa. -->\n @if (isEditMode() && !dragFromAnywhere()) {\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 <!-- \uD83D\uDD34 Container C\u00D3 `getComponentOutlet`: khung n\u00E0y l\u00E0 V\u1ECE, v\u00E0 canvas con \u0111\u01B0\u1EE3c \u0111\u01B0a V\u00C0O TRONG\n component v\u1ECF qua input `childCanvas` (m\u1ED9t `TemplateRef`) \u2014 xem `ng-template #childCanvasTpl`\n b\u00EAn d\u01B0\u1EDBi. V\u1ECF t\u1EF1 quy\u1EBFt \u0111\u1EB7t canvas \u1EDF \u0111\u00E2u gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a n\u00F3.\n Tr\u01B0\u1EDBc \u0111\u00E2y canvas con l\u00E0 ANH EM c\u1EE7a khung n\u00E0y, n\u00EAn v\u1ECF chi\u1EBFm tr\u1ECDn chi\u1EC1u cao r\u1ED3i \u0111\u1EA9y to\u00E0n b\u1ED9\n th\u1EBB con ra ngo\u00E0i v\u00F9ng nh\u00ECn th\u1EA5y (\u0111o 14/09/2026: m\u1ECDi kh\u1ED1i gh\u00E9p ch\u1EC9 c\u00F2n nh\u00E3n v\u00E0 ru\u1ED9t tr\u1EAFng). -->\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) && !item.getComponentOutlet\">\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\n \uD83D\uDD34 Khu\u00F4n \u0111\u1EB7t trong `ng-template` \u0111\u1EC3 \u0111i \u0111\u01B0\u1EE3c HAI \u0111\u01B0\u1EDDng:\n - Container KH\u00D4NG c\u00F3 v\u1ECF ri\u00EAng \u2192 v\u1EBD th\u1EB3ng t\u1EA1i \u0111\u00E2y, y nh\u01B0 tr\u01B0\u1EDBc.\n - Container C\u00D3 `getComponentOutlet` \u2192 th\u01B0 vi\u1EC7n truy\u1EC1n khu\u00F4n n\u00E0y xu\u1ED1ng component v\u1ECF qua\n input `childCanvas`, v\u1ECF t\u1EF1 \u0111\u1EB7t n\u00F3 v\u00E0o gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a m\u00ECnh. V\u1EBD th\u00EAm \u1EDF\n \u0111\u00E2y n\u1EEFa l\u00E0 hai m\u1EB7t ph\u1EB3ng l\u1ED3ng ch\u1ED3ng nhau. -->\n <ng-template #childCanvasTpl>\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 [dragFromAnywhere]=\"dragFromAnywhere()\"\n [nestedScrollbar]=\"nestedScrollbar()\"\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 </ng-template>\n\n @if (!item.getComponentOutlet) {\n <ng-container [ngTemplateOutlet]=\"childCanvasTpl\" />\n }\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 tr\u00E1i -->\n @if (showZoomDock()) {\n <div\n #zoomDock\n id=\"canvas-zoom-dock\"\n role=\"toolbar\"\n tabindex=\"0\"\n class=\"absolute bottom-4 left-6 z-30 flex items-center bg-white rounded-full shadow-2xl border-2 border-slate-400 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-[28px] h-[28px] flex items-center justify-center rounded-full text-slate-700 hover:text-slate-900 hover:bg-slate-200 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-[28px] px-2 flex items-center gap-1 rounded-full text-xs font-semibold text-slate-700 hover:bg-slate-200 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_options' | translate\"\n (click)=\"handlerToggleMenu()\">\n <span>{{ zoomPercent() }}%</span>\n <svg class=\"w-[12px] h-[12px] 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 left-0 w-[256px] 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-600\" 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-[28px] h-[28px] flex items-center justify-center rounded-full text-slate-700 hover:text-slate-900 hover:bg-slate-200 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-[16px] 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-[28px] shrink-0 px-2.5 flex items-center gap-1 rounded-full text-xs text-slate-700 hover:text-slate-900 hover:bg-slate-200 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=\"whitespace-nowrap 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-[28px] 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-[12px] h-[12px]\" 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 <!-- \uD83D\uDD34 `ignoreInit` l\u00E0 c\u00F4ng t\u1EAFc B\u1EACT/T\u1EAET c\u1EE7a thanh cu\u1ED9n n\u1ED5i: directive kh\u00F4ng th\u1EC3 g\u1EAFn c\u00F3 \u0111i\u1EC1u ki\u1EC7n,\n n\u00EAn khi t\u1EAFt ph\u1EA3i ch\u1EB7n ngay \u1EDF \u0111\u00E2y \u2014 b\u1EADt r\u1ED3i m\u1EDBi b\u1ECDc th\u00EAm `div.libs-ui-scroll-overlay-container`.\n Nh\u1EDD v\u1EADy m\u1EB7t ph\u1EB3ng G\u1ED0C c\u1EE7a n\u01A1i d\u00F9ng kh\u00E1c gi\u1EEF nguy\u00EAn c\u1EA5u tr\u00FAc DOM nh\u01B0 tr\u01B0\u1EDBc. -->\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 LibsUiComponentsScrollOverlayDirective\n [ignoreInit]=\"!showNestedScrollbar()\"\n [options]=\"nestedScrollOverlayOptions\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\"\n (pointerdown)=\"handlerScrollAreaPointerDown()\"\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\n class=\"h-full w-full\"\n [class.display-grid]=\"showGridLines()\"\n [class.libs-ui-grid-layout-drag-anywhere]=\"dragFromAnywhere()\"\n [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 <!-- \uD83D\uDD34 `dragFromAnywhere` b\u1EADt th\u00EC B\u1ECE class ch\u1EB7n \u2014 C\u1EA4M g\u1EAFn l\u1EA1i v\u00F4 \u0111i\u1EC1u ki\u1EC7n.\n `libs-ui-grid-layout-block-parent-drag` CH\u00CDNH L\u00C0 `ignoreContentClass` c\u1EE7a gridster (xem\n `gridster-config.define`). V\u1EDBi `ignoreContent: true` n\u00F3 ch\u1EC9 mang ngh\u0129a \"kh\u00F4ng ph\u1EA3i tay\n c\u1EA7m\"; nh\u01B0ng v\u1EDBi `ignoreContent: false` (ch\u1EBF \u0111\u1ED9 c\u1EA7m-c\u1EA3-th\u1EBB) gridster hi\u1EC3u l\u00E0 \"v\u00F9ng C\u1EA4M\n k\u00E9o\": duy\u1EC7t t\u1EEB ch\u1ED7 b\u1EA5m l\u00EAn g\u1EB7p class n\u00E0y l\u00E0 d\u1EEBng h\u1EB3n, n\u00EAn KH\u1ED0I CON trong kh\u1ED1i gh\u00E9p b\u1EA5m\n v\u00E0o \u0111\u00E2u c\u0169ng kh\u00F4ng nh\u00FAc nh\u00EDch (ng\u01B0\u1EDDi d\u00F9ng ch\u1EC9 ra 15/09/2026; \u0111o \u0111\u01B0\u1EE3c k\u00E9o 150px m\u00E0 kh\u1ED1i\n l\u1EC7ch 0px). Ch\u1EB7n l\u01B0\u1EDBi cha \u1EDF ch\u1EBF \u0111\u1ED9 n\u00E0y do `handlerBlockParentDrag` tr\u00EAn ch\u00EDnh\n `gridster-item` lo, kh\u00F4ng c\u1EA7n class n\u00E0y. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [attr.data-node-id]=\"item.id\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas() && !dragFromAnywhere()\">\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 <!-- \uD83D\uDD34 `dragFromAnywhere` b\u1EADt th\u00EC KH\u00D4NG v\u1EBD d\u1EA3i: c\u1EA3 th\u1EBB \u0111\u00E3 l\u00E0 tay c\u1EA7m, \u0111\u1EC3 l\u1EA1i d\u1EA3i ch\u1EC9 th\u1EEBa. -->\n @if (isEditMode() && !dragFromAnywhere()) {\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 <!-- \uD83D\uDD34 Container C\u00D3 `getComponentOutlet`: khung n\u00E0y l\u00E0 V\u1ECE, v\u00E0 canvas con \u0111\u01B0\u1EE3c \u0111\u01B0a V\u00C0O TRONG\n component v\u1ECF qua input `childCanvas` (m\u1ED9t `TemplateRef`) \u2014 xem `ng-template #childCanvasTpl`\n b\u00EAn d\u01B0\u1EDBi. V\u1ECF t\u1EF1 quy\u1EBFt \u0111\u1EB7t canvas \u1EDF \u0111\u00E2u gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a n\u00F3.\n Tr\u01B0\u1EDBc \u0111\u00E2y canvas con l\u00E0 ANH EM c\u1EE7a khung n\u00E0y, n\u00EAn v\u1ECF chi\u1EBFm tr\u1ECDn chi\u1EC1u cao r\u1ED3i \u0111\u1EA9y to\u00E0n b\u1ED9\n th\u1EBB con ra ngo\u00E0i v\u00F9ng nh\u00ECn th\u1EA5y (\u0111o 14/09/2026: m\u1ECDi kh\u1ED1i gh\u00E9p ch\u1EC9 c\u00F2n nh\u00E3n v\u00E0 ru\u1ED9t tr\u1EAFng). -->\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) && !item.getComponentOutlet\">\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\n \uD83D\uDD34 Khu\u00F4n \u0111\u1EB7t trong `ng-template` \u0111\u1EC3 \u0111i \u0111\u01B0\u1EE3c HAI \u0111\u01B0\u1EDDng:\n - Container KH\u00D4NG c\u00F3 v\u1ECF ri\u00EAng \u2192 v\u1EBD th\u1EB3ng t\u1EA1i \u0111\u00E2y, y nh\u01B0 tr\u01B0\u1EDBc.\n - Container C\u00D3 `getComponentOutlet` \u2192 th\u01B0 vi\u1EC7n truy\u1EC1n khu\u00F4n n\u00E0y xu\u1ED1ng component v\u1ECF qua\n input `childCanvas`, v\u1ECF t\u1EF1 \u0111\u1EB7t n\u00F3 v\u00E0o gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a m\u00ECnh. V\u1EBD th\u00EAm \u1EDF\n \u0111\u00E2y n\u1EEFa l\u00E0 hai m\u1EB7t ph\u1EB3ng l\u1ED3ng ch\u1ED3ng nhau. -->\n <ng-template #childCanvasTpl>\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 [dragFromAnywhere]=\"dragFromAnywhere()\"\n [nestedScrollbar]=\"nestedScrollbar()\"\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 </ng-template>\n\n @if (!item.getComponentOutlet) {\n <ng-container [ngTemplateOutlet]=\"childCanvasTpl\" />\n }\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}.libs-ui-grid-layout-stage>gridster>gridster-item>.libs-ui-grid-layout-block-shell{inset:calc(var(--libs-ui-grid-layout-visual-gap, 0px) / 2);width:calc(100% - var(--libs-ui-grid-layout-visual-gap, 0px));height:calc(100% - var(--libs-ui-grid-layout-visual-gap, 0px));box-sizing:border-box}gridster.libs-ui-grid-layout-drag-anywhere gridster-item{user-select:none;-webkit-user-select:none}\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", "dragFromAnywhere", "gridsterOwnsScroll", "nestedScrollbar"], 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: LibsUiComponentsScrollOverlayDirective, selector: "[LibsUiComponentsScrollOverlayDirective]", inputs: ["debugMode", "ignoreInit", "classContainer", "options", "elementCheckScrollX", "elementCheckScrollY", "elementScroll"], outputs: ["outScroll", "outScrollX", "outScrollY", "outScrollTop", "outScrollBottom"] }, { 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 });
602
778
  }
603
779
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiGridLayoutCanvasComponent, decorators: [{
604
780
  type: Component,
605
- 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-start 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 mx-auto 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 [style.--libs-ui-grid-layout-visual-gap.px]=\"visualGap()\">\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 [attr.data-node-id]=\"item.id\"\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 <!-- \uD83D\uDD34 Container C\u00D3 `getComponentOutlet`: khung n\u00E0y l\u00E0 V\u1ECE, v\u00E0 canvas con \u0111\u01B0\u1EE3c \u0111\u01B0a V\u00C0O TRONG\n component v\u1ECF qua input `childCanvas` (m\u1ED9t `TemplateRef`) \u2014 xem `ng-template #childCanvasTpl`\n b\u00EAn d\u01B0\u1EDBi. V\u1ECF t\u1EF1 quy\u1EBFt \u0111\u1EB7t canvas \u1EDF \u0111\u00E2u gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a n\u00F3.\n Tr\u01B0\u1EDBc \u0111\u00E2y canvas con l\u00E0 ANH EM c\u1EE7a khung n\u00E0y, n\u00EAn v\u1ECF chi\u1EBFm tr\u1ECDn chi\u1EC1u cao r\u1ED3i \u0111\u1EA9y to\u00E0n b\u1ED9\n th\u1EBB con ra ngo\u00E0i v\u00F9ng nh\u00ECn th\u1EA5y (\u0111o 14/09/2026: m\u1ECDi kh\u1ED1i gh\u00E9p ch\u1EC9 c\u00F2n nh\u00E3n v\u00E0 ru\u1ED9t tr\u1EAFng). -->\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) && !item.getComponentOutlet\">\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\n \uD83D\uDD34 Khu\u00F4n \u0111\u1EB7t trong `ng-template` \u0111\u1EC3 \u0111i \u0111\u01B0\u1EE3c HAI \u0111\u01B0\u1EDDng:\n - Container KH\u00D4NG c\u00F3 v\u1ECF ri\u00EAng \u2192 v\u1EBD th\u1EB3ng t\u1EA1i \u0111\u00E2y, y nh\u01B0 tr\u01B0\u1EDBc.\n - Container C\u00D3 `getComponentOutlet` \u2192 th\u01B0 vi\u1EC7n truy\u1EC1n khu\u00F4n n\u00E0y xu\u1ED1ng component v\u1ECF qua\n input `childCanvas`, v\u1ECF t\u1EF1 \u0111\u1EB7t n\u00F3 v\u00E0o gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a m\u00ECnh. V\u1EBD th\u00EAm \u1EDF\n \u0111\u00E2y n\u1EEFa l\u00E0 hai m\u1EB7t ph\u1EB3ng l\u1ED3ng ch\u1ED3ng nhau. -->\n <ng-template #childCanvasTpl>\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 </ng-template>\n\n @if (!item.getComponentOutlet) {\n <ng-container [ngTemplateOutlet]=\"childCanvasTpl\" />\n }\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 tr\u00E1i -->\n @if (showZoomDock()) {\n <div\n #zoomDock\n id=\"canvas-zoom-dock\"\n role=\"toolbar\"\n tabindex=\"0\"\n class=\"absolute bottom-4 left-6 z-30 flex items-center bg-white rounded-full shadow-2xl border-2 border-slate-400 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-[28px] h-[28px] flex items-center justify-center rounded-full text-slate-700 hover:text-slate-900 hover:bg-slate-200 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-[28px] px-2 flex items-center gap-1 rounded-full text-xs font-semibold text-slate-700 hover:bg-slate-200 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_options' | translate\"\n (click)=\"handlerToggleMenu()\">\n <span>{{ zoomPercent() }}%</span>\n <svg class=\"w-[12px] h-[12px] 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 left-0 w-[256px] 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-600\" 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-[28px] h-[28px] flex items-center justify-center rounded-full text-slate-700 hover:text-slate-900 hover:bg-slate-200 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-[16px] 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-[28px] shrink-0 px-2.5 flex items-center gap-1 rounded-full text-xs text-slate-700 hover:text-slate-900 hover:bg-slate-200 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=\"whitespace-nowrap 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-[28px] 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-[12px] h-[12px]\" 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 [attr.data-node-id]=\"item.id\"\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 <!-- \uD83D\uDD34 Container C\u00D3 `getComponentOutlet`: khung n\u00E0y l\u00E0 V\u1ECE, v\u00E0 canvas con \u0111\u01B0\u1EE3c \u0111\u01B0a V\u00C0O TRONG\n component v\u1ECF qua input `childCanvas` (m\u1ED9t `TemplateRef`) \u2014 xem `ng-template #childCanvasTpl`\n b\u00EAn d\u01B0\u1EDBi. V\u1ECF t\u1EF1 quy\u1EBFt \u0111\u1EB7t canvas \u1EDF \u0111\u00E2u gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a n\u00F3.\n Tr\u01B0\u1EDBc \u0111\u00E2y canvas con l\u00E0 ANH EM c\u1EE7a khung n\u00E0y, n\u00EAn v\u1ECF chi\u1EBFm tr\u1ECDn chi\u1EC1u cao r\u1ED3i \u0111\u1EA9y to\u00E0n b\u1ED9\n th\u1EBB con ra ngo\u00E0i v\u00F9ng nh\u00ECn th\u1EA5y (\u0111o 14/09/2026: m\u1ECDi kh\u1ED1i gh\u00E9p ch\u1EC9 c\u00F2n nh\u00E3n v\u00E0 ru\u1ED9t tr\u1EAFng). -->\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) && !item.getComponentOutlet\">\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\n \uD83D\uDD34 Khu\u00F4n \u0111\u1EB7t trong `ng-template` \u0111\u1EC3 \u0111i \u0111\u01B0\u1EE3c HAI \u0111\u01B0\u1EDDng:\n - Container KH\u00D4NG c\u00F3 v\u1ECF ri\u00EAng \u2192 v\u1EBD th\u1EB3ng t\u1EA1i \u0111\u00E2y, y nh\u01B0 tr\u01B0\u1EDBc.\n - Container C\u00D3 `getComponentOutlet` \u2192 th\u01B0 vi\u1EC7n truy\u1EC1n khu\u00F4n n\u00E0y xu\u1ED1ng component v\u1ECF qua\n input `childCanvas`, v\u1ECF t\u1EF1 \u0111\u1EB7t n\u00F3 v\u00E0o gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a m\u00ECnh. V\u1EBD th\u00EAm \u1EDF\n \u0111\u00E2y n\u1EEFa l\u00E0 hai m\u1EB7t ph\u1EB3ng l\u1ED3ng ch\u1ED3ng nhau. -->\n <ng-template #childCanvasTpl>\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 </ng-template>\n\n @if (!item.getComponentOutlet) {\n <ng-container [ngTemplateOutlet]=\"childCanvasTpl\" />\n }\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}.libs-ui-grid-layout-stage>gridster>gridster-item>.libs-ui-grid-layout-block-shell{inset:calc(var(--libs-ui-grid-layout-visual-gap, 0px) / 2);width:calc(100% - var(--libs-ui-grid-layout-visual-gap, 0px));height:calc(100% - var(--libs-ui-grid-layout-visual-gap, 0px));box-sizing:border-box}\n"] }]
781
+ args: [{ selector: 'libs_ui-services-grid_layout-canvas', standalone: true, imports: [
782
+ GridsterModule,
783
+ LibsUiComponentsButtonsButtonComponent,
784
+ LibsUiComponentsScrollOverlayDirective,
785
+ NgTemplateOutlet,
786
+ TranslateModule,
787
+ ], 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-start items-start\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\"\n (pointerdown)=\"handlerScrollAreaPointerDown()\"\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 mx-auto 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 [style.--libs-ui-grid-layout-visual-gap.px]=\"visualGap()\">\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\n class=\"h-full w-full\"\n [class.display-grid]=\"showGridLines()\"\n [class.libs-ui-grid-layout-drag-anywhere]=\"dragFromAnywhere()\"\n [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 <!-- \uD83D\uDD34 `dragFromAnywhere` b\u1EADt th\u00EC B\u1ECE class ch\u1EB7n \u2014 C\u1EA4M g\u1EAFn l\u1EA1i v\u00F4 \u0111i\u1EC1u ki\u1EC7n.\n `libs-ui-grid-layout-block-parent-drag` CH\u00CDNH L\u00C0 `ignoreContentClass` c\u1EE7a gridster (xem\n `gridster-config.define`). V\u1EDBi `ignoreContent: true` n\u00F3 ch\u1EC9 mang ngh\u0129a \"kh\u00F4ng ph\u1EA3i tay\n c\u1EA7m\"; nh\u01B0ng v\u1EDBi `ignoreContent: false` (ch\u1EBF \u0111\u1ED9 c\u1EA7m-c\u1EA3-th\u1EBB) gridster hi\u1EC3u l\u00E0 \"v\u00F9ng C\u1EA4M\n k\u00E9o\": duy\u1EC7t t\u1EEB ch\u1ED7 b\u1EA5m l\u00EAn g\u1EB7p class n\u00E0y l\u00E0 d\u1EEBng h\u1EB3n, n\u00EAn KH\u1ED0I CON trong kh\u1ED1i gh\u00E9p b\u1EA5m\n v\u00E0o \u0111\u00E2u c\u0169ng kh\u00F4ng nh\u00FAc nh\u00EDch (ng\u01B0\u1EDDi d\u00F9ng ch\u1EC9 ra 15/09/2026; \u0111o \u0111\u01B0\u1EE3c k\u00E9o 150px m\u00E0 kh\u1ED1i\n l\u1EC7ch 0px). Ch\u1EB7n l\u01B0\u1EDBi cha \u1EDF ch\u1EBF \u0111\u1ED9 n\u00E0y do `handlerBlockParentDrag` tr\u00EAn ch\u00EDnh\n `gridster-item` lo, kh\u00F4ng c\u1EA7n class n\u00E0y. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [attr.data-node-id]=\"item.id\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas() && !dragFromAnywhere()\">\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 <!-- \uD83D\uDD34 `dragFromAnywhere` b\u1EADt th\u00EC KH\u00D4NG v\u1EBD d\u1EA3i: c\u1EA3 th\u1EBB \u0111\u00E3 l\u00E0 tay c\u1EA7m, \u0111\u1EC3 l\u1EA1i d\u1EA3i ch\u1EC9 th\u1EEBa. -->\n @if (isEditMode() && !dragFromAnywhere()) {\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 <!-- \uD83D\uDD34 Container C\u00D3 `getComponentOutlet`: khung n\u00E0y l\u00E0 V\u1ECE, v\u00E0 canvas con \u0111\u01B0\u1EE3c \u0111\u01B0a V\u00C0O TRONG\n component v\u1ECF qua input `childCanvas` (m\u1ED9t `TemplateRef`) \u2014 xem `ng-template #childCanvasTpl`\n b\u00EAn d\u01B0\u1EDBi. V\u1ECF t\u1EF1 quy\u1EBFt \u0111\u1EB7t canvas \u1EDF \u0111\u00E2u gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a n\u00F3.\n Tr\u01B0\u1EDBc \u0111\u00E2y canvas con l\u00E0 ANH EM c\u1EE7a khung n\u00E0y, n\u00EAn v\u1ECF chi\u1EBFm tr\u1ECDn chi\u1EC1u cao r\u1ED3i \u0111\u1EA9y to\u00E0n b\u1ED9\n th\u1EBB con ra ngo\u00E0i v\u00F9ng nh\u00ECn th\u1EA5y (\u0111o 14/09/2026: m\u1ECDi kh\u1ED1i gh\u00E9p ch\u1EC9 c\u00F2n nh\u00E3n v\u00E0 ru\u1ED9t tr\u1EAFng). -->\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) && !item.getComponentOutlet\">\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\n \uD83D\uDD34 Khu\u00F4n \u0111\u1EB7t trong `ng-template` \u0111\u1EC3 \u0111i \u0111\u01B0\u1EE3c HAI \u0111\u01B0\u1EDDng:\n - Container KH\u00D4NG c\u00F3 v\u1ECF ri\u00EAng \u2192 v\u1EBD th\u1EB3ng t\u1EA1i \u0111\u00E2y, y nh\u01B0 tr\u01B0\u1EDBc.\n - Container C\u00D3 `getComponentOutlet` \u2192 th\u01B0 vi\u1EC7n truy\u1EC1n khu\u00F4n n\u00E0y xu\u1ED1ng component v\u1ECF qua\n input `childCanvas`, v\u1ECF t\u1EF1 \u0111\u1EB7t n\u00F3 v\u00E0o gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a m\u00ECnh. V\u1EBD th\u00EAm \u1EDF\n \u0111\u00E2y n\u1EEFa l\u00E0 hai m\u1EB7t ph\u1EB3ng l\u1ED3ng ch\u1ED3ng nhau. -->\n <ng-template #childCanvasTpl>\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 [dragFromAnywhere]=\"dragFromAnywhere()\"\n [nestedScrollbar]=\"nestedScrollbar()\"\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 </ng-template>\n\n @if (!item.getComponentOutlet) {\n <ng-container [ngTemplateOutlet]=\"childCanvasTpl\" />\n }\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 tr\u00E1i -->\n @if (showZoomDock()) {\n <div\n #zoomDock\n id=\"canvas-zoom-dock\"\n role=\"toolbar\"\n tabindex=\"0\"\n class=\"absolute bottom-4 left-6 z-30 flex items-center bg-white rounded-full shadow-2xl border-2 border-slate-400 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-[28px] h-[28px] flex items-center justify-center rounded-full text-slate-700 hover:text-slate-900 hover:bg-slate-200 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-[28px] px-2 flex items-center gap-1 rounded-full text-xs font-semibold text-slate-700 hover:bg-slate-200 transition-colors cursor-pointer\"\n [title]=\"'i18n_zoom_options' | translate\"\n (click)=\"handlerToggleMenu()\">\n <span>{{ zoomPercent() }}%</span>\n <svg class=\"w-[12px] h-[12px] 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 left-0 w-[256px] 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-600\" 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-[28px] h-[28px] flex items-center justify-center rounded-full text-slate-700 hover:text-slate-900 hover:bg-slate-200 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-[16px] 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-[28px] shrink-0 px-2.5 flex items-center gap-1 rounded-full text-xs text-slate-700 hover:text-slate-900 hover:bg-slate-200 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=\"whitespace-nowrap 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-[28px] 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-[12px] h-[12px]\" 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 <!-- \uD83D\uDD34 `ignoreInit` l\u00E0 c\u00F4ng t\u1EAFc B\u1EACT/T\u1EAET c\u1EE7a thanh cu\u1ED9n n\u1ED5i: directive kh\u00F4ng th\u1EC3 g\u1EAFn c\u00F3 \u0111i\u1EC1u ki\u1EC7n,\n n\u00EAn khi t\u1EAFt ph\u1EA3i ch\u1EB7n ngay \u1EDF \u0111\u00E2y \u2014 b\u1EADt r\u1ED3i m\u1EDBi b\u1ECDc th\u00EAm `div.libs-ui-scroll-overlay-container`.\n Nh\u1EDD v\u1EADy m\u1EB7t ph\u1EB3ng G\u1ED0C c\u1EE7a n\u01A1i d\u00F9ng kh\u00E1c gi\u1EEF nguy\u00EAn c\u1EA5u tr\u00FAc DOM nh\u01B0 tr\u01B0\u1EDBc. -->\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 LibsUiComponentsScrollOverlayDirective\n [ignoreInit]=\"!showNestedScrollbar()\"\n [options]=\"nestedScrollOverlayOptions\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\"\n (pointerdown)=\"handlerScrollAreaPointerDown()\"\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\n class=\"h-full w-full\"\n [class.display-grid]=\"showGridLines()\"\n [class.libs-ui-grid-layout-drag-anywhere]=\"dragFromAnywhere()\"\n [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 <!-- \uD83D\uDD34 `dragFromAnywhere` b\u1EADt th\u00EC B\u1ECE class ch\u1EB7n \u2014 C\u1EA4M g\u1EAFn l\u1EA1i v\u00F4 \u0111i\u1EC1u ki\u1EC7n.\n `libs-ui-grid-layout-block-parent-drag` CH\u00CDNH L\u00C0 `ignoreContentClass` c\u1EE7a gridster (xem\n `gridster-config.define`). V\u1EDBi `ignoreContent: true` n\u00F3 ch\u1EC9 mang ngh\u0129a \"kh\u00F4ng ph\u1EA3i tay\n c\u1EA7m\"; nh\u01B0ng v\u1EDBi `ignoreContent: false` (ch\u1EBF \u0111\u1ED9 c\u1EA7m-c\u1EA3-th\u1EBB) gridster hi\u1EC3u l\u00E0 \"v\u00F9ng C\u1EA4M\n k\u00E9o\": duy\u1EC7t t\u1EEB ch\u1ED7 b\u1EA5m l\u00EAn g\u1EB7p class n\u00E0y l\u00E0 d\u1EEBng h\u1EB3n, n\u00EAn KH\u1ED0I CON trong kh\u1ED1i gh\u00E9p b\u1EA5m\n v\u00E0o \u0111\u00E2u c\u0169ng kh\u00F4ng nh\u00FAc nh\u00EDch (ng\u01B0\u1EDDi d\u00F9ng ch\u1EC9 ra 15/09/2026; \u0111o \u0111\u01B0\u1EE3c k\u00E9o 150px m\u00E0 kh\u1ED1i\n l\u1EC7ch 0px). Ch\u1EB7n l\u01B0\u1EDBi cha \u1EDF ch\u1EBF \u0111\u1ED9 n\u00E0y do `handlerBlockParentDrag` tr\u00EAn ch\u00EDnh\n `gridster-item` lo, kh\u00F4ng c\u1EA7n class n\u00E0y. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [attr.data-node-id]=\"item.id\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas() && !dragFromAnywhere()\">\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 <!-- \uD83D\uDD34 `dragFromAnywhere` b\u1EADt th\u00EC KH\u00D4NG v\u1EBD d\u1EA3i: c\u1EA3 th\u1EBB \u0111\u00E3 l\u00E0 tay c\u1EA7m, \u0111\u1EC3 l\u1EA1i d\u1EA3i ch\u1EC9 th\u1EEBa. -->\n @if (isEditMode() && !dragFromAnywhere()) {\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 <!-- \uD83D\uDD34 Container C\u00D3 `getComponentOutlet`: khung n\u00E0y l\u00E0 V\u1ECE, v\u00E0 canvas con \u0111\u01B0\u1EE3c \u0111\u01B0a V\u00C0O TRONG\n component v\u1ECF qua input `childCanvas` (m\u1ED9t `TemplateRef`) \u2014 xem `ng-template #childCanvasTpl`\n b\u00EAn d\u01B0\u1EDBi. V\u1ECF t\u1EF1 quy\u1EBFt \u0111\u1EB7t canvas \u1EDF \u0111\u00E2u gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a n\u00F3.\n Tr\u01B0\u1EDBc \u0111\u00E2y canvas con l\u00E0 ANH EM c\u1EE7a khung n\u00E0y, n\u00EAn v\u1ECF chi\u1EBFm tr\u1ECDn chi\u1EC1u cao r\u1ED3i \u0111\u1EA9y to\u00E0n b\u1ED9\n th\u1EBB con ra ngo\u00E0i v\u00F9ng nh\u00ECn th\u1EA5y (\u0111o 14/09/2026: m\u1ECDi kh\u1ED1i gh\u00E9p ch\u1EC9 c\u00F2n nh\u00E3n v\u00E0 ru\u1ED9t tr\u1EAFng). -->\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) && !item.getComponentOutlet\">\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\n \uD83D\uDD34 Khu\u00F4n \u0111\u1EB7t trong `ng-template` \u0111\u1EC3 \u0111i \u0111\u01B0\u1EE3c HAI \u0111\u01B0\u1EDDng:\n - Container KH\u00D4NG c\u00F3 v\u1ECF ri\u00EAng \u2192 v\u1EBD th\u1EB3ng t\u1EA1i \u0111\u00E2y, y nh\u01B0 tr\u01B0\u1EDBc.\n - Container C\u00D3 `getComponentOutlet` \u2192 th\u01B0 vi\u1EC7n truy\u1EC1n khu\u00F4n n\u00E0y xu\u1ED1ng component v\u1ECF qua\n input `childCanvas`, v\u1ECF t\u1EF1 \u0111\u1EB7t n\u00F3 v\u00E0o gi\u1EEFa ti\u00EAu \u0111\u1EC1 v\u00E0 ph\u1EA7n th\u00E2n c\u1EE7a m\u00ECnh. V\u1EBD th\u00EAm \u1EDF\n \u0111\u00E2y n\u1EEFa l\u00E0 hai m\u1EB7t ph\u1EB3ng l\u1ED3ng ch\u1ED3ng nhau. -->\n <ng-template #childCanvasTpl>\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 [dragFromAnywhere]=\"dragFromAnywhere()\"\n [nestedScrollbar]=\"nestedScrollbar()\"\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 </ng-template>\n\n @if (!item.getComponentOutlet) {\n <ng-container [ngTemplateOutlet]=\"childCanvasTpl\" />\n }\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}.libs-ui-grid-layout-stage>gridster>gridster-item>.libs-ui-grid-layout-block-shell{inset:calc(var(--libs-ui-grid-layout-visual-gap, 0px) / 2);width:calc(100% - var(--libs-ui-grid-layout-visual-gap, 0px));height:calc(100% - var(--libs-ui-grid-layout-visual-gap, 0px));box-sizing:border-box}gridster.libs-ui-grid-layout-drag-anywhere gridster-item{user-select:none;-webkit-user-select:none}\n"] }]
606
788
  }], ctorParameters: () => [], propDecorators: { handlerDocumentKeyDown: [{
607
789
  type: HostListener,
608
790
  args: ['document:keydown', ['$event']]
@@ -610,4 +792,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
610
792
  type: HostListener,
611
793
  args: ['document:click', ['$event']]
612
794
  }] } });
613
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC1sYXlvdXQtY2FudmFzLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvc2VydmljZXMvZ3JpZC1sYXlvdXQvc3JjL2NhbnZhcy9ncmlkLWxheW91dC1jYW52YXMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy11aS9zZXJ2aWNlcy9ncmlkLWxheW91dC9zcmMvY2FudmFzL2dyaWQtbGF5b3V0LWNhbnZhcy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFFVCxZQUFZLEVBR1osZ0JBQWdCLEVBQ2hCLFFBQVEsRUFDUixNQUFNLEVBQ04sTUFBTSxFQUNOLEtBQUssRUFDTCxLQUFLLEVBQ0wsTUFBTSxFQUNOLE1BQU0sRUFDTixTQUFTLEVBQ1QsWUFBWSxHQUNiLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzVGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxjQUFjLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDL0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNsRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQWFqRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQzs7OztBQUVuRjs7Ozs7R0FLRztBQVNILE1BQU0sT0FBTywrQkFBK0I7SUFDMUM7Ozs7O09BS0c7SUFDTSxJQUFJLEdBQUcsS0FBSyxFQUFxQixDQUFDO0lBQzNDLDhEQUE4RDtJQUNyRCxLQUFLLEdB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QyxjQUFjLEVBQUUsc0NBQXNDLEVBQUUsZ0JBQWdCLEVBQUUsZUFBZSxDQUFDLG1CQUduRix1QkFBdUIsQ0FBQyxNQUFNO3dEQStYckMsc0JBQXNCO3NCQUQvQixZQUFZO3VCQUFDLGtCQUFrQixFQUFFLENBQUMsUUFBUSxDQUFDO2dCQXFCbEMsb0JBQW9CO3NCQUQ3QixZQUFZO3VCQUFDLGdCQUFnQixFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENvbXBvbmVudCxcbiAgRWxlbWVudFJlZixcbiAgSG9zdExpc3RlbmVyLFxuICBPbkRlc3Ryb3ksXG4gIFRlbXBsYXRlUmVmLFxuICBWaWV3Q29udGFpbmVyUmVmLFxuICBjb21wdXRlZCxcbiAgZWZmZWN0LFxuICBpbmplY3QsXG4gIGlucHV0LFxuICBtb2RlbCxcbiAgb3V0cHV0LFxuICBzaWduYWwsXG4gIHZpZXdDaGlsZCxcbiAgdmlld0NoaWxkcmVuLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE5nVGVtcGxhdGVPdXRsZXQgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgTGlic1VpQ29tcG9uZW50c0J1dHRvbnNCdXR0b25Db21wb25lbnQgfSBmcm9tICdAbGlicy11aS9jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uJztcbmltcG9ydCB7IEdyaWRzdGVyQ29tcG9uZW50LCBHcmlkc3Rlck1vZHVsZSB9IGZyb20gJ2dyaWRzdGVyMTgnO1xuaW1wb3J0IHsgVHJhbnNsYXRlTW9kdWxlIH0gZnJvbSAnQG5neC10cmFuc2xhdGUvY29yZSc7XG5pbXBvcnQgeyBlbXB0eVJvb3ROb2RlIH0gZnJvbSAnLi4vZGVmaW5lcy9ncmlkc3Rlci1jb25maWcuZGVmaW5lJztcbmltcG9ydCB7IExpYnNVaUdyaWRMYXlvdXRTZXJ2aWNlIH0gZnJvbSAnLi4vZ3JpZC1sYXlvdXQuc2VydmljZSc7XG5pbXBvcnQge1xuICBUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQsXG4gIFRfZ3JpZExheW91dF9hZGRFdmVudCxcbiAgVF9ncmlkTGF5b3V0X2NoYW5nZUV2ZW50LFxuICBUX2dyaWRMYXlvdXRfZHJvcEV2ZW50LFxuICBUX2dyaWRMYXlvdXRfZHJvcEdob3N0LFxuICBUX2dyaWRMYXlvdXRfZXh0ZXJuYWxEcmFnUGF5bG9hZCxcbiAgVF9ncmlkTGF5b3V0X25ld05vZGUsXG4gIFRfZ3JpZExheW91dF9ub2RlLFxuICBUX2dyaWRMYXlvdXRfdG9vbGJhckNvbnRleHQsXG4gIFRfZ3JpZExheW91dF96b29tUHJlc2V0LFxufSBmcm9tICcuLi9pbnRlcmZhY2VzL2dyaWQtbGF5b3V0LmludGVyZmFjZSc7XG5pbXBvcnQgeyBjYWxjdWxhdGVEcm9wR2hvc3QsIHBhcnNlRHJvcFBheWxvYWQgfSBmcm9tICcuLi9kZWZpbmVzL2NvbGxpc2lvbi5kZWZpbmUnO1xuXG4vKipcbiAqIE3hurd0IHBo4bqzbmcga8OpbyB0aOG6oy4gxJDhu4cgcXV5OiBraOG7kWkgY8OzIGBjaGlsZHJlbmAgdGjDrCBydeG7mXQgbsOzIGzhuqFpIGzDoCBt4buZdCBt4bq3dCBwaOG6s25nIG7hu69hLlxuICpcbiAqIE7GoWkgZMO5bmcgS0jDlE5HIGThu7FuZyBjb21wb25lbnQgbsOgeSB0cuG7sWMgdGnhur9wIGNobyB04burbmcga2jhu5FpIOKAlCBjaOG7iSDEkeG6t3QgTeG7mFQgdGjhursg4bufIGfhu5FjIHbDoCB0cnV54buBblxuICogYG5vZGVDb21wb25lbnRUeXBlYCBxdWEgYExpYnNVaUdyaWRMYXlvdXRTZXJ2aWNlLmluaXQoKWAuIFRoxrAgdmnhu4duIHThu7EgZ+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+Pignem9vbURvY2snKTtcbiAgLyoqIG3hu5dpIMO0IG3hu5l0IGNo4buXIGfhuq9uIHJpw6puZyDigJQgY8O5bmcgdGjhu6kgdOG7sSB24bubaSBgY2hpbGRyZW4oKWAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBpdGVtSG9zdHMgPSB2aWV3Q2hpbGRyZW4oJ2l0ZW1Ib3N0JywgeyByZWFkOiBWaWV3Q29udGFpbmVyUmVmIH0pO1xuICAvKipcbiAgICogS2h1w7RuIG3hurd0IHBo4bqzbmcgTOG7kk5HIGPhu6dhIHThu6tuZyBraOG7kWkgY29udGFpbmVyLCDEkeG7gyDEkcawYSB2w6BvIGNvbXBvbmVudCB24buPIGtoaSBraOG7kWkgxJHDsyB04buxIGtoYWlcbiAgICogYGdldENvbXBvbmVudE91dGxldGAuXG4gICAqXG4gICAqIPCflLQgRGFuaCBzw6FjaCBuw6B5IEtIw5RORyBjw7luZyB0aOG7qSB04buxIHbhu5tpIGBjaGlsZHJlbigpYDogYG5nLXRlbXBsYXRlICNjaGlsZENhbnZhc1RwbGAgY2jhu4kgdOG7k24gdOG6oWlcbiAgICogY2hvIGto4buRaSBDw5MgQ09OLiBHaMOpcCB0aGVvIGluZGV4IGzDoCBnw6FuIG5o4bqnbSBraHXDtG4gY+G7p2Ega2jhu5FpIG7DoHkgY2hvIGto4buRaSBraMOhYyDigJQgcGjhuqNpIGzhu41jIGzhuqFpXG4gICAqIMSRw7puZyBjw7luZyDEkWnhu4F1IGtp4buHbiBtw6AgdGVtcGxhdGUgZMO5bmcgKHhlbSBgY2hpbGRDYW52YXNUZW1wbGF0ZU9mYCkuXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGNoaWxkQ2FudmFzVGVtcGxhdGVzID0gdmlld0NoaWxkcmVuPFRlbXBsYXRlUmVmPHVua25vd24+PignY2hpbGRDYW52YXNUcGwnKTtcblxuICAvKiogS2h1bmcgeGVtIHRyxrDhu5tjIHbhu4sgdHLDrSB0aOG6oyB0aOG6uyB04burIGLDqm4gbmdvw6BpIChzbmFwcGVkIGdob3N0IHByZXZpZXcpICovXG4gIHByb3RlY3RlZCByZWFkb25seSBkcm9wR2hvc3QgPSBzaWduYWw8VF9ncmlkTGF5b3V0X2Ryb3BHaG9zdCB8IHVuZGVmaW5lZD4odW5kZWZpbmVkKTtcblxuICBwcml2YXRlIGFjdGl2ZUhvdmVyZWRFbD86IEhUTUxFbGVtZW50O1xuICBwcml2YXRlIHJvb3RHcmlkc3RlckVsPzogSFRNTEVsZW1lbnQ7XG5cbiAgLyoqIE1lbnUgY2jhu41uIHByZXNldCB0aHUgcGjDs25nIMSRYW5nIG3hu58gaGF5IMSRw7NuZyAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaXNNZW51T3BlbiA9IHNpZ25hbDxib29sZWFuPihmYWxzZSk7XG5cbiAgLyoqIEPDoWMgbeG7kWMgdGh1IHBow7NuZyDEkeG7i25oIHPhurVuICovXG4gIHByb3RlY3RlZCByZWFkb25seSB6b29tUHJlc2V0czogQXJyYXk8VF9ncmlkTGF5b3V0X3pvb21QcmVzZXQ+ID0gW1xuICAgIHsgbGFiZWw6ICdpMThuX3pvb21fcHJlc2V0X292ZXJ2aWV3JywgdmFsdWU6IDAuNSB9LFxuICAgIHsgbGFiZWw6ICdpMThuX3pvb21fcHJlc2V0X3NtYWxsX2xhcHRvcCcsIHZhbHVlOiAwLjY3IH0sXG4gICAgeyBsYWJlbDogJ2kxOG5fem9vbV9wcmVzZXRfbGFwdG9wXzEwODBwJywgdmFsdWU6IDAuNzUgfSxcbiAgICB7IGxhYmVsOiAnaTE4bl96b29tX3ByZXNldF9zY3JlZW5fMTA4MHAnLCB2YWx1ZTogMC44NSB9LFxuICAgIHsgbGFiZWw6ICdpMThuX3pvb21fcHJlc2V0X3N0YW5kYXJkJywgdmFsdWU6IDAuOSB9LFxuICAgIHsgbGFiZWw6ICdpMThuX3pvb21fcHJlc2V0X29yaWdpbmFsX3NpemUnLCB2YWx1ZTogMSB9LFxuICAgIHsgbGFiZWw6ICdpMThuX3pvb21fcHJlc2V0X2xhcmdlX3NjcmVlbicsIHZhbHVlOiAxLjEgfSxcbiAgICB7IGxhYmVsOiAnaTE4bl96b29tX3ByZXNldF9oaWdoX2RwaV8yaycsIHZhbHVlOiAxLjI1IH0sXG4gIF07XG5cbiAgLyoqIFPDoXUgY2jhuqVtIGPhu6dhIHRheSBj4bqnbSBrw6lvIOKAlCBjaOG7iSDEkeG7gyBgQGZvcmAgZOG7sW5nIMSR4bunIDYgdGjhurssIGdpw6EgdHLhu4sga2jDtG5nIGTDuW5nIHThu5tpLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZHJhZ0JhckRvdHMgPSBbMCwgMSwgMiwgMywgNCwgNV07XG5cbiAgcHJpdmF0ZSByZWFkb25seSBzZXJ2aWNlID0gaW5qZWN0KExpYnNVaUdyaWRMYXlvdXRTZXJ2aWNlKTtcblxuICAvKiogQ2jhur8gxJHhu5kgdGh1IHBow7NuZyBjw7MgxJFhbmcgaG/huqF0IMSR4buZbmcgdHLDqm4gY2FudmFzIG7DoHkga2jDtG5nIChjaOG7iSByb290IGNhbnZhcyBkZXB0aCAwKSAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaXNab29tQWN0aXZlID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIHJldHVybiB0aGlzLmRlcHRoKCkgPT09IDAgJiYgKHRoaXMuZW5hYmxlWm9vbSgpIHx8IHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmlzWm9vbUVuYWJsZWQoKSk7XG4gIH0pO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBjdXJyZW50Wm9vbSA9IGNvbXB1dGVkKCgpID0+IHRoaXMuc2VydmljZS5ab29tKCkpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgem9vbVBlcmNlbnQgPSBjb21wdXRlZCgoKSA9PiBNYXRoLnJvdW5kKHRoaXMuY3VycmVudFpvb20oKSAqIDEwMCkpO1xuXG4gIC8qKlxuICAgKiBOb2RlIFRI4bqsVCBj4bunYSBt4bq3dCBwaOG6s25nIG7DoHkuXG4gICAqXG4gICAqIPCflLQgTeG6t3QgcGjhurNuZyBH4buQQyAoZGVwdGggMCkgxJHhu41jIGPDonkgdOG7qyBTRVJWSUNFLCBraMO0bmcgZMO5bmcgaW5wdXQuXG4gICAqIE7GoWkgZMO5bmcgdGjGsOG7nW5nIHPhu61hIGPDonkgVOG6oEkgQ0jhu5YgKGB0cmVlU2VydmljZS5jb21wYWN0KHRyZWUpYCkgcuG7k2kgZ+G7jWkgYHJlZnJlc2goKWAsIG7Dqm4gc2lnbmFsXG4gICAqIGByb290KClgIGPhu6dhIGjhu40gZ2nhu68gbmd1ecOqbiB0aGFtIGNoaeG6v3UgdsOgIGlucHV0IGBub2RlYCBraMO0bmcgaOG7gSDEkeG7lWkg4oCUIGBAZm9yYCBraMO0bmcgZOG7sW5nIGzhuqFpXG4gICAqIGBncmlkc3Rlci1pdGVtYCwgbMaw4bubaSDEkeG7qW5nIGltIGTDuSBk4buvIGxp4buHdSDEkcOjIGtow6FjICjEkW8gMjgvMDgvMjAyNjogYuG6pW0gXCJT4bqvcCBn4buNblwiIGtow7RuZyB0w6FjIGThu6VuZykuXG4gICAqIGByZWZyZXNoKClgIGPhu6dhIHNlcnZpY2UgdGhheSB0aGFtIGNoaeG6v3UgY8OieSwgbsOqbiDEkeG7jWMgdOG7qyDEkcOzIG3hu5tpIHRo4bqleSB0aGF5IMSR4buVaS5cbiAgICpcbiAgICogTeG6t3QgcGjhurNuZyBM4buSTkcgduG6q24gZMO5bmcgaW5wdXQ6IG7DsyBuaOG6rW4gbm9kZSBjb24gdOG7qyBt4bq3dCBwaOG6s25nIGNoYSwgduG7kW4gxJHDoyBsw6Agb2JqZWN0IG3hu5tpLlxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGN1cnJlbnROb2RlID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIHRoaXMuc2VydmljZS5WZXJzaW9uKCk7XG5cbiAgICBpZiAodGhpcy5kZXB0aCgpID4gMCkge1xuICAgICAgcmV0dXJuIHRoaXMubm9kZSgpID8/IGVtcHR5Um9vdE5vZGUoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5zZXJ2aWNlLlJvb3QoKSA/PyBlbXB0eVJvb3ROb2RlKCk7XG4gIH0pO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBjaGlsZHJlbiA9IGNvbXB1dGVkKCgpID0+IHRoaXMuY3VycmVudE5vZGUoKS5jaGlsZHJlbiA/PyBbXSk7XG5cbiAgLyoqXG4gICAqIELhu5FpIGPhuqNuaCBjaG8gYHRlbXBsYXRlVG9vbGJhcmAsIGThu7FuZyBz4bq1biB0aGVvIHThu6tuZyBraOG7kWkuXG4gICAqXG4gICAqIEThu7FuZyDhu58gYGNvbXB1dGVkYCBjaOG7qSBraMO0bmcgZ+G7jWkgaMOgbSB0csOqbiB0ZW1wbGF0ZTogZ+G7jWkgaMOgbSB0cm9uZyB0ZW1wbGF0ZSBjaOG6oXkgbOG6oWkgbeG7l2kgdsOybmdcbiAgICogZMOyIHRoYXkgxJHhu5VpIChydWxlIGBmZS1jb2RpbmctY29udmVudGlvbi10ZW1wbGF0ZXNgIG3hu6VjIDYpLlxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHRvb2xiYXJDb250ZXh0cyA9IGNvbXB1dGVkPEFycmF5PFRfZ3JpZExheW91dF90b29sYmFyQ29udGV4dD4+KCgpID0+XG4gICAgdGhpcy5jaGlsZHJlbigpLm1hcCgoaXRlbSkgPT4gKHtcbiAgICAgICRpbXBsaWNpdDogaXRlbSxcbiAgICAgIGlzQ29udGFpbmVyOiB0aGlzLmlzQ29udGFpbmVyKGl0ZW0pLFxuICAgICAgY2FuQWRkSW5zaWRlOiB0aGlzLmNhbkFkZEluc2lkZShpdGVtKSxcbiAgICAgIGNhblRvZ2dsZVR5cGU6IHRoaXMuY2FuVG9nZ2xlVHlwZShpdGVtKSxcbiAgICAgIHJlbW92ZTogKCkgPT4gdGhpcy5vdXRSZW1vdmVOb2RlLmVtaXQoeyBub2RlSWQ6IGl0ZW0uaWQsIG5vZGU6IGl0ZW0sIGNvbmZpcm06ICgpID0+IHRoaXMuc2VydmljZS5yZW1vdmVCbG9jayhpdGVtLmlkKSB9KSxcbiAgICAgIGFkZEluc2lkZTogKCkgPT4gdGhpcy5lbWl0QWRkSW5zaWRlKGl0ZW0pLFxuICAgICAgdG9nZ2xlVHlwZTogKCkgPT4gdGhpcy5lbWl0VG9nZ2xlVHlwZShpdGVtKSxcbiAgICB9KSlcbiAgKTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaXNOZXN0ZWRDYW52YXMgPSBjb21wdXRlZCgoKSA9PiB0aGlzLmRlcHRoKCkgPiAwKTtcblxuICAvKipcbiAgICogS2hlIG5ow6xuIHRo4bqleSBnaeG7r2EgY8OhYyBraOG7kWkgQ+G6pFAgR+G7kEMuIE3hurd0IHBo4bqzbmcgbOG7k25nIHRy4bqjIDAg4oCUIGto4buRaSBjb24gbMOgIGPDoWMgZMOybmcgY+G7p2EgY8O5bmcgbeG7mXRcbiAgICogdGjhurssIHTDoWNoIGNow7puZyByYSBsw6BtIGNow7puZyB0csO0bmcgbmjGsCB0aOG6uyBy4budaS5cbiAgICovXG4gIHByb3RlY3RlZCByZWFkb25seSB2aXN1YWxHYXAgPSBjb21wdXRlZCgoKSA9PiAodGhpcy5kZXB0aCgpID09PSAwID8gdGhpcy5zZXJ2aWNlLnZpc3VhbEdhcFB4IDogMCkpO1xuXG4gIC8qKiB0cuG6p24gbOG7k25nIGNvbnRhaW5lciDigJQga2jhu5FpIMSRw6MgY2jhuqFtIHRy4bqnbiB0aMOsIGtow7RuZyBjaG8gY2h1eeG7g24gdGjDoG5oIGNvbnRhaW5lciAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgbWF4Q29udGFpbmVyRGVwdGggPSBjb21wdXRlZCgoKSA9PiB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5tYXhDb250YWluZXJEZXB0aCgpKTtcblxuICAvKipcbiAgICogTOG7gSB0cm9uZyBj4bunYSBjb250YWluZXIsIHB4LlxuICAgKlxuICAgKiDwn5S0IMSQ4bq3dCB0csOqbiBW4buOIGNvbnRhaW5lciwga2jDtG5nIMSR4bq3dCB2w6BvIGzGsOG7m2kgY29uLiBMxrDhu5tpIGNvbiBnaeG7ryBuZ3V5w6puIGBtYXJnaW46IDJgIG5oxrAgY8WpOyBs4buBXG4gICAqIG7DoHkgY2jhu4kgY28gcnXhu5l0IGzhuqFpIGto4buPaSBiacOqbiB24buPLCBuaOG7nSDEkcOzIHRheSBj4bqnbSDEkeG7lWkga8OtY2ggdGjGsOG7m2MgY+G7p2Ega2jhu5FpIGNvbiBsdcO0biBu4bqxbSB0cm9uZyB04bqnbVxuICAgKiBjaHXhu5l0IChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNikuXG4gICAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY29udGFpbmVyUGFkZGluZyA9IGNvbXB1dGVkKCgpID0+IGAke3RoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmNvbnRhaW5lclBhZGRpbmcoKX1weGApO1xuXG4gIC8qKiBE4bqjaSBrw6lvIGNo4buJIGhp4buHbiDhu58gY2jhur8gxJHhu5kgc+G7rWEg4oCUIGNo4bq/IMSR4buZIHhlbSBraMO0bmcga8OpbyDEkcaw4bujYyBuw6puIGhp4buHbiBk4bqjaSBsw6AgZ8OieSBuaOG6p20uICovXG4gIHByb3RlY3RlZCByZWFkb25seSBpc0VkaXRNb2RlID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5zZXJ2aWNlLklzRWRpdE1vZGUoKSk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGdyaWRPcHRpb25zID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIC8vIMSQ4buNYyBgVmVyc2lvbmAgxJHhu4MgYGluaXQoKWAgbOG6oWkgKMSR4buVaSBjaOG6vyDEkeG7mSwgYuG6rXQvdOG6r3QgY2jhu5NuZyBraOG7kWkpIHNpbmggcmEgb3B0aW9ucyBN4buaSSDigJRcbiAgICAvLyBncmlkc3RlciBjaOG7iSDDoXAgZOG7pW5nIGPhuqV1IGjDrG5oIGtoaSB0aGFtIGNoaeG6v3UgYG9wdGlvbnNgIMSR4buVaS5cbiAgICB0aGlzLnNlcnZpY2UuVmVyc2lvbigpO1xuXG4gICAgcmV0dXJuIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmdyaWRzdGVyT3B0aW9ucyh0aGlzLmRlcHRoKCksICgpID0+IHRoaXMub3V0Q2hhbmdlLmVtaXQoeyByb290OiB0aGlzLmN1cnJlbnROb2RlKCkgfSkpO1xuICB9KTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICAvLyBH4bqvbiBjb21wb25lbnQgY+G7p2EgbsahaSBkw7luZyB2w6BvIHThu6tuZyDDtCwgdsOgIGfhuq9uIEzhuqBJIG3hu5dpIGtoaSBjw6J5IMSR4buVaS5cbiAgICAvLyBEw7luZyBlZmZlY3QgdsOsIGBpdGVtSG9zdHMoKWAgY2jhu4kgY8OzIGdpw6EgdHLhu4sgc2F1IGtoaSBBbmd1bGFyIGThu7FuZyB4b25nIEBmb3IuXG4gICAgZWZmZWN0KCgpID0+IHtcbiAgICAgIGNvbnN0IGhvc3RzID0gdGhpcy5pdGVtSG9zdHMoKTtcbiAgICAgIGNvbnN0IGl0ZW1zID0gdGhpcy5jaGlsZHJlbigpO1xuICAgICAgY29uc3QgY2FudmFzVGVtcGxhdGVzID0gdGhpcy5jaGlsZENhbnZhc1RlbXBsYXRlcygpO1xuXG4gICAgICBpdGVtcy5mb3JFYWNoKChpdGVtLCBpKSA9PiB7XG4gICAgICAgIGNvbnN0IGhvc3QgPSBob3N0c1tpXTtcblxuICAgICAgICBpZiAoIWhvc3QpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBLaOG7kWkgY8OzIGNvbiBsw6AgQ09OVEFJTkVSOiBSVeG7mFQgbsOzIGRvIG3hurd0IHBo4bqzbmcgbOG7k25nIHbhur0uXG4gICAgICAgIC8vXG4gICAgICAgIC8vIPCflLQgTmjGsG5nIGNvbnRhaW5lciBW4bqqTiDEkcaw4bujYyBn4bqvbiBjb21wb25lbnQgbuG6v3UgbsahaSBkw7luZyBjaOG7pyDEkeG7mW5nIGtoYWkgYGdldENvbXBvbmVudE91dGxldGBcbiAgICAgICAgLy8gY2hvIGNow61uaCBraOG7kWkgxJHDsyDigJQgxJHDsyBsw6AgVuG7jiBj4bunYSBraOG7kWkgZ2jDqXA6IGjDoG5nIHRpw6p1IMSR4buBLCBuw7p0IHRodSBn4buNbiwgbuG7gW4uIFbhu48gS0jDlE5HIHRoYXlcbiAgICAgICAgLy8gdGjhur8gbeG6t3QgcGjhurNuZyBs4buTbmc7IG7DsyBOSOG6rE4gbeG6t3QgcGjhurNuZyDEkcOzIHbDoG8gdHJvbmcgcXVhIGlucHV0IGBjaGlsZENhbnZhc2AgdsOgIHThu7EgxJHhurd0IHbDoG9cbiAgICAgICAgLy8gxJHDum5nIGNo4buXIGdp4buvYSB0acOqdSDEkeG7gSB2w6AgcGjhuqduIHRow6JuIGPhu6dhIG3DrG5oLlxuICAgICAgICAvL1xuICAgICAgICAvLyBDb250YWluZXIgS0jDlE5HIGtoYWkgb3V0bGV0IHJpw6puZyB0aMOsIHbhuqtuIGLhu48gcXVhIHkgbmjGsCBjxakg4oCUIGBjb25maWcuZ2V0Q29tcG9uZW50T3V0bGV0YFxuICAgICAgICAvLyBkw7luZyBjaHVuZyBsw6AgxJHhu4MgduG6vSBO4buYSSBEVU5HIGto4buRaSBsw6EsIMSRZW0gw6FwIGNobyBjb250YWluZXIgc+G6vSDEkcOoIG3huqV0IG3hurd0IHBo4bqzbmcgbOG7k25nLlxuICAgICAgICBjb25zdCBpc0NvbnRhaW5lck5vZGUgPSAoaXRlbS5jaGlsZHJlbj8ubGVuZ3RoID8/IDApID4gMDtcblxuICAgICAgICBpZiAoaXNDb250YWluZXJOb2RlICYmICFpdGVtLmdldENvbXBvbmVudE91dGxldCkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmF0dGFjaE5vZGUoXG4gICAgICAgICAgaXRlbSxcbiAgICAgICAgICB0aGlzLmRlcHRoKCkgKyAxLFxuICAgICAgICAgIGhvc3QsXG4gICAgICAgICAgaXNDb250YWluZXJOb2RlID8gdGhpcy5jaGlsZENhbnZhc1RlbXBsYXRlT2YoaXRlbSwgY2FudmFzVGVtcGxhdGVzKSA6IHVuZGVmaW5lZFxuICAgICAgICApO1xuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICAvLyDEkOG7k25nIGLhu5kgbuG6v3UgbW9kZWwgem9vbSBiw6puIG5nb8OgaSB0aGF5IMSR4buVaVxuICAgIGVmZmVjdCgoKSA9PiB7XG4gICAgICBjb25zdCB6ID0gdGhpcy56b29tKCk7XG4gICAgICBpZiAoeiAhPT0gdW5kZWZpbmVkICYmIE1hdGguYWJzKHogLSB0aGlzLnNlcnZpY2UuWm9vbSgpKSA+IDAuMDAxKSB7XG4gICAgICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLnNldFpvb20oeiwgdGhpcy5zdG9yYWdlS2V5KCkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc1pvb21BY3RpdmUoKSB8fCAhdGhpcy5hdXRvRml0T25Mb2FkKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBrZXkgPSB0aGlzLnN0b3JhZ2VLZXkoKTtcbiAgICBsZXQgaGFzU2F2ZWQgPSBmYWxzZTtcbiAgICBpZiAoa2V5ICYmIHR5cGVvZiBsb2NhbFN0b3JhZ2UgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB0cnkge1xuICAgICAgICBoYXNTYXZlZCA9IEJvb2xlYW4obG9jYWxTdG9yYWdlLmdldEl0ZW0oa2V5KSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gaWdub3JlIGxvY2FsU3RvcmFnZSBhY2Nlc3MgZXJyb3JcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIWhhc1NhdmVkKSB7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgY29uc3Qgc2Nyb2xsRWwgPSB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudDtcbiAgICAgICAgaWYgKHNjcm9sbEVsICYmIHNjcm9sbEVsLmNsaWVudFdpZHRoID4gMCAmJiBzY3JvbGxFbC5jbGllbnRXaWR0aCA8IHRoaXMuc3RhZ2VXaWR0aCgpICsgNjQpIHtcbiAgICAgICAgICB0aGlzLmhhbmRsZXJGaXRTY3JlZW4oKTtcbiAgICAgICAgfVxuICAgICAgfSwgNTApO1xuICAgIH1cbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuY2xlYW51cERyYWdHcmlkKCk7XG4gICAgZm9yIChjb25zdCBpdGVtIG9mIHRoaXMuY2hpbGRyZW4oKSkge1xuICAgICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuZGV0YWNoTm9kZShpdGVtLmlkKTtcbiAgICB9XG4gIH1cblxuICAvKiogVuG6vSBs4bqhaSBsxrDhu5tpIOKAlCBn4buNaSBraGkga2h1bmcgbmdvw6BpIMSR4buVaSBrw61jaCB0aMaw4bubYyBob+G6t2MgY8OieSB24burYSDEkeG7lWkgZGnhu4duIHLhu5luZy4gKi9cbiAgZ2V0IEZ1bmN0aW9uQ29udHJvbCgpIHtcbiAgICByZXR1cm4ge1xuICAgICAgdmVMYWlMdW9pOiAoKSA9PiB0aGlzLmdyaWRzdGVyUmVmKCk/Lm9wdGlvbnNDaGFuZ2VkKCksXG4gICAgICB6b29tSW46ICgpID0+IHRoaXMuaGFuZGxlclpvb21JbigpLFxuICAgICAgem9vbU91dDogKCkgPT4gdGhpcy5oYW5kbGVyWm9vbU91dCgpLFxuICAgICAgcmVzZXRab29tOiAoKSA9PiB0aGlzLmhhbmRsZXJSZXNldFpvb20oKSxcbiAgICAgIGZpdFRvU2NyZWVuOiAoKSA9PiB0aGlzLmhhbmRsZXJGaXRTY3JlZW4oKSxcbiAgICAgIHNldFpvb206ICh2YWw6IG51bWJlcikgPT4ge1xuICAgICAgICB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5zZXRab29tKHZhbCwgdGhpcy5zdG9yYWdlS2V5KCkpO1xuICAgICAgICB0aGlzLnN5bmNab29tT3V0KCk7XG4gICAgICB9LFxuICAgICAgLy8gVsO5bmcgY3Xhu5luIGPhu6dhIG3hurd0IHBo4bqzbmcg4oCUIG7GoWkgZMO5bmcgY+G6p24gbsOzIMSR4buDIHThu7EgY3Xhu5luIHThu5tpIG3hu5l0IGto4buRaSwgaG/hurdjIMSRbyB0b+G6oSDEkeG7mSBraGkga8Opby5cbiAgICAgIC8vIEtow7RuZyBt4bufIOG7nyDEkcOieSB0aMOsIG7GoWkgZMO5bmcgcGjhuqNpIGTDsiBi4bqxbmcgdMOqbiBs4bubcCBDU1MgbuG7mWkgYuG7mSwgdsOgIHTDqm4gxJHDsyDEkeG7lWkgbMO6YyBuw6BvIGtow7RuZyBoYXkuXG4gICAgICBzY3JvbGxBcmVhOiAoKTogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQgPT4gdGhpcy5zY3JvbGxBcmVhUmVmKCk/Lm5hdGl2ZUVsZW1lbnQsXG4gICAgfTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyWm9vbUluKCk6IHZvaWQge1xuICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLnpvb21Jbih0aGlzLnpvb21TdGVwKCksIHRoaXMuc3RvcmFnZUtleSgpKTtcbiAgICB0aGlzLnN5bmNab29tT3V0KCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlclpvb21PdXQoKTogdm9pZCB7XG4gICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuem9vbU91dCh0aGlzLnpvb21TdGVwKCksIHRoaXMuc3RvcmFnZUtleSgpKTtcbiAgICB0aGlzLnN5bmNab29tT3V0KCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlclJlc2V0Wm9vbSgpOiB2b2lkIHtcbiAgICB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5yZXNldFpvb20odGhpcy5zdG9yYWdlS2V5KCkpO1xuICAgIHRoaXMuc3luY1pvb21PdXQoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyRml0U2NyZWVuKCk6IHZvaWQge1xuICAgIHRoaXMuaXNNZW51T3Blbi5zZXQoZmFsc2UpO1xuICAgIGNvbnN0IHNjcm9sbEVsID0gdGhpcy5zY3JvbGxBcmVhUmVmKCk/Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgY29uc3QgY29udGFpbmVyV2lkdGggPSBzY3JvbGxFbD8uY2xpZW50V2lkdGggfHwgKHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnID8gd2luZG93LmlubmVyV2lkdGggLSAzMDAgOiAxMjAwKTtcbiAgICB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5maXRUb1NjcmVlbihjb250YWluZXJXaWR0aCwgdGhpcy5zdGFnZVdpZHRoKCksIDQ4LCB0aGlzLnN0b3JhZ2VLZXkoKSk7XG4gICAgdGhpcy5zeW5jWm9vbU91dCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJUb2dnbGVNZW51KCk6IHZvaWQge1xuICAgIHRoaXMuaXNNZW51T3Blbi51cGRhdGUoKG9wZW4pID0+ICFvcGVuKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyU2VsZWN0UHJlc2V0KHZhbHVlOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLmlzTWVudU9wZW4uc2V0KGZhbHNlKTtcbiAgICB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5zZXRab29tKHZhbHVlLCB0aGlzLnN0b3JhZ2VLZXkoKSk7XG4gICAgdGhpcy5zeW5jWm9vbU91dCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGlzQ3VycmVudFByZXNldCh2YWx1ZTogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIE1hdGguYWJzKHRoaXMuY3VycmVudFpvb20oKSAtIHZhbHVlKSA8IDAuMDE7XG4gIH1cblxuICBwcml2YXRlIHN5bmNab29tT3V0KCk6IHZvaWQge1xuICAgIGNvbnN0IHZhbCA9IHRoaXMuc2VydmljZS5ab29tKCk7XG4gICAgdGhpcy5vdXRab29tQ2hhbmdlLmVtaXQodmFsKTtcbiAgICBpZiAodGhpcy56b29tKCkgIT09IHZhbCkge1xuICAgICAgdGhpcy56b29tLnNldCh2YWwpO1xuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyV2hlZWwoZXZlbnQ6IFdoZWVsRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNab29tQWN0aXZlKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoZXZlbnQuY3RybEtleSB8fCBldmVudC5tZXRhS2V5KSB7XG4gICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgaWYgKGV2ZW50LmRlbHRhWSA8IDApIHtcbiAgICAgICAgdGhpcy5oYW5kbGVyWm9vbUluKCk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmRlbHRhWSA+IDApIHtcbiAgICAgICAgdGhpcy5oYW5kbGVyWm9vbU91dCgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIEBIb3N0TGlzdGVuZXIoJ2RvY3VtZW50OmtleWRvd24nLCBbJyRldmVudCddKVxuICBwcm90ZWN0ZWQgaGFuZGxlckRvY3VtZW50S2V5RG93bihldmVudDogS2V5Ym9hcmRFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc1pvb21BY3RpdmUoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChldmVudC5jdHJsS2V5IHx8IGV2ZW50Lm1ldGFLZXkpIHtcbiAgICAgIGlmIChldmVudC5rZXkgPT09ICc9JyB8fCBldmVudC5rZXkgPT09ICcrJykge1xuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICB0aGlzLmhhbmRsZXJab29tSW4oKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnLScgfHwgZXZlbnQua2V5ID09PSAnXycpIHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgdGhpcy5oYW5kbGVyWm9vbU91dCgpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICcwJykge1xuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICB0aGlzLmhhbmRsZXJSZXNldFpvb20oKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkb2N1bWVudDpjbGljaycsIFsnJGV2ZW50J10pXG4gIHByb3RlY3RlZCBoYW5kbGVyRG9jdW1lbnRDbGljayhldmVudDogTW91c2VFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc01lbnVPcGVuKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBkb2NrRWwgPSB0aGlzLnpvb21Eb2NrUmVmKCk/Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgaWYgKGRvY2tFbCAmJiAhZG9ja0VsLmNvbnRhaW5zKGV2ZW50LnRhcmdldCBhcyBOb2RlKSkge1xuICAgICAgdGhpcy5pc01lbnVPcGVuLnNldChmYWxzZSk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJUcmFja05vZGUoX2luZGV4OiBudW1iZXIsIGl0ZW06IFRfZ3JpZExheW91dF9ub2RlKTogc3RyaW5nIHtcbiAgICByZXR1cm4gaXRlbS5pZDtcbiAgfVxuXG4gIC8qKlxuICAgKiDwn5S0IENo4bq3biBgbW91c2Vkb3duYCBu4buVaSBsw6puIGzGsOG7m2kgQ0hBIGtoaSB0aGFvIHTDoWMgdHJvbmcgbMaw4bubaSBjb24uXG4gICAqIEtow7RuZyBjaOG6t24gdGjDrCBj4bqjIGhhaSBsxrDhu5tpIGPDuW5nIG5o4bqtbiBt4buZdCBjw7ogYuG6pW06IGvDqW8ga2jhu5FpIGNvbiBsw6AgbmjhuqVjIGx1w7RuIGNvbnRhaW5lci5cbiAgICovXG4gIC8qKlxuICAgKiBDaOG6t24gYG1vdXNlZG93bmAgY+G7p2Ega2jhu5FpIHRyb25nIGzGsOG7m2kgTOG7kk5HIG7hu5VpIGzDqm4gbMaw4bubaSBjaGEuXG4gICAqXG4gICAqIENo4buJIGNo4bq3biDhu58gbMaw4bubaSBs4buTbmc6IGzGsOG7m2kgY+G6pXAgdHJhbmcga2jDtG5nIGPDsyBjaGEgbsOgbyDEkeG7gyBjaOG6t24sIHbDoCBjaOG6t24g4bufIMSRw7Mgc+G6vSBudeG7kXQgbHXDtG4gdGhhb1xuICAgKiB0w6FjIGPhu6dhIGNow61uaCBuw7MuXG4gICAqL1xuICAvKipcbiAgICog8J+UtCBCYSBoYW5kbGVyIGTGsOG7m2kgxJHDonkgUEjhuqJJIGBzdG9wUHJvcGFnYXRpb25gLlxuICAgKiBOw7p0IG7hurFtIHRyb25nIHbDuW5nIG1hbmcgYGRyYWdIYW5kbGVDbGFzc2AsIGtow7RuZyBjaOG6t24gdGjDrCBjw7ogYuG6pW0gYuG7iyBncmlkc3RlciBoaeG7g3UgbMOgIGLhuq90IMSR4bqndSBrw6lvXG4gICAqIHbDoCBgZGVsYXlTdGFydDogMTYwYCBudeG7kXQgbHXDtG4gYGNsaWNrYCDigJQgbmfGsOG7nWkgZMO5bmcgdGjhuqV5IG5oxrAgbsO6dCBjaOG6v3QuXG4gICAqL1xuICBwcm90ZWN0ZWQgaGFuZGxlclJlbW92ZShldmVudDogRXZlbnQsIG5vZGU6IFRfZ3JpZExheW91dF9ub2RlKTogdm9pZCB7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICB0aGlzLm91dFJlbW92ZU5vZGUuZW1pdCh7IG5vZGVJZDogbm9kZS5pZCwgbm9kZSwgY29uZmlybTogKCkgPT4gdGhpcy5zZXJ2aWNlLnJlbW92ZUJsb2NrKG5vZGUuaWQpIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJBZGRJbnNpZGUoZXZlbnQ6IEV2ZW50LCBub2RlOiBUX2dyaWRMYXlvdXRfbm9kZSk6IHZvaWQge1xuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgdGhpcy5lbWl0QWRkSW5zaWRlKG5vZGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJUb2dnbGVUeXBlKGV2ZW50OiBFdmVudCwgbm9kZTogVF9ncmlkTGF5b3V0X25vZGUpOiB2b2lkIHtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIHRoaXMuZW1pdFRvZ2dsZVR5cGUobm9kZSk7XG4gIH1cblxuICAvKipcbiAgICogQ2jhurduIGPDuiBi4bqlbSB0cm9uZyB0aGFuaCBjw7RuZyBj4bulIG7hu5VpIGzDqm4gZ3JpZHN0ZXIuXG4gICAqXG4gICAqIENo4buJIGBzdG9wUHJvcGFnYXRpb25gLCBLSMOUTkcgYHByZXZlbnREZWZhdWx0YDogbsO6dCBiw6puIHRyb25nIHRlbXBsYXRlIGPhu6dhIG7GoWkgZMO5bmcgduG6q24gY+G6p25cbiAgICogaMOgbmggdmkgbeG6t2MgxJHhu4tuaCBj4bunYSBtw6xuaCAobeG7nyBkcm9wZG93biwgZm9jdXPigKYpLlxuICAgKi9cbiAgcHJvdGVjdGVkIGhhbmRsZXJTdG9wRXZlbnQoZXZlbnQ6IEV2ZW50KTogdm9pZCB7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gIH1cblxuICAvKiogUGjDoXQgYG91dEFkZEJsb2NrSW5zaWRlYCDigJQgZMO5bmcgY2h1bmcgY2hvIG7DunQgbeG6t2MgxJHhu4tuaCB2w6AgYHRlbXBsYXRlVG9vbGJhcmAuICovXG4gIHByaXZhdGUgZW1pdEFkZEluc2lkZShub2RlOiBUX2dyaWRMYXlvdXRfbm9kZSk6IHZvaWQge1xuICAgIHRoaXMub3V0QWRkQmxvY2tJbnNpZGUuZW1pdCh7XG4gICAgICBub2RlSWQ6IG5vZGUuaWQsXG4gICAgICBub2RlLFxuICAgICAgY29uZmlybTogKGl0ZW06IFRfZ3JpZExheW91dF9uZXdOb2RlKSA9PiB0aGlzLnNlcnZpY2UuYWRkQmxvY2soaXRlbSwgbm9kZS5pZCksXG4gICAgfSk7XG4gIH1cblxuICAvKiogUGjDoXQgYG91dFRvZ2dsZVR5cGVgIOKAlCBkw7luZyBjaHVuZyBjaG8gbsO6dCBt4bq3YyDEkeG7i25oIHbDoCBgdGVtcGxhdGVUb29sYmFyYC4gKi9cbiAgcHJpdmF0ZSBlbWl0VG9nZ2xlVHlwZShub2RlOiBUX2dyaWRMYXlvdXRfbm9kZSk6IHZvaWQge1xuICAgIHRoaXMub3V0VG9nZ2xlVHlwZS5lbWl0KHtcbiAgICAgIG5vZGVJZDogbm9kZS5pZCxcbiAgICAgIG5vZGUsXG4gICAgICAvLyBH4buZcCBoYXkgdMOhY2ggc3V5IHJhIHThu6sgY2jDrW5oIGto4buRaTogxJFhbmcgbMOgIGNvbnRhaW5lciB0aMOsIHTDoWNoLCBraMO0bmcgdGjDrCBn4buZcC5cbiAgICAgIGNvbmZpcm06ICgpID0+ICh0aGlzLmlzQ29udGFpbmVyKG5vZGUpID8gdGhpcy5zZXJ2aWNlLnRvU2luZ2xlKG5vZGUuaWQpIDogdGhpcy5zZXJ2aWNlLnRvQ29udGFpbmVyKG5vZGUuaWQpKSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBraOG7kWkgbsOgeSBjw7MgcGjhuqNpIGNvbnRhaW5lciBraMO0bmcgKGPDsyBraOG7kWkgY29uKSAqL1xuICBwcm90ZWN0ZWQgaXNDb250YWluZXIoaXRlbTogVF9ncmlkTGF5b3V0X25vZGUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKGl0ZW0uY2hpbGRyZW4/Lmxlbmd0aCA/PyAwKSA+IDA7XG4gIH1cblxuICAvKipcbiAgICogTOG6pXkgxJHDum5nIGtodcO0biBt4bq3dCBwaOG6s25nIGzhu5NuZyBj4bunYSBN4buYVCBraOG7kWkuXG4gICAqXG4gICAqIPCflLQgYHZpZXdDaGlsZHJlbignY2hpbGRDYW52YXNUcGwnKWAgY2jhu4kgY2jhu6lhIGtodcO0biBj4bunYSBraOG7kWkgQ8OTIENPTiwgY8OybiBgY2hpbGRyZW4oKWAgY2jhu6lhIG3hu41pXG4gICAqIGto4buRaSDigJQgaGFpIGRhbmggc8OhY2ggbOG7h2NoIG5oYXUuIFBo4bqjaSBs4buNYyBgY2hpbGRyZW4oKWAgdGhlbyBjw7luZyDEkWnhu4F1IGtp4buHbiBtw6AgdGVtcGxhdGUgZMO5bmcgcuG7k2lcbiAgICogbeG7m2kgdHJhIHRoZW8gduG7iyB0csOtIHRyb25nIGRhbmggc8OhY2ggxJHDoyBs4buNYzsgZ2jDqXAgdGjhurNuZyB0aGVvIGluZGV4IGPhu6dhIGBjaGlsZHJlbigpYCBsw6AgZ8OhbiBraHXDtG5cbiAgICogY+G7p2Ega2jhu5FpIG7DoHkgY2hvIGto4buRaSBraMOhYywgdsOgIHRo4bq7IGNvbiBoaeG7h24gc2FpIGNo4buXLlxuICAgKi9cbiAgcHJpdmF0ZSBjaGlsZENhbnZhc1RlbXBsYXRlT2YoXG4gICAgaXRlbTogVF9ncmlkTGF5b3V0X25vZGUsXG4gICAgdGVtcGxhdGVzOiByZWFkb25seSBUZW1wbGF0ZVJlZjx1bmtub3duPltdXG4gICk6IFRlbXBsYXRlUmVmPHVua25vd24+IHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBpbmRleCA9IHRoaXMuY2hpbGRyZW4oKVxuICAgICAgLmZpbHRlcigobm9kZSkgPT4gKG5vZGUuY2hpbGRyZW4/Lmxlbmd0aCA/PyAwKSA+IDApXG4gICAgICAuZmluZEluZGV4KChub2RlKSA9PiBub2RlLmlkID09PSBpdGVtLmlkKTtcblxuICAgIHJldHVybiBpbmRleCA+PSAwID8gdGVtcGxhdGVzW2luZGV4XSA6IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKiBjb250YWluZXIgxJHDoyBjaOG6oW0gdHLhuqduIGzhu5NuZyB0aMOsIGtow7RuZyBjaG8gdGjDqm0ga2jhu5FpIHbDoG8gdHJvbmcgbuG7r2EgKi9cbiAgcHJvdGVjdGVkIGNhbkFkZEluc2lkZShpdGVtOiBUX2dyaWRMYXlvdXRfbm9kZSk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmlzQ29udGFpbmVyKGl0ZW0pICYmIHRoaXMuZGVwdGgoKSA8IHRoaXMubWF4Q29udGFpbmVyRGVwdGgoKTtcbiAgfVxuXG4gIC8qKiBraOG7kWkgxJHGoW4g4bufIGPhuqVwIGNoxrBhIGNo4bqhbSB0cuG6p24gbeG7m2kgxJHGsOG7o2MgY2h1eeG7g24gdGjDoG5oIGNvbnRhaW5lciAqL1xuICBwcm90ZWN0ZWQgY2FuVG9nZ2xlVHlwZShpdGVtOiBUX2dyaWRMYXlvdXRfbm9kZSk6IGJvb2xlYW4ge1xuICAgIGlmICh0aGlzLmlzQ29udGFpbmVyKGl0ZW0pKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5kZXB0aCgpIDwgdGhpcy5tYXhDb250YWluZXJEZXB0aCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJCbG9ja1BhcmVudERyYWcoZXZlbnQ6IEV2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmlzTmVzdGVkQ2FudmFzKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyTW91c2VEb3duVHJvbmdMdW9pKGV2ZW50OiBFdmVudCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmRlcHRoKCkgPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyU2VsZWN0Tm9kZShub2RlOiBUX2dyaWRMYXlvdXRfbm9kZSwgZXZlbnQ/OiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgaWYgKGV2ZW50KSB7XG4gICAgICAvLyDwn5S0IENI4buIIGLhu48gcXVhIGtoaSDEkcOtY2ggdGjhu7FjIHPhu7EgbMOgIG7DunQgYuG6pW0uIFRyxrDhu5tjIMSRw6J5IGLhu48gcXVhIGPhuqMgdsO5bmdcbiAgICAgIC8vIGAubGlicy11aS1ncmlkLWxheW91dC10b29sYmFyYDogdGhhbmggbsOgeSBwaOG7pyBn4bqnbiBrw61uIGto4buRaSBuaOG7jyBuw6puIGLhuqVtIHbDoG8ga2jhu5FpIGtow7RuZyBiYW8gZ2nhu51cbiAgICAgIC8vIGNo4buNbiDEkcaw4bujYywgbsahaSBkw7luZyBwaOG6o2kgdOG6r3QgYHBvaW50ZXItZXZlbnRzYCBj4bunYSBj4bqjIHRoYW5oIMSR4buDIGPhu6l1LlxuICAgICAgY29uc3QgdGFyZ2V0ID0gZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50O1xuICAgICAgaWYgKHRhcmdldC5jbG9zZXN0KCdidXR0b24nKSB8fCB0YXJnZXQuY2xvc2VzdCgnW2RhdGEtZ3JpZC1sYXlvdXQtYWN0aW9uXScpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5zZWxlY3RlZE5vZGVJZC5zZXQobm9kZS5pZCk7XG4gICAgdGhpcy5vdXRTZWxlY3ROb2RlLmVtaXQobm9kZSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckNvbmZpZyhldmVudDogRXZlbnQsIG5vZGU6IFRfZ3JpZExheW91dF9ub2RlKTogdm9pZCB7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgY29uc3QgYWN0aW9uRXZlbnQ6IFRfZ3JpZExheW91dF9hY3Rpb25FdmVudCA9IHtcbiAgICAgIG5vZGVJZDogbm9kZS5pZCxcbiAgICAgIG5vZGUsXG4gICAgICBjb25maXJtOiAoKSA9PiB0cnVlLFxuICAgIH07XG4gICAgdGhpcy5vdXRDb25maWdOb2RlLmVtaXQoYWN0aW9uRXZlbnQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJEcmFnT3ZlcihldmVudDogRHJhZ0V2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmVuYWJsZUV4dGVybmFsRHJvcCgpIHx8IHRoaXMuZGVwdGgoKSA+IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICBpZiAoZXZlbnQuZGF0YVRyYW5zZmVyKSB7XG4gICAgICBldmVudC5kYXRhVHJhbnNmZXIuZHJvcEVmZmVjdCA9ICdjb3B5JztcbiAgICB9XG5cbiAgICBjb25zdCBjb250YWluZXIgPSB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudDtcbiAgICBjb25zdCByb290ID0gdGhpcy5zZXJ2aWNlLlJvb3QoKTtcbiAgICBjb25zdCBwYXlsb2FkID0gcGFyc2VEcm9wUGF5bG9hZDxUX2dyaWRMYXlvdXRfZXh0ZXJuYWxEcmFnUGF5bG9hZD4oXG4gICAgICB0aGlzLnNlcnZpY2UuZ2V0RHJhZ1BheWxvYWQoKSBhcyBUX2dyaWRMYXlvdXRfZXh0ZXJuYWxEcmFnUGF5bG9hZCB8IHVuZGVmaW5lZCxcbiAgICAgIGV2ZW50LmRhdGFUcmFuc2ZlcixcbiAgICApO1xuXG4gICAgaWYgKCFjb250YWluZXIgfHwgIXJvb3QgfHwgIXBheWxvYWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCByb290R3JpZHN0ZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3RvcignZ3JpZHN0ZXInKSBhcyBIVE1MRWxlbWVudCB8IG51bGw7XG4gICAgaWYgKHJvb3RHcmlkc3Rlcikge1xuICAgICAgaWYgKCFyb290R3JpZHN0ZXIuY2xhc3NMaXN0LmNvbnRhaW5zKCdkaXNwbGF5LWdyaWQnKSkge1xuICAgICAgICByb290R3JpZHN0ZXIuY2xhc3NMaXN0LmFkZCgnZGlzcGxheS1ncmlkJyk7XG4gICAgICB9XG4gICAgICB0aGlzLnJvb3RHcmlkc3RlckVsID0gcm9vdEdyaWRzdGVyO1xuICAgIH1cblxuICAgIGNvbnN0IGdob3N0ID0gY2FsY3VsYXRlRHJvcEdob3N0KGV2ZW50LCBjb250YWluZXIsIHJvb3QsIHBheWxvYWQsIHRoaXMuc2VydmljZS50b3RhbENvbHVtbnMsIHRoaXMuc2VydmljZS5yb3dTdGVwUHgpO1xuXG4gICAgaWYgKGdob3N0Py5pc0luc2lkZUdyb3VwICYmIGdob3N0LmhvdmVyZWRHcmlkc3Rlcikge1xuICAgICAgaWYgKHRoaXMuYWN0aXZlSG92ZXJlZEVsICE9PSBnaG9zdC5ob3ZlcmVkR3JpZHN0ZXIpIHtcbiAgICAgICAgdGhpcy5jbGVhbnVwSG92ZXJlZEdyb3VwKCk7XG4gICAgICAgIGdob3N0LmhvdmVyZWRHcmlkc3Rlci5jbGFzc0xpc3QuYWRkKCdkaXNwbGF5LWdyaWQnKTtcbiAgICAgICAgdGhpcy5hY3RpdmVIb3ZlcmVkRWwgPSBnaG9zdC5ob3ZlcmVkR3JpZHN0ZXI7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY2xlYW51cEhvdmVyZWRHcm91cCgpO1xuICAgIH1cblxuICAgIHRoaXMuZHJvcEdob3N0LnNldChnaG9zdCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckRyYWdMZWF2ZShldmVudDogRHJhZ0V2ZW50KTogdm9pZCB7XG4gICAgaWYgKHRoaXMuZGVwdGgoKSA+IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGV2ZW50LnJlbGF0ZWRUYXJnZXQpIHtcbiAgICAgIGNvbnN0IGNvbnRhaW5lciA9IHRoaXMuc2Nyb2xsQXJlYVJlZigpPy5uYXRpdmVFbGVtZW50O1xuICAgICAgaWYgKGNvbnRhaW5lciAmJiBjb250YWluZXIuY29udGFpbnMoZXZlbnQucmVsYXRlZFRhcmdldCBhcyBOb2RlKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuZHJvcEdob3N0LnNldCh1bmRlZmluZWQpO1xuICAgIHRoaXMuY2xlYW51cERyYWdHcmlkKCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckRyb3AoZXZlbnQ6IERyYWdFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5lbmFibGVFeHRlcm5hbERyb3AoKSB8fCB0aGlzLmRlcHRoKCkgPiAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgY29uc3QgZ2hvc3QgPSB0aGlzLmRyb3BHaG9zdCgpO1xuICAgIGNvbnN0IHBheWxvYWQgPSBwYXJzZURyb3BQYXlsb2FkPFRfZ3JpZExheW91dF9leHRlcm5hbERyYWdQYXlsb2FkPihcbiAgICAgIHRoaXMuc2VydmljZS5nZXREcmFnUGF5bG9hZCgpIGFzIFRfZ3JpZExheW91dF9leHRlcm5hbERyYWdQYXlsb2FkIHwgdW5kZWZpbmVkLFxuICAgICAgZXZlbnQuZGF0YVRyYW5zZmVyLFxuICAgICk7XG5cbiAgICB0aGlzLmRyb3BHaG9zdC5zZXQodW5kZWZpbmVkKTtcbiAgICB0aGlzLmNsZWFudXBEcmFnR3JpZCgpO1xuICAgIHRoaXMuc2VydmljZS5jbGVhckRyYWdQYXlsb2FkKCk7XG5cbiAgICBpZiAoIWdob3N0IHx8ICFwYXlsb2FkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgdGFyZ2V0UGFyZW50SWQgPSBnaG9zdC5pc0luc2lkZUdyb3VwID8gZ2hvc3QudGFyZ2V0UGFyZW50SWQgOiB1bmRlZmluZWQ7XG4gICAgY29uc3QgaXNJbnNpZGUgPSBCb29sZWFuKHRhcmdldFBhcmVudElkKTtcblxuICAgIGxldCBpdGVtVG9BZGQ6IFRfZ3JpZExheW91dF9uZXdOb2RlO1xuICAgIGNvbnN0IHBheWxvYWRBbnkgPSBwYXlsb2FkIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgIGlmICh0eXBlb2YgcGF5bG9hZEFueVsncmVzb2x2ZU5vZGUnXSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgY29uc3QgcmVzb2x2ZWQgPSAocGF5bG9hZEFueVsncmVzb2x2ZU5vZGUnXSBhcyAoaW5zaWRlOiBib29sZWFuKSA9PiBUX2dyaWRMYXlvdXRfbmV3Tm9kZSB8IHVuZGVmaW5lZCkoaXNJbnNpZGUpO1xuICAgICAgaXRlbVRvQWRkID0gcmVzb2x2ZWQgPz8ge1xuICAgICAgICBjb2xzOiBnaG9zdC5jb2xzLFxuICAgICAgICByb3dzOiBnaG9zdC5yb3dzLFxuICAgICAgICBkYXRhOiBwYXlsb2FkLmRhdGEgPz8gKHBheWxvYWQudHlwZSA/IHsgdHlwZTogcGF5bG9hZC50eXBlLCB0aXRsZTogcGF5bG9hZC50aXRsZSB9IDogdW5kZWZpbmVkKSxcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IG5ld05vZGVEYXRhID0gcGF5bG9hZC5kYXRhID8/IChwYXlsb2FkLnR5cGUgPyB7IHR5cGU6IHBheWxvYWQudHlwZSwgdGl0bGU6IHBheWxvYWQudGl0bGUgfSA6IHVuZGVmaW5lZCk7XG4gICAgICBpdGVtVG9BZGQgPSB7XG4gICAgICAgIGNvbHM6IGdob3N0LmNvbHMsXG4gICAgICAgIHJvd3M6IGdob3N0LnJvd3MsXG4gICAgICAgIGRhdGE6IG5ld05vZGVEYXRhLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICBjb25zdCBhZGRlZE5vZGUgPSB0aGlzLnNlcnZpY2UuaW5zZXJ0QmxvY2tXaXRoQ29sbGlzaW9uKFxuICAgICAgaXRlbVRvQWRkLFxuICAgICAgZ2hvc3QuY29sLFxuICAgICAgZ2hvc3Qucm93LFxuICAgICAgdGFyZ2V0UGFyZW50SWQsXG4gICAgKTtcblxuICAgIGlmIChhZGRlZE5vZGUpIHtcbiAgICAgIHRoaXMuc2VsZWN0ZWROb2RlSWQuc2V0KGFkZGVkTm9kZS5pZCk7XG4gICAgICB0aGlzLm91dERyb3BOb2RlLmVtaXQoe1xuICAgICAgICBub2RlOiBhZGRlZE5vZGUsXG4gICAgICAgIHBhcmVudElkOiB0YXJnZXRQYXJlbnRJZCxcbiAgICAgICAgY29sOiBnaG9zdC5jb2wsXG4gICAgICAgIHJvdzogZ2hvc3Qucm93LFxuICAgICAgICBjb2xzOiBnaG9zdC5jb2xzLFxuICAgICAgICByb3dzOiBnaG9zdC5yb3dzLFxuICAgICAgICByYXdFdmVudDogZXZlbnQsXG4gICAgICAgIHBheWxvYWQsXG4gICAgICB9KTtcbiAgICAgIGNvbnN0IHJvb3QgPSB0aGlzLnNlcnZpY2UuUm9vdCgpO1xuICAgICAgaWYgKHJvb3QpIHtcbiAgICAgICAgdGhpcy5vdXRDaGFuZ2UuZW1pdCh7XG4gICAgICAgICAgcm9vdCxcbiAgICAgICAgICBub2RlSWQ6IGFkZGVkTm9kZS5pZCxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjbGVhbnVwSG92ZXJlZEdyb3VwKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmFjdGl2ZUhvdmVyZWRFbCkge1xuICAgICAgdGhpcy5hY3RpdmVIb3ZlcmVkRWwuY2xhc3NMaXN0LnJlbW92ZSgnZGlzcGxheS1ncmlkJyk7XG4gICAgICB0aGlzLmFjdGl2ZUhvdmVyZWRFbCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGNsZWFudXBEcmFnR3JpZCgpOiB2b2lkIHtcbiAgICB0aGlzLmNsZWFudXBIb3ZlcmVkR3JvdXAoKTtcbiAgICBpZiAodGhpcy5yb290R3JpZHN0ZXJFbCAmJiAhdGhpcy5zaG93R3JpZExpbmVzKCkpIHtcbiAgICAgIHRoaXMucm9vdEdyaWRzdGVyRWwuY2xhc3NMaXN0LnJlbW92ZSgnZGlzcGxheS1ncmlkJyk7XG4gICAgICB0aGlzLnJvb3RHcmlkc3RlckVsID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBjb25zdCBjb250YWluZXIgPSB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudDtcbiAgICBpZiAoY29udGFpbmVyICYmICF0aGlzLnNob3dHcmlkTGluZXMoKSkge1xuICAgICAgY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoJ2dyaWRzdGVyLmRpc3BsYXktZ3JpZCcpLmZvckVhY2goKGVsKSA9PiB7XG4gICAgICAgIGVsLmNsYXNzTGlzdC5yZW1vdmUoJ2Rpc3BsYXktZ3JpZCcpO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG59XG4iLCJAaWYgKGlzWm9vbUFjdGl2ZSgpKSB7XG4gIDwhLS0gS2h1IHbhu7FjIGN14buZbiBjaOG7qWEgY2FudmFzIHN0YWdlIHRodSBwaMOzbmcgLS0+XG4gIDxkaXZcbiAgICAjc2Nyb2xsQXJlYVxuICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1zY3JvbGwtYXJlYSByZWxhdGl2ZSBoLWZ1bGwgbWluLWgtMCB3LWZ1bGwgbWluLXctMCBvdmVyZmxvdy1hdXRvIHAtNiBmbGV4IGp1c3RpZnktc3RhcnQgaXRlbXMtc3RhcnRcIlxuICAgIChtb3VzZWRvd24pPVwiaGFuZGxlck1vdXNlRG93blRyb25nTHVvaSgkZXZlbnQpXCJcbiAgICAod2hlZWwpPVwiaGFuZGxlcldoZWVsKCRldmVudClcIlxuICAgIChkcmFnb3Zlcik9XCJoYW5kbGVyRHJhZ092ZXIoJGV2ZW50KVwiXG4gICAgKGRyYWdsZWF2ZSk9XCJoYW5kbGVyRHJhZ0xlYXZlKCRldmVudClcIlxuICAgIChkcm9wKT1cImhhbmRsZXJEcm9wKCRldmVudClcIj5cbiAgICA8ZGl2XG4gICAgICAjY2FudmFzU3RhZ2VcbiAgICAgIGlkPVwiYmktY2FudmFzLXN0YWdlXCJcbiAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1zdGFnZSByZWxhdGl2ZSBmbGV4IGZsZXgtY29sIHRyYW5zaXRpb24tW3pvb21dIGR1cmF0aW9uLTE1MCBvcmlnaW4tdG9wIHNocmluay0wIG14LWF1dG8gYmctd2hpdGUvODAgYm9yZGVyIGJvcmRlci1zbGF0ZS0yMDAvODAgcm91bmRlZC0yeGwgc2hhZG93LXNtIHAtNFwiXG4gICAgICBbc3R5bGUuem9vbV09XCJjdXJyZW50Wm9vbSgpXCJcbiAgICAgIFtzdHlsZS53aWR0aC5weF09XCJzdGFnZVdpZHRoKClcIlxuICAgICAgW3N0eWxlLm1pbi13aWR0aC5weF09XCJzdGFnZVdpZHRoKClcIlxuICAgICAgW3N0eWxlLm1pbi1oZWlnaHQucHhdPVwic3RhZ2VNaW5IZWlnaHQoKVwiXG4gICAgICBbc3R5bGUuLS1saWJzLXVpLWdyaWQtbGF5b3V0LXZpc3VhbC1nYXAucHhdPVwidmlzdWFsR2FwKClcIj5cbiAgICAgIDwhLS0gU25hcHBlZCBEcm9wIEdob3N0IFByZXZpZXcgLS0+XG4gICAgICBAaWYgKGRyb3BHaG9zdCgpOyBhcyBnaG9zdCkge1xuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJwb2ludGVyLWV2ZW50cy1ub25lIGFic29sdXRlIHotMzAgZmxleCBmbGV4LWNvbCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgb3ZlcmZsb3ctaGlkZGVuIHJvdW5kZWQtbGcgYm9yZGVyLTIgYm9yZGVyLWRhc2hlZCBib3JkZXItYmx1ZS01MDAgYmctYmx1ZS01MDAvMTUgcC0xIHNoYWRvdy1zbSB0cmFuc2l0aW9uLWFsbCBkdXJhdGlvbi03NSBlYXNlLW91dCBiYWNrZHJvcC1ibHVyLVswLjVweF0gc2VsZWN0LW5vbmVcIlxuICAgICAgICAgIFtzdHlsZS5sZWZ0LnB4XT1cImdob3N0LnBpeGVsTGVmdFwiXG4gICAgICAgICAgW3N0eWxlLnRvcC5weF09XCJnaG9zdC5waXhlbFRvcFwiXG4gICAgICAgICAgW3N0eWxlLndpZHRoLnB4XT1cImdob3N0LnBpeGVsV2lkdGhcIlxuICAgICAgICAgIFtzdHlsZS5oZWlnaHQucHhdPVwiZ2hvc3QucGl4ZWxIZWlnaHRcIj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiaW5saW5lLWZsZXggbWF4LXctWzk1JV0gaXRlbXMtY2VudGVyIGdhcC0xIHRydW5jYXRlIHJvdW5kZWQtbWQgYmctYmx1ZS02MDAgcHgtMiBweS0wLjUgdGV4dC1bMTFweF0gZm9udC1zZW1pYm9sZCB0ZXh0LXdoaXRlIHNoYWRvdy14c1wiPlxuICAgICAgICAgICAgQGlmIChnaG9zdC5jYXJkSWNvbkNsYXNzKSB7XG4gICAgICAgICAgICAgIDxpIFtjbGFzc109XCJnaG9zdC5jYXJkSWNvbkNsYXNzICsgJyB0ZXh0LXhzJ1wiPjwvaT5cbiAgICAgICAgICAgIH0gQGVsc2UgaWYgKGdob3N0LmNhcmRJY29uVGV4dCkge1xuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtWzEwcHhdIGZvbnQtYm9sZFwiPnt7IGdob3N0LmNhcmRJY29uVGV4dCB9fTwvc3Bhbj5cbiAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQteHMgZm9udC1ib2xkXCI+e3sgZ2hvc3QuaXNJbnNpZGVHcm91cCA/ICfihrMnIDogJysnIH19PC9zcGFuPlxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0cnVuY2F0ZVwiPnt7IGdob3N0LmNhcmRUaXRsZSB9fTwvc3Bhbj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1bOXB4XSBmb250LW1vbm8gb3BhY2l0eS03NVwiPih7eyBnaG9zdC5jb2xzIH19Yyk8L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICBAaWYgKGdob3N0LnBpeGVsSGVpZ2h0ID49IDgwKSB7XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibXQtMSBmbGV4IG1heC13LVs5NSVdIGl0ZW1zLWNlbnRlciBnYXAtMS41IHRydW5jYXRlIHJvdW5kZWQgYm9yZGVyIGJvcmRlci1ibHVlLTEwMCBiZy13aGl0ZS85NSBweC0yIHB5LTAuNSB0ZXh0LVsxMHB4XSBmb250LW1lZGl1bSB0ZXh0LWJsdWUtNzAwIHNoYWRvdy14c1wiPlxuICAgICAgICAgICAgICBAaWYgKGdob3N0LmlzSW5zaWRlR3JvdXApIHtcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRydW5jYXRlIGZvbnQtYm9sZCB0ZXh0LWJsdWUtOTAwXCI+4oazIHt7IGdob3N0Lmdyb3VwVGl0bGUgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LWJsdWUtMzAwXCI+4oCiPC9zcGFuPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDxzcGFuPkPhu5l0IHt7IGdob3N0LmNvbCArIDEgfX0te3sgZ2hvc3QuY29sICsgZ2hvc3QuY29scyB9fTwvc3Bhbj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgICA8Z3JpZHN0ZXIgY2xhc3M9XCJoLWZ1bGwgdy1mdWxsXCIgW2NsYXNzLmRpc3BsYXktZ3JpZF09XCJzaG93R3JpZExpbmVzKClcIiBbb3B0aW9uc109XCJncmlkT3B0aW9ucygpXCI+XG4gICAgICAgICAgQGZvciAoaXRlbSBvZiBjaGlsZHJlbigpOyB0cmFjayBpdGVtLmlkOyBsZXQgaXRlbUluZGV4ID0gJGluZGV4KSB7XG4gICAgPCEtLSDwn5S0IENo4bq3biBgbW91c2Vkb3duYCB04bqhaSDEkMOCWSBjaG8gbMaw4bubaSBs4buTbmcg4oCUIEPhuqRNIGLhu48gdsOgIEPhuqRNIGNodXnhu4NuIHNhbmcgY2jhu5cga2jDoWMuXG4gICAgICAgICBgaWdub3JlQ29udGVudENsYXNzYCBt4buZdCBtw6xuaCBLSMOUTkcgxJHhu6c6IGBkZWxheVN0YXJ0OiAxNjBgIGzDoG0gTeG7lkkgbMaw4bubaSBsw6puIGzhu4tjaFxuICAgICAgICAgYGRyYWdTdGFydGAgYuG6sW5nIGBzZXRUaW1lb3V0YCBuZ2F5IHThuqFpIGBtb3VzZWRvd25gLCBjw7JuIGBzdG9wUHJvcGFnYXRpb25gIG3DoCBsaWIgZ+G7jWkgYsOqblxuICAgICAgICAgdHJvbmcgYGRyYWdTdGFydGAgY2jhuqF5IHNhdSAxNjBtcyBuw6puIHF1w6EgbXXhu5luIOKAlCBsxrDhu5tpIENIQSDEkcOjIGvhu4twIG5o4bqtbiAoxJFvIDI4LzA4LzIwMjY6IGvDqW9cbiAgICAgICAgIGThuqNpIGto4buRaSBjb24sIGNodeG7l2kgY2xhc3MgZHV54buHdCBsw6puIMSQw5pORyBtw6AgY29udGFpbmVyIHbhuqtuIG5o4bqjeSAwLDAg4oaSIDAsMSkuXG4gICAgICAgICBH4bqvbiDhu58gYGdyaWRzdGVyLWl0ZW1gIGzDoCDEkcO6bmcgYmnDqm46IGzGsOG7m2kgY29uIG5naGUgdHLDqm4gY2jDrW5oIHBo4bqnbiB04butIG7DoHkgbsOqbiDEkcOjIG5o4bqtbiB4b25nLFxuICAgICAgICAgbMaw4bubaSBjaGEg4bufIG5nb8OgaSBi4buLIGNo4bq3bi4gLS0+XG4gICAgPGdyaWRzdGVyLWl0ZW1cbiAgICAgIFtpdGVtXT1cIml0ZW1cIlxuICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtc2VsZWN0ZWRdPVwic2VsZWN0ZWROb2RlSWQoKSA9PT0gaXRlbS5pZFwiXG4gICAgICAoY2xpY2spPVwiaGFuZGxlclNlbGVjdE5vZGUoaXRlbSwgJGV2ZW50KVwiXG4gICAgICAobW91c2Vkb3duKT1cImhhbmRsZXJCbG9ja1BhcmVudERyYWcoJGV2ZW50KVwiPlxuICAgICAgPCEtLSDwn5S0IFbhu48gbsOgeSBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgIOKAlCBD4bqkTSBi4buPLlxuICAgICAgICAgICBgaWdub3JlQ29udGVudDogdHJ1ZWAgYuG6r3QgZ3JpZHN0ZXIgQ0jhu4ggY2hvIGvDqW8ga2hpIGNo4buXIGLhuqVtIG7hurFtIGTGsOG7m2kgbeG7mXQgcGjhuqduIHThu60gY8OzXG4gICAgICAgICAgIGBkcmFnSGFuZGxlQ2xhc3NgLiBLaMO0bmcgY8OzIHbhu48gbsOgeSB0aMOsIGNvbXBvbmVudCBj4bunYSBuxqFpIGTDuW5nICh0aMawIHZp4buHbiBraMO0bmcga2nhu4NtIHNvw6F0XG4gICAgICAgICAgIMSRxrDhu6NjIGNsYXNzIGPhu6dhIG7Dsykgc+G6vSBraMO0bmcgY8OzIHRheSBj4bqnbSBuw6BvLCB2w6AgS0jDlE5HIGto4buRaSBuw6BvIGvDqW8gxJHGsOG7o2Mg4oCUIMSRbyAyOC8wOC8yMDI2OlxuICAgICAgICAgICBrw6lvIDMyMHB4LCB0b+G6oSDEkeG7mSBraOG7kWkgZ2nhu68gbmd1ecOqbiB4PTMseT0wLlxuICAgICAgICAgICBUw6puIGNsYXNzIHRoZW8gY+G6pXA6IGzGsOG7m2kgdHJhbmcgdsOgIGzGsOG7m2kgbOG7k25nIGTDuW5nIGhhaSB0w6puIEtIw4FDIG5oYXUsIG7hur91IGtow7RuZyBsxrDhu5tpIGNoYVxuICAgICAgICAgICBjxaluZyBuaOG6rW4gcmEgdGF5IGPhuqdtIGPhu6dhIGto4buRaSBjb24gdsOgIG5o4bqlYyBsdcO0biBjb250YWluZXIuIC0tPlxuICAgICAgPCEtLSDwn5S0IFbhu48gS0jDlE5HIG1hbmcgYGRyYWdIYW5kbGVDbGFzc2Ag4oCUIEPhuqRNIHRow6ptIGzhuqFpLlxuICAgICAgICAgICBLw6lvIGNo4buJIMSRxrDhu6NjIHBow6lwIGLhuq90IMSR4bqndSB04burIEThuqJJIFhBTkggKG5nxrDhu51pIGTDuW5nIGNo4buRdCAyOC8wOC8yMDI2OiBcInBo4bqjaSBob3ZlciB2w6BvIHbDuW5nXG4gICAgICAgICAgIG3DoHUgeGFuaCB0aMOsIG3hu5tpIGvDqW8gdGjhuqMgxJHGsOG7o2MgY2hvIMSR4buTbmcgbmjhuqV0XCIpLiBDaG8gY+G6oyB24buPIGzDoG0gdGF5IGPhuqdtIHRow6wgYuG6pW0gxJHDonUgY8WpbmdcbiAgICAgICAgICAga8OpbywgdsOgIGto4buRaSBjb24gbuG6sW0gdHJvbmcgY29udGFpbmVyIHPhur0gbmjhuqVjIGx1w7RuIGNvbnRhaW5lciB2w6wgbMaw4bubaSBjaGEgZHV54buHdCBsw6puIGfhurdwXG4gICAgICAgICAgIHRheSBj4bqnbSBj4bunYSB24buPIGNvbnRhaW5lciB0csaw4bubYy5cbiAgICAgICAgICAgVuG7jyBraOG7kWkgY29uIHbhuqtuIGdp4buvIGNsYXNzIENI4bq2TiDEkeG7gyBsxrDhu5tpIGNoYSBk4burbmcgxJHDum5nIOG7nyBiacOqbi4gLS0+XG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1ibG9jay1zaGVsbCBncm91cCByZWxhdGl2ZSBoLWZ1bGwgdy1mdWxsIG1pbi13LTBcIlxuICAgICAgICBbYXR0ci5kYXRhLW5vZGUtaWRdPVwiaXRlbS5pZFwiXG4gICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWJsb2NrLXBhcmVudC1kcmFnXT1cImlzTmVzdGVkQ2FudmFzKClcIj5cbiAgICAgICAgPCEtLSBE4bqjaSBrw6lvOiBoaeG7h24ga2hpIHLDqiBjaHXhu5l0IHbDoG8ga2jhu5FpLCBiYSBjaOG6pW0gbMOgIGThuqV1IGhp4buHdSBrw6lvIHF1ZW4gdGh14buZYy5cbiAgICAgICAgICAgICBOZ8aw4budaSBkw7luZyBj4bqnbiBt4buZdCBjaOG7lyBiw6FtIFLDlSBSw4BORyB0aGF5IHbDrCDEkW/DoW4geGVtIGLhuqVtIMSRw6J1IHRow6wga8OpbyDEkcaw4bujY1xuICAgICAgICAgICAgIChjaOG7kXQgMjgvMDgvMjAyNiwgdGhlbyDEkcO6bmcgZOG6o2kgxJFhbmcgZMO5bmcg4bufIG3DoG4gQ3VzdG9tZXIgMzYwIGPhu6dhIG1vYmlvLXdlYikuIC0tPlxuICAgICAgICBAaWYgKGlzRWRpdE1vZGUoKSkge1xuICAgICAgICAgIDwhLS0g8J+UtCBE4bqjaSBQSOG6okkgbWFuZyB0YXkgY+G6p20gxJDDmk5HIEPhuqRQIGPhu6dhIG7Dsy4gROG6o2kgbmjhuq1uIGNodeG7mXQgKGBwb2ludGVyLWV2ZW50czogYXV0b2ApLFxuICAgICAgICAgICAgICAgbsOqbiBu4bq/dSBraMO0bmcgbWFuZyB0YXkgY+G6p20gdGjDrCBgY2hlY2tEcmFnSGFuZGxlQ2xhc3NgIGR1eeG7h3QgbMOqbiBn4bq3cCB0YXkgY+G6p20gY+G7p2FcbiAgICAgICAgICAgICAgIGNvbnRhaW5lciB2w6AgbmjhuqVjIGNvbnRhaW5lciB0aGF5IHbDrCBraOG7kWkgY29uICjEkW8gMjgvMDgvMjAyNjoga8OpbyBk4bqjaSBraOG7kWkgY29uLFxuICAgICAgICAgICAgICAgY29udGFpbmVyIG5o4bqjeSAwLDAg4oaSIDAsMSBjw7JuIGto4buRaSBjb24gxJHhu6luZyBpbSkuXG4gICAgICAgICAgICAgICDwn5S0IEThuqNpIENI4buIIG1hbmcgdGF5IGPhuqdtLCBD4bqkTSBtYW5nIHRow6ptIGNsYXNzIGNo4bq3bjogYGNoZWNrRHJhZ0hhbmRsZUNsYXNzYCB4w6l0IGhhaVxuICAgICAgICAgICAgICAgY2xhc3MgxJHDsyB0csOqbiBDw5lORyBt4buZdCBub2RlIG7Dqm4gxJHhu4MgY2h1bmcgbMOgIGvDqW8gaOG7j25nICjEkW8gMjgvMDgvMjAyNjogYuG6pW0gZOG6o2kga2jhu5FpXG4gICAgICAgICAgICAgICBjb24ga8OpbyAxNDBweCwga2jhu5FpIMSR4bupbmcgaW0pLiBWaeG7h2MgY2jhurduIGzGsOG7m2kgY2hhIGRvIFbhu44gS0jhu5BJIGxvLiAtLT5cbiAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXJcIlxuICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1kcmFnLWhhbmRsZV09XCIhaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1kcmFnLWhhbmRsZS1jaGlsZF09XCJpc05lc3RlZENhbnZhcygpXCI+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXItZG90c1wiPlxuICAgICAgICAgICAgICBAZm9yIChkb3Qgb2YgZHJhZ0JhckRvdHM7IHRyYWNrIGRvdCkge1xuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1kcmFnLWJhci1kb3RcIj48L3NwYW4+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuXG4gICAgICAgIDwhLS0gS2h1bmcgY2jhu6lhIGNvbXBvbmVudCBj4bunYSBuxqFpIGTDuW5nLlxuICAgICAgICAgICAgIPCflLQgUEjhuqJJIGPDsyBraHVuZyB0aOG6rXQgKGtow7RuZyBwaOG6o2kgYG5nLWNvbnRhaW5lcmAgdHLhu5FuZyk6IG7DsyBsbyBo4buZIG7GoWkgZMO5bmcgcGjhuqduIGzhuqVwIMSR4bqneVxuICAgICAgICAgICAgIMO0IHbDoCB2aeG7gW4gYsOhbyBjaOG6vyDEkeG7mSBz4butYS4gS2jDtG5nIGPDsyBraHVuZyB0aMOsIG3hu5dpIGNvbXBvbmVudCBu4buZaSBkdW5nIGzhuqFpIHBo4bqjaSB04buxIHZp4bq/dFxuICAgICAgICAgICAgIGA6aG9zdCB7IHdpZHRoL2hlaWdodDogMTAwJSB9YCArIHZp4buBbiDEkeG7qXQg4oCUIMSRw7MgbMOgIHZp4buHYyBj4bunYSB0aMawIHZp4buHbiwga2jDtG5nIHBo4bqjaSBj4bunYVxuICAgICAgICAgICAgIG5nxrDhu51pIGTDuW5nIChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNikuXG4gICAgICAgICAgICAgTeG7l2kga2jhu5FpIG3hu5l0IFZpZXdDb250YWluZXJSZWYgcmnDqm5nIG7Dqm4gY29tcG9uZW50IG7hurFtIMSRw7puZyDDtCBj4bunYSBuw7MuIC0tPlxuICAgICAgICA8IS0tIPCflLQgQ29udGFpbmVyIEPDkyBgZ2V0Q29tcG9uZW50T3V0bGV0YDoga2h1bmcgbsOgeSBsw6AgVuG7jiwgdsOgIGNhbnZhcyBjb24gxJHGsOG7o2MgxJHGsGEgVsOATyBUUk9OR1xuICAgICAgICAgICAgIGNvbXBvbmVudCB24buPIHF1YSBpbnB1dCBgY2hpbGRDYW52YXNgICht4buZdCBgVGVtcGxhdGVSZWZgKSDigJQgeGVtIGBuZy10ZW1wbGF0ZSAjY2hpbGRDYW52YXNUcGxgXG4gICAgICAgICAgICAgYsOqbiBkxrDhu5tpLiBW4buPIHThu7EgcXV54bq/dCDEkeG6t3QgY2FudmFzIOG7nyDEkcOidSBnaeG7r2EgdGnDqnUgxJHhu4EgdsOgIHBo4bqnbiB0aMOibiBj4bunYSBuw7MuXG4gICAgICAgICAgICAgVHLGsOG7m2MgxJHDonkgY2FudmFzIGNvbiBsw6AgQU5IIEVNIGPhu6dhIGtodW5nIG7DoHksIG7Dqm4gduG7jyBjaGnhur9tIHRy4buNbiBjaGnhu4F1IGNhbyBy4buTaSDEkeG6qXkgdG/DoG4gYuG7mVxuICAgICAgICAgICAgIHRo4bq7IGNvbiByYSBuZ2/DoGkgdsO5bmcgbmjDrG4gdGjhuqV5ICjEkW8gMTQvMDkvMjAyNjogbeG7jWkga2jhu5FpIGdow6lwIGNo4buJIGPDsm4gbmjDo24gdsOgIHJ14buZdCB0cuG6r25nKS4gLS0+XG4gICAgICAgIDxkaXZcbiAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGVudC1mcmFtZVwiXG4gICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGVudC1mcmFtZS1lZGl0XT1cImlzRWRpdE1vZGUoKSAmJiAhaXNDb250YWluZXIoaXRlbSlcIlxuICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRlbnQtZnJhbWUtZW1wdHldPVwiaXNDb250YWluZXIoaXRlbSkgJiYgIWl0ZW0uZ2V0Q29tcG9uZW50T3V0bGV0XCI+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciAjaXRlbUhvc3QgLz5cbiAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgPCEtLSBE4bqldSBoaeG7h3UgQ09OVEFJTkVSOiBuaMOjbiBnw7NjIHRyw6puLXBo4bqjaSArIHZp4buBbiDEkeG7qXQgYmFvIHJ14buZdC4gS2jDtG5nIGPDsyBk4bqldSBoaeG7h3UgdGjDrFxuICAgICAgICAgICAgIG5nxrDhu51pIGTDuW5nIGtow7RuZyBiaeG6v3Qga2jhu5FpIG7DoG8gY2jhu6lhIMSRxrDhu6NjIGto4buRaSBraMOhYyAoY2jhu5F0IDI4LzA4LzIwMjYsIHRoZW8gxJHDum5nIGPDoWNoXG4gICAgICAgICAgICAgbcOgbiBDdXN0b21lciAzNjAgY+G7p2EgbW9iaW8td2ViIMSRYW5nIGzDoG0pLiAtLT5cbiAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkgJiYgaXNDb250YWluZXIoaXRlbSkpIHtcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1jb250YWluZXItbGFiZWxcIj57eyAnaTE4bl9jb250YWluZXJfYmxvY2snIHwgdHJhbnNsYXRlIH19PC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGFpbmVyLWJvcmRlclwiPjwvZGl2PlxuICAgICAgICB9XG5cbiAgICAgICAgPCEtLSBUaGFuaCBjw7RuZyBj4bulOiBoaeG7h24ga2hpIHLDqiBjaHXhu5l0IHbDoG8ga2jhu5FpLlxuICAgICAgICAgICAgIPCflLQgRMO5bmcgYGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvbmAg4oCUIEPhuqRNIHThu7EgZOG7sW5nIHRo4bq7IGA8YnV0dG9uPmAuXG4gICAgICAgICAgICAgQ29tcG9uZW50IG7DoHkgxJHDoyBjw7Mgc+G6tW4ga2nhu4N1IG7DunQsIGPhu6EgbsO6dCB2w6AgYm9uZyBiw7NuZyBjaMO6IHRow61jaCB0aGVvIMSRw7puZyBi4buZIGdpYW8gZGnhu4duXG4gICAgICAgICAgICAgY+G7p2Egc+G6o24gcGjhuqltOyB04buxIGThu7FuZyBsw6AgbeG7l2kgbsahaSBt4buZdCBraeG7g3UgdsOgIG3huqV0IGx1w7RuIHRvb2x0aXAuIC0tPlxuICAgICAgICBAaWYgKGlzRWRpdE1vZGUoKSAmJiAhaGlkZGVuVG9vbGJhcigpKSB7XG4gICAgICAgICAgQGlmICh0ZW1wbGF0ZVRvb2xiYXIoKSkge1xuICAgICAgICAgICAgPCEtLSBOxqFpIGTDuW5nIHThu7EgduG6vSB0aGFuaCBjw7RuZyBj4bulOiBuaOG6rW4ga2jhu5FpICsgY8OhYyBow6BtIHBow6F0IGV2ZW50IHF1YSBjb250ZXh0LlxuXG4gICAgICAgICAgICAgICAgIPCflLQgS2jhu5FpIGLhu41jIHThu7EgY2jhurduIGBtb3VzZWRvd25gL2BjbGlja2AgbuG7lWkgbMOqbiBncmlkc3Rlci4gVGhhbmggY8O0bmcgY+G7pSBu4bqxbSB0cm9uZ1xuICAgICAgICAgICAgICAgICB2w7luZyBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgOyBraMO0bmcgY2jhurduIHRow6wgY8O6IGLhuqVtIGLhu4sgaGnhu4N1IGzDoCBi4bqvdCDEkeG6p3Uga8OpbyB2w6BcbiAgICAgICAgICAgICAgICAgYGRlbGF5U3RhcnQ6IDE2MGAgbnXhu5F0IGx1w7RuIGBjbGlja2Ag4oCUIG7DunQgdHJvbmcgdGVtcGxhdGUgbmdvw6BpIGLhuqVtIGtow7RuZyDEg25cbiAgICAgICAgICAgICAgICAgKMSRbyAwOS8wOS8yMDI2OiBi4bqlbSBi4bqxbmcgYC5jbGljaygpYCBj4bunYSBET00gdGjDrCBt4bufLCBi4bqlbSBjaHXhu5l0IHRo4bqtdCB0aMOsIGtow7RuZykuXG4gICAgICAgICAgICAgICAgIENo4bq3biDhu58gxJDDglkgxJHhu4MgbsahaSBkw7luZyBraMO0bmcgcGjhuqNpIGJp4bq/dCBi4bqreSBuw6B5LiAtLT5cbiAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXJcIlxuICAgICAgICAgICAgICByb2xlPVwidG9vbGJhclwiXG4gICAgICAgICAgICAgIHRhYmluZGV4PVwiMFwiXG4gICAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICAgIChtb3VzZWRvd24pPVwiaGFuZGxlclN0b3BFdmVudCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKGtleWRvd24pPVwiaGFuZGxlclN0b3BFdmVudCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJTdG9wRXZlbnQoJGV2ZW50KVwiPlxuICAgICAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAgICAgKm5nVGVtcGxhdGVPdXRsZXQ9XCJ0ZW1wbGF0ZVRvb2xiYXIoKSE7IGNvbnRleHQ6IHRvb2xiYXJDb250ZXh0cygpW2l0ZW1JbmRleF1cIiAvPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC10b29sYmFyXCJcbiAgICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtdG9vbGJhci1jaGlsZF09XCJpc05lc3RlZENhbnZhcygpXCI+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1kaW0tYmFkZ2VcIj5cbiAgICAgICAgICAgICAgICB7eyBpdGVtLnJvd3MgfX0gaMOgbmcgw5cge3sgaXRlbS5jb2xzIH19IGPhu5l0XG4gICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgQGlmIChjYW5BZGRJbnNpZGUoaXRlbSkpIHtcbiAgICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgICBbdHlwZV09XCInYnV0dG9uLXRoaXJkJ1wiXG4gICAgICAgICAgICAgICAgICBbaWNvbk9ubHlUeXBlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgW3NpemVCdXR0b25dPVwiJ3NtYWxsZXInXCJcbiAgICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tYWRkJ1wiXG4gICAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiAnVGjDqm0ga2jhu5FpIHbDoG8gdHJvbmcnLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyQWRkSW5zaWRlKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIEBpZiAoY2FuVG9nZ2xlVHlwZShpdGVtKSkge1xuICAgICAgICAgICAgICAgIDxsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25cbiAgICAgICAgICAgICAgICAgIFt0eXBlXT1cIididXR0b24tdGhpcmQnXCJcbiAgICAgICAgICAgICAgICAgIFtpY29uT25seVR5cGVdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgICAgW2NsYXNzSWNvbkxlZnRdPVwiaXNDb250YWluZXIoaXRlbSkgPyAnbGlicy11aS1pY29uLXNwbGl0LWNlbGwnIDogJ2xpYnMtdWktaWNvbi1tZXJnZS1jZWxsJ1wiXG4gICAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiBpc0NvbnRhaW5lcihpdGVtKSA/ICfEkMawYSB24buBIGto4buRaSDEkcahbicgOiAnQ2h1eeG7g24gdGjDoG5oIGto4buRaSBnaMOpcCcsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgICAgKG91dENsaWNrKT1cImhhbmRsZXJUb2dnbGVUeXBlKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDxsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25cbiAgICAgICAgICAgICAgICBbdHlwZV09XCInYnV0dG9uLXRoaXJkJ1wiXG4gICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tc2V0dGluZydcIlxuICAgICAgICAgICAgICAgIFtwb3BvdmVyXT1cInsgY29uZmlnOiB7IGNvbnRlbnQ6ICdD4bqldSBow6xuaCB0aOG6uycsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyQ29uZmlnKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZC1ob3Zlci1kYW5nZXInXCJcbiAgICAgICAgICAgICAgICBbaWNvbk9ubHlUeXBlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgIFtzaXplQnV0dG9uXT1cIidzbWFsbGVyJ1wiXG4gICAgICAgICAgICAgICAgW2NsYXNzSWNvbkxlZnRdPVwiJ2xpYnMtdWktaWNvbi1yZW1vdmUnXCJcbiAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiBpc0NvbnRhaW5lcihpdGVtKSA/ICdYb8OhIGPhuqMga2jhu5FpIGdow6lwJyA6ICdYb8OhIGto4buRaScsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyUmVtb3ZlKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgQGlmIChpdGVtLmNoaWxkcmVuICYmIGl0ZW0uY2hpbGRyZW4ubGVuZ3RoID4gMCkge1xuICAgICAgICAgIDwhLS0gS2jhu5FpIGPDsyBjb24g4oaSIHJ14buZdCBuw7MgbOG6oWkgbMOgIG3hu5l0IG3hurd0IHBo4bqzbmcgbuG7r2EuIMSQ4buHIHF1eSDhu58gxJHDonksIG7GoWkgZMO5bmcga2jDtG5nIHBo4bqjaSBsby5cbiAgICAgICAgICAgICAgIEzhu4EgxJHhurd0IOG7nyDEkMOCWSAoduG7jyBjb250YWluZXIpLCBraMO0bmcgxJHhurd0IHbDoG8gbMaw4bubaSBjb24g4oCUIGzGsOG7m2kgY29uIGdp4buvIG5ndXnDqm4ga2hlIDJweC5cblxuICAgICAgICAgICAgICAg8J+UtCBLaHXDtG4gxJHhurd0IHRyb25nIGBuZy10ZW1wbGF0ZWAgxJHhu4MgxJFpIMSRxrDhu6NjIEhBSSDEkcaw4budbmc6XG4gICAgICAgICAgICAgICAtIENvbnRhaW5lciBLSMOUTkcgY8OzIHbhu48gcmnDqm5nIOKGkiB24bq9IHRo4bqzbmcgdOG6oWkgxJHDonksIHkgbmjGsCB0csaw4bubYy5cbiAgICAgICAgICAgICAgIC0gQ29udGFpbmVyIEPDkyBgZ2V0Q29tcG9uZW50T3V0bGV0YCDihpIgdGjGsCB2aeG7h24gdHJ1eeG7gW4ga2h1w7RuIG7DoHkgeHXhu5FuZyBjb21wb25lbnQgduG7jyBxdWFcbiAgICAgICAgICAgICAgICAgaW5wdXQgYGNoaWxkQ2FudmFzYCwgduG7jyB04buxIMSR4bq3dCBuw7MgdsOgbyBnaeG7r2EgdGnDqnUgxJHhu4EgdsOgIHBo4bqnbiB0aMOibiBj4bunYSBtw6xuaC4gVuG6vSB0aMOqbSDhu59cbiAgICAgICAgICAgICAgICAgxJHDonkgbuG7r2EgbMOgIGhhaSBt4bq3dCBwaOG6s25nIGzhu5NuZyBjaOG7k25nIG5oYXUuIC0tPlxuICAgICAgICAgIDxuZy10ZW1wbGF0ZSAjY2hpbGRDYW52YXNUcGw+XG4gICAgICAgICAgICA8bGlic191aS1zZXJ2aWNlcy1ncmlkX2xheW91dC1jYW52YXNcbiAgICAgICAgICAgICAgY2xhc3M9XCJibG9jayBoLWZ1bGwgdy1mdWxsXCJcbiAgICAgICAgICAgICAgW3N0eWxlLnBhZGRpbmddPVwiY29udGFpbmVyUGFkZGluZygpXCJcbiAgICAgICAgICAgICAgW25vZGVdPVwiaXRlbVwiXG4gICAgICAgICAgICAgIFtkZXB0aF09XCJkZXB0aCgpICsgMVwiXG4gICAgICAgICAgICAgIFtoaWRkZW5Ub29sYmFyXT1cImhpZGRlblRvb2xiYXIoKVwiXG4gICAgICAgICAgICAgIFt0ZW1wbGF0ZVRvb2xiYXJdPVwidGVtcGxhdGVUb29sYmFyKClcIlxuICAgICAgICAgICAgICBbc2VsZWN0ZWROb2RlSWRdPVwic2VsZWN0ZWROb2RlSWQoKVwiXG4gICAgICAgICAgICAgIChvdXRTZWxlY3ROb2RlKT1cIm91dFNlbGVjdE5vZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dENvbmZpZ05vZGUpPVwib3V0Q29uZmlnTm9kZS5lbWl0KCRldmVudClcIlxuICAgICAgICAgICAgICAob3V0Q2hhbmdlKT1cIm91dENoYW5nZS5lbWl0KCRldmVudClcIlxuICAgICAgICAgICAgICAob3V0UmVtb3ZlTm9kZSk9XCJvdXRSZW1vdmVOb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChvdXRBZGRCbG9ja0luc2lkZSk9XCJvdXRBZGRCbG9ja0luc2lkZS5lbWl0KCRldmVudClcIlxuICAgICAgICAgICAgICAob3V0VG9nZ2xlVHlwZSk9XCJvdXRUb2dnbGVUeXBlLmVtaXQoJGV2ZW50KVwiIC8+XG4gICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cblxuICAgICAgICAgIEBpZiAoIWl0ZW0uZ2V0Q29tcG9uZW50T3V0bGV0KSB7XG4gICAgICAgICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImNoaWxkQ2FudmFzVHBsXCIgLz5cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIDwvZ3JpZHN0ZXItaXRlbT5cbiAgfVxuICAgICAgPC9ncmlkc3Rlcj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPCEtLSBUaGFuaCBkb2NrIMSRaeG7gXUga2hp4buDbiB0aHUgcGjDs25nIG7hu5VpIOG7nyBnw7NjIGTGsOG7m2kgdHLDoWkgLS0+XG4gIEBpZiAoc2hvd1pvb21Eb2NrKCkpIHtcbiAgICA8ZGl2XG4gICAgICAjem9vbURvY2tcbiAgICAgIGlkPVwiY2FudmFzLXpvb20tZG9ja1wiXG4gICAgICByb2xlPVwidG9vbGJhclwiXG4gICAgICB0YWJpbmRleD1cIjBcIlxuICAgICAgY2xhc3M9XCJhYnNvbHV0ZSBib3R0b20tNCBsZWZ0LTYgei0zMCBmbGV4IGl0ZW1zLWNlbnRlciBiZy13aGl0ZSByb3VuZGVkLWZ1bGwgc2hhZG93LTJ4bCBib3JkZXItMiBib3JkZXItc2xhdGUtNDAwIHAtMSBzcGFjZS14LTEgc2VsZWN0LW5vbmVcIlxuICAgICAgKG1vdXNlZG93bik9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgKGtleWRvd24pPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgIChjbGljayk9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIj5cbiAgICAgIDwhLS0gVGh1IG5o4buPICgtKSAtLT5cbiAgICAgIDxidXR0b25cbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwidy1bMjhweF0gaC1bMjhweF0gZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgcm91bmRlZC1mdWxsIHRleHQtc2xhdGUtNzAwIGhvdmVyOnRleHQtc2xhdGUtOTAwIGhvdmVyOmJnLXNsYXRlLTIwMCBkaXNhYmxlZDpvcGFjaXR5LTMwIGRpc2FibGVkOmhvdmVyOmJnLXRyYW5zcGFyZW50IHRyYW5zaXRpb24tY29sb3JzIGN1cnNvci1wb2ludGVyXCJcbiAgICAgICAgW2Rpc2FibGVkXT1cImN1cnJlbnRab29tKCkgPD0gem9vbU1pbigpXCJcbiAgICAgICAgW3RpdGxlXT1cIidpMThuX3pvb21fb3V0JyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgIChjbGljayk9XCJoYW5kbGVyWm9vbU91dCgpXCI+XG4gICAgICAgIDxzdmcgY2xhc3M9XCJ3LTMuNSBoLTMuNVwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjIuNVwiIHN0cm9rZS1saW5lY2FwPVwicm91bmRcIj48bGluZSB4MT1cIjVcIiB5MT1cIjEyXCIgeDI9XCIxOVwiIHkyPVwiMTJcIi8+PC9zdmc+XG4gICAgICA8L2J1dHRvbj5cblxuICAgICAgPCEtLSBU4bu3IGzhu4cgJSB2w6AgbWVudSBwcmVzZXQgLS0+XG4gICAgICA8ZGl2IGNsYXNzPVwicmVsYXRpdmVcIj5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgIGNsYXNzPVwiaC1bMjhweF0gcHgtMiBmbGV4IGl0ZW1zLWNlbnRlciBnYXAtMSByb3VuZGVkLWZ1bGwgdGV4dC14cyBmb250LXNlbWlib2xkIHRleHQtc2xhdGUtNzAwIGhvdmVyOmJnLXNsYXRlLTIwMCB0cmFuc2l0aW9uLWNvbG9ycyBjdXJzb3ItcG9pbnRlclwiXG4gICAgICAgICAgW3RpdGxlXT1cIidpMThuX3pvb21fb3B0aW9ucycgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgIChjbGljayk9XCJoYW5kbGVyVG9nZ2xlTWVudSgpXCI+XG4gICAgICAgICAgPHNwYW4+e3sgem9vbVBlcmNlbnQoKSB9fSU8L3NwYW4+XG4gICAgICAgICAgPHN2ZyBjbGFzcz1cInctWzEycHhdIGgtWzEycHhdIHRleHQtc2xhdGUtNDAwIHRyYW5zaXRpb24tdHJhbnNmb3JtIGR1cmF0aW9uLTE1MFwiIFtjbGFzcy5yb3RhdGUtMTgwXT1cImlzTWVudU9wZW4oKVwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjIuNVwiIHN0cm9rZS1saW5lY2FwPVwicm91bmRcIiBzdHJva2UtbGluZWpvaW49XCJyb3VuZFwiPjxwb2x5bGluZSBwb2ludHM9XCI2IDkgMTIgMTUgMTggOVwiLz48L3N2Zz5cbiAgICAgICAgPC9idXR0b24+XG5cbiAgICAgICAgPCEtLSBNZW51IGRyb3Bkb3duIGPDoWMgbeG7kWMgdGh1IHBow7NuZyAtLT5cbiAgICAgICAgQGlmIChpc01lbnVPcGVuKCkpIHtcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiYWJzb2x1dGUgYm90dG9tLWZ1bGwgbWItMiBsZWZ0LTAgdy1bMjU2cHhdIGJnLXdoaXRlIHJvdW5kZWQteGwgc2hhZG93LXhsIGJvcmRlciBib3JkZXItc2xhdGUtMjAwIHB5LTEuNSB6LTQwIHRleHQteHMgc2VsZWN0LW5vbmVcIj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJweC0zIHB5LTEgdGV4dC1bMTFweF0gZm9udC1zZW1pYm9sZCB0ZXh0LXNsYXRlLTQwMCB1cHBlcmNhc2UgdHJhY2tpbmctd2lkZXJcIj5cbiAgICAgICAgICAgICAge3sgJ2kxOG5fem9vbV9jYW52YXNfd2lkdGgnIHwgdHJhbnNsYXRlOiB7IHdpZHRoOiBzdGFnZVdpZHRoKCkgfSB9fVxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICBjbGFzcz1cInctZnVsbCBweC0zIHB5LTIgdGV4dC1sZWZ0IGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktYmV0d2VlbiBob3ZlcjpiZy1zbGF0ZS01MCB0cmFuc2l0aW9uLWNvbG9ycyBjdXJzb3ItcG9pbnRlclwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJoYW5kbGVyRml0U2NyZWVuKClcIj5cbiAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGl0ZW1zLWNlbnRlciBnYXAtMiB0ZXh0LXNsYXRlLTcwMCBmb250LW1lZGl1bVwiPlxuICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9XCJ3LTMuNSBoLTMuNSB0ZXh0LXNsYXRlLTYwMFwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjJcIj48cG9seWxpbmUgcG9pbnRzPVwiMTUgMyAyMSAzIDIxIDlcIi8+PHBvbHlsaW5lIHBvaW50cz1cIjkgMjEgMyAyMSAzIDE1XCIvPjxsaW5lIHgxPVwiMjFcIiB5MT1cIjNcIiB4Mj1cIjE0XCIgeTI9XCIxMFwiLz48bGluZSB4MT1cIjNcIiB5MT1cIjIxXCIgeDI9XCIxMFwiIHkyPVwiMTRcIi8+PC9zdmc+XG4gICAgICAgICAgICAgICAge3sgJ2kxOG5fem9vbV9maXRfc2NyZWVuJyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1bMTBweF0gYmctYmx1ZS01MCB0ZXh0LWJsdWUtNjAwIHB4LTEuNSBweS0wLjUgcm91bmRlZCBmb250LW1lZGl1bVwiPnt7ICdpMThuX2F1dG8nIHwgdHJhbnNsYXRlIH19PC9zcGFuPlxuICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiaC1weCBiZy1zbGF0ZS0xMDAgbXktMVwiPjwvZGl2PlxuICAgICAgICAgICAgQGZvciAocHJlc2V0IG9mIHpvb21QcmVzZXRzOyB0cmFjayBwcmVzZXQudmFsdWUpIHtcbiAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgIGNsYXNzPVwidy1mdWxsIHB4LTMgcHktMS41IHRleHQtbGVmdCBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWJldHdlZW4gaG92ZXI6Ymctc2xhdGUtNTAgdHJhbnNpdGlvbi1jb2xvcnMgY3Vyc29yLXBvaW50ZXJcIlxuICAgICAgICAgICAgICAgIFtjbGFzcy50ZXh0LWJsdWUtNjAwXT1cImlzQ3VycmVudFByZXNldChwcmVzZXQudmFsdWUpXCJcbiAgICAgICAgICAgICAgICBbY2xhc3MuZm9udC1zZW1pYm9sZF09XCJpc0N1cnJlbnRQcmVzZXQocHJlc2V0LnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgW2NsYXNzLmJnLWJsdWUtNTBdPVwiaXNDdXJyZW50UHJlc2V0KHByZXNldC52YWx1ZSlcIlxuICAgICAgICAgICAgICAgIChjbGljayk9XCJoYW5kbGVyU2VsZWN0UHJlc2V0KHByZXNldC52YWx1ZSlcIj5cbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtc2xhdGUtNzAwXCIgW2NsYXNzLnRleHQtYmx1ZS02MDBdPVwiaXNDdXJyZW50UHJlc2V0KHByZXNldC52YWx1ZSlcIj5cbiAgICAgICAgICAgICAgICAgIHt7IHByZXNldC5sYWJlbCB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgICBAaWYgKGlzQ3VycmVudFByZXNldChwcmVzZXQudmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPVwidy0zLjUgaC0zLjUgdGV4dC1ibHVlLTYwMFwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjIuNVwiIHN0cm9rZS1saW5lY2FwPVwicm91bmRcIiBzdHJva2UtbGluZWpvaW49XCJyb3VuZFwiPjxwb2x5bGluZSBwb2ludHM9XCIyMCA2IDkgMTcgNCAxMlwiLz48L3N2Zz5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgfVxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cblxuICAgICAgPCEtLSBQaMOzbmcgdG8gKCspIC0tPlxuICAgICAgPGJ1dHRvblxuICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgY2xhc3M9XCJ3LVsyOHB4XSBoLVsyOHB4XSBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciByb3VuZGVkLWZ1bGwgdGV4dC1zbGF0ZS03MDAgaG92ZXI6dGV4dC1zbGF0ZS05MDAgaG92ZXI6Ymctc2xhdGUtMjAwIGRpc2FibGVkOm9wYWNpdHktMzAgZGlzYWJsZWQ6aG92ZXI6YmctdHJhbnNwYXJlbnQgdHJhbnNpdGlvbi1jb2xvcnMgY3Vyc29yLXBvaW50ZXJcIlxuICAgICAgICBbZGlzYWJsZWRdPVwiY3VycmVudFpvb20oKSA+PSB6b29tTWF4KClcIlxuICAgICAgICBbdGl0bGVdPVwiJ2kxOG5fem9vbV9pbicgfCB0cmFuc2xhdGVcIlxuICAgICAgICAoY2xpY2spPVwiaGFuZGxlclpvb21JbigpXCI+XG4gICAgICAgIDxzdmcgY2xhc3M9XCJ3LTMuNSBoLTMuNVwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjIuNVwiIHN0cm9rZS1saW5lY2FwPVwicm91bmRcIj48bGluZSB4MT1cIjEyXCIgeTE9XCI1XCIgeDI9XCIxMlwiIHkyPVwiMTlcIi8+PGxpbmUgeDE9XCI1XCIgeTE9XCIxMlwiIHgyPVwiMTlcIiB5Mj1cIjEyXCIvPjwvc3ZnPlxuICAgICAgPC9idXR0b24+XG5cbiAgICAgIDwhLS0gUGjDom4gY8OhY2ggLS0+XG4gICAgICA8ZGl2IGNsYXNzPVwiaC1bMTZweF0gdy1weCBiZy1zbGF0ZS0yMDBcIj48L2Rpdj5cblxuICAgICAgPCEtLSBOw7p0IFbhu6thIG3DoG4gaMOsbmggbmhhbmggLS0+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICBjbGFzcz1cImgtWzI4cHhdIHNocmluay0wIHB4LTIuNSBmbGV4IGl0ZW1zLWNlbnRlciBnYXAtMSByb3VuZGVkLWZ1bGwgdGV4dC14cyB0ZXh0LXNsYXRlLTcwMCBob3Zlcjp0ZXh0LXNsYXRlLTkwMCBob3ZlcjpiZy1zbGF0ZS0yMDAgdHJhbnNpdGlvbi1jb2xvcnMgY3Vyc29yLXBvaW50ZXJcIlxuICAgICAgICBbdGl0bGVdPVwiJ2kxOG5fem9vbV9maXRfc2NyZWVuX3Rvb2x0aXAnIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJGaXRTY3JlZW4oKVwiPlxuICAgICAgICA8c3ZnIGNsYXNzPVwidy0zLjUgaC0zLjVcIiB2aWV3Qm94PVwiMCAwIDI0IDI0XCIgZmlsbD1cIm5vbmVcIiBzdHJva2U9XCJjdXJyZW50Q29sb3JcIiBzdHJva2Utd2lkdGg9XCIyXCI+PHBvbHlsaW5lIHBvaW50cz1cIjE1IDMgMjEgMyAyMSA5XCIvPjxwb2x5bGluZSBwb2ludHM9XCI5IDIxIDMgMjEgMyAxNVwiLz48bGluZSB4MT1cIjIxXCIgeTE9XCIzXCIgeDI9XCIxNFwiIHkyPVwiMTBcIi8+PGxpbmUgeDE9XCIzXCIgeTE9XCIyMVwiIHgyPVwiMTBcIiB5Mj1cIjE0XCIvPjwvc3ZnPlxuICAgICAgICA8c3BhbiBjbGFzcz1cIndoaXRlc3BhY2Utbm93cmFwIGZvbnQtbWVkaXVtXCI+e3sgJ2kxOG5fem9vbV9maXRfc2NyZWVuJyB8IHRyYW5zbGF0ZSB9fTwvc3Bhbj5cbiAgICAgIDwvYnV0dG9uPlxuXG4gICAgICA8IS0tIE7DunQgxJDhurd0IGzhuqFpIDEwMCUgKGNo4buJIGhp4buHbiBraGkga2jDoWMgMTAwJSkgLS0+XG4gICAgICBAaWYgKHpvb21QZXJjZW50KCkgIT09IDEwMCkge1xuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgY2xhc3M9XCJoLVsyOHB4XSBweC0yIGZsZXggaXRlbXMtY2VudGVyIGdhcC0xIHJvdW5kZWQtZnVsbCB0ZXh0LXhzIGZvbnQtc2VtaWJvbGQgdGV4dC1ibHVlLTYwMCBob3ZlcjpiZy1ibHVlLTUwIHRyYW5zaXRpb24tY29sb3JzIGN1cnNvci1wb2ludGVyXCJcbiAgICAgICAgICBbdGl0bGVdPVwiJ2kxOG5fem9vbV9yZXNldF90b29sdGlwJyB8IHRyYW5zbGF0ZTogeyB3aWR0aDogc3RhZ2VXaWR0aCgpLCBoZWlnaHQ6IHN0YWdlTWluSGVpZ2h0KCkgfVwiXG4gICAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJSZXNldFpvb20oKVwiPlxuICAgICAgICAgIDxzdmcgY2xhc3M9XCJ3LVsxMnB4XSBoLVsxMnB4XVwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjJcIiBzdHJva2UtbGluZWNhcD1cInJvdW5kXCIgc3Ryb2tlLWxpbmVqb2luPVwicm91bmRcIj48cG9seWxpbmUgcG9pbnRzPVwiMSA0IDEgMTAgNyAxMFwiLz48cGF0aCBkPVwiTTMuNTEgMTVhOSA5IDAgMSAwIDIuMTMtOS4zNkwxIDEwXCIvPjwvc3ZnPlxuICAgICAgICAgIDxzcGFuPjEwMCU8L3NwYW4+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgfVxuICAgIDwvZGl2PlxuICB9XG59IEBlbHNlIHtcbiAgPCEtLSDwn5S0IGByZWxhdGl2ZWAgbMOgIELhuq5UIEJV4buYQywgQ+G6pE0gYuG7jy5cbiAgICAgICBncmlkc3RlciDEkeG7lWkgduG7iyB0csOtIGNodeG7mXQgdGjDoG5oIMO0IGzGsOG7m2kgYuG6sW5nIGBlLmNsaWVudFkgKyAoZWwuc2Nyb2xsVG9wIC0gZWwub2Zmc2V0VG9wKWAg4oCUIGPDtG5nXG4gICAgICAgdGjhu6ljIMSRw7MgY2jhu4kgxJHDum5nIGtoaSBgb2Zmc2V0UGFyZW50YCBj4bunYSA8Z3JpZHN0ZXI+IENIw41OSCBMw4Aga2jhu5FpIGN14buZbiBuw6B5LiBUaGnhur91IGByZWxhdGl2ZWAgdGjDrFxuICAgICAgIGBvZmZzZXRQYXJlbnRgIG5o4bqjeSBsw6puIGto4buRaSBiYW8gbmdvw6BpOiBgb2Zmc2V0VG9wYCB0cuG6oyAxNiB0cm9uZyBraGkgbOG7h2NoIHRo4bqtdCBsw6AgNjUg4oaSIGdyaWRzdGVyXG4gICAgICAgdMOtbmggw7Qgc2FpIDQ5cHgsIMO0IGNo4budIMSR4bq3dCBoaeG7h24gbOG7h2NoIGto4buPaSBjb24gdHLhu48gbsOqbiBrw6lvIHRo4bqjIGtow7RuZyDEkeG7lWkgxJHGsOG7o2MgduG7iyB0csOtLCB2w6Aga8OpbyBtw6lwXG4gICAgICAgY8WpbmcgdMOtbmggc2FpIMSRaeG7g20gZOG7q25nLiAtLT5cbiAgPGRpdlxuICAgICNzY3JvbGxBcmVhXG4gICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LXNjcm9sbC1hcmVhIHJlbGF0aXZlIGgtZnVsbCBtaW4taC0wIHctZnVsbCBtaW4tdy0wXCJcbiAgICBbY2xhc3Mub3ZlcmZsb3cteS1hdXRvXT1cIiFpc05lc3RlZENhbnZhcygpXCJcbiAgICAobW91c2Vkb3duKT1cImhhbmRsZXJNb3VzZURvd25Ucm9uZ0x1b2koJGV2ZW50KVwiXG4gICAgKGRyYWdvdmVyKT1cImhhbmRsZXJEcmFnT3ZlcigkZXZlbnQpXCJcbiAgICAoZHJhZ2xlYXZlKT1cImhhbmRsZXJEcmFnTGVhdmUoJGV2ZW50KVwiXG4gICAgKGRyb3ApPVwiaGFuZGxlckRyb3AoJGV2ZW50KVwiPlxuICAgIDwhLS0gU25hcHBlZCBEcm9wIEdob3N0IFByZXZpZXcgLS0+XG4gICAgQGlmIChkcm9wR2hvc3QoKTsgYXMgZ2hvc3QpIHtcbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJwb2ludGVyLWV2ZW50cy1ub25lIGFic29sdXRlIHotMzAgZmxleCBmbGV4LWNvbCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgb3ZlcmZsb3ctaGlkZGVuIHJvdW5kZWQtbGcgYm9yZGVyLTIgYm9yZGVyLWRhc2hlZCBib3JkZXItYmx1ZS01MDAgYmctYmx1ZS01MDAvMTUgcC0xIHNoYWRvdy1zbSB0cmFuc2l0aW9uLWFsbCBkdXJhdGlvbi03NSBlYXNlLW91dCBiYWNrZHJvcC1ibHVyLVswLjVweF0gc2VsZWN0LW5vbmVcIlxuICAgICAgICBbc3R5bGUubGVmdC5weF09XCJnaG9zdC5waXhlbExlZnRcIlxuICAgICAgICBbc3R5bGUudG9wLnB4XT1cImdob3N0LnBpeGVsVG9wXCJcbiAgICAgICAgW3N0eWxlLndpZHRoLnB4XT1cImdob3N0LnBpeGVsV2lkdGhcIlxuICAgICAgICBbc3R5bGUuaGVpZ2h0LnB4XT1cImdob3N0LnBpeGVsSGVpZ2h0XCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJpbmxpbmUtZmxleCBtYXgtdy1bOTUlXSBpdGVtcy1jZW50ZXIgZ2FwLTEgdHJ1bmNhdGUgcm91bmRlZC1tZCBiZy1ibHVlLTYwMCBweC0yIHB5LTAuNSB0ZXh0LVsxMXB4XSBmb250LXNlbWlib2xkIHRleHQtd2hpdGUgc2hhZG93LXhzXCI+XG4gICAgICAgICAgQGlmIChnaG9zdC5jYXJkSWNvbkNsYXNzKSB7XG4gICAgICAgICAgICA8aSBbY2xhc3NdPVwiZ2hvc3QuY2FyZEljb25DbGFzcyArICcgdGV4dC14cydcIj48L2k+XG4gICAgICAgICAgfSBAZWxzZSBpZiAoZ2hvc3QuY2FyZEljb25UZXh0KSB7XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtWzEwcHhdIGZvbnQtYm9sZFwiPnt7IGdob3N0LmNhcmRJY29uVGV4dCB9fTwvc3Bhbj5cbiAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC14cyBmb250LWJvbGRcIj57eyBnaG9zdC5pc0luc2lkZUdyb3VwID8gJ+KGsycgOiAnKycgfX08L3NwYW4+XG4gICAgICAgICAgfVxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwidHJ1bmNhdGVcIj57eyBnaG9zdC5jYXJkVGl0bGUgfX08L3NwYW4+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LVs5cHhdIGZvbnQtbW9ubyBvcGFjaXR5LTc1XCI+KHt7IGdob3N0LmNvbHMgfX1jKTwvc3Bhbj5cbiAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgQGlmIChnaG9zdC5waXhlbEhlaWdodCA+PSA4MCkge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJtdC0xIGZsZXggbWF4LXctWzk1JV0gaXRlbXMtY2VudGVyIGdhcC0xLjUgdHJ1bmNhdGUgcm91bmRlZCBib3JkZXIgYm9yZGVyLWJsdWUtMTAwIGJnLXdoaXRlLzk1IHB4LTIgcHktMC41IHRleHQtWzEwcHhdIGZvbnQtbWVkaXVtIHRleHQtYmx1ZS03MDAgc2hhZG93LXhzXCI+XG4gICAgICAgICAgICBAaWYgKGdob3N0LmlzSW5zaWRlR3JvdXApIHtcbiAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0cnVuY2F0ZSBmb250LWJvbGQgdGV4dC1ibHVlLTkwMFwiPuKGsyB7eyBnaG9zdC5ncm91cFRpdGxlIH19PC9zcGFuPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtYmx1ZS0zMDBcIj7igKI8L3NwYW4+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICA8c3Bhbj5D4buZdCB7eyBnaG9zdC5jb2wgKyAxIH19LXt7IGdob3N0LmNvbCArIGdob3N0LmNvbHMgfX08L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgICA8Z3JpZHN0ZXIgY2xhc3M9XCJoLWZ1bGwgdy1mdWxsXCIgW2NsYXNzLmRpc3BsYXktZ3JpZF09XCJzaG93R3JpZExpbmVzKClcIiBbb3B0aW9uc109XCJncmlkT3B0aW9ucygpXCI+XG4gICAgICAgIEBmb3IgKGl0ZW0gb2YgY2hpbGRyZW4oKTsgdHJhY2sgaXRlbS5pZDsgbGV0IGl0ZW1JbmRleCA9ICRpbmRleCkge1xuICAgIDwhLS0g8J+UtCBDaOG6t24gYG1vdXNlZG93bmAgdOG6oWkgxJDDglkgY2hvIGzGsOG7m2kgbOG7k25nIOKAlCBD4bqkTSBi4buPIHbDoCBD4bqkTSBjaHV54buDbiBzYW5nIGNo4buXIGtow6FjLlxuICAgICAgICAgYGlnbm9yZUNvbnRlbnRDbGFzc2AgbeG7mXQgbcOsbmggS0jDlE5HIMSR4bunOiBgZGVsYXlTdGFydDogMTYwYCBsw6BtIE3hu5ZJIGzGsOG7m2kgbMOqbiBs4buLY2hcbiAgICAgICAgIGBkcmFnU3RhcnRgIGLhurFuZyBgc2V0VGltZW91dGAgbmdheSB04bqhaSBgbW91c2Vkb3duYCwgY8OybiBgc3RvcFByb3BhZ2F0aW9uYCBtw6AgbGliIGfhu41pIGLDqm5cbiAgICAgICAgIHRyb25nIGBkcmFnU3RhcnRgIGNo4bqheSBzYXUgMTYwbXMgbsOqbiBxdcOhIG114buZbiDigJQgbMaw4bubaSBDSEEgxJHDoyBr4buLcCBuaOG6rW4gKMSRbyAyOC8wOC8yMDI2OiBrw6lvXG4gICAgICAgICBk4bqjaSBraOG7kWkgY29uLCBjaHXhu5dpIGNsYXNzIGR1eeG7h3QgbMOqbiDEkMOaTkcgbcOgIGNvbnRhaW5lciB24bqrbiBuaOG6o3kgMCwwIOKGkiAwLDEpLlxuICAgICAgICAgR+G6r24g4bufIGBncmlkc3Rlci1pdGVtYCBsw6AgxJHDum5nIGJpw6puOiBsxrDhu5tpIGNvbiBuZ2hlIHRyw6puIGNow61uaCBwaOG6p24gdOG7rSBuw6B5IG7Dqm4gxJHDoyBuaOG6rW4geG9uZyxcbiAgICAgICAgIGzGsOG7m2kgY2hhIOG7nyBuZ2/DoGkgYuG7iyBjaOG6t24uIC0tPlxuICAgIDxncmlkc3Rlci1pdGVtXG4gICAgICBbaXRlbV09XCJpdGVtXCJcbiAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LXNlbGVjdGVkXT1cInNlbGVjdGVkTm9kZUlkKCkgPT09IGl0ZW0uaWRcIlxuICAgICAgKGNsaWNrKT1cImhhbmRsZXJTZWxlY3ROb2RlKGl0ZW0sICRldmVudClcIlxuICAgICAgKG1vdXNlZG93bik9XCJoYW5kbGVyQmxvY2tQYXJlbnREcmFnKCRldmVudClcIj5cbiAgICAgIDwhLS0g8J+UtCBW4buPIG7DoHkgbWFuZyBgZHJhZ0hhbmRsZUNsYXNzYCDigJQgQ+G6pE0gYuG7jy5cbiAgICAgICAgICAgYGlnbm9yZUNvbnRlbnQ6IHRydWVgIGLhuq90IGdyaWRzdGVyIENI4buIIGNobyBrw6lvIGtoaSBjaOG7lyBi4bqlbSBu4bqxbSBkxrDhu5tpIG3hu5l0IHBo4bqnbiB04butIGPDs1xuICAgICAgICAgICBgZHJhZ0hhbmRsZUNsYXNzYC4gS2jDtG5nIGPDsyB24buPIG7DoHkgdGjDrCBjb21wb25lbnQgY+G7p2EgbsahaSBkw7luZyAodGjGsCB2aeG7h24ga2jDtG5nIGtp4buDbSBzb8OhdFxuICAgICAgICAgICDEkcaw4bujYyBjbGFzcyBj4bunYSBuw7MpIHPhur0ga2jDtG5nIGPDsyB0YXkgY+G6p20gbsOgbywgdsOgIEtIw5RORyBraOG7kWkgbsOgbyBrw6lvIMSRxrDhu6NjIOKAlCDEkW8gMjgvMDgvMjAyNjpcbiAgICAgICAgICAga8OpbyAzMjBweCwgdG/huqEgxJHhu5kga2jhu5FpIGdp4buvIG5ndXnDqm4geD0zLHk9MC5cbiAgICAgICAgICAgVMOqbiBjbGFzcyB0aGVvIGPhuqVwOiBsxrDhu5tpIHRyYW5nIHbDoCBsxrDhu5tpIGzhu5NuZyBkw7luZyBoYWkgdMOqbiBLSMOBQyBuaGF1LCBu4bq/dSBraMO0bmcgbMaw4bubaSBjaGFcbiAgICAgICAgICAgY8Wpbmcgbmjhuq1uIHJhIHRheSBj4bqnbSBj4bunYSBraOG7kWkgY29uIHbDoCBuaOG6pWMgbHXDtG4gY29udGFpbmVyLiAtLT5cbiAgICAgIDwhLS0g8J+UtCBW4buPIEtIw5RORyBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgIOKAlCBD4bqkTSB0aMOqbSBs4bqhaS5cbiAgICAgICAgICAgS8OpbyBjaOG7iSDEkcaw4bujYyBwaMOpcCBi4bqvdCDEkeG6p3UgdOG7qyBE4bqiSSBYQU5IIChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNjogXCJwaOG6o2kgaG92ZXIgdsOgbyB2w7luZ1xuICAgICAgICAgICBtw6B1IHhhbmggdGjDrCBt4bubaSBrw6lvIHRo4bqjIMSRxrDhu6NjIGNobyDEkeG7k25nIG5o4bqldFwiKS4gQ2hvIGPhuqMgduG7jyBsw6BtIHRheSBj4bqnbSB0aMOsIGLhuqVtIMSRw6J1IGPFqW5nXG4gICAgICAgICAgIGvDqW8sIHbDoCBraOG7kWkgY29uIG7hurFtIHRyb25nIGNvbnRhaW5lciBz4bq9IG5o4bqlYyBsdcO0biBjb250YWluZXIgdsOsIGzGsOG7m2kgY2hhIGR1eeG7h3QgbMOqbiBn4bq3cFxuICAgICAgICAgICB0YXkgY+G6p20gY+G7p2EgduG7jyBjb250YWluZXIgdHLGsOG7m2MuXG4gICAgICAgICAgIFbhu48ga2jhu5FpIGNvbiB24bqrbiBnaeG7ryBjbGFzcyBDSOG6tk4gxJHhu4MgbMaw4bubaSBjaGEgZOG7q25nIMSRw7puZyDhu58gYmnDqm4uIC0tPlxuICAgICAgPGRpdlxuICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtYmxvY2stc2hlbGwgZ3JvdXAgcmVsYXRpdmUgaC1mdWxsIHctZnVsbCBtaW4tdy0wXCJcbiAgICAgICAgW2F0dHIuZGF0YS1ub2RlLWlkXT1cIml0ZW0uaWRcIlxuICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1ibG9jay1wYXJlbnQtZHJhZ109XCJpc05lc3RlZENhbnZhcygpXCI+XG4gICAgICAgIDwhLS0gROG6o2kga8OpbzogaGnhu4duIGtoaSByw6ogY2h14buZdCB2w6BvIGto4buRaSwgYmEgY2jhuqVtIGzDoCBk4bqldSBoaeG7h3Uga8OpbyBxdWVuIHRodeG7mWMuXG4gICAgICAgICAgICAgTmfGsOG7nWkgZMO5bmcgY+G6p24gbeG7mXQgY2jhu5cgYsOhbSBSw5UgUsOATkcgdGhheSB2w6wgxJFvw6FuIHhlbSBi4bqlbSDEkcOidSB0aMOsIGvDqW8gxJHGsOG7o2NcbiAgICAgICAgICAgICAoY2jhu5F0IDI4LzA4LzIwMjYsIHRoZW8gxJHDum5nIGThuqNpIMSRYW5nIGTDuW5nIOG7nyBtw6BuIEN1c3RvbWVyIDM2MCBj4bunYSBtb2Jpby13ZWIpLiAtLT5cbiAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkpIHtcbiAgICAgICAgICA8IS0tIPCflLQgROG6o2kgUEjhuqJJIG1hbmcgdGF5IGPhuqdtIMSQw5pORyBD4bqkUCBj4bunYSBuw7MuIEThuqNpIG5o4bqtbiBjaHXhu5l0IChgcG9pbnRlci1ldmVudHM6IGF1dG9gKSxcbiAgICAgICAgICAgICAgIG7Dqm4gbuG6v3Uga2jDtG5nIG1hbmcgdGF5IGPhuqdtIHRow6wgYGNoZWNrRHJhZ0hhbmRsZUNsYXNzYCBkdXnhu4d0IGzDqm4gZ+G6t3AgdGF5IGPhuqdtIGPhu6dhXG4gICAgICAgICAgICAgICBjb250YWluZXIgdsOgIG5o4bqlYyBjb250YWluZXIgdGhheSB2w6wga2jhu5FpIGNvbiAoxJFvIDI4LzA4LzIwMjY6IGvDqW8gZOG6o2kga2jhu5FpIGNvbixcbiAgICAgICAgICAgICAgIGNvbnRhaW5lciBuaOG6o3kgMCwwIOKGkiAwLDEgY8OybiBraOG7kWkgY29uIMSR4bupbmcgaW0pLlxuICAgICAgICAgICAgICAg8J+UtCBE4bqjaSBDSOG7iCBtYW5nIHRheSBj4bqnbSwgQ+G6pE0gbWFuZyB0aMOqbSBjbGFzcyBjaOG6t246IGBjaGVja0RyYWdIYW5kbGVDbGFzc2AgeMOpdCBoYWlcbiAgICAgICAgICAgICAgIGNsYXNzIMSRw7MgdHLDqm4gQ8OZTkcgbeG7mXQgbm9kZSBuw6puIMSR4buDIGNodW5nIGzDoCBrw6lvIGjhu49uZyAoxJFvIDI4LzA4LzIwMjY6IGLhuqVtIGThuqNpIGto4buRaVxuICAgICAgICAgICAgICAgY29uIGvDqW8gMTQwcHgsIGto4buRaSDEkeG7qW5nIGltKS4gVmnhu4djIGNo4bq3biBsxrDhu5tpIGNoYSBkbyBW4buOIEtI4buQSSBsby4gLS0+XG4gICAgICAgICAgPGRpdlxuICAgICAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyXCJcbiAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyLWNoaWxkXT1cImlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1oYW5kbGVdPVwiIWlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1oYW5kbGUtY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyLWRvdHNcIj5cbiAgICAgICAgICAgICAgQGZvciAoZG90IG9mIGRyYWdCYXJEb3RzOyB0cmFjayBkb3QpIHtcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXItZG90XCI+PC9zcGFuPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cblxuICAgICAgICA8IS0tIEtodW5nIGNo4bupYSBjb21wb25lbnQgY+G7p2EgbsahaSBkw7luZy5cbiAgICAgICAgICAgICDwn5S0IFBI4bqiSSBjw7Mga2h1bmcgdGjhuq10IChraMO0bmcgcGjhuqNpIGBuZy1jb250YWluZXJgIHRy4buRbmcpOiBuw7MgbG8gaOG7mSBuxqFpIGTDuW5nIHBo4bqnbiBs4bqlcCDEkeG6p3lcbiAgICAgICAgICAgICDDtCB2w6Agdmnhu4FuIGLDoW8gY2jhur8gxJHhu5kgc+G7rWEuIEtow7RuZyBjw7Mga2h1bmcgdGjDrCBt4buXaSBjb21wb25lbnQgbuG7mWkgZHVuZyBs4bqhaSBwaOG6o2kgdOG7sSB2aeG6v3RcbiAgICAgICAgICAgICBgOmhvc3QgeyB3aWR0aC9oZWlnaHQ6IDEwMCUgfWAgKyB2aeG7gW4gxJHhu6l0IOKAlCDEkcOzIGzDoCB2aeG7h2MgY+G7p2EgdGjGsCB2aeG7h24sIGtow7RuZyBwaOG6o2kgY+G7p2FcbiAgICAgICAgICAgICBuZ8aw4budaSBkw7luZyAobmfGsOG7nWkgZMO5bmcgY2jhu5F0IDI4LzA4LzIwMjYpLlxuICAgICAgICAgICAgIE3hu5dpIGto4buRaSBt4buZdCBWaWV3Q29udGFpbmVyUmVmIHJpw6puZyBuw6puIGNvbXBvbmVudCBu4bqxbSDEkcO6bmcgw7QgY+G7p2EgbsOzLiAtLT5cbiAgICAgICAgPCEtLSDwn5S0IENvbnRhaW5lciBDw5MgYGdldENvbXBvbmVudE91dGxldGA6IGtodW5nIG7DoHkgbMOgIFbhu44sIHbDoCBjYW52YXMgY29uIMSRxrDhu6NjIMSRxrBhIFbDgE8gVFJPTkdcbiAgICAgICAgICAgICBjb21wb25lbnQgduG7jyBxdWEgaW5wdXQgYGNoaWxkQ2FudmFzYCAobeG7mXQgYFRlbXBsYXRlUmVmYCkg4oCUIHhlbSBgbmctdGVtcGxhdGUgI2NoaWxkQ2FudmFzVHBsYFxuICAgICAgICAgICAgIGLDqm4gZMaw4bubaS4gVuG7jyB04buxIHF1eeG6v3QgxJHhurd0IGNhbnZhcyDhu58gxJHDonUgZ2nhu69hIHRpw6p1IMSR4buBIHbDoCBwaOG6p24gdGjDom4gY+G7p2EgbsOzLlxuICAgICAgICAgICAgIFRyxrDhu5tjIMSRw6J5IGNhbnZhcyBjb24gbMOgIEFOSCBFTSBj4bunYSBraHVuZyBuw6B5LCBuw6puIHbhu48gY2hp4bq/bSB0cuG7jW4gY2hp4buBdSBjYW8gcuG7k2kgxJHhuql5IHRvw6BuIGLhu5lcbiAgICAgICAgICAgICB0aOG6uyBjb24gcmEgbmdvw6BpIHbDuW5nIG5ow6xuIHRo4bqleSAoxJFvIDE0LzA5LzIwMjY6IG3hu41pIGto4buRaSBnaMOpcCBjaOG7iSBjw7JuIG5ow6NuIHbDoCBydeG7mXQgdHLhuq9uZykuIC0tPlxuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRlbnQtZnJhbWVcIlxuICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRlbnQtZnJhbWUtZWRpdF09XCJpc0VkaXRNb2RlKCkgJiYgIWlzQ29udGFpbmVyKGl0ZW0pXCJcbiAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1jb250ZW50LWZyYW1lLWVtcHR5XT1cImlzQ29udGFpbmVyKGl0ZW0pICYmICFpdGVtLmdldENvbXBvbmVudE91dGxldFwiPlxuICAgICAgICAgIDxuZy1jb250YWluZXIgI2l0ZW1Ib3N0IC8+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDwhLS0gROG6pXUgaGnhu4d1IENPTlRBSU5FUjogbmjDo24gZ8OzYyB0csOqbi1waOG6o2kgKyB2aeG7gW4gxJHhu6l0IGJhbyBydeG7mXQuIEtow7RuZyBjw7MgZOG6pXUgaGnhu4d1IHRow6xcbiAgICAgICAgICAgICBuZ8aw4budaSBkw7luZyBraMO0bmcgYmnhur90IGto4buRaSBuw6BvIGNo4bupYSDEkcaw4bujYyBraOG7kWkga2jDoWMgKGNo4buRdCAyOC8wOC8yMDI2LCB0aGVvIMSRw7puZyBjw6FjaFxuICAgICAgICAgICAgIG3DoG4gQ3VzdG9tZXIgMzYwIGPhu6dhIG1vYmlvLXdlYiDEkWFuZyBsw6BtKS4gLS0+XG4gICAgICAgIEBpZiAoaXNFZGl0TW9kZSgpICYmIGlzQ29udGFpbmVyKGl0ZW0pKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGFpbmVyLWxhYmVsXCI+e3sgJ2kxOG5fY29udGFpbmVyX2Jsb2NrJyB8IHRyYW5zbGF0ZSB9fTwvZGl2PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRhaW5lci1ib3JkZXJcIj48L2Rpdj5cbiAgICAgICAgfVxuXG4gICAgICAgIDwhLS0gVGhhbmggY8O0bmcgY+G7pTogaGnhu4duIGtoaSByw6ogY2h14buZdCB2w6BvIGto4buRaS5cbiAgICAgICAgICAgICDwn5S0IETDuW5nIGBsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25gIOKAlCBD4bqkTSB04buxIGThu7FuZyB0aOG6uyBgPGJ1dHRvbj5gLlxuICAgICAgICAgICAgIENvbXBvbmVudCBuw6B5IMSRw6MgY8OzIHPhurVuIGtp4buDdSBuw7p0LCBj4buhIG7DunQgdsOgIGJvbmcgYsOzbmcgY2jDuiB0aMOtY2ggdGhlbyDEkcO6bmcgYuG7mSBnaWFvIGRp4buHblxuICAgICAgICAgICAgIGPhu6dhIHPhuqNuIHBo4bqpbTsgdOG7sSBk4buxbmcgbMOgIG3hu5dpIG7GoWkgbeG7mXQga2nhu4N1IHbDoCBt4bqldCBsdcO0biB0b29sdGlwLiAtLT5cbiAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkgJiYgIWhpZGRlblRvb2xiYXIoKSkge1xuICAgICAgICAgIEBpZiAodGVtcGxhdGVUb29sYmFyKCkpIHtcbiAgICAgICAgICAgIDwhLS0gTsahaSBkw7luZyB04buxIHbhur0gdGhhbmggY8O0bmcgY+G7pTogbmjhuq1uIGto4buRaSArIGPDoWMgaMOgbSBwaMOhdCBldmVudCBxdWEgY29udGV4dC5cblxuICAgICAgICAgICAgICAgICDwn5S0IEto4buRaSBi4buNYyB04buxIGNo4bq3biBgbW91c2Vkb3duYC9gY2xpY2tgIG7hu5VpIGzDqm4gZ3JpZHN0ZXIuIFRoYW5oIGPDtG5nIGPhu6UgbuG6sW0gdHJvbmdcbiAgICAgICAgICAgICAgICAgdsO5bmcgbWFuZyBgZHJhZ0hhbmRsZUNsYXNzYDsga2jDtG5nIGNo4bq3biB0aMOsIGPDuiBi4bqlbSBi4buLIGhp4buDdSBsw6AgYuG6r3QgxJHhuqd1IGvDqW8gdsOgXG4gICAgICAgICAgICAgICAgIGBkZWxheVN0YXJ0OiAxNjBgIG514buRdCBsdcO0biBgY2xpY2tgIOKAlCBuw7p0IHRyb25nIHRlbXBsYXRlIG5nb8OgaSBi4bqlbSBraMO0bmcgxINuXG4gICAgICAgICAgICAgICAgICjEkW8gMDkvMDkvMjAyNjogYuG6pW0gYuG6sW5nIGAuY2xpY2soKWAgY+G7p2EgRE9NIHRow6wgbeG7nywgYuG6pW0gY2h14buZdCB0aOG6rXQgdGjDrCBraMO0bmcpLlxuICAgICAgICAgICAgICAgICBDaOG6t24g4bufIMSQw4JZIMSR4buDIG7GoWkgZMO5bmcga2jDtG5nIHBo4bqjaSBiaeG6v3QgYuG6q3kgbsOgeS4gLS0+XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC10b29sYmFyXCJcbiAgICAgICAgICAgICAgcm9sZT1cInRvb2xiYXJcIlxuICAgICAgICAgICAgICB0YWJpbmRleD1cIjBcIlxuICAgICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC10b29sYmFyLWNoaWxkXT1cImlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgICAobW91c2Vkb3duKT1cImhhbmRsZXJTdG9wRXZlbnQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChrZXlkb3duKT1cImhhbmRsZXJTdG9wRXZlbnQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJoYW5kbGVyU3RvcEV2ZW50KCRldmVudClcIj5cbiAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgICAgICpuZ1RlbXBsYXRlT3V0bGV0PVwidGVtcGxhdGVUb29sYmFyKCkhOyBjb250ZXh0OiB0b29sYmFyQ29udGV4dHMoKVtpdGVtSW5kZXhdXCIgLz5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtdG9vbGJhclwiXG4gICAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZGltLWJhZGdlXCI+XG4gICAgICAgICAgICAgICAge3sgaXRlbS5yb3dzIH19IGjDoG5nIMOXIHt7IGl0ZW0uY29scyB9fSBj4buZdFxuICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgIEBpZiAoY2FuQWRkSW5zaWRlKGl0ZW0pKSB7XG4gICAgICAgICAgICAgICAgPGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvblxuICAgICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZCdcIlxuICAgICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIFtzaXplQnV0dG9uXT1cIidzbWFsbGVyJ1wiXG4gICAgICAgICAgICAgICAgICBbY2xhc3NJY29uTGVmdF09XCInbGlicy11aS1pY29uLWFkZCdcIlxuICAgICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogJ1Row6ptIGto4buRaSB2w6BvIHRyb25nJywgekluZGV4OiAxMzAwIH0gfVwiXG4gICAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlckFkZEluc2lkZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBAaWYgKGNhblRvZ2dsZVR5cGUoaXRlbSkpIHtcbiAgICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgICBbdHlwZV09XCInYnV0dG9uLXRoaXJkJ1wiXG4gICAgICAgICAgICAgICAgICBbaWNvbk9ubHlUeXBlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgW3NpemVCdXR0b25dPVwiJ3NtYWxsZXInXCJcbiAgICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cImlzQ29udGFpbmVyKGl0ZW0pID8gJ2xpYnMtdWktaWNvbi1zcGxpdC1jZWxsJyA6ICdsaWJzLXVpLWljb24tbWVyZ2UtY2VsbCdcIlxuICAgICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogaXNDb250YWluZXIoaXRlbSkgPyAnxJDGsGEgduG7gSBraOG7kWkgxJHGoW4nIDogJ0NodXnhu4NuIHRow6BuaCBraOG7kWkgZ2jDqXAnLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyVG9nZ2xlVHlwZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZCdcIlxuICAgICAgICAgICAgICAgIFtpY29uT25seVR5cGVdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgW3NpemVCdXR0b25dPVwiJ3NtYWxsZXInXCJcbiAgICAgICAgICAgICAgICBbY2xhc3NJY29uTGVmdF09XCInbGlicy11aS1pY29uLXNldHRpbmcnXCJcbiAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiAnQ+G6pXUgaMOsbmggdGjhursnLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlckNvbmZpZygkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgICAgPGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvblxuICAgICAgICAgICAgICAgIFt0eXBlXT1cIididXR0b24tdGhpcmQtaG92ZXItZGFuZ2VyJ1wiXG4gICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tcmVtb3ZlJ1wiXG4gICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogaXNDb250YWluZXIoaXRlbSkgPyAnWG/DoSBj4bqjIGto4buRaSBnaMOpcCcgOiAnWG/DoSBraOG7kWknLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlclJlbW92ZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIEBpZiAoaXRlbS5jaGlsZHJlbiAmJiBpdGVtLmNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICA8IS0tIEto4buRaSBjw7MgY29uIOKGkiBydeG7mXQgbsOzIGzhuqFpIGzDoCBt4buZdCBt4bq3dCBwaOG6s25nIG7hu69hLiDEkOG7hyBxdXkg4bufIMSRw6J5LCBuxqFpIGTDuW5nIGtow7RuZyBwaOG6o2kgbG8uXG4gICAgICAgICAgICAgICBM4buBIMSR4bq3dCDhu58gxJDDglkgKHbhu48gY29udGFpbmVyKSwga2jDtG5nIMSR4bq3dCB2w6BvIGzGsOG7m2kgY29uIOKAlCBsxrDhu5tpIGNvbiBnaeG7ryBuZ3V5w6puIGtoZSAycHguXG5cbiAgICAgICAgICAgICAgIPCflLQgS2h1w7RuIMSR4bq3dCB0cm9uZyBgbmctdGVtcGxhdGVgIMSR4buDIMSRaSDEkcaw4bujYyBIQUkgxJHGsOG7nW5nOlxuICAgICAgICAgICAgICAgLSBDb250YWluZXIgS0jDlE5HIGPDsyB24buPIHJpw6puZyDihpIgduG6vSB0aOG6s25nIHThuqFpIMSRw6J5LCB5IG5oxrAgdHLGsOG7m2MuXG4gICAgICAgICAgICAgICAtIENvbnRhaW5lciBDw5MgYGdldENvbXBvbmVudE91dGxldGAg4oaSIHRoxrAgdmnhu4duIHRydXnhu4FuIGtodcO0biBuw6B5IHh14buRbmcgY29tcG9uZW50IHbhu48gcXVhXG4gICAgICAgICAgICAgICAgIGlucHV0IGBjaGlsZENhbnZhc2AsIHbhu48gdOG7sSDEkeG6t3QgbsOzIHbDoG8gZ2nhu69hIHRpw6p1IMSR4buBIHbDoCBwaOG6p24gdGjDom4gY+G7p2EgbcOsbmguIFbhur0gdGjDqm0g4bufXG4gICAgICAgICAgICAgICAgIMSRw6J5IG7hu69hIGzDoCBoYWkgbeG6t3QgcGjhurNuZyBs4buTbmcgY2jhu5NuZyBuaGF1LiAtLT5cbiAgICAgICAgICA8bmctdGVtcGxhdGUgI2NoaWxkQ2FudmFzVHBsPlxuICAgICAgICAgICAgPGxpYnNfdWktc2VydmljZXMtZ3JpZF9sYXlvdXQtY2FudmFzXG4gICAgICAgICAgICAgIGNsYXNzPVwiYmxvY2sgaC1mdWxsIHctZnVsbFwiXG4gICAgICAgICAgICAgIFtzdHlsZS5wYWRkaW5nXT1cImNvbnRhaW5lclBhZGRpbmcoKVwiXG4gICAgICAgICAgICAgIFtub2RlXT1cIml0ZW1cIlxuICAgICAgICAgICAgICBbZGVwdGhdPVwiZGVwdGgoKSArIDFcIlxuICAgICAgICAgICAgICBbaGlkZGVuVG9vbGJhcl09XCJoaWRkZW5Ub29sYmFyKClcIlxuICAgICAgICAgICAgICBbdGVtcGxhdGVUb29sYmFyXT1cInRlbXBsYXRlVG9vbGJhcigpXCJcbiAgICAgICAgICAgICAgW3NlbGVjdGVkTm9kZUlkXT1cInNlbGVjdGVkTm9kZUlkKClcIlxuICAgICAgICAgICAgICAob3V0U2VsZWN0Tm9kZSk9XCJvdXRTZWxlY3ROb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChvdXRDb25maWdOb2RlKT1cIm91dENvbmZpZ05vZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dENoYW5nZSk9XCJvdXRDaGFuZ2UuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dFJlbW92ZU5vZGUpPVwib3V0UmVtb3ZlTm9kZS5lbWl0KCRldmVudClcIlxuICAgICAgICAgICAgICAob3V0QWRkQmxvY2tJbnNpZGUpPVwib3V0QWRkQmxvY2tJbnNpZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dFRvZ2dsZVR5cGUpPVwib3V0VG9nZ2xlVHlwZS5lbWl0KCRldmVudClcIiAvPlxuICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG5cbiAgICAgICAgICBAaWYgKCFpdGVtLmdldENvbXBvbmVudE91dGxldCkge1xuICAgICAgICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJjaGlsZENhbnZhc1RwbFwiIC8+XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cbiAgICA8L2dyaWRzdGVyLWl0ZW0+XG4gIH1cbiAgICA8L2dyaWRzdGVyPlxuICA8L2Rpdj5cbn1cbiJdfQ==
795
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC1sYXlvdXQtY2FudmFzLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvc2VydmljZXMvZ3JpZC1sYXlvdXQvc3JjL2NhbnZhcy9ncmlkLWxheW91dC1jYW52YXMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy11aS9zZXJ2aWNlcy9ncmlkLWxheW91dC9zcmMvY2FudmFzL2dyaWQtbGF5b3V0LWNhbnZhcy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFFVCxZQUFZLEVBR1osZ0JBQWdCLEVBQ2hCLFFBQVEsRUFDUixNQUFNLEVBQ04sTUFBTSxFQUNOLEtBQUssRUFDTCxLQUFLLEVBQ0wsTUFBTSxFQUNOLE1BQU0sRUFDTixTQUFTLEVBQ1QsWUFBWSxHQUNiLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxzQ0FBc0MsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzVGLE9BQU8sRUFBeUIsc0NBQXNDLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUNuSCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsY0FBYyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQy9ELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN0RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDbEUsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFhakUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLGdCQUFnQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDbkYsT0FBTyxFQUFFLDJCQUEyQixFQUFFLGNBQWMsRUFBRSxNQUFNLGlDQUFpQyxDQUFDOzs7O0FBRTlGOzs7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QUU7Z0JBQ2pFLEVBQUUsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1lBQ3RDLENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztJQUNILENBQUM7d0dBaDRCVSwrQkFBK0I7NEZBQS9CLCtCQUErQix3ekdBcUdELGlCQUFpQiw4VUE4Q0ksZ0JBQWdCLHlKQ2hOaEYsc3k2Q0FnbUJBLGdtTURuaUJhLCtCQUErQixra0JBVnhDLGNBQWMsb1JBQ2Qsc0NBQXNDLHNqQkFDdEMsc0NBQXNDLDZTQUN0QyxnQkFBZ0IsbUpBQ2hCLGVBQWU7OzRGQU1OLCtCQUErQjtrQkFkM0MsU0FBUzsrQkFDRSxxQ0FBcUMsY0FDbkMsSUFBSSxXQUNQO3dCQUNQLGNBQWM7d0JBQ2Qsc0NBQXNDO3dCQUN0QyxzQ0FBc0M7d0JBQ3RDLGdCQUFnQjt3QkFDaEIsZUFBZTtxQkFDaEIsbUJBR2dCLHVCQUF1QixDQUFDLE1BQU07d0RBd2pCckMsc0JBQXNCO3NCQUQvQixZQUFZO3VCQUFDLGtCQUFrQixFQUFFLENBQUMsUUFBUSxDQUFDO2dCQXFCbEMsb0JBQW9CO3NCQUQ3QixZQUFZO3VCQUFDLGdCQUFnQixFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENvbXBvbmVudCxcbiAgRWxlbWVudFJlZixcbiAgSG9zdExpc3RlbmVyLFxuICBPbkRlc3Ryb3ksXG4gIFRlbXBsYXRlUmVmLFxuICBWaWV3Q29udGFpbmVyUmVmLFxuICBjb21wdXRlZCxcbiAgZWZmZWN0LFxuICBpbmplY3QsXG4gIGlucHV0LFxuICBtb2RlbCxcbiAgb3V0cHV0LFxuICBzaWduYWwsXG4gIHZpZXdDaGlsZCxcbiAgdmlld0NoaWxkcmVuLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE5nVGVtcGxhdGVPdXRsZXQgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgTGlic1VpQ29tcG9uZW50c0J1dHRvbnNCdXR0b25Db21wb25lbnQgfSBmcm9tICdAbGlicy11aS9jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uJztcbmltcG9ydCB7IElTY3JvbGxPdmVybGF5T3B0aW9ucywgTGlic1VpQ29tcG9uZW50c1Njcm9sbE92ZXJsYXlEaXJlY3RpdmUgfSBmcm9tICdAbGlicy11aS9jb21wb25lbnRzLXNjcm9sbC1vdmVybGF5JztcbmltcG9ydCB7IEdyaWRzdGVyQ29tcG9uZW50LCBHcmlkc3Rlck1vZHVsZSB9IGZyb20gJ2dyaWRzdGVyMTgnO1xuaW1wb3J0IHsgVHJhbnNsYXRlTW9kdWxlIH0gZnJvbSAnQG5neC10cmFuc2xhdGUvY29yZSc7XG5pbXBvcnQgeyBlbXB0eVJvb3ROb2RlIH0gZnJvbSAnLi4vZGVmaW5lcy9ncmlkc3Rlci1jb25maWcuZGVmaW5lJztcbmltcG9ydCB7IExpYnNVaUdyaWRMYXlvdXRTZXJ2aWNlIH0gZnJvbSAnLi4vZ3JpZC1sYXlvdXQuc2VydmljZSc7XG5pbXBvcnQge1xuICBUX2dyaWRMYXlvdXRfYWN0aW9uRXZlbnQsXG4gIFRfZ3JpZExheW91dF9hZGRFdmVudCxcbiAgVF9ncmlkTGF5b3V0X2NoYW5nZUV2ZW50LFxuICBUX2dyaWRMYXlvdXRfZHJvcEV2ZW50LFxuICBUX2dyaWRMYXlvdXRfZHJvcEdob3N0LFxuICBUX2dyaWRMYXlvdXRfZXh0ZXJuYWxEcmFnUGF5bG9hZCxcbiAgVF9ncmlkTGF5b3V0X25ld05vZGUsXG4gIFRfZ3JpZExheW91dF9ub2RlLFxuICBUX2dyaWRMYXlvdXRfdG9vbGJhckNvbnRleHQsXG4gIFRfZ3JpZExheW91dF96b29tUHJlc2V0LFxufSBmcm9tICcuLi9pbnRlcmZhY2VzL2dyaWQtbGF5b3V0LmludGVyZmFjZSc7XG5pbXBvcnQgeyBjYWxjdWxhdGVEcm9wR2hvc3QsIHBhcnNlRHJvcFBheWxvYWQgfSBmcm9tICcuLi9kZWZpbmVzL2NvbGxpc2lvbi5kZWZpbmUnO1xuaW1wb3J0IHsgYnJpZGdlU2Nyb2xsQmV0d2VlbkVsZW1lbnRzLCBlZGdlU2Nyb2xsU3RlcCB9IGZyb20gJy4uL2RlZmluZXMvc2Nyb2xsLWJyaWRnZS5kZWZpbmUnO1xuXG4vKipcbiAqIE3hurd0IHBo4bqzbmcga8OpbyB0aOG6oy4gxJDhu4cgcXV5OiBraOG7kWkgY8OzIGBjaGlsZHJlbmAgdGjDrCBydeG7mXQgbsOzIGzhuqFpIGzDoCBt4buZdCBt4bq3dCBwaOG6s25nIG7hu69hLlxuICpcbiAqIE7GoWkgZMO5bmcgS0jDlE5HIGThu7FuZyBjb21wb25lbnQgbsOgeSB0cuG7sWMgdGnhur9wIGNobyB04burbmcga2jhu5FpIOKAlCBjaOG7iSDEkeG6t3QgTeG7mFQgdGjhursg4bufIGfhu5FjIHbDoCB0cnV54buBblxuICogYG5vZGVDb21wb25lbnRUeXBlYCBxdWEgYExpYnNVaUdyaWRMYXlvdXRTZXJ2aWNlLmluaXQoKWAuIFRoxrAgdmnhu4duIHThu7EgZ+G6r24gY29tcG9uZW50IGNobyBt4buNaSBraOG7kWkuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2xpYnNfdWktc2VydmljZXMtZ3JpZF9sYXlvdXQtY2FudmFzJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIEdyaWRzdGVyTW9kdWxlLFxuICAgIExpYnNVaUNvbXBvbmVudHNCdXR0b25zQnV0dG9uQ29tcG9uZW50LFxuICAgIExpYnNVaUNvbXBvbmVudHNTY3JvbGxPdmVybGF5RGlyZWN0aXZlLFxuICAgIE5nVGVtcGxhdGVPdXRsZXQsXG4gICAgVHJhbnNsYXRlTW9kdWxlLFxuICBdLFxuICB0ZW1wbGF0ZVVybDogJy4vZ3JpZC1sYXlvdXQtY2FudmFzLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmw6ICcuL2dyaWQtbGF5b3V0LWNhbnZhcy5jb21wb25lbnQuc2NzcycsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBMaWJzVWlHcmlkTGF5b3V0Q2FudmFzQ29tcG9uZW50IGltcGxlbWVudHMgQWZ0ZXJWaWV3SW5pdCwgT25EZXN0cm95IHtcbiAgLyoqXG4gICAqIEBpbnRlcm5hbCBDaOG7iSBt4bq3dCBwaOG6s25nIEzhu5JORyBkw7luZyDigJQgdGjGsCB2aeG7h24gdOG7sSB0cnV54buBbiBraGkgduG6vSBydeG7mXQgY29udGFpbmVyLlxuICAgKlxuICAgKiBOxqFpIGTDuW5nIEtIw5RORyB0cnV54buBbjogbeG6t3QgcGjhurNuZyBn4buRYyDEkeG7jWMgY8OieSB04burIHNlcnZpY2UgKMSRw6MgxJHGsGEgdsOgbyBxdWEgYGluaXQoKWApLCB0cnV54buBbiB0aMOqbVxuICAgKiDhu58gxJHDonkgbMOgIGtoYWkgaGFpIGzhuqduIGPDuW5nIG3hu5l0IHRo4bupIChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNikuXG4gICAqL1xuICByZWFkb25seSBub2RlID0gaW5wdXQ8VF9ncmlkTGF5b3V0X25vZGU+KCk7XG4gIC8qKiBAaW50ZXJuYWwgQ2jhu4kgbeG6t3QgcGjhurNuZyBM4buSTkcgZMO5bmcg4oCUIHRoxrAgdmnhu4duIHThu7EgdHJ1eeG7gW4uICovXG4gIHJlYWRvbmx5IGRlcHRoID0gaW5wdXQ8bnVtYmVyPigwKTtcblxuICAvKipcbiAgICog4bqobiBo4bqzbiB0aGFuaCBjw7RuZyBj4bulIG3hurdjIMSR4buLbmggKHRow6ptLXbDoG8tdHJvbmcgwrcgZ+G7mXAvdMOhY2ggwrcgeG/DoSkgY+G7p2EgTeG7jEkga2jhu5FpLlxuICAgKlxuICAgKiBEw7luZyBraGkgc+G6o24gcGjhuqltIGdvbSBjw6FjIGjDoG5oIMSR4buZbmcgxJHDsyB2w6BvIGNo4buXIGtow6FjIOKAlCB2ZCBt4buZdCBuw7p0IOKLriBu4bqxbSB0cm9uZyBjaMOtbmggbuG7mWkgZHVuZ1xuICAgKiBraOG7kWkuIOG6qG4gcuG7k2kgdGjDrCBgb3V0UmVtb3ZlTm9kZWAgwrcgYG91dEFkZEJsb2NrSW5zaWRlYCDCtyBgb3V0VG9nZ2xlVHlwZWAga2jDtG5nIGPDsm4gbmd14buTbiBwaMOhdFxuICAgKiB04burIHRoxrAgdmnhu4duOyBuxqFpIGTDuW5nIHThu7EgZ+G7jWkgYHJlbW92ZUJsb2NrYCDCtyBgYWRkQmxvY2tgIMK3IGB0b2dnbGVUeXBlYCBj4bunYSBzZXJ2aWNlLlxuICAgKi9cbiAgcmVhZG9ubHkgaGlkZGVuVG9vbGJhciA9IGlucHV0PGJvb2xlYW4sIGJvb2xlYW4gfCB1bmRlZmluZWQ+KGZhbHNlLCB7IHRyYW5zZm9ybTogKHZhbHVlKSA9PiB2YWx1ZSA/PyBmYWxzZSB9KTtcblxuICAvKipcbiAgICogVGVtcGxhdGUgdOG7sSB24bq9IHRoYW5oIGPDtG5nIGPhu6UsIHRoYXkgY2hvIGLhu5kgbsO6dCBt4bq3YyDEkeG7i25oLlxuICAgKlxuICAgKiBOaOG6rW4gY29udGV4dCBgeyAkaW1wbGljaXQ6IG5vZGUsIGlzQ29udGFpbmVyLCBjYW5BZGRJbnNpZGUsIGNhblRvZ2dsZVR5cGUgfWAgxJHhu4MgbsahaSBkw7luZyB24bq9IMSRw7puZ1xuICAgKiBuw7p0IGNobyB04burbmcga2jhu5FpLCBy4buTaSBn4buNaSBuZ8aw4bujYyBxdWEgYG91dFJlbW92ZU5vZGVgIMK3IGBvdXRBZGRCbG9ja0luc2lkZWAgwrcgYG91dFRvZ2dsZVR5cGVgXG4gICAqIGLhurFuZyBjaMOtbmggY8OhYyBow6BtIHRyb25nIGNvbnRleHQuIFRydXnhu4FuIGPhuqMgYGhpZGRlblRvb2xiYXJgIHbDoCBgdGVtcGxhdGVUb29sYmFyYCB0aMOsXG4gICAqIGBoaWRkZW5Ub29sYmFyYCB0aOG6r25nIOKAlCBraMO0bmcgduG6vSBnw6wuXG4gICAqL1xuICByZWFkb25seSB0ZW1wbGF0ZVRvb2xiYXIgPSBpbnB1dDxUZW1wbGF0ZVJlZjxUX2dyaWRMYXlvdXRfdG9vbGJhckNvbnRleHQ+IHwgdW5kZWZpbmVkPih1bmRlZmluZWQpO1xuXG4gIC8qKlxuICAgKiBC4buRIGPhu6VjIHbhu6thIMSR4buVaSBkbyBOR8av4bucSSBEw5lORyBrw6lvIGhv4bq3YyDEkeG7lWkga8OtY2ggdGjGsOG7m2Mga2jhu5FpLlxuICAgKlxuICAgKiBOxqFpIGTDuW5nIHThu7EgZ+G7jWkgYGFkZEJsb2NrYCDCtyBgcmVtb3ZlQmxvY2tgIMK3IGBjb21wYWN0YCB0aMOsIEtIw5RORyBi4bqvbiDigJQgaOG7jSB24burYSBn4buNaSB0aMOsIMSRw6MgYmnhur90LFxuICAgKiBi4bqvbiBuZ8aw4bujYyBs4bqhaSBsw6AgdGjhu6thIChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNikuXG4gICAqL1xuICByZWFkb25seSBvdXRDaGFuZ2UgPSBvdXRwdXQ8VF9ncmlkTGF5b3V0X2NoYW5nZUV2ZW50PigpO1xuICAvKipcbiAgICogTmfGsOG7nWkgZMO5bmcgYuG6pW0gbsO6dCBYT8OBIHRyw6puIG3hu5l0IGto4buRaS4gVGjGsCB2aeG7h24gQ0jGr0EgeG/DoSDigJQgZ+G7jWkgYGV2ZW50LmNvbmZpcm0oKWAgxJHhu4MgeG/DoSB0aOG6rXQuXG4gICAqIEPhuqduIGjhu49pIHRyxrDhu5tjIHRow6wgaOG7j2kgeG9uZyBt4bubaSBn4buNaTsga2jDtG5nIGNobyBwaMOpcCB0aMOsIMSR4burbmcgZ+G7jWkuXG4gICAqL1xuICByZWFkb25seSBvdXRSZW1vdmVOb2RlID0gb3V0cHV0PFRfZ3JpZExheW91dF9hY3Rpb25FdmVudD4oKTtcbiAgLyoqXG4gICAqIE5nxrDhu51pIGTDuW5nIGLhuqVtIG7DunQgVEjDik0gdHLDqm4gbeG7mXQgY29udGFpbmVyLiBUaMawIHZp4buHbiBLSMOUTkcgdOG7sSB0aMOqbSDigJQgbsOzIGtow7RuZyBiaeG6v3QgYuG6oW4gY8OzXG4gICAqIG5o4buvbmcgbG/huqFpIGto4buRaSBuw6BvLiBN4bufIG1lbnUgY2jhu41uIGxv4bqhaSBy4buTaSBn4buNaSBgZXZlbnQuY29uZmlybShpdGVtKWAgduG7m2kga2jhu5FpIGLhuqFuIG114buRbiB0aMOqbS5cbiAgICovXG4gIHJlYWRvbmx5IG91dEFkZEJsb2NrSW5zaWRlID0gb3V0cHV0PFRfZ3JpZExheW91dF9hZGRFdmVudD4oKTtcbiAgLyoqIE5nxrDhu51pIGTDuW5nIGLhuqVtIG7DunQgR+G7mFAvVMOBQ0guIFRoxrAgdmnhu4duIGNoxrBhIGzDoG0gZ8OsIOKAlCBn4buNaSBgZXZlbnQuY29uZmlybSgpYCDEkeG7gyB0aOG7sWMgaGnhu4duLiAqL1xuICByZWFkb25seSBvdXRUb2dnbGVUeXBlID0gb3V0cHV0PFRfZ3JpZExheW91dF9hY3Rpb25FdmVudD4oKTtcblxuICAvKiogQuG6rXQgdMOtbmggbsSDbmcgdGh1IHBow7NuZyAoem9vbSkgY2FudmFzLiBDaOG7iSDDoXAgZOG7pW5nIGNobyBjYW52YXMgZ+G7kWMgKGRlcHRoIDApLiAqL1xuICByZWFkb25seSBlbmFibGVab29tID0gaW5wdXQ8Ym9vbGVhbiwgYm9vbGVhbiB8IHVuZGVmaW5lZD4oZmFsc2UsIHsgdHJhbnNmb3JtOiAodmFsdWUpID0+IHZhbHVlID8/IGZhbHNlIH0pO1xuXG4gIC8qKiBU4bu3IGzhu4cgdGh1IHBow7NuZyBoaeG7h24gdOG6oWkgKGjhu5cgdHLhu6MgdHdvLXdheSBiaW5kaW5nIFsoem9vbSldPVwibXlab29tXCIpLiAqL1xuICByZWFkb25seSB6b29tID0gbW9kZWw8bnVtYmVyIHwgdW5kZWZpbmVkPih1bmRlZmluZWQpO1xuXG4gIC8qKiBHaeG7m2kgaOG6oW4gdGh1IHBow7NuZyB04buRaSB0aGnhu4N1LiBN4bq3YyDEkeG7i25oIDAuNCAoNDAlKS4gKi9cbiAgcmVhZG9ubHkgem9vbU1pbiA9IGlucHV0PG51bWJlcj4oMC40KTtcblxuICAvKiogR2nhu5tpIGjhuqFuIHRodSBwaMOzbmcgdOG7kWkgxJFhLiBN4bq3YyDEkeG7i25oIDEuNSAoMTUwJSkuICovXG4gIHJlYWRvbmx5IHpvb21NYXggPSBpbnB1dDxudW1iZXI+KDEuNSk7XG5cbiAgLyoqIELGsOG7m2MgbmjhuqN5IHRodSBwaMOzbmcgbeG7l2kgbOG6p24gYuG6pW0gKy8tIGhv4bq3YyBsxINuIGNodeG7mXQuIE3hurdjIMSR4buLbmggMC4wNSAoNSUpLiAqL1xuICByZWFkb25seSB6b29tU3RlcCA9IGlucHV0PG51bWJlcj4oMC4wNSk7XG5cbiAgLyoqIENoaeG7gXUgcuG7mW5nIGNodeG6qW4gY+G7p2EgY2FudmFzIHN0YWdlIHTDrW5oIGLhurFuZyBweC4gTeG6t2MgxJHhu4tuaCAxNjY4LiAqL1xuICByZWFkb25seSBzdGFnZVdpZHRoID0gaW5wdXQ8bnVtYmVyPigxNjY4KTtcblxuICAvKiogQ2hp4buBdSBjYW8gdOG7kWkgdGhp4buDdSBj4bunYSBjYW52YXMgc3RhZ2UgdMOtbmggYuG6sW5nIHB4LiBN4bq3YyDEkeG7i25oIDkzNy4gKi9cbiAgcmVhZG9ubHkgc3RhZ2VNaW5IZWlnaHQgPSBpbnB1dDxudW1iZXI+KDkzNyk7XG5cbiAgLyoqIEtow7NhIGzGsHUgdOG7tyBs4buHIHRodSBwaMOzbmcgdsOgbyBsb2NhbFN0b3JhZ2UuIE3hurdjIMSR4buLbmggJ29jYl9iaV9jYW52YXNfem9vbScuICovXG4gIHJlYWRvbmx5IHN0b3JhZ2VLZXkgPSBpbnB1dDxzdHJpbmcgfCB1bmRlZmluZWQ+KCdvY2JfYmlfY2FudmFzX3pvb20nKTtcblxuICAvKiogSGnhu4NuIHRo4buLIHRoYW5oIGRvY2sgxJFp4buBdSBraGnhu4NuIHRodSBwaMOzbmcgbuG7lWkg4bufIGfDs2MgZMaw4bubaSBwaOG6o2kuIE3hurdjIMSR4buLbmggdHJ1ZS4gKi9cbiAgcmVhZG9ubHkgc2hvd1pvb21Eb2NrID0gaW5wdXQ8Ym9vbGVhbiwgYm9vbGVhbiB8IHVuZGVmaW5lZD4odHJ1ZSwgeyB0cmFuc2Zvcm06ICh2YWx1ZSkgPT4gdmFsdWUgPz8gdHJ1ZSB9KTtcblxuICAvKiogVOG7sSDEkeG7mW5nIHbhu6thIG3DoG4gaMOsbmgga2hpIHThuqNpIHRyYW5nIGzhuqduIMSR4bqndSBu4bq/dSBraHVuZyBuaMOsbiBuaOG7jyBoxqFuIHN0YWdlV2lkdGguIE3hurdjIMSR4buLbmggdHJ1ZS4gKi9cbiAgcmVhZG9ubHkgYXV0b0ZpdE9uTG9hZCA9IGlucHV0PGJvb2xlYW4sIGJvb2xlYW4gfCB1bmRlZmluZWQ+KHRydWUsIHsgdHJhbnNmb3JtOiAodmFsdWUpID0+IHZhbHVlID8/IHRydWUgfSk7XG5cbiAgLyoqIFBow6F0IHJhIGtoaSB04bu3IGzhu4cgdGh1IHBow7NuZyB0aGF5IMSR4buVaS4gKi9cbiAgcmVhZG9ubHkgb3V0Wm9vbUNoYW5nZSA9IG91dHB1dDxudW1iZXI+KCk7XG5cbiAgLyoqIELhuq10IHTDrW5oIG7Eg25nIGvDqW8gdGjhuqMgdGjhursgdOG7qyBiw6puIG5nb8OgaSB2w6BvIGNhbnZhcyAobeG6t2MgxJHhu4tuaCB0cnVlKSAqL1xuICByZWFkb25seSBlbmFibGVFeHRlcm5hbERyb3AgPSBpbnB1dDxib29sZWFuLCBib29sZWFuIHwgdW5kZWZpbmVkPih0cnVlLCB7IHRyYW5zZm9ybTogKHZhbHVlKSA9PiB2YWx1ZSA/PyB0cnVlIH0pO1xuXG4gIC8qKiBMdcO0biBoaeG7g24gdGjhu4sgxJHGsOG7nW5nIGvhursgbMaw4bubaSAyNCBj4buZdCAqL1xuICByZWFkb25seSBzaG93R3JpZExpbmVzID0gaW5wdXQ8Ym9vbGVhbiwgYm9vbGVhbiB8IHVuZGVmaW5lZD4oZmFsc2UsIHsgdHJhbnNmb3JtOiAodmFsdWUpID0+IHZhbHVlID8/IGZhbHNlIH0pO1xuXG4gIC8qKiBNw6MgaWQgY+G7p2Ega2jhu5FpIMSRYW5nIMSRxrDhu6NjIGNo4buNbiAqL1xuICByZWFkb25seSBzZWxlY3RlZE5vZGVJZCA9IG1vZGVsPHN0cmluZyB8IG51bGwgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG5cbiAgLyoqIFBow6F0IHJhIGtoaSB0aOG6oyBt4buZdCB0aOG6uyBt4bubaSB04burIGLDqm4gbmdvw6BpIHbDoG8gY2FudmFzICovXG4gIHJlYWRvbmx5IG91dERyb3BOb2RlID0gb3V0cHV0PFRfZ3JpZExheW91dF9kcm9wRXZlbnQ+KCk7XG5cbiAgLyoqIFBow6F0IHJhIGtoaSBuZ8aw4budaSBkw7luZyBjbGljayBjaOG7jW4gbeG7mXQga2jhu5FpICovXG4gIHJlYWRvbmx5IG91dFNlbGVjdE5vZGUgPSBvdXRwdXQ8VF9ncmlkTGF5b3V0X25vZGU+KCk7XG5cbiAgLyoqIFBow6F0IHJhIGtoaSBuZ8aw4budaSBkw7luZyBi4bqlbSBuw7p0IGPhuqV1IGjDrG5oIHRyw6puIHRoYW5oIGPDtG5nIGPhu6UgY+G7p2Ega2jhu5FpICovXG4gIHJlYWRvbmx5IG91dENvbmZpZ05vZGUgPSBvdXRwdXQ8VF9ncmlkTGF5b3V0X2FjdGlvbkV2ZW50PigpO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgZ3JpZHN0ZXJSZWYgPSB2aWV3Q2hpbGQoR3JpZHN0ZXJDb21wb25lbnQpO1xuICBwcml2YXRlIHJlYWRvbmx5IHNjcm9sbEFyZWFSZWYgPSB2aWV3Q2hpbGQ8RWxlbWVudFJlZjxIVE1MRWxlbWVudD4+KCdzY3JvbGxBcmVhJyk7XG4gIC8qKlxuICAgKiBD4bqnbSBC4bqkVCBD4buoIMSQw4JVIHRyw6puIGto4buRaSDEkeG7gyBrw6lvLCB0aGF5IGNobyBk4bqjaSBrw6lvIGJhIGNo4bqlbS5cbiAgICpcbiAgICog8J+UtCBN4bq3YyDEkeG7i25oIGBmYWxzZWAg4oCUIGdp4buvIG5ndXnDqm4gbHXhuq10IMSRw6MgY2jhu5F0IDI4LzA4LzIwMjYgY2hvIG3hu41pIHPhuqNuIHBo4bqpbTogKlwicGjhuqNpIGhvdmVyIHbDoG8gdsO5bmdcbiAgICogbcOgdSB4YW5oIHRow6wgbeG7m2kga8OpbyB0aOG6oyDEkcaw4bujYyBjaG8gxJHhu5NuZyBuaOG6pXRcIiouIE7GoWkgZMO5bmcgbsOgbyBtdeG7kW4ga2nhu4N1IFwiY+G6p20gdsOgbyB0aOG6uyBsw6Aga8Opb1wiIHRow6wgdOG7sVxuICAgKiBi4bqtdCwga2jDtG5nIMSR4buVaSBow6BuaCB2aSBj4bunYSBtw6BuIGtow6FjLlxuICAgKlxuICAgKiBC4bqtdCBsw6puIHRow6wgZOG6o2kga8OpbyBiaeG6v24gbeG6pXQgdsOgIGdyaWRzdGVyIGNodXnhu4NuIHNhbmcgYGlnbm9yZUNvbnRlbnQ6IGZhbHNlYCDigJQgY2hvIGvDqW8gdOG7qyBt4buNaSBjaOG7l1xuICAgKiBUUuG7qiBwaOG6p24gdOG7rSBtYW5nIGBpZ25vcmVDb250ZW50Q2xhc3NgLiBOaOG7nSB24bqteSBraOG7kWkgY29uIG7hurFtIHRyb25nIGto4buRaSBnaMOpcCB24bqrbiBraMO0bmcgbmjhuqVjIG5o4bqnbVxuICAgKiBraOG7kWkgZ2jDqXA6IHbhu48ga2jhu5FpIGNvbiDEkcOjIG1hbmcgc+G6tW4gY2xhc3MgY2jhurduIMSRw7MuXG4gICAqL1xuICByZWFkb25seSBkcmFnRnJvbUFueXdoZXJlID0gaW5wdXQoZmFsc2UpO1xuICAvKipcbiAgICogTsahaSBkw7luZyDEkcOjIMSR4bqpeSB2w7luZyBjdeG7mW4gROG7jEMgdsOgbyB0aOG6s25nIGA8Z3JpZHN0ZXI+YCAoYuG6sW5nIENTUyBj4bunYSBjaMOtbmggbsOzKS5cbiAgICpcbiAgICog8J+UtCBC4bqtdCBj4budIG7DoHkgbMOgIHRy4bqjIGBhbmd1bGFyLWdyaWRzdGVyMmAgduG7gSDEkcO6bmcgZ2nhuqMgxJHhu4tuaCBn4buRYyBj4bunYSBuw7Mg4oCUIGBkcmFnTW92ZWAgxJHhu41jXG4gICAqIGBncmlkc3Rlci5lbC5zY3JvbGxUb3BgLCBgc2Nyb2xsKClgIMSRbyBgZ3JpZHN0ZXJFbGVtZW50Lm9mZnNldEhlaWdodGAg4oCUIG7Dqm4gdGjGsCB2aeG7h24gS0jDlE5HIGLhuq9jXG4gICAqIGPhuqd1IGBzY3JvbGxUb3BgIHNhbmcga2h1bmcgYuG7jWMgbuG7r2EsIHbDoCB2w7JuZyB04buxIGN14buZbiBraGkga8OpbyBjxaluZyBjdeG7mW4gdGjhurNuZyB2w6BvIGdyaWRzdGVyLlxuICAgKlxuICAgKiDwn5S0IFBI4bqiSSBsw6AgaW5wdXQsIEtIw5RORyBkw7IgYuG6sW5nIGBnZXRDb21wdXRlZFN0eWxlYDogdGhhbmggY3Xhu5luIG7hu5VpIGPhu6dhIGxpYnMtdWkgbeG7m2kgbMOgIHRo4bupIMSR4bq3dFxuICAgKiBgb3ZlcmZsb3dgIGzDqm4gcGjhuqduIHThu60sIG3DoCBuw7MgY2jhuqF5IFNBVSBgbmdBZnRlclZpZXdJbml0YCDigJQgZMOyIGzDumMgxJHDsyBsdcO0biByYSBcImtow7RuZyBjdeG7mW5cIiB2w6AgY+G6p3VcbiAgICogbuG7kWkgduG6q24gY+G6r20gdsOgbywgbnXhu5F0IHPhuqFjaCDEkW/huqFuIGN14buZbiB0aOG6rXQgKMSRbyAxNS8wOS8yMDI2OiBgZy5zY3JvbGxUb3AgPSAzMDBgIHhvbmcgxJHhu41jIGzhuqFpIHbhuqtuIDApLlxuICAgKi9cbiAgcmVhZG9ubHkgZ3JpZHN0ZXJPd25zU2Nyb2xsID0gaW5wdXQoZmFsc2UpO1xuICAvKipcbiAgICogVuG6vSB0aGFuaCBjdeG7mW4gbuG7lWkgY+G7p2EgbGlicy11aSBjaG8gbeG6t3QgcGjhurNuZyBM4buSTkcgKHJ14buZdCBraOG7kWkgZ2jDqXApLlxuICAgKlxuICAgKiDwn5S0IFbDuW5nIGN14buZbiBj4bunYSBt4bq3dCBwaOG6s25nIGzhu5NuZyBwaOG6o2kgZ2nhuqV1IHRoYW5oIGN14buZbiBt4bq3YyDEkeG7i25oIGPhu6dhIHRyw6xuaCBkdXnhu4d0IOKAlCBuw7MgZMOgeSwgeMOhbSwgdsOgXG4gICAqIMSDbiBt4bqldCBi4buBIHLhu5luZyBj4bunYSBsxrDhu5tpIGNvbiBuw6puIDI0IGPhu5l0IGjhurlwIGzhuqFpIG3hu5dpIGtoaSBu4buZaSBkdW5nIHbhu6thIMSR4bunIHRyw6BuLiBI4buHIHF14bqjIGzDoCBuZ8aw4budaVxuICAgKiBkw7luZyBjdeG7mW4gxJHGsOG7o2MgbmjGsG5nIEtIw5RORyBUSOG6pFkgY8OzIGfDrCDEkeG7gyBjdeG7mW46IHRo4bq7IGNvbiBu4bqxbSBkxrDhu5tpIMSRw6F5IHRyw7RuZyBuaMawIGJp4bq/biBt4bqldCAobmfGsOG7nWlcbiAgICogZMO5bmcgY2jhu4kgcmEgMTYvMDkvMjAyNikuIELhuq10IGPhu50gbsOgeSDEkeG7gyB0aMawIHZp4buHbiBn4bqvbiB0aGFuaCBjdeG7mW4gbuG7lWkg4oCUIG5ow6xuIHRo4bqleSDEkcaw4bujYyBtw6Aga2jDtG5nXG4gICAqIGNoaeG6v20gY2jhu5cuXG4gICAqL1xuICByZWFkb25seSBuZXN0ZWRTY3JvbGxiYXIgPSBpbnB1dChmYWxzZSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgem9vbURvY2tSZWYgPSB2aWV3Q2hpbGQ8RWxlbWVudFJlZjxIVE1MRWxlbWVudD4+KCd6b29tRG9jaycpO1xuICAvKiogR+G7oSBj4bqndSBu4buRaSBzY3JvbGwgxJHDoyBj4bqvbSBsw6puIHBo4bqnbiB04butIGA8Z3JpZHN0ZXI+YCDigJQgeGVtIGBicmlkZ2VTY3JvbGxUb0dyaWRzdGVyYC4gKi9cbiAgcHJpdmF0ZSByZW1vdmVTY3JvbGxCcmlkZ2U6ICgoKSA9PiB2b2lkKSB8IHVuZGVmaW5lZDtcbiAgLyoqIEfhu6EgdsOybmcgdOG7sSBjdeG7mW4ga2hpIGvDqW8g4oCUIHhlbSBgc3RhcnREcmFnQXV0b1Njcm9sbGAuICovXG4gIHByaXZhdGUgc3RvcERyYWdBdXRvU2Nyb2xsOiAoKCkgPT4gdm9pZCkgfCB1bmRlZmluZWQ7XG5cbiAgLyoqIEtob+G6o25nIGPDoWNoIHThu5tpIG3DqXAgdsO5bmcgY3Xhu5luIChweCkgYuG6r3QgxJHhuqd1IHThu7EgY3Xhu5luLCB2w6AgYsaw4bubYyBjdeG7mW4gbeG7l2kga2h1bmcgaMOsbmguICovXG4gIHByaXZhdGUgcmVhZG9ubHkgZHJhZ1Njcm9sbEVkZ2UgPSAzNjtcbiAgcHJpdmF0ZSByZWFkb25seSBkcmFnU2Nyb2xsU3RlcCA9IDg7XG4gIC8qKiBt4buXaSDDtCBt4buZdCBjaOG7lyBn4bqvbiByacOqbmcg4oCUIGPDuW5nIHRo4bupIHThu7EgduG7m2kgYGNoaWxkcmVuKClgICovXG4gIHByaXZhdGUgcmVhZG9ubHkgaXRlbUhvc3RzID0gdmlld0NoaWxkcmVuKCdpdGVtSG9zdCcsIHsgcmVhZDogVmlld0NvbnRhaW5lclJlZiB9KTtcbiAgLyoqXG4gICAqIEtodcO0biBt4bq3dCBwaOG6s25nIEzhu5JORyBj4bunYSB04burbmcga2jhu5FpIGNvbnRhaW5lciwgxJHhu4MgxJHGsGEgdsOgbyBjb21wb25lbnQgduG7jyBraGkga2jhu5FpIMSRw7MgdOG7sSBraGFpXG4gICAqIGBnZXRDb21wb25lbnRPdXRsZXRgLlxuICAgKlxuICAgKiDwn5S0IERhbmggc8OhY2ggbsOgeSBLSMOUTkcgY8O5bmcgdGjhu6kgdOG7sSB24bubaSBgY2hpbGRyZW4oKWA6IGBuZy10ZW1wbGF0ZSAjY2hpbGRDYW52YXNUcGxgIGNo4buJIHThu5NuIHThuqFpXG4gICAqIGNobyBraOG7kWkgQ8OTIENPTi4gR2jDqXAgdGhlbyBpbmRleCBsw6AgZ8OhbiBuaOG6p20ga2h1w7RuIGPhu6dhIGto4buRaSBuw6B5IGNobyBraOG7kWkga2jDoWMg4oCUIHBo4bqjaSBs4buNYyBs4bqhaVxuICAgKiDEkcO6bmcgY8O5bmcgxJFp4buBdSBraeG7h24gbcOgIHRlbXBsYXRlIGTDuW5nICh4ZW0gYGNoaWxkQ2FudmFzVGVtcGxhdGVPZmApLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBjaGlsZENhbnZhc1RlbXBsYXRlcyA9IHZpZXdDaGlsZHJlbjxUZW1wbGF0ZVJlZjx1bmtub3duPj4oJ2NoaWxkQ2FudmFzVHBsJyk7XG5cbiAgLyoqIEtodW5nIHhlbSB0csaw4bubYyB24buLIHRyw60gdGjhuqMgdGjhursgdOG7qyBiw6puIG5nb8OgaSAoc25hcHBlZCBnaG9zdCBwcmV2aWV3KSAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZHJvcEdob3N0ID0gc2lnbmFsPFRfZ3JpZExheW91dF9kcm9wR2hvc3QgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG5cbiAgcHJpdmF0ZSBhY3RpdmVIb3ZlcmVkRWw/OiBIVE1MRWxlbWVudDtcbiAgcHJpdmF0ZSByb290R3JpZHN0ZXJFbD86IEhUTUxFbGVtZW50O1xuXG4gIC8qKiBNZW51IGNo4buNbiBwcmVzZXQgdGh1IHBow7NuZyDEkWFuZyBt4bufIGhheSDEkcOzbmcgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGlzTWVudU9wZW4gPSBzaWduYWw8Ym9vbGVhbj4oZmFsc2UpO1xuXG4gIC8qKiBDw6FjIG3hu5FjIHRodSBwaMOzbmcgxJHhu4tuaCBz4bq1biAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgem9vbVByZXNldHM6IEFycmF5PFRfZ3JpZExheW91dF96b29tUHJlc2V0PiA9IFtcbiAgICB7IGxhYmVsOiAnaTE4bl96b29tX3ByZXNldF9vdmVydmlldycsIHZhbHVlOiAwLjUgfSxcbiAgICB7IGxhYmVsOiAnaTE4bl96b29tX3ByZXNldF9zbWFsbF9sYXB0b3AnLCB2YWx1ZTogMC42NyB9LFxuICAgIHsgbGFiZWw6ICdpMThuX3pvb21fcHJlc2V0X2xhcHRvcF8xMDgwcCcsIHZhbHVlOiAwLjc1IH0sXG4gICAgeyBsYWJlbDogJ2kxOG5fem9vbV9wcmVzZXRfc2NyZWVuXzEwODBwJywgdmFsdWU6IDAuODUgfSxcbiAgICB7IGxhYmVsOiAnaTE4bl96b29tX3ByZXNldF9zdGFuZGFyZCcsIHZhbHVlOiAwLjkgfSxcbiAgICB7IGxhYmVsOiAnaTE4bl96b29tX3ByZXNldF9vcmlnaW5hbF9zaXplJywgdmFsdWU6IDEgfSxcbiAgICB7IGxhYmVsOiAnaTE4bl96b29tX3ByZXNldF9sYXJnZV9zY3JlZW4nLCB2YWx1ZTogMS4xIH0sXG4gICAgeyBsYWJlbDogJ2kxOG5fem9vbV9wcmVzZXRfaGlnaF9kcGlfMmsnLCB2YWx1ZTogMS4yNSB9LFxuICBdO1xuXG4gIC8qKiBTw6F1IGNo4bqlbSBj4bunYSB0YXkgY+G6p20ga8OpbyDigJQgY2jhu4kgxJHhu4MgYEBmb3JgIGThu7FuZyDEkeG7pyA2IHRo4bq7LCBnacOhIHRy4buLIGtow7RuZyBkw7luZyB04bubaS4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGRyYWdCYXJEb3RzID0gWzAsIDEsIDIsIDMsIDQsIDVdO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgc2VydmljZSA9IGluamVjdChMaWJzVWlHcmlkTGF5b3V0U2VydmljZSk7XG5cbiAgLyoqIENo4bq/IMSR4buZIHRodSBwaMOzbmcgY8OzIMSRYW5nIGhv4bqhdCDEkeG7mW5nIHRyw6puIGNhbnZhcyBuw6B5IGtow7RuZyAoY2jhu4kgcm9vdCBjYW52YXMgZGVwdGggMCkgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGlzWm9vbUFjdGl2ZSA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICByZXR1cm4gdGhpcy5kZXB0aCgpID09PSAwICYmICh0aGlzLmVuYWJsZVpvb20oKSB8fCB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5pc1pvb21FbmFibGVkKCkpO1xuICB9KTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY3VycmVudFpvb20gPSBjb21wdXRlZCgoKSA9PiB0aGlzLnNlcnZpY2UuWm9vbSgpKTtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHpvb21QZXJjZW50ID0gY29tcHV0ZWQoKCkgPT4gTWF0aC5yb3VuZCh0aGlzLmN1cnJlbnRab29tKCkgKiAxMDApKTtcblxuICAvKipcbiAgICogTm9kZSBUSOG6rFQgY+G7p2EgbeG6t3QgcGjhurNuZyBuw6B5LlxuICAgKlxuICAgKiDwn5S0IE3hurd0IHBo4bqzbmcgR+G7kEMgKGRlcHRoIDApIMSR4buNYyBjw6J5IHThu6sgU0VSVklDRSwga2jDtG5nIGTDuW5nIGlucHV0LlxuICAgKiBOxqFpIGTDuW5nIHRoxrDhu51uZyBz4butYSBjw6J5IFThuqBJIENI4buWIChgdHJlZVNlcnZpY2UuY29tcGFjdCh0cmVlKWApIHLhu5NpIGfhu41pIGByZWZyZXNoKClgLCBuw6puIHNpZ25hbFxuICAgKiBgcm9vdCgpYCBj4bunYSBo4buNIGdp4buvIG5ndXnDqm4gdGhhbSBjaGnhur91IHbDoCBpbnB1dCBgbm9kZWAga2jDtG5nIGjhu4EgxJHhu5VpIOKAlCBgQGZvcmAga2jDtG5nIGThu7FuZyBs4bqhaVxuICAgKiBgZ3JpZHN0ZXItaXRlbWAsIGzGsOG7m2kgxJHhu6luZyBpbSBkw7kgZOG7ryBsaeG7h3UgxJHDoyBraMOhYyAoxJFvIDI4LzA4LzIwMjY6IGLhuqVtIFwiU+G6r3AgZ+G7jW5cIiBraMO0bmcgdMOhYyBk4bulbmcpLlxuICAgKiBgcmVmcmVzaCgpYCBj4bunYSBzZXJ2aWNlIHRoYXkgdGhhbSBjaGnhur91IGPDonksIG7Dqm4gxJHhu41jIHThu6sgxJHDsyBt4bubaSB0aOG6pXkgdGhheSDEkeG7lWkuXG4gICAqXG4gICAqIE3hurd0IHBo4bqzbmcgTOG7kk5HIHbhuqtuIGTDuW5nIGlucHV0OiBuw7Mgbmjhuq1uIG5vZGUgY29uIHThu6sgbeG6t3QgcGjhurNuZyBjaGEsIHbhu5FuIMSRw6MgbMOgIG9iamVjdCBt4bubaS5cbiAgICovXG4gIHByb3RlY3RlZCByZWFkb25seSBjdXJyZW50Tm9kZSA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICB0aGlzLnNlcnZpY2UuVmVyc2lvbigpO1xuXG4gICAgaWYgKHRoaXMuZGVwdGgoKSA+IDApIHtcbiAgICAgIHJldHVybiB0aGlzLm5vZGUoKSA/PyBlbXB0eVJvb3ROb2RlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuc2VydmljZS5Sb290KCkgPz8gZW1wdHlSb290Tm9kZSgpO1xuICB9KTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgY2hpbGRyZW4gPSBjb21wdXRlZCgoKSA9PiB0aGlzLmN1cnJlbnROb2RlKCkuY2hpbGRyZW4gPz8gW10pO1xuXG4gIC8qKlxuICAgKiBC4buRaSBj4bqjbmggY2hvIGB0ZW1wbGF0ZVRvb2xiYXJgLCBk4buxbmcgc+G6tW4gdGhlbyB04burbmcga2jhu5FpLlxuICAgKlxuICAgKiBE4buxbmcg4bufIGBjb21wdXRlZGAgY2jhu6kga2jDtG5nIGfhu41pIGjDoG0gdHLDqm4gdGVtcGxhdGU6IGfhu41pIGjDoG0gdHJvbmcgdGVtcGxhdGUgY2jhuqF5IGzhuqFpIG3hu5dpIHbDsm5nXG4gICAqIGTDsiB0aGF5IMSR4buVaSAocnVsZSBgZmUtY29kaW5nLWNvbnZlbnRpb24tdGVtcGxhdGVzYCBt4bulYyA2KS5cbiAgICovXG4gIHByb3RlY3RlZCByZWFkb25seSB0b29sYmFyQ29udGV4dHMgPSBjb21wdXRlZDxBcnJheTxUX2dyaWRMYXlvdXRfdG9vbGJhckNvbnRleHQ+PigoKSA9PlxuICAgIHRoaXMuY2hpbGRyZW4oKS5tYXAoKGl0ZW0pID0+ICh7XG4gICAgICAkaW1wbGljaXQ6IGl0ZW0sXG4gICAgICBpc0NvbnRhaW5lcjogdGhpcy5pc0NvbnRhaW5lcihpdGVtKSxcbiAgICAgIGNhbkFkZEluc2lkZTogdGhpcy5jYW5BZGRJbnNpZGUoaXRlbSksXG4gICAgICBjYW5Ub2dnbGVUeXBlOiB0aGlzLmNhblRvZ2dsZVR5cGUoaXRlbSksXG4gICAgICByZW1vdmU6ICgpID0+IHRoaXMub3V0UmVtb3ZlTm9kZS5lbWl0KHsgbm9kZUlkOiBpdGVtLmlkLCBub2RlOiBpdGVtLCBjb25maXJtOiAoKSA9PiB0aGlzLnNlcnZpY2UucmVtb3ZlQmxvY2soaXRlbS5pZCkgfSksXG4gICAgICBhZGRJbnNpZGU6ICgpID0+IHRoaXMuZW1pdEFkZEluc2lkZShpdGVtKSxcbiAgICAgIHRvZ2dsZVR5cGU6ICgpID0+IHRoaXMuZW1pdFRvZ2dsZVR5cGUoaXRlbSksXG4gICAgfSkpXG4gICk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGlzTmVzdGVkQ2FudmFzID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5kZXB0aCgpID4gMCk7XG4gIC8qKiBDaOG7iSBt4bq3dCBwaOG6s25nIEzhu5JORyBt4bubaSB24bq9IHRoYW5oIGN14buZbiBu4buVaSDigJQgbeG6t3QgcGjhurNuZyBn4buRYyBkbyBuxqFpIGTDuW5nIHThu7EgbG8uICovXG4gIHByb3RlY3RlZCByZWFkb25seSBzaG93TmVzdGVkU2Nyb2xsYmFyID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5pc05lc3RlZENhbnZhcygpICYmIHRoaXMubmVzdGVkU2Nyb2xsYmFyKCkpO1xuICAvKipcbiAgICogQ2jhu4kgY3Xhu5luIEThu4xDOiBi4buBIHLhu5luZyBraOG7kWkgY29uIGx1w7RuIGLhu4sgZ3JpZHN0ZXIga+G6uXAgdHJvbmcgc+G7kSBj4buZdCBj4bunYSBsxrDhu5tpIG7Dqm4ga2jDtG5nIGPDsyBnw6wgxJHhu4NcbiAgICogY3Xhu5luIG5nYW5nLlxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IG5lc3RlZFNjcm9sbE92ZXJsYXlPcHRpb25zOiBJU2Nyb2xsT3ZlcmxheU9wdGlvbnMgPSB7IHNjcm9sbFg6ICdoaWRkZW4nLCBzY3JvbGxZOiAnc2Nyb2xsJyB9O1xuXG4gIC8qKlxuICAgKiBLaGUgbmjDrG4gdGjhuqV5IGdp4buvYSBjw6FjIGto4buRaSBD4bqkUCBH4buQQy4gTeG6t3QgcGjhurNuZyBs4buTbmcgdHLhuqMgMCDigJQga2jhu5FpIGNvbiBsw6AgY8OhYyBkw7JuZyBj4bunYSBjw7luZyBt4buZdFxuICAgKiB0aOG6uywgdMOhY2ggY2jDum5nIHJhIGzDoG0gY2jDum5nIHRyw7RuZyBuaMawIHRo4bq7IHLhu51pLlxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHZpc3VhbEdhcCA9IGNvbXB1dGVkKCgpID0+ICh0aGlzLmRlcHRoKCkgPT09IDAgPyB0aGlzLnNlcnZpY2UudmlzdWFsR2FwUHggOiAwKSk7XG5cbiAgLyoqIHRy4bqnbiBs4buTbmcgY29udGFpbmVyIOKAlCBraOG7kWkgxJHDoyBjaOG6oW0gdHLhuqduIHRow6wga2jDtG5nIGNobyBjaHV54buDbiB0aMOgbmggY29udGFpbmVyICovXG4gIHByb3RlY3RlZCByZWFkb25seSBtYXhDb250YWluZXJEZXB0aCA9IGNvbXB1dGVkKCgpID0+IHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLm1heENvbnRhaW5lckRlcHRoKCkpO1xuXG4gIC8qKlxuICAgKiBM4buBIHRyb25nIGPhu6dhIGNvbnRhaW5lciwgcHguXG4gICAqXG4gICAqIPCflLQgxJDhurd0IHRyw6puIFbhu44gY29udGFpbmVyLCBraMO0bmcgxJHhurd0IHbDoG8gbMaw4bubaSBjb24uIEzGsOG7m2kgY29uIGdp4buvIG5ndXnDqm4gYG1hcmdpbjogMmAgbmjGsCBjxak7IGzhu4FcbiAgICogbsOgeSBjaOG7iSBjbyBydeG7mXQgbOG6oWkga2jhu49pIGJpw6puIHbhu48sIG5o4budIMSRw7MgdGF5IGPhuqdtIMSR4buVaSBrw61jaCB0aMaw4bubYyBj4bunYSBraOG7kWkgY29uIGx1w7RuIG7hurFtIHRyb25nIHThuqdtXG4gICAqIGNodeG7mXQgKG5nxrDhu51pIGTDuW5nIGNo4buRdCAyOC8wOC8yMDI2KS5cbiAgICovXG4gIHByb3RlY3RlZCByZWFkb25seSBjb250YWluZXJQYWRkaW5nID0gY29tcHV0ZWQoKCkgPT4gYCR7dGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuY29udGFpbmVyUGFkZGluZygpfXB4YCk7XG5cbiAgLyoqIEThuqNpIGvDqW8gY2jhu4kgaGnhu4duIOG7nyBjaOG6vyDEkeG7mSBz4butYSDigJQgY2jhur8gxJHhu5kgeGVtIGtow7RuZyBrw6lvIMSRxrDhu6NjIG7Dqm4gaGnhu4duIGThuqNpIGzDoCBnw6J5IG5o4bqnbS4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGlzRWRpdE1vZGUgPSBjb21wdXRlZCgoKSA9PiB0aGlzLnNlcnZpY2UuSXNFZGl0TW9kZSgpKTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZ3JpZE9wdGlvbnMgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgLy8gxJDhu41jIGBWZXJzaW9uYCDEkeG7gyBgaW5pdCgpYCBs4bqhaSAoxJHhu5VpIGNo4bq/IMSR4buZLCBi4bqtdC904bqvdCBjaOG7k25nIGto4buRaSkgc2luaCByYSBvcHRpb25zIE3hu5pJIOKAlFxuICAgIC8vIGdyaWRzdGVyIGNo4buJIMOhcCBk4bulbmcgY+G6pXUgaMOsbmgga2hpIHRoYW0gY2hp4bq/dSBgb3B0aW9uc2AgxJHhu5VpLlxuICAgIHRoaXMuc2VydmljZS5WZXJzaW9uKCk7XG5cbiAgICBjb25zdCBvcHRpb25zID0gdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuZ3JpZHN0ZXJPcHRpb25zKHRoaXMuZGVwdGgoKSwgKCkgPT5cbiAgICAgIHRoaXMub3V0Q2hhbmdlLmVtaXQoeyByb290OiB0aGlzLmN1cnJlbnROb2RlKCkgfSlcbiAgICApO1xuXG4gICAgaWYgKCF0aGlzLmRyYWdGcm9tQW55d2hlcmUoKSkge1xuICAgICAgcmV0dXJuIG9wdGlvbnM7XG4gICAgfVxuXG4gICAgLy8g8J+UtCBgZGVsYXlTdGFydGAgUEjhuqJJIGtow6FjIDAg4bufIGNo4bq/IMSR4buZIG7DoHkuIFbhu5tpIHRheSBj4bqnbSBjaHV5w6puIGThu6VuZyB0aMOsIGAwYCBsw6AgxJHDum5nIChi4bqlbSB2w6BvIGThuqNpXG4gICAgLy8gY2jhu4kgY8OzIHRo4buDIGzDoCDDvSDEkeG7i25oIGvDqW8pLCBuaMawbmcga2hpIGPhuqMgdGjhursgbMOgIHRheSBj4bqnbSB0aMOsIGAwYCBudeG7kXQgbHXDtG4gbeG7jWkgY8O6IGLhuqVtIHbDoG8gbsO6dCB2w6BcbiAgICAvLyBsacOqbiBr4bq/dCBiw6puIHRyb25nIHRo4bq7LiAxNTBtczogYuG6pW0gbmhhbmggduG6q24gxINuLCBnaeG7ryBs4bqhaSBt4buZdCBuaOG7i3AgbeG7m2kgbMOgIGvDqW8uXG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLm9wdGlvbnMsXG4gICAgICBkcmFnZ2FibGU6IHsgLi4ub3B0aW9ucy5kcmFnZ2FibGUsIGlnbm9yZUNvbnRlbnQ6IGZhbHNlLCBkZWxheVN0YXJ0OiAxNTAgfSxcbiAgICB9O1xuICB9KTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICAvLyBH4bqvbiBjb21wb25lbnQgY+G7p2EgbsahaSBkw7luZyB2w6BvIHThu6tuZyDDtCwgdsOgIGfhuq9uIEzhuqBJIG3hu5dpIGtoaSBjw6J5IMSR4buVaS5cbiAgICAvLyBEw7luZyBlZmZlY3QgdsOsIGBpdGVtSG9zdHMoKWAgY2jhu4kgY8OzIGdpw6EgdHLhu4sgc2F1IGtoaSBBbmd1bGFyIGThu7FuZyB4b25nIEBmb3IuXG4gICAgZWZmZWN0KCgpID0+IHtcbiAgICAgIGNvbnN0IGhvc3RzID0gdGhpcy5pdGVtSG9zdHMoKTtcbiAgICAgIGNvbnN0IGl0ZW1zID0gdGhpcy5jaGlsZHJlbigpO1xuICAgICAgY29uc3QgY2FudmFzVGVtcGxhdGVzID0gdGhpcy5jaGlsZENhbnZhc1RlbXBsYXRlcygpO1xuXG4gICAgICBpdGVtcy5mb3JFYWNoKChpdGVtLCBpKSA9PiB7XG4gICAgICAgIGNvbnN0IGhvc3QgPSBob3N0c1tpXTtcblxuICAgICAgICBpZiAoIWhvc3QpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBLaOG7kWkgY8OzIGNvbiBsw6AgQ09OVEFJTkVSOiBSVeG7mFQgbsOzIGRvIG3hurd0IHBo4bqzbmcgbOG7k25nIHbhur0uXG4gICAgICAgIC8vXG4gICAgICAgIC8vIPCflLQgTmjGsG5nIGNvbnRhaW5lciBW4bqqTiDEkcaw4bujYyBn4bqvbiBjb21wb25lbnQgbuG6v3UgbsahaSBkw7luZyBjaOG7pyDEkeG7mW5nIGtoYWkgYGdldENvbXBvbmVudE91dGxldGBcbiAgICAgICAgLy8gY2hvIGNow61uaCBraOG7kWkgxJHDsyDigJQgxJHDsyBsw6AgVuG7jiBj4bunYSBraOG7kWkgZ2jDqXA6IGjDoG5nIHRpw6p1IMSR4buBLCBuw7p0IHRodSBn4buNbiwgbuG7gW4uIFbhu48gS0jDlE5HIHRoYXlcbiAgICAgICAgLy8gdGjhur8gbeG6t3QgcGjhurNuZyBs4buTbmc7IG7DsyBOSOG6rE4gbeG6t3QgcGjhurNuZyDEkcOzIHbDoG8gdHJvbmcgcXVhIGlucHV0IGBjaGlsZENhbnZhc2AgdsOgIHThu7EgxJHhurd0IHbDoG9cbiAgICAgICAgLy8gxJHDum5nIGNo4buXIGdp4buvYSB0acOqdSDEkeG7gSB2w6AgcGjhuqduIHRow6JuIGPhu6dhIG3DrG5oLlxuICAgICAgICAvL1xuICAgICAgICAvLyBDb250YWluZXIgS0jDlE5HIGtoYWkgb3V0bGV0IHJpw6puZyB0aMOsIHbhuqtuIGLhu48gcXVhIHkgbmjGsCBjxakg4oCUIGBjb25maWcuZ2V0Q29tcG9uZW50T3V0bGV0YFxuICAgICAgICAvLyBkw7luZyBjaHVuZyBsw6AgxJHhu4MgduG6vSBO4buYSSBEVU5HIGto4buRaSBsw6EsIMSRZW0gw6FwIGNobyBjb250YWluZXIgc+G6vSDEkcOoIG3huqV0IG3hurd0IHBo4bqzbmcgbOG7k25nLlxuICAgICAgICBjb25zdCBpc0NvbnRhaW5lck5vZGUgPSAoaXRlbS5jaGlsZHJlbj8ubGVuZ3RoID8/IDApID4gMDtcblxuICAgICAgICBpZiAoaXNDb250YWluZXJOb2RlICYmICFpdGVtLmdldENvbXBvbmVudE91dGxldCkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLmF0dGFjaE5vZGUoXG4gICAgICAgICAgaXRlbSxcbiAgICAgICAgICB0aGlzLmRlcHRoKCkgKyAxLFxuICAgICAgICAgIGhvc3QsXG4gICAgICAgICAgaXNDb250YWluZXJOb2RlID8gdGhpcy5jaGlsZENhbnZhc1RlbXBsYXRlT2YoaXRlbSwgY2FudmFzVGVtcGxhdGVzKSA6IHVuZGVmaW5lZFxuICAgICAgICApO1xuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICAvLyDEkOG7k25nIGLhu5kgbuG6v3UgbW9kZWwgem9vbSBiw6puIG5nb8OgaSB0aGF5IMSR4buVaVxuICAgIGVmZmVjdCgoKSA9PiB7XG4gICAgICBjb25zdCB6ID0gdGhpcy56b29tKCk7XG4gICAgICBpZiAoeiAhPT0gdW5kZWZpbmVkICYmIE1hdGguYWJzKHogLSB0aGlzLnNlcnZpY2UuWm9vbSgpKSA+IDAuMDAxKSB7XG4gICAgICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLnNldFpvb20oeiwgdGhpcy5zdG9yYWdlS2V5KCkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgIHRoaXMuYnJpZGdlU2Nyb2xsVG9Hcmlkc3RlcigpO1xuICAgIGlmICghdGhpcy5pc1pvb21BY3RpdmUoKSB8fCAhdGhpcy5hdXRvRml0T25Mb2FkKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBrZXkgPSB0aGlzLnN0b3JhZ2VLZXkoKTtcbiAgICBsZXQgaGFzU2F2ZWQgPSBmYWxzZTtcbiAgICBpZiAoa2V5ICYmIHR5cGVvZiBsb2NhbFN0b3JhZ2UgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB0cnkge1xuICAgICAgICBoYXNTYXZlZCA9IEJvb2xlYW4obG9jYWxTdG9yYWdlLmdldEl0ZW0oa2V5KSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gaWdub3JlIGxvY2FsU3RvcmFnZSBhY2Nlc3MgZXJyb3JcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIWhhc1NhdmVkKSB7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgY29uc3Qgc2Nyb2xsRWwgPSB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudDtcbiAgICAgICAgaWYgKHNjcm9sbEVsICYmIHNjcm9sbEVsLmNsaWVudFdpZHRoID4gMCAmJiBzY3JvbGxFbC5jbGllbnRXaWR0aCA8IHRoaXMuc3RhZ2VXaWR0aCgpICsgNjQpIHtcbiAgICAgICAgICB0aGlzLmhhbmRsZXJGaXRTY3JlZW4oKTtcbiAgICAgICAgfVxuICAgICAgfSwgNTApO1xuICAgIH1cbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMucmVtb3ZlU2Nyb2xsQnJpZGdlPy4oKTtcbiAgICB0aGlzLnN0b3BEcmFnQXV0b1Njcm9sbD8uKCk7XG4gICAgdGhpcy5jbGVhbnVwRHJhZ0dyaWQoKTtcbiAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgdGhpcy5jaGlsZHJlbigpKSB7XG4gICAgICB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC5kZXRhY2hOb2RlKGl0ZW0uaWQpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBW4bq9IGzhuqFpIGzGsOG7m2kg4oCUIGfhu41pIGtoaSBraHVuZyBuZ2/DoGkgxJHhu5VpIGvDrWNoIHRoxrDhu5tjIGhv4bq3YyBjw6J5IHbhu6thIMSR4buVaSBkaeG7h24gcuG7mW5nLiAqL1xuICBnZXQgRnVuY3Rpb25Db250cm9sKCkge1xuICAgIHJldHVybiB7XG4gICAgICB2ZUxhaUx1b2k6ICgpID0+IHRoaXMuZ3JpZHN0ZXJSZWYoKT8ub3B0aW9uc0NoYW5nZWQoKSxcbiAgICAgIHpvb21JbjogKCkgPT4gdGhpcy5oYW5kbGVyWm9vbUluKCksXG4gICAgICB6b29tT3V0OiAoKSA9PiB0aGlzLmhhbmRsZXJab29tT3V0KCksXG4gICAgICByZXNldFpvb206ICgpID0+IHRoaXMuaGFuZGxlclJlc2V0Wm9vbSgpLFxuICAgICAgZml0VG9TY3JlZW46ICgpID0+IHRoaXMuaGFuZGxlckZpdFNjcmVlbigpLFxuICAgICAgc2V0Wm9vbTogKHZhbDogbnVtYmVyKSA9PiB7XG4gICAgICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLnNldFpvb20odmFsLCB0aGlzLnN0b3JhZ2VLZXkoKSk7XG4gICAgICAgIHRoaXMuc3luY1pvb21PdXQoKTtcbiAgICAgIH0sXG4gICAgICAvLyBWw7luZyBjdeG7mW4gY+G7p2EgbeG6t3QgcGjhurNuZyDigJQgbsahaSBkw7luZyBj4bqnbiBuw7MgxJHhu4MgdOG7sSBjdeG7mW4gdOG7m2kgbeG7mXQga2jhu5FpLCBob+G6t2MgxJFvIHRv4bqhIMSR4buZIGtoaSBrw6lvLlxuICAgICAgLy8gS2jDtG5nIG3hu58g4bufIMSRw6J5IHRow6wgbsahaSBkw7luZyBwaOG6o2kgZMOyIGLhurFuZyB0w6puIGzhu5twIENTUyBu4buZaSBi4buZLCB2w6AgdMOqbiDEkcOzIMSR4buVaSBsw7pjIG7DoG8ga2jDtG5nIGhheS5cbiAgICAgIHNjcm9sbEFyZWE6ICgpOiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCA9PiB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudCxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIELhuq9jIEPhuqZVIE7hu5BJIMSR4buDIGA8Z3JpZHN0ZXI+YCBiw6FvIGPDoW8gc2Nyb2xsIGPhu6dhIEtIVU5HIENV4buYTiwgdGhheSB2w6wgc2Nyb2xsIGPhu6dhIGNow61uaCBuw7MuXG4gICAqXG4gICAqIPCflLQgVsOsIHNhbyBj4bqnbjogZ3JpZHN0ZXIgxJHhu5VpIHbhu4sgdHLDrSBjaHXhu5l0IHRow6BuaCDDtCBsxrDhu5tpIGLhurFuZ1xuICAgKiBgZS5jbGllbnRZICsgKGVsLnNjcm9sbFRvcCAtIGVsLm9mZnNldFRvcClgICh4ZW0gYGNhbGN1bGF0ZUl0ZW1Qb3NpdGlvbldpdGhvdXRTY2FsZWAgdsOgXG4gICAqIGB0aGlzLm9mZnNldFRvcCA9IHRoaXMuZ3JpZHN0ZXIuZWwuc2Nyb2xsVG9wIC0gdGhpcy5ncmlkc3Rlci5lbC5vZmZzZXRUb3BgIHRyb25nXG4gICAqIGBhbmd1bGFyLWdyaWRzdGVyMkAxOGApLiBDw7RuZyB0aOG7qWMgY2jhu4kgxJHDum5nIGtoaSBDSMONTkggYDxncmlkc3Rlcj5gIGzDoCBwaOG6p24gdOG7rSBjdeG7mW4g4oCUIMSRw7puZyBuaMawXG4gICAqIGNvbW1lbnQgxJHDoyBnaGkg4bufIHRlbXBsYXRlIGNobyBgb2Zmc2V0UGFyZW50YC4g4bueIG5ow6FuaCBUSFUgUEjDk05HLCB2aeG7h2MgY3Xhu5luIG7hurFtIOG7nyBgI3Njcm9sbEFyZWFgXG4gICAqIGPDsm4gYDxncmlkc3Rlcj5gIGdp4buvIMSR4bunIGPhu6Egc8OibiBraOG6pXUgYsOqbiB0cm9uZyBt4buZdCB04bqnbmcgYHRyYW5zZm9ybTogc2NhbGVgLCBuw6puXG4gICAqIGBlbC5zY3JvbGxUb3BgIGx1w7RuIGLhurFuZyAwOiBt4buNaSBweCBuZ8aw4budaSBkw7luZyBjdeG7mW4gxJHhu4F1IHbDtCBow6xuaCB24bubaSB0aMawIHZp4buHbi4gSOG7hyBxdeG6oyDEkW8gxJHGsOG7o2NcbiAgICogMTUvMDkvMjAyNiB0csOqbiBt4bq3dCBwaOG6s25nIGPhuqVwIHRyYW5nOiB04buxIGN14buZbiBsw7pjIGvDqW8ga2jDtG5nIGNo4bqheSwgdsOgIG7hur91IG7GoWkgZMO5bmcgdOG7sSBjdeG7mW4gdGjDrCB0aOG6u1xuICAgKiDEkeG7qW5nIGltIHRyb25nIGtoaSBt4bq3dCBwaOG6s25nIHRyw7RpLlxuICAgKlxuICAgKiDwn5S0IENI4buIIHbDoSBgc2Nyb2xsVG9wYC9gc2Nyb2xsTGVmdGAuIEPhuqRNIMSR4bulbmcgYG9mZnNldFdpZHRoYC9gb2Zmc2V0SGVpZ2h0YDogYHNldEdyaWRTaXplKClgIGPhu6dhXG4gICAqIGdyaWRzdGVyIMSR4buNYyBoYWkgY8OhaSDEkcOzIMSR4buDIHTDrW5oIGBjdXJDb2xXaWR0aGAsIMSRw6ggdsOgbyBsw6AgduG7oSBi4buBIHLhu5luZyBj4buZdCBj4bunYSBt4buNaSBsxrDhu5tpLlxuICAgKlxuICAgKiDwn5S0IENoaWEgY2hvIG3hu6ljIHBow7NuZzogYG9mZnNldFRvcGAgbMOgIHB4IELhu5AgQ+G7pEMsIGPDsm4gYHNjcm9sbFRvcGAgY+G7p2Ega2h1bmcgY3Xhu5luIGzDoCBweCDEkcOjIG5ow6JuXG4gICAqIHRodSBwaMOzbmcuIEtow7RuZyBxdXkgxJHhu5VpIHRow6wg4bufIG3hu6ljIHBow7NuZyBraMOhYyAxMDAlIGto4buRaSB0csO0aSBuaGFuaCBoxqFuIGhv4bq3YyBjaOG6rW0gaMahbiBjb24gdHLhu48uXG4gICAqL1xuICAvKiogUGjhuqduIHThu60gdGjhuq10IHPhu7EgY3Xhu5luIHRoZW8gY2hp4buBdSBk4buNYyDigJQgYDxncmlkc3Rlcj5gIG7hur91IG7GoWkgZMO5bmcgxJHDoyDEkeG6qXkgY3Xhu5luIHbDoG8gdHJvbmcgbsOzLiAqL1xuICBwcml2YXRlIHZ1bmdDdW9uRG9jKCk6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBncmlkc3RlckVsID0gdGhpcy5ncmlkc3RlclJlZigpPy5lbCBhcyBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZDtcblxuICAgIGlmICh0aGlzLmdyaWRzdGVyT3duc1Njcm9sbCgpICYmIGdyaWRzdGVyRWwpIHtcbiAgICAgIHJldHVybiBncmlkc3RlckVsO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudDtcbiAgfVxuXG4gIHByaXZhdGUgYnJpZGdlU2Nyb2xsVG9Hcmlkc3RlcigpOiB2b2lkIHtcbiAgICAvLyBgR3JpZHN0ZXJDb21wb25lbnQuZWxgIGzDoCBIVE1MRWxlbWVudCB0aOG6s25nLCBraMO0bmcgcGjhuqNpIGBFbGVtZW50UmVmYCAoeGVtIGBncmlkc3Rlci5jb21wb25lbnQuZC50c2ApLlxuICAgIGNvbnN0IGdyaWRzdGVyRWwgPSB0aGlzLmdyaWRzdGVyUmVmKCk/LmVsO1xuICAgIGNvbnN0IHNjcm9sbEVsID0gdGhpcy5zY3JvbGxBcmVhUmVmKCk/Lm5hdGl2ZUVsZW1lbnQ7XG5cbiAgICBpZiAoIXRoaXMuaXNab29tQWN0aXZlKCkgfHwgIWdyaWRzdGVyRWwgfHwgIXNjcm9sbEVsIHx8IHRoaXMucmVtb3ZlU2Nyb2xsQnJpZGdlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8g8J+UtCBgPGdyaWRzdGVyPmAgxJHDoyB04buxIGN14buZbiB0aMOsIEPhuqRNIGLhuq9jIGPhuqd1OiBj4bqndSBu4buRaSBnaGkgxJHDqCBgc2Nyb2xsVG9wYC9gc2Nyb2xsTGVmdGAgY+G7p2EgcGjhuqduIHThu61cbiAgICAvLyBi4bqxbmcgc+G7kSBj4bunYSBraHVuZyBi4buNYywgbsOqbiBjaMOtbmggxJFv4bqhbiBjdeG7mW4gdGjhuq10IGPhu6dhIGdyaWRzdGVyIGLhu4sgbnXhu5F0IOKAlCDEkeG6t3QgYHNjcm9sbFRvcGAga2jDtG5nXG4gICAgLy8gxINuLCBrw6lvIGto4buRaSBsw6AgbsOzIG5o4bqjeSB24buBIMSR4bqndSBsxrDhu5tpLlxuICAgIGlmICh0aGlzLmdyaWRzdGVyT3duc1Njcm9sbCgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMucmVtb3ZlU2Nyb2xsQnJpZGdlID0gYnJpZGdlU2Nyb2xsQmV0d2VlbkVsZW1lbnRzKGdyaWRzdGVyRWwsIHNjcm9sbEVsLCAoKSA9PiB0aGlzLnNlcnZpY2UuWm9vbSgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBU4buxIGN14buZbiBt4bq3dCBwaOG6s25nIGtoaSBuZ8aw4budaSBkw7luZyBrw6lvIGto4buRaSB04bubaSBzw6F0IG3DqXAuXG4gICAqXG4gICAqIPCflLQgVOG7sSBjdeG7mW4gc+G6tW4gY8OzIGPhu6dhIGdyaWRzdGVyIChgc2Nyb2xsU2Vuc2l0aXZpdHlgKSBraMO0bmcgZMO5bmcgxJHGsOG7o2Mg4bufIG5ow6FuaCB0aHUgcGjDs25nOiBow6BtXG4gICAqIGBzY3JvbGwoKWAgY+G7p2EgbsOzIMSRbyBi4bqxbmcgYGdyaWRzdGVyRWxlbWVudC5vZmZzZXRIZWlnaHRgLCBtw6AgYDxncmlkc3Rlcj5gIGNhbyBi4bqxbmcgQ+G6oiBzw6JuIGto4bqldVxuICAgKiBuw6puIGNvbiB0cuG7jyBraMO0bmcgYmFvIGdp4budIFwidOG7m2kgc8OhdCBtw6lwXCIgdGhlbyBjb24gbeG6r3QgY+G7p2EgbsOzLlxuICAgKlxuICAgKiDwn5S0IENo4buJIGN14buZbiBraGkgdGjGsCB2aeG7h24gxJBBTkcga8OpbyB0aOG6rXQgKGAuZ3JpZHN0ZXItaXRlbS1tb3ZpbmdgKSDigJQga2jDtG5nIGPDsyBjaOG7kXQgbsOgeSB0aMOsIG3hu5l0IGPDulxuICAgKiBi4bqlbSBiw6xuaCB0aMaw4budbmcgZ+G6p24gbcOpcCBjxaluZyBsw6BtIG3hurd0IHBo4bqzbmcgdHLDtGkuXG4gICAqL1xuICBwcm90ZWN0ZWQgaGFuZGxlclNjcm9sbEFyZWFQb2ludGVyRG93bigpOiB2b2lkIHtcbiAgICB0aGlzLnN0YXJ0RHJhZ0F1dG9TY3JvbGwoKTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhcnREcmFnQXV0b1Njcm9sbCgpOiB2b2lkIHtcbiAgICB0aGlzLnN0b3BEcmFnQXV0b1Njcm9sbD8uKCk7XG4gICAgY29uc3Qgc2Nyb2xsRWwgPSB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudDtcbiAgICBjb25zdCBjdW9uRG9jID0gdGhpcy52dW5nQ3VvbkRvYygpO1xuXG4gICAgaWYgKCFzY3JvbGxFbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgdmlUcmk6IHsgeDogbnVtYmVyOyB5OiBudW1iZXIgfSB8IHVuZGVmaW5lZDtcbiAgICBsZXQga2h1bmc6IG51bWJlciB8IHVuZGVmaW5lZDtcbiAgICBjb25zdCBjaGF5ID0gKCk6IHZvaWQgPT4ge1xuICAgICAgaWYgKHZpVHJpICYmIHNjcm9sbEVsLnF1ZXJ5U2VsZWN0b3IoJy5ncmlkc3Rlci1pdGVtLW1vdmluZycpKSB7XG4gICAgICAgIGNvbnN0IGhvcCA9IHNjcm9sbEVsLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCBidW9jID0geyBlZGdlOiB0aGlzLmRyYWdTY3JvbGxFZGdlLCBzdGVwOiB0aGlzLmRyYWdTY3JvbGxTdGVwIH07XG4gICAgICAgIC8vIGBlZGdlU2Nyb2xsU3RlcGAgdOG7sSBr4bq5cCBtw6lwIHRoZW8gY+G6oW5oIGtodW5nLCBuw6puIGtodW5nIG5o4buPIG5oxrAgcnXhu5l0IGto4buRaSBnaMOpcCB24bqrbiDEkcO6bmcuXG4gICAgICAgIGNvbnN0IGRvYyA9IGVkZ2VTY3JvbGxTdGVwKHsgLi4uYnVvYywgcG9zOiB2aVRyaS55LCBtaW46IGhvcC50b3AsIG1heDogaG9wLmJvdHRvbSB9KTtcbiAgICAgICAgY29uc3QgbmdhbmcgPSBlZGdlU2Nyb2xsU3RlcCh7IC4uLmJ1b2MsIHBvczogdmlUcmkueCwgbWluOiBob3AubGVmdCwgbWF4OiBob3AucmlnaHQgfSk7XG5cbiAgICAgICAgaWYgKGRvYyAhPT0gMCB8fCBuZ2FuZyAhPT0gMCkge1xuICAgICAgICAgIC8vIEhhaSBjaGnhu4F1IGPDsyB0aOG7gyBu4bqxbSDhu58gSEFJIHBo4bqnbiB04butIGtow6FjIG5oYXU6IG7GoWkgZMO5bmcgxJHGsOG7o2MgcGjDqXAgxJHhuql5IGN14buZbiBk4buNYyB2w6BvIHRyb25nXG4gICAgICAgICAgLy8gYDxncmlkc3Rlcj5gIG3DoCB24bqrbiDEkeG7gyBjdeG7mW4gbmdhbmcg4bufIGtodW5nIGLhu41jIChzw6JuIGto4bqldSBy4buZbmcgaMahbiBraHVuZyBraGkgcGjDs25nIHRvKS5cbiAgICAgICAgICAoY3VvbkRvYyA/PyBzY3JvbGxFbCkuc2Nyb2xsVG9wICs9IGRvYztcbiAgICAgICAgICBzY3JvbGxFbC5zY3JvbGxMZWZ0ICs9IG5nYW5nO1xuICAgICAgICAgIC8vIPCflLQgQ3Xhu5luIHhvbmcgUEjhuqJJIGLhuq9uIGzhuqFpIG3hu5l0IG5o4buLcCBgbW91c2Vtb3ZlYCDhu58gxJHDum5nIGNo4buXIGNvbiB0cuG7jyDEkWFuZyDEkeG7qW5nLiBncmlkc3RlciBjaOG7iVxuICAgICAgICAgIC8vIHTDrW5oIGzhuqFpIHbhu4sgdHLDrSBraOG7kWkgdHJvbmcgYGRyYWdNb3ZlYCwgdOG7qWMgY2jhu4kga2hpIGPDsyBz4buxIGtp4buHbiBjaHXhu5l0IOKAlCBjdeG7mW4gYuG6sW5nIG3DoyBtw6BcbiAgICAgICAgICAvLyBraMO0bmcgYuG6r24gdGjDrCBnaeG7r2EgaGFpIGzhuqduIG5nxrDhu51pIGTDuW5nIHLDqiBjaHXhu5l0LCBt4bq3dCBwaOG6s25nIHRyw7RpIGPDsm4ga2jhu5FpIMSR4bupbmcgaW0gcuG7k2kgZ2nhuq10XG4gICAgICAgICAgLy8gduG7gSBjaOG7lyDEkcO6bmcg4bufIG5o4buLcCBzYXUgKG5nxrDhu51pIGTDuW5nIGNo4buJIHJhIDE1LzA5LzIwMjY6IFwia8OpbyB4deG7kW5nIGTGsOG7m2kgYuG7iyBnaeG6rXQgZ2nhuq10XCIpLlxuICAgICAgICAgIC8vIELhuq9uIGBtb3VzZW1vdmVgIGNo4bupIGtow7RuZyBwaOG6o2kgYHBvaW50ZXJtb3ZlYDogZHJhZ2dhYmxlIGPhu6dhIGdyaWRzdGVyIGNo4buJIG5naGUgYG1vdXNlbW92ZWBcbiAgICAgICAgICAvLyB2w6AgYHRvdWNobW92ZWAsIG7Dqm4gY8Wpbmcga2jDtG5nIGThu5lpIG5nxrDhu6NjIHbDoG8gY2jDrW5oIGxpc3RlbmVyIGLDqm4gZMaw4bubaS5cbiAgICAgICAgICBkb2N1bWVudC5kaXNwYXRjaEV2ZW50KFxuICAgICAgICAgICAgbmV3IE1vdXNlRXZlbnQoJ21vdXNlbW92ZScsIHsgY2xpZW50WDogdmlUcmkueCwgY2xpZW50WTogdmlUcmkueSwgYnViYmxlczogdHJ1ZSwgY2FuY2VsYWJsZTogdHJ1ZSwgdmlldzogd2luZG93IH0pXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAga2h1bmcgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoY2hheSk7XG4gICAgfTtcbiAgICBjb25zdCB0aGVvQ2h1b3QgPSAoZXZlbnQ6IFBvaW50ZXJFdmVudCk6IHZvaWQgPT4ge1xuICAgICAgdmlUcmkgPSB7IHg6IGV2ZW50LmNsaWVudFgsIHk6IGV2ZW50LmNsaWVudFkgfTtcbiAgICB9O1xuXG4gICAgdGhpcy5zdG9wRHJhZ0F1dG9TY3JvbGwgPSAoKSA9PiB7XG4gICAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdwb2ludGVybW92ZScsIHRoZW9DaHVvdCk7XG4gICAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdwb2ludGVydXAnLCBkdW5nKTtcbiAgICAgIGlmIChraHVuZyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKGtodW5nKTtcbiAgICAgICAga2h1bmcgPSB1bmRlZmluZWQ7XG4gICAgICB9XG4gICAgICB0aGlzLnN0b3BEcmFnQXV0b1Njcm9sbCA9IHVuZGVmaW5lZDtcbiAgICB9O1xuXG4gICAgY29uc3QgZHVuZyA9ICgpOiB2b2lkID0+IHRoaXMuc3RvcERyYWdBdXRvU2Nyb2xsPy4oKTtcblxuICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJtb3ZlJywgdGhlb0NodW90KTtcbiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVydXAnLCBkdW5nKTtcbiAgICBjaGF5KCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlclpvb21JbigpOiB2b2lkIHtcbiAgICB0aGlzLnNlcnZpY2UuRnVuY3Rpb25zQ29udHJvbC56b29tSW4odGhpcy56b29tU3RlcCgpLCB0aGlzLnN0b3JhZ2VLZXkoKSk7XG4gICAgdGhpcy5zeW5jWm9vbU91dCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJab29tT3V0KCk6IHZvaWQge1xuICAgIHRoaXMuc2VydmljZS5GdW5jdGlvbnNDb250cm9sLnpvb21PdXQodGhpcy56b29tU3RlcCgpLCB0aGlzLnN0b3JhZ2VLZXkoKSk7XG4gICAgdGhpcy5zeW5jWm9vbU91dCgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJSZXNldFpvb20oKTogdm9pZCB7XG4gICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wucmVzZXRab29tKHRoaXMuc3RvcmFnZUtleSgpKTtcbiAgICB0aGlzLnN5bmNab29tT3V0KCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckZpdFNjcmVlbigpOiB2b2lkIHtcbiAgICB0aGlzLmlzTWVudU9wZW4uc2V0KGZhbHNlKTtcbiAgICBjb25zdCBzY3JvbGxFbCA9IHRoaXMuc2Nyb2xsQXJlYVJlZigpPy5uYXRpdmVFbGVtZW50O1xuICAgIGNvbnN0IGNvbnRhaW5lcldpZHRoID0gc2Nyb2xsRWw/LmNsaWVudFdpZHRoIHx8ICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdy5pbm5lcldpZHRoIC0gMzAwIDogMTIwMCk7XG4gICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuZml0VG9TY3JlZW4oY29udGFpbmVyV2lkdGgsIHRoaXMuc3RhZ2VXaWR0aCgpLCA0OCwgdGhpcy5zdG9yYWdlS2V5KCkpO1xuICAgIHRoaXMuc3luY1pvb21PdXQoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyVG9nZ2xlTWVudSgpOiB2b2lkIHtcbiAgICB0aGlzLmlzTWVudU9wZW4udXBkYXRlKChvcGVuKSA9PiAhb3Blbik7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlclNlbGVjdFByZXNldCh2YWx1ZTogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5pc01lbnVPcGVuLnNldChmYWxzZSk7XG4gICAgdGhpcy5zZXJ2aWNlLkZ1bmN0aW9uc0NvbnRyb2wuc2V0Wm9vbSh2YWx1ZSwgdGhpcy5zdG9yYWdlS2V5KCkpO1xuICAgIHRoaXMuc3luY1pvb21PdXQoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBpc0N1cnJlbnRQcmVzZXQodmFsdWU6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgIHJldHVybiBNYXRoLmFicyh0aGlzLmN1cnJlbnRab29tKCkgLSB2YWx1ZSkgPCAwLjAxO1xuICB9XG5cbiAgcHJpdmF0ZSBzeW5jWm9vbU91dCgpOiB2b2lkIHtcbiAgICBjb25zdCB2YWwgPSB0aGlzLnNlcnZpY2UuWm9vbSgpO1xuICAgIHRoaXMub3V0Wm9vbUNoYW5nZS5lbWl0KHZhbCk7XG4gICAgaWYgKHRoaXMuem9vbSgpICE9PSB2YWwpIHtcbiAgICAgIHRoaXMuem9vbS5zZXQodmFsKTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlcldoZWVsKGV2ZW50OiBXaGVlbEV2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmlzWm9vbUFjdGl2ZSgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSkge1xuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgIGlmIChldmVudC5kZWx0YVkgPCAwKSB7XG4gICAgICAgIHRoaXMuaGFuZGxlclpvb21JbigpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5kZWx0YVkgPiAwKSB7XG4gICAgICAgIHRoaXMuaGFuZGxlclpvb21PdXQoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkb2N1bWVudDprZXlkb3duJywgWyckZXZlbnQnXSlcbiAgcHJvdGVjdGVkIGhhbmRsZXJEb2N1bWVudEtleURvd24oZXZlbnQ6IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNab29tQWN0aXZlKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoZXZlbnQuY3RybEtleSB8fCBldmVudC5tZXRhS2V5KSB7XG4gICAgICBpZiAoZXZlbnQua2V5ID09PSAnPScgfHwgZXZlbnQua2V5ID09PSAnKycpIHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgdGhpcy5oYW5kbGVyWm9vbUluKCk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJy0nIHx8IGV2ZW50LmtleSA9PT0gJ18nKSB7XG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgIHRoaXMuaGFuZGxlclpvb21PdXQoKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnMCcpIHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgdGhpcy5oYW5kbGVyUmVzZXRab29tKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignZG9jdW1lbnQ6Y2xpY2snLCBbJyRldmVudCddKVxuICBwcm90ZWN0ZWQgaGFuZGxlckRvY3VtZW50Q2xpY2soZXZlbnQ6IE1vdXNlRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNNZW51T3BlbigpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgZG9ja0VsID0gdGhpcy56b29tRG9ja1JlZigpPy5uYXRpdmVFbGVtZW50O1xuICAgIGlmIChkb2NrRWwgJiYgIWRvY2tFbC5jb250YWlucyhldmVudC50YXJnZXQgYXMgTm9kZSkpIHtcbiAgICAgIHRoaXMuaXNNZW51T3Blbi5zZXQoZmFsc2UpO1xuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyVHJhY2tOb2RlKF9pbmRleDogbnVtYmVyLCBpdGVtOiBUX2dyaWRMYXlvdXRfbm9kZSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGl0ZW0uaWQ7XG4gIH1cblxuICAvKipcbiAgICog8J+UtCBDaOG6t24gYG1vdXNlZG93bmAgbuG7lWkgbMOqbiBsxrDhu5tpIENIQSBraGkgdGhhbyB0w6FjIHRyb25nIGzGsOG7m2kgY29uLlxuICAgKiBLaMO0bmcgY2jhurduIHRow6wgY+G6oyBoYWkgbMaw4bubaSBjw7luZyBuaOG6rW4gbeG7mXQgY8O6IGLhuqVtOiBrw6lvIGto4buRaSBjb24gbMOgIG5o4bqlYyBsdcO0biBjb250YWluZXIuXG4gICAqL1xuICAvKipcbiAgICogQ2jhurduIGBtb3VzZWRvd25gIGPhu6dhIGto4buRaSB0cm9uZyBsxrDhu5tpIEzhu5JORyBu4buVaSBsw6puIGzGsOG7m2kgY2hhLlxuICAgKlxuICAgKiBDaOG7iSBjaOG6t24g4bufIGzGsOG7m2kgbOG7k25nOiBsxrDhu5tpIGPhuqVwIHRyYW5nIGtow7RuZyBjw7MgY2hhIG7DoG8gxJHhu4MgY2jhurduLCB2w6AgY2jhurduIOG7nyDEkcOzIHPhur0gbnXhu5F0IGx1w7RuIHRoYW9cbiAgICogdMOhYyBj4bunYSBjaMOtbmggbsOzLlxuICAgKi9cbiAgLyoqXG4gICAqIPCflLQgQmEgaGFuZGxlciBkxrDhu5tpIMSRw6J5IFBI4bqiSSBgc3RvcFByb3BhZ2F0aW9uYC5cbiAgICogTsO6dCBu4bqxbSB0cm9uZyB2w7luZyBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgLCBraMO0bmcgY2jhurduIHRow6wgY8O6IGLhuqVtIGLhu4sgZ3JpZHN0ZXIgaGnhu4N1IGzDoCBi4bqvdCDEkeG6p3Uga8Opb1xuICAgKiB2w6AgYGRlbGF5U3RhcnQ6IDE2MGAgbnXhu5F0IGx1w7RuIGBjbGlja2Ag4oCUIG5nxrDhu51pIGTDuW5nIHRo4bqleSBuaMawIG7DunQgY2jhur90LlxuICAgKi9cbiAgcHJvdGVjdGVkIGhhbmRsZXJSZW1vdmUoZXZlbnQ6IEV2ZW50LCBub2RlOiBUX2dyaWRMYXlvdXRfbm9kZSk6IHZvaWQge1xuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgdGhpcy5vdXRSZW1vdmVOb2RlLmVtaXQoeyBub2RlSWQ6IG5vZGUuaWQsIG5vZGUsIGNvbmZpcm06ICgpID0+IHRoaXMuc2VydmljZS5yZW1vdmVCbG9jayhub2RlLmlkKSB9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyQWRkSW5zaWRlKGV2ZW50OiBFdmVudCwgbm9kZTogVF9ncmlkTGF5b3V0X25vZGUpOiB2b2lkIHtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIHRoaXMuZW1pdEFkZEluc2lkZShub2RlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyVG9nZ2xlVHlwZShldmVudDogRXZlbnQsIG5vZGU6IFRfZ3JpZExheW91dF9ub2RlKTogdm9pZCB7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICB0aGlzLmVtaXRUb2dnbGVUeXBlKG5vZGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIENo4bq3biBjw7ogYuG6pW0gdHJvbmcgdGhhbmggY8O0bmcgY+G7pSBu4buVaSBsw6puIGdyaWRzdGVyLlxuICAgKlxuICAgKiBDaOG7iSBgc3RvcFByb3BhZ2F0aW9uYCwgS0jDlE5HIGBwcmV2ZW50RGVmYXVsdGA6IG7DunQgYsOqbiB0cm9uZyB0ZW1wbGF0ZSBj4bunYSBuxqFpIGTDuW5nIHbhuqtuIGPhuqduXG4gICAqIGjDoG5oIHZpIG3hurdjIMSR4buLbmggY+G7p2EgbcOsbmggKG3hu58gZHJvcGRvd24sIGZvY3Vz4oCmKS5cbiAgICovXG4gIHByb3RlY3RlZCBoYW5kbGVyU3RvcEV2ZW50KGV2ZW50OiBFdmVudCk6IHZvaWQge1xuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICB9XG5cbiAgLyoqIFBow6F0IGBvdXRBZGRCbG9ja0luc2lkZWAg4oCUIGTDuW5nIGNodW5nIGNobyBuw7p0IG3hurdjIMSR4buLbmggdsOgIGB0ZW1wbGF0ZVRvb2xiYXJgLiAqL1xuICBwcml2YXRlIGVtaXRBZGRJbnNpZGUobm9kZTogVF9ncmlkTGF5b3V0X25vZGUpOiB2b2lkIHtcbiAgICB0aGlzLm91dEFkZEJsb2NrSW5zaWRlLmVtaXQoe1xuICAgICAgbm9kZUlkOiBub2RlLmlkLFxuICAgICAgbm9kZSxcbiAgICAgIGNvbmZpcm06IChpdGVtOiBUX2dyaWRMYXlvdXRfbmV3Tm9kZSkgPT4gdGhpcy5zZXJ2aWNlLmFkZEJsb2NrKGl0ZW0sIG5vZGUuaWQpLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIFBow6F0IGBvdXRUb2dnbGVUeXBlYCDigJQgZMO5bmcgY2h1bmcgY2hvIG7DunQgbeG6t2MgxJHhu4tuaCB2w6AgYHRlbXBsYXRlVG9vbGJhcmAuICovXG4gIHByaXZhdGUgZW1pdFRvZ2dsZVR5cGUobm9kZTogVF9ncmlkTGF5b3V0X25vZGUpOiB2b2lkIHtcbiAgICB0aGlzLm91dFRvZ2dsZVR5cGUuZW1pdCh7XG4gICAgICBub2RlSWQ6IG5vZGUuaWQsXG4gICAgICBub2RlLFxuICAgICAgLy8gR+G7mXAgaGF5IHTDoWNoIHN1eSByYSB04burIGNow61uaCBraOG7kWk6IMSRYW5nIGzDoCBjb250YWluZXIgdGjDrCB0w6FjaCwga2jDtG5nIHRow6wgZ+G7mXAuXG4gICAgICBjb25maXJtOiAoKSA9PiAodGhpcy5pc0NvbnRhaW5lcihub2RlKSA/IHRoaXMuc2VydmljZS50b1NpbmdsZShub2RlLmlkKSA6IHRoaXMuc2VydmljZS50b0NvbnRhaW5lcihub2RlLmlkKSksXG4gICAgfSk7XG4gIH1cblxuICAvKioga2jhu5FpIG7DoHkgY8OzIHBo4bqjaSBjb250YWluZXIga2jDtG5nIChjw7Mga2jhu5FpIGNvbikgKi9cbiAgcHJvdGVjdGVkIGlzQ29udGFpbmVyKGl0ZW06IFRfZ3JpZExheW91dF9ub2RlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChpdGVtLmNoaWxkcmVuPy5sZW5ndGggPz8gMCkgPiAwO1xuICB9XG5cbiAgLyoqXG4gICAqIEzhuqV5IMSRw7puZyBraHXDtG4gbeG6t3QgcGjhurNuZyBs4buTbmcgY+G7p2EgTeG7mFQga2jhu5FpLlxuICAgKlxuICAgKiDwn5S0IGB2aWV3Q2hpbGRyZW4oJ2NoaWxkQ2FudmFzVHBsJylgIGNo4buJIGNo4bupYSBraHXDtG4gY+G7p2Ega2jhu5FpIEPDkyBDT04sIGPDsm4gYGNoaWxkcmVuKClgIGNo4bupYSBt4buNaVxuICAgKiBraOG7kWkg4oCUIGhhaSBkYW5oIHPDoWNoIGzhu4djaCBuaGF1LiBQaOG6o2kgbOG7jWMgYGNoaWxkcmVuKClgIHRoZW8gY8O5bmcgxJFp4buBdSBraeG7h24gbcOgIHRlbXBsYXRlIGTDuW5nIHLhu5NpXG4gICAqIG3hu5tpIHRyYSB0aGVvIHbhu4sgdHLDrSB0cm9uZyBkYW5oIHPDoWNoIMSRw6MgbOG7jWM7IGdow6lwIHRo4bqzbmcgdGhlbyBpbmRleCBj4bunYSBgY2hpbGRyZW4oKWAgbMOgIGfDoW4ga2h1w7RuXG4gICAqIGPhu6dhIGto4buRaSBuw6B5IGNobyBraOG7kWkga2jDoWMsIHbDoCB0aOG6uyBjb24gaGnhu4duIHNhaSBjaOG7ly5cbiAgICovXG4gIHByaXZhdGUgY2hpbGRDYW52YXNUZW1wbGF0ZU9mKFxuICAgIGl0ZW06IFRfZ3JpZExheW91dF9ub2RlLFxuICAgIHRlbXBsYXRlczogcmVhZG9ubHkgVGVtcGxhdGVSZWY8dW5rbm93bj5bXVxuICApOiBUZW1wbGF0ZVJlZjx1bmtub3duPiB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3QgaW5kZXggPSB0aGlzLmNoaWxkcmVuKClcbiAgICAgIC5maWx0ZXIoKG5vZGUpID0+IChub2RlLmNoaWxkcmVuPy5sZW5ndGggPz8gMCkgPiAwKVxuICAgICAgLmZpbmRJbmRleCgobm9kZSkgPT4gbm9kZS5pZCA9PT0gaXRlbS5pZCk7XG5cbiAgICByZXR1cm4gaW5kZXggPj0gMCA/IHRlbXBsYXRlc1tpbmRleF0gOiB1bmRlZmluZWQ7XG4gIH1cblxuICAvKiogY29udGFpbmVyIMSRw6MgY2jhuqFtIHRy4bqnbiBs4buTbmcgdGjDrCBraMO0bmcgY2hvIHRow6ptIGto4buRaSB2w6BvIHRyb25nIG7hu69hICovXG4gIHByb3RlY3RlZCBjYW5BZGRJbnNpZGUoaXRlbTogVF9ncmlkTGF5b3V0X25vZGUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pc0NvbnRhaW5lcihpdGVtKSAmJiB0aGlzLmRlcHRoKCkgPCB0aGlzLm1heENvbnRhaW5lckRlcHRoKCk7XG4gIH1cblxuICAvKioga2jhu5FpIMSRxqFuIOG7nyBj4bqlcCBjaMawYSBjaOG6oW0gdHLhuqduIG3hu5tpIMSRxrDhu6NjIGNodXnhu4NuIHRow6BuaCBjb250YWluZXIgKi9cbiAgcHJvdGVjdGVkIGNhblRvZ2dsZVR5cGUoaXRlbTogVF9ncmlkTGF5b3V0X25vZGUpOiBib29sZWFuIHtcbiAgICBpZiAodGhpcy5pc0NvbnRhaW5lcihpdGVtKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuZGVwdGgoKSA8IHRoaXMubWF4Q29udGFpbmVyRGVwdGgoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyQmxvY2tQYXJlbnREcmFnKGV2ZW50OiBFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc05lc3RlZENhbnZhcygpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlck1vdXNlRG93blRyb25nTHVvaShldmVudDogRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5kZXB0aCgpID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8g8J+UtCBgZHJhZ0Zyb21Bbnl3aGVyZWAgYuG6rXQgdGjDrCBLSMOUTkcgY2jhurduIOKAlCBD4bqkTSBi4buPIG5ow6FuaCBuw6B5LlxuICAgIC8vIEto4buRaSBnaMOpcCBraMO0bmcgY8OybiBk4bqjaSBrw6lvIHJpw6puZywgbsOqbiBjaOG7lyBEVVkgTkjhuqRUIGPDsm4gbOG6oWkgxJHhu4MgY+G6p20gbsOzIGzDoCBO4buATiBUUuG7kE5HIGPhu6dhIG3hurd0XG4gICAgLy8gcGjhurNuZyBs4buTbmcgYsOqbiB0cm9uZy4gQ2jhurduIOG7nyDEkcOieSBsw6AgYuG7i3QgbuG7kXQgxJHGsOG7nW5nIMSRw7M6IG5nxrDhu51pIGTDuW5nIGLhuqVtIHbDoG8gxJHDonUgdHJvbmcga2jhu5FpIGdow6lwXG4gICAgLy8gY8Wpbmcga2jDtG5nIGvDqW8gbsOzIMSRaSDEkcaw4bujYyAobmfGsOG7nWkgZMO5bmcgY2jhu4kgcmEgMTUvMDkvMjAyNikuXG4gICAgLy8gS2jhu5FpIENPTiB24bqrbiBhbiB0b8OgbjogYGdyaWRzdGVyLWl0ZW1gIGPhu6dhIGNow7puZyB04buxIGNo4bq3biAoeGVtIGBoYW5kbGVyQmxvY2tQYXJlbnREcmFnYCksIG7Dqm5cbiAgICAvLyBjaOG7iSBz4buxIGtp4buHbiByxqFpIHbDoG8gbuG7gW4gdHLhu5FuZyBt4bubaSBu4buVaSBsw6puIHThu5tpIGto4buRaSBnaMOpcC5cbiAgICBpZiAodGhpcy5kcmFnRnJvbUFueXdoZXJlKCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBoYW5kbGVyU2VsZWN0Tm9kZShub2RlOiBUX2dyaWRMYXlvdXRfbm9kZSwgZXZlbnQ/OiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgaWYgKGV2ZW50KSB7XG4gICAgICAvLyDwn5S0IENI4buIIGLhu48gcXVhIGtoaSDEkcOtY2ggdGjhu7FjIHPhu7EgbMOgIG7DunQgYuG6pW0uIFRyxrDhu5tjIMSRw6J5IGLhu48gcXVhIGPhuqMgdsO5bmdcbiAgICAgIC8vIGAubGlicy11aS1ncmlkLWxheW91dC10b29sYmFyYDogdGhhbmggbsOgeSBwaOG7pyBn4bqnbiBrw61uIGto4buRaSBuaOG7jyBuw6puIGLhuqVtIHbDoG8ga2jhu5FpIGtow7RuZyBiYW8gZ2nhu51cbiAgICAgIC8vIGNo4buNbiDEkcaw4bujYywgbsahaSBkw7luZyBwaOG6o2kgdOG6r3QgYHBvaW50ZXItZXZlbnRzYCBj4bunYSBj4bqjIHRoYW5oIMSR4buDIGPhu6l1LlxuICAgICAgY29uc3QgdGFyZ2V0ID0gZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50O1xuICAgICAgaWYgKHRhcmdldC5jbG9zZXN0KCdidXR0b24nKSB8fCB0YXJnZXQuY2xvc2VzdCgnW2RhdGEtZ3JpZC1sYXlvdXQtYWN0aW9uXScpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5zZWxlY3RlZE5vZGVJZC5zZXQobm9kZS5pZCk7XG4gICAgdGhpcy5vdXRTZWxlY3ROb2RlLmVtaXQobm9kZSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckNvbmZpZyhldmVudDogRXZlbnQsIG5vZGU6IFRfZ3JpZExheW91dF9ub2RlKTogdm9pZCB7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgY29uc3QgYWN0aW9uRXZlbnQ6IFRfZ3JpZExheW91dF9hY3Rpb25FdmVudCA9IHtcbiAgICAgIG5vZGVJZDogbm9kZS5pZCxcbiAgICAgIG5vZGUsXG4gICAgICBjb25maXJtOiAoKSA9PiB0cnVlLFxuICAgIH07XG4gICAgdGhpcy5vdXRDb25maWdOb2RlLmVtaXQoYWN0aW9uRXZlbnQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGhhbmRsZXJEcmFnT3ZlcihldmVudDogRHJhZ0V2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmVuYWJsZUV4dGVybmFsRHJvcCgpIHx8IHRoaXMuZGVwdGgoKSA+IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICBpZiAoZXZlbnQuZGF0YVRyYW5zZmVyKSB7XG4gICAgICBldmVudC5kYXRhVHJhbnNmZXIuZHJvcEVmZmVjdCA9ICdjb3B5JztcbiAgICB9XG5cbiAgICBjb25zdCBjb250YWluZXIgPSB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudDtcbiAgICBjb25zdCByb290ID0gdGhpcy5zZXJ2aWNlLlJvb3QoKTtcbiAgICBjb25zdCBwYXlsb2FkID0gcGFyc2VEcm9wUGF5bG9hZDxUX2dyaWRMYXlvdXRfZXh0ZXJuYWxEcmFnUGF5bG9hZD4oXG4gICAgICB0aGlzLnNlcnZpY2UuZ2V0RHJhZ1BheWxvYWQoKSBhcyBUX2dyaWRMYXlvdXRfZXh0ZXJuYWxEcmFnUGF5bG9hZCB8IHVuZGVmaW5lZCxcbiAgICAgIGV2ZW50LmRhdGFUcmFuc2ZlcixcbiAgICApO1xuXG4gICAgaWYgKCFjb250YWluZXIgfHwgIXJvb3QgfHwgIXBheWxvYWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCByb290R3JpZHN0ZXIgPSBjb250YWluZXIucXVlcnlTZWxlY3RvcignZ3JpZHN0ZXInKSBhcyBIVE1MRWxlbWVudCB8IG51bGw7XG4gICAgaWYgKHJvb3RHcmlkc3Rlcikge1xuICAgICAgaWYgKCFyb290R3JpZHN0ZXIuY2xhc3NMaXN0LmNvbnRhaW5zKCdkaXNwbGF5LWdyaWQnKSkge1xuICAgICAgICByb290R3JpZHN0ZXIuY2xhc3NMaXN0LmFkZCgnZGlzcGxheS1ncmlkJyk7XG4gICAgICB9XG4gICAgICB0aGlzLnJvb3RHcmlkc3RlckVsID0gcm9vdEdyaWRzdGVyO1xuICAgIH1cblxuICAgIGNvbnN0IGdob3N0ID0gY2FsY3VsYXRlRHJvcEdob3N0KGV2ZW50LCBjb250YWluZXIsIHJvb3QsIHBheWxvYWQsIHRoaXMuc2VydmljZS50b3RhbENvbHVtbnMsIHRoaXMuc2VydmljZS5yb3dTdGVwUHgpO1xuXG4gICAgaWYgKGdob3N0Py5pc0luc2lkZUdyb3VwICYmIGdob3N0LmhvdmVyZWRHcmlkc3Rlcikge1xuICAgICAgaWYgKHRoaXMuYWN0aXZlSG92ZXJlZEVsICE9PSBnaG9zdC5ob3ZlcmVkR3JpZHN0ZXIpIHtcbiAgICAgICAgdGhpcy5jbGVhbnVwSG92ZXJlZEdyb3VwKCk7XG4gICAgICAgIGdob3N0LmhvdmVyZWRHcmlkc3Rlci5jbGFzc0xpc3QuYWRkKCdkaXNwbGF5LWdyaWQnKTtcbiAgICAgICAgdGhpcy5hY3RpdmVIb3ZlcmVkRWwgPSBnaG9zdC5ob3ZlcmVkR3JpZHN0ZXI7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY2xlYW51cEhvdmVyZWRHcm91cCgpO1xuICAgIH1cblxuICAgIHRoaXMuZHJvcEdob3N0LnNldChnaG9zdCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckRyYWdMZWF2ZShldmVudDogRHJhZ0V2ZW50KTogdm9pZCB7XG4gICAgaWYgKHRoaXMuZGVwdGgoKSA+IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGV2ZW50LnJlbGF0ZWRUYXJnZXQpIHtcbiAgICAgIGNvbnN0IGNvbnRhaW5lciA9IHRoaXMuc2Nyb2xsQXJlYVJlZigpPy5uYXRpdmVFbGVtZW50O1xuICAgICAgaWYgKGNvbnRhaW5lciAmJiBjb250YWluZXIuY29udGFpbnMoZXZlbnQucmVsYXRlZFRhcmdldCBhcyBOb2RlKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuZHJvcEdob3N0LnNldCh1bmRlZmluZWQpO1xuICAgIHRoaXMuY2xlYW51cERyYWdHcmlkKCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgaGFuZGxlckRyb3AoZXZlbnQ6IERyYWdFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5lbmFibGVFeHRlcm5hbERyb3AoKSB8fCB0aGlzLmRlcHRoKCkgPiAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgY29uc3QgZ2hvc3QgPSB0aGlzLmRyb3BHaG9zdCgpO1xuICAgIGNvbnN0IHBheWxvYWQgPSBwYXJzZURyb3BQYXlsb2FkPFRfZ3JpZExheW91dF9leHRlcm5hbERyYWdQYXlsb2FkPihcbiAgICAgIHRoaXMuc2VydmljZS5nZXREcmFnUGF5bG9hZCgpIGFzIFRfZ3JpZExheW91dF9leHRlcm5hbERyYWdQYXlsb2FkIHwgdW5kZWZpbmVkLFxuICAgICAgZXZlbnQuZGF0YVRyYW5zZmVyLFxuICAgICk7XG5cbiAgICB0aGlzLmRyb3BHaG9zdC5zZXQodW5kZWZpbmVkKTtcbiAgICB0aGlzLmNsZWFudXBEcmFnR3JpZCgpO1xuICAgIHRoaXMuc2VydmljZS5jbGVhckRyYWdQYXlsb2FkKCk7XG5cbiAgICBpZiAoIWdob3N0IHx8ICFwYXlsb2FkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgdGFyZ2V0UGFyZW50SWQgPSBnaG9zdC5pc0luc2lkZUdyb3VwID8gZ2hvc3QudGFyZ2V0UGFyZW50SWQgOiB1bmRlZmluZWQ7XG4gICAgY29uc3QgaXNJbnNpZGUgPSBCb29sZWFuKHRhcmdldFBhcmVudElkKTtcblxuICAgIGxldCBpdGVtVG9BZGQ6IFRfZ3JpZExheW91dF9uZXdOb2RlO1xuICAgIGNvbnN0IHBheWxvYWRBbnkgPSBwYXlsb2FkIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAgIGlmICh0eXBlb2YgcGF5bG9hZEFueVsncmVzb2x2ZU5vZGUnXSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgY29uc3QgcmVzb2x2ZWQgPSAocGF5bG9hZEFueVsncmVzb2x2ZU5vZGUnXSBhcyAoaW5zaWRlOiBib29sZWFuKSA9PiBUX2dyaWRMYXlvdXRfbmV3Tm9kZSB8IHVuZGVmaW5lZCkoaXNJbnNpZGUpO1xuICAgICAgaXRlbVRvQWRkID0gcmVzb2x2ZWQgPz8ge1xuICAgICAgICBjb2xzOiBnaG9zdC5jb2xzLFxuICAgICAgICByb3dzOiBnaG9zdC5yb3dzLFxuICAgICAgICBkYXRhOiBwYXlsb2FkLmRhdGEgPz8gKHBheWxvYWQudHlwZSA/IHsgdHlwZTogcGF5bG9hZC50eXBlLCB0aXRsZTogcGF5bG9hZC50aXRsZSB9IDogdW5kZWZpbmVkKSxcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IG5ld05vZGVEYXRhID0gcGF5bG9hZC5kYXRhID8/IChwYXlsb2FkLnR5cGUgPyB7IHR5cGU6IHBheWxvYWQudHlwZSwgdGl0bGU6IHBheWxvYWQudGl0bGUgfSA6IHVuZGVmaW5lZCk7XG4gICAgICBpdGVtVG9BZGQgPSB7XG4gICAgICAgIGNvbHM6IGdob3N0LmNvbHMsXG4gICAgICAgIHJvd3M6IGdob3N0LnJvd3MsXG4gICAgICAgIGRhdGE6IG5ld05vZGVEYXRhLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICBjb25zdCBhZGRlZE5vZGUgPSB0aGlzLnNlcnZpY2UuaW5zZXJ0QmxvY2tXaXRoQ29sbGlzaW9uKFxuICAgICAgaXRlbVRvQWRkLFxuICAgICAgZ2hvc3QuY29sLFxuICAgICAgZ2hvc3Qucm93LFxuICAgICAgdGFyZ2V0UGFyZW50SWQsXG4gICAgKTtcblxuICAgIGlmIChhZGRlZE5vZGUpIHtcbiAgICAgIHRoaXMuc2VsZWN0ZWROb2RlSWQuc2V0KGFkZGVkTm9kZS5pZCk7XG4gICAgICB0aGlzLm91dERyb3BOb2RlLmVtaXQoe1xuICAgICAgICBub2RlOiBhZGRlZE5vZGUsXG4gICAgICAgIHBhcmVudElkOiB0YXJnZXRQYXJlbnRJZCxcbiAgICAgICAgY29sOiBnaG9zdC5jb2wsXG4gICAgICAgIHJvdzogZ2hvc3Qucm93LFxuICAgICAgICBjb2xzOiBnaG9zdC5jb2xzLFxuICAgICAgICByb3dzOiBnaG9zdC5yb3dzLFxuICAgICAgICByYXdFdmVudDogZXZlbnQsXG4gICAgICAgIHBheWxvYWQsXG4gICAgICB9KTtcbiAgICAgIGNvbnN0IHJvb3QgPSB0aGlzLnNlcnZpY2UuUm9vdCgpO1xuICAgICAgaWYgKHJvb3QpIHtcbiAgICAgICAgdGhpcy5vdXRDaGFuZ2UuZW1pdCh7XG4gICAgICAgICAgcm9vdCxcbiAgICAgICAgICBub2RlSWQ6IGFkZGVkTm9kZS5pZCxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjbGVhbnVwSG92ZXJlZEdyb3VwKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmFjdGl2ZUhvdmVyZWRFbCkge1xuICAgICAgdGhpcy5hY3RpdmVIb3ZlcmVkRWwuY2xhc3NMaXN0LnJlbW92ZSgnZGlzcGxheS1ncmlkJyk7XG4gICAgICB0aGlzLmFjdGl2ZUhvdmVyZWRFbCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGNsZWFudXBEcmFnR3JpZCgpOiB2b2lkIHtcbiAgICB0aGlzLmNsZWFudXBIb3ZlcmVkR3JvdXAoKTtcbiAgICBpZiAodGhpcy5yb290R3JpZHN0ZXJFbCAmJiAhdGhpcy5zaG93R3JpZExpbmVzKCkpIHtcbiAgICAgIHRoaXMucm9vdEdyaWRzdGVyRWwuY2xhc3NMaXN0LnJlbW92ZSgnZGlzcGxheS1ncmlkJyk7XG4gICAgICB0aGlzLnJvb3RHcmlkc3RlckVsID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBjb25zdCBjb250YWluZXIgPSB0aGlzLnNjcm9sbEFyZWFSZWYoKT8ubmF0aXZlRWxlbWVudDtcbiAgICBpZiAoY29udGFpbmVyICYmICF0aGlzLnNob3dHcmlkTGluZXMoKSkge1xuICAgICAgY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoJ2dyaWRzdGVyLmRpc3BsYXktZ3JpZCcpLmZvckVhY2goKGVsKSA9PiB7XG4gICAgICAgIGVsLmNsYXNzTGlzdC5yZW1vdmUoJ2Rpc3BsYXktZ3JpZCcpO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG59XG4iLCJAaWYgKGlzWm9vbUFjdGl2ZSgpKSB7XG4gIDwhLS0gS2h1IHbhu7FjIGN14buZbiBjaOG7qWEgY2FudmFzIHN0YWdlIHRodSBwaMOzbmcgLS0+XG4gIDxkaXZcbiAgICAjc2Nyb2xsQXJlYVxuICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1zY3JvbGwtYXJlYSByZWxhdGl2ZSBoLWZ1bGwgbWluLWgtMCB3LWZ1bGwgbWluLXctMCBvdmVyZmxvdy1hdXRvIHAtNiBmbGV4IGp1c3RpZnktc3RhcnQgaXRlbXMtc3RhcnRcIlxuICAgIChtb3VzZWRvd24pPVwiaGFuZGxlck1vdXNlRG93blRyb25nTHVvaSgkZXZlbnQpXCJcbiAgICAocG9pbnRlcmRvd24pPVwiaGFuZGxlclNjcm9sbEFyZWFQb2ludGVyRG93bigpXCJcbiAgICAod2hlZWwpPVwiaGFuZGxlcldoZWVsKCRldmVudClcIlxuICAgIChkcmFnb3Zlcik9XCJoYW5kbGVyRHJhZ092ZXIoJGV2ZW50KVwiXG4gICAgKGRyYWdsZWF2ZSk9XCJoYW5kbGVyRHJhZ0xlYXZlKCRldmVudClcIlxuICAgIChkcm9wKT1cImhhbmRsZXJEcm9wKCRldmVudClcIj5cbiAgICA8ZGl2XG4gICAgICAjY2FudmFzU3RhZ2VcbiAgICAgIGlkPVwiYmktY2FudmFzLXN0YWdlXCJcbiAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1zdGFnZSByZWxhdGl2ZSBmbGV4IGZsZXgtY29sIHRyYW5zaXRpb24tW3pvb21dIGR1cmF0aW9uLTE1MCBvcmlnaW4tdG9wIHNocmluay0wIG14LWF1dG8gYmctd2hpdGUvODAgYm9yZGVyIGJvcmRlci1zbGF0ZS0yMDAvODAgcm91bmRlZC0yeGwgc2hhZG93LXNtIHAtNFwiXG4gICAgICBbc3R5bGUuem9vbV09XCJjdXJyZW50Wm9vbSgpXCJcbiAgICAgIFtzdHlsZS53aWR0aC5weF09XCJzdGFnZVdpZHRoKClcIlxuICAgICAgW3N0eWxlLm1pbi13aWR0aC5weF09XCJzdGFnZVdpZHRoKClcIlxuICAgICAgW3N0eWxlLm1pbi1oZWlnaHQucHhdPVwic3RhZ2VNaW5IZWlnaHQoKVwiXG4gICAgICBbc3R5bGUuLS1saWJzLXVpLWdyaWQtbGF5b3V0LXZpc3VhbC1nYXAucHhdPVwidmlzdWFsR2FwKClcIj5cbiAgICAgIDwhLS0gU25hcHBlZCBEcm9wIEdob3N0IFByZXZpZXcgLS0+XG4gICAgICBAaWYgKGRyb3BHaG9zdCgpOyBhcyBnaG9zdCkge1xuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJwb2ludGVyLWV2ZW50cy1ub25lIGFic29sdXRlIHotMzAgZmxleCBmbGV4LWNvbCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgb3ZlcmZsb3ctaGlkZGVuIHJvdW5kZWQtbGcgYm9yZGVyLTIgYm9yZGVyLWRhc2hlZCBib3JkZXItYmx1ZS01MDAgYmctYmx1ZS01MDAvMTUgcC0xIHNoYWRvdy1zbSB0cmFuc2l0aW9uLWFsbCBkdXJhdGlvbi03NSBlYXNlLW91dCBiYWNrZHJvcC1ibHVyLVswLjVweF0gc2VsZWN0LW5vbmVcIlxuICAgICAgICAgIFtzdHlsZS5sZWZ0LnB4XT1cImdob3N0LnBpeGVsTGVmdFwiXG4gICAgICAgICAgW3N0eWxlLnRvcC5weF09XCJnaG9zdC5waXhlbFRvcFwiXG4gICAgICAgICAgW3N0eWxlLndpZHRoLnB4XT1cImdob3N0LnBpeGVsV2lkdGhcIlxuICAgICAgICAgIFtzdHlsZS5oZWlnaHQucHhdPVwiZ2hvc3QucGl4ZWxIZWlnaHRcIj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiaW5saW5lLWZsZXggbWF4LXctWzk1JV0gaXRlbXMtY2VudGVyIGdhcC0xIHRydW5jYXRlIHJvdW5kZWQtbWQgYmctYmx1ZS02MDAgcHgtMiBweS0wLjUgdGV4dC1bMTFweF0gZm9udC1zZW1pYm9sZCB0ZXh0LXdoaXRlIHNoYWRvdy14c1wiPlxuICAgICAgICAgICAgQGlmIChnaG9zdC5jYXJkSWNvbkNsYXNzKSB7XG4gICAgICAgICAgICAgIDxpIFtjbGFzc109XCJnaG9zdC5jYXJkSWNvbkNsYXNzICsgJyB0ZXh0LXhzJ1wiPjwvaT5cbiAgICAgICAgICAgIH0gQGVsc2UgaWYgKGdob3N0LmNhcmRJY29uVGV4dCkge1xuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtWzEwcHhdIGZvbnQtYm9sZFwiPnt7IGdob3N0LmNhcmRJY29uVGV4dCB9fTwvc3Bhbj5cbiAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQteHMgZm9udC1ib2xkXCI+e3sgZ2hvc3QuaXNJbnNpZGVHcm91cCA/ICfihrMnIDogJysnIH19PC9zcGFuPlxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0cnVuY2F0ZVwiPnt7IGdob3N0LmNhcmRUaXRsZSB9fTwvc3Bhbj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1bOXB4XSBmb250LW1vbm8gb3BhY2l0eS03NVwiPih7eyBnaG9zdC5jb2xzIH19Yyk8L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICBAaWYgKGdob3N0LnBpeGVsSGVpZ2h0ID49IDgwKSB7XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibXQtMSBmbGV4IG1heC13LVs5NSVdIGl0ZW1zLWNlbnRlciBnYXAtMS41IHRydW5jYXRlIHJvdW5kZWQgYm9yZGVyIGJvcmRlci1ibHVlLTEwMCBiZy13aGl0ZS85NSBweC0yIHB5LTAuNSB0ZXh0LVsxMHB4XSBmb250LW1lZGl1bSB0ZXh0LWJsdWUtNzAwIHNoYWRvdy14c1wiPlxuICAgICAgICAgICAgICBAaWYgKGdob3N0LmlzSW5zaWRlR3JvdXApIHtcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRydW5jYXRlIGZvbnQtYm9sZCB0ZXh0LWJsdWUtOTAwXCI+4oazIHt7IGdob3N0Lmdyb3VwVGl0bGUgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LWJsdWUtMzAwXCI+4oCiPC9zcGFuPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDxzcGFuPkPhu5l0IHt7IGdob3N0LmNvbCArIDEgfX0te3sgZ2hvc3QuY29sICsgZ2hvc3QuY29scyB9fTwvc3Bhbj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgICA8Z3JpZHN0ZXJcbiAgICAgICAgY2xhc3M9XCJoLWZ1bGwgdy1mdWxsXCJcbiAgICAgICAgW2NsYXNzLmRpc3BsYXktZ3JpZF09XCJzaG93R3JpZExpbmVzKClcIlxuICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1kcmFnLWFueXdoZXJlXT1cImRyYWdGcm9tQW55d2hlcmUoKVwiXG4gICAgICAgIFtvcHRpb25zXT1cImdyaWRPcHRpb25zKClcIj5cbiAgICAgICAgICBAZm9yIChpdGVtIG9mIGNoaWxkcmVuKCk7IHRyYWNrIGl0ZW0uaWQ7IGxldCBpdGVtSW5kZXggPSAkaW5kZXgpIHtcbiAgICA8IS0tIPCflLQgQ2jhurduIGBtb3VzZWRvd25gIHThuqFpIMSQw4JZIGNobyBsxrDhu5tpIGzhu5NuZyDigJQgQ+G6pE0gYuG7jyB2w6AgQ+G6pE0gY2h1eeG7g24gc2FuZyBjaOG7lyBraMOhYy5cbiAgICAgICAgIGBpZ25vcmVDb250ZW50Q2xhc3NgIG3hu5l0IG3DrG5oIEtIw5RORyDEkeG7pzogYGRlbGF5U3RhcnQ6IDE2MGAgbMOgbSBN4buWSSBsxrDhu5tpIGzDqm4gbOG7i2NoXG4gICAgICAgICBgZHJhZ1N0YXJ0YCBi4bqxbmcgYHNldFRpbWVvdXRgIG5nYXkgdOG6oWkgYG1vdXNlZG93bmAsIGPDsm4gYHN0b3BQcm9wYWdhdGlvbmAgbcOgIGxpYiBn4buNaSBiw6puXG4gICAgICAgICB0cm9uZyBgZHJhZ1N0YXJ0YCBjaOG6oXkgc2F1IDE2MG1zIG7Dqm4gcXXDoSBtdeG7mW4g4oCUIGzGsOG7m2kgQ0hBIMSRw6Mga+G7i3Agbmjhuq1uICjEkW8gMjgvMDgvMjAyNjoga8Opb1xuICAgICAgICAgZOG6o2kga2jhu5FpIGNvbiwgY2h14buXaSBjbGFzcyBkdXnhu4d0IGzDqm4gxJDDmk5HIG3DoCBjb250YWluZXIgduG6q24gbmjhuqN5IDAsMCDihpIgMCwxKS5cbiAgICAgICAgIEfhuq9uIOG7nyBgZ3JpZHN0ZXItaXRlbWAgbMOgIMSRw7puZyBiacOqbjogbMaw4bubaSBjb24gbmdoZSB0csOqbiBjaMOtbmggcGjhuqduIHThu60gbsOgeSBuw6puIMSRw6Mgbmjhuq1uIHhvbmcsXG4gICAgICAgICBsxrDhu5tpIGNoYSDhu58gbmdvw6BpIGLhu4sgY2jhurduLiAtLT5cbiAgICA8Z3JpZHN0ZXItaXRlbVxuICAgICAgW2l0ZW1dPVwiaXRlbVwiXG4gICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1zZWxlY3RlZF09XCJzZWxlY3RlZE5vZGVJZCgpID09PSBpdGVtLmlkXCJcbiAgICAgIChjbGljayk9XCJoYW5kbGVyU2VsZWN0Tm9kZShpdGVtLCAkZXZlbnQpXCJcbiAgICAgIChtb3VzZWRvd24pPVwiaGFuZGxlckJsb2NrUGFyZW50RHJhZygkZXZlbnQpXCI+XG4gICAgICA8IS0tIPCflLQgVuG7jyBuw6B5IG1hbmcgYGRyYWdIYW5kbGVDbGFzc2Ag4oCUIEPhuqRNIGLhu48uXG4gICAgICAgICAgIGBpZ25vcmVDb250ZW50OiB0cnVlYCBi4bqvdCBncmlkc3RlciBDSOG7iCBjaG8ga8OpbyBraGkgY2jhu5cgYuG6pW0gbuG6sW0gZMaw4bubaSBt4buZdCBwaOG6p24gdOG7rSBjw7NcbiAgICAgICAgICAgYGRyYWdIYW5kbGVDbGFzc2AuIEtow7RuZyBjw7MgduG7jyBuw6B5IHRow6wgY29tcG9uZW50IGPhu6dhIG7GoWkgZMO5bmcgKHRoxrAgdmnhu4duIGtow7RuZyBraeG7g20gc2/DoXRcbiAgICAgICAgICAgxJHGsOG7o2MgY2xhc3MgY+G7p2EgbsOzKSBz4bq9IGtow7RuZyBjw7MgdGF5IGPhuqdtIG7DoG8sIHbDoCBLSMOUTkcga2jhu5FpIG7DoG8ga8OpbyDEkcaw4bujYyDigJQgxJFvIDI4LzA4LzIwMjY6XG4gICAgICAgICAgIGvDqW8gMzIwcHgsIHRv4bqhIMSR4buZIGto4buRaSBnaeG7ryBuZ3V5w6puIHg9Myx5PTAuXG4gICAgICAgICAgIFTDqm4gY2xhc3MgdGhlbyBj4bqlcDogbMaw4bubaSB0cmFuZyB2w6AgbMaw4bubaSBs4buTbmcgZMO5bmcgaGFpIHTDqm4gS0jDgUMgbmhhdSwgbuG6v3Uga2jDtG5nIGzGsOG7m2kgY2hhXG4gICAgICAgICAgIGPFqW5nIG5o4bqtbiByYSB0YXkgY+G6p20gY+G7p2Ega2jhu5FpIGNvbiB2w6AgbmjhuqVjIGx1w7RuIGNvbnRhaW5lci4gLS0+XG4gICAgICA8IS0tIPCflLQgVuG7jyBLSMOUTkcgbWFuZyBgZHJhZ0hhbmRsZUNsYXNzYCDigJQgQ+G6pE0gdGjDqm0gbOG6oWkuXG4gICAgICAgICAgIEvDqW8gY2jhu4kgxJHGsOG7o2MgcGjDqXAgYuG6r3QgxJHhuqd1IHThu6sgROG6okkgWEFOSCAobmfGsOG7nWkgZMO5bmcgY2jhu5F0IDI4LzA4LzIwMjY6IFwicGjhuqNpIGhvdmVyIHbDoG8gdsO5bmdcbiAgICAgICAgICAgbcOgdSB4YW5oIHRow6wgbeG7m2kga8OpbyB0aOG6oyDEkcaw4bujYyBjaG8gxJHhu5NuZyBuaOG6pXRcIikuIENobyBj4bqjIHbhu48gbMOgbSB0YXkgY+G6p20gdGjDrCBi4bqlbSDEkcOidSBjxaluZ1xuICAgICAgICAgICBrw6lvLCB2w6Aga2jhu5FpIGNvbiBu4bqxbSB0cm9uZyBjb250YWluZXIgc+G6vSBuaOG6pWMgbHXDtG4gY29udGFpbmVyIHbDrCBsxrDhu5tpIGNoYSBkdXnhu4d0IGzDqm4gZ+G6t3BcbiAgICAgICAgICAgdGF5IGPhuqdtIGPhu6dhIHbhu48gY29udGFpbmVyIHRyxrDhu5tjLlxuICAgICAgICAgICBW4buPIGto4buRaSBjb24gduG6q24gZ2nhu68gY2xhc3MgQ0jhurZOIMSR4buDIGzGsOG7m2kgY2hhIGThu6tuZyDEkcO6bmcg4bufIGJpw6puLiAtLT5cbiAgICAgIDwhLS0g8J+UtCBgZHJhZ0Zyb21Bbnl3aGVyZWAgYuG6rXQgdGjDrCBC4buOIGNsYXNzIGNo4bq3biDigJQgQ+G6pE0gZ+G6r24gbOG6oWkgdsO0IMSRaeG7gXUga2nhu4duLlxuICAgICAgICAgICBgbGlicy11aS1ncmlkLWxheW91dC1ibG9jay1wYXJlbnQtZHJhZ2AgQ0jDjU5IIEzDgCBgaWdub3JlQ29udGVudENsYXNzYCBj4bunYSBncmlkc3RlciAoeGVtXG4gICAgICAgICAgIGBncmlkc3Rlci1jb25maWcuZGVmaW5lYCkuIFbhu5tpIGBpZ25vcmVDb250ZW50OiB0cnVlYCBuw7MgY2jhu4kgbWFuZyBuZ2jEqWEgXCJraMO0bmcgcGjhuqNpIHRheVxuICAgICAgICAgICBj4bqnbVwiOyBuaMawbmcgduG7m2kgYGlnbm9yZUNvbnRlbnQ6IGZhbHNlYCAoY2jhur8gxJHhu5kgY+G6p20tY+G6oy10aOG6uykgZ3JpZHN0ZXIgaGnhu4N1IGzDoCBcInbDuW5nIEPhuqRNXG4gICAgICAgICAgIGvDqW9cIjogZHV54buHdCB04burIGNo4buXIGLhuqVtIGzDqm4gZ+G6t3AgY2xhc3MgbsOgeSBsw6AgZOG7q25nIGjhurNuLCBuw6puIEtI4buQSSBDT04gdHJvbmcga2jhu5FpIGdow6lwIGLhuqVtXG4gICAgICAgICAgIHbDoG8gxJHDonUgY8Wpbmcga2jDtG5nIG5ow7pjIG5ow61jaCAobmfGsOG7nWkgZMO5bmcgY2jhu4kgcmEgMTUvMDkvMjAyNjsgxJFvIMSRxrDhu6NjIGvDqW8gMTUwcHggbcOgIGto4buRaVxuICAgICAgICAgICBs4buHY2ggMHB4KS4gQ2jhurduIGzGsOG7m2kgY2hhIOG7nyBjaOG6vyDEkeG7mSBuw6B5IGRvIGBoYW5kbGVyQmxvY2tQYXJlbnREcmFnYCB0csOqbiBjaMOtbmhcbiAgICAgICAgICAgYGdyaWRzdGVyLWl0ZW1gIGxvLCBraMO0bmcgY+G6p24gY2xhc3MgbsOgeS4gLS0+XG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1ibG9jay1zaGVsbCBncm91cCByZWxhdGl2ZSBoLWZ1bGwgdy1mdWxsIG1pbi13LTBcIlxuICAgICAgICBbYXR0ci5kYXRhLW5vZGUtaWRdPVwiaXRlbS5pZFwiXG4gICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWJsb2NrLXBhcmVudC1kcmFnXT1cImlzTmVzdGVkQ2FudmFzKCkgJiYgIWRyYWdGcm9tQW55d2hlcmUoKVwiPlxuICAgICAgICA8IS0tIEThuqNpIGvDqW86IGhp4buHbiBraGkgcsOqIGNodeG7mXQgdsOgbyBraOG7kWksIGJhIGNo4bqlbSBsw6AgZOG6pXUgaGnhu4d1IGvDqW8gcXVlbiB0aHXhu5ljLlxuICAgICAgICAgICAgIE5nxrDhu51pIGTDuW5nIGPhuqduIG3hu5l0IGNo4buXIGLDoW0gUsOVIFLDgE5HIHRoYXkgdsOsIMSRb8OhbiB4ZW0gYuG6pW0gxJHDonUgdGjDrCBrw6lvIMSRxrDhu6NjXG4gICAgICAgICAgICAgKGNo4buRdCAyOC8wOC8yMDI2LCB0aGVvIMSRw7puZyBk4bqjaSDEkWFuZyBkw7luZyDhu58gbcOgbiBDdXN0b21lciAzNjAgY+G7p2EgbW9iaW8td2ViKS4gLS0+XG4gICAgICAgIDwhLS0g8J+UtCBgZHJhZ0Zyb21Bbnl3aGVyZWAgYuG6rXQgdGjDrCBLSMOUTkcgduG6vSBk4bqjaTogY+G6oyB0aOG6uyDEkcOjIGzDoCB0YXkgY+G6p20sIMSR4buDIGzhuqFpIGThuqNpIGNo4buJIHRo4burYS4gLS0+XG4gICAgICAgIEBpZiAoaXNFZGl0TW9kZSgpICYmICFkcmFnRnJvbUFueXdoZXJlKCkpIHtcbiAgICAgICAgICA8IS0tIPCflLQgROG6o2kgUEjhuqJJIG1hbmcgdGF5IGPhuqdtIMSQw5pORyBD4bqkUCBj4bunYSBuw7MuIEThuqNpIG5o4bqtbiBjaHXhu5l0IChgcG9pbnRlci1ldmVudHM6IGF1dG9gKSxcbiAgICAgICAgICAgICAgIG7Dqm4gbuG6v3Uga2jDtG5nIG1hbmcgdGF5IGPhuqdtIHRow6wgYGNoZWNrRHJhZ0hhbmRsZUNsYXNzYCBkdXnhu4d0IGzDqm4gZ+G6t3AgdGF5IGPhuqdtIGPhu6dhXG4gICAgICAgICAgICAgICBjb250YWluZXIgdsOgIG5o4bqlYyBjb250YWluZXIgdGhheSB2w6wga2jhu5FpIGNvbiAoxJFvIDI4LzA4LzIwMjY6IGvDqW8gZOG6o2kga2jhu5FpIGNvbixcbiAgICAgICAgICAgICAgIGNvbnRhaW5lciBuaOG6o3kgMCwwIOKGkiAwLDEgY8OybiBraOG7kWkgY29uIMSR4bupbmcgaW0pLlxuICAgICAgICAgICAgICAg8J+UtCBE4bqjaSBDSOG7iCBtYW5nIHRheSBj4bqnbSwgQ+G6pE0gbWFuZyB0aMOqbSBjbGFzcyBjaOG6t246IGBjaGVja0RyYWdIYW5kbGVDbGFzc2AgeMOpdCBoYWlcbiAgICAgICAgICAgICAgIGNsYXNzIMSRw7MgdHLDqm4gQ8OZTkcgbeG7mXQgbm9kZSBuw6puIMSR4buDIGNodW5nIGzDoCBrw6lvIGjhu49uZyAoxJFvIDI4LzA4LzIwMjY6IGLhuqVtIGThuqNpIGto4buRaVxuICAgICAgICAgICAgICAgY29uIGvDqW8gMTQwcHgsIGto4buRaSDEkeG7qW5nIGltKS4gVmnhu4djIGNo4bq3biBsxrDhu5tpIGNoYSBkbyBW4buOIEtI4buQSSBsby4gLS0+XG4gICAgICAgICAgPGRpdlxuICAgICAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyXCJcbiAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyLWNoaWxkXT1cImlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1oYW5kbGVdPVwiIWlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1oYW5kbGUtY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWRyYWctYmFyLWRvdHNcIj5cbiAgICAgICAgICAgICAgQGZvciAoZG90IG9mIGRyYWdCYXJEb3RzOyB0cmFjayBkb3QpIHtcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXItZG90XCI+PC9zcGFuPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cblxuICAgICAgICA8IS0tIEtodW5nIGNo4bupYSBjb21wb25lbnQgY+G7p2EgbsahaSBkw7luZy5cbiAgICAgICAgICAgICDwn5S0IFBI4bqiSSBjw7Mga2h1bmcgdGjhuq10IChraMO0bmcgcGjhuqNpIGBuZy1jb250YWluZXJgIHRy4buRbmcpOiBuw7MgbG8gaOG7mSBuxqFpIGTDuW5nIHBo4bqnbiBs4bqlcCDEkeG6p3lcbiAgICAgICAgICAgICDDtCB2w6Agdmnhu4FuIGLDoW8gY2jhur8gxJHhu5kgc+G7rWEuIEtow7RuZyBjw7Mga2h1bmcgdGjDrCBt4buXaSBjb21wb25lbnQgbuG7mWkgZHVuZyBs4bqhaSBwaOG6o2kgdOG7sSB2aeG6v3RcbiAgICAgICAgICAgICBgOmhvc3QgeyB3aWR0aC9oZWlnaHQ6IDEwMCUgfWAgKyB2aeG7gW4gxJHhu6l0IOKAlCDEkcOzIGzDoCB2aeG7h2MgY+G7p2EgdGjGsCB2aeG7h24sIGtow7RuZyBwaOG6o2kgY+G7p2FcbiAgICAgICAgICAgICBuZ8aw4budaSBkw7luZyAobmfGsOG7nWkgZMO5bmcgY2jhu5F0IDI4LzA4LzIwMjYpLlxuICAgICAgICAgICAgIE3hu5dpIGto4buRaSBt4buZdCBWaWV3Q29udGFpbmVyUmVmIHJpw6puZyBuw6puIGNvbXBvbmVudCBu4bqxbSDEkcO6bmcgw7QgY+G7p2EgbsOzLiAtLT5cbiAgICAgICAgPCEtLSDwn5S0IENvbnRhaW5lciBDw5MgYGdldENvbXBvbmVudE91dGxldGA6IGtodW5nIG7DoHkgbMOgIFbhu44sIHbDoCBjYW52YXMgY29uIMSRxrDhu6NjIMSRxrBhIFbDgE8gVFJPTkdcbiAgICAgICAgICAgICBjb21wb25lbnQgduG7jyBxdWEgaW5wdXQgYGNoaWxkQ2FudmFzYCAobeG7mXQgYFRlbXBsYXRlUmVmYCkg4oCUIHhlbSBgbmctdGVtcGxhdGUgI2NoaWxkQ2FudmFzVHBsYFxuICAgICAgICAgICAgIGLDqm4gZMaw4bubaS4gVuG7jyB04buxIHF1eeG6v3QgxJHhurd0IGNhbnZhcyDhu58gxJHDonUgZ2nhu69hIHRpw6p1IMSR4buBIHbDoCBwaOG6p24gdGjDom4gY+G7p2EgbsOzLlxuICAgICAgICAgICAgIFRyxrDhu5tjIMSRw6J5IGNhbnZhcyBjb24gbMOgIEFOSCBFTSBj4bunYSBraHVuZyBuw6B5LCBuw6puIHbhu48gY2hp4bq/bSB0cuG7jW4gY2hp4buBdSBjYW8gcuG7k2kgxJHhuql5IHRvw6BuIGLhu5lcbiAgICAgICAgICAgICB0aOG6uyBjb24gcmEgbmdvw6BpIHbDuW5nIG5ow6xuIHRo4bqleSAoxJFvIDE0LzA5LzIwMjY6IG3hu41pIGto4buRaSBnaMOpcCBjaOG7iSBjw7JuIG5ow6NuIHbDoCBydeG7mXQgdHLhuq9uZykuIC0tPlxuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRlbnQtZnJhbWVcIlxuICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRlbnQtZnJhbWUtZWRpdF09XCJpc0VkaXRNb2RlKCkgJiYgIWlzQ29udGFpbmVyKGl0ZW0pXCJcbiAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1jb250ZW50LWZyYW1lLWVtcHR5XT1cImlzQ29udGFpbmVyKGl0ZW0pICYmICFpdGVtLmdldENvbXBvbmVudE91dGxldFwiPlxuICAgICAgICAgIDxuZy1jb250YWluZXIgI2l0ZW1Ib3N0IC8+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDwhLS0gROG6pXUgaGnhu4d1IENPTlRBSU5FUjogbmjDo24gZ8OzYyB0csOqbi1waOG6o2kgKyB2aeG7gW4gxJHhu6l0IGJhbyBydeG7mXQuIEtow7RuZyBjw7MgZOG6pXUgaGnhu4d1IHRow6xcbiAgICAgICAgICAgICBuZ8aw4budaSBkw7luZyBraMO0bmcgYmnhur90IGto4buRaSBuw6BvIGNo4bupYSDEkcaw4bujYyBraOG7kWkga2jDoWMgKGNo4buRdCAyOC8wOC8yMDI2LCB0aGVvIMSRw7puZyBjw6FjaFxuICAgICAgICAgICAgIG3DoG4gQ3VzdG9tZXIgMzYwIGPhu6dhIG1vYmlvLXdlYiDEkWFuZyBsw6BtKS4gLS0+XG4gICAgICAgIEBpZiAoaXNFZGl0TW9kZSgpICYmIGlzQ29udGFpbmVyKGl0ZW0pKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGFpbmVyLWxhYmVsXCI+e3sgJ2kxOG5fY29udGFpbmVyX2Jsb2NrJyB8IHRyYW5zbGF0ZSB9fTwvZGl2PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRhaW5lci1ib3JkZXJcIj48L2Rpdj5cbiAgICAgICAgfVxuXG4gICAgICAgIDwhLS0gVGhhbmggY8O0bmcgY+G7pTogaGnhu4duIGtoaSByw6ogY2h14buZdCB2w6BvIGto4buRaS5cbiAgICAgICAgICAgICDwn5S0IETDuW5nIGBsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25gIOKAlCBD4bqkTSB04buxIGThu7FuZyB0aOG6uyBgPGJ1dHRvbj5gLlxuICAgICAgICAgICAgIENvbXBvbmVudCBuw6B5IMSRw6MgY8OzIHPhurVuIGtp4buDdSBuw7p0LCBj4buhIG7DunQgdsOgIGJvbmcgYsOzbmcgY2jDuiB0aMOtY2ggdGhlbyDEkcO6bmcgYuG7mSBnaWFvIGRp4buHblxuICAgICAgICAgICAgIGPhu6dhIHPhuqNuIHBo4bqpbTsgdOG7sSBk4buxbmcgbMOgIG3hu5dpIG7GoWkgbeG7mXQga2nhu4N1IHbDoCBt4bqldCBsdcO0biB0b29sdGlwLiAtLT5cbiAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkgJiYgIWhpZGRlblRvb2xiYXIoKSkge1xuICAgICAgICAgIEBpZiAodGVtcGxhdGVUb29sYmFyKCkpIHtcbiAgICAgICAgICAgIDwhLS0gTsahaSBkw7luZyB04buxIHbhur0gdGhhbmggY8O0bmcgY+G7pTogbmjhuq1uIGto4buRaSArIGPDoWMgaMOgbSBwaMOhdCBldmVudCBxdWEgY29udGV4dC5cblxuICAgICAgICAgICAgICAgICDwn5S0IEto4buRaSBi4buNYyB04buxIGNo4bq3biBgbW91c2Vkb3duYC9gY2xpY2tgIG7hu5VpIGzDqm4gZ3JpZHN0ZXIuIFRoYW5oIGPDtG5nIGPhu6UgbuG6sW0gdHJvbmdcbiAgICAgICAgICAgICAgICAgdsO5bmcgbWFuZyBgZHJhZ0hhbmRsZUNsYXNzYDsga2jDtG5nIGNo4bq3biB0aMOsIGPDuiBi4bqlbSBi4buLIGhp4buDdSBsw6AgYuG6r3QgxJHhuqd1IGvDqW8gdsOgXG4gICAgICAgICAgICAgICAgIGBkZWxheVN0YXJ0OiAxNjBgIG514buRdCBsdcO0biBgY2xpY2tgIOKAlCBuw7p0IHRyb25nIHRlbXBsYXRlIG5nb8OgaSBi4bqlbSBraMO0bmcgxINuXG4gICAgICAgICAgICAgICAgICjEkW8gMDkvMDkvMjAyNjogYuG6pW0gYuG6sW5nIGAuY2xpY2soKWAgY+G7p2EgRE9NIHRow6wgbeG7nywgYuG6pW0gY2h14buZdCB0aOG6rXQgdGjDrCBraMO0bmcpLlxuICAgICAgICAgICAgICAgICBDaOG6t24g4bufIMSQw4JZIMSR4buDIG7GoWkgZMO5bmcga2jDtG5nIHBo4bqjaSBiaeG6v3QgYuG6q3kgbsOgeS4gLS0+XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC10b29sYmFyXCJcbiAgICAgICAgICAgICAgcm9sZT1cInRvb2xiYXJcIlxuICAgICAgICAgICAgICB0YWJpbmRleD1cIjBcIlxuICAgICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC10b29sYmFyLWNoaWxkXT1cImlzTmVzdGVkQ2FudmFzKClcIlxuICAgICAgICAgICAgICAobW91c2Vkb3duKT1cImhhbmRsZXJTdG9wRXZlbnQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChrZXlkb3duKT1cImhhbmRsZXJTdG9wRXZlbnQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJoYW5kbGVyU3RvcEV2ZW50KCRldmVudClcIj5cbiAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgICAgICpuZ1RlbXBsYXRlT3V0bGV0PVwidGVtcGxhdGVUb29sYmFyKCkhOyBjb250ZXh0OiB0b29sYmFyQ29udGV4dHMoKVtpdGVtSW5kZXhdXCIgLz5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtdG9vbGJhclwiXG4gICAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZGltLWJhZGdlXCI+XG4gICAgICAgICAgICAgICAge3sgaXRlbS5yb3dzIH19IGjDoG5nIMOXIHt7IGl0ZW0uY29scyB9fSBj4buZdFxuICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgIEBpZiAoY2FuQWRkSW5zaWRlKGl0ZW0pKSB7XG4gICAgICAgICAgICAgICAgPGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvblxuICAgICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZCdcIlxuICAgICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIFtzaXplQnV0dG9uXT1cIidzbWFsbGVyJ1wiXG4gICAgICAgICAgICAgICAgICBbY2xhc3NJY29uTGVmdF09XCInbGlicy11aS1pY29uLWFkZCdcIlxuICAgICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogJ1Row6ptIGto4buRaSB2w6BvIHRyb25nJywgekluZGV4OiAxMzAwIH0gfVwiXG4gICAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlckFkZEluc2lkZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBAaWYgKGNhblRvZ2dsZVR5cGUoaXRlbSkpIHtcbiAgICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgICBbdHlwZV09XCInYnV0dG9uLXRoaXJkJ1wiXG4gICAgICAgICAgICAgICAgICBbaWNvbk9ubHlUeXBlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgW3NpemVCdXR0b25dPVwiJ3NtYWxsZXInXCJcbiAgICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cImlzQ29udGFpbmVyKGl0ZW0pID8gJ2xpYnMtdWktaWNvbi1zcGxpdC1jZWxsJyA6ICdsaWJzLXVpLWljb24tbWVyZ2UtY2VsbCdcIlxuICAgICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogaXNDb250YWluZXIoaXRlbSkgPyAnxJDGsGEgduG7gSBraOG7kWkgxJHGoW4nIDogJ0NodXnhu4NuIHRow6BuaCBraOG7kWkgZ2jDqXAnLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyVG9nZ2xlVHlwZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZCdcIlxuICAgICAgICAgICAgICAgIFtpY29uT25seVR5cGVdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgW3NpemVCdXR0b25dPVwiJ3NtYWxsZXInXCJcbiAgICAgICAgICAgICAgICBbY2xhc3NJY29uTGVmdF09XCInbGlicy11aS1pY29uLXNldHRpbmcnXCJcbiAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiAnQ+G6pXUgaMOsbmggdGjhursnLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlckNvbmZpZygkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgICAgPGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvblxuICAgICAgICAgICAgICAgIFt0eXBlXT1cIididXR0b24tdGhpcmQtaG92ZXItZGFuZ2VyJ1wiXG4gICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tcmVtb3ZlJ1wiXG4gICAgICAgICAgICAgICAgW3BvcG92ZXJdPVwieyBjb25maWc6IHsgY29udGVudDogaXNDb250YWluZXIoaXRlbSkgPyAnWG/DoSBj4bqjIGto4buRaSBnaMOpcCcgOiAnWG/DoSBraOG7kWknLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAob3V0Q2xpY2spPVwiaGFuZGxlclJlbW92ZSgkZXZlbnQsIGl0ZW0pXCIgLz5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIEBpZiAoaXRlbS5jaGlsZHJlbiAmJiBpdGVtLmNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICA8IS0tIEto4buRaSBjw7MgY29uIOKGkiBydeG7mXQgbsOzIGzhuqFpIGzDoCBt4buZdCBt4bq3dCBwaOG6s25nIG7hu69hLiDEkOG7hyBxdXkg4bufIMSRw6J5LCBuxqFpIGTDuW5nIGtow7RuZyBwaOG6o2kgbG8uXG4gICAgICAgICAgICAgICBM4buBIMSR4bq3dCDhu58gxJDDglkgKHbhu48gY29udGFpbmVyKSwga2jDtG5nIMSR4bq3dCB2w6BvIGzGsOG7m2kgY29uIOKAlCBsxrDhu5tpIGNvbiBnaeG7ryBuZ3V5w6puIGtoZSAycHguXG5cbiAgICAgICAgICAgICAgIPCflLQgS2h1w7RuIMSR4bq3dCB0cm9uZyBgbmctdGVtcGxhdGVgIMSR4buDIMSRaSDEkcaw4bujYyBIQUkgxJHGsOG7nW5nOlxuICAgICAgICAgICAgICAgLSBDb250YWluZXIgS0jDlE5HIGPDsyB24buPIHJpw6puZyDihpIgduG6vSB0aOG6s25nIHThuqFpIMSRw6J5LCB5IG5oxrAgdHLGsOG7m2MuXG4gICAgICAgICAgICAgICAtIENvbnRhaW5lciBDw5MgYGdldENvbXBvbmVudE91dGxldGAg4oaSIHRoxrAgdmnhu4duIHRydXnhu4FuIGtodcO0biBuw6B5IHh14buRbmcgY29tcG9uZW50IHbhu48gcXVhXG4gICAgICAgICAgICAgICAgIGlucHV0IGBjaGlsZENhbnZhc2AsIHbhu48gdOG7sSDEkeG6t3QgbsOzIHbDoG8gZ2nhu69hIHRpw6p1IMSR4buBIHbDoCBwaOG6p24gdGjDom4gY+G7p2EgbcOsbmguIFbhur0gdGjDqm0g4bufXG4gICAgICAgICAgICAgICAgIMSRw6J5IG7hu69hIGzDoCBoYWkgbeG6t3QgcGjhurNuZyBs4buTbmcgY2jhu5NuZyBuaGF1LiAtLT5cbiAgICAgICAgICA8bmctdGVtcGxhdGUgI2NoaWxkQ2FudmFzVHBsPlxuICAgICAgICAgICAgPGxpYnNfdWktc2VydmljZXMtZ3JpZF9sYXlvdXQtY2FudmFzXG4gICAgICAgICAgICAgIGNsYXNzPVwiYmxvY2sgaC1mdWxsIHctZnVsbFwiXG4gICAgICAgICAgICAgIFtzdHlsZS5wYWRkaW5nXT1cImNvbnRhaW5lclBhZGRpbmcoKVwiXG4gICAgICAgICAgICAgIFtub2RlXT1cIml0ZW1cIlxuICAgICAgICAgICAgICBbZGVwdGhdPVwiZGVwdGgoKSArIDFcIlxuICAgICAgICAgICAgICBbZHJhZ0Zyb21Bbnl3aGVyZV09XCJkcmFnRnJvbUFueXdoZXJlKClcIlxuICAgICAgICAgICAgICBbbmVzdGVkU2Nyb2xsYmFyXT1cIm5lc3RlZFNjcm9sbGJhcigpXCJcbiAgICAgICAgICAgICAgW2hpZGRlblRvb2xiYXJdPVwiaGlkZGVuVG9vbGJhcigpXCJcbiAgICAgICAgICAgICAgW3RlbXBsYXRlVG9vbGJhcl09XCJ0ZW1wbGF0ZVRvb2xiYXIoKVwiXG4gICAgICAgICAgICAgIFtzZWxlY3RlZE5vZGVJZF09XCJzZWxlY3RlZE5vZGVJZCgpXCJcbiAgICAgICAgICAgICAgKG91dFNlbGVjdE5vZGUpPVwib3V0U2VsZWN0Tm9kZS5lbWl0KCRldmVudClcIlxuICAgICAgICAgICAgICAob3V0Q29uZmlnTm9kZSk9XCJvdXRDb25maWdOb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChvdXRDaGFuZ2UpPVwib3V0Q2hhbmdlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChvdXRSZW1vdmVOb2RlKT1cIm91dFJlbW92ZU5vZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dEFkZEJsb2NrSW5zaWRlKT1cIm91dEFkZEJsb2NrSW5zaWRlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChvdXRUb2dnbGVUeXBlKT1cIm91dFRvZ2dsZVR5cGUuZW1pdCgkZXZlbnQpXCIgLz5cbiAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuXG4gICAgICAgICAgQGlmICghaXRlbS5nZXRDb21wb25lbnRPdXRsZXQpIHtcbiAgICAgICAgICAgIDxuZy1jb250YWluZXIgW25nVGVtcGxhdGVPdXRsZXRdPVwiY2hpbGRDYW52YXNUcGxcIiAvPlxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG4gICAgPC9ncmlkc3Rlci1pdGVtPlxuICB9XG4gICAgICA8L2dyaWRzdGVyPlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cblxuICA8IS0tIFRoYW5oIGRvY2sgxJFp4buBdSBraGnhu4NuIHRodSBwaMOzbmcgbuG7lWkg4bufIGfDs2MgZMaw4bubaSB0csOhaSAtLT5cbiAgQGlmIChzaG93Wm9vbURvY2soKSkge1xuICAgIDxkaXZcbiAgICAgICN6b29tRG9ja1xuICAgICAgaWQ9XCJjYW52YXMtem9vbS1kb2NrXCJcbiAgICAgIHJvbGU9XCJ0b29sYmFyXCJcbiAgICAgIHRhYmluZGV4PVwiMFwiXG4gICAgICBjbGFzcz1cImFic29sdXRlIGJvdHRvbS00IGxlZnQtNiB6LTMwIGZsZXggaXRlbXMtY2VudGVyIGJnLXdoaXRlIHJvdW5kZWQtZnVsbCBzaGFkb3ctMnhsIGJvcmRlci0yIGJvcmRlci1zbGF0ZS00MDAgcC0xIHNwYWNlLXgtMSBzZWxlY3Qtbm9uZVwiXG4gICAgICAobW91c2Vkb3duKT1cIiRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiXG4gICAgICAoa2V5ZG93bik9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgKGNsaWNrKT1cIiRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiPlxuICAgICAgPCEtLSBUaHUgbmjhu48gKC0pIC0tPlxuICAgICAgPGJ1dHRvblxuICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgY2xhc3M9XCJ3LVsyOHB4XSBoLVsyOHB4XSBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlciByb3VuZGVkLWZ1bGwgdGV4dC1zbGF0ZS03MDAgaG92ZXI6dGV4dC1zbGF0ZS05MDAgaG92ZXI6Ymctc2xhdGUtMjAwIGRpc2FibGVkOm9wYWNpdHktMzAgZGlzYWJsZWQ6aG92ZXI6YmctdHJhbnNwYXJlbnQgdHJhbnNpdGlvbi1jb2xvcnMgY3Vyc29yLXBvaW50ZXJcIlxuICAgICAgICBbZGlzYWJsZWRdPVwiY3VycmVudFpvb20oKSA8PSB6b29tTWluKClcIlxuICAgICAgICBbdGl0bGVdPVwiJ2kxOG5fem9vbV9vdXQnIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJab29tT3V0KClcIj5cbiAgICAgICAgPHN2ZyBjbGFzcz1cInctMy41IGgtMy41XCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMi41XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiPjxsaW5lIHgxPVwiNVwiIHkxPVwiMTJcIiB4Mj1cIjE5XCIgeTI9XCIxMlwiLz48L3N2Zz5cbiAgICAgIDwvYnV0dG9uPlxuXG4gICAgICA8IS0tIFThu7cgbOG7hyAlIHbDoCBtZW51IHByZXNldCAtLT5cbiAgICAgIDxkaXYgY2xhc3M9XCJyZWxhdGl2ZVwiPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgY2xhc3M9XCJoLVsyOHB4XSBweC0yIGZsZXggaXRlbXMtY2VudGVyIGdhcC0xIHJvdW5kZWQtZnVsbCB0ZXh0LXhzIGZvbnQtc2VtaWJvbGQgdGV4dC1zbGF0ZS03MDAgaG92ZXI6Ymctc2xhdGUtMjAwIHRyYW5zaXRpb24tY29sb3JzIGN1cnNvci1wb2ludGVyXCJcbiAgICAgICAgICBbdGl0bGVdPVwiJ2kxOG5fem9vbV9vcHRpb25zJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJUb2dnbGVNZW51KClcIj5cbiAgICAgICAgICA8c3Bhbj57eyB6b29tUGVyY2VudCgpIH19JTwvc3Bhbj5cbiAgICAgICAgICA8c3ZnIGNsYXNzPVwidy1bMTJweF0gaC1bMTJweF0gdGV4dC1zbGF0ZS00MDAgdHJhbnNpdGlvbi10cmFuc2Zvcm0gZHVyYXRpb24tMTUwXCIgW2NsYXNzLnJvdGF0ZS0xODBdPVwiaXNNZW51T3BlbigpXCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMi41XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCI+PHBvbHlsaW5lIHBvaW50cz1cIjYgOSAxMiAxNSAxOCA5XCIvPjwvc3ZnPlxuICAgICAgICA8L2J1dHRvbj5cblxuICAgICAgICA8IS0tIE1lbnUgZHJvcGRvd24gY8OhYyBt4buRYyB0aHUgcGjDs25nIC0tPlxuICAgICAgICBAaWYgKGlzTWVudU9wZW4oKSkge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJhYnNvbHV0ZSBib3R0b20tZnVsbCBtYi0yIGxlZnQtMCB3LVsyNTZweF0gYmctd2hpdGUgcm91bmRlZC14bCBzaGFkb3cteGwgYm9yZGVyIGJvcmRlci1zbGF0ZS0yMDAgcHktMS41IHotNDAgdGV4dC14cyBzZWxlY3Qtbm9uZVwiPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInB4LTMgcHktMSB0ZXh0LVsxMXB4XSBmb250LXNlbWlib2xkIHRleHQtc2xhdGUtNDAwIHVwcGVyY2FzZSB0cmFja2luZy13aWRlclwiPlxuICAgICAgICAgICAgICB7eyAnaTE4bl96b29tX2NhbnZhc193aWR0aCcgfCB0cmFuc2xhdGU6IHsgd2lkdGg6IHN0YWdlV2lkdGgoKSB9IH19XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgIGNsYXNzPVwidy1mdWxsIHB4LTMgcHktMiB0ZXh0LWxlZnQgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1iZXR3ZWVuIGhvdmVyOmJnLXNsYXRlLTUwIHRyYW5zaXRpb24tY29sb3JzIGN1cnNvci1wb2ludGVyXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJGaXRTY3JlZW4oKVwiPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggaXRlbXMtY2VudGVyIGdhcC0yIHRleHQtc2xhdGUtNzAwIGZvbnQtbWVkaXVtXCI+XG4gICAgICAgICAgICAgICAgPHN2ZyBjbGFzcz1cInctMy41IGgtMy41IHRleHQtc2xhdGUtNjAwXCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMlwiPjxwb2x5bGluZSBwb2ludHM9XCIxNSAzIDIxIDMgMjEgOVwiLz48cG9seWxpbmUgcG9pbnRzPVwiOSAyMSAzIDIxIDMgMTVcIi8+PGxpbmUgeDE9XCIyMVwiIHkxPVwiM1wiIHgyPVwiMTRcIiB5Mj1cIjEwXCIvPjxsaW5lIHgxPVwiM1wiIHkxPVwiMjFcIiB4Mj1cIjEwXCIgeTI9XCIxNFwiLz48L3N2Zz5cbiAgICAgICAgICAgICAgICB7eyAnaTE4bl96b29tX2ZpdF9zY3JlZW4nIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LVsxMHB4XSBiZy1ibHVlLTUwIHRleHQtYmx1ZS02MDAgcHgtMS41IHB5LTAuNSByb3VuZGVkIGZvbnQtbWVkaXVtXCI+e3sgJ2kxOG5fYXV0bycgfCB0cmFuc2xhdGUgfX08L3NwYW4+XG4gICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJoLXB4IGJnLXNsYXRlLTEwMCBteS0xXCI+PC9kaXY+XG4gICAgICAgICAgICBAZm9yIChwcmVzZXQgb2Ygem9vbVByZXNldHM7IHRyYWNrIHByZXNldC52YWx1ZSkge1xuICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJ3LWZ1bGwgcHgtMyBweS0xLjUgdGV4dC1sZWZ0IGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktYmV0d2VlbiBob3ZlcjpiZy1zbGF0ZS01MCB0cmFuc2l0aW9uLWNvbG9ycyBjdXJzb3ItcG9pbnRlclwiXG4gICAgICAgICAgICAgICAgW2NsYXNzLnRleHQtYmx1ZS02MDBdPVwiaXNDdXJyZW50UHJlc2V0KHByZXNldC52YWx1ZSlcIlxuICAgICAgICAgICAgICAgIFtjbGFzcy5mb250LXNlbWlib2xkXT1cImlzQ3VycmVudFByZXNldChwcmVzZXQudmFsdWUpXCJcbiAgICAgICAgICAgICAgICBbY2xhc3MuYmctYmx1ZS01MF09XCJpc0N1cnJlbnRQcmVzZXQocHJlc2V0LnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJTZWxlY3RQcmVzZXQocHJlc2V0LnZhbHVlKVwiPlxuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1zbGF0ZS03MDBcIiBbY2xhc3MudGV4dC1ibHVlLTYwMF09XCJpc0N1cnJlbnRQcmVzZXQocHJlc2V0LnZhbHVlKVwiPlxuICAgICAgICAgICAgICAgICAge3sgcHJlc2V0LmxhYmVsIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgICAgIEBpZiAoaXNDdXJyZW50UHJlc2V0KHByZXNldC52YWx1ZSkpIHtcbiAgICAgICAgICAgICAgICAgIDxzdmcgY2xhc3M9XCJ3LTMuNSBoLTMuNSB0ZXh0LWJsdWUtNjAwXCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMi41XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCI+PHBvbHlsaW5lIHBvaW50cz1cIjIwIDYgOSAxNyA0IDEyXCIvPjwvc3ZnPlxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8IS0tIFBow7NuZyB0byAoKykgLS0+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICBjbGFzcz1cInctWzI4cHhdIGgtWzI4cHhdIGZsZXggaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIHJvdW5kZWQtZnVsbCB0ZXh0LXNsYXRlLTcwMCBob3Zlcjp0ZXh0LXNsYXRlLTkwMCBob3ZlcjpiZy1zbGF0ZS0yMDAgZGlzYWJsZWQ6b3BhY2l0eS0zMCBkaXNhYmxlZDpob3ZlcjpiZy10cmFuc3BhcmVudCB0cmFuc2l0aW9uLWNvbG9ycyBjdXJzb3ItcG9pbnRlclwiXG4gICAgICAgIFtkaXNhYmxlZF09XCJjdXJyZW50Wm9vbSgpID49IHpvb21NYXgoKVwiXG4gICAgICAgIFt0aXRsZV09XCInaTE4bl96b29tX2luJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgIChjbGljayk9XCJoYW5kbGVyWm9vbUluKClcIj5cbiAgICAgICAgPHN2ZyBjbGFzcz1cInctMy41IGgtMy41XCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMi41XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiPjxsaW5lIHgxPVwiMTJcIiB5MT1cIjVcIiB4Mj1cIjEyXCIgeTI9XCIxOVwiLz48bGluZSB4MT1cIjVcIiB5MT1cIjEyXCIgeDI9XCIxOVwiIHkyPVwiMTJcIi8+PC9zdmc+XG4gICAgICA8L2J1dHRvbj5cblxuICAgICAgPCEtLSBQaMOibiBjw6FjaCAtLT5cbiAgICAgIDxkaXYgY2xhc3M9XCJoLVsxNnB4XSB3LXB4IGJnLXNsYXRlLTIwMFwiPjwvZGl2PlxuXG4gICAgICA8IS0tIE7DunQgVuG7q2EgbcOgbiBow6xuaCBuaGFuaCAtLT5cbiAgICAgIDxidXR0b25cbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwiaC1bMjhweF0gc2hyaW5rLTAgcHgtMi41IGZsZXggaXRlbXMtY2VudGVyIGdhcC0xIHJvdW5kZWQtZnVsbCB0ZXh0LXhzIHRleHQtc2xhdGUtNzAwIGhvdmVyOnRleHQtc2xhdGUtOTAwIGhvdmVyOmJnLXNsYXRlLTIwMCB0cmFuc2l0aW9uLWNvbG9ycyBjdXJzb3ItcG9pbnRlclwiXG4gICAgICAgIFt0aXRsZV09XCInaTE4bl96b29tX2ZpdF9zY3JlZW5fdG9vbHRpcCcgfCB0cmFuc2xhdGVcIlxuICAgICAgICAoY2xpY2spPVwiaGFuZGxlckZpdFNjcmVlbigpXCI+XG4gICAgICAgIDxzdmcgY2xhc3M9XCJ3LTMuNSBoLTMuNVwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjJcIj48cG9seWxpbmUgcG9pbnRzPVwiMTUgMyAyMSAzIDIxIDlcIi8+PHBvbHlsaW5lIHBvaW50cz1cIjkgMjEgMyAyMSAzIDE1XCIvPjxsaW5lIHgxPVwiMjFcIiB5MT1cIjNcIiB4Mj1cIjE0XCIgeTI9XCIxMFwiLz48bGluZSB4MT1cIjNcIiB5MT1cIjIxXCIgeDI9XCIxMFwiIHkyPVwiMTRcIi8+PC9zdmc+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwid2hpdGVzcGFjZS1ub3dyYXAgZm9udC1tZWRpdW1cIj57eyAnaTE4bl96b29tX2ZpdF9zY3JlZW4nIHwgdHJhbnNsYXRlIH19PC9zcGFuPlxuICAgICAgPC9idXR0b24+XG5cbiAgICAgIDwhLS0gTsO6dCDEkOG6t3QgbOG6oWkgMTAwJSAoY2jhu4kgaGnhu4duIGtoaSBraMOhYyAxMDAlKSAtLT5cbiAgICAgIEBpZiAoem9vbVBlcmNlbnQoKSAhPT0gMTAwKSB7XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICBjbGFzcz1cImgtWzI4cHhdIHB4LTIgZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTEgcm91bmRlZC1mdWxsIHRleHQteHMgZm9udC1zZW1pYm9sZCB0ZXh0LWJsdWUtNjAwIGhvdmVyOmJnLWJsdWUtNTAgdHJhbnNpdGlvbi1jb2xvcnMgY3Vyc29yLXBvaW50ZXJcIlxuICAgICAgICAgIFt0aXRsZV09XCInaTE4bl96b29tX3Jlc2V0X3Rvb2x0aXAnIHwgdHJhbnNsYXRlOiB7IHdpZHRoOiBzdGFnZVdpZHRoKCksIGhlaWdodDogc3RhZ2VNaW5IZWlnaHQoKSB9XCJcbiAgICAgICAgICAoY2xpY2spPVwiaGFuZGxlclJlc2V0Wm9vbSgpXCI+XG4gICAgICAgICAgPHN2ZyBjbGFzcz1cInctWzEycHhdIGgtWzEycHhdXCIgdmlld0JveD1cIjAgMCAyNCAyNFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgc3Ryb2tlLXdpZHRoPVwiMlwiIHN0cm9rZS1saW5lY2FwPVwicm91bmRcIiBzdHJva2UtbGluZWpvaW49XCJyb3VuZFwiPjxwb2x5bGluZSBwb2ludHM9XCIxIDQgMSAxMCA3IDEwXCIvPjxwYXRoIGQ9XCJNMy41MSAxNWE5IDkgMCAxIDAgMi4xMy05LjM2TDEgMTBcIi8+PC9zdmc+XG4gICAgICAgICAgPHNwYW4+MTAwJTwvc3Bhbj5cbiAgICAgICAgPC9idXR0b24+XG4gICAgICB9XG4gICAgPC9kaXY+XG4gIH1cbn0gQGVsc2Uge1xuICA8IS0tIPCflLQgYHJlbGF0aXZlYCBsw6AgQuG6rlQgQlXhu5hDLCBD4bqkTSBi4buPLlxuICAgICAgIGdyaWRzdGVyIMSR4buVaSB24buLIHRyw60gY2h14buZdCB0aMOgbmggw7QgbMaw4bubaSBi4bqxbmcgYGUuY2xpZW50WSArIChlbC5zY3JvbGxUb3AgLSBlbC5vZmZzZXRUb3ApYCDigJQgY8O0bmdcbiAgICAgICB0aOG7qWMgxJHDsyBjaOG7iSDEkcO6bmcga2hpIGBvZmZzZXRQYXJlbnRgIGPhu6dhIDxncmlkc3Rlcj4gQ0jDjU5IIEzDgCBraOG7kWkgY3Xhu5luIG7DoHkuIFRoaeG6v3UgYHJlbGF0aXZlYCB0aMOsXG4gICAgICAgYG9mZnNldFBhcmVudGAgbmjhuqN5IGzDqm4ga2jhu5FpIGJhbyBuZ2/DoGk6IGBvZmZzZXRUb3BgIHRy4bqjIDE2IHRyb25nIGtoaSBs4buHY2ggdGjhuq10IGzDoCA2NSDihpIgZ3JpZHN0ZXJcbiAgICAgICB0w61uaCDDtCBzYWkgNDlweCwgw7QgY2jhu50gxJHhurd0IGhp4buHbiBs4buHY2gga2jhu49pIGNvbiB0cuG7jyBuw6puIGvDqW8gdGjhuqMga2jDtG5nIMSR4buVaSDEkcaw4bujYyB24buLIHRyw60sIHbDoCBrw6lvIG3DqXBcbiAgICAgICBjxaluZyB0w61uaCBzYWkgxJFp4buDbSBk4burbmcuIC0tPlxuICA8IS0tIPCflLQgYGlnbm9yZUluaXRgIGzDoCBjw7RuZyB04bqvYyBC4bqsVC9U4bquVCBj4bunYSB0aGFuaCBjdeG7mW4gbuG7lWk6IGRpcmVjdGl2ZSBraMO0bmcgdGjhu4MgZ+G6r24gY8OzIMSRaeG7gXUga2nhu4duLFxuICAgICAgIG7Dqm4ga2hpIHThuq90IHBo4bqjaSBjaOG6t24gbmdheSDhu58gxJHDonkg4oCUIGLhuq10IHLhu5NpIG3hu5tpIGLhu41jIHRow6ptIGBkaXYubGlicy11aS1zY3JvbGwtb3ZlcmxheS1jb250YWluZXJgLlxuICAgICAgIE5o4budIHbhuq15IG3hurd0IHBo4bqzbmcgR+G7kEMgY+G7p2EgbsahaSBkw7luZyBraMOhYyBnaeG7ryBuZ3V5w6puIGPhuqV1IHRyw7pjIERPTSBuaMawIHRyxrDhu5tjLiAtLT5cbiAgPGRpdlxuICAgICNzY3JvbGxBcmVhXG4gICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LXNjcm9sbC1hcmVhIHJlbGF0aXZlIGgtZnVsbCBtaW4taC0wIHctZnVsbCBtaW4tdy0wXCJcbiAgICBbY2xhc3Mub3ZlcmZsb3cteS1hdXRvXT1cIiFpc05lc3RlZENhbnZhcygpXCJcbiAgICBMaWJzVWlDb21wb25lbnRzU2Nyb2xsT3ZlcmxheURpcmVjdGl2ZVxuICAgIFtpZ25vcmVJbml0XT1cIiFzaG93TmVzdGVkU2Nyb2xsYmFyKClcIlxuICAgIFtvcHRpb25zXT1cIm5lc3RlZFNjcm9sbE92ZXJsYXlPcHRpb25zXCJcbiAgICAobW91c2Vkb3duKT1cImhhbmRsZXJNb3VzZURvd25Ucm9uZ0x1b2koJGV2ZW50KVwiXG4gICAgKHBvaW50ZXJkb3duKT1cImhhbmRsZXJTY3JvbGxBcmVhUG9pbnRlckRvd24oKVwiXG4gICAgKGRyYWdvdmVyKT1cImhhbmRsZXJEcmFnT3ZlcigkZXZlbnQpXCJcbiAgICAoZHJhZ2xlYXZlKT1cImhhbmRsZXJEcmFnTGVhdmUoJGV2ZW50KVwiXG4gICAgKGRyb3ApPVwiaGFuZGxlckRyb3AoJGV2ZW50KVwiPlxuICAgIDwhLS0gU25hcHBlZCBEcm9wIEdob3N0IFByZXZpZXcgLS0+XG4gICAgQGlmIChkcm9wR2hvc3QoKTsgYXMgZ2hvc3QpIHtcbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJwb2ludGVyLWV2ZW50cy1ub25lIGFic29sdXRlIHotMzAgZmxleCBmbGV4LWNvbCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgb3ZlcmZsb3ctaGlkZGVuIHJvdW5kZWQtbGcgYm9yZGVyLTIgYm9yZGVyLWRhc2hlZCBib3JkZXItYmx1ZS01MDAgYmctYmx1ZS01MDAvMTUgcC0xIHNoYWRvdy1zbSB0cmFuc2l0aW9uLWFsbCBkdXJhdGlvbi03NSBlYXNlLW91dCBiYWNrZHJvcC1ibHVyLVswLjVweF0gc2VsZWN0LW5vbmVcIlxuICAgICAgICBbc3R5bGUubGVmdC5weF09XCJnaG9zdC5waXhlbExlZnRcIlxuICAgICAgICBbc3R5bGUudG9wLnB4XT1cImdob3N0LnBpeGVsVG9wXCJcbiAgICAgICAgW3N0eWxlLndpZHRoLnB4XT1cImdob3N0LnBpeGVsV2lkdGhcIlxuICAgICAgICBbc3R5bGUuaGVpZ2h0LnB4XT1cImdob3N0LnBpeGVsSGVpZ2h0XCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJpbmxpbmUtZmxleCBtYXgtdy1bOTUlXSBpdGVtcy1jZW50ZXIgZ2FwLTEgdHJ1bmNhdGUgcm91bmRlZC1tZCBiZy1ibHVlLTYwMCBweC0yIHB5LTAuNSB0ZXh0LVsxMXB4XSBmb250LXNlbWlib2xkIHRleHQtd2hpdGUgc2hhZG93LXhzXCI+XG4gICAgICAgICAgQGlmIChnaG9zdC5jYXJkSWNvbkNsYXNzKSB7XG4gICAgICAgICAgICA8aSBbY2xhc3NdPVwiZ2hvc3QuY2FyZEljb25DbGFzcyArICcgdGV4dC14cydcIj48L2k+XG4gICAgICAgICAgfSBAZWxzZSBpZiAoZ2hvc3QuY2FyZEljb25UZXh0KSB7XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtWzEwcHhdIGZvbnQtYm9sZFwiPnt7IGdob3N0LmNhcmRJY29uVGV4dCB9fTwvc3Bhbj5cbiAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC14cyBmb250LWJvbGRcIj57eyBnaG9zdC5pc0luc2lkZUdyb3VwID8gJ+KGsycgOiAnKycgfX08L3NwYW4+XG4gICAgICAgICAgfVxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwidHJ1bmNhdGVcIj57eyBnaG9zdC5jYXJkVGl0bGUgfX08L3NwYW4+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LVs5cHhdIGZvbnQtbW9ubyBvcGFjaXR5LTc1XCI+KHt7IGdob3N0LmNvbHMgfX1jKTwvc3Bhbj5cbiAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgQGlmIChnaG9zdC5waXhlbEhlaWdodCA+PSA4MCkge1xuICAgICAgICAgIDxkaXYgY2xhc3M9XCJtdC0xIGZsZXggbWF4LXctWzk1JV0gaXRlbXMtY2VudGVyIGdhcC0xLjUgdHJ1bmNhdGUgcm91bmRlZCBib3JkZXIgYm9yZGVyLWJsdWUtMTAwIGJnLXdoaXRlLzk1IHB4LTIgcHktMC41IHRleHQtWzEwcHhdIGZvbnQtbWVkaXVtIHRleHQtYmx1ZS03MDAgc2hhZG93LXhzXCI+XG4gICAgICAgICAgICBAaWYgKGdob3N0LmlzSW5zaWRlR3JvdXApIHtcbiAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0cnVuY2F0ZSBmb250LWJvbGQgdGV4dC1ibHVlLTkwMFwiPuKGsyB7eyBnaG9zdC5ncm91cFRpdGxlIH19PC9zcGFuPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtYmx1ZS0zMDBcIj7igKI8L3NwYW4+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICA8c3Bhbj5D4buZdCB7eyBnaG9zdC5jb2wgKyAxIH19LXt7IGdob3N0LmNvbCArIGdob3N0LmNvbHMgfX08L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgICA8Z3JpZHN0ZXJcbiAgICAgIGNsYXNzPVwiaC1mdWxsIHctZnVsbFwiXG4gICAgICBbY2xhc3MuZGlzcGxheS1ncmlkXT1cInNob3dHcmlkTGluZXMoKVwiXG4gICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1kcmFnLWFueXdoZXJlXT1cImRyYWdGcm9tQW55d2hlcmUoKVwiXG4gICAgICBbb3B0aW9uc109XCJncmlkT3B0aW9ucygpXCI+XG4gICAgICAgIEBmb3IgKGl0ZW0gb2YgY2hpbGRyZW4oKTsgdHJhY2sgaXRlbS5pZDsgbGV0IGl0ZW1JbmRleCA9ICRpbmRleCkge1xuICAgIDwhLS0g8J+UtCBDaOG6t24gYG1vdXNlZG93bmAgdOG6oWkgxJDDglkgY2hvIGzGsOG7m2kgbOG7k25nIOKAlCBD4bqkTSBi4buPIHbDoCBD4bqkTSBjaHV54buDbiBzYW5nIGNo4buXIGtow6FjLlxuICAgICAgICAgYGlnbm9yZUNvbnRlbnRDbGFzc2AgbeG7mXQgbcOsbmggS0jDlE5HIMSR4bunOiBgZGVsYXlTdGFydDogMTYwYCBsw6BtIE3hu5ZJIGzGsOG7m2kgbMOqbiBs4buLY2hcbiAgICAgICAgIGBkcmFnU3RhcnRgIGLhurFuZyBgc2V0VGltZW91dGAgbmdheSB04bqhaSBgbW91c2Vkb3duYCwgY8OybiBgc3RvcFByb3BhZ2F0aW9uYCBtw6AgbGliIGfhu41pIGLDqm5cbiAgICAgICAgIHRyb25nIGBkcmFnU3RhcnRgIGNo4bqheSBzYXUgMTYwbXMgbsOqbiBxdcOhIG114buZbiDigJQgbMaw4bubaSBDSEEgxJHDoyBr4buLcCBuaOG6rW4gKMSRbyAyOC8wOC8yMDI2OiBrw6lvXG4gICAgICAgICBk4bqjaSBraOG7kWkgY29uLCBjaHXhu5dpIGNsYXNzIGR1eeG7h3QgbMOqbiDEkMOaTkcgbcOgIGNvbnRhaW5lciB24bqrbiBuaOG6o3kgMCwwIOKGkiAwLDEpLlxuICAgICAgICAgR+G6r24g4bufIGBncmlkc3Rlci1pdGVtYCBsw6AgxJHDum5nIGJpw6puOiBsxrDhu5tpIGNvbiBuZ2hlIHRyw6puIGNow61uaCBwaOG6p24gdOG7rSBuw6B5IG7Dqm4gxJHDoyBuaOG6rW4geG9uZyxcbiAgICAgICAgIGzGsOG7m2kgY2hhIOG7nyBuZ2/DoGkgYuG7iyBjaOG6t24uIC0tPlxuICAgIDxncmlkc3Rlci1pdGVtXG4gICAgICBbaXRlbV09XCJpdGVtXCJcbiAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LXNlbGVjdGVkXT1cInNlbGVjdGVkTm9kZUlkKCkgPT09IGl0ZW0uaWRcIlxuICAgICAgKGNsaWNrKT1cImhhbmRsZXJTZWxlY3ROb2RlKGl0ZW0sICRldmVudClcIlxuICAgICAgKG1vdXNlZG93bik9XCJoYW5kbGVyQmxvY2tQYXJlbnREcmFnKCRldmVudClcIj5cbiAgICAgIDwhLS0g8J+UtCBW4buPIG7DoHkgbWFuZyBgZHJhZ0hhbmRsZUNsYXNzYCDigJQgQ+G6pE0gYuG7jy5cbiAgICAgICAgICAgYGlnbm9yZUNvbnRlbnQ6IHRydWVgIGLhuq90IGdyaWRzdGVyIENI4buIIGNobyBrw6lvIGtoaSBjaOG7lyBi4bqlbSBu4bqxbSBkxrDhu5tpIG3hu5l0IHBo4bqnbiB04butIGPDs1xuICAgICAgICAgICBgZHJhZ0hhbmRsZUNsYXNzYC4gS2jDtG5nIGPDsyB24buPIG7DoHkgdGjDrCBjb21wb25lbnQgY+G7p2EgbsahaSBkw7luZyAodGjGsCB2aeG7h24ga2jDtG5nIGtp4buDbSBzb8OhdFxuICAgICAgICAgICDEkcaw4bujYyBjbGFzcyBj4bunYSBuw7MpIHPhur0ga2jDtG5nIGPDsyB0YXkgY+G6p20gbsOgbywgdsOgIEtIw5RORyBraOG7kWkgbsOgbyBrw6lvIMSRxrDhu6NjIOKAlCDEkW8gMjgvMDgvMjAyNjpcbiAgICAgICAgICAga8OpbyAzMjBweCwgdG/huqEgxJHhu5kga2jhu5FpIGdp4buvIG5ndXnDqm4geD0zLHk9MC5cbiAgICAgICAgICAgVMOqbiBjbGFzcyB0aGVvIGPhuqVwOiBsxrDhu5tpIHRyYW5nIHbDoCBsxrDhu5tpIGzhu5NuZyBkw7luZyBoYWkgdMOqbiBLSMOBQyBuaGF1LCBu4bq/dSBraMO0bmcgbMaw4bubaSBjaGFcbiAgICAgICAgICAgY8Wpbmcgbmjhuq1uIHJhIHRheSBj4bqnbSBj4bunYSBraOG7kWkgY29uIHbDoCBuaOG6pWMgbHXDtG4gY29udGFpbmVyLiAtLT5cbiAgICAgIDwhLS0g8J+UtCBW4buPIEtIw5RORyBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgIOKAlCBD4bqkTSB0aMOqbSBs4bqhaS5cbiAgICAgICAgICAgS8OpbyBjaOG7iSDEkcaw4bujYyBwaMOpcCBi4bqvdCDEkeG6p3UgdOG7qyBE4bqiSSBYQU5IIChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNjogXCJwaOG6o2kgaG92ZXIgdsOgbyB2w7luZ1xuICAgICAgICAgICBtw6B1IHhhbmggdGjDrCBt4bubaSBrw6lvIHRo4bqjIMSRxrDhu6NjIGNobyDEkeG7k25nIG5o4bqldFwiKS4gQ2hvIGPhuqMgduG7jyBsw6BtIHRheSBj4bqnbSB0aMOsIGLhuqVtIMSRw6J1IGPFqW5nXG4gICAgICAgICAgIGvDqW8sIHbDoCBraOG7kWkgY29uIG7hurFtIHRyb25nIGNvbnRhaW5lciBz4bq9IG5o4bqlYyBsdcO0biBjb250YWluZXIgdsOsIGzGsOG7m2kgY2hhIGR1eeG7h3QgbMOqbiBn4bq3cFxuICAgICAgICAgICB0YXkgY+G6p20gY+G7p2EgduG7jyBjb250YWluZXIgdHLGsOG7m2MuXG4gICAgICAgICAgIFbhu48ga2jhu5FpIGNvbiB24bqrbiBnaeG7ryBjbGFzcyBDSOG6tk4gxJHhu4MgbMaw4bubaSBjaGEgZOG7q25nIMSRw7puZyDhu58gYmnDqm4uIC0tPlxuICAgICAgPCEtLSDwn5S0IGBkcmFnRnJvbUFueXdoZXJlYCBi4bqtdCB0aMOsIELhu44gY2xhc3MgY2jhurduIOKAlCBD4bqkTSBn4bqvbiBs4bqhaSB2w7QgxJFp4buBdSBraeG7h24uXG4gICAgICAgICAgIGBsaWJzLXVpLWdyaWQtbGF5b3V0LWJsb2NrLXBhcmVudC1kcmFnYCBDSMONTkggTMOAIGBpZ25vcmVDb250ZW50Q2xhc3NgIGPhu6dhIGdyaWRzdGVyICh4ZW1cbiAgICAgICAgICAgYGdyaWRzdGVyLWNvbmZpZy5kZWZpbmVgKS4gVuG7m2kgYGlnbm9yZUNvbnRlbnQ6IHRydWVgIG7DsyBjaOG7iSBtYW5nIG5naMSpYSBcImtow7RuZyBwaOG6o2kgdGF5XG4gICAgICAgICAgIGPhuqdtXCI7IG5oxrBuZyB24bubaSBgaWdub3JlQ29udGVudDogZmFsc2VgIChjaOG6vyDEkeG7mSBj4bqnbS1j4bqjLXRo4bq7KSBncmlkc3RlciBoaeG7g3UgbMOgIFwidsO5bmcgQ+G6pE1cbiAgICAgICAgICAga8Opb1wiOiBkdXnhu4d0IHThu6sgY2jhu5cgYuG6pW0gbMOqbiBn4bq3cCBjbGFzcyBuw6B5IGzDoCBk4burbmcgaOG6s24sIG7Dqm4gS0jhu5BJIENPTiB0cm9uZyBraOG7kWkgZ2jDqXAgYuG6pW1cbiAgICAgICAgICAgdsOgbyDEkcOidSBjxaluZyBraMO0bmcgbmjDumMgbmjDrWNoIChuZ8aw4budaSBkw7luZyBjaOG7iSByYSAxNS8wOS8yMDI2OyDEkW8gxJHGsOG7o2Mga8OpbyAxNTBweCBtw6Aga2jhu5FpXG4gICAgICAgICAgIGzhu4djaCAwcHgpLiBDaOG6t24gbMaw4bubaSBjaGEg4bufIGNo4bq/IMSR4buZIG7DoHkgZG8gYGhhbmRsZXJCbG9ja1BhcmVudERyYWdgIHRyw6puIGNow61uaFxuICAgICAgICAgICBgZ3JpZHN0ZXItaXRlbWAgbG8sIGtow7RuZyBj4bqnbiBjbGFzcyBuw6B5LiAtLT5cbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LWJsb2NrLXNoZWxsIGdyb3VwIHJlbGF0aXZlIGgtZnVsbCB3LWZ1bGwgbWluLXctMFwiXG4gICAgICAgIFthdHRyLmRhdGEtbm9kZS1pZF09XCJpdGVtLmlkXCJcbiAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtYmxvY2stcGFyZW50LWRyYWddPVwiaXNOZXN0ZWRDYW52YXMoKSAmJiAhZHJhZ0Zyb21Bbnl3aGVyZSgpXCI+XG4gICAgICAgIDwhLS0gROG6o2kga8OpbzogaGnhu4duIGtoaSByw6ogY2h14buZdCB2w6BvIGto4buRaSwgYmEgY2jhuqVtIGzDoCBk4bqldSBoaeG7h3Uga8OpbyBxdWVuIHRodeG7mWMuXG4gICAgICAgICAgICAgTmfGsOG7nWkgZMO5bmcgY+G6p24gbeG7mXQgY2jhu5cgYsOhbSBSw5UgUsOATkcgdGhheSB2w6wgxJFvw6FuIHhlbSBi4bqlbSDEkcOidSB0aMOsIGvDqW8gxJHGsOG7o2NcbiAgICAgICAgICAgICAoY2jhu5F0IDI4LzA4LzIwMjYsIHRoZW8gxJHDum5nIGThuqNpIMSRYW5nIGTDuW5nIOG7nyBtw6BuIEN1c3RvbWVyIDM2MCBj4bunYSBtb2Jpby13ZWIpLiAtLT5cbiAgICAgICAgPCEtLSDwn5S0IGBkcmFnRnJvbUFueXdoZXJlYCBi4bqtdCB0aMOsIEtIw5RORyB24bq9IGThuqNpOiBj4bqjIHRo4bq7IMSRw6MgbMOgIHRheSBj4bqnbSwgxJHhu4MgbOG6oWkgZOG6o2kgY2jhu4kgdGjhu6thLiAtLT5cbiAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkgJiYgIWRyYWdGcm9tQW55d2hlcmUoKSkge1xuICAgICAgICAgIDwhLS0g8J+UtCBE4bqjaSBQSOG6okkgbWFuZyB0YXkgY+G6p20gxJDDmk5HIEPhuqRQIGPhu6dhIG7Dsy4gROG6o2kgbmjhuq1uIGNodeG7mXQgKGBwb2ludGVyLWV2ZW50czogYXV0b2ApLFxuICAgICAgICAgICAgICAgbsOqbiBu4bq/dSBraMO0bmcgbWFuZyB0YXkgY+G6p20gdGjDrCBgY2hlY2tEcmFnSGFuZGxlQ2xhc3NgIGR1eeG7h3QgbMOqbiBn4bq3cCB0YXkgY+G6p20gY+G7p2FcbiAgICAgICAgICAgICAgIGNvbnRhaW5lciB2w6AgbmjhuqVjIGNvbnRhaW5lciB0aGF5IHbDrCBraOG7kWkgY29uICjEkW8gMjgvMDgvMjAyNjoga8OpbyBk4bqjaSBraOG7kWkgY29uLFxuICAgICAgICAgICAgICAgY29udGFpbmVyIG5o4bqjeSAwLDAg4oaSIDAsMSBjw7JuIGto4buRaSBjb24gxJHhu6luZyBpbSkuXG4gICAgICAgICAgICAgICDwn5S0IEThuqNpIENI4buIIG1hbmcgdGF5IGPhuqdtLCBD4bqkTSBtYW5nIHRow6ptIGNsYXNzIGNo4bq3bjogYGNoZWNrRHJhZ0hhbmRsZUNsYXNzYCB4w6l0IGhhaVxuICAgICAgICAgICAgICAgY2xhc3MgxJHDsyB0csOqbiBDw5lORyBt4buZdCBub2RlIG7Dqm4gxJHhu4MgY2h1bmcgbMOgIGvDqW8gaOG7j25nICjEkW8gMjgvMDgvMjAyNjogYuG6pW0gZOG6o2kga2jhu5FpXG4gICAgICAgICAgICAgICBjb24ga8OpbyAxNDBweCwga2jhu5FpIMSR4bupbmcgaW0pLiBWaeG7h2MgY2jhurduIGzGsOG7m2kgY2hhIGRvIFbhu44gS0jhu5BJIGxvLiAtLT5cbiAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXJcIlxuICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1kcmFnLWhhbmRsZV09XCIhaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICBbY2xhc3MubGlicy11aS1ncmlkLWxheW91dC1kcmFnLWhhbmRsZS1jaGlsZF09XCJpc05lc3RlZENhbnZhcygpXCI+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtZHJhZy1iYXItZG90c1wiPlxuICAgICAgICAgICAgICBAZm9yIChkb3Qgb2YgZHJhZ0JhckRvdHM7IHRyYWNrIGRvdCkge1xuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1kcmFnLWJhci1kb3RcIj48L3NwYW4+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuXG4gICAgICAgIDwhLS0gS2h1bmcgY2jhu6lhIGNvbXBvbmVudCBj4bunYSBuxqFpIGTDuW5nLlxuICAgICAgICAgICAgIPCflLQgUEjhuqJJIGPDsyBraHVuZyB0aOG6rXQgKGtow7RuZyBwaOG6o2kgYG5nLWNvbnRhaW5lcmAgdHLhu5FuZyk6IG7DsyBsbyBo4buZIG7GoWkgZMO5bmcgcGjhuqduIGzhuqVwIMSR4bqneVxuICAgICAgICAgICAgIMO0IHbDoCB2aeG7gW4gYsOhbyBjaOG6vyDEkeG7mSBz4butYS4gS2jDtG5nIGPDsyBraHVuZyB0aMOsIG3hu5dpIGNvbXBvbmVudCBu4buZaSBkdW5nIGzhuqFpIHBo4bqjaSB04buxIHZp4bq/dFxuICAgICAgICAgICAgIGA6aG9zdCB7IHdpZHRoL2hlaWdodDogMTAwJSB9YCArIHZp4buBbiDEkeG7qXQg4oCUIMSRw7MgbMOgIHZp4buHYyBj4bunYSB0aMawIHZp4buHbiwga2jDtG5nIHBo4bqjaSBj4bunYVxuICAgICAgICAgICAgIG5nxrDhu51pIGTDuW5nIChuZ8aw4budaSBkw7luZyBjaOG7kXQgMjgvMDgvMjAyNikuXG4gICAgICAgICAgICAgTeG7l2kga2jhu5FpIG3hu5l0IFZpZXdDb250YWluZXJSZWYgcmnDqm5nIG7Dqm4gY29tcG9uZW50IG7hurFtIMSRw7puZyDDtCBj4bunYSBuw7MuIC0tPlxuICAgICAgICA8IS0tIPCflLQgQ29udGFpbmVyIEPDkyBgZ2V0Q29tcG9uZW50T3V0bGV0YDoga2h1bmcgbsOgeSBsw6AgVuG7jiwgdsOgIGNhbnZhcyBjb24gxJHGsOG7o2MgxJHGsGEgVsOATyBUUk9OR1xuICAgICAgICAgICAgIGNvbXBvbmVudCB24buPIHF1YSBpbnB1dCBgY2hpbGRDYW52YXNgICht4buZdCBgVGVtcGxhdGVSZWZgKSDigJQgeGVtIGBuZy10ZW1wbGF0ZSAjY2hpbGRDYW52YXNUcGxgXG4gICAgICAgICAgICAgYsOqbiBkxrDhu5tpLiBW4buPIHThu7EgcXV54bq/dCDEkeG6t3QgY2FudmFzIOG7nyDEkcOidSBnaeG7r2EgdGnDqnUgxJHhu4EgdsOgIHBo4bqnbiB0aMOibiBj4bunYSBuw7MuXG4gICAgICAgICAgICAgVHLGsOG7m2MgxJHDonkgY2FudmFzIGNvbiBsw6AgQU5IIEVNIGPhu6dhIGtodW5nIG7DoHksIG7Dqm4gduG7jyBjaGnhur9tIHRy4buNbiBjaGnhu4F1IGNhbyBy4buTaSDEkeG6qXkgdG/DoG4gYuG7mVxuICAgICAgICAgICAgIHRo4bq7IGNvbiByYSBuZ2/DoGkgdsO5bmcgbmjDrG4gdGjhuqV5ICjEkW8gMTQvMDkvMjAyNjogbeG7jWkga2jhu5FpIGdow6lwIGNo4buJIGPDsm4gbmjDo24gdsOgIHJ14buZdCB0cuG6r25nKS4gLS0+XG4gICAgICAgIDxkaXZcbiAgICAgICAgICBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGVudC1mcmFtZVwiXG4gICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGVudC1mcmFtZS1lZGl0XT1cImlzRWRpdE1vZGUoKSAmJiAhaXNDb250YWluZXIoaXRlbSlcIlxuICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LWNvbnRlbnQtZnJhbWUtZW1wdHldPVwiaXNDb250YWluZXIoaXRlbSkgJiYgIWl0ZW0uZ2V0Q29tcG9uZW50T3V0bGV0XCI+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciAjaXRlbUhvc3QgLz5cbiAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgPCEtLSBE4bqldSBoaeG7h3UgQ09OVEFJTkVSOiBuaMOjbiBnw7NjIHRyw6puLXBo4bqjaSArIHZp4buBbiDEkeG7qXQgYmFvIHJ14buZdC4gS2jDtG5nIGPDsyBk4bqldSBoaeG7h3UgdGjDrFxuICAgICAgICAgICAgIG5nxrDhu51pIGTDuW5nIGtow7RuZyBiaeG6v3Qga2jhu5FpIG7DoG8gY2jhu6lhIMSRxrDhu6NjIGto4buRaSBraMOhYyAoY2jhu5F0IDI4LzA4LzIwMjYsIHRoZW8gxJHDum5nIGPDoWNoXG4gICAgICAgICAgICAgbcOgbiBDdXN0b21lciAzNjAgY+G7p2EgbW9iaW8td2ViIMSRYW5nIGzDoG0pLiAtLT5cbiAgICAgICAgQGlmIChpc0VkaXRNb2RlKCkgJiYgaXNDb250YWluZXIoaXRlbSkpIHtcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1jb250YWluZXItbGFiZWxcIj57eyAnaTE4bl9jb250YWluZXJfYmxvY2snIHwgdHJhbnNsYXRlIH19PC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImxpYnMtdWktZ3JpZC1sYXlvdXQtY29udGFpbmVyLWJvcmRlclwiPjwvZGl2PlxuICAgICAgICB9XG5cbiAgICAgICAgPCEtLSBUaGFuaCBjw7RuZyBj4bulOiBoaeG7h24ga2hpIHLDqiBjaHXhu5l0IHbDoG8ga2jhu5FpLlxuICAgICAgICAgICAgIPCflLQgRMO5bmcgYGxpYnNfdWktY29tcG9uZW50cy1idXR0b25zLWJ1dHRvbmAg4oCUIEPhuqRNIHThu7EgZOG7sW5nIHRo4bq7IGA8YnV0dG9uPmAuXG4gICAgICAgICAgICAgQ29tcG9uZW50IG7DoHkgxJHDoyBjw7Mgc+G6tW4ga2nhu4N1IG7DunQsIGPhu6EgbsO6dCB2w6AgYm9uZyBiw7NuZyBjaMO6IHRow61jaCB0aGVvIMSRw7puZyBi4buZIGdpYW8gZGnhu4duXG4gICAgICAgICAgICAgY+G7p2Egc+G6o24gcGjhuqltOyB04buxIGThu7FuZyBsw6AgbeG7l2kgbsahaSBt4buZdCBraeG7g3UgdsOgIG3huqV0IGx1w7RuIHRvb2x0aXAuIC0tPlxuICAgICAgICBAaWYgKGlzRWRpdE1vZGUoKSAmJiAhaGlkZGVuVG9vbGJhcigpKSB7XG4gICAgICAgICAgQGlmICh0ZW1wbGF0ZVRvb2xiYXIoKSkge1xuICAgICAgICAgICAgPCEtLSBOxqFpIGTDuW5nIHThu7EgduG6vSB0aGFuaCBjw7RuZyBj4bulOiBuaOG6rW4ga2jhu5FpICsgY8OhYyBow6BtIHBow6F0IGV2ZW50IHF1YSBjb250ZXh0LlxuXG4gICAgICAgICAgICAgICAgIPCflLQgS2jhu5FpIGLhu41jIHThu7EgY2jhurduIGBtb3VzZWRvd25gL2BjbGlja2AgbuG7lWkgbMOqbiBncmlkc3Rlci4gVGhhbmggY8O0bmcgY+G7pSBu4bqxbSB0cm9uZ1xuICAgICAgICAgICAgICAgICB2w7luZyBtYW5nIGBkcmFnSGFuZGxlQ2xhc3NgOyBraMO0bmcgY2jhurduIHRow6wgY8O6IGLhuqVtIGLhu4sgaGnhu4N1IGzDoCBi4bqvdCDEkeG6p3Uga8OpbyB2w6BcbiAgICAgICAgICAgICAgICAgYGRlbGF5U3RhcnQ6IDE2MGAgbnXhu5F0IGx1w7RuIGBjbGlja2Ag4oCUIG7DunQgdHJvbmcgdGVtcGxhdGUgbmdvw6BpIGLhuqVtIGtow7RuZyDEg25cbiAgICAgICAgICAgICAgICAgKMSRbyAwOS8wOS8yMDI2OiBi4bqlbSBi4bqxbmcgYC5jbGljaygpYCBj4bunYSBET00gdGjDrCBt4bufLCBi4bqlbSBjaHXhu5l0IHRo4bqtdCB0aMOsIGtow7RuZykuXG4gICAgICAgICAgICAgICAgIENo4bq3biDhu58gxJDDglkgxJHhu4MgbsahaSBkw7luZyBraMO0bmcgcGjhuqNpIGJp4bq/dCBi4bqreSBuw6B5LiAtLT5cbiAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgY2xhc3M9XCJsaWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXJcIlxuICAgICAgICAgICAgICByb2xlPVwidG9vbGJhclwiXG4gICAgICAgICAgICAgIHRhYmluZGV4PVwiMFwiXG4gICAgICAgICAgICAgIFtjbGFzcy5saWJzLXVpLWdyaWQtbGF5b3V0LXRvb2xiYXItY2hpbGRdPVwiaXNOZXN0ZWRDYW52YXMoKVwiXG4gICAgICAgICAgICAgIChtb3VzZWRvd24pPVwiaGFuZGxlclN0b3BFdmVudCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKGtleWRvd24pPVwiaGFuZGxlclN0b3BFdmVudCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cImhhbmRsZXJTdG9wRXZlbnQoJGV2ZW50KVwiPlxuICAgICAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAgICAgKm5nVGVtcGxhdGVPdXRsZXQ9XCJ0ZW1wbGF0ZVRvb2xiYXIoKSE7IGNvbnRleHQ6IHRvb2xiYXJDb250ZXh0cygpW2l0ZW1JbmRleF1cIiAvPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC10b29sYmFyXCJcbiAgICAgICAgICAgICAgW2NsYXNzLmxpYnMtdWktZ3JpZC1sYXlvdXQtdG9vbGJhci1jaGlsZF09XCJpc05lc3RlZENhbnZhcygpXCI+XG4gICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibGlicy11aS1ncmlkLWxheW91dC1kaW0tYmFkZ2VcIj5cbiAgICAgICAgICAgICAgICB7eyBpdGVtLnJvd3MgfX0gaMOgbmcgw5cge3sgaXRlbS5jb2xzIH19IGPhu5l0XG4gICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgQGlmIChjYW5BZGRJbnNpZGUoaXRlbSkpIHtcbiAgICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgICBbdHlwZV09XCInYnV0dG9uLXRoaXJkJ1wiXG4gICAgICAgICAgICAgICAgICBbaWNvbk9ubHlUeXBlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgW3NpemVCdXR0b25dPVwiJ3NtYWxsZXInXCJcbiAgICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tYWRkJ1wiXG4gICAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiAnVGjDqm0ga2jhu5FpIHbDoG8gdHJvbmcnLCB6SW5kZXg6IDEzMDAgfSB9XCJcbiAgICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyQWRkSW5zaWRlKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIEBpZiAoY2FuVG9nZ2xlVHlwZShpdGVtKSkge1xuICAgICAgICAgICAgICAgIDxsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25cbiAgICAgICAgICAgICAgICAgIFt0eXBlXT1cIididXR0b24tdGhpcmQnXCJcbiAgICAgICAgICAgICAgICAgIFtpY29uT25seVR5cGVdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgICAgW2NsYXNzSWNvbkxlZnRdPVwiaXNDb250YWluZXIoaXRlbSkgPyAnbGlicy11aS1pY29uLXNwbGl0LWNlbGwnIDogJ2xpYnMtdWktaWNvbi1tZXJnZS1jZWxsJ1wiXG4gICAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiBpc0NvbnRhaW5lcihpdGVtKSA/ICfEkMawYSB24buBIGto4buRaSDEkcahbicgOiAnQ2h1eeG7g24gdGjDoG5oIGto4buRaSBnaMOpcCcsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgICAgKG91dENsaWNrKT1cImhhbmRsZXJUb2dnbGVUeXBlKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDxsaWJzX3VpLWNvbXBvbmVudHMtYnV0dG9ucy1idXR0b25cbiAgICAgICAgICAgICAgICBbdHlwZV09XCInYnV0dG9uLXRoaXJkJ1wiXG4gICAgICAgICAgICAgICAgW2ljb25Pbmx5VHlwZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBbc2l6ZUJ1dHRvbl09XCInc21hbGxlcidcIlxuICAgICAgICAgICAgICAgIFtjbGFzc0ljb25MZWZ0XT1cIidsaWJzLXVpLWljb24tc2V0dGluZydcIlxuICAgICAgICAgICAgICAgIFtwb3BvdmVyXT1cInsgY29uZmlnOiB7IGNvbnRlbnQ6ICdD4bqldSBow6xuaCB0aOG6uycsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyQ29uZmlnKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgICA8bGlic191aS1jb21wb25lbnRzLWJ1dHRvbnMtYnV0dG9uXG4gICAgICAgICAgICAgICAgW3R5cGVdPVwiJ2J1dHRvbi10aGlyZC1ob3Zlci1kYW5nZXInXCJcbiAgICAgICAgICAgICAgICBbaWNvbk9ubHlUeXBlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgIFtzaXplQnV0dG9uXT1cIidzbWFsbGVyJ1wiXG4gICAgICAgICAgICAgICAgW2NsYXNzSWNvbkxlZnRdPVwiJ2xpYnMtdWktaWNvbi1yZW1vdmUnXCJcbiAgICAgICAgICAgICAgICBbcG9wb3Zlcl09XCJ7IGNvbmZpZzogeyBjb250ZW50OiBpc0NvbnRhaW5lcihpdGVtKSA/ICdYb8OhIGPhuqMga2jhu5FpIGdow6lwJyA6ICdYb8OhIGto4buRaScsIHpJbmRleDogMTMwMCB9IH1cIlxuICAgICAgICAgICAgICAgIChvdXRDbGljayk9XCJoYW5kbGVyUmVtb3ZlKCRldmVudCwgaXRlbSlcIiAvPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgQGlmIChpdGVtLmNoaWxkcmVuICYmIGl0ZW0uY2hpbGRyZW4ubGVuZ3RoID4gMCkge1xuICAgICAgICAgIDwhLS0gS2jhu5FpIGPDsyBjb24g4oaSIHJ14buZdCBuw7MgbOG6oWkgbMOgIG3hu5l0IG3hurd0IHBo4bqzbmcgbuG7r2EuIMSQ4buHIHF1eSDhu58gxJHDonksIG7GoWkgZMO5bmcga2jDtG5nIHBo4bqjaSBsby5cbiAgICAgICAgICAgICAgIEzhu4EgxJHhurd0IOG7nyDEkMOCWSAoduG7jyBjb250YWluZXIpLCBraMO0bmcgxJHhurd0IHbDoG8gbMaw4bubaSBjb24g4oCUIGzGsOG7m2kgY29uIGdp4buvIG5ndXnDqm4ga2hlIDJweC5cblxuICAgICAgICAgICAgICAg8J+UtCBLaHXDtG4gxJHhurd0IHRyb25nIGBuZy10ZW1wbGF0ZWAgxJHhu4MgxJFpIMSRxrDhu6NjIEhBSSDEkcaw4budbmc6XG4gICAgICAgICAgICAgICAtIENvbnRhaW5lciBLSMOUTkcgY8OzIHbhu48gcmnDqm5nIOKGkiB24bq9IHRo4bqzbmcgdOG6oWkgxJHDonksIHkgbmjGsCB0csaw4bubYy5cbiAgICAgICAgICAgICAgIC0gQ29udGFpbmVyIEPDkyBgZ2V0Q29tcG9uZW50T3V0bGV0YCDihpIgdGjGsCB2aeG7h24gdHJ1eeG7gW4ga2h1w7RuIG7DoHkgeHXhu5FuZyBjb21wb25lbnQgduG7jyBxdWFcbiAgICAgICAgICAgICAgICAgaW5wdXQgYGNoaWxkQ2FudmFzYCwgduG7jyB04buxIMSR4bq3dCBuw7MgdsOgbyBnaeG7r2EgdGnDqnUgxJHhu4EgdsOgIHBo4bqnbiB0aMOibiBj4bunYSBtw6xuaC4gVuG6vSB0aMOqbSDhu59cbiAgICAgICAgICAgICAgICAgxJHDonkgbuG7r2EgbMOgIGhhaSBt4bq3dCBwaOG6s25nIGzhu5NuZyBjaOG7k25nIG5oYXUuIC0tPlxuICAgICAgICAgIDxuZy10ZW1wbGF0ZSAjY2hpbGRDYW52YXNUcGw+XG4gICAgICAgICAgICA8bGlic191aS1zZXJ2aWNlcy1ncmlkX2xheW91dC1jYW52YXNcbiAgICAgICAgICAgICAgY2xhc3M9XCJibG9jayBoLWZ1bGwgdy1mdWxsXCJcbiAgICAgICAgICAgICAgW3N0eWxlLnBhZGRpbmddPVwiY29udGFpbmVyUGFkZGluZygpXCJcbiAgICAgICAgICAgICAgW25vZGVdPVwiaXRlbVwiXG4gICAgICAgICAgICAgIFtkZXB0aF09XCJkZXB0aCgpICsgMVwiXG4gICAgICAgICAgICAgIFtkcmFnRnJvbUFueXdoZXJlXT1cImRyYWdGcm9tQW55d2hlcmUoKVwiXG4gICAgICAgICAgICAgIFtuZXN0ZWRTY3JvbGxiYXJdPVwibmVzdGVkU2Nyb2xsYmFyKClcIlxuICAgICAgICAgICAgICBbaGlkZGVuVG9vbGJhcl09XCJoaWRkZW5Ub29sYmFyKClcIlxuICAgICAgICAgICAgICBbdGVtcGxhdGVUb29sYmFyXT1cInRlbXBsYXRlVG9vbGJhcigpXCJcbiAgICAgICAgICAgICAgW3NlbGVjdGVkTm9kZUlkXT1cInNlbGVjdGVkTm9kZUlkKClcIlxuICAgICAgICAgICAgICAob3V0U2VsZWN0Tm9kZSk9XCJvdXRTZWxlY3ROb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgIChvdXRDb25maWdOb2RlKT1cIm91dENvbmZpZ05vZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dENoYW5nZSk9XCJvdXRDaGFuZ2UuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dFJlbW92ZU5vZGUpPVwib3V0UmVtb3ZlTm9kZS5lbWl0KCRldmVudClcIlxuICAgICAgICAgICAgICAob3V0QWRkQmxvY2tJbnNpZGUpPVwib3V0QWRkQmxvY2tJbnNpZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKG91dFRvZ2dsZVR5cGUpPVwib3V0VG9nZ2xlVHlwZS5lbWl0KCRldmVudClcIiAvPlxuICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG5cbiAgICAgICAgICBAaWYgKCFpdGVtLmdldENvbXBvbmVudE91dGxldCkge1xuICAgICAgICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJjaGlsZENhbnZhc1RwbFwiIC8+XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cbiAgICA8L2dyaWRzdGVyLWl0ZW0+XG4gIH1cbiAgICA8L2dyaWRzdGVyPlxuICA8L2Rpdj5cbn1cbiJdfQ==