@firestitch/report 18.0.0

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.
Files changed (58) hide show
  1. package/app/reports/components/component-chart/component-chart.component.d.ts +16 -0
  2. package/app/reports/components/component-kpi/component-kpi.component.d.ts +12 -0
  3. package/app/reports/components/component-list/component-list.component.d.ts +27 -0
  4. package/app/reports/components/report-canvas/report-canvas.component.d.ts +84 -0
  5. package/app/reports/components/report-component/report-component.component.d.ts +71 -0
  6. package/app/reports/components/timezone-select/timezone-select.component.d.ts +18 -0
  7. package/app/reports/data/report.data.d.ts +48 -0
  8. package/app/reports/dialogs/component-settings/component-settings.component.d.ts +49 -0
  9. package/app/reports/dialogs/report-settings/report-settings.component.d.ts +25 -0
  10. package/app/reports/export/offscreen-chart.d.ts +2 -0
  11. package/app/reports/export/report-export-collector.service.d.ts +26 -0
  12. package/app/reports/export/report-pdf.service.d.ts +13 -0
  13. package/app/reports/export/report-pptx.service.d.ts +14 -0
  14. package/app/reports/format.d.ts +1 -0
  15. package/app/reports/interfaces/report.interface.d.ts +174 -0
  16. package/app/reports/layout.d.ts +11 -0
  17. package/app/reports/option-builder.d.ts +15 -0
  18. package/app/reports/report-filter-items.d.ts +9 -0
  19. package/app/reports/services/report-filter-state.service.d.ts +25 -0
  20. package/app/reports/services/report.service.d.ts +15 -0
  21. package/app/reports/theme/echarts.d.ts +2 -0
  22. package/app/reports/theme/palette.d.ts +2 -0
  23. package/app/reports/views/report/report.component.d.ts +50 -0
  24. package/esm2022/app/reports/components/component-chart/component-chart.component.mjs +47 -0
  25. package/esm2022/app/reports/components/component-kpi/component-kpi.component.mjs +33 -0
  26. package/esm2022/app/reports/components/component-list/component-list.component.mjs +178 -0
  27. package/esm2022/app/reports/components/report-canvas/report-canvas.component.mjs +347 -0
  28. package/esm2022/app/reports/components/report-component/report-component.component.mjs +453 -0
  29. package/esm2022/app/reports/components/timezone-select/timezone-select.component.mjs +70 -0
  30. package/esm2022/app/reports/data/report.data.mjs +152 -0
  31. package/esm2022/app/reports/dialogs/component-settings/component-settings.component.mjs +221 -0
  32. package/esm2022/app/reports/dialogs/report-settings/report-settings.component.mjs +109 -0
  33. package/esm2022/app/reports/export/offscreen-chart.mjs +33 -0
  34. package/esm2022/app/reports/export/report-export-collector.service.mjs +94 -0
  35. package/esm2022/app/reports/export/report-pdf.service.mjs +155 -0
  36. package/esm2022/app/reports/export/report-pptx.service.mjs +224 -0
  37. package/esm2022/app/reports/format.mjs +25 -0
  38. package/esm2022/app/reports/interfaces/report.interface.mjs +16 -0
  39. package/esm2022/app/reports/layout.mjs +50 -0
  40. package/esm2022/app/reports/option-builder.mjs +293 -0
  41. package/esm2022/app/reports/report-filter-items.mjs +125 -0
  42. package/esm2022/app/reports/services/report-filter-state.service.mjs +177 -0
  43. package/esm2022/app/reports/services/report.service.mjs +56 -0
  44. package/esm2022/app/reports/theme/echarts.mjs +59 -0
  45. package/esm2022/app/reports/theme/palette.mjs +10 -0
  46. package/esm2022/app/reports/views/report/report.component.mjs +354 -0
  47. package/esm2022/firestitch-report.mjs +5 -0
  48. package/esm2022/public_api.mjs +13 -0
  49. package/fesm2022/firestitch-report-echarts-BxYnpz7n.mjs +60 -0
  50. package/fesm2022/firestitch-report-echarts-BxYnpz7n.mjs.map +1 -0
  51. package/fesm2022/firestitch-report-firestitch-report-Cnotycly.mjs +3107 -0
  52. package/fesm2022/firestitch-report-firestitch-report-Cnotycly.mjs.map +1 -0
  53. package/fesm2022/firestitch-report.mjs +2 -0
  54. package/fesm2022/firestitch-report.mjs.map +1 -0
  55. package/index.d.ts +5 -0
  56. package/package.json +39 -0
  57. package/public_api.d.ts +5 -0
  58. package/styles.scss +1 -0
@@ -0,0 +1,347 @@
1
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, EventEmitter, Input, Output, ViewChild, inject, } from '@angular/core';
2
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
+ import { MatButton } from '@angular/material/button';
4
+ import { MatIcon } from '@angular/material/icon';
5
+ import { MatTooltip } from '@angular/material/tooltip';
6
+ import { FsZoomPanComponent, FsZoomPanModule } from '@firestitch/zoom-pan';
7
+ import { ReportData } from '../../data/report.data';
8
+ import { DEFAULT_HEADING_SIZE, FLOW_PADDING } from '../../layout';
9
+ import { ReportComponentComponent } from '../report-component/report-component.component';
10
+ import * as i0 from "@angular/core";
11
+ import * as i1 from "@firestitch/zoom-pan";
12
+ // Screen density of the canvas: 96 CSS px per inch (the CSS reference pixel),
13
+ // before zoom. All geometry stays in inches; only rendering multiplies by it.
14
+ export const PX_PER_INCH = 96;
15
+ // The fixed-size page canvas — the PowerPoint model. Pages are tabs (only the
16
+ // ACTIVE page's components exist → only they fetch); the page is a
17
+ // pageWidth×pageHeight INCHES sheet rendered at 96px/in inside fs-zoom-pan
18
+ // (wheel zoom, background drag pan, toolbar buttons).
19
+ //
20
+ // Freeform layout: components are absolutely positioned; dragging gets smart
21
+ // alignment guides (edges + centers of siblings and the page) with snapping.
22
+ // Flow layout: components flow into rows (flowWidth % of the content width),
23
+ // drag reorders, east/south handles resize width%/height.
24
+ export class ReportCanvasComponent {
25
+ report;
26
+ editMode = false;
27
+ // A component asked for its settings dialog — the page-level host opens it
28
+ // (it owns MatDialog + the post-save reload).
29
+ componentSettings = new EventEmitter();
30
+ // The report's structure changed here (a page was added) — the host re-fetches
31
+ // it (it owns the assembled report + filter state).
32
+ reportChanged = new EventEmitter();
33
+ // The viewer dismissed edit mode from the on-canvas badge — the host owns the
34
+ // editMode flag, so it flips it off (mirrors the menu's "Done editing layout").
35
+ editDone = new EventEmitter();
36
+ zoomPan;
37
+ _viewport;
38
+ _guides;
39
+ PX = PX_PER_INCH;
40
+ flowPadding = FLOW_PADDING;
41
+ activePageIndex = 0;
42
+ zoom = 1;
43
+ groups = new Map();
44
+ selectedComponentId = null;
45
+ // The snapper handed to every component (stable identity for OnPush).
46
+ snapper = {
47
+ snapMove: (component, x, y) => this._snapMove(component, x, y),
48
+ snapResize: (component, geometry, handle) => this._snapResize(component, geometry, handle),
49
+ clear: () => this._clearGuides(),
50
+ };
51
+ _reportData = inject(ReportData);
52
+ _cdRef = inject(ChangeDetectorRef);
53
+ _destroyRef = inject(DestroyRef);
54
+ _fitted = false;
55
+ _previousPageCount = 0;
56
+ ngOnChanges() {
57
+ this.groups = new Map((this.report?.filterGroups ?? []).map((group) => [group.id, group]));
58
+ const pageCount = this.report?.pages?.length ?? 0;
59
+ // A page was just added (the host re-fetched the report) — land on it.
60
+ if (pageCount > this._previousPageCount && this._previousPageCount > 0) {
61
+ this.activePageIndex = pageCount - 1;
62
+ }
63
+ this._previousPageCount = pageCount;
64
+ if (this.activePageIndex >= pageCount) {
65
+ this.activePageIndex = 0;
66
+ }
67
+ }
68
+ // Report heading size in px (config is points; the canvas renders at
69
+ // 96px/in, so points × 96/72). Inherited by component titles via a CSS var.
70
+ get headingSizePx() {
71
+ const points = this.report?.config?.styles?.heading?.size ?? DEFAULT_HEADING_SIZE;
72
+ return points * (PX_PER_INCH / 72);
73
+ }
74
+ ngAfterViewInit() {
75
+ // Fit the page to the viewport once on first render; after that the
76
+ // viewer's zoom choice is respected across re-renders.
77
+ setTimeout(() => this.zoomFit());
78
+ }
79
+ get isFlow() {
80
+ return this.report?.layout === 'flow';
81
+ }
82
+ get activePage() {
83
+ return this.report?.pages?.[this.activePageIndex] ?? null;
84
+ }
85
+ // Flow render order (the assembler already sorts, but a local reorder
86
+ // mid-session must hold without a re-fetch).
87
+ get flowComponents() {
88
+ return [...(this.activePage?.components ?? [])].sort((a, b) => a.order - b.order);
89
+ }
90
+ selectPage(index) {
91
+ this.activePageIndex = index;
92
+ this.selectedComponentId = null;
93
+ }
94
+ // Append a blank page; the host re-fetches the report and ngOnChanges lands
95
+ // the view on the new (last) page.
96
+ addPage() {
97
+ if (!this.report) {
98
+ return;
99
+ }
100
+ this._reportData.addPage(this.report.id)
101
+ .pipe(takeUntilDestroyed(this._destroyRef))
102
+ .subscribe(() => this.reportChanged.emit());
103
+ }
104
+ pageLabel(page, index) {
105
+ return page.name || `Page ${index + 1}`;
106
+ }
107
+ selectComponent(componentId) {
108
+ this.selectedComponentId = componentId;
109
+ this._cdRef.markForCheck();
110
+ }
111
+ onCanvasPointerDown() {
112
+ // Background click (pan start) deselects.
113
+ if (this.selectedComponentId !== null) {
114
+ this.selectComponent(null);
115
+ }
116
+ }
117
+ // ----- zoom toolbar ---------------------------------------------------------
118
+ onZoomed(scale) {
119
+ this.zoom = scale;
120
+ this._cdRef.markForCheck();
121
+ }
122
+ zoomIn() {
123
+ this.zoomPan?.zoomIn();
124
+ }
125
+ zoomOut() {
126
+ this.zoomPan?.zoomOut();
127
+ }
128
+ zoomFit() {
129
+ const viewport = this._viewport?.nativeElement;
130
+ if (!viewport || !this.report?.pageWidth || !this.zoomPan) {
131
+ return;
132
+ }
133
+ const fit = Math.min((viewport.clientWidth - 32) / (this.report.pageWidth * PX_PER_INCH), (viewport.clientHeight - 32) / (this.report.pageHeight * PX_PER_INCH));
134
+ this.zoomPan.zoom(Math.min(1.5, Math.max(0.1, fit)));
135
+ this.zoomPan.move(16, 16);
136
+ this._fitted = true;
137
+ }
138
+ zoomActual() {
139
+ this.zoomPan?.zoom(1);
140
+ }
141
+ // ----- persistence ----------------------------------------------------------
142
+ // A freeform drag/resize or a flow width resize landed — one PUT per gesture.
143
+ onPositionChanged(position) {
144
+ this._clearGuides();
145
+ this._reportData.savePositions(this.report.id, [position])
146
+ .pipe(takeUntilDestroyed(this._destroyRef))
147
+ .subscribe();
148
+ }
149
+ // A flow drag dropped: reorder the page's components around the moved one
150
+ // and persist every order in one PUT.
151
+ onFlowReorder(event) {
152
+ const page = this.activePage;
153
+ if (!page) {
154
+ return;
155
+ }
156
+ const ordered = this.flowComponents.filter((component) => component.id !== event.componentId);
157
+ const moved = page.components.find((component) => component.id === event.componentId);
158
+ if (!moved) {
159
+ return;
160
+ }
161
+ const index = this._flowDropIndex(ordered, event.clientX, event.clientY);
162
+ ordered.splice(index, 0, moved);
163
+ const positions = ordered.map((component, order) => {
164
+ component.order = order;
165
+ return { componentId: component.id, order };
166
+ });
167
+ this._cdRef.markForCheck();
168
+ this._reportData.savePositions(this.report.id, positions)
169
+ .pipe(takeUntilDestroyed(this._destroyRef))
170
+ .subscribe();
171
+ }
172
+ // Where the pointer landed among the flowed components: before the first
173
+ // component whose center the pointer is above/left of.
174
+ _flowDropIndex(ordered, clientX, clientY) {
175
+ const hosts = Array.from(this._viewport.nativeElement.querySelectorAll('app-report-component')).filter((host) => !host.classList.contains('flow-dragging'));
176
+ for (let index = 0; index < hosts.length && index < ordered.length; index++) {
177
+ const rect = hosts[index].getBoundingClientRect();
178
+ if (clientY < rect.top + rect.height / 2
179
+ || (clientY < rect.bottom && clientX < rect.left + rect.width / 2)) {
180
+ return index;
181
+ }
182
+ }
183
+ return ordered.length;
184
+ }
185
+ // ----- smart guides ---------------------------------------------------------
186
+ // Pixel feel of the snap, converted to inches at the current zoom.
187
+ get _snapThreshold() {
188
+ return 8 / (PX_PER_INCH * Math.max(0.1, this.zoom));
189
+ }
190
+ _snapMove(component, x, y) {
191
+ const { vertical, horizontal } = this._candidates(component);
192
+ const guides = [];
193
+ const snappedX = this._snapAxis([
194
+ { offset: 0, value: x },
195
+ { offset: component.w / 2, value: x + component.w / 2 },
196
+ { offset: component.w, value: x + component.w },
197
+ ], vertical);
198
+ if (snappedX !== null) {
199
+ x = snappedX.base;
200
+ guides.push({ orientation: 'v', position: snappedX.guide });
201
+ }
202
+ const snappedY = this._snapAxis([
203
+ { offset: 0, value: y },
204
+ { offset: component.h / 2, value: y + component.h / 2 },
205
+ { offset: component.h, value: y + component.h },
206
+ ], horizontal);
207
+ if (snappedY !== null) {
208
+ y = snappedY.base;
209
+ guides.push({ orientation: 'h', position: snappedY.guide });
210
+ }
211
+ this._renderGuides(guides);
212
+ return { x, y };
213
+ }
214
+ _snapResize(component, geometry, handle) {
215
+ const { vertical, horizontal } = this._candidates(component);
216
+ const guides = [];
217
+ const result = { ...geometry };
218
+ // Only the edges the handle moves participate in snapping.
219
+ if (handle.includes('e')) {
220
+ const snapped = this._nearest(geometry.x + geometry.w, vertical);
221
+ if (snapped !== null) {
222
+ result.w = snapped - geometry.x;
223
+ guides.push({ orientation: 'v', position: snapped });
224
+ }
225
+ }
226
+ if (handle.includes('w')) {
227
+ const snapped = this._nearest(geometry.x, vertical);
228
+ if (snapped !== null) {
229
+ result.w = geometry.x + geometry.w - snapped;
230
+ result.x = snapped;
231
+ guides.push({ orientation: 'v', position: snapped });
232
+ }
233
+ }
234
+ if (handle.includes('s')) {
235
+ const snapped = this._nearest(geometry.y + geometry.h, horizontal);
236
+ if (snapped !== null) {
237
+ result.h = snapped - geometry.y;
238
+ guides.push({ orientation: 'h', position: snapped });
239
+ }
240
+ }
241
+ if (handle.includes('n')) {
242
+ const snapped = this._nearest(geometry.y, horizontal);
243
+ if (snapped !== null) {
244
+ result.h = geometry.y + geometry.h - snapped;
245
+ result.y = snapped;
246
+ guides.push({ orientation: 'h', position: snapped });
247
+ }
248
+ }
249
+ this._renderGuides(guides);
250
+ return result;
251
+ }
252
+ // Alignment candidates: the page's edges + center, and every sibling's
253
+ // edges + centers.
254
+ _candidates(moving) {
255
+ const vertical = [0, this.report.pageWidth / 2, this.report.pageWidth];
256
+ const horizontal = [0, this.report.pageHeight / 2, this.report.pageHeight];
257
+ for (const component of this.activePage?.components ?? []) {
258
+ if (component.id === moving.id) {
259
+ continue;
260
+ }
261
+ vertical.push(component.x, component.x + component.w / 2, component.x + component.w);
262
+ horizontal.push(component.y, component.y + component.h / 2, component.y + component.h);
263
+ }
264
+ return { vertical, horizontal };
265
+ }
266
+ // Snap one axis: of the moving box's three lines (edge/center/edge), find
267
+ // the closest candidate within threshold and return the adjusted base
268
+ // coordinate plus the guide to draw.
269
+ _snapAxis(lines, candidates) {
270
+ let best = null;
271
+ for (const line of lines) {
272
+ for (const candidate of candidates) {
273
+ const distance = Math.abs(candidate - line.value);
274
+ if (distance <= this._snapThreshold && (!best || distance < best.distance)) {
275
+ best = { base: candidate - line.offset, guide: candidate, distance };
276
+ }
277
+ }
278
+ }
279
+ return best ? { base: best.base, guide: best.guide } : null;
280
+ }
281
+ _nearest(value, candidates) {
282
+ let best = null;
283
+ let bestDistance = this._snapThreshold;
284
+ for (const candidate of candidates) {
285
+ const distance = Math.abs(candidate - value);
286
+ if (distance <= bestDistance) {
287
+ best = candidate;
288
+ bestDistance = distance;
289
+ }
290
+ }
291
+ return best;
292
+ }
293
+ // The guides overlay is drawn imperatively — the drag loop runs outside
294
+ // Angular and repaints on every pointermove.
295
+ _renderGuides(guides) {
296
+ const host = this._guides?.nativeElement;
297
+ if (!host) {
298
+ return;
299
+ }
300
+ host.replaceChildren(...guides.map((guide) => {
301
+ const line = document.createElement('div');
302
+ line.className = `guide guide-${guide.orientation}`;
303
+ if (guide.orientation === 'v') {
304
+ line.style.left = `${guide.position * PX_PER_INCH}px`;
305
+ }
306
+ else {
307
+ line.style.top = `${guide.position * PX_PER_INCH}px`;
308
+ }
309
+ return line;
310
+ }));
311
+ }
312
+ _clearGuides() {
313
+ this._guides?.nativeElement?.replaceChildren();
314
+ }
315
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReportCanvasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
316
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ReportCanvasComponent, isStandalone: true, selector: "app-report-canvas", inputs: { report: "report", editMode: "editMode" }, outputs: { componentSettings: "componentSettings", reportChanged: "reportChanged", editDone: "editDone" }, viewQueries: [{ propertyName: "zoomPan", first: true, predicate: FsZoomPanComponent, descendants: true }, { propertyName: "_viewport", first: true, predicate: ["viewport"], descendants: true, static: true }, { propertyName: "_guides", first: true, predicate: ["guides"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"canvas\">\n <div\n #viewport\n class=\"page-viewport\"\n (pointerdown)=\"onCanvasPointerDown()\">\n <fs-zoom-pan\n [zoomMin]=\"0.1\"\n [zoomMax]=\"3\"\n (zoomed)=\"onZoomed($event)\">\n @if (activePage; as page) {\n <div\n class=\"page\"\n [class.flow]=\"isFlow\"\n [class.editing]=\"editMode\"\n [style.width.px]=\"report.pageWidth * PX\"\n [style.height.px]=\"report.pageHeight * PX\"\n [style.padding.px]=\"isFlow ? flowPadding * PX : 0\"\n [style.--canvas-zoom]=\"zoom\"\n [style.--report-heading-size.px]=\"headingSizePx\">\n @if (isFlow) {\n @for (component of flowComponents; track component.id) {\n <app-report-component\n class=\"flow-item\"\n [reportId]=\"report.id\"\n [component]=\"component\"\n [groups]=\"groups\"\n [editMode]=\"editMode\"\n [layout]=\"'flow'\"\n [zoom]=\"zoom\"\n [snapper]=\"snapper\"\n [selected]=\"component.id === selectedComponentId\"\n [style.width.%]=\"component.flowWidth\"\n [style.height.px]=\"component.autoHeight ? null : component.h * PX\"\n (selectComponent)=\"selectComponent($event)\"\n (positionChanged)=\"onPositionChanged($event)\"\n (flowReorder)=\"onFlowReorder($event)\"\n (openSettings)=\"componentSettings.emit($event)\">\n </app-report-component>\n }\n } @else {\n @for (component of page.components; track component.id) {\n <app-report-component\n [reportId]=\"report.id\"\n [component]=\"component\"\n [groups]=\"groups\"\n [editMode]=\"editMode\"\n [layout]=\"'freeform'\"\n [zoom]=\"zoom\"\n [snapper]=\"snapper\"\n [selected]=\"component.id === selectedComponentId\"\n [style.left.px]=\"component.x * PX\"\n [style.top.px]=\"component.y * PX\"\n [style.width.px]=\"component.w * PX\"\n [style.height.px]=\"component.autoHeight ? null : component.h * PX\"\n (selectComponent)=\"selectComponent($event)\"\n (positionChanged)=\"onPositionChanged($event)\"\n (openSettings)=\"componentSettings.emit($event)\">\n </app-report-component>\n }\n }\n\n <div\n #guides\n class=\"guides\">\n </div>\n\n @if (!page.components.length) {\n <div class=\"page-empty\">\n Ask the assistant to add a chart, list or KPI to this page.\n </div>\n }\n </div>\n }\n </fs-zoom-pan>\n\n @if (editMode) {\n <button\n mat-flat-button\n type=\"button\"\n color=\"primary\"\n class=\"edit-done\"\n matTooltip=\"Exit layout editing\"\n (click)=\"editDone.emit()\">\n <mat-icon>check</mat-icon>\n Done editing\n </button>\n }\n </div>\n\n <div class=\"canvas-toolbar\">\n <div class=\"page-tabs\">\n @if ((report?.pages?.length ?? 0) > 1) {\n @for (page of report.pages; track page.id; let index = $index) {\n <button\n type=\"button\"\n class=\"page-tab\"\n [class.active]=\"index === activePageIndex\"\n (click)=\"selectPage(index)\">\n {{ pageLabel(page, index) }}\n </button>\n }\n }\n <button\n type=\"button\"\n class=\"page-tab add-page\"\n matTooltip=\"Add page\"\n (click)=\"addPage()\">\n <mat-icon>add</mat-icon>\n Page\n </button>\n </div>\n\n <div class=\"zoom-controls\">\n <button\n type=\"button\"\n class=\"zoom-button\"\n matTooltip=\"Zoom out\"\n (click)=\"zoomOut()\">\n <mat-icon>remove</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"zoom-label\"\n matTooltip=\"Zoom to 100%\"\n (click)=\"zoomActual()\">\n {{ (zoom * 100).toFixed(0) }}%\n </button>\n <button\n type=\"button\"\n class=\"zoom-button\"\n matTooltip=\"Zoom in\"\n (click)=\"zoomIn()\">\n <mat-icon>add</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"zoom-button\"\n matTooltip=\"Fit page\"\n (click)=\"zoomFit()\">\n <mat-icon>fit_screen</mat-icon>\n </button>\n </div>\n </div>\n</div>\n", styles: [":host{display:flex;flex-direction:column;min-width:0;min-height:0;flex:1;margin-top:10px}.canvas{display:flex;flex-direction:column;gap:8px;flex:1;min-height:0}.canvas-toolbar{display:flex;align-items:center;justify-content:space-between;gap:8px}.canvas-toolbar .page-tabs{display:flex;gap:4px;flex-wrap:wrap}.canvas-toolbar .page-tabs .page-tab{border:1px solid #e4e7eb;border-radius:16px;background:#fff;padding:4px 14px;font-size:12px;color:#52606d;cursor:pointer}.canvas-toolbar .page-tabs .page-tab.active{background:var(--brand-primary-color, #3b82f6);border-color:var(--brand-primary-color, #3b82f6);color:#fff}.canvas-toolbar .page-tabs .page-tab.add-page{display:inline-flex;align-items:center;gap:2px;padding-left:8px;border-style:dashed;color:#52606d}.canvas-toolbar .page-tabs .page-tab.add-page mat-icon{font-size:16px;width:16px;height:16px}.canvas-toolbar .zoom-controls{display:flex;align-items:center;gap:2px;margin-left:auto;border:1px solid #e4e7eb;border-radius:18px;background:#fff;padding:2px 6px}.canvas-toolbar .zoom-controls .zoom-button{display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:#52606d;cursor:pointer}.canvas-toolbar .zoom-controls .zoom-button:hover{background:#f0f4f8}.canvas-toolbar .zoom-controls .zoom-button mat-icon{font-size:18px;width:18px;height:18px}.canvas-toolbar .zoom-controls .zoom-label{border:none;background:transparent;font-size:12px;color:#52606d;min-width:44px;cursor:pointer}.canvas-toolbar .zoom-controls .zoom-label:hover{color:#1f2933}.page-viewport{position:relative;flex:1;min-height:420px;border:1px solid #e4e7eb;border-radius:8px;background:#eef1f5;overflow:hidden}.page-viewport fs-zoom-pan{width:100%;height:100%}.edit-done{position:absolute;top:12px;right:12px;z-index:40;border-radius:999px;box-shadow:0 2px 10px #0f172a2e}.page{position:relative;background:#fff;border-radius:2px;box-shadow:0 2px 12px #0f172a1f,0 0 0 1px #0f172a0a;box-sizing:border-box}.page.flow{display:flex;flex-wrap:wrap;align-content:flex-start;align-items:flex-start}.page.editing{background-image:radial-gradient(circle,#d8dee6 1px,transparent 1px);background-size:24px 24px}.guides{position:absolute;inset:0;pointer-events:none;z-index:30}.guides ::ng-deep .guide{position:absolute;background:#2196f3}.guides ::ng-deep .guide.guide-v{top:0;bottom:0;width:calc(1.25px / var(--canvas-zoom, 1))}.guides ::ng-deep .guide.guide-h{left:0;right:0;height:calc(1.25px / var(--canvas-zoom, 1))}.page-empty{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#9aa5b1;font-size:14px;pointer-events:none}\n"], dependencies: [{ kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: FsZoomPanModule }, { kind: "component", type: i1.FsZoomPanComponent, selector: "fs-zoom-pan", inputs: ["zoomMax", "zoomMin", "zoomScale", "zoomDefault", "zoomFactor", "top", "left"], outputs: ["moved", "zoomed"] }, { kind: "component", type: ReportComponentComponent, selector: "app-report-component", inputs: ["reportId", "component", "groups", "editMode", "layout", "zoom", "snapper", "selected"], outputs: ["selectComponent", "positionChanged", "flowReorder", "openSettings"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
317
+ }
318
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReportCanvasComponent, decorators: [{
319
+ type: Component,
320
+ args: [{ selector: 'app-report-canvas', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
321
+ MatButton,
322
+ MatIcon,
323
+ MatTooltip,
324
+ FsZoomPanModule,
325
+ ReportComponentComponent,
326
+ ], template: "<div class=\"canvas\">\n <div\n #viewport\n class=\"page-viewport\"\n (pointerdown)=\"onCanvasPointerDown()\">\n <fs-zoom-pan\n [zoomMin]=\"0.1\"\n [zoomMax]=\"3\"\n (zoomed)=\"onZoomed($event)\">\n @if (activePage; as page) {\n <div\n class=\"page\"\n [class.flow]=\"isFlow\"\n [class.editing]=\"editMode\"\n [style.width.px]=\"report.pageWidth * PX\"\n [style.height.px]=\"report.pageHeight * PX\"\n [style.padding.px]=\"isFlow ? flowPadding * PX : 0\"\n [style.--canvas-zoom]=\"zoom\"\n [style.--report-heading-size.px]=\"headingSizePx\">\n @if (isFlow) {\n @for (component of flowComponents; track component.id) {\n <app-report-component\n class=\"flow-item\"\n [reportId]=\"report.id\"\n [component]=\"component\"\n [groups]=\"groups\"\n [editMode]=\"editMode\"\n [layout]=\"'flow'\"\n [zoom]=\"zoom\"\n [snapper]=\"snapper\"\n [selected]=\"component.id === selectedComponentId\"\n [style.width.%]=\"component.flowWidth\"\n [style.height.px]=\"component.autoHeight ? null : component.h * PX\"\n (selectComponent)=\"selectComponent($event)\"\n (positionChanged)=\"onPositionChanged($event)\"\n (flowReorder)=\"onFlowReorder($event)\"\n (openSettings)=\"componentSettings.emit($event)\">\n </app-report-component>\n }\n } @else {\n @for (component of page.components; track component.id) {\n <app-report-component\n [reportId]=\"report.id\"\n [component]=\"component\"\n [groups]=\"groups\"\n [editMode]=\"editMode\"\n [layout]=\"'freeform'\"\n [zoom]=\"zoom\"\n [snapper]=\"snapper\"\n [selected]=\"component.id === selectedComponentId\"\n [style.left.px]=\"component.x * PX\"\n [style.top.px]=\"component.y * PX\"\n [style.width.px]=\"component.w * PX\"\n [style.height.px]=\"component.autoHeight ? null : component.h * PX\"\n (selectComponent)=\"selectComponent($event)\"\n (positionChanged)=\"onPositionChanged($event)\"\n (openSettings)=\"componentSettings.emit($event)\">\n </app-report-component>\n }\n }\n\n <div\n #guides\n class=\"guides\">\n </div>\n\n @if (!page.components.length) {\n <div class=\"page-empty\">\n Ask the assistant to add a chart, list or KPI to this page.\n </div>\n }\n </div>\n }\n </fs-zoom-pan>\n\n @if (editMode) {\n <button\n mat-flat-button\n type=\"button\"\n color=\"primary\"\n class=\"edit-done\"\n matTooltip=\"Exit layout editing\"\n (click)=\"editDone.emit()\">\n <mat-icon>check</mat-icon>\n Done editing\n </button>\n }\n </div>\n\n <div class=\"canvas-toolbar\">\n <div class=\"page-tabs\">\n @if ((report?.pages?.length ?? 0) > 1) {\n @for (page of report.pages; track page.id; let index = $index) {\n <button\n type=\"button\"\n class=\"page-tab\"\n [class.active]=\"index === activePageIndex\"\n (click)=\"selectPage(index)\">\n {{ pageLabel(page, index) }}\n </button>\n }\n }\n <button\n type=\"button\"\n class=\"page-tab add-page\"\n matTooltip=\"Add page\"\n (click)=\"addPage()\">\n <mat-icon>add</mat-icon>\n Page\n </button>\n </div>\n\n <div class=\"zoom-controls\">\n <button\n type=\"button\"\n class=\"zoom-button\"\n matTooltip=\"Zoom out\"\n (click)=\"zoomOut()\">\n <mat-icon>remove</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"zoom-label\"\n matTooltip=\"Zoom to 100%\"\n (click)=\"zoomActual()\">\n {{ (zoom * 100).toFixed(0) }}%\n </button>\n <button\n type=\"button\"\n class=\"zoom-button\"\n matTooltip=\"Zoom in\"\n (click)=\"zoomIn()\">\n <mat-icon>add</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"zoom-button\"\n matTooltip=\"Fit page\"\n (click)=\"zoomFit()\">\n <mat-icon>fit_screen</mat-icon>\n </button>\n </div>\n </div>\n</div>\n", styles: [":host{display:flex;flex-direction:column;min-width:0;min-height:0;flex:1;margin-top:10px}.canvas{display:flex;flex-direction:column;gap:8px;flex:1;min-height:0}.canvas-toolbar{display:flex;align-items:center;justify-content:space-between;gap:8px}.canvas-toolbar .page-tabs{display:flex;gap:4px;flex-wrap:wrap}.canvas-toolbar .page-tabs .page-tab{border:1px solid #e4e7eb;border-radius:16px;background:#fff;padding:4px 14px;font-size:12px;color:#52606d;cursor:pointer}.canvas-toolbar .page-tabs .page-tab.active{background:var(--brand-primary-color, #3b82f6);border-color:var(--brand-primary-color, #3b82f6);color:#fff}.canvas-toolbar .page-tabs .page-tab.add-page{display:inline-flex;align-items:center;gap:2px;padding-left:8px;border-style:dashed;color:#52606d}.canvas-toolbar .page-tabs .page-tab.add-page mat-icon{font-size:16px;width:16px;height:16px}.canvas-toolbar .zoom-controls{display:flex;align-items:center;gap:2px;margin-left:auto;border:1px solid #e4e7eb;border-radius:18px;background:#fff;padding:2px 6px}.canvas-toolbar .zoom-controls .zoom-button{display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:#52606d;cursor:pointer}.canvas-toolbar .zoom-controls .zoom-button:hover{background:#f0f4f8}.canvas-toolbar .zoom-controls .zoom-button mat-icon{font-size:18px;width:18px;height:18px}.canvas-toolbar .zoom-controls .zoom-label{border:none;background:transparent;font-size:12px;color:#52606d;min-width:44px;cursor:pointer}.canvas-toolbar .zoom-controls .zoom-label:hover{color:#1f2933}.page-viewport{position:relative;flex:1;min-height:420px;border:1px solid #e4e7eb;border-radius:8px;background:#eef1f5;overflow:hidden}.page-viewport fs-zoom-pan{width:100%;height:100%}.edit-done{position:absolute;top:12px;right:12px;z-index:40;border-radius:999px;box-shadow:0 2px 10px #0f172a2e}.page{position:relative;background:#fff;border-radius:2px;box-shadow:0 2px 12px #0f172a1f,0 0 0 1px #0f172a0a;box-sizing:border-box}.page.flow{display:flex;flex-wrap:wrap;align-content:flex-start;align-items:flex-start}.page.editing{background-image:radial-gradient(circle,#d8dee6 1px,transparent 1px);background-size:24px 24px}.guides{position:absolute;inset:0;pointer-events:none;z-index:30}.guides ::ng-deep .guide{position:absolute;background:#2196f3}.guides ::ng-deep .guide.guide-v{top:0;bottom:0;width:calc(1.25px / var(--canvas-zoom, 1))}.guides ::ng-deep .guide.guide-h{left:0;right:0;height:calc(1.25px / var(--canvas-zoom, 1))}.page-empty{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#9aa5b1;font-size:14px;pointer-events:none}\n"] }]
327
+ }], propDecorators: { report: [{
328
+ type: Input
329
+ }], editMode: [{
330
+ type: Input
331
+ }], componentSettings: [{
332
+ type: Output
333
+ }], reportChanged: [{
334
+ type: Output
335
+ }], editDone: [{
336
+ type: Output
337
+ }], zoomPan: [{
338
+ type: ViewChild,
339
+ args: [FsZoomPanComponent]
340
+ }], _viewport: [{
341
+ type: ViewChild,
342
+ args: ['viewport', { static: true }]
343
+ }], _guides: [{
344
+ type: ViewChild,
345
+ args: ['guides', { static: false }]
346
+ }] } });
347
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVwb3J0LWNhbnZhcy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBwL3JlcG9ydHMvY29tcG9uZW50cy9yZXBvcnQtY2FudmFzL3JlcG9ydC1jYW52YXMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2NvbXBvbmVudHMvcmVwb3J0LWNhbnZhcy9yZXBvcnQtY2FudmFzLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDVSx1QkFBdUIsRUFBRSxpQkFBaUIsRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUNoRixVQUFVLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBYSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sR0FDdEUsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFaEUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNqRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFdkQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRTNFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUVwRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsWUFBWSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ2xFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGdEQUFnRCxDQUFDOzs7QUFHMUYsOEVBQThFO0FBQzlFLDhFQUE4RTtBQUM5RSxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsRUFBRSxDQUFDO0FBaUI5Qiw4RUFBOEU7QUFDOUUsbUVBQW1FO0FBQ25FLDJFQUEyRTtBQUMzRSxzREFBc0Q7QUFDdEQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSw2RUFBNkU7QUFDN0UsNkVBQTZFO0FBQzdFLDBEQUEwRDtBQWUxRCxNQUFNLE9BQU8scUJBQXFCO0lBRWhCLE1BQU0sQ0FBUztJQUNmLFFBQVEsR0FBRyxLQUFLLENBQUM7SUFFakMsMkVBQTJFO0lBQzNFLDhDQUE4QztJQUM3QixpQkFBaUIsR0FBRyxJQUFJLFlBQVksRUFBbUIsQ0FBQztJQUV6RSwrRUFBK0U7SUFDL0Usb0RBQW9EO0lBQ25DLGFBQWEsR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO0lBRTFELDhFQUE4RTtJQUM5RSxnRkFBZ0Y7SUFDL0QsUUFBUSxHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7SUFHOUMsT0FBTyxDQUFxQjtJQUczQixTQUFTLENBQTZCO0lBR3RDLE9BQU8sQ0FBNkI7SUFFNUIsRUFBRSxHQUFHLFdBQVcsQ0FBQztJQUNqQixXQUFXLEdBQUcsWUFBWSxDQUFDO0lBRXBDLGVBQWUsR0FBRyxDQUFDLENBQUM7SUFDcEIsSUFBSSxHQUFHLENBQUMsQ0FBQztJQUNULE1BQU0sR0FBRyxJQUFJLEdBQUcsRUFBNkIsQ0FBQztJQUM5QyxtQkFBbUIsR0FBa0IsSUFBSSxDQUFDO0lBRWpELHNFQUFzRTtJQUN0RCxPQUFPLEdBQWtCO1FBQ3ZDLFFBQVEsRUFBRSxDQUFDLFNBQVMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzlELFVBQVUsRUFBRSxDQUFDLFNBQVMsRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLFNBQVMsRUFBRSxRQUFRLEVBQUUsTUFBTSxDQUFDO1FBQzFGLEtBQUssRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFO0tBQ2pDLENBQUM7SUFFTSxXQUFXLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ2pDLE1BQU0sR0FBRyxNQUFNLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUNuQyxXQUFXLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ2pDLE9BQU8sR0FBRyxLQUFLLENBQUM7SUFDaEIsa0JBQWtCLEdBQUcsQ0FBQyxDQUFDO0lBRXhCLFdBQVc7UUFDaEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsWUFBWSxJQUFJLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUUzRixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQyxDQUFDO1FBRWxELHVFQUF1RTtRQUN2RSxJQUFJLFNBQVMsR0FBRyxJQUFJLENBQUMsa0JBQWtCLElBQUksSUFBSSxDQUFDLGtCQUFrQixHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3ZFLElBQUksQ0FBQyxlQUFlLEdBQUcsU0FBUyxHQUFHLENBQUMsQ0FBQztRQUN2QyxDQUFDO1FBQ0QsSUFBSSxDQUFDLGtCQUFrQixHQUFHLFNBQVMsQ0FBQztRQUVwQyxJQUFJLElBQUksQ0FBQyxlQUFlLElBQUksU0FBUyxFQUFFLENBQUM7WUFDdEMsSUFBSSxDQUFDLGVBQWUsR0FBRyxDQUFDLENBQUM7UUFDM0IsQ0FBQztJQUNILENBQUM7SUFFRCxxRUFBcUU7SUFDckUsNEVBQTRFO0lBQzVFLElBQVcsYUFBYTtRQUN0QixNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLElBQUksSUFBSSxvQkFBb0IsQ0FBQztRQUVsRixPQUFPLE1BQU0sR0FBRyxDQUFDLFdBQVcsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUNyQyxDQUFDO0lBRU0sZUFBZTtRQUNwQixvRUFBb0U7UUFDcEUsdURBQXVEO1FBQ3ZELFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQsSUFBVyxNQUFNO1FBQ2YsT0FBTyxJQUFJLENBQUMsTUFBTSxFQUFFLE1BQU0sS0FBSyxNQUFNLENBQUM7SUFDeEMsQ0FBQztJQUVELElBQVcsVUFBVTtRQUNuQixPQUFPLElBQUksQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUM1RCxDQUFDO0lBRUQsc0VBQXNFO0lBQ3RFLDZDQUE2QztJQUM3QyxJQUFXLGNBQWM7UUFDdkIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3BGLENBQUM7SUFFTSxVQUFVLENBQUMsS0FBYTtRQUM3QixJQUFJLENBQUMsZUFBZSxHQUFHLEtBQUssQ0FBQztRQUM3QixJQUFJLENBQUMsbUJBQW1CLEdBQUcsSUFBSSxDQUFDO0lBQ2xDLENBQUM7SUFFRCw0RUFBNEU7SUFDNUUsbUNBQW1DO0lBQzVCLE9BQU87UUFDWixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2pCLE9BQU87UUFDVCxDQUFDO1FBRUQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7YUFDckMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQzthQUMxQyxTQUFTLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ2hELENBQUM7SUFFTSxTQUFTLENBQUMsSUFBZ0IsRUFBRSxLQUFhO1FBQzlDLE9BQU8sSUFBSSxDQUFDLElBQUksSUFBSSxRQUFRLEtBQUssR0FBRyxDQUFDLEVBQUUsQ0FBQztJQUMxQyxDQUFDO0lBRU0sZUFBZSxDQUFDLFdBQTBCO1FBQy9DLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxXQUFXLENBQUM7UUFDdkMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUM3QixDQUFDO0lBRU0sbUJBQW1CO1FBQ3hCLDBDQUEwQztRQUMxQyxJQUFJLElBQUksQ0FBQyxtQkFBbUIsS0FBSyxJQUFJLEVBQUUsQ0FBQztZQUN0QyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzdCLENBQUM7SUFDSCxDQUFDO0lBRUQsK0VBQStFO0lBRXhFLFFBQVEsQ0FBQyxLQUFhO1FBQzNCLElBQUksQ0FBQyxJQUFJLEdBQUcsS0FBSyxDQUFDO1FBQ2xCLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDN0IsQ0FBQztJQUVNLE1BQU07UUFDWCxJQUFJLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFTSxPQUFPO1FBQ1osSUFBSSxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQztJQUMxQixDQUFDO0lBRU0sT0FBTztRQUNaLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxTQUFTLEVBQUUsYUFBYSxDQUFDO1FBQy9DLElBQUksQ0FBQyxRQUFRLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLFNBQVMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUMxRCxPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQ2xCLENBQUMsUUFBUSxDQUFDLFdBQVcsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxHQUFHLFdBQVcsQ0FBQyxFQUNuRSxDQUFDLFFBQVEsQ0FBQyxZQUFZLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVUsR0FBRyxXQUFXLENBQUMsQ0FDdEUsQ0FBQztRQUVGLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNyRCxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDMUIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7SUFDdEIsQ0FBQztJQUVNLFVBQVU7UUFDZixJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN4QixDQUFDO0lBRUQsK0VBQStFO0lBRS9FLDhFQUE4RTtJQUN2RSxpQkFBaUIsQ0FBQyxRQUEwRDtRQUNqRixJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDcEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEVBQUUsQ0FBQyxRQUFlLENBQUMsQ0FBQzthQUM5RCxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO2FBQzFDLFNBQVMsRUFBRSxDQUFDO0lBQ2pCLENBQUM7SUFFRCwwRUFBMEU7SUFDMUUsc0NBQXNDO0lBQy9CLGFBQWEsQ0FBQyxLQUFnRTtRQUNuRixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDO1FBQzdCLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNWLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQzlGLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUN0RixJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDWCxPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3pFLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUVoQyxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLEtBQUssRUFBRSxFQUFFO1lBQ2pELFNBQVMsQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1lBRXhCLE9BQU8sRUFBRSxXQUFXLEVBQUUsU0FBUyxDQUFDLEVBQUUsRUFBRSxLQUFLLEVBQUUsQ0FBQztRQUM5QyxDQUFDLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDM0IsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEVBQUUsU0FBUyxDQUFDO2FBQ3RELElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7YUFDMUMsU0FBUyxFQUFFLENBQUM7SUFDakIsQ0FBQztJQUVELHlFQUF5RTtJQUN6RSx1REFBdUQ7SUFDL0MsY0FBYyxDQUFDLE9BQTBCLEVBQUUsT0FBZSxFQUFFLE9BQWU7UUFDakYsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FDdEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsZ0JBQWdCLENBQWMsc0JBQXNCLENBQUMsQ0FDbkYsQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQztRQUU5RCxLQUFLLElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUcsS0FBSyxDQUFDLE1BQU0sSUFBSSxLQUFLLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDO1lBQzVFLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1lBQ2xELElBQUksT0FBTyxHQUFHLElBQUksQ0FBQyxHQUFHLEdBQUcsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDO21CQUNuQyxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsTUFBTSxJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDckUsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1FBQ0gsQ0FBQztRQUVELE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQztJQUN4QixDQUFDO0lBRUQsK0VBQStFO0lBRS9FLG1FQUFtRTtJQUNuRSxJQUFZLGNBQWM7UUFDeEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUVPLFNBQVMsQ0FBQyxTQUEwQixFQUFFLENBQVMsRUFBRSxDQUFTO1FBQ2hFLE1BQU0sRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM3RCxNQUFNLE1BQU0sR0FBZ0IsRUFBRSxDQUFDO1FBRS9CLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQzdCO1lBQ0UsRUFBRSxNQUFNLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUU7WUFDdkIsRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRTtZQUN2RCxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRTtTQUNoRCxFQUNELFFBQVEsQ0FDVCxDQUFDO1FBQ0YsSUFBSSxRQUFRLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDdEIsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFDbEIsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLFdBQVcsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQzlELENBQUM7UUFFRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUM3QjtZQUNFLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFO1lBQ3ZCLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUU7WUFDdkQsRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUU7U0FDaEQsRUFDRCxVQUFVLENBQ1gsQ0FBQztRQUNGLElBQUksUUFBUSxLQUFLLElBQUksRUFBRSxDQUFDO1lBQ3RCLENBQUMsR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDO1lBQ2xCLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxXQUFXLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUM5RCxDQUFDO1FBRUQsSUFBSSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUUzQixPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDO0lBQ2xCLENBQUM7SUFFTyxXQUFXLENBQ2pCLFNBQTBCLEVBQzFCLFFBQXdELEVBQ3hELE1BQWM7UUFFZCxNQUFNLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDN0QsTUFBTSxNQUFNLEdBQWdCLEVBQUUsQ0FBQztRQUMvQixNQUFNLE1BQU0sR0FBRyxFQUFFLEdBQUcsUUFBUSxFQUFFLENBQUM7UUFFL0IsMkRBQTJEO1FBQzNELElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3pCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsR0FBRyxRQUFRLENBQUMsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQ2pFLElBQUksT0FBTyxLQUFLLElBQUksRUFBRSxDQUFDO2dCQUNyQixNQUFNLENBQUMsQ0FBQyxHQUFHLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDO2dCQUNoQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsV0FBVyxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxDQUFDO1FBQ0gsQ0FBQztRQUNELElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3pCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQztZQUNwRCxJQUFJLE9BQU8sS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDckIsTUFBTSxDQUFDLENBQUMsR0FBRyxRQUFRLENBQUMsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxDQUFDLEdBQUcsT0FBTyxDQUFDO2dCQUM3QyxNQUFNLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQztnQkFDbkIsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLFdBQVcsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDdkQsQ0FBQztRQUNILENBQUM7UUFDRCxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN6QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEdBQUcsUUFBUSxDQUFDLENBQUMsRUFBRSxVQUFVLENBQUMsQ0FBQztZQUNuRSxJQUFJLE9BQU8sS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDckIsTUFBTSxDQUFDLENBQUMsR0FBRyxPQUFPLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQztnQkFDaEMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLFdBQVcsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDdkQsQ0FBQztRQUNILENBQUM7UUFDRCxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN6QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsVUFBVSxDQUFDLENBQUM7WUFDdEQsSUFBSSxPQUFPLEtBQUssSUFBSSxFQUFFLENBQUM7Z0JBQ3JCLE1BQU0sQ0FBQyxDQUFDLEdBQUcsUUFBUSxDQUFDLENBQUMsR0FBRyxRQUFRLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQztnQkFDN0MsTUFBTSxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUM7Z0JBQ25CLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxXQUFXLEVBQUUsR0FBRyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ3ZELENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUUzQixPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRUQsdUVBQXVFO0lBQ3ZFLG1CQUFtQjtJQUNYLFdBQVcsQ0FBQyxNQUF1QjtRQUN6QyxNQUFNLFFBQVEsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN2RSxNQUFNLFVBQVUsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVUsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUUzRSxLQUFLLE1BQU0sU0FBUyxJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsVUFBVSxJQUFJLEVBQUUsRUFBRSxDQUFDO1lBQzFELElBQUksU0FBUyxDQUFDLEVBQUUsS0FBSyxNQUFNLENBQUMsRUFBRSxFQUFFLENBQUM7Z0JBQy9CLFNBQVM7WUFDWCxDQUFDO1lBRUQsUUFBUSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDckYsVUFBVSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDekYsQ0FBQztRQUVELE9BQU8sRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLENBQUM7SUFDbEMsQ0FBQztJQUVELDBFQUEwRTtJQUMxRSxzRUFBc0U7SUFDdEUscUNBQXFDO0lBQzdCLFNBQVMsQ0FDZixLQUEwQyxFQUMxQyxVQUFvQjtRQUVwQixJQUFJLElBQUksR0FBNkQsSUFBSSxDQUFDO1FBRTFFLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxFQUFFLENBQUM7WUFDekIsS0FBSyxNQUFNLFNBQVMsSUFBSSxVQUFVLEVBQUUsQ0FBQztnQkFDbkMsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUNsRCxJQUFJLFFBQVEsSUFBSSxJQUFJLENBQUMsY0FBYyxJQUFJLENBQUMsQ0FBQyxJQUFJLElBQUksUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO29CQUMzRSxJQUFJLEdBQUcsRUFBRSxJQUFJLEVBQUUsU0FBUyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsQ0FBQztnQkFDdkUsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQzlELENBQUM7SUFFTyxRQUFRLENBQUMsS0FBYSxFQUFFLFVBQW9CO1FBQ2xELElBQUksSUFBSSxHQUFrQixJQUFJLENBQUM7UUFDL0IsSUFBSSxZQUFZLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQztRQUV2QyxLQUFLLE1BQU0sU0FBUyxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQ25DLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQyxDQUFDO1lBQzdDLElBQUksUUFBUSxJQUFJLFlBQVksRUFBRSxDQUFDO2dCQUM3QixJQUFJLEdBQUcsU0FBUyxDQUFDO2dCQUNqQixZQUFZLEdBQUcsUUFBUSxDQUFDO1lBQzFCLENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsd0VBQXdFO0lBQ3hFLDZDQUE2QztJQUNyQyxhQUFhLENBQUMsTUFBbUI7UUFDdkMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sRUFBRSxhQUFhLENBQUM7UUFDekMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ1YsT0FBTztRQUNULENBQUM7UUFFRCxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFO1lBQzNDLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDM0MsSUFBSSxDQUFDLFNBQVMsR0FBRyxlQUFlLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNwRCxJQUFJLEtBQUssQ0FBQyxXQUFXLEtBQUssR0FBRyxFQUFFLENBQUM7Z0JBQzlCLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxHQUFHLEdBQUcsS0FBSyxDQUFDLFFBQVEsR0FBRyxXQUFXLElBQUksQ0FBQztZQUN4RCxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLEdBQUcsR0FBRyxLQUFLLENBQUMsUUFBUSxHQUFHLFdBQVcsSUFBSSxDQUFDO1lBQ3ZELENBQUM7WUFFRCxPQUFPLElBQUksQ0FBQztRQUNkLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDTixDQUFDO0lBRU8sWUFBWTtRQUNsQixJQUFJLENBQUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxlQUFlLEVBQUUsQ0FBQztJQUNqRCxDQUFDO3dHQTdYVSxxQkFBcUI7NEZBQXJCLHFCQUFxQixxUkFpQnJCLGtCQUFrQiwrUEM3RS9CLG9wSkFnSkEseW9GRDNGSSxTQUFTLGlMQUNULE9BQU8sMklBQ1AsVUFBVSxnUkFDVixlQUFlLG1PQUNmLHdCQUF3Qjs7NEZBR2YscUJBQXFCO2tCQWRqQyxTQUFTOytCQUNFLG1CQUFtQixtQkFHWix1QkFBdUIsQ0FBQyxNQUFNLGNBQ25DLElBQUksV0FDUDt3QkFDUCxTQUFTO3dCQUNULE9BQU87d0JBQ1AsVUFBVTt3QkFDVixlQUFlO3dCQUNmLHdCQUF3QjtxQkFDekI7OEJBSWUsTUFBTTtzQkFBckIsS0FBSztnQkFDVSxRQUFRO3NCQUF2QixLQUFLO2dCQUlXLGlCQUFpQjtzQkFBakMsTUFBTTtnQkFJVSxhQUFhO3NCQUE3QixNQUFNO2dCQUlVLFFBQVE7c0JBQXhCLE1BQU07Z0JBR0EsT0FBTztzQkFEYixTQUFTO3VCQUFDLGtCQUFrQjtnQkFJckIsU0FBUztzQkFEaEIsU0FBUzt1QkFBQyxVQUFVLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO2dCQUkvQixPQUFPO3NCQURkLFNBQVM7dUJBQUMsUUFBUSxFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEFmdGVyVmlld0luaXQsIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LCBDaGFuZ2VEZXRlY3RvclJlZiwgQ29tcG9uZW50LCBEZXN0cm95UmVmLFxuICBFbGVtZW50UmVmLCBFdmVudEVtaXR0ZXIsIElucHV0LCBPbkNoYW5nZXMsIE91dHB1dCwgVmlld0NoaWxkLCBpbmplY3QsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgdGFrZVVudGlsRGVzdHJveWVkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZS9yeGpzLWludGVyb3AnO1xuXG5pbXBvcnQgeyBNYXRCdXR0b24gfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9idXR0b24nO1xuaW1wb3J0IHsgTWF0SWNvbiB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2ljb24nO1xuaW1wb3J0IHsgTWF0VG9vbHRpcCB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xuXG5pbXBvcnQgeyBGc1pvb21QYW5Db21wb25lbnQsIEZzWm9vbVBhbk1vZHVsZSB9IGZyb20gJ0BmaXJlc3RpdGNoL3pvb20tcGFuJztcblxuaW1wb3J0IHsgUmVwb3J0RGF0YSB9IGZyb20gJy4uLy4uL2RhdGEvcmVwb3J0LmRhdGEnO1xuaW1wb3J0IHsgUmVwb3J0LCBSZXBvcnRDb21wb25lbnQsIFJlcG9ydEZpbHRlckdyb3VwLCBSZXBvcnRQYWdlIH0gZnJvbSAnLi4vLi4vaW50ZXJmYWNlcy9yZXBvcnQuaW50ZXJmYWNlJztcbmltcG9ydCB7IERFRkFVTFRfSEVBRElOR19TSVpFLCBGTE9XX1BBRERJTkcgfSBmcm9tICcuLi8uLi9sYXlvdXQnO1xuaW1wb3J0IHsgUmVwb3J0Q29tcG9uZW50Q29tcG9uZW50IH0gZnJvbSAnLi4vcmVwb3J0LWNvbXBvbmVudC9yZXBvcnQtY29tcG9uZW50LmNvbXBvbmVudCc7XG5cblxuLy8gU2NyZWVuIGRlbnNpdHkgb2YgdGhlIGNhbnZhczogOTYgQ1NTIHB4IHBlciBpbmNoICh0aGUgQ1NTIHJlZmVyZW5jZSBwaXhlbCksXG4vLyBiZWZvcmUgem9vbS4gQWxsIGdlb21ldHJ5IHN0YXlzIGluIGluY2hlczsgb25seSByZW5kZXJpbmcgbXVsdGlwbGllcyBieSBpdC5cbmV4cG9ydCBjb25zdCBQWF9QRVJfSU5DSCA9IDk2O1xuXG4vLyBPbmUgc21hcnQgZ3VpZGUgbGluZSwgaW4gcGFnZSBpbmNoZXMuXG5leHBvcnQgaW50ZXJmYWNlIEd1aWRlTGluZSB7XG4gIG9yaWVudGF0aW9uOiAndicgfCAnaCc7XG4gIHBvc2l0aW9uOiBudW1iZXI7XG59XG5cbi8vIFRoZSBzbmFwcGluZyBjb250cmFjdCBoYW5kZWQgdG8gZWFjaCBjb21wb25lbnQncyBkcmFnIGxvb3AuIEltcGxlbWVudGVkIGJ5XG4vLyB0aGUgY2FudmFzIGJlY2F1c2Ugb25seSBpdCBzZWVzIGV2ZXJ5IGNvbXBvbmVudCAoYWxpZ25tZW50IGNhbmRpZGF0ZXMpIGFuZFxuLy8gb3ducyB0aGUgZ3VpZGVzIG92ZXJsYXkuXG5leHBvcnQgaW50ZXJmYWNlIENhbnZhc1NuYXBwZXIge1xuICBzbmFwTW92ZShjb21wb25lbnQ6IFJlcG9ydENvbXBvbmVudCwgeDogbnVtYmVyLCB5OiBudW1iZXIpOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyIH07XG4gIHNuYXBSZXNpemUoY29tcG9uZW50OiBSZXBvcnRDb21wb25lbnQsIGdlb21ldHJ5OiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB3OiBudW1iZXI7IGg6IG51bWJlciB9LCBoYW5kbGU6IHN0cmluZyk6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHc6IG51bWJlcjsgaDogbnVtYmVyIH07XG4gIGNsZWFyKCk6IHZvaWQ7XG59XG5cbi8vIFRoZSBmaXhlZC1zaXplIHBhZ2UgY2FudmFzIOKAlCB0aGUgUG93ZXJQb2ludCBtb2RlbC4gUGFnZXMgYXJlIHRhYnMgKG9ubHkgdGhlXG4vLyBBQ1RJVkUgcGFnZSdzIGNvbXBvbmVudHMgZXhpc3Qg4oaSIG9ubHkgdGhleSBmZXRjaCk7IHRoZSBwYWdlIGlzIGFcbi8vIHBhZ2VXaWR0aMOXcGFnZUhlaWdodCBJTkNIRVMgc2hlZXQgcmVuZGVyZWQgYXQgOTZweC9pbiBpbnNpZGUgZnMtem9vbS1wYW5cbi8vICh3aGVlbCB6b29tLCBiYWNrZ3JvdW5kIGRyYWcgcGFuLCB0b29sYmFyIGJ1dHRvbnMpLlxuLy9cbi8vIEZyZWVmb3JtIGxheW91dDogY29tcG9uZW50cyBhcmUgYWJzb2x1dGVseSBwb3NpdGlvbmVkOyBkcmFnZ2luZyBnZXRzIHNtYXJ0XG4vLyBhbGlnbm1lbnQgZ3VpZGVzIChlZGdlcyArIGNlbnRlcnMgb2Ygc2libGluZ3MgYW5kIHRoZSBwYWdlKSB3aXRoIHNuYXBwaW5nLlxuLy8gRmxvdyBsYXlvdXQ6IGNvbXBvbmVudHMgZmxvdyBpbnRvIHJvd3MgKGZsb3dXaWR0aCAlIG9mIHRoZSBjb250ZW50IHdpZHRoKSxcbi8vIGRyYWcgcmVvcmRlcnMsIGVhc3Qvc291dGggaGFuZGxlcyByZXNpemUgd2lkdGglL2hlaWdodC5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2FwcC1yZXBvcnQtY2FudmFzJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3JlcG9ydC1jYW52YXMuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9yZXBvcnQtY2FudmFzLmNvbXBvbmVudC5zY3NzJ10sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgTWF0QnV0dG9uLFxuICAgIE1hdEljb24sXG4gICAgTWF0VG9vbHRpcCxcbiAgICBGc1pvb21QYW5Nb2R1bGUsXG4gICAgUmVwb3J0Q29tcG9uZW50Q29tcG9uZW50LFxuICBdLFxufSlcbmV4cG9ydCBjbGFzcyBSZXBvcnRDYW52YXNDb21wb25lbnQgaW1wbGVtZW50cyBPbkNoYW5nZXMsIEFmdGVyVmlld0luaXQge1xuXG4gIEBJbnB1dCgpIHB1YmxpYyByZXBvcnQ6IFJlcG9ydDtcbiAgQElucHV0KCkgcHVibGljIGVkaXRNb2RlID0gZmFsc2U7XG5cbiAgLy8gQSBjb21wb25lbnQgYXNrZWQgZm9yIGl0cyBzZXR0aW5ncyBkaWFsb2cg4oCUIHRoZSBwYWdlLWxldmVsIGhvc3Qgb3BlbnMgaXRcbiAgLy8gKGl0IG93bnMgTWF0RGlhbG9nICsgdGhlIHBvc3Qtc2F2ZSByZWxvYWQpLlxuICBAT3V0cHV0KCkgcHVibGljIGNvbXBvbmVudFNldHRpbmdzID0gbmV3IEV2ZW50RW1pdHRlcjxSZXBvcnRDb21wb25lbnQ+KCk7XG5cbiAgLy8gVGhlIHJlcG9ydCdzIHN0cnVjdHVyZSBjaGFuZ2VkIGhlcmUgKGEgcGFnZSB3YXMgYWRkZWQpIOKAlCB0aGUgaG9zdCByZS1mZXRjaGVzXG4gIC8vIGl0IChpdCBvd25zIHRoZSBhc3NlbWJsZWQgcmVwb3J0ICsgZmlsdGVyIHN0YXRlKS5cbiAgQE91dHB1dCgpIHB1YmxpYyByZXBvcnRDaGFuZ2VkID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuXG4gIC8vIFRoZSB2aWV3ZXIgZGlzbWlzc2VkIGVkaXQgbW9kZSBmcm9tIHRoZSBvbi1jYW52YXMgYmFkZ2Ug4oCUIHRoZSBob3N0IG93bnMgdGhlXG4gIC8vIGVkaXRNb2RlIGZsYWcsIHNvIGl0IGZsaXBzIGl0IG9mZiAobWlycm9ycyB0aGUgbWVudSdzIFwiRG9uZSBlZGl0aW5nIGxheW91dFwiKS5cbiAgQE91dHB1dCgpIHB1YmxpYyBlZGl0RG9uZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcblxuICBAVmlld0NoaWxkKEZzWm9vbVBhbkNvbXBvbmVudClcbiAgcHVibGljIHpvb21QYW46IEZzWm9vbVBhbkNvbXBvbmVudDtcblxuICBAVmlld0NoaWxkKCd2aWV3cG9ydCcsIHsgc3RhdGljOiB0cnVlIH0pXG4gIHByaXZhdGUgX3ZpZXdwb3J0OiBFbGVtZW50UmVmPEhUTUxEaXZFbGVtZW50PjtcblxuICBAVmlld0NoaWxkKCdndWlkZXMnLCB7IHN0YXRpYzogZmFsc2UgfSlcbiAgcHJpdmF0ZSBfZ3VpZGVzOiBFbGVtZW50UmVmPEhUTUxEaXZFbGVtZW50PjtcblxuICBwdWJsaWMgcmVhZG9ubHkgUFggPSBQWF9QRVJfSU5DSDtcbiAgcHVibGljIHJlYWRvbmx5IGZsb3dQYWRkaW5nID0gRkxPV19QQURESU5HO1xuXG4gIHB1YmxpYyBhY3RpdmVQYWdlSW5kZXggPSAwO1xuICBwdWJsaWMgem9vbSA9IDE7XG4gIHB1YmxpYyBncm91cHMgPSBuZXcgTWFwPG51bWJlciwgUmVwb3J0RmlsdGVyR3JvdXA+KCk7XG4gIHB1YmxpYyBzZWxlY3RlZENvbXBvbmVudElkOiBudW1iZXIgfCBudWxsID0gbnVsbDtcblxuICAvLyBUaGUgc25hcHBlciBoYW5kZWQgdG8gZXZlcnkgY29tcG9uZW50IChzdGFibGUgaWRlbnRpdHkgZm9yIE9uUHVzaCkuXG4gIHB1YmxpYyByZWFkb25seSBzbmFwcGVyOiBDYW52YXNTbmFwcGVyID0ge1xuICAgIHNuYXBNb3ZlOiAoY29tcG9uZW50LCB4LCB5KSA9PiB0aGlzLl9zbmFwTW92ZShjb21wb25lbnQsIHgsIHkpLFxuICAgIHNuYXBSZXNpemU6IChjb21wb25lbnQsIGdlb21ldHJ5LCBoYW5kbGUpID0+IHRoaXMuX3NuYXBSZXNpemUoY29tcG9uZW50LCBnZW9tZXRyeSwgaGFuZGxlKSxcbiAgICBjbGVhcjogKCkgPT4gdGhpcy5fY2xlYXJHdWlkZXMoKSxcbiAgfTtcblxuICBwcml2YXRlIF9yZXBvcnREYXRhID0gaW5qZWN0KFJlcG9ydERhdGEpO1xuICBwcml2YXRlIF9jZFJlZiA9IGluamVjdChDaGFuZ2VEZXRlY3RvclJlZik7XG4gIHByaXZhdGUgX2Rlc3Ryb3lSZWYgPSBpbmplY3QoRGVzdHJveVJlZik7XG4gIHByaXZhdGUgX2ZpdHRlZCA9IGZhbHNlO1xuICBwcml2YXRlIF9wcmV2aW91c1BhZ2VDb3VudCA9IDA7XG5cbiAgcHVibGljIG5nT25DaGFuZ2VzKCk6IHZvaWQge1xuICAgIHRoaXMuZ3JvdXBzID0gbmV3IE1hcCgodGhpcy5yZXBvcnQ/LmZpbHRlckdyb3VwcyA/PyBbXSkubWFwKChncm91cCkgPT4gW2dyb3VwLmlkLCBncm91cF0pKTtcblxuICAgIGNvbnN0IHBhZ2VDb3VudCA9IHRoaXMucmVwb3J0Py5wYWdlcz8ubGVuZ3RoID8/IDA7XG5cbiAgICAvLyBBIHBhZ2Ugd2FzIGp1c3QgYWRkZWQgKHRoZSBob3N0IHJlLWZldGNoZWQgdGhlIHJlcG9ydCkg4oCUIGxhbmQgb24gaXQuXG4gICAgaWYgKHBhZ2VDb3VudCA+IHRoaXMuX3ByZXZpb3VzUGFnZUNvdW50ICYmIHRoaXMuX3ByZXZpb3VzUGFnZUNvdW50ID4gMCkge1xuICAgICAgdGhpcy5hY3RpdmVQYWdlSW5kZXggPSBwYWdlQ291bnQgLSAxO1xuICAgIH1cbiAgICB0aGlzLl9wcmV2aW91c1BhZ2VDb3VudCA9IHBhZ2VDb3VudDtcblxuICAgIGlmICh0aGlzLmFjdGl2ZVBhZ2VJbmRleCA+PSBwYWdlQ291bnQpIHtcbiAgICAgIHRoaXMuYWN0aXZlUGFnZUluZGV4ID0gMDtcbiAgICB9XG4gIH1cblxuICAvLyBSZXBvcnQgaGVhZGluZyBzaXplIGluIHB4IChjb25maWcgaXMgcG9pbnRzOyB0aGUgY2FudmFzIHJlbmRlcnMgYXRcbiAgLy8gOTZweC9pbiwgc28gcG9pbnRzIMOXIDk2LzcyKS4gSW5oZXJpdGVkIGJ5IGNvbXBvbmVudCB0aXRsZXMgdmlhIGEgQ1NTIHZhci5cbiAgcHVibGljIGdldCBoZWFkaW5nU2l6ZVB4KCk6IG51bWJlciB7XG4gICAgY29uc3QgcG9pbnRzID0gdGhpcy5yZXBvcnQ/LmNvbmZpZz8uc3R5bGVzPy5oZWFkaW5nPy5zaXplID8/IERFRkFVTFRfSEVBRElOR19TSVpFO1xuXG4gICAgcmV0dXJuIHBvaW50cyAqIChQWF9QRVJfSU5DSCAvIDcyKTtcbiAgfVxuXG4gIHB1YmxpYyBuZ0FmdGVyVmlld0luaXQoKTogdm9pZCB7XG4gICAgLy8gRml0IHRoZSBwYWdlIHRvIHRoZSB2aWV3cG9ydCBvbmNlIG9uIGZpcnN0IHJlbmRlcjsgYWZ0ZXIgdGhhdCB0aGVcbiAgICAvLyB2aWV3ZXIncyB6b29tIGNob2ljZSBpcyByZXNwZWN0ZWQgYWNyb3NzIHJlLXJlbmRlcnMuXG4gICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLnpvb21GaXQoKSk7XG4gIH1cblxuICBwdWJsaWMgZ2V0IGlzRmxvdygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5yZXBvcnQ/LmxheW91dCA9PT0gJ2Zsb3cnO1xuICB9XG5cbiAgcHVibGljIGdldCBhY3RpdmVQYWdlKCk6IFJlcG9ydFBhZ2UgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5yZXBvcnQ/LnBhZ2VzPy5bdGhpcy5hY3RpdmVQYWdlSW5kZXhdID8/IG51bGw7XG4gIH1cblxuICAvLyBGbG93IHJlbmRlciBvcmRlciAodGhlIGFzc2VtYmxlciBhbHJlYWR5IHNvcnRzLCBidXQgYSBsb2NhbCByZW9yZGVyXG4gIC8vIG1pZC1zZXNzaW9uIG11c3QgaG9sZCB3aXRob3V0IGEgcmUtZmV0Y2gpLlxuICBwdWJsaWMgZ2V0IGZsb3dDb21wb25lbnRzKCk6IFJlcG9ydENvbXBvbmVudFtdIHtcbiAgICByZXR1cm4gWy4uLih0aGlzLmFjdGl2ZVBhZ2U/LmNvbXBvbmVudHMgPz8gW10pXS5zb3J0KChhLCBiKSA9PiBhLm9yZGVyIC0gYi5vcmRlcik7XG4gIH1cblxuICBwdWJsaWMgc2VsZWN0UGFnZShpbmRleDogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5hY3RpdmVQYWdlSW5kZXggPSBpbmRleDtcbiAgICB0aGlzLnNlbGVjdGVkQ29tcG9uZW50SWQgPSBudWxsO1xuICB9XG5cbiAgLy8gQXBwZW5kIGEgYmxhbmsgcGFnZTsgdGhlIGhvc3QgcmUtZmV0Y2hlcyB0aGUgcmVwb3J0IGFuZCBuZ09uQ2hhbmdlcyBsYW5kc1xuICAvLyB0aGUgdmlldyBvbiB0aGUgbmV3IChsYXN0KSBwYWdlLlxuICBwdWJsaWMgYWRkUGFnZSgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMucmVwb3J0KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5fcmVwb3J0RGF0YS5hZGRQYWdlKHRoaXMucmVwb3J0LmlkKVxuICAgICAgLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuX2Rlc3Ryb3lSZWYpKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLnJlcG9ydENoYW5nZWQuZW1pdCgpKTtcbiAgfVxuXG4gIHB1YmxpYyBwYWdlTGFiZWwocGFnZTogUmVwb3J0UGFnZSwgaW5kZXg6IG51bWJlcik6IHN0cmluZyB7XG4gICAgcmV0dXJuIHBhZ2UubmFtZSB8fCBgUGFnZSAke2luZGV4ICsgMX1gO1xuICB9XG5cbiAgcHVibGljIHNlbGVjdENvbXBvbmVudChjb21wb25lbnRJZDogbnVtYmVyIHwgbnVsbCk6IHZvaWQge1xuICAgIHRoaXMuc2VsZWN0ZWRDb21wb25lbnRJZCA9IGNvbXBvbmVudElkO1xuICAgIHRoaXMuX2NkUmVmLm1hcmtGb3JDaGVjaygpO1xuICB9XG5cbiAgcHVibGljIG9uQ2FudmFzUG9pbnRlckRvd24oKTogdm9pZCB7XG4gICAgLy8gQmFja2dyb3VuZCBjbGljayAocGFuIHN0YXJ0KSBkZXNlbGVjdHMuXG4gICAgaWYgKHRoaXMuc2VsZWN0ZWRDb21wb25lbnRJZCAhPT0gbnVsbCkge1xuICAgICAgdGhpcy5zZWxlY3RDb21wb25lbnQobnVsbCk7XG4gICAgfVxuICB9XG5cbiAgLy8gLS0tLS0gem9vbSB0b29sYmFyIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIHB1YmxpYyBvblpvb21lZChzY2FsZTogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy56b29tID0gc2NhbGU7XG4gICAgdGhpcy5fY2RSZWYubWFya0ZvckNoZWNrKCk7XG4gIH1cblxuICBwdWJsaWMgem9vbUluKCk6IHZvaWQge1xuICAgIHRoaXMuem9vbVBhbj8uem9vbUluKCk7XG4gIH1cblxuICBwdWJsaWMgem9vbU91dCgpOiB2b2lkIHtcbiAgICB0aGlzLnpvb21QYW4/Lnpvb21PdXQoKTtcbiAgfVxuXG4gIHB1YmxpYyB6b29tRml0KCk6IHZvaWQge1xuICAgIGNvbnN0IHZpZXdwb3J0ID0gdGhpcy5fdmlld3BvcnQ/Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgaWYgKCF2aWV3cG9ydCB8fCAhdGhpcy5yZXBvcnQ/LnBhZ2VXaWR0aCB8fCAhdGhpcy56b29tUGFuKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgZml0ID0gTWF0aC5taW4oXG4gICAgICAodmlld3BvcnQuY2xpZW50V2lkdGggLSAzMikgLyAodGhpcy5yZXBvcnQucGFnZVdpZHRoICogUFhfUEVSX0lOQ0gpLFxuICAgICAgKHZpZXdwb3J0LmNsaWVudEhlaWdodCAtIDMyKSAvICh0aGlzLnJlcG9ydC5wYWdlSGVpZ2h0ICogUFhfUEVSX0lOQ0gpLFxuICAgICk7XG5cbiAgICB0aGlzLnpvb21QYW4uem9vbShNYXRoLm1pbigxLjUsIE1hdGgubWF4KDAuMSwgZml0KSkpO1xuICAgIHRoaXMuem9vbVBhbi5tb3ZlKDE2LCAxNik7XG4gICAgdGhpcy5fZml0dGVkID0gdHJ1ZTtcbiAgfVxuXG4gIHB1YmxpYyB6b29tQWN0dWFsKCk6IHZvaWQge1xuICAgIHRoaXMuem9vbVBhbj8uem9vbSgxKTtcbiAgfVxuXG4gIC8vIC0tLS0tIHBlcnNpc3RlbmNlIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuICAvLyBBIGZyZWVmb3JtIGRyYWcvcmVzaXplIG9yIGEgZmxvdyB3aWR0aCByZXNpemUgbGFuZGVkIOKAlCBvbmUgUFVUIHBlciBnZXN0dXJlLlxuICBwdWJsaWMgb25Qb3NpdGlvbkNoYW5nZWQocG9zaXRpb246IHsgY29tcG9uZW50SWQ6IG51bWJlciB9ICYgUmVjb3JkPHN0cmluZywgbnVtYmVyPik6IHZvaWQge1xuICAgIHRoaXMuX2NsZWFyR3VpZGVzKCk7XG4gICAgdGhpcy5fcmVwb3J0RGF0YS5zYXZlUG9zaXRpb25zKHRoaXMucmVwb3J0LmlkLCBbcG9zaXRpb24gYXMgYW55XSlcbiAgICAgIC5waXBlKHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSlcbiAgICAgIC5zdWJzY3JpYmUoKTtcbiAgfVxuXG4gIC8vIEEgZmxvdyBkcmFnIGRyb3BwZWQ6IHJlb3JkZXIgdGhlIHBhZ2UncyBjb21wb25lbnRzIGFyb3VuZCB0aGUgbW92ZWQgb25lXG4gIC8vIGFuZCBwZXJzaXN0IGV2ZXJ5IG9yZGVyIGluIG9uZSBQVVQuXG4gIHB1YmxpYyBvbkZsb3dSZW9yZGVyKGV2ZW50OiB7IGNvbXBvbmVudElkOiBudW1iZXI7IGNsaWVudFg6IG51bWJlcjsgY2xpZW50WTogbnVtYmVyIH0pOiB2b2lkIHtcbiAgICBjb25zdCBwYWdlID0gdGhpcy5hY3RpdmVQYWdlO1xuICAgIGlmICghcGFnZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IG9yZGVyZWQgPSB0aGlzLmZsb3dDb21wb25lbnRzLmZpbHRlcigoY29tcG9uZW50KSA9PiBjb21wb25lbnQuaWQgIT09IGV2ZW50LmNvbXBvbmVudElkKTtcbiAgICBjb25zdCBtb3ZlZCA9IHBhZ2UuY29tcG9uZW50cy5maW5kKChjb21wb25lbnQpID0+IGNvbXBvbmVudC5pZCA9PT0gZXZlbnQuY29tcG9uZW50SWQpO1xuICAgIGlmICghbW92ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBpbmRleCA9IHRoaXMuX2Zsb3dEcm9wSW5kZXgob3JkZXJlZCwgZXZlbnQuY2xpZW50WCwgZXZlbnQuY2xpZW50WSk7XG4gICAgb3JkZXJlZC5zcGxpY2UoaW5kZXgsIDAsIG1vdmVkKTtcblxuICAgIGNvbnN0IHBvc2l0aW9ucyA9IG9yZGVyZWQubWFwKChjb21wb25lbnQsIG9yZGVyKSA9PiB7XG4gICAgICBjb21wb25lbnQub3JkZXIgPSBvcmRlcjtcblxuICAgICAgcmV0dXJuIHsgY29tcG9uZW50SWQ6IGNvbXBvbmVudC5pZCwgb3JkZXIgfTtcbiAgICB9KTtcblxuICAgIHRoaXMuX2NkUmVmLm1hcmtGb3JDaGVjaygpO1xuICAgIHRoaXMuX3JlcG9ydERhdGEuc2F2ZVBvc2l0aW9ucyh0aGlzLnJlcG9ydC5pZCwgcG9zaXRpb25zKVxuICAgICAgLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuX2Rlc3Ryb3lSZWYpKVxuICAgICAgLnN1YnNjcmliZSgpO1xuICB9XG5cbiAgLy8gV2hlcmUgdGhlIHBvaW50ZXIgbGFuZGVkIGFtb25nIHRoZSBmbG93ZWQgY29tcG9uZW50czogYmVmb3JlIHRoZSBmaXJzdFxuICAvLyBjb21wb25lbnQgd2hvc2UgY2VudGVyIHRoZSBwb2ludGVyIGlzIGFib3ZlL2xlZnQgb2YuXG4gIHByaXZhdGUgX2Zsb3dEcm9wSW5kZXgob3JkZXJlZDogUmVwb3J0Q29tcG9uZW50W10sIGNsaWVudFg6IG51bWJlciwgY2xpZW50WTogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBjb25zdCBob3N0cyA9IEFycmF5LmZyb20oXG4gICAgICB0aGlzLl92aWV3cG9ydC5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGw8SFRNTEVsZW1lbnQ+KCdhcHAtcmVwb3J0LWNvbXBvbmVudCcpLFxuICAgICkuZmlsdGVyKChob3N0KSA9PiAhaG9zdC5jbGFzc0xpc3QuY29udGFpbnMoJ2Zsb3ctZHJhZ2dpbmcnKSk7XG5cbiAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgaG9zdHMubGVuZ3RoICYmIGluZGV4IDwgb3JkZXJlZC5sZW5ndGg7IGluZGV4KyspIHtcbiAgICAgIGNvbnN0IHJlY3QgPSBob3N0c1tpbmRleF0uZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICBpZiAoY2xpZW50WSA8IHJlY3QudG9wICsgcmVjdC5oZWlnaHQgLyAyXG4gICAgICAgIHx8IChjbGllbnRZIDwgcmVjdC5ib3R0b20gJiYgY2xpZW50WCA8IHJlY3QubGVmdCArIHJlY3Qud2lkdGggLyAyKSkge1xuICAgICAgICByZXR1cm4gaW5kZXg7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG9yZGVyZWQubGVuZ3RoO1xuICB9XG5cbiAgLy8gLS0tLS0gc21hcnQgZ3VpZGVzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIC8vIFBpeGVsIGZlZWwgb2YgdGhlIHNuYXAsIGNvbnZlcnRlZCB0byBpbmNoZXMgYXQgdGhlIGN1cnJlbnQgem9vbS5cbiAgcHJpdmF0ZSBnZXQgX3NuYXBUaHJlc2hvbGQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gOCAvIChQWF9QRVJfSU5DSCAqIE1hdGgubWF4KDAuMSwgdGhpcy56b29tKSk7XG4gIH1cblxuICBwcml2YXRlIF9zbmFwTW92ZShjb21wb25lbnQ6IFJlcG9ydENvbXBvbmVudCwgeDogbnVtYmVyLCB5OiBudW1iZXIpOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyIH0ge1xuICAgIGNvbnN0IHsgdmVydGljYWwsIGhvcml6b250YWwgfSA9IHRoaXMuX2NhbmRpZGF0ZXMoY29tcG9uZW50KTtcbiAgICBjb25zdCBndWlkZXM6IEd1aWRlTGluZVtdID0gW107XG5cbiAgICBjb25zdCBzbmFwcGVkWCA9IHRoaXMuX3NuYXBBeGlzKFxuICAgICAgW1xuICAgICAgICB7IG9mZnNldDogMCwgdmFsdWU6IHggfSxcbiAgICAgICAgeyBvZmZzZXQ6IGNvbXBvbmVudC53IC8gMiwgdmFsdWU6IHggKyBjb21wb25lbnQudyAvIDIgfSxcbiAgICAgICAgeyBvZmZzZXQ6IGNvbXBvbmVudC53LCB2YWx1ZTogeCArIGNvbXBvbmVudC53IH0sXG4gICAgICBdLFxuICAgICAgdmVydGljYWwsXG4gICAgKTtcbiAgICBpZiAoc25hcHBlZFggIT09IG51bGwpIHtcbiAgICAgIHggPSBzbmFwcGVkWC5iYXNlO1xuICAgICAgZ3VpZGVzLnB1c2goeyBvcmllbnRhdGlvbjogJ3YnLCBwb3NpdGlvbjogc25hcHBlZFguZ3VpZGUgfSk7XG4gICAgfVxuXG4gICAgY29uc3Qgc25hcHBlZFkgPSB0aGlzLl9zbmFwQXhpcyhcbiAgICAgIFtcbiAgICAgICAgeyBvZmZzZXQ6IDAsIHZhbHVlOiB5IH0sXG4gICAgICAgIHsgb2Zmc2V0OiBjb21wb25lbnQuaCAvIDIsIHZhbHVlOiB5ICsgY29tcG9uZW50LmggLyAyIH0sXG4gICAgICAgIHsgb2Zmc2V0OiBjb21wb25lbnQuaCwgdmFsdWU6IHkgKyBjb21wb25lbnQuaCB9LFxuICAgICAgXSxcbiAgICAgIGhvcml6b250YWwsXG4gICAgKTtcbiAgICBpZiAoc25hcHBlZFkgIT09IG51bGwpIHtcbiAgICAgIHkgPSBzbmFwcGVkWS5iYXNlO1xuICAgICAgZ3VpZGVzLnB1c2goeyBvcmllbnRhdGlvbjogJ2gnLCBwb3NpdGlvbjogc25hcHBlZFkuZ3VpZGUgfSk7XG4gICAgfVxuXG4gICAgdGhpcy5fcmVuZGVyR3VpZGVzKGd1aWRlcyk7XG5cbiAgICByZXR1cm4geyB4LCB5IH07XG4gIH1cblxuICBwcml2YXRlIF9zbmFwUmVzaXplKFxuICAgIGNvbXBvbmVudDogUmVwb3J0Q29tcG9uZW50LFxuICAgIGdlb21ldHJ5OiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB3OiBudW1iZXI7IGg6IG51bWJlciB9LFxuICAgIGhhbmRsZTogc3RyaW5nLFxuICApOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB3OiBudW1iZXI7IGg6IG51bWJlciB9IHtcbiAgICBjb25zdCB7IHZlcnRpY2FsLCBob3Jpem9udGFsIH0gPSB0aGlzLl9jYW5kaWRhdGVzKGNvbXBvbmVudCk7XG4gICAgY29uc3QgZ3VpZGVzOiBHdWlkZUxpbmVbXSA9IFtdO1xuICAgIGNvbnN0IHJlc3VsdCA9IHsgLi4uZ2VvbWV0cnkgfTtcblxuICAgIC8vIE9ubHkgdGhlIGVkZ2VzIHRoZSBoYW5kbGUgbW92ZXMgcGFydGljaXBhdGUgaW4gc25hcHBpbmcuXG4gICAgaWYgKGhhbmRsZS5pbmNsdWRlcygnZScpKSB7XG4gICAgICBjb25zdCBzbmFwcGVkID0gdGhpcy5fbmVhcmVzdChnZW9tZXRyeS54ICsgZ2VvbWV0cnkudywgdmVydGljYWwpO1xuICAgICAgaWYgKHNuYXBwZWQgIT09IG51bGwpIHtcbiAgICAgICAgcmVzdWx0LncgPSBzbmFwcGVkIC0gZ2VvbWV0cnkueDtcbiAgICAgICAgZ3VpZGVzLnB1c2goeyBvcmllbnRhdGlvbjogJ3YnLCBwb3NpdGlvbjogc25hcHBlZCB9KTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGhhbmRsZS5pbmNsdWRlcygndycpKSB7XG4gICAgICBjb25zdCBzbmFwcGVkID0gdGhpcy5fbmVhcmVzdChnZW9tZXRyeS54LCB2ZXJ0aWNhbCk7XG4gICAgICBpZiAoc25hcHBlZCAhPT0gbnVsbCkge1xuICAgICAgICByZXN1bHQudyA9IGdlb21ldHJ5LnggKyBnZW9tZXRyeS53IC0gc25hcHBlZDtcbiAgICAgICAgcmVzdWx0LnggPSBzbmFwcGVkO1xuICAgICAgICBndWlkZXMucHVzaCh7IG9yaWVudGF0aW9uOiAndicsIHBvc2l0aW9uOiBzbmFwcGVkIH0pO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoaGFuZGxlLmluY2x1ZGVzKCdzJykpIHtcbiAgICAgIGNvbnN0IHNuYXBwZWQgPSB0aGlzLl9uZWFyZXN0KGdlb21ldHJ5LnkgKyBnZW9tZXRyeS5oLCBob3Jpem9udGFsKTtcbiAgICAgIGlmIChzbmFwcGVkICE9PSBudWxsKSB7XG4gICAgICAgIHJlc3VsdC5oID0gc25hcHBlZCAtIGdlb21ldHJ5Lnk7XG4gICAgICAgIGd1aWRlcy5wdXNoKHsgb3JpZW50YXRpb246ICdoJywgcG9zaXRpb246IHNuYXBwZWQgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChoYW5kbGUuaW5jbHVkZXMoJ24nKSkge1xuICAgICAgY29uc3Qgc25hcHBlZCA9IHRoaXMuX25lYXJlc3QoZ2VvbWV0cnkueSwgaG9yaXpvbnRhbCk7XG4gICAgICBpZiAoc25hcHBlZCAhPT0gbnVsbCkge1xuICAgICAgICByZXN1bHQuaCA9IGdlb21ldHJ5LnkgKyBnZW9tZXRyeS5oIC0gc25hcHBlZDtcbiAgICAgICAgcmVzdWx0LnkgPSBzbmFwcGVkO1xuICAgICAgICBndWlkZXMucHVzaCh7IG9yaWVudGF0aW9uOiAnaCcsIHBvc2l0aW9uOiBzbmFwcGVkIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMuX3JlbmRlckd1aWRlcyhndWlkZXMpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIC8vIEFsaWdubWVudCBjYW5kaWRhdGVzOiB0aGUgcGFnZSdzIGVkZ2VzICsgY2VudGVyLCBhbmQgZXZlcnkgc2libGluZydzXG4gIC8vIGVkZ2VzICsgY2VudGVycy5cbiAgcHJpdmF0ZSBfY2FuZGlkYXRlcyhtb3Zpbmc6IFJlcG9ydENvbXBvbmVudCk6IHsgdmVydGljYWw6IG51bWJlcltdOyBob3Jpem9udGFsOiBudW1iZXJbXSB9IHtcbiAgICBjb25zdCB2ZXJ0aWNhbCA9IFswLCB0aGlzLnJlcG9ydC5wYWdlV2lkdGggLyAyLCB0aGlzLnJlcG9ydC5wYWdlV2lkdGhdO1xuICAgIGNvbnN0IGhvcml6b250YWwgPSBbMCwgdGhpcy5yZXBvcnQucGFnZUhlaWdodCAvIDIsIHRoaXMucmVwb3J0LnBhZ2VIZWlnaHRdO1xuXG4gICAgZm9yIChjb25zdCBjb21wb25lbnQgb2YgdGhpcy5hY3RpdmVQYWdlPy5jb21wb25lbnRzID8/IFtdKSB7XG4gICAgICBpZiAoY29tcG9uZW50LmlkID09PSBtb3ZpbmcuaWQpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIHZlcnRpY2FsLnB1c2goY29tcG9uZW50LngsIGNvbXBvbmVudC54ICsgY29tcG9uZW50LncgLyAyLCBjb21wb25lbnQueCArIGNvbXBvbmVudC53KTtcbiAgICAgIGhvcml6b250YWwucHVzaChjb21wb25lbnQueSwgY29tcG9uZW50LnkgKyBjb21wb25lbnQuaCAvIDIsIGNvbXBvbmVudC55ICsgY29tcG9uZW50LmgpO1xuICAgIH1cblxuICAgIHJldHVybiB7IHZlcnRpY2FsLCBob3Jpem9udGFsIH07XG4gIH1cblxuICAvLyBTbmFwIG9uZSBheGlzOiBvZiB0aGUgbW92aW5nIGJveCdzIHRocmVlIGxpbmVzIChlZGdlL2NlbnRlci9lZGdlKSwgZmluZFxuICAvLyB0aGUgY2xvc2VzdCBjYW5kaWRhdGUgd2l0aGluIHRocmVzaG9sZCBhbmQgcmV0dXJuIHRoZSBhZGp1c3RlZCBiYXNlXG4gIC8vIGNvb3JkaW5hdGUgcGx1cyB0aGUgZ3VpZGUgdG8gZHJhdy5cbiAgcHJpdmF0ZSBfc25hcEF4aXMoXG4gICAgbGluZXM6IHsgb2Zmc2V0OiBudW1iZXI7IHZhbHVlOiBudW1iZXIgfVtdLFxuICAgIGNhbmRpZGF0ZXM6IG51bWJlcltdLFxuICApOiB7IGJhc2U6IG51bWJlcjsgZ3VpZGU6IG51bWJlciB9IHwgbnVsbCB7XG4gICAgbGV0IGJlc3Q6IHsgYmFzZTogbnVtYmVyOyBndWlkZTogbnVtYmVyOyBkaXN0YW5jZTogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcblxuICAgIGZvciAoY29uc3QgbGluZSBvZiBsaW5lcykge1xuICAgICAgZm9yIChjb25zdCBjYW5kaWRhdGUgb2YgY2FuZGlkYXRlcykge1xuICAgICAgICBjb25zdCBkaXN0YW5jZSA9IE1hdGguYWJzKGNhbmRpZGF0ZSAtIGxpbmUudmFsdWUpO1xuICAgICAgICBpZiAoZGlzdGFuY2UgPD0gdGhpcy5fc25hcFRocmVzaG9sZCAmJiAoIWJlc3QgfHwgZGlzdGFuY2UgPCBiZXN0LmRpc3RhbmNlKSkge1xuICAgICAgICAgIGJlc3QgPSB7IGJhc2U6IGNhbmRpZGF0ZSAtIGxpbmUub2Zmc2V0LCBndWlkZTogY2FuZGlkYXRlLCBkaXN0YW5jZSB9O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGJlc3QgPyB7IGJhc2U6IGJlc3QuYmFzZSwgZ3VpZGU6IGJlc3QuZ3VpZGUgfSA6IG51bGw7XG4gIH1cblxuICBwcml2YXRlIF9uZWFyZXN0KHZhbHVlOiBudW1iZXIsIGNhbmRpZGF0ZXM6IG51bWJlcltdKTogbnVtYmVyIHwgbnVsbCB7XG4gICAgbGV0IGJlc3Q6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICAgIGxldCBiZXN0RGlzdGFuY2UgPSB0aGlzLl9zbmFwVGhyZXNob2xkO1xuXG4gICAgZm9yIChjb25zdCBjYW5kaWRhdGUgb2YgY2FuZGlkYXRlcykge1xuICAgICAgY29uc3QgZGlzdGFuY2UgPSBNYXRoLmFicyhjYW5kaWRhdGUgLSB2YWx1ZSk7XG4gICAgICBpZiAoZGlzdGFuY2UgPD0gYmVzdERpc3RhbmNlKSB7XG4gICAgICAgIGJlc3QgPSBjYW5kaWRhdGU7XG4gICAgICAgIGJlc3REaXN0YW5jZSA9IGRpc3RhbmNlO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBiZXN0O1xuICB9XG5cbiAgLy8gVGhlIGd1aWRlcyBvdmVybGF5IGlzIGRyYXduIGltcGVyYXRpdmVseSDigJQgdGhlIGRyYWcgbG9vcCBydW5zIG91dHNpZGVcbiAgLy8gQW5ndWxhciBhbmQgcmVwYWludHMgb24gZXZlcnkgcG9pbnRlcm1vdmUuXG4gIHByaXZhdGUgX3JlbmRlckd1aWRlcyhndWlkZXM6IEd1aWRlTGluZVtdKTogdm9pZCB7XG4gICAgY29uc3QgaG9zdCA9IHRoaXMuX2d1aWRlcz8ubmF0aXZlRWxlbWVudDtcbiAgICBpZiAoIWhvc3QpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBob3N0LnJlcGxhY2VDaGlsZHJlbiguLi5ndWlkZXMubWFwKChndWlkZSkgPT4ge1xuICAgICAgY29uc3QgbGluZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgICAgbGluZS5jbGFzc05hbWUgPSBgZ3VpZGUgZ3VpZGUtJHtndWlkZS5vcmllbnRhdGlvbn1gO1xuICAgICAgaWYgKGd1aWRlLm9yaWVudGF0aW9uID09PSAndicpIHtcbiAgICAgICAgbGluZS5zdHlsZS5sZWZ0ID0gYCR7Z3VpZGUucG9zaXRpb24gKiBQWF9QRVJfSU5DSH1weGA7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsaW5lLnN0eWxlLnRvcCA9IGAke2d1aWRlLnBvc2l0aW9uICogUFhfUEVSX0lOQ0h9cHhgO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gbGluZTtcbiAgICB9KSk7XG4gIH1cblxuICBwcml2YXRlIF9jbGVhckd1aWRlcygpOiB2b2lkIHtcbiAgICB0aGlzLl9ndWlkZXM/Lm5hdGl2ZUVsZW1lbnQ/LnJlcGxhY2VDaGlsZHJlbigpO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwiY2FudmFzXCI+XG4gIDxkaXZcbiAgICAjdmlld3BvcnRcbiAgICBjbGFzcz1cInBhZ2Utdmlld3BvcnRcIlxuICAgIChwb2ludGVyZG93bik9XCJvbkNhbnZhc1BvaW50ZXJEb3duKClcIj5cbiAgICA8ZnMtem9vbS1wYW5cbiAgICAgIFt6b29tTWluXT1cIjAuMVwiXG4gICAgICBbem9vbU1heF09XCIzXCJcbiAgICAgICh6b29tZWQpPVwib25ab29tZWQoJGV2ZW50KVwiPlxuICAgICAgQGlmIChhY3RpdmVQYWdlOyBhcyBwYWdlKSB7XG4gICAgICAgIDxkaXZcbiAgICAgICAgICBjbGFzcz1cInBhZ2VcIlxuICAgICAgICAgIFtjbGFzcy5mbG93XT1cImlzRmxvd1wiXG4gICAgICAgICAgW2NsYXNzLmVkaXRpbmddPVwiZWRpdE1vZGVcIlxuICAgICAgICAgIFtzdHlsZS53aWR0aC5weF09XCJyZXBvcnQucGFnZVdpZHRoICogUFhcIlxuICAgICAgICAgIFtzdHlsZS5oZWlnaHQucHhdPVwicmVwb3J0LnBhZ2VIZWlnaHQgKiBQWFwiXG4gICAgICAgICAgW3N0eWxlLnBhZGRpbmcucHhdPVwiaXNGbG93ID8gZmxvd1BhZGRpbmcgKiBQWCA6IDBcIlxuICAgICAgICAgIFtzdHlsZS4tLWNhbnZhcy16b29tXT1cInpvb21cIlxuICAgICAgICAgIFtzdHlsZS4tLXJlcG9ydC1oZWFkaW5nLXNpemUucHhdPVwiaGVhZGluZ1NpemVQeFwiPlxuICAgICAgICAgIEBpZiAoaXNGbG93KSB7XG4gICAgICAgICAgICBAZm9yIChjb21wb25lbnQgb2YgZmxvd0NvbXBvbmVudHM7IHRyYWNrIGNvbXBvbmVudC5pZCkge1xuICAgICAgICAgICAgICA8YXBwLXJlcG9ydC1jb21wb25lbnRcbiAgICAgICAgICAgICAgICBjbGFzcz1cImZsb3ctaXRlbVwiXG4gICAgICAgICAgICAgICAgW3JlcG9ydElkXT1cInJlcG9ydC5pZFwiXG4gICAgICAgICAgICAgICAgW2NvbXBvbmVudF09XCJjb21wb25lbnRcIlxuICAgICAgICAgICAgICAgIFtncm91cHNdPVwiZ3JvdXBzXCJcbiAgICAgICAgICAgICAgICBbZWRpdE1vZGVdPVwiZWRpdE1vZGVcIlxuICAgICAgICAgICAgICAgIFtsYXlvdXRdPVwiJ2Zsb3cnXCJcbiAgICAgICAgICAgICAgICBbem9vbV09XCJ6b29tXCJcbiAgICAgICAgICAgICAgICBbc25hcHBlcl09XCJzbmFwcGVyXCJcbiAgICAgICAgICAgICAgICBbc2VsZWN0ZWRdPVwiY29tcG9uZW50LmlkID09PSBzZWxlY3RlZENvbXBvbmVudElkXCJcbiAgICAgICAgICAgICAgICBbc3R5bGUud2lkdGguJV09XCJjb21wb25lbnQuZmxvd1dpZHRoXCJcbiAgICAgICAgICAgICAgICBbc3R5bGUuaGVpZ2h0LnB4XT1cImNvbXBvbmVudC5hdXRvSGVpZ2h0ID8gbnVsbCA6IGNvbXBvbmVudC5oICogUFhcIlxuICAgICAgICAgICAgICAgIChzZWxlY3RDb21wb25lbnQpPVwic2VsZWN0Q29tcG9uZW50KCRldmVudClcIlxuICAgICAgICAgICAgICAgIChwb3NpdGlvbkNoYW5nZWQpPVwib25Qb3NpdGlvbkNoYW5nZWQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgICAgKGZsb3dSZW9yZGVyKT1cIm9uRmxvd1Jlb3JkZXIoJGV2ZW50KVwiXG4gICAgICAgICAgICAgICAgKG9wZW5TZXR0aW5ncyk9XCJjb21wb25lbnRTZXR0aW5ncy5lbWl0KCRldmVudClcIj5cbiAgICAgICAgICAgICAgPC9hcHAtcmVwb3J0LWNvbXBvbmVudD5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgIEBmb3IgKGNvbXBvbmVudCBvZiBwYWdlLmNvbXBvbmVudHM7IHRyYWNrIGNvbXBvbmVudC5pZCkge1xuICAgICAgICAgICAgICA8YXBwLXJlcG9ydC1jb21wb25lbnRcbiAgICAgICAgICAgICAgICBbcmVwb3J0SWRdPVwicmVwb3J0LmlkXCJcbiAgICAgICAgICAgICAgICBbY29tcG9uZW50XT1cImNvbXBvbmVudFwiXG4gICAgICAgICAgICAgICAgW2dyb3Vwc109XCJncm91cHNcIlxuICAgICAgICAgICAgICAgIFtlZGl0TW9kZV09XCJlZGl0TW9kZVwiXG4gICAgICAgICAgICAgICAgW2xheW91dF09XCInZnJlZWZvcm0nXCJcbiAgICAgICAgICAgICAgICBbem9vbV09XCJ6b29tXCJcbiAgICAgICAgICAgICAgICBbc25hcHBlcl09XCJzbmFwcGVyXCJcbiAgICAgICAgICAgICAgICBbc2VsZWN0ZWRdPVwiY29tcG9uZW50LmlkID09PSBzZWxlY3RlZENvbXBvbmVudElkXCJcbiAgICAgICAgICAgICAgICBbc3R5bGUubGVmdC5weF09XCJjb21wb25lbnQueCAqIFBYXCJcbiAgICAgICAgICAgICAgICBbc3R5bGUudG9wLnB4XT1cImNvbXBvbmVudC55ICogUFhcIlxuICAgICAgICAgICAgICAgIFtzdHlsZS53aWR0aC5weF09XCJjb21wb25lbnQudyAqIFBYXCJcbiAgICAgICAgICAgICAgICBbc3R5bGUuaGVpZ2h0LnB4XT1cImNvbXBvbmVudC5hdXRvSGVpZ2h0ID8gbnVsbCA6IGNvbXBvbmVudC5oICogUFhcIlxuICAgICAgICAgICAgICAgIChzZWxlY3RDb21wb25lbnQpPVwic2VsZWN0Q29tcG9uZW50KCRldmVudClcIlxuICAgICAgICAgICAgICAgIChwb3NpdGlvbkNoYW5nZWQpPVwib25Qb3NpdGlvbkNoYW5nZWQoJGV2ZW50KVwiXG4gICAgICAgICAgICAgICAgKG9wZW5TZXR0aW5ncyk9XCJjb21wb25lbnRTZXR0aW5ncy5lbWl0KCRldmVudClcIj5cbiAgICAgICAgICAgICAgPC9hcHAtcmVwb3J0LWNvbXBvbmVudD5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAjZ3VpZGVzXG4gICAgICAgICAgICBjbGFzcz1cImd1aWRlc1wiPlxuICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgQGlmICghcGFnZS5jb21wb25lbnRzLmxlbmd0aCkge1xuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInBhZ2UtZW1wdHlcIj5cbiAgICAgICAgICAgICAgQXNrIHRoZSBhc3Npc3RhbnQgdG8gYWRkIGEgY2hhcnQsIGxpc3Qgb3IgS1BJIHRvIHRoaXMgcGFnZS5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgPC9mcy16b29tLXBhbj5cblxuICAgIEBpZiAoZWRpdE1vZGUpIHtcbiAgICAgIDxidXR0b25cbiAgICAgICAgbWF0LWZsYXQtYnV0dG9uXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICBjb2xvcj1cInByaW1hcnlcIlxuICAgICAgICBjbGFzcz1cImVkaXQtZG9uZVwiXG4gICAgICAgIG1hdFRvb2x0aXA9XCJFeGl0IGxheW91dCBlZGl0aW5nXCJcbiAgICAgICAgKGNsaWNrKT1cImVkaXREb25lLmVtaXQoKVwiPlxuICAgICAgICA8bWF0LWljb24+Y2hlY2s8L21hdC1pY29uPlxuICAgICAgICBEb25lIGVkaXRpbmdcbiAgICAgIDwvYnV0dG9uPlxuICAgIH1cbiAgPC9kaXY+XG5cbiAgPGRpdiBjbGFzcz1cImNhbnZhcy10b29sYmFyXCI+XG4gICAgPGRpdiBjbGFzcz1cInBhZ2UtdGFic1wiPlxuICAgICAgQGlmICgocmVwb3J0Py5wYWdlcz8ubGVuZ3RoID8/IDApID4gMSkge1xuICAgICAgICBAZm9yIChwYWdlIG9mIHJlcG9ydC5wYWdlczsgdHJhY2sgcGFnZS5pZDsgbGV0IGluZGV4ID0gJGluZGV4KSB7XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICBjbGFzcz1cInBhZ2UtdGFiXCJcbiAgICAgICAgICAgIFtjbGFzcy5hY3RpdmVdPVwiaW5kZXggPT09IGFjdGl2ZVBhZ2VJbmRleFwiXG4gICAgICAgICAgICAoY2xpY2spPVwic2VsZWN0UGFnZShpbmRleClcIj5cbiAgICAgICAgICAgIHt7IHBhZ2VMYWJlbChwYWdlLCBpbmRleCkgfX1cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgPGJ1dHRvblxuICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgY2xhc3M9XCJwYWdlLXRhYiBhZGQtcGFnZVwiXG4gICAgICAgIG1hdFRvb2x0aXA9XCJBZGQgcGFnZVwiXG4gICAgICAgIChjbGljayk9XCJhZGRQYWdlKClcIj5cbiAgICAgICAgPG1hdC1pY29uPmFkZDwvbWF0LWljb24+XG4gICAgICAgIFBhZ2VcbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZGl2PlxuXG4gICAgPGRpdiBjbGFzcz1cInpvb20tY29udHJvbHNcIj5cbiAgICAgIDxidXR0b25cbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwiem9vbS1idXR0b25cIlxuICAgICAgICBtYXRUb29sdGlwPVwiWm9vbSBvdXRcIlxuICAgICAgICAoY2xpY2spPVwiem9vbU91dCgpXCI+XG4gICAgICAgIDxtYXQtaWNvbj5yZW1vdmU8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICBjbGFzcz1cInpvb20tbGFiZWxcIlxuICAgICAgICBtYXRUb29sdGlwPVwiWm9vbSB0byAxMDAlXCJcbiAgICAgICAgKGNsaWNrKT1cInpvb21BY3R1YWwoKVwiPlxuICAgICAgICB7eyAoem9vbSAqIDEwMCkudG9GaXhlZCgwKSB9fSVcbiAgICAgIDwvYnV0dG9uPlxuICAgICAgPGJ1dHRvblxuICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgY2xhc3M9XCJ6b29tLWJ1dHRvblwiXG4gICAgICAgIG1hdFRvb2x0aXA9XCJab29tIGluXCJcbiAgICAgICAgKGNsaWNrKT1cInpvb21JbigpXCI+XG4gICAgICAgIDxtYXQtaWNvbj5hZGQ8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICBjbGFzcz1cInpvb20tYnV0dG9uXCJcbiAgICAgICAgbWF0VG9vbHRpcD1cIkZpdCBwYWdlXCJcbiAgICAgICAgKGNsaWNrKT1cInpvb21GaXQoKVwiPlxuICAgICAgICA8bWF0LWljb24+Zml0X3NjcmVlbjwvbWF0LWljb24+XG4gICAgICA8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG48L2Rpdj5cbiJdfQ==