@jax-data-science/sci-image-visualizer 0.2.18 → 0.3.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 (35) hide show
  1. package/esm2022/index.mjs +2 -9
  2. package/esm2022/lib/contracts/instance-segmenter.contract.mjs +10 -10
  3. package/esm2022/lib/contracts/semantic-segmenter.contract.mjs +10 -9
  4. package/esm2022/lib/contracts/toolbar-tool.contract.mjs +64 -0
  5. package/esm2022/lib/toolbar/model-info.mjs +32 -0
  6. package/esm2022/lib/toolbar/toolbar.component.mjs +82 -55
  7. package/esm2022/lib/visualizer.component.mjs +131 -138
  8. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-iPNL2JyC.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs} +2 -2
  9. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-iPNL2JyC.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map} +1 -1
  10. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +710 -1565
  11. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  12. package/index.d.ts +1 -6
  13. package/lib/contracts/instance-segmenter.contract.d.ts +7 -3
  14. package/lib/contracts/semantic-segmenter.contract.d.ts +7 -2
  15. package/lib/contracts/toolbar-tool.contract.d.ts +190 -0
  16. package/lib/toolbar/model-info.d.ts +17 -0
  17. package/lib/toolbar/toolbar.component.d.ts +53 -30
  18. package/lib/visualizer.component.d.ts +49 -58
  19. package/package.json +2 -4
  20. package/src/lib/assets/cell-detection.svg +37 -0
  21. package/src/lib/assets/cells.svg +1 -1
  22. package/src/lib/assets/sam-box-prompt.svg +61 -0
  23. package/src/lib/assets/sam-point-prompt.svg +91 -0
  24. package/esm2022/lib/toolbar/segmentation/retinal-layer-tool.service.mjs +0 -197
  25. package/esm2022/lib/toolbar/segmentation/retinal-model-registry.mjs +0 -119
  26. package/esm2022/lib/toolbar/segmentation/retinal-segmenter.service.mjs +0 -172
  27. package/esm2022/lib/toolbar/segmentation/yolo-detect-tool.service.mjs +0 -208
  28. package/esm2022/lib/toolbar/segmentation/yolo-model-registry.mjs +0 -129
  29. package/esm2022/lib/toolbar/segmentation/yolo-segmenter.service.mjs +0 -172
  30. package/lib/toolbar/segmentation/retinal-layer-tool.service.d.ts +0 -49
  31. package/lib/toolbar/segmentation/retinal-model-registry.d.ts +0 -71
  32. package/lib/toolbar/segmentation/retinal-segmenter.service.d.ts +0 -60
  33. package/lib/toolbar/segmentation/yolo-detect-tool.service.d.ts +0 -51
  34. package/lib/toolbar/segmentation/yolo-model-registry.d.ts +0 -59
  35. package/lib/toolbar/segmentation/yolo-segmenter.service.d.ts +0 -50
@@ -1,6 +1,7 @@
1
1
  import { Component, EventEmitter, Input, Output } from '@angular/core';
2
2
  import { PlotType, isNapari3d, isNapariIsosurface, isNapariSurface, isNapariScatter, NAPARI_DECIMATE_OPTIONS, NAPARI_DEFAULT_DECIMATE, } from '../contracts/plot-type';
3
3
  import { ALL_TOOLBAR_TOOLS } from '../contracts/toolbar-config';
4
+ import { MODEL_INFO } from './model-info';
4
5
  import * as i0 from "@angular/core";
5
6
  import * as i1 from "@angular/common";
6
7
  import * as i2 from "@angular/forms";
@@ -14,7 +15,6 @@ import * as i9 from "primeng/slider";
14
15
  import * as i10 from "primeng/inputnumber";
15
16
  import * as i11 from "primeng/dialog";
16
17
  import * as i12 from "primeng/ripple";
17
- import * as i13 from "primeng/overlaypanel";
18
18
  /**
19
19
  * Presentational toolbar for the plotting viewport.
20
20
  *
@@ -67,12 +67,14 @@ export class ToolbarComponent {
67
67
  /** SAM model picker options + current selection (jit-ui#90 P1). */
68
68
  this.samModels = [];
69
69
  this.samModelId = '';
70
- /** YOLO checkpoints offered by the host's registry, and the active one. */
71
- this.yoloModels = [];
72
- this.yoloModelId = '';
73
- /** Retinal-layer checkpoints offered by the host's registry, and the active one. */
74
- this.retinalModels = [];
75
- this.retinalModelId = '';
70
+ /**
71
+ * Tools contributed through {@link TOOLBAR_TOOLS}, already filtered to the
72
+ * ones with models and sorted. The host resolves them so it can own the
73
+ * per-tool parameter state; this component only draws them.
74
+ */
75
+ this.contributedTools = [];
76
+ /** Active checkpoint per contributed tool, keyed by tool id. */
77
+ this.toolModelIds = {};
76
78
  this.selectPlotType = new EventEmitter();
77
79
  /** Intensity (LINE) mode: add another colored line ROI + inset trace. */
78
80
  this.addProfileLine = new EventEmitter();
@@ -111,18 +113,12 @@ export class ToolbarComponent {
111
113
  this.segmentRegions = new EventEmitter();
112
114
  /** Run cellpose-SAM (auto) inside each drawn rectangle's crop (jit-ui#90). */
113
115
  this.segmentCellpose = new EventEmitter();
114
- /** Run YOLO detection over the current view. */
115
- this.detectYolo = new EventEmitter();
116
- /** Pick the YOLO checkpoint. */
117
- this.yoloModelChange = new EventEmitter();
118
- /** Open the YOLO parameter dialog. */
119
- this.openYoloParams = new EventEmitter();
120
- /** Run retinal-layer segmentation over the current view. */
121
- this.segmentRetinal = new EventEmitter();
122
- /** Pick the retinal-layer checkpoint. */
123
- this.retinalModelChange = new EventEmitter();
124
- /** Open the retinal-layer parameter dialog. */
125
- this.openRetinalParams = new EventEmitter();
116
+ /** Run a contributed tool over the current view, by tool id. */
117
+ this.runTool = new EventEmitter();
118
+ /** Pick a contributed tool's checkpoint. */
119
+ this.toolModelChange = new EventEmitter();
120
+ /** Open a contributed tool's parameter dialog, by tool id. */
121
+ this.openToolParams = new EventEmitter();
126
122
  /** SAM model picker (jit-ui#90 P1). */
127
123
  this.samModelChange = new EventEmitter();
128
124
  this.wandSensitivityChange = new EventEmitter();
@@ -149,12 +145,23 @@ export class ToolbarComponent {
149
145
  * Held as a stable array (rebuilt only when `samModels`/`samModelId` change),
150
146
  * NOT a getter: a getter returns a fresh array with new `command` closures on
151
147
  * every change-detection tick, which makes the bound `p-menu` overlay rebuild
152
- * its DOM mid-interaction and swallow the click on a menu item. */
148
+ * its DOM mid-interaction and swallow the click on a menu item.
149
+ *
150
+ * `tooltip` carries the model's description from {@link MODEL_INFO}; the
151
+ * menus' shared item template turns it into the hover info icon. p-menu's own
152
+ * rendering ignores the field (it reads `item.title`), so it is free to use. */
153
153
  this.samMenuItems = [];
154
- /** Same stable-array reasoning as {@link samMenuItems}. */
155
- this.yoloMenuItems = [];
156
- /** Same stable-array reasoning as {@link samMenuItems}. */
157
- this.retinalMenuItems = [];
154
+ /**
155
+ * Model menus for the contributed tools, keyed by tool id. Same stable-array
156
+ * reasoning as {@link samMenuItems} — and it is why this is a map rebuilt in
157
+ * `ngOnChanges` rather than a method called from the template, which would
158
+ * hand `p-menu` a new array every tick.
159
+ *
160
+ * A contributed tool supplies its own per-model description, so unlike the
161
+ * SAM menu these do not consult {@link MODEL_INFO}: this library has no
162
+ * copy for checkpoints it does not know about.
163
+ */
164
+ this.toolMenuItems = {};
158
165
  }
159
166
  /** Rebuild the SAM model menu when the model list or active selection
160
167
  * changes (keeps the array reference stable across other CD ticks). */
@@ -163,24 +170,54 @@ export class ToolbarComponent {
163
170
  this.samMenuItems = this.samModels.map((m) => ({
164
171
  label: m.label,
165
172
  icon: m.id === this.samModelId ? 'pi pi-check' : 'pi pi-fw',
173
+ tooltip: MODEL_INFO[m.id],
166
174
  command: () => this.samModelChange.emit(m.id),
167
175
  }));
168
176
  }
169
- if (changes['yoloModels'] || changes['yoloModelId']) {
170
- this.yoloMenuItems = this.yoloModels.map((m) => ({
171
- label: m.label,
172
- icon: m.id === this.yoloModelId ? 'pi pi-check' : 'pi pi-fw',
173
- command: () => this.yoloModelChange.emit(m.id),
174
- }));
175
- }
176
- if (changes['retinalModels'] || changes['retinalModelId']) {
177
- this.retinalMenuItems = this.retinalModels.map((m) => ({
178
- label: m.label,
179
- icon: m.id === this.retinalModelId ? 'pi pi-check' : 'pi pi-fw',
180
- command: () => this.retinalModelChange.emit(m.id),
181
- }));
177
+ if (changes['contributedTools'] || changes['toolModelIds']) {
178
+ this.toolMenuItems = {};
179
+ for (const tool of this.contributedTools) {
180
+ const active = this.toolModelIds[tool.id] ?? tool.defaultModelId();
181
+ this.toolMenuItems[tool.id] = tool.models().map((m) => ({
182
+ label: m.label,
183
+ icon: m.id === active ? 'pi pi-check' : 'pi pi-fw',
184
+ tooltip: m.info,
185
+ command: () => this.toolModelChange.emit({ toolId: tool.id, modelId: m.id }),
186
+ }));
187
+ }
182
188
  }
183
189
  }
190
+ /**
191
+ * Strip markup from a model description so it can be read aloud.
192
+ *
193
+ * The copy in MODEL_INFO and in a contribution's `ToolModelOption.info` is
194
+ * written for a visual tooltip rendered with `[escape]="false"`, so it carries
195
+ * `<b>` and `<br>`. Passed to `aria-label` verbatim a screen reader announces
196
+ * the tags, so they are removed and `<br>` becomes a sentence break. Entities
197
+ * used in that copy (`&nbsp;`, `&times;`, `&amp;`) are decoded for the same
198
+ * reason.
199
+ */
200
+ plainText(html) {
201
+ return (html ?? '')
202
+ .replace(/<br\s*\/?>/gi, '. ')
203
+ .replace(/<[^>]+>/g, '')
204
+ .replace(/&nbsp;/g, ' ')
205
+ .replace(/&times;/g, 'x')
206
+ .replace(/&amp;/g, '&')
207
+ .replace(/\s+/g, ' ')
208
+ .trim();
209
+ }
210
+ /** Contributed tool names for the help dialog's prompted/no-prompt contrast,
211
+ * e.g. "YOLO, Retinal layers". Empty when nothing is registered, which is
212
+ * why the sentence that uses it is itself conditional. */
213
+ get contributedToolNames() {
214
+ return this.contributedTools.map((t) => t.label).join(', ');
215
+ }
216
+ /** Keeps each contributed tool's `p-menu` overlay alive across CD ticks —
217
+ * re-creating it mid-interaction swallows the click on a menu item. */
218
+ trackToolById(_index, tool) {
219
+ return tool.id;
220
+ }
184
221
  /** The Image plot type renders as a natively pan/zoom-able raster, so the
185
222
  * generic zoom/pan tools are hidden. */
186
223
  get isImageView() {
@@ -234,11 +271,11 @@ export class ToolbarComponent {
234
271
  this.displayHelpDialog = true;
235
272
  }
236
273
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: ToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
237
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: ToolbarComponent, selector: "plotting-toolbar", inputs: { imageInfo: "imageInfo", plotTypeOptions: "plotTypeOptions", selectedPlotType: "selectedPlotType", isoRange: "isoRange", maxIndex: "maxIndex", zIndex: "zIndex", isHeatmap: "isHeatmap", activeDragMode: "activeDragMode", activeSurface3dMode: "activeSurface3dMode", axesVisible: "axesVisible", wireframeActive: "wireframeActive", resolutionScale: "resolutionScale", wandSensitivity: "wandSensitivity", brushSize: "brushSize", vertexEraserRadius: "vertexEraserRadius", canUndo: "canUndo", canRedo: "canRedo", stackOptions: "stackOptions", tools: "tools", imageSmoothingEnabled: "imageSmoothingEnabled", samModels: "samModels", samModelId: "samModelId", yoloModels: "yoloModels", yoloModelId: "yoloModelId", retinalModels: "retinalModels", retinalModelId: "retinalModelId" }, outputs: { selectPlotType: "selectPlotType", addProfileLine: "addProfileLine", toggleImageSmoothing: "toggleImageSmoothing", isoRangeChange: "isoRangeChange", openChannelHistogram: "openChannelHistogram", openRegionEditor: "openRegionEditor", selectStackOption: "selectStackOption", zScrub: "zScrub", zSlide: "zSlide", zIndexInput: "zIndexInput", reloadAndPlot: "reloadAndPlot", downloadImage: "downloadImage", autoscaleImage: "autoscaleImage", toggleDragMode: "toggleDragMode", zoomIn: "zoomIn", zoomOut: "zoomOut", toggleSurface3dMode: "toggleSurface3dMode", resetSurfaceCamera: "resetSurfaceCamera", toggleAxes: "toggleAxes", toggleWireframe: "toggleWireframe", selectResolution: "selectResolution", deleteRegion: "deleteRegion", undoRegion: "undoRegion", redoRegion: "redoRegion", segmentRegions: "segmentRegions", segmentCellpose: "segmentCellpose", detectYolo: "detectYolo", yoloModelChange: "yoloModelChange", openYoloParams: "openYoloParams", segmentRetinal: "segmentRetinal", retinalModelChange: "retinalModelChange", openRetinalParams: "openRetinalParams", samModelChange: "samModelChange", wandSensitivityChange: "wandSensitivityChange", brushSizeChange: "brushSizeChange", vertexEraserRadiusChange: "vertexEraserRadiusChange" }, usesOnChanges: true, ngImport: i0, template: "<p-toolbar>\n <div class=\"p-toolbar-group-start flex-wrap align-items-center gap-1\">\n\n <!-- Host-provided custom toolbar buttons, shown first (e.g. app-specific\n actions the visualization view itself doesn't own). -->\n <ng-content></ng-content>\n\n <!-- Plot type: available for any image (grayscale or RGB). -->\n <ng-container *ngIf=\"imageInfo && tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-dropdown [options]=\"plotTypeOptions\" optionLabel=\"label\" optionValue=\"type\"\n [(ngModel)]=\"selectedPlotType\" appendTo=\"body\"\n pTooltip=\"Select plot type\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '11rem'}\"\n styleClass=\"plotmode-dropdown\" panelStyleClass=\"plotmode-panel\"\n (onChange)=\"selectPlotType.emit($event.value)\">\n <ng-template let-opt pTemplate=\"selectedItem\">\n <span class=\"plotmode-item\" *ngIf=\"opt\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n <ng-template let-opt pTemplate=\"item\">\n <span class=\"plotmode-item\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n </p-dropdown>\n <ng-container *ngIf=\"isIsosurfaceMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Iso</span>\n <p-slider [(ngModel)]=\"isoRange\" [range]=\"true\"\n [min]=\"isoValueMin\" [max]=\"isoValueMax\" [step]=\"isoValueStep\"\n (onChange)=\"isoRangeChange.emit($event.values)\"\n pTooltip=\"Isosurface band (0\u2013255), mapped onto the volume's\n actual intensity range. Surfaces are drawn for voxels whose\n intensity falls in the selected band \u2014 narrow it to isolate\n features, widen it to see the whole structure.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ isoRange[0] }}\u2013{{ isoRange[1] }}</span>\n </ng-container>\n <!-- Intensity tools (Heatmap / Image mode): add another colored line ROI.\n Each added line gets the next bright palette colour and its own inset\n trace; select a line on the image and use the trash tool to remove it\n (and its trace). The lines are Region-based but excluded from the\n Region Editor and exports. -->\n <ng-container *ngIf=\"isIntensityCapable\">\n <span class=\"toolbar-separator\"></span>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Add an intensity line (new colour). Click a line to select it,\n then use the trash tool to remove it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"addProfileLine.emit()\">\n <img src=\"assets/plotting/spline-chart.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n </ng-container>\n <!-- Channels & Histogram: brightness/contrast, gamma, per-channel display,\n colormap/reverse/invert \u2014 opens the floating dialog. -->\n <p-button *ngIf=\"tools.specialTools\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Channels &amp; Histogram (brightness/contrast, colormap)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openChannelHistogram.emit()\">\n <img src=\"assets/plotting/invert.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Smoothen toggle: bilinear smoothing vs nearest-neighbour raw pixels (Image\n view only \u2014 it's an OpenSeadragon drawer setting). Highlighted when smoothing on. -->\n <p-button *ngIf=\"tools.specialTools && isImageView\" size=\"small\"\n [text]=\"!imageSmoothingEnabled\"\n pTooltip=\"Smoothen (turn on image smoothing)\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleImageSmoothing.emit()\">\n <img src=\"assets/plotting/smooth.svg\" style=\"width: 1rem\"\n [class]=\"imageSmoothingEnabled ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"/>\n </p-button>\n\n <!-- Stack controls -->\n <ng-container *ngIf=\"imageInfo?.isStack\">\n <span class=\"toolbar-separator\"></span>\n <!-- Image view: scrub z-slices live (the renderer swaps slices in place). -->\n <ng-container *ngIf=\"showsLiveSliceScrubber\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <p-slider [(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onChange)=\"zScrub.emit($event.value)\" (onSlideEnd)=\"zSlide.emit($event.value)\"\n pTooltip=\"Slice through the stack (\u2190 / \u2192 keys)\" tooltipPosition=\"bottom\"\n [style]=\"{'width':'16rem'}\" styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text\">{{zIndex}} of {{maxIndex}}</span>\n </ng-container>\n <!-- Heatmap view: single-image (high-def zoom) vs stack (frame slider). Plotly-only \u2014\n hidden for napari, which has no single/stack toggle or per-frame fetch. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode\">\n <p-button icon=\"pi pi-image\" size=\"small\"\n [text]=\"imageInfo?.showStack\"\n pTooltip=\"Single image mode: enables high-def zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[0])\">\n </p-button>\n <p-button icon=\"pi pi-images\" size=\"small\"\n [text]=\"!imageInfo?.showStack\"\n pTooltip=\"Stack mode, enables slicing through image frames\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[1])\">\n </p-button>\n <ng-container *ngIf=\"!imageInfo?.showStack\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <div class=\"p-inputgroup\" style=\"width: auto\">\n <p-inputNumber styleClass=\"w-3rem toolbar-input\"\n\t\t\t\t\tpTooltip=\"View image in stack (&#x23CE;)\"\n\t\t\t\t\t[(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onInput)=\"zIndexInput.emit(zIndex)\" (keydown.enter)=\"reloadAndPlot.emit()\"/>\n <p-button icon=\"pi pi-refresh\" size=\"small\" [text]=\"true\" (onClick)=\"reloadAndPlot.emit()\"\n pTooltip=\"Plot this slice\" tooltipPosition=\"bottom\">\n </p-button>\n </div>\n <span class=\"flex align-items-center mx-1 toolbar-text\">of {{maxIndex}}</span>\n </ng-container>\n </ng-container>\n </ng-container>\n\n <!-- Image actions (special tools) -->\n <ng-container *ngIf=\"tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-download\" size=\"small\" [text]=\"true\"\n pTooltip=\"Download image as PNG\" tooltipPosition=\"bottom\"\n (onClick)=\"downloadImage.emit()\">\n </p-button>\n <p-button *ngIf=\"isHeatmap\" icon=\"pi pi-window-maximize\" size=\"small\" [text]=\"true\"\n pTooltip=\"Fit to view\" tooltipPosition=\"bottom\"\n (onClick)=\"autoscaleImage.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Zoom tools. Plotly rubber-band drag-zoom: on OpenSeadragon the box-zoom\n covers this and a bare drag pans natively, so it's hidden in image view. -->\n <ng-container *ngIf=\"isHeatmap && tools.zoomTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button *ngIf=\"!isImageView\" icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeDragMode !== 'zoom'\"\n pTooltip=\"Zoom drag\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoom')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'zoomToBox'\"\n pTooltip=\"Zoom box (<kbd>b</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoomToBox')\">\n <img src=\"assets/plotting/zoom-box.svg\"\n [class]=\"activeDragMode === 'zoomToBox' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeDragMode !== 'pan'\"\n pTooltip=\"Pan the image (<kbd>p</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-search-plus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom in (<kbd>+</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomIn.emit()\">\n </p-button>\n <p-button icon=\"pi pi-search-minus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom out (<kbd>-</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomOut.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Surface 3D controls -->\n <ng-container *ngIf=\"!isHeatmap\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'zoom'\"\n pTooltip=\"Zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('zoom')\">\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'pan'\"\n pTooltip=\"Pan\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-globe\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'orbit'\"\n pTooltip=\"Orbital rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('orbit')\">\n </p-button>\n <p-button icon=\"pi pi-sync\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'turntable'\"\n pTooltip=\"Turntable rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('turntable')\">\n </p-button>\n <p-button icon=\"pi pi-home\" size=\"small\" [text]=\"true\"\n pTooltip=\"Reset camera to default\" tooltipPosition=\"bottom\"\n (onClick)=\"resetSurfaceCamera.emit()\">\n </p-button>\n <!-- 3D coordinate-axes / scale gizmo toggle (napari-js WebGPU volume/isosurface only).\n `p-button-icon-only` gives the same square sizing as the neighbouring pi-icon buttons\n (the projected <img> doesn't trigger PrimeNG's automatic icon-only sizing). -->\n <p-button size=\"small\" *ngIf=\"isNapariMode\" styleClass=\"p-button-icon-only\"\n [text]=\"!axesVisible\"\n pTooltip=\"Toggle 3D axes / scale\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleAxes.emit()\">\n <img src=\"assets/plotting/3d-coordinates.svg\"\n [class]=\"axesVisible ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <!-- Surface wireframe toggle (napari-js WebGPU surface only). -->\n <p-button icon=\"pi pi-share-alt\" size=\"small\" *ngIf=\"isNapariSurfaceMode\"\n [text]=\"!wireframeActive\"\n pTooltip=\"Toggle wireframe\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleWireframe.emit()\">\n </p-button>\n <!-- Resolution (decimate factor) for the napari 3D types \u2014 reloads at coarser/finer sampling. -->\n <ng-container *ngIf=\"isNapari3dMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Res</span>\n <p-dropdown [options]=\"decimateOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"resolutionScale\" appendTo=\"body\"\n pTooltip=\"Resolution (decimate factor)\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '6rem'}\"\n (onChange)=\"selectResolution.emit($event.value)\">\n </p-dropdown>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"isHeatmap && tools.regionTools\">\n <span class=\"toolbar-separator\"></span>\n <!-- Region Editor: opens the editor as a floating dialog (formerly the\n right-panel Regions tab). Sits with the region drawing tools. -->\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Open the Region Editor\" tooltipPosition=\"bottom\"\n (onClick)=\"openRegionEditor.emit()\">\n <img src=\"assets/plotting/region-editor.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <!-- Region drawing tools -->\n <p-button icon=\"pi pi-arrow-up-right\" size=\"small\"\n [text]=\"activeDragMode !== 'select'\"\n pTooltip=\"Selection mode (<kbd>s</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('select')\">\n </p-button>\n <p-button icon=\"pi pi-stop\" size=\"small\"\n [text]=\"activeDragMode !== 'drawrect'\"\n pTooltip=\"Draw a rectangular region (<kbd>r</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawrect')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawopenpath'\"\n pTooltip=\"Draw a polyline (<kbd>l</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawopenpath')\">\n <img src=\"assets/plotting/polyline.svg\"\n [class]=\"activeDragMode === 'drawopenpath' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-pencil\" size=\"small\"\n [text]=\"activeDragMode !== 'drawclosedpath'\"\n pTooltip=\"Draw a freeform region (<kbd>f</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawclosedpath')\">\n </p-button>\n <!-- Brush: paint a region by dragging a disc of the given size; the Size\n slider appears while it's active. Hold Shift to erase (subtract). -->\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'brush'\"\n pTooltip=\"Brush: click and drag to paint a region.\n <br>Hold <kbd>Shift</kbd> while painting to erase from a region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('brush')\">\n <img src=\"assets/plotting/paintbrush.svg\"\n [class]=\"activeDragMode === 'brush' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'brush'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Size</span>\n <p-slider [(ngModel)]=\"brushSize\"\n [min]=\"brushSizeMin\" [max]=\"brushSizeMax\" [step]=\"brushSizeStep\"\n (onChange)=\"brushSizeChange.emit($event.value)\"\n pTooltip=\"Brush diameter in image pixels.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ brushSize }}</span>\n </ng-container>\n\n <!-- Vertex editing \u2014 backends with a vertex-editing overlay (OSD + napari-js image). -->\n <ng-container *ngIf=\"supportsRegionVertexTools\">\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawpolygon'\"\n pTooltip=\"Polygon: click to place each vertex; click the first point to close\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawpolygon')\">\n <img src=\"assets/plotting/polygon-vertices.svg\"\n [class]=\"activeDragMode === 'drawpolygon' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'addpoint'\"\n pTooltip=\"Add a vertex: click an edge of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('addpoint')\">\n <img src=\"assets/plotting/vertex-add.svg\"\n [class]=\"activeDragMode === 'addpoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'deletepoint'\"\n pTooltip=\"Delete a vertex: click a vertex of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('deletepoint')\">\n <img src=\"assets/plotting/vertex-delete.svg\"\n [class]=\"activeDragMode === 'deletepoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'wand'\"\n pTooltip=\"Wand: click and drag to grow a region by similar pixel values (<kbd>w</kbd>).\n <br>Hold <kbd>Shift</kbd> while clicking or dragging on a region to erase pixels from it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('wand')\">\n <img src=\"assets/plotting/wand.svg\"\n [class]=\"activeDragMode === 'wand' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'wand'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Sens.</span>\n <p-slider [(ngModel)]=\"wandSensitivity\"\n [min]=\"wandSensitivityMin\" [max]=\"wandSensitivityMax\" [step]=\"wandSensitivityStep\"\n (onChange)=\"wandSensitivityChange.emit($event.value)\"\n pTooltip=\"Higher = stricter (smaller selection); lower = looser.\n Takes effect from the next click/drag tick.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ wandSensitivity.toFixed(1) }}</span>\n </ng-container>\n <p-button icon=\"pi pi-eraser\" size=\"small\"\n [text]=\"activeDragMode !== 'eraseVertex'\"\n pTooltip=\"Vertex eraser (<kbd>e</kbd>): click or drag to remove polygon vertices inside\n the cursor circle from any region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('eraseVertex')\">\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'eraseVertex'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Radius</span>\n <p-slider [(ngModel)]=\"vertexEraserRadius\"\n [min]=\"vertexEraserRadiusMin\" [max]=\"vertexEraserRadiusMax\" [step]=\"vertexEraserRadiusStep\"\n (onChange)=\"vertexEraserRadiusChange.emit($event.value)\"\n pTooltip=\"Eraser radius in image pixels.\n Vertices within this distance of the cursor are removed.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ vertexEraserRadius }}</span>\n </ng-container>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canUndo\"\n pTooltip=\"Undo the last region action (<kbd>Ctrl</kbd>+<kbd>Z</kbd>).\n Up to 10 actions can be undone.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"undoRegion.emit()\">\n <img src=\"assets/plotting/undo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canRedo\"\n pTooltip=\"Redo the last undone region action\n (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd>).\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"redoRegion.emit()\">\n <img src=\"assets/plotting/redo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-trash\" size=\"small\" [text]=\"true\"\n pTooltip=\"Delete selected region (<kbd>Del</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"deleteRegion.emit()\">\n </p-button>\n <!-- SAM (jit-ui#90): box-prompt segment, interactive point prompts, and a\n model picker. -->\n <span class=\"toolbar-separator\"></span>\n <!-- SAM controls grouped: box-prompt Segment, then the point-prompt tool,\n then the model-picker dropdown (the chevron) which governs BOTH tools.\n Falls back to Segment + point with no model picker when none are set. -->\n <span *ngIf=\"samModels.length; else segmentPlain\" class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/picture-segmentation.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the SAM model (used by both segment tools)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"samModelMenu.toggle($event)\">\n </p-button>\n </span>\n <ng-template #segmentPlain>\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/picture-segmentation.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n </span>\n </ng-template>\n <!-- Point-prompt tool \u2014 shared between the models / no-models layouts so it\n always sits just left of the model dropdown. -->\n <ng-template #samPointBtn>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'samPoint'\"\n pTooltip=\"SAM point prompts: click a fiber to segment it as a new region;\n Shift/Alt-click to refine (exclude) the current one.<br>Esc undoes it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('samPoint')\">\n <img src=\"assets/plotting/click.svg\"\n [class]=\"activeDragMode === 'samPoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-template>\n <p-menu #samModelMenu [model]=\"samMenuItems\" [popup]=\"true\" appendTo=\"body\"></p-menu>\n <!-- Help: explains the ViT-T vs ViT-B trade-off behind the model picker. -->\n <p-button *ngIf=\"samModels.length\" icon=\"pi pi-info-circle\" size=\"small\" [text]=\"true\"\n pTooltip=\"About the SAM models\" tooltipPosition=\"bottom\"\n (onClick)=\"samModelInfo.toggle($event)\">\n </p-button>\n <p-overlayPanel #samModelInfo appendTo=\"body\" styleClass=\"sam-model-info\">\n <div style=\"max-width: 24rem; line-height: 1.4\">\n <div class=\"font-bold mb-1\">SAM models</div>\n <p class=\"mt-0 mb-2\">\n All are promptable Segment Anything models finetuned for biomedical images. They differ\n in the <i>image encoder</i> (the heavy once-per-image step) and the training domain \u2014\n box and point prompting behave the same.\n </p>\n <div class=\"mb-2\">\n <div class=\"font-bold\">micro-sam ViT-T <span class=\"font-normal\">(default)</span></div>\n <div>Light-microscopy finetuned. TinyViT encoder, ~14&nbsp;MB, very fast, runs on WASM.\n Good mask quality \u2014 best for quick interactive work and many/large objects.</div>\n </div>\n <div class=\"mb-2\">\n <div class=\"font-bold\">micro-sam ViT-B</div>\n <div>Light-microscopy finetuned. ViT-Base encoder, ~172&nbsp;MB, slower, GPU-accelerated\n (WebGPU). Highest accuracy \u2014 when ViT-T misses subtle or overlapping boundaries.</div>\n </div>\n <div class=\"mb-2\">\n <div class=\"font-bold\">patho-sam ViT-B</div>\n <div>Histopathology finetuned (H&amp;E / stained tissue). ViT-Base encoder, GPU-accelerated.\n Use it for pathology slides \u2014 it segments tissue/nuclei better than the microscopy models.</div>\n </div>\n <div class=\"mb-2\">\n <div class=\"font-bold\">patho-sam ViT-B (int8)</div>\n <div>Same model, int8-quantized \u2014 ~100&nbsp;MB vs ~180&nbsp;MB (smaller download). Runs on\n WASM (slower encode than the fp16 version); masks are near-identical. Pick it on slow\n connections.</div>\n </div>\n <div class=\"text-sm\" style=\"opacity: 0.75\">\n The choice applies to both the box and point tools. Each tool loads its own encoder,\n so the first click after switching re-encodes once.\n </div>\n </div>\n </p-overlayPanel>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Auto-segment cells inside each drawn rectangle (cellpose-SAM, client-side)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentCellpose.emit()\">\n <img src=\"assets/plotting/cells.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- YOLO: detect objects across the current view. Unlike the SAM and\n cellpose tools it takes no prompt \u2014 a detector finds objects in a\n field rather than being pointed at one \u2014 so there is nothing to draw\n first. Split button: run, pick checkpoint, open parameters. -->\n <span *ngIf=\"yoloModels.length\" class=\"p-buttonset segment-split\">\n <p-button icon=\"pi pi-microchip-ai\" size=\"small\" [text]=\"true\"\n pTooltip=\"Detect objects in the current view with YOLO (client-side)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"detectYolo.emit()\">\n </p-button>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the YOLO model\"\n tooltipPosition=\"bottom\"\n (onClick)=\"yoloModelMenu.toggle($event)\">\n </p-button>\n <p-button icon=\"pi pi-sliders-h\" size=\"small\" [text]=\"true\"\n pTooltip=\"YOLO parameters\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openYoloParams.emit()\">\n </p-button>\n </span>\n <p-menu #yoloModelMenu [model]=\"yoloMenuItems\" [popup]=\"true\" appendTo=\"body\"></p-menu>\n\n <!-- Retinal layers: semantic segmentation of the current view. Same\n no-prompt shape as YOLO \u2014 it labels every pixel rather than being\n pointed at an object \u2014 so the same split button: run, pick\n checkpoint, open parameters. -->\n <span *ngIf=\"retinalModels.length\" class=\"p-buttonset segment-split\">\n <p-button icon=\"pi pi-align-left\" size=\"small\" [text]=\"true\"\n pTooltip=\"Segment retinal layers in the current view (client-side)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRetinal.emit()\">\n </p-button>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the retinal layer model\"\n tooltipPosition=\"bottom\"\n (onClick)=\"retinalModelMenu.toggle($event)\">\n </p-button>\n <p-button icon=\"pi pi-sliders-h\" size=\"small\" [text]=\"true\"\n pTooltip=\"Retinal layer parameters\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openRetinalParams.emit()\">\n </p-button>\n </span>\n <p-menu #retinalModelMenu [model]=\"retinalMenuItems\" [popup]=\"true\" appendTo=\"body\"></p-menu>\n </ng-container>\n\n <ng-container *ngIf=\"tools.help\">\n <span class=\"toolbar-separator\"></span>\n\n <!-- Help -->\n <p-button icon=\"pi pi-question-circle\" size=\"small\" [text]=\"true\"\n pTooltip=\"Help\" tooltipPosition=\"bottom\"\n (onClick)=\"showHelp()\">\n </p-button>\n </ng-container>\n\n </div>\n</p-toolbar>\n<p-dialog header=\"Plotting help\" [(visible)]=\"displayHelpDialog\" [modal]=\"true\" [style]=\"{width: '50vw'}\"\n [maximizable]=\"true\" [draggable]=\"false\" [resizable]=\"false\">\n <p>Click a file in the left-hand tree to display it. By default it opens in the\n <b>Image</b> view \u2014 a natively tiled, deeply zoomable raster (OpenSeadragon) with a\n navigator minimap and a physical-units scale bar. <b>Click the image to zoom</b> toward\n the clicked point; scroll to zoom and drag to pan. When a stack is open, a slice slider\n (Image view) or image-mode toggle (other views) appears. Regions can also be edited in the\n <b>Region Editor</b>, opened with the\n <img src=\"assets/plotting/region-editor.svg\" width=\"14\" height=\"14\" alt=\"Region Editor\"/>\n toolbar button as a floating dialog. Drag the <i class=\"pi pi-bars\"></i> handle to detach\n this toolbar into a floating window.\n </p>\n\n <h3>Plot-type dropdown</h3>\n <p>\n The <b>plot-type dropdown</b> switches how the file is visualized:\n <b>Image</b>, <b>Contour</b>, <b>Scatter (regions)</b>, <b>Surface (3D)</b>,\n <b>Scatter 3D</b>, and <b>Isosurface (3D)</b>. Scalar and 3D types require a grayscale\n image (the 3D volume types also require a z-stack). The non-image views render with\n Plotly and support \"real zooming\" (a downscaled overview that re-fetches higher\n resolution as you zoom in).\n </p>\n <ul>\n <li>\"<b>Isosurface band</b>\" slider (Isosurface): sets the intensity band the surfaces are\n drawn for, mapped onto the volume's real intensity range.</li>\n <li>\"<b>3D camera</b>\" (Surface / Scatter 3D / Isosurface): zoom <i class=\"pi pi-search\"></i>,\n pan <i class=\"pi pi-arrows-alt\"></i>, orbit <i class=\"pi pi-globe\"></i>,\n turntable <i class=\"pi pi-sync\"></i>, and reset camera <i class=\"pi pi-home\"></i>;\n scroll to zoom the 3D scene.</li>\n </ul>\n\n <h3>Image manipulation tools</h3>\n <ul>\n <li>\"<b>Region Editor</b>\" <img src=\"assets/plotting/region-editor.svg\" width=\"15\" height=\"15\">:\n opens the Region Editor as a floating, draggable dialog \u2014 a per-image table of every region\n (class, colour, area) with bulk select/label/colour/delete and GeoJSON import/export. It stays\n in sync with the image, so you can keep it open while drawing.</li>\n <li>\"<b>Intensity profile lines</b>\" <img src=\"assets/plotting/spline-chart.svg\" width=\"15\" height=\"15\">\n (<i>work in progress \u2014 not yet stable</i>): adds a coloured line ROI; a floating inset\n chart plots intensity along each line, updates live as you drag it, and re-samples at the\n current zoom resolution. Each added line gets its own colour; select a line and use the\n trash tool <i class=\"pi pi-trash\"></i> to remove it (and its trace).</li>\n <li>\"<b>Channels &amp; Histogram</b>\" <img src=\"assets/plotting/invert.svg\" width=\"15\" height=\"15\">:\n opens a floating dialog for brightness/contrast, gamma, and per-channel display, plus the\n <b>colormap / LUT</b> controls (grayscale only) \u2014 a <b>reverse</b> toggle and a colormap\n dropdown (default: inverted greys).</li>\n <li>\"<b>Smoothen</b>\" <img src=\"assets/plotting/smooth.svg\" width=\"15\" height=\"15\">\n (Image view only): the image opens showing <b>raw pixels</b> (nearest-neighbour), so\n zooming in renders crisp blocks for pixel inspection. Toggle this on for bilinear\n smoothing; the icon turns white while smoothing is active.</li>\n </ul>\n\n <h3>Download &amp; view tools</h3>\n <ul>\n <li>\"<b>Download</b>\" <i class=\"pi pi-download\"></i>: download the current image as a PNG.</li>\n <li>\"<b>Fit to view</b>\" / autoscale <i class=\"pi pi-window-maximize\"></i>: resets the image\n to its original dimensions. In stack mode it also resets to single-image mode.</li>\n </ul>\n\n <h3>Zoom tools</h3>\n <ul>\n <li>\"<b>Zoom box</b>\" <img src=\"assets/plotting/zoom-box.svg\" width=\"15\" height=\"15\">\n (<kbd>b</kbd>): drag a box to select an area to zoom into.</li>\n <li>\"<b>Pan</b>\" <i class=\"pi pi-arrows-alt\"></i> (<kbd>p</kbd>): drag to pan the image.</li>\n <li>\"<b>Zoom in</b>\" <i class=\"pi pi-search-plus\"></i> (<kbd>+</kbd>): zoom in one step.</li>\n <li>\"<b>Zoom out</b>\" <i class=\"pi pi-search-minus\"></i> (<kbd>-</kbd>): zoom out one step,\n stopping at the original image dimensions.</li>\n </ul>\n\n <h3>Region tools</h3>\n <p>By default no tool is selected (all greyed-out); selecting a tool highlights it. The\n vertex-editing tools (polygon, add/delete vertex, move, B\u00E9zier conversions) are available\n in the <b>Image</b> view only. On a <b>donut</b> (a region with a hole), the inner-ring\n vertices are editable too \u2014 drag, add, and delete them just like the outer ring.</p>\n <ul>\n <li>\"<b>Selection</b>\" <i class=\"pi pi-arrow-up-right\"></i> (<kbd>s</kbd>): a neutral mode\n that turns off whatever drawing tool was active (wand, vertex eraser, rectangle, etc.).\n Use it to step out of editing without picking another tool.</li>\n <li>\"<b>Rectangular region</b>\" <i class=\"pi pi-stop\"></i> (<kbd>r</kbd>): click and drag to\n create a rectangular region.</li>\n <li>\"<b>Polyline</b>\" <img src=\"assets/plotting/polyline.svg\" width=\"14\" height=\"14\">\n (<kbd>l</kbd>): draw an open (non-closed) polyline by clicking and dragging. Polylines can\n be edited and exported to GeoJSON as a <code>LineString</code> feature.</li>\n <li>\"<b>Freeform region</b>\" <i class=\"pi pi-pencil\"></i> (<kbd>f</kbd>): click and drag to\n draw a closed freeform polygon.</li>\n <li>\"<b>Brush</b>\" <img src=\"assets/plotting/paintbrush.svg\" width=\"15\" height=\"15\">: click\n and drag to paint a region with a circular brush \u2014 the painted stroke is the region.\n A <b>Size</b> slider (brush diameter, in image pixels) appears in the toolbar while the\n brush is active. Hold <kbd>Shift</kbd> while painting to <b>erase</b> (subtract) from a\n region instead. Painting over an existing region extends it; brushing across two regions\n merges them. Painting a closed ring (or shift-erasing an interior pocket) leaves the\n enclosed area empty \u2014 a <b>donut</b> (Image view).</li>\n <li>\"<b>Polygon</b>\" <img src=\"assets/plotting/polygon-vertices.svg\" width=\"15\" height=\"15\">:\n click to place each vertex; click the first point to close the polygon.</li>\n <li>\"<b>Add vertex</b>\" <img src=\"assets/plotting/vertex-add.svg\" width=\"15\" height=\"15\">:\n click an edge of the selected polygon to insert a new vertex.</li>\n <li>\"<b>Delete vertex</b>\" <img src=\"assets/plotting/vertex-delete.svg\" width=\"15\" height=\"15\">:\n click a vertex of the selected polygon to remove it.</li>\n <li>\"<b>Move region</b>\" <img src=\"assets/plotting/region-move.svg\" width=\"15\" height=\"15\">:\n drag to move the selected region.</li>\n <li>\"<b>To B\u00E9zier</b>\" <img src=\"assets/plotting/to-bezier.svg\" width=\"15\" height=\"15\"> /\n \"<b>To polygon</b>\" <img src=\"assets/plotting/to-polygon.svg\" width=\"15\" height=\"15\">:\n convert the selected region to a smooth B\u00E9zier curve, or back to a straight-edged polygon.</li>\n <li>\"<b>Wand</b>\" <img src=\"assets/plotting/wand.svg\" width=\"15\" height=\"15\"> (<kbd>w</kbd>):\n click to grow a region from pixels with similar values around the click point. Hold\n <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> for an exact-match flood fill; drag to keep extending the\n selection while moving; hold <kbd>Shift</kbd> while clicking or dragging on an existing\n region to <b>erase</b> pixels from it. Use the <b>Sens.</b> slider that appears to make the\n wand stricter (higher = smaller selection) or looser. Subsequent clicks keep extending the\n same region; clicking inside an existing region adopts it, and growing into another region\n merges them.</li>\n <li>\"<b>Vertex eraser</b>\" <i class=\"pi pi-eraser\"></i> (<kbd>e</kbd>): click or click-and-drag\n to remove polygon vertices that fall inside the cursor circle from any region, including a\n region's inner rings (donut holes). Use the <b>Radius</b> slider to set the eraser size in\n image pixels. Polygons reduced below 3 vertices (or polylines below 2) are deleted entirely;\n a hole reduced below 3 vertices is removed.</li>\n <li>\"<b>Undo</b>\" <img src=\"assets/plotting/undo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd>): reverts the\n last region action (draw, delete, edit, wand/brush stroke, segmentation, \u2026). Up to the last\n <b>10</b> actions can be undone; the button greys out when there's nothing left to undo.</li>\n <li>\"<b>Redo</b>\" <img src=\"assets/plotting/redo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> or <kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Y</kbd>):\n re-applies the most recently undone action. Performing a new region action clears what can be redone.</li>\n <li>\"<b>Delete region</b>\" <i class=\"pi pi-trash\"></i> (<kbd>Del</kbd>): deletes the selected\n region. This can also be done from the Region Editor.</li>\n </ul>\n\n <h3>Region actions (right-click the image)</h3>\n <p>Right-click a region \u2014 or the current multi-selection \u2014 to open the\n \"<b>Selected region(s)</b>\" group at the top of the context menu. These act immediately on\n the selection (they're not drawing modes) and are fully undo/redo-able:</p>\n <ul>\n <li>\"<b>Select all regions</b>\" <i class=\"pi pi-check-square\"></i>: select every region on\n the image (also in the Region Editor toolbar).</li>\n <li>\"<b>Merge / group</b>\": combine 2+ selected regions into one. If the pieces don't touch,\n the result is a single multi-part region (sparse \"bits\").</li>\n <li>\"<b>Ungroup</b>\": split a multi-part region back into one region per piece.</li>\n <li>\"<b>Inverse</b>\": replace the selection with everything <i>outside</i> it \u2014 the image\n rectangle with the selected regions punched out as holes. Works at any image size.</li>\n <li>\"<b>Simplify</b>\": reduce a region's vertex count (Douglas\u2013Peucker) by an altitude\n threshold in pixels \u2014 pick <b>Light</b> (1&nbsp;px), <b>Medium</b> (3&nbsp;px),\n <b>Strong</b> (8&nbsp;px), or <b>Custom\u2026</b> for a slider. Applies to the exterior and any\n holes (and every part of a multi-part region).</li>\n <li>The B\u00E9zier conversions and Delete also live in this group (the right-click equivalents of\n the toolbar / vertex tools above).</li>\n </ul>\n\n <h3>Segmentation tools (SAM &amp; cellpose)</h3>\n <p>AI-assisted segmentation that runs entirely in the browser. The box and point\n tools use a promptable <b>micro-sam</b> model; the cellpose tool auto-detects cells.\n Generated regions inherit the color of the rectangle they came from. The first run\n downloads the model (a progress toast is shown); it's then cached.</p>\n <ul>\n <li>\"<b>Segment</b>\" <img src=\"assets/plotting/picture-segmentation.svg\" width=\"15\" height=\"15\">\n (box prompt): draw one or more <b>Rectangular regions</b> around the objects of interest,\n then click Segment. Each rectangle is replaced by the SAM mask of the object inside it.</li>\n <li>\"<b>Point prompts</b>\" <img src=\"assets/plotting/click.svg\" width=\"15\" height=\"15\">: click directly on an object to\n segment it as a <b>new</b> region. Each click starts a fresh object \u2014 clicking another\n object won't grow the previous one. <kbd>Shift</kbd>/<kbd>Alt</kbd>-click adds an\n <i>exclude</i> point that refines the current object; <kbd>Esc</kbd> undoes it,\n <kbd>Enter</kbd> commits it.</li>\n <li>\"<b>Model picker</b>\" <i class=\"pi pi-chevron-down\"></i>: the dropdown attached to the\n Segment button chooses the SAM model \u2014 <b>micro-sam ViT-T</b> (default; small, fast) or\n <b>micro-sam ViT-B</b> (larger, highest accuracy) for light microscopy, or\n <b>patho-sam ViT-B</b> for histopathology (H&amp;E / stained tissue). The choice applies to\n <i>both</i> the box and point tools. The <i class=\"pi pi-info-circle\"></i> info button next\n to it explains the trade-off.</li>\n <li>\"<b>Cellpose</b>\" <img src=\"assets/plotting/cells.svg\" width=\"15\" height=\"15\">: draw rectangles, then click Cellpose to\n automatically segment every cell inside each rectangle (client-side cellpose-SAM) \u2014 one\n region per detected cell. No clicking on individual cells needed.</li>\n </ul>\n\n <h3>Help</h3>\n <p>\"<b>Help</b>\" <i class=\"pi pi-question-circle\"></i>: opens this dialog.</p>\n\n <h3>Keyboard shortcuts</h3>\n <table class=\"shortcuts-table\">\n <tr><td><kbd>+</kbd> / <kbd>=</kbd></td><td>Zoom in</td></tr>\n <tr><td><kbd>-</kbd></td><td>Zoom out</td></tr>\n <tr><td><kbd>b</kbd></td><td>Toggle zoom box mode</td></tr>\n <tr><td><kbd>p</kbd></td><td>Toggle pan mode</td></tr>\n <tr><td><kbd>f</kbd></td><td>Toggle freeform region drawing</td></tr>\n <tr><td><kbd>l</kbd></td><td>Toggle polyline drawing</td></tr>\n <tr><td><kbd>r</kbd></td><td>Toggle rectangular region drawing</td></tr>\n <tr><td><kbd>w</kbd></td><td>Toggle wand tool</td></tr>\n <tr><td>Wand + <kbd>Shift</kbd></td><td>Erase pixels from the active region</td></tr>\n <tr><td>Wand + <kbd>Ctrl</kbd>/<kbd>Cmd</kbd></td><td>Exact-match flood fill (no smoothing)</td></tr>\n <tr><td><kbd>e</kbd></td><td>Toggle vertex eraser</td></tr>\n <tr><td><kbd>s</kbd></td><td>Selection mode (deactivates drawing tools)</td></tr>\n <tr><td>Point prompt + <kbd>Shift</kbd>/<kbd>Alt</kbd></td><td>Add an exclude point (refine the current object)</td></tr>\n <tr><td><kbd>Enter</kbd></td><td>Commit the current SAM point object</td></tr>\n <tr><td><kbd>Esc</kbd></td><td>Undo the current SAM point object</td></tr>\n <tr><td><kbd>Del</kbd> / <kbd>\u232B</kbd></td><td>Delete selected region</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd></td><td>Undo the last region action (up to 10)</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd></td><td>Redo the last undone region action</td></tr>\n </table>\n\n <ng-template pTemplate=\"footer\">\n <p-button icon=\"pi pi-check\" (click)=\"displayHelpDialog=false\" label=\"Ok\" styleClass=\"p-button-text\">\n </p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:block}:host ::ng-deep .p-toolbar{padding:.25rem .5rem}.toolbar-text{color:var(--primary-color);font-size:.875rem}::ng-deep .toolbar-input .p-inputtext{color:var(--primary-color);border-color:var(--primary-color)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-icon-active{filter:brightness(0) invert(1)}::ng-deep .plotmode-item{display:inline-flex;align-items:center;gap:.5rem;color:#000}::ng-deep .plotmode-item i{font-size:1rem;color:var(--primary-color)}::ng-deep .plotmode-item .plotmode-icon-img{width:1.1rem;height:1.1rem;object-fit:contain;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-sensitivity-slider{width:8rem;align-self:center;margin:0 .25rem}.wand-sensitivity-value{min-width:2rem;text-align:right}span.colormap{width:50px;height:auto;display:inline-block}img.colormap{width:50px;height:auto}.colormap{background:no-repeat;background-size:100%;vertical-align:middle}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.shortcuts-table{border-collapse:collapse}.shortcuts-table td{padding:.25rem .75rem .25rem 0;vertical-align:middle}.shortcuts-table td:first-child{white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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: "component", type: i3.Toolbar, selector: "p-toolbar", inputs: ["style", "styleClass", "ariaLabelledBy"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i5.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i6.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "appendTo", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }, { kind: "directive", type: i7.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: i8.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: "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: "component", type: i10.InputNumber, selector: "p-inputNumber", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "component", type: i11.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "visible", "style", "position"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i12.Ripple, selector: "[pRipple]" }, { kind: "component", type: i13.OverlayPanel, selector: "p-overlayPanel", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "showCloseIcon", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }] }); }
274
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: ToolbarComponent, selector: "plotting-toolbar", inputs: { imageInfo: "imageInfo", plotTypeOptions: "plotTypeOptions", selectedPlotType: "selectedPlotType", isoRange: "isoRange", maxIndex: "maxIndex", zIndex: "zIndex", isHeatmap: "isHeatmap", activeDragMode: "activeDragMode", activeSurface3dMode: "activeSurface3dMode", axesVisible: "axesVisible", wireframeActive: "wireframeActive", resolutionScale: "resolutionScale", wandSensitivity: "wandSensitivity", brushSize: "brushSize", vertexEraserRadius: "vertexEraserRadius", canUndo: "canUndo", canRedo: "canRedo", stackOptions: "stackOptions", tools: "tools", imageSmoothingEnabled: "imageSmoothingEnabled", samModels: "samModels", samModelId: "samModelId", contributedTools: "contributedTools", toolModelIds: "toolModelIds" }, outputs: { selectPlotType: "selectPlotType", addProfileLine: "addProfileLine", toggleImageSmoothing: "toggleImageSmoothing", isoRangeChange: "isoRangeChange", openChannelHistogram: "openChannelHistogram", openRegionEditor: "openRegionEditor", selectStackOption: "selectStackOption", zScrub: "zScrub", zSlide: "zSlide", zIndexInput: "zIndexInput", reloadAndPlot: "reloadAndPlot", downloadImage: "downloadImage", autoscaleImage: "autoscaleImage", toggleDragMode: "toggleDragMode", zoomIn: "zoomIn", zoomOut: "zoomOut", toggleSurface3dMode: "toggleSurface3dMode", resetSurfaceCamera: "resetSurfaceCamera", toggleAxes: "toggleAxes", toggleWireframe: "toggleWireframe", selectResolution: "selectResolution", deleteRegion: "deleteRegion", undoRegion: "undoRegion", redoRegion: "redoRegion", segmentRegions: "segmentRegions", segmentCellpose: "segmentCellpose", runTool: "runTool", toolModelChange: "toolModelChange", openToolParams: "openToolParams", samModelChange: "samModelChange", wandSensitivityChange: "wandSensitivityChange", brushSizeChange: "brushSizeChange", vertexEraserRadiusChange: "vertexEraserRadiusChange" }, usesOnChanges: true, ngImport: i0, template: "<p-toolbar>\n <div class=\"p-toolbar-group-start flex-wrap align-items-center gap-1\">\n\n <!-- Host-provided custom toolbar buttons, shown first (e.g. app-specific\n actions the visualization view itself doesn't own). -->\n <ng-content></ng-content>\n\n <!-- Plot type: available for any image (grayscale or RGB). -->\n <ng-container *ngIf=\"imageInfo && tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-dropdown [options]=\"plotTypeOptions\" optionLabel=\"label\" optionValue=\"type\"\n [(ngModel)]=\"selectedPlotType\" appendTo=\"body\"\n pTooltip=\"Select plot type\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '11rem'}\"\n styleClass=\"plotmode-dropdown\" panelStyleClass=\"plotmode-panel\"\n (onChange)=\"selectPlotType.emit($event.value)\">\n <ng-template let-opt pTemplate=\"selectedItem\">\n <span class=\"plotmode-item\" *ngIf=\"opt\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n <ng-template let-opt pTemplate=\"item\">\n <span class=\"plotmode-item\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n </p-dropdown>\n <ng-container *ngIf=\"isIsosurfaceMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Iso</span>\n <p-slider [(ngModel)]=\"isoRange\" [range]=\"true\"\n [min]=\"isoValueMin\" [max]=\"isoValueMax\" [step]=\"isoValueStep\"\n (onChange)=\"isoRangeChange.emit($event.values)\"\n pTooltip=\"Isosurface band (0\u2013255), mapped onto the volume's\n actual intensity range. Surfaces are drawn for voxels whose\n intensity falls in the selected band \u2014 narrow it to isolate\n features, widen it to see the whole structure.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ isoRange[0] }}\u2013{{ isoRange[1] }}</span>\n </ng-container>\n <!-- Intensity tools (Heatmap / Image mode): add another colored line ROI.\n Each added line gets the next bright palette colour and its own inset\n trace; select a line on the image and use the trash tool to remove it\n (and its trace). The lines are Region-based but excluded from the\n Region Editor and exports. -->\n <ng-container *ngIf=\"isIntensityCapable\">\n <span class=\"toolbar-separator\"></span>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Add an intensity line (new colour). Click a line to select it,\n then use the trash tool to remove it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"addProfileLine.emit()\">\n <img src=\"assets/plotting/spline-chart.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n </ng-container>\n <!-- Channels & Histogram: brightness/contrast, gamma, per-channel display,\n colormap/reverse/invert \u2014 opens the floating dialog. -->\n <p-button *ngIf=\"tools.specialTools\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Channels &amp; Histogram (brightness/contrast, colormap)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openChannelHistogram.emit()\">\n <img src=\"assets/plotting/invert.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Smoothen toggle: bilinear smoothing vs nearest-neighbour raw pixels (Image\n view only \u2014 it's an OpenSeadragon drawer setting). Highlighted when smoothing on. -->\n <p-button *ngIf=\"tools.specialTools && isImageView\" size=\"small\"\n [text]=\"!imageSmoothingEnabled\"\n pTooltip=\"Smoothen (turn on image smoothing)\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleImageSmoothing.emit()\">\n <img src=\"assets/plotting/smooth.svg\" style=\"width: 1rem\"\n [class]=\"imageSmoothingEnabled ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"/>\n </p-button>\n\n <!-- Stack controls -->\n <ng-container *ngIf=\"imageInfo?.isStack\">\n <span class=\"toolbar-separator\"></span>\n <!-- Image view: scrub z-slices live (the renderer swaps slices in place). -->\n <ng-container *ngIf=\"showsLiveSliceScrubber\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <p-slider [(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onChange)=\"zScrub.emit($event.value)\" (onSlideEnd)=\"zSlide.emit($event.value)\"\n pTooltip=\"Slice through the stack (\u2190 / \u2192 keys)\" tooltipPosition=\"bottom\"\n [style]=\"{'width':'16rem'}\" styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text\">{{zIndex}} of {{maxIndex}}</span>\n </ng-container>\n <!-- Heatmap view: single-image (high-def zoom) vs stack (frame slider). Plotly-only \u2014\n hidden for napari, which has no single/stack toggle or per-frame fetch. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode\">\n <p-button icon=\"pi pi-image\" size=\"small\"\n [text]=\"imageInfo?.showStack\"\n pTooltip=\"Single image mode: enables high-def zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[0])\">\n </p-button>\n <p-button icon=\"pi pi-images\" size=\"small\"\n [text]=\"!imageInfo?.showStack\"\n pTooltip=\"Stack mode, enables slicing through image frames\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[1])\">\n </p-button>\n <ng-container *ngIf=\"!imageInfo?.showStack\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <div class=\"p-inputgroup\" style=\"width: auto\">\n <p-inputNumber styleClass=\"w-3rem toolbar-input\"\n\t\t\t\t\tpTooltip=\"View image in stack (&#x23CE;)\"\n\t\t\t\t\t[(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onInput)=\"zIndexInput.emit(zIndex)\" (keydown.enter)=\"reloadAndPlot.emit()\"/>\n <p-button icon=\"pi pi-refresh\" size=\"small\" [text]=\"true\" (onClick)=\"reloadAndPlot.emit()\"\n pTooltip=\"Plot this slice\" tooltipPosition=\"bottom\">\n </p-button>\n </div>\n <span class=\"flex align-items-center mx-1 toolbar-text\">of {{maxIndex}}</span>\n </ng-container>\n </ng-container>\n </ng-container>\n\n <!-- Image actions (special tools) -->\n <ng-container *ngIf=\"tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-download\" size=\"small\" [text]=\"true\"\n pTooltip=\"Download image as PNG\" tooltipPosition=\"bottom\"\n (onClick)=\"downloadImage.emit()\">\n </p-button>\n <p-button *ngIf=\"isHeatmap\" icon=\"pi pi-window-maximize\" size=\"small\" [text]=\"true\"\n pTooltip=\"Fit to view\" tooltipPosition=\"bottom\"\n (onClick)=\"autoscaleImage.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Zoom tools. Plotly rubber-band drag-zoom: on OpenSeadragon the box-zoom\n covers this and a bare drag pans natively, so it's hidden in image view. -->\n <ng-container *ngIf=\"isHeatmap && tools.zoomTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button *ngIf=\"!isImageView\" icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeDragMode !== 'zoom'\"\n pTooltip=\"Zoom drag\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoom')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'zoomToBox'\"\n pTooltip=\"Zoom box (<kbd>b</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoomToBox')\">\n <img src=\"assets/plotting/zoom-box.svg\"\n [class]=\"activeDragMode === 'zoomToBox' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeDragMode !== 'pan'\"\n pTooltip=\"Pan the image (<kbd>p</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-search-plus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom in (<kbd>+</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomIn.emit()\">\n </p-button>\n <p-button icon=\"pi pi-search-minus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom out (<kbd>-</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomOut.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Surface 3D controls -->\n <ng-container *ngIf=\"!isHeatmap\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'zoom'\"\n pTooltip=\"Zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('zoom')\">\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'pan'\"\n pTooltip=\"Pan\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-globe\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'orbit'\"\n pTooltip=\"Orbital rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('orbit')\">\n </p-button>\n <p-button icon=\"pi pi-sync\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'turntable'\"\n pTooltip=\"Turntable rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('turntable')\">\n </p-button>\n <p-button icon=\"pi pi-home\" size=\"small\" [text]=\"true\"\n pTooltip=\"Reset camera to default\" tooltipPosition=\"bottom\"\n (onClick)=\"resetSurfaceCamera.emit()\">\n </p-button>\n <!-- 3D coordinate-axes / scale gizmo toggle (napari-js WebGPU volume/isosurface only).\n `p-button-icon-only` gives the same square sizing as the neighbouring pi-icon buttons\n (the projected <img> doesn't trigger PrimeNG's automatic icon-only sizing). -->\n <p-button size=\"small\" *ngIf=\"isNapariMode\" styleClass=\"p-button-icon-only\"\n [text]=\"!axesVisible\"\n pTooltip=\"Toggle 3D axes / scale\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleAxes.emit()\">\n <img src=\"assets/plotting/3d-coordinates.svg\"\n [class]=\"axesVisible ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <!-- Surface wireframe toggle (napari-js WebGPU surface only). -->\n <p-button icon=\"pi pi-share-alt\" size=\"small\" *ngIf=\"isNapariSurfaceMode\"\n [text]=\"!wireframeActive\"\n pTooltip=\"Toggle wireframe\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleWireframe.emit()\">\n </p-button>\n <!-- Resolution (decimate factor) for the napari 3D types \u2014 reloads at coarser/finer sampling. -->\n <ng-container *ngIf=\"isNapari3dMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Res</span>\n <p-dropdown [options]=\"decimateOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"resolutionScale\" appendTo=\"body\"\n pTooltip=\"Resolution (decimate factor)\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '6rem'}\"\n (onChange)=\"selectResolution.emit($event.value)\">\n </p-dropdown>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"isHeatmap && tools.regionTools\">\n <span class=\"toolbar-separator\"></span>\n <!-- Region Editor: opens the editor as a floating dialog (formerly the\n right-panel Regions tab). Sits with the region drawing tools. -->\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Open the Region Editor\" tooltipPosition=\"bottom\"\n (onClick)=\"openRegionEditor.emit()\">\n <img src=\"assets/plotting/region-editor.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <!-- Region drawing tools -->\n <p-button icon=\"pi pi-arrow-up-right\" size=\"small\"\n [text]=\"activeDragMode !== 'select'\"\n pTooltip=\"Selection mode (<kbd>s</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('select')\">\n </p-button>\n <p-button icon=\"pi pi-stop\" size=\"small\"\n [text]=\"activeDragMode !== 'drawrect'\"\n pTooltip=\"Draw a rectangular region (<kbd>r</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawrect')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawopenpath'\"\n pTooltip=\"Draw a polyline (<kbd>l</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawopenpath')\">\n <img src=\"assets/plotting/polyline.svg\"\n [class]=\"activeDragMode === 'drawopenpath' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-pencil\" size=\"small\"\n [text]=\"activeDragMode !== 'drawclosedpath'\"\n pTooltip=\"Draw a freeform region (<kbd>f</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawclosedpath')\">\n </p-button>\n <!-- Brush: paint a region by dragging a disc of the given size; the Size\n slider appears while it's active. Hold Shift to erase (subtract). -->\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'brush'\"\n pTooltip=\"Brush: click and drag to paint a region.\n <br>Hold <kbd>Shift</kbd> while painting to erase from a region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('brush')\">\n <img src=\"assets/plotting/paintbrush.svg\"\n [class]=\"activeDragMode === 'brush' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'brush'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Size</span>\n <p-slider [(ngModel)]=\"brushSize\"\n [min]=\"brushSizeMin\" [max]=\"brushSizeMax\" [step]=\"brushSizeStep\"\n (onChange)=\"brushSizeChange.emit($event.value)\"\n pTooltip=\"Brush diameter in image pixels.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ brushSize }}</span>\n </ng-container>\n\n <!-- Vertex editing \u2014 backends with a vertex-editing overlay (OSD + napari-js image). -->\n <ng-container *ngIf=\"supportsRegionVertexTools\">\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawpolygon'\"\n pTooltip=\"Polygon: click to place each vertex; click the first point to close\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawpolygon')\">\n <img src=\"assets/plotting/polygon-vertices.svg\"\n [class]=\"activeDragMode === 'drawpolygon' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'addpoint'\"\n pTooltip=\"Add a vertex: click an edge of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('addpoint')\">\n <img src=\"assets/plotting/vertex-add.svg\"\n [class]=\"activeDragMode === 'addpoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'deletepoint'\"\n pTooltip=\"Delete a vertex: click a vertex of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('deletepoint')\">\n <img src=\"assets/plotting/vertex-delete.svg\"\n [class]=\"activeDragMode === 'deletepoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'wand'\"\n pTooltip=\"Wand: click and drag to grow a region by similar pixel values (<kbd>w</kbd>).\n <br>Hold <kbd>Shift</kbd> while clicking or dragging on a region to erase pixels from it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('wand')\">\n <img src=\"assets/plotting/wand.svg\"\n [class]=\"activeDragMode === 'wand' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'wand'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Sens.</span>\n <p-slider [(ngModel)]=\"wandSensitivity\"\n [min]=\"wandSensitivityMin\" [max]=\"wandSensitivityMax\" [step]=\"wandSensitivityStep\"\n (onChange)=\"wandSensitivityChange.emit($event.value)\"\n pTooltip=\"Higher = stricter (smaller selection); lower = looser.\n Takes effect from the next click/drag tick.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ wandSensitivity.toFixed(1) }}</span>\n </ng-container>\n <p-button icon=\"pi pi-eraser\" size=\"small\"\n [text]=\"activeDragMode !== 'eraseVertex'\"\n pTooltip=\"Vertex eraser (<kbd>e</kbd>): click or drag to remove polygon vertices inside\n the cursor circle from any region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('eraseVertex')\">\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'eraseVertex'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Radius</span>\n <p-slider [(ngModel)]=\"vertexEraserRadius\"\n [min]=\"vertexEraserRadiusMin\" [max]=\"vertexEraserRadiusMax\" [step]=\"vertexEraserRadiusStep\"\n (onChange)=\"vertexEraserRadiusChange.emit($event.value)\"\n pTooltip=\"Eraser radius in image pixels.\n Vertices within this distance of the cursor are removed.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ vertexEraserRadius }}</span>\n </ng-container>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canUndo\"\n pTooltip=\"Undo the last region action (<kbd>Ctrl</kbd>+<kbd>Z</kbd>).\n Up to 10 actions can be undone.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"undoRegion.emit()\">\n <img src=\"assets/plotting/undo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canRedo\"\n pTooltip=\"Redo the last undone region action\n (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd>).\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"redoRegion.emit()\">\n <img src=\"assets/plotting/redo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-trash\" size=\"small\" [text]=\"true\"\n pTooltip=\"Delete selected region (<kbd>Del</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"deleteRegion.emit()\">\n </p-button>\n <!-- SAM (jit-ui#90): box-prompt segment, interactive point prompts, and a\n model picker. -->\n <span class=\"toolbar-separator\"></span>\n <!-- SAM controls grouped: box-prompt Segment, then the point-prompt tool,\n then the model-picker dropdown (the chevron) which governs BOTH tools.\n Falls back to Segment + point with no model picker when none are set. -->\n <span *ngIf=\"samModels.length; else segmentPlain\" class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/sam-box-prompt.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the SAM model (used by both segment tools)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"samModelMenu.toggle($event)\">\n </p-button>\n </span>\n <ng-template #segmentPlain>\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/sam-box-prompt.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n </span>\n </ng-template>\n <!-- Point-prompt tool \u2014 shared between the models / no-models layouts so it\n always sits just left of the model dropdown. -->\n <ng-template #samPointBtn>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'samPoint'\"\n pTooltip=\"SAM point prompts: click a fiber to segment it as a new region;\n Shift/Alt-click to refine (exclude) the current one.<br>Esc undoes it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('samPoint')\">\n <img src=\"assets/plotting/sam-point-prompt.svg\"\n [class]=\"activeDragMode === 'samPoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-template>\n <p-menu #samModelMenu styleClass=\"model-menu\" [model]=\"samMenuItems\" [popup]=\"true\" appendTo=\"body\">\n <ng-template pTemplate=\"item\" let-item>\n <ng-container [ngTemplateOutlet]=\"modelMenuItem\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n </ng-template>\n </p-menu>\n\n <!-- Shared item body for all three model pickers (SAM / YOLO / retinal):\n the standard check-or-blank icon and label, plus an info icon carrying\n the model's description as a hover tooltip. This replaced a single\n \"About the SAM models\" overlay panel \u2014 per-model text sits next to the\n model it describes, and YOLO/retinal get the same affordance.\n\n p-menu puts its click handler on the wrapping `.p-menuitem-content`,\n outside this template, so `command` still fires and the whole row stays\n clickable \u2014 which is also why the info icon has to stop propagation, or\n reading about a model would switch to it. -->\n <ng-template #modelMenuItem let-item>\n <a class=\"p-menuitem-link model-menu-item\">\n <span class=\"p-menuitem-icon\" [ngClass]=\"item.icon\"></span>\n <span class=\"p-menuitem-text\">{{ item.label }}</span>\n <!-- `bottom` like every other tooltip in this toolbar, and for room:\n these menus open under buttons that can sit near the right edge,\n where a `right` tooltip has a few hundred px and squeezes its text\n into a narrow column instead of using its max-width. -->\n <!-- A focusable <button>, not a bare <i>. As an <i> the description\n was reachable only by hovering a mouse: not tabbable, no\n accessible name, nothing for a screen reader to announce \u2014 so\n keyboard and AT users could pick a checkpoint but never read what\n it was. PrimeNG's tooltip shows on focus as well as hover, so\n making it a button restores it for free.\n\n `aria-describedby` is deliberately not used: the tooltip is\n rendered into `body` on demand and has no stable id to point at.\n The description is carried on `aria-label` instead, which is\n announced whether or not the tooltip is showing. It is stripped of\n markup first, since the copy contains `<b>`/`<br>` for the visual\n tooltip and those would otherwise be read out literally. -->\n <button *ngIf=\"item.tooltip\" type=\"button\"\n class=\"pi pi-info-circle model-info-icon\"\n [attr.aria-label]=\"'About ' + item.label + ': ' + plainText(item.tooltip)\"\n [pTooltip]=\"item.tooltip\" [escape]=\"false\" tooltipPosition=\"bottom\"\n appendTo=\"body\" tooltipStyleClass=\"model-info-tooltip\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.space)=\"$event.stopPropagation()\"></button>\n </a>\n </ng-template>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Auto-segment cells inside each drawn rectangle (cellpose-SAM)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentCellpose.emit()\">\n <img src=\"assets/plotting/cells.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Contributed no-prompt tools (TOOLBAR_TOOLS). Unlike the SAM and\n cellpose tools these take no prompt \u2014 they sweep the whole current\n view rather than being pointed at something \u2014 so there is nothing to\n draw first. Each renders the same split button: run, pick checkpoint,\n open parameters.\n\n Nothing here names a specific tool. The library ships none; a host\n registers them, which is what lets this package be published without\n the closed model dependencies. -->\n <ng-container *ngFor=\"let tool of contributedTools; trackBy: trackToolById\">\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.runTooltip\"\n tooltipPosition=\"bottom\"\n (onClick)=\"runTool.emit(tool.id)\">\n <!-- `toolbar-icon-dark` is hardcoded rather than bound, unlike the\n toggle tools: these fire a run and have no active state. -->\n <img *ngIf=\"tool.icon.src\" [src]=\"tool.icon.src\" class=\"toolbar-icon-dark\"\n style=\"width: 1rem\" [alt]=\"tool.label\" />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.modelTooltip || 'Pick the model'\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toolMenu.toggle($event)\">\n </p-button>\n <p-button *ngIf=\"tool.params.length\"\n icon=\"pi pi-sliders-h\" size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.paramsTooltip || 'Parameters'\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openToolParams.emit(tool.id)\">\n </p-button>\n </span>\n <p-menu #toolMenu styleClass=\"model-menu\" [model]=\"toolMenuItems[tool.id] || []\"\n [popup]=\"true\" appendTo=\"body\">\n <ng-template pTemplate=\"item\" let-item>\n <ng-container [ngTemplateOutlet]=\"modelMenuItem\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n </ng-template>\n </p-menu>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"tools.help\">\n <span class=\"toolbar-separator\"></span>\n\n <!-- Help -->\n <p-button icon=\"pi pi-question-circle\" size=\"small\" [text]=\"true\"\n pTooltip=\"Help\" tooltipPosition=\"bottom\"\n (onClick)=\"showHelp()\">\n </p-button>\n </ng-container>\n\n </div>\n</p-toolbar>\n<p-dialog header=\"Plotting help\" [(visible)]=\"displayHelpDialog\" [modal]=\"true\" [style]=\"{width: '50vw'}\"\n [maximizable]=\"true\" [draggable]=\"false\" [resizable]=\"false\">\n <p>Click a file in the left-hand tree to display it. By default it opens in the\n <b>Image</b> view \u2014 a natively tiled, deeply zoomable raster (OpenSeadragon) with a\n navigator minimap and a physical-units scale bar. <b>Click the image to zoom</b> toward\n the clicked point; scroll to zoom and drag to pan. When a stack is open, a slice slider\n (Image view) or image-mode toggle (other views) appears. Regions can also be edited in the\n <b>Region Editor</b>, opened with the\n <img src=\"assets/plotting/region-editor.svg\" width=\"14\" height=\"14\" alt=\"Region Editor\"/>\n toolbar button as a floating dialog. Drag the <i class=\"pi pi-bars\"></i> handle to detach\n this toolbar into a floating window.\n </p>\n\n <h3>Plot-type dropdown</h3>\n <p>\n The <b>plot-type dropdown</b> switches how the file is visualized:\n <b>Image</b>, <b>Contour</b>, <b>Scatter (regions)</b>, <b>Surface (3D)</b>,\n <b>Scatter 3D</b>, and <b>Isosurface (3D)</b>. Scalar and 3D types require a grayscale\n image (the 3D volume types also require a z-stack). The non-image views render with\n Plotly and support \"real zooming\" (a downscaled overview that re-fetches higher\n resolution as you zoom in).\n </p>\n <ul>\n <li>\"<b>Isosurface band</b>\" slider (Isosurface): sets the intensity band the surfaces are\n drawn for, mapped onto the volume's real intensity range.</li>\n <li>\"<b>3D camera</b>\" (Surface / Scatter 3D / Isosurface): zoom <i class=\"pi pi-search\"></i>,\n pan <i class=\"pi pi-arrows-alt\"></i>, orbit <i class=\"pi pi-globe\"></i>,\n turntable <i class=\"pi pi-sync\"></i>, and reset camera <i class=\"pi pi-home\"></i>;\n scroll to zoom the 3D scene.</li>\n </ul>\n\n <h3>Image manipulation tools</h3>\n <ul>\n <li>\"<b>Region Editor</b>\" <img src=\"assets/plotting/region-editor.svg\" width=\"15\" height=\"15\">:\n opens the Region Editor as a floating, draggable dialog \u2014 a per-image table of every region\n (class, colour, area) with bulk select/label/colour/delete and GeoJSON import/export. It stays\n in sync with the image, so you can keep it open while drawing.</li>\n <li>\"<b>Intensity profile lines</b>\" <img src=\"assets/plotting/spline-chart.svg\" width=\"15\" height=\"15\">\n (<i>work in progress \u2014 not yet stable</i>): adds a coloured line ROI; a floating inset\n chart plots intensity along each line, updates live as you drag it, and re-samples at the\n current zoom resolution. Each added line gets its own colour; select a line and use the\n trash tool <i class=\"pi pi-trash\"></i> to remove it (and its trace).</li>\n <li>\"<b>Channels &amp; Histogram</b>\" <img src=\"assets/plotting/invert.svg\" width=\"15\" height=\"15\">:\n opens a floating dialog for brightness/contrast, gamma, and per-channel display, plus the\n <b>colormap / LUT</b> controls (grayscale only) \u2014 a <b>reverse</b> toggle and a colormap\n dropdown (default: inverted greys).</li>\n <li>\"<b>Smoothen</b>\" <img src=\"assets/plotting/smooth.svg\" width=\"15\" height=\"15\">\n (Image view only): the image opens showing <b>raw pixels</b> (nearest-neighbour), so\n zooming in renders crisp blocks for pixel inspection. Toggle this on for bilinear\n smoothing; the icon turns white while smoothing is active.</li>\n </ul>\n\n <h3>Download &amp; view tools</h3>\n <ul>\n <li>\"<b>Download</b>\" <i class=\"pi pi-download\"></i>: download the current image as a PNG.</li>\n <li>\"<b>Fit to view</b>\" / autoscale <i class=\"pi pi-window-maximize\"></i>: resets the image\n to its original dimensions. In stack mode it also resets to single-image mode.</li>\n </ul>\n\n <h3>Zoom tools</h3>\n <ul>\n <li>\"<b>Zoom box</b>\" <img src=\"assets/plotting/zoom-box.svg\" width=\"15\" height=\"15\">\n (<kbd>b</kbd>): drag a box to select an area to zoom into.</li>\n <li>\"<b>Pan</b>\" <i class=\"pi pi-arrows-alt\"></i> (<kbd>p</kbd>): drag to pan the image.</li>\n <li>\"<b>Zoom in</b>\" <i class=\"pi pi-search-plus\"></i> (<kbd>+</kbd>): zoom in one step.</li>\n <li>\"<b>Zoom out</b>\" <i class=\"pi pi-search-minus\"></i> (<kbd>-</kbd>): zoom out one step,\n stopping at the original image dimensions.</li>\n </ul>\n\n <h3>Region tools</h3>\n <p>By default no tool is selected (all greyed-out); selecting a tool highlights it. The\n vertex-editing tools (polygon, add/delete vertex, move, B\u00E9zier conversions) are available\n in the <b>Image</b> view only. On a <b>donut</b> (a region with a hole), the inner-ring\n vertices are editable too \u2014 drag, add, and delete them just like the outer ring.</p>\n <ul>\n <li>\"<b>Selection</b>\" <i class=\"pi pi-arrow-up-right\"></i> (<kbd>s</kbd>): a neutral mode\n that turns off whatever drawing tool was active (wand, vertex eraser, rectangle, etc.).\n Use it to step out of editing without picking another tool.</li>\n <li>\"<b>Rectangular region</b>\" <i class=\"pi pi-stop\"></i> (<kbd>r</kbd>): click and drag to\n create a rectangular region.</li>\n <li>\"<b>Polyline</b>\" <img src=\"assets/plotting/polyline.svg\" width=\"14\" height=\"14\">\n (<kbd>l</kbd>): draw an open (non-closed) polyline by clicking and dragging. Polylines can\n be edited and exported to GeoJSON as a <code>LineString</code> feature.</li>\n <li>\"<b>Freeform region</b>\" <i class=\"pi pi-pencil\"></i> (<kbd>f</kbd>): click and drag to\n draw a closed freeform polygon.</li>\n <li>\"<b>Brush</b>\" <img src=\"assets/plotting/paintbrush.svg\" width=\"15\" height=\"15\">: click\n and drag to paint a region with a circular brush \u2014 the painted stroke is the region.\n A <b>Size</b> slider (brush diameter, in image pixels) appears in the toolbar while the\n brush is active. Hold <kbd>Shift</kbd> while painting to <b>erase</b> (subtract) from a\n region instead. Painting over an existing region extends it; brushing across two regions\n merges them. Painting a closed ring (or shift-erasing an interior pocket) leaves the\n enclosed area empty \u2014 a <b>donut</b> (Image view).</li>\n <li>\"<b>Polygon</b>\" <img src=\"assets/plotting/polygon-vertices.svg\" width=\"15\" height=\"15\">:\n click to place each vertex; click the first point to close the polygon.</li>\n <li>\"<b>Add vertex</b>\" <img src=\"assets/plotting/vertex-add.svg\" width=\"15\" height=\"15\">:\n click an edge of the selected polygon to insert a new vertex.</li>\n <li>\"<b>Delete vertex</b>\" <img src=\"assets/plotting/vertex-delete.svg\" width=\"15\" height=\"15\">:\n click a vertex of the selected polygon to remove it.</li>\n <li>\"<b>Move region</b>\" <img src=\"assets/plotting/region-move.svg\" width=\"15\" height=\"15\">:\n drag to move the selected region.</li>\n <li>\"<b>To B\u00E9zier</b>\" <img src=\"assets/plotting/to-bezier.svg\" width=\"15\" height=\"15\"> /\n \"<b>To polygon</b>\" <img src=\"assets/plotting/to-polygon.svg\" width=\"15\" height=\"15\">:\n convert the selected region to a smooth B\u00E9zier curve, or back to a straight-edged polygon.</li>\n <li>\"<b>Wand</b>\" <img src=\"assets/plotting/wand.svg\" width=\"15\" height=\"15\"> (<kbd>w</kbd>):\n click to grow a region from pixels with similar values around the click point. Hold\n <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> for an exact-match flood fill; drag to keep extending the\n selection while moving; hold <kbd>Shift</kbd> while clicking or dragging on an existing\n region to <b>erase</b> pixels from it. Use the <b>Sens.</b> slider that appears to make the\n wand stricter (higher = smaller selection) or looser. Subsequent clicks keep extending the\n same region; clicking inside an existing region adopts it, and growing into another region\n merges them.</li>\n <li>\"<b>Vertex eraser</b>\" <i class=\"pi pi-eraser\"></i> (<kbd>e</kbd>): click or click-and-drag\n to remove polygon vertices that fall inside the cursor circle from any region, including a\n region's inner rings (donut holes). Use the <b>Radius</b> slider to set the eraser size in\n image pixels. Polygons reduced below 3 vertices (or polylines below 2) are deleted entirely;\n a hole reduced below 3 vertices is removed.</li>\n <li>\"<b>Undo</b>\" <img src=\"assets/plotting/undo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd>): reverts the\n last region action (draw, delete, edit, wand/brush stroke, segmentation, \u2026). Up to the last\n <b>10</b> actions can be undone; the button greys out when there's nothing left to undo.</li>\n <li>\"<b>Redo</b>\" <img src=\"assets/plotting/redo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> or <kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Y</kbd>):\n re-applies the most recently undone action. Performing a new region action clears what can be redone.</li>\n <li>\"<b>Delete region</b>\" <i class=\"pi pi-trash\"></i> (<kbd>Del</kbd>): deletes the selected\n region. This can also be done from the Region Editor.</li>\n </ul>\n\n <h3>Region actions (right-click the image)</h3>\n <p>Right-click a region \u2014 or the current multi-selection \u2014 to open the\n \"<b>Selected region(s)</b>\" group at the top of the context menu. These act immediately on\n the selection (they're not drawing modes) and are fully undo/redo-able:</p>\n <ul>\n <li>\"<b>Select all regions</b>\" <i class=\"pi pi-check-square\"></i>: select every region on\n the image (also in the Region Editor toolbar).</li>\n <li>\"<b>Merge / group</b>\": combine 2+ selected regions into one. If the pieces don't touch,\n the result is a single multi-part region (sparse \"bits\").</li>\n <li>\"<b>Ungroup</b>\": split a multi-part region back into one region per piece.</li>\n <li>\"<b>Inverse</b>\": replace the selection with everything <i>outside</i> it \u2014 the image\n rectangle with the selected regions punched out as holes. Works at any image size.</li>\n <li>\"<b>Simplify</b>\": reduce a region's vertex count (Douglas\u2013Peucker) by an altitude\n threshold in pixels \u2014 pick <b>Light</b> (1&nbsp;px), <b>Medium</b> (3&nbsp;px),\n <b>Strong</b> (8&nbsp;px), or <b>Custom\u2026</b> for a slider. Applies to the exterior and any\n holes (and every part of a multi-part region).</li>\n <li>The B\u00E9zier conversions and Delete also live in this group (the right-click equivalents of\n the toolbar / vertex tools above).</li>\n </ul>\n\n <h3>Segmentation &amp; detection tools</h3>\n <p>AI-assisted segmentation and detection that run entirely in the browser. The first run\n of any model downloads it (a progress toast is shown); it's then cached.</p>\n <p>Two shapes of tool: the <b>prompted</b> ones (Segment, Point prompts, Cellpose) act on\n rectangles you draw, and generated regions inherit the color of the rectangle they came\n from<span *ngIf=\"contributedTools.length\">; the <b>no-prompt</b> ones ({{ contributedToolNames }})\n sweep the whole current view, so there is nothing to draw first</span>.</p>\n <p>Each model dropdown <i class=\"pi pi-chevron-down\"></i> lists the available checkpoints with\n a <i class=\"pi pi-info-circle\"></i> beside each one \u2014 hover it for that model's size, speed,\n training domain and accuracy, so you can pick without leaving the toolbar.</p>\n <ul>\n <li>\"<b>Segment</b>\" <img src=\"assets/plotting/sam-box-prompt.svg\" width=\"15\" height=\"15\">\n (box prompt): draw one or more <b>Rectangular regions</b> around the objects of interest,\n then click Segment. Each rectangle is replaced by the SAM mask of the object inside it.</li>\n <li>\"<b>Point prompts</b>\" <img src=\"assets/plotting/sam-point-prompt.svg\" width=\"15\" height=\"15\">: click directly on an object to\n segment it as a <b>new</b> region. Each click starts a fresh object \u2014 clicking another\n object won't grow the previous one. <kbd>Shift</kbd>/<kbd>Alt</kbd>-click adds an\n <i>exclude</i> point that refines the current object; <kbd>Esc</kbd> undoes it,\n <kbd>Enter</kbd> commits it.</li>\n <li>\"<b>Model picker</b>\" <i class=\"pi pi-chevron-down\"></i>: the dropdown attached to the\n Segment button chooses the SAM model \u2014 <b>micro-sam ViT-T</b> (default; small, fast) or\n <b>micro-sam ViT-B</b> (larger, highest accuracy) for light microscopy, or\n <b>patho-sam ViT-B</b> for histopathology (H&amp;E / stained tissue). All are promptable\n Segment Anything models finetuned for biomedical images; they differ in the <i>image\n encoder</i> (the heavy once-per-image step) and the training domain \u2014 box and point\n prompting behave the same. The choice applies to <i>both</i> the box and point tools, and\n each tool loads its own encoder, so the first click after switching re-encodes once.</li>\n <li>\"<b>Cellpose</b>\" <img src=\"assets/plotting/cells.svg\" width=\"15\" height=\"15\">: draw rectangles, then click Cellpose to\n automatically segment every cell inside each rectangle (client-side cellpose-SAM) \u2014 one\n region per detected cell. No clicking on individual cells needed.</li>\n <!-- Contributed tools describe themselves. This library ships none, so an\n open build's help lists only the prompted tools above \u2014 the text no\n longer promises buttons that are not there. -->\n <ng-container *ngFor=\"let tool of contributedTools\">\n <li *ngIf=\"tool.help\" [innerHTML]=\"tool.help.body\"></li>\n </ng-container>\n </ul>\n\n <h3>Help</h3>\n <p>\"<b>Help</b>\" <i class=\"pi pi-question-circle\"></i>: opens this dialog.</p>\n\n <h3>Keyboard shortcuts</h3>\n <table class=\"shortcuts-table\">\n <tr><td><kbd>+</kbd> / <kbd>=</kbd></td><td>Zoom in</td></tr>\n <tr><td><kbd>-</kbd></td><td>Zoom out</td></tr>\n <tr><td><kbd>b</kbd></td><td>Toggle zoom box mode</td></tr>\n <tr><td><kbd>p</kbd></td><td>Toggle pan mode</td></tr>\n <tr><td><kbd>f</kbd></td><td>Toggle freeform region drawing</td></tr>\n <tr><td><kbd>l</kbd></td><td>Toggle polyline drawing</td></tr>\n <tr><td><kbd>r</kbd></td><td>Toggle rectangular region drawing</td></tr>\n <tr><td><kbd>w</kbd></td><td>Toggle wand tool</td></tr>\n <tr><td>Wand + <kbd>Shift</kbd></td><td>Erase pixels from the active region</td></tr>\n <tr><td>Wand + <kbd>Ctrl</kbd>/<kbd>Cmd</kbd></td><td>Exact-match flood fill (no smoothing)</td></tr>\n <tr><td><kbd>e</kbd></td><td>Toggle vertex eraser</td></tr>\n <tr><td><kbd>s</kbd></td><td>Selection mode (deactivates drawing tools)</td></tr>\n <tr><td>Point prompt + <kbd>Shift</kbd>/<kbd>Alt</kbd></td><td>Add an exclude point (refine the current object)</td></tr>\n <tr><td><kbd>Enter</kbd></td><td>Commit the current SAM point object</td></tr>\n <tr><td><kbd>Esc</kbd></td><td>Undo the current SAM point object</td></tr>\n <tr><td><kbd>Del</kbd> / <kbd>\u232B</kbd></td><td>Delete selected region</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd></td><td>Undo the last region action (up to 10)</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd></td><td>Redo the last undone region action</td></tr>\n </table>\n\n <ng-template pTemplate=\"footer\">\n <p-button icon=\"pi pi-check\" (click)=\"displayHelpDialog=false\" label=\"Ok\" styleClass=\"p-button-text\">\n </p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:block}:host ::ng-deep .p-toolbar{padding:.25rem .5rem}.toolbar-text{color:var(--primary-color);font-size:.875rem}::ng-deep .toolbar-input .p-inputtext{color:var(--primary-color);border-color:var(--primary-color)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-icon-active{filter:brightness(0) invert(1)}::ng-deep .plotmode-item{display:inline-flex;align-items:center;gap:.5rem;color:#000}::ng-deep .plotmode-item i{font-size:1rem;color:var(--primary-color)}::ng-deep .plotmode-item .plotmode-icon-img{width:1.1rem;height:1.1rem;object-fit:contain;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .p-menu.model-menu{width:auto;min-width:12.5rem}::ng-deep .model-menu-item{display:flex;align-items:center;gap:.5rem}::ng-deep .model-menu-item .p-menuitem-text{flex:1 1 auto;white-space:nowrap}::ng-deep .model-menu-item .model-info-icon{flex:0 0 auto;font-size:.875rem;opacity:.55;cursor:help;appearance:none;background:none;border:0;padding:0;margin:0;color:inherit;line-height:1}::ng-deep .model-menu-item .model-info-icon:hover{opacity:1}::ng-deep .model-menu-item .model-info-icon:focus-visible{opacity:1;outline:2px solid var(--primary-color, #2196f3);outline-offset:2px;border-radius:2px}::ng-deep .model-info-tooltip .p-tooltip-text{min-width:18rem;max-width:24rem;line-height:1.4}::ng-deep .wand-sensitivity-slider{width:8rem;align-self:center;margin:0 .25rem}.wand-sensitivity-value{min-width:2rem;text-align:right}span.colormap{width:50px;height:auto;display:inline-block}img.colormap{width:50px;height:auto}.colormap{background:no-repeat;background-size:100%;vertical-align:middle}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.shortcuts-table{border-collapse:collapse}.shortcuts-table td{padding:.25rem .75rem .25rem 0;vertical-align:middle}.shortcuts-table td:first-child{white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: "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: "component", type: i3.Toolbar, selector: "p-toolbar", inputs: ["style", "styleClass", "ariaLabelledBy"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i5.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i6.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "appendTo", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }, { kind: "directive", type: i7.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: i8.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: "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: "component", type: i10.InputNumber, selector: "p-inputNumber", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "component", type: i11.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "visible", "style", "position"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i12.Ripple, selector: "[pRipple]" }] }); }
238
275
  }
239
276
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: ToolbarComponent, decorators: [{
240
277
  type: Component,
241
- args: [{ selector: 'plotting-toolbar', template: "<p-toolbar>\n <div class=\"p-toolbar-group-start flex-wrap align-items-center gap-1\">\n\n <!-- Host-provided custom toolbar buttons, shown first (e.g. app-specific\n actions the visualization view itself doesn't own). -->\n <ng-content></ng-content>\n\n <!-- Plot type: available for any image (grayscale or RGB). -->\n <ng-container *ngIf=\"imageInfo && tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-dropdown [options]=\"plotTypeOptions\" optionLabel=\"label\" optionValue=\"type\"\n [(ngModel)]=\"selectedPlotType\" appendTo=\"body\"\n pTooltip=\"Select plot type\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '11rem'}\"\n styleClass=\"plotmode-dropdown\" panelStyleClass=\"plotmode-panel\"\n (onChange)=\"selectPlotType.emit($event.value)\">\n <ng-template let-opt pTemplate=\"selectedItem\">\n <span class=\"plotmode-item\" *ngIf=\"opt\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n <ng-template let-opt pTemplate=\"item\">\n <span class=\"plotmode-item\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n </p-dropdown>\n <ng-container *ngIf=\"isIsosurfaceMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Iso</span>\n <p-slider [(ngModel)]=\"isoRange\" [range]=\"true\"\n [min]=\"isoValueMin\" [max]=\"isoValueMax\" [step]=\"isoValueStep\"\n (onChange)=\"isoRangeChange.emit($event.values)\"\n pTooltip=\"Isosurface band (0\u2013255), mapped onto the volume's\n actual intensity range. Surfaces are drawn for voxels whose\n intensity falls in the selected band \u2014 narrow it to isolate\n features, widen it to see the whole structure.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ isoRange[0] }}\u2013{{ isoRange[1] }}</span>\n </ng-container>\n <!-- Intensity tools (Heatmap / Image mode): add another colored line ROI.\n Each added line gets the next bright palette colour and its own inset\n trace; select a line on the image and use the trash tool to remove it\n (and its trace). The lines are Region-based but excluded from the\n Region Editor and exports. -->\n <ng-container *ngIf=\"isIntensityCapable\">\n <span class=\"toolbar-separator\"></span>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Add an intensity line (new colour). Click a line to select it,\n then use the trash tool to remove it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"addProfileLine.emit()\">\n <img src=\"assets/plotting/spline-chart.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n </ng-container>\n <!-- Channels & Histogram: brightness/contrast, gamma, per-channel display,\n colormap/reverse/invert \u2014 opens the floating dialog. -->\n <p-button *ngIf=\"tools.specialTools\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Channels &amp; Histogram (brightness/contrast, colormap)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openChannelHistogram.emit()\">\n <img src=\"assets/plotting/invert.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Smoothen toggle: bilinear smoothing vs nearest-neighbour raw pixels (Image\n view only \u2014 it's an OpenSeadragon drawer setting). Highlighted when smoothing on. -->\n <p-button *ngIf=\"tools.specialTools && isImageView\" size=\"small\"\n [text]=\"!imageSmoothingEnabled\"\n pTooltip=\"Smoothen (turn on image smoothing)\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleImageSmoothing.emit()\">\n <img src=\"assets/plotting/smooth.svg\" style=\"width: 1rem\"\n [class]=\"imageSmoothingEnabled ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"/>\n </p-button>\n\n <!-- Stack controls -->\n <ng-container *ngIf=\"imageInfo?.isStack\">\n <span class=\"toolbar-separator\"></span>\n <!-- Image view: scrub z-slices live (the renderer swaps slices in place). -->\n <ng-container *ngIf=\"showsLiveSliceScrubber\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <p-slider [(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onChange)=\"zScrub.emit($event.value)\" (onSlideEnd)=\"zSlide.emit($event.value)\"\n pTooltip=\"Slice through the stack (\u2190 / \u2192 keys)\" tooltipPosition=\"bottom\"\n [style]=\"{'width':'16rem'}\" styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text\">{{zIndex}} of {{maxIndex}}</span>\n </ng-container>\n <!-- Heatmap view: single-image (high-def zoom) vs stack (frame slider). Plotly-only \u2014\n hidden for napari, which has no single/stack toggle or per-frame fetch. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode\">\n <p-button icon=\"pi pi-image\" size=\"small\"\n [text]=\"imageInfo?.showStack\"\n pTooltip=\"Single image mode: enables high-def zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[0])\">\n </p-button>\n <p-button icon=\"pi pi-images\" size=\"small\"\n [text]=\"!imageInfo?.showStack\"\n pTooltip=\"Stack mode, enables slicing through image frames\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[1])\">\n </p-button>\n <ng-container *ngIf=\"!imageInfo?.showStack\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <div class=\"p-inputgroup\" style=\"width: auto\">\n <p-inputNumber styleClass=\"w-3rem toolbar-input\"\n\t\t\t\t\tpTooltip=\"View image in stack (&#x23CE;)\"\n\t\t\t\t\t[(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onInput)=\"zIndexInput.emit(zIndex)\" (keydown.enter)=\"reloadAndPlot.emit()\"/>\n <p-button icon=\"pi pi-refresh\" size=\"small\" [text]=\"true\" (onClick)=\"reloadAndPlot.emit()\"\n pTooltip=\"Plot this slice\" tooltipPosition=\"bottom\">\n </p-button>\n </div>\n <span class=\"flex align-items-center mx-1 toolbar-text\">of {{maxIndex}}</span>\n </ng-container>\n </ng-container>\n </ng-container>\n\n <!-- Image actions (special tools) -->\n <ng-container *ngIf=\"tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-download\" size=\"small\" [text]=\"true\"\n pTooltip=\"Download image as PNG\" tooltipPosition=\"bottom\"\n (onClick)=\"downloadImage.emit()\">\n </p-button>\n <p-button *ngIf=\"isHeatmap\" icon=\"pi pi-window-maximize\" size=\"small\" [text]=\"true\"\n pTooltip=\"Fit to view\" tooltipPosition=\"bottom\"\n (onClick)=\"autoscaleImage.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Zoom tools. Plotly rubber-band drag-zoom: on OpenSeadragon the box-zoom\n covers this and a bare drag pans natively, so it's hidden in image view. -->\n <ng-container *ngIf=\"isHeatmap && tools.zoomTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button *ngIf=\"!isImageView\" icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeDragMode !== 'zoom'\"\n pTooltip=\"Zoom drag\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoom')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'zoomToBox'\"\n pTooltip=\"Zoom box (<kbd>b</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoomToBox')\">\n <img src=\"assets/plotting/zoom-box.svg\"\n [class]=\"activeDragMode === 'zoomToBox' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeDragMode !== 'pan'\"\n pTooltip=\"Pan the image (<kbd>p</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-search-plus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom in (<kbd>+</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomIn.emit()\">\n </p-button>\n <p-button icon=\"pi pi-search-minus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom out (<kbd>-</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomOut.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Surface 3D controls -->\n <ng-container *ngIf=\"!isHeatmap\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'zoom'\"\n pTooltip=\"Zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('zoom')\">\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'pan'\"\n pTooltip=\"Pan\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-globe\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'orbit'\"\n pTooltip=\"Orbital rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('orbit')\">\n </p-button>\n <p-button icon=\"pi pi-sync\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'turntable'\"\n pTooltip=\"Turntable rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('turntable')\">\n </p-button>\n <p-button icon=\"pi pi-home\" size=\"small\" [text]=\"true\"\n pTooltip=\"Reset camera to default\" tooltipPosition=\"bottom\"\n (onClick)=\"resetSurfaceCamera.emit()\">\n </p-button>\n <!-- 3D coordinate-axes / scale gizmo toggle (napari-js WebGPU volume/isosurface only).\n `p-button-icon-only` gives the same square sizing as the neighbouring pi-icon buttons\n (the projected <img> doesn't trigger PrimeNG's automatic icon-only sizing). -->\n <p-button size=\"small\" *ngIf=\"isNapariMode\" styleClass=\"p-button-icon-only\"\n [text]=\"!axesVisible\"\n pTooltip=\"Toggle 3D axes / scale\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleAxes.emit()\">\n <img src=\"assets/plotting/3d-coordinates.svg\"\n [class]=\"axesVisible ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <!-- Surface wireframe toggle (napari-js WebGPU surface only). -->\n <p-button icon=\"pi pi-share-alt\" size=\"small\" *ngIf=\"isNapariSurfaceMode\"\n [text]=\"!wireframeActive\"\n pTooltip=\"Toggle wireframe\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleWireframe.emit()\">\n </p-button>\n <!-- Resolution (decimate factor) for the napari 3D types \u2014 reloads at coarser/finer sampling. -->\n <ng-container *ngIf=\"isNapari3dMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Res</span>\n <p-dropdown [options]=\"decimateOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"resolutionScale\" appendTo=\"body\"\n pTooltip=\"Resolution (decimate factor)\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '6rem'}\"\n (onChange)=\"selectResolution.emit($event.value)\">\n </p-dropdown>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"isHeatmap && tools.regionTools\">\n <span class=\"toolbar-separator\"></span>\n <!-- Region Editor: opens the editor as a floating dialog (formerly the\n right-panel Regions tab). Sits with the region drawing tools. -->\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Open the Region Editor\" tooltipPosition=\"bottom\"\n (onClick)=\"openRegionEditor.emit()\">\n <img src=\"assets/plotting/region-editor.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <!-- Region drawing tools -->\n <p-button icon=\"pi pi-arrow-up-right\" size=\"small\"\n [text]=\"activeDragMode !== 'select'\"\n pTooltip=\"Selection mode (<kbd>s</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('select')\">\n </p-button>\n <p-button icon=\"pi pi-stop\" size=\"small\"\n [text]=\"activeDragMode !== 'drawrect'\"\n pTooltip=\"Draw a rectangular region (<kbd>r</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawrect')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawopenpath'\"\n pTooltip=\"Draw a polyline (<kbd>l</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawopenpath')\">\n <img src=\"assets/plotting/polyline.svg\"\n [class]=\"activeDragMode === 'drawopenpath' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-pencil\" size=\"small\"\n [text]=\"activeDragMode !== 'drawclosedpath'\"\n pTooltip=\"Draw a freeform region (<kbd>f</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawclosedpath')\">\n </p-button>\n <!-- Brush: paint a region by dragging a disc of the given size; the Size\n slider appears while it's active. Hold Shift to erase (subtract). -->\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'brush'\"\n pTooltip=\"Brush: click and drag to paint a region.\n <br>Hold <kbd>Shift</kbd> while painting to erase from a region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('brush')\">\n <img src=\"assets/plotting/paintbrush.svg\"\n [class]=\"activeDragMode === 'brush' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'brush'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Size</span>\n <p-slider [(ngModel)]=\"brushSize\"\n [min]=\"brushSizeMin\" [max]=\"brushSizeMax\" [step]=\"brushSizeStep\"\n (onChange)=\"brushSizeChange.emit($event.value)\"\n pTooltip=\"Brush diameter in image pixels.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ brushSize }}</span>\n </ng-container>\n\n <!-- Vertex editing \u2014 backends with a vertex-editing overlay (OSD + napari-js image). -->\n <ng-container *ngIf=\"supportsRegionVertexTools\">\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawpolygon'\"\n pTooltip=\"Polygon: click to place each vertex; click the first point to close\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawpolygon')\">\n <img src=\"assets/plotting/polygon-vertices.svg\"\n [class]=\"activeDragMode === 'drawpolygon' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'addpoint'\"\n pTooltip=\"Add a vertex: click an edge of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('addpoint')\">\n <img src=\"assets/plotting/vertex-add.svg\"\n [class]=\"activeDragMode === 'addpoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'deletepoint'\"\n pTooltip=\"Delete a vertex: click a vertex of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('deletepoint')\">\n <img src=\"assets/plotting/vertex-delete.svg\"\n [class]=\"activeDragMode === 'deletepoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'wand'\"\n pTooltip=\"Wand: click and drag to grow a region by similar pixel values (<kbd>w</kbd>).\n <br>Hold <kbd>Shift</kbd> while clicking or dragging on a region to erase pixels from it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('wand')\">\n <img src=\"assets/plotting/wand.svg\"\n [class]=\"activeDragMode === 'wand' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'wand'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Sens.</span>\n <p-slider [(ngModel)]=\"wandSensitivity\"\n [min]=\"wandSensitivityMin\" [max]=\"wandSensitivityMax\" [step]=\"wandSensitivityStep\"\n (onChange)=\"wandSensitivityChange.emit($event.value)\"\n pTooltip=\"Higher = stricter (smaller selection); lower = looser.\n Takes effect from the next click/drag tick.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ wandSensitivity.toFixed(1) }}</span>\n </ng-container>\n <p-button icon=\"pi pi-eraser\" size=\"small\"\n [text]=\"activeDragMode !== 'eraseVertex'\"\n pTooltip=\"Vertex eraser (<kbd>e</kbd>): click or drag to remove polygon vertices inside\n the cursor circle from any region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('eraseVertex')\">\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'eraseVertex'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Radius</span>\n <p-slider [(ngModel)]=\"vertexEraserRadius\"\n [min]=\"vertexEraserRadiusMin\" [max]=\"vertexEraserRadiusMax\" [step]=\"vertexEraserRadiusStep\"\n (onChange)=\"vertexEraserRadiusChange.emit($event.value)\"\n pTooltip=\"Eraser radius in image pixels.\n Vertices within this distance of the cursor are removed.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ vertexEraserRadius }}</span>\n </ng-container>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canUndo\"\n pTooltip=\"Undo the last region action (<kbd>Ctrl</kbd>+<kbd>Z</kbd>).\n Up to 10 actions can be undone.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"undoRegion.emit()\">\n <img src=\"assets/plotting/undo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canRedo\"\n pTooltip=\"Redo the last undone region action\n (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd>).\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"redoRegion.emit()\">\n <img src=\"assets/plotting/redo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-trash\" size=\"small\" [text]=\"true\"\n pTooltip=\"Delete selected region (<kbd>Del</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"deleteRegion.emit()\">\n </p-button>\n <!-- SAM (jit-ui#90): box-prompt segment, interactive point prompts, and a\n model picker. -->\n <span class=\"toolbar-separator\"></span>\n <!-- SAM controls grouped: box-prompt Segment, then the point-prompt tool,\n then the model-picker dropdown (the chevron) which governs BOTH tools.\n Falls back to Segment + point with no model picker when none are set. -->\n <span *ngIf=\"samModels.length; else segmentPlain\" class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/picture-segmentation.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the SAM model (used by both segment tools)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"samModelMenu.toggle($event)\">\n </p-button>\n </span>\n <ng-template #segmentPlain>\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/picture-segmentation.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n </span>\n </ng-template>\n <!-- Point-prompt tool \u2014 shared between the models / no-models layouts so it\n always sits just left of the model dropdown. -->\n <ng-template #samPointBtn>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'samPoint'\"\n pTooltip=\"SAM point prompts: click a fiber to segment it as a new region;\n Shift/Alt-click to refine (exclude) the current one.<br>Esc undoes it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('samPoint')\">\n <img src=\"assets/plotting/click.svg\"\n [class]=\"activeDragMode === 'samPoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-template>\n <p-menu #samModelMenu [model]=\"samMenuItems\" [popup]=\"true\" appendTo=\"body\"></p-menu>\n <!-- Help: explains the ViT-T vs ViT-B trade-off behind the model picker. -->\n <p-button *ngIf=\"samModels.length\" icon=\"pi pi-info-circle\" size=\"small\" [text]=\"true\"\n pTooltip=\"About the SAM models\" tooltipPosition=\"bottom\"\n (onClick)=\"samModelInfo.toggle($event)\">\n </p-button>\n <p-overlayPanel #samModelInfo appendTo=\"body\" styleClass=\"sam-model-info\">\n <div style=\"max-width: 24rem; line-height: 1.4\">\n <div class=\"font-bold mb-1\">SAM models</div>\n <p class=\"mt-0 mb-2\">\n All are promptable Segment Anything models finetuned for biomedical images. They differ\n in the <i>image encoder</i> (the heavy once-per-image step) and the training domain \u2014\n box and point prompting behave the same.\n </p>\n <div class=\"mb-2\">\n <div class=\"font-bold\">micro-sam ViT-T <span class=\"font-normal\">(default)</span></div>\n <div>Light-microscopy finetuned. TinyViT encoder, ~14&nbsp;MB, very fast, runs on WASM.\n Good mask quality \u2014 best for quick interactive work and many/large objects.</div>\n </div>\n <div class=\"mb-2\">\n <div class=\"font-bold\">micro-sam ViT-B</div>\n <div>Light-microscopy finetuned. ViT-Base encoder, ~172&nbsp;MB, slower, GPU-accelerated\n (WebGPU). Highest accuracy \u2014 when ViT-T misses subtle or overlapping boundaries.</div>\n </div>\n <div class=\"mb-2\">\n <div class=\"font-bold\">patho-sam ViT-B</div>\n <div>Histopathology finetuned (H&amp;E / stained tissue). ViT-Base encoder, GPU-accelerated.\n Use it for pathology slides \u2014 it segments tissue/nuclei better than the microscopy models.</div>\n </div>\n <div class=\"mb-2\">\n <div class=\"font-bold\">patho-sam ViT-B (int8)</div>\n <div>Same model, int8-quantized \u2014 ~100&nbsp;MB vs ~180&nbsp;MB (smaller download). Runs on\n WASM (slower encode than the fp16 version); masks are near-identical. Pick it on slow\n connections.</div>\n </div>\n <div class=\"text-sm\" style=\"opacity: 0.75\">\n The choice applies to both the box and point tools. Each tool loads its own encoder,\n so the first click after switching re-encodes once.\n </div>\n </div>\n </p-overlayPanel>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Auto-segment cells inside each drawn rectangle (cellpose-SAM, client-side)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentCellpose.emit()\">\n <img src=\"assets/plotting/cells.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- YOLO: detect objects across the current view. Unlike the SAM and\n cellpose tools it takes no prompt \u2014 a detector finds objects in a\n field rather than being pointed at one \u2014 so there is nothing to draw\n first. Split button: run, pick checkpoint, open parameters. -->\n <span *ngIf=\"yoloModels.length\" class=\"p-buttonset segment-split\">\n <p-button icon=\"pi pi-microchip-ai\" size=\"small\" [text]=\"true\"\n pTooltip=\"Detect objects in the current view with YOLO (client-side)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"detectYolo.emit()\">\n </p-button>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the YOLO model\"\n tooltipPosition=\"bottom\"\n (onClick)=\"yoloModelMenu.toggle($event)\">\n </p-button>\n <p-button icon=\"pi pi-sliders-h\" size=\"small\" [text]=\"true\"\n pTooltip=\"YOLO parameters\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openYoloParams.emit()\">\n </p-button>\n </span>\n <p-menu #yoloModelMenu [model]=\"yoloMenuItems\" [popup]=\"true\" appendTo=\"body\"></p-menu>\n\n <!-- Retinal layers: semantic segmentation of the current view. Same\n no-prompt shape as YOLO \u2014 it labels every pixel rather than being\n pointed at an object \u2014 so the same split button: run, pick\n checkpoint, open parameters. -->\n <span *ngIf=\"retinalModels.length\" class=\"p-buttonset segment-split\">\n <p-button icon=\"pi pi-align-left\" size=\"small\" [text]=\"true\"\n pTooltip=\"Segment retinal layers in the current view (client-side)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRetinal.emit()\">\n </p-button>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the retinal layer model\"\n tooltipPosition=\"bottom\"\n (onClick)=\"retinalModelMenu.toggle($event)\">\n </p-button>\n <p-button icon=\"pi pi-sliders-h\" size=\"small\" [text]=\"true\"\n pTooltip=\"Retinal layer parameters\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openRetinalParams.emit()\">\n </p-button>\n </span>\n <p-menu #retinalModelMenu [model]=\"retinalMenuItems\" [popup]=\"true\" appendTo=\"body\"></p-menu>\n </ng-container>\n\n <ng-container *ngIf=\"tools.help\">\n <span class=\"toolbar-separator\"></span>\n\n <!-- Help -->\n <p-button icon=\"pi pi-question-circle\" size=\"small\" [text]=\"true\"\n pTooltip=\"Help\" tooltipPosition=\"bottom\"\n (onClick)=\"showHelp()\">\n </p-button>\n </ng-container>\n\n </div>\n</p-toolbar>\n<p-dialog header=\"Plotting help\" [(visible)]=\"displayHelpDialog\" [modal]=\"true\" [style]=\"{width: '50vw'}\"\n [maximizable]=\"true\" [draggable]=\"false\" [resizable]=\"false\">\n <p>Click a file in the left-hand tree to display it. By default it opens in the\n <b>Image</b> view \u2014 a natively tiled, deeply zoomable raster (OpenSeadragon) with a\n navigator minimap and a physical-units scale bar. <b>Click the image to zoom</b> toward\n the clicked point; scroll to zoom and drag to pan. When a stack is open, a slice slider\n (Image view) or image-mode toggle (other views) appears. Regions can also be edited in the\n <b>Region Editor</b>, opened with the\n <img src=\"assets/plotting/region-editor.svg\" width=\"14\" height=\"14\" alt=\"Region Editor\"/>\n toolbar button as a floating dialog. Drag the <i class=\"pi pi-bars\"></i> handle to detach\n this toolbar into a floating window.\n </p>\n\n <h3>Plot-type dropdown</h3>\n <p>\n The <b>plot-type dropdown</b> switches how the file is visualized:\n <b>Image</b>, <b>Contour</b>, <b>Scatter (regions)</b>, <b>Surface (3D)</b>,\n <b>Scatter 3D</b>, and <b>Isosurface (3D)</b>. Scalar and 3D types require a grayscale\n image (the 3D volume types also require a z-stack). The non-image views render with\n Plotly and support \"real zooming\" (a downscaled overview that re-fetches higher\n resolution as you zoom in).\n </p>\n <ul>\n <li>\"<b>Isosurface band</b>\" slider (Isosurface): sets the intensity band the surfaces are\n drawn for, mapped onto the volume's real intensity range.</li>\n <li>\"<b>3D camera</b>\" (Surface / Scatter 3D / Isosurface): zoom <i class=\"pi pi-search\"></i>,\n pan <i class=\"pi pi-arrows-alt\"></i>, orbit <i class=\"pi pi-globe\"></i>,\n turntable <i class=\"pi pi-sync\"></i>, and reset camera <i class=\"pi pi-home\"></i>;\n scroll to zoom the 3D scene.</li>\n </ul>\n\n <h3>Image manipulation tools</h3>\n <ul>\n <li>\"<b>Region Editor</b>\" <img src=\"assets/plotting/region-editor.svg\" width=\"15\" height=\"15\">:\n opens the Region Editor as a floating, draggable dialog \u2014 a per-image table of every region\n (class, colour, area) with bulk select/label/colour/delete and GeoJSON import/export. It stays\n in sync with the image, so you can keep it open while drawing.</li>\n <li>\"<b>Intensity profile lines</b>\" <img src=\"assets/plotting/spline-chart.svg\" width=\"15\" height=\"15\">\n (<i>work in progress \u2014 not yet stable</i>): adds a coloured line ROI; a floating inset\n chart plots intensity along each line, updates live as you drag it, and re-samples at the\n current zoom resolution. Each added line gets its own colour; select a line and use the\n trash tool <i class=\"pi pi-trash\"></i> to remove it (and its trace).</li>\n <li>\"<b>Channels &amp; Histogram</b>\" <img src=\"assets/plotting/invert.svg\" width=\"15\" height=\"15\">:\n opens a floating dialog for brightness/contrast, gamma, and per-channel display, plus the\n <b>colormap / LUT</b> controls (grayscale only) \u2014 a <b>reverse</b> toggle and a colormap\n dropdown (default: inverted greys).</li>\n <li>\"<b>Smoothen</b>\" <img src=\"assets/plotting/smooth.svg\" width=\"15\" height=\"15\">\n (Image view only): the image opens showing <b>raw pixels</b> (nearest-neighbour), so\n zooming in renders crisp blocks for pixel inspection. Toggle this on for bilinear\n smoothing; the icon turns white while smoothing is active.</li>\n </ul>\n\n <h3>Download &amp; view tools</h3>\n <ul>\n <li>\"<b>Download</b>\" <i class=\"pi pi-download\"></i>: download the current image as a PNG.</li>\n <li>\"<b>Fit to view</b>\" / autoscale <i class=\"pi pi-window-maximize\"></i>: resets the image\n to its original dimensions. In stack mode it also resets to single-image mode.</li>\n </ul>\n\n <h3>Zoom tools</h3>\n <ul>\n <li>\"<b>Zoom box</b>\" <img src=\"assets/plotting/zoom-box.svg\" width=\"15\" height=\"15\">\n (<kbd>b</kbd>): drag a box to select an area to zoom into.</li>\n <li>\"<b>Pan</b>\" <i class=\"pi pi-arrows-alt\"></i> (<kbd>p</kbd>): drag to pan the image.</li>\n <li>\"<b>Zoom in</b>\" <i class=\"pi pi-search-plus\"></i> (<kbd>+</kbd>): zoom in one step.</li>\n <li>\"<b>Zoom out</b>\" <i class=\"pi pi-search-minus\"></i> (<kbd>-</kbd>): zoom out one step,\n stopping at the original image dimensions.</li>\n </ul>\n\n <h3>Region tools</h3>\n <p>By default no tool is selected (all greyed-out); selecting a tool highlights it. The\n vertex-editing tools (polygon, add/delete vertex, move, B\u00E9zier conversions) are available\n in the <b>Image</b> view only. On a <b>donut</b> (a region with a hole), the inner-ring\n vertices are editable too \u2014 drag, add, and delete them just like the outer ring.</p>\n <ul>\n <li>\"<b>Selection</b>\" <i class=\"pi pi-arrow-up-right\"></i> (<kbd>s</kbd>): a neutral mode\n that turns off whatever drawing tool was active (wand, vertex eraser, rectangle, etc.).\n Use it to step out of editing without picking another tool.</li>\n <li>\"<b>Rectangular region</b>\" <i class=\"pi pi-stop\"></i> (<kbd>r</kbd>): click and drag to\n create a rectangular region.</li>\n <li>\"<b>Polyline</b>\" <img src=\"assets/plotting/polyline.svg\" width=\"14\" height=\"14\">\n (<kbd>l</kbd>): draw an open (non-closed) polyline by clicking and dragging. Polylines can\n be edited and exported to GeoJSON as a <code>LineString</code> feature.</li>\n <li>\"<b>Freeform region</b>\" <i class=\"pi pi-pencil\"></i> (<kbd>f</kbd>): click and drag to\n draw a closed freeform polygon.</li>\n <li>\"<b>Brush</b>\" <img src=\"assets/plotting/paintbrush.svg\" width=\"15\" height=\"15\">: click\n and drag to paint a region with a circular brush \u2014 the painted stroke is the region.\n A <b>Size</b> slider (brush diameter, in image pixels) appears in the toolbar while the\n brush is active. Hold <kbd>Shift</kbd> while painting to <b>erase</b> (subtract) from a\n region instead. Painting over an existing region extends it; brushing across two regions\n merges them. Painting a closed ring (or shift-erasing an interior pocket) leaves the\n enclosed area empty \u2014 a <b>donut</b> (Image view).</li>\n <li>\"<b>Polygon</b>\" <img src=\"assets/plotting/polygon-vertices.svg\" width=\"15\" height=\"15\">:\n click to place each vertex; click the first point to close the polygon.</li>\n <li>\"<b>Add vertex</b>\" <img src=\"assets/plotting/vertex-add.svg\" width=\"15\" height=\"15\">:\n click an edge of the selected polygon to insert a new vertex.</li>\n <li>\"<b>Delete vertex</b>\" <img src=\"assets/plotting/vertex-delete.svg\" width=\"15\" height=\"15\">:\n click a vertex of the selected polygon to remove it.</li>\n <li>\"<b>Move region</b>\" <img src=\"assets/plotting/region-move.svg\" width=\"15\" height=\"15\">:\n drag to move the selected region.</li>\n <li>\"<b>To B\u00E9zier</b>\" <img src=\"assets/plotting/to-bezier.svg\" width=\"15\" height=\"15\"> /\n \"<b>To polygon</b>\" <img src=\"assets/plotting/to-polygon.svg\" width=\"15\" height=\"15\">:\n convert the selected region to a smooth B\u00E9zier curve, or back to a straight-edged polygon.</li>\n <li>\"<b>Wand</b>\" <img src=\"assets/plotting/wand.svg\" width=\"15\" height=\"15\"> (<kbd>w</kbd>):\n click to grow a region from pixels with similar values around the click point. Hold\n <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> for an exact-match flood fill; drag to keep extending the\n selection while moving; hold <kbd>Shift</kbd> while clicking or dragging on an existing\n region to <b>erase</b> pixels from it. Use the <b>Sens.</b> slider that appears to make the\n wand stricter (higher = smaller selection) or looser. Subsequent clicks keep extending the\n same region; clicking inside an existing region adopts it, and growing into another region\n merges them.</li>\n <li>\"<b>Vertex eraser</b>\" <i class=\"pi pi-eraser\"></i> (<kbd>e</kbd>): click or click-and-drag\n to remove polygon vertices that fall inside the cursor circle from any region, including a\n region's inner rings (donut holes). Use the <b>Radius</b> slider to set the eraser size in\n image pixels. Polygons reduced below 3 vertices (or polylines below 2) are deleted entirely;\n a hole reduced below 3 vertices is removed.</li>\n <li>\"<b>Undo</b>\" <img src=\"assets/plotting/undo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd>): reverts the\n last region action (draw, delete, edit, wand/brush stroke, segmentation, \u2026). Up to the last\n <b>10</b> actions can be undone; the button greys out when there's nothing left to undo.</li>\n <li>\"<b>Redo</b>\" <img src=\"assets/plotting/redo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> or <kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Y</kbd>):\n re-applies the most recently undone action. Performing a new region action clears what can be redone.</li>\n <li>\"<b>Delete region</b>\" <i class=\"pi pi-trash\"></i> (<kbd>Del</kbd>): deletes the selected\n region. This can also be done from the Region Editor.</li>\n </ul>\n\n <h3>Region actions (right-click the image)</h3>\n <p>Right-click a region \u2014 or the current multi-selection \u2014 to open the\n \"<b>Selected region(s)</b>\" group at the top of the context menu. These act immediately on\n the selection (they're not drawing modes) and are fully undo/redo-able:</p>\n <ul>\n <li>\"<b>Select all regions</b>\" <i class=\"pi pi-check-square\"></i>: select every region on\n the image (also in the Region Editor toolbar).</li>\n <li>\"<b>Merge / group</b>\": combine 2+ selected regions into one. If the pieces don't touch,\n the result is a single multi-part region (sparse \"bits\").</li>\n <li>\"<b>Ungroup</b>\": split a multi-part region back into one region per piece.</li>\n <li>\"<b>Inverse</b>\": replace the selection with everything <i>outside</i> it \u2014 the image\n rectangle with the selected regions punched out as holes. Works at any image size.</li>\n <li>\"<b>Simplify</b>\": reduce a region's vertex count (Douglas\u2013Peucker) by an altitude\n threshold in pixels \u2014 pick <b>Light</b> (1&nbsp;px), <b>Medium</b> (3&nbsp;px),\n <b>Strong</b> (8&nbsp;px), or <b>Custom\u2026</b> for a slider. Applies to the exterior and any\n holes (and every part of a multi-part region).</li>\n <li>The B\u00E9zier conversions and Delete also live in this group (the right-click equivalents of\n the toolbar / vertex tools above).</li>\n </ul>\n\n <h3>Segmentation tools (SAM &amp; cellpose)</h3>\n <p>AI-assisted segmentation that runs entirely in the browser. The box and point\n tools use a promptable <b>micro-sam</b> model; the cellpose tool auto-detects cells.\n Generated regions inherit the color of the rectangle they came from. The first run\n downloads the model (a progress toast is shown); it's then cached.</p>\n <ul>\n <li>\"<b>Segment</b>\" <img src=\"assets/plotting/picture-segmentation.svg\" width=\"15\" height=\"15\">\n (box prompt): draw one or more <b>Rectangular regions</b> around the objects of interest,\n then click Segment. Each rectangle is replaced by the SAM mask of the object inside it.</li>\n <li>\"<b>Point prompts</b>\" <img src=\"assets/plotting/click.svg\" width=\"15\" height=\"15\">: click directly on an object to\n segment it as a <b>new</b> region. Each click starts a fresh object \u2014 clicking another\n object won't grow the previous one. <kbd>Shift</kbd>/<kbd>Alt</kbd>-click adds an\n <i>exclude</i> point that refines the current object; <kbd>Esc</kbd> undoes it,\n <kbd>Enter</kbd> commits it.</li>\n <li>\"<b>Model picker</b>\" <i class=\"pi pi-chevron-down\"></i>: the dropdown attached to the\n Segment button chooses the SAM model \u2014 <b>micro-sam ViT-T</b> (default; small, fast) or\n <b>micro-sam ViT-B</b> (larger, highest accuracy) for light microscopy, or\n <b>patho-sam ViT-B</b> for histopathology (H&amp;E / stained tissue). The choice applies to\n <i>both</i> the box and point tools. The <i class=\"pi pi-info-circle\"></i> info button next\n to it explains the trade-off.</li>\n <li>\"<b>Cellpose</b>\" <img src=\"assets/plotting/cells.svg\" width=\"15\" height=\"15\">: draw rectangles, then click Cellpose to\n automatically segment every cell inside each rectangle (client-side cellpose-SAM) \u2014 one\n region per detected cell. No clicking on individual cells needed.</li>\n </ul>\n\n <h3>Help</h3>\n <p>\"<b>Help</b>\" <i class=\"pi pi-question-circle\"></i>: opens this dialog.</p>\n\n <h3>Keyboard shortcuts</h3>\n <table class=\"shortcuts-table\">\n <tr><td><kbd>+</kbd> / <kbd>=</kbd></td><td>Zoom in</td></tr>\n <tr><td><kbd>-</kbd></td><td>Zoom out</td></tr>\n <tr><td><kbd>b</kbd></td><td>Toggle zoom box mode</td></tr>\n <tr><td><kbd>p</kbd></td><td>Toggle pan mode</td></tr>\n <tr><td><kbd>f</kbd></td><td>Toggle freeform region drawing</td></tr>\n <tr><td><kbd>l</kbd></td><td>Toggle polyline drawing</td></tr>\n <tr><td><kbd>r</kbd></td><td>Toggle rectangular region drawing</td></tr>\n <tr><td><kbd>w</kbd></td><td>Toggle wand tool</td></tr>\n <tr><td>Wand + <kbd>Shift</kbd></td><td>Erase pixels from the active region</td></tr>\n <tr><td>Wand + <kbd>Ctrl</kbd>/<kbd>Cmd</kbd></td><td>Exact-match flood fill (no smoothing)</td></tr>\n <tr><td><kbd>e</kbd></td><td>Toggle vertex eraser</td></tr>\n <tr><td><kbd>s</kbd></td><td>Selection mode (deactivates drawing tools)</td></tr>\n <tr><td>Point prompt + <kbd>Shift</kbd>/<kbd>Alt</kbd></td><td>Add an exclude point (refine the current object)</td></tr>\n <tr><td><kbd>Enter</kbd></td><td>Commit the current SAM point object</td></tr>\n <tr><td><kbd>Esc</kbd></td><td>Undo the current SAM point object</td></tr>\n <tr><td><kbd>Del</kbd> / <kbd>\u232B</kbd></td><td>Delete selected region</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd></td><td>Undo the last region action (up to 10)</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd></td><td>Redo the last undone region action</td></tr>\n </table>\n\n <ng-template pTemplate=\"footer\">\n <p-button icon=\"pi pi-check\" (click)=\"displayHelpDialog=false\" label=\"Ok\" styleClass=\"p-button-text\">\n </p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:block}:host ::ng-deep .p-toolbar{padding:.25rem .5rem}.toolbar-text{color:var(--primary-color);font-size:.875rem}::ng-deep .toolbar-input .p-inputtext{color:var(--primary-color);border-color:var(--primary-color)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-icon-active{filter:brightness(0) invert(1)}::ng-deep .plotmode-item{display:inline-flex;align-items:center;gap:.5rem;color:#000}::ng-deep .plotmode-item i{font-size:1rem;color:var(--primary-color)}::ng-deep .plotmode-item .plotmode-icon-img{width:1.1rem;height:1.1rem;object-fit:contain;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-sensitivity-slider{width:8rem;align-self:center;margin:0 .25rem}.wand-sensitivity-value{min-width:2rem;text-align:right}span.colormap{width:50px;height:auto;display:inline-block}img.colormap{width:50px;height:auto}.colormap{background:no-repeat;background-size:100%;vertical-align:middle}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.shortcuts-table{border-collapse:collapse}.shortcuts-table td{padding:.25rem .75rem .25rem 0;vertical-align:middle}.shortcuts-table td:first-child{white-space:nowrap}\n"] }]
278
+ args: [{ selector: 'plotting-toolbar', template: "<p-toolbar>\n <div class=\"p-toolbar-group-start flex-wrap align-items-center gap-1\">\n\n <!-- Host-provided custom toolbar buttons, shown first (e.g. app-specific\n actions the visualization view itself doesn't own). -->\n <ng-content></ng-content>\n\n <!-- Plot type: available for any image (grayscale or RGB). -->\n <ng-container *ngIf=\"imageInfo && tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-dropdown [options]=\"plotTypeOptions\" optionLabel=\"label\" optionValue=\"type\"\n [(ngModel)]=\"selectedPlotType\" appendTo=\"body\"\n pTooltip=\"Select plot type\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '11rem'}\"\n styleClass=\"plotmode-dropdown\" panelStyleClass=\"plotmode-panel\"\n (onChange)=\"selectPlotType.emit($event.value)\">\n <ng-template let-opt pTemplate=\"selectedItem\">\n <span class=\"plotmode-item\" *ngIf=\"opt\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n <ng-template let-opt pTemplate=\"item\">\n <span class=\"plotmode-item\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n </p-dropdown>\n <ng-container *ngIf=\"isIsosurfaceMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Iso</span>\n <p-slider [(ngModel)]=\"isoRange\" [range]=\"true\"\n [min]=\"isoValueMin\" [max]=\"isoValueMax\" [step]=\"isoValueStep\"\n (onChange)=\"isoRangeChange.emit($event.values)\"\n pTooltip=\"Isosurface band (0\u2013255), mapped onto the volume's\n actual intensity range. Surfaces are drawn for voxels whose\n intensity falls in the selected band \u2014 narrow it to isolate\n features, widen it to see the whole structure.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ isoRange[0] }}\u2013{{ isoRange[1] }}</span>\n </ng-container>\n <!-- Intensity tools (Heatmap / Image mode): add another colored line ROI.\n Each added line gets the next bright palette colour and its own inset\n trace; select a line on the image and use the trash tool to remove it\n (and its trace). The lines are Region-based but excluded from the\n Region Editor and exports. -->\n <ng-container *ngIf=\"isIntensityCapable\">\n <span class=\"toolbar-separator\"></span>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Add an intensity line (new colour). Click a line to select it,\n then use the trash tool to remove it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"addProfileLine.emit()\">\n <img src=\"assets/plotting/spline-chart.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n </ng-container>\n <!-- Channels & Histogram: brightness/contrast, gamma, per-channel display,\n colormap/reverse/invert \u2014 opens the floating dialog. -->\n <p-button *ngIf=\"tools.specialTools\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Channels &amp; Histogram (brightness/contrast, colormap)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openChannelHistogram.emit()\">\n <img src=\"assets/plotting/invert.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Smoothen toggle: bilinear smoothing vs nearest-neighbour raw pixels (Image\n view only \u2014 it's an OpenSeadragon drawer setting). Highlighted when smoothing on. -->\n <p-button *ngIf=\"tools.specialTools && isImageView\" size=\"small\"\n [text]=\"!imageSmoothingEnabled\"\n pTooltip=\"Smoothen (turn on image smoothing)\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleImageSmoothing.emit()\">\n <img src=\"assets/plotting/smooth.svg\" style=\"width: 1rem\"\n [class]=\"imageSmoothingEnabled ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"/>\n </p-button>\n\n <!-- Stack controls -->\n <ng-container *ngIf=\"imageInfo?.isStack\">\n <span class=\"toolbar-separator\"></span>\n <!-- Image view: scrub z-slices live (the renderer swaps slices in place). -->\n <ng-container *ngIf=\"showsLiveSliceScrubber\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <p-slider [(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onChange)=\"zScrub.emit($event.value)\" (onSlideEnd)=\"zSlide.emit($event.value)\"\n pTooltip=\"Slice through the stack (\u2190 / \u2192 keys)\" tooltipPosition=\"bottom\"\n [style]=\"{'width':'16rem'}\" styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text\">{{zIndex}} of {{maxIndex}}</span>\n </ng-container>\n <!-- Heatmap view: single-image (high-def zoom) vs stack (frame slider). Plotly-only \u2014\n hidden for napari, which has no single/stack toggle or per-frame fetch. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode\">\n <p-button icon=\"pi pi-image\" size=\"small\"\n [text]=\"imageInfo?.showStack\"\n pTooltip=\"Single image mode: enables high-def zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[0])\">\n </p-button>\n <p-button icon=\"pi pi-images\" size=\"small\"\n [text]=\"!imageInfo?.showStack\"\n pTooltip=\"Stack mode, enables slicing through image frames\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[1])\">\n </p-button>\n <ng-container *ngIf=\"!imageInfo?.showStack\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <div class=\"p-inputgroup\" style=\"width: auto\">\n <p-inputNumber styleClass=\"w-3rem toolbar-input\"\n\t\t\t\t\tpTooltip=\"View image in stack (&#x23CE;)\"\n\t\t\t\t\t[(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onInput)=\"zIndexInput.emit(zIndex)\" (keydown.enter)=\"reloadAndPlot.emit()\"/>\n <p-button icon=\"pi pi-refresh\" size=\"small\" [text]=\"true\" (onClick)=\"reloadAndPlot.emit()\"\n pTooltip=\"Plot this slice\" tooltipPosition=\"bottom\">\n </p-button>\n </div>\n <span class=\"flex align-items-center mx-1 toolbar-text\">of {{maxIndex}}</span>\n </ng-container>\n </ng-container>\n </ng-container>\n\n <!-- Image actions (special tools) -->\n <ng-container *ngIf=\"tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-download\" size=\"small\" [text]=\"true\"\n pTooltip=\"Download image as PNG\" tooltipPosition=\"bottom\"\n (onClick)=\"downloadImage.emit()\">\n </p-button>\n <p-button *ngIf=\"isHeatmap\" icon=\"pi pi-window-maximize\" size=\"small\" [text]=\"true\"\n pTooltip=\"Fit to view\" tooltipPosition=\"bottom\"\n (onClick)=\"autoscaleImage.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Zoom tools. Plotly rubber-band drag-zoom: on OpenSeadragon the box-zoom\n covers this and a bare drag pans natively, so it's hidden in image view. -->\n <ng-container *ngIf=\"isHeatmap && tools.zoomTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button *ngIf=\"!isImageView\" icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeDragMode !== 'zoom'\"\n pTooltip=\"Zoom drag\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoom')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'zoomToBox'\"\n pTooltip=\"Zoom box (<kbd>b</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoomToBox')\">\n <img src=\"assets/plotting/zoom-box.svg\"\n [class]=\"activeDragMode === 'zoomToBox' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeDragMode !== 'pan'\"\n pTooltip=\"Pan the image (<kbd>p</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-search-plus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom in (<kbd>+</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomIn.emit()\">\n </p-button>\n <p-button icon=\"pi pi-search-minus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom out (<kbd>-</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomOut.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Surface 3D controls -->\n <ng-container *ngIf=\"!isHeatmap\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'zoom'\"\n pTooltip=\"Zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('zoom')\">\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'pan'\"\n pTooltip=\"Pan\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-globe\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'orbit'\"\n pTooltip=\"Orbital rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('orbit')\">\n </p-button>\n <p-button icon=\"pi pi-sync\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'turntable'\"\n pTooltip=\"Turntable rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('turntable')\">\n </p-button>\n <p-button icon=\"pi pi-home\" size=\"small\" [text]=\"true\"\n pTooltip=\"Reset camera to default\" tooltipPosition=\"bottom\"\n (onClick)=\"resetSurfaceCamera.emit()\">\n </p-button>\n <!-- 3D coordinate-axes / scale gizmo toggle (napari-js WebGPU volume/isosurface only).\n `p-button-icon-only` gives the same square sizing as the neighbouring pi-icon buttons\n (the projected <img> doesn't trigger PrimeNG's automatic icon-only sizing). -->\n <p-button size=\"small\" *ngIf=\"isNapariMode\" styleClass=\"p-button-icon-only\"\n [text]=\"!axesVisible\"\n pTooltip=\"Toggle 3D axes / scale\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleAxes.emit()\">\n <img src=\"assets/plotting/3d-coordinates.svg\"\n [class]=\"axesVisible ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <!-- Surface wireframe toggle (napari-js WebGPU surface only). -->\n <p-button icon=\"pi pi-share-alt\" size=\"small\" *ngIf=\"isNapariSurfaceMode\"\n [text]=\"!wireframeActive\"\n pTooltip=\"Toggle wireframe\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleWireframe.emit()\">\n </p-button>\n <!-- Resolution (decimate factor) for the napari 3D types \u2014 reloads at coarser/finer sampling. -->\n <ng-container *ngIf=\"isNapari3dMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Res</span>\n <p-dropdown [options]=\"decimateOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"resolutionScale\" appendTo=\"body\"\n pTooltip=\"Resolution (decimate factor)\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '6rem'}\"\n (onChange)=\"selectResolution.emit($event.value)\">\n </p-dropdown>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"isHeatmap && tools.regionTools\">\n <span class=\"toolbar-separator\"></span>\n <!-- Region Editor: opens the editor as a floating dialog (formerly the\n right-panel Regions tab). Sits with the region drawing tools. -->\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Open the Region Editor\" tooltipPosition=\"bottom\"\n (onClick)=\"openRegionEditor.emit()\">\n <img src=\"assets/plotting/region-editor.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <!-- Region drawing tools -->\n <p-button icon=\"pi pi-arrow-up-right\" size=\"small\"\n [text]=\"activeDragMode !== 'select'\"\n pTooltip=\"Selection mode (<kbd>s</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('select')\">\n </p-button>\n <p-button icon=\"pi pi-stop\" size=\"small\"\n [text]=\"activeDragMode !== 'drawrect'\"\n pTooltip=\"Draw a rectangular region (<kbd>r</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawrect')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawopenpath'\"\n pTooltip=\"Draw a polyline (<kbd>l</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawopenpath')\">\n <img src=\"assets/plotting/polyline.svg\"\n [class]=\"activeDragMode === 'drawopenpath' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-pencil\" size=\"small\"\n [text]=\"activeDragMode !== 'drawclosedpath'\"\n pTooltip=\"Draw a freeform region (<kbd>f</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawclosedpath')\">\n </p-button>\n <!-- Brush: paint a region by dragging a disc of the given size; the Size\n slider appears while it's active. Hold Shift to erase (subtract). -->\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'brush'\"\n pTooltip=\"Brush: click and drag to paint a region.\n <br>Hold <kbd>Shift</kbd> while painting to erase from a region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('brush')\">\n <img src=\"assets/plotting/paintbrush.svg\"\n [class]=\"activeDragMode === 'brush' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'brush'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Size</span>\n <p-slider [(ngModel)]=\"brushSize\"\n [min]=\"brushSizeMin\" [max]=\"brushSizeMax\" [step]=\"brushSizeStep\"\n (onChange)=\"brushSizeChange.emit($event.value)\"\n pTooltip=\"Brush diameter in image pixels.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ brushSize }}</span>\n </ng-container>\n\n <!-- Vertex editing \u2014 backends with a vertex-editing overlay (OSD + napari-js image). -->\n <ng-container *ngIf=\"supportsRegionVertexTools\">\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawpolygon'\"\n pTooltip=\"Polygon: click to place each vertex; click the first point to close\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawpolygon')\">\n <img src=\"assets/plotting/polygon-vertices.svg\"\n [class]=\"activeDragMode === 'drawpolygon' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'addpoint'\"\n pTooltip=\"Add a vertex: click an edge of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('addpoint')\">\n <img src=\"assets/plotting/vertex-add.svg\"\n [class]=\"activeDragMode === 'addpoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'deletepoint'\"\n pTooltip=\"Delete a vertex: click a vertex of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('deletepoint')\">\n <img src=\"assets/plotting/vertex-delete.svg\"\n [class]=\"activeDragMode === 'deletepoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'wand'\"\n pTooltip=\"Wand: click and drag to grow a region by similar pixel values (<kbd>w</kbd>).\n <br>Hold <kbd>Shift</kbd> while clicking or dragging on a region to erase pixels from it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('wand')\">\n <img src=\"assets/plotting/wand.svg\"\n [class]=\"activeDragMode === 'wand' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'wand'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Sens.</span>\n <p-slider [(ngModel)]=\"wandSensitivity\"\n [min]=\"wandSensitivityMin\" [max]=\"wandSensitivityMax\" [step]=\"wandSensitivityStep\"\n (onChange)=\"wandSensitivityChange.emit($event.value)\"\n pTooltip=\"Higher = stricter (smaller selection); lower = looser.\n Takes effect from the next click/drag tick.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ wandSensitivity.toFixed(1) }}</span>\n </ng-container>\n <p-button icon=\"pi pi-eraser\" size=\"small\"\n [text]=\"activeDragMode !== 'eraseVertex'\"\n pTooltip=\"Vertex eraser (<kbd>e</kbd>): click or drag to remove polygon vertices inside\n the cursor circle from any region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('eraseVertex')\">\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'eraseVertex'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Radius</span>\n <p-slider [(ngModel)]=\"vertexEraserRadius\"\n [min]=\"vertexEraserRadiusMin\" [max]=\"vertexEraserRadiusMax\" [step]=\"vertexEraserRadiusStep\"\n (onChange)=\"vertexEraserRadiusChange.emit($event.value)\"\n pTooltip=\"Eraser radius in image pixels.\n Vertices within this distance of the cursor are removed.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ vertexEraserRadius }}</span>\n </ng-container>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canUndo\"\n pTooltip=\"Undo the last region action (<kbd>Ctrl</kbd>+<kbd>Z</kbd>).\n Up to 10 actions can be undone.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"undoRegion.emit()\">\n <img src=\"assets/plotting/undo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canRedo\"\n pTooltip=\"Redo the last undone region action\n (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd>).\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"redoRegion.emit()\">\n <img src=\"assets/plotting/redo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-trash\" size=\"small\" [text]=\"true\"\n pTooltip=\"Delete selected region (<kbd>Del</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"deleteRegion.emit()\">\n </p-button>\n <!-- SAM (jit-ui#90): box-prompt segment, interactive point prompts, and a\n model picker. -->\n <span class=\"toolbar-separator\"></span>\n <!-- SAM controls grouped: box-prompt Segment, then the point-prompt tool,\n then the model-picker dropdown (the chevron) which governs BOTH tools.\n Falls back to Segment + point with no model picker when none are set. -->\n <span *ngIf=\"samModels.length; else segmentPlain\" class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/sam-box-prompt.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the SAM model (used by both segment tools)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"samModelMenu.toggle($event)\">\n </p-button>\n </span>\n <ng-template #segmentPlain>\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/sam-box-prompt.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n </span>\n </ng-template>\n <!-- Point-prompt tool \u2014 shared between the models / no-models layouts so it\n always sits just left of the model dropdown. -->\n <ng-template #samPointBtn>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'samPoint'\"\n pTooltip=\"SAM point prompts: click a fiber to segment it as a new region;\n Shift/Alt-click to refine (exclude) the current one.<br>Esc undoes it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('samPoint')\">\n <img src=\"assets/plotting/sam-point-prompt.svg\"\n [class]=\"activeDragMode === 'samPoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-template>\n <p-menu #samModelMenu styleClass=\"model-menu\" [model]=\"samMenuItems\" [popup]=\"true\" appendTo=\"body\">\n <ng-template pTemplate=\"item\" let-item>\n <ng-container [ngTemplateOutlet]=\"modelMenuItem\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n </ng-template>\n </p-menu>\n\n <!-- Shared item body for all three model pickers (SAM / YOLO / retinal):\n the standard check-or-blank icon and label, plus an info icon carrying\n the model's description as a hover tooltip. This replaced a single\n \"About the SAM models\" overlay panel \u2014 per-model text sits next to the\n model it describes, and YOLO/retinal get the same affordance.\n\n p-menu puts its click handler on the wrapping `.p-menuitem-content`,\n outside this template, so `command` still fires and the whole row stays\n clickable \u2014 which is also why the info icon has to stop propagation, or\n reading about a model would switch to it. -->\n <ng-template #modelMenuItem let-item>\n <a class=\"p-menuitem-link model-menu-item\">\n <span class=\"p-menuitem-icon\" [ngClass]=\"item.icon\"></span>\n <span class=\"p-menuitem-text\">{{ item.label }}</span>\n <!-- `bottom` like every other tooltip in this toolbar, and for room:\n these menus open under buttons that can sit near the right edge,\n where a `right` tooltip has a few hundred px and squeezes its text\n into a narrow column instead of using its max-width. -->\n <!-- A focusable <button>, not a bare <i>. As an <i> the description\n was reachable only by hovering a mouse: not tabbable, no\n accessible name, nothing for a screen reader to announce \u2014 so\n keyboard and AT users could pick a checkpoint but never read what\n it was. PrimeNG's tooltip shows on focus as well as hover, so\n making it a button restores it for free.\n\n `aria-describedby` is deliberately not used: the tooltip is\n rendered into `body` on demand and has no stable id to point at.\n The description is carried on `aria-label` instead, which is\n announced whether or not the tooltip is showing. It is stripped of\n markup first, since the copy contains `<b>`/`<br>` for the visual\n tooltip and those would otherwise be read out literally. -->\n <button *ngIf=\"item.tooltip\" type=\"button\"\n class=\"pi pi-info-circle model-info-icon\"\n [attr.aria-label]=\"'About ' + item.label + ': ' + plainText(item.tooltip)\"\n [pTooltip]=\"item.tooltip\" [escape]=\"false\" tooltipPosition=\"bottom\"\n appendTo=\"body\" tooltipStyleClass=\"model-info-tooltip\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.space)=\"$event.stopPropagation()\"></button>\n </a>\n </ng-template>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Auto-segment cells inside each drawn rectangle (cellpose-SAM)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentCellpose.emit()\">\n <img src=\"assets/plotting/cells.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Contributed no-prompt tools (TOOLBAR_TOOLS). Unlike the SAM and\n cellpose tools these take no prompt \u2014 they sweep the whole current\n view rather than being pointed at something \u2014 so there is nothing to\n draw first. Each renders the same split button: run, pick checkpoint,\n open parameters.\n\n Nothing here names a specific tool. The library ships none; a host\n registers them, which is what lets this package be published without\n the closed model dependencies. -->\n <ng-container *ngFor=\"let tool of contributedTools; trackBy: trackToolById\">\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.runTooltip\"\n tooltipPosition=\"bottom\"\n (onClick)=\"runTool.emit(tool.id)\">\n <!-- `toolbar-icon-dark` is hardcoded rather than bound, unlike the\n toggle tools: these fire a run and have no active state. -->\n <img *ngIf=\"tool.icon.src\" [src]=\"tool.icon.src\" class=\"toolbar-icon-dark\"\n style=\"width: 1rem\" [alt]=\"tool.label\" />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.modelTooltip || 'Pick the model'\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toolMenu.toggle($event)\">\n </p-button>\n <p-button *ngIf=\"tool.params.length\"\n icon=\"pi pi-sliders-h\" size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.paramsTooltip || 'Parameters'\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openToolParams.emit(tool.id)\">\n </p-button>\n </span>\n <p-menu #toolMenu styleClass=\"model-menu\" [model]=\"toolMenuItems[tool.id] || []\"\n [popup]=\"true\" appendTo=\"body\">\n <ng-template pTemplate=\"item\" let-item>\n <ng-container [ngTemplateOutlet]=\"modelMenuItem\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n </ng-template>\n </p-menu>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"tools.help\">\n <span class=\"toolbar-separator\"></span>\n\n <!-- Help -->\n <p-button icon=\"pi pi-question-circle\" size=\"small\" [text]=\"true\"\n pTooltip=\"Help\" tooltipPosition=\"bottom\"\n (onClick)=\"showHelp()\">\n </p-button>\n </ng-container>\n\n </div>\n</p-toolbar>\n<p-dialog header=\"Plotting help\" [(visible)]=\"displayHelpDialog\" [modal]=\"true\" [style]=\"{width: '50vw'}\"\n [maximizable]=\"true\" [draggable]=\"false\" [resizable]=\"false\">\n <p>Click a file in the left-hand tree to display it. By default it opens in the\n <b>Image</b> view \u2014 a natively tiled, deeply zoomable raster (OpenSeadragon) with a\n navigator minimap and a physical-units scale bar. <b>Click the image to zoom</b> toward\n the clicked point; scroll to zoom and drag to pan. When a stack is open, a slice slider\n (Image view) or image-mode toggle (other views) appears. Regions can also be edited in the\n <b>Region Editor</b>, opened with the\n <img src=\"assets/plotting/region-editor.svg\" width=\"14\" height=\"14\" alt=\"Region Editor\"/>\n toolbar button as a floating dialog. Drag the <i class=\"pi pi-bars\"></i> handle to detach\n this toolbar into a floating window.\n </p>\n\n <h3>Plot-type dropdown</h3>\n <p>\n The <b>plot-type dropdown</b> switches how the file is visualized:\n <b>Image</b>, <b>Contour</b>, <b>Scatter (regions)</b>, <b>Surface (3D)</b>,\n <b>Scatter 3D</b>, and <b>Isosurface (3D)</b>. Scalar and 3D types require a grayscale\n image (the 3D volume types also require a z-stack). The non-image views render with\n Plotly and support \"real zooming\" (a downscaled overview that re-fetches higher\n resolution as you zoom in).\n </p>\n <ul>\n <li>\"<b>Isosurface band</b>\" slider (Isosurface): sets the intensity band the surfaces are\n drawn for, mapped onto the volume's real intensity range.</li>\n <li>\"<b>3D camera</b>\" (Surface / Scatter 3D / Isosurface): zoom <i class=\"pi pi-search\"></i>,\n pan <i class=\"pi pi-arrows-alt\"></i>, orbit <i class=\"pi pi-globe\"></i>,\n turntable <i class=\"pi pi-sync\"></i>, and reset camera <i class=\"pi pi-home\"></i>;\n scroll to zoom the 3D scene.</li>\n </ul>\n\n <h3>Image manipulation tools</h3>\n <ul>\n <li>\"<b>Region Editor</b>\" <img src=\"assets/plotting/region-editor.svg\" width=\"15\" height=\"15\">:\n opens the Region Editor as a floating, draggable dialog \u2014 a per-image table of every region\n (class, colour, area) with bulk select/label/colour/delete and GeoJSON import/export. It stays\n in sync with the image, so you can keep it open while drawing.</li>\n <li>\"<b>Intensity profile lines</b>\" <img src=\"assets/plotting/spline-chart.svg\" width=\"15\" height=\"15\">\n (<i>work in progress \u2014 not yet stable</i>): adds a coloured line ROI; a floating inset\n chart plots intensity along each line, updates live as you drag it, and re-samples at the\n current zoom resolution. Each added line gets its own colour; select a line and use the\n trash tool <i class=\"pi pi-trash\"></i> to remove it (and its trace).</li>\n <li>\"<b>Channels &amp; Histogram</b>\" <img src=\"assets/plotting/invert.svg\" width=\"15\" height=\"15\">:\n opens a floating dialog for brightness/contrast, gamma, and per-channel display, plus the\n <b>colormap / LUT</b> controls (grayscale only) \u2014 a <b>reverse</b> toggle and a colormap\n dropdown (default: inverted greys).</li>\n <li>\"<b>Smoothen</b>\" <img src=\"assets/plotting/smooth.svg\" width=\"15\" height=\"15\">\n (Image view only): the image opens showing <b>raw pixels</b> (nearest-neighbour), so\n zooming in renders crisp blocks for pixel inspection. Toggle this on for bilinear\n smoothing; the icon turns white while smoothing is active.</li>\n </ul>\n\n <h3>Download &amp; view tools</h3>\n <ul>\n <li>\"<b>Download</b>\" <i class=\"pi pi-download\"></i>: download the current image as a PNG.</li>\n <li>\"<b>Fit to view</b>\" / autoscale <i class=\"pi pi-window-maximize\"></i>: resets the image\n to its original dimensions. In stack mode it also resets to single-image mode.</li>\n </ul>\n\n <h3>Zoom tools</h3>\n <ul>\n <li>\"<b>Zoom box</b>\" <img src=\"assets/plotting/zoom-box.svg\" width=\"15\" height=\"15\">\n (<kbd>b</kbd>): drag a box to select an area to zoom into.</li>\n <li>\"<b>Pan</b>\" <i class=\"pi pi-arrows-alt\"></i> (<kbd>p</kbd>): drag to pan the image.</li>\n <li>\"<b>Zoom in</b>\" <i class=\"pi pi-search-plus\"></i> (<kbd>+</kbd>): zoom in one step.</li>\n <li>\"<b>Zoom out</b>\" <i class=\"pi pi-search-minus\"></i> (<kbd>-</kbd>): zoom out one step,\n stopping at the original image dimensions.</li>\n </ul>\n\n <h3>Region tools</h3>\n <p>By default no tool is selected (all greyed-out); selecting a tool highlights it. The\n vertex-editing tools (polygon, add/delete vertex, move, B\u00E9zier conversions) are available\n in the <b>Image</b> view only. On a <b>donut</b> (a region with a hole), the inner-ring\n vertices are editable too \u2014 drag, add, and delete them just like the outer ring.</p>\n <ul>\n <li>\"<b>Selection</b>\" <i class=\"pi pi-arrow-up-right\"></i> (<kbd>s</kbd>): a neutral mode\n that turns off whatever drawing tool was active (wand, vertex eraser, rectangle, etc.).\n Use it to step out of editing without picking another tool.</li>\n <li>\"<b>Rectangular region</b>\" <i class=\"pi pi-stop\"></i> (<kbd>r</kbd>): click and drag to\n create a rectangular region.</li>\n <li>\"<b>Polyline</b>\" <img src=\"assets/plotting/polyline.svg\" width=\"14\" height=\"14\">\n (<kbd>l</kbd>): draw an open (non-closed) polyline by clicking and dragging. Polylines can\n be edited and exported to GeoJSON as a <code>LineString</code> feature.</li>\n <li>\"<b>Freeform region</b>\" <i class=\"pi pi-pencil\"></i> (<kbd>f</kbd>): click and drag to\n draw a closed freeform polygon.</li>\n <li>\"<b>Brush</b>\" <img src=\"assets/plotting/paintbrush.svg\" width=\"15\" height=\"15\">: click\n and drag to paint a region with a circular brush \u2014 the painted stroke is the region.\n A <b>Size</b> slider (brush diameter, in image pixels) appears in the toolbar while the\n brush is active. Hold <kbd>Shift</kbd> while painting to <b>erase</b> (subtract) from a\n region instead. Painting over an existing region extends it; brushing across two regions\n merges them. Painting a closed ring (or shift-erasing an interior pocket) leaves the\n enclosed area empty \u2014 a <b>donut</b> (Image view).</li>\n <li>\"<b>Polygon</b>\" <img src=\"assets/plotting/polygon-vertices.svg\" width=\"15\" height=\"15\">:\n click to place each vertex; click the first point to close the polygon.</li>\n <li>\"<b>Add vertex</b>\" <img src=\"assets/plotting/vertex-add.svg\" width=\"15\" height=\"15\">:\n click an edge of the selected polygon to insert a new vertex.</li>\n <li>\"<b>Delete vertex</b>\" <img src=\"assets/plotting/vertex-delete.svg\" width=\"15\" height=\"15\">:\n click a vertex of the selected polygon to remove it.</li>\n <li>\"<b>Move region</b>\" <img src=\"assets/plotting/region-move.svg\" width=\"15\" height=\"15\">:\n drag to move the selected region.</li>\n <li>\"<b>To B\u00E9zier</b>\" <img src=\"assets/plotting/to-bezier.svg\" width=\"15\" height=\"15\"> /\n \"<b>To polygon</b>\" <img src=\"assets/plotting/to-polygon.svg\" width=\"15\" height=\"15\">:\n convert the selected region to a smooth B\u00E9zier curve, or back to a straight-edged polygon.</li>\n <li>\"<b>Wand</b>\" <img src=\"assets/plotting/wand.svg\" width=\"15\" height=\"15\"> (<kbd>w</kbd>):\n click to grow a region from pixels with similar values around the click point. Hold\n <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> for an exact-match flood fill; drag to keep extending the\n selection while moving; hold <kbd>Shift</kbd> while clicking or dragging on an existing\n region to <b>erase</b> pixels from it. Use the <b>Sens.</b> slider that appears to make the\n wand stricter (higher = smaller selection) or looser. Subsequent clicks keep extending the\n same region; clicking inside an existing region adopts it, and growing into another region\n merges them.</li>\n <li>\"<b>Vertex eraser</b>\" <i class=\"pi pi-eraser\"></i> (<kbd>e</kbd>): click or click-and-drag\n to remove polygon vertices that fall inside the cursor circle from any region, including a\n region's inner rings (donut holes). Use the <b>Radius</b> slider to set the eraser size in\n image pixels. Polygons reduced below 3 vertices (or polylines below 2) are deleted entirely;\n a hole reduced below 3 vertices is removed.</li>\n <li>\"<b>Undo</b>\" <img src=\"assets/plotting/undo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd>): reverts the\n last region action (draw, delete, edit, wand/brush stroke, segmentation, \u2026). Up to the last\n <b>10</b> actions can be undone; the button greys out when there's nothing left to undo.</li>\n <li>\"<b>Redo</b>\" <img src=\"assets/plotting/redo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> or <kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Y</kbd>):\n re-applies the most recently undone action. Performing a new region action clears what can be redone.</li>\n <li>\"<b>Delete region</b>\" <i class=\"pi pi-trash\"></i> (<kbd>Del</kbd>): deletes the selected\n region. This can also be done from the Region Editor.</li>\n </ul>\n\n <h3>Region actions (right-click the image)</h3>\n <p>Right-click a region \u2014 or the current multi-selection \u2014 to open the\n \"<b>Selected region(s)</b>\" group at the top of the context menu. These act immediately on\n the selection (they're not drawing modes) and are fully undo/redo-able:</p>\n <ul>\n <li>\"<b>Select all regions</b>\" <i class=\"pi pi-check-square\"></i>: select every region on\n the image (also in the Region Editor toolbar).</li>\n <li>\"<b>Merge / group</b>\": combine 2+ selected regions into one. If the pieces don't touch,\n the result is a single multi-part region (sparse \"bits\").</li>\n <li>\"<b>Ungroup</b>\": split a multi-part region back into one region per piece.</li>\n <li>\"<b>Inverse</b>\": replace the selection with everything <i>outside</i> it \u2014 the image\n rectangle with the selected regions punched out as holes. Works at any image size.</li>\n <li>\"<b>Simplify</b>\": reduce a region's vertex count (Douglas\u2013Peucker) by an altitude\n threshold in pixels \u2014 pick <b>Light</b> (1&nbsp;px), <b>Medium</b> (3&nbsp;px),\n <b>Strong</b> (8&nbsp;px), or <b>Custom\u2026</b> for a slider. Applies to the exterior and any\n holes (and every part of a multi-part region).</li>\n <li>The B\u00E9zier conversions and Delete also live in this group (the right-click equivalents of\n the toolbar / vertex tools above).</li>\n </ul>\n\n <h3>Segmentation &amp; detection tools</h3>\n <p>AI-assisted segmentation and detection that run entirely in the browser. The first run\n of any model downloads it (a progress toast is shown); it's then cached.</p>\n <p>Two shapes of tool: the <b>prompted</b> ones (Segment, Point prompts, Cellpose) act on\n rectangles you draw, and generated regions inherit the color of the rectangle they came\n from<span *ngIf=\"contributedTools.length\">; the <b>no-prompt</b> ones ({{ contributedToolNames }})\n sweep the whole current view, so there is nothing to draw first</span>.</p>\n <p>Each model dropdown <i class=\"pi pi-chevron-down\"></i> lists the available checkpoints with\n a <i class=\"pi pi-info-circle\"></i> beside each one \u2014 hover it for that model's size, speed,\n training domain and accuracy, so you can pick without leaving the toolbar.</p>\n <ul>\n <li>\"<b>Segment</b>\" <img src=\"assets/plotting/sam-box-prompt.svg\" width=\"15\" height=\"15\">\n (box prompt): draw one or more <b>Rectangular regions</b> around the objects of interest,\n then click Segment. Each rectangle is replaced by the SAM mask of the object inside it.</li>\n <li>\"<b>Point prompts</b>\" <img src=\"assets/plotting/sam-point-prompt.svg\" width=\"15\" height=\"15\">: click directly on an object to\n segment it as a <b>new</b> region. Each click starts a fresh object \u2014 clicking another\n object won't grow the previous one. <kbd>Shift</kbd>/<kbd>Alt</kbd>-click adds an\n <i>exclude</i> point that refines the current object; <kbd>Esc</kbd> undoes it,\n <kbd>Enter</kbd> commits it.</li>\n <li>\"<b>Model picker</b>\" <i class=\"pi pi-chevron-down\"></i>: the dropdown attached to the\n Segment button chooses the SAM model \u2014 <b>micro-sam ViT-T</b> (default; small, fast) or\n <b>micro-sam ViT-B</b> (larger, highest accuracy) for light microscopy, or\n <b>patho-sam ViT-B</b> for histopathology (H&amp;E / stained tissue). All are promptable\n Segment Anything models finetuned for biomedical images; they differ in the <i>image\n encoder</i> (the heavy once-per-image step) and the training domain \u2014 box and point\n prompting behave the same. The choice applies to <i>both</i> the box and point tools, and\n each tool loads its own encoder, so the first click after switching re-encodes once.</li>\n <li>\"<b>Cellpose</b>\" <img src=\"assets/plotting/cells.svg\" width=\"15\" height=\"15\">: draw rectangles, then click Cellpose to\n automatically segment every cell inside each rectangle (client-side cellpose-SAM) \u2014 one\n region per detected cell. No clicking on individual cells needed.</li>\n <!-- Contributed tools describe themselves. This library ships none, so an\n open build's help lists only the prompted tools above \u2014 the text no\n longer promises buttons that are not there. -->\n <ng-container *ngFor=\"let tool of contributedTools\">\n <li *ngIf=\"tool.help\" [innerHTML]=\"tool.help.body\"></li>\n </ng-container>\n </ul>\n\n <h3>Help</h3>\n <p>\"<b>Help</b>\" <i class=\"pi pi-question-circle\"></i>: opens this dialog.</p>\n\n <h3>Keyboard shortcuts</h3>\n <table class=\"shortcuts-table\">\n <tr><td><kbd>+</kbd> / <kbd>=</kbd></td><td>Zoom in</td></tr>\n <tr><td><kbd>-</kbd></td><td>Zoom out</td></tr>\n <tr><td><kbd>b</kbd></td><td>Toggle zoom box mode</td></tr>\n <tr><td><kbd>p</kbd></td><td>Toggle pan mode</td></tr>\n <tr><td><kbd>f</kbd></td><td>Toggle freeform region drawing</td></tr>\n <tr><td><kbd>l</kbd></td><td>Toggle polyline drawing</td></tr>\n <tr><td><kbd>r</kbd></td><td>Toggle rectangular region drawing</td></tr>\n <tr><td><kbd>w</kbd></td><td>Toggle wand tool</td></tr>\n <tr><td>Wand + <kbd>Shift</kbd></td><td>Erase pixels from the active region</td></tr>\n <tr><td>Wand + <kbd>Ctrl</kbd>/<kbd>Cmd</kbd></td><td>Exact-match flood fill (no smoothing)</td></tr>\n <tr><td><kbd>e</kbd></td><td>Toggle vertex eraser</td></tr>\n <tr><td><kbd>s</kbd></td><td>Selection mode (deactivates drawing tools)</td></tr>\n <tr><td>Point prompt + <kbd>Shift</kbd>/<kbd>Alt</kbd></td><td>Add an exclude point (refine the current object)</td></tr>\n <tr><td><kbd>Enter</kbd></td><td>Commit the current SAM point object</td></tr>\n <tr><td><kbd>Esc</kbd></td><td>Undo the current SAM point object</td></tr>\n <tr><td><kbd>Del</kbd> / <kbd>\u232B</kbd></td><td>Delete selected region</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd></td><td>Undo the last region action (up to 10)</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd></td><td>Redo the last undone region action</td></tr>\n </table>\n\n <ng-template pTemplate=\"footer\">\n <p-button icon=\"pi pi-check\" (click)=\"displayHelpDialog=false\" label=\"Ok\" styleClass=\"p-button-text\">\n </p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:block}:host ::ng-deep .p-toolbar{padding:.25rem .5rem}.toolbar-text{color:var(--primary-color);font-size:.875rem}::ng-deep .toolbar-input .p-inputtext{color:var(--primary-color);border-color:var(--primary-color)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-icon-active{filter:brightness(0) invert(1)}::ng-deep .plotmode-item{display:inline-flex;align-items:center;gap:.5rem;color:#000}::ng-deep .plotmode-item i{font-size:1rem;color:var(--primary-color)}::ng-deep .plotmode-item .plotmode-icon-img{width:1.1rem;height:1.1rem;object-fit:contain;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .p-menu.model-menu{width:auto;min-width:12.5rem}::ng-deep .model-menu-item{display:flex;align-items:center;gap:.5rem}::ng-deep .model-menu-item .p-menuitem-text{flex:1 1 auto;white-space:nowrap}::ng-deep .model-menu-item .model-info-icon{flex:0 0 auto;font-size:.875rem;opacity:.55;cursor:help;appearance:none;background:none;border:0;padding:0;margin:0;color:inherit;line-height:1}::ng-deep .model-menu-item .model-info-icon:hover{opacity:1}::ng-deep .model-menu-item .model-info-icon:focus-visible{opacity:1;outline:2px solid var(--primary-color, #2196f3);outline-offset:2px;border-radius:2px}::ng-deep .model-info-tooltip .p-tooltip-text{min-width:18rem;max-width:24rem;line-height:1.4}::ng-deep .wand-sensitivity-slider{width:8rem;align-self:center;margin:0 .25rem}.wand-sensitivity-value{min-width:2rem;text-align:right}span.colormap{width:50px;height:auto;display:inline-block}img.colormap{width:50px;height:auto}.colormap{background:no-repeat;background-size:100%;vertical-align:middle}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.shortcuts-table{border-collapse:collapse}.shortcuts-table td{padding:.25rem .75rem .25rem 0;vertical-align:middle}.shortcuts-table td:first-child{white-space:nowrap}\n"] }]
242
279
  }], propDecorators: { imageInfo: [{
243
280
  type: Input
244
281
  }], plotTypeOptions: [{
@@ -283,13 +320,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
283
320
  type: Input
284
321
  }], samModelId: [{
285
322
  type: Input
286
- }], yoloModels: [{
287
- type: Input
288
- }], yoloModelId: [{
289
- type: Input
290
- }], retinalModels: [{
323
+ }], contributedTools: [{
291
324
  type: Input
292
- }], retinalModelId: [{
325
+ }], toolModelIds: [{
293
326
  type: Input
294
327
  }], selectPlotType: [{
295
328
  type: Output
@@ -343,17 +376,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
343
376
  type: Output
344
377
  }], segmentCellpose: [{
345
378
  type: Output
346
- }], detectYolo: [{
347
- type: Output
348
- }], yoloModelChange: [{
349
- type: Output
350
- }], openYoloParams: [{
351
- type: Output
352
- }], segmentRetinal: [{
379
+ }], runTool: [{
353
380
  type: Output
354
- }], retinalModelChange: [{
381
+ }], toolModelChange: [{
355
382
  type: Output
356
- }], openRetinalParams: [{
383
+ }], openToolParams: [{
357
384
  type: Output
358
385
  }], samModelChange: [{
359
386
  type: Output
@@ -364,4 +391,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
364
391
  }], vertexEraserRadiusChange: [{
365
392
  type: Output
366
393
  }] } });
367
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbGJhci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQWEsTUFBTSxFQUFpQixNQUFNLGVBQWUsQ0FBQztBQUlqRyxPQUFPLEVBQ0wsUUFBUSxFQUVSLFVBQVUsRUFDVixrQkFBa0IsRUFDbEIsZUFBZSxFQUNmLGVBQWUsRUFDZix1QkFBdUIsRUFDdkIsdUJBQXVCLEdBQ3hCLE1BQU0sd0JBQXdCLENBQUM7QUFDaEMsT0FBTyxFQUF5QixpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDOzs7Ozs7Ozs7Ozs7Ozs7QUFFdkY7Ozs7Ozs7Ozs7O0dBV0c7QUFRSCxNQUFNLE9BQU8sZ0JBQWdCO0lBUDdCO1FBVUUsZ0RBQWdEO1FBQ3ZDLG9CQUFlLEdBQXlCLEVBQUUsQ0FBQztRQUMzQyxxQkFBZ0IsR0FBYSxRQUFRLENBQUMsS0FBSyxDQUFDO1FBQ3JEOzRFQUNvRTtRQUMzRCxhQUFRLEdBQWEsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDOUIsYUFBUSxHQUFHLENBQUMsQ0FBQztRQUNiLFdBQU0sR0FBRyxDQUFDLENBQUM7UUFDcEIsa0RBQWtEO1FBQ3pDLGNBQVMsR0FBRyxJQUFJLENBQUM7UUFDakIsbUJBQWMsR0FBa0IsSUFBSSxDQUFDO1FBQ3JDLHdCQUFtQixHQUFHLFdBQVcsQ0FBQztRQUMzQyw0RkFBNEY7UUFDbkYsZ0JBQVcsR0FBRyxJQUFJLENBQUM7UUFDbkIsb0JBQWUsR0FBRyxLQUFLLENBQUM7UUFDakMsc0VBQXNFO1FBQzdELG9CQUFlLEdBQUcsdUJBQXVCLENBQUM7UUFDbkQsMkRBQTJEO1FBQ2xE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O2dCQUVHLGVBQWU7c0JBQXhCLE1BQU07Z0JBRUcsZ0JBQWdCO3NCQUF6QixNQUFNO2dCQUNHLFlBQVk7c0JBQXJCLE1BQU07Z0JBRUcsVUFBVTtzQkFBbkIsTUFBTTtnQkFFRyxVQUFVO3NCQUFuQixNQUFNO2dCQUVHLGNBQWM7c0JBQXZCLE1BQU07Z0JBRUcsZUFBZTtzQkFBeEIsTUFBTTtnQkFFRyxVQUFVO3NCQUFuQixNQUFNO2dCQUVHLGVBQWU7c0JBQXhCLE1BQU07Z0JBRUcsY0FBYztzQkFBdkIsTUFBTTtnQkFFRyxjQUFjO3NCQUF2QixNQUFNO2dCQUVHLGtCQUFrQjtzQkFBM0IsTUFBTTtnQkFFRyxpQkFBaUI7c0JBQTFCLE1BQU07Z0JBRUcsY0FBYztzQkFBdkIsTUFBTTtnQkFDRyxxQkFBcUI7c0JBQTlCLE1BQU07Z0JBQ0csZUFBZTtzQkFBeEIsTUFBTTtnQkFDRyx3QkFBd0I7c0JBQWpDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIEV2ZW50RW1pdHRlciwgSW5wdXQsIE9uQ2hhbmdlcywgT3V0cHV0LCBTaW1wbGVDaGFuZ2VzIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBNZW51SXRlbSB9IGZyb20gJ3ByaW1lbmcvYXBpJztcblxuaW1wb3J0IHsgSUltYWdlSW5mbyB9IGZyb20gJy4uL2NvbnRyYWN0cy9pbWFnZS5jb250cmFjdCc7XG5pbXBvcnQge1xuICBQbG90VHlwZSxcbiAgUGxvdFR5cGVEZXNjcmlwdG9yLFxuICBpc05hcGFyaTNkLFxuICBpc05hcGFyaUlzb3N1cmZhY2UsXG4gIGlzTmFwYXJpU3VyZmFjZSxcbiAgaXNOYXBhcmlTY2F0dGVyLFxuICBOQVBBUklfREVDSU1BVEVfT1BUSU9OUyxcbiAgTkFQQVJJX0RFRkFVTFRfREVDSU1BVEUsXG59IGZyb20gJy4uL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuaW1wb3J0IHsgVG9vbGJhclRvb2xWaXNpYmlsaXR5LCBBTExfVE9PTEJBUl9UT09MUyB9IGZyb20gJy4uL2NvbnRyYWN0cy90b29sYmFyLWNvbmZpZyc7XG5cbi8qKlxuICogUHJlc2VudGF0aW9uYWwgdG9vbGJhciBmb3IgdGhlIHBsb3R0aW5nIHZpZXdwb3J0LlxuICpcbiAqIFJlbmRlcnMgdGhlIHBsb3QtdHlwZSBzZWxlY3RvciwgY29sb3JtYXAvTFVUIGNvbnRyb2xzLCBzdGFjayBuYXZpZ2F0aW9uLFxuICogaW1hZ2UgYWN0aW9ucyAoZG93bmxvYWQgLyBhdXRvc2NhbGUgLyBwaXBlbGluZSksIHZpZXdwb3J0IHpvb20vcGFuLCB0aGVcbiAqIFN1cmZhY2UtM0QgY2FtZXJhIGNvbnRyb2xzLCBhbmQgdGhlIHJlZ2lvbi1kcmF3aW5nIHRvb2xzIOKAlCBwbHVzIHRoZSBwbG90dGluZ1xuICogaGVscCBkaWFsb2cuIEl0IG93bnMgbm8gcmVuZGVyaW5nIG9yIHZpc3VhbGl6YXRpb24gc3RhdGU6IHRoZSBob3N0XG4gKiAoVmlzdWFsaXplckNvbXBvbmVudCkgc3RheXMgdGhlIG9yY2hlc3RyYXRvciwgcGFzc2luZyBzdGF0ZSBpbiB2aWEgYEBJbnB1dGAgYW5kXG4gKiBoYW5kbGluZyBldmVyeSBhY3Rpb24gdmlhIGBAT3V0cHV0YC4gVGhlIHNhbWUgYWN0aW9ucyBhcmUgYWxzbyBkcml2ZW4gYnkgdGhlXG4gKiBkaWFncmFtJ3MgcmlnaHQtY2xpY2sgY29udGV4dCBtZW51IGFuZCBrZXlib2FyZCBzaG9ydGN1dHMsIHNvIGtlZXBpbmcgdGhlXG4gKiBoYW5kbGVycyBpbiB0aGUgaG9zdCBhdm9pZHMgZHVwbGljYXRpbmcgdGhhdCBsb2dpYy5cbiAqL1xuQENvbXBvbmVudCh7XG4gIC8vIENhbm9uaWNhbCBwcmVmaXhlZCBzZWxlY3RvciBmaXJzdDsgdGhlIHVucHJlZml4ZWQgb3JpZ2luYWwgaXMga2VwdCBhcyBhblxuICAvLyBhbGlhcyBmb3Igb25lIHJlbGVhc2UgKHByZS1wdWJsaWNhdGlvbiBiYWNrLWNvbXBhdCkuXG4gIHNlbGVjdG9yOiAncGxvdHRpbmctdG9vbGJhcicsXG4gIHRlbXBsYXRlVXJsOiAnLi90b29sYmFyLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vdG9vbGJhci5jb21wb25lbnQuc2NzcyddLFxufSlcbmV4cG9ydCBjbGFzcyBUb29sYmFyQ29tcG9uZW50IGltcGxlbWVudHMgT25DaGFuZ2VzIHtcbiAgLyoqIEN1cnJlbnQgaW1hZ2UgKGdhdGVzIHdoaWNoIGNvbnRyb2wgZ3JvdXBzIGFyZSBzaG93bikuICovXG4gIEBJbnB1dCgpIGltYWdlSW5mbzogSUltYWdlSW5mbyB8IHVuZGVmaW5lZDtcbiAgLyoqIFBsb3QgdHlwZXMgdGhlIGFjdGl2ZSBiYWNrZW5kIGFkdmVydGlzZXMuICovXG4gIEBJbnB1dCgpIHBsb3RUeXBlT3B0aW9uczogUGxvdFR5cGVEZXNjcmlwdG9yW10gPSBbXTtcbiAgQElucHV0KCkgc2VsZWN0ZWRQbG90VHlwZTogUGxvdFR5cGUgPSBQbG90VHlwZS5JTUFHRTtcbiAgLyoqIElzb3N1cmZhY2UgYmFuZCBhcyBhIDDigJMyNTUgc2xpZGVyIHBvc2l0aW9uIChtYXBwZWQgb250byB0aGUgdm9sdW1lJ3MgcmVhbFxuICAgKiAgaW50ZW5zaXR5IHJhbmdlIGJ5IHRoZSByZW5kZXJlcikuIERlZmF1bHRzIHRvIHRoZSBmdWxsIHJhbmdlLiAqL1xuICBASW5wdXQoKSBpc29SYW5nZTogbnVtYmVyW10gPSBbMCwgMjU1XTtcbiAgQElucHV0KCkgbWF4SW5kZXggPSAwO1xuICBASW5wdXQoKSB6SW5kZXggPSAwO1xuICAvKiogVHJ1ZSBmb3IgYW55IDJEIChub24gU3VyZmFjZS0zRCkgcGxvdCB0eXBlLiAqL1xuICBASW5wdXQoKSBpc0hlYXRtYXAgPSB0cnVlO1xuICBASW5wdXQoKSBhY3RpdmVEcmFnTW9kZTogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIEBJbnB1dCgpIGFjdGl2ZVN1cmZhY2UzZE1vZGUgPSAndHVybnRhYmxlJztcbiAgLyoqIFdoZXRoZXIgdGhlIG5hcGFyaSAzRCBheGVzL3NjYWxlIGdpem1vIGlzIGN1cnJlbnRseSBzaG93biAoZHJpdmVzIHRoZSB0b2dnbGUncyBsb29rKS4gKi9cbiAgQElucHV0KCkgYXhlc1Zpc2libGUgPSB0cnVlO1xuICBASW5wdXQoKSB3aXJlZnJhbWVBY3RpdmUgPSBmYWxzZTtcbiAgLyoqIEFjdGl2ZSBuYXBhcmkgM0QgZGVjaW1hdGUgZmFjdG9yICgxID0gRnVsbCDigKYgOCA9IOKFmzsgZGVmYXVsdCDCvSkuICovXG4gIEBJbnB1dCgpIHJlc29sdXRpb25TY2FsZSA9IE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFO1xuICAvKiogRGVjaW1hdGUtZmFjdG9yIG9wdGlvbnMgZm9yIHRoZSBSZXNvbHV0aW9uIGRyb3Bkb3duLiAqL1xuICByZWFkb25seSBkZWNpbWF0ZU9wdGlvbnMgPSBOQVBBUklfREVDSU1BVEVfT1BUSU9OUztcbiAgQElucHV0KCkgd2FuZFNlbnNpdGl2aXR5ID0gMi4wO1xuICBASW5wdXQoKSBicnVzaFNpemUgPSA0MDtcbiAgQElucHV0KCkgdmVydGV4RXJhc2VyUmFkaXVzID0gMjA7XG4gIC8qKiBXaGV0aGVyIGEgcmVnaW9uIGFjdGlvbiBpcyBhdmFpbGFibGUgdG8gdW5kbyAoaml0LXVpIzg1KS4gR3JleXMgb3V0IHRoZVxuICAgKiAgVW5kbyBidXR0b24gd2hlbiBmYWxzZSAobm90aGluZyBkb25lIHlldCwgb3IgYWxsIHN0ZXBzIGFscmVhZHkgdW5kb25lKS4gKi9cbiAgQElucHV0KCkgY2FuVW5kbyA9IGZhbHNlO1xuICAvKiogV2hldGhlciBhbiB1bmRvbmUgcmVnaW9uIGFjdGlvbiBpcyBhdmFpbGFibGUgdG8gcmVkbyAoaml0LXVpIzg1KS4gR3JleXNcbiAgICogIG91dCB0aGUgUmVkbyBidXR0b24gd2hlbiBmYWxzZS4gKi9cbiAgQElucHV0KCkgY2FuUmVkbyA9IGZhbHNlO1xuICBASW5wdXQoKSBzdGFja09wdGlvbnM6IHsgbmFtZTogc3RyaW5nOyB2YWw6IHN0cmluZyB9W10gPVxuICAgIFt7IG5hbWU6ICdTaW5nbGUgaW1hZ2UnLCB2YWw6ICdmYWxzZScgfSwgeyBuYW1lOiAnU3RhY2snLCB2YWw6ICd0cnVlJyB9XTtcbiAgLyoqIFdoaWNoIHRvb2xiYXIgZ3JvdXBzIHRvIHNob3cuIERlZmF1bHRzIHRvIHRoZSBmdWxsIHRvb2xiYXI7IHRoZSBob3N0IGZvcndhcmRzXG4gICAqICB0aGUgY29uc3VtZXIncyBjaG9pY2UgKGUuZy4gdGhlIHBpcGVsaW5lIHNob3dzIG9ubHkgem9vbSArIHJlZ2lvbiB0b29scykuICovXG4gIEBJbnB1dCgpIHRvb2xzOiBSZXF1aXJlZDxUb29sYmFyVG9vbFZpc2liaWxpdHk+ID0gQUxMX1RPT0xCQVJfVE9PTFM7XG4gIC8qKiBDdXJyZW50IGltYWdlLXNtb290aGluZyBzdGF0ZSAoZHJpdmVzIHRoZSBTbW9vdGhlbiB0b2dnbGUgYnV0dG9uIGxvb2spLlxuICAgKiAgYGZhbHNlYCA9IG5lYXJlc3QtbmVpZ2hib3VyIChjcmlzcCByYXcgcGl4ZWxzKSwgdGhlIGRlZmF1bHQuICovXG4gIEBJbnB1dCgpIGltYWdlU21vb3RoaW5nRW5hYmxlZCA9IGZhbHNlO1xuICAvKiogU0FNIG1vZGVsIHBpY2tlciBvcHRpb25zICsgY3VycmVudCBzZWxlY3Rpb24gKGppdC11aSM5MCBQMSkuICovXG4gIEBJbnB1dCgpIHNhbU1vZGVsczogeyBpZDogc3RyaW5nOyBsYWJlbDogc3RyaW5nIH1bXSA9IFtdO1xuICBASW5wdXQoKSBzYW1Nb2RlbElkID0gJyc7XG4gIC8qKiBZT0xPIGNoZWNrcG9pbnRzIG9mZmVyZWQgYnkgdGhlIGhvc3QncyByZWdpc3RyeSwgYW5kIHRoZSBhY3RpdmUgb25lLiAqL1xuICBASW5wdXQoKSB5b2xvTW9kZWxzOiB7IGlkOiBzdHJpbmc7IGxhYmVsOiBzdHJpbmcgfVtdID0gW107XG4gIEBJbnB1dCgpIHlvbG9Nb2RlbElkID0gJyc7XG4gIC8qKiBSZXRpbmFsLWxheWVyIGNoZWNrcG9pbnRzIG9mZmVyZWQgYnkgdGhlIGhvc3QncyByZWdpc3RyeSwgYW5kIHRoZSBhY3RpdmUgb25lLiAqL1xuICBASW5wdXQoKSByZXRpbmFsTW9kZWxzOiB7IGlkOiBzdHJpbmc7IGxhYmVsOiBzdHJpbmcgfVtdID0gW107XG4gIEBJbnB1dCgpIHJldGluYWxNb2RlbElkID0gJyc7XG5cbiAgQE91dHB1dCgpIHNlbGVjdFBsb3RUeXBlID0gbmV3IEV2ZW50RW1pdHRlcjxQbG90VHlwZT4oKTtcbiAgLyoqIEludGVuc2l0eSAoTElORSkgbW9kZTogYWRkIGFub3RoZXIgY29sb3JlZCBsaW5lIFJPSSArIGluc2V0IHRyYWNlLiAqL1xuICBAT3V0cHV0KCkgYWRkUHJvZmlsZUxpbmUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBUb2dnbGUgaW1hZ2Ugc21vb3RoaW5nIChiaWxpbmVhcikgdnMgbmVhcmVzdC1uZWlnaGJvdXIgKGNyaXNwIHBpeGVscykuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVJbWFnZVNtb290aGluZyA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLy8gUHJpbWVORyBzbGlkZXIvaW5wdXROdW1iZXIgY2hhbmdlIGV2ZW50cyBjYXJyeSBvcHRpb25hbCB2YWx1ZXMsIG1pcnJvcmVkIGJ5XG4gIC8vIHRoZSBob3N0IGhhbmRsZXJzICh3aGljaCBhY2NlcHQgYHwgdW5kZWZpbmVkYCkuXG4gIEBPdXRwdXQoKSBpc29SYW5nZUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyW10gfCB1bmRlZmluZWQ+KCk7XG4gIC8qKiBPcGVuIHRoZSBDaGFubmVscyAmIEhpc3RvZ3JhbSBkaWFsb2cgKGJyaWdodG5lc3MvY29udHJhc3QsIGNvbG9ybWFwLCDigKYpLiAqL1xuICBAT3V0cHV0KCkgb3BlbkNoYW5uZWxIaXN0b2dyYW0gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSBvcGVuUmVnaW9uRWRpdG9yID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgc2VsZWN0U3RhY2tPcHRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHsgbmFtZTogc3RyaW5nOyB2YWw6IHN0cmluZyB9PigpO1xuICBAT3V0cHV0KCkgelNjcnViID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIEBPdXRwdXQoKSB6U2xpZGUgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcbiAgQE91dHB1dCgpIHpJbmRleElucHV0ID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG4gIEBPdXRwdXQoKSByZWxvYWRBbmRQbG90ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgZG93bmxvYWRJbWFnZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIGF1dG9zY2FsZUltYWdlID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgdG9nZ2xlRHJhZ01vZGUgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgQE91dHB1dCgpIHpvb21JbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIHpvb21PdXQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSB0b2dnbGVTdXJmYWNlM2RNb2RlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIEBPdXRwdXQoKSByZXNldFN1cmZhY2VDYW1lcmEgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBUb2dnbGUgdGhlIG5hcGFyaSAzRCBjb29yZGluYXRlLWF4ZXMgLyBzY2FsZSBnaXptbyBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVBeGVzID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogVG9nZ2xlIHRoZSBuYXBhcmkgc3VyZmFjZSB3aXJlZnJhbWUgKGVkZ2VzKSBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVXaXJlZnJhbWUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBDaGFuZ2UgdGhlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKHJlbG9hZHMgYXQgdGhlIG5ldyByZXNvbHV0aW9uKS4gKi9cbiAgQE91dHB1dCgpIHNlbGVjdFJlc29sdXRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlcj4oKTtcbiAgQE91dHB1dCgpIGRlbGV0ZVJlZ2lvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFVuZG8gdGhlIGxhc3QgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gKi9cbiAgQE91dHB1dCgpIHVuZG9SZWdpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBSZWRvIHRoZSBsYXN0IHVuZG9uZSByZWdpb24gYWN0aW9uIChqaXQtdWkjODUpLiAqL1xuICBAT3V0cHV0KCkgcmVkb1JlZ2lvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biBib3gtcHJvbXB0ZWQgU0FNIHNlZ21lbnRhdGlvbiBvbiB0aGUgZHJhd24gcmVjdGFuZ2xlcyAoaml0LXVpIzkwKS4gKi9cbiAgQE91dHB1dCgpIHNlZ21lbnRSZWdpb25zID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogUnVuIGNlbGxwb3NlLVNBTSAoYXV0bykgaW5zaWRlIGVhY2ggZHJhd24gcmVjdGFuZ2xlJ3MgY3JvcCAoaml0LXVpIzkwKS4gKi9cbiAgQE91dHB1dCgpIHNlZ21lbnRDZWxscG9zZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biBZT0xPIGRldGVjdGlvbiBvdmVyIHRoZSBjdXJyZW50IHZpZXcuICovXG4gIEBPdXRwdXQoKSBkZXRlY3RZb2xvID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogUGljayB0aGUgWU9MTyBjaGVja3BvaW50LiAqL1xuICBAT3V0cHV0KCkgeW9sb01vZGVsQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIC8qKiBPcGVuIHRoZSBZT0xPIHBhcmFtZXRlciBkaWFsb2cuICovXG4gIEBPdXRwdXQoKSBvcGVuWW9sb1BhcmFtcyA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biByZXRpbmFsLWxheWVyIHNlZ21lbnRhdGlvbiBvdmVyIHRoZSBjdXJyZW50IHZpZXcuICovXG4gIEBPdXRwdXQoKSBzZWdtZW50UmV0aW5hbCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFBpY2sgdGhlIHJldGluYWwtbGF5ZXIgY2hlY2twb2ludC4gKi9cbiAgQE91dHB1dCgpIHJldGluYWxNb2RlbENoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuICAvKiogT3BlbiB0aGUgcmV0aW5hbC1sYXllciBwYXJhbWV0ZXIgZGlhbG9nLiAqL1xuICBAT3V0cHV0KCkgb3BlblJldGluYWxQYXJhbXMgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIChqaXQtdWkjOTAgUDEpLiAqL1xuICBAT3V0cHV0KCkgc2FtTW9kZWxDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgQE91dHB1dCgpIHdhbmRTZW5zaXRpdml0eUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyIHwgdW5kZWZpbmVkPigpO1xuICBAT3V0cHV0KCkgYnJ1c2hTaXplQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIEBPdXRwdXQoKSB2ZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcblxuICAvKiogU2xpZGVycycgYm91bmRzIOKAlCB0b29sYmFyIFVJIGNvbnN0YW50cyAodGhlIHZhbHVlcyBhcmUgaG9zdC1vd25lZCkuICovXG4gIHJlYWRvbmx5IHdhbmRTZW5zaXRpdml0eU1pbiA9IDAuNTtcbiAgcmVhZG9ubHkgd2FuZFNlbnNpdGl2aXR5TWF4ID0gMTAuMDtcbiAgcmVhZG9ubHkgd2FuZFNlbnNpdGl2aXR5U3RlcCA9IDAuMTtcbiAgcmVhZG9ubHkgYnJ1c2hTaXplTWluID0gNTtcbiAgcmVhZG9ubHkgYnJ1c2hTaXplTWF4ID0gMzAwO1xuICByZWFkb25seSBicnVzaFNpemVTdGVwID0gNTtcbiAgcmVhZG9ubHkgdmVydGV4RXJhc2VyUmFkaXVzTWluID0gNTtcbiAgcmVhZG9ubHkgdmVydGV4RXJhc2VyUmFkaXVzTWF4ID0gMzAwO1xuICByZWFkb25seSB2ZXJ0ZXhFcmFzZXJSYWRpdXNTdGVwID0gNTtcbiAgcmVhZG9ubHkgaXNvVmFsdWVNaW4gPSAwO1xuICByZWFkb25seSBpc29WYWx1ZU1heCA9IDI1NTtcbiAgcmVhZG9ubHkgaXNvVmFsdWVTdGVwID0gMTtcblxuICBkaXNwbGF5SGVscERpYWxvZyA9IGZhbHNlO1xuXG4gIC8qKiBNb2RlbCBwaWNrZXIgZm9yIHRoZSBTZWdtZW50IGJ1dHRvbidzIGRyb3Bkb3duIG1lbnUuIFRoZSBhY3RpdmUgbW9kZWxcbiAgICogIChgc2FtTW9kZWxJZGApIGlzIG1hcmtlZCB3aXRoIGEgY2hlY2s7IHNlbGVjdGluZyBhbiBpdGVtIGVtaXRzXG4gICAqICBgc2FtTW9kZWxDaGFuZ2VgIChqaXQtdWkjOTAgUDEpLlxuICAgKlxuICAgKiAgSGVsZCBhcyBhIHN0YWJsZSBhcnJheSAocmVidWlsdCBvbmx5IHdoZW4gYHNhbU1vZGVsc2AvYHNhbU1vZGVsSWRgIGNoYW5nZSksXG4gICAqICBOT1QgYSBnZXR0ZXI6IGEgZ2V0dGVyIHJldHVybnMgYSBmcmVzaCBhcnJheSB3aXRoIG5ldyBgY29tbWFuZGAgY2xvc3VyZXMgb25cbiAgICogIGV2ZXJ5IGNoYW5nZS1kZXRlY3Rpb24gdGljaywgd2hpY2ggbWFrZXMgdGhlIGJvdW5kIGBwLW1lbnVgIG92ZXJsYXkgcmVidWlsZFxuICAgKiAgaXRzIERPTSBtaWQtaW50ZXJhY3Rpb24gYW5kIHN3YWxsb3cgdGhlIGNsaWNrIG9uIGEgbWVudSBpdGVtLiAqL1xuICBzYW1NZW51SXRlbXM6IE1lbnVJdGVtW10gPSBbXTtcbiAgLyoqIFNhbWUgc3RhYmxlLWFycmF5IHJlYXNvbmluZyBhcyB7QGxpbmsgc2FtTWVudUl0ZW1zfS4gKi9cbiAgeW9sb01lbnVJdGVtczogTWVudUl0ZW1bXSA9IFtdO1xuICAvKiogU2FtZSBzdGFibGUtYXJyYXkgcmVhc29uaW5nIGFzIHtAbGluayBzYW1NZW51SXRlbXN9LiAqL1xuICByZXRpbmFsTWVudUl0ZW1zOiBNZW51SXRlbVtdID0gW107XG5cbiAgLyoqIFJlYnVpbGQgdGhlIFNBTSBtb2RlbCBtZW51IHdoZW4gdGhlIG1vZGVsIGxpc3Qgb3IgYWN0aXZlIHNlbGVjdGlvblxuICAgKiAgY2hhbmdlcyAoa2VlcHMgdGhlIGFycmF5IHJlZmVyZW5jZSBzdGFibGUgYWNyb3NzIG90aGVyIENEIHRpY2tzKS4gKi9cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIGlmIChjaGFuZ2VzWydzYW1Nb2RlbHMnXSB8fCBjaGFuZ2VzWydzYW1Nb2RlbElkJ10pIHtcbiAgICAgIHRoaXMuc2FtTWVudUl0ZW1zID0gdGhpcy5zYW1Nb2RlbHMubWFwKChtKSA9PiAoe1xuICAgICAgICBsYWJlbDogbS5sYWJlbCxcbiAgICAgICAgaWNvbjogbS5pZCA9PT0gdGhpcy5zYW1Nb2RlbElkID8gJ3BpIHBpLWNoZWNrJyA6ICdwaSBwaS1mdycsXG4gICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2FtTW9kZWxDaGFuZ2UuZW1pdChtLmlkKSxcbiAgICAgIH0pKTtcbiAgICB9XG4gICAgaWYgKGNoYW5nZXNbJ3lvbG9Nb2RlbHMnXSB8fCBjaGFuZ2VzWyd5b2xvTW9kZWxJZCddKSB7XG4gICAgICB0aGlzLnlvbG9NZW51SXRlbXMgPSB0aGlzLnlvbG9Nb2RlbHMubWFwKChtKSA9PiAoe1xuICAgICAgICBsYWJlbDogbS5sYWJlbCxcbiAgICAgICAgaWNvbjogbS5pZCA9PT0gdGhpcy55b2xvTW9kZWxJZCA/ICdwaSBwaS1jaGVjaycgOiAncGkgcGktZncnLFxuICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnlvbG9Nb2RlbENoYW5nZS5lbWl0KG0uaWQpLFxuICAgICAgfSkpO1xuICAgIH1cbiAgICBpZiAoY2hhbmdlc1sncmV0aW5hbE1vZGVscyddIHx8IGNoYW5nZXNbJ3JldGluYWxNb2RlbElkJ10pIHtcbiAgICAgIHRoaXMucmV0aW5hbE1lbnVJdGVtcyA9IHRoaXMucmV0aW5hbE1vZGVscy5tYXAoKG0pID0+ICh7XG4gICAgICAgIGxhYmVsOiBtLmxhYmVsLFxuICAgICAgICBpY29uOiBtLmlkID09PSB0aGlzLnJldGluYWxNb2RlbElkID8gJ3BpIHBpLWNoZWNrJyA6ICdwaSBwaS1mdycsXG4gICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMucmV0aW5hbE1vZGVsQ2hhbmdlLmVtaXQobS5pZCksXG4gICAgICB9KSk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFRoZSBJbWFnZSBwbG90IHR5cGUgcmVuZGVycyBhcyBhIG5hdGl2ZWx5IHBhbi96b29tLWFibGUgcmFzdGVyLCBzbyB0aGVcbiAgICogIGdlbmVyaWMgem9vbS9wYW4gdG9vbHMgYXJlIGhpZGRlbi4gKi9cbiAgZ2V0IGlzSW1hZ2VWaWV3KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnNlbGVjdGVkUGxvdFR5cGUgPT09IFBsb3RUeXBlLklNQUdFO1xuICB9XG5cbiAgLyoqIEEgcGxvdC10eXBlIGljb24gaXMgYSBQcmltZU5HIGZvbnQgZ2x5cGggKGUuZy4gYHBpIHBpLWltYWdlYCkgcmF0aGVyIHRoYW4gYW5cbiAgICogIFNWRyBhc3NldCBwYXRoIOKAlCBkcml2ZXMgd2hpY2ggZWxlbWVudCB0aGUgc2VsZWN0b3IgaXRlbSB0ZW1wbGF0ZSByZW5kZXJzLiAqL1xuICBpc1BpSWNvbihpY29uOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISFpY29uICYmIGljb24uc3RhcnRzV2l0aCgncGkgJyk7XG4gIH1cblxuICAvKiogQmFja2VuZHMgd2l0aCBhIHZlcnRleC1lZGl0aW5nIHJlZ2lvbiBvdmVybGF5OiBPU0QgSW1hZ2UgYW5kIG5hcGFyaS1qcyBXZWJHUFUgaW1hZ2VcbiAgICogIChqaXQtdWkjMTAyKS4gR2F0ZXMgdGhlIHBvbHlnb24gLyBhZGQtdmVydGV4IC8gZGVsZXRlLXZlcnRleCAvIELDqXppZXItY29udmVydCB0b29scy4gKi9cbiAgZ2V0IHN1cHBvcnRzUmVnaW9uVmVydGV4VG9vbHMoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChcbiAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0UgfHxcbiAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBQbG90IHR5cGVzIHRoYXQgc2NydWIgYSB6LXN0YWNrIGxpdmUgKHRoZSByZW5kZXJlciBzd2FwcyB0aGUgc2xpY2UgaW4gcGxhY2UpOiB0aGUgT1NEIEltYWdlXG4gICAqICB2aWV3LCB0aGUgbmFwYXJpLWpzIFdlYkdQVSBpbWFnZSwgYW5kIHRoZSBuYXBhcmktanMgc3VyZmFjZSDigJQgd2hpY2ggcmVidWlsZHMgdGhlIGhlaWdodCBmaWVsZFxuICAgKiAgZnJvbSB0aGUgcGlja2VkIHNsaWNlIChqaXQtdWkjMTAyKS4gRHJpdmVzIHRoZSBwZXItc2xpY2Ugc2xpZGVyLiAqL1xuICBnZXQgc2hvd3NMaXZlU2xpY2VTY3J1YmJlcigpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRSB8fFxuICAgICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlID09PSBQbG90VHlwZS5OQVBBUklfSU1BR0UgfHxcbiAgICAgIGlzTmFwYXJpU3VyZmFjZSh0aGlzLnNlbGVjdGVkUGxvdFR5cGUpIHx8XG4gICAgICBpc05hcGFyaVNjYXR0ZXIodGhpcy5zZWxlY3RlZFBsb3RUeXBlKVxuICAgICk7XG4gIH1cblxuICAvKiogQW55IG5hcGFyaS1qcyBXZWJHUFUgcGxvdCB0eXBlLiBUaGUgc2luZ2xlLWltYWdlL3N0YWNrIHRvZ2dsZSArIHNsaWNlLW51bWJlciBmaWVsZCBhcmVcbiAgICogIFBsb3RseS1vbmx5IHN0YWNrIGNvbnRyb2xzLCBzbyB0aGV5J3JlIGhpZGRlbiBmb3IgbmFwYXJpIChqaXQtdWkjMTAyKS4gKi9cbiAgZ2V0IGlzTmFwYXJpTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlID09PSBQbG90VHlwZS5OQVBBUklfSU1BR0UgfHxcbiAgICAgIGlzTmFwYXJpU2NhdHRlcih0aGlzLnNlbGVjdGVkUGxvdFR5cGUpIHx8XG4gICAgICBpc05hcGFyaTNkKHRoaXMuc2VsZWN0ZWRQbG90VHlwZSlcbiAgICApO1xuICB9XG5cbiAgLyoqIElTT1NVUkZBQ0UgKFBsb3RseSBvciBuYXBhcmktanMgV2ViR1BVLCBlaXRoZXIgcmVzb2x1dGlvbikgc2hvd3MgdGhlIGlzb3ZhbHVlIHJhbmdlIHNsaWRlci4gKi9cbiAgZ2V0IGlzSXNvc3VyZmFjZU1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChcbiAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9PT0gUGxvdFR5cGUuSVNPU1VSRkFDRSB8fCBpc05hcGFyaUlzb3N1cmZhY2UodGhpcy5zZWxlY3RlZFBsb3RUeXBlKVxuICAgICk7XG4gIH1cblxuICAvKiogVGhlIG5hcGFyaS1qcyBXZWJHUFUgc3VyZmFjZSDigJQgc2hvd3MgdGhlIHdpcmVmcmFtZSB0b2dnbGUuICovXG4gIGdldCBpc05hcGFyaVN1cmZhY2VNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc05hcGFyaVN1cmZhY2UodGhpcy5zZWxlY3RlZFBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBBbnkgbmFwYXJpLWpzIFdlYkdQVSAzRCB0eXBlICh2b2x1bWUvaXNvc3VyZmFjZS9zdXJmYWNlKSDigJQgc2hvd3MgdGhlIFJlc29sdXRpb24gY29udHJvbC4gKi9cbiAgZ2V0IGlzTmFwYXJpM2RNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc05hcGFyaTNkKHRoaXMuc2VsZWN0ZWRQbG90VHlwZSk7XG4gIH1cblxuICAvKiogSW50ZW5zaXR5IHByb2ZpbGUgbGluZXMgYXJlIFJlZ2lvbi1iYXNlZCBhbmQgYXZhaWxhYmxlIGluIHRoZSBIZWF0bWFwIGFuZFxuICAgKiAgSW1hZ2UgcGxvdCB0eXBlcywgd2hpY2ggc2hvdyB0aGUgSW50ZW5zaXR5IHRvb2wgZ3JvdXAuICovXG4gIGdldCBpc0ludGVuc2l0eUNhcGFibGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9PT0gUGxvdFR5cGUuSEVBVE1BUCB8fCB0aGlzLnNlbGVjdGVkUGxvdFR5cGUgPT09IFBsb3RUeXBlLklNQUdFO1xuICB9XG5cbiAgc2hvd0hlbHAoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5SGVscERpYWxvZyA9IHRydWU7XG4gIH1cbn1cbiIsIjxwLXRvb2xiYXI+XG4gIDxkaXYgY2xhc3M9XCJwLXRvb2xiYXItZ3JvdXAtc3RhcnQgZmxleC13cmFwIGFsaWduLWl0ZW1zLWNlbnRlciBnYXAtMVwiPlxuXG4gICAgPCEtLSBIb3N0LXByb3ZpZGVkIGN1c3RvbSB0b29sYmFyIGJ1dHRvbnMsIHNob3duIGZpcnN0IChlLmcuIGFwcC1zcGVjaWZpY1xuICAgICAgICAgYWN0aW9ucyB0aGUgdmlzdWFsaXphdGlvbiB2aWV3IGl0c2VsZiBkb2Vzbid0IG93bikuIC0tPlxuICAgIDxuZy1jb250ZW50PjwvbmctY29udGVudD5cblxuICAgIDwhLS0gUGxvdCB0eXBlOiBhdmFpbGFibGUgZm9yIGFueSBpbWFnZSAoZ3JheXNjYWxlIG9yIFJHQikuIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpbWFnZUluZm8gJiYgdG9vbHMuc3BlY2lhbFRvb2xzXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgPHAtZHJvcGRvd24gW29wdGlvbnNdPVwicGxvdFR5cGVPcHRpb25zXCIgb3B0aW9uTGFiZWw9XCJsYWJlbFwiIG9wdGlvblZhbHVlPVwidHlwZVwiXG4gICAgICAgICAgICAgICAgICBbKG5nTW9kZWwpXT1cInNlbGVjdGVkUGxvdFR5cGVcIiBhcHBlbmRUbz1cImJvZHlcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTZWxlY3QgcGxvdCB0eXBlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIFtzdHlsZV09XCJ7J21pbi13aWR0aCc6ICcxMXJlbSd9XCJcbiAgICAgICAgICAgICAgICAgIHN0eWxlQ2xhc3M9XCJwbG90bW9kZS1kcm9wZG93blwiIHBhbmVsU3R5bGVDbGFzcz1cInBsb3Rtb2RlLXBhbmVsXCJcbiAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJzZWxlY3RQbG90VHlwZS5lbWl0KCRldmVudC52YWx1ZSlcIj5cbiAgICAgICAgPG5nLXRlbXBsYXRlIGxldC1vcHQgcFRlbXBsYXRlPVwic2VsZWN0ZWRJdGVtXCI+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJwbG90bW9kZS1pdGVtXCIgKm5nSWY9XCJvcHRcIj5cbiAgICAgICAgICAgIDxpICpuZ0lmPVwiaXNQaUljb24ob3B0Lmljb24pXCIgW2NsYXNzXT1cIm9wdC5pY29uXCI+PC9pPlxuICAgICAgICAgICAgPGltZyAqbmdJZj1cIm9wdC5pY29uICYmICFpc1BpSWNvbihvcHQuaWNvbilcIiBjbGFzcz1cInBsb3Rtb2RlLWljb24taW1nXCIgW3NyY109XCJvcHQuaWNvblwiIGFsdD1cIlwiIC8+XG4gICAgICAgICAgICA8c3Bhbj57eyBvcHQubGFiZWwgfX08L3NwYW4+XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICA8bmctdGVtcGxhdGUgbGV0LW9wdCBwVGVtcGxhdGU9XCJpdGVtXCI+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJwbG90bW9kZS1pdGVtXCI+XG4gICAgICAgICAgICA8aSAqbmdJZj1cImlzUGlJY29uKG9wdC5pY29uKVwiIFtjbGFzc109XCJvcHQuaWNvblwiPjwvaT5cbiAgICAgICAgICAgIDxpbWcgKm5nSWY9XCJvcHQuaWNvbiAmJiAhaXNQaUljb24ob3B0Lmljb24pXCIgY2xhc3M9XCJwbG90bW9kZS1pY29uLWltZ1wiIFtzcmNdPVwib3B0Lmljb25cIiBhbHQ9XCJcIiAvPlxuICAgICAgICAgICAgPHNwYW4+e3sgb3B0LmxhYmVsIH19PC9zcGFuPlxuICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgIDwvcC1kcm9wZG93bj5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc0lzb3N1cmZhY2VNb2RlXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5Jc288L3NwYW4+XG4gICAgICAgIDxwLXNsaWRlciBbKG5nTW9kZWwpXT1cImlzb1JhbmdlXCIgW3JhbmdlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgW21pbl09XCJpc29WYWx1ZU1pblwiIFttYXhdPVwiaXNvVmFsdWVNYXhcIiBbc3RlcF09XCJpc29WYWx1ZVN0ZXBcIlxuICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cImlzb1JhbmdlQ2hhbmdlLmVtaXQoJGV2ZW50LnZhbHVlcylcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJJc29zdXJmYWNlIGJhbmQgKDDigJMyNTUpLCBtYXBwZWQgb250byB0aGUgdm9sdW1lJ3NcbiAgICAgICAgICAgICAgICAgIGFjdHVhbCBpbnRlbnNpdHkgcmFuZ2UuIFN1cmZhY2VzIGFyZSBkcmF3biBmb3Igdm94ZWxzIHdob3NlXG4gICAgICAgICAgICAgICAgICBpbnRlbnNpdHkgZmFsbHMgaW4gdGhlIHNlbGVjdGVkIGJhbmQg4oCUIG5hcnJvdyBpdCB0byBpc29sYXRlXG4gICAgICAgICAgICAgICAgICBmZWF0dXJlcywgd2lkZW4gaXQgdG8gc2VlIHRoZSB3aG9sZSBzdHJ1Y3R1cmUuXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgc3R5bGVDbGFzcz1cIndhbmQtc2Vuc2l0aXZpdHktc2xpZGVyXCI+PC9wLXNsaWRlcj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dCB3YW5kLXNlbnNpdGl2aXR5LXZhbHVlXCI+XG4gICAgICAgICAge3sgaXNvUmFuZ2VbMF0gfX3igJN7eyBpc29SYW5nZVsxXSB9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPCEtLSBJbnRlbnNpdHkgdG9vbHMgKEhlYXRtYXAgLyBJbWFnZSBtb2RlKTogYWRkIGFub3RoZXIgY29sb3JlZCBsaW5lIFJPSS5cbiAgICAgICAgICAgRWFjaCBhZGRlZCBsaW5lIGdldHMgdGhlIG5leHQgYnJpZ2h0IHBhbGV0dGUgY29sb3VyIGFuZCBpdHMgb3duIGluc2V0XG4gICAgICAgICAgIHRyYWNlOyBzZWxlY3QgYSBsaW5lIG9uIHRoZSBpbWFnZSBhbmQgdXNlIHRoZSB0cmFzaCB0b29sIHRvIHJlbW92ZSBpdFxuICAgICAgICAgICAoYW5kIGl0cyB0cmFjZSkuIFRoZSBsaW5lcyBhcmUgUmVnaW9uLWJhc2VkIGJ1dCBleGNsdWRlZCBmcm9tIHRoZVxuICAgICAgICAgICBSZWdpb24gRWRpdG9yIGFuZCBleHBvcnRzLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc0ludGVuc2l0eUNhcGFibGVcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJBZGQgYW4gaW50ZW5zaXR5IGxpbmUgKG5ldyBjb2xvdXIpLiBDbGljayBhIGxpbmUgdG8gc2VsZWN0IGl0LFxuICAgICAgICAgICAgICAgICAgdGhlbiB1c2UgdGhlIHRyYXNoIHRvb2wgdG8gcmVtb3ZlIGl0LlwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cImFkZFByb2ZpbGVMaW5lLmVtaXQoKVwiPlxuICAgICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NwbGluZS1jaGFydC5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwhLS0gQ2hhbm5lbHMgJiBIaXN0b2dyYW06IGJyaWdodG5lc3MvY29udHJhc3QsIGdhbW1hLCBwZXItY2hhbm5lbCBkaXNwbGF5LFxuICAgICAgICAgY29sb3JtYXAvcmV2ZXJzZS9pbnZlcnQg4oCUIG9wZW5zIHRoZSBmbG9hdGluZyBkaWFsb2cuIC0tPlxuICAgIDxwLWJ1dHRvbiAqbmdJZj1cInRvb2xzLnNwZWNpYWxUb29sc1wiIHBSaXBwbGUgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgIHBUb29sdGlwPVwiQ2hhbm5lbHMgJmFtcDsgSGlzdG9ncmFtIChicmlnaHRuZXNzL2NvbnRyYXN0LCBjb2xvcm1hcClcIlxuICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAob25DbGljayk9XCJvcGVuQ2hhbm5lbEhpc3RvZ3JhbS5lbWl0KClcIj5cbiAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL2ludmVydC5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICA8L3AtYnV0dG9uPlxuXG4gICAgPCEtLSBTbW9vdGhlbiB0b2dnbGU6IGJpbGluZWFyIHNtb290aGluZyB2cyBuZWFyZXN0LW5laWdoYm91ciByYXcgcGl4ZWxzIChJbWFnZVxuICAgICAgICAgdmlldyBvbmx5IOKAlCBpdCdzIGFuIE9wZW5TZWFkcmFnb24gZHJhd2VyIHNldHRpbmcpLiBIaWdobGlnaHRlZCB3aGVuIHNtb290aGluZyBvbi4gLS0+XG4gICAgPHAtYnV0dG9uICpuZ0lmPVwidG9vbHMuc3BlY2lhbFRvb2xzICYmIGlzSW1hZ2VWaWV3XCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgW3RleHRdPVwiIWltYWdlU21vb3RoaW5nRW5hYmxlZFwiXG4gICAgICAgICAgICAgIHBUb29sdGlwPVwiU21vb3RoZW4gKHR1cm4gb24gaW1hZ2Ugc21vb3RoaW5nKVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZUltYWdlU21vb3RoaW5nLmVtaXQoKVwiPlxuICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc21vb3RoLnN2Z1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIlxuICAgICAgICAgICBbY2xhc3NdPVwiaW1hZ2VTbW9vdGhpbmdFbmFibGVkID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiLz5cbiAgICA8L3AtYnV0dG9uPlxuXG4gICAgPCEtLSBTdGFjayBjb250cm9scyAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaW1hZ2VJbmZvPy5pc1N0YWNrXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgPCEtLSBJbWFnZSB2aWV3OiBzY3J1YiB6LXNsaWNlcyBsaXZlICh0aGUgcmVuZGVyZXIgc3dhcHMgc2xpY2VzIGluIHBsYWNlKS4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwic2hvd3NMaXZlU2xpY2VTY3J1YmJlclwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+U2xpY2U8L3NwYW4+XG4gICAgICAgIDxwLXNsaWRlciBbKG5nTW9kZWwpXT1cInpJbmRleFwiIFttaW5dPVwiMFwiIFttYXhdPVwibWF4SW5kZXhcIlxuICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cInpTY3J1Yi5lbWl0KCRldmVudC52YWx1ZSlcIiAob25TbGlkZUVuZCk9XCJ6U2xpZGUuZW1pdCgkZXZlbnQudmFsdWUpXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU2xpY2UgdGhyb3VnaCB0aGUgc3RhY2sgKOKGkCAvIOKGkiBrZXlzKVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICBbc3R5bGVdPVwieyd3aWR0aCc6JzE2cmVtJ31cIiBzdHlsZUNsYXNzPVwid2FuZC1zZW5zaXRpdml0eS1zbGlkZXJcIj48L3Atc2xpZGVyPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+e3t6SW5kZXh9fSBvZiB7e21heEluZGV4fX08L3NwYW4+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgIDwhLS0gSGVhdG1hcCB2aWV3OiBzaW5nbGUtaW1hZ2UgKGhpZ2gtZGVmIHpvb20pIHZzIHN0YWNrIChmcmFtZSBzbGlkZXIpLiBQbG90bHktb25seSDigJRcbiAgICAgICAgICAgaGlkZGVuIGZvciBuYXBhcmksIHdoaWNoIGhhcyBubyBzaW5nbGUvc3RhY2sgdG9nZ2xlIG9yIHBlci1mcmFtZSBmZXRjaC4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiIXNob3dzTGl2ZVNsaWNlU2NydWJiZXIgJiYgIWlzTmFwYXJpTW9kZVwiPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1pbWFnZVwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiaW1hZ2VJbmZvPy5zaG93U3RhY2tcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU2luZ2xlIGltYWdlIG1vZGU6IGVuYWJsZXMgaGlnaC1kZWYgem9vbVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2VsZWN0U3RhY2tPcHRpb24uZW1pdChzdGFja09wdGlvbnNbMF0pXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1pbWFnZXNcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cIiFpbWFnZUluZm8/LnNob3dTdGFja1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTdGFjayBtb2RlLCBlbmFibGVzIHNsaWNpbmcgdGhyb3VnaCBpbWFnZSBmcmFtZXNcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNlbGVjdFN0YWNrT3B0aW9uLmVtaXQoc3RhY2tPcHRpb25zWzFdKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIhaW1hZ2VJbmZvPy5zaG93U3RhY2tcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPlNsaWNlPC9zcGFuPlxuICAgICAgICA8ZGl2IGNsYXNzPVwicC1pbnB1dGdyb3VwXCIgc3R5bGU9XCJ3aWR0aDogYXV0b1wiPlxuICAgICAgICAgIDxwLWlucHV0TnVtYmVyIHN0eWxlQ2xhc3M9XCJ3LTNyZW0gdG9vbGJhci1pbnB1dFwiXG5cdFx0XHRcdFx0cFRvb2x0aXA9XCJWaWV3IGltYWdlIGluIHN0YWNrICgmI3gyM0NFOylcIlxuXHRcdFx0XHRcdFsobmdNb2RlbCldPVwiekluZGV4XCIgW21pbl09XCIwXCIgW21heF09XCJtYXhJbmRleFwiXG4gICAgICAgICAgIChvbklucHV0KT1cInpJbmRleElucHV0LmVtaXQoekluZGV4KVwiIChrZXlkb3duLmVudGVyKT1cInJlbG9hZEFuZFBsb3QuZW1pdCgpXCIvPlxuICAgICAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktcmVmcmVzaFwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIiAob25DbGljayk9XCJyZWxvYWRBbmRQbG90LmVtaXQoKVwiXG4gICAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUGxvdCB0aGlzIHNsaWNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCI+XG4gICAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5vZiB7e21heEluZGV4fX08L3NwYW4+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8IS0tIEltYWdlIGFjdGlvbnMgKHNwZWNpYWwgdG9vbHMpIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJ0b29scy5zcGVjaWFsVG9vbHNcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWRvd25sb2FkXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEb3dubG9hZCBpbWFnZSBhcyBQTkdcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cImRvd25sb2FkSW1hZ2UuZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uICpuZ0lmPVwiaXNIZWF0bWFwXCIgaWNvbj1cInBpIHBpLXdpbmRvdy1tYXhpbWl6ZVwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRml0IHRvIHZpZXdcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cImF1dG9zY2FsZUltYWdlLmVtaXQoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDwhLS0gWm9vbSB0b29scy4gUGxvdGx5IHJ1YmJlci1iYW5kIGRyYWctem9vbTogb24gT3BlblNlYWRyYWdvbiB0aGUgYm94LXpvb21cbiAgICAgICAgIGNvdmVycyB0aGlzIGFuZCBhIGJhcmUgZHJhZyBwYW5zIG5hdGl2ZWx5LCBzbyBpdCdzIGhpZGRlbiBpbiBpbWFnZSB2aWV3LiAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNIZWF0bWFwICYmIHRvb2xzLnpvb21Ub29sc1wiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDxwLWJ1dHRvbiAqbmdJZj1cIiFpc0ltYWdlVmlld1wiIGljb249XCJwaSBwaS1zZWFyY2hcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnem9vbSdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiWm9vbSBkcmFnXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCd6b29tJylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ3pvb21Ub0JveCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiWm9vbSBib3ggKDxrYmQ+Yjwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCd6b29tVG9Cb3gnKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy96b29tLWJveC5zdmdcIlxuICAgICAgICAgICAgIFtjbGFzc109XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ3pvb21Ub0JveCcgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktYXJyb3dzLWFsdFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdwYW4nXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlBhbiB0aGUgaW1hZ2UgKDxrYmQ+cDwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdwYW4nKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktc2VhcmNoLXBsdXNcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlpvb20gaW4gKDxrYmQ+Kzwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ6b29tSW4uZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1zZWFyY2gtbWludXNcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlpvb20gb3V0ICg8a2JkPi08L2tiZD4pXCIgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiem9vbU91dC5lbWl0KClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8IS0tIFN1cmZhY2UgM0QgY29udHJvbHMgLS0+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc0hlYXRtYXBcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXNlYXJjaFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlU3VyZmFjZTNkTW9kZSAhPT0gJ3pvb20nXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlpvb21cIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZVN1cmZhY2UzZE1vZGUuZW1pdCgnem9vbScpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1hcnJvd3MtYWx0XCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVTdXJmYWNlM2RNb2RlICE9PSAncGFuJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJQYW5cIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZVN1cmZhY2UzZE1vZGUuZW1pdCgncGFuJylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWdsb2JlXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVTdXJmYWNlM2RNb2RlICE9PSAnb3JiaXQnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIk9yYml0YWwgcm90YXRpb25cIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZVN1cmZhY2UzZE1vZGUuZW1pdCgnb3JiaXQnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktc3luY1wiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlU3VyZmFjZTNkTW9kZSAhPT0gJ3R1cm50YWJsZSdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiVHVybnRhYmxlIHJvdGF0aW9uXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVTdXJmYWNlM2RNb2RlLmVtaXQoJ3R1cm50YWJsZScpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1ob21lXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJSZXNldCBjYW1lcmEgdG8gZGVmYXVsdFwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwicmVzZXRTdXJmYWNlQ2FtZXJhLmVtaXQoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwhLS0gM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gdG9nZ2xlIChuYXBhcmktanMgV2ViR1BVIHZvbHVtZS9pc29zdXJmYWNlIG9ubHkpLlxuICAgICAgICAgICBgcC1idXR0b24taWNvbi1vbmx5YCBnaXZlcyB0aGUgc2FtZSBzcXVhcmUgc2l6aW5nIGFzIHRoZSBuZWlnaGJvdXJpbmcgcGktaWNvbiBidXR0b25zXG4gICAgICAgICAgICh0aGUgcHJvamVjdGVkIDxpbWc+IGRvZXNuJ3QgdHJpZ2dlciBQcmltZU5HJ3MgYXV0b21hdGljIGljb24tb25seSBzaXppbmcpLiAtLT5cbiAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiAqbmdJZj1cImlzTmFwYXJpTW9kZVwiIHN0eWxlQ2xhc3M9XCJwLWJ1dHRvbi1pY29uLW9ubHlcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cIiFheGVzVmlzaWJsZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJUb2dnbGUgM0QgYXhlcyAvIHNjYWxlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVBeGVzLmVtaXQoKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy8zZC1jb29yZGluYXRlcy5zdmdcIlxuICAgICAgICAgICAgIFtjbGFzc109XCJheGVzVmlzaWJsZSA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPCEtLSBTdXJmYWNlIHdpcmVmcmFtZSB0b2dnbGUgKG5hcGFyaS1qcyBXZWJHUFUgc3VyZmFjZSBvbmx5KS4gLS0+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXNoYXJlLWFsdFwiIHNpemU9XCJzbWFsbFwiICpuZ0lmPVwiaXNOYXBhcmlTdXJmYWNlTW9kZVwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiIXdpcmVmcmFtZUFjdGl2ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJUb2dnbGUgd2lyZWZyYW1lXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVXaXJlZnJhbWUuZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPCEtLSBSZXNvbHV0aW9uIChkZWNpbWF0ZSBmYWN0b3IpIGZvciB0aGUgbmFwYXJpIDNEIHR5cGVzIOKAlCByZWxvYWRzIGF0IGNvYXJzZXIvZmluZXIgc2FtcGxpbmcuIC0tPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImlzTmFwYXJpM2RNb2RlXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5SZXM8L3NwYW4+XG4gICAgICAgIDxwLWRyb3Bkb3duIFtvcHRpb25zXT1cImRlY2ltYXRlT3B0aW9uc1wiIG9wdGlvbkxhYmVsPVwibGFiZWxcIiBvcHRpb25WYWx1ZT1cInZhbHVlXCJcbiAgICAgICAgICAgICAgICAgICAgW25nTW9kZWxdPVwicmVzb2x1dGlvblNjYWxlXCIgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJSZXNvbHV0aW9uIChkZWNpbWF0ZSBmYWN0b3IpXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgICAgW3N0eWxlXT1cInsnbWluLXdpZHRoJzogJzZyZW0nfVwiXG4gICAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJzZWxlY3RSZXNvbHV0aW9uLmVtaXQoJGV2ZW50LnZhbHVlKVwiPlxuICAgICAgICA8L3AtZHJvcGRvd24+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc0hlYXRtYXAgJiYgdG9vbHMucmVnaW9uVG9vbHNcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8IS0tIFJlZ2lvbiBFZGl0b3I6IG9wZW5zIHRoZSBlZGl0b3IgYXMgYSBmbG9hdGluZyBkaWFsb2cgKGZvcm1lcmx5IHRoZVxuICAgICAgICAgICByaWdodC1wYW5lbCBSZWdpb25zIHRhYikuIFNpdHMgd2l0aCB0aGUgcmVnaW9uIGRyYXdpbmcgdG9vbHMuIC0tPlxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiT3BlbiB0aGUgUmVnaW9uIEVkaXRvclwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwib3BlblJlZ2lvbkVkaXRvci5lbWl0KClcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVnaW9uLWVkaXRvci5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8IS0tIFJlZ2lvbiBkcmF3aW5nIHRvb2xzIC0tPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1hcnJvdy11cC1yaWdodFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdzZWxlY3QnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNlbGVjdGlvbiBtb2RlICg8a2JkPnM8L2tiZD4pXCJcbiAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdzZWxlY3QnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktc3RvcFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdkcmF3cmVjdCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRHJhdyBhIHJlY3Rhbmd1bGFyIHJlZ2lvbiAoPGtiZD5yPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnZHJhd3JlY3QnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnZHJhd29wZW5wYXRoJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEcmF3IGEgcG9seWxpbmUgKDxrYmQ+bDwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCJcbiAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2RyYXdvcGVucGF0aCcpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3BvbHlsaW5lLnN2Z1wiXG4gICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnZHJhd29wZW5wYXRoJyA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1wZW5jaWxcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnZHJhd2Nsb3NlZHBhdGgnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkRyYXcgYSBmcmVlZm9ybSByZWdpb24gKDxrYmQ+Zjwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCJcbiAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2RyYXdjbG9zZWRwYXRoJylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8IS0tIEJydXNoOiBwYWludCBhIHJlZ2lvbiBieSBkcmFnZ2luZyBhIGRpc2Mgb2YgdGhlIGdpdmVuIHNpemU7IHRoZSBTaXplXG4gICAgICAgICAgIHNsaWRlciBhcHBlYXJzIHdoaWxlIGl0J3MgYWN0aXZlLiBIb2xkIFNoaWZ0IHRvIGVyYXNlIChzdWJ0cmFjdCkuIC0tPlxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdicnVzaCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiQnJ1c2g6IGNsaWNrIGFuZCBkcmFnIHRvIHBhaW50IGEgcmVnaW9uLlxuICAgICAgICAgICAgICAgIDxicj5Ib2xkIDxrYmQ+U2hpZnQ8L2tiZD4gd2hpbGUgcGFpbnRpbmcgdG8gZXJhc2UgZnJvbSBhIHJlZ2lvbi5cIlxuICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2JydXNoJylcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcGFpbnRicnVzaC5zdmdcIlxuICAgICAgICAgICAgIFtjbGFzc109XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ2JydXNoJyA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImFjdGl2ZURyYWdNb2RlID09PSAnYnJ1c2gnXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5TaXplPC9zcGFuPlxuICAgICAgICA8cC1zbGlkZXIgWyhuZ01vZGVsKV09XCJicnVzaFNpemVcIlxuICAgICAgICAgICAgICAgICAgW21pbl09XCJicnVzaFNpemVNaW5cIiBbbWF4XT1cImJydXNoU2l6ZU1heFwiIFtzdGVwXT1cImJydXNoU2l6ZVN0ZXBcIlxuICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cImJydXNoU2l6ZUNoYW5nZS5lbWl0KCRldmVudC52YWx1ZSlcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJCcnVzaCBkaWFtZXRlciBpbiBpbWFnZSBwaXhlbHMuXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgc3R5bGVDbGFzcz1cIndhbmQtc2Vuc2l0aXZpdHktc2xpZGVyXCI+PC9wLXNsaWRlcj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dCB3YW5kLXNlbnNpdGl2aXR5LXZhbHVlXCI+XG4gICAgICAgICAge3sgYnJ1c2hTaXplIH19PC9zcGFuPlxuICAgICAgPC9uZy1jb250YWluZXI+XG5cbiAgICAgIDwhLS0gVmVydGV4IGVkaXRpbmcg4oCUIGJhY2tlbmRzIHdpdGggYSB2ZXJ0ZXgtZWRpdGluZyBvdmVybGF5IChPU0QgKyBuYXBhcmktanMgaW1hZ2UpLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJzdXBwb3J0c1JlZ2lvblZlcnRleFRvb2xzXCI+XG4gICAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdkcmF3cG9seWdvbidcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJQb2x5Z29uOiBjbGljayB0byBwbGFjZSBlYWNoIHZlcnRleDsgY2xpY2sgdGhlIGZpcnN0IHBvaW50IHRvIGNsb3NlXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnZHJhd3BvbHlnb24nKVwiPlxuICAgICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3BvbHlnb24tdmVydGljZXMuc3ZnXCJcbiAgICAgICAgICAgICAgIFtjbGFzc109XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ2RyYXdwb2x5Z29uJyA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2FkZHBvaW50J1wiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkFkZCBhIHZlcnRleDogY2xpY2sgYW4gZWRnZSBvZiB0aGUgc2VsZWN0ZWQgcG9seWdvblwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2FkZHBvaW50JylcIj5cbiAgICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy92ZXJ0ZXgtYWRkLnN2Z1wiXG4gICAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdhZGRwb2ludCcgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdkZWxldGVwb2ludCdcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEZWxldGUgYSB2ZXJ0ZXg6IGNsaWNrIGEgdmVydGV4IG9mIHRoZSBzZWxlY3RlZCBwb2x5Z29uXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnZGVsZXRlcG9pbnQnKVwiPlxuICAgICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3ZlcnRleC1kZWxldGUuc3ZnXCJcbiAgICAgICAgICAgICAgIFtjbGFzc109XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ2RlbGV0ZXBvaW50JyA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ3dhbmQnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIldhbmQ6IGNsaWNrIGFuZCBkcmFnIHRvIGdyb3cgYSByZWdpb24gYnkgc2ltaWxhciBwaXhlbCB2YWx1ZXMgKDxrYmQ+dzwva2JkPikuXG4gICAgICAgICAgICAgICAgPGJyPkhvbGQgPGtiZD5TaGlmdDwva2JkPiB3aGlsZSBjbGlja2luZyBvciBkcmFnZ2luZyBvbiBhIHJlZ2lvbiB0byBlcmFzZSBwaXhlbHMgZnJvbSBpdC5cIlxuICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ3dhbmQnKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy93YW5kLnN2Z1wiXG4gICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnd2FuZCcgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ3dhbmQnXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5TZW5zLjwvc3Bhbj5cbiAgICAgICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwid2FuZFNlbnNpdGl2aXR5XCJcbiAgICAgICAgICAgICAgICAgIFttaW5dPVwid2FuZFNlbnNpdGl2aXR5TWluXCIgW21heF09XCJ3YW5kU2Vuc2l0aXZpdHlNYXhcIiBbc3RlcF09XCJ3YW5kU2Vuc2l0aXZpdHlTdGVwXCJcbiAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJ3YW5kU2Vuc2l0aXZpdHlDaGFuZ2UuZW1pdCgkZXZlbnQudmFsdWUpXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiSGlnaGVyID0gc3RyaWN0ZXIgKHNtYWxsZXIgc2VsZWN0aW9uKTsgbG93ZXIgPSBsb29zZXIuXG4gICAgICAgICAgICAgICAgICBUYWtlcyBlZmZlY3QgZnJvbSB0aGUgbmV4dCBjbGljay9kcmFnIHRpY2suXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgc3R5bGVDbGFzcz1cIndhbmQtc2Vuc2l0aXZpdHktc2xpZGVyXCI+PC9wLXNsaWRlcj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dCB3YW5kLXNlbnNpdGl2aXR5LXZhbHVlXCI+XG4gICAgICAgICAge3sgd2FuZFNlbnNpdGl2aXR5LnRvRml4ZWQoMSkgfX08L3NwYW4+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktZXJhc2VyXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2VyYXNlVmVydGV4J1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJWZXJ0ZXggZXJhc2VyICg8a2JkPmU8L2tiZD4pOiBjbGljayBvciBkcmFnIHRvIHJlbW92ZSBwb2x5Z29uIHZlcnRpY2VzIGluc2lkZVxuICAgICAgICAgICAgICAgIHRoZSBjdXJzb3IgY2lyY2xlIGZyb20gYW55IHJlZ2lvbi5cIlxuICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2VyYXNlVmVydGV4JylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdlcmFzZVZlcnRleCdcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPlJhZGl1czwvc3Bhbj5cbiAgICAgICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwidmVydGV4RXJhc2VyUmFkaXVzXCJcbiAgICAgICAgICAgICAgICAgIFttaW5dPVwidmVydGV4RXJhc2VyUmFkaXVzTWluXCIgW21heF09XCJ2ZXJ0ZXhFcmFzZXJSYWRpdXNNYXhcIiBbc3RlcF09XCJ2ZXJ0ZXhFcmFzZXJSYWRpdXNTdGVwXCJcbiAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJ2ZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UuZW1pdCgkZXZlbnQudmFsdWUpXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRXJhc2VyIHJhZGl1cyBpbiBpbWFnZSBwaXhlbHMuXG4gICAgICAgICAgICAgICAgICBWZXJ0aWNlcyB3aXRoaW4gdGhpcyBkaXN0YW5jZSBvZiB0aGUgY3Vyc29yIGFyZSByZW1vdmVkLlwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIHN0eWxlQ2xhc3M9XCJ3YW5kLXNlbnNpdGl2aXR5LXNsaWRlclwiPjwvcC1zbGlkZXI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHQgd2FuZC1zZW5zaXRpdml0eS12YWx1ZVwiPlxuICAgICAgICAgIHt7IHZlcnRleEVyYXNlclJhZGl1cyB9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhY2FuVW5kb1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJVbmRvIHRoZSBsYXN0IHJlZ2lvbiBhY3Rpb24gKDxrYmQ+Q3RybDwva2JkPis8a2JkPlo8L2tiZD4pLlxuICAgICAgICAgICAgICAgIFVwIHRvIDEwIGFjdGlvbnMgY2FuIGJlIHVuZG9uZS5cIlxuICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInVuZG9SZWdpb24uZW1pdCgpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3VuZG8uc3ZnXCIgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhY2FuUmVkb1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJSZWRvIHRoZSBsYXN0IHVuZG9uZSByZWdpb24gYWN0aW9uXG4gICAgICAgICAgICAgICAgKDxrYmQ+Q3RybDwva2JkPis8a2JkPlNoaWZ0PC9rYmQ+KzxrYmQ+Wjwva2JkPiAvIDxrYmQ+Q3RybDwva2JkPis8a2JkPlk8L2tiZD4pLlwiXG4gICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwicmVkb1JlZ2lvbi5lbWl0KClcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVkby5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXRyYXNoXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEZWxldGUgc2VsZWN0ZWQgcmVnaW9uICg8a2JkPkRlbDwva2JkPilcIlxuICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cImRlbGV0ZVJlZ2lvbi5lbWl0KClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8IS0tIFNBTSAoaml0LXVpIzkwKTogYm94LXByb21wdCBzZWdtZW50LCBpbnRlcmFjdGl2ZSBwb2ludCBwcm9tcHRzLCBhbmQgYVxuICAgICAgICAgICBtb2RlbCBwaWNrZXIuIC0tPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDwhLS0gU0FNIGNvbnRyb2xzIGdyb3VwZWQ6IGJveC1wcm9tcHQgU2VnbWVudCwgdGhlbiB0aGUgcG9pbnQtcHJvbXB0IHRvb2wsXG4gICAgICAgICAgIHRoZW4gdGhlIG1vZGVsLXBpY2tlciBkcm9wZG93biAodGhlIGNoZXZyb24pIHdoaWNoIGdvdmVybnMgQk9USCB0b29scy5cbiAgICAgICAgICAgRmFsbHMgYmFjayB0byBTZWdtZW50ICsgcG9pbnQgd2l0aCBubyBtb2RlbCBwaWNrZXIgd2hlbiBub25lIGFyZSBzZXQuIC0tPlxuICAgICAgPHNwYW4gKm5nSWY9XCJzYW1Nb2RlbHMubGVuZ3RoOyBlbHNlIHNlZ21lbnRQbGFpblwiIGNsYXNzPVwicC1idXR0b25zZXQgc2VnbWVudC1zcGxpdFwiPlxuICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNlZ21lbnQgdGhlIGRyYXduIHJlY3RhbmdsZXMgd2l0aCBTQU0gKGJveCBwcm9tcHQpXCJcbiAgICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJzZWdtZW50UmVnaW9ucy5lbWl0KClcIj5cbiAgICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9waWN0dXJlLXNlZ21lbnRhdGlvbi5zdmdcIlxuICAgICAgICAgICAgICAgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgIDxuZy1jb250YWluZXIgW25nVGVtcGxhdGVPdXRsZXRdPVwic2FtUG9pbnRCdG5cIj48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1jaGV2cm9uLWRvd25cIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUGljayB0aGUgU0FNIG1vZGVsICh1c2VkIGJ5IGJvdGggc2VnbWVudCB0b29scylcIlxuICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNhbU1vZGVsTWVudS50b2dnbGUoJGV2ZW50KVwiPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgPC9zcGFuPlxuICAgICAgPG5nLXRlbXBsYXRlICNzZWdtZW50UGxhaW4+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwicC1idXR0b25zZXQgc2VnbWVudC1zcGxpdFwiPlxuICAgICAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTZWdtZW50IHRoZSBkcmF3biByZWN0YW5nbGVzIHdpdGggU0FNIChib3ggcHJvbXB0KVwiXG4gICAgICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNlZ21lbnRSZWdpb25zLmVtaXQoKVwiPlxuICAgICAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcGljdHVyZS1zZWdtZW50YXRpb24uc3ZnXCJcbiAgICAgICAgICAgICAgICAgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cInNhbVBvaW50QnRuXCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvc3Bhbj5cbiAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICA8IS0tIFBvaW50LXByb21wdCB0b29sIOKAlCBzaGFyZWQgYmV0d2VlbiB0aGUgbW9kZWxzIC8gbm8tbW9kZWxzIGxheW91dHMgc28gaXRcbiAgICAgICAgICAgYWx3YXlzIHNpdHMganVzdCBsZWZ0IG9mIHRoZSBtb2RlbCBkcm9wZG93bi4gLS0+XG4gICAgICA8bmctdGVtcGxhdGUgI3NhbVBvaW50QnRuPlxuICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnc2FtUG9pbnQnXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU0FNIHBvaW50IHByb21wdHM6IGNsaWNrIGEgZmliZXIgdG8gc2VnbWVudCBpdCBhcyBhIG5ldyByZWdpb247XG4gICAgICAgICAgICAgICAgICBTaGlmdC9BbHQtY2xpY2sgdG8gcmVmaW5lIChleGNsdWRlKSB0aGUgY3VycmVudCBvbmUuPGJyPkVzYyB1bmRvZXMgaXQuXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnc2FtUG9pbnQnKVwiPlxuICAgICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL2NsaWNrLnN2Z1wiXG4gICAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdzYW1Qb2ludCcgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgPHAtbWVudSAjc2FtTW9kZWxNZW51IFttb2RlbF09XCJzYW1NZW51SXRlbXNcIiBbcG9wdXBdPVwidHJ1ZVwiIGFwcGVuZFRvPVwiYm9keVwiPjwvcC1tZW51PlxuICAgICAgPCEtLSBIZWxwOiBleHBsYWlucyB0aGUgVmlULVQgdnMgVmlULUIgdHJhZGUtb2ZmIGJlaGluZCB0aGUgbW9kZWwgcGlja2VyLiAtLT5cbiAgICAgIDxwLWJ1dHRvbiAqbmdJZj1cInNhbU1vZGVscy5sZW5ndGhcIiBpY29uPVwicGkgcGktaW5mby1jaXJjbGVcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkFib3V0IHRoZSBTQU0gbW9kZWxzXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJzYW1Nb2RlbEluZm8udG9nZ2xlKCRldmVudClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1vdmVybGF5UGFuZWwgI3NhbU1vZGVsSW5mbyBhcHBlbmRUbz1cImJvZHlcIiBzdHlsZUNsYXNzPVwic2FtLW1vZGVsLWluZm9cIj5cbiAgICAgICAgPGRpdiBzdHlsZT1cIm1heC13aWR0aDogMjRyZW07IGxpbmUtaGVpZ2h0OiAxLjRcIj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiZm9udC1ib2xkIG1iLTFcIj5TQU0gbW9kZWxzPC9kaXY+XG4gICAgICAgICAgPHAgY2xhc3M9XCJtdC0wIG1iLTJcIj5cbiAgICAgICAgICAgIEFsbCBhcmUgcHJvbXB0YWJsZSBTZWdtZW50IEFueXRoaW5nIG1vZGVscyBmaW5ldHVuZWQgZm9yIGJpb21lZGljYWwgaW1hZ2VzLiBUaGV5IGRpZmZlclxuICAgICAgICAgICAgaW4gdGhlIDxpPmltYWdlIGVuY29kZXI8L2k+ICh0aGUgaGVhdnkgb25jZS1wZXItaW1hZ2Ugc3RlcCkgYW5kIHRoZSB0cmFpbmluZyBkb21haW4g4oCUXG4gICAgICAgICAgICBib3ggYW5kIHBvaW50IHByb21wdGluZyBiZWhhdmUgdGhlIHNhbWUuXG4gICAgICAgICAgPC9wPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJtYi0yXCI+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZm9udC1ib2xkXCI+bWljcm8tc2FtIFZpVC1UIDxzcGFuIGNsYXNzPVwiZm9udC1ub3JtYWxcIj4oZGVmYXVsdCk8L3NwYW4+PC9kaXY+XG4gICAgICAgICAgICA8ZGl2PkxpZ2h0LW1pY3Jvc2NvcHkgZmluZXR1bmVkLiBUaW55VmlUIGVuY29kZXIsIH4xNCZuYnNwO01CLCB2ZXJ5IGZhc3QsIHJ1bnMgb24gV0FTTS5cbiAgICAgICAgICAgICAgR29vZCBtYXNrIHF1YWxpdHkg4oCUIGJlc3QgZm9yIHF1aWNrIGludGVyYWN0aXZlIHdvcmsgYW5kIG1hbnkvbGFyZ2Ugb2JqZWN0cy48L2Rpdj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwibWItMlwiPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImZvbnQtYm9sZFwiPm1pY3JvLXNhbSBWaVQtQjwvZGl2PlxuICAgICAgICAgICAgPGRpdj5MaWdodC1taWNyb3Njb3B5IGZpbmV0dW5lZC4gVmlULUJhc2UgZW5jb2RlciwgfjE3MiZuYnNwO01CLCBzbG93ZXIsIEdQVS1hY2NlbGVyYXRlZFxuICAgICAgICAgICAgICAoV2ViR1BVKS4gSGlnaGVzdCBhY2N1cmFjeSDigJQgd2hlbiBWaVQtVCBtaXNzZXMgc3VidGxlIG9yIG92ZXJsYXBwaW5nIGJvdW5kYXJpZXMuPC9kaXY+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm1iLTJcIj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmb250LWJvbGRcIj5wYXRoby1zYW0gVmlULUI8L2Rpdj5cbiAgICAgICAgICAgIDxkaXY+SGlzdG9wYXRob2xvZ3kgZmluZXR1bmVkIChIJmFtcDtFIC8gc3RhaW5lZCB0aXNzdWUpLiBWaVQtQmFzZSBlbmNvZGVyLCBHUFUtYWNjZWxlcmF0ZWQuXG4gICAgICAgICAgICAgIFVzZSBpdCBmb3IgcGF0aG9sb2d5IHNsaWRlcyDigJQgaXQgc2VnbWVudHMgdGlzc3VlL251Y2xlaSBiZXR0ZXIgdGhhbiB0aGUgbWljcm9zY29weSBtb2RlbHMuPC9kaXY+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm1iLTJcIj5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmb250LWJvbGRcIj5wYXRoby1zYW0gVmlULUIgKGludDgpPC9kaXY+XG4gICAgICAgICAgICA8ZGl2PlNhbWUgbW9kZWwsIGludDgtcXVhbnRpemVkIOKAlCB+MTAwJm5ic3A7TUIgdnMgfjE4MCZuYnNwO01CIChzbWFsbGVyIGRvd25sb2FkKS4gUnVucyBvblxuICAgICAgICAgICAgICBXQVNNIChzbG93ZXIgZW5jb2RlIHRoYW4gdGhlIGZwMTYgdmVyc2lvbik7IG1hc2tzIGFyZSBuZWFyLWlkZW50aWNhbC4gUGljayBpdCBvbiBzbG93XG4gICAgICAgICAgICAgIGNvbm5lY3Rpb25zLjwvZGl2PlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0ZXh0LXNtXCIgc3R5bGU9XCJvcGFjaXR5OiAwLjc1XCI+XG4gICAgICAgICAgICBUaGUgY2hvaWNlIGFwcGxpZXMgdG8gYm90aCB0aGUgYm94IGFuZCBwb2ludCB0b29scy4gRWFjaCB0b29sIGxvYWRzIGl0cyBvd24gZW5jb2RlcixcbiAgICAgICAgICAgIHNvIHRoZSBmaXJzdCBjbGljayBhZnRlciBzd2l0Y2hpbmcgcmUtZW5jb2RlcyBvbmNlLlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvcC1vdmVybGF5UGFuZWw+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJBdXRvLXNlZ21lbnQgY2VsbHMgaW5zaWRlIGVhY2ggZHJhd24gcmVjdGFuZ2xlIChjZWxscG9zZS1TQU0sIGNsaWVudC1zaWRlKVwiXG4gICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJzZWdtZW50Q2VsbHBvc2UuZW1pdCgpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL2NlbGxzLnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cblxuICAgICAgPCEtLSBZT0xPOiBkZXRlY3Qgb2JqZWN0cyBhY3Jvc3MgdGhlIGN1cnJlbnQgdmlldy4gVW5saWtlIHRoZSBTQU0gYW5kXG4gICAgICAgICAgIGNlbGxwb3NlIHRvb2xzIGl0IHRha2VzIG5vIHByb21wdCDigJQgYSBkZXRlY3RvciBmaW5kcyBvYmplY3RzIGluIGFcbiAgICAgICAgICAgZmllbGQgcmF0aGVyIHRoYW4gYmVpbmcgcG9pbnRlZCBhdCBvbmUg4oCUIHNvIHRoZXJlIGlzIG5vdGhpbmcgdG8gZHJhd1xuICAgICAgICAgICBmaXJzdC4gU3BsaXQgYnV0dG9uOiBydW4sIHBpY2sgY2hlY2twb2ludCwgb3BlbiBwYXJhbWV0ZXJzLiAtLT5cbiAgICAgIDxzcGFuICpuZ0lmPVwieW9sb01vZGVscy5sZW5ndGhcIiBjbGFzcz1cInAtYnV0dG9uc2V0IHNlZ21lbnQtc3BsaXRcIj5cbiAgICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1taWNyb2NoaXAtYWlcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRGV0ZWN0IG9iamVjdHMgaW4gdGhlIGN1cnJlbnQgdmlldyB3aXRoIFlPTE8gKGNsaWVudC1zaWRlKVwiXG4gICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiZGV0ZWN0WW9sby5lbWl0KClcIj5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1jaGV2cm9uLWRvd25cIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUGljayB0aGUgWU9MTyBtb2RlbFwiXG4gICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwieW9sb01vZGVsTWVudS50b2dnbGUoJGV2ZW50KVwiPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXNsaWRlcnMtaFwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJZT0xPIHBhcmFtZXRlcnNcIlxuICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cIm9wZW5Zb2xvUGFyYW1zLmVtaXQoKVwiPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgPC9zcGFuPlxuICAgICAgPHAtbWVudSAjeW9sb01vZGVsTWVudSBbbW9kZWxdPVwieW9sb01lbnVJdGVtc1wiIFtwb3B1cF09XCJ0cnVlXCIgYXBwZW5kVG89XCJib2R5XCI+PC9wLW1lbnU+XG5cbiAgICAgIDwhLS0gUmV0aW5hbCBsYXllcnM6IHNlbWFudGljIHNlZ21lbnRhdGlvbiBvZiB0aGUgY3VycmVudCB2aWV3LiBTYW1lXG4gICAgICAgICAgIG5vLXByb21wdCBzaGFwZSBhcyBZT0xPIOKAlCBpdCBsYWJlbHMgZXZlcnkgcGl4ZWwgcmF0aGVyIHRoYW4gYmVpbmdcbiAgICAgICAgICAgcG9pbnRlZCBhdCBhbiBvYmplY3Qg4oCUIHNvIHRoZSBzYW1lIHNwbGl0IGJ1dHRvbjogcnVuLCBwaWNrXG4gICAgICAgICAgIGNoZWNrcG9pbnQsIG9wZW4gcGFyYW1ldGVycy4gLS0+XG4gICAgICA8c3BhbiAqbmdJZj1cInJldGluYWxNb2RlbHMubGVuZ3RoXCIgY2xhc3M9XCJwLWJ1dHRvbnNldCBzZWdtZW50LXNwbGl0XCI+XG4gICAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktYWxpZ24tbGVmdFwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTZWdtZW50IHJldGluYWwgbGF5ZXJzIGluIHRoZSBjdXJyZW50IHZpZXcgKGNsaWVudC1zaWRlKVwiXG4gICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2VnbWVudFJldGluYWwuZW1pdCgpXCI+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktY2hldnJvbi1kb3duXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlBpY2sgdGhlIHJldGluYWwgbGF5ZXIgbW9kZWxcIlxuICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInJldGluYWxNb2RlbE1lbnUudG9nZ2xlKCRldmVudClcIj5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1zbGlkZXJzLWhcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUmV0aW5hbCBsYXllciBwYXJhbWV0ZXJzXCJcbiAgICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJvcGVuUmV0aW5hbFBhcmFtcy5lbWl0KClcIj5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwvc3Bhbj5cbiAgICAgIDxwLW1lbnUgI3JldGluYWxNb2RlbE1lbnUgW21vZGVsXT1cInJldGluYWxNZW51SXRlbXNcIiBbcG9wdXBdPVwidHJ1ZVwiIGFwcGVuZFRvPVwiYm9keVwiPjwvcC1tZW51PlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cInRvb2xzLmhlbHBcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG5cbiAgICAgIDwhLS0gSGVscCAtLT5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktcXVlc3Rpb24tY2lyY2xlXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJIZWxwXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJzaG93SGVscCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gIDwvZGl2PlxuPC9wLXRvb2xiYXI+XG48cC1kaWFsb2cgaGVhZGVyPVwiUGxvdHRpbmcgaGVscFwiIFsodmlzaWJsZSldPVwiZGlzcGxheUhlbHBEaWFsb2dcIiBbbW9kYWxdPVwidHJ1ZVwiIFtzdHlsZV09XCJ7d2lkdGg6ICc1MHZ3J31cIlxuICAgICAgICAgIFttYXhpbWl6YWJsZV09XCJ0cnVlXCIgW2RyYWdnYWJsZV09XCJmYWxzZVwiIFtyZXNpemFibGVdPVwiZmFsc2VcIj5cbiAgPHA+Q2xpY2sgYSBmaWxlIGluIHRoZSBsZWZ0LWhhbmQgdHJlZSB0byBkaXNwbGF5IGl0LiBCeSBkZWZhdWx0IGl0IG9wZW5zIGluIHRoZVxuICAgIDxiPkltYWdlPC9iPiB2aWV3IOKAlCBhIG5hdGl2ZWx5IHRpbGVkLCBkZWVwbHkgem9vbWFibGUgcmFzdGVyIChPcGVuU2VhZHJhZ29uKSB3aXRoIGFcbiAgICBuYXZpZ2F0b3IgbWluaW1hcCBhbmQgYSBwaHlzaWNhbC11bml0cyBzY2FsZSBiYXIuIDxiPkNsaWNrIHRoZSBpbWFnZSB0byB6b29tPC9iPiB0b3dhcmRcbiAgICB0aGUgY2xpY2tlZCBwb2ludDsgc2Nyb2xsIHRvIHpvb20gYW5kIGRyYWcgdG8gcGFuLiBXaGVuIGEgc3RhY2sgaXMgb3BlbiwgYSBzbGljZSBzbGlkZXJcbiAgICAoSW1hZ2Ugdmlldykgb3IgaW1hZ2UtbW9kZSB0b2dnbGUgKG90aGVyIHZpZXdzKSBhcHBlYXJzLiBSZWdpb25zIGNhbiBhbHNvIGJlIGVkaXRlZCBpbiB0aGVcbiAgICA8Yj5SZWdpb24gRWRpdG9yPC9iPiwgb3BlbmVkIHdpdGggdGhlXG4gICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVnaW9uLWVkaXRvci5zdmdcIiB3aWR0aD1cIjE0XCIgaGVpZ2h0PVwiMTRcIiBhbHQ9XCJSZWdpb24gRWRpdG9yXCIvPlxuICAgIHRvb2xiYXIgYnV0dG9uIGFzIGEgZmxvYXRpbmcgZGlhbG9nLiBEcmFnIHRoZSA8aSBjbGFzcz1cInBpIHBpLWJhcnNcIj48L2k+IGhhbmRsZSB0byBkZXRhY2hcbiAgICB0aGlzIHRvb2xiYXIgaW50byBhIGZsb2F0aW5nIHdpbmRvdy5cbiAgPC9wPlxuXG4gIDxoMz5QbG90LXR5cGUgZHJvcGRvd248L2gzPlxuICA8cD5cbiAgICBUaGUgPGI+cGxvdC10eXBlIGRyb3Bkb3duPC9iPiBzd2l0Y2hlcyBob3cgdGhlIGZpbGUgaXMgdmlzdWFsaXplZDpcbiAgICA8Yj5JbWFnZTwvYj4sIDxiPkNvbnRvdXI8L2I+LCA8Yj5TY2F0dGVyIChyZWdpb25zKTwvYj4sIDxiPlN1cmZhY2UgKDNEKTwvYj4sXG4gICAgPGI+U2NhdHRlciAzRDwvYj4sIGFuZCA8Yj5Jc29zdXJmYWNlICgzRCk8L2I+LiBTY2FsYXIgYW5kIDNEIHR5cGVzIHJlcXVpcmUgYSBncmF5c2NhbGVcbiAgICBpbWFnZSAodGhlIDNEIHZvbHVtZSB0eXBlcyBhbHNvIHJlcXVpcmUgYSB6LXN0YWNrKS4gVGhlIG5vbi1pbWFnZSB2aWV3cyByZW5kZXIgd2l0aFxuICAgIFBsb3RseSBhbmQgc3VwcG9ydCBcInJlYWwgem9vbWluZ1wiIChhIGRvd25zY2FsZWQgb3ZlcnZpZXcgdGhhdCByZS1mZXRjaGVzIGhpZ2hlclxuICAgIHJlc29sdXRpb24gYXMgeW91IHpvb20gaW4pLlxuICA8L3A+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5Jc29zdXJmYWNlIGJhbmQ8L2I+XCIgc2xpZGVyIChJc29zdXJmYWNlKTogc2V0cyB0aGUgaW50ZW5zaXR5IGJhbmQgdGhlIHN1cmZhY2VzIGFyZVxuICAgICAgZHJhd24gZm9yLCBtYXBwZWQgb250byB0aGUgdm9sdW1lJ3MgcmVhbCBpbnRlbnNpdHkgcmFuZ2UuPC9saT5cbiAgICA8bGk+XCI8Yj4zRCBjYW1lcmE8L2I+XCIgKFN1cmZhY2UgLyBTY2F0dGVyIDNEIC8gSXNvc3VyZmFjZSk6IHpvb20gPGkgY2xhc3M9XCJwaSBwaS1zZWFyY2hcIj48L2k+LFxuICAgICAgcGFuIDxpIGNsYXNzPVwicGkgcGktYXJyb3dzLWFsdFwiPjwvaT4sIG9yYml0IDxpIGNsYXNzPVwicGkgcGktZ2xvYmVcIj48L2k+LFxuICAgICAgdHVybnRhYmxlIDxpIGNsYXNzPVwicGkgcGktc3luY1wiPjwvaT4sIGFuZCByZXNldCBjYW1lcmEgPGkgY2xhc3M9XCJwaSBwaS1ob21lXCI+PC9pPjtcbiAgICAgIHNjcm9sbCB0byB6b29tIHRoZSAzRCBzY2VuZS48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5JbWFnZSBtYW5pcHVsYXRpb24gdG9vbHM8L2gzPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+UmVnaW9uIEVkaXRvcjwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9yZWdpb24tZWRpdG9yLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjpcbiAgICAgIG9wZW5zIHRoZSBSZWdpb24gRWRpdG9yIGFzIGEgZmxvYXRpbmcsIGRyYWdnYWJsZSBkaWFsb2cg4oCUIGEgcGVyLWltYWdlIHRhYmxlIG9mIGV2ZXJ5IHJlZ2lvblxuICAgICAgKGNsYXNzLCBjb2xvdXIsIGFyZWEpIHdpdGggYnVsayBzZWxlY3QvbGFiZWwvY29sb3VyL2RlbGV0ZSBhbmQgR2VvSlNPTiBpbXBvcnQvZXhwb3J0LiBJdCBzdGF5c1xuICAgICAgaW4gc3luYyB3aXRoIHRoZSBpbWFnZSwgc28geW91IGNhbiBrZWVwIGl0IG9wZW4gd2hpbGUgZHJhd2luZy48L2xpPlxuICAgIDxsaT5cIjxiPkludGVuc2l0eSBwcm9maWxlIGxpbmVzPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NwbGluZS1jaGFydC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgICg8aT53b3JrIGluIHByb2dyZXNzIOKAlCBub3QgeWV0IHN0YWJsZTwvaT4pOiBhZGRzIGEgY29sb3VyZWQgbGluZSBST0k7IGEgZmxvYXRpbmcgaW5zZXRcbiAgICAgIGNoYXJ0IHBsb3RzIGludGVuc2l0eSBhbG9uZyBlYWNoIGxpbmUsIHVwZGF0ZXMgbGl2ZSBhcyB5b3UgZHJhZyBpdCwgYW5kIHJlLXNhbXBsZXMgYXQgdGhlXG4gICAgICBjdXJyZW50IHpvb20gcmVzb2x1dGlvbi4gRWFjaCBhZGRlZCBsaW5lIGdldHMgaXRzIG93biBjb2xvdXI7IHNlbGVjdCBhIGxpbmUgYW5kIHVzZSB0aGVcbiAgICAgIHRyYXNoIHRvb2wgPGkgY2xhc3M9XCJwaSBwaS10cmFzaFwiPjwvaT4gdG8gcmVtb3ZlIGl0IChhbmQgaXRzIHRyYWNlKS48L2xpPlxuICAgIDxsaT5cIjxiPkNoYW5uZWxzICZhbXA7IEhpc3RvZ3JhbTwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9pbnZlcnQuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgb3BlbnMgYSBmbG9hdGluZyBkaWFsb2cgZm9yIGJyaWdodG5lc3MvY29udHJhc3QsIGdhbW1hLCBhbmQgcGVyLWNoYW5uZWwgZGlzcGxheSwgcGx1cyB0aGVcbiAgICAgIDxiPmNvbG9ybWFwIC8gTFVUPC9iPiBjb250cm9scyAoZ3JheXNjYWxlIG9ubHkpIOKAlCBhIDxiPnJldmVyc2U8L2I+IHRvZ2dsZSBhbmQgYSBjb2xvcm1hcFxuICAgICAgZHJvcGRvd24gKGRlZmF1bHQ6IGludmVydGVkIGdyZXlzKS48L2xpPlxuICAgIDxsaT5cIjxiPlNtb290aGVuPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3Ntb290aC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgIChJbWFnZSB2aWV3IG9ubHkpOiB0aGUgaW1hZ2Ugb3BlbnMgc2hvd2luZyA8Yj5yYXcgcGl4ZWxzPC9iPiAobmVhcmVzdC1uZWlnaGJvdXIpLCBzb1xuICAgICAgem9vbWluZyBpbiByZW5kZXJzIGNyaXNwIGJsb2NrcyBmb3IgcGl4ZWwgaW5zcGVjdGlvbi4gVG9nZ2xlIHRoaXMgb24gZm9yIGJpbGluZWFyXG4gICAgICBzbW9vdGhpbmc7IHRoZSBpY29uIHR1cm5zIHdoaXRlIHdoaWxlIHNtb290aGluZyBpcyBhY3RpdmUuPC9saT5cbiAgPC91bD5cblxuICA8aDM+RG93bmxvYWQgJmFtcDsgdmlldyB0b29sczwvaDM+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5Eb3dubG9hZDwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLWRvd25sb2FkXCI+PC9pPjogZG93bmxvYWQgdGhlIGN1cnJlbnQgaW1hZ2UgYXMgYSBQTkcuPC9saT5cbiAgICA8bGk+XCI8Yj5GaXQgdG8gdmlldzwvYj5cIiAvIGF1dG9zY2FsZSA8aSBjbGFzcz1cInBpIHBpLXdpbmRvdy1tYXhpbWl6ZVwiPjwvaT46IHJlc2V0cyB0aGUgaW1hZ2VcbiAgICAgIHRvIGl0cyBvcmlnaW5hbCBkaW1lbnNpb25zLiBJbiBzdGFjayBtb2RlIGl0IGFsc28gcmVzZXRzIHRvIHNpbmdsZS1pbWFnZSBtb2RlLjwvbGk+XG4gIDwvdWw+XG5cbiAgPGgzPlpvb20gdG9vbHM8L2gzPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+Wm9vbSBib3g8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvem9vbS1ib3guc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+XG4gICAgICAoPGtiZD5iPC9rYmQ+KTogZHJhZyBhIGJveCB0byBzZWxlY3QgYW4gYXJlYSB0byB6b29tIGludG8uPC9saT5cbiAgICA8bGk+XCI8Yj5QYW48L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1hcnJvd3MtYWx0XCI+PC9pPiAoPGtiZD5wPC9rYmQ+KTogZHJhZyB0byBwYW4gdGhlIGltYWdlLjwvbGk+XG4gICAgPGxpPlwiPGI+Wm9vbSBpbjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXNlYXJjaC1wbHVzXCI+PC9pPiAoPGtiZD4rPC9rYmQ+KTogem9vbSBpbiBvbmUgc3RlcC48L2xpPlxuICAgIDxsaT5cIjxiPlpvb20gb3V0PC9iPlwiIDxpIGNsYXNzPVwicGkgcGktc2VhcmNoLW1pbnVzXCI+PC9pPiAoPGtiZD4tPC9rYmQ+KTogem9vbSBvdXQgb25lIHN0ZXAsXG4gICAgICBzdG9wcGluZyBhdCB0aGUgb3JpZ2luYWwgaW1hZ2UgZGltZW5zaW9ucy48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5SZWdpb24gdG9vbHM8L2gzPlxuICA8cD5CeSBkZWZhdWx0IG5vIHRvb2wgaXMgc2VsZWN0ZWQgKGFsbCBncmV5ZWQtb3V0KTsgc2VsZWN0aW5nIGEgdG9vbCBoaWdobGlnaHRzIGl0LiBUaGVcbiAgICB2ZXJ0ZXgtZWRpdGluZyB0b29scyAocG9seWdvbiwgYWRkL2RlbGV0ZSB2ZXJ0ZXgsIG1vdmUsIELDqXppZXIgY29udmVyc2lvbnMpIGFyZSBhdmFpbGFibGVcbiAgICBpbiB0aGUgPGI+SW1hZ2U8L2I+IHZpZXcgb25seS4gT24gYSA8Yj5kb251dDwvYj4gKGEgcmVnaW9uIHdpdGggYSBob2xlKSwgdGhlIGlubmVyLXJpbmdcbiAgICB2ZXJ0aWNlcyBhcmUgZWRpdGFibGUgdG9vIOKAlCBkcmFnLCBhZGQsIGFuZCBkZWxldGUgdGhlbSBqdXN0IGxpa2UgdGhlIG91dGVyIHJpbmcuPC9wPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+U2VsZWN0aW9uPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktYXJyb3ctdXAtcmlnaHRcIj48L2k+ICg8a2JkPnM8L2tiZD4pOiBhIG5ldXRyYWwgbW9kZVxuICAgICAgdGhhdCB0dXJucyBvZmYgd2hhdGV2ZXIgZHJhd2luZyB0b29sIHdhcyBhY3RpdmUgKHdhbmQsIHZlcnRleCBlcmFzZXIsIHJlY3RhbmdsZSwgZXRjLikuXG4gICAgICBVc2UgaXQgdG8gc3RlcCBvdXQgb2YgZWRpdGluZyB3aXRob3V0IHBpY2tpbmcgYW5vdGhlciB0b29sLjwvbGk+XG4gICAgPGxpPlwiPGI+UmVjdGFuZ3VsYXIgcmVnaW9uPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktc3RvcFwiPjwvaT4gKDxrYmQ+cjwva2JkPik6IGNsaWNrIGFuZCBkcmFnIHRvXG4gICAgICBjcmVhdGUgYSByZWN0YW5ndWxhciByZWdpb24uPC9saT5cbiAgICA8bGk+XCI8Yj5Qb2x5bGluZTwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9wb2x5bGluZS5zdmdcIiB3aWR0aD1cIjE0XCIgaGVpZ2h0PVwiMTRcIj5cbiAgICAgICg8a2JkPmw8L2tiZD4pOiBkcmF3IGFuIG9wZW4gKG5vbi1jbG9zZWQpIHBvbHlsaW5lIGJ5IGNsaWNraW5nIGFuZCBkcmFnZ2luZy4gUG9seWxpbmVzIGNhblxuICAgICAgYmUgZWRpdGVkIGFuZCBleHBvcnRlZCB0byBHZW9KU09OIGFzIGEgPGNvZGU+TGluZVN0cmluZzwvY29kZT4gZmVhdHVyZS48L2xpPlxuICAgIDxsaT5cIjxiPkZyZWVmb3JtIHJlZ2lvbjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXBlbmNpbFwiPjwvaT4gKDxrYmQ+Zjwva2JkPik6IGNsaWNrIGFuZCBkcmFnIHRvXG4gICAgICBkcmF3IGEgY2xvc2VkIGZyZWVmb3JtIHBvbHlnb24uPC9saT5cbiAgICA8bGk+XCI8Yj5CcnVzaDwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9wYWludGJydXNoLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjogY2xpY2tcbiAgICAgIGFuZCBkcmFnIHRvIHBhaW50IGEgcmVnaW9uIHdpdGggYSBjaXJjdWxhciBicnVzaCDigJQgdGhlIHBhaW50ZWQgc3Ryb2tlIGlzIHRoZSByZWdpb24uXG4gICAgICBBIDxiPlNpemU8L2I+IHNsaWRlciAoYnJ1c2ggZGlhbWV0ZXIsIGluIGltYWdlIHBpeGVscykgYXBwZWFycyBpbiB0aGUgdG9vbGJhciB3aGlsZSB0aGVcbiAgICAgIGJydXNoIGlzIGFjdGl2ZS4gSG9sZCA8a2JkPlNoaWZ0PC9rYmQ+IHdoaWxlIHBhaW50aW5nIHRvIDxiPmVyYXNlPC9iPiAoc3VidHJhY3QpIGZyb20gYVxuICAgICAgcmVnaW9uIGluc3RlYWQuIFBhaW50aW5nIG92ZXIgYW4gZXhpc3RpbmcgcmVnaW9uIGV4dGVuZHMgaXQ7IGJydXNoaW5nIGFjcm9zcyB0d28gcmVnaW9uc1xuICAgICAgbWVyZ2VzIHRoZW0uIFBhaW50aW5nIGEgY2xvc2VkIHJpbmcgKG9yIHNoaWZ0LWVyYXNpbmcgYW4gaW50ZXJpb3IgcG9ja2V0KSBsZWF2ZXMgdGhlXG4gICAgICBlbmNsb3NlZCBhcmVhIGVtcHR5IOKAlCBhIDxiPmRvbnV0PC9iPiAoSW1hZ2UgdmlldykuPC9saT5cbiAgICA8bGk+XCI8Yj5Qb2x5Z29uPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3BvbHlnb24tdmVydGljZXMuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgY2xpY2sgdG8gcGxhY2UgZWFjaCB2ZXJ0ZXg7IGNsaWNrIHRoZSBmaXJzdCBwb2ludCB0byBjbG9zZSB0aGUgcG9seWdvbi48L2xpPlxuICAgIDxsaT5cIjxiPkFkZCB2ZXJ0ZXg8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvdmVydGV4LWFkZC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46XG4gICAgICBjbGljayBhbiBlZGdlIG9mIHRoZSBzZWxlY3RlZCBwb2x5Z29uIHRvIGluc2VydCBhIG5ldyB2ZXJ0ZXguPC9saT5cbiAgICA8bGk+XCI8Yj5EZWxldGUgdmVydGV4PC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3ZlcnRleC1kZWxldGUuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgY2xpY2sgYSB2ZXJ0ZXggb2YgdGhlIHNlbGVjdGVkIHBvbHlnb24gdG8gcmVtb3ZlIGl0LjwvbGk+XG4gICAgPGxpPlwiPGI+TW92ZSByZWdpb248L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVnaW9uLW1vdmUuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgZHJhZyB0byBtb3ZlIHRoZSBzZWxlY3RlZCByZWdpb24uPC9saT5cbiAgICA8bGk+XCI8Yj5UbyBCw6l6aWVyPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3RvLWJlemllci5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj4gL1xuICAgICAgXCI8Yj5UbyBwb2x5Z29uPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3RvLXBvbHlnb24uc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgY29udmVydCB0aGUgc2VsZWN0ZWQgcmVnaW9uIHRvIGEgc21vb3RoIELDqXppZXIgY3VydmUsIG9yIGJhY2sgdG8gYSBzdHJhaWdodC1lZGdlZCBwb2x5Z29uLjwvbGk+XG4gICAgPGxpPlwiPGI+V2FuZDwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy93YW5kLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPiAoPGtiZD53PC9rYmQ+KTpcbiAgICAgIGNsaWNrIHRvIGdyb3cgYSByZWdpb24gZnJvbSBwaXhlbHMgd2l0aCBzaW1pbGFyIHZhbHVlcyBhcm91bmQgdGhlIGNsaWNrIHBvaW50LiBIb2xkXG4gICAgICA8a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4gZm9yIGFuIGV4YWN0LW1hdGNoIGZsb29kIGZpbGw7IGRyYWcgdG8ga2VlcCBleHRlbmRpbmcgdGhlXG4gICAgICBzZWxlY3Rpb24gd2hpbGUgbW92aW5nOyBob2xkIDxrYmQ+U2hpZnQ8L2tiZD4gd2hpbGUgY2xpY2tpbmcgb3IgZHJhZ2dpbmcgb24gYW4gZXhpc3RpbmdcbiAgICAgIHJlZ2lvbiB0byA8Yj5lcmFzZTwvYj4gcGl4ZWxzIGZyb20gaXQuIFVzZSB0aGUgPGI+U2Vucy48L2I+IHNsaWRlciB0aGF0IGFwcGVhcnMgdG8gbWFrZSB0aGVcbiAgICAgIHdhbmQgc3RyaWN0ZXIgKGhpZ2hlciA9IHNtYWxsZXIgc2VsZWN0aW9uKSBvciBsb29zZXIuIFN1YnNlcXVlbnQgY2xpY2tzIGtlZXAgZXh0ZW5kaW5nIHRoZVxuICAgICAgc2FtZSByZWdpb247IGNsaWNraW5nIGluc2lkZSBhbiBleGlzdGluZyByZWdpb24gYWRvcHRzIGl0LCBhbmQgZ3Jvd2luZyBpbnRvIGFub3RoZXIgcmVnaW9uXG4gICAgICBtZXJnZXMgdGhlbS48L2xpPlxuICAgIDxsaT5cIjxiPlZlcnRleCBlcmFzZXI8L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1lcmFzZXJcIj48L2k+ICg8a2JkPmU8L2tiZD4pOiBjbGljayBvciBjbGljay1hbmQtZHJhZ1xuICAgICAgdG8gcmVtb3ZlIHBvbHlnb24gdmVydGljZXMgdGhhdCBmYWxsIGluc2lkZSB0aGUgY3Vyc29yIGNpcmNsZSBmcm9tIGFueSByZWdpb24sIGluY2x1ZGluZyBhXG4gICAgICByZWdpb24ncyBpbm5lciByaW5ncyAoZG9udXQgaG9sZXMpLiBVc2UgdGhlIDxiPlJhZGl1czwvYj4gc2xpZGVyIHRvIHNldCB0aGUgZXJhc2VyIHNpemUgaW5cbiAgICAgIGltYWdlIHBpeGVscy4gUG9seWdvbnMgcmVkdWNlZCBiZWxvdyAzIHZlcnRpY2VzIChvciBwb2x5bGluZXMgYmVsb3cgMikgYXJlIGRlbGV0ZWQgZW50aXJlbHk7XG4gICAgICBhIGhvbGUgcmVkdWNlZCBiZWxvdyAzIHZlcnRpY2VzIGlzIHJlbW92ZWQuPC9saT5cbiAgICA8bGk+XCI8Yj5VbmRvPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3VuZG8uc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+XG4gICAgICAoPGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+KzxrYmQ+Wjwva2JkPik6IHJldmVydHMgdGhlXG4gICAgICBsYXN0IHJlZ2lvbiBhY3Rpb24gKGRyYXcsIGRlbGV0ZSwgZWRpdCwgd2FuZC9icnVzaCBzdHJva2UsIHNlZ21lbnRhdGlvbiwg4oCmKS4gVXAgdG8gdGhlIGxhc3RcbiAgICAgIDxiPjEwPC9iPiBhY3Rpb25zIGNhbiBiZSB1bmRvbmU7IHRoZSBidXR0b24gZ3JleXMgb3V0IHdoZW4gdGhlcmUncyBub3RoaW5nIGxlZnQgdG8gdW5kby48L2xpPlxuICAgIDxsaT5cIjxiPlJlZG88L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVkby5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgICg8a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4rPGtiZD5TaGlmdDwva2JkPis8a2JkPlo8L2tiZD4gb3IgPGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+KzxrYmQ+WTwva2JkPik6XG4gICAgICByZS1hcHBsaWVzIHRoZSBtb3N0IHJlY2VudGx5IHVuZG9uZSBhY3Rpb24uIFBlcmZvcm1pbmcgYSBuZXcgcmVnaW9uIGFjdGlvbiBjbGVhcnMgd2hhdCBjYW4gYmUgcmVkb25lLjwvbGk+XG4gICAgPGxpPlwiPGI+RGVsZXRlIHJlZ2lvbjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXRyYXNoXCI+PC9pPiAoPGtiZD5EZWw8L2tiZD4pOiBkZWxldGVzIHRoZSBzZWxlY3RlZFxuICAgICAgcmVnaW9uLiBUaGlzIGNhbiBhbHNvIGJlIGRvbmUgZnJvbSB0aGUgUmVnaW9uIEVkaXRvci48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5SZWdpb24gYWN0aW9ucyAocmlnaHQtY2xpY2sgdGhlIGltYWdlKTwvaDM+XG4gIDxwPlJpZ2h0LWNsaWNrIGEgcmVnaW9uIOKAlCBvciB0aGUgY3VycmVudCBtdWx0aS1zZWxlY3Rpb24g4oCUIHRvIG9wZW4gdGhlXG4gICAgXCI8Yj5TZWxlY3RlZCByZWdpb24ocyk8L2I+XCIgZ3JvdXAgYXQgdGhlIHRvcCBvZiB0aGUgY29udGV4dCBtZW51LiBUaGVzZSBhY3QgaW1tZWRpYXRlbHkgb25cbiAgICB0aGUgc2VsZWN0aW9uICh0aGV5J3JlIG5vdCBkcmF3aW5nIG1vZGVzKSBhbmQgYXJlIGZ1bGx5IHVuZG8vcmVkby1hYmxlOjwvcD5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPlNlbGVjdCBhbGwgcmVnaW9uczwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLWNoZWNrLXNxdWFyZVwiPjwvaT46IHNlbGVjdCBldmVyeSByZWdpb24gb25cbiAgICAgIHRoZSBpbWFnZSAoYWxzbyBpbiB0aGUgUmVnaW9uIEVkaXRvciB0b29sYmFyKS48L2xpPlxuICAgIDxsaT5cIjxiPk1lcmdlIC8gZ3JvdXA8L2I+XCI6IGNvbWJpbmUgMisgc2VsZWN0ZWQgcmVnaW9ucyBpbnRvIG9uZS4gSWYgdGhlIHBpZWNlcyBkb24ndCB0b3VjaCxcbiAgICAgIHRoZSByZXN1bHQgaXMgYSBzaW5nbGUgbXVsdGktcGFydCByZWdpb24gKHNwYXJzZSBcImJpdHNcIikuPC9saT5cbiAgICA8bGk+XCI8Yj5Vbmdyb3VwPC9iPlwiOiBzcGxpdCBhIG11bHRpLXBhcnQgcmVnaW9uIGJhY2sgaW50byBvbmUgcmVnaW9uIHBlciBwaWVjZS48L2xpPlxuICAgIDxsaT5cIjxiPkludmVyc2U8L2I+XCI6IHJlcGxhY2UgdGhlIHNlbGVjdGlvbiB3aXRoIGV2ZXJ5dGhpbmcgPGk+b3V0c2lkZTwvaT4gaXQg4oCUIHRoZSBpbWFnZVxuICAgICAgcmVjdGFuZ2xlIHdpdGggdGhlIHNlbGVjdGVkIHJlZ2lvbnMgcHVuY2hlZCBvdXQgYXMgaG9sZXMuIFdvcmtzIGF0IGFueSBpbWFnZSBzaXplLjwvbGk+XG4gICAgPGxpPlwiPGI+U2ltcGxpZnk8L2I+XCI6IHJlZHVjZSBhIHJlZ2lvbidzIHZlcnRleCBjb3VudCAoRG91Z2xhc+KAk1BldWNrZXIpIGJ5IGFuIGFsdGl0dWRlXG4gICAgICB0aHJlc2hvbGQgaW4gcGl4ZWxzIOKAlCBwaWNrIDxiPkxpZ2h0PC9iPiAoMSZuYnNwO3B4KSwgPGI+TWVkaXVtPC9iPiAoMyZuYnNwO3B4KSxcbiAgICAgIDxiPlN0cm9uZzwvYj4gKDgmbmJzcDtweCksIG9yIDxiPkN1c3RvbeKApjwvYj4gZm9yIGEgc2xpZGVyLiBBcHBsaWVzIHRvIHRoZSBleHRlcmlvciBhbmQgYW55XG4gICAgICBob2xlcyAoYW5kIGV2ZXJ5IHBhcnQgb2YgYSBtdWx0aS1wYXJ0IHJlZ2lvbikuPC9saT5cbiAgICA8bGk+VGhlIELDqXppZXIgY29udmVyc2lvbnMgYW5kIERlbGV0ZSBhbHNvIGxpdmUgaW4gdGhpcyBncm91cCAodGhlIHJpZ2h0LWNsaWNrIGVxdWl2YWxlbnRzIG9mXG4gICAgICB0aGUgdG9vbGJhciAvIHZlcnRleCB0b29scyBhYm92ZSkuPC9saT5cbiAgPC91bD5cblxuICA8aDM+U2VnbWVudGF0aW9uIHRvb2xzIChTQU0gJmFtcDsgY2VsbHBvc2UpPC9oMz5cbiAgPHA+QUktYXNzaXN0ZWQgc2VnbWVudGF0aW9uIHRoYXQgcnVucyBlbnRpcmVseSBpbiB0aGUgYnJvd3Nlci4gVGhlIGJveCBhbmQgcG9pbnRcbiAgICB0b29scyB1c2UgYSBwcm9tcHRhYmxlIDxiPm1pY3JvLXNhbTwvYj4gbW9kZWw7IHRoZSBjZWxscG9zZSB0b29sIGF1dG8tZGV0ZWN0cyBjZWxscy5cbiAgICBHZW5lcmF0ZWQgcmVnaW9ucyBpbmhlcml0IHRoZSBjb2xvciBvZiB0aGUgcmVjdGFuZ2xlIHRoZXkgY2FtZSBmcm9tLiBUaGUgZmlyc3QgcnVuXG4gICAgZG93bmxvYWRzIHRoZSBtb2RlbCAoYSBwcm9ncmVzcyB0b2FzdCBpcyBzaG93bik7IGl0J3MgdGhlbiBjYWNoZWQuPC9wPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+U2VnbWVudDwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9waWN0dXJlLXNlZ21lbnRhdGlvbi5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgIChib3ggcHJvbXB0KTogZHJhdyBvbmUgb3IgbW9yZSA8Yj5SZWN0YW5ndWxhciByZWdpb25zPC9iPiBhcm91bmQgdGhlIG9iamVjdHMgb2YgaW50ZXJlc3QsXG4gICAgICB0aGVuIGNsaWNrIFNlZ21lbnQuIEVhY2ggcmVjdGFuZ2xlIGlzIHJlcGxhY2VkIGJ5IHRoZSBTQU0gbWFzayBvZiB0aGUgb2JqZWN0IGluc2lkZSBpdC48L2xpPlxuICAgIDxsaT5cIjxiPlBvaW50IHByb21wdHM8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvY2xpY2suc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OiBjbGljayBkaXJlY3RseSBvbiBhbiBvYmplY3QgdG9cbiAgICAgIHNlZ21lbnQgaXQgYXMgYSA8Yj5uZXc8L2I+IHJlZ2lvbi4gRWFjaCBjbGljayBzdGFydHMgYSBmcmVzaCBvYmplY3Qg4oCUIGNsaWNraW5nIGFub3RoZXJcbiAgICAgIG9iamVjdCB3b24ndCBncm93IHRoZSBwcmV2aW91cyBvbmUuIDxrYmQ+U2hpZnQ8L2tiZD4vPGtiZD5BbHQ8L2tiZD4tY2xpY2sgYWRkcyBhblxuICAgICAgPGk+ZXhjbHVkZTwvaT4gcG9pbnQgdGhhdCByZWZpbmVzIHRoZSBjdXJyZW50IG9iamVjdDsgPGtiZD5Fc2M8L2tiZD4gdW5kb2VzIGl0LFxuICAgICAgPGtiZD5FbnRlcjwva2JkPiBjb21taXRzIGl0LjwvbGk+XG4gICAgPGxpPlwiPGI+TW9kZWwgcGlja2VyPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktY2hldnJvbi1kb3duXCI+PC9pPjogdGhlIGRyb3Bkb3duIGF0dGFjaGVkIHRvIHRoZVxuICAgICAgU2VnbWVudCBidXR0b24gY2hvb3NlcyB0aGUgU0FNIG1vZGVsIOKAlCA8Yj5taWNyby1zYW0gVmlULVQ8L2I+IChkZWZhdWx0OyBzbWFsbCwgZmFzdCkgb3JcbiAgICAgIDxiPm1pY3JvLXNhbSBWaVQtQjwvYj4gKGxhcmdlciwgaGlnaGVzdCBhY2N1cmFjeSkgZm9yIGxpZ2h0IG1pY3Jvc2NvcHksIG9yXG4gICAgICA8Yj5wYXRoby1zYW0gVmlULUI8L2I+IGZvciBoaXN0b3BhdGhvbG9neSAoSCZhbXA7RSAvIHN0YWluZWQgdGlzc3VlKS4gVGhlIGNob2ljZSBhcHBsaWVzIHRvXG4gICAgICA8aT5ib3RoPC9pPiB0aGUgYm94IGFuZCBwb2ludCB0b29scy4gVGhlIDxpIGNsYXNzPVwicGkgcGktaW5mby1jaXJjbGVcIj48L2k+IGluZm8gYnV0dG9uIG5leHRcbiAgICAgIHRvIGl0IGV4cGxhaW5zIHRoZSB0cmFkZS1vZmYuPC9saT5cbiAgICA8bGk+XCI8Yj5DZWxscG9zZTwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9jZWxscy5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46IGRyYXcgcmVjdGFuZ2xlcywgdGhlbiBjbGljayBDZWxscG9zZSB0b1xuICAgICAgYXV0b21hdGljYWxseSBzZWdtZW50IGV2ZXJ5IGNlbGwgaW5zaWRlIGVhY2ggcmVjdGFuZ2xlIChjbGllbnQtc2lkZSBjZWxscG9zZS1TQU0pIOKAlCBvbmVcbiAgICAgIHJlZ2lvbiBwZXIgZGV0ZWN0ZWQgY2VsbC4gTm8gY2xpY2tpbmcgb24gaW5kaXZpZHVhbCBjZWxscyBuZWVkZWQuPC9saT5cbiAgPC91bD5cblxuICA8aDM+SGVscDwvaDM+XG4gIDxwPlwiPGI+SGVscDwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXF1ZXN0aW9uLWNpcmNsZVwiPjwvaT46IG9wZW5zIHRoaXMgZGlhbG9nLjwvcD5cblxuICA8aDM+S2V5Ym9hcmQgc2hvcnRjdXRzPC9oMz5cbiAgPHRhYmxlIGNsYXNzPVwic2hvcnRjdXRzLXRhYmxlXCI+XG4gICAgPHRyPjx0ZD48a2JkPis8L2tiZD4gLyA8a2JkPj08L2tiZD48L3RkPjx0ZD5ab29tIGluPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD4tPC9rYmQ+PC90ZD48dGQ+Wm9vbSBvdXQ8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPmI8L2tiZD48L3RkPjx0ZD5Ub2dnbGUgem9vbSBib3ggbW9kZTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+cDwva2JkPjwvdGQ+PHRkPlRvZ2dsZSBwYW4gbW9kZTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+Zjwva2JkPjwvdGQ+PHRkPlRvZ2dsZSBmcmVlZm9ybSByZWdpb24gZHJhd2luZzwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+bDwva2JkPjwvdGQ+PHRkPlRvZ2dsZSBwb2x5bGluZSBkcmF3aW5nPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5yPC9rYmQ+PC90ZD48dGQ+VG9nZ2xlIHJlY3Rhbmd1bGFyIHJlZ2lvbiBkcmF3aW5nPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD53PC9rYmQ+PC90ZD48dGQ+VG9nZ2xlIHdhbmQgdG9vbDwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPldhbmQgKyA8a2JkPlNoaWZ0PC9rYmQ+PC90ZD48dGQ+RXJhc2UgcGl4ZWxzIGZyb20gdGhlIGFjdGl2ZSByZWdpb248L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD5XYW5kICsgPGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+PC90ZD48dGQ+RXhhY3QtbWF0Y2ggZmxvb2QgZmlsbCAobm8gc21vb3RoaW5nKTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+ZTwva2JkPjwvdGQ+PHRkPlRvZ2dsZSB2ZXJ0ZXggZXJhc2VyPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5zPC9rYmQ+PC90ZD48dGQ+U2VsZWN0aW9uIG1vZGUgKGRlYWN0aXZhdGVzIGRyYXdpbmcgdG9vbHMpPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+UG9pbnQgcHJvbXB0ICsgPGtiZD5TaGlmdDwva2JkPi88a2JkPkFsdDwva2JkPjwvdGQ+PHRkPkFkZCBhbiBleGNsdWRlIHBvaW50IChyZWZpbmUgdGhlIGN1cnJlbnQgb2JqZWN0KTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+RW50ZXI8L2tiZD48L3RkPjx0ZD5Db21taXQgdGhlIGN1cnJlbnQgU0FNIHBvaW50IG9iamVjdDwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+RXNjPC9rYmQ+PC90ZD48dGQ+VW5kbyB0aGUgY3VycmVudCBTQU0gcG9pbnQgb2JqZWN0PC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5EZWw8L2tiZD4gLyA8a2JkPuKMqzwva2JkPjwvdGQ+PHRkPkRlbGV0ZSBzZWxlY3RlZCByZWdpb248L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4rPGtiZD5aPC9rYmQ+PC90ZD48dGQ+VW5kbyB0aGUgbGFzdCByZWdpb24gYWN0aW9uICh1cCB0byAxMCk8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4rPGtiZD5TaGlmdDwva2JkPis8a2JkPlo8L2tiZD4gLyA8a2JkPkN0cmw8L2tiZD4rPGtiZD5ZPC9rYmQ+PC90ZD48dGQ+UmVkbyB0aGUgbGFzdCB1bmRvbmUgcmVnaW9uIGFjdGlvbjwvdGQ+PC90cj5cbiAgPC90YWJsZT5cblxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1jaGVja1wiIChjbGljayk9XCJkaXNwbGF5SGVscERpYWxvZz1mYWxzZVwiIGxhYmVsPVwiT2tcIiBzdHlsZUNsYXNzPVwicC1idXR0b24tdGV4dFwiPlxuICAgIDwvcC1idXR0b24+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtZGlhbG9nPlxuIl19
394
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbGJhci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQWEsTUFBTSxFQUFpQixNQUFNLGVBQWUsQ0FBQztBQUlqRyxPQUFPLEVBQ0wsUUFBUSxFQUVSLFVBQVUsRUFDVixrQkFBa0IsRUFDbEIsZUFBZSxFQUNmLGVBQWUsRUFDZix1QkFBdUIsRUFDdkIsdUJBQXVCLEdBQ3hCLE1BQU0sd0JBQXdCLENBQUM7QUFDaEMsT0FBTyxFQUF5QixpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXZGLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxjQUFjLENBQUM7Ozs7Ozs7Ozs7Ozs7O0FBRTFDOzs7Ozs7Ozs7OztHQVdHO0FBUUgsTUFBTSxPQUFPLGdCQUFnQjtJQVA3QjtRQVVFLGdEQUFnRDtRQUN2QyxvQkFBZSxHQUF5QixFQUFFLENBQUM7UUFDM0MscUJBQWdCLEdBQWEsUUFBUSxDQUFDLEtBQUssQ0FBQztRQUNyRDs0RUFDb0U7UUFDM0QsYUFBUSxHQUFhLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzlCLGFBQVEsR0FBRyxDQUFDLENBQUM7UUFDYixXQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ3BCLGtEQUFrRDtRQUN6QyxjQUFTLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLG1CQUFjLEdBQWtCLElBQUksQ0FBQztRQUNyQyx3QkFBbUIsR0FBRyxXQUFXLENBQUM7UUFDM0MsNEZBQTRGO1FBQ25GLGdCQUFXLEdBQUcsSUFBSSxDQUFDO1FBQ25CLG9CQUFlLEdBQUcsS0FBSyxDQUFDO1FBQ2pDLHNFQUFzRTtRQUM3RCxv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QU07Z0JBQ0csTUFBTTtzQkFBZixNQUFNO2dCQUNHLE1BQU07c0JBQWYsTUFBTTtnQkFDRyxXQUFXO3NCQUFwQixNQUFNO2dCQUNHLGFBQWE7c0JBQXRCLE1BQU07Z0JBQ0csYUFBYTtzQkFBdEIsTUFBTTtnQkFDRyxjQUFjO3NCQUF2QixNQUFNO2dCQUNHLGNBQWM7c0JBQXZCLE1BQU07Z0JBQ0csTUFBTTtzQkFBZixNQUFNO2dCQUNHLE9BQU87c0JBQWhCLE1BQU07Z0JBQ0csbUJBQW1CO3NCQUE1QixNQUFNO2dCQUNHLGtCQUFrQjtzQkFBM0IsTUFBTTtnQkFFRyxVQUFVO3NCQUFuQixNQUFNO2dCQUVHLGVBQWU7c0JBQXhCLE1BQU07Z0JBRUcsZ0JBQWdCO3NCQUF6QixNQUFNO2dCQUNHLFlBQVk7c0JBQXJCLE1BQU07Z0JBRUcsVUFBVTtzQkFBbkIsTUFBTTtnQkFFRyxVQUFVO3NCQUFuQixNQUFNO2dCQUVHLGNBQWM7c0JBQXZCLE1BQU07Z0JBRUcsZUFBZTtzQkFBeEIsTUFBTTtnQkFFRyxPQUFPO3NCQUFoQixNQUFNO2dCQUVHLGVBQWU7c0JBQXhCLE1BQU07Z0JBRUcsY0FBYztzQkFBdkIsTUFBTTtnQkFFRyxjQUFjO3NCQUF2QixNQUFNO2dCQUNHLHFCQUFxQjtzQkFBOUIsTUFBTTtnQkFDRyxlQUFlO3NCQUF4QixNQUFNO2dCQUNHLHdCQUF3QjtzQkFBakMsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgRXZlbnRFbWl0dGVyLCBJbnB1dCwgT25DaGFuZ2VzLCBPdXRwdXQsIFNpbXBsZUNoYW5nZXMgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE1lbnVJdGVtIH0gZnJvbSAncHJpbWVuZy9hcGknO1xuXG5pbXBvcnQgeyBJSW1hZ2VJbmZvIH0gZnJvbSAnLi4vY29udHJhY3RzL2ltYWdlLmNvbnRyYWN0JztcbmltcG9ydCB7XG4gIFBsb3RUeXBlLFxuICBQbG90VHlwZURlc2NyaXB0b3IsXG4gIGlzTmFwYXJpM2QsXG4gIGlzTmFwYXJpSXNvc3VyZmFjZSxcbiAgaXNOYXBhcmlTdXJmYWNlLFxuICBpc05hcGFyaVNjYXR0ZXIsXG4gIE5BUEFSSV9ERUNJTUFURV9PUFRJT05TLFxuICBOQVBBUklfREVGQVVMVF9ERUNJTUFURSxcbn0gZnJvbSAnLi4vY29udHJhY3RzL3Bsb3QtdHlwZSc7XG5pbXBvcnQgeyBUb29sYmFyVG9vbFZpc2liaWxpdHksIEFMTF9UT09MQkFSX1RPT0xTIH0gZnJvbSAnLi4vY29udHJhY3RzL3Rvb2xiYXItY29uZmlnJztcbmltcG9ydCB7IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uIH0gZnJvbSAnLi4vY29udHJhY3RzL3Rvb2xiYXItdG9vbC5jb250cmFjdCc7XG5pbXBvcnQgeyBNT0RFTF9JTkZPIH0gZnJvbSAnLi9tb2RlbC1pbmZvJztcblxuLyoqXG4gKiBQcmVzZW50YXRpb25hbCB0b29sYmFyIGZvciB0aGUgcGxvdHRpbmcgdmlld3BvcnQuXG4gKlxuICogUmVuZGVycyB0aGUgcGxvdC10eXBlIHNlbGVjdG9yLCBjb2xvcm1hcC9MVVQgY29udHJvbHMsIHN0YWNrIG5hdmlnYXRpb24sXG4gKiBpbWFnZSBhY3Rpb25zIChkb3dubG9hZCAvIGF1dG9zY2FsZSAvIHBpcGVsaW5lKSwgdmlld3BvcnQgem9vbS9wYW4sIHRoZVxuICogU3VyZmFjZS0zRCBjYW1lcmEgY29udHJvbHMsIGFuZCB0aGUgcmVnaW9uLWRyYXdpbmcgdG9vbHMg4oCUIHBsdXMgdGhlIHBsb3R0aW5nXG4gKiBoZWxwIGRpYWxvZy4gSXQgb3ducyBubyByZW5kZXJpbmcgb3IgdmlzdWFsaXphdGlvbiBzdGF0ZTogdGhlIGhvc3RcbiAqIChWaXN1YWxpemVyQ29tcG9uZW50KSBzdGF5cyB0aGUgb3JjaGVzdHJhdG9yLCBwYXNzaW5nIHN0YXRlIGluIHZpYSBgQElucHV0YCBhbmRcbiAqIGhhbmRsaW5nIGV2ZXJ5IGFjdGlvbiB2aWEgYEBPdXRwdXRgLiBUaGUgc2FtZSBhY3Rpb25zIGFyZSBhbHNvIGRyaXZlbiBieSB0aGVcbiAqIGRpYWdyYW0ncyByaWdodC1jbGljayBjb250ZXh0IG1lbnUgYW5kIGtleWJvYXJkIHNob3J0Y3V0cywgc28ga2VlcGluZyB0aGVcbiAqIGhhbmRsZXJzIGluIHRoZSBob3N0IGF2b2lkcyBkdXBsaWNhdGluZyB0aGF0IGxvZ2ljLlxuICovXG5AQ29tcG9uZW50KHtcbiAgLy8gQ2Fub25pY2FsIHByZWZpeGVkIHNlbGVjdG9yIGZpcnN0OyB0aGUgdW5wcmVmaXhlZCBvcmlnaW5hbCBpcyBrZXB0IGFzIGFuXG4gIC8vIGFsaWFzIGZvciBvbmUgcmVsZWFzZSAocHJlLXB1YmxpY2F0aW9uIGJhY2stY29tcGF0KS5cbiAgc2VsZWN0b3I6ICdwbG90dGluZy10b29sYmFyJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3Rvb2xiYXIuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi90b29sYmFyLmNvbXBvbmVudC5zY3NzJ10sXG59KVxuZXhwb3J0IGNsYXNzIFRvb2xiYXJDb21wb25lbnQgaW1wbGVtZW50cyBPbkNoYW5nZXMge1xuICAvKiogQ3VycmVudCBpbWFnZSAoZ2F0ZXMgd2hpY2ggY29udHJvbCBncm91cHMgYXJlIHNob3duKS4gKi9cbiAgQElucHV0KCkgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkO1xuICAvKiogUGxvdCB0eXBlcyB0aGUgYWN0aXZlIGJhY2tlbmQgYWR2ZXJ0aXNlcy4gKi9cbiAgQElucHV0KCkgcGxvdFR5cGVPcHRpb25zOiBQbG90VHlwZURlc2NyaXB0b3JbXSA9IFtdO1xuICBASW5wdXQoKSBzZWxlY3RlZFBsb3RUeXBlOiBQbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuICAvKiogSXNvc3VyZmFjZSBiYW5kIGFzIGEgMOKAkzI1NSBzbGlkZXIgcG9zaXRpb24gKG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUncyByZWFsXG4gICAqICBpbnRlbnNpdHkgcmFuZ2UgYnkgdGhlIHJlbmRlcmVyKS4gRGVmYXVsdHMgdG8gdGhlIGZ1bGwgcmFuZ2UuICovXG4gIEBJbnB1dCgpIGlzb1JhbmdlOiBudW1iZXJbXSA9IFswLCAyNTVdO1xuICBASW5wdXQoKSBtYXhJbmRleCA9IDA7XG4gIEBJbnB1dCgpIHpJbmRleCA9IDA7XG4gIC8qKiBUcnVlIGZvciBhbnkgMkQgKG5vbiBTdXJmYWNlLTNEKSBwbG90IHR5cGUuICovXG4gIEBJbnB1dCgpIGlzSGVhdG1hcCA9IHRydWU7XG4gIEBJbnB1dCgpIGFjdGl2ZURyYWdNb2RlOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgQElucHV0KCkgYWN0aXZlU3VyZmFjZTNkTW9kZSA9ICd0dXJudGFibGUnO1xuICAvKiogV2hldGhlciB0aGUgbmFwYXJpIDNEIGF4ZXMvc2NhbGUgZ2l6bW8gaXMgY3VycmVudGx5IHNob3duIChkcml2ZXMgdGhlIHRvZ2dsZSdzIGxvb2spLiAqL1xuICBASW5wdXQoKSBheGVzVmlzaWJsZSA9IHRydWU7XG4gIEBJbnB1dCgpIHdpcmVmcmFtZUFjdGl2ZSA9IGZhbHNlO1xuICAvKiogQWN0aXZlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKDEgPSBGdWxsIOKApiA4ID0g4oWbOyBkZWZhdWx0IMK9KS4gKi9cbiAgQElucHV0KCkgcmVzb2x1dGlvblNjYWxlID0gTkFQQVJJX0RFRkFVTFRfREVDSU1BVEU7XG4gIC8qKiBEZWNpbWF0ZS1mYWN0b3Igb3B0aW9ucyBmb3IgdGhlIFJlc29sdXRpb24gZHJvcGRvd24uICovXG4gIHJlYWRvbmx5IGRlY2ltYXRlT3B0aW9ucyA9IE5BUEFSSV9ERUNJTUFURV9PUFRJT05TO1xuICBASW5wdXQoKSB3YW5kU2Vuc2l0aXZpdHkgPSAyLjA7XG4gIEBJbnB1dCgpIGJydXNoU2l6ZSA9IDQwO1xuICBASW5wdXQoKSB2ZXJ0ZXhFcmFzZXJSYWRpdXMgPSAyMDtcbiAgLyoqIFdoZXRoZXIgYSByZWdpb24gYWN0aW9uIGlzIGF2YWlsYWJsZSB0byB1bmRvIChqaXQtdWkjODUpLiBHcmV5cyBvdXQgdGhlXG4gICAqICBVbmRvIGJ1dHRvbiB3aGVuIGZhbHNlIChub3RoaW5nIGRvbmUgeWV0LCBvciBhbGwgc3RlcHMgYWxyZWFkeSB1bmRvbmUpLiAqL1xuICBASW5wdXQoKSBjYW5VbmRvID0gZmFsc2U7XG4gIC8qKiBXaGV0aGVyIGFuIHVuZG9uZSByZWdpb24gYWN0aW9uIGlzIGF2YWlsYWJsZSB0byByZWRvIChqaXQtdWkjODUpLiBHcmV5c1xuICAgKiAgb3V0IHRoZSBSZWRvIGJ1dHRvbiB3aGVuIGZhbHNlLiAqL1xuICBASW5wdXQoKSBjYW5SZWRvID0gZmFsc2U7XG4gIEBJbnB1dCgpIHN0YWNrT3B0aW9uczogeyBuYW1lOiBzdHJpbmc7IHZhbDogc3RyaW5nIH1bXSA9XG4gICAgW3sgbmFtZTogJ1NpbmdsZSBpbWFnZScsIHZhbDogJ2ZhbHNlJyB9LCB7IG5hbWU6ICdTdGFjaycsIHZhbDogJ3RydWUnIH1dO1xuICAvKiogV2hpY2ggdG9vbGJhciBncm91cHMgdG8gc2hvdy4gRGVmYXVsdHMgdG8gdGhlIGZ1bGwgdG9vbGJhcjsgdGhlIGhvc3QgZm9yd2FyZHNcbiAgICogIHRoZSBjb25zdW1lcidzIGNob2ljZSAoZS5nLiB0aGUgcGlwZWxpbmUgc2hvd3Mgb25seSB6b29tICsgcmVnaW9uIHRvb2xzKS4gKi9cbiAgQElucHV0KCkgdG9vbHM6IFJlcXVpcmVkPFRvb2xiYXJUb29sVmlzaWJpbGl0eT4gPSBBTExfVE9PTEJBUl9UT09MUztcbiAgLyoqIEN1cnJlbnQgaW1hZ2Utc21vb3RoaW5nIHN0YXRlIChkcml2ZXMgdGhlIFNtb290aGVuIHRvZ2dsZSBidXR0b24gbG9vaykuXG4gICAqICBgZmFsc2VgID0gbmVhcmVzdC1uZWlnaGJvdXIgKGNyaXNwIHJhdyBwaXhlbHMpLCB0aGUgZGVmYXVsdC4gKi9cbiAgQElucHV0KCkgaW1hZ2VTbW9vdGhpbmdFbmFibGVkID0gZmFsc2U7XG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIG9wdGlvbnMgKyBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzkwIFAxKS4gKi9cbiAgQElucHV0KCkgc2FtTW9kZWxzOiB7IGlkOiBzdHJpbmc7IGxhYmVsOiBzdHJpbmcgfVtdID0gW107XG4gIEBJbnB1dCgpIHNhbU1vZGVsSWQgPSAnJztcbiAgLyoqXG4gICAqIFRvb2xzIGNvbnRyaWJ1dGVkIHRocm91Z2gge0BsaW5rIFRPT0xCQVJfVE9PTFN9LCBhbHJlYWR5IGZpbHRlcmVkIHRvIHRoZVxuICAgKiBvbmVzIHdpdGggbW9kZWxzIGFuZCBzb3J0ZWQuIFRoZSBob3N0IHJlc29sdmVzIHRoZW0gc28gaXQgY2FuIG93biB0aGVcbiAgICogcGVyLXRvb2wgcGFyYW1ldGVyIHN0YXRlOyB0aGlzIGNvbXBvbmVudCBvbmx5IGRyYXdzIHRoZW0uXG4gICAqL1xuICBASW5wdXQoKSBjb250cmlidXRlZFRvb2xzOiBUb29sYmFyVG9vbENvbnRyaWJ1dGlvbltdID0gW107XG4gIC8qKiBBY3RpdmUgY2hlY2twb2ludCBwZXIgY29udHJpYnV0ZWQgdG9vbCwga2V5ZWQgYnkgdG9vbCBpZC4gKi9cbiAgQElucHV0KCkgdG9vbE1vZGVsSWRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgQE91dHB1dCgpIHNlbGVjdFBsb3RUeXBlID0gbmV3IEV2ZW50RW1pdHRlcjxQbG90VHlwZT4oKTtcbiAgLyoqIEludGVuc2l0eSAoTElORSkgbW9kZTogYWRkIGFub3RoZXIgY29sb3JlZCBsaW5lIFJPSSArIGluc2V0IHRyYWNlLiAqL1xuICBAT3V0cHV0KCkgYWRkUHJvZmlsZUxpbmUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBUb2dnbGUgaW1hZ2Ugc21vb3RoaW5nIChiaWxpbmVhcikgdnMgbmVhcmVzdC1uZWlnaGJvdXIgKGNyaXNwIHBpeGVscykuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVJbWFnZVNtb290aGluZyA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLy8gUHJpbWVORyBzbGlkZXIvaW5wdXROdW1iZXIgY2hhbmdlIGV2ZW50cyBjYXJyeSBvcHRpb25hbCB2YWx1ZXMsIG1pcnJvcmVkIGJ5XG4gIC8vIHRoZSBob3N0IGhhbmRsZXJzICh3aGljaCBhY2NlcHQgYHwgdW5kZWZpbmVkYCkuXG4gIEBPdXRwdXQoKSBpc29SYW5nZUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyW10gfCB1bmRlZmluZWQ+KCk7XG4gIC8qKiBPcGVuIHRoZSBDaGFubmVscyAmIEhpc3RvZ3JhbSBkaWFsb2cgKGJyaWdodG5lc3MvY29udHJhc3QsIGNvbG9ybWFwLCDigKYpLiAqL1xuICBAT3V0cHV0KCkgb3BlbkNoYW5uZWxIaXN0b2dyYW0gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSBvcGVuUmVnaW9uRWRpdG9yID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgc2VsZWN0U3RhY2tPcHRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHsgbmFtZTogc3RyaW5nOyB2YWw6IHN0cmluZyB9PigpO1xuICBAT3V0cHV0KCkgelNjcnViID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIEBPdXRwdXQoKSB6U2xpZGUgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcbiAgQE91dHB1dCgpIHpJbmRleElucHV0ID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG4gIEBPdXRwdXQoKSByZWxvYWRBbmRQbG90ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgZG93bmxvYWRJbWFnZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIGF1dG9zY2FsZUltYWdlID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgdG9nZ2xlRHJhZ01vZGUgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgQE91dHB1dCgpIHpvb21JbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIHpvb21PdXQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSB0b2dnbGVTdXJmYWNlM2RNb2RlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIEBPdXRwdXQoKSByZXNldFN1cmZhY2VDYW1lcmEgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBUb2dnbGUgdGhlIG5hcGFyaSAzRCBjb29yZGluYXRlLWF4ZXMgLyBzY2FsZSBnaXptbyBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVBeGVzID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogVG9nZ2xlIHRoZSBuYXBhcmkgc3VyZmFjZSB3aXJlZnJhbWUgKGVkZ2VzKSBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVXaXJlZnJhbWUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBDaGFuZ2UgdGhlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKHJlbG9hZHMgYXQgdGhlIG5ldyByZXNvbHV0aW9uKS4gKi9cbiAgQE91dHB1dCgpIHNlbGVjdFJlc29sdXRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlcj4oKTtcbiAgQE91dHB1dCgpIGRlbGV0ZVJlZ2lvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFVuZG8gdGhlIGxhc3QgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gKi9cbiAgQE91dHB1dCgpIHVuZG9SZWdpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBSZWRvIHRoZSBsYXN0IHVuZG9uZSByZWdpb24gYWN0aW9uIChqaXQtdWkjODUpLiAqL1xuICBAT3V0cHV0KCkgcmVkb1JlZ2lvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biBib3gtcHJvbXB0ZWQgU0FNIHNlZ21lbnRhdGlvbiBvbiB0aGUgZHJhd24gcmVjdGFuZ2xlcyAoaml0LXVpIzkwKS4gKi9cbiAgQE91dHB1dCgpIHNlZ21lbnRSZWdpb25zID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogUnVuIGNlbGxwb3NlLVNBTSAoYXV0bykgaW5zaWRlIGVhY2ggZHJhd24gcmVjdGFuZ2xlJ3MgY3JvcCAoaml0LXVpIzkwKS4gKi9cbiAgQE91dHB1dCgpIHNlZ21lbnRDZWxscG9zZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biBhIGNvbnRyaWJ1dGVkIHRvb2wgb3ZlciB0aGUgY3VycmVudCB2aWV3LCBieSB0b29sIGlkLiAqL1xuICBAT3V0cHV0KCkgcnVuVG9vbCA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuICAvKiogUGljayBhIGNvbnRyaWJ1dGVkIHRvb2wncyBjaGVja3BvaW50LiAqL1xuICBAT3V0cHV0KCkgdG9vbE1vZGVsQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjx7IHRvb2xJZDogc3RyaW5nOyBtb2RlbElkOiBzdHJpbmcgfT4oKTtcbiAgLyoqIE9wZW4gYSBjb250cmlidXRlZCB0b29sJ3MgcGFyYW1ldGVyIGRpYWxvZywgYnkgdG9vbCBpZC4gKi9cbiAgQE91dHB1dCgpIG9wZW5Ub29sUGFyYW1zID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIChqaXQtdWkjOTAgUDEpLiAqL1xuICBAT3V0cHV0KCkgc2FtTW9kZWxDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgQE91dHB1dCgpIHdhbmRTZW5zaXRpdml0eUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyIHwgdW5kZWZpbmVkPigpO1xuICBAT3V0cHV0KCkgYnJ1c2hTaXplQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIEBPdXRwdXQoKSB2ZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcblxuICAvKiogU2xpZGVycycgYm91bmRzIOKAlCB0b29sYmFyIFVJIGNvbnN0YW50cyAodGhlIHZhbHVlcyBhcmUgaG9zdC1vd25lZCkuICovXG4gIHJlYWRvbmx5IHdhbmRTZW5zaXRpdml0eU1pbiA9IDAuNTtcbiAgcmVhZG9ubHkgd2FuZFNlbnNpdGl2aXR5TWF4ID0gMTAuMDtcbiAgcmVhZG9ubHkgd2FuZFNlbnNpdGl2aXR5U3RlcCA9IDAuMTtcbiAgcmVhZG9ubHkgYnJ1c2hTaXplTWluID0gNTtcbiAgcmVhZG9ubHkgYnJ1c2hTaXplTWF4ID0gMzAwO1xuICByZWFkb25seSBicnVzaFNpemVTdGVwID0gNTtcbiAgcmVhZG9ubHkgdmVydGV4RXJhc2VyUmFkaXVzTWluID0gNTtcbiAgcmVhZG9ubHkgdmVydGV4RXJhc2VyUmFkaXVzTWF4ID0gMzAwO1xuICByZWFkb25seSB2ZXJ0ZXhFcmFzZXJSYWRpdXNTdGVwID0gNTtcbiAgcmVhZG9ubHkgaXNvVmFsdWVNaW4gPSAwO1xuICByZWFkb25seSBpc29WYWx1ZU1heCA9IDI1NTtcbiAgcmVhZG9ubHkgaXNvVmFsdWVTdGVwID0gMTtcblxuICBkaXNwbGF5SGVscERpYWxvZyA9IGZhbHNlO1xuXG4gIC8qKiBNb2RlbCBwaWNrZXIgZm9yIHRoZSBTZWdtZW50IGJ1dHRvbidzIGRyb3Bkb3duIG1lbnUuIFRoZSBhY3RpdmUgbW9kZWxcbiAgICogIChgc2FtTW9kZWxJZGApIGlzIG1hcmtlZCB3aXRoIGEgY2hlY2s7IHNlbGVjdGluZyBhbiBpdGVtIGVtaXRzXG4gICAqICBgc2FtTW9kZWxDaGFuZ2VgIChqaXQtdWkjOTAgUDEpLlxuICAgKlxuICAgKiAgSGVsZCBhcyBhIHN0YWJsZSBhcnJheSAocmVidWlsdCBvbmx5IHdoZW4gYHNhbU1vZGVsc2AvYHNhbU1vZGVsSWRgIGNoYW5nZSksXG4gICAqICBOT1QgYSBnZXR0ZXI6IGEgZ2V0dGVyIHJldHVybnMgYSBmcmVzaCBhcnJheSB3aXRoIG5ldyBgY29tbWFuZGAgY2xvc3VyZXMgb25cbiAgICogIGV2ZXJ5IGNoYW5nZS1kZXRlY3Rpb24gdGljaywgd2hpY2ggbWFrZXMgdGhlIGJvdW5kIGBwLW1lbnVgIG92ZXJsYXkgcmVidWlsZFxuICAgKiAgaXRzIERPTSBtaWQtaW50ZXJhY3Rpb24gYW5kIHN3YWxsb3cgdGhlIGNsaWNrIG9uIGEgbWVudSBpdGVtLlxuICAgKlxuICAgKiAgYHRvb2x0aXBgIGNhcnJpZXMgdGhlIG1vZGVsJ3MgZGVzY3JpcHRpb24gZnJvbSB7QGxpbmsgTU9ERUxfSU5GT307IHRoZVxuICAgKiAgbWVudXMnIHNoYXJlZCBpdGVtIHRlbXBsYXRlIHR1cm5zIGl0IGludG8gdGhlIGhvdmVyIGluZm8gaWNvbi4gcC1tZW51J3Mgb3duXG4gICAqICByZW5kZXJpbmcgaWdub3JlcyB0aGUgZmllbGQgKGl0IHJlYWRzIGBpdGVtLnRpdGxlYCksIHNvIGl0IGlzIGZyZWUgdG8gdXNlLiAqL1xuICBzYW1NZW51SXRlbXM6IE1lbnVJdGVtW10gPSBbXTtcbiAgLyoqXG4gICAqIE1vZGVsIG1lbnVzIGZvciB0aGUgY29udHJpYnV0ZWQgdG9vbHMsIGtleWVkIGJ5IHRvb2wgaWQuIFNhbWUgc3RhYmxlLWFycmF5XG4gICAqIHJlYXNvbmluZyBhcyB7QGxpbmsgc2FtTWVudUl0ZW1zfSDigJQgYW5kIGl0IGlzIHdoeSB0aGlzIGlzIGEgbWFwIHJlYnVpbHQgaW5cbiAgICogYG5nT25DaGFuZ2VzYCByYXRoZXIgdGhhbiBhIG1ldGhvZCBjYWxsZWQgZnJvbSB0aGUgdGVtcGxhdGUsIHdoaWNoIHdvdWxkXG4gICAqIGhhbmQgYHAtbWVudWAgYSBuZXcgYXJyYXkgZXZlcnkgdGljay5cbiAgICpcbiAgICogQSBjb250cmlidXRlZCB0b29sIHN1cHBsaWVzIGl0cyBvd24gcGVyLW1vZGVsIGRlc2NyaXB0aW9uLCBzbyB1bmxpa2UgdGhlXG4gICAqIFNBTSBtZW51IHRoZXNlIGRvIG5vdCBjb25zdWx0IHtAbGluayBNT0RFTF9JTkZPfTogdGhpcyBsaWJyYXJ5IGhhcyBub1xuICAgKiBjb3B5IGZvciBjaGVja3BvaW50cyBpdCBkb2VzIG5vdCBrbm93IGFib3V0LlxuICAgKi9cbiAgdG9vbE1lbnVJdGVtczogUmVjb3JkPHN0cmluZywgTWVudUl0ZW1bXT4gPSB7fTtcblxuICAvKiogUmVidWlsZCB0aGUgU0FNIG1vZGVsIG1lbnUgd2hlbiB0aGUgbW9kZWwgbGlzdCBvciBhY3RpdmUgc2VsZWN0aW9uXG4gICAqICBjaGFuZ2VzIChrZWVwcyB0aGUgYXJyYXkgcmVmZXJlbmNlIHN0YWJsZSBhY3Jvc3Mgb3RoZXIgQ0QgdGlja3MpLiAqL1xuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgaWYgKGNoYW5nZXNbJ3NhbU1vZGVscyddIHx8IGNoYW5nZXNbJ3NhbU1vZGVsSWQnXSkge1xuICAgICAgdGhpcy5zYW1NZW51SXRlbXMgPSB0aGlzLnNhbU1vZGVscy5tYXAoKG0pID0+ICh7XG4gICAgICAgIGxhYmVsOiBtLmxhYmVsLFxuICAgICAgICBpY29uOiBtLmlkID09PSB0aGlzLnNhbU1vZGVsSWQgPyAncGkgcGktY2hlY2snIDogJ3BpIHBpLWZ3JyxcbiAgICAgICAgdG9vbHRpcDogTU9ERUxfSU5GT1ttLmlkXSxcbiAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy5zYW1Nb2RlbENoYW5nZS5lbWl0KG0uaWQpLFxuICAgICAgfSkpO1xuICAgIH1cbiAgICBpZiAoY2hhbmdlc1snY29udHJpYnV0ZWRUb29scyddIHx8IGNoYW5nZXNbJ3Rvb2xNb2RlbElkcyddKSB7XG4gICAgICB0aGlzLnRvb2xNZW51SXRlbXMgPSB7fTtcbiAgICAgIGZvciAoY29uc3QgdG9vbCBvZiB0aGlzLmNvbnRyaWJ1dGVkVG9vbHMpIHtcbiAgICAgICAgY29uc3QgYWN0aXZlID0gdGhpcy50b29sTW9kZWxJZHNbdG9vbC5pZF0gPz8gdG9vbC5kZWZhdWx0TW9kZWxJZCgpO1xuICAgICAgICB0aGlzLnRvb2xNZW51SXRlbXNbdG9vbC5pZF0gPSB0b29sLm1vZGVscygpLm1hcCgobSkgPT4gKHtcbiAgICAgICAgICBsYWJlbDogbS5sYWJlbCxcbiAgICAgICAgICBpY29uOiBtLmlkID09PSBhY3RpdmUgPyAncGkgcGktY2hlY2snIDogJ3BpIHBpLWZ3JyxcbiAgICAgICAgICB0b29sdGlwOiBtLmluZm8sXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b29sTW9kZWxDaGFuZ2UuZW1pdCh7IHRvb2xJZDogdG9vbC5pZCwgbW9kZWxJZDogbS5pZCB9KSxcbiAgICAgICAgfSkpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTdHJpcCBtYXJrdXAgZnJvbSBhIG1vZGVsIGRlc2NyaXB0aW9uIHNvIGl0IGNhbiBiZSByZWFkIGFsb3VkLlxuICAgKlxuICAgKiBUaGUgY29weSBpbiBNT0RFTF9JTkZPIGFuZCBpbiBhIGNvbnRyaWJ1dGlvbidzIGBUb29sTW9kZWxPcHRpb24uaW5mb2AgaXNcbiAgICogd3JpdHRlbiBmb3IgYSB2aXN1YWwgdG9vbHRpcCByZW5kZXJlZCB3aXRoIGBbZXNjYXBlXT1cImZhbHNlXCJgLCBzbyBpdCBjYXJyaWVzXG4gICAqIGA8Yj5gIGFuZCBgPGJyPmAuIFBhc3NlZCB0byBgYXJpYS1sYWJlbGAgdmVyYmF0aW0gYSBzY3JlZW4gcmVhZGVyIGFubm91bmNlc1xuICAgKiB0aGUgdGFncywgc28gdGhleSBhcmUgcmVtb3ZlZCBhbmQgYDxicj5gIGJlY29tZXMgYSBzZW50ZW5jZSBicmVhay4gRW50aXRpZXNcbiAgICogdXNlZCBpbiB0aGF0IGNvcHkgKGAmbmJzcDtgLCBgJnRpbWVzO2AsIGAmYW1wO2ApIGFyZSBkZWNvZGVkIGZvciB0aGUgc2FtZVxuICAgKiByZWFzb24uXG4gICAqL1xuICBwbGFpblRleHQoaHRtbDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gKGh0bWwgPz8gJycpXG4gICAgICAucmVwbGFjZSgvPGJyXFxzKlxcLz8+L2dpLCAnLiAnKVxuICAgICAgLnJlcGxhY2UoLzxbXj5dKz4vZywgJycpXG4gICAgICAucmVwbGFjZSgvJm5ic3A7L2csICcgJylcbiAgICAgIC5yZXBsYWNlKC8mdGltZXM7L2csICd4JylcbiAgICAgIC5yZXBsYWNlKC8mYW1wOy9nLCAnJicpXG4gICAgICAucmVwbGFjZSgvXFxzKy9nLCAnICcpXG4gICAgICAudHJpbSgpO1xuICB9XG5cbiAgLyoqIENvbnRyaWJ1dGVkIHRvb2wgbmFtZXMgZm9yIHRoZSBoZWxwIGRpYWxvZydzIHByb21wdGVkL25vLXByb21wdCBjb250cmFzdCxcbiAgICogIGUuZy4gXCJZT0xPLCBSZXRpbmFsIGxheWVyc1wiLiBFbXB0eSB3aGVuIG5vdGhpbmcgaXMgcmVnaXN0ZXJlZCwgd2hpY2ggaXNcbiAgICogIHdoeSB0aGUgc2VudGVuY2UgdGhhdCB1c2VzIGl0IGlzIGl0c2VsZiBjb25kaXRpb25hbC4gKi9cbiAgZ2V0IGNvbnRyaWJ1dGVkVG9vbE5hbWVzKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuY29udHJpYnV0ZWRUb29scy5tYXAoKHQpID0+IHQubGFiZWwpLmpvaW4oJywgJyk7XG4gIH1cblxuICAvKiogS2VlcHMgZWFjaCBjb250cmlidXRlZCB0b29sJ3MgYHAtbWVudWAgb3ZlcmxheSBhbGl2ZSBhY3Jvc3MgQ0QgdGlja3Mg4oCUXG4gICAqICByZS1jcmVhdGluZyBpdCBtaWQtaW50ZXJhY3Rpb24gc3dhbGxvd3MgdGhlIGNsaWNrIG9uIGEgbWVudSBpdGVtLiAqL1xuICB0cmFja1Rvb2xCeUlkKF9pbmRleDogbnVtYmVyLCB0b29sOiBUb29sYmFyVG9vbENvbnRyaWJ1dGlvbik6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRvb2wuaWQ7XG4gIH1cblxuICAvKiogVGhlIEltYWdlIHBsb3QgdHlwZSByZW5kZXJzIGFzIGEgbmF0aXZlbHkgcGFuL3pvb20tYWJsZSByYXN0ZXIsIHNvIHRoZVxuICAgKiAgZ2VuZXJpYyB6b29tL3BhbiB0b29scyBhcmUgaGlkZGVuLiAqL1xuICBnZXQgaXNJbWFnZVZpZXcoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gIH1cblxuICAvKiogQSBwbG90LXR5cGUgaWNvbiBpcyBhIFByaW1lTkcgZm9udCBnbHlwaCAoZS5nLiBgcGkgcGktaW1hZ2VgKSByYXRoZXIgdGhhbiBhblxuICAgKiAgU1ZHIGFzc2V0IHBhdGgg4oCUIGRyaXZlcyB3aGljaCBlbGVtZW50IHRoZSBzZWxlY3RvciBpdGVtIHRlbXBsYXRlIHJlbmRlcnMuICovXG4gIGlzUGlJY29uKGljb246IHN0cmluZyB8IHVuZGVmaW5lZCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAhIWljb24gJiYgaWNvbi5zdGFydHNXaXRoKCdwaSAnKTtcbiAgfVxuXG4gIC8qKiBCYWNrZW5kcyB3aXRoIGEgdmVydGV4LWVkaXRpbmcgcmVnaW9uIG92ZXJsYXk6IE9TRCBJbWFnZSBhbmQgbmFwYXJpLWpzIFdlYkdQVSBpbWFnZVxuICAgKiAgKGppdC11aSMxMDIpLiBHYXRlcyB0aGUgcG9seWdvbiAvIGFkZC12ZXJ0ZXggLyBkZWxldGUtdmVydGV4IC8gQsOpemllci1jb252ZXJ0IHRvb2xzLiAqL1xuICBnZXQgc3VwcG9ydHNSZWdpb25WZXJ0ZXhUb29scygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRSB8fFxuICAgICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlID09PSBQbG90VHlwZS5OQVBBUklfSU1BR0VcbiAgICApO1xuICB9XG5cbiAgLyoqIFBsb3QgdHlwZXMgdGhhdCBzY3J1YiBhIHotc3RhY2sgbGl2ZSAodGhlIHJlbmRlcmVyIHN3YXBzIHRoZSBzbGljZSBpbiBwbGFjZSk6IHRoZSBPU0QgSW1hZ2VcbiAgICogIHZpZXcsIHRoZSBuYXBhcmktanMgV2ViR1BVIGltYWdlLCBhbmQgdGhlIG5hcGFyaS1qcyBzdXJmYWNlIOKAlCB3aGljaCByZWJ1aWxkcyB0aGUgaGVpZ2h0IGZpZWxkXG4gICAqICBmcm9tIHRoZSBwaWNrZWQgc2xpY2UgKGppdC11aSMxMDIpLiBEcml2ZXMgdGhlIHBlci1zbGljZSBzbGlkZXIuICovXG4gIGdldCBzaG93c0xpdmVTbGljZVNjcnViYmVyKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGUgPT09IFBsb3RUeXBlLklNQUdFIHx8XG4gICAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGUgPT09IFBsb3RUeXBlLk5BUEFSSV9JTUFHRSB8fFxuICAgICAgaXNOYXBhcmlTdXJmYWNlKHRoaXMuc2VsZWN0ZWRQbG90VHlwZSkgfHxcbiAgICAgIGlzTmFwYXJpU2NhdHRlcih0aGlzLnNlbGVjdGVkUGxvdFR5cGUpXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBBbnkgbmFwYXJpLWpzIFdlYkdQVSBwbG90IHR5cGUuIFRoZSBzaW5nbGUtaW1hZ2Uvc3RhY2sgdG9nZ2xlICsgc2xpY2UtbnVtYmVyIGZpZWxkIGFyZVxuICAgKiAgUGxvdGx5LW9ubHkgc3RhY2sgY29udHJvbHMsIHNvIHRoZXkncmUgaGlkZGVuIGZvciBuYXBhcmkgKGppdC11aSMxMDIpLiAqL1xuICBnZXQgaXNOYXBhcmlNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGUgPT09IFBsb3RUeXBlLk5BUEFSSV9JTUFHRSB8fFxuICAgICAgaXNOYXBhcmlTY2F0dGVyKHRoaXMuc2VsZWN0ZWRQbG90VHlwZSkgfHxcbiAgICAgIGlzTmFwYXJpM2QodGhpcy5zZWxlY3RlZFBsb3RUeXBlKVxuICAgICk7XG4gIH1cblxuICAvKiogSVNPU1VSRkFDRSAoUGxvdGx5IG9yIG5hcGFyaS1qcyBXZWJHUFUsIGVpdGhlciByZXNvbHV0aW9uKSBzaG93cyB0aGUgaXNvdmFsdWUgcmFuZ2Ugc2xpZGVyLiAqL1xuICBnZXQgaXNJc29zdXJmYWNlTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlID09PSBQbG90VHlwZS5JU09TVVJGQUNFIHx8IGlzTmFwYXJpSXNvc3VyZmFjZSh0aGlzLnNlbGVjdGVkUGxvdFR5cGUpXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBUaGUgbmFwYXJpLWpzIFdlYkdQVSBzdXJmYWNlIOKAlCBzaG93cyB0aGUgd2lyZWZyYW1lIHRvZ2dsZS4gKi9cbiAgZ2V0IGlzTmFwYXJpU3VyZmFjZU1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGlzTmFwYXJpU3VyZmFjZSh0aGlzLnNlbGVjdGVkUGxvdFR5cGUpO1xuICB9XG5cbiAgLyoqIEFueSBuYXBhcmktanMgV2ViR1BVIDNEIHR5cGUgKHZvbHVtZS9pc29zdXJmYWNlL3N1cmZhY2UpIOKAlCBzaG93cyB0aGUgUmVzb2x1dGlvbiBjb250cm9sLiAqL1xuICBnZXQgaXNOYXBhcmkzZE1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGlzTmFwYXJpM2QodGhpcy5zZWxlY3RlZFBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBJbnRlbnNpdHkgcHJvZmlsZSBsaW5lcyBhcmUgUmVnaW9uLWJhc2VkIGFuZCBhdmFpbGFibGUgaW4gdGhlIEhlYXRtYXAgYW5kXG4gICAqICBJbWFnZSBwbG90IHR5cGVzLCB3aGljaCBzaG93IHRoZSBJbnRlbnNpdHkgdG9vbCBncm91cC4gKi9cbiAgZ2V0IGlzSW50ZW5zaXR5Q2FwYWJsZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zZWxlY3RlZFBsb3RUeXBlID09PSBQbG90VHlwZS5IRUFUTUFQIHx8IHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gIH1cblxuICBzaG93SGVscCgpOiB2b2lkIHtcbiAgICB0aGlzLmRpc3BsYXlIZWxwRGlhbG9nID0gdHJ1ZTtcbiAgfVxufVxuIiwiPHAtdG9vbGJhcj5cbiAgPGRpdiBjbGFzcz1cInAtdG9vbGJhci1ncm91cC1zdGFydCBmbGV4LXdyYXAgYWxpZ24taXRlbXMtY2VudGVyIGdhcC0xXCI+XG5cbiAgICA8IS0tIEhvc3QtcHJvdmlkZWQgY3VzdG9tIHRvb2xiYXIgYnV0dG9ucywgc2hvd24gZmlyc3QgKGUuZy4gYXBwLXNwZWNpZmljXG4gICAgICAgICBhY3Rpb25zIHRoZSB2aXN1YWxpemF0aW9uIHZpZXcgaXRzZWxmIGRvZXNuJ3Qgb3duKS4gLS0+XG4gICAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuXG4gICAgPCEtLSBQbG90IHR5cGU6IGF2YWlsYWJsZSBmb3IgYW55IGltYWdlIChncmF5c2NhbGUgb3IgUkdCKS4gLS0+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImltYWdlSW5mbyAmJiB0b29scy5zcGVjaWFsVG9vbHNcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8cC1kcm9wZG93biBbb3B0aW9uc109XCJwbG90VHlwZU9wdGlvbnNcIiBvcHRpb25MYWJlbD1cImxhYmVsXCIgb3B0aW9uVmFsdWU9XCJ0eXBlXCJcbiAgICAgICAgICAgICAgICAgIFsobmdNb2RlbCldPVwic2VsZWN0ZWRQbG90VHlwZVwiIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNlbGVjdCBwbG90IHR5cGVcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgW3N0eWxlXT1cInsnbWluLXdpZHRoJzogJzExcmVtJ31cIlxuICAgICAgICAgICAgICAgICAgc3R5bGVDbGFzcz1cInBsb3Rtb2RlLWRyb3Bkb3duXCIgcGFuZWxTdHlsZUNsYXNzPVwicGxvdG1vZGUtcGFuZWxcIlxuICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cInNlbGVjdFBsb3RUeXBlLmVtaXQoJGV2ZW50LnZhbHVlKVwiPlxuICAgICAgICA8bmctdGVtcGxhdGUgbGV0LW9wdCBwVGVtcGxhdGU9XCJzZWxlY3RlZEl0ZW1cIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInBsb3Rtb2RlLWl0ZW1cIiAqbmdJZj1cIm9wdFwiPlxuICAgICAgICAgICAgPGkgKm5nSWY9XCJpc1BpSWNvbihvcHQuaWNvbilcIiBbY2xhc3NdPVwib3B0Lmljb25cIj48L2k+XG4gICAgICAgICAgICA8aW1nICpuZ0lmPVwib3B0Lmljb24gJiYgIWlzUGlJY29uKG9wdC5pY29uKVwiIGNsYXNzPVwicGxvdG1vZGUtaWNvbi1pbWdcIiBbc3JjXT1cIm9wdC5pY29uXCIgYWx0PVwiXCIgLz5cbiAgICAgICAgICAgIDxzcGFuPnt7IG9wdC5sYWJlbCB9fTwvc3Bhbj5cbiAgICAgICAgICA8L3NwYW4+XG4gICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgIDxuZy10ZW1wbGF0ZSBsZXQtb3B0IHBUZW1wbGF0ZT1cIml0ZW1cIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInBsb3Rtb2RlLWl0ZW1cIj5cbiAgICAgICAgICAgIDxpICpuZ0lmPVwiaXNQaUljb24ob3B0Lmljb24pXCIgW2NsYXNzXT1cIm9wdC5pY29uXCI+PC9pPlxuICAgICAgICAgICAgPGltZyAqbmdJZj1cIm9wdC5pY29uICYmICFpc1BpSWNvbihvcHQuaWNvbilcIiBjbGFzcz1cInBsb3Rtb2RlLWljb24taW1nXCIgW3NyY109XCJvcHQuaWNvblwiIGFsdD1cIlwiIC8+XG4gICAgICAgICAgICA8c3Bhbj57eyBvcHQubGFiZWwgfX08L3NwYW4+XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgPC9wLWRyb3Bkb3duPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImlzSXNvc3VyZmFjZU1vZGVcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPklzbzwvc3Bhbj5cbiAgICAgICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwiaXNvUmFuZ2VcIiBbcmFuZ2VdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBbbWluXT1cImlzb1ZhbHVlTWluXCIgW21heF09XCJpc29WYWx1ZU1heFwiIFtzdGVwXT1cImlzb1ZhbHVlU3RlcFwiXG4gICAgICAgICAgICAgICAgICAob25DaGFuZ2UpPVwiaXNvUmFuZ2VDaGFuZ2UuZW1pdCgkZXZlbnQudmFsdWVzKVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIklzb3N1cmZhY2UgYmFuZCAoMOKAkzI1NSksIG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUnc1xuICAgICAgICAgICAgICAgICAgYWN0dWFsIGludGVuc2l0eSByYW5nZS4gU3VyZmFjZXMgYXJlIGRyYXduIGZvciB2b3hlbHMgd2hvc2VcbiAgICAgICAgICAgICAgICAgIGludGVuc2l0eSBmYWxscyBpbiB0aGUgc2VsZWN0ZWQgYmFuZCDigJQgbmFycm93IGl0IHRvIGlzb2xhdGVcbiAgICAgICAgICAgICAgICAgIGZlYXR1cmVzLCB3aWRlbiBpdCB0byBzZWUgdGhlIHdob2xlIHN0cnVjdHVyZS5cIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICBzdHlsZUNsYXNzPVwid2FuZC1zZW5zaXRpdml0eS1zbGlkZXJcIj48L3Atc2xpZGVyPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0IHdhbmQtc2Vuc2l0aXZpdHktdmFsdWVcIj5cbiAgICAgICAgICB7eyBpc29SYW5nZVswXSB9feKAk3t7IGlzb1JhbmdlWzFdIH19PC9zcGFuPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICA8IS0tIEludGVuc2l0eSB0b29scyAoSGVhdG1hcCAvIEltYWdlIG1vZGUpOiBhZGQgYW5vdGhlciBjb2xvcmVkIGxpbmUgUk9JLlxuICAgICAgICAgICBFYWNoIGFkZGVkIGxpbmUgZ2V0cyB0aGUgbmV4dCBicmlnaHQgcGFsZXR0ZSBjb2xvdXIgYW5kIGl0cyBvd24gaW5zZXRcbiAgICAgICAgICAgdHJhY2U7IHNlbGVjdCBhIGxpbmUgb24gdGhlIGltYWdlIGFuZCB1c2UgdGhlIHRyYXNoIHRvb2wgdG8gcmVtb3ZlIGl0XG4gICAgICAgICAgIChhbmQgaXRzIHRyYWNlKS4gVGhlIGxpbmVzIGFyZSBSZWdpb24tYmFzZWQgYnV0IGV4Y2x1ZGVkIGZyb20gdGhlXG4gICAgICAgICAgIFJlZ2lvbiBFZGl0b3IgYW5kIGV4cG9ydHMuIC0tPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImlzSW50ZW5zaXR5Q2FwYWJsZVwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkFkZCBhbiBpbnRlbnNpdHkgbGluZSAobmV3IGNvbG91cikuIENsaWNrIGEgbGluZSB0byBzZWxlY3QgaXQsXG4gICAgICAgICAgICAgICAgICB0aGVuIHVzZSB0aGUgdHJhc2ggdG9vbCB0byByZW1vdmUgaXQuXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiYWRkUHJvZmlsZUxpbmUuZW1pdCgpXCI+XG4gICAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc3BsaW5lLWNoYXJ0LnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9uZy1jb250YWluZXI+XG4gICAgPCEtLSBDaGFubmVscyAmIEhpc3RvZ3JhbTogYnJpZ2h0bmVzcy9jb250cmFzdCwgZ2FtbWEsIHBlci1jaGFubmVsIGRpc3BsYXksXG4gICAgICAgICBjb2xvcm1hcC9yZXZlcnNlL2ludmVydCDigJQgb3BlbnMgdGhlIGZsb2F0aW5nIGRpYWxvZy4gLS0+XG4gICAgPHAtYnV0dG9uICpuZ0lmPVwidG9vbHMuc3BlY2lhbFRvb2xzXCIgcFJpcHBsZSBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJDaGFubmVscyAmYW1wOyBIaXN0b2dyYW0gKGJyaWdodG5lc3MvY29udHJhc3QsIGNvbG9ybWFwKVwiXG4gICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChvbkNsaWNrKT1cIm9wZW5DaGFubmVsSGlzdG9ncmFtLmVtaXQoKVwiPlxuICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvaW52ZXJ0LnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgIDwvcC1idXR0b24+XG5cbiAgICA8IS0tIFNtb290aGVuIHRvZ2dsZTogYmlsaW5lYXIgc21vb3RoaW5nIHZzIG5lYXJlc3QtbmVpZ2hib3VyIHJhdyBwaXhlbHMgKEltYWdlXG4gICAgICAgICB2aWV3IG9ubHkg4oCUIGl0J3MgYW4gT3BlblNlYWRyYWdvbiBkcmF3ZXIgc2V0dGluZykuIEhpZ2hsaWdodGVkIHdoZW4gc21vb3RoaW5nIG9uLiAtLT5cbiAgICA8cC1idXR0b24gKm5nSWY9XCJ0b29scy5zcGVjaWFsVG9vbHMgJiYgaXNJbWFnZVZpZXdcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICBbdGV4dF09XCIhaW1hZ2VTbW9vdGhpbmdFbmFibGVkXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTbW9vdGhlbiAodHVybiBvbiBpbWFnZSBzbW9vdGhpbmcpXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlSW1hZ2VTbW9vdGhpbmcuZW1pdCgpXCI+XG4gICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zbW9vdGguc3ZnXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiXG4gICAgICAgICAgIFtjbGFzc109XCJpbWFnZVNtb290aGluZ0VuYWJsZWQgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCIvPlxuICAgIDwvcC1idXR0b24+XG5cbiAgICA8IS0tIFN0YWNrIGNvbnRyb2xzIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpbWFnZUluZm8/LmlzU3RhY2tcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8IS0tIEltYWdlIHZpZXc6IHNjcnViIHotc2xpY2VzIGxpdmUgKHRoZSByZW5kZXJlciBzd2FwcyBzbGljZXMgaW4gcGxhY2UpLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJzaG93c0xpdmVTbGljZVNjcnViYmVyXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5TbGljZTwvc3Bhbj5cbiAgICAgICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwiekluZGV4XCIgW21pbl09XCIwXCIgW21heF09XCJtYXhJbmRleFwiXG4gICAgICAgICAgICAgICAgICAob25DaGFuZ2UpPVwielNjcnViLmVtaXQoJGV2ZW50LnZhbHVlKVwiIChvblNsaWRlRW5kKT1cInpTbGlkZS5lbWl0KCRldmVudC52YWx1ZSlcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTbGljZSB0aHJvdWdoIHRoZSBzdGFjayAo4oaQIC8g4oaSIGtleXMpXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIFtzdHlsZV09XCJ7J3dpZHRoJzonMTZyZW0nfVwiIHN0eWxlQ2xhc3M9XCJ3YW5kLXNlbnNpdGl2aXR5LXNsaWRlclwiPjwvcC1zbGlkZXI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj57e3pJbmRleH19IG9mIHt7bWF4SW5kZXh9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPCEtLSBIZWF0bWFwIHZpZXc6IHNpbmdsZS1pbWFnZSAoaGlnaC1kZWYgem9vbSkgdnMgc3RhY2sgKGZyYW1lIHNsaWRlcikuIFBsb3RseS1vbmx5IOKAlFxuICAgICAgICAgICBoaWRkZW4gZm9yIG5hcGFyaSwgd2hpY2ggaGFzIG5vIHNpbmdsZS9zdGFjayB0b2dnbGUgb3IgcGVyLWZyYW1lIGZldGNoLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIhc2hvd3NMaXZlU2xpY2VTY3J1YmJlciAmJiAhaXNOYXBhcmlNb2RlXCI+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWltYWdlXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJpbWFnZUluZm8/LnNob3dTdGFja1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTaW5nbGUgaW1hZ2UgbW9kZTogZW5hYmxlcyBoaWdoLWRlZiB6b29tXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJzZWxlY3RTdGFja09wdGlvbi5lbWl0KHN0YWNrT3B0aW9uc1swXSlcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWltYWdlc1wiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiIWltYWdlSW5mbz8uc2hvd1N0YWNrXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlN0YWNrIG1vZGUsIGVuYWJsZXMgc2xpY2luZyB0aHJvdWdoIGltYWdlIGZyYW1lc1wiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2VsZWN0U3RhY2tPcHRpb24uZW1pdChzdGFja09wdGlvbnNbMV0pXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpbWFnZUluZm8/LnNob3dTdGFja1wiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+U2xpY2U8L3NwYW4+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJwLWlucHV0Z3JvdXBcIiBzdHlsZT1cIndpZHRoOiBhdXRvXCI+XG4gICAgICAgICAgPHAtaW5wdXROdW1iZXIgc3R5bGVDbGFzcz1cInctM3JlbSB0b29sYmFyLWlucHV0XCJcblx0XHRcdFx0XHRwVG9vbHRpcD1cIlZpZXcgaW1hZ2UgaW4gc3RhY2sgKCYjeDIzQ0U7KVwiXG5cdFx0XHRcdFx0WyhuZ01vZGVsKV09XCJ6SW5kZXhcIiBbbWluXT1cIjBcIiBbbWF4XT1cIm1heEluZGV4XCJcbiAgICAgICAgICAgKG9uSW5wdXQpPVwiekluZGV4SW5wdXQuZW1pdCh6SW5kZXgpXCIgKGtleWRvd24uZW50ZXIpPVwicmVsb2FkQW5kUGxvdC5lbWl0KClcIi8+XG4gICAgICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1yZWZyZXNoXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiIChvbkNsaWNrKT1cInJlbG9hZEFuZFBsb3QuZW1pdCgpXCJcbiAgICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJQbG90IHRoaXMgc2xpY2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIj5cbiAgICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPm9mIHt7bWF4SW5kZXh9fTwvc3Bhbj5cbiAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDwhLS0gSW1hZ2UgYWN0aW9ucyAoc3BlY2lhbCB0b29scykgLS0+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cInRvb2xzLnNwZWNpYWxUb29sc1wiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktZG93bmxvYWRcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkRvd25sb2FkIGltYWdlIGFzIFBOR1wiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiZG93bmxvYWRJbWFnZS5lbWl0KClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gKm5nSWY9XCJpc0hlYXRtYXBcIiBpY29uPVwicGkgcGktd2luZG93LW1heGltaXplXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJGaXQgdG8gdmlld1wiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiYXV0b3NjYWxlSW1hZ2UuZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgPCEtLSBab29tIHRvb2xzLiBQbG90bHkgcnViYmVyLWJhbmQgZHJhZy16b29tOiBvbiBPcGVuU2VhZHJhZ29uIHRoZSBib3gtem9vbVxuICAgICAgICAgY292ZXJzIHRoaXMgYW5kIGEgYmFyZSBkcmFnIHBhbnMgbmF0aXZlbHksIHNvIGl0J3MgaGlkZGVuIGluIGltYWdlIHZpZXcuIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc0hlYXRtYXAgJiYgdG9vbHMuem9vbVRvb2xzXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgPHAtYnV0dG9uICpuZ0lmPVwiIWlzSW1hZ2VWaWV3XCIgaWNvbj1cInBpIHBpLXNlYXJjaFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICd6b29tJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJab29tIGRyYWdcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ3pvb20nKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnem9vbVRvQm94J1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJab29tIGJveCAoPGtiZD5iPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ3pvb21Ub0JveCcpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3pvb20tYm94LnN2Z1wiXG4gICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnem9vbVRvQm94JyA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1hcnJvd3MtYWx0XCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ3BhbidcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUGFuIHRoZSBpbWFnZSAoPGtiZD5wPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ3BhbicpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1zZWFyY2gtcGx1c1wiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiWm9vbSBpbiAoPGtiZD4rPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInpvb21Jbi5lbWl0KClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXNlYXJjaC1taW51c1wiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiWm9vbSBvdXQgKDxrYmQ+LTwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ6b29tT3V0LmVtaXQoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDwhLS0gU3VyZmFjZSAzRCBjb250cm9scyAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiIWlzSGVhdG1hcFwiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktc2VhcmNoXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVTdXJmYWNlM2RNb2RlICE9PSAnem9vbSdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiWm9vbVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlU3VyZmFjZTNkTW9kZS5lbWl0KCd6b29tJylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWFycm93cy1hbHRcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZVN1cmZhY2UzZE1vZGUgIT09ICdwYW4nXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlBhblwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlU3VyZmFjZTNkTW9kZS5lbWl0KCdwYW4nKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktZ2xvYmVcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZVN1cmZhY2UzZE1vZGUgIT09ICdvcmJpdCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiT3JiaXRhbCByb3RhdGlvblwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlU3VyZmFjZTNkTW9kZS5lbWl0KCdvcmJpdCcpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1zeW5jXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVTdXJmYWNlM2RNb2RlICE9PSAndHVybnRhYmxlJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJUdXJudGFibGUgcm90YXRpb25cIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZVN1cmZhY2UzZE1vZGUuZW1pdCgndHVybnRhYmxlJylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWhvbWVcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlJlc2V0IGNhbWVyYSB0byBkZWZhdWx0XCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJyZXNldFN1cmZhY2VDYW1lcmEuZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPCEtLSAzRCBjb29yZGluYXRlLWF4ZXMgLyBzY2FsZSBnaXptbyB0b2dnbGUgKG5hcGFyaS1qcyBXZWJHUFUgdm9sdW1lL2lzb3N1cmZhY2Ugb25seSkuXG4gICAgICAgICAgIGBwLWJ1dHRvbi1pY29uLW9ubHlgIGdpdmVzIHRoZSBzYW1lIHNxdWFyZSBzaXppbmcgYXMgdGhlIG5laWdoYm91cmluZyBwaS1pY29uIGJ1dHRvbnNcbiAgICAgICAgICAgKHRoZSBwcm9qZWN0ZWQgPGltZz4gZG9lc24ndCB0cmlnZ2VyIFByaW1lTkcncyBhdXRvbWF0aWMgaWNvbi1vbmx5IHNpemluZykuIC0tPlxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiICpuZ0lmPVwiaXNOYXBhcmlNb2RlXCIgc3R5bGVDbGFzcz1cInAtYnV0dG9uLWljb24tb25seVwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiIWF4ZXNWaXNpYmxlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlRvZ2dsZSAzRCBheGVzIC8gc2NhbGVcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZUF4ZXMuZW1pdCgpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nLzNkLWNvb3JkaW5hdGVzLnN2Z1wiXG4gICAgICAgICAgICAgW2NsYXNzXT1cImF4ZXNWaXNpYmxlID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8IS0tIFN1cmZhY2Ugd2lyZWZyYW1lIHRvZ2dsZSAobmFwYXJpLWpzIFdlYkdQVSBzdXJmYWNlIG9ubHkpLiAtLT5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktc2hhcmUtYWx0XCIgc2l6ZT1cInNtYWxsXCIgKm5nSWY9XCJpc05hcGFyaVN1cmZhY2VNb2RlXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCIhd2lyZWZyYW1lQWN0aXZlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlRvZ2dsZSB3aXJlZnJhbWVcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZVdpcmVmcmFtZS5lbWl0KClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8IS0tIFJlc29sdXRpb24gKGRlY2ltYXRlIGZhY3RvcikgZm9yIHRoZSBuYXBhcmkgM0QgdHlwZXMg4oCUIHJlbG9hZHMgYXQgY29hcnNlci9maW5lciBzYW1wbGluZy4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNOYXBhcmkzZE1vZGVcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPlJlczwvc3Bhbj5cbiAgICAgICAgPHAtZHJvcGRvd24gW29wdGlvbnNdPVwiZGVjaW1hdGVPcHRpb25zXCIgb3B0aW9uTGFiZWw9XCJsYWJlbFwiIG9wdGlvblZhbHVlPVwidmFsdWVcIlxuICAgICAgICAgICAgICAgICAgICBbbmdNb2RlbF09XCJyZXNvbHV0aW9uU2NhbGVcIiBhcHBlbmRUbz1cImJvZHlcIlxuICAgICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlJlc29sdXRpb24gKGRlY2ltYXRlIGZhY3RvcilcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgICBbc3R5bGVdPVwieydtaW4td2lkdGgnOiAnNnJlbSd9XCJcbiAgICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cInNlbGVjdFJlc29sdXRpb24uZW1pdCgkZXZlbnQudmFsdWUpXCI+XG4gICAgICAgIDwvcC1kcm9wZG93bj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImlzSGVhdG1hcCAmJiB0b29scy5yZWdpb25Ub29sc1wiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDwhLS0gUmVnaW9uIEVkaXRvcjogb3BlbnMgdGhlIGVkaXRvciBhcyBhIGZsb2F0aW5nIGRpYWxvZyAoZm9ybWVybHkgdGhlXG4gICAgICAgICAgIHJpZ2h0LXBhbmVsIFJlZ2lvbnMgdGFiKS4gU2l0cyB3aXRoIHRoZSByZWdpb24gZHJhd2luZyB0b29scy4gLS0+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJPcGVuIHRoZSBSZWdpb24gRWRpdG9yXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJvcGVuUmVnaW9uRWRpdG9yLmVtaXQoKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9yZWdpb24tZWRpdG9yLnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwhLS0gUmVnaW9uIGRyYXdpbmcgdG9vbHMgLS0+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWFycm93LXVwLXJpZ2h0XCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ3NlbGVjdCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU2VsZWN0aW9uIG1vZGUgKDxrYmQ+czwva2JkPilcIlxuICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ3NlbGVjdCcpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1zdG9wXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2RyYXdyZWN0J1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEcmF3IGEgcmVjdGFuZ3VsYXIgcmVnaW9uICg8a2JkPnI8L2tiZD4pXCIgW2VzY2FwZV09XCJmYWxzZVwiXG4gICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdkcmF3cmVjdCcpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdkcmF3b3BlbnBhdGgnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkRyYXcgYSBwb2x5bGluZSAoPGtiZD5sPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnZHJhd29wZW5wYXRoJylcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcG9seWxpbmUuc3ZnXCJcbiAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdkcmF3b3BlbnBhdGgnID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXBlbmNpbFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdkcmF3Y2xvc2VkcGF0aCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRHJhdyBhIGZyZWVmb3JtIHJlZ2lvbiAoPGtiZD5mPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnZHJhd2Nsb3NlZHBhdGgnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwhLS0gQnJ1c2g6IHBhaW50IGEgcmVnaW9uIGJ5IGRyYWdnaW5nIGEgZGlzYyBvZiB0aGUgZ2l2ZW4gc2l6ZTsgdGhlIFNpemVcbiAgICAgICAgICAgc2xpZGVyIGFwcGVhcnMgd2hpbGUgaXQncyBhY3RpdmUuIEhvbGQgU2hpZnQgdG8gZXJhc2UgKHN1YnRyYWN0KS4gLS0+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2JydXNoJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJCcnVzaDogY2xpY2sgYW5kIGRyYWcgdG8gcGFpbnQgYSByZWdpb24uXG4gICAgICAgICAgICAgICAgPGJyPkhvbGQgPGtiZD5TaGlmdDwva2JkPiB3aGlsZSBwYWludGluZyB0byBlcmFzZSBmcm9tIGEgcmVnaW9uLlwiXG4gICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnYnJ1c2gnKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9wYWludGJydXNoLnN2Z1wiXG4gICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnYnJ1c2gnID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdicnVzaCdcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPlNpemU8L3NwYW4+XG4gICAgICAgIDxwLXNsaWRlciBbKG5nTW9kZWwpXT1cImJydXNoU2l6ZVwiXG4gICAgICAgICAgICAgICAgICBbbWluXT1cImJydXNoU2l6ZU1pblwiIFttYXhdPVwiYnJ1c2hTaXplTWF4XCIgW3N0ZXBdPVwiYnJ1c2hTaXplU3RlcFwiXG4gICAgICAgICAgICAgICAgICAob25DaGFuZ2UpPVwiYnJ1c2hTaXplQ2hhbmdlLmVtaXQoJGV2ZW50LnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkJydXNoIGRpYW1ldGVyIGluIGltYWdlIHBpeGVscy5cIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICBzdHlsZUNsYXNzPVwid2FuZC1zZW5zaXRpdml0eS1zbGlkZXJcIj48L3Atc2xpZGVyPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0IHdhbmQtc2Vuc2l0aXZpdHktdmFsdWVcIj5cbiAgICAgICAgICB7eyBicnVzaFNpemUgfX08L3NwYW4+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgPCEtLSBWZXJ0ZXggZWRpdGluZyDigJQgYmFja2VuZHMgd2l0aCBhIHZlcnRleC1lZGl0aW5nIG92ZXJsYXkgKE9TRCArIG5hcGFyaS1qcyBpbWFnZSkuIC0tPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cInN1cHBvcnRzUmVnaW9uVmVydGV4VG9vbHNcIj5cbiAgICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2RyYXdwb2x5Z29uJ1wiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlBvbHlnb246IGNsaWNrIHRvIHBsYWNlIGVhY2ggdmVydGV4OyBjbGljayB0aGUgZmlyc3QgcG9pbnQgdG8gY2xvc2VcIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdkcmF3cG9seWdvbicpXCI+XG4gICAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcG9seWdvbi12ZXJ0aWNlcy5zdmdcIlxuICAgICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnZHJhd3BvbHlnb24nID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnYWRkcG9pbnQnXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiQWRkIGEgdmVydGV4OiBjbGljayBhbiBlZGdlIG9mIHRoZSBzZWxlY3RlZCBwb2x5Z29uXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnYWRkcG9pbnQnKVwiPlxuICAgICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3ZlcnRleC1hZGQuc3ZnXCJcbiAgICAgICAgICAgICAgIFtjbGFzc109XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ2FkZHBvaW50JyA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2RlbGV0ZXBvaW50J1wiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkRlbGV0ZSBhIHZlcnRleDogY2xpY2sgYSB2ZXJ0ZXggb2YgdGhlIHNlbGVjdGVkIHBvbHlnb25cIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdkZWxldGVwb2ludCcpXCI+XG4gICAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvdmVydGV4LWRlbGV0ZS5zdmdcIlxuICAgICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnZGVsZXRlcG9pbnQnID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgPC9uZy1jb250YWluZXI+XG5cbiAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnd2FuZCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiV2FuZDogY2xpY2sgYW5kIGRyYWcgdG8gZ3JvdyBhIHJlZ2lvbiBieSBzaW1pbGFyIHBpeGVsIHZhbHVlcyAoPGtiZD53PC9rYmQ+KS5cbiAgICAgICAgICAgICAgICA8YnI+SG9sZCA8a2JkPlNoaWZ0PC9rYmQ+IHdoaWxlIGNsaWNraW5nIG9yIGRyYWdnaW5nIG9uIGEgcmVnaW9uIHRvIGVyYXNlIHBpeGVscyBmcm9tIGl0LlwiXG4gICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnd2FuZCcpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3dhbmQuc3ZnXCJcbiAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICd3YW5kJyA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImFjdGl2ZURyYWdNb2RlID09PSAnd2FuZCdcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPlNlbnMuPC9zcGFuPlxuICAgICAgICA8cC1zbGlkZXIgWyhuZ01vZGVsKV09XCJ3YW5kU2Vuc2l0aXZpdHlcIlxuICAgICAgICAgICAgICAgICAgW21pbl09XCJ3YW5kU2Vuc2l0aXZpdHlNaW5cIiBbbWF4XT1cIndhbmRTZW5zaXRpdml0eU1heFwiIFtzdGVwXT1cIndhbmRTZW5zaXRpdml0eVN0ZXBcIlxuICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cIndhbmRTZW5zaXRpdml0eUNoYW5nZS5lbWl0KCRldmVudC52YWx1ZSlcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJIaWdoZXIgPSBzdHJpY3RlciAoc21hbGxlciBzZWxlY3Rpb24pOyBsb3dlciA9IGxvb3Nlci5cbiAgICAgICAgICAgICAgICAgIFRha2VzIGVmZmVjdCBmcm9tIHRoZSBuZXh0IGNsaWNrL2RyYWcgdGljay5cIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICBzdHlsZUNsYXNzPVwid2FuZC1zZW5zaXRpdml0eS1zbGlkZXJcIj48L3Atc2xpZGVyPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0IHdhbmQtc2Vuc2l0aXZpdHktdmFsdWVcIj5cbiAgICAgICAgICB7eyB3YW5kU2Vuc2l0aXZpdHkudG9GaXhlZCgxKSB9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1lcmFzZXJcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnZXJhc2VWZXJ0ZXgnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlZlcnRleCBlcmFzZXIgKDxrYmQ+ZTwva2JkPik6IGNsaWNrIG9yIGRyYWcgdG8gcmVtb3ZlIHBvbHlnb24gdmVydGljZXMgaW5zaWRlXG4gICAgICAgICAgICAgICAgdGhlIGN1cnNvciBjaXJjbGUgZnJvbSBhbnkgcmVnaW9uLlwiXG4gICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnZXJhc2VWZXJ0ZXgnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ2VyYXNlVmVydGV4J1wiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+UmFkaXVzPC9zcGFuPlxuICAgICAgICA8cC1zbGlkZXIgWyhuZ01vZGVsKV09XCJ2ZXJ0ZXhFcmFzZXJSYWRpdXNcIlxuICAgICAgICAgICAgICAgICAgW21pbl09XCJ2ZXJ0ZXhFcmFzZXJSYWRpdXNNaW5cIiBbbWF4XT1cInZlcnRleEVyYXNlclJhZGl1c01heFwiIFtzdGVwXT1cInZlcnRleEVyYXNlclJhZGl1c1N0ZXBcIlxuICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cInZlcnRleEVyYXNlclJhZGl1c0NoYW5nZS5lbWl0KCRldmVudC52YWx1ZSlcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJFcmFzZXIgcmFkaXVzIGluIGltYWdlIHBpeGVscy5cbiAgICAgICAgICAgICAgICAgIFZlcnRpY2VzIHdpdGhpbiB0aGlzIGRpc3RhbmNlIG9mIHRoZSBjdXJzb3IgYXJlIHJlbW92ZWQuXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgc3R5bGVDbGFzcz1cIndhbmQtc2Vuc2l0aXZpdHktc2xpZGVyXCI+PC9wLXNsaWRlcj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dCB3YW5kLXNlbnNpdGl2aXR5LXZhbHVlXCI+XG4gICAgICAgICAge3sgdmVydGV4RXJhc2VyUmFkaXVzIH19PC9zcGFuPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cIiFjYW5VbmRvXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlVuZG8gdGhlIGxhc3QgcmVnaW9uIGFjdGlvbiAoPGtiZD5DdHJsPC9rYmQ+KzxrYmQ+Wjwva2JkPikuXG4gICAgICAgICAgICAgICAgVXAgdG8gMTAgYWN0aW9ucyBjYW4gYmUgdW5kb25lLlwiXG4gICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidW5kb1JlZ2lvbi5lbWl0KClcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvdW5kby5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cIiFjYW5SZWRvXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlJlZG8gdGhlIGxhc3QgdW5kb25lIHJlZ2lvbiBhY3Rpb25cbiAgICAgICAgICAgICAgICAoPGtiZD5DdHJsPC9rYmQ+KzxrYmQ+U2hpZnQ8L2tiZD4rPGtiZD5aPC9rYmQ+IC8gPGtiZD5DdHJsPC9rYmQ+KzxrYmQ+WTwva2JkPikuXCJcbiAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJyZWRvUmVnaW9uLmVtaXQoKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9yZWRvLnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktdHJhc2hcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkRlbGV0ZSBzZWxlY3RlZCByZWdpb24gKDxrYmQ+RGVsPC9rYmQ+KVwiXG4gICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiZGVsZXRlUmVnaW9uLmVtaXQoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwhLS0gU0FNIChqaXQtdWkjOTApOiBib3gtcHJvbXB0IHNlZ21lbnQsIGludGVyYWN0aXZlIHBvaW50IHByb21wdHMsIGFuZCBhXG4gICAgICAgICAgIG1vZGVsIHBpY2tlci4gLS0+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgPCEtLSBTQU0gY29udHJvbHMgZ3JvdXBlZDogYm94LXByb21wdCBTZWdtZW50LCB0aGVuIHRoZSBwb2ludC1wcm9tcHQgdG9vbCxcbiAgICAgICAgICAgdGhlbiB0aGUgbW9kZWwtcGlja2VyIGRyb3Bkb3duICh0aGUgY2hldnJvbikgd2hpY2ggZ292ZXJucyBCT1RIIHRvb2xzLlxuICAgICAgICAgICBGYWxscyBiYWNrIHRvIFNlZ21lbnQgKyBwb2ludCB3aXRoIG5vIG1vZGVsIHBpY2tlciB3aGVuIG5vbmUgYXJlIHNldC4gLS0+XG4gICAgICA8c3BhbiAqbmdJZj1cInNhbU1vZGVscy5sZW5ndGg7IGVsc2Ugc2VnbWVudFBsYWluXCIgY2xhc3M9XCJwLWJ1dHRvbnNldCBzZWdtZW50LXNwbGl0XCI+XG4gICAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU2VnbWVudCB0aGUgZHJhd24gcmVjdGFuZ2xlcyB3aXRoIFNBTSAoYm94IHByb21wdClcIlxuICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNlZ21lbnRSZWdpb25zLmVtaXQoKVwiPlxuICAgICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NhbS1ib3gtcHJvbXB0LnN2Z1wiXG4gICAgICAgICAgICAgICBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJzYW1Qb2ludEJ0blwiPjwvbmctY29udGFpbmVyPlxuICAgICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWNoZXZyb24tZG93blwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJQaWNrIHRoZSBTQU0gbW9kZWwgKHVzZWQgYnkgYm90aCBzZWdtZW50IHRvb2xzKVwiXG4gICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2FtTW9kZWxNZW51LnRvZ2dsZSgkZXZlbnQpXCI+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICA8L3NwYW4+XG4gICAgICA8bmctdGVtcGxhdGUgI3NlZ21lbnRQbGFpbj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJwLWJ1dHRvbnNldCBzZWdtZW50LXNwbGl0XCI+XG4gICAgICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNlZ21lbnQgdGhlIGRyYXduIHJlY3RhbmdsZXMgd2l0aCBTQU0gKGJveCBwcm9tcHQpXCJcbiAgICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2VnbWVudFJlZ2lvbnMuZW1pdCgpXCI+XG4gICAgICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zYW0tYm94LXByb21wdC5zdmdcIlxuICAgICAgICAgICAgICAgICBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgICAgIDxuZy1jb250YWluZXIgW25nVGVtcGxhdGVPdXRsZXRdPVwic2FtUG9pbnRCdG5cIj48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgPC9zcGFuPlxuICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgIDwhLS0gUG9pbnQtcHJvbXB0IHRvb2wg4oCUIHNoYXJlZCBiZXR3ZWVuIHRoZSBtb2RlbHMgLyBuby1tb2RlbHMgbGF5b3V0cyBzbyBpdFxuICAgICAgICAgICBhbHdheXMgc2l0cyBqdXN0IGxlZnQgb2YgdGhlIG1vZGVsIGRyb3Bkb3duLiAtLT5cbiAgICAgIDxuZy10ZW1wbGF0ZSAjc2FtUG9pbnRCdG4+XG4gICAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdzYW1Qb2ludCdcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTQU0gcG9pbnQgcHJvbXB0czogY2xpY2sgYSBmaWJlciB0byBzZWdtZW50IGl0IGFzIGEgbmV3IHJlZ2lvbjtcbiAgICAgICAgICAgICAgICAgIFNoaWZ0L0FsdC1jbGljayB0byByZWZpbmUgKGV4Y2x1ZGUpIHRoZSBjdXJyZW50IG9uZS48YnI+RXNjIHVuZG9lcyBpdC5cIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdzYW1Qb2ludCcpXCI+XG4gICAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc2FtLXBvaW50LXByb21wdC5zdmdcIlxuICAgICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnc2FtUG9pbnQnID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgIDxwLW1lbnUgI3NhbU1vZGVsTWVudSBzdHlsZUNsYXNzPVwibW9kZWwtbWVudVwiIFttb2RlbF09XCJzYW1NZW51SXRlbXNcIiBbcG9wdXBdPVwidHJ1ZVwiIGFwcGVuZFRvPVwiYm9keVwiPlxuICAgICAgICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiaXRlbVwiIGxldC1pdGVtPlxuICAgICAgICAgIDxuZy1jb250YWluZXIgW25nVGVtcGxhdGVPdXRsZXRdPVwibW9kZWxNZW51SXRlbVwiXG4gICAgICAgICAgICAgICAgICAgICAgICBbbmdUZW1wbGF0ZU91dGxldENvbnRleHRdPVwieyAkaW1wbGljaXQ6IGl0ZW0gfVwiPjwvbmctY29udGFpbmVyPlxuICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgPC9wLW1lbnU+XG5cbiAgICAgIDwhLS0gU2hhcmVkIGl0ZW0gYm9keSBmb3IgYWxsIHRocmVlIG1vZGVsIHBpY2tlcnMgKFNBTSAvIFlPTE8gLyByZXRpbmFsKTpcbiAgICAgICAgICAgdGhlIHN0YW5kYXJkIGNoZWNrLW9yLWJsYW5rIGljb24gYW5kIGxhYmVsLCBwbHVzIGFuIGluZm8gaWNvbiBjYXJyeWluZ1xuICAgICAgICAgICB0aGUgbW9kZWwncyBkZXNjcmlwdGlvbiBhcyBhIGhvdmVyIHRvb2x0aXAuIFRoaXMgcmVwbGFjZWQgYSBzaW5nbGVcbiAgICAgICAgICAgXCJBYm91dCB0aGUgU0FNIG1vZGVsc1wiIG92ZXJsYXkgcGFuZWwg4oCUIHBlci1tb2RlbCB0ZXh0IHNpdHMgbmV4dCB0byB0aGVcbiAgICAgICAgICAgbW9kZWwgaXQgZGVzY3JpYmVzLCBhbmQgWU9MTy9yZXRpbmFsIGdldCB0aGUgc2FtZSBhZmZvcmRhbmNlLlxuXG4gICAgICAgICAgIHAtbWVudSBwdXRzIGl0cyBjbGljayBoYW5kbGVyIG9uIHRoZSB3cmFwcGluZyBgLnAtbWVudWl0ZW0tY29udGVudGAsXG4gICAgICAgICAgIG91dHNpZGUgdGhpcyB0ZW1wbGF0ZSwgc28gYGNvbW1hbmRgIHN0aWxsIGZpcmVzIGFuZCB0aGUgd2hvbGUgcm93IHN0YXlzXG4gICAgICAgICAgIGNsaWNrYWJsZSDigJQgd2hpY2ggaXMgYWxzbyB3aHkgdGhlIGluZm8gaWNvbiBoYXMgdG8gc3RvcCBwcm9wYWdhdGlvbiwgb3JcbiAgICAgICAgICAgcmVhZGluZyBhYm91dCBhIG1vZGVsIHdvdWxkIHN3aXRjaCB0byBpdC4gLS0+XG4gICAgICA8bmctdGVtcGxhdGUgI21vZGVsTWVudUl0ZW0gbGV0LWl0ZW0+XG4gICAgICAgIDxhIGNsYXNzPVwicC1tZW51aXRlbS1saW5rIG1vZGVsLW1lbnUtaXRlbVwiPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwicC1tZW51aXRlbS1pY29uXCIgW25nQ2xhc3NdPVwiaXRlbS5pY29uXCI+PC9zcGFuPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwicC1tZW51aXRlbS10ZXh0XCI+e3sgaXRlbS5sYWJlbCB9fTwvc3Bhbj5cbiAgICAgICAgICA8IS0tIGBib3R0b21gIGxpa2UgZXZlcnkgb3RoZXIgdG9vbHRpcCBpbiB0aGlzIHRvb2xiYXIsIGFuZCBmb3Igcm9vbTpcbiAgICAgICAgICAgICAgIHRoZXNlIG1lbnVzIG9wZW4gdW5kZXIgYnV0dG9ucyB0aGF0IGNhbiBzaXQgbmVhciB0aGUgcmlnaHQgZWRnZSxcbiAgICAgICAgICAgICAgIHdoZXJlIGEgYHJpZ2h0YCB0b29sdGlwIGhhcyBhIGZldyBodW5kcmVkIHB4IGFuZCBzcXVlZXplcyBpdHMgdGV4dFxuICAgICAgICAgICAgICAgaW50byBhIG5hcnJvdyBjb2x1bW4gaW5zdGVhZCBvZiB1c2luZyBpdHMgbWF4LXdpZHRoLiAtLT5cbiAgICAgICAgICA8IS0tIEEgZm9jdXNhYmxlIDxidXR0b24+LCBub3QgYSBiYXJlIDxpPi4gQXMgYW4gPGk+IHRoZSBkZXNjcmlwdGlvblxuICAgICAgICAgICAgICAgd2FzIHJlYWNoYWJsZSBvbmx5IGJ5IGhvdmVyaW5nIGEgbW91c2U6IG5vdCB0YWJiYWJsZSwgbm9cbiAgICAgICAgICAgICAgIGFjY2Vzc2libGUgbmFtZSwgbm90aGluZyBmb3IgYSBzY3JlZW4gcmVhZGVyIHRvIGFubm91bmNlIOKAlCBzb1xuICAgICAgICAgICAgICAga2V5Ym9hcmQgYW5kIEFUIHVzZXJzIGNvdWxkIHBpY2sgYSBjaGVja3BvaW50IGJ1dCBuZXZlciByZWFkIHdoYXRcbiAgICAgICAgICAgICAgIGl0IHdhcy4gUHJpbWVORydzIHRvb2x0aXAgc2hvd3Mgb24gZm9jdXMgYXMgd2VsbCBhcyBob3Zlciwgc29cbiAgICAgICAgICAgICAgIG1ha2luZyBpdCBhIGJ1dHRvbiByZXN0b3JlcyBpdCBmb3IgZnJlZS5cblxuICAgICAgICAgICAgICAgYGFyaWEtZGVzY3JpYmVkYnlgIGlzIGRlbGliZXJhdGVseSBub3QgdXNlZDogdGhlIHRvb2x0aXAgaXNcbiAgICAgICAgICAgICAgIHJlbmRlcmVkIGludG8gYGJvZHlgIG9uIGRlbWFuZCBhbmQgaGFzIG5vIHN0YWJsZSBpZCB0byBwb2ludCBhdC5cbiAgICAgICAgICAgICAgIFRoZSBkZXNjcmlwdGlvbiBpcyBjYXJyaWVkIG9uIGBhcmlhLWxhYmVsYCBpbnN0ZWFkLCB3aGljaCBpc1xuICAgICAgICAgICAgICAgYW5ub3VuY2VkIHdoZXRoZXIgb3Igbm90IHRoZSB0b29sdGlwIGlzIHNob3dpbmcuIEl0IGlzIHN0cmlwcGVkIG9mXG4gICAgICAgICAgICAgICBtYXJrdXAgZmlyc3QsIHNpbmNlIHRoZSBjb3B5IGNvbnRhaW5zIGA8Yj5gL2A8YnI+YCBmb3IgdGhlIHZpc3VhbFxuICAgICAgICAgICAgICAgdG9vbHRpcCBhbmQgdGhvc2Ugd291bGQgb3RoZXJ3aXNlIGJlIHJlYWQgb3V0IGxpdGVyYWxseS4gLS0+XG4gICAgICAgICAgPGJ1dHRvbiAqbmdJZj1cIml0ZW0udG9vbHRpcFwiIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgICAgY2xhc3M9XCJwaSBwaS1pbmZvLWNpcmNsZSBtb2RlbC1pbmZvLWljb25cIlxuICAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCInQWJvdXQgJyArIGl0ZW0ubGFiZWwgKyAnOiAnICsgcGxhaW5UZXh0KGl0ZW0udG9vbHRpcClcIlxuICAgICAgICAgICAgICAgICAgW3BUb29sdGlwXT1cIml0ZW0udG9vbHRpcFwiIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgYXBwZW5kVG89XCJib2R5XCIgdG9vbHRpcFN0eWxlQ2xhc3M9XCJtb2RlbC1pbmZvLXRvb2x0aXBcIlxuICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIiRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiXG4gICAgICAgICAgICAgICAgICAoa2V5ZG93bi5lbnRlcik9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgICAgICAgICAgICAgKGtleWRvd24uc3BhY2UpPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCI+PC9idXR0b24+XG4gICAgICAgIDwvYT5cbiAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJBdXRvLXNlZ21lbnQgY2VsbHMgaW5zaWRlIGVhY2ggZHJhd24gcmVjdGFuZ2xlIChjZWxscG9zZS1TQU0pXCJcbiAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNlZ21lbnRDZWxscG9zZS5lbWl0KClcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvY2VsbHMuc3ZnXCIgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuXG4gICAgICA8IS0tIENvbnRyaWJ1dGVkIG5vLXByb21wdCB0b29scyAoVE9PTEJBUl9UT09MUykuIFVubGlrZSB0aGUgU0FNIGFuZFxuICAgICAgICAgICBjZWxscG9zZSB0b29scyB0aGVzZSB0YWtlIG5vIHByb21wdCDigJQgdGhleSBzd2VlcCB0aGUgd2hvbGUgY3VycmVudFxuICAgICAgICAgICB2aWV3IHJhdGhlciB0aGFuIGJlaW5nIHBvaW50ZWQgYXQgc29tZXRoaW5nIOKAlCBzbyB0aGVyZSBpcyBub3RoaW5nIHRvXG4gICAgICAgICAgIGRyYXcgZmlyc3QuIEVhY2ggcmVuZGVycyB0aGUgc2FtZSBzcGxpdCBidXR0b246IHJ1biwgcGljayBjaGVja3BvaW50LFxuICAgICAgICAgICBvcGVuIHBhcmFtZXRlcnMuXG5cbiAgICAgICAgICAgTm90aGluZyBoZXJlIG5hbWVzIGEgc3BlY2lmaWMgdG9vbC4gVGhlIGxpYnJhcnkgc2hpcHMgbm9uZTsgYSBob3N0XG4gICAgICAgICAgIHJlZ2lzdGVycyB0aGVtLCB3aGljaCBpcyB3aGF0IGxldHMgdGhpcyBwYWNrYWdlIGJlIHB1Ymxpc2hlZCB3aXRob3V0XG4gICAgICAgICAgIHRoZSBjbG9zZWQgbW9kZWwgZGVwZW5kZW5jaWVzLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IHRvb2wgb2YgY29udHJpYnV0ZWRUb29sczsgdHJhY2tCeTogdHJhY2tUb29sQnlJZFwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cInAtYnV0dG9uc2V0IHNlZ21lbnQtc3BsaXRcIj5cbiAgICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICAgIFtwVG9vbHRpcF09XCJ0b29sLnJ1blRvb2x0aXBcIlxuICAgICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgICAob25DbGljayk9XCJydW5Ub29sLmVtaXQodG9vbC5pZClcIj5cbiAgICAgICAgICAgIDwhLS0gYHRvb2xiYXItaWNvbi1kYXJrYCBpcyBoYXJkY29kZWQgcmF0aGVyIHRoYW4gYm91bmQsIHVubGlrZSB0aGVcbiAgICAgICAgICAgICAgICAgdG9nZ2xlIHRvb2xzOiB0aGVzZSBmaXJlIGEgcnVuIGFuZCBoYXZlIG5vIGFjdGl2ZSBzdGF0ZS4gLS0+XG4gICAgICAgICAgICA8aW1nICpuZ0lmPVwidG9vbC5pY29uLnNyY1wiIFtzcmNdPVwidG9vbC5pY29uLnNyY1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIlxuICAgICAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIgW2FsdF09XCJ0b29sLmxhYmVsXCIgLz5cbiAgICAgICAgICAgIDxpICpuZ0lmPVwidG9vbC5pY29uLnBpXCIgY2xhc3M9XCJwaSB7eyB0b29sLmljb24ucGkgfX1cIj48L2k+XG4gICAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWNoZXZyb24tZG93blwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgICBbcFRvb2x0aXBdPVwidG9vbC5tb2RlbFRvb2x0aXAgfHwgJ1BpY2sgdGhlIG1vZGVsJ1wiXG4gICAgICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvb2xNZW51LnRvZ2dsZSgkZXZlbnQpXCI+XG4gICAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgICA8cC1idXR0b24gKm5nSWY9XCJ0b29sLnBhcmFtcy5sZW5ndGhcIlxuICAgICAgICAgICAgICAgICAgICBpY29uPVwicGkgcGktc2xpZGVycy1oXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICAgIFtwVG9vbHRpcF09XCJ0b29sLnBhcmFtc1Rvb2x0aXAgfHwgJ1BhcmFtZXRlcnMnXCJcbiAgICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwib3BlblRvb2xQYXJhbXMuZW1pdCh0b29sLmlkKVwiPlxuICAgICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgIDwvc3Bhbj5cbiAgICAgICAgPHAtbWVudSAjdG9vbE1lbnUgc3R5bGVDbGFzcz1cIm1vZGVsLW1lbnVcIiBbbW9kZWxdPVwidG9vbE1lbnVJdGVtc1t0b29sLmlkXSB8fCBbXVwiXG4gICAgICAgICAgICAgICAgW3BvcHVwXT1cInRydWVcIiBhcHBlbmRUbz1cImJvZHlcIj5cbiAgICAgICAgICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiaXRlbVwiIGxldC1pdGVtPlxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJtb2RlbE1lbnVJdGVtXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgW25nVGVtcGxhdGVPdXRsZXRDb250ZXh0XT1cInsgJGltcGxpY2l0OiBpdGVtIH1cIj48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICA8L3AtbWVudT5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cInRvb2xzLmhlbHBcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG5cbiAgICAgIDwhLS0gSGVscCAtLT5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktcXVlc3Rpb24tY2lyY2xlXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJIZWxwXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJzaG93SGVscCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gIDwvZGl2PlxuPC9wLXRvb2xiYXI+XG48cC1kaWFsb2cgaGVhZGVyPVwiUGxvdHRpbmcgaGVscFwiIFsodmlzaWJsZSldPVwiZGlzcGxheUhlbHBEaWFsb2dcIiBbbW9kYWxdPVwidHJ1ZVwiIFtzdHlsZV09XCJ7d2lkdGg6ICc1MHZ3J31cIlxuICAgICAgICAgIFttYXhpbWl6YWJsZV09XCJ0cnVlXCIgW2RyYWdnYWJsZV09XCJmYWxzZVwiIFtyZXNpemFibGVdPVwiZmFsc2VcIj5cbiAgPHA+Q2xpY2sgYSBmaWxlIGluIHRoZSBsZWZ0LWhhbmQgdHJlZSB0byBkaXNwbGF5IGl0LiBCeSBkZWZhdWx0IGl0IG9wZW5zIGluIHRoZVxuICAgIDxiPkltYWdlPC9iPiB2aWV3IOKAlCBhIG5hdGl2ZWx5IHRpbGVkLCBkZWVwbHkgem9vbWFibGUgcmFzdGVyIChPcGVuU2VhZHJhZ29uKSB3aXRoIGFcbiAgICBuYXZpZ2F0b3IgbWluaW1hcCBhbmQgYSBwaHlzaWNhbC11bml0cyBzY2FsZSBiYXIuIDxiPkNsaWNrIHRoZSBpbWFnZSB0byB6b29tPC9iPiB0b3dhcmRcbiAgICB0aGUgY2xpY2tlZCBwb2ludDsgc2Nyb2xsIHRvIHpvb20gYW5kIGRyYWcgdG8gcGFuLiBXaGVuIGEgc3RhY2sgaXMgb3BlbiwgYSBzbGljZSBzbGlkZXJcbiAgICAoSW1hZ2Ugdmlldykgb3IgaW1hZ2UtbW9kZSB0b2dnbGUgKG90aGVyIHZpZXdzKSBhcHBlYXJzLiBSZWdpb25zIGNhbiBhbHNvIGJlIGVkaXRlZCBpbiB0aGVcbiAgICA8Yj5SZWdpb24gRWRpdG9yPC9iPiwgb3BlbmVkIHdpdGggdGhlXG4gICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVnaW9uLWVkaXRvci5zdmdcIiB3aWR0aD1cIjE0XCIgaGVpZ2h0PVwiMTRcIiBhbHQ9XCJSZWdpb24gRWRpdG9yXCIvPlxuICAgIHRvb2xiYXIgYnV0dG9uIGFzIGEgZmxvYXRpbmcgZGlhbG9nLiBEcmFnIHRoZSA8aSBjbGFzcz1cInBpIHBpLWJhcnNcIj48L2k+IGhhbmRsZSB0byBkZXRhY2hcbiAgICB0aGlzIHRvb2xiYXIgaW50byBhIGZsb2F0aW5nIHdpbmRvdy5cbiAgPC9wPlxuXG4gIDxoMz5QbG90LXR5cGUgZHJvcGRvd248L2gzPlxuICA8cD5cbiAgICBUaGUgPGI+cGxvdC10eXBlIGRyb3Bkb3duPC9iPiBzd2l0Y2hlcyBob3cgdGhlIGZpbGUgaXMgdmlzdWFsaXplZDpcbiAgICA8Yj5JbWFnZTwvYj4sIDxiPkNvbnRvdXI8L2I+LCA8Yj5TY2F0dGVyIChyZWdpb25zKTwvYj4sIDxiPlN1cmZhY2UgKDNEKTwvYj4sXG4gICAgPGI+U2NhdHRlciAzRDwvYj4sIGFuZCA8Yj5Jc29zdXJmYWNlICgzRCk8L2I+LiBTY2FsYXIgYW5kIDNEIHR5cGVzIHJlcXVpcmUgYSBncmF5c2NhbGVcbiAgICBpbWFnZSAodGhlIDNEIHZvbHVtZSB0eXBlcyBhbHNvIHJlcXVpcmUgYSB6LXN0YWNrKS4gVGhlIG5vbi1pbWFnZSB2aWV3cyByZW5kZXIgd2l0aFxuICAgIFBsb3RseSBhbmQgc3VwcG9ydCBcInJlYWwgem9vbWluZ1wiIChhIGRvd25zY2FsZWQgb3ZlcnZpZXcgdGhhdCByZS1mZXRjaGVzIGhpZ2hlclxuICAgIHJlc29sdXRpb24gYXMgeW91IHpvb20gaW4pLlxuICA8L3A+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5Jc29zdXJmYWNlIGJhbmQ8L2I+XCIgc2xpZGVyIChJc29zdXJmYWNlKTogc2V0cyB0aGUgaW50ZW5zaXR5IGJhbmQgdGhlIHN1cmZhY2VzIGFyZVxuICAgICAgZHJhd24gZm9yLCBtYXBwZWQgb250byB0aGUgdm9sdW1lJ3MgcmVhbCBpbnRlbnNpdHkgcmFuZ2UuPC9saT5cbiAgICA8bGk+XCI8Yj4zRCBjYW1lcmE8L2I+XCIgKFN1cmZhY2UgLyBTY2F0dGVyIDNEIC8gSXNvc3VyZmFjZSk6IHpvb20gPGkgY2xhc3M9XCJwaSBwaS1zZWFyY2hcIj48L2k+LFxuICAgICAgcGFuIDxpIGNsYXNzPVwicGkgcGktYXJyb3dzLWFsdFwiPjwvaT4sIG9yYml0IDxpIGNsYXNzPVwicGkgcGktZ2xvYmVcIj48L2k+LFxuICAgICAgdHVybnRhYmxlIDxpIGNsYXNzPVwicGkgcGktc3luY1wiPjwvaT4sIGFuZCByZXNldCBjYW1lcmEgPGkgY2xhc3M9XCJwaSBwaS1ob21lXCI+PC9pPjtcbiAgICAgIHNjcm9sbCB0byB6b29tIHRoZSAzRCBzY2VuZS48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5JbWFnZSBtYW5pcHVsYXRpb24gdG9vbHM8L2gzPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+UmVnaW9uIEVkaXRvcjwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9yZWdpb24tZWRpdG9yLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjpcbiAgICAgIG9wZW5zIHRoZSBSZWdpb24gRWRpdG9yIGFzIGEgZmxvYXRpbmcsIGRyYWdnYWJsZSBkaWFsb2cg4oCUIGEgcGVyLWltYWdlIHRhYmxlIG9mIGV2ZXJ5IHJlZ2lvblxuICAgICAgKGNsYXNzLCBjb2xvdXIsIGFyZWEpIHdpdGggYnVsayBzZWxlY3QvbGFiZWwvY29sb3VyL2RlbGV0ZSBhbmQgR2VvSlNPTiBpbXBvcnQvZXhwb3J0LiBJdCBzdGF5c1xuICAgICAgaW4gc3luYyB3aXRoIHRoZSBpbWFnZSwgc28geW91IGNhbiBrZWVwIGl0IG9wZW4gd2hpbGUgZHJhd2luZy48L2xpPlxuICAgIDxsaT5cIjxiPkludGVuc2l0eSBwcm9maWxlIGxpbmVzPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NwbGluZS1jaGFydC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgICg8aT53b3JrIGluIHByb2dyZXNzIOKAlCBub3QgeWV0IHN0YWJsZTwvaT4pOiBhZGRzIGEgY29sb3VyZWQgbGluZSBST0k7IGEgZmxvYXRpbmcgaW5zZXRcbiAgICAgIGNoYXJ0IHBsb3RzIGludGVuc2l0eSBhbG9uZyBlYWNoIGxpbmUsIHVwZGF0ZXMgbGl2ZSBhcyB5b3UgZHJhZyBpdCwgYW5kIHJlLXNhbXBsZXMgYXQgdGhlXG4gICAgICBjdXJyZW50IHpvb20gcmVzb2x1dGlvbi4gRWFjaCBhZGRlZCBsaW5lIGdldHMgaXRzIG93biBjb2xvdXI7IHNlbGVjdCBhIGxpbmUgYW5kIHVzZSB0aGVcbiAgICAgIHRyYXNoIHRvb2wgPGkgY2xhc3M9XCJwaSBwaS10cmFzaFwiPjwvaT4gdG8gcmVtb3ZlIGl0IChhbmQgaXRzIHRyYWNlKS48L2xpPlxuICAgIDxsaT5cIjxiPkNoYW5uZWxzICZhbXA7IEhpc3RvZ3JhbTwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9pbnZlcnQuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgb3BlbnMgYSBmbG9hdGluZyBkaWFsb2cgZm9yIGJyaWdodG5lc3MvY29udHJhc3QsIGdhbW1hLCBhbmQgcGVyLWNoYW5uZWwgZGlzcGxheSwgcGx1cyB0aGVcbiAgICAgIDxiPmNvbG9ybWFwIC8gTFVUPC9iPiBjb250cm9scyAoZ3JheXNjYWxlIG9ubHkpIOKAlCBhIDxiPnJldmVyc2U8L2I+IHRvZ2dsZSBhbmQgYSBjb2xvcm1hcFxuICAgICAgZHJvcGRvd24gKGRlZmF1bHQ6IGludmVydGVkIGdyZXlzKS48L2xpPlxuICAgIDxsaT5cIjxiPlNtb290aGVuPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3Ntb290aC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgIChJbWFnZSB2aWV3IG9ubHkpOiB0aGUgaW1hZ2Ugb3BlbnMgc2hvd2luZyA8Yj5yYXcgcGl4ZWxzPC9iPiAobmVhcmVzdC1uZWlnaGJvdXIpLCBzb1xuICAgICAgem9vbWluZyBpbiByZW5kZXJzIGNyaXNwIGJsb2NrcyBmb3IgcGl4ZWwgaW5zcGVjdGlvbi4gVG9nZ2xlIHRoaXMgb24gZm9yIGJpbGluZWFyXG4gICAgICBzbW9vdGhpbmc7IHRoZSBpY29uIHR1cm5zIHdoaXRlIHdoaWxlIHNtb290aGluZyBpcyBhY3RpdmUuPC9saT5cbiAgPC91bD5cblxuICA8aDM+RG93bmxvYWQgJmFtcDsgdmlldyB0b29sczwvaDM+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5Eb3dubG9hZDwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLWRvd25sb2FkXCI+PC9pPjogZG93bmxvYWQgdGhlIGN1cnJlbnQgaW1hZ2UgYXMgYSBQTkcuPC9saT5cbiAgICA8bGk+XCI8Yj5GaXQgdG8gdmlldzwvYj5cIiAvIGF1dG9zY2FsZSA8aSBjbGFzcz1cInBpIHBpLXdpbmRvdy1tYXhpbWl6ZVwiPjwvaT46IHJlc2V0cyB0aGUgaW1hZ2VcbiAgICAgIHRvIGl0cyBvcmlnaW5hbCBkaW1lbnNpb25zLiBJbiBzdGFjayBtb2RlIGl0IGFsc28gcmVzZXRzIHRvIHNpbmdsZS1pbWFnZSBtb2RlLjwvbGk+XG4gIDwvdWw+XG5cbiAgPGgzPlpvb20gdG9vbHM8L2gzPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+Wm9vbSBib3g8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvem9vbS1ib3guc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+XG4gICAgICAoPGtiZD5iPC9rYmQ+KTogZHJhZyBhIGJveCB0byBzZWxlY3QgYW4gYXJlYSB0byB6b29tIGludG8uPC9saT5cbiAgICA8bGk+XCI8Yj5QYW48L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1hcnJvd3MtYWx0XCI+PC9pPiAoPGtiZD5wPC9rYmQ+KTogZHJhZyB0byBwYW4gdGhlIGltYWdlLjwvbGk+XG4gICAgPGxpPlwiPGI+Wm9vbSBpbjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXNlYXJjaC1wbHVzXCI+PC9pPiAoPGtiZD4rPC9rYmQ+KTogem9vbSBpbiBvbmUgc3RlcC48L2xpPlxuICAgIDxsaT5cIjxiPlpvb20gb3V0PC9iPlwiIDxpIGNsYXNzPVwicGkgcGktc2VhcmNoLW1pbnVzXCI+PC9pPiAoPGtiZD4tPC9rYmQ+KTogem9vbSBvdXQgb25lIHN0ZXAsXG4gICAgICBzdG9wcGluZyBhdCB0aGUgb3JpZ2luYWwgaW1hZ2UgZGltZW5zaW9ucy48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5SZWdpb24gdG9vbHM8L2gzPlxuICA8cD5CeSBkZWZhdWx0IG5vIHRvb2wgaXMgc2VsZWN0ZWQgKGFsbCBncmV5ZWQtb3V0KTsgc2VsZWN0aW5nIGEgdG9vbCBoaWdobGlnaHRzIGl0LiBUaGVcbiAgICB2ZXJ0ZXgtZWRpdGluZyB0b29scyAocG9seWdvbiwgYWRkL2RlbGV0ZSB2ZXJ0ZXgsIG1vdmUsIELDqXppZXIgY29udmVyc2lvbnMpIGFyZSBhdmFpbGFibGVcbiAgICBpbiB0aGUgPGI+SW1hZ2U8L2I+IHZpZXcgb25seS4gT24gYSA8Yj5kb251dDwvYj4gKGEgcmVnaW9uIHdpdGggYSBob2xlKSwgdGhlIGlubmVyLXJpbmdcbiAgICB2ZXJ0aWNlcyBhcmUgZWRpdGFibGUgdG9vIOKAlCBkcmFnLCBhZGQsIGFuZCBkZWxldGUgdGhlbSBqdXN0IGxpa2UgdGhlIG91dGVyIHJpbmcuPC9wPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+U2VsZWN0aW9uPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktYXJyb3ctdXAtcmlnaHRcIj48L2k+ICg8a2JkPnM8L2tiZD4pOiBhIG5ldXRyYWwgbW9kZVxuICAgICAgdGhhdCB0dXJucyBvZmYgd2hhdGV2ZXIgZHJhd2luZyB0b29sIHdhcyBhY3RpdmUgKHdhbmQsIHZlcnRleCBlcmFzZXIsIHJlY3RhbmdsZSwgZXRjLikuXG4gICAgICBVc2UgaXQgdG8gc3RlcCBvdXQgb2YgZWRpdGluZyB3aXRob3V0IHBpY2tpbmcgYW5vdGhlciB0b29sLjwvbGk+XG4gICAgPGxpPlwiPGI+UmVjdGFuZ3VsYXIgcmVnaW9uPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktc3RvcFwiPjwvaT4gKDxrYmQ+cjwva2JkPik6IGNsaWNrIGFuZCBkcmFnIHRvXG4gICAgICBjcmVhdGUgYSByZWN0YW5ndWxhciByZWdpb24uPC9saT5cbiAgICA8bGk+XCI8Yj5Qb2x5bGluZTwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9wb2x5bGluZS5zdmdcIiB3aWR0aD1cIjE0XCIgaGVpZ2h0PVwiMTRcIj5cbiAgICAgICg8a2JkPmw8L2tiZD4pOiBkcmF3IGFuIG9wZW4gKG5vbi1jbG9zZWQpIHBvbHlsaW5lIGJ5IGNsaWNraW5nIGFuZCBkcmFnZ2luZy4gUG9seWxpbmVzIGNhblxuICAgICAgYmUgZWRpdGVkIGFuZCBleHBvcnRlZCB0byBHZW9KU09OIGFzIGEgPGNvZGU+TGluZVN0cmluZzwvY29kZT4gZmVhdHVyZS48L2xpPlxuICAgIDxsaT5cIjxiPkZyZWVmb3JtIHJlZ2lvbjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXBlbmNpbFwiPjwvaT4gKDxrYmQ+Zjwva2JkPik6IGNsaWNrIGFuZCBkcmFnIHRvXG4gICAgICBkcmF3IGEgY2xvc2VkIGZyZWVmb3JtIHBvbHlnb24uPC9saT5cbiAgICA8bGk+XCI8Yj5CcnVzaDwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9wYWludGJydXNoLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjogY2xpY2tcbiAgICAgIGFuZCBkcmFnIHRvIHBhaW50IGEgcmVnaW9uIHdpdGggYSBjaXJjdWxhciBicnVzaCDigJQgdGhlIHBhaW50ZWQgc3Ryb2tlIGlzIHRoZSByZWdpb24uXG4gICAgICBBIDxiPlNpemU8L2I+IHNsaWRlciAoYnJ1c2ggZGlhbWV0ZXIsIGluIGltYWdlIHBpeGVscykgYXBwZWFycyBpbiB0aGUgdG9vbGJhciB3aGlsZSB0aGVcbiAgICAgIGJydXNoIGlzIGFjdGl2ZS4gSG9sZCA8a2JkPlNoaWZ0PC9rYmQ+IHdoaWxlIHBhaW50aW5nIHRvIDxiPmVyYXNlPC9iPiAoc3VidHJhY3QpIGZyb20gYVxuICAgICAgcmVnaW9uIGluc3RlYWQuIFBhaW50aW5nIG92ZXIgYW4gZXhpc3RpbmcgcmVnaW9uIGV4dGVuZHMgaXQ7IGJydXNoaW5nIGFjcm9zcyB0d28gcmVnaW9uc1xuICAgICAgbWVyZ2VzIHRoZW0uIFBhaW50aW5nIGEgY2xvc2VkIHJpbmcgKG9yIHNoaWZ0LWVyYXNpbmcgYW4gaW50ZXJpb3IgcG9ja2V0KSBsZWF2ZXMgdGhlXG4gICAgICBlbmNsb3NlZCBhcmVhIGVtcHR5IOKAlCBhIDxiPmRvbnV0PC9iPiAoSW1hZ2UgdmlldykuPC9saT5cbiAgICA8bGk+XCI8Yj5Qb2x5Z29uPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3BvbHlnb24tdmVydGljZXMuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgY2xpY2sgdG8gcGxhY2UgZWFjaCB2ZXJ0ZXg7IGNsaWNrIHRoZSBmaXJzdCBwb2ludCB0byBjbG9zZSB0aGUgcG9seWdvbi48L2xpPlxuICAgIDxsaT5cIjxiPkFkZCB2ZXJ0ZXg8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvdmVydGV4LWFkZC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46XG4gICAgICBjbGljayBhbiBlZGdlIG9mIHRoZSBzZWxlY3RlZCBwb2x5Z29uIHRvIGluc2VydCBhIG5ldyB2ZXJ0ZXguPC9saT5cbiAgICA8bGk+XCI8Yj5EZWxldGUgdmVydGV4PC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3ZlcnRleC1kZWxldGUuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgY2xpY2sgYSB2ZXJ0ZXggb2YgdGhlIHNlbGVjdGVkIHBvbHlnb24gdG8gcmVtb3ZlIGl0LjwvbGk+XG4gICAgPGxpPlwiPGI+TW92ZSByZWdpb248L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVnaW9uLW1vdmUuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgZHJhZyB0byBtb3ZlIHRoZSBzZWxlY3RlZCByZWdpb24uPC9saT5cbiAgICA8bGk+XCI8Yj5UbyBCw6l6aWVyPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3RvLWJlemllci5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj4gL1xuICAgICAgXCI8Yj5UbyBwb2x5Z29uPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3RvLXBvbHlnb24uc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgY29udmVydCB0aGUgc2VsZWN0ZWQgcmVnaW9uIHRvIGEgc21vb3RoIELDqXppZXIgY3VydmUsIG9yIGJhY2sgdG8gYSBzdHJhaWdodC1lZGdlZCBwb2x5Z29uLjwvbGk+XG4gICAgPGxpPlwiPGI+V2FuZDwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy93YW5kLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPiAoPGtiZD53PC9rYmQ+KTpcbiAgICAgIGNsaWNrIHRvIGdyb3cgYSByZWdpb24gZnJvbSBwaXhlbHMgd2l0aCBzaW1pbGFyIHZhbHVlcyBhcm91bmQgdGhlIGNsaWNrIHBvaW50LiBIb2xkXG4gICAgICA8a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4gZm9yIGFuIGV4YWN0LW1hdGNoIGZsb29kIGZpbGw7IGRyYWcgdG8ga2VlcCBleHRlbmRpbmcgdGhlXG4gICAgICBzZWxlY3Rpb24gd2hpbGUgbW92aW5nOyBob2xkIDxrYmQ+U2hpZnQ8L2tiZD4gd2hpbGUgY2xpY2tpbmcgb3IgZHJhZ2dpbmcgb24gYW4gZXhpc3RpbmdcbiAgICAgIHJlZ2lvbiB0byA8Yj5lcmFzZTwvYj4gcGl4ZWxzIGZyb20gaXQuIFVzZSB0aGUgPGI+U2Vucy48L2I+IHNsaWRlciB0aGF0IGFwcGVhcnMgdG8gbWFrZSB0aGVcbiAgICAgIHdhbmQgc3RyaWN0ZXIgKGhpZ2hlciA9IHNtYWxsZXIgc2VsZWN0aW9uKSBvciBsb29zZXIuIFN1YnNlcXVlbnQgY2xpY2tzIGtlZXAgZXh0ZW5kaW5nIHRoZVxuICAgICAgc2FtZSByZWdpb247IGNsaWNraW5nIGluc2lkZSBhbiBleGlzdGluZyByZWdpb24gYWRvcHRzIGl0LCBhbmQgZ3Jvd2luZyBpbnRvIGFub3RoZXIgcmVnaW9uXG4gICAgICBtZXJnZXMgdGhlbS48L2xpPlxuICAgIDxsaT5cIjxiPlZlcnRleCBlcmFzZXI8L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1lcmFzZXJcIj48L2k+ICg8a2JkPmU8L2tiZD4pOiBjbGljayBvciBjbGljay1hbmQtZHJhZ1xuICAgICAgdG8gcmVtb3ZlIHBvbHlnb24gdmVydGljZXMgdGhhdCBmYWxsIGluc2lkZSB0aGUgY3Vyc29yIGNpcmNsZSBmcm9tIGFueSByZWdpb24sIGluY2x1ZGluZyBhXG4gICAgICByZWdpb24ncyBpbm5lciByaW5ncyAoZG9udXQgaG9sZXMpLiBVc2UgdGhlIDxiPlJhZGl1czwvYj4gc2xpZGVyIHRvIHNldCB0aGUgZXJhc2VyIHNpemUgaW5cbiAgICAgIGltYWdlIHBpeGVscy4gUG9seWdvbnMgcmVkdWNlZCBiZWxvdyAzIHZlcnRpY2VzIChvciBwb2x5bGluZXMgYmVsb3cgMikgYXJlIGRlbGV0ZWQgZW50aXJlbHk7XG4gICAgICBhIGhvbGUgcmVkdWNlZCBiZWxvdyAzIHZlcnRpY2VzIGlzIHJlbW92ZWQuPC9saT5cbiAgICA8bGk+XCI8Yj5VbmRvPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3VuZG8uc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+XG4gICAgICAoPGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+KzxrYmQ+Wjwva2JkPik6IHJldmVydHMgdGhlXG4gICAgICBsYXN0IHJlZ2lvbiBhY3Rpb24gKGRyYXcsIGRlbGV0ZSwgZWRpdCwgd2FuZC9icnVzaCBzdHJva2UsIHNlZ21lbnRhdGlvbiwg4oCmKS4gVXAgdG8gdGhlIGxhc3RcbiAgICAgIDxiPjEwPC9iPiBhY3Rpb25zIGNhbiBiZSB1bmRvbmU7IHRoZSBidXR0b24gZ3JleXMgb3V0IHdoZW4gdGhlcmUncyBub3RoaW5nIGxlZnQgdG8gdW5kby48L2xpPlxuICAgIDxsaT5cIjxiPlJlZG88L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVkby5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgICg8a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4rPGtiZD5TaGlmdDwva2JkPis8a2JkPlo8L2tiZD4gb3IgPGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+KzxrYmQ+WTwva2JkPik6XG4gICAgICByZS1hcHBsaWVzIHRoZSBtb3N0IHJlY2VudGx5IHVuZG9uZSBhY3Rpb24uIFBlcmZvcm1pbmcgYSBuZXcgcmVnaW9uIGFjdGlvbiBjbGVhcnMgd2hhdCBjYW4gYmUgcmVkb25lLjwvbGk+XG4gICAgPGxpPlwiPGI+RGVsZXRlIHJlZ2lvbjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXRyYXNoXCI+PC9pPiAoPGtiZD5EZWw8L2tiZD4pOiBkZWxldGVzIHRoZSBzZWxlY3RlZFxuICAgICAgcmVnaW9uLiBUaGlzIGNhbiBhbHNvIGJlIGRvbmUgZnJvbSB0aGUgUmVnaW9uIEVkaXRvci48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5SZWdpb24gYWN0aW9ucyAocmlnaHQtY2xpY2sgdGhlIGltYWdlKTwvaDM+XG4gIDxwPlJpZ2h0LWNsaWNrIGEgcmVnaW9uIOKAlCBvciB0aGUgY3VycmVudCBtdWx0aS1zZWxlY3Rpb24g4oCUIHRvIG9wZW4gdGhlXG4gICAgXCI8Yj5TZWxlY3RlZCByZWdpb24ocyk8L2I+XCIgZ3JvdXAgYXQgdGhlIHRvcCBvZiB0aGUgY29udGV4dCBtZW51LiBUaGVzZSBhY3QgaW1tZWRpYXRlbHkgb25cbiAgICB0aGUgc2VsZWN0aW9uICh0aGV5J3JlIG5vdCBkcmF3aW5nIG1vZGVzKSBhbmQgYXJlIGZ1bGx5IHVuZG8vcmVkby1hYmxlOjwvcD5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPlNlbGVjdCBhbGwgcmVnaW9uczwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLWNoZWNrLXNxdWFyZVwiPjwvaT46IHNlbGVjdCBldmVyeSByZWdpb24gb25cbiAgICAgIHRoZSBpbWFnZSAoYWxzbyBpbiB0aGUgUmVnaW9uIEVkaXRvciB0b29sYmFyKS48L2xpPlxuICAgIDxsaT5cIjxiPk1lcmdlIC8gZ3JvdXA8L2I+XCI6IGNvbWJpbmUgMisgc2VsZWN0ZWQgcmVnaW9ucyBpbnRvIG9uZS4gSWYgdGhlIHBpZWNlcyBkb24ndCB0b3VjaCxcbiAgICAgIHRoZSByZXN1bHQgaXMgYSBzaW5nbGUgbXVsdGktcGFydCByZWdpb24gKHNwYXJzZSBcImJpdHNcIikuPC9saT5cbiAgICA8bGk+XCI8Yj5Vbmdyb3VwPC9iPlwiOiBzcGxpdCBhIG11bHRpLXBhcnQgcmVnaW9uIGJhY2sgaW50byBvbmUgcmVnaW9uIHBlciBwaWVjZS48L2xpPlxuICAgIDxsaT5cIjxiPkludmVyc2U8L2I+XCI6IHJlcGxhY2UgdGhlIHNlbGVjdGlvbiB3aXRoIGV2ZXJ5dGhpbmcgPGk+b3V0c2lkZTwvaT4gaXQg4oCUIHRoZSBpbWFnZVxuICAgICAgcmVjdGFuZ2xlIHdpdGggdGhlIHNlbGVjdGVkIHJlZ2lvbnMgcHVuY2hlZCBvdXQgYXMgaG9sZXMuIFdvcmtzIGF0IGFueSBpbWFnZSBzaXplLjwvbGk+XG4gICAgPGxpPlwiPGI+U2ltcGxpZnk8L2I+XCI6IHJlZHVjZSBhIHJlZ2lvbidzIHZlcnRleCBjb3VudCAoRG91Z2xhc+KAk1BldWNrZXIpIGJ5IGFuIGFsdGl0dWRlXG4gICAgICB0aHJlc2hvbGQgaW4gcGl4ZWxzIOKAlCBwaWNrIDxiPkxpZ2h0PC9iPiAoMSZuYnNwO3B4KSwgPGI+TWVkaXVtPC9iPiAoMyZuYnNwO3B4KSxcbiAgICAgIDxiPlN0cm9uZzwvYj4gKDgmbmJzcDtweCksIG9yIDxiPkN1c3RvbeKApjwvYj4gZm9yIGEgc2xpZGVyLiBBcHBsaWVzIHRvIHRoZSBleHRlcmlvciBhbmQgYW55XG4gICAgICBob2xlcyAoYW5kIGV2ZXJ5IHBhcnQgb2YgYSBtdWx0aS1wYXJ0IHJlZ2lvbikuPC9saT5cbiAgICA8bGk+VGhlIELDqXppZXIgY29udmVyc2lvbnMgYW5kIERlbGV0ZSBhbHNvIGxpdmUgaW4gdGhpcyBncm91cCAodGhlIHJpZ2h0LWNsaWNrIGVxdWl2YWxlbnRzIG9mXG4gICAgICB0aGUgdG9vbGJhciAvIHZlcnRleCB0b29scyBhYm92ZSkuPC9saT5cbiAgPC91bD5cblxuICA8aDM+U2VnbWVudGF0aW9uICZhbXA7IGRldGVjdGlvbiB0b29sczwvaDM+XG4gIDxwPkFJLWFzc2lzdGVkIHNlZ21lbnRhdGlvbiBhbmQgZGV0ZWN0aW9uIHRoYXQgcnVuIGVudGlyZWx5IGluIHRoZSBicm93c2VyLiBUaGUgZmlyc3QgcnVuXG4gICAgb2YgYW55IG1vZGVsIGRvd25sb2FkcyBpdCAoYSBwcm9ncmVzcyB0b2FzdCBpcyBzaG93bik7IGl0J3MgdGhlbiBjYWNoZWQuPC9wPlxuICA8cD5Ud28gc2hhcGVzIG9mIHRvb2w6IHRoZSA8Yj5wcm9tcHRlZDwvYj4gb25lcyAoU2VnbWVudCwgUG9pbnQgcHJvbXB0cywgQ2VsbHBvc2UpIGFjdCBvblxuICAgIHJlY3RhbmdsZXMgeW91IGRyYXcsIGFuZCBnZW5lcmF0ZWQgcmVnaW9ucyBpbmhlcml0IHRoZSBjb2xvciBvZiB0aGUgcmVjdGFuZ2xlIHRoZXkgY2FtZVxuICAgIGZyb208c3BhbiAqbmdJZj1cImNvbnRyaWJ1dGVkVG9vbHMubGVuZ3RoXCI+OyB0aGUgPGI+bm8tcHJvbXB0PC9iPiBvbmVzICh7eyBjb250cmlidXRlZFRvb2xOYW1lcyB9fSlcbiAgICBzd2VlcCB0aGUgd2hvbGUgY3VycmVudCB2aWV3LCBzbyB0aGVyZSBpcyBub3RoaW5nIHRvIGRyYXcgZmlyc3Q8L3NwYW4+LjwvcD5cbiAgPHA+RWFjaCBtb2RlbCBkcm9wZG93biA8aSBjbGFzcz1cInBpIHBpLWNoZXZyb24tZG93blwiPjwvaT4gbGlzdHMgdGhlIGF2YWlsYWJsZSBjaGVja3BvaW50cyB3aXRoXG4gICAgYSA8aSBjbGFzcz1cInBpIHBpLWluZm8tY2lyY2xlXCI+PC9pPiBiZXNpZGUgZWFjaCBvbmUg4oCUIGhvdmVyIGl0IGZvciB0aGF0IG1vZGVsJ3Mgc2l6ZSwgc3BlZWQsXG4gICAgdHJhaW5pbmcgZG9tYWluIGFuZCBhY2N1cmFjeSwgc28geW91IGNhbiBwaWNrIHdpdGhvdXQgbGVhdmluZyB0aGUgdG9vbGJhci48L3A+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5TZWdtZW50PC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NhbS1ib3gtcHJvbXB0LnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPlxuICAgICAgKGJveCBwcm9tcHQpOiBkcmF3IG9uZSBvciBtb3JlIDxiPlJlY3Rhbmd1bGFyIHJlZ2lvbnM8L2I+IGFyb3VuZCB0aGUgb2JqZWN0cyBvZiBpbnRlcmVzdCxcbiAgICAgIHRoZW4gY2xpY2sgU2VnbWVudC4gRWFjaCByZWN0YW5nbGUgaXMgcmVwbGFjZWQgYnkgdGhlIFNBTSBtYXNrIG9mIHRoZSBvYmplY3QgaW5zaWRlIGl0LjwvbGk+XG4gICAgPGxpPlwiPGI+UG9pbnQgcHJvbXB0czwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zYW0tcG9pbnQtcHJvbXB0LnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjogY2xpY2sgZGlyZWN0bHkgb24gYW4gb2JqZWN0IHRvXG4gICAgICBzZWdtZW50IGl0IGFzIGEgPGI+bmV3PC9iPiByZWdpb24uIEVhY2ggY2xpY2sgc3RhcnRzIGEgZnJlc2ggb2JqZWN0IOKAlCBjbGlja2luZyBhbm90aGVyXG4gICAgICBvYmplY3Qgd29uJ3QgZ3JvdyB0aGUgcHJldmlvdXMgb25lLiA8a2JkPlNoaWZ0PC9rYmQ+LzxrYmQ+QWx0PC9rYmQ+LWNsaWNrIGFkZHMgYW5cbiAgICAgIDxpPmV4Y2x1ZGU8L2k+IHBvaW50IHRoYXQgcmVmaW5lcyB0aGUgY3VycmVudCBvYmplY3Q7IDxrYmQ+RXNjPC9rYmQ+IHVuZG9lcyBpdCxcbiAgICAgIDxrYmQ+RW50ZXI8L2tiZD4gY29tbWl0cyBpdC48L2xpPlxuICAgIDxsaT5cIjxiPk1vZGVsIHBpY2tlcjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLWNoZXZyb24tZG93blwiPjwvaT46IHRoZSBkcm9wZG93biBhdHRhY2hlZCB0byB0aGVcbiAgICAgIFNlZ21lbnQgYnV0dG9uIGNob29zZXMgdGhlIFNBTSBtb2RlbCDigJQgPGI+bWljcm8tc2FtIFZpVC1UPC9iPiAoZGVmYXVsdDsgc21hbGwsIGZhc3QpIG9yXG4gICAgICA8Yj5taWNyby1zYW0gVmlULUI8L2I+IChsYXJnZXIsIGhpZ2hlc3QgYWNjdXJhY3kpIGZvciBsaWdodCBtaWNyb3Njb3B5LCBvclxuICAgICAgPGI+cGF0aG8tc2FtIFZpVC1CPC9iPiBmb3IgaGlzdG9wYXRob2xvZ3kgKEgmYW1wO0UgLyBzdGFpbmVkIHRpc3N1ZSkuIEFsbCBhcmUgcHJvbXB0YWJsZVxuICAgICAgU2VnbWVudCBBbnl0aGluZyBtb2RlbHMgZmluZXR1bmVkIGZvciBiaW9tZWRpY2FsIGltYWdlczsgdGhleSBkaWZmZXIgaW4gdGhlIDxpPmltYWdlXG4gICAgICBlbmNvZGVyPC9pPiAodGhlIGhlYXZ5IG9uY2UtcGVyLWltYWdlIHN0ZXApIGFuZCB0aGUgdHJhaW5pbmcgZG9tYWluIOKAlCBib3ggYW5kIHBvaW50XG4gICAgICBwcm9tcHRpbmcgYmVoYXZlIHRoZSBzYW1lLiBUaGUgY2hvaWNlIGFwcGxpZXMgdG8gPGk+Ym90aDwvaT4gdGhlIGJveCBhbmQgcG9pbnQgdG9vbHMsIGFuZFxuICAgICAgZWFjaCB0b29sIGxvYWRzIGl0cyBvd24gZW5jb2Rlciwgc28gdGhlIGZpcnN0IGNsaWNrIGFmdGVyIHN3aXRjaGluZyByZS1lbmNvZGVzIG9uY2UuPC9saT5cbiAgICA8bGk+XCI8Yj5DZWxscG9zZTwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9jZWxscy5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46IGRyYXcgcmVjdGFuZ2xlcywgdGhlbiBjbGljayBDZWxscG9zZSB0b1xuICAgICAgYXV0b21hdGljYWxseSBzZWdtZW50IGV2ZXJ5IGNlbGwgaW5zaWRlIGVhY2ggcmVjdGFuZ2xlIChjbGllbnQtc2lkZSBjZWxscG9zZS1TQU0pIOKAlCBvbmVcbiAgICAgIHJlZ2lvbiBwZXIgZGV0ZWN0ZWQgY2VsbC4gTm8gY2xpY2tpbmcgb24gaW5kaXZpZHVhbCBjZWxscyBuZWVkZWQuPC9saT5cbiAgICA8IS0tIENvbnRyaWJ1dGVkIHRvb2xzIGRlc2NyaWJlIHRoZW1zZWx2ZXMuIFRoaXMgbGlicmFyeSBzaGlwcyBub25lLCBzbyBhblxuICAgICAgICAgb3BlbiBidWlsZCdzIGhlbHAgbGlzdHMgb25seSB0aGUgcHJvbXB0ZWQgdG9vbHMgYWJvdmUg4oCUIHRoZSB0ZXh0IG5vXG4gICAgICAgICBsb25nZXIgcHJvbWlzZXMgYnV0dG9ucyB0aGF0IGFyZSBub3QgdGhlcmUuIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IHRvb2wgb2YgY29udHJpYnV0ZWRUb29sc1wiPlxuICAgICAgPGxpICpuZ0lmPVwidG9vbC5oZWxwXCIgW2lubmVySFRNTF09XCJ0b29sLmhlbHAuYm9keVwiPjwvbGk+XG4gICAgPC9uZy1jb250YWluZXI+XG4gIDwvdWw+XG5cbiAgPGgzPkhlbHA8L2gzPlxuICA8cD5cIjxiPkhlbHA8L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1xdWVzdGlvbi1jaXJjbGVcIj48L2k+OiBvcGVucyB0aGlzIGRpYWxvZy48L3A+XG5cbiAgPGgzPktleWJvYXJkIHNob3J0Y3V0czwvaDM+XG4gIDx0YWJsZSBjbGFzcz1cInNob3J0Y3V0cy10YWJsZVwiPlxuICAgIDx0cj48dGQ+PGtiZD4rPC9rYmQ+IC8gPGtiZD49PC9rYmQ+PC90ZD48dGQ+Wm9vbSBpbjwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+LTwva2JkPjwvdGQ+PHRkPlpvb20gb3V0PC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5iPC9rYmQ+PC90ZD48dGQ+VG9nZ2xlIHpvb20gYm94IG1vZGU8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPnA8L2tiZD48L3RkPjx0ZD5Ub2dnbGUgcGFuIG1vZGU8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPmY8L2tiZD48L3RkPjx0ZD5Ub2dnbGUgZnJlZWZvcm0gcmVnaW9uIGRyYXdpbmc8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPmw8L2tiZD48L3RkPjx0ZD5Ub2dnbGUgcG9seWxpbmUgZHJhd2luZzwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+cjwva2JkPjwvdGQ+PHRkPlRvZ2dsZSByZWN0YW5ndWxhciByZWdpb24gZHJhd2luZzwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+dzwva2JkPjwvdGQ+PHRkPlRvZ2dsZSB3YW5kIHRvb2w8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD5XYW5kICsgPGtiZD5TaGlmdDwva2JkPjwvdGQ+PHRkPkVyYXNlIHBpeGVscyBmcm9tIHRoZSBhY3RpdmUgcmVnaW9uPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+V2FuZCArIDxrYmQ+Q3RybDwva2JkPi88a2JkPkNtZDwva2JkPjwvdGQ+PHRkPkV4YWN0LW1hdGNoIGZsb29kIGZpbGwgKG5vIHNtb290aGluZyk8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPmU8L2tiZD48L3RkPjx0ZD5Ub2dnbGUgdmVydGV4IGVyYXNlcjwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+czwva2JkPjwvdGQ+PHRkPlNlbGVjdGlvbiBtb2RlIChkZWFjdGl2YXRlcyBkcmF3aW5nIHRvb2xzKTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPlBvaW50IHByb21wdCArIDxrYmQ+U2hpZnQ8L2tiZD4vPGtiZD5BbHQ8L2tiZD48L3RkPjx0ZD5BZGQgYW4gZXhjbHVkZSBwb2ludCAocmVmaW5lIHRoZSBjdXJyZW50IG9iamVjdCk8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPkVudGVyPC9rYmQ+PC90ZD48dGQ+Q29tbWl0IHRoZSBjdXJyZW50IFNBTSBwb2ludCBvYmplY3Q8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPkVzYzwva2JkPjwvdGQ+PHRkPlVuZG8gdGhlIGN1cnJlbnQgU0FNIHBvaW50IG9iamVjdDwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+RGVsPC9rYmQ+IC8gPGtiZD7ijKs8L2tiZD48L3RkPjx0ZD5EZWxldGUgc2VsZWN0ZWQgcmVnaW9uPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+KzxrYmQ+Wjwva2JkPjwvdGQ+PHRkPlVuZG8gdGhlIGxhc3QgcmVnaW9uIGFjdGlvbiAodXAgdG8gMTApPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+KzxrYmQ+U2hpZnQ8L2tiZD4rPGtiZD5aPC9rYmQ+IC8gPGtiZD5DdHJsPC9rYmQ+KzxrYmQ+WTwva2JkPjwvdGQ+PHRkPlJlZG8gdGhlIGxhc3QgdW5kb25lIHJlZ2lvbiBhY3Rpb248L3RkPjwvdHI+XG4gIDwvdGFibGU+XG5cbiAgPG5nLXRlbXBsYXRlIHBUZW1wbGF0ZT1cImZvb3RlclwiPlxuICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktY2hlY2tcIiAoY2xpY2spPVwiZGlzcGxheUhlbHBEaWFsb2c9ZmFsc2VcIiBsYWJlbD1cIk9rXCIgc3R5bGVDbGFzcz1cInAtYnV0dG9uLXRleHRcIj5cbiAgICA8L3AtYnV0dG9uPlxuICA8L25nLXRlbXBsYXRlPlxuPC9wLWRpYWxvZz5cbiJdfQ==