@jax-data-science/sci-image-visualizer 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +19 -1
  2. package/esm2022/lib/contracts/display-types.mjs +7 -1
  3. package/esm2022/lib/contracts/image.contract.mjs +1 -1
  4. package/esm2022/lib/contracts/region-editor-api.contract.mjs +1 -1
  5. package/esm2022/lib/contracts/spatial-dataset.contract.mjs +1 -1
  6. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  7. package/esm2022/lib/geometry/reconstruct.mjs +11 -1
  8. package/esm2022/lib/implementations/napari-js/napari-image-2d-scene.mjs +17 -2
  9. package/esm2022/lib/implementations/napari-js/napari-spatial-2d-scene.mjs +88 -10
  10. package/esm2022/lib/implementations/napari-js/napari-spatial-3d-scene.mjs +62 -13
  11. package/esm2022/lib/implementations/napari-js/napari-spatial-encoding.mjs +21 -1
  12. package/esm2022/lib/implementations/napari-js/napari-spatial-scene.mjs +5 -1
  13. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +9 -1
  14. package/esm2022/lib/implementations/napari-js/spatial-tiles/categorical-lookup.mjs +26 -3
  15. package/esm2022/lib/implementations/napari-js/spatial-tiles/cell-layers.mjs +3 -2
  16. package/esm2022/lib/implementations/napari-js/spatial-tiles/density-layer.mjs +20 -4
  17. package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-hover.mjs +4 -2
  18. package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-jobs.mjs +4 -2
  19. package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-layers.mjs +42 -15
  20. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +3 -1
  21. package/esm2022/lib/region-editor/region-editor.component.mjs +13 -1
  22. package/esm2022/lib/routing-visualizer.service.mjs +4 -1
  23. package/esm2022/lib/spatial/lod.mjs +6 -3
  24. package/esm2022/lib/spatial/spatial-controls.facade.mjs +78 -23
  25. package/esm2022/lib/spatial/spatial-gene-filter.mjs +138 -0
  26. package/esm2022/lib/spatial/spatial-selection.mjs +70 -1
  27. package/esm2022/lib/spatial/spatial-tile-merge.mjs +42 -1
  28. package/esm2022/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.mjs +3 -3
  29. package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +54 -11
  30. package/esm2022/lib/spatial-controls/spatial-gene-style/spatial-gene-style.component.mjs +66 -0
  31. package/esm2022/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.mjs +106 -26
  32. package/esm2022/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.mjs +3 -3
  33. package/esm2022/lib/spatial-controls/spatial-key/spatial-key.component.mjs +3 -3
  34. package/esm2022/lib/spatial-controls/spatial-marker-genes-form/spatial-marker-genes-form.component.mjs +3 -3
  35. package/esm2022/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.mjs +110 -8
  36. package/esm2022/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.mjs +41 -9
  37. package/esm2022/lib/store/region-selection.mjs +21 -4
  38. package/esm2022/lib/store/region-store.service.mjs +22 -3
  39. package/esm2022/lib/store/spatial-selection.service.mjs +13 -1
  40. package/esm2022/lib/store/visualizer-store.service.mjs +50 -1
  41. package/esm2022/lib/toolbar/toolbar-help-dialog/toolbar-help-dialog.component.mjs +3 -3
  42. package/fesm2022/{jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-DnPlIP2F.mjs → jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-DvFp1jNH.mjs} +1777 -793
  43. package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-DvFp1jNH.mjs.map +1 -0
  44. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-Bs987_-E.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-D7lSUAM1.mjs} +2 -2
  45. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-Bs987_-E.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-D7lSUAM1.mjs.map} +1 -1
  46. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +2 -2
  47. package/lib/contracts/channel-histogram-api.contract.d.ts +14 -0
  48. package/lib/contracts/display-types.d.ts +27 -0
  49. package/lib/contracts/image.contract.d.ts +2 -0
  50. package/lib/contracts/region-editor-api.contract.d.ts +3 -0
  51. package/lib/contracts/spatial-dataset.contract.d.ts +7 -0
  52. package/lib/contracts/visualizer.contract.d.ts +28 -2
  53. package/lib/geometry/reconstruct.d.ts +6 -0
  54. package/lib/implementations/napari-js/napari-image-2d-scene.d.ts +7 -0
  55. package/lib/implementations/napari-js/napari-spatial-2d-scene.d.ts +13 -2
  56. package/lib/implementations/napari-js/napari-spatial-encoding.d.ts +5 -0
  57. package/lib/implementations/napari-js/napari-spatial-scene.d.ts +4 -0
  58. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +5 -0
  59. package/lib/implementations/napari-js/spatial-tiles/categorical-lookup.d.ts +6 -1
  60. package/lib/implementations/napari-js/spatial-tiles/transcript-hover.d.ts +4 -0
  61. package/lib/implementations/napari-js/spatial-tiles/transcript-layers.d.ts +3 -1
  62. package/lib/routing-visualizer.service.d.ts +1 -0
  63. package/lib/spatial/lod.d.ts +3 -1
  64. package/lib/spatial/spatial-controls.facade.d.ts +19 -2
  65. package/lib/spatial/spatial-gene-filter.d.ts +44 -0
  66. package/lib/spatial/spatial-selection.d.ts +16 -1
  67. package/lib/spatial/spatial-tile-merge.d.ts +17 -0
  68. package/lib/spatial-controls/spatial-controls.component.d.ts +14 -3
  69. package/lib/spatial-controls/spatial-gene-style/spatial-gene-style.component.d.ts +30 -0
  70. package/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.d.ts +39 -7
  71. package/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.d.ts +33 -1
  72. package/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.d.ts +13 -1
  73. package/lib/store/region-selection.d.ts +14 -3
  74. package/lib/store/region-store.service.d.ts +14 -1
  75. package/lib/store/spatial-selection.service.d.ts +8 -0
  76. package/lib/store/visualizer-store.service.d.ts +13 -0
  77. package/package.json +1 -1
  78. package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-DnPlIP2F.mjs.map +0 -1
@@ -1,21 +1,31 @@
1
1
  import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
+ import { of } from 'rxjs';
3
4
  import { FormsModule } from '@angular/forms';
4
5
  import { CheckboxModule } from 'primeng/checkbox';
5
6
  import { DropdownModule } from 'primeng/dropdown';
7
+ import { MultiSelectModule } from 'primeng/multiselect';
8
+ import { OverlayPanelModule } from 'primeng/overlaypanel';
9
+ import { SelectButtonModule } from 'primeng/selectbutton';
6
10
  import { SliderModule } from 'primeng/slider';
7
11
  import { TooltipModule } from 'primeng/tooltip';
8
- import { DEFAULT_SPATIAL_VIEW } from '../../contracts/display-types';
12
+ import { DEFAULT_SPATIAL_VIEW, } from '../../contracts/display-types';
13
+ import { observationGeneColor, observationGeneGlyph } from '../../spatial/spatial-gene-filter';
9
14
  import { emptySelection } from '../../spatial/spatial-selection';
10
- import { CLIP_OPTIONS, columnOptions, middleSection, sectionLabel, } from '../../spatial/spatial-panel-model';
15
+ import { CLIP_OPTIONS, columnOptions, glyphPoints, middleSection, sectionLabel, } from '../../spatial/spatial-panel-model';
16
+ import { SpatialGeneStyleComponent } from '../spatial-gene-style/spatial-gene-style.component';
11
17
  import { SpatialKeyComponent } from '../spatial-key/spatial-key.component';
12
18
  import * as i0 from "@angular/core";
13
19
  import * as i1 from "@angular/common";
14
20
  import * as i2 from "@angular/forms";
15
21
  import * as i3 from "primeng/checkbox";
16
- import * as i4 from "primeng/dropdown";
17
- import * as i5 from "primeng/slider";
18
- import * as i6 from "primeng/tooltip";
22
+ import * as i4 from "primeng/api";
23
+ import * as i5 from "primeng/dropdown";
24
+ import * as i6 from "primeng/multiselect";
25
+ import * as i7 from "primeng/overlaypanel";
26
+ import * as i8 from "primeng/selectbutton";
27
+ import * as i9 from "primeng/slider";
28
+ import * as i10 from "primeng/tooltip";
19
29
  /**
20
30
  * The Observations section — one marker per observation (cell centroid, Visium spot) — and
21
31
  * what else draws them: what colours them (a column or a gene, mutually exclusive, with
@@ -55,6 +65,11 @@ export class SpatialObservationsPanelComponent {
55
65
  /** A legend row was clicked (see the key). */
56
66
  this.categoryClicked = new EventEmitter();
57
67
  this.clipOptions = CLIP_OPTIONS;
68
+ /** How several genes of the observation filter combine. */
69
+ this.matchOptions = [
70
+ { label: 'Any gene', value: 'any' },
71
+ { label: 'All genes', value: 'all' },
72
+ ];
58
73
  /** Virtual-scrolled only past this many options (see the cells panel's gene dropdown). */
59
74
  this.geneVirtualScrollFrom = 200;
60
75
  /** Colour-by column choices — "None" plus every column the dataset declares. */
@@ -67,8 +82,17 @@ export class SpatialObservationsPanelComponent {
67
82
  * must not sit in a template getter.
68
83
  */
69
84
  this.sections = null;
85
+ /** Whether the gene filter could not be applied (an expression read failed); one stream per
86
+ * `controls`, so the template's async pipe keeps its subscription. */
87
+ this.geneFilterFailed$ = of(false);
88
+ // ── a picked gene's icon and colour ─────────────────────────────────────
89
+ /** The gene the icon and colour picker is open for. */
90
+ this.styleGene = null;
70
91
  }
71
92
  ngOnChanges(changes) {
93
+ if (changes['controls']) {
94
+ this.geneFilterFailed$ = this.controls?.getGeneFilterFailed$?.() ?? of(false);
95
+ }
72
96
  if (changes['dataset'] || changes['controls']) {
73
97
  this.columnOptions = columnOptions(this.dataset);
74
98
  // A new dataset almost certainly has different columns; drop stale UI state.
@@ -101,6 +125,80 @@ export class SpatialObservationsPanelComponent {
101
125
  this.selectedColumn = null;
102
126
  this.controls?.colorByFeature(name);
103
127
  }
128
+ // ── which observations are drawn ────────────────────────────────────────
129
+ /** Genes picked for the observation filter; none draws every observation. */
130
+ onObservationGenes(genes) {
131
+ this.controls?.setViewState({ observationGenes: genes ?? [] });
132
+ }
133
+ onObservationGenesMatch(match) {
134
+ if (!match)
135
+ return;
136
+ this.controls?.setViewState({ observationGenesMatch: match });
137
+ }
138
+ /** Ask for the picked genes' expression again (a failed read is not cached). */
139
+ retryGeneFilter() {
140
+ this.controls?.setViewState({ observationGenes: [...this.view.observationGenes] });
141
+ }
142
+ onObservationGenesColorBy(on) {
143
+ this.controls?.setViewState({ observationGenesColorBy: on });
144
+ }
145
+ removeObservationGene(gene) {
146
+ this.onObservationGenes(this.view.observationGenes.filter((g) => g !== gene));
147
+ }
148
+ isObservationGene(gene) {
149
+ return this.view.observationGenes.includes(gene);
150
+ }
151
+ trackByGene(_i, gene) {
152
+ return gene;
153
+ }
154
+ geneColorOf(gene) {
155
+ return observationGeneColor(this.view, gene);
156
+ }
157
+ geneGlyphOf(gene) {
158
+ return observationGeneGlyph(this.view, gene);
159
+ }
160
+ glyphPointsOf(gene) {
161
+ return glyphPoints(this.geneGlyphOf(gene));
162
+ }
163
+ /** Open the picker on the gene's swatch. Inside the genes dropdown the click must not also
164
+ * reach the option, which would unpick the gene. */
165
+ openGeneStyle(event, gene, panel) {
166
+ event.stopPropagation();
167
+ if (this.styleGene === gene) {
168
+ panel.toggle(event);
169
+ return;
170
+ }
171
+ this.styleGene = gene;
172
+ panel.hide();
173
+ // Re-open anchored on the swatch that was clicked, once the panel has closed.
174
+ setTimeout(() => panel.toggle(event));
175
+ }
176
+ onGeneColor(gene, hex) {
177
+ this.controls?.setViewState({ observationGeneColors: { ...this.view.observationGeneColors, [gene]: hex } });
178
+ }
179
+ onGeneGlyph(gene, glyph) {
180
+ this.controls?.setViewState({ observationGeneGlyphs: { ...this.view.observationGeneGlyphs, [gene]: glyph } });
181
+ }
182
+ /** Back to the gene's default colour (its place in the list) and icon (a circle). */
183
+ resetGeneStyle(gene) {
184
+ const colors = { ...this.view.observationGeneColors };
185
+ const glyphs = { ...this.view.observationGeneGlyphs };
186
+ delete colors[gene];
187
+ delete glyphs[gene];
188
+ this.controls?.setViewState({ observationGeneColors: colors, observationGeneGlyphs: glyphs });
189
+ }
190
+ /** What the filter hides, in words. */
191
+ get geneFilterHint() {
192
+ const genes = this.view.observationGenes;
193
+ const which = genes.length === 1
194
+ ? genes[0]
195
+ : this.view.observationGenesMatch === 'all'
196
+ ? 'all of these genes'
197
+ : 'any of these genes';
198
+ return (`Only observations expressing ${which} are drawn; the others are hidden, with their cell outlines and the ` +
199
+ 'transcripts assigned to them. The transcript density map, and transcripts grouped by area when zoomed ' +
200
+ 'out, are hidden while this is on: they carry no cells to filter by.');
201
+ }
104
202
  /** Cell outlines on offer, which changes what the circles mean. */
105
203
  get hasCells() {
106
204
  return !!(this.dataset?.polygonTiles || this.dataset?.polygons);
@@ -257,7 +355,7 @@ export class SpatialObservationsPanelComponent {
257
355
  this.controls?.setViewState({ percentileClip: value });
258
356
  }
259
357
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SpatialObservationsPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
260
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: SpatialObservationsPanelComponent, isStandalone: true, selector: "spatial-observations-panel", inputs: { controls: "controls", dataset: "dataset", view: "view", genes: "genes", legend: "legend", colorBarCss: "colorBarCss", selection: "selection", selectedCategory: "selectedCategory", colormapOptions: "colormapOptions", is3d: "is3d", open: "open" }, outputs: { openChange: "openChange", categoryClicked: "categoryClicked" }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"dataset as ds\">\n <!-- Observations: one marker per observation (cell centroid, Visium spot), and what\n colours them. The lead section for datasets without cell outlines. -->\n <div class=\"sc-panel\" [class.open]=\"open\">\n <div class=\"sc-panel-head\" (click)=\"openChange.emit(!open)\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showPoints\"\n (click)=\"$event.stopPropagation()\"\n (onChange)=\"onShowPoints($event.checked)\"\n ></p-checkbox>\n <span class=\"sc-panel-title\">Observations</span>\n <i class=\"pi\" [class.pi-chevron-down]=\"open\" [class.pi-chevron-right]=\"!open\"></i>\n </div>\n <div class=\"sc-panel-body\" *ngIf=\"open\">\n <small class=\"sc-hint\" *ngIf=\"hasCells && !is3d\">\n One circle per cell. With cell outlines on, circles are only drawn where cells are too small on screen to\n outline.\n </small>\n <!-- Colour source. Column and gene are mutually exclusive: picking one\n clears the other, so what drives the colours is never ambiguous. -->\n <div class=\"sc-section\">\n <label class=\"sc-lbl\" for=\"sc-column\">Colour by column</label>\n <p-dropdown\n inputId=\"sc-column\"\n [options]=\"columnOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"selectedColumn\"\n (ngModelChange)=\"onColumn($event)\"\n placeholder=\"None (flat colour)\"\n appendTo=\"body\"\n styleClass=\"sc-full\"\n ></p-dropdown>\n </div>\n\n <ng-container *ngIf=\"genes?.state$ | async as gene\">\n <div class=\"sc-section\" *ngIf=\"ds.features\">\n <label class=\"sc-lbl\" for=\"sc-gene\">Colour by gene</label>\n <!-- A filterable dropdown, not a free-text box: the genes are visible before\n anything is typed, and each keystroke narrows a list the user can see.\n Virtual-scrolled because a targeted panel runs to a few thousand names. -->\n <p-dropdown\n inputId=\"sc-gene\"\n [options]=\"gene.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"selectedGene\"\n (ngModelChange)=\"onGene($event)\"\n [filter]=\"true\"\n filterBy=\"label\"\n [resetFilterOnHide]=\"true\"\n (onFilter)=\"genes?.onFilter($event.filter)\"\n (onShow)=\"genes?.ensureList()\"\n [showClear]=\"true\"\n [virtualScroll]=\"gene.options.length > geneVirtualScrollFrom\"\n [virtualScrollItemSize]=\"30\"\n [emptyFilterMessage]=\"gene.remote ? 'No match \u2014 keep typing' : 'No match'\"\n [emptyMessage]=\"gene.remote ? 'Type to search genes' : 'No genes'\"\n appendTo=\"body\"\n placeholder=\"Search {{ ds.features.count | number }} genes\u2026\"\n styleClass=\"sc-full\"\n ></p-dropdown>\n <small class=\"sc-hint\" *ngIf=\"gene.failed\">Gene lookup failed.</small>\n </div>\n </ng-container>\n\n <spatial-key\n [controls]=\"controls\"\n [dataset]=\"ds\"\n [view]=\"view\"\n [legend]=\"legend\"\n [colorBarCss]=\"colorBarCss\"\n [selectedCategory]=\"selectedCategory\"\n [colormapOptions]=\"colormapOptions\"\n (categoryClicked)=\"categoryClicked.emit($event)\"\n ></spatial-key>\n\n <!-- Display -->\n <div class=\"sc-section\">\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Point size</span>\n <p-slider\n [ngModel]=\"view.pointScale\"\n [min]=\"0.25\"\n [max]=\"5\"\n [step]=\"0.25\"\n (onChange)=\"onPointScale($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.pointScale }}\u00D7</span>\n </div>\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Opacity</span>\n <p-slider\n [ngModel]=\"view.opacity\"\n [min]=\"0.1\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.opacity }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Only for a gene: the map draws that gene's expression as a field. In 2D\n it goes under the cells of the displayed plane; in 3D it is one sheet per\n imaged section, optionally smoothed along z into a volume. -->\n <div class=\"sc-section\" *ngIf=\"canMapGene\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.geneMap\"\n (onChange)=\"onGeneMap($event.checked)\"\n label=\"Gene map (expression field)\"\n [pTooltip]=\"\n is3d\n ? 'Draw this gene\\'s expression as one field per imaged section \u2014 an estimate between the cells, empty where nothing was measured'\n : 'Draw this gene\\'s expression as a smooth field UNDER the cells \u2014 an estimate between them, transparent where nothing was measured'\n \"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- 3D only: interpolating along z turns the measured sheets into an\n estimate, so it is its own decision and its own label. -->\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.geneMapVolume\"\n (onChange)=\"onGeneMapVolume($event.checked)\"\n label=\"Volume rendering (interpolate along z)\"\n pTooltip=\"Smooth the per-section fields into a continuous volume \u2014 an ESTIMATE between the imaged sections, not measured tissue\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- Meaningless while interpolating: a volume built from one section would\n smear that slide through the specimen's whole depth. -->\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d && isSectioned && !view.geneMapVolume\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"geneMapOneSection\"\n (onChange)=\"onGeneMapOneSection($event.checked)\"\n label=\"One map section at a time\"\n pTooltip=\"Draw a single imaged section's field instead of the whole stack of sheets\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d && isSectioned && !view.geneMapVolume && geneMapOneSection\">\n <span class=\"sc-lbl\">Map section</span>\n <p-slider\n [ngModel]=\"view.geneMapSection\"\n [min]=\"0\"\n [max]=\"lastSection\"\n [step]=\"1\"\n (onChange)=\"onGeneMapSection($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ geneMapSectionLabel }}</span>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap\">\n <span class=\"sc-lbl\">Smoothing</span>\n <p-slider\n [ngModel]=\"view.geneMapSmoothing\"\n [min]=\"0.5\"\n [max]=\"4\"\n [step]=\"0.5\"\n (onChange)=\"onGeneMapSmoothing($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.geneMapSmoothing }}\u00D7</span>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap\">\n <span class=\"sc-lbl\">Map opacity</span>\n <p-slider\n [ngModel]=\"view.geneMapOpacity\"\n [min]=\"0.1\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onGeneMapOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.geneMapOpacity }}</span>\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.geneMap && !is3d\">\n Kernel-weighted mean per cell, not a sum \u2014 so a crowded region does not glow for being crowded. Transparent\n where no cell was measured. Turn the cells' own Opacity down to read the field under them.\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.geneMap && is3d\">\n {{ geneMapVolumeNote }}\n </div>\n </div>\n\n <!-- 3D only: the volume, the cloud and the density volumes occupy the same\n space, so each hides the others. Independent toggles, because the useful\n views are the combinations \u2014 the field alone, one section's cells over it,\n the anatomy alone. -->\n <div class=\"sc-section\" *ngIf=\"is3d\">\n <span class=\"sc-lbl\">Show</span>\n <div class=\"sc-row\" *ngIf=\"ds.volume\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showVolume\"\n (onChange)=\"onShowVolume($event.checked)\"\n label=\"Reference volume\"\n pTooltip=\"The registered anatomy the observations sit in\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- The backdrop's own opacity: reading the cloud or a density field\n through the anatomy means turning the anatomy down, not the data. -->\n <div class=\"sc-row\" *ngIf=\"ds.volume && view.showVolume\">\n <span class=\"sc-lbl\">Volume opacity</span>\n <p-slider\n [ngModel]=\"view.volumeOpacity\"\n [min]=\"0.05\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onVolumeOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.volumeOpacity }}</span>\n </div>\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showPoints\"\n (onChange)=\"onShowPoints($event.checked)\"\n label=\"Observations\"\n pTooltip=\"The measured cells. Hide them to read the density volumes underneath\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- Only for data that really is sectioned; a continuous z has no sections\n to step through. -->\n <div class=\"sc-row\" *ngIf=\"view.showPoints && isSectioned\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"oneSection\"\n (onChange)=\"onOneSection($event.checked)\"\n label=\"One section at a time\"\n pTooltip=\"Draw a single imaged section's cells, so the density volumes between the sections stay visible\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.showPoints && isSectioned && oneSection\">\n <span class=\"sc-lbl\">Section</span>\n <p-slider\n [ngModel]=\"view.pointSection\"\n [min]=\"0\"\n [max]=\"lastSection\"\n [step]=\"1\"\n (onChange)=\"onPointSection($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ sectionLabel }}</span>\n </div>\n </div>\n\n <div class=\"sc-section\" *ngIf=\"is3d\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.densityVolume\"\n (onChange)=\"onDensityVolume($event.checked)\"\n label=\"Cluster density volumes\"\n pTooltip=\"Raymarch each cluster as a smooth density field \u2014 an ESTIMATE between the imaged sections, not measured cells\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.densityVolume\">\n <span class=\"sc-lbl\">Smoothing</span>\n <p-slider\n [ngModel]=\"view.densitySmoothing\"\n [min]=\"0.5\"\n [max]=\"4\"\n [step]=\"0.5\"\n (onChange)=\"onDensitySmoothing($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.densitySmoothing }}\u00D7</span>\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.densityVolume\">{{ densityNote }}</div>\n </div>\n\n <!-- Continuous-only knobs: meaningless for a categorical palette. -->\n <div class=\"sc-section\" *ngIf=\"isContinuous\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.logScale\"\n (onChange)=\"onLogScale($event.checked)\"\n label=\"Log scale\"\n pTooltip=\"Counts collapse against their outliers on a linear ramp\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Clip outliers</span>\n <p-dropdown\n [options]=\"clipOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"view.percentileClip\"\n (ngModelChange)=\"onClip($event)\"\n appendTo=\"body\"\n pTooltip=\"Percentile the contrast window is taken at\"\n tooltipPosition=\"top\"\n ></p-dropdown>\n <!-- Reserves the readout column this row has no readout for, so the\n control's right edge lines up with every slider's. -->\n <span class=\"sc-val\"></span>\n </div>\n </div>\n</ng-container>\n", styles: [".sc-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d)}.sc-error{color:var(--red-600, #c62828)}.sc-section{display:flex;flex-direction:column;gap:.35rem}.sc-lbl{font-size:.78rem;color:var(--text-color-secondary, #6c757d);white-space:nowrap}.sc-row{display:flex;align-items:center;gap:.6rem}.sc-row p-slider,.sc-row p-dropdown,.sc-row p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row p-slider .p-slider{width:100%}:host ::ng-deep .sc-row p-dropdown .p-dropdown,:host ::ng-deep .sc-row p-treeSelect .p-treeselect{width:100%}.sc-row .sc-lbl{flex:0 0 calc(50% - .6rem);min-width:0;overflow:hidden;text-overflow:ellipsis}.sc-cmap-option{display:inline-flex;align-items:center;gap:.4rem}.sc-cmap-swatch{width:3.5rem;height:.7rem;border-radius:2px;display:block}.sc-val{flex:0 0 4rem;text-align:right;font-size:.78rem;font-variant-numeric:tabular-nums;white-space:nowrap}:host ::ng-deep .sc-full,:host ::ng-deep .sc-full .p-dropdown,:host ::ng-deep .sc-full .p-autocomplete,:host ::ng-deep .sc-full input{width:100%}.sc-colorbar{height:.75rem;border-radius:2px;border:1px solid rgba(0,0,0,.2)}.sc-colorbar-ends{display:flex;justify-content:space-between;font-size:.7rem;color:var(--text-color-secondary, #6c757d);margin-top:.2rem}.sc-panel{border:1px solid var(--surface-border, #dee2e6);border-radius:6px;margin-bottom:.5rem;overflow:hidden}.sc-panel-head{display:flex;align-items:center;gap:.6rem;padding:.5rem .7rem;cursor:pointer;-webkit-user-select:none;user-select:none;background:var(--surface-100, #f5f6f7)}.sc-panel-head .sc-panel-title{flex:1;font-weight:600}.sc-panel.open>.sc-panel-head{background:var(--primary-color, #2196f3);color:var(--primary-color-text, #fff)}.sc-panel-body{display:flex;flex-direction:column;gap:.45rem;padding:.6rem .75rem .75rem}.sc-subhead{display:flex;align-items:center;gap:.5rem;margin-top:.4rem;font-size:.72rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-color-secondary, #6c757d)}.sc-subhead:after{content:\"\";flex:1;border-top:1px solid var(--surface-border, #dee2e6)}:host ::ng-deep .sc-buttons .p-button{padding:.35rem .75rem;font-size:.85rem}.sc-row-picker p-dropdown,.sc-row-picker p-multiSelect,.sc-row-picker p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row-picker .sc-variant{width:6.5rem}:host ::ng-deep .sc-row-picker p-multiselect .p-multiselect{width:100%}:host ::ng-deep .sc-row-picker p-dropdown:has(.sc-variant){flex:0 0 6.5rem}:host ::ng-deep .sc-row-picker p-treeselect:has(.sc-palette){flex:0 0 3rem}:host ::ng-deep .sc-palette .p-treeselect-label{width:0;padding:0}.sc-icon-btn{flex:0 0 auto;border:0;background:transparent;cursor:pointer;padding:.25rem .35rem;font:inherit;font-size:1.05rem;color:var(--text-color, #333);border-radius:4px}.sc-icon-btn:hover{background:var(--surface-200, #e9ecef)}.sc-icon-btn.active{color:var(--primary-color, #2196f3);font-weight:700}.sc-icon-btn:disabled{opacity:.5;cursor:default}.sc-small-btn{margin-left:auto;border:1px solid var(--surface-border, #ced4da);background:var(--surface-0, #fff);border-radius:4px;padding:.2rem .6rem;font:inherit;font-size:.8rem;cursor:pointer}:host ::ng-deep .sc-group-heading{font-size:.7rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;opacity:.7}.sc-group-list,.sc-gene-tree{max-height:16rem;overflow-y:auto;border:1px solid var(--surface-border, #dee2e6);border-radius:4px;padding:.25rem .4rem}.sc-group-row,.sc-gene-row{display:flex;align-items:center;gap:.45rem;padding:.15rem 0;font-size:.88rem}.sc-group-row .sc-group-label,.sc-gene-row .sc-group-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-group-all,.sc-gene-root,.sc-gene-group{font-weight:600}.sc-group-all .sc-icon-btn,.sc-gene-root .sc-icon-btn,.sc-gene-group .sc-icon-btn{margin-left:auto;font-size:.85rem}.sc-count{flex:0 0 auto;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sc-glyph-option{display:inline-flex;align-items:center;gap:.4rem}.sc-glyph-svg{width:1rem;height:1rem;fill:currentColor}:host ::ng-deep .sc-glyph-pick .p-dropdown-label{padding:.2rem .4rem}.sc-color-input{width:2.5rem;height:1.8rem;border:1px solid var(--surface-border, #ced4da);border-radius:4px;padding:0;background:none}.sc-row-number p-slider,.sc-row-range p-slider{flex:1 1 0;min-width:0}:host ::ng-deep .sc-num{width:4.5rem;padding:.3rem .4rem;text-align:center}:host ::ng-deep .sc-row-number p-inputnumber,:host ::ng-deep .sc-row-range p-inputnumber{flex:0 0 auto}:host{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox", inputs: ["value", "name", "disabled", "binary", "label", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "styleClass", "labelStyleClass", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i4.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: SliderModule }, { kind: "component", type: i5.Slider, selector: "p-slider", inputs: ["animate", "disabled", "min", "max", "orientation", "step", "range", "style", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: SpatialKeyComponent, selector: "spatial-key", inputs: ["controls", "dataset", "view", "legend", "colorBarCss", "selectedCategory", "colormapOptions"], outputs: ["categoryClicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
358
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: SpatialObservationsPanelComponent, isStandalone: true, selector: "spatial-observations-panel", inputs: { controls: "controls", dataset: "dataset", view: "view", genes: "genes", legend: "legend", colorBarCss: "colorBarCss", selection: "selection", selectedCategory: "selectedCategory", colormapOptions: "colormapOptions", is3d: "is3d", open: "open" }, outputs: { openChange: "openChange", categoryClicked: "categoryClicked" }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"dataset as ds\">\n <!-- Observations: one marker per observation (cell centroid, Visium spot), and what\n colours them. The lead section for datasets without cell outlines. -->\n <div class=\"sc-panel\" [class.open]=\"open\">\n <div class=\"sc-panel-head\" (click)=\"openChange.emit(!open)\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showPoints\"\n (click)=\"$event.stopPropagation()\"\n (onChange)=\"onShowPoints($event.checked)\"\n ></p-checkbox>\n <span class=\"sc-panel-title\">Observations</span>\n <i class=\"pi\" [class.pi-chevron-down]=\"open\" [class.pi-chevron-right]=\"!open\"></i>\n </div>\n <div class=\"sc-panel-body\" *ngIf=\"open\">\n <small class=\"sc-hint\" *ngIf=\"hasCells && !is3d\">\n One circle per cell. With cell outlines on, circles are only drawn where cells are too small on screen to\n outline.\n </small>\n <!-- Colour source. Column and gene are mutually exclusive: picking one\n clears the other, so what drives the colours is never ambiguous. -->\n <div class=\"sc-section\">\n <label class=\"sc-lbl\" for=\"sc-column\">Colour by column</label>\n <p-dropdown\n inputId=\"sc-column\"\n [options]=\"columnOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"selectedColumn\"\n (ngModelChange)=\"onColumn($event)\"\n placeholder=\"None (flat colour)\"\n appendTo=\"body\"\n styleClass=\"sc-full\"\n ></p-dropdown>\n </div>\n\n <ng-container *ngIf=\"genes?.state$ | async as gene\">\n <div class=\"sc-section\" *ngIf=\"ds.features\">\n <label class=\"sc-lbl\" for=\"sc-gene\">Colour by gene</label>\n <!-- A filterable dropdown, not a free-text box: the genes are visible before\n anything is typed, and each keystroke narrows a list the user can see.\n Virtual-scrolled because a targeted panel runs to a few thousand names. -->\n <p-dropdown\n inputId=\"sc-gene\"\n [options]=\"gene.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"selectedGene\"\n (ngModelChange)=\"onGene($event)\"\n [filter]=\"true\"\n filterBy=\"label\"\n [resetFilterOnHide]=\"true\"\n (onFilter)=\"genes?.onFilter($event.filter)\"\n (onShow)=\"genes?.ensureList()\"\n [showClear]=\"true\"\n [virtualScroll]=\"gene.options.length > geneVirtualScrollFrom\"\n [virtualScrollItemSize]=\"30\"\n [emptyFilterMessage]=\"gene.remote ? 'No match \u2014 keep typing' : 'No match'\"\n [emptyMessage]=\"gene.remote ? 'Type to search genes' : 'No genes'\"\n appendTo=\"body\"\n placeholder=\"Search {{ ds.features.count | number }} genes\u2026\"\n styleClass=\"sc-full\"\n ></p-dropdown>\n <small class=\"sc-hint\" *ngIf=\"gene.failed\">Gene lookup failed.</small>\n </div>\n\n <!-- Which observations are drawn at all, by what they express. Several genes at\n once, unlike the colour, which reads one: picking genes here hides every\n observation that expresses none of them (or not all of them), and by default\n colours (and, in 2D, marks) each drawn one by its gene. -->\n <div class=\"sc-section\" *ngIf=\"ds.features\">\n <label class=\"sc-lbl\" for=\"sc-show-genes\">Show observations expressing</label>\n <p-multiSelect\n inputId=\"sc-show-genes\"\n [options]=\"gene.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"view.observationGenes\"\n (onChange)=\"onObservationGenes($event.value)\"\n [filter]=\"true\"\n filterBy=\"label\"\n [resetFilterOnHide]=\"true\"\n (onFilter)=\"genes?.onFilter($event.filter)\"\n (onPanelShow)=\"genes?.ensureList()\"\n [virtualScroll]=\"gene.options.length > geneVirtualScrollFrom\"\n [virtualScrollItemSize]=\"30\"\n [showToggleAll]=\"false\"\n [showClear]=\"true\"\n [emptyFilterMessage]=\"gene.remote ? 'No match \u2014 keep typing' : 'No match'\"\n [emptyMessage]=\"gene.remote ? 'Type to search genes' : 'No genes'\"\n appendTo=\"body\"\n placeholder=\"Any gene (every observation)\"\n [selectedItemsLabel]=\"'{0} genes'\"\n [maxSelectedLabels]=\"3\"\n styleClass=\"sc-full\"\n >\n <!-- A picked gene shows its icon in its colour; clicking it edits them. -->\n <ng-template let-option pTemplate=\"item\">\n <div class=\"sc-obs-gene-option\">\n <span class=\"sc-obs-gene-name\">{{ option.label }}</span>\n <ng-container *ngIf=\"isObservationGene(option.value)\">\n <ng-container *ngTemplateOutlet=\"geneMark; context: { $implicit: option.value }\"></ng-container>\n </ng-container>\n </div>\n </ng-template>\n </p-multiSelect>\n <p-selectButton\n *ngIf=\"view.observationGenes.length > 1\"\n [options]=\"matchOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"view.observationGenesMatch\"\n (ngModelChange)=\"onObservationGenesMatch($event)\"\n [allowEmpty]=\"false\"\n styleClass=\"sc-buttons\"\n ></p-selectButton>\n <!-- The picked genes: each one's icon and colour (click to edit), and a way out. -->\n <div class=\"sc-obs-genes\" *ngIf=\"view.observationGenes.length\">\n <div class=\"sc-obs-gene-row\" *ngFor=\"let g of view.observationGenes; trackBy: trackByGene\">\n <ng-container *ngTemplateOutlet=\"geneMark; context: { $implicit: g }\"></ng-container>\n <span class=\"sc-obs-gene-name\">{{ g }}</span>\n <button\n type=\"button\"\n class=\"sc-icon-btn\"\n [attr.aria-label]=\"'Stop showing ' + g\"\n pTooltip=\"Remove\"\n tooltipPosition=\"top\"\n (click)=\"removeObservationGene(g)\"\n >\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.observationGenes.length\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.observationGenesColorBy\"\n (onChange)=\"onObservationGenesColorBy($event.checked)\"\n label=\"Colour by these genes\"\n pTooltip=\"Each observation in the colour (and, in 2D, the icon) of the picked gene it expresses most, relative to that gene's highest value. Off, Colour by column or gene applies.\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <small class=\"sc-hint\" *ngIf=\"view.observationGenes.length\">{{ geneFilterHint }}</small>\n <!-- The filter is never dropped silently: a failed expression read keeps the observations\n as they were drawn, and says so. -->\n <small class=\"sc-hint sc-error\" *ngIf=\"view.observationGenes.length && (geneFilterFailed$ | async)\">\n The expression of the picked genes could not be read, so the observations still show the previous\n filter.\n <a href=\"\" (click)=\"$event.preventDefault(); retryGeneFilter()\">Try again</a>\n </small>\n </div>\n </ng-container>\n\n <spatial-key\n [controls]=\"controls\"\n [dataset]=\"ds\"\n [view]=\"view\"\n [legend]=\"legend\"\n [colorBarCss]=\"colorBarCss\"\n [selectedCategory]=\"selectedCategory\"\n [colormapOptions]=\"colormapOptions\"\n (categoryClicked)=\"categoryClicked.emit($event)\"\n ></spatial-key>\n\n <!-- Display -->\n <div class=\"sc-section\">\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Point size</span>\n <p-slider\n [ngModel]=\"view.pointScale\"\n [min]=\"0.25\"\n [max]=\"5\"\n [step]=\"0.25\"\n (onChange)=\"onPointScale($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.pointScale }}\u00D7</span>\n </div>\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Opacity</span>\n <p-slider\n [ngModel]=\"view.opacity\"\n [min]=\"0.1\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.opacity }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Only for a gene: the map draws that gene's expression as a field. In 2D\n it goes under the cells of the displayed plane; in 3D it is one sheet per\n imaged section, optionally smoothed along z into a volume. -->\n <div class=\"sc-section\" *ngIf=\"canMapGene\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.geneMap\"\n (onChange)=\"onGeneMap($event.checked)\"\n label=\"Gene map (expression field)\"\n [pTooltip]=\"\n is3d\n ? 'Draw this gene\\'s expression as one field per imaged section \u2014 an estimate between the cells, empty where nothing was measured'\n : 'Draw this gene\\'s expression as a smooth field UNDER the cells \u2014 an estimate between them, transparent where nothing was measured'\n \"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- 3D only: interpolating along z turns the measured sheets into an\n estimate, so it is its own decision and its own label. -->\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.geneMapVolume\"\n (onChange)=\"onGeneMapVolume($event.checked)\"\n label=\"Volume rendering (interpolate along z)\"\n pTooltip=\"Smooth the per-section fields into a continuous volume \u2014 an ESTIMATE between the imaged sections, not measured tissue\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- Meaningless while interpolating: a volume built from one section would\n smear that slide through the specimen's whole depth. -->\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d && isSectioned && !view.geneMapVolume\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"geneMapOneSection\"\n (onChange)=\"onGeneMapOneSection($event.checked)\"\n label=\"One map section at a time\"\n pTooltip=\"Draw a single imaged section's field instead of the whole stack of sheets\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d && isSectioned && !view.geneMapVolume && geneMapOneSection\">\n <span class=\"sc-lbl\">Map section</span>\n <p-slider\n [ngModel]=\"view.geneMapSection\"\n [min]=\"0\"\n [max]=\"lastSection\"\n [step]=\"1\"\n (onChange)=\"onGeneMapSection($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ geneMapSectionLabel }}</span>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap\">\n <span class=\"sc-lbl\">Smoothing</span>\n <p-slider\n [ngModel]=\"view.geneMapSmoothing\"\n [min]=\"0.5\"\n [max]=\"4\"\n [step]=\"0.5\"\n (onChange)=\"onGeneMapSmoothing($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.geneMapSmoothing }}\u00D7</span>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap\">\n <span class=\"sc-lbl\">Map opacity</span>\n <p-slider\n [ngModel]=\"view.geneMapOpacity\"\n [min]=\"0.1\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onGeneMapOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.geneMapOpacity }}</span>\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.geneMap && !is3d\">\n Kernel-weighted mean per cell, not a sum \u2014 so a crowded region does not glow for being crowded. Transparent\n where no cell was measured. Turn the cells' own Opacity down to read the field under them.\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.geneMap && is3d\">\n {{ geneMapVolumeNote }}\n </div>\n </div>\n\n <!-- 3D only: the volume, the cloud and the density volumes occupy the same\n space, so each hides the others. Independent toggles, because the useful\n views are the combinations \u2014 the field alone, one section's cells over it,\n the anatomy alone. -->\n <div class=\"sc-section\" *ngIf=\"is3d\">\n <span class=\"sc-lbl\">Show</span>\n <div class=\"sc-row\" *ngIf=\"ds.volume\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showVolume\"\n (onChange)=\"onShowVolume($event.checked)\"\n label=\"Reference volume\"\n pTooltip=\"The registered anatomy the observations sit in\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- The backdrop's own opacity: reading the cloud or a density field\n through the anatomy means turning the anatomy down, not the data. -->\n <div class=\"sc-row\" *ngIf=\"ds.volume && view.showVolume\">\n <span class=\"sc-lbl\">Volume opacity</span>\n <p-slider\n [ngModel]=\"view.volumeOpacity\"\n [min]=\"0.05\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onVolumeOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.volumeOpacity }}</span>\n </div>\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showPoints\"\n (onChange)=\"onShowPoints($event.checked)\"\n label=\"Observations\"\n pTooltip=\"The measured cells. Hide them to read the density volumes underneath\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- Only for data that really is sectioned; a continuous z has no sections\n to step through. -->\n <div class=\"sc-row\" *ngIf=\"view.showPoints && isSectioned\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"oneSection\"\n (onChange)=\"onOneSection($event.checked)\"\n label=\"One section at a time\"\n pTooltip=\"Draw a single imaged section's cells, so the density volumes between the sections stay visible\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.showPoints && isSectioned && oneSection\">\n <span class=\"sc-lbl\">Section</span>\n <p-slider\n [ngModel]=\"view.pointSection\"\n [min]=\"0\"\n [max]=\"lastSection\"\n [step]=\"1\"\n (onChange)=\"onPointSection($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ sectionLabel }}</span>\n </div>\n </div>\n\n <div class=\"sc-section\" *ngIf=\"is3d\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.densityVolume\"\n (onChange)=\"onDensityVolume($event.checked)\"\n label=\"Cluster density volumes\"\n pTooltip=\"Raymarch each cluster as a smooth density field \u2014 an ESTIMATE between the imaged sections, not measured cells\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.densityVolume\">\n <span class=\"sc-lbl\">Smoothing</span>\n <p-slider\n [ngModel]=\"view.densitySmoothing\"\n [min]=\"0.5\"\n [max]=\"4\"\n [step]=\"0.5\"\n (onChange)=\"onDensitySmoothing($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.densitySmoothing }}\u00D7</span>\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.densityVolume\">{{ densityNote }}</div>\n </div>\n\n <!-- Continuous-only knobs: meaningless for a categorical palette. -->\n <div class=\"sc-section\" *ngIf=\"isContinuous\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.logScale\"\n (onChange)=\"onLogScale($event.checked)\"\n label=\"Log scale\"\n pTooltip=\"Counts collapse against their outliers on a linear ramp\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Clip outliers</span>\n <p-dropdown\n [options]=\"clipOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"view.percentileClip\"\n (ngModelChange)=\"onClip($event)\"\n appendTo=\"body\"\n pTooltip=\"Percentile the contrast window is taken at\"\n tooltipPosition=\"top\"\n ></p-dropdown>\n <!-- Reserves the readout column this row has no readout for, so the\n control's right edge lines up with every slider's. -->\n <span class=\"sc-val\"></span>\n </div>\n </div>\n\n <!-- A gene's icon in its colour: the button that opens its icon and colour picker. -->\n <ng-template #geneMark let-g>\n <button\n type=\"button\"\n class=\"sc-gene-icon\"\n [style.color]=\"geneColorOf(g)\"\n [attr.aria-label]=\"'Icon and colour for ' + g\"\n pTooltip=\"Icon and colour\"\n tooltipPosition=\"top\"\n (click)=\"openGeneStyle($event, g, genePop)\"\n >\n <svg viewBox=\"-1.2 -1.2 2.4 2.4\" class=\"sc-glyph-svg\">\n <polygon [attr.points]=\"glyphPointsOf(g)\"></polygon>\n </svg>\n </button>\n </ng-template>\n <p-overlayPanel #genePop styleClass=\"sc-gene-style\" appendTo=\"body\">\n <ng-template pTemplate=\"content\">\n <spatial-gene-style\n *ngIf=\"styleGene as g\"\n [gene]=\"g\"\n what=\"Observation\"\n [color]=\"geneColorOf(g)\"\n [glyph]=\"geneGlyphOf(g)\"\n iconHint=\"Icons show on the 2D observation markers.\"\n (colorChange)=\"onGeneColor(g, $event)\"\n (glyphChange)=\"onGeneGlyph(g, $event)\"\n (resetStyle)=\"resetGeneStyle(g)\"\n ></spatial-gene-style>\n </ng-template>\n </p-overlayPanel>\n</ng-container>\n", styles: [".sc-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d)}.sc-error{color:var(--red-600, #c62828)}.sc-section{display:flex;flex-direction:column;gap:.35rem}.sc-lbl{font-size:.78rem;color:var(--text-color-secondary, #6c757d);white-space:nowrap}.sc-row{display:flex;align-items:center;gap:.6rem}.sc-row p-slider,.sc-row p-dropdown,.sc-row p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row p-slider .p-slider{width:100%}:host ::ng-deep .sc-row p-dropdown .p-dropdown,:host ::ng-deep .sc-row p-treeSelect .p-treeselect{width:100%}.sc-row .sc-lbl{flex:0 0 calc(50% - .6rem);min-width:0;overflow:hidden;text-overflow:ellipsis}.sc-cmap-option{display:inline-flex;align-items:center;gap:.4rem}.sc-cmap-swatch{width:3.5rem;height:.7rem;border-radius:2px;display:block}.sc-val{flex:0 0 4rem;text-align:right;font-size:.78rem;font-variant-numeric:tabular-nums;white-space:nowrap}:host ::ng-deep .sc-full,:host ::ng-deep .sc-full .p-dropdown,:host ::ng-deep .sc-full .p-autocomplete,:host ::ng-deep .sc-full input{width:100%}.sc-colorbar{height:.75rem;border-radius:2px;border:1px solid rgba(0,0,0,.2)}.sc-colorbar-ends{display:flex;justify-content:space-between;font-size:.7rem;color:var(--text-color-secondary, #6c757d);margin-top:.2rem}.sc-panel{border:1px solid var(--surface-border, #dee2e6);border-radius:6px;margin-bottom:.5rem;overflow:hidden}.sc-panel-head{display:flex;align-items:center;gap:.6rem;padding:.5rem .7rem;cursor:pointer;-webkit-user-select:none;user-select:none;background:var(--surface-100, #f5f6f7)}.sc-panel-head .sc-panel-title{flex:1;font-weight:600}.sc-panel.open>.sc-panel-head{background:var(--primary-color, #2196f3);color:var(--primary-color-text, #fff)}.sc-panel-body{display:flex;flex-direction:column;gap:.45rem;padding:.6rem .75rem .75rem}.sc-subhead{display:flex;align-items:center;gap:.5rem;margin-top:.4rem;font-size:.72rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-color-secondary, #6c757d)}.sc-subhead:after{content:\"\";flex:1;border-top:1px solid var(--surface-border, #dee2e6)}:host ::ng-deep .sc-buttons .p-button{padding:.35rem .75rem;font-size:.85rem}.sc-row-picker p-dropdown,.sc-row-picker p-multiSelect,.sc-row-picker p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row-picker .sc-variant{width:6.5rem}:host ::ng-deep .sc-row-picker p-multiselect .p-multiselect{width:100%}:host ::ng-deep .sc-row-picker p-dropdown:has(.sc-variant){flex:0 0 6.5rem}:host ::ng-deep .sc-row-picker p-treeselect:has(.sc-palette){flex:0 0 3rem}:host ::ng-deep .sc-palette .p-treeselect-label{width:0;padding:0}.sc-icon-btn{flex:0 0 auto;border:0;background:transparent;cursor:pointer;padding:.25rem .35rem;font:inherit;font-size:1.05rem;color:var(--text-color, #333);border-radius:4px}.sc-icon-btn:hover{background:var(--surface-200, #e9ecef)}.sc-icon-btn.active{color:var(--primary-color, #2196f3);font-weight:700}.sc-icon-btn:disabled{opacity:.5;cursor:default}.sc-small-btn{margin-left:auto;border:1px solid var(--surface-border, #ced4da);background:var(--surface-0, #fff);border-radius:4px;padding:.2rem .6rem;font:inherit;font-size:.8rem;cursor:pointer}:host ::ng-deep .sc-group-heading{font-size:.7rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;opacity:.7}.sc-group-list,.sc-gene-tree{max-height:16rem;overflow-y:auto;border:1px solid var(--surface-border, #dee2e6);border-radius:4px;padding:.25rem .4rem}.sc-group-row,.sc-gene-row{display:flex;align-items:center;gap:.45rem;padding:.15rem 0;font-size:.88rem}.sc-group-row .sc-group-label,.sc-gene-row .sc-group-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-group-all,.sc-gene-root,.sc-gene-group{font-weight:600}.sc-group-all .sc-icon-btn,.sc-gene-root .sc-icon-btn,.sc-gene-group .sc-icon-btn{margin-left:auto;font-size:.85rem}.sc-count{flex:0 0 auto;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sc-glyph-option{display:inline-flex;align-items:center;gap:.4rem}.sc-glyph-svg{width:1rem;height:1rem;fill:currentColor}:host ::ng-deep .sc-glyph-pick .p-dropdown-label{padding:.2rem .4rem}.sc-color-input{width:2.5rem;height:1.8rem;border:1px solid var(--surface-border, #ced4da);border-radius:4px;padding:0;background:none}.sc-row-number p-slider,.sc-row-range p-slider{flex:1 1 0;min-width:0}:host ::ng-deep .sc-num{width:4.5rem;padding:.3rem .4rem;text-align:center}:host ::ng-deep .sc-row-number p-inputnumber,:host ::ng-deep .sc-row-range p-inputnumber{flex:0 0 auto}.sc-gene-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1.4rem;height:1.4rem;padding:0;border:1px solid transparent;border-radius:4px;background:transparent;cursor:pointer}.sc-gene-icon:hover,.sc-gene-icon:focus-visible{border-color:var(--surface-border, rgba(0, 0, 0, .2))}:host{display:contents}.sc-obs-genes{display:flex;flex-direction:column;gap:.15rem;margin-top:.35rem}.sc-obs-gene-row,.sc-obs-gene-option{display:flex;align-items:center;gap:.4rem;min-width:0}.sc-obs-gene-option{flex:1 1 auto;justify-content:space-between}.sc-obs-gene-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i3.Checkbox, selector: "p-checkbox", inputs: ["value", "name", "disabled", "binary", "label", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "styleClass", "labelStyleClass", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i5.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i6.MultiSelect, selector: "p-multiSelect", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: OverlayPanelModule }, { kind: "component", type: i7.OverlayPanel, selector: "p-overlayPanel", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "showCloseIcon", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i8.SelectButton, selector: "p-selectButton", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "style", "styleClass", "ariaLabelledBy", "disabled", "dataKey", "autofocus"], outputs: ["onOptionClick", "onChange"] }, { kind: "ngmodule", type: SliderModule }, { kind: "component", type: i9.Slider, selector: "p-slider", inputs: ["animate", "disabled", "min", "max", "orientation", "step", "range", "style", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i10.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: SpatialKeyComponent, selector: "spatial-key", inputs: ["controls", "dataset", "view", "legend", "colorBarCss", "selectedCategory", "colormapOptions"], outputs: ["categoryClicked"] }, { kind: "component", type: SpatialGeneStyleComponent, selector: "spatial-gene-style", inputs: ["gene", "what", "color", "glyph", "iconHint"], outputs: ["colorChange", "glyphChange", "resetStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
261
359
  }
262
360
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SpatialObservationsPanelComponent, decorators: [{
263
361
  type: Component,
@@ -266,10 +364,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
266
364
  FormsModule,
267
365
  CheckboxModule,
268
366
  DropdownModule,
367
+ MultiSelectModule,
368
+ OverlayPanelModule,
369
+ SelectButtonModule,
269
370
  SliderModule,
270
371
  TooltipModule,
271
372
  SpatialKeyComponent,
272
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *ngIf=\"dataset as ds\">\n <!-- Observations: one marker per observation (cell centroid, Visium spot), and what\n colours them. The lead section for datasets without cell outlines. -->\n <div class=\"sc-panel\" [class.open]=\"open\">\n <div class=\"sc-panel-head\" (click)=\"openChange.emit(!open)\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showPoints\"\n (click)=\"$event.stopPropagation()\"\n (onChange)=\"onShowPoints($event.checked)\"\n ></p-checkbox>\n <span class=\"sc-panel-title\">Observations</span>\n <i class=\"pi\" [class.pi-chevron-down]=\"open\" [class.pi-chevron-right]=\"!open\"></i>\n </div>\n <div class=\"sc-panel-body\" *ngIf=\"open\">\n <small class=\"sc-hint\" *ngIf=\"hasCells && !is3d\">\n One circle per cell. With cell outlines on, circles are only drawn where cells are too small on screen to\n outline.\n </small>\n <!-- Colour source. Column and gene are mutually exclusive: picking one\n clears the other, so what drives the colours is never ambiguous. -->\n <div class=\"sc-section\">\n <label class=\"sc-lbl\" for=\"sc-column\">Colour by column</label>\n <p-dropdown\n inputId=\"sc-column\"\n [options]=\"columnOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"selectedColumn\"\n (ngModelChange)=\"onColumn($event)\"\n placeholder=\"None (flat colour)\"\n appendTo=\"body\"\n styleClass=\"sc-full\"\n ></p-dropdown>\n </div>\n\n <ng-container *ngIf=\"genes?.state$ | async as gene\">\n <div class=\"sc-section\" *ngIf=\"ds.features\">\n <label class=\"sc-lbl\" for=\"sc-gene\">Colour by gene</label>\n <!-- A filterable dropdown, not a free-text box: the genes are visible before\n anything is typed, and each keystroke narrows a list the user can see.\n Virtual-scrolled because a targeted panel runs to a few thousand names. -->\n <p-dropdown\n inputId=\"sc-gene\"\n [options]=\"gene.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"selectedGene\"\n (ngModelChange)=\"onGene($event)\"\n [filter]=\"true\"\n filterBy=\"label\"\n [resetFilterOnHide]=\"true\"\n (onFilter)=\"genes?.onFilter($event.filter)\"\n (onShow)=\"genes?.ensureList()\"\n [showClear]=\"true\"\n [virtualScroll]=\"gene.options.length > geneVirtualScrollFrom\"\n [virtualScrollItemSize]=\"30\"\n [emptyFilterMessage]=\"gene.remote ? 'No match \u2014 keep typing' : 'No match'\"\n [emptyMessage]=\"gene.remote ? 'Type to search genes' : 'No genes'\"\n appendTo=\"body\"\n placeholder=\"Search {{ ds.features.count | number }} genes\u2026\"\n styleClass=\"sc-full\"\n ></p-dropdown>\n <small class=\"sc-hint\" *ngIf=\"gene.failed\">Gene lookup failed.</small>\n </div>\n </ng-container>\n\n <spatial-key\n [controls]=\"controls\"\n [dataset]=\"ds\"\n [view]=\"view\"\n [legend]=\"legend\"\n [colorBarCss]=\"colorBarCss\"\n [selectedCategory]=\"selectedCategory\"\n [colormapOptions]=\"colormapOptions\"\n (categoryClicked)=\"categoryClicked.emit($event)\"\n ></spatial-key>\n\n <!-- Display -->\n <div class=\"sc-section\">\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Point size</span>\n <p-slider\n [ngModel]=\"view.pointScale\"\n [min]=\"0.25\"\n [max]=\"5\"\n [step]=\"0.25\"\n (onChange)=\"onPointScale($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.pointScale }}\u00D7</span>\n </div>\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Opacity</span>\n <p-slider\n [ngModel]=\"view.opacity\"\n [min]=\"0.1\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.opacity }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Only for a gene: the map draws that gene's expression as a field. In 2D\n it goes under the cells of the displayed plane; in 3D it is one sheet per\n imaged section, optionally smoothed along z into a volume. -->\n <div class=\"sc-section\" *ngIf=\"canMapGene\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.geneMap\"\n (onChange)=\"onGeneMap($event.checked)\"\n label=\"Gene map (expression field)\"\n [pTooltip]=\"\n is3d\n ? 'Draw this gene\\'s expression as one field per imaged section \u2014 an estimate between the cells, empty where nothing was measured'\n : 'Draw this gene\\'s expression as a smooth field UNDER the cells \u2014 an estimate between them, transparent where nothing was measured'\n \"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- 3D only: interpolating along z turns the measured sheets into an\n estimate, so it is its own decision and its own label. -->\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.geneMapVolume\"\n (onChange)=\"onGeneMapVolume($event.checked)\"\n label=\"Volume rendering (interpolate along z)\"\n pTooltip=\"Smooth the per-section fields into a continuous volume \u2014 an ESTIMATE between the imaged sections, not measured tissue\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- Meaningless while interpolating: a volume built from one section would\n smear that slide through the specimen's whole depth. -->\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d && isSectioned && !view.geneMapVolume\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"geneMapOneSection\"\n (onChange)=\"onGeneMapOneSection($event.checked)\"\n label=\"One map section at a time\"\n pTooltip=\"Draw a single imaged section's field instead of the whole stack of sheets\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d && isSectioned && !view.geneMapVolume && geneMapOneSection\">\n <span class=\"sc-lbl\">Map section</span>\n <p-slider\n [ngModel]=\"view.geneMapSection\"\n [min]=\"0\"\n [max]=\"lastSection\"\n [step]=\"1\"\n (onChange)=\"onGeneMapSection($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ geneMapSectionLabel }}</span>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap\">\n <span class=\"sc-lbl\">Smoothing</span>\n <p-slider\n [ngModel]=\"view.geneMapSmoothing\"\n [min]=\"0.5\"\n [max]=\"4\"\n [step]=\"0.5\"\n (onChange)=\"onGeneMapSmoothing($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.geneMapSmoothing }}\u00D7</span>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap\">\n <span class=\"sc-lbl\">Map opacity</span>\n <p-slider\n [ngModel]=\"view.geneMapOpacity\"\n [min]=\"0.1\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onGeneMapOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.geneMapOpacity }}</span>\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.geneMap && !is3d\">\n Kernel-weighted mean per cell, not a sum \u2014 so a crowded region does not glow for being crowded. Transparent\n where no cell was measured. Turn the cells' own Opacity down to read the field under them.\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.geneMap && is3d\">\n {{ geneMapVolumeNote }}\n </div>\n </div>\n\n <!-- 3D only: the volume, the cloud and the density volumes occupy the same\n space, so each hides the others. Independent toggles, because the useful\n views are the combinations \u2014 the field alone, one section's cells over it,\n the anatomy alone. -->\n <div class=\"sc-section\" *ngIf=\"is3d\">\n <span class=\"sc-lbl\">Show</span>\n <div class=\"sc-row\" *ngIf=\"ds.volume\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showVolume\"\n (onChange)=\"onShowVolume($event.checked)\"\n label=\"Reference volume\"\n pTooltip=\"The registered anatomy the observations sit in\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- The backdrop's own opacity: reading the cloud or a density field\n through the anatomy means turning the anatomy down, not the data. -->\n <div class=\"sc-row\" *ngIf=\"ds.volume && view.showVolume\">\n <span class=\"sc-lbl\">Volume opacity</span>\n <p-slider\n [ngModel]=\"view.volumeOpacity\"\n [min]=\"0.05\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onVolumeOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.volumeOpacity }}</span>\n </div>\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showPoints\"\n (onChange)=\"onShowPoints($event.checked)\"\n label=\"Observations\"\n pTooltip=\"The measured cells. Hide them to read the density volumes underneath\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- Only for data that really is sectioned; a continuous z has no sections\n to step through. -->\n <div class=\"sc-row\" *ngIf=\"view.showPoints && isSectioned\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"oneSection\"\n (onChange)=\"onOneSection($event.checked)\"\n label=\"One section at a time\"\n pTooltip=\"Draw a single imaged section's cells, so the density volumes between the sections stay visible\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.showPoints && isSectioned && oneSection\">\n <span class=\"sc-lbl\">Section</span>\n <p-slider\n [ngModel]=\"view.pointSection\"\n [min]=\"0\"\n [max]=\"lastSection\"\n [step]=\"1\"\n (onChange)=\"onPointSection($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ sectionLabel }}</span>\n </div>\n </div>\n\n <div class=\"sc-section\" *ngIf=\"is3d\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.densityVolume\"\n (onChange)=\"onDensityVolume($event.checked)\"\n label=\"Cluster density volumes\"\n pTooltip=\"Raymarch each cluster as a smooth density field \u2014 an ESTIMATE between the imaged sections, not measured cells\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.densityVolume\">\n <span class=\"sc-lbl\">Smoothing</span>\n <p-slider\n [ngModel]=\"view.densitySmoothing\"\n [min]=\"0.5\"\n [max]=\"4\"\n [step]=\"0.5\"\n (onChange)=\"onDensitySmoothing($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.densitySmoothing }}\u00D7</span>\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.densityVolume\">{{ densityNote }}</div>\n </div>\n\n <!-- Continuous-only knobs: meaningless for a categorical palette. -->\n <div class=\"sc-section\" *ngIf=\"isContinuous\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.logScale\"\n (onChange)=\"onLogScale($event.checked)\"\n label=\"Log scale\"\n pTooltip=\"Counts collapse against their outliers on a linear ramp\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Clip outliers</span>\n <p-dropdown\n [options]=\"clipOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"view.percentileClip\"\n (ngModelChange)=\"onClip($event)\"\n appendTo=\"body\"\n pTooltip=\"Percentile the contrast window is taken at\"\n tooltipPosition=\"top\"\n ></p-dropdown>\n <!-- Reserves the readout column this row has no readout for, so the\n control's right edge lines up with every slider's. -->\n <span class=\"sc-val\"></span>\n </div>\n </div>\n</ng-container>\n", styles: [".sc-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d)}.sc-error{color:var(--red-600, #c62828)}.sc-section{display:flex;flex-direction:column;gap:.35rem}.sc-lbl{font-size:.78rem;color:var(--text-color-secondary, #6c757d);white-space:nowrap}.sc-row{display:flex;align-items:center;gap:.6rem}.sc-row p-slider,.sc-row p-dropdown,.sc-row p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row p-slider .p-slider{width:100%}:host ::ng-deep .sc-row p-dropdown .p-dropdown,:host ::ng-deep .sc-row p-treeSelect .p-treeselect{width:100%}.sc-row .sc-lbl{flex:0 0 calc(50% - .6rem);min-width:0;overflow:hidden;text-overflow:ellipsis}.sc-cmap-option{display:inline-flex;align-items:center;gap:.4rem}.sc-cmap-swatch{width:3.5rem;height:.7rem;border-radius:2px;display:block}.sc-val{flex:0 0 4rem;text-align:right;font-size:.78rem;font-variant-numeric:tabular-nums;white-space:nowrap}:host ::ng-deep .sc-full,:host ::ng-deep .sc-full .p-dropdown,:host ::ng-deep .sc-full .p-autocomplete,:host ::ng-deep .sc-full input{width:100%}.sc-colorbar{height:.75rem;border-radius:2px;border:1px solid rgba(0,0,0,.2)}.sc-colorbar-ends{display:flex;justify-content:space-between;font-size:.7rem;color:var(--text-color-secondary, #6c757d);margin-top:.2rem}.sc-panel{border:1px solid var(--surface-border, #dee2e6);border-radius:6px;margin-bottom:.5rem;overflow:hidden}.sc-panel-head{display:flex;align-items:center;gap:.6rem;padding:.5rem .7rem;cursor:pointer;-webkit-user-select:none;user-select:none;background:var(--surface-100, #f5f6f7)}.sc-panel-head .sc-panel-title{flex:1;font-weight:600}.sc-panel.open>.sc-panel-head{background:var(--primary-color, #2196f3);color:var(--primary-color-text, #fff)}.sc-panel-body{display:flex;flex-direction:column;gap:.45rem;padding:.6rem .75rem .75rem}.sc-subhead{display:flex;align-items:center;gap:.5rem;margin-top:.4rem;font-size:.72rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-color-secondary, #6c757d)}.sc-subhead:after{content:\"\";flex:1;border-top:1px solid var(--surface-border, #dee2e6)}:host ::ng-deep .sc-buttons .p-button{padding:.35rem .75rem;font-size:.85rem}.sc-row-picker p-dropdown,.sc-row-picker p-multiSelect,.sc-row-picker p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row-picker .sc-variant{width:6.5rem}:host ::ng-deep .sc-row-picker p-multiselect .p-multiselect{width:100%}:host ::ng-deep .sc-row-picker p-dropdown:has(.sc-variant){flex:0 0 6.5rem}:host ::ng-deep .sc-row-picker p-treeselect:has(.sc-palette){flex:0 0 3rem}:host ::ng-deep .sc-palette .p-treeselect-label{width:0;padding:0}.sc-icon-btn{flex:0 0 auto;border:0;background:transparent;cursor:pointer;padding:.25rem .35rem;font:inherit;font-size:1.05rem;color:var(--text-color, #333);border-radius:4px}.sc-icon-btn:hover{background:var(--surface-200, #e9ecef)}.sc-icon-btn.active{color:var(--primary-color, #2196f3);font-weight:700}.sc-icon-btn:disabled{opacity:.5;cursor:default}.sc-small-btn{margin-left:auto;border:1px solid var(--surface-border, #ced4da);background:var(--surface-0, #fff);border-radius:4px;padding:.2rem .6rem;font:inherit;font-size:.8rem;cursor:pointer}:host ::ng-deep .sc-group-heading{font-size:.7rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;opacity:.7}.sc-group-list,.sc-gene-tree{max-height:16rem;overflow-y:auto;border:1px solid var(--surface-border, #dee2e6);border-radius:4px;padding:.25rem .4rem}.sc-group-row,.sc-gene-row{display:flex;align-items:center;gap:.45rem;padding:.15rem 0;font-size:.88rem}.sc-group-row .sc-group-label,.sc-gene-row .sc-group-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-group-all,.sc-gene-root,.sc-gene-group{font-weight:600}.sc-group-all .sc-icon-btn,.sc-gene-root .sc-icon-btn,.sc-gene-group .sc-icon-btn{margin-left:auto;font-size:.85rem}.sc-count{flex:0 0 auto;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sc-glyph-option{display:inline-flex;align-items:center;gap:.4rem}.sc-glyph-svg{width:1rem;height:1rem;fill:currentColor}:host ::ng-deep .sc-glyph-pick .p-dropdown-label{padding:.2rem .4rem}.sc-color-input{width:2.5rem;height:1.8rem;border:1px solid var(--surface-border, #ced4da);border-radius:4px;padding:0;background:none}.sc-row-number p-slider,.sc-row-range p-slider{flex:1 1 0;min-width:0}:host ::ng-deep .sc-num{width:4.5rem;padding:.3rem .4rem;text-align:center}:host ::ng-deep .sc-row-number p-inputnumber,:host ::ng-deep .sc-row-range p-inputnumber{flex:0 0 auto}:host{display:contents}\n"] }]
373
+ SpatialGeneStyleComponent,
374
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *ngIf=\"dataset as ds\">\n <!-- Observations: one marker per observation (cell centroid, Visium spot), and what\n colours them. The lead section for datasets without cell outlines. -->\n <div class=\"sc-panel\" [class.open]=\"open\">\n <div class=\"sc-panel-head\" (click)=\"openChange.emit(!open)\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showPoints\"\n (click)=\"$event.stopPropagation()\"\n (onChange)=\"onShowPoints($event.checked)\"\n ></p-checkbox>\n <span class=\"sc-panel-title\">Observations</span>\n <i class=\"pi\" [class.pi-chevron-down]=\"open\" [class.pi-chevron-right]=\"!open\"></i>\n </div>\n <div class=\"sc-panel-body\" *ngIf=\"open\">\n <small class=\"sc-hint\" *ngIf=\"hasCells && !is3d\">\n One circle per cell. With cell outlines on, circles are only drawn where cells are too small on screen to\n outline.\n </small>\n <!-- Colour source. Column and gene are mutually exclusive: picking one\n clears the other, so what drives the colours is never ambiguous. -->\n <div class=\"sc-section\">\n <label class=\"sc-lbl\" for=\"sc-column\">Colour by column</label>\n <p-dropdown\n inputId=\"sc-column\"\n [options]=\"columnOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"selectedColumn\"\n (ngModelChange)=\"onColumn($event)\"\n placeholder=\"None (flat colour)\"\n appendTo=\"body\"\n styleClass=\"sc-full\"\n ></p-dropdown>\n </div>\n\n <ng-container *ngIf=\"genes?.state$ | async as gene\">\n <div class=\"sc-section\" *ngIf=\"ds.features\">\n <label class=\"sc-lbl\" for=\"sc-gene\">Colour by gene</label>\n <!-- A filterable dropdown, not a free-text box: the genes are visible before\n anything is typed, and each keystroke narrows a list the user can see.\n Virtual-scrolled because a targeted panel runs to a few thousand names. -->\n <p-dropdown\n inputId=\"sc-gene\"\n [options]=\"gene.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"selectedGene\"\n (ngModelChange)=\"onGene($event)\"\n [filter]=\"true\"\n filterBy=\"label\"\n [resetFilterOnHide]=\"true\"\n (onFilter)=\"genes?.onFilter($event.filter)\"\n (onShow)=\"genes?.ensureList()\"\n [showClear]=\"true\"\n [virtualScroll]=\"gene.options.length > geneVirtualScrollFrom\"\n [virtualScrollItemSize]=\"30\"\n [emptyFilterMessage]=\"gene.remote ? 'No match \u2014 keep typing' : 'No match'\"\n [emptyMessage]=\"gene.remote ? 'Type to search genes' : 'No genes'\"\n appendTo=\"body\"\n placeholder=\"Search {{ ds.features.count | number }} genes\u2026\"\n styleClass=\"sc-full\"\n ></p-dropdown>\n <small class=\"sc-hint\" *ngIf=\"gene.failed\">Gene lookup failed.</small>\n </div>\n\n <!-- Which observations are drawn at all, by what they express. Several genes at\n once, unlike the colour, which reads one: picking genes here hides every\n observation that expresses none of them (or not all of them), and by default\n colours (and, in 2D, marks) each drawn one by its gene. -->\n <div class=\"sc-section\" *ngIf=\"ds.features\">\n <label class=\"sc-lbl\" for=\"sc-show-genes\">Show observations expressing</label>\n <p-multiSelect\n inputId=\"sc-show-genes\"\n [options]=\"gene.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"view.observationGenes\"\n (onChange)=\"onObservationGenes($event.value)\"\n [filter]=\"true\"\n filterBy=\"label\"\n [resetFilterOnHide]=\"true\"\n (onFilter)=\"genes?.onFilter($event.filter)\"\n (onPanelShow)=\"genes?.ensureList()\"\n [virtualScroll]=\"gene.options.length > geneVirtualScrollFrom\"\n [virtualScrollItemSize]=\"30\"\n [showToggleAll]=\"false\"\n [showClear]=\"true\"\n [emptyFilterMessage]=\"gene.remote ? 'No match \u2014 keep typing' : 'No match'\"\n [emptyMessage]=\"gene.remote ? 'Type to search genes' : 'No genes'\"\n appendTo=\"body\"\n placeholder=\"Any gene (every observation)\"\n [selectedItemsLabel]=\"'{0} genes'\"\n [maxSelectedLabels]=\"3\"\n styleClass=\"sc-full\"\n >\n <!-- A picked gene shows its icon in its colour; clicking it edits them. -->\n <ng-template let-option pTemplate=\"item\">\n <div class=\"sc-obs-gene-option\">\n <span class=\"sc-obs-gene-name\">{{ option.label }}</span>\n <ng-container *ngIf=\"isObservationGene(option.value)\">\n <ng-container *ngTemplateOutlet=\"geneMark; context: { $implicit: option.value }\"></ng-container>\n </ng-container>\n </div>\n </ng-template>\n </p-multiSelect>\n <p-selectButton\n *ngIf=\"view.observationGenes.length > 1\"\n [options]=\"matchOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"view.observationGenesMatch\"\n (ngModelChange)=\"onObservationGenesMatch($event)\"\n [allowEmpty]=\"false\"\n styleClass=\"sc-buttons\"\n ></p-selectButton>\n <!-- The picked genes: each one's icon and colour (click to edit), and a way out. -->\n <div class=\"sc-obs-genes\" *ngIf=\"view.observationGenes.length\">\n <div class=\"sc-obs-gene-row\" *ngFor=\"let g of view.observationGenes; trackBy: trackByGene\">\n <ng-container *ngTemplateOutlet=\"geneMark; context: { $implicit: g }\"></ng-container>\n <span class=\"sc-obs-gene-name\">{{ g }}</span>\n <button\n type=\"button\"\n class=\"sc-icon-btn\"\n [attr.aria-label]=\"'Stop showing ' + g\"\n pTooltip=\"Remove\"\n tooltipPosition=\"top\"\n (click)=\"removeObservationGene(g)\"\n >\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.observationGenes.length\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.observationGenesColorBy\"\n (onChange)=\"onObservationGenesColorBy($event.checked)\"\n label=\"Colour by these genes\"\n pTooltip=\"Each observation in the colour (and, in 2D, the icon) of the picked gene it expresses most, relative to that gene's highest value. Off, Colour by column or gene applies.\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <small class=\"sc-hint\" *ngIf=\"view.observationGenes.length\">{{ geneFilterHint }}</small>\n <!-- The filter is never dropped silently: a failed expression read keeps the observations\n as they were drawn, and says so. -->\n <small class=\"sc-hint sc-error\" *ngIf=\"view.observationGenes.length && (geneFilterFailed$ | async)\">\n The expression of the picked genes could not be read, so the observations still show the previous\n filter.\n <a href=\"\" (click)=\"$event.preventDefault(); retryGeneFilter()\">Try again</a>\n </small>\n </div>\n </ng-container>\n\n <spatial-key\n [controls]=\"controls\"\n [dataset]=\"ds\"\n [view]=\"view\"\n [legend]=\"legend\"\n [colorBarCss]=\"colorBarCss\"\n [selectedCategory]=\"selectedCategory\"\n [colormapOptions]=\"colormapOptions\"\n (categoryClicked)=\"categoryClicked.emit($event)\"\n ></spatial-key>\n\n <!-- Display -->\n <div class=\"sc-section\">\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Point size</span>\n <p-slider\n [ngModel]=\"view.pointScale\"\n [min]=\"0.25\"\n [max]=\"5\"\n [step]=\"0.25\"\n (onChange)=\"onPointScale($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.pointScale }}\u00D7</span>\n </div>\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Opacity</span>\n <p-slider\n [ngModel]=\"view.opacity\"\n [min]=\"0.1\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.opacity }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Only for a gene: the map draws that gene's expression as a field. In 2D\n it goes under the cells of the displayed plane; in 3D it is one sheet per\n imaged section, optionally smoothed along z into a volume. -->\n <div class=\"sc-section\" *ngIf=\"canMapGene\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.geneMap\"\n (onChange)=\"onGeneMap($event.checked)\"\n label=\"Gene map (expression field)\"\n [pTooltip]=\"\n is3d\n ? 'Draw this gene\\'s expression as one field per imaged section \u2014 an estimate between the cells, empty where nothing was measured'\n : 'Draw this gene\\'s expression as a smooth field UNDER the cells \u2014 an estimate between them, transparent where nothing was measured'\n \"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- 3D only: interpolating along z turns the measured sheets into an\n estimate, so it is its own decision and its own label. -->\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.geneMapVolume\"\n (onChange)=\"onGeneMapVolume($event.checked)\"\n label=\"Volume rendering (interpolate along z)\"\n pTooltip=\"Smooth the per-section fields into a continuous volume \u2014 an ESTIMATE between the imaged sections, not measured tissue\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- Meaningless while interpolating: a volume built from one section would\n smear that slide through the specimen's whole depth. -->\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d && isSectioned && !view.geneMapVolume\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"geneMapOneSection\"\n (onChange)=\"onGeneMapOneSection($event.checked)\"\n label=\"One map section at a time\"\n pTooltip=\"Draw a single imaged section's field instead of the whole stack of sheets\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap && is3d && isSectioned && !view.geneMapVolume && geneMapOneSection\">\n <span class=\"sc-lbl\">Map section</span>\n <p-slider\n [ngModel]=\"view.geneMapSection\"\n [min]=\"0\"\n [max]=\"lastSection\"\n [step]=\"1\"\n (onChange)=\"onGeneMapSection($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ geneMapSectionLabel }}</span>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap\">\n <span class=\"sc-lbl\">Smoothing</span>\n <p-slider\n [ngModel]=\"view.geneMapSmoothing\"\n [min]=\"0.5\"\n [max]=\"4\"\n [step]=\"0.5\"\n (onChange)=\"onGeneMapSmoothing($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.geneMapSmoothing }}\u00D7</span>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.geneMap\">\n <span class=\"sc-lbl\">Map opacity</span>\n <p-slider\n [ngModel]=\"view.geneMapOpacity\"\n [min]=\"0.1\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onGeneMapOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.geneMapOpacity }}</span>\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.geneMap && !is3d\">\n Kernel-weighted mean per cell, not a sum \u2014 so a crowded region does not glow for being crowded. Transparent\n where no cell was measured. Turn the cells' own Opacity down to read the field under them.\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.geneMap && is3d\">\n {{ geneMapVolumeNote }}\n </div>\n </div>\n\n <!-- 3D only: the volume, the cloud and the density volumes occupy the same\n space, so each hides the others. Independent toggles, because the useful\n views are the combinations \u2014 the field alone, one section's cells over it,\n the anatomy alone. -->\n <div class=\"sc-section\" *ngIf=\"is3d\">\n <span class=\"sc-lbl\">Show</span>\n <div class=\"sc-row\" *ngIf=\"ds.volume\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showVolume\"\n (onChange)=\"onShowVolume($event.checked)\"\n label=\"Reference volume\"\n pTooltip=\"The registered anatomy the observations sit in\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- The backdrop's own opacity: reading the cloud or a density field\n through the anatomy means turning the anatomy down, not the data. -->\n <div class=\"sc-row\" *ngIf=\"ds.volume && view.showVolume\">\n <span class=\"sc-lbl\">Volume opacity</span>\n <p-slider\n [ngModel]=\"view.volumeOpacity\"\n [min]=\"0.05\"\n [max]=\"1\"\n [step]=\"0.05\"\n (onChange)=\"onVolumeOpacity($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.volumeOpacity }}</span>\n </div>\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.showPoints\"\n (onChange)=\"onShowPoints($event.checked)\"\n label=\"Observations\"\n pTooltip=\"The measured cells. Hide them to read the density volumes underneath\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <!-- Only for data that really is sectioned; a continuous z has no sections\n to step through. -->\n <div class=\"sc-row\" *ngIf=\"view.showPoints && isSectioned\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"oneSection\"\n (onChange)=\"onOneSection($event.checked)\"\n label=\"One section at a time\"\n pTooltip=\"Draw a single imaged section's cells, so the density volumes between the sections stay visible\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.showPoints && isSectioned && oneSection\">\n <span class=\"sc-lbl\">Section</span>\n <p-slider\n [ngModel]=\"view.pointSection\"\n [min]=\"0\"\n [max]=\"lastSection\"\n [step]=\"1\"\n (onChange)=\"onPointSection($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ sectionLabel }}</span>\n </div>\n </div>\n\n <div class=\"sc-section\" *ngIf=\"is3d\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.densityVolume\"\n (onChange)=\"onDensityVolume($event.checked)\"\n label=\"Cluster density volumes\"\n pTooltip=\"Raymarch each cluster as a smooth density field \u2014 an ESTIMATE between the imaged sections, not measured cells\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\" *ngIf=\"view.densityVolume\">\n <span class=\"sc-lbl\">Smoothing</span>\n <p-slider\n [ngModel]=\"view.densitySmoothing\"\n [min]=\"0.5\"\n [max]=\"4\"\n [step]=\"0.5\"\n (onChange)=\"onDensitySmoothing($event.value)\"\n ></p-slider>\n <span class=\"sc-val\">{{ view.densitySmoothing }}\u00D7</span>\n </div>\n <div class=\"sc-hint\" *ngIf=\"view.densityVolume\">{{ densityNote }}</div>\n </div>\n\n <!-- Continuous-only knobs: meaningless for a categorical palette. -->\n <div class=\"sc-section\" *ngIf=\"isContinuous\">\n <div class=\"sc-row\">\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"view.logScale\"\n (onChange)=\"onLogScale($event.checked)\"\n label=\"Log scale\"\n pTooltip=\"Counts collapse against their outliers on a linear ramp\"\n tooltipPosition=\"top\"\n ></p-checkbox>\n </div>\n <div class=\"sc-row\">\n <span class=\"sc-lbl\">Clip outliers</span>\n <p-dropdown\n [options]=\"clipOptions\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"view.percentileClip\"\n (ngModelChange)=\"onClip($event)\"\n appendTo=\"body\"\n pTooltip=\"Percentile the contrast window is taken at\"\n tooltipPosition=\"top\"\n ></p-dropdown>\n <!-- Reserves the readout column this row has no readout for, so the\n control's right edge lines up with every slider's. -->\n <span class=\"sc-val\"></span>\n </div>\n </div>\n\n <!-- A gene's icon in its colour: the button that opens its icon and colour picker. -->\n <ng-template #geneMark let-g>\n <button\n type=\"button\"\n class=\"sc-gene-icon\"\n [style.color]=\"geneColorOf(g)\"\n [attr.aria-label]=\"'Icon and colour for ' + g\"\n pTooltip=\"Icon and colour\"\n tooltipPosition=\"top\"\n (click)=\"openGeneStyle($event, g, genePop)\"\n >\n <svg viewBox=\"-1.2 -1.2 2.4 2.4\" class=\"sc-glyph-svg\">\n <polygon [attr.points]=\"glyphPointsOf(g)\"></polygon>\n </svg>\n </button>\n </ng-template>\n <p-overlayPanel #genePop styleClass=\"sc-gene-style\" appendTo=\"body\">\n <ng-template pTemplate=\"content\">\n <spatial-gene-style\n *ngIf=\"styleGene as g\"\n [gene]=\"g\"\n what=\"Observation\"\n [color]=\"geneColorOf(g)\"\n [glyph]=\"geneGlyphOf(g)\"\n iconHint=\"Icons show on the 2D observation markers.\"\n (colorChange)=\"onGeneColor(g, $event)\"\n (glyphChange)=\"onGeneGlyph(g, $event)\"\n (resetStyle)=\"resetGeneStyle(g)\"\n ></spatial-gene-style>\n </ng-template>\n </p-overlayPanel>\n</ng-container>\n", styles: [".sc-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d)}.sc-error{color:var(--red-600, #c62828)}.sc-section{display:flex;flex-direction:column;gap:.35rem}.sc-lbl{font-size:.78rem;color:var(--text-color-secondary, #6c757d);white-space:nowrap}.sc-row{display:flex;align-items:center;gap:.6rem}.sc-row p-slider,.sc-row p-dropdown,.sc-row p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row p-slider .p-slider{width:100%}:host ::ng-deep .sc-row p-dropdown .p-dropdown,:host ::ng-deep .sc-row p-treeSelect .p-treeselect{width:100%}.sc-row .sc-lbl{flex:0 0 calc(50% - .6rem);min-width:0;overflow:hidden;text-overflow:ellipsis}.sc-cmap-option{display:inline-flex;align-items:center;gap:.4rem}.sc-cmap-swatch{width:3.5rem;height:.7rem;border-radius:2px;display:block}.sc-val{flex:0 0 4rem;text-align:right;font-size:.78rem;font-variant-numeric:tabular-nums;white-space:nowrap}:host ::ng-deep .sc-full,:host ::ng-deep .sc-full .p-dropdown,:host ::ng-deep .sc-full .p-autocomplete,:host ::ng-deep .sc-full input{width:100%}.sc-colorbar{height:.75rem;border-radius:2px;border:1px solid rgba(0,0,0,.2)}.sc-colorbar-ends{display:flex;justify-content:space-between;font-size:.7rem;color:var(--text-color-secondary, #6c757d);margin-top:.2rem}.sc-panel{border:1px solid var(--surface-border, #dee2e6);border-radius:6px;margin-bottom:.5rem;overflow:hidden}.sc-panel-head{display:flex;align-items:center;gap:.6rem;padding:.5rem .7rem;cursor:pointer;-webkit-user-select:none;user-select:none;background:var(--surface-100, #f5f6f7)}.sc-panel-head .sc-panel-title{flex:1;font-weight:600}.sc-panel.open>.sc-panel-head{background:var(--primary-color, #2196f3);color:var(--primary-color-text, #fff)}.sc-panel-body{display:flex;flex-direction:column;gap:.45rem;padding:.6rem .75rem .75rem}.sc-subhead{display:flex;align-items:center;gap:.5rem;margin-top:.4rem;font-size:.72rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-color-secondary, #6c757d)}.sc-subhead:after{content:\"\";flex:1;border-top:1px solid var(--surface-border, #dee2e6)}:host ::ng-deep .sc-buttons .p-button{padding:.35rem .75rem;font-size:.85rem}.sc-row-picker p-dropdown,.sc-row-picker p-multiSelect,.sc-row-picker p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row-picker .sc-variant{width:6.5rem}:host ::ng-deep .sc-row-picker p-multiselect .p-multiselect{width:100%}:host ::ng-deep .sc-row-picker p-dropdown:has(.sc-variant){flex:0 0 6.5rem}:host ::ng-deep .sc-row-picker p-treeselect:has(.sc-palette){flex:0 0 3rem}:host ::ng-deep .sc-palette .p-treeselect-label{width:0;padding:0}.sc-icon-btn{flex:0 0 auto;border:0;background:transparent;cursor:pointer;padding:.25rem .35rem;font:inherit;font-size:1.05rem;color:var(--text-color, #333);border-radius:4px}.sc-icon-btn:hover{background:var(--surface-200, #e9ecef)}.sc-icon-btn.active{color:var(--primary-color, #2196f3);font-weight:700}.sc-icon-btn:disabled{opacity:.5;cursor:default}.sc-small-btn{margin-left:auto;border:1px solid var(--surface-border, #ced4da);background:var(--surface-0, #fff);border-radius:4px;padding:.2rem .6rem;font:inherit;font-size:.8rem;cursor:pointer}:host ::ng-deep .sc-group-heading{font-size:.7rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;opacity:.7}.sc-group-list,.sc-gene-tree{max-height:16rem;overflow-y:auto;border:1px solid var(--surface-border, #dee2e6);border-radius:4px;padding:.25rem .4rem}.sc-group-row,.sc-gene-row{display:flex;align-items:center;gap:.45rem;padding:.15rem 0;font-size:.88rem}.sc-group-row .sc-group-label,.sc-gene-row .sc-group-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-group-all,.sc-gene-root,.sc-gene-group{font-weight:600}.sc-group-all .sc-icon-btn,.sc-gene-root .sc-icon-btn,.sc-gene-group .sc-icon-btn{margin-left:auto;font-size:.85rem}.sc-count{flex:0 0 auto;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sc-glyph-option{display:inline-flex;align-items:center;gap:.4rem}.sc-glyph-svg{width:1rem;height:1rem;fill:currentColor}:host ::ng-deep .sc-glyph-pick .p-dropdown-label{padding:.2rem .4rem}.sc-color-input{width:2.5rem;height:1.8rem;border:1px solid var(--surface-border, #ced4da);border-radius:4px;padding:0;background:none}.sc-row-number p-slider,.sc-row-range p-slider{flex:1 1 0;min-width:0}:host ::ng-deep .sc-num{width:4.5rem;padding:.3rem .4rem;text-align:center}:host ::ng-deep .sc-row-number p-inputnumber,:host ::ng-deep .sc-row-range p-inputnumber{flex:0 0 auto}.sc-gene-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1.4rem;height:1.4rem;padding:0;border:1px solid transparent;border-radius:4px;background:transparent;cursor:pointer}.sc-gene-icon:hover,.sc-gene-icon:focus-visible{border-color:var(--surface-border, rgba(0, 0, 0, .2))}:host{display:contents}.sc-obs-genes{display:flex;flex-direction:column;gap:.15rem;margin-top:.35rem}.sc-obs-gene-row,.sc-obs-gene-option{display:flex;align-items:center;gap:.4rem;min-width:0}.sc-obs-gene-option{flex:1 1 auto;justify-content:space-between}.sc-obs-gene-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"] }]
273
375
  }], propDecorators: { controls: [{
274
376
  type: Input
275
377
  }], dataset: [{
@@ -297,4 +399,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
297
399
  }], categoryClicked: [{
298
400
  type: Output
299
401
  }] } });
300
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC1vYnNlcnZhdGlvbnMtcGFuZWwuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9zcGF0aWFsLWNvbnRyb2xzL3NwYXRpYWwtb2JzZXJ2YXRpb25zLXBhbmVsL3NwYXRpYWwtb2JzZXJ2YXRpb25zLXBhbmVsLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvc3BhdGlhbC1jb250cm9scy9zcGF0aWFsLW9ic2VydmF0aW9ucy1wYW5lbC9zcGF0aWFsLW9ic2VydmF0aW9ucy1wYW5lbC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFDVCxZQUFZLEVBQ1osS0FBSyxFQUVMLE1BQU0sR0FFUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDbEQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUloRCxPQUFPLEVBQWdCLG9CQUFvQixFQUFvQixNQUFNLCtCQUErQixDQUFDO0FBQ3JHLE9BQU8sRUFBd0IsY0FBYyxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDdkYsT0FBTyxFQUNMLFlBQVksRUFFWixhQUFhLEVBQ2IsYUFBYSxFQUNiLFlBQVksR0FDYixNQUFNLG1DQUFtQyxDQUFDO0FBRzNDLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHNDQUFzQyxDQUFDOzs7Ozs7OztBQUUzRTs7Ozs7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LENBQUM7OEdBcFBVLGlDQUFpQztrR0FBakMsaUNBQWlDLHNiQ3pEOUMscTlZQXFUQSwrOUlEeFFJLFlBQVksK09BQ1osV0FBVyw4VkFDWCxjQUFjLGdaQUNkLGNBQWMsdXRDQUNkLFlBQVksK1JBQ1osYUFBYSxzWEFDYixtQkFBbUI7OzJGQU1WLGlDQUFpQztrQkFoQjdDLFNBQVM7K0JBQ0UsNEJBQTRCLGNBQzFCLElBQUksV0FDUDt3QkFDUCxZQUFZO3dCQUNaLFdBQVc7d0JBQ1gsY0FBYzt3QkFDZCxjQUFjO3dCQUNkLFlBQVk7d0JBQ1osYUFBYTt3QkFDYixtQkFBbUI7cUJBQ3BCLG1CQUdnQix1QkFBdUIsQ0FBQyxNQUFNOzhCQUl0QyxRQUFRO3NCQUFoQixLQUFLO2dCQUVHLE9BQU87c0JBQWYsS0FBSztnQkFFRyxJQUFJO3NCQUFaLEtBQUs7Z0JBRUcsS0FBSztzQkFBYixLQUFLO2dCQUVHLE1BQU07c0JBQWQsS0FBSztnQkFFRyxXQUFXO3NCQUFuQixLQUFLO2dCQUVHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBRUcsZ0JBQWdCO3NCQUF4QixLQUFLO2dCQUVHLGVBQWU7c0JBQXZCLEtBQUs7Z0JBRUcsSUFBSTtzQkFBWixLQUFLO2dCQUVHLElBQUk7c0JBQVosS0FBSztnQkFFYSxVQUFVO3NCQUE1QixNQUFNO2dCQUVZLGVBQWU7c0JBQWpDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgQ29tcG9uZW50LFxuICBFdmVudEVtaXR0ZXIsXG4gIElucHV0LFxuICBPbkNoYW5nZXMsXG4gIE91dHB1dCxcbiAgU2ltcGxlQ2hhbmdlcyxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgeyBDaGVja2JveE1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvY2hlY2tib3gnO1xuaW1wb3J0IHsgRHJvcGRvd25Nb2R1bGUgfSBmcm9tICdwcmltZW5nL2Ryb3Bkb3duJztcbmltcG9ydCB7IFNsaWRlck1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvc2xpZGVyJztcbmltcG9ydCB7IFRvb2x0aXBNb2R1bGUgfSBmcm9tICdwcmltZW5nL3Rvb2x0aXAnO1xuXG5pbXBvcnQgdHlwZSB7IElTcGF0aWFsQ29udHJvbHMgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvdmlzdWFsaXplci5jb250cmFjdCc7XG5pbXBvcnQgdHlwZSB7IFNwYXRpYWxEYXRhc2V0IH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5pbXBvcnQgeyBDb2xvcm1hcE5vZGUsIERFRkFVTFRfU1BBVElBTF9WSUVXLCBTcGF0aWFsVmlld1N0YXRlIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2Rpc3BsYXktdHlwZXMnO1xuaW1wb3J0IHsgU3BhdGlhbFNlbGVjdGlvbk1hc2ssIGVtcHR5U2VsZWN0aW9uIH0gZnJvbSAnLi4vLi4vc3BhdGlhbC9zcGF0aWFsLXNlbGVjdGlvbic7XG5pbXBvcnQge1xuICBDTElQX09QVElPTlMsXG4gIFBhbmVsT3B0aW9uLFxuICBjb2x1bW5PcHRpb25zLFxuICBtaWRkbGVTZWN0aW9uLFxuICBzZWN0aW9uTGFiZWwsXG59IGZyb20gJy4uLy4uL3NwYXRpYWwvc3BhdGlhbC1wYW5lbC1tb2RlbCc7XG5pbXBvcnQgdHlwZSB7IEdlbmVQaWNrZXJNb2RlbCB9IGZyb20gJy4uL3NwYXRpYWwtZ2VuZS1waWNrZXInO1xuaW1wb3J0IHR5cGUgeyBTcGF0aWFsTGVnZW5kRW50cnkgfSBmcm9tICcuLi9zcGF0aWFsLWtleS9zcGF0aWFsLWtleS5tb2RlbCc7XG5pbXBvcnQgeyBTcGF0aWFsS2V5Q29tcG9uZW50IH0gZnJvbSAnLi4vc3BhdGlhbC1rZXkvc3BhdGlhbC1rZXkuY29tcG9uZW50JztcblxuLyoqXG4gKiBUaGUgT2JzZXJ2YXRpb25zIHNlY3Rpb24g4oCUIG9uZSBtYXJrZXIgcGVyIG9ic2VydmF0aW9uIChjZWxsIGNlbnRyb2lkLCBWaXNpdW0gc3BvdCkg4oCUIGFuZFxuICogd2hhdCBlbHNlIGRyYXdzIHRoZW06IHdoYXQgY29sb3VycyB0aGVtIChhIGNvbHVtbiBvciBhIGdlbmUsIG11dHVhbGx5IGV4Y2x1c2l2ZSwgd2l0aFxuICogdGhlIHtAbGluayBTcGF0aWFsS2V5Q29tcG9uZW50IGtleX0pLCBwb2ludCBzaXplIGFuZCBvcGFjaXR5LCB0aGUgZ2VuZSBtYXAsIHRoZSAzRCBzY2VuZVxuICogKHZvbHVtZSwgY2xvdWQsIHNlY3Rpb25zLCBkZW5zaXR5IHZvbHVtZXMpIGFuZCB0aGUgY29udGludW91cy1vbmx5IGtub2JzLlxuICpcbiAqIFJlbmRlcnMgdGhlIHNlY3Rpb24gYW5kIHRoZSBzZWN0aW9ucyBhZnRlciBpdCBhcyBzaWJsaW5ncyBpbiB0aGUgZGlhbG9nIGJvZHlcbiAqIChgZGlzcGxheTogY29udGVudHNgKS4gV3JpdGVzIG9ubHkgdGhyb3VnaCB0aGUgcG9ydDogYHNldFZpZXdTdGF0ZWAsIGFuZCB0aGUgY29sb3VyXG4gKiBzb3VyY2UncyBvd24gYGNvbG9yQnlDb2x1bW5gIC8gYGNvbG9yQnlGZWF0dXJlYCAvIGBjbGVhckNvbG9yQnlgLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdzcGF0aWFsLW9ic2VydmF0aW9ucy1wYW5lbCcsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBDb21tb25Nb2R1bGUsXG4gICAgRm9ybXNNb2R1bGUsXG4gICAgQ2hlY2tib3hNb2R1bGUsXG4gICAgRHJvcGRvd25Nb2R1bGUsXG4gICAgU2xpZGVyTW9kdWxlLFxuICAgIFRvb2x0aXBNb2R1bGUsXG4gICAgU3BhdGlhbEtleUNvbXBvbmVudCxcbiAgXSxcbiAgdGVtcGxhdGVVcmw6ICcuL3NwYXRpYWwtb2JzZXJ2YXRpb25zLXBhbmVsLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vc3BhdGlhbC1vYnNlcnZhdGlvbnMtcGFuZWwuY29tcG9uZW50LnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIFNwYXRpYWxPYnNlcnZhdGlvbnNQYW5lbENvbXBvbmVudCBpbXBsZW1lbnRzIE9uQ2hhbmdlcyB7XG4gIC8qKiBUaGUgc3BhdGlhbCBjb250cm9scyB0aGlzIHBhbmVsIHJlYWRzIGFuZCB3cml0ZXMgdGhyb3VnaDsgbnVsbCB3aXRob3V0IGEgYFNQQVRJQUxfREFUQV9QT1JUYC4gKi9cbiAgQElucHV0KCkgY29udHJvbHM6IElTcGF0aWFsQ29udHJvbHMgfCBudWxsID0gbnVsbDtcbiAgLyoqIFRoZSBkYXRhc2V0IG9uIG9mZmVyLCBvciBudWxsLiAqL1xuICBASW5wdXQoKSBkYXRhc2V0OiBTcGF0aWFsRGF0YXNldCB8IG51bGwgPSBudWxsO1xuICAvKiogVGhlIGN1cnJlbnQgc3BhdGlhbCB2aWV3IHN0YXRlOiB3aGF0IGlzIGRyYXduLCBhbmQgaG93LiAqL1xuICBASW5wdXQoKSB2aWV3OiBTcGF0aWFsVmlld1N0YXRlID0gREVGQVVMVF9TUEFUSUFMX1ZJRVc7XG4gIC8qKiBUaGUgZ2VuZSBsaXN0IFwiQ29sb3VyIGJ5IGdlbmVcIiBwaWNrcyBmcm9tLCBzaGFyZWQgd2l0aCB0aGUgb3RoZXIgZ2VuZSBkcm9wZG93bnMuICovXG4gIEBJbnB1dCgpIGdlbmVzOiBHZW5lUGlja2VyTW9kZWwgfCBudWxsID0gbnVsbDtcbiAgLyoqIFRoZSBrZXkncyBjYXRlZ29yaWNhbCBsZWdlbmQsIG9yIG51bGwg4oCUIHdoaWNoIGFsc28gZGVjaWRlcyB0aGUgY29udGludW91cyBrbm9icy4gKi9cbiAgQElucHV0KCkgbGVnZW5kOiBTcGF0aWFsTGVnZW5kRW50cnlbXSB8IG51bGwgPSBudWxsO1xuICAvKiogVGhlIGtleSdzIGNvbnRpbnVvdXMgY29sb3VyIGJhciAoYSBDU1MgZ3JhZGllbnQpLCBvciBudWxsIHdoZW4gY2F0ZWdvcmljYWwuICovXG4gIEBJbnB1dCgpIGNvbG9yQmFyQ3NzOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgLyoqIFRoZSBjdXJyZW50IHNlbGVjdGlvbiAoaXRzIGNvdW50LCBhbmQgd2hhdCBpcyBtdXRlZCkuICovXG4gIEBJbnB1dCgpIHNlbGVjdGlvbjogU3BhdGlhbFNlbGVjdGlvbk1hc2sgPSBlbXB0eVNlbGVjdGlvbigpO1xuICAvKiogVGhlIGxlZ2VuZCByb3cgd2hvc2UgY2F0ZWdvcnkgaXMgc2VsZWN0ZWQsIGZvciBoaWdobGlnaHRpbmcuICovXG4gIEBJbnB1dCgpIHNlbGVjdGVkQ2F0ZWdvcnk6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICAvKiogQ29sb3JtYXAgdHJlZSBmb3IgdGhlIGNvbnRpbnVvdXMgY29sb3VyIHNjYWxlLiAqL1xuICBASW5wdXQoKSBjb2xvcm1hcE9wdGlvbnM6IENvbG9ybWFwTm9kZVtdID0gW107XG4gIC8qKiBTZXQgd2hpbGUgdGhlIDNEIGNsb3VkIGlzIHRoZSBhY3RpdmUgbW9kZS4gKi9cbiAgQElucHV0KCkgaXMzZCA9IGZhbHNlO1xuICAvKiogV2hldGhlciB0aGUgT2JzZXJ2YXRpb25zIHNlY3Rpb24gaXMgZXhwYW5kZWQuICovXG4gIEBJbnB1dCgpIG9wZW4gPSBmYWxzZTtcbiAgLyoqIEFza3MgdG8gb3BlbiBvciBjbG9zZSBpdDsgdHdvLXdheSB3aXRoIGBvcGVuYC4gKi9cbiAgQE91dHB1dCgpIHJlYWRvbmx5IG9wZW5DaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPGJvb2xlYW4+KCk7XG4gIC8qKiBBIGxlZ2VuZCByb3cgd2FzIGNsaWNrZWQgKHNlZSB0aGUga2V5KS4gKi9cbiAgQE91dHB1dCgpIHJlYWRvbmx5IGNhdGVnb3J5Q2xpY2tlZCA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyPigpO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBjbGlwT3B0aW9ucyA9IENMSVBfT1BUSU9OUztcbiAgLyoqIFZpcnR1YWwtc2Nyb2xsZWQgb25seSBwYXN0IHRoaXMgbWFueSBvcHRpb25zIChzZWUgdGhlIGNlbGxzIHBhbmVsJ3MgZ2VuZSBkcm9wZG93bikuICovXG4gIHByb3RlY3RlZCByZWFkb25seSBnZW5lVmlydHVhbFNjcm9sbEZyb20gPSAyMDA7XG5cbiAgLyoqIENvbG91ci1ieSBjb2x1bW4gY2hvaWNlcyDigJQgXCJOb25lXCIgcGx1cyBldmVyeSBjb2x1bW4gdGhlIGRhdGFzZXQgZGVjbGFyZXMuICovXG4gIHByb3RlY3RlZCBjb2x1bW5PcHRpb25zOiBQYW5lbE9wdGlvbjxzdHJpbmcgfCBudWxsPltdID0gW107XG4gIHByb3RlY3RlZCBzZWxlY3RlZENvbHVtbjogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIHByb3RlY3RlZCBzZWxlY3RlZEdlbmU6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAvKipcbiAgICogVGhlIGRhdGFzZXQncyBpbWFnZWQgc2VjdGlvbiBwb3NpdGlvbnMsIG9yIG51bGwgd2hlbiBpdHMgeiBpcyBjb250aW51b3VzIHJhdGhlciB0aGFuXG4gICAqIHNlY3Rpb25lZC4gUmVhZCBvbmNlIHBlciBkYXRhc2V0IOKAlCB0aGUgc2NhbiB3YWxrcyB0aGUgeiBvZiBldmVyeSBvYnNlcnZhdGlvbiwgc28gaXRcbiAgICogbXVzdCBub3Qgc2l0IGluIGEgdGVtcGxhdGUgZ2V0dGVyLlxuICAgKi9cbiAgcHJvdGVjdGVkIHNlY3Rpb25zOiBGbG9hdDMyQXJyYXkgfCBudWxsID0gbnVsbDtcblxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgaWYgKGNoYW5nZXNbJ2RhdGFzZXQnXSB8fCBjaGFuZ2VzWydjb250cm9scyddKSB7XG4gICAgICB0aGlzLmNvbHVtbk9wdGlvbnMgPSBjb2x1bW5PcHRpb25zKHRoaXMuZGF0YXNldCk7XG4gICAgICAvLyBBIG5ldyBkYXRhc2V0IGFsbW9zdCBjZXJ0YWlubHkgaGFzIGRpZmZlcmVudCBjb2x1bW5zOyBkcm9wIHN0YWxlIFVJIHN0YXRlLlxuICAgICAgdGhpcy5zZWxlY3RlZEdlbmUgPSBudWxsO1xuICAgICAgdGhpcy5zZWN0aW9ucyA9IHRoaXMuY29udHJvbHM/LnNhbXBsZWRTZWN0aW9ucygpID8/IG51bGw7XG4gICAgfVxuICAgIGlmIChjaGFuZ2VzWyd2aWV3J10pIHtcbiAgICAgIHRoaXMuc2VsZWN0ZWRDb2x1bW4gPSB0aGlzLnZpZXcuY29sb3JCeT8ua2luZCA9PT0gJ2NvbHVtbicgPyB0aGlzLnZpZXcuY29sb3JCeS5uYW1lIDogbnVsbDtcbiAgICAgIHRoaXMuc2VsZWN0ZWRHZW5lID0gdGhpcy52aWV3LmNvbG9yQnk/LmtpbmQgPT09ICdmZWF0dXJlJyA/IHRoaXMudmlldy5jb2xvckJ5Lm5hbWUgOiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8vIOKUgOKUgCBjb2xvdXIgc291cmNlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBDb2x1bW4gZHJvcGRvd24uIENob29zaW5nIGEgY29sdW1uIHN1cGVyc2VkZXMgYW55IGdlbmUgc2VsZWN0aW9uLiAqL1xuICBwcm90ZWN0ZWQgb25Db2x1bW4obmFtZTogc3RyaW5nIHwgbnVsbCk6IHZvaWQge1xuICAgIHRoaXMuc2VsZWN0ZWRDb2x1bW4gPSBuYW1lO1xuICAgIHRoaXMuc2VsZWN0ZWRHZW5lID0gbnVsbDtcbiAgICBpZiAoIW5hbWUpIHtcbiAgICAgIHRoaXMuY29udHJvbHM/LmNsZWFyQ29sb3JCeSgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmNvbnRyb2xzPy5jb2xvckJ5Q29sdW1uKG5hbWUpO1xuICB9XG5cbiAgLyoqIEEgZ2VuZSB3YXMgcGlja2VkOyBpdCBzdXBlcnNlZGVzIGFueSBjb2x1bW4gc2VsZWN0aW9uLiAqL1xuICBwcm90ZWN0ZWQgb25HZW5lKG5hbWU6IHN0cmluZyB8IG51bGwpOiB2b2lkIHtcbiAgICB0aGlzLnNlbGVjdGVkR2VuZSA9IG5hbWU7XG4gICAgaWYgKCFuYW1lKSB7XG4gICAgICB0aGlzLmNvbnRyb2xzPy5jbGVhckNvbG9yQnkoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5zZWxlY3RlZENvbHVtbiA9IG51bGw7XG4gICAgdGhpcy5jb250cm9scz8uY29sb3JCeUZlYXR1cmUobmFtZSk7XG4gIH1cblxuICAvKiogQ2VsbCBvdXRsaW5lcyBvbiBvZmZlciwgd2hpY2ggY2hhbmdlcyB3aGF0IHRoZSBjaXJjbGVzIG1lYW4uICovXG4gIHByb3RlY3RlZCBnZXQgaGFzQ2VsbHMoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICEhKHRoaXMuZGF0YXNldD8ucG9seWdvblRpbGVzIHx8IHRoaXMuZGF0YXNldD8ucG9seWdvbnMpO1xuICB9XG5cbiAgLyoqIFRydWUgd2hpbGUgYSBzZWxlY3Rpb24gaXMgYWN0aXZlIOKAlCBldmVyeXRoaW5nIGVsc2UgcmVuZGVycyBtdXRlZC4gKi9cbiAgZ2V0IGhhc1NlbGVjdGlvbigpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zZWxlY3Rpb24uY291bnQgPiAwO1xuICB9XG5cbiAgLyoqIFdoZXRoZXIgbG9nL2NsaXAgYXBwbHkg4oCUIHRoZXkgYXJlIGNvbnRpbnVvdXMtb25seSBrbm9icy4gKi9cbiAgcHJvdGVjdGVkIGdldCBpc0NvbnRpbnVvdXMoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICEhdGhpcy52aWV3LmNvbG9yQnkgJiYgdGhpcy5sZWdlbmQgPT09IG51bGw7XG4gIH1cblxuICAvLyDilIDilIAgZGlzcGxheSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvLyBQcmltZU5HJ3Mgc2xpZGVyIHJlcG9ydHMgYG51bWJlciB8IHVuZGVmaW5lZGA7IGlnbm9yZSB0aGUgZW1wdHkgY2FzZSByYXRoZXJcbiAgLy8gdGhhbiB3cml0aW5nIGB1bmRlZmluZWRgIGludG8gdGhlIHN0b3JlIGFuZCByZW5kZXJpbmcgTmFOLXNpemVkIG1hcmtlcnMuXG4gIHByb3RlY3RlZCBvblBvaW50U2NhbGUodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgcG9pbnRTY2FsZTogdmFsdWUgfSk7XG4gIH1cbiAgcHJvdGVjdGVkIG9uT3BhY2l0eSh2YWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkKTogdm9pZCB7XG4gICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHJldHVybjtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBvcGFjaXR5OiB2YWx1ZSB9KTtcbiAgfVxuICBwcm90ZWN0ZWQgb25Mb2dTY2FsZShvbjogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IGxvZ1NjYWxlOiBvbiB9KTtcbiAgfVxuICBwcm90ZWN0ZWQgb25HZW5lTWFwKG9uOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgZ2VuZU1hcDogb24gfSk7XG4gIH1cbiAgLyoqIDNEOiBzbW9vdGggdGhlIHBlci1zZWN0aW9uIHNoZWV0cyBhbG9uZyB6IGludG8gYSBjb250aW51b3VzIHZvbHVtZS4gKi9cbiAgcHJvdGVjdGVkIG9uR2VuZU1hcFZvbHVtZShvbjogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IGdlbmVNYXBWb2x1bWU6IG9uIH0pO1xuICB9XG4gIC8qKiAzRDogcmVzdHJpY3QgdGhlIHNoZWV0cyB0byBvbmUgaW1hZ2VkIHNlY3Rpb24uICovXG4gIHByb3RlY3RlZCBvbkdlbmVNYXBPbmVTZWN0aW9uKG9uOiBib29sZWFuKTogdm9pZCB7XG4gICAgaWYgKCFvbikge1xuICAgICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgZ2VuZU1hcFNlY3Rpb246IG51bGwgfSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IGdlbmVNYXBTZWN0aW9uOiBtaWRkbGVTZWN0aW9uKHRoaXMuc2VjdGlvbnMpIH0pO1xuICB9XG4gIHByb3RlY3RlZCBvbkdlbmVNYXBTZWN0aW9uKHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IGdlbmVNYXBTZWN0aW9uOiB2YWx1ZSB9KTtcbiAgfVxuICBwcm90ZWN0ZWQgZ2V0IGdlbmVNYXBPbmVTZWN0aW9uKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnZpZXcuZ2VuZU1hcFNlY3Rpb24gIT0gbnVsbDtcbiAgfVxuICAvKiogXCIxMiBvZiA1M1wiIGZvciB0aGUgZ2VuZSBtYXAncyBvd24gc2VjdGlvbiwgMS1iYXNlZCBsaWtlIHRoZSBjbG91ZCdzLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGdlbmVNYXBTZWN0aW9uTGFiZWwoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gc2VjdGlvbkxhYmVsKHRoaXMuc2VjdGlvbnMsIHRoaXMudmlldy5nZW5lTWFwU2VjdGlvbik7XG4gIH1cbiAgcHJvdGVjdGVkIG9uR2VuZU1hcFNtb290aGluZyh2YWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkKTogdm9pZCB7XG4gICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHJldHVybjtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBnZW5lTWFwU21vb3RoaW5nOiB2YWx1ZSB9KTtcbiAgfVxuICBwcm90ZWN0ZWQgb25HZW5lTWFwT3BhY2l0eSh2YWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkKTogdm9pZCB7XG4gICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHJldHVybjtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBnZW5lTWFwT3BhY2l0eTogdmFsdWUgfSk7XG4gIH1cbiAgLyoqXG4gICAqIFdoYXQgdGhlIDNEIGdlbmUgbWFwIGlzIGN1cnJlbnRseSBzaG93aW5nLCBzYWlkIHBsYWlubHkg4oCUIHRoZSBzaGVldHMgYXJlIGFcbiAgICogbWVhc3VyZW1lbnQgYW5kIHRoZSB2b2x1bWUgaXMgYW4gZXN0aW1hdGUsIGFuZCB0aGUgcGFuZWwgaGFzIHRvIGJlIHRoZSBwbGFjZVxuICAgKiB0aGF0IHNheXMgd2hpY2ggb25lIGlzIG9uIHNjcmVlbi5cbiAgICovXG4gIHByb3RlY3RlZCBnZXQgZ2VuZU1hcFZvbHVtZU5vdGUoKTogc3RyaW5nIHtcbiAgICBjb25zdCB0b3RhbCA9IHRoaXMuc2VjdGlvbnM/Lmxlbmd0aCA/PyAwO1xuICAgIGlmICh0aGlzLnZpZXcuZ2VuZU1hcFZvbHVtZSkge1xuICAgICAgcmV0dXJuIChcbiAgICAgICAgJ0ludGVycG9sYXRlZCBhbG9uZyB6OiB0aGUgcGxhbmVzIGJldHdlZW4gdGhlIGltYWdlZCBzZWN0aW9ucyBjYXJyeSBhbiAnICtcbiAgICAgICAgXCJFU1RJTUFURSwgc21vb3RoZWQgZnJvbSB0aGVpciBuZWlnaGJvdXJzJyBtZWFuLiBOb3RoaW5nIGlzIGRyYXduIGJleW9uZCB0aGUgXCIgK1xuICAgICAgICAnb3V0ZXJtb3N0IHNlY3Rpb24uJ1xuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuZ2VuZU1hcE9uZVNlY3Rpb24pIHtcbiAgICAgIHJldHVybiAoXG4gICAgICAgIFwiT25lIGltYWdlZCBzZWN0aW9uJ3MgZmllbGQg4oCUIG1lYXN1cmVkLCBub3QgaW50ZXJwb2xhdGVkLiBIaWRlIHRoZSBcIiArXG4gICAgICAgICdvYnNlcnZhdGlvbnMgdG8gcmVhZCBpdCwgb3IgbGVhdmUgdGhlbSBvbiB0byBjaGVjayB0aGUgZmllbGQgYWdhaW5zdCB0aGVtLidcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiAoXG4gICAgICBgT25lIGZpZWxkIHBlciBpbWFnZWQgc2VjdGlvbiR7dG90YWwgPyBgICgke3RvdGFsfSlgIDogJyd9LCBhdCBpdHMgb3duIGRlcHRoLCBgICtcbiAgICAgICd3aXRoIHRoZSBnYXBzIGJldHdlZW4gc2VjdGlvbnMgZW1wdHkuIEtlcm5lbC13ZWlnaHRlZCBtZWFuIHBlciBjZWxsLCBub3QgYSBzdW0uJ1xuICAgICk7XG4gIH1cblxuICAvKiogVHJ1ZSB3aGlsZSBhIGdlbmUgaXMgdGhlIGNvbG91ciBzb3VyY2Ug4oCUIHRoZSBvbmx5IHRoaW5nIGEgZ2VuZSBtYXAgY2FuIG1hcC4gKi9cbiAgcHJvdGVjdGVkIGdldCBjYW5NYXBHZW5lKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnZpZXcuY29sb3JCeT8ua2luZCA9PT0gJ2ZlYXR1cmUnO1xuICB9XG5cbiAgLy8g4pSA4pSAIHdoYXQgdGhlIDNEIHNjZW5lIGRyYXdzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICAvLyBUaGUgdm9sdW1lLCB0aGUgY2xvdWQgYW5kIHRoZSBkZW5zaXR5IHZvbHVtZXMgc2hhcmUgb25lIHNwYWNlLCBzbyBlYWNoIG9uZVxuICAvLyBoaWRlcyB0aGUgb3RoZXJzIHRvIHNvbWUgZGVncmVlLiBJbmRlcGVuZGVudCB0b2dnbGVzIGJlY2F1c2UgdGhlIHVzZWZ1bCB2aWV3c1xuICAvLyBhcmUgdGhlIGNvbWJpbmF0aW9ucywgbm90IGEgc2luZ2xlIFwiM0QgbW9kZVwiLlxuXG4gIHByb3RlY3RlZCBvblNob3dWb2x1bWUob246IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBzaG93Vm9sdW1lOiBvbiB9KTtcbiAgfVxuICBwcm90ZWN0ZWQgb25Wb2x1bWVPcGFjaXR5KHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IHZvbHVtZU9wYWNpdHk6IHZhbHVlIH0pO1xuICB9XG4gIHByb3RlY3RlZCBvblNob3dQb2ludHMob246IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBzaG93UG9pbnRzOiBvbiB9KTtcbiAgfVxuICAvKiogVGhlIFwib25lIHNlY3Rpb24gYXQgYSB0aW1lXCIgc3dpdGNoOiBudWxsIHJlc3RvcmVzIHRoZSB3aG9sZSBzdGFjay4gKi9cbiAgcHJvdGVjdGVkIG9uT25lU2VjdGlvbihvbjogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghb24pIHtcbiAgICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IHBvaW50U2VjdGlvbjogbnVsbCB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gT3BlbiBpbiB0aGUgbWlkZGxlIG9mIHRoZSBzdGFjayByYXRoZXIgdGhhbiBvbiB0aGUgZmlyc3Qgc2VjdGlvbiwgd2hpY2ggZm9yXG4gICAgLy8gYSBicmFpbiBpcyBhIG5lYXJseSBlbXB0eSBvbGZhY3RvcnktYnVsYiBzbGlkZS5cbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBwb2ludFNlY3Rpb246IG1pZGRsZVNlY3Rpb24odGhpcy5zZWN0aW9ucykgfSk7XG4gIH1cbiAgcHJvdGVjdGVkIG9uUG9pbnRTZWN0aW9uKHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IHBvaW50U2VjdGlvbjogdmFsdWUgfSk7XG4gIH1cbiAgLyoqIFRydWUgd2hpbGUgdGhlIGNsb3VkIGlzIHJlc3RyaWN0ZWQgdG8gYSBzaW5nbGUgc2VjdGlvbi4gKi9cbiAgcHJvdGVjdGVkIGdldCBvbmVTZWN0aW9uKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnZpZXcucG9pbnRTZWN0aW9uICE9IG51bGw7XG4gIH1cbiAgLyoqIEhpZ2hlc3Qgc2VjdGlvbiBpbmRleCB0aGUgc2xpZGVyIGNhbiByZWFjaC4gKi9cbiAgcHJvdGVjdGVkIGdldCBsYXN0U2VjdGlvbigpOiBudW1iZXIge1xuICAgIHJldHVybiBNYXRoLm1heCgwLCAodGhpcy5zZWN0aW9ucz8ubGVuZ3RoID8/IDEpIC0gMSk7XG4gIH1cbiAgLyoqIFwiMTIgb2YgNTNcIiDigJQgMS1iYXNlZCwgYmVjYXVzZSB0aGUgc2VjdGlvbnMgYXJlIHNsaWRlcywgbm90IGFycmF5IHNsb3RzLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IHNlY3Rpb25MYWJlbCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBzZWN0aW9uTGFiZWwodGhpcy5zZWN0aW9ucywgdGhpcy52aWV3LnBvaW50U2VjdGlvbik7XG4gIH1cbiAgLyoqIFdoZXRoZXIgdGhpcyBkYXRhc2V0IGhhcyBzZWN0aW9ucyB0byBwaWNrIGZyb20gYXQgYWxsLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGlzU2VjdGlvbmVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAodGhpcy5zZWN0aW9ucz8ubGVuZ3RoID8/IDApID4gMTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvbkRlbnNpdHlWb2x1bWUob246IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBkZW5zaXR5Vm9sdW1lOiBvbiB9KTtcbiAgfVxuICBwcm90ZWN0ZWQgb25EZW5zaXR5U21vb3RoaW5nKHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IGRlbnNpdHlTbW9vdGhpbmc6IHZhbHVlIH0pO1xuICB9XG5cbiAgLyoqIFdoYXQgdGhlIGRlbnNpdHkgdm9sdW1lcyBhcmUgYWN0dWFsbHkgc2hvd2luZywgc2FpZCBwbGFpbmx5IOKAlCBhbiBlc3RpbWF0ZSBpc1xuICAgKiAgb25seSBob25lc3QgaWYgdGhlIHJlYWRlciBrbm93cyBpdCBpcyBvbmUsIGFuZCB3aGljaCBjbHVzdGVycyBhcmUgaW4gdmlldy4gKi9cbiAgcHJvdGVjdGVkIGdldCBkZW5zaXR5Tm90ZSgpOiBzdHJpbmcge1xuICAgIGNvbnN0IGNhcHBlZCA9IGB0aGUgJHtTcGF0aWFsT2JzZXJ2YXRpb25zUGFuZWxDb21wb25lbnQuREVOU0lUWV9NQVhfQ0xVU1RFUlN9IGxhcmdlc3QgY2x1c3RlcnNgO1xuICAgIGNvbnN0IHdoYXQgPSB0aGlzLmxlZ2VuZCA/IGNhcHBlZCA6IHRoaXMuaGFzU2VsZWN0aW9uID8gJ3RoZSBzZWxlY3RlZCBjZWxscycgOiAnYWxsIGNlbGxzJztcbiAgICByZXR1cm4gKFxuICAgICAgYERlbnNpdHkgZXN0aW1hdGUgb3ZlciAke3doYXR9IOKAlCBzbW9vdGhlZCBiZXR3ZWVuIHRoZSBpbWFnZWQgc2VjdGlvbnMsIGAgK1xuICAgICAgJ25vdCBtZWFzdXJlZCBjZWxscy4gTG93ZXIgT3BhY2l0eSB0byByZWFkIHRoZSBmaWVsZHMgdW5kZXIgdGhlIGNsb3VkLidcbiAgICApO1xuICB9XG5cbiAgLyoqIE1pcnJvcnMgdGhlIHJlbmRlcmVyJ3MgY2FwLCBmb3IgdGhlIG5vdGUgb25seS4gKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgREVOU0lUWV9NQVhfQ0xVU1RFUlMgPSA2O1xuICBwcm90ZWN0ZWQgb25DbGlwKHZhbHVlOiBbbnVtYmVyLCBudW1iZXJdKTogdm9pZCB7XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgcGVyY2VudGlsZUNsaXA6IHZhbHVlIH0pO1xuICB9XG59XG4iLCI8bmctY29udGFpbmVyICpuZ0lmPVwiZGF0YXNldCBhcyBkc1wiPlxuICA8IS0tIE9ic2VydmF0aW9uczogb25lIG1hcmtlciBwZXIgb2JzZXJ2YXRpb24gKGNlbGwgY2VudHJvaWQsIFZpc2l1bSBzcG90KSwgYW5kIHdoYXRcbiAgICAgY29sb3VycyB0aGVtLiBUaGUgbGVhZCBzZWN0aW9uIGZvciBkYXRhc2V0cyB3aXRob3V0IGNlbGwgb3V0bGluZXMuIC0tPlxuICA8ZGl2IGNsYXNzPVwic2MtcGFuZWxcIiBbY2xhc3Mub3Blbl09XCJvcGVuXCI+XG4gICAgPGRpdiBjbGFzcz1cInNjLXBhbmVsLWhlYWRcIiAoY2xpY2spPVwib3BlbkNoYW5nZS5lbWl0KCFvcGVuKVwiPlxuICAgICAgPHAtY2hlY2tib3hcbiAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5zaG93UG9pbnRzXCJcbiAgICAgICAgKGNsaWNrKT1cIiRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiXG4gICAgICAgIChvbkNoYW5nZSk9XCJvblNob3dQb2ludHMoJGV2ZW50LmNoZWNrZWQpXCJcbiAgICAgID48L3AtY2hlY2tib3g+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLXBhbmVsLXRpdGxlXCI+T2JzZXJ2YXRpb25zPC9zcGFuPlxuICAgICAgPGkgY2xhc3M9XCJwaVwiIFtjbGFzcy5waS1jaGV2cm9uLWRvd25dPVwib3BlblwiIFtjbGFzcy5waS1jaGV2cm9uLXJpZ2h0XT1cIiFvcGVuXCI+PC9pPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJzYy1wYW5lbC1ib2R5XCIgKm5nSWY9XCJvcGVuXCI+XG4gICAgICA8c21hbGwgY2xhc3M9XCJzYy1oaW50XCIgKm5nSWY9XCJoYXNDZWxscyAmJiAhaXMzZFwiPlxuICAgICAgICBPbmUgY2lyY2xlIHBlciBjZWxsLiBXaXRoIGNlbGwgb3V0bGluZXMgb24sIGNpcmNsZXMgYXJlIG9ubHkgZHJhd24gd2hlcmUgY2VsbHMgYXJlIHRvbyBzbWFsbCBvbiBzY3JlZW4gdG9cbiAgICAgICAgb3V0bGluZS5cbiAgICAgIDwvc21hbGw+XG4gICAgICA8IS0tIENvbG91ciBzb3VyY2UuIENvbHVtbiBhbmQgZ2VuZSBhcmUgbXV0dWFsbHkgZXhjbHVzaXZlOiBwaWNraW5nIG9uZVxuICAgICBjbGVhcnMgdGhlIG90aGVyLCBzbyB3aGF0IGRyaXZlcyB0aGUgY29sb3VycyBpcyBuZXZlciBhbWJpZ3VvdXMuIC0tPlxuICAgICAgPGRpdiBjbGFzcz1cInNjLXNlY3Rpb25cIj5cbiAgICAgICAgPGxhYmVsIGNsYXNzPVwic2MtbGJsXCIgZm9yPVwic2MtY29sdW1uXCI+Q29sb3VyIGJ5IGNvbHVtbjwvbGFiZWw+XG4gICAgICAgIDxwLWRyb3Bkb3duXG4gICAgICAgICAgaW5wdXRJZD1cInNjLWNvbHVtblwiXG4gICAgICAgICAgW29wdGlvbnNdPVwiY29sdW1uT3B0aW9uc1wiXG4gICAgICAgICAgb3B0aW9uTGFiZWw9XCJsYWJlbFwiXG4gICAgICAgICAgb3B0aW9uVmFsdWU9XCJ2YWx1ZVwiXG4gICAgICAgICAgW25nTW9kZWxdPVwic2VsZWN0ZWRDb2x1bW5cIlxuICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cIm9uQ29sdW1uKCRldmVudClcIlxuICAgICAgICAgIHBsYWNlaG9sZGVyPVwiTm9uZSAoZmxhdCBjb2xvdXIpXCJcbiAgICAgICAgICBhcHBlbmRUbz1cImJvZHlcIlxuICAgICAgICAgIHN0eWxlQ2xhc3M9XCJzYy1mdWxsXCJcbiAgICAgICAgPjwvcC1kcm9wZG93bj5cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiZ2VuZXM/LnN0YXRlJCB8IGFzeW5jIGFzIGdlbmVcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cInNjLXNlY3Rpb25cIiAqbmdJZj1cImRzLmZlYXR1cmVzXCI+XG4gICAgICAgICAgPGxhYmVsIGNsYXNzPVwic2MtbGJsXCIgZm9yPVwic2MtZ2VuZVwiPkNvbG91ciBieSBnZW5lPC9sYWJlbD5cbiAgICAgICAgICA8IS0tIEEgZmlsdGVyYWJsZSBkcm9wZG93biwgbm90IGEgZnJlZS10ZXh0IGJveDogdGhlIGdlbmVzIGFyZSB2aXNpYmxlIGJlZm9yZVxuICAgICAgIGFueXRoaW5nIGlzIHR5cGVkLCBhbmQgZWFjaCBrZXlzdHJva2UgbmFycm93cyBhIGxpc3QgdGhlIHVzZXIgY2FuIHNlZS5cbiAgICAgICBWaXJ0dWFsLXNjcm9sbGVkIGJlY2F1c2UgYSB0YXJnZXRlZCBwYW5lbCBydW5zIHRvIGEgZmV3IHRob3VzYW5kIG5hbWVzLiAtLT5cbiAgICAgICAgICA8cC1kcm9wZG93blxuICAgICAgICAgICAgaW5wdXRJZD1cInNjLWdlbmVcIlxuICAgICAgICAgICAgW29wdGlvbnNdPVwiZ2VuZS5vcHRpb25zXCJcbiAgICAgICAgICAgIG9wdGlvbkxhYmVsPVwibGFiZWxcIlxuICAgICAgICAgICAgb3B0aW9uVmFsdWU9XCJ2YWx1ZVwiXG4gICAgICAgICAgICBbbmdNb2RlbF09XCJzZWxlY3RlZEdlbmVcIlxuICAgICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwib25HZW5lKCRldmVudClcIlxuICAgICAgICAgICAgW2ZpbHRlcl09XCJ0cnVlXCJcbiAgICAgICAgICAgIGZpbHRlckJ5PVwibGFiZWxcIlxuICAgICAgICAgICAgW3Jlc2V0RmlsdGVyT25IaWRlXT1cInRydWVcIlxuICAgICAgICAgICAgKG9uRmlsdGVyKT1cImdlbmVzPy5vbkZpbHRlcigkZXZlbnQuZmlsdGVyKVwiXG4gICAgICAgICAgICAob25TaG93KT1cImdlbmVzPy5lbnN1cmVMaXN0KClcIlxuICAgICAgICAgICAgW3Nob3dDbGVhcl09XCJ0cnVlXCJcbiAgICAgICAgICAgIFt2aXJ0dWFsU2Nyb2xsXT1cImdlbmUub3B0aW9ucy5sZW5ndGggPiBnZW5lVmlydHVhbFNjcm9sbEZyb21cIlxuICAgICAgICAgICAgW3ZpcnR1YWxTY3JvbGxJdGVtU2l6ZV09XCIzMFwiXG4gICAgICAgICAgICBbZW1wdHlGaWx0ZXJNZXNzYWdlXT1cImdlbmUucmVtb3RlID8gJ05vIG1hdGNoIOKAlCBrZWVwIHR5cGluZycgOiAnTm8gbWF0Y2gnXCJcbiAgICAgICAgICAgIFtlbXB0eU1lc3NhZ2VdPVwiZ2VuZS5yZW1vdGUgPyAnVHlwZSB0byBzZWFyY2ggZ2VuZXMnIDogJ05vIGdlbmVzJ1wiXG4gICAgICAgICAgICBhcHBlbmRUbz1cImJvZHlcIlxuICAgICAgICAgICAgcGxhY2Vob2xkZXI9XCJTZWFyY2gge3sgZHMuZmVhdHVyZXMuY291bnQgfCBudW1iZXIgfX0gZ2VuZXPigKZcIlxuICAgICAgICAgICAgc3R5bGVDbGFzcz1cInNjLWZ1bGxcIlxuICAgICAgICAgID48L3AtZHJvcGRvd24+XG4gICAgICAgICAgPHNtYWxsIGNsYXNzPVwic2MtaGludFwiICpuZ0lmPVwiZ2VuZS5mYWlsZWRcIj5HZW5lIGxvb2t1cCBmYWlsZWQuPC9zbWFsbD5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgPHNwYXRpYWwta2V5XG4gICAgICAgIFtjb250cm9sc109XCJjb250cm9sc1wiXG4gICAgICAgIFtkYXRhc2V0XT1cImRzXCJcbiAgICAgICAgW3ZpZXddPVwidmlld1wiXG4gICAgICAgIFtsZWdlbmRdPVwibGVnZW5kXCJcbiAgICAgICAgW2NvbG9yQmFyQ3NzXT1cImNvbG9yQmFyQ3NzXCJcbiAgICAgICAgW3NlbGVjdGVkQ2F0ZWdvcnldPVwic2VsZWN0ZWRDYXRlZ29yeVwiXG4gICAgICAgIFtjb2xvcm1hcE9wdGlvbnNdPVwiY29sb3JtYXBPcHRpb25zXCJcbiAgICAgICAgKGNhdGVnb3J5Q2xpY2tlZCk9XCJjYXRlZ29yeUNsaWNrZWQuZW1pdCgkZXZlbnQpXCJcbiAgICAgID48L3NwYXRpYWwta2V5PlxuXG4gICAgICA8IS0tIERpc3BsYXkgLS0+XG4gICAgICA8ZGl2IGNsYXNzPVwic2Mtc2VjdGlvblwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCI+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJzYy1sYmxcIj5Qb2ludCBzaXplPC9zcGFuPlxuICAgICAgICAgIDxwLXNsaWRlclxuICAgICAgICAgICAgW25nTW9kZWxdPVwidmlldy5wb2ludFNjYWxlXCJcbiAgICAgICAgICAgIFttaW5dPVwiMC4yNVwiXG4gICAgICAgICAgICBbbWF4XT1cIjVcIlxuICAgICAgICAgICAgW3N0ZXBdPVwiMC4yNVwiXG4gICAgICAgICAgICAob25DaGFuZ2UpPVwib25Qb2ludFNjYWxlKCRldmVudC52YWx1ZSlcIlxuICAgICAgICAgID48L3Atc2xpZGVyPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwic2MtdmFsXCI+e3sgdmlldy5wb2ludFNjYWxlIH19w5c8L3NwYW4+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCI+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJzYy1sYmxcIj5PcGFjaXR5PC9zcGFuPlxuICAgICAgICAgIDxwLXNsaWRlclxuICAgICAgICAgICAgW25nTW9kZWxdPVwidmlldy5vcGFjaXR5XCJcbiAgICAgICAgICAgIFttaW5dPVwiMC4xXCJcbiAgICAgICAgICAgIFttYXhdPVwiMVwiXG4gICAgICAgICAgICBbc3RlcF09XCIwLjA1XCJcbiAgICAgICAgICAgIChvbkNoYW5nZSk9XCJvbk9wYWNpdHkoJGV2ZW50LnZhbHVlKVwiXG4gICAgICAgICAgPjwvcC1zbGlkZXI+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJzYy12YWxcIj57eyB2aWV3Lm9wYWNpdHkgfX08L3NwYW4+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDwhLS0gT25seSBmb3IgYSBnZW5lOiB0aGUgbWFwIGRyYXdzIHRoYXQgZ2VuZSdzIGV4cHJlc3Npb24gYXMgYSBmaWVsZC4gSW4gMkRcbiAgICAgaXQgZ29lcyB1bmRlciB0aGUgY2VsbHMgb2YgdGhlIGRpc3BsYXllZCBwbGFuZTsgaW4gM0QgaXQgaXMgb25lIHNoZWV0IHBlclxuICAgICBpbWFnZWQgc2VjdGlvbiwgb3B0aW9uYWxseSBzbW9vdGhlZCBhbG9uZyB6IGludG8gYSB2b2x1bWUuIC0tPlxuICA8ZGl2IGNsYXNzPVwic2Mtc2VjdGlvblwiICpuZ0lmPVwiY2FuTWFwR2VuZVwiPlxuICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIj5cbiAgICAgIDxwLWNoZWNrYm94XG4gICAgICAgIFtiaW5hcnldPVwidHJ1ZVwiXG4gICAgICAgIFtuZ01vZGVsXT1cInZpZXcuZ2VuZU1hcFwiXG4gICAgICAgIChvbkNoYW5nZSk9XCJvbkdlbmVNYXAoJGV2ZW50LmNoZWNrZWQpXCJcbiAgICAgICAgbGFiZWw9XCJHZW5lIG1hcCAoZXhwcmVzc2lvbiBmaWVsZClcIlxuICAgICAgICBbcFRvb2x0aXBdPVwiXG4gICAgICAgICAgaXMzZFxuICAgICAgICAgICAgPyAnRHJhdyB0aGlzIGdlbmVcXCdzIGV4cHJlc3Npb24gYXMgb25lIGZpZWxkIHBlciBpbWFnZWQgc2VjdGlvbiDigJQgYW4gZXN0aW1hdGUgYmV0d2VlbiB0aGUgY2VsbHMsIGVtcHR5IHdoZXJlIG5vdGhpbmcgd2FzIG1lYXN1cmVkJ1xuICAgICAgICAgICAgOiAnRHJhdyB0aGlzIGdlbmVcXCdzIGV4cHJlc3Npb24gYXMgYSBzbW9vdGggZmllbGQgVU5ERVIgdGhlIGNlbGxzIOKAlCBhbiBlc3RpbWF0ZSBiZXR3ZWVuIHRoZW0sIHRyYW5zcGFyZW50IHdoZXJlIG5vdGhpbmcgd2FzIG1lYXN1cmVkJ1xuICAgICAgICBcIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIlxuICAgICAgPjwvcC1jaGVja2JveD5cbiAgICA8L2Rpdj5cbiAgICA8IS0tIDNEIG9ubHk6IGludGVycG9sYXRpbmcgYWxvbmcgeiB0dXJucyB0aGUgbWVhc3VyZWQgc2hlZXRzIGludG8gYW5cbiAgICAgICBlc3RpbWF0ZSwgc28gaXQgaXMgaXRzIG93biBkZWNpc2lvbiBhbmQgaXRzIG93biBsYWJlbC4gLS0+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiICpuZ0lmPVwidmlldy5nZW5lTWFwICYmIGlzM2RcIj5cbiAgICAgIDxwLWNoZWNrYm94XG4gICAgICAgIFtiaW5hcnldPVwidHJ1ZVwiXG4gICAgICAgIFtuZ01vZGVsXT1cInZpZXcuZ2VuZU1hcFZvbHVtZVwiXG4gICAgICAgIChvbkNoYW5nZSk9XCJvbkdlbmVNYXBWb2x1bWUoJGV2ZW50LmNoZWNrZWQpXCJcbiAgICAgICAgbGFiZWw9XCJWb2x1bWUgcmVuZGVyaW5nIChpbnRlcnBvbGF0ZSBhbG9uZyB6KVwiXG4gICAgICAgIHBUb29sdGlwPVwiU21vb3RoIHRoZSBwZXItc2VjdGlvbiBmaWVsZHMgaW50byBhIGNvbnRpbnVvdXMgdm9sdW1lIOKAlCBhbiBFU1RJTUFURSBiZXR3ZWVuIHRoZSBpbWFnZWQgc2VjdGlvbnMsIG5vdCBtZWFzdXJlZCB0aXNzdWVcIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIlxuICAgICAgPjwvcC1jaGVja2JveD5cbiAgICA8L2Rpdj5cbiAgICA8IS0tIE1lYW5pbmdsZXNzIHdoaWxlIGludGVycG9sYXRpbmc6IGEgdm9sdW1lIGJ1aWx0IGZyb20gb25lIHNlY3Rpb24gd291bGRcbiAgICAgICBzbWVhciB0aGF0IHNsaWRlIHRocm91Z2ggdGhlIHNwZWNpbWVuJ3Mgd2hvbGUgZGVwdGguIC0tPlxuICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIiAqbmdJZj1cInZpZXcuZ2VuZU1hcCAmJiBpczNkICYmIGlzU2VjdGlvbmVkICYmICF2aWV3LmdlbmVNYXBWb2x1bWVcIj5cbiAgICAgIDxwLWNoZWNrYm94XG4gICAgICAgIFtiaW5hcnldPVwidHJ1ZVwiXG4gICAgICAgIFtuZ01vZGVsXT1cImdlbmVNYXBPbmVTZWN0aW9uXCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uR2VuZU1hcE9uZVNlY3Rpb24oJGV2ZW50LmNoZWNrZWQpXCJcbiAgICAgICAgbGFiZWw9XCJPbmUgbWFwIHNlY3Rpb24gYXQgYSB0aW1lXCJcbiAgICAgICAgcFRvb2x0aXA9XCJEcmF3IGEgc2luZ2xlIGltYWdlZCBzZWN0aW9uJ3MgZmllbGQgaW5zdGVhZCBvZiB0aGUgd2hvbGUgc3RhY2sgb2Ygc2hlZXRzXCJcbiAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwidG9wXCJcbiAgICAgID48L3AtY2hlY2tib3g+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiICpuZ0lmPVwidmlldy5nZW5lTWFwICYmIGlzM2QgJiYgaXNTZWN0aW9uZWQgJiYgIXZpZXcuZ2VuZU1hcFZvbHVtZSAmJiBnZW5lTWFwT25lU2VjdGlvblwiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJzYy1sYmxcIj5NYXAgc2VjdGlvbjwvc3Bhbj5cbiAgICAgIDxwLXNsaWRlclxuICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LmdlbmVNYXBTZWN0aW9uXCJcbiAgICAgICAgW21pbl09XCIwXCJcbiAgICAgICAgW21heF09XCJsYXN0U2VjdGlvblwiXG4gICAgICAgIFtzdGVwXT1cIjFcIlxuICAgICAgICAob25DaGFuZ2UpPVwib25HZW5lTWFwU2VjdGlvbigkZXZlbnQudmFsdWUpXCJcbiAgICAgID48L3Atc2xpZGVyPlxuICAgICAgPHNwYW4gY2xhc3M9XCJzYy12YWxcIj57eyBnZW5lTWFwU2VjdGlvbkxhYmVsIH19PC9zcGFuPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIiAqbmdJZj1cInZpZXcuZ2VuZU1hcFwiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJzYy1sYmxcIj5TbW9vdGhpbmc8L3NwYW4+XG4gICAgICA8cC1zbGlkZXJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5nZW5lTWFwU21vb3RoaW5nXCJcbiAgICAgICAgW21pbl09XCIwLjVcIlxuICAgICAgICBbbWF4XT1cIjRcIlxuICAgICAgICBbc3RlcF09XCIwLjVcIlxuICAgICAgICAob25DaGFuZ2UpPVwib25HZW5lTWFwU21vb3RoaW5nKCRldmVudC52YWx1ZSlcIlxuICAgICAgPjwvcC1zbGlkZXI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLXZhbFwiPnt7IHZpZXcuZ2VuZU1hcFNtb290aGluZyB9fcOXPC9zcGFuPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIiAqbmdJZj1cInZpZXcuZ2VuZU1hcFwiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJzYy1sYmxcIj5NYXAgb3BhY2l0eTwvc3Bhbj5cbiAgICAgIDxwLXNsaWRlclxuICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LmdlbmVNYXBPcGFjaXR5XCJcbiAgICAgICAgW21pbl09XCIwLjFcIlxuICAgICAgICBbbWF4XT1cIjFcIlxuICAgICAgICBbc3RlcF09XCIwLjA1XCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uR2VuZU1hcE9wYWNpdHkoJGV2ZW50LnZhbHVlKVwiXG4gICAgICA+PC9wLXNsaWRlcj5cbiAgICAgIDxzcGFuIGNsYXNzPVwic2MtdmFsXCI+e3sgdmlldy5nZW5lTWFwT3BhY2l0eSB9fTwvc3Bhbj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwic2MtaGludFwiICpuZ0lmPVwidmlldy5nZW5lTWFwICYmICFpczNkXCI+XG4gICAgICBLZXJuZWwtd2VpZ2h0ZWQgbWVhbiBwZXIgY2VsbCwgbm90IGEgc3VtIOKAlCBzbyBhIGNyb3dkZWQgcmVnaW9uIGRvZXMgbm90IGdsb3cgZm9yIGJlaW5nIGNyb3dkZWQuIFRyYW5zcGFyZW50XG4gICAgICB3aGVyZSBubyBjZWxsIHdhcyBtZWFzdXJlZC4gVHVybiB0aGUgY2VsbHMnIG93biBPcGFjaXR5IGRvd24gdG8gcmVhZCB0aGUgZmllbGQgdW5kZXIgdGhlbS5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwic2MtaGludFwiICpuZ0lmPVwidmlldy5nZW5lTWFwICYmIGlzM2RcIj5cbiAgICAgIHt7IGdlbmVNYXBWb2x1bWVOb3RlIH19XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDwhLS0gM0Qgb25seTogdGhlIHZvbHVtZSwgdGhlIGNsb3VkIGFuZCB0aGUgZGVuc2l0eSB2b2x1bWVzIG9jY3VweSB0aGUgc2FtZVxuICAgICBzcGFjZSwgc28gZWFjaCBoaWRlcyB0aGUgb3RoZXJzLiBJbmRlcGVuZGVudCB0b2dnbGVzLCBiZWNhdXNlIHRoZSB1c2VmdWxcbiAgICAgdmlld3MgYXJlIHRoZSBjb21iaW5hdGlvbnMg4oCUIHRoZSBmaWVsZCBhbG9uZSwgb25lIHNlY3Rpb24ncyBjZWxscyBvdmVyIGl0LFxuICAgICB0aGUgYW5hdG9teSBhbG9uZS4gLS0+XG4gIDxkaXYgY2xhc3M9XCJzYy1zZWN0aW9uXCIgKm5nSWY9XCJpczNkXCI+XG4gICAgPHNwYW4gY2xhc3M9XCJzYy1sYmxcIj5TaG93PC9zcGFuPlxuICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIiAqbmdJZj1cImRzLnZvbHVtZVwiPlxuICAgICAgPHAtY2hlY2tib3hcbiAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5zaG93Vm9sdW1lXCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uU2hvd1ZvbHVtZSgkZXZlbnQuY2hlY2tlZClcIlxuICAgICAgICBsYWJlbD1cIlJlZmVyZW5jZSB2b2x1bWVcIlxuICAgICAgICBwVG9vbHRpcD1cIlRoZSByZWdpc3RlcmVkIGFuYXRvbXkgdGhlIG9ic2VydmF0aW9ucyBzaXQgaW5cIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIlxuICAgICAgPjwvcC1jaGVja2JveD5cbiAgICA8L2Rpdj5cbiAgICA8IS0tIFRoZSBiYWNrZHJvcCdzIG93biBvcGFjaXR5OiByZWFkaW5nIHRoZSBjbG91ZCBvciBhIGRlbnNpdHkgZmllbGRcbiAgICAgICB0aHJvdWdoIHRoZSBhbmF0b215IG1lYW5zIHR1cm5pbmcgdGhlIGFuYXRvbXkgZG93biwgbm90IHRoZSBkYXRhLiAtLT5cbiAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCIgKm5nSWY9XCJkcy52b2x1bWUgJiYgdmlldy5zaG93Vm9sdW1lXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLWxibFwiPlZvbHVtZSBvcGFjaXR5PC9zcGFuPlxuICAgICAgPHAtc2xpZGVyXG4gICAgICAgIFtuZ01vZGVsXT1cInZpZXcudm9sdW1lT3BhY2l0eVwiXG4gICAgICAgIFttaW5dPVwiMC4wNVwiXG4gICAgICAgIFttYXhdPVwiMVwiXG4gICAgICAgIFtzdGVwXT1cIjAuMDVcIlxuICAgICAgICAob25DaGFuZ2UpPVwib25Wb2x1bWVPcGFjaXR5KCRldmVudC52YWx1ZSlcIlxuICAgICAgPjwvcC1zbGlkZXI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLXZhbFwiPnt7IHZpZXcudm9sdW1lT3BhY2l0eSB9fTwvc3Bhbj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCI+XG4gICAgICA8cC1jaGVja2JveFxuICAgICAgICBbYmluYXJ5XT1cInRydWVcIlxuICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LnNob3dQb2ludHNcIlxuICAgICAgICAob25DaGFuZ2UpPVwib25TaG93UG9pbnRzKCRldmVudC5jaGVja2VkKVwiXG4gICAgICAgIGxhYmVsPVwiT2JzZXJ2YXRpb25zXCJcbiAgICAgICAgcFRvb2x0aXA9XCJUaGUgbWVhc3VyZWQgY2VsbHMuIEhpZGUgdGhlbSB0byByZWFkIHRoZSBkZW5zaXR5IHZvbHVtZXMgdW5kZXJuZWF0aFwiXG4gICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cInRvcFwiXG4gICAgICA+PC9wLWNoZWNrYm94PlxuICAgIDwvZGl2PlxuICAgIDwhLS0gT25seSBmb3IgZGF0YSB0aGF0IHJlYWxseSBpcyBzZWN0aW9uZWQ7IGEgY29udGludW91cyB6IGhhcyBubyBzZWN0aW9uc1xuICAgICAgIHRvIHN0ZXAgdGhyb3VnaC4gLS0+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiICpuZ0lmPVwidmlldy5zaG93UG9pbnRzICYmIGlzU2VjdGlvbmVkXCI+XG4gICAgICA8cC1jaGVja2JveFxuICAgICAgICBbYmluYXJ5XT1cInRydWVcIlxuICAgICAgICBbbmdNb2RlbF09XCJvbmVTZWN0aW9uXCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uT25lU2VjdGlvbigkZXZlbnQuY2hlY2tlZClcIlxuICAgICAgICBsYWJlbD1cIk9uZSBzZWN0aW9uIGF0IGEgdGltZVwiXG4gICAgICAgIHBUb29sdGlwPVwiRHJhdyBhIHNpbmdsZSBpbWFnZWQgc2VjdGlvbidzIGNlbGxzLCBzbyB0aGUgZGVuc2l0eSB2b2x1bWVzIGJldHdlZW4gdGhlIHNlY3Rpb25zIHN0YXkgdmlzaWJsZVwiXG4gICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cInRvcFwiXG4gICAgICA+PC9wLWNoZWNrYm94PlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIiAqbmdJZj1cInZpZXcuc2hvd1BvaW50cyAmJiBpc1NlY3Rpb25lZCAmJiBvbmVTZWN0aW9uXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLWxibFwiPlNlY3Rpb248L3NwYW4+XG4gICAgICA8cC1zbGlkZXJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5wb2ludFNlY3Rpb25cIlxuICAgICAgICBbbWluXT1cIjBcIlxuICAgICAgICBbbWF4XT1cImxhc3RTZWN0aW9uXCJcbiAgICAgICAgW3N0ZXBdPVwiMVwiXG4gICAgICAgIChvbkNoYW5nZSk9XCJvblBvaW50U2VjdGlvbigkZXZlbnQudmFsdWUpXCJcbiAgICAgID48L3Atc2xpZGVyPlxuICAgICAgPHNwYW4gY2xhc3M9XCJzYy12YWxcIj57eyBzZWN0aW9uTGFiZWwgfX08L3NwYW4+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDxkaXYgY2xhc3M9XCJzYy1zZWN0aW9uXCIgKm5nSWY9XCJpczNkXCI+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiPlxuICAgICAgPHAtY2hlY2tib3hcbiAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5kZW5zaXR5Vm9sdW1lXCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uRGVuc2l0eVZvbHVtZSgkZXZlbnQuY2hlY2tlZClcIlxuICAgICAgICBsYWJlbD1cIkNsdXN0ZXIgZGVuc2l0eSB2b2x1bWVzXCJcbiAgICAgICAgcFRvb2x0aXA9XCJSYXltYXJjaCBlYWNoIGNsdXN0ZXIgYXMgYSBzbW9vdGggZGVuc2l0eSBmaWVsZCDigJQgYW4gRVNUSU1BVEUgYmV0d2VlbiB0aGUgaW1hZ2VkIHNlY3Rpb25zLCBub3QgbWVhc3VyZWQgY2VsbHNcIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIlxuICAgICAgPjwvcC1jaGVja2JveD5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCIgKm5nSWY9XCJ2aWV3LmRlbnNpdHlWb2x1bWVcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwic2MtbGJsXCI+U21vb3RoaW5nPC9zcGFuPlxuICAgICAgPHAtc2xpZGVyXG4gICAgICAgIFtuZ01vZGVsXT1cInZpZXcuZGVuc2l0eVNtb290aGluZ1wiXG4gICAgICAgIFttaW5dPVwiMC41XCJcbiAgICAgICAgW21heF09XCI0XCJcbiAgICAgICAgW3N0ZXBdPVwiMC41XCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uRGVuc2l0eVNtb290aGluZygkZXZlbnQudmFsdWUpXCJcbiAgICAgID48L3Atc2xpZGVyPlxuICAgICAgPHNwYW4gY2xhc3M9XCJzYy12YWxcIj57eyB2aWV3LmRlbnNpdHlTbW9vdGhpbmcgfX3Dlzwvc3Bhbj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwic2MtaGludFwiICpuZ0lmPVwidmlldy5kZW5zaXR5Vm9sdW1lXCI+e3sgZGVuc2l0eU5vdGUgfX08L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPCEtLSBDb250aW51b3VzLW9ubHkga25vYnM6IG1lYW5pbmdsZXNzIGZvciBhIGNhdGVnb3JpY2FsIHBhbGV0dGUuIC0tPlxuICA8ZGl2IGNsYXNzPVwic2Mtc2VjdGlvblwiICpuZ0lmPVwiaXNDb250aW51b3VzXCI+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiPlxuICAgICAgPHAtY2hlY2tib3hcbiAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5sb2dTY2FsZVwiXG4gICAgICAgIChvbkNoYW5nZSk9XCJvbkxvZ1NjYWxlKCRldmVudC5jaGVja2VkKVwiXG4gICAgICAgIGxhYmVsPVwiTG9nIHNjYWxlXCJcbiAgICAgICAgcFRvb2x0aXA9XCJDb3VudHMgY29sbGFwc2UgYWdhaW5zdCB0aGVpciBvdXRsaWVycyBvbiBhIGxpbmVhciByYW1wXCJcbiAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwidG9wXCJcbiAgICAgID48L3AtY2hlY2tib3g+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJzYy1sYmxcIj5DbGlwIG91dGxpZXJzPC9zcGFuPlxuICAgICAgPHAtZHJvcGRvd25cbiAgICAgICAgW29wdGlvbnNdPVwiY2xpcE9wdGlvbnNcIlxuICAgICAgICBvcHRpb25MYWJlbD1cImxhYmVsXCJcbiAgICAgICAgb3B0aW9uVmFsdWU9XCJ2YWx1ZVwiXG4gICAgICAgIFtuZ01vZGVsXT1cInZpZXcucGVyY2VudGlsZUNsaXBcIlxuICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJvbkNsaXAoJGV2ZW50KVwiXG4gICAgICAgIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgIHBUb29sdGlwPVwiUGVyY2VudGlsZSB0aGUgY29udHJhc3Qgd2luZG93IGlzIHRha2VuIGF0XCJcbiAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwidG9wXCJcbiAgICAgID48L3AtZHJvcGRvd24+XG4gICAgICA8IS0tIFJlc2VydmVzIHRoZSByZWFkb3V0IGNvbHVtbiB0aGlzIHJvdyBoYXMgbm8gcmVhZG91dCBmb3IsIHNvIHRoZVxuICAgICAgICAgY29udHJvbCdzIHJpZ2h0IGVkZ2UgbGluZXMgdXAgd2l0aCBldmVyeSBzbGlkZXIncy4gLS0+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLXZhbFwiPjwvc3Bhbj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG48L25nLWNvbnRhaW5lcj5cbiJdfQ==
402
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC1vYnNlcnZhdGlvbnMtcGFuZWwuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9zcGF0aWFsLWNvbnRyb2xzL3NwYXRpYWwtb2JzZXJ2YXRpb25zLXBhbmVsL3NwYXRpYWwtb2JzZXJ2YXRpb25zLXBhbmVsLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvc3BhdGlhbC1jb250cm9scy9zcGF0aWFsLW9ic2VydmF0aW9ucy1wYW5lbC9zcGF0aWFsLW9ic2VydmF0aW9ucy1wYW5lbC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFDVCxZQUFZLEVBQ1osS0FBSyxFQUVMLE1BQU0sR0FFUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFjLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN0QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0MsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUMxRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUMxRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDOUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBSWhELE9BQU8sRUFFTCxvQkFBb0IsR0FHckIsTUFBTSwrQkFBK0IsQ0FBQztBQUN2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Qix5QkFBeUI7cUJBQzFCLG1CQUdnQix1QkFBdUIsQ0FBQyxNQUFNOzhCQUl0QyxRQUFRO3NCQUFoQixLQUFLO2dCQUVHLE9BQU87c0JBQWYsS0FBSztnQkFFRyxJQUFJO3NCQUFaLEtBQUs7Z0JBRUcsS0FBSztzQkFBYixLQUFLO2dCQUVHLE1BQU07c0JBQWQsS0FBSztnQkFFRyxXQUFXO3NCQUFuQixLQUFLO2dCQUVHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBRUcsZ0JBQWdCO3NCQUF4QixLQUFLO2dCQUVHLGVBQWU7c0JBQXZCLEtBQUs7Z0JBRUcsSUFBSTtzQkFBWixLQUFLO2dCQUVHLElBQUk7c0JBQVosS0FBSztnQkFFYSxVQUFVO3NCQUE1QixNQUFNO2dCQUVZLGVBQWU7c0JBQWpDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgQ29tcG9uZW50LFxuICBFdmVudEVtaXR0ZXIsXG4gIElucHV0LFxuICBPbkNoYW5nZXMsXG4gIE91dHB1dCxcbiAgU2ltcGxlQ2hhbmdlcyxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEZvcm1zTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgQ2hlY2tib3hNb2R1bGUgfSBmcm9tICdwcmltZW5nL2NoZWNrYm94JztcbmltcG9ydCB7IERyb3Bkb3duTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9kcm9wZG93bic7XG5pbXBvcnQgeyBNdWx0aVNlbGVjdE1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvbXVsdGlzZWxlY3QnO1xuaW1wb3J0IHsgT3ZlcmxheVBhbmVsTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9vdmVybGF5cGFuZWwnO1xuaW1wb3J0IHsgU2VsZWN0QnV0dG9uTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9zZWxlY3RidXR0b24nO1xuaW1wb3J0IHsgU2xpZGVyTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9zbGlkZXInO1xuaW1wb3J0IHsgVG9vbHRpcE1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvdG9vbHRpcCc7XG5cbmltcG9ydCB0eXBlIHsgSVNwYXRpYWxDb250cm9scyB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy92aXN1YWxpemVyLmNvbnRyYWN0JztcbmltcG9ydCB0eXBlIHsgU3BhdGlhbERhdGFzZXQgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvc3BhdGlhbC1kYXRhc2V0LmNvbnRyYWN0JztcbmltcG9ydCB7XG4gIENvbG9ybWFwTm9kZSxcbiAgREVGQVVMVF9TUEFUSUFMX1ZJRVcsXG4gIFNwYXRpYWxWaWV3U3RhdGUsXG4gIFRyYW5zY3JpcHRHbHlwaE5hbWUsXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9kaXNwbGF5LXR5cGVzJztcbmltcG9ydCB7IG9ic2VydmF0aW9uR2VuZUNvbG9yLCBvYnNlcnZhdGlvbkdlbmVHbHlwaCB9IGZyb20gJy4uLy4uL3NwYXRpYWwvc3BhdGlhbC1nZW5lLWZpbHRlcic7XG5pbXBvcnQgeyBTcGF0aWFsU2VsZWN0aW9uTWFzaywgZW1wdHlTZWxlY3Rpb24gfSBmcm9tICcuLi8uLi9zcGF0aWFsL3NwYXRpYWwtc2VsZWN0aW9uJztcbmltcG9ydCB7XG4gIENMSVBfT1BUSU9OUyxcbiAgUGFuZWxPcHRpb24sXG4gIGNvbHVtbk9wdGlvbnMsXG4gIGdseXBoUG9pbnRzLFxuICBtaWRkbGVTZWN0aW9uLFxuICBzZWN0aW9uTGFiZWwsXG59IGZyb20gJy4uLy4uL3NwYXRpYWwvc3BhdGlhbC1wYW5lbC1tb2RlbCc7XG5pbXBvcnQgeyBTcGF0aWFsR2VuZVN0eWxlQ29tcG9uZW50IH0gZnJvbSAnLi4vc3BhdGlhbC1nZW5lLXN0eWxlL3NwYXRpYWwtZ2VuZS1zdHlsZS5jb21wb25lbnQnO1xuaW1wb3J0IHR5cGUgeyBHZW5lUGlja2VyTW9kZWwgfSBmcm9tICcuLi9zcGF0aWFsLWdlbmUtcGlja2VyJztcbmltcG9ydCB0eXBlIHsgU3BhdGlhbExlZ2VuZEVudHJ5IH0gZnJvbSAnLi4vc3BhdGlhbC1rZXkvc3BhdGlhbC1rZXkubW9kZWwnO1xuaW1wb3J0IHsgU3BhdGlhbEtleUNvbXBvbmVudCB9IGZyb20gJy4uL3NwYXRpYWwta2V5L3NwYXRpYWwta2V5LmNvbXBvbmVudCc7XG5cbi8qKlxuICogVGhlIE9ic2VydmF0aW9ucyBzZWN0aW9uIOKAlCBvbmUgbWFya2VyIHBlciBvYnNlcnZhdGlvbiAoY2VsbCBjZW50cm9pZCwgVmlzaXVtIHNwb3QpIOKAlCBhbmRcbiAqIHdoYXQgZWxzZSBkcmF3cyB0aGVtOiB3aGF0IGNvbG91cnMgdGhlbSAoYSBjb2x1bW4gb3IgYSBnZW5lLCBtdXR1YWxseSBleGNsdXNpdmUsIHdpdGhcbiAqIHRoZSB7QGxpbmsgU3BhdGlhbEtleUNvbXBvbmVudCBrZXl9KSwgcG9pbnQgc2l6ZSBhbmQgb3BhY2l0eSwgdGhlIGdlbmUgbWFwLCB0aGUgM0Qgc2NlbmVcbiAqICh2b2x1bWUsIGNsb3VkLCBzZWN0aW9ucywgZGVuc2l0eSB2b2x1bWVzKSBhbmQgdGhlIGNvbnRpbnVvdXMtb25seSBrbm9icy5cbiAqXG4gKiBSZW5kZXJzIHRoZSBzZWN0aW9uIGFuZCB0aGUgc2VjdGlvbnMgYWZ0ZXIgaXQgYXMgc2libGluZ3MgaW4gdGhlIGRpYWxvZyBib2R5XG4gKiAoYGRpc3BsYXk6IGNvbnRlbnRzYCkuIFdyaXRlcyBvbmx5IHRocm91Z2ggdGhlIHBvcnQ6IGBzZXRWaWV3U3RhdGVgLCBhbmQgdGhlIGNvbG91clxuICogc291cmNlJ3Mgb3duIGBjb2xvckJ5Q29sdW1uYCAvIGBjb2xvckJ5RmVhdHVyZWAgLyBgY2xlYXJDb2xvckJ5YC5cbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnc3BhdGlhbC1vYnNlcnZhdGlvbnMtcGFuZWwnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIEZvcm1zTW9kdWxlLFxuICAgIENoZWNrYm94TW9kdWxlLFxuICAgIERyb3Bkb3duTW9kdWxlLFxuICAgIE11bHRpU2VsZWN0TW9kdWxlLFxuICAgIE92ZXJsYXlQYW5lbE1vZHVsZSxcbiAgICBTZWxlY3RCdXR0b25Nb2R1bGUsXG4gICAgU2xpZGVyTW9kdWxlLFxuICAgIFRvb2x0aXBNb2R1bGUsXG4gICAgU3BhdGlhbEtleUNvbXBvbmVudCxcbiAgICBTcGF0aWFsR2VuZVN0eWxlQ29tcG9uZW50LFxuICBdLFxuICB0ZW1wbGF0ZVVybDogJy4vc3BhdGlhbC1vYnNlcnZhdGlvbnMtcGFuZWwuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9zcGF0aWFsLW9ic2VydmF0aW9ucy1wYW5lbC5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgU3BhdGlhbE9ic2VydmF0aW9uc1BhbmVsQ29tcG9uZW50IGltcGxlbWVudHMgT25DaGFuZ2VzIHtcbiAgLyoqIFRoZSBzcGF0aWFsIGNvbnRyb2xzIHRoaXMgcGFuZWwgcmVhZHMgYW5kIHdyaXRlcyB0aHJvdWdoOyBudWxsIHdpdGhvdXQgYSBgU1BBVElBTF9EQVRBX1BPUlRgLiAqL1xuICBASW5wdXQoKSBjb250cm9sczogSVNwYXRpYWxDb250cm9scyB8IG51bGwgPSBudWxsO1xuICAvKiogVGhlIGRhdGFzZXQgb24gb2ZmZXIsIG9yIG51bGwuICovXG4gIEBJbnB1dCgpIGRhdGFzZXQ6IFNwYXRpYWxEYXRhc2V0IHwgbnVsbCA9IG51bGw7XG4gIC8qKiBUaGUgY3VycmVudCBzcGF0aWFsIHZpZXcgc3RhdGU6IHdoYXQgaXMgZHJhd24sIGFuZCBob3cuICovXG4gIEBJbnB1dCgpIHZpZXc6IFNwYXRpYWxWaWV3U3RhdGUgPSBERUZBVUxUX1NQQVRJQUxfVklFVztcbiAgLyoqIFRoZSBnZW5lIGxpc3QgXCJDb2xvdXIgYnkgZ2VuZVwiIHBpY2tzIGZyb20sIHNoYXJlZCB3aXRoIHRoZSBvdGhlciBnZW5lIGRyb3Bkb3ducy4gKi9cbiAgQElucHV0KCkgZ2VuZXM6IEdlbmVQaWNrZXJNb2RlbCB8IG51bGwgPSBudWxsO1xuICAvKiogVGhlIGtleSdzIGNhdGVnb3JpY2FsIGxlZ2VuZCwgb3IgbnVsbCDigJQgd2hpY2ggYWxzbyBkZWNpZGVzIHRoZSBjb250aW51b3VzIGtub2JzLiAqL1xuICBASW5wdXQoKSBsZWdlbmQ6IFNwYXRpYWxMZWdlbmRFbnRyeVtdIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBUaGUga2V5J3MgY29udGludW91cyBjb2xvdXIgYmFyIChhIENTUyBncmFkaWVudCksIG9yIG51bGwgd2hlbiBjYXRlZ29yaWNhbC4gKi9cbiAgQElucHV0KCkgY29sb3JCYXJDc3M6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAvKiogVGhlIGN1cnJlbnQgc2VsZWN0aW9uIChpdHMgY291bnQsIGFuZCB3aGF0IGlzIG11dGVkKS4gKi9cbiAgQElucHV0KCkgc2VsZWN0aW9uOiBTcGF0aWFsU2VsZWN0aW9uTWFzayA9IGVtcHR5U2VsZWN0aW9uKCk7XG4gIC8qKiBUaGUgbGVnZW5kIHJvdyB3aG9zZSBjYXRlZ29yeSBpcyBzZWxlY3RlZCwgZm9yIGhpZ2hsaWdodGluZy4gKi9cbiAgQElucHV0KCkgc2VsZWN0ZWRDYXRlZ29yeTogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBDb2xvcm1hcCB0cmVlIGZvciB0aGUgY29udGludW91cyBjb2xvdXIgc2NhbGUuICovXG4gIEBJbnB1dCgpIGNvbG9ybWFwT3B0aW9uczogQ29sb3JtYXBOb2RlW10gPSBbXTtcbiAgLyoqIFNldCB3aGlsZSB0aGUgM0QgY2xvdWQgaXMgdGhlIGFjdGl2ZSBtb2RlLiAqL1xuICBASW5wdXQoKSBpczNkID0gZmFsc2U7XG4gIC8qKiBXaGV0aGVyIHRoZSBPYnNlcnZhdGlvbnMgc2VjdGlvbiBpcyBleHBhbmRlZC4gKi9cbiAgQElucHV0KCkgb3BlbiA9IGZhbHNlO1xuICAvKiogQXNrcyB0byBvcGVuIG9yIGNsb3NlIGl0OyB0d28td2F5IHdpdGggYG9wZW5gLiAqL1xuICBAT3V0cHV0KCkgcmVhZG9ubHkgb3BlbkNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8Ym9vbGVhbj4oKTtcbiAgLyoqIEEgbGVnZW5kIHJvdyB3YXMgY2xpY2tlZCAoc2VlIHRoZSBrZXkpLiAqL1xuICBAT3V0cHV0KCkgcmVhZG9ubHkgY2F0ZWdvcnlDbGlja2VkID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNsaXBPcHRpb25zID0gQ0xJUF9PUFRJT05TO1xuICAvKiogSG93IHNldmVyYWwgZ2VuZXMgb2YgdGhlIG9ic2VydmF0aW9uIGZpbHRlciBjb21iaW5lLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgbWF0Y2hPcHRpb25zOiBQYW5lbE9wdGlvbjxTcGF0aWFsVmlld1N0YXRlWydvYnNlcnZhdGlvbkdlbmVzTWF0Y2gnXT5bXSA9IFtcbiAgICB7IGxhYmVsOiAnQW55IGdlbmUnLCB2YWx1ZTogJ2FueScgfSxcbiAgICB7IGxhYmVsOiAnQWxsIGdlbmVzJywgdmFsdWU6ICdhbGwnIH0sXG4gIF07XG4gIC8qKiBWaXJ0dWFsLXNjcm9sbGVkIG9ubHkgcGFzdCB0aGlzIG1hbnkgb3B0aW9ucyAoc2VlIHRoZSBjZWxscyBwYW5lbCdzIGdlbmUgZHJvcGRvd24pLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZ2VuZVZpcnR1YWxTY3JvbGxGcm9tID0gMjAwO1xuXG4gIC8qKiBDb2xvdXItYnkgY29sdW1uIGNob2ljZXMg4oCUIFwiTm9uZVwiIHBsdXMgZXZlcnkgY29sdW1uIHRoZSBkYXRhc2V0IGRlY2xhcmVzLiAqL1xuICBwcm90ZWN0ZWQgY29sdW1uT3B0aW9uczogUGFuZWxPcHRpb248c3RyaW5nIHwgbnVsbD5bXSA9IFtdO1xuICBwcm90ZWN0ZWQgc2VsZWN0ZWRDb2x1bW46IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICBwcm90ZWN0ZWQgc2VsZWN0ZWRHZW5lOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgLyoqXG4gICAqIFRoZSBkYXRhc2V0J3MgaW1hZ2VkIHNlY3Rpb24gcG9zaXRpb25zLCBvciBudWxsIHdoZW4gaXRzIHogaXMgY29udGludW91cyByYXRoZXIgdGhhblxuICAgKiBzZWN0aW9uZWQuIFJlYWQgb25jZSBwZXIgZGF0YXNldCDigJQgdGhlIHNjYW4gd2Fsa3MgdGhlIHogb2YgZXZlcnkgb2JzZXJ2YXRpb24sIHNvIGl0XG4gICAqIG11c3Qgbm90IHNpdCBpbiBhIHRlbXBsYXRlIGdldHRlci5cbiAgICovXG4gIHByb3RlY3RlZCBzZWN0aW9uczogRmxvYXQzMkFycmF5IHwgbnVsbCA9IG51bGw7XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIGlmIChjaGFuZ2VzWydjb250cm9scyddKSB7XG4gICAgICB0aGlzLmdlbmVGaWx0ZXJGYWlsZWQkID0gdGhpcy5jb250cm9scz8uZ2V0R2VuZUZpbHRlckZhaWxlZCQ/LigpID8/IG9mKGZhbHNlKTtcbiAgICB9XG4gICAgaWYgKGNoYW5nZXNbJ2RhdGFzZXQnXSB8fCBjaGFuZ2VzWydjb250cm9scyddKSB7XG4gICAgICB0aGlzLmNvbHVtbk9wdGlvbnMgPSBjb2x1bW5PcHRpb25zKHRoaXMuZGF0YXNldCk7XG4gICAgICAvLyBBIG5ldyBkYXRhc2V0IGFsbW9zdCBjZXJ0YWlubHkgaGFzIGRpZmZlcmVudCBjb2x1bW5zOyBkcm9wIHN0YWxlIFVJIHN0YXRlLlxuICAgICAgdGhpcy5zZWxlY3RlZEdlbmUgPSBudWxsO1xuICAgICAgdGhpcy5zZWN0aW9ucyA9IHRoaXMuY29udHJvbHM/LnNhbXBsZWRTZWN0aW9ucygpID8/IG51bGw7XG4gICAgfVxuICAgIGlmIChjaGFuZ2VzWyd2aWV3J10pIHtcbiAgICAgIHRoaXMuc2VsZWN0ZWRDb2x1bW4gPSB0aGlzLnZpZXcuY29sb3JCeT8ua2luZCA9PT0gJ2NvbHVtbicgPyB0aGlzLnZpZXcuY29sb3JCeS5uYW1lIDogbnVsbDtcbiAgICAgIHRoaXMuc2VsZWN0ZWRHZW5lID0gdGhpcy52aWV3LmNvbG9yQnk/LmtpbmQgPT09ICdmZWF0dXJlJyA/IHRoaXMudmlldy5jb2xvckJ5Lm5hbWUgOiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8vIOKUgOKUgCBjb2xvdXIgc291cmNlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBDb2x1bW4gZHJvcGRvd24uIENob29zaW5nIGEgY29sdW1uIHN1cGVyc2VkZXMgYW55IGdlbmUgc2VsZWN0aW9uLiAqL1xuICBwcm90ZWN0ZWQgb25Db2x1bW4obmFtZTogc3RyaW5nIHwgbnVsbCk6IHZvaWQge1xuICAgIHRoaXMuc2VsZWN0ZWRDb2x1bW4gPSBuYW1lO1xuICAgIHRoaXMuc2VsZWN0ZWRHZW5lID0gbnVsbDtcbiAgICBpZiAoIW5hbWUpIHtcbiAgICAgIHRoaXMuY29udHJvbHM/LmNsZWFyQ29sb3JCeSgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmNvbnRyb2xzPy5jb2xvckJ5Q29sdW1uKG5hbWUpO1xuICB9XG5cbiAgLyoqIEEgZ2VuZSB3YXMgcGlja2VkOyBpdCBzdXBlcnNlZGVzIGFueSBjb2x1bW4gc2VsZWN0aW9uLiAqL1xuICBwcm90ZWN0ZWQgb25HZW5lKG5hbWU6IHN0cmluZyB8IG51bGwpOiB2b2lkIHtcbiAgICB0aGlzLnNlbGVjdGVkR2VuZSA9IG5hbWU7XG4gICAgaWYgKCFuYW1lKSB7XG4gICAgICB0aGlzLmNvbnRyb2xzPy5jbGVhckNvbG9yQnkoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5zZWxlY3RlZENvbHVtbiA9IG51bGw7XG4gICAgdGhpcy5jb250cm9scz8uY29sb3JCeUZlYXR1cmUobmFtZSk7XG4gIH1cblxuICAvLyDilIDilIAgd2hpY2ggb2JzZXJ2YXRpb25zIGFyZSBkcmF3biDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKiogR2VuZXMgcGlja2VkIGZvciB0aGUgb2JzZXJ2YXRpb24gZmlsdGVyOyBub25lIGRyYXdzIGV2ZXJ5IG9ic2VydmF0aW9uLiAqL1xuICBwcm90ZWN0ZWQgb25PYnNlcnZhdGlvbkdlbmVzKGdlbmVzOiBzdHJpbmdbXSB8IG51bGwpOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBvYnNlcnZhdGlvbkdlbmVzOiBnZW5lcyA/PyBbXSB9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvbk9ic2VydmF0aW9uR2VuZXNNYXRjaChtYXRjaDogU3BhdGlhbFZpZXdTdGF0ZVsnb2JzZXJ2YXRpb25HZW5lc01hdGNoJ10gfCBudWxsKTogdm9pZCB7XG4gICAgaWYgKCFtYXRjaCkgcmV0dXJuO1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IG9ic2VydmF0aW9uR2VuZXNNYXRjaDogbWF0Y2ggfSk7XG4gIH1cblxuICAvKiogV2hldGhlciB0aGUgZ2VuZSBmaWx0ZXIgY291bGQgbm90IGJlIGFwcGxpZWQgKGFuIGV4cHJlc3Npb24gcmVhZCBmYWlsZWQpOyBvbmUgc3RyZWFtIHBlclxuICAgKiAgYGNvbnRyb2xzYCwgc28gdGhlIHRlbXBsYXRlJ3MgYXN5bmMgcGlwZSBrZWVwcyBpdHMgc3Vic2NyaXB0aW9uLiAqL1xuICBwcm90ZWN0ZWQgZ2VuZUZpbHRlckZhaWxlZCQ6IE9ic2VydmFibGU8Ym9vbGVhbj4gPSBvZihmYWxzZSk7XG5cbiAgLyoqIEFzayBmb3IgdGhlIHBpY2tlZCBnZW5lcycgZXhwcmVzc2lvbiBhZ2FpbiAoYSBmYWlsZWQgcmVhZCBpcyBub3QgY2FjaGVkKS4gKi9cbiAgcHJvdGVjdGVkIHJldHJ5R2VuZUZpbHRlcigpOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBvYnNlcnZhdGlvbkdlbmVzOiBbLi4udGhpcy52aWV3Lm9ic2VydmF0aW9uR2VuZXNdIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG9uT2JzZXJ2YXRpb25HZW5lc0NvbG9yQnkob246IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBvYnNlcnZhdGlvbkdlbmVzQ29sb3JCeTogb24gfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVtb3ZlT2JzZXJ2YXRpb25HZW5lKGdlbmU6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMub25PYnNlcnZhdGlvbkdlbmVzKHRoaXMudmlldy5vYnNlcnZhdGlvbkdlbmVzLmZpbHRlcigoZykgPT4gZyAhPT0gZ2VuZSkpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGlzT2JzZXJ2YXRpb25HZW5lKGdlbmU6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnZpZXcub2JzZXJ2YXRpb25HZW5lcy5pbmNsdWRlcyhnZW5lKTtcbiAgfVxuXG4gIHByb3RlY3RlZCB0cmFja0J5R2VuZShfaTogbnVtYmVyLCBnZW5lOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBnZW5lO1xuICB9XG5cbiAgLy8g4pSA4pSAIGEgcGlja2VkIGdlbmUncyBpY29uIGFuZCBjb2xvdXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIFRoZSBnZW5lIHRoZSBpY29uIGFuZCBjb2xvdXIgcGlja2VyIGlzIG9wZW4gZm9yLiAqL1xuICBwcm90ZWN0ZWQgc3R5bGVHZW5lOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuICBwcm90ZWN0ZWQgZ2VuZUNvbG9yT2YoZ2VuZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gb2JzZXJ2YXRpb25HZW5lQ29sb3IodGhpcy52aWV3LCBnZW5lKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBnZW5lR2x5cGhPZihnZW5lOiBzdHJpbmcpOiBUcmFuc2NyaXB0R2x5cGhOYW1lIHtcbiAgICByZXR1cm4gb2JzZXJ2YXRpb25HZW5lR2x5cGgodGhpcy52aWV3LCBnZW5lKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBnbHlwaFBvaW50c09mKGdlbmU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGdseXBoUG9pbnRzKHRoaXMuZ2VuZUdseXBoT2YoZ2VuZSkpO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIHBpY2tlciBvbiB0aGUgZ2VuZSdzIHN3YXRjaC4gSW5zaWRlIHRoZSBnZW5lcyBkcm9wZG93biB0aGUgY2xpY2sgbXVzdCBub3QgYWxzb1xuICAgKiAgcmVhY2ggdGhlIG9wdGlvbiwgd2hpY2ggd291bGQgdW5waWNrIHRoZSBnZW5lLiAqL1xuICBwcm90ZWN0ZWQgb3BlbkdlbmVTdHlsZShldmVudDogRXZlbnQsIGdlbmU6IHN0cmluZywgcGFuZWw6IHsgdG9nZ2xlKGU6IEV2ZW50KTogdm9pZDsgaGlkZSgpOiB2b2lkIH0pOiB2b2lkIHtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBpZiAodGhpcy5zdHlsZUdlbmUgPT09IGdlbmUpIHtcbiAgICAgIHBhbmVsLnRvZ2dsZShldmVudCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuc3R5bGVHZW5lID0gZ2VuZTtcbiAgICBwYW5lbC5oaWRlKCk7XG4gICAgLy8gUmUtb3BlbiBhbmNob3JlZCBvbiB0aGUgc3dhdGNoIHRoYXQgd2FzIGNsaWNrZWQsIG9uY2UgdGhlIHBhbmVsIGhhcyBjbG9zZWQuXG4gICAgc2V0VGltZW91dCgoKSA9PiBwYW5lbC50b2dnbGUoZXZlbnQpKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvbkdlbmVDb2xvcihnZW5lOiBzdHJpbmcsIGhleDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgb2JzZXJ2YXRpb25HZW5lQ29sb3JzOiB7IC4uLnRoaXMudmlldy5vYnNlcnZhdGlvbkdlbmVDb2xvcnMsIFtnZW5lXTogaGV4IH0gfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgb25HZW5lR2x5cGgoZ2VuZTogc3RyaW5nLCBnbHlwaDogVHJhbnNjcmlwdEdseXBoTmFtZSk6IHZvaWQge1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IG9ic2VydmF0aW9uR2VuZUdseXBoczogeyAuLi50aGlzLnZpZXcub2JzZXJ2YXRpb25HZW5lR2x5cGhzLCBbZ2VuZV06IGdseXBoIH0gfSk7XG4gIH1cblxuICAvKiogQmFjayB0byB0aGUgZ2VuZSdzIGRlZmF1bHQgY29sb3VyIChpdHMgcGxhY2UgaW4gdGhlIGxpc3QpIGFuZCBpY29uIChhIGNpcmNsZSkuICovXG4gIHByb3RlY3RlZCByZXNldEdlbmVTdHlsZShnZW5lOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBjb2xvcnMgPSB7IC4uLnRoaXMudmlldy5vYnNlcnZhdGlvbkdlbmVDb2xvcnMgfTtcbiAgICBjb25zdCBnbHlwaHMgPSB7IC4uLnRoaXMudmlldy5vYnNlcnZhdGlvbkdlbmVHbHlwaHMgfTtcbiAgICBkZWxldGUgY29sb3JzW2dlbmVdO1xuICAgIGRlbGV0ZSBnbHlwaHNbZ2VuZV07XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgb2JzZXJ2YXRpb25HZW5lQ29sb3JzOiBjb2xvcnMsIG9ic2VydmF0aW9uR2VuZUdseXBoczogZ2x5cGhzIH0pO1xuICB9XG5cbiAgLyoqIFdoYXQgdGhlIGZpbHRlciBoaWRlcywgaW4gd29yZHMuICovXG4gIHByb3RlY3RlZCBnZXQgZ2VuZUZpbHRlckhpbnQoKTogc3RyaW5nIHtcbiAgICBjb25zdCBnZW5lcyA9IHRoaXMudmlldy5vYnNlcnZhdGlvbkdlbmVzO1xuICAgIGNvbnN0IHdoaWNoID1cbiAgICAgIGdlbmVzLmxlbmd0aCA9PT0gMVxuICAgICAgICA/IGdlbmVzWzBdXG4gICAgICAgIDogdGhpcy52aWV3Lm9ic2VydmF0aW9uR2VuZXNNYXRjaCA9PT0gJ2FsbCdcbiAgICAgICAgICA/ICdhbGwgb2YgdGhlc2UgZ2VuZXMnXG4gICAgICAgICAgOiAnYW55IG9mIHRoZXNlIGdlbmVzJztcbiAgICByZXR1cm4gKFxuICAgICAgYE9ubHkgb2JzZXJ2YXRpb25zIGV4cHJlc3NpbmcgJHt3aGljaH0gYXJlIGRyYXduOyB0aGUgb3RoZXJzIGFyZSBoaWRkZW4sIHdpdGggdGhlaXIgY2VsbCBvdXRsaW5lcyBhbmQgdGhlIGAgK1xuICAgICAgJ3RyYW5zY3JpcHRzIGFzc2lnbmVkIHRvIHRoZW0uIFRoZSB0cmFuc2NyaXB0IGRlbnNpdHkgbWFwLCBhbmQgdHJhbnNjcmlwdHMgZ3JvdXBlZCBieSBhcmVhIHdoZW4gem9vbWVkICcgK1xuICAgICAgJ291dCwgYXJlIGhpZGRlbiB3aGlsZSB0aGlzIGlzIG9uOiB0aGV5IGNhcnJ5IG5vIGNlbGxzIHRvIGZpbHRlciBieS4nXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBDZWxsIG91dGxpbmVzIG9uIG9mZmVyLCB3aGljaCBjaGFuZ2VzIHdoYXQgdGhlIGNpcmNsZXMgbWVhbi4gKi9cbiAgcHJvdGVjdGVkIGdldCBoYXNDZWxscygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISEodGhpcy5kYXRhc2V0Py5wb2x5Z29uVGlsZXMgfHwgdGhpcy5kYXRhc2V0Py5wb2x5Z29ucyk7XG4gIH1cblxuICAvKiogVHJ1ZSB3aGlsZSBhIHNlbGVjdGlvbiBpcyBhY3RpdmUg4oCUIGV2ZXJ5dGhpbmcgZWxzZSByZW5kZXJzIG11dGVkLiAqL1xuICBnZXQgaGFzU2VsZWN0aW9uKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnNlbGVjdGlvbi5jb3VudCA+IDA7XG4gIH1cblxuICAvKiogV2hldGhlciBsb2cvY2xpcCBhcHBseSDigJQgdGhleSBhcmUgY29udGludW91cy1vbmx5IGtub2JzLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGlzQ29udGludW91cygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISF0aGlzLnZpZXcuY29sb3JCeSAmJiB0aGlzLmxlZ2VuZCA9PT0gbnVsbDtcbiAgfVxuXG4gIC8vIOKUgOKUgCBkaXNwbGF5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8vIFByaW1lTkcncyBzbGlkZXIgcmVwb3J0cyBgbnVtYmVyIHwgdW5kZWZpbmVkYDsgaWdub3JlIHRoZSBlbXB0eSBjYXNlIHJhdGhlclxuICAvLyB0aGFuIHdyaXRpbmcgYHVuZGVmaW5lZGAgaW50byB0aGUgc3RvcmUgYW5kIHJlbmRlcmluZyBOYU4tc2l6ZWQgbWFya2Vycy5cbiAgcHJvdGVjdGVkIG9uUG9pbnRTY2FsZSh2YWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkKTogdm9pZCB7XG4gICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHJldHVybjtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBwb2ludFNjYWxlOiB2YWx1ZSB9KTtcbiAgfVxuICBwcm90ZWN0ZWQgb25PcGFjaXR5KHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IG9wYWNpdHk6IHZhbHVlIH0pO1xuICB9XG4gIHByb3RlY3RlZCBvbkxvZ1NjYWxlKG9uOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgbG9nU2NhbGU6IG9uIH0pO1xuICB9XG4gIHByb3RlY3RlZCBvbkdlbmVNYXAob246IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBnZW5lTWFwOiBvbiB9KTtcbiAgfVxuICAvKiogM0Q6IHNtb290aCB0aGUgcGVyLXNlY3Rpb24gc2hlZXRzIGFsb25nIHogaW50byBhIGNvbnRpbnVvdXMgdm9sdW1lLiAqL1xuICBwcm90ZWN0ZWQgb25HZW5lTWFwVm9sdW1lKG9uOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgZ2VuZU1hcFZvbHVtZTogb24gfSk7XG4gIH1cbiAgLyoqIDNEOiByZXN0cmljdCB0aGUgc2hlZXRzIHRvIG9uZSBpbWFnZWQgc2VjdGlvbi4gKi9cbiAgcHJvdGVjdGVkIG9uR2VuZU1hcE9uZVNlY3Rpb24ob246IGJvb2xlYW4pOiB2b2lkIHtcbiAgICBpZiAoIW9uKSB7XG4gICAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBnZW5lTWFwU2VjdGlvbjogbnVsbCB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgZ2VuZU1hcFNlY3Rpb246IG1pZGRsZVNlY3Rpb24odGhpcy5zZWN0aW9ucykgfSk7XG4gIH1cbiAgcHJvdGVjdGVkIG9uR2VuZU1hcFNlY3Rpb24odmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgZ2VuZU1hcFNlY3Rpb246IHZhbHVlIH0pO1xuICB9XG4gIHByb3RlY3RlZCBnZXQgZ2VuZU1hcE9uZVNlY3Rpb24oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMudmlldy5nZW5lTWFwU2VjdGlvbiAhPSBudWxsO1xuICB9XG4gIC8qKiBcIjEyIG9mIDUzXCIgZm9yIHRoZSBnZW5lIG1hcCdzIG93biBzZWN0aW9uLCAxLWJhc2VkIGxpa2UgdGhlIGNsb3VkJ3MuICovXG4gIHByb3RlY3RlZCBnZXQgZ2VuZU1hcFNlY3Rpb25MYWJlbCgpOiBzdHJpbmcge1xuICAgIHJldHVybiBzZWN0aW9uTGFiZWwodGhpcy5zZWN0aW9ucywgdGhpcy52aWV3LmdlbmVNYXBTZWN0aW9uKTtcbiAgfVxuICBwcm90ZWN0ZWQgb25HZW5lTWFwU21vb3RoaW5nKHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IGdlbmVNYXBTbW9vdGhpbmc6IHZhbHVlIH0pO1xuICB9XG4gIHByb3RlY3RlZCBvbkdlbmVNYXBPcGFjaXR5KHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IGdlbmVNYXBPcGFjaXR5OiB2YWx1ZSB9KTtcbiAgfVxuICAvKipcbiAgICogV2hhdCB0aGUgM0QgZ2VuZSBtYXAgaXMgY3VycmVudGx5IHNob3dpbmcsIHNhaWQgcGxhaW5seSDigJQgdGhlIHNoZWV0cyBhcmUgYVxuICAgKiBtZWFzdXJlbWVudCBhbmQgdGhlIHZvbHVtZSBpcyBhbiBlc3RpbWF0ZSwgYW5kIHRoZSBwYW5lbCBoYXMgdG8gYmUgdGhlIHBsYWNlXG4gICAqIHRoYXQgc2F5cyB3aGljaCBvbmUgaXMgb24gc2NyZWVuLlxuICAgKi9cbiAgcHJvdGVjdGVkIGdldCBnZW5lTWFwVm9sdW1lTm90ZSgpOiBzdHJpbmcge1xuICAgIGNvbnN0IHRvdGFsID0gdGhpcy5zZWN0aW9ucz8ubGVuZ3RoID8/IDA7XG4gICAgaWYgKHRoaXMudmlldy5nZW5lTWFwVm9sdW1lKSB7XG4gICAgICByZXR1cm4gKFxuICAgICAgICAnSW50ZXJwb2xhdGVkIGFsb25nIHo6IHRoZSBwbGFuZXMgYmV0d2VlbiB0aGUgaW1hZ2VkIHNlY3Rpb25zIGNhcnJ5IGFuICcgK1xuICAgICAgICBcIkVTVElNQVRFLCBzbW9vdGhlZCBmcm9tIHRoZWlyIG5laWdoYm91cnMnIG1lYW4uIE5vdGhpbmcgaXMgZHJhd24gYmV5b25kIHRoZSBcIiArXG4gICAgICAgICdvdXRlcm1vc3Qgc2VjdGlvbi4nXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAodGhpcy5nZW5lTWFwT25lU2VjdGlvbikge1xuICAgICAgcmV0dXJuIChcbiAgICAgICAgXCJPbmUgaW1hZ2VkIHNlY3Rpb24ncyBmaWVsZCDigJQgbWVhc3VyZWQsIG5vdCBpbnRlcnBvbGF0ZWQuIEhpZGUgdGhlIFwiICtcbiAgICAgICAgJ29ic2VydmF0aW9ucyB0byByZWFkIGl0LCBvciBsZWF2ZSB0aGVtIG9uIHRvIGNoZWNrIHRoZSBmaWVsZCBhZ2FpbnN0IHRoZW0uJ1xuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIChcbiAgICAgIGBPbmUgZmllbGQgcGVyIGltYWdlZCBzZWN0aW9uJHt0b3RhbCA/IGAgKCR7dG90YWx9KWAgOiAnJ30sIGF0IGl0cyBvd24gZGVwdGgsIGAgK1xuICAgICAgJ3dpdGggdGhlIGdhcHMgYmV0d2VlbiBzZWN0aW9ucyBlbXB0eS4gS2VybmVsLXdlaWdodGVkIG1lYW4gcGVyIGNlbGwsIG5vdCBhIHN1bS4nXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBUcnVlIHdoaWxlIGEgZ2VuZSBpcyB0aGUgY29sb3VyIHNvdXJjZSDigJQgdGhlIG9ubHkgdGhpbmcgYSBnZW5lIG1hcCBjYW4gbWFwLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGNhbk1hcEdlbmUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMudmlldy5jb2xvckJ5Py5raW5kID09PSAnZmVhdHVyZSc7XG4gIH1cblxuICAvLyDilIDilIAgd2hhdCB0aGUgM0Qgc2NlbmUgZHJhd3Mg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vIFRoZSB2b2x1bWUsIHRoZSBjbG91ZCBhbmQgdGhlIGRlbnNpdHkgdm9sdW1lcyBzaGFyZSBvbmUgc3BhY2UsIHNvIGVhY2ggb25lXG4gIC8vIGhpZGVzIHRoZSBvdGhlcnMgdG8gc29tZSBkZWdyZWUuIEluZGVwZW5kZW50IHRvZ2dsZXMgYmVjYXVzZSB0aGUgdXNlZnVsIHZpZXdzXG4gIC8vIGFyZSB0aGUgY29tYmluYXRpb25zLCBub3QgYSBzaW5nbGUgXCIzRCBtb2RlXCIuXG5cbiAgcHJvdGVjdGVkIG9uU2hvd1ZvbHVtZShvbjogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IHNob3dWb2x1bWU6IG9uIH0pO1xuICB9XG4gIHByb3RlY3RlZCBvblZvbHVtZU9wYWNpdHkodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgdm9sdW1lT3BhY2l0eTogdmFsdWUgfSk7XG4gIH1cbiAgcHJvdGVjdGVkIG9uU2hvd1BvaW50cyhvbjogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IHNob3dQb2ludHM6IG9uIH0pO1xuICB9XG4gIC8qKiBUaGUgXCJvbmUgc2VjdGlvbiBhdCBhIHRpbWVcIiBzd2l0Y2g6IG51bGwgcmVzdG9yZXMgdGhlIHdob2xlIHN0YWNrLiAqL1xuICBwcm90ZWN0ZWQgb25PbmVTZWN0aW9uKG9uOiBib29sZWFuKTogdm9pZCB7XG4gICAgaWYgKCFvbikge1xuICAgICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgcG9pbnRTZWN0aW9uOiBudWxsIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBPcGVuIGluIHRoZSBtaWRkbGUgb2YgdGhlIHN0YWNrIHJhdGhlciB0aGFuIG9uIHRoZSBmaXJzdCBzZWN0aW9uLCB3aGljaCBmb3JcbiAgICAvLyBhIGJyYWluIGlzIGEgbmVhcmx5IGVtcHR5IG9sZmFjdG9yeS1idWxiIHNsaWRlLlxuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IHBvaW50U2VjdGlvbjogbWlkZGxlU2VjdGlvbih0aGlzLnNlY3Rpb25zKSB9KTtcbiAgfVxuICBwcm90ZWN0ZWQgb25Qb2ludFNlY3Rpb24odmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgcG9pbnRTZWN0aW9uOiB2YWx1ZSB9KTtcbiAgfVxuICAvKiogVHJ1ZSB3aGlsZSB0aGUgY2xvdWQgaXMgcmVzdHJpY3RlZCB0byBhIHNpbmdsZSBzZWN0aW9uLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IG9uZVNlY3Rpb24oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMudmlldy5wb2ludFNlY3Rpb24gIT0gbnVsbDtcbiAgfVxuICAvKiogSGlnaGVzdCBzZWN0aW9uIGluZGV4IHRoZSBzbGlkZXIgY2FuIHJlYWNoLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGxhc3RTZWN0aW9uKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIE1hdGgubWF4KDAsICh0aGlzLnNlY3Rpb25zPy5sZW5ndGggPz8gMSkgLSAxKTtcbiAgfVxuICAvKiogXCIxMiBvZiA1M1wiIOKAlCAxLWJhc2VkLCBiZWNhdXNlIHRoZSBzZWN0aW9ucyBhcmUgc2xpZGVzLCBub3QgYXJyYXkgc2xvdHMuICovXG4gIHByb3RlY3RlZCBnZXQgc2VjdGlvbkxhYmVsKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHNlY3Rpb25MYWJlbCh0aGlzLnNlY3Rpb25zLCB0aGlzLnZpZXcucG9pbnRTZWN0aW9uKTtcbiAgfVxuICAvKiogV2hldGhlciB0aGlzIGRhdGFzZXQgaGFzIHNlY3Rpb25zIHRvIHBpY2sgZnJvbSBhdCBhbGwuICovXG4gIHByb3RlY3RlZCBnZXQgaXNTZWN0aW9uZWQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICh0aGlzLnNlY3Rpb25zPy5sZW5ndGggPz8gMCkgPiAxO1xuICB9XG5cbiAgcHJvdGVjdGVkIG9uRGVuc2l0eVZvbHVtZShvbjogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuY29udHJvbHM/LnNldFZpZXdTdGF0ZSh7IGRlbnNpdHlWb2x1bWU6IG9uIH0pO1xuICB9XG4gIHByb3RlY3RlZCBvbkRlbnNpdHlTbW9vdGhpbmcodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgZGVuc2l0eVNtb290aGluZzogdmFsdWUgfSk7XG4gIH1cblxuICAvKiogV2hhdCB0aGUgZGVuc2l0eSB2b2x1bWVzIGFyZSBhY3R1YWxseSBzaG93aW5nLCBzYWlkIHBsYWlubHkg4oCUIGFuIGVzdGltYXRlIGlzXG4gICAqICBvbmx5IGhvbmVzdCBpZiB0aGUgcmVhZGVyIGtub3dzIGl0IGlzIG9uZSwgYW5kIHdoaWNoIGNsdXN0ZXJzIGFyZSBpbiB2aWV3LiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGRlbnNpdHlOb3RlKCk6IHN0cmluZyB7XG4gICAgY29uc3QgY2FwcGVkID0gYHRoZSAke1NwYXRpYWxPYnNlcnZhdGlvbnNQYW5lbENvbXBvbmVudC5ERU5TSVRZX01BWF9DTFVTVEVSU30gbGFyZ2VzdCBjbHVzdGVyc2A7XG4gICAgY29uc3Qgd2hhdCA9IHRoaXMubGVnZW5kID8gY2FwcGVkIDogdGhpcy5oYXNTZWxlY3Rpb24gPyAndGhlIHNlbGVjdGVkIGNlbGxzJyA6ICdhbGwgY2VsbHMnO1xuICAgIHJldHVybiAoXG4gICAgICBgRGVuc2l0eSBlc3RpbWF0ZSBvdmVyICR7d2hhdH0g4oCUIHNtb290aGVkIGJldHdlZW4gdGhlIGltYWdlZCBzZWN0aW9ucywgYCArXG4gICAgICAnbm90IG1lYXN1cmVkIGNlbGxzLiBMb3dlciBPcGFjaXR5IHRvIHJlYWQgdGhlIGZpZWxkcyB1bmRlciB0aGUgY2xvdWQuJ1xuICAgICk7XG4gIH1cblxuICAvKiogTWlycm9ycyB0aGUgcmVuZGVyZXIncyBjYXAsIGZvciB0aGUgbm90ZSBvbmx5LiAqL1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBERU5TSVRZX01BWF9DTFVTVEVSUyA9IDY7XG4gIHByb3RlY3RlZCBvbkNsaXAodmFsdWU6IFtudW1iZXIsIG51bWJlcl0pOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyBwZXJjZW50aWxlQ2xpcDogdmFsdWUgfSk7XG4gIH1cbn1cbiIsIjxuZy1jb250YWluZXIgKm5nSWY9XCJkYXRhc2V0IGFzIGRzXCI+XG4gIDwhLS0gT2JzZXJ2YXRpb25zOiBvbmUgbWFya2VyIHBlciBvYnNlcnZhdGlvbiAoY2VsbCBjZW50cm9pZCwgVmlzaXVtIHNwb3QpLCBhbmQgd2hhdFxuICAgICBjb2xvdXJzIHRoZW0uIFRoZSBsZWFkIHNlY3Rpb24gZm9yIGRhdGFzZXRzIHdpdGhvdXQgY2VsbCBvdXRsaW5lcy4gLS0+XG4gIDxkaXYgY2xhc3M9XCJzYy1wYW5lbFwiIFtjbGFzcy5vcGVuXT1cIm9wZW5cIj5cbiAgICA8ZGl2IGNsYXNzPVwic2MtcGFuZWwtaGVhZFwiIChjbGljayk9XCJvcGVuQ2hhbmdlLmVtaXQoIW9wZW4pXCI+XG4gICAgICA8cC1jaGVja2JveFxuICAgICAgICBbYmluYXJ5XT1cInRydWVcIlxuICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LnNob3dQb2ludHNcIlxuICAgICAgICAoY2xpY2spPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uU2hvd1BvaW50cygkZXZlbnQuY2hlY2tlZClcIlxuICAgICAgPjwvcC1jaGVja2JveD5cbiAgICAgIDxzcGFuIGNsYXNzPVwic2MtcGFuZWwtdGl0bGVcIj5PYnNlcnZhdGlvbnM8L3NwYW4+XG4gICAgICA8aSBjbGFzcz1cInBpXCIgW2NsYXNzLnBpLWNoZXZyb24tZG93bl09XCJvcGVuXCIgW2NsYXNzLnBpLWNoZXZyb24tcmlnaHRdPVwiIW9wZW5cIj48L2k+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInNjLXBhbmVsLWJvZHlcIiAqbmdJZj1cIm9wZW5cIj5cbiAgICAgIDxzbWFsbCBjbGFzcz1cInNjLWhpbnRcIiAqbmdJZj1cImhhc0NlbGxzICYmICFpczNkXCI+XG4gICAgICAgIE9uZSBjaXJjbGUgcGVyIGNlbGwuIFdpdGggY2VsbCBvdXRsaW5lcyBvbiwgY2lyY2xlcyBhcmUgb25seSBkcmF3biB3aGVyZSBjZWxscyBhcmUgdG9vIHNtYWxsIG9uIHNjcmVlbiB0b1xuICAgICAgICBvdXRsaW5lLlxuICAgICAgPC9zbWFsbD5cbiAgICAgIDwhLS0gQ29sb3VyIHNvdXJjZS4gQ29sdW1uIGFuZCBnZW5lIGFyZSBtdXR1YWxseSBleGNsdXNpdmU6IHBpY2tpbmcgb25lXG4gICAgIGNsZWFycyB0aGUgb3RoZXIsIHNvIHdoYXQgZHJpdmVzIHRoZSBjb2xvdXJzIGlzIG5ldmVyIGFtYmlndW91cy4gLS0+XG4gICAgICA8ZGl2IGNsYXNzPVwic2Mtc2VjdGlvblwiPlxuICAgICAgICA8bGFiZWwgY2xhc3M9XCJzYy1sYmxcIiBmb3I9XCJzYy1jb2x1bW5cIj5Db2xvdXIgYnkgY29sdW1uPC9sYWJlbD5cbiAgICAgICAgPHAtZHJvcGRvd25cbiAgICAgICAgICBpbnB1dElkPVwic2MtY29sdW1uXCJcbiAgICAgICAgICBbb3B0aW9uc109XCJjb2x1bW5PcHRpb25zXCJcbiAgICAgICAgICBvcHRpb25MYWJlbD1cImxhYmVsXCJcbiAgICAgICAgICBvcHRpb25WYWx1ZT1cInZhbHVlXCJcbiAgICAgICAgICBbbmdNb2RlbF09XCJzZWxlY3RlZENvbHVtblwiXG4gICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwib25Db2x1bW4oJGV2ZW50KVwiXG4gICAgICAgICAgcGxhY2Vob2xkZXI9XCJOb25lIChmbGF0IGNvbG91cilcIlxuICAgICAgICAgIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgc3R5bGVDbGFzcz1cInNjLWZ1bGxcIlxuICAgICAgICA+PC9wLWRyb3Bkb3duPlxuICAgICAgPC9kaXY+XG5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJnZW5lcz8uc3RhdGUkIHwgYXN5bmMgYXMgZ2VuZVwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwic2Mtc2VjdGlvblwiICpuZ0lmPVwiZHMuZmVhdHVyZXNcIj5cbiAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJzYy1sYmxcIiBmb3I9XCJzYy1nZW5lXCI+Q29sb3VyIGJ5IGdlbmU8L2xhYmVsPlxuICAgICAgICAgIDwhLS0gQSBmaWx0ZXJhYmxlIGRyb3Bkb3duLCBub3QgYSBmcmVlLXRleHQgYm94OiB0aGUgZ2VuZXMgYXJlIHZpc2libGUgYmVmb3JlXG4gICAgICAgYW55dGhpbmcgaXMgdHlwZWQsIGFuZCBlYWNoIGtleXN0cm9rZSBuYXJyb3dzIGEgbGlzdCB0aGUgdXNlciBjYW4gc2VlLlxuICAgICAgIFZpcnR1YWwtc2Nyb2xsZWQgYmVjYXVzZSBhIHRhcmdldGVkIHBhbmVsIHJ1bnMgdG8gYSBmZXcgdGhvdXNhbmQgbmFtZXMuIC0tPlxuICAgICAgICAgIDxwLWRyb3Bkb3duXG4gICAgICAgICAgICBpbnB1dElkPVwic2MtZ2VuZVwiXG4gICAgICAgICAgICBbb3B0aW9uc109XCJnZW5lLm9wdGlvbnNcIlxuICAgICAgICAgICAgb3B0aW9uTGFiZWw9XCJsYWJlbFwiXG4gICAgICAgICAgICBvcHRpb25WYWx1ZT1cInZhbHVlXCJcbiAgICAgICAgICAgIFtuZ01vZGVsXT1cInNlbGVjdGVkR2VuZVwiXG4gICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJvbkdlbmUoJGV2ZW50KVwiXG4gICAgICAgICAgICBbZmlsdGVyXT1cInRydWVcIlxuICAgICAgICAgICAgZmlsdGVyQnk9XCJsYWJlbFwiXG4gICAgICAgICAgICBbcmVzZXRGaWx0ZXJPbkhpZGVdPVwidHJ1ZVwiXG4gICAgICAgICAgICAob25GaWx0ZXIpPVwiZ2VuZXM/Lm9uRmlsdGVyKCRldmVudC5maWx0ZXIpXCJcbiAgICAgICAgICAgIChvblNob3cpPVwiZ2VuZXM/LmVuc3VyZUxpc3QoKVwiXG4gICAgICAgICAgICBbc2hvd0NsZWFyXT1cInRydWVcIlxuICAgICAgICAgICAgW3ZpcnR1YWxTY3JvbGxdPVwiZ2VuZS5vcHRpb25zLmxlbmd0aCA+IGdlbmVWaXJ0dWFsU2Nyb2xsRnJvbVwiXG4gICAgICAgICAgICBbdmlydHVhbFNjcm9sbEl0ZW1TaXplXT1cIjMwXCJcbiAgICAgICAgICAgIFtlbXB0eUZpbHRlck1lc3NhZ2VdPVwiZ2VuZS5yZW1vdGUgPyAnTm8gbWF0Y2gg4oCUIGtlZXAgdHlwaW5nJyA6ICdObyBtYXRjaCdcIlxuICAgICAgICAgICAgW2VtcHR5TWVzc2FnZV09XCJnZW5lLnJlbW90ZSA/ICdUeXBlIHRvIHNlYXJjaCBnZW5lcycgOiAnTm8gZ2VuZXMnXCJcbiAgICAgICAgICAgIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgICBwbGFjZWhvbGRlcj1cIlNlYXJjaCB7eyBkcy5mZWF0dXJlcy5jb3VudCB8IG51bWJlciB9fSBnZW5lc+KAplwiXG4gICAgICAgICAgICBzdHlsZUNsYXNzPVwic2MtZnVsbFwiXG4gICAgICAgICAgPjwvcC1kcm9wZG93bj5cbiAgICAgICAgICA8c21hbGwgY2xhc3M9XCJzYy1oaW50XCIgKm5nSWY9XCJnZW5lLmZhaWxlZFwiPkdlbmUgbG9va3VwIGZhaWxlZC48L3NtYWxsPlxuICAgICAgICA8L2Rpdj5cblxuICAgICAgICA8IS0tIFdoaWNoIG9ic2VydmF0aW9ucyBhcmUgZHJhd24gYXQgYWxsLCBieSB3aGF0IHRoZXkgZXhwcmVzcy4gU2V2ZXJhbCBnZW5lcyBhdFxuICAgICAgIG9uY2UsIHVubGlrZSB0aGUgY29sb3VyLCB3aGljaCByZWFkcyBvbmU6IHBpY2tpbmcgZ2VuZXMgaGVyZSBoaWRlcyBldmVyeVxuICAgICAgIG9ic2VydmF0aW9uIHRoYXQgZXhwcmVzc2VzIG5vbmUgb2YgdGhlbSAob3Igbm90IGFsbCBvZiB0aGVtKSwgYW5kIGJ5IGRlZmF1bHRcbiAgICAgICBjb2xvdXJzIChhbmQsIGluIDJELCBtYXJrcykgZWFjaCBkcmF3biBvbmUgYnkgaXRzIGdlbmUuIC0tPlxuICAgICAgICA8ZGl2IGNsYXNzPVwic2Mtc2VjdGlvblwiICpuZ0lmPVwiZHMuZmVhdHVyZXNcIj5cbiAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJzYy1sYmxcIiBmb3I9XCJzYy1zaG93LWdlbmVzXCI+U2hvdyBvYnNlcnZhdGlvbnMgZXhwcmVzc2luZzwvbGFiZWw+XG4gICAgICAgICAgPHAtbXVsdGlTZWxlY3RcbiAgICAgICAgICAgIGlucHV0SWQ9XCJzYy1zaG93LWdlbmVzXCJcbiAgICAgICAgICAgIFtvcHRpb25zXT1cImdlbmUub3B0aW9uc1wiXG4gICAgICAgICAgICBvcHRpb25MYWJlbD1cImxhYmVsXCJcbiAgICAgICAgICAgIG9wdGlvblZhbHVlPVwidmFsdWVcIlxuICAgICAgICAgICAgW25nTW9kZWxdPVwidmlldy5vYnNlcnZhdGlvbkdlbmVzXCJcbiAgICAgICAgICAgIChvbkNoYW5nZSk9XCJvbk9ic2VydmF0aW9uR2VuZXMoJGV2ZW50LnZhbHVlKVwiXG4gICAgICAgICAgICBbZmlsdGVyXT1cInRydWVcIlxuICAgICAgICAgICAgZmlsdGVyQnk9XCJsYWJlbFwiXG4gICAgICAgICAgICBbcmVzZXRGaWx0ZXJPbkhpZGVdPVwidHJ1ZVwiXG4gICAgICAgICAgICAob25GaWx0ZXIpPVwiZ2VuZXM/Lm9uRmlsdGVyKCRldmVudC5maWx0ZXIpXCJcbiAgICAgICAgICAgIChvblBhbmVsU2hvdyk9XCJnZW5lcz8uZW5zdXJlTGlzdCgpXCJcbiAgICAgICAgICAgIFt2aXJ0dWFsU2Nyb2xsXT1cImdlbmUub3B0aW9ucy5sZW5ndGggPiBnZW5lVmlydHVhbFNjcm9sbEZyb21cIlxuICAgICAgICAgICAgW3ZpcnR1YWxTY3JvbGxJdGVtU2l6ZV09XCIzMFwiXG4gICAgICAgICAgICBbc2hvd1RvZ2dsZUFsbF09XCJmYWxzZVwiXG4gICAgICAgICAgICBbc2hvd0NsZWFyXT1cInRydWVcIlxuICAgICAgICAgICAgW2VtcHR5RmlsdGVyTWVzc2FnZV09XCJnZW5lLnJlbW90ZSA/ICdObyBtYXRjaCDigJQga2VlcCB0eXBpbmcnIDogJ05vIG1hdGNoJ1wiXG4gICAgICAgICAgICBbZW1wdHlNZXNzYWdlXT1cImdlbmUucmVtb3RlID8gJ1R5cGUgdG8gc2VhcmNoIGdlbmVzJyA6ICdObyBnZW5lcydcIlxuICAgICAgICAgICAgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiQW55IGdlbmUgKGV2ZXJ5IG9ic2VydmF0aW9uKVwiXG4gICAgICAgICAgICBbc2VsZWN0ZWRJdGVtc0xhYmVsXT1cIid7MH0gZ2VuZXMnXCJcbiAgICAgICAgICAgIFttYXhTZWxlY3RlZExhYmVsc109XCIzXCJcbiAgICAgICAgICAgIHN0eWxlQ2xhc3M9XCJzYy1mdWxsXCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICA8IS0tIEEgcGlja2VkIGdlbmUgc2hvd3MgaXRzIGljb24gaW4gaXRzIGNvbG91cjsgY2xpY2tpbmcgaXQgZWRpdHMgdGhlbS4gLS0+XG4gICAgICAgICAgICA8bmctdGVtcGxhdGUgbGV0LW9wdGlvbiBwVGVtcGxhdGU9XCJpdGVtXCI+XG4gICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJzYy1vYnMtZ2VuZS1vcHRpb25cIj5cbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInNjLW9icy1nZW5lLW5hbWVcIj57eyBvcHRpb24ubGFiZWwgfX08L3NwYW4+XG4gICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImlzT2JzZXJ2YXRpb25HZW5lKG9wdGlvbi52YWx1ZSlcIj5cbiAgICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nVGVtcGxhdGVPdXRsZXQ9XCJnZW5lTWFyazsgY29udGV4dDogeyAkaW1wbGljaXQ6IG9wdGlvbi52YWx1ZSB9XCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgICA8L3AtbXVsdGlTZWxlY3Q+XG4gICAgICAgICAgPHAtc2VsZWN0QnV0dG9uXG4gICAgICAgICAgICAqbmdJZj1cInZpZXcub2JzZXJ2YXRpb25HZW5lcy5sZW5ndGggPiAxXCJcbiAgICAgICAgICAgIFtvcHRpb25zXT1cIm1hdGNoT3B0aW9uc1wiXG4gICAgICAgICAgICBvcHRpb25MYWJlbD1cImxhYmVsXCJcbiAgICAgICAgICAgIG9wdGlvblZhbHVlPVwidmFsdWVcIlxuICAgICAgICAgICAgW25nTW9kZWxdPVwidmlldy5vYnNlcnZhdGlvbkdlbmVzTWF0Y2hcIlxuICAgICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwib25PYnNlcnZhdGlvbkdlbmVzTWF0Y2goJGV2ZW50KVwiXG4gICAgICAgICAgICBbYWxsb3dFbXB0eV09XCJmYWxzZVwiXG4gICAgICAgICAgICBzdHlsZUNsYXNzPVwic2MtYnV0dG9uc1wiXG4gICAgICAgICAgPjwvcC1zZWxlY3RCdXR0b24+XG4gICAgICAgICAgPCEtLSBUaGUgcGlja2VkIGdlbmVzOiBlYWNoIG9uZSdzIGljb24gYW5kIGNvbG91ciAoY2xpY2sgdG8gZWRpdCksIGFuZCBhIHdheSBvdXQuIC0tPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJzYy1vYnMtZ2VuZXNcIiAqbmdJZj1cInZpZXcub2JzZXJ2YXRpb25HZW5lcy5sZW5ndGhcIj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJzYy1vYnMtZ2VuZS1yb3dcIiAqbmdGb3I9XCJsZXQgZyBvZiB2aWV3Lm9ic2VydmF0aW9uR2VuZXM7IHRyYWNrQnk6IHRyYWNrQnlHZW5lXCI+XG4gICAgICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nVGVtcGxhdGVPdXRsZXQ9XCJnZW5lTWFyazsgY29udGV4dDogeyAkaW1wbGljaXQ6IGcgfVwiPjwvbmctY29udGFpbmVyPlxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInNjLW9icy1nZW5lLW5hbWVcIj57eyBnIH19PC9zcGFuPlxuICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJzYy1pY29uLWJ0blwiXG4gICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCInU3RvcCBzaG93aW5nICcgKyBnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlJlbW92ZVwiXG4gICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwidG9wXCJcbiAgICAgICAgICAgICAgICAoY2xpY2spPVwicmVtb3ZlT2JzZXJ2YXRpb25HZW5lKGcpXCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxpIGNsYXNzPVwicGkgcGktdGltZXNcIj48L2k+XG4gICAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiICpuZ0lmPVwidmlldy5vYnNlcnZhdGlvbkdlbmVzLmxlbmd0aFwiPlxuICAgICAgICAgICAgPHAtY2hlY2tib3hcbiAgICAgICAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgW25nTW9kZWxdPVwidmlldy5vYnNlcnZhdGlvbkdlbmVzQ29sb3JCeVwiXG4gICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJvbk9ic2VydmF0aW9uR2VuZXNDb2xvckJ5KCRldmVudC5jaGVja2VkKVwiXG4gICAgICAgICAgICAgIGxhYmVsPVwiQ29sb3VyIGJ5IHRoZXNlIGdlbmVzXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJFYWNoIG9ic2VydmF0aW9uIGluIHRoZSBjb2xvdXIgKGFuZCwgaW4gMkQsIHRoZSBpY29uKSBvZiB0aGUgcGlja2VkIGdlbmUgaXQgZXhwcmVzc2VzIG1vc3QsIHJlbGF0aXZlIHRvIHRoYXQgZ2VuZSdzIGhpZ2hlc3QgdmFsdWUuIE9mZiwgQ29sb3VyIGJ5IGNvbHVtbiBvciBnZW5lIGFwcGxpZXMuXCJcbiAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwidG9wXCJcbiAgICAgICAgICAgID48L3AtY2hlY2tib3g+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPHNtYWxsIGNsYXNzPVwic2MtaGludFwiICpuZ0lmPVwidmlldy5vYnNlcnZhdGlvbkdlbmVzLmxlbmd0aFwiPnt7IGdlbmVGaWx0ZXJIaW50IH19PC9zbWFsbD5cbiAgICAgICAgICA8IS0tIFRoZSBmaWx0ZXIgaXMgbmV2ZXIgZHJvcHBlZCBzaWxlbnRseTogYSBmYWlsZWQgZXhwcmVzc2lvbiByZWFkIGtlZXBzIHRoZSBvYnNlcnZhdGlvbnNcbiAgICAgICAgICAgICAgIGFzIHRoZXkgd2VyZSBkcmF3biwgYW5kIHNheXMgc28uIC0tPlxuICAgICAgICAgIDxzbWFsbCBjbGFzcz1cInNjLWhpbnQgc2MtZXJyb3JcIiAqbmdJZj1cInZpZXcub2JzZXJ2YXRpb25HZW5lcy5sZW5ndGggJiYgKGdlbmVGaWx0ZXJGYWlsZWQkIHwgYXN5bmMpXCI+XG4gICAgICAgICAgICBUaGUgZXhwcmVzc2lvbiBvZiB0aGUgcGlja2VkIGdlbmVzIGNvdWxkIG5vdCBiZSByZWFkLCBzbyB0aGUgb2JzZXJ2YXRpb25zIHN0aWxsIHNob3cgdGhlIHByZXZpb3VzXG4gICAgICAgICAgICBmaWx0ZXIuXG4gICAgICAgICAgICA8YSBocmVmPVwiXCIgKGNsaWNrKT1cIiRldmVudC5wcmV2ZW50RGVmYXVsdCgpOyByZXRyeUdlbmVGaWx0ZXIoKVwiPlRyeSBhZ2FpbjwvYT5cbiAgICAgICAgICA8L3NtYWxsPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICA8c3BhdGlhbC1rZXlcbiAgICAgICAgW2NvbnRyb2xzXT1cImNvbnRyb2xzXCJcbiAgICAgICAgW2RhdGFzZXRdPVwiZHNcIlxuICAgICAgICBbdmlld109XCJ2aWV3XCJcbiAgICAgICAgW2xlZ2VuZF09XCJsZWdlbmRcIlxuICAgICAgICBbY29sb3JCYXJDc3NdPVwiY29sb3JCYXJDc3NcIlxuICAgICAgICBbc2VsZWN0ZWRDYXRlZ29yeV09XCJzZWxlY3RlZENhdGVnb3J5XCJcbiAgICAgICAgW2NvbG9ybWFwT3B0aW9uc109XCJjb2xvcm1hcE9wdGlvbnNcIlxuICAgICAgICAoY2F0ZWdvcnlDbGlja2VkKT1cImNhdGVnb3J5Q2xpY2tlZC5lbWl0KCRldmVudClcIlxuICAgICAgPjwvc3BhdGlhbC1rZXk+XG5cbiAgICAgIDwhLS0gRGlzcGxheSAtLT5cbiAgICAgIDxkaXYgY2xhc3M9XCJzYy1zZWN0aW9uXCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInNjLWxibFwiPlBvaW50IHNpemU8L3NwYW4+XG4gICAgICAgICAgPHAtc2xpZGVyXG4gICAgICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LnBvaW50U2NhbGVcIlxuICAgICAgICAgICAgW21pbl09XCIwLjI1XCJcbiAgICAgICAgICAgIFttYXhdPVwiNVwiXG4gICAgICAgICAgICBbc3RlcF09XCIwLjI1XCJcbiAgICAgICAgICAgIChvbkNoYW5nZSk9XCJvblBvaW50U2NhbGUoJGV2ZW50LnZhbHVlKVwiXG4gICAgICAgICAgPjwvcC1zbGlkZXI+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJzYy12YWxcIj57eyB2aWV3LnBvaW50U2NhbGUgfX3Dlzwvc3Bhbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInNjLWxibFwiPk9wYWNpdHk8L3NwYW4+XG4gICAgICAgICAgPHAtc2xpZGVyXG4gICAgICAgICAgICBbbmdNb2RlbF09XCJ2aWV3Lm9wYWNpdHlcIlxuICAgICAgICAgICAgW21pbl09XCIwLjFcIlxuICAgICAgICAgICAgW21heF09XCIxXCJcbiAgICAgICAgICAgIFtzdGVwXT1cIjAuMDVcIlxuICAgICAgICAgICAgKG9uQ2hhbmdlKT1cIm9uT3BhY2l0eSgkZXZlbnQudmFsdWUpXCJcbiAgICAgICAgICA+PC9wLXNsaWRlcj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInNjLXZhbFwiPnt7IHZpZXcub3BhY2l0eSB9fTwvc3Bhbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPCEtLSBPbmx5IGZvciBhIGdlbmU6IHRoZSBtYXAgZHJhd3MgdGhhdCBnZW5lJ3MgZXhwcmVzc2lvbiBhcyBhIGZpZWxkLiBJbiAyRFxuICAgICBpdCBnb2VzIHVuZGVyIHRoZSBjZWxscyBvZiB0aGUgZGlzcGxheWVkIHBsYW5lOyBpbiAzRCBpdCBpcyBvbmUgc2hlZXQgcGVyXG4gICAgIGltYWdlZCBzZWN0aW9uLCBvcHRpb25hbGx5IHNtb290aGVkIGFsb25nIHogaW50byBhIHZvbHVtZS4gLS0+XG4gIDxkaXYgY2xhc3M9XCJzYy1zZWN0aW9uXCIgKm5nSWY9XCJjYW5NYXBHZW5lXCI+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiPlxuICAgICAgPHAtY2hlY2tib3hcbiAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5nZW5lTWFwXCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uR2VuZU1hcCgkZXZlbnQuY2hlY2tlZClcIlxuICAgICAgICBsYWJlbD1cIkdlbmUgbWFwIChleHByZXNzaW9uIGZpZWxkKVwiXG4gICAgICAgIFtwVG9vbHRpcF09XCJcbiAgICAgICAgICBpczNkXG4gICAgICAgICAgICA/ICdEcmF3IHRoaXMgZ2VuZVxcJ3MgZXhwcmVzc2lvbiBhcyBvbmUgZmllbGQgcGVyIGltYWdlZCBzZWN0aW9uIOKAlCBhbiBlc3RpbWF0ZSBiZXR3ZWVuIHRoZSBjZWxscywgZW1wdHkgd2hlcmUgbm90aGluZyB3YXMgbWVhc3VyZWQnXG4gICAgICAgICAgICA6ICdEcmF3IHRoaXMgZ2VuZVxcJ3MgZXhwcmVzc2lvbiBhcyBhIHNtb290aCBmaWVsZCBVTkRFUiB0aGUgY2VsbHMg4oCUIGFuIGVzdGltYXRlIGJldHdlZW4gdGhlbSwgdHJhbnNwYXJlbnQgd2hlcmUgbm90aGluZyB3YXMgbWVhc3VyZWQnXG4gICAgICAgIFwiXG4gICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cInRvcFwiXG4gICAgICA+PC9wLWNoZWNrYm94PlxuICAgIDwvZGl2PlxuICAgIDwhLS0gM0Qgb25seTogaW50ZXJwb2xhdGluZyBhbG9uZyB6IHR1cm5zIHRoZSBtZWFzdXJlZCBzaGVldHMgaW50byBhblxuICAgICAgIGVzdGltYXRlLCBzbyBpdCBpcyBpdHMgb3duIGRlY2lzaW9uIGFuZCBpdHMgb3duIGxhYmVsLiAtLT5cbiAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCIgKm5nSWY9XCJ2aWV3LmdlbmVNYXAgJiYgaXMzZFwiPlxuICAgICAgPHAtY2hlY2tib3hcbiAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5nZW5lTWFwVm9sdW1lXCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uR2VuZU1hcFZvbHVtZSgkZXZlbnQuY2hlY2tlZClcIlxuICAgICAgICBsYWJlbD1cIlZvbHVtZSByZW5kZXJpbmcgKGludGVycG9sYXRlIGFsb25nIHopXCJcbiAgICAgICAgcFRvb2x0aXA9XCJTbW9vdGggdGhlIHBlci1zZWN0aW9uIGZpZWxkcyBpbnRvIGEgY29udGludW91cyB2b2x1bWUg4oCUIGFuIEVTVElNQVRFIGJldHdlZW4gdGhlIGltYWdlZCBzZWN0aW9ucywgbm90IG1lYXN1cmVkIHRpc3N1ZVwiXG4gICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cInRvcFwiXG4gICAgICA+PC9wLWNoZWNrYm94PlxuICAgIDwvZGl2PlxuICAgIDwhLS0gTWVhbmluZ2xlc3Mgd2hpbGUgaW50ZXJwb2xhdGluZzogYSB2b2x1bWUgYnVpbHQgZnJvbSBvbmUgc2VjdGlvbiB3b3VsZFxuICAgICAgIHNtZWFyIHRoYXQgc2xpZGUgdGhyb3VnaCB0aGUgc3BlY2ltZW4ncyB3aG9sZSBkZXB0aC4gLS0+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiICpuZ0lmPVwidmlldy5nZW5lTWFwICYmIGlzM2QgJiYgaXNTZWN0aW9uZWQgJiYgIXZpZXcuZ2VuZU1hcFZvbHVtZVwiPlxuICAgICAgPHAtY2hlY2tib3hcbiAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgW25nTW9kZWxdPVwiZ2VuZU1hcE9uZVNlY3Rpb25cIlxuICAgICAgICAob25DaGFuZ2UpPVwib25HZW5lTWFwT25lU2VjdGlvbigkZXZlbnQuY2hlY2tlZClcIlxuICAgICAgICBsYWJlbD1cIk9uZSBtYXAgc2VjdGlvbiBhdCBhIHRpbWVcIlxuICAgICAgICBwVG9vbHRpcD1cIkRyYXcgYSBzaW5nbGUgaW1hZ2VkIHNlY3Rpb24ncyBmaWVsZCBpbnN0ZWFkIG9mIHRoZSB3aG9sZSBzdGFjayBvZiBzaGVldHNcIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIlxuICAgICAgPjwvcC1jaGVja2JveD5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCIgKm5nSWY9XCJ2aWV3LmdlbmVNYXAgJiYgaXMzZCAmJiBpc1NlY3Rpb25lZCAmJiAhdmlldy5nZW5lTWFwVm9sdW1lICYmIGdlbmVNYXBPbmVTZWN0aW9uXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLWxibFwiPk1hcCBzZWN0aW9uPC9zcGFuPlxuICAgICAgPHAtc2xpZGVyXG4gICAgICAgIFtuZ01vZGVsXT1cInZpZXcuZ2VuZU1hcFNlY3Rpb25cIlxuICAgICAgICBbbWluXT1cIjBcIlxuICAgICAgICBbbWF4XT1cImxhc3RTZWN0aW9uXCJcbiAgICAgICAgW3N0ZXBdPVwiMVwiXG4gICAgICAgIChvbkNoYW5nZSk9XCJvbkdlbmVNYXBTZWN0aW9uKCRldmVudC52YWx1ZSlcIlxuICAgICAgPjwvcC1zbGlkZXI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLXZhbFwiPnt7IGdlbmVNYXBTZWN0aW9uTGFiZWwgfX08L3NwYW4+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiICpuZ0lmPVwidmlldy5nZW5lTWFwXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLWxibFwiPlNtb290aGluZzwvc3Bhbj5cbiAgICAgIDxwLXNsaWRlclxuICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LmdlbmVNYXBTbW9vdGhpbmdcIlxuICAgICAgICBbbWluXT1cIjAuNVwiXG4gICAgICAgIFttYXhdPVwiNFwiXG4gICAgICAgIFtzdGVwXT1cIjAuNVwiXG4gICAgICAgIChvbkNoYW5nZSk9XCJvbkdlbmVNYXBTbW9vdGhpbmcoJGV2ZW50LnZhbHVlKVwiXG4gICAgICA+PC9wLXNsaWRlcj5cbiAgICAgIDxzcGFuIGNsYXNzPVwic2MtdmFsXCI+e3sgdmlldy5nZW5lTWFwU21vb3RoaW5nIH19w5c8L3NwYW4+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiICpuZ0lmPVwidmlldy5nZW5lTWFwXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLWxibFwiPk1hcCBvcGFjaXR5PC9zcGFuPlxuICAgICAgPHAtc2xpZGVyXG4gICAgICAgIFtuZ01vZGVsXT1cInZpZXcuZ2VuZU1hcE9wYWNpdHlcIlxuICAgICAgICBbbWluXT1cIjAuMVwiXG4gICAgICAgIFttYXhdPVwiMVwiXG4gICAgICAgIFtzdGVwXT1cIjAuMDVcIlxuICAgICAgICAob25DaGFuZ2UpPVwib25HZW5lTWFwT3BhY2l0eSgkZXZlbnQudmFsdWUpXCJcbiAgICAgID48L3Atc2xpZGVyPlxuICAgICAgPHNwYW4gY2xhc3M9XCJzYy12YWxcIj57eyB2aWV3LmdlbmVNYXBPcGFjaXR5IH19PC9zcGFuPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJzYy1oaW50XCIgKm5nSWY9XCJ2aWV3LmdlbmVNYXAgJiYgIWlzM2RcIj5cbiAgICAgIEtlcm5lbC13ZWlnaHRlZCBtZWFuIHBlciBjZWxsLCBub3QgYSBzdW0g4oCUIHNvIGEgY3Jvd2RlZCByZWdpb24gZG9lcyBub3QgZ2xvdyBmb3IgYmVpbmcgY3Jvd2RlZC4gVHJhbnNwYXJlbnRcbiAgICAgIHdoZXJlIG5vIGNlbGwgd2FzIG1lYXN1cmVkLiBUdXJuIHRoZSBjZWxscycgb3duIE9wYWNpdHkgZG93biB0byByZWFkIHRoZSBmaWVsZCB1bmRlciB0aGVtLlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJzYy1oaW50XCIgKm5nSWY9XCJ2aWV3LmdlbmVNYXAgJiYgaXMzZFwiPlxuICAgICAge3sgZ2VuZU1hcFZvbHVtZU5vdGUgfX1cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPCEtLSAzRCBvbmx5OiB0aGUgdm9sdW1lLCB0aGUgY2xvdWQgYW5kIHRoZSBkZW5zaXR5IHZvbHVtZXMgb2NjdXB5IHRoZSBzYW1lXG4gICAgIHNwYWNlLCBzbyBlYWNoIGhpZGVzIHRoZSBvdGhlcnMuIEluZGVwZW5kZW50IHRvZ2dsZXMsIGJlY2F1c2UgdGhlIHVzZWZ1bFxuICAgICB2aWV3cyBhcmUgdGhlIGNvbWJpbmF0aW9ucyDigJQgdGhlIGZpZWxkIGFsb25lLCBvbmUgc2VjdGlvbidzIGNlbGxzIG92ZXIgaXQsXG4gICAgIHRoZSBhbmF0b215IGFsb25lLiAtLT5cbiAgPGRpdiBjbGFzcz1cInNjLXNlY3Rpb25cIiAqbmdJZj1cImlzM2RcIj5cbiAgICA8c3BhbiBjbGFzcz1cInNjLWxibFwiPlNob3c8L3NwYW4+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiICpuZ0lmPVwiZHMudm9sdW1lXCI+XG4gICAgICA8cC1jaGVja2JveFxuICAgICAgICBbYmluYXJ5XT1cInRydWVcIlxuICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LnNob3dWb2x1bWVcIlxuICAgICAgICAob25DaGFuZ2UpPVwib25TaG93Vm9sdW1lKCRldmVudC5jaGVja2VkKVwiXG4gICAgICAgIGxhYmVsPVwiUmVmZXJlbmNlIHZvbHVtZVwiXG4gICAgICAgIHBUb29sdGlwPVwiVGhlIHJlZ2lzdGVyZWQgYW5hdG9teSB0aGUgb2JzZXJ2YXRpb25zIHNpdCBpblwiXG4gICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cInRvcFwiXG4gICAgICA+PC9wLWNoZWNrYm94PlxuICAgIDwvZGl2PlxuICAgIDwhLS0gVGhlIGJhY2tkcm9wJ3Mgb3duIG9wYWNpdHk6IHJlYWRpbmcgdGhlIGNsb3VkIG9yIGEgZGVuc2l0eSBmaWVsZFxuICAgICAgIHRocm91Z2ggdGhlIGFuYXRvbXkgbWVhbnMgdHVybmluZyB0aGUgYW5hdG9teSBkb3duLCBub3QgdGhlIGRhdGEuIC0tPlxuICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIiAqbmdJZj1cImRzLnZvbHVtZSAmJiB2aWV3LnNob3dWb2x1bWVcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwic2MtbGJsXCI+Vm9sdW1lIG9wYWNpdHk8L3NwYW4+XG4gICAgICA8cC1zbGlkZXJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy52b2x1bWVPcGFjaXR5XCJcbiAgICAgICAgW21pbl09XCIwLjA1XCJcbiAgICAgICAgW21heF09XCIxXCJcbiAgICAgICAgW3N0ZXBdPVwiMC4wNVwiXG4gICAgICAgIChvbkNoYW5nZSk9XCJvblZvbHVtZU9wYWNpdHkoJGV2ZW50LnZhbHVlKVwiXG4gICAgICA+PC9wLXNsaWRlcj5cbiAgICAgIDxzcGFuIGNsYXNzPVwic2MtdmFsXCI+e3sgdmlldy52b2x1bWVPcGFjaXR5IH19PC9zcGFuPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIj5cbiAgICAgIDxwLWNoZWNrYm94XG4gICAgICAgIFtiaW5hcnldPVwidHJ1ZVwiXG4gICAgICAgIFtuZ01vZGVsXT1cInZpZXcuc2hvd1BvaW50c1wiXG4gICAgICAgIChvbkNoYW5nZSk9XCJvblNob3dQb2ludHMoJGV2ZW50LmNoZWNrZWQpXCJcbiAgICAgICAgbGFiZWw9XCJPYnNlcnZhdGlvbnNcIlxuICAgICAgICBwVG9vbHRpcD1cIlRoZSBtZWFzdXJlZCBjZWxscy4gSGlkZSB0aGVtIHRvIHJlYWQgdGhlIGRlbnNpdHkgdm9sdW1lcyB1bmRlcm5lYXRoXCJcbiAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwidG9wXCJcbiAgICAgID48L3AtY2hlY2tib3g+XG4gICAgPC9kaXY+XG4gICAgPCEtLSBPbmx5IGZvciBkYXRhIHRoYXQgcmVhbGx5IGlzIHNlY3Rpb25lZDsgYSBjb250aW51b3VzIHogaGFzIG5vIHNlY3Rpb25zXG4gICAgICAgdG8gc3RlcCB0aHJvdWdoLiAtLT5cbiAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCIgKm5nSWY9XCJ2aWV3LnNob3dQb2ludHMgJiYgaXNTZWN0aW9uZWRcIj5cbiAgICAgIDxwLWNoZWNrYm94XG4gICAgICAgIFtiaW5hcnldPVwidHJ1ZVwiXG4gICAgICAgIFtuZ01vZGVsXT1cIm9uZVNlY3Rpb25cIlxuICAgICAgICAob25DaGFuZ2UpPVwib25PbmVTZWN0aW9uKCRldmVudC5jaGVja2VkKVwiXG4gICAgICAgIGxhYmVsPVwiT25lIHNlY3Rpb24gYXQgYSB0aW1lXCJcbiAgICAgICAgcFRvb2x0aXA9XCJEcmF3IGEgc2luZ2xlIGltYWdlZCBzZWN0aW9uJ3MgY2VsbHMsIHNvIHRoZSBkZW5zaXR5IHZvbHVtZXMgYmV0d2VlbiB0aGUgc2VjdGlvbnMgc3RheSB2aXNpYmxlXCJcbiAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwidG9wXCJcbiAgICAgID48L3AtY2hlY2tib3g+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInNjLXJvd1wiICpuZ0lmPVwidmlldy5zaG93UG9pbnRzICYmIGlzU2VjdGlvbmVkICYmIG9uZVNlY3Rpb25cIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwic2MtbGJsXCI+U2VjdGlvbjwvc3Bhbj5cbiAgICAgIDxwLXNsaWRlclxuICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LnBvaW50U2VjdGlvblwiXG4gICAgICAgIFttaW5dPVwiMFwiXG4gICAgICAgIFttYXhdPVwibGFzdFNlY3Rpb25cIlxuICAgICAgICBbc3RlcF09XCIxXCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uUG9pbnRTZWN0aW9uKCRldmVudC52YWx1ZSlcIlxuICAgICAgPjwvcC1zbGlkZXI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLXZhbFwiPnt7IHNlY3Rpb25MYWJlbCB9fTwvc3Bhbj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPGRpdiBjbGFzcz1cInNjLXNlY3Rpb25cIiAqbmdJZj1cImlzM2RcIj5cbiAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCI+XG4gICAgICA8cC1jaGVja2JveFxuICAgICAgICBbYmluYXJ5XT1cInRydWVcIlxuICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LmRlbnNpdHlWb2x1bWVcIlxuICAgICAgICAob25DaGFuZ2UpPVwib25EZW5zaXR5Vm9sdW1lKCRldmVudC5jaGVja2VkKVwiXG4gICAgICAgIGxhYmVsPVwiQ2x1c3RlciBkZW5zaXR5IHZvbHVtZXNcIlxuICAgICAgICBwVG9vbHRpcD1cIlJheW1hcmNoIGVhY2ggY2x1c3RlciBhcyBhIHNtb290aCBkZW5zaXR5IGZpZWxkIOKAlCBhbiBFU1RJTUFURSBiZXR3ZWVuIHRoZSBpbWFnZWQgc2VjdGlvbnMsIG5vdCBtZWFzdXJlZCBjZWxsc1wiXG4gICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cInRvcFwiXG4gICAgICA+PC9wLWNoZWNrYm94PlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJzYy1yb3dcIiAqbmdJZj1cInZpZXcuZGVuc2l0eVZvbHVtZVwiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJzYy1sYmxcIj5TbW9vdGhpbmc8L3NwYW4+XG4gICAgICA8cC1zbGlkZXJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5kZW5zaXR5U21vb3RoaW5nXCJcbiAgICAgICAgW21pbl09XCIwLjVcIlxuICAgICAgICBbbWF4XT1cIjRcIlxuICAgICAgICBbc3RlcF09XCIwLjVcIlxuICAgICAgICAob25DaGFuZ2UpPVwib25EZW5zaXR5U21vb3RoaW5nKCRldmVudC52YWx1ZSlcIlxuICAgICAgPjwvcC1zbGlkZXI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLXZhbFwiPnt7IHZpZXcuZGVuc2l0eVNtb290aGluZyB9fcOXPC9zcGFuPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJzYy1oaW50XCIgKm5nSWY9XCJ2aWV3LmRlbnNpdHlWb2x1bWVcIj57eyBkZW5zaXR5Tm90ZSB9fTwvZGl2PlxuICA8L2Rpdj5cblxuICA8IS0tIENvbnRpbnVvdXMtb25seSBrbm9iczogbWVhbmluZ2xlc3MgZm9yIGEgY2F0ZWdvcmljYWwgcGFsZXR0ZS4gLS0+XG4gIDxkaXYgY2xhc3M9XCJzYy1zZWN0aW9uXCIgKm5nSWY9XCJpc0NvbnRpbnVvdXNcIj5cbiAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCI+XG4gICAgICA8cC1jaGVja2JveFxuICAgICAgICBbYmluYXJ5XT1cInRydWVcIlxuICAgICAgICBbbmdNb2RlbF09XCJ2aWV3LmxvZ1NjYWxlXCJcbiAgICAgICAgKG9uQ2hhbmdlKT1cIm9uTG9nU2NhbGUoJGV2ZW50LmNoZWNrZWQpXCJcbiAgICAgICAgbGFiZWw9XCJMb2cgc2NhbGVcIlxuICAgICAgICBwVG9vbHRpcD1cIkNvdW50cyBjb2xsYXBzZSBhZ2FpbnN0IHRoZWlyIG91dGxpZXJzIG9uIGEgbGluZWFyIHJhbXBcIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIlxuICAgICAgPjwvcC1jaGVja2JveD5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwic2Mtcm93XCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInNjLWxibFwiPkNsaXAgb3V0bGllcnM8L3NwYW4+XG4gICAgICA8cC1kcm9wZG93blxuICAgICAgICBbb3B0aW9uc109XCJjbGlwT3B0aW9uc1wiXG4gICAgICAgIG9wdGlvbkxhYmVsPVwibGFiZWxcIlxuICAgICAgICBvcHRpb25WYWx1ZT1cInZhbHVlXCJcbiAgICAgICAgW25nTW9kZWxdPVwidmlldy5wZXJjZW50aWxlQ2xpcFwiXG4gICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cIm9uQ2xpcCgkZXZlbnQpXCJcbiAgICAgICAgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgcFRvb2x0aXA9XCJQZXJjZW50aWxlIHRoZSBjb250cmFzdCB3aW5kb3cgaXMgdGFrZW4gYXRcIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIlxuICAgICAgPjwvcC1kcm9wZG93bj5cbiAgICAgIDwhLS0gUmVzZXJ2ZXMgdGhlIHJlYWRvdXQgY29sdW1uIHRoaXMgcm93IGhhcyBubyByZWFkb3V0IGZvciwgc28gdGhlXG4gICAgICAgICBjb250cm9sJ3MgcmlnaHQgZWRnZSBsaW5lcyB1cCB3aXRoIGV2ZXJ5IHNsaWRlcidzLiAtLT5cbiAgICAgIDxzcGFuIGNsYXNzPVwic2MtdmFsXCI+PC9zcGFuPlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cblxuICA8IS0tIEEgZ2VuZSdzIGljb24gaW4gaXRzIGNvbG91cjogdGhlIGJ1dHRvbiB0aGF0IG9wZW5zIGl0cyBpY29uIGFuZCBjb2xvdXIgcGlja2VyLiAtLT5cbiAgPG5nLXRlbXBsYXRlICNnZW5lTWFyayBsZXQtZz5cbiAgICA8YnV0dG9uXG4gICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgIGNsYXNzPVwic2MtZ2VuZS1pY29uXCJcbiAgICAgIFtzdHlsZS5jb2xvcl09XCJnZW5lQ29sb3JPZihnKVwiXG4gICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidJY29uIGFuZCBjb2xvdXIgZm9yICcgKyBnXCJcbiAgICAgIHBUb29sdGlwPVwiSWNvbiBhbmQgY29sb3VyXCJcbiAgICAgIHRvb2x0aXBQb3NpdGlvbj1cInRvcFwiXG4gICAgICAoY2xpY2spPVwib3BlbkdlbmVTdHlsZSgkZXZlbnQsIGcsIGdlbmVQb3ApXCJcbiAgICA+XG4gICAgICA8c3ZnIHZpZXdCb3g9XCItMS4yIC0xLjIgMi40IDIuNFwiIGNsYXNzPVwic2MtZ2x5cGgtc3ZnXCI+XG4gICAgICAgIDxwb2x5Z29uIFthdHRyLnBvaW50c109XCJnbHlwaFBvaW50c09mKGcpXCI+PC9wb2x5Z29uPlxuICAgICAgPC9zdmc+XG4gICAgPC9idXR0b24+XG4gIDwvbmctdGVtcGxhdGU+XG4gIDxwLW92ZXJsYXlQYW5lbCAjZ2VuZVBvcCBzdHlsZUNsYXNzPVwic2MtZ2VuZS1zdHlsZVwiIGFwcGVuZFRvPVwiYm9keVwiPlxuICAgIDxuZy10ZW1wbGF0ZSBwVGVtcGxhdGU9XCJjb250ZW50XCI+XG4gICAgICA8c3BhdGlhbC1nZW5lLXN0eWxlXG4gICAgICAgICpuZ0lmPVwic3R5bGVHZW5lIGFzIGdcIlxuICAgICAgICBbZ2VuZV09XCJnXCJcbiAgICAgICAgd2hhdD1cIk9ic2VydmF0aW9uXCJcbiAgICAgICAgW2NvbG9yXT1cImdlbmVDb2xvck9mKGcpXCJcbiAgICAgICAgW2dseXBoXT1cImdlbmVHbHlwaE9mKGcpXCJcbiAgICAgICAgaWNvbkhpbnQ9XCJJY29ucyBzaG93IG9uIHRoZSAyRCBvYnNlcnZhdGlvbiBtYXJrZXJzLlwiXG4gICAgICAgIChjb2xvckNoYW5nZSk9XCJvbkdlbmVDb2xvcihnLCAkZXZlbnQpXCJcbiAgICAgICAgKGdseXBoQ2hhbmdlKT1cIm9uR2VuZUdseXBoKGcsICRldmVudClcIlxuICAgICAgICAocmVzZXRTdHlsZSk9XCJyZXNldEdlbmVTdHlsZShnKVwiXG4gICAgICA+PC9zcGF0aWFsLWdlbmUtc3R5bGU+XG4gICAgPC9uZy10ZW1wbGF0ZT5cbiAgPC9wLW92ZXJsYXlQYW5lbD5cbjwvbmctY29udGFpbmVyPlxuIl19