@jax-data-science/sci-image-visualizer 0.5.0 → 0.7.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 (42) hide show
  1. package/README.md +49 -0
  2. package/esm2022/lib/contracts/display-types.mjs +28 -1
  3. package/esm2022/lib/contracts/plot-type-contribution.contract.mjs +1 -1
  4. package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -1
  5. package/esm2022/lib/contracts/spatial-dataset.contract.mjs +3 -1
  6. package/esm2022/lib/contracts/toolbar-tool.contract.mjs +46 -4
  7. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  8. package/esm2022/lib/implementations/napari-js/napari-navigator.mjs +195 -0
  9. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +10 -2
  10. package/esm2022/lib/implementations/napari-js/napari-spatial-tiles.mjs +1098 -0
  11. package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +6 -3
  12. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +186 -10
  13. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +2 -2
  14. package/esm2022/lib/implementations/plotly/plotly.service.mjs +2 -3
  15. package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +120 -2
  16. package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +76 -1
  17. package/esm2022/lib/routing-visualizer.service.mjs +10 -1
  18. package/esm2022/lib/spatial/spatial-tiles.mjs +506 -0
  19. package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +659 -19
  20. package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +44 -3
  21. package/esm2022/lib/toolbar/toolbar.component.mjs +15 -3
  22. package/esm2022/lib/visualizer.component.mjs +221 -7
  23. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +3238 -84
  24. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  25. package/lib/contracts/display-types.d.ts +104 -1
  26. package/lib/contracts/plot-type-contribution.contract.d.ts +27 -0
  27. package/lib/contracts/ports/spatial-data.port.d.ts +59 -1
  28. package/lib/contracts/spatial-dataset.contract.d.ts +167 -0
  29. package/lib/contracts/toolbar-tool.contract.d.ts +84 -5
  30. package/lib/contracts/visualizer.contract.d.ts +22 -1
  31. package/lib/implementations/napari-js/napari-navigator.d.ts +72 -0
  32. package/lib/implementations/napari-js/napari-region-overlay.d.ts +3 -0
  33. package/lib/implementations/napari-js/napari-spatial-tiles.d.ts +240 -0
  34. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +42 -2
  35. package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +26 -2
  36. package/lib/implementations/spatial-data-http/spatial-wire.d.ts +34 -1
  37. package/lib/spatial/spatial-tiles.d.ts +222 -0
  38. package/lib/spatial-controls/spatial-controls.component.d.ts +234 -3
  39. package/lib/toolbar/brush/brush-tool.service.d.ts +21 -0
  40. package/lib/toolbar/toolbar.component.d.ts +11 -3
  41. package/lib/visualizer.component.d.ts +54 -2
  42. package/package.json +1 -1
@@ -89,6 +89,10 @@ export class ToolbarComponent {
89
89
  * per-tool parameter state; this component only draws them.
90
90
  */
91
91
  this.contributedTools = [];
92
+ /** Dialog tools (TOOLBAR_TOOLS, kind: 'dialog'), sorted; shown in the Image view. */
93
+ this.dialogTools = [];
94
+ /** Id of the dialog tool whose dialog is open, or null. */
95
+ this.openDialogToolId = null;
92
96
  /** Active checkpoint per contributed tool, keyed by tool id. */
93
97
  this.toolModelIds = {};
94
98
  this.selectPlotType = new EventEmitter();
@@ -133,6 +137,8 @@ export class ToolbarComponent {
133
137
  this.segmentCellpose = new EventEmitter();
134
138
  /** Run a contributed tool over the current view, by tool id. */
135
139
  this.runTool = new EventEmitter();
140
+ /** A dialog tool's button was clicked: open its dialog, or close it if open. */
141
+ this.toggleDialogTool = new EventEmitter();
136
142
  /** Pick a contributed tool's checkpoint. */
137
143
  this.toolModelChange = new EventEmitter();
138
144
  /** Open a contributed tool's parameter dialog, by tool id. */
@@ -305,11 +311,11 @@ export class ToolbarComponent {
305
311
  this.displayHelpDialog = true;
306
312
  }
307
313
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: ToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
308
- 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", basePlotType: "basePlotType", isoRange: "isoRange", maxIndex: "maxIndex", zIndex: "zIndex", isHeatmap: "isHeatmap", is3dRegions: "is3dRegions", 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", openSpatialControls: "openSpatialControls", 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 <!-- Spatial-omics controls. Only meaningful in the spatial mode, which is\n itself only offered while a dataset is loaded \u2014 so this button appears\n exactly when there is something for it to control. -->\n <p-button *ngIf=\"tools.specialTools && isSpatialMode\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Spatial omics (colour by column or gene, legend, selection, distributions)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openSpatialControls.emit()\">\n <img src=\"assets/plotting/spatial-omics.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, and for the\n spatial-omics modes: a 3D omics dataset publishes its reference volume as a z-stack\n image, and these modes draw observations, not one of its slices. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode && !isSpatialMode\">\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 || is3dRegions) && 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 *ngIf=\"!is3dRegions\" 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 <!-- Brush and polyline are absent from a 3D cloud: the brush paints in IMAGE\n pixels and there is no raster to sample, and an open polyline is a\n profile tool, not a closed region a selection can test against. -->\n <p-button *ngIf=\"!is3dRegions\" 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>Heatmap</b>, <b>Contour</b>, <b>Surface</b>, <b>Volume</b>,\n <b>Isosurface</b>, and \u2014 when a spatial-omics dataset is open \u2014\n <b>Spatial omics</b> and <b>Spatial omics 3D</b>.\n </p>\n <p>\n <b>Only the modes the data can actually support are offered</b>, so the list changes\n with what is loaded rather than showing entries that would come up blank:\n </p>\n <ul>\n <li><b>Contour</b>, <b>Volume</b> and <b>Isosurface</b> need a grayscale image; the\n two 3D ones also need a z-stack.</li>\n <li><b>Spatial omics</b> needs a published dataset, and <b>Spatial omics 3D</b> needs\n one whose coordinates carry a z.</li>\n <li>The pixel modes (<b>Image</b>, <b>Heatmap</b>, <b>Surface</b>) are withheld for a\n dataset that brings no tissue image \u2014 seqFISH records coordinates and no section, so\n there is nothing for them to draw.</li>\n </ul>\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>Spatial omics</h3>\n <p>\n In <b>Spatial omics</b> mode each observation \u2014 a cell or a Visium spot \u2014 is drawn at\n its own coordinates, over the tissue image where the dataset registers onto one. The\n <img src=\"assets/plotting/spatial-omics.svg\" width=\"14\" height=\"14\" alt=\"Spatial omics\"/>\n panel colours them by a <b>column</b> (a cell type, a cluster) or by a <b>gene</b>, and\n the two are exclusive so what drives the colours is never ambiguous.\n </p>\n <ul>\n <li>\"<b>Select from ROIs</b>\": turns the regions you have drawn into a selection of\n observations. Everything else then follows that selection.</li>\n <li>\"<b>Point size</b>\" / \"<b>Opacity</b>\": how the markers are drawn, not what is\n selected.</li>\n </ul>\n\n <h3>Distribution charts</h3>\n <p>\n The <b>Distribution</b> section of the spatial-omics panel charts whatever the map is\n coloured by, so the two can never disagree. Each tab answers a different question, and\n the <i class=\"pi pi-question-circle\"></i> beside them explains the one on screen \u2014\n including what it must <em>not</em> be read for.\n </p>\n <ul>\n <li>\"<b>Counts</b>\": how many observations per category. A category is a label, not a\n magnitude, so a frequency is the only distribution it has.</li>\n <li>\"<b>Histogram</b>\" / \"<b>Violin</b>\" / \"<b>Box</b>\": how a continuous value is\n distributed \u2014 over everything, or split by a grouping column.</li>\n <li>\"<b>Heatmap</b>\": mean expression of the genes you pick within each group.\n Z-scored per gene by default, so a colour means \"above or below this gene's own\n average\" rather than \"highly expressed\".</li>\n <li>\"<b>Embedding</b>\": a UMAP, PCA or t-SNE over the same cells. <b>Lasso it to\n select on the map</b> \u2014 both views read the same selection. Only PCA labels its axes\n with a percentage, because only its axes explain a measurable share of the variance;\n UMAP and t-SNE coordinates are arbitrary, so distance between distant clusters\n should not be read.</li>\n <li>\"<b>Detach</b>\": moves the charts into their own window so they can sit beside the\n map instead of inside the panel. Switching tabs keeps the window where it is.</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 .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]" }] }); }
314
+ 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", basePlotType: "basePlotType", isoRange: "isoRange", maxIndex: "maxIndex", zIndex: "zIndex", isHeatmap: "isHeatmap", is3dRegions: "is3dRegions", 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", dialogTools: "dialogTools", openDialogToolId: "openDialogToolId", toolModelIds: "toolModelIds" }, outputs: { selectPlotType: "selectPlotType", addProfileLine: "addProfileLine", toggleImageSmoothing: "toggleImageSmoothing", isoRangeChange: "isoRangeChange", openChannelHistogram: "openChannelHistogram", openSpatialControls: "openSpatialControls", 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", toggleDialogTool: "toggleDialogTool", 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 <!-- Contributed dialog tools (TOOLBAR_TOOLS, kind: 'dialog'): app-level actions\n like the host's own buttons above, so they sit right beside them. Image view\n only, the view their dialog draws over. Highlighted while the dialog is open. -->\n <ng-container *ngIf=\"isImageView\">\n <p-button *ngFor=\"let tool of dialogTools; trackBy: trackToolById\"\n size=\"small\" [text]=\"openDialogToolId !== tool.id\"\n [pTooltip]=\"tool.tooltip\" tooltipPosition=\"bottom\"\n [ariaLabel]=\"tool.label\"\n [attr.aria-pressed]=\"openDialogToolId === tool.id\"\n (onClick)=\"toggleDialogTool.emit(tool.id)\">\n <img *ngIf=\"tool.icon.src\" [src]=\"tool.icon.src\" style=\"width: 1rem\" [alt]=\"tool.label\"\n [class]=\"openDialogToolId === tool.id ? 'toolbar-icon-active' : 'toolbar-icon-dark'\" />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n </ng-container>\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 <!-- Spatial-omics controls. Only meaningful in the spatial mode, which is\n itself only offered while a dataset is loaded \u2014 so this button appears\n exactly when there is something for it to control. -->\n <p-button *ngIf=\"tools.specialTools && isSpatialMode\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Spatial omics (colour by column or gene, legend, selection, distributions)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openSpatialControls.emit()\">\n <img src=\"assets/plotting/spatial-omics.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, and for the\n spatial-omics modes: a 3D omics dataset publishes its reference volume as a z-stack\n image, and these modes draw observations, not one of its slices. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode && !isSpatialMode\">\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 || is3dRegions) && 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 *ngIf=\"!is3dRegions\" 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 <!-- Brush and polyline are absent from a 3D cloud: the brush paints in IMAGE\n pixels and there is no raster to sample, and an open polyline is a\n profile tool, not a closed region a selection can test against. -->\n <p-button *ngIf=\"!is3dRegions\" 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>Heatmap</b>, <b>Contour</b>, <b>Surface</b>, <b>Volume</b>,\n <b>Isosurface</b>, and \u2014 when a spatial-omics dataset is open \u2014\n <b>Spatial omics</b> and <b>Spatial omics 3D</b>.\n </p>\n <p>\n <b>Only the modes the data can actually support are offered</b>, so the list changes\n with what is loaded rather than showing entries that would come up blank:\n </p>\n <ul>\n <li><b>Contour</b>, <b>Volume</b> and <b>Isosurface</b> need a grayscale image; the\n two 3D ones also need a z-stack.</li>\n <li><b>Spatial omics</b> needs a published dataset, and <b>Spatial omics 3D</b> needs\n one whose coordinates carry a z.</li>\n <li>The pixel modes (<b>Image</b>, <b>Heatmap</b>, <b>Surface</b>) are withheld for a\n dataset that brings no tissue image \u2014 seqFISH records coordinates and no section, so\n there is nothing for them to draw.</li>\n </ul>\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 <ng-container *ngFor=\"let tool of dialogTools\">\n <li *ngIf=\"tool.help\" [innerHTML]=\"tool.help.body\"></li>\n </ng-container>\n </ul>\n\n <h3>Spatial omics</h3>\n <p>\n In <b>Spatial omics</b> mode each observation \u2014 a cell or a Visium spot \u2014 is drawn at\n its own coordinates, over the tissue image where the dataset registers onto one. The\n <img src=\"assets/plotting/spatial-omics.svg\" width=\"14\" height=\"14\" alt=\"Spatial omics\"/>\n panel colours them by a <b>column</b> (a cell type, a cluster) or by a <b>gene</b>, and\n the two are exclusive so what drives the colours is never ambiguous.\n </p>\n <ul>\n <li>\"<b>Select from ROIs</b>\": turns the regions you have drawn into a selection of\n observations. Everything else then follows that selection.</li>\n <li>\"<b>Point size</b>\" / \"<b>Opacity</b>\": how the markers are drawn, not what is\n selected.</li>\n </ul>\n\n <h3>Distribution charts</h3>\n <p>\n The <b>Distribution</b> section of the spatial-omics panel charts whatever the map is\n coloured by, so the two can never disagree. Each tab answers a different question, and\n the <i class=\"pi pi-question-circle\"></i> beside them explains the one on screen \u2014\n including what it must <em>not</em> be read for.\n </p>\n <ul>\n <li>\"<b>Counts</b>\": how many observations per category. A category is a label, not a\n magnitude, so a frequency is the only distribution it has.</li>\n <li>\"<b>Histogram</b>\" / \"<b>Violin</b>\" / \"<b>Box</b>\": how a continuous value is\n distributed \u2014 over everything, or split by a grouping column.</li>\n <li>\"<b>Heatmap</b>\": mean expression of the genes you pick within each group.\n Z-scored per gene by default, so a colour means \"above or below this gene's own\n average\" rather than \"highly expressed\".</li>\n <li>\"<b>Embedding</b>\": a UMAP, PCA or t-SNE over the same cells. <b>Lasso it to\n select on the map</b> \u2014 both views read the same selection. Only PCA labels its axes\n with a percentage, because only its axes explain a measurable share of the variance;\n UMAP and t-SNE coordinates are arbitrary, so distance between distant clusters\n should not be read.</li>\n <li>\"<b>Detach</b>\": moves the charts into their own window so they can sit beside the\n map instead of inside the panel. Switching tabs keeps the window where it is.</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 .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]" }] }); }
309
315
  }
310
316
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: ToolbarComponent, decorators: [{
311
317
  type: Component,
312
- 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 <!-- Spatial-omics controls. Only meaningful in the spatial mode, which is\n itself only offered while a dataset is loaded \u2014 so this button appears\n exactly when there is something for it to control. -->\n <p-button *ngIf=\"tools.specialTools && isSpatialMode\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Spatial omics (colour by column or gene, legend, selection, distributions)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openSpatialControls.emit()\">\n <img src=\"assets/plotting/spatial-omics.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, and for the\n spatial-omics modes: a 3D omics dataset publishes its reference volume as a z-stack\n image, and these modes draw observations, not one of its slices. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode && !isSpatialMode\">\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 || is3dRegions) && 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 *ngIf=\"!is3dRegions\" 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 <!-- Brush and polyline are absent from a 3D cloud: the brush paints in IMAGE\n pixels and there is no raster to sample, and an open polyline is a\n profile tool, not a closed region a selection can test against. -->\n <p-button *ngIf=\"!is3dRegions\" 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>Heatmap</b>, <b>Contour</b>, <b>Surface</b>, <b>Volume</b>,\n <b>Isosurface</b>, and \u2014 when a spatial-omics dataset is open \u2014\n <b>Spatial omics</b> and <b>Spatial omics 3D</b>.\n </p>\n <p>\n <b>Only the modes the data can actually support are offered</b>, so the list changes\n with what is loaded rather than showing entries that would come up blank:\n </p>\n <ul>\n <li><b>Contour</b>, <b>Volume</b> and <b>Isosurface</b> need a grayscale image; the\n two 3D ones also need a z-stack.</li>\n <li><b>Spatial omics</b> needs a published dataset, and <b>Spatial omics 3D</b> needs\n one whose coordinates carry a z.</li>\n <li>The pixel modes (<b>Image</b>, <b>Heatmap</b>, <b>Surface</b>) are withheld for a\n dataset that brings no tissue image \u2014 seqFISH records coordinates and no section, so\n there is nothing for them to draw.</li>\n </ul>\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>Spatial omics</h3>\n <p>\n In <b>Spatial omics</b> mode each observation \u2014 a cell or a Visium spot \u2014 is drawn at\n its own coordinates, over the tissue image where the dataset registers onto one. The\n <img src=\"assets/plotting/spatial-omics.svg\" width=\"14\" height=\"14\" alt=\"Spatial omics\"/>\n panel colours them by a <b>column</b> (a cell type, a cluster) or by a <b>gene</b>, and\n the two are exclusive so what drives the colours is never ambiguous.\n </p>\n <ul>\n <li>\"<b>Select from ROIs</b>\": turns the regions you have drawn into a selection of\n observations. Everything else then follows that selection.</li>\n <li>\"<b>Point size</b>\" / \"<b>Opacity</b>\": how the markers are drawn, not what is\n selected.</li>\n </ul>\n\n <h3>Distribution charts</h3>\n <p>\n The <b>Distribution</b> section of the spatial-omics panel charts whatever the map is\n coloured by, so the two can never disagree. Each tab answers a different question, and\n the <i class=\"pi pi-question-circle\"></i> beside them explains the one on screen \u2014\n including what it must <em>not</em> be read for.\n </p>\n <ul>\n <li>\"<b>Counts</b>\": how many observations per category. A category is a label, not a\n magnitude, so a frequency is the only distribution it has.</li>\n <li>\"<b>Histogram</b>\" / \"<b>Violin</b>\" / \"<b>Box</b>\": how a continuous value is\n distributed \u2014 over everything, or split by a grouping column.</li>\n <li>\"<b>Heatmap</b>\": mean expression of the genes you pick within each group.\n Z-scored per gene by default, so a colour means \"above or below this gene's own\n average\" rather than \"highly expressed\".</li>\n <li>\"<b>Embedding</b>\": a UMAP, PCA or t-SNE over the same cells. <b>Lasso it to\n select on the map</b> \u2014 both views read the same selection. Only PCA labels its axes\n with a percentage, because only its axes explain a measurable share of the variance;\n UMAP and t-SNE coordinates are arbitrary, so distance between distant clusters\n should not be read.</li>\n <li>\"<b>Detach</b>\": moves the charts into their own window so they can sit beside the\n map instead of inside the panel. Switching tabs keeps the window where it is.</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 .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"] }]
318
+ 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 <!-- Contributed dialog tools (TOOLBAR_TOOLS, kind: 'dialog'): app-level actions\n like the host's own buttons above, so they sit right beside them. Image view\n only, the view their dialog draws over. Highlighted while the dialog is open. -->\n <ng-container *ngIf=\"isImageView\">\n <p-button *ngFor=\"let tool of dialogTools; trackBy: trackToolById\"\n size=\"small\" [text]=\"openDialogToolId !== tool.id\"\n [pTooltip]=\"tool.tooltip\" tooltipPosition=\"bottom\"\n [ariaLabel]=\"tool.label\"\n [attr.aria-pressed]=\"openDialogToolId === tool.id\"\n (onClick)=\"toggleDialogTool.emit(tool.id)\">\n <img *ngIf=\"tool.icon.src\" [src]=\"tool.icon.src\" style=\"width: 1rem\" [alt]=\"tool.label\"\n [class]=\"openDialogToolId === tool.id ? 'toolbar-icon-active' : 'toolbar-icon-dark'\" />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n </ng-container>\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 <!-- Spatial-omics controls. Only meaningful in the spatial mode, which is\n itself only offered while a dataset is loaded \u2014 so this button appears\n exactly when there is something for it to control. -->\n <p-button *ngIf=\"tools.specialTools && isSpatialMode\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Spatial omics (colour by column or gene, legend, selection, distributions)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openSpatialControls.emit()\">\n <img src=\"assets/plotting/spatial-omics.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, and for the\n spatial-omics modes: a 3D omics dataset publishes its reference volume as a z-stack\n image, and these modes draw observations, not one of its slices. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode && !isSpatialMode\">\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 || is3dRegions) && 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 *ngIf=\"!is3dRegions\" 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 <!-- Brush and polyline are absent from a 3D cloud: the brush paints in IMAGE\n pixels and there is no raster to sample, and an open polyline is a\n profile tool, not a closed region a selection can test against. -->\n <p-button *ngIf=\"!is3dRegions\" 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>Heatmap</b>, <b>Contour</b>, <b>Surface</b>, <b>Volume</b>,\n <b>Isosurface</b>, and \u2014 when a spatial-omics dataset is open \u2014\n <b>Spatial omics</b> and <b>Spatial omics 3D</b>.\n </p>\n <p>\n <b>Only the modes the data can actually support are offered</b>, so the list changes\n with what is loaded rather than showing entries that would come up blank:\n </p>\n <ul>\n <li><b>Contour</b>, <b>Volume</b> and <b>Isosurface</b> need a grayscale image; the\n two 3D ones also need a z-stack.</li>\n <li><b>Spatial omics</b> needs a published dataset, and <b>Spatial omics 3D</b> needs\n one whose coordinates carry a z.</li>\n <li>The pixel modes (<b>Image</b>, <b>Heatmap</b>, <b>Surface</b>) are withheld for a\n dataset that brings no tissue image \u2014 seqFISH records coordinates and no section, so\n there is nothing for them to draw.</li>\n </ul>\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 <ng-container *ngFor=\"let tool of dialogTools\">\n <li *ngIf=\"tool.help\" [innerHTML]=\"tool.help.body\"></li>\n </ng-container>\n </ul>\n\n <h3>Spatial omics</h3>\n <p>\n In <b>Spatial omics</b> mode each observation \u2014 a cell or a Visium spot \u2014 is drawn at\n its own coordinates, over the tissue image where the dataset registers onto one. The\n <img src=\"assets/plotting/spatial-omics.svg\" width=\"14\" height=\"14\" alt=\"Spatial omics\"/>\n panel colours them by a <b>column</b> (a cell type, a cluster) or by a <b>gene</b>, and\n the two are exclusive so what drives the colours is never ambiguous.\n </p>\n <ul>\n <li>\"<b>Select from ROIs</b>\": turns the regions you have drawn into a selection of\n observations. Everything else then follows that selection.</li>\n <li>\"<b>Point size</b>\" / \"<b>Opacity</b>\": how the markers are drawn, not what is\n selected.</li>\n </ul>\n\n <h3>Distribution charts</h3>\n <p>\n The <b>Distribution</b> section of the spatial-omics panel charts whatever the map is\n coloured by, so the two can never disagree. Each tab answers a different question, and\n the <i class=\"pi pi-question-circle\"></i> beside them explains the one on screen \u2014\n including what it must <em>not</em> be read for.\n </p>\n <ul>\n <li>\"<b>Counts</b>\": how many observations per category. A category is a label, not a\n magnitude, so a frequency is the only distribution it has.</li>\n <li>\"<b>Histogram</b>\" / \"<b>Violin</b>\" / \"<b>Box</b>\": how a continuous value is\n distributed \u2014 over everything, or split by a grouping column.</li>\n <li>\"<b>Heatmap</b>\": mean expression of the genes you pick within each group.\n Z-scored per gene by default, so a colour means \"above or below this gene's own\n average\" rather than \"highly expressed\".</li>\n <li>\"<b>Embedding</b>\": a UMAP, PCA or t-SNE over the same cells. <b>Lasso it to\n select on the map</b> \u2014 both views read the same selection. Only PCA labels its axes\n with a percentage, because only its axes explain a measurable share of the variance;\n UMAP and t-SNE coordinates are arbitrary, so distance between distant clusters\n should not be read.</li>\n <li>\"<b>Detach</b>\": moves the charts into their own window so they can sit beside the\n map instead of inside the panel. Switching tabs keeps the window where it is.</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 .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"] }]
313
319
  }], propDecorators: { imageInfo: [{
314
320
  type: Input
315
321
  }], plotTypeOptions: [{
@@ -360,6 +366,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
360
366
  type: Input
361
367
  }], contributedTools: [{
362
368
  type: Input
369
+ }], dialogTools: [{
370
+ type: Input
371
+ }], openDialogToolId: [{
372
+ type: Input
363
373
  }], toolModelIds: [{
364
374
  type: Input
365
375
  }], selectPlotType: [{
@@ -418,6 +428,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
418
428
  type: Output
419
429
  }], runTool: [{
420
430
  type: Output
431
+ }], toggleDialogTool: [{
432
+ type: Output
421
433
  }], toolModelChange: [{
422
434
  type: Output
423
435
  }], openToolParams: [{
@@ -431,4 +443,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
431
443
  }], vertexEraserRadiusChange: [{
432
444
  type: Output
433
445
  }] } });
434
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbGJhci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQWEsTUFBTSxFQUFpQixNQUFNLGVBQWUsQ0FBQztBQUlqRyxPQUFPLEVBQUUsUUFBUSxFQUFjLGlCQUFpQixFQUFFLFVBQVUsRUFBRSxrQkFBa0IsRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLHVCQUF1QixFQUFFLHVCQUF1QixFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3ZPLE9BQU8sRUFBeUIsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUd2RixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sY0FBYyxDQUFDOzs7Ozs7Ozs7Ozs7OztBQUUxQzs7Ozs7Ozs7Ozs7R0FXRztBQVFILE1BQU0sT0FBTyxnQkFBZ0I7SUFQN0I7UUFVRSw0RUFBNEU7UUFDbkUsb0JBQWUsR0FBcUIsRUFBRSxDQUFDO1FBQ2hELHdFQUF3RTtRQUMvRCxxQkFBZ0IsR0FBZSxRQUFRLENBQUMsS0FBSyxDQUFDO1FBQ3ZEOzs7Ozs7V0FNRztRQUNNLGlCQUFZLEdBQW9CLElBQUksQ0FBQztRQUM5Qzs0RUFDb0U7UUFDM0QsYUFBUSxHQUFhLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzlCLGFBQVEsR0FBRyxDQUFDLENBQUM7UUFDYixXQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ3BCLGtEQUFrRDtRQUN6QyxjQUFTLEdBQUcsSUFBSSxDQUFDO1FBQzFCOzs7OztXQUtHO1FBQ00sZ0JBQVcsR0FBRyxLQUFLLENBQUM7UUFDcEIsbUJBQWMsR0FB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LE1BQU07Z0JBRUcsZUFBZTtzQkFBeEIsTUFBTTtnQkFFRyxnQkFBZ0I7c0JBQXpCLE1BQU07Z0JBQ0csWUFBWTtzQkFBckIsTUFBTTtnQkFFRyxVQUFVO3NCQUFuQixNQUFNO2dCQUVHLFVBQVU7c0JBQW5CLE1BQU07Z0JBRUcsY0FBYztzQkFBdkIsTUFBTTtnQkFFRyxlQUFlO3NCQUF4QixNQUFNO2dCQUVHLE9BQU87c0JBQWhCLE1BQU07Z0JBRUcsZUFBZTtzQkFBeEIsTUFBTTtnQkFFRyxjQUFjO3NCQUF2QixNQUFNO2dCQUVHLGNBQWM7c0JBQXZCLE1BQU07Z0JBQ0cscUJBQXFCO3NCQUE5QixNQUFNO2dCQUNHLGVBQWU7c0JBQXhCLE1BQU07Z0JBQ0csd0JBQXdCO3NCQUFqQyxNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIElucHV0LCBPbkNoYW5nZXMsIE91dHB1dCwgU2ltcGxlQ2hhbmdlcyB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgTWVudUl0ZW0gfSBmcm9tICdwcmltZW5nL2FwaSc7XG5cbmltcG9ydCB7IElJbWFnZUluZm8gfSBmcm9tICcuLi9jb250cmFjdHMvaW1hZ2UuY29udHJhY3QnO1xuaW1wb3J0IHsgUGxvdFR5cGUsIFBsb3RUeXBlSWQsIGlzQnVpbHRpblBsb3RUeXBlLCBpc05hcGFyaTNkLCBpc05hcGFyaUlzb3N1cmZhY2UsIGlzTmFwYXJpU3VyZmFjZSwgaXNOYXBhcmlTY2F0dGVyLCBOQVBBUklfREVDSU1BVEVfT1BUSU9OUywgTkFQQVJJX0RFRkFVTFRfREVDSU1BVEUsIGlzU3BhdGlhbE9taWNzLCBpc1NwYXRpYWxPbWljczNkIH0gZnJvbSAnLi4vY29udHJhY3RzL3Bsb3QtdHlwZSc7XG5pbXBvcnQgeyBUb29sYmFyVG9vbFZpc2liaWxpdHksIEFMTF9UT09MQkFSX1RPT0xTIH0gZnJvbSAnLi4vY29udHJhY3RzL3Rvb2xiYXItY29uZmlnJztcbmltcG9ydCB7IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uIH0gZnJvbSAnLi4vY29udHJhY3RzL3Rvb2xiYXItdG9vbC5jb250cmFjdCc7XG5pbXBvcnQgeyBQbG90VHlwZU9wdGlvbiB9IGZyb20gJy4uL2NvbnRyYWN0cy9wbG90LXR5cGUtY29udHJpYnV0aW9uLmNvbnRyYWN0JztcbmltcG9ydCB7IE1PREVMX0lORk8gfSBmcm9tICcuL21vZGVsLWluZm8nO1xuXG4vKipcbiAqIFByZXNlbnRhdGlvbmFsIHRvb2xiYXIgZm9yIHRoZSBwbG90dGluZyB2aWV3cG9ydC5cbiAqXG4gKiBSZW5kZXJzIHRoZSBwbG90LXR5cGUgc2VsZWN0b3IsIGNvbG9ybWFwL0xVVCBjb250cm9scywgc3RhY2sgbmF2aWdhdGlvbixcbiAqIGltYWdlIGFjdGlvbnMgKGRvd25sb2FkIC8gYXV0b3NjYWxlIC8gcGlwZWxpbmUpLCB2aWV3cG9ydCB6b29tL3BhbiwgdGhlXG4gKiBTdXJmYWNlLTNEIGNhbWVyYSBjb250cm9scywgYW5kIHRoZSByZWdpb24tZHJhd2luZyB0b29scyDigJQgcGx1cyB0aGUgcGxvdHRpbmdcbiAqIGhlbHAgZGlhbG9nLiBJdCBvd25zIG5vIHJlbmRlcmluZyBvciB2aXN1YWxpemF0aW9uIHN0YXRlOiB0aGUgaG9zdFxuICogKFZpc3VhbGl6ZXJDb21wb25lbnQpIHN0YXlzIHRoZSBvcmNoZXN0cmF0b3IsIHBhc3Npbmcgc3RhdGUgaW4gdmlhIGBASW5wdXRgIGFuZFxuICogaGFuZGxpbmcgZXZlcnkgYWN0aW9uIHZpYSBgQE91dHB1dGAuIFRoZSBzYW1lIGFjdGlvbnMgYXJlIGFsc28gZHJpdmVuIGJ5IHRoZVxuICogZGlhZ3JhbSdzIHJpZ2h0LWNsaWNrIGNvbnRleHQgbWVudSBhbmQga2V5Ym9hcmQgc2hvcnRjdXRzLCBzbyBrZWVwaW5nIHRoZVxuICogaGFuZGxlcnMgaW4gdGhlIGhvc3QgYXZvaWRzIGR1cGxpY2F0aW5nIHRoYXQgbG9naWMuXG4gKi9cbkBDb21wb25lbnQoe1xuICAvLyBDYW5vbmljYWwgcHJlZml4ZWQgc2VsZWN0b3IgZmlyc3Q7IHRoZSB1bnByZWZpeGVkIG9yaWdpbmFsIGlzIGtlcHQgYXMgYW5cbiAgLy8gYWxpYXMgZm9yIG9uZSByZWxlYXNlIChwcmUtcHVibGljYXRpb24gYmFjay1jb21wYXQpLlxuICBzZWxlY3RvcjogJ3Bsb3R0aW5nLXRvb2xiYXInLFxuICB0ZW1wbGF0ZVVybDogJy4vdG9vbGJhci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3Rvb2xiYXIuY29tcG9uZW50LnNjc3MnXSxcbn0pXG5leHBvcnQgY2xhc3MgVG9vbGJhckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uQ2hhbmdlcyB7XG4gIC8qKiBDdXJyZW50IGltYWdlIChnYXRlcyB3aGljaCBjb250cm9sIGdyb3VwcyBhcmUgc2hvd24pLiAqL1xuICBASW5wdXQoKSBpbWFnZUluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQ7XG4gIC8qKiBQbG90IHR5cGVzIHRoZSBhY3RpdmUgYmFja2VuZCBhZHZlcnRpc2VzLCB0aGVuIGFueSBjb250cmlidXRlZCBtb2Rlcy4gKi9cbiAgQElucHV0KCkgcGxvdFR5cGVPcHRpb25zOiBQbG90VHlwZU9wdGlvbltdID0gW107XG4gIC8qKiBUaGUgc2VsZWN0b3IncyB2YWx1ZTogYSBidWlsdC1pbiB0eXBlIG9yIGEgY29udHJpYnV0ZWQgbW9kZSdzIGlkLiAqL1xuICBASW5wdXQoKSBzZWxlY3RlZFBsb3RUeXBlOiBQbG90VHlwZUlkID0gUGxvdFR5cGUuSU1BR0U7XG4gIC8qKlxuICAgKiBUaGUgYnVpbHQtaW4gdHlwZSB0aGF0IHJlbmRlcnMge0BsaW5rIHNlbGVjdGVkUGxvdFR5cGV9IOKAlCBpdHMgYGJhc2VUeXBlYFxuICAgKiBmb3IgYSBjb250cmlidXRlZCBtb2RlLiBFdmVyeSB0b29sL2NvbnRyb2wgZGVjaXNpb24gYmVsb3cgcmVhZHNcbiAgICoge0BsaW5rIGVmZmVjdGl2ZVBsb3RUeXBlfSwgc28gYSBjb250cmlidXRlZCBtb2RlIGdldHMgZXhhY3RseSBpdHMgYmFzZVxuICAgKiB0eXBlJ3MgdG9vbGJhci4gV2hlbiB1bmJvdW5kLCBhIGJ1aWx0LWluIHNlbGVjdGlvbiBpcyBpdHMgb3duIGJhc2UgYW5kXG4gICAqIGFueXRoaW5nIGVsc2UgaXMgdHJlYXRlZCBhcyB0aGUgZGVmYXVsdCBJbWFnZSB2aWV3LlxuICAgKi9cbiAgQElucHV0KCkgYmFzZVBsb3RUeXBlOiBQbG90VHlwZSB8IG51bGwgPSBudWxsO1xuICAvKiogSXNvc3VyZmFjZSBiYW5kIGFzIGEgMOKAkzI1NSBzbGlkZXIgcG9zaXRpb24gKG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUncyByZWFsXG4gICAqICBpbnRlbnNpdHkgcmFuZ2UgYnkgdGhlIHJlbmRlcmVyKS4gRGVmYXVsdHMgdG8gdGhlIGZ1bGwgcmFuZ2UuICovXG4gIEBJbnB1dCgpIGlzb1JhbmdlOiBudW1iZXJbXSA9IFswLCAyNTVdO1xuICBASW5wdXQoKSBtYXhJbmRleCA9IDA7XG4gIEBJbnB1dCgpIHpJbmRleCA9IDA7XG4gIC8qKiBUcnVlIGZvciBhbnkgMkQgKG5vbiBTdXJmYWNlLTNEKSBwbG90IHR5cGUuICovXG4gIEBJbnB1dCgpIGlzSGVhdG1hcCA9IHRydWU7XG4gIC8qKlxuICAgKiBSZWdpb24gdG9vbHMgYXBwbHkgZXZlbiB0aG91Z2ggdGhpcyBpcyBhIDNEIHZpZXcsIGJlY2F1c2UgdGhleSBkcmF3IGFcbiAgICogU0NSRUVOLVNQQUNFIGxhc3NvICh0aGUgM0Qgc3BhdGlhbCBjbG91ZCkuIFNlcGFyYXRlIGZyb20ge0BsaW5rIGlzSGVhdG1hcH0sXG4gICAqIHdoaWNoIG1lYW5zIFwiMkQgdmlld1wiIGFuZCBhbHNvIGRyaXZlcyB0aGUgem9vbSB0b29scyBhbmQgdGhlIDNEIGNhbWVyYVxuICAgKiBjb250cm9scyDigJQgZmxpcHBpbmcgdGhhdCB3b3VsZCBzaG93IDJEIHBhbi96b29tIGFuZCBoaWRlIHRoZSBvcmJpdCBjb250cm9scy5cbiAgICovXG4gIEBJbnB1dCgpIGlzM2RSZWdpb25zID0gZmFsc2U7XG4gIEBJbnB1dCgpIGFjdGl2ZURyYWdNb2RlOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgQElucHV0KCkgYWN0aXZlU3VyZmFjZTNkTW9kZSA9ICd0dXJudGFibGUnO1xuICAvKiogV2hldGhlciB0aGUgbmFwYXJpIDNEIGF4ZXMvc2NhbGUgZ2l6bW8gaXMgY3VycmVudGx5IHNob3duIChkcml2ZXMgdGhlIHRvZ2dsZSdzIGxvb2spLiAqL1xuICBASW5wdXQoKSBheGVzVmlzaWJsZSA9IHRydWU7XG4gIEBJbnB1dCgpIHdpcmVmcmFtZUFjdGl2ZSA9IGZhbHNlO1xuICAvKiogQWN0aXZlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKDEgPSBGdWxsIOKApiA4ID0g4oWbOyBkZWZhdWx0IMK9KS4gKi9cbiAgQElucHV0KCkgcmVzb2x1dGlvblNjYWxlID0gTkFQQVJJX0RFRkFVTFRfREVDSU1BVEU7XG4gIC8qKiBEZWNpbWF0ZS1mYWN0b3Igb3B0aW9ucyBmb3IgdGhlIFJlc29sdXRpb24gZHJvcGRvd24uICovXG4gIHJlYWRvbmx5IGRlY2ltYXRlT3B0aW9ucyA9IE5BUEFSSV9ERUNJTUFURV9PUFRJT05TO1xuICBASW5wdXQoKSB3YW5kU2Vuc2l0aXZpdHkgPSAyLjA7XG4gIEBJbnB1dCgpIGJydXNoU2l6ZSA9IDQwO1xuICBASW5wdXQoKSB2ZXJ0ZXhFcmFzZXJSYWRpdXMgPSAyMDtcbiAgLyoqIFdoZXRoZXIgYSByZWdpb24gYWN0aW9uIGlzIGF2YWlsYWJsZSB0byB1bmRvIChqaXQtdWkjODUpLiBHcmV5cyBvdXQgdGhlXG4gICAqICBVbmRvIGJ1dHRvbiB3aGVuIGZhbHNlIChub3RoaW5nIGRvbmUgeWV0LCBvciBhbGwgc3RlcHMgYWxyZWFkeSB1bmRvbmUpLiAqL1xuICBASW5wdXQoKSBjYW5VbmRvID0gZmFsc2U7XG4gIC8qKiBXaGV0aGVyIGFuIHVuZG9uZSByZWdpb24gYWN0aW9uIGlzIGF2YWlsYWJsZSB0byByZWRvIChqaXQtdWkjODUpLiBHcmV5c1xuICAgKiAgb3V0IHRoZSBSZWRvIGJ1dHRvbiB3aGVuIGZhbHNlLiAqL1xuICBASW5wdXQoKSBjYW5SZWRvID0gZmFsc2U7XG4gIEBJbnB1dCgpIHN0YWNrT3B0aW9uczogeyBuYW1lOiBzdHJpbmc7IHZhbDogc3RyaW5nIH1bXSA9XG4gICAgW3sgbmFtZTogJ1NpbmdsZSBpbWFnZScsIHZhbDogJ2ZhbHNlJyB9LCB7IG5hbWU6ICdTdGFjaycsIHZhbDogJ3RydWUnIH1dO1xuICAvKiogV2hpY2ggdG9vbGJhciBncm91cHMgdG8gc2hvdy4gRGVmYXVsdHMgdG8gdGhlIGZ1bGwgdG9vbGJhcjsgdGhlIGhvc3QgZm9yd2FyZHNcbiAgICogIHRoZSBjb25zdW1lcidzIGNob2ljZSAoZS5nLiB0aGUgcGlwZWxpbmUgc2hvd3Mgb25seSB6b29tICsgcmVnaW9uIHRvb2xzKS4gKi9cbiAgQElucHV0KCkgdG9vbHM6IFJlcXVpcmVkPFRvb2xiYXJUb29sVmlzaWJpbGl0eT4gPSBBTExfVE9PTEJBUl9UT09MUztcbiAgLyoqIEN1cnJlbnQgaW1hZ2Utc21vb3RoaW5nIHN0YXRlIChkcml2ZXMgdGhlIFNtb290aGVuIHRvZ2dsZSBidXR0b24gbG9vaykuXG4gICAqICBgZmFsc2VgID0gbmVhcmVzdC1uZWlnaGJvdXIgKGNyaXNwIHJhdyBwaXhlbHMpLCB0aGUgZGVmYXVsdC4gKi9cbiAgQElucHV0KCkgaW1hZ2VTbW9vdGhpbmdFbmFibGVkID0gZmFsc2U7XG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIG9wdGlvbnMgKyBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzkwIFAxKS4gKi9cbiAgQElucHV0KCkgc2FtTW9kZWxzOiB7IGlkOiBzdHJpbmc7IGxhYmVsOiBzdHJpbmcgfVtdID0gW107XG4gIEBJbnB1dCgpIHNhbU1vZGVsSWQgPSAnJztcbiAgLyoqXG4gICAqIFRvb2xzIGNvbnRyaWJ1dGVkIHRocm91Z2gge0BsaW5rIFRPT0xCQVJfVE9PTFN9LCBhbHJlYWR5IGZpbHRlcmVkIHRvIHRoZVxuICAgKiBvbmVzIHdpdGggbW9kZWxzIGFuZCBzb3J0ZWQuIFRoZSBob3N0IHJlc29sdmVzIHRoZW0gc28gaXQgY2FuIG93biB0aGVcbiAgICogcGVyLXRvb2wgcGFyYW1ldGVyIHN0YXRlOyB0aGlzIGNvbXBvbmVudCBvbmx5IGRyYXdzIHRoZW0uXG4gICAqL1xuICBASW5wdXQoKSBjb250cmlidXRlZFRvb2xzOiBUb29sYmFyVG9vbENvbnRyaWJ1dGlvbltdID0gW107XG4gIC8qKiBBY3RpdmUgY2hlY2twb2ludCBwZXIgY29udHJpYnV0ZWQgdG9vbCwga2V5ZWQgYnkgdG9vbCBpZC4gKi9cbiAgQElucHV0KCkgdG9vbE1vZGVsSWRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgQE91dHB1dCgpIHNlbGVjdFBsb3RUeXBlID0gbmV3IEV2ZW50RW1pdHRlcjxQbG90VHlwZUlkPigpO1xuICAvKiogSW50ZW5zaXR5IChMSU5FKSBtb2RlOiBhZGQgYW5vdGhlciBjb2xvcmVkIGxpbmUgUk9JICsgaW5zZXQgdHJhY2UuICovXG4gIEBPdXRwdXQoKSBhZGRQcm9maWxlTGluZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFRvZ2dsZSBpbWFnZSBzbW9vdGhpbmcgKGJpbGluZWFyKSB2cyBuZWFyZXN0LW5laWdoYm91ciAoY3Jpc3AgcGl4ZWxzKS4gKi9cbiAgQE91dHB1dCgpIHRvZ2dsZUltYWdlU21vb3RoaW5nID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvLyBQcmltZU5HIHNsaWRlci9pbnB1dE51bWJlciBjaGFuZ2UgZXZlbnRzIGNhcnJ5IG9wdGlvbmFsIHZhbHVlcywgbWlycm9yZWQgYnlcbiAgLy8gdGhlIGhvc3QgaGFuZGxlcnMgKHdoaWNoIGFjY2VwdCBgfCB1bmRlZmluZWRgKS5cbiAgQE91dHB1dCgpIGlzb1JhbmdlQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXJbXSB8IHVuZGVmaW5lZD4oKTtcbiAgLyoqIE9wZW4gdGhlIENoYW5uZWxzICYgSGlzdG9ncmFtIGRpYWxvZyAoYnJpZ2h0bmVzcy9jb250cmFzdCwgY29sb3JtYXAsIOKApikuICovXG4gIEBPdXRwdXQoKSBvcGVuQ2hhbm5lbEhpc3RvZ3JhbSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIE9wZW4gdGhlIHNwYXRpYWwtb21pY3MgY29udHJvbHMgKGNvbG91ci1ieSwgbGVnZW5kLCBwb2ludCBkaXNwbGF5KS4gKi9cbiAgQE91dHB1dCgpIG9wZW5TcGF0aWFsQ29udHJvbHMgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSBvcGVuUmVnaW9uRWRpdG9yID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgc2VsZWN0U3RhY2tPcHRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHsgbmFtZTogc3RyaW5nOyB2YWw6IHN0cmluZyB9PigpO1xuICBAT3V0cHV0KCkgelNjcnViID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIEBPdXRwdXQoKSB6U2xpZGUgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcbiAgQE91dHB1dCgpIHpJbmRleElucHV0ID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG4gIEBPdXRwdXQoKSByZWxvYWRBbmRQbG90ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgZG93bmxvYWRJbWFnZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIGF1dG9zY2FsZUltYWdlID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgdG9nZ2xlRHJhZ01vZGUgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgQE91dHB1dCgpIHpvb21JbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIHpvb21PdXQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSB0b2dnbGVTdXJmYWNlM2RNb2RlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIEBPdXRwdXQoKSByZXNldFN1cmZhY2VDYW1lcmEgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBUb2dnbGUgdGhlIG5hcGFyaSAzRCBjb29yZGluYXRlLWF4ZXMgLyBzY2FsZSBnaXptbyBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVBeGVzID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogVG9nZ2xlIHRoZSBuYXBhcmkgc3VyZmFjZSB3aXJlZnJhbWUgKGVkZ2VzKSBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVXaXJlZnJhbWUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBDaGFuZ2UgdGhlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKHJlbG9hZHMgYXQgdGhlIG5ldyByZXNvbHV0aW9uKS4gKi9cbiAgQE91dHB1dCgpIHNlbGVjdFJlc29sdXRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlcj4oKTtcbiAgQE91dHB1dCgpIGRlbGV0ZVJlZ2lvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFVuZG8gdGhlIGxhc3QgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gKi9cbiAgQE91dHB1dCgpIHVuZG9SZWdpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBSZWRvIHRoZSBsYXN0IHVuZG9uZSByZWdpb24gYWN0aW9uIChqaXQtdWkjODUpLiAqL1xuICBAT3V0cHV0KCkgcmVkb1JlZ2lvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biBib3gtcHJvbXB0ZWQgU0FNIHNlZ21lbnRhdGlvbiBvbiB0aGUgZHJhd24gcmVjdGFuZ2xlcyAoaml0LXVpIzkwKS4gKi9cbiAgQE91dHB1dCgpIHNlZ21lbnRSZWdpb25zID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogUnVuIGNlbGxwb3NlLVNBTSAoYXV0bykgaW5zaWRlIGVhY2ggZHJhd24gcmVjdGFuZ2xlJ3MgY3JvcCAoaml0LXVpIzkwKS4gKi9cbiAgQE91dHB1dCgpIHNlZ21lbnRDZWxscG9zZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biBhIGNvbnRyaWJ1dGVkIHRvb2wgb3ZlciB0aGUgY3VycmVudCB2aWV3LCBieSB0b29sIGlkLiAqL1xuICBAT3V0cHV0KCkgcnVuVG9vbCA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuICAvKiogUGljayBhIGNvbnRyaWJ1dGVkIHRvb2wncyBjaGVja3BvaW50LiAqL1xuICBAT3V0cHV0KCkgdG9vbE1vZGVsQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjx7IHRvb2xJZDogc3RyaW5nOyBtb2RlbElkOiBzdHJpbmcgfT4oKTtcbiAgLyoqIE9wZW4gYSBjb250cmlidXRlZCB0b29sJ3MgcGFyYW1ldGVyIGRpYWxvZywgYnkgdG9vbCBpZC4gKi9cbiAgQE91dHB1dCgpIG9wZW5Ub29sUGFyYW1zID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIChqaXQtdWkjOTAgUDEpLiAqL1xuICBAT3V0cHV0KCkgc2FtTW9kZWxDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgQE91dHB1dCgpIHdhbmRTZW5zaXRpdml0eUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyIHwgdW5kZWZpbmVkPigpO1xuICBAT3V0cHV0KCkgYnJ1c2hTaXplQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIEBPdXRwdXQoKSB2ZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcblxuICAvKiogU2xpZGVycycgYm91bmRzIOKAlCB0b29sYmFyIFVJIGNvbnN0YW50cyAodGhlIHZhbHVlcyBhcmUgaG9zdC1vd25lZCkuICovXG4gIHJlYWRvbmx5IHdhbmRTZW5zaXRpdml0eU1pbiA9IDAuNTtcbiAgcmVhZG9ubHkgd2FuZFNlbnNpdGl2aXR5TWF4ID0gMTAuMDtcbiAgcmVhZG9ubHkgd2FuZFNlbnNpdGl2aXR5U3RlcCA9IDAuMTtcbiAgcmVhZG9ubHkgYnJ1c2hTaXplTWluID0gNTtcbiAgcmVhZG9ubHkgYnJ1c2hTaXplTWF4ID0gMzAwO1xuICByZWFkb25seSBicnVzaFNpemVTdGVwID0gNTtcbiAgcmVhZG9ubHkgdmVydGV4RXJhc2VyUmFkaXVzTWluID0gNTtcbiAgcmVhZG9ubHkgdmVydGV4RXJhc2VyUmFkaXVzTWF4ID0gMzAwO1xuICByZWFkb25seSB2ZXJ0ZXhFcmFzZXJSYWRpdXNTdGVwID0gNTtcbiAgcmVhZG9ubHkgaXNvVmFsdWVNaW4gPSAwO1xuICByZWFkb25seSBpc29WYWx1ZU1heCA9IDI1NTtcbiAgcmVhZG9ubHkgaXNvVmFsdWVTdGVwID0gMTtcblxuICBkaXNwbGF5SGVscERpYWxvZyA9IGZhbHNlO1xuXG4gIC8qKiBNb2RlbCBwaWNrZXIgZm9yIHRoZSBTZWdtZW50IGJ1dHRvbidzIGRyb3Bkb3duIG1lbnUuIFRoZSBhY3RpdmUgbW9kZWxcbiAgICogIChgc2FtTW9kZWxJZGApIGlzIG1hcmtlZCB3aXRoIGEgY2hlY2s7IHNlbGVjdGluZyBhbiBpdGVtIGVtaXRzXG4gICAqICBgc2FtTW9kZWxDaGFuZ2VgIChqaXQtdWkjOTAgUDEpLlxuICAgKlxuICAgKiAgSGVsZCBhcyBhIHN0YWJsZSBhcnJheSAocmVidWlsdCBvbmx5IHdoZW4gYHNhbU1vZGVsc2AvYHNhbU1vZGVsSWRgIGNoYW5nZSksXG4gICAqICBOT1QgYSBnZXR0ZXI6IGEgZ2V0dGVyIHJldHVybnMgYSBmcmVzaCBhcnJheSB3aXRoIG5ldyBgY29tbWFuZGAgY2xvc3VyZXMgb25cbiAgICogIGV2ZXJ5IGNoYW5nZS1kZXRlY3Rpb24gdGljaywgd2hpY2ggbWFrZXMgdGhlIGJvdW5kIGBwLW1lbnVgIG92ZXJsYXkgcmVidWlsZFxuICAgKiAgaXRzIERPTSBtaWQtaW50ZXJhY3Rpb24gYW5kIHN3YWxsb3cgdGhlIGNsaWNrIG9uIGEgbWVudSBpdGVtLlxuICAgKlxuICAgKiAgYHRvb2x0aXBgIGNhcnJpZXMgdGhlIG1vZGVsJ3MgZGVzY3JpcHRpb24gZnJvbSB7QGxpbmsgTU9ERUxfSU5GT307IHRoZVxuICAgKiAgbWVudXMnIHNoYXJlZCBpdGVtIHRlbXBsYXRlIHR1cm5zIGl0IGludG8gdGhlIGhvdmVyIGluZm8gaWNvbi4gcC1tZW51J3Mgb3duXG4gICAqICByZW5kZXJpbmcgaWdub3JlcyB0aGUgZmllbGQgKGl0IHJlYWRzIGBpdGVtLnRpdGxlYCksIHNvIGl0IGlzIGZyZWUgdG8gdXNlLiAqL1xuICBzYW1NZW51SXRlbXM6IE1lbnVJdGVtW10gPSBbXTtcbiAgLyoqXG4gICAqIE1vZGVsIG1lbnVzIGZvciB0aGUgY29udHJpYnV0ZWQgdG9vbHMsIGtleWVkIGJ5IHRvb2wgaWQuIFNhbWUgc3RhYmxlLWFycmF5XG4gICAqIHJlYXNvbmluZyBhcyB7QGxpbmsgc2FtTWVudUl0ZW1zfSDigJQgYW5kIGl0IGlzIHdoeSB0aGlzIGlzIGEgbWFwIHJlYnVpbHQgaW5cbiAgICogYG5nT25DaGFuZ2VzYCByYXRoZXIgdGhhbiBhIG1ldGhvZCBjYWxsZWQgZnJvbSB0aGUgdGVtcGxhdGUsIHdoaWNoIHdvdWxkXG4gICAqIGhhbmQgYHAtbWVudWAgYSBuZXcgYXJyYXkgZXZlcnkgdGljay5cbiAgICpcbiAgICogQSBjb250cmlidXRlZCB0b29sIHN1cHBsaWVzIGl0cyBvd24gcGVyLW1vZGVsIGRlc2NyaXB0aW9uLCBzbyB1bmxpa2UgdGhlXG4gICAqIFNBTSBtZW51IHRoZXNlIGRvIG5vdCBjb25zdWx0IHtAbGluayBNT0RFTF9JTkZPfTogdGhpcyBsaWJyYXJ5IGhhcyBub1xuICAgKiBjb3B5IGZvciBjaGVja3BvaW50cyBpdCBkb2VzIG5vdCBrbm93IGFib3V0LlxuICAgKi9cbiAgdG9vbE1lbnVJdGVtczogUmVjb3JkPHN0cmluZywgTWVudUl0ZW1bXT4gPSB7fTtcblxuICAvKiogUmVidWlsZCB0aGUgU0FNIG1vZGVsIG1lbnUgd2hlbiB0aGUgbW9kZWwgbGlzdCBvciBhY3RpdmUgc2VsZWN0aW9uXG4gICAqICBjaGFuZ2VzIChrZWVwcyB0aGUgYXJyYXkgcmVmZXJlbmNlIHN0YWJsZSBhY3Jvc3Mgb3RoZXIgQ0QgdGlja3MpLiAqL1xuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgaWYgKGNoYW5nZXNbJ3NhbU1vZGVscyddIHx8IGNoYW5nZXNbJ3NhbU1vZGVsSWQnXSkge1xuICAgICAgdGhpcy5zYW1NZW51SXRlbXMgPSB0aGlzLnNhbU1vZGVscy5tYXAoKG0pID0+ICh7XG4gICAgICAgIGxhYmVsOiBtLmxhYmVsLFxuICAgICAgICBpY29uOiBtLmlkID09PSB0aGlzLnNhbU1vZGVsSWQgPyAncGkgcGktY2hlY2snIDogJ3BpIHBpLWZ3JyxcbiAgICAgICAgdG9vbHRpcDogTU9ERUxfSU5GT1ttLmlkXSxcbiAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy5zYW1Nb2RlbENoYW5nZS5lbWl0KG0uaWQpLFxuICAgICAgfSkpO1xuICAgIH1cbiAgICBpZiAoY2hhbmdlc1snY29udHJpYnV0ZWRUb29scyddIHx8IGNoYW5nZXNbJ3Rvb2xNb2RlbElkcyddKSB7XG4gICAgICB0aGlzLnRvb2xNZW51SXRlbXMgPSB7fTtcbiAgICAgIGZvciAoY29uc3QgdG9vbCBvZiB0aGlzLmNvbnRyaWJ1dGVkVG9vbHMpIHtcbiAgICAgICAgY29uc3QgYWN0aXZlID0gdGhpcy50b29sTW9kZWxJZHNbdG9vbC5pZF0gPz8gdG9vbC5kZWZhdWx0TW9kZWxJZCgpO1xuICAgICAgICB0aGlzLnRvb2xNZW51SXRlbXNbdG9vbC5pZF0gPSB0b29sLm1vZGVscygpLm1hcCgobSkgPT4gKHtcbiAgICAgICAgICBsYWJlbDogbS5sYWJlbCxcbiAgICAgICAgICBpY29uOiBtLmlkID09PSBhY3RpdmUgPyAncGkgcGktY2hlY2snIDogJ3BpIHBpLWZ3JyxcbiAgICAgICAgICB0b29sdGlwOiBtLmluZm8sXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b29sTW9kZWxDaGFuZ2UuZW1pdCh7IHRvb2xJZDogdG9vbC5pZCwgbW9kZWxJZDogbS5pZCB9KSxcbiAgICAgICAgfSkpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTdHJpcCBtYXJrdXAgZnJvbSBhIG1vZGVsIGRlc2NyaXB0aW9uIHNvIGl0IGNhbiBiZSByZWFkIGFsb3VkLlxuICAgKlxuICAgKiBUaGUgY29weSBpbiBNT0RFTF9JTkZPIGFuZCBpbiBhIGNvbnRyaWJ1dGlvbidzIGBUb29sTW9kZWxPcHRpb24uaW5mb2AgaXNcbiAgICogd3JpdHRlbiBmb3IgYSB2aXN1YWwgdG9vbHRpcCByZW5kZXJlZCB3aXRoIGBbZXNjYXBlXT1cImZhbHNlXCJgLCBzbyBpdCBjYXJyaWVzXG4gICAqIGA8Yj5gIGFuZCBgPGJyPmAuIFBhc3NlZCB0byBgYXJpYS1sYWJlbGAgdmVyYmF0aW0gYSBzY3JlZW4gcmVhZGVyIGFubm91bmNlc1xuICAgKiB0aGUgdGFncywgc28gdGhleSBhcmUgcmVtb3ZlZCBhbmQgYDxicj5gIGJlY29tZXMgYSBzZW50ZW5jZSBicmVhay4gRW50aXRpZXNcbiAgICogdXNlZCBpbiB0aGF0IGNvcHkgKGAmbmJzcDtgLCBgJnRpbWVzO2AsIGAmYW1wO2ApIGFyZSBkZWNvZGVkIGZvciB0aGUgc2FtZVxuICAgKiByZWFzb24uXG4gICAqL1xuICBwbGFpblRleHQoaHRtbDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gKGh0bWwgPz8gJycpXG4gICAgICAucmVwbGFjZSgvPGJyXFxzKlxcLz8+L2dpLCAnLiAnKVxuICAgICAgLnJlcGxhY2UoLzxbXj5dKz4vZywgJycpXG4gICAgICAucmVwbGFjZSgvJm5ic3A7L2csICcgJylcbiAgICAgIC5yZXBsYWNlKC8mdGltZXM7L2csICd4JylcbiAgICAgIC5yZXBsYWNlKC8mYW1wOy9nLCAnJicpXG4gICAgICAucmVwbGFjZSgvXFxzKy9nLCAnICcpXG4gICAgICAudHJpbSgpO1xuICB9XG5cbiAgLyoqIENvbnRyaWJ1dGVkIHRvb2wgbmFtZXMgZm9yIHRoZSBoZWxwIGRpYWxvZydzIHByb21wdGVkL25vLXByb21wdCBjb250cmFzdCxcbiAgICogIGUuZy4gXCJZT0xPLCBSZXRpbmFsIGxheWVyc1wiLiBFbXB0eSB3aGVuIG5vdGhpbmcgaXMgcmVnaXN0ZXJlZCwgd2hpY2ggaXNcbiAgICogIHdoeSB0aGUgc2VudGVuY2UgdGhhdCB1c2VzIGl0IGlzIGl0c2VsZiBjb25kaXRpb25hbC4gKi9cbiAgZ2V0IGNvbnRyaWJ1dGVkVG9vbE5hbWVzKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuY29udHJpYnV0ZWRUb29scy5tYXAoKHQpID0+IHQubGFiZWwpLmpvaW4oJywgJyk7XG4gIH1cblxuICAvKiogS2VlcHMgZWFjaCBjb250cmlidXRlZCB0b29sJ3MgYHAtbWVudWAgb3ZlcmxheSBhbGl2ZSBhY3Jvc3MgQ0QgdGlja3Mg4oCUXG4gICAqICByZS1jcmVhdGluZyBpdCBtaWQtaW50ZXJhY3Rpb24gc3dhbGxvd3MgdGhlIGNsaWNrIG9uIGEgbWVudSBpdGVtLiAqL1xuICB0cmFja1Rvb2xCeUlkKF9pbmRleDogbnVtYmVyLCB0b29sOiBUb29sYmFyVG9vbENvbnRyaWJ1dGlvbik6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRvb2wuaWQ7XG4gIH1cblxuICAvKiogVGhlIGJ1aWx0LWluIHR5cGUgd2hvc2UgdG9vbHMgYW5kIGNvbnRyb2xzIGFwcGx5IHRvIHRoZSBjdXJyZW50IHNlbGVjdGlvbi4gKi9cbiAgZ2V0IGVmZmVjdGl2ZVBsb3RUeXBlKCk6IFBsb3RUeXBlIHtcbiAgICBpZiAodGhpcy5iYXNlUGxvdFR5cGUpIHJldHVybiB0aGlzLmJhc2VQbG90VHlwZTtcbiAgICByZXR1cm4gaXNCdWlsdGluUGxvdFR5cGUodGhpcy5zZWxlY3RlZFBsb3RUeXBlKSA/IHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA6IFBsb3RUeXBlLklNQUdFO1xuICB9XG5cbiAgLyoqIFRoZSBJbWFnZSBwbG90IHR5cGUgcmVuZGVycyBhcyBhIG5hdGl2ZWx5IHBhbi96b29tLWFibGUgcmFzdGVyLCBzbyB0aGVcbiAgICogIGdlbmVyaWMgem9vbS9wYW4gdG9vbHMgYXJlIGhpZGRlbi4gKi9cbiAgZ2V0IGlzSW1hZ2VWaWV3KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRTtcbiAgfVxuXG4gIC8qKiBFaXRoZXIgc3BhdGlhbC1vbWljcyBtb2RlIGlzIGFjdGl2ZSwgc28gaXRzIGNvbnRyb2xzIGFyZSB3b3J0aCBvZmZlcmluZy5cbiAgICogIFRoZSAzRCBjbG91ZCBuZWVkcyB0aGUgcGFuZWwgTU9SRSB0aGFuIHRoZSAyRCB2aWV3IGRvZXMsIG5vdCBsZXNzOiBjb2xvdXJpbmcsXG4gICAqICB0aGUgbGVnZW5kIGFuZCBjYXRlZ29yeSBzaG93L2hpZGUgYXJlIHRoZSBvbmx5IHdheSB0byBtYWtlIHNlbnNlIG9mIGFcbiAgICogIG1pbGxpb24gb3ZlcmxhcHBpbmcgcG9pbnRzLiAqL1xuICBnZXQgaXNTcGF0aWFsTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gaXNTcGF0aWFsT21pY3ModGhpcy5lZmZlY3RpdmVQbG90VHlwZSkgfHwgaXNTcGF0aWFsT21pY3MzZCh0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBBIHBsb3QtdHlwZSBpY29uIGlzIGEgUHJpbWVORyBmb250IGdseXBoIChlLmcuIGBwaSBwaS1pbWFnZWApIHJhdGhlciB0aGFuIGFuXG4gICAqICBTVkcgYXNzZXQgcGF0aCDigJQgZHJpdmVzIHdoaWNoIGVsZW1lbnQgdGhlIHNlbGVjdG9yIGl0ZW0gdGVtcGxhdGUgcmVuZGVycy4gKi9cbiAgaXNQaUljb24oaWNvbjogc3RyaW5nIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICEhaWNvbiAmJiBpY29uLnN0YXJ0c1dpdGgoJ3BpICcpO1xuICB9XG5cbiAgLyoqIEJhY2tlbmRzIHdpdGggYSB2ZXJ0ZXgtZWRpdGluZyByZWdpb24gb3ZlcmxheTogT1NEIEltYWdlIGFuZCBuYXBhcmktanMgV2ViR1BVIGltYWdlXG4gICAqICAoaml0LXVpIzEwMikuIEdhdGVzIHRoZSBwb2x5Z29uIC8gYWRkLXZlcnRleCAvIGRlbGV0ZS12ZXJ0ZXggLyBCw6l6aWVyLWNvbnZlcnQgdG9vbHMuICovXG4gIGdldCBzdXBwb3J0c1JlZ2lvblZlcnRleFRvb2xzKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRSB8fFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBQbG90IHR5cGVzIHRoYXQgc2NydWIgYSB6LXN0YWNrIGxpdmUgKHRoZSByZW5kZXJlciBzd2FwcyB0aGUgc2xpY2UgaW4gcGxhY2UpOiB0aGUgT1NEIEltYWdlXG4gICAqICB2aWV3LCB0aGUgbmFwYXJpLWpzIFdlYkdQVSBpbWFnZSwgdGhlIG5hcGFyaS1qcyBzdXJmYWNlIOKAlCB3aGljaCByZWJ1aWxkcyB0aGUgaGVpZ2h0IGZpZWxkXG4gICAqICBmcm9tIHRoZSBwaWNrZWQgc2xpY2UgKGppdC11aSMxMDIpIOKAlCBhbmQgdGhlIDJEIHNwYXRpYWwtb21pY3Mgdmlldywgd2hlcmUgYSAzRCBkYXRhc2V0J3NcbiAgICogIHJlZ2lzdGVyZWQgdm9sdW1lIGlzIHRoZSBpbWFnZSBhbmQgdGhlIHNsaWRlciBwaWNrcyB0aGUgc2VjdGlvbiB3aG9zZSBvYnNlcnZhdGlvbnMgYXJlXG4gICAqICBkcmF3bi4gRHJpdmVzIHRoZSBwZXItc2xpY2Ugc2xpZGVyLiAqL1xuICBnZXQgc2hvd3NMaXZlU2xpY2VTY3J1YmJlcigpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0UgfHxcbiAgICAgIHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLk5BUEFSSV9JTUFHRSB8fFxuICAgICAgaXNOYXBhcmlTdXJmYWNlKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpIHx8XG4gICAgICBpc05hcGFyaVNjYXR0ZXIodGhpcy5lZmZlY3RpdmVQbG90VHlwZSkgfHxcbiAgICAgIGlzU3BhdGlhbE9taWNzKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBBbnkgbmFwYXJpLWpzIFdlYkdQVSBwbG90IHR5cGUuIFRoZSBzaW5nbGUtaW1hZ2Uvc3RhY2sgdG9nZ2xlICsgc2xpY2UtbnVtYmVyIGZpZWxkIGFyZVxuICAgKiAgUGxvdGx5LW9ubHkgc3RhY2sgY29udHJvbHMsIHNvIHRoZXkncmUgaGlkZGVuIGZvciBuYXBhcmkgKGppdC11aSMxMDIpLiAqL1xuICBnZXQgaXNOYXBhcmlNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5OQVBBUklfSU1BR0UgfHxcbiAgICAgIGlzTmFwYXJpU2NhdHRlcih0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKSB8fFxuICAgICAgaXNOYXBhcmkzZCh0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKVxuICAgICk7XG4gIH1cblxuICAvKiogSVNPU1VSRkFDRSAoUGxvdGx5IG9yIG5hcGFyaS1qcyBXZWJHUFUsIGVpdGhlciByZXNvbHV0aW9uKSBzaG93cyB0aGUgaXNvdmFsdWUgcmFuZ2Ugc2xpZGVyLiAqL1xuICBnZXQgaXNJc29zdXJmYWNlTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSVNPU1VSRkFDRSB8fCBpc05hcGFyaUlzb3N1cmZhY2UodGhpcy5lZmZlY3RpdmVQbG90VHlwZSlcbiAgICApO1xuICB9XG5cbiAgLyoqIFRoZSBuYXBhcmktanMgV2ViR1BVIHN1cmZhY2Ug4oCUIHNob3dzIHRoZSB3aXJlZnJhbWUgdG9nZ2xlLiAqL1xuICBnZXQgaXNOYXBhcmlTdXJmYWNlTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gaXNOYXBhcmlTdXJmYWNlKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpO1xuICB9XG5cbiAgLyoqIEFueSBuYXBhcmktanMgV2ViR1BVIDNEIHR5cGUgKHZvbHVtZS9pc29zdXJmYWNlL3N1cmZhY2UpIOKAlCBzaG93cyB0aGUgUmVzb2x1dGlvbiBjb250cm9sLiAqL1xuICBnZXQgaXNOYXBhcmkzZE1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGlzTmFwYXJpM2QodGhpcy5lZmZlY3RpdmVQbG90VHlwZSk7XG4gIH1cblxuICAvKiogSW50ZW5zaXR5IHByb2ZpbGUgbGluZXMgYXJlIFJlZ2lvbi1iYXNlZCBhbmQgYXZhaWxhYmxlIGluIHRoZSBIZWF0bWFwIGFuZFxuICAgKiAgSW1hZ2UgcGxvdCB0eXBlcywgd2hpY2ggc2hvdyB0aGUgSW50ZW5zaXR5IHRvb2wgZ3JvdXAuICovXG4gIGdldCBpc0ludGVuc2l0eUNhcGFibGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLkhFQVRNQVAgfHwgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gIH1cblxuICBzaG93SGVscCgpOiB2b2lkIHtcbiAgICB0aGlzLmRpc3BsYXlIZWxwRGlhbG9nID0gdHJ1ZTtcbiAgfVxufVxuIiwiPHAtdG9vbGJhcj5cbiAgPGRpdiBjbGFzcz1cInAtdG9vbGJhci1ncm91cC1zdGFydCBmbGV4LXdyYXAgYWxpZ24taXRlbXMtY2VudGVyIGdhcC0xXCI+XG5cbiAgICA8IS0tIEhvc3QtcHJvdmlkZWQgY3VzdG9tIHRvb2xiYXIgYnV0dG9ucywgc2hvd24gZmlyc3QgKGUuZy4gYXBwLXNwZWNpZmljXG4gICAgICAgICBhY3Rpb25zIHRoZSB2aXN1YWxpemF0aW9uIHZpZXcgaXRzZWxmIGRvZXNuJ3Qgb3duKS4gLS0+XG4gICAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuXG4gICAgPCEtLSBQbG90IHR5cGU6IGF2YWlsYWJsZSBmb3IgYW55IGltYWdlIChncmF5c2NhbGUgb3IgUkdCKS4gLS0+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImltYWdlSW5mbyAmJiB0b29scy5zcGVjaWFsVG9vbHNcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8cC1kcm9wZG93biBbb3B0aW9uc109XCJwbG90VHlwZU9wdGlvbnNcIiBvcHRpb25MYWJlbD1cImxhYmVsXCIgb3B0aW9uVmFsdWU9XCJ0eXBlXCJcbiAgICAgICAgICAgICAgICAgIFsobmdNb2RlbCldPVwic2VsZWN0ZWRQbG90VHlwZVwiIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNlbGVjdCBwbG90IHR5cGVcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgW3N0eWxlXT1cInsnbWluLXdpZHRoJzogJzExcmVtJ31cIlxuICAgICAgICAgICAgICAgICAgc3R5bGVDbGFzcz1cInBsb3Rtb2RlLWRyb3Bkb3duXCIgcGFuZWxTdHlsZUNsYXNzPVwicGxvdG1vZGUtcGFuZWxcIlxuICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cInNlbGVjdFBsb3RUeXBlLmVtaXQoJGV2ZW50LnZhbHVlKVwiPlxuICAgICAgICA8bmctdGVtcGxhdGUgbGV0LW9wdCBwVGVtcGxhdGU9XCJzZWxlY3RlZEl0ZW1cIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInBsb3Rtb2RlLWl0ZW1cIiAqbmdJZj1cIm9wdFwiPlxuICAgICAgICAgICAgPGkgKm5nSWY9XCJpc1BpSWNvbihvcHQuaWNvbilcIiBbY2xhc3NdPVwib3B0Lmljb25cIj48L2k+XG4gICAgICAgICAgICA8aW1nICpuZ0lmPVwib3B0Lmljb24gJiYgIWlzUGlJY29uKG9wdC5pY29uKVwiIGNsYXNzPVwicGxvdG1vZGUtaWNvbi1pbWdcIiBbc3JjXT1cIm9wdC5pY29uXCIgYWx0PVwiXCIgLz5cbiAgICAgICAgICAgIDxzcGFuPnt7IG9wdC5sYWJlbCB9fTwvc3Bhbj5cbiAgICAgICAgICA8L3NwYW4+XG4gICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgIDxuZy10ZW1wbGF0ZSBsZXQtb3B0IHBUZW1wbGF0ZT1cIml0ZW1cIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInBsb3Rtb2RlLWl0ZW1cIj5cbiAgICAgICAgICAgIDxpICpuZ0lmPVwiaXNQaUljb24ob3B0Lmljb24pXCIgW2NsYXNzXT1cIm9wdC5pY29uXCI+PC9pPlxuICAgICAgICAgICAgPGltZyAqbmdJZj1cIm9wdC5pY29uICYmICFpc1BpSWNvbihvcHQuaWNvbilcIiBjbGFzcz1cInBsb3Rtb2RlLWljb24taW1nXCIgW3NyY109XCJvcHQuaWNvblwiIGFsdD1cIlwiIC8+XG4gICAgICAgICAgICA8c3Bhbj57eyBvcHQubGFiZWwgfX08L3NwYW4+XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgPC9wLWRyb3Bkb3duPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImlzSXNvc3VyZmFjZU1vZGVcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPklzbzwvc3Bhbj5cbiAgICAgICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwiaXNvUmFuZ2VcIiBbcmFuZ2VdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBbbWluXT1cImlzb1ZhbHVlTWluXCIgW21heF09XCJpc29WYWx1ZU1heFwiIFtzdGVwXT1cImlzb1ZhbHVlU3RlcFwiXG4gICAgICAgICAgICAgICAgICAob25DaGFuZ2UpPVwiaXNvUmFuZ2VDaGFuZ2UuZW1pdCgkZXZlbnQudmFsdWVzKVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIklzb3N1cmZhY2UgYmFuZCAoMOKAkzI1NSksIG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUnc1xuICAgICAgICAgICAgICAgICAgYWN0dWFsIGludGVuc2l0eSByYW5nZS4gU3VyZmFjZXMgYXJlIGRyYXduIGZvciB2b3hlbHMgd2hvc2VcbiAgICAgICAgICAgICAgICAgIGludGVuc2l0eSBmYWxscyBpbiB0aGUgc2VsZWN0ZWQgYmFuZCDigJQgbmFycm93IGl0IHRvIGlzb2xhdGVcbiAgICAgICAgICAgICAgICAgIGZlYXR1cmVzLCB3aWRlbiBpdCB0byBzZWUgdGhlIHdob2xlIHN0cnVjdHVyZS5cIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICBzdHlsZUNsYXNzPVwid2FuZC1zZW5zaXRpdml0eS1zbGlkZXJcIj48L3Atc2xpZGVyPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0IHdhbmQtc2Vuc2l0aXZpdHktdmFsdWVcIj5cbiAgICAgICAgICB7eyBpc29SYW5nZVswXSB9feKAk3t7IGlzb1JhbmdlWzFdIH19PC9zcGFuPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICA8IS0tIEludGVuc2l0eSB0b29scyAoSGVhdG1hcCAvIEltYWdlIG1vZGUpOiBhZGQgYW5vdGhlciBjb2xvcmVkIGxpbmUgUk9JLlxuICAgICAgICAgICBFYWNoIGFkZGVkIGxpbmUgZ2V0cyB0aGUgbmV4dCBicmlnaHQgcGFsZXR0ZSBjb2xvdXIgYW5kIGl0cyBvd24gaW5zZXRcbiAgICAgICAgICAgdHJhY2U7IHNlbGVjdCBhIGxpbmUgb24gdGhlIGltYWdlIGFuZCB1c2UgdGhlIHRyYXNoIHRvb2wgdG8gcmVtb3ZlIGl0XG4gICAgICAgICAgIChhbmQgaXRzIHRyYWNlKS4gVGhlIGxpbmVzIGFyZSBSZWdpb24tYmFzZWQgYnV0IGV4Y2x1ZGVkIGZyb20gdGhlXG4gICAgICAgICAgIFJlZ2lvbiBFZGl0b3IgYW5kIGV4cG9ydHMuIC0tPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImlzSW50ZW5zaXR5Q2FwYWJsZVwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkFkZCBhbiBpbnRlbnNpdHkgbGluZSAobmV3IGNvbG91cikuIENsaWNrIGEgbGluZSB0byBzZWxlY3QgaXQsXG4gICAgICAgICAgICAgICAgICB0aGVuIHVzZSB0aGUgdHJhc2ggdG9vbCB0byByZW1vdmUgaXQuXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiYWRkUHJvZmlsZUxpbmUuZW1pdCgpXCI+XG4gICAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc3BsaW5lLWNoYXJ0LnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9uZy1jb250YWluZXI+XG4gICAgPCEtLSBDaGFubmVscyAmIEhpc3RvZ3JhbTogYnJpZ2h0bmVzcy9jb250cmFzdCwgZ2FtbWEsIHBlci1jaGFubmVsIGRpc3BsYXksXG4gICAgICAgICBjb2xvcm1hcC9yZXZlcnNlL2ludmVydCDigJQgb3BlbnMgdGhlIGZsb2F0aW5nIGRpYWxvZy4gLS0+XG4gICAgPHAtYnV0dG9uICpuZ0lmPVwidG9vbHMuc3BlY2lhbFRvb2xzXCIgcFJpcHBsZSBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJDaGFubmVscyAmYW1wOyBIaXN0b2dyYW0gKGJyaWdodG5lc3MvY29udHJhc3QsIGNvbG9ybWFwKVwiXG4gICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChvbkNsaWNrKT1cIm9wZW5DaGFubmVsSGlzdG9ncmFtLmVtaXQoKVwiPlxuICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvaW52ZXJ0LnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgIDwvcC1idXR0b24+XG5cbiAgICA8IS0tIFNwYXRpYWwtb21pY3MgY29udHJvbHMuIE9ubHkgbWVhbmluZ2Z1bCBpbiB0aGUgc3BhdGlhbCBtb2RlLCB3aGljaCBpc1xuICAgICAgICAgaXRzZWxmIG9ubHkgb2ZmZXJlZCB3aGlsZSBhIGRhdGFzZXQgaXMgbG9hZGVkIOKAlCBzbyB0aGlzIGJ1dHRvbiBhcHBlYXJzXG4gICAgICAgICBleGFjdGx5IHdoZW4gdGhlcmUgaXMgc29tZXRoaW5nIGZvciBpdCB0byBjb250cm9sLiAtLT5cbiAgICA8cC1idXR0b24gKm5nSWY9XCJ0b29scy5zcGVjaWFsVG9vbHMgJiYgaXNTcGF0aWFsTW9kZVwiIHBSaXBwbGUgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgIHBUb29sdGlwPVwiU3BhdGlhbCBvbWljcyAoY29sb3VyIGJ5IGNvbHVtbiBvciBnZW5lLCBsZWdlbmQsIHNlbGVjdGlvbiwgZGlzdHJpYnV0aW9ucylcIlxuICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAob25DbGljayk9XCJvcGVuU3BhdGlhbENvbnRyb2xzLmVtaXQoKVwiPlxuICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc3BhdGlhbC1vbWljcy5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICA8L3AtYnV0dG9uPlxuXG4gICAgPCEtLSBTbW9vdGhlbiB0b2dnbGU6IGJpbGluZWFyIHNtb290aGluZyB2cyBuZWFyZXN0LW5laWdoYm91ciByYXcgcGl4ZWxzIChJbWFnZVxuICAgICAgICAgdmlldyBvbmx5IOKAlCBpdCdzIGFuIE9wZW5TZWFkcmFnb24gZHJhd2VyIHNldHRpbmcpLiBIaWdobGlnaHRlZCB3aGVuIHNtb290aGluZyBvbi4gLS0+XG4gICAgPHAtYnV0dG9uICpuZ0lmPVwidG9vbHMuc3BlY2lhbFRvb2xzICYmIGlzSW1hZ2VWaWV3XCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgW3RleHRdPVwiIWltYWdlU21vb3RoaW5nRW5hYmxlZFwiXG4gICAgICAgICAgICAgIHBUb29sdGlwPVwiU21vb3RoZW4gKHR1cm4gb24gaW1hZ2Ugc21vb3RoaW5nKVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZUltYWdlU21vb3RoaW5nLmVtaXQoKVwiPlxuICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc21vb3RoLnN2Z1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIlxuICAgICAgICAgICBbY2xhc3NdPVwiaW1hZ2VTbW9vdGhpbmdFbmFibGVkID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiLz5cbiAgICA8L3AtYnV0dG9uPlxuXG4gICAgPCEtLSBTdGFjayBjb250cm9scyAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaW1hZ2VJbmZvPy5pc1N0YWNrXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgPCEtLSBJbWFnZSB2aWV3OiBzY3J1YiB6LXNsaWNlcyBsaXZlICh0aGUgcmVuZGVyZXIgc3dhcHMgc2xpY2VzIGluIHBsYWNlKS4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwic2hvd3NMaXZlU2xpY2VTY3J1YmJlclwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+U2xpY2U8L3NwYW4+XG4gICAgICAgIDxwLXNsaWRlciBbKG5nTW9kZWwpXT1cInpJbmRleFwiIFttaW5dPVwiMFwiIFttYXhdPVwibWF4SW5kZXhcIlxuICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cInpTY3J1Yi5lbWl0KCRldmVudC52YWx1ZSlcIiAob25TbGlkZUVuZCk9XCJ6U2xpZGUuZW1pdCgkZXZlbnQudmFsdWUpXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU2xpY2UgdGhyb3VnaCB0aGUgc3RhY2sgKOKGkCAvIOKGkiBrZXlzKVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICBbc3R5bGVdPVwieyd3aWR0aCc6JzE2cmVtJ31cIiBzdHlsZUNsYXNzPVwid2FuZC1zZW5zaXRpdml0eS1zbGlkZXJcIj48L3Atc2xpZGVyPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+e3t6SW5kZXh9fSBvZiB7e21heEluZGV4fX08L3NwYW4+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgIDwhLS0gSGVhdG1hcCB2aWV3OiBzaW5nbGUtaW1hZ2UgKGhpZ2gtZGVmIHpvb20pIHZzIHN0YWNrIChmcmFtZSBzbGlkZXIpLiBQbG90bHktb25seSDigJRcbiAgICAgICAgICAgaGlkZGVuIGZvciBuYXBhcmksIHdoaWNoIGhhcyBubyBzaW5nbGUvc3RhY2sgdG9nZ2xlIG9yIHBlci1mcmFtZSBmZXRjaCwgYW5kIGZvciB0aGVcbiAgICAgICAgICAgc3BhdGlhbC1vbWljcyBtb2RlczogYSAzRCBvbWljcyBkYXRhc2V0IHB1Ymxpc2hlcyBpdHMgcmVmZXJlbmNlIHZvbHVtZSBhcyBhIHotc3RhY2tcbiAgICAgICAgICAgaW1hZ2UsIGFuZCB0aGVzZSBtb2RlcyBkcmF3IG9ic2VydmF0aW9ucywgbm90IG9uZSBvZiBpdHMgc2xpY2VzLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIhc2hvd3NMaXZlU2xpY2VTY3J1YmJlciAmJiAhaXNOYXBhcmlNb2RlICYmICFpc1NwYXRpYWxNb2RlXCI+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+XG4gICAgICAgIDwvcC1kcm9wZG93bj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIihpc0hlYXRtYXAgfHwgaXMzZFJlZ2lvbnMpICYmIHRvb2xzLnJlZ2lvblRvb2xzXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgPCEtLSBSZWdpb24gRWRpdG9yOiBvcGVucyB0aGUgZWRpdG9yIGFzIGEgZmxvYXRpbmcgZGlhbG9nIChmb3JtZXJseSB0aGVcbiAgICAgICAgICAgcmlnaHQtcGFuZWwgUmVnaW9ucyB0YWIpLiBTaXRzIHdpdGggdGhlIHJlZ2lvbiBkcmF3aW5nIHRvb2xzLiAtLT5cbiAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIk9wZW4gdGhlIFJlZ2lvbiBFZGl0b3JcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cIm9wZW5SZWdpb25FZGl0b3IuZW1pdCgpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3JlZ2lvbi1lZGl0b3Iuc3ZnXCIgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPCEtLSBSZWdpb24gZHJhd2luZyB0b29scyAtLT5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktYXJyb3ctdXAtcmlnaHRcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnc2VsZWN0J1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTZWxlY3Rpb24gbW9kZSAoPGtiZD5zPC9rYmQ+KVwiXG4gICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnc2VsZWN0JylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXN0b3BcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnZHJhd3JlY3QnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkRyYXcgYSByZWN0YW5ndWxhciByZWdpb24gKDxrYmQ+cjwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCJcbiAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2RyYXdyZWN0JylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gKm5nSWY9XCIhaXMzZFJlZ2lvbnNcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnZHJhd29wZW5wYXRoJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEcmF3IGEgcG9seWxpbmUgKDxrYmQ+bDwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCJcbiAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2RyYXdvcGVucGF0aCcpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3BvbHlsaW5lLnN2Z1wiXG4gICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnZHJhd29wZW5wYXRoJyA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1wZW5jaWxcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnZHJhd2Nsb3NlZHBhdGgnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkRyYXcgYSBmcmVlZm9ybSByZWdpb24gKDxrYmQ+Zjwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCJcbiAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2RyYXdjbG9zZWRwYXRoJylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8IS0tIEJydXNoOiBwYWludCBhIHJlZ2lvbiBieSBkcmFnZ2luZyBhIGRpc2Mgb2YgdGhlIGdpdmVuIHNpemU7IHRoZSBTaXplXG4gICAgICAgICAgIHNsaWRlciBhcHBlYXJzIHdoaWxlIGl0J3MgYWN0aXZlLiBIb2xkIFNoaWZ0IHRvIGVyYXNlIChzdWJ0cmFjdCkuIC0tPlxuICAgICAgPCEtLSBCcnVzaCBhbmQgcG9seWxpbmUgYXJlIGFic2VudCBmcm9tIGEgM0QgY2xvdWQ6IHRoZSBicnVzaCBwYWludHMgaW4gSU1BR0VcbiAgICAgICAgICAgICAgICAgcGl4ZWxzIGFuZCB0aGVyZSBpcyBubyByYXN0ZXIgdG8gc2FtcGxlLCBhbmQgYW4gb3BlbiBwb2x5bGluZSBpcyBhXG4gICAgICAgICAgICAgICAgIHByb2ZpbGUgdG9vbCwgbm90IGEgY2xvc2VkIHJlZ2lvbiBhIHNlbGVjdGlvbiBjYW4gdGVzdCBhZ2FpbnN0LiAtLT5cbiAgICAgICAgICAgIDxwLWJ1dHRvbiAqbmdJZj1cIiFpczNkUmVnaW9uc1wiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdicnVzaCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiQnJ1c2g6IGNsaWNrIGFuZCBkcmFnIHRvIHBhaW50IGEgcmVnaW9uLlxuICAgICAgICAgICAgICAgIDxicj5Ib2xkIDxrYmQ+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+XG4gICAgICA8IS0tIFNBTSAoaml0LXVpIzkwKTogYm94LXByb21wdCBzZWdtZW50LCBpbnRlcmFjdGl2ZSBwb2ludCBwcm9tcHRzLCBhbmQgYVxuICAgICAgICAgICBtb2RlbCBwaWNrZXIuIC0tPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDwhLS0gU0FNIGNvbnRyb2xzIGdyb3VwZWQ6IGJveC1wcm9tcHQgU2VnbWVudCwgdGhlbiB0aGUgcG9pbnQtcHJvbXB0IHRvb2wsXG4gICAgICAgICAgIHRoZW4gdGhlIG1vZGVsLXBpY2tlciBkcm9wZG93biAodGhlIGNoZXZyb24pIHdoaWNoIGdvdmVybnMgQk9USCB0b29scy5cbiAgICAgICAgICAgRmFsbHMgYmFjayB0byBTZWdtZW50ICsgcG9pbnQgd2l0aCBubyBtb2RlbCBwaWNrZXIgd2hlbiBub25lIGFyZSBzZXQuIC0tPlxuICAgICAgPHNwYW4gKm5nSWY9XCJzYW1Nb2RlbHMubGVuZ3RoOyBlbHNlIHNlZ21lbnRQbGFpblwiIGNsYXNzPVwicC1idXR0b25zZXQgc2VnbWVudC1zcGxpdFwiPlxuICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNlZ21lbnQgdGhlIGRyYXduIHJlY3RhbmdsZXMgd2l0aCBTQU0gKGJveCBwcm9tcHQpXCJcbiAgICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJzZWdtZW50UmVnaW9ucy5lbWl0KClcIj5cbiAgICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zYW0tYm94LXByb21wdC5zdmdcIlxuICAgICAgICAgICAgICAgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgIDxuZy1jb250YWluZXIgW25nVGVtcGxhdGVPdXRsZXRdPVwic2FtUG9pbnRCdG5cIj48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1jaGV2cm9uLWRvd25cIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUGljayB0aGUgU0FNIG1vZGVsICh1c2VkIGJ5IGJvdGggc2VnbWVudCB0b29scylcIlxuICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNhbU1vZGVsTWVudS50b2dnbGUoJGV2ZW50KVwiPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgPC9zcGFuPlxuICAgICAgPG5nLXRlbXBsYXRlICNzZWdtZW50UGxhaW4+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwicC1idXR0b25zZXQgc2VnbWVudC1zcGxpdFwiPlxuICAgICAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTZWdtZW50IHRoZSBkcmF3biByZWN0YW5nbGVzIHdpdGggU0FNIChib3ggcHJvbXB0KVwiXG4gICAgICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNlZ21lbnRSZWdpb25zLmVtaXQoKVwiPlxuICAgICAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc2FtLWJveC1wcm9tcHQuc3ZnXCJcbiAgICAgICAgICAgICAgICAgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cInNhbVBvaW50QnRuXCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvc3Bhbj5cbiAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICA8IS0tIFBvaW50LXByb21wdCB0b29sIOKAlCBzaGFyZWQgYmV0d2VlbiB0aGUgbW9kZWxzIC8gbm8tbW9kZWxzIGxheW91dHMgc28gaXRcbiAgICAgICAgICAgYWx3YXlzIHNpdHMganVzdCBsZWZ0IG9mIHRoZSBtb2RlbCBkcm9wZG93bi4gLS0+XG4gICAgICA8bmctdGVtcGxhdGUgI3NhbVBvaW50QnRuPlxuICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnc2FtUG9pbnQnXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU0FNIHBvaW50IHByb21wdHM6IGNsaWNrIGEgZmliZXIgdG8gc2VnbWVudCBpdCBhcyBhIG5ldyByZWdpb247XG4gICAgICAgICAgICAgICAgICBTaGlmdC9BbHQtY2xpY2sgdG8gcmVmaW5lIChleGNsdWRlKSB0aGUgY3VycmVudCBvbmUuPGJyPkVzYyB1bmRvZXMgaXQuXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnc2FtUG9pbnQnKVwiPlxuICAgICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NhbS1wb2ludC1wcm9tcHQuc3ZnXCJcbiAgICAgICAgICAgICAgIFtjbGFzc109XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ3NhbVBvaW50JyA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICA8cC1tZW51ICNzYW1Nb2RlbE1lbnUgc3R5bGVDbGFzcz1cIm1vZGVsLW1lbnVcIiBbbW9kZWxdPVwic2FtTWVudUl0ZW1zXCIgW3BvcHVwXT1cInRydWVcIiBhcHBlbmRUbz1cImJvZHlcIj5cbiAgICAgICAgPG5nLXRlbXBsYXRlIHBUZW1wbGF0ZT1cIml0ZW1cIiBsZXQtaXRlbT5cbiAgICAgICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIm1vZGVsTWVudUl0ZW1cIlxuICAgICAgICAgICAgICAgICAgICAgICAgW25nVGVtcGxhdGVPdXRsZXRDb250ZXh0XT1cInsgJGltcGxpY2l0OiBpdGVtIH1cIj48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgIDwvcC1tZW51PlxuXG4gICAgICA8IS0tIFNoYXJlZCBpdGVtIGJvZHkgZm9yIGFsbCB0aHJlZSBtb2RlbCBwaWNrZXJzIChTQU0gLyBZT0xPIC8gcmV0aW5hbCk6XG4gICAgICAgICAgIHRoZSBzdGFuZGFyZCBjaGVjay1vci1ibGFuayBpY29uIGFuZCBsYWJlbCwgcGx1cyBhbiBpbmZvIGljb24gY2FycnlpbmdcbiAgICAgICAgICAgdGhlIG1vZGVsJ3MgZGVzY3JpcHRpb24gYXMgYSBob3ZlciB0b29sdGlwLiBUaGlzIHJlcGxhY2VkIGEgc2luZ2xlXG4gICAgICAgICAgIFwiQWJvdXQgdGhlIFNBTSBtb2RlbHNcIiBvdmVybGF5IHBhbmVsIOKAlCBwZXItbW9kZWwgdGV4dCBzaXRzIG5leHQgdG8gdGhlXG4gICAgICAgICAgIG1vZGVsIGl0IGRlc2NyaWJlcywgYW5kIFlPTE8vcmV0aW5hbCBnZXQgdGhlIHNhbWUgYWZmb3JkYW5jZS5cblxuICAgICAgICAgICBwLW1lbnUgcHV0cyBpdHMgY2xpY2sgaGFuZGxlciBvbiB0aGUgd3JhcHBpbmcgYC5wLW1lbnVpdGVtLWNvbnRlbnRgLFxuICAgICAgICAgICBvdXRzaWRlIHRoaXMgdGVtcGxhdGUsIHNvIGBjb21tYW5kYCBzdGlsbCBmaXJlcyBhbmQgdGhlIHdob2xlIHJvdyBzdGF5c1xuICAgICAgICAgICBjbGlja2FibGUg4oCUIHdoaWNoIGlzIGFsc28gd2h5IHRoZSBpbmZvIGljb24gaGFzIHRvIHN0b3AgcHJvcGFnYXRpb24sIG9yXG4gICAgICAgICAgIHJlYWRpbmcgYWJvdXQgYSBtb2RlbCB3b3VsZCBzd2l0Y2ggdG8gaXQuIC0tPlxuICAgICAgPG5nLXRlbXBsYXRlICNtb2RlbE1lbnVJdGVtIGxldC1pdGVtPlxuICAgICAgICA8YSBjbGFzcz1cInAtbWVudWl0ZW0tbGluayBtb2RlbC1tZW51LWl0ZW1cIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInAtbWVudWl0ZW0taWNvblwiIFtuZ0NsYXNzXT1cIml0ZW0uaWNvblwiPjwvc3Bhbj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInAtbWVudWl0ZW0tdGV4dFwiPnt7IGl0ZW0ubGFiZWwgfX08L3NwYW4+XG4gICAgICAgICAgPCEtLSBgYm90dG9tYCBsaWtlIGV2ZXJ5IG90aGVyIHRvb2x0aXAgaW4gdGhpcyB0b29sYmFyLCBhbmQgZm9yIHJvb206XG4gICAgICAgICAgICAgICB0aGVzZSBtZW51cyBvcGVuIHVuZGVyIGJ1dHRvbnMgdGhhdCBjYW4gc2l0IG5lYXIgdGhlIHJpZ2h0IGVkZ2UsXG4gICAgICAgICAgICAgICB3aGVyZSBhIGByaWdodGAgdG9vbHRpcCBoYXMgYSBmZXcgaHVuZHJlZCBweCBhbmQgc3F1ZWV6ZXMgaXRzIHRleHRcbiAgICAgICAgICAgICAgIGludG8gYSBuYXJyb3cgY29sdW1uIGluc3RlYWQgb2YgdXNpbmcgaXRzIG1heC13aWR0aC4gLS0+XG4gICAgICAgICAgPCEtLSBBIGZvY3VzYWJsZSA8YnV0dG9uPiwgbm90IGEgYmFyZSA8aT4uIEFzIGFuIDxpPiB0aGUgZGVzY3JpcHRpb25cbiAgICAgICAgICAgICAgIHdhcyByZWFjaGFibGUgb25seSBieSBob3ZlcmluZyBhIG1vdXNlOiBub3QgdGFiYmFibGUsIG5vXG4gICAgICAgICAgICAgICBhY2Nlc3NpYmxlIG5hbWUsIG5vdGhpbmcgZm9yIGEgc2NyZWVuIHJlYWRlciB0byBhbm5vdW5jZSDigJQgc29cbiAgICAgICAgICAgICAgIGtleWJvYXJkIGFuZCBBVCB1c2VycyBjb3VsZCBwaWNrIGEgY2hlY2twb2ludCBidXQgbmV2ZXIgcmVhZCB3aGF0XG4gICAgICAgICAgICAgICBpdCB3YXMuIFByaW1lTkcncyB0b29sdGlwIHNob3dzIG9uIGZvY3VzIGFzIHdlbGwgYXMgaG92ZXIsIHNvXG4gICAgICAgICAgICAgICBtYWtpbmcgaXQgYSBidXR0b24gcmVzdG9yZXMgaXQgZm9yIGZyZWUuXG5cbiAgICAgICAgICAgICAgIGBhcmlhLWRlc2NyaWJlZGJ5YCBpcyBkZWxpYmVyYXRlbHkgbm90IHVzZWQ6IHRoZSB0b29sdGlwIGlzXG4gICAgICAgICAgICAgICByZW5kZXJlZCBpbnRvIGBib2R5YCBvbiBkZW1hbmQgYW5kIGhhcyBubyBzdGFibGUgaWQgdG8gcG9pbnQgYXQuXG4gICAgICAgICAgICAgICBUaGUgZGVzY3JpcHRpb24gaXMgY2FycmllZCBvbiBgYXJpYS1sYWJlbGAgaW5zdGVhZCwgd2hpY2ggaXNcbiAgICAgICAgICAgICAgIGFubm91bmNlZCB3aGV0aGVyIG9yIG5vdCB0aGUgdG9vbHRpcCBpcyBzaG93aW5nLiBJdCBpcyBzdHJpcHBlZCBvZlxuICAgICAgICAgICAgICAgbWFya3VwIGZpcnN0LCBzaW5jZSB0aGUgY29weSBjb250YWlucyBgPGI+YC9gPGJyPmAgZm9yIHRoZSB2aXN1YWxcbiAgICAgICAgICAgICAgIHRvb2x0aXAgYW5kIHRob3NlIHdvdWxkIG90aGVyd2lzZSBiZSByZWFkIG91dCBsaXRlcmFsbHkuIC0tPlxuICAgICAgICAgIDxidXR0b24gKm5nSWY9XCJpdGVtLnRvb2x0aXBcIiB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgICAgIGNsYXNzPVwicGkgcGktaW5mby1jaXJjbGUgbW9kZWwtaW5mby1pY29uXCJcbiAgICAgICAgICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ0Fib3V0ICcgKyBpdGVtLmxhYmVsICsgJzogJyArIHBsYWluVGV4dChpdGVtLnRvb2x0aXApXCJcbiAgICAgICAgICAgICAgICAgIFtwVG9vbHRpcF09XCJpdGVtLnRvb2x0aXBcIiBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIGFwcGVuZFRvPVwiYm9keVwiIHRvb2x0aXBTdHlsZUNsYXNzPVwibW9kZWwtaW5mby10b29sdGlwXCJcbiAgICAgICAgICAgICAgICAgIChjbGljayk9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgICAgICAgICAgICAgKGtleWRvd24uZW50ZXIpPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgICAgICAgICAgICAgIChrZXlkb3duLnNwYWNlKT1cIiRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiPjwvYnV0dG9uPlxuICAgICAgICA8L2E+XG4gICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiQXV0by1zZWdtZW50IGNlbGxzIGluc2lkZSBlYWNoIGRyYXduIHJlY3RhbmdsZSAoY2VsbHBvc2UtU0FNKVwiXG4gICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJzZWdtZW50Q2VsbHBvc2UuZW1pdCgpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL2NlbGxzLnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cblxuICAgICAgPCEtLSBDb250cmlidXRlZCBuby1wcm9tcHQgdG9vbHMgKFRPT0xCQVJfVE9PTFMpLiBVbmxpa2UgdGhlIFNBTSBhbmRcbiAgICAgICAgICAgY2VsbHBvc2UgdG9vbHMgdGhlc2UgdGFrZSBubyBwcm9tcHQg4oCUIHRoZXkgc3dlZXAgdGhlIHdob2xlIGN1cnJlbnRcbiAgICAgICAgICAgdmlldyByYXRoZXIgdGhhbiBiZWluZyBwb2ludGVkIGF0IHNvbWV0aGluZyDigJQgc28gdGhlcmUgaXMgbm90aGluZyB0b1xuICAgICAgICAgICBkcmF3IGZpcnN0LiBFYWNoIHJlbmRlcnMgdGhlIHNhbWUgc3BsaXQgYnV0dG9uOiBydW4sIHBpY2sgY2hlY2twb2ludCxcbiAgICAgICAgICAgb3BlbiBwYXJhbWV0ZXJzLlxuXG4gICAgICAgICAgIE5vdGhpbmcgaGVyZSBuYW1lcyBhIHNwZWNpZmljIHRvb2wuIFRoZSBsaWJyYXJ5IHNoaXBzIG5vbmU7IGEgaG9zdFxuICAgICAgICAgICByZWdpc3RlcnMgdGhlbSwgd2hpY2ggaXMgd2hhdCBsZXRzIHRoaXMgcGFja2FnZSBiZSBwdWJsaXNoZWQgd2l0aG91dFxuICAgICAgICAgICB0aGUgY2xvc2VkIG1vZGVsIGRlcGVuZGVuY2llcy4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCB0b29sIG9mIGNvbnRyaWJ1dGVkVG9vbHM7IHRyYWNrQnk6IHRyYWNrVG9vbEJ5SWRcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJwLWJ1dHRvbnNldCBzZWdtZW50LXNwbGl0XCI+XG4gICAgICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgICBbcFRvb2x0aXBdPVwidG9vbC5ydW5Ub29sdGlwXCJcbiAgICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwicnVuVG9vbC5lbWl0KHRvb2wuaWQpXCI+XG4gICAgICAgICAgICA8IS0tIGB0b29sYmFyLWljb24tZGFya2AgaXMgaGFyZGNvZGVkIHJhdGhlciB0aGFuIGJvdW5kLCB1bmxpa2UgdGhlXG4gICAgICAgICAgICAgICAgIHRvZ2dsZSB0b29sczogdGhlc2UgZmlyZSBhIHJ1biBhbmQgaGF2ZSBubyBhY3RpdmUgc3RhdGUuIC0tPlxuICAgICAgICAgICAgPGltZyAqbmdJZj1cInRvb2wuaWNvbi5zcmNcIiBbc3JjXT1cInRvb2wuaWNvbi5zcmNcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCJcbiAgICAgICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiIFthbHRdPVwidG9vbC5sYWJlbFwiIC8+XG4gICAgICAgICAgICA8aSAqbmdJZj1cInRvb2wuaWNvbi5waVwiIGNsYXNzPVwicGkge3sgdG9vbC5pY29uLnBpIH19XCI+PC9pPlxuICAgICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1jaGV2cm9uLWRvd25cIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgICAgW3BUb29sdGlwXT1cInRvb2wubW9kZWxUb29sdGlwIHx8ICdQaWNrIHRoZSBtb2RlbCdcIlxuICAgICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b29sTWVudS50b2dnbGUoJGV2ZW50KVwiPlxuICAgICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgICAgPHAtYnV0dG9uICpuZ0lmPVwidG9vbC5wYXJhbXMubGVuZ3RoXCJcbiAgICAgICAgICAgICAgICAgICAgaWNvbj1cInBpIHBpLXNsaWRlcnMtaFwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgICBbcFRvb2x0aXBdPVwidG9vbC5wYXJhbXNUb29sdGlwIHx8ICdQYXJhbWV0ZXJzJ1wiXG4gICAgICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cIm9wZW5Ub29sUGFyYW1zLmVtaXQodG9vbC5pZClcIj5cbiAgICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgICA8L3NwYW4+XG4gICAgICAgIDxwLW1lbnUgI3Rvb2xNZW51IHN0eWxlQ2xhc3M9XCJtb2RlbC1tZW51XCIgW21vZGVsXT1cInRvb2xNZW51SXRlbXNbdG9vbC5pZF0gfHwgW11cIlxuICAgICAgICAgICAgICAgIFtwb3B1cF09XCJ0cnVlXCIgYXBwZW5kVG89XCJib2R5XCI+XG4gICAgICAgICAgPG5nLXRlbXBsYXRlIHBUZW1wbGF0ZT1cIml0ZW1cIiBsZXQtaXRlbT5cbiAgICAgICAgICAgIDxuZy1jb250YWluZXIgW25nVGVtcGxhdGVPdXRsZXRdPVwibW9kZWxNZW51SXRlbVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFtuZ1RlbXBsYXRlT3V0bGV0Q29udGV4dF09XCJ7ICRpbXBsaWNpdDogaXRlbSB9XCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgPC9wLW1lbnU+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJ0b29scy5oZWxwXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuXG4gICAgICA8IS0tIEhlbHAgLS0+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXF1ZXN0aW9uLWNpcmNsZVwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiSGVscFwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2hvd0hlbHAoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICA8L2Rpdj5cbjwvcC10b29sYmFyPlxuPHAtZGlhbG9nIGhlYWRlcj1cIlBsb3R0aW5nIGhlbHBcIiBbKHZpc2libGUpXT1cImRpc3BsYXlIZWxwRGlhbG9nXCIgW21vZGFsXT1cInRydWVcIiBbc3R5bGVdPVwie3dpZHRoOiAnNTB2dyd9XCJcbiAgICAgICAgICBbbWF4aW1pemFibGVdPVwidHJ1ZVwiIFtkcmFnZ2FibGVdPVwiZmFsc2VcIiBbcmVzaXphYmxlXT1cImZhbHNlXCI+XG4gIDxwPkNsaWNrIGEgZmlsZSBpbiB0aGUgbGVmdC1oYW5kIHRyZWUgdG8gZGlzcGxheSBpdC4gQnkgZGVmYXVsdCBpdCBvcGVucyBpbiB0aGVcbiAgICA8Yj5JbWFnZTwvYj4gdmlldyDigJQgYSBuYXRpdmVseSB0aWxlZCwgZGVlcGx5IHpvb21hYmxlIHJhc3RlciAoT3BlblNlYWRyYWdvbikgd2l0aCBhXG4gICAgbmF2aWdhdG9yIG1pbmltYXAgYW5kIGEgcGh5c2ljYWwtdW5pdHMgc2NhbGUgYmFyLiA8Yj5DbGljayB0aGUgaW1hZ2UgdG8gem9vbTwvYj4gdG93YXJkXG4gICAgdGhlIGNsaWNrZWQgcG9pbnQ7IHNjcm9sbCB0byB6b29tIGFuZCBkcmFnIHRvIHBhbi4gV2hlbiBhIHN0YWNrIGlzIG9wZW4sIGEgc2xpY2Ugc2xpZGVyXG4gICAgKEltYWdlIHZpZXcpIG9yIGltYWdlLW1vZGUgdG9nZ2xlIChvdGhlciB2aWV3cykgYXBwZWFycy4gUmVnaW9ucyBjYW4gYWxzbyBiZSBlZGl0ZWQgaW4gdGhlXG4gICAgPGI+UmVnaW9uIEVkaXRvcjwvYj4sIG9wZW5lZCB3aXRoIHRoZVxuICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3JlZ2lvbi1lZGl0b3Iuc3ZnXCIgd2lkdGg9XCIxNFwiIGhlaWdodD1cIjE0XCIgYWx0PVwiUmVnaW9uIEVkaXRvclwiLz5cbiAgICB0b29sYmFyIGJ1dHRvbiBhcyBhIGZsb2F0aW5nIGRpYWxvZy4gRHJhZyB0aGUgPGkgY2xhc3M9XCJwaSBwaS1iYXJzXCI+PC9pPiBoYW5kbGUgdG8gZGV0YWNoXG4gICAgdGhpcyB0b29sYmFyIGludG8gYSBmbG9hdGluZyB3aW5kb3cuXG4gIDwvcD5cblxuICA8aDM+UGxvdC10eXBlIGRyb3Bkb3duPC9oMz5cbiAgPHA+XG4gICAgVGhlIDxiPnBsb3QtdHlwZSBkcm9wZG93bjwvYj4gc3dpdGNoZXMgaG93IHRoZSBmaWxlIGlzIHZpc3VhbGl6ZWQ6XG4gICAgPGI+SW1hZ2U8L2I+LCA8Yj5IZWF0bWFwPC9iPiwgPGI+Q29udG91cjwvYj4sIDxiPlN1cmZhY2U8L2I+LCA8Yj5Wb2x1bWU8L2I+LFxuICAgIDxiPklzb3N1cmZhY2U8L2I+LCBhbmQg4oCUIHdoZW4gYSBzcGF0aWFsLW9taWNzIGRhdGFzZXQgaXMgb3BlbiDigJRcbiAgICA8Yj5TcGF0aWFsIG9taWNzPC9iPiBhbmQgPGI+U3BhdGlhbCBvbWljcyAzRDwvYj4uXG4gIDwvcD5cbiAgPHA+XG4gICAgPGI+T25seSB0aGUgbW9kZXMgdGhlIGRhdGEgY2FuIGFjdHVhbGx5IHN1cHBvcnQgYXJlIG9mZmVyZWQ8L2I+LCBzbyB0aGUgbGlzdCBjaGFuZ2VzXG4gICAgd2l0aCB3aGF0IGlzIGxvYWRlZCByYXRoZXIgdGhhbiBzaG93aW5nIGVudHJpZXMgdGhhdCB3b3VsZCBjb21lIHVwIGJsYW5rOlxuICA8L3A+XG4gIDx1bD5cbiAgICA8bGk+PGI+Q29udG91cjwvYj4sIDxiPlZvbHVtZTwvYj4gYW5kIDxiPklzb3N1cmZhY2U8L2I+IG5lZWQgYSBncmF5c2NhbGUgaW1hZ2U7IHRoZVxuICAgICAgdHdvIDNEIG9uZXMgYWxzbyBuZWVkIGEgei1zdGFjay48L2xpPlxuICAgIDxsaT48Yj5TcGF0aWFsIG9taWNzPC9iPiBuZWVkcyBhIHB1Ymxpc2hlZCBkYXRhc2V0LCBhbmQgPGI+U3BhdGlhbCBvbWljcyAzRDwvYj4gbmVlZHNcbiAgICAgIG9uZSB3aG9zZSBjb29yZGluYXRlcyBjYXJyeSBhIHouPC9saT5cbiAgICA8bGk+VGhlIHBpeGVsIG1vZGVzICg8Yj5JbWFnZTwvYj4sIDxiPkhlYXRtYXA8L2I+LCA8Yj5TdXJmYWNlPC9iPikgYXJlIHdpdGhoZWxkIGZvciBhXG4gICAgICBkYXRhc2V0IHRoYXQgYnJpbmdzIG5vIHRpc3N1ZSBpbWFnZSDigJQgc2VxRklTSCByZWNvcmRzIGNvb3JkaW5hdGVzIGFuZCBubyBzZWN0aW9uLCBzb1xuICAgICAgdGhlcmUgaXMgbm90aGluZyBmb3IgdGhlbSB0byBkcmF3LjwvbGk+XG4gIDwvdWw+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+XG5cbiAgPGgzPlNwYXRpYWwgb21pY3M8L2gzPlxuICA8cD5cbiAgICBJbiA8Yj5TcGF0aWFsIG9taWNzPC9iPiBtb2RlIGVhY2ggb2JzZXJ2YXRpb24g4oCUIGEgY2VsbCBvciBhIFZpc2l1bSBzcG90IOKAlCBpcyBkcmF3biBhdFxuICAgIGl0cyBvd24gY29vcmRpbmF0ZXMsIG92ZXIgdGhlIHRpc3N1ZSBpbWFnZSB3aGVyZSB0aGUgZGF0YXNldCByZWdpc3RlcnMgb250byBvbmUuIFRoZVxuICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NwYXRpYWwtb21pY3Muc3ZnXCIgd2lkdGg9XCIxNFwiIGhlaWdodD1cIjE0XCIgYWx0PVwiU3BhdGlhbCBvbWljc1wiLz5cbiAgICBwYW5lbCBjb2xvdXJzIHRoZW0gYnkgYSA8Yj5jb2x1bW48L2I+IChhIGNlbGwgdHlwZSwgYSBjbHVzdGVyKSBvciBieSBhIDxiPmdlbmU8L2I+LCBhbmRcbiAgICB0aGUgdHdvIGFyZSBleGNsdXNpdmUgc28gd2hhdCBkcml2ZXMgdGhlIGNvbG91cnMgaXMgbmV2ZXIgYW1iaWd1b3VzLlxuICA8L3A+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5TZWxlY3QgZnJvbSBST0lzPC9iPlwiOiB0dXJucyB0aGUgcmVnaW9ucyB5b3UgaGF2ZSBkcmF3biBpbnRvIGEgc2VsZWN0aW9uIG9mXG4gICAgICBvYnNlcnZhdGlvbnMuIEV2ZXJ5dGhpbmcgZWxzZSB0aGVuIGZvbGxvd3MgdGhhdCBzZWxlY3Rpb24uPC9saT5cbiAgICA8bGk+XCI8Yj5Qb2ludCBzaXplPC9iPlwiIC8gXCI8Yj5PcGFjaXR5PC9iPlwiOiBob3cgdGhlIG1hcmtlcnMgYXJlIGRyYXduLCBub3Qgd2hhdCBpc1xuICAgICAgc2VsZWN0ZWQuPC9saT5cbiAgPC91bD5cblxuICA8aDM+RGlzdHJpYnV0aW9uIGNoYXJ0czwvaDM+XG4gIDxwPlxuICAgIFRoZSA8Yj5EaXN0cmlidXRpb248L2I+IHNlY3Rpb24gb2YgdGhlIHNwYXRpYWwtb21pY3MgcGFuZWwgY2hhcnRzIHdoYXRldmVyIHRoZSBtYXAgaXNcbiAgICBjb2xvdXJlZCBieSwgc28gdGhlIHR3byBjYW4gbmV2ZXIgZGlzYWdyZWUuIEVhY2ggdGFiIGFuc3dlcnMgYSBkaWZmZXJlbnQgcXVlc3Rpb24sIGFuZFxuICAgIHRoZSA8aSBjbGFzcz1cInBpIHBpLXF1ZXN0aW9uLWNpcmNsZVwiPjwvaT4gYmVzaWRlIHRoZW0gZXhwbGFpbnMgdGhlIG9uZSBvbiBzY3JlZW4g4oCUXG4gICAgaW5jbHVkaW5nIHdoYXQgaXQgbXVzdCA8ZW0+bm90PC9lbT4gYmUgcmVhZCBmb3IuXG4gIDwvcD5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPkNvdW50czwvYj5cIjogaG93IG1hbnkgb2JzZXJ2YXRpb25zIHBlciBjYXRlZ29yeS4gQSBjYXRlZ29yeSBpcyBhIGxhYmVsLCBub3QgYVxuICAgICAgbWFnbml0dWRlLCBzbyBhIGZyZXF1ZW5jeSBpcyB0aGUgb25seSBkaXN0cmlidXRpb24gaXQgaGFzLjwvbGk+XG4gICAgPGxpPlwiPGI+SGlzdG9ncmFtPC9iPlwiIC8gXCI8Yj5WaW9saW48L2I+XCIgLyBcIjxiPkJveDwvYj5cIjogaG93IGEgY29udGludW91cyB2YWx1ZSBpc1xuICAgICAgZGlzdHJpYnV0ZWQg4oCUIG92ZXIgZXZlcnl0aGluZywgb3Igc3BsaXQgYnkgYSBncm91cGluZyBjb2x1bW4uPC9saT5cbiAgICA8bGk+XCI8Yj5IZWF0bWFwPC9iPlwiOiBtZWFuIGV4cHJlc3Npb24gb2YgdGhlIGdlbmVzIHlvdSBwaWNrIHdpdGhpbiBlYWNoIGdyb3VwLlxuICAgICAgWi1zY29yZWQgcGVyIGdlbmUgYnkgZGVmYXVsdCwgc28gYSBjb2xvdXIgbWVhbnMgXCJhYm92ZSBvciBiZWxvdyB0aGlzIGdlbmUncyBvd25cbiAgICAgIGF2ZXJhZ2VcIiByYXRoZXIgdGhhbiBcImhpZ2hseSBleHByZXNzZWRcIi48L2xpPlxuICAgIDxsaT5cIjxiPkVtYmVkZGluZzwvYj5cIjogYSBVTUFQLCBQQ0Egb3IgdC1TTkUgb3ZlciB0aGUgc2FtZSBjZWxscy4gPGI+TGFzc28gaXQgdG9cbiAgICAgIHNlbGVjdCBvbiB0aGUgbWFwPC9iPiDigJQgYm90aCB2aWV3cyByZWFkIHRoZSBzYW1lIHNlbGVjdGlvbi4gT25seSBQQ0EgbGFiZWxzIGl0cyBheGVzXG4gICAgICB3aXRoIGEgcGVyY2VudGFnZSwgYmVjYXVzZSBvbmx5IGl0cyBheGVzIGV4cGxhaW4gYSBtZWFzdXJhYmxlIHNoYXJlIG9mIHRoZSB2YXJpYW5jZTtcbiAgICAgIFVNQVAgYW5kIHQtU05FIGNvb3JkaW5hdGVzIGFyZSBhcmJpdHJhcnksIHNvIGRpc3RhbmNlIGJldHdlZW4gZGlzdGFudCBjbHVzdGVyc1xuICAgICAgc2hvdWxkIG5vdCBiZSByZWFkLjwvbGk+XG4gICAgPGxpPlwiPGI+RGV0YWNoPC9iPlwiOiBtb3ZlcyB0aGUgY2hhcnRzIGludG8gdGhlaXIgb3duIHdpbmRvdyBzbyB0aGV5IGNhbiBzaXQgYmVzaWRlIHRoZVxuICAgICAgbWFwIGluc3RlYWQgb2YgaW5zaWRlIHRoZSBwYW5lbC4gU3dpdGNoaW5nIHRhYnMga2VlcHMgdGhlIHdpbmRvdyB3aGVyZSBpdCBpcy48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5IZWxwPC9oMz5cbiAgPHA+XCI8Yj5IZWxwPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktcXVlc3Rpb24tY2lyY2xlXCI+PC9pPjogb3BlbnMgdGhpcyBkaWFsb2cuPC9wPlxuXG4gIDxoMz5LZXlib2FyZCBzaG9ydGN1dHM8L2gzPlxuICA8dGFibGUgY2xhc3M9XCJzaG9ydGN1dHMtdGFibGVcIj5cbiAgICA8dHI+PHRkPjxrYmQ+Kzwva2JkPiAvIDxrYmQ+PTwva2JkPjwvdGQ+PHRkPlpvb20gaW48L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPi08L2tiZD48L3RkPjx0ZD5ab29tIG91dDwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+Yjwva2JkPjwvdGQ+PHRkPlRvZ2dsZSB6b29tIGJveCBtb2RlPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5wPC9rYmQ+PC90ZD48dGQ+VG9nZ2xlIHBhbiBtb2RlPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5mPC9rYmQ+PC90ZD48dGQ+VG9nZ2xlIGZyZWVmb3JtIHJlZ2lvbiBkcmF3aW5nPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5sPC9rYmQ+PC90ZD48dGQ+VG9nZ2xlIHBvbHlsaW5lIGRyYXdpbmc8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPnI8L2tiZD48L3RkPjx0ZD5Ub2dnbGUgcmVjdGFuZ3VsYXIgcmVnaW9uIGRyYXdpbmc8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPnc8L2tiZD48L3RkPjx0ZD5Ub2dnbGUgd2FuZCB0b29sPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+V2FuZCArIDxrYmQ+U2hpZnQ8L2tiZD48L3RkPjx0ZD5FcmFzZSBwaXhlbHMgZnJvbSB0aGUgYWN0aXZlIHJlZ2lvbjwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPldhbmQgKyA8a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD48L3RkPjx0ZD5FeGFjdC1tYXRjaCBmbG9vZCBmaWxsIChubyBzbW9vdGhpbmcpPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5lPC9rYmQ+PC90ZD48dGQ+VG9nZ2xlIHZlcnRleCBlcmFzZXI8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPnM8L2tiZD48L3RkPjx0ZD5TZWxlY3Rpb24gbW9kZSAoZGVhY3RpdmF0ZXMgZHJhd2luZyB0b29scyk8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD5Qb2ludCBwcm9tcHQgKyA8a2JkPlNoaWZ0PC9rYmQ+LzxrYmQ+QWx0PC9rYmQ+PC90ZD48dGQ+QWRkIGFuIGV4Y2x1ZGUgcG9pbnQgKHJlZmluZSB0aGUgY3VycmVudCBvYmplY3QpPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5FbnRlcjwva2JkPjwvdGQ+PHRkPkNvbW1pdCB0aGUgY3VycmVudCBTQU0gcG9pbnQgb2JqZWN0PC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5Fc2M8L2tiZD48L3RkPjx0ZD5VbmRvIHRoZSBjdXJyZW50IFNBTSBwb2ludCBvYmplY3Q8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPkRlbDwva2JkPiAvIDxrYmQ+4oyrPC9rYmQ+PC90ZD48dGQ+RGVsZXRlIHNlbGVjdGVkIHJlZ2lvbjwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+Q3RybDwva2JkPi88a2JkPkNtZDwva2JkPis8a2JkPlo8L2tiZD48L3RkPjx0ZD5VbmRvIHRoZSBsYXN0IHJlZ2lvbiBhY3Rpb24gKHVwIHRvIDEwKTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+Q3RybDwva2JkPi88a2JkPkNtZDwva2JkPis8a2JkPlNoaWZ0PC9rYmQ+KzxrYmQ+Wjwva2JkPiAvIDxrYmQ+Q3RybDwva2JkPis8a2JkPlk8L2tiZD48L3RkPjx0ZD5SZWRvIHRoZSBsYXN0IHVuZG9uZSByZWdpb24gYWN0aW9uPC90ZD48L3RyPlxuICA8L3RhYmxlPlxuXG4gIDxuZy10ZW1wbGF0ZSBwVGVtcGxhdGU9XCJmb290ZXJcIj5cbiAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWNoZWNrXCIgKGNsaWNrKT1cImRpc3BsYXlIZWxwRGlhbG9nPWZhbHNlXCIgbGFiZWw9XCJPa1wiIHN0eWxlQ2xhc3M9XCJwLWJ1dHRvbi10ZXh0XCI+XG4gICAgPC9wLWJ1dHRvbj5cbiAgPC9uZy10ZW1wbGF0ZT5cbjwvcC1kaWFsb2c+XG4iXX0=
446
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbGJhci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQWEsTUFBTSxFQUFpQixNQUFNLGVBQWUsQ0FBQztBQUlqRyxPQUFPLEVBQUUsUUFBUSxFQUFjLGlCQUFpQixFQUFFLFVBQVUsRUFBRSxrQkFBa0IsRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLHVCQUF1QixFQUFFLHVCQUF1QixFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3ZPLE9BQU8sRUFBeUIsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUd2RixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sY0FBYyxDQUFDOzs7Ozs7Ozs7Ozs7OztBQUUxQzs7Ozs7Ozs7Ozs7R0FXRztBQVFILE1BQU0sT0FBTyxnQkFBZ0I7SUFQN0I7UUFVRSw0RUFBNEU7UUFDbkUsb0JBQWUsR0FBcUIsRUFBRSxDQUFDO1FBQ2hELHdFQUF3RTtRQUMvRCxxQkFBZ0IsR0FBZSxRQUFRLENBQUMsS0FBSyxDQUFDO1FBQ3ZEOzs7Ozs7V0FNRztRQUNNLGlCQUFZLEdBQW9CLElBQUksQ0FBQztRQUM5Qzs0RUFDb0U7UUFDM0QsYUFBUSxHQUFhLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzlCLGFBQVEsR0FBRyxDQUFDLENBQUM7UUFDYixXQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ3BCLGtEQUFrRDtRQUN6QyxjQUFTLEdBQUcsSUFBSSxDQUFDO1FBQzFCOzs7OztXQUtHO1FBQ00sZ0JBQVcsR0FBRyxLQUFLLENBQUM7UUFDcEIsbUJBQWMsR0FB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O2dCQUNHLGFBQWE7c0JBQXRCLE1BQU07Z0JBQ0csY0FBYztzQkFBdkIsTUFBTTtnQkFDRyxjQUFjO3NCQUF2QixNQUFNO2dCQUNHLE1BQU07c0JBQWYsTUFBTTtnQkFDRyxPQUFPO3NCQUFoQixNQUFNO2dCQUNHLG1CQUFtQjtzQkFBNUIsTUFBTTtnQkFDRyxrQkFBa0I7c0JBQTNCLE1BQU07Z0JBRUcsVUFBVTtzQkFBbkIsTUFBTTtnQkFFRyxlQUFlO3NCQUF4QixNQUFNO2dCQUVHLGdCQUFnQjtzQkFBekIsTUFBTTtnQkFDRyxZQUFZO3NCQUFyQixNQUFNO2dCQUVHLFVBQVU7c0JBQW5CLE1BQU07Z0JBRUcsVUFBVTtzQkFBbkIsTUFBTTtnQkFFRyxjQUFjO3NCQUF2QixNQUFNO2dCQUVHLGVBQWU7c0JBQXhCLE1BQU07Z0JBRUcsT0FBTztzQkFBaEIsTUFBTTtnQkFFRyxnQkFBZ0I7c0JBQXpCLE1BQU07Z0JBRUcsZUFBZTtzQkFBeEIsTUFBTTtnQkFFRyxjQUFjO3NCQUF2QixNQUFNO2dCQUVHLGNBQWM7c0JBQXZCLE1BQU07Z0JBQ0cscUJBQXFCO3NCQUE5QixNQUFNO2dCQUNHLGVBQWU7c0JBQXhCLE1BQU07Z0JBQ0csd0JBQXdCO3NCQUFqQyxNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIElucHV0LCBPbkNoYW5nZXMsIE91dHB1dCwgU2ltcGxlQ2hhbmdlcyB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgTWVudUl0ZW0gfSBmcm9tICdwcmltZW5nL2FwaSc7XG5cbmltcG9ydCB7IElJbWFnZUluZm8gfSBmcm9tICcuLi9jb250cmFjdHMvaW1hZ2UuY29udHJhY3QnO1xuaW1wb3J0IHsgUGxvdFR5cGUsIFBsb3RUeXBlSWQsIGlzQnVpbHRpblBsb3RUeXBlLCBpc05hcGFyaTNkLCBpc05hcGFyaUlzb3N1cmZhY2UsIGlzTmFwYXJpU3VyZmFjZSwgaXNOYXBhcmlTY2F0dGVyLCBOQVBBUklfREVDSU1BVEVfT1BUSU9OUywgTkFQQVJJX0RFRkFVTFRfREVDSU1BVEUsIGlzU3BhdGlhbE9taWNzLCBpc1NwYXRpYWxPbWljczNkIH0gZnJvbSAnLi4vY29udHJhY3RzL3Bsb3QtdHlwZSc7XG5pbXBvcnQgeyBUb29sYmFyVG9vbFZpc2liaWxpdHksIEFMTF9UT09MQkFSX1RPT0xTIH0gZnJvbSAnLi4vY29udHJhY3RzL3Rvb2xiYXItY29uZmlnJztcbmltcG9ydCB7IFRvb2xiYXJEaWFsb2dUb29sQ29udHJpYnV0aW9uLCBUb29sYmFyVG9vbENvbnRyaWJ1dGlvbiB9IGZyb20gJy4uL2NvbnRyYWN0cy90b29sYmFyLXRvb2wuY29udHJhY3QnO1xuaW1wb3J0IHsgUGxvdFR5cGVPcHRpb24gfSBmcm9tICcuLi9jb250cmFjdHMvcGxvdC10eXBlLWNvbnRyaWJ1dGlvbi5jb250cmFjdCc7XG5pbXBvcnQgeyBNT0RFTF9JTkZPIH0gZnJvbSAnLi9tb2RlbC1pbmZvJztcblxuLyoqXG4gKiBQcmVzZW50YXRpb25hbCB0b29sYmFyIGZvciB0aGUgcGxvdHRpbmcgdmlld3BvcnQuXG4gKlxuICogUmVuZGVycyB0aGUgcGxvdC10eXBlIHNlbGVjdG9yLCBjb2xvcm1hcC9MVVQgY29udHJvbHMsIHN0YWNrIG5hdmlnYXRpb24sXG4gKiBpbWFnZSBhY3Rpb25zIChkb3dubG9hZCAvIGF1dG9zY2FsZSAvIHBpcGVsaW5lKSwgdmlld3BvcnQgem9vbS9wYW4sIHRoZVxuICogU3VyZmFjZS0zRCBjYW1lcmEgY29udHJvbHMsIGFuZCB0aGUgcmVnaW9uLWRyYXdpbmcgdG9vbHMg4oCUIHBsdXMgdGhlIHBsb3R0aW5nXG4gKiBoZWxwIGRpYWxvZy4gSXQgb3ducyBubyByZW5kZXJpbmcgb3IgdmlzdWFsaXphdGlvbiBzdGF0ZTogdGhlIGhvc3RcbiAqIChWaXN1YWxpemVyQ29tcG9uZW50KSBzdGF5cyB0aGUgb3JjaGVzdHJhdG9yLCBwYXNzaW5nIHN0YXRlIGluIHZpYSBgQElucHV0YCBhbmRcbiAqIGhhbmRsaW5nIGV2ZXJ5IGFjdGlvbiB2aWEgYEBPdXRwdXRgLiBUaGUgc2FtZSBhY3Rpb25zIGFyZSBhbHNvIGRyaXZlbiBieSB0aGVcbiAqIGRpYWdyYW0ncyByaWdodC1jbGljayBjb250ZXh0IG1lbnUgYW5kIGtleWJvYXJkIHNob3J0Y3V0cywgc28ga2VlcGluZyB0aGVcbiAqIGhhbmRsZXJzIGluIHRoZSBob3N0IGF2b2lkcyBkdXBsaWNhdGluZyB0aGF0IGxvZ2ljLlxuICovXG5AQ29tcG9uZW50KHtcbiAgLy8gQ2Fub25pY2FsIHByZWZpeGVkIHNlbGVjdG9yIGZpcnN0OyB0aGUgdW5wcmVmaXhlZCBvcmlnaW5hbCBpcyBrZXB0IGFzIGFuXG4gIC8vIGFsaWFzIGZvciBvbmUgcmVsZWFzZSAocHJlLXB1YmxpY2F0aW9uIGJhY2stY29tcGF0KS5cbiAgc2VsZWN0b3I6ICdwbG90dGluZy10b29sYmFyJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3Rvb2xiYXIuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi90b29sYmFyLmNvbXBvbmVudC5zY3NzJ10sXG59KVxuZXhwb3J0IGNsYXNzIFRvb2xiYXJDb21wb25lbnQgaW1wbGVtZW50cyBPbkNoYW5nZXMge1xuICAvKiogQ3VycmVudCBpbWFnZSAoZ2F0ZXMgd2hpY2ggY29udHJvbCBncm91cHMgYXJlIHNob3duKS4gKi9cbiAgQElucHV0KCkgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkO1xuICAvKiogUGxvdCB0eXBlcyB0aGUgYWN0aXZlIGJhY2tlbmQgYWR2ZXJ0aXNlcywgdGhlbiBhbnkgY29udHJpYnV0ZWQgbW9kZXMuICovXG4gIEBJbnB1dCgpIHBsb3RUeXBlT3B0aW9uczogUGxvdFR5cGVPcHRpb25bXSA9IFtdO1xuICAvKiogVGhlIHNlbGVjdG9yJ3MgdmFsdWU6IGEgYnVpbHQtaW4gdHlwZSBvciBhIGNvbnRyaWJ1dGVkIG1vZGUncyBpZC4gKi9cbiAgQElucHV0KCkgc2VsZWN0ZWRQbG90VHlwZTogUGxvdFR5cGVJZCA9IFBsb3RUeXBlLklNQUdFO1xuICAvKipcbiAgICogVGhlIGJ1aWx0LWluIHR5cGUgdGhhdCByZW5kZXJzIHtAbGluayBzZWxlY3RlZFBsb3RUeXBlfSDigJQgaXRzIGBiYXNlVHlwZWBcbiAgICogZm9yIGEgY29udHJpYnV0ZWQgbW9kZS4gRXZlcnkgdG9vbC9jb250cm9sIGRlY2lzaW9uIGJlbG93IHJlYWRzXG4gICAqIHtAbGluayBlZmZlY3RpdmVQbG90VHlwZX0sIHNvIGEgY29udHJpYnV0ZWQgbW9kZSBnZXRzIGV4YWN0bHkgaXRzIGJhc2VcbiAgICogdHlwZSdzIHRvb2xiYXIuIFdoZW4gdW5ib3VuZCwgYSBidWlsdC1pbiBzZWxlY3Rpb24gaXMgaXRzIG93biBiYXNlIGFuZFxuICAgKiBhbnl0aGluZyBlbHNlIGlzIHRyZWF0ZWQgYXMgdGhlIGRlZmF1bHQgSW1hZ2Ugdmlldy5cbiAgICovXG4gIEBJbnB1dCgpIGJhc2VQbG90VHlwZTogUGxvdFR5cGUgfCBudWxsID0gbnVsbDtcbiAgLyoqIElzb3N1cmZhY2UgYmFuZCBhcyBhIDDigJMyNTUgc2xpZGVyIHBvc2l0aW9uIChtYXBwZWQgb250byB0aGUgdm9sdW1lJ3MgcmVhbFxuICAgKiAgaW50ZW5zaXR5IHJhbmdlIGJ5IHRoZSByZW5kZXJlcikuIERlZmF1bHRzIHRvIHRoZSBmdWxsIHJhbmdlLiAqL1xuICBASW5wdXQoKSBpc29SYW5nZTogbnVtYmVyW10gPSBbMCwgMjU1XTtcbiAgQElucHV0KCkgbWF4SW5kZXggPSAwO1xuICBASW5wdXQoKSB6SW5kZXggPSAwO1xuICAvKiogVHJ1ZSBmb3IgYW55IDJEIChub24gU3VyZmFjZS0zRCkgcGxvdCB0eXBlLiAqL1xuICBASW5wdXQoKSBpc0hlYXRtYXAgPSB0cnVlO1xuICAvKipcbiAgICogUmVnaW9uIHRvb2xzIGFwcGx5IGV2ZW4gdGhvdWdoIHRoaXMgaXMgYSAzRCB2aWV3LCBiZWNhdXNlIHRoZXkgZHJhdyBhXG4gICAqIFNDUkVFTi1TUEFDRSBsYXNzbyAodGhlIDNEIHNwYXRpYWwgY2xvdWQpLiBTZXBhcmF0ZSBmcm9tIHtAbGluayBpc0hlYXRtYXB9LFxuICAgKiB3aGljaCBtZWFucyBcIjJEIHZpZXdcIiBhbmQgYWxzbyBkcml2ZXMgdGhlIHpvb20gdG9vbHMgYW5kIHRoZSAzRCBjYW1lcmFcbiAgICogY29udHJvbHMg4oCUIGZsaXBwaW5nIHRoYXQgd291bGQgc2hvdyAyRCBwYW4vem9vbSBhbmQgaGlkZSB0aGUgb3JiaXQgY29udHJvbHMuXG4gICAqL1xuICBASW5wdXQoKSBpczNkUmVnaW9ucyA9IGZhbHNlO1xuICBASW5wdXQoKSBhY3RpdmVEcmFnTW9kZTogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIEBJbnB1dCgpIGFjdGl2ZVN1cmZhY2UzZE1vZGUgPSAndHVybnRhYmxlJztcbiAgLyoqIFdoZXRoZXIgdGhlIG5hcGFyaSAzRCBheGVzL3NjYWxlIGdpem1vIGlzIGN1cnJlbnRseSBzaG93biAoZHJpdmVzIHRoZSB0b2dnbGUncyBsb29rKS4gKi9cbiAgQElucHV0KCkgYXhlc1Zpc2libGUgPSB0cnVlO1xuICBASW5wdXQoKSB3aXJlZnJhbWVBY3RpdmUgPSBmYWxzZTtcbiAgLyoqIEFjdGl2ZSBuYXBhcmkgM0QgZGVjaW1hdGUgZmFjdG9yICgxID0gRnVsbCDigKYgOCA9IOKFmzsgZGVmYXVsdCDCvSkuICovXG4gIEBJbnB1dCgpIHJlc29sdXRpb25TY2FsZSA9IE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFO1xuICAvKiogRGVjaW1hdGUtZmFjdG9yIG9wdGlvbnMgZm9yIHRoZSBSZXNvbHV0aW9uIGRyb3Bkb3duLiAqL1xuICByZWFkb25seSBkZWNpbWF0ZU9wdGlvbnMgPSBOQVBBUklfREVDSU1BVEVfT1BUSU9OUztcbiAgQElucHV0KCkgd2FuZFNlbnNpdGl2aXR5ID0gMi4wO1xuICBASW5wdXQoKSBicnVzaFNpemUgPSA0MDtcbiAgQElucHV0KCkgdmVydGV4RXJhc2VyUmFkaXVzID0gMjA7XG4gIC8qKiBXaGV0aGVyIGEgcmVnaW9uIGFjdGlvbiBpcyBhdmFpbGFibGUgdG8gdW5kbyAoaml0LXVpIzg1KS4gR3JleXMgb3V0IHRoZVxuICAgKiAgVW5kbyBidXR0b24gd2hlbiBmYWxzZSAobm90aGluZyBkb25lIHlldCwgb3IgYWxsIHN0ZXBzIGFscmVhZHkgdW5kb25lKS4gKi9cbiAgQElucHV0KCkgY2FuVW5kbyA9IGZhbHNlO1xuICAvKiogV2hldGhlciBhbiB1bmRvbmUgcmVnaW9uIGFjdGlvbiBpcyBhdmFpbGFibGUgdG8gcmVkbyAoaml0LXVpIzg1KS4gR3JleXNcbiAgICogIG91dCB0aGUgUmVkbyBidXR0b24gd2hlbiBmYWxzZS4gKi9cbiAgQElucHV0KCkgY2FuUmVkbyA9IGZhbHNlO1xuICBASW5wdXQoKSBzdGFja09wdGlvbnM6IHsgbmFtZTogc3RyaW5nOyB2YWw6IHN0cmluZyB9W10gPVxuICAgIFt7IG5hbWU6ICdTaW5nbGUgaW1hZ2UnLCB2YWw6ICdmYWxzZScgfSwgeyBuYW1lOiAnU3RhY2snLCB2YWw6ICd0cnVlJyB9XTtcbiAgLyoqIFdoaWNoIHRvb2xiYXIgZ3JvdXBzIHRvIHNob3cuIERlZmF1bHRzIHRvIHRoZSBmdWxsIHRvb2xiYXI7IHRoZSBob3N0IGZvcndhcmRzXG4gICAqICB0aGUgY29uc3VtZXIncyBjaG9pY2UgKGUuZy4gdGhlIHBpcGVsaW5lIHNob3dzIG9ubHkgem9vbSArIHJlZ2lvbiB0b29scykuICovXG4gIEBJbnB1dCgpIHRvb2xzOiBSZXF1aXJlZDxUb29sYmFyVG9vbFZpc2liaWxpdHk+ID0gQUxMX1RPT0xCQVJfVE9PTFM7XG4gIC8qKiBDdXJyZW50IGltYWdlLXNtb290aGluZyBzdGF0ZSAoZHJpdmVzIHRoZSBTbW9vdGhlbiB0b2dnbGUgYnV0dG9uIGxvb2spLlxuICAgKiAgYGZhbHNlYCA9IG5lYXJlc3QtbmVpZ2hib3VyIChjcmlzcCByYXcgcGl4ZWxzKSwgdGhlIGRlZmF1bHQuICovXG4gIEBJbnB1dCgpIGltYWdlU21vb3RoaW5nRW5hYmxlZCA9IGZhbHNlO1xuICAvKiogU0FNIG1vZGVsIHBpY2tlciBvcHRpb25zICsgY3VycmVudCBzZWxlY3Rpb24gKGppdC11aSM5MCBQMSkuICovXG4gIEBJbnB1dCgpIHNhbU1vZGVsczogeyBpZDogc3RyaW5nOyBsYWJlbDogc3RyaW5nIH1bXSA9IFtdO1xuICBASW5wdXQoKSBzYW1Nb2RlbElkID0gJyc7XG4gIC8qKlxuICAgKiBUb29scyBjb250cmlidXRlZCB0aHJvdWdoIHtAbGluayBUT09MQkFSX1RPT0xTfSwgYWxyZWFkeSBmaWx0ZXJlZCB0byB0aGVcbiAgICogb25lcyB3aXRoIG1vZGVscyBhbmQgc29ydGVkLiBUaGUgaG9zdCByZXNvbHZlcyB0aGVtIHNvIGl0IGNhbiBvd24gdGhlXG4gICAqIHBlci10b29sIHBhcmFtZXRlciBzdGF0ZTsgdGhpcyBjb21wb25lbnQgb25seSBkcmF3cyB0aGVtLlxuICAgKi9cbiAgQElucHV0KCkgY29udHJpYnV0ZWRUb29sczogVG9vbGJhclRvb2xDb250cmlidXRpb25bXSA9IFtdO1xuICAvKiogRGlhbG9nIHRvb2xzIChUT09MQkFSX1RPT0xTLCBraW5kOiAnZGlhbG9nJyksIHNvcnRlZDsgc2hvd24gaW4gdGhlIEltYWdlIHZpZXcuICovXG4gIEBJbnB1dCgpIGRpYWxvZ1Rvb2xzOiBUb29sYmFyRGlhbG9nVG9vbENvbnRyaWJ1dGlvbltdID0gW107XG4gIC8qKiBJZCBvZiB0aGUgZGlhbG9nIHRvb2wgd2hvc2UgZGlhbG9nIGlzIG9wZW4sIG9yIG51bGwuICovXG4gIEBJbnB1dCgpIG9wZW5EaWFsb2dUb29sSWQ6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAvKiogQWN0aXZlIGNoZWNrcG9pbnQgcGVyIGNvbnRyaWJ1dGVkIHRvb2wsIGtleWVkIGJ5IHRvb2wgaWQuICovXG4gIEBJbnB1dCgpIHRvb2xNb2RlbElkczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuXG4gIEBPdXRwdXQoKSBzZWxlY3RQbG90VHlwZSA9IG5ldyBFdmVudEVtaXR0ZXI8UGxvdFR5cGVJZD4oKTtcbiAgLyoqIEludGVuc2l0eSAoTElORSkgbW9kZTogYWRkIGFub3RoZXIgY29sb3JlZCBsaW5lIFJPSSArIGluc2V0IHRyYWNlLiAqL1xuICBAT3V0cHV0KCkgYWRkUHJvZmlsZUxpbmUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBUb2dnbGUgaW1hZ2Ugc21vb3RoaW5nIChiaWxpbmVhcikgdnMgbmVhcmVzdC1uZWlnaGJvdXIgKGNyaXNwIHBpeGVscykuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVJbWFnZVNtb290aGluZyA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLy8gUHJpbWVORyBzbGlkZXIvaW5wdXROdW1iZXIgY2hhbmdlIGV2ZW50cyBjYXJyeSBvcHRpb25hbCB2YWx1ZXMsIG1pcnJvcmVkIGJ5XG4gIC8vIHRoZSBob3N0IGhhbmRsZXJzICh3aGljaCBhY2NlcHQgYHwgdW5kZWZpbmVkYCkuXG4gIEBPdXRwdXQoKSBpc29SYW5nZUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyW10gfCB1bmRlZmluZWQ+KCk7XG4gIC8qKiBPcGVuIHRoZSBDaGFubmVscyAmIEhpc3RvZ3JhbSBkaWFsb2cgKGJyaWdodG5lc3MvY29udHJhc3QsIGNvbG9ybWFwLCDigKYpLiAqL1xuICBAT3V0cHV0KCkgb3BlbkNoYW5uZWxIaXN0b2dyYW0gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBPcGVuIHRoZSBzcGF0aWFsLW9taWNzIGNvbnRyb2xzIChjb2xvdXItYnksIGxlZ2VuZCwgcG9pbnQgZGlzcGxheSkuICovXG4gIEBPdXRwdXQoKSBvcGVuU3BhdGlhbENvbnRyb2xzID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgb3BlblJlZ2lvbkVkaXRvciA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIHNlbGVjdFN0YWNrT3B0aW9uID0gbmV3IEV2ZW50RW1pdHRlcjx7IG5hbWU6IHN0cmluZzsgdmFsOiBzdHJpbmcgfT4oKTtcbiAgQE91dHB1dCgpIHpTY3J1YiA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyIHwgdW5kZWZpbmVkPigpO1xuICBAT3V0cHV0KCkgelNsaWRlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIEBPdXRwdXQoKSB6SW5kZXhJbnB1dCA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyPigpO1xuICBAT3V0cHV0KCkgcmVsb2FkQW5kUGxvdCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIGRvd25sb2FkSW1hZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSBhdXRvc2NhbGVJbWFnZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIHRvZ2dsZURyYWdNb2RlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIEBPdXRwdXQoKSB6b29tSW4gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSB6b29tT3V0ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgdG9nZ2xlU3VyZmFjZTNkTW9kZSA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuICBAT3V0cHV0KCkgcmVzZXRTdXJmYWNlQ2FtZXJhID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogVG9nZ2xlIHRoZSBuYXBhcmkgM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gb24vb2ZmLiAqL1xuICBAT3V0cHV0KCkgdG9nZ2xlQXhlcyA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFRvZ2dsZSB0aGUgbmFwYXJpIHN1cmZhY2Ugd2lyZWZyYW1lIChlZGdlcykgb24vb2ZmLiAqL1xuICBAT3V0cHV0KCkgdG9nZ2xlV2lyZWZyYW1lID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogQ2hhbmdlIHRoZSBuYXBhcmkgM0QgZGVjaW1hdGUgZmFjdG9yIChyZWxvYWRzIGF0IHRoZSBuZXcgcmVzb2x1dGlvbikuICovXG4gIEBPdXRwdXQoKSBzZWxlY3RSZXNvbHV0aW9uID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG4gIEBPdXRwdXQoKSBkZWxldGVSZWdpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBVbmRvIHRoZSBsYXN0IHJlZ2lvbiBhY3Rpb24gKGppdC11aSM4NSkuICovXG4gIEBPdXRwdXQoKSB1bmRvUmVnaW9uID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogUmVkbyB0aGUgbGFzdCB1bmRvbmUgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gKi9cbiAgQE91dHB1dCgpIHJlZG9SZWdpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBSdW4gYm94LXByb21wdGVkIFNBTSBzZWdtZW50YXRpb24gb24gdGhlIGRyYXduIHJlY3RhbmdsZXMgKGppdC11aSM5MCkuICovXG4gIEBPdXRwdXQoKSBzZWdtZW50UmVnaW9ucyA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biBjZWxscG9zZS1TQU0gKGF1dG8pIGluc2lkZSBlYWNoIGRyYXduIHJlY3RhbmdsZSdzIGNyb3AgKGppdC11aSM5MCkuICovXG4gIEBPdXRwdXQoKSBzZWdtZW50Q2VsbHBvc2UgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBSdW4gYSBjb250cmlidXRlZCB0b29sIG92ZXIgdGhlIGN1cnJlbnQgdmlldywgYnkgdG9vbCBpZC4gKi9cbiAgQE91dHB1dCgpIHJ1blRvb2wgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgLyoqIEEgZGlhbG9nIHRvb2wncyBidXR0b24gd2FzIGNsaWNrZWQ6IG9wZW4gaXRzIGRpYWxvZywgb3IgY2xvc2UgaXQgaWYgb3Blbi4gKi9cbiAgQE91dHB1dCgpIHRvZ2dsZURpYWxvZ1Rvb2wgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgLyoqIFBpY2sgYSBjb250cmlidXRlZCB0b29sJ3MgY2hlY2twb2ludC4gKi9cbiAgQE91dHB1dCgpIHRvb2xNb2RlbENoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8eyB0b29sSWQ6IHN0cmluZzsgbW9kZWxJZDogc3RyaW5nIH0+KCk7XG4gIC8qKiBPcGVuIGEgY29udHJpYnV0ZWQgdG9vbCdzIHBhcmFtZXRlciBkaWFsb2csIGJ5IHRvb2wgaWQuICovXG4gIEBPdXRwdXQoKSBvcGVuVG9vbFBhcmFtcyA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuICAvKiogU0FNIG1vZGVsIHBpY2tlciAoaml0LXVpIzkwIFAxKS4gKi9cbiAgQE91dHB1dCgpIHNhbU1vZGVsQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIEBPdXRwdXQoKSB3YW5kU2Vuc2l0aXZpdHlDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcbiAgQE91dHB1dCgpIGJydXNoU2l6ZUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyIHwgdW5kZWZpbmVkPigpO1xuICBAT3V0cHV0KCkgdmVydGV4RXJhc2VyUmFkaXVzQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG5cbiAgLyoqIFNsaWRlcnMnIGJvdW5kcyDigJQgdG9vbGJhciBVSSBjb25zdGFudHMgKHRoZSB2YWx1ZXMgYXJlIGhvc3Qtb3duZWQpLiAqL1xuICByZWFkb25seSB3YW5kU2Vuc2l0aXZpdHlNaW4gPSAwLjU7XG4gIHJlYWRvbmx5IHdhbmRTZW5zaXRpdml0eU1heCA9IDEwLjA7XG4gIHJlYWRvbmx5IHdhbmRTZW5zaXRpdml0eVN0ZXAgPSAwLjE7XG4gIHJlYWRvbmx5IGJydXNoU2l6ZU1pbiA9IDU7XG4gIHJlYWRvbmx5IGJydXNoU2l6ZU1heCA9IDMwMDtcbiAgcmVhZG9ubHkgYnJ1c2hTaXplU3RlcCA9IDU7XG4gIHJlYWRvbmx5IHZlcnRleEVyYXNlclJhZGl1c01pbiA9IDU7XG4gIHJlYWRvbmx5IHZlcnRleEVyYXNlclJhZGl1c01heCA9IDMwMDtcbiAgcmVhZG9ubHkgdmVydGV4RXJhc2VyUmFkaXVzU3RlcCA9IDU7XG4gIHJlYWRvbmx5IGlzb1ZhbHVlTWluID0gMDtcbiAgcmVhZG9ubHkgaXNvVmFsdWVNYXggPSAyNTU7XG4gIHJlYWRvbmx5IGlzb1ZhbHVlU3RlcCA9IDE7XG5cbiAgZGlzcGxheUhlbHBEaWFsb2cgPSBmYWxzZTtcblxuICAvKiogTW9kZWwgcGlja2VyIGZvciB0aGUgU2VnbWVudCBidXR0b24ncyBkcm9wZG93biBtZW51LiBUaGUgYWN0aXZlIG1vZGVsXG4gICAqICAoYHNhbU1vZGVsSWRgKSBpcyBtYXJrZWQgd2l0aCBhIGNoZWNrOyBzZWxlY3RpbmcgYW4gaXRlbSBlbWl0c1xuICAgKiAgYHNhbU1vZGVsQ2hhbmdlYCAoaml0LXVpIzkwIFAxKS5cbiAgICpcbiAgICogIEhlbGQgYXMgYSBzdGFibGUgYXJyYXkgKHJlYnVpbHQgb25seSB3aGVuIGBzYW1Nb2RlbHNgL2BzYW1Nb2RlbElkYCBjaGFuZ2UpLFxuICAgKiAgTk9UIGEgZ2V0dGVyOiBhIGdldHRlciByZXR1cm5zIGEgZnJlc2ggYXJyYXkgd2l0aCBuZXcgYGNvbW1hbmRgIGNsb3N1cmVzIG9uXG4gICAqICBldmVyeSBjaGFuZ2UtZGV0ZWN0aW9uIHRpY2ssIHdoaWNoIG1ha2VzIHRoZSBib3VuZCBgcC1tZW51YCBvdmVybGF5IHJlYnVpbGRcbiAgICogIGl0cyBET00gbWlkLWludGVyYWN0aW9uIGFuZCBzd2FsbG93IHRoZSBjbGljayBvbiBhIG1lbnUgaXRlbS5cbiAgICpcbiAgICogIGB0b29sdGlwYCBjYXJyaWVzIHRoZSBtb2RlbCdzIGRlc2NyaXB0aW9uIGZyb20ge0BsaW5rIE1PREVMX0lORk99OyB0aGVcbiAgICogIG1lbnVzJyBzaGFyZWQgaXRlbSB0ZW1wbGF0ZSB0dXJucyBpdCBpbnRvIHRoZSBob3ZlciBpbmZvIGljb24uIHAtbWVudSdzIG93blxuICAgKiAgcmVuZGVyaW5nIGlnbm9yZXMgdGhlIGZpZWxkIChpdCByZWFkcyBgaXRlbS50aXRsZWApLCBzbyBpdCBpcyBmcmVlIHRvIHVzZS4gKi9cbiAgc2FtTWVudUl0ZW1zOiBNZW51SXRlbVtdID0gW107XG4gIC8qKlxuICAgKiBNb2RlbCBtZW51cyBmb3IgdGhlIGNvbnRyaWJ1dGVkIHRvb2xzLCBrZXllZCBieSB0b29sIGlkLiBTYW1lIHN0YWJsZS1hcnJheVxuICAgKiByZWFzb25pbmcgYXMge0BsaW5rIHNhbU1lbnVJdGVtc30g4oCUIGFuZCBpdCBpcyB3aHkgdGhpcyBpcyBhIG1hcCByZWJ1aWx0IGluXG4gICAqIGBuZ09uQ2hhbmdlc2AgcmF0aGVyIHRoYW4gYSBtZXRob2QgY2FsbGVkIGZyb20gdGhlIHRlbXBsYXRlLCB3aGljaCB3b3VsZFxuICAgKiBoYW5kIGBwLW1lbnVgIGEgbmV3IGFycmF5IGV2ZXJ5IHRpY2suXG4gICAqXG4gICAqIEEgY29udHJpYnV0ZWQgdG9vbCBzdXBwbGllcyBpdHMgb3duIHBlci1tb2RlbCBkZXNjcmlwdGlvbiwgc28gdW5saWtlIHRoZVxuICAgKiBTQU0gbWVudSB0aGVzZSBkbyBub3QgY29uc3VsdCB7QGxpbmsgTU9ERUxfSU5GT306IHRoaXMgbGlicmFyeSBoYXMgbm9cbiAgICogY29weSBmb3IgY2hlY2twb2ludHMgaXQgZG9lcyBub3Qga25vdyBhYm91dC5cbiAgICovXG4gIHRvb2xNZW51SXRlbXM6IFJlY29yZDxzdHJpbmcsIE1lbnVJdGVtW10+ID0ge307XG5cbiAgLyoqIFJlYnVpbGQgdGhlIFNBTSBtb2RlbCBtZW51IHdoZW4gdGhlIG1vZGVsIGxpc3Qgb3IgYWN0aXZlIHNlbGVjdGlvblxuICAgKiAgY2hhbmdlcyAoa2VlcHMgdGhlIGFycmF5IHJlZmVyZW5jZSBzdGFibGUgYWNyb3NzIG90aGVyIENEIHRpY2tzKS4gKi9cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIGlmIChjaGFuZ2VzWydzYW1Nb2RlbHMnXSB8fCBjaGFuZ2VzWydzYW1Nb2RlbElkJ10pIHtcbiAgICAgIHRoaXMuc2FtTWVudUl0ZW1zID0gdGhpcy5zYW1Nb2RlbHMubWFwKChtKSA9PiAoe1xuICAgICAgICBsYWJlbDogbS5sYWJlbCxcbiAgICAgICAgaWNvbjogbS5pZCA9PT0gdGhpcy5zYW1Nb2RlbElkID8gJ3BpIHBpLWNoZWNrJyA6ICdwaSBwaS1mdycsXG4gICAgICAgIHRvb2x0aXA6IE1PREVMX0lORk9bbS5pZF0sXG4gICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2FtTW9kZWxDaGFuZ2UuZW1pdChtLmlkKSxcbiAgICAgIH0pKTtcbiAgICB9XG4gICAgaWYgKGNoYW5nZXNbJ2NvbnRyaWJ1dGVkVG9vbHMnXSB8fCBjaGFuZ2VzWyd0b29sTW9kZWxJZHMnXSkge1xuICAgICAgdGhpcy50b29sTWVudUl0ZW1zID0ge307XG4gICAgICBmb3IgKGNvbnN0IHRvb2wgb2YgdGhpcy5jb250cmlidXRlZFRvb2xzKSB7XG4gICAgICAgIGNvbnN0IGFjdGl2ZSA9IHRoaXMudG9vbE1vZGVsSWRzW3Rvb2wuaWRdID8/IHRvb2wuZGVmYXVsdE1vZGVsSWQoKTtcbiAgICAgICAgdGhpcy50b29sTWVudUl0ZW1zW3Rvb2wuaWRdID0gdG9vbC5tb2RlbHMoKS5tYXAoKG0pID0+ICh7XG4gICAgICAgICAgbGFiZWw6IG0ubGFiZWwsXG4gICAgICAgICAgaWNvbjogbS5pZCA9PT0gYWN0aXZlID8gJ3BpIHBpLWNoZWNrJyA6ICdwaSBwaS1mdycsXG4gICAgICAgICAgdG9vbHRpcDogbS5pbmZvLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9vbE1vZGVsQ2hhbmdlLmVtaXQoeyB0b29sSWQ6IHRvb2wuaWQsIG1vZGVsSWQ6IG0uaWQgfSksXG4gICAgICAgIH0pKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU3RyaXAgbWFya3VwIGZyb20gYSBtb2RlbCBkZXNjcmlwdGlvbiBzbyBpdCBjYW4gYmUgcmVhZCBhbG91ZC5cbiAgICpcbiAgICogVGhlIGNvcHkgaW4gTU9ERUxfSU5GTyBhbmQgaW4gYSBjb250cmlidXRpb24ncyBgVG9vbE1vZGVsT3B0aW9uLmluZm9gIGlzXG4gICAqIHdyaXR0ZW4gZm9yIGEgdmlzdWFsIHRvb2x0aXAgcmVuZGVyZWQgd2l0aCBgW2VzY2FwZV09XCJmYWxzZVwiYCwgc28gaXQgY2Fycmllc1xuICAgKiBgPGI+YCBhbmQgYDxicj5gLiBQYXNzZWQgdG8gYGFyaWEtbGFiZWxgIHZlcmJhdGltIGEgc2NyZWVuIHJlYWRlciBhbm5vdW5jZXNcbiAgICogdGhlIHRhZ3MsIHNvIHRoZXkgYXJlIHJlbW92ZWQgYW5kIGA8YnI+YCBiZWNvbWVzIGEgc2VudGVuY2UgYnJlYWsuIEVudGl0aWVzXG4gICAqIHVzZWQgaW4gdGhhdCBjb3B5IChgJm5ic3A7YCwgYCZ0aW1lcztgLCBgJmFtcDtgKSBhcmUgZGVjb2RlZCBmb3IgdGhlIHNhbWVcbiAgICogcmVhc29uLlxuICAgKi9cbiAgcGxhaW5UZXh0KGh0bWw6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIChodG1sID8/ICcnKVxuICAgICAgLnJlcGxhY2UoLzxiclxccypcXC8/Pi9naSwgJy4gJylcbiAgICAgIC5yZXBsYWNlKC88W14+XSs+L2csICcnKVxuICAgICAgLnJlcGxhY2UoLyZuYnNwOy9nLCAnICcpXG4gICAgICAucmVwbGFjZSgvJnRpbWVzOy9nLCAneCcpXG4gICAgICAucmVwbGFjZSgvJmFtcDsvZywgJyYnKVxuICAgICAgLnJlcGxhY2UoL1xccysvZywgJyAnKVxuICAgICAgLnRyaW0oKTtcbiAgfVxuXG4gIC8qKiBDb250cmlidXRlZCB0b29sIG5hbWVzIGZvciB0aGUgaGVscCBkaWFsb2cncyBwcm9tcHRlZC9uby1wcm9tcHQgY29udHJhc3QsXG4gICAqICBlLmcuIFwiWU9MTywgUmV0aW5hbCBsYXllcnNcIi4gRW1wdHkgd2hlbiBub3RoaW5nIGlzIHJlZ2lzdGVyZWQsIHdoaWNoIGlzXG4gICAqICB3aHkgdGhlIHNlbnRlbmNlIHRoYXQgdXNlcyBpdCBpcyBpdHNlbGYgY29uZGl0aW9uYWwuICovXG4gIGdldCBjb250cmlidXRlZFRvb2xOYW1lcygpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmNvbnRyaWJ1dGVkVG9vbHMubWFwKCh0KSA9PiB0LmxhYmVsKS5qb2luKCcsICcpO1xuICB9XG5cbiAgLyoqIEtlZXBzIGVhY2ggY29udHJpYnV0ZWQgdG9vbCdzIGBwLW1lbnVgIG92ZXJsYXkgYWxpdmUgYWNyb3NzIENEIHRpY2tzIOKAlFxuICAgKiAgcmUtY3JlYXRpbmcgaXQgbWlkLWludGVyYWN0aW9uIHN3YWxsb3dzIHRoZSBjbGljayBvbiBhIG1lbnUgaXRlbS4gKi9cbiAgdHJhY2tUb29sQnlJZChfaW5kZXg6IG51bWJlciwgdG9vbDogeyBpZDogc3RyaW5nIH0pOiBzdHJpbmcge1xuICAgIHJldHVybiB0b29sLmlkO1xuICB9XG5cbiAgLyoqIFRoZSBidWlsdC1pbiB0eXBlIHdob3NlIHRvb2xzIGFuZCBjb250cm9scyBhcHBseSB0byB0aGUgY3VycmVudCBzZWxlY3Rpb24uICovXG4gIGdldCBlZmZlY3RpdmVQbG90VHlwZSgpOiBQbG90VHlwZSB7XG4gICAgaWYgKHRoaXMuYmFzZVBsb3RUeXBlKSByZXR1cm4gdGhpcy5iYXNlUGxvdFR5cGU7XG4gICAgcmV0dXJuIGlzQnVpbHRpblBsb3RUeXBlKHRoaXMuc2VsZWN0ZWRQbG90VHlwZSkgPyB0aGlzLnNlbGVjdGVkUGxvdFR5cGUgOiBQbG90VHlwZS5JTUFHRTtcbiAgfVxuXG4gIC8qKiBUaGUgSW1hZ2UgcGxvdCB0eXBlIHJlbmRlcnMgYXMgYSBuYXRpdmVseSBwYW4vem9vbS1hYmxlIHJhc3Rlciwgc28gdGhlXG4gICAqICBnZW5lcmljIHpvb20vcGFuIHRvb2xzIGFyZSBoaWRkZW4uICovXG4gIGdldCBpc0ltYWdlVmlldygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gIH1cblxuICAvKiogRWl0aGVyIHNwYXRpYWwtb21pY3MgbW9kZSBpcyBhY3RpdmUsIHNvIGl0cyBjb250cm9scyBhcmUgd29ydGggb2ZmZXJpbmcuXG4gICAqICBUaGUgM0QgY2xvdWQgbmVlZHMgdGhlIHBhbmVsIE1PUkUgdGhhbiB0aGUgMkQgdmlldyBkb2VzLCBub3QgbGVzczogY29sb3VyaW5nLFxuICAgKiAgdGhlIGxlZ2VuZCBhbmQgY2F0ZWdvcnkgc2hvdy9oaWRlIGFyZSB0aGUgb25seSB3YXkgdG8gbWFrZSBzZW5zZSBvZiBhXG4gICAqICBtaWxsaW9uIG92ZXJsYXBwaW5nIHBvaW50cy4gKi9cbiAgZ2V0IGlzU3BhdGlhbE1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGlzU3BhdGlhbE9taWNzKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpIHx8IGlzU3BhdGlhbE9taWNzM2QodGhpcy5lZmZlY3RpdmVQbG90VHlwZSk7XG4gIH1cblxuICAvKiogQSBwbG90LXR5cGUgaWNvbiBpcyBhIFByaW1lTkcgZm9udCBnbHlwaCAoZS5nLiBgcGkgcGktaW1hZ2VgKSByYXRoZXIgdGhhbiBhblxuICAgKiAgU1ZHIGFzc2V0IHBhdGgg4oCUIGRyaXZlcyB3aGljaCBlbGVtZW50IHRoZSBzZWxlY3RvciBpdGVtIHRlbXBsYXRlIHJlbmRlcnMuICovXG4gIGlzUGlJY29uKGljb246IHN0cmluZyB8IHVuZGVmaW5lZCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAhIWljb24gJiYgaWNvbi5zdGFydHNXaXRoKCdwaSAnKTtcbiAgfVxuXG4gIC8qKiBCYWNrZW5kcyB3aXRoIGEgdmVydGV4LWVkaXRpbmcgcmVnaW9uIG92ZXJsYXk6IE9TRCBJbWFnZSBhbmQgbmFwYXJpLWpzIFdlYkdQVSBpbWFnZVxuICAgKiAgKGppdC11aSMxMDIpLiBHYXRlcyB0aGUgcG9seWdvbiAvIGFkZC12ZXJ0ZXggLyBkZWxldGUtdmVydGV4IC8gQsOpemllci1jb252ZXJ0IHRvb2xzLiAqL1xuICBnZXQgc3VwcG9ydHNSZWdpb25WZXJ0ZXhUb29scygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0UgfHxcbiAgICAgIHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLk5BUEFSSV9JTUFHRVxuICAgICk7XG4gIH1cblxuICAvKiogUGxvdCB0eXBlcyB0aGF0IHNjcnViIGEgei1zdGFjayBsaXZlICh0aGUgcmVuZGVyZXIgc3dhcHMgdGhlIHNsaWNlIGluIHBsYWNlKTogdGhlIE9TRCBJbWFnZVxuICAgKiAgdmlldywgdGhlIG5hcGFyaS1qcyBXZWJHUFUgaW1hZ2UsIHRoZSBuYXBhcmktanMgc3VyZmFjZSDigJQgd2hpY2ggcmVidWlsZHMgdGhlIGhlaWdodCBmaWVsZFxuICAgKiAgZnJvbSB0aGUgcGlja2VkIHNsaWNlIChqaXQtdWkjMTAyKSDigJQgYW5kIHRoZSAyRCBzcGF0aWFsLW9taWNzIHZpZXcsIHdoZXJlIGEgM0QgZGF0YXNldCdzXG4gICAqICByZWdpc3RlcmVkIHZvbHVtZSBpcyB0aGUgaW1hZ2UgYW5kIHRoZSBzbGlkZXIgcGlja3MgdGhlIHNlY3Rpb24gd2hvc2Ugb2JzZXJ2YXRpb25zIGFyZVxuICAgKiAgZHJhd24uIERyaXZlcyB0aGUgcGVyLXNsaWNlIHNsaWRlci4gKi9cbiAgZ2V0IHNob3dzTGl2ZVNsaWNlU2NydWJiZXIoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChcbiAgICAgIHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLklNQUdFIHx8XG4gICAgICB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5OQVBBUklfSU1BR0UgfHxcbiAgICAgIGlzTmFwYXJpU3VyZmFjZSh0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKSB8fFxuICAgICAgaXNOYXBhcmlTY2F0dGVyKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpIHx8XG4gICAgICBpc1NwYXRpYWxPbWljcyh0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKVxuICAgICk7XG4gIH1cblxuICAvKiogQW55IG5hcGFyaS1qcyBXZWJHUFUgcGxvdCB0eXBlLiBUaGUgc2luZ2xlLWltYWdlL3N0YWNrIHRvZ2dsZSArIHNsaWNlLW51bWJlciBmaWVsZCBhcmVcbiAgICogIFBsb3RseS1vbmx5IHN0YWNrIGNvbnRyb2xzLCBzbyB0aGV5J3JlIGhpZGRlbiBmb3IgbmFwYXJpIChqaXQtdWkjMTAyKS4gKi9cbiAgZ2V0IGlzTmFwYXJpTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFIHx8XG4gICAgICBpc05hcGFyaVNjYXR0ZXIodGhpcy5lZmZlY3RpdmVQbG90VHlwZSkgfHxcbiAgICAgIGlzTmFwYXJpM2QodGhpcy5lZmZlY3RpdmVQbG90VHlwZSlcbiAgICApO1xuICB9XG5cbiAgLyoqIElTT1NVUkZBQ0UgKFBsb3RseSBvciBuYXBhcmktanMgV2ViR1BVLCBlaXRoZXIgcmVzb2x1dGlvbikgc2hvd3MgdGhlIGlzb3ZhbHVlIHJhbmdlIHNsaWRlci4gKi9cbiAgZ2V0IGlzSXNvc3VyZmFjZU1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChcbiAgICAgIHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLklTT1NVUkZBQ0UgfHwgaXNOYXBhcmlJc29zdXJmYWNlKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBUaGUgbmFwYXJpLWpzIFdlYkdQVSBzdXJmYWNlIOKAlCBzaG93cyB0aGUgd2lyZWZyYW1lIHRvZ2dsZS4gKi9cbiAgZ2V0IGlzTmFwYXJpU3VyZmFjZU1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGlzTmFwYXJpU3VyZmFjZSh0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBBbnkgbmFwYXJpLWpzIFdlYkdQVSAzRCB0eXBlICh2b2x1bWUvaXNvc3VyZmFjZS9zdXJmYWNlKSDigJQgc2hvd3MgdGhlIFJlc29sdXRpb24gY29udHJvbC4gKi9cbiAgZ2V0IGlzTmFwYXJpM2RNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc05hcGFyaTNkKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpO1xuICB9XG5cbiAgLyoqIEludGVuc2l0eSBwcm9maWxlIGxpbmVzIGFyZSBSZWdpb24tYmFzZWQgYW5kIGF2YWlsYWJsZSBpbiB0aGUgSGVhdG1hcCBhbmRcbiAgICogIEltYWdlIHBsb3QgdHlwZXMsIHdoaWNoIHNob3cgdGhlIEludGVuc2l0eSB0b29sIGdyb3VwLiAqL1xuICBnZXQgaXNJbnRlbnNpdHlDYXBhYmxlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5IRUFUTUFQIHx8IHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLklNQUdFO1xuICB9XG5cbiAgc2hvd0hlbHAoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5SGVscERpYWxvZyA9IHRydWU7XG4gIH1cbn1cbiIsIjxwLXRvb2xiYXI+XG4gIDxkaXYgY2xhc3M9XCJwLXRvb2xiYXItZ3JvdXAtc3RhcnQgZmxleC13cmFwIGFsaWduLWl0ZW1zLWNlbnRlciBnYXAtMVwiPlxuXG4gICAgPCEtLSBIb3N0LXByb3ZpZGVkIGN1c3RvbSB0b29sYmFyIGJ1dHRvbnMsIHNob3duIGZpcnN0IChlLmcuIGFwcC1zcGVjaWZpY1xuICAgICAgICAgYWN0aW9ucyB0aGUgdmlzdWFsaXphdGlvbiB2aWV3IGl0c2VsZiBkb2Vzbid0IG93bikuIC0tPlxuICAgIDxuZy1jb250ZW50PjwvbmctY29udGVudD5cblxuICAgIDwhLS0gQ29udHJpYnV0ZWQgZGlhbG9nIHRvb2xzIChUT09MQkFSX1RPT0xTLCBraW5kOiAnZGlhbG9nJyk6IGFwcC1sZXZlbCBhY3Rpb25zXG4gICAgICAgICBsaWtlIHRoZSBob3N0J3Mgb3duIGJ1dHRvbnMgYWJvdmUsIHNvIHRoZXkgc2l0IHJpZ2h0IGJlc2lkZSB0aGVtLiBJbWFnZSB2aWV3XG4gICAgICAgICBvbmx5LCB0aGUgdmlldyB0aGVpciBkaWFsb2cgZHJhd3Mgb3Zlci4gSGlnaGxpZ2h0ZWQgd2hpbGUgdGhlIGRpYWxvZyBpcyBvcGVuLiAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNJbWFnZVZpZXdcIj5cbiAgICAgIDxwLWJ1dHRvbiAqbmdGb3I9XCJsZXQgdG9vbCBvZiBkaWFsb2dUb29sczsgdHJhY2tCeTogdHJhY2tUb29sQnlJZFwiXG4gICAgICAgICAgICAgICAgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwib3BlbkRpYWxvZ1Rvb2xJZCAhPT0gdG9vbC5pZFwiXG4gICAgICAgICAgICAgICAgW3BUb29sdGlwXT1cInRvb2wudG9vbHRpcFwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgW2FyaWFMYWJlbF09XCJ0b29sLmxhYmVsXCJcbiAgICAgICAgICAgICAgICBbYXR0ci5hcmlhLXByZXNzZWRdPVwib3BlbkRpYWxvZ1Rvb2xJZCA9PT0gdG9vbC5pZFwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRGlhbG9nVG9vbC5lbWl0KHRvb2wuaWQpXCI+XG4gICAgICAgIDxpbWcgKm5nSWY9XCJ0b29sLmljb24uc3JjXCIgW3NyY109XCJ0b29sLmljb24uc3JjXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiIFthbHRdPVwidG9vbC5sYWJlbFwiXG4gICAgICAgICAgICAgW2NsYXNzXT1cIm9wZW5EaWFsb2dUb29sSWQgPT09IHRvb2wuaWQgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCIgLz5cbiAgICAgICAgPGkgKm5nSWY9XCJ0b29sLmljb24ucGlcIiBjbGFzcz1cInBpIHt7IHRvb2wuaWNvbi5waSB9fVwiPjwvaT5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8IS0tIFBsb3QgdHlwZTogYXZhaWxhYmxlIGZvciBhbnkgaW1hZ2UgKGdyYXlzY2FsZSBvciBSR0IpLiAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaW1hZ2VJbmZvICYmIHRvb2xzLnNwZWNpYWxUb29sc1wiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDxwLWRyb3Bkb3duIFtvcHRpb25zXT1cInBsb3RUeXBlT3B0aW9uc1wiIG9wdGlvbkxhYmVsPVwibGFiZWxcIiBvcHRpb25WYWx1ZT1cInR5cGVcIlxuICAgICAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJzZWxlY3RlZFBsb3RUeXBlXCIgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU2VsZWN0IHBsb3QgdHlwZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICBbc3R5bGVdPVwieydtaW4td2lkdGgnOiAnMTFyZW0nfVwiXG4gICAgICAgICAgICAgICAgICBzdHlsZUNsYXNzPVwicGxvdG1vZGUtZHJvcGRvd25cIiBwYW5lbFN0eWxlQ2xhc3M9XCJwbG90bW9kZS1wYW5lbFwiXG4gICAgICAgICAgICAgICAgICAob25DaGFuZ2UpPVwic2VsZWN0UGxvdFR5cGUuZW1pdCgkZXZlbnQudmFsdWUpXCI+XG4gICAgICAgIDxuZy10ZW1wbGF0ZSBsZXQtb3B0IHBUZW1wbGF0ZT1cInNlbGVjdGVkSXRlbVwiPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwicGxvdG1vZGUtaXRlbVwiICpuZ0lmPVwib3B0XCI+XG4gICAgICAgICAgICA8aSAqbmdJZj1cImlzUGlJY29uKG9wdC5pY29uKVwiIFtjbGFzc109XCJvcHQuaWNvblwiPjwvaT5cbiAgICAgICAgICAgIDxpbWcgKm5nSWY9XCJvcHQuaWNvbiAmJiAhaXNQaUljb24ob3B0Lmljb24pXCIgY2xhc3M9XCJwbG90bW9kZS1pY29uLWltZ1wiIFtzcmNdPVwib3B0Lmljb25cIiBhbHQ9XCJcIiAvPlxuICAgICAgICAgICAgPHNwYW4+e3sgb3B0LmxhYmVsIH19PC9zcGFuPlxuICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgPG5nLXRlbXBsYXRlIGxldC1vcHQgcFRlbXBsYXRlPVwiaXRlbVwiPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwicGxvdG1vZGUtaXRlbVwiPlxuICAgICAgICAgICAgPGkgKm5nSWY9XCJpc1BpSWNvbihvcHQuaWNvbilcIiBbY2xhc3NdPVwib3B0Lmljb25cIj48L2k+XG4gICAgICAgICAgICA8aW1nICpuZ0lmPVwib3B0Lmljb24gJiYgIWlzUGlJY29uKG9wdC5pY29uKVwiIGNsYXNzPVwicGxvdG1vZGUtaWNvbi1pbWdcIiBbc3JjXT1cIm9wdC5pY29uXCIgYWx0PVwiXCIgLz5cbiAgICAgICAgICAgIDxzcGFuPnt7IG9wdC5sYWJlbCB9fTwvc3Bhbj5cbiAgICAgICAgICA8L3NwYW4+XG4gICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICA8L3AtZHJvcGRvd24+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNJc29zdXJmYWNlTW9kZVwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+SXNvPC9zcGFuPlxuICAgICAgICA8cC1zbGlkZXIgWyhuZ01vZGVsKV09XCJpc29SYW5nZVwiIFtyYW5nZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIFttaW5dPVwiaXNvVmFsdWVNaW5cIiBbbWF4XT1cImlzb1ZhbHVlTWF4XCIgW3N0ZXBdPVwiaXNvVmFsdWVTdGVwXCJcbiAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJpc29SYW5nZUNoYW5nZS5lbWl0KCRldmVudC52YWx1ZXMpXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiSXNvc3VyZmFjZSBiYW5kICgw4oCTMjU1KSwgbWFwcGVkIG9udG8gdGhlIHZvbHVtZSdzXG4gICAgICAgICAgICAgICAgICBhY3R1YWwgaW50ZW5zaXR5IHJhbmdlLiBTdXJmYWNlcyBhcmUgZHJhd24gZm9yIHZveGVscyB3aG9zZVxuICAgICAgICAgICAgICAgICAgaW50ZW5zaXR5IGZhbGxzIGluIHRoZSBzZWxlY3RlZCBiYW5kIOKAlCBuYXJyb3cgaXQgdG8gaXNvbGF0ZVxuICAgICAgICAgICAgICAgICAgZmVhdHVyZXMsIHdpZGVuIGl0IHRvIHNlZSB0aGUgd2hvbGUgc3RydWN0dXJlLlwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIHN0eWxlQ2xhc3M9XCJ3YW5kLXNlbnNpdGl2aXR5LXNsaWRlclwiPjwvcC1zbGlkZXI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHQgd2FuZC1zZW5zaXRpdml0eS12YWx1ZVwiPlxuICAgICAgICAgIHt7IGlzb1JhbmdlWzBdIH194oCTe3sgaXNvUmFuZ2VbMV0gfX08L3NwYW4+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgIDwhLS0gSW50ZW5zaXR5IHRvb2xzIChIZWF0bWFwIC8gSW1hZ2UgbW9kZSk6IGFkZCBhbm90aGVyIGNvbG9yZWQgbGluZSBST0kuXG4gICAgICAgICAgIEVhY2ggYWRkZWQgbGluZSBnZXRzIHRoZSBuZXh0IGJyaWdodCBwYWxldHRlIGNvbG91ciBhbmQgaXRzIG93biBpbnNldFxuICAgICAgICAgICB0cmFjZTsgc2VsZWN0IGEgbGluZSBvbiB0aGUgaW1hZ2UgYW5kIHVzZSB0aGUgdHJhc2ggdG9vbCB0byByZW1vdmUgaXRcbiAgICAgICAgICAgKGFuZCBpdHMgdHJhY2UpLiBUaGUgbGluZXMgYXJlIFJlZ2lvbi1iYXNlZCBidXQgZXhjbHVkZWQgZnJvbSB0aGVcbiAgICAgICAgICAgUmVnaW9uIEVkaXRvciBhbmQgZXhwb3J0cy4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNJbnRlbnNpdHlDYXBhYmxlXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiQWRkIGFuIGludGVuc2l0eSBsaW5lIChuZXcgY29sb3VyKS4gQ2xpY2sgYSBsaW5lIHRvIHNlbGVjdCBpdCxcbiAgICAgICAgICAgICAgICAgIHRoZW4gdXNlIHRoZSB0cmFzaCB0b29sIHRvIHJlbW92ZSBpdC5cIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJhZGRQcm9maWxlTGluZS5lbWl0KClcIj5cbiAgICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zcGxpbmUtY2hhcnQuc3ZnXCIgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8IS0tIENoYW5uZWxzICYgSGlzdG9ncmFtOiBicmlnaHRuZXNzL2NvbnRyYXN0LCBnYW1tYSwgcGVyLWNoYW5uZWwgZGlzcGxheSxcbiAgICAgICAgIGNvbG9ybWFwL3JldmVyc2UvaW52ZXJ0IOKAlCBvcGVucyB0aGUgZmxvYXRpbmcgZGlhbG9nLiAtLT5cbiAgICA8cC1idXR0b24gKm5nSWY9XCJ0b29scy5zcGVjaWFsVG9vbHNcIiBwUmlwcGxlIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICBwVG9vbHRpcD1cIkNoYW5uZWxzICZhbXA7IEhpc3RvZ3JhbSAoYnJpZ2h0bmVzcy9jb250cmFzdCwgY29sb3JtYXApXCJcbiAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwib3BlbkNoYW5uZWxIaXN0b2dyYW0uZW1pdCgpXCI+XG4gICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9pbnZlcnQuc3ZnXCIgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgPC9wLWJ1dHRvbj5cblxuICAgIDwhLS0gU3BhdGlhbC1vbWljcyBjb250cm9scy4gT25seSBtZWFuaW5nZnVsIGluIHRoZSBzcGF0aWFsIG1vZGUsIHdoaWNoIGlzXG4gICAgICAgICBpdHNlbGYgb25seSBvZmZlcmVkIHdoaWxlIGEgZGF0YXNldCBpcyBsb2FkZWQg4oCUIHNvIHRoaXMgYnV0dG9uIGFwcGVhcnNcbiAgICAgICAgIGV4YWN0bHkgd2hlbiB0aGVyZSBpcyBzb21ldGhpbmcgZm9yIGl0IHRvIGNvbnRyb2wuIC0tPlxuICAgIDxwLWJ1dHRvbiAqbmdJZj1cInRvb2xzLnNwZWNpYWxUb29scyAmJiBpc1NwYXRpYWxNb2RlXCIgcFJpcHBsZSBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTcGF0aWFsIG9taWNzIChjb2xvdXIgYnkgY29sdW1uIG9yIGdlbmUsIGxlZ2VuZCwgc2VsZWN0aW9uLCBkaXN0cmlidXRpb25zKVwiXG4gICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIChvbkNsaWNrKT1cIm9wZW5TcGF0aWFsQ29udHJvbHMuZW1pdCgpXCI+XG4gICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zcGF0aWFsLW9taWNzLnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgIDwvcC1idXR0b24+XG5cbiAgICA8IS0tIFNtb290aGVuIHRvZ2dsZTogYmlsaW5lYXIgc21vb3RoaW5nIHZzIG5lYXJlc3QtbmVpZ2hib3VyIHJhdyBwaXhlbHMgKEltYWdlXG4gICAgICAgICB2aWV3IG9ubHkg4oCUIGl0J3MgYW4gT3BlblNlYWRyYWdvbiBkcmF3ZXIgc2V0dGluZykuIEhpZ2hsaWdodGVkIHdoZW4gc21vb3RoaW5nIG9uLiAtLT5cbiAgICA8cC1idXR0b24gKm5nSWY9XCJ0b29scy5zcGVjaWFsVG9vbHMgJiYgaXNJbWFnZVZpZXdcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICBbdGV4dF09XCIhaW1hZ2VTbW9vdGhpbmdFbmFibGVkXCJcbiAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTbW9vdGhlbiAodHVybiBvbiBpbWFnZSBzbW9vdGhpbmcpXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlSW1hZ2VTbW9vdGhpbmcuZW1pdCgpXCI+XG4gICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zbW9vdGguc3ZnXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiXG4gICAgICAgICAgIFtjbGFzc109XCJpbWFnZVNtb290aGluZ0VuYWJsZWQgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCIvPlxuICAgIDwvcC1idXR0b24+XG5cbiAgICA8IS0tIFN0YWNrIGNvbnRyb2xzIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpbWFnZUluZm8/LmlzU3RhY2tcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8IS0tIEltYWdlIHZpZXc6IHNjcnViIHotc2xpY2VzIGxpdmUgKHRoZSByZW5kZXJlciBzd2FwcyBzbGljZXMgaW4gcGxhY2UpLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJzaG93c0xpdmVTbGljZVNjcnViYmVyXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5TbGljZTwvc3Bhbj5cbiAgICAgICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwiekluZGV4XCIgW21pbl09XCIwXCIgW21heF09XCJtYXhJbmRleFwiXG4gICAgICAgICAgICAgICAgICAob25DaGFuZ2UpPVwielNjcnViLmVtaXQoJGV2ZW50LnZhbHVlKVwiIChvblNsaWRlRW5kKT1cInpTbGlkZS5lbWl0KCRldmVudC52YWx1ZSlcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTbGljZSB0aHJvdWdoIHRoZSBzdGFjayAo4oaQIC8g4oaSIGtleXMpXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIFtzdHlsZV09XCJ7J3dpZHRoJzonMTZyZW0nfVwiIHN0eWxlQ2xhc3M9XCJ3YW5kLXNlbnNpdGl2aXR5LXNsaWRlclwiPjwvcC1zbGlkZXI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj57e3pJbmRleH19IG9mIHt7bWF4SW5kZXh9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPCEtLSBIZWF0bWFwIHZpZXc6IHNpbmdsZS1pbWFnZSAoaGlnaC1kZWYgem9vbSkgdnMgc3RhY2sgKGZyYW1lIHNsaWRlcikuIFBsb3RseS1vbmx5IOKAlFxuICAgICAgICAgICBoaWRkZW4gZm9yIG5hcGFyaSwgd2hpY2ggaGFzIG5vIHNpbmdsZS9zdGFjayB0b2dnbGUgb3IgcGVyLWZyYW1lIGZldGNoLCBhbmQgZm9yIHRoZVxuICAgICAgICAgICBzcGF0aWFsLW9taWNzIG1vZGVzOiBhIDNEIG9taWNzIGRhdGFzZXQgcHVibGlzaGVzIGl0cyByZWZlcmVuY2Ugdm9sdW1lIGFzIGEgei1zdGFja1xuICAgICAgICAgICBpbWFnZSwgYW5kIHRoZXNlIG1vZGVzIGRyYXcgb2JzZXJ2YXRpb25zLCBub3Qgb25lIG9mIGl0cyBzbGljZXMuIC0tPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFzaG93c0xpdmVTbGljZVNjcnViYmVyICYmICFpc05hcGFyaU1vZGUgJiYgIWlzU3BhdGlhbE1vZGVcIj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktaW1hZ2VcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImltYWdlSW5mbz8uc2hvd1N0YWNrXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNpbmdsZSBpbWFnZSBtb2RlOiBlbmFibGVzIGhpZ2gtZGVmIHpvb21cIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNlbGVjdFN0YWNrT3B0aW9uLmVtaXQoc3RhY2tPcHRpb25zWzBdKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktaW1hZ2VzXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCIhaW1hZ2VJbmZvPy5zaG93U3RhY2tcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU3RhY2sgbW9kZSwgZW5hYmxlcyBzbGljaW5nIHRocm91Z2ggaW1hZ2UgZnJhbWVzXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJzZWxlY3RTdGFja09wdGlvbi5lbWl0KHN0YWNrT3B0aW9uc1sxXSlcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiIWltYWdlSW5mbz8uc2hvd1N0YWNrXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5TbGljZTwvc3Bhbj5cbiAgICAgICAgPGRpdiBjbGFzcz1cInAtaW5wdXRncm91cFwiIHN0eWxlPVwid2lkdGg6IGF1dG9cIj5cbiAgICAgICAgICA8cC1pbnB1dE51bWJlciBzdHlsZUNsYXNzPVwidy0zcmVtIHRvb2xiYXItaW5wdXRcIlxuXHRcdFx0XHRcdHBUb29sdGlwPVwiVmlldyBpbWFnZSBpbiBzdGFjayAoJiN4MjNDRTspXCJcblx0XHRcdFx0XHRbKG5nTW9kZWwpXT1cInpJbmRleFwiIFttaW5dPVwiMFwiIFttYXhdPVwibWF4SW5kZXhcIlxuICAgICAgICAgICAob25JbnB1dCk9XCJ6SW5kZXhJbnB1dC5lbWl0KHpJbmRleClcIiAoa2V5ZG93bi5lbnRlcik9XCJyZWxvYWRBbmRQbG90LmVtaXQoKVwiLz5cbiAgICAgICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXJlZnJlc2hcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCIgKG9uQ2xpY2spPVwicmVsb2FkQW5kUGxvdC5lbWl0KClcIlxuICAgICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlBsb3QgdGhpcyBzbGljZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiPlxuICAgICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+b2Yge3ttYXhJbmRleH19PC9zcGFuPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgPCEtLSBJbWFnZSBhY3Rpb25zIChzcGVjaWFsIHRvb2xzKSAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwidG9vbHMuc3BlY2lhbFRvb2xzXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1kb3dubG9hZFwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRG93bmxvYWQgaW1hZ2UgYXMgUE5HXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJkb3dubG9hZEltYWdlLmVtaXQoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiAqbmdJZj1cImlzSGVhdG1hcFwiIGljb249XCJwaSBwaS13aW5kb3ctbWF4aW1pemVcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkZpdCB0byB2aWV3XCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJhdXRvc2NhbGVJbWFnZS5lbWl0KClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8IS0tIFpvb20gdG9vbHMuIFBsb3RseSBydWJiZXItYmFuZCBkcmFnLXpvb206IG9uIE9wZW5TZWFkcmFnb24gdGhlIGJveC16b29tXG4gICAgICAgICBjb3ZlcnMgdGhpcyBhbmQgYSBiYXJlIGRyYWcgcGFucyBuYXRpdmVseSwgc28gaXQncyBoaWRkZW4gaW4gaW1hZ2Ugdmlldy4gLS0+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImlzSGVhdG1hcCAmJiB0b29scy56b29tVG9vbHNcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8cC1idXR0b24gKm5nSWY9XCIhaXNJbWFnZVZpZXdcIiBpY29uPVwicGkgcGktc2VhcmNoXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ3pvb20nXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlpvb20gZHJhZ1wiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnem9vbScpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICd6b29tVG9Cb3gnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlpvb20gYm94ICg8a2JkPmI8L2tiZD4pXCIgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnem9vbVRvQm94JylcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvem9vbS1ib3guc3ZnXCJcbiAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICd6b29tVG9Cb3gnID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWFycm93cy1hbHRcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAncGFuJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJQYW4gdGhlIGltYWdlICg8a2JkPnA8L2tiZD4pXCIgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgncGFuJylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXNlYXJjaC1wbHVzXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJab29tIGluICg8a2JkPis8L2tiZD4pXCIgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiem9vbUluLmVtaXQoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktc2VhcmNoLW1pbnVzXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJab29tIG91dCAoPGtiZD4tPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInpvb21PdXQuZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgPCEtLSBTdXJmYWNlIDNEIGNvbnRyb2xzIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIhaXNIZWF0bWFwXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1zZWFyY2hcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZVN1cmZhY2UzZE1vZGUgIT09ICd6b29tJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJab29tXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVTdXJmYWNlM2RNb2RlLmVtaXQoJ3pvb20nKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktYXJyb3dzLWFsdFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlU3VyZmFjZTNkTW9kZSAhPT0gJ3BhbidcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUGFuXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVTdXJmYWNlM2RNb2RlLmVtaXQoJ3BhbicpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1nbG9iZVwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlU3VyZmFjZTNkTW9kZSAhPT0gJ29yYml0J1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJPcmJpdGFsIHJvdGF0aW9uXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVTdXJmYWNlM2RNb2RlLmVtaXQoJ29yYml0JylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXN5bmNcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZVN1cmZhY2UzZE1vZGUgIT09ICd0dXJudGFibGUnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlR1cm50YWJsZSByb3RhdGlvblwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlU3VyZmFjZTNkTW9kZS5lbWl0KCd0dXJudGFibGUnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktaG9tZVwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUmVzZXQgY2FtZXJhIHRvIGRlZmF1bHRcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInJlc2V0U3VyZmFjZUNhbWVyYS5lbWl0KClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8IS0tIDNEIGNvb3JkaW5hdGUtYXhlcyAvIHNjYWxlIGdpem1vIHRvZ2dsZSAobmFwYXJpLWpzIFdlYkdQVSB2b2x1bWUvaXNvc3VyZmFjZSBvbmx5KS5cbiAgICAgICAgICAgYHAtYnV0dG9uLWljb24tb25seWAgZ2l2ZXMgdGhlIHNhbWUgc3F1YXJlIHNpemluZyBhcyB0aGUgbmVpZ2hib3VyaW5nIHBpLWljb24gYnV0dG9uc1xuICAgICAgICAgICAodGhlIHByb2plY3RlZCA8aW1nPiBkb2Vzbid0IHRyaWdnZXIgUHJpbWVORydzIGF1dG9tYXRpYyBpY29uLW9ubHkgc2l6aW5nKS4gLS0+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgKm5nSWY9XCJpc05hcGFyaU1vZGVcIiBzdHlsZUNsYXNzPVwicC1idXR0b24taWNvbi1vbmx5XCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCIhYXhlc1Zpc2libGVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiVG9nZ2xlIDNEIGF4ZXMgLyBzY2FsZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlQXhlcy5lbWl0KClcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvM2QtY29vcmRpbmF0ZXMuc3ZnXCJcbiAgICAgICAgICAgICBbY2xhc3NdPVwiYXhlc1Zpc2libGUgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwhLS0gU3VyZmFjZSB3aXJlZnJhbWUgdG9nZ2xlIChuYXBhcmktanMgV2ViR1BVIHN1cmZhY2Ugb25seSkuIC0tPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1zaGFyZS1hbHRcIiBzaXplPVwic21hbGxcIiAqbmdJZj1cImlzTmFwYXJpU3VyZmFjZU1vZGVcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cIiF3aXJlZnJhbWVBY3RpdmVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiVG9nZ2xlIHdpcmVmcmFtZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlV2lyZWZyYW1lLmVtaXQoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwhLS0gUmVzb2x1dGlvbiAoZGVjaW1hdGUgZmFjdG9yKSBmb3IgdGhlIG5hcGFyaSAzRCB0eXBlcyDigJQgcmVsb2FkcyBhdCBjb2Fyc2VyL2ZpbmVyIHNhbXBsaW5nLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc05hcGFyaTNkTW9kZVwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+UmVzPC9zcGFuPlxuICAgICAgICA8cC1kcm9wZG93biBbb3B0aW9uc109XCJkZWNpbWF0ZU9wdGlvbnNcIiBvcHRpb25MYWJlbD1cImxhYmVsXCIgb3B0aW9uVmFsdWU9XCJ2YWx1ZVwiXG4gICAgICAgICAgICAgICAgICAgIFtuZ01vZGVsXT1cInJlc29sdXRpb25TY2FsZVwiIGFwcGVuZFRvPVwiYm9keVwiXG4gICAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUmVzb2x1dGlvbiAoZGVjaW1hdGUgZmFjdG9yKVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAgIFtzdHlsZV09XCJ7J21pbi13aWR0aCc6ICc2cmVtJ31cIlxuICAgICAgICAgICAgICAgICAgICAob25DaGFuZ2UpPVwic2VsZWN0UmVzb2x1dGlvbi5lbWl0KCRldmVudC52YWx1ZSlcIj5cbiAgICAgICAgPC9wLWRyb3Bkb3duPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiKGlzSGVhdG1hcCB8fCBpczNkUmVnaW9ucykgJiYgdG9vbHMucmVnaW9uVG9vbHNcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8IS0tIFJlZ2lvbiBFZGl0b3I6IG9wZW5zIHRoZSBlZGl0b3IgYXMgYSBmbG9hdGluZyBkaWFsb2cgKGZvcm1lcmx5IHRoZVxuICAgICAgICAgICByaWdodC1wYW5lbCBSZWdpb25zIHRhYikuIFNpdHMgd2l0aCB0aGUgcmVnaW9uIGRyYXdpbmcgdG9vbHMuIC0tPlxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiT3BlbiB0aGUgUmVnaW9uIEVkaXRvclwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwib3BlblJlZ2lvbkVkaXRvci5lbWl0KClcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVnaW9uLWVkaXRvci5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8IS0tIFJlZ2lvbiBkcmF3aW5nIHRvb2xzIC0tPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1hcnJvdy11cC1yaWdodFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdzZWxlY3QnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNlbGVjdGlvbiBtb2RlICg8a2JkPnM8L2tiZD4pXCJcbiAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdzZWxlY3QnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktc3RvcFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdkcmF3cmVjdCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRHJhdyBhIHJlY3Rhbmd1bGFyIHJlZ2lvbiAoPGtiZD5yPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnZHJhd3JlY3QnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiAqbmdJZj1cIiFpczNkUmVnaW9uc1wiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdkcmF3b3BlbnBhdGgnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkRyYXcgYSBwb2x5bGluZSAoPGtiZD5sPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnZHJhd29wZW5wYXRoJylcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcG9seWxpbmUuc3ZnXCJcbiAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdkcmF3b3BlbnBhdGgnID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXBlbmNpbFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdkcmF3Y2xvc2VkcGF0aCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRHJhdyBhIGZyZWVmb3JtIHJlZ2lvbiAoPGtiZD5mPC9rYmQ+KVwiIFtlc2NhcGVdPVwiZmFsc2VcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnZHJhd2Nsb3NlZHBhdGgnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwhLS0gQnJ1c2g6IHBhaW50IGEgcmVnaW9uIGJ5IGRyYWdnaW5nIGEgZGlzYyBvZiB0aGUgZ2l2ZW4gc2l6ZTsgdGhlIFNpemVcbiAgICAgICAgICAgc2xpZGVyIGFwcGVhcnMgd2hpbGUgaXQncyBhY3RpdmUuIEhvbGQgU2hpZnQgdG8gZXJhc2UgKHN1YnRyYWN0KS4gLS0+XG4gICAgICA8IS0tIEJydXNoIGFuZCBwb2x5bGluZSBhcmUgYWJzZW50IGZyb20gYSAzRCBjbG91ZDogdGhlIGJydXNoIHBhaW50cyBpbiBJTUFHRVxuICAgICAgICAgICAgICAgICBwaXhlbHMgYW5kIHRoZXJlIGlzIG5vIHJhc3RlciB0byBzYW1wbGUsIGFuZCBhbiBvcGVuIHBvbHlsaW5lIGlzIGFcbiAgICAgICAgICAgICAgICAgcHJvZmlsZSB0b29sLCBub3QgYSBjbG9zZWQgcmVnaW9uIGEgc2VsZWN0aW9uIGNhbiB0ZXN0IGFnYWluc3QuIC0tPlxuICAgICAgICAgICAgPHAtYnV0dG9uICpuZ0lmPVwiIWlzM2RSZWdpb25zXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2JydXNoJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJCcnVzaDogY2xpY2sgYW5kIGRyYWcgdG8gcGFpbnQgYSByZWdpb24uXG4gICAgICAgICAgICAgICAgPGJyPkhvbGQgPGtiZD5TaGlmdDwva2JkPiB3aGlsZSBwYWludGluZyB0byBlcmFzZSBmcm9tIGEgcmVnaW9uLlwiXG4gICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9nZ2xlRHJhZ01vZGUuZW1pdCgnYnJ1c2gnKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9wYWludGJydXNoLnN2Z1wiXG4gICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnYnJ1c2gnID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+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+cGxvdC10eXBlIGRyb3Bkb3duPC9iPiBzd2l0Y2hlcyBob3cgdGhlIGZpbGUgaXMgdmlzdWFsaXplZDpcbiAgICA8Yj5JbWFnZTwvYj4sIDxiPkhlYXRtYXA8L2I+LCA8Yj5Db250b3VyPC9iPiwgPGI+U3VyZmFjZTwvYj4sIDxiPlZvbHVtZTwvYj4sXG4gICAgPGI+SXNvc3VyZmFjZTwvYj4sIGFuZCDigJQgd2hlbiBhIHNwYXRpYWwtb21pY3MgZGF0YXNldCBpcyBvcGVuIOKAlFxuICAgIDxiPlNwYXRpYWwgb21pY3M8L2I+IGFuZCA8Yj5TcGF0aWFsIG9taWNzIDNEPC9iPi5cbiAgPC9wPlxuICA8cD5cbiAgICA8Yj5Pbmx5IHRoZSBtb2RlcyB0aGUgZGF0YSBjYW4gYWN0dWFsbHkgc3VwcG9ydCBhcmUgb2ZmZXJlZDwvYj4sIHNvIHRoZSBsaXN0IGNoYW5nZXNcbiAgICB3aXRoIHdoYXQgaXMgbG9hZGVkIHJhdGhlciB0aGFuIHNob3dpbmcgZW50cmllcyB0aGF0IHdvdWxkIGNvbWUgdXAgYmxhbms6XG4gIDwvcD5cbiAgPHVsPlxuICAgIDxsaT48Yj5Db250b3VyPC9iPiwgPGI+Vm9sdW1lPC9iPiBhbmQgPGI+SXNvc3VyZmFjZTwvYj4gbmVlZCBhIGdyYXlzY2FsZSBpbWFnZTsgdGhlXG4gICAgICB0d28gM0Qgb25lcyBhbHNvIG5lZWQgYSB6LXN0YWNrLjwvbGk+XG4gICAgPGxpPjxiPlNwYXRpYWwgb21pY3M8L2I+IG5lZWRzIGEgcHVibGlzaGVkIGRhdGFzZXQsIGFuZCA8Yj5TcGF0aWFsIG9taWNzIDNEPC9iPiBuZWVkc1xuICAgICAgb25lIHdob3NlIGNvb3JkaW5hdGVzIGNhcnJ5IGEgei48L2xpPlxuICAgIDxsaT5UaGUgcGl4ZWwgbW9kZXMgKDxiPkltYWdlPC9iPiwgPGI+SGVhdG1hcDwvYj4sIDxiPlN1cmZhY2U8L2I+KSBhcmUgd2l0aGhlbGQgZm9yIGFcbiAgICAgIGRhdGFzZXQgdGhhdCBicmluZ3Mgbm8gdGlzc3VlIGltYWdlIOKAlCBzZXFGSVNIIHJlY29yZHMgY29vcmRpbmF0ZXMgYW5kIG5vIHNlY3Rpb24sIHNvXG4gICAgICB0aGVyZSBpcyBub3RoaW5nIGZvciB0aGVtIHRvIGRyYXcuPC9saT5cbiAgPC91bD5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPklzb3N1cmZhY2UgYmFuZDwvYj5cIiBzbGlkZXIgKElzb3N1cmZhY2UpOiBzZXRzIHRoZSBpbnRlbnNpdHkgYmFuZCB0aGUgc3VyZmFjZXMgYXJlXG4gICAgICBkcmF3biBmb3IsIG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUncyByZWFsIGludGVuc2l0eSByYW5nZS48L2xpPlxuICAgIDxsaT5cIjxiPjNEIGNhbWVyYTwvYj5cIiAoU3VyZmFjZSAvIFNjYXR0ZXIgM0QgLyBJc29zdXJmYWNlKTogem9vbSA8aSBjbGFzcz1cInBpIHBpLXNlYXJjaFwiPjwvaT4sXG4gICAgICBwYW4gPGkgY2xhc3M9XCJwaSBwaS1hcnJvd3MtYWx0XCI+PC9pPiwgb3JiaXQgPGkgY2xhc3M9XCJwaSBwaS1nbG9iZVwiPjwvaT4sXG4gICAgICB0dXJudGFibGUgPGkgY2xhc3M9XCJwaSBwaS1zeW5jXCI+PC9pPiwgYW5kIHJlc2V0IGNhbWVyYSA8aSBjbGFzcz1cInBpIHBpLWhvbWVcIj48L2k+O1xuICAgICAgc2Nyb2xsIHRvIHpvb20gdGhlIDNEIHNjZW5lLjwvbGk+XG4gIDwvdWw+XG5cbiAgPGgzPkltYWdlIG1hbmlwdWxhdGlvbiB0b29sczwvaDM+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5SZWdpb24gRWRpdG9yPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3JlZ2lvbi1lZGl0b3Iuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgb3BlbnMgdGhlIFJlZ2lvbiBFZGl0b3IgYXMgYSBmbG9hdGluZywgZHJhZ2dhYmxlIGRpYWxvZyDigJQgYSBwZXItaW1hZ2UgdGFibGUgb2YgZXZlcnkgcmVnaW9uXG4gICAgICAoY2xhc3MsIGNvbG91ciwgYXJlYSkgd2l0aCBidWxrIHNlbGVjdC9sYWJlbC9jb2xvdXIvZGVsZXRlIGFuZCBHZW9KU09OIGltcG9ydC9leHBvcnQuIEl0IHN0YXlzXG4gICAgICBpbiBzeW5jIHdpdGggdGhlIGltYWdlLCBzbyB5b3UgY2FuIGtlZXAgaXQgb3BlbiB3aGlsZSBkcmF3aW5nLjwvbGk+XG4gICAgPGxpPlwiPGI+SW50ZW5zaXR5IHByb2ZpbGUgbGluZXM8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc3BsaW5lLWNoYXJ0LnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPlxuICAgICAgKDxpPndvcmsgaW4gcHJvZ3Jlc3Mg4oCUIG5vdCB5ZXQgc3RhYmxlPC9pPik6IGFkZHMgYSBjb2xvdXJlZCBsaW5lIFJPSTsgYSBmbG9hdGluZyBpbnNldFxuICAgICAgY2hhcnQgcGxvdHMgaW50ZW5zaXR5IGFsb25nIGVhY2ggbGluZSwgdXBkYXRlcyBsaXZlIGFzIHlvdSBkcmFnIGl0LCBhbmQgcmUtc2FtcGxlcyBhdCB0aGVcbiAgICAgIGN1cnJlbnQgem9vbSByZXNvbHV0aW9uLiBFYWNoIGFkZGVkIGxpbmUgZ2V0cyBpdHMgb3duIGNvbG91cjsgc2VsZWN0IGEgbGluZSBhbmQgdXNlIHRoZVxuICAgICAgdHJhc2ggdG9vbCA8aSBjbGFzcz1cInBpIHBpLXRyYXNoXCI+PC9pPiB0byByZW1vdmUgaXQgKGFuZCBpdHMgdHJhY2UpLjwvbGk+XG4gICAgPGxpPlwiPGI+Q2hhbm5lbHMgJmFtcDsgSGlzdG9ncmFtPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL2ludmVydC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46XG4gICAgICBvcGVucyBhIGZsb2F0aW5nIGRpYWxvZyBmb3IgYnJpZ2h0bmVzcy9jb250cmFzdCwgZ2FtbWEsIGFuZCBwZXItY2hhbm5lbCBkaXNwbGF5LCBwbHVzIHRoZVxuICAgICAgPGI+Y29sb3JtYXAgLyBMVVQ8L2I+IGNvbnRyb2xzIChncmF5c2NhbGUgb25seSkg4oCUIGEgPGI+cmV2ZXJzZTwvYj4gdG9nZ2xlIGFuZCBhIGNvbG9ybWFwXG4gICAgICBkcm9wZG93biAoZGVmYXVsdDogaW52ZXJ0ZWQgZ3JleXMpLjwvbGk+XG4gICAgPGxpPlwiPGI+U21vb3RoZW48L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc21vb3RoLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPlxuICAgICAgKEltYWdlIHZpZXcgb25seSk6IHRoZSBpbWFnZSBvcGVucyBzaG93aW5nIDxiPnJhdyBwaXhlbHM8L2I+IChuZWFyZXN0LW5laWdoYm91ciksIHNvXG4gICAgICB6b29taW5nIGluIHJlbmRlcnMgY3Jpc3AgYmxvY2tzIGZvciBwaXhlbCBpbnNwZWN0aW9uLiBUb2dnbGUgdGhpcyBvbiBmb3IgYmlsaW5lYXJcbiAgICAgIHNtb290aGluZzsgdGhlIGljb24gdHVybnMgd2hpdGUgd2hpbGUgc21vb3RoaW5nIGlzIGFjdGl2ZS48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5Eb3dubG9hZCAmYW1wOyB2aWV3IHRvb2xzPC9oMz5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPkRvd25sb2FkPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktZG93bmxvYWRcIj48L2k+OiBkb3dubG9hZCB0aGUgY3VycmVudCBpbWFnZSBhcyBhIFBORy48L2xpPlxuICAgIDxsaT5cIjxiPkZpdCB0byB2aWV3PC9iPlwiIC8gYXV0b3NjYWxlIDxpIGNsYXNzPVwicGkgcGktd2luZG93LW1heGltaXplXCI+PC9pPjogcmVzZXRzIHRoZSBpbWFnZVxuICAgICAgdG8gaXRzIG9yaWdpbmFsIGRpbWVuc2lvbnMuIEluIHN0YWNrIG1vZGUgaXQgYWxzbyByZXNldHMgdG8gc2luZ2xlLWltYWdlIG1vZGUuPC9saT5cbiAgPC91bD5cblxuICA8aDM+Wm9vbSB0b29sczwvaDM+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5ab29tIGJveDwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy96b29tLWJveC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgICg8a2JkPmI8L2tiZD4pOiBkcmFnIGEgYm94IHRvIHNlbGVjdCBhbiBhcmVhIHRvIHpvb20gaW50by48L2xpPlxuICAgIDxsaT5cIjxiPlBhbjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLWFycm93cy1hbHRcIj48L2k+ICg8a2JkPnA8L2tiZD4pOiBkcmFnIHRvIHBhbiB0aGUgaW1hZ2UuPC9saT5cbiAgICA8bGk+XCI8Yj5ab29tIGluPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktc2VhcmNoLXBsdXNcIj48L2k+ICg8a2JkPis8L2tiZD4pOiB6b29tIGluIG9uZSBzdGVwLjwvbGk+XG4gICAgPGxpPlwiPGI+Wm9vbSBvdXQ8L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1zZWFyY2gtbWludXNcIj48L2k+ICg8a2JkPi08L2tiZD4pOiB6b29tIG91dCBvbmUgc3RlcCxcbiAgICAgIHN0b3BwaW5nIGF0IHRoZSBvcmlnaW5hbCBpbWFnZSBkaW1lbnNpb25zLjwvbGk+XG4gIDwvdWw+XG5cbiAgPGgzPlJlZ2lvbiB0b29sczwvaDM+XG4gIDxwPkJ5IGRlZmF1bHQgbm8gdG9vbCBpcyBzZWxlY3RlZCAoYWxsIGdyZXllZC1vdXQpOyBzZWxlY3RpbmcgYSB0b29sIGhpZ2hsaWdodHMgaXQuIFRoZVxuICAgIHZlcnRleC1lZGl0aW5nIHRvb2xzIChwb2x5Z29uLCBhZGQvZGVsZXRlIHZlcnRleCwgbW92ZSwgQsOpemllciBjb252ZXJzaW9ucykgYXJlIGF2YWlsYWJsZVxuICAgIGluIHRoZSA8Yj5JbWFnZTwvYj4gdmlldyBvbmx5LiBPbiBhIDxiPmRvbnV0PC9iPiAoYSByZWdpb24gd2l0aCBhIGhvbGUpLCB0aGUgaW5uZXItcmluZ1xuICAgIHZlcnRpY2VzIGFyZSBlZGl0YWJsZSB0b28g4oCUIGRyYWcsIGFkZCwgYW5kIGRlbGV0ZSB0aGVtIGp1c3QgbGlrZSB0aGUgb3V0ZXIgcmluZy48L3A+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5TZWxlY3Rpb248L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1hcnJvdy11cC1yaWdodFwiPjwvaT4gKDxrYmQ+czwva2JkPik6IGEgbmV1dHJhbCBtb2RlXG4gICAgICB0aGF0IHR1cm5zIG9mZiB3aGF0ZXZlciBkcmF3aW5nIHRvb2wgd2FzIGFjdGl2ZSAod2FuZCwgdmVydGV4IGVyYXNlciwgcmVjdGFuZ2xlLCBldGMuKS5cbiAgICAgIFVzZSBpdCB0byBzdGVwIG91dCBvZiBlZGl0aW5nIHdpdGhvdXQgcGlja2luZyBhbm90aGVyIHRvb2wuPC9saT5cbiAgICA8bGk+XCI8Yj5SZWN0YW5ndWxhciByZWdpb248L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1zdG9wXCI+PC9pPiAoPGtiZD5yPC9rYmQ+KTogY2xpY2sgYW5kIGRyYWcgdG9cbiAgICAgIGNyZWF0ZSBhIHJlY3Rhbmd1bGFyIHJlZ2lvbi48L2xpPlxuICAgIDxsaT5cIjxiPlBvbHlsaW5lPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3BvbHlsaW5lLnN2Z1wiIHdpZHRoPVwiMTRcIiBoZWlnaHQ9XCIxNFwiPlxuICAgICAgKDxrYmQ+bDwva2JkPik6IGRyYXcgYW4gb3BlbiAobm9uLWNsb3NlZCkgcG9seWxpbmUgYnkgY2xpY2tpbmcgYW5kIGRyYWdnaW5nLiBQb2x5bGluZXMgY2FuXG4gICAgICBiZSBlZGl0ZWQgYW5kIGV4cG9ydGVkIHRvIEdlb0pTT04gYXMgYSA8Y29kZT5MaW5lU3RyaW5nPC9jb2RlPiBmZWF0dXJlLjwvbGk+XG4gICAgPGxpPlwiPGI+RnJlZWZvcm0gcmVnaW9uPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktcGVuY2lsXCI+PC9pPiAoPGtiZD5mPC9rYmQ+KTogY2xpY2sgYW5kIGRyYWcgdG9cbiAgICAgIGRyYXcgYSBjbG9zZWQgZnJlZWZvcm0gcG9seWdvbi48L2xpPlxuICAgIDxsaT5cIjxiPkJydXNoPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3BhaW50YnJ1c2guc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OiBjbGlja1xuICAgICAgYW5kIGRyYWcgdG8gcGFpbnQgYSByZWdpb24gd2l0aCBhIGNpcmN1bGFyIGJydXNoIOKAlCB0aGUgcGFpbnRlZCBzdHJva2UgaXMgdGhlIHJlZ2lvbi5cbiAgICAgIEEgPGI+U2l6ZTwvYj4gc2xpZGVyIChicnVzaCBkaWFtZXRlciwgaW4gaW1hZ2UgcGl4ZWxzKSBhcHBlYXJzIGluIHRoZSB0b29sYmFyIHdoaWxlIHRoZVxuICAgICAgYnJ1c2ggaXMgYWN0aXZlLiBIb2xkIDxrYmQ+U2hpZnQ8L2tiZD4gd2hpbGUgcGFpbnRpbmcgdG8gPGI+ZXJhc2U8L2I+IChzdWJ0cmFjdCkgZnJvbSBhXG4gICAgICByZWdpb24gaW5zdGVhZC4gUGFpbnRpbmcgb3ZlciBhbiBleGlzdGluZyByZWdpb24gZXh0ZW5kcyBpdDsgYnJ1c2hpbmcgYWNyb3NzIHR3byByZWdpb25zXG4gICAgICBtZXJnZXMgdGhlbS4gUGFpbnRpbmcgYSBjbG9zZWQgcmluZyAob3Igc2hpZnQtZXJhc2luZyBhbiBpbnRlcmlvciBwb2NrZXQpIGxlYXZlcyB0aGVcbiAgICAgIGVuY2xvc2VkIGFyZWEgZW1wdHkg4oCUIGEgPGI+ZG9udXQ8L2I+IChJbWFnZSB2aWV3KS48L2xpPlxuICAgIDxsaT5cIjxiPlBvbHlnb248L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcG9seWdvbi12ZXJ0aWNlcy5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46XG4gICAgICBjbGljayB0byBwbGFjZSBlYWNoIHZlcnRleDsgY2xpY2sgdGhlIGZpcnN0IHBvaW50IHRvIGNsb3NlIHRoZSBwb2x5Z29uLjwvbGk+XG4gICAgPGxpPlwiPGI+QWRkIHZlcnRleDwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy92ZXJ0ZXgtYWRkLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjpcbiAgICAgIGNsaWNrIGFuIGVkZ2Ugb2YgdGhlIHNlbGVjdGVkIHBvbHlnb24gdG8gaW5zZXJ0IGEgbmV3IHZlcnRleC48L2xpPlxuICAgIDxsaT5cIjxiPkRlbGV0ZSB2ZXJ0ZXg8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvdmVydGV4LWRlbGV0ZS5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46XG4gICAgICBjbGljayBhIHZlcnRleCBvZiB0aGUgc2VsZWN0ZWQgcG9seWdvbiB0byByZW1vdmUgaXQuPC9saT5cbiAgICA8bGk+XCI8Yj5Nb3ZlIHJlZ2lvbjwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9yZWdpb24tbW92ZS5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46XG4gICAgICBkcmFnIHRvIG1vdmUgdGhlIHNlbGVjdGVkIHJlZ2lvbi48L2xpPlxuICAgIDxsaT5cIjxiPlRvIELDqXppZXI8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvdG8tYmV6aWVyLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPiAvXG4gICAgICBcIjxiPlRvIHBvbHlnb248L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvdG8tcG9seWdvbi5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46XG4gICAgICBjb252ZXJ0IHRoZSBzZWxlY3RlZCByZWdpb24gdG8gYSBzbW9vdGggQsOpemllciBjdXJ2ZSwgb3IgYmFjayB0byBhIHN0cmFpZ2h0LWVkZ2VkIHBvbHlnb24uPC9saT5cbiAgICA8bGk+XCI8Yj5XYW5kPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3dhbmQuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+ICg8a2JkPnc8L2tiZD4pOlxuICAgICAgY2xpY2sgdG8gZ3JvdyBhIHJlZ2lvbiBmcm9tIHBpeGVscyB3aXRoIHNpbWlsYXIgdmFsdWVzIGFyb3VuZCB0aGUgY2xpY2sgcG9pbnQuIEhvbGRcbiAgICAgIDxrYmQ+Q3RybDwva2JkPi88a2JkPkNtZDwva2JkPiBmb3IgYW4gZXhhY3QtbWF0Y2ggZmxvb2QgZmlsbDsgZHJhZyB0byBrZWVwIGV4dGVuZGluZyB0aGVcbiAgICAgIHNlbGVjdGlvbiB3aGlsZSBtb3Zpbmc7IGhvbGQgPGtiZD5TaGlmdDwva2JkPiB3aGlsZSBjbGlja2luZyBvciBkcmFnZ2luZyBvbiBhbiBleGlzdGluZ1xuICAgICAgcmVnaW9uIHRvIDxiPmVyYXNlPC9iPiBwaXhlbHMgZnJvbSBpdC4gVXNlIHRoZSA8Yj5TZW5zLjwvYj4gc2xpZGVyIHRoYXQgYXBwZWFycyB0byBtYWtlIHRoZVxuICAgICAgd2FuZCBzdHJpY3RlciAoaGlnaGVyID0gc21hbGxlciBzZWxlY3Rpb24pIG9yIGxvb3Nlci4gU3Vic2VxdWVudCBjbGlja3Mga2VlcCBleHRlbmRpbmcgdGhlXG4gICAgICBzYW1lIHJlZ2lvbjsgY2xpY2tpbmcgaW5zaWRlIGFuIGV4aXN0aW5nIHJlZ2lvbiBhZG9wdHMgaXQsIGFuZCBncm93aW5nIGludG8gYW5vdGhlciByZWdpb25cbiAgICAgIG1lcmdlcyB0aGVtLjwvbGk+XG4gICAgPGxpPlwiPGI+VmVydGV4IGVyYXNlcjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLWVyYXNlclwiPjwvaT4gKDxrYmQ+ZTwva2JkPik6IGNsaWNrIG9yIGNsaWNrLWFuZC1kcmFnXG4gICAgICB0byByZW1vdmUgcG9seWdvbiB2ZXJ0aWNlcyB0aGF0IGZhbGwgaW5zaWRlIHRoZSBjdXJzb3IgY2lyY2xlIGZyb20gYW55IHJlZ2lvbiwgaW5jbHVkaW5nIGFcbiAgICAgIHJlZ2lvbidzIGlubmVyIHJpbmdzIChkb251dCBob2xlcykuIFVzZSB0aGUgPGI+UmFkaXVzPC9iPiBzbGlkZXIgdG8gc2V0IHRoZSBlcmFzZXIgc2l6ZSBpblxuICAgICAgaW1hZ2UgcGl4ZWxzLiBQb2x5Z29ucyByZWR1Y2VkIGJlbG93IDMgdmVydGljZXMgKG9yIHBvbHlsaW5lcyBiZWxvdyAyKSBhcmUgZGVsZXRlZCBlbnRpcmVseTtcbiAgICAgIGEgaG9sZSByZWR1Y2VkIGJlbG93IDMgdmVydGljZXMgaXMgcmVtb3ZlZC48L2xpPlxuICAgIDxsaT5cIjxiPlVuZG88L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvdW5kby5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgICg8a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4rPGtiZD5aPC9rYmQ+KTogcmV2ZXJ0cyB0aGVcbiAgICAgIGxhc3QgcmVnaW9uIGFjdGlvbiAoZHJhdywgZGVsZXRlLCBlZGl0LCB3YW5kL2JydXNoIHN0cm9rZSwgc2VnbWVudGF0aW9uLCDigKYpLiBVcCB0byB0aGUgbGFzdFxuICAgICAgPGI+MTA8L2I+IGFjdGlvbnMgY2FuIGJlIHVuZG9uZTsgdGhlIGJ1dHRvbiBncmV5cyBvdXQgd2hlbiB0aGVyZSdzIG5vdGhpbmcgbGVmdCB0byB1bmRvLjwvbGk+XG4gICAgPGxpPlwiPGI+UmVkbzwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9yZWRvLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPlxuICAgICAgKDxrYmQ+Q3RybDwva2JkPi88a2JkPkNtZDwva2JkPis8a2JkPlNoaWZ0PC9rYmQ+KzxrYmQ+Wjwva2JkPiBvciA8a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4rPGtiZD5ZPC9rYmQ+KTpcbiAgICAgIHJlLWFwcGxpZXMgdGhlIG1vc3QgcmVjZW50bHkgdW5kb25lIGFjdGlvbi4gUGVyZm9ybWluZyBhIG5ldyByZWdpb24gYWN0aW9uIGNsZWFycyB3aGF0IGNhbiBiZSByZWRvbmUuPC9saT5cbiAgICA8bGk+XCI8Yj5EZWxldGUgcmVnaW9uPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktdHJhc2hcIj48L2k+ICg8a2JkPkRlbDwva2JkPik6IGRlbGV0ZXMgdGhlIHNlbGVjdGVkXG4gICAgICByZWdpb24uIFRoaXMgY2FuIGFsc28gYmUgZG9uZSBmcm9tIHRoZSBSZWdpb24gRWRpdG9yLjwvbGk+XG4gIDwvdWw+XG5cbiAgPGgzPlJlZ2lvbiBhY3Rpb25zIChyaWdodC1jbGljayB0aGUgaW1hZ2UpPC9oMz5cbiAgPHA+UmlnaHQtY2xpY2sgYSByZWdpb24g4oCUIG9yIHRoZSBjdXJyZW50IG11bHRpLXNlbGVjdGlvbiDigJQgdG8gb3BlbiB0aGVcbiAgICBcIjxiPlNlbGVjdGVkIHJlZ2lvbihzKTwvYj5cIiBncm91cCBhdCB0aGUgdG9wIG9mIHRoZSBjb250ZXh0IG1lbnUuIFRoZXNlIGFjdCBpbW1lZGlhdGVseSBvblxuICAgIHRoZSBzZWxlY3Rpb24gKHRoZXkncmUgbm90IGRyYXdpbmcgbW9kZXMpIGFuZCBhcmUgZnVsbHkgdW5kby9yZWRvLWFibGU6PC9wPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+U2VsZWN0IGFsbCByZWdpb25zPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktY2hlY2stc3F1YXJlXCI+PC9pPjogc2VsZWN0IGV2ZXJ5IHJlZ2lvbiBvblxuICAgICAgdGhlIGltYWdlIChhbHNvIGluIHRoZSBSZWdpb24gRWRpdG9yIHRvb2xiYXIpLjwvbGk+XG4gICAgPGxpPlwiPGI+TWVyZ2UgLyBncm91cDwvYj5cIjogY29tYmluZSAyKyBzZWxlY3RlZCByZWdpb25zIGludG8gb25lLiBJZiB0aGUgcGllY2VzIGRvbid0IHRvdWNoLFxuICAgICAgdGhlIHJlc3VsdCBpcyBhIHNpbmdsZSBtdWx0aS1wYXJ0IHJlZ2lvbiAoc3BhcnNlIFwiYml0c1wiKS48L2xpPlxuICAgIDxsaT5cIjxiPlVuZ3JvdXA8L2I+XCI6IHNwbGl0IGEgbXVsdGktcGFydCByZWdpb24gYmFjayBpbnRvIG9uZSByZWdpb24gcGVyIHBpZWNlLjwvbGk+XG4gICAgPGxpPlwiPGI+SW52ZXJzZTwvYj5cIjogcmVwbGFjZSB0aGUgc2VsZWN0aW9uIHdpdGggZXZlcnl0aGluZyA8aT5vdXRzaWRlPC9pPiBpdCDigJQgdGhlIGltYWdlXG4gICAgICByZWN0YW5nbGUgd2l0aCB0aGUgc2VsZWN0ZWQgcmVnaW9ucyBwdW5jaGVkIG91dCBhcyBob2xlcy4gV29ya3MgYXQgYW55IGltYWdlIHNpemUuPC9saT5cbiAgICA8bGk+XCI8Yj5TaW1wbGlmeTwvYj5cIjogcmVkdWNlIGEgcmVnaW9uJ3MgdmVydGV4IGNvdW50IChEb3VnbGFz4oCTUGV1Y2tlcikgYnkgYW4gYWx0aXR1ZGVcbiAgICAgIHRocmVzaG9sZCBpbiBwaXhlbHMg4oCUIHBpY2sgPGI+TGlnaHQ8L2I+ICgxJm5ic3A7cHgpLCA8Yj5NZWRpdW08L2I+ICgzJm5ic3A7cHgpLFxuICAgICAgPGI+U3Ryb25nPC9iPiAoOCZuYnNwO3B4KSwgb3IgPGI+Q3VzdG9t4oCmPC9iPiBmb3IgYSBzbGlkZXIuIEFwcGxpZXMgdG8gdGhlIGV4dGVyaW9yIGFuZCBhbnlcbiAgICAgIGhvbGVzIChhbmQgZXZlcnkgcGFydCBvZiBhIG11bHRpLXBhcnQgcmVnaW9uKS48L2xpPlxuICAgIDxsaT5UaGUgQsOpemllciBjb252ZXJzaW9ucyBhbmQgRGVsZXRlIGFsc28gbGl2ZSBpbiB0aGlzIGdyb3VwICh0aGUgcmlnaHQtY2xpY2sgZXF1aXZhbGVudHMgb2ZcbiAgICAgIHRoZSB0b29sYmFyIC8gdmVydGV4IHRvb2xzIGFib3ZlKS48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5TZWdtZW50YXRpb24gJmFtcDsgZGV0ZWN0aW9uIHRvb2xzPC9oMz5cbiAgPHA+QUktYXNzaXN0ZWQgc2VnbWVudGF0aW9uIGFuZCBkZXRlY3Rpb24gdGhhdCBydW4gZW50aXJlbHkgaW4gdGhlIGJyb3dzZXIuIFRoZSBmaXJzdCBydW5cbiAgICBvZiBhbnkgbW9kZWwgZG93bmxvYWRzIGl0IChhIHByb2dyZXNzIHRvYXN0IGlzIHNob3duKTsgaXQncyB0aGVuIGNhY2hlZC48L3A+XG4gIDxwPlR3byBzaGFwZXMgb2YgdG9vbDogdGhlIDxiPnByb21wdGVkPC9iPiBvbmVzIChTZWdtZW50LCBQb2ludCBwcm9tcHRzLCBDZWxscG9zZSkgYWN0IG9uXG4gICAgcmVjdGFuZ2xlcyB5b3UgZHJhdywgYW5kIGdlbmVyYXRlZCByZWdpb25zIGluaGVyaXQgdGhlIGNvbG9yIG9mIHRoZSByZWN0YW5nbGUgdGhleSBjYW1lXG4gICAgZnJvbTxzcGFuICpuZ0lmPVwiY29udHJpYnV0ZWRUb29scy5sZW5ndGhcIj47IHRoZSA8Yj5uby1wcm9tcHQ8L2I+IG9uZXMgKHt7IGNvbnRyaWJ1dGVkVG9vbE5hbWVzIH19KVxuICAgIHN3ZWVwIHRoZSB3aG9sZSBjdXJyZW50IHZpZXcsIHNvIHRoZXJlIGlzIG5vdGhpbmcgdG8gZHJhdyBmaXJzdDwvc3Bhbj4uPC9wPlxuICA8cD5FYWNoIG1vZGVsIGRyb3Bkb3duIDxpIGNsYXNzPVwicGkgcGktY2hldnJvbi1kb3duXCI+PC9pPiBsaXN0cyB0aGUgYXZhaWxhYmxlIGNoZWNrcG9pbnRzIHdpdGhcbiAgICBhIDxpIGNsYXNzPVwicGkgcGktaW5mby1jaXJjbGVcIj48L2k+IGJlc2lkZSBlYWNoIG9uZSDigJQgaG92ZXIgaXQgZm9yIHRoYXQgbW9kZWwncyBzaXplLCBzcGVlZCxcbiAgICB0cmFpbmluZyBkb21haW4gYW5kIGFjY3VyYWN5LCBzbyB5b3UgY2FuIHBpY2sgd2l0aG91dCBsZWF2aW5nIHRoZSB0b29sYmFyLjwvcD5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPlNlZ21lbnQ8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc2FtLWJveC1wcm9tcHQuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+XG4gICAgICAoYm94IHByb21wdCk6IGRyYXcgb25lIG9yIG1vcmUgPGI+UmVjdGFuZ3VsYXIgcmVnaW9uczwvYj4gYXJvdW5kIHRoZSBvYmplY3RzIG9mIGludGVyZXN0LFxuICAgICAgdGhlbiBjbGljayBTZWdtZW50LiBFYWNoIHJlY3RhbmdsZSBpcyByZXBsYWNlZCBieSB0aGUgU0FNIG1hc2sgb2YgdGhlIG9iamVjdCBpbnNpZGUgaXQuPC9saT5cbiAgICA8bGk+XCI8Yj5Qb2ludCBwcm9tcHRzPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NhbS1wb2ludC1wcm9tcHQuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OiBjbGljayBkaXJlY3RseSBvbiBhbiBvYmplY3QgdG9cbiAgICAgIHNlZ21lbnQgaXQgYXMgYSA8Yj5uZXc8L2I+IHJlZ2lvbi4gRWFjaCBjbGljayBzdGFydHMgYSBmcmVzaCBvYmplY3Qg4oCUIGNsaWNraW5nIGFub3RoZXJcbiAgICAgIG9iamVjdCB3b24ndCBncm93IHRoZSBwcmV2aW91cyBvbmUuIDxrYmQ+U2hpZnQ8L2tiZD4vPGtiZD5BbHQ8L2tiZD4tY2xpY2sgYWRkcyBhblxuICAgICAgPGk+ZXhjbHVkZTwvaT4gcG9pbnQgdGhhdCByZWZpbmVzIHRoZSBjdXJyZW50IG9iamVjdDsgPGtiZD5Fc2M8L2tiZD4gdW5kb2VzIGl0LFxuICAgICAgPGtiZD5FbnRlcjwva2JkPiBjb21taXRzIGl0LjwvbGk+XG4gICAgPGxpPlwiPGI+TW9kZWwgcGlja2VyPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktY2hldnJvbi1kb3duXCI+PC9pPjogdGhlIGRyb3Bkb3duIGF0dGFjaGVkIHRvIHRoZVxuICAgICAgU2VnbWVudCBidXR0b24gY2hvb3NlcyB0aGUgU0FNIG1vZGVsIOKAlCA8Yj5taWNyby1zYW0gVmlULVQ8L2I+IChkZWZhdWx0OyBzbWFsbCwgZmFzdCkgb3JcbiAgICAgIDxiPm1pY3JvLXNhbSBWaVQtQjwvYj4gKGxhcmdlciwgaGlnaGVzdCBhY2N1cmFjeSkgZm9yIGxpZ2h0IG1pY3Jvc2NvcHksIG9yXG4gICAgICA8Yj5wYXRoby1zYW0gVmlULUI8L2I+IGZvciBoaXN0b3BhdGhvbG9neSAoSCZhbXA7RSAvIHN0YWluZWQgdGlzc3VlKS4gQWxsIGFyZSBwcm9tcHRhYmxlXG4gICAgICBTZWdtZW50IEFueXRoaW5nIG1vZGVscyBmaW5ldHVuZWQgZm9yIGJpb21lZGljYWwgaW1hZ2VzOyB0aGV5IGRpZmZlciBpbiB0aGUgPGk+aW1hZ2VcbiAgICAgIGVuY29kZXI8L2k+ICh0aGUgaGVhdnkgb25jZS1wZXItaW1hZ2Ugc3RlcCkgYW5kIHRoZSB0cmFpbmluZyBkb21haW4g4oCUIGJveCBhbmQgcG9pbnRcbiAgICAgIHByb21wdGluZyBiZWhhdmUgdGhlIHNhbWUuIFRoZSBjaG9pY2UgYXBwbGllcyB0byA8aT5ib3RoPC9pPiB0aGUgYm94IGFuZCBwb2ludCB0b29scywgYW5kXG4gICAgICBlYWNoIHRvb2wgbG9hZHMgaXRzIG93biBlbmNvZGVyLCBzbyB0aGUgZmlyc3QgY2xpY2sgYWZ0ZXIgc3dpdGNoaW5nIHJlLWVuY29kZXMgb25jZS48L2xpPlxuICAgIDxsaT5cIjxiPkNlbGxwb3NlPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL2NlbGxzLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjogZHJhdyByZWN0YW5nbGVzLCB0aGVuIGNsaWNrIENlbGxwb3NlIHRvXG4gICAgICBhdXRvbWF0aWNhbGx5IHNlZ21lbnQgZXZlcnkgY2VsbCBpbnNpZGUgZWFjaCByZWN0YW5nbGUgKGNsaWVudC1zaWRlIGNlbGxwb3NlLVNBTSkg4oCUIG9uZVxuICAgICAgcmVnaW9uIHBlciBkZXRlY3RlZCBjZWxsLiBObyBjbGlja2luZyBvbiBpbmRpdmlkdWFsIGNlbGxzIG5lZWRlZC48L2xpPlxuICAgIDwhLS0gQ29udHJpYnV0ZWQgdG9vbHMgZGVzY3JpYmUgdGhlbXNlbHZlcy4gVGhpcyBsaWJyYXJ5IHNoaXBzIG5vbmUsIHNvIGFuXG4gICAgICAgICBvcGVuIGJ1aWxkJ3MgaGVscCBsaXN0cyBvbmx5IHRoZSBwcm9tcHRlZCB0b29scyBhYm92ZSDigJQgdGhlIHRleHQgbm9cbiAgICAgICAgIGxvbmdlciBwcm9taXNlcyBidXR0b25zIHRoYXQgYXJlIG5vdCB0aGVyZS4gLS0+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdGb3I9XCJsZXQgdG9vbCBvZiBjb250cmlidXRlZFRvb2xzXCI+XG4gICAgICA8bGkgKm5nSWY9XCJ0b29sLmhlbHBcIiBbaW5uZXJIVE1MXT1cInRvb2wuaGVscC5ib2R5XCI+PC9saT5cbiAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCB0b29sIG9mIGRpYWxvZ1Rvb2xzXCI+XG4gICAgICA8bGkgKm5nSWY9XCJ0b29sLmhlbHBcIiBbaW5uZXJIVE1MXT1cInRvb2wuaGVscC5ib2R5XCI+PC9saT5cbiAgICA8L25nLWNvbnRhaW5lcj5cbiAgPC91bD5cblxuICA8aDM+U3BhdGlhbCBvbWljczwvaDM+XG4gIDxwPlxuICAgIEluIDxiPlNwYXRpYWwgb21pY3M8L2I+IG1vZGUgZWFjaCBvYnNlcnZhdGlvbiDigJQgYSBjZWxsIG9yIGEgVmlzaXVtIHNwb3Qg4oCUIGlzIGRyYXduIGF0XG4gICAgaXRzIG93biBjb29yZGluYXRlcywgb3ZlciB0aGUgdGlzc3VlIGltYWdlIHdoZXJlIHRoZSBkYXRhc2V0IHJlZ2lzdGVycyBvbnRvIG9uZS4gVGhlXG4gICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc3BhdGlhbC1vbWljcy5zdmdcIiB3aWR0aD1cIjE0XCIgaGVpZ2h0PVwiMTRcIiBhbHQ9XCJTcGF0aWFsIG9taWNzXCIvPlxuICAgIHBhbmVsIGNvbG91cnMgdGhlbSBieSBhIDxiPmNvbHVtbjwvYj4gKGEgY2VsbCB0eXBlLCBhIGNsdXN0ZXIpIG9yIGJ5IGEgPGI+Z2VuZTwvYj4sIGFuZFxuICAgIHRoZSB0d28gYXJlIGV4Y2x1c2l2ZSBzbyB3aGF0IGRyaXZlcyB0aGUgY29sb3VycyBpcyBuZXZlciBhbWJpZ3VvdXMuXG4gIDwvcD5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPlNlbGVjdCBmcm9tIFJPSXM8L2I+XCI6IHR1cm5zIHRoZSByZWdpb25zIHlvdSBoYXZlIGRyYXduIGludG8gYSBzZWxlY3Rpb24gb2ZcbiAgICAgIG9ic2VydmF0aW9ucy4gRXZlcnl0aGluZyBlbHNlIHRoZW4gZm9sbG93cyB0aGF0IHNlbGVjdGlvbi48L2xpPlxuICAgIDxsaT5cIjxiPlBvaW50IHNpemU8L2I+XCIgLyBcIjxiPk9wYWNpdHk8L2I+XCI6IGhvdyB0aGUgbWFya2VycyBhcmUgZHJhd24sIG5vdCB3aGF0IGlzXG4gICAgICBzZWxlY3RlZC48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5EaXN0cmlidXRpb24gY2hhcnRzPC9oMz5cbiAgPHA+XG4gICAgVGhlIDxiPkRpc3RyaWJ1dGlvbjwvYj4gc2VjdGlvbiBvZiB0aGUgc3BhdGlhbC1vbWljcyBwYW5lbCBjaGFydHMgd2hhdGV2ZXIgdGhlIG1hcCBpc1xuICAgIGNvbG91cmVkIGJ5LCBzbyB0aGUgdHdvIGNhbiBuZXZlciBkaXNhZ3JlZS4gRWFjaCB0YWIgYW5zd2VycyBhIGRpZmZlcmVudCBxdWVzdGlvbiwgYW5kXG4gICAgdGhlIDxpIGNsYXNzPVwicGkgcGktcXVlc3Rpb24tY2lyY2xlXCI+PC9pPiBiZXNpZGUgdGhlbSBleHBsYWlucyB0aGUgb25lIG9uIHNjcmVlbiDigJRcbiAgICBpbmNsdWRpbmcgd2hhdCBpdCBtdXN0IDxlbT5ub3Q8L2VtPiBiZSByZWFkIGZvci5cbiAgPC9wPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+Q291bnRzPC9iPlwiOiBob3cgbWFueSBvYnNlcnZhdGlvbnMgcGVyIGNhdGVnb3J5LiBBIGNhdGVnb3J5IGlzIGEgbGFiZWwsIG5vdCBhXG4gICAgICBtYWduaXR1ZGUsIHNvIGEgZnJlcXVlbmN5IGlzIHRoZSBvbmx5IGRpc3RyaWJ1dGlvbiBpdCBoYXMuPC9saT5cbiAgICA8bGk+XCI8Yj5IaXN0b2dyYW08L2I+XCIgLyBcIjxiPlZpb2xpbjwvYj5cIiAvIFwiPGI+Qm94PC9iPlwiOiBob3cgYSBjb250aW51b3VzIHZhbHVlIGlzXG4gICAgICBkaXN0cmlidXRlZCDigJQgb3ZlciBldmVyeXRoaW5nLCBvciBzcGxpdCBieSBhIGdyb3VwaW5nIGNvbHVtbi48L2xpPlxuICAgIDxsaT5cIjxiPkhlYXRtYXA8L2I+XCI6IG1lYW4gZXhwcmVzc2lvbiBvZiB0aGUgZ2VuZXMgeW91IHBpY2sgd2l0aGluIGVhY2ggZ3JvdXAuXG4gICAgICBaLXNjb3JlZCBwZXIgZ2VuZSBieSBkZWZhdWx0LCBzbyBhIGNvbG91ciBtZWFucyBcImFib3ZlIG9yIGJlbG93IHRoaXMgZ2VuZSdzIG93blxuICAgICAgYXZlcmFnZVwiIHJhdGhlciB0aGFuIFwiaGlnaGx5IGV4cHJlc3NlZFwiLjwvbGk+XG4gICAgPGxpPlwiPGI+RW1iZWRkaW5nPC9iPlwiOiBhIFVNQVAsIFBDQSBvciB0LVNORSBvdmVyIHRoZSBzYW1lIGNlbGxzLiA8Yj5MYXNzbyBpdCB0b1xuICAgICAgc2VsZWN0IG9uIHRoZSBtYXA8L2I+IOKAlCBib3RoIHZpZXdzIHJlYWQgdGhlIHNhbWUgc2VsZWN0aW9uLiBPbmx5IFBDQSBsYWJlbHMgaXRzIGF4ZXNcbiAgICAgIHdpdGggYSBwZXJjZW50YWdlLCBiZWNhdXNlIG9ubHkgaXRzIGF4ZXMgZXhwbGFpbiBhIG1lYXN1cmFibGUgc2hhcmUgb2YgdGhlIHZhcmlhbmNlO1xuICAgICAgVU1BUCBhbmQgdC1TTkUgY29vcmRpbmF0ZXMgYXJlIGFyYml0cmFyeSwgc28gZGlzdGFuY2UgYmV0d2VlbiBkaXN0YW50IGNsdXN0ZXJzXG4gICAgICBzaG91bGQgbm90IGJlIHJlYWQuPC9saT5cbiAgICA8bGk+XCI8Yj5EZXRhY2g8L2I+XCI6IG1vdmVzIHRoZSBjaGFydHMgaW50byB0aGVpciBvd24gd2luZG93IHNvIHRoZXkgY2FuIHNpdCBiZXNpZGUgdGhlXG4gICAgICBtYXAgaW5zdGVhZCBvZiBpbnNpZGUgdGhlIHBhbmVsLiBTd2l0Y2hpbmcgdGFicyBrZWVwcyB0aGUgd2luZG93IHdoZXJlIGl0IGlzLjwvbGk+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==