@smarterplan/ngx-smarterplan-core 1.4.20 → 1.4.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/esm2022/lib/components/splat-viewer/splat-viewer.component.mjs +361 -0
  2. package/esm2022/lib/matterport-extensions/security-camera/SecurityCamera.mjs +28 -7
  3. package/esm2022/lib/ngx-smarterplan-core.module.mjs +9 -4
  4. package/esm2022/lib/services/matterport-object3d.service.mjs +657 -133
  5. package/esm2022/lib/services/matterport-tag.service.mjs +12 -7
  6. package/esm2022/lib/services/matterport.service.mjs +330 -9
  7. package/esm2022/lib/services/obj-to-glb.mjs +649 -0
  8. package/esm2022/lib/services/s3.service.mjs +43 -3
  9. package/esm2022/lib/services/threejs-import.service.mjs +6 -4
  10. package/esm2022/lib/services/threejs.service.mjs +867 -95
  11. package/esm2022/lib/services/utils/matterport-framing.util.mjs +63 -0
  12. package/esm2022/lib/services/viewer.service.mjs +189 -7
  13. package/esm2022/lib/types.service.mjs +3 -1
  14. package/esm2022/public-api.mjs +3 -1
  15. package/fesm2022/smarterplan-ngx-smarterplan-core.mjs +3217 -282
  16. package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
  17. package/lib/components/splat-viewer/splat-viewer.component.d.ts +98 -0
  18. package/lib/ngx-smarterplan-core.module.d.ts +7 -6
  19. package/lib/services/matterport-object3d.service.d.ts +125 -5
  20. package/lib/services/matterport-tag.service.d.ts +5 -5
  21. package/lib/services/matterport.service.d.ts +106 -7
  22. package/lib/services/obj-to-glb.d.ts +66 -0
  23. package/lib/services/s3.service.d.ts +7 -0
  24. package/lib/services/threejs.service.d.ts +336 -16
  25. package/lib/services/utils/matterport-framing.util.d.ts +40 -0
  26. package/lib/services/viewer.service.d.ts +110 -8
  27. package/lib/types.service.d.ts +42 -1
  28. package/package.json +1 -1
  29. package/public-api.d.ts +2 -0
  30. package/src/assets/viewers/splat/chunk-BYLI4YBI.js +17037 -0
  31. package/src/assets/viewers/splat/chunk-LDV6YPMG.js +1 -0
  32. package/src/assets/viewers/splat/chunk-WZ5Z4TU3.js +105719 -0
  33. package/src/assets/viewers/splat/convert-4DRLH77X.js +1 -0
  34. package/src/assets/viewers/splat/convert.html +11 -0
  35. package/src/assets/viewers/splat/converter-SLBB5MHG.js +1 -0
  36. package/src/assets/viewers/splat/index.html +90 -0
  37. package/src/assets/viewers/splat/spz-DEYRea_v-QE6YSGIB.js +5292 -0
  38. package/src/assets/viewers/splat/viewer-W4X3FWQJ.js +2704 -0
  39. package/src/assets/viewers/splat/viewer.css +1096 -0
  40. package/src/assets/viewers/splat/webp.wasm +0 -0
  41. package/src/assets/viewers/splat/worker.mjs +1383 -0
@@ -0,0 +1,361 @@
1
+ import { Component, EventEmitter, HostListener, Input, Output, ViewChild, } from '@angular/core';
2
+ import { Subject, takeUntil } from 'rxjs';
3
+ import { getSignedFile } from '../../services/s3.service';
4
+ import { VIEWER_BACKGROUND } from '../../services/threejs.service';
5
+ import { isStreamedSplatFile } from '../../services/viewer.service';
6
+ import * as i0 from "@angular/core";
7
+ import * as i1 from "../../services/viewer.service";
8
+ import * as i2 from "@angular/platform-browser";
9
+ import * as i3 from "@ngx-translate/core";
10
+ /**
11
+ * Gaussian splats, drawn by SuperSplat's viewer (PlayCanvas) in an iframe, as Matterport's showcase
12
+ * is: the page built from projects/splat-viewer, which each app serves from assets/viewers/splat.
13
+ *
14
+ * The visit's Three.js scene stays underneath, unseen, holding the tags, their calibration and the
15
+ * measurement, as it does for a model it draws itself; this hands the viewer what the scene holds,
16
+ * and turns what is clicked in it into what a click in the scene does - through ViewerService, so
17
+ * that whatever drives the scene (the add buttons, the calibration panel, the measuring tool, the
18
+ * tag lists) drives this as well. Tags clicked come out of ViewerService.threeJsTagClicked$, as
19
+ * clicks in the scene do.
20
+ *
21
+ * What it says of loading is for the app's own loading overlay; see the outputs.
22
+ */
23
+ export class SplatViewerComponent {
24
+ viewerService;
25
+ host;
26
+ sanitizer;
27
+ translate;
28
+ /** The model: the S3 key - or assets/ path - of a splat file, or of a Streamed SOG's lod-meta.json. */
29
+ modelKey = null;
30
+ /** Which of the model's axes is up, from its capture's placement; null for the viewer's own idea. */
31
+ up = null;
32
+ /** How far loading has got, from 0 to 1, or null when the size is not known. 0 when it starts. */
33
+ loadProgress = new EventEmitter();
34
+ loaded = new EventEmitter();
35
+ loadFailed = new EventEmitter();
36
+ /** Said whenever the mode changes, the viewer's own buttons included. */
37
+ modeChange = new EventEmitter();
38
+ frame;
39
+ /**
40
+ * The viewer's page, made once, in ngOnInit: a new object, even for the same address, would make
41
+ * Angular set the iframe's src again, which loads the viewer afresh with nothing in it.
42
+ */
43
+ page;
44
+ /** Whether the page has said it is ready; it says so again each time it starts over. */
45
+ ready = false;
46
+ /** The model handed to the page, so the same one is not downloaded twice. */
47
+ loadedKey = null;
48
+ /** Set by cancelLoad: the page, starting over empty, waits to be asked again (reload). */
49
+ cancelled = false;
50
+ /** Whether the page was last asked to take a click for a calibration. */
51
+ pickingForCalibration = false;
52
+ /** The calibration under way, as ViewerService last said. */
53
+ alignment = null;
54
+ /** Each segment's length of the measurement on the model, as ViewerService last said. */
55
+ measureDistances = [];
56
+ /** The last lod-meta.json handed to the page, as a link of this page's own. */
57
+ streamedMeta = null;
58
+ /** Pictures asked of the page, by the number they were asked with, until it answers. */
59
+ snapshots = new Map();
60
+ snapshotCount = 0;
61
+ destroyed$ = new Subject();
62
+ constructor(viewerService, host, sanitizer, translate) {
63
+ this.viewerService = viewerService;
64
+ this.host = host;
65
+ this.sanitizer = sanitizer;
66
+ this.translate = translate;
67
+ }
68
+ ngOnInit() {
69
+ // In the app's language, which the viewer's controls speak, and in its colours, as this element's
70
+ // styles take them from the app's - by now in the page, where they reach it.
71
+ const style = getComputedStyle(this.host.nativeElement);
72
+ const query = new URLSearchParams({
73
+ lang: this.translate.currentLang || 'en',
74
+ accent: style.color,
75
+ background: style.backgroundColor,
76
+ });
77
+ this.page = this.sanitizer.bypassSecurityTrustResourceUrl(`assets/viewers/splat/index.html?${query}`);
78
+ // The Three.js scene underneath has nothing to draw while this covers it, and the splat viewer
79
+ // needs all the graphics card there is.
80
+ this.viewerService.setThreeJsRenderingPaused(true);
81
+ const on = (source, next) => source.pipe(takeUntil(this.destroyed$)).subscribe(next);
82
+ on(this.viewerService.threeJsTagsChanged$, () => this.postTags());
83
+ // Placing a tag starts from the add buttons, the same in every viewer; the page picks the spot
84
+ // and says where ('picked').
85
+ on(this.viewerService.threeJsPlacementChanged$, (placing) => this.post({ type: 'placing', on: placing }));
86
+ on(this.viewerService.threeJsAlignmentProgress$, (progress) => {
87
+ this.alignment = progress;
88
+ this.postCalibration();
89
+ });
90
+ on(this.viewerService.threeJsAlignmentCancelled$, () => {
91
+ this.alignment = null;
92
+ this.postCalibration();
93
+ });
94
+ on(this.viewerService.threeJsMeasuringChanged$, (measuring) => this.post({ type: 'measuring', on: measuring }));
95
+ on(this.viewerService.threeJsMeasurementProgress$, (progress) => {
96
+ this.measureDistances = progress?.distances ?? [];
97
+ this.postMeasurement(!!progress);
98
+ });
99
+ // A finished measurement stays drawn while its form is open; this is when it goes.
100
+ on(this.viewerService.threeJsMeasurementCleared$, () => {
101
+ this.measureDistances = [];
102
+ this.postMeasurement(false);
103
+ });
104
+ // The camera sent to a tag, from a list: the page points it out.
105
+ on(this.viewerService.threeJsTagFocused$, (id) => this.post({ type: 'goToTag', id }));
106
+ }
107
+ ngOnChanges(changes) {
108
+ if (changes['modelKey'] && this.ready && !this.cancelled) {
109
+ void this.load();
110
+ }
111
+ }
112
+ ngOnDestroy() {
113
+ this.destroyed$.next();
114
+ this.destroyed$.complete();
115
+ this.viewerService.setThreeJsRenderingPaused(false);
116
+ if (this.streamedMeta) {
117
+ URL.revokeObjectURL(this.streamedMeta);
118
+ }
119
+ this.snapshots.forEach((resolve) => resolve(null));
120
+ this.snapshots.clear();
121
+ }
122
+ /** Stops the model loading; nothing is loaded again until reload(). */
123
+ cancelLoad() {
124
+ this.cancelled = true;
125
+ this.loadedKey = null;
126
+ this.post({ type: 'cancel-load' });
127
+ }
128
+ /** Loads the model again, after cancelLoad. */
129
+ reload() {
130
+ this.cancelled = false;
131
+ this.loadedKey = null;
132
+ if (this.ready) {
133
+ void this.load();
134
+ }
135
+ }
136
+ setMode(mode) {
137
+ this.post({ type: 'mode', mode });
138
+ }
139
+ /**
140
+ * The scene as it is on screen, with the measurement drawn on it and no tags, as a JPEG data URL;
141
+ * null when there is no scene to take, or the page does not answer within ten seconds.
142
+ */
143
+ snapshot() {
144
+ const id = ++this.snapshotCount;
145
+ return new Promise((resolve) => {
146
+ this.snapshots.set(id, resolve);
147
+ this.post({ type: 'snapshot', id });
148
+ setTimeout(() => {
149
+ if (this.snapshots.delete(id)) {
150
+ resolve(null);
151
+ }
152
+ }, 10000);
153
+ });
154
+ }
155
+ /** What the page says: that it is ready, how loading goes, what was clicked in it. */
156
+ onMessage(event) {
157
+ // Only this component's page, served from this same origin.
158
+ if (event.source !== this.frame?.nativeElement.contentWindow || event.origin !== window.location.origin) {
159
+ return;
160
+ }
161
+ const message = event.data;
162
+ const aligning = this.viewerService.isThreeJsAligningTags();
163
+ switch (message?.type) {
164
+ case 'ready':
165
+ // A new page, with nothing loaded or armed in it: the first, or the viewer starting over
166
+ // for the next model or after a cancelled load.
167
+ this.ready = true;
168
+ this.loadedKey = null;
169
+ this.pickingForCalibration = false;
170
+ if (!this.cancelled) {
171
+ void this.load();
172
+ }
173
+ this.postTags();
174
+ this.postCalibration();
175
+ this.post({ type: 'measuring', on: this.viewerService.isThreeJsMeasuring() });
176
+ this.postMeasurement(this.viewerService.getThreeJsMeasurePointsInModelFrame().length > 0);
177
+ break;
178
+ case 'progress':
179
+ this.loadProgress.emit(message.fraction ?? null);
180
+ break;
181
+ case 'loaded':
182
+ this.loaded.emit();
183
+ break;
184
+ case 'error':
185
+ console.error('Splat viewer:', message.message);
186
+ this.loadFailed.emit(String(message.message ?? ''));
187
+ break;
188
+ case 'mode':
189
+ this.modeChange.emit(message.mode);
190
+ break;
191
+ case 'picked':
192
+ if (aligning && this.alignment?.awaiting === 'point') {
193
+ // Where the tag being matched really is, as a click on the model in the scene would say.
194
+ if (!this.viewerService.matchThreeJsAlignmentTagAtModelPoint(message.position)) {
195
+ this.viewerService.ignoreThreeJsAlignmentClick('point');
196
+ }
197
+ break;
198
+ }
199
+ // As a click on the model in the scene: the app opens the creation form from threeJsTagPlaced$.
200
+ this.viewerService.placeThreeJsTagAtModelPoint(message.position, message.normal ?? null);
201
+ break;
202
+ case 'placing-cancelled':
203
+ // Escape, as in the scene: it ends a measurement or a calibration as it ends placing a tag.
204
+ if (this.viewerService.isThreeJsMeasuring()) {
205
+ this.viewerService.action_toolbox_cancel_mesure();
206
+ }
207
+ else if (aligning) {
208
+ this.viewerService.cancelThreeJsTagAlignment();
209
+ }
210
+ else {
211
+ this.viewerService.cancelThreeJsTagPlacement();
212
+ }
213
+ break;
214
+ case 'calibration-undo':
215
+ // Ctrl+Z or Backspace in the page, as in the scene.
216
+ this.viewerService.undoThreeJsAlignmentStep();
217
+ break;
218
+ case 'measure-point':
219
+ this.viewerService.addThreeJsMeasurePointAtModelPoint(message.position);
220
+ break;
221
+ case 'tagClick':
222
+ if (aligning) {
223
+ // Calibrating: a tag clicked is the one to match, as in the scene, and opens nothing.
224
+ if (this.alignment?.awaiting !== 'tag' || !this.viewerService.selectThreeJsAlignmentTag(message.id)) {
225
+ this.viewerService.ignoreThreeJsAlignmentClick('tag');
226
+ }
227
+ break;
228
+ }
229
+ this.viewerService.clickThreeJsTag(message.id);
230
+ break;
231
+ case 'snapshot':
232
+ this.snapshots.get(message.id)?.(message.image ?? null);
233
+ this.snapshots.delete(message.id);
234
+ break;
235
+ case 'sign': {
236
+ // A piece of the Streamed SOG open, by its name beside lod-meta.json.
237
+ const key = this.loadedKey;
238
+ const folder = key ? key.slice(0, key.lastIndexOf('/') + 1) : '';
239
+ const file = String(message.file ?? '');
240
+ const allowed = key && isStreamedSplatFile(key) && /^[\w.-]+$/.test(file);
241
+ (allowed ? getSignedFile(folder + file) : Promise.resolve(null)).then((url) => {
242
+ this.post({ type: 'signed', id: message.id, url });
243
+ });
244
+ break;
245
+ }
246
+ default:
247
+ break;
248
+ }
249
+ }
250
+ async load() {
251
+ const key = this.modelKey;
252
+ if (!key || key === this.loadedKey) {
253
+ return;
254
+ }
255
+ this.loadedKey = key;
256
+ this.loadProgress.emit(0);
257
+ let url = key.startsWith('assets/') ? key : await getSignedFile(key);
258
+ if (url && isStreamedSplatFile(key)) {
259
+ url = await this.streamedEntry(url);
260
+ }
261
+ if (this.loadedKey !== key) {
262
+ // Another model was asked for, or the load cancelled, while the link was being signed.
263
+ return;
264
+ }
265
+ this.post({ type: 'load', url, fileName: key.split('/').pop(), up: this.up });
266
+ }
267
+ /**
268
+ * The lod-meta.json of a Streamed SOG, with its pieces named so that the page asks for each of
269
+ * them when it needs it ('sign').
270
+ *
271
+ * The pieces sit beside lod-meta.json in S3 and are named relative to it, which a signed link -
272
+ * its signature in the query - does not survive. And they are fetched while the visit lasts, as the
273
+ * camera reaches them, long after any link signed up front has run out.
274
+ */
275
+ async streamedEntry(signedMeta) {
276
+ const meta = await (await fetch(signedMeta)).json();
277
+ meta.filenames = (meta.filenames ?? []).map((name) => `https://splat.invalid/${name}`);
278
+ if (this.streamedMeta) {
279
+ URL.revokeObjectURL(this.streamedMeta);
280
+ }
281
+ this.streamedMeta = URL.createObjectURL(new Blob([JSON.stringify(meta)], { type: 'application/json' }));
282
+ return this.streamedMeta;
283
+ }
284
+ postTags() {
285
+ const { tags, alignment } = this.viewerService.getThreeJsTagSnapshot();
286
+ // Tags placed elsewhere - in Matterport, in another model - are in that place's coordinates,
287
+ // and only a calibration says where they are in this one. Without it they float off the model
288
+ // and seem to slide as the camera moves: they are left out until it is calibrated. A
289
+ // calibration under way, or its panel open, shows them all: they are what it matches.
290
+ const all = this.viewerService.isThreeJsCalibrated() || this.viewerService.isThreeJsShowingCalibrationTags();
291
+ const shown = all ? tags : tags.filter((tag) => tag.inModelFrame);
292
+ this.post({
293
+ type: 'tags',
294
+ tags: shown.map(({ id, position, label, iconUrl, inModelFrame, normal }) => ({ id, position, label, iconUrl, inModelFrame, normal })),
295
+ alignment,
296
+ });
297
+ }
298
+ /**
299
+ * The calibration under way, for the page to draw - the pins dropped, the tag being matched - and,
300
+ * while the place of a tag is asked for, to take the click on the model that says where ('picked').
301
+ */
302
+ postCalibration() {
303
+ const on = this.viewerService.isThreeJsAligningTags() && !!this.alignment;
304
+ const awaiting = on ? this.alignment.awaiting : null;
305
+ this.post({
306
+ type: 'calibration',
307
+ calibration: on
308
+ ? {
309
+ selected: awaiting === 'point' ? this.alignment.tagId ?? null : null,
310
+ pairs: this.alignment.matched.map(({ tagId, target }) => ({ tagId, target })),
311
+ }
312
+ : null,
313
+ });
314
+ const picking = awaiting === 'point';
315
+ // Only when it changes: placing a tag uses the same click, and is not to be switched off here.
316
+ if (picking !== this.pickingForCalibration) {
317
+ this.pickingForCalibration = picking;
318
+ this.post({ type: 'placing', on: picking });
319
+ }
320
+ }
321
+ /** The measurement for the page to draw - its points, each segment's length - or nothing. */
322
+ postMeasurement(drawn) {
323
+ this.post({
324
+ type: 'measurement',
325
+ measurement: drawn
326
+ ? { points: this.viewerService.getThreeJsMeasurePointsInModelFrame(), distances: this.measureDistances }
327
+ : null,
328
+ });
329
+ }
330
+ post(command) {
331
+ const page = this.frame?.nativeElement.contentWindow;
332
+ if (page && this.ready) {
333
+ page.postMessage(command, window.location.origin);
334
+ }
335
+ }
336
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SplatViewerComponent, deps: [{ token: i1.ViewerService }, { token: i0.ElementRef }, { token: i2.DomSanitizer }, { token: i3.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
337
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SplatViewerComponent, selector: "lib-splat-viewer", inputs: { modelKey: "modelKey", up: "up" }, outputs: { loadProgress: "loadProgress", loaded: "loaded", loadFailed: "loadFailed", modeChange: "modeChange" }, host: { listeners: { "window:message": "onMessage($event)" } }, viewQueries: [{ propertyName: "frame", first: true, predicate: ["frame"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: '<iframe #frame [src]="page" title="3D" allow="fullscreen; xr-spatial-tracking" allowfullscreen></iframe>', isInline: true, styles: [":host{display:block;color:var(--smarterplan-primary, rgb(83, 182, 187));background-color:var(--smarterplan-viewer-background, #e0f7ff)}iframe{display:block;width:100%;height:100%;border:0}\n"] });
338
+ }
339
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SplatViewerComponent, decorators: [{
340
+ type: Component,
341
+ args: [{ selector: 'lib-splat-viewer', template: '<iframe #frame [src]="page" title="3D" allow="fullscreen; xr-spatial-tracking" allowfullscreen></iframe>', styles: [":host{display:block;color:var(--smarterplan-primary, rgb(83, 182, 187));background-color:var(--smarterplan-viewer-background, #e0f7ff)}iframe{display:block;width:100%;height:100%;border:0}\n"] }]
342
+ }], ctorParameters: () => [{ type: i1.ViewerService }, { type: i0.ElementRef }, { type: i2.DomSanitizer }, { type: i3.TranslateService }], propDecorators: { modelKey: [{
343
+ type: Input
344
+ }], up: [{
345
+ type: Input
346
+ }], loadProgress: [{
347
+ type: Output
348
+ }], loaded: [{
349
+ type: Output
350
+ }], loadFailed: [{
351
+ type: Output
352
+ }], modeChange: [{
353
+ type: Output
354
+ }], frame: [{
355
+ type: ViewChild,
356
+ args: ['frame', { static: true }]
357
+ }], onMessage: [{
358
+ type: HostListener,
359
+ args: ['window:message', ['$event']]
360
+ }] } });
361
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BsYXQtdmlld2VyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1zbWFydGVycGxhbi1jb3JlL3NyYy9saWIvY29tcG9uZW50cy9zcGxhdC12aWV3ZXIvc3BsYXQtdmlld2VyLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUFjLFlBQVksRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFnQyxNQUFNLEVBQWlCLFNBQVMsR0FDekgsTUFBTSxlQUFlLENBQUM7QUFHdkIsT0FBTyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFMUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzFELE9BQU8sRUFBNEIsaUJBQWlCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUM3RixPQUFPLEVBQUUsbUJBQW1CLEVBQWlCLE1BQU0sK0JBQStCLENBQUM7Ozs7O0FBS25GOzs7Ozs7Ozs7Ozs7R0FZRztBQWVILE1BQU0sT0FBTyxvQkFBb0I7SUFzRHJCO0lBQ0E7SUFDQTtJQUNBO0lBeERWLHVHQUF1RztJQUM5RixRQUFRLEdBQWtCLElBQUksQ0FBQztJQUV4QyxxR0FBcUc7SUFDNUYsRUFBRSxHQUEwQixJQUFJLENBQUM7SUFFMUMsa0dBQWtHO0lBQ3hGLFlBQVksR0FBRyxJQUFJLFlBQVksRUFBaUIsQ0FBQztJQUVqRCxNQUFNLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztJQUVsQyxVQUFVLEdBQUcsSUFBSSxZQUFZLEVBQVUsQ0FBQztJQUVsRCx5RUFBeUU7SUFDL0QsVUFBVSxHQUFHLElBQUksWUFBWSxFQUFtQixDQUFDO0lBRXJCLEtBQUssQ0FBaUM7SUFFNUU7OztPQUdHO0lBQ0gsSUFBSSxDQUFtQjtJQUV2Qix3RkFBd0Y7SUFDaEYsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUV0Qiw2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RixlQUFlLENBQUMsS0FBYztRQUNwQyxJQUFJLENBQUMsSUFBSSxDQUFDO1lBQ1IsSUFBSSxFQUFFLGFBQWE7WUFDbkIsV0FBVyxFQUFFLEtBQUs7Z0JBQ2hCLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLG1DQUFtQyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxnQkFBZ0IsRUFBRTtnQkFDeEcsQ0FBQyxDQUFDLElBQUk7U0FDVCxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU8sSUFBSSxDQUFDLE9BQWU7UUFDMUIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssRUFBRSxhQUFhLENBQUMsYUFBYSxDQUFDO1FBQ3JELElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3BELENBQUM7SUFDSCxDQUFDO3dHQXRWVSxvQkFBb0I7NEZBQXBCLG9CQUFvQix3WkFackIsMEdBQTBHOzs0RkFZekcsb0JBQW9CO2tCQWRoQyxTQUFTOytCQUNFLGtCQUFrQixZQUNsQiwwR0FBMEc7cUtBYzNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBR0csRUFBRTtzQkFBVixLQUFLO2dCQUdJLFlBQVk7c0JBQXJCLE1BQU07Z0JBRUcsTUFBTTtzQkFBZixNQUFNO2dCQUVHLFVBQVU7c0JBQW5CLE1BQU07Z0JBR0csVUFBVTtzQkFBbkIsTUFBTTtnQkFFK0IsS0FBSztzQkFBMUMsU0FBUzt1QkFBQyxPQUFPLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO2dCQThJcEMsU0FBUztzQkFEUixZQUFZO3VCQUFDLGdCQUFnQixFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ29tcG9uZW50LCBFbGVtZW50UmVmLCBFdmVudEVtaXR0ZXIsIEhvc3RMaXN0ZW5lciwgSW5wdXQsIE9uQ2hhbmdlcywgT25EZXN0cm95LCBPbkluaXQsIE91dHB1dCwgU2ltcGxlQ2hhbmdlcywgVmlld0NoaWxkLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IERvbVNhbml0aXplciwgU2FmZVJlc291cmNlVXJsIH0gZnJvbSAnQGFuZ3VsYXIvcGxhdGZvcm0tYnJvd3Nlcic7XG5pbXBvcnQgeyBUcmFuc2xhdGVTZXJ2aWNlIH0gZnJvbSAnQG5neC10cmFuc2xhdGUvY29yZSc7XG5pbXBvcnQgeyBTdWJqZWN0LCB0YWtlVW50aWwgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IFRocmVlSnNNb2RlbFVwIH0gZnJvbSAnLi4vLi4vaGVscGVycy5zZXJ2aWNlJztcbmltcG9ydCB7IGdldFNpZ25lZEZpbGUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9zMy5zZXJ2aWNlJztcbmltcG9ydCB7IFRocmVlSnNBbGlnbm1lbnRQcm9ncmVzcywgVklFV0VSX0JBQ0tHUk9VTkQgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy90aHJlZWpzLnNlcnZpY2UnO1xuaW1wb3J0IHsgaXNTdHJlYW1lZFNwbGF0RmlsZSwgVmlld2VyU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3ZpZXdlci5zZXJ2aWNlJztcblxuLyoqIEhvdyB0aGUgc3BsYXQgdmlld2VyIGlzIG1vdmVkIHRocm91Z2g6IGFyb3VuZCB0aGUgbW9kZWwsIG9yIGluc2lkZSBpdC4gKi9cbmV4cG9ydCB0eXBlIFNwbGF0Vmlld2VyTW9kZSA9ICdvcmJpdCcgfCAnd2Fsayc7XG5cbi8qKlxuICogR2F1c3NpYW4gc3BsYXRzLCBkcmF3biBieSBTdXBlclNwbGF0J3Mgdmlld2VyIChQbGF5Q2FudmFzKSBpbiBhbiBpZnJhbWUsIGFzIE1hdHRlcnBvcnQncyBzaG93Y2FzZVxuICogaXM6IHRoZSBwYWdlIGJ1aWx0IGZyb20gcHJvamVjdHMvc3BsYXQtdmlld2VyLCB3aGljaCBlYWNoIGFwcCBzZXJ2ZXMgZnJvbSBhc3NldHMvdmlld2Vycy9zcGxhdC5cbiAqXG4gKiBUaGUgdmlzaXQncyBUaHJlZS5qcyBzY2VuZSBzdGF5cyB1bmRlcm5lYXRoLCB1bnNlZW4sIGhvbGRpbmcgdGhlIHRhZ3MsIHRoZWlyIGNhbGlicmF0aW9uIGFuZCB0aGVcbiAqIG1lYXN1cmVtZW50LCBhcyBpdCBkb2VzIGZvciBhIG1vZGVsIGl0IGRyYXdzIGl0c2VsZjsgdGhpcyBoYW5kcyB0aGUgdmlld2VyIHdoYXQgdGhlIHNjZW5lIGhvbGRzLFxuICogYW5kIHR1cm5zIHdoYXQgaXMgY2xpY2tlZCBpbiBpdCBpbnRvIHdoYXQgYSBjbGljayBpbiB0aGUgc2NlbmUgZG9lcyAtIHRocm91Z2ggVmlld2VyU2VydmljZSwgc29cbiAqIHRoYXQgd2hhdGV2ZXIgZHJpdmVzIHRoZSBzY2VuZSAodGhlIGFkZCBidXR0b25zLCB0aGUgY2FsaWJyYXRpb24gcGFuZWwsIHRoZSBtZWFzdXJpbmcgdG9vbCwgdGhlXG4gKiB0YWcgbGlzdHMpIGRyaXZlcyB0aGlzIGFzIHdlbGwuIFRhZ3MgY2xpY2tlZCBjb21lIG91dCBvZiBWaWV3ZXJTZXJ2aWNlLnRocmVlSnNUYWdDbGlja2VkJCwgYXNcbiAqIGNsaWNrcyBpbiB0aGUgc2NlbmUgZG8uXG4gKlxuICogV2hhdCBpdCBzYXlzIG9mIGxvYWRpbmcgaXMgZm9yIHRoZSBhcHAncyBvd24gbG9hZGluZyBvdmVybGF5OyBzZWUgdGhlIG91dHB1dHMuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2xpYi1zcGxhdC12aWV3ZXInLFxuICB0ZW1wbGF0ZTogJzxpZnJhbWUgI2ZyYW1lIFtzcmNdPVwicGFnZVwiIHRpdGxlPVwiM0RcIiBhbGxvdz1cImZ1bGxzY3JlZW47IHhyLXNwYXRpYWwtdHJhY2tpbmdcIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+JyxcbiAgLy8gVGhlIGFwcCdzIGNvbG91cnMsIHdoaWNoIHRoZSBwYWdlIGlzIG9wZW5lZCB3aXRoIChzZWUgbmdPbkluaXQpOiBjb2xvciBpcyBpdHMgcHJpbWFyeSwgZm9yIHRoZVxuICAvLyB2aWV3ZXIncyBjb250cm9scywgYW5kIGJhY2tncm91bmQtY29sb3IgdGhlIDNEIHZpZXdlcnMnIGJhY2tncm91bmQsIHRoZSBUaHJlZS5qcyBzY2VuZSdzIHRvby5cbiAgc3R5bGVzOiBbYFxuICAgIDpob3N0IHtcbiAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgY29sb3I6IHZhcigtLXNtYXJ0ZXJwbGFuLXByaW1hcnksIHJnYig4MywgMTgyLCAxODcpKTtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6IHZhcigtLXNtYXJ0ZXJwbGFuLXZpZXdlci1iYWNrZ3JvdW5kLCAke1ZJRVdFUl9CQUNLR1JPVU5EfSk7XG4gICAgfVxuICAgIGlmcmFtZSB7IGRpc3BsYXk6IGJsb2NrOyB3aWR0aDogMTAwJTsgaGVpZ2h0OiAxMDAlOyBib3JkZXI6IDA7IH1cbiAgYF0sXG59KVxuZXhwb3J0IGNsYXNzIFNwbGF0Vmlld2VyQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSB7XG4gIC8qKiBUaGUgbW9kZWw6IHRoZSBTMyBrZXkgLSBvciBhc3NldHMvIHBhdGggLSBvZiBhIHNwbGF0IGZpbGUsIG9yIG9mIGEgU3RyZWFtZWQgU09HJ3MgbG9kLW1ldGEuanNvbi4gKi9cbiAgQElucHV0KCkgbW9kZWxLZXk6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8qKiBXaGljaCBvZiB0aGUgbW9kZWwncyBheGVzIGlzIHVwLCBmcm9tIGl0cyBjYXB0dXJlJ3MgcGxhY2VtZW50OyBudWxsIGZvciB0aGUgdmlld2VyJ3Mgb3duIGlkZWEuICovXG4gIEBJbnB1dCgpIHVwOiBUaHJlZUpzTW9kZWxVcCB8IG51bGwgPSBudWxsO1xuXG4gIC8qKiBIb3cgZmFyIGxvYWRpbmcgaGFzIGdvdCwgZnJvbSAwIHRvIDEsIG9yIG51bGwgd2hlbiB0aGUgc2l6ZSBpcyBub3Qga25vd24uIDAgd2hlbiBpdCBzdGFydHMuICovXG4gIEBPdXRwdXQoKSBsb2FkUHJvZ3Jlc3MgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IG51bGw+KCk7XG5cbiAgQE91dHB1dCgpIGxvYWRlZCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcblxuICBAT3V0cHV0KCkgbG9hZEZhaWxlZCA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuXG4gIC8qKiBTYWlkIHdoZW5ldmVyIHRoZSBtb2RlIGNoYW5nZXMsIHRoZSB2aWV3ZXIncyBvd24gYnV0dG9ucyBpbmNsdWRlZC4gKi9cbiAgQE91dHB1dCgpIG1vZGVDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPFNwbGF0Vmlld2VyTW9kZT4oKTtcblxuICBAVmlld0NoaWxkKCdmcmFtZScsIHsgc3RhdGljOiB0cnVlIH0pIGZyYW1lITogRWxlbWVudFJlZjxIVE1MSUZyYW1lRWxlbWVudD47XG5cbiAgLyoqXG4gICAqIFRoZSB2aWV3ZXIncyBwYWdlLCBtYWRlIG9uY2UsIGluIG5nT25Jbml0OiBhIG5ldyBvYmplY3QsIGV2ZW4gZm9yIHRoZSBzYW1lIGFkZHJlc3MsIHdvdWxkIG1ha2VcbiAgICogQW5ndWxhciBzZXQgdGhlIGlmcmFtZSdzIHNyYyBhZ2Fpbiwgd2hpY2ggbG9hZHMgdGhlIHZpZXdlciBhZnJlc2ggd2l0aCBub3RoaW5nIGluIGl0LlxuICAgKi9cbiAgcGFnZSE6IFNhZmVSZXNvdXJjZVVybDtcblxuICAvKiogV2hldGhlciB0aGUgcGFnZSBoYXMgc2FpZCBpdCBpcyByZWFkeTsgaXQgc2F5cyBzbyBhZ2FpbiBlYWNoIHRpbWUgaXQgc3RhcnRzIG92ZXIuICovXG4gIHByaXZhdGUgcmVhZHkgPSBmYWxzZTtcblxuICAvKiogVGhlIG1vZGVsIGhhbmRlZCB0byB0aGUgcGFnZSwgc28gdGhlIHNhbWUgb25lIGlzIG5vdCBkb3dubG9hZGVkIHR3aWNlLiAqL1xuICBwcml2YXRlIGxvYWRlZEtleTogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG5cbiAgLyoqIFNldCBieSBjYW5jZWxMb2FkOiB0aGUgcGFnZSwgc3RhcnRpbmcgb3ZlciBlbXB0eSwgd2FpdHMgdG8gYmUgYXNrZWQgYWdhaW4gKHJlbG9hZCkuICovXG4gIHByaXZhdGUgY2FuY2VsbGVkID0gZmFsc2U7XG5cbiAgLyoqIFdoZXRoZXIgdGhlIHBhZ2Ugd2FzIGxhc3QgYXNrZWQgdG8gdGFrZSBhIGNsaWNrIGZvciBhIGNhbGlicmF0aW9uLiAqL1xuICBwcml2YXRlIHBpY2tpbmdGb3JDYWxpYnJhdGlvbiA9IGZhbHNlO1xuXG4gIC8qKiBUaGUgY2FsaWJyYXRpb24gdW5kZXIgd2F5LCBhcyBWaWV3ZXJTZXJ2aWNlIGxhc3Qgc2FpZC4gKi9cbiAgcHJpdmF0ZSBhbGlnbm1lbnQ6IFRocmVlSnNBbGlnbm1lbnRQcm9ncmVzcyB8IG51bGwgPSBudWxsO1xuXG4gIC8qKiBFYWNoIHNlZ21lbnQncyBsZW5ndGggb2YgdGhlIG1lYXN1cmVtZW50IG9uIHRoZSBtb2RlbCwgYXMgVmlld2VyU2VydmljZSBsYXN0IHNhaWQuICovXG4gIHByaXZhdGUgbWVhc3VyZURpc3RhbmNlczogbnVtYmVyW10gPSBbXTtcblxuICAvKiogVGhlIGxhc3QgbG9kLW1ldGEuanNvbiBoYW5kZWQgdG8gdGhlIHBhZ2UsIGFzIGEgbGluayBvZiB0aGlzIHBhZ2UncyBvd24uICovXG4gIHByaXZhdGUgc3RyZWFtZWRNZXRhOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuICAvKiogUGljdHVyZXMgYXNrZWQgb2YgdGhlIHBhZ2UsIGJ5IHRoZSBudW1iZXIgdGhleSB3ZXJlIGFza2VkIHdpdGgsIHVudGlsIGl0IGFuc3dlcnMuICovXG4gIHByaXZhdGUgc25hcHNob3RzID0gbmV3IE1hcDxudW1iZXIsIChpbWFnZTogc3RyaW5nIHwgbnVsbCkgPT4gdm9pZD4oKTtcblxuICBwcml2YXRlIHNuYXBzaG90Q291bnQgPSAwO1xuXG4gIHByaXZhdGUgZGVzdHJveWVkJCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSB2aWV3ZXJTZXJ2aWNlOiBWaWV3ZXJTZXJ2aWNlLFxuICAgIHByaXZhdGUgaG9zdDogRWxlbWVudFJlZjxIVE1MRWxlbWVudD4sXG4gICAgcHJpdmF0ZSBzYW5pdGl6ZXI6IERvbVNhbml0aXplcixcbiAgICBwcml2YXRlIHRyYW5zbGF0ZTogVHJhbnNsYXRlU2VydmljZSxcbiAgKSB7fVxuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIC8vIEluIHRoZSBhcHAncyBsYW5ndWFnZSwgd2hpY2ggdGhlIHZpZXdlcidzIGNvbnRyb2xzIHNwZWFrLCBhbmQgaW4gaXRzIGNvbG91cnMsIGFzIHRoaXMgZWxlbWVudCdzXG4gICAgLy8gc3R5bGVzIHRha2UgdGhlbSBmcm9tIHRoZSBhcHAncyAtIGJ5IG5vdyBpbiB0aGUgcGFnZSwgd2hlcmUgdGhleSByZWFjaCBpdC5cbiAgICBjb25zdCBzdHlsZSA9IGdldENvbXB1dGVkU3R5bGUodGhpcy5ob3N0Lm5hdGl2ZUVsZW1lbnQpO1xuICAgIGNvbnN0IHF1ZXJ5ID0gbmV3IFVSTFNlYXJjaFBhcmFtcyh7XG4gICAgICBsYW5nOiB0aGlzLnRyYW5zbGF0ZS5jdXJyZW50TGFuZyB8fCAnZW4nLFxuICAgICAgYWNjZW50OiBzdHlsZS5jb2xvcixcbiAgICAgIGJhY2tncm91bmQ6IHN0eWxlLmJhY2tncm91bmRDb2xvcixcbiAgICB9KTtcbiAgICB0aGlzLnBhZ2UgPSB0aGlzLnNhbml0aXplci5ieXBhc3NTZWN1cml0eVRydXN0UmVzb3VyY2VVcmwoYGFzc2V0cy92aWV3ZXJzL3NwbGF0L2luZGV4Lmh0bWw/JHtxdWVyeX1gKTtcblxuICAgIC8vIFRoZSBUaHJlZS5qcyBzY2VuZSB1bmRlcm5lYXRoIGhhcyBub3RoaW5nIHRvIGRyYXcgd2hpbGUgdGhpcyBjb3ZlcnMgaXQsIGFuZCB0aGUgc3BsYXQgdmlld2VyXG4gICAgLy8gbmVlZHMgYWxsIHRoZSBncmFwaGljcyBjYXJkIHRoZXJlIGlzLlxuICAgIHRoaXMudmlld2VyU2VydmljZS5zZXRUaHJlZUpzUmVuZGVyaW5nUGF1c2VkKHRydWUpO1xuICAgIGNvbnN0IG9uID0gPFQ+KHNvdXJjZTogeyBwaXBlOiBGdW5jdGlvbiB9LCBuZXh0OiAodmFsdWU6IFQpID0+IHZvaWQpID0+XG4gICAgICAoc291cmNlLnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveWVkJCkpIGFzIGFueSkuc3Vic2NyaWJlKG5leHQpO1xuXG4gICAgb24odGhpcy52aWV3ZXJTZXJ2aWNlLnRocmVlSnNUYWdzQ2hhbmdlZCQsICgpID0+IHRoaXMucG9zdFRhZ3MoKSk7XG4gICAgLy8gUGxhY2luZyBhIHRhZyBzdGFydHMgZnJvbSB0aGUgYWRkIGJ1dHRvbnMsIHRoZSBzYW1lIGluIGV2ZXJ5IHZpZXdlcjsgdGhlIHBhZ2UgcGlja3MgdGhlIHNwb3RcbiAgICAvLyBhbmQgc2F5cyB3aGVyZSAoJ3BpY2tlZCcpLlxuICAgIG9uPGJvb2xlYW4+KHRoaXMudmlld2VyU2VydmljZS50aHJlZUpzUGxhY2VtZW50Q2hhbmdlZCQsIChwbGFjaW5nKSA9PiB0aGlzLnBvc3QoeyB0eXBlOiAncGxhY2luZycsIG9uOiBwbGFjaW5nIH0pKTtcbiAgICBvbjxUaHJlZUpzQWxpZ25tZW50UHJvZ3Jlc3M+KHRoaXMudmlld2VyU2VydmljZS50aHJlZUpzQWxpZ25tZW50UHJvZ3Jlc3MkLCAocHJvZ3Jlc3MpID0+IHtcbiAgICAgIHRoaXMuYWxpZ25tZW50ID0gcHJvZ3Jlc3M7XG4gICAgICB0aGlzLnBvc3RDYWxpYnJhdGlvbigpO1xuICAgIH0pO1xuICAgIG9uKHRoaXMudmlld2VyU2VydmljZS50aHJlZUpzQWxpZ25tZW50Q2FuY2VsbGVkJCwgKCkgPT4ge1xuICAgICAgdGhpcy5hbGlnbm1lbnQgPSBudWxsO1xuICAgICAgdGhpcy5wb3N0Q2FsaWJyYXRpb24oKTtcbiAgICB9KTtcbiAgICBvbjxib29sZWFuPih0aGlzLnZpZXdlclNlcnZpY2UudGhyZWVKc01lYXN1cmluZ0NoYW5nZWQkLCAobWVhc3VyaW5nKSA9PiB0aGlzLnBvc3QoeyB0eXBlOiAnbWVhc3VyaW5nJywgb246IG1lYXN1cmluZyB9KSk7XG4gICAgb248eyBwb2ludHM6IG51bWJlcjsgZGlzdGFuY2VzOiBudW1iZXJbXSB9IHwgbnVsbD4odGhpcy52aWV3ZXJTZXJ2aWNlLnRocmVlSnNNZWFzdXJlbWVudFByb2dyZXNzJCwgKHByb2dyZXNzKSA9PiB7XG4gICAgICB0aGlzLm1lYXN1cmVEaXN0YW5jZXMgPSBwcm9ncmVzcz8uZGlzdGFuY2VzID8/IFtdO1xuICAgICAgdGhpcy5wb3N0TWVhc3VyZW1lbnQoISFwcm9ncmVzcyk7XG4gICAgfSk7XG4gICAgLy8gQSBmaW5pc2hlZCBtZWFzdXJlbWVudCBzdGF5cyBkcmF3biB3aGlsZSBpdHMgZm9ybSBpcyBvcGVuOyB0aGlzIGlzIHdoZW4gaXQgZ29lcy5cbiAgICBvbih0aGlzLnZpZXdlclNlcnZpY2UudGhyZWVKc01lYXN1cmVtZW50Q2xlYXJlZCQsICgpID0+IHtcbiAgICAgIHRoaXMubWVhc3VyZURpc3RhbmNlcyA9IFtdO1xuICAgICAgdGhpcy5wb3N0TWVhc3VyZW1lbnQoZmFsc2UpO1xuICAgIH0pO1xuICAgIC8vIFRoZSBjYW1lcmEgc2VudCB0byBhIHRhZywgZnJvbSBhIGxpc3Q6IHRoZSBwYWdlIHBvaW50cyBpdCBvdXQuXG4gICAgb248c3RyaW5nPih0aGlzLnZpZXdlclNlcnZpY2UudGhyZWVKc1RhZ0ZvY3VzZWQkLCAoaWQpID0+IHRoaXMucG9zdCh7IHR5cGU6ICdnb1RvVGFnJywgaWQgfSkpO1xuICB9XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIGlmIChjaGFuZ2VzWydtb2RlbEtleSddICYmIHRoaXMucmVhZHkgJiYgIXRoaXMuY2FuY2VsbGVkKSB7XG4gICAgICB2b2lkIHRoaXMubG9hZCgpO1xuICAgIH1cbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuZGVzdHJveWVkJC5uZXh0KCk7XG4gICAgdGhpcy5kZXN0cm95ZWQkLmNvbXBsZXRlKCk7XG4gICAgdGhpcy52aWV3ZXJTZXJ2aWNlLnNldFRocmVlSnNSZW5kZXJpbmdQYXVzZWQoZmFsc2UpO1xuICAgIGlmICh0aGlzLnN0cmVhbWVkTWV0YSkge1xuICAgICAgVVJMLnJldm9rZU9iamVjdFVSTCh0aGlzLnN0cmVhbWVkTWV0YSk7XG4gICAgfVxuICAgIHRoaXMuc25hcHNob3RzLmZvckVhY2goKHJlc29sdmUpID0+IHJlc29sdmUobnVsbCkpO1xuICAgIHRoaXMuc25hcHNob3RzLmNsZWFyKCk7XG4gIH1cblxuICAvKiogU3RvcHMgdGhlIG1vZGVsIGxvYWRpbmc7IG5vdGhpbmcgaXMgbG9hZGVkIGFnYWluIHVudGlsIHJlbG9hZCgpLiAqL1xuICBjYW5jZWxMb2FkKCk6IHZvaWQge1xuICAgIHRoaXMuY2FuY2VsbGVkID0gdHJ1ZTtcbiAgICB0aGlzLmxvYWRlZEtleSA9IG51bGw7XG4gICAgdGhpcy5wb3N0KHsgdHlwZTogJ2NhbmNlbC1sb2FkJyB9KTtcbiAgfVxuXG4gIC8qKiBMb2FkcyB0aGUgbW9kZWwgYWdhaW4sIGFmdGVyIGNhbmNlbExvYWQuICovXG4gIHJlbG9hZCgpOiB2b2lkIHtcbiAgICB0aGlzLmNhbmNlbGxlZCA9IGZhbHNlO1xuICAgIHRoaXMubG9hZGVkS2V5ID0gbnVsbDtcbiAgICBpZiAodGhpcy5yZWFkeSkge1xuICAgICAgdm9pZCB0aGlzLmxvYWQoKTtcbiAgICB9XG4gIH1cblxuICBzZXRNb2RlKG1vZGU6IFNwbGF0Vmlld2VyTW9kZSk6IHZvaWQge1xuICAgIHRoaXMucG9zdCh7IHR5cGU6ICdtb2RlJywgbW9kZSB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgc2NlbmUgYXMgaXQgaXMgb24gc2NyZWVuLCB3aXRoIHRoZSBtZWFzdXJlbWVudCBkcmF3biBvbiBpdCBhbmQgbm8gdGFncywgYXMgYSBKUEVHIGRhdGEgVVJMO1xuICAgKiBudWxsIHdoZW4gdGhlcmUgaXMgbm8gc2NlbmUgdG8gdGFrZSwgb3IgdGhlIHBhZ2UgZG9lcyBub3QgYW5zd2VyIHdpdGhpbiB0ZW4gc2Vjb25kcy5cbiAgICovXG4gIHNuYXBzaG90KCk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICAgIGNvbnN0IGlkID0gKyt0aGlzLnNuYXBzaG90Q291bnQ7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICB0aGlzLnNuYXBzaG90cy5zZXQoaWQsIHJlc29sdmUpO1xuICAgICAgdGhpcy5wb3N0KHsgdHlwZTogJ3NuYXBzaG90JywgaWQgfSk7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgaWYgKHRoaXMuc25hcHNob3RzLmRlbGV0ZShpZCkpIHtcbiAgICAgICAgICByZXNvbHZlKG51bGwpO1xuICAgICAgICB9XG4gICAgICB9LCAxMDAwMCk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogV2hhdCB0aGUgcGFnZSBzYXlzOiB0aGF0IGl0IGlzIHJlYWR5LCBob3cgbG9hZGluZyBnb2VzLCB3aGF0IHdhcyBjbGlja2VkIGluIGl0LiAqL1xuICBASG9zdExpc3RlbmVyKCd3aW5kb3c6bWVzc2FnZScsIFsnJGV2ZW50J10pXG4gIG9uTWVzc2FnZShldmVudDogTWVzc2FnZUV2ZW50KTogdm9pZCB7XG4gICAgLy8gT25seSB0aGlzIGNvbXBvbmVudCdzIHBhZ2UsIHNlcnZlZCBmcm9tIHRoaXMgc2FtZSBvcmlnaW4uXG4gICAgaWYgKGV2ZW50LnNvdXJjZSAhPT0gdGhpcy5mcmFtZT8ubmF0aXZlRWxlbWVudC5jb250ZW50V2luZG93IHx8IGV2ZW50Lm9yaWdpbiAhPT0gd2luZG93LmxvY2F0aW9uLm9yaWdpbikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBtZXNzYWdlID0gZXZlbnQuZGF0YTtcbiAgICBjb25zdCBhbGlnbmluZyA9IHRoaXMudmlld2VyU2VydmljZS5pc1RocmVlSnNBbGlnbmluZ1RhZ3MoKTtcbiAgICBzd2l0Y2ggKG1lc3NhZ2U/LnR5cGUpIHtcbiAgICAgIGNhc2UgJ3JlYWR5JzpcbiAgICAgICAgLy8gQSBuZXcgcGFnZSwgd2l0aCBub3RoaW5nIGxvYWRlZCBvciBhcm1lZCBpbiBpdDogdGhlIGZpcnN0LCBvciB0aGUgdmlld2VyIHN0YXJ0aW5nIG92ZXJcbiAgICAgICAgLy8gZm9yIHRoZSBuZXh0IG1vZGVsIG9yIGFmdGVyIGEgY2FuY2VsbGVkIGxvYWQuXG4gICAgICAgIHRoaXMucmVhZHkgPSB0cnVlO1xuICAgICAgICB0aGlzLmxvYWRlZEtleSA9IG51bGw7XG4gICAgICAgIHRoaXMucGlja2luZ0ZvckNhbGlicmF0aW9uID0gZmFsc2U7XG4gICAgICAgIGlmICghdGhpcy5jYW5jZWxsZWQpIHtcbiAgICAgICAgICB2b2lkIHRoaXMubG9hZCgpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMucG9zdFRhZ3MoKTtcbiAgICAgICAgdGhpcy5wb3N0Q2FsaWJyYXRpb24oKTtcbiAgICAgICAgdGhpcy5wb3N0KHsgdHlwZTogJ21lYXN1cmluZycsIG9uOiB0aGlzLnZpZXdlclNlcnZpY2UuaXNUaHJlZUpzTWVhc3VyaW5nKCkgfSk7XG4gICAgICAgIHRoaXMucG9zdE1lYXN1cmVtZW50KHRoaXMudmlld2VyU2VydmljZS5nZXRUaHJlZUpzTWVhc3VyZVBvaW50c0luTW9kZWxGcmFtZSgpLmxlbmd0aCA+IDApO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3Byb2dyZXNzJzpcbiAgICAgICAgdGhpcy5sb2FkUHJvZ3Jlc3MuZW1pdChtZXNzYWdlLmZyYWN0aW9uID8/IG51bGwpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ2xvYWRlZCc6XG4gICAgICAgIHRoaXMubG9hZGVkLmVtaXQoKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdlcnJvcic6XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ1NwbGF0IHZpZXdlcjonLCBtZXNzYWdlLm1lc3NhZ2UpO1xuICAgICAgICB0aGlzLmxvYWRGYWlsZWQuZW1pdChTdHJpbmcobWVzc2FnZS5tZXNzYWdlID8/ICcnKSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnbW9kZSc6XG4gICAgICAgIHRoaXMubW9kZUNoYW5nZS5lbWl0KG1lc3NhZ2UubW9kZSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAncGlja2VkJzpcbiAgICAgICAgaWYgKGFsaWduaW5nICYmIHRoaXMuYWxpZ25tZW50Py5hd2FpdGluZyA9PT0gJ3BvaW50Jykge1xuICAgICAgICAgIC8vIFdoZXJlIHRoZSB0YWcgYmVpbmcgbWF0Y2hlZCByZWFsbHkgaXMsIGFzIGEgY2xpY2sgb24gdGhlIG1vZGVsIGluIHRoZSBzY2VuZSB3b3VsZCBzYXkuXG4gICAgICAgICAgaWYgKCF0aGlzLnZpZXdlclNlcnZpY2UubWF0Y2hUaHJlZUpzQWxpZ25tZW50VGFnQXRNb2RlbFBvaW50KG1lc3NhZ2UucG9zaXRpb24pKSB7XG4gICAgICAgICAgICB0aGlzLnZpZXdlclNlcnZpY2UuaWdub3JlVGhyZWVKc0FsaWdubWVudENsaWNrKCdwb2ludCcpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICAvLyBBcyBhIGNsaWNrIG9uIHRoZSBtb2RlbCBpbiB0aGUgc2NlbmU6IHRoZSBhcHAgb3BlbnMgdGhlIGNyZWF0aW9uIGZvcm0gZnJvbSB0aHJlZUpzVGFnUGxhY2VkJC5cbiAgICAgICAgdGhpcy52aWV3ZXJTZXJ2aWNlLnBsYWNlVGhyZWVKc1RhZ0F0TW9kZWxQb2ludChtZXNzYWdlLnBvc2l0aW9uLCBtZXNzYWdlLm5vcm1hbCA/PyBudWxsKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdwbGFjaW5nLWNhbmNlbGxlZCc6XG4gICAgICAgIC8vIEVzY2FwZSwgYXMgaW4gdGhlIHNjZW5lOiBpdCBlbmRzIGEgbWVhc3VyZW1lbnQgb3IgYSBjYWxpYnJhdGlvbiBhcyBpdCBlbmRzIHBsYWNpbmcgYSB0YWcuXG4gICAgICAgIGlmICh0aGlzLnZpZXdlclNlcnZpY2UuaXNUaHJlZUpzTWVhc3VyaW5nKCkpIHtcbiAgICAgICAgICB0aGlzLnZpZXdlclNlcnZpY2UuYWN0aW9uX3Rvb2xib3hfY2FuY2VsX21lc3VyZSgpO1xuICAgICAgICB9IGVsc2UgaWYgKGFsaWduaW5nKSB7XG4gICAgICAgICAgdGhpcy52aWV3ZXJTZXJ2aWNlLmNhbmNlbFRocmVlSnNUYWdBbGlnbm1lbnQoKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLnZpZXdlclNlcnZpY2UuY2FuY2VsVGhyZWVKc1RhZ1BsYWNlbWVudCgpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnY2FsaWJyYXRpb24tdW5kbyc6XG4gICAgICAgIC8vIEN0cmwrWiBvciBCYWNrc3BhY2UgaW4gdGhlIHBhZ2UsIGFzIGluIHRoZSBzY2VuZS5cbiAgICAgICAgdGhpcy52aWV3ZXJTZXJ2aWNlLnVuZG9UaHJlZUpzQWxpZ25tZW50U3RlcCgpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ21lYXN1cmUtcG9pbnQnOlxuICAgICAgICB0aGlzLnZpZXdlclNlcnZpY2UuYWRkVGhyZWVKc01lYXN1cmVQb2ludEF0TW9kZWxQb2ludChtZXNzYWdlLnBvc2l0aW9uKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd0YWdDbGljayc6XG4gICAgICAgIGlmIChhbGlnbmluZykge1xuICAgICAgICAgIC8vIENhbGlicmF0aW5nOiBhIHRhZyBjbGlja2VkIGlzIHRoZSBvbmUgdG8gbWF0Y2gsIGFzIGluIHRoZSBzY2VuZSwgYW5kIG9wZW5zIG5vdGhpbmcuXG4gICAgICAgICAgaWYgKHRoaXMuYWxpZ25tZW50Py5hd2FpdGluZyAhPT0gJ3RhZycgfHwgIXRoaXMudmlld2VyU2VydmljZS5zZWxlY3RUaHJlZUpzQWxpZ25tZW50VGFnKG1lc3NhZ2UuaWQpKSB7XG4gICAgICAgICAgICB0aGlzLnZpZXdlclNlcnZpY2UuaWdub3JlVGhyZWVKc0FsaWdubWVudENsaWNrKCd0YWcnKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy52aWV3ZXJTZXJ2aWNlLmNsaWNrVGhyZWVKc1RhZyhtZXNzYWdlLmlkKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdzbmFwc2hvdCc6XG4gICAgICAgIHRoaXMuc25hcHNob3RzLmdldChtZXNzYWdlLmlkKT8uKG1lc3NhZ2UuaW1hZ2UgPz8gbnVsbCk7XG4gICAgICAgIHRoaXMuc25hcHNob3RzLmRlbGV0ZShtZXNzYWdlLmlkKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdzaWduJzoge1xuICAgICAgICAvLyBBIHBpZWNlIG9mIHRoZSBTdHJlYW1lZCBTT0cgb3BlbiwgYnkgaXRzIG5hbWUgYmVzaWRlIGxvZC1tZXRhLmpzb24uXG4gICAgICAgIGNvbnN0IGtleSA9IHRoaXMubG9hZGVkS2V5O1xuICAgICAgICBjb25zdCBmb2xkZXIgPSBrZXkgPyBrZXkuc2xpY2UoMCwga2V5Lmxhc3RJbmRleE9mKCcvJykgKyAxKSA6ICcnO1xuICAgICAgICBjb25zdCBmaWxlID0gU3RyaW5nKG1lc3NhZ2UuZmlsZSA/PyAnJyk7XG4gICAgICAgIGNvbnN0IGFsbG93ZWQgPSBrZXkgJiYgaXNTdHJlYW1lZFNwbGF0RmlsZShrZXkpICYmIC9eW1xcdy4tXSskLy50ZXN0KGZpbGUpO1xuICAgICAgICAoYWxsb3dlZCA/IGdldFNpZ25lZEZpbGUoZm9sZGVyICsgZmlsZSkgOiBQcm9taXNlLnJlc29sdmUobnVsbCkpLnRoZW4oKHVybCkgPT4ge1xuICAgICAgICAgIHRoaXMucG9zdCh7IHR5cGU6ICdzaWduZWQnLCBpZDogbWVzc2FnZS5pZCwgdXJsIH0pO1xuICAgICAgICB9KTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBkZWZhdWx0OlxuICAgICAgICBicmVhaztcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGxvYWQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qga2V5ID0gdGhpcy5tb2RlbEtleTtcbiAgICBpZiAoIWtleSB8fCBrZXkgPT09IHRoaXMubG9hZGVkS2V5KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMubG9hZGVkS2V5ID0ga2V5O1xuICAgIHRoaXMubG9hZFByb2dyZXNzLmVtaXQoMCk7XG4gICAgbGV0IHVybCA9IGtleS5zdGFydHNXaXRoKCdhc3NldHMvJykgPyBrZXkgOiBhd2FpdCBnZXRTaWduZWRGaWxlKGtleSk7XG4gICAgaWYgKHVybCAmJiBpc1N0cmVhbWVkU3BsYXRGaWxlKGtleSkpIHtcbiAgICAgIHVybCA9IGF3YWl0IHRoaXMuc3RyZWFtZWRFbnRyeSh1cmwpO1xuICAgIH1cbiAgICBpZiAodGhpcy5sb2FkZWRLZXkgIT09IGtleSkge1xuICAgICAgLy8gQW5vdGhlciBtb2RlbCB3YXMgYXNrZWQgZm9yLCBvciB0aGUgbG9hZCBjYW5jZWxsZWQsIHdoaWxlIHRoZSBsaW5rIHdhcyBiZWluZyBzaWduZWQuXG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMucG9zdCh7IHR5cGU6ICdsb2FkJywgdXJsLCBmaWxlTmFtZToga2V5LnNwbGl0KCcvJykucG9wKCksIHVwOiB0aGlzLnVwIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBsb2QtbWV0YS5qc29uIG9mIGEgU3RyZWFtZWQgU09HLCB3aXRoIGl0cyBwaWVjZXMgbmFtZWQgc28gdGhhdCB0aGUgcGFnZSBhc2tzIGZvciBlYWNoIG9mXG4gICAqIHRoZW0gd2hlbiBpdCBuZWVkcyBpdCAoJ3NpZ24nKS5cbiAgICpcbiAgICogVGhlIHBpZWNlcyBzaXQgYmVzaWRlIGxvZC1tZXRhLmpzb24gaW4gUzMgYW5kIGFyZSBuYW1lZCByZWxhdGl2ZSB0byBpdCwgd2hpY2ggYSBzaWduZWQgbGluayAtXG4gICAqIGl0cyBzaWduYXR1cmUgaW4gdGhlIHF1ZXJ5IC0gZG9lcyBub3Qgc3Vydml2ZS4gQW5kIHRoZXkgYXJlIGZldGNoZWQgd2hpbGUgdGhlIHZpc2l0IGxhc3RzLCBhcyB0aGVcbiAgICogY2FtZXJhIHJlYWNoZXMgdGhlbSwgbG9uZyBhZnRlciBhbnkgbGluayBzaWduZWQgdXAgZnJvbnQgaGFzIHJ1biBvdXQuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHN0cmVhbWVkRW50cnkoc2lnbmVkTWV0YTogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBjb25zdCBtZXRhID0gYXdhaXQgKGF3YWl0IGZldGNoKHNpZ25lZE1ldGEpKS5qc29uKCk7XG4gICAgbWV0YS5maWxlbmFtZXMgPSAobWV0YS5maWxlbmFtZXMgPz8gW10pLm1hcCgobmFtZTogc3RyaW5nKSA9PiBgaHR0cHM6Ly9zcGxhdC5pbnZhbGlkLyR7bmFtZX1gKTtcbiAgICBpZiAodGhpcy5zdHJlYW1lZE1ldGEpIHtcbiAgICAgIFVSTC5yZXZva2VPYmplY3RVUkwodGhpcy5zdHJlYW1lZE1ldGEpO1xuICAgIH1cbiAgICB0aGlzLnN0cmVhbWVkTWV0YSA9IFVSTC5jcmVhdGVPYmplY3RVUkwobmV3IEJsb2IoW0pTT04uc3RyaW5naWZ5KG1ldGEpXSwgeyB0eXBlOiAnYXBwbGljYXRpb24vanNvbicgfSkpO1xuICAgIHJldHVybiB0aGlzLnN0cmVhbWVkTWV0YTtcbiAgfVxuXG4gIHByaXZhdGUgcG9zdFRhZ3MoKTogdm9pZCB7XG4gICAgY29uc3QgeyB0YWdzLCBhbGlnbm1lbnQgfSA9IHRoaXMudmlld2VyU2VydmljZS5nZXRUaHJlZUpzVGFnU25hcHNob3QoKTtcbiAgICAvLyBUYWdzIHBsYWNlZCBlbHNld2hlcmUgLSBpbiBNYXR0ZXJwb3J0LCBpbiBhbm90aGVyIG1vZGVsIC0gYXJlIGluIHRoYXQgcGxhY2UncyBjb29yZGluYXRlcyxcbiAgICAvLyBhbmQgb25seSBhIGNhbGlicmF0aW9uIHNheXMgd2hlcmUgdGhleSBhcmUgaW4gdGhpcyBvbmUuIFdpdGhvdXQgaXQgdGhleSBmbG9hdCBvZmYgdGhlIG1vZGVsXG4gICAgLy8gYW5kIHNlZW0gdG8gc2xpZGUgYXMgdGhlIGNhbWVyYSBtb3ZlczogdGhleSBhcmUgbGVmdCBvdXQgdW50aWwgaXQgaXMgY2FsaWJyYXRlZC4gQVxuICAgIC8vIGNhbGlicmF0aW9uIHVuZGVyIHdheSwgb3IgaXRzIHBhbmVsIG9wZW4sIHNob3dzIHRoZW0gYWxsOiB0aGV5IGFyZSB3aGF0IGl0IG1hdGNoZXMuXG4gICAgY29uc3QgYWxsID0gdGhpcy52aWV3ZXJTZXJ2aWNlLmlzVGhyZWVKc0NhbGlicmF0ZWQoKSB8fCB0aGlzLnZpZXdlclNlcnZpY2UuaXNUaHJlZUpzU2hvd2luZ0NhbGlicmF0aW9uVGFncygpO1xuICAgIGNvbnN0IHNob3duID0gYWxsID8gdGFncyA6IHRhZ3MuZmlsdGVyKCh0YWcpID0+IHRhZy5pbk1vZGVsRnJhbWUpO1xuICAgIHRoaXMucG9zdCh7XG4gICAgICB0eXBlOiAndGFncycsXG4gICAgICB0YWdzOiBzaG93bi5tYXAoKHsgaWQsIHBvc2l0aW9uLCBsYWJlbCwgaWNvblVybCwgaW5Nb2RlbEZyYW1lLCBub3JtYWwgfSkgPT4gKHsgaWQsIHBvc2l0aW9uLCBsYWJlbCwgaWNvblVybCwgaW5Nb2RlbEZyYW1lLCBub3JtYWwgfSkpLFxuICAgICAgYWxpZ25tZW50LFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBjYWxpYnJhdGlvbiB1bmRlciB3YXksIGZvciB0aGUgcGFnZSB0byBkcmF3IC0gdGhlIHBpbnMgZHJvcHBlZCwgdGhlIHRhZyBiZWluZyBtYXRjaGVkIC0gYW5kLFxuICAgKiB3aGlsZSB0aGUgcGxhY2Ugb2YgYSB0YWcgaXMgYXNrZWQgZm9yLCB0byB0YWtlIHRoZSBjbGljayBvbiB0aGUgbW9kZWwgdGhhdCBzYXlzIHdoZXJlICgncGlja2VkJykuXG4gICAqL1xuICBwcml2YXRlIHBvc3RDYWxpYnJhdGlvbigpOiB2b2lkIHtcbiAgICBjb25zdCBvbiA9IHRoaXMudmlld2VyU2VydmljZS5pc1RocmVlSnNBbGlnbmluZ1RhZ3MoKSAmJiAhIXRoaXMuYWxpZ25tZW50O1xuICAgIGNvbnN0IGF3YWl0aW5nID0gb24gPyB0aGlzLmFsaWdubWVudCEuYXdhaXRpbmcgOiBudWxsO1xuICAgIHRoaXMucG9zdCh7XG4gICAgICB0eXBlOiAnY2FsaWJyYXRpb24nLFxuICAgICAgY2FsaWJyYXRpb246IG9uXG4gICAgICAgID8ge1xuICAgICAgICAgIHNlbGVjdGVkOiBhd2FpdGluZyA9PT0gJ3BvaW50JyA/IHRoaXMuYWxpZ25tZW50IS50YWdJZCA/PyBudWxsIDogbnVsbCxcbiAgICAgICAgICBwYWlyczogdGhpcy5hbGlnbm1lbnQhLm1hdGNoZWQubWFwKCh7IHRhZ0lkLCB0YXJnZXQgfSkgPT4gKHsgdGFnSWQsIHRhcmdldCB9KSksXG4gICAgICAgIH1cbiAgICAgICAgOiBudWxsLFxuICAgIH0pO1xuICAgIGNvbnN0IHBpY2tpbmcgPSBhd2FpdGluZyA9PT0gJ3BvaW50JztcbiAgICAvLyBPbmx5IHdoZW4gaXQgY2hhbmdlczogcGxhY2luZyBhIHRhZyB1c2VzIHRoZSBzYW1lIGNsaWNrLCBhbmQgaXMgbm90IHRvIGJlIHN3aXRjaGVkIG9mZiBoZXJlLlxuICAgIGlmIChwaWNraW5nICE9PSB0aGlzLnBpY2tpbmdGb3JDYWxpYnJhdGlvbikge1xuICAgICAgdGhpcy5waWNraW5nRm9yQ2FsaWJyYXRpb24gPSBwaWNraW5nO1xuICAgICAgdGhpcy5wb3N0KHsgdHlwZTogJ3BsYWNpbmcnLCBvbjogcGlja2luZyB9KTtcbiAgICB9XG4gIH1cblxuICAvKiogVGhlIG1lYXN1cmVtZW50IGZvciB0aGUgcGFnZSB0byBkcmF3IC0gaXRzIHBvaW50cywgZWFjaCBzZWdtZW50J3MgbGVuZ3RoIC0gb3Igbm90aGluZy4gKi9cbiAgcHJpdmF0ZSBwb3N0TWVhc3VyZW1lbnQoZHJhd246IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLnBvc3Qoe1xuICAgICAgdHlwZTogJ21lYXN1cmVtZW50JyxcbiAgICAgIG1lYXN1cmVtZW50OiBkcmF3blxuICAgICAgICA/IHsgcG9pbnRzOiB0aGlzLnZpZXdlclNlcnZpY2UuZ2V0VGhyZWVKc01lYXN1cmVQb2ludHNJbk1vZGVsRnJhbWUoKSwgZGlzdGFuY2VzOiB0aGlzLm1lYXN1cmVEaXN0YW5jZXMgfVxuICAgICAgICA6IG51bGwsXG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIHBvc3QoY29tbWFuZDogb2JqZWN0KTogdm9pZCB7XG4gICAgY29uc3QgcGFnZSA9IHRoaXMuZnJhbWU/Lm5hdGl2ZUVsZW1lbnQuY29udGVudFdpbmRvdztcbiAgICBpZiAocGFnZSAmJiB0aGlzLnJlYWR5KSB7XG4gICAgICBwYWdlLnBvc3RNZXNzYWdlKGNvbW1hbmQsIHdpbmRvdy5sb2NhdGlvbi5vcmlnaW4pO1xuICAgIH1cbiAgfVxufVxuIl19