@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,2283 @@
|
|
|
1
|
+
import { Inject, Injectable, Optional } from '@angular/core';
|
|
2
|
+
import { BehaviorSubject, Subject, combineLatest, from, of } from 'rxjs';
|
|
3
|
+
import { saveAs } from 'file-saver';
|
|
4
|
+
import { Viewer, histogramScalar, colormapFromLut, tintColormap, reverseColormap, heightField, MultiChannelImageView, MultiChannelVolumeView, } from 'napari-js';
|
|
5
|
+
import { buildColormapLut } from '../../contracts/colormap-lut';
|
|
6
|
+
import { PlotType, PLOT_TYPE_DESCRIPTORS, isNapari3d, isNapariIsosurface, isNapariSurface, isNapariScatter, isNapariScatter3d, NAPARI_DEFAULT_DECIMATE, } from '../../contracts/plot-type';
|
|
7
|
+
import { ViewerFeature, capabilitiesOf, } from '../../contracts/capabilities.contract';
|
|
8
|
+
import { VIZ_CONFIG } from '../../contracts/viz-config';
|
|
9
|
+
import { TILE_ACCESS_PORT } from '../../contracts/ports/tile-access.port';
|
|
10
|
+
import { BaseStoreVisualizer } from '../base-store-visualizer';
|
|
11
|
+
import { SimpleSliceAccessService } from '../simple-slice-access.service';
|
|
12
|
+
import { VisualizerStore } from '../../store/visualizer-store.service';
|
|
13
|
+
import { RegionStore } from '../../store/region-store.service';
|
|
14
|
+
import { NapariScaleBar, formatUm } from './napari-scale-bar';
|
|
15
|
+
import { NapariRegionOverlay } from './napari-region-overlay';
|
|
16
|
+
import { NapariAxesLabels } from './napari-axes-labels';
|
|
17
|
+
import { NapariVolumeZHandle } from './napari-volume-z-handle';
|
|
18
|
+
import { WandToolService } from '../../toolbar/wand/wand-tool.service';
|
|
19
|
+
import { BrushToolService } from '../../toolbar/brush/brush-tool.service';
|
|
20
|
+
import { VertexEraserToolService, } from '../../toolbar/vertex-eraser/vertex-eraser-tool.service';
|
|
21
|
+
import { ZoomToBoxToolService, } from '../../toolbar/zoom-to-box/zoom-to-box-tool.service';
|
|
22
|
+
import { SamToolService } from '../../toolbar/segmentation/sam-tool.service';
|
|
23
|
+
import { SamPointToolService } from '../../toolbar/segmentation/sam-point-tool.service';
|
|
24
|
+
import { CellSegmentToolService } from '../../toolbar/segmentation/cell-segment-tool.service';
|
|
25
|
+
import { CELL_SEGMENTER } from '../../contracts/cell-segmenter.contract';
|
|
26
|
+
import * as i0 from "@angular/core";
|
|
27
|
+
import * as i1 from "../../store/visualizer-store.service";
|
|
28
|
+
import * as i2 from "../../store/region-store.service";
|
|
29
|
+
import * as i3 from "../../toolbar/wand/wand-tool.service";
|
|
30
|
+
import * as i4 from "../../toolbar/brush/brush-tool.service";
|
|
31
|
+
import * as i5 from "../../toolbar/vertex-eraser/vertex-eraser-tool.service";
|
|
32
|
+
import * as i6 from "../../toolbar/zoom-to-box/zoom-to-box-tool.service";
|
|
33
|
+
import * as i7 from "../../toolbar/segmentation/sam-tool.service";
|
|
34
|
+
import * as i8 from "../../toolbar/segmentation/sam-point-tool.service";
|
|
35
|
+
import * as i9 from "../../toolbar/segmentation/cell-segment-tool.service";
|
|
36
|
+
import * as i10 from "../simple-slice-access.service";
|
|
37
|
+
/** ICoordinateTransform over the napari camera: pointer client coords ↔ image/world coords. */
|
|
38
|
+
class NapariCoordinateTransform {
|
|
39
|
+
constructor(viewer, ready) {
|
|
40
|
+
this.viewer = viewer;
|
|
41
|
+
this.ready = ready;
|
|
42
|
+
}
|
|
43
|
+
clientToData(clientX, clientY) {
|
|
44
|
+
const [x, y] = this.viewer.canvasToWorld(clientX, clientY);
|
|
45
|
+
return { x, y };
|
|
46
|
+
}
|
|
47
|
+
dataLengthToScreen(dataLength) {
|
|
48
|
+
return dataLength * this.viewer.camera.zoom; // CSS px per world unit
|
|
49
|
+
}
|
|
50
|
+
isReady() {
|
|
51
|
+
return this.ready();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const VOLUME_MAX_SLICE = 1024; // "Full" in-plane cap; the default ¼ load uses 256
|
|
55
|
+
/** Reference in-plane world size (long side) for the volume/axes box, in arbitrary world units.
|
|
56
|
+
* The box is anchored to this reference regardless of the chosen decimate factor, so changing the
|
|
57
|
+
* resolution changes DETAIL, not the volume's proportions (Z no longer appears to shrink when the
|
|
58
|
+
* in-plane sampling grows). Set to the DEFAULT decimate's in-plane cap so the default view is
|
|
59
|
+
* unchanged; higher/lower resolutions keep that same shape. See {@link mountVolume}. */
|
|
60
|
+
const VOLUME_WORLD_INPLANE_REF = VOLUME_MAX_SLICE / NAPARI_DEFAULT_DECIMATE;
|
|
61
|
+
/** Max concurrent slice fetches when assembling a volume — keeps the connection pool busy
|
|
62
|
+
* without flooding it (browsers cap ~6/host) on a deep stack. */
|
|
63
|
+
const VOLUME_FETCH_CONCURRENCY = 8;
|
|
64
|
+
/** Volume/isosurface sampling for a decimate factor `scale` (1 = Full 1024, 2 = ½ 512, 4 = ¼ 256
|
|
65
|
+
* default, 8 = ⅛ 128): the in-plane cap halves each step; slices stay un-subsampled until ⅛, then
|
|
66
|
+
* subsample. */
|
|
67
|
+
function volumeResolutionFor(scale) {
|
|
68
|
+
const s = Math.max(1, Math.round(scale));
|
|
69
|
+
return {
|
|
70
|
+
maxSlice: Math.max(8, Math.round(VOLUME_MAX_SLICE / s)),
|
|
71
|
+
sliceStep: Math.max(1, Math.floor(s / 4)),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Fully-normalized intensity height as a fraction of the in-plane extent — matches the Plotly
|
|
75
|
+
* SURFACE z-aspect (~0.4) so the relief isn't exaggerated. */
|
|
76
|
+
const SURFACE_Z_ASPECT = 0.4;
|
|
77
|
+
/** "Full" mesh grid cap; the decimate factor divides it (default ¼ → 220, ½ → 440, Full → 880). */
|
|
78
|
+
const SURFACE_MAX_GRID = 880;
|
|
79
|
+
/** Coefficient scaling the pyramid tile budget with the target resolution — a higher target pulls a
|
|
80
|
+
* finer pyramid level (more real detail). Shared by the surface plane fetch + volume assembly. */
|
|
81
|
+
const STITCH_BUDGET_COEFF = 16;
|
|
82
|
+
/** Surface mesh target grid for a decimate factor `scale`: the grid cap shrinks by `scale` (every
|
|
83
|
+
* slice is kept — the z-slider needs them all). The source is fetched at a matching resolution. */
|
|
84
|
+
function surfaceResolutionFor(scale) {
|
|
85
|
+
const s = Math.max(1, Math.round(scale));
|
|
86
|
+
return { maxGrid: Math.max(16, Math.round(SURFACE_MAX_GRID / s)) };
|
|
87
|
+
}
|
|
88
|
+
/** 3D scatter: in-plane cap for the assembled voxel grid, and the max number of points emitted
|
|
89
|
+
* (the grid is flat-strided down to this) — keeps the billboard count interactive. */
|
|
90
|
+
const SCATTER3D_MAX_XY = 64;
|
|
91
|
+
const SCATTER3D_MAX_POINTS = 150000;
|
|
92
|
+
const TILE_SIZE = 512; // server tile edge (matches the OSD backend)
|
|
93
|
+
/** Max tiles stitched for one displayed slice (512px tiles → up to ~6144² at full res). Beyond
|
|
94
|
+
* this we step to a coarser pyramid level so a large image stays tractable. */
|
|
95
|
+
const MAX_STITCH_TILES = 144;
|
|
96
|
+
/** WebGPU default `maxTextureDimension2D` — a stitched slice's longest side must fit a texture. */
|
|
97
|
+
const MAX_TEXTURE_DIM = 8192;
|
|
98
|
+
/** Concurrent tile requests per stitched slice. Firing a whole grid at once (hundreds of requests
|
|
99
|
+
* for a large level) overwhelmed the tile server (504s); a small pool keeps the pipe full without
|
|
100
|
+
* flooding it — and slice-level workers already run several stitches in parallel on top of this. */
|
|
101
|
+
const TILE_FETCH_CONCURRENCY = 6;
|
|
102
|
+
/** How long to poll `/tiles/info` (202 while the server caches the source) before falling back to
|
|
103
|
+
* a single tile. Generous like the OSD backend — a cold whole-slide can take minutes to cache. */
|
|
104
|
+
const DESCRIPTOR_TIMEOUT_MS = 120000;
|
|
105
|
+
/** Default per-channel tints (Fiji-style) when the store/descriptor offers no colour. */
|
|
106
|
+
const DEFAULT_TINTS = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff', '#ffffff'];
|
|
107
|
+
/** Default tint for a channel index, cycling the Fiji palette. */
|
|
108
|
+
function tintFor(channel) {
|
|
109
|
+
return DEFAULT_TINTS[channel % DEFAULT_TINTS.length];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A WebGPU image backend built on the published `napari-js` library — the POC engine for
|
|
113
|
+
* jit-ui#102 (a browser-based napari shipped as a JS library, swapping image plotting with
|
|
114
|
+
* OpenSeadragon and 3D slicing/isosurfaces with Plotly).
|
|
115
|
+
*
|
|
116
|
+
* Render strategy: rather than re-implement the server's tile/pyramid protocol (which OSD
|
|
117
|
+
* already handles), this backend renders the **complete per-slice image URLs the app already
|
|
118
|
+
* produces** (`IImageInfo.urls`) — `urls[z]` for the 2D image, and the full `urls` stack
|
|
119
|
+
* assembled into a downsampled volume for the 3D types. Region state and display options
|
|
120
|
+
* delegate to the shared {@link RegionStore} / {@link VisualizerStore}, exactly as OSD does.
|
|
121
|
+
*
|
|
122
|
+
* Follow-ups (jit-ui#102): native-resolution pyramidal tiling, on-canvas tools, region
|
|
123
|
+
* overlay rendering, per-channel histograms, TIFF export.
|
|
124
|
+
*/
|
|
125
|
+
export class NapariVisualizerService extends BaseStoreVisualizer {
|
|
126
|
+
constructor(tiles, store, regionStore, wandTool, brushTool, eraserTool, zoomToBoxTool, samTool, samPointTool, cellSegmentTool, cellSegmenter, simpleStack, config) {
|
|
127
|
+
super(regionStore, store);
|
|
128
|
+
this.tiles = tiles;
|
|
129
|
+
this.wandTool = wandTool;
|
|
130
|
+
this.brushTool = brushTool;
|
|
131
|
+
this.eraserTool = eraserTool;
|
|
132
|
+
this.zoomToBoxTool = zoomToBoxTool;
|
|
133
|
+
this.samTool = samTool;
|
|
134
|
+
this.samPointTool = samPointTool;
|
|
135
|
+
this.cellSegmentTool = cellSegmentTool;
|
|
136
|
+
this.cellSegmenter = cellSegmenter;
|
|
137
|
+
this.simpleStack = simpleStack;
|
|
138
|
+
this.capabilities = capabilitiesOf([
|
|
139
|
+
ViewerFeature.ImageDisplay,
|
|
140
|
+
ViewerFeature.StackSlider,
|
|
141
|
+
ViewerFeature.PixelReadback,
|
|
142
|
+
ViewerFeature.Surface3D,
|
|
143
|
+
ViewerFeature.Isosurface,
|
|
144
|
+
]);
|
|
145
|
+
this.viewer = null;
|
|
146
|
+
this.canvas = null;
|
|
147
|
+
this.host = null;
|
|
148
|
+
this.loaded = null;
|
|
149
|
+
this.lastPixels = null;
|
|
150
|
+
this.currentPlotType = PlotType.NAPARI_IMAGE;
|
|
151
|
+
/** napari-js high-level view owning the 3D volume layers (one additive tinted layer per channel
|
|
152
|
+
* for multichannel, or a single grayscale volume). Null in 2D. */
|
|
153
|
+
this.volumeView = null;
|
|
154
|
+
/** True when the volume is composited from per-channel layers (vs a single grayscale volume). */
|
|
155
|
+
this.volumeMultichannel = false;
|
|
156
|
+
/** napari-js height-field surface mesh (NAPARI_SURFACE plot type; null otherwise). Built from a
|
|
157
|
+
* single grayscale slice by {@link buildSurface} via napari-js's `heightField` + `addSurface`. */
|
|
158
|
+
this.surfaceLayer = null;
|
|
159
|
+
/** Which band the surface samples (a channel index for multichannel, else the composite). */
|
|
160
|
+
this.surfaceChannel = undefined;
|
|
161
|
+
/** Pre-loaded per-slice luminance planes (already decimated to the surface grid), keyed by z,
|
|
162
|
+
* so the stack slider rebuilds the surface instantly. Filled by {@link preloadSurfacePlanes}. */
|
|
163
|
+
this.surfacePlanes = new Map();
|
|
164
|
+
/** In-plane grid cap for the active surface load (full grid ÷ the decimate factor). */
|
|
165
|
+
this.surfaceMaxGrid = SURFACE_MAX_GRID;
|
|
166
|
+
/** Contrast window [min,max] the current surface mesh was built with. A change reshapes the
|
|
167
|
+
* mesh (pixel height = intensity within [min,max]), so it triggers a geometry rebuild. */
|
|
168
|
+
this.surfaceWindow = null;
|
|
169
|
+
/** Persisted wireframe choice for the surface (re-applied when a new surface mounts). */
|
|
170
|
+
this.surfaceWireframe = false;
|
|
171
|
+
/** napari-js 2D scatter points (region centroids) + its region-change subscription. */
|
|
172
|
+
this.scatter2dPoints = null;
|
|
173
|
+
this.scatterRegionSub = null;
|
|
174
|
+
/** napari-js 3D scatter (voxel point cloud). */
|
|
175
|
+
this.scatter3dLayer = null;
|
|
176
|
+
/** Monotonic load generation. Bumped by {@link reset} and {@link cancelLoading}; the frame-loading
|
|
177
|
+
* loops (volume assembly, surface preload) capture it and bail when it changes, so a Cancel (or a
|
|
178
|
+
* new plot) actually stops fetching frames instead of running to completion in the background. */
|
|
179
|
+
this.loadToken = 0;
|
|
180
|
+
/** Decimate factor for the napari 3D types (1 = Full, 2 = ½ default, 4 = ¼, 8 = ⅛). Applied when a
|
|
181
|
+
* volume/isosurface/surface (re)loads; changing it needs a re-plot (it changes fetched data). */
|
|
182
|
+
this.resolutionScale = NAPARI_DEFAULT_DECIMATE;
|
|
183
|
+
/** 3D coordinate-axes / scale gizmo for the volume/isosurface view (null in 2D). */
|
|
184
|
+
this.axesLayer = null;
|
|
185
|
+
/** DOM X/Y/Z + scale labels tracking the 3D axes gizmo (null in 2D). */
|
|
186
|
+
this.axesLabels = null;
|
|
187
|
+
/** Draggable Z-height grip over the volume (null unless a volume/isosurface is mounted). */
|
|
188
|
+
this.zHandle = null;
|
|
189
|
+
/** User Z-height factor for the volume (1 = the volume's natural proportions); driven by the
|
|
190
|
+
* in-view drag handle. Reset per mount. */
|
|
191
|
+
this.volumeZScale = 1;
|
|
192
|
+
/** Volume world box at `volumeZScale = 1` (base) + the sampled voxel depth — enough to recompute
|
|
193
|
+
* the Z-axis voxel scale, axes depth, and overlay anchors as the handle stretches Z. */
|
|
194
|
+
this.volumeWorldBase = null;
|
|
195
|
+
this.volumeSampledDepth = 1;
|
|
196
|
+
/** Persisted axes on/off choice, re-applied when a new volume mounts. Defaults on. */
|
|
197
|
+
this.axesVisible = true;
|
|
198
|
+
this.volumeDims = null;
|
|
199
|
+
/** Assembled uint8 volume data per channel (key = channel index), kept for the volume intensity
|
|
200
|
+
* histogram. Key 0 holds the grayscale/composite volume in the single-channel case. */
|
|
201
|
+
this.volumeChannelData = new Map();
|
|
202
|
+
this.imageW = 0;
|
|
203
|
+
this.imageH = 0;
|
|
204
|
+
/** Monotonic slice-request id so a slow out-of-order slice fetch can't clobber a newer one. */
|
|
205
|
+
this.sliceReq = 0;
|
|
206
|
+
/** Cached `/tiles/info` pyramid descriptor + the infoB64 it was fetched for. */
|
|
207
|
+
this.descriptor = null;
|
|
208
|
+
this.descriptorKey = null;
|
|
209
|
+
/** How the current 2D image is composited (drives histogram + state application). */
|
|
210
|
+
this.imageMode = 'rgb';
|
|
211
|
+
/** napari-js high-level view that owns the per-channel layer set (build + live display
|
|
212
|
+
* updates) for the current {@link imageMode}. Rebuilt on each (re)render. */
|
|
213
|
+
this.channelView = null;
|
|
214
|
+
/** Live subscription applying channel-state / colormap changes to the layers. */
|
|
215
|
+
this.displaySub = null;
|
|
216
|
+
/** Latest grayscale colormap selection from the store (applied to the single gray layer). */
|
|
217
|
+
this.currentColormap = null;
|
|
218
|
+
this.currentReverse = false;
|
|
219
|
+
/** Latest invert toggle from the store (flips intensity before the colormap, like OSD). */
|
|
220
|
+
this.invertEnabled = false;
|
|
221
|
+
/** Physical scale bar overlay for the 2D image (null when 3D or the image has no µm/pixel). */
|
|
222
|
+
this.scaleBar = null;
|
|
223
|
+
/** SVG region-drawing overlay for the 2D image (null until a 2D image is plotted). */
|
|
224
|
+
this.regionOverlay = null;
|
|
225
|
+
/** Pixel-tool plumbing: the plot div id, coord transform, and bound tool hosts. */
|
|
226
|
+
this.plotDivId = '';
|
|
227
|
+
this.coordTransform = null;
|
|
228
|
+
this.wandHost = null;
|
|
229
|
+
this.eraserHost = null;
|
|
230
|
+
/** Cached CachedImageData built from the last readback (rebuilt when lastPixels changes). */
|
|
231
|
+
this.cachedImage = null;
|
|
232
|
+
this.cachedImageSource = null;
|
|
233
|
+
/** Visible world rect captured AT the last readback — must pair with `lastPixels` so the pixel
|
|
234
|
+
* tools' ratios/origin match the matrix's camera (using the live rect after a pan/zoom would
|
|
235
|
+
* mis-scale the region). */
|
|
236
|
+
this.lastPixelsRect = null;
|
|
237
|
+
/** Image smoothing (bilinear) vs nearest-neighbour (crisp pixels, the default). */
|
|
238
|
+
this.imageSmoothing = false;
|
|
239
|
+
/** True when the 2D image is rendered via pyramidal TiledSources (descriptor available). */
|
|
240
|
+
this.tiled = false;
|
|
241
|
+
/** Debounced readback timer + camera-change unsubscribe (keep lastPixels current for tools). */
|
|
242
|
+
this.readbackTimer = null;
|
|
243
|
+
this.cameraReadbackOff = null;
|
|
244
|
+
/** Coarse per-channel luminance sample (keyed by channel index) for the histogram in tiled mode,
|
|
245
|
+
* where the layers have no full in-memory pixels. Refreshed on plot + slice change. */
|
|
246
|
+
this.histSamples = new Map();
|
|
247
|
+
/** Native-bit-depth histograms from `/histogram`, keyed `${z}|${channel}` (>8-bit images). */
|
|
248
|
+
this.nativeHistograms = new Map();
|
|
249
|
+
this.stackLoading$ = new BehaviorSubject(false);
|
|
250
|
+
this.stackLoadingProgress$ = new BehaviorSubject(0);
|
|
251
|
+
this.autoscaleEvent$ = new Subject();
|
|
252
|
+
this.intensityProfile$ = new Subject();
|
|
253
|
+
this.viewportChange$ = new Subject();
|
|
254
|
+
this.api = config.slideCropServer;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Fetch + cache the server pyramid descriptor (`GET /tiles/info`): the REAL per-level tile grid,
|
|
258
|
+
* tile size, channel metadata and physical pixel size. The backend returns 202 while the source
|
|
259
|
+
* is still caching, so we poll briefly. Cached per `infoB64`; returns null if it never becomes
|
|
260
|
+
* ready (callers fall back to a single-tile fetch). This is the authoritative grid — guessing
|
|
261
|
+
* level dims from `trueImageSize` overshoots the real grid and the server 400s out-of-range tiles.
|
|
262
|
+
*/
|
|
263
|
+
async ensureDescriptor() {
|
|
264
|
+
// Self-contained multi-slice stack (no tile server — e.g. a numbered image
|
|
265
|
+
// series assembled client-side, each slice a different file): there is no
|
|
266
|
+
// single server-tiled pyramid to describe. Returning null routes every
|
|
267
|
+
// caller (2D image, volume, surface) to their stitched/single-fetch
|
|
268
|
+
// fallback, which — via fetchSlice's own SimpleSliceAccessService branch —
|
|
269
|
+
// correctly fetches each slice's own URL instead of one fixed file's tile
|
|
270
|
+
// pyramid.
|
|
271
|
+
if (this.simpleStack.isSimple(this.loaded?.imageInfo))
|
|
272
|
+
return null;
|
|
273
|
+
const infoB64 = this.tiles.getSelectedInfoB64();
|
|
274
|
+
if (!infoB64)
|
|
275
|
+
return null;
|
|
276
|
+
if (this.descriptor && this.descriptorKey === infoB64)
|
|
277
|
+
return this.descriptor;
|
|
278
|
+
const headers = await this.tiles
|
|
279
|
+
.getAuthHeaders()
|
|
280
|
+
.catch(() => ({}));
|
|
281
|
+
const url = `${this.api}tiles/info?info=${infoB64}`;
|
|
282
|
+
const deadline = Date.now() + DESCRIPTOR_TIMEOUT_MS;
|
|
283
|
+
for (;;) {
|
|
284
|
+
try {
|
|
285
|
+
const resp = await fetch(url, { headers });
|
|
286
|
+
if (resp.status === 200) {
|
|
287
|
+
const body = (await resp.json());
|
|
288
|
+
if (body?.levels?.length) {
|
|
289
|
+
this.descriptor = body;
|
|
290
|
+
this.descriptorKey = infoB64;
|
|
291
|
+
return body;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// 202 (still caching) or empty body → re-poll until the deadline.
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
console.warn('[napari-js] tiles/info poll retry', err);
|
|
298
|
+
}
|
|
299
|
+
if (Date.now() > deadline) {
|
|
300
|
+
console.warn('[napari-js] tiles/info not ready; falling back to single-tile fetch');
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
await new Promise((r) => setTimeout(r, 1200));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Fetch a COMPLETE rendered slice as an `ImageBitmap` by stitching the server's REAL tile grid
|
|
308
|
+
* (from `/tiles/info`) — not just the top-left tile, which only ever showed a large image's
|
|
309
|
+
* corner. Picks the finest pyramid level whose grid fits `budgetTiles` and whose longest side is
|
|
310
|
+
* within the GPU texture limit, fetches that grid concurrently, and stitches it into one canvas.
|
|
311
|
+
*
|
|
312
|
+
* `channel` selects a single band as grayscale (real levels only); omit for the server composite.
|
|
313
|
+
* `budgetTiles` caps the grid: the 2D view uses the full budget for detail; the 3D volume passes
|
|
314
|
+
* 1 for a cheap overview tile per slice (it downsamples to {@link VOLUME_MAX_SLICE}). Falls back
|
|
315
|
+
* to a single `col=0,row=0` tile when no descriptor is available (small/simple images, volumes).
|
|
316
|
+
*/
|
|
317
|
+
async fetchSlice(z, channel, budgetTiles = MAX_STITCH_TILES,
|
|
318
|
+
/** When a specific channel has no pyramid level within `budgetTiles`, drop to the server
|
|
319
|
+
* COMPOSITE overview instead of stitching the huge per-channel level. Only safe when the caller
|
|
320
|
+
* wants a single decimated plane (the surface height): for a multichannel VOLUME every channel
|
|
321
|
+
* would then fetch the same composite and the channels would collapse into one, so volume
|
|
322
|
+
* assembly leaves this `false` to keep each band distinct. */
|
|
323
|
+
allowCompositeFallback = false) {
|
|
324
|
+
// Self-contained multi-slice stack (tiled:false): `z` indexes a completely
|
|
325
|
+
// different file's own preview URL, not an internal slice of one server-
|
|
326
|
+
// tiled file — resolved and fetched via SimpleSliceAccessService (shared
|
|
327
|
+
// with OSD; see its docs for why this can't be a bare fetch()) instead of
|
|
328
|
+
// building a /tile?info=...&z= request against whichever single file
|
|
329
|
+
// getSelectedInfoB64() points at (which would 400/mismatch for any z
|
|
330
|
+
// beyond that one file's own extent).
|
|
331
|
+
const info = this.loaded?.imageInfo;
|
|
332
|
+
if (this.simpleStack.isSimple(info)) {
|
|
333
|
+
const url = this.simpleStack.urlFor(info, z);
|
|
334
|
+
if (!url)
|
|
335
|
+
throw new Error(`[napari-js] no URL for slice ${z}`);
|
|
336
|
+
return this.simpleStack.fetchAsBitmap(url);
|
|
337
|
+
}
|
|
338
|
+
const infoB64 = this.tiles.getSelectedInfoB64();
|
|
339
|
+
if (!infoB64)
|
|
340
|
+
throw new Error('[napari-js] no selected image info (getSelectedInfoB64 null)');
|
|
341
|
+
const headers = await this.tiles
|
|
342
|
+
.getAuthHeaders()
|
|
343
|
+
.catch(() => ({}));
|
|
344
|
+
const desc = await this.ensureDescriptor();
|
|
345
|
+
// The requested band; may be dropped to the composite (undefined) below when the channel has no
|
|
346
|
+
// pyramid level small enough to stitch within budget.
|
|
347
|
+
let effectiveChannel = channel;
|
|
348
|
+
const fetchTile = async (res, col, row, t) => {
|
|
349
|
+
const ch = effectiveChannel == null ? '' : `&channel=${effectiveChannel}`;
|
|
350
|
+
const url = `${this.api}tile?info=${infoB64}&res=${res}&col=${col}&row=${row}&z=${z}&tileSize=${t}${ch}`;
|
|
351
|
+
const resp = await fetch(url, { headers });
|
|
352
|
+
if (!resp.ok) {
|
|
353
|
+
throw new Error(`[napari-js] slice fetch failed: ${resp.status} (${col}/${row} res ${res})`);
|
|
354
|
+
}
|
|
355
|
+
return createImageBitmap(await resp.blob());
|
|
356
|
+
};
|
|
357
|
+
// No descriptor → single top-left tile (legacy fallback; correct for small/simple images).
|
|
358
|
+
if (!desc || !desc.levels?.length)
|
|
359
|
+
return fetchTile(0, 0, 0, TILE_SIZE);
|
|
360
|
+
const t = desc.tileSize || TILE_SIZE;
|
|
361
|
+
// Per-channel tiles exist ONLY at REAL Bio-Formats levels (the front of `levels`); the server
|
|
362
|
+
// composite exists at every level, including the small overviews.
|
|
363
|
+
const perChannelLevels = desc.realLevels ?? desc.levels.length;
|
|
364
|
+
const usable = channel == null ? desc.levels : desc.levels.slice(0, Math.max(1, perChannelLevels));
|
|
365
|
+
// Finest level whose stitched grid fits BOTH the tile budget and the GPU texture limit; if none
|
|
366
|
+
// fits, the coarsest available (fits=false).
|
|
367
|
+
const tilesFor = (lvl) => Math.max(1, Math.ceil(lvl.width / t)) * Math.max(1, Math.ceil(lvl.height / t));
|
|
368
|
+
const pick = (levels) => {
|
|
369
|
+
let c = levels[0];
|
|
370
|
+
for (const lvl of levels) {
|
|
371
|
+
c = lvl;
|
|
372
|
+
if (tilesFor(lvl) <= budgetTiles && Math.max(lvl.width, lvl.height) <= MAX_TEXTURE_DIM) {
|
|
373
|
+
return { lvl, fits: true };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return { lvl: c, fits: false };
|
|
377
|
+
};
|
|
378
|
+
let sel = pick(usable);
|
|
379
|
+
// A specific channel only has tiles at the (few, large) real levels. When none of them fit the
|
|
380
|
+
// budget — e.g. the coarsest real level is still 14982×18670 → ~1100 full-res tiles — stitching
|
|
381
|
+
// it per slice floods the server (504s) and stalls the load. The composite pyramid has small
|
|
382
|
+
// overview levels, so fetch the composite instead and derive luminance from it. Every caller here
|
|
383
|
+
// (surface height, volume assembly, readback) downscales the plane anyway, so a composite-derived
|
|
384
|
+
// plane is the right trade for one that actually loads. Only kicks in when the channel can't fit.
|
|
385
|
+
if (allowCompositeFallback && !sel.fits && channel != null && desc.levels.length > perChannelLevels) {
|
|
386
|
+
const composite = pick(desc.levels);
|
|
387
|
+
if (composite.fits || tilesFor(composite.lvl) < tilesFor(sel.lvl)) {
|
|
388
|
+
effectiveChannel = undefined;
|
|
389
|
+
sel = composite;
|
|
390
|
+
console.warn(`[napari-js] channel ${channel} has no pyramid level within the ${budgetTiles}-tile ` +
|
|
391
|
+
`budget; using the composite overview (res ${sel.lvl.res}, ${sel.lvl.width}×` +
|
|
392
|
+
`${sel.lvl.height}) for this plane.`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const chosen = sel.lvl;
|
|
396
|
+
const cols = Math.max(1, Math.ceil(chosen.width / t));
|
|
397
|
+
const rows = Math.max(1, Math.ceil(chosen.height / t));
|
|
398
|
+
if (!sel.fits && budgetTiles === MAX_STITCH_TILES) {
|
|
399
|
+
console.warn(`[napari-js] full resolution exceeds the ${budgetTiles}-tile/${MAX_TEXTURE_DIM}px budget; ` +
|
|
400
|
+
`displaying overview level res ${chosen.res} (${chosen.width}×${chosen.height}).`);
|
|
401
|
+
}
|
|
402
|
+
if (cols === 1 && rows === 1)
|
|
403
|
+
return fetchTile(chosen.res, 0, 0, t);
|
|
404
|
+
// Stitch into one level-sized canvas. Fetch the grid with BOUNDED concurrency: firing every tile
|
|
405
|
+
// at once (a big grid = hundreds of requests) overwhelmed the tile server (504s). Edge tiles are
|
|
406
|
+
// narrower/shorter; drawImage places each at its grid offset so partial tiles line up.
|
|
407
|
+
const coords = [];
|
|
408
|
+
for (let row = 0; row < rows; row++) {
|
|
409
|
+
for (let col = 0; col < cols; col++)
|
|
410
|
+
coords.push({ col, row });
|
|
411
|
+
}
|
|
412
|
+
const tiles = [];
|
|
413
|
+
const stitchWorker = async () => {
|
|
414
|
+
for (;;) {
|
|
415
|
+
const job = coords.shift();
|
|
416
|
+
if (!job)
|
|
417
|
+
return;
|
|
418
|
+
const bmp = await fetchTile(chosen.res, job.col, job.row, t);
|
|
419
|
+
tiles.push({ col: job.col, row: job.row, bmp });
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
await Promise.all(Array.from({ length: Math.min(TILE_FETCH_CONCURRENCY, coords.length) }, () => stitchWorker()));
|
|
423
|
+
let canvas;
|
|
424
|
+
if (typeof OffscreenCanvas !== 'undefined') {
|
|
425
|
+
canvas = new OffscreenCanvas(chosen.width, chosen.height);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
canvas = document.createElement('canvas');
|
|
429
|
+
canvas.width = chosen.width;
|
|
430
|
+
canvas.height = chosen.height;
|
|
431
|
+
}
|
|
432
|
+
const ctx = canvas.getContext('2d');
|
|
433
|
+
if (!ctx)
|
|
434
|
+
throw new Error('[napari-js] slice stitch: 2D context unavailable');
|
|
435
|
+
for (const { col, row, bmp } of tiles) {
|
|
436
|
+
ctx.drawImage(bmp, col * t, row * t);
|
|
437
|
+
bmp.close?.();
|
|
438
|
+
}
|
|
439
|
+
// Safety net: if the chosen level still exceeds the GPU texture limit (e.g. a multichannel
|
|
440
|
+
// image whose coarsest REAL level is huge — overview levels are composite-only), downscale the
|
|
441
|
+
// stitched canvas to fit so the WebGPU texture upload can't fail.
|
|
442
|
+
const longest = Math.max(chosen.width, chosen.height);
|
|
443
|
+
if (longest > MAX_TEXTURE_DIM) {
|
|
444
|
+
const scale = MAX_TEXTURE_DIM / longest;
|
|
445
|
+
const outW = Math.max(1, Math.floor(chosen.width * scale));
|
|
446
|
+
const outH = Math.max(1, Math.floor(chosen.height * scale));
|
|
447
|
+
console.warn(`[napari-js] stitched level ${chosen.width}×${chosen.height} exceeds the ` +
|
|
448
|
+
`${MAX_TEXTURE_DIM}px texture limit; downscaling to ${outW}×${outH}.`);
|
|
449
|
+
let out;
|
|
450
|
+
if (typeof OffscreenCanvas !== 'undefined') {
|
|
451
|
+
out = new OffscreenCanvas(outW, outH);
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
out = document.createElement('canvas');
|
|
455
|
+
out.width = outW;
|
|
456
|
+
out.height = outH;
|
|
457
|
+
}
|
|
458
|
+
const octx = out.getContext('2d');
|
|
459
|
+
if (!octx)
|
|
460
|
+
throw new Error('[napari-js] slice downscale: 2D context unavailable');
|
|
461
|
+
octx.drawImage(canvas, 0, 0, outW, outH);
|
|
462
|
+
return createImageBitmap(out);
|
|
463
|
+
}
|
|
464
|
+
return createImageBitmap(canvas);
|
|
465
|
+
}
|
|
466
|
+
// ── IDataRenderer: load / render / viewport ───────────────────────────────
|
|
467
|
+
async load(imageInfo, zIndex) {
|
|
468
|
+
// Keeps SimpleSliceAccessService's blob cache in sync even when the user
|
|
469
|
+
// switches backends (e.g. OSD Image → napari Volume) on the same file —
|
|
470
|
+
// whichever backend loads a genuinely different file first evicts it.
|
|
471
|
+
this.simpleStack.noteActiveFile(imageInfo.fileName);
|
|
472
|
+
this.loaded = { imageInfo, z: zIndex, filename: imageInfo.fileName };
|
|
473
|
+
return this.loaded;
|
|
474
|
+
}
|
|
475
|
+
async plot(plotDiv, imageLoaded, imageInfo, screenHeight, plotType, _inPlace) {
|
|
476
|
+
const host = document.getElementById(plotDiv);
|
|
477
|
+
if (!host) {
|
|
478
|
+
console.error(`[napari-js] plot target #${plotDiv} not found`);
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
this.reset();
|
|
482
|
+
this.host = host;
|
|
483
|
+
this.plotDivId = plotDiv;
|
|
484
|
+
this.currentPlotType = plotType;
|
|
485
|
+
const canvas = document.createElement('canvas');
|
|
486
|
+
canvas.style.display = 'block';
|
|
487
|
+
canvas.style.width = '100%';
|
|
488
|
+
canvas.style.height = screenHeight ? `${screenHeight}px` : '100%';
|
|
489
|
+
host.appendChild(canvas);
|
|
490
|
+
this.canvas = canvas;
|
|
491
|
+
const info = imageLoaded?.imageInfo ?? imageInfo;
|
|
492
|
+
const z = imageLoaded?.z ?? 0;
|
|
493
|
+
try {
|
|
494
|
+
const viewer = new Viewer({ canvas, background: { r: 0.07, g: 0.07, b: 0.09, a: 1 } });
|
|
495
|
+
this.viewer = viewer;
|
|
496
|
+
await viewer.ready;
|
|
497
|
+
if (isNapariScatter(plotType)) {
|
|
498
|
+
await this.mountScatter(viewer, z);
|
|
499
|
+
}
|
|
500
|
+
else if (isNapariScatter3d(plotType)) {
|
|
501
|
+
await this.mountScatter3d(viewer, info);
|
|
502
|
+
}
|
|
503
|
+
else if (isNapariSurface(plotType)) {
|
|
504
|
+
await this.mountSurface(viewer);
|
|
505
|
+
}
|
|
506
|
+
else if (isNapari3d(plotType)) {
|
|
507
|
+
await this.mountVolume(viewer, info, plotType);
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
await this.renderImage(z);
|
|
511
|
+
this.fitCameraSoon();
|
|
512
|
+
this.subscribeDisplayState();
|
|
513
|
+
this.installScaleBar();
|
|
514
|
+
this.regionOverlay = new NapariRegionOverlay(host, viewer, this.regionStore);
|
|
515
|
+
this.buildToolHosts();
|
|
516
|
+
// Keep the pixel-tool readback (lastPixels) current as the view pans/zooms and tiled
|
|
517
|
+
// levels load, so wand/brush/SAM sample the actually-displayed image (jit-ui#102).
|
|
518
|
+
this.cameraReadbackOff = viewer.camera.changed.connect(() => this.armReadback());
|
|
519
|
+
}
|
|
520
|
+
this.scheduleReadback();
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
catch (err) {
|
|
524
|
+
console.error('[napari-js] plot failed:', err);
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// ── Channels: per-channel composite, LUT, native histograms (jit-ui#102) ──────────────────
|
|
529
|
+
/** The grayscale display colormap from the store selection (+reverse), defaulting to gray.
|
|
530
|
+
* Maps the jit-ui store colormap node to a napari `Colormap` via the library's LUT factory;
|
|
531
|
+
* the multichannel tint ramps are built inside {@link MultiChannelImageView} from each
|
|
532
|
+
* channel's hex colour. */
|
|
533
|
+
grayscaleColormap() {
|
|
534
|
+
const value = this.currentColormap?.data?.value;
|
|
535
|
+
const lut = value != null ? buildColormapLut(value, this.currentReverse) : null;
|
|
536
|
+
if (lut)
|
|
537
|
+
return colormapFromLut('gray-cmap', lut);
|
|
538
|
+
return this.currentReverse
|
|
539
|
+
? colormapFromLut('gray-rev', [[255, 255, 255], [0, 0, 0]])
|
|
540
|
+
: 'gray';
|
|
541
|
+
}
|
|
542
|
+
/** Fetch a stitched slice and read it back as a single-channel uint8 plane. `channel` selects a
|
|
543
|
+
* band (multichannel); omit it for the grayscale composite (all overview levels available, so a
|
|
544
|
+
* large image picks a fitting downscaled level rather than only the full-res real level). */
|
|
545
|
+
async fetchChannelData(z, channel, budgetTiles) {
|
|
546
|
+
const bmp = await this.fetchSlice(z, channel, budgetTiles);
|
|
547
|
+
return this.bitmapToLuminance(bmp);
|
|
548
|
+
}
|
|
549
|
+
/** Decode an `ImageBitmap` (server bands are grayscale, R=G=B) to a single-channel uint8 plane.
|
|
550
|
+
* `maxSide` caps the longest side (downscaling on the canvas draw) — used to keep pre-loaded
|
|
551
|
+
* surface slice planes small. */
|
|
552
|
+
bitmapToLuminance(bmp, maxSide) {
|
|
553
|
+
const scale = maxSide ? Math.min(1, maxSide / Math.max(bmp.width, bmp.height, 1)) : 1;
|
|
554
|
+
const w = Math.max(1, Math.round(bmp.width * scale));
|
|
555
|
+
const h = Math.max(1, Math.round(bmp.height * scale));
|
|
556
|
+
let canvas;
|
|
557
|
+
if (typeof OffscreenCanvas !== 'undefined') {
|
|
558
|
+
canvas = new OffscreenCanvas(w, h);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
canvas = document.createElement('canvas');
|
|
562
|
+
canvas.width = w;
|
|
563
|
+
canvas.height = h;
|
|
564
|
+
}
|
|
565
|
+
const ctx = canvas.getContext('2d');
|
|
566
|
+
if (!ctx)
|
|
567
|
+
throw new Error('[napari-js] channel readback: 2D context unavailable');
|
|
568
|
+
// Scale the WHOLE bitmap into the (possibly smaller) target canvas — drawing at natural size
|
|
569
|
+
// would crop to the top-left w×h corner when downscaling a large slice (maxSide < bmp size).
|
|
570
|
+
ctx.drawImage(bmp, 0, 0, w, h);
|
|
571
|
+
bmp.close?.();
|
|
572
|
+
const rgba = ctx.getImageData(0, 0, w, h).data;
|
|
573
|
+
const data = new Uint8Array(w * h);
|
|
574
|
+
for (let i = 0; i < w * h; i++)
|
|
575
|
+
data[i] = rgba[i * 4];
|
|
576
|
+
return { data, width: w, height: h };
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Render the 2D image for slice `z`. With a server pyramid descriptor we use a pyramidal
|
|
580
|
+
* {@link TiledSource} per layer so the view refines to higher resolution on zoom (like OSD) and
|
|
581
|
+
* sits naturally in full-resolution coordinates; without one we fall back to the single-level
|
|
582
|
+
* stitch. Three display modes either way: multichannel additive tint, grayscale colormap, RGB.
|
|
583
|
+
*/
|
|
584
|
+
async renderImage(z, token) {
|
|
585
|
+
const v = this.viewer;
|
|
586
|
+
if (!v)
|
|
587
|
+
return;
|
|
588
|
+
const desc = await this.ensureDescriptor();
|
|
589
|
+
if (desc && desc.levels?.length) {
|
|
590
|
+
if (token != null && token !== this.sliceReq)
|
|
591
|
+
return;
|
|
592
|
+
await this.renderImageTiled(z, desc);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
return this.renderImageStitched(z, token);
|
|
596
|
+
}
|
|
597
|
+
/** Single-level stitch fallback (no descriptor): the pre-tiling path. */
|
|
598
|
+
async renderImageStitched(z, token) {
|
|
599
|
+
const v = this.viewer;
|
|
600
|
+
if (!v)
|
|
601
|
+
return;
|
|
602
|
+
const desc = await this.ensureDescriptor();
|
|
603
|
+
const states = this.store.currentChannelStates();
|
|
604
|
+
const channelCount = desc?.channels ?? (states.length || 1);
|
|
605
|
+
const multichannel = !!desc?.multichannel && channelCount > 1;
|
|
606
|
+
// 1) Fetch all pixel data BEFORE touching the viewer, so a superseded scrub can bail without
|
|
607
|
+
// having torn down the visible layers (avoids flicker / out-of-order layer state).
|
|
608
|
+
let mode;
|
|
609
|
+
let planes = [];
|
|
610
|
+
let bitmap = null;
|
|
611
|
+
if (multichannel) {
|
|
612
|
+
mode = 'multichannel';
|
|
613
|
+
planes = await Promise.all(Array.from({ length: channelCount }, (_, c) => this.fetchChannelData(z, c)));
|
|
614
|
+
}
|
|
615
|
+
else if (channelCount === 1) {
|
|
616
|
+
mode = 'grayscale';
|
|
617
|
+
// Composite fetch (no channel) → all overview levels usable, so a large grayscale image
|
|
618
|
+
// selects a fitting downscaled level instead of the full-res real level (texture limit).
|
|
619
|
+
planes = [await this.fetchChannelData(z)];
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
mode = 'rgb';
|
|
623
|
+
bitmap = await this.fetchSlice(z);
|
|
624
|
+
}
|
|
625
|
+
// 2) Commit — unless a newer scrub superseded us or the viewer was torn down.
|
|
626
|
+
if ((token != null && token !== this.sliceReq) || this.viewer !== v) {
|
|
627
|
+
bitmap?.close?.();
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
// The displayed texture may be a downscaled pyramid level; scale the layer into FULL-RESOLUTION
|
|
631
|
+
// world coordinates (level-0 pixels) so the camera, readback and — critically — pre-saved
|
|
632
|
+
// regions (stored in full-res coords, e.g. ndpi) all line up regardless of which level is shown.
|
|
633
|
+
// This mirrors OSD, whose coordinate system is always level 0.
|
|
634
|
+
const texW = mode === 'rgb' ? bitmap.width : planes[0]?.width ?? 0;
|
|
635
|
+
const texH = mode === 'rgb' ? bitmap.height : planes[0]?.height ?? 0;
|
|
636
|
+
const fullW = desc?.width || texW || 1;
|
|
637
|
+
const fullH = desc?.height || texH || 1;
|
|
638
|
+
const scale = [texW ? fullW / texW : 1, texH ? fullH / texH : 1];
|
|
639
|
+
this.imageMode = mode;
|
|
640
|
+
const interpolation = this.imageSmoothing ? 'linear' : 'nearest';
|
|
641
|
+
this.channelView = new MultiChannelImageView(v);
|
|
642
|
+
if (mode === 'multichannel') {
|
|
643
|
+
const views = planes.map((d, c) => {
|
|
644
|
+
const st = states.find((s) => s.index === c);
|
|
645
|
+
const color = st?.color ?? desc?.channelInfo?.[c]?.color ?? tintFor(c);
|
|
646
|
+
return {
|
|
647
|
+
source: { kind: 'typed', width: d.width, height: d.height, channels: 1, dtype: 'uint8', data: d.data },
|
|
648
|
+
tint: color,
|
|
649
|
+
name: st?.name ?? `ch${c}`,
|
|
650
|
+
contrastLimits: [st?.min ?? 0, st?.max ?? 255],
|
|
651
|
+
gamma: st?.gamma ?? 1,
|
|
652
|
+
visible: st?.visible ?? true,
|
|
653
|
+
invert: this.invertEnabled,
|
|
654
|
+
scale,
|
|
655
|
+
};
|
|
656
|
+
});
|
|
657
|
+
this.channelView.render('multichannel', views, { interpolation });
|
|
658
|
+
}
|
|
659
|
+
else if (mode === 'grayscale') {
|
|
660
|
+
const d = planes[0];
|
|
661
|
+
const st = states[0];
|
|
662
|
+
this.channelView.render('grayscale', [
|
|
663
|
+
{
|
|
664
|
+
source: { kind: 'typed', width: d.width, height: d.height, channels: 1, dtype: 'uint8', data: d.data },
|
|
665
|
+
colormap: this.grayscaleColormap(),
|
|
666
|
+
contrastLimits: [st?.min ?? 0, st?.max ?? 255],
|
|
667
|
+
gamma: st?.gamma ?? 1,
|
|
668
|
+
invert: this.invertEnabled,
|
|
669
|
+
scale,
|
|
670
|
+
},
|
|
671
|
+
], { interpolation });
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
this.channelView.render('rgb', [{ source: bitmap, scale }], { interpolation });
|
|
675
|
+
}
|
|
676
|
+
this.imageW = fullW;
|
|
677
|
+
this.imageH = fullH;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Render the 2D image with pyramidal {@link TiledSource}s — the view refines to higher resolution
|
|
681
|
+
* as you zoom in (the visual fetches the level whose texels ≈ screen pixels) and sits in full-res
|
|
682
|
+
* coordinates so regions align. Same three modes as the stitch path. Per-channel layers use the
|
|
683
|
+
* REAL pyramid levels (per-channel tiles only exist there); the composite uses all levels.
|
|
684
|
+
*/
|
|
685
|
+
async renderImageTiled(z, desc) {
|
|
686
|
+
const v = this.viewer;
|
|
687
|
+
if (!v)
|
|
688
|
+
return;
|
|
689
|
+
const states = this.store.currentChannelStates();
|
|
690
|
+
const channelCount = desc.channels ?? (states.length || 1);
|
|
691
|
+
const multichannel = !!desc.multichannel && channelCount > 1;
|
|
692
|
+
const interpolation = this.imageSmoothing ? 'linear' : 'nearest';
|
|
693
|
+
this.tiled = true;
|
|
694
|
+
this.channelView = new MultiChannelImageView(v);
|
|
695
|
+
if (multichannel) {
|
|
696
|
+
this.imageMode = 'multichannel';
|
|
697
|
+
const views = [];
|
|
698
|
+
for (let c = 0; c < channelCount; c++) {
|
|
699
|
+
const st = states.find((s) => s.index === c);
|
|
700
|
+
const color = st?.color ?? desc.channelInfo?.[c]?.color ?? tintFor(c);
|
|
701
|
+
views.push({
|
|
702
|
+
source: this.buildTiledSource(desc, c, 1),
|
|
703
|
+
tint: color,
|
|
704
|
+
name: st?.name ?? `ch${c}`,
|
|
705
|
+
contrastLimits: [st?.min ?? 0, st?.max ?? 255],
|
|
706
|
+
gamma: st?.gamma ?? 1,
|
|
707
|
+
visible: st?.visible ?? true,
|
|
708
|
+
invert: this.invertEnabled,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
this.channelView.render('multichannel', views, { interpolation });
|
|
712
|
+
}
|
|
713
|
+
else if (channelCount === 1) {
|
|
714
|
+
this.imageMode = 'grayscale';
|
|
715
|
+
const st = states[0];
|
|
716
|
+
this.channelView.render('grayscale', [
|
|
717
|
+
{
|
|
718
|
+
source: this.buildTiledSource(desc, undefined, 1),
|
|
719
|
+
colormap: this.grayscaleColormap(),
|
|
720
|
+
contrastLimits: [st?.min ?? 0, st?.max ?? 255],
|
|
721
|
+
gamma: st?.gamma ?? 1,
|
|
722
|
+
invert: this.invertEnabled,
|
|
723
|
+
},
|
|
724
|
+
], { interpolation });
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
this.imageMode = 'rgb';
|
|
728
|
+
this.channelView.render('rgb', [{ source: this.buildTiledSource(desc, undefined, 4) }], {
|
|
729
|
+
interpolation,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
this.imageW = desc.width;
|
|
733
|
+
this.imageH = desc.height;
|
|
734
|
+
// Await on the initial render so getHistogram/autoContrast have data immediately; slice changes
|
|
735
|
+
// refresh fire-and-forget (the histogram pane retries).
|
|
736
|
+
await this.refreshHistogramSamples(z, desc);
|
|
737
|
+
}
|
|
738
|
+
/** Build a pyramidal TiledSource backed by the server `/tile` endpoint. `channel` selects a band
|
|
739
|
+
* (grayscale luminance, real levels only); omit it for the composite (RGBA, all levels). */
|
|
740
|
+
buildTiledSource(desc, channel, channels) {
|
|
741
|
+
const infoB64 = this.tiles.getSelectedInfoB64() ?? '';
|
|
742
|
+
// Per-channel tiles exist only at REAL Bio-Formats levels; the composite exists at all levels.
|
|
743
|
+
const usable = channel == null
|
|
744
|
+
? desc.levels
|
|
745
|
+
: desc.levels.slice(0, Math.max(1, desc.realLevels ?? desc.levels.length));
|
|
746
|
+
const levelScales = usable.map((l) => desc.width / Math.max(1, l.width)); // level-0 px per level px
|
|
747
|
+
const tileSize = desc.tileSize || TILE_SIZE;
|
|
748
|
+
const ch = channel == null ? '' : `&channel=${channel}`;
|
|
749
|
+
const api = this.api;
|
|
750
|
+
return {
|
|
751
|
+
kind: 'tiled',
|
|
752
|
+
width: desc.width,
|
|
753
|
+
height: desc.height,
|
|
754
|
+
tileSize,
|
|
755
|
+
levels: usable.length,
|
|
756
|
+
levelScales,
|
|
757
|
+
depth: Math.max(1, desc.z || 1),
|
|
758
|
+
channels,
|
|
759
|
+
dtype: 'uint8',
|
|
760
|
+
fetchTile: async (key) => {
|
|
761
|
+
const res = usable[key.level]?.res ?? key.level;
|
|
762
|
+
const url = `${api}tile?info=${infoB64}&res=${res}&col=${key.col}&row=${key.row}&z=${key.z}&tileSize=${tileSize}${ch}`;
|
|
763
|
+
const headers = await this.tiles
|
|
764
|
+
.getAuthHeaders()
|
|
765
|
+
.catch(() => ({}));
|
|
766
|
+
const resp = await fetch(url, { headers });
|
|
767
|
+
if (!resp.ok) {
|
|
768
|
+
throw new Error(`[napari-js] tile ${key.level}/${key.col}/${key.row} → ${resp.status}`);
|
|
769
|
+
}
|
|
770
|
+
const bmp = await createImageBitmap(await resp.blob());
|
|
771
|
+
if (channels === 4)
|
|
772
|
+
return { width: bmp.width, height: bmp.height, data: bmp };
|
|
773
|
+
return this.bitmapToLuminance(bmp);
|
|
774
|
+
},
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
/** (Re)fetch a coarse per-channel luminance sample for the histogram (tiled mode has no full
|
|
778
|
+
* in-memory pixels). One cheap overview tile per channel; cached by channel index. */
|
|
779
|
+
async refreshHistogramSamples(z, desc) {
|
|
780
|
+
this.histSamples.clear();
|
|
781
|
+
if (this.imageMode === 'rgb')
|
|
782
|
+
return; // RGB uses the displayed-pixel readback (rgbHistogram)
|
|
783
|
+
const channelCount = this.imageMode === 'multichannel' ? desc.channels ?? 1 : 1;
|
|
784
|
+
for (let c = 0; c < channelCount; c++) {
|
|
785
|
+
try {
|
|
786
|
+
const ch = this.imageMode === 'multichannel' ? c : undefined;
|
|
787
|
+
const d = await this.fetchChannelData(z, ch, 1); // budget 1 → coarsest single tile
|
|
788
|
+
this.histSamples.set(c, d.data);
|
|
789
|
+
}
|
|
790
|
+
catch {
|
|
791
|
+
/* leave this channel's sample unset */
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
/** Subscribe channel states + grayscale colormap → live-apply to the rendered layers (no
|
|
796
|
+
* re-fetch; only z changes re-fetch). Replaces any prior subscription. */
|
|
797
|
+
subscribeDisplayState() {
|
|
798
|
+
this.displaySub?.unsubscribe();
|
|
799
|
+
this.displaySub = combineLatest([
|
|
800
|
+
this.store.getChannelStates(),
|
|
801
|
+
this.store.getColormap(),
|
|
802
|
+
this.store.getReverseScale(),
|
|
803
|
+
this.store.getInvert(),
|
|
804
|
+
]).subscribe(([channels, colormap, reverse, invert]) => {
|
|
805
|
+
this.currentColormap = colormap ?? null;
|
|
806
|
+
this.currentReverse = reverse;
|
|
807
|
+
this.invertEnabled = invert;
|
|
808
|
+
this.applyDisplayState(channels);
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
/** Subscribe the store colormap (+reverse), invert, and channel state → the volume/isosurface
|
|
812
|
+
* transfer function: colour (channel tint or selected colormap), the intensity **window
|
|
813
|
+
* (min/max → contrastLimits)** and **gamma**, mirroring the grayscale image's display controls
|
|
814
|
+
* so the histogram pane drives the 3D render. Replaces any prior subscription. */
|
|
815
|
+
subscribeVolumeDisplayState() {
|
|
816
|
+
this.displaySub?.unsubscribe();
|
|
817
|
+
this.displaySub = combineLatest([
|
|
818
|
+
this.store.getColormap(),
|
|
819
|
+
this.store.getReverseScale(),
|
|
820
|
+
this.store.getInvert(),
|
|
821
|
+
this.store.getChannelStates(),
|
|
822
|
+
]).subscribe(([colormap, reverse, invert, channels]) => {
|
|
823
|
+
this.currentColormap = colormap ?? null;
|
|
824
|
+
this.currentReverse = reverse;
|
|
825
|
+
this.invertEnabled = invert;
|
|
826
|
+
const view = this.volumeView;
|
|
827
|
+
if (!view)
|
|
828
|
+
return;
|
|
829
|
+
if (this.volumeMultichannel) {
|
|
830
|
+
// Each channel's layer is tinted by its colour and gets its own window/gamma/visibility.
|
|
831
|
+
view.layers.forEach((_, c) => {
|
|
832
|
+
const st = channels.find((s) => s.index === c);
|
|
833
|
+
view.updateChannel(c, {
|
|
834
|
+
colormap: this.channelTintColormap(st?.color ?? '#ffffff'),
|
|
835
|
+
...(st
|
|
836
|
+
? {
|
|
837
|
+
contrastLimits: [st.min, st.max],
|
|
838
|
+
gamma: st.gamma,
|
|
839
|
+
visible: st.visible,
|
|
840
|
+
}
|
|
841
|
+
: {}),
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
const st = channels[0];
|
|
847
|
+
view.updateChannel(0, {
|
|
848
|
+
colormap: this.volumeColormap(st),
|
|
849
|
+
...(st ? { contrastLimits: [st.min, st.max], gamma: st.gamma } : {}),
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
/** A channel's tint colormap (black→colour) with reverse-scale / invert applied by flipping. */
|
|
855
|
+
channelTintColormap(color) {
|
|
856
|
+
let cmap = tintColormap(color);
|
|
857
|
+
if (this.currentReverse)
|
|
858
|
+
cmap = reverseColormap(cmap);
|
|
859
|
+
if (this.invertEnabled)
|
|
860
|
+
cmap = reverseColormap(cmap);
|
|
861
|
+
return cmap;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Colour map for the volume/isosurface. A real colormap selection (viridis/magma/…) wins;
|
|
865
|
+
* otherwise the channel's colour tints it (so the channel-dialog colour swatch recolors the 3D
|
|
866
|
+
* render). Reverse-scale and invert each flip the ramp — the `VolumeLayer` has no per-layer
|
|
867
|
+
* invert, so both are emulated by reversing the colormap.
|
|
868
|
+
*/
|
|
869
|
+
volumeColormap(st) {
|
|
870
|
+
const node = this.currentColormap;
|
|
871
|
+
// A colored colormap (viridis/magma/…) drives the volume; the default grayscale family
|
|
872
|
+
// (gray / Greys / Greys Inv) yields to the channel's colour so the dialog colour swatch
|
|
873
|
+
// recolors the 3D render.
|
|
874
|
+
const label = (node?.label ?? '').toLowerCase();
|
|
875
|
+
const grayFamily = label === '' || label.includes('grey') || label.includes('gray');
|
|
876
|
+
const value = node?.data?.value;
|
|
877
|
+
const lut = !grayFamily && value != null ? buildColormapLut(value, false) : null;
|
|
878
|
+
let cmap = lut
|
|
879
|
+
? colormapFromLut('vol-cmap', lut)
|
|
880
|
+
: tintColormap(st?.color ?? '#ffffff');
|
|
881
|
+
// Reverse-scale and invert each flip the ramp (the VolumeLayer has no per-layer invert).
|
|
882
|
+
if (this.currentReverse)
|
|
883
|
+
cmap = reverseColormap(cmap);
|
|
884
|
+
if (this.invertEnabled)
|
|
885
|
+
cmap = reverseColormap(cmap);
|
|
886
|
+
return cmap;
|
|
887
|
+
}
|
|
888
|
+
/** Apply the current channel states / colormap to the live layers (no re-fetch), delegating the
|
|
889
|
+
* per-channel layer mutations to the {@link MultiChannelImageView}. */
|
|
890
|
+
applyDisplayState(channels) {
|
|
891
|
+
const view = this.channelView;
|
|
892
|
+
if (!this.viewer || !view)
|
|
893
|
+
return;
|
|
894
|
+
if (this.imageMode === 'multichannel') {
|
|
895
|
+
view.layers.forEach((_, c) => {
|
|
896
|
+
const st = channels.find((s) => s.index === c);
|
|
897
|
+
if (!st)
|
|
898
|
+
return;
|
|
899
|
+
view.updateChannel(c, {
|
|
900
|
+
tint: st.color,
|
|
901
|
+
contrastLimits: [st.min, st.max],
|
|
902
|
+
gamma: st.gamma,
|
|
903
|
+
visible: st.visible,
|
|
904
|
+
invert: this.invertEnabled,
|
|
905
|
+
});
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
else if (this.imageMode === 'grayscale') {
|
|
909
|
+
const st = channels[0];
|
|
910
|
+
view.updateChannel(0, {
|
|
911
|
+
colormap: this.grayscaleColormap(),
|
|
912
|
+
invert: this.invertEnabled,
|
|
913
|
+
...(st ? { contrastLimits: [st.min, st.max], gamma: st.gamma } : {}),
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
/** (Re)install the physical scale bar over the 2D image, sized from the image's µm/pixel
|
|
918
|
+
* (`/tiles/info` mppX, falling back to the image metadata). No-op without a physical size. */
|
|
919
|
+
installScaleBar() {
|
|
920
|
+
this.scaleBar?.destroy();
|
|
921
|
+
this.scaleBar = null;
|
|
922
|
+
const mppX = this.descriptor?.mppX || this.loaded?.imageInfo.imageMeta?.[0]?.mppX || 0;
|
|
923
|
+
if (this.viewer && this.host && mppX > 0) {
|
|
924
|
+
this.scaleBar = new NapariScaleBar(this.host, this.viewer.camera, mppX);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
/** Convert a napari-js `Histogram` (bin count + min/max) to the pane's `IHistogram` (bin edges). */
|
|
928
|
+
toIHistogram(h) {
|
|
929
|
+
const span = h.max - h.min || 1;
|
|
930
|
+
const bins = Array.from({ length: h.bins }, (_, i) => h.min + (i * span) / h.bins);
|
|
931
|
+
const counts = Array.from(h.counts);
|
|
932
|
+
return { bins, counts, max: counts.reduce((m, c) => (c > m ? c : m), 0) };
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Mount the 3D volume/isosurface. A multichannel image becomes one additive, tinted
|
|
936
|
+
* {@link VolumeLayer} per channel (so each channel's colour composites into the render); a
|
|
937
|
+
* grayscale/composite image uses a single volume. Adds the axes gizmo + labels and wires the
|
|
938
|
+
* display-state subscription. The caller owns `viewer.ready`.
|
|
939
|
+
*/
|
|
940
|
+
async mountVolume(viewer, info, plotType) {
|
|
941
|
+
const token = this.loadToken; // bail before rendering if a Cancel / new plot bumps this
|
|
942
|
+
const desc = await this.ensureDescriptor();
|
|
943
|
+
const channelCount = desc?.channels ?? 1;
|
|
944
|
+
const multichannel = !!desc?.multichannel && channelCount > 1;
|
|
945
|
+
const res = volumeResolutionFor(this.resolutionScale);
|
|
946
|
+
const rendering = isNapariIsosurface(plotType) ? 'iso' : 'mip';
|
|
947
|
+
const states = this.store.currentChannelStates();
|
|
948
|
+
this.volumeChannelData.clear();
|
|
949
|
+
this.volumeMultichannel = multichannel;
|
|
950
|
+
this.imageMode = multichannel ? 'multichannel' : 'grayscale';
|
|
951
|
+
const view = new MultiChannelVolumeView(viewer);
|
|
952
|
+
this.volumeView = view;
|
|
953
|
+
// Assemble per-channel scalar volumes from the server tiles (jit-specific); the napari-js view
|
|
954
|
+
// owns the layer orchestration (one additive tinted volume per channel, or a single grayscale
|
|
955
|
+
// volume). The adapter computes each channel's colormap (incl. invert/reverse flips).
|
|
956
|
+
let dims = null;
|
|
957
|
+
const channels = [];
|
|
958
|
+
this.stackLoading$.next(true);
|
|
959
|
+
this.stackLoadingProgress$.next(0);
|
|
960
|
+
try {
|
|
961
|
+
if (multichannel) {
|
|
962
|
+
for (let c = 0; c < channelCount; c++) {
|
|
963
|
+
const vol = await this.assembleVolume(info, res, c);
|
|
964
|
+
if (!vol)
|
|
965
|
+
continue;
|
|
966
|
+
dims = vol;
|
|
967
|
+
this.volumeChannelData.set(c, vol.data);
|
|
968
|
+
const st = states.find((s) => s.index === c);
|
|
969
|
+
const color = st?.color ?? desc?.channelInfo?.[c]?.color ?? tintFor(c);
|
|
970
|
+
channels.push({
|
|
971
|
+
data: vol.data,
|
|
972
|
+
width: vol.width,
|
|
973
|
+
height: vol.height,
|
|
974
|
+
depth: vol.depth,
|
|
975
|
+
colormap: this.channelTintColormap(color),
|
|
976
|
+
contrastLimits: [st?.min ?? 0, st?.max ?? 255],
|
|
977
|
+
gamma: st?.gamma ?? 1,
|
|
978
|
+
visible: st?.visible ?? true,
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
const vol = await this.assembleVolume(info, res);
|
|
984
|
+
if (vol) {
|
|
985
|
+
dims = vol;
|
|
986
|
+
this.volumeChannelData.set(0, vol.data);
|
|
987
|
+
const st = states[0];
|
|
988
|
+
channels.push({
|
|
989
|
+
data: vol.data,
|
|
990
|
+
width: vol.width,
|
|
991
|
+
height: vol.height,
|
|
992
|
+
depth: vol.depth,
|
|
993
|
+
colormap: this.volumeColormap(st),
|
|
994
|
+
contrastLimits: [st?.min ?? 0, st?.max ?? 255],
|
|
995
|
+
gamma: st?.gamma ?? 1,
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
finally {
|
|
1001
|
+
this.stackLoading$.next(false);
|
|
1002
|
+
this.stackLoadingProgress$.next(0);
|
|
1003
|
+
}
|
|
1004
|
+
if (!dims || !channels.length || this.loadToken !== token)
|
|
1005
|
+
return; // cancelled → don't render
|
|
1006
|
+
// Resolution-invariant world box. Sizing the box by the sampled voxel counts made higher
|
|
1007
|
+
// in-plane resolution grow X/Y while the depth stayed the (constant) slice count — so Z appeared
|
|
1008
|
+
// to shrink at higher resolution. Instead anchor the in-plane long side to a fixed reference and
|
|
1009
|
+
// let Z span the full slice count; the box shape is then identical at every decimate factor. The
|
|
1010
|
+
// per-axis `voxelSize` (napari `scale`) maps the sampled grid onto that fixed world box.
|
|
1011
|
+
const fullW = this.descriptor?.width ?? dims.width;
|
|
1012
|
+
const fullH = this.descriptor?.height ?? dims.height;
|
|
1013
|
+
const fullD = this.loaded?.imageInfo.imageMeta?.[0]?.z || this.loaded?.imageInfo.urls?.length || dims.depth;
|
|
1014
|
+
const fullLong = Math.max(1, fullW, fullH);
|
|
1015
|
+
const world = {
|
|
1016
|
+
width: (fullW * VOLUME_WORLD_INPLANE_REF) / fullLong,
|
|
1017
|
+
height: (fullH * VOLUME_WORLD_INPLANE_REF) / fullLong,
|
|
1018
|
+
depth: fullD,
|
|
1019
|
+
};
|
|
1020
|
+
// Base box (Z-scale = 1) + the sampled depth drive the live Z-height handle below; the persisted
|
|
1021
|
+
// `volumeZScale` (user drag) applies on top so changing resolution keeps the chosen height.
|
|
1022
|
+
this.volumeWorldBase = world;
|
|
1023
|
+
this.volumeSampledDepth = Math.max(1, dims.depth);
|
|
1024
|
+
const worldZ = world.depth * this.volumeZScale;
|
|
1025
|
+
const voxelSize = [
|
|
1026
|
+
world.width / dims.width,
|
|
1027
|
+
world.height / dims.height,
|
|
1028
|
+
worldZ / this.volumeSampledDepth,
|
|
1029
|
+
];
|
|
1030
|
+
for (const ch of channels)
|
|
1031
|
+
ch.voxelSize = voxelSize;
|
|
1032
|
+
view.render(multichannel ? 'multichannel' : 'grayscale', channels, { rendering });
|
|
1033
|
+
this.imageW = dims.width;
|
|
1034
|
+
this.imageH = dims.height;
|
|
1035
|
+
this.volumeDims = dims;
|
|
1036
|
+
// 3D coordinate-axes / scale gizmo + labels, sharing the volume's world box so the gizmo tracks
|
|
1037
|
+
// the rendered proportions. Physical scale text still comes from the FULL image extent.
|
|
1038
|
+
const mppX = this.descriptor?.mppX || this.loaded?.imageInfo.imageMeta?.[0]?.mppX || 0;
|
|
1039
|
+
this.axesLayer = viewer.addAxes(world.width, world.height, worldZ, { visible: this.axesVisible });
|
|
1040
|
+
if (this.host) {
|
|
1041
|
+
this.axesLabels = new NapariAxesLabels(this.host, viewer.camera3d, this.buildAxesLabels({ width: world.width, height: world.height, depth: worldZ }, mppX));
|
|
1042
|
+
this.axesLabels.setVisible(this.axesVisible);
|
|
1043
|
+
// In-view drag handle at the TOP END OF THE Z AXIS (the box's min-XY corner, where the blue
|
|
1044
|
+
// "Z" axis + label live), so it reads as the Z-height control. Floated a little past the axis
|
|
1045
|
+
// tip (×1.12) so the grip clears the "Z · …" label. Drag ↕ to restretch Z live.
|
|
1046
|
+
this.zHandle = new NapariVolumeZHandle(this.host, viewer.camera3d, {
|
|
1047
|
+
topAnchor: () => [
|
|
1048
|
+
-this.volumeWorldBase.width / 2,
|
|
1049
|
+
-this.volumeWorldBase.height / 2,
|
|
1050
|
+
((this.volumeWorldBase.depth * this.volumeZScale) / 2) * 1.12,
|
|
1051
|
+
],
|
|
1052
|
+
getScale: () => this.volumeZScale,
|
|
1053
|
+
setScale: (s) => this.setVolumeZScale(s),
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
this.subscribeVolumeDisplayState();
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Restretch the volume's Z height live (driven by the in-view {@link NapariVolumeZHandle}). Only
|
|
1060
|
+
* the per-axis `voxelSize` / axes depth change — the voxel textures are untouched — so dragging is
|
|
1061
|
+
* smooth. `factor` is relative to the volume's natural proportions (1). Persists across re-mounts
|
|
1062
|
+
* (resolution changes) so the chosen height sticks.
|
|
1063
|
+
*/
|
|
1064
|
+
setVolumeZScale(factor) {
|
|
1065
|
+
this.volumeZScale = Math.min(10, Math.max(0.1, factor));
|
|
1066
|
+
const base = this.volumeWorldBase;
|
|
1067
|
+
if (!base || !this.volumeView)
|
|
1068
|
+
return;
|
|
1069
|
+
const worldZ = base.depth * this.volumeZScale;
|
|
1070
|
+
const vsZ = worldZ / this.volumeSampledDepth;
|
|
1071
|
+
for (const layer of this.volumeView.layers) {
|
|
1072
|
+
const [sx, sy] = layer.voxelSize;
|
|
1073
|
+
layer.voxelSize = [sx, sy, vsZ];
|
|
1074
|
+
}
|
|
1075
|
+
if (this.axesLayer)
|
|
1076
|
+
this.axesLayer.depth = worldZ;
|
|
1077
|
+
const mppX = this.descriptor?.mppX || this.loaded?.imageInfo.imageMeta?.[0]?.mppX || 0;
|
|
1078
|
+
this.axesLabels?.updateAnchors(this.buildAxesLabels({ width: base.width, height: base.height, depth: worldZ }, mppX));
|
|
1079
|
+
this.zHandle?.reposition();
|
|
1080
|
+
this.viewer?.requestRender();
|
|
1081
|
+
}
|
|
1082
|
+
/** Build the X/Y/Z axis-end label specs for the 3D gizmo. Anchors are in the volume's centred
|
|
1083
|
+
* world box (matching the AxesLayer geometry); the scale text reflects the FULL image extent —
|
|
1084
|
+
* physical µm when µm/pixel is known, else pixel (X/Y) / slice (Z) counts. */
|
|
1085
|
+
buildAxesLabels(vol, mppX) {
|
|
1086
|
+
const hx = vol.width / 2;
|
|
1087
|
+
const hy = vol.height / 2;
|
|
1088
|
+
const hz = vol.depth / 2;
|
|
1089
|
+
const descW = this.descriptor?.width ?? vol.width;
|
|
1090
|
+
const descH = this.descriptor?.height ?? vol.height;
|
|
1091
|
+
const slices = this.loaded?.imageInfo.imageMeta?.[0]?.z || this.loaded?.imageInfo.urls?.length || vol.depth;
|
|
1092
|
+
const len = (px) => (mppX > 0 ? formatUm(px * mppX) : `${px} px`);
|
|
1093
|
+
return [
|
|
1094
|
+
{ anchor: [hx, -hy, -hz], text: `X · ${len(descW)}`, color: '#ed4545' },
|
|
1095
|
+
{ anchor: [-hx, hy, -hz], text: `Y · ${len(descH)}`, color: '#4dd959' },
|
|
1096
|
+
{ anchor: [-hx, -hy, hz], text: `Z · ${slices} px`, color: '#668cff' },
|
|
1097
|
+
];
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Assemble a downsampled uint8 volume (luminance) from the per-slice tile endpoint. Slices are
|
|
1101
|
+
* fetched with bounded concurrency (keeps the connection pool full without flooding it on a deep
|
|
1102
|
+
* stack) and read into the volume as each arrives, driving {@link stackLoadingProgress$} so the
|
|
1103
|
+
* host shows a determinate progress bar instead of a bare spinner.
|
|
1104
|
+
*/
|
|
1105
|
+
async assembleVolume(info, opts = {}, channel) {
|
|
1106
|
+
const token = this.loadToken; // bail if a Cancel / new plot bumps this while we fetch
|
|
1107
|
+
const fullDepth = info?.imageMeta?.[0]?.z || info?.urls?.length || 1;
|
|
1108
|
+
if (fullDepth < 1) {
|
|
1109
|
+
console.warn('[napari-js] no slices to assemble a volume');
|
|
1110
|
+
return null;
|
|
1111
|
+
}
|
|
1112
|
+
const step = Math.max(1, Math.floor(opts.sliceStep ?? 1));
|
|
1113
|
+
const maxSlice = opts.maxSlice ?? VOLUME_MAX_SLICE;
|
|
1114
|
+
// Source-slice indices sampled into the volume (every `step`th plane → low-res is faster).
|
|
1115
|
+
const zIndices = [];
|
|
1116
|
+
for (let z = 0; z < fullDepth; z += step)
|
|
1117
|
+
zIndices.push(z);
|
|
1118
|
+
const depth = zIndices.length;
|
|
1119
|
+
this.stackLoadingProgress$.next(0);
|
|
1120
|
+
try {
|
|
1121
|
+
// Fetch each slice at a pyramid level matching `maxSlice` (budget scales with the decimate
|
|
1122
|
+
// factor), so a higher factor pulls a finer level → more real in-plane detail; then downsample
|
|
1123
|
+
// to `maxSlice`. `channel` selects a band (multichannel volume); omit for the grayscale
|
|
1124
|
+
// composite. The caller owns the stackLoading flag (multichannel assembles channels in turn).
|
|
1125
|
+
const budget = this.tileBudgetFor(maxSlice);
|
|
1126
|
+
const first = await this.fetchSlice(zIndices[0], channel, budget);
|
|
1127
|
+
const scale = Math.min(1, maxSlice / Math.max(first.width, first.height, 1));
|
|
1128
|
+
const width = Math.max(1, Math.round(first.width * scale));
|
|
1129
|
+
const height = Math.max(1, Math.round(first.height * scale));
|
|
1130
|
+
const data = new Uint8Array(width * height * depth);
|
|
1131
|
+
let canvas;
|
|
1132
|
+
if (typeof OffscreenCanvas !== 'undefined') {
|
|
1133
|
+
canvas = new OffscreenCanvas(width, height);
|
|
1134
|
+
}
|
|
1135
|
+
else {
|
|
1136
|
+
canvas = document.createElement('canvas');
|
|
1137
|
+
canvas.width = width;
|
|
1138
|
+
canvas.height = height;
|
|
1139
|
+
}
|
|
1140
|
+
const ctx = canvas.getContext('2d');
|
|
1141
|
+
if (!ctx)
|
|
1142
|
+
throw new Error('napari-js volume assembly: 2D context unavailable');
|
|
1143
|
+
// Read one fetched slice bitmap into the volume plane `z` (luminance). Synchronous between
|
|
1144
|
+
// awaits, so the shared 2D context is safe to reuse across the concurrent fetch workers.
|
|
1145
|
+
let done = 0;
|
|
1146
|
+
const readSlice = (z, bmp) => {
|
|
1147
|
+
ctx.clearRect(0, 0, width, height);
|
|
1148
|
+
ctx.drawImage(bmp, 0, 0, width, height);
|
|
1149
|
+
const rgba = ctx.getImageData(0, 0, width, height).data;
|
|
1150
|
+
const base = z * width * height;
|
|
1151
|
+
for (let i = 0; i < width * height; i++) {
|
|
1152
|
+
data[base + i] =
|
|
1153
|
+
(rgba[i * 4] * 0.299 + rgba[i * 4 + 1] * 0.587 + rgba[i * 4 + 2] * 0.114) | 0;
|
|
1154
|
+
}
|
|
1155
|
+
bmp.close?.();
|
|
1156
|
+
done++;
|
|
1157
|
+
this.stackLoadingProgress$.next(Math.round((done / depth) * 100));
|
|
1158
|
+
};
|
|
1159
|
+
readSlice(0, first);
|
|
1160
|
+
// Remaining planes via a small pool of workers draining a shared queue. Each plane `p`
|
|
1161
|
+
// maps to source slice `zIndices[p]` (subsampled for low-res).
|
|
1162
|
+
const pending = [];
|
|
1163
|
+
for (let p = 1; p < depth; p++)
|
|
1164
|
+
pending.push(p);
|
|
1165
|
+
const worker = async () => {
|
|
1166
|
+
for (;;) {
|
|
1167
|
+
const p = pending.shift();
|
|
1168
|
+
if (p === undefined || this.loadToken !== token)
|
|
1169
|
+
return;
|
|
1170
|
+
readSlice(p, await this.fetchSlice(zIndices[p], channel, budget));
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
const poolSize = Math.min(VOLUME_FETCH_CONCURRENCY, Math.max(1, depth - 1));
|
|
1174
|
+
await Promise.all(Array.from({ length: poolSize }, () => worker()));
|
|
1175
|
+
if (this.loadToken !== token)
|
|
1176
|
+
return null; // cancelled → don't render a partial volume
|
|
1177
|
+
return { data, width, height, depth };
|
|
1178
|
+
}
|
|
1179
|
+
finally {
|
|
1180
|
+
this.stackLoadingProgress$.next(0);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Mount the NAPARI_SCATTER 2D scatter: the slice image with a points layer at each region's
|
|
1185
|
+
* centroid (napari-js analog of Plotly's region-centroid scatter). Rebuilds the points live as
|
|
1186
|
+
* regions change.
|
|
1187
|
+
*/
|
|
1188
|
+
async mountScatter(viewer, z) {
|
|
1189
|
+
await this.renderImage(z);
|
|
1190
|
+
this.fitCameraSoon();
|
|
1191
|
+
this.subscribeDisplayState();
|
|
1192
|
+
this.installScaleBar();
|
|
1193
|
+
this.rebuildScatterPoints();
|
|
1194
|
+
this.scatterRegionSub = this.regionStore
|
|
1195
|
+
.getRegionUpdateEvent()
|
|
1196
|
+
.subscribe(() => this.rebuildScatterPoints());
|
|
1197
|
+
this.scheduleReadback();
|
|
1198
|
+
}
|
|
1199
|
+
/** (Re)build the 2D scatter's point layer at the current region centroids. */
|
|
1200
|
+
rebuildScatterPoints() {
|
|
1201
|
+
const v = this.viewer;
|
|
1202
|
+
if (!v)
|
|
1203
|
+
return;
|
|
1204
|
+
if (this.scatter2dPoints) {
|
|
1205
|
+
v.layers.remove(this.scatter2dPoints);
|
|
1206
|
+
this.scatter2dPoints = null;
|
|
1207
|
+
}
|
|
1208
|
+
const centroids = this.regionCentroids();
|
|
1209
|
+
if (centroids.length === 0)
|
|
1210
|
+
return;
|
|
1211
|
+
this.scatter2dPoints = v.addPoints(centroids, {
|
|
1212
|
+
size: 12,
|
|
1213
|
+
faceColor: [1, 0.85, 0.2, 1],
|
|
1214
|
+
borderColor: [0, 0, 0, 1],
|
|
1215
|
+
borderWidth: 2,
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
/** Region centroids as flat `[x, y, …]` data coords (rectangle / polygon / multipolygon). */
|
|
1219
|
+
regionCentroids() {
|
|
1220
|
+
const out = [];
|
|
1221
|
+
const polyCentroid = (xs, ys) => {
|
|
1222
|
+
const n = xs.length;
|
|
1223
|
+
if (n === 0)
|
|
1224
|
+
return;
|
|
1225
|
+
let cx = 0;
|
|
1226
|
+
let cy = 0;
|
|
1227
|
+
for (let i = 0; i < n; i++) {
|
|
1228
|
+
cx += xs[i];
|
|
1229
|
+
cy += ys[i];
|
|
1230
|
+
}
|
|
1231
|
+
out.push(cx / n, cy / n);
|
|
1232
|
+
};
|
|
1233
|
+
for (const r of this.regionStore.getRegions()) {
|
|
1234
|
+
const b = r.bounds;
|
|
1235
|
+
if (!b)
|
|
1236
|
+
continue;
|
|
1237
|
+
if ('width' in b && 'x' in b) {
|
|
1238
|
+
out.push(b.x + b.width / 2, b.y + b.height / 2);
|
|
1239
|
+
}
|
|
1240
|
+
else if ('xpoints' in b) {
|
|
1241
|
+
polyCentroid(b.xpoints, b.ypoints);
|
|
1242
|
+
}
|
|
1243
|
+
else if ('polygons' in b) {
|
|
1244
|
+
for (const p of b.polygons)
|
|
1245
|
+
polyCentroid(p.xpoints, p.ypoints);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
return new Float32Array(out);
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Mount the NAPARI_SCATTER3D 3D scatter: the downsampled voxel grid as a 3D point cloud colored
|
|
1252
|
+
* by intensity (napari-js analog of Plotly's voxel scatter3d). Assembles a coarse volume, then
|
|
1253
|
+
* emits a flat-strided sample of voxels (capped at {@link SCATTER3D_MAX_POINTS}) via `addPoints3D`.
|
|
1254
|
+
*/
|
|
1255
|
+
async mountScatter3d(viewer, info) {
|
|
1256
|
+
this.imageMode = 'grayscale';
|
|
1257
|
+
this.volumeMultichannel = false;
|
|
1258
|
+
const res = volumeResolutionFor(this.resolutionScale);
|
|
1259
|
+
this.stackLoading$.next(true);
|
|
1260
|
+
this.stackLoadingProgress$.next(0);
|
|
1261
|
+
let vol = null;
|
|
1262
|
+
try {
|
|
1263
|
+
vol = await this.assembleVolume(info, {
|
|
1264
|
+
maxSlice: Math.min(res.maxSlice, SCATTER3D_MAX_XY),
|
|
1265
|
+
sliceStep: res.sliceStep,
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
finally {
|
|
1269
|
+
this.stackLoading$.next(false);
|
|
1270
|
+
this.stackLoadingProgress$.next(0);
|
|
1271
|
+
}
|
|
1272
|
+
if (!vol || this.viewer !== viewer)
|
|
1273
|
+
return;
|
|
1274
|
+
const { data, width, height, depth } = vol;
|
|
1275
|
+
const zScale = Math.max(width, height) / Math.max(1, depth); // ≈ cubic aspect
|
|
1276
|
+
const total = width * height * depth;
|
|
1277
|
+
const stride = Math.max(1, Math.ceil(total / SCATTER3D_MAX_POINTS));
|
|
1278
|
+
const pos = [];
|
|
1279
|
+
const val = [];
|
|
1280
|
+
for (let i = 0; i < total; i += stride) {
|
|
1281
|
+
const x = i % width;
|
|
1282
|
+
const y = Math.floor(i / width) % height;
|
|
1283
|
+
const zi = Math.floor(i / (width * height));
|
|
1284
|
+
pos.push(x, y, zi * zScale);
|
|
1285
|
+
val.push(data[i]);
|
|
1286
|
+
}
|
|
1287
|
+
const st = this.store.currentChannelStates()[0];
|
|
1288
|
+
this.scatter3dLayer = viewer.addPoints3D(new Float32Array(pos), new Float32Array(val), {
|
|
1289
|
+
colormap: this.volumeColormap(st),
|
|
1290
|
+
contrastLimits: [st?.min ?? 0, st?.max ?? 255],
|
|
1291
|
+
size: 3,
|
|
1292
|
+
});
|
|
1293
|
+
this.imageW = width;
|
|
1294
|
+
this.imageH = height;
|
|
1295
|
+
this.volumeDims = { width, height, depth };
|
|
1296
|
+
// Feed the intensity histogram from the assembled volume (key 0).
|
|
1297
|
+
this.volumeChannelData.clear();
|
|
1298
|
+
this.volumeChannelData.set(0, data);
|
|
1299
|
+
this.subscribeScatter3dDisplayState();
|
|
1300
|
+
this.scheduleReadback();
|
|
1301
|
+
}
|
|
1302
|
+
/** Store colormap / reverse / invert / channel window → the 3D scatter's colormap + contrast. */
|
|
1303
|
+
subscribeScatter3dDisplayState() {
|
|
1304
|
+
this.displaySub?.unsubscribe();
|
|
1305
|
+
this.displaySub = combineLatest([
|
|
1306
|
+
this.store.getColormap(),
|
|
1307
|
+
this.store.getReverseScale(),
|
|
1308
|
+
this.store.getInvert(),
|
|
1309
|
+
this.store.getChannelStates(),
|
|
1310
|
+
]).subscribe(([colormap, reverse, invert, channels]) => {
|
|
1311
|
+
this.currentColormap = colormap ?? null;
|
|
1312
|
+
this.currentReverse = reverse;
|
|
1313
|
+
this.invertEnabled = invert;
|
|
1314
|
+
const layer = this.scatter3dLayer;
|
|
1315
|
+
if (!layer)
|
|
1316
|
+
return;
|
|
1317
|
+
const st = channels[0];
|
|
1318
|
+
layer.colormap = this.volumeColormap(st);
|
|
1319
|
+
if (st)
|
|
1320
|
+
layer.contrastLimits = [st.min, st.max];
|
|
1321
|
+
this.viewer?.requestRender();
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Mount the NAPARI_SURFACE height-field surface. A height field is single-scalar, so for a
|
|
1326
|
+
* multichannel image the surface follows ONE band — the first visible channel (fallback 0) —
|
|
1327
|
+
* coloured by that channel's window/colormap (like the Plotly SURFACE, which is grayscale-only).
|
|
1328
|
+
* Pre-loads every slice's height data with a progress bar (as the volume does) so the stack
|
|
1329
|
+
* slider re-slices instantly, then builds the mesh for the current slice. All mesh + GPU work
|
|
1330
|
+
* lives in napari-js (`heightField` + `Viewer.addSurface`); this backend supplies scalar slices.
|
|
1331
|
+
*/
|
|
1332
|
+
async mountSurface(viewer) {
|
|
1333
|
+
const desc = await this.ensureDescriptor();
|
|
1334
|
+
const multichannel = !!desc?.multichannel && (desc?.channels ?? 1) > 1;
|
|
1335
|
+
const states = this.store.currentChannelStates();
|
|
1336
|
+
this.surfaceChannel = multichannel ? states.find((s) => s.visible)?.index ?? 0 : undefined;
|
|
1337
|
+
this.surfaceMaxGrid = surfaceResolutionFor(this.resolutionScale).maxGrid;
|
|
1338
|
+
this.imageMode = 'grayscale';
|
|
1339
|
+
this.volumeMultichannel = false;
|
|
1340
|
+
await this.preloadSurfacePlanes(viewer);
|
|
1341
|
+
if (this.viewer !== viewer)
|
|
1342
|
+
return;
|
|
1343
|
+
await this.buildSurface(viewer, this.loaded?.z ?? 0);
|
|
1344
|
+
this.installSurfaceAxes(viewer);
|
|
1345
|
+
// Subscribe after the first build so display-state edits target a live layer.
|
|
1346
|
+
this.subscribeSurfaceDisplayState();
|
|
1347
|
+
}
|
|
1348
|
+
/** Add the 3D axes gizmo + DOM labels around the (origin-centered) surface mesh, matching the
|
|
1349
|
+
* volume/isosurface. Installed once per mount; the box tracks the mesh bounds, X/Y show the
|
|
1350
|
+
* physical (or pixel) extent, and Z is the intensity/height axis. */
|
|
1351
|
+
installSurfaceAxes(viewer) {
|
|
1352
|
+
if (!this.surfaceLayer)
|
|
1353
|
+
return;
|
|
1354
|
+
const b = this.surfaceLayer.bounds();
|
|
1355
|
+
const boxW = Math.max(1, b.max[0] - b.min[0]);
|
|
1356
|
+
const boxH = Math.max(1, b.max[1] - b.min[1]);
|
|
1357
|
+
const boxD = Math.max(1, b.max[2] - b.min[2]);
|
|
1358
|
+
const mppX = this.descriptor?.mppX || this.loaded?.imageInfo.imageMeta?.[0]?.mppX || 0;
|
|
1359
|
+
const voxel = mppX > 0 ? (mppX * (this.descriptor?.width ?? this.imageW)) / Math.max(1, this.imageW) : 1;
|
|
1360
|
+
this.axesLayer = viewer.addAxes(boxW, boxH, boxD, {
|
|
1361
|
+
voxelSize: [voxel, voxel, 1],
|
|
1362
|
+
visible: this.axesVisible,
|
|
1363
|
+
});
|
|
1364
|
+
if (this.host) {
|
|
1365
|
+
this.axesLabels = new NapariAxesLabels(this.host, viewer.camera3d, this.buildSurfaceAxesLabels(boxW, boxH, boxD, mppX));
|
|
1366
|
+
this.axesLabels.setVisible(this.axesVisible);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
/** X/Y/Z end-labels for the surface gizmo: X/Y are the physical (µm) or pixel extent of the FULL
|
|
1370
|
+
* image; Z is the intensity/height axis. Anchors are in the centered box (matching AxesLayer). */
|
|
1371
|
+
buildSurfaceAxesLabels(boxW, boxH, boxD, mppX) {
|
|
1372
|
+
const hx = boxW / 2;
|
|
1373
|
+
const hy = boxH / 2;
|
|
1374
|
+
const hz = boxD / 2;
|
|
1375
|
+
const descW = this.descriptor?.width ?? this.imageW;
|
|
1376
|
+
const descH = this.descriptor?.height ?? this.imageH;
|
|
1377
|
+
const len = (px) => (mppX > 0 ? formatUm(px * mppX) : `${px} px`);
|
|
1378
|
+
return [
|
|
1379
|
+
{ anchor: [hx, -hy, -hz], text: `X · ${len(descW)}`, color: '#ed4545' },
|
|
1380
|
+
{ anchor: [-hx, hy, -hz], text: `Y · ${len(descH)}`, color: '#4dd959' },
|
|
1381
|
+
{ anchor: [-hx, -hy, hz], text: 'Z · intensity', color: '#668cff' },
|
|
1382
|
+
];
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Pre-fetch every stack slice's luminance plane (decimated to the surface grid) into
|
|
1386
|
+
* {@link surfacePlanes}, driving {@link stackLoadingProgress$} — the same load-with-progress UX
|
|
1387
|
+
* as the volume, but keeping one 2D plane per slice rather than packing a 3D volume. Bounded
|
|
1388
|
+
* concurrency keeps the connection pool busy without flooding it on a deep stack.
|
|
1389
|
+
*/
|
|
1390
|
+
async preloadSurfacePlanes(viewer) {
|
|
1391
|
+
const token = this.loadToken; // bail if a Cancel / new plot bumps this while we fetch
|
|
1392
|
+
const info = this.loaded?.imageInfo;
|
|
1393
|
+
const depth = info?.imageMeta?.[0]?.z || info?.urls?.length || 1;
|
|
1394
|
+
const { maxGrid } = surfaceResolutionFor(this.resolutionScale);
|
|
1395
|
+
this.surfacePlanes.clear();
|
|
1396
|
+
this.stackLoading$.next(true);
|
|
1397
|
+
this.stackLoadingProgress$.next(0);
|
|
1398
|
+
try {
|
|
1399
|
+
const pending = [];
|
|
1400
|
+
for (let z = 0; z < depth; z++)
|
|
1401
|
+
pending.push(z);
|
|
1402
|
+
let done = 0;
|
|
1403
|
+
const worker = async () => {
|
|
1404
|
+
for (;;) {
|
|
1405
|
+
const z = pending.shift();
|
|
1406
|
+
if (z === undefined || this.viewer !== viewer || this.loadToken !== token)
|
|
1407
|
+
return;
|
|
1408
|
+
try {
|
|
1409
|
+
this.surfacePlanes.set(z, await this.fetchSurfacePlane(z, maxGrid));
|
|
1410
|
+
}
|
|
1411
|
+
catch (err) {
|
|
1412
|
+
console.warn(`[napari-js] surface slice ${z} preload failed`, err);
|
|
1413
|
+
}
|
|
1414
|
+
done++;
|
|
1415
|
+
this.stackLoadingProgress$.next(Math.round((done / depth) * 100));
|
|
1416
|
+
}
|
|
1417
|
+
};
|
|
1418
|
+
const pool = Math.min(VOLUME_FETCH_CONCURRENCY, Math.max(1, depth));
|
|
1419
|
+
await Promise.all(Array.from({ length: pool }, () => worker()));
|
|
1420
|
+
}
|
|
1421
|
+
finally {
|
|
1422
|
+
this.stackLoading$.next(false);
|
|
1423
|
+
this.stackLoadingProgress$.next(0);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Fetch slice `z` as a single WHOLE-image luminance plane (decimated to `maxGrid`). Prefers the
|
|
1428
|
+
* app's complete per-slice image (`smallUrls`/`urls`, exactly like the Plotly surface) so the
|
|
1429
|
+
* surface always covers the FULL slice — the tile-pyramid `fetchSlice` path would fall back to a
|
|
1430
|
+
* single top-left tile (a corner) for self-contained / non-`/tiles/info` images. Falls back to
|
|
1431
|
+
* stitching the tile grid only when no complete-image URL is available.
|
|
1432
|
+
*/
|
|
1433
|
+
/** Tile budget to stitch a whole slice at ~`targetPx` resolution from the pyramid: a higher target
|
|
1434
|
+
* pulls a FINER pyramid level (more real detail). Shared by the surface plane fetch and the volume
|
|
1435
|
+
* assembly so both scale their in-plane resolution with the decimate factor. */
|
|
1436
|
+
tileBudgetFor(targetPx) {
|
|
1437
|
+
const tileSize = this.descriptor?.tileSize || TILE_SIZE;
|
|
1438
|
+
return Math.min(MAX_STITCH_TILES, Math.max(1, Math.round((targetPx / tileSize) ** 2 * STITCH_BUDGET_COEFF)));
|
|
1439
|
+
}
|
|
1440
|
+
async fetchSurfacePlane(z, maxGrid) {
|
|
1441
|
+
const info = this.loaded?.imageInfo;
|
|
1442
|
+
const desc = await this.ensureDescriptor();
|
|
1443
|
+
let plane = null;
|
|
1444
|
+
// Preferred: stitch the WHOLE slice from the server pyramid at a resolution driven by the
|
|
1445
|
+
// decimate factor — a higher target grid pulls a FINER pyramid level (more real detail). With a
|
|
1446
|
+
// descriptor, fetchSlice stitches the whole chosen level (never a corner), then we downscale to
|
|
1447
|
+
// the grid. This is what makes "Full" actually higher-res than "½", not just a fixed preview.
|
|
1448
|
+
if (desc?.levels?.length) {
|
|
1449
|
+
const budget = this.tileBudgetFor(maxGrid);
|
|
1450
|
+
// The surface is a single decimated plane, so the composite fallback is acceptable when the
|
|
1451
|
+
// channel has no small pyramid level (keeps it fast); a multichannel VOLUME must not do this.
|
|
1452
|
+
plane = this.bitmapToLuminance(await this.fetchSlice(z, this.surfaceChannel, budget, true), maxGrid);
|
|
1453
|
+
}
|
|
1454
|
+
// Fallback (no pyramid): the app's COMPLETE per-slice image (urls[z], not the small blurry
|
|
1455
|
+
// thumbnail) — whole slice, avoids a corner tile. Capped at the image's own resolution.
|
|
1456
|
+
if (!plane) {
|
|
1457
|
+
const url = info?.urls?.[z] ?? info?.smallUrls?.[z];
|
|
1458
|
+
if (url) {
|
|
1459
|
+
try {
|
|
1460
|
+
const headers = await this.tiles
|
|
1461
|
+
.getAuthHeaders()
|
|
1462
|
+
.catch(() => ({}));
|
|
1463
|
+
const resp = await fetch(url, { headers });
|
|
1464
|
+
if (resp.ok) {
|
|
1465
|
+
plane = this.bitmapToLuminance(await createImageBitmap(await resp.blob()), maxGrid);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
catch (err) {
|
|
1469
|
+
console.warn(`[napari-js] surface url fetch failed for z=${z}`, err);
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
// Last resort (no pyramid and no complete image): a single tile.
|
|
1474
|
+
if (!plane) {
|
|
1475
|
+
plane = this.bitmapToLuminance(await this.fetchSlice(z, this.surfaceChannel, this.tileBudgetFor(maxGrid), true), maxGrid);
|
|
1476
|
+
}
|
|
1477
|
+
return plane;
|
|
1478
|
+
}
|
|
1479
|
+
/** The channel state driving the surface: the chosen band for multichannel (matched by index),
|
|
1480
|
+
* else the single grayscale channel. */
|
|
1481
|
+
surfaceState(channels) {
|
|
1482
|
+
if (this.surfaceChannel == null)
|
|
1483
|
+
return channels[0];
|
|
1484
|
+
return channels.find((s) => s.index === this.surfaceChannel) ?? channels[0];
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* (Re)build the surface mesh for slice `z` from the pre-loaded plane cache (instant — this is
|
|
1488
|
+
* what the stack slider calls); a slice missing from the cache is fetched on demand. napari-js's
|
|
1489
|
+
* pure `heightField` builds the triangle grid (z = normalized intensity), then `addSurface`
|
|
1490
|
+
* renders it. The slice plane also feeds the intensity histogram (key 0).
|
|
1491
|
+
*/
|
|
1492
|
+
async buildSurface(viewer, z) {
|
|
1493
|
+
let plane = this.surfacePlanes.get(z);
|
|
1494
|
+
if (!plane) {
|
|
1495
|
+
this.stackLoading$.next(true);
|
|
1496
|
+
try {
|
|
1497
|
+
plane = await this.fetchSurfacePlane(z, this.surfaceMaxGrid);
|
|
1498
|
+
this.surfacePlanes.set(z, plane);
|
|
1499
|
+
}
|
|
1500
|
+
catch (err) {
|
|
1501
|
+
console.error('[napari-js] surface slice fetch failed:', err);
|
|
1502
|
+
}
|
|
1503
|
+
finally {
|
|
1504
|
+
this.stackLoading$.next(false);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
if (!plane || plane.width < 2 || plane.height < 2 || this.viewer !== viewer)
|
|
1508
|
+
return;
|
|
1509
|
+
const st = this.surfaceState(this.store.currentChannelStates());
|
|
1510
|
+
const win = [st?.min ?? 0, st?.max ?? 255];
|
|
1511
|
+
this.surfaceWindow = win;
|
|
1512
|
+
// Height AND colour are normalized by the same contrast window, so changing min/max reshapes the
|
|
1513
|
+
// surface (a pixel's height = its intensity within [min,max]). Center it for the axes gizmo. The
|
|
1514
|
+
// plane is already decimated to the grid cap → stride 1.
|
|
1515
|
+
const zScale = SURFACE_Z_ASPECT * Math.max(plane.width, plane.height);
|
|
1516
|
+
const { vertices, faces, values } = heightField(plane.data, plane.width, plane.height, {
|
|
1517
|
+
zScale,
|
|
1518
|
+
zLimits: win,
|
|
1519
|
+
center: true,
|
|
1520
|
+
});
|
|
1521
|
+
// Preserve the orbit camera across a re-slice / window rebuild — only the first mount frames, so
|
|
1522
|
+
// stepping the stack or changing the window keeps the current zoom/pan/orientation.
|
|
1523
|
+
const cam = viewer.camera3d;
|
|
1524
|
+
const preserveCamera = this.surfaceLayer != null;
|
|
1525
|
+
const savedTarget = cam.target;
|
|
1526
|
+
const savedDistance = cam.distance;
|
|
1527
|
+
if (this.surfaceLayer) {
|
|
1528
|
+
viewer.layers.remove(this.surfaceLayer);
|
|
1529
|
+
this.surfaceLayer = null;
|
|
1530
|
+
}
|
|
1531
|
+
this.surfaceLayer = viewer.addSurface(vertices, faces, values, {
|
|
1532
|
+
colormap: this.volumeColormap(st),
|
|
1533
|
+
contrastLimits: win,
|
|
1534
|
+
gamma: st?.gamma ?? 1,
|
|
1535
|
+
wireframe: this.surfaceWireframe,
|
|
1536
|
+
});
|
|
1537
|
+
if (preserveCamera) {
|
|
1538
|
+
cam.target = savedTarget;
|
|
1539
|
+
cam.distance = savedDistance;
|
|
1540
|
+
}
|
|
1541
|
+
this.imageW = plane.width;
|
|
1542
|
+
this.imageH = plane.height;
|
|
1543
|
+
this.volumeDims = { width: plane.width, height: plane.height, depth: Math.max(1, Math.round(zScale)) };
|
|
1544
|
+
// Reuse the volume intensity-histogram path: the slice's scalar plane is the histogram source.
|
|
1545
|
+
this.volumeChannelData.clear();
|
|
1546
|
+
this.volumeChannelData.set(0, plane.data);
|
|
1547
|
+
this.scheduleReadback();
|
|
1548
|
+
}
|
|
1549
|
+
/** Subscribe the store colormap / reverse / invert / channel window → the surface, so histogram
|
|
1550
|
+
* & channel-dialog edits update it live without a re-fetch. **min/max reshapes the surface's
|
|
1551
|
+
* height** (a pixel's height = its intensity within [min,max]), so a window change rebuilds the
|
|
1552
|
+
* mesh geometry (from the cached slice); colour-only edits (colormap/LUT, gamma, reverse, invert)
|
|
1553
|
+
* just update the layer's uniforms. */
|
|
1554
|
+
subscribeSurfaceDisplayState() {
|
|
1555
|
+
this.displaySub?.unsubscribe();
|
|
1556
|
+
this.displaySub = combineLatest([
|
|
1557
|
+
this.store.getColormap(),
|
|
1558
|
+
this.store.getReverseScale(),
|
|
1559
|
+
this.store.getInvert(),
|
|
1560
|
+
this.store.getChannelStates(),
|
|
1561
|
+
]).subscribe(([colormap, reverse, invert, channels]) => {
|
|
1562
|
+
this.currentColormap = colormap ?? null;
|
|
1563
|
+
this.currentReverse = reverse;
|
|
1564
|
+
this.invertEnabled = invert;
|
|
1565
|
+
const layer = this.surfaceLayer;
|
|
1566
|
+
if (!layer || !this.viewer)
|
|
1567
|
+
return;
|
|
1568
|
+
const st = this.surfaceState(channels);
|
|
1569
|
+
const win = [st?.min ?? 0, st?.max ?? 255];
|
|
1570
|
+
const windowChanged = !this.surfaceWindow || win[0] !== this.surfaceWindow[0] || win[1] !== this.surfaceWindow[1];
|
|
1571
|
+
if (windowChanged) {
|
|
1572
|
+
// Height follows the contrast window → rebuild the mesh for the new [min,max] (camera kept).
|
|
1573
|
+
void this.buildSurface(this.viewer, this.loaded?.z ?? 0).catch((err) => console.error('[napari-js] surface window rebuild failed:', err));
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
// Colour-only change: update uniforms in place, no geometry rebuild.
|
|
1577
|
+
layer.colormap = this.volumeColormap(st);
|
|
1578
|
+
if (st) {
|
|
1579
|
+
layer.contrastLimits = win;
|
|
1580
|
+
layer.gamma = st.gamma;
|
|
1581
|
+
}
|
|
1582
|
+
this.viewer.requestRender();
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
fitCameraSoon() {
|
|
1586
|
+
const run = () => {
|
|
1587
|
+
if (this.viewer && this.canvas && this.imageW > 0 && this.imageH > 0) {
|
|
1588
|
+
this.viewer.camera.fit(this.imageW, this.imageH, this.canvas.clientWidth || this.imageW, this.canvas.clientHeight || this.imageH);
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
if (typeof requestAnimationFrame === 'function')
|
|
1592
|
+
requestAnimationFrame(run);
|
|
1593
|
+
else
|
|
1594
|
+
run();
|
|
1595
|
+
}
|
|
1596
|
+
/** @deprecated Plotly-specific; host re-drives plot() from its image stream. */
|
|
1597
|
+
reloadAndPlot() {
|
|
1598
|
+
/* no-op */
|
|
1599
|
+
}
|
|
1600
|
+
reset() {
|
|
1601
|
+
this.loadToken++; // invalidate any in-flight frame loading from the previous plot
|
|
1602
|
+
this.displaySub?.unsubscribe();
|
|
1603
|
+
this.displaySub = null;
|
|
1604
|
+
this.scaleBar?.destroy();
|
|
1605
|
+
this.scaleBar = null;
|
|
1606
|
+
this.regionOverlay?.destroy();
|
|
1607
|
+
this.regionOverlay = null;
|
|
1608
|
+
this.axesLabels?.destroy();
|
|
1609
|
+
this.axesLabels = null;
|
|
1610
|
+
this.zHandle?.destroy();
|
|
1611
|
+
this.zHandle = null;
|
|
1612
|
+
this.volumeWorldBase = null;
|
|
1613
|
+
this.cachedImage = null;
|
|
1614
|
+
this.cachedImageSource = null;
|
|
1615
|
+
this.lastPixelsRect = null;
|
|
1616
|
+
this.nativeHistograms.clear();
|
|
1617
|
+
this.tiled = false;
|
|
1618
|
+
this.histSamples.clear();
|
|
1619
|
+
if (this.readbackTimer != null) {
|
|
1620
|
+
clearTimeout(this.readbackTimer);
|
|
1621
|
+
this.readbackTimer = null;
|
|
1622
|
+
}
|
|
1623
|
+
this.cameraReadbackOff?.();
|
|
1624
|
+
this.cameraReadbackOff = null;
|
|
1625
|
+
this.channelView = null;
|
|
1626
|
+
this.viewer?.dispose();
|
|
1627
|
+
this.viewer = null;
|
|
1628
|
+
if (this.canvas && this.host?.contains(this.canvas))
|
|
1629
|
+
this.host.removeChild(this.canvas);
|
|
1630
|
+
this.canvas = null;
|
|
1631
|
+
this.lastPixels = null;
|
|
1632
|
+
this.volumeView = null;
|
|
1633
|
+
this.volumeMultichannel = false;
|
|
1634
|
+
this.surfaceLayer = null;
|
|
1635
|
+
this.surfaceChannel = undefined;
|
|
1636
|
+
this.surfacePlanes.clear();
|
|
1637
|
+
this.surfaceWindow = null;
|
|
1638
|
+
this.scatterRegionSub?.unsubscribe();
|
|
1639
|
+
this.scatterRegionSub = null;
|
|
1640
|
+
this.scatter2dPoints = null;
|
|
1641
|
+
this.scatter3dLayer = null;
|
|
1642
|
+
this.axesLayer = null;
|
|
1643
|
+
this.volumeDims = null;
|
|
1644
|
+
this.volumeChannelData.clear();
|
|
1645
|
+
}
|
|
1646
|
+
relayout(_trueImageSize) {
|
|
1647
|
+
this.viewer?.requestRender();
|
|
1648
|
+
}
|
|
1649
|
+
/** @deprecated Plotly-specific axis reset. */
|
|
1650
|
+
resetAxes() {
|
|
1651
|
+
this.fitCameraSoon();
|
|
1652
|
+
}
|
|
1653
|
+
/** @deprecated Plotly-specific autoscale. */
|
|
1654
|
+
autoscale() {
|
|
1655
|
+
this.fitCameraSoon();
|
|
1656
|
+
this.autoscaleEvent$.next(undefined);
|
|
1657
|
+
}
|
|
1658
|
+
zoomIn() {
|
|
1659
|
+
if (this.viewer)
|
|
1660
|
+
this.viewer.camera.zoom = this.viewer.camera.zoom * 1.3;
|
|
1661
|
+
}
|
|
1662
|
+
zoomOut() {
|
|
1663
|
+
if (this.viewer)
|
|
1664
|
+
this.viewer.camera.zoom = this.viewer.camera.zoom / 1.3;
|
|
1665
|
+
}
|
|
1666
|
+
setDragMode(_mode) {
|
|
1667
|
+
// No-op: pan and zoom are always the napari camera's default gestures, and pan/zoom gating for
|
|
1668
|
+
// drawing is owned by the region overlay's setMode (setControlsEnabled). Toggling controls here
|
|
1669
|
+
// would fight that — the host calls setDragMode(false) alongside overlay.setMode(<tool>).
|
|
1670
|
+
}
|
|
1671
|
+
setNavigatorVisible(_visible) {
|
|
1672
|
+
/* napari-js has no minimap; no-op */
|
|
1673
|
+
}
|
|
1674
|
+
setImageSmoothingEnabled(enabled) {
|
|
1675
|
+
this.imageSmoothing = enabled;
|
|
1676
|
+
// Apply live to the rendered image layers; baked into the next render too.
|
|
1677
|
+
this.channelView?.setInterpolation(enabled ? 'linear' : 'nearest');
|
|
1678
|
+
}
|
|
1679
|
+
setShowStack(_showstack) {
|
|
1680
|
+
/* stack navigated via setZIndex */
|
|
1681
|
+
}
|
|
1682
|
+
setZIndex(zIndex) {
|
|
1683
|
+
if (this.loaded)
|
|
1684
|
+
this.loaded.z = zIndex;
|
|
1685
|
+
const v = this.viewer;
|
|
1686
|
+
if (!v)
|
|
1687
|
+
return;
|
|
1688
|
+
// Surface: one slice → one mesh, so re-build the height field for the new slice.
|
|
1689
|
+
if (this.surfaceLayer) {
|
|
1690
|
+
void this.buildSurface(v, zIndex).catch((err) => console.error('[napari-js] setZIndex surface failed:', err));
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
// Volume / isosurface: step the volume's z plane in place.
|
|
1694
|
+
if (this.volumeView) {
|
|
1695
|
+
v.dims.z = zIndex;
|
|
1696
|
+
this.scheduleReadback();
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
// Tiled 2D image: just move the dims plane — the tiled visual fetches the new slice's tiles
|
|
1700
|
+
// (cached per z), no layer rebuild. Refresh the coarse histogram sample for the new slice.
|
|
1701
|
+
if (this.tiled) {
|
|
1702
|
+
v.dims.z = zIndex;
|
|
1703
|
+
if (this.descriptor)
|
|
1704
|
+
void this.refreshHistogramSamples(zIndex, this.descriptor);
|
|
1705
|
+
this.scheduleReadback();
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
// 2D image (stitch fallback): re-render the slice (re-fetches per-channel / composite). Branch
|
|
1709
|
+
// of a volume layer (not currentPlotType, which could be stale and silently no-op the swap).
|
|
1710
|
+
// The token lets renderImage drop a superseded scrub so a slow older slice can't clobber a newer one.
|
|
1711
|
+
const req = ++this.sliceReq;
|
|
1712
|
+
void this.renderImage(zIndex, req)
|
|
1713
|
+
.then(() => {
|
|
1714
|
+
if (req === this.sliceReq)
|
|
1715
|
+
this.scheduleReadback();
|
|
1716
|
+
})
|
|
1717
|
+
.catch((err) => console.error('[napari-js] setZIndex slice failed:', err));
|
|
1718
|
+
}
|
|
1719
|
+
setStackLoading(stackLoading) {
|
|
1720
|
+
this.stackLoading$.next(stackLoading);
|
|
1721
|
+
}
|
|
1722
|
+
/** Cancel in-flight frame loading: bump the load generation so the volume-assembly / surface
|
|
1723
|
+
* preload workers stop fetching more frames, and clear the loading flag + progress. */
|
|
1724
|
+
cancelLoading() {
|
|
1725
|
+
this.loadToken++;
|
|
1726
|
+
this.stackLoading$.next(false);
|
|
1727
|
+
this.stackLoadingProgress$.next(0);
|
|
1728
|
+
}
|
|
1729
|
+
isStackLoading() {
|
|
1730
|
+
return this.stackLoading$.asObservable();
|
|
1731
|
+
}
|
|
1732
|
+
getStackLoadingProgress() {
|
|
1733
|
+
return this.stackLoadingProgress$.asObservable();
|
|
1734
|
+
}
|
|
1735
|
+
getTrueImageSize() {
|
|
1736
|
+
return this.imageW > 0 && this.imageH > 0 ? { width: this.imageW, height: this.imageH } : null;
|
|
1737
|
+
}
|
|
1738
|
+
getCurrentImage() {
|
|
1739
|
+
return Promise.resolve(null);
|
|
1740
|
+
}
|
|
1741
|
+
getDisplayedPixelData() {
|
|
1742
|
+
return this.lastPixels;
|
|
1743
|
+
}
|
|
1744
|
+
getDisplayedSourceRect() {
|
|
1745
|
+
const v = this.viewer;
|
|
1746
|
+
const size = this.getTrueImageSize();
|
|
1747
|
+
if (!v || !size)
|
|
1748
|
+
return null;
|
|
1749
|
+
const r = v.visibleWorldRect();
|
|
1750
|
+
const x = Math.max(0, r.x);
|
|
1751
|
+
const y = Math.max(0, r.y);
|
|
1752
|
+
return {
|
|
1753
|
+
x,
|
|
1754
|
+
y,
|
|
1755
|
+
width: Math.min(size.width, r.x + r.width) - x,
|
|
1756
|
+
height: Math.min(size.height, r.y + r.height) - y,
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
downloadImage() {
|
|
1760
|
+
void this.exportComposite();
|
|
1761
|
+
}
|
|
1762
|
+
setPlotType(plotType) {
|
|
1763
|
+
this.currentPlotType = plotType;
|
|
1764
|
+
}
|
|
1765
|
+
/** Map a Plotly-style 3D drag mode onto napari-js's camera drag mode. */
|
|
1766
|
+
setSurfaceDragMode(mode) {
|
|
1767
|
+
if (!this.viewer)
|
|
1768
|
+
return;
|
|
1769
|
+
const m = mode === 'pan' ? 'pan' : mode === 'zoom' ? 'zoom' : 'rotate'; // orbit/turntable → rotate
|
|
1770
|
+
this.viewer.setCameraDragMode(m);
|
|
1771
|
+
}
|
|
1772
|
+
/** Re-frame the 3D camera on the current 3D layer. A surface mesh sits in the positive octant
|
|
1773
|
+
* (not centred), so frame it by its own bounds; a volume/isosurface uses its centred box. */
|
|
1774
|
+
resetSurfaceCamera() {
|
|
1775
|
+
if (!this.viewer)
|
|
1776
|
+
return;
|
|
1777
|
+
const meshLayer = this.surfaceLayer ?? this.scatter3dLayer;
|
|
1778
|
+
if (meshLayer) {
|
|
1779
|
+
const b = meshLayer.bounds();
|
|
1780
|
+
this.viewer.camera3d.target = b.center;
|
|
1781
|
+
this.viewer.camera3d.distance = Math.max(b.radius * 2.5, 1e-3);
|
|
1782
|
+
this.viewer.requestRender();
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
const d = this.volumeDims;
|
|
1786
|
+
if (d)
|
|
1787
|
+
this.viewer.camera3d.frame(d.width, d.height, d.depth);
|
|
1788
|
+
}
|
|
1789
|
+
getAutoscaleEvent() {
|
|
1790
|
+
return this.autoscaleEvent$.asObservable();
|
|
1791
|
+
}
|
|
1792
|
+
getPlotTypeDescriptors() {
|
|
1793
|
+
// The WebGPU napari-js options, offered alongside (not replacing) the OSD/Plotly types.
|
|
1794
|
+
return [
|
|
1795
|
+
PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_IMAGE],
|
|
1796
|
+
PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_SCATTER],
|
|
1797
|
+
PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_SURFACE],
|
|
1798
|
+
PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_SCATTER3D],
|
|
1799
|
+
PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_VOLUME],
|
|
1800
|
+
PLOT_TYPE_DESCRIPTORS[PlotType.NAPARI_ISOSURFACE],
|
|
1801
|
+
];
|
|
1802
|
+
}
|
|
1803
|
+
/** The napari 3D decimate factor (1 = full … 8 = ⅛). Read by the toolbar to init the control. */
|
|
1804
|
+
getResolutionScale() {
|
|
1805
|
+
return this.resolutionScale;
|
|
1806
|
+
}
|
|
1807
|
+
/** Set the decimate factor for the napari 3D types. Takes effect on the next (re)load — the host
|
|
1808
|
+
* re-plots after calling this, since decimation changes the fetched/assembled data. */
|
|
1809
|
+
setResolutionScale(scale) {
|
|
1810
|
+
this.resolutionScale = Math.max(1, Math.round(scale));
|
|
1811
|
+
}
|
|
1812
|
+
getIntensityProfile$() {
|
|
1813
|
+
return this.intensityProfile$.asObservable();
|
|
1814
|
+
}
|
|
1815
|
+
renderIntensityInset(_divId, _profiles) {
|
|
1816
|
+
/* Plotly owns the intensity inset */
|
|
1817
|
+
}
|
|
1818
|
+
/** Read the displayed composite into `lastPixels` (the pixel-tools' source) + emit the clamped
|
|
1819
|
+
* visible region. A fresh PixelData object means cachedImageData() rebuilds automatically. */
|
|
1820
|
+
async runReadback() {
|
|
1821
|
+
const v = this.viewer;
|
|
1822
|
+
if (!v)
|
|
1823
|
+
return;
|
|
1824
|
+
try {
|
|
1825
|
+
const px = await v.readDisplayedPixels();
|
|
1826
|
+
if (this.viewer !== v)
|
|
1827
|
+
return;
|
|
1828
|
+
const rect = v.visibleWorldRect(); // capture WITH the pixels (same camera)
|
|
1829
|
+
this.lastPixels = px;
|
|
1830
|
+
this.lastPixelsRect = rect;
|
|
1831
|
+
const size = this.getTrueImageSize();
|
|
1832
|
+
if (size && rect) {
|
|
1833
|
+
const x = Math.max(0, Math.min(size.width, rect.x));
|
|
1834
|
+
const y = Math.max(0, Math.min(size.height, rect.y));
|
|
1835
|
+
const width = Math.max(1, Math.min(size.width - x, rect.width));
|
|
1836
|
+
const height = Math.max(1, Math.min(size.height - y, rect.height));
|
|
1837
|
+
this.viewportChange$.next({ x, y, width, height });
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
catch {
|
|
1841
|
+
/* readback unavailable */
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
scheduleReadback() {
|
|
1845
|
+
if (!this.viewer)
|
|
1846
|
+
return;
|
|
1847
|
+
if (typeof requestAnimationFrame === 'function')
|
|
1848
|
+
requestAnimationFrame(() => void this.runReadback());
|
|
1849
|
+
else
|
|
1850
|
+
setTimeout(() => void this.runReadback(), 0);
|
|
1851
|
+
}
|
|
1852
|
+
/** Debounced readback — armed on camera changes so `lastPixels` tracks the current view after a
|
|
1853
|
+
* pan/zoom settles (and after tiled levels finish loading), which the on-canvas pixel tools
|
|
1854
|
+
* (wand/brush/SAM) read synchronously. Coalesces rapid changes. */
|
|
1855
|
+
armReadback(delayMs = 250) {
|
|
1856
|
+
if (this.readbackTimer != null)
|
|
1857
|
+
clearTimeout(this.readbackTimer);
|
|
1858
|
+
this.readbackTimer = setTimeout(() => {
|
|
1859
|
+
this.readbackTimer = null;
|
|
1860
|
+
void this.runReadback();
|
|
1861
|
+
}, delayMs);
|
|
1862
|
+
}
|
|
1863
|
+
// ── IRegionStore + classification colours ──────────────────────────────────
|
|
1864
|
+
// Inherited from BaseStoreVisualizer — pure delegations to the shared
|
|
1865
|
+
// RegionStore / VisualizerStore (identical to the OSD backend).
|
|
1866
|
+
// ── Pixel tools: reuse the shared, backend-agnostic tool services with a napari host ───────
|
|
1867
|
+
/** Build the coordinate transform + tool hosts for the current viewer (called from plot()). The
|
|
1868
|
+
* pixel tools read displayed pixels synchronously from the last readback and convert pointer
|
|
1869
|
+
* coords via the napari camera, exactly mirroring the OSD host. */
|
|
1870
|
+
buildToolHosts() {
|
|
1871
|
+
if (!this.viewer)
|
|
1872
|
+
return;
|
|
1873
|
+
this.coordTransform = new NapariCoordinateTransform(this.viewer, () => !!this.viewer && this.imageW > 0);
|
|
1874
|
+
const regions = this.regionStore;
|
|
1875
|
+
this.wandHost = {
|
|
1876
|
+
getRegions: () => regions.getRegions(),
|
|
1877
|
+
setRegions: (r) => regions.setRegions(r),
|
|
1878
|
+
getCachedImageData: () => this.cachedImageData(),
|
|
1879
|
+
getActiveFrameIndex: () => this.loaded?.z ?? 0,
|
|
1880
|
+
getOverlayContainer: () => this.host,
|
|
1881
|
+
getCoordinateTransform: () => this.coordTransform,
|
|
1882
|
+
getFileName: () => this.loaded?.filename,
|
|
1883
|
+
getShapeColor: () => regions.getShapeColor(),
|
|
1884
|
+
};
|
|
1885
|
+
this.eraserHost = {
|
|
1886
|
+
getRegions: () => regions.getRegions(),
|
|
1887
|
+
setRegions: (r) => regions.setRegions(r),
|
|
1888
|
+
invalidateWandRegion: () => this.wandTool.clearActiveRegion(),
|
|
1889
|
+
getOverlayContainer: () => this.host,
|
|
1890
|
+
getCoordinateTransform: () => this.coordTransform,
|
|
1891
|
+
getCachedImageRatio: () => this.cachedImageData()?.ratios[0] ?? 1,
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
zoomBoxHost() {
|
|
1895
|
+
return {
|
|
1896
|
+
getPlotDiv: () => this.plotDivId,
|
|
1897
|
+
pixelToData: (px, py) => {
|
|
1898
|
+
const rect = this.host?.getBoundingClientRect();
|
|
1899
|
+
if (!this.viewer || !rect)
|
|
1900
|
+
return { x: 0, y: 0 };
|
|
1901
|
+
const [x, y] = this.viewer.canvasToWorld(rect.left + px, rect.top + py);
|
|
1902
|
+
return { x, y };
|
|
1903
|
+
},
|
|
1904
|
+
applyZoomToBox: (coords) => this.applyZoomToBox(coords),
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
/** Zoom/pan the camera to fit a data-space rectangle `[xMin, xMax, yMax, yMin]`. */
|
|
1908
|
+
applyZoomToBox(coordinates) {
|
|
1909
|
+
const v = this.viewer;
|
|
1910
|
+
if (!v || !this.canvas || coordinates.length < 4)
|
|
1911
|
+
return;
|
|
1912
|
+
const [xMin, xMax, yA, yB] = coordinates;
|
|
1913
|
+
const x0 = Math.min(xMin, xMax);
|
|
1914
|
+
const x1 = Math.max(xMin, xMax);
|
|
1915
|
+
const y0 = Math.min(yA, yB);
|
|
1916
|
+
const y1 = Math.max(yA, yB);
|
|
1917
|
+
const w = Math.max(1, x1 - x0);
|
|
1918
|
+
const h = Math.max(1, y1 - y0);
|
|
1919
|
+
const vw = this.canvas.clientWidth || w;
|
|
1920
|
+
const vh = this.canvas.clientHeight || h;
|
|
1921
|
+
v.camera.center = [(x0 + x1) / 2, (y0 + y1) / 2];
|
|
1922
|
+
v.camera.zoom = Math.min(vw / w, vh / h);
|
|
1923
|
+
v.requestRender();
|
|
1924
|
+
}
|
|
1925
|
+
/** Build CachedImageData from the most recent readback (RGBA device pixels → [y][x]=[r,g,b]),
|
|
1926
|
+
* with ratios/origin mapping image coords ↔ readback pixels. Cached until the readback changes. */
|
|
1927
|
+
cachedImageData() {
|
|
1928
|
+
const px = this.lastPixels;
|
|
1929
|
+
if (!px || !this.viewer)
|
|
1930
|
+
return null;
|
|
1931
|
+
if (this.cachedImage && this.cachedImageSource === px)
|
|
1932
|
+
return this.cachedImage;
|
|
1933
|
+
const w = px.width;
|
|
1934
|
+
const h = px.height;
|
|
1935
|
+
const data = px.data;
|
|
1936
|
+
const matrix = new Array(h);
|
|
1937
|
+
for (let y = 0; y < h; y++) {
|
|
1938
|
+
const row = new Array(w);
|
|
1939
|
+
const base = y * w * 4;
|
|
1940
|
+
for (let x = 0; x < w; x++) {
|
|
1941
|
+
const o = base + x * 4;
|
|
1942
|
+
row[x] = [data[o], data[o + 1], data[o + 2]];
|
|
1943
|
+
}
|
|
1944
|
+
matrix[y] = row;
|
|
1945
|
+
}
|
|
1946
|
+
// Use the rect captured WITH this readback (not the live one) so ratios/origin match the
|
|
1947
|
+
// matrix's camera — otherwise a pan/zoom since the readback mis-scales the traced region.
|
|
1948
|
+
const rect = this.lastPixelsRect ?? this.viewer.visibleWorldRect();
|
|
1949
|
+
this.cachedImage = {
|
|
1950
|
+
frames: [matrix],
|
|
1951
|
+
width: w,
|
|
1952
|
+
height: h,
|
|
1953
|
+
ratios: [rect.width / w, rect.height / h],
|
|
1954
|
+
isGrayscale: false,
|
|
1955
|
+
originX: rect.x,
|
|
1956
|
+
originY: rect.y,
|
|
1957
|
+
};
|
|
1958
|
+
this.cachedImageSource = px;
|
|
1959
|
+
return this.cachedImage;
|
|
1960
|
+
}
|
|
1961
|
+
// ── IToolController ────────────────────────────────────────────────────────
|
|
1962
|
+
// Control gating: a tool that ACTIVATES disables the camera controls; deactivation is a no-op
|
|
1963
|
+
// because the host always calls the region overlay's setMode FIRST in each toggle (which sets
|
|
1964
|
+
// the baseline enabled/disabled), so re-enabling here would fight a freshly-activated draw mode.
|
|
1965
|
+
setWandMode(active, options) {
|
|
1966
|
+
if (!this.viewer)
|
|
1967
|
+
return; // no plot yet → nothing to drive
|
|
1968
|
+
if (!active) {
|
|
1969
|
+
this.wandTool.setMode(false);
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
this.viewer?.setControlsEnabled(false);
|
|
1973
|
+
this.cachedImageSource = null;
|
|
1974
|
+
this.armReadback(0);
|
|
1975
|
+
if (this.wandHost)
|
|
1976
|
+
this.wandTool.bindHost(this.wandHost);
|
|
1977
|
+
this.wandTool.setMode(true, (options ?? {}));
|
|
1978
|
+
}
|
|
1979
|
+
setWandOptions(options) {
|
|
1980
|
+
this.wandTool.setOptions((options ?? {}));
|
|
1981
|
+
}
|
|
1982
|
+
clearActiveWandRegion() {
|
|
1983
|
+
this.wandTool.clearActiveRegion();
|
|
1984
|
+
}
|
|
1985
|
+
setBrushMode(active, options) {
|
|
1986
|
+
if (!this.viewer)
|
|
1987
|
+
return;
|
|
1988
|
+
if (!active) {
|
|
1989
|
+
this.brushTool.setMode(false);
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1992
|
+
this.viewer?.setControlsEnabled(false);
|
|
1993
|
+
this.cachedImageSource = null;
|
|
1994
|
+
this.armReadback(0);
|
|
1995
|
+
if (this.wandHost)
|
|
1996
|
+
this.brushTool.bindHost(this.wandHost);
|
|
1997
|
+
this.brushTool.setMode(true, (options ?? {}));
|
|
1998
|
+
}
|
|
1999
|
+
setBrushOptions(options) {
|
|
2000
|
+
if (options?.size != null)
|
|
2001
|
+
this.brushTool.setSize(options.size);
|
|
2002
|
+
}
|
|
2003
|
+
setVertexEraserMode(active) {
|
|
2004
|
+
if (!this.viewer)
|
|
2005
|
+
return;
|
|
2006
|
+
if (active) {
|
|
2007
|
+
this.viewer?.setControlsEnabled(false);
|
|
2008
|
+
this.cachedImageSource = null;
|
|
2009
|
+
if (this.eraserHost)
|
|
2010
|
+
this.eraserTool.bindHost(this.eraserHost);
|
|
2011
|
+
}
|
|
2012
|
+
this.eraserTool.setMode(active);
|
|
2013
|
+
}
|
|
2014
|
+
setVertexEraserRadius(radius) {
|
|
2015
|
+
this.eraserTool.setRadius(radius);
|
|
2016
|
+
}
|
|
2017
|
+
setZoomToBoxMode(active) {
|
|
2018
|
+
if (!this.viewer)
|
|
2019
|
+
return;
|
|
2020
|
+
if (active) {
|
|
2021
|
+
this.viewer?.setControlsEnabled(false);
|
|
2022
|
+
this.zoomToBoxTool.bindHost(this.zoomBoxHost());
|
|
2023
|
+
}
|
|
2024
|
+
this.zoomToBoxTool.setMode(active);
|
|
2025
|
+
}
|
|
2026
|
+
// SAM / cellpose — server round-trips that read the drawn rectangles + displayed pixels through
|
|
2027
|
+
// the same wand host (rectangles from the RegionStore, image from the readback).
|
|
2028
|
+
async segmentRectangles() {
|
|
2029
|
+
if (!this.viewer || !this.wandHost)
|
|
2030
|
+
return 0;
|
|
2031
|
+
await this.runReadback(); // the SAM embedding samples the currently-displayed image
|
|
2032
|
+
this.cachedImageSource = null;
|
|
2033
|
+
this.samTool.bindHost(this.wandHost);
|
|
2034
|
+
return this.samTool.segmentBoxes();
|
|
2035
|
+
}
|
|
2036
|
+
async segmentRectanglesCellpose() {
|
|
2037
|
+
if (!this.viewer || !this.wandHost || !this.cellSegmenter)
|
|
2038
|
+
return 0;
|
|
2039
|
+
await this.runReadback();
|
|
2040
|
+
this.cachedImageSource = null;
|
|
2041
|
+
this.cellSegmentTool.bindHost(this.wandHost);
|
|
2042
|
+
return this.cellSegmentTool.segmentBoxes(this.cellSegmenter);
|
|
2043
|
+
}
|
|
2044
|
+
setSamModel(id) {
|
|
2045
|
+
this.samTool.setModel(id);
|
|
2046
|
+
this.samPointTool.setModel(id);
|
|
2047
|
+
}
|
|
2048
|
+
setSamPointMode(active) {
|
|
2049
|
+
if (!this.viewer)
|
|
2050
|
+
return;
|
|
2051
|
+
if (active) {
|
|
2052
|
+
this.viewer?.setControlsEnabled(false);
|
|
2053
|
+
this.cachedImageSource = null;
|
|
2054
|
+
this.armReadback(0); // refresh the readback the click's embedding will sample
|
|
2055
|
+
if (this.wandHost)
|
|
2056
|
+
this.samPointTool.bindHost(this.wandHost);
|
|
2057
|
+
}
|
|
2058
|
+
this.samPointTool.setMode(active);
|
|
2059
|
+
}
|
|
2060
|
+
commitSamPoints() {
|
|
2061
|
+
this.samPointTool.commit();
|
|
2062
|
+
}
|
|
2063
|
+
clearSamPoints() {
|
|
2064
|
+
this.samPointTool.clear();
|
|
2065
|
+
}
|
|
2066
|
+
// ── IDisplayOptions ───────────────────────────────────────────────────────
|
|
2067
|
+
// Inherited from BaseStoreVisualizer — pure delegations to the shared
|
|
2068
|
+
// VisualizerStore (identical to the OSD backend).
|
|
2069
|
+
// ── IIntensitySampling: Plotly owns sampling; emit viewport changes ───────
|
|
2070
|
+
ensureIntensitySampling(_imageInfo, _zIndex) {
|
|
2071
|
+
return Promise.resolve();
|
|
2072
|
+
}
|
|
2073
|
+
refreshIntensitySamplingForRoi(_x, _y, _width, _height, _zIndex) { }
|
|
2074
|
+
getViewportChange$() {
|
|
2075
|
+
return this.viewportChange$.asObservable();
|
|
2076
|
+
}
|
|
2077
|
+
// ── IVisualizer composite members ─────────────────────────────────────────
|
|
2078
|
+
getRegionOverlay() {
|
|
2079
|
+
return this.regionOverlay;
|
|
2080
|
+
}
|
|
2081
|
+
getIsosurfaceControls() {
|
|
2082
|
+
if (!this.volumeView)
|
|
2083
|
+
return null;
|
|
2084
|
+
return {
|
|
2085
|
+
setIsoRange: (isoMin, isoMax) => {
|
|
2086
|
+
// Apply to every channel's volume layer.
|
|
2087
|
+
for (const layer of this.volumeView?.layers ?? []) {
|
|
2088
|
+
layer.contrastLimits = [isoMin, isoMax];
|
|
2089
|
+
layer.rendering = 'iso';
|
|
2090
|
+
layer.isoThreshold = 0.5;
|
|
2091
|
+
}
|
|
2092
|
+
this.viewer?.requestRender();
|
|
2093
|
+
},
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
getIntensityControls() {
|
|
2097
|
+
return null;
|
|
2098
|
+
}
|
|
2099
|
+
getSurface3dControls() {
|
|
2100
|
+
if (!this.volumeView && !this.surfaceLayer && !this.scatter3dLayer)
|
|
2101
|
+
return null;
|
|
2102
|
+
return {
|
|
2103
|
+
setSurfaceDragMode: (mode) => this.setSurfaceDragMode(mode),
|
|
2104
|
+
resetSurfaceCamera: () => this.resetSurfaceCamera(),
|
|
2105
|
+
setAxesVisible: (visible) => {
|
|
2106
|
+
this.axesVisible = visible;
|
|
2107
|
+
this.axesLabels?.setVisible(visible);
|
|
2108
|
+
if (this.axesLayer) {
|
|
2109
|
+
this.axesLayer.visible = visible;
|
|
2110
|
+
this.viewer?.requestRender();
|
|
2111
|
+
}
|
|
2112
|
+
},
|
|
2113
|
+
axesVisible: () => this.axesVisible,
|
|
2114
|
+
// Surface wireframe (napari-js surface only) — a live layer property, no rebuild needed.
|
|
2115
|
+
setWireframe: (on) => {
|
|
2116
|
+
this.surfaceWireframe = on;
|
|
2117
|
+
if (this.surfaceLayer) {
|
|
2118
|
+
this.surfaceLayer.wireframe = on;
|
|
2119
|
+
this.viewer?.requestRender();
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
wireframe: () => this.surfaceWireframe,
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
getHistogram(channelIndex, bins) {
|
|
2126
|
+
const v = this.viewer;
|
|
2127
|
+
if (!v)
|
|
2128
|
+
return null;
|
|
2129
|
+
// Volume / isosurface: intensity histogram of the assembled (downsampled) uint8 volume for the
|
|
2130
|
+
// requested channel (multichannel) or the single grayscale volume (key 0).
|
|
2131
|
+
if (this.volumeChannelData.size) {
|
|
2132
|
+
const data = this.volumeChannelData.get(channelIndex) ?? this.volumeChannelData.get(0);
|
|
2133
|
+
return data ? this.toIHistogram(histogramScalar(data, bins, 0, 255)) : null;
|
|
2134
|
+
}
|
|
2135
|
+
// Tiled mode has no full in-memory pixels → use the coarse per-channel sample (RGB: readback).
|
|
2136
|
+
if (this.tiled) {
|
|
2137
|
+
if (this.imageMode === 'rgb')
|
|
2138
|
+
return this.rgbHistogram(channelIndex, bins);
|
|
2139
|
+
const sample = this.histSamples.get(this.imageMode === 'grayscale' ? 0 : channelIndex);
|
|
2140
|
+
return sample ? this.toIHistogram(histogramScalar(sample, bins, 0, 255)) : null;
|
|
2141
|
+
}
|
|
2142
|
+
// Grayscale/multichannel (stitch): native per-channel histogram straight from the in-memory
|
|
2143
|
+
// scalar layer (no GPU readback). RGB: bin the displayed pixels' R/G/B byte (8-bit client path).
|
|
2144
|
+
const layer = this.channelView?.layers[this.imageMode === 'grayscale' ? 0 : channelIndex];
|
|
2145
|
+
if (layer) {
|
|
2146
|
+
const h = v.layerHistogram(layer, bins);
|
|
2147
|
+
if (h)
|
|
2148
|
+
return this.toIHistogram(h);
|
|
2149
|
+
}
|
|
2150
|
+
if (this.imageMode === 'rgb')
|
|
2151
|
+
return this.rgbHistogram(channelIndex, bins);
|
|
2152
|
+
return null;
|
|
2153
|
+
}
|
|
2154
|
+
getHistogram$(channelIndex, bins) {
|
|
2155
|
+
// >8-bit channels: fetch the true native distribution from the server (the displayed pixels
|
|
2156
|
+
// are 8-bit, so the client histogram would be clipped). 8-bit channels use the client path.
|
|
2157
|
+
const bitDepth = this.descriptor?.channelInfo?.[channelIndex]?.bitDepth ?? 8;
|
|
2158
|
+
if (bitDepth > 8) {
|
|
2159
|
+
const z = this.loaded?.z ?? 0;
|
|
2160
|
+
const key = `${z}|${channelIndex}`;
|
|
2161
|
+
const cached = this.nativeHistograms.get(key);
|
|
2162
|
+
if (cached)
|
|
2163
|
+
return of(cached);
|
|
2164
|
+
return from(this.fetchNativeHistogram(channelIndex, bins, z, key));
|
|
2165
|
+
}
|
|
2166
|
+
return of(this.getHistogram(channelIndex, bins));
|
|
2167
|
+
}
|
|
2168
|
+
/** Fetch + cache one channel's native-bit-depth histogram from `GET /histogram` (>8-bit). */
|
|
2169
|
+
async fetchNativeHistogram(channel, bins, z, key) {
|
|
2170
|
+
const infoB64 = this.tiles.getSelectedInfoB64();
|
|
2171
|
+
if (!infoB64)
|
|
2172
|
+
return null;
|
|
2173
|
+
const headers = await this.tiles
|
|
2174
|
+
.getAuthHeaders()
|
|
2175
|
+
.catch(() => ({}));
|
|
2176
|
+
const url = `${this.api}histogram?info=${infoB64}&channel=${channel}&z=${z}&bins=${bins}`;
|
|
2177
|
+
try {
|
|
2178
|
+
const resp = await fetch(url, { headers });
|
|
2179
|
+
if (!resp.ok)
|
|
2180
|
+
return null; // 202 (still caching) or transient → null; the pane retries
|
|
2181
|
+
const hi = (await resp.json());
|
|
2182
|
+
if (!hi?.counts)
|
|
2183
|
+
return null;
|
|
2184
|
+
const out = {
|
|
2185
|
+
bins: hi.counts.map((_, i) => hi.rangeMin + i * hi.binWidth),
|
|
2186
|
+
counts: hi.counts,
|
|
2187
|
+
max: hi.counts.reduce((m, c) => (c > m ? c : m), 0),
|
|
2188
|
+
bitDepth: hi.bitDepth,
|
|
2189
|
+
rangeMin: hi.rangeMin,
|
|
2190
|
+
rangeMax: hi.rangeMax,
|
|
2191
|
+
observedMin: hi.observedMin,
|
|
2192
|
+
observedMax: hi.observedMax,
|
|
2193
|
+
};
|
|
2194
|
+
this.nativeHistograms.set(key, out);
|
|
2195
|
+
return out;
|
|
2196
|
+
}
|
|
2197
|
+
catch (err) {
|
|
2198
|
+
console.warn('[napari-js] native histogram fetch failed', err);
|
|
2199
|
+
return null;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
/** Per-channel histogram of the displayed RGB composite (R/G/B byte), from the last readback. */
|
|
2203
|
+
rgbHistogram(channelIndex, bins) {
|
|
2204
|
+
const px = this.lastPixels;
|
|
2205
|
+
if (!px)
|
|
2206
|
+
return null;
|
|
2207
|
+
const band = Math.max(0, Math.min(2, channelIndex));
|
|
2208
|
+
const n = Math.max(1, Math.min(256, Math.floor(bins) || 256));
|
|
2209
|
+
const counts = new Array(n).fill(0);
|
|
2210
|
+
const data = px.data;
|
|
2211
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
2212
|
+
if (data[i + 3] === 0)
|
|
2213
|
+
continue; // skip transparent padding
|
|
2214
|
+
counts[Math.min(n - 1, (data[i + band] * n) >> 8)]++;
|
|
2215
|
+
}
|
|
2216
|
+
const binsArr = Array.from({ length: n }, (_, i) => (i * 256) / n);
|
|
2217
|
+
return { bins: binsArr, counts, max: counts.reduce((m, c) => (c > m ? c : m), 0) };
|
|
2218
|
+
}
|
|
2219
|
+
exportComposite() {
|
|
2220
|
+
const v = this.viewer;
|
|
2221
|
+
if (!v)
|
|
2222
|
+
return;
|
|
2223
|
+
void v.screenshot().then((blob) => {
|
|
2224
|
+
const url = URL.createObjectURL(blob);
|
|
2225
|
+
const a = document.createElement('a');
|
|
2226
|
+
a.href = url;
|
|
2227
|
+
a.download = 'napari-js.png';
|
|
2228
|
+
a.click();
|
|
2229
|
+
URL.revokeObjectURL(url);
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
/** Native-bit-depth (16/32-bit) multi-band TIFF export via the server `/export/tiff` endpoint —
|
|
2233
|
+
* the displayed PNG is an 8-bit figure, this preserves the true pixel values. Visible channels
|
|
2234
|
+
* only (omitted when all are visible → server default). Mirrors the OSD backend. */
|
|
2235
|
+
async exportData() {
|
|
2236
|
+
const infoB64 = this.tiles.getSelectedInfoB64();
|
|
2237
|
+
if (!infoB64)
|
|
2238
|
+
return;
|
|
2239
|
+
const states = this.store.currentChannelStates();
|
|
2240
|
+
const visible = states.filter((c) => c.visible).map((c) => c.index);
|
|
2241
|
+
const chParam = visible.length && visible.length < states.length ? `&channels=${visible.join(',')}` : '';
|
|
2242
|
+
const z = this.loaded?.z ?? 0;
|
|
2243
|
+
const url = `${this.api}export/tiff?info=${infoB64}&z=${z}${chParam}`;
|
|
2244
|
+
const stem = (this.loaded?.filename || 'image').replace(/\.[^.]+$/, '');
|
|
2245
|
+
const headers = await this.tiles
|
|
2246
|
+
.getAuthHeaders()
|
|
2247
|
+
.catch(() => ({}));
|
|
2248
|
+
try {
|
|
2249
|
+
const resp = await fetch(url, { headers });
|
|
2250
|
+
if (resp.status === 202) {
|
|
2251
|
+
console.warn('[napari-js] 16-bit export: file still caching — try again shortly.');
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
if (!resp.ok)
|
|
2255
|
+
throw new Error(`status ${resp.status}`);
|
|
2256
|
+
saveAs(await resp.blob(), `${stem}_16bit.ome.tif`);
|
|
2257
|
+
}
|
|
2258
|
+
catch (err) {
|
|
2259
|
+
console.warn('[napari-js] 16-bit TIFF export failed', err);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
unsubscribe() {
|
|
2263
|
+
this.reset();
|
|
2264
|
+
}
|
|
2265
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: NapariVisualizerService, deps: [{ token: TILE_ACCESS_PORT }, { token: i1.VisualizerStore }, { token: i2.RegionStore }, { token: i3.WandToolService }, { token: i4.BrushToolService }, { token: i5.VertexEraserToolService }, { token: i6.ZoomToBoxToolService }, { token: i7.SamToolService }, { token: i8.SamPointToolService }, { token: i9.CellSegmentToolService }, { token: CELL_SEGMENTER, optional: true }, { token: i10.SimpleSliceAccessService }, { token: VIZ_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2266
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: NapariVisualizerService, providedIn: 'root' }); }
|
|
2267
|
+
}
|
|
2268
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: NapariVisualizerService, decorators: [{
|
|
2269
|
+
type: Injectable,
|
|
2270
|
+
args: [{ providedIn: 'root' }]
|
|
2271
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
2272
|
+
type: Inject,
|
|
2273
|
+
args: [TILE_ACCESS_PORT]
|
|
2274
|
+
}] }, { type: i1.VisualizerStore }, { type: i2.RegionStore }, { type: i3.WandToolService }, { type: i4.BrushToolService }, { type: i5.VertexEraserToolService }, { type: i6.ZoomToBoxToolService }, { type: i7.SamToolService }, { type: i8.SamPointToolService }, { type: i9.CellSegmentToolService }, { type: undefined, decorators: [{
|
|
2275
|
+
type: Optional
|
|
2276
|
+
}, {
|
|
2277
|
+
type: Inject,
|
|
2278
|
+
args: [CELL_SEGMENTER]
|
|
2279
|
+
}] }, { type: i10.SimpleSliceAccessService }, { type: undefined, decorators: [{
|
|
2280
|
+
type: Inject,
|
|
2281
|
+
args: [VIZ_CONFIG]
|
|
2282
|
+
}] }] });
|
|
2283
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmFwYXJpLXZpc3VhbGl6ZXIuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvaW1wbGVtZW50YXRpb25zL25hcGFyaS1qcy9uYXBhcmktdmlzdWFsaXplci5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM3RCxPQUFPLEVBQWMsZUFBZSxFQUFFLE9BQU8sRUFBZ0IsYUFBYSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFbkcsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUNwQyxPQUFPLEVBQ0wsTUFBTSxFQUNOLGVBQWUsRUFDZixlQUFlLEVBQ2YsWUFBWSxFQUNaLGVBQWUsRUFDZixXQUFXLEVBQ1gscUJBQXFCLEVBQ3JCLHNCQUFzQixHQUN2QixNQUFNLFdBQVcsQ0FBQztBQWdCbkIsT0FBTyxFQUFFLGdCQUFnQixFQUFPLE1BQU0sOEJBQThCLENBQUM7QUFDckUsT0FBTyxFQUNMLFFBQVEsRUFFUixxQkFBcUIsRUFDckIsVUFBVSxFQUNWLGtCQUFrQixFQUNsQixlQUFlLEVBQ2YsZUFBZSxFQUNmLGlCQUFpQixFQUNqQix1QkFBdUIsR0FDeEIsTUFBTSwyQkFBMkIsQ0FBQztBQVNuQyxPQUFPLEVBRUwsYUFBYSxFQUNiLGNBQWMsR0FDZixNQUFNLHVDQUF1QyxDQUFDO0FBSS9DLE9BQU8sRUFBRSxVQUFVLEVBQWEsTUFBTSw0QkFBNEIsQ0FBQztBQUNuRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQWtCLE1BQU0sd0NBQXdDLENBQUM7QUFDMUYsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDL0QsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDMUUsT0FBTyxFQUFF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MkIsQ0FBQyxDQUFDO1FBQy9DLElBQUksQ0FBQztZQUNILE1BQU0sSUFBSSxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsRUFBRSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDM0MsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDO2dCQUN4QixPQUFPLENBQUMsSUFBSSxDQUFDLG9FQUFvRSxDQUFDLENBQUM7Z0JBQ25GLE9BQU87WUFDVCxDQUFDO1lBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFO2dCQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsVUFBVSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUN2RCxNQUFNLENBQUMsTUFBTSxJQUFJLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxJQUFJLGdCQUFnQixDQUFDLENBQUM7UUFDckQsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixPQUFPLENBQUMsSUFBSSxDQUFDLHVDQUF1QyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzdELENBQUM7SUFDSCxDQUFDO0lBQ0QsV0FBVztRQUNULElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNmLENBQUM7OEdBMXVFVSx1QkFBdUIsa0JBb0l4QixnQkFBZ0Isd1RBVUosY0FBYyxzRUFFMUIsVUFBVTtrSEFoSlQsdUJBQXVCLGNBRFYsTUFBTTs7MkZBQ25CLHVCQUF1QjtrQkFEbkMsVUFBVTttQkFBQyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUU7OzBCQXFJN0IsTUFBTTsyQkFBQyxnQkFBZ0I7OzBCQVV2QixRQUFROzswQkFBSSxNQUFNOzJCQUFDLGNBQWM7OzBCQUVqQyxNQUFNOzJCQUFDLFVBQVUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3QsIEluamVjdGFibGUsIE9wdGlvbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCBCZWhhdmlvclN1YmplY3QsIFN1YmplY3QsIFN1YnNjcmlwdGlvbiwgY29tYmluZUxhdGVzdCwgZnJvbSwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEltYWdlIH0gZnJvbSAnaW1hZ2UtanMnO1xuaW1wb3J0IHsgc2F2ZUFzIH0gZnJvbSAnZmlsZS1zYXZlcic7XG5pbXBvcnQge1xuICBWaWV3ZXIsXG4gIGhpc3RvZ3JhbVNjYWxhcixcbiAgY29sb3JtYXBGcm9tTHV0LFxuICB0aW50Q29sb3JtYXAsXG4gIHJldmVyc2VDb2xvcm1hcCxcbiAgaGVpZ2h0RmllbGQsXG4gIE11bHRpQ2hhbm5lbEltYWdlVmlldyxcbiAgTXVsdGlDaGFubmVsVm9sdW1lVmlldyxcbn0gZnJvbSAnbmFwYXJpLWpzJztcbmltcG9ydCB0eXBlIHtcbiAgQXhlc0xheWVyLFxuICBTdXJmYWNlTGF5ZXIsXG4gIFBvaW50c0xheWVyLFxuICBQb2ludHMzRExheWVyLFxuICBUaWxlZFNvdXJjZSxcbiAgVGlsZUtleSxcbiAgUGl4ZWxDaHVuayxcbiAgQ2hhbm5lbFZpZXcsXG4gIFZvbHVtZUNoYW5uZWwsXG4gIENvbG9ybWFwLFxufSBmcm9tICduYXBhcmktanMnO1xuXG5pbXBvcnQgeyBJSW1hZ2VJbmZvIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2ltYWdlLmNvbnRyYWN0JztcbmltcG9ydCB7IElDaGFubmVsU3RhdGUgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2hhbm5lbC1oaXN0b2dyYW0tYXBpLmNvbnRyYWN0JztcbmltcG9ydCB7IGJ1aWxkQ29sb3JtYXBMdXQsIFJnYiB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jb2xvcm1hcC1sdXQnO1xuaW1wb3J0IHtcbiAgUGxvdFR5cGUsXG4gIFBsb3RUeXBlRGVzY3JpcHRvcixcbiAgUExPVF9UWVBFX0RFU0NSSVBUT1JTLFxuICBpc05hcGFyaTNkLFxuICBpc05hcGFyaUlzb3N1cmZhY2UsXG4gIGlzTmFwYXJpU3VyZmFjZSxcbiAgaXNOYXBhcmlTY2F0dGVyLFxuICBpc05hcGFyaVNjYXR0ZXIzZCxcbiAgTkFQQVJJX0RFRkFVTFRfREVDSU1BVEUsXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuaW1wb3J0IHtcbiAgSVZpc3VhbGl6ZXIsXG4gIFBpeGVsRGF0YSxcbiAgSW50ZW5zaXR5UHJvZmlsZSxcbiAgSUlzb3N1cmZhY2VDb250cm9scyxcbiAgSUludGVuc2l0eUNvbnRyb2xzLFxuICBJU3VyZmFjZTNkQ29udHJvbHMsXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy92aXN1YWxpemVyLmNvbnRyYWN0JztcbmltcG9ydCB7XG4gIFZpZXdlckNhcGFiaWxpdGllcyxcbiAgVmlld2VyRmVhdHVyZSxcbiAgY2FwYWJpbGl0aWVzT2YsXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jYXBhYmlsaXRpZXMuY29udHJhY3QnO1xuaW1wb3J0IHsgSVJlZ2lvbk92ZXJsYXkgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvcmVnaW9uLW92ZXJsYXkuY29udHJhY3QnO1xuaW1wb3J0IHsgSUhpc3RvZ3JhbSB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jaGFubmVsLWhpc3RvZ3JhbS1hcGkuY29udHJhY3QnO1xuaW1wb3J0IHsgQ29sb3JtYXBOb2RlLCBJV2FuZE9wdGlvbnMsIElCcnVzaE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvZGlzcGxheS10eXBlcyc7XG5pbXBvcnQgeyBWSVpfQ09ORklHLCBWaXpDb25maWcgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvdml6LWNvbmZpZyc7XG5pbXBvcnQgeyBUSUxFX0FDQ0VTU19QT1JULCBUaWxlQWNjZXNzUG9ydCB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9wb3J0cy90aWxlLWFjY2Vzcy5wb3J0JztcbmltcG9ydCB7IEJhc2VTdG9yZVZpc3VhbGl6ZXIgfSBmcm9tICcuLi9iYXNlLXN0b3JlLXZpc3VhbGl6ZXInO1xuaW1wb3J0IHsgU2ltcGxlU2xpY2VBY2Nlc3NTZXJ2aWNlIH0gZnJvbSAnLi4vc2ltcGxlLXNsaWNlLWFjY2Vzcy5zZXJ2aWNlJztcbmltcG9ydCB7IFZpc3VhbGl6ZXJTdG9yZSB9IGZyb20gJy4uLy4uL3N0b3JlL3Zpc3VhbGl6ZXItc3RvcmUuc2VydmljZSc7XG5pbXBvcnQgeyBSZWdpb25TdG9yZSB9IGZyb20gJy4uLy4uL3N0b3JlL3JlZ2lvbi1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IE5hcGFyaVNjYWxlQmFyLCBmb3JtYXRVbSB9IGZyb20gJy4vbmFwYXJpLXNjYWxlLWJhcic7XG5pbXBvcnQgeyBOYXBhcmlSZWdpb25PdmVybGF5IH0gZnJvbSAnLi9uYXBhcmktcmVnaW9uLW92ZXJsYXknO1xuaW1wb3J0IHsgTmFwYXJpQXhlc0xhYmVscywgQXhpc0xhYmVsU3BlYyB9IGZyb20gJy4vbmFwYXJpLWF4ZXMtbGFiZWxzJztcbmltcG9ydCB7IE5hcGFyaVZvbHVtZVpIYW5kbGUgfSBmcm9tICcuL25hcGFyaS12b2x1bWUtei1oYW5kbGUnO1xuaW1wb3J0IHtcbiAgSUNvb3JkaW5hdGVUcmFuc2Zvcm0sXG59IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9jb29yZGluYXRlLXRyYW5zZm9ybS5jb250cmFjdCc7XG5pbXBvcnQgeyBXYW5kVG9vbFNlcnZpY2UsIFdhbmRUb29sSG9zdCwgQ2FjaGVkSW1hZ2VEYXRhIH0gZnJvbSAnLi4vLi4vdG9vbGJhci93YW5kL3dhbmQtdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IFdhbmRPcHRpb25zIH0gZnJvbSAnLi4vLi4vdG9vbGJhci93YW5kL3dhbmQuc2VydmljZSc7XG5pbXBvcnQgeyBCcnVzaFRvb2xTZXJ2aWNlLCBCcnVzaE9wdGlvbnMgfSBmcm9tICcuLi8uLi90b29sYmFyL2JydXNoL2JydXNoLXRvb2wuc2VydmljZSc7XG5pbXBvcnQge1xuICBWZXJ0ZXhFcmFzZXJUb29sU2VydmljZSxcbiAgVmVydGV4RXJhc2VyVG9vbEhvc3QsXG59IGZyb20gJy4uLy4uL3Rvb2xiYXIvdmVydGV4LWVyYXNlci92ZXJ0ZXgtZXJhc2VyLXRvb2wuc2VydmljZSc7XG5pbXBvcnQge1xuICBab29tVG9Cb3hUb29sU2VydmljZSxcbiAgWm9vbVRvQm94VG9vbEhvc3QsXG59IGZyb20gJy4uLy4uL3Rvb2xiYXIvem9vbS10by1ib3gvem9vbS10by1ib3gtdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IFNhbVRvb2xTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vdG9vbGJhci9zZWdtZW50YXRpb24vc2FtLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBTYW1Qb2ludFRvb2xTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vdG9vbGJhci9zZWdtZW50YXRpb24vc2FtLXBvaW50LXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBDZWxsU2VnbWVudFRvb2xTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vdG9vbGJhci9zZWdtZW50YXRpb24vY2VsbC1zZWdtZW50LXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBJQ2VsbFNlZ21lbnRlciwgQ0VMTF9TRUdNRU5URVIgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2VsbC1zZWdtZW50ZXIuY29udHJhY3QnO1xuXG4vKiogSUNvb3JkaW5hdGVUcmFuc2Zvcm0gb3ZlciB0aGUgbmFwYXJpIGNhbWVyYTogcG9pbnRlciBjbGllbnQgY29vcmRzIOKGlCBpbWFnZS93b3JsZCBjb29yZHMuICovXG5jbGFzcyBOYXBhcmlDb29yZGluYXRlVHJhbnNmb3JtIGltcGxlbWVudHMgSUNvb3JkaW5hdGVUcmFuc2Zvcm0ge1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IHZpZXdlcjoge1xuICAgICAgY2FudmFzVG9Xb3JsZChjbGllbnRYOiBudW1iZXIsIGNsaWVudFk6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl07XG4gICAgICByZWFkb25seSBjYW1lcmE6IHsgem9vbTogbnVtYmVyIH07XG4gICAgfSxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHJlYWR5OiAoKSA9PiBib29sZWFuLFxuICApIHt9XG4gIGNsaWVudFRvRGF0YShjbGllbnRYOiBudW1iZXIsIGNsaWVudFk6IG51bWJlcik6IHsgeDogbnVtYmVyOyB5OiBudW1iZXIgfSB7XG4gICAgY29uc3QgW3gsIHldID0gdGhpcy52aWV3ZXIuY2FudmFzVG9Xb3JsZChjbGllbnRYLCBjbGllbnRZKTtcbiAgICByZXR1cm4geyB4LCB5IH07XG4gIH1cbiAgZGF0YUxlbmd0aFRvU2NyZWVuKGRhdGFMZW5ndGg6IG51bWJlcik6IG51bWJlciB7XG4gICAgcmV0dXJuIGRhdGFMZW5ndGggKiB0aGlzLnZpZXdlci5jYW1lcmEuem9vbTsgLy8gQ1NTIHB4IHBlciB3b3JsZCB1bml0XG4gIH1cbiAgaXNSZWFkeSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkeSgpO1xuICB9XG59XG5cbi8qKiBPcGFxdWUgaGFuZGxlIGZyb20ge0BsaW5rIE5hcGFyaVZpc3VhbGl6ZXJTZXJ2aWNlLmxvYWR9LCBwYXNzZWQgYmFjayB0byBwbG90KCkuICovXG5pbnRlcmZhY2UgTmFwYXJpTG9hZGVkIHtcbiAgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvO1xuICB6OiBudW1iZXI7XG4gIC8qKiBNdXN0IG1hdGNoIGBJSW1hZ2VJbmZvLmZpbGVOYW1lYCDigJQgdGhlIHJlbmRlciBvcmNoZXN0cmF0b3IgZHJvcHMgdGhlIHJlc3VsdCBpZiB0aGVcbiAgICogIGhhbmRsZSdzIGBmaWxlbmFtZWAgZG9lc24ndCBtYXRjaCB0aGUgcmVxdWVzdGVkIGltYWdlIChndWFyZHMgYWdhaW5zdCBzdGFsZSBjbGlja3MpLiAqL1xuICBmaWxlbmFtZTogc3RyaW5nO1xufVxuXG5jb25zdCBWT0xVTUVfTUFYX1NMSUNFID0gMTAyNDsgLy8gXCJGdWxsXCIgaW4tcGxhbmUgY2FwOyB0aGUgZGVmYXVsdCDCvCBsb2FkIHVzZXMgMjU2XG4vKiogUmVmZXJlbmNlIGluLXBsYW5lIHdvcmxkIHNpemUgKGxvbmcgc2lkZSkgZm9yIHRoZSB2b2x1bWUvYXhlcyBib3gsIGluIGFyYml0cmFyeSB3b3JsZCB1bml0cy5cbiAqICBUaGUgYm94IGlzIGFuY2hvcmVkIHRvIHRoaXMgcmVmZXJlbmNlIHJlZ2FyZGxlc3Mgb2YgdGhlIGNob3NlbiBkZWNpbWF0ZSBmYWN0b3IsIHNvIGNoYW5naW5nIHRoZVxuICogIHJlc29sdXRpb24gY2hhbmdlcyBERVRBSUwsIG5vdCB0aGUgdm9sdW1lJ3MgcHJvcG9ydGlvbnMgKFogbm8gbG9uZ2VyIGFwcGVhcnMgdG8gc2hyaW5rIHdoZW4gdGhlXG4gKiAgaW4tcGxhbmUgc2FtcGxpbmcgZ3Jvd3MpLiBTZXQgdG8gdGhlIERFRkFVTFQgZGVjaW1hdGUncyBpbi1wbGFuZSBjYXAgc28gdGhlIGRlZmF1bHQgdmlldyBpc1xuICogIHVuY2hhbmdlZDsgaGlnaGVyL2xvd2VyIHJlc29sdXRpb25zIGtlZXAgdGhhdCBzYW1lIHNoYXBlLiBTZWUge0BsaW5rIG1vdW50Vm9sdW1lfS4gKi9cbmNvbnN0IFZPTFVNRV9XT1JMRF9JTlBMQU5FX1JFRiA9IFZPTFVNRV9NQVhfU0xJQ0UgLyBOQVBBUklfREVGQVVMVF9ERUNJTUFURTtcbi8qKiBNYXggY29uY3VycmVudCBzbGljZSBmZXRjaGVzIHdoZW4gYXNzZW1ibGluZyBhIHZvbHVtZSDigJQga2VlcHMgdGhlIGNvbm5lY3Rpb24gcG9vbCBidXN5XG4gKiAgd2l0aG91dCBmbG9vZGluZyBpdCAoYnJvd3NlcnMgY2FwIH42L2hvc3QpIG9uIGEgZGVlcCBzdGFjay4gKi9cbmNvbnN0IFZPTFVNRV9GRVRDSF9DT05DVVJSRU5DWSA9IDg7XG5cbi8qKiBWb2x1bWUvaXNvc3VyZmFjZSBzYW1wbGluZyBmb3IgYSBkZWNpbWF0ZSBmYWN0b3IgYHNjYWxlYCAoMSA9IEZ1bGwgMTAyNCwgMiA9IMK9IDUxMiwgNCA9IMK8IDI1NlxuICogIGRlZmF1bHQsIDggPSDihZsgMTI4KTogdGhlIGluLXBsYW5lIGNhcCBoYWx2ZXMgZWFjaCBzdGVwOyBzbGljZXMgc3RheSB1bi1zdWJzYW1wbGVkIHVudGlsIOKFmywgdGhlblxuICogIHN1YnNhbXBsZS4gKi9cbmZ1bmN0aW9uIHZvbHVtZVJlc29sdXRpb25Gb3Ioc2NhbGU6IG51bWJlcik6IHsgbWF4U2xpY2U6IG51bWJlcjsgc2xpY2VTdGVwOiBudW1iZXIgfSB7XG4gIGNvbnN0IHMgPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKHNjYWxlKSk7XG4gIHJldHVybiB7XG4gICAgbWF4U2xpY2U6IE1hdGgubWF4KDgsIE1hdGgucm91bmQoVk9MVU1FX01BWF9TTElDRSAvIHMpKSxcbiAgICBzbGljZVN0ZXA6IE1hdGgubWF4KDEsIE1hdGguZmxvb3IocyAvIDQpKSxcbiAgfTtcbn1cbi8qKiBGdWxseS1ub3JtYWxpemVkIGludGVuc2l0eSBoZWlnaHQgYXMgYSBmcmFjdGlvbiBvZiB0aGUgaW4tcGxhbmUgZXh0ZW50IOKAlCBtYXRjaGVzIHRoZSBQbG90bHlcbiAqICBTVVJGQUNFIHotYXNwZWN0ICh+MC40KSBzbyB0aGUgcmVsaWVmIGlzbid0IGV4YWdnZXJhdGVkLiAqL1xuY29uc3QgU1VSRkFDRV9aX0FTUEVDVCA9IDAuNDtcbi8qKiBcIkZ1bGxcIiBtZXNoIGdyaWQgY2FwOyB0aGUgZGVjaW1hdGUgZmFjdG9yIGRpdmlkZXMgaXQgKGRlZmF1bHQgwrwg4oaSIDIyMCwgwr0g4oaSIDQ0MCwgRnVsbCDihpIgODgwKS4gKi9cbmNvbnN0IFNVUkZBQ0VfTUFYX0dSSUQgPSA4ODA7XG4vKiogQ29lZmZpY2llbnQgc2NhbGluZyB0aGUgcHlyYW1pZCB0aWxlIGJ1ZGdldCB3aXRoIHRoZSB0YXJnZXQgcmVzb2x1dGlvbiDigJQgYSBoaWdoZXIgdGFyZ2V0IHB1bGxzIGFcbiAqICBmaW5lciBweXJhbWlkIGxldmVsIChtb3JlIHJlYWwgZGV0YWlsKS4gU2hhcmVkIGJ5IHRoZSBzdXJmYWNlIHBsYW5lIGZldGNoICsgdm9sdW1lIGFzc2VtYmx5LiAqL1xuY29uc3QgU1RJVENIX0JVREdFVF9DT0VGRiA9IDE2O1xuLyoqIFN1cmZhY2UgbWVzaCB0YXJnZXQgZ3JpZCBmb3IgYSBkZWNpbWF0ZSBmYWN0b3IgYHNjYWxlYDogdGhlIGdyaWQgY2FwIHNocmlua3MgYnkgYHNjYWxlYCAoZXZlcnlcbiAqICBzbGljZSBpcyBrZXB0IOKAlCB0aGUgei1zbGlkZXIgbmVlZHMgdGhlbSBhbGwpLiBUaGUgc291cmNlIGlzIGZldGNoZWQgYXQgYSBtYXRjaGluZyByZXNvbHV0aW9uLiAqL1xuZnVuY3Rpb24gc3VyZmFjZVJlc29sdXRpb25Gb3Ioc2NhbGU6IG51bWJlcik6IHsgbWF4R3JpZDogbnVtYmVyIH0ge1xuICBjb25zdCBzID0gTWF0aC5tYXgoMSwgTWF0aC5yb3VuZChzY2FsZSkpO1xuICByZXR1cm4geyBtYXhHcmlkOiBNYXRoLm1heCgxNiwgTWF0aC5yb3VuZChTVVJGQUNFX01BWF9HUklEIC8gcykpIH07XG59XG5cbi8qKiAzRCBzY2F0dGVyOiBpbi1wbGFuZSBjYXAgZm9yIHRoZSBhc3NlbWJsZWQgdm94ZWwgZ3JpZCwgYW5kIHRoZSBtYXggbnVtYmVyIG9mIHBvaW50cyBlbWl0dGVkXG4gKiAgKHRoZSBncmlkIGlzIGZsYXQtc3RyaWRlZCBkb3duIHRvIHRoaXMpIOKAlCBrZWVwcyB0aGUgYmlsbGJvYXJkIGNvdW50IGludGVyYWN0aXZlLiAqL1xuY29uc3QgU0NBVFRFUjNEX01BWF9YWSA9IDY0O1xuY29uc3QgU0NBVFRFUjNEX01BWF9QT0lOVFMgPSAxNTAwMDA7XG5cbmNvbnN0IFRJTEVfU0laRSA9IDUxMjsgLy8gc2VydmVyIHRpbGUgZWRnZSAobWF0Y2hlcyB0aGUgT1NEIGJhY2tlbmQpXG4vKiogTWF4IHRpbGVzIHN0aXRjaGVkIGZvciBvbmUgZGlzcGxheWVkIHNsaWNlICg1MTJweCB0aWxlcyDihpIgdXAgdG8gfjYxNDTCsiBhdCBmdWxsIHJlcykuIEJleW9uZFxuICogIHRoaXMgd2Ugc3RlcCB0byBhIGNvYXJzZXIgcHlyYW1pZCBsZXZlbCBzbyBhIGxhcmdlIGltYWdlIHN0YXlzIHRyYWN0YWJsZS4gKi9cbmNvbnN0IE1BWF9TVElUQ0hfVElMRVMgPSAxNDQ7XG4vKiogV2ViR1BVIGRlZmF1bHQgYG1heFRleHR1cmVEaW1lbnNpb24yRGAg4oCUIGEgc3RpdGNoZWQgc2xpY2UncyBsb25nZXN0IHNpZGUgbXVzdCBmaXQgYSB0ZXh0dXJlLiAqL1xuY29uc3QgTUFYX1RFWFRVUkVfRElNID0gODE5Mjtcbi8qKiBDb25jdXJyZW50IHRpbGUgcmVxdWVzdHMgcGVyIHN0aXRjaGVkIHNsaWNlLiBGaXJpbmcgYSB3aG9sZSBncmlkIGF0IG9uY2UgKGh1bmRyZWRzIG9mIHJlcXVlc3RzXG4gKiAgZm9yIGEgbGFyZ2UgbGV2ZWwpIG92ZXJ3aGVsbWVkIHRoZSB0aWxlIHNlcnZlciAoNTA0cyk7IGEgc21hbGwgcG9vbCBrZWVwcyB0aGUgcGlwZSBmdWxsIHdpdGhvdXRcbiAqICBmbG9vZGluZyBpdCDigJQgYW5kIHNsaWNlLWxldmVsIHdvcmtlcnMgYWxyZWFkeSBydW4gc2V2ZXJhbCBzdGl0Y2hlcyBpbiBwYXJhbGxlbCBvbiB0b3Agb2YgdGhpcy4gKi9cbmNvbnN0IFRJTEVfRkVUQ0hfQ09OQ1VSUkVOQ1kgPSA2O1xuLyoqIEhvdyBsb25nIHRvIHBvbGwgYC90aWxlcy9pbmZvYCAoMjAyIHdoaWxlIHRoZSBzZXJ2ZXIgY2FjaGVzIHRoZSBzb3VyY2UpIGJlZm9yZSBmYWxsaW5nIGJhY2sgdG9cbiAqICBhIHNpbmdsZSB0aWxlLiBHZW5lcm91cyBsaWtlIHRoZSBPU0QgYmFja2VuZCDigJQgYSBjb2xkIHdob2xlLXNsaWRlIGNhbiB0YWtlIG1pbnV0ZXMgdG8gY2FjaGUuICovXG5jb25zdCBERVNDUklQVE9SX1RJTUVPVVRfTVMgPSAxMjAwMDA7XG5cbi8qKiBEZWZhdWx0IHBlci1jaGFubmVsIHRpbnRzIChGaWppLXN0eWxlKSB3aGVuIHRoZSBzdG9yZS9kZXNjcmlwdG9yIG9mZmVycyBubyBjb2xvdXIuICovXG5jb25zdCBERUZBVUxUX1RJTlRTID0gWycjZmYwMDAwJywgJyMwMGZmMDAnLCAnIzAwMDBmZicsICcjZmZmZjAwJywgJyNmZjAwZmYnLCAnIzAwZmZmZicsICcjZmZmZmZmJ107XG5cbi8qKiBEZWZhdWx0IHRpbnQgZm9yIGEgY2hhbm5lbCBpbmRleCwgY3ljbGluZyB0aGUgRmlqaSBwYWxldHRlLiAqL1xuZnVuY3Rpb24gdGludEZvcihjaGFubmVsOiBudW1iZXIpOiBzdHJpbmcge1xuICByZXR1cm4gREVGQVVMVF9USU5UU1tjaGFubmVsICUgREVGQVVMVF9USU5UUy5sZW5ndGhdO1xufVxuXG4vKiogT25lIHB5cmFtaWQgbGV2ZWwgZnJvbSBgR0VUIC90aWxlcy9pbmZvYCAocmVzIDAgPSBmdWxsIHJlc29sdXRpb24pLiAqL1xuaW50ZXJmYWNlIFRpbGVMZXZlbCB7XG4gIHJlczogbnVtYmVyO1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbn1cblxuLyoqIFN1YnNldCBvZiB0aGUgYC90aWxlcy9pbmZvYCBkZXNjcmlwdG9yIHRoaXMgYmFja2VuZCByZWFkcyAodGlsZSBncmlkLCBjaGFubmVscywgc2NhbGUpLiAqL1xuaW50ZXJmYWNlIFRpbGVEZXNjcmlwdG9yIHtcbiAgd2lkdGg6IG51bWJlcjtcbiAgaGVpZ2h0OiBudW1iZXI7XG4gIHRpbGVTaXplOiBudW1iZXI7XG4gIHo6IG51bWJlcjtcbiAgY2hhbm5lbHM6IG51bWJlcjtcbiAgbXVsdGljaGFubmVsPzogYm9vbGVhbjtcbiAgLyoqIFJlYWwgQmlvLUZvcm1hdHMgbGV2ZWxzIGF0IHRoZSBmcm9udCBvZiBgbGV2ZWxzYDsgcGVyLWNoYW5uZWwgdGlsZXMgZXhpc3Qgb25seSB0aGVyZS4gKi9cbiAgcmVhbExldmVscz86IG51bWJlcjtcbiAgY2hhbm5lbEluZm8/OiBBcnJheTx7XG4gICAgbmFtZT86IHN0cmluZztcbiAgICBjb2xvcj86IHN0cmluZztcbiAgICBiaXREZXB0aD86IG51bWJlcjtcbiAgICBtaW5BbGxvd2VkPzogbnVtYmVyO1xuICAgIG1heEFsbG93ZWQ/OiBudW1iZXI7XG4gIH0+IHwgbnVsbDtcbiAgbGV2ZWxzOiBUaWxlTGV2ZWxbXTtcbiAgbXBwWD86IG51bWJlcjtcbiAgbXBwWT86IG51bWJlcjtcbn1cblxuLyoqXG4gKiBBIFdlYkdQVSBpbWFnZSBiYWNrZW5kIGJ1aWx0IG9uIHRoZSBwdWJsaXNoZWQgYG5hcGFyaS1qc2AgbGlicmFyeSDigJQgdGhlIFBPQyBlbmdpbmUgZm9yXG4gKiBqaXQtdWkjMTAyIChhIGJyb3dzZXItYmFzZWQgbmFwYXJpIHNoaXBwZWQgYXMgYSBKUyBsaWJyYXJ5LCBzd2FwcGluZyBpbWFnZSBwbG90dGluZyB3aXRoXG4gKiBPcGVuU2VhZHJhZ29uIGFuZCAzRCBzbGljaW5nL2lzb3N1cmZhY2VzIHdpdGggUGxvdGx5KS5cbiAqXG4gKiBSZW5kZXIgc3RyYXRlZ3k6IHJhdGhlciB0aGFuIHJlLWltcGxlbWVudCB0aGUgc2VydmVyJ3MgdGlsZS9weXJhbWlkIHByb3RvY29sICh3aGljaCBPU0RcbiAqIGFscmVhZHkgaGFuZGxlcyksIHRoaXMgYmFja2VuZCByZW5kZXJzIHRoZSAqKmNvbXBsZXRlIHBlci1zbGljZSBpbWFnZSBVUkxzIHRoZSBhcHAgYWxyZWFkeVxuICogcHJvZHVjZXMqKiAoYElJbWFnZUluZm8udXJsc2ApIOKAlCBgdXJsc1t6XWAgZm9yIHRoZSAyRCBpbWFnZSwgYW5kIHRoZSBmdWxsIGB1cmxzYCBzdGFja1xuICogYXNzZW1ibGVkIGludG8gYSBkb3duc2FtcGxlZCB2b2x1bWUgZm9yIHRoZSAzRCB0eXBlcy4gUmVnaW9uIHN0YXRlIGFuZCBkaXNwbGF5IG9wdGlvbnNcbiAqIGRlbGVnYXRlIHRvIHRoZSBzaGFyZWQge0BsaW5rIFJlZ2lvblN0b3JlfSAvIHtAbGluayBWaXN1YWxpemVyU3RvcmV9LCBleGFjdGx5IGFzIE9TRCBkb2VzLlxuICpcbiAqIEZvbGxvdy11cHMgKGppdC11aSMxMDIpOiBuYXRpdmUtcmVzb2x1dGlvbiBweXJhbWlkYWwgdGlsaW5nLCBvbi1jYW52YXMgdG9vbHMsIHJlZ2lvblxuICogb3ZlcmxheSByZW5kZXJpbmcsIHBlci1jaGFubmVsIGhpc3RvZ3JhbXMsIFRJRkYgZXhwb3J0LlxuICovXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIE5hcGFyaVZpc3VhbGl6ZXJTZXJ2aWNlIGV4dGVuZHMgQmFzZVN0b3JlVmlzdWFsaXplciBpbXBsZW1lbnRzIElWaXN1YWxpemVyIHtcbiAgcmVhZG9ubHkgY2FwYWJpbGl0aWVzOiBWaWV3ZXJDYXBhYmlsaXRpZXMgPSBjYXBhYmlsaXRpZXNPZihbXG4gICAgVmlld2VyRmVhdHVyZS5JbWFnZURpc3BsYXksXG4gICAgVmlld2VyRmVhdHVyZS5TdGFja1NsaWRlcixcbiAgICBWaWV3ZXJGZWF0dXJlLlBpeGVsUmVhZGJhY2ssXG4gICAgVmlld2VyRmVhdHVyZS5TdXJmYWNlM0QsXG4gICAgVmlld2VyRmVhdHVyZS5Jc29zdXJmYWNlLFxuICBdKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IGFwaTogc3RyaW5nO1xuXG4gIHByaXZhdGUgdmlld2VyOiBWaWV3ZXIgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgaG9zdDogSFRNTEVsZW1lbnQgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBsb2FkZWQ6IE5hcGFyaUxvYWRlZCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGxhc3RQaXhlbHM6IFBpeGVsRGF0YSB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGN1cnJlbnRQbG90VHlwZTogUGxvdFR5cGUgPSBQbG90VHlwZS5OQVBBUklfSU1BR0U7XG4gIC8qKiBuYXBhcmktanMgaGlnaC1sZXZlbCB2aWV3IG93bmluZyB0aGUgM0Qgdm9sdW1lIGxheWVycyAob25lIGFkZGl0aXZlIHRpbnRlZCBsYXllciBwZXIgY2hhbm5lbFxuICAgKiAgZm9yIG11bHRpY2hhbm5lbCwgb3IgYSBzaW5nbGUgZ3JheXNjYWxlIHZvbHVtZSkuIE51bGwgaW4gMkQuICovXG4gIHByaXZhdGUgdm9sdW1lVmlldzogTXVsdGlDaGFubmVsVm9sdW1lVmlldyB8IG51bGwgPSBudWxsO1xuICAvKiogVHJ1ZSB3aGVuIHRoZSB2b2x1bWUgaXMgY29tcG9zaXRlZCBmcm9tIHBlci1jaGFubmVsIGxheWVycyAodnMgYSBzaW5nbGUgZ3JheXNjYWxlIHZvbHVtZSkuICovXG4gIHByaXZhdGUgdm9sdW1lTXVsdGljaGFubmVsID0gZmFsc2U7XG4gIC8qKiBuYXBhcmktanMgaGVpZ2h0LWZpZWxkIHN1cmZhY2UgbWVzaCAoTkFQQVJJX1NVUkZBQ0UgcGxvdCB0eXBlOyBudWxsIG90aGVyd2lzZSkuIEJ1aWx0IGZyb20gYVxuICAgKiAgc2luZ2xlIGdyYXlzY2FsZSBzbGljZSBieSB7QGxpbmsgYnVpbGRTdXJmYWNlfSB2aWEgbmFwYXJpLWpzJ3MgYGhlaWdodEZpZWxkYCArIGBhZGRTdXJmYWNlYC4gKi9cbiAgcHJpdmF0ZSBzdXJmYWNlTGF5ZXI6IFN1cmZhY2VMYXllciB8IG51bGwgPSBudWxsO1xuICAvKiogV2hpY2ggYmFuZCB0aGUgc3VyZmFjZSBzYW1wbGVzIChhIGNoYW5uZWwgaW5kZXggZm9yIG11bHRpY2hhbm5lbCwgZWxzZSB0aGUgY29tcG9zaXRlKS4gKi9cbiAgcHJpdmF0ZSBzdXJmYWNlQ2hhbm5lbDogbnVtYmVyIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuICAvKiogUHJlLWxvYWRlZCBwZXItc2xpY2UgbHVtaW5hbmNlIHBsYW5lcyAoYWxyZWFkeSBkZWNpbWF0ZWQgdG8gdGhlIHN1cmZhY2UgZ3JpZCksIGtleWVkIGJ5IHosXG4gICAqICBzbyB0aGUgc3RhY2sgc2xpZGVyIHJlYnVpbGRzIHRoZSBzdXJmYWNlIGluc3RhbnRseS4gRmlsbGVkIGJ5IHtAbGluayBwcmVsb2FkU3VyZmFjZVBsYW5lc30uICovXG4gIHByaXZhdGUgcmVhZG9ubHkgc3VyZmFjZVBsYW5lcyA9IG5ldyBNYXA8XG4gICAgbnVtYmVyLFxuICAgIHsgZGF0YTogVWludDhBcnJheTsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfVxuICA+KCk7XG4gIC8qKiBJbi1wbGFuZSBncmlkIGNhcCBmb3IgdGhlIGFjdGl2ZSBzdXJmYWNlIGxvYWQgKGZ1bGwgZ3JpZCDDtyB0aGUgZGVjaW1hdGUgZmFjdG9yKS4gKi9cbiAgcHJpdmF0ZSBzdXJmYWNlTWF4R3JpZCA9IFNVUkZBQ0VfTUFYX0dSSUQ7XG4gIC8qKiBDb250cmFzdCB3aW5kb3cgW21pbixtYXhdIHRoZSBjdXJyZW50IHN1cmZhY2UgbWVzaCB3YXMgYnVpbHQgd2l0aC4gQSBjaGFuZ2UgcmVzaGFwZXMgdGhlXG4gICAqICBtZXNoIChwaXhlbCBoZWlnaHQgPSBpbnRlbnNpdHkgd2l0aGluIFttaW4sbWF4XSksIHNvIGl0IHRyaWdnZXJzIGEgZ2VvbWV0cnkgcmVidWlsZC4gKi9cbiAgcHJpdmF0ZSBzdXJmYWNlV2luZG93OiBbbnVtYmVyLCBudW1iZXJdIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBQZXJzaXN0ZWQgd2lyZWZyYW1lIGNob2ljZSBmb3IgdGhlIHN1cmZhY2UgKHJlLWFwcGxpZWQgd2hlbiBhIG5ldyBzdXJmYWNlIG1vdW50cykuICovXG4gIHByaXZhdGUgc3VyZmFjZVdpcmVmcmFtZSA9IGZhbHNlO1xuICAvKiogbmFwYXJpLWpzIDJEIHNjYXR0ZXIgcG9pbnRzIChyZWdpb24gY2VudHJvaWRzKSArIGl0cyByZWdpb24tY2hhbmdlIHN1YnNjcmlwdGlvbi4gKi9cbiAgcHJpdmF0ZSBzY2F0dGVyMmRQb2ludHM6IFBvaW50c0xheWVyIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgc2NhdHRlclJlZ2lvblN1YjogU3Vic2NyaXB0aW9uIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBuYXBhcmktanMgM0Qgc2NhdHRlciAodm94ZWwgcG9pbnQgY2xvdWQpLiAqL1xuICBwcml2YXRlIHNjYXR0ZXIzZExheWVyOiBQb2ludHMzRExheWVyIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBNb25vdG9uaWMgbG9hZCBnZW5lcmF0aW9uLiBCdW1wZWQgYnkge0BsaW5rIHJlc2V0fSBhbmQge0BsaW5rIGNhbmNlbExvYWRpbmd9OyB0aGUgZnJhbWUtbG9hZGluZ1xuICAgKiAgbG9vcHMgKHZvbHVtZSBhc3NlbWJseSwgc3VyZmFjZSBwcmVsb2FkKSBjYXB0dXJlIGl0IGFuZCBiYWlsIHdoZW4gaXQgY2hhbmdlcywgc28gYSBDYW5jZWwgKG9yIGFcbiAgICogIG5ldyBwbG90KSBhY3R1YWxseSBzdG9wcyBmZXRjaGluZyBmcmFtZXMgaW5zdGVhZCBvZiBydW5uaW5nIHRvIGNvbXBsZXRpb24gaW4gdGhlIGJhY2tncm91bmQuICovXG4gIHByaXZhdGUgbG9hZFRva2VuID0gMDtcbiAgLyoqIERlY2ltYXRlIGZhY3RvciBmb3IgdGhlIG5hcGFyaSAzRCB0eXBlcyAoMSA9IEZ1bGwsIDIgPSDCvSBkZWZhdWx0LCA0ID0gwrwsIDggPSDihZspLiBBcHBsaWVkIHdoZW4gYVxuICAgKiAgdm9sdW1lL2lzb3N1cmZhY2Uvc3VyZmFjZSAocmUpbG9hZHM7IGNoYW5naW5nIGl0IG5lZWRzIGEgcmUtcGxvdCAoaXQgY2hhbmdlcyBmZXRjaGVkIGRhdGEpLiAqL1xuICBwcml2YXRlIHJlc29sdXRpb25TY2FsZSA9IE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFO1xuICAvKiogM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gZm9yIHRoZSB2b2x1bWUvaXNvc3VyZmFjZSB2aWV3IChudWxsIGluIDJEKS4gKi9cbiAgcHJpdmF0ZSBheGVzTGF5ZXI6IEF4ZXNMYXllciB8IG51bGwgPSBudWxsO1xuICAvKiogRE9NIFgvWS9aICsgc2NhbGUgbGFiZWxzIHRyYWNraW5nIHRoZSAzRCBheGVzIGdpem1vIChudWxsIGluIDJEKS4gKi9cbiAgcHJpdmF0ZSBheGVzTGFiZWxzOiBOYXBhcmlBeGVzTGFiZWxzIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBEcmFnZ2FibGUgWi1oZWlnaHQgZ3JpcCBvdmVyIHRoZSB2b2x1bWUgKG51bGwgdW5sZXNzIGEgdm9sdW1lL2lzb3N1cmZhY2UgaXMgbW91bnRlZCkuICovXG4gIHByaXZhdGUgekhhbmRsZTogTmFwYXJpVm9sdW1lWkhhbmRsZSB8IG51bGwgPSBudWxsO1xuICAvKiogVXNlciBaLWhlaWdodCBmYWN0b3IgZm9yIHRoZSB2b2x1bWUgKDEgPSB0aGUgdm9sdW1lJ3MgbmF0dXJhbCBwcm9wb3J0aW9ucyk7IGRyaXZlbiBieSB0aGVcbiAgICogIGluLXZpZXcgZHJhZyBoYW5kbGUuIFJlc2V0IHBlciBtb3VudC4gKi9cbiAgcHJpdmF0ZSB2b2x1bWVaU2NhbGUgPSAxO1xuICAvKiogVm9sdW1lIHdvcmxkIGJveCBhdCBgdm9sdW1lWlNjYWxlID0gMWAgKGJhc2UpICsgdGhlIHNhbXBsZWQgdm94ZWwgZGVwdGgg4oCUIGVub3VnaCB0byByZWNvbXB1dGVcbiAgICogIHRoZSBaLWF4aXMgdm94ZWwgc2NhbGUsIGF4ZXMgZGVwdGgsIGFuZCBvdmVybGF5IGFuY2hvcnMgYXMgdGhlIGhhbmRsZSBzdHJldGNoZXMgWi4gKi9cbiAgcHJpdmF0ZSB2b2x1bWVXb3JsZEJhc2U6IHsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXI7IGRlcHRoOiBudW1iZXIgfSB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIHZvbHVtZVNhbXBsZWREZXB0aCA9IDE7XG4gIC8qKiBQZXJzaXN0ZWQgYXhlcyBvbi9vZmYgY2hvaWNlLCByZS1hcHBsaWVkIHdoZW4gYSBuZXcgdm9sdW1lIG1vdW50cy4gRGVmYXVsdHMgb24uICovXG4gIHByaXZhdGUgYXhlc1Zpc2libGUgPSB0cnVlO1xuICBwcml2YXRlIHZvbHVtZURpbXM6IHsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXI7IGRlcHRoOiBudW1iZXIgfSB8IG51bGwgPSBudWxsO1xuICAvKiogQXNzZW1ibGVkIHVpbnQ4IHZvbHVtZSBkYXRhIHBlciBjaGFubmVsIChrZXkgPSBjaGFubmVsIGluZGV4KSwga2VwdCBmb3IgdGhlIHZvbHVtZSBpbnRlbnNpdHlcbiAgICogIGhpc3RvZ3JhbS4gS2V5IDAgaG9sZHMgdGhlIGdyYXlzY2FsZS9jb21wb3NpdGUgdm9sdW1lIGluIHRoZSBzaW5nbGUtY2hhbm5lbCBjYXNlLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHZvbHVtZUNoYW5uZWxEYXRhID0gbmV3IE1hcDxudW1iZXIsIFVpbnQ4QXJyYXk+KCk7XG4gIHByaXZhdGUgaW1hZ2VXID0gMDtcbiAgcHJpdmF0ZSBpbWFnZUggPSAwO1xuICAvKiogTW9ub3RvbmljIHNsaWNlLXJlcXVlc3QgaWQgc28gYSBzbG93IG91dC1vZi1vcmRlciBzbGljZSBmZXRjaCBjYW4ndCBjbG9iYmVyIGEgbmV3ZXIgb25lLiAqL1xuICBwcml2YXRlIHNsaWNlUmVxID0gMDtcbiAgLyoqIENhY2hlZCBgL3RpbGVzL2luZm9gIHB5cmFtaWQgZGVzY3JpcHRvciArIHRoZSBpbmZvQjY0IGl0IHdhcyBmZXRjaGVkIGZvci4gKi9cbiAgcHJpdmF0ZSBkZXNjcmlwdG9yOiBUaWxlRGVzY3JpcHRvciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGRlc2NyaXB0b3JLZXk6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIC8qKiBIb3cgdGhlIGN1cnJlbnQgMkQgaW1hZ2UgaXMgY29tcG9zaXRlZCAoZHJpdmVzIGhpc3RvZ3JhbSArIHN0YXRlIGFwcGxpY2F0aW9uKS4gKi9cbiAgcHJpdmF0ZSBpbWFnZU1vZGU6ICdncmF5c2NhbGUnIHwgJ211bHRpY2hhbm5lbCcgfCAncmdiJyA9ICdyZ2InO1xuICAvKiogbmFwYXJpLWpzIGhpZ2gtbGV2ZWwgdmlldyB0aGF0IG93bnMgdGhlIHBlci1jaGFubmVsIGxheWVyIHNldCAoYnVpbGQgKyBsaXZlIGRpc3BsYXlcbiAgICogIHVwZGF0ZXMpIGZvciB0aGUgY3VycmVudCB7QGxpbmsgaW1hZ2VNb2RlfS4gUmVidWlsdCBvbiBlYWNoIChyZSlyZW5kZXIuICovXG4gIHByaXZhdGUgY2hhbm5lbFZpZXc6IE11bHRpQ2hhbm5lbEltYWdlVmlldyB8IG51bGwgPSBudWxsO1xuICAvKiogTGl2ZSBzdWJzY3JpcHRpb24gYXBwbHlpbmcgY2hhbm5lbC1zdGF0ZSAvIGNvbG9ybWFwIGNoYW5nZXMgdG8gdGhlIGxheWVycy4gKi9cbiAgcHJpdmF0ZSBkaXNwbGF5U3ViOiBTdWJzY3JpcHRpb24gfCBudWxsID0gbnVsbDtcbiAgLyoqIExhdGVzdCBncmF5c2NhbGUgY29sb3JtYXAgc2VsZWN0aW9uIGZyb20gdGhlIHN0b3JlIChhcHBsaWVkIHRvIHRoZSBzaW5nbGUgZ3JheSBsYXllcikuICovXG4gIHByaXZhdGUgY3VycmVudENvbG9ybWFwOiBDb2xvcm1hcE5vZGUgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjdXJyZW50UmV2ZXJzZSA9IGZhbHNlO1xuICAvKiogTGF0ZXN0IGludmVydCB0b2dnbGUgZnJvbSB0aGUgc3RvcmUgKGZsaXBzIGludGVuc2l0eSBiZWZvcmUgdGhlIGNvbG9ybWFwLCBsaWtlIE9TRCkuICovXG4gIHByaXZhdGUgaW52ZXJ0RW5hYmxlZCA9IGZhbHNlO1xuICAvKiogUGh5c2ljYWwgc2NhbGUgYmFyIG92ZXJsYXkgZm9yIHRoZSAyRCBpbWFnZSAobnVsbCB3aGVuIDNEIG9yIHRoZSBpbWFnZSBoYXMgbm8gwrVtL3BpeGVsKS4gKi9cbiAgcHJpdmF0ZSBzY2FsZUJhcjogTmFwYXJpU2NhbGVCYXIgfCBudWxsID0gbnVsbDtcbiAgLyoqIFNWRyByZWdpb24tZHJhd2luZyBvdmVybGF5IGZvciB0aGUgMkQgaW1hZ2UgKG51bGwgdW50aWwgYSAyRCBpbWFnZSBpcyBwbG90dGVkKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25PdmVybGF5OiBOYXBhcmlSZWdpb25PdmVybGF5IHwgbnVsbCA9IG51bGw7XG4gIC8qKiBQaXhlbC10b29sIHBsdW1iaW5nOiB0aGUgcGxvdCBkaXYgaWQsIGNvb3JkIHRyYW5zZm9ybSwgYW5kIGJvdW5kIHRvb2wgaG9zdHMuICovXG4gIHByaXZhdGUgcGxvdERpdklkID0gJyc7XG4gIHByaXZhdGUgY29vcmRUcmFuc2Zvcm06IElDb29yZGluYXRlVHJhbnNmb3JtIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgd2FuZEhvc3Q6IFdhbmRUb29sSG9zdCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGVyYXNlckhvc3Q6IFZlcnRleEVyYXNlclRvb2xIb3N0IHwgbnVsbCA9IG51bGw7XG4gIC8qKiBDYWNoZWQgQ2FjaGVkSW1hZ2VEYXRhIGJ1aWx0IGZyb20gdGhlIGxhc3QgcmVhZGJhY2sgKHJlYnVpbHQgd2hlbiBsYXN0UGl4ZWxzIGNoYW5nZXMpLiAqL1xuICBwcml2YXRlIGNhY2hlZEltYWdlOiBDYWNoZWRJbWFnZURhdGEgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjYWNoZWRJbWFnZVNvdXJjZTogUGl4ZWxEYXRhIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBWaXNpYmxlIHdvcmxkIHJlY3QgY2FwdHVyZWQgQVQgdGhlIGxhc3QgcmVhZGJhY2sg4oCUIG11c3QgcGFpciB3aXRoIGBsYXN0UGl4ZWxzYCBzbyB0aGUgcGl4ZWxcbiAgICogIHRvb2xzJyByYXRpb3Mvb3JpZ2luIG1hdGNoIHRoZSBtYXRyaXgncyBjYW1lcmEgKHVzaW5nIHRoZSBsaXZlIHJlY3QgYWZ0ZXIgYSBwYW4vem9vbSB3b3VsZFxuICAgKiAgbWlzLXNjYWxlIHRoZSByZWdpb24pLiAqL1xuICBwcml2YXRlIGxhc3RQaXhlbHNSZWN0OiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHwgbnVsbCA9IG51bGw7XG4gIC8qKiBJbWFnZSBzbW9vdGhpbmcgKGJpbGluZWFyKSB2cyBuZWFyZXN0LW5laWdoYm91ciAoY3Jpc3AgcGl4ZWxzLCB0aGUgZGVmYXVsdCkuICovXG4gIHByaXZhdGUgaW1hZ2VTbW9vdGhpbmcgPSBmYWxzZTtcbiAgLyoqIFRydWUgd2hlbiB0aGUgMkQgaW1hZ2UgaXMgcmVuZGVyZWQgdmlhIHB5cmFtaWRhbCBUaWxlZFNvdXJjZXMgKGRlc2NyaXB0b3IgYXZhaWxhYmxlKS4gKi9cbiAgcHJpdmF0ZSB0aWxlZCA9IGZhbHNlO1xuICAvKiogRGVib3VuY2VkIHJlYWRiYWNrIHRpbWVyICsgY2FtZXJhLWNoYW5nZSB1bnN1YnNjcmliZSAoa2VlcCBsYXN0UGl4ZWxzIGN1cnJlbnQgZm9yIHRvb2xzKS4gKi9cbiAgcHJpdmF0ZSByZWFkYmFja1RpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGNhbWVyYVJlYWRiYWNrT2ZmOiAoKCkgPT4gdm9pZCkgfCBudWxsID0gbnVsbDtcbiAgLyoqIENvYXJzZSBwZXItY2hhbm5lbCBsdW1pbmFuY2Ugc2FtcGxlIChrZXllZCBieSBjaGFubmVsIGluZGV4KSBmb3IgdGhlIGhpc3RvZ3JhbSBpbiB0aWxlZCBtb2RlLFxuICAgKiAgd2hlcmUgdGhlIGxheWVycyBoYXZlIG5vIGZ1bGwgaW4tbWVtb3J5IHBpeGVscy4gUmVmcmVzaGVkIG9uIHBsb3QgKyBzbGljZSBjaGFuZ2UuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgaGlzdFNhbXBsZXMgPSBuZXcgTWFwPG51bWJlciwgVWludDhBcnJheT4oKTtcbiAgLyoqIE5hdGl2ZS1iaXQtZGVwdGggaGlzdG9ncmFtcyBmcm9tIGAvaGlzdG9ncmFtYCwga2V5ZWQgYCR7en18JHtjaGFubmVsfWAgKD44LWJpdCBpbWFnZXMpLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IG5hdGl2ZUhpc3RvZ3JhbXMgPSBuZXcgTWFwPHN0cmluZywgSUhpc3RvZ3JhbT4oKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IHN0YWNrTG9hZGluZyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgcHJpdmF0ZSByZWFkb25seSBzdGFja0xvYWRpbmdQcm9ncmVzcyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PG51bWJlcj4oMCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgYXV0b3NjYWxlRXZlbnQkID0gbmV3IFN1YmplY3Q8dW5rbm93bj4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSBpbnRlbnNpdHlQcm9maWxlJCA9IG5ldyBTdWJqZWN0PEludGVuc2l0eVByb2ZpbGVbXT4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSB2aWV3cG9ydENoYW5nZSQgPSBuZXcgU3ViamVjdDx7XG4gICAgeDogbnVtYmVyO1xuICAgIHk6IG51bWJlcjtcbiAgICB3aWR0aDogbnVtYmVyO1xuICAgIGhlaWdodDogbnVtYmVyO1xuICB9PigpO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIEBJbmplY3QoVElMRV9BQ0NFU1NfUE9SVCkgcHJpdmF0ZSByZWFkb25seSB0aWxlczogVGlsZUFjY2Vzc1BvcnQsXG4gICAgc3RvcmU6IFZpc3VhbGl6ZXJTdG9yZSxcbiAgICByZWdpb25TdG9yZTogUmVnaW9uU3RvcmUsXG4gICAgcHJpdmF0ZSByZWFkb25seSB3YW5kVG9vbDogV2FuZFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgYnJ1c2hUb29sOiBCcnVzaFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgZXJhc2VyVG9vbDogVmVydGV4RXJhc2VyVG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSByZWFkb25seSB6b29tVG9Cb3hUb29sOiBab29tVG9Cb3hUb29sU2VydmljZSxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNhbVRvb2w6IFNhbVRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgc2FtUG9pbnRUb29sOiBTYW1Qb2ludFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgY2VsbFNlZ21lbnRUb29sOiBDZWxsU2VnbWVudFRvb2xTZXJ2aWNlLFxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoQ0VMTF9TRUdNRU5URVIpIHByaXZhdGUgcmVhZG9ubHkgY2VsbFNlZ21lbnRlcjogSUNlbGxTZWdtZW50ZXIgfCBudWxsLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgc2ltcGxlU3RhY2s6IFNpbXBsZVNsaWNlQWNjZXNzU2VydmljZSxcbiAgICBASW5qZWN0KFZJWl9DT05GSUcpIGNvbmZpZzogVml6Q29uZmlnLFxuICApIHtcbiAgICBzdXBlcihyZWdpb25TdG9yZSwgc3RvcmUpO1xuICAgIHRoaXMuYXBpID0gY29uZmlnLnNsaWRlQ3JvcFNlcnZlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBGZXRjaCArIGNhY2hlIHRoZSBzZXJ2ZXIgcHlyYW1pZCBkZXNjcmlwdG9yIChgR0VUIC90aWxlcy9pbmZvYCk6IHRoZSBSRUFMIHBlci1sZXZlbCB0aWxlIGdyaWQsXG4gICAqIHRpbGUgc2l6ZSwgY2hhbm5lbCBtZXRhZGF0YSBhbmQgcGh5c2ljYWwgcGl4ZWwgc2l6ZS4gVGhlIGJhY2tlbmQgcmV0dXJucyAyMDIgd2hpbGUgdGhlIHNvdXJjZVxuICAgKiBpcyBzdGlsbCBjYWNoaW5nLCBzbyB3ZSBwb2xsIGJyaWVmbHkuIENhY2hlZCBwZXIgYGluZm9CNjRgOyByZXR1cm5zIG51bGwgaWYgaXQgbmV2ZXIgYmVjb21lc1xuICAgKiByZWFkeSAoY2FsbGVycyBmYWxsIGJhY2sgdG8gYSBzaW5nbGUtdGlsZSBmZXRjaCkuIFRoaXMgaXMgdGhlIGF1dGhvcml0YXRpdmUgZ3JpZCDigJQgZ3Vlc3NpbmdcbiAgICogbGV2ZWwgZGltcyBmcm9tIGB0cnVlSW1hZ2VTaXplYCBvdmVyc2hvb3RzIHRoZSByZWFsIGdyaWQgYW5kIHRoZSBzZXJ2ZXIgNDAwcyBvdXQtb2YtcmFuZ2UgdGlsZXMuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGVuc3VyZURlc2NyaXB0b3IoKTogUHJvbWlzZTxUaWxlRGVzY3JpcHRvciB8IG51bGw+IHtcbiAgICAvLyBTZWxmLWNvbnRhaW5lZCBtdWx0aS1zbGljZSBzdGFjayAobm8gdGlsZSBzZXJ2ZXIg4oCUIGUuZy4gYSBudW1iZXJlZCBpbWFnZVxuICAgIC8vIHNlcmllcyBhc3NlbWJsZWQgY2xpZW50LXNpZGUsIGVhY2ggc2xpY2UgYSBkaWZmZXJlbnQgZmlsZSk6IHRoZXJlIGlzIG5vXG4gICAgLy8gc2luZ2xlIHNlcnZlci10aWxlZCBweXJhbWlkIHRvIGRlc2NyaWJlLiBSZXR1cm5pbmcgbnVsbCByb3V0ZXMgZXZlcnlcbiAgICAvLyBjYWxsZXIgKDJEIGltYWdlLCB2b2x1bWUsIHN1cmZhY2UpIHRvIHRoZWlyIHN0aXRjaGVkL3NpbmdsZS1mZXRjaFxuICAgIC8vIGZhbGxiYWNrLCB3aGljaCDigJQgdmlhIGZldGNoU2xpY2UncyBvd24gU2ltcGxlU2xpY2VBY2Nlc3NTZXJ2aWNlIGJyYW5jaCDigJRcbiAgICAvLyBjb3JyZWN0bHkgZmV0Y2hlcyBlYWNoIHNsaWNlJ3Mgb3duIFVSTCBpbnN0ZWFkIG9mIG9uZSBmaXhlZCBmaWxlJ3MgdGlsZVxuICAgIC8vIHB5cmFtaWQuXG4gICAgaWYgKHRoaXMuc2ltcGxlU3RhY2suaXNTaW1wbGUodGhpcy5sb2FkZWQ/LmltYWdlSW5mbykpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IGluZm9CNjQgPSB0aGlzLnRpbGVzLmdldFNlbGVjdGVkSW5mb0I2NCgpO1xuICAgIGlmICghaW5mb0I2NCkgcmV0dXJuIG51bGw7XG4gICAgaWYgKHRoaXMuZGVzY3JpcHRvciAmJiB0aGlzLmRlc2NyaXB0b3JLZXkgPT09IGluZm9CNjQpIHJldHVybiB0aGlzLmRlc2NyaXB0b3I7XG4gICAgY29uc3QgaGVhZGVycyA9IGF3YWl0IHRoaXMudGlsZXNcbiAgICAgIC5nZXRBdXRoSGVhZGVycygpXG4gICAgICAuY2F0Y2goKCkgPT4gKHt9KSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTtcbiAgICBjb25zdCB1cmwgPSBgJHt0aGlzLmFwaX10aWxlcy9pbmZvP2luZm89JHtpbmZvQjY0fWA7XG4gICAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgREVTQ1JJUFRPUl9USU1FT1VUX01TO1xuICAgIGZvciAoOzspIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc3AgPSBhd2FpdCBmZXRjaCh1cmwsIHsgaGVhZGVycyB9KTtcbiAgICAgICAgaWYgKHJlc3Auc3RhdHVzID09PSAyMDApIHtcbiAgICAgICAgICBjb25zdCBib2R5ID0gKGF3YWl0IHJlc3AuanNvbigpKSBhcyBUaWxlRGVzY3JpcHRvcjtcbiAgICAgICAgICBpZiAoYm9keT8ubGV2ZWxzPy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHRoaXMuZGVzY3JpcHRvciA9IGJvZHk7XG4gICAgICAgICAgICB0aGlzLmRlc2NyaXB0b3JLZXkgPSBpbmZvQjY0O1xuICAgICAgICAgICAgcmV0dXJuIGJvZHk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIDIwMiAoc3RpbGwgY2FjaGluZykgb3IgZW1wdHkgYm9keSDihpIgcmUtcG9sbCB1bnRpbCB0aGUgZGVhZGxpbmUuXG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdbbmFwYXJpLWpzXSB0aWxlcy9pbmZvIHBvbGwgcmV0cnknLCBlcnIpO1xuICAgICAgfVxuICAgICAgaWYgKERhdGUubm93KCkgPiBkZWFkbGluZSkge1xuICAgICAgICBjb25zb2xlLndhcm4oJ1tuYXBhcmktanNdIHRpbGVzL2luZm8gbm90IHJlYWR5OyBmYWxsaW5nIGJhY2sgdG8gc2luZ2xlLXRpbGUgZmV0Y2gnKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocikgPT4gc2V0VGltZW91dChyLCAxMjAwKSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoIGEgQ09NUExFVEUgcmVuZGVyZWQgc2xpY2UgYXMgYW4gYEltYWdlQml0bWFwYCBieSBzdGl0Y2hpbmcgdGhlIHNlcnZlcidzIFJFQUwgdGlsZSBncmlkXG4gICAqIChmcm9tIGAvdGlsZXMvaW5mb2ApIOKAlCBub3QganVzdCB0aGUgdG9wLWxlZnQgdGlsZSwgd2hpY2ggb25seSBldmVyIHNob3dlZCBhIGxhcmdlIGltYWdlJ3NcbiAgICogY29ybmVyLiBQaWNrcyB0aGUgZmluZXN0IHB5cmFtaWQgbGV2ZWwgd2hvc2UgZ3JpZCBmaXRzIGBidWRnZXRUaWxlc2AgYW5kIHdob3NlIGxvbmdlc3Qgc2lkZSBpc1xuICAgKiB3aXRoaW4gdGhlIEdQVSB0ZXh0dXJlIGxpbWl0LCBmZXRjaGVzIHRoYXQgZ3JpZCBjb25jdXJyZW50bHksIGFuZCBzdGl0Y2hlcyBpdCBpbnRvIG9uZSBjYW52YXMuXG4gICAqXG4gICAqIGBjaGFubmVsYCBzZWxlY3RzIGEgc2luZ2xlIGJhbmQgYXMgZ3JheXNjYWxlIChyZWFsIGxldmVscyBvbmx5KTsgb21pdCBmb3IgdGhlIHNlcnZlciBjb21wb3NpdGUuXG4gICAqIGBidWRnZXRUaWxlc2AgY2FwcyB0aGUgZ3JpZDogdGhlIDJEIHZpZXcgdXNlcyB0aGUgZnVsbCBidWRnZXQgZm9yIGRldGFpbDsgdGhlIDNEIHZvbHVtZSBwYXNzZXNcbiAgICogMSBmb3IgYSBjaGVhcCBvdmVydmlldyB0aWxlIHBlciBzbGljZSAoaXQgZG93bnNhbXBsZXMgdG8ge0BsaW5rIFZPTFVNRV9NQVhfU0xJQ0V9KS4gRmFsbHMgYmFja1xuICAgKiB0byBhIHNpbmdsZSBgY29sPTAscm93PTBgIHRpbGUgd2hlbiBubyBkZXNjcmlwdG9yIGlzIGF2YWlsYWJsZSAoc21hbGwvc2ltcGxlIGltYWdlcywgdm9sdW1lcykuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoU2xpY2UoXG4gICAgejogbnVtYmVyLFxuICAgIGNoYW5uZWw/OiBudW1iZXIsXG4gICAgYnVkZ2V0VGlsZXM6IG51bWJlciA9IE1BWF9TVElUQ0hfVElMRVMsXG4gICAgLyoqIFdoZW4gYSBzcGVjaWZpYyBjaGFubmVsIGhhcyBubyBweXJhbWlkIGxldmVsIHdpdGhpbiBgYnVkZ2V0VGlsZXNgLCBkcm9wIHRvIHRoZSBzZXJ2ZXJcbiAgICAgKiAgQ09NUE9TSVRFIG92ZXJ2aWV3IGluc3RlYWQgb2Ygc3RpdGNoaW5nIHRoZSBodWdlIHBlci1jaGFubmVsIGxldmVsLiBPbmx5IHNhZmUgd2hlbiB0aGUgY2FsbGVyXG4gICAgICogIHdhbnRzIGEgc2luZ2xlIGRlY2ltYXRlZCBwbGFuZSAodGhlIHN1cmZhY2UgaGVpZ2h0KTogZm9yIGEgbXVsdGljaGFubmVsIFZPTFVNRSBldmVyeSBjaGFubmVsXG4gICAgICogIHdvdWxkIHRoZW4gZmV0Y2ggdGhlIHNhbWUgY29tcG9zaXRlIGFuZCB0aGUgY2hhbm5lbHMgd291bGQgY29sbGFwc2UgaW50byBvbmUsIHNvIHZvbHVtZVxuICAgICAqICBhc3NlbWJseSBsZWF2ZXMgdGhpcyBgZmFsc2VgIHRvIGtlZXAgZWFjaCBiYW5kIGRpc3RpbmN0LiAqL1xuICAgIGFsbG93Q29tcG9zaXRlRmFsbGJhY2sgPSBmYWxzZSxcbiAgKTogUHJvbWlzZTxJbWFnZUJpdG1hcD4ge1xuICAgIC8vIFNlbGYtY29udGFpbmVkIG11bHRpLXNsaWNlIHN0YWNrICh0aWxlZDpmYWxzZSk6IGB6YCBpbmRleGVzIGEgY29tcGxldGVseVxuICAgIC8vIGRpZmZlcmVudCBmaWxlJ3Mgb3duIHByZXZpZXcgVVJMLCBub3QgYW4gaW50ZXJuYWwgc2xpY2Ugb2Ygb25lIHNlcnZlci1cbiAgICAvLyB0aWxlZCBmaWxlIOKAlCByZXNvbHZlZCBhbmQgZmV0Y2hlZCB2aWEgU2ltcGxlU2xpY2VBY2Nlc3NTZXJ2aWNlIChzaGFyZWRcbiAgICAvLyB3aXRoIE9TRDsgc2VlIGl0cyBkb2NzIGZvciB3aHkgdGhpcyBjYW4ndCBiZSBhIGJhcmUgZmV0Y2goKSkgaW5zdGVhZCBvZlxuICAgIC8vIGJ1aWxkaW5nIGEgL3RpbGU/aW5mbz0uLi4mej0gcmVxdWVzdCBhZ2FpbnN0IHdoaWNoZXZlciBzaW5nbGUgZmlsZVxuICAgIC8vIGdldFNlbGVjdGVkSW5mb0I2NCgpIHBvaW50cyBhdCAod2hpY2ggd291bGQgNDAwL21pc21hdGNoIGZvciBhbnkgelxuICAgIC8vIGJleW9uZCB0aGF0IG9uZSBmaWxlJ3Mgb3duIGV4dGVudCkuXG4gICAgY29uc3QgaW5mbyA9IHRoaXMubG9hZGVkPy5pbWFnZUluZm87XG4gICAgaWYgKHRoaXMuc2ltcGxlU3RhY2suaXNTaW1wbGUoaW5mbykpIHtcbiAgICAgIGNvbnN0IHVybCA9IHRoaXMuc2ltcGxlU3RhY2sudXJsRm9yKGluZm8gYXMgSUltYWdlSW5mbywgeik7XG4gICAgICBpZiAoIXVybCkgdGhyb3cgbmV3IEVycm9yKGBbbmFwYXJpLWpzXSBubyBVUkwgZm9yIHNsaWNlICR7en1gKTtcbiAgICAgIHJldHVybiB0aGlzLnNpbXBsZVN0YWNrLmZldGNoQXNCaXRtYXAodXJsKTtcbiAgICB9XG4gICAgY29uc3QgaW5mb0I2NCA9IHRoaXMudGlsZXMuZ2V0U2VsZWN0ZWRJbmZvQjY0KCk7XG4gICAgaWYgKCFpbmZvQjY0KSB0aHJvdyBuZXcgRXJyb3IoJ1tuYXBhcmktanNdIG5vIHNlbGVjdGVkIGltYWdlIGluZm8gKGdldFNlbGVjdGVkSW5mb0I2NCBudWxsKScpO1xuICAgIGNvbnN0IGhlYWRlcnMgPSBhd2FpdCB0aGlzLnRpbGVzXG4gICAgICAuZ2V0QXV0aEhlYWRlcnMoKVxuICAgICAgLmNhdGNoKCgpID0+ICh7fSkgYXMgUmVjb3JkPHN0cmluZywgc3RyaW5nPik7XG4gICAgY29uc3QgZGVzYyA9IGF3YWl0IHRoaXMuZW5zdXJlRGVzY3JpcHRvcigpO1xuXG4gICAgLy8gVGhlIHJlcXVlc3RlZCBiYW5kOyBtYXkgYmUgZHJvcHBlZCB0byB0aGUgY29tcG9zaXRlICh1bmRlZmluZWQpIGJlbG93IHdoZW4gdGhlIGNoYW5uZWwgaGFzIG5vXG4gICAgLy8gcHlyYW1pZCBsZXZlbCBzbWFsbCBlbm91Z2ggdG8gc3RpdGNoIHdpdGhpbiBidWRnZXQuXG4gICAgbGV0IGVmZmVjdGl2ZUNoYW5uZWwgPSBjaGFubmVsO1xuICAgIGNvbnN0IGZldGNoVGlsZSA9IGFzeW5jIChcbiAgICAgIHJlczogbnVtYmVyLFxuICAgICAgY29sOiBudW1iZXIsXG4gICAgICByb3c6IG51bWJlcixcbiAgICAgIHQ6IG51bWJlcixcbiAgICApOiBQcm9taXNlPEltYWdlQml0bWFwPiA9PiB7XG4gICAgICBjb25zdCBjaCA9IGVmZmVjdGl2ZUNoYW5uZWwgPT0gbnVsbCA/ICcnIDogYCZjaGFubmVsPSR7ZWZmZWN0aXZlQ2hhbm5lbH1gO1xuICAgICAgY29uc3QgdXJsID0gYCR7dGhpcy5hcGl9dGlsZT9pbmZvPSR7aW5mb0I2NH0mcmVzPSR7cmVzfSZjb2w9JHtjb2x9JnJvdz0ke3Jvd30mej0ke3p9JnRpbGVTaXplPSR7dH0ke2NofWA7XG4gICAgICBjb25zdCByZXNwID0gYXdhaXQgZmV0Y2godXJsLCB7IGhlYWRlcnMgfSk7XG4gICAgICBpZiAoIXJlc3Aub2spIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBbbmFwYXJpLWpzXSBzbGljZSBmZXRjaCBmYWlsZWQ6ICR7cmVzcC5zdGF0dXN9ICgke2NvbH0vJHtyb3d9IHJlcyAke3Jlc30pYCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gY3JlYXRlSW1hZ2VCaXRtYXAoYXdhaXQgcmVzcC5ibG9iKCkpO1xuICAgIH07XG5cbiAgICAvLyBObyBkZXNjcmlwdG9yIOKGkiBzaW5nbGUgdG9wLWxlZnQgdGlsZSAobGVnYWN5IGZhbGxiYWNrOyBjb3JyZWN0IGZvciBzbWFsbC9zaW1wbGUgaW1hZ2VzKS5cbiAgICBpZiAoIWRlc2MgfHwgIWRlc2MubGV2ZWxzPy5sZW5ndGgpIHJldHVybiBmZXRjaFRpbGUoMCwgMCwgMCwgVElMRV9TSVpFKTtcblxuICAgIGNvbnN0IHQgPSBkZXNjLnRpbGVTaXplIHx8IFRJTEVfU0laRTtcbiAgICAvLyBQZXItY2hhbm5lbCB0aWxlcyBleGlzdCBPTkxZIGF0IFJFQUwgQmlvLUZvcm1hdHMgbGV2ZWxzICh0aGUgZnJvbnQgb2YgYGxldmVsc2ApOyB0aGUgc2VydmVyXG4gICAgLy8gY29tcG9zaXRlIGV4aXN0cyBhdCBldmVyeSBsZXZlbCwgaW5jbHVkaW5nIHRoZSBzbWFsbCBvdmVydmlld3MuXG4gICAgY29uc3QgcGVyQ2hhbm5lbExldmVscyA9IGRlc2MucmVhbExldmVscyA/PyBkZXNjLmxldmVscy5sZW5ndGg7XG4gICAgY29uc3QgdXNhYmxlID1cbiAgICAgIGNoYW5uZWwgPT0gbnVsbCA/IGRlc2MubGV2ZWxzIDogZGVzYy5sZXZlbHMuc2xpY2UoMCwgTWF0aC5tYXgoMSwgcGVyQ2hhbm5lbExldmVscykpO1xuXG4gICAgLy8gRmluZXN0IGxldmVsIHdob3NlIHN0aXRjaGVkIGdyaWQgZml0cyBCT1RIIHRoZSB0aWxlIGJ1ZGdldCBhbmQgdGhlIEdQVSB0ZXh0dXJlIGxpbWl0OyBpZiBub25lXG4gICAgLy8gZml0cywgdGhlIGNvYXJzZXN0IGF2YWlsYWJsZSAoZml0cz1mYWxzZSkuXG4gICAgY29uc3QgdGlsZXNGb3IgPSAobHZsOiBUaWxlTGV2ZWwpOiBudW1iZXIgPT5cbiAgICAgIE1hdGgubWF4KDEsIE1hdGguY2VpbChsdmwud2lkdGggLyB0KSkgKiBNYXRoLm1heCgxLCBNYXRoLmNlaWwobHZsLmhlaWdodCAvIHQpKTtcbiAgICBjb25zdCBwaWNrID0gKGxldmVsczogVGlsZUxldmVsW10pOiB7IGx2bDogVGlsZUxldmVsOyBmaXRzOiBib29sZWFuIH0gPT4ge1xuICAgICAgbGV0IGMgPSBsZXZlbHNbMF07XG4gICAgICBmb3IgKGNvbnN0IGx2bCBvZiBsZXZlbHMpIHtcbiAgICAgICAgYyA9IGx2bDtcbiAgICAgICAgaWYgKHRpbGVzRm9yKGx2bCkgPD0gYnVkZ2V0VGlsZXMgJiYgTWF0aC5tYXgobHZsLndpZHRoLCBsdmwuaGVpZ2h0KSA8PSBNQVhfVEVYVFVSRV9ESU0pIHtcbiAgICAgICAgICByZXR1cm4geyBsdmwsIGZpdHM6IHRydWUgfTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHsgbHZsOiBjLCBmaXRzOiBmYWxzZSB9O1xuICAgIH07XG5cbiAgICBsZXQgc2VsID0gcGljayh1c2FibGUpO1xuICAgIC8vIEEgc3BlY2lmaWMgY2hhbm5lbCBvbmx5IGhhcyB0aWxlcyBhdCB0aGUgKGZldywgbGFyZ2UpIHJlYWwgbGV2ZWxzLiBXaGVuIG5vbmUgb2YgdGhlbSBmaXQgdGhlXG4gICAgLy8gYnVkZ2V0IOKAlCBlLmcuIHRoZSBjb2Fyc2VzdCByZWFsIGxldmVsIGlzIHN0aWxsIDE0OTgyw5cxODY3MCDihpIgfjExMDAgZnVsbC1yZXMgdGlsZXMg4oCUIHN0aXRjaGluZ1xuICAgIC8vIGl0IHBlciBzbGljZSBmbG9vZHMgdGhlIHNlcnZlciAoNTA0cykgYW5kIHN0YWxscyB0aGUgbG9hZC4gVGhlIGNvbXBvc2l0ZSBweXJhbWlkIGhhcyBzbWFsbFxuICAgIC8vIG92ZXJ2aWV3IGxldmVscywgc28gZmV0Y2ggdGhlIGNvbXBvc2l0ZSBpbnN0ZWFkIGFuZCBkZXJpdmUgbHVtaW5hbmNlIGZyb20gaXQuIEV2ZXJ5IGNhbGxlciBoZXJlXG4gICAgLy8gKHN1cmZhY2UgaGVpZ2h0LCB2b2x1bWUgYXNzZW1ibHksIHJlYWRiYWNrKSBkb3duc2NhbGVzIHRoZSBwbGFuZSBhbnl3YXksIHNvIGEgY29tcG9zaXRlLWRlcml2ZWRcbiAgICAvLyBwbGFuZSBpcyB0aGUgcmlnaHQgdHJhZGUgZm9yIG9uZSB0aGF0IGFjdHVhbGx5IGxvYWRzLiBPbmx5IGtpY2tzIGluIHdoZW4gdGhlIGNoYW5uZWwgY2FuJ3QgZml0LlxuICAgIGlmIChhbGxvd0NvbXBvc2l0ZUZhbGxiYWNrICYmICFzZWwuZml0cyAmJiBjaGFubmVsICE9IG51bGwgJiYgZGVzYy5sZXZlbHMubGVuZ3RoID4gcGVyQ2hhbm5lbExldmVscykge1xuICAgICAgY29uc3QgY29tcG9zaXRlID0gcGljayhkZXNjLmxldmVscyk7XG4gICAgICBpZiAoY29tcG9zaXRlLmZpdHMgfHwgdGlsZXNGb3IoY29tcG9zaXRlLmx2bCkgPCB0aWxlc0ZvcihzZWwubHZsKSkge1xuICAgICAgICBlZmZlY3RpdmVDaGFubmVsID0gdW5kZWZpbmVkO1xuICAgICAgICBzZWwgPSBjb21wb3NpdGU7XG4gICAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICBgW25hcGFyaS1qc10gY2hhbm5lbCAke2NoYW5uZWx9IGhhcyBubyBweXJhbWlkIGxldmVsIHdpdGhpbiB0aGUgJHtidWRnZXRUaWxlc30tdGlsZSBgICtcbiAgICAgICAgICAgIGBidWRnZXQ7IHVzaW5nIHRoZSBjb21wb3NpdGUgb3ZlcnZpZXcgKHJlcyAke3NlbC5sdmwucmVzfSwgJHtzZWwubHZsLndpZHRofcOXYCArXG4gICAgICAgICAgICBgJHtzZWwubHZsLmhlaWdodH0pIGZvciB0aGlzIHBsYW5lLmAsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGNob3NlbiA9IHNlbC5sdmw7XG4gICAgY29uc3QgY29scyA9IE1hdGgubWF4KDEsIE1hdGguY2VpbChjaG9zZW4ud2lkdGggLyB0KSk7XG4gICAgY29uc3Qgcm93cyA9IE1hdGgubWF4KDEsIE1hdGguY2VpbChjaG9zZW4uaGVpZ2h0IC8gdCkpO1xuICAgIGlmICghc2VsLmZpdHMgJiYgYnVkZ2V0VGlsZXMgPT09IE1BWF9TVElUQ0hfVElMRVMpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgYFtuYXBhcmktanNdIGZ1bGwgcmVzb2x1dGlvbiBleGNlZWRzIHRoZSAke2J1ZGdldFRpbGVzfS10aWxlLyR7TUFYX1RFWFRVUkVfRElNfXB4IGJ1ZGdldDsgYCArXG4gICAgICAgICAgYGRpc3BsYXlpbmcgb3ZlcnZpZXcgbGV2ZWwgcmVzICR7Y2hvc2VuLnJlc30gKCR7Y2hvc2VuLndpZHRofcOXJHtjaG9zZW4uaGVpZ2h0fSkuYCxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKGNvbHMgPT09IDEgJiYgcm93cyA9PT0gMSkgcmV0dXJuIGZldGNoVGlsZShjaG9zZW4ucmVzLCAwLCAwLCB0KTtcblxuICAgIC8vIFN0aXRjaCBpbnRvIG9uZSBsZXZlbC1zaXplZCBjYW52YXMuIEZldGNoIHRoZSBncmlkIHdpdGggQk9VTkRFRCBjb25jdXJyZW5jeTogZmlyaW5nIGV2ZXJ5IHRpbGVcbiAgICAvLyBhdCBvbmNlIChhIGJpZyBncmlkID0gaHVuZHJlZHMgb2YgcmVxdWVzdHMpIG92ZXJ3aGVsbWVkIHRoZSB0aWxlIHNlcnZlciAoNTA0cykuIEVkZ2UgdGlsZXMgYXJlXG4gICAgLy8gbmFycm93ZXIvc2hvcnRlcjsgZHJhd0ltYWdlIHBsYWNlcyBlYWNoIGF0IGl0cyBncmlkIG9mZnNldCBzbyBwYXJ0aWFsIHRpbGVzIGxpbmUgdXAuXG4gICAgY29uc3QgY29vcmRzOiBBcnJheTx7IGNvbDogbnVtYmVyOyByb3c6IG51bWJlciB9PiA9IFtdO1xuICAgIGZvciAobGV0IHJvdyA9IDA7IHJvdyA8IHJvd3M7IHJvdysrKSB7XG4gICAgICBmb3IgKGxldCBjb2wgPSAwOyBjb2wgPCBjb2xzOyBjb2wrKykgY29vcmRzLnB1c2goeyBjb2wsIHJvdyB9KTtcbiAgICB9XG4gICAgY29uc3QgdGlsZXM6IEFycmF5PHsgY29sOiBudW1iZXI7IHJvdzogbnVtYmVyOyBibXA6IEltYWdlQml0bWFwIH0+ID0gW107XG4gICAgY29uc3Qgc3RpdGNoV29ya2VyID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgZm9yICg7Oykge1xuICAgICAgICBjb25zdCBqb2IgPSBjb29yZHMuc2hpZnQoKTtcbiAgICAgICAgaWYgKCFqb2IpIHJldHVybjtcbiAgICAgICAgY29uc3QgYm1wID0gYXdhaXQgZmV0Y2hUaWxlKGNob3Nlbi5yZXMsIGpvYi5jb2wsIGpvYi5yb3csIHQpO1xuICAgICAgICB0aWxlcy5wdXNoKHsgY29sOiBqb2IuY29sLCByb3c6IGpvYi5yb3csIGJtcCB9KTtcbiAgICAgIH1cbiAgICB9O1xuICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgQXJyYXkuZnJvbSh7IGxlbmd0aDogTWF0aC5taW4oVElMRV9GRVRDSF9DT05DVVJSRU5DWSwgY29vcmRzLmxlbmd0aCkgfSwgKCkgPT4gc3RpdGNoV29ya2VyKCkpLFxuICAgICk7XG5cbiAgICBsZXQgY2FudmFzOiBIVE1MQ2FudmFzRWxlbWVudCB8IE9mZnNjcmVlbkNhbnZhcztcbiAgICBpZiAodHlwZW9mIE9mZnNjcmVlbkNhbnZhcyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIGNhbnZhcyA9IG5ldyBPZmZzY3JlZW5DYW52YXMoY2hvc2VuLndpZHRoLCBjaG9zZW4uaGVpZ2h0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgY2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgICBjYW52YXMud2lkdGggPSBjaG9zZW4ud2lkdGg7XG4gICAgICBjYW52YXMuaGVpZ2h0ID0gY2hvc2VuLmhlaWdodDtcbiAgICB9XG4gICAgY29uc3QgY3R4ID0gY2FudmFzLmdldENvbnRleHQoJzJkJykgYXNcbiAgICAgIHwgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEXG4gICAgICB8IE9mZnNjcmVlbkNhbnZhc1JlbmRlcmluZ0NvbnRleHQyRFxuICAgICAgfCBudWxsO1xuICAgIGlmICghY3R4KSB0aHJvdyBuZXcgRXJyb3IoJ1tuYXBhcmktanNdIHNsaWNlIHN0aXRjaDogMkQgY29udGV4dCB1bmF2YWlsYWJsZScpO1xuICAgIGZvciAoY29uc3QgeyBjb2wsIHJvdywgYm1wIH0gb2YgdGlsZXMpIHtcbiAgICAgIGN0eC5kcmF3SW1hZ2UoYm1wLCBjb2wgKiB0LCByb3cgKiB0KTtcbiAgICAgIGJtcC5jbG9zZT8uKCk7XG4gICAgfVxuXG4gICAgLy8gU2FmZXR5IG5ldDogaWYgdGhlIGNob3NlbiBsZXZlbCBzdGlsbCBleGNlZWRzIHRoZSBHUFUgdGV4dHVyZSBsaW1pdCAoZS5nLiBhIG11bHRpY2hhbm5lbFxuICAgIC8vIGltYWdlIHdob3NlIGNvYXJzZXN0IFJFQUwgbGV2ZWwgaXMgaHVnZSDigJQgb3ZlcnZpZXcgbGV2ZWxzIGFyZSBjb21wb3NpdGUtb25seSksIGRvd25zY2FsZSB0aGVcbiAgICAvLyBzdGl0Y2hlZCBjYW52YXMgdG8gZml0IHNvIHRoZSBXZWJHUFUgdGV4dHVyZSB1cGxvYWQgY2FuJ3QgZmFpbC5cbiAgICBjb25zdCBsb25nZXN0ID0gTWF0aC5tYXgoY2hvc2VuLndpZHRoLCBjaG9zZW4uaGVpZ2h0KTtcbiAgICBpZiAobG9uZ2VzdCA+IE1BWF9URVhUVVJFX0RJTSkge1xuICAgICAgY29uc3Qgc2NhbGUgPSBNQVhfVEVYVFVSRV9ESU0gLyBsb25nZXN0O1xuICAgICAgY29uc3Qgb3V0VyA9IE1hdGgubWF4KDEsIE1hdGguZmxvb3IoY2hvc2VuLndpZHRoICogc2NhbGUpKTtcbiAgICAgIGNvbnN0IG91dEggPSBNYXRoLm1heCgxLCBNYXRoLmZsb29yKGNob3Nlbi5oZWlnaHQgKiBzY2FsZSkpO1xuICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICBgW25hcGFyaS1qc10gc3RpdGNoZWQgbGV2ZWwgJHtjaG9zZW4ud2lkdGh9w5cke2Nob3Nlbi5oZWlnaHR9IGV4Y2VlZHMgdGhlIGAgK1xuICAgICAgICAgIGAke01BWF9URVhUVVJFX0RJTX1weCB0ZXh0dXJlIGxpbWl0OyBkb3duc2NhbGluZyB0byAke291dFd9w5cke291dEh9LmAsXG4gICAgICApO1xuICAgICAgbGV0IG91dDogSFRNTENhbnZhc0VsZW1lbnQgfCBPZmZzY3JlZW5DYW52YXM7XG4gICAgICBpZiAodHlwZW9mIE9mZnNjcmVlbkNhbnZhcyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgb3V0ID0gbmV3IE9mZnNjcmVlbkNhbnZhcyhvdXRXLCBvdXRIKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG91dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgICAgICBvdXQud2lkdGggPSBvdXRXO1xuICAgICAgICBvdXQuaGVpZ2h0ID0gb3V0SDtcbiAgICAgIH1cbiAgICAgIGNvbnN0IG9jdHggPSBvdXQuZ2V0Q29udGV4dCgnMmQnKSBhc1xuICAgICAgICB8IENhbnZhc1JlbmRlcmluZ0NvbnRleHQyRFxuICAgICAgICB8IE9mZnNjcmVlbkNhbnZhc1JlbmRlcmluZ0NvbnRleHQyRFxuICAgICAgICB8IG51bGw7XG4gICAgICBpZiAoIW9jdHgpIHRocm93IG5ldyBFcnJvcignW25hcGFyaS1qc10gc2xpY2UgZG93bnNjYWxlOiAyRCBjb250ZXh0IHVuYXZhaWxhYmxlJyk7XG4gICAgICBvY3R4LmRyYXdJbWFnZShjYW52YXMgYXMgdW5rbm93biBhcyBDYW52YXNJbWFnZVNvdXJjZSwgMCwgMCwgb3V0Vywgb3V0SCk7XG4gICAgICByZXR1cm4gY3JlYXRlSW1hZ2VCaXRtYXAob3V0IGFzIHVua25vd24gYXMgSW1hZ2VCaXRtYXBTb3VyY2UpO1xuICAgIH1cbiAgICByZXR1cm4gY3JlYXRlSW1hZ2VCaXRtYXAoY2FudmFzIGFzIHVua25vd24gYXMgSW1hZ2VCaXRtYXBTb3VyY2UpO1xuICB9XG5cbiAgLy8g4pSA4pSAIElEYXRhUmVuZGVyZXI6IGxvYWQgLyByZW5kZXIgLyB2aWV3cG9ydCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgYXN5bmMgbG9hZChpbWFnZUluZm86IElJbWFnZUluZm8sIHpJbmRleDogbnVtYmVyKTogUHJvbWlzZTxOYXBhcmlMb2FkZWQ+IHtcbiAgICAvLyBLZWVwcyBTaW1wbGVTbGljZUFjY2Vzc1NlcnZpY2UncyBibG9iIGNhY2hlIGluIHN5bmMgZXZlbiB3aGVuIHRoZSB1c2VyXG4gICAgLy8gc3dpdGNoZXMgYmFja2VuZHMgKGUuZy4gT1NEIEltYWdlIOKGkiBuYXBhcmkgVm9sdW1lKSBvbiB0aGUgc2FtZSBmaWxlIOKAlFxuICAgIC8vIHdoaWNoZXZlciBiYWNrZW5kIGxvYWRzIGEgZ2VudWluZWx5IGRpZmZlcmVudCBmaWxlIGZpcnN0IGV2aWN0cyBpdC5cbiAgICB0aGlzLnNpbXBsZVN0YWNrLm5vdGVBY3RpdmVGaWxlKGltYWdlSW5mby5maWxlTmFtZSk7XG4gICAgdGhpcy5sb2FkZWQgPSB7IGltYWdlSW5mbywgejogekluZGV4LCBmaWxlbmFtZTogaW1hZ2VJbmZvLmZpbGVOYW1lIH07XG4gICAgcmV0dXJuIHRoaXMubG9hZGVkO1xuICB9XG5cbiAgYXN5bmMgcGxvdChcbiAgICBwbG90RGl2OiBzdHJpbmcsXG4gICAgaW1hZ2VMb2FkZWQ6IHVua25vd24sXG4gICAgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvLFxuICAgIHNjcmVlbkhlaWdodDogbnVtYmVyLFxuICAgIHBsb3RUeXBlOiBQbG90VHlwZSxcbiAgICBfaW5QbGFjZT86IGJvb2xlYW4sXG4gICk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IGhvc3QgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChwbG90RGl2KTtcbiAgICBpZiAoIWhvc3QpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYFtuYXBhcmktanNdIHBsb3QgdGFyZ2V0ICMke3Bsb3REaXZ9IG5vdCBmb3VuZGApO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICB0aGlzLnJlc2V0KCk7XG4gICAgdGhpcy5ob3N0ID0gaG9zdDtcbiAgICB0aGlzLnBsb3REaXZJZCA9IHBsb3REaXY7XG4gICAgdGhpcy5jdXJyZW50UGxvdFR5cGUgPSBwbG90VHlwZTtcblxuICAgIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgIGNhbnZhcy5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJztcbiAgICBjYW52YXMuc3R5bGUud2lkdGggPSAnMTAwJSc7XG4gICAgY2FudmFzLnN0eWxlLmhlaWdodCA9IHNjcmVlbkhlaWdodCA/IGAke3NjcmVlbkhlaWdodH1weGAgOiAnMTAwJSc7XG4gICAgaG9zdC5hcHBlbmRDaGlsZChjYW52YXMpO1xuICAgIHRoaXMuY2FudmFzID0gY2FudmFzO1xuXG4gICAgY29uc3QgaW5mbyA9IChpbWFnZUxvYWRlZCBhcyBOYXBhcmlMb2FkZWQpPy5pbWFnZUluZm8gPz8gaW1hZ2VJbmZvO1xuICAgIGNvbnN0IHogPSAoaW1hZ2VMb2FkZWQgYXMgTmFwYXJpTG9hZGVkKT8ueiA/PyAwO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHZpZXdlciA9IG5ldyBWaWV3ZXIoeyBjYW52YXMsIGJhY2tncm91bmQ6IHsgcjogMC4wNywgZzogMC4wNywgYjogMC4wOSwgYTogMSB9IH0pO1xuICAgICAgdGhpcy52aWV3ZXIgPSB2aWV3ZXI7XG4gICAgICBhd2FpdCB2aWV3ZXIucmVhZHk7XG5cbiAgICAgIGlmIChpc05hcGFyaVNjYXR0ZXIocGxvdFR5cGUpKSB7XG4gICAgICAgIGF3YWl0IHRoaXMubW91bnRTY2F0dGVyKHZpZXdlciwgeik7XG4gICAgICB9IGVsc2UgaWYgKGlzTmFwYXJpU2NhdHRlcjNkKHBsb3RUeXBlKSkge1xuICAgICAgICBhd2FpdCB0aGlzLm1vdW50U2NhdHRlcjNkKHZpZXdlciwgaW5mbyk7XG4gICAgICB9IGVsc2UgaWYgKGlzTmFwYXJpU3VyZmFjZShwbG90VHlwZSkpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5tb3VudFN1cmZhY2Uodmlld2VyKTtcbiAgICAgIH0gZWxzZSBpZiAoaXNOYXBhcmkzZChwbG90VHlwZSkpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5tb3VudFZvbHVtZSh2aWV3ZXIsIGluZm8sIHBsb3RUeXBlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGF3YWl0IHRoaXMucmVuZGVySW1hZ2Uoeik7XG4gICAgICAgIHRoaXMuZml0Q2FtZXJhU29vbigpO1xuICAgICAgICB0aGlzLnN1YnNjcmliZURpc3BsYXlTdGF0ZSgpO1xuICAgICAgICB0aGlzLmluc3RhbGxTY2FsZUJhcigpO1xuICAgICAgICB0aGlzLnJlZ2lvbk92ZXJsYXkgPSBuZXcgTmFwYXJpUmVnaW9uT3ZlcmxheShob3N0LCB2aWV3ZXIsIHRoaXMucmVnaW9uU3RvcmUpO1xuICAgICAgICB0aGlzLmJ1aWxkVG9vbEhvc3RzKCk7XG4gICAgICAgIC8vIEtlZXAgdGhlIHBpeGVsLXRvb2wgcmVhZGJhY2sgKGxhc3RQaXhlbHMpIGN1cnJlbnQgYXMgdGhlIHZpZXcgcGFucy96b29tcyBhbmQgdGlsZWRcbiAgICAgICAgLy8gbGV2ZWxzIGxvYWQsIHNvIHdhbmQvYnJ1c2gvU0FNIHNhbXBsZSB0aGUgYWN0dWFsbHktZGlzcGxheWVkIGltYWdlIChqaXQtdWkjMTAyKS5cbiAgICAgICAgdGhpcy5jYW1lcmFSZWFkYmFja09mZiA9IHZpZXdlci5jYW1lcmEuY2hhbmdlZC5jb25uZWN0KCgpID0+IHRoaXMuYXJtUmVhZGJhY2soKSk7XG4gICAgICB9XG4gICAgICB0aGlzLnNjaGVkdWxlUmVhZGJhY2soKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgY29uc29sZS5lcnJvcignW25hcGFyaS1qc10gcGxvdCBmYWlsZWQ6JywgZXJyKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvLyDilIDilIAgQ2hhbm5lbHM6IHBlci1jaGFubmVsIGNvbXBvc2l0ZSwgTFVULCBuYXRpdmUgaGlzdG9ncmFtcyAoaml0LXVpIzEwMikg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIFRoZSBncmF5c2NhbGUgZGlzcGxheSBjb2xvcm1hcCBmcm9tIHRoZSBzdG9yZSBzZWxlY3Rpb24gKCtyZXZlcnNlKSwgZGVmYXVsdGluZyB0byBncmF5LlxuICAgKiAgTWFwcyB0aGUgaml0LXVpIHN0b3JlIGNvbG9ybWFwIG5vZGUgdG8gYSBuYXBhcmkgYENvbG9ybWFwYCB2aWEgdGhlIGxpYnJhcnkncyBMVVQgZmFjdG9yeTtcbiAgICogIHRoZSBtdWx0aWNoYW5uZWwgdGludCByYW1wcyBhcmUgYnVpbHQgaW5zaWRlIHtAbGluayBNdWx0aUNoYW5uZWxJbWFnZVZpZXd9IGZyb20gZWFjaFxuICAgKiAgY2hhbm5lbCdzIGhleCBjb2xvdXIuICovXG4gIHByaXZhdGUgZ3JheXNjYWxlQ29sb3JtYXAoKTogQ29sb3JtYXAgfCBzdHJpbmcge1xuICAgIGNvbnN0IHZhbHVlID0gKHRoaXMuY3VycmVudENvbG9ybWFwIGFzIHsgZGF0YT86IHsgdmFsdWU/OiB1bmtub3duIH0gfSB8IG51bGwpPy5kYXRhPy52YWx1ZTtcbiAgICBjb25zdCBsdXQgPSB2YWx1ZSAhPSBudWxsID8gYnVpbGRDb2xvcm1hcEx1dCh2YWx1ZSwgdGhpcy5jdXJyZW50UmV2ZXJzZSkgOiBudWxsO1xuICAgIGlmIChsdXQpIHJldHVybiBjb2xvcm1hcEZyb21MdXQoJ2dyYXktY21hcCcsIGx1dCk7XG4gICAgcmV0dXJuIHRoaXMuY3VycmVudFJldmVyc2VcbiAgICAgID8gY29sb3JtYXBGcm9tTHV0KCdncmF5LXJldicsIFtbMjU1LCAyNTUsIDI1NV0sIFswLCAwLCAwXV0gYXMgUmdiW10pXG4gICAgICA6ICdncmF5JztcbiAgfVxuXG4gIC8qKiBGZXRjaCBhIHN0aXRjaGVkIHNsaWNlIGFuZCByZWFkIGl0IGJhY2sgYXMgYSBzaW5nbGUtY2hhbm5lbCB1aW50OCBwbGFuZS4gYGNoYW5uZWxgIHNlbGVjdHMgYVxuICAgKiAgYmFuZCAobXVsdGljaGFubmVsKTsgb21pdCBpdCBmb3IgdGhlIGdyYXlzY2FsZSBjb21wb3NpdGUgKGFsbCBvdmVydmlldyBsZXZlbHMgYXZhaWxhYmxlLCBzbyBhXG4gICAqICBsYXJnZSBpbWFnZSBwaWNrcyBhIGZpdHRpbmcgZG93bnNjYWxlZCBsZXZlbCByYXRoZXIgdGhhbiBvbmx5IHRoZSBmdWxsLXJlcyByZWFsIGxldmVsKS4gKi9cbiAgcHJpdmF0ZSBhc3luYyBmZXRjaENoYW5uZWxEYXRhKFxuICAgIHo6IG51bWJlcixcbiAgICBjaGFubmVsPzogbnVtYmVyLFxuICAgIGJ1ZGdldFRpbGVzPzogbnVtYmVyLFxuICApOiBQcm9taXNlPHsgZGF0YTogVWludDhBcnJheTsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfT4ge1xuICAgIGNvbnN0IGJtcCA9IGF3YWl0IHRoaXMuZmV0Y2hTbGljZSh6LCBjaGFubmVsLCBidWRnZXRUaWxlcyk7XG4gICAgcmV0dXJuIHRoaXMuYml0bWFwVG9MdW1pbmFuY2UoYm1wKTtcbiAgfVxuXG4gIC8qKiBEZWNvZGUgYW4gYEltYWdlQml0bWFwYCAoc2VydmVyIGJhbmRzIGFyZSBncmF5c2NhbGUsIFI9Rz1CKSB0byBhIHNpbmdsZS1jaGFubmVsIHVpbnQ4IHBsYW5lLlxuICAgKiAgYG1heFNpZGVgIGNhcHMgdGhlIGxvbmdlc3Qgc2lkZSAoZG93bnNjYWxpbmcgb24gdGhlIGNhbnZhcyBkcmF3KSDigJQgdXNlZCB0byBrZWVwIHByZS1sb2FkZWRcbiAgICogIHN1cmZhY2Ugc2xpY2UgcGxhbmVzIHNtYWxsLiAqL1xuICBwcml2YXRlIGJpdG1hcFRvTHVtaW5hbmNlKFxuICAgIGJtcDogSW1hZ2VCaXRtYXAsXG4gICAgbWF4U2lkZT86IG51bWJlcixcbiAgKTogeyBkYXRhOiBVaW50OEFycmF5OyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHtcbiAgICBjb25zdCBzY2FsZSA9IG1heFNpZGUgPyBNYXRoLm1pbigxLCBtYXhTaWRlIC8gTWF0aC5tYXgoYm1wLndpZHRoLCBibXAuaGVpZ2h0LCAxKSkgOiAxO1xuICAgIGNvbnN0IHcgPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKGJtcC53aWR0aCAqIHNjYWxlKSk7XG4gICAgY29uc3QgaCA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoYm1wLmhlaWdodCAqIHNjYWxlKSk7XG4gICAgbGV0IGNhbnZhczogSFRNTENhbnZhc0VsZW1lbnQgfCBPZmZzY3JlZW5DYW52YXM7XG4gICAgaWYgKHR5cGVvZiBPZmZzY3JlZW5DYW52YXMgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICBjYW52YXMgPSBuZXcgT2Zmc2NyZWVuQ2FudmFzKHcsIGgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICAgIGNhbnZhcy53aWR0aCA9IHc7XG4gICAgICBjYW52YXMuaGVpZ2h0ID0gaDtcbiAgICB9XG4gICAgY29uc3QgY3R4ID0gY2FudmFzLmdldENvbnRleHQoJzJkJykgYXNcbiAgICAgIHwgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEXG4gICAgICB8IE9mZnNjcmVlbkNhbnZhc1JlbmRlcmluZ0NvbnRleHQyRFxuICAgICAgfCBudWxsO1xuICAgIGlmICghY3R4KSB0aHJvdyBuZXcgRXJyb3IoJ1tuYXBhcmktanNdIGNoYW5uZWwgcmVhZGJhY2s6IDJEIGNvbnRleHQgdW5hdmFpbGFibGUnKTtcbiAgICAvLyBTY2FsZSB0aGUgV0hPTEUgYml0bWFwIGludG8gdGhlIChwb3NzaWJseSBzbWFsbGVyKSB0YXJnZXQgY2FudmFzIOKAlCBkcmF3aW5nIGF0IG5hdHVyYWwgc2l6ZVxuICAgIC8vIHdvdWxkIGNyb3AgdG8gdGhlIHRvcC1sZWZ0IHfDl2ggY29ybmVyIHdoZW4gZG93bnNjYWxpbmcgYSBsYXJnZSBzbGljZSAobWF4U2lkZSA8IGJtcCBzaXplKS5cbiAgICBjdHguZHJhd0ltYWdlKGJtcCwgMCwgMCwgdywgaCk7XG4gICAgYm1wLmNsb3NlPy4oKTtcbiAgICBjb25zdCByZ2JhID0gY3R4LmdldEltYWdlRGF0YSgwLCAwLCB3LCBoKS5kYXRhO1xuICAgIGNvbnN0IGRhdGEgPSBuZXcgVWludDhBcnJheSh3ICogaCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB3ICogaDsgaSsrKSBkYXRhW2ldID0gcmdiYVtpICogNF07XG4gICAgcmV0dXJuIHsgZGF0YSwgd2lkdGg6IHcsIGhlaWdodDogaCB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbmRlciB0aGUgMkQgaW1hZ2UgZm9yIHNsaWNlIGB6YC4gV2l0aCBhIHNlcnZlciBweXJhbWlkIGRlc2NyaXB0b3Igd2UgdXNlIGEgcHlyYW1pZGFsXG4gICAqIHtAbGluayBUaWxlZFNvdXJjZX0gcGVyIGxheWVyIHNvIHRoZSB2aWV3IHJlZmluZXMgdG8gaGlnaGVyIHJlc29sdXRpb24gb24gem9vbSAobGlrZSBPU0QpIGFuZFxuICAgKiBzaXRzIG5hdHVyYWxseSBpbiBmdWxsLXJlc29sdXRpb24gY29vcmRpbmF0ZXM7IHdpdGhvdXQgb25lIHdlIGZhbGwgYmFjayB0byB0aGUgc2luZ2xlLWxldmVsXG4gICAqIHN0aXRjaC4gVGhyZWUgZGlzcGxheSBtb2RlcyBlaXRoZXIgd2F5OiBtdWx0aWNoYW5uZWwgYWRkaXRpdmUgdGludCwgZ3JheXNjYWxlIGNvbG9ybWFwLCBSR0IuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHJlbmRlckltYWdlKHo6IG51bWJlciwgdG9rZW4/OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB2ID0gdGhpcy52aWV3ZXI7XG4gICAgaWYgKCF2KSByZXR1cm47XG4gICAgY29uc3QgZGVzYyA9IGF3YWl0IHRoaXMuZW5zdXJlRGVzY3JpcHRvcigpO1xuICAgIGlmIChkZXNjICYmIGRlc2MubGV2ZWxzPy5sZW5ndGgpIHtcbiAgICAgIGlmICh0b2tlbiAhPSBudWxsICYmIHRva2VuICE9PSB0aGlzLnNsaWNlUmVxKSByZXR1cm47XG4gICAgICBhd2FpdCB0aGlzLnJlbmRlckltYWdlVGlsZWQoeiwgZGVzYyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnJlbmRlckltYWdlU3RpdGNoZWQoeiwgdG9rZW4pO1xuICB9XG5cbiAgLyoqIFNpbmdsZS1sZXZlbCBzdGl0Y2ggZmFsbGJhY2sgKG5vIGRlc2NyaXB0b3IpOiB0aGUgcHJlLXRpbGluZyBwYXRoLiAqL1xuICBwcml2YXRlIGFzeW5jIHJlbmRlckltYWdlU3RpdGNoZWQoejogbnVtYmVyLCB0b2tlbj86IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHYgPSB0aGlzLnZpZXdlcjtcbiAgICBpZiAoIXYpIHJldHVybjtcbiAgICBjb25zdCBkZXNjID0gYXdhaXQgdGhpcy5lbnN1cmVEZXNjcmlwdG9yKCk7XG4gICAgY29uc3Qgc3RhdGVzID0gdGhpcy5zdG9yZS5jdXJyZW50Q2hhbm5lbFN0YXRlcygpO1xuICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IGRlc2M/LmNoYW5uZWxzID8/IChzdGF0ZXMubGVuZ3RoIHx8IDEpO1xuICAgIGNvbnN0IG11bHRpY2hhbm5lbCA9ICEhZGVzYz8ubXVsdGljaGFubmVsICYmIGNoYW5uZWxDb3VudCA+IDE7XG5cbiAgICAvLyAxKSBGZXRjaCBhbGwgcGl4ZWwgZGF0YSBCRUZPUkUgdG91Y2hpbmcgdGhlIHZpZXdlciwgc28gYSBzdXBlcnNlZGVkIHNjcnViIGNhbiBiYWlsIHdpdGhvdXRcbiAgICAvLyAgICBoYXZpbmcgdG9ybiBkb3duIHRoZSB2aXNpYmxlIGxheWVycyAoYXZvaWRzIGZsaWNrZXIgLyBvdXQtb2Ytb3JkZXIgbGF5ZXIgc3RhdGUpLlxuICAgIGxldCBtb2RlOiAnZ3JheXNjYWxlJyB8ICdtdWx0aWNoYW5uZWwnIHwgJ3JnYic7XG4gICAgbGV0IHBsYW5lczogQXJyYXk8eyBkYXRhOiBVaW50OEFycmF5OyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9PiA9IFtdO1xuICAgIGxldCBiaXRtYXA6IEltYWdlQml0bWFwIHwgbnVsbCA9IG51bGw7XG4gICAgaWYgKG11bHRpY2hhbm5lbCkge1xuICAgICAgbW9kZSA9ICdtdWx0aWNoYW5uZWwnO1xuICAgICAgcGxhbmVzID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIEFycmF5LmZyb20oeyBsZW5ndGg6IGNoYW5uZWxDb3VudCB9LCAoXywgYykgPT4gdGhpcy5mZXRjaENoYW5uZWxEYXRhKHosIGMpKSxcbiAgICAgICk7XG4gICAgfSBlbHNlIGlmIChjaGFubmVsQ291bnQgPT09IDEpIHtcbiAgICAgIG1vZGUgPSAnZ3JheXNjYWxlJztcbiAgICAgIC8vIENvbXBvc2l0ZSBmZXRjaCAobm8gY2hhbm5lbCkg4oaSIGFsbCBvdmVydmlldyBsZXZlbHMgdXNhYmxlLCBzbyBhIGxhcmdlIGdyYXlzY2FsZSBpbWFnZVxuICAgICAgLy8gc2VsZWN0cyBhIGZpdHRpbmcgZG93bnNjYWxlZCBsZXZlbCBpbnN0ZWFkIG9mIHRoZSBmdWxsLXJlcyByZWFsIGxldmVsICh0ZXh0dXJlIGxpbWl0KS5cbiAgICAgIHBsYW5lcyA9IFthd2FpdCB0aGlzLmZldGNoQ2hhbm5lbERhdGEoeildO1xuICAgIH0gZWxzZSB7XG4gICAgICBtb2RlID0gJ3JnYic7XG4gICAgICBiaXRtYXAgPSBhd2FpdCB0aGlzLmZldGNoU2xpY2Uoeik7XG4gICAgfVxuXG4gICAgLy8gMikgQ29tbWl0IOKAlCB1bmxlc3MgYSBuZXdlciBzY3J1YiBzdXBlcnNlZGVkIHVzIG9yIHRoZSB2aWV3ZXIgd2FzIHRvcm4gZG93bi5cbiAgICBpZiAoKHRva2VuICE9IG51bGwgJiYgdG9rZW4gIT09IHRoaXMuc2xpY2VSZXEpIHx8IHRoaXMudmlld2VyICE9PSB2KSB7XG4gICAgICBiaXRtYXA/LmNsb3NlPy4oKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBUaGUgZGlzcGxheWVkIHRleHR1cmUgbWF5IGJlIGEgZG93bnNjYWxlZCBweXJhbWlkIGxldmVsOyBzY2FsZSB0aGUgbGF5ZXIgaW50byBGVUxMLVJFU09MVVRJT05cbiAgICAvLyB3b3JsZCBjb29yZGluYXRlcyAobGV2ZWwtMCBwaXhlbHMpIHNvIHRoZSBjYW1lcmEsIHJlYWRiYWNrIGFuZCDigJQgY3JpdGljYWxseSDigJQgcHJlLXNhdmVkXG4gICAgLy8gcmVnaW9ucyAoc3RvcmVkIGluIGZ1bGwtcmVzIGNvb3JkcywgZS5nLiBuZHBpKSBhbGwgbGluZSB1cCByZWdhcmRsZXNzIG9mIHdoaWNoIGxldmVsIGlzIHNob3duLlxuICAgIC8vIFRoaXMgbWlycm9ycyBPU0QsIHdob3NlIGNvb3JkaW5hdGUgc3lzdGVtIGlzIGFsd2F5cyBsZXZlbCAwLlxuICAgIGNvbnN0IHRleFcgPSBtb2RlID09PSAncmdiJyA/IChiaXRtYXAgYXMgSW1hZ2VCaXRtYXApLndpZHRoIDogcGxhbmVzWzBdPy53aWR0aCA/PyAwO1xuICAgIGNvbnN0IHRleEggPSBtb2RlID09PSAncmdiJyA/IChiaXRtYXAgYXMgSW1hZ2VCaXRtYXApLmhlaWdodCA6IHBsYW5lc1swXT8uaGVpZ2h0ID8/IDA7XG4gICAgY29uc3QgZnVsbFcgPSBkZXNjPy53aWR0aCB8fCB0ZXhXIHx8IDE7XG4gICAgY29uc3QgZnVsbEggPSBkZXNjPy5oZWlnaHQgfHwgdGV4SCB8fCAxO1xuICAgIGNvbnN0IHNjYWxlOiBbbnVtYmVyLCBudW1iZXJdID0gW3RleFcgPyBmdWxsVyAvIHRleFcgOiAxLCB0ZXhIID8gZnVsbEggLyB0ZXhIIDogMV07XG5cbiAgICB0aGlzLmltYWdlTW9kZSA9IG1vZGU7XG4gICAgY29uc3QgaW50ZXJwb2xhdGlvbjogJ2xpbmVhcicgfCAnbmVhcmVzdCcgPSB0aGlzLmltYWdlU21vb3RoaW5nID8gJ2xpbmVhcicgOiAnbmVhcmVzdCc7XG4gICAgdGhpcy5jaGFubmVsVmlldyA9IG5ldyBNdWx0aUNoYW5uZWxJbWFnZVZpZXcodik7XG4gICAgaWYgKG1vZGUgPT09ICdtdWx0aWNoYW5uZWwnKSB7XG4gICAgICBjb25zdCB2aWV3czogQ2hhbm5lbFZpZXdbXSA9IHBsYW5lcy5tYXAoKGQsIGMpID0+IHtcbiAgICAgICAgY29uc3Qgc3QgPSBzdGF0ZXMuZmluZCgocykgPT4gcy5pbmRleCA9PT0gYyk7XG4gICAgICAgIGNvbnN0IGNvbG9yID0gc3Q/LmNvbG9yID8/IGRlc2M/LmNoYW5uZWxJbmZvPy5bY10/LmNvbG9yID8/IHRpbnRGb3IoYyk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgc291cmNlOiB7IGtpbmQ6ICd0eXBlZCcsIHdpZHRoOiBkLndpZHRoLCBoZWlnaHQ6IGQuaGVpZ2h0LCBjaGFubmVsczogMSwgZHR5cGU6ICd1aW50OCcsIGRhdGE6IGQuZGF0YSB9LFxuICAgICAgICAgIHRpbnQ6IGNvbG9yLFxuICAgICAgICAgIG5hbWU6IHN0Py5uYW1lID8/IGBjaCR7Y31gLFxuICAgICAgICAgIGNvbnRyYXN0TGltaXRzOiBbc3Q/Lm1pbiA/PyAwLCBzdD8ubWF4ID8/IDI1NV0sXG4gICAgICAgICAgZ2FtbWE6IHN0Py5nYW1tYSA/PyAxLFxuICAgICAgICAgIHZpc2libGU6IHN0Py52aXNpYmxlID8/IHRydWUsXG4gICAgICAgICAgaW52ZXJ0OiB0aGlzLmludmVydEVuYWJsZWQsXG4gICAgICAgICAgc2NhbGUsXG4gICAgICAgIH07XG4gICAgICB9KTtcbiAgICAgIHRoaXMuY2hhbm5lbFZpZXcucmVuZGVyKCdtdWx0aWNoYW5uZWwnLCB2aWV3cywgeyBpbnRlcnBvbGF0aW9uIH0pO1xuICAgIH0gZWxzZSBpZiAobW9kZSA9PT0gJ2dyYXlzY2FsZScpIHtcbiAgICAgIGNvbnN0IGQgPSBwbGFuZXNbMF07XG4gICAgICBjb25zdCBzdCA9IHN0YXRlc1swXTtcbiAgICAgIHRoaXMuY2hhbm5lbFZpZXcucmVuZGVyKFxuICAgICAgICAnZ3JheXNjYWxlJyxcbiAgICAgICAgW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIHNvdXJjZTogeyBraW5kOiAndHlwZWQnLCB3aWR0aDogZC53aWR0aCwgaGVpZ2h0OiBkLmhlaWdodCwgY2hhbm5lbHM6IDEsIGR0eXBlOiAndWludDgnLCBkYXRhOiBkLmRhdGEgfSxcbiAgICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLmdyYXlzY2FsZUNvbG9ybWFwKCksXG4gICAgICAgICAgICBjb250cmFzdExpbWl0czogW3N0Py5taW4gPz8gMCwgc3Q/Lm1heCA/PyAyNTVdLFxuICAgICAgICAgICAgZ2FtbWE6IHN0Py5nYW1tYSA/PyAxLFxuICAgICAgICAgICAgaW52ZXJ0OiB0aGlzLmludmVydEVuYWJsZWQsXG4gICAgICAgICAgICBzY2FsZSxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgICB7IGludGVycG9sYXRpb24gfSxcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY2hhbm5lbFZpZXcucmVuZGVyKCdyZ2InLCBbeyBzb3VyY2U6IGJpdG1hcCBhcyBJbWFnZUJpdG1hcCwgc2NhbGUgfV0sIHsgaW50ZXJwb2xhdGlvbiB9KTtcbiAgICB9XG4gICAgdGhpcy5pbWFnZVcgPSBmdWxsVztcbiAgICB0aGlzLmltYWdlSCA9IGZ1bGxIO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbmRlciB0aGUgMkQgaW1hZ2Ugd2l0aCBweXJhbWlkYWwge0BsaW5rIFRpbGVkU291cmNlfXMg4oCUIHRoZSB2aWV3IHJlZmluZXMgdG8gaGlnaGVyIHJlc29sdXRpb25cbiAgICogYXMgeW91IHpvb20gaW4gKHRoZSB2aXN1YWwgZmV0Y2hlcyB0aGUgbGV2ZWwgd2hvc2UgdGV4ZWxzIOKJiCBzY3JlZW4gcGl4ZWxzKSBhbmQgc2l0cyBpbiBmdWxsLXJlc1xuICAgKiBjb29yZGluYXRlcyBzbyByZWdpb25zIGFsaWduLiBTYW1lIHRocmVlIG1vZGVzIGFzIHRoZSBzdGl0Y2ggcGF0aC4gUGVyLWNoYW5uZWwgbGF5ZXJzIHVzZSB0aGVcbiAgICogUkVBTCBweXJhbWlkIGxldmVscyAocGVyLWNoYW5uZWwgdGlsZXMgb25seSBleGlzdCB0aGVyZSk7IHRoZSBjb21wb3NpdGUgdXNlcyBhbGwgbGV2ZWxzLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByZW5kZXJJbWFnZVRpbGVkKHo6IG51bWJlciwgZGVzYzogVGlsZURlc2NyaXB0b3IpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB2ID0gdGhpcy52aWV3ZXI7XG4gICAgaWYgKCF2KSByZXR1cm47XG4gICAgY29uc3Qgc3RhdGVzID0gdGhpcy5zdG9yZS5jdXJyZW50Q2hhbm5lbFN0YXRlcygpO1xuICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IGRlc2MuY2hhbm5lbHMgPz8gKHN0YXRlcy5sZW5ndGggfHwgMSk7XG4gICAgY29uc3QgbXVsdGljaGFubmVsID0gISFkZXNjLm11bHRpY2hhbm5lbCAmJiBjaGFubmVsQ291bnQgPiAxO1xuICAgIGNvbnN0IGludGVycG9sYXRpb246ICdsaW5lYXInIHwgJ25lYXJlc3QnID0gdGhpcy5pbWFnZVNtb290aGluZyA/ICdsaW5lYXInIDogJ25lYXJlc3QnO1xuXG4gICAgdGhpcy50aWxlZCA9IHRydWU7XG4gICAgdGhpcy5jaGFubmVsVmlldyA9IG5ldyBNdWx0aUNoYW5uZWxJbWFnZVZpZXcodik7XG5cbiAgICBpZiAobXVsdGljaGFubmVsKSB7XG4gICAgICB0aGlzLmltYWdlTW9kZSA9ICdtdWx0aWNoYW5uZWwnO1xuICAgICAgY29uc3Qgdmlld3M6IENoYW5uZWxWaWV3W10gPSBbXTtcbiAgICAgIGZvciAobGV0IGMgPSAwOyBjIDwgY2hhbm5lbENvdW50OyBjKyspIHtcbiAgICAgICAgY29uc3Qgc3QgPSBzdGF0ZXMuZmluZCgocykgPT4gcy5pbmRleCA9PT0gYyk7XG4gICAgICAgIGNvbnN0IGNvbG9yID0gc3Q/LmNvbG9yID8/IGRlc2MuY2hhbm5lbEluZm8/LltjXT8uY29sb3IgPz8gdGludEZvcihjKTtcbiAgICAgICAgdmlld3MucHVzaCh7XG4gICAgICAgICAgc291cmNlOiB0aGlzLmJ1aWxkVGlsZWRTb3VyY2UoZGVzYywgYywgMSksXG4gICAgICAgICAgdGludDogY29sb3IsXG4gICAgICAgICAgbmFtZTogc3Q/Lm5hbWUgPz8gYGNoJHtjfWAsXG4gICAgICAgICAgY29udHJhc3RMaW1pdHM6IFtzdD8ubWluID8/IDAsIHN0Py5tYXggPz8gMjU1XSxcbiAgICAgICAgICBnYW1tYTogc3Q/LmdhbW1hID8/IDEsXG4gICAgICAgICAgdmlzaWJsZTogc3Q/LnZpc2libGUgPz8gdHJ1ZSxcbiAgICAgICAgICBpbnZlcnQ6IHRoaXMuaW52ZXJ0RW5hYmxlZCxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0aGlzLmNoYW5uZWxWaWV3LnJlbmRlcignbXVsdGljaGFubmVsJywgdmlld3MsIHsgaW50ZXJwb2xhdGlvbiB9KTtcbiAgICB9IGVsc2UgaWYgKGNoYW5uZWxDb3VudCA9PT0gMSkge1xuICAgICAgdGhpcy5pbWFnZU1vZGUgPSAnZ3JheXNjYWxlJztcbiAgICAgIGNvbnN0IHN0ID0gc3RhdGVzWzBdO1xuICAgICAgdGhpcy5jaGFubmVsVmlldy5yZW5kZXIoXG4gICAgICAgICdncmF5c2NhbGUnLFxuICAgICAgICBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgc291cmNlOiB0aGlzLmJ1aWxkVGlsZWRTb3VyY2UoZGVzYywgdW5kZWZpbmVkLCAxKSxcbiAgICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLmdyYXlzY2FsZUNvbG9ybWFwKCksXG4gICAgICAgICAgICBjb250cmFzdExpbWl0czogW3N0Py5taW4gPz8gMCwgc3Q/Lm1heCA/PyAyNTVdLFxuICAgICAgICAgICAgZ2FtbWE6IHN0Py5nYW1tYSA/PyAxLFxuICAgICAgICAgICAgaW52ZXJ0OiB0aGlzLmludmVydEVuYWJsZWQsXG4gICAgICAgICAgfSxcbiAgICAgICAgXSxcbiAgICAgICAgeyBpbnRlcnBvbGF0aW9uIH0sXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmltYWdlTW9kZSA9ICdyZ2InO1xuICAgICAgdGhpcy5jaGFubmVsVmlldy5yZW5kZXIoJ3JnYicsIFt7IHNvdXJjZTogdGhpcy5idWlsZFRpbGVkU291cmNlKGRlc2MsIHVuZGVmaW5lZCwgNCkgfV0sIHtcbiAgICAgICAgaW50ZXJwb2xhdGlvbixcbiAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLmltYWdlVyA9IGRlc2Mud2lkdGg7XG4gICAgdGhpcy5pbWFnZUggPSBkZXNjLmhlaWdodDtcbiAgICAvLyBBd2FpdCBvbiB0aGUgaW5pdGlhbCByZW5kZXIgc28gZ2V0SGlzdG9ncmFtL2F1dG9Db250cmFzdCBoYXZlIGRhdGEgaW1tZWRpYXRlbHk7IHNsaWNlIGNoYW5nZXNcbiAgICAvLyByZWZyZXNoIGZpcmUtYW5kLWZvcmdldCAodGhlIGhpc3RvZ3JhbSBwYW5lIHJldHJpZXMpLlxuICAgIGF3YWl0IHRoaXMucmVmcmVzaEhpc3RvZ3JhbVNhbXBsZXMoeiwgZGVzYyk7XG4gIH1cblxuICAvKiogQnVpbGQgYSBweXJhbWlkYWwgVGlsZWRTb3VyY2UgYmFja2VkIGJ5IHRoZSBzZXJ2ZXIgYC90aWxlYCBlbmRwb2ludC4gYGNoYW5uZWxgIHNlbGVjdHMgYSBiYW5kXG4gICAqICAoZ3JheXNjYWxlIGx1bWluYW5jZSwgcmVhbCBsZXZlbHMgb25seSk7IG9taXQgaXQgZm9yIHRoZSBjb21wb3NpdGUgKFJHQkEsIGFsbCBsZXZlbHMpLiAqL1xuICBwcml2YXRlIGJ1aWxkVGlsZWRTb3VyY2UoXG4gICAgZGVzYzogVGlsZURlc2NyaXB0b3IsXG4gICAgY2hhbm5lbDogbnVtYmVyIHwgdW5kZWZpbmVkLFxuICAgIGNoYW5uZWxzOiAxIHwgNCxcbiAgKTogVGlsZWRTb3VyY2Uge1xuICAgIGNvbnN0IGluZm9CNjQgPSB0aGlzLnRpbGVzLmdldFNlbGVjdGVkSW5mb0I2NCgpID8/ICcnO1xuICAgIC8vIFBlci1jaGFubmVsIHRpbGVzIGV4aXN0IG9ubHkgYXQgUkVBTCBCaW8tRm9ybWF0cyBsZXZlbHM7IHRoZSBjb21wb3NpdGUgZXhpc3RzIGF0IGFsbCBsZXZlbHMuXG4gICAgY29uc3QgdXNhYmxlID1cbiAgICAgIGNoYW5uZWwgPT0gbnVsbFxuICAgICAgICA/IGRlc2MubGV2ZWxzXG4gICAgICAgIDogZGVzYy5sZXZlbHMuc2xpY2UoMCwgTWF0aC5tYXgoMSwgZGVzYy5yZWFsTGV2ZWxzID8/IGRlc2MubGV2ZWxzLmxlbmd0aCkpO1xuICAgIGNvbnN0IGxldmVsU2NhbGVzID0gdXNhYmxlLm1hcCgobCkgPT4gZGVzYy53aWR0aCAvIE1hdGgubWF4KDEsIGwud2lkdGgpKTsgLy8gbGV2ZWwtMCBweCBwZXIgbGV2ZWwgcHhcbiAgICBjb25zdCB0aWxlU2l6ZSA9IGRlc2MudGlsZVNpemUgfHwgVElMRV9TSVpFO1xuICAgIGNvbnN0IGNoID0gY2hhbm5lbCA9PSBudWxsID8gJycgOiBgJmNoYW5uZWw9JHtjaGFubmVsfWA7XG4gICAgY29uc3QgYXBpID0gdGhpcy5hcGk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGtpbmQ6ICd0aWxlZCcsXG4gICAgICB3aWR0aDogZGVzYy53aWR0aCxcbiAgICAgIGhlaWdodDogZGVzYy5oZWlnaHQsXG4gICAgICB0aWxlU2l6ZSxcbiAgICAgIGxldmVsczogdXNhYmxlLmxlbmd0aCxcbiAgICAgIGxldmVsU2NhbGVzLFxuICAgICAgZGVwdGg6IE1hdGgubWF4KDEsIGRlc2MueiB8fCAxKSxcbiAgICAgIGNoYW5uZWxzLFxuICAgICAgZHR5cGU6ICd1aW50OCcsXG4gICAgICBmZXRjaFRpbGU6IGFzeW5jIChrZXk6IFRpbGVLZXkpOiBQcm9taXNlPFBpeGVsQ2h1bms+ID0+IHtcbiAgICAgICAgY29uc3QgcmVzID0gdXNhYmxlW2tleS5sZXZlbF0/LnJlcyA/PyBrZXkubGV2ZWw7XG4gICAgICAgIGNvbnN0IHVybCA9IGAke2FwaX10aWxlP2luZm89JHtpbmZvQjY0fSZyZXM9JHtyZXN9JmNvbD0ke2tleS5jb2x9JnJvdz0ke2tleS5yb3d9Jno9JHtrZXkuen0mdGlsZVNpemU9JHt0aWxlU2l6ZX0ke2NofWA7XG4gICAgICAgIGNvbnN0IGhlYWRlcnMgPSBhd2FpdCB0aGlzLnRpbGVzXG4gICAgICAgICAgLmdldEF1dGhIZWFkZXJzKClcbiAgICAgICAgICAuY2F0Y2goKCkgPT4gKHt9KSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTtcbiAgICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IGZldGNoKHVybCwgeyBoZWFkZXJzIH0pO1xuICAgICAgICBpZiAoIXJlc3Aub2spIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFtuYXBhcmktanNdIHRpbGUgJHtrZXkubGV2ZWx9LyR7a2V5LmNvbH0vJHtrZXkucm93fSDihpIgJHtyZXNwLnN0YXR1c31gKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBibXAgPSBhd2FpdCBjcmVhdGVJbWFnZUJpdG1hcChhd2FpdCByZXNwLmJsb2IoKSk7XG4gICAgICAgIGlmIChjaGFubmVscyA9PT0gNCkgcmV0dXJuIHsgd2lkdGg6IGJtcC53aWR0aCwgaGVpZ2h0OiBibXAuaGVpZ2h0LCBkYXRhOiBibXAgfTtcbiAgICAgICAgcmV0dXJuIHRoaXMuYml0bWFwVG9MdW1pbmFuY2UoYm1wKTtcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxuXG4gIC8qKiAoUmUpZmV0Y2ggYSBjb2Fyc2UgcGVyLWNoYW5uZWwgbHVtaW5hbmNlIHNhbXBsZSBmb3IgdGhlIGhpc3RvZ3JhbSAodGlsZWQgbW9kZSBoYXMgbm8gZnVsbFxuICAgKiAgaW4tbWVtb3J5IHBpeGVscykuIE9uZSBjaGVhcCBvdmVydmlldyB0aWxlIHBlciBjaGFubmVsOyBjYWNoZWQgYnkgY2hhbm5lbCBpbmRleC4gKi9cbiAgcHJpdmF0ZSBhc3luYyByZWZyZXNoSGlzdG9ncmFtU2FtcGxlcyh6OiBudW1iZXIsIGRlc2M6IFRpbGVEZXNjcmlwdG9yKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5oaXN0U2FtcGxlcy5jbGVhcigpO1xuICAgIGlmICh0aGlzLmltYWdlTW9kZSA9PT0gJ3JnYicpIHJldHVybjsgLy8gUkdCIHVzZXMgdGhlIGRpc3BsYXllZC1waXhlbCByZWFkYmFjayAocmdiSGlzdG9ncmFtKVxuICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IHRoaXMuaW1hZ2VNb2RlID09PSAnbXVsdGljaGFubmVsJyA/IGRlc2MuY2hhbm5lbHMgPz8gMSA6IDE7XG4gICAgZm9yIChsZXQgYyA9IDA7IGMgPCBjaGFubmVsQ291bnQ7IGMrKykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgY2ggPSB0aGlzLmltYWdlTW9kZSA9PT0gJ211bHRpY2hhbm5lbCcgPyBjIDogdW5kZWZpbmVkO1xuICAgICAgICBjb25zdCBkID0gYXdhaXQgdGhpcy5mZXRjaENoYW5uZWxEYXRhKHosIGNoLCAxKTsgLy8gYnVkZ2V0IDEg4oaSIGNvYXJzZXN0IHNpbmdsZSB0aWxlXG4gICAgICAgIHRoaXMuaGlzdFNhbXBsZXMuc2V0KGMsIGQuZGF0YSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLyogbGVhdmUgdGhpcyBjaGFubmVsJ3Mgc2FtcGxlIHVuc2V0ICovXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqIFN1YnNjcmliZSBjaGFubmVsIHN0YXRlcyArIGdyYXlzY2FsZSBjb2xvcm1hcCDihpIgbGl2ZS1hcHBseSB0byB0aGUgcmVuZGVyZWQgbGF5ZXJzIChub1xuICAgKiAgcmUtZmV0Y2g7IG9ubHkgeiBjaGFuZ2VzIHJlLWZldGNoKS4gUmVwbGFjZXMgYW55IHByaW9yIHN1YnNjcmlwdGlvbi4gKi9cbiAgcHJpdmF0ZSBzdWJzY3JpYmVEaXNwbGF5U3RhdGUoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5U3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuZGlzcGxheVN1YiA9IGNvbWJpbmVMYXRlc3QoW1xuICAgICAgdGhpcy5zdG9yZS5nZXRDaGFubmVsU3RhdGVzKCksXG4gICAgICB0aGlzLnN0b3JlLmdldENvbG9ybWFwKCksXG4gICAgICB0aGlzLnN0b3JlLmdldFJldmVyc2VTY2FsZSgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRJbnZlcnQoKSxcbiAgICBdKS5zdWJzY3JpYmUoKFtjaGFubmVscywgY29sb3JtYXAsIHJldmVyc2UsIGludmVydF0pID0+IHtcbiAgICAgIHRoaXMuY3VycmVudENvbG9ybWFwID0gKGNvbG9ybWFwIGFzIENvbG9ybWFwTm9kZSkgPz8gbnVsbDtcbiAgICAgIHRoaXMuY3VycmVudFJldmVyc2UgPSByZXZlcnNlO1xuICAgICAgdGhpcy5pbnZlcnRFbmFibGVkID0gaW52ZXJ0O1xuICAgICAgdGhpcy5hcHBseURpc3BsYXlTdGF0ZShjaGFubmVscyk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogU3Vic2NyaWJlIHRoZSBzdG9yZSBjb2xvcm1hcCAoK3JldmVyc2UpLCBpbnZlcnQsIGFuZCBjaGFubmVsIHN0YXRlIOKGkiB0aGUgdm9sdW1lL2lzb3N1cmZhY2VcbiAgICogIHRyYW5zZmVyIGZ1bmN0aW9uOiBjb2xvdXIgKGNoYW5uZWwgdGludCBvciBzZWxlY3RlZCBjb2xvcm1hcCksIHRoZSBpbnRlbnNpdHkgKip3aW5kb3dcbiAgICogIChtaW4vbWF4IOKGkiBjb250cmFzdExpbWl0cykqKiBhbmQgKipnYW1tYSoqLCBtaXJyb3JpbmcgdGhlIGdyYXlzY2FsZSBpbWFnZSdzIGRpc3BsYXkgY29udHJvbHNcbiAgICogIHNvIHRoZSBoaXN0b2dyYW0gcGFuZSBkcml2ZXMgdGhlIDNEIHJlbmRlci4gUmVwbGFjZXMgYW55IHByaW9yIHN1YnNjcmlwdGlvbi4gKi9cbiAgcHJpdmF0ZSBzdWJzY3JpYmVWb2x1bWVEaXNwbGF5U3RhdGUoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5U3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuZGlzcGxheVN1YiA9IGNvbWJpbmVMYXRlc3QoW1xuICAgICAgdGhpcy5zdG9yZS5nZXRDb2xvcm1hcCgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRSZXZlcnNlU2NhbGUoKSxcbiAgICAgIHRoaXMuc3RvcmUuZ2V0SW52ZXJ0KCksXG4gICAgICB0aGlzLnN0b3JlLmdldENoYW5uZWxTdGF0ZXMoKSxcbiAgICBdKS5zdWJzY3JpYmUoKFtjb2xvcm1hcCwgcmV2ZXJzZSwgaW52ZXJ0LCBjaGFubmVsc10pID0+IHtcbiAgICAgIHRoaXMuY3VycmVudENvbG9ybWFwID0gKGNvbG9ybWFwIGFzIENvbG9ybWFwTm9kZSkgPz8gbnVsbDtcbiAgICAgIHRoaXMuY3VycmVudFJldmVyc2UgPSByZXZlcnNlO1xuICAgICAgdGhpcy5pbnZlcnRFbmFibGVkID0gaW52ZXJ0O1xuICAgICAgY29uc3QgdmlldyA9IHRoaXMudm9sdW1lVmlldztcbiAgICAgIGlmICghdmlldykgcmV0dXJuO1xuICAgICAgaWYgKHRoaXMudm9sdW1lTXVsdGljaGFubmVsKSB7XG4gICAgICAgIC8vIEVhY2ggY2hhbm5lbCdzIGxheWVyIGlzIHRpbnRlZCBieSBpdHMgY29sb3VyIGFuZCBnZXRzIGl0cyBvd24gd2luZG93L2dhbW1hL3Zpc2liaWxpdHkuXG4gICAgICAgIHZpZXcubGF5ZXJzLmZvckVhY2goKF8sIGMpID0+IHtcbiAgICAgICAgICBjb25zdCBzdCA9IGNoYW5uZWxzLmZpbmQoKHMpID0+IHMuaW5kZXggPT09IGMpO1xuICAgICAgICAgIHZpZXcudXBkYXRlQ2hhbm5lbChjLCB7XG4gICAgICAgICAgICBjb2xvcm1hcDogdGhpcy5jaGFubmVsVGludENvbG9ybWFwKHN0Py5jb2xvciA/PyAnI2ZmZmZmZicpLFxuICAgICAgICAgICAgLi4uKHN0XG4gICAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgICAgY29udHJhc3RMaW1pdHM6IFtzdC5taW4sIHN0Lm1heF0gYXMgW251bWJlciwgbnVtYmVyXSxcbiAgICAgICAgICAgICAgICAgIGdhbW1hOiBzdC5nYW1tYSxcbiAgICAgICAgICAgICAgICAgIHZpc2libGU6IHN0LnZpc2libGUsXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA6IHt9KSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBzdCA9IGNoYW5uZWxzWzBdO1xuICAgICAgICB2aWV3LnVwZGF0ZUNoYW5uZWwoMCwge1xuICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLnZvbHVtZUNvbG9ybWFwKHN0KSxcbiAgICAgICAgICAuLi4oc3QgPyB7IGNvbnRyYXN0TGltaXRzOiBbc3QubWluLCBzdC5tYXhdIGFzIFtudW1iZXIsIG51bWJlcl0sIGdhbW1hOiBzdC5nYW1tYSB9IDoge30pLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBBIGNoYW5uZWwncyB0aW50IGNvbG9ybWFwIChibGFja+KGkmNvbG91cikgd2l0aCByZXZlcnNlLXNjYWxlIC8gaW52ZXJ0IGFwcGxpZWQgYnkgZmxpcHBpbmcuICovXG4gIHByaXZhdGUgY2hhbm5lbFRpbnRDb2xvcm1hcChjb2xvcjogc3RyaW5nKTogQ29sb3JtYXAgfCBzdHJpbmcge1xuICAgIGxldCBjbWFwOiBDb2xvcm1hcCB8IHN0cmluZyA9IHRpbnRDb2xvcm1hcChjb2xvcik7XG4gICAgaWYgKHRoaXMuY3VycmVudFJldmVyc2UpIGNtYXAgPSByZXZlcnNlQ29sb3JtYXAoY21hcCk7XG4gICAgaWYgKHRoaXMuaW52ZXJ0RW5hYmxlZCkgY21hcCA9IHJldmVyc2VDb2xvcm1hcChjbWFwKTtcbiAgICByZXR1cm4gY21hcDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb2xvdXIgbWFwIGZvciB0aGUgdm9sdW1lL2lzb3N1cmZhY2UuIEEgcmVhbCBjb2xvcm1hcCBzZWxlY3Rpb24gKHZpcmlkaXMvbWFnbWEv4oCmKSB3aW5zO1xuICAgKiBvdGhlcndpc2UgdGhlIGNoYW5uZWwncyBjb2xvdXIgdGludHMgaXQgKHNvIHRoZSBjaGFubmVsLWRpYWxvZyBjb2xvdXIgc3dhdGNoIHJlY29sb3JzIHRoZSAzRFxuICAgKiByZW5kZXIpLiBSZXZlcnNlLXNjYWxlIGFuZCBpbnZlcnQgZWFjaCBmbGlwIHRoZSByYW1wIOKAlCB0aGUgYFZvbHVtZUxheWVyYCBoYXMgbm8gcGVyLWxheWVyXG4gICAqIGludmVydCwgc28gYm90aCBhcmUgZW11bGF0ZWQgYnkgcmV2ZXJzaW5nIHRoZSBjb2xvcm1hcC5cbiAgICovXG4gIHByaXZhdGUgdm9sdW1lQ29sb3JtYXAoc3Q6IElDaGFubmVsU3RhdGUgfCB1bmRlZmluZWQpOiBDb2xvcm1hcCB8IHN0cmluZyB7XG4gICAgY29uc3Qgbm9kZSA9IHRoaXMuY3VycmVudENvbG9ybWFwIGFzIHsgbGFiZWw/OiBzdHJpbmc7IGRhdGE/OiB7IHZhbHVlPzogdW5rbm93biB9IH0gfCBudWxsO1xuICAgIC8vIEEgY29sb3JlZCBjb2xvcm1hcCAodmlyaWRpcy9tYWdtYS/igKYpIGRyaXZlcyB0aGUgdm9sdW1lOyB0aGUgZGVmYXVsdCBncmF5c2NhbGUgZmFtaWx5XG4gICAgLy8gKGdyYXkgLyBHcmV5cyAvIEdyZXlzIEludikgeWllbGRzIHRvIHRoZSBjaGFubmVsJ3MgY29sb3VyIHNvIHRoZSBkaWFsb2cgY29sb3VyIHN3YXRjaFxuICAgIC8vIHJlY29sb3JzIHRoZSAzRCByZW5kZXIuXG4gICAgY29uc3QgbGFiZWwgPSAobm9kZT8ubGFiZWwgPz8gJycpLnRvTG93ZXJDYXNlKCk7XG4gICAgY29uc3QgZ3JheUZhbWlseSA9IGxhYmVsID09PSAnJyB8fCBsYWJlbC5pbmNsdWRlcygnZ3JleScpIHx8IGxhYmVsLmluY2x1ZGVzKCdncmF5Jyk7XG4gICAgY29uc3QgdmFsdWUgPSBub2RlPy5kYXRhPy52YWx1ZTtcbiAgICBjb25zdCBsdXQgPSAhZ3JheUZhbWlseSAmJiB2YWx1ZSAhPSBudWxsID8gYnVpbGRDb2xvcm1hcEx1dCh2YWx1ZSwgZmFsc2UpIDogbnVsbDtcbiAgICBsZXQgY21hcDogQ29sb3JtYXAgfCBzdHJpbmcgPSBsdXRcbiAgICAgID8gY29sb3JtYXBGcm9tTHV0KCd2b2wtY21hcCcsIGx1dClcbiAgICAgIDogdGludENvbG9ybWFwKHN0Py5jb2xvciA/PyAnI2ZmZmZmZicpO1xuICAgIC8vIFJldmVyc2Utc2NhbGUgYW5kIGludmVydCBlYWNoIGZsaXAgdGhlIHJhbXAgKHRoZSBWb2x1bWVMYXllciBoYXMgbm8gcGVyLWxheWVyIGludmVydCkuXG4gICAgaWYgKHRoaXMuY3VycmVudFJldmVyc2UpIGNtYXAgPSByZXZlcnNlQ29sb3JtYXAoY21hcCk7XG4gICAgaWYgKHRoaXMuaW52ZXJ0RW5hYmxlZCkgY21hcCA9IHJldmVyc2VDb2xvcm1hcChjbWFwKTtcbiAgICByZXR1cm4gY21hcDtcbiAgfVxuXG4gIC8qKiBBcHBseSB0aGUgY3VycmVudCBjaGFubmVsIHN0YXRlcyAvIGNvbG9ybWFwIHRvIHRoZSBsaXZlIGxheWVycyAobm8gcmUtZmV0Y2gpLCBkZWxlZ2F0aW5nIHRoZVxuICAgKiAgcGVyLWNoYW5uZWwgbGF5ZXIgbXV0YXRpb25zIHRvIHRoZSB7QGxpbmsgTXVsdGlDaGFubmVsSW1hZ2VWaWV3fS4gKi9cbiAgcHJpdmF0ZSBhcHBseURpc3BsYXlTdGF0ZShjaGFubmVsczogSUNoYW5uZWxTdGF0ZVtdKTogdm9pZCB7XG4gICAgY29uc3QgdmlldyA9IHRoaXMuY2hhbm5lbFZpZXc7XG4gICAgaWYgKCF0aGlzLnZpZXdlciB8fCAhdmlldykgcmV0dXJuO1xuICAgIGlmICh0aGlzLmltYWdlTW9kZSA9PT0gJ211bHRpY2hhbm5lbCcpIHtcbiAgICAgIHZpZXcubGF5ZXJzLmZvckVhY2goKF8sIGMpID0+IHtcbiAgICAgICAgY29uc3Qgc3QgPSBjaGFubmVscy5maW5kKChzKSA9PiBzLmluZGV4ID09PSBjKTtcbiAgICAgICAgaWYgKCFzdCkgcmV0dXJuO1xuICAgICAgICB2aWV3LnVwZGF0ZUNoYW5uZWwoYywge1xuICAgICAgICAgIHRpbnQ6IHN0LmNvbG9yLFxuICAgICAgICAgIGNvbnRyYXN0TGltaXRzOiBbc3QubWluLCBzdC5tYXhdLFxuICAgICAgICAgIGdhbW1hOiBzdC5nYW1tYSxcbiAgICAgICAgICB2aXNpYmxlOiBzdC52aXNpYmxlLFxuICAgICAgICAgIGludmVydDogdGhpcy5pbnZlcnRFbmFibGVkLFxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSBpZiAodGhpcy5pbWFnZU1vZGUgPT09ICdncmF5c2NhbGUnKSB7XG4gICAgICBjb25zdCBzdCA9IGNoYW5uZWxzWzBdO1xuICAgICAgdmlldy51cGRhdGVDaGFubmVsKDAsIHtcbiAgICAgICAgY29sb3JtYXA6IHRoaXMuZ3JheXNjYWxlQ29sb3JtYXAoKSxcbiAgICAgICAgaW52ZXJ0OiB0aGlzLmludmVydEVuYWJsZWQsXG4gICAgICAgIC4uLihzdCA/IHsgY29udHJhc3RMaW1pdHM6IFtzdC5taW4sIHN0Lm1heF0gYXMgW251bWJlciwgbnVtYmVyXSwgZ2FtbWE6IHN0LmdhbW1hIH0gOiB7fSksXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvKiogKFJlKWluc3RhbGwgdGhlIHBoeXNpY2FsIHNjYWxlIGJhciBvdmVyIHRoZSAyRCBpbWFnZSwgc2l6ZWQgZnJvbSB0aGUgaW1hZ2UncyDCtW0vcGl4ZWxcbiAgICogIChgL3RpbGVzL2luZm9gIG1wcFgsIGZhbGxpbmcgYmFjayB0byB0aGUgaW1hZ2UgbWV0YWRhdGEpLiBOby1vcCB3aXRob3V0IGEgcGh5c2ljYWwgc2l6ZS4gKi9cbiAgcHJpdmF0ZSBpbnN0YWxsU2NhbGVCYXIoKTogdm9pZCB7XG4gICAgdGhpcy5zY2FsZUJhcj8uZGVzdHJveSgpO1xuICAgIHRoaXMuc2NhbGVCYXIgPSBudWxsO1xuICAgIGNvbnN0IG1wcFggPSB0aGlzLmRlc2NyaXB0b3I/Lm1wcFggfHwgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ubXBwWCB8fCAwO1xuICAgIGlmICh0aGlzLnZpZXdlciAmJiB0aGlzLmhvc3QgJiYgbXBwWCA+IDApIHtcbiAgICAgIHRoaXMuc2NhbGVCYXIgPSBuZXcgTmFwYXJpU2NhbGVCYXIodGhpcy5ob3N0LCB0aGlzLnZpZXdlci5jYW1lcmEsIG1wcFgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBDb252ZXJ0IGEgbmFwYXJpLWpzIGBIaXN0b2dyYW1gIChiaW4gY291bnQgKyBtaW4vbWF4KSB0byB0aGUgcGFuZSdzIGBJSGlzdG9ncmFtYCAoYmluIGVkZ2VzKS4gKi9cbiAgcHJpdmF0ZSB0b0lIaXN0b2dyYW0oaDogeyBjb3VudHM6IFVpbnQzMkFycmF5OyBiaW5zOiBudW1iZXI7IG1pbjogbnVtYmVyOyBtYXg6IG51bWJlciB9KTogSUhpc3RvZ3JhbSB7XG4gICAgY29uc3Qgc3BhbiA9IGgubWF4IC0gaC5taW4gfHwgMTtcbiAgICBjb25zdCBiaW5zID0gQXJyYXkuZnJvbSh7IGxlbmd0aDogaC5iaW5zIH0sIChfLCBpKSA9PiBoLm1pbiArIChpICogc3BhbikgLyBoLmJpbnMpO1xuICAgIGNvbnN0IGNvdW50cyA9IEFycmF5LmZyb20oaC5jb3VudHMpO1xuICAgIHJldHVybiB7IGJpbnMsIGNvdW50cywgbWF4OiBjb3VudHMucmVkdWNlKChtLCBjKSA9PiAoYyA+IG0gPyBjIDogbSksIDApIH07XG4gIH1cblxuICAvKipcbiAgICogTW91bnQgdGhlIDNEIHZvbHVtZS9pc29zdXJmYWNlLiBBIG11bHRpY2hhbm5lbCBpbWFnZSBiZWNvbWVzIG9uZSBhZGRpdGl2ZSwgdGludGVkXG4gICAqIHtAbGluayBWb2x1bWVMYXllcn0gcGVyIGNoYW5uZWwgKHNvIGVhY2ggY2hhbm5lbCdzIGNvbG91ciBjb21wb3NpdGVzIGludG8gdGhlIHJlbmRlcik7IGFcbiAgICogZ3JheXNjYWxlL2NvbXBvc2l0ZSBpbWFnZSB1c2VzIGEgc2luZ2xlIHZvbHVtZS4gQWRkcyB0aGUgYXhlcyBnaXptbyArIGxhYmVscyBhbmQgd2lyZXMgdGhlXG4gICAqIGRpc3BsYXktc3RhdGUgc3Vic2NyaXB0aW9uLiBUaGUgY2FsbGVyIG93bnMgYHZpZXdlci5yZWFkeWAuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIG1vdW50Vm9sdW1lKFxuICAgIHZpZXdlcjogVmlld2VyLFxuICAgIGluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQsXG4gICAgcGxvdFR5cGU6IFBsb3RUeXBlLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB0b2tlbiA9IHRoaXMubG9hZFRva2VuOyAvLyBiYWlsIGJlZm9yZSByZW5kZXJpbmcgaWYgYSBDYW5jZWwgLyBuZXcgcGxvdCBidW1wcyB0aGlzXG4gICAgY29uc3QgZGVzYyA9IGF3YWl0IHRoaXMuZW5zdXJlRGVzY3JpcHRvcigpO1xuICAgIGNvbnN0IGNoYW5uZWxDb3VudCA9IGRlc2M/LmNoYW5uZWxzID8/IDE7XG4gICAgY29uc3QgbXVsdGljaGFubmVsID0gISFkZXNjPy5tdWx0aWNoYW5uZWwgJiYgY2hhbm5lbENvdW50ID4gMTtcbiAgICBjb25zdCByZXMgPSB2b2x1bWVSZXNvbHV0aW9uRm9yKHRoaXMucmVzb2x1dGlvblNjYWxlKTtcbiAgICBjb25zdCByZW5kZXJpbmc6ICdpc28nIHwgJ21pcCcgPSBpc05hcGFyaUlzb3N1cmZhY2UocGxvdFR5cGUpID8gJ2lzbycgOiAnbWlwJztcbiAgICBjb25zdCBzdGF0ZXMgPSB0aGlzLnN0b3JlLmN1cnJlbnRDaGFubmVsU3RhdGVzKCk7XG5cbiAgICB0aGlzLnZvbHVtZUNoYW5uZWxEYXRhLmNsZWFyKCk7XG4gICAgdGhpcy52b2x1bWVNdWx0aWNoYW5uZWwgPSBtdWx0aWNoYW5uZWw7XG4gICAgdGhpcy5pbWFnZU1vZGUgPSBtdWx0aWNoYW5uZWwgPyAnbXVsdGljaGFubmVsJyA6ICdncmF5c2NhbGUnO1xuICAgIGNvbnN0IHZpZXcgPSBuZXcgTXVsdGlDaGFubmVsVm9sdW1lVmlldyh2aWV3ZXIpO1xuICAgIHRoaXMudm9sdW1lVmlldyA9IHZpZXc7XG5cbiAgICAvLyBBc3NlbWJsZSBwZXItY2hhbm5lbCBzY2FsYXIgdm9sdW1lcyBmcm9tIHRoZSBzZXJ2ZXIgdGlsZXMgKGppdC1zcGVjaWZpYyk7IHRoZSBuYXBhcmktanMgdmlld1xuICAgIC8vIG93bnMgdGhlIGxheWVyIG9yY2hlc3RyYXRpb24gKG9uZSBhZGRpdGl2ZSB0aW50ZWQgdm9sdW1lIHBlciBjaGFubmVsLCBvciBhIHNpbmdsZSBncmF5c2NhbGVcbiAgICAvLyB2b2x1bWUpLiBUaGUgYWRhcHRlciBjb21wdXRlcyBlYWNoIGNoYW5uZWwncyBjb2xvcm1hcCAoaW5jbC4gaW52ZXJ0L3JldmVyc2UgZmxpcHMpLlxuICAgIGxldCBkaW1zOiB7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyOyBkZXB0aDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcbiAgICBjb25zdCBjaGFubmVsczogVm9sdW1lQ2hhbm5lbFtdID0gW107XG4gICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQodHJ1ZSk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgICB0cnkge1xuICAgICAgaWYgKG11bHRpY2hhbm5lbCkge1xuICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IGNoYW5uZWxDb3VudDsgYysrKSB7XG4gICAgICAgICAgY29uc3Qgdm9sID0gYXdhaXQgdGhpcy5hc3NlbWJsZVZvbHVtZShpbmZvLCByZXMsIGMpO1xuICAgICAgICAgIGlmICghdm9sKSBjb250aW51ZTtcbiAgICAgICAgICBkaW1zID0gdm9sO1xuICAgICAgICAgIHRoaXMudm9sdW1lQ2hhbm5lbERhdGEuc2V0KGMsIHZvbC5kYXRhKTtcbiAgICAgICAgICBjb25zdCBzdCA9IHN0YXRlcy5maW5kKChzKSA9PiBzLmluZGV4ID09PSBjKTtcbiAgICAgICAgICBjb25zdCBjb2xvciA9IHN0Py5jb2xvciA/PyBkZXNjPy5jaGFubmVsSW5mbz8uW2NdPy5jb2xvciA/PyB0aW50Rm9yKGMpO1xuICAgICAgICAgIGNoYW5uZWxzLnB1c2goe1xuICAgICAgICAgICAgZGF0YTogdm9sLmRhdGEsXG4gICAgICAgICAgICB3aWR0aDogdm9sLndpZHRoLFxuICAgICAgICAgICAgaGVpZ2h0OiB2b2wuaGVpZ2h0LFxuICAgICAgICAgICAgZGVwdGg6IHZvbC5kZXB0aCxcbiAgICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLmNoYW5uZWxUaW50Q29sb3JtYXAoY29sb3IpLFxuICAgICAgICAgICAgY29udHJhc3RMaW1pdHM6IFtzdD8ubWluID8/IDAsIHN0Py5tYXggPz8gMjU1XSxcbiAgICAgICAgICAgIGdhbW1hOiBzdD8uZ2FtbWEgPz8gMSxcbiAgICAgICAgICAgIHZpc2libGU6IHN0Py52aXNpYmxlID8/IHRydWUsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IHZvbCA9IGF3YWl0IHRoaXMuYXNzZW1ibGVWb2x1bWUoaW5mbywgcmVzKTtcbiAgICAgICAgaWYgKHZvbCkge1xuICAgICAgICAgIGRpbXMgPSB2b2w7XG4gICAgICAgICAgdGhpcy52b2x1bWVDaGFubmVsRGF0YS5zZXQoMCwgdm9sLmRhdGEpO1xuICAgICAgICAgIGNvbnN0IHN0ID0gc3RhdGVzWzBdO1xuICAgICAgICAgIGNoYW5uZWxzLnB1c2goe1xuICAgICAgICAgICAgZGF0YTogdm9sLmRhdGEsXG4gICAgICAgICAgICB3aWR0aDogdm9sLndpZHRoLFxuICAgICAgICAgICAgaGVpZ2h0OiB2b2wuaGVpZ2h0LFxuICAgICAgICAgICAgZGVwdGg6IHZvbC5kZXB0aCxcbiAgICAgICAgICAgIGNvbG9ybWFwOiB0aGlzLnZvbHVtZUNvbG9ybWFwKHN0KSxcbiAgICAgICAgICAgIGNvbnRyYXN0TGltaXRzOiBbc3Q/Lm1pbiA/PyAwLCBzdD8ubWF4ID8/IDI1NV0sXG4gICAgICAgICAgICBnYW1tYTogc3Q/LmdhbW1hID8/IDEsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQoZmFsc2UpO1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgICB9XG5cbiAgICBpZiAoIWRpbXMgfHwgIWNoYW5uZWxzLmxlbmd0aCB8fCB0aGlzLmxvYWRUb2tlbiAhPT0gdG9rZW4pIHJldHVybjsgLy8gY2FuY2VsbGVkIOKGkiBkb24ndCByZW5kZXJcblxuICAgIC8vIFJlc29sdXRpb24taW52YXJpYW50IHdvcmxkIGJveC4gU2l6aW5nIHRoZSBib3ggYnkgdGhlIHNhbXBsZWQgdm94ZWwgY291bnRzIG1hZGUgaGlnaGVyXG4gICAgLy8gaW4tcGxhbmUgcmVzb2x1dGlvbiBncm93IFgvWSB3aGlsZSB0aGUgZGVwdGggc3RheWVkIHRoZSAoY29uc3RhbnQpIHNsaWNlIGNvdW50IOKAlCBzbyBaIGFwcGVhcmVkXG4gICAgLy8gdG8gc2hyaW5rIGF0IGhpZ2hlciByZXNvbHV0aW9uLiBJbnN0ZWFkIGFuY2hvciB0aGUgaW4tcGxhbmUgbG9uZyBzaWRlIHRvIGEgZml4ZWQgcmVmZXJlbmNlIGFuZFxuICAgIC8vIGxldCBaIHNwYW4gdGhlIGZ1bGwgc2xpY2UgY291bnQ7IHRoZSBib3ggc2hhcGUgaXMgdGhlbiBpZGVudGljYWwgYXQgZXZlcnkgZGVjaW1hdGUgZmFjdG9yLiBUaGVcbiAgICAvLyBwZXItYXhpcyBgdm94ZWxTaXplYCAobmFwYXJpIGBzY2FsZWApIG1hcHMgdGhlIHNhbXBsZWQgZ3JpZCBvbnRvIHRoYXQgZml4ZWQgd29ybGQgYm94LlxuICAgIGNvbnN0IGZ1bGxXID0gdGhpcy5kZXNjcmlwdG9yPy53aWR0aCA/PyBkaW1zLndpZHRoO1xuICAgIGNvbnN0IGZ1bGxIID0gdGhpcy5kZXNjcmlwdG9yPy5oZWlnaHQgPz8gZGltcy5oZWlnaHQ7XG4gICAgY29uc3QgZnVsbEQgPVxuICAgICAgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ueiB8fCB0aGlzLmxvYWRlZD8uaW1hZ2VJbmZvLnVybHM/Lmxlbmd0aCB8fCBkaW1zLmRlcHRoO1xuICAgIGNvbnN0IGZ1bGxMb25nID0gTWF0aC5tYXgoMSwgZnVsbFcsIGZ1bGxIKTtcbiAgICBjb25zdCB3b3JsZCA9IHtcbiAgICAgIHdpZHRoOiAoZnVsbFcgKiBWT0xVTUVfV09STERfSU5QTEFORV9SRUYpIC8gZnVsbExvbmcsXG4gICAgICBoZWlnaHQ6IChmdWxsSCAqIFZPTFVNRV9XT1JMRF9JTlBMQU5FX1JFRikgLyBmdWxsTG9uZyxcbiAgICAgIGRlcHRoOiBmdWxsRCxcbiAgICB9O1xuICAgIC8vIEJhc2UgYm94IChaLXNjYWxlID0gMSkgKyB0aGUgc2FtcGxlZCBkZXB0aCBkcml2ZSB0aGUgbGl2ZSBaLWhlaWdodCBoYW5kbGUgYmVsb3c7IHRoZSBwZXJzaXN0ZWRcbiAgICAvLyBgdm9sdW1lWlNjYWxlYCAodXNlciBkcmFnKSBhcHBsaWVzIG9uIHRvcCBzbyBjaGFuZ2luZyByZXNvbHV0aW9uIGtlZXBzIHRoZSBjaG9zZW4gaGVpZ2h0LlxuICAgIHRoaXMudm9sdW1lV29ybGRCYXNlID0gd29ybGQ7XG4gICAgdGhpcy52b2x1bWVTYW1wbGVkRGVwdGggPSBNYXRoLm1heCgxLCBkaW1zLmRlcHRoKTtcbiAgICBjb25zdCB3b3JsZFogPSB3b3JsZC5kZXB0aCAqIHRoaXMudm9sdW1lWlNjYWxlO1xuICAgIGNvbnN0IHZveGVsU2l6ZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdID0gW1xuICAgICAgd29ybGQud2lkdGggLyBkaW1zLndpZHRoLFxuICAgICAgd29ybGQuaGVpZ2h0IC8gZGltcy5oZWlnaHQsXG4gICAgICB3b3JsZFogLyB0aGlzLnZvbHVtZVNhbXBsZWREZXB0aCxcbiAgICBdO1xuICAgIGZvciAoY29uc3QgY2ggb2YgY2hhbm5lbHMpIGNoLnZveGVsU2l6ZSA9IHZveGVsU2l6ZTtcblxuICAgIHZpZXcucmVuZGVyKG11bHRpY2hhbm5lbCA/ICdtdWx0aWNoYW5uZWwnIDogJ2dyYXlzY2FsZScsIGNoYW5uZWxzLCB7IHJlbmRlcmluZyB9KTtcbiAgICB0aGlzLmltYWdlVyA9IGRpbXMud2lkdGg7XG4gICAgdGhpcy5pbWFnZUggPSBkaW1zLmhlaWdodDtcbiAgICB0aGlzLnZvbHVtZURpbXMgPSBkaW1zO1xuXG4gICAgLy8gM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gKyBsYWJlbHMsIHNoYXJpbmcgdGhlIHZvbHVtZSdzIHdvcmxkIGJveCBzbyB0aGUgZ2l6bW8gdHJhY2tzXG4gICAgLy8gdGhlIHJlbmRlcmVkIHByb3BvcnRpb25zLiBQaHlzaWNhbCBzY2FsZSB0ZXh0IHN0aWxsIGNvbWVzIGZyb20gdGhlIEZVTEwgaW1hZ2UgZXh0ZW50LlxuICAgIGNvbnN0IG1wcFggPSB0aGlzLmRlc2NyaXB0b3I/Lm1wcFggfHwgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ubXBwWCB8fCAwO1xuICAgIHRoaXMuYXhlc0xheWVyID0gdmlld2VyLmFkZEF4ZXMod29ybGQud2lkdGgsIHdvcmxkLmhlaWdodCwgd29ybGRaLCB7IHZpc2libGU6IHRoaXMuYXhlc1Zpc2libGUgfSk7XG4gICAgaWYgKHRoaXMuaG9zdCkge1xuICAgICAgdGhpcy5heGVzTGFiZWxzID0gbmV3IE5hcGFyaUF4ZXNMYWJlbHMoXG4gICAgICAgIHRoaXMuaG9zdCxcbiAgICAgICAgdmlld2VyLmNhbWVyYTNkLFxuICAgICAgICB0aGlzLmJ1aWxkQXhlc0xhYmVscyh7IHdpZHRoOiB3b3JsZC53aWR0aCwgaGVpZ2h0OiB3b3JsZC5oZWlnaHQsIGRlcHRoOiB3b3JsZFogfSwgbXBwWCksXG4gICAgICApO1xuICAgICAgdGhpcy5heGVzTGFiZWxzLnNldFZpc2libGUodGhpcy5heGVzVmlzaWJsZSk7XG4gICAgICAvLyBJbi12aWV3IGRyYWcgaGFuZGxlIGF0IHRoZSBUT1AgRU5EIE9GIFRIRSBaIEFYSVMgKHRoZSBib3gncyBtaW4tWFkgY29ybmVyLCB3aGVyZSB0aGUgYmx1ZVxuICAgICAgLy8gXCJaXCIgYXhpcyArIGxhYmVsIGxpdmUpLCBzbyBpdCByZWFkcyBhcyB0aGUgWi1oZWlnaHQgY29udHJvbC4gRmxvYXRlZCBhIGxpdHRsZSBwYXN0IHRoZSBheGlzXG4gICAgICAvLyB0aXAgKMOXMS4xMikgc28gdGhlIGdyaXAgY2xlYXJzIHRoZSBcIlogwrcg4oCmXCIgbGFiZWwuIERyYWcg4oaVIHRvIHJlc3RyZXRjaCBaIGxpdmUuXG4gICAgICB0aGlzLnpIYW5kbGUgPSBuZXcgTmFwYXJpVm9sdW1lWkhhbmRsZSh0aGlzLmhvc3QsIHZpZXdlci5jYW1lcmEzZCwge1xuICAgICAgICB0b3BBbmNob3I6ICgpID0+IFtcbiAgICAgICAgICAtdGhpcy52b2x1bWVXb3JsZEJhc2UhLndpZHRoIC8gMixcbiAgICAgICAgICAtdGhpcy52b2x1bWVXb3JsZEJhc2UhLmhlaWdodCAvIDIsXG4gICAgICAgICAgKCh0aGlzLnZvbHVtZVdvcmxkQmFzZSEuZGVwdGggKiB0aGlzLnZvbHVtZVpTY2FsZSkgLyAyKSAqIDEuMTIsXG4gICAgICAgIF0sXG4gICAgICAgIGdldFNjYWxlOiAoKSA9PiB0aGlzLnZvbHVtZVpTY2FsZSxcbiAgICAgICAgc2V0U2NhbGU6IChzKSA9PiB0aGlzLnNldFZvbHVtZVpTY2FsZShzKSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLnN1YnNjcmliZVZvbHVtZURpc3BsYXlTdGF0ZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc3RyZXRjaCB0aGUgdm9sdW1lJ3MgWiBoZWlnaHQgbGl2ZSAoZHJpdmVuIGJ5IHRoZSBpbi12aWV3IHtAbGluayBOYXBhcmlWb2x1bWVaSGFuZGxlfSkuIE9ubHlcbiAgICogdGhlIHBlci1heGlzIGB2b3hlbFNpemVgIC8gYXhlcyBkZXB0aCBjaGFuZ2Ug4oCUIHRoZSB2b3hlbCB0ZXh0dXJlcyBhcmUgdW50b3VjaGVkIOKAlCBzbyBkcmFnZ2luZyBpc1xuICAgKiBzbW9vdGguIGBmYWN0b3JgIGlzIHJlbGF0aXZlIHRvIHRoZSB2b2x1bWUncyBuYXR1cmFsIHByb3BvcnRpb25zICgxKS4gUGVyc2lzdHMgYWNyb3NzIHJlLW1vdW50c1xuICAgKiAocmVzb2x1dGlvbiBjaGFuZ2VzKSBzbyB0aGUgY2hvc2VuIGhlaWdodCBzdGlja3MuXG4gICAqL1xuICBwcml2YXRlIHNldFZvbHVtZVpTY2FsZShmYWN0b3I6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMudm9sdW1lWlNjYWxlID0gTWF0aC5taW4oMTAsIE1hdGgubWF4KDAuMSwgZmFjdG9yKSk7XG4gICAgY29uc3QgYmFzZSA9IHRoaXMudm9sdW1lV29ybGRCYXNlO1xuICAgIGlmICghYmFzZSB8fCAhdGhpcy52b2x1bWVWaWV3KSByZXR1cm47XG4gICAgY29uc3Qgd29ybGRaID0gYmFzZS5kZXB0aCAqIHRoaXMudm9sdW1lWlNjYWxlO1xuICAgIGNvbnN0IHZzWiA9IHdvcmxkWiAvIHRoaXMudm9sdW1lU2FtcGxlZERlcHRoO1xuICAgIGZvciAoY29uc3QgbGF5ZXIgb2YgdGhpcy52b2x1bWVWaWV3LmxheWVycykge1xuICAgICAgY29uc3QgW3N4LCBzeV0gPSBsYXllci52b3hlbFNpemU7XG4gICAgICBsYXllci52b3hlbFNpemUgPSBbc3gsIHN5LCB2c1pdO1xuICAgIH1cbiAgICBpZiAodGhpcy5heGVzTGF5ZXIpIHRoaXMuYXhlc0xheWVyLmRlcHRoID0gd29ybGRaO1xuICAgIGNvbnN0IG1wcFggPSB0aGlzLmRlc2NyaXB0b3I/Lm1wcFggfHwgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ubXBwWCB8fCAwO1xuICAgIHRoaXMuYXhlc0xhYmVscz8udXBkYXRlQW5jaG9ycyhcbiAgICAgIHRoaXMuYnVpbGRBeGVzTGFiZWxzKHsgd2lkdGg6IGJhc2Uud2lkdGgsIGhlaWdodDogYmFzZS5oZWlnaHQsIGRlcHRoOiB3b3JsZFogfSwgbXBwWCksXG4gICAgKTtcbiAgICB0aGlzLnpIYW5kbGU/LnJlcG9zaXRpb24oKTtcbiAgICB0aGlzLnZpZXdlcj8ucmVxdWVzdFJlbmRlcigpO1xuICB9XG5cbiAgLyoqIEJ1aWxkIHRoZSBYL1kvWiBheGlzLWVuZCBsYWJlbCBzcGVjcyBmb3IgdGhlIDNEIGdpem1vLiBBbmNob3JzIGFyZSBpbiB0aGUgdm9sdW1lJ3MgY2VudHJlZFxuICAgKiAgd29ybGQgYm94IChtYXRjaGluZyB0aGUgQXhlc0xheWVyIGdlb21ldHJ5KTsgdGhlIHNjYWxlIHRleHQgcmVmbGVjdHMgdGhlIEZVTEwgaW1hZ2UgZXh0ZW50IOKAlFxuICAgKiAgcGh5c2ljYWwgwrVtIHdoZW4gwrVtL3BpeGVsIGlzIGtub3duLCBlbHNlIHBpeGVsIChYL1kpIC8gc2xpY2UgKFopIGNvdW50cy4gKi9cbiAgcHJpdmF0ZSBidWlsZEF4ZXNMYWJlbHMoXG4gICAgdm9sOiB7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyOyBkZXB0aDogbnVtYmVyIH0sXG4gICAgbXBwWDogbnVtYmVyLFxuICApOiBBeGlzTGFiZWxTcGVjW10ge1xuICAgIGNvbnN0IGh4ID0gdm9sLndpZHRoIC8gMjtcbiAgICBjb25zdCBoeSA9IHZvbC5oZWlnaHQgLyAyO1xuICAgIGNvbnN0IGh6ID0gdm9sLmRlcHRoIC8gMjtcbiAgICBjb25zdCBkZXNjVyA9IHRoaXMuZGVzY3JpcHRvcj8ud2lkdGggPz8gdm9sLndpZHRoO1xuICAgIGNvbnN0IGRlc2NIID0gdGhpcy5kZXNjcmlwdG9yPy5oZWlnaHQgPz8gdm9sLmhlaWdodDtcbiAgICBjb25zdCBzbGljZXMgPVxuICAgICAgdGhpcy5sb2FkZWQ/LmltYWdlSW5mby5pbWFnZU1ldGE/LlswXT8ueiB8fCB0aGlzLmxvYWRlZD8uaW1hZ2VJbmZvLnVybHM/Lmxlbmd0aCB8fCB2b2wuZGVwdGg7XG4gICAgY29uc3QgbGVuID0gKHB4OiBudW1iZXIpOiBzdHJpbmcgPT4gKG1wcFggPiAwID8gZm9ybWF0VW0ocHggKiBtcHBYKSA6IGAke3B4fSBweGApO1xuICAgIHJldHVybiBbXG4gICAgICB7IGFuY2hvcjogW2h4LCAtaHksIC1oel0sIHRleHQ6IGBYIMK3ICR7bGVuKGRlc2NXKX1gLCBjb2xvcjogJyNlZDQ1NDUnIH0sXG4gICAgICB7IGFuY2hvcjogWy1oeCwgaHksIC1oel0sIHRleHQ6IGBZIMK3ICR7bGVuKGRlc2NIKX1gLCBjb2xvcjogJyM0ZGQ5NTknIH0sXG4gICAgICB7IGFuY2hvcjogWy1oeCwgLWh5LCBoel0sIHRleHQ6IGBaIMK3ICR7c2xpY2VzfSBweGAsIGNvbG9yOiAnIzY2OGNmZicgfSxcbiAgICBdO1xuICB9XG5cbiAgLyoqXG4gICAqIEFzc2VtYmxlIGEgZG93bnNhbXBsZWQgdWludDggdm9sdW1lIChsdW1pbmFuY2UpIGZyb20gdGhlIHBlci1zbGljZSB0aWxlIGVuZHBvaW50LiBTbGljZXMgYXJlXG4gICAqIGZldGNoZWQgd2l0aCBib3VuZGVkIGNvbmN1cnJlbmN5IChrZWVwcyB0aGUgY29ubmVjdGlvbiBwb29sIGZ1bGwgd2l0aG91dCBmbG9vZGluZyBpdCBvbiBhIGRlZXBcbiAgICogc3RhY2spIGFuZCByZWFkIGludG8gdGhlIHZvbHVtZSBhcyBlYWNoIGFycml2ZXMsIGRyaXZpbmcge0BsaW5rIHN0YWNrTG9hZGluZ1Byb2dyZXNzJH0gc28gdGhlXG4gICAqIGhvc3Qgc2hvd3MgYSBkZXRlcm1pbmF0ZSBwcm9ncmVzcyBiYXIgaW5zdGVhZCBvZiBhIGJhcmUgc3Bpbm5lci5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgYXNzZW1ibGVWb2x1bWUoXG4gICAgaW5mbzogSUltYWdlSW5mbyB8IHVuZGVmaW5lZCxcbiAgICBvcHRzOiB7IG1heFNsaWNlPzogbnVtYmVyOyBzbGljZVN0ZXA/OiBudW1iZXIgfSA9IHt9LFxuICAgIGNoYW5uZWw/OiBudW1iZXIsXG4gICk6IFByb21pc2U8eyBkYXRhOiBVaW50OEFycmF5OyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlcjsgZGVwdGg6IG51bWJlciB9IHwgbnVsbD4ge1xuICAgIGNvbnN0IHRva2VuID0gdGhpcy5sb2FkVG9rZW47IC8vIGJhaWwgaWYgYSBDYW5jZWwgLyBuZXcgcGxvdCBidW1wcyB0aGlzIHdoaWxlIHdlIGZldGNoXG4gICAgY29uc3QgZnVsbERlcHRoID0gaW5mbz8uaW1hZ2VNZXRhPy5bMF0/LnogfHwgaW5mbz8udXJscz8ubGVuZ3RoIHx8IDE7XG4gICAgaWYgKGZ1bGxEZXB0aCA8IDEpIHtcbiAgICAgIGNvbnNvbGUud2FybignW25hcGFyaS1qc10gbm8gc2xpY2VzIHRvIGFzc2VtYmxlIGEgdm9sdW1lJyk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgY29uc3Qgc3RlcCA9IE1hdGgubWF4KDEsIE1hdGguZmxvb3Iob3B0cy5zbGljZVN0ZXAgPz8gMSkpO1xuICAgIGNvbnN0IG1heFNsaWNlID0gb3B0cy5tYXhTbGljZSA/PyBWT0xVTUVfTUFYX1NMSUNFO1xuICAgIC8vIFNvdXJjZS1zbGljZSBpbmRpY2VzIHNhbXBsZWQgaW50byB0aGUgdm9sdW1lIChldmVyeSBgc3RlcGB0aCBwbGFuZSDihpIgbG93LXJlcyBpcyBmYXN0ZXIpLlxuICAgIGNvbnN0IHpJbmRpY2VzOiBudW1iZXJbXSA9IFtdO1xuICAgIGZvciAobGV0IHogPSAwOyB6IDwgZnVsbERlcHRoOyB6ICs9IHN0ZXApIHpJbmRpY2VzLnB1c2goeik7XG4gICAgY29uc3QgZGVwdGggPSB6SW5kaWNlcy5sZW5ndGg7XG5cbiAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KDApO1xuICAgIHRyeSB7XG4gICAgICAvLyBGZXRjaCBlYWNoIHNsaWNlIGF0IGEgcHlyYW1pZCBsZXZlbCBtYXRjaGluZyBgbWF4U2xpY2VgIChidWRnZXQgc2NhbGVzIHdpdGggdGhlIGRlY2ltYXRlXG4gICAgICAvLyBmYWN0b3IpLCBzbyBhIGhpZ2hlciBmYWN0b3IgcHVsbHMgYSBmaW5lciBsZXZlbCDihpIgbW9yZSByZWFsIGluLXBsYW5lIGRldGFpbDsgdGhlbiBkb3duc2FtcGxlXG4gICAgICAvLyB0byBgbWF4U2xpY2VgLiBgY2hhbm5lbGAgc2VsZWN0cyBhIGJhbmQgKG11bHRpY2hhbm5lbCB2b2x1bWUpOyBvbWl0IGZvciB0aGUgZ3JheXNjYWxlXG4gICAgICAvLyBjb21wb3NpdGUuIFRoZSBjYWxsZXIgb3ducyB0aGUgc3RhY2tMb2FkaW5nIGZsYWcgKG11bHRpY2hhbm5lbCBhc3NlbWJsZXMgY2hhbm5lbHMgaW4gdHVybikuXG4gICAgICBjb25zdCBidWRnZXQgPSB0aGlzLnRpbGVCdWRnZXRGb3IobWF4U2xpY2UpO1xuICAgICAgY29uc3QgZmlyc3QgPSBhd2FpdCB0aGlzLmZldGNoU2xpY2UoekluZGljZXNbMF0sIGNoYW5uZWwsIGJ1ZGdldCk7XG4gICAgICBjb25zdCBzY2FsZSA9IE1hdGgubWluKDEsIG1heFNsaWNlIC8gTWF0aC5tYXgoZmlyc3Qud2lkdGgsIGZpcnN0LmhlaWdodCwgMSkpO1xuICAgICAgY29uc3Qgd2lkdGggPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKGZpcnN0LndpZHRoICogc2NhbGUpKTtcbiAgICAgIGNvbnN0IGhlaWdodCA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoZmlyc3QuaGVpZ2h0ICogc2NhbGUpKTtcbiAgICAgIGNvbnN0IGRhdGEgPSBuZXcgVWludDhBcnJheSh3aWR0aCAqIGhlaWdodCAqIGRlcHRoKTtcblxuICAgICAgbGV0IGNhbnZhczogSFRNTENhbnZhc0VsZW1lbnQgfCBPZmZzY3JlZW5DYW52YXM7XG4gICAgICBpZiAodHlwZW9mIE9mZnNjcmVlbkNhbnZhcyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgY2FudmFzID0gbmV3IE9mZnNjcmVlbkNhbnZhcyh3aWR0aCwgaGVpZ2h0KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgICAgICBjYW52YXMud2lkdGggPSB3aWR0aDtcbiAgICAgICAgY2FudmFzLmhlaWdodCA9IGhlaWdodDtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGN0eCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpIGFzXG4gICAgICAgIHwgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEXG4gICAgICAgIHwgT2Zmc2NyZWVuQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEXG4gICAgICAgIHwgbnVsbDtcbiAgICAgIGlmICghY3R4KSB0aHJvdyBuZXcgRXJyb3IoJ25hcGFyaS1qcyB2b2x1bWUgYXNzZW1ibHk6IDJEIGNvbnRleHQgdW5hdmFpbGFibGUnKTtcblxuICAgICAgLy8gUmVhZCBvbmUgZmV0Y2hlZCBzbGljZSBiaXRtYXAgaW50byB0aGUgdm9sdW1lIHBsYW5lIGB6YCAobHVtaW5hbmNlKS4gU3luY2hyb25vdXMgYmV0d2VlblxuICAgICAgLy8gYXdhaXRzLCBzbyB0aGUgc2hhcmVkIDJEIGNvbnRleHQgaXMgc2FmZSB0byByZXVzZSBhY3Jvc3MgdGhlIGNvbmN1cnJlbnQgZmV0Y2ggd29ya2Vycy5cbiAgICAgIGxldCBkb25lID0gMDtcbiAgICAgIGNvbnN0IHJlYWRTbGljZSA9ICh6OiBudW1iZXIsIGJtcDogSW1hZ2VCaXRtYXApOiB2b2lkID0+IHtcbiAgICAgICAgY3R4LmNsZWFyUmVjdCgwLCAwLCB3aWR0aCwgaGVpZ2h0KTtcbiAgICAgICAgY3R4LmRyYXdJbWFnZShibXAsIDAsIDAsIHdpZHRoLCBoZWlnaHQpO1xuICAgICAgICBjb25zdCByZ2JhID0gY3R4LmdldEltYWdlRGF0YSgwLCAwLCB3aWR0aCwgaGVpZ2h0KS5kYXRhO1xuICAgICAgICBjb25zdCBiYXNlID0geiAqIHdpZHRoICogaGVpZ2h0O1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHdpZHRoICogaGVpZ2h0OyBpKyspIHtcbiAgICAgICAgICBkYXRhW2Jhc2UgKyBpXSA9XG4gICAgICAgICAgICAocmdiYVtpICogNF0gKiAwLjI5OSArIHJnYmFbaSAqIDQgKyAxXSAqIDAuNTg3ICsgcmdiYVtpICogNCArIDJdICogMC4xMTQpIHwgMDtcbiAgICAgICAgfVxuICAgICAgICBibXAuY2xvc2U/LigpO1xuICAgICAgICBkb25lKys7XG4gICAgICAgIHRoaXMuc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkLm5leHQoTWF0aC5yb3VuZCgoZG9uZSAvIGRlcHRoKSAqIDEwMCkpO1xuICAgICAgfTtcblxuICAgICAgcmVhZFNsaWNlKDAsIGZpcnN0KTtcblxuICAgICAgLy8gUmVtYWluaW5nIHBsYW5lcyB2aWEgYSBzbWFsbCBwb29sIG9mIHdvcmtlcnMgZHJhaW5pbmcgYSBzaGFyZWQgcXVldWUuIEVhY2ggcGxhbmUgYHBgXG4gICAgICAvLyBtYXBzIHRvIHNvdXJjZSBzbGljZSBgekluZGljZXNbcF1gIChzdWJzYW1wbGVkIGZvciBsb3ctcmVzKS5cbiAgICAgIGNvbnN0IHBlbmRpbmc6IG51bWJlcltdID0gW107XG4gICAgICBmb3IgKGxldCBwID0gMTsgcCA8IGRlcHRoOyBwKyspIHBlbmRpbmcucHVzaChwKTtcbiAgICAgIGNvbnN0IHdvcmtlciA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgICAgZm9yICg7Oykge1xuICAgICAgICAgIGNvbnN0IHAgPSBwZW5kaW5nLnNoaWZ0KCk7XG4gICAgICAgICAgaWYgKHAgPT09IHVuZGVmaW5lZCB8fCB0aGlzLmxvYWRUb2tlbiAhPT0gdG9rZW4pIHJldHVybjtcbiAgICAgICAgICByZWFkU2xpY2UocCwgYXdhaXQgdGhpcy5mZXRjaFNsaWNlKHpJbmRpY2VzW3BdLCBjaGFubmVsLCBidWRnZXQpKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNvbnN0IHBvb2xTaXplID0gTWF0aC5taW4oVk9MVU1FX0ZFVENIX0NPTkNVUlJFTkNZLCBNYXRoLm1heCgxLCBkZXB0aCAtIDEpKTtcbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oeyBsZW5ndGg6IHBvb2xTaXplIH0sICgpID0+IHdvcmtlcigpKSk7XG5cbiAgICAgIGlmICh0aGlzLmxvYWRUb2tlbiAhPT0gdG9rZW4pIHJldHVybiBudWxsOyAvLyBjYW5jZWxsZWQg4oaSIGRvbid0IHJlbmRlciBhIHBhcnRpYWwgdm9sdW1lXG4gICAgICByZXR1cm4geyBkYXRhLCB3aWR0aCwgaGVpZ2h0LCBkZXB0aCB9O1xuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KDApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBNb3VudCB0aGUgTkFQQVJJX1NDQVRURVIgMkQgc2NhdHRlcjogdGhlIHNsaWNlIGltYWdlIHdpdGggYSBwb2ludHMgbGF5ZXIgYXQgZWFjaCByZWdpb24nc1xuICAgKiBjZW50cm9pZCAobmFwYXJpLWpzIGFuYWxvZyBvZiBQbG90bHkncyByZWdpb24tY2VudHJvaWQgc2NhdHRlcikuIFJlYnVpbGRzIHRoZSBwb2ludHMgbGl2ZSBhc1xuICAgKiByZWdpb25zIGNoYW5nZS5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgbW91bnRTY2F0dGVyKHZpZXdlcjogVmlld2VyLCB6OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLnJlbmRlckltYWdlKHopO1xuICAgIHRoaXMuZml0Q2FtZXJhU29vbigpO1xuICAgIHRoaXMuc3Vic2NyaWJlRGlzcGxheVN0YXRlKCk7XG4gICAgdGhpcy5pbnN0YWxsU2NhbGVCYXIoKTtcbiAgICB0aGlzLnJlYnVpbGRTY2F0dGVyUG9pbnRzKCk7XG4gICAgdGhpcy5zY2F0dGVyUmVnaW9uU3ViID0gdGhpcy5yZWdpb25TdG9yZVxuICAgICAgLmdldFJlZ2lvblVwZGF0ZUV2ZW50KClcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy5yZWJ1aWxkU2NhdHRlclBvaW50cygpKTtcbiAgICB0aGlzLnNjaGVkdWxlUmVhZGJhY2soKTtcbiAgfVxuXG4gIC8qKiAoUmUpYnVpbGQgdGhlIDJEIHNjYXR0ZXIncyBwb2ludCBsYXllciBhdCB0aGUgY3VycmVudCByZWdpb24gY2VudHJvaWRzLiAqL1xuICBwcml2YXRlIHJlYnVpbGRTY2F0dGVyUG9pbnRzKCk6IHZvaWQge1xuICAgIGNvbnN0IHYgPSB0aGlzLnZpZXdlcjtcbiAgICBpZiAoIXYpIHJldHVybjtcbiAgICBpZiAodGhpcy5zY2F0dGVyMmRQb2ludHMpIHtcbiAgICAgIHYubGF5ZXJzLnJlbW92ZSh0aGlzLnNjYXR0ZXIyZFBvaW50cyk7XG4gICAgICB0aGlzLnNjYXR0ZXIyZFBvaW50cyA9IG51bGw7XG4gICAgfVxuICAgIGNvbnN0IGNlbnRyb2lkcyA9IHRoaXMucmVnaW9uQ2VudHJvaWRzKCk7XG4gICAgaWYgKGNlbnRyb2lkcy5sZW5ndGggPT09IDApIHJldHVybjtcbiAgICB0aGlzLnNjYXR0ZXIyZFBvaW50cyA9IHYuYWRkUG9pbnRzKGNlbnRyb2lkcywge1xuICAgICAgc2l6ZTogMTIsXG4gICAgICBmYWNlQ29sb3I6IFsxLCAwLjg1LCAwLjIsIDFdLFxuICAgICAgYm9yZGVyQ29sb3I6IFswLCAwLCAwLCAxXSxcbiAgICAgIGJvcmRlcldpZHRoOiAyLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIFJlZ2lvbiBjZW50cm9pZHMgYXMgZmxhdCBgW3gsIHksIOKApl1gIGRhdGEgY29vcmRzIChyZWN0YW5nbGUgLyBwb2x5Z29uIC8gbXVsdGlwb2x5Z29uKS4gKi9cbiAgcHJpdmF0ZSByZWdpb25DZW50cm9pZHMoKTogRmxvYXQzMkFycmF5IHtcbiAgICBjb25zdCBvdXQ6IG51bWJlcltdID0gW107XG4gICAgY29uc3QgcG9seUNlbnRyb2lkID0gKHhzOiBudW1iZXJbXSwgeXM6IG51bWJlcltdKTogdm9pZCA9PiB7XG4gICAgICBjb25zdCBuID0geHMubGVuZ3RoO1xuICAgICAgaWYgKG4gPT09IDApIHJldHVybjtcbiAgICAgIGxldCBjeCA9IDA7XG4gICAgICBsZXQgY3kgPSAwO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuOyBpKyspIHtcbiAgICAgICAgY3ggKz0geHNbaV07XG4gICAgICAgIGN5ICs9IHlzW2ldO1xuICAgICAgfVxuICAgICAgb3V0LnB1c2goY3ggLyBuLCBjeSAvIG4pO1xuICAgIH07XG4gICAgZm9yIChjb25zdCByIG9mIHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9ucygpKSB7XG4gICAgICBjb25zdCBiID0gci5ib3VuZHMgYXNcbiAgICAgICAgfCB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9XG4gICAgICAgIHwgeyB4cG9pbnRzOiBudW1iZXJbXTsgeXBvaW50czogbnVtYmVyW10gfVxuICAgICAgICB8IHsgcG9seWdvbnM6IHsgeHBvaW50czogbnVtYmVyW107IHlwb2ludHM6IG51bWJlcltdIH1bXSB9XG4gICAgICAgIHwgbnVsbFxuICAgICAgICB8IHVuZGVmaW5lZDtcbiAgICAgIGlmICghYikgY29udGludWU7XG4gICAgICBpZiAoJ3dpZHRoJyBpbiBiICYmICd4JyBpbiBiKSB7XG4gICAgICAgIG91dC5wdXNoKGIueCArIGIud2lkdGggLyAyLCBiLnkgKyBiLmhlaWdodCAvIDIpO1xuICAgICAgfSBlbHNlIGlmICgneHBvaW50cycgaW4gYikge1xuICAgICAgICBwb2x5Q2VudHJvaWQoYi54cG9pbnRzLCBiLnlwb2ludHMpO1xuICAgICAgfSBlbHNlIGlmICgncG9seWdvbnMnIGluIGIpIHtcbiAgICAgICAgZm9yIChjb25zdCBwIG9mIGIucG9seWdvbnMpIHBvbHlDZW50cm9pZChwLnhwb2ludHMsIHAueXBvaW50cyk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBuZXcgRmxvYXQzMkFycmF5KG91dCk7XG4gIH1cblxuICAvKipcbiAgICogTW91bnQgdGhlIE5BUEFSSV9TQ0FUVEVSM0QgM0Qgc2NhdHRlcjogdGhlIGRvd25zYW1wbGVkIHZveGVsIGdyaWQgYXMgYSAzRCBwb2ludCBjbG91ZCBjb2xvcmVkXG4gICAqIGJ5IGludGVuc2l0eSAobmFwYXJpLWpzIGFuYWxvZyBvZiBQbG90bHkncyB2b3hlbCBzY2F0dGVyM2QpLiBBc3NlbWJsZXMgYSBjb2Fyc2Ugdm9sdW1lLCB0aGVuXG4gICAqIGVtaXRzIGEgZmxhdC1zdHJpZGVkIHNhbXBsZSBvZiB2b3hlbHMgKGNhcHBlZCBhdCB7QGxpbmsgU0NBVFRFUjNEX01BWF9QT0lOVFN9KSB2aWEgYGFkZFBvaW50czNEYC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgbW91bnRTY2F0dGVyM2Qodmlld2VyOiBWaWV3ZXIsIGluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmltYWdlTW9kZSA9ICdncmF5c2NhbGUnO1xuICAgIHRoaXMudm9sdW1lTXVsdGljaGFubmVsID0gZmFsc2U7XG4gICAgY29uc3QgcmVzID0gdm9sdW1lUmVzb2x1dGlvbkZvcih0aGlzLnJlc29sdXRpb25TY2FsZSk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQodHJ1ZSk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgICBsZXQgdm9sOiB7IGRhdGE6IFVpbnQ4QXJyYXk7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyOyBkZXB0aDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcbiAgICB0cnkge1xuICAgICAgdm9sID0gYXdhaXQgdGhpcy5hc3NlbWJsZVZvbHVtZShpbmZvLCB7XG4gICAgICAgIG1heFNsaWNlOiBNYXRoLm1pbihyZXMubWF4U2xpY2UsIFNDQVRURVIzRF9NQVhfWFkpLFxuICAgICAgICBzbGljZVN0ZXA6IHJlcy5zbGljZVN0ZXAsXG4gICAgICB9KTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQoZmFsc2UpO1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgICB9XG4gICAgaWYgKCF2b2wgfHwgdGhpcy52aWV3ZXIgIT09IHZpZXdlcikgcmV0dXJuO1xuXG4gICAgY29uc3QgeyBkYXRhLCB3aWR0aCwgaGVpZ2h0LCBkZXB0aCB9ID0gdm9sO1xuICAgIGNvbnN0IHpTY2FsZSA9IE1hdGgubWF4KHdpZHRoLCBoZWlnaHQpIC8gTWF0aC5tYXgoMSwgZGVwdGgpOyAvLyDiiYggY3ViaWMgYXNwZWN0XG4gICAgY29uc3QgdG90YWwgPSB3aWR0aCAqIGhlaWdodCAqIGRlcHRoO1xuICAgIGNvbnN0IHN0cmlkZSA9IE1hdGgubWF4KDEsIE1hdGguY2VpbCh0b3RhbCAvIFNDQVRURVIzRF9NQVhfUE9JTlRTKSk7XG4gICAgY29uc3QgcG9zOiBudW1iZXJbXSA9IFtdO1xuICAgIGNvbnN0IHZhbDogbnVtYmVyW10gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRvdGFsOyBpICs9IHN0cmlkZSkge1xuICAgICAgY29uc3QgeCA9IGkgJSB3aWR0aDtcbiAgICAgIGNvbnN0IHkgPSBNYXRoLmZsb29yKGkgLyB3aWR0aCkgJSBoZWlnaHQ7XG4gICAgICBjb25zdCB6aSA9IE1hdGguZmxvb3IoaSAvICh3aWR0aCAqIGhlaWdodCkpO1xuICAgICAgcG9zLnB1c2goeCwgeSwgemkgKiB6U2NhbGUpO1xuICAgICAgdmFsLnB1c2goZGF0YVtpXSk7XG4gICAgfVxuXG4gICAgY29uc3Qgc3QgPSB0aGlzLnN0b3JlLmN1cnJlbnRDaGFubmVsU3RhdGVzKClbMF07XG4gICAgdGhpcy5zY2F0dGVyM2RMYXllciA9IHZpZXdlci5hZGRQb2ludHMzRChuZXcgRmxvYXQzMkFycmF5KHBvcyksIG5ldyBGbG9hdDMyQXJyYXkodmFsKSwge1xuICAgICAgY29sb3JtYXA6IHRoaXMudm9sdW1lQ29sb3JtYXAoc3QpLFxuICAgICAgY29udHJhc3RMaW1pdHM6IFtzdD8ubWluID8/IDAsIHN0Py5tYXggPz8gMjU1XSxcbiAgICAgIHNpemU6IDMsXG4gICAgfSk7XG4gICAgdGhpcy5pbWFnZVcgPSB3aWR0aDtcbiAgICB0aGlzLmltYWdlSCA9IGhlaWdodDtcbiAgICB0aGlzLnZvbHVtZURpbXMgPSB7IHdpZHRoLCBoZWlnaHQsIGRlcHRoIH07XG4gICAgLy8gRmVlZCB0aGUgaW50ZW5zaXR5IGhpc3RvZ3JhbSBmcm9tIHRoZSBhc3NlbWJsZWQgdm9sdW1lIChrZXkgMCkuXG4gICAgdGhpcy52b2x1bWVDaGFubmVsRGF0YS5jbGVhcigpO1xuICAgIHRoaXMudm9sdW1lQ2hhbm5lbERhdGEuc2V0KDAsIGRhdGEpO1xuICAgIHRoaXMuc3Vic2NyaWJlU2NhdHRlcjNkRGlzcGxheVN0YXRlKCk7XG4gICAgdGhpcy5zY2hlZHVsZVJlYWRiYWNrKCk7XG4gIH1cblxuICAvKiogU3RvcmUgY29sb3JtYXAgLyByZXZlcnNlIC8gaW52ZXJ0IC8gY2hhbm5lbCB3aW5kb3cg4oaSIHRoZSAzRCBzY2F0dGVyJ3MgY29sb3JtYXAgKyBjb250cmFzdC4gKi9cbiAgcHJpdmF0ZSBzdWJzY3JpYmVTY2F0dGVyM2REaXNwbGF5U3RhdGUoKTogdm9pZCB7XG4gICAgdGhpcy5kaXNwbGF5U3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuZGlzcGxheVN1YiA9IGNvbWJpbmVMYXRlc3QoW1xuICAgICAgdGhpcy5zdG9yZS5nZXRDb2xvcm1hcCgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRSZXZlcnNlU2NhbGUoKSxcbiAgICAgIHRoaXMuc3RvcmUuZ2V0SW52ZXJ0KCksXG4gICAgICB0aGlzLnN0b3JlLmdldENoYW5uZWxTdGF0ZXMoKSxcbiAgICBdKS5zdWJzY3JpYmUoKFtjb2xvcm1hcCwgcmV2ZXJzZSwgaW52ZXJ0LCBjaGFubmVsc10pID0+IHtcbiAgICAgIHRoaXMuY3VycmVudENvbG9ybWFwID0gKGNvbG9ybWFwIGFzIENvbG9ybWFwTm9kZSkgPz8gbnVsbDtcbiAgICAgIHRoaXMuY3VycmVudFJldmVyc2UgPSByZXZlcnNlO1xuICAgICAgdGhpcy5pbnZlcnRFbmFibGVkID0gaW52ZXJ0O1xuICAgICAgY29uc3QgbGF5ZXIgPSB0aGlzLnNjYXR0ZXIzZExheWVyO1xuICAgICAgaWYgKCFsYXllcikgcmV0dXJuO1xuICAgICAgY29uc3Qgc3QgPSBjaGFubmVsc1swXTtcbiAgICAgIGxheWVyLmNvbG9ybWFwID0gdGhpcy52b2x1bWVDb2xvcm1hcChzdCk7XG4gICAgICBpZiAoc3QpIGxheWVyLmNvbnRyYXN0TGltaXRzID0gW3N0Lm1pbiwgc3QubWF4XTtcbiAgICAgIHRoaXMudmlld2VyPy5yZXF1ZXN0UmVuZGVyKCk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogTW91bnQgdGhlIE5BUEFSSV9TVVJGQUNFIGhlaWdodC1maWVsZCBzdXJmYWNlLiBBIGhlaWdodCBmaWVsZCBpcyBzaW5nbGUtc2NhbGFyLCBzbyBmb3IgYVxuICAgKiBtdWx0aWNoYW5uZWwgaW1hZ2UgdGhlIHN1cmZhY2UgZm9sbG93cyBPTkUgYmFuZCDigJQgdGhlIGZpcnN0IHZpc2libGUgY2hhbm5lbCAoZmFsbGJhY2sgMCkg4oCUXG4gICAqIGNvbG91cmVkIGJ5IHRoYXQgY2hhbm5lbCdzIHdpbmRvdy9jb2xvcm1hcCAobGlrZSB0aGUgUGxvdGx5IFNVUkZBQ0UsIHdoaWNoIGlzIGdyYXlzY2FsZS1vbmx5KS5cbiAgICogUHJlLWxvYWRzIGV2ZXJ5IHNsaWNlJ3MgaGVpZ2h0IGRhdGEgd2l0aCBhIHByb2dyZXNzIGJhciAoYXMgdGhlIHZvbHVtZSBkb2VzKSBzbyB0aGUgc3RhY2tcbiAgICogc2xpZGVyIHJlLXNsaWNlcyBpbnN0YW50bHksIHRoZW4gYnVpbGRzIHRoZSBtZXNoIGZvciB0aGUgY3VycmVudCBzbGljZS4gQWxsIG1lc2ggKyBHUFUgd29ya1xuICAgKiBsaXZlcyBpbiBuYXBhcmktanMgKGBoZWlnaHRGaWVsZGAgKyBgVmlld2VyLmFkZFN1cmZhY2VgKTsgdGhpcyBiYWNrZW5kIHN1cHBsaWVzIHNjYWxhciBzbGljZXMuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIG1vdW50U3VyZmFjZSh2aWV3ZXI6IFZpZXdlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGRlc2MgPSBhd2FpdCB0aGlzLmVuc3VyZURlc2NyaXB0b3IoKTtcbiAgICBjb25zdCBtdWx0aWNoYW5uZWwgPSAhIWRlc2M/Lm11bHRpY2hhbm5lbCAmJiAoZGVzYz8uY2hhbm5lbHMgPz8gMSkgPiAxO1xuICAgIGNvbnN0IHN0YXRlcyA9IHRoaXMuc3RvcmUuY3VycmVudENoYW5uZWxTdGF0ZXMoKTtcbiAgICB0aGlzLnN1cmZhY2VDaGFubmVsID0gbXVsdGljaGFubmVsID8gc3RhdGVzLmZpbmQoKHMpID0+IHMudmlzaWJsZSk/LmluZGV4ID8/IDAgOiB1bmRlZmluZWQ7XG4gICAgdGhpcy5zdXJmYWNlTWF4R3JpZCA9IHN1cmZhY2VSZXNvbHV0aW9uRm9yKHRoaXMucmVzb2x1dGlvblNjYWxlKS5tYXhHcmlkO1xuICAgIHRoaXMuaW1hZ2VNb2RlID0gJ2dyYXlzY2FsZSc7XG4gICAgdGhpcy52b2x1bWVNdWx0aWNoYW5uZWwgPSBmYWxzZTtcbiAgICBhd2FpdCB0aGlzLnByZWxvYWRTdXJmYWNlUGxhbmVzKHZpZXdlcik7XG4gICAgaWYgKHRoaXMudmlld2VyICE9PSB2aWV3ZXIpIHJldHVybjtcbiAgICBhd2FpdCB0aGlzLmJ1aWxkU3VyZmFjZSh2aWV3ZXIsIHRoaXMubG9hZGVkPy56ID8/IDApO1xuICAgIHRoaXMuaW5zdGFsbFN1cmZhY2VBeGVzKHZpZXdlcik7XG4gICAgLy8gU3Vic2NyaWJlIGFmdGVyIHRoZSBmaXJzdCBidWlsZCBzbyBkaXNwbGF5LXN0YXRlIGVkaXRzIHRhcmdldCBhIGxpdmUgbGF5ZXIuXG4gICAgdGhpcy5zdWJzY3JpYmVTdXJmYWNlRGlzcGxheVN0YXRlKCk7XG4gIH1cblxuICAvKiogQWRkIHRoZSAzRCBheGVzIGdpem1vICsgRE9NIGxhYmVscyBhcm91bmQgdGhlIChvcmlnaW4tY2VudGVyZWQpIHN1cmZhY2UgbWVzaCwgbWF0Y2hpbmcgdGhlXG4gICAqICB2b2x1bWUvaXNvc3VyZmFjZS4gSW5zdGFsbGVkIG9uY2UgcGVyIG1vdW50OyB0aGUgYm94IHRyYWNrcyB0aGUgbWVzaCBib3VuZHMsIFgvWSBzaG93IHRoZVxuICAgKiAgcGh5c2ljYWwgKG9yIHBpeGVsKSBleHRlbnQsIGFuZCBaIGlzIHRoZSBpbnRlbnNpdHkvaGVpZ2h0IGF4aXMuICovXG4gIHByaXZhdGUgaW5zdGFsbFN1cmZhY2VBeGVzKHZpZXdlcjogVmlld2VyKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnN1cmZhY2VMYXllcikgcmV0dXJuO1xuICAgIGNvbnN0IGIgPSB0aGlzLnN1cmZhY2VMYXllci5ib3VuZHMoKTtcbiAgICBjb25zdCBib3hXID0gTWF0aC5tYXgoMSwgYi5tYXhbMF0gLSBiLm1pblswXSk7XG4gICAgY29uc3QgYm94SCA9IE1hdGgubWF4KDEsIGIubWF4WzFdIC0gYi5taW5bMV0pO1xuICAgIGNvbnN0IGJveEQgPSBNYXRoLm1heCgxLCBiLm1heFsyXSAtIGIubWluWzJdKTtcbiAgICBjb25zdCBtcHBYID0gdGhpcy5kZXNjcmlwdG9yPy5tcHBYIHx8IHRoaXMubG9hZGVkPy5pbWFnZUluZm8uaW1hZ2VNZXRhPy5bMF0/Lm1wcFggfHwgMDtcbiAgICBjb25zdCB2b3hlbCA9IG1wcFggPiAwID8gKG1wcFggKiAodGhpcy5kZXNjcmlwdG9yPy53aWR0aCA/PyB0aGlzLmltYWdlVykpIC8gTWF0aC5tYXgoMSwgdGhpcy5pbWFnZVcpIDogMTtcbiAgICB0aGlzLmF4ZXNMYXllciA9IHZpZXdlci5hZGRBeGVzKGJveFcsIGJveEgsIGJveEQsIHtcbiAgICAgIHZveGVsU2l6ZTogW3ZveGVsLCB2b3hlbCwgMV0sXG4gICAgICB2aXNpYmxlOiB0aGlzLmF4ZXNWaXNpYmxlLFxuICAgIH0pO1xuICAgIGlmICh0aGlzLmhvc3QpIHtcbiAgICAgIHRoaXMuYXhlc0xhYmVscyA9IG5ldyBOYXBhcmlBeGVzTGFiZWxzKFxuICAgICAgICB0aGlzLmhvc3QsXG4gICAgICAgIHZpZXdlci5jYW1lcmEzZCxcbiAgICAgICAgdGhpcy5idWlsZFN1cmZhY2VBeGVzTGFiZWxzKGJveFcsIGJveEgsIGJveEQsIG1wcFgpLFxuICAgICAgKTtcbiAgICAgIHRoaXMuYXhlc0xhYmVscy5zZXRWaXNpYmxlKHRoaXMuYXhlc1Zpc2libGUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBYL1kvWiBlbmQtbGFiZWxzIGZvciB0aGUgc3VyZmFjZSBnaXptbzogWC9ZIGFyZSB0aGUgcGh5c2ljYWwgKMK1bSkgb3IgcGl4ZWwgZXh0ZW50IG9mIHRoZSBGVUxMXG4gICAqICBpbWFnZTsgWiBpcyB0aGUgaW50ZW5zaXR5L2hlaWdodCBheGlzLiBBbmNob3JzIGFyZSBpbiB0aGUgY2VudGVyZWQgYm94IChtYXRjaGluZyBBeGVzTGF5ZXIpLiAqL1xuICBwcml2YXRlIGJ1aWxkU3VyZmFjZUF4ZXNMYWJlbHMoXG4gICAgYm94VzogbnVtYmVyLFxuICAgIGJveEg6IG51bWJlcixcbiAgICBib3hEOiBudW1iZXIsXG4gICAgbXBwWDogbnVtYmVyLFxuICApOiBBeGlzTGFiZWxTcGVjW10ge1xuICAgIGNvbnN0IGh4ID0gYm94VyAvIDI7XG4gICAgY29uc3QgaHkgPSBib3hIIC8gMjtcbiAgICBjb25zdCBoeiA9IGJveEQgLyAyO1xuICAgIGNvbnN0IGRlc2NXID0gdGhpcy5kZXNjcmlwdG9yPy53aWR0aCA/PyB0aGlzLmltYWdlVztcbiAgICBjb25zdCBkZXNjSCA9IHRoaXMuZGVzY3JpcHRvcj8uaGVpZ2h0ID8/IHRoaXMuaW1hZ2VIO1xuICAgIGNvbnN0IGxlbiA9IChweDogbnVtYmVyKTogc3RyaW5nID0+IChtcHBYID4gMCA/IGZvcm1hdFVtKHB4ICogbXBwWCkgOiBgJHtweH0gcHhgKTtcbiAgICByZXR1cm4gW1xuICAgICAgeyBhbmNob3I6IFtoeCwgLWh5LCAtaHpdLCB0ZXh0OiBgWCDCtyAke2xlbihkZXNjVyl9YCwgY29sb3I6ICcjZWQ0NTQ1JyB9LFxuICAgICAgeyBhbmNob3I6IFstaHgsIGh5LCAtaHpdLCB0ZXh0OiBgWSDCtyAke2xlbihkZXNjSCl9YCwgY29sb3I6ICcjNGRkOTU5JyB9LFxuICAgICAgeyBhbmNob3I6IFstaHgsIC1oeSwgaHpdLCB0ZXh0OiAnWiDCtyBpbnRlbnNpdHknLCBjb2xvcjogJyM2NjhjZmYnIH0sXG4gICAgXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcmUtZmV0Y2ggZXZlcnkgc3RhY2sgc2xpY2UncyBsdW1pbmFuY2UgcGxhbmUgKGRlY2ltYXRlZCB0byB0aGUgc3VyZmFjZSBncmlkKSBpbnRvXG4gICAqIHtAbGluayBzdXJmYWNlUGxhbmVzfSwgZHJpdmluZyB7QGxpbmsgc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkfSDigJQgdGhlIHNhbWUgbG9hZC13aXRoLXByb2dyZXNzIFVYXG4gICAqIGFzIHRoZSB2b2x1bWUsIGJ1dCBrZWVwaW5nIG9uZSAyRCBwbGFuZSBwZXIgc2xpY2UgcmF0aGVyIHRoYW4gcGFja2luZyBhIDNEIHZvbHVtZS4gQm91bmRlZFxuICAgKiBjb25jdXJyZW5jeSBrZWVwcyB0aGUgY29ubmVjdGlvbiBwb29sIGJ1c3kgd2l0aG91dCBmbG9vZGluZyBpdCBvbiBhIGRlZXAgc3RhY2suXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHByZWxvYWRTdXJmYWNlUGxhbmVzKHZpZXdlcjogVmlld2VyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgdG9rZW4gPSB0aGlzLmxvYWRUb2tlbjsgLy8gYmFpbCBpZiBhIENhbmNlbCAvIG5ldyBwbG90IGJ1bXBzIHRoaXMgd2hpbGUgd2UgZmV0Y2hcbiAgICBjb25zdCBpbmZvID0gdGhpcy5sb2FkZWQ/LmltYWdlSW5mbztcbiAgICBjb25zdCBkZXB0aCA9IGluZm8/LmltYWdlTWV0YT8uWzBdPy56IHx8IGluZm8/LnVybHM/Lmxlbmd0aCB8fCAxO1xuICAgIGNvbnN0IHsgbWF4R3JpZCB9ID0gc3VyZmFjZVJlc29sdXRpb25Gb3IodGhpcy5yZXNvbHV0aW9uU2NhbGUpO1xuICAgIHRoaXMuc3VyZmFjZVBsYW5lcy5jbGVhcigpO1xuICAgIHRoaXMuc3RhY2tMb2FkaW5nJC5uZXh0KHRydWUpO1xuICAgIHRoaXMuc3RhY2tMb2FkaW5nUHJvZ3Jlc3MkLm5leHQoMCk7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHBlbmRpbmc6IG51bWJlcltdID0gW107XG4gICAgICBmb3IgKGxldCB6ID0gMDsgeiA8IGRlcHRoOyB6KyspIHBlbmRpbmcucHVzaCh6KTtcbiAgICAgIGxldCBkb25lID0gMDtcbiAgICAgIGNvbnN0IHdvcmtlciA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgICAgZm9yICg7Oykge1xuICAgICAgICAgIGNvbnN0IHogPSBwZW5kaW5nLnNoaWZ0KCk7XG4gICAgICAgICAgaWYgKHogPT09IHVuZGVmaW5lZCB8fCB0aGlzLnZpZXdlciAhPT0gdmlld2VyIHx8IHRoaXMubG9hZFRva2VuICE9PSB0b2tlbikgcmV0dXJuO1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICB0aGlzLnN1cmZhY2VQbGFuZXMuc2V0KHosIGF3YWl0IHRoaXMuZmV0Y2hTdXJmYWNlUGxhbmUoeiwgbWF4R3JpZCkpO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGBbbmFwYXJpLWpzXSBzdXJmYWNlIHNsaWNlICR7en0gcHJlbG9hZCBmYWlsZWRgLCBlcnIpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBkb25lKys7XG4gICAgICAgICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dChNYXRoLnJvdW5kKChkb25lIC8gZGVwdGgpICogMTAwKSk7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjb25zdCBwb29sID0gTWF0aC5taW4oVk9MVU1FX0ZFVENIX0NPTkNVUlJFTkNZLCBNYXRoLm1heCgxLCBkZXB0aCkpO1xuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoQXJyYXkuZnJvbSh7IGxlbmd0aDogcG9vbCB9LCAoKSA9PiB3b3JrZXIoKSkpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLnN0YWNrTG9hZGluZyQubmV4dChmYWxzZSk7XG4gICAgICB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5uZXh0KDApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGZXRjaCBzbGljZSBgemAgYXMgYSBzaW5nbGUgV0hPTEUtaW1hZ2UgbHVtaW5hbmNlIHBsYW5lIChkZWNpbWF0ZWQgdG8gYG1heEdyaWRgKS4gUHJlZmVycyB0aGVcbiAgICogYXBwJ3MgY29tcGxldGUgcGVyLXNsaWNlIGltYWdlIChgc21hbGxVcmxzYC9gdXJsc2AsIGV4YWN0bHkgbGlrZSB0aGUgUGxvdGx5IHN1cmZhY2UpIHNvIHRoZVxuICAgKiBzdXJmYWNlIGFsd2F5cyBjb3ZlcnMgdGhlIEZVTEwgc2xpY2Ug4oCUIHRoZSB0aWxlLXB5cmFtaWQgYGZldGNoU2xpY2VgIHBhdGggd291bGQgZmFsbCBiYWNrIHRvIGFcbiAgICogc2luZ2xlIHRvcC1sZWZ0IHRpbGUgKGEgY29ybmVyKSBmb3Igc2VsZi1jb250YWluZWQgLyBub24tYC90aWxlcy9pbmZvYCBpbWFnZXMuIEZhbGxzIGJhY2sgdG9cbiAgICogc3RpdGNoaW5nIHRoZSB0aWxlIGdyaWQgb25seSB3aGVuIG5vIGNvbXBsZXRlLWltYWdlIFVSTCBpcyBhdmFpbGFibGUuXG4gICAqL1xuICAvKiogVGlsZSBidWRnZXQgdG8gc3RpdGNoIGEgd2hvbGUgc2xpY2UgYXQgfmB0YXJnZXRQeGAgcmVzb2x1dGlvbiBmcm9tIHRoZSBweXJhbWlkOiBhIGhpZ2hlciB0YXJnZXRcbiAgICogIHB1bGxzIGEgRklORVIgcHlyYW1pZCBsZXZlbCAobW9yZSByZWFsIGRldGFpbCkuIFNoYXJlZCBieSB0aGUgc3VyZmFjZSBwbGFuZSBmZXRjaCBhbmQgdGhlIHZvbHVtZVxuICAgKiAgYXNzZW1ibHkgc28gYm90aCBzY2FsZSB0aGVpciBpbi1wbGFuZSByZXNvbHV0aW9uIHdpdGggdGhlIGRlY2ltYXRlIGZhY3Rvci4gKi9cbiAgcHJpdmF0ZSB0aWxlQnVkZ2V0Rm9yKHRhcmdldFB4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IHRpbGVTaXplID0gdGhpcy5kZXNjcmlwdG9yPy50aWxlU2l6ZSB8fCBUSUxFX1NJWkU7XG4gICAgcmV0dXJuIE1hdGgubWluKFxuICAgICAgTUFYX1NUSVRDSF9USUxFUyxcbiAgICAgIE1hdGgubWF4KDEsIE1hdGgucm91bmQoKHRhcmdldFB4IC8gdGlsZVNpemUpICoqIDIgKiBTVElUQ0hfQlVER0VUX0NPRUZGKSksXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmV0Y2hTdXJmYWNlUGxhbmUoXG4gICAgejogbnVtYmVyLFxuICAgIG1heEdyaWQ6IG51bWJlcixcbiAgKTogUHJvbWlzZTx7IGRhdGE6IFVpbnQ4QXJyYXk7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0+IHtcbiAgICBjb25zdCBpbmZvID0gdGhpcy5sb2FkZWQ/LmltYWdlSW5mbztcbiAgICBjb25zdCBkZXNjID0gYXdhaXQgdGhpcy5lbnN1cmVEZXNjcmlwdG9yKCk7XG4gICAgbGV0IHBsYW5lOiB7IGRhdGE6IFVpbnQ4QXJyYXk7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0gfCBudWxsID0gbnVsbDtcblxuICAgIC8vIFByZWZlcnJlZDogc3RpdGNoIHRoZSBXSE9MRSBzbGljZSBmcm9tIHRoZSBzZXJ2ZXIgcHlyYW1pZCBhdCBhIHJlc29sdXRpb24gZHJpdmVuIGJ5IHRoZVxuICAgIC8vIGRlY2ltYXRlIGZhY3RvciDigJQgYSBoaWdoZXIgdGFyZ2V0IGdyaWQgcHVsbHMgYSBGSU5FUiBweXJhbWlkIGxldmVsIChtb3JlIHJlYWwgZGV0YWlsKS4gV2l0aCBhXG4gICAgLy8gZGVzY3JpcHRvciwgZmV0Y2hTbGljZSBzdGl0Y2hlcyB0aGUgd2hvbGUgY2hvc2VuIGxldmVsIChuZXZlciBhIGNvcm5lciksIHRoZW4gd2UgZG93bnNjYWxlIHRvXG4gICAgLy8gdGhlIGdyaWQuIFRoaXMgaXMgd2hhdCBtYWtlcyBcIkZ1bGxcIiBhY3R1YWxseSBoaWdoZXItcmVzIHRoYW4gXCLCvVwiLCBub3QganVzdCBhIGZpeGVkIHByZXZpZXcuXG4gICAgaWYgKGRlc2M/LmxldmVscz8ubGVuZ3RoKSB7XG4gICAgICBjb25zdCBidWRnZXQgPSB0aGlzLnRpbGVCdWRnZXRGb3IobWF4R3JpZCk7XG4gICAgICAvLyBUaGUgc3VyZmFjZSBpcyBhIHNpbmdsZSBkZWNpbWF0ZWQgcGxhbmUsIHNvIHRoZSBjb21wb3NpdGUgZmFsbGJhY2sgaXMgYWNjZXB0YWJsZSB3aGVuIHRoZVxuICAgICAgLy8gY2hhbm5lbCBoYXMgbm8gc21hbGwgcHlyYW1pZCBsZXZlbCAoa2VlcHMgaXQgZmFzdCk7IGEgbXVsdGljaGFubmVsIFZPTFVNRSBtdXN0IG5vdCBkbyB0aGlzLlxuICAgICAgcGxhbmUgPSB0aGlzLmJpdG1hcFRvTHVtaW5hbmNlKFxuICAgICAgICBhd2FpdCB0aGlzLmZldGNoU2xpY2UoeiwgdGhpcy5zdXJmYWNlQ2hhbm5lbCwgYnVkZ2V0LCB0cnVlKSxcbiAgICAgICAgbWF4R3JpZCxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gRmFsbGJhY2sgKG5vIHB5cmFtaWQpOiB0aGUgYXBwJ3MgQ09NUExFVEUgcGVyLXNsaWNlIGltYWdlICh1cmxzW3pdLCBub3QgdGhlIHNtYWxsIGJsdXJyeVxuICAgIC8vIHRodW1ibmFpbCkg4oCUIHdob2xlIHNsaWNlLCBhdm9pZHMgYSBjb3JuZXIgdGlsZS4gQ2FwcGVkIGF0IHRoZSBpbWFnZSdzIG93biByZXNvbHV0aW9uLlxuICAgIGlmICghcGxhbmUpIHtcbiAgICAgIGNvbnN0IHVybCA9IGluZm8/LnVybHM/Llt6XSA/PyBpbmZvPy5zbWFsbFVybHM/Llt6XTtcbiAgICAgIGlmICh1cmwpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy50aWxlc1xuICAgICAgICAgICAgLmdldEF1dGhIZWFkZXJzKClcbiAgICAgICAgICAgIC5jYXRjaCgoKSA9PiAoe30pIGFzIFJlY29yZDxzdHJpbmcsIHN0cmluZz4pO1xuICAgICAgICAgIGNvbnN0IHJlc3AgPSBhd2FpdCBmZXRjaCh1cmwsIHsgaGVhZGVycyB9KTtcbiAgICAgICAgICBpZiAocmVzcC5vaykge1xuICAgICAgICAgICAgcGxhbmUgPSB0aGlzLmJpdG1hcFRvTHVtaW5hbmNlKGF3YWl0IGNyZWF0ZUltYWdlQml0bWFwKGF3YWl0IHJlc3AuYmxvYigpKSwgbWF4R3JpZCk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBjb25zb2xlLndhcm4oYFtuYXBhcmktanNdIHN1cmZhY2UgdXJsIGZldGNoIGZhaWxlZCBmb3Igej0ke3p9YCwgZXJyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIExhc3QgcmVzb3J0IChubyBweXJhbWlkIGFuZCBubyBjb21wbGV0ZSBpbWFnZSk6IGEgc2luZ2xlIHRpbGUuXG4gICAgaWYgKCFwbGFuZSkge1xuICAgICAgcGxhbmUgPSB0aGlzLmJpdG1hcFRvTHVtaW5hbmNlKFxuICAgICAgICBhd2FpdCB0aGlzLmZldGNoU2xpY2UoeiwgdGhpcy5zdXJmYWNlQ2hhbm5lbCwgdGhpcy50aWxlQnVkZ2V0Rm9yKG1heEdyaWQpLCB0cnVlKSxcbiAgICAgICAgbWF4R3JpZCxcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiBwbGFuZTtcbiAgfVxuXG4gIC8qKiBUaGUgY2hhbm5lbCBzdGF0ZSBkcml2aW5nIHRoZSBzdXJmYWNlOiB0aGUgY2hvc2VuIGJhbmQgZm9yIG11bHRpY2hhbm5lbCAobWF0Y2hlZCBieSBpbmRleCksXG4gICAqICBlbHNlIHRoZSBzaW5nbGUgZ3JheXNjYWxlIGNoYW5uZWwuICovXG4gIHByaXZhdGUgc3VyZmFjZVN0YXRlKGNoYW5uZWxzOiBJQ2hhbm5lbFN0YXRlW10pOiBJQ2hhbm5lbFN0YXRlIHwgdW5kZWZpbmVkIHtcbiAgICBpZiAodGhpcy5zdXJmYWNlQ2hhbm5lbCA9PSBudWxsKSByZXR1cm4gY2hhbm5lbHNbMF07XG4gICAgcmV0dXJuIGNoYW5uZWxzLmZpbmQoKHMpID0+IHMuaW5kZXggPT09IHRoaXMuc3VyZmFjZUNoYW5uZWwpID8/IGNoYW5uZWxzWzBdO1xuICB9XG5cbiAgLyoqXG4gICAqIChSZSlidWlsZCB0aGUgc3VyZmFjZSBtZXNoIGZvciBzbGljZSBgemAgZnJvbSB0aGUgcHJlLWxvYWRlZCBwbGFuZSBjYWNoZSAoaW5zdGFudCDigJQgdGhpcyBpc1xuICAgKiB3aGF0IHRoZSBzdGFjayBzbGlkZXIgY2FsbHMpOyBhIHNsaWNlIG1pc3NpbmcgZnJvbSB0aGUgY2FjaGUgaXMgZmV0Y2hlZCBvbiBkZW1hbmQuIG5hcGFyaS1qcydzXG4gICAqIHB1cmUgYGhlaWdodEZpZWxkYCBidWlsZHMgdGhlIHRyaWFuZ2xlIGdyaWQgKHogPSBub3JtYWxpemVkIGludGVuc2l0eSksIHRoZW4gYGFkZFN1cmZhY2VgXG4gICAqIHJlbmRlcnMgaXQuIFRoZSBzbGljZSBwbGFuZSBhbHNvIGZlZWRzIHRoZSBpbnRlbnNpdHkgaGlzdG9ncmFtIChrZXkgMCkuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGJ1aWxkU3VyZmFjZSh2aWV3ZXI6IFZpZXdlciwgejogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbGV0IHBsYW5lID0gdGhpcy5zdXJmYWNlUGxhbmVzLmdldCh6KTtcbiAgICBpZiAoIXBsYW5lKSB7XG4gICAgICB0aGlzLnN0YWNrTG9hZGluZyQubmV4dCh0cnVlKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIHBsYW5lID0gYXdhaXQgdGhpcy5mZXRjaFN1cmZhY2VQbGFuZSh6LCB0aGlzLnN1cmZhY2VNYXhHcmlkKTtcbiAgICAgICAgdGhpcy5zdXJmYWNlUGxhbmVzLnNldCh6LCBwbGFuZSk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcignW25hcGFyaS1qc10gc3VyZmFjZSBzbGljZSBmZXRjaCBmYWlsZWQ6JywgZXJyKTtcbiAgICAgIH0gZmluYWxseSB7XG4gICAgICAgIHRoaXMuc3RhY2tMb2FkaW5nJC5uZXh0KGZhbHNlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKCFwbGFuZSB8fCBwbGFuZS53aWR0aCA8IDIgfHwgcGxhbmUuaGVpZ2h0IDwgMiB8fCB0aGlzLnZpZXdlciAhPT0gdmlld2VyKSByZXR1cm47XG5cbiAgICBjb25zdCBzdCA9IHRoaXMuc3VyZmFjZVN0YXRlKHRoaXMuc3RvcmUuY3VycmVudENoYW5uZWxTdGF0ZXMoKSk7XG4gICAgY29uc3Qgd2luOiBbbnVtYmVyLCBudW1iZXJdID0gW3N0Py5taW4gPz8gMCwgc3Q/Lm1heCA/PyAyNTVdO1xuICAgIHRoaXMuc3VyZmFjZVdpbmRvdyA9IHdpbjtcbiAgICAvLyBIZWlnaHQgQU5EIGNvbG91ciBhcmUgbm9ybWFsaXplZCBieSB0aGUgc2FtZSBjb250cmFzdCB3aW5kb3csIHNvIGNoYW5naW5nIG1pbi9tYXggcmVzaGFwZXMgdGhlXG4gICAgLy8gc3VyZmFjZSAoYSBwaXhlbCdzIGhlaWdodCA9IGl0cyBpbnRlbnNpdHkgd2l0aGluIFttaW4sbWF4XSkuIENlbnRlciBpdCBmb3IgdGhlIGF4ZXMgZ2l6bW8uIFRoZVxuICAgIC8vIHBsYW5lIGlzIGFscmVhZHkgZGVjaW1hdGVkIHRvIHRoZSBncmlkIGNhcCDihpIgc3RyaWRlIDEuXG4gICAgY29uc3QgelNjYWxlID0gU1VSRkFDRV9aX0FTUEVDVCAqIE1hdGgubWF4KHBsYW5lLndpZHRoLCBwbGFuZS5oZWlnaHQpO1xuICAgIGNvbnN0IHsgdmVydGljZXMsIGZhY2VzLCB2YWx1ZXMgfSA9IGhlaWdodEZpZWxkKHBsYW5lLmRhdGEsIHBsYW5lLndpZHRoLCBwbGFuZS5oZWlnaHQsIHtcbiAgICAgIHpTY2FsZSxcbiAgICAgIHpMaW1pdHM6IHdpbixcbiAgICAgIGNlbnRlcjogdHJ1ZSxcbiAgICB9KTtcblxuICAgIC8vIFByZXNlcnZlIHRoZSBvcmJpdCBjYW1lcmEgYWNyb3NzIGEgcmUtc2xpY2UgLyB3aW5kb3cgcmVidWlsZCDigJQgb25seSB0aGUgZmlyc3QgbW91bnQgZnJhbWVzLCBzb1xuICAgIC8vIHN0ZXBwaW5nIHRoZSBzdGFjayBvciBjaGFuZ2luZyB0aGUgd2luZG93IGtlZXBzIHRoZSBjdXJyZW50IHpvb20vcGFuL29yaWVudGF0aW9uLlxuICAgIGNvbnN0IGNhbSA9IHZpZXdlci5jYW1lcmEzZDtcbiAgICBjb25zdCBwcmVzZXJ2ZUNhbWVyYSA9IHRoaXMuc3VyZmFjZUxheWVyICE9IG51bGw7XG4gICAgY29uc3Qgc2F2ZWRUYXJnZXQgPSBjYW0udGFyZ2V0O1xuICAgIGNvbnN0IHNhdmVkRGlzdGFuY2UgPSBjYW0uZGlzdGFuY2U7XG4gICAgaWYgKHRoaXMuc3VyZmFjZUxheWVyKSB7XG4gICAgICB2aWV3ZXIubGF5ZXJzLnJlbW92ZSh0aGlzLnN1cmZhY2VMYXllcik7XG4gICAgICB0aGlzLnN1cmZhY2VMYXllciA9IG51bGw7XG4gICAgfVxuICAgIHRoaXMuc3VyZmFjZUxheWVyID0gdmlld2VyLmFkZFN1cmZhY2UodmVydGljZXMsIGZhY2VzLCB2YWx1ZXMsIHtcbiAgICAgIGNvbG9ybWFwOiB0aGlzLnZvbHVtZUNvbG9ybWFwKHN0KSxcbiAgICAgIGNvbnRyYXN0TGltaXRzOiB3aW4sXG4gICAgICBnYW1tYTogc3Q/LmdhbW1hID8/IDEsXG4gICAgICB3aXJlZnJhbWU6IHRoaXMuc3VyZmFjZVdpcmVmcmFtZSxcbiAgICB9KTtcbiAgICBpZiAocHJlc2VydmVDYW1lcmEpIHtcbiAgICAgIGNhbS50YXJnZXQgPSBzYXZlZFRhcmdldDtcbiAgICAgIGNhbS5kaXN0YW5jZSA9IHNhdmVkRGlzdGFuY2U7XG4gICAgfVxuXG4gICAgdGhpcy5pbWFnZVcgPSBwbGFuZS53aWR0aDtcbiAgICB0aGlzLmltYWdlSCA9IHBsYW5lLmhlaWdodDtcbiAgICB0aGlzLnZvbHVtZURpbXMgPSB7IHdpZHRoOiBwbGFuZS53aWR0aCwgaGVpZ2h0OiBwbGFuZS5oZWlnaHQsIGRlcHRoOiBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKHpTY2FsZSkpIH07XG4gICAgLy8gUmV1c2UgdGhlIHZvbHVtZSBpbnRlbnNpdHktaGlzdG9ncmFtIHBhdGg6IHRoZSBzbGljZSdzIHNjYWxhciBwbGFuZSBpcyB0aGUgaGlzdG9ncmFtIHNvdXJjZS5cbiAgICB0aGlzLnZvbHVtZUNoYW5uZWxEYXRhLmNsZWFyKCk7XG4gICAgdGhpcy52b2x1bWVDaGFubmVsRGF0YS5zZXQoMCwgcGxhbmUuZGF0YSk7XG4gICAgdGhpcy5zY2hlZHVsZVJlYWRiYWNrKCk7XG4gIH1cblxuICAvKiogU3Vic2NyaWJlIHRoZSBzdG9yZSBjb2xvcm1hcCAvIHJldmVyc2UgLyBpbnZlcnQgLyBjaGFubmVsIHdpbmRvdyDihpIgdGhlIHN1cmZhY2UsIHNvIGhpc3RvZ3JhbVxuICAgKiAgJiBjaGFubmVsLWRpYWxvZyBlZGl0cyB1cGRhdGUgaXQgbGl2ZSB3aXRob3V0IGEgcmUtZmV0Y2guICoqbWluL21heCByZXNoYXBlcyB0aGUgc3VyZmFjZSdzXG4gICAqICBoZWlnaHQqKiAoYSBwaXhlbCdzIGhlaWdodCA9IGl0cyBpbnRlbnNpdHkgd2l0aGluIFttaW4sbWF4XSksIHNvIGEgd2luZG93IGNoYW5nZSByZWJ1aWxkcyB0aGVcbiAgICogIG1lc2ggZ2VvbWV0cnkgKGZyb20gdGhlIGNhY2hlZCBzbGljZSk7IGNvbG91ci1vbmx5IGVkaXRzIChjb2xvcm1hcC9MVVQsIGdhbW1hLCByZXZlcnNlLCBpbnZlcnQpXG4gICAqICBqdXN0IHVwZGF0ZSB0aGUgbGF5ZXIncyB1bmlmb3Jtcy4gKi9cbiAgcHJpdmF0ZSBzdWJzY3JpYmVTdXJmYWNlRGlzcGxheVN0YXRlKCk6IHZvaWQge1xuICAgIHRoaXMuZGlzcGxheVN1Yj8udW5zdWJzY3JpYmUoKTtcbiAgICB0aGlzLmRpc3BsYXlTdWIgPSBjb21iaW5lTGF0ZXN0KFtcbiAgICAgIHRoaXMuc3RvcmUuZ2V0Q29sb3JtYXAoKSxcbiAgICAgIHRoaXMuc3RvcmUuZ2V0UmV2ZXJzZVNjYWxlKCksXG4gICAgICB0aGlzLnN0b3JlLmdldEludmVydCgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRDaGFubmVsU3RhdGVzKCksXG4gICAgXSkuc3Vic2NyaWJlKChbY29sb3JtYXAsIHJldmVyc2UsIGludmVydCwgY2hhbm5lbHNdKSA9PiB7XG4gICAgICB0aGlzLmN1cnJlbnRDb2xvcm1hcCA9IChjb2xvcm1hcCBhcyBDb2xvcm1hcE5vZGUpID8/IG51bGw7XG4gICAgICB0aGlzLmN1cnJlbnRSZXZlcnNlID0gcmV2ZXJzZTtcbiAgICAgIHRoaXMuaW52ZXJ0RW5hYmxlZCA9IGludmVydDtcbiAgICAgIGNvbnN0IGxheWVyID0gdGhpcy5zdXJmYWNlTGF5ZXI7XG4gICAgICBpZiAoIWxheWVyIHx8ICF0aGlzLnZpZXdlcikgcmV0dXJuO1xuICAgICAgY29uc3Qgc3QgPSB0aGlzLnN1cmZhY2VTdGF0ZShjaGFubmVscyk7XG4gICAgICBjb25zdCB3aW46IFtudW1iZXIsIG51bWJlcl0gPSBbc3Q/Lm1pbiA/PyAwLCBzdD8ubWF4ID8/IDI1NV07XG4gICAgICBjb25zdCB3aW5kb3dDaGFuZ2VkID1cbiAgICAgICAgIXRoaXMuc3VyZmFjZVdpbmRvdyB8fCB3aW5bMF0gIT09IHRoaXMuc3VyZmFjZVdpbmRvd1swXSB8fCB3aW5bMV0gIT09IHRoaXMuc3VyZmFjZVdpbmRvd1sxXTtcbiAgICAgIGlmICh3aW5kb3dDaGFuZ2VkKSB7XG4gICAgICAgIC8vIEhlaWdodCBmb2xsb3dzIHRoZSBjb250cmFzdCB3aW5kb3cg4oaSIHJlYnVpbGQgdGhlIG1lc2ggZm9yIHRoZSBuZXcgW21pbixtYXhdIChjYW1lcmEga2VwdCkuXG4gICAgICAgIHZvaWQgdGhpcy5idWlsZFN1cmZhY2UodGhpcy52aWV3ZXIsIHRoaXMubG9hZGVkPy56ID8/IDApLmNhdGNoKChlcnIpID0+XG4gICAgICAgICAgY29uc29sZS5lcnJvcignW25hcGFyaS1qc10gc3VyZmFjZSB3aW5kb3cgcmVidWlsZCBmYWlsZWQ6JywgZXJyKSxcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgLy8gQ29sb3VyLW9ubHkgY2hhbmdlOiB1cGRhdGUgdW5pZm9ybXMgaW4gcGxhY2UsIG5vIGdlb21ldHJ5IHJlYnVpbGQuXG4gICAgICBsYXllci5jb2xvcm1hcCA9IHRoaXMudm9sdW1lQ29sb3JtYXAoc3QpO1xuICAgICAgaWYgKHN0KSB7XG4gICAgICAgIGxheWVyLmNvbnRyYXN0TGltaXRzID0gd2luO1xuICAgICAgICBsYXllci5nYW1tYSA9IHN0LmdhbW1hO1xuICAgICAgfVxuICAgICAgdGhpcy52aWV3ZXIucmVxdWVzdFJlbmRlcigpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBmaXRDYW1lcmFTb29uKCk6IHZvaWQge1xuICAgIGNvbnN0IHJ1biA9ICgpOiB2b2lkID0+IHtcbiAgICAgIGlmICh0aGlzLnZpZXdlciAmJiB0aGlzLmNhbnZhcyAmJiB0aGlzLmltYWdlVyA+IDAgJiYgdGhpcy5pbWFnZUggPiAwKSB7XG4gICAgICAgIHRoaXMudmlld2VyLmNhbWVyYS5maXQoXG4gICAgICAgICAgdGhpcy5pbWFnZVcsXG4gICAgICAgICAgdGhpcy5pbWFnZUgsXG4gICAgICAgICAgdGhpcy5jYW52YXMuY2xpZW50V2lkdGggfHwgdGhpcy5pbWFnZVcsXG4gICAgICAgICAgdGhpcy5jYW52YXMuY2xpZW50SGVpZ2h0IHx8IHRoaXMuaW1hZ2VILFxuICAgICAgICApO1xuICAgICAgfVxuICAgIH07XG4gICAgaWYgKHR5cGVvZiByZXF1ZXN0QW5pbWF0aW9uRnJhbWUgPT09ICdmdW5jdGlvbicpIHJlcXVlc3RBbmltYXRpb25GcmFtZShydW4pO1xuICAgIGVsc2UgcnVuKCk7XG4gIH1cblxuICAvKiogQGRlcHJlY2F0ZWQgUGxvdGx5LXNwZWNpZmljOyBob3N0IHJlLWRyaXZlcyBwbG90KCkgZnJvbSBpdHMgaW1hZ2Ugc3RyZWFtLiAqL1xuICByZWxvYWRBbmRQbG90KCk6IHZvaWQge1xuICAgIC8qIG5vLW9wICovXG4gIH1cblxuICByZXNldCgpOiB2b2lkIHtcbiAgICB0aGlzLmxvYWRUb2tlbisrOyAvLyBpbnZhbGlkYXRlIGFueSBpbi1mbGlnaHQgZnJhbWUgbG9hZGluZyBmcm9tIHRoZSBwcmV2aW91cyBwbG90XG4gICAgdGhpcy5kaXNwbGF5U3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuZGlzcGxheVN1YiA9IG51bGw7XG4gICAgdGhpcy5zY2FsZUJhcj8uZGVzdHJveSgpO1xuICAgIHRoaXMuc2NhbGVCYXIgPSBudWxsO1xuICAgIHRoaXMucmVnaW9uT3ZlcmxheT8uZGVzdHJveSgpO1xuICAgIHRoaXMucmVnaW9uT3ZlcmxheSA9IG51bGw7XG4gICAgdGhpcy5heGVzTGFiZWxzPy5kZXN0cm95KCk7XG4gICAgdGhpcy5heGVzTGFiZWxzID0gbnVsbDtcbiAgICB0aGlzLnpIYW5kbGU/LmRlc3Ryb3koKTtcbiAgICB0aGlzLnpIYW5kbGUgPSBudWxsO1xuICAgIHRoaXMudm9sdW1lV29ybGRCYXNlID0gbnVsbDtcbiAgICB0aGlzLmNhY2hlZEltYWdlID0gbnVsbDtcbiAgICB0aGlzLmNhY2hlZEltYWdlU291cmNlID0gbnVsbDtcbiAgICB0aGlzLmxhc3RQaXhlbHNSZWN0ID0gbnVsbDtcbiAgICB0aGlzLm5hdGl2ZUhpc3RvZ3JhbXMuY2xlYXIoKTtcbiAgICB0aGlzLnRpbGVkID0gZmFsc2U7XG4gICAgdGhpcy5oaXN0U2FtcGxlcy5jbGVhcigpO1xuICAgIGlmICh0aGlzLnJlYWRiYWNrVGltZXIgIT0gbnVsbCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMucmVhZGJhY2tUaW1lcik7XG4gICAgICB0aGlzLnJlYWRiYWNrVGltZXIgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLmNhbWVyYVJlYWRiYWNrT2ZmPy4oKTtcbiAgICB0aGlzLmNhbWVyYVJlYWRiYWNrT2ZmID0gbnVsbDtcbiAgICB0aGlzLmNoYW5uZWxWaWV3ID0gbnVsbDtcbiAgICB0aGlzLnZpZXdlcj8uZGlzcG9zZSgpO1xuICAgIHRoaXMudmlld2VyID0gbnVsbDtcbiAgICBpZiAodGhpcy5jYW52YXMgJiYgdGhpcy5ob3N0Py5jb250YWlucyh0aGlzLmNhbnZhcykpIHRoaXMuaG9zdC5yZW1vdmVDaGlsZCh0aGlzLmNhbnZhcyk7XG4gICAgdGhpcy5jYW52YXMgPSBudWxsO1xuICAgIHRoaXMubGFzdFBpeGVscyA9IG51bGw7XG4gICAgdGhpcy52b2x1bWVWaWV3ID0gbnVsbDtcbiAgICB0aGlzLnZvbHVtZU11bHRpY2hhbm5lbCA9IGZhbHNlO1xuICAgIHRoaXMuc3VyZmFjZUxheWVyID0gbnVsbDtcbiAgICB0aGlzLnN1cmZhY2VDaGFubmVsID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuc3VyZmFjZVBsYW5lcy5jbGVhcigpO1xuICAgIHRoaXMuc3VyZmFjZVdpbmRvdyA9IG51bGw7XG4gICAgdGhpcy5zY2F0dGVyUmVnaW9uU3ViPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuc2NhdHRlclJlZ2lvblN1YiA9IG51bGw7XG4gICAgdGhpcy5zY2F0dGVyMmRQb2ludHMgPSBudWxsO1xuICAgIHRoaXMuc2NhdHRlcjNkTGF5ZXIgPSBudWxsO1xuICAgIHRoaXMuYXhlc0xheWVyID0gbnVsbDtcbiAgICB0aGlzLnZvbHVtZURpbXMgPSBudWxsO1xuICAgIHRoaXMudm9sdW1lQ2hhbm5lbERhdGEuY2xlYXIoKTtcbiAgfVxuXG4gIHJlbGF5b3V0KF90cnVlSW1hZ2VTaXplPzogbnVtYmVyW10pOiB2b2lkIHtcbiAgICB0aGlzLnZpZXdlcj8ucmVxdWVzdFJlbmRlcigpO1xuICB9XG5cbiAgLyoqIEBkZXByZWNhdGVkIFBsb3RseS1zcGVjaWZpYyBheGlzIHJlc2V0LiAqL1xuICByZXNldEF4ZXMoKTogdm9pZCB7XG4gICAgdGhpcy5maXRDYW1lcmFTb29uKCk7XG4gIH1cblxuICAvKiogQGRlcHJlY2F0ZWQgUGxvdGx5LXNwZWNpZmljIGF1dG9zY2FsZS4gKi9cbiAgYXV0b3NjYWxlKCk6IHZvaWQge1xuICAgIHRoaXMuZml0Q2FtZXJhU29vbigpO1xuICAgIHRoaXMuYXV0b3NjYWxlRXZlbnQkLm5leHQodW5kZWZpbmVkKTtcbiAgfVxuXG4gIHpvb21JbigpOiB2b2lkIHtcbiAgICBpZiAodGhpcy52aWV3ZXIpIHRoaXMudmlld2VyLmNhbWVyYS56b29tID0gdGhpcy52aWV3ZXIuY2FtZXJhLnpvb20gKiAxLjM7XG4gIH1cblxuICB6b29tT3V0KCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnZpZXdlcikgdGhpcy52aWV3ZXIuY2FtZXJhLnpvb20gPSB0aGlzLnZpZXdlci5jYW1lcmEuem9vbSAvIDEuMztcbiAgfVxuXG4gIHNldERyYWdNb2RlKF9tb2RlOiBzdHJpbmcgfCBmYWxzZSk6IHZvaWQge1xuICAgIC8vIE5vLW9wOiBwYW4gYW5kIHpvb20gYXJlIGFsd2F5cyB0aGUgbmFwYXJpIGNhbWVyYSdzIGRlZmF1bHQgZ2VzdHVyZXMsIGFuZCBwYW4vem9vbSBnYXRpbmcgZm9yXG4gICAgLy8gZHJhd2luZyBpcyBvd25lZCBieSB0aGUgcmVnaW9uIG92ZXJsYXkncyBzZXRNb2RlIChzZXRDb250cm9sc0VuYWJsZWQpLiBUb2dnbGluZyBjb250cm9scyBoZXJlXG4gICAgLy8gd291bGQgZmlnaHQgdGhhdCDigJQgdGhlIGhvc3QgY2FsbHMgc2V0RHJhZ01vZGUoZmFsc2UpIGFsb25nc2lkZSBvdmVybGF5LnNldE1vZGUoPHRvb2w+KS5cbiAgfVxuXG4gIHNldE5hdmlnYXRvclZpc2libGUoX3Zpc2libGU6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICAvKiBuYXBhcmktanMgaGFzIG5vIG1pbmltYXA7IG5vLW9wICovXG4gIH1cblxuICBzZXRJbWFnZVNtb290aGluZ0VuYWJsZWQoZW5hYmxlZDogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuaW1hZ2VTbW9vdGhpbmcgPSBlbmFibGVkO1xuICAgIC8vIEFwcGx5IGxpdmUgdG8gdGhlIHJlbmRlcmVkIGltYWdlIGxheWVyczsgYmFrZWQgaW50byB0aGUgbmV4dCByZW5kZXIgdG9vLlxuICAgIHRoaXMuY2hhbm5lbFZpZXc/LnNldEludGVycG9sYXRpb24oZW5hYmxlZCA/ICdsaW5lYXInIDogJ25lYXJlc3QnKTtcbiAgfVxuXG4gIHNldFNob3dTdGFjayhfc2hvd3N0YWNrOiBib29sZWFuKTogdm9pZCB7XG4gICAgLyogc3RhY2sgbmF2aWdhdGVkIHZpYSBzZXRaSW5kZXggKi9cbiAgfVxuXG4gIHNldFpJbmRleCh6SW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIGlmICh0aGlzLmxvYWRlZCkgdGhpcy5sb2FkZWQueiA9IHpJbmRleDtcbiAgICBjb25zdCB2ID0gdGhpcy52aWV3ZXI7XG4gICAgaWYgKCF2KSByZXR1cm47XG4gICAgLy8gU3VyZmFjZTogb25lIHNsaWNlIOKGkiBvbmUgbWVzaCwgc28gcmUtYnVpbGQgdGhlIGhlaWdodCBmaWVsZCBmb3IgdGhlIG5ldyBzbGljZS5cbiAgICBpZiAodGhpcy5zdXJmYWNlTGF5ZXIpIHtcbiAgICAgIHZvaWQgdGhpcy5idWlsZFN1cmZhY2UodiwgekluZGV4KS5jYXRjaCgoZXJyKSA9PlxuICAgICAgICBjb25zb2xlLmVycm9yKCdbbmFwYXJpLWpzXSBzZXRaSW5kZXggc3VyZmFjZSBmYWlsZWQ6JywgZXJyKSxcbiAgICAgICk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIFZvbHVtZSAvIGlzb3N1cmZhY2U6IHN0ZXAgdGhlIHZvbHVtZSdzIHogcGxhbmUgaW4gcGxhY2UuXG4gICAgaWYgKHRoaXMudm9sdW1lVmlldykge1xuICAgICAgdi5kaW1zLnogPSB6SW5kZXg7XG4gICAgICB0aGlzLnNjaGVkdWxlUmVhZGJhY2soKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gVGlsZWQgMkQgaW1hZ2U6IGp1c3QgbW92ZSB0aGUgZGltcyBwbGFuZSDigJQgdGhlIHRpbGVkIHZpc3VhbCBmZXRjaGVzIHRoZSBuZXcgc2xpY2UncyB0aWxlc1xuICAgIC8vIChjYWNoZWQgcGVyIHopLCBubyBsYXllciByZWJ1aWxkLiBSZWZyZXNoIHRoZSBjb2Fyc2UgaGlzdG9ncmFtIHNhbXBsZSBmb3IgdGhlIG5ldyBzbGljZS5cbiAgICBpZiAodGhpcy50aWxlZCkge1xuICAgICAgdi5kaW1zLnogPSB6SW5kZXg7XG4gICAgICBpZiAodGhpcy5kZXNjcmlwdG9yKSB2b2lkIHRoaXMucmVmcmVzaEhpc3RvZ3JhbVNhbXBsZXMoekluZGV4LCB0aGlzLmRlc2NyaXB0b3IpO1xuICAgICAgdGhpcy5zY2hlZHVsZVJlYWRiYWNrKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIDJEIGltYWdlIChzdGl0Y2ggZmFsbGJhY2spOiByZS1yZW5kZXIgdGhlIHNsaWNlIChyZS1mZXRjaGVzIHBlci1jaGFubmVsIC8gY29tcG9zaXRlKS4gQnJhbmNoXG4gICAgLy8gb2YgYSB2b2x1bWUgbGF5ZXIgKG5vdCBjdXJyZW50UGxvdFR5cGUsIHdoaWNoIGNvdWxkIGJlIHN0YWxlIGFuZCBzaWxlbnRseSBuby1vcCB0aGUgc3dhcCkuXG4gICAgLy8gVGhlIHRva2VuIGxldHMgcmVuZGVySW1hZ2UgZHJvcCBhIHN1cGVyc2VkZWQgc2NydWIgc28gYSBzbG93IG9sZGVyIHNsaWNlIGNhbid0IGNsb2JiZXIgYSBuZXdlciBvbmUuXG4gICAgY29uc3QgcmVxID0gKyt0aGlzLnNsaWNlUmVxO1xuICAgIHZvaWQgdGhpcy5yZW5kZXJJbWFnZSh6SW5kZXgsIHJlcSlcbiAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgaWYgKHJlcSA9PT0gdGhpcy5zbGljZVJlcSkgdGhpcy5zY2hlZHVsZVJlYWRiYWNrKCk7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKChlcnIpID0+IGNvbnNvbGUuZXJyb3IoJ1tuYXBhcmktanNdIHNldFpJbmRleCBzbGljZSBmYWlsZWQ6JywgZXJyKSk7XG4gIH1cblxuICBzZXRTdGFja0xvYWRpbmcoc3RhY2tMb2FkaW5nOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5zdGFja0xvYWRpbmckLm5leHQoc3RhY2tMb2FkaW5nKTtcbiAgfVxuXG4gIC8qKiBDYW5jZWwgaW4tZmxpZ2h0IGZyYW1lIGxvYWRpbmc6IGJ1bXAgdGhlIGxvYWQgZ2VuZXJhdGlvbiBzbyB0aGUgdm9sdW1lLWFzc2VtYmx5IC8gc3VyZmFjZVxuICAgKiAgcHJlbG9hZCB3b3JrZXJzIHN0b3AgZmV0Y2hpbmcgbW9yZSBmcmFtZXMsIGFuZCBjbGVhciB0aGUgbG9hZGluZyBmbGFnICsgcHJvZ3Jlc3MuICovXG4gIGNhbmNlbExvYWRpbmcoKTogdm9pZCB7XG4gICAgdGhpcy5sb2FkVG9rZW4rKztcbiAgICB0aGlzLnN0YWNrTG9hZGluZyQubmV4dChmYWxzZSk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQubmV4dCgwKTtcbiAgfVxuXG4gIGlzU3RhY2tMb2FkaW5nKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIHJldHVybiB0aGlzLnN0YWNrTG9hZGluZyQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICBnZXRTdGFja0xvYWRpbmdQcm9ncmVzcygpOiBPYnNlcnZhYmxlPG51bWJlcj4ge1xuICAgIHJldHVybiB0aGlzLnN0YWNrTG9hZGluZ1Byb2dyZXNzJC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIGdldFRydWVJbWFnZVNpemUoKTogeyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuaW1hZ2VXID4gMCAmJiB0aGlzLmltYWdlSCA+IDAgPyB7IHdpZHRoOiB0aGlzLmltYWdlVywgaGVpZ2h0OiB0aGlzLmltYWdlSCB9IDogbnVsbDtcbiAgfVxuXG4gIGdldEN1cnJlbnRJbWFnZSgpOiBQcm9taXNlPEltYWdlIHwgbnVsbD4ge1xuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobnVsbCk7XG4gIH1cblxuICBnZXREaXNwbGF5ZWRQaXhlbERhdGEoKTogUGl4ZWxEYXRhIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMubGFzdFBpeGVscztcbiAgfVxuXG4gIGdldERpc3BsYXllZFNvdXJjZVJlY3QoKTogeyB4OiBudW1iZXI7IHk6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSB8IG51bGwge1xuICAgIGNvbnN0IHYgPSB0aGlzLnZpZXdlcjtcbiAgICBjb25zdCBzaXplID0gdGhpcy5nZXRUcnVlSW1hZ2VTaXplKCk7XG4gICAgaWYgKCF2IHx8ICFzaXplKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCByID0gdi52aXNpYmxlV29ybGRSZWN0KCk7XG4gICAgY29uc3QgeCA9IE1hdGgubWF4KDAsIHIueCk7XG4gICAgY29uc3QgeSA9IE1hdGgubWF4KDAsIHIueSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHgsXG4gICAgICB5LFxuICAgICAgd2lkdGg6IE1hdGgubWluKHNpemUud2lkdGgsIHIueCArIHIud2lkdGgpIC0geCxcbiAgICAgIGhlaWdodDogTWF0aC5taW4oc2l6ZS5oZWlnaHQsIHIueSArIHIuaGVpZ2h0KSAtIHksXG4gICAgfTtcbiAgfVxuXG4gIGRvd25sb2FkSW1hZ2UoKTogdm9pZCB7XG4gICAgdm9pZCB0aGlzLmV4cG9ydENvbXBvc2l0ZSgpO1xuICB9XG5cbiAgc2V0UGxvdFR5cGUocGxvdFR5cGU6IFBsb3RUeXBlKTogdm9pZCB7XG4gICAgdGhpcy5jdXJyZW50UGxvdFR5cGUgPSBwbG90VHlwZTtcbiAgfVxuXG4gIC8qKiBNYXAgYSBQbG90bHktc3R5bGUgM0QgZHJhZyBtb2RlIG9udG8gbmFwYXJpLWpzJ3MgY2FtZXJhIGRyYWcgbW9kZS4gKi9cbiAgc2V0U3VyZmFjZURyYWdNb2RlKG1vZGU6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBjb25zdCBtID0gbW9kZSA9PT0gJ3BhbicgPyAncGFuJyA6IG1vZGUgPT09ICd6b29tJyA/ICd6b29tJyA6ICdyb3RhdGUnOyAvLyBvcmJpdC90dXJudGFibGUg4oaSIHJvdGF0ZVxuICAgIHRoaXMudmlld2VyLnNldENhbWVyYURyYWdNb2RlKG0pO1xuICB9XG5cbiAgLyoqIFJlLWZyYW1lIHRoZSAzRCBjYW1lcmEgb24gdGhlIGN1cnJlbnQgM0QgbGF5ZXIuIEEgc3VyZmFjZSBtZXNoIHNpdHMgaW4gdGhlIHBvc2l0aXZlIG9jdGFudFxuICAgKiAgKG5vdCBjZW50cmVkKSwgc28gZnJhbWUgaXQgYnkgaXRzIG93biBib3VuZHM7IGEgdm9sdW1lL2lzb3N1cmZhY2UgdXNlcyBpdHMgY2VudHJlZCBib3guICovXG4gIHJlc2V0U3VyZmFjZUNhbWVyYSgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMudmlld2VyKSByZXR1cm47XG4gICAgY29uc3QgbWVzaExheWVyID0gdGhpcy5zdXJmYWNlTGF5ZXIgPz8gdGhpcy5zY2F0dGVyM2RMYXllcjtcbiAgICBpZiAobWVzaExheWVyKSB7XG4gICAgICBjb25zdCBiID0gbWVzaExheWVyLmJvdW5kcygpO1xuICAgICAgdGhpcy52aWV3ZXIuY2FtZXJhM2QudGFyZ2V0ID0gYi5jZW50ZXI7XG4gICAgICB0aGlzLnZpZXdlci5jYW1lcmEzZC5kaXN0YW5jZSA9IE1hdGgubWF4KGIucmFkaXVzICogMi41LCAxZS0zKTtcbiAgICAgIHRoaXMudmlld2VyLnJlcXVlc3RSZW5kZXIoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgZCA9IHRoaXMudm9sdW1lRGltcztcbiAgICBpZiAoZCkgdGhpcy52aWV3ZXIuY2FtZXJhM2QuZnJhbWUoZC53aWR0aCwgZC5oZWlnaHQsIGQuZGVwdGgpO1xuICB9XG5cbiAgZ2V0QXV0b3NjYWxlRXZlbnQoKTogT2JzZXJ2YWJsZTx1bmtub3duPiB7XG4gICAgcmV0dXJuIHRoaXMuYXV0b3NjYWxlRXZlbnQkLmFzT2JzZXJ2YWJsZSgpO1xuICB9XG5cbiAgZ2V0UGxvdFR5cGVEZXNjcmlwdG9ycygpOiBQbG90VHlwZURlc2NyaXB0b3JbXSB7XG4gICAgLy8gVGhlIFdlYkdQVSBuYXBhcmktanMgb3B0aW9ucywgb2ZmZXJlZCBhbG9uZ3NpZGUgKG5vdCByZXBsYWNpbmcpIHRoZSBPU0QvUGxvdGx5IHR5cGVzLlxuICAgIHJldHVybiBbXG4gICAgICBQTE9UX1RZUEVfREVTQ1JJUFRPUlNbUGxvdFR5cGUuTkFQQVJJX0lNQUdFXSEsXG4gICAgICBQTE9UX1RZUEVfREVTQ1JJUFRPUlNbUGxvdFR5cGUuTkFQQVJJX1NDQVRURVJdISxcbiAgICAgIFBMT1RfVFlQRV9ERVNDUklQVE9SU1tQbG90VHlwZS5OQVBBUklfU1VSRkFDRV0hLFxuICAgICAgUExPVF9UWVBFX0RFU0NSSVBUT1JTW1Bsb3RUeXBlLk5BUEFSSV9TQ0FUVEVSM0RdISxcbiAgICAgIFBMT1RfVFlQRV9ERVNDUklQVE9SU1tQbG90VHlwZS5OQVBBUklfVk9MVU1FXSEsXG4gICAgICBQTE9UX1RZUEVfREVTQ1JJUFRPUlNbUGxvdFR5cGUuTkFQQVJJX0lTT1NVUkZBQ0VdISxcbiAgICBdO1xuICB9XG5cbiAgLyoqIFRoZSBuYXBhcmkgM0QgZGVjaW1hdGUgZmFjdG9yICgxID0gZnVsbCDigKYgOCA9IOKFmykuIFJlYWQgYnkgdGhlIHRvb2xiYXIgdG8gaW5pdCB0aGUgY29udHJvbC4gKi9cbiAgZ2V0UmVzb2x1dGlvblNjYWxlKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMucmVzb2x1dGlvblNjYWxlO1xuICB9XG5cbiAgLyoqIFNldCB0aGUgZGVjaW1hdGUgZmFjdG9yIGZvciB0aGUgbmFwYXJpIDNEIHR5cGVzLiBUYWtlcyBlZmZlY3Qgb24gdGhlIG5leHQgKHJlKWxvYWQg4oCUIHRoZSBob3N0XG4gICAqICByZS1wbG90cyBhZnRlciBjYWxsaW5nIHRoaXMsIHNpbmNlIGRlY2ltYXRpb24gY2hhbmdlcyB0aGUgZmV0Y2hlZC9hc3NlbWJsZWQgZGF0YS4gKi9cbiAgc2V0UmVzb2x1dGlvblNjYWxlKHNjYWxlOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnJlc29sdXRpb25TY2FsZSA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoc2NhbGUpKTtcbiAgfVxuXG4gIGdldEludGVuc2l0eVByb2ZpbGUkKCk6IE9ic2VydmFibGU8SW50ZW5zaXR5UHJvZmlsZVtdPiB7XG4gICAgcmV0dXJuIHRoaXMuaW50ZW5zaXR5UHJvZmlsZSQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICByZW5kZXJJbnRlbnNpdHlJbnNldChfZGl2SWQ6IHN0cmluZywgX3Byb2ZpbGVzOiBJbnRlbnNpdHlQcm9maWxlW10pOiB2b2lkIHtcbiAgICAvKiBQbG90bHkgb3ducyB0aGUgaW50ZW5zaXR5IGluc2V0ICovXG4gIH1cblxuICAvKiogUmVhZCB0aGUgZGlzcGxheWVkIGNvbXBvc2l0ZSBpbnRvIGBsYXN0UGl4ZWxzYCAodGhlIHBpeGVsLXRvb2xzJyBzb3VyY2UpICsgZW1pdCB0aGUgY2xhbXBlZFxuICAgKiAgdmlzaWJsZSByZWdpb24uIEEgZnJlc2ggUGl4ZWxEYXRhIG9iamVjdCBtZWFucyBjYWNoZWRJbWFnZURhdGEoKSByZWJ1aWxkcyBhdXRvbWF0aWNhbGx5LiAqL1xuICBwcml2YXRlIGFzeW5jIHJ1blJlYWRiYWNrKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHYgPSB0aGlzLnZpZXdlcjtcbiAgICBpZiAoIXYpIHJldHVybjtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcHggPSBhd2FpdCB2LnJlYWREaXNwbGF5ZWRQaXhlbHMoKTtcbiAgICAgIGlmICh0aGlzLnZpZXdlciAhPT0gdikgcmV0dXJuO1xuICAgICAgY29uc3QgcmVjdCA9IHYudmlzaWJsZVdvcmxkUmVjdCgpOyAvLyBjYXB0dXJlIFdJVEggdGhlIHBpeGVscyAoc2FtZSBjYW1lcmEpXG4gICAgICB0aGlzLmxhc3RQaXhlbHMgPSBweDtcbiAgICAgIHRoaXMubGFzdFBpeGVsc1JlY3QgPSByZWN0O1xuICAgICAgY29uc3Qgc2l6ZSA9IHRoaXMuZ2V0VHJ1ZUltYWdlU2l6ZSgpO1xuICAgICAgaWYgKHNpemUgJiYgcmVjdCkge1xuICAgICAgICBjb25zdCB4ID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oc2l6ZS53aWR0aCwgcmVjdC54KSk7XG4gICAgICAgIGNvbnN0IHkgPSBNYXRoLm1heCgwLCBNYXRoLm1pbihzaXplLmhlaWdodCwgcmVjdC55KSk7XG4gICAgICAgIGNvbnN0IHdpZHRoID0gTWF0aC5tYXgoMSwgTWF0aC5taW4oc2l6ZS53aWR0aCAtIHgsIHJlY3Qud2lkdGgpKTtcbiAgICAgICAgY29uc3QgaGVpZ2h0ID0gTWF0aC5tYXgoMSwgTWF0aC5taW4oc2l6ZS5oZWlnaHQgLSB5LCByZWN0LmhlaWdodCkpO1xuICAgICAgICB0aGlzLnZpZXdwb3J0Q2hhbmdlJC5uZXh0KHsgeCwgeSwgd2lkdGgsIGhlaWdodCB9KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIHtcbiAgICAgIC8qIHJlYWRiYWNrIHVuYXZhaWxhYmxlICovXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzY2hlZHVsZVJlYWRiYWNrKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBpZiAodHlwZW9mIHJlcXVlc3RBbmltYXRpb25GcmFtZSA9PT0gJ2Z1bmN0aW9uJykgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKCgpID0+IHZvaWQgdGhpcy5ydW5SZWFkYmFjaygpKTtcbiAgICBlbHNlIHNldFRpbWVvdXQoKCkgPT4gdm9pZCB0aGlzLnJ1blJlYWRiYWNrKCksIDApO1xuICB9XG5cbiAgLyoqIERlYm91bmNlZCByZWFkYmFjayDigJQgYXJtZWQgb24gY2FtZXJhIGNoYW5nZXMgc28gYGxhc3RQaXhlbHNgIHRyYWNrcyB0aGUgY3VycmVudCB2aWV3IGFmdGVyIGFcbiAgICogIHBhbi96b29tIHNldHRsZXMgKGFuZCBhZnRlciB0aWxlZCBsZXZlbHMgZmluaXNoIGxvYWRpbmcpLCB3aGljaCB0aGUgb24tY2FudmFzIHBpeGVsIHRvb2xzXG4gICAqICAod2FuZC9icnVzaC9TQU0pIHJlYWQgc3luY2hyb25vdXNseS4gQ29hbGVzY2VzIHJhcGlkIGNoYW5nZXMuICovXG4gIHByaXZhdGUgYXJtUmVhZGJhY2soZGVsYXlNcyA9IDI1MCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnJlYWRiYWNrVGltZXIgIT0gbnVsbCkgY2xlYXJUaW1lb3V0KHRoaXMucmVhZGJhY2tUaW1lcik7XG4gICAgdGhpcy5yZWFkYmFja1RpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLnJlYWRiYWNrVGltZXIgPSBudWxsO1xuICAgICAgdm9pZCB0aGlzLnJ1blJlYWRiYWNrKCk7XG4gICAgfSwgZGVsYXlNcyk7XG4gIH1cblxuICAvLyDilIDilIAgSVJlZ2lvblN0b3JlICsgY2xhc3NpZmljYXRpb24gY29sb3VycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgLy8gSW5oZXJpdGVkIGZyb20gQmFzZVN0b3JlVmlzdWFsaXplciDigJQgcHVyZSBkZWxlZ2F0aW9ucyB0byB0aGUgc2hhcmVkXG4gIC8vIFJlZ2lvblN0b3JlIC8gVmlzdWFsaXplclN0b3JlIChpZGVudGljYWwgdG8gdGhlIE9TRCBiYWNrZW5kKS5cblxuICAvLyDilIDilIAgUGl4ZWwgdG9vbHM6IHJldXNlIHRoZSBzaGFyZWQsIGJhY2tlbmQtYWdub3N0aWMgdG9vbCBzZXJ2aWNlcyB3aXRoIGEgbmFwYXJpIGhvc3Qg4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIEJ1aWxkIHRoZSBjb29yZGluYXRlIHRyYW5zZm9ybSArIHRvb2wgaG9zdHMgZm9yIHRoZSBjdXJyZW50IHZpZXdlciAoY2FsbGVkIGZyb20gcGxvdCgpKS4gVGhlXG4gICAqICBwaXhlbCB0b29scyByZWFkIGRpc3BsYXllZCBwaXhlbHMgc3luY2hyb25vdXNseSBmcm9tIHRoZSBsYXN0IHJlYWRiYWNrIGFuZCBjb252ZXJ0IHBvaW50ZXJcbiAgICogIGNvb3JkcyB2aWEgdGhlIG5hcGFyaSBjYW1lcmEsIGV4YWN0bHkgbWlycm9yaW5nIHRoZSBPU0QgaG9zdC4gKi9cbiAgcHJpdmF0ZSBidWlsZFRvb2xIb3N0cygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMudmlld2VyKSByZXR1cm47XG4gICAgdGhpcy5jb29yZFRyYW5zZm9ybSA9IG5ldyBOYXBhcmlDb29yZGluYXRlVHJhbnNmb3JtKFxuICAgICAgdGhpcy52aWV3ZXIsXG4gICAgICAoKSA9PiAhIXRoaXMudmlld2VyICYmIHRoaXMuaW1hZ2VXID4gMCxcbiAgICApO1xuICAgIGNvbnN0IHJlZ2lvbnMgPSB0aGlzLnJlZ2lvblN0b3JlO1xuICAgIHRoaXMud2FuZEhvc3QgPSB7XG4gICAgICBnZXRSZWdpb25zOiAoKSA9PiByZWdpb25zLmdldFJlZ2lvbnMoKSxcbiAgICAgIHNldFJlZ2lvbnM6IChyKSA9PiByZWdpb25zLnNldFJlZ2lvbnMociksXG4gICAgICBnZXRDYWNoZWRJbWFnZURhdGE6ICgpID0+IHRoaXMuY2FjaGVkSW1hZ2VEYXRhKCksXG4gICAgICBnZXRBY3RpdmVGcmFtZUluZGV4OiAoKSA9PiB0aGlzLmxvYWRlZD8ueiA/PyAwLFxuICAgICAgZ2V0T3ZlcmxheUNvbnRhaW5lcjogKCkgPT4gdGhpcy5ob3N0LFxuICAgICAgZ2V0Q29vcmRpbmF0ZVRyYW5zZm9ybTogKCkgPT4gdGhpcy5jb29yZFRyYW5zZm9ybSBhcyBJQ29vcmRpbmF0ZVRyYW5zZm9ybSxcbiAgICAgIGdldEZpbGVOYW1lOiAoKSA9PiB0aGlzLmxvYWRlZD8uZmlsZW5hbWUsXG4gICAgICBnZXRTaGFwZUNvbG9yOiAoKSA9PiByZWdpb25zLmdldFNoYXBlQ29sb3IoKSxcbiAgICB9O1xuICAgIHRoaXMuZXJhc2VySG9zdCA9IHtcbiAgICAgIGdldFJlZ2lvbnM6ICgpID0+IHJlZ2lvbnMuZ2V0UmVnaW9ucygpLFxuICAgICAgc2V0UmVnaW9uczogKHIpID0+IHJlZ2lvbnMuc2V0UmVnaW9ucyhyKSxcbiAgICAgIGludmFsaWRhdGVXYW5kUmVnaW9uOiAoKSA9PiB0aGlzLndhbmRUb29sLmNsZWFyQWN0aXZlUmVnaW9uKCksXG4gICAgICBnZXRPdmVybGF5Q29udGFpbmVyOiAoKSA9PiB0aGlzLmhvc3QsXG4gICAgICBnZXRDb29yZGluYXRlVHJhbnNmb3JtOiAoKSA9PiB0aGlzLmNvb3JkVHJhbnNmb3JtIGFzIElDb29yZGluYXRlVHJhbnNmb3JtLFxuICAgICAgZ2V0Q2FjaGVkSW1hZ2VSYXRpbzogKCkgPT4gdGhpcy5jYWNoZWRJbWFnZURhdGEoKT8ucmF0aW9zWzBdID8/IDEsXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgem9vbUJveEhvc3QoKTogWm9vbVRvQm94VG9vbEhvc3Qge1xuICAgIHJldHVybiB7XG4gICAgICBnZXRQbG90RGl2OiAoKSA9PiB0aGlzLnBsb3REaXZJZCxcbiAgICAgIHBpeGVsVG9EYXRhOiAocHgsIHB5KSA9PiB7XG4gICAgICAgIGNvbnN0IHJlY3QgPSB0aGlzLmhvc3Q/LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBpZiAoIXRoaXMudmlld2VyIHx8ICFyZWN0KSByZXR1cm4geyB4OiAwLCB5OiAwIH07XG4gICAgICAgIGNvbnN0IFt4LCB5XSA9IHRoaXMudmlld2VyLmNhbnZhc1RvV29ybGQocmVjdC5sZWZ0ICsgcHgsIHJlY3QudG9wICsgcHkpO1xuICAgICAgICByZXR1cm4geyB4LCB5IH07XG4gICAgICB9LFxuICAgICAgYXBwbHlab29tVG9Cb3g6IChjb29yZHMpID0+IHRoaXMuYXBwbHlab29tVG9Cb3goY29vcmRzKSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFpvb20vcGFuIHRoZSBjYW1lcmEgdG8gZml0IGEgZGF0YS1zcGFjZSByZWN0YW5nbGUgYFt4TWluLCB4TWF4LCB5TWF4LCB5TWluXWAuICovXG4gIHByaXZhdGUgYXBwbHlab29tVG9Cb3goY29vcmRpbmF0ZXM6IG51bWJlcltdKTogdm9pZCB7XG4gICAgY29uc3QgdiA9IHRoaXMudmlld2VyO1xuICAgIGlmICghdiB8fCAhdGhpcy5jYW52YXMgfHwgY29vcmRpbmF0ZXMubGVuZ3RoIDwgNCkgcmV0dXJuO1xuICAgIGNvbnN0IFt4TWluLCB4TWF4LCB5QSwgeUJdID0gY29vcmRpbmF0ZXM7XG4gICAgY29uc3QgeDAgPSBNYXRoLm1pbih4TWluLCB4TWF4KTtcbiAgICBjb25zdCB4MSA9IE1hdGgubWF4KHhNaW4sIHhNYXgpO1xuICAgIGNvbnN0IHkwID0gTWF0aC5taW4oeUEsIHlCKTtcbiAgICBjb25zdCB5MSA9IE1hdGgubWF4KHlBLCB5Qik7XG4gICAgY29uc3QgdyA9IE1hdGgubWF4KDEsIHgxIC0geDApO1xuICAgIGNvbnN0IGggPSBNYXRoLm1heCgxLCB5MSAtIHkwKTtcbiAgICBjb25zdCB2dyA9IHRoaXMuY2FudmFzLmNsaWVudFdpZHRoIHx8IHc7XG4gICAgY29uc3QgdmggPSB0aGlzLmNhbnZhcy5jbGllbnRIZWlnaHQgfHwgaDtcbiAgICB2LmNhbWVyYS5jZW50ZXIgPSBbKHgwICsgeDEpIC8gMiwgKHkwICsgeTEpIC8gMl07XG4gICAgdi5jYW1lcmEuem9vbSA9IE1hdGgubWluKHZ3IC8gdywgdmggLyBoKTtcbiAgICB2LnJlcXVlc3RSZW5kZXIoKTtcbiAgfVxuXG4gIC8qKiBCdWlsZCBDYWNoZWRJbWFnZURhdGEgZnJvbSB0aGUgbW9zdCByZWNlbnQgcmVhZGJhY2sgKFJHQkEgZGV2aWNlIHBpeGVscyDihpIgW3ldW3hdPVtyLGcsYl0pLFxuICAgKiAgd2l0aCByYXRpb3Mvb3JpZ2luIG1hcHBpbmcgaW1hZ2UgY29vcmRzIOKGlCByZWFkYmFjayBwaXhlbHMuIENhY2hlZCB1bnRpbCB0aGUgcmVhZGJhY2sgY2hhbmdlcy4gKi9cbiAgcHJpdmF0ZSBjYWNoZWRJbWFnZURhdGEoKTogQ2FjaGVkSW1hZ2VEYXRhIHwgbnVsbCB7XG4gICAgY29uc3QgcHggPSB0aGlzLmxhc3RQaXhlbHM7XG4gICAgaWYgKCFweCB8fCAhdGhpcy52aWV3ZXIpIHJldHVybiBudWxsO1xuICAgIGlmICh0aGlzLmNhY2hlZEltYWdlICYmIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPT09IHB4KSByZXR1cm4gdGhpcy5jYWNoZWRJbWFnZTtcbiAgICBjb25zdCB3ID0gcHgud2lkdGg7XG4gICAgY29uc3QgaCA9IHB4LmhlaWdodDtcbiAgICBjb25zdCBkYXRhID0gcHguZGF0YTtcbiAgICBjb25zdCBtYXRyaXg6IG51bWJlcltdW11bXSA9IG5ldyBBcnJheShoKTtcbiAgICBmb3IgKGxldCB5ID0gMDsgeSA8IGg7IHkrKykge1xuICAgICAgY29uc3Qgcm93OiBudW1iZXJbXVtdID0gbmV3IEFycmF5KHcpO1xuICAgICAgY29uc3QgYmFzZSA9IHkgKiB3ICogNDtcbiAgICAgIGZvciAobGV0IHggPSAwOyB4IDwgdzsgeCsrKSB7XG4gICAgICAgIGNvbnN0IG8gPSBiYXNlICsgeCAqIDQ7XG4gICAgICAgIHJvd1t4XSA9IFtkYXRhW29dLCBkYXRhW28gKyAxXSwgZGF0YVtvICsgMl1dO1xuICAgICAgfVxuICAgICAgbWF0cml4W3ldID0gcm93O1xuICAgIH1cbiAgICAvLyBVc2UgdGhlIHJlY3QgY2FwdHVyZWQgV0lUSCB0aGlzIHJlYWRiYWNrIChub3QgdGhlIGxpdmUgb25lKSBzbyByYXRpb3Mvb3JpZ2luIG1hdGNoIHRoZVxuICAgIC8vIG1hdHJpeCdzIGNhbWVyYSDigJQgb3RoZXJ3aXNlIGEgcGFuL3pvb20gc2luY2UgdGhlIHJlYWRiYWNrIG1pcy1zY2FsZXMgdGhlIHRyYWNlZCByZWdpb24uXG4gICAgY29uc3QgcmVjdCA9IHRoaXMubGFzdFBpeGVsc1JlY3QgPz8gdGhpcy52aWV3ZXIudmlzaWJsZVdvcmxkUmVjdCgpO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2UgPSB7XG4gICAgICBmcmFtZXM6IFttYXRyaXhdLFxuICAgICAgd2lkdGg6IHcsXG4gICAgICBoZWlnaHQ6IGgsXG4gICAgICByYXRpb3M6IFtyZWN0LndpZHRoIC8gdywgcmVjdC5oZWlnaHQgLyBoXSxcbiAgICAgIGlzR3JheXNjYWxlOiBmYWxzZSxcbiAgICAgIG9yaWdpblg6IHJlY3QueCxcbiAgICAgIG9yaWdpblk6IHJlY3QueSxcbiAgICB9O1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBweDtcbiAgICByZXR1cm4gdGhpcy5jYWNoZWRJbWFnZTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBJVG9vbENvbnRyb2xsZXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vIENvbnRyb2wgZ2F0aW5nOiBhIHRvb2wgdGhhdCBBQ1RJVkFURVMgZGlzYWJsZXMgdGhlIGNhbWVyYSBjb250cm9sczsgZGVhY3RpdmF0aW9uIGlzIGEgbm8tb3BcbiAgLy8gYmVjYXVzZSB0aGUgaG9zdCBhbHdheXMgY2FsbHMgdGhlIHJlZ2lvbiBvdmVybGF5J3Mgc2V0TW9kZSBGSVJTVCBpbiBlYWNoIHRvZ2dsZSAod2hpY2ggc2V0c1xuICAvLyB0aGUgYmFzZWxpbmUgZW5hYmxlZC9kaXNhYmxlZCksIHNvIHJlLWVuYWJsaW5nIGhlcmUgd291bGQgZmlnaHQgYSBmcmVzaGx5LWFjdGl2YXRlZCBkcmF3IG1vZGUuXG4gIHNldFdhbmRNb2RlKGFjdGl2ZTogYm9vbGVhbiwgb3B0aW9ucz86IElXYW5kT3B0aW9ucyk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjsgLy8gbm8gcGxvdCB5ZXQg4oaSIG5vdGhpbmcgdG8gZHJpdmVcbiAgICBpZiAoIWFjdGl2ZSkge1xuICAgICAgdGhpcy53YW5kVG9vbC5zZXRNb2RlKGZhbHNlKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy52aWV3ZXI/LnNldENvbnRyb2xzRW5hYmxlZChmYWxzZSk7XG4gICAgdGhpcy5jYWNoZWRJbWFnZVNvdXJjZSA9IG51bGw7XG4gICAgdGhpcy5hcm1SZWFkYmFjaygwKTtcbiAgICBpZiAodGhpcy53YW5kSG9zdCkgdGhpcy53YW5kVG9vbC5iaW5kSG9zdCh0aGlzLndhbmRIb3N0KTtcbiAgICB0aGlzLndhbmRUb29sLnNldE1vZGUodHJ1ZSwgKG9wdGlvbnMgPz8ge30pIGFzIHVua25vd24gYXMgV2FuZE9wdGlvbnMpO1xuICB9XG4gIHNldFdhbmRPcHRpb25zKG9wdGlvbnM6IElXYW5kT3B0aW9ucyk6IHZvaWQge1xuICAgIHRoaXMud2FuZFRvb2wuc2V0T3B0aW9ucygob3B0aW9ucyA/PyB7fSkgYXMgdW5rbm93biBhcyBXYW5kT3B0aW9ucyk7XG4gIH1cbiAgY2xlYXJBY3RpdmVXYW5kUmVnaW9uKCk6IHZvaWQge1xuICAgIHRoaXMud2FuZFRvb2wuY2xlYXJBY3RpdmVSZWdpb24oKTtcbiAgfVxuICBzZXRCcnVzaE1vZGUoYWN0aXZlOiBib29sZWFuLCBvcHRpb25zPzogSUJydXNoT3B0aW9ucyk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBpZiAoIWFjdGl2ZSkge1xuICAgICAgdGhpcy5icnVzaFRvb2wuc2V0TW9kZShmYWxzZSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMudmlld2VyPy5zZXRDb250cm9sc0VuYWJsZWQoZmFsc2UpO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBudWxsO1xuICAgIHRoaXMuYXJtUmVhZGJhY2soMCk7XG4gICAgaWYgKHRoaXMud2FuZEhvc3QpIHRoaXMuYnJ1c2hUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHRoaXMuYnJ1c2hUb29sLnNldE1vZGUodHJ1ZSwgKG9wdGlvbnMgPz8ge30pIGFzIHVua25vd24gYXMgQnJ1c2hPcHRpb25zKTtcbiAgfVxuICBzZXRCcnVzaE9wdGlvbnMob3B0aW9uczogSUJydXNoT3B0aW9ucyk6IHZvaWQge1xuICAgIGlmIChvcHRpb25zPy5zaXplICE9IG51bGwpIHRoaXMuYnJ1c2hUb29sLnNldFNpemUob3B0aW9ucy5zaXplKTtcbiAgfVxuICBzZXRWZXJ0ZXhFcmFzZXJNb2RlKGFjdGl2ZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBpZiAoYWN0aXZlKSB7XG4gICAgICB0aGlzLnZpZXdlcj8uc2V0Q29udHJvbHNFbmFibGVkKGZhbHNlKTtcbiAgICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBudWxsO1xuICAgICAgaWYgKHRoaXMuZXJhc2VySG9zdCkgdGhpcy5lcmFzZXJUb29sLmJpbmRIb3N0KHRoaXMuZXJhc2VySG9zdCk7XG4gICAgfVxuICAgIHRoaXMuZXJhc2VyVG9vbC5zZXRNb2RlKGFjdGl2ZSk7XG4gIH1cbiAgc2V0VmVydGV4RXJhc2VyUmFkaXVzKHJhZGl1czogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5lcmFzZXJUb29sLnNldFJhZGl1cyhyYWRpdXMpO1xuICB9XG4gIHNldFpvb21Ub0JveE1vZGUoYWN0aXZlOiBib29sZWFuKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnZpZXdlcikgcmV0dXJuO1xuICAgIGlmIChhY3RpdmUpIHtcbiAgICAgIHRoaXMudmlld2VyPy5zZXRDb250cm9sc0VuYWJsZWQoZmFsc2UpO1xuICAgICAgdGhpcy56b29tVG9Cb3hUb29sLmJpbmRIb3N0KHRoaXMuem9vbUJveEhvc3QoKSk7XG4gICAgfVxuICAgIHRoaXMuem9vbVRvQm94VG9vbC5zZXRNb2RlKGFjdGl2ZSk7XG4gIH1cbiAgLy8gU0FNIC8gY2VsbHBvc2Ug4oCUIHNlcnZlciByb3VuZC10cmlwcyB0aGF0IHJlYWQgdGhlIGRyYXduIHJlY3RhbmdsZXMgKyBkaXNwbGF5ZWQgcGl4ZWxzIHRocm91Z2hcbiAgLy8gdGhlIHNhbWUgd2FuZCBob3N0IChyZWN0YW5nbGVzIGZyb20gdGhlIFJlZ2lvblN0b3JlLCBpbWFnZSBmcm9tIHRoZSByZWFkYmFjaykuXG4gIGFzeW5jIHNlZ21lbnRSZWN0YW5nbGVzKCk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgaWYgKCF0aGlzLnZpZXdlciB8fCAhdGhpcy53YW5kSG9zdCkgcmV0dXJuIDA7XG4gICAgYXdhaXQgdGhpcy5ydW5SZWFkYmFjaygpOyAvLyB0aGUgU0FNIGVtYmVkZGluZyBzYW1wbGVzIHRoZSBjdXJyZW50bHktZGlzcGxheWVkIGltYWdlXG4gICAgdGhpcy5jYWNoZWRJbWFnZVNvdXJjZSA9IG51bGw7XG4gICAgdGhpcy5zYW1Ub29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHJldHVybiB0aGlzLnNhbVRvb2wuc2VnbWVudEJveGVzKCk7XG4gIH1cbiAgYXN5bmMgc2VnbWVudFJlY3RhbmdsZXNDZWxscG9zZSgpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIGlmICghdGhpcy52aWV3ZXIgfHwgIXRoaXMud2FuZEhvc3QgfHwgIXRoaXMuY2VsbFNlZ21lbnRlcikgcmV0dXJuIDA7XG4gICAgYXdhaXQgdGhpcy5ydW5SZWFkYmFjaygpO1xuICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBudWxsO1xuICAgIHRoaXMuY2VsbFNlZ21lbnRUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHJldHVybiB0aGlzLmNlbGxTZWdtZW50VG9vbC5zZWdtZW50Qm94ZXModGhpcy5jZWxsU2VnbWVudGVyKTtcbiAgfVxuICBzZXRTYW1Nb2RlbChpZDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Ub29sLnNldE1vZGVsKGlkKTtcbiAgICB0aGlzLnNhbVBvaW50VG9vbC5zZXRNb2RlbChpZCk7XG4gIH1cbiAgc2V0U2FtUG9pbnRNb2RlKGFjdGl2ZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBpZiAoYWN0aXZlKSB7XG4gICAgICB0aGlzLnZpZXdlcj8uc2V0Q29udHJvbHNFbmFibGVkKGZhbHNlKTtcbiAgICAgIHRoaXMuY2FjaGVkSW1hZ2VTb3VyY2UgPSBudWxsO1xuICAgICAgdGhpcy5hcm1SZWFkYmFjaygwKTsgLy8gcmVmcmVzaCB0aGUgcmVhZGJhY2sgdGhlIGNsaWNrJ3MgZW1iZWRkaW5nIHdpbGwgc2FtcGxlXG4gICAgICBpZiAodGhpcy53YW5kSG9zdCkgdGhpcy5zYW1Qb2ludFRvb2wuYmluZEhvc3QodGhpcy53YW5kSG9zdCk7XG4gICAgfVxuICAgIHRoaXMuc2FtUG9pbnRUb29sLnNldE1vZGUoYWN0aXZlKTtcbiAgfVxuICBjb21taXRTYW1Qb2ludHMoKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Qb2ludFRvb2wuY29tbWl0KCk7XG4gIH1cbiAgY2xlYXJTYW1Qb2ludHMoKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Qb2ludFRvb2wuY2xlYXIoKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBJRGlzcGxheU9wdGlvbnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vIEluaGVyaXRlZCBmcm9tIEJhc2VTdG9yZVZpc3VhbGl6ZXIg4oCUIHB1cmUgZGVsZWdhdGlvbnMgdG8gdGhlIHNoYXJlZFxuICAvLyBWaXN1YWxpemVyU3RvcmUgKGlkZW50aWNhbCB0byB0aGUgT1NEIGJhY2tlbmQpLlxuXG4gIC8vIOKUgOKUgCBJSW50ZW5zaXR5U2FtcGxpbmc6IFBsb3RseSBvd25zIHNhbXBsaW5nOyBlbWl0IHZpZXdwb3J0IGNoYW5nZXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIGVuc3VyZUludGVuc2l0eVNhbXBsaW5nKF9pbWFnZUluZm86IElJbWFnZUluZm8sIF96SW5kZXg6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgfVxuICByZWZyZXNoSW50ZW5zaXR5U2FtcGxpbmdGb3JSb2koXG4gICAgX3g6IG51bWJlcixcbiAgICBfeTogbnVtYmVyLFxuICAgIF93aWR0aDogbnVtYmVyLFxuICAgIF9oZWlnaHQ6IG51bWJlcixcbiAgICBfekluZGV4OiBudW1iZXIsXG4gICk6IHZvaWQge31cbiAgZ2V0Vmlld3BvcnRDaGFuZ2UkKCk6IE9ic2VydmFibGU8eyB4OiBudW1iZXI7IHk6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfT4ge1xuICAgIHJldHVybiB0aGlzLnZpZXdwb3J0Q2hhbmdlJC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBJVmlzdWFsaXplciBjb21wb3NpdGUgbWVtYmVycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgZ2V0UmVnaW9uT3ZlcmxheSgpOiBJUmVnaW9uT3ZlcmxheSB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLnJlZ2lvbk92ZXJsYXk7XG4gIH1cbiAgZ2V0SXNvc3VyZmFjZUNvbnRyb2xzKCk6IElJc29zdXJmYWNlQ29udHJvbHMgfCBudWxsIHtcbiAgICBpZiAoIXRoaXMudm9sdW1lVmlldykgcmV0dXJuIG51bGw7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNldElzb1JhbmdlOiAoaXNvTWluOiBudW1iZXIsIGlzb01heDogbnVtYmVyKTogdm9pZCA9PiB7XG4gICAgICAgIC8vIEFwcGx5IHRvIGV2ZXJ5IGNoYW5uZWwncyB2b2x1bWUgbGF5ZXIuXG4gICAgICAgIGZvciAoY29uc3QgbGF5ZXIgb2YgdGhpcy52b2x1bWVWaWV3Py5sYXllcnMgPz8gW10pIHtcbiAgICAgICAgICBsYXllci5jb250cmFzdExpbWl0cyA9IFtpc29NaW4sIGlzb01heF07XG4gICAgICAgICAgbGF5ZXIucmVuZGVyaW5nID0gJ2lzbyc7XG4gICAgICAgICAgbGF5ZXIuaXNvVGhyZXNob2xkID0gMC41O1xuICAgICAgICB9XG4gICAgICAgIHRoaXMudmlld2VyPy5yZXF1ZXN0UmVuZGVyKCk7XG4gICAgICB9LFxuICAgIH07XG4gIH1cbiAgZ2V0SW50ZW5zaXR5Q29udHJvbHMoKTogSUludGVuc2l0eUNvbnRyb2xzIHwgbnVsbCB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgZ2V0U3VyZmFjZTNkQ29udHJvbHMoKTogSVN1cmZhY2UzZENvbnRyb2xzIHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLnZvbHVtZVZpZXcgJiYgIXRoaXMuc3VyZmFjZUxheWVyICYmICF0aGlzLnNjYXR0ZXIzZExheWVyKSByZXR1cm4gbnVsbDtcbiAgICByZXR1cm4ge1xuICAgICAgc2V0U3VyZmFjZURyYWdNb2RlOiAobW9kZTogc3RyaW5nKTogdm9pZCA9PiB0aGlzLnNldFN1cmZhY2VEcmFnTW9kZShtb2RlKSxcbiAgICAgIHJlc2V0U3VyZmFjZUNhbWVyYTogKCk6IHZvaWQgPT4gdGhpcy5yZXNldFN1cmZhY2VDYW1lcmEoKSxcbiAgICAgIHNldEF4ZXNWaXNpYmxlOiAodmlzaWJsZTogYm9vbGVhbik6IHZvaWQgPT4ge1xuICAgICAgICB0aGlzLmF4ZXNWaXNpYmxlID0gdmlzaWJsZTtcbiAgICAgICAgdGhpcy5heGVzTGFiZWxzPy5zZXRWaXNpYmxlKHZpc2libGUpO1xuICAgICAgICBpZiAodGhpcy5heGVzTGF5ZXIpIHtcbiAgICAgICAgICB0aGlzLmF4ZXNMYXllci52aXNpYmxlID0gdmlzaWJsZTtcbiAgICAgICAgICB0aGlzLnZpZXdlcj8ucmVxdWVzdFJlbmRlcigpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgYXhlc1Zpc2libGU6ICgpOiBib29sZWFuID0+IHRoaXMuYXhlc1Zpc2libGUsXG4gICAgICAvLyBTdXJmYWNlIHdpcmVmcmFtZSAobmFwYXJpLWpzIHN1cmZhY2Ugb25seSkg4oCUIGEgbGl2ZSBsYXllciBwcm9wZXJ0eSwgbm8gcmVidWlsZCBuZWVkZWQuXG4gICAgICBzZXRXaXJlZnJhbWU6IChvbjogYm9vbGVhbik6IHZvaWQgPT4ge1xuICAgICAgICB0aGlzLnN1cmZhY2VXaXJlZnJhbWUgPSBvbjtcbiAgICAgICAgaWYgKHRoaXMuc3VyZmFjZUxheWVyKSB7XG4gICAgICAgICAgdGhpcy5zdXJmYWNlTGF5ZXIud2lyZWZyYW1lID0gb247XG4gICAgICAgICAgdGhpcy52aWV3ZXI/LnJlcXVlc3RSZW5kZXIoKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHdpcmVmcmFtZTogKCk6IGJvb2xlYW4gPT4gdGhpcy5zdXJmYWNlV2lyZWZyYW1lLFxuICAgIH07XG4gIH1cbiAgZ2V0SGlzdG9ncmFtKGNoYW5uZWxJbmRleDogbnVtYmVyLCBiaW5zOiBudW1iZXIpOiBJSGlzdG9ncmFtIHwgbnVsbCB7XG4gICAgY29uc3QgdiA9IHRoaXMudmlld2VyO1xuICAgIGlmICghdikgcmV0dXJuIG51bGw7XG4gICAgLy8gVm9sdW1lIC8gaXNvc3VyZmFjZTogaW50ZW5zaXR5IGhpc3RvZ3JhbSBvZiB0aGUgYXNzZW1ibGVkIChkb3duc2FtcGxlZCkgdWludDggdm9sdW1lIGZvciB0aGVcbiAgICAvLyByZXF1ZXN0ZWQgY2hhbm5lbCAobXVsdGljaGFubmVsKSBvciB0aGUgc2luZ2xlIGdyYXlzY2FsZSB2b2x1bWUgKGtleSAwKS5cbiAgICBpZiAodGhpcy52b2x1bWVDaGFubmVsRGF0YS5zaXplKSB7XG4gICAgICBjb25zdCBkYXRhID0gdGhpcy52b2x1bWVDaGFubmVsRGF0YS5nZXQoY2hhbm5lbEluZGV4KSA/PyB0aGlzLnZvbHVtZUNoYW5uZWxEYXRhLmdldCgwKTtcbiAgICAgIHJldHVybiBkYXRhID8gdGhpcy50b0lIaXN0b2dyYW0oaGlzdG9ncmFtU2NhbGFyKGRhdGEsIGJpbnMsIDAsIDI1NSkpIDogbnVsbDtcbiAgICB9XG4gICAgLy8gVGlsZWQgbW9kZSBoYXMgbm8gZnVsbCBpbi1tZW1vcnkgcGl4ZWxzIOKGkiB1c2UgdGhlIGNvYXJzZSBwZXItY2hhbm5lbCBzYW1wbGUgKFJHQjogcmVhZGJhY2spLlxuICAgIGlmICh0aGlzLnRpbGVkKSB7XG4gICAgICBpZiAodGhpcy5pbWFnZU1vZGUgPT09ICdyZ2InKSByZXR1cm4gdGhpcy5yZ2JIaXN0b2dyYW0oY2hhbm5lbEluZGV4LCBiaW5zKTtcbiAgICAgIGNvbnN0IHNhbXBsZSA9IHRoaXMuaGlzdFNhbXBsZXMuZ2V0KHRoaXMuaW1hZ2VNb2RlID09PSAnZ3JheXNjYWxlJyA/IDAgOiBjaGFubmVsSW5kZXgpO1xuICAgICAgcmV0dXJuIHNhbXBsZSA/IHRoaXMudG9JSGlzdG9ncmFtKGhpc3RvZ3JhbVNjYWxhcihzYW1wbGUsIGJpbnMsIDAsIDI1NSkpIDogbnVsbDtcbiAgICB9XG4gICAgLy8gR3JheXNjYWxlL211bHRpY2hhbm5lbCAoc3RpdGNoKTogbmF0aXZlIHBlci1jaGFubmVsIGhpc3RvZ3JhbSBzdHJhaWdodCBmcm9tIHRoZSBpbi1tZW1vcnlcbiAgICAvLyBzY2FsYXIgbGF5ZXIgKG5vIEdQVSByZWFkYmFjaykuIFJHQjogYmluIHRoZSBkaXNwbGF5ZWQgcGl4ZWxzJyBSL0cvQiBieXRlICg4LWJpdCBjbGllbnQgcGF0aCkuXG4gICAgY29uc3QgbGF5ZXIgPSB0aGlzLmNoYW5uZWxWaWV3Py5sYXllcnNbdGhpcy5pbWFnZU1vZGUgPT09ICdncmF5c2NhbGUnID8gMCA6IGNoYW5uZWxJbmRleF07XG4gICAgaWYgKGxheWVyKSB7XG4gICAgICBjb25zdCBoID0gdi5sYXllckhpc3RvZ3JhbShsYXllciwgYmlucyk7XG4gICAgICBpZiAoaCkgcmV0dXJuIHRoaXMudG9JSGlzdG9ncmFtKGgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5pbWFnZU1vZGUgPT09ICdyZ2InKSByZXR1cm4gdGhpcy5yZ2JIaXN0b2dyYW0oY2hhbm5lbEluZGV4LCBiaW5zKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBnZXRIaXN0b2dyYW0kKGNoYW5uZWxJbmRleDogbnVtYmVyLCBiaW5zOiBudW1iZXIpOiBPYnNlcnZhYmxlPElIaXN0b2dyYW0gfCBudWxsPiB7XG4gICAgLy8gPjgtYml0IGNoYW5uZWxzOiBmZXRjaCB0aGUgdHJ1ZSBuYXRpdmUgZGlzdHJpYnV0aW9uIGZyb20gdGhlIHNlcnZlciAodGhlIGRpc3BsYXllZCBwaXhlbHNcbiAgICAvLyBhcmUgOC1iaXQsIHNvIHRoZSBjbGllbnQgaGlzdG9ncmFtIHdvdWxkIGJlIGNsaXBwZWQpLiA4LWJpdCBjaGFubmVscyB1c2UgdGhlIGNsaWVudCBwYXRoLlxuICAgIGNvbnN0IGJpdERlcHRoID0gdGhpcy5kZXNjcmlwdG9yPy5jaGFubmVsSW5mbz8uW2NoYW5uZWxJbmRleF0/LmJpdERlcHRoID8/IDg7XG4gICAgaWYgKGJpdERlcHRoID4gOCkge1xuICAgICAgY29uc3QgeiA9IHRoaXMubG9hZGVkPy56ID8/IDA7XG4gICAgICBjb25zdCBrZXkgPSBgJHt6fXwke2NoYW5uZWxJbmRleH1gO1xuICAgICAgY29uc3QgY2FjaGVkID0gdGhpcy5uYXRpdmVIaXN0b2dyYW1zLmdldChrZXkpO1xuICAgICAgaWYgKGNhY2hlZCkgcmV0dXJuIG9mKGNhY2hlZCk7XG4gICAgICByZXR1cm4gZnJvbSh0aGlzLmZldGNoTmF0aXZlSGlzdG9ncmFtKGNoYW5uZWxJbmRleCwgYmlucywgeiwga2V5KSk7XG4gICAgfVxuICAgIHJldHVybiBvZih0aGlzLmdldEhpc3RvZ3JhbShjaGFubmVsSW5kZXgsIGJpbnMpKTtcbiAgfVxuXG4gIC8qKiBGZXRjaCArIGNhY2hlIG9uZSBjaGFubmVsJ3MgbmF0aXZlLWJpdC1kZXB0aCBoaXN0b2dyYW0gZnJvbSBgR0VUIC9oaXN0b2dyYW1gICg+OC1iaXQpLiAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoTmF0aXZlSGlzdG9ncmFtKFxuICAgIGNoYW5uZWw6IG51bWJlcixcbiAgICBiaW5zOiBudW1iZXIsXG4gICAgejogbnVtYmVyLFxuICAgIGtleTogc3RyaW5nLFxuICApOiBQcm9taXNlPElIaXN0b2dyYW0gfCBudWxsPiB7XG4gICAgY29uc3QgaW5mb0I2NCA9IHRoaXMudGlsZXMuZ2V0U2VsZWN0ZWRJbmZvQjY0KCk7XG4gICAgaWYgKCFpbmZvQjY0KSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy50aWxlc1xuICAgICAgLmdldEF1dGhIZWFkZXJzKClcbiAgICAgIC5jYXRjaCgoKSA9PiAoe30pIGFzIFJlY29yZDxzdHJpbmcsIHN0cmluZz4pO1xuICAgIGNvbnN0IHVybCA9IGAke3RoaXMuYXBpfWhpc3RvZ3JhbT9pbmZvPSR7aW5mb0I2NH0mY2hhbm5lbD0ke2NoYW5uZWx9Jno9JHt6fSZiaW5zPSR7Ymluc31gO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXNwID0gYXdhaXQgZmV0Y2godXJsLCB7IGhlYWRlcnMgfSk7XG4gICAgICBpZiAoIXJlc3Aub2spIHJldHVybiBudWxsOyAvLyAyMDIgKHN0aWxsIGNhY2hpbmcpIG9yIHRyYW5zaWVudCDihpIgbnVsbDsgdGhlIHBhbmUgcmV0cmllc1xuICAgICAgY29uc3QgaGkgPSAoYXdhaXQgcmVzcC5qc29uKCkpIGFzIHtcbiAgICAgICAgYml0RGVwdGg6IG51bWJlcjtcbiAgICAgICAgcmFuZ2VNaW46IG51bWJlcjtcbiAgICAgICAgcmFuZ2VNYXg6IG51bWJlcjtcbiAgICAgICAgb2JzZXJ2ZWRNaW46IG51bWJlcjtcbiAgICAgICAgb2JzZXJ2ZWRNYXg6IG51bWJlcjtcbiAgICAgICAgYmluV2lkdGg6IG51bWJlcjtcbiAgICAgICAgY291bnRzOiBudW1iZXJbXTtcbiAgICAgIH07XG4gICAgICBpZiAoIWhpPy5jb3VudHMpIHJldHVybiBudWxsO1xuICAgICAgY29uc3Qgb3V0OiBJSGlzdG9ncmFtID0ge1xuICAgICAgICBiaW5zOiBoaS5jb3VudHMubWFwKChfLCBpKSA9PiBoaS5yYW5nZU1pbiArIGkgKiBoaS5iaW5XaWR0aCksXG4gICAgICAgIGNvdW50czogaGkuY291bnRzLFxuICAgICAgICBtYXg6IGhpLmNvdW50cy5yZWR1Y2UoKG0sIGMpID0+IChjID4gbSA/IGMgOiBtKSwgMCksXG4gICAgICAgIGJpdERlcHRoOiBoaS5iaXREZXB0aCxcbiAgICAgICAgcmFuZ2VNaW46IGhpLnJhbmdlTWluLFxuICAgICAgICByYW5nZU1heDogaGkucmFuZ2VNYXgsXG4gICAgICAgIG9ic2VydmVkTWluOiBoaS5vYnNlcnZlZE1pbixcbiAgICAgICAgb2JzZXJ2ZWRNYXg6IGhpLm9ic2VydmVkTWF4LFxuICAgICAgfTtcbiAgICAgIHRoaXMubmF0aXZlSGlzdG9ncmFtcy5zZXQoa2V5LCBvdXQpO1xuICAgICAgcmV0dXJuIG91dDtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUud2FybignW25hcGFyaS1qc10gbmF0aXZlIGhpc3RvZ3JhbSBmZXRjaCBmYWlsZWQnLCBlcnIpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICB9XG5cbiAgLyoqIFBlci1jaGFubmVsIGhpc3RvZ3JhbSBvZiB0aGUgZGlzcGxheWVkIFJHQiBjb21wb3NpdGUgKFIvRy9CIGJ5dGUpLCBmcm9tIHRoZSBsYXN0IHJlYWRiYWNrLiAqL1xuICBwcml2YXRlIHJnYkhpc3RvZ3JhbShjaGFubmVsSW5kZXg6IG51bWJlciwgYmluczogbnVtYmVyKTogSUhpc3RvZ3JhbSB8IG51bGwge1xuICAgIGNvbnN0IHB4ID0gdGhpcy5sYXN0UGl4ZWxzO1xuICAgIGlmICghcHgpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IGJhbmQgPSBNYXRoLm1heCgwLCBNYXRoLm1pbigyLCBjaGFubmVsSW5kZXgpKTtcbiAgICBjb25zdCBuID0gTWF0aC5tYXgoMSwgTWF0aC5taW4oMjU2LCBNYXRoLmZsb29yKGJpbnMpIHx8IDI1NikpO1xuICAgIGNvbnN0IGNvdW50cyA9IG5ldyBBcnJheShuKS5maWxsKDApO1xuICAgIGNvbnN0IGRhdGEgPSBweC5kYXRhO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZGF0YS5sZW5ndGg7IGkgKz0gNCkge1xuICAgICAgaWYgKGRhdGFbaSArIDNdID09PSAwKSBjb250aW51ZTsgLy8gc2tpcCB0cmFuc3BhcmVudCBwYWRkaW5nXG4gICAgICBjb3VudHNbTWF0aC5taW4obiAtIDEsIChkYXRhW2kgKyBiYW5kXSAqIG4pID4+IDgpXSsrO1xuICAgIH1cbiAgICBjb25zdCBiaW5zQXJyID0gQXJyYXkuZnJvbSh7IGxlbmd0aDogbiB9LCAoXywgaSkgPT4gKGkgKiAyNTYpIC8gbik7XG4gICAgcmV0dXJuIHsgYmluczogYmluc0FyciwgY291bnRzLCBtYXg6IGNvdW50cy5yZWR1Y2UoKG0sIGMpID0+IChjID4gbSA/IGMgOiBtKSwgMCkgfTtcbiAgfVxuICBleHBvcnRDb21wb3NpdGUoKTogdm9pZCB7XG4gICAgY29uc3QgdiA9IHRoaXMudmlld2VyO1xuICAgIGlmICghdikgcmV0dXJuO1xuICAgIHZvaWQgdi5zY3JlZW5zaG90KCkudGhlbigoYmxvYikgPT4ge1xuICAgICAgY29uc3QgdXJsID0gVVJMLmNyZWF0ZU9iamVjdFVSTChibG9iKTtcbiAgICAgIGNvbnN0IGEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgICBhLmhyZWYgPSB1cmw7XG4gICAgICBhLmRvd25sb2FkID0gJ25hcGFyaS1qcy5wbmcnO1xuICAgICAgYS5jbGljaygpO1xuICAgICAgVVJMLnJldm9rZU9iamVjdFVSTCh1cmwpO1xuICAgIH0pO1xuICB9XG4gIC8qKiBOYXRpdmUtYml0LWRlcHRoICgxNi8zMi1iaXQpIG11bHRpLWJhbmQgVElGRiBleHBvcnQgdmlhIHRoZSBzZXJ2ZXIgYC9leHBvcnQvdGlmZmAgZW5kcG9pbnQg4oCUXG4gICAqICB0aGUgZGlzcGxheWVkIFBORyBpcyBhbiA4LWJpdCBmaWd1cmUsIHRoaXMgcHJlc2VydmVzIHRoZSB0cnVlIHBpeGVsIHZhbHVlcy4gVmlzaWJsZSBjaGFubmVsc1xuICAgKiAgb25seSAob21pdHRlZCB3aGVuIGFsbCBhcmUgdmlzaWJsZSDihpIgc2VydmVyIGRlZmF1bHQpLiBNaXJyb3JzIHRoZSBPU0QgYmFja2VuZC4gKi9cbiAgYXN5bmMgZXhwb3J0RGF0YSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBpbmZvQjY0ID0gdGhpcy50aWxlcy5nZXRTZWxlY3RlZEluZm9CNjQoKTtcbiAgICBpZiAoIWluZm9CNjQpIHJldHVybjtcbiAgICBjb25zdCBzdGF0ZXMgPSB0aGlzLnN0b3JlLmN1cnJlbnRDaGFubmVsU3RhdGVzKCk7XG4gICAgY29uc3QgdmlzaWJsZSA9IHN0YXRlcy5maWx0ZXIoKGMpID0+IGMudmlzaWJsZSkubWFwKChjKSA9PiBjLmluZGV4KTtcbiAgICBjb25zdCBjaFBhcmFtID1cbiAgICAgIHZpc2libGUubGVuZ3RoICYmIHZpc2libGUubGVuZ3RoIDwgc3RhdGVzLmxlbmd0aCA/IGAmY2hhbm5lbHM9JHt2aXNpYmxlLmpvaW4oJywnKX1gIDogJyc7XG4gICAgY29uc3QgeiA9IHRoaXMubG9hZGVkPy56ID8/IDA7XG4gICAgY29uc3QgdXJsID0gYCR7dGhpcy5hcGl9ZXhwb3J0L3RpZmY/aW5mbz0ke2luZm9CNjR9Jno9JHt6fSR7Y2hQYXJhbX1gO1xuICAgIGNvbnN0IHN0ZW0gPSAodGhpcy5sb2FkZWQ/LmZpbGVuYW1lIHx8ICdpbWFnZScpLnJlcGxhY2UoL1xcLlteLl0rJC8sICcnKTtcbiAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgdGhpcy50aWxlc1xuICAgICAgLmdldEF1dGhIZWFkZXJzKClcbiAgICAgIC5jYXRjaCgoKSA9PiAoe30pIGFzIFJlY29yZDxzdHJpbmcsIHN0cmluZz4pO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXNwID0gYXdhaXQgZmV0Y2godXJsLCB7IGhlYWRlcnMgfSk7XG4gICAgICBpZiAocmVzcC5zdGF0dXMgPT09IDIwMikge1xuICAgICAgICBjb25zb2xlLndhcm4oJ1tuYXBhcmktanNdIDE2LWJpdCBleHBvcnQ6IGZpbGUgc3RpbGwgY2FjaGluZyDigJQgdHJ5IGFnYWluIHNob3J0bHkuJyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmICghcmVzcC5vaykgdGhyb3cgbmV3IEVycm9yKGBzdGF0dXMgJHtyZXNwLnN0YXR1c31gKTtcbiAgICAgIHNhdmVBcyhhd2FpdCByZXNwLmJsb2IoKSwgYCR7c3RlbX1fMTZiaXQub21lLnRpZmApO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgY29uc29sZS53YXJuKCdbbmFwYXJpLWpzXSAxNi1iaXQgVElGRiBleHBvcnQgZmFpbGVkJywgZXJyKTtcbiAgICB9XG4gIH1cbiAgdW5zdWJzY3JpYmUoKTogdm9pZCB7XG4gICAgdGhpcy5yZXNldCgpO1xuICB9XG59XG4iXX0=
|