@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,2137 @@
|
|
|
1
|
+
import { Injectable, Inject, Optional } from '@angular/core';
|
|
2
|
+
import * as Plotly from 'plotly.js-dist-min';
|
|
3
|
+
import { Image } from 'image-js';
|
|
4
|
+
import { HttpClient } from '@angular/common/http';
|
|
5
|
+
import { firstValueFrom } from 'rxjs';
|
|
6
|
+
import { Polygon, Rectangle, Region } from '../../models/region';
|
|
7
|
+
import { Buffer } from 'buffer';
|
|
8
|
+
import { TILE_ACCESS_PORT } from '../../contracts/ports/tile-access.port';
|
|
9
|
+
import { IMAGE_STATE_PORT } from '../../contracts/ports/image-state.port';
|
|
10
|
+
import { BehaviorSubject, EMPTY, Subject, combineLatest, of } from 'rxjs';
|
|
11
|
+
import { MessageService } from 'primeng/api';
|
|
12
|
+
import { ShapeSelection } from '../../models/shape';
|
|
13
|
+
import { CONFIG, CONFIG_SURFACE, PlotUtilities } from '../../plot.utilities';
|
|
14
|
+
import { WandService } from '../../toolbar/wand/wand.service';
|
|
15
|
+
import { WandToolService } from '../../toolbar/wand/wand-tool.service';
|
|
16
|
+
import { BrushToolService } from '../../toolbar/brush/brush-tool.service';
|
|
17
|
+
import { SamToolService } from '../../toolbar/segmentation/sam-tool.service';
|
|
18
|
+
import { SamPointToolService } from '../../toolbar/segmentation/sam-point-tool.service';
|
|
19
|
+
import { CellSegmentToolService } from '../../toolbar/segmentation/cell-segment-tool.service';
|
|
20
|
+
import { CELL_SEGMENTER } from '../../contracts/cell-segmenter.contract';
|
|
21
|
+
import { VertexEraserToolService } from '../../toolbar/vertex-eraser/vertex-eraser-tool.service';
|
|
22
|
+
import { ZoomToBoxToolService } from '../../toolbar/zoom-to-box/zoom-to-box-tool.service';
|
|
23
|
+
import { PlotType, PLOT_TYPE_DESCRIPTORS } from '../../contracts/plot-type';
|
|
24
|
+
import { PLOTLY_PLOT_TYPE_IMPLS, } from './plotly-trace-builders';
|
|
25
|
+
import { bt601Luminance, histogram256 } from '../../contracts/intensity';
|
|
26
|
+
import { ViewerFeature, capabilitiesOf } from '../../contracts/capabilities.contract';
|
|
27
|
+
import { PlotlyRegionOverlay } from './plotly-region-overlay';
|
|
28
|
+
import { PlotlyCoordinateTransform } from './plotly-coordinate-transform';
|
|
29
|
+
import { VisualizerStore } from '../../store/visualizer-store.service';
|
|
30
|
+
import { RegionStore } from '../../store/region-store.service';
|
|
31
|
+
import * as i0 from "@angular/core";
|
|
32
|
+
import * as i1 from "primeng/api";
|
|
33
|
+
import * as i2 from "@angular/common/http";
|
|
34
|
+
import * as i3 from "../../toolbar/wand/wand.service";
|
|
35
|
+
import * as i4 from "../../toolbar/wand/wand-tool.service";
|
|
36
|
+
import * as i5 from "../../toolbar/brush/brush-tool.service";
|
|
37
|
+
import * as i6 from "../../toolbar/segmentation/sam-tool.service";
|
|
38
|
+
import * as i7 from "../../toolbar/segmentation/sam-point-tool.service";
|
|
39
|
+
import * as i8 from "../../toolbar/segmentation/cell-segment-tool.service";
|
|
40
|
+
import * as i9 from "../../toolbar/vertex-eraser/vertex-eraser-tool.service";
|
|
41
|
+
import * as i10 from "../../toolbar/zoom-to-box/zoom-to-box-tool.service";
|
|
42
|
+
import * as i11 from "../../store/visualizer-store.service";
|
|
43
|
+
import * as i12 from "../../store/region-store.service";
|
|
44
|
+
// Re-exported so existing consumers can keep importing PlotType from this
|
|
45
|
+
// module while it physically lives in the backend-neutral contracts/ dir.
|
|
46
|
+
// TODO(plotting-abstraction): once all consumers import from
|
|
47
|
+
// './contracts/plot-type' directly, drop this re-export.
|
|
48
|
+
export { PlotType } from '../../contracts/plot-type';
|
|
49
|
+
window.Buffer = Buffer;
|
|
50
|
+
export class PlotlyService {
|
|
51
|
+
constructor(tiles, state, messageService, http, wandService, wandTool, brushTool, samTool, samPointTool, cellSegmentTool, cellSegmenter, vertexEraserTool, zoomToBoxTool, store, regionStore) {
|
|
52
|
+
this.tiles = tiles;
|
|
53
|
+
this.state = state;
|
|
54
|
+
this.messageService = messageService;
|
|
55
|
+
this.http = http;
|
|
56
|
+
this.wandService = wandService;
|
|
57
|
+
this.wandTool = wandTool;
|
|
58
|
+
this.brushTool = brushTool;
|
|
59
|
+
this.samTool = samTool;
|
|
60
|
+
this.samPointTool = samPointTool;
|
|
61
|
+
this.cellSegmentTool = cellSegmentTool;
|
|
62
|
+
this.cellSegmenter = cellSegmenter;
|
|
63
|
+
this.vertexEraserTool = vertexEraserTool;
|
|
64
|
+
this.zoomToBoxTool = zoomToBoxTool;
|
|
65
|
+
this.store = store;
|
|
66
|
+
this.regionStore = regionStore;
|
|
67
|
+
/**
|
|
68
|
+
* Plotly is the full-featured data backend: it supports every feature,
|
|
69
|
+
* including 3D scenes, live scalar colormaps, pixel readback and the
|
|
70
|
+
* server-side high-def zoom re-fetch. (OpenSeadragon's stub advertises only
|
|
71
|
+
* image display.)
|
|
72
|
+
*/
|
|
73
|
+
this.capabilities = capabilitiesOf([
|
|
74
|
+
ViewerFeature.ImageDisplay,
|
|
75
|
+
ViewerFeature.Surface3D,
|
|
76
|
+
ViewerFeature.ScalarColormap,
|
|
77
|
+
ViewerFeature.PixelReadback,
|
|
78
|
+
ViewerFeature.HighDefZoom,
|
|
79
|
+
ViewerFeature.StackSlider,
|
|
80
|
+
ViewerFeature.Isosurface,
|
|
81
|
+
]);
|
|
82
|
+
this.shapes = [];
|
|
83
|
+
this.previousShapes = [];
|
|
84
|
+
this.isRegionSavedOn = true;
|
|
85
|
+
this.isRealZoom = true;
|
|
86
|
+
this.scaleratio = true;
|
|
87
|
+
this.zoomCoordinates = [];
|
|
88
|
+
this.plotUtilities = new PlotUtilities();
|
|
89
|
+
this.onPlotMouseDown = null;
|
|
90
|
+
this.cachedImageWidth = 0;
|
|
91
|
+
this.cachedImageHeight = 0;
|
|
92
|
+
this.cachedImageRatios = [1, 1];
|
|
93
|
+
this.cachedIsGrayscale = false;
|
|
94
|
+
/** Data-space coordinate of the cached frame's pixel (0,0). [0,0] for the full
|
|
95
|
+
* image; a crop's top-left when zoomed (so the intensity profile samples the
|
|
96
|
+
* zoom-level pixels at the right offset/resolution). */
|
|
97
|
+
this.cachedFrameOrigin = [0, 0];
|
|
98
|
+
/** Last visible image-pixel rectangle from the active backend's viewport (set by
|
|
99
|
+
* the OSD viewport-change hook via refreshIntensitySamplingForRoi). New profile lines are
|
|
100
|
+
* placed within it so they land on-screen when zoomed in; null → use full image. */
|
|
101
|
+
this.lastVisibleRoi = null;
|
|
102
|
+
this.stackLoading$ = new BehaviorSubject(false);
|
|
103
|
+
this.stackLoadingProgress$ = new BehaviorSubject(0);
|
|
104
|
+
// current index of image in stack (if stack), 0 if single image
|
|
105
|
+
this.zIndex = new BehaviorSubject(0);
|
|
106
|
+
this.autoscaleEvent = new Subject();
|
|
107
|
+
// The region update event and selection stream are owned by the shared
|
|
108
|
+
// RegionStore; getRegionUpdateEvent()/getSelectedShapeIndices$() delegate to
|
|
109
|
+
// it so every consumer (and the OSD backend) sees one stream.
|
|
110
|
+
/** Bright, well-separated colours cycled as the user adds profile-line ROIs.
|
|
111
|
+
* The matching inset trace is drawn in the same colour. */
|
|
112
|
+
this.PROFILE_PALETTE = [
|
|
113
|
+
'#FFD400', // yellow
|
|
114
|
+
'#00E5FF', // cyan
|
|
115
|
+
'#FF2D95', // magenta
|
|
116
|
+
'#39FF14', // green
|
|
117
|
+
'#FF9500', // orange
|
|
118
|
+
'#7C4DFF', // violet
|
|
119
|
+
'#FF3B30', // red
|
|
120
|
+
'#18FFFF', // aqua
|
|
121
|
+
];
|
|
122
|
+
/** Palette cursor for profile lines (never reset on delete, so colours keep
|
|
123
|
+
* cycling across the session). */
|
|
124
|
+
this.profileColorSeq = 0;
|
|
125
|
+
this.intensityProfile$ = new Subject();
|
|
126
|
+
/** ISOSURFACE band as a 0–255 slider position, mapped onto the volume's real
|
|
127
|
+
* intensity range at render time (see mapIsoBand). Defaults to the full range
|
|
128
|
+
* so the first render shows the whole structure regardless of how bright or
|
|
129
|
+
* dark the volume is; the user then narrows the band to isolate intensities. */
|
|
130
|
+
this.isoMin = 0;
|
|
131
|
+
this.isoMax = 255;
|
|
132
|
+
/** Actual intensity range of the volume on screen ([min,max] over the loaded
|
|
133
|
+
* frames). The slider spans 0–255, but a given stack often occupies only a
|
|
134
|
+
* sub-range, so we clamp the iso band into this range (with a small margin)
|
|
135
|
+
* to guarantee non-degenerate surfaces — both on first render and on live
|
|
136
|
+
* slider moves. Null until an isosurface volume has been measured. */
|
|
137
|
+
this.isoDataRange = null;
|
|
138
|
+
// Id minting and the selection index stream are owned by the shared
|
|
139
|
+
// RegionStore. Selection still drives Plotly's `_activeShapeIndex` (so a
|
|
140
|
+
// single shape gets the edit handles) — see setSelectedShapeIndices().
|
|
141
|
+
this.imageCached = false;
|
|
142
|
+
this.coordinateTransform = new PlotlyCoordinateTransform(() => document.getElementById(this.plotDiv), () => this.getOverlayContainer());
|
|
143
|
+
// relayout event router
|
|
144
|
+
this.onRelayoutEvent = (event) => { this.relayoutEventHandler(event); };
|
|
145
|
+
// Tool services read/mutate our state via small *Host interfaces. Stored
|
|
146
|
+
// so we can re-bind them to ourselves whenever a Plotly tool is activated
|
|
147
|
+
// (the OpenSeadragon backend re-binds the same singleton tools to itself).
|
|
148
|
+
this.wandHost = {
|
|
149
|
+
getRegions: () => this.regionStore.getRegions(),
|
|
150
|
+
setRegions: (regions) => this.setRegions(regions),
|
|
151
|
+
getCachedImageData: () => this.getCachedImageData(),
|
|
152
|
+
getActiveFrameIndex: () => this.activeFrameIndex(),
|
|
153
|
+
getOverlayContainer: () => this.getOverlayContainer(),
|
|
154
|
+
getCoordinateTransform: () => this.getCoordinateTransform(),
|
|
155
|
+
getFileName: () => this.fileName,
|
|
156
|
+
getShapeColor: () => this.regionStore.getShapeColor(),
|
|
157
|
+
};
|
|
158
|
+
this.eraserHost = {
|
|
159
|
+
getRegions: () => this.regionStore.getRegions(),
|
|
160
|
+
setRegions: (regions) => this.setRegions(regions),
|
|
161
|
+
invalidateWandRegion: () => this.wandTool.clearActiveRegion(),
|
|
162
|
+
getOverlayContainer: () => this.getOverlayContainer(),
|
|
163
|
+
getCoordinateTransform: () => this.getCoordinateTransform(),
|
|
164
|
+
getCachedImageRatio: () => this.cachedImageRatios[0] || 1,
|
|
165
|
+
};
|
|
166
|
+
this.wandTool.bindHost(this.wandHost);
|
|
167
|
+
// The brush reuses the wand host (same coordinate frame + region access).
|
|
168
|
+
this.brushTool.bindHost(this.wandHost);
|
|
169
|
+
this.vertexEraserTool.bindHost(this.eraserHost);
|
|
170
|
+
this.bindZoomToBoxHost();
|
|
171
|
+
this.imageCachedSubscription = this.state.isImageCached$().subscribe(imageCached => {
|
|
172
|
+
this.imageCached = imageCached;
|
|
173
|
+
});
|
|
174
|
+
this.filenameSubscription = this.state.getFilename$().subscribe(filename => {
|
|
175
|
+
this.fileName = filename;
|
|
176
|
+
});
|
|
177
|
+
// Live recolor from the Channels & Histogram pane: restyle the display window (zmin/zmax for
|
|
178
|
+
// a heatmap, cmin/cmax for isosurface/surface), the colorscale (colormap), and reverse/invert
|
|
179
|
+
// when the channel state OR the colormap changes. (Gamma is applied by the OSD image view; the
|
|
180
|
+
// Plotly colorscale window covers the common contrast case.)
|
|
181
|
+
this.channelSub = combineLatest([
|
|
182
|
+
this.store.getChannelStates(),
|
|
183
|
+
this.store.getReverseScale(),
|
|
184
|
+
this.store.getInvert(),
|
|
185
|
+
this.store.getColormap(),
|
|
186
|
+
]).subscribe(([channels, rev, inv]) => this.applyChannelDisplay(channels, rev, inv));
|
|
187
|
+
// Profile lines are store regions; any region change (add/drag/delete, on
|
|
188
|
+
// either backend) should refresh the inset traces. The live-edit stream
|
|
189
|
+
// fires per frame during a drag (OSD coalesces regionUpdate$ until release),
|
|
190
|
+
// so the inset tracks an OSD line ROI live as it's moved.
|
|
191
|
+
this.regionUpdateSubscription = this.regionStore.getRegionUpdateEvent().subscribe(() => {
|
|
192
|
+
this.emitProfiles();
|
|
193
|
+
});
|
|
194
|
+
this.regionLiveEditSubscription = this.regionStore.getRegionLiveEdit$().subscribe(() => {
|
|
195
|
+
this.emitProfiles();
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
getTrueImageSize() {
|
|
199
|
+
if (!this.trueImgSize)
|
|
200
|
+
return null;
|
|
201
|
+
return {
|
|
202
|
+
width: this.trueImgSize[1] - this.trueImgSize[0],
|
|
203
|
+
height: this.trueImgSize[3] - this.trueImgSize[2],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Load image
|
|
208
|
+
* @param imageInfo
|
|
209
|
+
* @param zIndex index of the image to load
|
|
210
|
+
* @return an object with data and ratio keys
|
|
211
|
+
*/
|
|
212
|
+
async load(imageInfo, zIndex) {
|
|
213
|
+
const urls = imageInfo.urls;
|
|
214
|
+
// use zIndex provided if any, 0 otherwise
|
|
215
|
+
let imageUrl;
|
|
216
|
+
if (zIndex) {
|
|
217
|
+
imageUrl = urls[zIndex];
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
imageUrl = urls[0];
|
|
221
|
+
}
|
|
222
|
+
const isGrayscale = imageInfo.isGrayscale;
|
|
223
|
+
const image = await this.loadImage(imageUrl);
|
|
224
|
+
const xRatio = imageInfo.trueImageSize[0] / image.width;
|
|
225
|
+
const yRatio = imageInfo.trueImageSize[1] / image.height;
|
|
226
|
+
const trueImageSize = [];
|
|
227
|
+
// [x0, x1, y0, y1]
|
|
228
|
+
trueImageSize[0] = 0;
|
|
229
|
+
trueImageSize[1] = imageInfo.trueImageSize[0];
|
|
230
|
+
trueImageSize[2] = 0;
|
|
231
|
+
trueImageSize[3] = imageInfo.trueImageSize[1];
|
|
232
|
+
this.trueImgSize = trueImageSize;
|
|
233
|
+
this.fileName = imageInfo.fileName;
|
|
234
|
+
if (imageInfo.isStack && imageInfo.showStack) {
|
|
235
|
+
const images = [];
|
|
236
|
+
this.stackLoadingProgress$.next(0);
|
|
237
|
+
// One URL per slice — load them all (earlier `length-1` dropped the last).
|
|
238
|
+
for (let i = 0; i < urls.length; i++) {
|
|
239
|
+
console.log(`image stack ${i}`);
|
|
240
|
+
// do not keep with loading if filename is different (a new file has been selected)
|
|
241
|
+
// or if the stackLoading value is set to false
|
|
242
|
+
if (this.fileName === imageInfo.fileName && this.stackLoading$.value) {
|
|
243
|
+
const image = await this.loadImage(urls[i]);
|
|
244
|
+
if (isGrayscale) {
|
|
245
|
+
const grey = image.grey();
|
|
246
|
+
const imgData = this.plotUtilities.arrayToMatrix(Array.from(grey.data), image.width);
|
|
247
|
+
images.push(imgData);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
const rgbData = image.getPixelsArray();
|
|
251
|
+
const rgbMatrix = this.plotUtilities.arrayToMatrix(rgbData, image.width);
|
|
252
|
+
images.push(rgbMatrix);
|
|
253
|
+
}
|
|
254
|
+
this.stackLoadingProgress$.next(Math.round((i * 100) / urls.length));
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// break stack loading
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// reset stackLoading progress to 0
|
|
262
|
+
this.stackLoadingProgress$.next(0);
|
|
263
|
+
return { data: images, ratios: [xRatio, yRatio],
|
|
264
|
+
sizes: [image.width, image.height],
|
|
265
|
+
filename: imageInfo.fileName };
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
let imageData;
|
|
269
|
+
if (isGrayscale) {
|
|
270
|
+
const grey = image.grey();
|
|
271
|
+
imageData = this.plotUtilities.arrayToMatrix(Array.from(grey.data), image.width);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
const rgbData = image.getPixelsArray();
|
|
275
|
+
imageData = this.plotUtilities.arrayToMatrix(rgbData, image.width);
|
|
276
|
+
}
|
|
277
|
+
return { data: [imageData],
|
|
278
|
+
ratios: [xRatio, yRatio],
|
|
279
|
+
sizes: [image.width, image.height],
|
|
280
|
+
filename: imageInfo.fileName };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
*
|
|
285
|
+
* @param plotDiv
|
|
286
|
+
* @param imageLoaded object with data, ratios and sizes key
|
|
287
|
+
* @param imageInfo ImageInfo object
|
|
288
|
+
* @param screenHeight size of the available screen height
|
|
289
|
+
* @param plotType type of plotting
|
|
290
|
+
*/
|
|
291
|
+
/**
|
|
292
|
+
* @param inPlace when true, updates the existing plot via Plotly.react
|
|
293
|
+
* instead of Plotly.purge + Plotly.newPlot. Used by the multi-tier
|
|
294
|
+
* diagram swap (small → large) so the canvas doesn't briefly blank
|
|
295
|
+
* between phases.
|
|
296
|
+
*/
|
|
297
|
+
plot(plotDiv, imageLoaded, imageInfo, screenHeight, plotType, inPlace = false) {
|
|
298
|
+
const trueImageSize = [];
|
|
299
|
+
this.zoomCoordinates = [];
|
|
300
|
+
// [x0, x1, y0, y1]
|
|
301
|
+
trueImageSize[0] = 0;
|
|
302
|
+
trueImageSize[1] = imageInfo.trueImageSize[0];
|
|
303
|
+
trueImageSize[2] = 0;
|
|
304
|
+
trueImageSize[3] = imageInfo.trueImageSize[1];
|
|
305
|
+
// Save the current image's regions and pull in any cached regions for the
|
|
306
|
+
// image we're about to display, before Plotly.newPlot reads `this.shapes`.
|
|
307
|
+
this.setActiveImage(imageInfo);
|
|
308
|
+
this.imageInfo = imageInfo;
|
|
309
|
+
this.plotType = plotType;
|
|
310
|
+
// Cache pixel matrices so the wand tool can sample them.
|
|
311
|
+
this.cachedImageFrames = imageLoaded.data;
|
|
312
|
+
this.cachedImageWidth = imageLoaded.sizes[0];
|
|
313
|
+
this.cachedImageHeight = imageLoaded.sizes[1];
|
|
314
|
+
this.cachedImageRatios = imageLoaded.ratios;
|
|
315
|
+
this.cachedFrameOrigin = [0, 0]; // full image — reset any prior zoom-crop origin
|
|
316
|
+
this.cachedIsGrayscale = !!imageInfo.isGrayscale;
|
|
317
|
+
// Pluggable plot types (contour, scatter, scatter3d, isosurface) render
|
|
318
|
+
// through the trace-builder registry. The original HEATMAP/SURFACE/RGB-image
|
|
319
|
+
// renderers keep their dedicated paths below (also reused by the high-def
|
|
320
|
+
// zoom re-fetch). Intensity profiles are no longer a plot type — they're
|
|
321
|
+
// Region-based line ROIs, available in HEATMAP/IMAGE mode.
|
|
322
|
+
const impl = PLOTLY_PLOT_TYPE_IMPLS[plotType];
|
|
323
|
+
if (impl) {
|
|
324
|
+
this.imageInfo.isGrayscale = !!imageInfo.isGrayscale;
|
|
325
|
+
return this.plotViaRegistry(plotDiv, impl, this.buildTraceInput(imageInfo, imageLoaded, trueImageSize), screenHeight, inPlace);
|
|
326
|
+
}
|
|
327
|
+
if (imageInfo.isGrayscale) {
|
|
328
|
+
if (plotType === PlotType.SURFACE) {
|
|
329
|
+
return this.plotSurface(plotDiv, imageInfo.urls, imageLoaded.data, trueImageSize, imageLoaded.ratios, screenHeight, inPlace);
|
|
330
|
+
}
|
|
331
|
+
return this.plotHeatmap(plotDiv, imageInfo.urls, imageLoaded.data, trueImageSize, imageLoaded.ratios, screenHeight, inPlace);
|
|
332
|
+
}
|
|
333
|
+
return this.plotRGBHeatmap(plotDiv, imageInfo.urls, imageLoaded.data, trueImageSize, imageLoaded.ratios, imageLoaded.sizes[0], imageLoaded.sizes[1], screenHeight, inPlace);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Function used to plot a grayscale heatmap
|
|
337
|
+
* @param plotDiv
|
|
338
|
+
* @param urls
|
|
339
|
+
* @param images
|
|
340
|
+
* @param trueImgSize array containing the following : [x0, x1, y0, y1]
|
|
341
|
+
* @param ratios
|
|
342
|
+
* @param screenHeight
|
|
343
|
+
* @return Promise true when plotting is finished
|
|
344
|
+
*/
|
|
345
|
+
plotHeatmap(plotDiv, urls, images, trueImgSize, ratios, screenHeight, inPlace = false) {
|
|
346
|
+
if (!inPlace)
|
|
347
|
+
Plotly.purge(plotDiv);
|
|
348
|
+
this.plotDiv = plotDiv;
|
|
349
|
+
this.imageLength = images.length;
|
|
350
|
+
this.urls = urls;
|
|
351
|
+
this.screenHeight = screenHeight;
|
|
352
|
+
// plot
|
|
353
|
+
const traces = [];
|
|
354
|
+
images.forEach((dataset, index) => {
|
|
355
|
+
const trace = {
|
|
356
|
+
x0: trueImgSize[0],
|
|
357
|
+
dx: ratios[0],
|
|
358
|
+
y0: trueImgSize[2],
|
|
359
|
+
dy: ratios[0],
|
|
360
|
+
z: dataset,
|
|
361
|
+
type: 'heatmap',
|
|
362
|
+
// We remove the tooltip annotation for performances reasons
|
|
363
|
+
// text: dataset.map((row: any[], i: any) => row.map((item, j) => {
|
|
364
|
+
// return `x: ${+(j * ratios[0]).toFixed(2)}<br>y: ${+(i * ratios[0]).toFixed(2)}<br>value: ${item}`})),
|
|
365
|
+
hoverinfo: 'none',
|
|
366
|
+
colorscale: this.store.currentColormap().data.value,
|
|
367
|
+
reversescale: this.store.currentReverseScale(),
|
|
368
|
+
name: `Slice ${index + 1}`,
|
|
369
|
+
visible: index === 0
|
|
370
|
+
};
|
|
371
|
+
traces.push(trace);
|
|
372
|
+
});
|
|
373
|
+
const render = inPlace ? Plotly.react : Plotly.newPlot;
|
|
374
|
+
return render(plotDiv, traces, this.getHeatmapLayout([trueImgSize[0], trueImgSize[1]], [trueImgSize[3],
|
|
375
|
+
trueImgSize[2]]), CONFIG).then(() => {
|
|
376
|
+
// handle the relayout event for zoom / rois and the click event
|
|
377
|
+
this.setEvents(plotDiv, true, screenHeight);
|
|
378
|
+
return true;
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Function used to plot a grayscale surface
|
|
383
|
+
* @param plotDiv
|
|
384
|
+
* @param urls
|
|
385
|
+
* @param images
|
|
386
|
+
* @param trueImgSize array containing the following : [x0, x1, y0, y1]
|
|
387
|
+
* @param ratios
|
|
388
|
+
* @param screenHeight
|
|
389
|
+
* @return Promise true when plotting is finished
|
|
390
|
+
*/
|
|
391
|
+
plotSurface(plotDiv, urls, images, trueImgSize, ratios, screenHeight, inPlace = false) {
|
|
392
|
+
if (!inPlace)
|
|
393
|
+
Plotly.purge(plotDiv);
|
|
394
|
+
this.plotDiv = plotDiv;
|
|
395
|
+
this.imageLength = images.length;
|
|
396
|
+
this.urls = urls;
|
|
397
|
+
this.screenHeight = screenHeight;
|
|
398
|
+
// plot
|
|
399
|
+
const traces = [];
|
|
400
|
+
images.forEach((dataset, index) => {
|
|
401
|
+
const trace = {
|
|
402
|
+
z: dataset,
|
|
403
|
+
type: 'surface',
|
|
404
|
+
// hoverinfo: 'none',
|
|
405
|
+
colorscale: this.store.currentColormap().data.value,
|
|
406
|
+
reversescale: this.store.currentReverseScale(),
|
|
407
|
+
// name: `Slice ${index + 1}`,
|
|
408
|
+
// visible: index === 0
|
|
409
|
+
};
|
|
410
|
+
traces.push(trace);
|
|
411
|
+
});
|
|
412
|
+
const xrange = [trueImgSize[0], trueImgSize[1]];
|
|
413
|
+
const yrange = [trueImgSize[2], trueImgSize[3]];
|
|
414
|
+
const render = inPlace ? Plotly.react : Plotly.newPlot;
|
|
415
|
+
return render(plotDiv, traces, this.getSurfaceLayout(xrange, yrange, 0.4), CONFIG_SURFACE).then(() => {
|
|
416
|
+
// handle the relayout event for zoom / rois and the click event
|
|
417
|
+
this.setEvents(plotDiv, true, screenHeight);
|
|
418
|
+
return true;
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Function used to plot an RGB heatmap
|
|
423
|
+
* @param plotDiv
|
|
424
|
+
* @param urls
|
|
425
|
+
* @param images
|
|
426
|
+
* @param trueImgSize
|
|
427
|
+
* @param width
|
|
428
|
+
* @param height
|
|
429
|
+
* @param ratios
|
|
430
|
+
* @param screenHeight
|
|
431
|
+
* @return Promise
|
|
432
|
+
*/
|
|
433
|
+
plotRGBHeatmap(plotDiv, urls, images, trueImgSize, ratios, width, height, screenHeight, inPlace = false) {
|
|
434
|
+
// Plotly.react updates in place; Plotly.purge + Plotly.newPlot blanks
|
|
435
|
+
// the canvas for ~100ms which makes the multi-tier small→large swap
|
|
436
|
+
// look like a regression to "loading" before the sharper version
|
|
437
|
+
// appears.
|
|
438
|
+
if (!inPlace)
|
|
439
|
+
Plotly.purge(plotDiv);
|
|
440
|
+
this.plotDiv = plotDiv;
|
|
441
|
+
this.imageLength = images.length;
|
|
442
|
+
this.urls = urls;
|
|
443
|
+
this.screenHeight = screenHeight;
|
|
444
|
+
// as autorange is set to false, and not reversed we need to set the yrange correcly here
|
|
445
|
+
const layout = this.getHeatmapLayout([trueImgSize[0], trueImgSize[1]], [trueImgSize[3], trueImgSize[2]]);
|
|
446
|
+
const traces = [];
|
|
447
|
+
images.forEach((dataset, index) => {
|
|
448
|
+
const trace = {
|
|
449
|
+
x0: trueImgSize[0],
|
|
450
|
+
dx: ratios[0],
|
|
451
|
+
y0: trueImgSize[2],
|
|
452
|
+
dy: ratios[0],
|
|
453
|
+
x: Array.from(Array(width).keys()),
|
|
454
|
+
y: Array.from(Array(height).keys()),
|
|
455
|
+
z: dataset,
|
|
456
|
+
// text: dataset.map((row: any[], i: any) => row.map((item, j) => {
|
|
457
|
+
// return `x: ${+(j * ratios[0]).toFixed(2)}<br>y: ${+(i * ratios[0]).toFixed(2)}<br>value: ${item}`})),
|
|
458
|
+
hoverinfo: 'none',
|
|
459
|
+
type: 'image',
|
|
460
|
+
name: `Slice ${index + 1}`,
|
|
461
|
+
visible: index === 0
|
|
462
|
+
};
|
|
463
|
+
traces.push(trace);
|
|
464
|
+
});
|
|
465
|
+
const render = inPlace ? Plotly.react : Plotly.newPlot;
|
|
466
|
+
return render(plotDiv, traces, layout, CONFIG).then(() => {
|
|
467
|
+
// handle the relayout event
|
|
468
|
+
this.setEvents(plotDiv, false, screenHeight);
|
|
469
|
+
return true;
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Assemble the normalised input the pluggable trace builders consume.
|
|
474
|
+
* Pure data only — no Plotly handles — so the builders stay backend-neutral.
|
|
475
|
+
*/
|
|
476
|
+
buildTraceInput(imageInfo, imageLoaded, trueImageSize) {
|
|
477
|
+
// Measure the volume's real intensity range so the iso band can be clamped
|
|
478
|
+
// into it (the slider is a fixed 0–255 but a stack may occupy only part of
|
|
479
|
+
// that, which would otherwise leave the surfaces with nothing to cross).
|
|
480
|
+
if (this.plotType === PlotType.ISOSURFACE) {
|
|
481
|
+
this.isoDataRange = this.measureIntensityRange(imageLoaded.data, !!imageInfo.isGrayscale);
|
|
482
|
+
}
|
|
483
|
+
const [isoMin, isoMax] = this.mapIsoBand(this.isoMin, this.isoMax);
|
|
484
|
+
return {
|
|
485
|
+
frames: imageLoaded.data,
|
|
486
|
+
width: imageLoaded.sizes[0],
|
|
487
|
+
height: imageLoaded.sizes[1],
|
|
488
|
+
ratios: imageLoaded.ratios,
|
|
489
|
+
trueImageSize,
|
|
490
|
+
isGrayscale: !!imageInfo.isGrayscale,
|
|
491
|
+
colorscale: this.store.currentColormap().data.value,
|
|
492
|
+
reversescale: this.store.currentReverseScale(),
|
|
493
|
+
regions: this.getRegionPolygons(),
|
|
494
|
+
shapeColor: this.regionStore.getShapeColor(),
|
|
495
|
+
isoMin,
|
|
496
|
+
isoMax,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
/** Min/max intensity over every voxel of the loaded frames (RGB → luminance),
|
|
500
|
+
* used to keep the iso band inside the data. Returns null for empty input. */
|
|
501
|
+
measureIntensityRange(frames, isGrayscale) {
|
|
502
|
+
let min = Infinity, max = -Infinity;
|
|
503
|
+
for (const frame of frames || []) {
|
|
504
|
+
for (const row of frame || []) {
|
|
505
|
+
for (const cell of row || []) {
|
|
506
|
+
const v = isGrayscale ? cell
|
|
507
|
+
: 0.299 * cell[0] + 0.587 * cell[1] + 0.114 * cell[2];
|
|
508
|
+
if (!Number.isFinite(v))
|
|
509
|
+
continue;
|
|
510
|
+
if (v < min)
|
|
511
|
+
min = v;
|
|
512
|
+
if (v > max)
|
|
513
|
+
max = v;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return (Number.isFinite(min) && Number.isFinite(max) && max > min) ? [min, max] : null;
|
|
518
|
+
}
|
|
519
|
+
/** Map the 0–255 slider band onto the volume's actual intensity range. The
|
|
520
|
+
* slider is fixed 0–255, but a stack often occupies only a narrow sub-range
|
|
521
|
+
* (e.g. this EDOF volume is ~2–50 with structure near 6–10). Mapping spreads
|
|
522
|
+
* the data across the whole slider so every intensity is easy to reach and
|
|
523
|
+
* the band always lands inside the data — a small inset keeps the extreme
|
|
524
|
+
* surfaces off the exact data edges, which are degenerate and draw nothing. */
|
|
525
|
+
mapIsoBand(isoMin, isoMax) {
|
|
526
|
+
let lo = Math.min(isoMin, isoMax);
|
|
527
|
+
let hi = Math.max(isoMin, isoMax);
|
|
528
|
+
if (this.isoDataRange) {
|
|
529
|
+
const [vMin, vMax] = this.isoDataRange;
|
|
530
|
+
const span = vMax - vMin;
|
|
531
|
+
const pad = 0.03 * span;
|
|
532
|
+
const usable = span - 2 * pad;
|
|
533
|
+
const SLIDER_MAX = 255; // toolbar slider domain (isoValueMax)
|
|
534
|
+
lo = vMin + pad + (lo / SLIDER_MAX) * usable;
|
|
535
|
+
hi = vMin + pad + (hi / SLIDER_MAX) * usable;
|
|
536
|
+
if (lo > hi) {
|
|
537
|
+
const t = lo;
|
|
538
|
+
lo = hi;
|
|
539
|
+
hi = t;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return [lo, hi];
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Update the isosurface intensity bounds. When isosurface is on screen this
|
|
546
|
+
* restyles `isomin`/`isomax` in place for a live update (no volume rebuild).
|
|
547
|
+
* Slider values are mapped onto the volume's real range so the surfaces always
|
|
548
|
+
* have data to cross (matching the initial render).
|
|
549
|
+
*/
|
|
550
|
+
setIsoRange(isoMin, isoMax) {
|
|
551
|
+
this.isoMin = isoMin;
|
|
552
|
+
this.isoMax = isoMax;
|
|
553
|
+
if (this.plotType === PlotType.ISOSURFACE && this.plotDiv) {
|
|
554
|
+
const [lo, hi] = this.mapIsoBand(isoMin, isoMax);
|
|
555
|
+
try {
|
|
556
|
+
Plotly.restyle(this.plotDiv, { isomin: [lo], isomax: [hi] });
|
|
557
|
+
}
|
|
558
|
+
catch { /* plot not ready */ }
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/** Plotly renders isosurfaces, so it exposes the isosurface controls (itself). */
|
|
562
|
+
getIsosurfaceControls() { return this; }
|
|
563
|
+
/**
|
|
564
|
+
* Render a registry-backed plot type: build its traces from the input, pick
|
|
565
|
+
* the matching layout, render, and wire the usual relayout/click events.
|
|
566
|
+
* Mirrors the structure of the dedicated heatmap/surface renderers.
|
|
567
|
+
*/
|
|
568
|
+
plotViaRegistry(plotDiv, impl, input, screenHeight, inPlace = false) {
|
|
569
|
+
if (!inPlace)
|
|
570
|
+
Plotly.purge(plotDiv);
|
|
571
|
+
this.plotDiv = plotDiv;
|
|
572
|
+
this.imageLength = input.frames.length;
|
|
573
|
+
this.urls = this.imageInfo?.urls ?? this.urls;
|
|
574
|
+
this.screenHeight = screenHeight;
|
|
575
|
+
const traces = impl.buildTraces(input);
|
|
576
|
+
const [x0, x1, y0, y1] = input.trueImageSize;
|
|
577
|
+
let layout;
|
|
578
|
+
switch (impl.layoutKind) {
|
|
579
|
+
case '3d-volume':
|
|
580
|
+
layout = this.getVolumeLayout();
|
|
581
|
+
break;
|
|
582
|
+
case '2d-chart':
|
|
583
|
+
layout = this.getChartLayout();
|
|
584
|
+
break;
|
|
585
|
+
case '2d-overlay':
|
|
586
|
+
layout = this.getOverlayLayout([x0, x1], [y1, y0]); // reversed y like the image
|
|
587
|
+
break;
|
|
588
|
+
default: // '2d-image'
|
|
589
|
+
layout = this.getHeatmapLayout([x0, x1], [y1, y0]);
|
|
590
|
+
}
|
|
591
|
+
const config = impl.threeD ? CONFIG_SURFACE : CONFIG;
|
|
592
|
+
const render = inPlace ? Plotly.react : Plotly.newPlot;
|
|
593
|
+
return render(plotDiv, traces, layout, config).then(() => {
|
|
594
|
+
this.setEvents(plotDiv, input.isGrayscale, screenHeight);
|
|
595
|
+
return true;
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
/** Plain 2D chart axes (intensity profile / line plots). */
|
|
599
|
+
getChartLayout() {
|
|
600
|
+
return {
|
|
601
|
+
margin: { t: 30, b: 45, l: 60, r: 20 },
|
|
602
|
+
height: this.screenHeight,
|
|
603
|
+
autosize: true,
|
|
604
|
+
xaxis: { title: 'Position (px)' },
|
|
605
|
+
yaxis: { title: 'Intensity' },
|
|
606
|
+
dragmode: false,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
/** Image-aligned 2D axes without the z-plane slider (region scatter). */
|
|
610
|
+
getOverlayLayout(xRange, yRange) {
|
|
611
|
+
return {
|
|
612
|
+
xaxis: { constrain: 'range', constraintoward: 'center', side: 'top', ticks: '', range: xRange },
|
|
613
|
+
yaxis: {
|
|
614
|
+
constrain: 'range', constraintoward: 'center', range: yRange, ticks: '',
|
|
615
|
+
ticksuffix: ' ', autorange: false, scaleanchor: this.scaleratio ? 'x' : false,
|
|
616
|
+
},
|
|
617
|
+
margin: { t: 30, b: 5, l: 55, r: 5 },
|
|
618
|
+
height: this.screenHeight,
|
|
619
|
+
autosize: true,
|
|
620
|
+
shapes: this.currentRenderShapes(this.regionStore.getShowShapeLabel()),
|
|
621
|
+
dragmode: this.dragMode ? this.dragMode : false,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
/** 3D scene for volumetric plot types (scatter3d, isosurface). */
|
|
625
|
+
getVolumeLayout() {
|
|
626
|
+
return {
|
|
627
|
+
margin: { t: 0, b: 0, l: 0, r: 0 },
|
|
628
|
+
height: this.screenHeight,
|
|
629
|
+
autosize: true,
|
|
630
|
+
scene: {
|
|
631
|
+
xaxis: { title: 'X' },
|
|
632
|
+
yaxis: { title: 'Y' },
|
|
633
|
+
zaxis: { title: 'Z-plane' },
|
|
634
|
+
// 'cube' (not 'data'): a z-stack has far fewer planes than X/Y pixels,
|
|
635
|
+
// so 'data' squashes the volume into a near-flat slab that reads as
|
|
636
|
+
// empty edge-on. A cube gives the z dimension real height so the
|
|
637
|
+
// isosurface/voxels are actually visible.
|
|
638
|
+
aspectmode: 'cube',
|
|
639
|
+
},
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
/** Plot types this backend advertises (drives the UI selector). */
|
|
643
|
+
getPlotTypeDescriptors() {
|
|
644
|
+
return Object.values(PLOT_TYPE_DESCRIPTORS).filter((d) => !!d);
|
|
645
|
+
}
|
|
646
|
+
/** This backend's region renderer. Plotly draws shapes natively, so the
|
|
647
|
+
* overlay is a thin adapter over this service's drag modes. */
|
|
648
|
+
getRegionOverlay() {
|
|
649
|
+
if (!this.regionOverlay)
|
|
650
|
+
this.regionOverlay = new PlotlyRegionOverlay(this);
|
|
651
|
+
return this.regionOverlay;
|
|
652
|
+
}
|
|
653
|
+
// ── Intensity profile (Region-based line ROIs) ──────────────────────
|
|
654
|
+
/** Emits the full set of intensity profiles (one per profile-line region)
|
|
655
|
+
* whenever a profile line is added, moved, or removed. */
|
|
656
|
+
getIntensityProfile$() {
|
|
657
|
+
return this.intensityProfile$.asObservable();
|
|
658
|
+
}
|
|
659
|
+
/** Plotly renders the line ROIs + inset, so it exposes the intensity controls. */
|
|
660
|
+
getIntensityControls() { return this; }
|
|
661
|
+
/**
|
|
662
|
+
* Render the floating intensity-profile inset chart into `divId` — one line
|
|
663
|
+
* trace per ROI, each drawn in its ROI's colour. Kept here (not in the diagram
|
|
664
|
+
* component) so the consumer never touches Plotly directly.
|
|
665
|
+
*/
|
|
666
|
+
renderIntensityInset(divId, profiles) {
|
|
667
|
+
const el = document.getElementById(divId);
|
|
668
|
+
if (!el)
|
|
669
|
+
return;
|
|
670
|
+
const traces = (profiles ?? []).map((p, i) => ({
|
|
671
|
+
x: p.positions,
|
|
672
|
+
y: p.values,
|
|
673
|
+
type: 'scatter',
|
|
674
|
+
mode: 'lines',
|
|
675
|
+
line: { color: p.color ?? '#FFD400', width: 2 },
|
|
676
|
+
name: `Line ${i + 1}`,
|
|
677
|
+
hoverinfo: 'x+y',
|
|
678
|
+
}));
|
|
679
|
+
// X axis is in microns when the image carries a physical pixel size (mpp),
|
|
680
|
+
// otherwise in pixels — driven by the unit the sampler tagged on the profile.
|
|
681
|
+
const unit = (profiles ?? []).find(p => p.unit)?.unit ?? 'px';
|
|
682
|
+
const xTitle = unit === 'µm' ? 'Position (µm)' : 'Position (px)';
|
|
683
|
+
Plotly.react(el, traces, {
|
|
684
|
+
margin: { t: 6, r: 8, b: 38, l: 40 },
|
|
685
|
+
xaxis: { title: xTitle, zeroline: false, color: '#ddd' },
|
|
686
|
+
yaxis: { title: 'Intensity', zeroline: false, color: '#ddd' },
|
|
687
|
+
showlegend: false,
|
|
688
|
+
paper_bgcolor: 'rgba(25,25,25,0.9)',
|
|
689
|
+
plot_bgcolor: 'rgba(25,25,25,0.9)',
|
|
690
|
+
font: { color: '#eee', size: 10 },
|
|
691
|
+
}, { displayModeBar: false, responsive: true });
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Region shapes (filtered/redrawn for the current file). The profile-line ROIs
|
|
695
|
+
* are ordinary store regions (tagged `kind: 'profile'`), so they are already
|
|
696
|
+
* part of `this.shapes` — no separate append is needed.
|
|
697
|
+
*/
|
|
698
|
+
currentRenderShapes(showLabel = this.regionStore.getShowShapeLabel()) {
|
|
699
|
+
return this.shapesToRedraw(showLabel);
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* IIntensityControls: add another intensity-profile line as a neutral Region
|
|
703
|
+
* (tagged `kind: 'profile'`). It is a horizontal open 2-point polyline spanning
|
|
704
|
+
* the image width, staggered vertically so successive lines stay distinct, in
|
|
705
|
+
* the next bright palette colour. Added to the shared store so both backends
|
|
706
|
+
* (Plotly + OpenSeadragon) render and drag it; excluded from the Regions tab
|
|
707
|
+
* and exports by its kind.
|
|
708
|
+
*/
|
|
709
|
+
addProfileLine() {
|
|
710
|
+
if (!this.trueImgSize)
|
|
711
|
+
return null;
|
|
712
|
+
const count = this.getProfileRegions().length;
|
|
713
|
+
const imgX0 = this.trueImgSize[0], imgX1 = this.trueImgSize[1];
|
|
714
|
+
const imgTop = this.trueImgSize[2], imgBottom = this.trueImgSize[3];
|
|
715
|
+
// Place the line within the currently VISIBLE image area, so when zoomed in it
|
|
716
|
+
// lands on-screen instead of spanning the whole (mostly off-screen) image, at
|
|
717
|
+
// 2/3 of the visible width and horizontally centred. Falls back to the full
|
|
718
|
+
// image when no viewport ROI is known (Plotly heatmap) or a stale ROI no longer
|
|
719
|
+
// overlaps the image.
|
|
720
|
+
const roi = this.lastVisibleRoi;
|
|
721
|
+
const overlaps = !!roi && roi.width > 0 && roi.height > 0
|
|
722
|
+
&& roi.x < imgX1 && roi.x + roi.width > imgX0
|
|
723
|
+
&& roi.y < imgBottom && roi.y + roi.height > imgTop;
|
|
724
|
+
const x0v = overlaps ? Math.max(imgX0, roi.x) : imgX0;
|
|
725
|
+
const x1v = overlaps ? Math.min(imgX1, roi.x + roi.width) : imgX1;
|
|
726
|
+
const topV = overlaps ? Math.max(imgTop, roi.y) : imgTop;
|
|
727
|
+
const bottomV = overlaps ? Math.min(imgBottom, roi.y + roi.height) : imgBottom;
|
|
728
|
+
const cx = (x0v + x1v) / 2;
|
|
729
|
+
const half = ((x1v - x0v) * (2 / 3)) / 2; // line spans 2/3 of the visible width
|
|
730
|
+
const x0 = cx - half, x1 = cx + half;
|
|
731
|
+
const bandH = bottomV - topV;
|
|
732
|
+
const midY = (topV + bottomV) / 2;
|
|
733
|
+
// Stagger successive lines across the visible band so they stay distinct.
|
|
734
|
+
const y = Math.min(bottomV, Math.max(topV, midY + count * bandH * 0.12));
|
|
735
|
+
const poly = new Polygon();
|
|
736
|
+
poly.npoints = 2;
|
|
737
|
+
poly.xpoints = [x0, x1];
|
|
738
|
+
poly.ypoints = [y, y];
|
|
739
|
+
poly.coordinates = [[x0, y], [x1, y]];
|
|
740
|
+
poly.closed = false;
|
|
741
|
+
const region = new Region();
|
|
742
|
+
region.bounds = poly;
|
|
743
|
+
region.kind = 'profile';
|
|
744
|
+
region.color = this.PROFILE_PALETTE[this.profileColorSeq++ % this.PROFILE_PALETTE.length];
|
|
745
|
+
// Leave region.label undefined so applyClassificationColors won't recolor it.
|
|
746
|
+
this.regionStore.addRegion(region);
|
|
747
|
+
// If a Plotly plot is on screen, re-render shapes so the new line appears.
|
|
748
|
+
// (The OSD overlay renders automatically via its region-update subscription.)
|
|
749
|
+
if (this.plotDiv) {
|
|
750
|
+
this.syncShapesFromStore();
|
|
751
|
+
try {
|
|
752
|
+
Plotly.relayout(this.plotDiv, { shapes: this.currentRenderShapes() });
|
|
753
|
+
}
|
|
754
|
+
catch { /* div not a Plotly plot (OSD owns it) */ }
|
|
755
|
+
}
|
|
756
|
+
return region;
|
|
757
|
+
}
|
|
758
|
+
/** The profile-line ROIs currently in the store (kind === 'profile'). */
|
|
759
|
+
getProfileRegions() {
|
|
760
|
+
return this.regionStore.getRegions().filter(r => r.kind === 'profile');
|
|
761
|
+
}
|
|
762
|
+
/** Recompute and broadcast a profile for every profile-line region (id + colour
|
|
763
|
+
* tagged so the inset trace matches its line). */
|
|
764
|
+
emitProfiles() {
|
|
765
|
+
this.intensityProfile$.next(this.getProfileRegions().map(r => ({
|
|
766
|
+
...this.computeIntensityProfileForRegion(r),
|
|
767
|
+
id: r.id,
|
|
768
|
+
color: r.color,
|
|
769
|
+
})));
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Sample the intensity profile for a profile-line region: extract its two
|
|
773
|
+
* polygon endpoints and reuse the shared `computeIntensityProfile` sampling.
|
|
774
|
+
*/
|
|
775
|
+
computeIntensityProfileForRegion(region) {
|
|
776
|
+
// Duck-typed (not `instanceof Polygon`): after a drag round-trips through the
|
|
777
|
+
// store the bounds can be a plain object, so match on the point arrays.
|
|
778
|
+
const poly = region.bounds;
|
|
779
|
+
if (!poly || !Array.isArray(poly.xpoints) || !Array.isArray(poly.ypoints) ||
|
|
780
|
+
poly.xpoints.length < 2 || poly.ypoints.length < 2) {
|
|
781
|
+
return { positions: [], values: [] };
|
|
782
|
+
}
|
|
783
|
+
return this.computeIntensityProfile({
|
|
784
|
+
x0: poly.xpoints[0], y0: poly.ypoints[0],
|
|
785
|
+
x1: poly.xpoints[1], y1: poly.ypoints[1],
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Supply pixel frames for intensity sampling (used by the OSD/image backend,
|
|
790
|
+
* whose cached frames may be empty). Optionally also sets the data/pixel ratios.
|
|
791
|
+
*/
|
|
792
|
+
setSamplingFrames(frames, ratios, origin = [0, 0]) {
|
|
793
|
+
this.cachedImageFrames = frames;
|
|
794
|
+
if (ratios)
|
|
795
|
+
this.cachedImageRatios = ratios;
|
|
796
|
+
this.cachedFrameOrigin = origin;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Populate the sampling cache for the intensity profiles when OpenSeadragon is
|
|
800
|
+
* the active backend (the Image plot type). In OSD mode Plotly never rendered,
|
|
801
|
+
* so `cachedImageFrames`/`trueImgSize` are unset — fetch the current slice's
|
|
802
|
+
* preview (the same pixels the heatmap would sample) so the line ROIs have
|
|
803
|
+
* pixel data and a placement extent. Re-emits the profiles once loaded.
|
|
804
|
+
*/
|
|
805
|
+
async ensureIntensitySampling(imageInfo, zIndex) {
|
|
806
|
+
if (!imageInfo?.urls?.length)
|
|
807
|
+
return;
|
|
808
|
+
// Sample just the displayed slice (showStack off → single frame); `load`
|
|
809
|
+
// also sets `this.trueImgSize`, which addProfileLine needs for placement.
|
|
810
|
+
const single = { ...imageInfo, showStack: false };
|
|
811
|
+
const loaded = await this.load(single, zIndex || 0);
|
|
812
|
+
this.cachedImageFrames = loaded.data;
|
|
813
|
+
this.cachedImageRatios = loaded.ratios;
|
|
814
|
+
this.cachedImageWidth = loaded.sizes?.[0] ?? this.cachedImageWidth;
|
|
815
|
+
this.cachedImageHeight = loaded.sizes?.[1] ?? this.cachedImageHeight;
|
|
816
|
+
this.cachedFrameOrigin = [0, 0]; // full preview
|
|
817
|
+
this.cachedIsGrayscale = !!imageInfo.isGrayscale;
|
|
818
|
+
this.emitProfiles();
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* {@link IIntensitySampling} stub — the viewport-change signal is OpenSeadragon's
|
|
822
|
+
* (it re-samples on OSD zoom/pan). Plotly's own high-def zoom updates the
|
|
823
|
+
* sampling cache inline, so it never emits here; returns EMPTY so a uniform
|
|
824
|
+
* `IVisualizer` consumer can subscribe regardless of the active backend.
|
|
825
|
+
*/
|
|
826
|
+
getViewportChange$() {
|
|
827
|
+
return EMPTY;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Re-fetch the given image-pixel ROI at the display resolution and use it as
|
|
831
|
+
* the intensity-sampling source, so the profile reflects the data at the
|
|
832
|
+
* current zoom level. Shared by both backends: the OSD viewport-change hook
|
|
833
|
+
* calls it on zoom/pan; Plotly's own high-def zoom (triggerZoom) updates the
|
|
834
|
+
* same cache inline. `roi` is in full-image pixel coordinates.
|
|
835
|
+
*/
|
|
836
|
+
refreshIntensitySamplingForRoi(x, y, width, height, zIndex) {
|
|
837
|
+
if (width <= 0 || height <= 0)
|
|
838
|
+
return;
|
|
839
|
+
// Remember the visible region so a new profile line is placed inside it.
|
|
840
|
+
this.lastVisibleRoi = { x, y, width, height };
|
|
841
|
+
const roi = new Rectangle();
|
|
842
|
+
roi.x = Math.round(x);
|
|
843
|
+
roi.y = Math.round(y);
|
|
844
|
+
roi.width = Math.round(width);
|
|
845
|
+
roi.height = Math.round(height);
|
|
846
|
+
const screen = this.plotUtilities.getDomRectangle('diagram');
|
|
847
|
+
// Snapshot the filename so a response that arrives after the user switched
|
|
848
|
+
// files is dropped (the request carried the file selected at call time).
|
|
849
|
+
const reqName = this.fileName;
|
|
850
|
+
this.tiles.zoomOnRegion(roi, screen, zIndex || 0).subscribe({
|
|
851
|
+
next: (zoomData) => {
|
|
852
|
+
Image.load(Buffer.from(new Uint8Array(zoomData))).then((image) => {
|
|
853
|
+
if (this.fileName !== reqName)
|
|
854
|
+
return;
|
|
855
|
+
const isGray = !!this.imageInfo?.isGrayscale;
|
|
856
|
+
const frame = isGray
|
|
857
|
+
? this.plotUtilities.arrayToMatrix(image.grey().data, image.width)
|
|
858
|
+
: this.plotUtilities.arrayToMatrix(image.getPixelsArray(), image.width);
|
|
859
|
+
this.setSamplingFrames([frame], [roi.width / image.width, roi.height / image.height], [roi.x, roi.y]);
|
|
860
|
+
this.emitProfiles();
|
|
861
|
+
});
|
|
862
|
+
},
|
|
863
|
+
error: () => { },
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Sample the active frame's intensity (grayscale value or RGB luminance)
|
|
868
|
+
* along the line ROI. Returns distance-along-line positions and values.
|
|
869
|
+
*/
|
|
870
|
+
computeIntensityProfile(line) {
|
|
871
|
+
const empty = { positions: [], values: [] };
|
|
872
|
+
if (!this.cachedImageFrames?.length || !line)
|
|
873
|
+
return empty;
|
|
874
|
+
const frame = this.cachedImageFrames[this.activeFrameIndex()] ?? this.cachedImageFrames[0];
|
|
875
|
+
if (!frame?.length)
|
|
876
|
+
return empty;
|
|
877
|
+
const rx = this.cachedImageRatios[0] || 1;
|
|
878
|
+
const ry = this.cachedImageRatios[1] || rx;
|
|
879
|
+
const [ox, oy] = this.cachedFrameOrigin;
|
|
880
|
+
const x0 = +line.x0, y0 = +line.y0, x1 = +line.x1, y1 = +line.y1;
|
|
881
|
+
const dxData = x1 - x0, dyData = y1 - y0;
|
|
882
|
+
// Scale the along-line distance by the physical pixel size (microns/pixel)
|
|
883
|
+
// when the image carries one — anisotropic mppX/mppY are applied per axis so
|
|
884
|
+
// diagonal lines measure the true physical length. Falls back to pixels.
|
|
885
|
+
const { mppX, mppY } = this.currentMpp();
|
|
886
|
+
const useMicrons = mppX != null;
|
|
887
|
+
const lenData = useMicrons
|
|
888
|
+
? Math.hypot(dxData * mppX, dyData * (mppY ?? mppX))
|
|
889
|
+
: Math.hypot(dxData, dyData);
|
|
890
|
+
const lenPx = Math.hypot(dxData / rx, dyData / ry);
|
|
891
|
+
const n = Math.max(2, Math.round(lenPx));
|
|
892
|
+
const h = frame.length;
|
|
893
|
+
const w = frame[0].length;
|
|
894
|
+
const positions = [];
|
|
895
|
+
const values = [];
|
|
896
|
+
for (let i = 0; i < n; i++) {
|
|
897
|
+
const t = i / (n - 1);
|
|
898
|
+
// Map data coords to the cached frame's pixel grid, offset by the frame's
|
|
899
|
+
// origin (non-zero when the frame is a zoomed crop).
|
|
900
|
+
const px = Math.round((x0 + t * dxData - ox) / rx);
|
|
901
|
+
const py = Math.round((y0 + t * dyData - oy) / ry);
|
|
902
|
+
let v = 0;
|
|
903
|
+
if (px >= 0 && px < w && py >= 0 && py < h) {
|
|
904
|
+
const cell = frame[py][px];
|
|
905
|
+
v = Array.isArray(cell) ? (0.299 * cell[0] + 0.587 * cell[1] + 0.114 * cell[2]) : cell;
|
|
906
|
+
}
|
|
907
|
+
positions.push(t * lenData);
|
|
908
|
+
values.push(v);
|
|
909
|
+
}
|
|
910
|
+
return { positions, values, unit: useMicrons ? 'µm' : 'px' };
|
|
911
|
+
}
|
|
912
|
+
/** Physical pixel size (microns/pixel) for the current image, if known.
|
|
913
|
+
* mpp is constant across frames/channels, so the first metadata entry that
|
|
914
|
+
* carries a positive mppX wins. Returns nulls when the image is unscaled. */
|
|
915
|
+
currentMpp() {
|
|
916
|
+
const meta = this.imageInfo?.imageMeta;
|
|
917
|
+
const m = Array.isArray(meta)
|
|
918
|
+
? (meta.find(e => e && (e.mppX ?? 0) > 0) ?? meta[0])
|
|
919
|
+
: undefined;
|
|
920
|
+
const mppX = m && (m.mppX ?? 0) > 0 ? m.mppX : null;
|
|
921
|
+
const mppY = m && (m.mppY ?? 0) > 0 ? m.mppY : null;
|
|
922
|
+
return { mppX, mppY };
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* autoscale the plot
|
|
926
|
+
*/
|
|
927
|
+
autoscale() {
|
|
928
|
+
if (this.plotDiv) {
|
|
929
|
+
this.zoomCoordinates = [];
|
|
930
|
+
Plotly.relayout(this.plotDiv, {
|
|
931
|
+
'xaxis.autorange': true,
|
|
932
|
+
'yaxis.autorange': false
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* relayout the plot
|
|
938
|
+
*/
|
|
939
|
+
relayout(trueImageSize) {
|
|
940
|
+
let imgSize;
|
|
941
|
+
if (trueImageSize) {
|
|
942
|
+
imgSize = trueImageSize;
|
|
943
|
+
}
|
|
944
|
+
else {
|
|
945
|
+
imgSize = this.trueImgSize;
|
|
946
|
+
}
|
|
947
|
+
if (this.plotDiv) {
|
|
948
|
+
// Refresh height from current DOM so panel resizes are reflected
|
|
949
|
+
const plotEl = document.getElementById(this.plotDiv);
|
|
950
|
+
if (plotEl?.offsetHeight) {
|
|
951
|
+
this.screenHeight = plotEl.offsetHeight;
|
|
952
|
+
}
|
|
953
|
+
try {
|
|
954
|
+
if (this.zoomCoordinates.length > 0) {
|
|
955
|
+
Plotly.relayout(this.plotDiv, this.getHeatmapLayout([this.zoomCoordinates[0], this.zoomCoordinates[1]], [this.zoomCoordinates[2], this.zoomCoordinates[3]])); // reverse the y range
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
// as autorange is set to false, and not reversed we need to set the yrange correcly here
|
|
959
|
+
Plotly.relayout(this.plotDiv, this.getHeatmapLayout([imgSize[0], imgSize[1]], [imgSize[3], imgSize[2]]));
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
catch (err) {
|
|
963
|
+
const msg = err?.error?.message || err?.message || err?.statusText || String(err);
|
|
964
|
+
console.error('Error occured', err);
|
|
965
|
+
this.messageService.add({ key: 'center-toast', sticky: true, severity: 'error', summary: 'An error occured', detail: `The following
|
|
966
|
+
error occured: ${msg}. Please try to open the image again through the
|
|
967
|
+
file navigator.` });
|
|
968
|
+
// TODO correctly clear the plot
|
|
969
|
+
this.reset();
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
setEvents(plotDiv, isGrayscale, screenHeight) {
|
|
974
|
+
this.imageInfo.isGrayscale = isGrayscale;
|
|
975
|
+
this.screenHeight = screenHeight;
|
|
976
|
+
const plot = document.getElementById(plotDiv);
|
|
977
|
+
if (plot) {
|
|
978
|
+
// unbind previous event
|
|
979
|
+
plot.removeEventListener('plotly_relayout', this.onRelayoutEvent);
|
|
980
|
+
// bind new event
|
|
981
|
+
plot.on('plotly_relayout', this.onRelayoutEvent);
|
|
982
|
+
// Clicking on a shape activates it but Plotly doesn't fire a dedicated
|
|
983
|
+
// event — sample _activeShapeIndex on the next microtask so subscribers
|
|
984
|
+
// (Region Editor) can mirror the selection.
|
|
985
|
+
plot.removeEventListener('mousedown', this.onPlotMouseDown);
|
|
986
|
+
this.onPlotMouseDown = () => setTimeout(() => this.maybeEmitActiveShapeIndex(), 0);
|
|
987
|
+
plot.addEventListener('mousedown', this.onPlotMouseDown);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
relayoutEventHandler(event) {
|
|
991
|
+
if (Object.keys(event).includes('dragmode')) {
|
|
992
|
+
this.dragMode = event.dragmode;
|
|
993
|
+
}
|
|
994
|
+
// when relayout event occurs
|
|
995
|
+
// update the region selection udpdates
|
|
996
|
+
const keys = Object.keys(event);
|
|
997
|
+
let shapesModified = false;
|
|
998
|
+
keys.forEach((key) => {
|
|
999
|
+
if (typeof key === 'string' && key.startsWith('shapes[')) {
|
|
1000
|
+
const shapeNumber = +key.split('[')[1].split(']')[0];
|
|
1001
|
+
const shapeChange = key.split('.')[1];
|
|
1002
|
+
if (this.shapes && this.shapes[shapeNumber]) {
|
|
1003
|
+
if (shapeChange === 'path') {
|
|
1004
|
+
this.shapes[shapeNumber][shapeChange] = this.plotUtilities.roundPathCoordinates(event[key]);
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
this.shapes[shapeNumber][shapeChange] = Math.round(+event[key]);
|
|
1008
|
+
}
|
|
1009
|
+
shapesModified = true;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
if (shapesModified) {
|
|
1014
|
+
// Mirror the in-place edit into the shared store (syncs its cache + emits).
|
|
1015
|
+
this.commitShapesToStore();
|
|
1016
|
+
}
|
|
1017
|
+
// manage high def zoom (not if we are showing a stack)
|
|
1018
|
+
if (keys.length === 4 && this.isRealZoom) {
|
|
1019
|
+
const coordinates = [];
|
|
1020
|
+
keys.forEach(key => {
|
|
1021
|
+
if (key.startsWith('xaxis.range[')) {
|
|
1022
|
+
coordinates.push(event[key]);
|
|
1023
|
+
}
|
|
1024
|
+
if (key.startsWith('yaxis.range[')) {
|
|
1025
|
+
coordinates.push(event[key]);
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
this.zoomCoordinates = coordinates;
|
|
1029
|
+
if (!this.imageInfo.showStack) {
|
|
1030
|
+
this.triggerZoom(coordinates);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
// when shape is created
|
|
1034
|
+
if (keys.length === 1 && 'shapes' in event) {
|
|
1035
|
+
this.shapes = event.shapes;
|
|
1036
|
+
for (let i = 0; i < this.shapes.length; i++) {
|
|
1037
|
+
// A freshly Plotly-drawn shape has no id yet — give it a label object
|
|
1038
|
+
// (when labels are on) before the store mints id + default name.
|
|
1039
|
+
const isNew = this.shapes[i].id == null;
|
|
1040
|
+
this.shapes[i].fileName = this.fileName;
|
|
1041
|
+
this.shapes[i] = this.plotUtilities.snapRegion(this.shapes[i]);
|
|
1042
|
+
if (isNew && this.regionStore.getShowShapeLabel()) {
|
|
1043
|
+
this.shapes[i].label = { text: this.shapes[i].label,
|
|
1044
|
+
texttemplate: this.shapes[i].label,
|
|
1045
|
+
font: { color: this.regionStore.getShapeColor() },
|
|
1046
|
+
textposition: 'top left'
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
// Mint ids/names in the store, write them back onto the dicts, and emit.
|
|
1051
|
+
this.commitShapesToStore();
|
|
1052
|
+
if (this.isRegionSavedOn) {
|
|
1053
|
+
this.previousShapes = this.shapes.slice();
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
// if autoscale
|
|
1057
|
+
if (keys.includes('xaxis.autorange') && keys.includes('yaxis.autorange')) {
|
|
1058
|
+
// trigger an autoscale event to reset the selected image mode dropdown
|
|
1059
|
+
this.autoscaleEvent.next('an autoscale has happened');
|
|
1060
|
+
// select display type to trigger a new plotting update with all the necessary plotting parameters set
|
|
1061
|
+
this.tiles.selectDiagramDisplay();
|
|
1062
|
+
}
|
|
1063
|
+
// if showstack or aspectratio event
|
|
1064
|
+
if (event.showstack !== undefined || event.aspectratio !== undefined) {
|
|
1065
|
+
// Set new image info to trigger a plot update
|
|
1066
|
+
this.setImageInfo(event.showstack, event.aspectratio);
|
|
1067
|
+
}
|
|
1068
|
+
// Plotly may have updated the active shape index as part of the relayout
|
|
1069
|
+
// (e.g. clicking a shape's edit handle, or finishing a draw) — surface
|
|
1070
|
+
// that change to the Region Editor.
|
|
1071
|
+
this.maybeEmitActiveShapeIndex();
|
|
1072
|
+
}
|
|
1073
|
+
reloadAndPlot() {
|
|
1074
|
+
this.setImageInfo();
|
|
1075
|
+
}
|
|
1076
|
+
// ── Tool delegations ────────────────────────────────────────────────
|
|
1077
|
+
setWandMode(active, options = {}) {
|
|
1078
|
+
// Re-bind to ourselves in case the OSD backend last used these singletons.
|
|
1079
|
+
if (active)
|
|
1080
|
+
this.wandTool.bindHost(this.wandHost);
|
|
1081
|
+
this.wandTool.setMode(active, options);
|
|
1082
|
+
}
|
|
1083
|
+
setWandOptions(options) {
|
|
1084
|
+
this.wandTool.setOptions(options);
|
|
1085
|
+
}
|
|
1086
|
+
clearActiveWandRegion() {
|
|
1087
|
+
this.wandTool.clearActiveRegion();
|
|
1088
|
+
}
|
|
1089
|
+
setBrushMode(active, options = {}) {
|
|
1090
|
+
// Re-bind to ourselves in case the OSD backend last used this singleton.
|
|
1091
|
+
if (active)
|
|
1092
|
+
this.brushTool.bindHost(this.wandHost);
|
|
1093
|
+
this.brushTool.setMode(active, options);
|
|
1094
|
+
}
|
|
1095
|
+
setBrushOptions(options) {
|
|
1096
|
+
if (options.size != null)
|
|
1097
|
+
this.brushTool.setSize(options.size);
|
|
1098
|
+
}
|
|
1099
|
+
setVertexEraserMode(active) {
|
|
1100
|
+
if (active)
|
|
1101
|
+
this.vertexEraserTool.bindHost(this.eraserHost);
|
|
1102
|
+
this.vertexEraserTool.setMode(active);
|
|
1103
|
+
}
|
|
1104
|
+
setVertexEraserRadius(radius) {
|
|
1105
|
+
this.vertexEraserTool.setRadius(radius);
|
|
1106
|
+
}
|
|
1107
|
+
setZoomToBoxMode(active) {
|
|
1108
|
+
// The zoom-to-box tool is a root singleton shared with the OSD backend,
|
|
1109
|
+
// which rebinds it to its own host on activation. Rebind to ours when we
|
|
1110
|
+
// activate so a prior OSD zoom-to-box doesn't leave the singleton pointing
|
|
1111
|
+
// at OSD's coordinate/zoom handlers (which would break box-zoom on heatmaps).
|
|
1112
|
+
if (active)
|
|
1113
|
+
this.bindZoomToBoxHost();
|
|
1114
|
+
this.zoomToBoxTool.setMode(active);
|
|
1115
|
+
}
|
|
1116
|
+
/** Bind the shared zoom-to-box tool to this (Plotly) backend's host. */
|
|
1117
|
+
bindZoomToBoxHost() {
|
|
1118
|
+
this.zoomToBoxTool.bindHost({
|
|
1119
|
+
getPlotDiv: () => this.plotDiv,
|
|
1120
|
+
pixelToData: (px, py) => this.zoomBoxPixelToData(px, py),
|
|
1121
|
+
applyZoomToBox: (coords) => this.applyZoomToBox(coords),
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
/** Box-prompted SAM: segment the drawn rectangles. The SAM tool reuses the
|
|
1125
|
+
* wand host (cached frame + coordinate transform + region store). */
|
|
1126
|
+
segmentRectangles() {
|
|
1127
|
+
this.samTool.bindHost(this.wandHost);
|
|
1128
|
+
return this.samTool.segmentBoxes();
|
|
1129
|
+
}
|
|
1130
|
+
segmentRectanglesCellpose() {
|
|
1131
|
+
if (!this.cellSegmenter)
|
|
1132
|
+
return Promise.resolve(0);
|
|
1133
|
+
this.cellSegmentTool.bindHost(this.wandHost);
|
|
1134
|
+
return this.cellSegmentTool.segmentBoxes(this.cellSegmenter);
|
|
1135
|
+
}
|
|
1136
|
+
setSamModel(id) {
|
|
1137
|
+
this.samTool.setModel(id);
|
|
1138
|
+
this.samPointTool.setModel(id);
|
|
1139
|
+
}
|
|
1140
|
+
setSamPointMode(active) {
|
|
1141
|
+
if (active)
|
|
1142
|
+
this.samPointTool.bindHost(this.wandHost);
|
|
1143
|
+
this.samPointTool.setMode(active);
|
|
1144
|
+
}
|
|
1145
|
+
commitSamPoints() { this.samPointTool.commit(); }
|
|
1146
|
+
clearSamPoints() { this.samPointTool.clear(); }
|
|
1147
|
+
/**
|
|
1148
|
+
* Tool-host callback: apply the zoom-to-box selection. Stack mode does a
|
|
1149
|
+
* pure axis-range relayout; non-stack mode goes through the high-def
|
|
1150
|
+
* triggerZoom pipeline so the image is re-fetched at the new resolution.
|
|
1151
|
+
*/
|
|
1152
|
+
/** Overlay-pixel -> Plotly data coords via the axis objects (subtracting the
|
|
1153
|
+
* plot margin offset). The zoom-to-box tool calls this through its host. */
|
|
1154
|
+
zoomBoxPixelToData(px, py) {
|
|
1155
|
+
const gd = document.getElementById(this.plotDiv);
|
|
1156
|
+
const xaxis = gd._fullLayout.xaxis;
|
|
1157
|
+
const yaxis = gd._fullLayout.yaxis;
|
|
1158
|
+
return { x: xaxis.p2d(px - xaxis._offset), y: yaxis.p2d(py - yaxis._offset) };
|
|
1159
|
+
}
|
|
1160
|
+
applyZoomToBox(coordinates) {
|
|
1161
|
+
this.zoomCoordinates = coordinates;
|
|
1162
|
+
if (this.imageInfo?.showStack) {
|
|
1163
|
+
Plotly.relayout(this.plotDiv, {
|
|
1164
|
+
'xaxis.range[0]': coordinates[0],
|
|
1165
|
+
'xaxis.range[1]': coordinates[1],
|
|
1166
|
+
'yaxis.range[0]': coordinates[2],
|
|
1167
|
+
'yaxis.range[1]': coordinates[3],
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
else {
|
|
1171
|
+
this.triggerZoom(coordinates);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
// ── IViewportHost implementation (for the on-canvas tools) ──────────
|
|
1175
|
+
/** The plot element the tool canvas overlays attach to. */
|
|
1176
|
+
getOverlayContainer() {
|
|
1177
|
+
return this.plotDiv ? document.getElementById(this.plotDiv) : null;
|
|
1178
|
+
}
|
|
1179
|
+
/** Plotly coordinate transform (screen <-> data via the axis objects). */
|
|
1180
|
+
getCoordinateTransform() {
|
|
1181
|
+
return this.coordinateTransform;
|
|
1182
|
+
}
|
|
1183
|
+
// ── WandToolHost implementation ─────────────────────────────────────
|
|
1184
|
+
/** Pixel data the wand needs for sampling. null = no image loaded. */
|
|
1185
|
+
getCachedImageData() {
|
|
1186
|
+
if (!this.cachedImageFrames || this.cachedImageFrames.length === 0)
|
|
1187
|
+
return null;
|
|
1188
|
+
return {
|
|
1189
|
+
frames: this.cachedImageFrames,
|
|
1190
|
+
width: this.cachedImageWidth,
|
|
1191
|
+
height: this.cachedImageHeight,
|
|
1192
|
+
ratios: this.cachedImageRatios,
|
|
1193
|
+
isGrayscale: this.cachedIsGrayscale,
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
// ── shared-store sync helpers ──────────────────────────────────────────
|
|
1197
|
+
//
|
|
1198
|
+
// `this.shapes` is Plotly's own representation (the dict array Plotly.relayout
|
|
1199
|
+
// consumes). The shared RegionStore is the single source of truth, holding the
|
|
1200
|
+
// neutral Region model. These helpers bridge the two: writes mirror
|
|
1201
|
+
// `this.shapes` INTO the store; image switches/external changes project the
|
|
1202
|
+
// store back OUT to `this.shapes`.
|
|
1203
|
+
/** Build Plotly shape dicts from the store's regions (the render projection). */
|
|
1204
|
+
regionsToShapeDicts() {
|
|
1205
|
+
const showLabel = this.regionStore.getShowShapeLabel();
|
|
1206
|
+
return this.regionStore.getRegions().map(r => {
|
|
1207
|
+
r.filename = this.fileName;
|
|
1208
|
+
return { ...r.getShape(showLabel) };
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
/** Rebuild the dict working-set from the store (after an image switch, or when
|
|
1212
|
+
* regions changed while another backend was rendering). */
|
|
1213
|
+
syncShapesFromStore() {
|
|
1214
|
+
this.shapes = this.regionsToShapeDicts();
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Make the shared store authoritative for the current dict working-set: convert
|
|
1218
|
+
* `this.shapes` to neutral regions and replace the store's list (the store
|
|
1219
|
+
* mints ids, applies class colours, syncs its per-image cache and emits the
|
|
1220
|
+
* region-update event). Store-minted ids are written back onto the dicts so the
|
|
1221
|
+
* two representations stay aligned. Profile-line ROIs are ordinary regions
|
|
1222
|
+
* (tagged `kind: 'profile'`) and round-trip through here too.
|
|
1223
|
+
*/
|
|
1224
|
+
commitShapesToStore() {
|
|
1225
|
+
const regions = this.shapes
|
|
1226
|
+
.map(s => Object.assign(new ShapeSelection(), s).getRegion());
|
|
1227
|
+
this.regionStore.setRegions(regions, this.regionStore.getShowShapeLabel(), true, this.regionStore.getFillColor(), false);
|
|
1228
|
+
const stored = this.regionStore.getRegions();
|
|
1229
|
+
for (let i = 0; i < this.shapes.length && i < stored.length; i++) {
|
|
1230
|
+
if (this.shapes[i].id == null)
|
|
1231
|
+
this.shapes[i].id = stored[i].id;
|
|
1232
|
+
if (this.shapes[i].name == null)
|
|
1233
|
+
this.shapes[i].name = stored[i].name;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Push the current `this.shapes` to Plotly, mirror them into the shared store
|
|
1238
|
+
* (which syncs its cache + emits the region-update event), and refresh the
|
|
1239
|
+
* local previous-shapes buffer. The wand/eraser tools call this after mutating
|
|
1240
|
+
* the shapes array directly.
|
|
1241
|
+
*/
|
|
1242
|
+
applyShapesChange() {
|
|
1243
|
+
if (this.isRegionSavedOn) {
|
|
1244
|
+
this.previousShapes = this.shapes.slice();
|
|
1245
|
+
}
|
|
1246
|
+
this.commitShapesToStore();
|
|
1247
|
+
if (this.plotDiv) {
|
|
1248
|
+
const dictArray = this.shapes.map(s => ({ ...s }));
|
|
1249
|
+
try {
|
|
1250
|
+
Plotly.relayout(this.plotDiv, this.shapesRelayout(dictArray));
|
|
1251
|
+
}
|
|
1252
|
+
catch { /* div owned by another backend (OSD) — its overlay renders shapes */ }
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
/** Active frame index in the cached image stack. */
|
|
1256
|
+
activeFrameIndex() {
|
|
1257
|
+
if (!this.cachedImageFrames || this.cachedImageFrames.length <= 1)
|
|
1258
|
+
return 0;
|
|
1259
|
+
const gd = document.getElementById(this.plotDiv);
|
|
1260
|
+
const sliderActive = gd?._fullLayout?.sliders?.[0]?.active;
|
|
1261
|
+
if (typeof sliderActive === 'number')
|
|
1262
|
+
return sliderActive;
|
|
1263
|
+
return this.zIndex.value || 0;
|
|
1264
|
+
}
|
|
1265
|
+
/** Capability-gated 3D scene controls (Plotly renders the 3D plot types). */
|
|
1266
|
+
getSurface3dControls() {
|
|
1267
|
+
return {
|
|
1268
|
+
setSurfaceDragMode: (mode) => this.setSurfaceDragMode(mode),
|
|
1269
|
+
resetSurfaceCamera: () => this.resetSurfaceCamera(),
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
setSurfaceDragMode(mode) {
|
|
1273
|
+
if (this.plotDiv) {
|
|
1274
|
+
Plotly.relayout(this.plotDiv, { 'scene.dragmode': mode });
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
resetSurfaceCamera() {
|
|
1278
|
+
if (this.plotDiv) {
|
|
1279
|
+
Plotly.relayout(this.plotDiv, { 'scene.camera': {} });
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
triggerZoom(coordinates) {
|
|
1283
|
+
if (coordinates.length === 0 || !this.trueImgSize)
|
|
1284
|
+
return;
|
|
1285
|
+
this.zoomCoordinates = coordinates;
|
|
1286
|
+
const rect = this.plotUtilities.getRectangle(coordinates, this.trueImgSize);
|
|
1287
|
+
if (this.plotUtilities.isZoomSameAsImgSize(rect, this.trueImgSize)) {
|
|
1288
|
+
this.autoscale();
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
// A brief "Caching image..." message for uncached files (large files take a
|
|
1292
|
+
// moment to cache); just a spinner otherwise.
|
|
1293
|
+
this.state.setImageLoadingMessage(this.imageCached ? '' : 'Caching image...');
|
|
1294
|
+
const screen = this.plotUtilities.getDomRectangle('diagram');
|
|
1295
|
+
const imageSize = [];
|
|
1296
|
+
imageSize[0] = rect.x;
|
|
1297
|
+
imageSize[1] = rect.x + rect.width;
|
|
1298
|
+
imageSize[2] = rect.y;
|
|
1299
|
+
imageSize[3] = rect.y + rect.height;
|
|
1300
|
+
// Snapshot the filename so a response that arrives after the user switched
|
|
1301
|
+
// files is dropped (the request carried the file selected at call time).
|
|
1302
|
+
const reqName = this.fileName;
|
|
1303
|
+
this.state.setImageLoading(true);
|
|
1304
|
+
this.state.setZoom(true);
|
|
1305
|
+
this.tiles.zoomOnRegion(rect, screen, this.zIndex.value).subscribe({ next: zoomData => {
|
|
1306
|
+
const uint8Array = new Uint8Array(zoomData);
|
|
1307
|
+
const buffer = Buffer.from(uint8Array);
|
|
1308
|
+
Image.load(buffer).then((image) => {
|
|
1309
|
+
const xRatio = rect.width / image.width;
|
|
1310
|
+
const yRatio = rect.height / image.height;
|
|
1311
|
+
if (this.fileName === reqName) {
|
|
1312
|
+
const isGray = this.imageInfo.isGrayscale;
|
|
1313
|
+
const frame = isGray
|
|
1314
|
+
? this.plotUtilities.arrayToMatrix(image.grey().data, image.width)
|
|
1315
|
+
: this.plotUtilities.arrayToMatrix(image.getPixelsArray(), image.width);
|
|
1316
|
+
// Also sample the intensity profiles from this high-def crop so the
|
|
1317
|
+
// inset reflects the zoom-level resolution (origin = crop top-left).
|
|
1318
|
+
this.setSamplingFrames([frame], [xRatio, yRatio], [imageSize[0], imageSize[2]]);
|
|
1319
|
+
this.emitProfiles();
|
|
1320
|
+
// Re-render the high-def crop in the SAME plot type the user is
|
|
1321
|
+
// viewing. Without this the zoom re-fetch always fell back to a
|
|
1322
|
+
// heatmap, so zooming in contour (or any registry type) reverted
|
|
1323
|
+
// to heatmap.
|
|
1324
|
+
const impl = PLOTLY_PLOT_TYPE_IMPLS[this.plotType];
|
|
1325
|
+
const renderPromise = impl
|
|
1326
|
+
? this.plotViaRegistry(this.plotDiv, impl, this.buildTraceInput(this.imageInfo, {
|
|
1327
|
+
data: [frame], ratios: [xRatio, yRatio],
|
|
1328
|
+
sizes: [image.width, image.height], filename: reqName,
|
|
1329
|
+
}, imageSize), this.screenHeight)
|
|
1330
|
+
: (isGray
|
|
1331
|
+
? this.plotHeatmap(this.plotDiv, this.urls, [frame], imageSize, [xRatio, yRatio], this.screenHeight)
|
|
1332
|
+
: this.plotRGBHeatmap(this.plotDiv, this.urls, [frame], imageSize, [xRatio, yRatio], image.width, image.height, this.screenHeight));
|
|
1333
|
+
// plotViaRegistry already applies the type's own layout/range for
|
|
1334
|
+
// non-image layouts (chart/overlay); only re-apply the heatmap
|
|
1335
|
+
// range for the image-aligned paths.
|
|
1336
|
+
const reapplyRange = !impl || impl.layoutKind === '2d-image';
|
|
1337
|
+
renderPromise.then(() => {
|
|
1338
|
+
this.state.setImageCached(true);
|
|
1339
|
+
this.state.setImageLoading(false);
|
|
1340
|
+
image = null;
|
|
1341
|
+
if (reapplyRange)
|
|
1342
|
+
this.relayout(imageSize);
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
}, error: err => {
|
|
1347
|
+
const msg = err?.error?.message || err?.message || err?.statusText || String(err);
|
|
1348
|
+
console.error('Error occured when zooming', err);
|
|
1349
|
+
this.messageService.add({ key: 'center-toast', sticky: true, severity: 'error', summary: 'An error occured',
|
|
1350
|
+
detail: `The following error occured while zooming: ${msg}.
|
|
1351
|
+
Please try to open the image again through the file navigator and
|
|
1352
|
+
zoom on the selected area once more.` });
|
|
1353
|
+
this.state.setLoadingError(true);
|
|
1354
|
+
this.state.setImageLoading(false);
|
|
1355
|
+
}, complete: () => {
|
|
1356
|
+
console.log('zooming complete');
|
|
1357
|
+
} });
|
|
1358
|
+
}
|
|
1359
|
+
setPlotType(plotType) {
|
|
1360
|
+
this.plotType = plotType;
|
|
1361
|
+
}
|
|
1362
|
+
setImageInfo(showStack, scaleratio) {
|
|
1363
|
+
// Build a partial image descriptor and push it to the host via the port.
|
|
1364
|
+
const imgInfo = {
|
|
1365
|
+
isGrayscale: this.imageInfo.isGrayscale,
|
|
1366
|
+
trueImageSize: [this.trueImgSize[1], this.trueImgSize[3]],
|
|
1367
|
+
urls: this.urls,
|
|
1368
|
+
isStack: this.urls.length > 1,
|
|
1369
|
+
scaleRatio: scaleratio !== undefined ? scaleratio : this.scaleratio,
|
|
1370
|
+
};
|
|
1371
|
+
if (showStack !== undefined)
|
|
1372
|
+
imgInfo.showStack = showStack;
|
|
1373
|
+
if (this.fileName)
|
|
1374
|
+
imgInfo.fileName = this.fileName;
|
|
1375
|
+
this.state.setImageInfo(imgInfo);
|
|
1376
|
+
}
|
|
1377
|
+
getShapes() {
|
|
1378
|
+
return this.shapes;
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Framework-neutral view of the current regions — delegates to the shared
|
|
1382
|
+
* RegionStore (the single source of truth). The canonical accessor on the
|
|
1383
|
+
* `IVisualizer` contract; consumers should prefer this over `getShapes()`
|
|
1384
|
+
* (which exposes Plotly-shaped dicts).
|
|
1385
|
+
*/
|
|
1386
|
+
getRegions() {
|
|
1387
|
+
return this.regionStore.getRegions();
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Switch the active image. The shared RegionStore owns the per-image region
|
|
1391
|
+
* cache (snapshot outgoing / restore incoming / clear selection / emit); we
|
|
1392
|
+
* delegate to it, then re-project the store's regions into Plotly's dict
|
|
1393
|
+
* working-set. Re-projecting unconditionally (even when the store no-ops for
|
|
1394
|
+
* the same image) means Plotly also picks up regions another backend (OSD)
|
|
1395
|
+
* added while it was off-screen. Called from `plot()` and from the router
|
|
1396
|
+
* before any backend renders.
|
|
1397
|
+
*/
|
|
1398
|
+
setActiveImage(imageInfo) {
|
|
1399
|
+
this.regionStore.setActiveImage(imageInfo);
|
|
1400
|
+
this.syncShapesFromStore();
|
|
1401
|
+
this.previousShapes = this.shapes.slice();
|
|
1402
|
+
}
|
|
1403
|
+
/** Test/dev hook — drop the entire per-image cache (logout / project switch). */
|
|
1404
|
+
clearRegionsByImageKey() {
|
|
1405
|
+
this.regionStore.clearRegionsByImageKey();
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Get all the selected regions as polygons. Open polylines (closed === false)
|
|
1409
|
+
* are excluded — they're annotation-only and cannot be used as filled regions
|
|
1410
|
+
* for image processing. Delegates to the shared store.
|
|
1411
|
+
*/
|
|
1412
|
+
getRegionPolygons() {
|
|
1413
|
+
return this.regionStore.getRegionPolygons();
|
|
1414
|
+
}
|
|
1415
|
+
isRectangle(bnds) {
|
|
1416
|
+
return 'x' in bnds && 'y' in bnds && 'width' in bnds && 'height' in bnds;
|
|
1417
|
+
}
|
|
1418
|
+
isPolygon(bnds) {
|
|
1419
|
+
return 'npoints' in bnds && 'xpoints' in bnds && 'ypoints' in bnds;
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Set plot regions. Delegates the state change to the shared RegionStore
|
|
1423
|
+
* (id/name minting, classification colours, append de-duplication, per-image
|
|
1424
|
+
* cache and the region-update event all live there), then re-projects the
|
|
1425
|
+
* store's regions into Plotly's dict working-set and renders.
|
|
1426
|
+
*
|
|
1427
|
+
* When `isRegionSaveOn` is false the regions are shown transiently — rendered
|
|
1428
|
+
* without altering the stored working-set (preserves the prior behaviour).
|
|
1429
|
+
*/
|
|
1430
|
+
setRegions(regions, showRegionLabel, isRegionSaveOn, fillColor, append = false) {
|
|
1431
|
+
const showLabel = showRegionLabel === undefined ? this.regionStore.getShowShapeLabel() : showRegionLabel;
|
|
1432
|
+
const save = isRegionSaveOn === undefined ? this.isRegionSavedOn : isRegionSaveOn;
|
|
1433
|
+
this.regionStore.setRegions(regions, showRegionLabel, isRegionSaveOn, fillColor, append);
|
|
1434
|
+
this.isRegionSavedOn = save;
|
|
1435
|
+
if (save) {
|
|
1436
|
+
// Re-project the authoritative regions into Plotly's working-set and render.
|
|
1437
|
+
this.syncShapesFromStore();
|
|
1438
|
+
this.renderShapes();
|
|
1439
|
+
}
|
|
1440
|
+
else {
|
|
1441
|
+
// Transient display only — render the passed regions, don't touch the
|
|
1442
|
+
// working-set (and the store didn't store them either).
|
|
1443
|
+
const dicts = regions.map(r => { r.filename = this.fileName; return { ...r.getShape(showLabel) }; });
|
|
1444
|
+
try {
|
|
1445
|
+
Plotly.relayout(this.plotDiv, this.shapesRelayout(dicts));
|
|
1446
|
+
}
|
|
1447
|
+
catch { /* div owned by another backend (OSD) — its overlay renders shapes */ }
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
/** The shapes-relayout payload: region shapes (profile-line ROIs included, as
|
|
1451
|
+
* they are ordinary regions) plus the active-shape fill colour. */
|
|
1452
|
+
shapesRelayout(shapeDicts) {
|
|
1453
|
+
return {
|
|
1454
|
+
shapes: shapeDicts,
|
|
1455
|
+
activeshape: { fillcolor: this.regionStore.getFillColor() },
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
/** Push the current dict working-set to Plotly (no-op when another backend
|
|
1459
|
+
* owns the div). */
|
|
1460
|
+
renderShapes() {
|
|
1461
|
+
if (!this.plotDiv)
|
|
1462
|
+
return;
|
|
1463
|
+
const dictArray = this.shapes.map(s => ({ ...s }));
|
|
1464
|
+
try {
|
|
1465
|
+
Plotly.relayout(this.plotDiv, this.shapesRelayout(dictArray));
|
|
1466
|
+
}
|
|
1467
|
+
catch { /* div owned by another backend (OSD) — its overlay renders shapes */ }
|
|
1468
|
+
}
|
|
1469
|
+
getShowShapeLabel() {
|
|
1470
|
+
return this.regionStore.getShowShapeLabel();
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Set previous saved shapes
|
|
1474
|
+
*/
|
|
1475
|
+
plotPreviousShapes() {
|
|
1476
|
+
// convert to dict so that plotly recognises the shapes
|
|
1477
|
+
const dictArray = this.previousShapes.map(s => ({ ...s }));
|
|
1478
|
+
try {
|
|
1479
|
+
Plotly.relayout(this.plotDiv, { shapes: dictArray });
|
|
1480
|
+
}
|
|
1481
|
+
catch { /* div owned by another backend (OSD) */ }
|
|
1482
|
+
}
|
|
1483
|
+
setPreviousShapes(shapes) {
|
|
1484
|
+
this.previousShapes = shapes;
|
|
1485
|
+
}
|
|
1486
|
+
getPreviousShapes() {
|
|
1487
|
+
return this.previousShapes;
|
|
1488
|
+
}
|
|
1489
|
+
// Undo (jit-ui#85): region history lives in the shared RegionStore, so the
|
|
1490
|
+
// active backend's overlay re-renders off its regionUpdate$ on restore.
|
|
1491
|
+
undo() { this.regionStore.undo(); }
|
|
1492
|
+
redo() { this.regionStore.redo(); }
|
|
1493
|
+
canUndo() { return this.regionStore.canUndo(); }
|
|
1494
|
+
canRedo() { return this.regionStore.canRedo(); }
|
|
1495
|
+
getCanUndo$() { return this.regionStore.getCanUndo$(); }
|
|
1496
|
+
getCanRedo$() { return this.regionStore.getCanRedo$(); }
|
|
1497
|
+
resetUndoHistory() { this.regionStore.resetUndoHistory(); }
|
|
1498
|
+
// ── Per-slice z-stack regions → RegionStore (jit-ui#93) ──────────────────
|
|
1499
|
+
enterStackMode(slices, initialZ, saveLayout) {
|
|
1500
|
+
this.regionStore.enterStackMode(slices, initialZ, saveLayout);
|
|
1501
|
+
}
|
|
1502
|
+
exitStackMode() { this.regionStore.exitStackMode(); }
|
|
1503
|
+
isStackMode() { return this.regionStore.isStackMode(); }
|
|
1504
|
+
getStackSaveLayout() {
|
|
1505
|
+
return this.regionStore.getStackSaveLayout();
|
|
1506
|
+
}
|
|
1507
|
+
setDisplaySlice(z) { this.regionStore.setDisplaySlice(z); }
|
|
1508
|
+
getSliceRegions() { return this.regionStore.getSliceRegions(); }
|
|
1509
|
+
getStackSaveSlices() { return this.regionStore.getStackSaveSlices(); }
|
|
1510
|
+
getHeatmapLayout(xRange, yRange) {
|
|
1511
|
+
return {
|
|
1512
|
+
xaxis: {
|
|
1513
|
+
constrain: 'range',
|
|
1514
|
+
constraintoward: 'center',
|
|
1515
|
+
side: 'top',
|
|
1516
|
+
ticks: '',
|
|
1517
|
+
range: xRange
|
|
1518
|
+
},
|
|
1519
|
+
yaxis: {
|
|
1520
|
+
constrain: 'range',
|
|
1521
|
+
constraintoward: 'center',
|
|
1522
|
+
range: yRange,
|
|
1523
|
+
ticks: '',
|
|
1524
|
+
ticksuffix: ' ',
|
|
1525
|
+
// set autorange to false so that plotly does not overwrite the range for the y axis
|
|
1526
|
+
autorange: false,
|
|
1527
|
+
scaleanchor: this.scaleratio ? 'x' : false
|
|
1528
|
+
},
|
|
1529
|
+
margin: { t: 30, b: 5, l: 55, r: 5 },
|
|
1530
|
+
height: this.screenHeight,
|
|
1531
|
+
sliders: [{
|
|
1532
|
+
pad: { t: 50 },
|
|
1533
|
+
currentvalue: {
|
|
1534
|
+
visible: true,
|
|
1535
|
+
prefix: 'Z-plane:',
|
|
1536
|
+
xanchor: 'right',
|
|
1537
|
+
},
|
|
1538
|
+
steps: this.getSteps()
|
|
1539
|
+
}],
|
|
1540
|
+
autosize: true,
|
|
1541
|
+
shapes: this.currentRenderShapes(this.regionStore.getShowShapeLabel()),
|
|
1542
|
+
activeshape: { fillcolor: this.regionStore.getFillColor() },
|
|
1543
|
+
dragmode: this.dragMode ? this.dragMode : false,
|
|
1544
|
+
newshape: { line: { color: this.regionStore.getShapeColor(), width: 3 } }
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
shapesToRedraw(showLabel) {
|
|
1548
|
+
const shapesToRedraw = [];
|
|
1549
|
+
for (const shape of this.shapes) {
|
|
1550
|
+
if (JSON.stringify(shape.fileName) === JSON.stringify(this.fileName)) {
|
|
1551
|
+
// Set label
|
|
1552
|
+
if (showLabel) {
|
|
1553
|
+
shape.label = {
|
|
1554
|
+
text: `${shape.legend}`,
|
|
1555
|
+
texttemplate: `${shape.legend}`,
|
|
1556
|
+
textposition: 'top left',
|
|
1557
|
+
font: { color: `${shape.line.color}` }
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
else {
|
|
1561
|
+
shape.label = {};
|
|
1562
|
+
}
|
|
1563
|
+
shapesToRedraw.push(shape);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
// convert to dict so that plotly recognises the shapes
|
|
1567
|
+
return shapesToRedraw.map(s => ({ ...s }));
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Unused (will be used when surface plot is added)
|
|
1571
|
+
* @param xRange
|
|
1572
|
+
* @param yRange
|
|
1573
|
+
* @param zRatio
|
|
1574
|
+
* @private
|
|
1575
|
+
*/
|
|
1576
|
+
getSurfaceLayout(xRange, yRange, zRatio) {
|
|
1577
|
+
return {
|
|
1578
|
+
margin: { t: 0, b: 0, l: 0, r: 0 },
|
|
1579
|
+
scene: {
|
|
1580
|
+
xaxis: {
|
|
1581
|
+
gridcolor: 'rgb(255, 255, 255)',
|
|
1582
|
+
zerolinecolor: 'rgb(255, 255, 255)',
|
|
1583
|
+
showbackground: true,
|
|
1584
|
+
backgroundcolor: 'rgb(230, 230,230)',
|
|
1585
|
+
// range: xRange
|
|
1586
|
+
},
|
|
1587
|
+
yaxis: {
|
|
1588
|
+
gridcolor: 'rgb(255, 255, 255)',
|
|
1589
|
+
zerolinecolor: 'rgb(255, 255, 255)',
|
|
1590
|
+
showbackground: true,
|
|
1591
|
+
backgroundcolor: 'rgb(230, 230, 230)',
|
|
1592
|
+
// autorange: 'reversed',
|
|
1593
|
+
// range: yRange,
|
|
1594
|
+
// scaleanchor: 'x'
|
|
1595
|
+
},
|
|
1596
|
+
zaxis: {
|
|
1597
|
+
gridcolor: 'rgb(255, 255, 255)',
|
|
1598
|
+
zerolinecolor: 'rgb(255, 255, 255)',
|
|
1599
|
+
showbackground: true,
|
|
1600
|
+
backgroundcolor: 'rgb(230, 230,230)'
|
|
1601
|
+
},
|
|
1602
|
+
aspectratio: { x: 1, y: 1, z: zRatio },
|
|
1603
|
+
aspectmode: 'manual',
|
|
1604
|
+
sliders: [{
|
|
1605
|
+
pad: { t: 50 },
|
|
1606
|
+
currentvalue: {
|
|
1607
|
+
visible: true,
|
|
1608
|
+
prefix: 'Z-plane:',
|
|
1609
|
+
xanchor: 'right',
|
|
1610
|
+
},
|
|
1611
|
+
steps: this.getSteps()
|
|
1612
|
+
}],
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
getSteps() {
|
|
1617
|
+
const steps = [];
|
|
1618
|
+
for (let i = 0; i < this.imageLength; i++) {
|
|
1619
|
+
steps.push({
|
|
1620
|
+
label: i + 1,
|
|
1621
|
+
method: 'restyle',
|
|
1622
|
+
args: ['visible', Array(this.imageLength).fill(false).fill(true, i, i + 1)],
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
return steps;
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Fetch an image via Angular HttpClient so that auth interceptors (Bearer token)
|
|
1629
|
+
* are applied, then decode it with image-js. This avoids raw browser fetch()
|
|
1630
|
+
* calls that bypass the interceptor chain and fail behind an OAuth2 proxy.
|
|
1631
|
+
*/
|
|
1632
|
+
async loadImage(url) {
|
|
1633
|
+
const buffer = await firstValueFrom(this.http.get(url, { responseType: 'arraybuffer' }));
|
|
1634
|
+
return Image.load(Buffer.from(buffer));
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Get the currently displayed image as an image-js Image instance.
|
|
1638
|
+
* Used by the processing pipeline to obtain the input image.
|
|
1639
|
+
*/
|
|
1640
|
+
async getCurrentImage() {
|
|
1641
|
+
if (!this.imageInfo || !this.imageInfo.urls || this.imageInfo.urls.length === 0) {
|
|
1642
|
+
return null;
|
|
1643
|
+
}
|
|
1644
|
+
const zIdx = this.zIndex.value || 0;
|
|
1645
|
+
const url = this.imageInfo.urls[zIdx] || this.imageInfo.urls[0];
|
|
1646
|
+
return this.loadImage(url);
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Get the currently displayed plot pixel data as a flat Uint8ClampedArray.
|
|
1650
|
+
* This captures whatever is actually rendered — including zoomed regions.
|
|
1651
|
+
* Returns { width, height, channels, data } or null if no plot is displayed.
|
|
1652
|
+
*/
|
|
1653
|
+
getDisplayedPixelData() {
|
|
1654
|
+
if (!this.plotDiv)
|
|
1655
|
+
return null;
|
|
1656
|
+
const gd = document.getElementById(this.plotDiv);
|
|
1657
|
+
if (!gd?.data || gd.data.length === 0)
|
|
1658
|
+
return null;
|
|
1659
|
+
// Find the visible trace
|
|
1660
|
+
const frameIdx = this.activeFrameIndex();
|
|
1661
|
+
const trace = gd.data[frameIdx] || gd.data[0];
|
|
1662
|
+
if (!trace || !trace.z)
|
|
1663
|
+
return null;
|
|
1664
|
+
const zData = trace.z;
|
|
1665
|
+
const height = zData.length;
|
|
1666
|
+
if (height === 0)
|
|
1667
|
+
return null;
|
|
1668
|
+
const width = zData[0].length;
|
|
1669
|
+
if (trace.type === 'image') {
|
|
1670
|
+
// RGB image: z[row][col] = [r, g, b] or [r, g, b, a]
|
|
1671
|
+
const sample = zData[0][0];
|
|
1672
|
+
const channels = Array.isArray(sample) ? sample.length : 3;
|
|
1673
|
+
const data = new Uint8ClampedArray(width * height * channels);
|
|
1674
|
+
for (let y = 0; y < height; y++) {
|
|
1675
|
+
for (let x = 0; x < width; x++) {
|
|
1676
|
+
const pixel = zData[y][x];
|
|
1677
|
+
const offset = (y * width + x) * channels;
|
|
1678
|
+
for (let c = 0; c < channels; c++) {
|
|
1679
|
+
data[offset + c] = pixel[c];
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
return { width, height, channels, data };
|
|
1684
|
+
}
|
|
1685
|
+
else {
|
|
1686
|
+
// Heatmap (grayscale): z[row][col] = scalar value
|
|
1687
|
+
const data = new Uint8ClampedArray(width * height);
|
|
1688
|
+
for (let y = 0; y < height; y++) {
|
|
1689
|
+
for (let x = 0; x < width; x++) {
|
|
1690
|
+
data[y * width + x] = Math.round(zData[y][x]);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
return { width, height, channels: 1, data };
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Region of the original image that the displayed `z` grid covers, in
|
|
1698
|
+
* full-image pixel coords. On a server zoom (`triggerZoom`) the trace `z` is
|
|
1699
|
+
* replaced with the high-def crop and the axes are relaid out to the crop's
|
|
1700
|
+
* original-image bounds (`imageSize`), so the live axis ranges ARE that
|
|
1701
|
+
* rectangle; when the whole image is in view the ranges span `trueImgSize`.
|
|
1702
|
+
* Reading the ranges therefore yields the crop origin + extent without
|
|
1703
|
+
* tracking zoom state separately. Falls back to the full image, then `null`.
|
|
1704
|
+
*/
|
|
1705
|
+
getDisplayedSourceRect() {
|
|
1706
|
+
const gd = this.plotDiv ? document.getElementById(this.plotDiv) : null;
|
|
1707
|
+
const xr = gd?._fullLayout?.xaxis?.range;
|
|
1708
|
+
const yr = gd?._fullLayout?.yaxis?.range;
|
|
1709
|
+
if (xr && yr) {
|
|
1710
|
+
const x0 = Math.min(xr[0], xr[1]);
|
|
1711
|
+
const y0 = Math.min(yr[0], yr[1]); // y axis is reversed for image layouts
|
|
1712
|
+
return { x: x0, y: y0, width: Math.abs(xr[1] - xr[0]), height: Math.abs(yr[1] - yr[0]) };
|
|
1713
|
+
}
|
|
1714
|
+
if (!this.trueImgSize)
|
|
1715
|
+
return null;
|
|
1716
|
+
return {
|
|
1717
|
+
x: this.trueImgSize[0],
|
|
1718
|
+
y: this.trueImgSize[2],
|
|
1719
|
+
width: this.trueImgSize[1] - this.trueImgSize[0],
|
|
1720
|
+
height: this.trueImgSize[3] - this.trueImgSize[2],
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
reset() {
|
|
1724
|
+
if (this.plotDiv) {
|
|
1725
|
+
Plotly.newPlot(this.plotDiv, [], this.getHeatmapLayout([0, 100], [100, 0]), CONFIG);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Fully remove Plotly's DOM from the plot div (axes included). Used when
|
|
1730
|
+
* handing the div to another backend (OpenSeadragon) — unlike `reset()`,
|
|
1731
|
+
* which re-renders an empty plot and would leave the Plotly axes showing.
|
|
1732
|
+
*/
|
|
1733
|
+
purgePlot() {
|
|
1734
|
+
if (this.plotDiv) {
|
|
1735
|
+
Plotly.purge(this.plotDiv);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
/** Export the heatmap as a PNG (the rendered plot already reflects the active
|
|
1739
|
+
* colormap/window). The full-res tile composite is OSD's job. */
|
|
1740
|
+
exportComposite() {
|
|
1741
|
+
this.downloadImage();
|
|
1742
|
+
}
|
|
1743
|
+
downloadImage() {
|
|
1744
|
+
if (this.plotDiv) {
|
|
1745
|
+
Plotly.downloadImage(this.plotDiv, { format: 'png', filename: this.fileName || 'image' });
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
resetAxes() {
|
|
1749
|
+
this.zoomCoordinates = [];
|
|
1750
|
+
this.relayout();
|
|
1751
|
+
}
|
|
1752
|
+
setDragMode(mode) {
|
|
1753
|
+
this.dragMode = mode ? mode : '';
|
|
1754
|
+
if (!this.plotDiv)
|
|
1755
|
+
return;
|
|
1756
|
+
// Only relayout a LIVE Plotly graph. When another backend (OSD / napari-js) owns the view the
|
|
1757
|
+
// div id is still set but has no Plotly plot attached, and Plotly.relayout throws
|
|
1758
|
+
// ("can't access property _guiEditing, n is undefined"). That throw aborted the caller
|
|
1759
|
+
// (deactivateActiveTool → onSelectPlotType) before the re-plot ran, wedging the plot type
|
|
1760
|
+
// (e.g. stuck switching napari volume ↔ isosurface). Guard on `_fullLayout` like the zoom
|
|
1761
|
+
// helpers do so it safely no-ops when Plotly isn't the active renderer.
|
|
1762
|
+
const gd = document.getElementById(this.plotDiv);
|
|
1763
|
+
if (!gd?._fullLayout)
|
|
1764
|
+
return;
|
|
1765
|
+
Plotly.relayout(this.plotDiv, { dragmode: mode });
|
|
1766
|
+
}
|
|
1767
|
+
/** {@link IDataRenderer} stub — Plotly has no overview navigator (it's an
|
|
1768
|
+
* OpenSeadragon feature), so toggling it is a no-op here. */
|
|
1769
|
+
setNavigatorVisible(_visible) {
|
|
1770
|
+
/* no-op: navigator is OpenSeadragon-only */
|
|
1771
|
+
}
|
|
1772
|
+
/** {@link IDataRenderer} stub — image smoothing is an OpenSeadragon canvas-drawer
|
|
1773
|
+
* setting; the Plotly image/heatmap traces don't expose it, so it's a no-op. */
|
|
1774
|
+
setImageSmoothingEnabled(_enabled) {
|
|
1775
|
+
/* no-op: smoothing toggle is OpenSeadragon-only */
|
|
1776
|
+
}
|
|
1777
|
+
zoomIn() {
|
|
1778
|
+
if (!this.plotDiv)
|
|
1779
|
+
return;
|
|
1780
|
+
const gd = document.getElementById(this.plotDiv);
|
|
1781
|
+
if (!gd?._fullLayout)
|
|
1782
|
+
return;
|
|
1783
|
+
const xl = gd._fullLayout.xaxis;
|
|
1784
|
+
const yl = gd._fullLayout.yaxis;
|
|
1785
|
+
// 3D scenes have no 2D xaxis/yaxis (they live under `scene`); the step-zoom
|
|
1786
|
+
// is meaningless there and would throw on `xl.range`. Plotly's scene handles
|
|
1787
|
+
// scroll-zoom natively, so just bail.
|
|
1788
|
+
if (!xl?.range || !yl?.range)
|
|
1789
|
+
return;
|
|
1790
|
+
const xc = (xl.range[0] + xl.range[1]) / 2;
|
|
1791
|
+
const yc = (yl.range[0] + yl.range[1]) / 2;
|
|
1792
|
+
const dx = (xl.range[1] - xl.range[0]) / 1.3;
|
|
1793
|
+
const dy = (yl.range[1] - yl.range[0]) / 1.3;
|
|
1794
|
+
const x0 = xc - dx / 2, x1 = xc + dx / 2;
|
|
1795
|
+
const y0 = yc - dy / 2, y1 = yc + dy / 2;
|
|
1796
|
+
this.zoomCoordinates = [x0, x1, y0, y1];
|
|
1797
|
+
Plotly.relayout(this.plotDiv, {
|
|
1798
|
+
'xaxis.range[0]': x0, 'xaxis.range[1]': x1,
|
|
1799
|
+
'yaxis.range[0]': y0, 'yaxis.range[1]': y1
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Delete every currently selected shape. Falls back to the single
|
|
1804
|
+
* `_activeShapeIndex` when no multi-selection is active (covers the case
|
|
1805
|
+
* where Plotly clicked a shape without going through the table).
|
|
1806
|
+
*/
|
|
1807
|
+
deleteActiveShape() {
|
|
1808
|
+
// Note: no `plotDiv` guard — when OpenSeadragon is the renderer, Plotly
|
|
1809
|
+
// never plotted (plotDiv is unset), but deletion only needs the shape list
|
|
1810
|
+
// + the current selection; the Plotly relayout below is guarded.
|
|
1811
|
+
const gd = this.plotDiv ? document.getElementById(this.plotDiv) : null;
|
|
1812
|
+
// Resolve what to delete: the store's selection, else the shape Plotly
|
|
1813
|
+
// tracks as clicked (when the user clicked a shape without going through the
|
|
1814
|
+
// Region Editor table).
|
|
1815
|
+
if (this.regionStore.getSelectedShapeIndices().length === 0) {
|
|
1816
|
+
const activeIndex = gd?._fullLayout?._activeShapeIndex;
|
|
1817
|
+
if (activeIndex === undefined || activeIndex < 0)
|
|
1818
|
+
return;
|
|
1819
|
+
this.regionStore.setSelectedShapeIndices([activeIndex]);
|
|
1820
|
+
}
|
|
1821
|
+
// The store removes the selected regions, clears the selection, syncs its
|
|
1822
|
+
// cache and emits the region-update event.
|
|
1823
|
+
this.regionStore.deleteActiveShape();
|
|
1824
|
+
// Re-project the remaining regions and render on the Plotly plot (a no-op,
|
|
1825
|
+
// caught, when another backend owns the div — its overlay re-renders via
|
|
1826
|
+
// the store's update event).
|
|
1827
|
+
if (gd?._fullLayout)
|
|
1828
|
+
gd._fullLayout._activeShapeIndex = -1;
|
|
1829
|
+
this.syncShapesFromStore();
|
|
1830
|
+
const dictArray = this.shapes.map(s => ({ ...s }));
|
|
1831
|
+
try {
|
|
1832
|
+
Plotly.relayout(this.plotDiv, { shapes: dictArray }).then(() => {
|
|
1833
|
+
if (gd?._fullLayout)
|
|
1834
|
+
Plotly.redraw(gd);
|
|
1835
|
+
}, () => { });
|
|
1836
|
+
}
|
|
1837
|
+
catch { /* div owned by another backend */ }
|
|
1838
|
+
}
|
|
1839
|
+
zoomOut() {
|
|
1840
|
+
if (!this.plotDiv)
|
|
1841
|
+
return;
|
|
1842
|
+
const gd = document.getElementById(this.plotDiv);
|
|
1843
|
+
if (!gd?._fullLayout)
|
|
1844
|
+
return;
|
|
1845
|
+
const xl = gd._fullLayout.xaxis;
|
|
1846
|
+
const yl = gd._fullLayout.yaxis;
|
|
1847
|
+
// 3D scenes have no 2D xaxis/yaxis — see zoomIn().
|
|
1848
|
+
if (!xl?.range || !yl?.range)
|
|
1849
|
+
return;
|
|
1850
|
+
const xc = (xl.range[0] + xl.range[1]) / 2;
|
|
1851
|
+
const yc = (yl.range[0] + yl.range[1]) / 2;
|
|
1852
|
+
const dx = (xl.range[1] - xl.range[0]) * 1.3;
|
|
1853
|
+
const dy = (yl.range[1] - yl.range[0]) * 1.3;
|
|
1854
|
+
const x0 = xc - dx / 2, x1 = xc + dx / 2;
|
|
1855
|
+
const y0 = yc - dy / 2, y1 = yc + dy / 2;
|
|
1856
|
+
this.zoomCoordinates = [x0, x1, y0, y1];
|
|
1857
|
+
Plotly.relayout(this.plotDiv, {
|
|
1858
|
+
'xaxis.range[0]': x0, 'xaxis.range[1]': x1,
|
|
1859
|
+
'yaxis.range[0]': y0, 'yaxis.range[1]': y1
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
isStackLoading$() {
|
|
1863
|
+
return this.stackLoading$.asObservable();
|
|
1864
|
+
}
|
|
1865
|
+
setStackLoading(stackLoading) {
|
|
1866
|
+
this.stackLoading$.next(stackLoading);
|
|
1867
|
+
}
|
|
1868
|
+
getStackLoadingProgress$() {
|
|
1869
|
+
return this.stackLoadingProgress$.asObservable();
|
|
1870
|
+
}
|
|
1871
|
+
// Colormap / reverse-scale / image metadata state lives in the shared
|
|
1872
|
+
// VisualizerStore; these delegate, keeping only the Plotly-specific live
|
|
1873
|
+
// restyle as render glue.
|
|
1874
|
+
getColormapOptions() {
|
|
1875
|
+
return this.store.getColormapOptions();
|
|
1876
|
+
}
|
|
1877
|
+
setColormap(colormap) {
|
|
1878
|
+
this.store.setColormap(colormap);
|
|
1879
|
+
try {
|
|
1880
|
+
Plotly.restyle(this.plotDiv, { 'colorscale': [colormap.data.value] });
|
|
1881
|
+
}
|
|
1882
|
+
catch { /* div owned by another backend (OSD) — it recolors via the LUT */ }
|
|
1883
|
+
}
|
|
1884
|
+
getColormap() {
|
|
1885
|
+
return this.store.getColormap();
|
|
1886
|
+
}
|
|
1887
|
+
setReverseScale(reverscale) {
|
|
1888
|
+
this.store.setReverseScale(reverscale);
|
|
1889
|
+
try {
|
|
1890
|
+
Plotly.restyle(this.plotDiv, { 'reversescale': reverscale });
|
|
1891
|
+
}
|
|
1892
|
+
catch { /* div owned by another backend (OSD) — it recolors via the LUT */ }
|
|
1893
|
+
}
|
|
1894
|
+
getReverseScale() {
|
|
1895
|
+
return this.store.getReverseScale();
|
|
1896
|
+
}
|
|
1897
|
+
setImageMeta(imageMeta) {
|
|
1898
|
+
this.store.setImageMeta(imageMeta);
|
|
1899
|
+
}
|
|
1900
|
+
getImageMeta() {
|
|
1901
|
+
return this.store.getImageMeta();
|
|
1902
|
+
}
|
|
1903
|
+
setShowStack(showstack) {
|
|
1904
|
+
if (!showstack) {
|
|
1905
|
+
this.zIndex.next(0);
|
|
1906
|
+
}
|
|
1907
|
+
this.imageInfo.showStack = showstack;
|
|
1908
|
+
this.stackLoading$.next(showstack);
|
|
1909
|
+
Plotly.relayout(this.plotDiv, { 'showstack': showstack });
|
|
1910
|
+
}
|
|
1911
|
+
getAutoscaleEvent() {
|
|
1912
|
+
return this.autoscaleEvent.asObservable();
|
|
1913
|
+
}
|
|
1914
|
+
getRegionUpdateEvent() {
|
|
1915
|
+
return this.regionStore.getRegionUpdateEvent();
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1918
|
+
* Stream of the indices of currently selected regions — owned by the shared
|
|
1919
|
+
* RegionStore so the selection is consistent across backends and the Region
|
|
1920
|
+
* Editor table. Empty = nothing selected.
|
|
1921
|
+
*/
|
|
1922
|
+
getSelectedShapeIndices$() {
|
|
1923
|
+
return this.regionStore.getSelectedShapeIndices$();
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* Programmatically select regions (or clear with []). The shared store owns
|
|
1927
|
+
* the selection state (validates/dedupes/emits); here we additionally point
|
|
1928
|
+
* Plotly's `_activeShapeIndex` at the last selected shape so it gets the edit
|
|
1929
|
+
* handles. No visual change to non-active shapes — Plotly's own active-shape
|
|
1930
|
+
* rendering is the only highlight.
|
|
1931
|
+
*/
|
|
1932
|
+
setSelectedShapeIndices(indices) {
|
|
1933
|
+
const valid = (indices || [])
|
|
1934
|
+
.filter(i => Number.isFinite(i) && i >= 0 && i < this.shapes.length);
|
|
1935
|
+
const seen = new Set();
|
|
1936
|
+
const cleaned = [];
|
|
1937
|
+
for (const i of valid) {
|
|
1938
|
+
if (!seen.has(i)) {
|
|
1939
|
+
seen.add(i);
|
|
1940
|
+
cleaned.push(i);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if (this.plotDiv) {
|
|
1944
|
+
const gd = document.getElementById(this.plotDiv);
|
|
1945
|
+
if (gd?._fullLayout) {
|
|
1946
|
+
gd._fullLayout._activeShapeIndex = cleaned.length > 0 ? cleaned[cleaned.length - 1] : -1;
|
|
1947
|
+
try {
|
|
1948
|
+
Plotly.redraw(gd);
|
|
1949
|
+
}
|
|
1950
|
+
catch { /* noop in tests */ }
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
this.regionStore.setSelectedShapeIndices(cleaned);
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Select a specific region (IRegionStore.selectRegion). Sets the shared store
|
|
1957
|
+
* selection by identity and points Plotly's active-shape at the matching
|
|
1958
|
+
* rendered shape so its edit handles appear.
|
|
1959
|
+
*/
|
|
1960
|
+
selectRegion(region) {
|
|
1961
|
+
this.regionStore.selectRegion(region);
|
|
1962
|
+
if (this.plotDiv) {
|
|
1963
|
+
const idx = this.shapes.findIndex(s => s.id === region?.id);
|
|
1964
|
+
const gd = document.getElementById(this.plotDiv);
|
|
1965
|
+
if (gd?._fullLayout && idx >= 0) {
|
|
1966
|
+
gd._fullLayout._activeShapeIndex = idx;
|
|
1967
|
+
try {
|
|
1968
|
+
Plotly.redraw(gd);
|
|
1969
|
+
}
|
|
1970
|
+
catch { /* noop in tests */ }
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
/**
|
|
1975
|
+
* Read Plotly's `_activeShapeIndex` and push it as the selection. Called from
|
|
1976
|
+
* the relayout handler and the post-click tick — Plotly doesn't fire a
|
|
1977
|
+
* dedicated active-shape event for clicks, so we sample. The store no-ops if
|
|
1978
|
+
* the selection is unchanged.
|
|
1979
|
+
*/
|
|
1980
|
+
maybeEmitActiveShapeIndex() {
|
|
1981
|
+
if (!this.plotDiv)
|
|
1982
|
+
return;
|
|
1983
|
+
const gd = document.getElementById(this.plotDiv);
|
|
1984
|
+
const raw = gd?._fullLayout?._activeShapeIndex;
|
|
1985
|
+
const idx = (typeof raw === 'number' && raw >= 0 && raw < this.shapes.length) ? raw : -1;
|
|
1986
|
+
this.regionStore.setSelectedShapeIndices(idx >= 0 ? [idx] : []);
|
|
1987
|
+
}
|
|
1988
|
+
setZIndex(zIndex) {
|
|
1989
|
+
this.zIndex.next(zIndex);
|
|
1990
|
+
}
|
|
1991
|
+
getShapeColor() {
|
|
1992
|
+
return this.regionStore.getShapeColor();
|
|
1993
|
+
}
|
|
1994
|
+
getFillColor() {
|
|
1995
|
+
return this.regionStore.getFillColor();
|
|
1996
|
+
}
|
|
1997
|
+
getClassificationColors() {
|
|
1998
|
+
return this.store.getClassificationColors();
|
|
1999
|
+
}
|
|
2000
|
+
setClassificationColor(label, color) {
|
|
2001
|
+
this.store.setClassificationColor(label, color);
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* Unsubscribe Subscriptions
|
|
2005
|
+
*/
|
|
2006
|
+
unsubscribe() {
|
|
2007
|
+
if (this.imageCachedSubscription) {
|
|
2008
|
+
this.imageCachedSubscription.unsubscribe();
|
|
2009
|
+
}
|
|
2010
|
+
if (this.filenameSubscription) {
|
|
2011
|
+
this.filenameSubscription.unsubscribe();
|
|
2012
|
+
}
|
|
2013
|
+
if (this.regionUpdateSubscription) {
|
|
2014
|
+
this.regionUpdateSubscription.unsubscribe();
|
|
2015
|
+
}
|
|
2016
|
+
if (this.regionLiveEditSubscription) {
|
|
2017
|
+
this.regionLiveEditSubscription.unsubscribe();
|
|
2018
|
+
}
|
|
2019
|
+
if (this.channelSub) {
|
|
2020
|
+
this.channelSub.unsubscribe();
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
/** Live-apply the channel display window (zmin/zmax) + reverse/invert to the
|
|
2024
|
+
* heatmap. No-op when no Plotly plot is mounted (OSD owns the div). */
|
|
2025
|
+
applyChannelDisplay(channels, rev, inv) {
|
|
2026
|
+
if (!this.plotDiv)
|
|
2027
|
+
return;
|
|
2028
|
+
const ch = channels?.[0];
|
|
2029
|
+
const colorscale = this.store.currentColormap()?.data?.value;
|
|
2030
|
+
// reverse-scale and invert each flip the ramp; both together cancel.
|
|
2031
|
+
const update = { reversescale: rev !== inv };
|
|
2032
|
+
if (colorscale != null)
|
|
2033
|
+
update.colorscale = colorscale; // live colormap recolor
|
|
2034
|
+
if (ch) {
|
|
2035
|
+
// Surface/isosurface/scatter3d colour-map via cmin/cmax; heatmap/contour via zmin/zmax.
|
|
2036
|
+
const threeD = this.plotType === PlotType.ISOSURFACE ||
|
|
2037
|
+
this.plotType === PlotType.SURFACE ||
|
|
2038
|
+
this.plotType === PlotType.SCATTER3D;
|
|
2039
|
+
if (threeD) {
|
|
2040
|
+
update.cmin = ch.min;
|
|
2041
|
+
update.cmax = ch.max;
|
|
2042
|
+
update.cauto = false;
|
|
2043
|
+
}
|
|
2044
|
+
else {
|
|
2045
|
+
update.zmin = ch.min;
|
|
2046
|
+
update.zmax = ch.max;
|
|
2047
|
+
update.zauto = false;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
try {
|
|
2051
|
+
Plotly.restyle(this.plotDiv, update);
|
|
2052
|
+
}
|
|
2053
|
+
catch {
|
|
2054
|
+
/* not a colour-mapped trace, or OSD owns the div */
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
/** Binned intensity histogram for a channel from the cached source frames
|
|
2058
|
+
* (raw, pre-LUT). Grayscale cells are numbers; RGB cells are [r,g,b]. */
|
|
2059
|
+
getHistogram(channelIndex, _bins) {
|
|
2060
|
+
const frames = this.cachedImageFrames;
|
|
2061
|
+
if (!frames?.length)
|
|
2062
|
+
return null;
|
|
2063
|
+
const frame = frames[this.activeFrameIndex()] ?? frames[0];
|
|
2064
|
+
if (!frame?.length)
|
|
2065
|
+
return null;
|
|
2066
|
+
const counts = new Array(256).fill(0);
|
|
2067
|
+
for (const row of frame) {
|
|
2068
|
+
if (!row)
|
|
2069
|
+
continue;
|
|
2070
|
+
for (const cell of row) {
|
|
2071
|
+
let v;
|
|
2072
|
+
if (Array.isArray(cell)) {
|
|
2073
|
+
v = channelIndex >= 0 && channelIndex < cell.length
|
|
2074
|
+
? cell[channelIndex]
|
|
2075
|
+
: Math.round(bt601Luminance(cell[0], cell[1], cell[2]));
|
|
2076
|
+
}
|
|
2077
|
+
else {
|
|
2078
|
+
v = cell;
|
|
2079
|
+
}
|
|
2080
|
+
v = v | 0;
|
|
2081
|
+
if (v < 0)
|
|
2082
|
+
v = 0;
|
|
2083
|
+
else if (v > 255)
|
|
2084
|
+
v = 255;
|
|
2085
|
+
counts[v]++;
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
return histogram256(counts);
|
|
2089
|
+
}
|
|
2090
|
+
/** Async histogram stream — Plotly renders heatmap frame data (already in
|
|
2091
|
+
* memory, 8-bit luminance), so this just wraps the synchronous 8-bit
|
|
2092
|
+
* histogram. Native 16-bit histograms only apply to the OSD tile path. */
|
|
2093
|
+
getHistogram$(channelIndex, bins) {
|
|
2094
|
+
return of(this.getHistogram(channelIndex, bins));
|
|
2095
|
+
}
|
|
2096
|
+
/** Data export (16-bit TIFF) is an OSD-tile-path feature backed by the server.
|
|
2097
|
+
* Plotly renders heatmaps from frame data, so there's nothing to export here. */
|
|
2098
|
+
exportData() {
|
|
2099
|
+
console.warn('[plotly] 16-bit data export is not available for the heatmap backend.');
|
|
2100
|
+
}
|
|
2101
|
+
importRegions(geoJsonStr) {
|
|
2102
|
+
return this.plotUtilities.importROIsFromGeoJson(geoJsonStr);
|
|
2103
|
+
}
|
|
2104
|
+
exportRegions(regions) {
|
|
2105
|
+
const jsonString = this.plotUtilities.exportROIsToGeoJson(regions);
|
|
2106
|
+
this.plotUtilities.saveToFile(jsonString, this.fileName);
|
|
2107
|
+
}
|
|
2108
|
+
getGeoJsonString(regions) {
|
|
2109
|
+
return this.plotUtilities.exportROIsToGeoJson(regions);
|
|
2110
|
+
}
|
|
2111
|
+
getStackLoadingProgress() {
|
|
2112
|
+
return this.getStackLoadingProgress$();
|
|
2113
|
+
}
|
|
2114
|
+
isStackLoading() {
|
|
2115
|
+
return this.isStackLoading$();
|
|
2116
|
+
}
|
|
2117
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlotlyService, deps: [{ token: TILE_ACCESS_PORT }, { token: IMAGE_STATE_PORT }, { token: i1.MessageService }, { token: i2.HttpClient }, { token: i3.WandService }, { token: i4.WandToolService }, { token: i5.BrushToolService }, { token: i6.SamToolService }, { token: i7.SamPointToolService }, { token: i8.CellSegmentToolService }, { token: CELL_SEGMENTER, optional: true }, { token: i9.VertexEraserToolService }, { token: i10.ZoomToBoxToolService }, { token: i11.VisualizerStore }, { token: i12.RegionStore }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2118
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlotlyService, providedIn: 'root' }); }
|
|
2119
|
+
}
|
|
2120
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlotlyService, decorators: [{
|
|
2121
|
+
type: Injectable,
|
|
2122
|
+
args: [{
|
|
2123
|
+
providedIn: 'root'
|
|
2124
|
+
}]
|
|
2125
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
2126
|
+
type: Inject,
|
|
2127
|
+
args: [TILE_ACCESS_PORT]
|
|
2128
|
+
}] }, { type: undefined, decorators: [{
|
|
2129
|
+
type: Inject,
|
|
2130
|
+
args: [IMAGE_STATE_PORT]
|
|
2131
|
+
}] }, { type: i1.MessageService }, { type: i2.HttpClient }, { type: i3.WandService }, { type: i4.WandToolService }, { type: i5.BrushToolService }, { type: i6.SamToolService }, { type: i7.SamPointToolService }, { type: i8.CellSegmentToolService }, { type: undefined, decorators: [{
|
|
2132
|
+
type: Optional
|
|
2133
|
+
}, {
|
|
2134
|
+
type: Inject,
|
|
2135
|
+
args: [CELL_SEGMENTER]
|
|
2136
|
+
}] }, { type: i9.VertexEraserToolService }, { type: i10.ZoomToBoxToolService }, { type: i11.VisualizerStore }, { type: i12.RegionStore }] });
|
|
2137
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGxvdGx5LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2ltcGxlbWVudGF0aW9ucy9wbG90bHkvcGxvdGx5LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTdELE9BQU8sS0FBSyxNQUFNLE1BQU0sb0JBQW9CLENBQUM7QUFDN0MsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUNqQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUV0QyxPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBRWhDLE9BQU8sRUFBa0IsZ0JBQWdCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUMxRixPQUFPLEVBQWtCLGdCQUFnQixFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDMUYsT0FBTyxFQUFFLGVBQWUsRUFBRSxLQUFLLEVBQWMsT0FBTyxFQUFnQixhQUFhLEVBQUUsRUFBRSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3BHLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDN0MsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLGFBQWEsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzdFLE9BQU8sRUFBRSxXQUFXLEVBQWUsTUFBTSxpQ0FBaUMsQ0FBQztBQUMzRSxPQUFPLEVBQW1CLGVBQWUsRUFBZ0IsTUFBTSxzQ0FBc0MsQ0FBQztBQUN0RyxP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Q0FBQyxLQUFLLEdBQUcsS0FBSztlQUMxQyxHQUFHLENBQUMsQ0FBQyxHQUFHLFNBQVMsSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3RELE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsR0FBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7UUFDdkQsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFJLENBQUMsQ0FBQyxHQUFHLEdBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDO1FBQ3BFLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsR0FBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFDMUQsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxHQUFJLENBQUMsQ0FBQyxHQUFHLEdBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO1FBRWpGLE1BQU0sRUFBRSxHQUFHLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMzQixNQUFNLElBQUksR0FBRyxDQUFDLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsc0NBQXNDO1FBQ2hGLE1BQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7UUFFckMsTUFBTSxLQUFLLEdBQUcsT0FBTyxHQUFHLElBQUksQ0FBQztRQUM3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RUQsYUFBYSxDQUFDLE9BQWlCO1FBQzdCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDbkUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRUQsZ0JBQWdCLENBQUMsT0FBaUI7UUFDaEMsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFFRCx1QkFBdUI7UUFDckIsT0FBTyxJQUFJLENBQUMsd0JBQXdCLEVBQUUsQ0FBQztJQUN6QyxDQUFDO0lBRUQsY0FBYztRQUNaLE9BQU8sSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO0lBQ2hDLENBQUM7OEdBcnBFVSxhQUFhLGtCQThISixnQkFBZ0IsYUFDaEIsZ0JBQWdCLHNRQVFKLGNBQWM7a0hBdkluQyxhQUFhLGNBRlosTUFBTTs7MkZBRVAsYUFBYTtrQkFIekIsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkI7OzBCQStIYyxNQUFNOzJCQUFDLGdCQUFnQjs7MEJBQ3ZCLE1BQU07MkJBQUMsZ0JBQWdCOzswQkFRdkIsUUFBUTs7MEJBQUksTUFBTTsyQkFBQyxjQUFjIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgSW5qZWN0LCBPcHRpb25hbCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgKiBhcyBQbG90bHkgZnJvbSAncGxvdGx5LmpzLWRpc3QtbWluJztcbmltcG9ydCB7IEltYWdlIH0gZnJvbSAnaW1hZ2UtanMnO1xuaW1wb3J0IHsgSHR0cENsaWVudCB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7IGZpcnN0VmFsdWVGcm9tIH0gZnJvbSAncnhqcyc7XG5cbmltcG9ydCB7IFBvbHlnb24sIFJlY3RhbmdsZSwgUmVnaW9uIH0gZnJvbSAnLi4vLi4vbW9kZWxzL3JlZ2lvbic7XG5pbXBvcnQgeyBCdWZmZXIgfSBmcm9tICdidWZmZXInO1xuaW1wb3J0IHsgSUltYWdlSW5mbywgSUltYWdlTWV0YWRhdGEgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvaW1hZ2UuY29udHJhY3QnO1xuaW1wb3J0IHsgVGlsZUFjY2Vzc1BvcnQsIFRJTEVfQUNDRVNTX1BPUlQgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvcG9ydHMvdGlsZS1hY2Nlc3MucG9ydCc7XG5pbXBvcnQgeyBJbWFnZVN0YXRlUG9ydCwgSU1BR0VfU1RBVEVfUE9SVCB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9wb3J0cy9pbWFnZS1zdGF0ZS5wb3J0JztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgRU1QVFksIE9ic2VydmFibGUsIFN1YmplY3QsIFN1YnNjcmlwdGlvbiwgY29tYmluZUxhdGVzdCwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IE1lc3NhZ2VTZXJ2aWNlIH0gZnJvbSAncHJpbWVuZy9hcGknO1xuaW1wb3J0IHsgU2hhcGVTZWxlY3Rpb24gfSBmcm9tICcuLi8uLi9tb2RlbHMvc2hhcGUnO1xuaW1wb3J0IHsgQ09ORklHLCBDT05GSUdfU1VSRkFDRSwgUGxvdFV0aWxpdGllcyB9IGZyb20gJy4uLy4uL3Bsb3QudXRpbGl0aWVzJztcbmltcG9ydCB7IFdhbmRTZXJ2aWNlLCBXYW5kT3B0aW9ucyB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvd2FuZC93YW5kLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ2FjaGVkSW1hZ2VEYXRhLCBXYW5kVG9vbFNlcnZpY2UsIFdhbmRUb29sSG9zdCB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvd2FuZC93YW5kLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBCcnVzaFRvb2xTZXJ2aWNlLCBCcnVzaE9wdGlvbnMgfSBmcm9tICcuLi8uLi90b29sYmFyL2JydXNoL2JydXNoLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBTYW1Ub29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgU2FtUG9pbnRUb29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS1wb2ludC10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ2VsbFNlZ21lbnRUb29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvc2VnbWVudGF0aW9uL2NlbGwtc2VnbWVudC10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgSUNlbGxTZWdtZW50ZXIsIENFTExfU0VHTUVOVEVSIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2NlbGwtc2VnbWVudGVyLmNvbnRyYWN0JztcbmltcG9ydCB7IFZlcnRleEVyYXNlclRvb2xTZXJ2aWNlLCBWZXJ0ZXhFcmFzZXJUb29sSG9zdCB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvdmVydGV4LWVyYXNlci92ZXJ0ZXgtZXJhc2VyLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBab29tVG9Cb3hUb29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvem9vbS10by1ib3gvem9vbS10by1ib3gtdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7XG4gIHBhcnNlU3ZnUGF0aFBvbHlnb24sXG4gIHZlcnRpY2VzVG9TdmdQYXRoLFxufSBmcm9tICcuLi8uLi9tb2RlbHMvZ2VvbWV0cnknO1xuaW1wb3J0IHsgUGxvdFR5cGUsIFBMT1RfVFlQRV9ERVNDUklQVE9SUywgUGxvdFR5cGVEZXNjcmlwdG9yIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3Bsb3QtdHlwZSc7XG5pbXBvcnQge1xuICBQTE9UTFlfUExPVF9UWVBFX0lNUExTLFxuICBQbG90bHlQbG90VHlwZUltcGwsXG4gIFRyYWNlQnVpbGRJbnB1dCxcbn0gZnJvbSAnLi9wbG90bHktdHJhY2UtYnVpbGRlcnMnO1xuaW1wb3J0IHsgSVZpc3VhbGl6ZXIsIEludGVuc2l0eVByb2ZpbGUsIElJc29zdXJmYWNlQ29udHJvbHMsIElJbnRlbnNpdHlDb250cm9scyB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy92aXN1YWxpemVyLmNvbnRyYWN0JztcbmltcG9ydCB7IElIaXN0b2dyYW0gfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2hhbm5lbC1oaXN0b2dyYW0tYXBpLmNvbnRyYWN0JztcbmltcG9ydCB7IGJ0NjAxTHVtaW5hbmNlLCBoaXN0b2dyYW0yNTYgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvaW50ZW5zaXR5JztcbmltcG9ydCB7IFZpZXdlckNhcGFiaWxpdGllcywgVmlld2VyRmVhdHVyZSwgY2FwYWJpbGl0aWVzT2YgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2FwYWJpbGl0aWVzLmNvbnRyYWN0JztcbmltcG9ydCB7IElSZWdpb25PdmVybGF5IH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3JlZ2lvbi1vdmVybGF5LmNvbnRyYWN0JztcbmltcG9ydCB7IFBsb3RseVJlZ2lvbk92ZXJsYXkgfSBmcm9tICcuL3Bsb3RseS1yZWdpb24tb3ZlcmxheSc7XG5pbXBvcnQgeyBJQ29vcmRpbmF0ZVRyYW5zZm9ybSB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jb29yZGluYXRlLXRyYW5zZm9ybS5jb250cmFjdCc7XG5pbXBvcnQgeyBQbG90bHlDb29yZGluYXRlVHJhbnNmb3JtIH0gZnJvbSAnLi9wbG90bHktY29vcmRpbmF0ZS10cmFuc2Zvcm0nO1xuaW1wb3J0IHsgVmlzdWFsaXplclN0b3JlIH0gZnJvbSAnLi4vLi4vc3RvcmUvdmlzdWFsaXplci1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IFJlZ2lvblN0b3JlIH0gZnJvbSAnLi4vLi4vc3RvcmUvcmVnaW9uLXN0b3JlLnNlcnZpY2UnO1xuXG4vLyBSZS1leHBvcnRlZCBzbyBleGlzdGluZyBjb25zdW1lcnMgY2FuIGtlZXAgaW1wb3J0aW5nIFBsb3RUeXBlIGZyb20gdGhpc1xuLy8gbW9kdWxlIHdoaWxlIGl0IHBoeXNpY2FsbHkgbGl2ZXMgaW4gdGhlIGJhY2tlbmQtbmV1dHJhbCBjb250cmFjdHMvIGRpci5cbi8vIFRPRE8ocGxvdHRpbmctYWJzdHJhY3Rpb24pOiBvbmNlIGFsbCBjb25zdW1lcnMgaW1wb3J0IGZyb21cbi8vICcuL2NvbnRyYWN0cy9wbG90LXR5cGUnIGRpcmVjdGx5LCBkcm9wIHRoaXMgcmUtZXhwb3J0LlxuZXhwb3J0IHsgUGxvdFR5cGUgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvcGxvdC10eXBlJztcblxuXG4od2luZG93IGFzIGFueSkuQnVmZmVyID0gQnVmZmVyO1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBQbG90bHlTZXJ2aWNlIGltcGxlbWVudHMgSVZpc3VhbGl6ZXIge1xuXG4gIC8qKlxuICAgKiBQbG90bHkgaXMgdGhlIGZ1bGwtZmVhdHVyZWQgZGF0YSBiYWNrZW5kOiBpdCBzdXBwb3J0cyBldmVyeSBmZWF0dXJlLFxuICAgKiBpbmNsdWRpbmcgM0Qgc2NlbmVzLCBsaXZlIHNjYWxhciBjb2xvcm1hcHMsIHBpeGVsIHJlYWRiYWNrIGFuZCB0aGVcbiAgICogc2VydmVyLXNpZGUgaGlnaC1kZWYgem9vbSByZS1mZXRjaC4gKE9wZW5TZWFkcmFnb24ncyBzdHViIGFkdmVydGlzZXMgb25seVxuICAgKiBpbWFnZSBkaXNwbGF5LilcbiAgICovXG4gIHJlYWRvbmx5IGNhcGFiaWxpdGllczogVmlld2VyQ2FwYWJpbGl0aWVzID0gY2FwYWJpbGl0aWVzT2YoW1xuICAgIFZpZXdlckZlYXR1cmUuSW1hZ2VEaXNwbGF5LFxuICAgIFZpZXdlckZlYXR1cmUuU3VyZmFjZTNELFxuICAgIFZpZXdlckZlYXR1cmUuU2NhbGFyQ29sb3JtYXAsXG4gICAgVmlld2VyRmVhdHVyZS5QaXhlbFJlYWRiYWNrLFxuICAgIFZpZXdlckZlYXR1cmUuSGlnaERlZlpvb20sXG4gICAgVmlld2VyRmVhdHVyZS5TdGFja1NsaWRlcixcbiAgICBWaWV3ZXJGZWF0dXJlLklzb3N1cmZhY2UsXG4gIF0pO1xuXG4gIHByaXZhdGUgc2hhcGVzOiBhbnlbXSA9IFtdO1xuICBwcml2YXRlIHByZXZpb3VzU2hhcGVzOiBTaGFwZVNlbGVjdGlvbltdID0gW107XG4gIHByaXZhdGUgaXNSZWdpb25TYXZlZE9uID0gdHJ1ZTtcbiAgcHJpdmF0ZSBpbWFnZUxlbmd0aCE6IG51bWJlcjtcbiAgcHJpdmF0ZSBzY3JlZW5IZWlnaHQhOiBudW1iZXI7XG4gIHByaXZhdGUgcGxvdERpdiE6IHN0cmluZztcbiAgcHJpdmF0ZSBpc1JlYWxab29tID0gdHJ1ZTtcbiAgcHJpdmF0ZSBzY2FsZXJhdGlvID0gdHJ1ZTtcbiAgcHJpdmF0ZSB0cnVlSW1nU2l6ZSE6IG51bWJlcltdO1xuICBwcml2YXRlIHpvb21Db29yZGluYXRlczogbnVtYmVyW10gPSBbXTtcbiAgcHJpdmF0ZSBmaWxlTmFtZSE6IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICBwcml2YXRlIGRyYWdNb2RlITogc3RyaW5nO1xuICAvLyBSZWdpb24gZGF0YSAodGhlIGxpc3QgKyBwZXItaW1hZ2UgY2FjaGUpLCBzZWxlY3Rpb24sIGlkIG1pbnRpbmcsIHRoZSByZWdpb25cbiAgLy8gdXBkYXRlIGV2ZW50IGFuZCB0aGUgc2hhcGUgY29sb3VyL2xhYmVsIHByZWZzIG5vdyBsaXZlIGluIHRoZSBzaGFyZWRcbiAgLy8gUmVnaW9uU3RvcmUgKGluamVjdGVkIGFzIGB0aGlzLnJlZ2lvblN0b3JlYCkg4oCUIHRoZSBzaW5nbGUgc291cmNlIG9mIHRydXRoXG4gIC8vIGJvdGggYmFja2VuZHMgZGVsZWdhdGUgdG8uIGB0aGlzLnNoYXBlc2AgYmVsb3cgaXMga2VwdCBhcyBQbG90bHkncyAqcmVuZGVyXG4gIC8vIHByb2plY3Rpb24qIG9mIHRob3NlIHJlZ2lvbnMgKHRoZSBkaWN0IGFycmF5IFBsb3RseS5yZWxheW91dCBjb25zdW1lcykgYW5kXG4gIC8vIGlzIG1pcnJvcmVkIGludG8gdGhlIHN0b3JlIG9uIGV2ZXJ5IHdyaXRlLiBDb2xvcm1hcCwgcmV2ZXJzZS1zY2FsZSwgaW1hZ2VcbiAgLy8gbWV0YWRhdGEgYW5kIGNsYXNzaWZpY2F0aW9uIGNvbG91cnMgbGl2ZSBpbiB0aGUgc2hhcmVkIFZpc3VhbGl6ZXJTdG9yZVxuICAvLyAoaW5qZWN0ZWQgYXMgYHRoaXMuc3RvcmVgKS5cbiAgcHJpdmF0ZSB1cmxzITogc3RyaW5nW107XG4gIGltYWdlSW5mbyE6IElJbWFnZUluZm87XG4gIHByaXZhdGUgcGxvdFV0aWxpdGllcyA9IG5ldyBQbG90VXRpbGl0aWVzKCk7XG4gIHByaXZhdGUgcGxvdFR5cGUhOiBQbG90VHlwZTtcblxuICBwcml2YXRlIG9uUGxvdE1vdXNlRG93bjogKCgpID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG5cbiAgLy8gVGhlIHBlci1pbWFnZSByZWdpb24gY2FjaGUgKHJlZ2lvbnNCeUltYWdlS2V5L2N1cnJlbnRJbWFnZUtleSkgbm93IGxpdmVzIGluXG4gIC8vIHRoZSBzaGFyZWQgUmVnaW9uU3RvcmU7IHNldEFjdGl2ZUltYWdlKCkgZGVsZWdhdGVzIHRvIGl0IGFuZCByZS1wcm9qZWN0c1xuICAvLyBgdGhpcy5zaGFwZXNgIGZyb20gdGhlIHN0b3JlIGFmdGVyd2FyZHMuXG5cbiAgLyoqIFBpeGVsIGRhdGEgY2FjaGVkIGZvciB3YW5kIHNhbXBsaW5nLiBkYXRhW3pJbmRleF0gaXMgYSAyLUQgbWF0cml4LiAqL1xuICBwcml2YXRlIGNhY2hlZEltYWdlRnJhbWVzPzogYW55W107XG4gIHByaXZhdGUgY2FjaGVkSW1hZ2VXaWR0aCA9IDA7XG4gIHByaXZhdGUgY2FjaGVkSW1hZ2VIZWlnaHQgPSAwO1xuICBwcml2YXRlIGNhY2hlZEltYWdlUmF0aW9zOiBudW1iZXJbXSA9IFsxLCAxXTtcbiAgcHJpdmF0ZSBjYWNoZWRJc0dyYXlzY2FsZSA9IGZhbHNlO1xuICAvKiogRGF0YS1zcGFjZSBjb29yZGluYXRlIG9mIHRoZSBjYWNoZWQgZnJhbWUncyBwaXhlbCAoMCwwKS4gWzAsMF0gZm9yIHRoZSBmdWxsXG4gICAqICBpbWFnZTsgYSBjcm9wJ3MgdG9wLWxlZnQgd2hlbiB6b29tZWQgKHNvIHRoZSBpbnRlbnNpdHkgcHJvZmlsZSBzYW1wbGVzIHRoZVxuICAgKiAgem9vbS1sZXZlbCBwaXhlbHMgYXQgdGhlIHJpZ2h0IG9mZnNldC9yZXNvbHV0aW9uKS4gKi9cbiAgcHJpdmF0ZSBjYWNoZWRGcmFtZU9yaWdpbjogW251bWJlciwgbnVtYmVyXSA9IFswLCAwXTtcbiAgLyoqIExhc3QgdmlzaWJsZSBpbWFnZS1waXhlbCByZWN0YW5nbGUgZnJvbSB0aGUgYWN0aXZlIGJhY2tlbmQncyB2aWV3cG9ydCAoc2V0IGJ5XG4gICAqICB0aGUgT1NEIHZpZXdwb3J0LWNoYW5nZSBob29rIHZpYSByZWZyZXNoSW50ZW5zaXR5U2FtcGxpbmdGb3JSb2kpLiBOZXcgcHJvZmlsZSBsaW5lcyBhcmVcbiAgICogIHBsYWNlZCB3aXRoaW4gaXQgc28gdGhleSBsYW5kIG9uLXNjcmVlbiB3aGVuIHpvb21lZCBpbjsgbnVsbCDihpIgdXNlIGZ1bGwgaW1hZ2UuICovXG4gIHByaXZhdGUgbGFzdFZpc2libGVSb2k6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcblxuICAvKiogZXZlbnRzICovXG4gIHByaXZhdGUgb25SZWxheW91dEV2ZW50OiBhbnk7XG5cbiAgcHJpdmF0ZSBzdGFja0xvYWRpbmckID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XG4gIHByaXZhdGUgc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxudW1iZXI+KDApO1xuICAvLyBjdXJyZW50IGluZGV4IG9mIGltYWdlIGluIHN0YWNrIChpZiBzdGFjayksIDAgaWYgc2luZ2xlIGltYWdlXG4gIHByaXZhdGUgekluZGV4ID0gbmV3IEJlaGF2aW9yU3ViamVjdDxudW1iZXI+KDApO1xuICBwcml2YXRlIGF1dG9zY2FsZUV2ZW50ID0gbmV3IFN1YmplY3Q8YW55PigpO1xuICAvLyBUaGUgcmVnaW9uIHVwZGF0ZSBldmVudCBhbmQgc2VsZWN0aW9uIHN0cmVhbSBhcmUgb3duZWQgYnkgdGhlIHNoYXJlZFxuICAvLyBSZWdpb25TdG9yZTsgZ2V0UmVnaW9uVXBkYXRlRXZlbnQoKS9nZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyQoKSBkZWxlZ2F0ZSB0b1xuICAvLyBpdCBzbyBldmVyeSBjb25zdW1lciAoYW5kIHRoZSBPU0QgYmFja2VuZCkgc2VlcyBvbmUgc3RyZWFtLlxuICAvKiogQnJpZ2h0LCB3ZWxsLXNlcGFyYXRlZCBjb2xvdXJzIGN5Y2xlZCBhcyB0aGUgdXNlciBhZGRzIHByb2ZpbGUtbGluZSBST0lzLlxuICAgKiAgVGhlIG1hdGNoaW5nIGluc2V0IHRyYWNlIGlzIGRyYXduIGluIHRoZSBzYW1lIGNvbG91ci4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBQUk9GSUxFX1BBTEVUVEUgPSBbXG4gICAgJyNGRkQ0MDAnLCAvLyB5ZWxsb3dcbiAgICAnIzAwRTVGRicsIC8vIGN5YW5cbiAgICAnI0ZGMkQ5NScsIC8vIG1hZ2VudGFcbiAgICAnIzM5RkYxNCcsIC8vIGdyZWVuXG4gICAgJyNGRjk1MDAnLCAvLyBvcmFuZ2VcbiAgICAnIzdDNERGRicsIC8vIHZpb2xldFxuICAgICcjRkYzQjMwJywgLy8gcmVkXG4gICAgJyMxOEZGRkYnLCAvLyBhcXVhXG4gIF07XG4gIC8qKiBQYWxldHRlIGN1cnNvciBmb3IgcHJvZmlsZSBsaW5lcyAobmV2ZXIgcmVzZXQgb24gZGVsZXRlLCBzbyBjb2xvdXJzIGtlZXBcbiAgICogIGN5Y2xpbmcgYWNyb3NzIHRoZSBzZXNzaW9uKS4gKi9cbiAgcHJpdmF0ZSBwcm9maWxlQ29sb3JTZXEgPSAwO1xuICBwcml2YXRlIGludGVuc2l0eVByb2ZpbGUkID0gbmV3IFN1YmplY3Q8SW50ZW5zaXR5UHJvZmlsZVtdPigpO1xuICAvKiogSVNPU1VSRkFDRSBiYW5kIGFzIGEgMOKAkzI1NSBzbGlkZXIgcG9zaXRpb24sIG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUncyByZWFsXG4gICAqICBpbnRlbnNpdHkgcmFuZ2UgYXQgcmVuZGVyIHRpbWUgKHNlZSBtYXBJc29CYW5kKS4gRGVmYXVsdHMgdG8gdGhlIGZ1bGwgcmFuZ2VcbiAgICogIHNvIHRoZSBmaXJzdCByZW5kZXIgc2hvd3MgdGhlIHdob2xlIHN0cnVjdHVyZSByZWdhcmRsZXNzIG9mIGhvdyBicmlnaHQgb3JcbiAgICogIGRhcmsgdGhlIHZvbHVtZSBpczsgdGhlIHVzZXIgdGhlbiBuYXJyb3dzIHRoZSBiYW5kIHRvIGlzb2xhdGUgaW50ZW5zaXRpZXMuICovXG4gIHByaXZhdGUgaXNvTWluID0gMDtcbiAgcHJpdmF0ZSBpc29NYXggPSAyNTU7XG4gIC8qKiBBY3R1YWwgaW50ZW5zaXR5IHJhbmdlIG9mIHRoZSB2b2x1bWUgb24gc2NyZWVuIChbbWluLG1heF0gb3ZlciB0aGUgbG9hZGVkXG4gICAqICBmcmFtZXMpLiBUaGUgc2xpZGVyIHNwYW5zIDDigJMyNTUsIGJ1dCBhIGdpdmVuIHN0YWNrIG9mdGVuIG9jY3VwaWVzIG9ubHkgYVxuICAgKiAgc3ViLXJhbmdlLCBzbyB3ZSBjbGFtcCB0aGUgaXNvIGJhbmQgaW50byB0aGlzIHJhbmdlICh3aXRoIGEgc21hbGwgbWFyZ2luKVxuICAgKiAgdG8gZ3VhcmFudGVlIG5vbi1kZWdlbmVyYXRlIHN1cmZhY2VzIOKAlCBib3RoIG9uIGZpcnN0IHJlbmRlciBhbmQgb24gbGl2ZVxuICAgKiAgc2xpZGVyIG1vdmVzLiBOdWxsIHVudGlsIGFuIGlzb3N1cmZhY2Ugdm9sdW1lIGhhcyBiZWVuIG1lYXN1cmVkLiAqL1xuICBwcml2YXRlIGlzb0RhdGFSYW5nZTogW251bWJlciwgbnVtYmVyXSB8IG51bGwgPSBudWxsO1xuICAvLyBJZCBtaW50aW5nIGFuZCB0aGUgc2VsZWN0aW9uIGluZGV4IHN0cmVhbSBhcmUgb3duZWQgYnkgdGhlIHNoYXJlZFxuICAvLyBSZWdpb25TdG9yZS4gU2VsZWN0aW9uIHN0aWxsIGRyaXZlcyBQbG90bHkncyBgX2FjdGl2ZVNoYXBlSW5kZXhgIChzbyBhXG4gIC8vIHNpbmdsZSBzaGFwZSBnZXRzIHRoZSBlZGl0IGhhbmRsZXMpIOKAlCBzZWUgc2V0U2VsZWN0ZWRTaGFwZUluZGljZXMoKS5cbiAgcHJpdmF0ZSBpbWFnZUNhY2hlZCA9IGZhbHNlO1xuICBwcml2YXRlIGltYWdlQ2FjaGVkU3Vic2NyaXB0aW9uOiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgZmlsZW5hbWVTdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbjtcbiAgLyoqIERyaXZlcyB0aGUgaW50ZW5zaXR5IGluc2V0IG9mZiByZWdpb24gYWRkcy9kcmFncy9kZWxldGVzIChib3RoIGJhY2tlbmRzKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25VcGRhdGVTdWJzY3JpcHRpb24/OiBTdWJzY3JpcHRpb247XG4gIC8qKiBMaXZlIChwZXItZnJhbWUsIG5vbi1jb2FsZXNjZWQpIHJlZ2lvbiBlZGl0cyDigJQgc28gdGhlIGluc2V0IHRyYWNrcyBhbiBPU0RcbiAgICogIGxpbmUgUk9JIGxpdmUgZHVyaW5nIGEgZHJhZyAoT1NEIGJhdGNoZXMgcmVnaW9uVXBkYXRlJCB1bnRpbCByZWxlYXNlKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25MaXZlRWRpdFN1YnNjcmlwdGlvbj86IFN1YnNjcmlwdGlvbjtcbiAgcHJpdmF0ZSBjaGFubmVsU3ViPzogU3Vic2NyaXB0aW9uO1xuXG4gIHByaXZhdGUgd2FuZEhvc3QhOiBXYW5kVG9vbEhvc3Q7XG4gIHByaXZhdGUgZXJhc2VySG9zdCE6IFZlcnRleEVyYXNlclRvb2xIb3N0O1xuICAvKiogVGhpcyBiYWNrZW5kJ3MgcmVnaW9uIHJlbmRlcmVyIChsYXppbHkgY3JlYXRlZCBpbiBnZXRSZWdpb25PdmVybGF5KS4gKi9cbiAgcHJpdmF0ZSByZWdpb25PdmVybGF5PzogSVJlZ2lvbk92ZXJsYXk7XG4gIHByaXZhdGUgcmVhZG9ubHkgY29vcmRpbmF0ZVRyYW5zZm9ybTogSUNvb3JkaW5hdGVUcmFuc2Zvcm0gPVxuICAgIG5ldyBQbG90bHlDb29yZGluYXRlVHJhbnNmb3JtKFxuICAgICAgKCkgPT4gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2KSxcbiAgICAgICgpID0+IHRoaXMuZ2V0T3ZlcmxheUNvbnRhaW5lcigpKTtcblxuICBjb25zdHJ1Y3RvcihASW5qZWN0KFRJTEVfQUNDRVNTX1BPUlQpIHByaXZhdGUgdGlsZXM6IFRpbGVBY2Nlc3NQb3J0LFxuICAgICAgICAgICAgICBASW5qZWN0KElNQUdFX1NUQVRFX1BPUlQpIHByaXZhdGUgc3RhdGU6IEltYWdlU3RhdGVQb3J0LFxuICAgICAgICAgICAgICBwdWJsaWMgbWVzc2FnZVNlcnZpY2U6IE1lc3NhZ2VTZXJ2aWNlLCBwcml2YXRlIGh0dHA6IEh0dHBDbGllbnQsXG4gICAgICAgICAgICAgIHByaXZhdGUgd2FuZFNlcnZpY2U6IFdhbmRTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHdhbmRUb29sOiBXYW5kVG9vbFNlcnZpY2UsXG4gICAgICAgICAgICAgIHByaXZhdGUgYnJ1c2hUb29sOiBCcnVzaFRvb2xTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHNhbVRvb2w6IFNhbVRvb2xTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHNhbVBvaW50VG9vbDogU2FtUG9pbnRUb29sU2VydmljZSxcbiAgICAgICAgICAgICAgcHJpdmF0ZSBjZWxsU2VnbWVudFRvb2w6IENlbGxTZWdtZW50VG9vbFNlcnZpY2UsXG4gICAgICAgICAgICAgIEBPcHRpb25hbCgpIEBJbmplY3QoQ0VMTF9TRUdNRU5URVIpIHByaXZhdGUgY2VsbFNlZ21lbnRlcjogSUNlbGxTZWdtZW50ZXIgfCBudWxsLFxuICAgICAgICAgICAgICBwcml2YXRlIHZlcnRleEVyYXNlclRvb2w6IFZlcnRleEVyYXNlclRvb2xTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHpvb21Ub0JveFRvb2w6IFpvb21Ub0JveFRvb2xTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIHN0b3JlOiBWaXN1YWxpemVyU3RvcmUsXG4gICAgICAgICAgICAgIHByaXZhdGUgcmVnaW9uU3RvcmU6IFJlZ2lvblN0b3JlKSB7XG4gICAgLy8gcmVsYXlvdXQgZXZlbnQgcm91dGVyXG4gICAgdGhpcy5vblJlbGF5b3V0RXZlbnQgPSAoZXZlbnQ6IGFueSkgPT4geyB0aGlzLnJlbGF5b3V0RXZlbnRIYW5kbGVyKGV2ZW50KTsgfTtcblxuICAgIC8vIFRvb2wgc2VydmljZXMgcmVhZC9tdXRhdGUgb3VyIHN0YXRlIHZpYSBzbWFsbCAqSG9zdCBpbnRlcmZhY2VzLiBTdG9yZWRcbiAgICAvLyBzbyB3ZSBjYW4gcmUtYmluZCB0aGVtIHRvIG91cnNlbHZlcyB3aGVuZXZlciBhIFBsb3RseSB0b29sIGlzIGFjdGl2YXRlZFxuICAgIC8vICh0aGUgT3BlblNlYWRyYWdvbiBiYWNrZW5kIHJlLWJpbmRzIHRoZSBzYW1lIHNpbmdsZXRvbiB0b29scyB0byBpdHNlbGYpLlxuICAgIHRoaXMud2FuZEhvc3QgPSB7XG4gICAgICBnZXRSZWdpb25zOiAoKSA9PiB0aGlzLnJlZ2lvblN0b3JlLmdldFJlZ2lvbnMoKSxcbiAgICAgIHNldFJlZ2lvbnM6IChyZWdpb25zKSA9PiB0aGlzLnNldFJlZ2lvbnMocmVnaW9ucyksXG4gICAgICBnZXRDYWNoZWRJbWFnZURhdGE6ICgpID0+IHRoaXMuZ2V0Q2FjaGVkSW1hZ2VEYXRhKCksXG4gICAgICBnZXRBY3RpdmVGcmFtZUluZGV4OiAoKSA9PiB0aGlzLmFjdGl2ZUZyYW1lSW5kZXgoKSxcbiAgICAgIGdldE92ZXJsYXlDb250YWluZXI6ICgpID0+IHRoaXMuZ2V0T3ZlcmxheUNvbnRhaW5lcigpLFxuICAgICAgZ2V0Q29vcmRpbmF0ZVRyYW5zZm9ybTogKCkgPT4gdGhpcy5nZXRDb29yZGluYXRlVHJhbnNmb3JtKCksXG4gICAgICBnZXRGaWxlTmFtZTogKCkgPT4gdGhpcy5maWxlTmFtZSxcbiAgICAgIGdldFNoYXBlQ29sb3I6ICgpID0+IHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hhcGVDb2xvcigpLFxuICAgIH07XG4gICAgdGhpcy5lcmFzZXJIb3N0ID0ge1xuICAgICAgZ2V0UmVnaW9uczogKCkgPT4gdGhpcy5yZWdpb25TdG9yZS5nZXRSZWdpb25zKCksXG4gICAgICBzZXRSZWdpb25zOiAocmVnaW9ucykgPT4gdGhpcy5zZXRSZWdpb25zKHJlZ2lvbnMpLFxuICAgICAgaW52YWxpZGF0ZVdhbmRSZWdpb246ICgpID0+IHRoaXMud2FuZFRvb2wuY2xlYXJBY3RpdmVSZWdpb24oKSxcbiAgICAgIGdldE92ZXJsYXlDb250YWluZXI6ICgpID0+IHRoaXMuZ2V0T3ZlcmxheUNvbnRhaW5lcigpLFxuICAgICAgZ2V0Q29vcmRpbmF0ZVRyYW5zZm9ybTogKCkgPT4gdGhpcy5nZXRDb29yZGluYXRlVHJhbnNmb3JtKCksXG4gICAgICBnZXRDYWNoZWRJbWFnZVJhdGlvOiAoKSA9PiB0aGlzLmNhY2hlZEltYWdlUmF0aW9zWzBdIHx8IDEsXG4gICAgfTtcbiAgICB0aGlzLndhbmRUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIC8vIFRoZSBicnVzaCByZXVzZXMgdGhlIHdhbmQgaG9zdCAoc2FtZSBjb29yZGluYXRlIGZyYW1lICsgcmVnaW9uIGFjY2VzcykuXG4gICAgdGhpcy5icnVzaFRvb2wuYmluZEhvc3QodGhpcy53YW5kSG9zdCk7XG4gICAgdGhpcy52ZXJ0ZXhFcmFzZXJUb29sLmJpbmRIb3N0KHRoaXMuZXJhc2VySG9zdCk7XG4gICAgdGhpcy5iaW5kWm9vbVRvQm94SG9zdCgpO1xuXG4gICAgdGhpcy5pbWFnZUNhY2hlZFN1YnNjcmlwdGlvbiA9IHRoaXMuc3RhdGUuaXNJbWFnZUNhY2hlZCQoKS5zdWJzY3JpYmUoaW1hZ2VDYWNoZWQgPT4ge1xuICAgICAgdGhpcy5pbWFnZUNhY2hlZCA9IGltYWdlQ2FjaGVkO1xuICAgIH0pO1xuICAgIHRoaXMuZmlsZW5hbWVTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmdldEZpbGVuYW1lJCgpLnN1YnNjcmliZShmaWxlbmFtZSA9PiB7XG4gICAgICB0aGlzLmZpbGVOYW1lID0gZmlsZW5hbWU7XG4gICAgfSk7XG4gICAgLy8gTGl2ZSByZWNvbG9yIGZyb20gdGhlIENoYW5uZWxzICYgSGlzdG9ncmFtIHBhbmU6IHJlc3R5bGUgdGhlIGRpc3BsYXkgd2luZG93ICh6bWluL3ptYXggZm9yXG4gICAgLy8gYSBoZWF0bWFwLCBjbWluL2NtYXggZm9yIGlzb3N1cmZhY2Uvc3VyZmFjZSksIHRoZSBjb2xvcnNjYWxlIChjb2xvcm1hcCksIGFuZCByZXZlcnNlL2ludmVydFxuICAgIC8vIHdoZW4gdGhlIGNoYW5uZWwgc3RhdGUgT1IgdGhlIGNvbG9ybWFwIGNoYW5nZXMuIChHYW1tYSBpcyBhcHBsaWVkIGJ5IHRoZSBPU0QgaW1hZ2UgdmlldzsgdGhlXG4gICAgLy8gUGxvdGx5IGNvbG9yc2NhbGUgd2luZG93IGNvdmVycyB0aGUgY29tbW9uIGNvbnRyYXN0IGNhc2UuKVxuICAgIHRoaXMuY2hhbm5lbFN1YiA9IGNvbWJpbmVMYXRlc3QoW1xuICAgICAgdGhpcy5zdG9yZS5nZXRDaGFubmVsU3RhdGVzKCksXG4gICAgICB0aGlzLnN0b3JlLmdldFJldmVyc2VTY2FsZSgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRJbnZlcnQoKSxcbiAgICAgIHRoaXMuc3RvcmUuZ2V0Q29sb3JtYXAoKSxcbiAgICBdKS5zdWJzY3JpYmUoKFtjaGFubmVscywgcmV2LCBpbnZdKSA9PiB0aGlzLmFwcGx5Q2hhbm5lbERpc3BsYXkoY2hhbm5lbHMsIHJldiwgaW52KSk7XG4gICAgLy8gUHJvZmlsZSBsaW5lcyBhcmUgc3RvcmUgcmVnaW9uczsgYW55IHJlZ2lvbiBjaGFuZ2UgKGFkZC9kcmFnL2RlbGV0ZSwgb25cbiAgICAvLyBlaXRoZXIgYmFja2VuZCkgc2hvdWxkIHJlZnJlc2ggdGhlIGluc2V0IHRyYWNlcy4gVGhlIGxpdmUtZWRpdCBzdHJlYW1cbiAgICAvLyBmaXJlcyBwZXIgZnJhbWUgZHVyaW5nIGEgZHJhZyAoT1NEIGNvYWxlc2NlcyByZWdpb25VcGRhdGUkIHVudGlsIHJlbGVhc2UpLFxuICAgIC8vIHNvIHRoZSBpbnNldCB0cmFja3MgYW4gT1NEIGxpbmUgUk9JIGxpdmUgYXMgaXQncyBtb3ZlZC5cbiAgICB0aGlzLnJlZ2lvblVwZGF0ZVN1YnNjcmlwdGlvbiA9IHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9uVXBkYXRlRXZlbnQoKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgdGhpcy5lbWl0UHJvZmlsZXMoKTtcbiAgICB9KTtcbiAgICB0aGlzLnJlZ2lvbkxpdmVFZGl0U3Vic2NyaXB0aW9uID0gdGhpcy5yZWdpb25TdG9yZS5nZXRSZWdpb25MaXZlRWRpdCQoKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgdGhpcy5lbWl0UHJvZmlsZXMoKTtcbiAgICB9KTtcbiAgfVxuXG4gIGdldFRydWVJbWFnZVNpemUoKTogeyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHwgbnVsbCB7XG4gICAgIGlmICghdGhpcy50cnVlSW1nU2l6ZSkgcmV0dXJuIG51bGw7XG4gICAgIHJldHVybiB7XG4gICAgICAgd2lkdGg6IHRoaXMudHJ1ZUltZ1NpemVbMV0gLSB0aGlzLnRydWVJbWdTaXplWzBdLFxuICAgICAgIGhlaWdodDogdGhpcy50cnVlSW1nU2l6ZVszXSAtIHRoaXMudHJ1ZUltZ1NpemVbMl0sXG4gICAgIH07XG4gIH1cblxuICAvKipcbiAgICogTG9hZCBpbWFnZVxuICAgKiBAcGFyYW0gaW1hZ2VJbmZvXG4gICAqIEBwYXJhbSB6SW5kZXggaW5kZXggb2YgdGhlIGltYWdlIHRvIGxvYWRcbiAgICogQHJldHVybiBhbiBvYmplY3Qgd2l0aCBkYXRhIGFuZCByYXRpbyBrZXlzXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgbG9hZChpbWFnZUluZm86IElJbWFnZUluZm8sIHpJbmRleDogbnVtYmVyKSB7XG4gICAgY29uc3QgdXJscyA9IGltYWdlSW5mby51cmxzO1xuICAgIC8vIHVzZSB6SW5kZXggcHJvdmlkZWQgaWYgYW55LCAwIG90aGVyd2lzZVxuICAgIGxldCBpbWFnZVVybDtcbiAgICBpZiAoekluZGV4KSB7XG4gICAgICBpbWFnZVVybCA9IHVybHNbekluZGV4XTtcbiAgICB9IGVsc2Uge1xuICAgICAgaW1hZ2VVcmwgPSB1cmxzWzBdO1xuICAgIH1cbiAgICBjb25zdCBpc0dyYXlzY2FsZSA9IGltYWdlSW5mby5pc0dyYXlzY2FsZTtcbiAgICBjb25zdCBpbWFnZSA9IGF3YWl0IHRoaXMubG9hZEltYWdlKGltYWdlVXJsKTtcbiAgICBjb25zdCB4UmF0aW8gPSBpbWFnZUluZm8udHJ1ZUltYWdlU2l6ZVswXSAvIGltYWdlLndpZHRoO1xuICAgIGNvbnN0IHlSYXRpbyA9IGltYWdlSW5mby50cnVlSW1hZ2VTaXplWzFdIC8gaW1hZ2UuaGVpZ2h0O1xuXG4gICAgY29uc3QgdHJ1ZUltYWdlU2l6ZSA9IFtdO1xuICAgIC8vIFt4MCwgeDEsIHkwLCB5MV1cbiAgICB0cnVlSW1hZ2VTaXplWzBdID0gMDtcbiAgICB0cnVlSW1hZ2VTaXplWzFdID0gaW1hZ2VJbmZvLnRydWVJbWFnZVNpemVbMF07XG4gICAgdHJ1ZUltYWdlU2l6ZVsyXSA9IDA7XG4gICAgdHJ1ZUltYWdlU2l6ZVszXSA9IGltYWdlSW5mby50cnVlSW1hZ2VTaXplWzFdO1xuICAgIHRoaXMudHJ1ZUltZ1NpemUgPSB0cnVlSW1hZ2VTaXplO1xuICAgIHRoaXMuZmlsZU5hbWUgPSBpbWFnZUluZm8uZmlsZU5hbWU7XG4gICAgaWYgKGltYWdlSW5mby5pc1N0YWNrICYmIGltYWdlSW5mby5zaG93U3RhY2spIHtcbiAgICAgIGNvbnN0IGltYWdlczogYW55W10gPSBbXTtcbiAgICAgIHRoaXMuc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkLm5leHQoMCk7XG4gICAgICAvLyBPbmUgVVJMIHBlciBzbGljZSDigJQgbG9hZCB0aGVtIGFsbCAoZWFybGllciBgbGVuZ3RoLTFgIGRyb3BwZWQgdGhlIGxhc3QpLlxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB1cmxzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBpbWFnZSBzdGFjayAke2l9YCk7XG4gICAgICAgIC8vIGRvIG5vdCBrZWVwIHdpdGggbG9hZGluZyBpZiBmaWxlbmFtZSBpcyBkaWZmZXJlbnQgKGEgbmV3IGZpbGUgaGFzIGJlZW4gc2VsZWN0ZWQpXG4gICAgICAgIC8vIG9yIGlmIHRoZSBzdGFja0xvYWRpbmcgdmFsdWUgaXMgc2V0IHRvIGZhbHNlXG4gICAgICAgIGlmICh0aGlzLmZpbGVOYW1lID09PSBpbWFnZUluZm8uZmlsZU5hbWUgJiYgdGhpcy5zdGFja0xvYWRpbmckLnZhbHVlKSB7XG4gICAgICAgICAgY29uc3QgaW1hZ2UgPSBhd2FpdCB0aGlzLmxvYWRJbWFnZSh1cmxzW2ldKTtcbiAgICAgICAgICBpZiAoaXNHcmF5c2NhbGUpIHtcbiAgICAgICAgICAgIGNvbnN0IGdyZXkgPSBpbWFnZS5ncmV5KCk7XG4gICAgICAgICAgICBjb25zdCBpbWdEYXRhID0gdGhpcy5wbG90VXRpbGl0aWVzLmFycmF5VG9NYXRyaXgoQXJyYXkuZnJvbShncmV5LmRhdGEpLCBpbWFnZS53aWR0aCk7XG4gICAgICAgICAgICBpbWFnZXMucHVzaChpbWdEYXRhKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgcmdiRGF0YSA9IGltYWdlLmdldFBpeGVsc0FycmF5KCk7XG4gICAgICAgICAgICBjb25zdCByZ2JNYXRyaXggPSB0aGlzLnBsb3RVdGlsaXRpZXMuYXJyYXlUb01hdHJpeChyZ2JEYXRhLCBpbWFnZS53aWR0aCk7XG4gICAgICAgICAgICBpbWFnZXMucHVzaChyZ2JNYXRyaXgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KE1hdGgucm91bmQoKGkgKiAxMDApIC8gdXJscy5sZW5ndGgpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBicmVhayBzdGFjayBsb2FkaW5nXG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIHJlc2V0IHN0YWNrTG9hZGluZyBwcm9ncmVzcyB0byAwXG4gICAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KDApO1xuICAgICAgcmV0dXJuIHsgZGF0YTogaW1hZ2VzLCByYXRpb3M6IFt4UmF0aW8sIHlSYXRpb10sXG4gICAgICAgICAgICAgICBzaXplczogW2ltYWdlLndpZHRoLCBpbWFnZS5oZWlnaHRdLFxuICAgICAgICAgICAgICAgZmlsZW5hbWU6IGltYWdlSW5mby5maWxlTmFtZSB9O1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgaW1hZ2VEYXRhO1xuICAgICAgaWYgKGlzR3JheXNjYWxlKSB7XG4gICAgICAgIGNvbnN0IGdyZXkgPSBpbWFnZS5ncmV5KCk7XG4gICAgICAgIGltYWdlRGF0YSA9IHRoaXMucGxvdFV0aWxpdGllcy5hcnJheVRvTWF0cml4KEFycmF5LmZyb20oZ3JleS5kYXRhKSwgaW1hZ2Uud2lkdGgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgcmdiRGF0YSA9IGltYWdlLmdldFBpeGVsc0FycmF5KCk7XG4gICAgICAgIGltYWdlRGF0YSA9IHRoaXMucGxvdFV0aWxpdGllcy5hcnJheVRvTWF0cml4KHJnYkRhdGEsIGltYWdlLndpZHRoKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB7IGRhdGE6IFtpbWFnZURhdGFdLFxuICAgICAgICByYXRpb3M6IFt4UmF0aW8sIHlSYXRpb10sXG4gICAgICAgIHNpemVzOiBbaW1hZ2Uud2lkdGgsIGltYWdlLmhlaWdodF0sXG4gICAgICAgIGZpbGVuYW1lOiBpbWFnZUluZm8uZmlsZU5hbWUgfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICpcbiAgICogQHBhcmFtIHBsb3REaXZcbiAgICogQHBhcmFtIGltYWdlTG9hZGVkIG9iamVjdCB3aXRoIGRhdGEsIHJhdGlvcyBhbmQgc2l6ZXMga2V5XG4gICAqIEBwYXJhbSBpbWFnZUluZm8gSW1hZ2VJbmZvIG9iamVjdFxuICAgKiBAcGFyYW0gc2NyZWVuSGVpZ2h0IHNpemUgb2YgdGhlIGF2YWlsYWJsZSBzY3JlZW4gaGVpZ2h0XG4gICAqIEBwYXJhbSBwbG90VHlwZSB0eXBlIG9mIHBsb3R0aW5nXG4gICAqL1xuICAvKipcbiAgICogQHBhcmFtIGluUGxhY2Ugd2hlbiB0cnVlLCB1cGRhdGVzIHRoZSBleGlzdGluZyBwbG90IHZpYSBQbG90bHkucmVhY3RcbiAgICogICBpbnN0ZWFkIG9mIFBsb3RseS5wdXJnZSArIFBsb3RseS5uZXdQbG90LiBVc2VkIGJ5IHRoZSBtdWx0aS10aWVyXG4gICAqICAgZGlhZ3JhbSBzd2FwIChzbWFsbCDihpIgbGFyZ2UpIHNvIHRoZSBjYW52YXMgZG9lc24ndCBicmllZmx5IGJsYW5rXG4gICAqICAgYmV0d2VlbiBwaGFzZXMuXG4gICAqL1xuICBwdWJsaWMgcGxvdChwbG90RGl2OiBzdHJpbmcsIGltYWdlTG9hZGVkOiBhbnksIGltYWdlSW5mbzogSUltYWdlSW5mbywgc2NyZWVuSGVpZ2h0OiBudW1iZXIsXG4gICAgICAgICAgICAgIHBsb3RUeXBlOiBQbG90VHlwZSwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKSB7XG4gICAgY29uc3QgdHJ1ZUltYWdlU2l6ZTogbnVtYmVyW10gPSBbXTtcbiAgICB0aGlzLnpvb21Db29yZGluYXRlcyA9IFtdO1xuICAgIC8vIFt4MCwgeDEsIHkwLCB5MV1cbiAgICB0cnVlSW1hZ2VTaXplWzBdID0gMDtcbiAgICB0cnVlSW1hZ2VTaXplWzFdID0gaW1hZ2VJbmZvLnRydWVJbWFnZVNpemVbMF07XG4gICAgdHJ1ZUltYWdlU2l6ZVsyXSA9IDA7XG4gICAgdHJ1ZUltYWdlU2l6ZVszXSA9IGltYWdlSW5mby50cnVlSW1hZ2VTaXplWzFdO1xuICAgIC8vIFNhdmUgdGhlIGN1cnJlbnQgaW1hZ2UncyByZWdpb25zIGFuZCBwdWxsIGluIGFueSBjYWNoZWQgcmVnaW9ucyBmb3IgdGhlXG4gICAgLy8gaW1hZ2Ugd2UncmUgYWJvdXQgdG8gZGlzcGxheSwgYmVmb3JlIFBsb3RseS5uZXdQbG90IHJlYWRzIGB0aGlzLnNoYXBlc2AuXG4gICAgdGhpcy5zZXRBY3RpdmVJbWFnZShpbWFnZUluZm8pO1xuICAgIHRoaXMuaW1hZ2VJbmZvID0gaW1hZ2VJbmZvO1xuICAgIHRoaXMucGxvdFR5cGUgPSBwbG90VHlwZTtcbiAgICAvLyBDYWNoZSBwaXhlbCBtYXRyaWNlcyBzbyB0aGUgd2FuZCB0b29sIGNhbiBzYW1wbGUgdGhlbS5cbiAgICB0aGlzLmNhY2hlZEltYWdlRnJhbWVzID0gaW1hZ2VMb2FkZWQuZGF0YTtcbiAgICB0aGlzLmNhY2hlZEltYWdlV2lkdGggPSBpbWFnZUxvYWRlZC5zaXplc1swXTtcbiAgICB0aGlzLmNhY2hlZEltYWdlSGVpZ2h0ID0gaW1hZ2VMb2FkZWQuc2l6ZXNbMV07XG4gICAgdGhpcy5jYWNoZWRJbWFnZVJhdGlvcyA9IGltYWdlTG9hZGVkLnJhdGlvcztcbiAgICB0aGlzLmNhY2hlZEZyYW1lT3JpZ2luID0gWzAsIDBdOyAvLyBmdWxsIGltYWdlIOKAlCByZXNldCBhbnkgcHJpb3Igem9vbS1jcm9wIG9yaWdpblxuICAgIHRoaXMuY2FjaGVkSXNHcmF5c2NhbGUgPSAhIWltYWdlSW5mby5pc0dyYXlzY2FsZTtcblxuICAgIC8vIFBsdWdnYWJsZSBwbG90IHR5cGVzIChjb250b3VyLCBzY2F0dGVyLCBzY2F0dGVyM2QsIGlzb3N1cmZhY2UpIHJlbmRlclxuICAgIC8vIHRocm91Z2ggdGhlIHRyYWNlLWJ1aWxkZXIgcmVnaXN0cnkuIFRoZSBvcmlnaW5hbCBIRUFUTUFQL1NVUkZBQ0UvUkdCLWltYWdlXG4gICAgLy8gcmVuZGVyZXJzIGtlZXAgdGhlaXIgZGVkaWNhdGVkIHBhdGhzIGJlbG93IChhbHNvIHJldXNlZCBieSB0aGUgaGlnaC1kZWZcbiAgICAvLyB6b29tIHJlLWZldGNoKS4gSW50ZW5zaXR5IHByb2ZpbGVzIGFyZSBubyBsb25nZXIgYSBwbG90IHR5cGUg4oCUIHRoZXkncmVcbiAgICAvLyBSZWdpb24tYmFzZWQgbGluZSBST0lzLCBhdmFpbGFibGUgaW4gSEVBVE1BUC9JTUFHRSBtb2RlLlxuICAgIGNvbnN0IGltcGwgPSBQTE9UTFlfUExPVF9UWVBFX0lNUExTW3Bsb3RUeXBlXTtcbiAgICBpZiAoaW1wbCkge1xuICAgICAgdGhpcy5pbWFnZUluZm8uaXNHcmF5c2NhbGUgPSAhIWltYWdlSW5mby5pc0dyYXlzY2FsZTtcbiAgICAgIHJldHVybiB0aGlzLnBsb3RWaWFSZWdpc3RyeShcbiAgICAgICAgcGxvdERpdiwgaW1wbCwgdGhpcy5idWlsZFRyYWNlSW5wdXQoaW1hZ2VJbmZvLCBpbWFnZUxvYWRlZCwgdHJ1ZUltYWdlU2l6ZSksXG4gICAgICAgIHNjcmVlbkhlaWdodCwgaW5QbGFjZSk7XG4gICAgfVxuXG4gICAgaWYgKGltYWdlSW5mby5pc0dyYXlzY2FsZSkge1xuICAgICAgaWYgKHBsb3RUeXBlID09PSBQbG90VHlwZS5TVVJGQUNFKSB7XG4gICAgICAgIHJldHVybiB0aGlzLnBsb3RTdXJmYWNlKHBsb3REaXYsIGltYWdlSW5mby51cmxzLCBpbWFnZUxvYWRlZC5kYXRhLCB0cnVlSW1hZ2VTaXplLFxuICAgICAgICAgIGltYWdlTG9hZGVkLnJhdGlvcywgc2NyZWVuSGVpZ2h0LCBpblBsYWNlKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBsb3RIZWF0bWFwKHBsb3REaXYsIGltYWdlSW5mby51cmxzLCBpbWFnZUxvYWRlZC5kYXRhLCB0cnVlSW1hZ2VTaXplLFxuICAgICAgICBpbWFnZUxvYWRlZC5yYXRpb3MsIHNjcmVlbkhlaWdodCwgaW5QbGFjZSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnBsb3RSR0JIZWF0bWFwKHBsb3REaXYsIGltYWdlSW5mby51cmxzLCBpbWFnZUxvYWRlZC5kYXRhLCB0cnVlSW1hZ2VTaXplLFxuICAgICAgaW1hZ2VMb2FkZWQucmF0aW9zLCBpbWFnZUxvYWRlZC5zaXplc1swXSwgaW1hZ2VMb2FkZWQuc2l6ZXNbMV0sIHNjcmVlbkhlaWdodCwgaW5QbGFjZSk7XG4gIH1cblxuICAvKipcbiAgICogRnVuY3Rpb24gdXNlZCB0byBwbG90IGEgZ3JheXNjYWxlIGhlYXRtYXBcbiAgICogQHBhcmFtIHBsb3REaXZcbiAgICogQHBhcmFtIHVybHNcbiAgICogQHBhcmFtIGltYWdlc1xuICAgKiBAcGFyYW0gdHJ1ZUltZ1NpemUgYXJyYXkgY29udGFpbmluZyB0aGUgZm9sbG93aW5nIDogW3gwLCB4MSwgeTAsIHkxXVxuICAgKiBAcGFyYW0gcmF0aW9zXG4gICAqIEBwYXJhbSBzY3JlZW5IZWlnaHRcbiAgICogQHJldHVybiBQcm9taXNlIHRydWUgd2hlbiBwbG90dGluZyBpcyBmaW5pc2hlZFxuICAgKi9cbiAgcHJpdmF0ZSBwbG90SGVhdG1hcChwbG90RGl2OiBzdHJpbmcsIHVybHM6IHN0cmluZ1tdLCBpbWFnZXM6IGFueVtdLCB0cnVlSW1nU2l6ZTogbnVtYmVyW10sXG4gICAgICAgICAgICAgICAgICAgICByYXRpb3M6IG51bWJlcltdLCBzY3JlZW5IZWlnaHQ6IG51bWJlciwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgaWYgKCFpblBsYWNlKSBQbG90bHkucHVyZ2UocGxvdERpdik7XG4gICAgdGhpcy5wbG90RGl2ID0gcGxvdERpdjtcbiAgICB0aGlzLmltYWdlTGVuZ3RoID0gaW1hZ2VzLmxlbmd0aDtcbiAgICB0aGlzLnVybHMgPSB1cmxzO1xuICAgIHRoaXMuc2NyZWVuSGVpZ2h0ID0gc2NyZWVuSGVpZ2h0O1xuICAgIC8vIHBsb3RcbiAgICBjb25zdCB0cmFjZXM6IHsgejogYW55OyB0eXBlOiBzdHJpbmc7IG5hbWU6IHN0cmluZzsgdmlzaWJsZTogYm9vbGVhbjsgfVtdID0gW107XG4gICAgaW1hZ2VzLmZvckVhY2goKGRhdGFzZXQsIGluZGV4KSA9PiB7XG4gICAgICBjb25zdCB0cmFjZSA9IHtcbiAgICAgICAgeDA6IHRydWVJbWdTaXplWzBdLFxuICAgICAgICBkeDogcmF0aW9zWzBdLFxuICAgICAgICB5MDogdHJ1ZUltZ1NpemVbMl0sXG4gICAgICAgIGR5OiByYXRpb3NbMF0sXG4gICAgICAgIHo6IGRhdGFzZXQsXG4gICAgICAgIHR5cGU6ICdoZWF0bWFwJyxcbiAgICAgICAgLy8gV2UgcmVtb3ZlIHRoZSB0b29sdGlwIGFubm90YXRpb24gZm9yIHBlcmZvcm1hbmNlcyByZWFzb25zXG4gICAgICAgIC8vIHRleHQ6IGRhdGFzZXQubWFwKChyb3c6IGFueVtdLCBpOiBhbnkpID0+IHJvdy5tYXAoKGl0ZW0sIGopID0+IHtcbiAgICAgICAgLy8gICByZXR1cm4gYHg6ICR7KyhqICogcmF0aW9zWzBdKS50b0ZpeGVkKDIpfTxicj55OiAkeysoaSAqIHJhdGlvc1swXSkudG9GaXhlZCgyKX08YnI+dmFsdWU6ICR7aXRlbX1gfSkpLFxuICAgICAgICBob3ZlcmluZm86ICdub25lJyxcbiAgICAgICAgY29sb3JzY2FsZTogdGhpcy5zdG9yZS5jdXJyZW50Q29sb3JtYXAoKS5kYXRhLnZhbHVlLFxuICAgICAgICByZXZlcnNlc2NhbGU6IHRoaXMuc3RvcmUuY3VycmVudFJldmVyc2VTY2FsZSgpLFxuICAgICAgICBuYW1lOiBgU2xpY2UgJHtpbmRleCArIDF9YCxcbiAgICAgICAgdmlzaWJsZTogaW5kZXggPT09IDBcbiAgICAgIH07XG4gICAgICB0cmFjZXMucHVzaCh0cmFjZSk7XG4gICAgfSk7XG4gICAgY29uc3QgcmVuZGVyID0gaW5QbGFjZSA/IFBsb3RseS5yZWFjdCA6IFBsb3RseS5uZXdQbG90O1xuICAgIHJldHVybiAocmVuZGVyIGFzIGFueSkocGxvdERpdiwgdHJhY2VzIGFzIGFueSxcbiAgICAgIHRoaXMuZ2V0SGVhdG1hcExheW91dChbdHJ1ZUltZ1NpemVbMF0sIHRydWVJbWdTaXplWzFdXSwgW3RydWVJbWdTaXplWzNdLFxuICAgICAgICB0cnVlSW1nU2l6ZVsyXV0pLCBDT05GSUcgYXMgYW55KS50aGVuKCgpID0+IHtcbiAgICAgIC8vIGhhbmRsZSB0aGUgcmVsYXlvdXQgZXZlbnQgZm9yIHpvb20gLyByb2lzIGFuZCB0aGUgY2xpY2sgZXZlbnRcbiAgICAgIHRoaXMuc2V0RXZlbnRzKHBsb3REaXYsIHRydWUsIHNjcmVlbkhlaWdodCk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGdW5jdGlvbiB1c2VkIHRvIHBsb3QgYSBncmF5c2NhbGUgc3VyZmFjZVxuICAgKiBAcGFyYW0gcGxvdERpdlxuICAgKiBAcGFyYW0gdXJsc1xuICAgKiBAcGFyYW0gaW1hZ2VzXG4gICAqIEBwYXJhbSB0cnVlSW1nU2l6ZSBhcnJheSBjb250YWluaW5nIHRoZSBmb2xsb3dpbmcgOiBbeDAsIHgxLCB5MCwgeTFdXG4gICAqIEBwYXJhbSByYXRpb3NcbiAgICogQHBhcmFtIHNjcmVlbkhlaWdodFxuICAgKiBAcmV0dXJuIFByb21pc2UgdHJ1ZSB3aGVuIHBsb3R0aW5nIGlzIGZpbmlzaGVkXG4gICAqL1xuICBwcml2YXRlIHBsb3RTdXJmYWNlKHBsb3REaXY6IHN0cmluZywgdXJsczogc3RyaW5nW10sIGltYWdlczogYW55W10sIHRydWVJbWdTaXplOiBudW1iZXJbXSxcbiAgICAgICAgICAgICAgICAgICAgICByYXRpb3M6IG51bWJlcltdLCBzY3JlZW5IZWlnaHQ6IG51bWJlciwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgaWYgKCFpblBsYWNlKSBQbG90bHkucHVyZ2UocGxvdERpdik7XG4gICAgdGhpcy5wbG90RGl2ID0gcGxvdERpdjtcbiAgICB0aGlzLmltYWdlTGVuZ3RoID0gaW1hZ2VzLmxlbmd0aDtcbiAgICB0aGlzLnVybHMgPSB1cmxzO1xuICAgIHRoaXMuc2NyZWVuSGVpZ2h0ID0gc2NyZWVuSGVpZ2h0O1xuICAgIC8vIHBsb3RcbiAgICBjb25zdCB0cmFjZXM6IHsgejogYW55OyB0eXBlOiBzdHJpbmc7ICB9W10gPSBbXTtcbiAgICBpbWFnZXMuZm9yRWFjaCgoZGF0YXNldCwgaW5kZXgpID0+IHtcbiAgICAgIGNvbnN0IHRyYWNlID0ge1xuICAgICAgICB6OiBkYXRhc2V0LFxuICAgICAgICB0eXBlOiAnc3VyZmFjZScsXG4gICAgICAgIC8vIGhvdmVyaW5mbzogJ25vbmUnLFxuICAgICAgICBjb2xvcnNjYWxlOiB0aGlzLnN0b3JlLmN1cnJlbnRDb2xvcm1hcCgpLmRhdGEudmFsdWUsXG4gICAgICAgIHJldmVyc2VzY2FsZTogdGhpcy5zdG9yZS5jdXJyZW50UmV2ZXJzZVNjYWxlKCksXG4gICAgICAgIC8vIG5hbWU6IGBTbGljZSAke2luZGV4ICsgMX1gLFxuICAgICAgICAvLyB2aXNpYmxlOiBpbmRleCA9PT0gMFxuICAgICAgfTtcbiAgICAgIHRyYWNlcy5wdXNoKHRyYWNlKTtcbiAgICB9KTtcbiAgICBjb25zdCB4cmFuZ2UgPSBbdHJ1ZUltZ1NpemVbMF0sIHRydWVJbWdTaXplWzFdXTtcbiAgICBjb25zdCB5cmFuZ2UgPSBbdHJ1ZUltZ1NpemVbMl0sIHRydWVJbWdTaXplWzNdXTtcbiAgICBjb25zdCByZW5kZXIgPSBpblBsYWNlID8gUGxvdGx5LnJlYWN0IDogUGxvdGx5Lm5ld1Bsb3Q7XG4gICAgcmV0dXJuIChyZW5kZXIgYXMgYW55KShwbG90RGl2LCB0cmFjZXMgYXMgYW55LFxuICAgICAgdGhpcy5nZXRTdXJmYWNlTGF5b3V0KHhyYW5nZSwgeXJhbmdlLCAwLjQpLCBDT05GSUdfU1VSRkFDRSBhcyBhbnkpLnRoZW4oKCkgPT4ge1xuICAgICAgLy8gaGFuZGxlIHRoZSByZWxheW91dCBldmVudCBmb3Igem9vbSAvIHJvaXMgYW5kIHRoZSBjbGljayBldmVudFxuICAgICAgdGhpcy5zZXRFdmVudHMocGxvdERpdiwgdHJ1ZSwgc2NyZWVuSGVpZ2h0KTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEZ1bmN0aW9uIHVzZWQgdG8gcGxvdCBhbiBSR0IgaGVhdG1hcFxuICAgKiBAcGFyYW0gcGxvdERpdlxuICAgKiBAcGFyYW0gdXJsc1xuICAgKiBAcGFyYW0gaW1hZ2VzXG4gICAqIEBwYXJhbSB0cnVlSW1nU2l6ZVxuICAgKiBAcGFyYW0gd2lkdGhcbiAgICogQHBhcmFtIGhlaWdodFxuICAgKiBAcGFyYW0gcmF0aW9zXG4gICAqIEBwYXJhbSBzY3JlZW5IZWlnaHRcbiAgICogQHJldHVybiBQcm9taXNlXG4gICAqL1xuICBwcml2YXRlIHBsb3RSR0JIZWF0bWFwKHBsb3REaXY6IHN0cmluZywgdXJsczogc3RyaW5nW10sIGltYWdlczogYW55W10sIHRydWVJbWdTaXplOiBudW1iZXJbXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICByYXRpb3M6IG51bWJlcltdLCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcixcbiAgICAgICAgICAgICAgICAgICAgICAgICBzY3JlZW5IZWlnaHQ6IG51bWJlciwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgLy8gUGxvdGx5LnJlYWN0IHVwZGF0ZXMgaW4gcGxhY2U7IFBsb3RseS5wdXJnZSArIFBsb3RseS5uZXdQbG90IGJsYW5rc1xuICAgIC8vIHRoZSBjYW52YXMgZm9yIH4xMDBtcyB3aGljaCBtYWtlcyB0aGUgbXVsdGktdGllciBzbWFsbOKGkmxhcmdlIHN3YXBcbiAgICAvLyBsb29rIGxpa2UgYSByZWdyZXNzaW9uIHRvIFwibG9hZGluZ1wiIGJlZm9yZSB0aGUgc2hhcnBlciB2ZXJzaW9uXG4gICAgLy8gYXBwZWFycy5cbiAgICBpZiAoIWluUGxhY2UpIFBsb3RseS5wdXJnZShwbG90RGl2KTtcbiAgICB0aGlzLnBsb3REaXYgPSBwbG90RGl2O1xuICAgIHRoaXMuaW1hZ2VMZW5ndGggPSBpbWFnZXMubGVuZ3RoO1xuICAgIHRoaXMudXJscyA9IHVybHM7XG4gICAgdGhpcy5zY3JlZW5IZWlnaHQgPSBzY3JlZW5IZWlnaHQ7XG5cbiAgICAvLyBhcyBhdXRvcmFuZ2UgaXMgc2V0IHRvIGZhbHNlLCBhbmQgbm90IHJldmVyc2VkIHdlIG5lZWQgdG8gc2V0IHRoZSB5cmFuZ2UgY29ycmVjbHkgaGVyZVxuICAgIGNvbnN0IGxheW91dCA9IHRoaXMuZ2V0SGVhdG1hcExheW91dChbdHJ1ZUltZ1NpemVbMF0sIHRydWVJbWdTaXplWzFdXSxcbiAgICAgIFt0cnVlSW1nU2l6ZVszXSwgdHJ1ZUltZ1NpemVbMl1dKTtcbiAgICBjb25zdCB0cmFjZXM6IHsgejogYW55OyB0eXBlOiBzdHJpbmc7IG5hbWU6IHN0cmluZzsgdmlzaWJsZTogYm9vbGVhbjsgfVtdID0gW107XG4gICAgaW1hZ2VzLmZvckVhY2goKGRhdGFzZXQsIGluZGV4KSA9PiB7XG4gICAgICBjb25zdCB0cmFjZSA9IHtcbiAgICAgICAgeDA6IHRydWVJbWdTaXplWzBdLFxuICAgICAgICBkeDogcmF0aW9zWzBdLFxuICAgICAgICB5MDogdHJ1ZUltZ1NpemVbMl0sXG4gICAgICAgIGR5OiByYXRpb3NbMF0sXG4gICAgICAgIHg6IEFycmF5LmZyb20oQXJyYXkod2lkdGgpLmtleXMoKSksXG4gICAgICAgIHk6IEFycmF5LmZyb20oQXJyYXkoaGVpZ2h0KS5rZXlzKCkpLFxuICAgICAgICB6OiBkYXRhc2V0LFxuICAgICAgICAvLyB0ZXh0OiBkYXRhc2V0Lm1hcCgocm93OiBhbnlbXSwgaTogYW55KSA9PiByb3cubWFwKChpdGVtLCBqKSA9PiB7XG4gICAgICAgIC8vICAgcmV0dXJuIGB4OiAkeysoaiAqIHJhdGlvc1swXSkudG9GaXhlZCgyKX08YnI+eTogJHsrKGkgKiByYXRpb3NbMF0pLnRvRml4ZWQoMil9PGJyPnZhbHVlOiAke2l0ZW19YH0pKSxcbiAgICAgICAgaG92ZXJpbmZvOiAnbm9uZScsXG4gICAgICAgIHR5cGU6ICdpbWFnZScsXG4gICAgICAgIG5hbWU6IGBTbGljZSAke2luZGV4ICsgMX1gLFxuICAgICAgICB2aXNpYmxlOiBpbmRleCA9PT0gMFxuICAgICAgfTtcbiAgICAgIHRyYWNlcy5wdXNoKHRyYWNlKTtcbiAgICB9KTtcbiAgICBjb25zdCByZW5kZXIgPSBpblBsYWNlID8gUGxvdGx5LnJlYWN0IDogUGxvdGx5Lm5ld1Bsb3Q7XG4gICAgcmV0dXJuIChyZW5kZXIgYXMgYW55KShwbG90RGl2LCB0cmFjZXMgYXMgYW55LCBsYXlvdXQsIENPTkZJRyBhcyBhbnkpLnRoZW4oKCkgPT4ge1xuICAgICAgLy8gaGFuZGxlIHRoZSByZWxheW91dCBldmVudFxuICAgICAgdGhpcy5zZXRFdmVudHMocGxvdERpdiwgZmFsc2UsIHNjcmVlbkhlaWdodCk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBc3NlbWJsZSB0aGUgbm9ybWFsaXNlZCBpbnB1dCB0aGUgcGx1Z2dhYmxlIHRyYWNlIGJ1aWxkZXJzIGNvbnN1bWUuXG4gICAqIFB1cmUgZGF0YSBvbmx5IOKAlCBubyBQbG90bHkgaGFuZGxlcyDigJQgc28gdGhlIGJ1aWxkZXJzIHN0YXkgYmFja2VuZC1uZXV0cmFsLlxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFRyYWNlSW5wdXQoaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvLCBpbWFnZUxvYWRlZDogYW55LFxuICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlSW1hZ2VTaXplOiBudW1iZXJbXSk6IFRyYWNlQnVpbGRJbnB1dCB7XG4gICAgLy8gTWVhc3VyZSB0aGUgdm9sdW1lJ3MgcmVhbCBpbnRlbnNpdHkgcmFuZ2Ugc28gdGhlIGlzbyBiYW5kIGNhbiBiZSBjbGFtcGVkXG4gICAgLy8gaW50byBpdCAodGhlIHNsaWRlciBpcyBhIGZpeGVkIDDigJMyNTUgYnV0IGEgc3RhY2sgbWF5IG9jY3VweSBvbmx5IHBhcnQgb2ZcbiAgICAvLyB0aGF0LCB3aGljaCB3b3VsZCBvdGhlcndpc2UgbGVhdmUgdGhlIHN1cmZhY2VzIHdpdGggbm90aGluZyB0byBjcm9zcykuXG4gICAgaWYgKHRoaXMucGxvdFR5cGUgPT09IFBsb3RUeXBlLklTT1NVUkZBQ0UpIHtcbiAgICAgIHRoaXMuaXNvRGF0YVJhbmdlID0gdGhpcy5tZWFzdXJlSW50ZW5zaXR5UmFuZ2UoaW1hZ2VMb2FkZWQuZGF0YSwgISFpbWFnZUluZm8uaXNHcmF5c2NhbGUpO1xuICAgIH1cbiAgICBjb25zdCBbaXNvTWluLCBpc29NYXhdID0gdGhpcy5tYXBJc29CYW5kKHRoaXMuaXNvTWluLCB0aGlzLmlzb01heCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGZyYW1lczogaW1hZ2VMb2FkZWQuZGF0YSxcbiAgICAgIHdpZHRoOiBpbWFnZUxvYWRlZC5zaXplc1swXSxcbiAgICAgIGhlaWdodDogaW1hZ2VMb2FkZWQuc2l6ZXNbMV0sXG4gICAgICByYXRpb3M6IGltYWdlTG9hZGVkLnJhdGlvcyxcbiAgICAgIHRydWVJbWFnZVNpemUsXG4gICAgICBpc0dyYXlzY2FsZTogISFpbWFnZUluZm8uaXNHcmF5c2NhbGUsXG4gICAgICBjb2xvcnNjYWxlOiB0aGlzLnN0b3JlLmN1cnJlbnRDb2xvcm1hcCgpLmRhdGEudmFsdWUsXG4gICAgICByZXZlcnNlc2NhbGU6IHRoaXMuc3RvcmUuY3VycmVudFJldmVyc2VTY2FsZSgpLFxuICAgICAgcmVnaW9uczogdGhpcy5nZXRSZWdpb25Qb2x5Z29ucygpLFxuICAgICAgc2hhcGVDb2xvcjogdGhpcy5yZWdpb25TdG9yZS5nZXRTaGFwZUNvbG9yKCksXG4gICAgICBpc29NaW4sXG4gICAgICBpc29NYXgsXG4gICAgfTtcbiAgfVxuXG4gIC8qKiBNaW4vbWF4IGludGVuc2l0eSBvdmVyIGV2ZXJ5IHZveGVsIG9mIHRoZSBsb2FkZWQgZnJhbWVzIChSR0Ig4oaSIGx1bWluYW5jZSksXG4gICAqICB1c2VkIHRvIGtlZXAgdGhlIGlzbyBiYW5kIGluc2lkZSB0aGUgZGF0YS4gUmV0dXJucyBudWxsIGZvciBlbXB0eSBpbnB1dC4gKi9cbiAgcHJpdmF0ZSBtZWFzdXJlSW50ZW5zaXR5UmFuZ2UoZnJhbWVzOiBhbnlbXSwgaXNHcmF5c2NhbGU6IGJvb2xlYW4pOiBbbnVtYmVyLCBudW1iZXJdIHwgbnVsbCB7XG4gICAgbGV0IG1pbiA9IEluZmluaXR5LCBtYXggPSAtSW5maW5pdHk7XG4gICAgZm9yIChjb25zdCBmcmFtZSBvZiBmcmFtZXMgfHwgW10pIHtcbiAgICAgIGZvciAoY29uc3Qgcm93IG9mIGZyYW1lIHx8IFtdKSB7XG4gICAgICAgIGZvciAoY29uc3QgY2VsbCBvZiByb3cgfHwgW10pIHtcbiAgICAgICAgICBjb25zdCB2ID0gaXNHcmF5c2NhbGUgPyBjZWxsXG4gICAgICAgICAgICA6IDAuMjk5ICogY2VsbFswXSArIDAuNTg3ICogY2VsbFsxXSArIDAuMTE0ICogY2VsbFsyXTtcbiAgICAgICAgICBpZiAoIU51bWJlci5pc0Zpbml0ZSh2KSkgY29udGludWU7XG4gICAgICAgICAgaWYgKHYgPCBtaW4pIG1pbiA9IHY7XG4gICAgICAgICAgaWYgKHYgPiBtYXgpIG1heCA9IHY7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIChOdW1iZXIuaXNGaW5pdGUobWluKSAmJiBOdW1iZXIuaXNGaW5pdGUobWF4KSAmJiBtYXggPiBtaW4pID8gW21pbiwgbWF4XSA6IG51bGw7XG4gIH1cblxuICAvKiogTWFwIHRoZSAw4oCTMjU1IHNsaWRlciBiYW5kIG9udG8gdGhlIHZvbHVtZSdzIGFjdHVhbCBpbnRlbnNpdHkgcmFuZ2UuIFRoZVxuICAgKiAgc2xpZGVyIGlzIGZpeGVkIDDigJMyNTUsIGJ1dCBhIHN0YWNrIG9mdGVuIG9jY3VwaWVzIG9ubHkgYSBuYXJyb3cgc3ViLXJhbmdlXG4gICAqICAoZS5nLiB0aGlzIEVET0Ygdm9sdW1lIGlzIH4y4oCTNTAgd2l0aCBzdHJ1Y3R1cmUgbmVhciA24oCTMTApLiBNYXBwaW5nIHNwcmVhZHNcbiAgICogIHRoZSBkYXRhIGFjcm9zcyB0aGUgd2hvbGUgc2xpZGVyIHNvIGV2ZXJ5IGludGVuc2l0eSBpcyBlYXN5IHRvIHJlYWNoIGFuZFxuICAgKiAgdGhlIGJhbmQgYWx3YXlzIGxhbmRzIGluc2lkZSB0aGUgZGF0YSDigJQgYSBzbWFsbCBpbnNldCBrZWVwcyB0aGUgZXh0cmVtZVxuICAgKiAgc3VyZmFjZXMgb2ZmIHRoZSBleGFjdCBkYXRhIGVkZ2VzLCB3aGljaCBhcmUgZGVnZW5lcmF0ZSBhbmQgZHJhdyBub3RoaW5nLiAqL1xuICBwcml2YXRlIG1hcElzb0JhbmQoaXNvTWluOiBudW1iZXIsIGlzb01heDogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gICAgbGV0IGxvID0gTWF0aC5taW4oaXNvTWluLCBpc29NYXgpO1xuICAgIGxldCBoaSA9IE1hdGgubWF4KGlzb01pbiwgaXNvTWF4KTtcbiAgICBpZiAodGhpcy5pc29EYXRhUmFuZ2UpIHtcbiAgICAgIGNvbnN0IFt2TWluLCB2TWF4XSA9IHRoaXMuaXNvRGF0YVJhbmdlO1xuICAgICAgY29uc3Qgc3BhbiA9IHZNYXggLSB2TWluO1xuICAgICAgY29uc3QgcGFkID0gMC4wMyAqIHNwYW47XG4gICAgICBjb25zdCB1c2FibGUgPSBzcGFuIC0gMiAqIHBhZDtcbiAgICAgIGNvbnN0IFNMSURFUl9NQVggPSAyNTU7IC8vIHRvb2xiYXIgc2xpZGVyIGRvbWFpbiAoaXNvVmFsdWVNYXgpXG4gICAgICBsbyA9IHZNaW4gKyBwYWQgKyAobG8gLyBTTElERVJfTUFYKSAqIHVzYWJsZTtcbiAgICAgIGhpID0gdk1pbiArIHBhZCArIChoaSAvIFNMSURFUl9NQVgpICogdXNhYmxlO1xuICAgICAgaWYgKGxvID4gaGkpIHsgY29uc3QgdCA9IGxvOyBsbyA9IGhpOyBoaSA9IHQ7IH1cbiAgICB9XG4gICAgcmV0dXJuIFtsbywgaGldO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSB0aGUgaXNvc3VyZmFjZSBpbnRlbnNpdHkgYm91bmRzLiBXaGVuIGlzb3N1cmZhY2UgaXMgb24gc2NyZWVuIHRoaXNcbiAgICogcmVzdHlsZXMgYGlzb21pbmAvYGlzb21heGAgaW4gcGxhY2UgZm9yIGEgbGl2ZSB1cGRhdGUgKG5vIHZvbHVtZSByZWJ1aWxkKS5cbiAgICogU2xpZGVyIHZhbHVlcyBhcmUgbWFwcGVkIG9udG8gdGhlIHZvbHVtZSdzIHJlYWwgcmFuZ2Ugc28gdGhlIHN1cmZhY2VzIGFsd2F5c1xuICAgKiBoYXZlIGRhdGEgdG8gY3Jvc3MgKG1hdGNoaW5nIHRoZSBpbml0aWFsIHJlbmRlcikuXG4gICAqL1xuICBwdWJsaWMgc2V0SXNvUmFuZ2UoaXNvTWluOiBudW1iZXIsIGlzb01heDogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5pc29NaW4gPSBpc29NaW47XG4gICAgdGhpcy5pc29NYXggPSBpc29NYXg7XG4gICAgaWYgKHRoaXMucGxvdFR5cGUgPT09IFBsb3RUeXBlLklTT1NVUkZBQ0UgJiYgdGhpcy5wbG90RGl2KSB7XG4gICAgICBjb25zdCBbbG8sIGhpXSA9IHRoaXMubWFwSXNvQmFuZChpc29NaW4sIGlzb01heCk7XG4gICAgICB0cnkge1xuICAgICAgICBQbG90bHkucmVzdHlsZSh0aGlzLnBsb3REaXYsIHsgaXNvbWluOiBbbG9dLCBpc29tYXg6IFtoaV0gfSBhcyBhbnkpO1xuICAgICAgfSBjYXRjaCB7IC8qIHBsb3Qgbm90IHJlYWR5ICovIH1cbiAgICB9XG4gIH1cblxuICAvKiogUGxvdGx5IHJlbmRlcnMgaXNvc3VyZmFjZXMsIHNvIGl0IGV4cG9zZXMgdGhlIGlzb3N1cmZhY2UgY29udHJvbHMgKGl0c2VsZikuICovXG4gIHB1YmxpYyBnZXRJc29zdXJmYWNlQ29udHJvbHMoKTogSUlzb3N1cmZhY2VDb250cm9scyB8IG51bGwgeyByZXR1cm4gdGhpczsgfVxuXG4gIC8qKlxuICAgKiBSZW5kZXIgYSByZWdpc3RyeS1iYWNrZWQgcGxvdCB0eXBlOiBidWlsZCBpdHMgdHJhY2VzIGZyb20gdGhlIGlucHV0LCBwaWNrXG4gICAqIHRoZSBtYXRjaGluZyBsYXlvdXQsIHJlbmRlciwgYW5kIHdpcmUgdGhlIHVzdWFsIHJlbGF5b3V0L2NsaWNrIGV2ZW50cy5cbiAgICogTWlycm9ycyB0aGUgc3RydWN0dXJlIG9mIHRoZSBkZWRpY2F0ZWQgaGVhdG1hcC9zdXJmYWNlIHJlbmRlcmVycy5cbiAgICovXG4gIHByaXZhdGUgcGxvdFZpYVJlZ2lzdHJ5KHBsb3REaXY6IHN0cmluZywgaW1wbDogUGxvdGx5UGxvdFR5cGVJbXBsLCBpbnB1dDogVHJhY2VCdWlsZElucHV0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JlZW5IZWlnaHQ6IG51bWJlciwgaW5QbGFjZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgaWYgKCFpblBsYWNlKSBQbG90bHkucHVyZ2UocGxvdERpdik7XG4gICAgdGhpcy5wbG90RGl2ID0gcGxvdERpdjtcbiAgICB0aGlzLmltYWdlTGVuZ3RoID0gaW5wdXQuZnJhbWVzLmxlbmd0aDtcbiAgICB0aGlzLnVybHMgPSB0aGlzLmltYWdlSW5mbz8udXJscyA/PyB0aGlzLnVybHM7XG4gICAgdGhpcy5zY3JlZW5IZWlnaHQgPSBzY3JlZW5IZWlnaHQ7XG5cbiAgICBjb25zdCB0cmFjZXMgPSBpbXBsLmJ1aWxkVHJhY2VzKGlucHV0KTtcbiAgICBjb25zdCBbeDAsIHgxLCB5MCwgeTFdID0gaW5wdXQudHJ1ZUltYWdlU2l6ZTtcbiAgICBsZXQgbGF5b3V0OiBhbnk7XG4gICAgc3dpdGNoIChpbXBsLmxheW91dEtpbmQpIHtcbiAgICAgIGNhc2UgJzNkLXZvbHVtZSc6XG4gICAgICAgIGxheW91dCA9IHRoaXMuZ2V0Vm9sdW1lTGF5b3V0KCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnMmQtY2hhcnQnOlxuICAgICAgICBsYXlvdXQgPSB0aGlzLmdldENoYXJ0TGF5b3V0KCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnMmQtb3ZlcmxheSc6XG4gICAgICAgIGxheW91dCA9IHRoaXMuZ2V0T3ZlcmxheUxheW91dChbeDAsIHgxXSwgW3kxLCB5MF0pOyAvLyByZXZlcnNlZCB5IGxpa2UgdGhlIGltYWdlXG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDogLy8gJzJkLWltYWdlJ1xuICAgICAgICBsYXlvdXQgPSB0aGlzLmdldEhlYXRtYXBMYXlvdXQoW3gwLCB4MV0sIFt5MSwgeTBdKTtcbiAgICB9XG4gICAgY29uc3QgY29uZmlnID0gaW1wbC50aHJlZUQgPyBDT05GSUdfU1VSRkFDRSA6IENPTkZJRztcbiAgICBjb25zdCByZW5kZXIgPSBpblBsYWNlID8gUGxvdGx5LnJlYWN0IDogUGxvdGx5Lm5ld1Bsb3Q7XG4gICAgcmV0dXJuIChyZW5kZXIgYXMgYW55KShwbG90RGl2LCB0cmFjZXMgYXMgYW55LCBsYXlvdXQsIGNvbmZpZyBhcyBhbnkpLnRoZW4oKCkgPT4ge1xuICAgICAgdGhpcy5zZXRFdmVudHMocGxvdERpdiwgaW5wdXQuaXNHcmF5c2NhbGUsIHNjcmVlbkhlaWdodCk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBQbGFpbiAyRCBjaGFydCBheGVzIChpbnRlbnNpdHkgcHJvZmlsZSAvIGxpbmUgcGxvdHMpLiAqL1xuICBwcml2YXRlIGdldENoYXJ0TGF5b3V0KCk6IGFueSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG1hcmdpbjogeyB0OiAzMCwgYjogNDUsIGw6IDYwLCByOiAyMCB9LFxuICAgICAgaGVpZ2h0OiB0aGlzLnNjcmVlbkhlaWdodCxcbiAgICAgIGF1dG9zaXplOiB0cnVlLFxuICAgICAgeGF4aXM6IHsgdGl0bGU6ICdQb3NpdGlvbiAocHgpJyB9LFxuICAgICAgeWF4aXM6IHsgdGl0bGU6ICdJbnRlbnNpdHknIH0sXG4gICAgICBkcmFnbW9kZTogZmFsc2UsXG4gICAgfTtcbiAgfVxuXG4gIC8qKiBJbWFnZS1hbGlnbmVkIDJEIGF4ZXMgd2l0aG91dCB0aGUgei1wbGFuZSBzbGlkZXIgKHJlZ2lvbiBzY2F0dGVyKS4gKi9cbiAgcHJpdmF0ZSBnZXRPdmVybGF5TGF5b3V0KHhSYW5nZTogbnVtYmVyW10sIHlSYW5nZTogbnVtYmVyW10pOiBhbnkge1xuICAgIHJldHVybiB7XG4gICAgICB4YXhpczogeyBjb25zdHJhaW46ICdyYW5nZScsIGNvbnN0cmFpbnRvd2FyZDogJ2NlbnRlcicsIHNpZGU6ICd0b3AnLCB0aWNrczogJycsIHJhbmdlOiB4UmFuZ2UgfSxcbiAgICAgIHlheGlzOiB7XG4gICAgICAgIGNvbnN0cmFpbjogJ3JhbmdlJywgY29uc3RyYWludG93YXJkOiAnY2VudGVyJywgcmFuZ2U6IHlSYW5nZSwgdGlja3M6ICcnLFxuICAgICAgICB0aWNrc3VmZml4OiAnICAnLCBhdXRvcmFuZ2U6IGZhbHNlLCBzY2FsZWFuY2hvcjogdGhpcy5zY2FsZXJhdGlvID8gJ3gnIDogZmFsc2UsXG4gICAgICB9LFxuICAgICAgbWFyZ2luOiB7IHQ6IDMwLCBiOiA1LCBsOiA1NSwgcjogNSB9LFxuICAgICAgaGVpZ2h0OiB0aGlzLnNjcmVlbkhlaWdodCxcbiAgICAgIGF1dG9zaXplOiB0cnVlLFxuICAgICAgc2hhcGVzOiB0aGlzLmN1cnJlbnRSZW5kZXJTaGFwZXModGhpcy5yZWdpb25TdG9yZS5nZXRTaG93U2hhcGVMYWJlbCgpKSxcbiAgICAgIGRyYWdtb2RlOiB0aGlzLmRyYWdNb2RlID8gdGhpcy5kcmFnTW9kZSA6IGZhbHNlLFxuICAgIH07XG4gIH1cblxuICAvKiogM0Qgc2NlbmUgZm9yIHZvbHVtZXRyaWMgcGxvdCB0eXBlcyAoc2NhdHRlcjNkLCBpc29zdXJmYWNlKS4gKi9cbiAgcHJpdmF0ZSBnZXRWb2x1bWVMYXlvdXQoKTogYW55IHtcbiAgICByZXR1cm4ge1xuICAgICAgbWFyZ2luOiB7IHQ6IDAsIGI6IDAsIGw6IDAsIHI6IDAgfSxcbiAgICAgIGhlaWdodDogdGhpcy5zY3JlZW5IZWlnaHQsXG4gICAgICBhdXRvc2l6ZTogdHJ1ZSxcbiAgICAgIHNjZW5lOiB7XG4gICAgICAgIHhheGlzOiB7IHRpdGxlOiAnWCcgfSxcbiAgICAgICAgeWF4aXM6IHsgdGl0bGU6ICdZJyB9LFxuICAgICAgICB6YXhpczogeyB0aXRsZTogJ1otcGxhbmUnIH0sXG4gICAgICAgIC8vICdjdWJlJyAobm90ICdkYXRhJyk6IGEgei1zdGFjayBoYXMgZmFyIGZld2VyIHBsYW5lcyB0aGFuIFgvWSBwaXhlbHMsXG4gICAgICAgIC8vIHNvICdkYXRhJyBzcXVhc2hlcyB0aGUgdm9sdW1lIGludG8gYSBuZWFyLWZsYXQgc2xhYiB0aGF0IHJlYWRzIGFzXG4gICAgICAgIC8vIGVtcHR5IGVkZ2Utb24uIEEgY3ViZSBnaXZlcyB0aGUgeiBkaW1lbnNpb24gcmVhbCBoZWlnaHQgc28gdGhlXG4gICAgICAgIC8vIGlzb3N1cmZhY2Uvdm94ZWxzIGFyZSBhY3R1YWxseSB2aXNpYmxlLlxuICAgICAgICBhc3BlY3Rtb2RlOiAnY3ViZScsXG4gICAgICB9LFxuICAgIH07XG4gIH1cblxuICAvKiogUGxvdCB0eXBlcyB0aGlzIGJhY2tlbmQgYWR2ZXJ0aXNlcyAoZHJpdmVzIHRoZSBVSSBzZWxlY3RvcikuICovXG4gIHB1YmxpYyBnZXRQbG90VHlwZURlc2NyaXB0b3JzKCk6IFBsb3RUeXBlRGVzY3JpcHRvcltdIHtcbiAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyhQTE9UX1RZUEVfREVTQ1JJUFRPUlMpLmZpbHRlcigoZCk6IGQgaXMgUGxvdFR5cGVEZXNjcmlwdG9yID0+ICEhZCk7XG4gIH1cblxuICAvKiogVGhpcyBiYWNrZW5kJ3MgcmVnaW9uIHJlbmRlcmVyLiBQbG90bHkgZHJhd3Mgc2hhcGVzIG5hdGl2ZWx5LCBzbyB0aGVcbiAgICogIG92ZXJsYXkgaXMgYSB0aGluIGFkYXB0ZXIgb3ZlciB0aGlzIHNlcnZpY2UncyBkcmFnIG1vZGVzLiAqL1xuICBwdWJsaWMgZ2V0UmVnaW9uT3ZlcmxheSgpOiBJUmVnaW9uT3ZlcmxheSB7XG4gICAgaWYgKCF0aGlzLnJlZ2lvbk92ZXJsYXkpIHRoaXMucmVnaW9uT3ZlcmxheSA9IG5ldyBQbG90bHlSZWdpb25PdmVybGF5KHRoaXMpO1xuICAgIHJldHVybiB0aGlzLnJlZ2lvbk92ZXJsYXk7XG4gIH1cblxuICAvLyDilIDilIAgSW50ZW5zaXR5IHByb2ZpbGUgKFJlZ2lvbi1iYXNlZCBsaW5lIFJPSXMpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBFbWl0cyB0aGUgZnVsbCBzZXQgb2YgaW50ZW5zaXR5IHByb2ZpbGVzIChvbmUgcGVyIHByb2ZpbGUtbGluZSByZWdpb24pXG4gICAqICB3aGVuZXZlciBhIHByb2ZpbGUgbGluZSBpcyBhZGRlZCwgbW92ZWQsIG9yIHJlbW92ZWQuICovXG4gIHB1YmxpYyBnZXRJbnRlbnNpdHlQcm9maWxlJCgpOiBPYnNlcnZhYmxlPEludGVuc2l0eVByb2ZpbGVbXT4ge1xuICAgIHJldHVybiB0aGlzLmludGVuc2l0eVByb2ZpbGUkLmFzT2JzZXJ2YWJsZSgpO1xuICB9XG5cbiAgLyoqIFBsb3RseSByZW5kZXJzIHRoZSBsaW5lIFJPSXMgKyBpbnNldCwgc28gaXQgZXhwb3NlcyB0aGUgaW50ZW5zaXR5IGNvbnRyb2xzLiAqL1xuICBwdWJsaWMgZ2V0SW50ZW5zaXR5Q29udHJvbHMoKTogSUludGVuc2l0eUNvbnRyb2xzIHwgbnVsbCB7IHJldHVybiB0aGlzOyB9XG5cbiAgLyoqXG4gICAqIFJlbmRlciB0aGUgZmxvYXRpbmcgaW50ZW5zaXR5LXByb2ZpbGUgaW5zZXQgY2hhcnQgaW50byBgZGl2SWRgIOKAlCBvbmUgbGluZVxuICAgKiB0cmFjZSBwZXIgUk9JLCBlYWNoIGRyYXduIGluIGl0cyBST0kncyBjb2xvdXIuIEtlcHQgaGVyZSAobm90IGluIHRoZSBkaWFncmFtXG4gICAqIGNvbXBvbmVudCkgc28gdGhlIGNvbnN1bWVyIG5ldmVyIHRvdWNoZXMgUGxvdGx5IGRpcmVjdGx5LlxuICAgKi9cbiAgcHVibGljIHJlbmRlckludGVuc2l0eUluc2V0KGRpdklkOiBzdHJpbmcsIHByb2ZpbGVzOiBJbnRlbnNpdHlQcm9maWxlW10pOiB2b2lkIHtcbiAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGRpdklkKTtcbiAgICBpZiAoIWVsKSByZXR1cm47XG4gICAgY29uc3QgdHJhY2VzID0gKHByb2ZpbGVzID8/IFtdKS5tYXAoKHAsIGkpID0+ICh7XG4gICAgICB4OiBwLnBvc2l0aW9ucyxcbiAgICAgIHk6IHAudmFsdWVzLFxuICAgICAgdHlwZTogJ3NjYXR0ZXInLFxuICAgICAgbW9kZTogJ2xpbmVzJyxcbiAgICAgIGxpbmU6IHsgY29sb3I6IHAuY29sb3IgPz8gJyNGRkQ0MDAnLCB3aWR0aDogMiB9LFxuICAgICAgbmFtZTogYExpbmUgJHtpICsgMX1gLFxuICAgICAgaG92ZXJpbmZvOiAneCt5JyxcbiAgICB9KSk7XG4gICAgLy8gWCBheGlzIGlzIGluIG1pY3JvbnMgd2hlbiB0aGUgaW1hZ2UgY2FycmllcyBhIHBoeXNpY2FsIHBpeGVsIHNpemUgKG1wcCksXG4gICAgLy8gb3RoZXJ3aXNlIGluIHBpeGVscyDigJQgZHJpdmVuIGJ5IHRoZSB1bml0IHRoZSBzYW1wbGVyIHRhZ2dlZCBvbiB0aGUgcHJvZmlsZS5cbiAgICBjb25zdCB1bml0ID0gKHByb2ZpbGVzID8/IFtdKS5maW5kKHAgPT4gcC51bml0KT8udW5pdCA/PyAncHgnO1xuICAgIGNvbnN0IHhUaXRsZSA9IHVuaXQgPT09ICfCtW0nID8gJ1Bvc2l0aW9uICjCtW0pJyA6ICdQb3NpdGlvbiAocHgpJztcbiAgICBQbG90bHkucmVhY3QoZWwsIHRyYWNlcyBhcyBhbnksIHtcbiAgICAgIG1hcmdpbjogeyB0OiA2LCByOiA4LCBiOiAzOCwgbDogNDAgfSxcbiAgICAgIHhheGlzOiB7IHRpdGxlOiB4VGl0bGUsIHplcm9saW5lOiBmYWxzZSwgY29sb3I6ICcjZGRkJyB9LFxuICAgICAgeWF4aXM6IHsgdGl0bGU6ICdJbnRlbnNpdHknLCB6ZXJvbGluZTogZmFsc2UsIGNvbG9yOiAnI2RkZCcgfSxcbiAgICAgIHNob3dsZWdlbmQ6IGZhbHNlLFxuICAgICAgcGFwZXJfYmdjb2xvcjogJ3JnYmEoMjUsMjUsMjUsMC45KScsXG4gICAgICBwbG90X2JnY29sb3I6ICdyZ2JhKDI1LDI1LDI1LDAuOSknLFxuICAgICAgZm9udDogeyBjb2xvcjogJyNlZWUnLCBzaXplOiAxMCB9LFxuICAgIH0gYXMgYW55LCB7IGRpc3BsYXlNb2RlQmFyOiBmYWxzZSwgcmVzcG9uc2l2ZTogdHJ1ZSB9IGFzIGFueSk7XG4gIH1cblxuICAvKipcbiAgICogUmVnaW9uIHNoYXBlcyAoZmlsdGVyZWQvcmVkcmF3biBmb3IgdGhlIGN1cnJlbnQgZmlsZSkuIFRoZSBwcm9maWxlLWxpbmUgUk9Jc1xuICAgKiBhcmUgb3JkaW5hcnkgc3RvcmUgcmVnaW9ucyAodGFnZ2VkIGBraW5kOiAncHJvZmlsZSdgKSwgc28gdGhleSBhcmUgYWxyZWFkeVxuICAgKiBwYXJ0IG9mIGB0aGlzLnNoYXBlc2Ag4oCUIG5vIHNlcGFyYXRlIGFwcGVuZCBpcyBuZWVkZWQuXG4gICAqL1xuICBwcml2YXRlIGN1cnJlbnRSZW5kZXJTaGFwZXMoc2hvd0xhYmVsID0gdGhpcy5yZWdpb25TdG9yZS5nZXRTaG93U2hhcGVMYWJlbCgpKTogYW55W10ge1xuICAgIHJldHVybiB0aGlzLnNoYXBlc1RvUmVkcmF3KHNob3dMYWJlbCk7XG4gIH1cblxuICAvKipcbiAgICogSUludGVuc2l0eUNvbnRyb2xzOiBhZGQgYW5vdGhlciBpbnRlbnNpdHktcHJvZmlsZSBsaW5lIGFzIGEgbmV1dHJhbCBSZWdpb25cbiAgICogKHRhZ2dlZCBga2luZDogJ3Byb2ZpbGUnYCkuIEl0IGlzIGEgaG9yaXpvbnRhbCBvcGVuIDItcG9pbnQgcG9seWxpbmUgc3Bhbm5pbmdcbiAgICogdGhlIGltYWdlIHdpZHRoLCBzdGFnZ2VyZWQgdmVydGljYWxseSBzbyBzdWNjZXNzaXZlIGxpbmVzIHN0YXkgZGlzdGluY3QsIGluXG4gICAqIHRoZSBuZXh0IGJyaWdodCBwYWxldHRlIGNvbG91ci4gQWRkZWQgdG8gdGhlIHNoYXJlZCBzdG9yZSBzbyBib3RoIGJhY2tlbmRzXG4gICAqIChQbG90bHkgKyBPcGVuU2VhZHJhZ29uKSByZW5kZXIgYW5kIGRyYWcgaXQ7IGV4Y2x1ZGVkIGZyb20gdGhlIFJlZ2lvbnMgdGFiXG4gICAqIGFuZCBleHBvcnRzIGJ5IGl0cyBraW5kLlxuICAgKi9cbiAgcHVibGljIGFkZFByb2ZpbGVMaW5lKCk6IFJlZ2lvbiB8IG51bGwge1xuICAgIGlmICghdGhpcy50cnVlSW1nU2l6ZSkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgY291bnQgPSB0aGlzLmdldFByb2ZpbGVSZWdpb25zKCkubGVuZ3RoO1xuICAgIGNvbnN0IGltZ1gwID0gdGhpcy50cnVlSW1nU2l6ZVswXSwgaW1nWDEgPSB0aGlzLnRydWVJbWdTaXplWzFdO1xuICAgIGNvbnN0IGltZ1RvcCA9IHRoaXMudHJ1ZUltZ1NpemVbMl0sIGltZ0JvdHRvbSA9IHRoaXMudHJ1ZUltZ1NpemVbM107XG5cbiAgICAvLyBQbGFjZSB0aGUgbGluZSB3aXRoaW4gdGhlIGN1cnJlbnRseSBWSVNJQkxFIGltYWdlIGFyZWEsIHNvIHdoZW4gem9vbWVkIGluIGl0XG4gICAgLy8gbGFuZHMgb24tc2NyZWVuIGluc3RlYWQgb2Ygc3Bhbm5pbmcgdGhlIHdob2xlIChtb3N0bHkgb2ZmLXNjcmVlbikgaW1hZ2UsIGF0XG4gICAgLy8gMi8zIG9mIHRoZSB2aXNpYmxlIHdpZHRoIGFuZCBob3Jpem9udGFsbHkgY2VudHJlZC4gRmFsbHMgYmFjayB0byB0aGUgZnVsbFxuICAgIC8vIGltYWdlIHdoZW4gbm8gdmlld3BvcnQgUk9JIGlzIGtub3duIChQbG90bHkgaGVhdG1hcCkgb3IgYSBzdGFsZSBST0kgbm8gbG9uZ2VyXG4gICAgLy8gb3ZlcmxhcHMgdGhlIGltYWdlLlxuICAgIGNvbnN0IHJvaSA9IHRoaXMubGFzdFZpc2libGVSb2k7XG4gICAgY29uc3Qgb3ZlcmxhcHMgPSAhIXJvaSAmJiByb2kud2lkdGggPiAwICYmIHJvaS5oZWlnaHQgPiAwXG4gICAgICAmJiByb2kueCA8IGltZ1gxICYmIHJvaS54ICsgcm9pLndpZHRoID4gaW1nWDBcbiAgICAgICYmIHJvaS55IDwgaW1nQm90dG9tICYmIHJvaS55ICsgcm9pLmhlaWdodCA+IGltZ1RvcDtcbiAgICBjb25zdCB4MHYgPSBvdmVybGFwcyA/IE1hdGgubWF4KGltZ1gwLCByb2khLngpIDogaW1nWDA7XG4gICAgY29uc3QgeDF2ID0gb3ZlcmxhcHMgPyBNYXRoLm1pbihpbWdYMSwgcm9pIS54ICsgcm9pIS53aWR0aCkgOiBpbWdYMTtcbiAgICBjb25zdCB0b3BWID0gb3ZlcmxhcHMgPyBNYXRoLm1heChpbWdUb3AsIHJvaSEueSkgOiBpbWdUb3A7XG4gICAgY29uc3QgYm90dG9tViA9IG92ZXJsYXBzID8gTWF0aC5taW4oaW1nQm90dG9tLCByb2khLnkgKyByb2khLmhlaWdodCkgOiBpbWdCb3R0b207XG5cbiAgICBjb25zdCBjeCA9ICh4MHYgKyB4MXYpIC8gMjtcbiAgICBjb25zdCBoYWxmID0gKCh4MXYgLSB4MHYpICogKDIgLyAzKSkgLyAyOyAvLyBsaW5lIHNwYW5zIDIvMyBvZiB0aGUgdmlzaWJsZSB3aWR0aFxuICAgIGNvbnN0IHgwID0gY3ggLSBoYWxmLCB4MSA9IGN4ICsgaGFsZjtcblxuICAgIGNvbnN0IGJhbmRIID0gYm90dG9tViAtIHRvcFY7XG4gICAgY29uc3QgbWlkWSA9ICh0b3BWICsgYm90dG9tVikgLyAyO1xuICAgIC8vIFN0YWdnZXIgc3VjY2Vzc2l2ZSBsaW5lcyBhY3Jvc3MgdGhlIHZpc2libGUgYmFuZCBzbyB0aGV5IHN0YXkgZGlzdGluY3QuXG4gICAgY29uc3QgeSA9IE1hdGgubWluKGJvdHRvbVYsIE1hdGgubWF4KHRvcFYsIG1pZFkgKyBjb3VudCAqIGJhbmRIICogMC4xMikpO1xuXG4gICAgY29uc3QgcG9seSA9IG5ldyBQb2x5Z29uKCk7XG4gICAgcG9seS5ucG9pbnRzID0gMjtcbiAgICBwb2x5Lnhwb2ludHMgPSBbeDAsIHgxXTtcbiAgICBwb2x5Lnlwb2ludHMgPSBbeSwgeV07XG4gICAgcG9seS5jb29yZGluYXRlcyA9IFtbeDAsIHldLCBbeDEsIHldXTtcbiAgICBwb2x5LmNsb3NlZCA9IGZhbHNlO1xuXG4gICAgY29uc3QgcmVnaW9uID0gbmV3IFJlZ2lvbigpO1xuICAgIHJlZ2lvbi5ib3VuZHMgPSBwb2x5O1xuICAgIHJlZ2lvbi5raW5kID0gJ3Byb2ZpbGUnO1xuICAgIHJlZ2lvbi5jb2xvciA9IHRoaXMuUFJPRklMRV9QQUxFVFRFW3RoaXMucHJvZmlsZUNvbG9yU2VxKysgJSB0aGlzLlBST0ZJTEVfUEFMRVRURS5sZW5ndGhdO1xuICAgIC8vIExlYXZlIHJlZ2lvbi5sYWJlbCB1bmRlZmluZWQgc28gYXBwbHlDbGFzc2lmaWNhdGlvbkNvbG9ycyB3b24ndCByZWNvbG9yIGl0LlxuXG4gICAgdGhpcy5yZWdpb25TdG9yZS5hZGRSZWdpb24ocmVnaW9uKTtcblxuICAgIC8vIElmIGEgUGxvdGx5IHBsb3QgaXMgb24gc2NyZWVuLCByZS1yZW5kZXIgc2hhcGVzIHNvIHRoZSBuZXcgbGluZSBhcHBlYXJzLlxuICAgIC8vIChUaGUgT1NEIG92ZXJsYXkgcmVuZGVycyBhdXRvbWF0aWNhbGx5IHZpYSBpdHMgcmVnaW9uLXVwZGF0ZSBzdWJzY3JpcHRpb24uKVxuICAgIGlmICh0aGlzLnBsb3REaXYpIHtcbiAgICAgIHRoaXMuc3luY1NoYXBlc0Zyb21TdG9yZSgpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgeyBzaGFwZXM6IHRoaXMuY3VycmVudFJlbmRlclNoYXBlcygpIH0gYXMgYW55KTtcbiAgICAgIH0gY2F0Y2ggeyAvKiBkaXYgbm90IGEgUGxvdGx5IHBsb3QgKE9TRCBvd25zIGl0KSAqLyB9XG4gICAgfVxuICAgIHJldHVybiByZWdpb247XG4gIH1cblxuICAvKiogVGhlIHByb2ZpbGUtbGluZSBST0lzIGN1cnJlbnRseSBpbiB0aGUgc3RvcmUgKGtpbmQgPT09ICdwcm9maWxlJykuICovXG4gIHByaXZhdGUgZ2V0UHJvZmlsZVJlZ2lvbnMoKTogUmVnaW9uW10ge1xuICAgIHJldHVybiB0aGlzLnJlZ2lvblN0b3JlLmdldFJlZ2lvbnMoKS5maWx0ZXIociA9PiAociBhcyBhbnkpLmtpbmQgPT09ICdwcm9maWxlJyk7XG4gIH1cblxuICAvKiogUmVjb21wdXRlIGFuZCBicm9hZGNhc3QgYSBwcm9maWxlIGZvciBldmVyeSBwcm9maWxlLWxpbmUgcmVnaW9uIChpZCArIGNvbG91clxuICAgKiAgdGFnZ2VkIHNvIHRoZSBpbnNldCB0cmFjZSBtYXRjaGVzIGl0cyBsaW5lKS4gKi9cbiAgcHJpdmF0ZSBlbWl0UHJvZmlsZXMoKTogdm9pZCB7XG4gICAgdGhpcy5pbnRlbnNpdHlQcm9maWxlJC5uZXh0KHRoaXMuZ2V0UHJvZmlsZVJlZ2lvbnMoKS5tYXAociA9PiAoe1xuICAgICAgLi4udGhpcy5jb21wdXRlSW50ZW5zaXR5UHJvZmlsZUZvclJlZ2lvbihyKSxcbiAgICAgIGlkOiByLmlkLFxuICAgICAgY29sb3I6IHIuY29sb3IsXG4gICAgfSkpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYW1wbGUgdGhlIGludGVuc2l0eSBwcm9maWxlIGZvciBhIHByb2ZpbGUtbGluZSByZWdpb246IGV4dHJhY3QgaXRzIHR3b1xuICAgKiBwb2x5Z29uIGVuZHBvaW50cyBhbmQgcmV1c2UgdGhlIHNoYXJlZCBgY29tcHV0ZUludGVuc2l0eVByb2ZpbGVgIHNhbXBsaW5nLlxuICAgKi9cbiAgcHJpdmF0ZSBjb21wdXRlSW50ZW5zaXR5UHJvZmlsZUZvclJlZ2lvbihyZWdpb246IFJlZ2lvbik6IEludGVuc2l0eVByb2ZpbGUge1xuICAgIC8vIER1Y2stdHlwZWQgKG5vdCBgaW5zdGFuY2VvZiBQb2x5Z29uYCk6IGFmdGVyIGEgZHJhZyByb3VuZC10cmlwcyB0aHJvdWdoIHRoZVxuICAgIC8vIHN0b3JlIHRoZSBib3VuZHMgY2FuIGJlIGEgcGxhaW4gb2JqZWN0LCBzbyBtYXRjaCBvbiB0aGUgcG9pbnQgYXJyYXlzLlxuICAgIGNvbnN0IHBvbHk6IGFueSA9IHJlZ2lvbi5ib3VuZHM7XG4gICAgaWYgKCFwb2x5IHx8ICFBcnJheS5pc0FycmF5KHBvbHkueHBvaW50cykgfHwgIUFycmF5LmlzQXJyYXkocG9seS55cG9pbnRzKSB8fFxuICAgICAgICBwb2x5Lnhwb2ludHMubGVuZ3RoIDwgMiB8fCBwb2x5Lnlwb2ludHMubGVuZ3RoIDwgMikge1xuICAgICAgcmV0dXJuIHsgcG9zaXRpb25zOiBbXSwgdmFsdWVzOiBbXSB9O1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5jb21wdXRlSW50ZW5zaXR5UHJvZmlsZSh7XG4gICAgICB4MDogcG9seS54cG9pbnRzWzBdLCB5MDogcG9seS55cG9pbnRzWzBdLFxuICAgICAgeDE6IHBvbHkueHBvaW50c1sxXSwgeTE6IHBvbHkueXBvaW50c1sxXSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdXBwbHkgcGl4ZWwgZnJhbWVzIGZvciBpbnRlbnNpdHkgc2FtcGxpbmcgKHVzZWQgYnkgdGhlIE9TRC9pbWFnZSBiYWNrZW5kLFxuICAgKiB3aG9zZSBjYWNoZWQgZnJhbWVzIG1heSBiZSBlbXB0eSkuIE9wdGlvbmFsbHkgYWxzbyBzZXRzIHRoZSBkYXRhL3BpeGVsIHJhdGlvcy5cbiAgICovXG4gIHB1YmxpYyBzZXRTYW1wbGluZ0ZyYW1lcyhmcmFtZXM6IGFueVtdLCByYXRpb3M6IG51bWJlcltdLCBvcmlnaW46IFtudW1iZXIsIG51bWJlcl0gPSBbMCwgMF0pOiB2b2lkIHtcbiAgICB0aGlzLmNhY2hlZEltYWdlRnJhbWVzID0gZnJhbWVzO1xuICAgIGlmIChyYXRpb3MpIHRoaXMuY2FjaGVkSW1hZ2VSYXRpb3MgPSByYXRpb3M7XG4gICAgdGhpcy5jYWNoZWRGcmFtZU9yaWdpbiA9IG9yaWdpbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBQb3B1bGF0ZSB0aGUgc2FtcGxpbmcgY2FjaGUgZm9yIHRoZSBpbnRlbnNpdHkgcHJvZmlsZXMgd2hlbiBPcGVuU2VhZHJhZ29uIGlzXG4gICAqIHRoZSBhY3RpdmUgYmFja2VuZCAodGhlIEltYWdlIHBsb3QgdHlwZSkuIEluIE9TRCBtb2RlIFBsb3RseSBuZXZlciByZW5kZXJlZCxcbiAgICogc28gYGNhY2hlZEltYWdlRnJhbWVzYC9gdHJ1ZUltZ1NpemVgIGFyZSB1bnNldCDigJQgZmV0Y2ggdGhlIGN1cnJlbnQgc2xpY2Unc1xuICAgKiBwcmV2aWV3ICh0aGUgc2FtZSBwaXhlbHMgdGhlIGhlYXRtYXAgd291bGQgc2FtcGxlKSBzbyB0aGUgbGluZSBST0lzIGhhdmVcbiAgICogcGl4ZWwgZGF0YSBhbmQgYSBwbGFjZW1lbnQgZXh0ZW50LiBSZS1lbWl0cyB0aGUgcHJvZmlsZXMgb25jZSBsb2FkZWQuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZW5zdXJlSW50ZW5zaXR5U2FtcGxpbmcoaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvLCB6SW5kZXg6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghaW1hZ2VJbmZvPy51cmxzPy5sZW5ndGgpIHJldHVybjtcbiAgICAvLyBTYW1wbGUganVzdCB0aGUgZGlzcGxheWVkIHNsaWNlIChzaG93U3RhY2sgb2ZmIOKGkiBzaW5nbGUgZnJhbWUpOyBgbG9hZGBcbiAgICAvLyBhbHNvIHNldHMgYHRoaXMudHJ1ZUltZ1NpemVgLCB3aGljaCBhZGRQcm9maWxlTGluZSBuZWVkcyBmb3IgcGxhY2VtZW50LlxuICAgIGNvbnN0IHNpbmdsZSA9IHsgLi4uaW1hZ2VJbmZvLCBzaG93U3RhY2s6IGZhbHNlIH0gYXMgSUltYWdlSW5mbztcbiAgICBjb25zdCBsb2FkZWQgPSBhd2FpdCB0aGlzLmxvYWQoc2luZ2xlLCB6SW5kZXggfHwgMCk7XG4gICAgdGhpcy5jYWNoZWRJbWFnZUZyYW1lcyA9IGxvYWRlZC5kYXRhO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VSYXRpb3MgPSBsb2FkZWQucmF0aW9zO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VXaWR0aCA9IGxvYWRlZC5zaXplcz8uWzBdID8/IHRoaXMuY2FjaGVkSW1hZ2VXaWR0aDtcbiAgICB0aGlzLmNhY2hlZEltYWdlSGVpZ2h0ID0gbG9hZGVkLnNpemVzPy5bMV0gPz8gdGhpcy5jYWNoZWRJbWFnZUhlaWdodDtcbiAgICB0aGlzLmNhY2hlZEZyYW1lT3JpZ2luID0gWzAsIDBdOyAvLyBmdWxsIHByZXZpZXdcbiAgICB0aGlzLmNhY2hlZElzR3JheXNjYWxlID0gISFpbWFnZUluZm8uaXNHcmF5c2NhbGU7XG4gICAgdGhpcy5lbWl0UHJvZmlsZXMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiB7QGxpbmsgSUludGVuc2l0eVNhbXBsaW5nfSBzdHViIOKAlCB0aGUgdmlld3BvcnQtY2hhbmdlIHNpZ25hbCBpcyBPcGVuU2VhZHJhZ29uJ3NcbiAgICogKGl0IHJlLXNhbXBsZXMgb24gT1NEIHpvb20vcGFuKS4gUGxvdGx5J3Mgb3duIGhpZ2gtZGVmIHpvb20gdXBkYXRlcyB0aGVcbiAgICogc2FtcGxpbmcgY2FjaGUgaW5saW5lLCBzbyBpdCBuZXZlciBlbWl0cyBoZXJlOyByZXR1cm5zIEVNUFRZIHNvIGEgdW5pZm9ybVxuICAgKiBgSVZpc3VhbGl6ZXJgIGNvbnN1bWVyIGNhbiBzdWJzY3JpYmUgcmVnYXJkbGVzcyBvZiB0aGUgYWN0aXZlIGJhY2tlbmQuXG4gICAqL1xuICBwdWJsaWMgZ2V0Vmlld3BvcnRDaGFuZ2UkKCk6IE9ic2VydmFibGU8eyB4OiBudW1iZXI7IHk6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfT4ge1xuICAgIHJldHVybiBFTVBUWTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZS1mZXRjaCB0aGUgZ2l2ZW4gaW1hZ2UtcGl4ZWwgUk9JIGF0IHRoZSBkaXNwbGF5IHJlc29sdXRpb24gYW5kIHVzZSBpdCBhc1xuICAgKiB0aGUgaW50ZW5zaXR5LXNhbXBsaW5nIHNvdXJjZSwgc28gdGhlIHByb2ZpbGUgcmVmbGVjdHMgdGhlIGRhdGEgYXQgdGhlXG4gICAqIGN1cnJlbnQgem9vbSBsZXZlbC4gU2hhcmVkIGJ5IGJvdGggYmFja2VuZHM6IHRoZSBPU0Qgdmlld3BvcnQtY2hhbmdlIGhvb2tcbiAgICogY2FsbHMgaXQgb24gem9vbS9wYW47IFBsb3RseSdzIG93biBoaWdoLWRlZiB6b29tICh0cmlnZ2VyWm9vbSkgdXBkYXRlcyB0aGVcbiAgICogc2FtZSBjYWNoZSBpbmxpbmUuIGByb2lgIGlzIGluIGZ1bGwtaW1hZ2UgcGl4ZWwgY29vcmRpbmF0ZXMuXG4gICAqL1xuICBwdWJsaWMgcmVmcmVzaEludGVuc2l0eVNhbXBsaW5nRm9yUm9pKHg6IG51bWJlciwgeTogbnVtYmVyLCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6SW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIGlmICh3aWR0aCA8PSAwIHx8IGhlaWdodCA8PSAwKSByZXR1cm47XG4gICAgLy8gUmVtZW1iZXIgdGhlIHZpc2libGUgcmVnaW9uIHNvIGEgbmV3IHByb2ZpbGUgbGluZSBpcyBwbGFjZWQgaW5zaWRlIGl0LlxuICAgIHRoaXMubGFzdFZpc2libGVSb2kgPSB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfTtcbiAgICBjb25zdCByb2kgPSBuZXcgUmVjdGFuZ2xlKCk7XG4gICAgcm9pLnggPSBNYXRoLnJvdW5kKHgpOyByb2kueSA9IE1hdGgucm91bmQoeSk7XG4gICAgcm9pLndpZHRoID0gTWF0aC5yb3VuZCh3aWR0aCk7IHJvaS5oZWlnaHQgPSBNYXRoLnJvdW5kKGhlaWdodCk7XG4gICAgY29uc3Qgc2NyZWVuID0gdGhpcy5wbG90VXRpbGl0aWVzLmdldERvbVJlY3RhbmdsZSgnZGlhZ3JhbScpO1xuICAgIC8vIFNuYXBzaG90IHRoZSBmaWxlbmFtZSBzbyBhIHJlc3BvbnNlIHRoYXQgYXJyaXZlcyBhZnRlciB0aGUgdXNlciBzd2l0Y2hlZFxuICAgIC8vIGZpbGVzIGlzIGRyb3BwZWQgKHRoZSByZXF1ZXN0IGNhcnJpZWQgdGhlIGZpbGUgc2VsZWN0ZWQgYXQgY2FsbCB0aW1lKS5cbiAgICBjb25zdCByZXFOYW1lID0gdGhpcy5maWxlTmFtZTtcbiAgICB0aGlzLnRpbGVzLnpvb21PblJlZ2lvbihyb2ksIHNjcmVlbiwgekluZGV4IHx8IDApLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAoem9vbURhdGEpID0+IHtcbiAgICAgICAgSW1hZ2UubG9hZChCdWZmZXIuZnJvbShuZXcgVWludDhBcnJheSh6b29tRGF0YSkpKS50aGVuKChpbWFnZTogYW55KSA9PiB7XG4gICAgICAgICAgaWYgKHRoaXMuZmlsZU5hbWUgIT09IHJlcU5hbWUpIHJldHVybjtcbiAgICAgICAgICBjb25zdCBpc0dyYXkgPSAhIXRoaXMuaW1hZ2VJbmZvPy5pc0dyYXlzY2FsZTtcbiAgICAgICAgICBjb25zdCBmcmFtZSA9IGlzR3JheVxuICAgICAgICAgICAgPyB0aGlzLnBsb3RVdGlsaXRpZXMuYXJyYXlUb01hdHJpeChpbWFnZS5ncmV5KCkuZGF0YSwgaW1hZ2Uud2lkdGgpXG4gICAgICAgICAgICA6IHRoaXMucGxvdFV0aWxpdGllcy5hcnJheVRvTWF0cml4KGltYWdlLmdldFBpeGVsc0FycmF5KCksIGltYWdlLndpZHRoKTtcbiAgICAgICAgICB0aGlzLnNldFNhbXBsaW5nRnJhbWVzKFtmcmFtZV0sIFtyb2kud2lkdGggLyBpbWFnZS53aWR0aCwgcm9pLmhlaWdodCAvIGltYWdlLmhlaWdodF0sXG4gICAgICAgICAgICBbcm9pLngsIHJvaS55XSk7XG4gICAgICAgICAgdGhpcy5lbWl0UHJvZmlsZXMoKTtcbiAgICAgICAgfSk7XG4gICAgICB9LFxuICAgICAgZXJyb3I6ICgpID0+IHsgLyoga2VlcCB0aGUgcHJldmlvdXMgc2FtcGxpbmcgZnJhbWUgb24gYSBmYWlsZWQgY3JvcCBmZXRjaCAqLyB9LFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFNhbXBsZSB0aGUgYWN0aXZlIGZyYW1lJ3MgaW50ZW5zaXR5IChncmF5c2NhbGUgdmFsdWUgb3IgUkdCIGx1bWluYW5jZSlcbiAgICogYWxvbmcgdGhlIGxpbmUgUk9JLiBSZXR1cm5zIGRpc3RhbmNlLWFsb25nLWxpbmUgcG9zaXRpb25zIGFuZCB2YWx1ZXMuXG4gICAqL1xuICBwcml2YXRlIGNvbXB1dGVJbnRlbnNpdHlQcm9maWxlKGxpbmU6IGFueSk6IEludGVuc2l0eVByb2ZpbGUge1xuICAgIGNvbnN0IGVtcHR5OiBJbnRlbnNpdHlQcm9maWxlID0geyBwb3NpdGlvbnM6IFtdLCB2YWx1ZXM6IFtdIH07XG4gICAgaWYgKCF0aGlzLmNhY2hlZEltYWdlRnJhbWVzPy5sZW5ndGggfHwgIWxpbmUpIHJldHVybiBlbXB0eTtcbiAgICBjb25zdCBmcmFtZSA9IHRoaXMuY2FjaGVkSW1hZ2VGcmFtZXNbdGhpcy5hY3RpdmVGcmFtZUluZGV4KCldID8/IHRoaXMuY2FjaGVkSW1hZ2VGcmFtZXNbMF07XG4gICAgaWYgKCFmcmFtZT8ubGVuZ3RoKSByZXR1cm4gZW1wdHk7XG4gICAgY29uc3QgcnggPSB0aGlzLmNhY2hlZEltYWdlUmF0aW9zWzBdIHx8IDE7XG4gICAgY29uc3QgcnkgPSB0aGlzLmNhY2hlZEltYWdlUmF0aW9zWzFdIHx8IHJ4O1xuICAgIGNvbnN0IFtveCwgb3ldID0gdGhpcy5jYWNoZWRGcmFtZU9yaWdpbjtcbiAgICBjb25zdCB4MCA9ICtsaW5lLngwLCB5MCA9ICtsaW5lLnkwLCB4MSA9ICtsaW5lLngxLCB5MSA9ICtsaW5lLnkxO1xuICAgIGNvbnN0IGR4RGF0YSA9IHgxIC0geDAsIGR5RGF0YSA9IHkxIC0geTA7XG4gICAgLy8gU2NhbGUgdGhlIGFsb25nLWxpbmUgZGlzdGFuY2UgYnkgdGhlIHBoeXNpY2FsIHBpeGVsIHNpemUgKG1pY3JvbnMvcGl4ZWwpXG4gICAgLy8gd2hlbiB0aGUgaW1hZ2UgY2FycmllcyBvbmUg4oCUIGFuaXNvdHJvcGljIG1wcFgvbXBwWSBhcmUgYXBwbGllZCBwZXIgYXhpcyBzb1xuICAgIC8vIGRpYWdvbmFsIGxpbmVzIG1lYXN1cmUgdGhlIHRydWUgcGh5c2ljYWwgbGVuZ3RoLiBGYWxscyBiYWNrIHRvIHBpeGVscy5cbiAgICBjb25zdCB7IG1wcFgsIG1wcFkgfSA9IHRoaXMuY3VycmVudE1wcCgpO1xuICAgIGNvbnN0IHVzZU1pY3JvbnMgPSBtcHBYICE9IG51bGw7XG4gICAgY29uc3QgbGVuRGF0YSA9IHVzZU1pY3JvbnNcbiAgICAgID8gTWF0aC5oeXBvdChkeERhdGEgKiBtcHBYLCBkeURhdGEgKiAobXBwWSA/PyBtcHBYKSlcbiAgICAgIDogTWF0aC5oeXBvdChkeERhdGEsIGR5RGF0YSk7XG4gICAgY29uc3QgbGVuUHggPSBNYXRoLmh5cG90KGR4RGF0YSAvIHJ4LCBkeURhdGEgLyByeSk7XG4gICAgY29uc3QgbiA9IE1hdGgubWF4KDIsIE1hdGgucm91bmQobGVuUHgpKTtcbiAgICBjb25zdCBoID0gZnJhbWUubGVuZ3RoO1xuICAgIGNvbnN0IHcgPSBmcmFtZVswXS5sZW5ndGg7XG4gICAgY29uc3QgcG9zaXRpb25zOiBudW1iZXJbXSA9IFtdO1xuICAgIGNvbnN0IHZhbHVlczogbnVtYmVyW10gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG47IGkrKykge1xuICAgICAgY29uc3QgdCA9IGkgLyAobiAtIDEpO1xuICAgICAgLy8gTWFwIGRhdGEgY29vcmRzIHRvIHRoZSBjYWNoZWQgZnJhbWUncyBwaXhlbCBncmlkLCBvZmZzZXQgYnkgdGhlIGZyYW1lJ3NcbiAgICAgIC8vIG9yaWdpbiAobm9uLXplcm8gd2hlbiB0aGUgZnJhbWUgaXMgYSB6b29tZWQgY3JvcCkuXG4gICAgICBjb25zdCBweCA9IE1hdGgucm91bmQoKHgwICsgdCAqIGR4RGF0YSAtIG94KSAvIHJ4KTtcbiAgICAgIGNvbnN0IHB5ID0gTWF0aC5yb3VuZCgoeTAgKyB0ICogZHlEYXRhIC0gb3kpIC8gcnkpO1xuICAgICAgbGV0IHYgPSAwO1xuICAgICAgaWYgKHB4ID49IDAgJiYgcHggPCB3ICYmIHB5ID49IDAgJiYgcHkgPCBoKSB7XG4gICAgICAgIGNvbnN0IGNlbGwgPSBmcmFtZVtweV1bcHhdO1xuICAgICAgICB2ID0gQXJyYXkuaXNBcnJheShjZWxsKSA/ICgwLjI5OSAqIGNlbGxbMF0gKyAwLjU4NyAqIGNlbGxbMV0gKyAwLjExNCAqIGNlbGxbMl0pIDogY2VsbDtcbiAgICAgIH1cbiAgICAgIHBvc2l0aW9ucy5wdXNoKHQgKiBsZW5EYXRhKTtcbiAgICAgIHZhbHVlcy5wdXNoKHYpO1xuICAgIH1cbiAgICByZXR1cm4geyBwb3NpdGlvbnMsIHZhbHVlcywgdW5pdDogdXNlTWljcm9ucyA/ICfCtW0nIDogJ3B4JyB9O1xuICB9XG5cbiAgLyoqIFBoeXNpY2FsIHBpeGVsIHNpemUgKG1pY3JvbnMvcGl4ZWwpIGZvciB0aGUgY3VycmVudCBpbWFnZSwgaWYga25vd24uXG4gICAqICBtcHAgaXMgY29uc3RhbnQgYWNyb3NzIGZyYW1lcy9jaGFubmVscywgc28gdGhlIGZpcnN0IG1ldGFkYXRhIGVudHJ5IHRoYXRcbiAgICogIGNhcnJpZXMgYSBwb3NpdGl2ZSBtcHBYIHdpbnMuIFJldHVybnMgbnVsbHMgd2hlbiB0aGUgaW1hZ2UgaXMgdW5zY2FsZWQuICovXG4gIHByaXZhdGUgY3VycmVudE1wcCgpOiB7IG1wcFg6IG51bWJlciB8IG51bGw7IG1wcFk6IG51bWJlciB8IG51bGwgfSB7XG4gICAgY29uc3QgbWV0YSA9IHRoaXMuaW1hZ2VJbmZvPy5pbWFnZU1ldGE7XG4gICAgY29uc3QgbSA9IEFycmF5LmlzQXJyYXkobWV0YSlcbiAgICAgID8gKG1ldGEuZmluZChlID0+IGUgJiYgKGUubXBwWCA/PyAwKSA+IDApID8/IG1ldGFbMF0pXG4gICAgICA6IHVuZGVmaW5lZDtcbiAgICBjb25zdCBtcHBYID0gbSAmJiAobS5tcHBYID8/IDApID4gMCA/IChtLm1wcFggYXMgbnVtYmVyKSA6IG51bGw7XG4gICAgY29uc3QgbXBwWSA9IG0gJiYgKG0ubXBwWSA/PyAwKSA+IDAgPyAobS5tcHBZIGFzIG51bWJlcikgOiBudWxsO1xuICAgIHJldHVybiB7IG1wcFgsIG1wcFkgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBhdXRvc2NhbGUgdGhlIHBsb3RcbiAgICovXG4gIHB1YmxpYyBhdXRvc2NhbGUoKSB7XG4gICAgaWYgKHRoaXMucGxvdERpdikge1xuICAgICAgdGhpcy56b29tQ29vcmRpbmF0ZXMgPSBbXTtcbiAgICAgIFBsb3RseS5yZWxheW91dCh0aGlzLnBsb3REaXYsIHtcbiAgICAgICAgJ3hheGlzLmF1dG9yYW5nZSc6IHRydWUsXG4gICAgICAgICd5YXhpcy5hdXRvcmFuZ2UnOiBmYWxzZSB9XG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiByZWxheW91dCB0aGUgcGxvdFxuICAgKi9cbiAgcHVibGljIHJlbGF5b3V0KHRydWVJbWFnZVNpemU/OiBudW1iZXJbXSkge1xuICAgIGxldCBpbWdTaXplO1xuICAgIGlmICh0cnVlSW1hZ2VTaXplKSB7XG4gICAgICBpbWdTaXplID0gdHJ1ZUltYWdlU2l6ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgaW1nU2l6ZSA9IHRoaXMudHJ1ZUltZ1NpemU7XG4gICAgfVxuICAgIGlmICh0aGlzLnBsb3REaXYpIHtcbiAgICAgIC8vIFJlZnJlc2ggaGVpZ2h0IGZyb20gY3VycmVudCBET00gc28gcGFuZWwgcmVzaXplcyBhcmUgcmVmbGVjdGVkXG4gICAgICBjb25zdCBwbG90RWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgICAgaWYgKHBsb3RFbD8ub2Zmc2V0SGVpZ2h0KSB7XG4gICAgICAgIHRoaXMuc2NyZWVuSGVpZ2h0ID0gcGxvdEVsLm9mZnNldEhlaWdodDtcbiAgICAgIH1cbiAgICAgIHRyeSB7XG4gICAgICAgIGlmICh0aGlzLnpvb21Db29yZGluYXRlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgdGhpcy5nZXRIZWF0bWFwTGF5b3V0KFxuICAgICAgICAgICAgW3RoaXMuem9vbUNvb3JkaW5hdGVzWzBdLCB0aGlzLnpvb21Db29yZGluYXRlc1sxXV0sXG4gICAgICAgICAgICBbdGhpcy56b29tQ29vcmRpbmF0ZXNbMl0sIHRoaXMuem9vbUNvb3JkaW5hdGVzWzNdXSkpOyAgICAvLyByZXZlcnNlIHRoZSB5IHJhbmdlXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gYXMgYXV0b3JhbmdlIGlzIHNldCB0byBmYWxzZSwgYW5kIG5vdCByZXZlcnNlZCB3ZSBuZWVkIHRvIHNldCB0aGUgeXJhbmdlIGNvcnJlY2x5IGhlcmVcbiAgICAgICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB0aGlzLmdldEhlYXRtYXBMYXlvdXQoXG4gICAgICAgICAgICBbaW1nU2l6ZVswXSwgaW1nU2l6ZVsxXV0sIFtpbWdTaXplWzNdLCBpbWdTaXplWzJdXSkpO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICBjb25zdCBtc2cgPSBlcnI/LmVycm9yPy5tZXNzYWdlIHx8IGVycj8ubWVzc2FnZSB8fCBlcnI/LnN0YXR1c1RleHQgfHwgU3RyaW5nKGVycik7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIG9jY3VyZWQnLCBlcnIpO1xuICAgICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7IGtleTogJ2NlbnRlci10b2FzdCcsIHN0aWNreTogdHJ1ZSwgc2V2ZXJpdHk6J2Vycm9yJywgc3VtbWFyeTonQW4gZXJyb3Igb2NjdXJlZCcsIGRldGFpbDpgVGhlIGZvbGxvd2luZ1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yIG9jY3VyZWQ6ICR7bXNnfS4gUGxlYXNlIHRyeSB0byBvcGVuIHRoZSBpbWFnZSBhZ2FpbiB0aHJvdWdoIHRoZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUgbmF2aWdhdG9yLmAgfSk7XG4gICAgICAgIC8vIFRPRE8gY29ycmVjdGx5IGNsZWFyIHRoZSBwbG90XG4gICAgICAgIHRoaXMucmVzZXQoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHNldEV2ZW50cyhwbG90RGl2OiBzdHJpbmcsIGlzR3JheXNjYWxlOiBib29sZWFuLCBzY3JlZW5IZWlnaHQ6IG51bWJlcikge1xuICAgIHRoaXMuaW1hZ2VJbmZvLmlzR3JheXNjYWxlID0gaXNHcmF5c2NhbGU7XG4gICAgdGhpcy5zY3JlZW5IZWlnaHQgPSBzY3JlZW5IZWlnaHQ7XG4gICAgY29uc3QgcGxvdDogYW55ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQocGxvdERpdik7XG4gICAgaWYgKHBsb3QpIHtcbiAgICAgIC8vIHVuYmluZCBwcmV2aW91cyBldmVudFxuICAgICAgcGxvdC5yZW1vdmVFdmVudExpc3RlbmVyKCdwbG90bHlfcmVsYXlvdXQnLCB0aGlzLm9uUmVsYXlvdXRFdmVudCk7XG4gICAgICAvLyBiaW5kIG5ldyBldmVudFxuICAgICAgcGxvdC5vbigncGxvdGx5X3JlbGF5b3V0JywgdGhpcy5vblJlbGF5b3V0RXZlbnQpO1xuXG4gICAgICAvLyBDbGlja2luZyBvbiBhIHNoYXBlIGFjdGl2YXRlcyBpdCBidXQgUGxvdGx5IGRvZXNuJ3QgZmlyZSBhIGRlZGljYXRlZFxuICAgICAgLy8gZXZlbnQg4oCUIHNhbXBsZSBfYWN0aXZlU2hhcGVJbmRleCBvbiB0aGUgbmV4dCBtaWNyb3Rhc2sgc28gc3Vic2NyaWJlcnNcbiAgICAgIC8vIChSZWdpb24gRWRpdG9yKSBjYW4gbWlycm9yIHRoZSBzZWxlY3Rpb24uXG4gICAgICBwbG90LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIHRoaXMub25QbG90TW91c2VEb3duKTtcbiAgICAgIHRoaXMub25QbG90TW91c2VEb3duID0gKCkgPT4gc2V0VGltZW91dCgoKSA9PiB0aGlzLm1heWJlRW1pdEFjdGl2ZVNoYXBlSW5kZXgoKSwgMCk7XG4gICAgICBwbG90LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIHRoaXMub25QbG90TW91c2VEb3duKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHJlbGF5b3V0RXZlbnRIYW5kbGVyKGV2ZW50OiBhbnkpIHtcbiAgICBpZiAoT2JqZWN0LmtleXMoZXZlbnQpLmluY2x1ZGVzKCdkcmFnbW9kZScpKSB7XG4gICAgICB0aGlzLmRyYWdNb2RlID0gZXZlbnQuZHJhZ21vZGU7XG4gICAgfVxuICAgIC8vIHdoZW4gcmVsYXlvdXQgZXZlbnQgb2NjdXJzXG4gICAgLy8gdXBkYXRlIHRoZSByZWdpb24gc2VsZWN0aW9uIHVkcGRhdGVzXG4gICAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKGV2ZW50KTtcbiAgICBsZXQgc2hhcGVzTW9kaWZpZWQgPSBmYWxzZTtcbiAgICBrZXlzLmZvckVhY2goKGtleTogYW55KSA9PiB7XG4gICAgICBpZiAodHlwZW9mIGtleSA9PT0gJ3N0cmluZycgJiYga2V5LnN0YXJ0c1dpdGgoJ3NoYXBlc1snKSkge1xuICAgICAgICBjb25zdCBzaGFwZU51bWJlciA9ICtrZXkuc3BsaXQoJ1snKVsxXS5zcGxpdCgnXScpWzBdO1xuICAgICAgICBjb25zdCBzaGFwZUNoYW5nZSA9IGtleS5zcGxpdCgnLicpWzFdO1xuICAgICAgICBpZiAodGhpcy5zaGFwZXMgJiYgdGhpcy5zaGFwZXNbc2hhcGVOdW1iZXJdKSB7XG4gICAgICAgICAgaWYgKHNoYXBlQ2hhbmdlID09PSAncGF0aCcpIHtcbiAgICAgICAgICAgIHRoaXMuc2hhcGVzW3NoYXBlTnVtYmVyXVtzaGFwZUNoYW5nZV0gPSB0aGlzLnBsb3RVdGlsaXRpZXMucm91bmRQYXRoQ29vcmRpbmF0ZXMoZXZlbnRba2V5XSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc2hhcGVzW3NoYXBlTnVtYmVyXVtzaGFwZUNoYW5nZV0gPSBNYXRoLnJvdW5kKCtldmVudFtrZXldKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgc2hhcGVzTW9kaWZpZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gICAgaWYgKHNoYXBlc01vZGlmaWVkKSB7XG4gICAgICAvLyBNaXJyb3IgdGhlIGluLXBsYWNlIGVkaXQgaW50byB0aGUgc2hhcmVkIHN0b3JlIChzeW5jcyBpdHMgY2FjaGUgKyBlbWl0cykuXG4gICAgICB0aGlzLmNvbW1pdFNoYXBlc1RvU3RvcmUoKTtcbiAgICB9XG4gICAgLy8gbWFuYWdlIGhpZ2ggZGVmIHpvb20gKG5vdCBpZiB3ZSBhcmUgc2hvd2luZyBhIHN0YWNrKVxuICAgIGlmIChrZXlzLmxlbmd0aCA9PT0gNCAmJiB0aGlzLmlzUmVhbFpvb20pIHtcbiAgICAgIGNvbnN0IGNvb3JkaW5hdGVzOiBhbnlbXSA9IFtdO1xuICAgICAga2V5cy5mb3JFYWNoKGtleSA9PiB7XG4gICAgICAgIGlmIChrZXkuc3RhcnRzV2l0aCgneGF4aXMucmFuZ2VbJykpIHtcbiAgICAgICAgICBjb29yZGluYXRlcy5wdXNoKGV2ZW50W2tleV0pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChrZXkuc3RhcnRzV2l0aCgneWF4aXMucmFuZ2VbJykpIHtcbiAgICAgICAgICBjb29yZGluYXRlcy5wdXNoKGV2ZW50W2tleV0pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHRoaXMuem9vbUNvb3JkaW5hdGVzID0gY29vcmRpbmF0ZXM7XG4gICAgICBpZiAoIXRoaXMuaW1hZ2VJbmZvLnNob3dTdGFjaykge1xuICAgICAgICB0aGlzLnRyaWdnZXJab29tKGNvb3JkaW5hdGVzKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gd2hlbiBzaGFwZSBpcyBjcmVhdGVkXG4gICAgaWYgKGtleXMubGVuZ3RoID09PSAxICYmICdzaGFwZXMnIGluIGV2ZW50KSB7XG4gICAgICB0aGlzLnNoYXBlcyA9IGV2ZW50LnNoYXBlcyBhcyBhbnlbXTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5zaGFwZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgLy8gQSBmcmVzaGx5IFBsb3RseS1kcmF3biBzaGFwZSBoYXMgbm8gaWQgeWV0IOKAlCBnaXZlIGl0IGEgbGFiZWwgb2JqZWN0XG4gICAgICAgIC8vICh3aGVuIGxhYmVscyBhcmUgb24pIGJlZm9yZSB0aGUgc3RvcmUgbWludHMgaWQgKyBkZWZhdWx0IG5hbWUuXG4gICAgICAgIGNvbnN0IGlzTmV3ID0gdGhpcy5zaGFwZXNbaV0uaWQgPT0gbnVsbDtcbiAgICAgICAgdGhpcy5zaGFwZXNbaV0uZmlsZU5hbWUgPSB0aGlzLmZpbGVOYW1lO1xuICAgICAgICB0aGlzLnNoYXBlc1tpXSA9IHRoaXMucGxvdFV0aWxpdGllcy5zbmFwUmVnaW9uKHRoaXMuc2hhcGVzW2ldKTtcbiAgICAgICAgaWYgKGlzTmV3ICYmIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hvd1NoYXBlTGFiZWwoKSkge1xuICAgICAgICAgIHRoaXMuc2hhcGVzW2ldLmxhYmVsID0geyB0ZXh0OiB0aGlzLnNoYXBlc1tpXS5sYWJlbCxcbiAgICAgICAgICAgIHRleHR0ZW1wbGF0ZTogdGhpcy5zaGFwZXNbaV0ubGFiZWwsXG4gICAgICAgICAgICBmb250OiB7IGNvbG9yOiB0aGlzLnJlZ2lvblN0b3JlLmdldFNoYXBlQ29sb3IoKSB9LFxuICAgICAgICAgICAgdGV4dHBvc2l0aW9uOiAndG9wIGxlZnQnXG4gICAgICAgICAgfTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy8gTWludCBpZHMvbmFtZXMgaW4gdGhlIHN0b3JlLCB3cml0ZSB0aGVtIGJhY2sgb250byB0aGUgZGljdHMsIGFuZCBlbWl0LlxuICAgICAgdGhpcy5jb21taXRTaGFwZXNUb1N0b3JlKCk7XG4gICAgICBpZiAodGhpcy5pc1JlZ2lvblNhdmVkT24pIHtcbiAgICAgICAgdGhpcy5wcmV2aW91c1NoYXBlcyA9IHRoaXMuc2hhcGVzLnNsaWNlKCk7XG4gICAgICB9XG4gICAgfVxuICAgIC8vIGlmIGF1dG9zY2FsZVxuICAgIGlmIChrZXlzLmluY2x1ZGVzKCd4YXhpcy5hdXRvcmFuZ2UnKSAmJiBrZXlzLmluY2x1ZGVzKCd5YXhpcy5hdXRvcmFuZ2UnKSkge1xuICAgICAgLy8gdHJpZ2dlciBhbiBhdXRvc2NhbGUgZXZlbnQgdG8gcmVzZXQgdGhlIHNlbGVjdGVkIGltYWdlIG1vZGUgZHJvcGRvd25cbiAgICAgIHRoaXMuYXV0b3NjYWxlRXZlbnQubmV4dCgnYW4gYXV0b3NjYWxlIGhhcyBoYXBwZW5lZCcpO1xuICAgICAgLy8gc2VsZWN0IGRpc3BsYXkgdHlwZSB0byB0cmlnZ2VyIGEgbmV3IHBsb3R0aW5nIHVwZGF0ZSB3aXRoIGFsbCB0aGUgbmVjZXNzYXJ5IHBsb3R0aW5nIHBhcmFtZXRlcnMgc2V0XG4gICAgICB0aGlzLnRpbGVzLnNlbGVjdERpYWdyYW1EaXNwbGF5KCk7XG4gICAgfVxuICAgIC8vIGlmIHNob3dzdGFjayBvciBhc3BlY3RyYXRpbyBldmVudFxuICAgIGlmIChldmVudC5zaG93c3RhY2sgIT09IHVuZGVmaW5lZCB8fCBldmVudC5hc3BlY3RyYXRpbyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAvLyBTZXQgbmV3IGltYWdlIGluZm8gdG8gdHJpZ2dlciBhIHBsb3QgdXBkYXRlXG4gICAgICB0aGlzLnNldEltYWdlSW5mbyhldmVudC5zaG93c3RhY2ssIGV2ZW50LmFzcGVjdHJhdGlvKTtcbiAgICB9XG5cbiAgICAvLyBQbG90bHkgbWF5IGhhdmUgdXBkYXRlZCB0aGUgYWN0aXZlIHNoYXBlIGluZGV4IGFzIHBhcnQgb2YgdGhlIHJlbGF5b3V0XG4gICAgLy8gKGUuZy4gY2xpY2tpbmcgYSBzaGFwZSdzIGVkaXQgaGFuZGxlLCBvciBmaW5pc2hpbmcgYSBkcmF3KSDigJQgc3VyZmFjZVxuICAgIC8vIHRoYXQgY2hhbmdlIHRvIHRoZSBSZWdpb24gRWRpdG9yLlxuICAgIHRoaXMubWF5YmVFbWl0QWN0aXZlU2hhcGVJbmRleCgpO1xuICB9XG5cbiAgcHVibGljIHJlbG9hZEFuZFBsb3QoKSB7XG4gICAgdGhpcy5zZXRJbWFnZUluZm8oKTtcbiAgfVxuXG5cbiAgLy8g4pSA4pSAIFRvb2wgZGVsZWdhdGlvbnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgcHVibGljIHNldFdhbmRNb2RlKGFjdGl2ZTogYm9vbGVhbiwgb3B0aW9uczogV2FuZE9wdGlvbnMgPSB7fSkge1xuICAgIC8vIFJlLWJpbmQgdG8gb3Vyc2VsdmVzIGluIGNhc2UgdGhlIE9TRCBiYWNrZW5kIGxhc3QgdXNlZCB0aGVzZSBzaW5nbGV0b25zLlxuICAgIGlmIChhY3RpdmUpIHRoaXMud2FuZFRvb2wuYmluZEhvc3QodGhpcy53YW5kSG9zdCk7XG4gICAgdGhpcy53YW5kVG9vbC5zZXRNb2RlKGFjdGl2ZSwgb3B0aW9ucyk7XG4gIH1cblxuICBwdWJsaWMgc2V0V2FuZE9wdGlvbnMob3B0aW9uczogV2FuZE9wdGlvbnMpIHtcbiAgICB0aGlzLndhbmRUb29sLnNldE9wdGlvbnMob3B0aW9ucyk7XG4gIH1cblxuICBwdWJsaWMgY2xlYXJBY3RpdmVXYW5kUmVnaW9uKCkge1xuICAgIHRoaXMud2FuZFRvb2wuY2xlYXJBY3RpdmVSZWdpb24oKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRCcnVzaE1vZGUoYWN0aXZlOiBib29sZWFuLCBvcHRpb25zOiBCcnVzaE9wdGlvbnMgPSB7fSkge1xuICAgIC8vIFJlLWJpbmQgdG8gb3Vyc2VsdmVzIGluIGNhc2UgdGhlIE9TRCBiYWNrZW5kIGxhc3QgdXNlZCB0aGlzIHNpbmdsZXRvbi5cbiAgICBpZiAoYWN0aXZlKSB0aGlzLmJydXNoVG9vbC5iaW5kSG9zdCh0aGlzLndhbmRIb3N0KTtcbiAgICB0aGlzLmJydXNoVG9vbC5zZXRNb2RlKGFjdGl2ZSwgb3B0aW9ucyk7XG4gIH1cblxuICBwdWJsaWMgc2V0QnJ1c2hPcHRpb25zKG9wdGlvbnM6IEJydXNoT3B0aW9ucykge1xuICAgIGlmIChvcHRpb25zLnNpemUgIT0gbnVsbCkgdGhpcy5icnVzaFRvb2wuc2V0U2l6ZShvcHRpb25zLnNpemUpO1xuICB9XG5cbiAgcHVibGljIHNldFZlcnRleEVyYXNlck1vZGUoYWN0aXZlOiBib29sZWFuKSB7XG4gICAgaWYgKGFjdGl2ZSkgdGhpcy52ZXJ0ZXhFcmFzZXJUb29sLmJpbmRIb3N0KHRoaXMuZXJhc2VySG9zdCk7XG4gICAgdGhpcy52ZXJ0ZXhFcmFzZXJUb29sLnNldE1vZGUoYWN0aXZlKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRWZXJ0ZXhFcmFzZXJSYWRpdXMocmFkaXVzOiBudW1iZXIpIHtcbiAgICB0aGlzLnZlcnRleEVyYXNlclRvb2wuc2V0UmFkaXVzKHJhZGl1cyk7XG4gIH1cblxuICBwdWJsaWMgc2V0Wm9vbVRvQm94TW9kZShhY3RpdmU6IGJvb2xlYW4pIHtcbiAgICAvLyBUaGUgem9vbS10by1ib3ggdG9vbCBpcyBhIHJvb3Qgc2luZ2xldG9uIHNoYXJlZCB3aXRoIHRoZSBPU0QgYmFja2VuZCxcbiAgICAvLyB3aGljaCByZWJpbmRzIGl0IHRvIGl0cyBvd24gaG9zdCBvbiBhY3RpdmF0aW9uLiBSZWJpbmQgdG8gb3VycyB3aGVuIHdlXG4gICAgLy8gYWN0aXZhdGUgc28gYSBwcmlvciBPU0Qgem9vbS10by1ib3ggZG9lc24ndCBsZWF2ZSB0aGUgc2luZ2xldG9uIHBvaW50aW5nXG4gICAgLy8gYXQgT1NEJ3MgY29vcmRpbmF0ZS96b29tIGhhbmRsZXJzICh3aGljaCB3b3VsZCBicmVhayBib3gtem9vbSBvbiBoZWF0bWFwcykuXG4gICAgaWYgKGFjdGl2ZSkgdGhpcy5iaW5kWm9vbVRvQm94SG9zdCgpO1xuICAgIHRoaXMuem9vbVRvQm94VG9vbC5zZXRNb2RlKGFjdGl2ZSk7XG4gIH1cblxuICAvKiogQmluZCB0aGUgc2hhcmVkIHpvb20tdG8tYm94IHRvb2wgdG8gdGhpcyAoUGxvdGx5KSBiYWNrZW5kJ3MgaG9zdC4gKi9cbiAgcHJpdmF0ZSBiaW5kWm9vbVRvQm94SG9zdCgpIHtcbiAgICB0aGlzLnpvb21Ub0JveFRvb2wuYmluZEhvc3Qoe1xuICAgICAgZ2V0UGxvdERpdjogKCkgPT4gdGhpcy5wbG90RGl2LFxuICAgICAgcGl4ZWxUb0RhdGE6IChweCwgcHkpID0+IHRoaXMuem9vbUJveFBpeGVsVG9EYXRhKHB4LCBweSksXG4gICAgICBhcHBseVpvb21Ub0JveDogKGNvb3JkcykgPT4gdGhpcy5hcHBseVpvb21Ub0JveChjb29yZHMpLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIEJveC1wcm9tcHRlZCBTQU06IHNlZ21lbnQgdGhlIGRyYXduIHJlY3RhbmdsZXMuIFRoZSBTQU0gdG9vbCByZXVzZXMgdGhlXG4gICAqICB3YW5kIGhvc3QgKGNhY2hlZCBmcmFtZSArIGNvb3JkaW5hdGUgdHJhbnNmb3JtICsgcmVnaW9uIHN0b3JlKS4gKi9cbiAgcHVibGljIHNlZ21lbnRSZWN0YW5nbGVzKCk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgdGhpcy5zYW1Ub29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHJldHVybiB0aGlzLnNhbVRvb2wuc2VnbWVudEJveGVzKCk7XG4gIH1cbiAgcHVibGljIHNlZ21lbnRSZWN0YW5nbGVzQ2VsbHBvc2UoKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICBpZiAoIXRoaXMuY2VsbFNlZ21lbnRlcikgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgwKTtcbiAgICB0aGlzLmNlbGxTZWdtZW50VG9vbC5iaW5kSG9zdCh0aGlzLndhbmRIb3N0KTtcbiAgICByZXR1cm4gdGhpcy5jZWxsU2VnbWVudFRvb2wuc2VnbWVudEJveGVzKHRoaXMuY2VsbFNlZ21lbnRlcik7XG4gIH1cbiAgcHVibGljIHNldFNhbU1vZGVsKGlkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnNhbVRvb2wuc2V0TW9kZWwoaWQpO1xuICAgIHRoaXMuc2FtUG9pbnRUb29sLnNldE1vZGVsKGlkKTtcbiAgfVxuICBwdWJsaWMgc2V0U2FtUG9pbnRNb2RlKGFjdGl2ZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmIChhY3RpdmUpIHRoaXMuc2FtUG9pbnRUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHRoaXMuc2FtUG9pbnRUb29sLnNldE1vZGUoYWN0aXZlKTtcbiAgfVxuICBwdWJsaWMgY29tbWl0U2FtUG9pbnRzKCk6IHZvaWQgeyB0aGlzLnNhbVBvaW50VG9vbC5jb21taXQoKTsgfVxuICBwdWJsaWMgY2xlYXJTYW1Qb2ludHMoKTogdm9pZCB7IHRoaXMuc2FtUG9pbnRUb29sLmNsZWFyKCk7IH1cblxuICAvKipcbiAgICogVG9vbC1ob3N0IGNhbGxiYWNrOiBhcHBseSB0aGUgem9vbS10by1ib3ggc2VsZWN0aW9uLiBTdGFjayBtb2RlIGRvZXMgYVxuICAgKiBwdXJlIGF4aXMtcmFuZ2UgcmVsYXlvdXQ7IG5vbi1zdGFjayBtb2RlIGdvZXMgdGhyb3VnaCB0aGUgaGlnaC1kZWZcbiAgICogdHJpZ2dlclpvb20gcGlwZWxpbmUgc28gdGhlIGltYWdlIGlzIHJlLWZldGNoZWQgYXQgdGhlIG5ldyByZXNvbHV0aW9uLlxuICAgKi9cbiAgLyoqIE92ZXJsYXktcGl4ZWwgLT4gUGxvdGx5IGRhdGEgY29vcmRzIHZpYSB0aGUgYXhpcyBvYmplY3RzIChzdWJ0cmFjdGluZyB0aGVcbiAgICogIHBsb3QgbWFyZ2luIG9mZnNldCkuIFRoZSB6b29tLXRvLWJveCB0b29sIGNhbGxzIHRoaXMgdGhyb3VnaCBpdHMgaG9zdC4gKi9cbiAgcHJpdmF0ZSB6b29tQm94UGl4ZWxUb0RhdGEocHg6IG51bWJlciwgcHk6IG51bWJlcik6IHsgeDogbnVtYmVyOyB5OiBudW1iZXIgfSB7XG4gICAgY29uc3QgZ2Q6IGFueSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdik7XG4gICAgY29uc3QgeGF4aXMgPSBnZC5fZnVsbExheW91dC54YXhpcztcbiAgICBjb25zdCB5YXhpcyA9IGdkLl9mdWxsTGF5b3V0LnlheGlzO1xuICAgIHJldHVybiB7IHg6IHhheGlzLnAyZChweCAtIHhheGlzLl9vZmZzZXQpLCB5OiB5YXhpcy5wMmQocHkgLSB5YXhpcy5fb2Zmc2V0KSB9O1xuICB9XG5cbiAgcHJpdmF0ZSBhcHBseVpvb21Ub0JveChjb29yZGluYXRlczogbnVtYmVyW10pIHtcbiAgICB0aGlzLnpvb21Db29yZGluYXRlcyA9IGNvb3JkaW5hdGVzO1xuICAgIGlmICh0aGlzLmltYWdlSW5mbz8uc2hvd1N0YWNrKSB7XG4gICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7XG4gICAgICAgICd4YXhpcy5yYW5nZVswXSc6IGNvb3JkaW5hdGVzWzBdLFxuICAgICAgICAneGF4aXMucmFuZ2VbMV0nOiBjb29yZGluYXRlc1sxXSxcbiAgICAgICAgJ3lheGlzLnJhbmdlWzBdJzogY29vcmRpbmF0ZXNbMl0sXG4gICAgICAgICd5YXhpcy5yYW5nZVsxXSc6IGNvb3JkaW5hdGVzWzNdLFxuICAgICAgfSBhcyBhbnkpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnRyaWdnZXJab29tKGNvb3JkaW5hdGVzKTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgSVZpZXdwb3J0SG9zdCBpbXBsZW1lbnRhdGlvbiAoZm9yIHRoZSBvbi1jYW52YXMgdG9vbHMpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBUaGUgcGxvdCBlbGVtZW50IHRoZSB0b29sIGNhbnZhcyBvdmVybGF5cyBhdHRhY2ggdG8uICovXG4gIHB1YmxpYyBnZXRPdmVybGF5Q29udGFpbmVyKCk6IEhUTUxFbGVtZW50IHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMucGxvdERpdiA/IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdikgOiBudWxsO1xuICB9XG5cbiAgLyoqIFBsb3RseSBjb29yZGluYXRlIHRyYW5zZm9ybSAoc2NyZWVuIDwtPiBkYXRhIHZpYSB0aGUgYXhpcyBvYmplY3RzKS4gKi9cbiAgcHVibGljIGdldENvb3JkaW5hdGVUcmFuc2Zvcm0oKTogSUNvb3JkaW5hdGVUcmFuc2Zvcm0ge1xuICAgIHJldHVybiB0aGlzLmNvb3JkaW5hdGVUcmFuc2Zvcm07XG4gIH1cblxuICAvLyDilIDilIAgV2FuZFRvb2xIb3N0IGltcGxlbWVudGF0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBQaXhlbCBkYXRhIHRoZSB3YW5kIG5lZWRzIGZvciBzYW1wbGluZy4gbnVsbCA9IG5vIGltYWdlIGxvYWRlZC4gKi9cbiAgcHJpdmF0ZSBnZXRDYWNoZWRJbWFnZURhdGEoKTogQ2FjaGVkSW1hZ2VEYXRhIHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLmNhY2hlZEltYWdlRnJhbWVzIHx8IHRoaXMuY2FjaGVkSW1hZ2VGcmFtZXMubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbDtcbiAgICByZXR1cm4ge1xuICAgICAgZnJhbWVzOiB0aGlzLmNhY2hlZEltYWdlRnJhbWVzLFxuICAgICAgd2lkdGg6IHRoaXMuY2FjaGVkSW1hZ2VXaWR0aCxcbiAgICAgIGhlaWdodDogdGhpcy5jYWNoZWRJbWFnZUhlaWdodCxcbiAgICAgIHJhdGlvczogdGhpcy5jYWNoZWRJbWFnZVJhdGlvcyxcbiAgICAgIGlzR3JheXNjYWxlOiB0aGlzLmNhY2hlZElzR3JheXNjYWxlLFxuICAgIH07XG4gIH1cblxuICAvLyDilIDilIAgc2hhcmVkLXN0b3JlIHN5bmMgaGVscGVycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgLy9cbiAgLy8gYHRoaXMuc2hhcGVzYCBpcyBQbG90bHkncyBvd24gcmVwcmVzZW50YXRpb24gKHRoZSBkaWN0IGFycmF5IFBsb3RseS5yZWxheW91dFxuICAvLyBjb25zdW1lcykuIFRoZSBzaGFyZWQgUmVnaW9uU3RvcmUgaXMgdGhlIHNpbmdsZSBzb3VyY2Ugb2YgdHJ1dGgsIGhvbGRpbmcgdGhlXG4gIC8vIG5ldXRyYWwgUmVnaW9uIG1vZGVsLiBUaGVzZSBoZWxwZXJzIGJyaWRnZSB0aGUgdHdvOiB3cml0ZXMgbWlycm9yXG4gIC8vIGB0aGlzLnNoYXBlc2AgSU5UTyB0aGUgc3RvcmU7IGltYWdlIHN3aXRjaGVzL2V4dGVybmFsIGNoYW5nZXMgcHJvamVjdCB0aGVcbiAgLy8gc3RvcmUgYmFjayBPVVQgdG8gYHRoaXMuc2hhcGVzYC5cblxuICAvKiogQnVpbGQgUGxvdGx5IHNoYXBlIGRpY3RzIGZyb20gdGhlIHN0b3JlJ3MgcmVnaW9ucyAodGhlIHJlbmRlciBwcm9qZWN0aW9uKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25zVG9TaGFwZURpY3RzKCk6IGFueVtdIHtcbiAgICBjb25zdCBzaG93TGFiZWwgPSB0aGlzLnJlZ2lvblN0b3JlLmdldFNob3dTaGFwZUxhYmVsKCk7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9ucygpLm1hcChyID0+IHtcbiAgICAgIHIuZmlsZW5hbWUgPSB0aGlzLmZpbGVOYW1lO1xuICAgICAgcmV0dXJuIHsgLi4uci5nZXRTaGFwZShzaG93TGFiZWwpIH07XG4gICAgfSk7XG4gIH1cblxuICAvKiogUmVidWlsZCB0aGUgZGljdCB3b3JraW5nLXNldCBmcm9tIHRoZSBzdG9yZSAoYWZ0ZXIgYW4gaW1hZ2Ugc3dpdGNoLCBvciB3aGVuXG4gICAqICByZWdpb25zIGNoYW5nZWQgd2hpbGUgYW5vdGhlciBiYWNrZW5kIHdhcyByZW5kZXJpbmcpLiAqL1xuICBwcml2YXRlIHN5bmNTaGFwZXNGcm9tU3RvcmUoKTogdm9pZCB7XG4gICAgdGhpcy5zaGFwZXMgPSB0aGlzLnJlZ2lvbnNUb1NoYXBlRGljdHMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNYWtlIHRoZSBzaGFyZWQgc3RvcmUgYXV0aG9yaXRhdGl2ZSBmb3IgdGhlIGN1cnJlbnQgZGljdCB3b3JraW5nLXNldDogY29udmVydFxuICAgKiBgdGhpcy5zaGFwZXNgIHRvIG5ldXRyYWwgcmVnaW9ucyBhbmQgcmVwbGFjZSB0aGUgc3RvcmUncyBsaXN0ICh0aGUgc3RvcmVcbiAgICogbWludHMgaWRzLCBhcHBsaWVzIGNsYXNzIGNvbG91cnMsIHN5bmNzIGl0cyBwZXItaW1hZ2UgY2FjaGUgYW5kIGVtaXRzIHRoZVxuICAgKiByZWdpb24tdXBkYXRlIGV2ZW50KS4gU3RvcmUtbWludGVkIGlkcyBhcmUgd3JpdHRlbiBiYWNrIG9udG8gdGhlIGRpY3RzIHNvIHRoZVxuICAgKiB0d28gcmVwcmVzZW50YXRpb25zIHN0YXkgYWxpZ25lZC4gUHJvZmlsZS1saW5lIFJPSXMgYXJlIG9yZGluYXJ5IHJlZ2lvbnNcbiAgICogKHRhZ2dlZCBga2luZDogJ3Byb2ZpbGUnYCkgYW5kIHJvdW5kLXRyaXAgdGhyb3VnaCBoZXJlIHRvby5cbiAgICovXG4gIHByaXZhdGUgY29tbWl0U2hhcGVzVG9TdG9yZSgpOiB2b2lkIHtcbiAgICBjb25zdCByZWdpb25zID0gdGhpcy5zaGFwZXNcbiAgICAgIC5tYXAocyA9PiBPYmplY3QuYXNzaWduKG5ldyBTaGFwZVNlbGVjdGlvbigpLCBzKS5nZXRSZWdpb24oKSk7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5zZXRSZWdpb25zKHJlZ2lvbnMsIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hvd1NoYXBlTGFiZWwoKSwgdHJ1ZSxcbiAgICAgIHRoaXMucmVnaW9uU3RvcmUuZ2V0RmlsbENvbG9yKCksIGZhbHNlKTtcbiAgICBjb25zdCBzdG9yZWQgPSB0aGlzLnJlZ2lvblN0b3JlLmdldFJlZ2lvbnMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuc2hhcGVzLmxlbmd0aCAmJiBpIDwgc3RvcmVkLmxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAodGhpcy5zaGFwZXNbaV0uaWQgPT0gbnVsbCkgdGhpcy5zaGFwZXNbaV0uaWQgPSBzdG9yZWRbaV0uaWQ7XG4gICAgICBpZiAodGhpcy5zaGFwZXNbaV0ubmFtZSA9PSBudWxsKSB0aGlzLnNoYXBlc1tpXS5uYW1lID0gc3RvcmVkW2ldLm5hbWU7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFB1c2ggdGhlIGN1cnJlbnQgYHRoaXMuc2hhcGVzYCB0byBQbG90bHksIG1pcnJvciB0aGVtIGludG8gdGhlIHNoYXJlZCBzdG9yZVxuICAgKiAod2hpY2ggc3luY3MgaXRzIGNhY2hlICsgZW1pdHMgdGhlIHJlZ2lvbi11cGRhdGUgZXZlbnQpLCBhbmQgcmVmcmVzaCB0aGVcbiAgICogbG9jYWwgcHJldmlvdXMtc2hhcGVzIGJ1ZmZlci4gVGhlIHdhbmQvZXJhc2VyIHRvb2xzIGNhbGwgdGhpcyBhZnRlciBtdXRhdGluZ1xuICAgKiB0aGUgc2hhcGVzIGFycmF5IGRpcmVjdGx5LlxuICAgKi9cbiAgcHVibGljIGFwcGx5U2hhcGVzQ2hhbmdlKCkge1xuICAgIGlmICh0aGlzLmlzUmVnaW9uU2F2ZWRPbikge1xuICAgICAgdGhpcy5wcmV2aW91c1NoYXBlcyA9IHRoaXMuc2hhcGVzLnNsaWNlKCk7XG4gICAgfVxuICAgIHRoaXMuY29tbWl0U2hhcGVzVG9TdG9yZSgpO1xuICAgIGlmICh0aGlzLnBsb3REaXYpIHtcbiAgICAgIGNvbnN0IGRpY3RBcnJheSA9IHRoaXMuc2hhcGVzLm1hcChzID0+ICh7IC4uLnMgfSkpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgdGhpcy5zaGFwZXNSZWxheW91dChkaWN0QXJyYXkpIGFzIGFueSk7XG4gICAgICB9IGNhdGNoIHsgLyogZGl2IG93bmVkIGJ5IGFub3RoZXIgYmFja2VuZCAoT1NEKSDigJQgaXRzIG92ZXJsYXkgcmVuZGVycyBzaGFwZXMgKi8gfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBBY3RpdmUgZnJhbWUgaW5kZXggaW4gdGhlIGNhY2hlZCBpbWFnZSBzdGFjay4gKi9cbiAgcHJpdmF0ZSBhY3RpdmVGcmFtZUluZGV4KCk6IG51bWJlciB7XG4gICAgaWYgKCF0aGlzLmNhY2hlZEltYWdlRnJhbWVzIHx8IHRoaXMuY2FjaGVkSW1hZ2VGcmFtZXMubGVuZ3RoIDw9IDEpIHJldHVybiAwO1xuICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgIGNvbnN0IHNsaWRlckFjdGl2ZSA9IGdkPy5fZnVsbExheW91dD8uc2xpZGVycz8uWzBdPy5hY3RpdmU7XG4gICAgaWYgKHR5cGVvZiBzbGlkZXJBY3RpdmUgPT09ICdudW1iZXInKSByZXR1cm4gc2xpZGVyQWN0aXZlO1xuICAgIHJldHVybiB0aGlzLnpJbmRleC52YWx1ZSB8fCAwO1xuICB9XG5cbiAgLyoqIENhcGFiaWxpdHktZ2F0ZWQgM0Qgc2NlbmUgY29udHJvbHMgKFBsb3RseSByZW5kZXJzIHRoZSAzRCBwbG90IHR5cGVzKS4gKi9cbiAgZ2V0U3VyZmFjZTNkQ29udHJvbHMoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNldFN1cmZhY2VEcmFnTW9kZTogKG1vZGU6IHN0cmluZykgPT4gdGhpcy5zZXRTdXJmYWNlRHJhZ01vZGUobW9kZSksXG4gICAgICByZXNldFN1cmZhY2VDYW1lcmE6ICgpID0+IHRoaXMucmVzZXRTdXJmYWNlQ2FtZXJhKCksXG4gICAgfTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRTdXJmYWNlRHJhZ01vZGUobW9kZTogc3RyaW5nKSB7XG4gICAgaWYgKHRoaXMucGxvdERpdikge1xuICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgeyAnc2NlbmUuZHJhZ21vZGUnOiBtb2RlIH0gYXMgYW55KTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgcmVzZXRTdXJmYWNlQ2FtZXJhKCkge1xuICAgIGlmICh0aGlzLnBsb3REaXYpIHtcbiAgICAgIFBsb3RseS5yZWxheW91dCh0aGlzLnBsb3REaXYsIHsgJ3NjZW5lLmNhbWVyYSc6IHt9IH0gYXMgYW55KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHRyaWdnZXJab29tKGNvb3JkaW5hdGVzOiBudW1iZXJbXSkge1xuICAgIGlmIChjb29yZGluYXRlcy5sZW5ndGggPT09IDAgfHwgIXRoaXMudHJ1ZUltZ1NpemUpIHJldHVybjtcbiAgICB0aGlzLnpvb21Db29yZGluYXRlcyA9IGNvb3JkaW5hdGVzO1xuICAgIGNvbnN0IHJlY3QgPSB0aGlzLnBsb3RVdGlsaXRpZXMuZ2V0UmVjdGFuZ2xlKGNvb3JkaW5hdGVzLCB0aGlzLnRydWVJbWdTaXplKTtcbiAgICBpZiAodGhpcy5wbG90VXRpbGl0aWVzLmlzWm9vbVNhbWVBc0ltZ1NpemUocmVjdCwgdGhpcy50cnVlSW1nU2l6ZSkpIHtcbiAgICAgIHRoaXMuYXV0b3NjYWxlKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIEEgYnJpZWYgXCJDYWNoaW5nIGltYWdlLi4uXCIgbWVzc2FnZSBmb3IgdW5jYWNoZWQgZmlsZXMgKGxhcmdlIGZpbGVzIHRha2UgYVxuICAgIC8vIG1vbWVudCB0byBjYWNoZSk7IGp1c3QgYSBzcGlubmVyIG90aGVyd2lzZS5cbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZ01lc3NhZ2UodGhpcy5pbWFnZUNhY2hlZCA/ICcnIDogJ0NhY2hpbmcgaW1hZ2UuLi4nKTtcbiAgICBjb25zdCBzY3JlZW4gPSB0aGlzLnBsb3RVdGlsaXRpZXMuZ2V0RG9tUmVjdGFuZ2xlKCdkaWFncmFtJyk7XG4gICAgY29uc3QgaW1hZ2VTaXplOiBhbnlbXSA9IFtdO1xuICAgIGltYWdlU2l6ZVswXSA9IHJlY3QueDtcbiAgICBpbWFnZVNpemVbMV0gPSByZWN0LnggKyByZWN0LndpZHRoO1xuICAgIGltYWdlU2l6ZVsyXSA9IHJlY3QueTtcbiAgICBpbWFnZVNpemVbM10gPSByZWN0LnkgKyByZWN0LmhlaWdodDtcbiAgICAvLyBTbmFwc2hvdCB0aGUgZmlsZW5hbWUgc28gYSByZXNwb25zZSB0aGF0IGFycml2ZXMgYWZ0ZXIgdGhlIHVzZXIgc3dpdGNoZWRcbiAgICAvLyBmaWxlcyBpcyBkcm9wcGVkICh0aGUgcmVxdWVzdCBjYXJyaWVkIHRoZSBmaWxlIHNlbGVjdGVkIGF0IGNhbGwgdGltZSkuXG4gICAgY29uc3QgcmVxTmFtZSA9IHRoaXMuZmlsZU5hbWU7XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcodHJ1ZSk7XG4gICAgdGhpcy5zdGF0ZS5zZXRab29tKHRydWUpO1xuICAgIHRoaXMudGlsZXMuem9vbU9uUmVnaW9uKHJlY3QsIHNjcmVlbiwgdGhpcy56SW5kZXgudmFsdWUpLnN1YnNjcmliZSh7IG5leHQ6IHpvb21EYXRhID0+IHtcbiAgICAgIGNvbnN0IHVpbnQ4QXJyYXkgPSBuZXcgVWludDhBcnJheSh6b29tRGF0YSk7XG4gICAgICBjb25zdCBidWZmZXIgPSBCdWZmZXIuZnJvbSh1aW50OEFycmF5KTtcbiAgICAgIEltYWdlLmxvYWQoYnVmZmVyKS50aGVuKChpbWFnZTogYW55KSA9PiB7XG4gICAgICAgIGNvbnN0IHhSYXRpbyA9IHJlY3Qud2lkdGggLyBpbWFnZS53aWR0aDtcbiAgICAgICAgY29uc3QgeVJhdGlvID0gcmVjdC5oZWlnaHQgLyBpbWFnZS5oZWlnaHQ7XG4gICAgICAgIGlmICh0aGlzLmZpbGVOYW1lID09PSByZXFOYW1lKSB7XG4gICAgICAgICAgY29uc3QgaXNHcmF5ID0gdGhpcy5pbWFnZUluZm8uaXNHcmF5c2NhbGU7XG4gICAgICAgICAgY29uc3QgZnJhbWUgPSBpc0dyYXlcbiAgICAgICAgICAgID8gdGhpcy5wbG90VXRpbGl0aWVzLmFycmF5VG9NYXRyaXgoaW1hZ2UuZ3JleSgpLmRhdGEsIGltYWdlLndpZHRoKVxuICAgICAgICAgICAgOiB0aGlzLnBsb3RVdGlsaXRpZXMuYXJyYXlUb01hdHJpeChpbWFnZS5nZXRQaXhlbHNBcnJheSgpLCBpbWFnZS53aWR0aCk7XG4gICAgICAgICAgLy8gQWxzbyBzYW1wbGUgdGhlIGludGVuc2l0eSBwcm9maWxlcyBmcm9tIHRoaXMgaGlnaC1kZWYgY3JvcCBzbyB0aGVcbiAgICAgICAgICAvLyBpbnNldCByZWZsZWN0cyB0aGUgem9vbS1sZXZlbCByZXNvbHV0aW9uIChvcmlnaW4gPSBjcm9wIHRvcC1sZWZ0KS5cbiAgICAgICAgICB0aGlzLnNldFNhbXBsaW5nRnJhbWVzKFtmcmFtZV0sIFt4UmF0aW8sIHlSYXRpb10sIFtpbWFnZVNpemVbMF0sIGltYWdlU2l6ZVsyXV0pO1xuICAgICAgICAgIHRoaXMuZW1pdFByb2ZpbGVzKCk7XG4gICAgICAgICAgLy8gUmUtcmVuZGVyIHRoZSBoaWdoLWRlZiBjcm9wIGluIHRoZSBTQU1FIHBsb3QgdHlwZSB0aGUgdXNlciBpc1xuICAgICAgICAgIC8vIHZpZXdpbmcuIFdpdGhvdXQgdGhpcyB0aGUgem9vbSByZS1mZXRjaCBhbHdheXMgZmVsbCBiYWNrIHRvIGFcbiAgICAgICAgICAvLyBoZWF0bWFwLCBzbyB6b29taW5nIGluIGNvbnRvdXIgKG9yIGFueSByZWdpc3RyeSB0eXBlKSByZXZlcnRlZFxuICAgICAgICAgIC8vIHRvIGhlYXRtYXAuXG4gICAgICAgICAgY29uc3QgaW1wbCA9IFBMT1RMWV9QTE9UX1RZUEVfSU1QTFNbdGhpcy5wbG90VHlwZV07XG4gICAgICAgICAgY29uc3QgcmVuZGVyUHJvbWlzZSA9IGltcGxcbiAgICAgICAgICAgID8gdGhpcy5wbG90VmlhUmVnaXN0cnkodGhpcy5wbG90RGl2LCBpbXBsLFxuICAgICAgICAgICAgICAgIHRoaXMuYnVpbGRUcmFjZUlucHV0KHRoaXMuaW1hZ2VJbmZvLCB7XG4gICAgICAgICAgICAgICAgICBkYXRhOiBbZnJhbWVdLCByYXRpb3M6IFt4UmF0aW8sIHlSYXRpb10sXG4gICAgICAgICAgICAgICAgICBzaXplczogW2ltYWdlLndpZHRoLCBpbWFnZS5oZWlnaHRdLCBmaWxlbmFtZTogcmVxTmFtZSxcbiAgICAgICAgICAgICAgICB9LCBpbWFnZVNpemUpLCB0aGlzLnNjcmVlbkhlaWdodClcbiAgICAgICAgICAgIDogKGlzR3JheVxuICAgICAgICAgICAgICAgID8gdGhpcy5wbG90SGVhdG1hcCh0aGlzLnBsb3REaXYsIHRoaXMudXJscywgW2ZyYW1lXSwgaW1hZ2VTaXplLCBbeFJhdGlvLCB5UmF0aW9dLFxuICAgICAgICAgICAgICAgICAgICB0aGlzLnNjcmVlbkhlaWdodClcbiAgICAgICAgICAgICAgICA6IHRoaXMucGxvdFJHQkhlYXRtYXAodGhpcy5wbG90RGl2LCB0aGlzLnVybHMsIFtmcmFtZV0sIGltYWdlU2l6ZSwgW3hSYXRpbywgeVJhdGlvXSxcbiAgICAgICAgICAgICAgICAgICAgaW1hZ2Uud2lkdGgsIGltYWdlLmhlaWdodCwgdGhpcy5zY3JlZW5IZWlnaHQpKTtcbiAgICAgICAgICAvLyBwbG90VmlhUmVnaXN0cnkgYWxyZWFkeSBhcHBsaWVzIHRoZSB0eXBlJ3Mgb3duIGxheW91dC9yYW5nZSBmb3JcbiAgICAgICAgICAvLyBub24taW1hZ2UgbGF5b3V0cyAoY2hhcnQvb3ZlcmxheSk7IG9ubHkgcmUtYXBwbHkgdGhlIGhlYXRtYXBcbiAgICAgICAgICAvLyByYW5nZSBmb3IgdGhlIGltYWdlLWFsaWduZWQgcGF0aHMuXG4gICAgICAgICAgY29uc3QgcmVhcHBseVJhbmdlID0gIWltcGwgfHwgaW1wbC5sYXlvdXRLaW5kID09PSAnMmQtaW1hZ2UnO1xuICAgICAgICAgIHJlbmRlclByb21pc2UudGhlbigoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnN0YXRlLnNldEltYWdlQ2FjaGVkKHRydWUpO1xuICAgICAgICAgICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgICAgICAgICAgaW1hZ2UgPSBudWxsO1xuICAgICAgICAgICAgaWYgKHJlYXBwbHlSYW5nZSkgdGhpcy5yZWxheW91dChpbWFnZVNpemUpO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9LCBlcnJvcjogZXJyID0+IHtcbiAgICAgIGNvbnN0IG1zZyA9IGVycj8uZXJyb3I/Lm1lc3NhZ2UgfHwgZXJyPy5tZXNzYWdlIHx8IGVycj8uc3RhdHVzVGV4dCB8fCBTdHJpbmcoZXJyKTtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIG9jY3VyZWQgd2hlbiB6b29taW5nJywgZXJyKTtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHsga2V5OiAnY2VudGVyLXRvYXN0Jywgc3RpY2t5OiB0cnVlLCBzZXZlcml0eTonZXJyb3InLCBzdW1tYXJ5OidBbiBlcnJvciBvY2N1cmVkJyxcbiAgICAgICAgZGV0YWlsOmBUaGUgZm9sbG93aW5nIGVycm9yIG9jY3VyZWQgd2hpbGUgem9vbWluZzogJHttc2d9LlxuICAgICAgICAgICAgICAgICAgICAgICAgICBQbGVhc2UgdHJ5IHRvIG9wZW4gdGhlIGltYWdlIGFnYWluIHRocm91Z2ggdGhlIGZpbGUgbmF2aWdhdG9yIGFuZFxuICAgICAgICAgICAgICAgICAgICAgICAgICB6b29tIG9uIHRoZSBzZWxlY3RlZCBhcmVhIG9uY2UgbW9yZS5gIH0pO1xuICAgICAgdGhpcy5zdGF0ZS5zZXRMb2FkaW5nRXJyb3IodHJ1ZSk7XG4gICAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyhmYWxzZSk7XG4gICAgfSwgY29tcGxldGU6ICgpID0+IHtcbiAgICAgIGNvbnNvbGUubG9nKCd6b29taW5nIGNvbXBsZXRlJyk7XG4gICAgfSB9KTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRQbG90VHlwZShwbG90VHlwZTogUGxvdFR5cGUpIHtcbiAgICB0aGlzLnBsb3RUeXBlID0gcGxvdFR5cGU7XG4gIH1cblxuICBwcml2YXRlIHNldEltYWdlSW5mbyhzaG93U3RhY2s/OiBib29sZWFuLCBzY2FsZXJhdGlvPzogYm9vbGVhbikge1xuICAgIC8vIEJ1aWxkIGEgcGFydGlhbCBpbWFnZSBkZXNjcmlwdG9yIGFuZCBwdXNoIGl0IHRvIHRoZSBob3N0IHZpYSB0aGUgcG9ydC5cbiAgICBjb25zdCBpbWdJbmZvOiBQYXJ0aWFsPElJbWFnZUluZm8+ID0ge1xuICAgICAgaXNHcmF5c2NhbGU6IHRoaXMuaW1hZ2VJbmZvLmlzR3JheXNjYWxlLFxuICAgICAgdHJ1ZUltYWdlU2l6ZTogW3RoaXMudHJ1ZUltZ1NpemVbMV0sIHRoaXMudHJ1ZUltZ1NpemVbM11dLFxuICAgICAgdXJsczogdGhpcy51cmxzLFxuICAgICAgaXNTdGFjazogdGhpcy51cmxzLmxlbmd0aCA+IDEsXG4gICAgICBzY2FsZVJhdGlvOiBzY2FsZXJhdGlvICE9PSB1bmRlZmluZWQgPyBzY2FsZXJhdGlvIDogdGhpcy5zY2FsZXJhdGlvLFxuICAgIH07XG4gICAgaWYgKHNob3dTdGFjayAhPT0gdW5kZWZpbmVkKSBpbWdJbmZvLnNob3dTdGFjayA9IHNob3dTdGFjaztcbiAgICBpZiAodGhpcy5maWxlTmFtZSkgaW1nSW5mby5maWxlTmFtZSA9IHRoaXMuZmlsZU5hbWU7XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUluZm8oaW1nSW5mbyk7XG4gIH1cblxuICBwdWJsaWMgZ2V0U2hhcGVzKCkge1xuICAgIHJldHVybiB0aGlzLnNoYXBlcztcbiAgfVxuXG4gIC8qKlxuICAgKiBGcmFtZXdvcmstbmV1dHJhbCB2aWV3IG9mIHRoZSBjdXJyZW50IHJlZ2lvbnMg4oCUIGRlbGVnYXRlcyB0byB0aGUgc2hhcmVkXG4gICAqIFJlZ2lvblN0b3JlICh0aGUgc2luZ2xlIHNvdXJjZSBvZiB0cnV0aCkuIFRoZSBjYW5vbmljYWwgYWNjZXNzb3Igb24gdGhlXG4gICAqIGBJVmlzdWFsaXplcmAgY29udHJhY3Q7IGNvbnN1bWVycyBzaG91bGQgcHJlZmVyIHRoaXMgb3ZlciBgZ2V0U2hhcGVzKClgXG4gICAqICh3aGljaCBleHBvc2VzIFBsb3RseS1zaGFwZWQgZGljdHMpLlxuICAgKi9cbiAgcHVibGljIGdldFJlZ2lvbnMoKTogUmVnaW9uW10ge1xuICAgIHJldHVybiB0aGlzLnJlZ2lvblN0b3JlLmdldFJlZ2lvbnMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTd2l0Y2ggdGhlIGFjdGl2ZSBpbWFnZS4gVGhlIHNoYXJlZCBSZWdpb25TdG9yZSBvd25zIHRoZSBwZXItaW1hZ2UgcmVnaW9uXG4gICAqIGNhY2hlIChzbmFwc2hvdCBvdXRnb2luZyAvIHJlc3RvcmUgaW5jb21pbmcgLyBjbGVhciBzZWxlY3Rpb24gLyBlbWl0KTsgd2VcbiAgICogZGVsZWdhdGUgdG8gaXQsIHRoZW4gcmUtcHJvamVjdCB0aGUgc3RvcmUncyByZWdpb25zIGludG8gUGxvdGx5J3MgZGljdFxuICAgKiB3b3JraW5nLXNldC4gUmUtcHJvamVjdGluZyB1bmNvbmRpdGlvbmFsbHkgKGV2ZW4gd2hlbiB0aGUgc3RvcmUgbm8tb3BzIGZvclxuICAgKiB0aGUgc2FtZSBpbWFnZSkgbWVhbnMgUGxvdGx5IGFsc28gcGlja3MgdXAgcmVnaW9ucyBhbm90aGVyIGJhY2tlbmQgKE9TRClcbiAgICogYWRkZWQgd2hpbGUgaXQgd2FzIG9mZi1zY3JlZW4uIENhbGxlZCBmcm9tIGBwbG90KClgIGFuZCBmcm9tIHRoZSByb3V0ZXJcbiAgICogYmVmb3JlIGFueSBiYWNrZW5kIHJlbmRlcnMuXG4gICAqL1xuICBwdWJsaWMgc2V0QWN0aXZlSW1hZ2UoaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvKSB7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5zZXRBY3RpdmVJbWFnZShpbWFnZUluZm8pO1xuICAgIHRoaXMuc3luY1NoYXBlc0Zyb21TdG9yZSgpO1xuICAgIHRoaXMucHJldmlvdXNTaGFwZXMgPSB0aGlzLnNoYXBlcy5zbGljZSgpO1xuICB9XG5cbiAgLyoqIFRlc3QvZGV2IGhvb2sg4oCUIGRyb3AgdGhlIGVudGlyZSBwZXItaW1hZ2UgY2FjaGUgKGxvZ291dCAvIHByb2plY3Qgc3dpdGNoKS4gKi9cbiAgcHVibGljIGNsZWFyUmVnaW9uc0J5SW1hZ2VLZXkoKSB7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5jbGVhclJlZ2lvbnNCeUltYWdlS2V5KCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGFsbCB0aGUgc2VsZWN0ZWQgcmVnaW9ucyBhcyBwb2x5Z29ucy4gT3BlbiBwb2x5bGluZXMgKGNsb3NlZCA9PT0gZmFsc2UpXG4gICAqIGFyZSBleGNsdWRlZCDigJQgdGhleSdyZSBhbm5vdGF0aW9uLW9ubHkgYW5kIGNhbm5vdCBiZSB1c2VkIGFzIGZpbGxlZCByZWdpb25zXG4gICAqIGZvciBpbWFnZSBwcm9jZXNzaW5nLiBEZWxlZ2F0ZXMgdG8gdGhlIHNoYXJlZCBzdG9yZS5cbiAgICovXG4gIHB1YmxpYyBnZXRSZWdpb25Qb2x5Z29ucygpOiBhbnlbXSB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9uUG9seWdvbnMoKTtcbiAgfVxuXG4gIGlzUmVjdGFuZ2xlKGJuZHM6IGFueSk6IGJuZHMgaXMgUmVjdGFuZ2xlIHtcbiAgICByZXR1cm4gJ3gnIGluIGJuZHMgJiYgJ3knIGluIGJuZHMgJiYgJ3dpZHRoJyBpbiBibmRzICYmICdoZWlnaHQnIGluIGJuZHM7XG4gIH1cblxuICBpc1BvbHlnb24oYm5kczogYW55KTogYm5kcyBpcyBQb2x5Z29uIHtcbiAgICByZXR1cm4gJ25wb2ludHMnIGluIGJuZHMgJiYgJ3hwb2ludHMnIGluIGJuZHMgJiYgJ3lwb2ludHMnIGluIGJuZHM7XG4gIH1cblxuICAvKipcbiAgICogU2V0IHBsb3QgcmVnaW9ucy4gRGVsZWdhdGVzIHRoZSBzdGF0ZSBjaGFuZ2UgdG8gdGhlIHNoYXJlZCBSZWdpb25TdG9yZVxuICAgKiAoaWQvbmFtZSBtaW50aW5nLCBjbGFzc2lmaWNhdGlvbiBjb2xvdXJzLCBhcHBlbmQgZGUtZHVwbGljYXRpb24sIHBlci1pbWFnZVxuICAgKiBjYWNoZSBhbmQgdGhlIHJlZ2lvbi11cGRhdGUgZXZlbnQgYWxsIGxpdmUgdGhlcmUpLCB0aGVuIHJlLXByb2plY3RzIHRoZVxuICAgKiBzdG9yZSdzIHJlZ2lvbnMgaW50byBQbG90bHkncyBkaWN0IHdvcmtpbmctc2V0IGFuZCByZW5kZXJzLlxuICAgKlxuICAgKiBXaGVuIGBpc1JlZ2lvblNhdmVPbmAgaXMgZmFsc2UgdGhlIHJlZ2lvbnMgYXJlIHNob3duIHRyYW5zaWVudGx5IOKAlCByZW5kZXJlZFxuICAgKiB3aXRob3V0IGFsdGVyaW5nIHRoZSBzdG9yZWQgd29ya2luZy1zZXQgKHByZXNlcnZlcyB0aGUgcHJpb3IgYmVoYXZpb3VyKS5cbiAgICovXG4gIHB1YmxpYyBzZXRSZWdpb25zKHJlZ2lvbnM6IFJlZ2lvbltdLCBzaG93UmVnaW9uTGFiZWw/OiBib29sZWFuLFxuICAgICAgICAgICAgICAgICAgICBpc1JlZ2lvblNhdmVPbj86IGJvb2xlYW4sIGZpbGxDb2xvcj86IHN0cmluZyxcbiAgICAgICAgICAgICAgICAgICAgYXBwZW5kOiBib29sZWFuID0gZmFsc2UpIHtcbiAgICBjb25zdCBzaG93TGFiZWwgPSBzaG93UmVnaW9uTGFiZWwgPT09IHVuZGVmaW5lZCA/IHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hvd1NoYXBlTGFiZWwoKSA6IHNob3dSZWdpb25MYWJlbDtcbiAgICBjb25zdCBzYXZlID0gaXNSZWdpb25TYXZlT24gPT09IHVuZGVmaW5lZCA/IHRoaXMuaXNSZWdpb25TYXZlZE9uIDogaXNSZWdpb25TYXZlT247XG5cbiAgICB0aGlzLnJlZ2lvblN0b3JlLnNldFJlZ2lvbnMocmVnaW9ucywgc2hvd1JlZ2lvbkxhYmVsLCBpc1JlZ2lvblNhdmVPbiwgZmlsbENvbG9yLCBhcHBlbmQpO1xuICAgIHRoaXMuaXNSZWdpb25TYXZlZE9uID0gc2F2ZTtcblxuICAgIGlmIChzYXZlKSB7XG4gICAgICAvLyBSZS1wcm9qZWN0IHRoZSBhdXRob3JpdGF0aXZlIHJlZ2lvbnMgaW50byBQbG90bHkncyB3b3JraW5nLXNldCBhbmQgcmVuZGVyLlxuICAgICAgdGhpcy5zeW5jU2hhcGVzRnJvbVN0b3JlKCk7XG4gICAgICB0aGlzLnJlbmRlclNoYXBlcygpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBUcmFuc2llbnQgZGlzcGxheSBvbmx5IOKAlCByZW5kZXIgdGhlIHBhc3NlZCByZWdpb25zLCBkb24ndCB0b3VjaCB0aGVcbiAgICAgIC8vIHdvcmtpbmctc2V0IChhbmQgdGhlIHN0b3JlIGRpZG4ndCBzdG9yZSB0aGVtIGVpdGhlcikuXG4gICAgICBjb25zdCBkaWN0cyA9IHJlZ2lvbnMubWFwKHIgPT4geyByLmZpbGVuYW1lID0gdGhpcy5maWxlTmFtZTsgcmV0dXJuIHsgLi4uci5nZXRTaGFwZShzaG93TGFiZWwpIH07IH0pO1xuICAgICAgdHJ5IHtcbiAgICAgICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgdGhpcy5zaGFwZXNSZWxheW91dChkaWN0cykgYXMgYW55KTtcbiAgICAgIH0gY2F0Y2ggeyAvKiBkaXYgb3duZWQgYnkgYW5vdGhlciBiYWNrZW5kIChPU0QpIOKAlCBpdHMgb3ZlcmxheSByZW5kZXJzIHNoYXBlcyAqLyB9XG4gICAgfVxuICB9XG5cbiAgLyoqIFRoZSBzaGFwZXMtcmVsYXlvdXQgcGF5bG9hZDogcmVnaW9uIHNoYXBlcyAocHJvZmlsZS1saW5lIFJPSXMgaW5jbHVkZWQsIGFzXG4gICAqICB0aGV5IGFyZSBvcmRpbmFyeSByZWdpb25zKSBwbHVzIHRoZSBhY3RpdmUtc2hhcGUgZmlsbCBjb2xvdXIuICovXG4gIHByaXZhdGUgc2hhcGVzUmVsYXlvdXQoc2hhcGVEaWN0czogYW55W10pOiBhbnkge1xuICAgIHJldHVybiB7XG4gICAgICBzaGFwZXM6IHNoYXBlRGljdHMsXG4gICAgICBhY3RpdmVzaGFwZTogeyBmaWxsY29sb3I6IHRoaXMucmVnaW9uU3RvcmUuZ2V0RmlsbENvbG9yKCkgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFB1c2ggdGhlIGN1cnJlbnQgZGljdCB3b3JraW5nLXNldCB0byBQbG90bHkgKG5vLW9wIHdoZW4gYW5vdGhlciBiYWNrZW5kXG4gICAqICBvd25zIHRoZSBkaXYpLiAqL1xuICBwcml2YXRlIHJlbmRlclNoYXBlcygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMucGxvdERpdikgcmV0dXJuO1xuICAgIGNvbnN0IGRpY3RBcnJheSA9IHRoaXMuc2hhcGVzLm1hcChzID0+ICh7IC4uLnMgfSkpO1xuICAgIHRyeSB7XG4gICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB0aGlzLnNoYXBlc1JlbGF5b3V0KGRpY3RBcnJheSkgYXMgYW55KTtcbiAgICB9IGNhdGNoIHsgLyogZGl2IG93bmVkIGJ5IGFub3RoZXIgYmFja2VuZCAoT1NEKSDigJQgaXRzIG92ZXJsYXkgcmVuZGVycyBzaGFwZXMgKi8gfVxuICB9XG5cbiAgZ2V0U2hvd1NoYXBlTGFiZWwoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hvd1NoYXBlTGFiZWwoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgcHJldmlvdXMgc2F2ZWQgc2hhcGVzXG4gICAqL1xuICBwdWJsaWMgcGxvdFByZXZpb3VzU2hhcGVzKCkge1xuICAgIC8vIGNvbnZlcnQgdG8gZGljdCBzbyB0aGF0IHBsb3RseSByZWNvZ25pc2VzIHRoZSBzaGFwZXNcbiAgICBjb25zdCBkaWN0QXJyYXkgPSB0aGlzLnByZXZpb3VzU2hhcGVzLm1hcChzID0+ICh7IC4uLnMgfSkpO1xuICAgIHRyeSB7XG4gICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7IHNoYXBlczogZGljdEFycmF5IH0pO1xuICAgIH0gY2F0Y2ggeyAvKiBkaXYgb3duZWQgYnkgYW5vdGhlciBiYWNrZW5kIChPU0QpICovIH1cbiAgfVxuXG4gIHNldFByZXZpb3VzU2hhcGVzKHNoYXBlczogU2hhcGVTZWxlY3Rpb25bXSkge1xuICAgIHRoaXMucHJldmlvdXNTaGFwZXMgPSBzaGFwZXM7XG4gIH1cblxuICBnZXRQcmV2aW91c1NoYXBlcygpIHtcbiAgICByZXR1cm4gdGhpcy5wcmV2aW91c1NoYXBlcztcbiAgfVxuXG4gIC8vIFVuZG8gKGppdC11aSM4NSk6IHJlZ2lvbiBoaXN0b3J5IGxpdmVzIGluIHRoZSBzaGFyZWQgUmVnaW9uU3RvcmUsIHNvIHRoZVxuICAvLyBhY3RpdmUgYmFja2VuZCdzIG92ZXJsYXkgcmUtcmVuZGVycyBvZmYgaXRzIHJlZ2lvblVwZGF0ZSQgb24gcmVzdG9yZS5cbiAgcHVibGljIHVuZG8oKTogdm9pZCB7IHRoaXMucmVnaW9uU3RvcmUudW5kbygpOyB9XG4gIHB1YmxpYyByZWRvKCk6IHZvaWQgeyB0aGlzLnJlZ2lvblN0b3JlLnJlZG8oKTsgfVxuICBwdWJsaWMgY2FuVW5kbygpOiBib29sZWFuIHsgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuY2FuVW5kbygpOyB9XG4gIHB1YmxpYyBjYW5SZWRvKCk6IGJvb2xlYW4geyByZXR1cm4gdGhpcy5yZWdpb25TdG9yZS5jYW5SZWRvKCk7IH1cbiAgcHVibGljIGdldENhblVuZG8kKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4geyByZXR1cm4gdGhpcy5yZWdpb25TdG9yZS5nZXRDYW5VbmRvJCgpOyB9XG4gIHB1YmxpYyBnZXRDYW5SZWRvJCgpOiBPYnNlcnZhYmxlPGJvb2xlYW4+IHsgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0Q2FuUmVkbyQoKTsgfVxuICBwdWJsaWMgcmVzZXRVbmRvSGlzdG9yeSgpOiB2b2lkIHsgdGhpcy5yZWdpb25TdG9yZS5yZXNldFVuZG9IaXN0b3J5KCk7IH1cblxuICAvLyDilIDilIAgUGVyLXNsaWNlIHotc3RhY2sgcmVnaW9ucyDihpIgUmVnaW9uU3RvcmUgKGppdC11aSM5Mykg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIHB1YmxpYyBlbnRlclN0YWNrTW9kZShzbGljZXM6IE1hcDxudW1iZXIsIFJlZ2lvbltdPiwgaW5pdGlhbFo/OiBudW1iZXIsXG4gICAgICAgICAgICAgICAgICAgICAgICBzYXZlTGF5b3V0PzogJ2NvbWJpbmVkJyB8ICdwZXItc2xpY2UtZmlsZScpOiB2b2lkIHtcbiAgICB0aGlzLnJlZ2lvblN0b3JlLmVudGVyU3RhY2tNb2RlKHNsaWNlcywgaW5pdGlhbFosIHNhdmVMYXlvdXQpO1xuICB9XG4gIHB1YmxpYyBleGl0U3RhY2tNb2RlKCk6IHZvaWQgeyB0aGlzLnJlZ2lvblN0b3JlLmV4aXRTdGFja01vZGUoKTsgfVxuICBwdWJsaWMgaXNTdGFja01vZGUoKTogYm9vbGVhbiB7IHJldHVybiB0aGlzLnJlZ2lvblN0b3JlLmlzU3RhY2tNb2RlKCk7IH1cbiAgcHVibGljIGdldFN0YWNrU2F2ZUxheW91dCgpOiAnY29tYmluZWQnIHwgJ3Blci1zbGljZS1maWxlJyB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U3RhY2tTYXZlTGF5b3V0KCk7XG4gIH1cbiAgcHVibGljIHNldERpc3BsYXlTbGljZSh6OiBudW1iZXIpOiB2b2lkIHsgdGhpcy5yZWdpb25TdG9yZS5zZXREaXNwbGF5U2xpY2Uoeik7IH1cbiAgcHVibGljIGdldFNsaWNlUmVnaW9ucygpOiBSZWdpb25bXSB7IHJldHVybiB0aGlzLnJlZ2lvblN0b3JlLmdldFNsaWNlUmVnaW9ucygpOyB9XG4gIHB1YmxpYyBnZXRTdGFja1NhdmVTbGljZXMoKTogTWFwPG51bWJlciwgUmVnaW9uW10+IHsgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U3RhY2tTYXZlU2xpY2VzKCk7IH1cblxuICBwcml2YXRlIGdldEhlYXRtYXBMYXlvdXQoeFJhbmdlOiBudW1iZXJbXSwgeVJhbmdlOiBudW1iZXJbXSk6IGFueSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHhheGlzOiB7XG4gICAgICAgIGNvbnN0cmFpbjogJ3JhbmdlJyxcbiAgICAgICAgY29uc3RyYWludG93YXJkOiAnY2VudGVyJyxcbiAgICAgICAgc2lkZTogJ3RvcCcsXG4gICAgICAgIHRpY2tzOiAnJyxcbiAgICAgICAgcmFuZ2U6IHhSYW5nZVxuICAgICAgfSxcbiAgICAgIHlheGlzOiB7XG4gICAgICAgIGNvbnN0cmFpbjogJ3JhbmdlJyxcbiAgICAgICAgY29uc3RyYWludG93YXJkOiAnY2VudGVyJyxcbiAgICAgICAgcmFuZ2U6IHlSYW5nZSxcbiAgICAgICAgdGlja3M6ICcnLFxuICAgICAgICB0aWNrc3VmZml4OiAnICAnLFxuXHRcdFx0XHQvLyBzZXQgYXV0b3JhbmdlIHRvIGZhbHNlIHNvIHRoYXQgcGxvdGx5IGRvZXMgbm90IG92ZXJ3cml0ZSB0aGUgcmFuZ2UgZm9yIHRoZSB5IGF4aXNcblx0XHRcdFx0YXV0b3JhbmdlOiBmYWxzZSxcbiAgICAgICAgc2NhbGVhbmNob3I6IHRoaXMuc2NhbGVyYXRpbyA/ICd4JyA6IGZhbHNlXG4gICAgIH0sXG4gICAgICBtYXJnaW46IHsgdDogMzAsIGI6IDUsIGw6IDU1LCByOiA1IH0sXG4gICAgICBoZWlnaHQ6IHRoaXMuc2NyZWVuSGVpZ2h0LFxuICAgICAgc2xpZGVyczogW3tcbiAgICAgICAgcGFkOiB7IHQ6IDUwIH0sXG4gICAgICAgIGN1cnJlbnR2YWx1ZToge1xuICAgICAgICAgIHZpc2libGU6IHRydWUsXG4gICAgICAgICAgcHJlZml4OiAnWi1wbGFuZTonLFxuICAgICAgICAgIHhhbmNob3I6ICdyaWdodCcsXG4gICAgICAgIH0sXG4gICAgICAgIHN0ZXBzOiB0aGlzLmdldFN0ZXBzKClcbiAgICAgIH1dLFxuICAgICAgYXV0b3NpemU6IHRydWUsXG4gICAgICBzaGFwZXM6IHRoaXMuY3VycmVudFJlbmRlclNoYXBlcyh0aGlzLnJlZ2lvblN0b3JlLmdldFNob3dTaGFwZUxhYmVsKCkpLFxuICAgICAgYWN0aXZlc2hhcGU6IHsgZmlsbGNvbG9yOiB0aGlzLnJlZ2lvblN0b3JlLmdldEZpbGxDb2xvcigpIH0sXG4gICAgICBkcmFnbW9kZTogdGhpcy5kcmFnTW9kZSA/IHRoaXMuZHJhZ01vZGUgOiBmYWxzZSxcbiAgICAgIG5ld3NoYXBlOiB7IGxpbmU6IHsgY29sb3I6IHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hhcGVDb2xvcigpLCB3aWR0aDogMyB9IH1cbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBzaGFwZXNUb1JlZHJhdyhzaG93TGFiZWw6IGJvb2xlYW4pIHtcbiAgICBjb25zdCBzaGFwZXNUb1JlZHJhdzogU2hhcGVTZWxlY3Rpb25bXSA9IFtdO1xuICAgIGZvciAoY29uc3Qgc2hhcGUgb2YgdGhpcy5zaGFwZXMpIHtcbiAgICAgIGlmIChKU09OLnN0cmluZ2lmeShzaGFwZS5maWxlTmFtZSkgPT09IEpTT04uc3RyaW5naWZ5KHRoaXMuZmlsZU5hbWUpKSB7XG4gICAgICAgIC8vIFNldCBsYWJlbFxuICAgICAgICBpZiAoc2hvd0xhYmVsKSB7XG4gICAgICAgICAgc2hhcGUubGFiZWwgPSB7XG4gICAgICAgICAgICB0ZXh0OiBgJHtzaGFwZS5sZWdlbmR9YCxcbiAgICAgICAgICAgIHRleHR0ZW1wbGF0ZTogYCR7c2hhcGUubGVnZW5kfWAsXG4gICAgICAgICAgICB0ZXh0cG9zaXRpb246ICd0b3AgbGVmdCcsXG4gICAgICAgICAgICBmb250OiB7IGNvbG9yOiBgJHtzaGFwZS5saW5lLmNvbG9yfWAgfVxuICAgICAgICAgIH07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc2hhcGUubGFiZWwgPSB7fTtcbiAgICAgICAgfVxuICAgICAgICBzaGFwZXNUb1JlZHJhdy5wdXNoKHNoYXBlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gY29udmVydCB0byBkaWN0IHNvIHRoYXQgcGxvdGx5IHJlY29nbmlzZXMgdGhlIHNoYXBlc1xuICAgIHJldHVybiBzaGFwZXNUb1JlZHJhdy5tYXAocyA9PiAoeyAuLi5zIH0pKTtcbiAgfVxuICAvKipcbiAgICogVW51c2VkICh3aWxsIGJlIHVzZWQgd2hlbiBzdXJmYWNlIHBsb3QgaXMgYWRkZWQpXG4gICAqIEBwYXJhbSB4UmFuZ2VcbiAgICogQHBhcmFtIHlSYW5nZVxuICAgKiBAcGFyYW0gelJhdGlvXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICBwcml2YXRlIGdldFN1cmZhY2VMYXlvdXQoeFJhbmdlOiBudW1iZXJbXSwgeVJhbmdlOiBudW1iZXJbXSwgelJhdGlvOiBudW1iZXIpOiBhbnkge1xuICAgIHJldHVybiB7XG4gICAgICBtYXJnaW46IHsgdDogMCwgYjogMCwgbDogMCwgcjogMCB9LFxuICAgICAgc2NlbmU6IHtcbiAgICAgICAgeGF4aXM6IHtcbiAgICAgICAgICBncmlkY29sb3I6ICdyZ2IoMjU1LCAyNTUsIDI1NSknLFxuICAgICAgICAgIHplcm9saW5lY29sb3I6ICdyZ2IoMjU1LCAyNTUsIDI1NSknLFxuICAgICAgICAgIHNob3diYWNrZ3JvdW5kOiB0cnVlLFxuICAgICAgICAgIGJhY2tncm91bmRjb2xvcjogJ3JnYigyMzAsIDIzMCwyMzApJyxcbiAgICAgICAgICAvLyByYW5nZTogeFJhbmdlXG4gICAgICAgIH0sXG4gICAgICAgIHlheGlzOiB7XG4gICAgICAgICAgZ3JpZGNvbG9yOiAncmdiKDI1NSwgMjU1LCAyNTUpJyxcbiAgICAgICAgICB6ZXJvbGluZWNvbG9yOiAncmdiKDI1NSwgMjU1LCAyNTUpJyxcbiAgICAgICAgICBzaG93YmFja2dyb3VuZDogdHJ1ZSxcbiAgICAgICAgICBiYWNrZ3JvdW5kY29sb3I6ICdyZ2IoMjMwLCAyMzAsIDIzMCknLFxuICAgICAgICAgIC8vIGF1dG9yYW5nZTogJ3JldmVyc2VkJyxcbiAgICAgICAgICAvLyByYW5nZTogeVJhbmdlLFxuICAgICAgICAgIC8vIHNjYWxlYW5jaG9yOiAneCdcbiAgICAgICAgfSxcbiAgICAgICAgemF4aXM6IHtcbiAgICAgICAgICBncmlkY29sb3I6ICdyZ2IoMjU1LCAyNTUsIDI1NSknLFxuICAgICAgICAgIHplcm9saW5lY29sb3I6ICdyZ2IoMjU1LCAyNTUsIDI1NSknLFxuICAgICAgICAgIHNob3diYWNrZ3JvdW5kOiB0cnVlLFxuICAgICAgICAgIGJhY2tncm91bmRjb2xvcjogJ3JnYigyMzAsIDIzMCwyMzApJ1xuICAgICAgICB9LFxuICAgICAgICBhc3BlY3RyYXRpbzogeyB4OiAxLCB5OiAxLCB6OiB6UmF0aW8gfSxcbiAgICAgICAgYXNwZWN0bW9kZTogJ21hbnVhbCcsXG4gICAgICAgIHNsaWRlcnM6IFt7XG4gICAgICAgICAgcGFkOiB7IHQ6IDUwIH0sXG4gICAgICAgICAgY3VycmVudHZhbHVlOiB7XG4gICAgICAgICAgICB2aXNpYmxlOiB0cnVlLFxuICAgICAgICAgICAgcHJlZml4OiAnWi1wbGFuZTonLFxuICAgICAgICAgICAgeGFuY2hvcjogJ3JpZ2h0JyxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHN0ZXBzOiB0aGlzLmdldFN0ZXBzKClcbiAgICAgICAgfV0sXG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0U3RlcHMoKSB7XG4gICAgY29uc3Qgc3RlcHMgPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuaW1hZ2VMZW5ndGg7IGkrKykge1xuICAgICAgc3RlcHMucHVzaCh7XG4gICAgICAgIGxhYmVsOiBpICsgMSxcbiAgICAgICAgbWV0aG9kOiAncmVzdHlsZScsXG4gICAgICAgIGFyZ3M6IFsndmlzaWJsZScsIEFycmF5KHRoaXMuaW1hZ2VMZW5ndGgpLmZpbGwoZmFsc2UpLmZpbGwodHJ1ZSwgaSwgaSArIDEpXSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gc3RlcHM7XG4gIH1cblxuICAvKipcbiAgICogRmV0Y2ggYW4gaW1hZ2UgdmlhIEFuZ3VsYXIgSHR0cENsaWVudCBzbyB0aGF0IGF1dGggaW50ZXJjZXB0b3JzIChCZWFyZXIgdG9rZW4pXG4gICAqIGFyZSBhcHBsaWVkLCB0aGVuIGRlY29kZSBpdCB3aXRoIGltYWdlLWpzLiBUaGlzIGF2b2lkcyByYXcgYnJvd3NlciBmZXRjaCgpXG4gICAqIGNhbGxzIHRoYXQgYnlwYXNzIHRoZSBpbnRlcmNlcHRvciBjaGFpbiBhbmQgZmFpbCBiZWhpbmQgYW4gT0F1dGgyIHByb3h5LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBsb2FkSW1hZ2UodXJsOiBzdHJpbmcpOiBQcm9taXNlPEltYWdlPiB7XG4gICAgY29uc3QgYnVmZmVyID0gYXdhaXQgZmlyc3RWYWx1ZUZyb20oXG4gICAgICB0aGlzLmh0dHAuZ2V0KHVybCwgeyByZXNwb25zZVR5cGU6ICdhcnJheWJ1ZmZlcicgfSlcbiAgICApO1xuICAgIHJldHVybiBJbWFnZS5sb2FkKEJ1ZmZlci5mcm9tKGJ1ZmZlcikpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgY3VycmVudGx5IGRpc3BsYXllZCBpbWFnZSBhcyBhbiBpbWFnZS1qcyBJbWFnZSBpbnN0YW5jZS5cbiAgICogVXNlZCBieSB0aGUgcHJvY2Vzc2luZyBwaXBlbGluZSB0byBvYnRhaW4gdGhlIGlucHV0IGltYWdlLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGdldEN1cnJlbnRJbWFnZSgpOiBQcm9taXNlPEltYWdlIHwgbnVsbD4ge1xuICAgIGlmICghdGhpcy5pbWFnZUluZm8gfHwgIXRoaXMuaW1hZ2VJbmZvLnVybHMgfHwgdGhpcy5pbWFnZUluZm8udXJscy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBjb25zdCB6SWR4ID0gdGhpcy56SW5kZXgudmFsdWUgfHwgMDtcbiAgICBjb25zdCB1cmwgPSB0aGlzLmltYWdlSW5mby51cmxzW3pJZHhdIHx8IHRoaXMuaW1hZ2VJbmZvLnVybHNbMF07XG4gICAgcmV0dXJuIHRoaXMubG9hZEltYWdlKHVybCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBjdXJyZW50bHkgZGlzcGxheWVkIHBsb3QgcGl4ZWwgZGF0YSBhcyBhIGZsYXQgVWludDhDbGFtcGVkQXJyYXkuXG4gICAqIFRoaXMgY2FwdHVyZXMgd2hhdGV2ZXIgaXMgYWN0dWFsbHkgcmVuZGVyZWQg4oCUIGluY2x1ZGluZyB6b29tZWQgcmVnaW9ucy5cbiAgICogUmV0dXJucyB7IHdpZHRoLCBoZWlnaHQsIGNoYW5uZWxzLCBkYXRhIH0gb3IgbnVsbCBpZiBubyBwbG90IGlzIGRpc3BsYXllZC5cbiAgICovXG4gIHB1YmxpYyBnZXREaXNwbGF5ZWRQaXhlbERhdGEoKTogeyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlcjsgY2hhbm5lbHM6IG51bWJlcjtcbiAgICBkYXRhOiBVaW50OENsYW1wZWRBcnJheSB9IHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLnBsb3REaXYpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgIGlmICghZ2Q/LmRhdGEgfHwgZ2QuZGF0YS5sZW5ndGggPT09IDApIHJldHVybiBudWxsO1xuXG4gICAgLy8gRmluZCB0aGUgdmlzaWJsZSB0cmFjZVxuICAgIGNvbnN0IGZyYW1lSWR4ID0gdGhpcy5hY3RpdmVGcmFtZUluZGV4KCk7XG4gICAgY29uc3QgdHJhY2UgPSBnZC5kYXRhW2ZyYW1lSWR4XSB8fCBnZC5kYXRhWzBdO1xuICAgIGlmICghdHJhY2UgfHwgIXRyYWNlLnopIHJldHVybiBudWxsO1xuXG4gICAgY29uc3QgekRhdGE6IGFueVtdW10gPSB0cmFjZS56O1xuICAgIGNvbnN0IGhlaWdodCA9IHpEYXRhLmxlbmd0aDtcbiAgICBpZiAoaGVpZ2h0ID09PSAwKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCB3aWR0aCA9IHpEYXRhWzBdLmxlbmd0aDtcblxuICAgIGlmICh0cmFjZS50eXBlID09PSAnaW1hZ2UnKSB7XG4gICAgICAvLyBSR0IgaW1hZ2U6IHpbcm93XVtjb2xdID0gW3IsIGcsIGJdIG9yIFtyLCBnLCBiLCBhXVxuICAgICAgY29uc3Qgc2FtcGxlID0gekRhdGFbMF1bMF07XG4gICAgICBjb25zdCBjaGFubmVscyA9IEFycmF5LmlzQXJyYXkoc2FtcGxlKSA/IHNhbXBsZS5sZW5ndGggOiAzO1xuICAgICAgY29uc3QgZGF0YSA9IG5ldyBVaW50OENsYW1wZWRBcnJheSh3aWR0aCAqIGhlaWdodCAqIGNoYW5uZWxzKTtcbiAgICAgIGZvciAobGV0IHkgPSAwOyB5IDwgaGVpZ2h0OyB5KyspIHtcbiAgICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCB3aWR0aDsgeCsrKSB7XG4gICAgICAgICAgY29uc3QgcGl4ZWwgPSB6RGF0YVt5XVt4XTtcbiAgICAgICAgICBjb25zdCBvZmZzZXQgPSAoeSAqIHdpZHRoICsgeCkgKiBjaGFubmVscztcbiAgICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IGNoYW5uZWxzOyBjKyspIHtcbiAgICAgICAgICAgIGRhdGFbb2Zmc2V0ICsgY10gPSBwaXhlbFtjXTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB7IHdpZHRoLCBoZWlnaHQsIGNoYW5uZWxzLCBkYXRhIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEhlYXRtYXAgKGdyYXlzY2FsZSk6IHpbcm93XVtjb2xdID0gc2NhbGFyIHZhbHVlXG4gICAgICBjb25zdCBkYXRhID0gbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KHdpZHRoICogaGVpZ2h0KTtcbiAgICAgIGZvciAobGV0IHkgPSAwOyB5IDwgaGVpZ2h0OyB5KyspIHtcbiAgICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCB3aWR0aDsgeCsrKSB7XG4gICAgICAgICAgZGF0YVt5ICogd2lkdGggKyB4XSA9IE1hdGgucm91bmQoekRhdGFbeV1beF0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4geyB3aWR0aCwgaGVpZ2h0LCBjaGFubmVsczogMSwgZGF0YSB9O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpb24gb2YgdGhlIG9yaWdpbmFsIGltYWdlIHRoYXQgdGhlIGRpc3BsYXllZCBgemAgZ3JpZCBjb3ZlcnMsIGluXG4gICAqIGZ1bGwtaW1hZ2UgcGl4ZWwgY29vcmRzLiBPbiBhIHNlcnZlciB6b29tIChgdHJpZ2dlclpvb21gKSB0aGUgdHJhY2UgYHpgIGlzXG4gICAqIHJlcGxhY2VkIHdpdGggdGhlIGhpZ2gtZGVmIGNyb3AgYW5kIHRoZSBheGVzIGFyZSByZWxhaWQgb3V0IHRvIHRoZSBjcm9wJ3NcbiAgICogb3JpZ2luYWwtaW1hZ2UgYm91bmRzIChgaW1hZ2VTaXplYCksIHNvIHRoZSBsaXZlIGF4aXMgcmFuZ2VzIEFSRSB0aGF0XG4gICAqIHJlY3RhbmdsZTsgd2hlbiB0aGUgd2hvbGUgaW1hZ2UgaXMgaW4gdmlldyB0aGUgcmFuZ2VzIHNwYW4gYHRydWVJbWdTaXplYC5cbiAgICogUmVhZGluZyB0aGUgcmFuZ2VzIHRoZXJlZm9yZSB5aWVsZHMgdGhlIGNyb3Agb3JpZ2luICsgZXh0ZW50IHdpdGhvdXRcbiAgICogdHJhY2tpbmcgem9vbSBzdGF0ZSBzZXBhcmF0ZWx5LiBGYWxscyBiYWNrIHRvIHRoZSBmdWxsIGltYWdlLCB0aGVuIGBudWxsYC5cbiAgICovXG4gIHB1YmxpYyBnZXREaXNwbGF5ZWRTb3VyY2VSZWN0KCk6IHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0gfCBudWxsIHtcbiAgICBjb25zdCBnZDogYW55ID0gdGhpcy5wbG90RGl2ID8gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2KSA6IG51bGw7XG4gICAgY29uc3QgeHI6IG51bWJlcltdIHwgdW5kZWZpbmVkID0gZ2Q/Ll9mdWxsTGF5b3V0Py54YXhpcz8ucmFuZ2U7XG4gICAgY29uc3QgeXI6IG51bWJlcltdIHwgdW5kZWZpbmVkID0gZ2Q/Ll9mdWxsTGF5b3V0Py55YXhpcz8ucmFuZ2U7XG4gICAgaWYgKHhyICYmIHlyKSB7XG4gICAgICBjb25zdCB4MCA9IE1hdGgubWluKHhyWzBdLCB4clsxXSk7XG4gICAgICBjb25zdCB5MCA9IE1hdGgubWluKHlyWzBdLCB5clsxXSk7IC8vIHkgYXhpcyBpcyByZXZlcnNlZCBmb3IgaW1hZ2UgbGF5b3V0c1xuICAgICAgcmV0dXJuIHsgeDogeDAsIHk6IHkwLCB3aWR0aDogTWF0aC5hYnMoeHJbMV0gLSB4clswXSksIGhlaWdodDogTWF0aC5hYnMoeXJbMV0gLSB5clswXSkgfTtcbiAgICB9XG4gICAgaWYgKCF0aGlzLnRydWVJbWdTaXplKSByZXR1cm4gbnVsbDtcbiAgICByZXR1cm4ge1xuICAgICAgeDogdGhpcy50cnVlSW1nU2l6ZVswXSxcbiAgICAgIHk6IHRoaXMudHJ1ZUltZ1NpemVbMl0sXG4gICAgICB3aWR0aDogdGhpcy50cnVlSW1nU2l6ZVsxXSAtIHRoaXMudHJ1ZUltZ1NpemVbMF0sXG4gICAgICBoZWlnaHQ6IHRoaXMudHJ1ZUltZ1NpemVbM10gLSB0aGlzLnRydWVJbWdTaXplWzJdLFxuICAgIH07XG4gIH1cblxuICBwdWJsaWMgcmVzZXQoKSB7XG4gICAgaWYgKHRoaXMucGxvdERpdikge1xuICAgICAgUGxvdGx5Lm5ld1Bsb3QodGhpcy5wbG90RGl2LCBbXSxcbiAgICAgICAgdGhpcy5nZXRIZWF0bWFwTGF5b3V0KFswLCAxMDBdLCBbMTAwLCAwXSksIENPTkZJRyBhcyBhbnkpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGdWxseSByZW1vdmUgUGxvdGx5J3MgRE9NIGZyb20gdGhlIHBsb3QgZGl2IChheGVzIGluY2x1ZGVkKS4gVXNlZCB3aGVuXG4gICAqIGhhbmRpbmcgdGhlIGRpdiB0byBhbm90aGVyIGJhY2tlbmQgKE9wZW5TZWFkcmFnb24pIOKAlCB1bmxpa2UgYHJlc2V0KClgLFxuICAgKiB3aGljaCByZS1yZW5kZXJzIGFuIGVtcHR5IHBsb3QgYW5kIHdvdWxkIGxlYXZlIHRoZSBQbG90bHkgYXhlcyBzaG93aW5nLlxuICAgKi9cbiAgcHVibGljIHB1cmdlUGxvdCgpIHtcbiAgICBpZiAodGhpcy5wbG90RGl2KSB7XG4gICAgICBQbG90bHkucHVyZ2UodGhpcy5wbG90RGl2KTtcbiAgICB9XG4gIH1cblxuICAvKiogRXhwb3J0IHRoZSBoZWF0bWFwIGFzIGEgUE5HICh0aGUgcmVuZGVyZWQgcGxvdCBhbHJlYWR5IHJlZmxlY3RzIHRoZSBhY3RpdmVcbiAgICogIGNvbG9ybWFwL3dpbmRvdykuIFRoZSBmdWxsLXJlcyB0aWxlIGNvbXBvc2l0ZSBpcyBPU0QncyBqb2IuICovXG4gIHB1YmxpYyBleHBvcnRDb21wb3NpdGUoKTogdm9pZCB7XG4gICAgdGhpcy5kb3dubG9hZEltYWdlKCk7XG4gIH1cblxuICBwdWJsaWMgZG93bmxvYWRJbWFnZSgpIHtcbiAgICBpZiAodGhpcy5wbG90RGl2KSB7XG4gICAgICAoUGxvdGx5IGFzIGFueSkuZG93bmxvYWRJbWFnZSh0aGlzLnBsb3REaXYsIHsgZm9ybWF0OiAncG5nJywgZmlsZW5hbWU6IHRoaXMuZmlsZU5hbWUgfHwgJ2ltYWdlJyB9KTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgcmVzZXRBeGVzKCkge1xuICAgIHRoaXMuem9vbUNvb3JkaW5hdGVzID0gW107XG4gICAgdGhpcy5yZWxheW91dCgpO1xuICB9XG5cbiAgcHVibGljIHNldERyYWdNb2RlKG1vZGU6IHN0cmluZyB8IGZhbHNlKSB7XG4gICAgdGhpcy5kcmFnTW9kZSA9IG1vZGUgPyAobW9kZSBhcyBzdHJpbmcpIDogJyc7XG4gICAgaWYgKCF0aGlzLnBsb3REaXYpIHJldHVybjtcbiAgICAvLyBPbmx5IHJlbGF5b3V0IGEgTElWRSBQbG90bHkgZ3JhcGguIFdoZW4gYW5vdGhlciBiYWNrZW5kIChPU0QgLyBuYXBhcmktanMpIG93bnMgdGhlIHZpZXcgdGhlXG4gICAgLy8gZGl2IGlkIGlzIHN0aWxsIHNldCBidXQgaGFzIG5vIFBsb3RseSBwbG90IGF0dGFjaGVkLCBhbmQgUGxvdGx5LnJlbGF5b3V0IHRocm93c1xuICAgIC8vIChcImNhbid0IGFjY2VzcyBwcm9wZXJ0eSBfZ3VpRWRpdGluZywgbiBpcyB1bmRlZmluZWRcIikuIFRoYXQgdGhyb3cgYWJvcnRlZCB0aGUgY2FsbGVyXG4gICAgLy8gKGRlYWN0aXZhdGVBY3RpdmVUb29sIOKGkiBvblNlbGVjdFBsb3RUeXBlKSBiZWZvcmUgdGhlIHJlLXBsb3QgcmFuLCB3ZWRnaW5nIHRoZSBwbG90IHR5cGVcbiAgICAvLyAoZS5nLiBzdHVjayBzd2l0Y2hpbmcgbmFwYXJpIHZvbHVtZSDihpQgaXNvc3VyZmFjZSkuIEd1YXJkIG9uIGBfZnVsbExheW91dGAgbGlrZSB0aGUgem9vbVxuICAgIC8vIGhlbHBlcnMgZG8gc28gaXQgc2FmZWx5IG5vLW9wcyB3aGVuIFBsb3RseSBpc24ndCB0aGUgYWN0aXZlIHJlbmRlcmVyLlxuICAgIGNvbnN0IGdkID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2KSBhcyB1bmtub3duIGFzIHsgX2Z1bGxMYXlvdXQ/OiB1bmtub3duIH0gfCBudWxsO1xuICAgIGlmICghZ2Q/Ll9mdWxsTGF5b3V0KSByZXR1cm47XG4gICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgeyBkcmFnbW9kZTogbW9kZSB9IGFzIGFueSk7XG4gIH1cblxuICAvKioge0BsaW5rIElEYXRhUmVuZGVyZXJ9IHN0dWIg4oCUIFBsb3RseSBoYXMgbm8gb3ZlcnZpZXcgbmF2aWdhdG9yIChpdCdzIGFuXG4gICAqICBPcGVuU2VhZHJhZ29uIGZlYXR1cmUpLCBzbyB0b2dnbGluZyBpdCBpcyBhIG5vLW9wIGhlcmUuICovXG4gIHB1YmxpYyBzZXROYXZpZ2F0b3JWaXNpYmxlKF92aXNpYmxlOiBib29sZWFuKTogdm9pZCB7XG4gICAgLyogbm8tb3A6IG5hdmlnYXRvciBpcyBPcGVuU2VhZHJhZ29uLW9ubHkgKi9cbiAgfVxuXG4gIC8qKiB7QGxpbmsgSURhdGFSZW5kZXJlcn0gc3R1YiDigJQgaW1hZ2Ugc21vb3RoaW5nIGlzIGFuIE9wZW5TZWFkcmFnb24gY2FudmFzLWRyYXdlclxuICAgKiAgc2V0dGluZzsgdGhlIFBsb3RseSBpbWFnZS9oZWF0bWFwIHRyYWNlcyBkb24ndCBleHBvc2UgaXQsIHNvIGl0J3MgYSBuby1vcC4gKi9cbiAgcHVibGljIHNldEltYWdlU21vb3RoaW5nRW5hYmxlZChfZW5hYmxlZDogYm9vbGVhbik6IHZvaWQge1xuICAgIC8qIG5vLW9wOiBzbW9vdGhpbmcgdG9nZ2xlIGlzIE9wZW5TZWFkcmFnb24tb25seSAqL1xuICB9XG5cbiAgcHVibGljIHpvb21JbigpIHtcbiAgICBpZiAoIXRoaXMucGxvdERpdikgcmV0dXJuO1xuICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgIGlmICghZ2Q/Ll9mdWxsTGF5b3V0KSByZXR1cm47XG4gICAgY29uc3QgeGwgPSBnZC5fZnVsbExheW91dC54YXhpcztcbiAgICBjb25zdCB5bCA9IGdkLl9mdWxsTGF5b3V0LnlheGlzO1xuICAgIC8vIDNEIHNjZW5lcyBoYXZlIG5vIDJEIHhheGlzL3lheGlzICh0aGV5IGxpdmUgdW5kZXIgYHNjZW5lYCk7IHRoZSBzdGVwLXpvb21cbiAgICAvLyBpcyBtZWFuaW5nbGVzcyB0aGVyZSBhbmQgd291bGQgdGhyb3cgb24gYHhsLnJhbmdlYC4gUGxvdGx5J3Mgc2NlbmUgaGFuZGxlc1xuICAgIC8vIHNjcm9sbC16b29tIG5hdGl2ZWx5LCBzbyBqdXN0IGJhaWwuXG4gICAgaWYgKCF4bD8ucmFuZ2UgfHwgIXlsPy5yYW5nZSkgcmV0dXJuO1xuICAgIGNvbnN0IHhjID0gKHhsLnJhbmdlWzBdICsgeGwucmFuZ2VbMV0pIC8gMjtcbiAgICBjb25zdCB5YyA9ICh5bC5yYW5nZVswXSArIHlsLnJhbmdlWzFdKSAvIDI7XG4gICAgY29uc3QgZHggPSAoeGwucmFuZ2VbMV0gLSB4bC5yYW5nZVswXSkgLyAxLjM7XG4gICAgY29uc3QgZHkgPSAoeWwucmFuZ2VbMV0gLSB5bC5yYW5nZVswXSkgLyAxLjM7XG4gICAgY29uc3QgeDAgPSB4YyAtIGR4IC8gMiwgeDEgPSB4YyArIGR4IC8gMjtcbiAgICBjb25zdCB5MCA9IHljIC0gZHkgLyAyLCB5MSA9IHljICsgZHkgLyAyO1xuICAgIHRoaXMuem9vbUNvb3JkaW5hdGVzID0gW3gwLCB4MSwgeTAsIHkxXTtcbiAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7XG4gICAgICAneGF4aXMucmFuZ2VbMF0nOiB4MCwgJ3hheGlzLnJhbmdlWzFdJzogeDEsXG4gICAgICAneWF4aXMucmFuZ2VbMF0nOiB5MCwgJ3lheGlzLnJhbmdlWzFdJzogeTFcbiAgICB9IGFzIGFueSk7XG4gIH1cblxuICAvKipcbiAgICogRGVsZXRlIGV2ZXJ5IGN1cnJlbnRseSBzZWxlY3RlZCBzaGFwZS4gRmFsbHMgYmFjayB0byB0aGUgc2luZ2xlXG4gICAqIGBfYWN0aXZlU2hhcGVJbmRleGAgd2hlbiBubyBtdWx0aS1zZWxlY3Rpb24gaXMgYWN0aXZlIChjb3ZlcnMgdGhlIGNhc2VcbiAgICogd2hlcmUgUGxvdGx5IGNsaWNrZWQgYSBzaGFwZSB3aXRob3V0IGdvaW5nIHRocm91Z2ggdGhlIHRhYmxlKS5cbiAgICovXG4gIHB1YmxpYyBkZWxldGVBY3RpdmVTaGFwZSgpIHtcbiAgICAvLyBOb3RlOiBubyBgcGxvdERpdmAgZ3VhcmQg4oCUIHdoZW4gT3BlblNlYWRyYWdvbiBpcyB0aGUgcmVuZGVyZXIsIFBsb3RseVxuICAgIC8vIG5ldmVyIHBsb3R0ZWQgKHBsb3REaXYgaXMgdW5zZXQpLCBidXQgZGVsZXRpb24gb25seSBuZWVkcyB0aGUgc2hhcGUgbGlzdFxuICAgIC8vICsgdGhlIGN1cnJlbnQgc2VsZWN0aW9uOyB0aGUgUGxvdGx5IHJlbGF5b3V0IGJlbG93IGlzIGd1YXJkZWQuXG4gICAgY29uc3QgZ2Q6IGFueSA9IHRoaXMucGxvdERpdiA/IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdikgOiBudWxsO1xuXG4gICAgLy8gUmVzb2x2ZSB3aGF0IHRvIGRlbGV0ZTogdGhlIHN0b3JlJ3Mgc2VsZWN0aW9uLCBlbHNlIHRoZSBzaGFwZSBQbG90bHlcbiAgICAvLyB0cmFja3MgYXMgY2xpY2tlZCAod2hlbiB0aGUgdXNlciBjbGlja2VkIGEgc2hhcGUgd2l0aG91dCBnb2luZyB0aHJvdWdoIHRoZVxuICAgIC8vIFJlZ2lvbiBFZGl0b3IgdGFibGUpLlxuICAgIGlmICh0aGlzLnJlZ2lvblN0b3JlLmdldFNlbGVjdGVkU2hhcGVJbmRpY2VzKCkubGVuZ3RoID09PSAwKSB7XG4gICAgICBjb25zdCBhY3RpdmVJbmRleDogbnVtYmVyID0gZ2Q/Ll9mdWxsTGF5b3V0Py5fYWN0aXZlU2hhcGVJbmRleDtcbiAgICAgIGlmIChhY3RpdmVJbmRleCA9PT0gdW5kZWZpbmVkIHx8IGFjdGl2ZUluZGV4IDwgMCkgcmV0dXJuO1xuICAgICAgdGhpcy5yZWdpb25TdG9yZS5zZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyhbYWN0aXZlSW5kZXhdKTtcbiAgICB9XG4gICAgLy8gVGhlIHN0b3JlIHJlbW92ZXMgdGhlIHNlbGVjdGVkIHJlZ2lvbnMsIGNsZWFycyB0aGUgc2VsZWN0aW9uLCBzeW5jcyBpdHNcbiAgICAvLyBjYWNoZSBhbmQgZW1pdHMgdGhlIHJlZ2lvbi11cGRhdGUgZXZlbnQuXG4gICAgdGhpcy5yZWdpb25TdG9yZS5kZWxldGVBY3RpdmVTaGFwZSgpO1xuXG4gICAgLy8gUmUtcHJvamVjdCB0aGUgcmVtYWluaW5nIHJlZ2lvbnMgYW5kIHJlbmRlciBvbiB0aGUgUGxvdGx5IHBsb3QgKGEgbm8tb3AsXG4gICAgLy8gY2F1Z2h0LCB3aGVuIGFub3RoZXIgYmFja2VuZCBvd25zIHRoZSBkaXYg4oCUIGl0cyBvdmVybGF5IHJlLXJlbmRlcnMgdmlhXG4gICAgLy8gdGhlIHN0b3JlJ3MgdXBkYXRlIGV2ZW50KS5cbiAgICBpZiAoZ2Q/Ll9mdWxsTGF5b3V0KSBnZC5fZnVsbExheW91dC5fYWN0aXZlU2hhcGVJbmRleCA9IC0xO1xuICAgIHRoaXMuc3luY1NoYXBlc0Zyb21TdG9yZSgpO1xuICAgIGNvbnN0IGRpY3RBcnJheSA9IHRoaXMuc2hhcGVzLm1hcChzID0+ICh7IC4uLnMgfSkpO1xuICAgIHRyeSB7XG4gICAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7IHNoYXBlczogZGljdEFycmF5IH0gYXMgYW55KS50aGVuKCgpID0+IHtcbiAgICAgICAgaWYgKGdkPy5fZnVsbExheW91dCkgKFBsb3RseSBhcyBhbnkpLnJlZHJhdyhnZCk7XG4gICAgICB9LCAoKSA9PiB7IC8qIGRpdiBub3QgYSBQbG90bHkgcGxvdCAqLyB9KTtcbiAgICB9IGNhdGNoIHsgLyogZGl2IG93bmVkIGJ5IGFub3RoZXIgYmFja2VuZCAqLyB9XG4gIH1cblxuICBwdWJsaWMgem9vbU91dCgpIHtcbiAgICBpZiAoIXRoaXMucGxvdERpdikgcmV0dXJuO1xuICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgIGlmICghZ2Q/Ll9mdWxsTGF5b3V0KSByZXR1cm47XG4gICAgY29uc3QgeGwgPSBnZC5fZnVsbExheW91dC54YXhpcztcbiAgICBjb25zdCB5bCA9IGdkLl9mdWxsTGF5b3V0LnlheGlzO1xuICAgIC8vIDNEIHNjZW5lcyBoYXZlIG5vIDJEIHhheGlzL3lheGlzIOKAlCBzZWUgem9vbUluKCkuXG4gICAgaWYgKCF4bD8ucmFuZ2UgfHwgIXlsPy5yYW5nZSkgcmV0dXJuO1xuICAgIGNvbnN0IHhjID0gKHhsLnJhbmdlWzBdICsgeGwucmFuZ2VbMV0pIC8gMjtcbiAgICBjb25zdCB5YyA9ICh5bC5yYW5nZVswXSArIHlsLnJhbmdlWzFdKSAvIDI7XG4gICAgY29uc3QgZHggPSAoeGwucmFuZ2VbMV0gLSB4bC5yYW5nZVswXSkgKiAxLjM7XG4gICAgY29uc3QgZHkgPSAoeWwucmFuZ2VbMV0gLSB5bC5yYW5nZVswXSkgKiAxLjM7XG4gICAgY29uc3QgeDAgPSB4YyAtIGR4IC8gMiwgeDEgPSB4YyArIGR4IC8gMjtcbiAgICBjb25zdCB5MCA9IHljIC0gZHkgLyAyLCB5MSA9IHljICsgZHkgLyAyO1xuICAgIHRoaXMuem9vbUNvb3JkaW5hdGVzID0gW3gwLCB4MSwgeTAsIHkxXTtcbiAgICBQbG90bHkucmVsYXlvdXQodGhpcy5wbG90RGl2LCB7XG4gICAgICAneGF4aXMucmFuZ2VbMF0nOiB4MCwgJ3hheGlzLnJhbmdlWzFdJzogeDEsXG4gICAgICAneWF4aXMucmFuZ2VbMF0nOiB5MCwgJ3lheGlzLnJhbmdlWzFdJzogeTFcbiAgICB9IGFzIGFueSk7XG4gIH1cblxuICBwdWJsaWMgaXNTdGFja0xvYWRpbmckKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIHJldHVybiB0aGlzLnN0YWNrTG9hZGluZyQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cbiAgcHVibGljIHNldFN0YWNrTG9hZGluZyhzdGFja0xvYWRpbmc6IGJvb2xlYW4pIHtcbiAgICB0aGlzLnN0YWNrTG9hZGluZyQubmV4dChzdGFja0xvYWRpbmcpO1xuICB9XG4gIHB1YmxpYyBnZXRTdGFja0xvYWRpbmdQcm9ncmVzcyQoKTogT2JzZXJ2YWJsZTxudW1iZXI+IHtcbiAgICByZXR1cm4gdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICAvLyBDb2xvcm1hcCAvIHJldmVyc2Utc2NhbGUgLyBpbWFnZSBtZXRhZGF0YSBzdGF0ZSBsaXZlcyBpbiB0aGUgc2hhcmVkXG4gIC8vIFZpc3VhbGl6ZXJTdG9yZTsgdGhlc2UgZGVsZWdhdGUsIGtlZXBpbmcgb25seSB0aGUgUGxvdGx5LXNwZWNpZmljIGxpdmVcbiAgLy8gcmVzdHlsZSBhcyByZW5kZXIgZ2x1ZS5cbiAgZ2V0Q29sb3JtYXBPcHRpb25zKCkge1xuICAgIHJldHVybiB0aGlzLnN0b3JlLmdldENvbG9ybWFwT3B0aW9ucygpO1xuICB9XG4gIHNldENvbG9ybWFwKGNvbG9ybWFwOiBhbnkpIHtcbiAgICB0aGlzLnN0b3JlLnNldENvbG9ybWFwKGNvbG9ybWFwKTtcbiAgICB0cnkge1xuICAgICAgUGxvdGx5LnJlc3R5bGUodGhpcy5wbG90RGl2LCB7ICdjb2xvcnNjYWxlJzogW2NvbG9ybWFwLmRhdGEudmFsdWVdIH0pO1xuICAgIH0gY2F0Y2ggeyAvKiBkaXYgb3duZWQgYnkgYW5vdGhlciBiYWNrZW5kIChPU0QpIOKAlCBpdCByZWNvbG9ycyB2aWEgdGhlIExVVCAqLyB9XG4gIH1cbiAgZ2V0Q29sb3JtYXAoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcmUuZ2V0Q29sb3JtYXAoKTtcbiAgfVxuXG4gIHNldFJldmVyc2VTY2FsZShyZXZlcnNjYWxlOiBhbnkpIHtcbiAgICB0aGlzLnN0b3JlLnNldFJldmVyc2VTY2FsZShyZXZlcnNjYWxlKTtcbiAgICB0cnkge1xuICAgICAgUGxvdGx5LnJlc3R5bGUodGhpcy5wbG90RGl2LCB7ICdyZXZlcnNlc2NhbGUnOiByZXZlcnNjYWxlIH0pO1xuICAgIH0gY2F0Y2ggeyAvKiBkaXYgb3duZWQgYnkgYW5vdGhlciBiYWNrZW5kIChPU0QpIOKAlCBpdCByZWNvbG9ycyB2aWEgdGhlIExVVCAqLyB9XG4gIH1cblxuICBnZXRSZXZlcnNlU2NhbGUoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcmUuZ2V0UmV2ZXJzZVNjYWxlKCk7XG4gIH1cblxuICBzZXRJbWFnZU1ldGEoaW1hZ2VNZXRhOiBJSW1hZ2VNZXRhZGF0YVtdKSB7XG4gICAgdGhpcy5zdG9yZS5zZXRJbWFnZU1ldGEoaW1hZ2VNZXRhKTtcbiAgfVxuXG4gIGdldEltYWdlTWV0YSgpIHtcbiAgICByZXR1cm4gdGhpcy5zdG9yZS5nZXRJbWFnZU1ldGEoKTtcbiAgfVxuXG4gIHNldFNob3dTdGFjayhzaG93c3RhY2s6IGJvb2xlYW4pIHtcbiAgICBpZiAoIXNob3dzdGFjaykge1xuICAgICAgdGhpcy56SW5kZXgubmV4dCgwKTtcbiAgICB9XG4gICAgdGhpcy5pbWFnZUluZm8uc2hvd1N0YWNrID0gc2hvd3N0YWNrO1xuICAgIHRoaXMuc3RhY2tMb2FkaW5nJC5uZXh0KHNob3dzdGFjayk7XG4gICAgUGxvdGx5LnJlbGF5b3V0KHRoaXMucGxvdERpdiwgeyAnc2hvd3N0YWNrJzogc2hvd3N0YWNrIH0gYXMgYW55KTtcbiAgfVxuXG4gIGdldEF1dG9zY2FsZUV2ZW50KCkge1xuICAgIHJldHVybiB0aGlzLmF1dG9zY2FsZUV2ZW50LmFzT2JzZXJ2YWJsZSgpO1xuICB9XG5cbiAgZ2V0UmVnaW9uVXBkYXRlRXZlbnQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9uVXBkYXRlRXZlbnQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdHJlYW0gb2YgdGhlIGluZGljZXMgb2YgY3VycmVudGx5IHNlbGVjdGVkIHJlZ2lvbnMg4oCUIG93bmVkIGJ5IHRoZSBzaGFyZWRcbiAgICogUmVnaW9uU3RvcmUgc28gdGhlIHNlbGVjdGlvbiBpcyBjb25zaXN0ZW50IGFjcm9zcyBiYWNrZW5kcyBhbmQgdGhlIFJlZ2lvblxuICAgKiBFZGl0b3IgdGFibGUuIEVtcHR5ID0gbm90aGluZyBzZWxlY3RlZC5cbiAgICovXG4gIGdldFNlbGVjdGVkU2hhcGVJbmRpY2VzJCgpOiBPYnNlcnZhYmxlPG51bWJlcltdPiB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2VsZWN0ZWRTaGFwZUluZGljZXMkKCk7XG4gIH1cblxuICAvKipcbiAgICogUHJvZ3JhbW1hdGljYWxseSBzZWxlY3QgcmVnaW9ucyAob3IgY2xlYXIgd2l0aCBbXSkuIFRoZSBzaGFyZWQgc3RvcmUgb3duc1xuICAgKiB0aGUgc2VsZWN0aW9uIHN0YXRlICh2YWxpZGF0ZXMvZGVkdXBlcy9lbWl0cyk7IGhlcmUgd2UgYWRkaXRpb25hbGx5IHBvaW50XG4gICAqIFBsb3RseSdzIGBfYWN0aXZlU2hhcGVJbmRleGAgYXQgdGhlIGxhc3Qgc2VsZWN0ZWQgc2hhcGUgc28gaXQgZ2V0cyB0aGUgZWRpdFxuICAgKiBoYW5kbGVzLiBObyB2aXN1YWwgY2hhbmdlIHRvIG5vbi1hY3RpdmUgc2hhcGVzIOKAlCBQbG90bHkncyBvd24gYWN0aXZlLXNoYXBlXG4gICAqIHJlbmRlcmluZyBpcyB0aGUgb25seSBoaWdobGlnaHQuXG4gICAqL1xuICBwdWJsaWMgc2V0U2VsZWN0ZWRTaGFwZUluZGljZXMoaW5kaWNlczogbnVtYmVyW10pIHtcbiAgICBjb25zdCB2YWxpZCA9IChpbmRpY2VzIHx8IFtdKVxuICAgICAgLmZpbHRlcihpID0+IE51bWJlci5pc0Zpbml0ZShpKSAmJiBpID49IDAgJiYgaSA8IHRoaXMuc2hhcGVzLmxlbmd0aCk7XG4gICAgY29uc3Qgc2VlbiA9IG5ldyBTZXQ8bnVtYmVyPigpO1xuICAgIGNvbnN0IGNsZWFuZWQ6IG51bWJlcltdID0gW107XG4gICAgZm9yIChjb25zdCBpIG9mIHZhbGlkKSB7XG4gICAgICBpZiAoIXNlZW4uaGFzKGkpKSB7IHNlZW4uYWRkKGkpOyBjbGVhbmVkLnB1c2goaSk7IH1cbiAgICB9XG4gICAgaWYgKHRoaXMucGxvdERpdikge1xuICAgICAgY29uc3QgZ2Q6IGFueSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdik7XG4gICAgICBpZiAoZ2Q/Ll9mdWxsTGF5b3V0KSB7XG4gICAgICAgIGdkLl9mdWxsTGF5b3V0Ll9hY3RpdmVTaGFwZUluZGV4ID0gY2xlYW5lZC5sZW5ndGggPiAwID8gY2xlYW5lZFtjbGVhbmVkLmxlbmd0aCAtIDFdIDogLTE7XG4gICAgICAgIHRyeSB7IChQbG90bHkgYXMgYW55KS5yZWRyYXcoZ2QpOyB9IGNhdGNoIHsgLyogbm9vcCBpbiB0ZXN0cyAqLyB9XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMucmVnaW9uU3RvcmUuc2V0U2VsZWN0ZWRTaGFwZUluZGljZXMoY2xlYW5lZCk7XG4gIH1cblxuICAvKipcbiAgICogU2VsZWN0IGEgc3BlY2lmaWMgcmVnaW9uIChJUmVnaW9uU3RvcmUuc2VsZWN0UmVnaW9uKS4gU2V0cyB0aGUgc2hhcmVkIHN0b3JlXG4gICAqIHNlbGVjdGlvbiBieSBpZGVudGl0eSBhbmQgcG9pbnRzIFBsb3RseSdzIGFjdGl2ZS1zaGFwZSBhdCB0aGUgbWF0Y2hpbmdcbiAgICogcmVuZGVyZWQgc2hhcGUgc28gaXRzIGVkaXQgaGFuZGxlcyBhcHBlYXIuXG4gICAqL1xuICBwdWJsaWMgc2VsZWN0UmVnaW9uKHJlZ2lvbjogUmVnaW9uKTogdm9pZCB7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5zZWxlY3RSZWdpb24ocmVnaW9uKTtcbiAgICBpZiAodGhpcy5wbG90RGl2KSB7XG4gICAgICBjb25zdCBpZHggPSB0aGlzLnNoYXBlcy5maW5kSW5kZXgocyA9PiBzLmlkID09PSByZWdpb24/LmlkKTtcbiAgICAgIGNvbnN0IGdkOiBhbnkgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpO1xuICAgICAgaWYgKGdkPy5fZnVsbExheW91dCAmJiBpZHggPj0gMCkge1xuICAgICAgICBnZC5fZnVsbExheW91dC5fYWN0aXZlU2hhcGVJbmRleCA9IGlkeDtcbiAgICAgICAgdHJ5IHsgKFBsb3RseSBhcyBhbnkpLnJlZHJhdyhnZCk7IH0gY2F0Y2ggeyAvKiBub29wIGluIHRlc3RzICovIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVhZCBQbG90bHkncyBgX2FjdGl2ZVNoYXBlSW5kZXhgIGFuZCBwdXNoIGl0IGFzIHRoZSBzZWxlY3Rpb24uIENhbGxlZCBmcm9tXG4gICAqIHRoZSByZWxheW91dCBoYW5kbGVyIGFuZCB0aGUgcG9zdC1jbGljayB0aWNrIOKAlCBQbG90bHkgZG9lc24ndCBmaXJlIGFcbiAgICogZGVkaWNhdGVkIGFjdGl2ZS1zaGFwZSBldmVudCBmb3IgY2xpY2tzLCBzbyB3ZSBzYW1wbGUuIFRoZSBzdG9yZSBuby1vcHMgaWZcbiAgICogdGhlIHNlbGVjdGlvbiBpcyB1bmNoYW5nZWQuXG4gICAqL1xuICBwcml2YXRlIG1heWJlRW1pdEFjdGl2ZVNoYXBlSW5kZXgoKSB7XG4gICAgaWYgKCF0aGlzLnBsb3REaXYpIHJldHVybjtcbiAgICBjb25zdCBnZDogYW55ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2KTtcbiAgICBjb25zdCByYXcgPSBnZD8uX2Z1bGxMYXlvdXQ/Ll9hY3RpdmVTaGFwZUluZGV4O1xuICAgIGNvbnN0IGlkeCA9ICh0eXBlb2YgcmF3ID09PSAnbnVtYmVyJyAmJiByYXcgPj0gMCAmJiByYXcgPCB0aGlzLnNoYXBlcy5sZW5ndGgpID8gcmF3IDogLTE7XG4gICAgdGhpcy5yZWdpb25TdG9yZS5zZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyhpZHggPj0gMCA/IFtpZHhdIDogW10pO1xuICB9XG4gIHNldFpJbmRleCh6SW5kZXg6IG51bWJlcikge1xuICAgIHRoaXMuekluZGV4Lm5leHQoekluZGV4KTtcbiAgfVxuXG4gIGdldFNoYXBlQ29sb3IoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVnaW9uU3RvcmUuZ2V0U2hhcGVDb2xvcigpO1xuICB9XG4gIGdldEZpbGxDb2xvcigpIHtcbiAgICByZXR1cm4gdGhpcy5yZWdpb25TdG9yZS5nZXRGaWxsQ29sb3IoKTtcbiAgfVxuXG4gIGdldENsYXNzaWZpY2F0aW9uQ29sb3JzKCk6IE1hcDxzdHJpbmcsIHN0cmluZz4ge1xuICAgIHJldHVybiB0aGlzLnN0b3JlLmdldENsYXNzaWZpY2F0aW9uQ29sb3JzKCk7XG4gIH1cblxuICBzZXRDbGFzc2lmaWNhdGlvbkNvbG9yKGxhYmVsOiBzdHJpbmcsIGNvbG9yOiBzdHJpbmcpIHtcbiAgICB0aGlzLnN0b3JlLnNldENsYXNzaWZpY2F0aW9uQ29sb3IobGFiZWwsIGNvbG9yKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVbnN1YnNjcmliZSBTdWJzY3JpcHRpb25zXG4gICAqL1xuICB1bnN1YnNjcmliZSgpIHtcbiAgICBpZiAodGhpcy5pbWFnZUNhY2hlZFN1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5pbWFnZUNhY2hlZFN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5maWxlbmFtZVN1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5maWxlbmFtZVN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5yZWdpb25VcGRhdGVTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMucmVnaW9uVXBkYXRlU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLnJlZ2lvbkxpdmVFZGl0U3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLnJlZ2lvbkxpdmVFZGl0U3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmNoYW5uZWxTdWIpIHtcbiAgICAgIHRoaXMuY2hhbm5lbFN1Yi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBMaXZlLWFwcGx5IHRoZSBjaGFubmVsIGRpc3BsYXkgd2luZG93ICh6bWluL3ptYXgpICsgcmV2ZXJzZS9pbnZlcnQgdG8gdGhlXG4gICAqICBoZWF0bWFwLiBOby1vcCB3aGVuIG5vIFBsb3RseSBwbG90IGlzIG1vdW50ZWQgKE9TRCBvd25zIHRoZSBkaXYpLiAqL1xuICBwcml2YXRlIGFwcGx5Q2hhbm5lbERpc3BsYXkoY2hhbm5lbHM6IGFueVtdLCByZXY6IGJvb2xlYW4sIGludjogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghdGhpcy5wbG90RGl2KSByZXR1cm47XG4gICAgY29uc3QgY2ggPSBjaGFubmVscz8uWzBdO1xuICAgIGNvbnN0IGNvbG9yc2NhbGUgPSB0aGlzLnN0b3JlLmN1cnJlbnRDb2xvcm1hcCgpPy5kYXRhPy52YWx1ZTtcbiAgICAvLyByZXZlcnNlLXNjYWxlIGFuZCBpbnZlcnQgZWFjaCBmbGlwIHRoZSByYW1wOyBib3RoIHRvZ2V0aGVyIGNhbmNlbC5cbiAgICBjb25zdCB1cGRhdGU6IGFueSA9IHsgcmV2ZXJzZXNjYWxlOiByZXYgIT09IGludiB9O1xuICAgIGlmIChjb2xvcnNjYWxlICE9IG51bGwpIHVwZGF0ZS5jb2xvcnNjYWxlID0gY29sb3JzY2FsZTsgLy8gbGl2ZSBjb2xvcm1hcCByZWNvbG9yXG4gICAgaWYgKGNoKSB7XG4gICAgICAvLyBTdXJmYWNlL2lzb3N1cmZhY2Uvc2NhdHRlcjNkIGNvbG91ci1tYXAgdmlhIGNtaW4vY21heDsgaGVhdG1hcC9jb250b3VyIHZpYSB6bWluL3ptYXguXG4gICAgICBjb25zdCB0aHJlZUQgPVxuICAgICAgICB0aGlzLnBsb3RUeXBlID09PSBQbG90VHlwZS5JU09TVVJGQUNFIHx8XG4gICAgICAgIHRoaXMucGxvdFR5cGUgPT09IFBsb3RUeXBlLlNVUkZBQ0UgfHxcbiAgICAgICAgdGhpcy5wbG90VHlwZSA9PT0gUGxvdFR5cGUuU0NBVFRFUjNEO1xuICAgICAgaWYgKHRocmVlRCkge1xuICAgICAgICB1cGRhdGUuY21pbiA9IGNoLm1pbjtcbiAgICAgICAgdXBkYXRlLmNtYXggPSBjaC5tYXg7XG4gICAgICAgIHVwZGF0ZS5jYXV0byA9IGZhbHNlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdXBkYXRlLnptaW4gPSBjaC5taW47XG4gICAgICAgIHVwZGF0ZS56bWF4ID0gY2gubWF4O1xuICAgICAgICB1cGRhdGUuemF1dG8gPSBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIFBsb3RseS5yZXN0eWxlKHRoaXMucGxvdERpdiwgdXBkYXRlKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8qIG5vdCBhIGNvbG91ci1tYXBwZWQgdHJhY2UsIG9yIE9TRCBvd25zIHRoZSBkaXYgKi9cbiAgICB9XG4gIH1cblxuICAvKiogQmlubmVkIGludGVuc2l0eSBoaXN0b2dyYW0gZm9yIGEgY2hhbm5lbCBmcm9tIHRoZSBjYWNoZWQgc291cmNlIGZyYW1lc1xuICAgKiAgKHJhdywgcHJlLUxVVCkuIEdyYXlzY2FsZSBjZWxscyBhcmUgbnVtYmVyczsgUkdCIGNlbGxzIGFyZSBbcixnLGJdLiAqL1xuICBnZXRIaXN0b2dyYW0oY2hhbm5lbEluZGV4OiBudW1iZXIsIF9iaW5zOiBudW1iZXIpOiBJSGlzdG9ncmFtIHwgbnVsbCB7XG4gICAgY29uc3QgZnJhbWVzID0gdGhpcy5jYWNoZWRJbWFnZUZyYW1lcztcbiAgICBpZiAoIWZyYW1lcz8ubGVuZ3RoKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCBmcmFtZSA9IGZyYW1lc1t0aGlzLmFjdGl2ZUZyYW1lSW5kZXgoKV0gPz8gZnJhbWVzWzBdO1xuICAgIGlmICghZnJhbWU/Lmxlbmd0aCkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgY291bnRzID0gbmV3IEFycmF5KDI1NikuZmlsbCgwKTtcbiAgICBmb3IgKGNvbnN0IHJvdyBvZiBmcmFtZSkge1xuICAgICAgaWYgKCFyb3cpIGNvbnRpbnVlO1xuICAgICAgZm9yIChjb25zdCBjZWxsIG9mIHJvdykge1xuICAgICAgICBsZXQgdjogbnVtYmVyO1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShjZWxsKSkge1xuICAgICAgICAgIHYgPSBjaGFubmVsSW5kZXggPj0gMCAmJiBjaGFubmVsSW5kZXggPCBjZWxsLmxlbmd0aFxuICAgICAgICAgICAgPyBjZWxsW2NoYW5uZWxJbmRleF1cbiAgICAgICAgICAgIDogTWF0aC5yb3VuZChidDYwMUx1bWluYW5jZShjZWxsWzBdLCBjZWxsWzFdLCBjZWxsWzJdKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdiA9IGNlbGw7XG4gICAgICAgIH1cbiAgICAgICAgdiA9IHYgfCAwO1xuICAgICAgICBpZiAodiA8IDApIHYgPSAwOyBlbHNlIGlmICh2ID4gMjU1KSB2ID0gMjU1O1xuICAgICAgICBjb3VudHNbdl0rKztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGhpc3RvZ3JhbTI1Nihjb3VudHMpO1xuICB9XG5cbiAgLyoqIEFzeW5jIGhpc3RvZ3JhbSBzdHJlYW0g4oCUIFBsb3RseSByZW5kZXJzIGhlYXRtYXAgZnJhbWUgZGF0YSAoYWxyZWFkeSBpblxuICAgKiAgbWVtb3J5LCA4LWJpdCBsdW1pbmFuY2UpLCBzbyB0aGlzIGp1c3Qgd3JhcHMgdGhlIHN5bmNocm9ub3VzIDgtYml0XG4gICAqICBoaXN0b2dyYW0uIE5hdGl2ZSAxNi1iaXQgaGlzdG9ncmFtcyBvbmx5IGFwcGx5IHRvIHRoZSBPU0QgdGlsZSBwYXRoLiAqL1xuICBnZXRIaXN0b2dyYW0kKGNoYW5uZWxJbmRleDogbnVtYmVyLCBiaW5zOiBudW1iZXIpOiBPYnNlcnZhYmxlPElIaXN0b2dyYW0gfCBudWxsPiB7XG4gICAgcmV0dXJuIG9mKHRoaXMuZ2V0SGlzdG9ncmFtKGNoYW5uZWxJbmRleCwgYmlucykpO1xuICB9XG5cbiAgLyoqIERhdGEgZXhwb3J0ICgxNi1iaXQgVElGRikgaXMgYW4gT1NELXRpbGUtcGF0aCBmZWF0dXJlIGJhY2tlZCBieSB0aGUgc2VydmVyLlxuICAgKiAgUGxvdGx5IHJlbmRlcnMgaGVhdG1hcHMgZnJvbSBmcmFtZSBkYXRhLCBzbyB0aGVyZSdzIG5vdGhpbmcgdG8gZXhwb3J0IGhlcmUuICovXG4gIGV4cG9ydERhdGEoKTogdm9pZCB7XG4gICAgY29uc29sZS53YXJuKCdbcGxvdGx5XSAxNi1iaXQgZGF0YSBleHBvcnQgaXMgbm90IGF2YWlsYWJsZSBmb3IgdGhlIGhlYXRtYXAgYmFja2VuZC4nKTtcbiAgfVxuXG4gIGltcG9ydFJlZ2lvbnMoZ2VvSnNvblN0cjogc3RyaW5nKTogUmVnaW9uW10ge1xuICAgIHJldHVybiB0aGlzLnBsb3RVdGlsaXRpZXMuaW1wb3J0Uk9Jc0Zyb21HZW9Kc29uKGdlb0pzb25TdHIpO1xuICB9XG5cbiAgZXhwb3J0UmVnaW9ucyhyZWdpb25zOiBSZWdpb25bXSkge1xuICAgIGNvbnN0IGpzb25TdHJpbmcgPSB0aGlzLnBsb3RVdGlsaXRpZXMuZXhwb3J0Uk9Jc1RvR2VvSnNvbihyZWdpb25zKTtcbiAgICB0aGlzLnBsb3RVdGlsaXRpZXMuc2F2ZVRvRmlsZShqc29uU3RyaW5nLCB0aGlzLmZpbGVOYW1lKTtcbiAgfVxuXG4gIGdldEdlb0pzb25TdHJpbmcocmVnaW9uczogUmVnaW9uW10pOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLnBsb3RVdGlsaXRpZXMuZXhwb3J0Uk9Jc1RvR2VvSnNvbihyZWdpb25zKTtcbiAgfVxuXG4gIGdldFN0YWNrTG9hZGluZ1Byb2dyZXNzKCkge1xuICAgIHJldHVybiB0aGlzLmdldFN0YWNrTG9hZGluZ1Byb2dyZXNzJCgpO1xuICB9XG5cbiAgaXNTdGFja0xvYWRpbmcoKSB7XG4gICAgcmV0dXJuIHRoaXMuaXNTdGFja0xvYWRpbmckKCk7XG4gIH1cblxufVxuIl19
|