@jax-data-science/sci-image-visualizer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +404 -0
- package/esm2022/index.mjs +52 -0
- package/esm2022/jax-data-science-sci-image-visualizer.mjs +5 -0
- package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +398 -0
- package/esm2022/lib/contracts/capabilities.contract.mjs +32 -0
- package/esm2022/lib/contracts/cell-segmenter.contract.mjs +12 -0
- package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +14 -0
- package/esm2022/lib/contracts/colormap-lut.mjs +95 -0
- package/esm2022/lib/contracts/coordinate-transform.contract.mjs +2 -0
- package/esm2022/lib/contracts/display-types.mjs +8 -0
- package/esm2022/lib/contracts/image.contract.mjs +11 -0
- package/esm2022/lib/contracts/intensity.mjs +28 -0
- package/esm2022/lib/contracts/plot-type.mjs +112 -0
- package/esm2022/lib/contracts/ports/image-state.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/preferences.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/region-io.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/tile-access.port.mjs +3 -0
- package/esm2022/lib/contracts/region-editor-api.contract.mjs +4 -0
- package/esm2022/lib/contracts/region-overlay.contract.mjs +2 -0
- package/esm2022/lib/contracts/region-store.contract.mjs +2 -0
- package/esm2022/lib/contracts/sam.contract.mjs +12 -0
- package/esm2022/lib/contracts/toolbar-config.mjs +8 -0
- package/esm2022/lib/contracts/visualizer.contract.mjs +10 -0
- package/esm2022/lib/contracts/viz-config.mjs +3 -0
- package/esm2022/lib/hex-color-picker/hex-color-picker.component.mjs +260 -0
- package/esm2022/lib/implementations/base-store-visualizer.mjs +76 -0
- package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +106 -0
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +847 -0
- package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +92 -0
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +2283 -0
- package/esm2022/lib/implementations/napari-js/napari-volume-z-handle.mjs +126 -0
- package/esm2022/lib/implementations/osd/display-pipeline.mjs +143 -0
- package/esm2022/lib/implementations/osd/histogram-sampler.mjs +217 -0
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +1780 -0
- package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +25 -0
- package/esm2022/lib/implementations/osd/osd-coords.mjs +53 -0
- package/esm2022/lib/implementations/osd/osd-lib.mjs +20 -0
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +1237 -0
- package/esm2022/lib/implementations/osd/osd-scale-bar.mjs +88 -0
- package/esm2022/lib/implementations/osd/slice-cache.mjs +488 -0
- package/esm2022/lib/implementations/osd/tile-client.mjs +29 -0
- package/esm2022/lib/implementations/plotly/plotly-coordinate-transform.mjs +37 -0
- package/esm2022/lib/implementations/plotly/plotly-region-overlay.mjs +31 -0
- package/esm2022/lib/implementations/plotly/plotly-trace-builders.mjs +183 -0
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +2137 -0
- package/esm2022/lib/implementations/simple-slice-access.service.mjs +99 -0
- package/esm2022/lib/models/bezier.mjs +120 -0
- package/esm2022/lib/models/class-preset.mjs +46 -0
- package/esm2022/lib/models/geometry.mjs +198 -0
- package/esm2022/lib/models/region.mjs +166 -0
- package/esm2022/lib/models/shape.mjs +53 -0
- package/esm2022/lib/plot.utilities.mjs +693 -0
- package/esm2022/lib/processing/image-converter.service.mjs +90 -0
- package/esm2022/lib/processing/image-crop.mjs +62 -0
- package/esm2022/lib/processing/processing-image.mjs +50 -0
- package/esm2022/lib/provide-visualization.mjs +63 -0
- package/esm2022/lib/region-editor/mask-raster.mjs +95 -0
- package/esm2022/lib/region-editor/mask-worker.mjs +10 -0
- package/esm2022/lib/region-editor/region-editor.component.mjs +1190 -0
- package/esm2022/lib/region-ops.service.mjs +301 -0
- package/esm2022/lib/render-orchestrator.mjs +87 -0
- package/esm2022/lib/routing-visualizer.service.mjs +533 -0
- package/esm2022/lib/store/class-color.util.mjs +52 -0
- package/esm2022/lib/store/region-store.service.mjs +1029 -0
- package/esm2022/lib/store/visualizer-store.service.mjs +352 -0
- package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +546 -0
- package/esm2022/lib/toolbar/crop/slide-crop.mjs +57 -0
- package/esm2022/lib/toolbar/segmentation/cell-segment-tool.service.mjs +115 -0
- package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +84 -0
- package/esm2022/lib/toolbar/segmentation/onnx-sam-session.mjs +133 -0
- package/esm2022/lib/toolbar/segmentation/ort-runtime-config.mjs +31 -0
- package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +108 -0
- package/esm2022/lib/toolbar/segmentation/sam-onnx-core.mjs +159 -0
- package/esm2022/lib/toolbar/segmentation/sam-point-tool.service.mjs +255 -0
- package/esm2022/lib/toolbar/segmentation/sam-prompt.mjs +94 -0
- package/esm2022/lib/toolbar/segmentation/sam-tool.service.mjs +188 -0
- package/esm2022/lib/toolbar/toolbar.component.mjs +311 -0
- package/esm2022/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.mjs +234 -0
- package/esm2022/lib/toolbar/wand/wand-tool.service.mjs +499 -0
- package/esm2022/lib/toolbar/wand/wand.service.mjs +921 -0
- package/esm2022/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.mjs +157 -0
- package/esm2022/lib/visualization.module.mjs +162 -0
- package/esm2022/lib/visualizer.component.mjs +1597 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs +12 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs +327 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +21314 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -0
- package/fesm2022/mask.worker.js +989 -0
- package/fesm2022/onnx-sam.worker.js +202 -0
- package/index.d.ts +39 -0
- package/lib/channel-histogram/channel-histogram.component.d.ts +112 -0
- package/lib/contracts/capabilities.contract.d.ts +30 -0
- package/lib/contracts/cell-segmenter.contract.d.ts +40 -0
- package/lib/contracts/channel-histogram-api.contract.d.ts +98 -0
- package/lib/contracts/colormap-lut.d.ts +21 -0
- package/lib/contracts/coordinate-transform.contract.d.ts +43 -0
- package/lib/contracts/display-types.d.ts +41 -0
- package/lib/contracts/image.contract.d.ts +72 -0
- package/lib/contracts/intensity.d.ts +18 -0
- package/lib/contracts/plot-type.d.ts +108 -0
- package/lib/contracts/ports/image-state.port.d.ts +29 -0
- package/lib/contracts/ports/preferences.port.d.ts +19 -0
- package/lib/contracts/ports/region-io.port.d.ts +29 -0
- package/lib/contracts/ports/tile-access.port.d.ts +27 -0
- package/lib/contracts/region-editor-api.contract.d.ts +83 -0
- package/lib/contracts/region-overlay.contract.d.ts +47 -0
- package/lib/contracts/region-store.contract.d.ts +74 -0
- package/lib/contracts/sam.contract.d.ts +98 -0
- package/lib/contracts/toolbar-config.d.ts +24 -0
- package/lib/contracts/visualizer.contract.d.ts +346 -0
- package/lib/contracts/viz-config.d.ts +25 -0
- package/lib/hex-color-picker/hex-color-picker.component.d.ts +47 -0
- package/lib/implementations/base-store-visualizer.d.ts +71 -0
- package/lib/implementations/napari-js/napari-axes-labels.d.ts +47 -0
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +115 -0
- package/lib/implementations/napari-js/napari-scale-bar.d.ts +31 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +455 -0
- package/lib/implementations/napari-js/napari-volume-z-handle.d.ts +46 -0
- package/lib/implementations/osd/display-pipeline.d.ts +47 -0
- package/lib/implementations/osd/histogram-sampler.d.ts +85 -0
- package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +476 -0
- package/lib/implementations/osd/osd-coordinate-transform.d.ts +17 -0
- package/lib/implementations/osd/osd-coords.d.ts +16 -0
- package/lib/implementations/osd/osd-lib.d.ts +19 -0
- package/lib/implementations/osd/osd-region-overlay.d.ts +191 -0
- package/lib/implementations/osd/osd-scale-bar.d.ts +17 -0
- package/lib/implementations/osd/slice-cache.d.ts +161 -0
- package/lib/implementations/osd/tile-client.d.ts +32 -0
- package/lib/implementations/plotly/plotly-coordinate-transform.d.ts +19 -0
- package/lib/implementations/plotly/plotly-region-overlay.d.ts +23 -0
- package/lib/implementations/plotly/plotly-trace-builders.d.ts +60 -0
- package/lib/implementations/plotly/plotly.service.d.ts +594 -0
- package/lib/implementations/simple-slice-access.service.d.ts +64 -0
- package/lib/models/bezier.d.ts +59 -0
- package/lib/models/class-preset.d.ts +39 -0
- package/lib/models/geometry.d.ts +66 -0
- package/lib/models/region.d.ts +132 -0
- package/lib/models/shape.d.ts +44 -0
- package/lib/plot.utilities.d.ts +107 -0
- package/lib/processing/image-converter.service.d.ts +28 -0
- package/lib/processing/image-crop.d.ts +18 -0
- package/lib/processing/processing-image.d.ts +17 -0
- package/lib/provide-visualization.d.ts +34 -0
- package/lib/region-editor/mask-raster.d.ts +61 -0
- package/lib/region-editor/mask-worker.d.ts +7 -0
- package/lib/region-editor/region-editor.component.d.ts +305 -0
- package/lib/region-ops.service.d.ts +92 -0
- package/lib/render-orchestrator.d.ts +59 -0
- package/lib/routing-visualizer.service.d.ts +227 -0
- package/lib/store/class-color.util.d.ts +26 -0
- package/lib/store/region-store.service.d.ts +315 -0
- package/lib/store/visualizer-store.service.d.ts +123 -0
- package/lib/toolbar/brush/brush-tool.service.d.ts +143 -0
- package/lib/toolbar/crop/slide-crop.d.ts +30 -0
- package/lib/toolbar/segmentation/cell-segment-tool.service.d.ts +32 -0
- package/lib/toolbar/segmentation/cellpose-segmenter.service.d.ts +39 -0
- package/lib/toolbar/segmentation/onnx-sam-session.d.ts +25 -0
- package/lib/toolbar/segmentation/ort-runtime-config.d.ts +4 -0
- package/lib/toolbar/segmentation/sam-model-registry.d.ts +36 -0
- package/lib/toolbar/segmentation/sam-onnx-core.d.ts +48 -0
- package/lib/toolbar/segmentation/sam-point-tool.service.d.ts +59 -0
- package/lib/toolbar/segmentation/sam-prompt.d.ts +34 -0
- package/lib/toolbar/segmentation/sam-tool.service.d.ts +50 -0
- package/lib/toolbar/toolbar.component.d.ts +167 -0
- package/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.d.ts +64 -0
- package/lib/toolbar/wand/wand-tool.service.d.ts +149 -0
- package/lib/toolbar/wand/wand.service.d.ts +133 -0
- package/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.d.ts +49 -0
- package/lib/visualization.module.d.ts +45 -0
- package/lib/visualizer.component.d.ts +378 -0
- package/package.json +73 -0
- package/src/lib/assets/3d-coordinates.svg +49 -0
- package/src/lib/assets/cells.svg +1 -0
- package/src/lib/assets/click.svg +1 -0
- package/src/lib/assets/colormap-luts.json +1 -0
- package/src/lib/assets/contour.svg +48 -0
- package/src/lib/assets/cube-3d.svg +1 -0
- package/src/lib/assets/edit-selection-white.svg +47 -0
- package/src/lib/assets/heatmap.svg +1 -0
- package/src/lib/assets/icons/colormap-Pastel1.png +0 -0
- package/src/lib/assets/icons/colormap-accent.png +0 -0
- package/src/lib/assets/icons/colormap-afmhot.png +0 -0
- package/src/lib/assets/icons/colormap-autumn.png +0 -0
- package/src/lib/assets/icons/colormap-berlin.png +0 -0
- package/src/lib/assets/icons/colormap-binary.png +0 -0
- package/src/lib/assets/icons/colormap-blackbody.png +0 -0
- package/src/lib/assets/icons/colormap-bluered.png +0 -0
- package/src/lib/assets/icons/colormap-blues.png +0 -0
- package/src/lib/assets/icons/colormap-bone.png +0 -0
- package/src/lib/assets/icons/colormap-brbg.png +0 -0
- package/src/lib/assets/icons/colormap-brg.png +0 -0
- package/src/lib/assets/icons/colormap-bugn.png +0 -0
- package/src/lib/assets/icons/colormap-bupu.png +0 -0
- package/src/lib/assets/icons/colormap-bwr.png +0 -0
- package/src/lib/assets/icons/colormap-cividis.png +0 -0
- package/src/lib/assets/icons/colormap-cmrmap.png +0 -0
- package/src/lib/assets/icons/colormap-cool.png +0 -0
- package/src/lib/assets/icons/colormap-coolwarm.png +0 -0
- package/src/lib/assets/icons/colormap-copper.png +0 -0
- package/src/lib/assets/icons/colormap-cubehelix.png +0 -0
- package/src/lib/assets/icons/colormap-dark2.png +0 -0
- package/src/lib/assets/icons/colormap-earth.png +0 -0
- package/src/lib/assets/icons/colormap-electric.png +0 -0
- package/src/lib/assets/icons/colormap-flag.png +0 -0
- package/src/lib/assets/icons/colormap-gist_earth.png +0 -0
- package/src/lib/assets/icons/colormap-gist_gray.png +0 -0
- package/src/lib/assets/icons/colormap-gist_heat.png +0 -0
- package/src/lib/assets/icons/colormap-gist_ncar.png +0 -0
- package/src/lib/assets/icons/colormap-gist_rainbow.png +0 -0
- package/src/lib/assets/icons/colormap-gist_stern.png +0 -0
- package/src/lib/assets/icons/colormap-gist_yarg.png +0 -0
- package/src/lib/assets/icons/colormap-gnbu.png +0 -0
- package/src/lib/assets/icons/colormap-gnuplot.png +0 -0
- package/src/lib/assets/icons/colormap-gnuplot2.png +0 -0
- package/src/lib/assets/icons/colormap-gray.png +0 -0
- package/src/lib/assets/icons/colormap-greens-inv.png +0 -0
- package/src/lib/assets/icons/colormap-greens.png +0 -0
- package/src/lib/assets/icons/colormap-greys-inv.png +0 -0
- package/src/lib/assets/icons/colormap-greys.png +0 -0
- package/src/lib/assets/icons/colormap-hot.png +0 -0
- package/src/lib/assets/icons/colormap-hsv.png +0 -0
- package/src/lib/assets/icons/colormap-inferno.png +0 -0
- package/src/lib/assets/icons/colormap-jet.png +0 -0
- package/src/lib/assets/icons/colormap-magma.png +0 -0
- package/src/lib/assets/icons/colormap-managua.png +0 -0
- package/src/lib/assets/icons/colormap-nipy_spectral.png +0 -0
- package/src/lib/assets/icons/colormap-ocean.png +0 -0
- package/src/lib/assets/icons/colormap-oranges.png +0 -0
- package/src/lib/assets/icons/colormap-orrd.png +0 -0
- package/src/lib/assets/icons/colormap-pastel2.png +0 -0
- package/src/lib/assets/icons/colormap-picnic.png +0 -0
- package/src/lib/assets/icons/colormap-pink.png +0 -0
- package/src/lib/assets/icons/colormap-piyg.png +0 -0
- package/src/lib/assets/icons/colormap-placeholder.png +0 -0
- package/src/lib/assets/icons/colormap-plasma.png +0 -0
- package/src/lib/assets/icons/colormap-portland.png +0 -0
- package/src/lib/assets/icons/colormap-prgn.png +0 -0
- package/src/lib/assets/icons/colormap-prism.png +0 -0
- package/src/lib/assets/icons/colormap-pubu.png +0 -0
- package/src/lib/assets/icons/colormap-pubugn.png +0 -0
- package/src/lib/assets/icons/colormap-puor.png +0 -0
- package/src/lib/assets/icons/colormap-purd.png +0 -0
- package/src/lib/assets/icons/colormap-purples.png +0 -0
- package/src/lib/assets/icons/colormap-rainbow.png +0 -0
- package/src/lib/assets/icons/colormap-rdbu.png +0 -0
- package/src/lib/assets/icons/colormap-rdgy.png +0 -0
- package/src/lib/assets/icons/colormap-rdpu.png +0 -0
- package/src/lib/assets/icons/colormap-rdylbu.png +0 -0
- package/src/lib/assets/icons/colormap-rdylgn.png +0 -0
- package/src/lib/assets/icons/colormap-reds.png +0 -0
- package/src/lib/assets/icons/colormap-seismic.png +0 -0
- package/src/lib/assets/icons/colormap-set1.png +0 -0
- package/src/lib/assets/icons/colormap-set2.png +0 -0
- package/src/lib/assets/icons/colormap-set3.png +0 -0
- package/src/lib/assets/icons/colormap-spectral.png +0 -0
- package/src/lib/assets/icons/colormap-spring.png +0 -0
- package/src/lib/assets/icons/colormap-summer.png +0 -0
- package/src/lib/assets/icons/colormap-tab10.png +0 -0
- package/src/lib/assets/icons/colormap-tab20.png +0 -0
- package/src/lib/assets/icons/colormap-tab20b.png +0 -0
- package/src/lib/assets/icons/colormap-tab20c.png +0 -0
- package/src/lib/assets/icons/colormap-terrain.png +0 -0
- package/src/lib/assets/icons/colormap-turbo.png +0 -0
- package/src/lib/assets/icons/colormap-twilight.png +0 -0
- package/src/lib/assets/icons/colormap-twilight_shifted.png +0 -0
- package/src/lib/assets/icons/colormap-vanimo.png +0 -0
- package/src/lib/assets/icons/colormap-viridis.png +0 -0
- package/src/lib/assets/icons/colormap-winter.png +0 -0
- package/src/lib/assets/icons/colormap-wistia.png +0 -0
- package/src/lib/assets/icons/colormap-ylgn.png +0 -0
- package/src/lib/assets/icons/colormap-ylgnbu-inv.png +0 -0
- package/src/lib/assets/icons/colormap-ylgnbu.png +0 -0
- package/src/lib/assets/icons/colormap-ylorbr.png +0 -0
- package/src/lib/assets/icons/colormap-ylorrd-inv.png +0 -0
- package/src/lib/assets/icons/colormap-ylorrd.png +0 -0
- package/src/lib/assets/interactive-segmentation-cursor.svg +1 -0
- package/src/lib/assets/invert.svg +1 -0
- package/src/lib/assets/isosurface.svg +7 -0
- package/src/lib/assets/mask.svg +1 -0
- package/src/lib/assets/paintbrush.svg +1 -0
- package/src/lib/assets/picture-segmentation.svg +43 -0
- package/src/lib/assets/polygon-vertices.svg +13 -0
- package/src/lib/assets/polyline.svg +3 -0
- package/src/lib/assets/redo.svg +39 -0
- package/src/lib/assets/region-editor.svg +37 -0
- package/src/lib/assets/region-move.svg +7 -0
- package/src/lib/assets/smooth.svg +56 -0
- package/src/lib/assets/spline-chart.svg +7 -0
- package/src/lib/assets/surface.svg +37 -0
- package/src/lib/assets/test/download-379.svg +1 -0
- package/src/lib/assets/test/download-98.svg +1 -0
- package/src/lib/assets/test/json-1.svg +1 -0
- package/src/lib/assets/test/load-53.svg +1 -0
- package/src/lib/assets/test/mask-44.svg +1 -0
- package/src/lib/assets/test/mask-download-44.svg +15 -0
- package/src/lib/assets/test/mask-save-44.svg +11 -0
- package/src/lib/assets/test/overlay-1.svg +1 -0
- package/src/lib/assets/test/overlay-4.svg +1 -0
- package/src/lib/assets/test/region-26.svg +1 -0
- package/src/lib/assets/test/save-56.svg +1 -0
- package/src/lib/assets/test/upload-43.svg +1 -0
- package/src/lib/assets/to-bezier.svg +15 -0
- package/src/lib/assets/to-polygon.svg +11 -0
- package/src/lib/assets/undo.svg +38 -0
- package/src/lib/assets/vertex-add.svg +12 -0
- package/src/lib/assets/vertex-delete.svg +12 -0
- package/src/lib/assets/wand.svg +54 -0
- package/src/lib/assets/zoom-box.svg +10 -0
|
@@ -0,0 +1,1597 @@
|
|
|
1
|
+
import { ChangeDetectorRef, Component, EventEmitter, HostListener, Inject, Input, NgZone, Optional, Output, ViewChild } from '@angular/core';
|
|
2
|
+
import { Subject, Subscription } from 'rxjs';
|
|
3
|
+
import { takeUntil } from 'rxjs/operators';
|
|
4
|
+
import { MessageService } from 'primeng/api';
|
|
5
|
+
import { ContextMenu } from 'primeng/contextmenu';
|
|
6
|
+
import { IMAGE_STATE_PORT } from './contracts/ports/image-state.port';
|
|
7
|
+
import { Polygon, Rectangle, MultiPolygon } from './models/region';
|
|
8
|
+
import { RegionOpsService } from './region-ops.service';
|
|
9
|
+
import { VisualizerStore } from './store/visualizer-store.service';
|
|
10
|
+
import { RenderOrchestrator, SliceScrubber } from './render-orchestrator';
|
|
11
|
+
import { PlotType, isNapari3d, NAPARI_DEFAULT_DECIMATE, } from './contracts/plot-type';
|
|
12
|
+
import { ViewerFeature } from './contracts/capabilities.contract';
|
|
13
|
+
import { VISUALIZER } from './contracts/visualizer.contract';
|
|
14
|
+
import { SAM_MODELS, getDefaultSamModelId, isSamModelReady } from './toolbar/segmentation/sam-model-registry';
|
|
15
|
+
import { SamToolService } from './toolbar/segmentation/sam-tool.service';
|
|
16
|
+
import { SamPointToolService } from './toolbar/segmentation/sam-point-tool.service';
|
|
17
|
+
import { CellSegmentToolService } from './toolbar/segmentation/cell-segment-tool.service';
|
|
18
|
+
import { ALL_TOOLBAR_TOOLS } from './contracts/toolbar-config';
|
|
19
|
+
import { VIZ_CONFIG } from './contracts/viz-config';
|
|
20
|
+
import * as i0 from "@angular/core";
|
|
21
|
+
import * as i1 from "primeng/api";
|
|
22
|
+
import * as i2 from "./store/visualizer-store.service";
|
|
23
|
+
import * as i3 from "./toolbar/segmentation/sam-tool.service";
|
|
24
|
+
import * as i4 from "./toolbar/segmentation/cell-segment-tool.service";
|
|
25
|
+
import * as i5 from "./toolbar/segmentation/sam-point-tool.service";
|
|
26
|
+
import * as i6 from "./region-ops.service";
|
|
27
|
+
import * as i7 from "@angular/common";
|
|
28
|
+
import * as i8 from "@angular/forms";
|
|
29
|
+
import * as i9 from "primeng/button";
|
|
30
|
+
import * as i10 from "primeng/tooltip";
|
|
31
|
+
import * as i11 from "primeng/slider";
|
|
32
|
+
import * as i12 from "primeng/dialog";
|
|
33
|
+
import * as i13 from "primeng/contextmenu";
|
|
34
|
+
import * as i14 from "primeng/progressbar";
|
|
35
|
+
import * as i15 from "primeng/toast";
|
|
36
|
+
import * as i16 from "./toolbar/toolbar.component";
|
|
37
|
+
import * as i17 from "./region-editor/region-editor.component";
|
|
38
|
+
import * as i18 from "./channel-histogram/channel-histogram.component";
|
|
39
|
+
/** Per-instance plot-div id source. The mount element's id must be unique so two
|
|
40
|
+
* live viewers (e.g. the main diagram + a modal preview) don't collide on the
|
|
41
|
+
* same DOM id — `getElementById` would otherwise return whichever came first.
|
|
42
|
+
* Styling hangs off the `.viz-plot` class instead of the id. */
|
|
43
|
+
let plotInstanceSeq = 0;
|
|
44
|
+
export class VisualizerComponent {
|
|
45
|
+
/** Per-instance toast key. MessageService is a global singleton, so two live
|
|
46
|
+
* `<visualizer>` instances (main viewer + pipeline-dialog preview) sharing
|
|
47
|
+
* one key would each render the same message — a duplicate toast. A unique
|
|
48
|
+
* key per instance scopes the toast to the component that raised it. */
|
|
49
|
+
static { this.nextToastId = 0; }
|
|
50
|
+
/** Which toolbar groups to show. A partial override merges over the full set,
|
|
51
|
+
* so `{ specialTools: false }` hides only that group. Defaults to everything. */
|
|
52
|
+
set toolbarTools(v) {
|
|
53
|
+
this._toolbarTools = { ...ALL_TOOLBAR_TOOLS, ...(v ?? {}) };
|
|
54
|
+
}
|
|
55
|
+
get toolbarTools() {
|
|
56
|
+
return this._toolbarTools;
|
|
57
|
+
}
|
|
58
|
+
/** A jit-service cache copy is actively in progress (determinate 0..99). Drives the cache
|
|
59
|
+
* progress bar independently of `imgLoading` so the bar can't be dropped — or masked by the
|
|
60
|
+
* "Sharpening preview..." overlay — mid-download if the loading flag flips early. */
|
|
61
|
+
get isCaching() {
|
|
62
|
+
return this.cacheProgress !== null && this.cacheProgress < 100;
|
|
63
|
+
}
|
|
64
|
+
/** LINE plot type shows the image + draggable line ROI + intensity inset. */
|
|
65
|
+
get isProfileMode() {
|
|
66
|
+
return this.selectedPlotType === PlotType.LINE;
|
|
67
|
+
}
|
|
68
|
+
/** True for the Image plot type, which renders as a natively pan/zoom-able
|
|
69
|
+
* raster — so the backend-agnostic zoom/pan toolbar tools are hidden. The
|
|
70
|
+
* component drives this off the plot type, not the active backend. */
|
|
71
|
+
get isImageView() {
|
|
72
|
+
return this.selectedPlotType === PlotType.IMAGE;
|
|
73
|
+
}
|
|
74
|
+
constructor(state, plotService, messageService, ngZone, cdr, session, samTool, cellSegmentTool, samPointTool, regionOps, vizConfig) {
|
|
75
|
+
this.state = state;
|
|
76
|
+
this.plotService = plotService;
|
|
77
|
+
this.messageService = messageService;
|
|
78
|
+
this.ngZone = ngZone;
|
|
79
|
+
this.cdr = cdr;
|
|
80
|
+
this.session = session;
|
|
81
|
+
this.samTool = samTool;
|
|
82
|
+
this.cellSegmentTool = cellSegmentTool;
|
|
83
|
+
this.samPointTool = samPointTool;
|
|
84
|
+
this.regionOps = regionOps;
|
|
85
|
+
this.vizConfig = vizConfig;
|
|
86
|
+
this.isStackEvent = new EventEmitter(false);
|
|
87
|
+
this.isGrayscaleEvent = new EventEmitter(false);
|
|
88
|
+
this._toolbarTools = ALL_TOOLBAR_TOOLS;
|
|
89
|
+
/** Expose test-only plot modes in the selector (e.g. the napari `Image`
|
|
90
|
+
* mode, otherwise hidden). The host app sets this from a `?test=1` URL
|
|
91
|
+
* toggle. Off by default, so production users never see them. */
|
|
92
|
+
this.testMode = false;
|
|
93
|
+
/** Image-smoothing state for the toolbar's Smoothen toggle (OSD only).
|
|
94
|
+
* Defaults to `false` so OSD shows raw pixels (nearest-neighbour). */
|
|
95
|
+
this.imageSmoothingEnabled = false;
|
|
96
|
+
this.loadingMessage = 'Loading image...';
|
|
97
|
+
this.zoom = false;
|
|
98
|
+
this.unsub = new Subject();
|
|
99
|
+
this.previewSubscription = new Subscription();
|
|
100
|
+
this.stackLoading = false;
|
|
101
|
+
this.imgLoading = false;
|
|
102
|
+
this.loadingPercentage = 0;
|
|
103
|
+
// Non-null (0..100) while jit-service is copying the source file into the
|
|
104
|
+
// local cache PVC on first click. When set, the loading overlay swaps the
|
|
105
|
+
// spinner for a determinate progress bar with a "Caching image" message.
|
|
106
|
+
this.cacheProgress = null;
|
|
107
|
+
// True between the small-tier render landing and the large-tier render
|
|
108
|
+
// landing in the multi-tier rendering path. The template uses this to
|
|
109
|
+
// overlay a translucent spinner on top of the blurry small-tier image so
|
|
110
|
+
// the user doesn't mistake it for the final preview.
|
|
111
|
+
this.sharpening = false;
|
|
112
|
+
this.running = false;
|
|
113
|
+
this.zIndex = 0;
|
|
114
|
+
this.maxIndex = 0;
|
|
115
|
+
this.contextMenuItems = [];
|
|
116
|
+
this.activeDragMode = null;
|
|
117
|
+
/** Whether a region action is available to undo (jit-ui#85). Mirrors the
|
|
118
|
+
* shared RegionStore's history depth; drives the toolbar Undo button. */
|
|
119
|
+
this.canUndoRegion = false;
|
|
120
|
+
/** Whether an undone region action is available to redo (jit-ui#85). */
|
|
121
|
+
this.canRedoRegion = false;
|
|
122
|
+
/** Custom-threshold Simplify dialog (jit-ui#85). */
|
|
123
|
+
this.displaySimplifyDialog = false;
|
|
124
|
+
this.simplifyThreshold = 3;
|
|
125
|
+
/** Current region selection (array indices) — mirrored from the store so the
|
|
126
|
+
* context-menu region actions can read it synchronously (jit-ui#85). */
|
|
127
|
+
this.selectedIndices = [];
|
|
128
|
+
/** Wand sensitivity — higher = stricter (smaller selection). Matches QuPath default. */
|
|
129
|
+
this.wandSensitivity = 2.0;
|
|
130
|
+
/** Brush diameter in image-pixel coordinates (drives the painted disc size). */
|
|
131
|
+
this.brushSize = 40;
|
|
132
|
+
/** SAM model picker options + current selection (jit-ui#90 P1). Only models
|
|
133
|
+
* with a hosted ONNX pair (configured via setSamModelUrls at app init) are
|
|
134
|
+
* offered, so the picker can't select a model that can't run. */
|
|
135
|
+
this.samModels = SAM_MODELS.filter(isSamModelReady).map((m) => ({ id: m.id, label: m.label }));
|
|
136
|
+
this.samModelId = getDefaultSamModelId();
|
|
137
|
+
/** SAM download/segment toast state (bound by the `sam` p-toast template). */
|
|
138
|
+
this.samStatus = '';
|
|
139
|
+
this.samProgress = 0; // 0..100, encoder download
|
|
140
|
+
this.samDownloading = false;
|
|
141
|
+
this.samBusy = false; // any SAM work in flight (drives the indeterminate spinner)
|
|
142
|
+
/** Whether the shared `sam` toast is currently shown (avoids stacking it on
|
|
143
|
+
* every point click, which re-runs inference). */
|
|
144
|
+
this.samToastShown = false;
|
|
145
|
+
this.samToastKey = `sam-${VisualizerComponent.nextToastId++}`;
|
|
146
|
+
/** Subscriptions to the point tool's live feeds (status/busy/download). */
|
|
147
|
+
this.samPointSub = new Subscription();
|
|
148
|
+
/** Vertex eraser radius in image-pixel coordinates. */
|
|
149
|
+
this.vertexEraserRadius = 20;
|
|
150
|
+
this.reversescale = false;
|
|
151
|
+
/** Channels & Histogram dialog visibility (opened from the toolbar). */
|
|
152
|
+
this.showChannelHistogram = false;
|
|
153
|
+
/** Region Editor dialog visibility (opened from the toolbar). */
|
|
154
|
+
this.showRegionEditor = false;
|
|
155
|
+
/** Region Editor dialog width. On open it is set to the configured host
|
|
156
|
+
* element's current width, or — when no selector is configured / the element
|
|
157
|
+
* isn't found — to a quarter of the page (see openRegionEditor). */
|
|
158
|
+
this.regionEditorWidth = '25vw';
|
|
159
|
+
this.stackOptions = [
|
|
160
|
+
{ name: 'Single image', val: 'false' },
|
|
161
|
+
{ name: 'Stack', val: 'true' },
|
|
162
|
+
];
|
|
163
|
+
this.selectedStackOption = this.stackOptions[0];
|
|
164
|
+
this.plotDivName = `viz-plot-${plotInstanceSeq++}`;
|
|
165
|
+
this.plotType = PlotType.IMAGE;
|
|
166
|
+
this.isHeatmap = true;
|
|
167
|
+
this.activeSurface3dMode = 'turntable';
|
|
168
|
+
/** Whether the napari 3D coordinate-axes / scale gizmo is shown (volume/isosurface). */
|
|
169
|
+
this.axesVisible = true;
|
|
170
|
+
this.wireframeActive = false;
|
|
171
|
+
/** napari 3D decimate factor (1 = Full … 8 = ⅛; default ½); changing it re-plots. */
|
|
172
|
+
this.resolutionScale = NAPARI_DEFAULT_DECIMATE;
|
|
173
|
+
/** Plot types the active backend advertises (3D gated by capability). */
|
|
174
|
+
this.plotTypeOptions = [];
|
|
175
|
+
this.selectedPlotType = PlotType.IMAGE;
|
|
176
|
+
/** Floating intensity-profile inset (LINE mode). */
|
|
177
|
+
this.intensityInsetDiv = 'intensity-inset-plot';
|
|
178
|
+
this.profilePanelPos = { x: 20, y: 70 };
|
|
179
|
+
this.profilePanelDragging = false;
|
|
180
|
+
this.profilePanelStart = { mx: 0, my: 0, x: 0, y: 0 };
|
|
181
|
+
/** Toolbar docking: docked across the top by default; dragging its handle
|
|
182
|
+
* detaches it into a floating, movable window (frees the top row for the
|
|
183
|
+
* visualization). */
|
|
184
|
+
this.toolbarFloating = false;
|
|
185
|
+
this.toolbarPos = { x: 8, y: 8 };
|
|
186
|
+
this.toolbarDragging = false;
|
|
187
|
+
this.toolbarStart = { mx: 0, my: 0, x: 0, y: 0 };
|
|
188
|
+
this.latestProfiles = [];
|
|
189
|
+
/** Drives the intensity inset panel's visibility — true whenever any
|
|
190
|
+
* intensity-profile line exists (independent of the current plot type). */
|
|
191
|
+
this.hasProfiles = false;
|
|
192
|
+
/** Isosurface band as a 0–255 slider position, mapped onto the volume's real
|
|
193
|
+
* intensity range by the renderer. Defaults to the full range. */
|
|
194
|
+
this.isoRange = [0, 255];
|
|
195
|
+
/** Debounced z-slice scrubbing (see SliceScrubber — refactoring plan Step 7). */
|
|
196
|
+
this.scrubber = new SliceScrubber((z) => {
|
|
197
|
+
this.plotService.setZIndex(z);
|
|
198
|
+
// In a per-slice z-stack session the store swaps the live region set to this
|
|
199
|
+
// slice (preserving edits made on the previous one); outside stack mode this
|
|
200
|
+
// just records the slice, leaving single-plane regions untouched (jit-ui#93).
|
|
201
|
+
this.plotService.setDisplaySlice(z);
|
|
202
|
+
});
|
|
203
|
+
this.colormapsOptions = plotService.getColormapOptions();
|
|
204
|
+
this.computePlotTypeOptions();
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Plot types offered in the selector for the current image:
|
|
208
|
+
* - Outside **test mode**, only the curated default set (descriptors with a
|
|
209
|
+
* `productionLabel`) is offered, shown under suffix-free names — Image,
|
|
210
|
+
* Heatmap, Contour and the napari Surface / Volume / Isosurface. Test mode
|
|
211
|
+
* exposes every backend's type under its full (backend-suffixed) label.
|
|
212
|
+
* - 3D types hidden when the backend can't render a 3D scene.
|
|
213
|
+
* - stack-only types (volume, isosurface) hidden unless the file is a stack —
|
|
214
|
+
* a volume needs multiple z-slices.
|
|
215
|
+
* - scalar-intensity types (contour, surface, isosurface) hidden for RGB
|
|
216
|
+
* images — they map a single intensity per pixel. Image and Heatmap render
|
|
217
|
+
* any image.
|
|
218
|
+
*/
|
|
219
|
+
computePlotTypeOptions() {
|
|
220
|
+
const caps = this.plotService.capabilities;
|
|
221
|
+
const isStack = !!this.imageInfo?.isStack;
|
|
222
|
+
const isGrayscale = !!this.imageInfo?.isGrayscale;
|
|
223
|
+
this.plotTypeOptions = this.plotService.getPlotTypeDescriptors()
|
|
224
|
+
.filter((d) => {
|
|
225
|
+
// Outside test mode, only the curated set (those with a productionLabel)
|
|
226
|
+
// is offered; test mode exposes every backend's type.
|
|
227
|
+
if (!this.testMode && !d.productionLabel)
|
|
228
|
+
return false;
|
|
229
|
+
if (d.dimensions === '3d' && !caps.has(ViewerFeature.Surface3D))
|
|
230
|
+
return false;
|
|
231
|
+
if (d.requiresStack && !isStack)
|
|
232
|
+
return false;
|
|
233
|
+
if (d.requiresGrayscale && !isGrayscale)
|
|
234
|
+
return false;
|
|
235
|
+
return true;
|
|
236
|
+
})
|
|
237
|
+
// Default selector shows the suffix-free productionLabel; test mode keeps
|
|
238
|
+
// the full backend-suffixed label so same-named modes stay distinguishable.
|
|
239
|
+
.map((d) => (this.testMode ? d : { ...d, label: d.productionLabel }));
|
|
240
|
+
}
|
|
241
|
+
ngOnChanges(changes) {
|
|
242
|
+
// The constructor computes the options with testMode still at its default;
|
|
243
|
+
// recompute once the host binds it (and on any later change) so the selector
|
|
244
|
+
// reflects test mode immediately, without waiting for an image to (re)load.
|
|
245
|
+
if (changes['testMode']) {
|
|
246
|
+
this.computePlotTypeOptions();
|
|
247
|
+
// If test mode is turned off while a test-only type (e.g. NAPARI_IMAGE) is
|
|
248
|
+
// active, that type is no longer offered — fall back to Image.
|
|
249
|
+
this.reconcileSelectedPlotType();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/** If the active plot type is no longer in the offered options (e.g. test mode
|
|
253
|
+
* turned off while a test-only type was active, or a scalar type carried onto
|
|
254
|
+
* an RGB image), fall back to the default 2D Image view. */
|
|
255
|
+
reconcileSelectedPlotType() {
|
|
256
|
+
if (!this.plotTypeOptions.some((d) => d.type === this.selectedPlotType)) {
|
|
257
|
+
this.selectedPlotType = PlotType.IMAGE;
|
|
258
|
+
this.plotType = PlotType.IMAGE;
|
|
259
|
+
this.isHeatmap = true;
|
|
260
|
+
this.plotService.setPlotType(PlotType.IMAGE);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
ngOnInit() {
|
|
264
|
+
this.state.setDiagram(this);
|
|
265
|
+
this.selectedColormap =
|
|
266
|
+
this.colormapsOptions[0].children.find((c) => c.label === 'Greys Inv') ??
|
|
267
|
+
this.colormapsOptions[0].children[0];
|
|
268
|
+
this.stackOptions = [
|
|
269
|
+
{ name: 'Single image', val: 'false' },
|
|
270
|
+
{ name: 'Stack', val: 'true' },
|
|
271
|
+
];
|
|
272
|
+
this.selectedStackOption = this.stackOptions[0];
|
|
273
|
+
this.autoscaleSubscription = this.plotService.getAutoscaleEvent().subscribe(() => {
|
|
274
|
+
// reset the image mode to single image
|
|
275
|
+
this.selectedStackOption = { name: 'Single image', val: 'false' };
|
|
276
|
+
this.activeDragMode = null;
|
|
277
|
+
this.session.setActiveTool(null);
|
|
278
|
+
});
|
|
279
|
+
this.imageLoadingSubscription = this.state.isImageLoading$().subscribe((isImageLoading) => {
|
|
280
|
+
this.imgLoading = isImageLoading;
|
|
281
|
+
});
|
|
282
|
+
// Undo availability (jit-ui#85): the shared RegionStore emits whenever its
|
|
283
|
+
// history depth changes; greys out the toolbar Undo button accordingly.
|
|
284
|
+
this.plotService
|
|
285
|
+
.getCanUndo$()
|
|
286
|
+
.pipe(takeUntil(this.unsub))
|
|
287
|
+
.subscribe((canUndo) => {
|
|
288
|
+
this.canUndoRegion = canUndo;
|
|
289
|
+
this.cdr.detectChanges();
|
|
290
|
+
});
|
|
291
|
+
this.plotService
|
|
292
|
+
.getCanRedo$()
|
|
293
|
+
.pipe(takeUntil(this.unsub))
|
|
294
|
+
.subscribe((canRedo) => {
|
|
295
|
+
this.canRedoRegion = canRedo;
|
|
296
|
+
this.cdr.detectChanges();
|
|
297
|
+
});
|
|
298
|
+
// Mirror the region selection so the right-click action group (jit-ui#85)
|
|
299
|
+
// can read it synchronously when the menu is built.
|
|
300
|
+
this.plotService
|
|
301
|
+
.getSelectedShapeIndices$()
|
|
302
|
+
.pipe(takeUntil(this.unsub))
|
|
303
|
+
.subscribe((indices) => {
|
|
304
|
+
this.selectedIndices = indices || [];
|
|
305
|
+
});
|
|
306
|
+
// Interactive point-prompt segmentation runs inside the renderer on each
|
|
307
|
+
// click; surface its live status + download progress in the shared `sam`
|
|
308
|
+
// toast so the user sees it working (the first click pulls the encoder).
|
|
309
|
+
this.samPointSub.add(this.samPointTool.progress$.subscribe((f) => {
|
|
310
|
+
this.samDownloading = f >= 0 && f < 1;
|
|
311
|
+
if (f >= 0)
|
|
312
|
+
this.samProgress = Math.min(100, Math.round(f * 100));
|
|
313
|
+
this.cdr.detectChanges();
|
|
314
|
+
}));
|
|
315
|
+
this.samPointSub.add(this.samPointTool.status$.subscribe((m) => {
|
|
316
|
+
this.samStatus = m;
|
|
317
|
+
this.cdr.detectChanges();
|
|
318
|
+
}));
|
|
319
|
+
this.samPointSub.add(this.samPointTool.busy$.subscribe((busy) => {
|
|
320
|
+
this.samBusy = busy;
|
|
321
|
+
if (busy)
|
|
322
|
+
this.showSamToast('SAM point segmentation');
|
|
323
|
+
this.cdr.detectChanges();
|
|
324
|
+
}));
|
|
325
|
+
this.plotService.getStackLoadingProgress().subscribe((loadingProgress) => {
|
|
326
|
+
this.loadingPercentage = loadingProgress;
|
|
327
|
+
});
|
|
328
|
+
this.plotService.isStackLoading().subscribe((stackLoading) => {
|
|
329
|
+
this.stackLoading = stackLoading;
|
|
330
|
+
});
|
|
331
|
+
this.plotWidthSubscription = this.state.getPanelWidth$().subscribe(() => {
|
|
332
|
+
this.plotService.relayout();
|
|
333
|
+
// The intensity inset is a separate Plotly chart in a floating panel; reflow
|
|
334
|
+
// it to its current size when the canvas resizes, else it keeps the stale
|
|
335
|
+
// size and blanks out. Defer a tick so the panel layout has settled.
|
|
336
|
+
if (this.hasProfiles) {
|
|
337
|
+
setTimeout(() => this.renderIntensityInset(), 0);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
this.imgLoadingMessageSubscription = this.state.getImageLoadingMessage$().subscribe((message) => {
|
|
341
|
+
this.loadingMessage = message;
|
|
342
|
+
});
|
|
343
|
+
this.state
|
|
344
|
+
.getCacheProgress$()
|
|
345
|
+
.pipe(takeUntil(this.unsub))
|
|
346
|
+
.subscribe((progress) => {
|
|
347
|
+
this.cacheProgress = progress;
|
|
348
|
+
});
|
|
349
|
+
this.isZoomSubscription = this.state.isZoom$().subscribe((zoom) => {
|
|
350
|
+
this.zoom = zoom;
|
|
351
|
+
});
|
|
352
|
+
this.filenameSubscription = this.state.getFilename$().subscribe((filename) => {
|
|
353
|
+
if (filename) {
|
|
354
|
+
this.fileName = filename;
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
this.plotService.getColormap().subscribe((colormap) => {
|
|
358
|
+
this.selectedColormap = colormap;
|
|
359
|
+
});
|
|
360
|
+
this.plotService.getReverseScale().subscribe((reversescale) => {
|
|
361
|
+
this.reversescale = reversescale;
|
|
362
|
+
});
|
|
363
|
+
this.intensityProfileSub = this.plotService.getIntensityProfile$().subscribe((profiles) => {
|
|
364
|
+
this.latestProfiles = profiles;
|
|
365
|
+
this.hasProfiles = profiles.length > 0;
|
|
366
|
+
// The panel div is behind *ngIf="hasProfiles". detectChanges() materializes
|
|
367
|
+
// it synchronously (the subscription may fire outside Angular's zone — e.g.
|
|
368
|
+
// from an OSD drag — so CD wouldn't run on its own). Then render on the next
|
|
369
|
+
// animation frame, AFTER the browser lays the panel out: a synchronous draw
|
|
370
|
+
// hits a zero-size container on first create and Plotly keeps that size, so
|
|
371
|
+
// the inset stays blank and live updates redraw into the same zero box.
|
|
372
|
+
this.cdr.detectChanges();
|
|
373
|
+
requestAnimationFrame(() => this.renderIntensityInset());
|
|
374
|
+
});
|
|
375
|
+
// When the OSD view settles at a new zoom/pan, re-sample the intensity lines
|
|
376
|
+
// from a crop of the visible region so the inset reflects the zoom-level
|
|
377
|
+
// resolution (Plotly's own high-def zoom updates the sampling cache inline).
|
|
378
|
+
this.viewportChangeSub = this.plotService.getViewportChange$().subscribe((roi) => {
|
|
379
|
+
if (this.hasProfiles && this.isImageView) {
|
|
380
|
+
this.plotService.refreshIntensitySamplingForRoi(roi.x, roi.y, roi.width, roi.height, this.zIndex);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
this.previewSubscription = this.state.getImageInfo$().subscribe({
|
|
384
|
+
next: (imgInfo) => {
|
|
385
|
+
if (imgInfo) {
|
|
386
|
+
this.imageInfo = imgInfo;
|
|
387
|
+
// Stack-only plot types (isosurface, scatter3d) depend on whether this
|
|
388
|
+
// file is a stack — recompute the selector options.
|
|
389
|
+
this.computePlotTypeOptions();
|
|
390
|
+
// If the active plot type isn't valid for this image (e.g. a scalar
|
|
391
|
+
// type like Contour carried over to an RGB image), fall back to Image.
|
|
392
|
+
this.reconcileSelectedPlotType();
|
|
393
|
+
// reset stack options selection
|
|
394
|
+
this.selectedStackOption = imgInfo.showStack ? this.stackOptions[1] : this.stackOptions[0];
|
|
395
|
+
this.isGrayscaleEvent.emit(this.imageInfo.isGrayscale);
|
|
396
|
+
this.isStackEvent.emit(this.imageInfo.isStack);
|
|
397
|
+
// Reset to the default 2D Image view when a different image is
|
|
398
|
+
// selected while a 3D type is active.
|
|
399
|
+
if (!this.isHeatmap && imgInfo.fileName !== this.loadedFileName) {
|
|
400
|
+
this.isHeatmap = true;
|
|
401
|
+
this.plotType = PlotType.IMAGE;
|
|
402
|
+
this.selectedPlotType = PlotType.IMAGE;
|
|
403
|
+
this.plotService.setPlotType(this.plotType);
|
|
404
|
+
this.activeSurface3dMode = 'turntable';
|
|
405
|
+
}
|
|
406
|
+
this.loadedFileName = imgInfo.fileName;
|
|
407
|
+
this.plotService.setImageMeta(this.imageInfo.imageMeta);
|
|
408
|
+
const urls = imgInfo.urls;
|
|
409
|
+
if (!this.running) {
|
|
410
|
+
// image size — measure the plot div directly so the toolbar height is excluded
|
|
411
|
+
const plotDiv = document.getElementById(this.plotDivName);
|
|
412
|
+
const screenHeight = plotDiv?.offsetHeight || 500;
|
|
413
|
+
if (urls) {
|
|
414
|
+
this.plotService.reset();
|
|
415
|
+
this.stackLoading = imgInfo.isStack && imgInfo.showStack;
|
|
416
|
+
// set max index of stack — computed before updateZIndex so a
|
|
417
|
+
// fresh stack (shorter or longer than whatever was previously
|
|
418
|
+
// loaded) clamps against ITS bounds, not the stale ones.
|
|
419
|
+
// One URL per slice (0-indexed), so the last reachable index is
|
|
420
|
+
// length-1 — earlier `length-2` dropped the final slice.
|
|
421
|
+
this.maxIndex = urls.length > 1 ? urls.length - 1 : 0;
|
|
422
|
+
// One-shot hint: jump straight to a specific slice (e.g. the
|
|
423
|
+
// file the user actually clicked within a numbered series).
|
|
424
|
+
// Consumed immediately so redelivering the same ImageInfo later
|
|
425
|
+
// (a plot-type switch, reloadAndPlot) doesn't reset the user's
|
|
426
|
+
// current scrub position back to it.
|
|
427
|
+
if (imgInfo.initialZIndex !== undefined) {
|
|
428
|
+
this.zIndex = imgInfo.initialZIndex;
|
|
429
|
+
imgInfo.initialZIndex = undefined;
|
|
430
|
+
}
|
|
431
|
+
// make sure the zindex is within bounds
|
|
432
|
+
this.updateZIndex(urls);
|
|
433
|
+
this.running = true;
|
|
434
|
+
// Multi-tier rendering (small blurry tier first, then sharpen in
|
|
435
|
+
// place) — sequencing lives in RenderOrchestrator; this component
|
|
436
|
+
// supplies the phase render and owns the UI flags via callbacks.
|
|
437
|
+
// 3D plot types render single-pass: the in-place large pass doesn't
|
|
438
|
+
// rebuild a 3D gl-mesh isosurface, so the sharpen step blanked it.
|
|
439
|
+
const hasSmallTier = this.isHeatmap &&
|
|
440
|
+
(imgInfo.smallUrls?.length ?? 0) === urls.length &&
|
|
441
|
+
(imgInfo.smallUrls?.length ?? 0) > 0;
|
|
442
|
+
const smallImgInfo = hasSmallTier ? { ...imgInfo, urls: imgInfo.smallUrls } : null;
|
|
443
|
+
// Enter per-slice stack mode with the given slice→regions map and
|
|
444
|
+
// seed the "previous shapes" from the now-live slice. enterStackMode
|
|
445
|
+
// makes zIndex's slice live and resets undo (jit-ui#93).
|
|
446
|
+
const enterStack = (slices, layout) => {
|
|
447
|
+
this.plotService.enterStackMode(slices, this.zIndex, layout);
|
|
448
|
+
const shapes = this.plotService
|
|
449
|
+
.getRegions()
|
|
450
|
+
.map((region) => region.getShape(this.plotService.getShowShapeLabel()));
|
|
451
|
+
this.plotService.setPreviousShapes(shapes);
|
|
452
|
+
};
|
|
453
|
+
const applyRoi = () => {
|
|
454
|
+
// Folder stack: a stack of self-contained per-slice files
|
|
455
|
+
// (tiled === false; see loadSeriesAsStack). Each slice-file may
|
|
456
|
+
// carry its own sibling "<stem>.geojson" (roiJsonStrs[z]), but a
|
|
457
|
+
// fresh folder with none yet leaves roiJsonStrs undefined — key
|
|
458
|
+
// off `tiled === false`, NOT roiJsonStrs, so an unannotated
|
|
459
|
+
// folder stack still enters the per-slice-file layout (and saves
|
|
460
|
+
// back one geojson per slice-file) rather than falling through to
|
|
461
|
+
// the single-file combined path (jit-ui#93).
|
|
462
|
+
if (imgInfo.isStack && imgInfo.tiled === false) {
|
|
463
|
+
const perSlice = imgInfo.roiJsonStrs;
|
|
464
|
+
const sliceCount = imgInfo.urls?.length ?? perSlice?.length ?? 0;
|
|
465
|
+
const slices = new Map();
|
|
466
|
+
for (let z = 0; z < sliceCount; z++) {
|
|
467
|
+
const json = perSlice?.[z] ?? null;
|
|
468
|
+
slices.set(z, json ? this.plotService.importRegions(json) : []);
|
|
469
|
+
}
|
|
470
|
+
enterStack(slices, 'per-slice-file');
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
// Single-file z-stack: one sibling geojson holding every slice's
|
|
474
|
+
// regions indexed by QuPath's geometry.plane.z. Enter per-slice
|
|
475
|
+
// mode (saving back one combined z-indexed geojson) when the
|
|
476
|
+
// geojson actually carries slice indices, or when there's nothing
|
|
477
|
+
// yet to author against. A legacy geojson whose regions are all on
|
|
478
|
+
// the default plane stays global (shown on every slice) so
|
|
479
|
+
// existing single-plane annotations aren't confined to slice 0.
|
|
480
|
+
const roiJson = imgInfo.roiJsonStr;
|
|
481
|
+
if (imgInfo.isStack) {
|
|
482
|
+
const regions = roiJson ? this.plotService.importRegions(roiJson) : [];
|
|
483
|
+
const hasSliceInfo = regions.some((r) => (r.z ?? 0) !== 0);
|
|
484
|
+
if (!roiJson || hasSliceInfo) {
|
|
485
|
+
const slices = new Map();
|
|
486
|
+
for (const r of regions) {
|
|
487
|
+
const z = r.z ?? 0;
|
|
488
|
+
const bucket = slices.get(z);
|
|
489
|
+
if (bucket)
|
|
490
|
+
bucket.push(r);
|
|
491
|
+
else
|
|
492
|
+
slices.set(z, [r]);
|
|
493
|
+
}
|
|
494
|
+
enterStack(slices, 'combined');
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
// Single-plane image (or a legacy global z-stack geojson): one
|
|
499
|
+
// region set for the whole image.
|
|
500
|
+
if (roiJson) {
|
|
501
|
+
const regions = this.plotService.importRegions(roiJson);
|
|
502
|
+
this.plotService.setRegions(regions);
|
|
503
|
+
const shapes = regions.map((region) => region.getShape(this.plotService.getShowShapeLabel()));
|
|
504
|
+
this.plotService.setPreviousShapes(shapes);
|
|
505
|
+
}
|
|
506
|
+
// Loading an image's saved ROIs is not a user edit — start the
|
|
507
|
+
// undo history fresh so the first undo can't wipe them (jit-ui#85).
|
|
508
|
+
this.plotService.resetUndoHistory();
|
|
509
|
+
};
|
|
510
|
+
let overlayReleased = false;
|
|
511
|
+
const releaseOverlay = () => {
|
|
512
|
+
if (overlayReleased)
|
|
513
|
+
return;
|
|
514
|
+
overlayReleased = true;
|
|
515
|
+
if (imgInfo.isStack && imgInfo.showStack)
|
|
516
|
+
this.stackLoading = false;
|
|
517
|
+
this.state.setImageLoading(false);
|
|
518
|
+
};
|
|
519
|
+
new RenderOrchestrator({
|
|
520
|
+
// inPlace=true updates the existing render instead of rebuilding
|
|
521
|
+
// it, so the canvas doesn't blank during the small→large swap.
|
|
522
|
+
renderPhase: (phaseInfo, inPlace) => this.plotService.load(phaseInfo, this.zIndex).then((loadedImage) => {
|
|
523
|
+
// Guard against a newer click reaching us mid-render.
|
|
524
|
+
if (phaseInfo.fileName !== loadedImage.filename)
|
|
525
|
+
return null;
|
|
526
|
+
return this.plotService.plot(this.plotDivName, loadedImage, phaseInfo, screenHeight, this.plotType, inPlace);
|
|
527
|
+
}),
|
|
528
|
+
smallShown: () => {
|
|
529
|
+
// If the file is still being cached/prepared server-side, keep the full
|
|
530
|
+
// cache-progress overlay up instead of dropping to the translucent "Sharpening
|
|
531
|
+
// preview..." spinner over a blank canvas (a large uncached image renders its
|
|
532
|
+
// small preview before its tiles exist). finished() releases the overlay once
|
|
533
|
+
// the real render lands.
|
|
534
|
+
if (this.isCaching)
|
|
535
|
+
return;
|
|
536
|
+
// Small tier on screen — drop the full overlay but keep a translucent spinner so
|
|
537
|
+
// the blurry render isn't mistaken for the final image.
|
|
538
|
+
releaseOverlay();
|
|
539
|
+
this.sharpening = true;
|
|
540
|
+
},
|
|
541
|
+
sharpenSettled: () => {
|
|
542
|
+
this.sharpening = false;
|
|
543
|
+
},
|
|
544
|
+
finished: (_viaSmall, logTag) => {
|
|
545
|
+
// Idempotent — releases now if smallShown deferred it (caching) or was skipped.
|
|
546
|
+
releaseOverlay();
|
|
547
|
+
this.running = false;
|
|
548
|
+
applyRoi();
|
|
549
|
+
console.log(logTag);
|
|
550
|
+
},
|
|
551
|
+
sharpenFailed: (err) => {
|
|
552
|
+
// The small tier stays on screen as the fallback — tell the
|
|
553
|
+
// user the sharper version isn't coming.
|
|
554
|
+
const msg = err?.error?.message || err?.message || err?.statusText || String(err);
|
|
555
|
+
this.messageService.add({
|
|
556
|
+
key: 'center-toast',
|
|
557
|
+
severity: 'warn',
|
|
558
|
+
summary: 'Preview not sharpened',
|
|
559
|
+
detail: `The full-resolution preview did not load (${msg}). The low-resolution preview is still shown. Try clicking the image again.`,
|
|
560
|
+
});
|
|
561
|
+
this.running = false;
|
|
562
|
+
applyRoi();
|
|
563
|
+
},
|
|
564
|
+
}).render(imgInfo, smallImgInfo);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
error: (err) => {
|
|
570
|
+
const msg = err?.error?.message || err?.message || err?.statusText || String(err);
|
|
571
|
+
console.error('Error occured when getting image info', err);
|
|
572
|
+
this.messageService.add({
|
|
573
|
+
key: 'center-toast',
|
|
574
|
+
sticky: true,
|
|
575
|
+
severity: 'error',
|
|
576
|
+
summary: 'An error occured',
|
|
577
|
+
detail: `The following error occured while getting image info: ${msg}.
|
|
578
|
+
Please try to open the image again through the file navigator.`,
|
|
579
|
+
});
|
|
580
|
+
this.stackLoading = false;
|
|
581
|
+
this.state.setImageLoading(false);
|
|
582
|
+
this.running = false;
|
|
583
|
+
this.plotService.reset();
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
ngAfterViewInit() {
|
|
588
|
+
this.plotContextMenuListener = (event) => {
|
|
589
|
+
const plotEl = document.getElementById(this.plotDivName);
|
|
590
|
+
if (!plotEl?.contains(event.target))
|
|
591
|
+
return;
|
|
592
|
+
event.preventDefault();
|
|
593
|
+
event.stopImmediatePropagation();
|
|
594
|
+
this.ngZone.run(() => {
|
|
595
|
+
this.contextMenuItems = this.buildContextMenuItems();
|
|
596
|
+
this.cdr.detectChanges();
|
|
597
|
+
this.contextMenu.show(event);
|
|
598
|
+
});
|
|
599
|
+
};
|
|
600
|
+
window.addEventListener('contextmenu', this.plotContextMenuListener, true);
|
|
601
|
+
this.keydownListener = (event) => {
|
|
602
|
+
const target = event.target;
|
|
603
|
+
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)
|
|
604
|
+
return;
|
|
605
|
+
// SAM point mode: Enter commits the object, Esc clears the prompt.
|
|
606
|
+
if (this.activeDragMode === 'samPoint' && (event.key === 'Enter' || event.key === 'Escape')) {
|
|
607
|
+
this.ngZone.run(() => {
|
|
608
|
+
if (event.key === 'Enter')
|
|
609
|
+
this.plotService.commitSamPoints();
|
|
610
|
+
else
|
|
611
|
+
this.plotService.clearSamPoints();
|
|
612
|
+
this.hideSamToast(); // prompt resolved → dismiss the status toast
|
|
613
|
+
});
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
// Region history (jit-ui#85): Ctrl/Cmd+Z undoes; Ctrl/Cmd+Shift+Z or
|
|
617
|
+
// Ctrl/Cmd+Y redoes.
|
|
618
|
+
if (event.ctrlKey || event.metaKey) {
|
|
619
|
+
const k = event.key.toLowerCase();
|
|
620
|
+
if (k === 'z' && !event.shiftKey) {
|
|
621
|
+
event.preventDefault();
|
|
622
|
+
this.ngZone.run(() => this.undoRegion());
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if ((k === 'z' && event.shiftKey) || k === 'y') {
|
|
626
|
+
event.preventDefault();
|
|
627
|
+
this.ngZone.run(() => this.redoRegion());
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (event.key === 'Delete' || event.key === 'Backspace' || event.key === 'd' || event.key === 'D') {
|
|
632
|
+
this.ngZone.run(() => this.deleteRegion());
|
|
633
|
+
}
|
|
634
|
+
else if (event.key === '+' || event.key === '=') {
|
|
635
|
+
this.ngZone.run(() => this.zoomIn());
|
|
636
|
+
}
|
|
637
|
+
else if (event.key === '-' || event.key === '_') {
|
|
638
|
+
this.ngZone.run(() => this.zoomOut());
|
|
639
|
+
}
|
|
640
|
+
else if (event.key === 'p') {
|
|
641
|
+
this.ngZone.run(() => this.toggleDragMode('pan'));
|
|
642
|
+
}
|
|
643
|
+
else if (event.key === 'b') {
|
|
644
|
+
this.ngZone.run(() => this.toggleDragMode('zoomToBox'));
|
|
645
|
+
}
|
|
646
|
+
else if (event.key === 'r') {
|
|
647
|
+
this.ngZone.run(() => this.toggleDragMode('drawrect'));
|
|
648
|
+
}
|
|
649
|
+
else if (event.key === 'f') {
|
|
650
|
+
this.ngZone.run(() => this.toggleDragMode('drawclosedpath'));
|
|
651
|
+
}
|
|
652
|
+
else if (event.key === 'w') {
|
|
653
|
+
this.ngZone.run(() => this.toggleDragMode('wand'));
|
|
654
|
+
}
|
|
655
|
+
else if (event.key === 'e') {
|
|
656
|
+
this.ngZone.run(() => this.toggleDragMode('eraseVertex'));
|
|
657
|
+
}
|
|
658
|
+
else if (event.key === 's') {
|
|
659
|
+
this.ngZone.run(() => this.toggleDragMode('select'));
|
|
660
|
+
}
|
|
661
|
+
else if (event.key === 'l') {
|
|
662
|
+
this.ngZone.run(() => this.toggleDragMode('drawopenpath'));
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
window.addEventListener('keydown', this.keydownListener);
|
|
666
|
+
this.wheelListener = (event) => {
|
|
667
|
+
const plotEl = document.getElementById(this.plotDivName);
|
|
668
|
+
if (!plotEl?.contains(event.target))
|
|
669
|
+
return;
|
|
670
|
+
// The Image view handles scroll-zoom natively (it respects the scroll
|
|
671
|
+
// delta). Intercepting here would fire a fixed zoom step per wheel
|
|
672
|
+
// event — far too sensitive — so let the renderer own it.
|
|
673
|
+
if (this.isImageView)
|
|
674
|
+
return;
|
|
675
|
+
// 3D plot types (surface, scatter3d, isosurface) render in a Plotly scene
|
|
676
|
+
// that orbits/zooms natively on scroll. The 2D step-zoom doesn't apply and
|
|
677
|
+
// would throw (no xaxis on a scene), so let Plotly handle the wheel.
|
|
678
|
+
if (!this.isHeatmap)
|
|
679
|
+
return;
|
|
680
|
+
event.preventDefault();
|
|
681
|
+
event.stopImmediatePropagation();
|
|
682
|
+
if (event.deltaY < 0) {
|
|
683
|
+
this.ngZone.run(() => this.zoomIn());
|
|
684
|
+
}
|
|
685
|
+
else if (event.deltaY > 0) {
|
|
686
|
+
this.ngZone.run(() => this.zoomOut());
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
window.addEventListener('wheel', this.wheelListener, { capture: true, passive: false });
|
|
690
|
+
// Drag handling for the floating intensity-profile panel.
|
|
691
|
+
this.profileDragMoveListener = (e) => {
|
|
692
|
+
if (this.profilePanelDragging) {
|
|
693
|
+
this.ngZone.run(() => {
|
|
694
|
+
this.profilePanelPos = {
|
|
695
|
+
x: this.profilePanelStart.x + (e.clientX - this.profilePanelStart.mx),
|
|
696
|
+
y: this.profilePanelStart.y + (e.clientY - this.profilePanelStart.my),
|
|
697
|
+
};
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
else if (this.toolbarDragging) {
|
|
701
|
+
this.ngZone.run(() => {
|
|
702
|
+
this.toolbarPos = {
|
|
703
|
+
x: this.toolbarStart.x + (e.clientX - this.toolbarStart.mx),
|
|
704
|
+
y: this.toolbarStart.y + (e.clientY - this.toolbarStart.my),
|
|
705
|
+
};
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
this.profileDragUpListener = () => {
|
|
710
|
+
this.profilePanelDragging = false;
|
|
711
|
+
this.toolbarDragging = false;
|
|
712
|
+
};
|
|
713
|
+
window.addEventListener('mousemove', this.profileDragMoveListener);
|
|
714
|
+
window.addEventListener('mouseup', this.profileDragUpListener);
|
|
715
|
+
// On any window resize, reflow the intensity inset to its (fixed) panel size
|
|
716
|
+
// on the next frame, once layout has settled. Without this the inset can be
|
|
717
|
+
// left at a stale/zero size by a mid-reflow resize and stop showing.
|
|
718
|
+
this.profileResizeListener = () => {
|
|
719
|
+
if (!this.hasProfiles)
|
|
720
|
+
return;
|
|
721
|
+
requestAnimationFrame(() => this.renderIntensityInset());
|
|
722
|
+
};
|
|
723
|
+
window.addEventListener('resize', this.profileResizeListener);
|
|
724
|
+
}
|
|
725
|
+
onProfilePanelDragStart(e) {
|
|
726
|
+
this.profilePanelDragging = true;
|
|
727
|
+
this.profilePanelStart = {
|
|
728
|
+
mx: e.clientX,
|
|
729
|
+
my: e.clientY,
|
|
730
|
+
x: this.profilePanelPos.x,
|
|
731
|
+
y: this.profilePanelPos.y,
|
|
732
|
+
};
|
|
733
|
+
e.preventDefault();
|
|
734
|
+
}
|
|
735
|
+
/** Grab the toolbar handle: detach it into a floating window (if still docked)
|
|
736
|
+
* and start dragging. */
|
|
737
|
+
onToolbarDragStart(e) {
|
|
738
|
+
if (!this.toolbarFloating) {
|
|
739
|
+
this.toolbarFloating = true;
|
|
740
|
+
this.toolbarPos = { x: 8, y: 8 };
|
|
741
|
+
}
|
|
742
|
+
this.toolbarDragging = true;
|
|
743
|
+
this.toolbarStart = {
|
|
744
|
+
mx: e.clientX,
|
|
745
|
+
my: e.clientY,
|
|
746
|
+
x: this.toolbarPos.x,
|
|
747
|
+
y: this.toolbarPos.y,
|
|
748
|
+
};
|
|
749
|
+
e.preventDefault();
|
|
750
|
+
}
|
|
751
|
+
/** Snap the floating toolbar back to its docked position across the top. */
|
|
752
|
+
dockToolbar() {
|
|
753
|
+
this.toolbarFloating = false;
|
|
754
|
+
}
|
|
755
|
+
/** Render the intensity inset chart from the latest profile data. The actual
|
|
756
|
+
* charting is owned by the visualizer service — the component just decides
|
|
757
|
+
* when (profile mode + a fresh profile). */
|
|
758
|
+
renderIntensityInset() {
|
|
759
|
+
if (!this.hasProfiles)
|
|
760
|
+
return;
|
|
761
|
+
this.plotService.renderIntensityInset(this.intensityInsetDiv, this.latestProfiles);
|
|
762
|
+
}
|
|
763
|
+
/** Toolbar "Intensity" group: add another line ROI (next bright colour). */
|
|
764
|
+
async addProfileLine() {
|
|
765
|
+
// Park the floating inset near the plot's top-right when the first line is
|
|
766
|
+
// added (it's position:fixed, so use viewport coords from the plot rect).
|
|
767
|
+
if (!this.hasProfiles) {
|
|
768
|
+
const rect = document.getElementById(this.plotDivName)?.getBoundingClientRect();
|
|
769
|
+
this.profilePanelPos = rect
|
|
770
|
+
? { x: Math.max(10, rect.right - 300), y: rect.top + 10 }
|
|
771
|
+
: { x: 20, y: 70 };
|
|
772
|
+
}
|
|
773
|
+
// Image (OSD) mode: Plotly never rendered, so it has no pixel cache / extent.
|
|
774
|
+
// Load the current slice's frames for sampling + line placement first.
|
|
775
|
+
if (this.isImageView && this.imageInfo) {
|
|
776
|
+
await this.plotService.ensureIntensitySampling(this.imageInfo, this.zIndex);
|
|
777
|
+
}
|
|
778
|
+
const region = this.plotService.getIntensityControls()?.addProfileLine();
|
|
779
|
+
// Auto-select the new line on the active backend (Plotly handles / OSD
|
|
780
|
+
// highlight) so it's ready to move or delete immediately.
|
|
781
|
+
if (region) {
|
|
782
|
+
this.plotService.selectRegion(region);
|
|
783
|
+
// OSD only draws a selected region's handles in an edit mode, so a line in
|
|
784
|
+
// 'none' mode looks unselected. Switch to 'select' so the new line shows
|
|
785
|
+
// its endpoint handles and can be dragged/deleted right away.
|
|
786
|
+
if (this.isImageView && this.activeDragMode !== 'select') {
|
|
787
|
+
this.toggleDragMode('select');
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
ngOnDestroy() {
|
|
792
|
+
this.scrubber.cancel();
|
|
793
|
+
this.samPointSub.unsubscribe();
|
|
794
|
+
if (this.plotContextMenuListener) {
|
|
795
|
+
window.removeEventListener('contextmenu', this.plotContextMenuListener, true);
|
|
796
|
+
}
|
|
797
|
+
if (this.keydownListener) {
|
|
798
|
+
window.removeEventListener('keydown', this.keydownListener);
|
|
799
|
+
}
|
|
800
|
+
if (this.wheelListener) {
|
|
801
|
+
window.removeEventListener('wheel', this.wheelListener, true);
|
|
802
|
+
}
|
|
803
|
+
if (this.profileDragMoveListener) {
|
|
804
|
+
window.removeEventListener('mousemove', this.profileDragMoveListener);
|
|
805
|
+
}
|
|
806
|
+
if (this.profileDragUpListener) {
|
|
807
|
+
window.removeEventListener('mouseup', this.profileDragUpListener);
|
|
808
|
+
}
|
|
809
|
+
if (this.profileResizeListener) {
|
|
810
|
+
window.removeEventListener('resize', this.profileResizeListener);
|
|
811
|
+
}
|
|
812
|
+
if (this.intensityProfileSub) {
|
|
813
|
+
this.intensityProfileSub.unsubscribe();
|
|
814
|
+
}
|
|
815
|
+
if (this.viewportChangeSub) {
|
|
816
|
+
this.viewportChangeSub.unsubscribe();
|
|
817
|
+
}
|
|
818
|
+
this.unsub.next();
|
|
819
|
+
this.unsub.complete();
|
|
820
|
+
if (this.previewSubscription) {
|
|
821
|
+
this.previewSubscription.unsubscribe();
|
|
822
|
+
}
|
|
823
|
+
if (this.plotWidthSubscription) {
|
|
824
|
+
this.plotWidthSubscription.unsubscribe();
|
|
825
|
+
}
|
|
826
|
+
if (this.imageLoadingSubscription) {
|
|
827
|
+
this.imageLoadingSubscription.unsubscribe();
|
|
828
|
+
}
|
|
829
|
+
if (this.imgLoadingMessageSubscription) {
|
|
830
|
+
this.imgLoadingMessageSubscription.unsubscribe();
|
|
831
|
+
}
|
|
832
|
+
if (this.isZoomSubscription) {
|
|
833
|
+
this.isZoomSubscription.unsubscribe();
|
|
834
|
+
}
|
|
835
|
+
if (this.autoscaleSubscription) {
|
|
836
|
+
this.autoscaleSubscription.unsubscribe();
|
|
837
|
+
}
|
|
838
|
+
if (this.filenameSubscription) {
|
|
839
|
+
this.filenameSubscription.unsubscribe();
|
|
840
|
+
}
|
|
841
|
+
this.plotService.unsubscribe();
|
|
842
|
+
}
|
|
843
|
+
hasRegions() {
|
|
844
|
+
// Use the contract's framework-neutral region accessor, not the raw
|
|
845
|
+
// Plotly-shaped getShapes() — the component only needs to know whether any
|
|
846
|
+
// region exists, and must not depend on a backend's wire format.
|
|
847
|
+
return this.plotService.getRegions().length > 0;
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* This method is called in mainFacade.configRequest() - DO NOT DELETE
|
|
851
|
+
*/
|
|
852
|
+
getRegionPolygons() {
|
|
853
|
+
return this.plotService.getRegionPolygons();
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Select the color scale type: called by the color scale select button
|
|
857
|
+
* @param event
|
|
858
|
+
*/
|
|
859
|
+
toggleReverseScale() {
|
|
860
|
+
this.reversescale = !this.reversescale;
|
|
861
|
+
this.plotService.setReverseScale(this.reversescale);
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Select a color map: called by the color map dropdown button
|
|
865
|
+
* @param colormapNode
|
|
866
|
+
*/
|
|
867
|
+
selectColormap(colormapNode) {
|
|
868
|
+
if (!colormapNode.children) {
|
|
869
|
+
this.plotService.setColormap(colormapNode);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
/** Open the Channels & Histogram dialog (toolbar button). */
|
|
873
|
+
openChannelHistogram() {
|
|
874
|
+
this.showChannelHistogram = true;
|
|
875
|
+
}
|
|
876
|
+
/** Open the Region Editor dialog (toolbar button). The editor stays linked to
|
|
877
|
+
* this plotting instance through the root RegionStore singleton, so it works
|
|
878
|
+
* the same as the former right-panel Regions tab. */
|
|
879
|
+
openRegionEditor() {
|
|
880
|
+
// Size the dialog to match the host's configured element (e.g. the app's
|
|
881
|
+
// right panel) at open time. Measured once — it intentionally does not
|
|
882
|
+
// track later browser/split resizes. Falls back to a quarter of the page
|
|
883
|
+
// when no selector is configured or the element isn't present.
|
|
884
|
+
const selector = this.vizConfig?.regionEditorWidthSelector;
|
|
885
|
+
const width = selector
|
|
886
|
+
? document.querySelector(selector)?.getBoundingClientRect().width
|
|
887
|
+
: undefined;
|
|
888
|
+
this.regionEditorWidth = width && width > 0 ? `${Math.round(width)}px` : '25vw';
|
|
889
|
+
this.showRegionEditor = true;
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Called by the stack/single image dropdown button
|
|
893
|
+
* @param showstack
|
|
894
|
+
*/
|
|
895
|
+
selectStackOption(selectedStackOption) {
|
|
896
|
+
const showstack = selectedStackOption.val === 'true';
|
|
897
|
+
this.selectedStackOption = selectedStackOption;
|
|
898
|
+
console.log('selected stack option' + JSON.stringify(this.selectedStackOption));
|
|
899
|
+
this.stackLoading = showstack;
|
|
900
|
+
this.state.setImageLoading(!showstack);
|
|
901
|
+
// Stack mode is always a 2D heatmap — reset surface mode if active
|
|
902
|
+
if (!this.isHeatmap) {
|
|
903
|
+
this.isHeatmap = true;
|
|
904
|
+
this.plotType = PlotType.HEATMAP;
|
|
905
|
+
this.selectedPlotType = PlotType.HEATMAP;
|
|
906
|
+
this.plotService.setPlotType(this.plotType);
|
|
907
|
+
}
|
|
908
|
+
this.plotService.setShowStack(showstack);
|
|
909
|
+
}
|
|
910
|
+
updateZIndex(urls) {
|
|
911
|
+
if (this.zIndex > this.maxIndex) {
|
|
912
|
+
this.zIndex = this.maxIndex;
|
|
913
|
+
}
|
|
914
|
+
if (this.zIndex < 0) {
|
|
915
|
+
this.zIndex = 0;
|
|
916
|
+
}
|
|
917
|
+
if (urls) {
|
|
918
|
+
if (this.zIndex > urls?.length) {
|
|
919
|
+
this.zIndex = 0;
|
|
920
|
+
this.maxIndex = urls.length - 1;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
this.plotService.setZIndex(this.zIndex);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Live z-slice scrub for the Image view: the renderer swaps the displayed
|
|
927
|
+
* slice in place (no re-mount), so dragging the slider steps through the
|
|
928
|
+
* stack the same way the heatmap frame slider does.
|
|
929
|
+
*/
|
|
930
|
+
onZSlide(z) {
|
|
931
|
+
if (z === undefined)
|
|
932
|
+
return;
|
|
933
|
+
this.zIndex = z;
|
|
934
|
+
this.scrubber.commit(z);
|
|
935
|
+
// Keep the intensity inset in sync with the displayed slice (Image/OSD mode,
|
|
936
|
+
// where the profile sampler is fed from the loaded preview frames).
|
|
937
|
+
if (this.hasProfiles && this.isImageView && this.imageInfo) {
|
|
938
|
+
this.plotService.ensureIntensitySampling(this.imageInfo, z);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Live (debounced) scrub while dragging the Image-view z-slider: update the
|
|
943
|
+
* slice as the user drags, but coalesce rapid changes so we don't fire a
|
|
944
|
+
* slice swap on every pixel. The final value also lands via onZSlide (onSlideEnd).
|
|
945
|
+
*/
|
|
946
|
+
onZScrub(z) {
|
|
947
|
+
if (z === undefined)
|
|
948
|
+
return;
|
|
949
|
+
this.zIndex = z;
|
|
950
|
+
this.scrubber.scrub(z);
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Keyboard stack navigation: ←/→ step the z-slice in the Image view, the same
|
|
954
|
+
* way the slider does. Only active for a loaded stack in Image view, and
|
|
955
|
+
* ignored while a form field is focused so typing isn't hijacked. Up/Down are
|
|
956
|
+
* left to OpenSeadragon (panning).
|
|
957
|
+
*/
|
|
958
|
+
onKeydown(e) {
|
|
959
|
+
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight')
|
|
960
|
+
return;
|
|
961
|
+
if (!this.imageInfo?.isStack || !this.isImageView)
|
|
962
|
+
return;
|
|
963
|
+
const t = e.target;
|
|
964
|
+
if (t && (/^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName) || t.isContentEditable))
|
|
965
|
+
return;
|
|
966
|
+
// The slice slider handles arrows natively when focused (also a +1 step) —
|
|
967
|
+
// skip here so we don't double-step it.
|
|
968
|
+
if (t && (t.getAttribute('role') === 'slider' || t.closest('.p-slider')))
|
|
969
|
+
return;
|
|
970
|
+
e.preventDefault();
|
|
971
|
+
this.stepSlice(e.key === 'ArrowRight' ? 1 : -1);
|
|
972
|
+
}
|
|
973
|
+
/** Move the displayed slice by `delta`, clamped to the stack bounds. */
|
|
974
|
+
stepSlice(delta) {
|
|
975
|
+
const next = Math.min(this.maxIndex, Math.max(0, this.zIndex + delta));
|
|
976
|
+
if (next === this.zIndex)
|
|
977
|
+
return;
|
|
978
|
+
this.onZSlide(next);
|
|
979
|
+
}
|
|
980
|
+
reloadAndPlot() {
|
|
981
|
+
this.state.setImageLoading(true);
|
|
982
|
+
// Re-drive the render pipeline from the current image info (the source of
|
|
983
|
+
// truth, with real urls/fileName). Don't use plotService.reloadAndPlot():
|
|
984
|
+
// that rebuilds the info from the renderer's internal state, which can be
|
|
985
|
+
// stale (e.g. after switching plot type Image -> Heatmap) and re-emit
|
|
986
|
+
// empty/old urls, failing to hand the div over to the new renderer.
|
|
987
|
+
if (this.imageInfo) {
|
|
988
|
+
this.state.setImageInfo(this.imageInfo);
|
|
989
|
+
}
|
|
990
|
+
else {
|
|
991
|
+
this.plotService.reloadAndPlot();
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
cancelLoading() {
|
|
995
|
+
// Stop any in-flight frame streaming (napari-js volume/surface preload) so the fetch loops
|
|
996
|
+
// actually abort — clearing the flag alone only routed to Plotly and left napari fetching.
|
|
997
|
+
this.plotService.cancelLoading?.();
|
|
998
|
+
this.stackLoading = false;
|
|
999
|
+
this.imgLoading = false;
|
|
1000
|
+
this.state.setImageLoading(false);
|
|
1001
|
+
this.zIndex = 0;
|
|
1002
|
+
this.plotService.setZIndex(this.zIndex);
|
|
1003
|
+
this.running = false;
|
|
1004
|
+
this.plotService.setStackLoading(false);
|
|
1005
|
+
}
|
|
1006
|
+
downloadImage() {
|
|
1007
|
+
this.plotService.downloadImage();
|
|
1008
|
+
}
|
|
1009
|
+
autoscaleImage() {
|
|
1010
|
+
this.plotService.autoscale();
|
|
1011
|
+
}
|
|
1012
|
+
/** Toolbar pixel/smooth toggle: flip smoothing and apply to the active backend. */
|
|
1013
|
+
onToggleImageSmoothing() {
|
|
1014
|
+
this.imageSmoothingEnabled = !this.imageSmoothingEnabled;
|
|
1015
|
+
this.plotService.setImageSmoothingEnabled(this.imageSmoothingEnabled);
|
|
1016
|
+
}
|
|
1017
|
+
resetAxes() {
|
|
1018
|
+
this.plotService.resetAxes();
|
|
1019
|
+
}
|
|
1020
|
+
toggleDragMode(mode) {
|
|
1021
|
+
// Toggle off if the same mode is re-selected.
|
|
1022
|
+
this.activeDragMode = this.activeDragMode === mode ? null : mode;
|
|
1023
|
+
const active = this.activeDragMode;
|
|
1024
|
+
// Record the armed tool in the shared session store.
|
|
1025
|
+
this.session.setActiveTool(active);
|
|
1026
|
+
// Region draw/select run through the renderer's region overlay.
|
|
1027
|
+
this.plotService
|
|
1028
|
+
.getRegionOverlay()
|
|
1029
|
+
?.setMode(this.isRegionMode(active) ? active : 'none');
|
|
1030
|
+
// Viewport drag modes (pan/box-zoom). Region modes own the drag mode via
|
|
1031
|
+
// the overlay, so don't also set one here.
|
|
1032
|
+
if (!this.isRegionMode(active)) {
|
|
1033
|
+
const viewportDrag = active === 'pan' || active === 'zoom';
|
|
1034
|
+
this.plotService.setDragMode(viewportDrag ? active : false);
|
|
1035
|
+
}
|
|
1036
|
+
// On-canvas tool overlays.
|
|
1037
|
+
this.plotService.setZoomToBoxMode(active === 'zoomToBox');
|
|
1038
|
+
this.plotService.setWandMode(active === 'wand', { sensitivity: this.wandSensitivity });
|
|
1039
|
+
this.plotService.setBrushMode(active === 'brush', { size: this.brushSize });
|
|
1040
|
+
this.plotService.setSamPointMode(active === 'samPoint');
|
|
1041
|
+
// Leaving point mode dismisses any lingering status toast.
|
|
1042
|
+
if (active !== 'samPoint')
|
|
1043
|
+
this.hideSamToast();
|
|
1044
|
+
this.plotService.setVertexEraserMode(active === 'eraseVertex');
|
|
1045
|
+
if (active === 'eraseVertex') {
|
|
1046
|
+
this.plotService.setVertexEraserRadius(this.vertexEraserRadius);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
/** Region draw/select/edit modes routed through the region overlay. The
|
|
1050
|
+
* The vertex tools (drawpolygon/addpoint/deletepoint/move) are
|
|
1051
|
+
* handled by the OpenSeadragon overlay; Plotly's overlay maps them to no-op. */
|
|
1052
|
+
isRegionMode(mode) {
|
|
1053
|
+
return (mode === 'drawrect' ||
|
|
1054
|
+
mode === 'drawclosedpath' ||
|
|
1055
|
+
mode === 'drawopenpath' ||
|
|
1056
|
+
mode === 'select' ||
|
|
1057
|
+
mode === 'drawpolygon' ||
|
|
1058
|
+
mode === 'addpoint' ||
|
|
1059
|
+
mode === 'deletepoint' ||
|
|
1060
|
+
mode === 'move');
|
|
1061
|
+
}
|
|
1062
|
+
/** Live-update the isosurface as the range slider moves. The control is only
|
|
1063
|
+
* available when the active backend renders isosurfaces (ISOSURFACE mode). */
|
|
1064
|
+
onIsoRangeChange(values) {
|
|
1065
|
+
if (!values || values.length < 2)
|
|
1066
|
+
return;
|
|
1067
|
+
this.isoRange = values;
|
|
1068
|
+
this.plotService.getIsosurfaceControls()?.setIsoRange(values[0], values[1]);
|
|
1069
|
+
}
|
|
1070
|
+
onWandSensitivityChange(value) {
|
|
1071
|
+
if (value === undefined || !Number.isFinite(value))
|
|
1072
|
+
return;
|
|
1073
|
+
this.wandSensitivity = value;
|
|
1074
|
+
this.plotService.setWandOptions({ sensitivity: value });
|
|
1075
|
+
}
|
|
1076
|
+
onBrushSizeChange(value) {
|
|
1077
|
+
if (value === undefined || !Number.isFinite(value))
|
|
1078
|
+
return;
|
|
1079
|
+
this.brushSize = value;
|
|
1080
|
+
this.plotService.setBrushOptions({ size: value });
|
|
1081
|
+
}
|
|
1082
|
+
onVertexEraserRadiusChange(value) {
|
|
1083
|
+
if (value === undefined || !Number.isFinite(value))
|
|
1084
|
+
return;
|
|
1085
|
+
this.vertexEraserRadius = value;
|
|
1086
|
+
this.plotService.setVertexEraserRadius(value);
|
|
1087
|
+
}
|
|
1088
|
+
zoomIn() {
|
|
1089
|
+
this.plotService.zoomIn();
|
|
1090
|
+
}
|
|
1091
|
+
zoomOut() {
|
|
1092
|
+
this.plotService.zoomOut();
|
|
1093
|
+
}
|
|
1094
|
+
deleteRegion() {
|
|
1095
|
+
this.plotService.deleteActiveShape();
|
|
1096
|
+
}
|
|
1097
|
+
/** Undo the most recent region action (jit-ui#85). Up to 10 steps back. */
|
|
1098
|
+
undoRegion() {
|
|
1099
|
+
this.plotService.undo();
|
|
1100
|
+
}
|
|
1101
|
+
/** Redo the most recently undone region action (jit-ui#85). */
|
|
1102
|
+
redoRegion() {
|
|
1103
|
+
this.plotService.redo();
|
|
1104
|
+
}
|
|
1105
|
+
// ── Region set-operations on the current selection (jit-ui#85) ──────────
|
|
1106
|
+
/** The currently-selected regions (live store instances). */
|
|
1107
|
+
get selectedRegions() {
|
|
1108
|
+
const all = this.plotService.getRegions();
|
|
1109
|
+
return this.selectedIndices
|
|
1110
|
+
.map((i) => all[i])
|
|
1111
|
+
.filter((r) => !!r);
|
|
1112
|
+
}
|
|
1113
|
+
/** Regions eligible for set-ops: closed areas (rect / closed polygon /
|
|
1114
|
+
* multi-polygon), excluding intensity-profile lines. */
|
|
1115
|
+
opEligible(regions) {
|
|
1116
|
+
return regions.filter((r) => r.kind !== 'profile' && (r.bounds instanceof Rectangle ||
|
|
1117
|
+
r.bounds instanceof MultiPolygon ||
|
|
1118
|
+
(r.bounds instanceof Polygon && r.bounds.closed !== false)));
|
|
1119
|
+
}
|
|
1120
|
+
/** Image pixel dimensions for the raster ops; falls back to the selection's
|
|
1121
|
+
* extent when the image size isn't known (keeps clamping sane). */
|
|
1122
|
+
opImageDims(regions) {
|
|
1123
|
+
let [w, h] = this.imageInfo?.trueImageSize ?? [0, 0];
|
|
1124
|
+
if (!(w > 0) || !(h > 0)) {
|
|
1125
|
+
let maxX = 0, maxY = 0;
|
|
1126
|
+
const scan = (xs, ys) => {
|
|
1127
|
+
for (const x of xs)
|
|
1128
|
+
maxX = Math.max(maxX, x);
|
|
1129
|
+
for (const y of ys)
|
|
1130
|
+
maxY = Math.max(maxY, y);
|
|
1131
|
+
};
|
|
1132
|
+
for (const r of regions) {
|
|
1133
|
+
const b = r.bounds;
|
|
1134
|
+
if (b instanceof Rectangle)
|
|
1135
|
+
scan([b.x + b.width], [b.y + b.height]);
|
|
1136
|
+
else if (b instanceof Polygon)
|
|
1137
|
+
scan(b.xpoints, b.ypoints);
|
|
1138
|
+
else if (b instanceof MultiPolygon)
|
|
1139
|
+
for (const p of b.polygons)
|
|
1140
|
+
scan(p.xpoints, p.ypoints);
|
|
1141
|
+
}
|
|
1142
|
+
w = Math.max(w, Math.ceil(maxX) + 2);
|
|
1143
|
+
h = Math.max(h, Math.ceil(maxY) + 2);
|
|
1144
|
+
}
|
|
1145
|
+
return { w, h };
|
|
1146
|
+
}
|
|
1147
|
+
/** True when ≥2 eligible regions are selected (Merge available). */
|
|
1148
|
+
get canMergeRegions() { return this.opEligible(this.selectedRegions).length >= 2; }
|
|
1149
|
+
/** True when any selected region is multi-part (Ungroup available). */
|
|
1150
|
+
get canUngroupRegions() { return this.selectedRegions.some((r) => this.regionOps.canUngroup(r)); }
|
|
1151
|
+
/** True when ≥1 eligible region is selected (Inverse / Simplify available). */
|
|
1152
|
+
get hasEligibleSelection() { return this.opEligible(this.selectedRegions).length >= 1; }
|
|
1153
|
+
/** Select every region on the image (excludes intensity-profile lines). */
|
|
1154
|
+
selectAllRegions() {
|
|
1155
|
+
const all = this.plotService.getRegions();
|
|
1156
|
+
const indices = [];
|
|
1157
|
+
all.forEach((r, i) => { if (r.kind !== 'profile')
|
|
1158
|
+
indices.push(i); });
|
|
1159
|
+
this.plotService.setSelectedShapeIndices(indices);
|
|
1160
|
+
}
|
|
1161
|
+
/** Merge the selected regions into a single (possibly multi-part) region. */
|
|
1162
|
+
mergeRegions() {
|
|
1163
|
+
const sel = this.opEligible(this.selectedRegions);
|
|
1164
|
+
if (sel.length < 2)
|
|
1165
|
+
return;
|
|
1166
|
+
const { w, h } = this.opImageDims(sel);
|
|
1167
|
+
const merged = this.regionOps.merge(sel, w, h);
|
|
1168
|
+
if (merged)
|
|
1169
|
+
this.replaceRegions(sel, [merged]);
|
|
1170
|
+
}
|
|
1171
|
+
/** Split each selected multi-part region into one region per part. */
|
|
1172
|
+
ungroupRegions() {
|
|
1173
|
+
const sel = this.selectedRegions.filter((r) => this.regionOps.canUngroup(r));
|
|
1174
|
+
if (sel.length === 0)
|
|
1175
|
+
return;
|
|
1176
|
+
const results = [];
|
|
1177
|
+
for (const r of sel)
|
|
1178
|
+
results.push(...this.regionOps.ungroup(r));
|
|
1179
|
+
this.replaceRegions(sel, results);
|
|
1180
|
+
}
|
|
1181
|
+
/** Replace the selection with its inverse inside the image rectangle. */
|
|
1182
|
+
inverseRegions() {
|
|
1183
|
+
const sel = this.opEligible(this.selectedRegions);
|
|
1184
|
+
if (sel.length === 0)
|
|
1185
|
+
return;
|
|
1186
|
+
const { w, h } = this.opImageDims(sel);
|
|
1187
|
+
const inv = this.regionOps.inverse(sel, w, h);
|
|
1188
|
+
if (!inv) {
|
|
1189
|
+
this.messageService.add({
|
|
1190
|
+
severity: 'warn', summary: 'Inverse',
|
|
1191
|
+
detail: 'Nothing to invert — select one or more closed regions first.',
|
|
1192
|
+
});
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
this.replaceRegions(sel, [inv]);
|
|
1196
|
+
}
|
|
1197
|
+
/** Douglas–Peucker simplify each selected region by `thresholdPx`. */
|
|
1198
|
+
simplifyRegions(thresholdPx) {
|
|
1199
|
+
const sel = this.opEligible(this.selectedRegions);
|
|
1200
|
+
if (sel.length === 0)
|
|
1201
|
+
return;
|
|
1202
|
+
this.replaceRegions(sel, sel.map((r) => this.regionOps.simplify(r, thresholdPx)));
|
|
1203
|
+
this.displaySimplifyDialog = false;
|
|
1204
|
+
}
|
|
1205
|
+
/** Open the custom-threshold simplify dialog. */
|
|
1206
|
+
openSimplifyDialog() { this.displaySimplifyDialog = true; }
|
|
1207
|
+
/**
|
|
1208
|
+
* Commit a set-op: drop the `remove` regions, append `add`, and select the
|
|
1209
|
+
* results. Goes through setRegions so it's undo-tracked and re-rendered;
|
|
1210
|
+
* profile lines and unselected regions are preserved.
|
|
1211
|
+
*/
|
|
1212
|
+
replaceRegions(remove, add) {
|
|
1213
|
+
if (add.length === 0)
|
|
1214
|
+
return;
|
|
1215
|
+
const removeSet = new Set(remove);
|
|
1216
|
+
const kept = this.plotService.getRegions().filter((r) => !removeSet.has(r));
|
|
1217
|
+
this.plotService.setRegions([...kept, ...add]); // mints ids on `add`, records undo
|
|
1218
|
+
const stored = this.plotService.getRegions();
|
|
1219
|
+
const sel = add.map((r) => stored.indexOf(r)).filter((i) => i >= 0);
|
|
1220
|
+
this.plotService.setSelectedShapeIndices(sel);
|
|
1221
|
+
}
|
|
1222
|
+
/** Box-prompted SAM segmentation of the drawn rectangles (jit-ui#90). A sticky
|
|
1223
|
+
* `sam` toast shows live status + a download progress bar (first run pulls the
|
|
1224
|
+
* encoder, ~170 MB); it stays open until the run finishes (bar hits 100%). */
|
|
1225
|
+
async segmentRegions() {
|
|
1226
|
+
await this.runSegmentWithToast('SAM', this.samTool, () => this.plotService.segmentRectangles());
|
|
1227
|
+
}
|
|
1228
|
+
/** Auto-segment cells inside each drawn rectangle with cellpose-SAM, client-side
|
|
1229
|
+
* (jit-ui#90). Each box is cropped (browser slide-crop) then run through the
|
|
1230
|
+
* cellpose-js model; the same sticky `sam` toast + progress bar is reused. */
|
|
1231
|
+
async segmentCellpose() {
|
|
1232
|
+
await this.runSegmentWithToast('Cellpose', this.cellSegmentTool, () => this.plotService.segmentRectanglesCellpose());
|
|
1233
|
+
}
|
|
1234
|
+
/** Shared driver for the box-prompt segment tools: wires the tool's status +
|
|
1235
|
+
* download progress into the sticky `sam` toast, runs `op`, and reports the
|
|
1236
|
+
* region count. Keeps the toast open until the run settles (bar hits 100%). */
|
|
1237
|
+
async runSegmentWithToast(label, tool, op) {
|
|
1238
|
+
this.samStatus = 'Starting…';
|
|
1239
|
+
this.samProgress = 0;
|
|
1240
|
+
this.samDownloading = false;
|
|
1241
|
+
this.samBusy = true;
|
|
1242
|
+
const psub = tool.progress$.subscribe((f) => {
|
|
1243
|
+
this.samDownloading = f >= 0 && f < 1;
|
|
1244
|
+
if (f >= 0)
|
|
1245
|
+
this.samProgress = Math.min(100, Math.round(f * 100));
|
|
1246
|
+
this.cdr.detectChanges();
|
|
1247
|
+
});
|
|
1248
|
+
const ssub = tool.status$.subscribe((m) => {
|
|
1249
|
+
if (m) {
|
|
1250
|
+
this.samStatus = m;
|
|
1251
|
+
this.cdr.detectChanges();
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
this.showSamToast(label);
|
|
1255
|
+
try {
|
|
1256
|
+
const n = await op();
|
|
1257
|
+
this.messageService.add({
|
|
1258
|
+
severity: n > 0 ? 'success' : 'warn',
|
|
1259
|
+
summary: label,
|
|
1260
|
+
detail: tool.status$.value || (n > 0 ? `Added ${n} region(s).` : 'No regions added.'),
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
catch (e) {
|
|
1264
|
+
this.messageService.add({ severity: 'error', summary: `${label} failed`, detail: String(e) });
|
|
1265
|
+
}
|
|
1266
|
+
finally {
|
|
1267
|
+
psub.unsubscribe();
|
|
1268
|
+
ssub.unsubscribe();
|
|
1269
|
+
this.hideSamToast();
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
/** Show the shared sticky `sam` toast once (idempotent — re-adding would stack
|
|
1273
|
+
* a new toast on every point click). */
|
|
1274
|
+
showSamToast(summary) {
|
|
1275
|
+
if (this.samToastShown)
|
|
1276
|
+
return;
|
|
1277
|
+
this.samToastShown = true;
|
|
1278
|
+
this.messageService.add({ key: this.samToastKey, sticky: true, severity: 'info', summary });
|
|
1279
|
+
}
|
|
1280
|
+
/** Dismiss the shared `sam` toast and reset its progress/spinner state. */
|
|
1281
|
+
hideSamToast() {
|
|
1282
|
+
this.samToastShown = false;
|
|
1283
|
+
this.samBusy = false;
|
|
1284
|
+
this.samDownloading = false;
|
|
1285
|
+
this.samProgress = 0;
|
|
1286
|
+
this.messageService.clear(this.samToastKey);
|
|
1287
|
+
this.cdr.detectChanges();
|
|
1288
|
+
}
|
|
1289
|
+
/** Pick the SAM model the segment tools use (jit-ui#90 P1). */
|
|
1290
|
+
onSamModelChange(id) {
|
|
1291
|
+
this.samModelId = id;
|
|
1292
|
+
this.plotService.setSamModel(id);
|
|
1293
|
+
}
|
|
1294
|
+
/** Convert the selected region to a smooth bezier curve. */
|
|
1295
|
+
toBezierRegion() {
|
|
1296
|
+
this.plotService.getRegionOverlay()?.setSelectedBezier(true);
|
|
1297
|
+
}
|
|
1298
|
+
/** Convert the selected region back to a straight-edged polygon. */
|
|
1299
|
+
toPolygonRegion() {
|
|
1300
|
+
this.plotService.getRegionOverlay()?.setSelectedBezier(false);
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Immediate region actions on the current selection (jit-ui#85): merge,
|
|
1304
|
+
* ungroup, inverse, simplify, the Bézier conversions, and delete — the
|
|
1305
|
+
* one-shot geometry transforms, grouped apart from the tool/mode toggles and
|
|
1306
|
+
* shown only when something is selected. Each item is gated on what the
|
|
1307
|
+
* selection supports.
|
|
1308
|
+
*/
|
|
1309
|
+
buildRegionActionItems() {
|
|
1310
|
+
const all = this.plotService.getRegions();
|
|
1311
|
+
const selectable = all.filter((r) => r.kind !== 'profile');
|
|
1312
|
+
if (selectable.length === 0)
|
|
1313
|
+
return [];
|
|
1314
|
+
const items = [];
|
|
1315
|
+
// Select all — available whenever the image has regions, regardless of the
|
|
1316
|
+
// current selection.
|
|
1317
|
+
items.push({
|
|
1318
|
+
label: 'Select all regions', icon: 'pi pi-check-square',
|
|
1319
|
+
command: () => this.selectAllRegions(),
|
|
1320
|
+
});
|
|
1321
|
+
const selAll = this.selectedRegions;
|
|
1322
|
+
if (selAll.length === 0)
|
|
1323
|
+
return items;
|
|
1324
|
+
const eligible = this.opEligible(selAll);
|
|
1325
|
+
if (eligible.length >= 2) {
|
|
1326
|
+
items.push({ label: 'Merge / group', icon: 'pi pi-link', command: () => this.mergeRegions() });
|
|
1327
|
+
}
|
|
1328
|
+
if (selAll.some((r) => this.regionOps.canUngroup(r))) {
|
|
1329
|
+
items.push({ label: 'Ungroup', icon: 'pi pi-sitemap', command: () => this.ungroupRegions() });
|
|
1330
|
+
}
|
|
1331
|
+
if (eligible.length >= 1) {
|
|
1332
|
+
items.push({ label: 'Inverse', icon: 'pi pi-clone', command: () => this.inverseRegions() }, {
|
|
1333
|
+
label: 'Simplify', icon: 'pi pi-chart-line',
|
|
1334
|
+
items: [
|
|
1335
|
+
{ label: 'Light (1 px)', command: () => this.simplifyRegions(1) },
|
|
1336
|
+
{ label: 'Medium (3 px)', command: () => this.simplifyRegions(3) },
|
|
1337
|
+
{ label: 'Strong (8 px)', command: () => this.simplifyRegions(8) },
|
|
1338
|
+
{ separator: true },
|
|
1339
|
+
{ label: 'Custom…', command: () => this.openSimplifyDialog() },
|
|
1340
|
+
],
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
// Bézier conversions are vertex-level edits — Image (OpenSeadragon) view
|
|
1344
|
+
// only — gated on the selection's current form.
|
|
1345
|
+
if (this.selectedPlotType === PlotType.IMAGE) {
|
|
1346
|
+
const hasStraight = selAll.some((r) => (r.bounds instanceof Polygon && !r.bounds.bezier) || r.bounds instanceof Rectangle);
|
|
1347
|
+
const hasBezier = selAll.some((r) => r.bounds instanceof Polygon && r.bounds.bezier);
|
|
1348
|
+
if (hasStraight) {
|
|
1349
|
+
items.push({ label: 'Convert to Bézier', icon: 'to-bezier-icon', command: () => this.toBezierRegion() });
|
|
1350
|
+
}
|
|
1351
|
+
if (hasBezier) {
|
|
1352
|
+
items.push({ label: 'Convert to polygon', icon: 'to-polygon-icon', command: () => this.toPolygonRegion() });
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
items.push({ label: 'Delete region', icon: 'pi pi-trash', command: () => this.deleteRegion() });
|
|
1356
|
+
return items;
|
|
1357
|
+
}
|
|
1358
|
+
buildContextMenuItems() {
|
|
1359
|
+
const active = this.activeDragMode;
|
|
1360
|
+
const activeClass = 'context-menu-active';
|
|
1361
|
+
const items = [];
|
|
1362
|
+
// Selected-region actions lead the menu (separated from the tool toggles).
|
|
1363
|
+
if (this.isHeatmap) {
|
|
1364
|
+
const actions = this.buildRegionActionItems();
|
|
1365
|
+
if (actions.length)
|
|
1366
|
+
items.push(...actions, { separator: true });
|
|
1367
|
+
}
|
|
1368
|
+
if (this.isHeatmap) {
|
|
1369
|
+
items.push({ label: 'Autoscale', icon: 'pi pi-window-maximize', command: () => this.autoscaleImage() }, { separator: true });
|
|
1370
|
+
// 'Zoom selection' is Plotly's rubber-band zoom; it doesn't apply to the
|
|
1371
|
+
// OpenSeadragon-backed Image view (use 'Zoom to box' there instead).
|
|
1372
|
+
if (!this.isImageView) {
|
|
1373
|
+
items.push({
|
|
1374
|
+
label: 'Zoom selection',
|
|
1375
|
+
icon: 'pi pi-search',
|
|
1376
|
+
styleClass: active === 'zoom' ? activeClass : '',
|
|
1377
|
+
command: () => this.toggleDragMode('zoom'),
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
items.push({
|
|
1381
|
+
label: 'Zoom to box',
|
|
1382
|
+
icon: 'zoom-box-off-icon',
|
|
1383
|
+
styleClass: active === 'zoomToBox' ? activeClass : '',
|
|
1384
|
+
command: () => this.toggleDragMode('zoomToBox'),
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
if (this.isHeatmap) {
|
|
1388
|
+
items.push({
|
|
1389
|
+
label: 'Pan',
|
|
1390
|
+
icon: 'pi pi-arrows-alt',
|
|
1391
|
+
styleClass: active === 'pan' ? activeClass : '',
|
|
1392
|
+
command: () => this.toggleDragMode('pan'),
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
else {
|
|
1396
|
+
const s3d = this.activeSurface3dMode;
|
|
1397
|
+
items.push({
|
|
1398
|
+
label: 'Zoom',
|
|
1399
|
+
icon: 'pi pi-search',
|
|
1400
|
+
styleClass: s3d === 'zoom' ? activeClass : '',
|
|
1401
|
+
command: () => this.toggleSurface3dMode('zoom'),
|
|
1402
|
+
}, {
|
|
1403
|
+
label: 'Pan',
|
|
1404
|
+
icon: 'pi pi-arrows-alt',
|
|
1405
|
+
styleClass: s3d === 'pan' ? activeClass : '',
|
|
1406
|
+
command: () => this.toggleSurface3dMode('pan'),
|
|
1407
|
+
}, {
|
|
1408
|
+
label: 'Orbital rotation',
|
|
1409
|
+
icon: 'pi pi-globe',
|
|
1410
|
+
styleClass: s3d === 'orbit' ? activeClass : '',
|
|
1411
|
+
command: () => this.toggleSurface3dMode('orbit'),
|
|
1412
|
+
}, {
|
|
1413
|
+
label: 'Turntable rotation',
|
|
1414
|
+
icon: 'pi pi-sync',
|
|
1415
|
+
styleClass: s3d === 'turntable' ? activeClass : '',
|
|
1416
|
+
command: () => this.toggleSurface3dMode('turntable'),
|
|
1417
|
+
}, { separator: true }, { label: 'Reset camera', icon: 'pi pi-home', command: () => this.resetSurfaceCamera() });
|
|
1418
|
+
}
|
|
1419
|
+
if (this.isHeatmap) {
|
|
1420
|
+
items.push({ label: 'Zoom in', icon: 'pi pi-search-plus', command: () => this.zoomIn() }, { label: 'Zoom out', icon: 'pi pi-search-minus', command: () => this.zoomOut() }, { separator: true }, {
|
|
1421
|
+
label: 'Select',
|
|
1422
|
+
icon: 'pi pi-arrow-up-right',
|
|
1423
|
+
styleClass: active === 'select' ? activeClass : '',
|
|
1424
|
+
command: () => this.toggleDragMode('select'),
|
|
1425
|
+
}, {
|
|
1426
|
+
label: 'Freeform',
|
|
1427
|
+
icon: 'pi pi-pencil',
|
|
1428
|
+
styleClass: active === 'drawclosedpath' ? activeClass : '',
|
|
1429
|
+
command: () => this.toggleDragMode('drawclosedpath'),
|
|
1430
|
+
}, {
|
|
1431
|
+
label: 'Brush',
|
|
1432
|
+
icon: 'brush-icon',
|
|
1433
|
+
styleClass: active === 'brush' ? activeClass : '',
|
|
1434
|
+
command: () => this.toggleDragMode('brush'),
|
|
1435
|
+
}, {
|
|
1436
|
+
label: 'Polyline',
|
|
1437
|
+
icon: 'polyline-icon',
|
|
1438
|
+
styleClass: active === 'drawopenpath' ? activeClass : '',
|
|
1439
|
+
command: () => this.toggleDragMode('drawopenpath'),
|
|
1440
|
+
}, {
|
|
1441
|
+
label: 'Rectangle',
|
|
1442
|
+
icon: 'pi pi-stop',
|
|
1443
|
+
styleClass: active === 'drawrect' ? activeClass : '',
|
|
1444
|
+
command: () => this.toggleDragMode('drawrect'),
|
|
1445
|
+
}, {
|
|
1446
|
+
label: 'Wand',
|
|
1447
|
+
icon: 'wand-icon',
|
|
1448
|
+
styleClass: active === 'wand' ? activeClass : '',
|
|
1449
|
+
command: () => this.toggleDragMode('wand'),
|
|
1450
|
+
}, {
|
|
1451
|
+
label: 'Vertex eraser',
|
|
1452
|
+
icon: 'pi pi-eraser',
|
|
1453
|
+
styleClass: active === 'eraseVertex' ? activeClass : '',
|
|
1454
|
+
command: () => this.toggleDragMode('eraseVertex'),
|
|
1455
|
+
});
|
|
1456
|
+
// Vertex editing runs on the OpenSeadragon overlay, which
|
|
1457
|
+
// backs the Image plot type. Hidden for other 2D types (Plotly), where
|
|
1458
|
+
// these modes are no-ops.
|
|
1459
|
+
if (this.selectedPlotType === PlotType.IMAGE) {
|
|
1460
|
+
items.push({
|
|
1461
|
+
label: 'Polygon (click vertices)',
|
|
1462
|
+
icon: 'polygon-vertices-icon',
|
|
1463
|
+
styleClass: active === 'drawpolygon' ? activeClass : '',
|
|
1464
|
+
command: () => this.toggleDragMode('drawpolygon'),
|
|
1465
|
+
}, {
|
|
1466
|
+
label: 'Add vertex',
|
|
1467
|
+
icon: 'vertex-add-icon',
|
|
1468
|
+
styleClass: active === 'addpoint' ? activeClass : '',
|
|
1469
|
+
command: () => this.toggleDragMode('addpoint'),
|
|
1470
|
+
}, {
|
|
1471
|
+
label: 'Delete vertex',
|
|
1472
|
+
icon: 'vertex-delete-icon',
|
|
1473
|
+
styleClass: active === 'deletepoint' ? activeClass : '',
|
|
1474
|
+
command: () => this.toggleDragMode('deletepoint'),
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
// Bézier conversions + Delete are immediate selection *actions*, now in the
|
|
1478
|
+
// "Selected region(s)" group at the top (see buildRegionActionItems).
|
|
1479
|
+
}
|
|
1480
|
+
return items;
|
|
1481
|
+
}
|
|
1482
|
+
toggleSurface3dMode(mode) {
|
|
1483
|
+
this.activeSurface3dMode = mode;
|
|
1484
|
+
this.plotService.setSurfaceDragMode(mode);
|
|
1485
|
+
}
|
|
1486
|
+
resetSurfaceCamera() {
|
|
1487
|
+
this.plotService.resetSurfaceCamera();
|
|
1488
|
+
}
|
|
1489
|
+
/** Toggle the napari 3D coordinate-axes / scale gizmo (volume/isosurface). No-op on backends
|
|
1490
|
+
* that don't render one (the control method is optional). */
|
|
1491
|
+
toggleAxes() {
|
|
1492
|
+
this.axesVisible = !this.axesVisible;
|
|
1493
|
+
this.plotService.getSurface3dControls()?.setAxesVisible?.(this.axesVisible);
|
|
1494
|
+
}
|
|
1495
|
+
/** Toggle the napari surface wireframe (edges vs filled). No-op on backends without a surface. */
|
|
1496
|
+
toggleWireframe() {
|
|
1497
|
+
this.wireframeActive = !this.wireframeActive;
|
|
1498
|
+
this.plotService.getSurface3dControls()?.setWireframe?.(this.wireframeActive);
|
|
1499
|
+
}
|
|
1500
|
+
/** Change the napari 3D decimate factor. Decimation changes the fetched/assembled data, so this
|
|
1501
|
+
* re-plots the current 3D type at the new resolution (unlike axes/wireframe, which are live). */
|
|
1502
|
+
selectResolution(scale) {
|
|
1503
|
+
if (scale === this.resolutionScale)
|
|
1504
|
+
return;
|
|
1505
|
+
this.resolutionScale = scale;
|
|
1506
|
+
this.plotService.setResolutionScale?.(scale);
|
|
1507
|
+
this.reloadAndPlot();
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Switch the active plot type (heatmap, surface, contour, scatter, line,
|
|
1511
|
+
* scatter3d, isosurface). Keeps `isHeatmap` as the 2D-vs-3D flag the rest of
|
|
1512
|
+
* the toolbar/context-menu logic relies on, deactivates 2D-only tools when
|
|
1513
|
+
* moving to a 3D type, then re-plots.
|
|
1514
|
+
*/
|
|
1515
|
+
onSelectPlotType(type) {
|
|
1516
|
+
this.plotType = type;
|
|
1517
|
+
this.selectedPlotType = type;
|
|
1518
|
+
const descriptor = this.plotTypeOptions.find((d) => d.type === type);
|
|
1519
|
+
const is3d = descriptor?.dimensions === '3d';
|
|
1520
|
+
this.isHeatmap = !is3d;
|
|
1521
|
+
this.plotService.setPlotType(type);
|
|
1522
|
+
// Switching plot type cancels the active tool — not every type supports the
|
|
1523
|
+
// same tools (line/scatter charts and 3D scenes have no box zoom or region
|
|
1524
|
+
// drawing), so leaving a tool armed would misbehave on the new plot.
|
|
1525
|
+
this.deactivateActiveTool();
|
|
1526
|
+
if (is3d) {
|
|
1527
|
+
this.activeSurface3dMode = 'turntable';
|
|
1528
|
+
}
|
|
1529
|
+
// napari-js volume/isosurface assemble their own 3D texture from the slice endpoint, so
|
|
1530
|
+
// they take the normal re-plot path — NOT Plotly's stack-frame loader (whose "Loading
|
|
1531
|
+
// frames" overlay would never clear, since Plotly isn't the active backend) (jit-ui#102).
|
|
1532
|
+
const isNapari = type === PlotType.NAPARI_IMAGE || isNapari3d(type);
|
|
1533
|
+
if (descriptor?.requiresStack && !isNapari) {
|
|
1534
|
+
// Volume types (isosurface, scatter3d) need the whole z-stack loaded as a
|
|
1535
|
+
// 3D array, and they always render on Plotly. Drive the stack reload
|
|
1536
|
+
// directly through the image-info stream rather than the active renderer's
|
|
1537
|
+
// setShowStack: when OpenSeadragon owns the Image view its setShowStack is
|
|
1538
|
+
// a no-op, so the reload never fired and the loader spun forever. We arm
|
|
1539
|
+
// Plotly's stack-loading flag (keeps its frame-fetch loop alive) and
|
|
1540
|
+
// re-emit the image info with showStack on so the pipeline reloads on
|
|
1541
|
+
// Plotly regardless of which backend was on screen.
|
|
1542
|
+
this.selectedStackOption = this.stackOptions[1];
|
|
1543
|
+
this.stackLoading = true;
|
|
1544
|
+
this.plotService.setStackLoading(true);
|
|
1545
|
+
if (this.imageInfo) {
|
|
1546
|
+
this.imageInfo.showStack = true;
|
|
1547
|
+
this.state.setImageInfo(this.imageInfo);
|
|
1548
|
+
}
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
this.reloadAndPlot();
|
|
1552
|
+
}
|
|
1553
|
+
/** Deactivate whatever tool is armed and clear every tool mode. */
|
|
1554
|
+
deactivateActiveTool() {
|
|
1555
|
+
this.activeDragMode = null;
|
|
1556
|
+
this.session.setActiveTool(null);
|
|
1557
|
+
this.plotService.getRegionOverlay()?.setMode('none');
|
|
1558
|
+
this.plotService.setDragMode(false);
|
|
1559
|
+
this.plotService.setZoomToBoxMode(false);
|
|
1560
|
+
this.plotService.setWandMode(false);
|
|
1561
|
+
this.plotService.setBrushMode(false);
|
|
1562
|
+
this.plotService.setSamPointMode(false);
|
|
1563
|
+
this.plotService.setVertexEraserMode(false);
|
|
1564
|
+
}
|
|
1565
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: VisualizerComponent, deps: [{ token: IMAGE_STATE_PORT }, { token: VISUALIZER }, { token: i1.MessageService }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i2.VisualizerStore }, { token: i3.SamToolService }, { token: i4.CellSegmentToolService }, { token: i5.SamPointToolService }, { token: i6.RegionOpsService }, { token: VIZ_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1566
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: VisualizerComponent, selector: "visualizer", inputs: { toolbarTools: "toolbarTools", testMode: "testMode" }, outputs: { isStackEvent: "isStackEvent", isGrayscaleEvent: "isGrayscaleEvent" }, host: { listeners: { "window:keydown": "onKeydown($event)" } }, viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["cm"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ samStatus || message.detail }}</div>\n <p-progressBar *ngIf=\"samDownloading\" [value]=\"samProgress\" styleClass=\"mt-2 sam-progress\"></p-progressBar>\n <p-progressBar *ngIf=\"samBusy && !samDownloading\" mode=\"indeterminate\" styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div class=\"toolbar-dock\" [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\">\n <div class=\"toolbar-drag-handle\" (mousedown)=\"onToolbarDragStart($event)\"\n pTooltip=\"Drag to float the toolbar\" tooltipPosition=\"bottom\">\n <i class=\"pi pi-bars\"></i>\n </div>\n<plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [plotTypeOptions]=\"plotTypeOptions\"\n [selectedPlotType]=\"selectedPlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"canUndoRegion\"\n [canRedo]=\"canRedoRegion\"\n [stackOptions]=\"stackOptions\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\">\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n</plotting-toolbar>\n <p-button *ngIf=\"toolbarFloating\" class=\"toolbar-dock-btn\" icon=\"pi pi-thumbtack\" size=\"small\" [text]=\"true\"\n pTooltip=\"Dock to top\" tooltipPosition=\"bottom\" (onClick)=\"dockToolbar()\">\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading || stackLoading || isCaching\" id=\"overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading; else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching; else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress\"></p-progressBar>\n <span class=\"text-loading\">{{loadingMessage}}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading && !zoom\" class=\"text-loading\">{{loadingMessage}}</span>\n <span *ngIf=\"imgLoading && zoom\" class=\"zoom-text-loading\">{{loadingMessage}}</span>\n </ng-template>\n </ng-template>\n <p-button *ngIf=\"stackLoading || imgLoading\" class=\"cancel-loading \" label=\"Cancel\"\n (onClick)=\"cancelLoading()\">\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"sharpening && !imgLoading && !stackLoading && !isCaching\" id=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n\n</div>\n\n<!-- Floating, draggable intensity-profile inset. Shown in LINE mode; the chart\n updates live as the line ROI on the image is moved. Kept OUTSIDE #plot and\n positioned fixed so it floats over the whole page (not clipped to the\n diagram/canvas area). -->\n<div *ngIf=\"hasProfiles\" class=\"intensity-panel\"\n [style.left.px]=\"profilePanelPos.x\" [style.top.px]=\"profilePanelPos.y\">\n <div class=\"intensity-panel-header\" (mousedown)=\"onProfilePanelDragStart($event)\">\n <span>Intensity profile</span>\n <i class=\"pi pi-arrows-alt\"></i>\n </div>\n <div [id]=\"intensityInsetDiv\" class=\"intensity-panel-plot\"></div>\n</div>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog header=\"Region Editor\" [(visible)]=\"showRegionEditor\"\n [modal]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n [maximizable]=\"true\" appendTo=\"body\" position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog header=\"Simplify region(s)\" [(visible)]=\"displaySimplifyDialog\" [modal]=\"true\"\n [style]=\"{width: '22rem'}\" [draggable]=\"false\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-2\">\n <span>Altitude threshold: <b>{{ simplifyThreshold }} px</b></span>\n <p-slider [(ngModel)]=\"simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"simplifyRegions(simplifyThreshold)\"></p-button>\n </ng-template>\n</p-dialog>\n\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}#overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}#sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}#sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}#sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.intensity-panel{position:fixed;z-index:1100;width:280px;height:180px;background:#191919e6;border:1px solid var(--primary-color, #3399ff);border-radius:6px;box-shadow:0 4px 14px #00000073;display:flex;flex-direction:column;overflow:hidden}.intensity-panel-header{display:flex;align-items:center;justify-content:space-between;padding:.25rem .5rem;cursor:move;-webkit-user-select:none;user-select:none;background:#ffffff14;color:#eee;font-size:.75rem;font-weight:600}.intensity-panel-header i{opacity:.6;font-size:.7rem}.intensity-panel-plot{flex:1 1 auto;width:100%}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"], dependencies: [{ kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i9.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: "directive", type: i10.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: i11.Slider, selector: "p-slider", inputs: ["animate", "disabled", "min", "max", "orientation", "step", "range", "style", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "component", type: i12.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: "component", type: i13.ContextMenu, selector: "p-contextMenu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "appendTo", "autoZIndex", "baseZIndex", "id", "ariaLabel", "ariaLabelledBy", "pressDelay"], outputs: ["onShow", "onHide"] }, { kind: "component", type: i14.ProgressBar, selector: "p-progressBar", inputs: ["value", "showValue", "styleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: i15.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "style", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "component", type: i16.ToolbarComponent, selector: "plotting-toolbar", inputs: ["imageInfo", "plotTypeOptions", "selectedPlotType", "isoRange", "maxIndex", "zIndex", "isHeatmap", "activeDragMode", "activeSurface3dMode", "axesVisible", "wireframeActive", "resolutionScale", "wandSensitivity", "brushSize", "vertexEraserRadius", "canUndo", "canRedo", "stackOptions", "tools", "imageSmoothingEnabled", "samModels", "samModelId"], outputs: ["selectPlotType", "addProfileLine", "toggleImageSmoothing", "isoRangeChange", "openChannelHistogram", "openRegionEditor", "selectStackOption", "zScrub", "zSlide", "zIndexInput", "reloadAndPlot", "downloadImage", "autoscaleImage", "toggleDragMode", "zoomIn", "zoomOut", "toggleSurface3dMode", "resetSurfaceCamera", "toggleAxes", "toggleWireframe", "selectResolution", "deleteRegion", "undoRegion", "redoRegion", "segmentRegions", "segmentCellpose", "samModelChange", "wandSensitivityChange", "brushSizeChange", "vertexEraserRadiusChange"] }, { kind: "component", type: i17.RegionEditorComponent, selector: "region-editor" }, { kind: "component", type: i18.ChannelHistogramComponent, selector: "channel-histogram", inputs: ["visible"], outputs: ["visibleChange"] }] }); }
|
|
1567
|
+
}
|
|
1568
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: VisualizerComponent, decorators: [{
|
|
1569
|
+
type: Component,
|
|
1570
|
+
args: [{ selector: 'visualizer', template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ samStatus || message.detail }}</div>\n <p-progressBar *ngIf=\"samDownloading\" [value]=\"samProgress\" styleClass=\"mt-2 sam-progress\"></p-progressBar>\n <p-progressBar *ngIf=\"samBusy && !samDownloading\" mode=\"indeterminate\" styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div class=\"toolbar-dock\" [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\">\n <div class=\"toolbar-drag-handle\" (mousedown)=\"onToolbarDragStart($event)\"\n pTooltip=\"Drag to float the toolbar\" tooltipPosition=\"bottom\">\n <i class=\"pi pi-bars\"></i>\n </div>\n<plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [plotTypeOptions]=\"plotTypeOptions\"\n [selectedPlotType]=\"selectedPlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"canUndoRegion\"\n [canRedo]=\"canRedoRegion\"\n [stackOptions]=\"stackOptions\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\">\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n</plotting-toolbar>\n <p-button *ngIf=\"toolbarFloating\" class=\"toolbar-dock-btn\" icon=\"pi pi-thumbtack\" size=\"small\" [text]=\"true\"\n pTooltip=\"Dock to top\" tooltipPosition=\"bottom\" (onClick)=\"dockToolbar()\">\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading || stackLoading || isCaching\" id=\"overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading; else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching; else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress\"></p-progressBar>\n <span class=\"text-loading\">{{loadingMessage}}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading && !zoom\" class=\"text-loading\">{{loadingMessage}}</span>\n <span *ngIf=\"imgLoading && zoom\" class=\"zoom-text-loading\">{{loadingMessage}}</span>\n </ng-template>\n </ng-template>\n <p-button *ngIf=\"stackLoading || imgLoading\" class=\"cancel-loading \" label=\"Cancel\"\n (onClick)=\"cancelLoading()\">\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"sharpening && !imgLoading && !stackLoading && !isCaching\" id=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n\n</div>\n\n<!-- Floating, draggable intensity-profile inset. Shown in LINE mode; the chart\n updates live as the line ROI on the image is moved. Kept OUTSIDE #plot and\n positioned fixed so it floats over the whole page (not clipped to the\n diagram/canvas area). -->\n<div *ngIf=\"hasProfiles\" class=\"intensity-panel\"\n [style.left.px]=\"profilePanelPos.x\" [style.top.px]=\"profilePanelPos.y\">\n <div class=\"intensity-panel-header\" (mousedown)=\"onProfilePanelDragStart($event)\">\n <span>Intensity profile</span>\n <i class=\"pi pi-arrows-alt\"></i>\n </div>\n <div [id]=\"intensityInsetDiv\" class=\"intensity-panel-plot\"></div>\n</div>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog header=\"Region Editor\" [(visible)]=\"showRegionEditor\"\n [modal]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n [maximizable]=\"true\" appendTo=\"body\" position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog header=\"Simplify region(s)\" [(visible)]=\"displaySimplifyDialog\" [modal]=\"true\"\n [style]=\"{width: '22rem'}\" [draggable]=\"false\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-2\">\n <span>Altitude threshold: <b>{{ simplifyThreshold }} px</b></span>\n <p-slider [(ngModel)]=\"simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"simplifyRegions(simplifyThreshold)\"></p-button>\n </ng-template>\n</p-dialog>\n\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}#overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}#sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}#sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}#sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.intensity-panel{position:fixed;z-index:1100;width:280px;height:180px;background:#191919e6;border:1px solid var(--primary-color, #3399ff);border-radius:6px;box-shadow:0 4px 14px #00000073;display:flex;flex-direction:column;overflow:hidden}.intensity-panel-header{display:flex;align-items:center;justify-content:space-between;padding:.25rem .5rem;cursor:move;-webkit-user-select:none;user-select:none;background:#ffffff14;color:#eee;font-size:.75rem;font-weight:600}.intensity-panel-header i{opacity:.6;font-size:.7rem}.intensity-panel-plot{flex:1 1 auto;width:100%}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"] }]
|
|
1571
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1572
|
+
type: Inject,
|
|
1573
|
+
args: [IMAGE_STATE_PORT]
|
|
1574
|
+
}] }, { type: undefined, decorators: [{
|
|
1575
|
+
type: Inject,
|
|
1576
|
+
args: [VISUALIZER]
|
|
1577
|
+
}] }, { type: i1.MessageService }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i2.VisualizerStore }, { type: i3.SamToolService }, { type: i4.CellSegmentToolService }, { type: i5.SamPointToolService }, { type: i6.RegionOpsService }, { type: undefined, decorators: [{
|
|
1578
|
+
type: Optional
|
|
1579
|
+
}, {
|
|
1580
|
+
type: Inject,
|
|
1581
|
+
args: [VIZ_CONFIG]
|
|
1582
|
+
}] }], propDecorators: { isStackEvent: [{
|
|
1583
|
+
type: Output
|
|
1584
|
+
}], isGrayscaleEvent: [{
|
|
1585
|
+
type: Output
|
|
1586
|
+
}], toolbarTools: [{
|
|
1587
|
+
type: Input
|
|
1588
|
+
}], testMode: [{
|
|
1589
|
+
type: Input
|
|
1590
|
+
}], contextMenu: [{
|
|
1591
|
+
type: ViewChild,
|
|
1592
|
+
args: ['cm']
|
|
1593
|
+
}], onKeydown: [{
|
|
1594
|
+
type: HostListener,
|
|
1595
|
+
args: ['window:keydown', ['$event']]
|
|
1596
|
+
}] } });
|
|
1597
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlzdWFsaXplci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3Zpc3VhbGl6ZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vc3JjL2xpYi92aXN1YWxpemVyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxTQUFTLEVBQWlCLFlBQVksRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQWdDLFFBQVEsRUFBRSxNQUFNLEVBQWlCLFNBQVMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUV6TSxPQUFPLEVBQW1CLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDOUQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTNDLE9BQU8sRUFBWSxjQUFjLEVBQVksTUFBTSxhQUFhLENBQUM7QUFDakUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRWxELE9BQU8sRUFBa0IsZ0JBQWdCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUN0RixPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQVUsTUFBTSxpQkFBaUIsQ0FBQztBQUMzRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN4RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDbkUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLGFBQWEsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzFFLE9BQU8sRUFDTCxRQUFRLEVBRVIsVUFBVSxFQUNWLHVCQUF1QixHQUN4QixNQUFNLHVCQUF1QixDQUFDO0FBQy9CLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNsRSxPQUFPLEVBQWlDLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxD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LENBQUM7UUFDeEMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM5QyxDQUFDOzhHQTFtRFUsbUJBQW1CLGtCQW9NcEIsZ0JBQWdCLGFBQ2hCLFVBQVUsd1FBU0UsVUFBVTtrR0E5TXJCLG1CQUFtQiwyWEMxQ2hDLDZzU0FpTEE7OzJGRHZJYSxtQkFBbUI7a0JBUC9CLFNBQVM7K0JBR0UsWUFBWTs7MEJBd01uQixNQUFNOzJCQUFDLGdCQUFnQjs7MEJBQ3ZCLE1BQU07MkJBQUMsVUFBVTs7MEJBU2pCLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsVUFBVTt5Q0F0TWhDLFlBQVk7c0JBRFgsTUFBTTtnQkFHUCxnQkFBZ0I7c0JBRGYsTUFBTTtnQkFNSCxZQUFZO3NCQURmLEtBQUs7Z0JBY04sUUFBUTtzQkFEUCxLQUFLO2dCQXFDVyxXQUFXO3NCQUEzQixTQUFTO3VCQUFDLElBQUk7Z0JBbTVCZixTQUFTO3NCQURSLFlBQVk7dUJBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxRQUFRLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFuZ2VEZXRlY3RvclJlZiwgQ29tcG9uZW50LCBBZnRlclZpZXdJbml0LCBFdmVudEVtaXR0ZXIsIEhvc3RMaXN0ZW5lciwgSW5qZWN0LCBJbnB1dCwgTmdab25lLCBPbkNoYW5nZXMsIE9uRGVzdHJveSwgT25Jbml0LCBPcHRpb25hbCwgT3V0cHV0LCBTaW1wbGVDaGFuZ2VzLCBWaWV3Q2hpbGQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBTdWJqZWN0LCBTdWJzY3JpcHRpb24gfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHsgTWVudUl0ZW0sIE1lc3NhZ2VTZXJ2aWNlLCBUcmVlTm9kZSB9IGZyb20gJ3ByaW1lbmcvYXBpJztcbmltcG9ydCB7IENvbnRleHRNZW51IH0gZnJvbSAncHJpbWVuZy9jb250ZXh0bWVudSc7XG5pbXBvcnQgeyBJSW1hZ2VJbmZvIH0gZnJvbSAnLi9jb250cmFjdHMvaW1hZ2UuY29udHJhY3QnO1xuaW1wb3J0IHsgSW1hZ2VTdGF0ZVBvcnQsIElNQUdFX1NUQVRFX1BPUlQgfSBmcm9tICcuL2NvbnRyYWN0cy9wb3J0cy9pbWFnZS1zdGF0ZS5wb3J0JztcbmltcG9ydCB7IFBvbHlnb24sIFJlY3RhbmdsZSwgTXVsdGlQb2x5Z29uLCBSZWdpb24gfSBmcm9tICcuL21vZGVscy9yZWdpb24nO1xuaW1wb3J0IHsgUmVnaW9uT3BzU2VydmljZSB9IGZyb20gJy4vcmVnaW9uLW9wcy5zZXJ2aWNlJztcbmltcG9ydCB7IFZpc3VhbGl6ZXJTdG9yZSB9IGZyb20gJy4vc3RvcmUvdmlzdWFsaXplci1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IFJlbmRlck9yY2hlc3RyYXRvciwgU2xpY2VTY3J1YmJlciB9IGZyb20gJy4vcmVuZGVyLW9yY2hlc3RyYXRvcic7XG5pbXBvcnQge1xuICBQbG90VHlwZSxcbiAgUGxvdFR5cGVEZXNjcmlwdG9yLFxuICBpc05hcGFyaTNkLFxuICBOQVBBUklfREVGQVVMVF9ERUNJTUFURSxcbn0gZnJvbSAnLi9jb250cmFjdHMvcGxvdC10eXBlJztcbmltcG9ydCB7IFZpZXdlckZlYXR1cmUgfSBmcm9tICcuL2NvbnRyYWN0cy9jYXBhYmlsaXRpZXMuY29udHJhY3QnO1xuaW1wb3J0IHsgSW50ZW5zaXR5UHJvZmlsZSwgSVZpc3VhbGl6ZXIsIFZJU1VBTElaRVIgfSBmcm9tICcuL2NvbnRyYWN0cy92aXN1YWxpemVyLmNvbnRyYWN0JztcbmltcG9ydCB7IFNBTV9NT0RFTFMsIGdldERlZmF1bHRTYW1Nb2RlbElkLCBpc1NhbU1vZGVsUmVhZHkgfSBmcm9tICcuL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS1tb2RlbC1yZWdpc3RyeSc7XG5pbXBvcnQgeyBTYW1Ub29sU2VydmljZSB9IGZyb20gJy4vdG9vbGJhci9zZWdtZW50YXRpb24vc2FtLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBTYW1Qb2ludFRvb2xTZXJ2aWNlIH0gZnJvbSAnLi90b29sYmFyL3NlZ21lbnRhdGlvbi9zYW0tcG9pbnQtdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IENlbGxTZWdtZW50VG9vbFNlcnZpY2UgfSBmcm9tICcuL3Rvb2xiYXIvc2VnbWVudGF0aW9uL2NlbGwtc2VnbWVudC10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgUmVnaW9uVG9vbE1vZGUgfSBmcm9tICcuL2NvbnRyYWN0cy9yZWdpb24tb3ZlcmxheS5jb250cmFjdCc7XG5pbXBvcnQgeyBUb29sYmFyVG9vbFZpc2liaWxpdHksIEFMTF9UT09MQkFSX1RPT0xTIH0gZnJvbSAnLi9jb250cmFjdHMvdG9vbGJhci1jb25maWcnO1xuaW1wb3J0IHsgVklaX0NPTkZJRywgVml6Q29uZmlnIH0gZnJvbSAnLi9jb250cmFjdHMvdml6LWNvbmZpZyc7XG5cbi8qKiBQZXItaW5zdGFuY2UgcGxvdC1kaXYgaWQgc291cmNlLiBUaGUgbW91bnQgZWxlbWVudCdzIGlkIG11c3QgYmUgdW5pcXVlIHNvIHR3b1xuICogIGxpdmUgdmlld2VycyAoZS5nLiB0aGUgbWFpbiBkaWFncmFtICsgYSBtb2RhbCBwcmV2aWV3KSBkb24ndCBjb2xsaWRlIG9uIHRoZVxuICogIHNhbWUgRE9NIGlkIOKAlCBgZ2V0RWxlbWVudEJ5SWRgIHdvdWxkIG90aGVyd2lzZSByZXR1cm4gd2hpY2hldmVyIGNhbWUgZmlyc3QuXG4gKiAgU3R5bGluZyBoYW5ncyBvZmYgdGhlIGAudml6LXBsb3RgIGNsYXNzIGluc3RlYWQgb2YgdGhlIGlkLiAqL1xubGV0IHBsb3RJbnN0YW5jZVNlcSA9IDA7XG5cbkBDb21wb25lbnQoe1xuICAvLyBDYW5vbmljYWwgcHJlZml4ZWQgc2VsZWN0b3IgZmlyc3Q7IHRoZSB1bnByZWZpeGVkIG9yaWdpbmFsIGlzIGtlcHQgYXMgYW5cbiAgLy8gYWxpYXMgZm9yIG9uZSByZWxlYXNlIChwcmUtcHVibGljYXRpb24gYmFjay1jb21wYXQpLlxuICBzZWxlY3RvcjogJ3Zpc3VhbGl6ZXInLFxuICB0ZW1wbGF0ZVVybDogJy4vdmlzdWFsaXplci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3Zpc3VhbGl6ZXIuY29tcG9uZW50LnNjc3MnXSxcbn0pXG5leHBvcnQgY2xhc3MgVmlzdWFsaXplckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzLCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xuICAvKiogUGVyLWluc3RhbmNlIHRvYXN0IGtleS4gTWVzc2FnZVNlcnZpY2UgaXMgYSBnbG9iYWwgc2luZ2xldG9uLCBzbyB0d28gbGl2ZVxuICAgKiAgYDx2aXN1YWxpemVyPmAgaW5zdGFuY2VzIChtYWluIHZpZXdlciArIHBpcGVsaW5lLWRpYWxvZyBwcmV2aWV3KSBzaGFyaW5nXG4gICAqICBvbmUga2V5IHdvdWxkIGVhY2ggcmVuZGVyIHRoZSBzYW1lIG1lc3NhZ2Ug4oCUIGEgZHVwbGljYXRlIHRvYXN0LiBBIHVuaXF1ZVxuICAgKiAga2V5IHBlciBpbnN0YW5jZSBzY29wZXMgdGhlIHRvYXN0IHRvIHRoZSBjb21wb25lbnQgdGhhdCByYWlzZWQgaXQuICovXG4gIHByaXZhdGUgc3RhdGljIG5leHRUb2FzdElkID0gMDtcblxuICBAT3V0cHV0KClcbiAgaXNTdGFja0V2ZW50ID0gbmV3IEV2ZW50RW1pdHRlcihmYWxzZSk7XG4gIEBPdXRwdXQoKVxuICBpc0dyYXlzY2FsZUV2ZW50ID0gbmV3IEV2ZW50RW1pdHRlcihmYWxzZSk7XG5cbiAgLyoqIFdoaWNoIHRvb2xiYXIgZ3JvdXBzIHRvIHNob3cuIEEgcGFydGlhbCBvdmVycmlkZSBtZXJnZXMgb3ZlciB0aGUgZnVsbCBzZXQsXG4gICAqICBzbyBgeyBzcGVjaWFsVG9vbHM6IGZhbHNlIH1gIGhpZGVzIG9ubHkgdGhhdCBncm91cC4gRGVmYXVsdHMgdG8gZXZlcnl0aGluZy4gKi9cbiAgQElucHV0KClcbiAgc2V0IHRvb2xiYXJUb29scyh2OiBUb29sYmFyVG9vbFZpc2liaWxpdHkgfCB1bmRlZmluZWQpIHtcbiAgICB0aGlzLl90b29sYmFyVG9vbHMgPSB7IC4uLkFMTF9UT09MQkFSX1RPT0xTLCAuLi4odiA/PyB7fSkgfTtcbiAgfVxuXG4gIGdldCB0b29sYmFyVG9vbHMoKTogUmVxdWlyZWQ8VG9vbGJhclRvb2xWaXNpYmlsaXR5PiB7XG4gICAgcmV0dXJuIHRoaXMuX3Rvb2xiYXJUb29scztcbiAgfVxuICBwcml2YXRlIF90b29sYmFyVG9vbHM6IFJlcXVpcmVkPFRvb2xiYXJUb29sVmlzaWJpbGl0eT4gPSBBTExfVE9PTEJBUl9UT09MUztcblxuICAvKiogRXhwb3NlIHRlc3Qtb25seSBwbG90IG1vZGVzIGluIHRoZSBzZWxlY3RvciAoZS5nLiB0aGUgbmFwYXJpIGBJbWFnZWBcbiAgICogIG1vZGUsIG90aGVyd2lzZSBoaWRkZW4pLiBUaGUgaG9zdCBhcHAgc2V0cyB0aGlzIGZyb20gYSBgP3Rlc3Q9MWAgVVJMXG4gICAqICB0b2dnbGUuIE9mZiBieSBkZWZhdWx0LCBzbyBwcm9kdWN0aW9uIHVzZXJzIG5ldmVyIHNlZSB0aGVtLiAqL1xuICBASW5wdXQoKVxuICB0ZXN0TW9kZSA9IGZhbHNlO1xuXG4gIC8qKiBJbWFnZS1zbW9vdGhpbmcgc3RhdGUgZm9yIHRoZSB0b29sYmFyJ3MgU21vb3RoZW4gdG9nZ2xlIChPU0Qgb25seSkuXG4gICAqICBEZWZhdWx0cyB0byBgZmFsc2VgIHNvIE9TRCBzaG93cyByYXcgcGl4ZWxzIChuZWFyZXN0LW5laWdoYm91cikuICovXG4gIGltYWdlU21vb3RoaW5nRW5hYmxlZCA9IGZhbHNlO1xuICBsb2FkaW5nTWVzc2FnZSA9ICdMb2FkaW5nIGltYWdlLi4uJztcbiAgem9vbSA9IGZhbHNlO1xuICBmaWxlTmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBwcml2YXRlIGxvYWRlZEZpbGVOYW1lOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGltYWdlSW5mbzogSUltYWdlSW5mbyB8IHVuZGVmaW5lZDtcblxuICBwcml2YXRlIHVuc3ViID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcbiAgcHJpdmF0ZSBwcmV2aWV3U3Vic2NyaXB0aW9uID0gbmV3IFN1YnNjcmlwdGlvbigpO1xuXG4gIHB1YmxpYyBzdGFja0xvYWRpbmcgPSBmYWxzZTtcbiAgcHVibGljIGltZ0xvYWRpbmcgPSBmYWxzZTtcbiAgcHVibGljIGxvYWRpbmdQZXJjZW50YWdlID0gMDtcbiAgLy8gTm9uLW51bGwgKDAuLjEwMCkgd2hpbGUgaml0LXNlcnZpY2UgaXMgY29weWluZyB0aGUgc291cmNlIGZpbGUgaW50byB0aGVcbiAgLy8gbG9jYWwgY2FjaGUgUFZDIG9uIGZpcnN0IGNsaWNrLiBXaGVuIHNldCwgdGhlIGxvYWRpbmcgb3ZlcmxheSBzd2FwcyB0aGVcbiAgLy8gc3Bpbm5lciBmb3IgYSBkZXRlcm1pbmF0ZSBwcm9ncmVzcyBiYXIgd2l0aCBhIFwiQ2FjaGluZyBpbWFnZVwiIG1lc3NhZ2UuXG4gIHB1YmxpYyBjYWNoZVByb2dyZXNzOiBudW1iZXIgfCBudWxsID0gbnVsbDtcbiAgLy8gVHJ1ZSBiZXR3ZWVuIHRoZSBzbWFsbC10aWVyIHJlbmRlciBsYW5kaW5nIGFuZCB0aGUgbGFyZ2UtdGllciByZW5kZXJcbiAgLy8gbGFuZGluZyBpbiB0aGUgbXVsdGktdGllciByZW5kZXJpbmcgcGF0aC4gVGhlIHRlbXBsYXRlIHVzZXMgdGhpcyB0b1xuICAvLyBvdmVybGF5IGEgdHJhbnNsdWNlbnQgc3Bpbm5lciBvbiB0b3Agb2YgdGhlIGJsdXJyeSBzbWFsbC10aWVyIGltYWdlIHNvXG4gIC8vIHRoZSB1c2VyIGRvZXNuJ3QgbWlzdGFrZSBpdCBmb3IgdGhlIGZpbmFsIHByZXZpZXcuXG4gIHB1YmxpYyBzaGFycGVuaW5nID0gZmFsc2U7XG4gIC8qKiBBIGppdC1zZXJ2aWNlIGNhY2hlIGNvcHkgaXMgYWN0aXZlbHkgaW4gcHJvZ3Jlc3MgKGRldGVybWluYXRlIDAuLjk5KS4gRHJpdmVzIHRoZSBjYWNoZVxuICAgKiAgcHJvZ3Jlc3MgYmFyIGluZGVwZW5kZW50bHkgb2YgYGltZ0xvYWRpbmdgIHNvIHRoZSBiYXIgY2FuJ3QgYmUgZHJvcHBlZCDigJQgb3IgbWFza2VkIGJ5IHRoZVxuICAgKiAgXCJTaGFycGVuaW5nIHByZXZpZXcuLi5cIiBvdmVybGF5IOKAlCBtaWQtZG93bmxvYWQgaWYgdGhlIGxvYWRpbmcgZmxhZyBmbGlwcyBlYXJseS4gKi9cbiAgZ2V0IGlzQ2FjaGluZygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5jYWNoZVByb2dyZXNzICE9PSBudWxsICYmIHRoaXMuY2FjaGVQcm9ncmVzcyA8IDEwMDtcbiAgfVxuICBwcml2YXRlIHJ1bm5pbmcgPSBmYWxzZTtcbiAgcHVibGljIHpJbmRleCA9IDA7XG4gIHB1YmxpYyBtYXhJbmRleCA9IDA7XG5cbiAgQFZpZXdDaGlsZCgnY20nKSBjb250ZXh0TWVudSE6IENvbnRleHRNZW51O1xuICBjb250ZXh0TWVudUl0ZW1zOiBNZW51SXRlbVtdID0gW107XG5cbiAgYWN0aXZlRHJhZ01vZGU6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8qKiBXaGV0aGVyIGEgcmVnaW9uIGFjdGlvbiBpcyBhdmFpbGFibGUgdG8gdW5kbyAoaml0LXVpIzg1KS4gTWlycm9ycyB0aGVcbiAgICogIHNoYXJlZCBSZWdpb25TdG9yZSdzIGhpc3RvcnkgZGVwdGg7IGRyaXZlcyB0aGUgdG9vbGJhciBVbmRvIGJ1dHRvbi4gKi9cbiAgY2FuVW5kb1JlZ2lvbiA9IGZhbHNlO1xuICAvKiogV2hldGhlciBhbiB1bmRvbmUgcmVnaW9uIGFjdGlvbiBpcyBhdmFpbGFibGUgdG8gcmVkbyAoaml0LXVpIzg1KS4gKi9cbiAgY2FuUmVkb1JlZ2lvbiA9IGZhbHNlO1xuXG4gIC8qKiBDdXN0b20tdGhyZXNob2xkIFNpbXBsaWZ5IGRpYWxvZyAoaml0LXVpIzg1KS4gKi9cbiAgZGlzcGxheVNpbXBsaWZ5RGlhbG9nID0gZmFsc2U7XG4gIHNpbXBsaWZ5VGhyZXNob2xkID0gMztcbiAgLyoqIEN1cnJlbnQgcmVnaW9uIHNlbGVjdGlvbiAoYXJyYXkgaW5kaWNlcykg4oCUIG1pcnJvcmVkIGZyb20gdGhlIHN0b3JlIHNvIHRoZVxuICAgKiAgY29udGV4dC1tZW51IHJlZ2lvbiBhY3Rpb25zIGNhbiByZWFkIGl0IHN5bmNocm9ub3VzbHkgKGppdC11aSM4NSkuICovXG4gIHByaXZhdGUgc2VsZWN0ZWRJbmRpY2VzOiBudW1iZXJbXSA9IFtdO1xuXG4gIC8qKiBXYW5kIHNlbnNpdGl2aXR5IOKAlCBoaWdoZXIgPSBzdHJpY3RlciAoc21hbGxlciBzZWxlY3Rpb24pLiBNYXRjaGVzIFF1UGF0aCBkZWZhdWx0LiAqL1xuICB3YW5kU2Vuc2l0aXZpdHkgPSAyLjA7XG4gIC8qKiBCcnVzaCBkaWFtZXRlciBpbiBpbWFnZS1waXhlbCBjb29yZGluYXRlcyAoZHJpdmVzIHRoZSBwYWludGVkIGRpc2Mgc2l6ZSkuICovXG4gIGJydXNoU2l6ZSA9IDQwO1xuICAvKiogU0FNIG1vZGVsIHBpY2tlciBvcHRpb25zICsgY3VycmVudCBzZWxlY3Rpb24gKGppdC11aSM5MCBQMSkuIE9ubHkgbW9kZWxzXG4gICAqICB3aXRoIGEgaG9zdGVkIE9OTlggcGFpciAoY29uZmlndXJlZCB2aWEgc2V0U2FtTW9kZWxVcmxzIGF0IGFwcCBpbml0KSBhcmVcbiAgICogIG9mZmVyZWQsIHNvIHRoZSBwaWNrZXIgY2FuJ3Qgc2VsZWN0IGEgbW9kZWwgdGhhdCBjYW4ndCBydW4uICovXG4gIHNhbU1vZGVscyA9IFNBTV9NT0RFTFMuZmlsdGVyKGlzU2FtTW9kZWxSZWFkeSkubWFwKChtKSA9PiAoeyBpZDogbS5pZCwgbGFiZWw6IG0ubGFiZWwgfSkpO1xuICBzYW1Nb2RlbElkID0gZ2V0RGVmYXVsdFNhbU1vZGVsSWQoKTtcbiAgLyoqIFNBTSBkb3dubG9hZC9zZWdtZW50IHRvYXN0IHN0YXRlIChib3VuZCBieSB0aGUgYHNhbWAgcC10b2FzdCB0ZW1wbGF0ZSkuICovXG4gIHNhbVN0YXR1cyA9ICcnO1xuICBzYW1Qcm9ncmVzcyA9IDA7IC8vIDAuLjEwMCwgZW5jb2RlciBkb3dubG9hZFxuICBzYW1Eb3dubG9hZGluZyA9IGZhbHNlO1xuICBzYW1CdXN5ID0gZmFsc2U7IC8vIGFueSBTQU0gd29yayBpbiBmbGlnaHQgKGRyaXZlcyB0aGUgaW5kZXRlcm1pbmF0ZSBzcGlubmVyKVxuICAvKiogV2hldGhlciB0aGUgc2hhcmVkIGBzYW1gIHRvYXN0IGlzIGN1cnJlbnRseSBzaG93biAoYXZvaWRzIHN0YWNraW5nIGl0IG9uXG4gICAqICBldmVyeSBwb2ludCBjbGljaywgd2hpY2ggcmUtcnVucyBpbmZlcmVuY2UpLiAqL1xuICBwcml2YXRlIHNhbVRvYXN0U2hvd24gPSBmYWxzZTtcblxuICByZWFkb25seSBzYW1Ub2FzdEtleSA9IGBzYW0tJHtWaXN1YWxpemVyQ29tcG9uZW50Lm5leHRUb2FzdElkKyt9YDtcbiAgLyoqIFN1YnNjcmlwdGlvbnMgdG8gdGhlIHBvaW50IHRvb2wncyBsaXZlIGZlZWRzIChzdGF0dXMvYnVzeS9kb3dubG9hZCkuICovXG4gIHByaXZhdGUgc2FtUG9pbnRTdWIgPSBuZXcgU3Vic2NyaXB0aW9uKCk7XG4gIC8qKiBWZXJ0ZXggZXJhc2VyIHJhZGl1cyBpbiBpbWFnZS1waXhlbCBjb29yZGluYXRlcy4gKi9cbiAgdmVydGV4RXJhc2VyUmFkaXVzID0gMjA7XG5cbiAgY29sb3JtYXBzT3B0aW9ucyE6IGFueTtcbiAgcmV2ZXJzZXNjYWxlID0gZmFsc2U7XG4gIHNlbGVjdGVkQ29sb3JtYXAhOiBhbnk7XG4gIC8qKiBDaGFubmVscyAmIEhpc3RvZ3JhbSBkaWFsb2cgdmlzaWJpbGl0eSAob3BlbmVkIGZyb20gdGhlIHRvb2xiYXIpLiAqL1xuICBzaG93Q2hhbm5lbEhpc3RvZ3JhbSA9IGZhbHNlO1xuICAvKiogUmVnaW9uIEVkaXRvciBkaWFsb2cgdmlzaWJpbGl0eSAob3BlbmVkIGZyb20gdGhlIHRvb2xiYXIpLiAqL1xuICBzaG93UmVnaW9uRWRpdG9yID0gZmFsc2U7XG4gIC8qKiBSZWdpb24gRWRpdG9yIGRpYWxvZyB3aWR0aC4gT24gb3BlbiBpdCBpcyBzZXQgdG8gdGhlIGNvbmZpZ3VyZWQgaG9zdFxuICAgKiAgZWxlbWVudCdzIGN1cnJlbnQgd2lkdGgsIG9yIOKAlCB3aGVuIG5vIHNlbGVjdG9yIGlzIGNvbmZpZ3VyZWQgLyB0aGUgZWxlbWVudFxuICAgKiAgaXNuJ3QgZm91bmQg4oCUIHRvIGEgcXVhcnRlciBvZiB0aGUgcGFnZSAoc2VlIG9wZW5SZWdpb25FZGl0b3IpLiAqL1xuICByZWdpb25FZGl0b3JXaWR0aCA9ICcyNXZ3JztcbiAgc3RhY2tPcHRpb25zID0gW1xuICAgIHsgbmFtZTogJ1NpbmdsZSBpbWFnZScsIHZhbDogJ2ZhbHNlJyB9LFxuICAgIHsgbmFtZTogJ1N0YWNrJywgdmFsOiAndHJ1ZScgfSxcbiAgXTtcbiAgc2VsZWN0ZWRTdGFja09wdGlvbjogeyBuYW1lOiBzdHJpbmc7IHZhbDogc3RyaW5nIH0gfCB1bmRlZmluZWQgPSB0aGlzLnN0YWNrT3B0aW9uc1swXTtcbiAgcmVhZG9ubHkgcGxvdERpdk5hbWUgPSBgdml6LXBsb3QtJHtwbG90SW5zdGFuY2VTZXErK31gO1xuICBwbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuICBpc0hlYXRtYXAgPSB0cnVlO1xuICBhY3RpdmVTdXJmYWNlM2RNb2RlID0gJ3R1cm50YWJsZSc7XG4gIC8qKiBXaGV0aGVyIHRoZSBuYXBhcmkgM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gaXMgc2hvd24gKHZvbHVtZS9pc29zdXJmYWNlKS4gKi9cbiAgYXhlc1Zpc2libGUgPSB0cnVlO1xuICB3aXJlZnJhbWVBY3RpdmUgPSBmYWxzZTtcbiAgLyoqIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKDEgPSBGdWxsIOKApiA4ID0g4oWbOyBkZWZhdWx0IMK9KTsgY2hhbmdpbmcgaXQgcmUtcGxvdHMuICovXG4gIHJlc29sdXRpb25TY2FsZSA9IE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFO1xuXG4gIC8qKiBQbG90IHR5cGVzIHRoZSBhY3RpdmUgYmFja2VuZCBhZHZlcnRpc2VzICgzRCBnYXRlZCBieSBjYXBhYmlsaXR5KS4gKi9cbiAgcGxvdFR5cGVPcHRpb25zOiBQbG90VHlwZURlc2NyaXB0b3JbXSA9IFtdO1xuICBzZWxlY3RlZFBsb3RUeXBlOiBQbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuXG4gIC8qKiBGbG9hdGluZyBpbnRlbnNpdHktcHJvZmlsZSBpbnNldCAoTElORSBtb2RlKS4gKi9cbiAgcmVhZG9ubHkgaW50ZW5zaXR5SW5zZXREaXYgPSAnaW50ZW5zaXR5LWluc2V0LXBsb3QnO1xuICBwcm9maWxlUGFuZWxQb3MgPSB7IHg6IDIwLCB5OiA3MCB9O1xuICBwcml2YXRlIHByb2ZpbGVQYW5lbERyYWdnaW5nID0gZmFsc2U7XG4gIHByaXZhdGUgcHJvZmlsZVBhbmVsU3RhcnQgPSB7IG14OiAwLCBteTogMCwgeDogMCwgeTogMCB9O1xuXG4gIC8qKiBUb29sYmFyIGRvY2tpbmc6IGRvY2tlZCBhY3Jvc3MgdGhlIHRvcCBieSBkZWZhdWx0OyBkcmFnZ2luZyBpdHMgaGFuZGxlXG4gICAqICBkZXRhY2hlcyBpdCBpbnRvIGEgZmxvYXRpbmcsIG1vdmFibGUgd2luZG93IChmcmVlcyB0aGUgdG9wIHJvdyBmb3IgdGhlXG4gICAqICB2aXN1YWxpemF0aW9uKS4gKi9cbiAgdG9vbGJhckZsb2F0aW5nID0gZmFsc2U7XG4gIHRvb2xiYXJQb3MgPSB7IHg6IDgsIHk6IDggfTtcbiAgcHJpdmF0ZSB0b29sYmFyRHJhZ2dpbmcgPSBmYWxzZTtcbiAgcHJpdmF0ZSB0b29sYmFyU3RhcnQgPSB7IG14OiAwLCBteTogMCwgeDogMCwgeTogMCB9O1xuICBwcml2YXRlIGxhdGVzdFByb2ZpbGVzOiBJbnRlbnNpdHlQcm9maWxlW10gPSBbXTtcbiAgLyoqIERyaXZlcyB0aGUgaW50ZW5zaXR5IGluc2V0IHBhbmVsJ3MgdmlzaWJpbGl0eSDigJQgdHJ1ZSB3aGVuZXZlciBhbnlcbiAgICogIGludGVuc2l0eS1wcm9maWxlIGxpbmUgZXhpc3RzIChpbmRlcGVuZGVudCBvZiB0aGUgY3VycmVudCBwbG90IHR5cGUpLiAqL1xuICBoYXNQcm9maWxlcyA9IGZhbHNlO1xuICBwcml2YXRlIGludGVuc2l0eVByb2ZpbGVTdWI/OiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgdmlld3BvcnRDaGFuZ2VTdWI/OiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgcHJvZmlsZURyYWdNb3ZlTGlzdGVuZXI/OiAoZTogTW91c2VFdmVudCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSBwcm9maWxlRHJhZ1VwTGlzdGVuZXI/OiAoKSA9PiB2b2lkO1xuICBwcml2YXRlIHByb2ZpbGVSZXNpemVMaXN0ZW5lcj86ICgpID0+IHZvaWQ7XG5cbiAgLyoqIExJTkUgcGxvdCB0eXBlIHNob3dzIHRoZSBpbWFnZSArIGRyYWdnYWJsZSBsaW5lIFJPSSArIGludGVuc2l0eSBpbnNldC4gKi9cbiAgZ2V0IGlzUHJvZmlsZU1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9PT0gUGxvdFR5cGUuTElORTtcbiAgfVxuXG4gIC8qKiBUcnVlIGZvciB0aGUgSW1hZ2UgcGxvdCB0eXBlLCB3aGljaCByZW5kZXJzIGFzIGEgbmF0aXZlbHkgcGFuL3pvb20tYWJsZVxuICAgKiAgcmFzdGVyIOKAlCBzbyB0aGUgYmFja2VuZC1hZ25vc3RpYyB6b29tL3BhbiB0b29sYmFyIHRvb2xzIGFyZSBoaWRkZW4uIFRoZVxuICAgKiAgY29tcG9uZW50IGRyaXZlcyB0aGlzIG9mZiB0aGUgcGxvdCB0eXBlLCBub3QgdGhlIGFjdGl2ZSBiYWNrZW5kLiAqL1xuICBnZXQgaXNJbWFnZVZpZXcoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gIH1cblxuICAvKiogSXNvc3VyZmFjZSBiYW5kIGFzIGEgMOKAkzI1NSBzbGlkZXIgcG9zaXRpb24sIG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUncyByZWFsXG4gICAqICBpbnRlbnNpdHkgcmFuZ2UgYnkgdGhlIHJlbmRlcmVyLiBEZWZhdWx0cyB0byB0aGUgZnVsbCByYW5nZS4gKi9cbiAgaXNvUmFuZ2U6IG51bWJlcltdID0gWzAsIDI1NV07XG5cbiAgcGxvdFdpZHRoU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuICBpbWFnZUxvYWRpbmdTdWJzY3JpcHRpb24/OiBTdWJzY3JpcHRpb247XG4gIGltZ0xvYWRpbmdNZXNzYWdlU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuICBpc1pvb21TdWJzY3JpcHRpb24/OiBTdWJzY3JpcHRpb247XG4gIGZpbGVuYW1lU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuICBhdXRvc2NhbGVTdWJzY3JpcHRpb24/OiBTdWJzY3JpcHRpb247XG5cbiAgcHJpdmF0ZSBwbG90Q29udGV4dE1lbnVMaXN0ZW5lcj86IChlOiBNb3VzZUV2ZW50KSA9PiB2b2lkO1xuICBwcml2YXRlIGtleWRvd25MaXN0ZW5lcj86IChlOiBLZXlib2FyZEV2ZW50KSA9PiB2b2lkO1xuICBwcml2YXRlIHdoZWVsTGlzdGVuZXI/OiAoZTogV2hlZWxFdmVudCkgPT4gdm9pZDtcblxuICAvKiogRGVib3VuY2VkIHotc2xpY2Ugc2NydWJiaW5nIChzZWUgU2xpY2VTY3J1YmJlciDigJQgcmVmYWN0b3JpbmcgcGxhbiBTdGVwIDcpLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHNjcnViYmVyID0gbmV3IFNsaWNlU2NydWJiZXIoKHopID0+IHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFpJbmRleCh6KTtcbiAgICAvLyBJbiBhIHBlci1zbGljZSB6LXN0YWNrIHNlc3Npb24gdGhlIHN0b3JlIHN3YXBzIHRoZSBsaXZlIHJlZ2lvbiBzZXQgdG8gdGhpc1xuICAgIC8vIHNsaWNlIChwcmVzZXJ2aW5nIGVkaXRzIG1hZGUgb24gdGhlIHByZXZpb3VzIG9uZSk7IG91dHNpZGUgc3RhY2sgbW9kZSB0aGlzXG4gICAgLy8ganVzdCByZWNvcmRzIHRoZSBzbGljZSwgbGVhdmluZyBzaW5nbGUtcGxhbmUgcmVnaW9ucyB1bnRvdWNoZWQgKGppdC11aSM5MykuXG4gICAgdGhpcy5wbG90U2VydmljZS5zZXREaXNwbGF5U2xpY2Uoeik7XG4gIH0pO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIEBJbmplY3QoSU1BR0VfU1RBVEVfUE9SVCkgcHJpdmF0ZSBzdGF0ZTogSW1hZ2VTdGF0ZVBvcnQsXG4gICAgQEluamVjdChWSVNVQUxJWkVSKSBwdWJsaWMgcGxvdFNlcnZpY2U6IElWaXN1YWxpemVyLFxuICAgIHB1YmxpYyBtZXNzYWdlU2VydmljZTogTWVzc2FnZVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBuZ1pvbmU6IE5nWm9uZSxcbiAgICBwcml2YXRlIGNkcjogQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgcHJpdmF0ZSBzZXNzaW9uOiBWaXN1YWxpemVyU3RvcmUsXG4gICAgcHJpdmF0ZSBzYW1Ub29sOiBTYW1Ub29sU2VydmljZSxcbiAgICBwcml2YXRlIGNlbGxTZWdtZW50VG9vbDogQ2VsbFNlZ21lbnRUb29sU2VydmljZSxcbiAgICBwcml2YXRlIHNhbVBvaW50VG9vbDogU2FtUG9pbnRUb29sU2VydmljZSxcbiAgICBwcml2YXRlIHJlZ2lvbk9wczogUmVnaW9uT3BzU2VydmljZSxcbiAgICBAT3B0aW9uYWwoKSBASW5qZWN0KFZJWl9DT05GSUcpIHByaXZhdGUgdml6Q29uZmlnPzogVml6Q29uZmlnLFxuICApIHtcbiAgICB0aGlzLmNvbG9ybWFwc09wdGlvbnMgPSBwbG90U2VydmljZS5nZXRDb2xvcm1hcE9wdGlvbnMoKTtcbiAgICB0aGlzLmNvbXB1dGVQbG90VHlwZU9wdGlvbnMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQbG90IHR5cGVzIG9mZmVyZWQgaW4gdGhlIHNlbGVjdG9yIGZvciB0aGUgY3VycmVudCBpbWFnZTpcbiAgICogIC0gT3V0c2lkZSAqKnRlc3QgbW9kZSoqLCBvbmx5IHRoZSBjdXJhdGVkIGRlZmF1bHQgc2V0IChkZXNjcmlwdG9ycyB3aXRoIGFcbiAgICogICAgYHByb2R1Y3Rpb25MYWJlbGApIGlzIG9mZmVyZWQsIHNob3duIHVuZGVyIHN1ZmZpeC1mcmVlIG5hbWVzIOKAlCBJbWFnZSxcbiAgICogICAgSGVhdG1hcCwgQ29udG91ciBhbmQgdGhlIG5hcGFyaSBTdXJmYWNlIC8gVm9sdW1lIC8gSXNvc3VyZmFjZS4gVGVzdCBtb2RlXG4gICAqICAgIGV4cG9zZXMgZXZlcnkgYmFja2VuZCdzIHR5cGUgdW5kZXIgaXRzIGZ1bGwgKGJhY2tlbmQtc3VmZml4ZWQpIGxhYmVsLlxuICAgKiAgLSAzRCB0eXBlcyBoaWRkZW4gd2hlbiB0aGUgYmFja2VuZCBjYW4ndCByZW5kZXIgYSAzRCBzY2VuZS5cbiAgICogIC0gc3RhY2stb25seSB0eXBlcyAodm9sdW1lLCBpc29zdXJmYWNlKSBoaWRkZW4gdW5sZXNzIHRoZSBmaWxlIGlzIGEgc3RhY2sg4oCUXG4gICAqICAgIGEgdm9sdW1lIG5lZWRzIG11bHRpcGxlIHotc2xpY2VzLlxuICAgKiAgLSBzY2FsYXItaW50ZW5zaXR5IHR5cGVzIChjb250b3VyLCBzdXJmYWNlLCBpc29zdXJmYWNlKSBoaWRkZW4gZm9yIFJHQlxuICAgKiAgICBpbWFnZXMg4oCUIHRoZXkgbWFwIGEgc2luZ2xlIGludGVuc2l0eSBwZXIgcGl4ZWwuIEltYWdlIGFuZCBIZWF0bWFwIHJlbmRlclxuICAgKiAgICBhbnkgaW1hZ2UuXG4gICAqL1xuICBwcml2YXRlIGNvbXB1dGVQbG90VHlwZU9wdGlvbnMoKSB7XG4gICAgY29uc3QgY2FwcyA9IHRoaXMucGxvdFNlcnZpY2UuY2FwYWJpbGl0aWVzO1xuICAgIGNvbnN0IGlzU3RhY2sgPSAhIXRoaXMuaW1hZ2VJbmZvPy5pc1N0YWNrO1xuICAgIGNvbnN0IGlzR3JheXNjYWxlID0gISF0aGlzLmltYWdlSW5mbz8uaXNHcmF5c2NhbGU7XG4gICAgdGhpcy5wbG90VHlwZU9wdGlvbnMgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFBsb3RUeXBlRGVzY3JpcHRvcnMoKVxuICAgICAgLmZpbHRlcigoZCkgPT4ge1xuICAgICAgICAvLyBPdXRzaWRlIHRlc3QgbW9kZSwgb25seSB0aGUgY3VyYXRlZCBzZXQgKHRob3NlIHdpdGggYSBwcm9kdWN0aW9uTGFiZWwpXG4gICAgICAgIC8vIGlzIG9mZmVyZWQ7IHRlc3QgbW9kZSBleHBvc2VzIGV2ZXJ5IGJhY2tlbmQncyB0eXBlLlxuICAgICAgICBpZiAoIXRoaXMudGVzdE1vZGUgJiYgIWQucHJvZHVjdGlvbkxhYmVsKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIGlmIChkLmRpbWVuc2lvbnMgPT09ICczZCcgJiYgIWNhcHMuaGFzKFZpZXdlckZlYXR1cmUuU3VyZmFjZTNEKSkgcmV0dXJuIGZhbHNlO1xuICAgICAgICBpZiAoZC5yZXF1aXJlc1N0YWNrICYmICFpc1N0YWNrKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIGlmIChkLnJlcXVpcmVzR3JheXNjYWxlICYmICFpc0dyYXlzY2FsZSkgcmV0dXJuIGZhbHNlO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH0pXG4gICAgICAvLyBEZWZhdWx0IHNlbGVjdG9yIHNob3dzIHRoZSBzdWZmaXgtZnJlZSBwcm9kdWN0aW9uTGFiZWw7IHRlc3QgbW9kZSBrZWVwc1xuICAgICAgLy8gdGhlIGZ1bGwgYmFja2VuZC1zdWZmaXhlZCBsYWJlbCBzbyBzYW1lLW5hbWVkIG1vZGVzIHN0YXkgZGlzdGluZ3Vpc2hhYmxlLlxuICAgICAgLm1hcCgoZCkgPT4gKHRoaXMudGVzdE1vZGUgPyBkIDogeyAuLi5kLCBsYWJlbDogZC5wcm9kdWN0aW9uTGFiZWwhIH0pKTtcbiAgfVxuXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICAvLyBUaGUgY29uc3RydWN0b3IgY29tcHV0ZXMgdGhlIG9wdGlvbnMgd2l0aCB0ZXN0TW9kZSBzdGlsbCBhdCBpdHMgZGVmYXVsdDtcbiAgICAvLyByZWNvbXB1dGUgb25jZSB0aGUgaG9zdCBiaW5kcyBpdCAoYW5kIG9uIGFueSBsYXRlciBjaGFuZ2UpIHNvIHRoZSBzZWxlY3RvclxuICAgIC8vIHJlZmxlY3RzIHRlc3QgbW9kZSBpbW1lZGlhdGVseSwgd2l0aG91dCB3YWl0aW5nIGZvciBhbiBpbWFnZSB0byAocmUpbG9hZC5cbiAgICBpZiAoY2hhbmdlc1sndGVzdE1vZGUnXSkge1xuICAgICAgdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCk7XG4gICAgICAvLyBJZiB0ZXN0IG1vZGUgaXMgdHVybmVkIG9mZiB3aGlsZSBhIHRlc3Qtb25seSB0eXBlIChlLmcuIE5BUEFSSV9JTUFHRSkgaXNcbiAgICAgIC8vIGFjdGl2ZSwgdGhhdCB0eXBlIGlzIG5vIGxvbmdlciBvZmZlcmVkIOKAlCBmYWxsIGJhY2sgdG8gSW1hZ2UuXG4gICAgICB0aGlzLnJlY29uY2lsZVNlbGVjdGVkUGxvdFR5cGUoKTtcbiAgICB9XG4gIH1cblxuICAvKiogSWYgdGhlIGFjdGl2ZSBwbG90IHR5cGUgaXMgbm8gbG9uZ2VyIGluIHRoZSBvZmZlcmVkIG9wdGlvbnMgKGUuZy4gdGVzdCBtb2RlXG4gICAqICB0dXJuZWQgb2ZmIHdoaWxlIGEgdGVzdC1vbmx5IHR5cGUgd2FzIGFjdGl2ZSwgb3IgYSBzY2FsYXIgdHlwZSBjYXJyaWVkIG9udG9cbiAgICogIGFuIFJHQiBpbWFnZSksIGZhbGwgYmFjayB0byB0aGUgZGVmYXVsdCAyRCBJbWFnZSB2aWV3LiAqL1xuICBwcml2YXRlIHJlY29uY2lsZVNlbGVjdGVkUGxvdFR5cGUoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnBsb3RUeXBlT3B0aW9ucy5zb21lKChkKSA9PiBkLnR5cGUgPT09IHRoaXMuc2VsZWN0ZWRQbG90VHlwZSkpIHtcbiAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuICAgICAgdGhpcy5wbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuICAgICAgdGhpcy5pc0hlYXRtYXAgPSB0cnVlO1xuICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRQbG90VHlwZShQbG90VHlwZS5JTUFHRSk7XG4gICAgfVxuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgdGhpcy5zdGF0ZS5zZXREaWFncmFtKHRoaXMpO1xuICAgIHRoaXMuc2VsZWN0ZWRDb2xvcm1hcCA9XG4gICAgICB0aGlzLmNvbG9ybWFwc09wdGlvbnNbMF0uY2hpbGRyZW4uZmluZCgoYzogYW55KSA9PiBjLmxhYmVsID09PSAnR3JleXMgSW52JykgPz9cbiAgICAgIHRoaXMuY29sb3JtYXBzT3B0aW9uc1swXS5jaGlsZHJlblswXTtcbiAgICB0aGlzLnN0YWNrT3B0aW9ucyA9IFtcbiAgICAgIHsgbmFtZTogJ1NpbmdsZSBpbWFnZScsIHZhbDogJ2ZhbHNlJyB9LFxuICAgICAgeyBuYW1lOiAnU3RhY2snLCB2YWw6ICd0cnVlJyB9LFxuICAgIF07XG4gICAgdGhpcy5zZWxlY3RlZFN0YWNrT3B0aW9uID0gdGhpcy5zdGFja09wdGlvbnNbMF07XG4gICAgdGhpcy5hdXRvc2NhbGVTdWJzY3JpcHRpb24gPSB0aGlzLnBsb3RTZXJ2aWNlLmdldEF1dG9zY2FsZUV2ZW50KCkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIC8vIHJlc2V0IHRoZSBpbWFnZSBtb2RlIHRvIHNpbmdsZSBpbWFnZVxuICAgICAgdGhpcy5zZWxlY3RlZFN0YWNrT3B0aW9uID0geyBuYW1lOiAnU2luZ2xlIGltYWdlJywgdmFsOiAnZmFsc2UnIH07XG4gICAgICB0aGlzLmFjdGl2ZURyYWdNb2RlID0gbnVsbDtcbiAgICAgIHRoaXMuc2Vzc2lvbi5zZXRBY3RpdmVUb29sKG51bGwpO1xuICAgIH0pO1xuICAgIHRoaXMuaW1hZ2VMb2FkaW5nU3Vic2NyaXB0aW9uID0gdGhpcy5zdGF0ZS5pc0ltYWdlTG9hZGluZyQoKS5zdWJzY3JpYmUoKGlzSW1hZ2VMb2FkaW5nKSA9PiB7XG4gICAgICB0aGlzLmltZ0xvYWRpbmcgPSBpc0ltYWdlTG9hZGluZztcbiAgICB9KTtcbiAgICAvLyBVbmRvIGF2YWlsYWJpbGl0eSAoaml0LXVpIzg1KTogdGhlIHNoYXJlZCBSZWdpb25TdG9yZSBlbWl0cyB3aGVuZXZlciBpdHNcbiAgICAvLyBoaXN0b3J5IGRlcHRoIGNoYW5nZXM7IGdyZXlzIG91dCB0aGUgdG9vbGJhciBVbmRvIGJ1dHRvbiBhY2NvcmRpbmdseS5cbiAgICB0aGlzLnBsb3RTZXJ2aWNlXG4gICAgICAuZ2V0Q2FuVW5kbyQoKVxuICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMudW5zdWIpKVxuICAgICAgLnN1YnNjcmliZSgoY2FuVW5kbykgPT4ge1xuICAgICAgICB0aGlzLmNhblVuZG9SZWdpb24gPSBjYW5VbmRvO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlXG4gICAgICAuZ2V0Q2FuUmVkbyQoKVxuICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMudW5zdWIpKVxuICAgICAgLnN1YnNjcmliZSgoY2FuUmVkbykgPT4ge1xuICAgICAgICB0aGlzLmNhblJlZG9SZWdpb24gPSBjYW5SZWRvO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9KTtcbiAgICAvLyBNaXJyb3IgdGhlIHJlZ2lvbiBzZWxlY3Rpb24gc28gdGhlIHJpZ2h0LWNsaWNrIGFjdGlvbiBncm91cCAoaml0LXVpIzg1KVxuICAgIC8vIGNhbiByZWFkIGl0IHN5bmNocm9ub3VzbHkgd2hlbiB0aGUgbWVudSBpcyBidWlsdC5cbiAgICB0aGlzLnBsb3RTZXJ2aWNlXG4gICAgICAuZ2V0U2VsZWN0ZWRTaGFwZUluZGljZXMkKClcbiAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLnVuc3ViKSlcbiAgICAgIC5zdWJzY3JpYmUoKGluZGljZXMpID0+IHtcbiAgICAgICAgdGhpcy5zZWxlY3RlZEluZGljZXMgPSBpbmRpY2VzIHx8IFtdO1xuICAgICAgfSk7XG4gICAgLy8gSW50ZXJhY3RpdmUgcG9pbnQtcHJvbXB0IHNlZ21lbnRhdGlvbiBydW5zIGluc2lkZSB0aGUgcmVuZGVyZXIgb24gZWFjaFxuICAgIC8vIGNsaWNrOyBzdXJmYWNlIGl0cyBsaXZlIHN0YXR1cyArIGRvd25sb2FkIHByb2dyZXNzIGluIHRoZSBzaGFyZWQgYHNhbWBcbiAgICAvLyB0b2FzdCBzbyB0aGUgdXNlciBzZWVzIGl0IHdvcmtpbmcgKHRoZSBmaXJzdCBjbGljayBwdWxscyB0aGUgZW5jb2RlcikuXG4gICAgdGhpcy5zYW1Qb2ludFN1Yi5hZGQoXG4gICAgICB0aGlzLnNhbVBvaW50VG9vbC5wcm9ncmVzcyQuc3Vic2NyaWJlKChmKSA9PiB7XG4gICAgICAgIHRoaXMuc2FtRG93bmxvYWRpbmcgPSBmID49IDAgJiYgZiA8IDE7XG4gICAgICAgIGlmIChmID49IDApIHRoaXMuc2FtUHJvZ3Jlc3MgPSBNYXRoLm1pbigxMDAsIE1hdGgucm91bmQoZiAqIDEwMCkpO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9KSxcbiAgICApO1xuICAgIHRoaXMuc2FtUG9pbnRTdWIuYWRkKFxuICAgICAgdGhpcy5zYW1Qb2ludFRvb2wuc3RhdHVzJC5zdWJzY3JpYmUoKG0pID0+IHtcbiAgICAgICAgdGhpcy5zYW1TdGF0dXMgPSBtO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9KSxcbiAgICApO1xuICAgIHRoaXMuc2FtUG9pbnRTdWIuYWRkKFxuICAgICAgdGhpcy5zYW1Qb2ludFRvb2wuYnVzeSQuc3Vic2NyaWJlKChidXN5KSA9PiB7XG4gICAgICAgIHRoaXMuc2FtQnVzeSA9IGJ1c3k7XG4gICAgICAgIGlmIChidXN5KSB0aGlzLnNob3dTYW1Ub2FzdCgnU0FNIHBvaW50IHNlZ21lbnRhdGlvbicpO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9KSxcbiAgICApO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0U3RhY2tMb2FkaW5nUHJvZ3Jlc3MoKS5zdWJzY3JpYmUoKGxvYWRpbmdQcm9ncmVzcykgPT4ge1xuICAgICAgdGhpcy5sb2FkaW5nUGVyY2VudGFnZSA9IGxvYWRpbmdQcm9ncmVzcztcbiAgICB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmlzU3RhY2tMb2FkaW5nKCkuc3Vic2NyaWJlKChzdGFja0xvYWRpbmcpID0+IHtcbiAgICAgIHRoaXMuc3RhY2tMb2FkaW5nID0gc3RhY2tMb2FkaW5nO1xuICAgIH0pO1xuICAgIHRoaXMucGxvdFdpZHRoU3Vic2NyaXB0aW9uID0gdGhpcy5zdGF0ZS5nZXRQYW5lbFdpZHRoJCgpLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlbGF5b3V0KCk7XG4gICAgICAvLyBUaGUgaW50ZW5zaXR5IGluc2V0IGlzIGEgc2VwYXJhdGUgUGxvdGx5IGNoYXJ0IGluIGEgZmxvYXRpbmcgcGFuZWw7IHJlZmxvd1xuICAgICAgLy8gaXQgdG8gaXRzIGN1cnJlbnQgc2l6ZSB3aGVuIHRoZSBjYW52YXMgcmVzaXplcywgZWxzZSBpdCBrZWVwcyB0aGUgc3RhbGVcbiAgICAgIC8vIHNpemUgYW5kIGJsYW5rcyBvdXQuIERlZmVyIGEgdGljayBzbyB0aGUgcGFuZWwgbGF5b3V0IGhhcyBzZXR0bGVkLlxuICAgICAgaWYgKHRoaXMuaGFzUHJvZmlsZXMpIHtcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLnJlbmRlckludGVuc2l0eUluc2V0KCksIDApO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMuaW1nTG9hZGluZ01lc3NhZ2VTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmdldEltYWdlTG9hZGluZ01lc3NhZ2UkKCkuc3Vic2NyaWJlKChtZXNzYWdlKSA9PiB7XG4gICAgICB0aGlzLmxvYWRpbmdNZXNzYWdlID0gbWVzc2FnZTtcbiAgICB9KTtcbiAgICB0aGlzLnN0YXRlXG4gICAgICAuZ2V0Q2FjaGVQcm9ncmVzcyQoKVxuICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMudW5zdWIpKVxuICAgICAgLnN1YnNjcmliZSgocHJvZ3Jlc3MpID0+IHtcbiAgICAgICAgdGhpcy5jYWNoZVByb2dyZXNzID0gcHJvZ3Jlc3M7XG4gICAgICB9KTtcbiAgICB0aGlzLmlzWm9vbVN1YnNjcmlwdGlvbiA9IHRoaXMuc3RhdGUuaXNab29tJCgpLnN1YnNjcmliZSgoem9vbSkgPT4ge1xuICAgICAgdGhpcy56b29tID0gem9vbTtcbiAgICB9KTtcbiAgICB0aGlzLmZpbGVuYW1lU3Vic2NyaXB0aW9uID0gdGhpcy5zdGF0ZS5nZXRGaWxlbmFtZSQoKS5zdWJzY3JpYmUoKGZpbGVuYW1lKSA9PiB7XG4gICAgICBpZiAoZmlsZW5hbWUpIHtcbiAgICAgICAgdGhpcy5maWxlTmFtZSA9IGZpbGVuYW1lO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0Q29sb3JtYXAoKS5zdWJzY3JpYmUoKGNvbG9ybWFwKSA9PiB7XG4gICAgICB0aGlzLnNlbGVjdGVkQ29sb3JtYXAgPSBjb2xvcm1hcDtcbiAgICB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFJldmVyc2VTY2FsZSgpLnN1YnNjcmliZSgocmV2ZXJzZXNjYWxlKSA9PiB7XG4gICAgICB0aGlzLnJldmVyc2VzY2FsZSA9IHJldmVyc2VzY2FsZTtcbiAgICB9KTtcbiAgICB0aGlzLmludGVuc2l0eVByb2ZpbGVTdWIgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldEludGVuc2l0eVByb2ZpbGUkKCkuc3Vic2NyaWJlKChwcm9maWxlcykgPT4ge1xuICAgICAgdGhpcy5sYXRlc3RQcm9maWxlcyA9IHByb2ZpbGVzO1xuICAgICAgdGhpcy5oYXNQcm9maWxlcyA9IHByb2ZpbGVzLmxlbmd0aCA+IDA7XG4gICAgICAvLyBUaGUgcGFuZWwgZGl2IGlzIGJlaGluZCAqbmdJZj1cImhhc1Byb2ZpbGVzXCIuIGRldGVjdENoYW5nZXMoKSBtYXRlcmlhbGl6ZXNcbiAgICAgIC8vIGl0IHN5bmNocm9ub3VzbHkgKHRoZSBzdWJzY3JpcHRpb24gbWF5IGZpcmUgb3V0c2lkZSBBbmd1bGFyJ3Mgem9uZSDigJQgZS5nLlxuICAgICAgLy8gZnJvbSBhbiBPU0QgZHJhZyDigJQgc28gQ0Qgd291bGRuJ3QgcnVuIG9uIGl0cyBvd24pLiBUaGVuIHJlbmRlciBvbiB0aGUgbmV4dFxuICAgICAgLy8gYW5pbWF0aW9uIGZyYW1lLCBBRlRFUiB0aGUgYnJvd3NlciBsYXlzIHRoZSBwYW5lbCBvdXQ6IGEgc3luY2hyb25vdXMgZHJhd1xuICAgICAgLy8gaGl0cyBhIHplcm8tc2l6ZSBjb250YWluZXIgb24gZmlyc3QgY3JlYXRlIGFuZCBQbG90bHkga2VlcHMgdGhhdCBzaXplLCBzb1xuICAgICAgLy8gdGhlIGluc2V0IHN0YXlzIGJsYW5rIGFuZCBsaXZlIHVwZGF0ZXMgcmVkcmF3IGludG8gdGhlIHNhbWUgemVybyBib3guXG4gICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gdGhpcy5yZW5kZXJJbnRlbnNpdHlJbnNldCgpKTtcbiAgICB9KTtcbiAgICAvLyBXaGVuIHRoZSBPU0QgdmlldyBzZXR0bGVzIGF0IGEgbmV3IHpvb20vcGFuLCByZS1zYW1wbGUgdGhlIGludGVuc2l0eSBsaW5lc1xuICAgIC8vIGZyb20gYSBjcm9wIG9mIHRoZSB2aXNpYmxlIHJlZ2lvbiBzbyB0aGUgaW5zZXQgcmVmbGVjdHMgdGhlIHpvb20tbGV2ZWxcbiAgICAvLyByZXNvbHV0aW9uIChQbG90bHkncyBvd24gaGlnaC1kZWYgem9vbSB1cGRhdGVzIHRoZSBzYW1wbGluZyBjYWNoZSBpbmxpbmUpLlxuICAgIHRoaXMudmlld3BvcnRDaGFuZ2VTdWIgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFZpZXdwb3J0Q2hhbmdlJCgpLnN1YnNjcmliZSgocm9pKSA9PiB7XG4gICAgICBpZiAodGhpcy5oYXNQcm9maWxlcyAmJiB0aGlzLmlzSW1hZ2VWaWV3KSB7XG4gICAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVmcmVzaEludGVuc2l0eVNhbXBsaW5nRm9yUm9pKHJvaS54LCByb2kueSwgcm9pLndpZHRoLCByb2kuaGVpZ2h0LCB0aGlzLnpJbmRleCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5wcmV2aWV3U3Vic2NyaXB0aW9uID0gdGhpcy5zdGF0ZS5nZXRJbWFnZUluZm8kKCkuc3Vic2NyaWJlKHtcbiAgICAgIG5leHQ6IChpbWdJbmZvKSA9PiB7XG4gICAgICAgIGlmIChpbWdJbmZvKSB7XG4gICAgICAgICAgdGhpcy5pbWFnZUluZm8gPSBpbWdJbmZvO1xuICAgICAgICAgIC8vIFN0YWNrLW9ubHkgcGxvdCB0eXBlcyAoaXNvc3VyZmFjZSwgc2NhdHRlcjNkKSBkZXBlbmQgb24gd2hldGhlciB0aGlzXG4gICAgICAgICAgLy8gZmlsZSBpcyBhIHN0YWNrIOKAlCByZWNvbXB1dGUgdGhlIHNlbGVjdG9yIG9wdGlvbnMuXG4gICAgICAgICAgdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCk7XG4gICAgICAgICAgLy8gSWYgdGhlIGFjdGl2ZSBwbG90IHR5cGUgaXNuJ3QgdmFsaWQgZm9yIHRoaXMgaW1hZ2UgKGUuZy4gYSBzY2FsYXJcbiAgICAgICAgICAvLyB0eXBlIGxpa2UgQ29udG91ciBjYXJyaWVkIG92ZXIgdG8gYW4gUkdCIGltYWdlKSwgZmFsbCBiYWNrIHRvIEltYWdlLlxuICAgICAgICAgIHRoaXMucmVjb25jaWxlU2VsZWN0ZWRQbG90VHlwZSgpO1xuICAgICAgICAgIC8vIHJlc2V0IHN0YWNrIG9wdGlvbnMgc2VsZWN0aW9uXG4gICAgICAgICAgdGhpcy5zZWxlY3RlZFN0YWNrT3B0aW9uID0gaW1nSW5mby5zaG93U3RhY2sgPyB0aGlzLnN0YWNrT3B0aW9uc1sxXSA6IHRoaXMuc3RhY2tPcHRpb25zWzBdO1xuXG4gICAgICAgICAgdGhpcy5pc0dyYXlzY2FsZUV2ZW50LmVtaXQodGhpcy5pbWFnZUluZm8uaXNHcmF5c2NhbGUpO1xuICAgICAgICAgIHRoaXMuaXNTdGFja0V2ZW50LmVtaXQodGhpcy5pbWFnZUluZm8uaXNTdGFjayk7XG4gICAgICAgICAgLy8gUmVzZXQgdG8gdGhlIGRlZmF1bHQgMkQgSW1hZ2UgdmlldyB3aGVuIGEgZGlmZmVyZW50IGltYWdlIGlzXG4gICAgICAgICAgLy8gc2VsZWN0ZWQgd2hpbGUgYSAzRCB0eXBlIGlzIGFjdGl2ZS5cbiAgICAgICAgICBpZiAoIXRoaXMuaXNIZWF0bWFwICYmIGltZ0luZm8uZmlsZU5hbWUgIT09IHRoaXMubG9hZGVkRmlsZU5hbWUpIHtcbiAgICAgICAgICAgIHRoaXMuaXNIZWF0bWFwID0gdHJ1ZTtcbiAgICAgICAgICAgIHRoaXMucGxvdFR5cGUgPSBQbG90VHlwZS5JTUFHRTtcbiAgICAgICAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRQbG90VHlwZSh0aGlzLnBsb3RUeXBlKTtcbiAgICAgICAgICAgIHRoaXMuYWN0aXZlU3VyZmFjZTNkTW9kZSA9ICd0dXJudGFibGUnO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLmxvYWRlZEZpbGVOYW1lID0gaW1nSW5mby5maWxlTmFtZTtcbiAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldEltYWdlTWV0YSh0aGlzLmltYWdlSW5mby5pbWFnZU1ldGEpO1xuICAgICAgICAgIGNvbnN0IHVybHMgPSBpbWdJbmZvLnVybHM7XG4gICAgICAgICAgaWYgKCF0aGlzLnJ1bm5pbmcpIHtcbiAgICAgICAgICAgIC8vIGltYWdlIHNpemUg4oCUIG1lYXN1cmUgdGhlIHBsb3QgZGl2IGRpcmVjdGx5IHNvIHRoZSB0b29sYmFyIGhlaWdodCBpcyBleGNsdWRlZFxuICAgICAgICAgICAgY29uc3QgcGxvdERpdjogSFRNTEVsZW1lbnQgfCBudWxsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2TmFtZSk7XG4gICAgICAgICAgICBjb25zdCBzY3JlZW5IZWlnaHQgPSBwbG90RGl2Py5vZmZzZXRIZWlnaHQgfHwgNTAwO1xuICAgICAgICAgICAgaWYgKHVybHMpIHtcbiAgICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5yZXNldCgpO1xuICAgICAgICAgICAgICB0aGlzLnN0YWNrTG9hZGluZyA9IGltZ0luZm8uaXNTdGFjayAmJiBpbWdJbmZvLnNob3dTdGFjaztcbiAgICAgICAgICAgICAgLy8gc2V0IG1heCBpbmRleCBvZiBzdGFjayDigJQgY29tcHV0ZWQgYmVmb3JlIHVwZGF0ZVpJbmRleCBzbyBhXG4gICAgICAgICAgICAgIC8vIGZyZXNoIHN0YWNrIChzaG9ydGVyIG9yIGxvbmdlciB0aGFuIHdoYXRldmVyIHdhcyBwcmV2aW91c2x5XG4gICAgICAgICAgICAgIC8vIGxvYWRlZCkgY2xhbXBzIGFnYWluc3QgSVRTIGJvdW5kcywgbm90IHRoZSBzdGFsZSBvbmVzLlxuICAgICAgICAgICAgICAvLyBPbmUgVVJMIHBlciBzbGljZSAoMC1pbmRleGVkKSwgc28gdGhlIGxhc3QgcmVhY2hhYmxlIGluZGV4IGlzXG4gICAgICAgICAgICAgIC8vIGxlbmd0aC0xIOKAlCBlYXJsaWVyIGBsZW5ndGgtMmAgZHJvcHBlZCB0aGUgZmluYWwgc2xpY2UuXG4gICAgICAgICAgICAgIHRoaXMubWF4SW5kZXggPSB1cmxzLmxlbmd0aCA+IDEgPyB1cmxzLmxlbmd0aCAtIDEgOiAwO1xuICAgICAgICAgICAgICAvLyBPbmUtc2hvdCBoaW50OiBqdW1wIHN0cmFpZ2h0IHRvIGEgc3BlY2lmaWMgc2xpY2UgKGUuZy4gdGhlXG4gICAgICAgICAgICAgIC8vIGZpbGUgdGhlIHVzZXIgYWN0dWFsbHkgY2xpY2tlZCB3aXRoaW4gYSBudW1iZXJlZCBzZXJpZXMpLlxuICAgICAgICAgICAgICAvLyBDb25zdW1lZCBpbW1lZGlhdGVseSBzbyByZWRlbGl2ZXJpbmcgdGhlIHNhbWUgSW1hZ2VJbmZvIGxhdGVyXG4gICAgICAgICAgICAgIC8vIChhIHBsb3QtdHlwZSBzd2l0Y2gsIHJlbG9hZEFuZFBsb3QpIGRvZXNuJ3QgcmVzZXQgdGhlIHVzZXInc1xuICAgICAgICAgICAgICAvLyBjdXJyZW50IHNjcnViIHBvc2l0aW9uIGJhY2sgdG8gaXQuXG4gICAgICAgICAgICAgIGlmIChpbWdJbmZvLmluaXRpYWxaSW5kZXggIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHRoaXMuekluZGV4ID0gaW1nSW5mby5pbml0aWFsWkluZGV4O1xuICAgICAgICAgICAgICAgIGltZ0luZm8uaW5pdGlhbFpJbmRleCA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAvLyBtYWtlIHN1cmUgdGhlIHppbmRleCBpcyB3aXRoaW4gYm91bmRzXG4gICAgICAgICAgICAgIHRoaXMudXBkYXRlWkluZGV4KHVybHMpO1xuICAgICAgICAgICAgICB0aGlzLnJ1bm5pbmcgPSB0cnVlO1xuICAgICAgICAgICAgICAvLyBNdWx0aS10aWVyIHJlbmRlcmluZyAoc21hbGwgYmx1cnJ5IHRpZXIgZmlyc3QsIHRoZW4gc2hhcnBlbiBpblxuICAgICAgICAgICAgICAvLyBwbGFjZSkg4oCUIHNlcXVlbmNpbmcgbGl2ZXMgaW4gUmVuZGVyT3JjaGVzdHJhdG9yOyB0aGlzIGNvbXBvbmVudFxuICAgICAgICAgICAgICAvLyBzdXBwbGllcyB0aGUgcGhhc2UgcmVuZGVyIGFuZCBvd25zIHRoZSBVSSBmbGFncyB2aWEgY2FsbGJhY2tzLlxuICAgICAgICAgICAgICAvLyAzRCBwbG90IHR5cGVzIHJlbmRlciBzaW5nbGUtcGFzczogdGhlIGluLXBsYWNlIGxhcmdlIHBhc3MgZG9lc24ndFxuICAgICAgICAgICAgICAvLyByZWJ1aWxkIGEgM0QgZ2wtbWVzaCBpc29zdXJmYWNlLCBzbyB0aGUgc2hhcnBlbiBzdGVwIGJsYW5rZWQgaXQuXG4gICAgICAgICAgICAgIGNvbnN0IGhhc1NtYWxsVGllciA9XG4gICAgICAgICAgICAgICAgdGhpcy5pc0hlYXRtYXAgJiZcbiAgICAgICAgICAgICAgICAoaW1nSW5mby5zbWFsbFVybHM/Lmxlbmd0aCA/PyAwKSA9PT0gdXJscy5sZW5ndGggJiZcbiAgICAgICAgICAgICAgICAoaW1nSW5mby5zbWFsbFVybHM/Lmxlbmd0aCA/PyAwKSA+IDA7XG4gICAgICAgICAgICAgIGNvbnN0IHNtYWxsSW1nSW5mbyA9IGhhc1NtYWxsVGllciA/IHsgLi4uaW1nSW5mbywgdXJsczogaW1nSW5mby5zbWFsbFVybHMgYXMgc3RyaW5nW10gfSA6IG51bGw7XG5cbiAgICAgICAgICAgICAgLy8gRW50ZXIgcGVyLXNsaWNlIHN0YWNrIG1vZGUgd2l0aCB0aGUgZ2l2ZW4gc2xpY2XihpJyZWdpb25zIG1hcCBhbmRcbiAgICAgICAgICAgICAgLy8gc2VlZCB0aGUgXCJwcmV2aW91cyBzaGFwZXNcIiBmcm9tIHRoZSBub3ctbGl2ZSBzbGljZS4gZW50ZXJTdGFja01vZGVcbiAgICAgICAgICAgICAgLy8gbWFrZXMgekluZGV4J3Mgc2xpY2UgbGl2ZSBhbmQgcmVzZXRzIHVuZG8gKGppdC11aSM5MykuXG4gICAgICAgICAgICAgIGNvbnN0IGVudGVyU3RhY2sgPSAoXG4gICAgICAgICAgICAgICAgc2xpY2VzOiBNYXA8bnVtYmVyLCBSZWdpb25bXT4sXG4gICAgICAgICAgICAgICAgbGF5b3V0OiAnY29tYmluZWQnIHwgJ3Blci1zbGljZS1maWxlJyxcbiAgICAgICAgICAgICAgKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5lbnRlclN0YWNrTW9kZShzbGljZXMsIHRoaXMuekluZGV4LCBsYXlvdXQpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHNoYXBlcyA9IHRoaXMucGxvdFNlcnZpY2VcbiAgICAgICAgICAgICAgICAgIC5nZXRSZWdpb25zKClcbiAgICAgICAgICAgICAgICAgIC5tYXAoKHJlZ2lvbikgPT4gcmVnaW9uLmdldFNoYXBlKHRoaXMucGxvdFNlcnZpY2UuZ2V0U2hvd1NoYXBlTGFiZWwoKSkpO1xuICAgICAgICAgICAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UHJldmlvdXNTaGFwZXMoc2hhcGVzKTtcbiAgICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgICBjb25zdCBhcHBseVJvaSA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICAvLyBGb2xkZXIgc3RhY2s6IGEgc3RhY2sgb2Ygc2VsZi1jb250YWluZWQgcGVyLXNsaWNlIGZpbGVzXG4gICAgICAgICAgICAgICAgLy8gKHRpbGVkID09PSBmYWxzZTsgc2VlIGxvYWRTZXJpZXNBc1N0YWNrKS4gRWFjaCBzbGljZS1maWxlIG1heVxuICAgICAgICAgICAgICAgIC8vIGNhcnJ5IGl0cyBvd24gc2libGluZyBcIjxzdGVtPi5nZW9qc29uXCIgKHJvaUpzb25TdHJzW3pdKSwgYnV0IGFcbiAgICAgICAgICAgICAgICAvLyBmcmVzaCBmb2xkZXIgd2l0aCBub25lIHlldCBsZWF2ZXMgcm9pSnNvblN0cnMgdW5kZWZpbmVkIOKAlCBrZXlcbiAgICAgICAgICAgICAgICAvLyBvZmYgYHRpbGVkID09PSBmYWxzZWAsIE5PVCByb2lKc29uU3Rycywgc28gYW4gdW5hbm5vdGF0ZWRcbiAgICAgICAgICAgICAgICAvLyBmb2xkZXIgc3RhY2sgc3RpbGwgZW50ZXJzIHRoZSBwZXItc2xpY2UtZmlsZSBsYXlvdXQgKGFuZCBzYXZlc1xuICAgICAgICAgICAgICAgIC8vIGJhY2sgb25lIGdlb2pzb24gcGVyIHNsaWNlLWZpbGUpIHJhdGhlciB0aGFuIGZhbGxpbmcgdGhyb3VnaCB0b1xuICAgICAgICAgICAgICAgIC8vIHRoZSBzaW5nbGUtZmlsZSBjb21iaW5lZCBwYXRoIChqaXQtdWkjOTMpLlxuICAgICAgICAgICAgICAgIGlmIChpbWdJbmZvLmlzU3RhY2sgJiYgaW1nSW5mby50aWxlZCA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHBlclNsaWNlID0gaW1nSW5mby5yb2lKc29uU3RycztcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHNsaWNlQ291bnQgPSBpbWdJbmZvLnVybHM/Lmxlbmd0aCA/PyBwZXJTbGljZT8ubGVuZ3RoID8/IDA7XG4gICAgICAgICAgICAgICAgICBjb25zdCBzbGljZXMgPSBuZXcgTWFwPG51bWJlciwgUmVnaW9uW10+KCk7XG4gICAgICAgICAgICAgICAgICBmb3IgKGxldCB6ID0gMDsgeiA8IHNsaWNlQ291bnQ7IHorKykge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBqc29uID0gcGVyU2xpY2U/Llt6XSA/PyBudWxsO1xuICAgICAgICAgICAgICAgICAgICBzbGljZXMuc2V0KHosIGpzb24gPyB0aGlzLnBsb3RTZXJ2aWNlLmltcG9ydFJlZ2lvbnMoanNvbikgOiBbXSk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBlbnRlclN0YWNrKHNsaWNlcywgJ3Blci1zbGljZS1maWxlJyk7XG4gICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIFNpbmdsZS1maWxlIHotc3RhY2s6IG9uZSBzaWJsaW5nIGdlb2pzb24gaG9sZGluZyBldmVyeSBzbGljZSdzXG4gICAgICAgICAgICAgICAgLy8gcmVnaW9ucyBpbmRleGVkIGJ5IFF1UGF0aCdzIGdlb21ldHJ5LnBsYW5lLnouIEVudGVyIHBlci1zbGljZVxuICAgICAgICAgICAgICAgIC8vIG1vZGUgKHNhdmluZyBiYWNrIG9uZSBjb21iaW5lZCB6LWluZGV4ZWQgZ2VvanNvbikgd2hlbiB0aGVcbiAgICAgICAgICAgICAgICAvLyBnZW9qc29uIGFjdHVhbGx5IGNhcnJpZXMgc2xpY2UgaW5kaWNlcywgb3Igd2hlbiB0aGVyZSdzIG5vdGhpbmdcbiAgICAgICAgICAgICAgICAvLyB5ZXQgdG8gYXV0aG9yIGFnYWluc3QuIEEgbGVnYWN5IGdlb2pzb24gd2hvc2UgcmVnaW9ucyBhcmUgYWxsIG9uXG4gICAgICAgICAgICAgICAgLy8gdGhlIGRlZmF1bHQgcGxhbmUgc3RheXMgZ2xvYmFsIChzaG93biBvbiBldmVyeSBzbGljZSkgc29cbiAgICAgICAgICAgICAgICAvLyBleGlzdGluZyBzaW5nbGUtcGxhbmUgYW5ub3RhdGlvbnMgYXJlbid0IGNvbmZpbmVkIHRvIHNsaWNlIDAuXG4gICAgICAgICAgICAgICAgY29uc3Qgcm9pSnNvbiA9IGltZ0luZm8ucm9pSnNvblN0cjtcbiAgICAgICAgICAgICAgICBpZiAoaW1nSW5mby5pc1N0YWNrKSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCByZWdpb25zID0gcm9pSnNvbiA/IHRoaXMucGxvdFNlcnZpY2UuaW1wb3J0UmVnaW9ucyhyb2lKc29uKSA6IFtdO1xuICAgICAgICAgICAgICAgICAgY29uc3QgaGFzU2xpY2VJbmZvID0gcmVnaW9ucy5zb21lKChyKSA9PiAoci56ID8/IDApICE9PSAwKTtcbiAgICAgICAgICAgICAgICAgIGlmICghcm9pSnNvbiB8fCBoYXNTbGljZUluZm8pIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3Qgc2xpY2VzID0gbmV3IE1hcDxudW1iZXIsIFJlZ2lvbltdPigpO1xuICAgICAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IHIgb2YgcmVnaW9ucykge1xuICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHogPSByLnogPz8gMDtcbiAgICAgICAgICAgICAgICAgICAgICBjb25zdCBidWNrZXQgPSBzbGljZXMuZ2V0KHopO1xuICAgICAgICAgICAgICAgICAgICAgIGlmIChidWNrZXQpIGJ1Y2tldC5wdXNoKHIpO1xuICAgICAgICAgICAgICAgICAgICAgIGVsc2Ugc2xpY2VzLnNldCh6LCBbcl0pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVudGVyU3RhY2soc2xpY2VzLCAnY29tYmluZWQnKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAvLyBTaW5nbGUtcGxhbmUgaW1hZ2UgKG9yIGEgbGVnYWN5IGdsb2JhbCB6LXN0YWNrIGdlb2pzb24pOiBvbmVcbiAgICAgICAgICAgICAgICAvLyByZWdpb24gc2V0IGZvciB0aGUgd2hvbGUgaW1hZ2UuXG4gICAgICAgICAgICAgICAgaWYgKHJvaUpzb24pIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHJlZ2lvbnMgPSB0aGlzLnBsb3RTZXJ2aWNlLmltcG9ydFJlZ2lvbnMocm9pSnNvbik7XG4gICAgICAgICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFJlZ2lvbnMocmVnaW9ucyk7XG4gICAgICAgICAgICAgICAgICBjb25zdCBzaGFwZXMgPSByZWdpb25zLm1hcCgocmVnaW9uKSA9PlxuICAgICAgICAgICAgICAgICAgICByZWdpb24uZ2V0U2hhcGUodGhpcy5wbG90U2VydmljZS5nZXRTaG93U2hhcGVMYWJlbCgpKSxcbiAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFByZXZpb3VzU2hhcGVzKHNoYXBlcyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIExvYWRpbmcgYW4gaW1hZ2UncyBzYXZlZCBST0lzIGlzIG5vdCBhIHVzZXIgZWRpdCDigJQgc3RhcnQgdGhlXG4gICAgICAgICAgICAgICAgLy8gdW5kbyBoaXN0b3J5IGZyZXNoIHNvIHRoZSBmaXJzdCB1bmRvIGNhbid0IHdpcGUgdGhlbSAoaml0LXVpIzg1KS5cbiAgICAgICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlc2V0VW5kb0hpc3RvcnkoKTtcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgbGV0IG92ZXJsYXlSZWxlYXNlZCA9IGZhbHNlO1xuICAgICAgICAgICAgICBjb25zdCByZWxlYXNlT3ZlcmxheSA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAob3ZlcmxheVJlbGVhc2VkKSByZXR1cm47XG4gICAgICAgICAgICAgICAgb3ZlcmxheVJlbGVhc2VkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBpZiAoaW1nSW5mby5pc1N0YWNrICYmIGltZ0luZm8uc2hvd1N0YWNrKSB0aGlzLnN0YWNrTG9hZGluZyA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VMb2FkaW5nKGZhbHNlKTtcbiAgICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgICBuZXcgUmVuZGVyT3JjaGVzdHJhdG9yKHtcbiAgICAgICAgICAgICAgICAvLyBpblBsYWNlPXRydWUgdXBkYXRlcyB0aGUgZXhpc3RpbmcgcmVuZGVyIGluc3RlYWQgb2YgcmVidWlsZGluZ1xuICAgICAgICAgICAgICAgIC8vIGl0LCBzbyB0aGUgY2FudmFzIGRvZXNuJ3QgYmxhbmsgZHVyaW5nIHRoZSBzbWFsbOKGkmxhcmdlIHN3YXAuXG4gICAgICAgICAgICAgICAgcmVuZGVyUGhhc2U6IChwaGFzZUluZm8sIGluUGxhY2UpID0+XG4gICAgICAgICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLmxvYWQocGhhc2VJbmZvLCB0aGlzLnpJbmRleCkudGhlbigobG9hZGVkSW1hZ2UpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgLy8gR3VhcmQgYWdhaW5zdCBhIG5ld2VyIGNsaWNrIHJlYWNoaW5nIHVzIG1pZC1yZW5kZXIuXG4gICAgICAgICAgICAgICAgICAgIGlmIChwaGFzZUluZm8uZmlsZU5hbWUgIT09IGxvYWRlZEltYWdlLmZpbGVuYW1lKSByZXR1cm4gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMucGxvdFNlcnZpY2UucGxvdChcbiAgICAgICAgICAgICAgICAgICAgICB0aGlzLnBsb3REaXZOYW1lLFxuICAgICAgICAgICAgICAgICAgICAgIGxvYWRlZEltYWdlLFxuICAgICAgICAgICAgICAgICAgICAgIHBoYXNlSW5mbyxcbiAgICAgICAgICAgICAgICAgICAgICBzY3JlZW5IZWlnaHQsXG4gICAgICAgICAgICAgICAgICAgICAgdGhpcy5wbG90VHlwZSxcbiAgICAgICAgICAgICAgICAgICAgICBpblBsYWNlLFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgICAgc21hbGxTaG93bjogKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgLy8gSWYgdGhlIGZpbGUgaXMgc3RpbGwgYmVpbmcgY2FjaGVkL3ByZXBhcmVkIHNlcnZlci1zaWRlLCBrZWVwIHRoZSBmdWxsXG4gICAgICAgICAgICAgICAgICAvLyBjYWNoZS1wcm9ncmVzcyBvdmVybGF5IHVwIGluc3RlYWQgb2YgZHJvcHBpbmcgdG8gdGhlIHRyYW5zbHVjZW50IFwiU2hhcnBlbmluZ1xuICAgICAgICAgICAgICAgICAgLy8gcHJldmlldy4uLlwiIHNwaW5uZXIgb3ZlciBhIGJsYW5rIGNhbnZhcyAoYSBsYXJnZSB1bmNhY2hlZCBpbWFnZSByZW5kZXJzIGl0c1xuICAgICAgICAgICAgICAgICAgLy8gc21hbGwgcHJldmlldyBiZWZvcmUgaXRzIHRpbGVzIGV4aXN0KS4gZmluaXNoZWQoKSByZWxlYXNlcyB0aGUgb3ZlcmxheSBvbmNlXG4gICAgICAgICAgICAgICAgICAvLyB0aGUgcmVhbCByZW5kZXIgbGFuZHMuXG4gICAgICAgICAgICAgICAgICBpZiAodGhpcy5pc0NhY2hpbmcpIHJldHVybjtcbiAgICAgICAgICAgICAgICAgIC8vIFNtYWxsIHRpZXIgb24gc2NyZWVuIOKAlCBkcm9wIHRoZSBmdWxsIG92ZXJsYXkgYnV0IGtlZXAgYSB0cmFuc2x1Y2VudCBzcGlubmVyIHNvXG4gICAgICAgICAgICAgICAgICAvLyB0aGUgYmx1cnJ5IHJlbmRlciBpc24ndCBtaXN0YWtlbiBmb3IgdGhlIGZpbmFsIGltYWdlLlxuICAgICAgICAgICAgICAgICAgcmVsZWFzZU92ZXJsYXkoKTtcbiAgICAgICAgICAgICAgICAgIHRoaXMuc2hhcnBlbmluZyA9IHRydWU7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBzaGFycGVuU2V0dGxlZDogKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgdGhpcy5zaGFycGVuaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBmaW5pc2hlZDogKF92aWFTbWFsbCwgbG9nVGFnKSA9PiB7XG4gICAgICAgICAgICAgICAgICAvLyBJZGVtcG90ZW50IOKAlCByZWxlYXNlcyBub3cgaWYgc21hbGxTaG93biBkZWZlcnJlZCBpdCAoY2FjaGluZykgb3Igd2FzIHNraXBwZWQuXG4gICAgICAgICAgICAgICAgICByZWxlYXNlT3ZlcmxheSgpO1xuICAgICAgICAgICAgICAgICAgdGhpcy5ydW5uaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICBhcHBseVJvaSgpO1xuICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2cobG9nVGFnKTtcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgIHNoYXJwZW5GYWlsZWQ6IChlcnI6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgICAgLy8gVGhlIHNtYWxsIHRpZXIgc3RheXMgb24gc2NyZWVuIGFzIHRoZSBmYWxsYmFjayDigJQgdGVsbCB0aGVcbiAgICAgICAgICAgICAgICAgIC8vIHVzZXIgdGhlIHNoYXJwZXIgdmVyc2lvbiBpc24ndCBjb21pbmcuXG4gICAgICAgICAgICAgICAgICBjb25zdCBtc2cgPSBlcnI/LmVycm9yPy5tZXNzYWdlIHx8IGVycj8ubWVzc2FnZSB8fCBlcnI/LnN0YXR1c1RleHQgfHwgU3RyaW5nKGVycik7XG4gICAgICAgICAgICAgICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgICAgICAgICAgICAgIGtleTogJ2NlbnRlci10b2FzdCcsXG4gICAgICAgICAgICAgICAgICAgIHNldmVyaXR5OiAnd2FybicsXG4gICAgICAgICAgICAgICAgICAgIHN1bW1hcnk6ICdQcmV2aWV3IG5vdCBzaGFycGVuZWQnLFxuICAgICAgICAgICAgICAgICAgICBkZXRhaWw6IGBUaGUgZnVsbC1yZXNvbHV0aW9uIHByZXZpZXcgZGlkIG5vdCBsb2FkICgke21zZ30pLiBUaGUgbG93LXJlc29sdXRpb24gcHJldmlldyBpcyBzdGlsbCBzaG93bi4gVHJ5IGNsaWNraW5nIHRoZSBpbWFnZSBhZ2Fpbi5gLFxuICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICB0aGlzLnJ1bm5pbmcgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgIGFwcGx5Um9pKCk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfSkucmVuZGVyKGltZ0luZm8sIHNtYWxsSW1nSW5mbyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgZXJyb3I6IChlcnIpID0+IHtcbiAgICAgICAgY29uc3QgbXNnID0gZXJyPy5lcnJvcj8ubWVzc2FnZSB8fCBlcnI/Lm1lc3NhZ2UgfHwgZXJyPy5zdGF0dXNUZXh0IHx8IFN0cmluZyhlcnIpO1xuICAgICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBvY2N1cmVkIHdoZW4gZ2V0dGluZyBpbWFnZSBpbmZvJywgZXJyKTtcbiAgICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICAgIGtleTogJ2NlbnRlci10b2FzdCcsXG4gICAgICAgICAgc3RpY2t5OiB0cnVlLFxuICAgICAgICAgIHNldmVyaXR5OiAnZXJyb3InLFxuICAgICAgICAgIHN1bW1hcnk6ICdBbiBlcnJvciBvY2N1cmVkJyxcbiAgICAgICAgICBkZXRhaWw6IGBUaGUgZm9sbG93aW5nIGVycm9yIG9jY3VyZWQgd2hpbGUgZ2V0dGluZyBpbWFnZSBpbmZvOiAke21zZ30uXG4gICAgICAgICAgICAgICAgICAgUGxlYXNlIHRyeSB0byBvcGVuIHRoZSBpbWFnZSBhZ2FpbiB0aHJvdWdoIHRoZSBmaWxlIG5hdmlnYXRvci5gLFxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5zdGFja0xvYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgICAgICB0aGlzLnJ1bm5pbmcgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5wbG90U2VydmljZS5yZXNldCgpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpIHtcbiAgICB0aGlzLnBsb3RDb250ZXh0TWVudUxpc3RlbmVyID0gKGV2ZW50OiBNb3VzZUV2ZW50KSA9PiB7XG4gICAgICBjb25zdCBwbG90RWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXZOYW1lKTtcbiAgICAgIGlmICghcGxvdEVsPy5jb250YWlucyhldmVudC50YXJnZXQgYXMgTm9kZSkpIHJldHVybjtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICBldmVudC5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcbiAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICAgIHRoaXMuY29udGV4dE1lbnVJdGVtcyA9IHRoaXMuYnVpbGRDb250ZXh0TWVudUl0ZW1zKCk7XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgICAgdGhpcy5jb250ZXh0TWVudS5zaG93KGV2ZW50KTtcbiAgICAgIH0pO1xuICAgIH07XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ2NvbnRleHRtZW51JywgdGhpcy5wbG90Q29udGV4dE1lbnVMaXN0ZW5lciwgdHJ1ZSk7XG5cbiAgICB0aGlzLmtleWRvd25MaXN0ZW5lciA9IChldmVudDogS2V5Ym9hcmRFdmVudCkgPT4ge1xuICAgICAgY29uc3QgdGFyZ2V0ID0gZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50O1xuICAgICAgaWYgKHRhcmdldC50YWdOYW1lID09PSAnSU5QVVQnIHx8IHRhcmdldC50YWdOYW1lID09PSAnVEVYVEFSRUEnIHx8IHRhcmdldC5pc0NvbnRlbnRFZGl0YWJsZSkgcmV0dXJuO1xuICAgICAgLy8gU0FNIHBvaW50IG1vZGU6IEVudGVyIGNvbW1pdHMgdGhlIG9iamVjdCwgRXNjIGNsZWFycyB0aGUgcHJvbXB0LlxuICAgICAgaWYgKHRoaXMuYWN0aXZlRHJhZ01vZGUgPT09ICdzYW1Qb2ludCcgJiYgKGV2ZW50LmtleSA9PT0gJ0VudGVyJyB8fCBldmVudC5rZXkgPT09ICdFc2NhcGUnKSkge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICAgIGlmIChldmVudC5rZXkgPT09ICdFbnRlcicpIHRoaXMucGxvdFNlcnZpY2UuY29tbWl0U2FtUG9pbnRzKCk7XG4gICAgICAgICAgZWxzZSB0aGlzLnBsb3RTZXJ2aWNlLmNsZWFyU2FtUG9pbnRzKCk7XG4gICAgICAgICAgdGhpcy5oaWRlU2FtVG9hc3QoKTsgLy8gcHJvbXB0IHJlc29sdmVkIOKGkiBkaXNtaXNzIHRoZSBzdGF0dXMgdG9hc3RcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIC8vIFJlZ2lvbiBoaXN0b3J5IChqaXQtdWkjODUpOiBDdHJsL0NtZCtaIHVuZG9lczsgQ3RybC9DbWQrU2hpZnQrWiBvclxuICAgICAgLy8gQ3RybC9DbWQrWSByZWRvZXMuXG4gICAgICBpZiAoZXZlbnQuY3RybEtleSB8fCBldmVudC5tZXRhS2V5KSB7XG4gICAgICAgIGNvbnN0IGsgPSBldmVudC5rZXkudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgaWYgKGsgPT09ICd6JyAmJiAhZXZlbnQuc2hpZnRLZXkpIHtcbiAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnVuZG9SZWdpb24oKSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmICgoayA9PT0gJ3onICYmIGV2ZW50LnNoaWZ0S2V5KSB8fCBrID09PSAneScpIHtcbiAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnJlZG9SZWdpb24oKSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoZXZlbnQua2V5ID09PSAnRGVsZXRlJyB8fCBldmVudC5rZXkgPT09ICdCYWNrc3BhY2UnIHx8IGV2ZW50LmtleSA9PT0gJ2QnIHx8IGV2ZW50LmtleSA9PT0gJ0QnKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLmRlbGV0ZVJlZ2lvbigpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnKycgfHwgZXZlbnQua2V5ID09PSAnPScpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMuem9vbUluKCkpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICctJyB8fCBldmVudC5rZXkgPT09ICdfJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy56b29tT3V0KCkpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICdwJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgncGFuJykpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICdiJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnem9vbVRvQm94JykpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICdyJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd3JlY3QnKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ2YnKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdkcmF3Y2xvc2VkcGF0aCcpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAndycpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3dhbmQnKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ2UnKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdlcmFzZVZlcnRleCcpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAncycpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3NlbGVjdCcpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnbCcpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2RyYXdvcGVucGF0aCcpKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdrZXlkb3duJywgdGhpcy5rZXlkb3duTGlzdGVuZXIpO1xuXG4gICAgdGhpcy53aGVlbExpc3RlbmVyID0gKGV2ZW50OiBXaGVlbEV2ZW50KSA9PiB7XG4gICAgICBjb25zdCBwbG90RWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXZOYW1lKTtcbiAgICAgIGlmICghcGxvdEVsPy5jb250YWlucyhldmVudC50YXJnZXQgYXMgTm9kZSkpIHJldHVybjtcbiAgICAgIC8vIFRoZSBJbWFnZSB2aWV3IGhhbmRsZXMgc2Nyb2xsLXpvb20gbmF0aXZlbHkgKGl0IHJlc3BlY3RzIHRoZSBzY3JvbGxcbiAgICAgIC8vIGRlbHRhKS4gSW50ZXJjZXB0aW5nIGhlcmUgd291bGQgZmlyZSBhIGZpeGVkIHpvb20gc3RlcCBwZXIgd2hlZWxcbiAgICAgIC8vIGV2ZW50IOKAlCBmYXIgdG9vIHNlbnNpdGl2ZSDigJQgc28gbGV0IHRoZSByZW5kZXJlciBvd24gaXQuXG4gICAgICBpZiAodGhpcy5pc0ltYWdlVmlldykgcmV0dXJuO1xuICAgICAgLy8gM0QgcGxvdCB0eXBlcyAoc3VyZmFjZSwgc2NhdHRlcjNkLCBpc29zdXJmYWNlKSByZW5kZXIgaW4gYSBQbG90bHkgc2NlbmVcbiAgICAgIC8vIHRoYXQgb3JiaXRzL3pvb21zIG5hdGl2ZWx5IG9uIHNjcm9sbC4gVGhlIDJEIHN0ZXAtem9vbSBkb2Vzbid0IGFwcGx5IGFuZFxuICAgICAgLy8gd291bGQgdGhyb3cgKG5vIHhheGlzIG9uIGEgc2NlbmUpLCBzbyBsZXQgUGxvdGx5IGhhbmRsZSB0aGUgd2hlZWwuXG4gICAgICBpZiAoIXRoaXMuaXNIZWF0bWFwKSByZXR1cm47XG4gICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgZXZlbnQuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCk7XG4gICAgICBpZiAoZXZlbnQuZGVsdGFZIDwgMCkge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy56b29tSW4oKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmRlbHRhWSA+IDApIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMuem9vbU91dCgpKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCd3aGVlbCcsIHRoaXMud2hlZWxMaXN0ZW5lciwgeyBjYXB0dXJlOiB0cnVlLCBwYXNzaXZlOiBmYWxzZSB9KTtcblxuICAgIC8vIERyYWcgaGFuZGxpbmcgZm9yIHRoZSBmbG9hdGluZyBpbnRlbnNpdHktcHJvZmlsZSBwYW5lbC5cbiAgICB0aGlzLnByb2ZpbGVEcmFnTW92ZUxpc3RlbmVyID0gKGU6IE1vdXNlRXZlbnQpID0+IHtcbiAgICAgIGlmICh0aGlzLnByb2ZpbGVQYW5lbERyYWdnaW5nKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICAgICAgdGhpcy5wcm9maWxlUGFuZWxQb3MgPSB7XG4gICAgICAgICAgICB4OiB0aGlzLnByb2ZpbGVQYW5lbFN0YXJ0LnggKyAoZS5jbGllbnRYIC0gdGhpcy5wcm9maWxlUGFuZWxTdGFydC5teCksXG4gICAgICAgICAgICB5OiB0aGlzLnByb2ZpbGVQYW5lbFN0YXJ0LnkgKyAoZS5jbGllbnRZIC0gdGhpcy5wcm9maWxlUGFuZWxTdGFydC5teSksXG4gICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYgKHRoaXMudG9vbGJhckRyYWdnaW5nKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICAgICAgdGhpcy50b29sYmFyUG9zID0ge1xuICAgICAgICAgICAgeDogdGhpcy50b29sYmFyU3RhcnQueCArIChlLmNsaWVudFggLSB0aGlzLnRvb2xiYXJTdGFydC5teCksXG4gICAgICAgICAgICB5OiB0aGlzLnRvb2xiYXJTdGFydC55ICsgKGUuY2xpZW50WSAtIHRoaXMudG9vbGJhclN0YXJ0Lm15KSxcbiAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMucHJvZmlsZURyYWdVcExpc3RlbmVyID0gKCkgPT4ge1xuICAgICAgdGhpcy5wcm9maWxlUGFuZWxEcmFnZ2luZyA9IGZhbHNlO1xuICAgICAgdGhpcy50b29sYmFyRHJhZ2dpbmcgPSBmYWxzZTtcbiAgICB9O1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCB0aGlzLnByb2ZpbGVEcmFnTW92ZUxpc3RlbmVyKTtcbiAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsIHRoaXMucHJvZmlsZURyYWdVcExpc3RlbmVyKTtcblxuICAgIC8vIE9uIGFueSB3aW5kb3cgcmVzaXplLCByZWZsb3cgdGhlIGludGVuc2l0eSBpbnNldCB0byBpdHMgKGZpeGVkKSBwYW5lbCBzaXplXG4gICAgLy8gb24gdGhlIG5leHQgZnJhbWUsIG9uY2UgbGF5b3V0IGhhcyBzZXR0bGVkLiBXaXRob3V0IHRoaXMgdGhlIGluc2V0IGNhbiBiZVxuICAgIC8vIGxlZnQgYXQgYSBzdGFsZS96ZXJvIHNpemUgYnkgYSBtaWQtcmVmbG93IHJlc2l6ZSBhbmQgc3RvcCBzaG93aW5nLlxuICAgIHRoaXMucHJvZmlsZVJlc2l6ZUxpc3RlbmVyID0gKCkgPT4ge1xuICAgICAgaWYgKCF0aGlzLmhhc1Byb2ZpbGVzKSByZXR1cm47XG4gICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gdGhpcy5yZW5kZXJJbnRlbnNpdHlJbnNldCgpKTtcbiAgICB9O1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCB0aGlzLnByb2ZpbGVSZXNpemVMaXN0ZW5lcik7XG4gIH1cblxuICBvblByb2ZpbGVQYW5lbERyYWdTdGFydChlOiBNb3VzZUV2ZW50KSB7XG4gICAgdGhpcy5wcm9maWxlUGFuZWxEcmFnZ2luZyA9IHRydWU7XG4gICAgdGhpcy5wcm9maWxlUGFuZWxTdGFydCA9IHtcbiAgICAgIG14OiBlLmNsaWVudFgsXG4gICAgICBteTogZS5jbGllbnRZLFxuICAgICAgeDogdGhpcy5wcm9maWxlUGFuZWxQb3MueCxcbiAgICAgIHk6IHRoaXMucHJvZmlsZVBhbmVsUG9zLnksXG4gICAgfTtcbiAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gIH1cblxuICAvKiogR3JhYiB0aGUgdG9vbGJhciBoYW5kbGU6IGRldGFjaCBpdCBpbnRvIGEgZmxvYXRpbmcgd2luZG93IChpZiBzdGlsbCBkb2NrZWQpXG4gICAqICBhbmQgc3RhcnQgZHJhZ2dpbmcuICovXG4gIG9uVG9vbGJhckRyYWdTdGFydChlOiBNb3VzZUV2ZW50KSB7XG4gICAgaWYgKCF0aGlzLnRvb2xiYXJGbG9hdGluZykge1xuICAgICAgdGhpcy50b29sYmFyRmxvYXRpbmcgPSB0cnVlO1xuICAgICAgdGhpcy50b29sYmFyUG9zID0geyB4OiA4LCB5OiA4IH07XG4gICAgfVxuICAgIHRoaXMudG9vbGJhckRyYWdnaW5nID0gdHJ1ZTtcbiAgICB0aGlzLnRvb2xiYXJTdGFydCA9IHtcbiAgICAgIG14OiBlLmNsaWVudFgsXG4gICAgICBteTogZS5jbGllbnRZLFxuICAgICAgeDogdGhpcy50b29sYmFyUG9zLngsXG4gICAgICB5OiB0aGlzLnRvb2xiYXJQb3MueSxcbiAgICB9O1xuICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgfVxuXG4gIC8qKiBTbmFwIHRoZSBmbG9hdGluZyB0b29sYmFyIGJhY2sgdG8gaXRzIGRvY2tlZCBwb3NpdGlvbiBhY3Jvc3MgdGhlIHRvcC4gKi9cbiAgZG9ja1Rvb2xiYXIoKSB7XG4gICAgdGhpcy50b29sYmFyRmxvYXRpbmcgPSBmYWxzZTtcbiAgfVxuXG4gIC8qKiBSZW5kZXIgdGhlIGludGVuc2l0eSBpbnNldCBjaGFydCBmcm9tIHRoZSBsYXRlc3QgcHJvZmlsZSBkYXRhLiBUaGUgYWN0dWFsXG4gICAqICBjaGFydGluZyBpcyBvd25lZCBieSB0aGUgdmlzdWFsaXplciBzZXJ2aWNlIOKAlCB0aGUgY29tcG9uZW50IGp1c3QgZGVjaWRlc1xuICAgKiAgd2hlbiAocHJvZmlsZSBtb2RlICsgYSBmcmVzaCBwcm9maWxlKS4gKi9cbiAgcHJpdmF0ZSByZW5kZXJJbnRlbnNpdHlJbnNldCgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaGFzUHJvZmlsZXMpIHJldHVybjtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlbmRlckludGVuc2l0eUluc2V0KHRoaXMuaW50ZW5zaXR5SW5zZXREaXYsIHRoaXMubGF0ZXN0UHJvZmlsZXMpO1xuICB9XG5cbiAgLyoqIFRvb2xiYXIgXCJJbnRlbnNpdHlcIiBncm91cDogYWRkIGFub3RoZXIgbGluZSBST0kgKG5leHQgYnJpZ2h0IGNvbG91cikuICovXG4gIGFzeW5jIGFkZFByb2ZpbGVMaW5lKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIFBhcmsgdGhlIGZsb2F0aW5nIGluc2V0IG5lYXIgdGhlIHBsb3QncyB0b3AtcmlnaHQgd2hlbiB0aGUgZmlyc3QgbGluZSBpc1xuICAgIC8vIGFkZGVkIChpdCdzIHBvc2l0aW9uOmZpeGVkLCBzbyB1c2Ugdmlld3BvcnQgY29vcmRzIGZyb20gdGhlIHBsb3QgcmVjdCkuXG4gICAgaWYgKCF0aGlzLmhhc1Byb2ZpbGVzKSB7XG4gICAgICBjb25zdCByZWN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2TmFtZSk/LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgdGhpcy5wcm9maWxlUGFuZWxQb3MgPSByZWN0XG4gICAgICAgID8geyB4OiBNYXRoLm1heCgxMCwgcmVjdC5yaWdodCAtIDMwMCksIHk6IHJlY3QudG9wICsgMTAgfVxuICAgICAgICA6IHsgeDogMjAsIHk6IDcwIH07XG4gICAgfVxuICAgIC8vIEltYWdlIChPU0QpIG1vZGU6IFBsb3RseSBuZXZlciByZW5kZXJlZCwgc28gaXQgaGFzIG5vIHBpeGVsIGNhY2hlIC8gZXh0ZW50LlxuICAgIC8vIExvYWQgdGhlIGN1cnJlbnQgc2xpY2UncyBmcmFtZXMgZm9yIHNhbXBsaW5nICsgbGluZSBwbGFjZW1lbnQgZmlyc3QuXG4gICAgaWYgKHRoaXMuaXNJbWFnZVZpZXcgJiYgdGhpcy5pbWFnZUluZm8pIHtcbiAgICAgIGF3YWl0IHRoaXMucGxvdFNlcnZpY2UuZW5zdXJlSW50ZW5zaXR5U2FtcGxpbmcodGhpcy5pbWFnZUluZm8sIHRoaXMuekluZGV4KTtcbiAgICB9XG4gICAgY29uc3QgcmVnaW9uID0gdGhpcy5wbG90U2VydmljZS5nZXRJbnRlbnNpdHlDb250cm9scygpPy5hZGRQcm9maWxlTGluZSgpO1xuICAgIC8vIEF1dG8tc2VsZWN0IHRoZSBuZXcgbGluZSBvbiB0aGUgYWN0aXZlIGJhY2tlbmQgKFBsb3RseSBoYW5kbGVzIC8gT1NEXG4gICAgLy8gaGlnaGxpZ2h0KSBzbyBpdCdzIHJlYWR5IHRvIG1vdmUgb3IgZGVsZXRlIGltbWVkaWF0ZWx5LlxuICAgIGlmIChyZWdpb24pIHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2VsZWN0UmVnaW9uKHJlZ2lvbik7XG4gICAgICAvLyBPU0Qgb25seSBkcmF3cyBhIHNlbGVjdGVkIHJlZ2lvbidzIGhhbmRsZXMgaW4gYW4gZWRpdCBtb2RlLCBzbyBhIGxpbmUgaW5cbiAgICAgIC8vICdub25lJyBtb2RlIGxvb2tzIHVuc2VsZWN0ZWQuIFN3aXRjaCB0byAnc2VsZWN0JyBzbyB0aGUgbmV3IGxpbmUgc2hvd3NcbiAgICAgIC8vIGl0cyBlbmRwb2ludCBoYW5kbGVzIGFuZCBjYW4gYmUgZHJhZ2dlZC9kZWxldGVkIHJpZ2h0IGF3YXkuXG4gICAgICBpZiAodGhpcy5pc0ltYWdlVmlldyAmJiB0aGlzLmFjdGl2ZURyYWdNb2RlICE9PSAnc2VsZWN0Jykge1xuICAgICAgICB0aGlzLnRvZ2dsZURyYWdNb2RlKCdzZWxlY3QnKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLnNjcnViYmVyLmNhbmNlbCgpO1xuICAgIHRoaXMuc2FtUG9pbnRTdWIudW5zdWJzY3JpYmUoKTtcbiAgICBpZiAodGhpcy5wbG90Q29udGV4dE1lbnVMaXN0ZW5lcikge1xuICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2NvbnRleHRtZW51JywgdGhpcy5wbG90Q29udGV4dE1lbnVMaXN0ZW5lciwgdHJ1ZSk7XG4gICAgfVxuICAgIGlmICh0aGlzLmtleWRvd25MaXN0ZW5lcikge1xuICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLCB0aGlzLmtleWRvd25MaXN0ZW5lcik7XG4gICAgfVxuICAgIGlmICh0aGlzLndoZWVsTGlzdGVuZXIpIHtcbiAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCd3aGVlbCcsIHRoaXMud2hlZWxMaXN0ZW5lciwgdHJ1ZSk7XG4gICAgfVxuICAgIGlmICh0aGlzLnByb2ZpbGVEcmFnTW92ZUxpc3RlbmVyKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcignbW91c2Vtb3ZlJywgdGhpcy5wcm9maWxlRHJhZ01vdmVMaXN0ZW5lcik7XG4gICAgfVxuICAgIGlmICh0aGlzLnByb2ZpbGVEcmFnVXBMaXN0ZW5lcikge1xuICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNldXAnLCB0aGlzLnByb2ZpbGVEcmFnVXBMaXN0ZW5lcik7XG4gICAgfVxuICAgIGlmICh0aGlzLnByb2ZpbGVSZXNpemVMaXN0ZW5lcikge1xuICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsIHRoaXMucHJvZmlsZVJlc2l6ZUxpc3RlbmVyKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaW50ZW5zaXR5UHJvZmlsZVN1Yikge1xuICAgICAgdGhpcy5pbnRlbnNpdHlQcm9maWxlU3ViLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLnZpZXdwb3J0Q2hhbmdlU3ViKSB7XG4gICAgICB0aGlzLnZpZXdwb3J0Q2hhbmdlU3ViLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIHRoaXMudW5zdWIubmV4dCgpO1xuICAgIHRoaXMudW5zdWIuY29tcGxldGUoKTtcbiAgICBpZiAodGhpcy5wcmV2aWV3U3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLnByZXZpZXdTdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucGxvdFdpZHRoU3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLnBsb3RXaWR0aFN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5pbWFnZUxvYWRpbmdTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMuaW1hZ2VMb2FkaW5nU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmltZ0xvYWRpbmdNZXNzYWdlU3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLmltZ0xvYWRpbmdNZXNzYWdlU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmlzWm9vbVN1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5pc1pvb21TdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuYXV0b3NjYWxlU3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLmF1dG9zY2FsZVN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5maWxlbmFtZVN1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5maWxlbmFtZVN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnVuc3Vic2NyaWJlKCk7XG4gIH1cblxuICBwdWJsaWMgaGFzUmVnaW9ucygpOiBib29sZWFuIHtcbiAgICAvLyBVc2UgdGhlIGNvbnRyYWN0J3MgZnJhbWV3b3JrLW5ldXRyYWwgcmVnaW9uIGFjY2Vzc29yLCBub3QgdGhlIHJhd1xuICAgIC8vIFBsb3RseS1zaGFwZWQgZ2V0U2hhcGVzKCkg4oCUIHRoZSBjb21wb25lbnQgb25seSBuZWVkcyB0byBrbm93IHdoZXRoZXIgYW55XG4gICAgLy8gcmVnaW9uIGV4aXN0cywgYW5kIG11c3Qgbm90IGRlcGVuZCBvbiBhIGJhY2tlbmQncyB3aXJlIGZvcm1hdC5cbiAgICByZXR1cm4gdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25zKCkubGVuZ3RoID4gMDtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgaW4gbWFpbkZhY2FkZS5jb25maWdSZXF1ZXN0KCkgLSBETyBOT1QgREVMRVRFXG4gICAqL1xuICBwdWJsaWMgZ2V0UmVnaW9uUG9seWdvbnMoKTogUG9seWdvbltdIHtcbiAgICByZXR1cm4gdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25Qb2x5Z29ucygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlbGVjdCB0aGUgY29sb3Igc2NhbGUgdHlwZTogY2FsbGVkIGJ5IHRoZSBjb2xvciBzY2FsZSBzZWxlY3QgYnV0dG9uXG4gICAqIEBwYXJhbSBldmVudFxuICAgKi9cbiAgdG9nZ2xlUmV2ZXJzZVNjYWxlKCkge1xuICAgIHRoaXMucmV2ZXJzZXNjYWxlID0gIXRoaXMucmV2ZXJzZXNjYWxlO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UmV2ZXJzZVNjYWxlKHRoaXMucmV2ZXJzZXNjYWxlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZWxlY3QgYSBjb2xvciBtYXA6IGNhbGxlZCBieSB0aGUgY29sb3IgbWFwIGRyb3Bkb3duIGJ1dHRvblxuICAgKiBAcGFyYW0gY29sb3JtYXBOb2RlXG4gICAqL1xuICBzZWxlY3RDb2xvcm1hcChjb2xvcm1hcE5vZGU6IFRyZWVOb2RlKSB7XG4gICAgaWYgKCFjb2xvcm1hcE5vZGUuY2hpbGRyZW4pIHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0Q29sb3JtYXAoY29sb3JtYXBOb2RlKTtcbiAgICB9XG4gIH1cblxuICAvKiogT3BlbiB0aGUgQ2hhbm5lbHMgJiBIaXN0b2dyYW0gZGlhbG9nICh0b29sYmFyIGJ1dHRvbikuICovXG4gIG9wZW5DaGFubmVsSGlzdG9ncmFtKCkge1xuICAgIHRoaXMuc2hvd0NoYW5uZWxIaXN0b2dyYW0gPSB0cnVlO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIFJlZ2lvbiBFZGl0b3IgZGlhbG9nICh0b29sYmFyIGJ1dHRvbikuIFRoZSBlZGl0b3Igc3RheXMgbGlua2VkIHRvXG4gICAqICB0aGlzIHBsb3R0aW5nIGluc3RhbmNlIHRocm91Z2ggdGhlIHJvb3QgUmVnaW9uU3RvcmUgc2luZ2xldG9uLCBzbyBpdCB3b3Jrc1xuICAgKiAgdGhlIHNhbWUgYXMgdGhlIGZvcm1lciByaWdodC1wYW5lbCBSZWdpb25zIHRhYi4gKi9cbiAgb3BlblJlZ2lvbkVkaXRvcigpIHtcbiAgICAvLyBTaXplIHRoZSBkaWFsb2cgdG8gbWF0Y2ggdGhlIGhvc3QncyBjb25maWd1cmVkIGVsZW1lbnQgKGUuZy4gdGhlIGFwcCdzXG4gICAgLy8gcmlnaHQgcGFuZWwpIGF0IG9wZW4gdGltZS4gTWVhc3VyZWQgb25jZSDigJQgaXQgaW50ZW50aW9uYWxseSBkb2VzIG5vdFxuICAgIC8vIHRyYWNrIGxhdGVyIGJyb3dzZXIvc3BsaXQgcmVzaXplcy4gRmFsbHMgYmFjayB0byBhIHF1YXJ0ZXIgb2YgdGhlIHBhZ2VcbiAgICAvLyB3aGVuIG5vIHNlbGVjdG9yIGlzIGNvbmZpZ3VyZWQgb3IgdGhlIGVsZW1lbnQgaXNuJ3QgcHJlc2VudC5cbiAgICBjb25zdCBzZWxlY3RvciA9IHRoaXMudml6Q29uZmlnPy5yZWdpb25FZGl0b3JXaWR0aFNlbGVjdG9yO1xuICAgIGNvbnN0IHdpZHRoID0gc2VsZWN0b3JcbiAgICAgID8gZG9jdW1lbnQucXVlcnlTZWxlY3RvcjxIVE1MRWxlbWVudD4oc2VsZWN0b3IpPy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS53aWR0aFxuICAgICAgOiB1bmRlZmluZWQ7XG4gICAgdGhpcy5yZWdpb25FZGl0b3JXaWR0aCA9IHdpZHRoICYmIHdpZHRoID4gMCA/IGAke01hdGgucm91bmQod2lkdGgpfXB4YCA6ICcyNXZ3JztcbiAgICB0aGlzLnNob3dSZWdpb25FZGl0b3IgPSB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGxlZCBieSB0aGUgc3RhY2svc2luZ2xlIGltYWdlIGRyb3Bkb3duIGJ1dHRvblxuICAgKiBAcGFyYW0gc2hvd3N0YWNrXG4gICAqL1xuICBzZWxlY3RTdGFja09wdGlvbihzZWxlY3RlZFN0YWNrT3B0aW9uOiBhbnkpIHtcbiAgICBjb25zdCBzaG93c3RhY2sgPSBzZWxlY3RlZFN0YWNrT3B0aW9uLnZhbCA9PT0gJ3RydWUnO1xuICAgIHRoaXMuc2VsZWN0ZWRTdGFja09wdGlvbiA9IHNlbGVjdGVkU3RhY2tPcHRpb247XG4gICAgY29uc29sZS5sb2coJ3NlbGVjdGVkIHN0YWNrIG9wdGlvbicgKyBKU09OLnN0cmluZ2lmeSh0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24pKTtcbiAgICB0aGlzLnN0YWNrTG9hZGluZyA9IHNob3dzdGFjaztcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyghc2hvd3N0YWNrKTtcbiAgICAvLyBTdGFjayBtb2RlIGlzIGFsd2F5cyBhIDJEIGhlYXRtYXAg4oCUIHJlc2V0IHN1cmZhY2UgbW9kZSBpZiBhY3RpdmVcbiAgICBpZiAoIXRoaXMuaXNIZWF0bWFwKSB7XG4gICAgICB0aGlzLmlzSGVhdG1hcCA9IHRydWU7XG4gICAgICB0aGlzLnBsb3RUeXBlID0gUGxvdFR5cGUuSEVBVE1BUDtcbiAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9IFBsb3RUeXBlLkhFQVRNQVA7XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFBsb3RUeXBlKHRoaXMucGxvdFR5cGUpO1xuICAgIH1cbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNob3dTdGFjayhzaG93c3RhY2spO1xuICB9XG5cbiAgdXBkYXRlWkluZGV4KHVybHM/OiBzdHJpbmdbXSkge1xuICAgIGlmICh0aGlzLnpJbmRleCA+IHRoaXMubWF4SW5kZXgpIHtcbiAgICAgIHRoaXMuekluZGV4ID0gdGhpcy5tYXhJbmRleDtcbiAgICB9XG4gICAgaWYgKHRoaXMuekluZGV4IDwgMCkge1xuICAgICAgdGhpcy56SW5kZXggPSAwO1xuICAgIH1cbiAgICBpZiAodXJscykge1xuICAgICAgaWYgKHRoaXMuekluZGV4ID4gdXJscz8ubGVuZ3RoKSB7XG4gICAgICAgIHRoaXMuekluZGV4ID0gMDtcbiAgICAgICAgdGhpcy5tYXhJbmRleCA9IHVybHMubGVuZ3RoIC0gMTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRaSW5kZXgodGhpcy56SW5kZXgpO1xuICB9XG5cbiAgLyoqXG4gICAqIExpdmUgei1zbGljZSBzY3J1YiBmb3IgdGhlIEltYWdlIHZpZXc6IHRoZSByZW5kZXJlciBzd2FwcyB0aGUgZGlzcGxheWVkXG4gICAqIHNsaWNlIGluIHBsYWNlIChubyByZS1tb3VudCksIHNvIGRyYWdnaW5nIHRoZSBzbGlkZXIgc3RlcHMgdGhyb3VnaCB0aGVcbiAgICogc3RhY2sgdGhlIHNhbWUgd2F5IHRoZSBoZWF0bWFwIGZyYW1lIHNsaWRlciBkb2VzLlxuICAgKi9cbiAgb25aU2xpZGUoejogbnVtYmVyIHwgdW5kZWZpbmVkKSB7XG4gICAgaWYgKHogPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIHRoaXMuekluZGV4ID0gejtcbiAgICB0aGlzLnNjcnViYmVyLmNvbW1pdCh6KTtcbiAgICAvLyBLZWVwIHRoZSBpbnRlbnNpdHkgaW5zZXQgaW4gc3luYyB3aXRoIHRoZSBkaXNwbGF5ZWQgc2xpY2UgKEltYWdlL09TRCBtb2RlLFxuICAgIC8vIHdoZXJlIHRoZSBwcm9maWxlIHNhbXBsZXIgaXMgZmVkIGZyb20gdGhlIGxvYWRlZCBwcmV2aWV3IGZyYW1lcykuXG4gICAgaWYgKHRoaXMuaGFzUHJvZmlsZXMgJiYgdGhpcy5pc0ltYWdlVmlldyAmJiB0aGlzLmltYWdlSW5mbykge1xuICAgICAgdGhpcy5wbG90U2VydmljZS5lbnN1cmVJbnRlbnNpdHlTYW1wbGluZyh0aGlzLmltYWdlSW5mbywgeik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIExpdmUgKGRlYm91bmNlZCkgc2NydWIgd2hpbGUgZHJhZ2dpbmcgdGhlIEltYWdlLXZpZXcgei1zbGlkZXI6IHVwZGF0ZSB0aGVcbiAgICogc2xpY2UgYXMgdGhlIHVzZXIgZHJhZ3MsIGJ1dCBjb2FsZXNjZSByYXBpZCBjaGFuZ2VzIHNvIHdlIGRvbid0IGZpcmUgYVxuICAgKiBzbGljZSBzd2FwIG9uIGV2ZXJ5IHBpeGVsLiBUaGUgZmluYWwgdmFsdWUgYWxzbyBsYW5kcyB2aWEgb25aU2xpZGUgKG9uU2xpZGVFbmQpLlxuICAgKi9cbiAgb25aU2NydWIoejogbnVtYmVyIHwgdW5kZWZpbmVkKSB7XG4gICAgaWYgKHogPT09IHVuZGVmaW5lZCkgcmV0dXJuO1xuICAgIHRoaXMuekluZGV4ID0gejtcbiAgICB0aGlzLnNjcnViYmVyLnNjcnViKHopO1xuICB9XG5cbiAgLyoqXG4gICAqIEtleWJvYXJkIHN0YWNrIG5hdmlnYXRpb246IOKGkC/ihpIgc3RlcCB0aGUgei1zbGljZSBpbiB0aGUgSW1hZ2UgdmlldywgdGhlIHNhbWVcbiAgICogd2F5IHRoZSBzbGlkZXIgZG9lcy4gT25seSBhY3RpdmUgZm9yIGEgbG9hZGVkIHN0YWNrIGluIEltYWdlIHZpZXcsIGFuZFxuICAgKiBpZ25vcmVkIHdoaWxlIGEgZm9ybSBmaWVsZCBpcyBmb2N1c2VkIHNvIHR5cGluZyBpc24ndCBoaWphY2tlZC4gVXAvRG93biBhcmVcbiAgICogbGVmdCB0byBPcGVuU2VhZHJhZ29uIChwYW5uaW5nKS5cbiAgICovXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzprZXlkb3duJywgWyckZXZlbnQnXSlcbiAgb25LZXlkb3duKGU6IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoZS5rZXkgIT09ICdBcnJvd0xlZnQnICYmIGUua2V5ICE9PSAnQXJyb3dSaWdodCcpIHJldHVybjtcbiAgICBpZiAoIXRoaXMuaW1hZ2VJbmZvPy5pc1N0YWNrIHx8ICF0aGlzLmlzSW1hZ2VWaWV3KSByZXR1cm47XG4gICAgY29uc3QgdCA9IGUudGFyZ2V0IGFzIEhUTUxFbGVtZW50IHwgbnVsbDtcbiAgICBpZiAodCAmJiAoL14oSU5QVVR8VEVYVEFSRUF8U0VMRUNUKSQvLnRlc3QodC50YWdOYW1lKSB8fCB0LmlzQ29udGVudEVkaXRhYmxlKSkgcmV0dXJuO1xuICAgIC8vIFRoZSBzbGljZSBzbGlkZXIgaGFuZGxlcyBhcnJvd3MgbmF0aXZlbHkgd2hlbiBmb2N1c2VkIChhbHNvIGEgKzEgc3RlcCkg4oCUXG4gICAgLy8gc2tpcCBoZXJlIHNvIHdlIGRvbid0IGRvdWJsZS1zdGVwIGl0LlxuICAgIGlmICh0ICYmICh0LmdldEF0dHJpYnV0ZSgncm9sZScpID09PSAnc2xpZGVyJyB8fCB0LmNsb3Nlc3QoJy5wLXNsaWRlcicpKSkgcmV0dXJuO1xuICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICB0aGlzLnN0ZXBTbGljZShlLmtleSA9PT0gJ0Fycm93UmlnaHQnID8gMSA6IC0xKTtcbiAgfVxuXG4gIC8qKiBNb3ZlIHRoZSBkaXNwbGF5ZWQgc2xpY2UgYnkgYGRlbHRhYCwgY2xhbXBlZCB0byB0aGUgc3RhY2sgYm91bmRzLiAqL1xuICBzdGVwU2xpY2UoZGVsdGE6IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IG5leHQgPSBNYXRoLm1pbih0aGlzLm1heEluZGV4LCBNYXRoLm1heCgwLCB0aGlzLnpJbmRleCArIGRlbHRhKSk7XG4gICAgaWYgKG5leHQgPT09IHRoaXMuekluZGV4KSByZXR1cm47XG4gICAgdGhpcy5vblpTbGlkZShuZXh0KTtcbiAgfVxuXG4gIHJlbG9hZEFuZFBsb3QoKSB7XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcodHJ1ZSk7XG4gICAgLy8gUmUtZHJpdmUgdGhlIHJlbmRlciBwaXBlbGluZSBmcm9tIHRoZSBjdXJyZW50IGltYWdlIGluZm8gKHRoZSBzb3VyY2Ugb2ZcbiAgICAvLyB0cnV0aCwgd2l0aCByZWFsIHVybHMvZmlsZU5hbWUpLiBEb24ndCB1c2UgcGxvdFNlcnZpY2UucmVsb2FkQW5kUGxvdCgpOlxuICAgIC8vIHRoYXQgcmVidWlsZHMgdGhlIGluZm8gZnJvbSB0aGUgcmVuZGVyZXIncyBpbnRlcm5hbCBzdGF0ZSwgd2hpY2ggY2FuIGJlXG4gICAgLy8gc3RhbGUgKGUuZy4gYWZ0ZXIgc3dpdGNoaW5nIHBsb3QgdHlwZSBJbWFnZSAtPiBIZWF0bWFwKSBhbmQgcmUtZW1pdFxuICAgIC8vIGVtcHR5L29sZCB1cmxzLCBmYWlsaW5nIHRvIGhhbmQgdGhlIGRpdiBvdmVyIHRvIHRoZSBuZXcgcmVuZGVyZXIuXG4gICAgaWYgKHRoaXMuaW1hZ2VJbmZvKSB7XG4gICAgICB0aGlzLnN0YXRlLnNldEltYWdlSW5mbyh0aGlzLmltYWdlSW5mbyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVsb2FkQW5kUGxvdCgpO1xuICAgIH1cbiAgfVxuICBjYW5jZWxMb2FkaW5nKCkge1xuICAgIC8vIFN0b3AgYW55IGluLWZsaWdodCBmcmFtZSBzdHJlYW1pbmcgKG5hcGFyaS1qcyB2b2x1bWUvc3VyZmFjZSBwcmVsb2FkKSBzbyB0aGUgZmV0Y2ggbG9vcHNcbiAgICAvLyBhY3R1YWxseSBhYm9ydCDigJQgY2xlYXJpbmcgdGhlIGZsYWcgYWxvbmUgb25seSByb3V0ZWQgdG8gUGxvdGx5IGFuZCBsZWZ0IG5hcGFyaSBmZXRjaGluZy5cbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmNhbmNlbExvYWRpbmc/LigpO1xuICAgIHRoaXMuc3RhY2tMb2FkaW5nID0gZmFsc2U7XG4gICAgdGhpcy5pbWdMb2FkaW5nID0gZmFsc2U7XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgIHRoaXMuekluZGV4ID0gMDtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFpJbmRleCh0aGlzLnpJbmRleCk7XG4gICAgdGhpcy5ydW5uaW5nID0gZmFsc2U7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRTdGFja0xvYWRpbmcoZmFsc2UpO1xuICB9XG5cbiAgZG93bmxvYWRJbWFnZSgpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmRvd25sb2FkSW1hZ2UoKTtcbiAgfVxuXG4gIGF1dG9zY2FsZUltYWdlKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UuYXV0b3NjYWxlKCk7XG4gIH1cblxuICAvKiogVG9vbGJhciBwaXhlbC9zbW9vdGggdG9nZ2xlOiBmbGlwIHNtb290aGluZyBhbmQgYXBwbHkgdG8gdGhlIGFjdGl2ZSBiYWNrZW5kLiAqL1xuICBvblRvZ2dsZUltYWdlU21vb3RoaW5nKCk6IHZvaWQge1xuICAgIHRoaXMuaW1hZ2VTbW9vdGhpbmdFbmFibGVkID0gIXRoaXMuaW1hZ2VTbW9vdGhpbmdFbmFibGVkO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0SW1hZ2VTbW9vdGhpbmdFbmFibGVkKHRoaXMuaW1hZ2VTbW9vdGhpbmdFbmFibGVkKTtcbiAgfVxuXG4gIHJlc2V0QXhlcygpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlc2V0QXhlcygpO1xuICB9XG5cbiAgdG9nZ2xlRHJhZ01vZGUobW9kZTogc3RyaW5nKSB7XG4gICAgLy8gVG9nZ2xlIG9mZiBpZiB0aGUgc2FtZSBtb2RlIGlzIHJlLXNlbGVjdGVkLlxuICAgIHRoaXMuYWN0aXZlRHJhZ01vZGUgPSB0aGlzLmFjdGl2ZURyYWdNb2RlID09PSBtb2RlID8gbnVsbCA6IG1vZGU7XG4gICAgY29uc3QgYWN0aXZlID0gdGhpcy5hY3RpdmVEcmFnTW9kZTtcbiAgICAvLyBSZWNvcmQgdGhlIGFybWVkIHRvb2wgaW4gdGhlIHNoYXJlZCBzZXNzaW9uIHN0b3JlLlxuICAgIHRoaXMuc2Vzc2lvbi5zZXRBY3RpdmVUb29sKGFjdGl2ZSk7XG5cbiAgICAvLyBSZWdpb24gZHJhdy9zZWxlY3QgcnVuIHRocm91Z2ggdGhlIHJlbmRlcmVyJ3MgcmVnaW9uIG92ZXJsYXkuXG4gICAgdGhpcy5wbG90U2VydmljZVxuICAgICAgLmdldFJlZ2lvbk92ZXJsYXkoKVxuICAgICAgPy5zZXRNb2RlKHRoaXMuaXNSZWdpb25Nb2RlKGFjdGl2ZSkgPyAoYWN0aXZlIGFzIFJlZ2lvblRvb2xNb2RlKSA6ICdub25lJyk7XG5cbiAgICAvLyBWaWV3cG9ydCBkcmFnIG1vZGVzIChwYW4vYm94LXpvb20pLiBSZWdpb24gbW9kZXMgb3duIHRoZSBkcmFnIG1vZGUgdmlhXG4gICAgLy8gdGhlIG92ZXJsYXksIHNvIGRvbid0IGFsc28gc2V0IG9uZSBoZXJlLlxuICAgIGlmICghdGhpcy5pc1JlZ2lvbk1vZGUoYWN0aXZlKSkge1xuICAgICAgY29uc3Qgdmlld3BvcnREcmFnID0gYWN0aXZlID09PSAncGFuJyB8fCBhY3RpdmUgPT09ICd6b29tJztcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0RHJhZ01vZGUodmlld3BvcnREcmFnID8gYWN0aXZlIDogZmFsc2UpO1xuICAgIH1cblxuICAgIC8vIE9uLWNhbnZhcyB0b29sIG92ZXJsYXlzLlxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0Wm9vbVRvQm94TW9kZShhY3RpdmUgPT09ICd6b29tVG9Cb3gnKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFdhbmRNb2RlKGFjdGl2ZSA9PT0gJ3dhbmQnLCB7IHNlbnNpdGl2aXR5OiB0aGlzLndhbmRTZW5zaXRpdml0eSB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldEJydXNoTW9kZShhY3RpdmUgPT09ICdicnVzaCcsIHsgc2l6ZTogdGhpcy5icnVzaFNpemUgfSk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRTYW1Qb2ludE1vZGUoYWN0aXZlID09PSAnc2FtUG9pbnQnKTtcbiAgICAvLyBMZWF2aW5nIHBvaW50IG1vZGUgZGlzbWlzc2VzIGFueSBsaW5nZXJpbmcgc3RhdHVzIHRvYXN0LlxuICAgIGlmIChhY3RpdmUgIT09ICdzYW1Qb2ludCcpIHRoaXMuaGlkZVNhbVRvYXN0KCk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRWZXJ0ZXhFcmFzZXJNb2RlKGFjdGl2ZSA9PT0gJ2VyYXNlVmVydGV4Jyk7XG4gICAgaWYgKGFjdGl2ZSA9PT0gJ2VyYXNlVmVydGV4Jykge1xuICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRWZXJ0ZXhFcmFzZXJSYWRpdXModGhpcy52ZXJ0ZXhFcmFzZXJSYWRpdXMpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBSZWdpb24gZHJhdy9zZWxlY3QvZWRpdCBtb2RlcyByb3V0ZWQgdGhyb3VnaCB0aGUgcmVnaW9uIG92ZXJsYXkuIFRoZVxuICAgKiAgVGhlIHZlcnRleCB0b29scyAoZHJhd3BvbHlnb24vYWRkcG9pbnQvZGVsZXRlcG9pbnQvbW92ZSkgYXJlXG4gICAqICBoYW5kbGVkIGJ5IHRoZSBPcGVuU2VhZHJhZ29uIG92ZXJsYXk7IFBsb3RseSdzIG92ZXJsYXkgbWFwcyB0aGVtIHRvIG5vLW9wLiAqL1xuICBwcml2YXRlIGlzUmVnaW9uTW9kZShtb2RlOiBzdHJpbmcgfCBudWxsKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChcbiAgICAgIG1vZGUgPT09ICdkcmF3cmVjdCcgfHxcbiAgICAgIG1vZGUgPT09ICdkcmF3Y2xvc2VkcGF0aCcgfHxcbiAgICAgIG1vZGUgPT09ICdkcmF3b3BlbnBhdGgnIHx8XG4gICAgICBtb2RlID09PSAnc2VsZWN0JyB8fFxuICAgICAgbW9kZSA9PT0gJ2RyYXdwb2x5Z29uJyB8fFxuICAgICAgbW9kZSA9PT0gJ2FkZHBvaW50JyB8fFxuICAgICAgbW9kZSA9PT0gJ2RlbGV0ZXBvaW50JyB8fFxuICAgICAgbW9kZSA9PT0gJ21vdmUnXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBMaXZlLXVwZGF0ZSB0aGUgaXNvc3VyZmFjZSBhcyB0aGUgcmFuZ2Ugc2xpZGVyIG1vdmVzLiBUaGUgY29udHJvbCBpcyBvbmx5XG4gICAqICBhdmFpbGFibGUgd2hlbiB0aGUgYWN0aXZlIGJhY2tlbmQgcmVuZGVycyBpc29zdXJmYWNlcyAoSVNPU1VSRkFDRSBtb2RlKS4gKi9cbiAgb25Jc29SYW5nZUNoYW5nZSh2YWx1ZXM6IG51bWJlcltdIHwgdW5kZWZpbmVkKSB7XG4gICAgaWYgKCF2YWx1ZXMgfHwgdmFsdWVzLmxlbmd0aCA8IDIpIHJldHVybjtcbiAgICB0aGlzLmlzb1JhbmdlID0gdmFsdWVzO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0SXNvc3VyZmFjZUNvbnRyb2xzKCk/LnNldElzb1JhbmdlKHZhbHVlc1swXSwgdmFsdWVzWzFdKTtcbiAgfVxuXG4gIG9uV2FuZFNlbnNpdGl2aXR5Q2hhbmdlKHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCB8fCAhTnVtYmVyLmlzRmluaXRlKHZhbHVlKSkgcmV0dXJuO1xuICAgIHRoaXMud2FuZFNlbnNpdGl2aXR5ID0gdmFsdWU7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRXYW5kT3B0aW9ucyh7IHNlbnNpdGl2aXR5OiB2YWx1ZSB9KTtcbiAgfVxuXG4gIG9uQnJ1c2hTaXplQ2hhbmdlKHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCB8fCAhTnVtYmVyLmlzRmluaXRlKHZhbHVlKSkgcmV0dXJuO1xuICAgIHRoaXMuYnJ1c2hTaXplID0gdmFsdWU7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRCcnVzaE9wdGlvbnMoeyBzaXplOiB2YWx1ZSB9KTtcbiAgfVxuXG4gIG9uVmVydGV4RXJhc2VyUmFkaXVzQ2hhbmdlKHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCB8fCAhTnVtYmVyLmlzRmluaXRlKHZhbHVlKSkgcmV0dXJuO1xuICAgIHRoaXMudmVydGV4RXJhc2VyUmFkaXVzID0gdmFsdWU7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRWZXJ0ZXhFcmFzZXJSYWRpdXModmFsdWUpO1xuICB9XG5cbiAgem9vbUluKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuem9vbUluKCk7XG4gIH1cblxuICB6b29tT3V0KCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuem9vbU91dCgpO1xuICB9XG5cbiAgZGVsZXRlUmVnaW9uKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZGVsZXRlQWN0aXZlU2hhcGUoKTtcbiAgfVxuXG4gIC8qKiBVbmRvIHRoZSBtb3N0IHJlY2VudCByZWdpb24gYWN0aW9uIChqaXQtdWkjODUpLiBVcCB0byAxMCBzdGVwcyBiYWNrLiAqL1xuICB1bmRvUmVnaW9uKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UudW5kbygpO1xuICB9XG5cbiAgLyoqIFJlZG8gdGhlIG1vc3QgcmVjZW50bHkgdW5kb25lIHJlZ2lvbiBhY3Rpb24gKGppdC11aSM4NSkuICovXG4gIHJlZG9SZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5yZWRvKCk7XG4gIH1cblxuICAvLyDilIDilIAgUmVnaW9uIHNldC1vcGVyYXRpb25zIG9uIHRoZSBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzg1KSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKiogVGhlIGN1cnJlbnRseS1zZWxlY3RlZCByZWdpb25zIChsaXZlIHN0b3JlIGluc3RhbmNlcykuICovXG4gIHByaXZhdGUgZ2V0IHNlbGVjdGVkUmVnaW9ucygpOiBSZWdpb25bXSB7XG4gICAgY29uc3QgYWxsID0gdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25zKCk7XG4gICAgcmV0dXJuIHRoaXMuc2VsZWN0ZWRJbmRpY2VzXG4gICAgICAubWFwKChpKSA9PiBhbGxbaV0pXG4gICAgICAuZmlsdGVyKChyKTogciBpcyBSZWdpb24gPT4gISFyKTtcbiAgfVxuXG4gIC8qKiBSZWdpb25zIGVsaWdpYmxlIGZvciBzZXQtb3BzOiBjbG9zZWQgYXJlYXMgKHJlY3QgLyBjbG9zZWQgcG9seWdvbiAvXG4gICAqICBtdWx0aS1wb2x5Z29uKSwgZXhjbHVkaW5nIGludGVuc2l0eS1wcm9maWxlIGxpbmVzLiAqL1xuICBwcml2YXRlIG9wRWxpZ2libGUocmVnaW9uczogUmVnaW9uW10pOiBSZWdpb25bXSB7XG4gICAgcmV0dXJuIHJlZ2lvbnMuZmlsdGVyKChyKSA9PiByLmtpbmQgIT09ICdwcm9maWxlJyAmJiAoXG4gICAgICByLmJvdW5kcyBpbnN0YW5jZW9mIFJlY3RhbmdsZSB8fFxuICAgICAgci5ib3VuZHMgaW5zdGFuY2VvZiBNdWx0aVBvbHlnb24gfHxcbiAgICAgIChyLmJvdW5kcyBpbnN0YW5jZW9mIFBvbHlnb24gJiYgci5ib3VuZHMuY2xvc2VkICE9PSBmYWxzZSlcbiAgICApKTtcbiAgfVxuXG4gIC8qKiBJbWFnZSBwaXhlbCBkaW1lbnNpb25zIGZvciB0aGUgcmFzdGVyIG9wczsgZmFsbHMgYmFjayB0byB0aGUgc2VsZWN0aW9uJ3NcbiAgICogIGV4dGVudCB3aGVuIHRoZSBpbWFnZSBzaXplIGlzbid0IGtub3duIChrZWVwcyBjbGFtcGluZyBzYW5lKS4gKi9cbiAgcHJpdmF0ZSBvcEltYWdlRGltcyhyZWdpb25zOiBSZWdpb25bXSk6IHsgdzogbnVtYmVyOyBoOiBudW1iZXIgfSB7XG4gICAgbGV0IFt3LCBoXSA9IHRoaXMuaW1hZ2VJbmZvPy50cnVlSW1hZ2VTaXplID8/IFswLCAwXTtcbiAgICBpZiAoISh3ID4gMCkgfHwgIShoID4gMCkpIHtcbiAgICAgIGxldCBtYXhYID0gMCwgbWF4WSA9IDA7XG4gICAgICBjb25zdCBzY2FuID0gKHhzOiBudW1iZXJbXSwgeXM6IG51bWJlcltdKSA9PiB7XG4gICAgICAgIGZvciAoY29uc3QgeCBvZiB4cykgbWF4WCA9IE1hdGgubWF4KG1heFgsIHgpO1xuICAgICAgICBmb3IgKGNvbnN0IHkgb2YgeXMpIG1heFkgPSBNYXRoLm1heChtYXhZLCB5KTtcbiAgICAgIH07XG4gICAgICBmb3IgKGNvbnN0IHIgb2YgcmVnaW9ucykge1xuICAgICAgICBjb25zdCBiID0gci5ib3VuZHM7XG4gICAgICAgIGlmIChiIGluc3RhbmNlb2YgUmVjdGFuZ2xlKSBzY2FuKFtiLnggKyBiLndpZHRoXSwgW2IueSArIGIuaGVpZ2h0XSk7XG4gICAgICAgIGVsc2UgaWYgKGIgaW5zdGFuY2VvZiBQb2x5Z29uKSBzY2FuKGIueHBvaW50cywgYi55cG9pbnRzKTtcbiAgICAgICAgZWxzZSBpZiAoYiBpbnN0YW5jZW9mIE11bHRpUG9seWdvbikgZm9yIChjb25zdCBwIG9mIGIucG9seWdvbnMpIHNjYW4ocC54cG9pbnRzLCBwLnlwb2ludHMpO1xuICAgICAgfVxuICAgICAgdyA9IE1hdGgubWF4KHcsIE1hdGguY2VpbChtYXhYKSArIDIpO1xuICAgICAgaCA9IE1hdGgubWF4KGgsIE1hdGguY2VpbChtYXhZKSArIDIpO1xuICAgIH1cbiAgICByZXR1cm4geyB3LCBoIH07XG4gIH1cblxuICAvKiogVHJ1ZSB3aGVuIOKJpTIgZWxpZ2libGUgcmVnaW9ucyBhcmUgc2VsZWN0ZWQgKE1lcmdlIGF2YWlsYWJsZSkuICovXG4gIGdldCBjYW5NZXJnZVJlZ2lvbnMoKTogYm9vbGVhbiB7IHJldHVybiB0aGlzLm9wRWxpZ2libGUodGhpcy5zZWxlY3RlZFJlZ2lvbnMpLmxlbmd0aCA+PSAyOyB9XG4gIC8qKiBUcnVlIHdoZW4gYW55IHNlbGVjdGVkIHJlZ2lvbiBpcyBtdWx0aS1wYXJ0IChVbmdyb3VwIGF2YWlsYWJsZSkuICovXG4gIGdldCBjYW5Vbmdyb3VwUmVnaW9ucygpOiBib29sZWFuIHsgcmV0dXJuIHRoaXMuc2VsZWN0ZWRSZWdpb25zLnNvbWUoKHIpID0+IHRoaXMucmVnaW9uT3BzLmNhblVuZ3JvdXAocikpOyB9XG4gIC8qKiBUcnVlIHdoZW4g4omlMSBlbGlnaWJsZSByZWdpb24gaXMgc2VsZWN0ZWQgKEludmVyc2UgLyBTaW1wbGlmeSBhdmFpbGFibGUpLiAqL1xuICBnZXQgaGFzRWxpZ2libGVTZWxlY3Rpb24oKTogYm9vbGVhbiB7IHJldHVybiB0aGlzLm9wRWxpZ2libGUodGhpcy5zZWxlY3RlZFJlZ2lvbnMpLmxlbmd0aCA+PSAxOyB9XG5cbiAgLyoqIFNlbGVjdCBldmVyeSByZWdpb24gb24gdGhlIGltYWdlIChleGNsdWRlcyBpbnRlbnNpdHktcHJvZmlsZSBsaW5lcykuICovXG4gIHNlbGVjdEFsbFJlZ2lvbnMoKTogdm9pZCB7XG4gICAgY29uc3QgYWxsID0gdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25zKCk7XG4gICAgY29uc3QgaW5kaWNlczogbnVtYmVyW10gPSBbXTtcbiAgICBhbGwuZm9yRWFjaCgociwgaSkgPT4geyBpZiAoci5raW5kICE9PSAncHJvZmlsZScpIGluZGljZXMucHVzaChpKTsgfSk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyhpbmRpY2VzKTtcbiAgfVxuXG4gIC8qKiBNZXJnZSB0aGUgc2VsZWN0ZWQgcmVnaW9ucyBpbnRvIGEgc2luZ2xlIChwb3NzaWJseSBtdWx0aS1wYXJ0KSByZWdpb24uICovXG4gIG1lcmdlUmVnaW9ucygpOiB2b2lkIHtcbiAgICBjb25zdCBzZWwgPSB0aGlzLm9wRWxpZ2libGUodGhpcy5zZWxlY3RlZFJlZ2lvbnMpO1xuICAgIGlmIChzZWwubGVuZ3RoIDwgMikgcmV0dXJuO1xuICAgIGNvbnN0IHsgdywgaCB9ID0gdGhpcy5vcEltYWdlRGltcyhzZWwpO1xuICAgIGNvbnN0IG1lcmdlZCA9IHRoaXMucmVnaW9uT3BzLm1lcmdlKHNlbCwgdywgaCk7XG4gICAgaWYgKG1lcmdlZCkgdGhpcy5yZXBsYWNlUmVnaW9ucyhzZWwsIFttZXJnZWRdKTtcbiAgfVxuXG4gIC8qKiBTcGxpdCBlYWNoIHNlbGVjdGVkIG11bHRpLXBhcnQgcmVnaW9uIGludG8gb25lIHJlZ2lvbiBwZXIgcGFydC4gKi9cbiAgdW5ncm91cFJlZ2lvbnMoKTogdm9pZCB7XG4gICAgY29uc3Qgc2VsID0gdGhpcy5zZWxlY3RlZFJlZ2lvbnMuZmlsdGVyKChyKSA9PiB0aGlzLnJlZ2lvbk9wcy5jYW5Vbmdyb3VwKHIpKTtcbiAgICBpZiAoc2VsLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuICAgIGNvbnN0IHJlc3VsdHM6IFJlZ2lvbltdID0gW107XG4gICAgZm9yIChjb25zdCByIG9mIHNlbCkgcmVzdWx0cy5wdXNoKC4uLnRoaXMucmVnaW9uT3BzLnVuZ3JvdXAocikpO1xuICAgIHRoaXMucmVwbGFjZVJlZ2lvbnMoc2VsLCByZXN1bHRzKTtcbiAgfVxuXG4gIC8qKiBSZXBsYWNlIHRoZSBzZWxlY3Rpb24gd2l0aCBpdHMgaW52ZXJzZSBpbnNpZGUgdGhlIGltYWdlIHJlY3RhbmdsZS4gKi9cbiAgaW52ZXJzZVJlZ2lvbnMoKTogdm9pZCB7XG4gICAgY29uc3Qgc2VsID0gdGhpcy5vcEVsaWdpYmxlKHRoaXMuc2VsZWN0ZWRSZWdpb25zKTtcbiAgICBpZiAoc2VsLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuICAgIGNvbnN0IHsgdywgaCB9ID0gdGhpcy5vcEltYWdlRGltcyhzZWwpO1xuICAgIGNvbnN0IGludiA9IHRoaXMucmVnaW9uT3BzLmludmVyc2Uoc2VsLCB3LCBoKTtcbiAgICBpZiAoIWludikge1xuICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICBzZXZlcml0eTogJ3dhcm4nLCBzdW1tYXJ5OiAnSW52ZXJzZScsXG4gICAgICAgIGRldGFpbDogJ05vdGhpbmcgdG8gaW52ZXJ0IOKAlCBzZWxlY3Qgb25lIG9yIG1vcmUgY2xvc2VkIHJlZ2lvbnMgZmlyc3QuJyxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLnJlcGxhY2VSZWdpb25zKHNlbCwgW2ludl0pO1xuICB9XG5cbiAgLyoqIERvdWdsYXPigJNQZXVja2VyIHNpbXBsaWZ5IGVhY2ggc2VsZWN0ZWQgcmVnaW9uIGJ5IGB0aHJlc2hvbGRQeGAuICovXG4gIHNpbXBsaWZ5UmVnaW9ucyh0aHJlc2hvbGRQeDogbnVtYmVyKTogdm9pZCB7XG4gICAgY29uc3Qgc2VsID0gdGhpcy5vcEVsaWdpYmxlKHRoaXMuc2VsZWN0ZWRSZWdpb25zKTtcbiAgICBpZiAoc2VsLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuICAgIHRoaXMucmVwbGFjZVJlZ2lvbnMoc2VsLCBzZWwubWFwKChyKSA9PiB0aGlzLnJlZ2lvbk9wcy5zaW1wbGlmeShyLCB0aHJlc2hvbGRQeCkpKTtcbiAgICB0aGlzLmRpc3BsYXlTaW1wbGlmeURpYWxvZyA9IGZhbHNlO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIGN1c3RvbS10aHJlc2hvbGQgc2ltcGxpZnkgZGlhbG9nLiAqL1xuICBvcGVuU2ltcGxpZnlEaWFsb2coKTogdm9pZCB7IHRoaXMuZGlzcGxheVNpbXBsaWZ5RGlhbG9nID0gdHJ1ZTsgfVxuXG4gIC8qKlxuICAgKiBDb21taXQgYSBzZXQtb3A6IGRyb3AgdGhlIGByZW1vdmVgIHJlZ2lvbnMsIGFwcGVuZCBgYWRkYCwgYW5kIHNlbGVjdCB0aGVcbiAgICogcmVzdWx0cy4gR29lcyB0aHJvdWdoIHNldFJlZ2lvbnMgc28gaXQncyB1bmRvLXRyYWNrZWQgYW5kIHJlLXJlbmRlcmVkO1xuICAgKiBwcm9maWxlIGxpbmVzIGFuZCB1bnNlbGVjdGVkIHJlZ2lvbnMgYXJlIHByZXNlcnZlZC5cbiAgICovXG4gIHByaXZhdGUgcmVwbGFjZVJlZ2lvbnMocmVtb3ZlOiBSZWdpb25bXSwgYWRkOiBSZWdpb25bXSk6IHZvaWQge1xuICAgIGlmIChhZGQubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgY29uc3QgcmVtb3ZlU2V0ID0gbmV3IFNldChyZW1vdmUpO1xuICAgIGNvbnN0IGtlcHQgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKS5maWx0ZXIoKHIpID0+ICFyZW1vdmVTZXQuaGFzKHIpKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFJlZ2lvbnMoWy4uLmtlcHQsIC4uLmFkZF0pOyAvLyBtaW50cyBpZHMgb24gYGFkZGAsIHJlY29yZHMgdW5kb1xuICAgIGNvbnN0IHN0b3JlZCA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0UmVnaW9ucygpO1xuICAgIGNvbnN0IHNlbCA9IGFkZC5tYXAoKHIpID0+IHN0b3JlZC5pbmRleE9mKHIpKS5maWx0ZXIoKGkpID0+IGkgPj0gMCk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyhzZWwpO1xuICB9XG5cbiAgLyoqIEJveC1wcm9tcHRlZCBTQU0gc2VnbWVudGF0aW9uIG9mIHRoZSBkcmF3biByZWN0YW5nbGVzIChqaXQtdWkjOTApLiBBIHN0aWNreVxuICAgKiAgYHNhbWAgdG9hc3Qgc2hvd3MgbGl2ZSBzdGF0dXMgKyBhIGRvd25sb2FkIHByb2dyZXNzIGJhciAoZmlyc3QgcnVuIHB1bGxzIHRoZVxuICAgKiAgZW5jb2RlciwgfjE3MCBNQik7IGl0IHN0YXlzIG9wZW4gdW50aWwgdGhlIHJ1biBmaW5pc2hlcyAoYmFyIGhpdHMgMTAwJSkuICovXG4gIGFzeW5jIHNlZ21lbnRSZWdpb25zKCkge1xuICAgIGF3YWl0IHRoaXMucnVuU2VnbWVudFdpdGhUb2FzdCgnU0FNJywgdGhpcy5zYW1Ub29sLCAoKSA9PiB0aGlzLnBsb3RTZXJ2aWNlLnNlZ21lbnRSZWN0YW5nbGVzKCkpO1xuICB9XG5cbiAgLyoqIEF1dG8tc2VnbWVudCBjZWxscyBpbnNpZGUgZWFjaCBkcmF3biByZWN0YW5nbGUgd2l0aCBjZWxscG9zZS1TQU0sIGNsaWVudC1zaWRlXG4gICAqICAoaml0LXVpIzkwKS4gRWFjaCBib3ggaXMgY3JvcHBlZCAoYnJvd3NlciBzbGlkZS1jcm9wKSB0aGVuIHJ1biB0aHJvdWdoIHRoZVxuICAgKiAgY2VsbHBvc2UtanMgbW9kZWw7IHRoZSBzYW1lIHN0aWNreSBgc2FtYCB0b2FzdCArIHByb2dyZXNzIGJhciBpcyByZXVzZWQuICovXG4gIGFzeW5jIHNlZ21lbnRDZWxscG9zZSgpIHtcbiAgICBhd2FpdCB0aGlzLnJ1blNlZ21lbnRXaXRoVG9hc3QoJ0NlbGxwb3NlJywgdGhpcy5jZWxsU2VnbWVudFRvb2wsICgpID0+XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNlZ21lbnRSZWN0YW5nbGVzQ2VsbHBvc2UoKSxcbiAgICApO1xuICB9XG5cbiAgLyoqIFNoYXJlZCBkcml2ZXIgZm9yIHRoZSBib3gtcHJvbXB0IHNlZ21lbnQgdG9vbHM6IHdpcmVzIHRoZSB0b29sJ3Mgc3RhdHVzICtcbiAgICogIGRvd25sb2FkIHByb2dyZXNzIGludG8gdGhlIHN0aWNreSBgc2FtYCB0b2FzdCwgcnVucyBgb3BgLCBhbmQgcmVwb3J0cyB0aGVcbiAgICogIHJlZ2lvbiBjb3VudC4gS2VlcHMgdGhlIHRvYXN0IG9wZW4gdW50aWwgdGhlIHJ1biBzZXR0bGVzIChiYXIgaGl0cyAxMDAlKS4gKi9cbiAgcHJpdmF0ZSBhc3luYyBydW5TZWdtZW50V2l0aFRvYXN0KFxuICAgIGxhYmVsOiBzdHJpbmcsXG4gICAgdG9vbDogeyBzdGF0dXMkOiBCZWhhdmlvclN1YmplY3Q8c3RyaW5nPjsgcHJvZ3Jlc3MkOiBCZWhhdmlvclN1YmplY3Q8bnVtYmVyPiB9LFxuICAgIG9wOiAoKSA9PiBQcm9taXNlPG51bWJlcj4sXG4gICkge1xuICAgIHRoaXMuc2FtU3RhdHVzID0gJ1N0YXJ0aW5n4oCmJztcbiAgICB0aGlzLnNhbVByb2dyZXNzID0gMDtcbiAgICB0aGlzLnNhbURvd25sb2FkaW5nID0gZmFsc2U7XG4gICAgdGhpcy5zYW1CdXN5ID0gdHJ1ZTtcbiAgICBjb25zdCBwc3ViID0gdG9vbC5wcm9ncmVzcyQuc3Vic2NyaWJlKChmKSA9PiB7XG4gICAgICB0aGlzLnNhbURvd25sb2FkaW5nID0gZiA+PSAwICYmIGYgPCAxO1xuICAgICAgaWYgKGYgPj0gMCkgdGhpcy5zYW1Qcm9ncmVzcyA9IE1hdGgubWluKDEwMCwgTWF0aC5yb3VuZChmICogMTAwKSk7XG4gICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgfSk7XG4gICAgY29uc3Qgc3N1YiA9IHRvb2wuc3RhdHVzJC5zdWJzY3JpYmUoKG0pID0+IHtcbiAgICAgIGlmIChtKSB7XG4gICAgICAgIHRoaXMuc2FtU3RhdHVzID0gbTtcbiAgICAgICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMuc2hvd1NhbVRvYXN0KGxhYmVsKTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgbiA9IGF3YWl0IG9wKCk7XG4gICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgIHNldmVyaXR5OiBuID4gMCA/ICdzdWNjZXNzJyA6ICd3YXJuJyxcbiAgICAgICAgc3VtbWFyeTogbGFiZWwsXG4gICAgICAgIGRldGFpbDogdG9vbC5zdGF0dXMkLnZhbHVlIHx8IChuID4gMCA/IGBBZGRlZCAke259IHJlZ2lvbihzKS5gIDogJ05vIHJlZ2lvbnMgYWRkZWQuJyksXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7IHNldmVyaXR5OiAnZXJyb3InLCBzdW1tYXJ5OiBgJHtsYWJlbH0gZmFpbGVkYCwgZGV0YWlsOiBTdHJpbmcoZSkgfSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHBzdWIudW5zdWJzY3JpYmUoKTtcbiAgICAgIHNzdWIudW5zdWJzY3JpYmUoKTtcbiAgICAgIHRoaXMuaGlkZVNhbVRvYXN0KCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFNob3cgdGhlIHNoYXJlZCBzdGlja3kgYHNhbWAgdG9hc3Qgb25jZSAoaWRlbXBvdGVudCDigJQgcmUtYWRkaW5nIHdvdWxkIHN0YWNrXG4gICAqICBhIG5ldyB0b2FzdCBvbiBldmVyeSBwb2ludCBjbGljaykuICovXG4gIHByaXZhdGUgc2hvd1NhbVRvYXN0KHN1bW1hcnk6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNhbVRvYXN0U2hvd24pIHJldHVybjtcbiAgICB0aGlzLnNhbVRvYXN0U2hvd24gPSB0cnVlO1xuICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHsga2V5OiB0aGlzLnNhbVRvYXN0S2V5LCBzdGlja3k6IHRydWUsIHNldmVyaXR5OiAnaW5mbycsIHN1bW1hcnkgfSk7XG4gIH1cblxuICAvKiogRGlzbWlzcyB0aGUgc2hhcmVkIGBzYW1gIHRvYXN0IGFuZCByZXNldCBpdHMgcHJvZ3Jlc3Mvc3Bpbm5lciBzdGF0ZS4gKi9cbiAgcHJpdmF0ZSBoaWRlU2FtVG9hc3QoKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Ub2FzdFNob3duID0gZmFsc2U7XG4gICAgdGhpcy5zYW1CdXN5ID0gZmFsc2U7XG4gICAgdGhpcy5zYW1Eb3dubG9hZGluZyA9IGZhbHNlO1xuICAgIHRoaXMuc2FtUHJvZ3Jlc3MgPSAwO1xuICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuY2xlYXIodGhpcy5zYW1Ub2FzdEtleSk7XG4gICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICB9XG5cbiAgLyoqIFBpY2sgdGhlIFNBTSBtb2RlbCB0aGUgc2VnbWVudCB0b29scyB1c2UgKGppdC11aSM5MCBQMSkuICovXG4gIG9uU2FtTW9kZWxDaGFuZ2UoaWQ6IHN0cmluZykge1xuICAgIHRoaXMuc2FtTW9kZWxJZCA9IGlkO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U2FtTW9kZWwoaWQpO1xuICB9XG5cbiAgLyoqIENvbnZlcnQgdGhlIHNlbGVjdGVkIHJlZ2lvbiB0byBhIHNtb290aCBiZXppZXIgY3VydmUuICovXG4gIHRvQmV6aWVyUmVnaW9uKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0UmVnaW9uT3ZlcmxheSgpPy5zZXRTZWxlY3RlZEJlemllcih0cnVlKTtcbiAgfVxuXG4gIC8qKiBDb252ZXJ0IHRoZSBzZWxlY3RlZCByZWdpb24gYmFjayB0byBhIHN0cmFpZ2h0LWVkZ2VkIHBvbHlnb24uICovXG4gIHRvUG9seWdvblJlZ2lvbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbk92ZXJsYXkoKT8uc2V0U2VsZWN0ZWRCZXppZXIoZmFsc2UpO1xuICB9XG5cbiAgLyoqXG4gICAqIEltbWVkaWF0ZSByZWdpb24gYWN0aW9ucyBvbiB0aGUgY3VycmVudCBzZWxlY3Rpb24gKGppdC11aSM4NSk6IG1lcmdlLFxuICAgKiB1bmdyb3VwLCBpbnZlcnNlLCBzaW1wbGlmeSwgdGhlIELDqXppZXIgY29udmVyc2lvbnMsIGFuZCBkZWxldGUg4oCUIHRoZVxuICAgKiBvbmUtc2hvdCBnZW9tZXRyeSB0cmFuc2Zvcm1zLCBncm91cGVkIGFwYXJ0IGZyb20gdGhlIHRvb2wvbW9kZSB0b2dnbGVzIGFuZFxuICAgKiBzaG93biBvbmx5IHdoZW4gc29tZXRoaW5nIGlzIHNlbGVjdGVkLiBFYWNoIGl0ZW0gaXMgZ2F0ZWQgb24gd2hhdCB0aGVcbiAgICogc2VsZWN0aW9uIHN1cHBvcnRzLlxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFJlZ2lvbkFjdGlvbkl0ZW1zKCk6IE1lbnVJdGVtW10ge1xuICAgIGNvbnN0IGFsbCA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0UmVnaW9ucygpO1xuICAgIGNvbnN0IHNlbGVjdGFibGUgPSBhbGwuZmlsdGVyKChyKSA9PiByLmtpbmQgIT09ICdwcm9maWxlJyk7XG4gICAgaWYgKHNlbGVjdGFibGUubGVuZ3RoID09PSAwKSByZXR1cm4gW107XG4gICAgY29uc3QgaXRlbXM6IE1lbnVJdGVtW10gPSBbXTtcblxuICAgIC8vIFNlbGVjdCBhbGwg4oCUIGF2YWlsYWJsZSB3aGVuZXZlciB0aGUgaW1hZ2UgaGFzIHJlZ2lvbnMsIHJlZ2FyZGxlc3Mgb2YgdGhlXG4gICAgLy8gY3VycmVudCBzZWxlY3Rpb24uXG4gICAgaXRlbXMucHVzaCh7XG4gICAgICBsYWJlbDogJ1NlbGVjdCBhbGwgcmVnaW9ucycsIGljb246ICdwaSBwaS1jaGVjay1zcXVhcmUnLFxuICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy5zZWxlY3RBbGxSZWdpb25zKCksXG4gICAgfSk7XG5cbiAgICBjb25zdCBzZWxBbGwgPSB0aGlzLnNlbGVjdGVkUmVnaW9ucztcbiAgICBpZiAoc2VsQWxsLmxlbmd0aCA9PT0gMCkgcmV0dXJuIGl0ZW1zO1xuICAgIGNvbnN0IGVsaWdpYmxlID0gdGhpcy5vcEVsaWdpYmxlKHNlbEFsbCk7XG5cbiAgICBpZiAoZWxpZ2libGUubGVuZ3RoID49IDIpIHtcbiAgICAgIGl0ZW1zLnB1c2goeyBsYWJlbDogJ01lcmdlIC8gZ3JvdXAnLCBpY29uOiAncGkgcGktbGluaycsIGNvbW1hbmQ6ICgpID0+IHRoaXMubWVyZ2VSZWdpb25zKCkgfSk7XG4gICAgfVxuICAgIGlmIChzZWxBbGwuc29tZSgocikgPT4gdGhpcy5yZWdpb25PcHMuY2FuVW5ncm91cChyKSkpIHtcbiAgICAgIGl0ZW1zLnB1c2goeyBsYWJlbDogJ1VuZ3JvdXAnLCBpY29uOiAncGkgcGktc2l0ZW1hcCcsIGNvbW1hbmQ6ICgpID0+IHRoaXMudW5ncm91cFJlZ2lvbnMoKSB9KTtcbiAgICB9XG4gICAgaWYgKGVsaWdpYmxlLmxlbmd0aCA+PSAxKSB7XG4gICAgICBpdGVtcy5wdXNoKFxuICAgICAgICB7IGxhYmVsOiAnSW52ZXJzZScsIGljb246ICdwaSBwaS1jbG9uZScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuaW52ZXJzZVJlZ2lvbnMoKSB9LFxuICAgICAgICB7XG4gICAgICAgICAgbGFiZWw6ICdTaW1wbGlmeScsIGljb246ICdwaSBwaS1jaGFydC1saW5lJyxcbiAgICAgICAgICBpdGVtczogW1xuICAgICAgICAgICAgeyBsYWJlbDogJ0xpZ2h0ICgxIHB4KScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2ltcGxpZnlSZWdpb25zKDEpIH0sXG4gICAgICAgICAgICB7IGxhYmVsOiAnTWVkaXVtICgzIHB4KScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2ltcGxpZnlSZWdpb25zKDMpIH0sXG4gICAgICAgICAgICB7IGxhYmVsOiAnU3Ryb25nICg4IHB4KScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2ltcGxpZnlSZWdpb25zKDgpIH0sXG4gICAgICAgICAgICB7IHNlcGFyYXRvcjogdHJ1ZSB9LFxuICAgICAgICAgICAgeyBsYWJlbDogJ0N1c3RvbeKApicsIGNvbW1hbmQ6ICgpID0+IHRoaXMub3BlblNpbXBsaWZ5RGlhbG9nKCkgfSxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgKTtcbiAgICB9XG4gICAgLy8gQsOpemllciBjb252ZXJzaW9ucyBhcmUgdmVydGV4LWxldmVsIGVkaXRzIOKAlCBJbWFnZSAoT3BlblNlYWRyYWdvbikgdmlld1xuICAgIC8vIG9ubHkg4oCUIGdhdGVkIG9uIHRoZSBzZWxlY3Rpb24ncyBjdXJyZW50IGZvcm0uXG4gICAgaWYgKHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0UpIHtcbiAgICAgIGNvbnN0IGhhc1N0cmFpZ2h0ID0gc2VsQWxsLnNvbWUoKHIpID0+XG4gICAgICAgIChyLmJvdW5kcyBpbnN0YW5jZW9mIFBvbHlnb24gJiYgIXIuYm91bmRzLmJlemllcikgfHwgci5ib3VuZHMgaW5zdGFuY2VvZiBSZWN0YW5nbGUpO1xuICAgICAgY29uc3QgaGFzQmV6aWVyID0gc2VsQWxsLnNvbWUoKHIpID0+IHIuYm91bmRzIGluc3RhbmNlb2YgUG9seWdvbiAmJiByLmJvdW5kcy5iZXppZXIpO1xuICAgICAgaWYgKGhhc1N0cmFpZ2h0KSB7XG4gICAgICAgIGl0ZW1zLnB1c2goeyBsYWJlbDogJ0NvbnZlcnQgdG8gQsOpemllcicsIGljb246ICd0by1iZXppZXItaWNvbicsIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9CZXppZXJSZWdpb24oKSB9KTtcbiAgICAgIH1cbiAgICAgIGlmIChoYXNCZXppZXIpIHtcbiAgICAgICAgaXRlbXMucHVzaCh7IGxhYmVsOiAnQ29udmVydCB0byBwb2x5Z29uJywgaWNvbjogJ3RvLXBvbHlnb24taWNvbicsIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9Qb2x5Z29uUmVnaW9uKCkgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIGl0ZW1zLnB1c2goeyBsYWJlbDogJ0RlbGV0ZSByZWdpb24nLCBpY29uOiAncGkgcGktdHJhc2gnLCBjb21tYW5kOiAoKSA9PiB0aGlzLmRlbGV0ZVJlZ2lvbigpIH0pO1xuICAgIHJldHVybiBpdGVtcztcbiAgfVxuXG4gIHByaXZhdGUgYnVpbGRDb250ZXh0TWVudUl0ZW1zKCk6IE1lbnVJdGVtW10ge1xuICAgIGNvbnN0IGFjdGl2ZSA9IHRoaXMuYWN0aXZlRHJhZ01vZGU7XG4gICAgY29uc3QgYWN0aXZlQ2xhc3MgPSAnY29udGV4dC1tZW51LWFjdGl2ZSc7XG4gICAgY29uc3QgaXRlbXM6IE1lbnVJdGVtW10gPSBbXTtcbiAgICAvLyBTZWxlY3RlZC1yZWdpb24gYWN0aW9ucyBsZWFkIHRoZSBtZW51IChzZXBhcmF0ZWQgZnJvbSB0aGUgdG9vbCB0b2dnbGVzKS5cbiAgICBpZiAodGhpcy5pc0hlYXRtYXApIHtcbiAgICAgIGNvbnN0IGFjdGlvbnMgPSB0aGlzLmJ1aWxkUmVnaW9uQWN0aW9uSXRlbXMoKTtcbiAgICAgIGlmIChhY3Rpb25zLmxlbmd0aCkgaXRlbXMucHVzaCguLi5hY3Rpb25zLCB7IHNlcGFyYXRvcjogdHJ1ZSB9KTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaXNIZWF0bWFwKSB7XG4gICAgICBpdGVtcy5wdXNoKFxuICAgICAgICB7IGxhYmVsOiAnQXV0b3NjYWxlJywgaWNvbjogJ3BpIHBpLXdpbmRvdy1tYXhpbWl6ZScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuYXV0b3NjYWxlSW1hZ2UoKSB9LFxuICAgICAgICB7IHNlcGFyYXRvcjogdHJ1ZSB9LFxuICAgICAgKTtcbiAgICAgIC8vICdab29tIHNlbGVjdGlvbicgaXMgUGxvdGx5J3MgcnViYmVyLWJhbmQgem9vbTsgaXQgZG9lc24ndCBhcHBseSB0byB0aGVcbiAgICAgIC8vIE9wZW5TZWFkcmFnb24tYmFja2VkIEltYWdlIHZpZXcgKHVzZSAnWm9vbSB0byBib3gnIHRoZXJlIGluc3RlYWQpLlxuICAgICAgaWYgKCF0aGlzLmlzSW1hZ2VWaWV3KSB7XG4gICAgICAgIGl0ZW1zLnB1c2goe1xuICAgICAgICAgIGxhYmVsOiAnWm9vbSBzZWxlY3Rpb24nLFxuICAgICAgICAgIGljb246ICdwaSBwaS1zZWFyY2gnLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ3pvb20nID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCd6b29tJyksXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgaXRlbXMucHVzaCh7XG4gICAgICAgIGxhYmVsOiAnWm9vbSB0byBib3gnLFxuICAgICAgICBpY29uOiAnem9vbS1ib3gtb2ZmLWljb24nLFxuICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICd6b29tVG9Cb3gnID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnem9vbVRvQm94JyksXG4gICAgICB9KTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaXNIZWF0bWFwKSB7XG4gICAgICBpdGVtcy5wdXNoKHtcbiAgICAgICAgbGFiZWw6ICdQYW4nLFxuICAgICAgICBpY29uOiAncGkgcGktYXJyb3dzLWFsdCcsXG4gICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ3BhbicgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdwYW4nKSxcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBzM2QgPSB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGU7XG4gICAgICBpdGVtcy5wdXNoKFxuICAgICAgICB7XG4gICAgICAgICAgbGFiZWw6ICdab29tJyxcbiAgICAgICAgICBpY29uOiAncGkgcGktc2VhcmNoJyxcbiAgICAgICAgICBzdHlsZUNsYXNzOiBzM2QgPT09ICd6b29tJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVTdXJmYWNlM2RNb2RlKCd6b29tJyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1BhbicsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLWFycm93cy1hbHQnLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IHMzZCA9PT0gJ3BhbicgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlU3VyZmFjZTNkTW9kZSgncGFuJyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ09yYml0YWwgcm90YXRpb24nLFxuICAgICAgICAgIGljb246ICdwaSBwaS1nbG9iZScsXG4gICAgICAgICAgc3R5bGVDbGFzczogczNkID09PSAnb3JiaXQnID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZVN1cmZhY2UzZE1vZGUoJ29yYml0JyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1R1cm50YWJsZSByb3RhdGlvbicsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLXN5bmMnLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IHMzZCA9PT0gJ3R1cm50YWJsZScgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlU3VyZmFjZTNkTW9kZSgndHVybnRhYmxlJyksXG4gICAgICAgIH0sXG4gICAgICAgIHsgc2VwYXJhdG9yOiB0cnVlIH0sXG4gICAgICAgIHsgbGFiZWw6ICdSZXNldCBjYW1lcmEnLCBpY29uOiAncGkgcGktaG9tZScsIGNvbW1hbmQ6ICgpID0+IHRoaXMucmVzZXRTdXJmYWNlQ2FtZXJhKCkgfSxcbiAgICAgICk7XG4gICAgfVxuICAgIGlmICh0aGlzLmlzSGVhdG1hcCkge1xuICAgICAgaXRlbXMucHVzaChcbiAgICAgICAgeyBsYWJlbDogJ1pvb20gaW4nLCBpY29uOiAncGkgcGktc2VhcmNoLXBsdXMnLCBjb21tYW5kOiAoKSA9PiB0aGlzLnpvb21JbigpIH0sXG4gICAgICAgIHsgbGFiZWw6ICdab29tIG91dCcsIGljb246ICdwaSBwaS1zZWFyY2gtbWludXMnLCBjb21tYW5kOiAoKSA9PiB0aGlzLnpvb21PdXQoKSB9LFxuICAgICAgICB7IHNlcGFyYXRvcjogdHJ1ZSB9LFxuICAgICAgICB7XG4gICAgICAgICAgbGFiZWw6ICdTZWxlY3QnLFxuICAgICAgICAgIGljb246ICdwaSBwaS1hcnJvdy11cC1yaWdodCcsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnc2VsZWN0JyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnc2VsZWN0JyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ0ZyZWVmb3JtJyxcbiAgICAgICAgICBpY29uOiAncGkgcGktcGVuY2lsJyxcbiAgICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICdkcmF3Y2xvc2VkcGF0aCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2RyYXdjbG9zZWRwYXRoJyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ0JydXNoJyxcbiAgICAgICAgICBpY29uOiAnYnJ1c2gtaWNvbicsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnYnJ1c2gnID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdicnVzaCcpLFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgbGFiZWw6ICdQb2x5bGluZScsXG4gICAgICAgICAgaWNvbjogJ3BvbHlsaW5lLWljb24nLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ2RyYXdvcGVucGF0aCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2RyYXdvcGVucGF0aCcpLFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgbGFiZWw6ICdSZWN0YW5nbGUnLFxuICAgICAgICAgIGljb246ICdwaSBwaS1zdG9wJyxcbiAgICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICdkcmF3cmVjdCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2RyYXdyZWN0JyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1dhbmQnLFxuICAgICAgICAgIGljb246ICd3YW5kLWljb24nLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ3dhbmQnID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCd3YW5kJyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1ZlcnRleCBlcmFzZXInLFxuICAgICAgICAgIGljb246ICdwaSBwaS1lcmFzZXInLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ2VyYXNlVmVydGV4JyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZXJhc2VWZXJ0ZXgnKSxcbiAgICAgICAgfSxcbiAgICAgICk7XG4gICAgICAvLyBWZXJ0ZXggZWRpdGluZyBydW5zIG9uIHRoZSBPcGVuU2VhZHJhZ29uIG92ZXJsYXksIHdoaWNoXG4gICAgICAvLyBiYWNrcyB0aGUgSW1hZ2UgcGxvdCB0eXBlLiBIaWRkZW4gZm9yIG90aGVyIDJEIHR5cGVzIChQbG90bHkpLCB3aGVyZVxuICAgICAgLy8gdGhlc2UgbW9kZXMgYXJlIG5vLW9wcy5cbiAgICAgIGlmICh0aGlzLnNlbGVjdGVkUGxvdFR5cGUgPT09IFBsb3RUeXBlLklNQUdFKSB7XG4gICAgICAgIGl0ZW1zLnB1c2goXG4gICAgICAgICAge1xuICAgICAgICAgICAgbGFiZWw6ICdQb2x5Z29uIChjbGljayB2ZXJ0aWNlcyknLFxuICAgICAgICAgICAgaWNvbjogJ3BvbHlnb24tdmVydGljZXMtaWNvbicsXG4gICAgICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICdkcmF3cG9seWdvbicgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd3BvbHlnb24nKSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiAnQWRkIHZlcnRleCcsXG4gICAgICAgICAgICBpY29uOiAndmVydGV4LWFkZC1pY29uJyxcbiAgICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ2FkZHBvaW50JyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdhZGRwb2ludCcpLFxuICAgICAgICAgIH0sXG4gICAgICAgICAge1xuICAgICAgICAgICAgbGFiZWw6ICdEZWxldGUgdmVydGV4JyxcbiAgICAgICAgICAgIGljb246ICd2ZXJ0ZXgtZGVsZXRlLWljb24nLFxuICAgICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnZGVsZXRlcG9pbnQnID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2RlbGV0ZXBvaW50JyksXG4gICAgICAgICAgfSxcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIC8vIELDqXppZXIgY29udmVyc2lvbnMgKyBEZWxldGUgYXJlIGltbWVkaWF0ZSBzZWxlY3Rpb24gKmFjdGlvbnMqLCBub3cgaW4gdGhlXG4gICAgICAvLyBcIlNlbGVjdGVkIHJlZ2lvbihzKVwiIGdyb3VwIGF0IHRoZSB0b3AgKHNlZSBidWlsZFJlZ2lvbkFjdGlvbkl0ZW1zKS5cbiAgICB9XG4gICAgcmV0dXJuIGl0ZW1zO1xuICB9XG5cbiAgdG9nZ2xlU3VyZmFjZTNkTW9kZShtb2RlOiBzdHJpbmcpIHtcbiAgICB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUgPSBtb2RlO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U3VyZmFjZURyYWdNb2RlKG1vZGUpO1xuICB9XG5cbiAgcmVzZXRTdXJmYWNlQ2FtZXJhKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UucmVzZXRTdXJmYWNlQ2FtZXJhKCk7XG4gIH1cblxuICAvKiogVG9nZ2xlIHRoZSBuYXBhcmkgM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gKHZvbHVtZS9pc29zdXJmYWNlKS4gTm8tb3Agb24gYmFja2VuZHNcbiAgICogIHRoYXQgZG9uJ3QgcmVuZGVyIG9uZSAodGhlIGNvbnRyb2wgbWV0aG9kIGlzIG9wdGlvbmFsKS4gKi9cbiAgdG9nZ2xlQXhlcygpIHtcbiAgICB0aGlzLmF4ZXNWaXNpYmxlID0gIXRoaXMuYXhlc1Zpc2libGU7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRTdXJmYWNlM2RDb250cm9scygpPy5zZXRBeGVzVmlzaWJsZT8uKHRoaXMuYXhlc1Zpc2libGUpO1xuICB9XG5cbiAgLyoqIFRvZ2dsZSB0aGUgbmFwYXJpIHN1cmZhY2Ugd2lyZWZyYW1lIChlZGdlcyB2cyBmaWxsZWQpLiBOby1vcCBvbiBiYWNrZW5kcyB3aXRob3V0IGEgc3VyZmFjZS4gKi9cbiAgdG9nZ2xlV2lyZWZyYW1lKCkge1xuICAgIHRoaXMud2lyZWZyYW1lQWN0aXZlID0gIXRoaXMud2lyZWZyYW1lQWN0aXZlO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0U3VyZmFjZTNkQ29udHJvbHMoKT8uc2V0V2lyZWZyYW1lPy4odGhpcy53aXJlZnJhbWVBY3RpdmUpO1xuICB9XG5cbiAgLyoqIENoYW5nZSB0aGUgbmFwYXJpIDNEIGRlY2ltYXRlIGZhY3Rvci4gRGVjaW1hdGlvbiBjaGFuZ2VzIHRoZSBmZXRjaGVkL2Fzc2VtYmxlZCBkYXRhLCBzbyB0aGlzXG4gICAqICByZS1wbG90cyB0aGUgY3VycmVudCAzRCB0eXBlIGF0IHRoZSBuZXcgcmVzb2x1dGlvbiAodW5saWtlIGF4ZXMvd2lyZWZyYW1lLCB3aGljaCBhcmUgbGl2ZSkuICovXG4gIHNlbGVjdFJlc29sdXRpb24oc2NhbGU6IG51bWJlcikge1xuICAgIGlmIChzY2FsZSA9PT0gdGhpcy5yZXNvbHV0aW9uU2NhbGUpIHJldHVybjtcbiAgICB0aGlzLnJlc29sdXRpb25TY2FsZSA9IHNjYWxlO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UmVzb2x1dGlvblNjYWxlPy4oc2NhbGUpO1xuICAgIHRoaXMucmVsb2FkQW5kUGxvdCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFN3aXRjaCB0aGUgYWN0aXZlIHBsb3QgdHlwZSAoaGVhdG1hcCwgc3VyZmFjZSwgY29udG91ciwgc2NhdHRlciwgbGluZSxcbiAgICogc2NhdHRlcjNkLCBpc29zdXJmYWNlKS4gS2VlcHMgYGlzSGVhdG1hcGAgYXMgdGhlIDJELXZzLTNEIGZsYWcgdGhlIHJlc3Qgb2ZcbiAgICogdGhlIHRvb2xiYXIvY29udGV4dC1tZW51IGxvZ2ljIHJlbGllcyBvbiwgZGVhY3RpdmF0ZXMgMkQtb25seSB0b29scyB3aGVuXG4gICAqIG1vdmluZyB0byBhIDNEIHR5cGUsIHRoZW4gcmUtcGxvdHMuXG4gICAqL1xuICBvblNlbGVjdFBsb3RUeXBlKHR5cGU6IFBsb3RUeXBlKSB7XG4gICAgdGhpcy5wbG90VHlwZSA9IHR5cGU7XG4gICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlID0gdHlwZTtcbiAgICBjb25zdCBkZXNjcmlwdG9yID0gdGhpcy5wbG90VHlwZU9wdGlvbnMuZmluZCgoZCkgPT4gZC50eXBlID09PSB0eXBlKTtcbiAgICBjb25zdCBpczNkID0gZGVzY3JpcHRvcj8uZGltZW5zaW9ucyA9PT0gJzNkJztcbiAgICB0aGlzLmlzSGVhdG1hcCA9ICFpczNkO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UGxvdFR5cGUodHlwZSk7XG4gICAgLy8gU3dpdGNoaW5nIHBsb3QgdHlwZSBjYW5jZWxzIHRoZSBhY3RpdmUgdG9vbCDigJQgbm90IGV2ZXJ5IHR5cGUgc3VwcG9ydHMgdGhlXG4gICAgLy8gc2FtZSB0b29scyAobGluZS9zY2F0dGVyIGNoYXJ0cyBhbmQgM0Qgc2NlbmVzIGhhdmUgbm8gYm94IHpvb20gb3IgcmVnaW9uXG4gICAgLy8gZHJhd2luZyksIHNvIGxlYXZpbmcgYSB0b29sIGFybWVkIHdvdWxkIG1pc2JlaGF2ZSBvbiB0aGUgbmV3IHBsb3QuXG4gICAgdGhpcy5kZWFjdGl2YXRlQWN0aXZlVG9vbCgpO1xuICAgIGlmIChpczNkKSB7XG4gICAgICB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUgPSAndHVybnRhYmxlJztcbiAgICB9XG4gICAgLy8gbmFwYXJpLWpzIHZvbHVtZS9pc29zdXJmYWNlIGFzc2VtYmxlIHRoZWlyIG93biAzRCB0ZXh0dXJlIGZyb20gdGhlIHNsaWNlIGVuZHBvaW50LCBzb1xuICAgIC8vIHRoZXkgdGFrZSB0aGUgbm9ybWFsIHJlLXBsb3QgcGF0aCDigJQgTk9UIFBsb3RseSdzIHN0YWNrLWZyYW1lIGxvYWRlciAod2hvc2UgXCJMb2FkaW5nXG4gICAgLy8gZnJhbWVzXCIgb3ZlcmxheSB3b3VsZCBuZXZlciBjbGVhciwgc2luY2UgUGxvdGx5IGlzbid0IHRoZSBhY3RpdmUgYmFja2VuZCkgKGppdC11aSMxMDIpLlxuICAgIGNvbnN0IGlzTmFwYXJpID0gdHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFIHx8IGlzTmFwYXJpM2QodHlwZSk7XG4gICAgaWYgKGRlc2NyaXB0b3I/LnJlcXVpcmVzU3RhY2sgJiYgIWlzTmFwYXJpKSB7XG4gICAgICAvLyBWb2x1bWUgdHlwZXMgKGlzb3N1cmZhY2UsIHNjYXR0ZXIzZCkgbmVlZCB0aGUgd2hvbGUgei1zdGFjayBsb2FkZWQgYXMgYVxuICAgICAgLy8gM0QgYXJyYXksIGFuZCB0aGV5IGFsd2F5cyByZW5kZXIgb24gUGxvdGx5LiBEcml2ZSB0aGUgc3RhY2sgcmVsb2FkXG4gICAgICAvLyBkaXJlY3RseSB0aHJvdWdoIHRoZSBpbWFnZS1pbmZvIHN0cmVhbSByYXRoZXIgdGhhbiB0aGUgYWN0aXZlIHJlbmRlcmVyJ3NcbiAgICAgIC8vIHNldFNob3dTdGFjazogd2hlbiBPcGVuU2VhZHJhZ29uIG93bnMgdGhlIEltYWdlIHZpZXcgaXRzIHNldFNob3dTdGFjayBpc1xuICAgICAgLy8gYSBuby1vcCwgc28gdGhlIHJlbG9hZCBuZXZlciBmaXJlZCBhbmQgdGhlIGxvYWRlciBzcHVuIGZvcmV2ZXIuIFdlIGFybVxuICAgICAgLy8gUGxvdGx5J3Mgc3RhY2stbG9hZGluZyBmbGFnIChrZWVwcyBpdHMgZnJhbWUtZmV0Y2ggbG9vcCBhbGl2ZSkgYW5kXG4gICAgICAvLyByZS1lbWl0IHRoZSBpbWFnZSBpbmZvIHdpdGggc2hvd1N0YWNrIG9uIHNvIHRoZSBwaXBlbGluZSByZWxvYWRzIG9uXG4gICAgICAvLyBQbG90bHkgcmVnYXJkbGVzcyBvZiB3aGljaCBiYWNrZW5kIHdhcyBvbiBzY3JlZW4uXG4gICAgICB0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24gPSB0aGlzLnN0YWNrT3B0aW9uc1sxXTtcbiAgICAgIHRoaXMuc3RhY2tMb2FkaW5nID0gdHJ1ZTtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U3RhY2tMb2FkaW5nKHRydWUpO1xuICAgICAgaWYgKHRoaXMuaW1hZ2VJbmZvKSB7XG4gICAgICAgIHRoaXMuaW1hZ2VJbmZvLnNob3dTdGFjayA9IHRydWU7XG4gICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKHRoaXMuaW1hZ2VJbmZvKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5yZWxvYWRBbmRQbG90KCk7XG4gIH1cblxuICAvKiogRGVhY3RpdmF0ZSB3aGF0ZXZlciB0b29sIGlzIGFybWVkIGFuZCBjbGVhciBldmVyeSB0b29sIG1vZGUuICovXG4gIHByaXZhdGUgZGVhY3RpdmF0ZUFjdGl2ZVRvb2woKSB7XG4gICAgdGhpcy5hY3RpdmVEcmFnTW9kZSA9IG51bGw7XG4gICAgdGhpcy5zZXNzaW9uLnNldEFjdGl2ZVRvb2wobnVsbCk7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25PdmVybGF5KCk/LnNldE1vZGUoJ25vbmUnKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldERyYWdNb2RlKGZhbHNlKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFpvb21Ub0JveE1vZGUoZmFsc2UpO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0V2FuZE1vZGUoZmFsc2UpO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0QnJ1c2hNb2RlKGZhbHNlKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNhbVBvaW50TW9kZShmYWxzZSk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRWZXJ0ZXhFcmFzZXJNb2RlKGZhbHNlKTtcbiAgfVxufVxuXG4iLCI8IS0tIFNBTSB0b2FzdDogbGl2ZSBzdGF0dXMgKyBlbmNvZGVyLWRvd25sb2FkIHByb2dyZXNzIGJhciAoaml0LXVpIzkwKS4gU3RpY2t5XG4gICAgIHdoaWxlIGEgc2VnbWVudCBydW5zOyBzdGF5cyBvcGVuIHVudGlsIHRoZSBiYXIgcmVhY2hlcyAxMDAlIGFuZCB0aGUgcnVuIGVuZHMuIC0tPlxuPHAtdG9hc3QgW2tleV09XCJzYW1Ub2FzdEtleVwiIHBvc2l0aW9uPVwiYm90dG9tLXJpZ2h0XCI+XG4gIDxuZy10ZW1wbGF0ZSBsZXQtbWVzc2FnZSBwVGVtcGxhdGU9XCJtZXNzYWdlXCI+XG4gICAgPGRpdiBzdHlsZT1cImZsZXg6IDE7IG1pbi13aWR0aDogMTZyZW1cIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJmb250LWJvbGQgbWItMVwiPnt7IG1lc3NhZ2Uuc3VtbWFyeSB9fTwvZGl2PlxuICAgICAgPGRpdj57eyBzYW1TdGF0dXMgfHwgbWVzc2FnZS5kZXRhaWwgfX08L2Rpdj5cbiAgICAgIDxwLXByb2dyZXNzQmFyICpuZ0lmPVwic2FtRG93bmxvYWRpbmdcIiBbdmFsdWVdPVwic2FtUHJvZ3Jlc3NcIiBzdHlsZUNsYXNzPVwibXQtMiBzYW0tcHJvZ3Jlc3NcIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgICA8cC1wcm9ncmVzc0JhciAqbmdJZj1cInNhbUJ1c3kgJiYgIXNhbURvd25sb2FkaW5nXCIgbW9kZT1cImluZGV0ZXJtaW5hdGVcIiBzdHlsZUNsYXNzPVwibXQtMlwiXG4gICAgICAgICAgICAgICAgICAgICBbc3R5bGVdPVwieyBoZWlnaHQ6ICc0cHgnIH1cIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgPC9kaXY+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtdG9hc3Q+XG5cbjwhLS0gVG9vbGJhciBkb2NrOiBkb2NrZWQgYWNyb3NzIHRoZSB0b3AgYnkgZGVmYXVsdDsgZHJhZyB0aGUgaGFuZGxlIHRvIGRldGFjaFxuICAgICBpdCBpbnRvIGEgZmxvYXRpbmcsIG1vdmFibGUgd2luZG93IChmcmVlcyB0aGUgdG9wIHJvdyBmb3IgdGhlIGltYWdlKS4gLS0+XG48ZGl2IGNsYXNzPVwidG9vbGJhci1kb2NrXCIgW2NsYXNzLmZsb2F0aW5nXT1cInRvb2xiYXJGbG9hdGluZ1wiXG4gICAgIFtzdHlsZS5sZWZ0LnB4XT1cInRvb2xiYXJGbG9hdGluZyA/IHRvb2xiYXJQb3MueCA6IG51bGxcIlxuICAgICBbc3R5bGUudG9wLnB4XT1cInRvb2xiYXJGbG9hdGluZyA/IHRvb2xiYXJQb3MueSA6IG51bGxcIj5cbiAgPGRpdiBjbGFzcz1cInRvb2xiYXItZHJhZy1oYW5kbGVcIiAobW91c2Vkb3duKT1cIm9uVG9vbGJhckRyYWdTdGFydCgkZXZlbnQpXCJcbiAgICAgICBwVG9vbHRpcD1cIkRyYWcgdG8gZmxvYXQgdGhlIHRvb2xiYXJcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIj5cbiAgICA8aSBjbGFzcz1cInBpIHBpLWJhcnNcIj48L2k+XG4gIDwvZGl2PlxuPHBsb3R0aW5nLXRvb2xiYXJcbiAgW2ltYWdlSW5mb109XCJpbWFnZUluZm9cIlxuICBbcGxvdFR5cGVPcHRpb25zXT1cInBsb3RUeXBlT3B0aW9uc1wiXG4gIFtzZWxlY3RlZFBsb3RUeXBlXT1cInNlbGVjdGVkUGxvdFR5cGVcIlxuICBbaXNvUmFuZ2VdPVwiaXNvUmFuZ2VcIlxuICBbbWF4SW5kZXhdPVwibWF4SW5kZXhcIlxuICBbekluZGV4XT1cInpJbmRleFwiXG4gIFtpc0hlYXRtYXBdPVwiaXNIZWF0bWFwXCJcbiAgW2FjdGl2ZURyYWdNb2RlXT1cImFjdGl2ZURyYWdNb2RlXCJcbiAgW2FjdGl2ZVN1cmZhY2UzZE1vZGVdPVwiYWN0aXZlU3VyZmFjZTNkTW9kZVwiXG4gIFtheGVzVmlzaWJsZV09XCJheGVzVmlzaWJsZVwiXG4gIFt3aXJlZnJhbWVBY3RpdmVdPVwid2lyZWZyYW1lQWN0aXZlXCJcbiAgW3Jlc29sdXRpb25TY2FsZV09XCJyZXNvbHV0aW9uU2NhbGVcIlxuICBbd2FuZFNlbnNpdGl2aXR5XT1cIndhbmRTZW5zaXRpdml0eVwiXG4gIFticnVzaFNpemVdPVwiYnJ1c2hTaXplXCJcbiAgW3NhbU1vZGVsc109XCJzYW1Nb2RlbHNcIlxuICBbc2FtTW9kZWxJZF09XCJzYW1Nb2RlbElkXCJcbiAgW3ZlcnRleEVyYXNlclJhZGl1c109XCJ2ZXJ0ZXhFcmFzZXJSYWRpdXNcIlxuICBbY2FuVW5kb109XCJjYW5VbmRvUmVnaW9uXCJcbiAgW2NhblJlZG9dPVwiY2FuUmVkb1JlZ2lvblwiXG4gIFtzdGFja09wdGlvbnNdPVwic3RhY2tPcHRpb25zXCJcbiAgW3Rvb2xzXT1cInRvb2xiYXJUb29sc1wiXG4gIFtpbWFnZVNtb290aGluZ0VuYWJsZWRdPVwiaW1hZ2VTbW9vdGhpbmdFbmFibGVkXCJcbiAgKHNlbGVjdFBsb3RUeXBlKT1cIm9uU2VsZWN0UGxvdFR5cGUoJGV2ZW50KVwiXG4gICh0b2dnbGVJbWFnZVNtb290aGluZyk9XCJvblRvZ2dsZUltYWdlU21vb3RoaW5nKClcIlxuICAoYWRkUHJvZmlsZUxpbmUpPVwiYWRkUHJvZmlsZUxpbmUoKVwiXG4gIChpc29SYW5nZUNoYW5nZSk9XCJvbklzb1JhbmdlQ2hhbmdlKCRldmVudClcIlxuICAob3BlbkNoYW5uZWxIaXN0b2dyYW0pPVwib3BlbkNoYW5uZWxIaXN0b2dyYW0oKVwiXG4gIChvcGVuUmVnaW9uRWRpdG9yKT1cIm9wZW5SZWdpb25FZGl0b3IoKVwiXG4gIChzZWxlY3RTdGFja09wdGlvbik9XCJzZWxlY3RTdGFja09wdGlvbigkZXZlbnQpXCJcbiAgKHpTY3J1Yik9XCJvblpTY3J1YigkZXZlbnQpXCJcbiAgKHpTbGlkZSk9XCJvblpTbGlkZSgkZXZlbnQpXCJcbiAgKHpJbmRleElucHV0KT1cInpJbmRleCA9ICRldmVudDsgdXBkYXRlWkluZGV4KClcIlxuICAocmVsb2FkQW5kUGxvdCk9XCJyZWxvYWRBbmRQbG90KClcIlxuICAoZG93bmxvYWRJbWFnZSk9XCJkb3dubG9hZEltYWdlKClcIlxuICAoYXV0b3NjYWxlSW1hZ2UpPVwiYXV0b3NjYWxlSW1hZ2UoKVwiXG4gICh0b2dnbGVEcmFnTW9kZSk9XCJ0b2dnbGVEcmFnTW9kZSgkZXZlbnQpXCJcbiAgKHpvb21Jbik9XCJ6b29tSW4oKVwiXG4gICh6b29tT3V0KT1cInpvb21PdXQoKVwiXG4gICh0b2dnbGVTdXJmYWNlM2RNb2RlKT1cInRvZ2dsZVN1cmZhY2UzZE1vZGUoJGV2ZW50KVwiXG4gIChyZXNldFN1cmZhY2VDYW1lcmEpPVwicmVzZXRTdXJmYWNlQ2FtZXJhKClcIlxuICAodG9nZ2xlQXhlcyk9XCJ0b2dnbGVBeGVzKClcIlxuICAodG9nZ2xlV2lyZWZyYW1lKT1cInRvZ2dsZVdpcmVmcmFtZSgpXCJcbiAgKHNlbGVjdFJlc29sdXRpb24pPVwic2VsZWN0UmVzb2x1dGlvbigkZXZlbnQpXCJcbiAgKGRlbGV0ZVJlZ2lvbik9XCJkZWxldGVSZWdpb24oKVwiXG4gICh1bmRvUmVnaW9uKT1cInVuZG9SZWdpb24oKVwiXG4gIChyZWRvUmVnaW9uKT1cInJlZG9SZWdpb24oKVwiXG4gIChzZWdtZW50UmVnaW9ucyk9XCJzZWdtZW50UmVnaW9ucygpXCJcbiAgKHNlZ21lbnRDZWxscG9zZSk9XCJzZWdtZW50Q2VsbHBvc2UoKVwiXG4gIChzYW1Nb2RlbENoYW5nZSk9XCJvblNhbU1vZGVsQ2hhbmdlKCRldmVudClcIlxuICAod2FuZFNlbnNpdGl2aXR5Q2hhbmdlKT1cIm9uV2FuZFNlbnNpdGl2aXR5Q2hhbmdlKCRldmVudClcIlxuICAoYnJ1c2hTaXplQ2hhbmdlKT1cIm9uQnJ1c2hTaXplQ2hhbmdlKCRldmVudClcIlxuICAodmVydGV4RXJhc2VyUmFkaXVzQ2hhbmdlKT1cIm9uVmVydGV4RXJhc2VyUmFkaXVzQ2hhbmdlKCRldmVudClcIj5cbiAgPCEtLSBIb3N0LXByb2plY3RlZCBjdXN0b20gdG9vbGJhciBidXR0b25zLCBmb3J3YXJkZWQgaW50byB0aGUgdG9vbGJhci4gLS0+XG4gIDxuZy1jb250ZW50PjwvbmctY29udGVudD5cbjwvcGxvdHRpbmctdG9vbGJhcj5cbiAgPHAtYnV0dG9uICpuZ0lmPVwidG9vbGJhckZsb2F0aW5nXCIgY2xhc3M9XCJ0b29sYmFyLWRvY2stYnRuXCIgaWNvbj1cInBpIHBpLXRodW1idGFja1wiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgcFRvb2x0aXA9XCJEb2NrIHRvIHRvcFwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiIChvbkNsaWNrKT1cImRvY2tUb29sYmFyKClcIj5cbiAgPC9wLWJ1dHRvbj5cbjwvZGl2PlxuXG48cC1jb250ZXh0TWVudSAjY20gW21vZGVsXT1cImNvbnRleHRNZW51SXRlbXNcIiBhcHBlbmRUbz1cImJvZHlcIj48L3AtY29udGV4dE1lbnU+XG48IS0tIE5vIGBoLWZ1bGxgIGhlcmU6IFByaW1lRmxleCdzIGhlaWdodDoxMDAlICFpbXBvcnRhbnQgbWFkZSAjcGxvdCBhcyB0YWxsIGFzXG4gICAgIHRoZSB3aG9sZSBob3N0LCBzbyB1bmRlciB0aGUgZG9ja2VkIHRvb2xiYXIgaXRzIGJvdHRvbSB0b29sYmFyLWhlaWdodCBzdHJpcFxuICAgICAoaW5jbC4gdGhlIE9TRCBzY2FsZSBiYXIgYXQgYm90dG9tLWxlZnQpIHdhcyBjbGlwcGVkIGJ5IHRoZSBob3N0J3NcbiAgICAgb3ZlcmZsb3c6aGlkZGVuLiBUaGUgY29tcG9uZW50IHN0eWxlc2hlZXQgc2l6ZXMgaXQgKC52aXotcGxvdCB7IGZsZXg6IDEgfSkuIC0tPlxuPGRpdiBjbGFzcz1cInctZnVsbCByZWxhdGl2ZSB2aXotcGxvdFwiIFtpZF09XCJwbG90RGl2TmFtZVwiPlxuICA8ZGl2ICpuZ0lmPVwiaW1nTG9hZGluZyB8fCBzdGFja0xvYWRpbmcgfHwgaXNDYWNoaW5nXCIgaWQ9XCJvdmVybGF5XCI+XG4gICAgPCEtLSBEZXRlcm1pbmF0ZSBzdGFjay92b2x1bWUgcHJvZ3Jlc3MgdGFrZXMgcHJlY2VkZW5jZTogd2hpbGUgaXQncyBhY3RpdmUgc2hvdyBPTkxZIHRoZVxuICAgICAgICAgcHJvZ3Jlc3MgYmFyICsgXCJMb2FkaW5nIGZyYW1lcy4uLlwiLCBub3QgdGhlIGdlbmVyaWMgc3Bpbm5lci9cIkxvYWRpbmcgaW1hZ2UuLi5cIiBvbiB0b3Agb2ZcbiAgICAgICAgIGl0IChib3RoIHdlcmUgcmVuZGVyaW5nIGF0IG9uY2UgZm9yIG5hcGFyaSB2b2x1bWUvaXNvc3VyZmFjZSBsb2FkcykuIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJzdGFja0xvYWRpbmc7IGVsc2UgaW1nTG9hZGluZ1RwbFwiPlxuICAgICAgPHAtcHJvZ3Jlc3NCYXIgY2xhc3M9XCJwcm9ncmVzc1wiIFt2YWx1ZV09XCJsb2FkaW5nUGVyY2VudGFnZVwiPjwvcC1wcm9ncmVzc0Jhcj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1sb2FkaW5nXCI+TG9hZGluZyBmcmFtZXMuLi48L3NwYW4+XG4gICAgPC9uZy1jb250YWluZXI+XG4gICAgPG5nLXRlbXBsYXRlICNpbWdMb2FkaW5nVHBsPlxuICAgICAgPCEtLSBDYWNoZS1jb3B5IHByb2dyZXNzIGlzIHNob3duIHdoZW5ldmVyIGEgY2FjaGUgZG93bmxvYWQgaXMgYWN0aXZlIChpc0NhY2hpbmcpLCBpbmRlcGVuZGVudFxuICAgICAgICAgICBvZiBgaW1nTG9hZGluZ2AsIHNvIGl0J3MgbmV2ZXIgZHJvcHBlZC9tYXNrZWQgbWlkLWRvd25sb2FkLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc0NhY2hpbmc7IGVsc2Ugc3Bpbm5lckxvYWRpbmdcIj5cbiAgICAgICAgPCEtLSBTdGFnZS1hd2FyZSBtZXNzYWdlIGNvbWVzIGZyb20gQ2FjaGVQcm9ncmVzc1NlcnZpY2UgdXBkYXRpbmdcbiAgICAgICAgICAgICBpbWFnZUxvYWRpbmdNZXNzYWdlJCAoXCJDYWNoaW5nIGltYWdlLi4uXCIgZHVyaW5nIHRoZSBHQ1MgZG93bmxvYWQsXG4gICAgICAgICAgICAgdGhlbiBcIkdlbmVyYXRpbmcgcHJldmlldy4uLlwiIGR1cmluZyBwZXItc2xpY2UgdGh1bWJuYWlsIGdlbikuIE9uY2UgdGhlXG4gICAgICAgICAgICAgZG93bmxvYWQgaGl0cyAxMDAlIHRoZSByZW1haW5pbmcgd29yayAoc2VydmVyIHByZXZpZXcvdGlsZSBnZW5lcmF0aW9uKVxuICAgICAgICAgICAgIGhhcyBubyBkZXRlcm1pbmF0ZSBwcm9ncmVzcywgc28gZmFsbCB0aHJvdWdoIHRvIHRoZSBzcGlubmVyIHRlbXBsYXRlXG4gICAgICAgICAgICAgcmF0aGVyIHRoYW4gcGFya2luZyBhIGZ1bGwgYmFyIHVuZGVyIFwiTG9hZGluZyBpbWFnZS4uLlwiLiAtLT5cbiAgICAgICAgPHAtcHJvZ3Jlc3NCYXIgY2xhc3M9XCJwcm9ncmVzc1wiIFt2YWx1ZV09XCJjYWNoZVByb2dyZXNzXCI+PC9wLXByb2dyZXNzQmFyPlxuICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtbG9hZGluZ1wiPnt7bG9hZGluZ01lc3NhZ2V9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPG5nLXRlbXBsYXRlICNzcGlubmVyTG9hZGluZz5cbiAgICAgICAgPGRpdiAqbmdJZj1cImltZ0xvYWRpbmdcIiBjbGFzcz1cInNwaW5uZXJcIj48L2Rpdj5cbiAgICAgICAgPHNwYW4gKm5nSWY9XCJpbWdMb2FkaW5nICYmICF6b29tXCIgY2xhc3M9XCJ0ZXh0LWxvYWRpbmdcIj57e2xvYWRpbmdNZXNzYWdlfX08L3NwYW4+XG4gICAgICAgIDxzcGFuICpuZ0lmPVwiaW1nTG9hZGluZyAmJiB6b29tXCIgY2xhc3M9XCJ6b29tLXRleHQtbG9hZGluZ1wiPnt7bG9hZGluZ01lc3NhZ2V9fTwvc3Bhbj5cbiAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgPC9uZy10ZW1wbGF0ZT5cbiAgICA8cC1idXR0b24gKm5nSWY9XCJzdGFja0xvYWRpbmcgfHwgaW1nTG9hZGluZ1wiIGNsYXNzPVwiY2FuY2VsLWxvYWRpbmcgXCIgbGFiZWw9XCJDYW5jZWxcIlxuICAgICAgICAgICAgICAob25DbGljayk9XCJjYW5jZWxMb2FkaW5nKClcIj5cbiAgICA8L3AtYnV0dG9uPlxuICA8L2Rpdj5cbiAgPCEtLSBUcmFuc2x1Y2VudCBcInNoYXJwZW5pbmdcIiBvdmVybGF5IHNob3duIGFmdGVyIHRoZSBzbWFsbC10aWVyIHByZXZpZXcgaXNcbiAgICAgICBvbiBzY3JlZW4gd2hpbGUgdGhlIGxhcmdlLXRpZXIgcmVuZGVyIGlzIHN0aWxsIGxvYWRpbmcgaW4gdGhlXG4gICAgICAgYmFja2dyb3VuZC4gTGV0cyB0aGUgdXNlciBzZWUgdGhlIGJsdXJyeSBpbWFnZSB1bmRlcm5lYXRoIGJ1dCBtYWtlc1xuICAgICAgIGl0IGNsZWFyIHNvbWV0aGluZyBpcyBzdGlsbCBsb2FkaW5nIHNvIHRoZXkgZG9uJ3QgbWlzdGFrZSB0aGVcbiAgICAgICBsb3ctcmVzIHJlbmRlciBmb3IgdGhlIGZpbmFsIGltYWdlLiAtLT5cbiAgPGRpdiAqbmdJZj1cInNoYXJwZW5pbmcgJiYgIWltZ0xvYWRpbmcgJiYgIXN0YWNrTG9hZGluZyAmJiAhaXNDYWNoaW5nXCIgaWQ9XCJzaGFycGVuaW5nLW92ZXJsYXlcIj5cbiAgICA8ZGl2IGNsYXNzPVwic3Bpbm5lclwiPjwvZGl2PlxuICAgIDxzcGFuIGNsYXNzPVwidGV4dC1sb2FkaW5nXCI+U2hhcnBlbmluZyBwcmV2aWV3Li4uPC9zcGFuPlxuICA8L2Rpdj5cblxuPC9kaXY+XG5cbjwhLS0gRmxvYXRpbmcsIGRyYWdnYWJsZSBpbnRlbnNpdHktcHJvZmlsZSBpbnNldC4gU2hvd24gaW4gTElORSBtb2RlOyB0aGUgY2hhcnRcbiAgICAgdXBkYXRlcyBsaXZlIGFzIHRoZSBsaW5lIFJPSSBvbiB0aGUgaW1hZ2UgaXMgbW92ZWQuIEtlcHQgT1VUU0lERSAjcGxvdCBhbmRcbiAgICAgcG9zaXRpb25lZCBmaXhlZCBzbyBpdCBmbG9hdHMgb3ZlciB0aGUgd2hvbGUgcGFnZSAobm90IGNsaXBwZWQgdG8gdGhlXG4gICAgIGRpYWdyYW0vY2FudmFzIGFyZWEpLiAtLT5cbjxkaXYgKm5nSWY9XCJoYXNQcm9maWxlc1wiIGNsYXNzPVwiaW50ZW5zaXR5LXBhbmVsXCJcbiAgICAgW3N0eWxlLmxlZnQucHhdPVwicHJvZmlsZVBhbmVsUG9zLnhcIiBbc3R5bGUudG9wLnB4XT1cInByb2ZpbGVQYW5lbFBvcy55XCI+XG4gIDxkaXYgY2xhc3M9XCJpbnRlbnNpdHktcGFuZWwtaGVhZGVyXCIgKG1vdXNlZG93bik9XCJvblByb2ZpbGVQYW5lbERyYWdTdGFydCgkZXZlbnQpXCI+XG4gICAgPHNwYW4+SW50ZW5zaXR5IHByb2ZpbGU8L3NwYW4+XG4gICAgPGkgY2xhc3M9XCJwaSBwaS1hcnJvd3MtYWx0XCI+PC9pPlxuICA8L2Rpdj5cbiAgPGRpdiBbaWRdPVwiaW50ZW5zaXR5SW5zZXREaXZcIiBjbGFzcz1cImludGVuc2l0eS1wYW5lbC1wbG90XCI+PC9kaXY+XG48L2Rpdj5cblxuPCEtLSBDaGFubmVscyAmIEhpc3RvZ3JhbTogbm9uLW1vZGFsIGZsb2F0aW5nIGRpYWxvZyBvcGVuZWQgZnJvbSB0aGUgdG9vbGJhci4gLS0+XG48Y2hhbm5lbC1oaXN0b2dyYW0gWyh2aXNpYmxlKV09XCJzaG93Q2hhbm5lbEhpc3RvZ3JhbVwiPjwvY2hhbm5lbC1oaXN0b2dyYW0+XG5cbjwhLS0gUmVnaW9uIEVkaXRvcjogbm9uLW1vZGFsLCBkcmFnZ2FibGUsIHJlc2l6YWJsZSBmbG9hdGluZyBkaWFsb2cgb3BlbmVkIGZyb21cbiAgICAgdGhlIHRvb2xiYXIgKGZvcm1lcmx5IHRoZSByaWdodC1wYW5lbCBSZWdpb25zIHRhYikuIE1vZGVsZXNzIHNvIHJlZ2lvbnMgY2FuXG4gICAgIHN0aWxsIGJlIGRyYXduL3NlbGVjdGVkIG9uIHRoZSBwbG90IHdoaWxlIGl0J3Mgb3Blbjsgc3RheXMgbGlua2VkIHRvIHRoaXNcbiAgICAgcGxvdHRpbmcgaW5zdGFuY2UgdmlhIHRoZSByb290IFJlZ2lvblN0b3JlIHNpbmdsZXRvbi4gT3BlbnMgb3ZlciB0aGUgcmlnaHRcbiAgICAgc2lkZSBvZiB0aGUgdmlldy4gLS0+XG48cC1kaWFsb2cgaGVhZGVyPVwiUmVnaW9uIEVkaXRvclwiIFsodmlzaWJsZSldPVwic2hvd1JlZ2lvbkVkaXRvclwiXG4gICAgICAgICAgW21vZGFsXT1cImZhbHNlXCIgW3Jlc2l6YWJsZV09XCJ0cnVlXCIgW2RyYWdnYWJsZV09XCJ0cnVlXCJcbiAgICAgICAgICBbbWF4aW1pemFibGVdPVwidHJ1ZVwiIGFwcGVuZFRvPVwiYm9keVwiIHBvc2l0aW9uPVwicmlnaHRcIlxuICAgICAgICAgIFtzdHlsZV09XCJ7IHdpZHRoOiByZWdpb25FZGl0b3JXaWR0aCwgaGVpZ2h0OiAnODV2aCcgfVwiXG4gICAgICAgICAgW2NvbnRlbnRTdHlsZV09XCJ7IG92ZXJmbG93OiAnYXV0bycgfVwiPlxuICA8cmVnaW9uLWVkaXRvcj48L3JlZ2lvbi1lZGl0b3I+XG48L3AtZGlhbG9nPlxuXG48IS0tIFNpbXBsaWZ5IChEb3VnbGFz4oCTUGV1Y2tlcik6IGN1c3RvbSBweC10aHJlc2hvbGQgcGlja2VyIGZvciB0aGUgc2VsZWN0ZWRcbiAgICAgcmVnaW9uKHMpLCBvcGVuZWQgZnJvbSB0aGUgcmlnaHQtY2xpY2sgXCJTaW1wbGlmeSDilrggQ3VzdG9t4oCmXCIgYWN0aW9uLiAtLT5cbjxwLWRpYWxvZyBoZWFkZXI9XCJTaW1wbGlmeSByZWdpb24ocylcIiBbKHZpc2libGUpXT1cImRpc3BsYXlTaW1wbGlmeURpYWxvZ1wiIFttb2RhbF09XCJ0cnVlXCJcbiAgICAgICAgICBbc3R5bGVdPVwie3dpZHRoOiAnMjJyZW0nfVwiIFtkcmFnZ2FibGVdPVwiZmFsc2VcIiBbcmVzaXphYmxlXT1cImZhbHNlXCI+XG4gIDxkaXYgY2xhc3M9XCJmbGV4IGZsZXgtY29sdW1uIGdhcC0yXCI+XG4gICAgPHNwYW4+QWx0aXR1ZGUgdGhyZXNob2xkOiA8Yj57eyBzaW1wbGlmeVRocmVzaG9sZCB9fSBweDwvYj48L3NwYW4+XG4gICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwic2ltcGxpZnlUaHJlc2hvbGRcIiBbbWluXT1cIjFcIiBbbWF4XT1cIjIwXCIgW3N0ZXBdPVwiMVwiPjwvcC1zbGlkZXI+XG4gICAgPHNtYWxsPlZlcnRpY2VzIHdpdGhpbiB0aGlzIGRpc3RhbmNlIG9mIHRoZSBsaW5lIGJldHdlZW4gdGhlaXIgbmVpZ2hib3VycyBhcmUgcmVtb3ZlZC48L3NtYWxsPlxuICA8L2Rpdj5cbiAgPG5nLXRlbXBsYXRlIHBUZW1wbGF0ZT1cImZvb3RlclwiPlxuICAgIDxwLWJ1dHRvbiBsYWJlbD1cIkNhbmNlbFwiIFt0ZXh0XT1cInRydWVcIiAob25DbGljayk9XCJkaXNwbGF5U2ltcGxpZnlEaWFsb2cgPSBmYWxzZVwiPjwvcC1idXR0b24+XG4gICAgPHAtYnV0dG9uIGxhYmVsPVwiQXBwbHlcIiBpY29uPVwicGkgcGktY2hlY2tcIiAob25DbGljayk9XCJzaW1wbGlmeVJlZ2lvbnMoc2ltcGxpZnlUaHJlc2hvbGQpXCI+PC9wLWJ1dHRvbj5cbiAgPC9uZy10ZW1wbGF0ZT5cbjwvcC1kaWFsb2c+XG5cbiJdfQ==
|