@firestitch/report 18.15.0 → 18.17.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.
- package/app/reports/components/report-canvas/report-canvas.component.d.ts +7 -0
- package/app/reports/views/report/fs-ai-report.component.d.ts +1 -1
- package/esm2022/app/reports/components/report-canvas/report-canvas.component.mjs +60 -4
- package/esm2022/app/reports/views/report/fs-ai-report.component.mjs +13 -22
- package/esm2022/public_api.mjs +5 -6
- package/fesm2022/{firestitch-report-echarts-DY_CfeEH.mjs → firestitch-report-echarts-7AUAX6RO.mjs} +2 -2
- package/fesm2022/{firestitch-report-echarts-DY_CfeEH.mjs.map → firestitch-report-echarts-7AUAX6RO.mjs.map} +1 -1
- package/fesm2022/{firestitch-report-firestitch-report-YDtFbTwA.mjs → firestitch-report-firestitch-report-DgeCapUH.mjs} +16417 -16838
- package/fesm2022/firestitch-report-firestitch-report-DgeCapUH.mjs.map +1 -0
- package/fesm2022/firestitch-report.mjs +1 -1
- package/package.json +1 -1
- package/public_api.d.ts +0 -1
- package/app/reports/views/report/report.component.d.ts +0 -60
- package/esm2022/app/reports/views/report/report.component.mjs +0 -505
- package/fesm2022/firestitch-report-firestitch-report-YDtFbTwA.mjs.map +0 -1
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, DestroyRef, ElementRef, NgZone, ViewChild, effect, inject, signal, untracked, } from '@angular/core';
|
|
2
|
-
import { FormsModule } from '@angular/forms';
|
|
3
|
-
import { MatButton } from '@angular/material/button';
|
|
4
|
-
import { MatDialog } from '@angular/material/dialog';
|
|
5
|
-
import { MatIcon } from '@angular/material/icon';
|
|
6
|
-
import { MatTooltip } from '@angular/material/tooltip';
|
|
7
|
-
import { FsAutocompleteChipsModule } from '@firestitch/autocomplete-chips';
|
|
8
|
-
import { FsFilterModule } from '@firestitch/filter';
|
|
9
|
-
import { FsMenuModule } from '@firestitch/menu';
|
|
10
|
-
import { FsProcess } from '@firestitch/process';
|
|
11
|
-
import { FsPrompt } from '@firestitch/prompt';
|
|
12
|
-
import { finalize, switchMap, takeUntil } from 'rxjs/operators';
|
|
13
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
14
|
-
import { provideEchartsCore } from 'ngx-echarts';
|
|
15
|
-
import { FsAiChatComponent } from '@firestitch/ai';
|
|
16
|
-
import { ReportCanvasComponent } from '../../components/report-canvas/report-canvas.component';
|
|
17
|
-
import { ReportData } from '../../data/report.data';
|
|
18
|
-
import { ComponentSettingsComponent, } from '../../dialogs/component-settings/component-settings.component';
|
|
19
|
-
import { ReportSettingsComponent } from '../../dialogs/report-settings/report-settings.component';
|
|
20
|
-
import { ReportExportCollectorService } from '../../export/report-export-collector.service';
|
|
21
|
-
import { ReportExportService } from '../../export/report-export.service';
|
|
22
|
-
import { ReportPdfService } from '../../export/report-pdf.service';
|
|
23
|
-
import { ReportPptxService } from '../../export/report-pptx.service';
|
|
24
|
-
import { ReportTextLayoutService } from '../../export/report-text-layout.service';
|
|
25
|
-
import { provideReportDatePicker } from '../../report-date-presets';
|
|
26
|
-
import { GROUP_BY_ITEM, filterItemForGroup, groupByFilterItem, groupBySignature, groupValuesFromQuery, hasPageGroup, isGranularityOffered, matchFilterItem, matchFromQuery, reportBarGroups, } from '../../report-filter-items';
|
|
27
|
-
import { ReportFilterOptionsService } from '../../services/report-filter-options.service';
|
|
28
|
-
import { ReportFilterStateService, defaultSignature } from '../../services/report-filter-state.service';
|
|
29
|
-
import { ReportService } from '../../services/report.service';
|
|
30
|
-
import { zipExportStates } from '../../tile-data-state';
|
|
31
|
-
import * as i0 from "@angular/core";
|
|
32
|
-
import * as i1 from "@angular/forms";
|
|
33
|
-
import * as i2 from "@firestitch/autocomplete-chips";
|
|
34
|
-
import * as i3 from "@firestitch/filter";
|
|
35
|
-
import * as i4 from "@firestitch/menu";
|
|
36
|
-
// The Reports page: AI chat on the left, the report canvas on the right —
|
|
37
|
-
// a split-pane layout; the viewer is our own canvas (no iframe, no embed URLs).
|
|
38
|
-
// The chat is always scoped to the selected report; its id rides on every
|
|
39
|
-
// turn via [requestData].
|
|
40
|
-
export class ReportComponent {
|
|
41
|
-
// The single report the viewer and chat are focused on.
|
|
42
|
-
selected = signal(null);
|
|
43
|
-
report = signal(null);
|
|
44
|
-
loadingReports = signal(true);
|
|
45
|
-
editMode = signal(false);
|
|
46
|
-
// The report toolbar IS an fs-filter: the picker rides in its heading
|
|
47
|
-
// template, the menu becomes its actions, and report-level filter groups
|
|
48
|
-
// become its items. Rebuilt whenever the loaded report (and thus its groups)
|
|
49
|
-
// changes; the menu's show-guards keep working while no report is selected.
|
|
50
|
-
reportFilterConfig = signal(null);
|
|
51
|
-
_split;
|
|
52
|
-
_chatPanel;
|
|
53
|
-
// Only the canvas knows which page is on screen, and a page-scoped export
|
|
54
|
-
// has to ask it.
|
|
55
|
-
_canvas;
|
|
56
|
-
introMessage = {
|
|
57
|
-
text: '',
|
|
58
|
-
};
|
|
59
|
-
_reportData = inject(ReportData);
|
|
60
|
-
_reportService = inject(ReportService);
|
|
61
|
-
_filterState = inject(ReportFilterStateService);
|
|
62
|
-
_filterOptions = inject(ReportFilterOptionsService);
|
|
63
|
-
_exportService = inject(ReportExportService);
|
|
64
|
-
_collector = inject(ReportExportCollectorService);
|
|
65
|
-
_process = inject(FsProcess);
|
|
66
|
-
_prompt = inject(FsPrompt);
|
|
67
|
-
_dialog = inject(MatDialog);
|
|
68
|
-
_destroyRef = inject(DestroyRef);
|
|
69
|
-
_zone = inject(NgZone);
|
|
70
|
-
// The state version the bar was last built for (reportFilterKey).
|
|
71
|
-
_barVersion = signal(0);
|
|
72
|
-
// The page on screen, as the canvas last reported it: the bar shows its
|
|
73
|
-
// page groups.
|
|
74
|
-
_pageId = signal(null);
|
|
75
|
-
constructor() {
|
|
76
|
-
this._buildReportConfig();
|
|
77
|
-
// resetToDefaults() moves the state version. The bar is built again for
|
|
78
|
-
// it, and reportFilterKey, which carries it, recreates fs-filter: it reads
|
|
79
|
-
// its config once.
|
|
80
|
-
effect(() => {
|
|
81
|
-
const version = this._filterState.stateVersion();
|
|
82
|
-
untracked(() => {
|
|
83
|
-
if (version !== this._barVersion()) {
|
|
84
|
-
this._buildReportConfig();
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
this._reportService.load()
|
|
89
|
-
.pipe(finalize(() => {
|
|
90
|
-
this.loadingReports.set(false);
|
|
91
|
-
}), takeUntilDestroyed(this._destroyRef))
|
|
92
|
-
.subscribe({ error: () => undefined });
|
|
93
|
-
}
|
|
94
|
-
fetchReports = (keyword) => {
|
|
95
|
-
return this._reportService.filter(keyword);
|
|
96
|
-
};
|
|
97
|
-
reportChange(report) {
|
|
98
|
-
this.report.set(null);
|
|
99
|
-
this.editMode.set(false);
|
|
100
|
-
this._pageId.set(null);
|
|
101
|
-
this._buildReportConfig();
|
|
102
|
-
if (report) {
|
|
103
|
-
this._loadReport(report.id);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
createReport() {
|
|
107
|
-
this._prompt.input({
|
|
108
|
-
title: 'Create Report',
|
|
109
|
-
label: 'Name',
|
|
110
|
-
commitLabel: 'Create',
|
|
111
|
-
required: true,
|
|
112
|
-
})
|
|
113
|
-
.pipe(switchMap((name) => this._reportService.create(name)), takeUntilDestroyed(this._destroyRef))
|
|
114
|
-
.subscribe((report) => {
|
|
115
|
-
this.selected.set(report);
|
|
116
|
-
this._loadReport(report.id);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
toggleEditMode() {
|
|
120
|
-
this.editMode.update((editMode) => !editMode);
|
|
121
|
-
}
|
|
122
|
-
// The edit tools live in this toolbar, but the canvas owns what they act on
|
|
123
|
-
// (the active page, the header/footer bands, which layer is live).
|
|
124
|
-
get isMasterMode() {
|
|
125
|
-
return this._canvas?.masterMode() ?? false;
|
|
126
|
-
}
|
|
127
|
-
addText() {
|
|
128
|
-
this._canvas?.addText();
|
|
129
|
-
}
|
|
130
|
-
toggleMasterMode() {
|
|
131
|
-
this._canvas?.toggleMasterMode();
|
|
132
|
-
}
|
|
133
|
-
// Report settings — name, page size. The picker list and structure both
|
|
134
|
-
// reload on save (a page resize re-renders the whole canvas).
|
|
135
|
-
reportSettings() {
|
|
136
|
-
if (!this.report()) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
const dialogRef = this._dialog.open(ReportSettingsComponent, {
|
|
140
|
-
data: { report: this.report() },
|
|
141
|
-
width: '460px',
|
|
142
|
-
});
|
|
143
|
-
const settings = dialogRef.componentInstance;
|
|
144
|
-
dialogRef.afterClosed()
|
|
145
|
-
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
146
|
-
.subscribe((result) => {
|
|
147
|
-
// The dialog confirms + performs the delete itself; we just react to
|
|
148
|
-
// what it reports back.
|
|
149
|
-
if (result?.action === 'deleted') {
|
|
150
|
-
this.selected.set(null);
|
|
151
|
-
this.report.set(null);
|
|
152
|
-
this._buildReportConfig();
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
// A save that failed part way (a refused name) may still have stored
|
|
156
|
-
// the filters before it, so a dismissed dialog that wrote reloads too.
|
|
157
|
-
if ((result?.action === 'saved' || settings.wrote()) && this.selected()) {
|
|
158
|
-
this._loadReport(this.selected().id);
|
|
159
|
-
this._refreshReportName(this.selected().id);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
// Component settings — title, exact geometry, and the diagnostic views
|
|
164
|
-
// (SQL, config, filter wiring).
|
|
165
|
-
componentSettings(component) {
|
|
166
|
-
if (!this.report() || !this._canvas) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
// The dialog gets what the component's title tokens fill with where it is
|
|
170
|
-
// drawn, for the title's Insert field menu and the line saying how it reads.
|
|
171
|
-
const dialogRef = this._dialog.open(ComponentSettingsComponent, {
|
|
172
|
-
data: { report: this.report(), component, tokens: this._canvas.componentTokens(component) },
|
|
173
|
-
width: '640px',
|
|
174
|
-
});
|
|
175
|
-
// Filter add/remove/level changes persist immediately inside the dialog (no
|
|
176
|
-
// "save") and can leave it by an exit that carries no result, so they are
|
|
177
|
-
// announced as they land. The reload waits for the close so the canvas isn't
|
|
178
|
-
// re-rendering underneath an open dialog.
|
|
179
|
-
let filtersChanged = false;
|
|
180
|
-
dialogRef.componentInstance.filtersChanged
|
|
181
|
-
.pipe(takeUntil(dialogRef.afterClosed()))
|
|
182
|
-
.subscribe(() => filtersChanged = true);
|
|
183
|
-
dialogRef.afterClosed()
|
|
184
|
-
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
185
|
-
.subscribe((result) => {
|
|
186
|
-
// The dialog confirms + performs the delete itself. The component is
|
|
187
|
-
// gone from the report, so reload rather than patching it out locally —
|
|
188
|
-
// its filters went with it, which the report bar has to be rebuilt for.
|
|
189
|
-
if (result?.deleted) {
|
|
190
|
-
if (this.selected()) {
|
|
191
|
-
this._loadReport(this.selected().id);
|
|
192
|
-
}
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
// The report's filter groups changed, so the report bar and the
|
|
196
|
-
// on-component strips have to be rebuilt from a fresh report.
|
|
197
|
-
if (filtersChanged) {
|
|
198
|
-
if (this.selected()) {
|
|
199
|
-
this._loadReport(this.selected().id);
|
|
200
|
-
}
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
// Nothing else in the dialog touches the query — title and padding are
|
|
204
|
-
// presentation. Repaint the one component it was opened for and leave
|
|
205
|
-
// every other component's data, and its query, alone.
|
|
206
|
-
if (result?.settings) {
|
|
207
|
-
this._canvas?.applyComponentSettings(component.id, result.settings);
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
// The canvas moved to another page. The bar shows the page groups of the
|
|
212
|
-
// page on screen.
|
|
213
|
-
onCanvasPageChanged(pageId) {
|
|
214
|
-
this._pageId.set(pageId);
|
|
215
|
-
// A header or footer tile reads its page's groups through it.
|
|
216
|
-
this._filterState.setPage(pageId);
|
|
217
|
-
if (hasPageGroup(this.report())) {
|
|
218
|
-
this._buildReportConfig();
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
// The canvas changed the report's structure (added a page) — re-fetch so the
|
|
222
|
-
// new page renders; the canvas lands the view on it.
|
|
223
|
-
onCanvasReportChanged() {
|
|
224
|
-
if (this.selected()) {
|
|
225
|
-
this._loadReport(this.selected().id);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
// Export the report to PowerPoint, entirely client-side: every component's
|
|
229
|
-
// data is fetched fresh with the current filter state (all pages, not just
|
|
230
|
-
// the visible one), then pptxgenjs builds a deck of NATIVE editable charts —
|
|
231
|
-
// page = slide, positions 1:1. The dock shows progress until the file saves.
|
|
232
|
-
exportPowerpoint() {
|
|
233
|
-
if (!this.report()) {
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
this._process.run('Exporting PowerPoint', this._exportService.powerpoint(this.report()));
|
|
237
|
-
}
|
|
238
|
-
// Export to PDF, entirely client-side: same collection pass, charts rendered
|
|
239
|
-
// through an offscreen ECharts instance at 3× pixel ratio (pixel-identical
|
|
240
|
-
// to the screen), tables via jspdf-autotable — page = PDF page.
|
|
241
|
-
exportPdf() {
|
|
242
|
-
if (!this.report()) {
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
this._process.run('Exporting PDF', this._exportService.pdf(this.report()));
|
|
246
|
-
}
|
|
247
|
-
// The whole report as files in one action: the server writes one CSV per
|
|
248
|
-
// component, each what the screen shows (a chart or KPI as drawn, at the
|
|
249
|
-
// viewer's group-by grain; a list in full), and zips them foldered by page
|
|
250
|
-
// with an Export Notes.txt that records the filters as the bar reads them.
|
|
251
|
-
// Each file carries its tile's own state (zipExportStates): a list's sort,
|
|
252
|
-
// search and hidden columns, a chart's drill path and the groups that match
|
|
253
|
-
// 'any'; a server that does not read it uses each list's saved sort. The
|
|
254
|
-
// notes get the filter lines as the cover words them (exportLines; a This
|
|
255
|
-
// Page zip names only the groups that reach the page), and each file is
|
|
256
|
-
// named after its tile's title as the canvas header reads it (titles). All
|
|
257
|
-
// of it is read once, when the export starts (exportWording). `thisPage`
|
|
258
|
-
// narrows it to the page the canvas is showing. Unlike the PDF/PowerPoint
|
|
259
|
-
// exports the files are written server-side, so they are not capped by
|
|
260
|
-
// what a slide can hold.
|
|
261
|
-
exportData(thisPage = false, format = 'csv') {
|
|
262
|
-
const report = this.report();
|
|
263
|
-
if (!report) {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
const pageId = thisPage ? this._canvas?.activePage?.id ?? null : null;
|
|
267
|
-
const filters = this._filterState.resolveForReport(report, pageId);
|
|
268
|
-
const frequency = this._filterState.frequency();
|
|
269
|
-
const snapshot = this._collector.snapshot(report);
|
|
270
|
-
const states = zipExportStates(report, snapshot.state, pageId, (component) => this._filterState.gridSessionLayout(component));
|
|
271
|
-
this._process.download(format === 'xlsx' ? 'Exporting Excel' : 'Exporting data', this._collector.exportWording(report, snapshot, pageId)
|
|
272
|
-
.pipe(switchMap(({ lines, titles }) => this._reportData.exportReport(report.id, filters, pageId, frequency, lines, states, titles, {}, format))));
|
|
273
|
-
}
|
|
274
|
-
// React to each chat turn (fs-chat handles the request + persistence); we
|
|
275
|
-
// only care about the report side effects this context's backend flags.
|
|
276
|
-
onChatResponse(response) {
|
|
277
|
-
// The agent changed the report's structure — re-fetch it. Session filter
|
|
278
|
-
// values survive (the filter-state service keeps values for groups that
|
|
279
|
-
// still exist).
|
|
280
|
-
if (response.reportChanged && this.selected()) {
|
|
281
|
-
this._loadReport(this.selected().id);
|
|
282
|
-
}
|
|
283
|
-
// A rename changes nothing structural — refresh the picker chip label.
|
|
284
|
-
if (response.reportRenamed && this.selected()) {
|
|
285
|
-
this._refreshReportName(this.selected().id);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
// Drag the divider between the chat and canvas panes (pointer-capture loop
|
|
289
|
-
// outside Angular).
|
|
290
|
-
onResizeStart(event) {
|
|
291
|
-
event.preventDefault();
|
|
292
|
-
const handle = event.target;
|
|
293
|
-
handle.setPointerCapture(event.pointerId);
|
|
294
|
-
this._split.nativeElement.classList.add('resizing');
|
|
295
|
-
this._zone.runOutsideAngular(() => {
|
|
296
|
-
const move = (e) => this._resizeTo(e.clientX);
|
|
297
|
-
const end = () => {
|
|
298
|
-
handle.releasePointerCapture(event.pointerId);
|
|
299
|
-
handle.removeEventListener('pointermove', move);
|
|
300
|
-
handle.removeEventListener('pointerup', end);
|
|
301
|
-
handle.removeEventListener('pointercancel', end);
|
|
302
|
-
this._split.nativeElement.classList.remove('resizing');
|
|
303
|
-
};
|
|
304
|
-
handle.addEventListener('pointermove', move);
|
|
305
|
-
handle.addEventListener('pointerup', end);
|
|
306
|
-
handle.addEventListener('pointercancel', end);
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
_resizeTo(clientX) {
|
|
310
|
-
const rect = this._split.nativeElement.getBoundingClientRect();
|
|
311
|
-
if (rect.width === 0) {
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
const ratio = (clientX - rect.left) / rect.width;
|
|
315
|
-
const clamped = Math.min(0.8, Math.max(0.2, ratio));
|
|
316
|
-
this._chatPanel.nativeElement.style.flexBasis = `${clamped * 100}%`;
|
|
317
|
-
}
|
|
318
|
-
_loadReport(reportId) {
|
|
319
|
-
this._reportData.get(reportId)
|
|
320
|
-
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
321
|
-
.subscribe((report) => {
|
|
322
|
-
this.report.set(report);
|
|
323
|
-
// A report keeps no state in the URL: its filters live in the state
|
|
324
|
-
// service for as long as the report is open.
|
|
325
|
-
this._filterState.init(report);
|
|
326
|
-
this._buildReportConfig();
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
// Report-level filter groups in their saved order — the ones that render in
|
|
330
|
-
// the toolbar (component-level groups render on their components), with
|
|
331
|
-
// the page groups of the page on screen.
|
|
332
|
-
_reportGroups() {
|
|
333
|
-
return reportBarGroups(this.report(), this._pageId());
|
|
334
|
-
}
|
|
335
|
-
// Gates the toolbar fs-filter so it isn't rendered (taking space) when there
|
|
336
|
-
// is nothing to put in it — no report-level filters and no group-by control.
|
|
337
|
-
get reportHasFilters() {
|
|
338
|
-
return this._reportGroups().length > 0 || !!this._groupByItem();
|
|
339
|
-
}
|
|
340
|
-
// fs-filter reads its config only once (at init) — re-binding does nothing.
|
|
341
|
-
// The template keys the toolbar fs-filter on this signature so it's recreated
|
|
342
|
-
// (and re-reads the rebuilt config) whenever the report-level filter set
|
|
343
|
-
// changes, e.g. after the component settings dialog adds/moves a filter.
|
|
344
|
-
get reportFilterKey() {
|
|
345
|
-
return [
|
|
346
|
-
// The state version the bar was built for: an applied view, or a reset
|
|
347
|
-
// to the base, replaces every value at once.
|
|
348
|
-
this._barVersion(),
|
|
349
|
-
// The effective zone is part of it too: a zone change re-resolves relative
|
|
350
|
-
// presets, and the bar must be recreated to show the new days.
|
|
351
|
-
this._filterState.timezone(),
|
|
352
|
-
// The stored default is part of the signature: saving a new default in report
|
|
353
|
-
// settings rebuilds the config, but without a key change fs-filter keeps
|
|
354
|
-
// rendering the values it was created with until a full page reload.
|
|
355
|
-
groupBySignature(this.report()),
|
|
356
|
-
...this._reportGroups().map((group) => `${group.id}:${group.level}:${group.label}:${defaultSignature(group)}`),
|
|
357
|
-
// A page group shows on its own page only, so such a bar is recreated
|
|
358
|
-
// when the page on screen changes.
|
|
359
|
-
...(hasPageGroup(this.report()) ? [`page:${this._pageId()}`] : []),
|
|
360
|
-
].join('|');
|
|
361
|
-
}
|
|
362
|
-
// (Re)build the toolbar fs-filter config — the report-level filter items and
|
|
363
|
-
// then the group-by control. The report's actions live in the fs-menu beside
|
|
364
|
-
// the picker, not here. Only built/used once a report is loaded (the template
|
|
365
|
-
// gates it on `report`).
|
|
366
|
-
_buildReportConfig() {
|
|
367
|
-
const reportId = this.report()?.id ?? null;
|
|
368
|
-
// Drop a grain the control no longer offers — the agent can narrow the list
|
|
369
|
-
// mid-session, and an override the viewer can't see is worse than none.
|
|
370
|
-
if (this.report() && !isGranularityOffered(this.report(), this._filterState.frequency())) {
|
|
371
|
-
this._filterState.setFrequency(null);
|
|
372
|
-
}
|
|
373
|
-
const features = this.report()?.viewerFeatures ?? [];
|
|
374
|
-
const items = reportId
|
|
375
|
-
? this._reportGroups().map((group) => filterItemForGroup(group, this._filterOptions, this.report(), this._filterState.value(group.id), features))
|
|
376
|
-
: [];
|
|
377
|
-
// The group-by control leads the bar, ahead of every filter: it sets the
|
|
378
|
-
// grain the whole report is read at, so it is the first thing a viewer
|
|
379
|
-
// decides. It is not a filter group — it re-buckets rows rather than
|
|
380
|
-
// narrowing them — so it is placed here rather than ordered among them.
|
|
381
|
-
const groupByItem = this._groupByItem();
|
|
382
|
-
if (groupByItem) {
|
|
383
|
-
items.unshift(groupByItem);
|
|
384
|
-
}
|
|
385
|
-
// How the groups combine closes the bar.
|
|
386
|
-
const matchItem = this._matchItem();
|
|
387
|
-
if (matchItem) {
|
|
388
|
-
items.push(matchItem);
|
|
389
|
-
}
|
|
390
|
-
this._barVersion.set(this._filterState.stateVersion());
|
|
391
|
-
this.reportFilterConfig.set({
|
|
392
|
-
// Never touch the URL or persist filter state — report filters are
|
|
393
|
-
// session-only, owned by ReportFilterStateService.
|
|
394
|
-
queryParam: false,
|
|
395
|
-
persist: false,
|
|
396
|
-
items,
|
|
397
|
-
change: (query) => this._barChanged(query ?? {}, !!matchItem),
|
|
398
|
-
// 'Clear filters' puts the screen back on the report's authored
|
|
399
|
-
// defaults and drops the temporary layer.
|
|
400
|
-
clear: () => this._filterState.resetToDefaults(),
|
|
401
|
-
// The chip shows exactly while the screen differs from the defaults a
|
|
402
|
-
// clear returns to (C5).
|
|
403
|
-
clearVisible: () => this._filterState.differsFromDefaults(),
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
// The bar's values into the state: every group it shows, the grain and,
|
|
407
|
-
// when the bar has the control, the match.
|
|
408
|
-
_barChanged(query, hasMatch) {
|
|
409
|
-
for (const { groupId, value } of groupValuesFromQuery(query, this._reportGroups())) {
|
|
410
|
-
this._filterState.setValue(groupId, value);
|
|
411
|
-
}
|
|
412
|
-
// Not a filter group — the grain is read straight off the query.
|
|
413
|
-
this._filterState.setFrequency(query[GROUP_BY_ITEM] || null);
|
|
414
|
-
// Nor is the match; a bar without the control leaves it as it is.
|
|
415
|
-
if (hasMatch) {
|
|
416
|
-
this._filterState.setMatch(matchFromQuery(query));
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
// The report's group-by control as an fs-filter item, or null when the report
|
|
420
|
-
// has none to show. Seeded from the session state so the grain the viewer
|
|
421
|
-
// picked survives the report being re-fetched after a chat edit.
|
|
422
|
-
_groupByItem() {
|
|
423
|
-
return this.report()
|
|
424
|
-
? groupByFilterItem(this.report(), this._filterState.frequency())
|
|
425
|
-
: null;
|
|
426
|
-
}
|
|
427
|
-
// The bar's Match control, seeded with the match on screen, or null when
|
|
428
|
-
// the bar holds fewer than two groups that combine.
|
|
429
|
-
_matchItem() {
|
|
430
|
-
return matchFilterItem(this.report(), this._reportGroups(), this._pageId(), this._filterState.match());
|
|
431
|
-
}
|
|
432
|
-
_refreshReportName(reportId) {
|
|
433
|
-
this._reportService.load()
|
|
434
|
-
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
435
|
-
.subscribe((reports) => {
|
|
436
|
-
const renamed = reports.find((report) => report.id === reportId);
|
|
437
|
-
if (renamed) {
|
|
438
|
-
this.selected.set(renamed);
|
|
439
|
-
if (this.report()) {
|
|
440
|
-
this.report.set({ ...this.report(), name: renamed.name });
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReportComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
446
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ReportComponent, isStandalone: true, selector: "ng-component", providers: [
|
|
447
|
-
// Session filter state is per-open-report-page, not app-global; the export
|
|
448
|
-
// services resolve filters through it, so they live at the same level.
|
|
449
|
-
ReportFilterStateService,
|
|
450
|
-
// The bars' select lists, narrowed by the other filters; per open report.
|
|
451
|
-
ReportFilterOptionsService,
|
|
452
|
-
ReportExportCollectorService,
|
|
453
|
-
ReportPptxService,
|
|
454
|
-
ReportPdfService,
|
|
455
|
-
ReportTextLayoutService,
|
|
456
|
-
ReportExportService,
|
|
457
|
-
// Tree-shaken ECharts core + the 'report' house theme, loaded lazily with
|
|
458
|
-
// this route's chunk.
|
|
459
|
-
provideEchartsCore({ echarts: () => import('../../theme/echarts').then((module) => module.default) }),
|
|
460
|
-
// The bar's date pickers start their weeks on Monday and list the built-in
|
|
461
|
-
// presets; a preset only sets the days.
|
|
462
|
-
provideReportDatePicker(),
|
|
463
|
-
], viewQueries: [{ propertyName: "_split", first: true, predicate: ["split"], descendants: true, static: true }, { propertyName: "_chatPanel", first: true, predicate: ["chatPanel"], descendants: true, read: ElementRef, static: true }, { propertyName: "_canvas", first: true, predicate: ReportCanvasComponent, descendants: true }], ngImport: i0, template: "<div\n #split\n class=\"report fs-row.align-start\">\n <fs-ai-chat\n #chatPanel\n class=\"chat\"\n style=\"min-height: 500px;\"\n basePath=\"reports\"\n [requestData]=\"{ reportId: selected()?.id ?? null }\"\n [introMessage]=\"introMessage\"\n (response)=\"onChatResponse($event)\">\n </fs-ai-chat>\n <div\n class=\"resizer\"\n (pointerdown)=\"onResizeStart($event)\">\n </div>\n <div class=\"viewer fs-flex fs-column\">\n <div class=\"fs-row.align-center.gap-sm\">\n <fs-autocomplete-chips\n class=\"fs-flex\"\n [fetch]=\"fetchReports\"\n [(ngModel)]=\"selected\"\n [disabled]=\"loadingReports()\"\n [padless]=\"true\"\n [multiple]=\"false\"\n [fetchOnFocus]=\"true\"\n (ngModelChange)=\"reportChange($event)\"\n placeholder=\"Report\"\n name=\"report\">\n <ng-template\n fsAutocompleteChipsTemplate\n let-object=\"object\">\n {{ object.name }}\n </ng-template>\n <ng-template\n fsAutocompleteChipsStatic\n (click)=\"createReport()\">\n Create Report\n </ng-template>\n </fs-autocomplete-chips>\n @if (report() && editMode()) {\n <!-- What layout editing can add or switch to, and the way out of it \u2014\n the same outlined buttons as the fs-ai-report toolbar. -->\n <button\n mat-stroked-button\n type=\"button\"\n matTooltip=\"Add a block of text\"\n (click)=\"addText()\">\n <mat-icon>title</mat-icon>\n Add text\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n class=\"master-toggle\"\n [class.active]=\"isMasterMode\"\n [matTooltip]=\"isMasterMode ? 'Back to editing this page' : 'Edit the header and footer shown on every page'\"\n (click)=\"toggleMasterMode()\">\n <mat-icon>web_asset</mat-icon>\n Header & footer\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"toggleEditMode()\">\n <mat-icon>check</mat-icon>\n Done editing\n </button>\n }\n @if (report()) {\n <fs-menu>\n <ng-template\n fs-menu-item\n (click)=\"reportSettings()\">\n <mat-icon>\n tune\n </mat-icon>\n Report settings\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"toggleEditMode()\"\n [disabled]=\"editMode()\">\n <mat-icon>\n dashboard_customize\n </mat-icon>\n {{ editMode() ? 'Done editing layout' : 'Edit layout' }}\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPowerpoint()\">\n <mat-icon>\n slideshow\n </mat-icon>\n Export PowerPoint\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPdf()\">\n <mat-icon>\n picture_as_pdf\n </mat-icon>\n Export PDF\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportData()\">\n <mat-icon>\n table_view\n </mat-icon>\n Export Raw Data\n </ng-template>\n @if ((report()?.pages?.length ?? 0) > 1) {\n <ng-template\n fs-menu-item\n (click)=\"exportData(true)\">\n <mat-icon>\n table_view\n </mat-icon>\n Export Raw Data (This Page)\n </ng-template>\n }\n <ng-template\n fs-menu-item\n (click)=\"exportData(false, 'xlsx')\">\n <mat-icon>\n grid_on\n </mat-icon>\n Export Excel\n </ng-template>\n @if ((report()?.pages?.length ?? 0) > 1) {\n <ng-template\n fs-menu-item\n (click)=\"exportData(true, 'xlsx')\">\n <mat-icon>\n grid_on\n </mat-icon>\n Export Excel (This Page)\n </ng-template>\n }\n </fs-menu>\n }\n </div>\n @if (report()) {\n <!-- Report-level filters only (the report's actions live in the menu\n above). fs-filter reads its config once at init, so it's keyed on the\n filter signature: when the report-level filter set changes the block\n is recreated and re-reads the rebuilt config. -->\n @if (reportHasFilters) {\n @for (key of [reportFilterKey]; track key) {\n <fs-filter [config]=\"reportFilterConfig()\"></fs-filter>\n }\n }\n <app-report-canvas\n [report]=\"report()\"\n [editMode]=\"editMode()\"\n (componentSettings)=\"componentSettings($event)\"\n (reportChanged)=\"onCanvasReportChanged()\"\n (pageChanged)=\"onCanvasPageChanged($event)\">\n </app-report-canvas>\n }\n </div>\n</div>", styles: [".report{height:100%;min-height:500px}.report .chat{display:block;flex:0 0 25%;width:100%;height:100%;min-height:500px;min-width:0;border:none}.report .viewer{display:flex;flex-direction:column;min-width:0;height:100%;overflow:hidden}.report .viewer fs-filter{flex:0 0 auto;margin-top:10px;margin-bottom:0}.report .viewer ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.report .resizer{flex:0 0 11px;align-self:stretch;display:flex;justify-content:center;cursor:col-resize;touch-action:none;-webkit-user-select:none;user-select:none}.report .resizer:before{content:\"\";width:0px;background:#0000001f;transition:width .12s ease,background-color .12s ease}.report .resizer:hover:before{width:3px;background:var(--brand-primary-color)}.report.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.report.resizing .resizer:before{width:3px;background:var(--brand-primary-color)}.report.resizing .chat,.report.resizing .viewer{pointer-events:none}::ng-deep body.body-report-reports .mat-mdc-card-content{display:flex;flex-direction:column;box-sizing:border-box}::ng-deep body.body-report-reports .mat-mdc-card-content mat-tab-nav-panel{flex:1;min-height:0}::ng-deep body.body-report-reports .mat-mdc-card-content mat-tab-nav-panel router-outlet+ng-component{height:100%}.master-toggle.active{background:#eceff3}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: FsAutocompleteChipsModule }, { kind: "component", type: i2.FsAutocompleteChipsComponent, selector: "fs-autocomplete-chips", inputs: ["fetch", "appearance", "floatLabel", "readonly", "size", "label", "placeholder", "chipImage", "chipBackground", "chipColor", "chipIcon", "chipIconColor", "chipClass", "chipPadding", "shape", "hint", "allowText", "allowObject", "delay", "minPanelWidth", "maxPanelHeight", "validateText", "removable", "allowClear", "color", "background", "orderable", "padless", "initOnClick", "fetchOnFocus", "multiple", "multipleAdd", "confirm", "disabled", "groupBy", "panelWidth", "panelClass", "compareWith"], outputs: ["selected", "removed", "reordered", "clear", "panelOpened", "panelClosed"] }, { kind: "directive", type: i2.FsAutocompleteObjectDirective, selector: "[fsAutocompleteObject],[fsAutocompleteChipsTemplate]" }, { kind: "directive", type: i2.FsAutocompleteChipsStaticDirective, selector: "[fsAutocompleteChipsStatic]", inputs: ["show", "disable"], outputs: ["click", "selected"] }, { kind: "ngmodule", type: FsFilterModule }, { kind: "component", type: i3.FilterComponent, selector: "fs-filter", inputs: ["config"], outputs: ["closed", "opened", "ready"] }, { kind: "ngmodule", type: FsMenuModule }, { kind: "component", type: i4.FsMenuComponent, selector: "fs-menu", inputs: ["class", "buttonClass", "buttonType", "buttonColor"], outputs: ["opened", "closed"] }, { kind: "directive", type: i4.FsMenuItemDirective, selector: "fs-menu-group,[fs-menu-item]" }, { kind: "component", type: FsAiChatComponent, selector: "fs-ai-chat", inputs: ["basePath", "requestData", "ready", "introMessage", "showThinking"], outputs: ["response", "showThinkingChange"] }, { 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: "component", type: ReportCanvasComponent, selector: "app-report-canvas", inputs: ["report", "editMode", "pageId"], outputs: ["componentSettings", "reportChanged", "pageChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
464
|
-
}
|
|
465
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReportComponent, decorators: [{
|
|
466
|
-
type: Component,
|
|
467
|
-
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [
|
|
468
|
-
// Session filter state is per-open-report-page, not app-global; the export
|
|
469
|
-
// services resolve filters through it, so they live at the same level.
|
|
470
|
-
ReportFilterStateService,
|
|
471
|
-
// The bars' select lists, narrowed by the other filters; per open report.
|
|
472
|
-
ReportFilterOptionsService,
|
|
473
|
-
ReportExportCollectorService,
|
|
474
|
-
ReportPptxService,
|
|
475
|
-
ReportPdfService,
|
|
476
|
-
ReportTextLayoutService,
|
|
477
|
-
ReportExportService,
|
|
478
|
-
// Tree-shaken ECharts core + the 'report' house theme, loaded lazily with
|
|
479
|
-
// this route's chunk.
|
|
480
|
-
provideEchartsCore({ echarts: () => import('../../theme/echarts').then((module) => module.default) }),
|
|
481
|
-
// The bar's date pickers start their weeks on Monday and list the built-in
|
|
482
|
-
// presets; a preset only sets the days.
|
|
483
|
-
provideReportDatePicker(),
|
|
484
|
-
], imports: [
|
|
485
|
-
FormsModule,
|
|
486
|
-
FsAutocompleteChipsModule,
|
|
487
|
-
FsFilterModule,
|
|
488
|
-
FsMenuModule,
|
|
489
|
-
FsAiChatComponent,
|
|
490
|
-
MatButton,
|
|
491
|
-
MatIcon,
|
|
492
|
-
MatTooltip,
|
|
493
|
-
ReportCanvasComponent,
|
|
494
|
-
], template: "<div\n #split\n class=\"report fs-row.align-start\">\n <fs-ai-chat\n #chatPanel\n class=\"chat\"\n style=\"min-height: 500px;\"\n basePath=\"reports\"\n [requestData]=\"{ reportId: selected()?.id ?? null }\"\n [introMessage]=\"introMessage\"\n (response)=\"onChatResponse($event)\">\n </fs-ai-chat>\n <div\n class=\"resizer\"\n (pointerdown)=\"onResizeStart($event)\">\n </div>\n <div class=\"viewer fs-flex fs-column\">\n <div class=\"fs-row.align-center.gap-sm\">\n <fs-autocomplete-chips\n class=\"fs-flex\"\n [fetch]=\"fetchReports\"\n [(ngModel)]=\"selected\"\n [disabled]=\"loadingReports()\"\n [padless]=\"true\"\n [multiple]=\"false\"\n [fetchOnFocus]=\"true\"\n (ngModelChange)=\"reportChange($event)\"\n placeholder=\"Report\"\n name=\"report\">\n <ng-template\n fsAutocompleteChipsTemplate\n let-object=\"object\">\n {{ object.name }}\n </ng-template>\n <ng-template\n fsAutocompleteChipsStatic\n (click)=\"createReport()\">\n Create Report\n </ng-template>\n </fs-autocomplete-chips>\n @if (report() && editMode()) {\n <!-- What layout editing can add or switch to, and the way out of it \u2014\n the same outlined buttons as the fs-ai-report toolbar. -->\n <button\n mat-stroked-button\n type=\"button\"\n matTooltip=\"Add a block of text\"\n (click)=\"addText()\">\n <mat-icon>title</mat-icon>\n Add text\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n class=\"master-toggle\"\n [class.active]=\"isMasterMode\"\n [matTooltip]=\"isMasterMode ? 'Back to editing this page' : 'Edit the header and footer shown on every page'\"\n (click)=\"toggleMasterMode()\">\n <mat-icon>web_asset</mat-icon>\n Header & footer\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"toggleEditMode()\">\n <mat-icon>check</mat-icon>\n Done editing\n </button>\n }\n @if (report()) {\n <fs-menu>\n <ng-template\n fs-menu-item\n (click)=\"reportSettings()\">\n <mat-icon>\n tune\n </mat-icon>\n Report settings\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"toggleEditMode()\"\n [disabled]=\"editMode()\">\n <mat-icon>\n dashboard_customize\n </mat-icon>\n {{ editMode() ? 'Done editing layout' : 'Edit layout' }}\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPowerpoint()\">\n <mat-icon>\n slideshow\n </mat-icon>\n Export PowerPoint\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPdf()\">\n <mat-icon>\n picture_as_pdf\n </mat-icon>\n Export PDF\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportData()\">\n <mat-icon>\n table_view\n </mat-icon>\n Export Raw Data\n </ng-template>\n @if ((report()?.pages?.length ?? 0) > 1) {\n <ng-template\n fs-menu-item\n (click)=\"exportData(true)\">\n <mat-icon>\n table_view\n </mat-icon>\n Export Raw Data (This Page)\n </ng-template>\n }\n <ng-template\n fs-menu-item\n (click)=\"exportData(false, 'xlsx')\">\n <mat-icon>\n grid_on\n </mat-icon>\n Export Excel\n </ng-template>\n @if ((report()?.pages?.length ?? 0) > 1) {\n <ng-template\n fs-menu-item\n (click)=\"exportData(true, 'xlsx')\">\n <mat-icon>\n grid_on\n </mat-icon>\n Export Excel (This Page)\n </ng-template>\n }\n </fs-menu>\n }\n </div>\n @if (report()) {\n <!-- Report-level filters only (the report's actions live in the menu\n above). fs-filter reads its config once at init, so it's keyed on the\n filter signature: when the report-level filter set changes the block\n is recreated and re-reads the rebuilt config. -->\n @if (reportHasFilters) {\n @for (key of [reportFilterKey]; track key) {\n <fs-filter [config]=\"reportFilterConfig()\"></fs-filter>\n }\n }\n <app-report-canvas\n [report]=\"report()\"\n [editMode]=\"editMode()\"\n (componentSettings)=\"componentSettings($event)\"\n (reportChanged)=\"onCanvasReportChanged()\"\n (pageChanged)=\"onCanvasPageChanged($event)\">\n </app-report-canvas>\n }\n </div>\n</div>", styles: [".report{height:100%;min-height:500px}.report .chat{display:block;flex:0 0 25%;width:100%;height:100%;min-height:500px;min-width:0;border:none}.report .viewer{display:flex;flex-direction:column;min-width:0;height:100%;overflow:hidden}.report .viewer fs-filter{flex:0 0 auto;margin-top:10px;margin-bottom:0}.report .viewer ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.report .resizer{flex:0 0 11px;align-self:stretch;display:flex;justify-content:center;cursor:col-resize;touch-action:none;-webkit-user-select:none;user-select:none}.report .resizer:before{content:\"\";width:0px;background:#0000001f;transition:width .12s ease,background-color .12s ease}.report .resizer:hover:before{width:3px;background:var(--brand-primary-color)}.report.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.report.resizing .resizer:before{width:3px;background:var(--brand-primary-color)}.report.resizing .chat,.report.resizing .viewer{pointer-events:none}::ng-deep body.body-report-reports .mat-mdc-card-content{display:flex;flex-direction:column;box-sizing:border-box}::ng-deep body.body-report-reports .mat-mdc-card-content mat-tab-nav-panel{flex:1;min-height:0}::ng-deep body.body-report-reports .mat-mdc-card-content mat-tab-nav-panel router-outlet+ng-component{height:100%}.master-toggle.active{background:#eceff3}\n"] }]
|
|
495
|
-
}], ctorParameters: () => [], propDecorators: { _split: [{
|
|
496
|
-
type: ViewChild,
|
|
497
|
-
args: ['split', { static: true }]
|
|
498
|
-
}], _chatPanel: [{
|
|
499
|
-
type: ViewChild,
|
|
500
|
-
args: ['chatPanel', { static: true, read: ElementRef }]
|
|
501
|
-
}], _canvas: [{
|
|
502
|
-
type: ViewChild,
|
|
503
|
-
args: [ReportCanvasComponent]
|
|
504
|
-
}] } });
|
|
505
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVwb3J0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcHAvcmVwb3J0cy92aWV3cy9yZXBvcnQvcmVwb3J0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcHAvcmVwb3J0cy92aWV3cy9yZXBvcnQvcmVwb3J0LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCxzQkFBc0IsRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQzFELFVBQVUsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxTQUFTLEdBQzdFLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUU3QyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDckQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNqRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFdkQsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDM0UsT0FBTyxFQUFnQixjQUFjLEVBQXFCLE1BQU0sb0JBQW9CLENBQUM7QUFDckYsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFOUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFaEUsT0FBTyxFQUFFLGtCQUFrQixF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ZURldGVjdGlvblN0cmF0ZWd5LCBDb21wb25lbnQsXG4gIERlc3Ryb3lSZWYsIEVsZW1lbnRSZWYsIE5nWm9uZSwgVmlld0NoaWxkLCBlZmZlY3QsIGluamVjdCwgc2lnbmFsLCB1bnRyYWNrZWQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5cbmltcG9ydCB7IE1hdEJ1dHRvbiB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2J1dHRvbic7XG5pbXBvcnQgeyBNYXREaWFsb2cgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9kaWFsb2cnO1xuaW1wb3J0IHsgTWF0SWNvbiB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2ljb24nO1xuaW1wb3J0IHsgTWF0VG9vbHRpcCB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xuXG5pbXBvcnQgeyBGc0F1dG9jb21wbGV0ZUNoaXBzTW9kdWxlIH0gZnJvbSAnQGZpcmVzdGl0Y2gvYXV0b2NvbXBsZXRlLWNoaXBzJztcbmltcG9ydCB7IEZpbHRlckNvbmZpZywgRnNGaWx0ZXJNb2R1bGUsIElGaWx0ZXJDb25maWdJdGVtIH0gZnJvbSAnQGZpcmVzdGl0Y2gvZmlsdGVyJztcbmltcG9ydCB7IEZzTWVudU1vZHVsZSB9IGZyb20gJ0BmaXJlc3RpdGNoL21lbnUnO1xuaW1wb3J0IHsgRnNQcm9jZXNzIH0gZnJvbSAnQGZpcmVzdGl0Y2gvcHJvY2Vzcyc7XG5pbXBvcnQgeyBGc1Byb21wdCB9IGZyb20gJ0BmaXJlc3RpdGNoL3Byb21wdCc7XG5cbmltcG9ydCB7IGZpbmFsaXplLCBzd2l0Y2hNYXAsIHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHsgdGFrZVVudGlsRGVzdHJveWVkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZS9yeGpzLWludGVyb3AnO1xuaW1wb3J0IHsgcHJvdmlkZUVjaGFydHNDb3JlIH0gZnJvbSAnbmd4LWVjaGFydHMnO1xuXG5pbXBvcnQgeyBGc0FpQ2hhdENvbXBvbmVudCwgRnNBaUNoYXRSZXNwb25zZSB9IGZyb20gJ0BmaXJlc3RpdGNoL2FpJztcblxuaW1wb3J0IHsgUmVwb3J0Q2FudmFzQ29tcG9uZW50IH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9yZXBvcnQtY2FudmFzL3JlcG9ydC1jYW52YXMuY29tcG9uZW50JztcbmltcG9ydCB7IFJlcG9ydERhdGEgfSBmcm9tICcuLi8uLi9kYXRhL3JlcG9ydC5kYXRhJztcbmltcG9ydCB7XG4gIENvbXBvbmVudFNldHRpbmdzQ29tcG9uZW50LCBDb21wb25lbnRTZXR0aW5nc0RpYWxvZ0RhdGEsIENvbXBvbmVudFNldHRpbmdzUmVzdWx0LFxufSBmcm9tICcuLi8uLi9kaWFsb2dzL2NvbXBvbmVudC1zZXR0aW5ncy9jb21wb25lbnQtc2V0dGluZ3MuY29tcG9uZW50JztcbmltcG9ydCB7IFJlcG9ydFNldHRpbmdzQ29tcG9uZW50LCBSZXBvcnRTZXR0aW5nc1Jlc3VsdCB9IGZyb20gJy4uLy4uL2RpYWxvZ3MvcmVwb3J0LXNldHRpbmdzL3JlcG9ydC1zZXR0aW5ncy5jb21wb25lbnQnO1xuaW1wb3J0IHsgUmVwb3J0RXhwb3J0Q29sbGVjdG9yU2VydmljZSB9IGZyb20gJy4uLy4uL2V4cG9ydC9yZXBvcnQtZXhwb3J0LWNvbGxlY3Rvci5zZXJ2aWNlJztcbmltcG9ydCB7IFJlcG9ydEV4cG9ydFNlcnZpY2UgfSBmcm9tICcuLi8uLi9leHBvcnQvcmVwb3J0LWV4cG9ydC5zZXJ2aWNlJztcbmltcG9ydCB7IFJlcG9ydFBkZlNlcnZpY2UgfSBmcm9tICcuLi8uLi9leHBvcnQvcmVwb3J0LXBkZi5zZXJ2aWNlJztcbmltcG9ydCB7IFJlcG9ydFBwdHhTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vZXhwb3J0L3JlcG9ydC1wcHR4LnNlcnZpY2UnO1xuaW1wb3J0IHsgUmVwb3J0VGV4dExheW91dFNlcnZpY2UgfSBmcm9tICcuLi8uLi9leHBvcnQvcmVwb3J0LXRleHQtbGF5b3V0LnNlcnZpY2UnO1xuaW1wb3J0IHtcbiAgQ2hhcnRHcmFudWxhcml0eSwgUmVwb3J0LFxuICBSZXBvcnRDb21wb25lbnQgYXMgUmVwb3J0Q29tcG9uZW50TW9kZWwsIFJlcG9ydEZpbHRlckdyb3VwLCBSZXBvcnRMaXN0SXRlbSxcbn0gZnJvbSAnLi4vLi4vaW50ZXJmYWNlcy9yZXBvcnQuaW50ZXJmYWNlJztcbmltcG9ydCB7IFJlcG9ydEV4cG9ydEZvcm1hdCB9IGZyb20gJy4uLy4uL2ludGVyZmFjZXMvcmVwb3J0LWV4cG9ydC1mb3JtYXQudHlwZSc7XG5pbXBvcnQgeyBwcm92aWRlUmVwb3J0RGF0ZVBpY2tlciB9IGZyb20gJy4uLy4uL3JlcG9ydC1kYXRlLXByZXNldHMnO1xuaW1wb3J0IHtcbiAgR1JPVVBfQllfSVRFTSwgZmlsdGVySXRlbUZvckdyb3VwLCBncm91cEJ5RmlsdGVySXRlbSwgZ3JvdXBCeVNpZ25hdHVyZSwgZ3JvdXBWYWx1ZXNGcm9tUXVlcnksXG4gIGhhc1BhZ2VHcm91cCwgaXNHcmFudWxhcml0eU9mZmVyZWQsIG1hdGNoRmlsdGVySXRlbSwgbWF0Y2hGcm9tUXVlcnksIHJlcG9ydEJhckdyb3Vwcyxcbn0gZnJvbSAnLi4vLi4vcmVwb3J0LWZpbHRlci1pdGVtcyc7XG5pbXBvcnQgeyBSZXBvcnRGaWx0ZXJPcHRpb25zU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3JlcG9ydC1maWx0ZXItb3B0aW9ucy5zZXJ2aWNlJztcbmltcG9ydCB7IFJlcG9ydEZpbHRlclN0YXRlU2VydmljZSwgZGVmYXVsdFNpZ25hdHVyZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3JlcG9ydC1maWx0ZXItc3RhdGUuc2VydmljZSc7XG5pbXBvcnQgeyBSZXBvcnRTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvcmVwb3J0LnNlcnZpY2UnO1xuaW1wb3J0IHsgemlwRXhwb3J0U3RhdGVzIH0gZnJvbSAnLi4vLi4vdGlsZS1kYXRhLXN0YXRlJztcblxuXG4vLyBUaGUgUmVwb3J0cyBwYWdlOiBBSSBjaGF0IG9uIHRoZSBsZWZ0LCB0aGUgcmVwb3J0IGNhbnZhcyBvbiB0aGUgcmlnaHQg4oCUXG4vLyBhIHNwbGl0LXBhbmUgbGF5b3V0OyB0aGUgdmlld2VyIGlzIG91ciBvd24gY2FudmFzIChubyBpZnJhbWUsIG5vIGVtYmVkIFVSTHMpLlxuLy8gVGhlIGNoYXQgaXMgYWx3YXlzIHNjb3BlZCB0byB0aGUgc2VsZWN0ZWQgcmVwb3J0OyBpdHMgaWQgcmlkZXMgb24gZXZlcnlcbi8vIHR1cm4gdmlhIFtyZXF1ZXN0RGF0YV0uXG5AQ29tcG9uZW50KHtcbiAgdGVtcGxhdGVVcmw6ICcuL3JlcG9ydC5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3JlcG9ydC5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgc2NoZW1hczogW0NVU1RPTV9FTEVNRU5UU19TQ0hFTUFdLFxuICBwcm92aWRlcnM6IFtcbiAgICAvLyBTZXNzaW9uIGZpbHRlciBzdGF0ZSBpcyBwZXItb3Blbi1yZXBvcnQtcGFnZSwgbm90IGFwcC1nbG9iYWw7IHRoZSBleHBvcnRcbiAgICAvLyBzZXJ2aWNlcyByZXNvbHZlIGZpbHRlcnMgdGhyb3VnaCBpdCwgc28gdGhleSBsaXZlIGF0IHRoZSBzYW1lIGxldmVsLlxuICAgIFJlcG9ydEZpbHRlclN0YXRlU2VydmljZSxcbiAgICAvLyBUaGUgYmFycycgc2VsZWN0IGxpc3RzLCBuYXJyb3dlZCBieSB0aGUgb3RoZXIgZmlsdGVyczsgcGVyIG9wZW4gcmVwb3J0LlxuICAgIFJlcG9ydEZpbHRlck9wdGlvbnNTZXJ2aWNlLFxuICAgIFJlcG9ydEV4cG9ydENvbGxlY3RvclNlcnZpY2UsXG4gICAgUmVwb3J0UHB0eFNlcnZpY2UsXG4gICAgUmVwb3J0UGRmU2VydmljZSxcbiAgICBSZXBvcnRUZXh0TGF5b3V0U2VydmljZSxcbiAgICBSZXBvcnRFeHBvcnRTZXJ2aWNlLFxuICAgIC8vIFRyZWUtc2hha2VuIEVDaGFydHMgY29yZSArIHRoZSAncmVwb3J0JyBob3VzZSB0aGVtZSwgbG9hZGVkIGxhemlseSB3aXRoXG4gICAgLy8gdGhpcyByb3V0ZSdzIGNodW5rLlxuICAgIHByb3ZpZGVFY2hhcnRzQ29yZSh7IGVjaGFydHM6ICgpID0+IGltcG9ydCgnLi4vLi4vdGhlbWUvZWNoYXJ0cycpLnRoZW4oKG1vZHVsZSkgPT4gbW9kdWxlLmRlZmF1bHQpIH0pLFxuICAgIC8vIFRoZSBiYXIncyBkYXRlIHBpY2tlcnMgc3RhcnQgdGhlaXIgd2Vla3Mgb24gTW9uZGF5IGFuZCBsaXN0IHRoZSBidWlsdC1pblxuICAgIC8vIHByZXNldHM7IGEgcHJlc2V0IG9ubHkgc2V0cyB0aGUgZGF5cy5cbiAgICBwcm92aWRlUmVwb3J0RGF0ZVBpY2tlcigpLFxuICBdLFxuICBpbXBvcnRzOiBbXG4gICAgRm9ybXNNb2R1bGUsXG4gICAgRnNBdXRvY29tcGxldGVDaGlwc01vZHVsZSxcbiAgICBGc0ZpbHRlck1vZHVsZSxcbiAgICBGc01lbnVNb2R1bGUsXG4gICAgRnNBaUNoYXRDb21wb25lbnQsXG4gICAgTWF0QnV0dG9uLFxuICAgIE1hdEljb24sXG4gICAgTWF0VG9vbHRpcCxcbiAgICBSZXBvcnRDYW52YXNDb21wb25lbnQsXG4gIF0sXG59KVxuZXhwb3J0IGNsYXNzIFJlcG9ydENvbXBvbmVudCB7XG5cbiAgLy8gVGhlIHNpbmdsZSByZXBvcnQgdGhlIHZpZXdlciBhbmQgY2hhdCBhcmUgZm9jdXNlZCBvbi5cbiAgcHVibGljIHJlYWRvbmx5IHNlbGVjdGVkID0gc2lnbmFsPFJlcG9ydExpc3RJdGVtIHwgbnVsbD4obnVsbCk7XG4gIHB1YmxpYyByZWFkb25seSByZXBvcnQgPSBzaWduYWw8UmVwb3J0IHwgbnVsbD4obnVsbCk7XG4gIHB1YmxpYyByZWFkb25seSBsb2FkaW5nUmVwb3J0cyA9IHNpZ25hbCh0cnVlKTtcbiAgcHVibGljIHJlYWRvbmx5IGVkaXRNb2RlID0gc2lnbmFsKGZhbHNlKTtcblxuICAvLyBUaGUgcmVwb3J0IHRvb2xiYXIgSVMgYW4gZnMtZmlsdGVyOiB0aGUgcGlja2VyIHJpZGVzIGluIGl0cyBoZWFkaW5nXG4gIC8vIHRlbXBsYXRlLCB0aGUgbWVudSBiZWNvbWVzIGl0cyBhY3Rpb25zLCBhbmQgcmVwb3J0LWxldmVsIGZpbHRlciBncm91cHNcbiAgLy8gYmVjb21lIGl0cyBpdGVtcy4gUmVidWlsdCB3aGVuZXZlciB0aGUgbG9hZGVkIHJlcG9ydCAoYW5kIHRodXMgaXRzIGdyb3VwcylcbiAgLy8gY2hhbmdlczsgdGhlIG1lbnUncyBzaG93LWd1YXJkcyBrZWVwIHdvcmtpbmcgd2hpbGUgbm8gcmVwb3J0IGlzIHNlbGVjdGVkLlxuICBwdWJsaWMgcmVhZG9ubHkgcmVwb3J0RmlsdGVyQ29uZmlnID0gc2lnbmFsPEZpbHRlckNvbmZpZyB8IG51bGw+KG51bGwpO1xuXG4gIEBWaWV3Q2hpbGQoJ3NwbGl0JywgeyBzdGF0aWM6IHRydWUgfSlcbiAgcHJpdmF0ZSBfc3BsaXQhOiBFbGVtZW50UmVmPEhUTUxEaXZFbGVtZW50PjtcblxuICBAVmlld0NoaWxkKCdjaGF0UGFuZWwnLCB7IHN0YXRpYzogdHJ1ZSwgcmVhZDogRWxlbWVudFJlZiB9KVxuICBwcml2YXRlIF9jaGF0UGFuZWwhOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PjtcblxuICAvLyBPbmx5IHRoZSBjYW52YXMga25vd3Mgd2hpY2ggcGFnZSBpcyBvbiBzY3JlZW4sIGFuZCBhIHBhZ2Utc2NvcGVkIGV4cG9ydFxuICAvLyBoYXMgdG8gYXNrIGl0LlxuICBAVmlld0NoaWxkKFJlcG9ydENhbnZhc0NvbXBvbmVudClcbiAgcHJpdmF0ZSBfY2FudmFzPzogUmVwb3J0Q2FudmFzQ29tcG9uZW50O1xuXG4gIHB1YmxpYyByZWFkb25seSBpbnRyb01lc3NhZ2UgPSB7XG4gICAgdGV4dDogJycsXG4gIH07XG5cbiAgcHJpdmF0ZSBfcmVwb3J0RGF0YSA9IGluamVjdChSZXBvcnREYXRhKTtcbiAgcHJpdmF0ZSBfcmVwb3J0U2VydmljZSA9IGluamVjdChSZXBvcnRTZXJ2aWNlKTtcbiAgcHJpdmF0ZSBfZmlsdGVyU3RhdGUgPSBpbmplY3QoUmVwb3J0RmlsdGVyU3RhdGVTZXJ2aWNlKTtcbiAgcHJpdmF0ZSBfZmlsdGVyT3B0aW9ucyA9IGluamVjdChSZXBvcnRGaWx0ZXJPcHRpb25zU2VydmljZSk7XG4gIHByaXZhdGUgX2V4cG9ydFNlcnZpY2UgPSBpbmplY3QoUmVwb3J0RXhwb3J0U2VydmljZSk7XG4gIHByaXZhdGUgX2NvbGxlY3RvciA9IGluamVjdChSZXBvcnRFeHBvcnRDb2xsZWN0b3JTZXJ2aWNlKTtcbiAgcHJpdmF0ZSBfcHJvY2VzcyA9IGluamVjdChGc1Byb2Nlc3MpO1xuICBwcml2YXRlIF9wcm9tcHQgPSBpbmplY3QoRnNQcm9tcHQpO1xuICBwcml2YXRlIF9kaWFsb2cgPSBpbmplY3QoTWF0RGlhbG9nKTtcbiAgcHJpdmF0ZSBfZGVzdHJveVJlZiA9IGluamVjdChEZXN0cm95UmVmKTtcbiAgcHJpdmF0ZSBfem9uZSA9IGluamVjdChOZ1pvbmUpO1xuXG4gIC8vIFRoZSBzdGF0ZSB2ZXJzaW9uIHRoZSBiYXIgd2FzIGxhc3QgYnVpbHQgZm9yIChyZXBvcnRGaWx0ZXJLZXkpLlxuICBwcml2YXRlIHJlYWRvbmx5IF9iYXJWZXJzaW9uID0gc2lnbmFsKDApO1xuXG4gIC8vIFRoZSBwYWdlIG9uIHNjcmVlbiwgYXMgdGhlIGNhbnZhcyBsYXN0IHJlcG9ydGVkIGl0OiB0aGUgYmFyIHNob3dzIGl0c1xuICAvLyBwYWdlIGdyb3Vwcy5cbiAgcHJpdmF0ZSByZWFkb25seSBfcGFnZUlkID0gc2lnbmFsPG51bWJlciB8IG51bGw+KG51bGwpO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMuX2J1aWxkUmVwb3J0Q29uZmlnKCk7XG5cbiAgICAvLyByZXNldFRvRGVmYXVsdHMoKSBtb3ZlcyB0aGUgc3RhdGUgdmVyc2lvbi4gVGhlIGJhciBpcyBidWlsdCBhZ2FpbiBmb3JcbiAgICAvLyBpdCwgYW5kIHJlcG9ydEZpbHRlcktleSwgd2hpY2ggY2FycmllcyBpdCwgcmVjcmVhdGVzIGZzLWZpbHRlcjogaXQgcmVhZHNcbiAgICAvLyBpdHMgY29uZmlnIG9uY2UuXG4gICAgZWZmZWN0KCgpID0+IHtcbiAgICAgIGNvbnN0IHZlcnNpb24gPSB0aGlzLl9maWx0ZXJTdGF0ZS5zdGF0ZVZlcnNpb24oKTtcblxuICAgICAgdW50cmFja2VkKCgpID0+IHtcbiAgICAgICAgaWYgKHZlcnNpb24gIT09IHRoaXMuX2JhclZlcnNpb24oKSkge1xuICAgICAgICAgIHRoaXMuX2J1aWxkUmVwb3J0Q29uZmlnKCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGhpcy5fcmVwb3J0U2VydmljZS5sb2FkKClcbiAgICAgIC5waXBlKFxuICAgICAgICBmaW5hbGl6ZSgoKSA9PiB7XG4gICAgICAgICAgdGhpcy5sb2FkaW5nUmVwb3J0cy5zZXQoZmFsc2UpO1xuICAgICAgICB9KSxcbiAgICAgICAgdGFrZVVudGlsRGVzdHJveWVkKHRoaXMuX2Rlc3Ryb3lSZWYpLFxuICAgICAgKVxuICAgICAgLnN1YnNjcmliZSh7IGVycm9yOiAoKSA9PiB1bmRlZmluZWQgfSk7XG4gIH1cblxuICBwdWJsaWMgZmV0Y2hSZXBvcnRzID0gKGtleXdvcmQ6IHN0cmluZykgPT4ge1xuICAgIHJldHVybiB0aGlzLl9yZXBvcnRTZXJ2aWNlLmZpbHRlcihrZXl3b3JkKTtcbiAgfTtcblxuICBwdWJsaWMgcmVwb3J0Q2hhbmdlKHJlcG9ydDogUmVwb3J0TGlzdEl0ZW0gfCBudWxsKTogdm9pZCB7XG4gICAgdGhpcy5yZXBvcnQuc2V0KG51bGwpO1xuICAgIHRoaXMuZWRpdE1vZGUuc2V0KGZhbHNlKTtcbiAgICB0aGlzLl9wYWdlSWQuc2V0KG51bGwpO1xuICAgIHRoaXMuX2J1aWxkUmVwb3J0Q29uZmlnKCk7XG5cbiAgICBpZiAocmVwb3J0KSB7XG4gICAgICB0aGlzLl9sb2FkUmVwb3J0KHJlcG9ydC5pZCk7XG4gICAgfVxuICB9XG5cbiAgcHVibGljIGNyZWF0ZVJlcG9ydCgpOiB2b2lkIHtcbiAgICB0aGlzLl9wcm9tcHQuaW5wdXQoe1xuICAgICAgdGl0bGU6ICdDcmVhdGUgUmVwb3J0JyxcbiAgICAgIGxhYmVsOiAnTmFtZScsXG4gICAgICBjb21taXRMYWJlbDogJ0NyZWF0ZScsXG4gICAgICByZXF1aXJlZDogdHJ1ZSxcbiAgICB9KVxuICAgICAgLnBpcGUoXG4gICAgICAgIHN3aXRjaE1hcCgobmFtZTogc3RyaW5nKSA9PiB0aGlzLl9yZXBvcnRTZXJ2aWNlLmNyZWF0ZShuYW1lKSksXG4gICAgICAgIHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSxcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKHJlcG9ydDogUmVwb3J0TGlzdEl0ZW0pID0+IHtcbiAgICAgICAgdGhpcy5zZWxlY3RlZC5zZXQocmVwb3J0KTtcbiAgICAgICAgdGhpcy5fbG9hZFJlcG9ydChyZXBvcnQuaWQpO1xuICAgICAgfSk7XG4gIH1cblxuICBwdWJsaWMgdG9nZ2xlRWRpdE1vZGUoKTogdm9pZCB7XG4gICAgdGhpcy5lZGl0TW9kZS51cGRhdGUoKGVkaXRNb2RlKSA9PiAhZWRpdE1vZGUpO1xuICB9XG5cbiAgLy8gVGhlIGVkaXQgdG9vbHMgbGl2ZSBpbiB0aGlzIHRvb2xiYXIsIGJ1dCB0aGUgY2FudmFzIG93bnMgd2hhdCB0aGV5IGFjdCBvblxuICAvLyAodGhlIGFjdGl2ZSBwYWdlLCB0aGUgaGVhZGVyL2Zvb3RlciBiYW5kcywgd2hpY2ggbGF5ZXIgaXMgbGl2ZSkuXG4gIHB1YmxpYyBnZXQgaXNNYXN0ZXJNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLl9jYW52YXM/Lm1hc3Rlck1vZGUoKSA/PyBmYWxzZTtcbiAgfVxuXG4gIHB1YmxpYyBhZGRUZXh0KCk6IHZvaWQge1xuICAgIHRoaXMuX2NhbnZhcz8uYWRkVGV4dCgpO1xuICB9XG5cbiAgcHVibGljIHRvZ2dsZU1hc3Rlck1vZGUoKTogdm9pZCB7XG4gICAgdGhpcy5fY2FudmFzPy50b2dnbGVNYXN0ZXJNb2RlKCk7XG4gIH1cblxuICAvLyBSZXBvcnQgc2V0dGluZ3Mg4oCUIG5hbWUsIHBhZ2Ugc2l6ZS4gVGhlIHBpY2tlciBsaXN0IGFuZCBzdHJ1Y3R1cmUgYm90aFxuICAvLyByZWxvYWQgb24gc2F2ZSAoYSBwYWdlIHJlc2l6ZSByZS1yZW5kZXJzIHRoZSB3aG9sZSBjYW52YXMpLlxuICBwdWJsaWMgcmVwb3J0U2V0dGluZ3MoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnJlcG9ydCgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgZGlhbG9nUmVmID0gdGhpcy5fZGlhbG9nLm9wZW48UmVwb3J0U2V0dGluZ3NDb21wb25lbnQsIHVua25vd24sIFJlcG9ydFNldHRpbmdzUmVzdWx0PihSZXBvcnRTZXR0aW5nc0NvbXBvbmVudCwge1xuICAgICAgZGF0YTogeyByZXBvcnQ6IHRoaXMucmVwb3J0KCkgfSxcbiAgICAgIHdpZHRoOiAnNDYwcHgnLFxuICAgIH0pO1xuICAgIGNvbnN0IHNldHRpbmdzID0gZGlhbG9nUmVmLmNvbXBvbmVudEluc3RhbmNlO1xuXG4gICAgZGlhbG9nUmVmLmFmdGVyQ2xvc2VkKClcbiAgICAgIC5waXBlKHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSlcbiAgICAgIC5zdWJzY3JpYmUoKHJlc3VsdCkgPT4ge1xuICAgICAgICAvLyBUaGUgZGlhbG9nIGNvbmZpcm1zICsgcGVyZm9ybXMgdGhlIGRlbGV0ZSBpdHNlbGY7IHdlIGp1c3QgcmVhY3QgdG9cbiAgICAgICAgLy8gd2hhdCBpdCByZXBvcnRzIGJhY2suXG4gICAgICAgIGlmIChyZXN1bHQ/LmFjdGlvbiA9PT0gJ2RlbGV0ZWQnKSB7XG4gICAgICAgICAgdGhpcy5zZWxlY3RlZC5zZXQobnVsbCk7XG4gICAgICAgICAgdGhpcy5yZXBvcnQuc2V0KG51bGwpO1xuICAgICAgICAgIHRoaXMuX2J1aWxkUmVwb3J0Q29uZmlnKCk7XG5cbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBIHNhdmUgdGhhdCBmYWlsZWQgcGFydCB3YXkgKGEgcmVmdXNlZCBuYW1lKSBtYXkgc3RpbGwgaGF2ZSBzdG9yZWRcbiAgICAgICAgLy8gdGhlIGZpbHRlcnMgYmVmb3JlIGl0LCBzbyBhIGRpc21pc3NlZCBkaWFsb2cgdGhhdCB3cm90ZSByZWxvYWRzIHRvby5cbiAgICAgICAgaWYgKChyZXN1bHQ/LmFjdGlvbiA9PT0gJ3NhdmVkJyB8fCBzZXR0aW5ncy53cm90ZSgpKSAmJiB0aGlzLnNlbGVjdGVkKCkpIHtcbiAgICAgICAgICB0aGlzLl9sb2FkUmVwb3J0KHRoaXMuc2VsZWN0ZWQoKS5pZCk7XG4gICAgICAgICAgdGhpcy5fcmVmcmVzaFJlcG9ydE5hbWUodGhpcy5zZWxlY3RlZCgpLmlkKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gIH1cblxuICAvLyBDb21wb25lbnQgc2V0dGluZ3Mg4oCUIHRpdGxlLCBleGFjdCBnZW9tZXRyeSwgYW5kIHRoZSBkaWFnbm9zdGljIHZpZXdzXG4gIC8vIChTUUwsIGNvbmZpZywgZmlsdGVyIHdpcmluZykuXG4gIHB1YmxpYyBjb21wb25lbnRTZXR0aW5ncyhjb21wb25lbnQ6IFJlcG9ydENvbXBvbmVudE1vZGVsKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnJlcG9ydCgpIHx8ICF0aGlzLl9jYW52YXMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBUaGUgZGlhbG9nIGdldHMgd2hhdCB0aGUgY29tcG9uZW50J3MgdGl0bGUgdG9rZW5zIGZpbGwgd2l0aCB3aGVyZSBpdCBpc1xuICAgIC8vIGRyYXduLCBmb3IgdGhlIHRpdGxlJ3MgSW5zZXJ0IGZpZWxkIG1lbnUgYW5kIHRoZSBsaW5lIHNheWluZyBob3cgaXQgcmVhZHMuXG4gICAgY29uc3QgZGlhbG9nUmVmID0gdGhpcy5fZGlhbG9nLm9wZW48Q29tcG9uZW50U2V0dGluZ3NDb21wb25lbnQsIENvbXBvbmVudFNldHRpbmdzRGlhbG9nRGF0YSwgQ29tcG9uZW50U2V0dGluZ3NSZXN1bHQ+KFxuICAgICAgQ29tcG9uZW50U2V0dGluZ3NDb21wb25lbnQsXG4gICAgICB7XG4gICAgICAgIGRhdGE6IHsgcmVwb3J0OiB0aGlzLnJlcG9ydCgpLCBjb21wb25lbnQsIHRva2VuczogdGhpcy5fY2FudmFzLmNvbXBvbmVudFRva2Vucyhjb21wb25lbnQpIH0sXG4gICAgICAgIHdpZHRoOiAnNjQwcHgnLFxuICAgICAgfSxcbiAgICApO1xuXG4gICAgLy8gRmlsdGVyIGFkZC9yZW1vdmUvbGV2ZWwgY2hhbmdlcyBwZXJzaXN0IGltbWVkaWF0ZWx5IGluc2lkZSB0aGUgZGlhbG9nIChub1xuICAgIC8vIFwic2F2ZVwiKSBhbmQgY2FuIGxlYXZlIGl0IGJ5IGFuIGV4aXQgdGhhdCBjYXJyaWVzIG5vIHJlc3VsdCwgc28gdGhleSBhcmVcbiAgICAvLyBhbm5vdW5jZWQgYXMgdGhleSBsYW5kLiBUaGUgcmVsb2FkIHdhaXRzIGZvciB0aGUgY2xvc2Ugc28gdGhlIGNhbnZhcyBpc24ndFxuICAgIC8vIHJlLXJlbmRlcmluZyB1bmRlcm5lYXRoIGFuIG9wZW4gZGlhbG9nLlxuICAgIGxldCBmaWx0ZXJzQ2hhbmdlZCA9IGZhbHNlO1xuICAgIGRpYWxvZ1JlZi5jb21wb25lbnRJbnN0YW5jZS5maWx0ZXJzQ2hhbmdlZFxuICAgICAgLnBpcGUodGFrZVVudGlsKGRpYWxvZ1JlZi5hZnRlckNsb3NlZCgpKSlcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4gZmlsdGVyc0NoYW5nZWQgPSB0cnVlKTtcblxuICAgIGRpYWxvZ1JlZi5hZnRlckNsb3NlZCgpXG4gICAgICAucGlwZSh0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKChyZXN1bHQpID0+IHtcbiAgICAgICAgLy8gVGhlIGRpYWxvZyBjb25maXJtcyArIHBlcmZvcm1zIHRoZSBkZWxldGUgaXRzZWxmLiBUaGUgY29tcG9uZW50IGlzXG4gICAgICAgIC8vIGdvbmUgZnJvbSB0aGUgcmVwb3J0LCBzbyByZWxvYWQgcmF0aGVyIHRoYW4gcGF0Y2hpbmcgaXQgb3V0IGxvY2FsbHkg4oCUXG4gICAgICAgIC8vIGl0cyBmaWx0ZXJzIHdlbnQgd2l0aCBpdCwgd2hpY2ggdGhlIHJlcG9ydCBiYXIgaGFzIHRvIGJlIHJlYnVpbHQgZm9yLlxuICAgICAgICBpZiAocmVzdWx0Py5kZWxldGVkKSB7XG4gICAgICAgICAgaWYgKHRoaXMuc2VsZWN0ZWQoKSkge1xuICAgICAgICAgICAgdGhpcy5fbG9hZFJlcG9ydCh0aGlzLnNlbGVjdGVkKCkuaWQpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFRoZSByZXBvcnQncyBmaWx0ZXIgZ3JvdXBzIGNoYW5nZWQsIHNvIHRoZSByZXBvcnQgYmFyIGFuZCB0aGVcbiAgICAgICAgLy8gb24tY29tcG9uZW50IHN0cmlwcyBoYXZlIHRvIGJlIHJlYnVpbHQgZnJvbSBhIGZyZXNoIHJlcG9ydC5cbiAgICAgICAgaWYgKGZpbHRlcnNDaGFuZ2VkKSB7XG4gICAgICAgICAgaWYgKHRoaXMuc2VsZWN0ZWQoKSkge1xuICAgICAgICAgICAgdGhpcy5fbG9hZFJlcG9ydCh0aGlzLnNlbGVjdGVkKCkuaWQpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIE5vdGhpbmcgZWxzZSBpbiB0aGUgZGlhbG9nIHRvdWNoZXMgdGhlIHF1ZXJ5IOKAlCB0aXRsZSBhbmQgcGFkZGluZyBhcmVcbiAgICAgICAgLy8gcHJlc2VudGF0aW9uLiBSZXBhaW50IHRoZSBvbmUgY29tcG9uZW50IGl0IHdhcyBvcGVuZWQgZm9yIGFuZCBsZWF2ZVxuICAgICAgICAvLyBldmVyeSBvdGhlciBjb21wb25lbnQncyBkYXRhLCBhbmQgaXRzIHF1ZXJ5LCBhbG9uZS5cbiAgICAgICAgaWYgKHJlc3VsdD8uc2V0dGluZ3MpIHtcbiAgICAgICAgICB0aGlzLl9jYW52YXM/LmFwcGx5Q29tcG9uZW50U2V0dGluZ3MoY29tcG9uZW50LmlkLCByZXN1bHQuc2V0dGluZ3MpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgfVxuXG4gIC8vIFRoZSBjYW52YXMgbW92ZWQgdG8gYW5vdGhlciBwYWdlLiBUaGUgYmFyIHNob3dzIHRoZSBwYWdlIGdyb3VwcyBvZiB0aGVcbiAgLy8gcGFnZSBvbiBzY3JlZW4uXG4gIHB1YmxpYyBvbkNhbnZhc1BhZ2VDaGFuZ2VkKHBhZ2VJZDogbnVtYmVyIHwgbnVsbCk6IHZvaWQge1xuICAgIHRoaXMuX3BhZ2VJZC5zZXQocGFnZUlkKTtcbiAgICAvLyBBIGhlYWRlciBvciBmb290ZXIgdGlsZSByZWFkcyBpdHMgcGFnZSdzIGdyb3VwcyB0aHJvdWdoIGl0LlxuICAgIHRoaXMuX2ZpbHRlclN0YXRlLnNldFBhZ2UocGFnZUlkKTtcblxuICAgIGlmIChoYXNQYWdlR3JvdXAodGhpcy5yZXBvcnQoKSkpIHtcbiAgICAgIHRoaXMuX2J1aWxkUmVwb3J0Q29uZmlnKCk7XG4gICAgfVxuICB9XG5cbiAgLy8gVGhlIGNhbnZhcyBjaGFuZ2VkIHRoZSByZXBvcnQncyBzdHJ1Y3R1cmUgKGFkZGVkIGEgcGFnZSkg4oCUIHJlLWZldGNoIHNvIHRoZVxuICAvLyBuZXcgcGFnZSByZW5kZXJzOyB0aGUgY2FudmFzIGxhbmRzIHRoZSB2aWV3IG9uIGl0LlxuICBwdWJsaWMgb25DYW52YXNSZXBvcnRDaGFuZ2VkKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNlbGVjdGVkKCkpIHtcbiAgICAgIHRoaXMuX2xvYWRSZXBvcnQodGhpcy5zZWxlY3RlZCgpLmlkKTtcbiAgICB9XG4gIH1cblxuICAvLyBFeHBvcnQgdGhlIHJlcG9ydCB0byBQb3dlclBvaW50LCBlbnRpcmVseSBjbGllbnQtc2lkZTogZXZlcnkgY29tcG9uZW50J3NcbiAgLy8gZGF0YSBpcyBmZXRjaGVkIGZyZXNoIHdpdGggdGhlIGN1cnJlbnQgZmlsdGVyIHN0YXRlIChhbGwgcGFnZXMsIG5vdCBqdXN0XG4gIC8vIHRoZSB2aXNpYmxlIG9uZSksIHRoZW4gcHB0eGdlbmpzIGJ1aWxkcyBhIGRlY2sgb2YgTkFUSVZFIGVkaXRhYmxlIGNoYXJ0cyDigJRcbiAgLy8gcGFnZSA9IHNsaWRlLCBwb3NpdGlvbnMgMToxLiBUaGUgZG9jayBzaG93cyBwcm9ncmVzcyB1bnRpbCB0aGUgZmlsZSBzYXZlcy5cbiAgcHVibGljIGV4cG9ydFBvd2VycG9pbnQoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnJlcG9ydCgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5fcHJvY2Vzcy5ydW4oXG4gICAgICAnRXhwb3J0aW5nIFBvd2VyUG9pbnQnLFxuICAgICAgdGhpcy5fZXhwb3J0U2VydmljZS5wb3dlcnBvaW50KHRoaXMucmVwb3J0KCkpLFxuICAgICk7XG4gIH1cblxuICAvLyBFeHBvcnQgdG8gUERGLCBlbnRpcmVseSBjbGllbnQtc2lkZTogc2FtZSBjb2xsZWN0aW9uIHBhc3MsIGNoYXJ0cyByZW5kZXJlZFxuICAvLyB0aHJvdWdoIGFuIG9mZnNjcmVlbiBFQ2hhcnRzIGluc3RhbmNlIGF0IDPDlyBwaXhlbCByYXRpbyAocGl4ZWwtaWRlbnRpY2FsXG4gIC8vIHRvIHRoZSBzY3JlZW4pLCB0YWJsZXMgdmlhIGpzcGRmLWF1dG90YWJsZSDigJQgcGFnZSA9IFBERiBwYWdlLlxuICBwdWJsaWMgZXhwb3J0UGRmKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5yZXBvcnQoKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuX3Byb2Nlc3MucnVuKFxuICAgICAgJ0V4cG9ydGluZyBQREYnLFxuICAgICAgdGhpcy5fZXhwb3J0U2VydmljZS5wZGYodGhpcy5yZXBvcnQoKSksXG4gICAgKTtcbiAgfVxuXG4gIC8vIFRoZSB3aG9sZSByZXBvcnQgYXMgZmlsZXMgaW4gb25lIGFjdGlvbjogdGhlIHNlcnZlciB3cml0ZXMgb25lIENTViBwZXJcbiAgLy8gY29tcG9uZW50LCBlYWNoIHdoYXQgdGhlIHNjcmVlbiBzaG93cyAoYSBjaGFydCBvciBLUEkgYXMgZHJhd24sIGF0IHRoZVxuICAvLyB2aWV3ZXIncyBncm91cC1ieSBncmFpbjsgYSBsaXN0IGluIGZ1bGwpLCBhbmQgemlwcyB0aGVtIGZvbGRlcmVkIGJ5IHBhZ2VcbiAgLy8gd2l0aCBhbiBFeHBvcnQgTm90ZXMudHh0IHRoYXQgcmVjb3JkcyB0aGUgZmlsdGVycyBhcyB0aGUgYmFyIHJlYWRzIHRoZW0uXG4gIC8vIEVhY2ggZmlsZSBjYXJyaWVzIGl0cyB0aWxlJ3Mgb3duIHN0YXRlICh6aXBFeHBvcnRTdGF0ZXMpOiBhIGxpc3QncyBzb3J0LFxuICAvLyBzZWFyY2ggYW5kIGhpZGRlbiBjb2x1bW5zLCBhIGNoYXJ0J3MgZHJpbGwgcGF0aCBhbmQgdGhlIGdyb3VwcyB0aGF0IG1hdGNoXG4gIC8vICdhbnknOyBhIHNlcnZlciB0aGF0IGRvZXMgbm90IHJlYWQgaXQgdXNlcyBlYWNoIGxpc3QncyBzYXZlZCBzb3J0LiBUaGVcbiAgLy8gbm90ZXMgZ2V0IHRoZSBmaWx0ZXIgbGluZXMgYXMgdGhlIGNvdmVyIHdvcmRzIHRoZW0gKGV4cG9ydExpbmVzOyBhIFRoaXNcbiAgLy8gUGFnZSB6aXAgbmFtZXMgb25seSB0aGUgZ3JvdXBzIHRoYXQgcmVhY2ggdGhlIHBhZ2UpLCBhbmQgZWFjaCBmaWxlIGlzXG4gIC8vIG5hbWVkIGFmdGVyIGl0cyB0aWxlJ3MgdGl0bGUgYXMgdGhlIGNhbnZhcyBoZWFkZXIgcmVhZHMgaXQgKHRpdGxlcykuIEFsbFxuICAvLyBvZiBpdCBpcyByZWFkIG9uY2UsIHdoZW4gdGhlIGV4cG9ydCBzdGFydHMgKGV4cG9ydFdvcmRpbmcpLiBgdGhpc1BhZ2VgXG4gIC8vIG5hcnJvd3MgaXQgdG8gdGhlIHBhZ2UgdGhlIGNhbnZhcyBpcyBzaG93aW5nLiBVbmxpa2UgdGhlIFBERi9Qb3dlclBvaW50XG4gIC8vIGV4cG9ydHMgdGhlIGZpbGVzIGFyZSB3cml0dGVuIHNlcnZlci1zaWRlLCBzbyB0aGV5IGFyZSBub3QgY2FwcGVkIGJ5XG4gIC8vIHdoYXQgYSBzbGlkZSBjYW4gaG9sZC5cbiAgcHVibGljIGV4cG9ydERhdGEodGhpc1BhZ2UgPSBmYWxzZSwgZm9ybWF0OiBSZXBvcnRFeHBvcnRGb3JtYXQgPSAnY3N2Jyk6IHZvaWQge1xuICAgIGNvbnN0IHJlcG9ydCA9IHRoaXMucmVwb3J0KCk7XG4gICAgaWYgKCFyZXBvcnQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBwYWdlSWQgPSB0aGlzUGFnZSA/IHRoaXMuX2NhbnZhcz8uYWN0aXZlUGFnZT8uaWQgPz8gbnVsbCA6IG51bGw7XG4gICAgY29uc3QgZmlsdGVycyA9IHRoaXMuX2ZpbHRlclN0YXRlLnJlc29sdmVGb3JSZXBvcnQocmVwb3J0LCBwYWdlSWQpO1xuICAgIGNvbnN0IGZyZXF1ZW5jeSA9IHRoaXMuX2ZpbHRlclN0YXRlLmZyZXF1ZW5jeSgpO1xuICAgIGNvbnN0IHNuYXBzaG90ID0gdGhpcy5fY29sbGVjdG9yLnNuYXBzaG90KHJlcG9ydCk7XG4gICAgY29uc3Qgc3RhdGVzID0gemlwRXhwb3J0U3RhdGVzKHJlcG9ydCwgc25hcHNob3Quc3RhdGUsIHBhZ2VJZCwgKGNvbXBvbmVudCkgPT4gdGhpcy5fZmlsdGVyU3RhdGUuZ3JpZFNlc3Npb25MYXlvdXQoY29tcG9uZW50KSk7XG5cbiAgICB0aGlzLl9wcm9jZXNzLmRvd25sb2FkKFxuICAgICAgZm9ybWF0ID09PSAneGxzeCcgPyAnRXhwb3J0aW5nIEV4Y2VsJyA6ICdFeHBvcnRpbmcgZGF0YScsXG4gICAgICB0aGlzLl9jb2xsZWN0b3IuZXhwb3J0V29yZGluZyhyZXBvcnQsIHNuYXBzaG90LCBwYWdlSWQpXG4gICAgICAgIC5waXBlKFxuICAgICAgICAgIHN3aXRjaE1hcCgoeyBsaW5lcywgdGl0bGVzIH0pID0+IHRoaXMuX3JlcG9ydERhdGEuZXhwb3J0UmVwb3J0KFxuICAgICAgICAgICAgcmVwb3J0LmlkLFxuICAgICAgICAgICAgZmlsdGVycyxcbiAgICAgICAgICAgIHBhZ2VJZCxcbiAgICAgICAgICAgIGZyZXF1ZW5jeSxcbiAgICAgICAgICAgIGxpbmVzLFxuICAgICAgICAgICAgc3RhdGVzLFxuICAgICAgICAgICAgdGl0bGVzLFxuICAgICAgICAgICAge30sXG4gICAgICAgICAgICBmb3JtYXQsXG4gICAgICAgICAgKSksXG4gICAgICAgICksXG4gICAgKTtcbiAgfVxuXG4gIC8vIFJlYWN0IHRvIGVhY2ggY2hhdCB0dXJuIChmcy1jaGF0IGhhbmRsZXMgdGhlIHJlcXVlc3QgKyBwZXJzaXN0ZW5jZSk7IHdlXG4gIC8vIG9ubHkgY2FyZSBhYm91dCB0aGUgcmVwb3J0IHNpZGUgZWZmZWN0cyB0aGlzIGNvbnRleHQncyBiYWNrZW5kIGZsYWdzLlxuICBwdWJsaWMgb25DaGF0UmVzcG9uc2UocmVzcG9uc2U6IEZzQWlDaGF0UmVzcG9uc2UpOiB2b2lkIHtcbiAgICAvLyBUaGUgYWdlbnQgY2hhbmdlZCB0aGUgcmVwb3J0J3Mgc3RydWN0dXJlIOKAlCByZS1mZXRjaCBpdC4gU2Vzc2lvbiBmaWx0ZXJcbiAgICAvLyB2YWx1ZXMgc3Vydml2ZSAodGhlIGZpbHRlci1zdGF0ZSBzZXJ2aWNlIGtlZXBzIHZhbHVlcyBmb3IgZ3JvdXBzIHRoYXRcbiAgICAvLyBzdGlsbCBleGlzdCkuXG4gICAgaWYgKHJlc3BvbnNlLnJlcG9ydENoYW5nZWQgJiYgdGhpcy5zZWxlY3RlZCgpKSB7XG4gICAgICB0aGlzLl9sb2FkUmVwb3J0KHRoaXMuc2VsZWN0ZWQoKS5pZCk7XG4gICAgfVxuXG4gICAgLy8gQSByZW5hbWUgY2hhbmdlcyBub3RoaW5nIHN0cnVjdHVyYWwg4oCUIHJlZnJlc2ggdGhlIHBpY2tlciBjaGlwIGxhYmVsLlxuICAgIGlmIChyZXNwb25zZS5yZXBvcnRSZW5hbWVkICYmIHRoaXMuc2VsZWN0ZWQoKSkge1xuICAgICAgdGhpcy5fcmVmcmVzaFJlcG9ydE5hbWUodGhpcy5zZWxlY3RlZCgpLmlkKTtcbiAgICB9XG4gIH1cblxuICAvLyBEcmFnIHRoZSBkaXZpZGVyIGJldHdlZW4gdGhlIGNoYXQgYW5kIGNhbnZhcyBwYW5lcyAocG9pbnRlci1jYXB0dXJlIGxvb3BcbiAgLy8gb3V0c2lkZSBBbmd1bGFyKS5cbiAgcHVibGljIG9uUmVzaXplU3RhcnQoZXZlbnQ6IFBvaW50ZXJFdmVudCk6IHZvaWQge1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cbiAgICBjb25zdCBoYW5kbGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7XG4gICAgaGFuZGxlLnNldFBvaW50ZXJDYXB0dXJlKGV2ZW50LnBvaW50ZXJJZCk7XG4gICAgdGhpcy5fc3BsaXQubmF0aXZlRWxlbWVudC5jbGFzc0xpc3QuYWRkKCdyZXNpemluZycpO1xuXG4gICAgdGhpcy5fem9uZS5ydW5PdXRzaWRlQW5ndWxhcigoKSA9PiB7XG4gICAgICBjb25zdCBtb3ZlID0gKGU6IFBvaW50ZXJFdmVudCkgPT4gdGhpcy5fcmVzaXplVG8oZS5jbGllbnRYKTtcbiAgICAgIGNvbnN0IGVuZCA9ICgpID0+IHtcbiAgICAgICAgaGFuZGxlLnJlbGVhc2VQb2ludGVyQ2FwdHVyZShldmVudC5wb2ludGVySWQpO1xuICAgICAgICBoYW5kbGUucmVtb3ZlRXZlbnRMaXN0ZW5lcigncG9pbnRlcm1vdmUnLCBtb3ZlKTtcbiAgICAgICAgaGFuZGxlLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJ1cCcsIGVuZCk7XG4gICAgICAgIGhhbmRsZS5yZW1vdmVFdmVudExpc3RlbmVyKCdwb2ludGVyY2FuY2VsJywgZW5kKTtcbiAgICAgICAgdGhpcy5fc3BsaXQubmF0aXZlRWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCdyZXNpemluZycpO1xuICAgICAgfTtcblxuICAgICAgaGFuZGxlLmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJtb3ZlJywgbW92ZSk7XG4gICAgICBoYW5kbGUuYWRkRXZlbnRMaXN0ZW5lcigncG9pbnRlcnVwJywgZW5kKTtcbiAgICAgIGhhbmRsZS5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVyY2FuY2VsJywgZW5kKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgX3Jlc2l6ZVRvKGNsaWVudFg6IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IHJlY3QgPSB0aGlzLl9zcGxpdC5uYXRpdmVFbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIGlmIChyZWN0LndpZHRoID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgcmF0aW8gPSAoY2xpZW50WCAtIHJlY3QubGVmdCkgLyByZWN0LndpZHRoO1xuICAgIGNvbnN0IGNsYW1wZWQgPSBNYXRoLm1pbigwLjgsIE1hdGgubWF4KDAuMiwgcmF0aW8pKTtcbiAgICB0aGlzLl9jaGF0UGFuZWwubmF0aXZlRWxlbWVudC5zdHlsZS5mbGV4QmFzaXMgPSBgJHtjbGFtcGVkICogMTAwfSVgO1xuICB9XG5cbiAgcHJpdmF0ZSBfbG9hZFJlcG9ydChyZXBvcnRJZDogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5fcmVwb3J0RGF0YS5nZXQocmVwb3J0SWQpXG4gICAgICAucGlwZSh0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKChyZXBvcnQ6IFJlcG9ydCkgPT4ge1xuICAgICAgICB0aGlzLnJlcG9ydC5zZXQocmVwb3J0KTtcbiAgICAgICAgLy8gQSByZXBvcnQga2VlcHMgbm8gc3RhdGUgaW4gdGhlIFVSTDogaXRzIGZpbHRlcnMgbGl2ZSBpbiB0aGUgc3RhdGVcbiAgICAgICAgLy8gc2VydmljZSBmb3IgYXMgbG9uZyBhcyB0aGUgcmVwb3J0IGlzIG9wZW4uXG4gICAgICAgIHRoaXMuX2ZpbHRlclN0YXRlLmluaXQocmVwb3J0KTtcbiAgICAgICAgdGhpcy5fYnVpbGRSZXBvcnRDb25maWcoKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgLy8gUmVwb3J0LWxldmVsIGZpbHRlciBncm91cHMgaW4gdGhlaXIgc2F2ZWQgb3JkZXIg4oCUIHRoZSBvbmVzIHRoYXQgcmVuZGVyIGluXG4gIC8vIHRoZSB0b29sYmFyIChjb21wb25lbnQtbGV2ZWwgZ3JvdXBzIHJlbmRlciBvbiB0aGVpciBjb21wb25lbnRzKSwgd2l0aFxuICAvLyB0aGUgcGFnZSBncm91cHMgb2YgdGhlIHBhZ2Ugb24gc2NyZWVuLlxuICBwcml2YXRlIF9yZXBvcnRHcm91cHMoKTogUmVwb3J0RmlsdGVyR3JvdXBbXSB7XG4gICAgcmV0dXJuIHJlcG9ydEJhckdyb3Vwcyh0aGlzLnJlcG9ydCgpLCB0aGlzLl9wYWdlSWQoKSk7XG4gIH1cblxuICAvLyBHYXRlcyB0aGUgdG9vbGJhciBmcy1maWx0ZXIgc28gaXQgaXNuJ3QgcmVuZGVyZWQgKHRha2luZyBzcGFjZSkgd2hlbiB0aGVyZVxuICAvLyBpcyBub3RoaW5nIHRvIHB1dCBpbiBpdCDigJQgbm8gcmVwb3J0LWxldmVsIGZpbHRlcnMgYW5kIG5vIGdyb3VwLWJ5IGNvbnRyb2wuXG4gIHB1YmxpYyBnZXQgcmVwb3J0SGFzRmlsdGVycygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5fcmVwb3J0R3JvdXBzKCkubGVuZ3RoID4gMCB8fCAhIXRoaXMuX2dyb3VwQnlJdGVtKCk7XG4gIH1cblxuICAvLyBmcy1maWx0ZXIgcmVhZHMgaXRzIGNvbmZpZyBvbmx5IG9uY2UgKGF0IGluaXQpIOKAlCByZS1iaW5kaW5nIGRvZXMgbm90aGluZy5cbiAgLy8gVGhlIHRlbXBsYXRlIGtleXMgdGhlIHRvb2xiYXIgZnMtZmlsdGVyIG9uIHRoaXMgc2lnbmF0dXJlIHNvIGl0J3MgcmVjcmVhdGVkXG4gIC8vIChhbmQgcmUtcmVhZHMgdGhlIHJlYnVpbHQgY29uZmlnKSB3aGVuZXZlciB0aGUgcmVwb3J0LWxldmVsIGZpbHRlciBzZXRcbiAgLy8gY2hhbmdlcywgZS5nLiBhZnRlciB0aGUgY29tcG9uZW50IHNldHRpbmdzIGRpYWxvZyBhZGRzL21vdmVzIGEgZmlsdGVyLlxuICBwdWJsaWMgZ2V0IHJlcG9ydEZpbHRlcktleSgpOiBzdHJpbmcge1xuICAgIHJldHVybiBbXG4gICAgICAvLyBUaGUgc3RhdGUgdmVyc2lvbiB0aGUgYmFyIHdhcyBidWlsdCBmb3I6IGFuIGFwcGxpZWQgdmlldywgb3IgYSByZXNldFxuICAgICAgLy8gdG8gdGhlIGJhc2UsIHJlcGxhY2VzIGV2ZXJ5IHZhbHVlIGF0IG9uY2UuXG4gICAgICB0aGlzLl9iYXJWZXJzaW9uKCksXG4gICAgICAvLyBUaGUgZWZmZWN0aXZlIHpvbmUgaXMgcGFydCBvZiBpdCB0b286IGEgem9uZSBjaGFuZ2UgcmUtcmVzb2x2ZXMgcmVsYXRpdmVcbiAgICAgIC8vIHByZXNldHMsIGFuZCB0aGUgYmFyIG11c3QgYmUgcmVjcmVhdGVkIHRvIHNob3cgdGhlIG5ldyBkYXlzLlxuICAgICAgdGhpcy5fZmlsdGVyU3RhdGUudGltZXpvbmUoKSxcbiAgICAgIC8vIFRoZSBzdG9yZWQgZGVmYXVsdCBpcyBwYXJ0IG9mIHRoZSBzaWduYXR1cmU6IHNhdmluZyBhIG5ldyBkZWZhdWx0IGluIHJlcG9ydFxuICAgICAgLy8gc2V0dGluZ3MgcmVidWlsZHMgdGhlIGNvbmZpZywgYnV0IHdpdGhvdXQgYSBrZXkgY2hhbmdlIGZzLWZpbHRlciBrZWVwc1xuICAgICAgLy8gcmVuZGVyaW5nIHRoZSB2YWx1ZXMgaXQgd2FzIGNyZWF0ZWQgd2l0aCB1bnRpbCBhIGZ1bGwgcGFnZSByZWxvYWQuXG4gICAgICBncm91cEJ5U2lnbmF0dXJlKHRoaXMucmVwb3J0KCkpLFxuICAgICAgLi4udGhpcy5fcmVwb3J0R3JvdXBzKCkubWFwKChncm91cCkgPT4gYCR7Z3JvdXAuaWR9OiR7Z3JvdXAubGV2ZWx9OiR7Z3JvdXAubGFiZWx9OiR7ZGVmYXVsdFNpZ25hdHVyZShncm91cCl9YCksXG4gICAgICAvLyBBIHBhZ2UgZ3JvdXAgc2hvd3Mgb24gaXRzIG93biBwYWdlIG9ubHksIHNvIHN1Y2ggYSBiYXIgaXMgcmVjcmVhdGVkXG4gICAgICAvLyB3aGVuIHRoZSBwYWdlIG9uIHNjcmVlbiBjaGFuZ2VzLlxuICAgICAgLi4uKGhhc1BhZ2VHcm91cCh0aGlzLnJlcG9ydCgpKSA/IFtgcGFnZToke3RoaXMuX3BhZ2VJZCgpfWBdIDogW10pLFxuICAgIF0uam9pbignfCcpO1xuICB9XG5cbiAgLy8gKFJlKWJ1aWxkIHRoZSB0b29sYmFyIGZzLWZpbHRlciBjb25maWcg4oCUIHRoZSByZXBvcnQtbGV2ZWwgZmlsdGVyIGl0ZW1zIGFuZFxuICAvLyB0aGVuIHRoZSBncm91cC1ieSBjb250cm9sLiBUaGUgcmVwb3J0J3MgYWN0aW9ucyBsaXZlIGluIHRoZSBmcy1tZW51IGJlc2lkZVxuICAvLyB0aGUgcGlja2VyLCBub3QgaGVyZS4gT25seSBidWlsdC91c2VkIG9uY2UgYSByZXBvcnQgaXMgbG9hZGVkICh0aGUgdGVtcGxhdGVcbiAgLy8gZ2F0ZXMgaXQgb24gYHJlcG9ydGApLlxuICBwcml2YXRlIF9idWlsZFJlcG9ydENvbmZpZygpOiB2b2lkIHtcbiAgICBjb25zdCByZXBvcnRJZCA9IHRoaXMucmVwb3J0KCk/LmlkID8/IG51bGw7XG5cbiAgICAvLyBEcm9wIGEgZ3JhaW4gdGhlIGNvbnRyb2wgbm8gbG9uZ2VyIG9mZmVycyDigJQgdGhlIGFnZW50IGNhbiBuYXJyb3cgdGhlIGxpc3RcbiAgICAvLyBtaWQtc2Vzc2lvbiwgYW5kIGFuIG92ZXJyaWRlIHRoZSB2aWV3ZXIgY2FuJ3Qgc2VlIGlzIHdvcnNlIHRoYW4gbm9uZS5cbiAgICBpZiAodGhpcy5yZXBvcnQoKSAmJiAhaXNHcmFudWxhcml0eU9mZmVyZWQodGhpcy5yZXBvcnQoKSwgdGhpcy5fZmlsdGVyU3RhdGUuZnJlcXVlbmN5KCkpKSB7XG4gICAgICB0aGlzLl9maWx0ZXJTdGF0ZS5zZXRGcmVxdWVuY3kobnVsbCk7XG4gICAgfVxuXG4gICAgY29uc3QgZmVhdHVyZXMgPSB0aGlzLnJlcG9ydCgpPy52aWV3ZXJGZWF0dXJlcyA/PyBbXTtcbiAgICBjb25zdCBpdGVtcyA9IHJlcG9ydElkXG4gICAgICA/IHRoaXMuX3JlcG9ydEdyb3VwcygpLm1hcCgoZ3JvdXApID0+XG4gICAgICAgIGZpbHRlckl0ZW1Gb3JHcm91cChncm91cCwgdGhpcy5fZmlsdGVyT3B0aW9ucywgdGhpcy5yZXBvcnQoKSwgdGhpcy5fZmlsdGVyU3RhdGUudmFsdWUoZ3JvdXAuaWQpLCBmZWF0dXJlcykpXG4gICAgICA6IFtdO1xuXG4gICAgLy8gVGhlIGdyb3VwLWJ5IGNvbnRyb2wgbGVhZHMgdGhlIGJhciwgYWhlYWQgb2YgZXZlcnkgZmlsdGVyOiBpdCBzZXRzIHRoZVxuICAgIC8vIGdyYWluIHRoZSB3aG9sZSByZXBvcnQgaXMgcmVhZCBhdCwgc28gaXQgaXMgdGhlIGZpcnN0IHRoaW5nIGEgdmlld2VyXG4gICAgLy8gZGVjaWRlcy4gSXQgaXMgbm90IGEgZmlsdGVyIGdyb3VwIOKAlCBpdCByZS1idWNrZXRzIHJvd3MgcmF0aGVyIHRoYW5cbiAgICAvLyBuYXJyb3dpbmcgdGhlbSDigJQgc28gaXQgaXMgcGxhY2VkIGhlcmUgcmF0aGVyIHRoYW4gb3JkZXJlZCBhbW9uZyB0aGVtLlxuICAgIGNvbnN0IGdyb3VwQnlJdGVtID0gdGhpcy5fZ3JvdXBCeUl0ZW0oKTtcbiAgICBpZiAoZ3JvdXBCeUl0ZW0pIHtcbiAgICAgIGl0ZW1zLnVuc2hpZnQoZ3JvdXBCeUl0ZW0pO1xuICAgIH1cblxuICAgIC8vIEhvdyB0aGUgZ3JvdXBzIGNvbWJpbmUgY2xvc2VzIHRoZSBiYXIuXG4gICAgY29uc3QgbWF0Y2hJdGVtID0gdGhpcy5fbWF0Y2hJdGVtKCk7XG4gICAgaWYgKG1hdGNoSXRlbSkge1xuICAgICAgaXRlbXMucHVzaChtYXRjaEl0ZW0pO1xuICAgIH1cblxuICAgIHRoaXMuX2JhclZlcnNpb24uc2V0KHRoaXMuX2ZpbHRlclN0YXRlLnN0YXRlVmVyc2lvbigpKTtcbiAgICB0aGlzLnJlcG9ydEZpbHRlckNvbmZpZy5zZXQoe1xuICAgICAgLy8gTmV2ZXIgdG91Y2ggdGhlIFVSTCBvciBwZXJzaXN0IGZpbHRlciBzdGF0ZSDigJQgcmVwb3J0IGZpbHRlcnMgYXJlXG4gICAgICAvLyBzZXNzaW9uLW9ubHksIG93bmVkIGJ5IFJlcG9ydEZpbHRlclN0YXRlU2VydmljZS5cbiAgICAgIHF1ZXJ5UGFyYW06IGZhbHNlLFxuICAgICAgcGVyc2lzdDogZmFsc2UsXG4gICAgICBpdGVtcyxcbiAgICAgIGNoYW5nZTogKHF1ZXJ5KSA9PiB0aGlzLl9iYXJDaGFuZ2VkKHF1ZXJ5ID8/IHt9LCAhIW1hdGNoSXRlbSksXG4gICAgICAvLyAnQ2xlYXIgZmlsdGVycycgcHV0cyB0aGUgc2NyZWVuIGJhY2sgb24gdGhlIHJlcG9ydCdzIGF1dGhvcmVkXG4gICAgICAvLyBkZWZhdWx0cyBhbmQgZHJvcHMgdGhlIHRlbXBvcmFyeSBsYXllci5cbiAgICAgIGNsZWFyOiAoKSA9PiB0aGlzLl9maWx0ZXJTdGF0ZS5yZXNldFRvRGVmYXVsdHMoKSxcbiAgICAgIC8vIFRoZSBjaGlwIHNob3dzIGV4YWN0bHkgd2hpbGUgdGhlIHNjcmVlbiBkaWZmZXJzIGZyb20gdGhlIGRlZmF1bHRzIGFcbiAgICAgIC8vIGNsZWFyIHJldHVybnMgdG8gKEM1KS5cbiAgICAgIGNsZWFyVmlzaWJsZTogKCkgPT4gdGhpcy5fZmlsdGVyU3RhdGUuZGlmZmVyc0Zyb21EZWZhdWx0cygpLFxuICAgIH0pO1xuICB9XG5cbiAgLy8gVGhlIGJhcidzIHZhbHVlcyBpbnRvIHRoZSBzdGF0ZTogZXZlcnkgZ3JvdXAgaXQgc2hvd3MsIHRoZSBncmFpbiBhbmQsXG4gIC8vIHdoZW4gdGhlIGJhciBoYXMgdGhlIGNvbnRyb2wsIHRoZSBtYXRjaC5cbiAgcHJpdmF0ZSBfYmFyQ2hhbmdlZChxdWVyeTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIGhhc01hdGNoOiBib29sZWFuKTogdm9pZCB7XG4gICAgZm9yIChjb25zdCB7IGdyb3VwSWQsIHZhbHVlIH0gb2YgZ3JvdXBWYWx1ZXNGcm9tUXVlcnkocXVlcnksIHRoaXMuX3JlcG9ydEdyb3VwcygpKSkge1xuICAgICAgdGhpcy5fZmlsdGVyU3RhdGUuc2V0VmFsdWUoZ3JvdXBJZCwgdmFsdWUpO1xuICAgIH1cbiAgICAvLyBOb3QgYSBmaWx0ZXIgZ3JvdXAg4oCUIHRoZSBncmFpbiBpcyByZWFkIHN0cmFpZ2h0IG9mZiB0aGUgcXVlcnkuXG4gICAgdGhpcy5fZmlsdGVyU3RhdGUuc2V0RnJlcXVlbmN5KChxdWVyeVtHUk9VUF9CWV9JVEVNXSBhcyBDaGFydEdyYW51bGFyaXR5KSB8fCBudWxsKTtcbiAgICAvLyBOb3IgaXMgdGhlIG1hdGNoOyBhIGJhciB3aXRob3V0IHRoZSBjb250cm9sIGxlYXZlcyBpdCBhcyBpdCBpcy5cbiAgICBpZiAoaGFzTWF0Y2gpIHtcbiAgICAgIHRoaXMuX2ZpbHRlclN0YXRlLnNldE1hdGNoKG1hdGNoRnJvbVF1ZXJ5KHF1ZXJ5KSk7XG4gICAgfVxuICB9XG5cbiAgLy8gVGhlIHJlcG9ydCdzIGdyb3VwLWJ5IGNvbnRyb2wgYXMgYW4gZnMtZmlsdGVyIGl0ZW0sIG9yIG51bGwgd2hlbiB0aGUgcmVwb3J0XG4gIC8vIGhhcyBub25lIHRvIHNob3cuIFNlZWRlZCBmcm9tIHRoZSBzZXNzaW9uIHN0YXRlIHNvIHRoZSBncmFpbiB0aGUgdmlld2VyXG4gIC8vIHBpY2tlZCBzdXJ2aXZlcyB0aGUgcmVwb3J0IGJlaW5nIHJlLWZldGNoZWQgYWZ0ZXIgYSBjaGF0IGVkaXQuXG4gIHByaXZhdGUgX2dyb3VwQnlJdGVtKCk6IElGaWx0ZXJDb25maWdJdGVtIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMucmVwb3J0KClcbiAgICAgID8gZ3JvdXBCeUZpbHRlckl0ZW0odGhpcy5yZXBvcnQoKSwgdGhpcy5fZmlsdGVyU3RhdGUuZnJlcXVlbmN5KCkpXG4gICAgICA6IG51bGw7XG4gIH1cblxuICAvLyBUaGUgYmFyJ3MgTWF0Y2ggY29udHJvbCwgc2VlZGVkIHdpdGggdGhlIG1hdGNoIG9uIHNjcmVlbiwgb3IgbnVsbCB3aGVuXG4gIC8vIHRoZSBiYXIgaG9sZHMgZmV3ZXIgdGhhbiB0d28gZ3JvdXBzIHRoYXQgY29tYmluZS5cbiAgcHJpdmF0ZSBfbWF0Y2hJdGVtKCk6IElGaWx0ZXJDb25maWdJdGVtIHwgbnVsbCB7XG4gICAgcmV0dXJuIG1hdGNoRmlsdGVySXRlbSh0aGlzLnJlcG9ydCgpLCB0aGlzLl9yZXBvcnRHcm91cHMoKSwgdGhpcy5fcGFnZUlkKCksIHRoaXMuX2ZpbHRlclN0YXRlLm1hdGNoKCkpO1xuICB9XG5cbiAgcHJpdmF0ZSBfcmVmcmVzaFJlcG9ydE5hbWUocmVwb3J0SWQ6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMuX3JlcG9ydFNlcnZpY2UubG9hZCgpXG4gICAgICAucGlwZSh0YWtlVW50aWxEZXN0cm95ZWQodGhpcy5fZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKChyZXBvcnRzOiBSZXBvcnRMaXN0SXRlbVtdKSA9PiB7XG4gICAgICAgIGNvbnN0IHJlbmFtZWQgPSByZXBvcnRzLmZpbmQoKHJlcG9ydCkgPT4gcmVwb3J0LmlkID09PSByZXBvcnRJZCk7XG4gICAgICAgIGlmIChyZW5hbWVkKSB7XG4gICAgICAgICAgdGhpcy5zZWxlY3RlZC5zZXQocmVuYW1lZCk7XG4gICAgICAgICAgaWYgKHRoaXMucmVwb3J0KCkpIHtcbiAgICAgICAgICAgIHRoaXMucmVwb3J0LnNldCh7IC4uLnRoaXMucmVwb3J0KCksIG5hbWU6IHJlbmFtZWQubmFtZSB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICB9XG59XG4iLCI8ZGl2XG4gICAgI3NwbGl0XG4gICAgY2xhc3M9XCJyZXBvcnQgZnMtcm93LmFsaWduLXN0YXJ0XCI+XG4gIDxmcy1haS1jaGF0XG4gICAgI2NoYXRQYW5lbFxuICAgIGNsYXNzPVwiY2hhdFwiXG4gICAgc3R5bGU9XCJtaW4taGVpZ2h0OiA1MDBweDtcIlxuICAgIGJhc2VQYXRoPVwicmVwb3J0c1wiXG4gICAgW3JlcXVlc3REYXRhXT1cInsgcmVwb3J0SWQ6IHNlbGVjdGVkKCk/LmlkID8/IG51bGwgfVwiXG4gICAgW2ludHJvTWVzc2FnZV09XCJpbnRyb01lc3NhZ2VcIlxuICAgIChyZXNwb25zZSk9XCJvbkNoYXRSZXNwb25zZSgkZXZlbnQpXCI+XG4gIDwvZnMtYWktY2hhdD5cbiAgPGRpdlxuICAgIGNsYXNzPVwicmVzaXplclwiXG4gICAgKHBvaW50ZXJkb3duKT1cIm9uUmVzaXplU3RhcnQoJGV2ZW50KVwiPlxuICA8L2Rpdj5cbiAgPGRpdiBjbGFzcz1cInZpZXdlciBmcy1mbGV4IGZzLWNvbHVtblwiPlxuICAgIDxkaXYgY2xhc3M9XCJmcy1yb3cuYWxpZ24tY2VudGVyLmdhcC1zbVwiPlxuICAgICAgPGZzLWF1dG9jb21wbGV0ZS1jaGlwc1xuICAgICAgICAgIGNsYXNzPVwiZnMtZmxleFwiXG4gICAgICAgICAgW2ZldGNoXT1cImZldGNoUmVwb3J0c1wiXG4gICAgICAgICAgWyhuZ01vZGVsKV09XCJzZWxlY3RlZFwiXG4gICAgICAgICAgW2Rpc2FibGVkXT1cImxvYWRpbmdSZXBvcnRzKClcIlxuICAgICAgICAgIFtwYWRsZXNzXT1cInRydWVcIlxuICAgICAgICAgIFttdWx0aXBsZV09XCJmYWxzZVwiXG4gICAgICAgICAgW2ZldGNoT25Gb2N1c109XCJ0cnVlXCJcbiAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJyZXBvcnRDaGFuZ2UoJGV2ZW50KVwiXG4gICAgICAgICAgcGxhY2Vob2xkZXI9XCJSZXBvcnRcIlxuICAgICAgICAgIG5hbWU9XCJyZXBvcnRcIj5cbiAgICAgICAgPG5nLXRlbXBsYXRlXG4gICAgICAgICAgICBmc0F1dG9jb21wbGV0ZUNoaXBzVGVtcGxhdGVcbiAgICAgICAgICAgIGxldC1vYmplY3Q9XCJvYmplY3RcIj5cbiAgICAgICAgICB7eyBvYmplY3QubmFtZSB9fVxuICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICA8bmctdGVtcGxhdGVcbiAgICAgICAgICAgIGZzQXV0b2NvbXBsZXRlQ2hpcHNTdGF0aWNcbiAgICAgICAgICAgIChjbGljayk9XCJjcmVhdGVSZXBvcnQoKVwiPlxuICAgICAgICAgIENyZWF0ZSBSZXBvcnRcbiAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgIDwvZnMtYXV0b2NvbXBsZXRlLWNoaXBzPlxuICAgICAgQGlmIChyZXBvcnQoKSAmJiBlZGl0TW9kZSgpKSB7XG4gICAgICAgIDwhLS0gV2hhdCBsYXlvdXQgZWRpdGluZyBjYW4gYWRkIG9yIHN3aXRjaCB0bywgYW5kIHRoZSB3YXkgb3V0IG9mIGl0IOKAlFxuICAgICAgICAgICAgIHRoZSBzYW1lIG91dGxpbmVkIGJ1dHRvbnMgYXMgdGhlIGZzLWFpLXJlcG9ydCB0b29sYmFyLiAtLT5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgbWF0LXN0cm9rZWQtYnV0dG9uXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgIG1hdFRvb2x0aXA9XCJBZGQgYSBibG9jayBvZiB0ZXh0XCJcbiAgICAgICAgICAgIChjbGljayk9XCJhZGRUZXh0KClcIj5cbiAgICAgICAgICA8bWF0LWljb24+dGl0bGU8L21hdC1pY29uPlxuICAgICAgICAgIEFkZCB0ZXh0XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICBtYXQtc3Ryb2tlZC1idXR0b25cbiAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgY2xhc3M9XCJtYXN0ZXItdG9nZ2xlXCJcbiAgICAgICAgICAgIFtjbGFzcy5hY3RpdmVdPVwiaXNNYXN0ZXJNb2RlXCJcbiAgICAgICAgICAgIFttYXRUb29sdGlwXT1cImlzTWFzdGVyTW9kZSA/ICdCYWNrIHRvIGVkaXRpbmcgdGhpcyBwYWdlJyA6ICdFZGl0IHRoZSBoZWFkZXIgYW5kIGZvb3RlciBzaG93biBvbiBldmVyeSBwYWdlJ1wiXG4gICAgICAgICAgICAoY2xpY2spPVwidG9nZ2xlTWFzdGVyTW9kZSgpXCI+XG4gICAgICAgICAgPG1hdC1pY29uPndlYl9hc3NldDwvbWF0LWljb24+XG4gICAgICAgICAgSGVhZGVyICYgZm9vdGVyXG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICBtYXQtc3Ryb2tlZC1idXR0b25cbiAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgKGNsaWNrKT1cInRvZ2dsZUVkaXRNb2RlKClcIj5cbiAgICAgICAgICA8bWF0LWljb24+Y2hlY2s8L21hdC1pY29uPlxuICAgICAgICAgIERvbmUgZWRpdGluZ1xuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIH1cbiAgICAgIEBpZiAocmVwb3J0KCkpIHtcbiAgICAgICAgPGZzLW1lbnU+XG4gICAgICAgICAgPG5nLXRlbXBsYXRlXG4gICAgICAgICAgICAgIGZzLW1lbnUtaXRlbVxuICAgICAgICAgICAgICAoY2xpY2spPVwicmVwb3J0U2V0dGluZ3MoKVwiPlxuICAgICAgICAgICAgPG1hdC1pY29uPlxuICAgICAgICAgICAgICB0dW5lXG4gICAgICAgICAgICA8L21hdC1pY29uPlxuICAgICAgICAgICAgUmVwb3J0IHNldHRpbmdzXG4gICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgICA8bmctdGVtcGxhdGVcbiAgICAgICAgICAgICAgZnMtbWVudS1pdGVtXG4gICAgICAgICAgICAgIChjbGljayk9XCJ0b2dnbGVFZGl0TW9kZSgpXCJcbiAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cImVkaXRNb2RlKClcIj5cbiAgICAgICAgICAgIDxtYXQtaWNvbj5cbiAgICAgICAgICAgICAgZGFzaGJvYXJkX2N1c3RvbWl6ZVxuICAgICAgICAgICAgPC9tYXQtaWNvbj5cbiAgICAgICAgICAgIHt7IGVkaXRNb2RlKCkgPyAnRG9uZSBlZGl0aW5nIGxheW91dCcgOiAnRWRpdCBsYXlvdXQnIH19XG4gICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgICA8bmctdGVtcGxhdGVcbiAgICAgICAgICAgICAgZnMtbWVudS1pdGVtXG4gICAgICAgICAgICAgIChjbGljayk9XCJleHBvcnRQb3dlcnBvaW50KClcIj5cbiAgICAgICAgICAgIDxtYXQtaWNvbj5cbiAgICAgICAgICAgICAgc2xpZGVzaG93XG4gICAgICAgICAgICA8L21hdC1pY29uPlxuICAgICAgICAgICAgRXhwb3J0IFBvd2VyUG9pbnRcbiAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICAgIDxuZy10ZW1wbGF0ZVxuICAgICAgICAgICAgICBmcy1tZW51LWl0ZW1cbiAgICAgICAgICAgICAgKGNsaWNrKT1cImV4cG9ydFBkZigpXCI+XG4gICAgICAgICAgICA8bWF0LWljb24+XG4gICAgICAgICAgICAgIHBpY3R1cmVfYXNfcGRmXG4gICAgICAgICAgICA8L21hdC1pY29uPlxuICAgICAgICAgICAgRXhwb3J0IFBERlxuICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgICAgPG5nLXRlbXBsYXRlXG4gICAgICAgICAgICAgIGZzLW1lbnUtaXRlbVxuICAgICAgICAgICAgICAoY2xpY2spPVwiZXhwb3J0RGF0YSgpXCI+XG4gICAgICAgICAgICA8bWF0LWljb24+XG4gICAgICAgICAgICAgIHRhYmxlX3ZpZXdcbiAgICAgICAgICAgIDwvbWF0LWljb24+XG4gICAgICAgICAgICBFeHBvcnQgUmF3IERhdGFcbiAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICAgIEBpZiAoKHJlcG9ydCgpPy5wYWdlcz8ubGVuZ3RoID8/IDApID4gMSkge1xuICAgICAgICAgICAgPG5nLXRlbXBsYXRlXG4gICAgICAgICAgICAgICAgZnMtbWVudS1pdGVtXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cImV4cG9ydERhdGEodHJ1ZSlcIj5cbiAgICAgICAgICAgICAgPG1hdC1pY29uPlxuICAgICAgICAgICAgICAgIHRhYmxlX3ZpZXdcbiAgICAgICAgICAgICAgPC9tYXQtaWNvbj5cbiAgICAgICAgICAgICAgRXhwb3J0IFJhdyBEYXRhIChUaGlzIFBhZ2UpXG4gICAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICAgIH1cbiAgICAgICAgICA8bmctdGVtcGxhdGVcbiAgICAgICAgICAgICAgZnMtbWVudS1pdGVtXG4gICAgICAgICAgICAgIChjbGljayk9XCJleHBvcnREYXRhKGZhbHNlLCAneGxzeCcpXCI+XG4gICAgICAgICAgICA8bWF0LWljb24+XG4gICAgICAgICAgICAgIGdyaWRfb25cbiAgICAgICAgICAgIDwvbWF0LWljb24+XG4gICAgICAgICAgICBFeHBvcnQgRXhjZWxcbiAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICAgIEBpZiAoKHJlcG9ydCgpPy5wYWdlcz8ubGVuZ3RoID8/IDApID4gMSkge1xuICAgICAgICAgICAgPG5nLXRlbXBsYXRlXG4gICAgICAgICAgICAgICAgZnMtbWVudS1pdGVtXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cImV4cG9ydERhdGEodHJ1ZSwgJ3hsc3gnKVwiPlxuICAgICAgICAgICAgICA8bWF0LWljb24+XG4gICAgICAgICAgICAgICAgZ3JpZF9vblxuICAgICAgICAgICAgICA8L21hdC1pY29uPlxuICAgICAgICAgICAgICBFeHBvcnQgRXhjZWwgKFRoaXMgUGFnZSlcbiAgICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgICAgfVxuICAgICAgICA8L2ZzLW1lbnU+XG4gICAgICB9XG4gICAgPC9kaXY+XG4gICAgQGlmIChyZXBvcnQoKSkge1xuICAgICAgPCEtLSBSZXBvcnQtbGV2ZWwgZmlsdGVycyBvbmx5ICh0aGUgcmVwb3J0J3MgYWN0aW9ucyBsaXZlIGluIHRoZSBtZW51XG4gICAgICAgICAgIGFib3ZlKS4gZnMtZmlsdGVyIHJlYWRzIGl0cyBjb25maWcgb25jZSBhdCBpbml0LCBzbyBpdCdzIGtleWVkIG9uIHRoZVxuICAgICAgICAgICBmaWx0ZXIgc2lnbmF0dXJlOiB3aGVuIHRoZSByZXBvcnQtbGV2ZWwgZmlsdGVyIHNldCBjaGFuZ2VzIHRoZSBibG9ja1xuICAgICAgICAgICBpcyByZWNyZWF0ZWQgYW5kIHJlLXJlYWRzIHRoZSByZWJ1aWx0IGNvbmZpZy4gLS0+XG4gICAgICBAaWYgKHJlcG9ydEhhc0ZpbHRlcnMpIHtcbiAgICAgICAgQGZvciAoa2V5IG9mIFtyZXBvcnRGaWx0ZXJLZXldOyB0cmFjayBrZXkpIHtcbiAgICAgICAgICA8ZnMtZmlsdGVyIFtjb25maWddPVwicmVwb3J0RmlsdGVyQ29uZmlnKClcIj48L2ZzLWZpbHRlcj5cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgPGFwcC1yZXBvcnQtY2FudmFzXG4gICAgICAgIFtyZXBvcnRdPVwicmVwb3J0KClcIlxuICAgICAgICBbZWRpdE1vZGVdPVwiZWRpdE1vZGUoKVwiXG4gICAgICAgIChjb21wb25lbnRTZXR0aW5ncyk9XCJjb21wb25lbnRTZXR0aW5ncygkZXZlbnQpXCJcbiAgICAgICAgKHJlcG9ydENoYW5nZWQpPVwib25DYW52YXNSZXBvcnRDaGFuZ2VkKClcIlxuICAgICAgICAocGFnZUNoYW5nZWQpPVwib25DYW52YXNQYWdlQ2hhbmdlZCgkZXZlbnQpXCI+XG4gICAgICA8L2FwcC1yZXBvcnQtY2FudmFzPlxuICAgIH1cbiAgPC9kaXY+XG48L2Rpdj4iXX0=
|