@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,1780 @@
|
|
|
1
|
+
import { Injectable, Inject, Optional } from '@angular/core';
|
|
2
|
+
import { HttpClient } from '@angular/common/http';
|
|
3
|
+
import { BehaviorSubject, Subject, combineLatest, firstValueFrom, of } from 'rxjs';
|
|
4
|
+
import { timeout } from 'rxjs/operators';
|
|
5
|
+
import { OSD } from './osd-lib';
|
|
6
|
+
import { VisualizerStore } from '../../store/visualizer-store.service';
|
|
7
|
+
import { RegionStore } from '../../store/region-store.service';
|
|
8
|
+
import { TILE_ACCESS_PORT } from '../../contracts/ports/tile-access.port';
|
|
9
|
+
import { VIZ_CONFIG } from '../../contracts/viz-config';
|
|
10
|
+
import { PlotType, PLOT_TYPE_DESCRIPTORS } from '../../contracts/plot-type';
|
|
11
|
+
import { ViewerFeature, capabilitiesOf } from '../../contracts/capabilities.contract';
|
|
12
|
+
import { OsdRegionOverlay } from './osd-region-overlay';
|
|
13
|
+
import { OsdScaleBar } from './osd-scale-bar';
|
|
14
|
+
import { OsdCoordinateTransform } from './osd-coordinate-transform';
|
|
15
|
+
import { elementToImage, imageRectToViewport, viewportRectToImage } from './osd-coords';
|
|
16
|
+
import { buildTileUrl, fetchTileBitmap } from './tile-client';
|
|
17
|
+
import { SliceCache } from './slice-cache';
|
|
18
|
+
import { DisplayPipeline } from './display-pipeline';
|
|
19
|
+
import { HistogramSampler } from './histogram-sampler';
|
|
20
|
+
import { BaseStoreVisualizer } from '../base-store-visualizer';
|
|
21
|
+
import { SimpleSliceAccessService } from '../simple-slice-access.service';
|
|
22
|
+
import { WandToolService } from '../../toolbar/wand/wand-tool.service';
|
|
23
|
+
import { BrushToolService } from '../../toolbar/brush/brush-tool.service';
|
|
24
|
+
import { SamToolService } from '../../toolbar/segmentation/sam-tool.service';
|
|
25
|
+
import { SamPointToolService } from '../../toolbar/segmentation/sam-point-tool.service';
|
|
26
|
+
import { CellSegmentToolService } from '../../toolbar/segmentation/cell-segment-tool.service';
|
|
27
|
+
import { CELL_SEGMENTER } from '../../contracts/cell-segmenter.contract';
|
|
28
|
+
import { VertexEraserToolService } from '../../toolbar/vertex-eraser/vertex-eraser-tool.service';
|
|
29
|
+
import { ZoomToBoxToolService } from '../../toolbar/zoom-to-box/zoom-to-box-tool.service';
|
|
30
|
+
import { buildColormapLut } from '../../contracts/colormap-lut';
|
|
31
|
+
import { saveAs } from 'file-saver';
|
|
32
|
+
import * as i0 from "@angular/core";
|
|
33
|
+
import * as i1 from "@angular/common/http";
|
|
34
|
+
import * as i2 from "../../toolbar/wand/wand-tool.service";
|
|
35
|
+
import * as i3 from "../../toolbar/brush/brush-tool.service";
|
|
36
|
+
import * as i4 from "../../toolbar/segmentation/sam-tool.service";
|
|
37
|
+
import * as i5 from "../../toolbar/segmentation/sam-point-tool.service";
|
|
38
|
+
import * as i6 from "../../toolbar/segmentation/cell-segment-tool.service";
|
|
39
|
+
import * as i7 from "../../toolbar/vertex-eraser/vertex-eraser-tool.service";
|
|
40
|
+
import * as i8 from "../../toolbar/zoom-to-box/zoom-to-box-tool.service";
|
|
41
|
+
import * as i9 from "../../store/visualizer-store.service";
|
|
42
|
+
import * as i10 from "../../store/region-store.service";
|
|
43
|
+
import * as i11 from "../simple-slice-access.service";
|
|
44
|
+
/**
|
|
45
|
+
* Quiet OpenSeadragon's "[Viewport.*] is not accurate with multi-image" advisories.
|
|
46
|
+
* A multichannel image is composited from one TiledImage per channel — a legitimate
|
|
47
|
+
* multi-image world — and OSD logs that advisory (at error level) from its own
|
|
48
|
+
* navigator/overview rendering on every animation frame, flooding the console. Our
|
|
49
|
+
* own image<->viewport conversions already route through world item 0 (see
|
|
50
|
+
* osd-coords), so the remaining advisories are OSD-internal and only affect the
|
|
51
|
+
* minimap's box accuracy (cosmetic). This wraps OSD's logger in a Proxy that drops
|
|
52
|
+
* only that one message string and forwards every other log untouched. Idempotent.
|
|
53
|
+
*/
|
|
54
|
+
function silenceOsdMultiImageAdvisory() {
|
|
55
|
+
const osd = OSD;
|
|
56
|
+
if (osd.__multiImageFilterInstalled)
|
|
57
|
+
return;
|
|
58
|
+
const base = (osd.console && typeof osd.console.error === 'function')
|
|
59
|
+
? osd.console
|
|
60
|
+
: (typeof console !== 'undefined' ? console : null);
|
|
61
|
+
if (!base)
|
|
62
|
+
return;
|
|
63
|
+
const isAdvisory = (a) => typeof a === 'string' && a.indexOf('not accurate with multi-image') !== -1;
|
|
64
|
+
osd.console = new Proxy(base, {
|
|
65
|
+
get(target, prop) {
|
|
66
|
+
const orig = target[prop];
|
|
67
|
+
if ((prop === 'error' || prop === 'warn') && typeof orig === 'function') {
|
|
68
|
+
return (...args) => { if (isAdvisory(args[0]))
|
|
69
|
+
return; orig.apply(target, args); };
|
|
70
|
+
}
|
|
71
|
+
return typeof orig === 'function' ? orig.bind(target) : orig;
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
osd.__multiImageFilterInstalled = true;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* OpenSeadragon visualization backend.
|
|
78
|
+
*
|
|
79
|
+
* Renders the *image* plot type as a natively-tiled, zoomable raster, backed by
|
|
80
|
+
* the jit-service tile endpoints (`GET /tiles/info` + `GET /tile`, which reuse
|
|
81
|
+
* the Bio-Formats ROI renderer). Plotly keeps the scientific/data plot types
|
|
82
|
+
* (scalar heatmap, surface, contour, scatter, line, scatter3d, isosurface) —
|
|
83
|
+
* hence this backend advertises only `ImageDisplay`.
|
|
84
|
+
*
|
|
85
|
+
* Wired: load → descriptor, plot → mount viewer with a custom tile source,
|
|
86
|
+
* zoom/pan → viewport API. NOT wired yet (follow-ups): region overlays + tools
|
|
87
|
+
* (a positioned canvas/SVG overlay or Annotorious), pixel readback, and the
|
|
88
|
+
* per-tile LUT/contrast params. Not registered in DI providers — `PlotlyService`
|
|
89
|
+
* is still the only active backend; this proves the contract supports a second
|
|
90
|
+
* implementation against real endpoints.
|
|
91
|
+
*/
|
|
92
|
+
export class OpenSeadragonVisualizerService extends BaseStoreVisualizer {
|
|
93
|
+
constructor(http, tiles, wandTool, brushTool, samTool, samPointTool, cellSegmentTool, cellSegmenter, eraserTool, zoomToBoxTool, store, regionStore, simpleStack, config) {
|
|
94
|
+
super(regionStore, store);
|
|
95
|
+
this.http = http;
|
|
96
|
+
this.tiles = tiles;
|
|
97
|
+
this.wandTool = wandTool;
|
|
98
|
+
this.brushTool = brushTool;
|
|
99
|
+
this.samTool = samTool;
|
|
100
|
+
this.samPointTool = samPointTool;
|
|
101
|
+
this.cellSegmentTool = cellSegmentTool;
|
|
102
|
+
this.cellSegmenter = cellSegmenter;
|
|
103
|
+
this.eraserTool = eraserTool;
|
|
104
|
+
this.zoomToBoxTool = zoomToBoxTool;
|
|
105
|
+
this.simpleStack = simpleStack;
|
|
106
|
+
/** OSD's strength is displaying a large zoomable image — nothing else here. */
|
|
107
|
+
this.capabilities = capabilitiesOf([ViewerFeature.ImageDisplay]);
|
|
108
|
+
this.viewer = null;
|
|
109
|
+
this.overlay = null;
|
|
110
|
+
this.scaleBar = null;
|
|
111
|
+
this.descriptor = null;
|
|
112
|
+
/** Base64 RawFileInfo + current z-slice, kept so setZIndex can rebuild the
|
|
113
|
+
* tile source and swap slices live (stack navigation). */
|
|
114
|
+
this.infoB64 = '';
|
|
115
|
+
this.currentZ = 0;
|
|
116
|
+
/** True while the current image is "simple" (tiled:false — self-contained
|
|
117
|
+
* per-slice URLs, no tile server; e.g. a numbered image series assembled
|
|
118
|
+
* client-side into a stack). setZIndex swaps the single-image source for
|
|
119
|
+
* the new slice's URL instead of updating a tiled z-param. */
|
|
120
|
+
this.simpleMode = false;
|
|
121
|
+
/** urls[] for the current simple-mode image, so setZIndex can look up the
|
|
122
|
+
* slice being scrubbed to (via {@link SimpleSliceAccessService.urlFor}).
|
|
123
|
+
* Unused (and left stale, harmlessly) in tiled mode. */
|
|
124
|
+
this.simpleUrls = [];
|
|
125
|
+
/** preview blob URL → full-res upscaled blob URL (see toFullResUrl), so
|
|
126
|
+
* re-visiting a simple-mode slice doesn't re-upscale. Revoked on file change. */
|
|
127
|
+
this.simpleFullResUrls = new Map();
|
|
128
|
+
/** Skip the full-res upscale above this longest-side dimension — a folder
|
|
129
|
+
* stack is per-file previews (well under this); this only guards against an
|
|
130
|
+
* accidental enormous canvas allocation. */
|
|
131
|
+
this.SIMPLE_UPSCALE_MAX_DIM = 8192;
|
|
132
|
+
this.coordTransform = null;
|
|
133
|
+
/** Wand sampling matrix read back from the rendered viewport; cached until
|
|
134
|
+
* the viewport changes (see readbackViewport). */
|
|
135
|
+
this.viewportPixels = null;
|
|
136
|
+
/** The DOM id of the element OSD is mounted in (shared with Plotly); the
|
|
137
|
+
* on-canvas tools attach their overlays here. */
|
|
138
|
+
this.plotDiv = '';
|
|
139
|
+
/** 256-entry RGB LUT for the active colormap, applied to grayscale tiles via
|
|
140
|
+
* the tile-invalidated pixel pipeline (mirrors Plotly's heatmap colorscale).
|
|
141
|
+
* Null while options resolve; recoloring is skipped until it's built. */
|
|
142
|
+
this.colorLut = null;
|
|
143
|
+
/** Only grayscale images get a colormap (RGB tiles pass through untouched). */
|
|
144
|
+
this.isGrayscaleImage = false;
|
|
145
|
+
this.colormapSub = null;
|
|
146
|
+
/** Latest per-channel display state (window/gamma/visibility) from the store,
|
|
147
|
+
* read synchronously by recolorTile. Channel 0 drives grayscale windowing;
|
|
148
|
+
* R/G/B (indices 0-2) drive RGB per-channel windowing. */
|
|
149
|
+
this.channelStates = [];
|
|
150
|
+
/** True for multichannel fluorescence (channelCount > 1, not RGB): tiles are
|
|
151
|
+
* composited client-side from per-channel single-band fetches (see
|
|
152
|
+
* recolorMultiChannelTile) rather than recolored in place. */
|
|
153
|
+
this.isMultiChannel = false;
|
|
154
|
+
/** Count of real Bio-Formats resolution levels (per-channel tiles exist only
|
|
155
|
+
* here). For multichannel images the tile source is built from these alone so
|
|
156
|
+
* every displayed tile supports a per-channel fetch. */
|
|
157
|
+
this.realLevels = 0;
|
|
158
|
+
/** Inverted background (white = zero): inverts the display value before the
|
|
159
|
+
* LUT (grayscale) / per channel (RGB). */
|
|
160
|
+
this.invertBg = false;
|
|
161
|
+
/** Bearer token for OSD's own tile fetches (HttpClient calls get it via the
|
|
162
|
+
* interceptor; OSD's loader does not, so we pass it as an ajax header). */
|
|
163
|
+
this.authHeaders = {};
|
|
164
|
+
/** Whether the overview navigator (minimap) is shown. Read at viewer creation;
|
|
165
|
+
* a consumer that doesn't want it (e.g. a small embedded preview) sets it off
|
|
166
|
+
* via {@link setNavigatorVisible} before the first render. */
|
|
167
|
+
this.navigatorVisible = true;
|
|
168
|
+
/** Image smoothing (bilinear). `false` → nearest-neighbour, so zoomed-in pixels
|
|
169
|
+
* render as crisp blocks (pixel inspection). Defaults to `false` so the image
|
|
170
|
+
* opens showing raw pixels. Read at viewer creation; toggled via
|
|
171
|
+
* {@link setImageSmoothingEnabled}. */
|
|
172
|
+
this.smoothingEnabled = false;
|
|
173
|
+
/** Overall deadline for the /tiles/info poll loop. An uncached whole-slide
|
|
174
|
+
* image (e.g. .ndpi) is cached server-side first (GCS->PVC), which can take
|
|
175
|
+
* several minutes — we poll (short requests; the cache-progress overlay
|
|
176
|
+
* shows the wait) until it's ready. Generous because each poll is cheap; on
|
|
177
|
+
* expiry the render pipeline gives up and the router falls back to Plotly. */
|
|
178
|
+
this.tilesInfoTimeoutMs = 600000; // 10 min
|
|
179
|
+
/** Per-channel fit-view tile budget (tiles at the coarsest real level × channels).
|
|
180
|
+
* Above it, a multichannel image renders server-composited instead of per-channel
|
|
181
|
+
* — its full-res reads (it has no overview pyramid) would be too many × N channels.
|
|
182
|
+
* 64 (e.g. a 4x4 single-FOV z-stack × 4ch) stays per-channel; a whole-slide
|
|
183
|
+
* (hundreds–thousands) falls back. */
|
|
184
|
+
this.MAX_MULTICHANNEL_FIT_TILES = 256;
|
|
185
|
+
/** Pixel display pipeline (window/gamma/invert/colormap + additive tint) —
|
|
186
|
+
* shared by tile recoloring and the composite export so they stay identical
|
|
187
|
+
* (see DisplayPipeline). Host closures read the service's live fields. */
|
|
188
|
+
this.display = new DisplayPipeline({
|
|
189
|
+
isGrayscale: () => this.isGrayscaleImage,
|
|
190
|
+
colorLut: () => this.colorLut,
|
|
191
|
+
channelStates: () => this.channelStates,
|
|
192
|
+
invertBg: () => this.invertBg,
|
|
193
|
+
});
|
|
194
|
+
/** Stack-slice cache + background preloader (see SliceCache — refactoring
|
|
195
|
+
* plan Step 3). The host accessors are live closures, so the cache always
|
|
196
|
+
* reads the service's current viewer/descriptor/z — exactly the fields the
|
|
197
|
+
* moved code used to read directly. */
|
|
198
|
+
this.cache = new SliceCache({
|
|
199
|
+
viewer: () => this.viewer,
|
|
200
|
+
hasImage: () => !!(this.viewer && this.descriptor && this.infoB64),
|
|
201
|
+
sliceCount: () => this.descriptor?.z ?? 1,
|
|
202
|
+
currentZ: () => this.currentZ,
|
|
203
|
+
isMultiChannel: () => this.isMultiChannel,
|
|
204
|
+
channelCount: () => Math.max(1, this.channelStates.length || (this.descriptor?.channels ?? 1)),
|
|
205
|
+
channelVisible: (c) => this.channelStates[c]?.visible !== false,
|
|
206
|
+
buildTileSource: (z, channel) => this.buildTileSource(this.descriptor, this.infoB64, z, channel),
|
|
207
|
+
onCompositeSliceAdded: (z) => this.sampler.computeImageWindow(this.descriptor, this.infoB64, z),
|
|
208
|
+
});
|
|
209
|
+
this.stackLoading$ = new BehaviorSubject(false);
|
|
210
|
+
this.stackLoadingProgress$ = new BehaviorSubject(0);
|
|
211
|
+
this.autoscaleEvent$ = new Subject();
|
|
212
|
+
/** Visible image region (full-image pixel coords) emitted when the view
|
|
213
|
+
* settles, so the intensity inset can re-sample at the current zoom level. */
|
|
214
|
+
this.viewportChange$ = new Subject();
|
|
215
|
+
// Region state (regions, selection, the update event) lives in the shared
|
|
216
|
+
// RegionStore; the IRegionStore methods below delegate to it.
|
|
217
|
+
this.intensityProfile$ = new Subject();
|
|
218
|
+
this.api = config.slideCropServer;
|
|
219
|
+
this.sampler = new HistogramSampler(this.http, this.api, {
|
|
220
|
+
realLevels: () => this.realLevels,
|
|
221
|
+
channelCount: (d) => this.channelStates.length || (d.channels ?? 1),
|
|
222
|
+
isGrayscale: () => this.isGrayscaleImage,
|
|
223
|
+
// Nudge the channel-states stream so the pane re-reads getHistogram now,
|
|
224
|
+
// in case its bounded retry window already lapsed.
|
|
225
|
+
onChannelHistogramsSampled: () => this.store.setChannelStates(this.store.currentChannelStates()),
|
|
226
|
+
onGrayWindowSampled: (min, max) => this.seedGrayWindow(min, max),
|
|
227
|
+
});
|
|
228
|
+
this.ensureColormapSubscription();
|
|
229
|
+
}
|
|
230
|
+
// Image metadata and classification colours live in the shared VisualizerStore.
|
|
231
|
+
/**
|
|
232
|
+
* Subscribe to the shared VisualizerStore colormap/reverse so OSD recolors in
|
|
233
|
+
* lock-step with Plotly. Idempotent and self-healing: this service is a root
|
|
234
|
+
* singleton, but `unsubscribe()` (called on VisualizerComponent destroy)
|
|
235
|
+
* tears the subscription down — and the constructor never runs again. So
|
|
236
|
+
* `plot()` calls this to re-establish it after a component teardown/recreate
|
|
237
|
+
* (e.g. switching images), otherwise colormap changes would be silently
|
|
238
|
+
* dropped on every image after the first switch.
|
|
239
|
+
*/
|
|
240
|
+
ensureColormapSubscription() {
|
|
241
|
+
if (this.colormapSub)
|
|
242
|
+
return;
|
|
243
|
+
// Colormap/reverse live in the shared VisualizerStore — the single source
|
|
244
|
+
// of truth for both backends. Rebuild the LUT and re-run the pixel pipeline
|
|
245
|
+
// whenever either changes, so OSD recolors in lock-step with Plotly.
|
|
246
|
+
// Colormap/reverse + per-channel window/gamma/visibility + invert all live in
|
|
247
|
+
// the shared VisualizerStore. Rebuild the LUT and re-run the pixel pipeline
|
|
248
|
+
// whenever any of them changes, so the Channels & Histogram pane updates the
|
|
249
|
+
// image live and OSD stays in lock-step with Plotly.
|
|
250
|
+
this.colormapSub = combineLatest([
|
|
251
|
+
this.store.getColormap(),
|
|
252
|
+
this.store.getReverseScale(),
|
|
253
|
+
this.store.getChannelStates(),
|
|
254
|
+
this.store.getInvert(),
|
|
255
|
+
]).subscribe(([cm, rev, channels, invert]) => {
|
|
256
|
+
this.colorLut = buildColormapLut(cm?.data?.value, !!rev);
|
|
257
|
+
this.channelStates = channels;
|
|
258
|
+
this.invertBg = !!invert;
|
|
259
|
+
// Multichannel: each channel is its own TiledImage — visibility is the
|
|
260
|
+
// image's opacity (window/gamma/colour are applied by recolorChannelTile
|
|
261
|
+
// on the invalidate below). Re-applying the current slice's reveal picks up
|
|
262
|
+
// the new per-channel visibility (cached slices stay hidden).
|
|
263
|
+
if (this.isMultiChannel) {
|
|
264
|
+
this.cache.revealChannelSlice(this.currentZ);
|
|
265
|
+
}
|
|
266
|
+
// requestInvalidate(true) restores each tile to its original data before
|
|
267
|
+
// re-running recolorTile, so a change always maps afresh (no compounding).
|
|
268
|
+
// RGB now recolors too (per-channel window/visibility), so don't gate on
|
|
269
|
+
// grayscale.
|
|
270
|
+
this.invalidateDisplay();
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
/** Seed the Intensity channel with a measured auto-window while it's still
|
|
274
|
+
* at full range (never clobber the user's manual window); if the user
|
|
275
|
+
* already windowed, just re-invalidate so painted tiles pick the LUT up. */
|
|
276
|
+
seedGrayWindow(min, max) {
|
|
277
|
+
const ch0 = this.store.currentChannelStates()[0];
|
|
278
|
+
if (ch0 && ch0.min === 0 && ch0.max === 255) {
|
|
279
|
+
this.store.setChannelState(0, { min, max });
|
|
280
|
+
}
|
|
281
|
+
else if (this.viewer && this.colorLut) {
|
|
282
|
+
try {
|
|
283
|
+
this.viewer.world.requestInvalidate(true);
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
/* no-op */
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
this.viewer.navigator?.world?.requestInvalidate(true);
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
/* no-op */
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// ── IDataRenderer ────────────────────────────────────────────────────
|
|
297
|
+
/**
|
|
298
|
+
* Fetch the tile-source descriptor for the selected file. The `info` param is
|
|
299
|
+
* the base64 RawFileInfo the rest of the API uses; the GET goes through the
|
|
300
|
+
* auth interceptor (Bearer). We also grab a token here for OSD's own tile
|
|
301
|
+
* fetches (its loader bypasses HttpClient).
|
|
302
|
+
*/
|
|
303
|
+
async load(imageInfo, zIndex) {
|
|
304
|
+
const filename = imageInfo?.fileName;
|
|
305
|
+
// A different image was selected — stop the previous stack's background
|
|
306
|
+
// loading immediately rather than letting it finish behind the new image.
|
|
307
|
+
if (filename && this.currentFileName && filename !== this.currentFileName) {
|
|
308
|
+
this.cache.cancelBackgroundLoad();
|
|
309
|
+
this.revokeSimpleFullResUrls();
|
|
310
|
+
}
|
|
311
|
+
this.simpleStack.noteActiveFile(filename);
|
|
312
|
+
// Simple (in-memory / non-tiled) image: skip the tile server entirely — no
|
|
313
|
+
// getSelectedInfoB64, no /tiles/info poll. `urls[zIndex]` is a complete image
|
|
314
|
+
// OSD opens via its single-image source (see plot()).
|
|
315
|
+
if (this.simpleStack.isSimple(imageInfo)) {
|
|
316
|
+
return this.loadSimple(imageInfo, zIndex);
|
|
317
|
+
}
|
|
318
|
+
const infoB64 = this.tiles.getSelectedInfoB64();
|
|
319
|
+
if (!infoB64)
|
|
320
|
+
return { descriptor: null, infoB64: '', z: zIndex || 0, filename };
|
|
321
|
+
try {
|
|
322
|
+
this.authHeaders = await this.tiles.getAuthHeaders();
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
this.authHeaders = {}; // fall back to cookie auth (ajaxWithCredentials)
|
|
326
|
+
}
|
|
327
|
+
// Poll /tiles/info: the backend returns 202 while the source file is still
|
|
328
|
+
// being cached (GCS->PVC) — it kicks the download off in the background, so
|
|
329
|
+
// each request is short (no long-held connection to trip ingress/proxy or
|
|
330
|
+
// client timeouts), and 200 with the descriptor once it's ready. The
|
|
331
|
+
// cache-progress overlay shows progress during the wait. We give up after
|
|
332
|
+
// tilesInfoTimeoutMs so the render pipeline never hangs (the router then
|
|
333
|
+
// falls back to Plotly for this image).
|
|
334
|
+
const url = `${this.api}tiles/info?info=${infoB64}`;
|
|
335
|
+
const deadline = Date.now() + this.tilesInfoTimeoutMs;
|
|
336
|
+
for (;;) {
|
|
337
|
+
if (Date.now() > deadline) {
|
|
338
|
+
throw new Error('tiles/info not ready before deadline — file still caching');
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
const resp = await firstValueFrom(this.http.get(url, { observe: 'response' }).pipe(timeout(45000)));
|
|
342
|
+
if (resp.status === 200 && resp.body) {
|
|
343
|
+
this.descriptor = resp.body;
|
|
344
|
+
return { descriptor: resp.body, infoB64, z: zIndex || 0, filename };
|
|
345
|
+
}
|
|
346
|
+
// 202 Accepted (or empty body) → still caching; fall through and re-poll.
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
// Tolerate transient failures WHILE the file is being prepared: a slow
|
|
350
|
+
// metadata read on a multi-GB file (IFD walk), a 5xx mid-pipeline, a
|
|
351
|
+
// proxy hiccup. A single bad poll must NOT drop us to Plotly — a large
|
|
352
|
+
// image always passes through this busy window, so bailing here is why
|
|
353
|
+
// it "always ends up on Plotly". Keep polling until ready or deadline.
|
|
354
|
+
console.warn('[OSD] tiles/info poll retry', err);
|
|
355
|
+
}
|
|
356
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** Build the `plot()` payload for a simple (tiled:false) image: a single-level
|
|
360
|
+
* descriptor sized from `trueImageSize`, plus the directly-loadable URL —
|
|
361
|
+
* resolved and fetched via {@link SimpleSliceAccessService} (shared with
|
|
362
|
+
* napari-js; see its docs for why this can't be a raw `fetch()`/`<img>`). */
|
|
363
|
+
async loadSimple(imageInfo, zIndex) {
|
|
364
|
+
const z = zIndex || 0;
|
|
365
|
+
const filename = imageInfo?.fileName;
|
|
366
|
+
this.authHeaders = {};
|
|
367
|
+
const [width, height] = imageInfo.trueImageSize ?? [0, 0];
|
|
368
|
+
const rawUrl = this.simpleStack.urlFor(imageInfo, z);
|
|
369
|
+
let url;
|
|
370
|
+
if (rawUrl) {
|
|
371
|
+
try {
|
|
372
|
+
const previewUrl = await this.simpleStack.fetchAsBlobUrl(rawUrl);
|
|
373
|
+
// Upscale the (downscaled) preview to full resolution so OSD's world
|
|
374
|
+
// matches the full-res ROI coordinate space — see toFullResUrl.
|
|
375
|
+
url = await this.toFullResUrl(previewUrl, width, height);
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
console.warn('[OSD] simple-mode slice fetch failed', err);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// No loadable URL (empty urls[] or the fetch failed): signal "couldn't
|
|
382
|
+
// load" with a null descriptor — same as the tiled path when there's no
|
|
383
|
+
// selected-file info — so plot() returns false (its `if (!d)` guard) and
|
|
384
|
+
// the router can fall back, instead of mounting an <img> with an undefined
|
|
385
|
+
// src that throws at runtime.
|
|
386
|
+
if (!url) {
|
|
387
|
+
console.warn('[OSD] simple-mode slice has no loadable URL; skipping OSD render', rawUrl);
|
|
388
|
+
return { descriptor: null, infoB64: '', z, filename };
|
|
389
|
+
}
|
|
390
|
+
const meta = imageInfo.imageMeta?.[0];
|
|
391
|
+
const descriptor = {
|
|
392
|
+
width,
|
|
393
|
+
height,
|
|
394
|
+
tileSize: 0, // unused: simple mode never calls buildTileSource
|
|
395
|
+
z: 1, // single frame
|
|
396
|
+
channels: meta?.rgbChannels ?? (imageInfo.isGrayscale ? 1 : 3),
|
|
397
|
+
multichannel: false,
|
|
398
|
+
realLevels: 1,
|
|
399
|
+
levels: [{ res: 0, width, height }],
|
|
400
|
+
mppX: meta?.mppX ?? 0,
|
|
401
|
+
mppY: meta?.mppY ?? 0,
|
|
402
|
+
};
|
|
403
|
+
return {
|
|
404
|
+
descriptor,
|
|
405
|
+
infoB64: '',
|
|
406
|
+
z,
|
|
407
|
+
filename,
|
|
408
|
+
simple: true,
|
|
409
|
+
url,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* OSD's `{type:'image'}` source sizes its coordinate world to the image's
|
|
414
|
+
* NATURAL pixels. A folder-stack slice is a server `/preview` whose pixel size
|
|
415
|
+
* needn't match the full-resolution image — it may be downscaled (large
|
|
416
|
+
* images) OR larger than the dimensions `/metadata` reports. Either way OSD's
|
|
417
|
+
* world would differ from the full-res ROI coordinate space (QuPath geojson,
|
|
418
|
+
* in level-0 pixels): a smaller preview renders ROIs oversized, a larger one
|
|
419
|
+
* renders them undersized/offset. Resample the preview to EXACTLY the
|
|
420
|
+
* full-resolution dimensions (blurry but positionally exact — scaling up or
|
|
421
|
+
* down as needed) so OSD's world matches the ROI coordinate space, while still
|
|
422
|
+
* using OSD's reliable single-image renderer. Cached per preview URL (revoked
|
|
423
|
+
* on file change); a no-op when the preview is already exactly full-res (small
|
|
424
|
+
* images, in-memory pipeline blobs), when the dims are unknown, or when
|
|
425
|
+
* they're implausibly large (guards against an enormous canvas). (jit-ui#93)
|
|
426
|
+
*/
|
|
427
|
+
async toFullResUrl(previewUrl, width, height) {
|
|
428
|
+
if (!width || !height || Math.max(width, height) > this.SIMPLE_UPSCALE_MAX_DIM)
|
|
429
|
+
return previewUrl;
|
|
430
|
+
const cached = this.simpleFullResUrls.get(previewUrl);
|
|
431
|
+
if (cached)
|
|
432
|
+
return cached;
|
|
433
|
+
let img;
|
|
434
|
+
try {
|
|
435
|
+
img = await this.loadImageEl(previewUrl);
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
return previewUrl; // couldn't decode — let OSD try the URL as-is
|
|
439
|
+
}
|
|
440
|
+
// Already the exact full-res world — no resample needed. (A preview that is
|
|
441
|
+
// larger OR smaller than full-res must still be resized so OSD's world ==
|
|
442
|
+
// the ROI coordinate space; only an exact match is skippable.)
|
|
443
|
+
if (img.naturalWidth === width && img.naturalHeight === height)
|
|
444
|
+
return previewUrl;
|
|
445
|
+
const canvas = document.createElement('canvas');
|
|
446
|
+
canvas.width = width;
|
|
447
|
+
canvas.height = height;
|
|
448
|
+
const ctx = canvas.getContext('2d');
|
|
449
|
+
if (!ctx)
|
|
450
|
+
return previewUrl;
|
|
451
|
+
ctx.drawImage(img, 0, 0, width, height); // scale preview (up or down) to full-res
|
|
452
|
+
const blob = await new Promise((resolve) => canvas.toBlob(resolve));
|
|
453
|
+
if (!blob)
|
|
454
|
+
return previewUrl;
|
|
455
|
+
const fullResUrl = URL.createObjectURL(blob);
|
|
456
|
+
this.simpleFullResUrls.set(previewUrl, fullResUrl);
|
|
457
|
+
return fullResUrl;
|
|
458
|
+
}
|
|
459
|
+
/** Load a URL into an HTMLImageElement (resolves once decoded). Uses
|
|
460
|
+
* document.createElement rather than `new Image()` because this file's
|
|
461
|
+
* `Image` import is image-js's decoder, not the DOM element. */
|
|
462
|
+
loadImageEl(url) {
|
|
463
|
+
return new Promise((resolve, reject) => {
|
|
464
|
+
const img = document.createElement('img');
|
|
465
|
+
img.onload = () => resolve(img);
|
|
466
|
+
img.onerror = (e) => reject(e);
|
|
467
|
+
img.src = url;
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
revokeSimpleFullResUrls() {
|
|
471
|
+
for (const u of this.simpleFullResUrls.values())
|
|
472
|
+
URL.revokeObjectURL(u);
|
|
473
|
+
this.simpleFullResUrls.clear();
|
|
474
|
+
}
|
|
475
|
+
/** Mount the viewer with a custom tile source pointing at `GET /tile`. */
|
|
476
|
+
plot(plotDiv, imageLoaded, imageInfo, _screenHeight, _plotType, inPlace) {
|
|
477
|
+
const loaded = imageLoaded;
|
|
478
|
+
const d = loaded?.descriptor;
|
|
479
|
+
if (!d)
|
|
480
|
+
return Promise.resolve(false);
|
|
481
|
+
// Re-establish the colormap subscription if a prior component teardown
|
|
482
|
+
// (unsubscribe()) tore it down — this service is a root singleton, so the
|
|
483
|
+
// constructor won't run again to recreate it.
|
|
484
|
+
this.ensureColormapSubscription();
|
|
485
|
+
// OSD tiles natively, so the in-place (large) pass is normally a no-op
|
|
486
|
+
// once mounted — the tile pyramid IS the full resolution regardless of
|
|
487
|
+
// which ImageInfo tier requested it. Simple mode has no pyramid to fall
|
|
488
|
+
// back on: it displays literally whatever URL was last opened, so
|
|
489
|
+
// without this swap the small tier's 128px placeholder would stay on
|
|
490
|
+
// screen forever (pixelated, never "sharpening" like the tiled path).
|
|
491
|
+
if (inPlace && this.viewer) {
|
|
492
|
+
if (loaded.simple && loaded.url) {
|
|
493
|
+
// Also refresh simpleUrls to THIS phase's (large-tier) urls[] — it was
|
|
494
|
+
// set from the small tier's 128px urls on the initial mount below,
|
|
495
|
+
// which never runs again for the in-place pass. Without this,
|
|
496
|
+
// setZIndex's later slider scrubs would keep reading the small
|
|
497
|
+
// tier's low-res URLs forever, even though the initial slice was
|
|
498
|
+
// correctly swapped to full resolution here.
|
|
499
|
+
this.simpleUrls = imageInfo?.urls ?? this.simpleUrls;
|
|
500
|
+
this.viewer.open({ type: 'image', url: loaded.url });
|
|
501
|
+
}
|
|
502
|
+
return Promise.resolve(true);
|
|
503
|
+
}
|
|
504
|
+
this.plotDiv = plotDiv;
|
|
505
|
+
this.currentFileName = imageInfo?.fileName;
|
|
506
|
+
this.descriptor = d;
|
|
507
|
+
this.infoB64 = loaded.infoB64;
|
|
508
|
+
this.currentZ = loaded.z;
|
|
509
|
+
this.simpleMode = !!loaded.simple;
|
|
510
|
+
this.simpleUrls = loaded.simple ? (imageInfo?.urls ?? []) : [];
|
|
511
|
+
// The descriptor's physical pixel size (server Bio-Formats `/tiles/info`) is
|
|
512
|
+
// authoritative — push it into the shared meta so the Region Editor reports
|
|
513
|
+
// areas in µm²/mm², matching the scale bar built from `d.mppX` below.
|
|
514
|
+
this.store.setPhysicalPixelSize(d.mppX, d.mppY);
|
|
515
|
+
// Use the SAME grayscale flag the colormap dropdown / Plotly use
|
|
516
|
+
// (imageInfo.isGrayscale ← rgbChannels === 1). The descriptor's `channels`
|
|
517
|
+
// (channelCount) diverges from rgbChannels for stacks, which left grayscale
|
|
518
|
+
// stacks un-recolored even though the colormap selector was shown.
|
|
519
|
+
this.isGrayscaleImage = !!imageInfo?.isGrayscale;
|
|
520
|
+
// Simple (non-tiled, in-memory) image: no pyramid, no per-channel tiles, and
|
|
521
|
+
// no server-side intensity sampling — OSD's single-image source decodes
|
|
522
|
+
// urls[z] directly. Skip the whole tiled-setup path below.
|
|
523
|
+
const simple = !!loaded.simple;
|
|
524
|
+
if (simple) {
|
|
525
|
+
this.realLevels = 1;
|
|
526
|
+
this.isMultiChannel = false;
|
|
527
|
+
this.cache.clearChannelGroups();
|
|
528
|
+
this.destroyViewer();
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
// Multichannel fluorescence (indexed/LUT-bearing stacks) composite client-side
|
|
532
|
+
// from per-channel tiles. Trust the server's explicit `multichannel` flag — the
|
|
533
|
+
// old `channels>1 && grayscale` heuristic also matched RGB photos Bio-Formats
|
|
534
|
+
// reads as separated planes (channels>1, rgbChannels==1), splitting them into N
|
|
535
|
+
// per-channel TiledImages that flooded the tile endpoint and hung on load.
|
|
536
|
+
this.realLevels = d.realLevels ?? d.levels.length;
|
|
537
|
+
// Tiles to cover the whole image at the coarsest REAL Bio-Formats level (the
|
|
538
|
+
// smallest level that has real, per-channel-fetchable tiles — synthetic overview
|
|
539
|
+
// levels are server-composited only). A pyramidal image's coarsest real level is
|
|
540
|
+
// tiny (few tiles); a flat/no-pyramid image's is the full-res grid (many tiles).
|
|
541
|
+
const coarse = d.levels[this.realLevels - 1] ?? d.levels[d.levels.length - 1];
|
|
542
|
+
const coarseW = coarse ? Math.ceil(coarse.width / d.tileSize) : 1;
|
|
543
|
+
const coarseH = coarse ? Math.ceil(coarse.height / d.tileSize) : 1;
|
|
544
|
+
const coarseTiles = coarseW * coarseH;
|
|
545
|
+
this.isMultiChannel = !!d.multichannel;
|
|
546
|
+
if (this.isMultiChannel) {
|
|
547
|
+
// Per-channel rendering can only use the REAL levels, so OSD requests the
|
|
548
|
+
// coarsest real level's whole tile grid × N channels at fit. When that's large
|
|
549
|
+
// (a whole-slide), it's too many (often slow, pyramid-less) full-res reads —
|
|
550
|
+
// fall back to the single server-composited source (which keeps the fast
|
|
551
|
+
// synthetic overviews). A small single-FOV z-stack stays per-channel. Size in
|
|
552
|
+
// BYTES isn't the signal — tile count is.
|
|
553
|
+
const fitTiles = coarseTiles * Math.max(1, d.channels ?? 1);
|
|
554
|
+
if (fitTiles > this.MAX_MULTICHANNEL_FIT_TILES) {
|
|
555
|
+
this.isMultiChannel = false;
|
|
556
|
+
console.warn('[OSD] multichannel composite too large for per-channel rendering: ' +
|
|
557
|
+
`${coarseW}x${coarseH} tiles x ${d.channels} channels = ${fitTiles} at the coarsest ` +
|
|
558
|
+
`real level (> ${this.MAX_MULTICHANNEL_FIT_TILES}); rendering server-composited for speed.`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
this.cache.clearChannelGroups();
|
|
562
|
+
// Auto-range grayscale tiles to the image's actual intensity span (like the
|
|
563
|
+
// heatmap), sampling the coarsest tile level so the window matches the raw
|
|
564
|
+
// tile values. Fire-and-forget: it re-invalidates once the window is known;
|
|
565
|
+
// tiles paint unwindowed until then. (Multichannel windows are per-channel,
|
|
566
|
+
// defaulting to full range; the user auto/edits each channel.)
|
|
567
|
+
if (this.isMultiChannel) {
|
|
568
|
+
this.sampler.computeMultiChannelHistograms(d, loaded.infoB64, loaded.z);
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
this.sampler.computeImageWindow(d, loaded.infoB64, loaded.z);
|
|
572
|
+
}
|
|
573
|
+
this.destroyViewer();
|
|
574
|
+
// Size the slice cache for this image: LRU cap to hold the whole stack
|
|
575
|
+
// (capped) and skip background preloading for stacks too large to preload
|
|
576
|
+
// (would flood full-res reads). Normal stacks keep the flicker-free pre-cache.
|
|
577
|
+
this.cache.configure(d.z ?? 1, coarseTiles);
|
|
578
|
+
}
|
|
579
|
+
// Simple mode: OSD's single-image source decodes the URL (blob:/data:/http)
|
|
580
|
+
// directly — no pyramid, no server. Tiled mode: the custom server-backed tile
|
|
581
|
+
// source (multichannel opens on channel 0; the open handler then swaps in this
|
|
582
|
+
// slice's per-channel group and the background loader pre-fills the rest).
|
|
583
|
+
const tileSource = simple
|
|
584
|
+
? { type: 'image', url: loaded.url }
|
|
585
|
+
: this.buildTileSource(d, loaded.infoB64, loaded.z, this.isMultiChannel ? 0 : undefined);
|
|
586
|
+
silenceOsdMultiImageAdvisory();
|
|
587
|
+
this.viewer = OSD({
|
|
588
|
+
id: plotDiv,
|
|
589
|
+
// Simple-mode z-scrub calls viewer.open() again per slice (see
|
|
590
|
+
// setZIndex) — without this it resets to the home zoom/pan on every
|
|
591
|
+
// slice change. Tiled mode never re-opens (it toggles pre-added
|
|
592
|
+
// TiledImages' opacity instead), so this is a no-op there.
|
|
593
|
+
preserveViewport: true,
|
|
594
|
+
// Use the 2D canvas drawer, not WebGL: creating/destroying a viewer on
|
|
595
|
+
// each engine toggle / image load churns WebGL contexts (browsers cap
|
|
596
|
+
// how many exist at once), which surfaces as "WebGL context was lost"
|
|
597
|
+
// and blank tiles. The canvas drawer has no such limit.
|
|
598
|
+
drawer: 'canvas',
|
|
599
|
+
showNavigationControl: false, // avoids needing the icon-image assets
|
|
600
|
+
// Overview thumbnail with the current-viewport rectangle, bottom-right.
|
|
601
|
+
showNavigator: this.navigatorVisible,
|
|
602
|
+
// Nearest-neighbour when off → crisp pixels on zoom-in (pixel inspection).
|
|
603
|
+
imageSmoothingEnabled: this.smoothingEnabled,
|
|
604
|
+
navigatorPosition: 'BOTTOM_RIGHT',
|
|
605
|
+
navigatorSizeRatio: 0.16,
|
|
606
|
+
navigatorAutoFade: false,
|
|
607
|
+
navigatorBackground: 'rgba(0,0,0,0.5)',
|
|
608
|
+
loadTilesWithAjax: true,
|
|
609
|
+
ajaxWithCredentials: true, // cookie auth (oauth2-proxy)
|
|
610
|
+
ajaxHeaders: this.authHeaders, // bearer auth (Auth0), when available
|
|
611
|
+
crossOriginPolicy: 'Anonymous',
|
|
612
|
+
// Gentler zoom: the default (1.2) feels fast and big jumps cross several
|
|
613
|
+
// pyramid levels at once, firing a burst of tile requests.
|
|
614
|
+
zoomPerScroll: 1.1,
|
|
615
|
+
// Click-to-zoom toward the clicked point (OpenSeadragon's default demo
|
|
616
|
+
// behaviour). Only applies when no region tool is active — an active tool
|
|
617
|
+
// disables OSD mouse-nav so clicks draw/select instead of zooming.
|
|
618
|
+
gestureSettingsMouse: { clickToZoom: true, scrollToZoom: true },
|
|
619
|
+
// Zoom limits (jit-ui#94). Default minZoomImageRatio (0.9) stops you from
|
|
620
|
+
// zooming the image smaller than ~home; drop it so the image can be shrunk
|
|
621
|
+
// freely (effectively no minimum). Default maxZoomPixelRatio (1.1) caps
|
|
622
|
+
// zoom-in at ~1 screen px per image px; raise it so you can zoom in to
|
|
623
|
+
// inspect individual pixels (paired with imageSmoothingEnabled=false for
|
|
624
|
+
// crisp nearest-neighbour blocks).
|
|
625
|
+
minZoomImageRatio: 0.01,
|
|
626
|
+
maxZoomPixelRatio: 20,
|
|
627
|
+
// Wait for the view to settle before pulling new tiles (less churn).
|
|
628
|
+
immediateRender: false,
|
|
629
|
+
animationTime: 0.4,
|
|
630
|
+
// Release memory aggressively: cap the tile cache so tiles outside the
|
|
631
|
+
// current view (e.g. the coarser levels left behind after zooming in) are
|
|
632
|
+
// evicted instead of accumulating. Large whole-slide tiles — doubled when
|
|
633
|
+
// the colormap pipeline keeps a recolored copy — make an unbounded cache
|
|
634
|
+
// costly. Tunable; lower = less memory, more re-fetch on pan-back.
|
|
635
|
+
// Stacks keep many slices resident (the whole stack is background-loaded),
|
|
636
|
+
// so they get a much larger tile budget — otherwise switching slices evicts
|
|
637
|
+
// each other's tiles and scrubbing back re-fetches. Scaled to the resident
|
|
638
|
+
// slice count; single images keep the lean default.
|
|
639
|
+
maxImageCacheCount: (d.z ?? 1) > 1 ? Math.min(2400, Math.max(600, this.cache.maxSlices() * 40)) : 150,
|
|
640
|
+
});
|
|
641
|
+
return new Promise((resolve) => {
|
|
642
|
+
// Always settle: if OSD never emits open/open-failed (a bad tile source,
|
|
643
|
+
// a viewer torn down mid-open), the render pipeline must not hang — a hang
|
|
644
|
+
// leaves imageLoading=true, sticking the spinner and the 500ms
|
|
645
|
+
// cache-progress poll forever (NS_BINDING_ABORTED storm).
|
|
646
|
+
let settled = false;
|
|
647
|
+
const done = (ok) => {
|
|
648
|
+
if (!settled) {
|
|
649
|
+
settled = true;
|
|
650
|
+
resolve(ok);
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
this.viewer.addOnceHandler('open', () => {
|
|
654
|
+
// Region overlay reads/writes the shared RegionStore, so regions stay
|
|
655
|
+
// in sync with Plotly and the Region Editor. Recreate it per open() so
|
|
656
|
+
// it binds to the freshly-opened viewer's canvas/MouseTracker.
|
|
657
|
+
this.overlay?.destroy();
|
|
658
|
+
this.overlay = new OsdRegionOverlay(this.viewer, this.regionStore);
|
|
659
|
+
this.coordTransform = new OsdCoordinateTransform(this.viewer);
|
|
660
|
+
this.scaleBar = new OsdScaleBar(this.viewer, d.mppX ?? 0);
|
|
661
|
+
this.buildToolHosts();
|
|
662
|
+
// Simple mode is a single, self-contained image — no slice cache to seed.
|
|
663
|
+
if (simple) {
|
|
664
|
+
/* nothing to cache: one frame, no pyramid */
|
|
665
|
+
}
|
|
666
|
+
else if (this.isMultiChannel) {
|
|
667
|
+
// Drop the single opened (channel-0) image and add this slice's channel
|
|
668
|
+
// group to the per-slice cache. The shared background loader / LRU then
|
|
669
|
+
// pre-fills the other slices' channel groups so z-scrub is flicker-free.
|
|
670
|
+
try {
|
|
671
|
+
const it0 = this.viewer.world?.getItemAt?.(0);
|
|
672
|
+
if (it0)
|
|
673
|
+
this.viewer.world.removeItem(it0);
|
|
674
|
+
}
|
|
675
|
+
catch { /* nothing to remove */ }
|
|
676
|
+
this.cache.addChannelSlice(this.currentZ);
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
// Seed the slice cache with the just-opened slice (world item 0), so
|
|
680
|
+
// scrubbing back to it later is an instant opacity toggle, not a re-open.
|
|
681
|
+
const firstItem = this.viewer.world?.getItemAt?.(0);
|
|
682
|
+
if (firstItem)
|
|
683
|
+
this.cache.seedComposite(this.currentZ, firstItem);
|
|
684
|
+
}
|
|
685
|
+
// The wand samples the *rendered viewport*, so its pixel matrix is only
|
|
686
|
+
// valid for the current view — drop it whenever the viewport changes.
|
|
687
|
+
this.viewer.addHandler('viewport-change', () => {
|
|
688
|
+
this.viewportPixels = null;
|
|
689
|
+
});
|
|
690
|
+
// Grayscale tiles get the active colormap via the pixel pipeline. The
|
|
691
|
+
// handler runs per tile on load (and on requestInvalidate); recoloring
|
|
692
|
+
// is a no-op for RGB images or before the LUT resolves.
|
|
693
|
+
// (isGrayscaleImage is set from imageInfo.isGrayscale in plot() above.)
|
|
694
|
+
this.viewer.addHandler('tile-invalidated', (event) => this.recolorTile(event));
|
|
695
|
+
// The overview navigator is a separate mini-viewer with its own tiles,
|
|
696
|
+
// so it doesn't receive the main viewer's tile-invalidated events. Apply
|
|
697
|
+
// the same recolor pipeline to it so the minimap tracks the main image's
|
|
698
|
+
// colormap/LUT instead of staying grayscale.
|
|
699
|
+
const nav = this.viewer.navigator;
|
|
700
|
+
nav?.addHandler('tile-invalidated', (event) => this.recolorTile(event));
|
|
701
|
+
if ((this.isGrayscaleImage && this.colorLut) || this.isMultiChannel) {
|
|
702
|
+
try {
|
|
703
|
+
this.viewer.world.requestInvalidate(true);
|
|
704
|
+
}
|
|
705
|
+
catch {
|
|
706
|
+
/* no-op */
|
|
707
|
+
}
|
|
708
|
+
try {
|
|
709
|
+
nav?.world?.requestInvalidate(true);
|
|
710
|
+
}
|
|
711
|
+
catch {
|
|
712
|
+
/* no-op */
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
// Prefetch adjacent z-slices once the view settles (and on each settle,
|
|
716
|
+
// so it tracks the current viewport as the user pans/zooms). Simple mode
|
|
717
|
+
// has a single frame, so there's nothing to prefetch.
|
|
718
|
+
if (!simple)
|
|
719
|
+
this.viewer.addHandler('animation-finish', () => this.cache.schedulePrefetch());
|
|
720
|
+
// Tell listeners (the intensity inset) the visible image region whenever
|
|
721
|
+
// the view settles, so they can re-sample at the current zoom resolution.
|
|
722
|
+
this.viewer.addHandler('animation-finish', () => this.emitViewportChange());
|
|
723
|
+
// Chrome compositor bug: after an OSD zoom the docked toolbar (a sibling of
|
|
724
|
+
// #plot in the <visualizer> host) is left laid-out-but-unpainted and
|
|
725
|
+
// vanishes — confirmed via DevTools (DOM intact, region simply not painted).
|
|
726
|
+
// CSS (z-index / isolation / contain) and DOM-reparenting don't fix it; a
|
|
727
|
+
// repaint reliably does. Nudge it each animation frame (so it never visibly
|
|
728
|
+
// drops) and on settle. The synchronous display toggle re-rasters with no
|
|
729
|
+
// visible gone-frame and no layout shift.
|
|
730
|
+
this.viewer.addHandler('animation', () => this.nudgeToolbarRepaint());
|
|
731
|
+
this.viewer.addHandler('animation-finish', () => this.nudgeToolbarRepaint());
|
|
732
|
+
if (!simple)
|
|
733
|
+
this.cache.schedulePrefetch();
|
|
734
|
+
// Force fit-to-view as the split/flex layout settles. A tall
|
|
735
|
+
// non-pyramidal image can otherwise open zoomed-in (image width filling
|
|
736
|
+
// the viewer), making OSD demand slow full-res res=0 tiles for the
|
|
737
|
+
// centre instead of the fast coarse overview → white canvas. goHome
|
|
738
|
+
// fits the whole image so OSD selects the coarse synthetic level. Retry
|
|
739
|
+
// across a few frames because the container may not have its final size
|
|
740
|
+
// on the first frame after 'open'.
|
|
741
|
+
const refit = () => {
|
|
742
|
+
try {
|
|
743
|
+
this.viewer?.viewport.goHome(true);
|
|
744
|
+
// The navigator was sized in the Viewer constructor — BEFORE the
|
|
745
|
+
// layout settled — so its element can carry a stale (even
|
|
746
|
+
// wrong-aspect) size that floats the visible minimap above the
|
|
747
|
+
// corner. Re-size it from the settled container.
|
|
748
|
+
this.resizeNavigator();
|
|
749
|
+
}
|
|
750
|
+
catch {
|
|
751
|
+
/* torn down */
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
requestAnimationFrame(refit);
|
|
755
|
+
setTimeout(refit, 150);
|
|
756
|
+
setTimeout(refit, 400);
|
|
757
|
+
// The timed retries above miss the case where the container is STILL
|
|
758
|
+
// zero-size at 400ms — which happens when the render starts while the
|
|
759
|
+
// file viewer is mid-switch from the folder view to the diagram (e.g.
|
|
760
|
+
// "Load as Stack" from a folder, with no image open first). Because
|
|
761
|
+
// preserveViewport suppresses OSD's own open-time fit, the image would
|
|
762
|
+
// then be left un-fitted at the top-left ("a tile"). A one-shot
|
|
763
|
+
// ResizeObserver fits the instant the container first has a real size,
|
|
764
|
+
// regardless of when the render began (jit-ui#106).
|
|
765
|
+
this.fitWhenContainerSized(document.getElementById(plotDiv), refit);
|
|
766
|
+
// Keep the navigator sized to the container as the panel resizes.
|
|
767
|
+
this.viewer.addHandler('resize', () => this.resizeNavigator());
|
|
768
|
+
done(true);
|
|
769
|
+
});
|
|
770
|
+
this.viewer.addOnceHandler('open-failed', (e) => {
|
|
771
|
+
console.warn('[OSD] open-failed', e?.message ?? e);
|
|
772
|
+
done(false);
|
|
773
|
+
});
|
|
774
|
+
setTimeout(() => {
|
|
775
|
+
if (!settled)
|
|
776
|
+
console.warn('[OSD] viewer open timed out');
|
|
777
|
+
done(false);
|
|
778
|
+
}, 8000);
|
|
779
|
+
this.viewer.open(tileSource);
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Fit-to-home once `el` first has a non-zero size. If it's already sized we
|
|
784
|
+
* rely on the timed refits the caller scheduled; otherwise a one-shot
|
|
785
|
+
* ResizeObserver runs `refit` the moment the container is laid out (then
|
|
786
|
+
* disconnects), so the initial fit doesn't depend on the render's start time
|
|
787
|
+
* relative to the diagram container's layout. No-op without a container or
|
|
788
|
+
* ResizeObserver (the timed refits remain the fallback). One-shot + only
|
|
789
|
+
* attached on a fresh mount, so slice-scrub re-opens keep the user's zoom.
|
|
790
|
+
*/
|
|
791
|
+
fitWhenContainerSized(el, refit) {
|
|
792
|
+
if (!el || typeof ResizeObserver === 'undefined')
|
|
793
|
+
return;
|
|
794
|
+
if (el.clientWidth > 0 && el.clientHeight > 0)
|
|
795
|
+
return; // already sized — timed refits suffice
|
|
796
|
+
const ro = new ResizeObserver(() => {
|
|
797
|
+
if (el.clientWidth > 0 && el.clientHeight > 0) {
|
|
798
|
+
ro.disconnect();
|
|
799
|
+
refit();
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
ro.observe(el);
|
|
803
|
+
// Safety net: stop observing even if it never gains a size.
|
|
804
|
+
setTimeout(() => ro.disconnect(), 5000);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Custom tile source built from the descriptor. OSD numbers levels
|
|
808
|
+
* coarsest-first; the backend numbers resolutions full-res-first, so
|
|
809
|
+
* `res = (levels-1) - osdLevel`. Overriding getLevelScale/getNumTiles lets us
|
|
810
|
+
* honour Bio-Formats' actual per-level dimensions (not assume power-of-two).
|
|
811
|
+
*/
|
|
812
|
+
buildTileSource(d, infoB64, z, channel) {
|
|
813
|
+
// Multichannel images composite from per-channel tiles, which only exist at
|
|
814
|
+
// real Bio-Formats resolutions — so drive OSD off the real levels alone and
|
|
815
|
+
// skip the synthetic (server-composited) overviews. Every displayed tile is
|
|
816
|
+
// then per-channel-fetchable at any zoom.
|
|
817
|
+
const levels = this.isMultiChannel ? d.levels.slice(0, this.realLevels) : d.levels;
|
|
818
|
+
const n = levels.length;
|
|
819
|
+
const t = d.tileSize;
|
|
820
|
+
const base = this.api;
|
|
821
|
+
// OSD level i (0 = coarsest) <-> backend resolution (n-1-i) (res 0 = full).
|
|
822
|
+
// The backend pyramid is NOT necessarily power-of-two, so we drive OSD off
|
|
823
|
+
// the backend's real per-level dimensions. We override ONLY getLevelScale:
|
|
824
|
+
// OSD derives getNumTiles, getTileBounds AND its per-zoom level selection
|
|
825
|
+
// from getLevelScale, so they all stay consistent — requesting exactly the
|
|
826
|
+
// tiles each resolution actually has (no out-of-range 400s, no flood).
|
|
827
|
+
const resForLevel = (level) => n - 1 - level;
|
|
828
|
+
const ts = new OSD.TileSource({
|
|
829
|
+
width: d.width,
|
|
830
|
+
height: d.height,
|
|
831
|
+
tileSize: t,
|
|
832
|
+
tileOverlap: 0,
|
|
833
|
+
minLevel: 0,
|
|
834
|
+
maxLevel: n - 1,
|
|
835
|
+
});
|
|
836
|
+
ts.getLevelScale = (level) => {
|
|
837
|
+
const lvl = levels[resForLevel(level)];
|
|
838
|
+
return lvl ? lvl.width / d.width : 1;
|
|
839
|
+
};
|
|
840
|
+
// Drive the tile COUNT off each level's own (independently-rounded) dimensions,
|
|
841
|
+
// not OSD's default `ceil(scale * fullDimension / tileSize)`. Synthetic AND real
|
|
842
|
+
// Bio-Formats levels aren't exact proportional scales of the full image, so
|
|
843
|
+
// `scale * fullHeight` can round up to one more row (or column) than the level
|
|
844
|
+
// actually has — OSD then requests an out-of-range tile that the server 400s
|
|
845
|
+
// ("Tile (col,row) out of range"). Using the level's real w/h matches the
|
|
846
|
+
// server's own bounds check exactly.
|
|
847
|
+
const Point = OSD.Point;
|
|
848
|
+
ts.getNumTiles = (level) => {
|
|
849
|
+
const lvl = levels[resForLevel(level)];
|
|
850
|
+
if (!lvl)
|
|
851
|
+
return new Point(0, 0);
|
|
852
|
+
return new Point(Math.ceil(lvl.width / t), Math.ceil(lvl.height / t));
|
|
853
|
+
};
|
|
854
|
+
ts.getTileUrl = (level, x, y) => buildTileUrl(base, infoB64, { res: resForLevel(level), col: x, row: y, z, tileSize: t, channel });
|
|
855
|
+
return ts;
|
|
856
|
+
}
|
|
857
|
+
/** Re-apply the display pipeline (window/gamma/colour/invert) after a state change.
|
|
858
|
+
* Multichannel invalidates ONLY the visible slice's channel images — invalidating
|
|
859
|
+
* the whole world would re-process every hidden/preloaded slice's tiles (hundreds),
|
|
860
|
+
* flooding OSD with "[CacheRecord] … InvalidStateError" and wasting work on tiles
|
|
861
|
+
* that aren't on screen. The other cached slices are marked stale and re-tinted
|
|
862
|
+
* lazily when revealed. Composite/grayscale invalidate the world as before. */
|
|
863
|
+
invalidateDisplay() {
|
|
864
|
+
const v = this.viewer;
|
|
865
|
+
if (!v)
|
|
866
|
+
return;
|
|
867
|
+
if (this.isMultiChannel) {
|
|
868
|
+
this.cache.invalidateChannelDisplay(this.currentZ);
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
try {
|
|
872
|
+
v.world.requestInvalidate(true);
|
|
873
|
+
}
|
|
874
|
+
catch { /* no-op */ }
|
|
875
|
+
try {
|
|
876
|
+
v.navigator?.world?.requestInvalidate(true);
|
|
877
|
+
}
|
|
878
|
+
catch { /* no-op */ }
|
|
879
|
+
}
|
|
880
|
+
destroyViewer() {
|
|
881
|
+
this.cache.cancelBackgroundLoad();
|
|
882
|
+
// Tear down any active tool overlays (shared singletons).
|
|
883
|
+
this.wandTool.setMode(false);
|
|
884
|
+
this.brushTool.setMode(false);
|
|
885
|
+
this.samPointTool.setMode(false);
|
|
886
|
+
this.eraserTool.setMode(false);
|
|
887
|
+
if (this.overlay) {
|
|
888
|
+
this.overlay.destroy();
|
|
889
|
+
this.overlay = null;
|
|
890
|
+
}
|
|
891
|
+
if (this.scaleBar) {
|
|
892
|
+
this.scaleBar.destroy();
|
|
893
|
+
this.scaleBar = null;
|
|
894
|
+
}
|
|
895
|
+
if (this.viewer) {
|
|
896
|
+
this.viewer.destroy();
|
|
897
|
+
this.viewer = null;
|
|
898
|
+
}
|
|
899
|
+
this.coordTransform = null;
|
|
900
|
+
this.cache.reset();
|
|
901
|
+
this.sampler.clear();
|
|
902
|
+
}
|
|
903
|
+
/** This backend's region renderer (the SVG overlay), once a plot is mounted. */
|
|
904
|
+
getRegionOverlay() {
|
|
905
|
+
return this.overlay;
|
|
906
|
+
}
|
|
907
|
+
/** OSD renders only the image type — no isosurface, so no controls. */
|
|
908
|
+
getIsosurfaceControls() {
|
|
909
|
+
return null;
|
|
910
|
+
}
|
|
911
|
+
/** OSD renders only the image type — no 3D scenes, so no controls. */
|
|
912
|
+
getSurface3dControls() {
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
/** OSD renders only the image type — no LINE intensity ROIs, so no controls. */
|
|
916
|
+
getIntensityControls() {
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
toOverlayMode(mode) {
|
|
920
|
+
switch (mode) {
|
|
921
|
+
case 'drawrect':
|
|
922
|
+
return 'drawrect';
|
|
923
|
+
case 'drawclosedpath':
|
|
924
|
+
return 'drawclosedpath';
|
|
925
|
+
case 'drawopenpath':
|
|
926
|
+
return 'drawopenpath';
|
|
927
|
+
case 'drawpolygon':
|
|
928
|
+
return 'drawpolygon';
|
|
929
|
+
case 'addpoint':
|
|
930
|
+
return 'addpoint';
|
|
931
|
+
case 'deletepoint':
|
|
932
|
+
return 'deletepoint';
|
|
933
|
+
case 'move':
|
|
934
|
+
return 'move';
|
|
935
|
+
case 'select':
|
|
936
|
+
return 'select';
|
|
937
|
+
default:
|
|
938
|
+
return 'none';
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
reloadAndPlot() {
|
|
942
|
+
/* host re-drives plot() via the image-info stream */
|
|
943
|
+
}
|
|
944
|
+
reset() {
|
|
945
|
+
this.destroyViewer();
|
|
946
|
+
}
|
|
947
|
+
relayout(_trueImageSize) {
|
|
948
|
+
const vp = this.viewer?.viewport;
|
|
949
|
+
if (!vp)
|
|
950
|
+
return;
|
|
951
|
+
// Keep the user's current view across a container/split resize instead of
|
|
952
|
+
// snapping home. Viewport bounds are image-relative (image width = 1), so
|
|
953
|
+
// they're independent of the container's pixel size — capture the visible
|
|
954
|
+
// region now and re-fit it once the new size has settled. OSD's autoResize
|
|
955
|
+
// fires asynchronously and the angular-split transition animates the width
|
|
956
|
+
// over a few hundred ms, so restore on the next frame and again after the
|
|
957
|
+
// transition completes.
|
|
958
|
+
const bounds = vp.getBounds(true);
|
|
959
|
+
const restore = () => {
|
|
960
|
+
try {
|
|
961
|
+
this.viewer?.viewport.fitBounds(bounds, true);
|
|
962
|
+
this.viewer?.viewport.applyConstraints(true);
|
|
963
|
+
}
|
|
964
|
+
catch {
|
|
965
|
+
/* viewer torn down */
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
requestAnimationFrame(restore);
|
|
969
|
+
setTimeout(restore, 350);
|
|
970
|
+
}
|
|
971
|
+
resetAxes() {
|
|
972
|
+
this.viewer?.viewport.goHome();
|
|
973
|
+
}
|
|
974
|
+
autoscale() {
|
|
975
|
+
this.viewer?.viewport.goHome();
|
|
976
|
+
this.autoscaleEvent$.next('autoscale');
|
|
977
|
+
}
|
|
978
|
+
zoomIn() {
|
|
979
|
+
this.viewer?.viewport.zoomBy(1.3);
|
|
980
|
+
this.viewer?.viewport.applyConstraints();
|
|
981
|
+
}
|
|
982
|
+
zoomOut() {
|
|
983
|
+
this.viewer?.viewport.zoomBy(1 / 1.3);
|
|
984
|
+
this.viewer?.viewport.applyConstraints();
|
|
985
|
+
}
|
|
986
|
+
setDragMode(mode) {
|
|
987
|
+
// Rectangle/polygon drawing run on the SVG overlay; wand/eraser/zoom-box
|
|
988
|
+
// (custom Plotly overlays) are routed to Plotly by the router and aren't
|
|
989
|
+
// handled here yet.
|
|
990
|
+
this.overlay?.setMode(this.toOverlayMode(mode));
|
|
991
|
+
}
|
|
992
|
+
setShowStack(_showstack) {
|
|
993
|
+
/* OSD navigates the stack via the z-slider -> setZIndex */
|
|
994
|
+
}
|
|
995
|
+
/** Show/hide the overview navigator. Stored for the next viewer creation, and
|
|
996
|
+
* applied live when a viewer is already mounted (hides/reveals its element —
|
|
997
|
+
* the navigator instance itself only exists when created with showNavigator). */
|
|
998
|
+
setNavigatorVisible(visible) {
|
|
999
|
+
this.navigatorVisible = visible;
|
|
1000
|
+
const navEl = this.viewer?.navigator?.element;
|
|
1001
|
+
if (navEl)
|
|
1002
|
+
navEl.style.display = visible ? '' : 'none';
|
|
1003
|
+
}
|
|
1004
|
+
/** Toggle bilinear smoothing vs nearest-neighbour (crisp pixels). Stored for the
|
|
1005
|
+
* next viewer creation, and applied live (with a redraw) to a mounted viewer. */
|
|
1006
|
+
setImageSmoothingEnabled(enabled) {
|
|
1007
|
+
this.smoothingEnabled = enabled;
|
|
1008
|
+
const drawer = this.viewer?.drawer;
|
|
1009
|
+
if (drawer?.setImageSmoothingEnabled) {
|
|
1010
|
+
drawer.setImageSmoothingEnabled(enabled);
|
|
1011
|
+
this.viewer?.forceRedraw();
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Swap the displayed z-slice live. Slices are kept as separate tiled images in
|
|
1016
|
+
* the world, so switching is just an opacity toggle: a previously-visited slice
|
|
1017
|
+
* shows instantly (its decoded + recolored tiles are still resident), and a
|
|
1018
|
+
* never-seen slice is added once. The region overlay, coordinate transform,
|
|
1019
|
+
* scale bar, colormap pipeline and current zoom/pan all persist — the x/y
|
|
1020
|
+
* geometry is identical across slices.
|
|
1021
|
+
*
|
|
1022
|
+
* Simple mode (tiled:false — a numbered image series assembled client-side,
|
|
1023
|
+
* each slice its own complete image with no shared tile server) has no
|
|
1024
|
+
* pyramid to toggle opacity on, so it re-opens the viewer on the new
|
|
1025
|
+
* slice's URL instead. preserveViewport (viewer option, see plot()) keeps
|
|
1026
|
+
* the current zoom/pan across that reopen.
|
|
1027
|
+
*/
|
|
1028
|
+
setZIndex(zIndex) {
|
|
1029
|
+
const z = zIndex || 0;
|
|
1030
|
+
if (z === this.currentZ)
|
|
1031
|
+
return;
|
|
1032
|
+
if (!this.viewer)
|
|
1033
|
+
return;
|
|
1034
|
+
if (this.simpleMode) {
|
|
1035
|
+
const rawUrl = this.simpleUrls[z] ?? this.simpleUrls[0];
|
|
1036
|
+
if (!rawUrl)
|
|
1037
|
+
return;
|
|
1038
|
+
this.currentZ = z;
|
|
1039
|
+
this.viewportPixels = null;
|
|
1040
|
+
// Fetched via SimpleSliceAccessService (auth interceptor applies) then
|
|
1041
|
+
// upscaled to full-res (same as the initial load — see toFullResUrl) so
|
|
1042
|
+
// the world stays full-res across slices and ROIs keep aligning. Guard
|
|
1043
|
+
// against a newer scrub landing first.
|
|
1044
|
+
this.simpleStack.fetchAsBlobUrl(rawUrl)
|
|
1045
|
+
.then((previewUrl) => this.toFullResUrl(previewUrl, this.descriptor?.width ?? 0, this.descriptor?.height ?? 0))
|
|
1046
|
+
.then((url) => {
|
|
1047
|
+
if (this.currentZ !== z || !this.viewer)
|
|
1048
|
+
return;
|
|
1049
|
+
this.viewer.addOnceHandler('open-failed', (e) => {
|
|
1050
|
+
console.warn('[OSD] slice re-open failed', e?.message ?? e);
|
|
1051
|
+
});
|
|
1052
|
+
this.viewer.open({ type: 'image', url });
|
|
1053
|
+
})
|
|
1054
|
+
.catch((err) => console.warn('[OSD] slice fetch failed', err));
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
if (!this.descriptor || !this.infoB64)
|
|
1058
|
+
return;
|
|
1059
|
+
this.currentZ = z;
|
|
1060
|
+
this.viewportPixels = null; // wand readback is slice-specific
|
|
1061
|
+
this.cache.showSlice(z);
|
|
1062
|
+
if (this.isMultiChannel) {
|
|
1063
|
+
this.sampler.computeMultiChannelHistograms(this.descriptor, this.infoB64, z);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
setStackLoading(stackLoading) {
|
|
1067
|
+
this.stackLoading$.next(stackLoading);
|
|
1068
|
+
}
|
|
1069
|
+
isStackLoading() {
|
|
1070
|
+
return this.stackLoading$.asObservable();
|
|
1071
|
+
}
|
|
1072
|
+
getStackLoadingProgress() {
|
|
1073
|
+
return this.stackLoadingProgress$.asObservable();
|
|
1074
|
+
}
|
|
1075
|
+
getTrueImageSize() {
|
|
1076
|
+
return this.descriptor ? { width: this.descriptor.width, height: this.descriptor.height } : null;
|
|
1077
|
+
}
|
|
1078
|
+
getCurrentImage() {
|
|
1079
|
+
return Promise.resolve(null);
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* The currently displayed pixels — the rendered viewport (what the user sees,
|
|
1083
|
+
* including the current zoom and any colormap recolor), as RGBA. Feeds the
|
|
1084
|
+
* processing-pipeline dialog's "use current view". Returns null until tiles
|
|
1085
|
+
* are drawn.
|
|
1086
|
+
*/
|
|
1087
|
+
getDisplayedPixelData() {
|
|
1088
|
+
const canvas = this.viewer?.drawer?.canvas;
|
|
1089
|
+
if (!canvas || !canvas.width || !canvas.height)
|
|
1090
|
+
return null;
|
|
1091
|
+
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
|
1092
|
+
if (!ctx)
|
|
1093
|
+
return null;
|
|
1094
|
+
const { width, height } = canvas;
|
|
1095
|
+
const img = ctx.getImageData(0, 0, width, height);
|
|
1096
|
+
return { width, height, channels: 4, data: img.data };
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Image-pixel rectangle the drawer canvas (what {@link getDisplayedPixelData}
|
|
1100
|
+
* reads) currently covers. Unlike {@link emitViewportChange}, this is NOT
|
|
1101
|
+
* clamped to the image bounds: the canvas maps 1:1 to the viewport rectangle,
|
|
1102
|
+
* so leaving it unclamped keeps `canvasPx -> imagePx` an exact affine map
|
|
1103
|
+
* (clamping would skew coordinates near the image edges). Routed through world
|
|
1104
|
+
* item 0 (see {@link viewportRectToImage}) for multi-layer accuracy.
|
|
1105
|
+
*/
|
|
1106
|
+
getDisplayedSourceRect() {
|
|
1107
|
+
const vp = this.viewer?.viewport;
|
|
1108
|
+
if (!vp || !this.descriptor)
|
|
1109
|
+
return null;
|
|
1110
|
+
try {
|
|
1111
|
+
const r = viewportRectToImage(this.viewer, vp.getBounds(true));
|
|
1112
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
1113
|
+
}
|
|
1114
|
+
catch {
|
|
1115
|
+
return null;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
downloadImage() {
|
|
1119
|
+
// Snapshot the currently rendered OSD view as a PNG (WYSIWYG) — the parallel
|
|
1120
|
+
// to Plotly's downloadImage. OSD uses the 2D canvas drawer and bakes the
|
|
1121
|
+
// active display settings (window / gamma / colormap / per-channel colours /
|
|
1122
|
+
// invert) into the tiles via the recolor pipeline, so the drawer canvas
|
|
1123
|
+
// already reflects exactly what's on screen at the current zoom/pan. The
|
|
1124
|
+
// full-resolution stitched export lives in exportComposite() (Channels &
|
|
1125
|
+
// Histogram dialog).
|
|
1126
|
+
const canvas = this.viewer?.drawer?.canvas;
|
|
1127
|
+
if (!canvas || !canvas.width || !canvas.height)
|
|
1128
|
+
return;
|
|
1129
|
+
const stem = (this.currentFileName || 'image').replace(/\.[^.]+$/, '');
|
|
1130
|
+
canvas.toBlob((blob) => {
|
|
1131
|
+
if (blob)
|
|
1132
|
+
saveAs(blob, `${stem}.png`);
|
|
1133
|
+
}, 'image/png');
|
|
1134
|
+
}
|
|
1135
|
+
setPlotType(_plotType) {
|
|
1136
|
+
/* OSD only renders the image type */
|
|
1137
|
+
}
|
|
1138
|
+
setSurfaceDragMode(_mode) {
|
|
1139
|
+
/* 3D not supported by OSD */
|
|
1140
|
+
}
|
|
1141
|
+
resetSurfaceCamera() {
|
|
1142
|
+
/* 3D not supported by OSD */
|
|
1143
|
+
}
|
|
1144
|
+
getAutoscaleEvent() {
|
|
1145
|
+
return this.autoscaleEvent$.asObservable();
|
|
1146
|
+
}
|
|
1147
|
+
/** Visible image region (full-image pixel coords), emitted when the view
|
|
1148
|
+
* settles. The intensity inset re-samples this region at the zoom resolution. */
|
|
1149
|
+
getViewportChange$() {
|
|
1150
|
+
return this.viewportChange$.asObservable();
|
|
1151
|
+
}
|
|
1152
|
+
/** {@link IIntensitySampling} stub — intensity sampling lives in the Plotly
|
|
1153
|
+
* backend (it owns pixel readback). OSD feeds it only via getViewportChange$;
|
|
1154
|
+
* the sampling-cache priming itself is a no-op here. */
|
|
1155
|
+
async ensureIntensitySampling(_imageInfo, _zIndex) {
|
|
1156
|
+
/* no-op: Plotly owns intensity sampling (see IIntensitySampling) */
|
|
1157
|
+
}
|
|
1158
|
+
/** {@link IIntensitySampling} stub — see {@link ensureIntensitySampling}. */
|
|
1159
|
+
refreshIntensitySamplingForRoi(_x, _y, _width, _height, _zIndex) {
|
|
1160
|
+
/* no-op: Plotly owns intensity sampling (see IIntensitySampling) */
|
|
1161
|
+
}
|
|
1162
|
+
/** Compute the current viewport's image-pixel rectangle (clamped to the image)
|
|
1163
|
+
* and broadcast it. */
|
|
1164
|
+
emitViewportChange() {
|
|
1165
|
+
const vp = this.viewer?.viewport;
|
|
1166
|
+
if (!vp || !this.descriptor)
|
|
1167
|
+
return;
|
|
1168
|
+
try {
|
|
1169
|
+
// Route through world item 0 (osd-coords): vp.viewportToImageRectangle is
|
|
1170
|
+
// inaccurate and warns when the world holds multiple images (per-channel
|
|
1171
|
+
// multichannel layers), which fed the intensity inset a wrong ROI.
|
|
1172
|
+
const r = viewportRectToImage(this.viewer, vp.getBounds(true));
|
|
1173
|
+
const iw = this.descriptor.width, ih = this.descriptor.height;
|
|
1174
|
+
const x = Math.max(0, Math.min(iw, r.x));
|
|
1175
|
+
const y = Math.max(0, Math.min(ih, r.y));
|
|
1176
|
+
const width = Math.max(1, Math.min(iw - x, r.width));
|
|
1177
|
+
const height = Math.max(1, Math.min(ih - y, r.height));
|
|
1178
|
+
this.viewportChange$.next({ x, y, width, height });
|
|
1179
|
+
}
|
|
1180
|
+
catch {
|
|
1181
|
+
/* viewport not ready */
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
/** OSD targets the image display; the scalar/3D plot types belong to Plotly. */
|
|
1185
|
+
getPlotTypeDescriptors() {
|
|
1186
|
+
return [PLOT_TYPE_DESCRIPTORS[PlotType.HEATMAP]];
|
|
1187
|
+
}
|
|
1188
|
+
getIntensityProfile$() {
|
|
1189
|
+
return this.intensityProfile$.asObservable();
|
|
1190
|
+
}
|
|
1191
|
+
/** OSD only renders the image type; the LINE intensity inset is Plotly-only. */
|
|
1192
|
+
renderIntensityInset(_divId, _profiles) {
|
|
1193
|
+
/* no LINE mode on OSD */
|
|
1194
|
+
}
|
|
1195
|
+
// ── IRegionStore + classification colours ────────────────────────────
|
|
1196
|
+
// Inherited from BaseStoreVisualizer — pure delegations to the shared
|
|
1197
|
+
// RegionStore / VisualizerStore (identical to napari-js). The OSD region
|
|
1198
|
+
// overlay (constructed in plot()) reads/writes that same store, so regions
|
|
1199
|
+
// stay in sync with Plotly and the Region Editor; OSD renders them as an SVG
|
|
1200
|
+
// overlay rather than carrying Plotly shape dicts.
|
|
1201
|
+
// ── IToolController ──────────────────────────────────────────────────
|
|
1202
|
+
// Wand + vertex eraser run over OSD via ICoordinateTransform. They share the
|
|
1203
|
+
// singleton tool services with Plotly, so we re-bind to our hosts on activate.
|
|
1204
|
+
setWandMode(active, options) {
|
|
1205
|
+
if (!active) {
|
|
1206
|
+
this.wandTool.setMode(false);
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
// Take the pointer from OSD so the drag draws instead of panning. (Nav is
|
|
1210
|
+
// re-enabled when the region overlay returns to 'none' on tool switch.)
|
|
1211
|
+
this.viewer?.setMouseNavEnabled(false);
|
|
1212
|
+
// The wand samples the rendered viewport — read back lazily on first use.
|
|
1213
|
+
this.viewportPixels = null;
|
|
1214
|
+
this.wandTool.bindHost(this.wandHost);
|
|
1215
|
+
this.wandTool.setMode(true, (options ?? {}));
|
|
1216
|
+
}
|
|
1217
|
+
setWandOptions(options) {
|
|
1218
|
+
this.wandTool.setOptions(options);
|
|
1219
|
+
}
|
|
1220
|
+
clearActiveWandRegion() {
|
|
1221
|
+
this.wandTool.clearActiveRegion();
|
|
1222
|
+
}
|
|
1223
|
+
setBrushMode(active, options) {
|
|
1224
|
+
if (!active) {
|
|
1225
|
+
this.brushTool.setMode(false);
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
// Take the pointer from OSD so the drag paints instead of panning.
|
|
1229
|
+
this.viewer?.setMouseNavEnabled(false);
|
|
1230
|
+
// The brush works in the rendered-viewport coordinate frame — read it back
|
|
1231
|
+
// lazily on first use, like the wand. It shares the wand's host.
|
|
1232
|
+
this.viewportPixels = null;
|
|
1233
|
+
this.brushTool.bindHost(this.wandHost);
|
|
1234
|
+
this.brushTool.setMode(true, (options ?? {}));
|
|
1235
|
+
}
|
|
1236
|
+
setBrushOptions(options) {
|
|
1237
|
+
this.brushTool.setSize(options?.size ?? 0);
|
|
1238
|
+
}
|
|
1239
|
+
setVertexEraserMode(active) {
|
|
1240
|
+
if (active) {
|
|
1241
|
+
this.viewer?.setMouseNavEnabled(false); // tool takes the pointer
|
|
1242
|
+
this.viewportPixels = null;
|
|
1243
|
+
this.eraserTool.bindHost(this.eraserHost);
|
|
1244
|
+
}
|
|
1245
|
+
this.eraserTool.setMode(active);
|
|
1246
|
+
}
|
|
1247
|
+
setVertexEraserRadius(radius) {
|
|
1248
|
+
this.eraserTool.setRadius(radius);
|
|
1249
|
+
}
|
|
1250
|
+
setZoomToBoxMode(active) {
|
|
1251
|
+
if (active) {
|
|
1252
|
+
this.viewer?.setMouseNavEnabled(false); // the box drag must not pan
|
|
1253
|
+
this.zoomToBoxTool.bindHost(this.zoomBoxHost());
|
|
1254
|
+
}
|
|
1255
|
+
this.zoomToBoxTool.setMode(active);
|
|
1256
|
+
if (!active)
|
|
1257
|
+
this.viewer?.setMouseNavEnabled(true);
|
|
1258
|
+
}
|
|
1259
|
+
/** Box-prompted SAM: segment the drawn rectangles. The SAM tool reuses the
|
|
1260
|
+
* wand host (viewport readback + coordinate transform + region store). */
|
|
1261
|
+
segmentRectangles() {
|
|
1262
|
+
this.viewportPixels = null; // segment against the current viewport readback
|
|
1263
|
+
this.samTool.bindHost(this.wandHost);
|
|
1264
|
+
return this.samTool.segmentBoxes();
|
|
1265
|
+
}
|
|
1266
|
+
segmentRectanglesCellpose() {
|
|
1267
|
+
if (!this.cellSegmenter)
|
|
1268
|
+
return Promise.resolve(0);
|
|
1269
|
+
this.viewportPixels = null; // crop against the current viewport readback
|
|
1270
|
+
this.cellSegmentTool.bindHost(this.wandHost);
|
|
1271
|
+
return this.cellSegmentTool.segmentBoxes(this.cellSegmenter);
|
|
1272
|
+
}
|
|
1273
|
+
setSamModel(id) {
|
|
1274
|
+
this.samTool.setModel(id);
|
|
1275
|
+
this.samPointTool.setModel(id);
|
|
1276
|
+
}
|
|
1277
|
+
setSamPointMode(active) {
|
|
1278
|
+
if (active) {
|
|
1279
|
+
this.viewer?.setMouseNavEnabled(false); // clicks add points, don't pan
|
|
1280
|
+
this.viewportPixels = null;
|
|
1281
|
+
this.samPointTool.bindHost(this.wandHost);
|
|
1282
|
+
}
|
|
1283
|
+
this.samPointTool.setMode(active);
|
|
1284
|
+
}
|
|
1285
|
+
commitSamPoints() { this.samPointTool.commit(); }
|
|
1286
|
+
clearSamPoints() { this.samPointTool.clear(); }
|
|
1287
|
+
/** Build the wand/eraser host objects bound to this backend. Both tools run
|
|
1288
|
+
* over OSD via the shared coordinate transform + a viewport pixel readback,
|
|
1289
|
+
* and read/write regions directly on the shared RegionStore (neutral model). */
|
|
1290
|
+
buildToolHosts() {
|
|
1291
|
+
this.wandHost = {
|
|
1292
|
+
getRegions: () => this.regionStore.getRegions(),
|
|
1293
|
+
setRegions: (regions) => this.regionStore.setRegions(regions),
|
|
1294
|
+
getCachedImageData: () => this.readbackViewport(),
|
|
1295
|
+
getActiveFrameIndex: () => this.currentZ,
|
|
1296
|
+
getOverlayContainer: () => this.getOverlayContainer(),
|
|
1297
|
+
getCoordinateTransform: () => this.coordTransform,
|
|
1298
|
+
getFileName: () => this.currentFileName,
|
|
1299
|
+
getShapeColor: () => this.regionStore.getShapeColor(),
|
|
1300
|
+
};
|
|
1301
|
+
this.eraserHost = {
|
|
1302
|
+
getRegions: () => this.regionStore.getRegions(),
|
|
1303
|
+
setRegions: (regions) => this.regionStore.setRegions(regions),
|
|
1304
|
+
invalidateWandRegion: () => this.wandTool.clearActiveRegion(),
|
|
1305
|
+
getOverlayContainer: () => this.getOverlayContainer(),
|
|
1306
|
+
getCoordinateTransform: () => this.coordTransform,
|
|
1307
|
+
getCachedImageRatio: () => this.readbackViewport()?.ratios[0] ?? 1,
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
/** The element the on-canvas tool overlays attach to (OSD is mounted here). */
|
|
1311
|
+
getOverlayContainer() {
|
|
1312
|
+
return this.plotDiv ? document.getElementById(this.plotDiv) : null;
|
|
1313
|
+
}
|
|
1314
|
+
/** Size the navigator from the CURRENT container (navigatorSizeRatio of the
|
|
1315
|
+
* viewer element) and keep it pinned to the corner. OSD computes the size
|
|
1316
|
+
* once in the Viewer constructor — before the host flex layout settles —
|
|
1317
|
+
* and never corrects it, leaving a stale-size element whose visible minimap
|
|
1318
|
+
* floats above the bottom-right corner. */
|
|
1319
|
+
resizeNavigator() {
|
|
1320
|
+
const v = this.viewer;
|
|
1321
|
+
const nav = v?.navigator;
|
|
1322
|
+
const el = v?.element;
|
|
1323
|
+
if (!nav?.element || !el?.clientWidth || !el?.clientHeight)
|
|
1324
|
+
return;
|
|
1325
|
+
const w = Math.round(el.clientWidth * 0.16);
|
|
1326
|
+
const h = Math.round(el.clientHeight * 0.16);
|
|
1327
|
+
if (nav.element.style.width !== `${w}px` || nav.element.style.height !== `${h}px`) {
|
|
1328
|
+
try {
|
|
1329
|
+
nav.setWidth(w);
|
|
1330
|
+
nav.setHeight(h);
|
|
1331
|
+
}
|
|
1332
|
+
catch { /* navigator torn down */ }
|
|
1333
|
+
}
|
|
1334
|
+
// Normalize OSD's control-corner stack so the minimap sits flush in the
|
|
1335
|
+
// corner, inset 12px to line up with the scale bar:
|
|
1336
|
+
// - the inline-block wrapper carries line-box struts and can retain stale
|
|
1337
|
+
// sizing → make it a tight block;
|
|
1338
|
+
// - anything else OSD left in the corner would stack below the navigator
|
|
1339
|
+
// and float it up → hide it;
|
|
1340
|
+
// - inset the corner itself (bottom/right 12px), keeping the element in
|
|
1341
|
+
// normal flow.
|
|
1342
|
+
const wrapper = nav.element.parentElement;
|
|
1343
|
+
const corner = wrapper?.parentElement;
|
|
1344
|
+
if (wrapper && corner) {
|
|
1345
|
+
wrapper.style.display = 'block';
|
|
1346
|
+
wrapper.style.lineHeight = '0';
|
|
1347
|
+
wrapper.style.fontSize = '0';
|
|
1348
|
+
// The wrapper keeps the navigator's stale pre-settle height as an
|
|
1349
|
+
// explicit size (the nav was 0.16x the UNSETTLED container at creation),
|
|
1350
|
+
// leaving an empty band under the resized minimap — measured live:
|
|
1351
|
+
// navH=87 inside wrapH=167. Force it to hug its content.
|
|
1352
|
+
wrapper.style.height = 'auto';
|
|
1353
|
+
wrapper.style.width = 'auto';
|
|
1354
|
+
for (const child of Array.from(corner.children)) {
|
|
1355
|
+
if (child !== wrapper)
|
|
1356
|
+
child.style.display = 'none';
|
|
1357
|
+
}
|
|
1358
|
+
for (const child of Array.from(wrapper.children)) {
|
|
1359
|
+
if (child !== nav.element)
|
|
1360
|
+
child.style.display = 'none';
|
|
1361
|
+
}
|
|
1362
|
+
corner.style.bottom = '12px';
|
|
1363
|
+
corner.style.right = '12px';
|
|
1364
|
+
}
|
|
1365
|
+
Object.assign(nav.element.style, { position: 'relative', top: '', left: '', bottom: '', right: '', margin: '0' });
|
|
1366
|
+
// TODO(debug): remove after verification
|
|
1367
|
+
const ner = nav.element.getBoundingClientRect();
|
|
1368
|
+
const wr = wrapper?.getBoundingClientRect();
|
|
1369
|
+
const cr = corner?.getBoundingClientRect();
|
|
1370
|
+
const per = el.getBoundingClientRect();
|
|
1371
|
+
console.warn(`[viz:nav] navBottom=${Math.round(ner.bottom)} wrapBottom=${Math.round(wr?.bottom ?? -1)} ` +
|
|
1372
|
+
`cornerBottom=${Math.round(cr?.bottom ?? -1)} plotBottom=${Math.round(per.bottom)} ` +
|
|
1373
|
+
`navH=${Math.round(ner.height)} wrapH=${Math.round(wr?.height ?? -1)} cornerH=${Math.round(cr?.height ?? -1)}`);
|
|
1374
|
+
}
|
|
1375
|
+
/** Force the docked toolbar to repaint after an OSD zoom. Chrome leaves it
|
|
1376
|
+
* laid-out-but-unpainted (the canvas's compositing churn strands the toolbar's
|
|
1377
|
+
* raster). A synchronous display toggle re-rasters it with no visible gone-frame
|
|
1378
|
+
* and no layout shift. Located via the DOM since this service doesn't own the
|
|
1379
|
+
* toolbar; a no-op when there's no toolbar (e.g. embedded without one). */
|
|
1380
|
+
nudgeToolbarRepaint() {
|
|
1381
|
+
const plotEl = this.plotDiv ? document.getElementById(this.plotDiv) : null;
|
|
1382
|
+
const dock = plotEl?.closest('visualization')?.querySelector('.toolbar-dock');
|
|
1383
|
+
if (!dock)
|
|
1384
|
+
return;
|
|
1385
|
+
const prev = dock.style.display;
|
|
1386
|
+
dock.style.display = 'none';
|
|
1387
|
+
void dock.offsetHeight; // reflow so the toggle re-rasters on the next paint
|
|
1388
|
+
dock.style.display = prev;
|
|
1389
|
+
}
|
|
1390
|
+
/** Host for the (shared) zoom-to-box tool: convert overlay pixels to image
|
|
1391
|
+
* coords via the viewport, and fit the viewport to the chosen rectangle. */
|
|
1392
|
+
zoomBoxHost() {
|
|
1393
|
+
return {
|
|
1394
|
+
getPlotDiv: () => this.plotDiv,
|
|
1395
|
+
pixelToData: (px, py) => elementToImage(this.viewer, px, py),
|
|
1396
|
+
applyZoomToBox: (coords) => this.applyZoomToBox(coords),
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
/** Fit the OSD viewport to an image-space rectangle (coords ordered
|
|
1400
|
+
* [xMin, xMax, yMax, yMin]). */
|
|
1401
|
+
applyZoomToBox(coordinates) {
|
|
1402
|
+
if (!this.viewer || coordinates.length < 4)
|
|
1403
|
+
return;
|
|
1404
|
+
const [a, b, c, d] = coordinates;
|
|
1405
|
+
const x = Math.min(a, b);
|
|
1406
|
+
const w = Math.abs(b - a);
|
|
1407
|
+
const y = Math.min(c, d);
|
|
1408
|
+
const h = Math.abs(c - d);
|
|
1409
|
+
if (w <= 0 || h <= 0)
|
|
1410
|
+
return;
|
|
1411
|
+
const rect = imageRectToViewport(this.viewer, x, y, w, h);
|
|
1412
|
+
this.viewer.viewport.fitBounds(rect, false);
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Read back the *currently rendered* OSD canvas as the wand's pixel matrix.
|
|
1416
|
+
* The matrix covers only the visible viewport at screen resolution, so when
|
|
1417
|
+
* the user is zoomed into a sub-region the wand samples that region's detail
|
|
1418
|
+
* (rather than the whole image at preview resolution). `originX/originY` and
|
|
1419
|
+
* `ratios` map image coords <-> readback-pixel coords for the wand's
|
|
1420
|
+
* data/ratio/origin model. Cached until the viewport changes.
|
|
1421
|
+
*/
|
|
1422
|
+
readbackViewport() {
|
|
1423
|
+
if (this.viewportPixels)
|
|
1424
|
+
return this.viewportPixels;
|
|
1425
|
+
const viewer = this.viewer;
|
|
1426
|
+
const canvas = viewer?.drawer?.canvas;
|
|
1427
|
+
const vp = viewer?.viewport;
|
|
1428
|
+
if (!canvas || !canvas.width || !canvas.height || !vp)
|
|
1429
|
+
return null;
|
|
1430
|
+
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
|
1431
|
+
if (!ctx)
|
|
1432
|
+
return null;
|
|
1433
|
+
const w = canvas.width; // device pixels
|
|
1434
|
+
const h = canvas.height;
|
|
1435
|
+
const data = ctx.getImageData(0, 0, w, h).data; // RGBA, row-major
|
|
1436
|
+
// Build the [y][x] = [r,g,b] matrix the wand expects for RGB frames.
|
|
1437
|
+
const matrix = new Array(h);
|
|
1438
|
+
for (let y = 0; y < h; y++) {
|
|
1439
|
+
const row = new Array(w);
|
|
1440
|
+
const base = y * w * 4;
|
|
1441
|
+
for (let x = 0; x < w; x++) {
|
|
1442
|
+
const o = base + x * 4;
|
|
1443
|
+
row[x] = [data[o], data[o + 1], data[o + 2]];
|
|
1444
|
+
}
|
|
1445
|
+
matrix[y] = row;
|
|
1446
|
+
}
|
|
1447
|
+
// Image-coord span the readback covers (CSS px in, image coords out). Route
|
|
1448
|
+
// through world item 0 (osd-coords) so it stays accurate — and quiet — when
|
|
1449
|
+
// the world holds multiple images (per-channel multichannel layers).
|
|
1450
|
+
const elW = canvas.clientWidth || w;
|
|
1451
|
+
const elH = canvas.clientHeight || h;
|
|
1452
|
+
const tl = elementToImage(this.viewer, 0, 0);
|
|
1453
|
+
const br = elementToImage(this.viewer, elW, elH);
|
|
1454
|
+
const ratioX = (br.x - tl.x) / w; // image px per readback px
|
|
1455
|
+
const ratioY = (br.y - tl.y) / h;
|
|
1456
|
+
this.viewportPixels = {
|
|
1457
|
+
frames: [matrix],
|
|
1458
|
+
width: w,
|
|
1459
|
+
height: h,
|
|
1460
|
+
ratios: [ratioX, ratioY],
|
|
1461
|
+
isGrayscale: false, // canvas readback is always RGBA
|
|
1462
|
+
originX: tl.x,
|
|
1463
|
+
originY: tl.y,
|
|
1464
|
+
};
|
|
1465
|
+
return this.viewportPixels;
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Recolor a grayscale tile through the active colormap LUT, using OSD's
|
|
1469
|
+
* tile-invalidated pixel pipeline (OSD 5+/6). Maps each pixel's grayscale
|
|
1470
|
+
* value (r==g==b) to the LUT's RGB. No-op for RGB images or until the LUT is
|
|
1471
|
+
* built. Handler is async — OSD awaits it (raiseEventAwaiting).
|
|
1472
|
+
*/
|
|
1473
|
+
async recolorTile(event) {
|
|
1474
|
+
if (this.isMultiChannel) {
|
|
1475
|
+
await this.recolorChannelTile(event);
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
const gray = this.isGrayscaleImage;
|
|
1479
|
+
if (gray) {
|
|
1480
|
+
if (!this.colorLut)
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
else if (!this.display.rgbNeedsRecolor()) {
|
|
1484
|
+
return; // RGB at default (all visible, full window, γ=1, no invert) → passthrough
|
|
1485
|
+
}
|
|
1486
|
+
// Read the tile's pixels. Prefer the rendering context, but some tile caches
|
|
1487
|
+
// (ajax PNG blobs) convert to an *empty* context2d — recoloring that would
|
|
1488
|
+
// blank the tile (white canvas). So if the context has no opaque pixels, draw
|
|
1489
|
+
// the tile's own bitmap/image and recolor that instead. We never write back
|
|
1490
|
+
// unless we actually recolored something, so a tile is never blanked.
|
|
1491
|
+
let ctx = null;
|
|
1492
|
+
try {
|
|
1493
|
+
ctx = await event.getData('context2d');
|
|
1494
|
+
}
|
|
1495
|
+
catch {
|
|
1496
|
+
/* try bitmap below */
|
|
1497
|
+
}
|
|
1498
|
+
let img = null;
|
|
1499
|
+
if (ctx && ctx.canvas && ctx.canvas.width && ctx.canvas.height) {
|
|
1500
|
+
try {
|
|
1501
|
+
img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
1502
|
+
}
|
|
1503
|
+
catch {
|
|
1504
|
+
img = null;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
if (!img || !this.hasOpaque(img.data)) {
|
|
1508
|
+
// Fall back to the tile's decoded image/bitmap.
|
|
1509
|
+
let src = null;
|
|
1510
|
+
try {
|
|
1511
|
+
src = await event.getData('imageBitmap');
|
|
1512
|
+
}
|
|
1513
|
+
catch {
|
|
1514
|
+
/* none */
|
|
1515
|
+
}
|
|
1516
|
+
if (!src || !src.width) {
|
|
1517
|
+
try {
|
|
1518
|
+
src = await event.getData('image');
|
|
1519
|
+
}
|
|
1520
|
+
catch {
|
|
1521
|
+
/* none */
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
if (!src || !src.width)
|
|
1525
|
+
return; // can't get pixels — leave the tile untouched
|
|
1526
|
+
const c = document.createElement('canvas');
|
|
1527
|
+
c.width = src.width;
|
|
1528
|
+
c.height = src.height;
|
|
1529
|
+
ctx = c.getContext('2d', { willReadFrequently: true });
|
|
1530
|
+
if (!ctx)
|
|
1531
|
+
return;
|
|
1532
|
+
ctx.drawImage(src, 0, 0);
|
|
1533
|
+
img = ctx.getImageData(0, 0, c.width, c.height);
|
|
1534
|
+
}
|
|
1535
|
+
if (!this.display.applyToRgba(img.data) || !ctx)
|
|
1536
|
+
return; // nothing opaque
|
|
1537
|
+
ctx.putImageData(img, 0, 0);
|
|
1538
|
+
// The tile's cache can be evicted between the awaits above and here (slice
|
|
1539
|
+
// change / invalidation), making setData throw a DOMException on a dead
|
|
1540
|
+
// canvas. Swallow it — the tile is gone, so there's nothing to recolor.
|
|
1541
|
+
try {
|
|
1542
|
+
await event.setData(ctx, 'context2d');
|
|
1543
|
+
}
|
|
1544
|
+
catch { /* tile evicted */ }
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Tint a single channel's tile in place. Each channel is its own OpenSeadragon
|
|
1548
|
+
* TiledImage (a single-band grayscale tile); OSD composites the N images
|
|
1549
|
+
* additively ('lighter') in its drawer — so this just maps the tile's intensity
|
|
1550
|
+
* through the channel's window/gamma and its pseudo-colour. Synchronous (no
|
|
1551
|
+
* cross-tile fetch) → no race, no seams. Channel is parsed from the tile URL.
|
|
1552
|
+
*/
|
|
1553
|
+
async recolorChannelTile(event) {
|
|
1554
|
+
const tile = event?.tile;
|
|
1555
|
+
const url = (tile && (typeof tile.getUrl === 'function' ? tile.getUrl() : tile.url)) || '';
|
|
1556
|
+
const m = /[?&]channel=(\d+)/.exec(url);
|
|
1557
|
+
const ch = m ? parseInt(m[1], 10) : 0;
|
|
1558
|
+
const st = this.channelStates[ch];
|
|
1559
|
+
let ctx = null;
|
|
1560
|
+
try {
|
|
1561
|
+
ctx = await event.getData('context2d');
|
|
1562
|
+
}
|
|
1563
|
+
catch { /* fallback below */ }
|
|
1564
|
+
let img = null;
|
|
1565
|
+
if (ctx && ctx.canvas && ctx.canvas.width && ctx.canvas.height) {
|
|
1566
|
+
try {
|
|
1567
|
+
img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
1568
|
+
}
|
|
1569
|
+
catch {
|
|
1570
|
+
img = null;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
if (!img || !this.hasOpaque(img.data)) {
|
|
1574
|
+
let src = null;
|
|
1575
|
+
try {
|
|
1576
|
+
src = await event.getData('imageBitmap');
|
|
1577
|
+
}
|
|
1578
|
+
catch { /* none */ }
|
|
1579
|
+
if (!src || !src.width) {
|
|
1580
|
+
try {
|
|
1581
|
+
src = await event.getData('image');
|
|
1582
|
+
}
|
|
1583
|
+
catch { /* none */ }
|
|
1584
|
+
}
|
|
1585
|
+
if (!src || !src.width)
|
|
1586
|
+
return;
|
|
1587
|
+
const c = document.createElement('canvas');
|
|
1588
|
+
c.width = src.width;
|
|
1589
|
+
c.height = src.height;
|
|
1590
|
+
ctx = c.getContext('2d', { willReadFrequently: true });
|
|
1591
|
+
if (!ctx)
|
|
1592
|
+
return;
|
|
1593
|
+
ctx.drawImage(src, 0, 0);
|
|
1594
|
+
img = ctx.getImageData(0, 0, c.width, c.height);
|
|
1595
|
+
}
|
|
1596
|
+
if (!ctx)
|
|
1597
|
+
return;
|
|
1598
|
+
// Precompute lum(0..255) → tinted RGB once, then map each pixel by lookup —
|
|
1599
|
+
// the channel tile is single-band, so this turns ~262k per-pixel
|
|
1600
|
+
// channelIntensity() (with Math.pow for gamma) calls into 256 — the difference
|
|
1601
|
+
// between a snappy and a sluggish slider on a 4-channel stack.
|
|
1602
|
+
const { r: rL, g: gL, b: bL } = this.display.channelRgbLut(st);
|
|
1603
|
+
const d = img.data;
|
|
1604
|
+
let changed = false;
|
|
1605
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
1606
|
+
if (d[i + 3] === 0)
|
|
1607
|
+
continue;
|
|
1608
|
+
const r = d[i], g = d[i + 1], b = d[i + 2];
|
|
1609
|
+
const lum = r >= g ? (r >= b ? r : b) : g >= b ? g : b; // single-band → max
|
|
1610
|
+
d[i] = rL[lum];
|
|
1611
|
+
d[i + 1] = gL[lum];
|
|
1612
|
+
d[i + 2] = bL[lum];
|
|
1613
|
+
changed = true;
|
|
1614
|
+
}
|
|
1615
|
+
if (!changed)
|
|
1616
|
+
return;
|
|
1617
|
+
ctx.putImageData(img, 0, 0);
|
|
1618
|
+
// See recolorTile: the tile cache can die between the awaits and here.
|
|
1619
|
+
try {
|
|
1620
|
+
await event.setData(ctx, 'context2d');
|
|
1621
|
+
}
|
|
1622
|
+
catch { /* tile evicted */ }
|
|
1623
|
+
}
|
|
1624
|
+
/** Per-channel histogram for the Channels & Histogram pane, from the current
|
|
1625
|
+
* slice's sampled tiles (grayscale → channel 0; RGB → R/G/B). Null until the
|
|
1626
|
+
* async sampling resolves or if it was skipped. */
|
|
1627
|
+
getHistogram(channelIndex, _bins) {
|
|
1628
|
+
return this.sampler.get(this.currentZ, channelIndex);
|
|
1629
|
+
}
|
|
1630
|
+
/** Native bit depth of a channel from the tile descriptor (8 when unknown). */
|
|
1631
|
+
channelBitDepth(channelIndex) {
|
|
1632
|
+
return this.descriptor?.channelInfo?.[channelIndex]?.bitDepth ?? 8;
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* Async histogram for the Channels & Histogram pane. For >8-bit images it
|
|
1636
|
+
* fetches the **native** distribution from the server `/histogram` endpoint
|
|
1637
|
+
* (the 8-bit canvas tiles can't carry 16-bit values) and caches it per
|
|
1638
|
+
* slice+channel; for 8-bit/RGB it returns the existing client-sampled 8-bit
|
|
1639
|
+
* histogram. Read-only — it never touches the tile/display pipeline.
|
|
1640
|
+
*/
|
|
1641
|
+
getHistogram$(channelIndex, bins) {
|
|
1642
|
+
if (this.channelBitDepth(channelIndex) <= 8 || !this.infoB64) {
|
|
1643
|
+
return of(this.getHistogram(channelIndex, bins));
|
|
1644
|
+
}
|
|
1645
|
+
return this.sampler.native$(this.infoB64, this.currentZ, channelIndex, bins);
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Export the underlying image data as a data-preserving multi-band TIFF at
|
|
1649
|
+
* native bit depth via `GET /export/tiff` (the server reads the raw 16-bit
|
|
1650
|
+
* planes — the client never sees them). Sends only the visible channels; the
|
|
1651
|
+
* existing 8-bit composite PNG export is unaffected.
|
|
1652
|
+
*/
|
|
1653
|
+
async exportData() {
|
|
1654
|
+
if (!this.infoB64)
|
|
1655
|
+
return;
|
|
1656
|
+
const visible = this.channelStates.filter((c) => c.visible).map((c) => c.index);
|
|
1657
|
+
const all = this.channelStates.length;
|
|
1658
|
+
// Omit `channels` when every channel is visible (server default = all).
|
|
1659
|
+
const chParam = visible.length && visible.length < all ? `&channels=${visible.join(',')}` : '';
|
|
1660
|
+
const url = `${this.api}export/tiff?info=${this.infoB64}&z=${this.currentZ}${chParam}`;
|
|
1661
|
+
const stem = (this.currentFileName || 'image').replace(/\.[^.]+$/, '');
|
|
1662
|
+
try {
|
|
1663
|
+
const resp = await firstValueFrom(this.http
|
|
1664
|
+
.get(url, { observe: 'response', responseType: 'blob' })
|
|
1665
|
+
.pipe(timeout(600000)));
|
|
1666
|
+
if (resp.status === 202) {
|
|
1667
|
+
console.warn('[OSD] 16-bit export: file still caching — try again shortly.');
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
const blob = resp.body;
|
|
1671
|
+
if (blob)
|
|
1672
|
+
saveAs(blob, `${stem}_16bit.ome.tif`);
|
|
1673
|
+
}
|
|
1674
|
+
catch (err) {
|
|
1675
|
+
console.warn('[OSD] 16-bit TIFF export failed', err);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Export the current slice as a publication-ready PNG composited with the
|
|
1680
|
+
* active display settings (window / gamma / colormap or per-channel pseudo-
|
|
1681
|
+
* colours / invert). Picks the largest pyramid level under a pixel cap (the
|
|
1682
|
+
* coarser overview for huge whole-slides), fetches that level's tile grid,
|
|
1683
|
+
* stitches it into one canvas, runs the shared display pipeline, and saves it.
|
|
1684
|
+
*/
|
|
1685
|
+
async exportComposite() {
|
|
1686
|
+
const desc = this.descriptor;
|
|
1687
|
+
const levels = desc?.levels ?? [];
|
|
1688
|
+
if (!desc || !this.infoB64 || !levels.length)
|
|
1689
|
+
return;
|
|
1690
|
+
const CAP = 32_000_000; // ~32 MP — bounds memory for whole-slide images
|
|
1691
|
+
let res = levels.length - 1; // coarsest fallback
|
|
1692
|
+
for (let i = 0; i < levels.length; i++) {
|
|
1693
|
+
if (levels[i].width * levels[i].height <= CAP) {
|
|
1694
|
+
res = i;
|
|
1695
|
+
break;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
const lw = levels[res].width;
|
|
1699
|
+
const lh = levels[res].height;
|
|
1700
|
+
const t = desc.tileSize;
|
|
1701
|
+
const cols = Math.max(1, Math.ceil(lw / t));
|
|
1702
|
+
const rows = Math.max(1, Math.ceil(lh / t));
|
|
1703
|
+
const canvas = document.createElement('canvas');
|
|
1704
|
+
canvas.width = lw;
|
|
1705
|
+
canvas.height = lh;
|
|
1706
|
+
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
|
1707
|
+
if (!ctx)
|
|
1708
|
+
return;
|
|
1709
|
+
const z = this.currentZ;
|
|
1710
|
+
const jobs = [];
|
|
1711
|
+
for (let row = 0; row < rows; row++) {
|
|
1712
|
+
for (let col = 0; col < cols; col++) {
|
|
1713
|
+
const url = buildTileUrl(this.api, this.infoB64, { res, col, row, z, tileSize: t });
|
|
1714
|
+
jobs.push((async () => {
|
|
1715
|
+
try {
|
|
1716
|
+
const bmp = await fetchTileBitmap(this.http, url, 30000);
|
|
1717
|
+
ctx.drawImage(bmp, col * t, row * t);
|
|
1718
|
+
bmp.close?.();
|
|
1719
|
+
}
|
|
1720
|
+
catch (err) {
|
|
1721
|
+
// Skip a failed tile — the exported composite has a gap there.
|
|
1722
|
+
console.warn('[viz:export] composite tile fetch failed, skipping', url, err);
|
|
1723
|
+
}
|
|
1724
|
+
})());
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
await Promise.all(jobs);
|
|
1728
|
+
try {
|
|
1729
|
+
const imageData = ctx.getImageData(0, 0, lw, lh);
|
|
1730
|
+
if (this.display.applyToRgba(imageData.data))
|
|
1731
|
+
ctx.putImageData(imageData, 0, 0);
|
|
1732
|
+
}
|
|
1733
|
+
catch (err) {
|
|
1734
|
+
// Keep the un-recolored composite if readback fails — but say why.
|
|
1735
|
+
console.warn('[viz:export] composite recolor readback failed — exporting raw tiles', err);
|
|
1736
|
+
}
|
|
1737
|
+
const stem = (this.currentFileName || 'image').replace(/\.[^.]+$/, '');
|
|
1738
|
+
canvas.toBlob((blob) => {
|
|
1739
|
+
if (blob)
|
|
1740
|
+
saveAs(blob, `${stem}_composite.png`);
|
|
1741
|
+
}, 'image/png');
|
|
1742
|
+
}
|
|
1743
|
+
/** True if any pixel in the RGBA buffer is non-transparent. */
|
|
1744
|
+
hasOpaque(d) {
|
|
1745
|
+
for (let i = 3; i < d.length; i += 4) {
|
|
1746
|
+
if (d[i] !== 0)
|
|
1747
|
+
return true;
|
|
1748
|
+
}
|
|
1749
|
+
return false;
|
|
1750
|
+
}
|
|
1751
|
+
// ── IDisplayOptions ──────────────────────────────────────────────────
|
|
1752
|
+
// Inherited from BaseStoreVisualizer — pure delegations to the shared
|
|
1753
|
+
// VisualizerStore, so OSD and Plotly stay in lock-step. OSD applies the
|
|
1754
|
+
// colormap to grayscale tiles via the LUT reactively (see the constructor's
|
|
1755
|
+
// colormap subscription), not in setColormap.
|
|
1756
|
+
// ── IVisualizer ──────────────────────────────────────────────────────
|
|
1757
|
+
unsubscribe() {
|
|
1758
|
+
this.colormapSub?.unsubscribe();
|
|
1759
|
+
this.colormapSub = null;
|
|
1760
|
+
this.destroyViewer();
|
|
1761
|
+
}
|
|
1762
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: OpenSeadragonVisualizerService, deps: [{ token: i1.HttpClient }, { token: TILE_ACCESS_PORT }, { token: i2.WandToolService }, { token: i3.BrushToolService }, { token: i4.SamToolService }, { token: i5.SamPointToolService }, { token: i6.CellSegmentToolService }, { token: CELL_SEGMENTER, optional: true }, { token: i7.VertexEraserToolService }, { token: i8.ZoomToBoxToolService }, { token: i9.VisualizerStore }, { token: i10.RegionStore }, { token: i11.SimpleSliceAccessService }, { token: VIZ_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1763
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: OpenSeadragonVisualizerService, providedIn: 'root' }); }
|
|
1764
|
+
}
|
|
1765
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: OpenSeadragonVisualizerService, decorators: [{
|
|
1766
|
+
type: Injectable,
|
|
1767
|
+
args: [{ providedIn: 'root' }]
|
|
1768
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
|
|
1769
|
+
type: Inject,
|
|
1770
|
+
args: [TILE_ACCESS_PORT]
|
|
1771
|
+
}] }, { type: i2.WandToolService }, { type: i3.BrushToolService }, { type: i4.SamToolService }, { type: i5.SamPointToolService }, { type: i6.CellSegmentToolService }, { type: undefined, decorators: [{
|
|
1772
|
+
type: Optional
|
|
1773
|
+
}, {
|
|
1774
|
+
type: Inject,
|
|
1775
|
+
args: [CELL_SEGMENTER]
|
|
1776
|
+
}] }, { type: i7.VertexEraserToolService }, { type: i8.ZoomToBoxToolService }, { type: i9.VisualizerStore }, { type: i10.RegionStore }, { type: i11.SimpleSliceAccessService }, { type: undefined, decorators: [{
|
|
1777
|
+
type: Inject,
|
|
1778
|
+
args: [VIZ_CONFIG]
|
|
1779
|
+
}] }] });
|
|
1780
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbnNlYWRyYWdvbi12aXN1YWxpemVyLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2ltcGxlbWVudGF0aW9ucy9vc2Qvb3BlbnNlYWRyYWdvbi12aXN1YWxpemVyLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzdELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsZUFBZSxFQUFjLE9BQU8sRUFBZ0IsYUFBYSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDN0csT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBR3pDLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFHaEMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUUvRCxPQUFPLEVBQWtCLGdCQUFnQixFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDMUYsT0FBTyxFQUFhLFVBQVUsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ25FLE9BQU8sRUFBRSxRQUFRLEVBQUUscUJBQXFCLEVBQXNCLE1BQU0sMkJBQTJCLENBQUM7QUFFaEcsT0FBTyxFQUFzQixhQUFhLEVBQUUsY0FBYyxFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFDMUcsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDeEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRzlDLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxjQUFjLEVB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QUUsVUFBVSxFQUFFLE1BQU0sRUFBRTs7MEJBMkg3QixNQUFNOzJCQUFDLGdCQUFnQjs7MEJBTXZCLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsY0FBYzs7MEJBTWpDLE1BQU07MkJBQUMsVUFBVSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIEluamVjdCwgT3B0aW9uYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEh0dHBDbGllbnQgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIE9ic2VydmFibGUsIFN1YmplY3QsIFN1YnNjcmlwdGlvbiwgY29tYmluZUxhdGVzdCwgZmlyc3RWYWx1ZUZyb20sIG9mIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyB0aW1lb3V0IH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgSW1hZ2UgfSBmcm9tICdpbWFnZS1qcyc7XG5pbXBvcnQgKiBhcyBPcGVuU2VhZHJhZ29uIGZyb20gJ29wZW5zZWFkcmFnb24nO1xuaW1wb3J0IHsgT1NEIH0gZnJvbSAnLi9vc2QtbGliJztcblxuXG5pbXBvcnQgeyBWaXN1YWxpemVyU3RvcmUgfSBmcm9tICcuLi8uLi9zdG9yZS92aXN1YWxpemVyLXN0b3JlLnNlcnZpY2UnO1xuaW1wb3J0IHsgUmVnaW9uU3RvcmUgfSBmcm9tICcuLi8uLi9zdG9yZS9yZWdpb24tc3RvcmUuc2VydmljZSc7XG5pbXBvcnQgeyBJSW1hZ2VJbmZvIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2ltYWdlLmNvbnRyYWN0JztcbmltcG9ydCB7IFRpbGVBY2Nlc3NQb3J0LCBUSUxFX0FDQ0VTU19QT1JUIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3BvcnRzL3RpbGUtYWNjZXNzLnBvcnQnO1xuaW1wb3J0IHsgVml6Q29uZmlnLCBWSVpfQ09ORklHIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3Zpei1jb25maWcnO1xuaW1wb3J0IHsgUGxvdFR5cGUsIFBMT1RfVFlQRV9ERVNDUklQVE9SUywgUGxvdFR5cGVEZXNjcmlwdG9yIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL3Bsb3QtdHlwZSc7XG5pbXBvcnQgeyBJVmlzdWFsaXplciwgUGl4ZWxEYXRhLCBJbnRlbnNpdHlQcm9maWxlLCBJSXNvc3VyZmFjZUNvbnRyb2xzLCBJSW50ZW5zaXR5Q29udHJvbHMsIElTdXJmYWNlM2RDb250cm9scyB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy92aXN1YWxpemVyLmNvbnRyYWN0JztcbmltcG9ydCB7IFZpZXdlckNhcGFiaWxpdGllcywgVmlld2VyRmVhdHVyZSwgY2FwYWJpbGl0aWVzT2YgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY2FwYWJpbGl0aWVzLmNvbnRyYWN0JztcbmltcG9ydCB7IE9zZFJlZ2lvbk92ZXJsYXkgfSBmcm9tICcuL29zZC1yZWdpb24tb3ZlcmxheSc7XG5pbXBvcnQgeyBPc2RTY2FsZUJhciB9IGZyb20gJy4vb3NkLXNjYWxlLWJhcic7XG5pbXBvcnQgeyBJUmVnaW9uT3ZlcmxheSwgUmVnaW9uVG9vbE1vZGUgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvcmVnaW9uLW92ZXJsYXkuY29udHJhY3QnO1xuaW1wb3J0IHsgSUNvb3JkaW5hdGVUcmFuc2Zvcm0gfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY29vcmRpbmF0ZS10cmFuc2Zvcm0uY29udHJhY3QnO1xuaW1wb3J0IHsgT3NkQ29vcmRpbmF0ZVRyYW5zZm9ybSB9IGZyb20gJy4vb3NkLWNvb3JkaW5hdGUtdHJhbnNmb3JtJztcbmltcG9ydCB7IGVsZW1lbnRUb0ltYWdlLCBpbWFnZVJlY3RUb1ZpZXdwb3J0LCB2aWV3cG9ydFJlY3RUb0ltYWdlIH0gZnJvbSAnLi9vc2QtY29vcmRzJztcbmltcG9ydCB7IGJ1aWxkVGlsZVVybCwgZmV0Y2hUaWxlQml0bWFwIH0gZnJvbSAnLi90aWxlLWNsaWVudCc7XG5pbXBvcnQgeyBTbGljZUNhY2hlIH0gZnJvbSAnLi9zbGljZS1jYWNoZSc7XG5pbXBvcnQgeyBEaXNwbGF5UGlwZWxpbmUgfSBmcm9tICcuL2Rpc3BsYXktcGlwZWxpbmUnO1xuaW1wb3J0IHsgSGlzdG9ncmFtU2FtcGxlciB9IGZyb20gJy4vaGlzdG9ncmFtLXNhbXBsZXInO1xuaW1wb3J0IHsgQmFzZVN0b3JlVmlzdWFsaXplciB9IGZyb20gJy4uL2Jhc2Utc3RvcmUtdmlzdWFsaXplcic7XG5pbXBvcnQgeyBTaW1wbGVTbGljZUFjY2Vzc1NlcnZpY2UgfSBmcm9tICcuLi9zaW1wbGUtc2xpY2UtYWNjZXNzLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ2FjaGVkSW1hZ2VEYXRhLCBXYW5kVG9vbFNlcnZpY2UsIFdhbmRUb29sSG9zdCB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvd2FuZC93YW5kLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBCcnVzaFRvb2xTZXJ2aWNlLCBCcnVzaE9wdGlvbnMgfSBmcm9tICcuLi8uLi90b29sYmFyL2JydXNoL2JydXNoLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBTYW1Ub29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgU2FtUG9pbnRUb29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS1wb2ludC10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ2VsbFNlZ21lbnRUb29sU2VydmljZSB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvc2VnbWVudGF0aW9uL2NlbGwtc2VnbWVudC10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgSUNlbGxTZWdtZW50ZXIsIENFTExfU0VHTUVOVEVSIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2NlbGwtc2VnbWVudGVyLmNvbnRyYWN0JztcbmltcG9ydCB7IFZlcnRleEVyYXNlclRvb2xTZXJ2aWNlLCBWZXJ0ZXhFcmFzZXJUb29sSG9zdCB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvdmVydGV4LWVyYXNlci92ZXJ0ZXgtZXJhc2VyLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBab29tVG9Cb3hUb29sU2VydmljZSwgWm9vbVRvQm94VG9vbEhvc3QgfSBmcm9tICcuLi8uLi90b29sYmFyL3pvb20tdG8tYm94L3pvb20tdG8tYm94LXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBXYW5kT3B0aW9ucyB9IGZyb20gJy4uLy4uL3Rvb2xiYXIvd2FuZC93YW5kLnNlcnZpY2UnO1xuaW1wb3J0IHsgYnVpbGRDb2xvcm1hcEx1dCwgUmdiIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2NvbG9ybWFwLWx1dCc7XG5pbXBvcnQgeyBJQ2hhbm5lbFN0YXRlLCBJSGlzdG9ncmFtIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2NoYW5uZWwtaGlzdG9ncmFtLWFwaS5jb250cmFjdCc7XG5pbXBvcnQgeyBzYXZlQXMgfSBmcm9tICdmaWxlLXNhdmVyJztcblxuLyoqXG4gKiBRdWlldCBPcGVuU2VhZHJhZ29uJ3MgXCJbVmlld3BvcnQuKl0gaXMgbm90IGFjY3VyYXRlIHdpdGggbXVsdGktaW1hZ2VcIiBhZHZpc29yaWVzLlxuICogQSBtdWx0aWNoYW5uZWwgaW1hZ2UgaXMgY29tcG9zaXRlZCBmcm9tIG9uZSBUaWxlZEltYWdlIHBlciBjaGFubmVsIOKAlCBhIGxlZ2l0aW1hdGVcbiAqIG11bHRpLWltYWdlIHdvcmxkIOKAlCBhbmQgT1NEIGxvZ3MgdGhhdCBhZHZpc29yeSAoYXQgZXJyb3IgbGV2ZWwpIGZyb20gaXRzIG93blxuICogbmF2aWdhdG9yL292ZXJ2aWV3IHJlbmRlcmluZyBvbiBldmVyeSBhbmltYXRpb24gZnJhbWUsIGZsb29kaW5nIHRoZSBjb25zb2xlLiBPdXJcbiAqIG93biBpbWFnZTwtPnZpZXdwb3J0IGNvbnZlcnNpb25zIGFscmVhZHkgcm91dGUgdGhyb3VnaCB3b3JsZCBpdGVtIDAgKHNlZVxuICogb3NkLWNvb3JkcyksIHNvIHRoZSByZW1haW5pbmcgYWR2aXNvcmllcyBhcmUgT1NELWludGVybmFsIGFuZCBvbmx5IGFmZmVjdCB0aGVcbiAqIG1pbmltYXAncyBib3ggYWNjdXJhY3kgKGNvc21ldGljKS4gVGhpcyB3cmFwcyBPU0QncyBsb2dnZXIgaW4gYSBQcm94eSB0aGF0IGRyb3BzXG4gKiBvbmx5IHRoYXQgb25lIG1lc3NhZ2Ugc3RyaW5nIGFuZCBmb3J3YXJkcyBldmVyeSBvdGhlciBsb2cgdW50b3VjaGVkLiBJZGVtcG90ZW50LlxuICovXG5mdW5jdGlvbiBzaWxlbmNlT3NkTXVsdGlJbWFnZUFkdmlzb3J5KCk6IHZvaWQge1xuICBjb25zdCBvc2Q6IGFueSA9IE9TRCBhcyBhbnk7XG4gIGlmIChvc2QuX19tdWx0aUltYWdlRmlsdGVySW5zdGFsbGVkKSByZXR1cm47XG4gIGNvbnN0IGJhc2U6IGFueSA9IChvc2QuY29uc29sZSAmJiB0eXBlb2Ygb3NkLmNvbnNvbGUuZXJyb3IgPT09ICdmdW5jdGlvbicpXG4gICAgPyBvc2QuY29uc29sZVxuICAgIDogKHR5cGVvZiBjb25zb2xlICE9PSAndW5kZWZpbmVkJyA/IGNvbnNvbGUgOiBudWxsKTtcbiAgaWYgKCFiYXNlKSByZXR1cm47XG4gIGNvbnN0IGlzQWR2aXNvcnkgPSAoYTogdW5rbm93bikgPT5cbiAgICB0eXBlb2YgYSA9PT0gJ3N0cmluZycgJiYgYS5pbmRleE9mKCdub3QgYWNjdXJhdGUgd2l0aCBtdWx0aS1pbWFnZScpICE9PSAtMTtcbiAgb3NkLmNvbnNvbGUgPSBuZXcgUHJveHkoYmFzZSwge1xuICAgIGdldCh0YXJnZXQ6IGFueSwgcHJvcDogc3RyaW5nKSB7XG4gICAgICBjb25zdCBvcmlnID0gdGFyZ2V0W3Byb3BdO1xuICAgICAgaWYgKChwcm9wID09PSAnZXJyb3InIHx8IHByb3AgPT09ICd3YXJuJykgJiYgdHlwZW9mIG9yaWcgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgcmV0dXJuICguLi5hcmdzOiBhbnlbXSkgPT4geyBpZiAoaXNBZHZpc29yeShhcmdzWzBdKSkgcmV0dXJuOyBvcmlnLmFwcGx5KHRhcmdldCwgYXJncyk7IH07XG4gICAgICB9XG4gICAgICByZXR1cm4gdHlwZW9mIG9yaWcgPT09ICdmdW5jdGlvbicgPyBvcmlnLmJpbmQodGFyZ2V0KSA6IG9yaWc7XG4gICAgfSxcbiAgfSk7XG4gIG9zZC5fX211bHRpSW1hZ2VGaWx0ZXJJbnN0YWxsZWQgPSB0cnVlO1xufVxuXG4vKiogT25lIHB5cmFtaWQgbGV2ZWwgZnJvbSBgR0VUIC90aWxlcy9pbmZvYC4gKi9cbmludGVyZmFjZSBUaWxlTGV2ZWwgeyByZXM6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXI7IH1cblxuLyoqIFRpbGUtc291cmNlIGRlc2NyaXB0b3IgcmV0dXJuZWQgYnkgYEdFVCAvdGlsZXMvaW5mb2AuICovXG5pbnRlcmZhY2UgVGlsZURlc2NyaXB0b3Ige1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbiAgdGlsZVNpemU6IG51bWJlcjtcbiAgejogbnVtYmVyO1xuICBjaGFubmVsczogbnVtYmVyO1xuICAvKiogVHJ1ZSBvbmx5IGZvciBnZW51aW5lIG11bHRpLWNoYW5uZWwgY29tcG9zaXRlcyAoaW5kZXhlZC9MVVQtYmVhcmluZ1xuICAgKiAgZmx1b3Jlc2NlbmNlIHN0YWNrcykgdGhlIGNsaWVudCBzaG91bGQgc3BsaXQgaW50byBwZXItY2hhbm5lbCBsYXllcnMuIFRoZVxuICAgKiAgc2VydmVyIHNldHMgaXQ7IGFuIFJHQiBwaG90byByZWFkIGFzIHNlcGFyYXRlZCBwbGFuZXMgaGFzIGNoYW5uZWxzPjEgYnV0XG4gICAqICBtdWx0aWNoYW5uZWw9ZmFsc2UsIHNvIGl0IHN0YXlzIGEgc2luZ2xlIGNvbXBvc2l0ZSB0aWxlIHNvdXJjZS4gKi9cbiAgbXVsdGljaGFubmVsPzogYm9vbGVhbjtcbiAgLyoqIFJlYWwgQmlvLUZvcm1hdHMgcmVzb2x1dGlvbiBsZXZlbHMgYXQgdGhlIGZyb250IG9mIGBsZXZlbHNgOyB0aGUgcmVtYWluaW5nXG4gICAqICBsZXZlbHMgYXJlIHN5bnRoZXRpYyBjb21wb3NpdGVkIG92ZXJ2aWV3cyAobm8gcGVyLWNoYW5uZWwgdGlsZXMpLiAqL1xuICByZWFsTGV2ZWxzPzogbnVtYmVyO1xuICAvKiogUGVyLWNoYW5uZWwgbWV0YWRhdGEgKG5hbWUvY29sb3IvYml0RGVwdGgvbWluLW1heEFsbG93ZWQpIGZvciBuYXRpdmUgMTYtYml0XG4gICAqICB3aW5kb3dpbmcgKyBoaXN0b2dyYW07IG51bGwgZm9yIHBsYWluIDgtYml0L1JHQiBzb3VyY2VzLiAqL1xuICBjaGFubmVsSW5mbz86IEFycmF5PHtcbiAgICBuYW1lPzogc3RyaW5nOyBjb2xvcj86IHN0cmluZzsgYml0RGVwdGg/OiBudW1iZXI7IG1pbkFsbG93ZWQ/OiBudW1iZXI7IG1heEFsbG93ZWQ/OiBudW1iZXI7XG4gIH0+IHwgbnVsbDtcbiAgbGV2ZWxzOiBUaWxlTGV2ZWxbXTtcbiAgLyoqIFBoeXNpY2FsIHBpeGVsIHNpemUgaW4gwrVtICgwIHdoZW4gdGhlIGZvcm1hdCBkb2Vzbid0IHJlcG9ydCBpdCkuICovXG4gIG1wcFg/OiBudW1iZXI7XG4gIG1wcFk/OiBudW1iZXI7XG59XG5cbi8qKiBXaGF0IGBsb2FkKClgIGhhbmRzIHRvIGBwbG90KClgLiAqL1xuaW50ZXJmYWNlIE9zZExvYWRlZCB7XG4gIGRlc2NyaXB0b3I6IFRpbGVEZXNjcmlwdG9yIHwgbnVsbDtcbiAgaW5mb0I2NDogc3RyaW5nO1xuICB6OiBudW1iZXI7XG4gIC8qKiBNaXJyb3JzIHRoZSBsb2FkZWQgaW1hZ2UncyBmaWxlbmFtZSDigJQgdGhlIGRpYWdyYW0ncyByZW5kZXIgcGlwZWxpbmVcbiAgICogIGd1YXJkcyBvbiBgbG9hZGVkLmZpbGVuYW1lID09PSBwaGFzZUluZm8uZmlsZU5hbWVgIGJlZm9yZSBjYWxsaW5nIHBsb3QoKS4gKi9cbiAgZmlsZW5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgLyoqIFNpbXBsZSAobm9uLXRpbGVkKSBpbWFnZTogYHBsb3QoKWAgb3BlbnMge0BsaW5rIHVybH0gZGlyZWN0bHkgdmlhIE9TRCdzXG4gICAqICBzaW5nbGUtaW1hZ2Ugc291cmNlIOKAlCBubyB0aWxlIHB5cmFtaWQsIG5vIHNlcnZlci4gU2V0IHdoZW4gdGhlIGltYWdlXG4gICAqICBjYXJyaWVzIGB0aWxlZCA9PT0gZmFsc2VgIChzZWUge0BsaW5rIElJbWFnZUluZm8udGlsZWR9KS4gKi9cbiAgc2ltcGxlPzogYm9vbGVhbjtcbiAgLyoqIFRoZSBkaXJlY3RseS1sb2FkYWJsZSBpbWFnZSBVUkwgKGBibG9iOmAvYGRhdGE6YC9gaHR0cGApIGZvciBzaW1wbGUgbW9kZS4gKi9cbiAgdXJsPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIE9wZW5TZWFkcmFnb24gdmlzdWFsaXphdGlvbiBiYWNrZW5kLlxuICpcbiAqIFJlbmRlcnMgdGhlICppbWFnZSogcGxvdCB0eXBlIGFzIGEgbmF0aXZlbHktdGlsZWQsIHpvb21hYmxlIHJhc3RlciwgYmFja2VkIGJ5XG4gKiB0aGUgaml0LXNlcnZpY2UgdGlsZSBlbmRwb2ludHMgKGBHRVQgL3RpbGVzL2luZm9gICsgYEdFVCAvdGlsZWAsIHdoaWNoIHJldXNlXG4gKiB0aGUgQmlvLUZvcm1hdHMgUk9JIHJlbmRlcmVyKS4gUGxvdGx5IGtlZXBzIHRoZSBzY2llbnRpZmljL2RhdGEgcGxvdCB0eXBlc1xuICogKHNjYWxhciBoZWF0bWFwLCBzdXJmYWNlLCBjb250b3VyLCBzY2F0dGVyLCBsaW5lLCBzY2F0dGVyM2QsIGlzb3N1cmZhY2UpIOKAlFxuICogaGVuY2UgdGhpcyBiYWNrZW5kIGFkdmVydGlzZXMgb25seSBgSW1hZ2VEaXNwbGF5YC5cbiAqXG4gKiBXaXJlZDogbG9hZCDihpIgZGVzY3JpcHRvciwgcGxvdCDihpIgbW91bnQgdmlld2VyIHdpdGggYSBjdXN0b20gdGlsZSBzb3VyY2UsXG4gKiB6b29tL3BhbiDihpIgdmlld3BvcnQgQVBJLiBOT1Qgd2lyZWQgeWV0IChmb2xsb3ctdXBzKTogcmVnaW9uIG92ZXJsYXlzICsgdG9vbHNcbiAqIChhIHBvc2l0aW9uZWQgY2FudmFzL1NWRyBvdmVybGF5IG9yIEFubm90b3Jpb3VzKSwgcGl4ZWwgcmVhZGJhY2ssIGFuZCB0aGVcbiAqIHBlci10aWxlIExVVC9jb250cmFzdCBwYXJhbXMuIE5vdCByZWdpc3RlcmVkIGluIERJIHByb3ZpZGVycyDigJQgYFBsb3RseVNlcnZpY2VgXG4gKiBpcyBzdGlsbCB0aGUgb25seSBhY3RpdmUgYmFja2VuZDsgdGhpcyBwcm92ZXMgdGhlIGNvbnRyYWN0IHN1cHBvcnRzIGEgc2Vjb25kXG4gKiBpbXBsZW1lbnRhdGlvbiBhZ2FpbnN0IHJlYWwgZW5kcG9pbnRzLlxuICovXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIE9wZW5TZWFkcmFnb25WaXN1YWxpemVyU2VydmljZSBleHRlbmRzIEJhc2VTdG9yZVZpc3VhbGl6ZXIgaW1wbGVtZW50cyBJVmlzdWFsaXplciB7XG4gIC8qKiBPU0QncyBzdHJlbmd0aCBpcyBkaXNwbGF5aW5nIGEgbGFyZ2Ugem9vbWFibGUgaW1hZ2Ug4oCUIG5vdGhpbmcgZWxzZSBoZXJlLiAqL1xuICByZWFkb25seSBjYXBhYmlsaXRpZXM6IFZpZXdlckNhcGFiaWxpdGllcyA9IGNhcGFiaWxpdGllc09mKFtWaWV3ZXJGZWF0dXJlLkltYWdlRGlzcGxheV0pO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgYXBpOiBzdHJpbmc7XG4gIHByaXZhdGUgdmlld2VyOiBPcGVuU2VhZHJhZ29uLlZpZXdlciB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIG92ZXJsYXk6IElSZWdpb25PdmVybGF5IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgc2NhbGVCYXI6IE9zZFNjYWxlQmFyIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgZGVzY3JpcHRvcjogVGlsZURlc2NyaXB0b3IgfCBudWxsID0gbnVsbDtcbiAgLyoqIEJhc2U2NCBSYXdGaWxlSW5mbyArIGN1cnJlbnQgei1zbGljZSwga2VwdCBzbyBzZXRaSW5kZXggY2FuIHJlYnVpbGQgdGhlXG4gICAqICB0aWxlIHNvdXJjZSBhbmQgc3dhcCBzbGljZXMgbGl2ZSAoc3RhY2sgbmF2aWdhdGlvbikuICovXG4gIHByaXZhdGUgaW5mb0I2NCA9ICcnO1xuICBwcml2YXRlIGN1cnJlbnRaID0gMDtcbiAgLyoqIFRydWUgd2hpbGUgdGhlIGN1cnJlbnQgaW1hZ2UgaXMgXCJzaW1wbGVcIiAodGlsZWQ6ZmFsc2Ug4oCUIHNlbGYtY29udGFpbmVkXG4gICAqICBwZXItc2xpY2UgVVJMcywgbm8gdGlsZSBzZXJ2ZXI7IGUuZy4gYSBudW1iZXJlZCBpbWFnZSBzZXJpZXMgYXNzZW1ibGVkXG4gICAqICBjbGllbnQtc2lkZSBpbnRvIGEgc3RhY2spLiBzZXRaSW5kZXggc3dhcHMgdGhlIHNpbmdsZS1pbWFnZSBzb3VyY2UgZm9yXG4gICAqICB0aGUgbmV3IHNsaWNlJ3MgVVJMIGluc3RlYWQgb2YgdXBkYXRpbmcgYSB0aWxlZCB6LXBhcmFtLiAqL1xuICBwcml2YXRlIHNpbXBsZU1vZGUgPSBmYWxzZTtcbiAgLyoqIHVybHNbXSBmb3IgdGhlIGN1cnJlbnQgc2ltcGxlLW1vZGUgaW1hZ2UsIHNvIHNldFpJbmRleCBjYW4gbG9vayB1cCB0aGVcbiAgICogIHNsaWNlIGJlaW5nIHNjcnViYmVkIHRvICh2aWEge0BsaW5rIFNpbXBsZVNsaWNlQWNjZXNzU2VydmljZS51cmxGb3J9KS5cbiAgICogIFVudXNlZCAoYW5kIGxlZnQgc3RhbGUsIGhhcm1sZXNzbHkpIGluIHRpbGVkIG1vZGUuICovXG4gIHByaXZhdGUgc2ltcGxlVXJsczogc3RyaW5nW10gPSBbXTtcbiAgLyoqIHByZXZpZXcgYmxvYiBVUkwg4oaSIGZ1bGwtcmVzIHVwc2NhbGVkIGJsb2IgVVJMIChzZWUgdG9GdWxsUmVzVXJsKSwgc29cbiAgICogIHJlLXZpc2l0aW5nIGEgc2ltcGxlLW1vZGUgc2xpY2UgZG9lc24ndCByZS11cHNjYWxlLiBSZXZva2VkIG9uIGZpbGUgY2hhbmdlLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHNpbXBsZUZ1bGxSZXNVcmxzID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oKTtcbiAgLyoqIFNraXAgdGhlIGZ1bGwtcmVzIHVwc2NhbGUgYWJvdmUgdGhpcyBsb25nZXN0LXNpZGUgZGltZW5zaW9uIOKAlCBhIGZvbGRlclxuICAgKiAgc3RhY2sgaXMgcGVyLWZpbGUgcHJldmlld3MgKHdlbGwgdW5kZXIgdGhpcyk7IHRoaXMgb25seSBndWFyZHMgYWdhaW5zdCBhblxuICAgKiAgYWNjaWRlbnRhbCBlbm9ybW91cyBjYW52YXMgYWxsb2NhdGlvbi4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBTSU1QTEVfVVBTQ0FMRV9NQVhfRElNID0gODE5MjtcbiAgcHJpdmF0ZSBjb29yZFRyYW5zZm9ybTogSUNvb3JkaW5hdGVUcmFuc2Zvcm0gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSB3YW5kSG9zdCE6IFdhbmRUb29sSG9zdDtcbiAgcHJpdmF0ZSBlcmFzZXJIb3N0ITogVmVydGV4RXJhc2VyVG9vbEhvc3Q7XG4gIC8qKiBXYW5kIHNhbXBsaW5nIG1hdHJpeCByZWFkIGJhY2sgZnJvbSB0aGUgcmVuZGVyZWQgdmlld3BvcnQ7IGNhY2hlZCB1bnRpbFxuICAgKiAgdGhlIHZpZXdwb3J0IGNoYW5nZXMgKHNlZSByZWFkYmFja1ZpZXdwb3J0KS4gKi9cbiAgcHJpdmF0ZSB2aWV3cG9ydFBpeGVsczogQ2FjaGVkSW1hZ2VEYXRhIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBUaGUgRE9NIGlkIG9mIHRoZSBlbGVtZW50IE9TRCBpcyBtb3VudGVkIGluIChzaGFyZWQgd2l0aCBQbG90bHkpOyB0aGVcbiAgICogIG9uLWNhbnZhcyB0b29scyBhdHRhY2ggdGhlaXIgb3ZlcmxheXMgaGVyZS4gKi9cbiAgcHJpdmF0ZSBwbG90RGl2ID0gJyc7XG4gIC8qKiBDdXJyZW50IGZpbGUgbmFtZSwgdGFnZ2VkIG9udG8gd2FuZC1jcmVhdGVkIHNoYXBlcy4gKi9cbiAgcHJpdmF0ZSBjdXJyZW50RmlsZU5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgLyoqIDI1Ni1lbnRyeSBSR0IgTFVUIGZvciB0aGUgYWN0aXZlIGNvbG9ybWFwLCBhcHBsaWVkIHRvIGdyYXlzY2FsZSB0aWxlcyB2aWFcbiAgICogIHRoZSB0aWxlLWludmFsaWRhdGVkIHBpeGVsIHBpcGVsaW5lIChtaXJyb3JzIFBsb3RseSdzIGhlYXRtYXAgY29sb3JzY2FsZSkuXG4gICAqICBOdWxsIHdoaWxlIG9wdGlvbnMgcmVzb2x2ZTsgcmVjb2xvcmluZyBpcyBza2lwcGVkIHVudGlsIGl0J3MgYnVpbHQuICovXG4gIHByaXZhdGUgY29sb3JMdXQ6IFJnYltdIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBPbmx5IGdyYXlzY2FsZSBpbWFnZXMgZ2V0IGEgY29sb3JtYXAgKFJHQiB0aWxlcyBwYXNzIHRocm91Z2ggdW50b3VjaGVkKS4gKi9cbiAgcHJpdmF0ZSBpc0dyYXlzY2FsZUltYWdlID0gZmFsc2U7XG4gIHByaXZhdGUgY29sb3JtYXBTdWI6IFN1YnNjcmlwdGlvbiB8IG51bGwgPSBudWxsO1xuICAvKiogTGF0ZXN0IHBlci1jaGFubmVsIGRpc3BsYXkgc3RhdGUgKHdpbmRvdy9nYW1tYS92aXNpYmlsaXR5KSBmcm9tIHRoZSBzdG9yZSxcbiAgICogIHJlYWQgc3luY2hyb25vdXNseSBieSByZWNvbG9yVGlsZS4gQ2hhbm5lbCAwIGRyaXZlcyBncmF5c2NhbGUgd2luZG93aW5nO1xuICAgKiAgUi9HL0IgKGluZGljZXMgMC0yKSBkcml2ZSBSR0IgcGVyLWNoYW5uZWwgd2luZG93aW5nLiAqL1xuICBwcml2YXRlIGNoYW5uZWxTdGF0ZXM6IElDaGFubmVsU3RhdGVbXSA9IFtdO1xuICAvKiogVHJ1ZSBmb3IgbXVsdGljaGFubmVsIGZsdW9yZXNjZW5jZSAoY2hhbm5lbENvdW50ID4gMSwgbm90IFJHQik6IHRpbGVzIGFyZVxuICAgKiAgY29tcG9zaXRlZCBjbGllbnQtc2lkZSBmcm9tIHBlci1jaGFubmVsIHNpbmdsZS1iYW5kIGZldGNoZXMgKHNlZVxuICAgKiAgcmVjb2xvck11bHRpQ2hhbm5lbFRpbGUpIHJhdGhlciB0aGFuIHJlY29sb3JlZCBpbiBwbGFjZS4gKi9cbiAgcHJpdmF0ZSBpc011bHRpQ2hhbm5lbCA9IGZhbHNlO1xuICAvKiogQ291bnQgb2YgcmVhbCBCaW8tRm9ybWF0cyByZXNvbHV0aW9uIGxldmVscyAocGVyLWNoYW5uZWwgdGlsZXMgZXhpc3Qgb25seVxuICAgKiAgaGVyZSkuIEZvciBtdWx0aWNoYW5uZWwgaW1hZ2VzIHRoZSB0aWxlIHNvdXJjZSBpcyBidWlsdCBmcm9tIHRoZXNlIGFsb25lIHNvXG4gICAqICBldmVyeSBkaXNwbGF5ZWQgdGlsZSBzdXBwb3J0cyBhIHBlci1jaGFubmVsIGZldGNoLiAqL1xuICBwcml2YXRlIHJlYWxMZXZlbHMgPSAwO1xuICAvKiogSW52ZXJ0ZWQgYmFja2dyb3VuZCAod2hpdGUgPSB6ZXJvKTogaW52ZXJ0cyB0aGUgZGlzcGxheSB2YWx1ZSBiZWZvcmUgdGhlXG4gICAqICBMVVQgKGdyYXlzY2FsZSkgLyBwZXIgY2hhbm5lbCAoUkdCKS4gKi9cbiAgcHJpdmF0ZSBpbnZlcnRCZyA9IGZhbHNlO1xuICAvKiogQmVhcmVyIHRva2VuIGZvciBPU0QncyBvd24gdGlsZSBmZXRjaGVzIChIdHRwQ2xpZW50IGNhbGxzIGdldCBpdCB2aWEgdGhlXG4gICAqICBpbnRlcmNlcHRvcjsgT1NEJ3MgbG9hZGVyIGRvZXMgbm90LCBzbyB3ZSBwYXNzIGl0IGFzIGFuIGFqYXggaGVhZGVyKS4gKi9cbiAgcHJpdmF0ZSBhdXRoSGVhZGVyczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuICAvKiogV2hldGhlciB0aGUgb3ZlcnZpZXcgbmF2aWdhdG9yIChtaW5pbWFwKSBpcyBzaG93bi4gUmVhZCBhdCB2aWV3ZXIgY3JlYXRpb247XG4gICAqICBhIGNvbnN1bWVyIHRoYXQgZG9lc24ndCB3YW50IGl0IChlLmcuIGEgc21hbGwgZW1iZWRkZWQgcHJldmlldykgc2V0cyBpdCBvZmZcbiAgICogIHZpYSB7QGxpbmsgc2V0TmF2aWdhdG9yVmlzaWJsZX0gYmVmb3JlIHRoZSBmaXJzdCByZW5kZXIuICovXG4gIHByaXZhdGUgbmF2aWdhdG9yVmlzaWJsZSA9IHRydWU7XG4gIC8qKiBJbWFnZSBzbW9vdGhpbmcgKGJpbGluZWFyKS4gYGZhbHNlYCDihpIgbmVhcmVzdC1uZWlnaGJvdXIsIHNvIHpvb21lZC1pbiBwaXhlbHNcbiAgICogIHJlbmRlciBhcyBjcmlzcCBibG9ja3MgKHBpeGVsIGluc3BlY3Rpb24pLiBEZWZhdWx0cyB0byBgZmFsc2VgIHNvIHRoZSBpbWFnZVxuICAgKiAgb3BlbnMgc2hvd2luZyByYXcgcGl4ZWxzLiBSZWFkIGF0IHZpZXdlciBjcmVhdGlvbjsgdG9nZ2xlZCB2aWFcbiAgICogIHtAbGluayBzZXRJbWFnZVNtb290aGluZ0VuYWJsZWR9LiAqL1xuICBwcml2YXRlIHNtb290aGluZ0VuYWJsZWQgPSBmYWxzZTtcblxuICAvKiogT3ZlcmFsbCBkZWFkbGluZSBmb3IgdGhlIC90aWxlcy9pbmZvIHBvbGwgbG9vcC4gQW4gdW5jYWNoZWQgd2hvbGUtc2xpZGVcbiAgICogIGltYWdlIChlLmcuIC5uZHBpKSBpcyBjYWNoZWQgc2VydmVyLXNpZGUgZmlyc3QgKEdDUy0+UFZDKSwgd2hpY2ggY2FuIHRha2VcbiAgICogIHNldmVyYWwgbWludXRlcyDigJQgd2UgcG9sbCAoc2hvcnQgcmVxdWVzdHM7IHRoZSBjYWNoZS1wcm9ncmVzcyBvdmVybGF5XG4gICAqICBzaG93cyB0aGUgd2FpdCkgdW50aWwgaXQncyByZWFkeS4gR2VuZXJvdXMgYmVjYXVzZSBlYWNoIHBvbGwgaXMgY2hlYXA7IG9uXG4gICAqICBleHBpcnkgdGhlIHJlbmRlciBwaXBlbGluZSBnaXZlcyB1cCBhbmQgdGhlIHJvdXRlciBmYWxscyBiYWNrIHRvIFBsb3RseS4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSB0aWxlc0luZm9UaW1lb3V0TXMgPSA2MDAwMDA7IC8vIDEwIG1pblxuXG4gIC8qKiBQZXItY2hhbm5lbCBmaXQtdmlldyB0aWxlIGJ1ZGdldCAodGlsZXMgYXQgdGhlIGNvYXJzZXN0IHJlYWwgbGV2ZWwgw5cgY2hhbm5lbHMpLlxuICAgKiAgQWJvdmUgaXQsIGEgbXVsdGljaGFubmVsIGltYWdlIHJlbmRlcnMgc2VydmVyLWNvbXBvc2l0ZWQgaW5zdGVhZCBvZiBwZXItY2hhbm5lbFxuICAgKiAg4oCUIGl0cyBmdWxsLXJlcyByZWFkcyAoaXQgaGFzIG5vIG92ZXJ2aWV3IHB5cmFtaWQpIHdvdWxkIGJlIHRvbyBtYW55IMOXIE4gY2hhbm5lbHMuXG4gICAqICA2NCAoZS5nLiBhIDR4NCBzaW5nbGUtRk9WIHotc3RhY2sgw5cgNGNoKSBzdGF5cyBwZXItY2hhbm5lbDsgYSB3aG9sZS1zbGlkZVxuICAgKiAgKGh1bmRyZWRz4oCTdGhvdXNhbmRzKSBmYWxscyBiYWNrLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IE1BWF9NVUxUSUNIQU5ORUxfRklUX1RJTEVTID0gMjU2O1xuXG4gIC8qKiBQaXhlbCBkaXNwbGF5IHBpcGVsaW5lICh3aW5kb3cvZ2FtbWEvaW52ZXJ0L2NvbG9ybWFwICsgYWRkaXRpdmUgdGludCkg4oCUXG4gICAqICBzaGFyZWQgYnkgdGlsZSByZWNvbG9yaW5nIGFuZCB0aGUgY29tcG9zaXRlIGV4cG9ydCBzbyB0aGV5IHN0YXkgaWRlbnRpY2FsXG4gICAqICAoc2VlIERpc3BsYXlQaXBlbGluZSkuIEhvc3QgY2xvc3VyZXMgcmVhZCB0aGUgc2VydmljZSdzIGxpdmUgZmllbGRzLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGRpc3BsYXkgPSBuZXcgRGlzcGxheVBpcGVsaW5lKHtcbiAgICBpc0dyYXlzY2FsZTogKCkgPT4gdGhpcy5pc0dyYXlzY2FsZUltYWdlLFxuICAgIGNvbG9yTHV0OiAoKSA9PiB0aGlzLmNvbG9yTHV0LFxuICAgIGNoYW5uZWxTdGF0ZXM6ICgpID0+IHRoaXMuY2hhbm5lbFN0YXRlcyxcbiAgICBpbnZlcnRCZzogKCkgPT4gdGhpcy5pbnZlcnRCZyxcbiAgfSk7XG5cbiAgLyoqIEhpc3RvZ3JhbSArIGF1dG8td2luZG93IHNhbXBsaW5nIChzZWUgSGlzdG9ncmFtU2FtcGxlcikuIENvbnN0cnVjdGVkIGluXG4gICAqICB0aGUgY3RvciBib2R5IGJlY2F1c2UgaXQgY2FwdHVyZXMgdGhlIHJlc29sdmVkIEFQSSBiYXNlIFVSTC4gKi9cbiAgcHJpdmF0ZSBzYW1wbGVyITogSGlzdG9ncmFtU2FtcGxlcjtcblxuICAvKiogU3RhY2stc2xpY2UgY2FjaGUgKyBiYWNrZ3JvdW5kIHByZWxvYWRlciAoc2VlIFNsaWNlQ2FjaGUg4oCUIHJlZmFjdG9yaW5nXG4gICAqICBwbGFuIFN0ZXAgMykuIFRoZSBob3N0IGFjY2Vzc29ycyBhcmUgbGl2ZSBjbG9zdXJlcywgc28gdGhlIGNhY2hlIGFsd2F5c1xuICAgKiAgcmVhZHMgdGhlIHNlcnZpY2UncyBjdXJyZW50IHZpZXdlci9kZXNjcmlwdG9yL3og4oCUIGV4YWN0bHkgdGhlIGZpZWxkcyB0aGVcbiAgICogIG1vdmVkIGNvZGUgdXNlZCB0byByZWFkIGRpcmVjdGx5LiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGNhY2hlID0gbmV3IFNsaWNlQ2FjaGUoe1xuICAgIHZpZXdlcjogKCkgPT4gdGhpcy52aWV3ZXIsXG4gICAgaGFzSW1hZ2U6ICgpID0+ICEhKHRoaXMudmlld2VyICYmIHRoaXMuZGVzY3JpcHRvciAmJiB0aGlzLmluZm9CNjQpLFxuICAgIHNsaWNlQ291bnQ6ICgpID0+IHRoaXMuZGVzY3JpcHRvcj8ueiA/PyAxLFxuICAgIGN1cnJlbnRaOiAoKSA9PiB0aGlzLmN1cnJlbnRaLFxuICAgIGlzTXVsdGlDaGFubmVsOiAoKSA9PiB0aGlzLmlzTXVsdGlDaGFubmVsLFxuICAgIGNoYW5uZWxDb3VudDogKCkgPT4gTWF0aC5tYXgoMSwgdGhpcy5jaGFubmVsU3RhdGVzLmxlbmd0aCB8fCAodGhpcy5kZXNjcmlwdG9yPy5jaGFubmVscyA/PyAxKSksXG4gICAgY2hhbm5lbFZpc2libGU6IChjOiBudW1iZXIpID0+IHRoaXMuY2hhbm5lbFN0YXRlc1tjXT8udmlzaWJsZSAhPT0gZmFsc2UsXG4gICAgYnVpbGRUaWxlU291cmNlOiAoejogbnVtYmVyLCBjaGFubmVsPzogbnVtYmVyKSA9PlxuICAgICAgdGhpcy5idWlsZFRpbGVTb3VyY2UodGhpcy5kZXNjcmlwdG9yISwgdGhpcy5pbmZvQjY0LCB6LCBjaGFubmVsKSxcbiAgICBvbkNvbXBvc2l0ZVNsaWNlQWRkZWQ6ICh6OiBudW1iZXIpID0+IHRoaXMuc2FtcGxlci5jb21wdXRlSW1hZ2VXaW5kb3codGhpcy5kZXNjcmlwdG9yISwgdGhpcy5pbmZvQjY0LCB6KSxcbiAgfSk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBodHRwOiBIdHRwQ2xpZW50LFxuICAgIEBJbmplY3QoVElMRV9BQ0NFU1NfUE9SVCkgcHJpdmF0ZSB0aWxlczogVGlsZUFjY2Vzc1BvcnQsXG4gICAgcHJpdmF0ZSB3YW5kVG9vbDogV2FuZFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgYnJ1c2hUb29sOiBCcnVzaFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgc2FtVG9vbDogU2FtVG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBzYW1Qb2ludFRvb2w6IFNhbVBvaW50VG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBjZWxsU2VnbWVudFRvb2w6IENlbGxTZWdtZW50VG9vbFNlcnZpY2UsXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChDRUxMX1NFR01FTlRFUikgcHJpdmF0ZSBjZWxsU2VnbWVudGVyOiBJQ2VsbFNlZ21lbnRlciB8IG51bGwsXG4gICAgcHJpdmF0ZSBlcmFzZXJUb29sOiBWZXJ0ZXhFcmFzZXJUb29sU2VydmljZSxcbiAgICBwcml2YXRlIHpvb21Ub0JveFRvb2w6IFpvb21Ub0JveFRvb2xTZXJ2aWNlLFxuICAgIHN0b3JlOiBWaXN1YWxpemVyU3RvcmUsXG4gICAgcmVnaW9uU3RvcmU6IFJlZ2lvblN0b3JlLFxuICAgIHByaXZhdGUgc2ltcGxlU3RhY2s6IFNpbXBsZVNsaWNlQWNjZXNzU2VydmljZSxcbiAgICBASW5qZWN0KFZJWl9DT05GSUcpIGNvbmZpZzogVml6Q29uZmlnLFxuICApIHtcbiAgICBzdXBlcihyZWdpb25TdG9yZSwgc3RvcmUpO1xuICAgIHRoaXMuYXBpID0gY29uZmlnLnNsaWRlQ3JvcFNlcnZlcjtcbiAgICB0aGlzLnNhbXBsZXIgPSBuZXcgSGlzdG9ncmFtU2FtcGxlcih0aGlzLmh0dHAsIHRoaXMuYXBpLCB7XG4gICAgICByZWFsTGV2ZWxzOiAoKSA9PiB0aGlzLnJlYWxMZXZlbHMsXG4gICAgICBjaGFubmVsQ291bnQ6IChkKSA9PiB0aGlzLmNoYW5uZWxTdGF0ZXMubGVuZ3RoIHx8IChkLmNoYW5uZWxzID8/IDEpLFxuICAgICAgaXNHcmF5c2NhbGU6ICgpID0+IHRoaXMuaXNHcmF5c2NhbGVJbWFnZSxcbiAgICAgIC8vIE51ZGdlIHRoZSBjaGFubmVsLXN0YXRlcyBzdHJlYW0gc28gdGhlIHBhbmUgcmUtcmVhZHMgZ2V0SGlzdG9ncmFtIG5vdyxcbiAgICAgIC8vIGluIGNhc2UgaXRzIGJvdW5kZWQgcmV0cnkgd2luZG93IGFscmVhZHkgbGFwc2VkLlxuICAgICAgb25DaGFubmVsSGlzdG9ncmFtc1NhbXBsZWQ6ICgpID0+IHRoaXMuc3RvcmUuc2V0Q2hhbm5lbFN0YXRlcyh0aGlzLnN0b3JlLmN1cnJlbnRDaGFubmVsU3RhdGVzKCkpLFxuICAgICAgb25HcmF5V2luZG93U2FtcGxlZDogKG1pbiwgbWF4KSA9PiB0aGlzLnNlZWRHcmF5V2luZG93KG1pbiwgbWF4KSxcbiAgICB9KTtcbiAgICB0aGlzLmVuc3VyZUNvbG9ybWFwU3Vic2NyaXB0aW9uKCk7XG4gIH1cblxuICBwcml2YXRlIHJlYWRvbmx5IHN0YWNrTG9hZGluZyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgcHJpdmF0ZSByZWFkb25seSBzdGFja0xvYWRpbmdQcm9ncmVzcyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PG51bWJlcj4oMCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgYXV0b3NjYWxlRXZlbnQkID0gbmV3IFN1YmplY3Q8YW55PigpO1xuICAvKiogVmlzaWJsZSBpbWFnZSByZWdpb24gKGZ1bGwtaW1hZ2UgcGl4ZWwgY29vcmRzKSBlbWl0dGVkIHdoZW4gdGhlIHZpZXdcbiAgICogIHNldHRsZXMsIHNvIHRoZSBpbnRlbnNpdHkgaW5zZXQgY2FuIHJlLXNhbXBsZSBhdCB0aGUgY3VycmVudCB6b29tIGxldmVsLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHZpZXdwb3J0Q2hhbmdlJCA9IG5ldyBTdWJqZWN0PHsgeDogbnVtYmVyOyB5OiBudW1iZXI7IHdpZHRoOiBudW1iZXI7IGhlaWdodDogbnVtYmVyIH0+KCk7XG4gIC8vIFJlZ2lvbiBzdGF0ZSAocmVnaW9ucywgc2VsZWN0aW9uLCB0aGUgdXBkYXRlIGV2ZW50KSBsaXZlcyBpbiB0aGUgc2hhcmVkXG4gIC8vIFJlZ2lvblN0b3JlOyB0aGUgSVJlZ2lvblN0b3JlIG1ldGhvZHMgYmVsb3cgZGVsZWdhdGUgdG8gaXQuXG4gIHByaXZhdGUgcmVhZG9ubHkgaW50ZW5zaXR5UHJvZmlsZSQgPSBuZXcgU3ViamVjdDxJbnRlbnNpdHlQcm9maWxlW10+KCk7XG4gIC8vIEltYWdlIG1ldGFkYXRhIGFuZCBjbGFzc2lmaWNhdGlvbiBjb2xvdXJzIGxpdmUgaW4gdGhlIHNoYXJlZCBWaXN1YWxpemVyU3RvcmUuXG5cbiAgLyoqXG4gICAqIFN1YnNjcmliZSB0byB0aGUgc2hhcmVkIFZpc3VhbGl6ZXJTdG9yZSBjb2xvcm1hcC9yZXZlcnNlIHNvIE9TRCByZWNvbG9ycyBpblxuICAgKiBsb2NrLXN0ZXAgd2l0aCBQbG90bHkuIElkZW1wb3RlbnQgYW5kIHNlbGYtaGVhbGluZzogdGhpcyBzZXJ2aWNlIGlzIGEgcm9vdFxuICAgKiBzaW5nbGV0b24sIGJ1dCBgdW5zdWJzY3JpYmUoKWAgKGNhbGxlZCBvbiBWaXN1YWxpemVyQ29tcG9uZW50IGRlc3Ryb3kpXG4gICAqIHRlYXJzIHRoZSBzdWJzY3JpcHRpb24gZG93biDigJQgYW5kIHRoZSBjb25zdHJ1Y3RvciBuZXZlciBydW5zIGFnYWluLiBTb1xuICAgKiBgcGxvdCgpYCBjYWxscyB0aGlzIHRvIHJlLWVzdGFibGlzaCBpdCBhZnRlciBhIGNvbXBvbmVudCB0ZWFyZG93bi9yZWNyZWF0ZVxuICAgKiAoZS5nLiBzd2l0Y2hpbmcgaW1hZ2VzKSwgb3RoZXJ3aXNlIGNvbG9ybWFwIGNoYW5nZXMgd291bGQgYmUgc2lsZW50bHlcbiAgICogZHJvcHBlZCBvbiBldmVyeSBpbWFnZSBhZnRlciB0aGUgZmlyc3Qgc3dpdGNoLlxuICAgKi9cbiAgcHJpdmF0ZSBlbnN1cmVDb2xvcm1hcFN1YnNjcmlwdGlvbigpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5jb2xvcm1hcFN1YikgcmV0dXJuO1xuICAgIC8vIENvbG9ybWFwL3JldmVyc2UgbGl2ZSBpbiB0aGUgc2hhcmVkIFZpc3VhbGl6ZXJTdG9yZSDigJQgdGhlIHNpbmdsZSBzb3VyY2VcbiAgICAvLyBvZiB0cnV0aCBmb3IgYm90aCBiYWNrZW5kcy4gUmVidWlsZCB0aGUgTFVUIGFuZCByZS1ydW4gdGhlIHBpeGVsIHBpcGVsaW5lXG4gICAgLy8gd2hlbmV2ZXIgZWl0aGVyIGNoYW5nZXMsIHNvIE9TRCByZWNvbG9ycyBpbiBsb2NrLXN0ZXAgd2l0aCBQbG90bHkuXG4gICAgLy8gQ29sb3JtYXAvcmV2ZXJzZSArIHBlci1jaGFubmVsIHdpbmRvdy9nYW1tYS92aXNpYmlsaXR5ICsgaW52ZXJ0IGFsbCBsaXZlIGluXG4gICAgLy8gdGhlIHNoYXJlZCBWaXN1YWxpemVyU3RvcmUuIFJlYnVpbGQgdGhlIExVVCBhbmQgcmUtcnVuIHRoZSBwaXhlbCBwaXBlbGluZVxuICAgIC8vIHdoZW5ldmVyIGFueSBvZiB0aGVtIGNoYW5nZXMsIHNvIHRoZSBDaGFubmVscyAmIEhpc3RvZ3JhbSBwYW5lIHVwZGF0ZXMgdGhlXG4gICAgLy8gaW1hZ2UgbGl2ZSBhbmQgT1NEIHN0YXlzIGluIGxvY2stc3RlcCB3aXRoIFBsb3RseS5cbiAgICB0aGlzLmNvbG9ybWFwU3ViID0gY29tYmluZUxhdGVzdChbXG4gICAgICB0aGlzLnN0b3JlLmdldENvbG9ybWFwKCksXG4gICAgICB0aGlzLnN0b3JlLmdldFJldmVyc2VTY2FsZSgpLFxuICAgICAgdGhpcy5zdG9yZS5nZXRDaGFubmVsU3RhdGVzKCksXG4gICAgICB0aGlzLnN0b3JlLmdldEludmVydCgpLFxuICAgIF0pLnN1YnNjcmliZSgoW2NtLCByZXYsIGNoYW5uZWxzLCBpbnZlcnRdKSA9PiB7XG4gICAgICB0aGlzLmNvbG9yTHV0ID0gYnVpbGRDb2xvcm1hcEx1dChjbT8uZGF0YT8udmFsdWUsICEhcmV2KTtcbiAgICAgIHRoaXMuY2hhbm5lbFN0YXRlcyA9IGNoYW5uZWxzO1xuICAgICAgdGhpcy5pbnZlcnRCZyA9ICEhaW52ZXJ0O1xuICAgICAgLy8gTXVsdGljaGFubmVsOiBlYWNoIGNoYW5uZWwgaXMgaXRzIG93biBUaWxlZEltYWdlIOKAlCB2aXNpYmlsaXR5IGlzIHRoZVxuICAgICAgLy8gaW1hZ2UncyBvcGFjaXR5ICh3aW5kb3cvZ2FtbWEvY29sb3VyIGFyZSBhcHBsaWVkIGJ5IHJlY29sb3JDaGFubmVsVGlsZVxuICAgICAgLy8gb24gdGhlIGludmFsaWRhdGUgYmVsb3cpLiBSZS1hcHBseWluZyB0aGUgY3VycmVudCBzbGljZSdzIHJldmVhbCBwaWNrcyB1cFxuICAgICAgLy8gdGhlIG5ldyBwZXItY2hhbm5lbCB2aXNpYmlsaXR5IChjYWNoZWQgc2xpY2VzIHN0YXkgaGlkZGVuKS5cbiAgICAgIGlmICh0aGlzLmlzTXVsdGlDaGFubmVsKSB7XG4gICAgICAgIHRoaXMuY2FjaGUucmV2ZWFsQ2hhbm5lbFNsaWNlKHRoaXMuY3VycmVudFopO1xuICAgICAgfVxuICAgICAgLy8gcmVxdWVzdEludmFsaWRhdGUodHJ1ZSkgcmVzdG9yZXMgZWFjaCB0aWxlIHRvIGl0cyBvcmlnaW5hbCBkYXRhIGJlZm9yZVxuICAgICAgLy8gcmUtcnVubmluZyByZWNvbG9yVGlsZSwgc28gYSBjaGFuZ2UgYWx3YXlzIG1hcHMgYWZyZXNoIChubyBjb21wb3VuZGluZykuXG4gICAgICAvLyBSR0Igbm93IHJlY29sb3JzIHRvbyAocGVyLWNoYW5uZWwgd2luZG93L3Zpc2liaWxpdHkpLCBzbyBkb24ndCBnYXRlIG9uXG4gICAgICAvLyBncmF5c2NhbGUuXG4gICAgICB0aGlzLmludmFsaWRhdGVEaXNwbGF5KCk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogU2VlZCB0aGUgSW50ZW5zaXR5IGNoYW5uZWwgd2l0aCBhIG1lYXN1cmVkIGF1dG8td2luZG93IHdoaWxlIGl0J3Mgc3RpbGxcbiAgICogIGF0IGZ1bGwgcmFuZ2UgKG5ldmVyIGNsb2JiZXIgdGhlIHVzZXIncyBtYW51YWwgd2luZG93KTsgaWYgdGhlIHVzZXJcbiAgICogIGFscmVhZHkgd2luZG93ZWQsIGp1c3QgcmUtaW52YWxpZGF0ZSBzbyBwYWludGVkIHRpbGVzIHBpY2sgdGhlIExVVCB1cC4gKi9cbiAgcHJpdmF0ZSBzZWVkR3JheVdpbmRvdyhtaW46IG51bWJlciwgbWF4OiBudW1iZXIpOiB2b2lkIHtcbiAgICBjb25zdCBjaDAgPSB0aGlzLnN0b3JlLmN1cnJlbnRDaGFubmVsU3RhdGVzKClbMF07XG4gICAgaWYgKGNoMCAmJiBjaDAubWluID09PSAwICYmIGNoMC5tYXggPT09IDI1NSkge1xuICAgICAgdGhpcy5zdG9yZS5zZXRDaGFubmVsU3RhdGUoMCwgeyBtaW4sIG1heCB9KTtcbiAgICB9IGVsc2UgaWYgKHRoaXMudmlld2VyICYmIHRoaXMuY29sb3JMdXQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgICh0aGlzLnZpZXdlciBhcyBhbnkpLndvcmxkLnJlcXVlc3RJbnZhbGlkYXRlKHRydWUpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8qIG5vLW9wICovXG4gICAgICB9XG4gICAgICB0cnkge1xuICAgICAgICAodGhpcy52aWV3ZXIgYXMgYW55KS5uYXZpZ2F0b3I/LndvcmxkPy5yZXF1ZXN0SW52YWxpZGF0ZSh0cnVlKTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvKiBuby1vcCAqL1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIOKUgOKUgCBJRGF0YVJlbmRlcmVyIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKlxuICAgKiBGZXRjaCB0aGUgdGlsZS1zb3VyY2UgZGVzY3JpcHRvciBmb3IgdGhlIHNlbGVjdGVkIGZpbGUuIFRoZSBgaW5mb2AgcGFyYW0gaXNcbiAgICogdGhlIGJhc2U2NCBSYXdGaWxlSW5mbyB0aGUgcmVzdCBvZiB0aGUgQVBJIHVzZXM7IHRoZSBHRVQgZ29lcyB0aHJvdWdoIHRoZVxuICAgKiBhdXRoIGludGVyY2VwdG9yIChCZWFyZXIpLiBXZSBhbHNvIGdyYWIgYSB0b2tlbiBoZXJlIGZvciBPU0QncyBvd24gdGlsZVxuICAgKiBmZXRjaGVzIChpdHMgbG9hZGVyIGJ5cGFzc2VzIEh0dHBDbGllbnQpLlxuICAgKi9cbiAgYXN5bmMgbG9hZChpbWFnZUluZm86IElJbWFnZUluZm8sIHpJbmRleDogbnVtYmVyKTogUHJvbWlzZTxPc2RMb2FkZWQ+IHtcbiAgICBjb25zdCBmaWxlbmFtZSA9IGltYWdlSW5mbz8uZmlsZU5hbWU7XG4gICAgLy8gQSBkaWZmZXJlbnQgaW1hZ2Ugd2FzIHNlbGVjdGVkIOKAlCBzdG9wIHRoZSBwcmV2aW91cyBzdGFjaydzIGJhY2tncm91bmRcbiAgICAvLyBsb2FkaW5nIGltbWVkaWF0ZWx5IHJhdGhlciB0aGFuIGxldHRpbmcgaXQgZmluaXNoIGJlaGluZCB0aGUgbmV3IGltYWdlLlxuICAgIGlmIChmaWxlbmFtZSAmJiB0aGlzLmN1cnJlbnRGaWxlTmFtZSAmJiBmaWxlbmFtZSAhPT0gdGhpcy5jdXJyZW50RmlsZU5hbWUpIHtcbiAgICAgIHRoaXMuY2FjaGUuY2FuY2VsQmFja2dyb3VuZExvYWQoKTtcbiAgICAgIHRoaXMucmV2b2tlU2ltcGxlRnVsbFJlc1VybHMoKTtcbiAgICB9XG4gICAgdGhpcy5zaW1wbGVTdGFjay5ub3RlQWN0aXZlRmlsZShmaWxlbmFtZSk7XG4gICAgLy8gU2ltcGxlIChpbi1tZW1vcnkgLyBub24tdGlsZWQpIGltYWdlOiBza2lwIHRoZSB0aWxlIHNlcnZlciBlbnRpcmVseSDigJQgbm9cbiAgICAvLyBnZXRTZWxlY3RlZEluZm9CNjQsIG5vIC90aWxlcy9pbmZvIHBvbGwuIGB1cmxzW3pJbmRleF1gIGlzIGEgY29tcGxldGUgaW1hZ2VcbiAgICAvLyBPU0Qgb3BlbnMgdmlhIGl0cyBzaW5nbGUtaW1hZ2Ugc291cmNlIChzZWUgcGxvdCgpKS5cbiAgICBpZiAodGhpcy5zaW1wbGVTdGFjay5pc1NpbXBsZShpbWFnZUluZm8pKSB7XG4gICAgICByZXR1cm4gdGhpcy5sb2FkU2ltcGxlKGltYWdlSW5mbywgekluZGV4KTtcbiAgICB9XG4gICAgY29uc3QgaW5mb0I2NCA9IHRoaXMudGlsZXMuZ2V0U2VsZWN0ZWRJbmZvQjY0KCk7XG4gICAgaWYgKCFpbmZvQjY0KSByZXR1cm4geyBkZXNjcmlwdG9yOiBudWxsLCBpbmZvQjY0OiAnJywgejogekluZGV4IHx8IDAsIGZpbGVuYW1lIH07XG5cbiAgICB0cnkge1xuICAgICAgdGhpcy5hdXRoSGVhZGVycyA9IGF3YWl0IHRoaXMudGlsZXMuZ2V0QXV0aEhlYWRlcnMoKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHRoaXMuYXV0aEhlYWRlcnMgPSB7fTsgLy8gZmFsbCBiYWNrIHRvIGNvb2tpZSBhdXRoIChhamF4V2l0aENyZWRlbnRpYWxzKVxuICAgIH1cbiAgICAvLyBQb2xsIC90aWxlcy9pbmZvOiB0aGUgYmFja2VuZCByZXR1cm5zIDIwMiB3aGlsZSB0aGUgc291cmNlIGZpbGUgaXMgc3RpbGxcbiAgICAvLyBiZWluZyBjYWNoZWQgKEdDUy0+UFZDKSDigJQgaXQga2lja3MgdGhlIGRvd25sb2FkIG9mZiBpbiB0aGUgYmFja2dyb3VuZCwgc29cbiAgICAvLyBlYWNoIHJlcXVlc3QgaXMgc2hvcnQgKG5vIGxvbmctaGVsZCBjb25uZWN0aW9uIHRvIHRyaXAgaW5ncmVzcy9wcm94eSBvclxuICAgIC8vIGNsaWVudCB0aW1lb3V0cyksIGFuZCAyMDAgd2l0aCB0aGUgZGVzY3JpcHRvciBvbmNlIGl0J3MgcmVhZHkuIFRoZVxuICAgIC8vIGNhY2hlLXByb2dyZXNzIG92ZXJsYXkgc2hvd3MgcHJvZ3Jlc3MgZHVyaW5nIHRoZSB3YWl0LiBXZSBnaXZlIHVwIGFmdGVyXG4gICAgLy8gdGlsZXNJbmZvVGltZW91dE1zIHNvIHRoZSByZW5kZXIgcGlwZWxpbmUgbmV2ZXIgaGFuZ3MgKHRoZSByb3V0ZXIgdGhlblxuICAgIC8vIGZhbGxzIGJhY2sgdG8gUGxvdGx5IGZvciB0aGlzIGltYWdlKS5cbiAgICBjb25zdCB1cmwgPSBgJHt0aGlzLmFwaX10aWxlcy9pbmZvP2luZm89JHtpbmZvQjY0fWA7XG4gICAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgdGhpcy50aWxlc0luZm9UaW1lb3V0TXM7XG4gICAgZm9yICg7Oykge1xuICAgICAgaWYgKERhdGUubm93KCkgPiBkZWFkbGluZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3RpbGVzL2luZm8gbm90IHJlYWR5IGJlZm9yZSBkZWFkbGluZSDigJQgZmlsZSBzdGlsbCBjYWNoaW5nJyk7XG4gICAgICB9XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXNwID0gYXdhaXQgZmlyc3RWYWx1ZUZyb20oXG4gICAgICAgICAgdGhpcy5odHRwLmdldDxUaWxlRGVzY3JpcHRvcj4odXJsLCB7IG9ic2VydmU6ICdyZXNwb25zZScgfSkucGlwZSh0aW1lb3V0KDQ1MDAwKSksXG4gICAgICAgICk7XG4gICAgICAgIGlmIChyZXNwLnN0YXR1cyA9PT0gMjAwICYmIHJlc3AuYm9keSkge1xuICAgICAgICAgIHRoaXMuZGVzY3JpcHRvciA9IHJlc3AuYm9keTtcbiAgICAgICAgICByZXR1cm4geyBkZXNjcmlwdG9yOiByZXNwLmJvZHksIGluZm9CNjQsIHo6IHpJbmRleCB8fCAwLCBmaWxlbmFtZSB9O1xuICAgICAgICB9XG4gICAgICAgIC8vIDIwMiBBY2NlcHRlZCAob3IgZW1wdHkgYm9keSkg4oaSIHN0aWxsIGNhY2hpbmc7IGZhbGwgdGhyb3VnaCBhbmQgcmUtcG9sbC5cbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAvLyBUb2xlcmF0ZSB0cmFuc2llbnQgZmFpbHVyZXMgV0hJTEUgdGhlIGZpbGUgaXMgYmVpbmcgcHJlcGFyZWQ6IGEgc2xvd1xuICAgICAgICAvLyBtZXRhZGF0YSByZWFkIG9uIGEgbXVsdGktR0IgZmlsZSAoSUZEIHdhbGspLCBhIDV4eCBtaWQtcGlwZWxpbmUsIGFcbiAgICAgICAgLy8gcHJveHkgaGljY3VwLiBBIHNpbmdsZSBiYWQgcG9sbCBtdXN0IE5PVCBkcm9wIHVzIHRvIFBsb3RseSDigJQgYSBsYXJnZVxuICAgICAgICAvLyBpbWFnZSBhbHdheXMgcGFzc2VzIHRocm91Z2ggdGhpcyBidXN5IHdpbmRvdywgc28gYmFpbGluZyBoZXJlIGlzIHdoeVxuICAgICAgICAvLyBpdCBcImFsd2F5cyBlbmRzIHVwIG9uIFBsb3RseVwiLiBLZWVwIHBvbGxpbmcgdW50aWwgcmVhZHkgb3IgZGVhZGxpbmUuXG4gICAgICAgIGNvbnNvbGUud2FybignW09TRF0gdGlsZXMvaW5mbyBwb2xsIHJldHJ5JywgZXJyKTtcbiAgICAgIH1cbiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyKSA9PiBzZXRUaW1lb3V0KHIsIDE1MDApKTtcbiAgICB9XG4gIH1cblxuICAvKiogQnVpbGQgdGhlIGBwbG90KClgIHBheWxvYWQgZm9yIGEgc2ltcGxlICh0aWxlZDpmYWxzZSkgaW1hZ2U6IGEgc2luZ2xlLWxldmVsXG4gICAqICBkZXNjcmlwdG9yIHNpemVkIGZyb20gYHRydWVJbWFnZVNpemVgLCBwbHVzIHRoZSBkaXJlY3RseS1sb2FkYWJsZSBVUkwg4oCUXG4gICAqICByZXNvbHZlZCBhbmQgZmV0Y2hlZCB2aWEge0BsaW5rIFNpbXBsZVNsaWNlQWNjZXNzU2VydmljZX0gKHNoYXJlZCB3aXRoXG4gICAqICBuYXBhcmktanM7IHNlZSBpdHMgZG9jcyBmb3Igd2h5IHRoaXMgY2FuJ3QgYmUgYSByYXcgYGZldGNoKClgL2A8aW1nPmApLiAqL1xuICBwcml2YXRlIGFzeW5jIGxvYWRTaW1wbGUoaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvLCB6SW5kZXg6IG51bWJlcik6IFByb21pc2U8T3NkTG9hZGVkPiB7XG4gICAgY29uc3QgeiA9IHpJbmRleCB8fCAwO1xuICAgIGNvbnN0IGZpbGVuYW1lID0gaW1hZ2VJbmZvPy5maWxlTmFtZTtcbiAgICB0aGlzLmF1dGhIZWFkZXJzID0ge307XG4gICAgY29uc3QgW3dpZHRoLCBoZWlnaHRdID0gaW1hZ2VJbmZvLnRydWVJbWFnZVNpemUgPz8gWzAsIDBdO1xuICAgIGNvbnN0IHJhd1VybCA9IHRoaXMuc2ltcGxlU3RhY2sudXJsRm9yKGltYWdlSW5mbywgeik7XG4gICAgbGV0IHVybDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIGlmIChyYXdVcmwpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHByZXZpZXdVcmwgPSBhd2FpdCB0aGlzLnNpbXBsZVN0YWNrLmZldGNoQXNCbG9iVXJsKHJhd1VybCk7XG4gICAgICAgIC8vIFVwc2NhbGUgdGhlIChkb3duc2NhbGVkKSBwcmV2aWV3IHRvIGZ1bGwgcmVzb2x1dGlvbiBzbyBPU0QncyB3b3JsZFxuICAgICAgICAvLyBtYXRjaGVzIHRoZSBmdWxsLXJlcyBST0kgY29vcmRpbmF0ZSBzcGFjZSDigJQgc2VlIHRvRnVsbFJlc1VybC5cbiAgICAgICAgdXJsID0gYXdhaXQgdGhpcy50b0Z1bGxSZXNVcmwocHJldmlld1VybCwgd2lkdGgsIGhlaWdodCk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdbT1NEXSBzaW1wbGUtbW9kZSBzbGljZSBmZXRjaCBmYWlsZWQnLCBlcnIpO1xuICAgICAgfVxuICAgIH1cbiAgICAvLyBObyBsb2FkYWJsZSBVUkwgKGVtcHR5IHVybHNbXSBvciB0aGUgZmV0Y2ggZmFpbGVkKTogc2lnbmFsIFwiY291bGRuJ3RcbiAgICAvLyBsb2FkXCIgd2l0aCBhIG51bGwgZGVzY3JpcHRvciDigJQgc2FtZSBhcyB0aGUgdGlsZWQgcGF0aCB3aGVuIHRoZXJlJ3Mgbm9cbiAgICAvLyBzZWxlY3RlZC1maWxlIGluZm8g4oCUIHNvIHBsb3QoKSByZXR1cm5zIGZhbHNlIChpdHMgYGlmICghZClgIGd1YXJkKSBhbmRcbiAgICAvLyB0aGUgcm91dGVyIGNhbiBmYWxsIGJhY2ssIGluc3RlYWQgb2YgbW91bnRpbmcgYW4gPGltZz4gd2l0aCBhbiB1bmRlZmluZWRcbiAgICAvLyBzcmMgdGhhdCB0aHJvd3MgYXQgcnVudGltZS5cbiAgICBpZiAoIXVybCkge1xuICAgICAgY29uc29sZS53YXJuKCdbT1NEXSBzaW1wbGUtbW9kZSBzbGljZSBoYXMgbm8gbG9hZGFibGUgVVJMOyBza2lwcGluZyBPU0QgcmVuZGVyJywgcmF3VXJsKTtcbiAgICAgIHJldHVybiB7IGRlc2NyaXB0b3I6IG51bGwsIGluZm9CNjQ6ICcnLCB6LCBmaWxlbmFtZSB9O1xuICAgIH1cbiAgICBjb25zdCBtZXRhID0gaW1hZ2VJbmZvLmltYWdlTWV0YT8uWzBdO1xuICAgIGNvbnN0IGRlc2NyaXB0b3I6IFRpbGVEZXNjcmlwdG9yID0ge1xuICAgICAgd2lkdGgsXG4gICAgICBoZWlnaHQsXG4gICAgICB0aWxlU2l6ZTogMCwgICAgICAgICAgICAvLyB1bnVzZWQ6IHNpbXBsZSBtb2RlIG5ldmVyIGNhbGxzIGJ1aWxkVGlsZVNvdXJjZVxuICAgICAgejogMSwgICAgICAgICAgICAgICAgICAgLy8gc2luZ2xlIGZyYW1lXG4gICAgICBjaGFubmVsczogbWV0YT8ucmdiQ2hhbm5lbHMgPz8gKGltYWdlSW5mby5pc0dyYXlzY2FsZSA/IDEgOiAzKSxcbiAgICAgIG11bHRpY2hhbm5lbDogZmFsc2UsXG4gICAgICByZWFsTGV2ZWxzOiAxLFxuICAgICAgbGV2ZWxzOiBbeyByZXM6IDAsIHdpZHRoLCBoZWlnaHQgfV0sXG4gICAgICBtcHBYOiBtZXRhPy5tcHBYID8/IDAsXG4gICAgICBtcHBZOiBtZXRhPy5tcHBZID8/IDAsXG4gICAgfTtcbiAgICByZXR1cm4ge1xuICAgICAgZGVzY3JpcHRvcixcbiAgICAgIGluZm9CNjQ6ICcnLFxuICAgICAgeixcbiAgICAgIGZpbGVuYW1lLFxuICAgICAgc2ltcGxlOiB0cnVlLFxuICAgICAgdXJsLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogT1NEJ3MgYHt0eXBlOidpbWFnZSd9YCBzb3VyY2Ugc2l6ZXMgaXRzIGNvb3JkaW5hdGUgd29ybGQgdG8gdGhlIGltYWdlJ3NcbiAgICogTkFUVVJBTCBwaXhlbHMuIEEgZm9sZGVyLXN0YWNrIHNsaWNlIGlzIGEgc2VydmVyIGAvcHJldmlld2Agd2hvc2UgcGl4ZWwgc2l6ZVxuICAgKiBuZWVkbid0IG1hdGNoIHRoZSBmdWxsLXJlc29sdXRpb24gaW1hZ2Ug4oCUIGl0IG1heSBiZSBkb3duc2NhbGVkIChsYXJnZVxuICAgKiBpbWFnZXMpIE9SIGxhcmdlciB0aGFuIHRoZSBkaW1lbnNpb25zIGAvbWV0YWRhdGFgIHJlcG9ydHMuIEVpdGhlciB3YXkgT1NEJ3NcbiAgICogd29ybGQgd291bGQgZGlmZmVyIGZyb20gdGhlIGZ1bGwtcmVzIFJPSSBjb29yZGluYXRlIHNwYWNlIChRdVBhdGggZ2VvanNvbixcbiAgICogaW4gbGV2ZWwtMCBwaXhlbHMpOiBhIHNtYWxsZXIgcHJldmlldyByZW5kZXJzIFJPSXMgb3ZlcnNpemVkLCBhIGxhcmdlciBvbmVcbiAgICogcmVuZGVycyB0aGVtIHVuZGVyc2l6ZWQvb2Zmc2V0LiBSZXNhbXBsZSB0aGUgcHJldmlldyB0byBFWEFDVExZIHRoZVxuICAgKiBmdWxsLXJlc29sdXRpb24gZGltZW5zaW9ucyAoYmx1cnJ5IGJ1dCBwb3NpdGlvbmFsbHkgZXhhY3Qg4oCUIHNjYWxpbmcgdXAgb3JcbiAgICogZG93biBhcyBuZWVkZWQpIHNvIE9TRCdzIHdvcmxkIG1hdGNoZXMgdGhlIFJPSSBjb29yZGluYXRlIHNwYWNlLCB3aGlsZSBzdGlsbFxuICAgKiB1c2luZyBPU0QncyByZWxpYWJsZSBzaW5nbGUtaW1hZ2UgcmVuZGVyZXIuIENhY2hlZCBwZXIgcHJldmlldyBVUkwgKHJldm9rZWRcbiAgICogb24gZmlsZSBjaGFuZ2UpOyBhIG5vLW9wIHdoZW4gdGhlIHByZXZpZXcgaXMgYWxyZWFkeSBleGFjdGx5IGZ1bGwtcmVzIChzbWFsbFxuICAgKiBpbWFnZXMsIGluLW1lbW9yeSBwaXBlbGluZSBibG9icyksIHdoZW4gdGhlIGRpbXMgYXJlIHVua25vd24sIG9yIHdoZW5cbiAgICogdGhleSdyZSBpbXBsYXVzaWJseSBsYXJnZSAoZ3VhcmRzIGFnYWluc3QgYW4gZW5vcm1vdXMgY2FudmFzKS4gKGppdC11aSM5MylcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgdG9GdWxsUmVzVXJsKHByZXZpZXdVcmw6IHN0cmluZywgd2lkdGg6IG51bWJlciwgaGVpZ2h0OiBudW1iZXIpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGlmICghd2lkdGggfHwgIWhlaWdodCB8fCBNYXRoLm1heCh3aWR0aCwgaGVpZ2h0KSA+IHRoaXMuU0lNUExFX1VQU0NBTEVfTUFYX0RJTSkgcmV0dXJuIHByZXZpZXdVcmw7XG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5zaW1wbGVGdWxsUmVzVXJscy5nZXQocHJldmlld1VybCk7XG4gICAgaWYgKGNhY2hlZCkgcmV0dXJuIGNhY2hlZDtcbiAgICBsZXQgaW1nOiBIVE1MSW1hZ2VFbGVtZW50O1xuICAgIHRyeSB7XG4gICAgICBpbWcgPSBhd2FpdCB0aGlzLmxvYWRJbWFnZUVsKHByZXZpZXdVcmwpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIHByZXZpZXdVcmw7IC8vIGNvdWxkbid0IGRlY29kZSDigJQgbGV0IE9TRCB0cnkgdGhlIFVSTCBhcy1pc1xuICAgIH1cbiAgICAvLyBBbHJlYWR5IHRoZSBleGFjdCBmdWxsLXJlcyB3b3JsZCDigJQgbm8gcmVzYW1wbGUgbmVlZGVkLiAoQSBwcmV2aWV3IHRoYXQgaXNcbiAgICAvLyBsYXJnZXIgT1Igc21hbGxlciB0aGFuIGZ1bGwtcmVzIG11c3Qgc3RpbGwgYmUgcmVzaXplZCBzbyBPU0QncyB3b3JsZCA9PVxuICAgIC8vIHRoZSBST0kgY29vcmRpbmF0ZSBzcGFjZTsgb25seSBhbiBleGFjdCBtYXRjaCBpcyBza2lwcGFibGUuKVxuICAgIGlmIChpbWcubmF0dXJhbFdpZHRoID09PSB3aWR0aCAmJiBpbWcubmF0dXJhbEhlaWdodCA9PT0gaGVpZ2h0KSByZXR1cm4gcHJldmlld1VybDtcbiAgICBjb25zdCBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICBjYW52YXMud2lkdGggPSB3aWR0aDtcbiAgICBjYW52YXMuaGVpZ2h0ID0gaGVpZ2h0O1xuICAgIGNvbnN0IGN0eCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpO1xuICAgIGlmICghY3R4KSByZXR1cm4gcHJldmlld1VybDtcbiAgICBjdHguZHJhd0ltYWdlKGltZywgMCwgMCwgd2lkdGgsIGhlaWdodCk7IC8vIHNjYWxlIHByZXZpZXcgKHVwIG9yIGRvd24pIHRvIGZ1bGwtcmVzXG4gICAgY29uc3QgYmxvYiA9IGF3YWl0IG5ldyBQcm9taXNlPEJsb2IgfCBudWxsPigocmVzb2x2ZSkgPT4gY2FudmFzLnRvQmxvYihyZXNvbHZlKSk7XG4gICAgaWYgKCFibG9iKSByZXR1cm4gcHJldmlld1VybDtcbiAgICBjb25zdCBmdWxsUmVzVXJsID0gVVJMLmNyZWF0ZU9iamVjdFVSTChibG9iKTtcbiAgICB0aGlzLnNpbXBsZUZ1bGxSZXNVcmxzLnNldChwcmV2aWV3VXJsLCBmdWxsUmVzVXJsKTtcbiAgICByZXR1cm4gZnVsbFJlc1VybDtcbiAgfVxuXG4gIC8qKiBMb2FkIGEgVVJMIGludG8gYW4gSFRNTEltYWdlRWxlbWVudCAocmVzb2x2ZXMgb25jZSBkZWNvZGVkKS4gVXNlc1xuICAgKiAgZG9jdW1lbnQuY3JlYXRlRWxlbWVudCByYXRoZXIgdGhhbiBgbmV3IEltYWdlKClgIGJlY2F1c2UgdGhpcyBmaWxlJ3NcbiAgICogIGBJbWFnZWAgaW1wb3J0IGlzIGltYWdlLWpzJ3MgZGVjb2Rlciwgbm90IHRoZSBET00gZWxlbWVudC4gKi9cbiAgcHJpdmF0ZSBsb2FkSW1hZ2VFbCh1cmw6IHN0cmluZyk6IFByb21pc2U8SFRNTEltYWdlRWxlbWVudD4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBpbWcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTtcbiAgICAgIGltZy5vbmxvYWQgPSAoKSA9PiByZXNvbHZlKGltZyk7XG4gICAgICBpbWcub25lcnJvciA9IChlKSA9PiByZWplY3QoZSk7XG4gICAgICBpbWcuc3JjID0gdXJsO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSByZXZva2VTaW1wbGVGdWxsUmVzVXJscygpOiB2b2lkIHtcbiAgICBmb3IgKGNvbnN0IHUgb2YgdGhpcy5zaW1wbGVGdWxsUmVzVXJscy52YWx1ZXMoKSkgVVJMLnJldm9rZU9iamVjdFVSTCh1KTtcbiAgICB0aGlzLnNpbXBsZUZ1bGxSZXNVcmxzLmNsZWFyKCk7XG4gIH1cblxuICAvKiogTW91bnQgdGhlIHZpZXdlciB3aXRoIGEgY3VzdG9tIHRpbGUgc291cmNlIHBvaW50aW5nIGF0IGBHRVQgL3RpbGVgLiAqL1xuICBwbG90KFxuICAgIHBsb3REaXY6IHN0cmluZyxcbiAgICBpbWFnZUxvYWRlZDogYW55LFxuICAgIGltYWdlSW5mbzogSUltYWdlSW5mbyxcbiAgICBfc2NyZWVuSGVpZ2h0OiBudW1iZXIsXG4gICAgX3Bsb3RUeXBlOiBQbG90VHlwZSxcbiAgICBpblBsYWNlPzogYm9vbGVhbixcbiAgKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3QgbG9hZGVkID0gaW1hZ2VMb2FkZWQgYXMgT3NkTG9hZGVkO1xuICAgIGNvbnN0IGQgPSBsb2FkZWQ/LmRlc2NyaXB0b3I7XG4gICAgaWYgKCFkKSByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGZhbHNlKTtcbiAgICAvLyBSZS1lc3RhYmxpc2ggdGhlIGNvbG9ybWFwIHN1YnNjcmlwdGlvbiBpZiBhIHByaW9yIGNvbXBvbmVudCB0ZWFyZG93blxuICAgIC8vICh1bnN1YnNjcmliZSgpKSB0b3JlIGl0IGRvd24g4oCUIHRoaXMgc2VydmljZSBpcyBhIHJvb3Qgc2luZ2xldG9uLCBzbyB0aGVcbiAgICAvLyBjb25zdHJ1Y3RvciB3b24ndCBydW4gYWdhaW4gdG8gcmVjcmVhdGUgaXQuXG4gICAgdGhpcy5lbnN1cmVDb2xvcm1hcFN1YnNjcmlwdGlvbigpO1xuICAgIC8vIE9TRCB0aWxlcyBuYXRpdmVseSwgc28gdGhlIGluLXBsYWNlIChsYXJnZSkgcGFzcyBpcyBub3JtYWxseSBhIG5vLW9wXG4gICAgLy8gb25jZSBtb3VudGVkIOKAlCB0aGUgdGlsZSBweXJhbWlkIElTIHRoZSBmdWxsIHJlc29sdXRpb24gcmVnYXJkbGVzcyBvZlxuICAgIC8vIHdoaWNoIEltYWdlSW5mbyB0aWVyIHJlcXVlc3RlZCBpdC4gU2ltcGxlIG1vZGUgaGFzIG5vIHB5cmFtaWQgdG8gZmFsbFxuICAgIC8vIGJhY2sgb246IGl0IGRpc3BsYXlzIGxpdGVyYWxseSB3aGF0ZXZlciBVUkwgd2FzIGxhc3Qgb3BlbmVkLCBzb1xuICAgIC8vIHdpdGhvdXQgdGhpcyBzd2FwIHRoZSBzbWFsbCB0aWVyJ3MgMTI4cHggcGxhY2Vob2xkZXIgd291bGQgc3RheSBvblxuICAgIC8vIHNjcmVlbiBmb3JldmVyIChwaXhlbGF0ZWQsIG5ldmVyIFwic2hhcnBlbmluZ1wiIGxpa2UgdGhlIHRpbGVkIHBhdGgpLlxuICAgIGlmIChpblBsYWNlICYmIHRoaXMudmlld2VyKSB7XG4gICAgICBpZiAobG9hZGVkLnNpbXBsZSAmJiBsb2FkZWQudXJsKSB7XG4gICAgICAgIC8vIEFsc28gcmVmcmVzaCBzaW1wbGVVcmxzIHRvIFRISVMgcGhhc2UncyAobGFyZ2UtdGllcikgdXJsc1tdIOKAlCBpdCB3YXNcbiAgICAgICAgLy8gc2V0IGZyb20gdGhlIHNtYWxsIHRpZXIncyAxMjhweCB1cmxzIG9uIHRoZSBpbml0aWFsIG1vdW50IGJlbG93LFxuICAgICAgICAvLyB3aGljaCBuZXZlciBydW5zIGFnYWluIGZvciB0aGUgaW4tcGxhY2UgcGFzcy4gV2l0aG91dCB0aGlzLFxuICAgICAgICAvLyBzZXRaSW5kZXgncyBsYXRlciBzbGlkZXIgc2NydWJzIHdvdWxkIGtlZXAgcmVhZGluZyB0aGUgc21hbGxcbiAgICAgICAgLy8gdGllcidzIGxvdy1yZXMgVVJMcyBmb3JldmVyLCBldmVuIHRob3VnaCB0aGUgaW5pdGlhbCBzbGljZSB3YXNcbiAgICAgICAgLy8gY29ycmVjdGx5IHN3YXBwZWQgdG8gZnVsbCByZXNvbHV0aW9uIGhlcmUuXG4gICAgICAgIHRoaXMuc2ltcGxlVXJscyA9IGltYWdlSW5mbz8udXJscyA/PyB0aGlzLnNpbXBsZVVybHM7XG4gICAgICAgIHRoaXMudmlld2VyLm9wZW4oeyB0eXBlOiAnaW1hZ2UnLCB1cmw6IGxvYWRlZC51cmwgfSBhcyBhbnkpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh0cnVlKTtcbiAgICB9XG4gICAgdGhpcy5wbG90RGl2ID0gcGxvdERpdjtcbiAgICB0aGlzLmN1cnJlbnRGaWxlTmFtZSA9IGltYWdlSW5mbz8uZmlsZU5hbWU7XG4gICAgdGhpcy5kZXNjcmlwdG9yID0gZDtcbiAgICB0aGlzLmluZm9CNjQgPSBsb2FkZWQuaW5mb0I2NDtcbiAgICB0aGlzLmN1cnJlbnRaID0gbG9hZGVkLno7XG4gICAgdGhpcy5zaW1wbGVNb2RlID0gISFsb2FkZWQuc2ltcGxlO1xuICAgIHRoaXMuc2ltcGxlVXJscyA9IGxvYWRlZC5zaW1wbGUgPyAoaW1hZ2VJbmZvPy51cmxzID8/IFtdKSA6IFtdO1xuICAgIC8vIFRoZSBkZXNjcmlwdG9yJ3MgcGh5c2ljYWwgcGl4ZWwgc2l6ZSAoc2VydmVyIEJpby1Gb3JtYXRzIGAvdGlsZXMvaW5mb2ApIGlzXG4gICAgLy8gYXV0aG9yaXRhdGl2ZSDigJQgcHVzaCBpdCBpbnRvIHRoZSBzaGFyZWQgbWV0YSBzbyB0aGUgUmVnaW9uIEVkaXRvciByZXBvcnRzXG4gICAgLy8gYXJlYXMgaW4gwrVtwrIvbW3CsiwgbWF0Y2hpbmcgdGhlIHNjYWxlIGJhciBidWlsdCBmcm9tIGBkLm1wcFhgIGJlbG93LlxuICAgIHRoaXMuc3RvcmUuc2V0UGh5c2ljYWxQaXhlbFNpemUoZC5tcHBYLCBkLm1wcFkpO1xuICAgIC8vIFVzZSB0aGUgU0FNRSBncmF5c2NhbGUgZmxhZyB0aGUgY29sb3JtYXAgZHJvcGRvd24gLyBQbG90bHkgdXNlXG4gICAgLy8gKGltYWdlSW5mby5pc0dyYXlzY2FsZSDihpAgcmdiQ2hhbm5lbHMgPT09IDEpLiBUaGUgZGVzY3JpcHRvcidzIGBjaGFubmVsc2BcbiAgICAvLyAoY2hhbm5lbENvdW50KSBkaXZlcmdlcyBmcm9tIHJnYkNoYW5uZWxzIGZvciBzdGFja3MsIHdoaWNoIGxlZnQgZ3JheXNjYWxlXG4gICAgLy8gc3RhY2tzIHVuLXJlY29sb3JlZCBldmVuIHRob3VnaCB0aGUgY29sb3JtYXAgc2VsZWN0b3Igd2FzIHNob3duLlxuICAgIHRoaXMuaXNHcmF5c2NhbGVJbWFnZSA9ICEhaW1hZ2VJbmZvPy5pc0dyYXlzY2FsZTtcbiAgICAvLyBTaW1wbGUgKG5vbi10aWxlZCwgaW4tbWVtb3J5KSBpbWFnZTogbm8gcHlyYW1pZCwgbm8gcGVyLWNoYW5uZWwgdGlsZXMsIGFuZFxuICAgIC8vIG5vIHNlcnZlci1zaWRlIGludGVuc2l0eSBzYW1wbGluZyDigJQgT1NEJ3Mgc2luZ2xlLWltYWdlIHNvdXJjZSBkZWNvZGVzXG4gICAgLy8gdXJsc1t6XSBkaXJlY3RseS4gU2tpcCB0aGUgd2hvbGUgdGlsZWQtc2V0dXAgcGF0aCBiZWxvdy5cbiAgICBjb25zdCBzaW1wbGUgPSAhIWxvYWRlZC5zaW1wbGU7XG4gICAgaWYgKHNpbXBsZSkge1xuICAgICAgdGhpcy5yZWFsTGV2ZWxzID0gMTtcbiAgICAgIHRoaXMuaXNNdWx0aUNoYW5uZWwgPSBmYWxzZTtcbiAgICAgIHRoaXMuY2FjaGUuY2xlYXJDaGFubmVsR3JvdXBzKCk7XG4gICAgICB0aGlzLmRlc3Ryb3lWaWV3ZXIoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gTXVsdGljaGFubmVsIGZsdW9yZXNjZW5jZSAoaW5kZXhlZC9MVVQtYmVhcmluZyBzdGFja3MpIGNvbXBvc2l0ZSBjbGllbnQtc2lkZVxuICAgICAgLy8gZnJvbSBwZXItY2hhbm5lbCB0aWxlcy4gVHJ1c3QgdGhlIHNlcnZlcidzIGV4cGxpY2l0IGBtdWx0aWNoYW5uZWxgIGZsYWcg4oCUIHRoZVxuICAgICAgLy8gb2xkIGBjaGFubmVscz4xICYmIGdyYXlzY2FsZWAgaGV1cmlzdGljIGFsc28gbWF0Y2hlZCBSR0IgcGhvdG9zIEJpby1Gb3JtYXRzXG4gICAgICAvLyByZWFkcyBhcyBzZXBhcmF0ZWQgcGxhbmVzIChjaGFubmVscz4xLCByZ2JDaGFubmVscz09MSksIHNwbGl0dGluZyB0aGVtIGludG8gTlxuICAgICAgLy8gcGVyLWNoYW5uZWwgVGlsZWRJbWFnZXMgdGhhdCBmbG9vZGVkIHRoZSB0aWxlIGVuZHBvaW50IGFuZCBodW5nIG9uIGxvYWQuXG4gICAgICB0aGlzLnJlYWxMZXZlbHMgPSBkLnJlYWxMZXZlbHMgPz8gZC5sZXZlbHMubGVuZ3RoO1xuICAgICAgLy8gVGlsZXMgdG8gY292ZXIgdGhlIHdob2xlIGltYWdlIGF0IHRoZSBjb2Fyc2VzdCBSRUFMIEJpby1Gb3JtYXRzIGxldmVsICh0aGVcbiAgICAgIC8vIHNtYWxsZXN0IGxldmVsIHRoYXQgaGFzIHJlYWwsIHBlci1jaGFubmVsLWZldGNoYWJsZSB0aWxlcyDigJQgc3ludGhldGljIG92ZXJ2aWV3XG4gICAgICAvLyBsZXZlbHMgYXJlIHNlcnZlci1jb21wb3NpdGVkIG9ubHkpLiBBIHB5cmFtaWRhbCBpbWFnZSdzIGNvYXJzZXN0IHJlYWwgbGV2ZWwgaXNcbiAgICAgIC8vIHRpbnkgKGZldyB0aWxlcyk7IGEgZmxhdC9uby1weXJhbWlkIGltYWdlJ3MgaXMgdGhlIGZ1bGwtcmVzIGdyaWQgKG1hbnkgdGlsZXMpLlxuICAgICAgY29uc3QgY29hcnNlID0gZC5sZXZlbHNbdGhpcy5yZWFsTGV2ZWxzIC0gMV0gPz8gZC5sZXZlbHNbZC5sZXZlbHMubGVuZ3RoIC0gMV07XG4gICAgICBjb25zdCBjb2Fyc2VXID0gY29hcnNlID8gTWF0aC5jZWlsKGNvYXJzZS53aWR0aCAvIGQudGlsZVNpemUpIDogMTtcbiAgICAgIGNvbnN0IGNvYXJzZUggPSBjb2Fyc2UgPyBNYXRoLmNlaWwoY29hcnNlLmhlaWdodCAvIGQudGlsZVNpemUpIDogMTtcbiAgICAgIGNvbnN0IGNvYXJzZVRpbGVzID0gY29hcnNlVyAqIGNvYXJzZUg7XG4gICAgICB0aGlzLmlzTXVsdGlDaGFubmVsID0gISFkLm11bHRpY2hhbm5lbDtcbiAgICAgIGlmICh0aGlzLmlzTXVsdGlDaGFubmVsKSB7XG4gICAgICAgIC8vIFBlci1jaGFubmVsIHJlbmRlcmluZyBjYW4gb25seSB1c2UgdGhlIFJFQUwgbGV2ZWxzLCBzbyBPU0QgcmVxdWVzdHMgdGhlXG4gICAgICAgIC8vIGNvYXJzZXN0IHJlYWwgbGV2ZWwncyB3aG9sZSB0aWxlIGdyaWQgw5cgTiBjaGFubmVscyBhdCBmaXQuIFdoZW4gdGhhdCdzIGxhcmdlXG4gICAgICAgIC8vIChhIHdob2xlLXNsaWRlKSwgaXQncyB0b28gbWFueSAob2Z0ZW4gc2xvdywgcHlyYW1pZC1sZXNzKSBmdWxsLXJlcyByZWFkcyDigJRcbiAgICAgICAgLy8gZmFsbCBiYWNrIHRvIHRoZSBzaW5nbGUgc2VydmVyLWNvbXBvc2l0ZWQgc291cmNlICh3aGljaCBrZWVwcyB0aGUgZmFzdFxuICAgICAgICAvLyBzeW50aGV0aWMgb3ZlcnZpZXdzKS4gQSBzbWFsbCBzaW5nbGUtRk9WIHotc3RhY2sgc3RheXMgcGVyLWNoYW5uZWwuIFNpemUgaW5cbiAgICAgICAgLy8gQllURVMgaXNuJ3QgdGhlIHNpZ25hbCDigJQgdGlsZSBjb3VudCBpcy5cbiAgICAgICAgY29uc3QgZml0VGlsZXMgPSBjb2Fyc2VUaWxlcyAqIE1hdGgubWF4KDEsIGQuY2hhbm5lbHMgPz8gMSk7XG4gICAgICAgIGlmIChmaXRUaWxlcyA+IHRoaXMuTUFYX01VTFRJQ0hBTk5FTF9GSVRfVElMRVMpIHtcbiAgICAgICAgICB0aGlzLmlzTXVsdGlDaGFubmVsID0gZmFsc2U7XG4gICAgICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgICAgJ1tPU0RdIG11bHRpY2hhbm5lbCBjb21wb3NpdGUgdG9vIGxhcmdlIGZvciBwZXItY2hhbm5lbCByZW5kZXJpbmc6ICcgK1xuICAgICAgICAgICAgYCR7Y29hcnNlV314JHtjb2Fyc2VIfSB0aWxlcyB4ICR7ZC5jaGFubmVsc30gY2hhbm5lbHMgPSAke2ZpdFRpbGVzfSBhdCB0aGUgY29hcnNlc3QgYCArXG4gICAgICAgICAgICBgcmVhbCBsZXZlbCAoPiAke3RoaXMuTUFYX01VTFRJQ0hBTk5FTF9GSVRfVElMRVN9KTsgcmVuZGVyaW5nIHNlcnZlci1jb21wb3NpdGVkIGZvciBzcGVlZC5gLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHRoaXMuY2FjaGUuY2xlYXJDaGFubmVsR3JvdXBzKCk7XG4gICAgICAvLyBBdXRvLXJhbmdlIGdyYXlzY2FsZSB0aWxlcyB0byB0aGUgaW1hZ2UncyBhY3R1YWwgaW50ZW5zaXR5IHNwYW4gKGxpa2UgdGhlXG4gICAgICAvLyBoZWF0bWFwKSwgc2FtcGxpbmcgdGhlIGNvYXJzZXN0IHRpbGUgbGV2ZWwgc28gdGhlIHdpbmRvdyBtYXRjaGVzIHRoZSByYXdcbiAgICAgIC8vIHRpbGUgdmFsdWVzLiBGaXJlLWFuZC1mb3JnZXQ6IGl0IHJlLWludmFsaWRhdGVzIG9uY2UgdGhlIHdpbmRvdyBpcyBrbm93bjtcbiAgICAgIC8vIHRpbGVzIHBhaW50IHVud2luZG93ZWQgdW50aWwgdGhlbi4gKE11bHRpY2hhbm5lbCB3aW5kb3dzIGFyZSBwZXItY2hhbm5lbCxcbiAgICAgIC8vIGRlZmF1bHRpbmcgdG8gZnVsbCByYW5nZTsgdGhlIHVzZXIgYXV0by9lZGl0cyBlYWNoIGNoYW5uZWwuKVxuICAgICAgaWYgKHRoaXMuaXNNdWx0aUNoYW5uZWwpIHtcbiAgICAgICAgdGhpcy5zYW1wbGVyLmNvbXB1dGVNdWx0aUNoYW5uZWxIaXN0b2dyYW1zKGQsIGxvYWRlZC5pbmZvQjY0LCBsb2FkZWQueik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnNhbXBsZXIuY29tcHV0ZUltYWdlV2luZG93KGQsIGxvYWRlZC5pbmZvQjY0LCBsb2FkZWQueik7XG4gICAgICB9XG4gICAgICB0aGlzLmRlc3Ryb3lWaWV3ZXIoKTtcbiAgICAgIC8vIFNpemUgdGhlIHNsaWNlIGNhY2hlIGZvciB0aGlzIGltYWdlOiBMUlUgY2FwIHRvIGhvbGQgdGhlIHdob2xlIHN0YWNrXG4gICAgICAvLyAoY2FwcGVkKSBhbmQgc2tpcCBiYWNrZ3JvdW5kIHByZWxvYWRpbmcgZm9yIHN0YWNrcyB0b28gbGFyZ2UgdG8gcHJlbG9hZFxuICAgICAgLy8gKHdvdWxkIGZsb29kIGZ1bGwtcmVzIHJlYWRzKS4gTm9ybWFsIHN0YWNrcyBrZWVwIHRoZSBmbGlja2VyLWZyZWUgcHJlLWNhY2hlLlxuICAgICAgdGhpcy5jYWNoZS5jb25maWd1cmUoZC56ID8/IDEsIGNvYXJzZVRpbGVzKTtcbiAgICB9XG5cbiAgICAvLyBTaW1wbGUgbW9kZTogT1NEJ3Mgc2luZ2xlLWltYWdlIHNvdXJjZSBkZWNvZGVzIHRoZSBVUkwgKGJsb2I6L2RhdGE6L2h0dHApXG4gICAgLy8gZGlyZWN0bHkg4oCUIG5vIHB5cmFtaWQsIG5vIHNlcnZlci4gVGlsZWQgbW9kZTogdGhlIGN1c3RvbSBzZXJ2ZXItYmFja2VkIHRpbGVcbiAgICAvLyBzb3VyY2UgKG11bHRpY2hhbm5lbCBvcGVucyBvbiBjaGFubmVsIDA7IHRoZSBvcGVuIGhhbmRsZXIgdGhlbiBzd2FwcyBpbiB0aGlzXG4gICAgLy8gc2xpY2UncyBwZXItY2hhbm5lbCBncm91cCBhbmQgdGhlIGJhY2tncm91bmQgbG9hZGVyIHByZS1maWxscyB0aGUgcmVzdCkuXG4gICAgY29uc3QgdGlsZVNvdXJjZSA9IHNpbXBsZVxuICAgICAgPyB7IHR5cGU6ICdpbWFnZScsIHVybDogbG9hZGVkLnVybCB9XG4gICAgICA6IHRoaXMuYnVpbGRUaWxlU291cmNlKGQsIGxvYWRlZC5pbmZvQjY0LCBsb2FkZWQueiwgdGhpcy5pc011bHRpQ2hhbm5lbCA/IDAgOiB1bmRlZmluZWQpO1xuICAgIHNpbGVuY2VPc2RNdWx0aUltYWdlQWR2aXNvcnkoKTtcbiAgICB0aGlzLnZpZXdlciA9IChPU0QgYXMgYW55KSh7XG4gICAgICBpZDogcGxvdERpdixcbiAgICAgIC8vIFNpbXBsZS1tb2RlIHotc2NydWIgY2FsbHMgdmlld2VyLm9wZW4oKSBhZ2FpbiBwZXIgc2xpY2UgKHNlZVxuICAgICAgLy8gc2V0WkluZGV4KSDigJQgd2l0aG91dCB0aGlzIGl0IHJlc2V0cyB0byB0aGUgaG9tZSB6b29tL3BhbiBvbiBldmVyeVxuICAgICAgLy8gc2xpY2UgY2hhbmdlLiBUaWxlZCBtb2RlIG5ldmVyIHJlLW9wZW5zIChpdCB0b2dnbGVzIHByZS1hZGRlZFxuICAgICAgLy8gVGlsZWRJbWFnZXMnIG9wYWNpdHkgaW5zdGVhZCksIHNvIHRoaXMgaXMgYSBuby1vcCB0aGVyZS5cbiAgICAgIHByZXNlcnZlVmlld3BvcnQ6IHRydWUsXG4gICAgICAvLyBVc2UgdGhlIDJEIGNhbnZhcyBkcmF3ZXIsIG5vdCBXZWJHTDogY3JlYXRpbmcvZGVzdHJveWluZyBhIHZpZXdlciBvblxuICAgICAgLy8gZWFjaCBlbmdpbmUgdG9nZ2xlIC8gaW1hZ2UgbG9hZCBjaHVybnMgV2ViR0wgY29udGV4dHMgKGJyb3dzZXJzIGNhcFxuICAgICAgLy8gaG93IG1hbnkgZXhpc3QgYXQgb25jZSksIHdoaWNoIHN1cmZhY2VzIGFzIFwiV2ViR0wgY29udGV4dCB3YXMgbG9zdFwiXG4gICAgICAvLyBhbmQgYmxhbmsgdGlsZXMuIFRoZSBjYW52YXMgZHJhd2VyIGhhcyBubyBzdWNoIGxpbWl0LlxuICAgICAgZHJhd2VyOiAnY2FudmFzJyxcbiAgICAgIHNob3dOYXZpZ2F0aW9uQ29udHJvbDogZmFsc2UsIC8vIGF2b2lkcyBuZWVkaW5nIHRoZSBpY29uLWltYWdlIGFzc2V0c1xuICAgICAgLy8gT3ZlcnZpZXcgdGh1bWJuYWlsIHdpdGggdGhlIGN1cnJlbnQtdmlld3BvcnQgcmVjdGFuZ2xlLCBib3R0b20tcmlnaHQuXG4gICAgICBzaG93TmF2aWdhdG9yOiB0aGlzLm5hdmlnYXRvclZpc2libGUsXG4gICAgICAvLyBOZWFyZXN0LW5laWdoYm91ciB3aGVuIG9mZiDihpIgY3Jpc3AgcGl4ZWxzIG9uIHpvb20taW4gKHBpeGVsIGluc3BlY3Rpb24pLlxuICAgICAgaW1hZ2VTbW9vdGhpbmdFbmFibGVkOiB0aGlzLnNtb290aGluZ0VuYWJsZWQsXG4gICAgICBuYXZpZ2F0b3JQb3NpdGlvbjogJ0JPVFRPTV9SSUdIVCcsXG4gICAgICBuYXZpZ2F0b3JTaXplUmF0aW86IDAuMTYsXG4gICAgICBuYXZpZ2F0b3JBdXRvRmFkZTogZmFsc2UsXG4gICAgICBuYXZpZ2F0b3JCYWNrZ3JvdW5kOiAncmdiYSgwLDAsMCwwLjUpJyxcbiAgICAgIGxvYWRUaWxlc1dpdGhBamF4OiB0cnVlLFxuICAgICAgYWpheFdpdGhDcmVkZW50aWFsczogdHJ1ZSwgLy8gY29va2llIGF1dGggKG9hdXRoMi1wcm94eSlcbiAgICAgIGFqYXhIZWFkZXJzOiB0aGlzLmF1dGhIZWFkZXJzLCAvLyBiZWFyZXIgYXV0aCAoQXV0aDApLCB3aGVuIGF2YWlsYWJsZVxuICAgICAgY3Jvc3NPcmlnaW5Qb2xpY3k6ICdBbm9ueW1vdXMnLFxuICAgICAgLy8gR2VudGxlciB6b29tOiB0aGUgZGVmYXVsdCAoMS4yKSBmZWVscyBmYXN0IGFuZCBiaWcganVtcHMgY3Jvc3Mgc2V2ZXJhbFxuICAgICAgLy8gcHlyYW1pZCBsZXZlbHMgYXQgb25jZSwgZmlyaW5nIGEgYnVyc3Qgb2YgdGlsZSByZXF1ZXN0cy5cbiAgICAgIHpvb21QZXJTY3JvbGw6IDEuMSxcbiAgICAgIC8vIENsaWNrLXRvLXpvb20gdG93YXJkIHRoZSBjbGlja2VkIHBvaW50IChPcGVuU2VhZHJhZ29uJ3MgZGVmYXVsdCBkZW1vXG4gICAgICAvLyBiZWhhdmlvdXIpLiBPbmx5IGFwcGxpZXMgd2hlbiBubyByZWdpb24gdG9vbCBpcyBhY3RpdmUg4oCUIGFuIGFjdGl2ZSB0b29sXG4gICAgICAvLyBkaXNhYmxlcyBPU0QgbW91c2UtbmF2IHNvIGNsaWNrcyBkcmF3L3NlbGVjdCBpbnN0ZWFkIG9mIHpvb21pbmcuXG4gICAgICBnZXN0dXJlU2V0dGluZ3NNb3VzZTogeyBjbGlja1RvWm9vbTogdHJ1ZSwgc2Nyb2xsVG9ab29tOiB0cnVlIH0sXG4gICAgICAvLyBab29tIGxpbWl0cyAoaml0LXVpIzk0KS4gRGVmYXVsdCBtaW5ab29tSW1hZ2VSYXRpbyAoMC45KSBzdG9wcyB5b3UgZnJvbVxuICAgICAgLy8gem9vbWluZyB0aGUgaW1hZ2Ugc21hbGxlciB0aGFuIH5ob21lOyBkcm9wIGl0IHNvIHRoZSBpbWFnZSBjYW4gYmUgc2hydW5rXG4gICAgICAvLyBmcmVlbHkgKGVmZmVjdGl2ZWx5IG5vIG1pbmltdW0pLiBEZWZhdWx0IG1heFpvb21QaXhlbFJhdGlvICgxLjEpIGNhcHNcbiAgICAgIC8vIHpvb20taW4gYXQgfjEgc2NyZWVuIHB4IHBlciBpbWFnZSBweDsgcmFpc2UgaXQgc28geW91IGNhbiB6b29tIGluIHRvXG4gICAgICAvLyBpbnNwZWN0IGluZGl2aWR1YWwgcGl4ZWxzIChwYWlyZWQgd2l0aCBpbWFnZVNtb290aGluZ0VuYWJsZWQ9ZmFsc2UgZm9yXG4gICAgICAvLyBjcmlzcCBuZWFyZXN0LW5laWdoYm91ciBibG9ja3MpLlxuICAgICAgbWluWm9vbUltYWdlUmF0aW86IDAuMDEsXG4gICAgICBtYXhab29tUGl4ZWxSYXRpbzogMjAsXG4gICAgICAvLyBXYWl0IGZvciB0aGUgdmlldyB0byBzZXR0bGUgYmVmb3JlIHB1bGxpbmcgbmV3IHRpbGVzIChsZXNzIGNodXJuKS5cbiAgICAgIGltbWVkaWF0ZVJlbmRlcjogZmFsc2UsXG4gICAgICBhbmltYXRpb25UaW1lOiAwLjQsXG4gICAgICAvLyBSZWxlYXNlIG1lbW9yeSBhZ2dyZXNzaXZlbHk6IGNhcCB0aGUgdGlsZSBjYWNoZSBzbyB0aWxlcyBvdXRzaWRlIHRoZVxuICAgICAgLy8gY3VycmVudCB2aWV3IChlLmcuIHRoZSBjb2Fyc2VyIGxldmVscyBsZWZ0IGJlaGluZCBhZnRlciB6b29taW5nIGluKSBhcmVcbiAgICAgIC8vIGV2aWN0ZWQgaW5zdGVhZCBvZiBhY2N1bXVsYXRpbmcuIExhcmdlIHdob2xlLXNsaWRlIHRpbGVzIOKAlCBkb3VibGVkIHdoZW5cbiAgICAgIC8vIHRoZSBjb2xvcm1hcCBwaXBlbGluZSBrZWVwcyBhIHJlY29sb3JlZCBjb3B5IOKAlCBtYWtlIGFuIHVuYm91bmRlZCBjYWNoZVxuICAgICAgLy8gY29zdGx5LiBUdW5hYmxlOyBsb3dlciA9IGxlc3MgbWVtb3J5LCBtb3JlIHJlLWZldGNoIG9uIHBhbi1iYWNrLlxuICAgICAgLy8gU3RhY2tzIGtlZXAgbWFueSBzbGljZXMgcmVzaWRlbnQgKHRoZSB3aG9sZSBzdGFjayBpcyBiYWNrZ3JvdW5kLWxvYWRlZCksXG4gICAgICAvLyBzbyB0aGV5IGdldCBhIG11Y2ggbGFyZ2VyIHRpbGUgYnVkZ2V0IOKAlCBvdGhlcndpc2Ugc3dpdGNoaW5nIHNsaWNlcyBldmljdHNcbiAgICAgIC8vIGVhY2ggb3RoZXIncyB0aWxlcyBhbmQgc2NydWJiaW5nIGJhY2sgcmUtZmV0Y2hlcy4gU2NhbGVkIHRvIHRoZSByZXNpZGVudFxuICAgICAgLy8gc2xpY2UgY291bnQ7IHNpbmdsZSBpbWFnZXMga2VlcCB0aGUgbGVhbiBkZWZhdWx0LlxuICAgICAgbWF4SW1hZ2VDYWNoZUNvdW50OiAoZC56ID8/IDEpID4gMSA/IE1hdGgubWluKDI0MDAsIE1hdGgubWF4KDYwMCwgdGhpcy5jYWNoZS5tYXhTbGljZXMoKSAqIDQwKSkgOiAxNTAsXG4gICAgfSk7XG5cbiAgICByZXR1cm4gbmV3IFByb21pc2U8Ym9vbGVhbj4oKHJlc29sdmUpID0+IHtcbiAgICAgIC8vIEFsd2F5cyBzZXR0bGU6IGlmIE9TRCBuZXZlciBlbWl0cyBvcGVuL29wZW4tZmFpbGVkIChhIGJhZCB0aWxlIHNvdXJjZSxcbiAgICAgIC8vIGEgdmlld2VyIHRvcm4gZG93biBtaWQtb3BlbiksIHRoZSByZW5kZXIgcGlwZWxpbmUgbXVzdCBub3QgaGFuZyDigJQgYSBoYW5nXG4gICAgICAvLyBsZWF2ZXMgaW1hZ2VMb2FkaW5nPXRydWUsIHN0aWNraW5nIHRoZSBzcGlubmVyIGFuZCB0aGUgNTAwbXNcbiAgICAgIC8vIGNhY2hlLXByb2dyZXNzIHBvbGwgZm9yZXZlciAoTlNfQklORElOR19BQk9SVEVEIHN0b3JtKS5cbiAgICAgIGxldCBzZXR0bGVkID0gZmFsc2U7XG4gICAgICBjb25zdCBkb25lID0gKG9rOiBib29sZWFuKSA9PiB7XG4gICAgICAgIGlmICghc2V0dGxlZCkge1xuICAgICAgICAgIHNldHRsZWQgPSB0cnVlO1xuICAgICAgICAgIHJlc29sdmUob2spO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgdGhpcy52aWV3ZXIhLmFkZE9uY2VIYW5kbGVyKCdvcGVuJywgKCkgPT4ge1xuICAgICAgICAvLyBSZWdpb24gb3ZlcmxheSByZWFkcy93cml0ZXMgdGhlIHNoYXJlZCBSZWdpb25TdG9yZSwgc28gcmVnaW9ucyBzdGF5XG4gICAgICAgIC8vIGluIHN5bmMgd2l0aCBQbG90bHkgYW5kIHRoZSBSZWdpb24gRWRpdG9yLiBSZWNyZWF0ZSBpdCBwZXIgb3BlbigpIHNvXG4gICAgICAgIC8vIGl0IGJpbmRzIHRvIHRoZSBmcmVzaGx5LW9wZW5lZCB2aWV3ZXIncyBjYW52YXMvTW91c2VUcmFja2VyLlxuICAgICAgICB0aGlzLm92ZXJsYXk/LmRlc3Ryb3koKTtcbiAgICAgICAgdGhpcy5vdmVybGF5ID0gbmV3IE9zZFJlZ2lvbk92ZXJsYXkodGhpcy52aWV3ZXIgYXMgYW55LCB0aGlzLnJlZ2lvblN0b3JlKTtcbiAgICAgICAgdGhpcy5jb29yZFRyYW5zZm9ybSA9IG5ldyBPc2RDb29yZGluYXRlVHJhbnNmb3JtKHRoaXMudmlld2VyIGFzIGFueSk7XG4gICAgICAgIHRoaXMuc2NhbGVCYXIgPSBuZXcgT3NkU2NhbGVCYXIodGhpcy52aWV3ZXIgYXMgYW55LCBkLm1wcFggPz8gMCk7XG4gICAgICAgIHRoaXMuYnVpbGRUb29sSG9zdHMoKTtcbiAgICAgICAgLy8gU2ltcGxlIG1vZGUgaXMgYSBzaW5nbGUsIHNlbGYtY29udGFpbmVkIGltYWdlIOKAlCBubyBzbGljZSBjYWNoZSB0byBzZWVkLlxuICAgICAgICBpZiAoc2ltcGxlKSB7XG4gICAgICAgICAgLyogbm90aGluZyB0byBjYWNoZTogb25lIGZyYW1lLCBubyBweXJhbWlkICovXG4gICAgICAgIH0gZWxzZSBpZiAodGhpcy5pc011bHRpQ2hhbm5lbCkge1xuICAgICAgICAgIC8vIERyb3AgdGhlIHNpbmdsZSBvcGVuZWQgKGNoYW5uZWwtMCkgaW1hZ2UgYW5kIGFkZCB0aGlzIHNsaWNlJ3MgY2hhbm5lbFxuICAgICAgICAgIC8vIGdyb3VwIHRvIHRoZSBwZXItc2xpY2UgY2FjaGUuIFRoZSBzaGFyZWQgYmFja2dyb3VuZCBsb2FkZXIgLyBMUlUgdGhlblxuICAgICAgICAgIC8vIHByZS1maWxscyB0aGUgb3RoZXIgc2xpY2VzJyBjaGFubmVsIGdyb3VwcyBzbyB6LXNjcnViIGlzIGZsaWNrZXItZnJlZS5cbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgaXQwID0gKHRoaXMudmlld2VyIGFzIGFueSkud29ybGQ/LmdldEl0ZW1BdD8uKDApO1xuICAgICAgICAgICAgaWYgKGl0MCkgKHRoaXMudmlld2VyIGFzIGFueSkud29ybGQucmVtb3ZlSXRlbShpdDApO1xuICAgICAgICAgIH0gY2F0Y2ggeyAvKiBub3RoaW5nIHRvIHJlbW92ZSAqLyB9XG4gICAgICAgICAgdGhpcy5jYWNoZS5hZGRDaGFubmVsU2xpY2UodGhpcy5jdXJyZW50Wik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gU2VlZCB0aGUgc2xpY2UgY2FjaGUgd2l0aCB0aGUganVzdC1vcGVuZWQgc2xpY2UgKHdvcmxkIGl0ZW0gMCksIHNvXG4gICAgICAgICAgLy8gc2NydWJiaW5nIGJhY2sgdG8gaXQgbGF0ZXIgaXMgYW4gaW5zdGFudCBvcGFjaXR5IHRvZ2dsZSwgbm90IGEgcmUtb3Blbi5cbiAgICAgICAgICBjb25zdCBmaXJzdEl0ZW0gPSAodGhpcy52aWV3ZXIgYXMgYW55KS53b3JsZD8uZ2V0SXRlbUF0Py4oMCk7XG4gICAgICAgICAgaWYgKGZpcnN0SXRlbSkgdGhpcy5jYWNoZS5zZWVkQ29tcG9zaXRlKHRoaXMuY3VycmVudFosIGZpcnN0SXRlbSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gVGhlIHdhbmQgc2FtcGxlcyB0aGUgKnJlbmRlcmVkIHZpZXdwb3J0Kiwgc28gaXRzIHBpeGVsIG1hdHJpeCBpcyBvbmx5XG4gICAgICAgIC8vIHZhbGlkIGZvciB0aGUgY3VycmVudCB2aWV3IOKAlCBkcm9wIGl0IHdoZW5ldmVyIHRoZSB2aWV3cG9ydCBjaGFuZ2VzLlxuICAgICAgICB0aGlzLnZpZXdlciEuYWRkSGFuZGxlcigndmlld3BvcnQtY2hhbmdlJywgKCkgPT4ge1xuICAgICAgICAgIHRoaXMudmlld3BvcnRQaXhlbHMgPSBudWxsO1xuICAgICAgICB9KTtcbiAgICAgICAgLy8gR3JheXNjYWxlIHRpbGVzIGdldCB0aGUgYWN0aXZlIGNvbG9ybWFwIHZpYSB0aGUgcGl4ZWwgcGlwZWxpbmUuIFRoZVxuICAgICAgICAvLyBoYW5kbGVyIHJ1bnMgcGVyIHRpbGUgb24gbG9hZCAoYW5kIG9uIHJlcXVlc3RJbnZhbGlkYXRlKTsgcmVjb2xvcmluZ1xuICAgICAgICAvLyBpcyBhIG5vLW9wIGZvciBSR0IgaW1hZ2VzIG9yIGJlZm9yZSB0aGUgTFVUIHJlc29sdmVzLlxuICAgICAgICAvLyAoaXNHcmF5c2NhbGVJbWFnZSBpcyBzZXQgZnJvbSBpbWFnZUluZm8uaXNHcmF5c2NhbGUgaW4gcGxvdCgpIGFib3ZlLilcbiAgICAgICAgdGhpcy52aWV3ZXIhLmFkZEhhbmRsZXIoJ3RpbGUtaW52YWxpZGF0ZWQnLCAoZXZlbnQ6IGFueSkgPT4gdGhpcy5yZWNvbG9yVGlsZShldmVudCkpO1xuICAgICAgICAvLyBUaGUgb3ZlcnZpZXcgbmF2aWdhdG9yIGlzIGEgc2VwYXJhdGUgbWluaS12aWV3ZXIgd2l0aCBpdHMgb3duIHRpbGVzLFxuICAgICAgICAvLyBzbyBpdCBkb2Vzbid0IHJlY2VpdmUgdGhlIG1haW4gdmlld2VyJ3MgdGlsZS1pbnZhbGlkYXRlZCBldmVudHMuIEFwcGx5XG4gICAgICAgIC8vIHRoZSBzYW1lIHJlY29sb3IgcGlwZWxpbmUgdG8gaXQgc28gdGhlIG1pbmltYXAgdHJhY2tzIHRoZSBtYWluIGltYWdlJ3NcbiAgICAgICAgLy8gY29sb3JtYXAvTFVUIGluc3RlYWQgb2Ygc3RheWluZyBncmF5c2NhbGUuXG4gICAgICAgIGNvbnN0IG5hdiA9ICh0aGlzLnZpZXdlciBhcyBhbnkpLm5hdmlnYXRvcjtcbiAgICAgICAgbmF2Py5hZGRIYW5kbGVyKCd0aWxlLWludmFsaWRhdGVkJywgKGV2ZW50OiBhbnkpID0+IHRoaXMucmVjb2xvclRpbGUoZXZlbnQpKTtcbiAgICAgICAgaWYgKCh0aGlzLmlzR3JheXNjYWxlSW1hZ2UgJiYgdGhpcy5jb2xvckx1dCkgfHwgdGhpcy5pc011bHRpQ2hhbm5lbCkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAodGhpcy52aWV3ZXIgYXMgYW55KS53b3JsZC5yZXF1ZXN0SW52YWxpZGF0ZSh0cnVlKTtcbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIC8qIG5vLW9wICovXG4gICAgICAgICAgfVxuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBuYXY/LndvcmxkPy5yZXF1ZXN0SW52YWxpZGF0ZSh0cnVlKTtcbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIC8qIG5vLW9wICovXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIFByZWZldGNoIGFkamFjZW50IHotc2xpY2VzIG9uY2UgdGhlIHZpZXcgc2V0dGxlcyAoYW5kIG9uIGVhY2ggc2V0dGxlLFxuICAgICAgICAvLyBzbyBpdCB0cmFja3MgdGhlIGN1cnJlbnQgdmlld3BvcnQgYXMgdGhlIHVzZXIgcGFucy96b29tcykuIFNpbXBsZSBtb2RlXG4gICAgICAgIC8vIGhhcyBhIHNpbmdsZSBmcmFtZSwgc28gdGhlcmUncyBub3RoaW5nIHRvIHByZWZldGNoLlxuICAgICAgICBpZiAoIXNpbXBsZSkgdGhpcy52aWV3ZXIhLmFkZEhhbmRsZXIoJ2FuaW1hdGlvbi1maW5pc2gnLCAoKSA9PiB0aGlzLmNhY2hlLnNjaGVkdWxlUHJlZmV0Y2goKSk7XG4gICAgICAgIC8vIFRlbGwgbGlzdGVuZXJzICh0aGUgaW50ZW5zaXR5IGluc2V0KSB0aGUgdmlzaWJsZSBpbWFnZSByZWdpb24gd2hlbmV2ZXJcbiAgICAgICAgLy8gdGhlIHZpZXcgc2V0dGxlcywgc28gdGhleSBjYW4gcmUtc2FtcGxlIGF0IHRoZSBjdXJyZW50IHpvb20gcmVzb2x1dGlvbi5cbiAgICAgICAgdGhpcy52aWV3ZXIhLmFkZEhhbmRsZXIoJ2FuaW1hdGlvbi1maW5pc2gnLCAoKSA9PiB0aGlzLmVtaXRWaWV3cG9ydENoYW5nZSgpKTtcbiAgICAgICAgLy8gQ2hyb21lIGNvbXBvc2l0b3IgYnVnOiBhZnRlciBhbiBPU0Qgem9vbSB0aGUgZG9ja2VkIHRvb2xiYXIgKGEgc2libGluZyBvZlxuICAgICAgICAvLyAjcGxvdCBpbiB0aGUgPHZpc3VhbGl6ZXI+IGhvc3QpIGlzIGxlZnQgbGFpZC1vdXQtYnV0LXVucGFpbnRlZCBhbmRcbiAgICAgICAgLy8gdmFuaXNoZXMg4oCUIGNvbmZpcm1lZCB2aWEgRGV2VG9vbHMgKERPTSBpbnRhY3QsIHJlZ2lvbiBzaW1wbHkgbm90IHBhaW50ZWQpLlxuICAgICAgICAvLyBDU1MgKHotaW5kZXggLyBpc29sYXRpb24gLyBjb250YWluKSBhbmQgRE9NLXJlcGFyZW50aW5nIGRvbid0IGZpeCBpdDsgYVxuICAgICAgICAvLyByZXBhaW50IHJlbGlhYmx5IGRvZXMuIE51ZGdlIGl0IGVhY2ggYW5pbWF0aW9uIGZyYW1lIChzbyBpdCBuZXZlciB2aXNpYmx5XG4gICAgICAgIC8vIGRyb3BzKSBhbmQgb24gc2V0dGxlLiBUaGUgc3luY2hyb25vdXMgZGlzcGxheSB0b2dnbGUgcmUtcmFzdGVycyB3aXRoIG5vXG4gICAgICAgIC8vIHZpc2libGUgZ29uZS1mcmFtZSBhbmQgbm8gbGF5b3V0IHNoaWZ0LlxuICAgICAgICB0aGlzLnZpZXdlciEuYWRkSGFuZGxlcignYW5pbWF0aW9uJywgKCkgPT4gdGhpcy5udWRnZVRvb2xiYXJSZXBhaW50KCkpO1xuICAgICAgICB0aGlzLnZpZXdlciEuYWRkSGFuZGxlcignYW5pbWF0aW9uLWZpbmlzaCcsICgpID0+IHRoaXMubnVkZ2VUb29sYmFyUmVwYWludCgpKTtcbiAgICAgICAgaWYgKCFzaW1wbGUpIHRoaXMuY2FjaGUuc2NoZWR1bGVQcmVmZXRjaCgpO1xuICAgICAgICAvLyBGb3JjZSBmaXQtdG8tdmlldyBhcyB0aGUgc3BsaXQvZmxleCBsYXlvdXQgc2V0dGxlcy4gQSB0YWxsXG4gICAgICAgIC8vIG5vbi1weXJhbWlkYWwgaW1hZ2UgY2FuIG90aGVyd2lzZSBvcGVuIHpvb21lZC1pbiAoaW1hZ2Ugd2lkdGggZmlsbGluZ1xuICAgICAgICAvLyB0aGUgdmlld2VyKSwgbWFraW5nIE9TRCBkZW1hbmQgc2xvdyBmdWxsLXJlcyByZXM9MCB0aWxlcyBmb3IgdGhlXG4gICAgICAgIC8vIGNlbnRyZSBpbnN0ZWFkIG9mIHRoZSBmYXN0IGNvYXJzZSBvdmVydmlldyDihpIgd2hpdGUgY2FudmFzLiBnb0hvbWVcbiAgICAgICAgLy8gZml0cyB0aGUgd2hvbGUgaW1hZ2Ugc28gT1NEIHNlbGVjdHMgdGhlIGNvYXJzZSBzeW50aGV0aWMgbGV2ZWwuIFJldHJ5XG4gICAgICAgIC8vIGFjcm9zcyBhIGZldyBmcmFtZXMgYmVjYXVzZSB0aGUgY29udGFpbmVyIG1heSBub3QgaGF2ZSBpdHMgZmluYWwgc2l6ZVxuICAgICAgICAvLyBvbiB0aGUgZmlyc3QgZnJhbWUgYWZ0ZXIgJ29wZW4nLlxuICAgICAgICBjb25zdCByZWZpdCA9ICgpID0+IHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgdGhpcy52aWV3ZXI/LnZpZXdwb3J0LmdvSG9tZSh0cnVlKTtcbiAgICAgICAgICAgIC8vIFRoZSBuYXZpZ2F0b3Igd2FzIHNpemVkIGluIHRoZSBWaWV3ZXIgY29uc3RydWN0b3Ig4oCUIEJFRk9SRSB0aGVcbiAgICAgICAgICAgIC8vIGxheW91dCBzZXR0bGVkIOKAlCBzbyBpdHMgZWxlbWVudCBjYW4gY2FycnkgYSBzdGFsZSAoZXZlblxuICAgICAgICAgICAgLy8gd3JvbmctYXNwZWN0KSBzaXplIHRoYXQgZmxvYXRzIHRoZSB2aXNpYmxlIG1pbmltYXAgYWJvdmUgdGhlXG4gICAgICAgICAgICAvLyBjb3JuZXIuIFJlLXNpemUgaXQgZnJvbSB0aGUgc2V0dGxlZCBjb250YWluZXIuXG4gICAgICAgICAgICB0aGlzLnJlc2l6ZU5hdmlnYXRvcigpO1xuICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgLyogdG9ybiBkb3duICovXG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUocmVmaXQpO1xuICAgICAgICBzZXRUaW1lb3V0KHJlZml0LCAxNTApO1xuICAgICAgICBzZXRUaW1lb3V0KHJlZml0LCA0MDApO1xuICAgICAgICAvLyBUaGUgdGltZWQgcmV0cmllcyBhYm92ZSBtaXNzIHRoZSBjYXNlIHdoZXJlIHRoZSBjb250YWluZXIgaXMgU1RJTExcbiAgICAgICAgLy8gemVyby1zaXplIGF0IDQwMG1zIOKAlCB3aGljaCBoYXBwZW5zIHdoZW4gdGhlIHJlbmRlciBzdGFydHMgd2hpbGUgdGhlXG4gICAgICAgIC8vIGZpbGUgdmlld2VyIGlzIG1pZC1zd2l0Y2ggZnJvbSB0aGUgZm9sZGVyIHZpZXcgdG8gdGhlIGRpYWdyYW0gKGUuZy5cbiAgICAgICAgLy8gXCJMb2FkIGFzIFN0YWNrXCIgZnJvbSBhIGZvbGRlciwgd2l0aCBubyBpbWFnZSBvcGVuIGZpcnN0KS4gQmVjYXVzZVxuICAgICAgICAvLyBwcmVzZXJ2ZVZpZXdwb3J0IHN1cHByZXNzZXMgT1NEJ3Mgb3duIG9wZW4tdGltZSBmaXQsIHRoZSBpbWFnZSB3b3VsZFxuICAgICAgICAvLyB0aGVuIGJlIGxlZnQgdW4tZml0dGVkIGF0IHRoZSB0b3AtbGVmdCAoXCJhIHRpbGVcIikuIEEgb25lLXNob3RcbiAgICAgICAgLy8gUmVzaXplT2JzZXJ2ZXIgZml0cyB0aGUgaW5zdGFudCB0aGUgY29udGFpbmVyIGZpcnN0IGhhcyBhIHJlYWwgc2l6ZSxcbiAgICAgICAgLy8gcmVnYXJkbGVzcyBvZiB3aGVuIHRoZSByZW5kZXIgYmVnYW4gKGppdC11aSMxMDYpLlxuICAgICAgICB0aGlzLmZpdFdoZW5Db250YWluZXJTaXplZChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChwbG90RGl2KSwgcmVmaXQpO1xuICAgICAgICAvLyBLZWVwIHRoZSBuYXZpZ2F0b3Igc2l6ZWQgdG8gdGhlIGNvbnRhaW5lciBhcyB0aGUgcGFuZWwgcmVzaXplcy5cbiAgICAgICAgdGhpcy52aWV3ZXIhLmFkZEhhbmRsZXIoJ3Jlc2l6ZScsICgpID0+IHRoaXMucmVzaXplTmF2aWdhdG9yKCkpO1xuICAgICAgICBkb25lKHRydWUpO1xuICAgICAgfSk7XG4gICAgICB0aGlzLnZpZXdlciEuYWRkT25jZUhhbmRsZXIoJ29wZW4tZmFpbGVkJywgKGU6IGFueSkgPT4ge1xuICAgICAgICBjb25zb2xlLndhcm4oJ1tPU0RdIG9wZW4tZmFpbGVkJywgZT8ubWVzc2FnZSA/PyBlKTtcbiAgICAgICAgZG9uZShmYWxzZSk7XG4gICAgICB9KTtcbiAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICBpZiAoIXNldHRsZWQpIGNvbnNvbGUud2FybignW09TRF0gdmlld2VyIG9wZW4gdGltZWQgb3V0Jyk7XG4gICAgICAgIGRvbmUoZmFsc2UpO1xuICAgICAgfSwgODAwMCk7XG4gICAgICB0aGlzLnZpZXdlciEub3Blbih0aWxlU291cmNlIGFzIGFueSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogRml0LXRvLWhvbWUgb25jZSBgZWxgIGZpcnN0IGhhcyBhIG5vbi16ZXJvIHNpemUuIElmIGl0J3MgYWxyZWFkeSBzaXplZCB3ZVxuICAgKiByZWx5IG9uIHRoZSB0aW1lZCByZWZpdHMgdGhlIGNhbGxlciBzY2hlZHVsZWQ7IG90aGVyd2lzZSBhIG9uZS1zaG90XG4gICAqIFJlc2l6ZU9ic2VydmVyIHJ1bnMgYHJlZml0YCB0aGUgbW9tZW50IHRoZSBjb250YWluZXIgaXMgbGFpZCBvdXQgKHRoZW5cbiAgICogZGlzY29ubmVjdHMpLCBzbyB0aGUgaW5pdGlhbCBmaXQgZG9lc24ndCBkZXBlbmQgb24gdGhlIHJlbmRlcidzIHN0YXJ0IHRpbWVcbiAgICogcmVsYXRpdmUgdG8gdGhlIGRpYWdyYW0gY29udGFpbmVyJ3MgbGF5b3V0LiBOby1vcCB3aXRob3V0IGEgY29udGFpbmVyIG9yXG4gICAqIFJlc2l6ZU9ic2VydmVyICh0aGUgdGltZWQgcmVmaXRzIHJlbWFpbiB0aGUgZmFsbGJhY2spLiBPbmUtc2hvdCArIG9ubHlcbiAgICogYXR0YWNoZWQgb24gYSBmcmVzaCBtb3VudCwgc28gc2xpY2Utc2NydWIgcmUtb3BlbnMga2VlcCB0aGUgdXNlcidzIHpvb20uXG4gICAqL1xuICBwcml2YXRlIGZpdFdoZW5Db250YWluZXJTaXplZChlbDogSFRNTEVsZW1lbnQgfCBudWxsLCByZWZpdDogKCkgPT4gdm9pZCk6IHZvaWQge1xuICAgIGlmICghZWwgfHwgdHlwZW9mIFJlc2l6ZU9ic2VydmVyID09PSAndW5kZWZpbmVkJykgcmV0dXJuO1xuICAgIGlmIChlbC5jbGllbnRXaWR0aCA+IDAgJiYgZWwuY2xpZW50SGVpZ2h0ID4gMCkgcmV0dXJuOyAvLyBhbHJlYWR5IHNpemVkIOKAlCB0aW1lZCByZWZpdHMgc3VmZmljZVxuICAgIGNvbnN0IHJvID0gbmV3IFJlc2l6ZU9ic2VydmVyKCgpID0+IHtcbiAgICAgIGlmIChlbC5jbGllbnRXaWR0aCA+IDAgJiYgZWwuY2xpZW50SGVpZ2h0ID4gMCkge1xuICAgICAgICByby5kaXNjb25uZWN0KCk7XG4gICAgICAgIHJlZml0KCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgcm8ub2JzZXJ2ZShlbCk7XG4gICAgLy8gU2FmZXR5IG5ldDogc3RvcCBvYnNlcnZpbmcgZXZlbiBpZiBpdCBuZXZlciBnYWlucyBhIHNpemUuXG4gICAgc2V0VGltZW91dCgoKSA9PiByby5kaXNjb25uZWN0KCksIDUwMDApO1xuICB9XG5cbiAgLyoqXG4gICAqIEN1c3RvbSB0aWxlIHNvdXJjZSBidWlsdCBmcm9tIHRoZSBkZXNjcmlwdG9yLiBPU0QgbnVtYmVycyBsZXZlbHNcbiAgICogY29hcnNlc3QtZmlyc3Q7IHRoZSBiYWNrZW5kIG51bWJlcnMgcmVzb2x1dGlvbnMgZnVsbC1yZXMtZmlyc3QsIHNvXG4gICAqIGByZXMgPSAobGV2ZWxzLTEpIC0gb3NkTGV2ZWxgLiBPdmVycmlkaW5nIGdldExldmVsU2NhbGUvZ2V0TnVtVGlsZXMgbGV0cyB1c1xuICAgKiBob25vdXIgQmlvLUZvcm1hdHMnIGFjdHVhbCBwZXItbGV2ZWwgZGltZW5zaW9ucyAobm90IGFzc3VtZSBwb3dlci1vZi10d28pLlxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFRpbGVTb3VyY2UoZDogVGlsZURlc2NyaXB0b3IsIGluZm9CNjQ6IHN0cmluZywgejogbnVtYmVyLCBjaGFubmVsPzogbnVtYmVyKTogYW55IHtcbiAgICAvLyBNdWx0aWNoYW5uZWwgaW1hZ2VzIGNvbXBvc2l0ZSBmcm9tIHBlci1jaGFubmVsIHRpbGVzLCB3aGljaCBvbmx5IGV4aXN0IGF0XG4gICAgLy8gcmVhbCBCaW8tRm9ybWF0cyByZXNvbHV0aW9ucyDigJQgc28gZHJpdmUgT1NEIG9mZiB0aGUgcmVhbCBsZXZlbHMgYWxvbmUgYW5kXG4gICAgLy8gc2tpcCB0aGUgc3ludGhldGljIChzZXJ2ZXItY29tcG9zaXRlZCkgb3ZlcnZpZXdzLiBFdmVyeSBkaXNwbGF5ZWQgdGlsZSBpc1xuICAgIC8vIHRoZW4gcGVyLWNoYW5uZWwtZmV0Y2hhYmxlIGF0IGFueSB6b29tLlxuICAgIGNvbnN0IGxldmVscyA9IHRoaXMuaXNNdWx0aUNoYW5uZWwgPyBkLmxldmVscy5zbGljZSgwLCB0aGlzLnJlYWxMZXZlbHMpIDogZC5sZXZlbHM7XG4gICAgY29uc3QgbiA9IGxldmVscy5sZW5ndGg7XG4gICAgY29uc3QgdCA9IGQudGlsZVNpemU7XG4gICAgY29uc3QgYmFzZSA9IHRoaXMuYXBpO1xuXG4gICAgLy8gT1NEIGxldmVsIGkgKDAgPSBjb2Fyc2VzdCkgPC0+IGJhY2tlbmQgcmVzb2x1dGlvbiAobi0xLWkpIChyZXMgMCA9IGZ1bGwpLlxuICAgIC8vIFRoZSBiYWNrZW5kIHB5cmFtaWQgaXMgTk9UIG5lY2Vzc2FyaWx5IHBvd2VyLW9mLXR3bywgc28gd2UgZHJpdmUgT1NEIG9mZlxuICAgIC8vIHRoZSBiYWNrZW5kJ3MgcmVhbCBwZXItbGV2ZWwgZGltZW5zaW9ucy4gV2Ugb3ZlcnJpZGUgT05MWSBnZXRMZXZlbFNjYWxlOlxuICAgIC8vIE9TRCBkZXJpdmVzIGdldE51bVRpbGVzLCBnZXRUaWxlQm91bmRzIEFORCBpdHMgcGVyLXpvb20gbGV2ZWwgc2VsZWN0aW9uXG4gICAgLy8gZnJvbSBnZXRMZXZlbFNjYWxlLCBzbyB0aGV5IGFsbCBzdGF5IGNvbnNpc3RlbnQg4oCUIHJlcXVlc3RpbmcgZXhhY3RseSB0aGVcbiAgICAvLyB0aWxlcyBlYWNoIHJlc29sdXRpb24gYWN0dWFsbHkgaGFzIChubyBvdXQtb2YtcmFuZ2UgNDAwcywgbm8gZmxvb2QpLlxuICAgIGNvbnN0IHJlc0ZvckxldmVsID0gKGxldmVsOiBudW1iZXIpID0+IG4gLSAxIC0gbGV2ZWw7XG4gICAgY29uc3QgdHM6IGFueSA9IG5ldyAoT1NEIGFzIGFueSkuVGlsZVNvdXJjZSh7XG4gICAgICB3aWR0aDogZC53aWR0aCxcbiAgICAgIGhlaWdodDogZC5oZWlnaHQsXG4gICAgICB0aWxlU2l6ZTogdCxcbiAgICAgIHRpbGVPdmVybGFwOiAwLFxuICAgICAgbWluTGV2ZWw6IDAsXG4gICAgICBtYXhMZXZlbDogbiAtIDEsXG4gICAgfSk7XG4gICAgdHMuZ2V0TGV2ZWxTY2FsZSA9IChsZXZlbDogbnVtYmVyKSA9PiB7XG4gICAgICBjb25zdCBsdmwgPSBsZXZlbHNbcmVzRm9yTGV2ZWwobGV2ZWwpXTtcbiAgICAgIHJldHVybiBsdmwgPyBsdmwud2lkdGggLyBkLndpZHRoIDogMTtcbiAgICB9O1xuICAgIC8vIERyaXZlIHRoZSB0aWxlIENPVU5UIG9mZiBlYWNoIGxldmVsJ3Mgb3duIChpbmRlcGVuZGVudGx5LXJvdW5kZWQpIGRpbWVuc2lvbnMsXG4gICAgLy8gbm90IE9TRCdzIGRlZmF1bHQgYGNlaWwoc2NhbGUgKiBmdWxsRGltZW5zaW9uIC8gdGlsZVNpemUpYC4gU3ludGhldGljIEFORCByZWFsXG4gICAgLy8gQmlvLUZvcm1hdHMgbGV2ZWxzIGFyZW4ndCBleGFjdCBwcm9wb3J0aW9uYWwgc2NhbGVzIG9mIHRoZSBmdWxsIGltYWdlLCBzb1xuICAgIC8vIGBzY2FsZSAqIGZ1bGxIZWlnaHRgIGNhbiByb3VuZCB1cCB0byBvbmUgbW9yZSByb3cgKG9yIGNvbHVtbikgdGhhbiB0aGUgbGV2ZWxcbiAgICAvLyBhY3R1YWxseSBoYXMg4oCUIE9TRCB0aGVuIHJlcXVlc3RzIGFuIG91dC1vZi1yYW5nZSB0aWxlIHRoYXQgdGhlIHNlcnZlciA0MDBzXG4gICAgLy8gKFwiVGlsZSAoY29sLHJvdykgb3V0IG9mIHJhbmdlXCIpLiBVc2luZyB0aGUgbGV2ZWwncyByZWFsIHcvaCBtYXRjaGVzIHRoZVxuICAgIC8vIHNlcnZlcidzIG93biBib3VuZHMgY2hlY2sgZXhhY3RseS5cbiAgICBjb25zdCBQb2ludCA9IChPU0QgYXMgYW55KS5Qb2ludDtcbiAgICB0cy5nZXROdW1UaWxlcyA9IChsZXZlbDogbnVtYmVyKSA9PiB7XG4gICAgICBjb25zdCBsdmwgPSBsZXZlbHNbcmVzRm9yTGV2ZWwobGV2ZWwpXTtcbiAgICAgIGlmICghbHZsKSByZXR1cm4gbmV3IFBvaW50KDAsIDApO1xuICAgICAgcmV0dXJuIG5ldyBQb2ludChNYXRoLmNlaWwobHZsLndpZHRoIC8gdCksIE1hdGguY2VpbChsdmwuaGVpZ2h0IC8gdCkpO1xuICAgIH07XG4gICAgdHMuZ2V0VGlsZVVybCA9IChsZXZlbDogbnVtYmVyLCB4OiBudW1iZXIsIHk6IG51bWJlcikgPT5cbiAgICAgIGJ1aWxkVGlsZVVybChiYXNlLCBpbmZvQjY0LCB7IHJlczogcmVzRm9yTGV2ZWwobGV2ZWwpLCBjb2w6IHgsIHJvdzogeSwgeiwgdGlsZVNpemU6IHQsIGNoYW5uZWwgfSk7XG4gICAgcmV0dXJuIHRzO1xuICB9XG5cbiAgLyoqIFJlLWFwcGx5IHRoZSBkaXNwbGF5IHBpcGVsaW5lICh3aW5kb3cvZ2FtbWEvY29sb3VyL2ludmVydCkgYWZ0ZXIgYSBzdGF0ZSBjaGFuZ2UuXG4gICAqICBNdWx0aWNoYW5uZWwgaW52YWxpZGF0ZXMgT05MWSB0aGUgdmlzaWJsZSBzbGljZSdzIGNoYW5uZWwgaW1hZ2VzIOKAlCBpbnZhbGlkYXRpbmdcbiAgICogIHRoZSB3aG9sZSB3b3JsZCB3b3VsZCByZS1wcm9jZXNzIGV2ZXJ5IGhpZGRlbi9wcmVsb2FkZWQgc2xpY2UncyB0aWxlcyAoaHVuZHJlZHMpLFxuICAgKiAgZmxvb2RpbmcgT1NEIHdpdGggXCJbQ2FjaGVSZWNvcmRdIOKApiBJbnZhbGlkU3RhdGVFcnJvclwiIGFuZCB3YXN0aW5nIHdvcmsgb24gdGlsZXNcbiAgICogIHRoYXQgYXJlbid0IG9uIHNjcmVlbi4gVGhlIG90aGVyIGNhY2hlZCBzbGljZXMgYXJlIG1hcmtlZCBzdGFsZSBhbmQgcmUtdGludGVkXG4gICAqICBsYXppbHkgd2hlbiByZXZlYWxlZC4gQ29tcG9zaXRlL2dyYXlzY2FsZSBpbnZhbGlkYXRlIHRoZSB3b3JsZCBhcyBiZWZvcmUuICovXG4gIHByaXZhdGUgaW52YWxpZGF0ZURpc3BsYXkoKTogdm9pZCB7XG4gICAgY29uc3QgdjogYW55ID0gdGhpcy52aWV3ZXI7XG4gICAgaWYgKCF2KSByZXR1cm47XG4gICAgaWYgKHRoaXMuaXNNdWx0aUNoYW5uZWwpIHtcbiAgICAgIHRoaXMuY2FjaGUuaW52YWxpZGF0ZUNoYW5uZWxEaXNwbGF5KHRoaXMuY3VycmVudFopO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0cnkgeyB2LndvcmxkLnJlcXVlc3RJbnZhbGlkYXRlKHRydWUpOyB9IGNhdGNoIHsgLyogbm8tb3AgKi8gfVxuICAgIHRyeSB7IHYubmF2aWdhdG9yPy53b3JsZD8ucmVxdWVzdEludmFsaWRhdGUodHJ1ZSk7IH0gY2F0Y2ggeyAvKiBuby1vcCAqLyB9XG4gIH1cblxuICBwcml2YXRlIGRlc3Ryb3lWaWV3ZXIoKTogdm9pZCB7XG4gICAgdGhpcy5jYWNoZS5jYW5jZWxCYWNrZ3JvdW5kTG9hZCgpO1xuICAgIC8vIFRlYXIgZG93biBhbnkgYWN0aXZlIHRvb2wgb3ZlcmxheXMgKHNoYXJlZCBzaW5nbGV0b25zKS5cbiAgICB0aGlzLndhbmRUb29sLnNldE1vZGUoZmFsc2UpO1xuICAgIHRoaXMuYnJ1c2hUb29sLnNldE1vZGUoZmFsc2UpO1xuICAgIHRoaXMuc2FtUG9pbnRUb29sLnNldE1vZGUoZmFsc2UpO1xuICAgIHRoaXMuZXJhc2VyVG9vbC5zZXRNb2RlKGZhbHNlKTtcbiAgICBpZiAodGhpcy5vdmVybGF5KSB7XG4gICAgICB0aGlzLm92ZXJsYXkuZGVzdHJveSgpO1xuICAgICAgdGhpcy5vdmVybGF5ID0gbnVsbDtcbiAgICB9XG4gICAgaWYgKHRoaXMuc2NhbGVCYXIpIHtcbiAgICAgIHRoaXMuc2NhbGVCYXIuZGVzdHJveSgpO1xuICAgICAgdGhpcy5zY2FsZUJhciA9IG51bGw7XG4gICAgfVxuICAgIGlmICh0aGlzLnZpZXdlcikge1xuICAgICAgdGhpcy52aWV3ZXIuZGVzdHJveSgpO1xuICAgICAgdGhpcy52aWV3ZXIgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLmNvb3JkVHJhbnNmb3JtID0gbnVsbDtcbiAgICB0aGlzLmNhY2hlLnJlc2V0KCk7XG4gICAgdGhpcy5zYW1wbGVyLmNsZWFyKCk7XG4gIH1cblxuICAvKiogVGhpcyBiYWNrZW5kJ3MgcmVnaW9uIHJlbmRlcmVyICh0aGUgU1ZHIG92ZXJsYXkpLCBvbmNlIGEgcGxvdCBpcyBtb3VudGVkLiAqL1xuICBnZXRSZWdpb25PdmVybGF5KCk6IElSZWdpb25PdmVybGF5IHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMub3ZlcmxheTtcbiAgfVxuXG4gIC8qKiBPU0QgcmVuZGVycyBvbmx5IHRoZSBpbWFnZSB0eXBlIOKAlCBubyBpc29zdXJmYWNlLCBzbyBubyBjb250cm9scy4gKi9cbiAgZ2V0SXNvc3VyZmFjZUNvbnRyb2xzKCk6IElJc29zdXJmYWNlQ29udHJvbHMgfCBudWxsIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKiBPU0QgcmVuZGVycyBvbmx5IHRoZSBpbWFnZSB0eXBlIOKAlCBubyAzRCBzY2VuZXMsIHNvIG5vIGNvbnRyb2xzLiAqL1xuICBnZXRTdXJmYWNlM2RDb250cm9scygpOiBJU3VyZmFjZTNkQ29udHJvbHMgfCBudWxsIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKiBPU0QgcmVuZGVycyBvbmx5IHRoZSBpbWFnZSB0eXBlIOKAlCBubyBMSU5FIGludGVuc2l0eSBST0lzLCBzbyBubyBjb250cm9scy4gKi9cbiAgZ2V0SW50ZW5zaXR5Q29udHJvbHMoKTogSUludGVuc2l0eUNvbnRyb2xzIHwgbnVsbCB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBwcml2YXRlIHRvT3ZlcmxheU1vZGUobW9kZTogc3RyaW5nIHwgZmFsc2UpOiBSZWdpb25Ub29sTW9kZSB7XG4gICAgc3dpdGNoIChtb2RlKSB7XG4gICAgICBjYXNlICdkcmF3cmVjdCc6XG4gICAgICAgIHJldHVybiAnZHJhd3JlY3QnO1xuICAgICAgY2FzZSAnZHJhd2Nsb3NlZHBhdGgnOlxuICAgICAgICByZXR1cm4gJ2RyYXdjbG9zZWRwYXRoJztcbiAgICAgIGNhc2UgJ2RyYXdvcGVucGF0aCc6XG4gICAgICAgIHJldHVybiAnZHJhd29wZW5wYXRoJztcbiAgICAgIGNhc2UgJ2RyYXdwb2x5Z29uJzpcbiAgICAgICAgcmV0dXJuICdkcmF3cG9seWdvbic7XG4gICAgICBjYXNlICdhZGRwb2ludCc6XG4gICAgICAgIHJldHVybiAnYWRkcG9pbnQnO1xuICAgICAgY2FzZSAnZGVsZXRlcG9pbnQnOlxuICAgICAgICByZXR1cm4gJ2RlbGV0ZXBvaW50JztcbiAgICAgIGNhc2UgJ21vdmUnOlxuICAgICAgICByZXR1cm4gJ21vdmUnO1xuICAgICAgY2FzZSAnc2VsZWN0JzpcbiAgICAgICAgcmV0dXJuICdzZWxlY3QnO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuICdub25lJztcbiAgICB9XG4gIH1cblxuICByZWxvYWRBbmRQbG90KCk6IHZvaWQge1xuICAgIC8qIGhvc3QgcmUtZHJpdmVzIHBsb3QoKSB2aWEgdGhlIGltYWdlLWluZm8gc3RyZWFtICovXG4gIH1cbiAgcmVzZXQoKTogdm9pZCB7XG4gICAgdGhpcy5kZXN0cm95Vmlld2VyKCk7XG4gIH1cbiAgcmVsYXlvdXQoX3RydWVJbWFnZVNpemU/OiBudW1iZXJbXSk6IHZvaWQge1xuICAgIGNvbnN0IHZwID0gdGhpcy52aWV3ZXI/LnZpZXdwb3J0O1xuICAgIGlmICghdnApIHJldHVybjtcbiAgICAvLyBLZWVwIHRoZSB1c2VyJ3MgY3VycmVudCB2aWV3IGFjcm9zcyBhIGNvbnRhaW5lci9zcGxpdCByZXNpemUgaW5zdGVhZCBvZlxuICAgIC8vIHNuYXBwaW5nIGhvbWUuIFZpZXdwb3J0IGJvdW5kcyBhcmUgaW1hZ2UtcmVsYXRpdmUgKGltYWdlIHdpZHRoID0gMSksIHNvXG4gICAgLy8gdGhleSdyZSBpbmRlcGVuZGVudCBvZiB0aGUgY29udGFpbmVyJ3MgcGl4ZWwgc2l6ZSDigJQgY2FwdHVyZSB0aGUgdmlzaWJsZVxuICAgIC8vIHJlZ2lvbiBub3cgYW5kIHJlLWZpdCBpdCBvbmNlIHRoZSBuZXcgc2l6ZSBoYXMgc2V0dGxlZC4gT1NEJ3MgYXV0b1Jlc2l6ZVxuICAgIC8vIGZpcmVzIGFzeW5jaHJvbm91c2x5IGFuZCB0aGUgYW5ndWxhci1zcGxpdCB0cmFuc2l0aW9uIGFuaW1hdGVzIHRoZSB3aWR0aFxuICAgIC8vIG92ZXIgYSBmZXcgaHVuZHJlZCBtcywgc28gcmVzdG9yZSBvbiB0aGUgbmV4dCBmcmFtZSBhbmQgYWdhaW4gYWZ0ZXIgdGhlXG4gICAgLy8gdHJhbnNpdGlvbiBjb21wbGV0ZXMuXG4gICAgY29uc3QgYm91bmRzID0gdnAuZ2V0Qm91bmRzKHRydWUpO1xuICAgIGNvbnN0IHJlc3RvcmUgPSAoKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICB0aGlzLnZpZXdlcj8udmlld3BvcnQuZml0Qm91bmRzKGJvdW5kcywgdHJ1ZSk7XG4gICAgICAgIHRoaXMudmlld2VyPy52aWV3cG9ydC5hcHBseUNvbnN0cmFpbnRzKHRydWUpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8qIHZpZXdlciB0b3JuIGRvd24gKi9cbiAgICAgIH1cbiAgICB9O1xuICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZShyZXN0b3JlKTtcbiAgICBzZXRUaW1lb3V0KHJlc3RvcmUsIDM1MCk7XG4gIH1cbiAgcmVzZXRBeGVzKCk6IHZvaWQge1xuICAgIHRoaXMudmlld2VyPy52aWV3cG9ydC5nb0hvbWUoKTtcbiAgfVxuICBhdXRvc2NhbGUoKTogdm9pZCB7XG4gICAgdGhpcy52aWV3ZXI/LnZpZXdwb3J0LmdvSG9tZSgpO1xuICAgIHRoaXMuYXV0b3NjYWxlRXZlbnQkLm5leHQoJ2F1dG9zY2FsZScpO1xuICB9XG4gIHpvb21JbigpOiB2b2lkIHtcbiAgICB0aGlzLnZpZXdlcj8udmlld3BvcnQuem9vbUJ5KDEuMyk7XG4gICAgdGhpcy52aWV3ZXI/LnZpZXdwb3J0LmFwcGx5Q29uc3RyYWludHMoKTtcbiAgfVxuICB6b29tT3V0KCk6IHZvaWQge1xuICAgIHRoaXMudmlld2VyPy52aWV3cG9ydC56b29tQnkoMSAvIDEuMyk7XG4gICAgdGhpcy52aWV3ZXI/LnZpZXdwb3J0LmFwcGx5Q29uc3RyYWludHMoKTtcbiAgfVxuICBzZXREcmFnTW9kZShtb2RlOiBzdHJpbmcgfCBmYWxzZSk6IHZvaWQge1xuICAgIC8vIFJlY3RhbmdsZS9wb2x5Z29uIGRyYXdpbmcgcnVuIG9uIHRoZSBTVkcgb3ZlcmxheTsgd2FuZC9lcmFzZXIvem9vbS1ib3hcbiAgICAvLyAoY3VzdG9tIFBsb3RseSBvdmVybGF5cykgYXJlIHJvdXRlZCB0byBQbG90bHkgYnkgdGhlIHJvdXRlciBhbmQgYXJlbid0XG4gICAgLy8gaGFuZGxlZCBoZXJlIHlldC5cbiAgICB0aGlzLm92ZXJsYXk/LnNldE1vZGUodGhpcy50b092ZXJsYXlNb2RlKG1vZGUpKTtcbiAgfVxuXG4gIHNldFNob3dTdGFjayhfc2hvd3N0YWNrOiBib29sZWFuKTogdm9pZCB7XG4gICAgLyogT1NEIG5hdmlnYXRlcyB0aGUgc3RhY2sgdmlhIHRoZSB6LXNsaWRlciAtPiBzZXRaSW5kZXggKi9cbiAgfVxuXG4gIC8qKiBTaG93L2hpZGUgdGhlIG92ZXJ2aWV3IG5hdmlnYXRvci4gU3RvcmVkIGZvciB0aGUgbmV4dCB2aWV3ZXIgY3JlYXRpb24sIGFuZFxuICAgKiAgYXBwbGllZCBsaXZlIHdoZW4gYSB2aWV3ZXIgaXMgYWxyZWFkeSBtb3VudGVkIChoaWRlcy9yZXZlYWxzIGl0cyBlbGVtZW50IOKAlFxuICAgKiAgdGhlIG5hdmlnYXRvciBpbnN0YW5jZSBpdHNlbGYgb25seSBleGlzdHMgd2hlbiBjcmVhdGVkIHdpdGggc2hvd05hdmlnYXRvcikuICovXG4gIHNldE5hdmlnYXRvclZpc2libGUodmlzaWJsZTogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMubmF2aWdhdG9yVmlzaWJsZSA9IHZpc2libGU7XG4gICAgY29uc3QgbmF2RWw6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkID0gKHRoaXMudmlld2VyIGFzIGFueSk/Lm5hdmlnYXRvcj8uZWxlbWVudDtcbiAgICBpZiAobmF2RWwpIG5hdkVsLnN0eWxlLmRpc3BsYXkgPSB2aXNpYmxlID8gJycgOiAnbm9uZSc7XG4gIH1cblxuICAvKiogVG9nZ2xlIGJpbGluZWFyIHNtb290aGluZyB2cyBuZWFyZXN0LW5laWdoYm91ciAoY3Jpc3AgcGl4ZWxzKS4gU3RvcmVkIGZvciB0aGVcbiAgICogIG5leHQgdmlld2VyIGNyZWF0aW9uLCBhbmQgYXBwbGllZCBsaXZlICh3aXRoIGEgcmVkcmF3KSB0byBhIG1vdW50ZWQgdmlld2VyLiAqL1xuICBzZXRJbWFnZVNtb290aGluZ0VuYWJsZWQoZW5hYmxlZDogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuc21vb3RoaW5nRW5hYmxlZCA9IGVuYWJsZWQ7XG4gICAgY29uc3QgZHJhd2VyOiBhbnkgPSAodGhpcy52aWV3ZXIgYXMgYW55KT8uZHJhd2VyO1xuICAgIGlmIChkcmF3ZXI/LnNldEltYWdlU21vb3RoaW5nRW5hYmxlZCkge1xuICAgICAgZHJhd2VyLnNldEltYWdlU21vb3RoaW5nRW5hYmxlZChlbmFibGVkKTtcbiAgICAgIHRoaXMudmlld2VyPy5mb3JjZVJlZHJhdygpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTd2FwIHRoZSBkaXNwbGF5ZWQgei1zbGljZSBsaXZlLiBTbGljZXMgYXJlIGtlcHQgYXMgc2VwYXJhdGUgdGlsZWQgaW1hZ2VzIGluXG4gICAqIHRoZSB3b3JsZCwgc28gc3dpdGNoaW5nIGlzIGp1c3QgYW4gb3BhY2l0eSB0b2dnbGU6IGEgcHJldmlvdXNseS12aXNpdGVkIHNsaWNlXG4gICAqIHNob3dzIGluc3RhbnRseSAoaXRzIGRlY29kZWQgKyByZWNvbG9yZWQgdGlsZXMgYXJlIHN0aWxsIHJlc2lkZW50KSwgYW5kIGFcbiAgICogbmV2ZXItc2VlbiBzbGljZSBpcyBhZGRlZCBvbmNlLiBUaGUgcmVnaW9uIG92ZXJsYXksIGNvb3JkaW5hdGUgdHJhbnNmb3JtLFxuICAgKiBzY2FsZSBiYXIsIGNvbG9ybWFwIHBpcGVsaW5lIGFuZCBjdXJyZW50IHpvb20vcGFuIGFsbCBwZXJzaXN0IOKAlCB0aGUgeC95XG4gICAqIGdlb21ldHJ5IGlzIGlkZW50aWNhbCBhY3Jvc3Mgc2xpY2VzLlxuICAgKlxuICAgKiBTaW1wbGUgbW9kZSAodGlsZWQ6ZmFsc2Ug4oCUIGEgbnVtYmVyZWQgaW1hZ2Ugc2VyaWVzIGFzc2VtYmxlZCBjbGllbnQtc2lkZSxcbiAgICogZWFjaCBzbGljZSBpdHMgb3duIGNvbXBsZXRlIGltYWdlIHdpdGggbm8gc2hhcmVkIHRpbGUgc2VydmVyKSBoYXMgbm9cbiAgICogcHlyYW1pZCB0byB0b2dnbGUgb3BhY2l0eSBvbiwgc28gaXQgcmUtb3BlbnMgdGhlIHZpZXdlciBvbiB0aGUgbmV3XG4gICAqIHNsaWNlJ3MgVVJMIGluc3RlYWQuIHByZXNlcnZlVmlld3BvcnQgKHZpZXdlciBvcHRpb24sIHNlZSBwbG90KCkpIGtlZXBzXG4gICAqIHRoZSBjdXJyZW50IHpvb20vcGFuIGFjcm9zcyB0aGF0IHJlb3Blbi5cbiAgICovXG4gIHNldFpJbmRleCh6SW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IHogPSB6SW5kZXggfHwgMDtcbiAgICBpZiAoeiA9PT0gdGhpcy5jdXJyZW50WikgcmV0dXJuO1xuICAgIGlmICghdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICBpZiAodGhpcy5zaW1wbGVNb2RlKSB7XG4gICAgICBjb25zdCByYXdVcmwgPSB0aGlzLnNpbXBsZVVybHNbel0gPz8gdGhpcy5zaW1wbGVVcmxzWzBdO1xuICAgICAgaWYgKCFyYXdVcmwpIHJldHVybjtcbiAgICAgIHRoaXMuY3VycmVudFogPSB6O1xuICAgICAgdGhpcy52aWV3cG9ydFBpeGVscyA9IG51bGw7XG4gICAgICAvLyBGZXRjaGVkIHZpYSBTaW1wbGVTbGljZUFjY2Vzc1NlcnZpY2UgKGF1dGggaW50ZXJjZXB0b3IgYXBwbGllcykgdGhlblxuICAgICAgLy8gdXBzY2FsZWQgdG8gZnVsbC1yZXMgKHNhbWUgYXMgdGhlIGluaXRpYWwgbG9hZCDigJQgc2VlIHRvRnVsbFJlc1VybCkgc29cbiAgICAgIC8vIHRoZSB3b3JsZCBzdGF5cyBmdWxsLXJlcyBhY3Jvc3Mgc2xpY2VzIGFuZCBST0lzIGtlZXAgYWxpZ25pbmcuIEd1YXJkXG4gICAgICAvLyBhZ2FpbnN0IGEgbmV3ZXIgc2NydWIgbGFuZGluZyBmaXJzdC5cbiAgICAgIHRoaXMuc2ltcGxlU3RhY2suZmV0Y2hBc0Jsb2JVcmwocmF3VXJsKVxuICAgICAgICAudGhlbigocHJldmlld1VybCkgPT5cbiAgICAgICAgICB0aGlzLnRvRnVsbFJlc1VybChwcmV2aWV3VXJsLCB0aGlzLmRlc2NyaXB0b3I/LndpZHRoID8/IDAsIHRoaXMuZGVzY3JpcHRvcj8uaGVpZ2h0ID8/IDApLFxuICAgICAgICApXG4gICAgICAgIC50aGVuKCh1cmwpID0+IHtcbiAgICAgICAgICBpZiAodGhpcy5jdXJyZW50WiAhPT0geiB8fCAhdGhpcy52aWV3ZXIpIHJldHVybjtcbiAgICAgICAgICB0aGlzLnZpZXdlci5hZGRPbmNlSGFuZGxlcignb3Blbi1mYWlsZWQnLCAoZTogYW55KSA9PiB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oJ1tPU0RdIHNsaWNlIHJlLW9wZW4gZmFpbGVkJywgZT8ubWVzc2FnZSA/PyBlKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgICB0aGlzLnZpZXdlci5vcGVuKHsgdHlwZTogJ2ltYWdlJywgdXJsIH0gYXMgYW55KTtcbiAgICAgICAgfSlcbiAgICAgICAgLmNhdGNoKChlcnIpID0+IGNvbnNvbGUud2FybignW09TRF0gc2xpY2UgZmV0Y2ggZmFpbGVkJywgZXJyKSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICghdGhpcy5kZXNjcmlwdG9yIHx8ICF0aGlzLmluZm9CNjQpIHJldHVybjtcbiAgICB0aGlzLmN1cnJlbnRaID0gejtcbiAgICB0aGlzLnZpZXdwb3J0UGl4ZWxzID0gbnVsbDsgLy8gd2FuZCByZWFkYmFjayBpcyBzbGljZS1zcGVjaWZpY1xuICAgIHRoaXMuY2FjaGUuc2hvd1NsaWNlKHopO1xuICAgIGlmICh0aGlzLmlzTXVsdGlDaGFubmVsKSB7XG4gICAgICB0aGlzLnNhbXBsZXIuY29tcHV0ZU11bHRpQ2hhbm5lbEhpc3RvZ3JhbXModGhpcy5kZXNjcmlwdG9yLCB0aGlzLmluZm9CNjQsIHopO1xuICAgIH1cbiAgfVxuXG4gIHNldFN0YWNrTG9hZGluZyhzdGFja0xvYWRpbmc6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLnN0YWNrTG9hZGluZyQubmV4dChzdGFja0xvYWRpbmcpO1xuICB9XG4gIGlzU3RhY2tMb2FkaW5nKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIHJldHVybiB0aGlzLnN0YWNrTG9hZGluZyQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cbiAgZ2V0U3RhY2tMb2FkaW5nUHJvZ3Jlc3MoKTogT2JzZXJ2YWJsZTxudW1iZXI+IHtcbiAgICByZXR1cm4gdGhpcy5zdGFja0xvYWRpbmdQcm9ncmVzcyQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICBnZXRUcnVlSW1hZ2VTaXplKCk6IHsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmRlc2NyaXB0b3IgPyB7IHdpZHRoOiB0aGlzLmRlc2NyaXB0b3Iud2lkdGgsIGhlaWdodDogdGhpcy5kZXNjcmlwdG9yLmhlaWdodCB9IDogbnVsbDtcbiAgfVxuICBnZXRDdXJyZW50SW1hZ2UoKTogUHJvbWlzZTxJbWFnZSB8IG51bGw+IHtcbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKG51bGwpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBjdXJyZW50bHkgZGlzcGxheWVkIHBpeGVscyDigJQgdGhlIHJlbmRlcmVkIHZpZXdwb3J0ICh3aGF0IHRoZSB1c2VyIHNlZXMsXG4gICAqIGluY2x1ZGluZyB0aGUgY3VycmVudCB6b29tIGFuZCBhbnkgY29sb3JtYXAgcmVjb2xvciksIGFzIFJHQkEuIEZlZWRzIHRoZVxuICAgKiBwcm9jZXNzaW5nLXBpcGVsaW5lIGRpYWxvZydzIFwidXNlIGN1cnJlbnQgdmlld1wiLiBSZXR1cm5zIG51bGwgdW50aWwgdGlsZXNcbiAgICogYXJlIGRyYXduLlxuICAgKi9cbiAgZ2V0RGlzcGxheWVkUGl4ZWxEYXRhKCk6IFBpeGVsRGF0YSB8IG51bGwge1xuICAgIGNvbnN0IGNhbnZhczogSFRNTENhbnZhc0VsZW1lbnQgfCB1bmRlZmluZWQgPSAodGhpcy52aWV3ZXIgYXMgYW55KT8uZHJhd2VyPy5jYW52YXM7XG4gICAgaWYgKCFjYW52YXMgfHwgIWNhbnZhcy53aWR0aCB8fCAhY2FudmFzLmhlaWdodCkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgY3R4ID0gY2FudmFzLmdldENvbnRleHQoJzJkJywgeyB3aWxsUmVhZEZyZXF1ZW50bHk6IHRydWUgfSk7XG4gICAgaWYgKCFjdHgpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IHsgd2lkdGgsIGhlaWdodCB9ID0gY2FudmFzO1xuICAgIGNvbnN0IGltZyA9IGN0eC5nZXRJbWFnZURhdGEoMCwgMCwgd2lkdGgsIGhlaWdodCk7XG4gICAgcmV0dXJuIHsgd2lkdGgsIGhlaWdodCwgY2hhbm5lbHM6IDQsIGRhdGE6IGltZy5kYXRhIH07XG4gIH1cblxuICAvKipcbiAgICogSW1hZ2UtcGl4ZWwgcmVjdGFuZ2xlIHRoZSBkcmF3ZXIgY2FudmFzICh3aGF0IHtAbGluayBnZXREaXNwbGF5ZWRQaXhlbERhdGF9XG4gICAqIHJlYWRzKSBjdXJyZW50bHkgY292ZXJzLiBVbmxpa2Uge0BsaW5rIGVtaXRWaWV3cG9ydENoYW5nZX0sIHRoaXMgaXMgTk9UXG4gICAqIGNsYW1wZWQgdG8gdGhlIGltYWdlIGJvdW5kczogdGhlIGNhbnZhcyBtYXBzIDE6MSB0byB0aGUgdmlld3BvcnQgcmVjdGFuZ2xlLFxuICAgKiBzbyBsZWF2aW5nIGl0IHVuY2xhbXBlZCBrZWVwcyBgY2FudmFzUHggLT4gaW1hZ2VQeGAgYW4gZXhhY3QgYWZmaW5lIG1hcFxuICAgKiAoY2xhbXBpbmcgd291bGQgc2tldyBjb29yZGluYXRlcyBuZWFyIHRoZSBpbWFnZSBlZGdlcykuIFJvdXRlZCB0aHJvdWdoIHdvcmxkXG4gICAqIGl0ZW0gMCAoc2VlIHtAbGluayB2aWV3cG9ydFJlY3RUb0ltYWdlfSkgZm9yIG11bHRpLWxheWVyIGFjY3VyYWN5LlxuICAgKi9cbiAgZ2V0RGlzcGxheWVkU291cmNlUmVjdCgpOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyOyB3aWR0aDogbnVtYmVyOyBoZWlnaHQ6IG51bWJlciB9IHwgbnVsbCB7XG4gICAgY29uc3QgdnA6IGFueSA9IHRoaXMudmlld2VyPy52aWV3cG9ydDtcbiAgICBpZiAoIXZwIHx8ICF0aGlzLmRlc2NyaXB0b3IpIHJldHVybiBudWxsO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByID0gdmlld3BvcnRSZWN0VG9JbWFnZSh0aGlzLnZpZXdlciwgdnAuZ2V0Qm91bmRzKHRydWUpKTtcbiAgICAgIHJldHVybiB7IHg6IHIueCwgeTogci55LCB3aWR0aDogci53aWR0aCwgaGVpZ2h0OiByLmhlaWdodCB9O1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICB9XG5cbiAgZG93bmxvYWRJbWFnZSgpOiB2b2lkIHtcbiAgICAvLyBTbmFwc2hvdCB0aGUgY3VycmVudGx5IHJlbmRlcmVkIE9TRCB2aWV3IGFzIGEgUE5HIChXWVNJV1lHKSDigJQgdGhlIHBhcmFsbGVsXG4gICAgLy8gdG8gUGxvdGx5J3MgZG93bmxvYWRJbWFnZS4gT1NEIHVzZXMgdGhlIDJEIGNhbnZhcyBkcmF3ZXIgYW5kIGJha2VzIHRoZVxuICAgIC8vIGFjdGl2ZSBkaXNwbGF5IHNldHRpbmdzICh3aW5kb3cgLyBnYW1tYSAvIGNvbG9ybWFwIC8gcGVyLWNoYW5uZWwgY29sb3VycyAvXG4gICAgLy8gaW52ZXJ0KSBpbnRvIHRoZSB0aWxlcyB2aWEgdGhlIHJlY29sb3IgcGlwZWxpbmUsIHNvIHRoZSBkcmF3ZXIgY2FudmFzXG4gICAgLy8gYWxyZWFkeSByZWZsZWN0cyBleGFjdGx5IHdoYXQncyBvbiBzY3JlZW4gYXQgdGhlIGN1cnJlbnQgem9vbS9wYW4uIFRoZVxuICAgIC8vIGZ1bGwtcmVzb2x1dGlvbiBzdGl0Y2hlZCBleHBvcnQgbGl2ZXMgaW4gZXhwb3J0Q29tcG9zaXRlKCkgKENoYW5uZWxzICZcbiAgICAvLyBIaXN0b2dyYW0gZGlhbG9nKS5cbiAgICBjb25zdCBjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50IHwgdW5kZWZpbmVkID0gKHRoaXMudmlld2VyIGFzIGFueSk/LmRyYXdlcj8uY2FudmFzO1xuICAgIGlmICghY2FudmFzIHx8ICFjYW52YXMud2lkdGggfHwgIWNhbnZhcy5oZWlnaHQpIHJldHVybjtcbiAgICBjb25zdCBzdGVtID0gKHRoaXMuY3VycmVudEZpbGVOYW1lIHx8ICdpbWFnZScpLnJlcGxhY2UoL1xcLlteLl0rJC8sICcnKTtcbiAgICBjYW52YXMudG9CbG9iKChibG9iKSA9PiB7XG4gICAgICBpZiAoYmxvYikgc2F2ZUFzKGJsb2IsIGAke3N0ZW19LnBuZ2ApO1xuICAgIH0sICdpbWFnZS9wbmcnKTtcbiAgfVxuXG4gIHNldFBsb3RUeXBlKF9wbG90VHlwZTogUGxvdFR5cGUpOiB2b2lkIHtcbiAgICAvKiBPU0Qgb25seSByZW5kZXJzIHRoZSBpbWFnZSB0eXBlICovXG4gIH1cbiAgc2V0U3VyZmFjZURyYWdNb2RlKF9tb2RlOiBzdHJpbmcpOiB2b2lkIHtcbiAgICAvKiAzRCBub3Qgc3VwcG9ydGVkIGJ5IE9TRCAqL1xuICB9XG4gIHJlc2V0U3VyZmFjZUNhbWVyYSgpOiB2b2lkIHtcbiAgICAvKiAzRCBub3Qgc3VwcG9ydGVkIGJ5IE9TRCAqL1xuICB9XG5cbiAgZ2V0QXV0b3NjYWxlRXZlbnQoKTogT2JzZXJ2YWJsZTxhbnk+IHtcbiAgICByZXR1cm4gdGhpcy5hdXRvc2NhbGVFdmVudCQuYXNPYnNlcnZhYmxlKCk7XG4gIH1cblxuICAvKiogVmlzaWJsZSBpbWFnZSByZWdpb24gKGZ1bGwtaW1hZ2UgcGl4ZWwgY29vcmRzKSwgZW1pdHRlZCB3aGVuIHRoZSB2aWV3XG4gICAqICBzZXR0bGVzLiBUaGUgaW50ZW5zaXR5IGluc2V0IHJlLXNhbXBsZXMgdGhpcyByZWdpb24gYXQgdGhlIHpvb20gcmVzb2x1dGlvbi4gKi9cbiAgZ2V0Vmlld3BvcnRDaGFuZ2UkKCk6IE9ic2VydmFibGU8eyB4OiBudW1iZXI7IHk6IG51bWJlcjsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfT4ge1xuICAgIHJldHVybiB0aGlzLnZpZXdwb3J0Q2hhbmdlJC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIC8qKiB7QGxpbmsgSUludGVuc2l0eVNhbXBsaW5nfSBzdHViIOKAlCBpbnRlbnNpdHkgc2FtcGxpbmcgbGl2ZXMgaW4gdGhlIFBsb3RseVxuICAgKiAgYmFja2VuZCAoaXQgb3ducyBwaXhlbCByZWFkYmFjaykuIE9TRCBmZWVkcyBpdCBvbmx5IHZpYSBnZXRWaWV3cG9ydENoYW5nZSQ7XG4gICAqICB0aGUgc2FtcGxpbmctY2FjaGUgcHJpbWluZyBpdHNlbGYgaXMgYSBuby1vcCBoZXJlLiAqL1xuICBhc3luYyBlbnN1cmVJbnRlbnNpdHlTYW1wbGluZyhfaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvLCBfekluZGV4OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvKiBuby1vcDogUGxvdGx5IG93bnMgaW50ZW5zaXR5IHNhbXBsaW5nIChzZWUgSUludGVuc2l0eVNhbXBsaW5nKSAqL1xuICB9XG5cbiAgLyoqIHtAbGluayBJSW50ZW5zaXR5U2FtcGxpbmd9IHN0dWIg4oCUIHNlZSB7QGxpbmsgZW5zdXJlSW50ZW5zaXR5U2FtcGxpbmd9LiAqL1xuICByZWZyZXNoSW50ZW5zaXR5U2FtcGxpbmdGb3JSb2koX3g6IG51bWJlciwgX3k6IG51bWJlciwgX3dpZHRoOiBudW1iZXIsIF9oZWlnaHQ6IG51bWJlcixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIF96SW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIC8qIG5vLW9wOiBQbG90bHkgb3ducyBpbnRlbnNpdHkgc2FtcGxpbmcgKHNlZSBJSW50ZW5zaXR5U2FtcGxpbmcpICovXG4gIH1cblxuICAvKiogQ29tcHV0ZSB0aGUgY3VycmVudCB2aWV3cG9ydCdzIGltYWdlLXBpeGVsIHJlY3RhbmdsZSAoY2xhbXBlZCB0byB0aGUgaW1hZ2UpXG4gICAqICBhbmQgYnJvYWRjYXN0IGl0LiAqL1xuICBwcml2YXRlIGVtaXRWaWV3cG9ydENoYW5nZSgpOiB2b2lkIHtcbiAgICBjb25zdCB2cDogYW55ID0gdGhpcy52aWV3ZXI/LnZpZXdwb3J0O1xuICAgIGlmICghdnAgfHwgIXRoaXMuZGVzY3JpcHRvcikgcmV0dXJuO1xuICAgIHRyeSB7XG4gICAgICAvLyBSb3V0ZSB0aHJvdWdoIHdvcmxkIGl0ZW0gMCAob3NkLWNvb3Jkcyk6IHZwLnZpZXdwb3J0VG9JbWFnZVJlY3RhbmdsZSBpc1xuICAgICAgLy8gaW5hY2N1cmF0ZSBhbmQgd2FybnMgd2hlbiB0aGUgd29ybGQgaG9sZHMgbXVsdGlwbGUgaW1hZ2VzIChwZXItY2hhbm5lbFxuICAgICAgLy8gbXVsdGljaGFubmVsIGxheWVycyksIHdoaWNoIGZlZCB0aGUgaW50ZW5zaXR5IGluc2V0IGEgd3JvbmcgUk9JLlxuICAgICAgY29uc3QgciA9IHZpZXdwb3J0UmVjdFRvSW1hZ2UodGhpcy52aWV3ZXIsIHZwLmdldEJvdW5kcyh0cnVlKSk7XG4gICAgICBjb25zdCBpdyA9IHRoaXMuZGVzY3JpcHRvci53aWR0aCwgaWggPSB0aGlzLmRlc2NyaXB0b3IuaGVpZ2h0O1xuICAgICAgY29uc3QgeCA9IE1hdGgubWF4KDAsIE1hdGgubWluKGl3LCByLngpKTtcbiAgICAgIGNvbnN0IHkgPSBNYXRoLm1heCgwLCBNYXRoLm1pbihpaCwgci55KSk7XG4gICAgICBjb25zdCB3aWR0aCA9IE1hdGgubWF4KDEsIE1hdGgubWluKGl3IC0geCwgci53aWR0aCkpO1xuICAgICAgY29uc3QgaGVpZ2h0ID0gTWF0aC5tYXgoMSwgTWF0aC5taW4oaWggLSB5LCByLmhlaWdodCkpO1xuICAgICAgdGhpcy52aWV3cG9ydENoYW5nZSQubmV4dCh7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSk7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvKiB2aWV3cG9ydCBub3QgcmVhZHkgKi9cbiAgICB9XG4gIH1cblxuICAvKiogT1NEIHRhcmdldHMgdGhlIGltYWdlIGRpc3BsYXk7IHRoZSBzY2FsYXIvM0QgcGxvdCB0eXBlcyBiZWxvbmcgdG8gUGxvdGx5LiAqL1xuICBnZXRQbG90VHlwZURlc2NyaXB0b3JzKCk6IFBsb3RUeXBlRGVzY3JpcHRvcltdIHtcbiAgICByZXR1cm4gW1BMT1RfVFlQRV9ERVNDUklQVE9SU1tQbG90VHlwZS5IRUFUTUFQXSFdO1xuICB9XG5cbiAgZ2V0SW50ZW5zaXR5UHJvZmlsZSQoKTogT2JzZXJ2YWJsZTxJbnRlbnNpdHlQcm9maWxlW10+IHtcbiAgICByZXR1cm4gdGhpcy5pbnRlbnNpdHlQcm9maWxlJC5hc09ic2VydmFibGUoKTtcbiAgfVxuICAvKiogT1NEIG9ubHkgcmVuZGVycyB0aGUgaW1hZ2UgdHlwZTsgdGhlIExJTkUgaW50ZW5zaXR5IGluc2V0IGlzIFBsb3RseS1vbmx5LiAqL1xuICByZW5kZXJJbnRlbnNpdHlJbnNldChfZGl2SWQ6IHN0cmluZywgX3Byb2ZpbGVzOiBJbnRlbnNpdHlQcm9maWxlW10pOiB2b2lkIHtcbiAgICAvKiBubyBMSU5FIG1vZGUgb24gT1NEICovXG4gIH1cblxuICAvLyDilIDilIAgSVJlZ2lvblN0b3JlICsgY2xhc3NpZmljYXRpb24gY29sb3VycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgLy8gSW5oZXJpdGVkIGZyb20gQmFzZVN0b3JlVmlzdWFsaXplciDigJQgcHVyZSBkZWxlZ2F0aW9ucyB0byB0aGUgc2hhcmVkXG4gIC8vIFJlZ2lvblN0b3JlIC8gVmlzdWFsaXplclN0b3JlIChpZGVudGljYWwgdG8gbmFwYXJpLWpzKS4gVGhlIE9TRCByZWdpb25cbiAgLy8gb3ZlcmxheSAoY29uc3RydWN0ZWQgaW4gcGxvdCgpKSByZWFkcy93cml0ZXMgdGhhdCBzYW1lIHN0b3JlLCBzbyByZWdpb25zXG4gIC8vIHN0YXkgaW4gc3luYyB3aXRoIFBsb3RseSBhbmQgdGhlIFJlZ2lvbiBFZGl0b3I7IE9TRCByZW5kZXJzIHRoZW0gYXMgYW4gU1ZHXG4gIC8vIG92ZXJsYXkgcmF0aGVyIHRoYW4gY2FycnlpbmcgUGxvdGx5IHNoYXBlIGRpY3RzLlxuXG4gIC8vIOKUgOKUgCBJVG9vbENvbnRyb2xsZXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vIFdhbmQgKyB2ZXJ0ZXggZXJhc2VyIHJ1biBvdmVyIE9TRCB2aWEgSUNvb3JkaW5hdGVUcmFuc2Zvcm0uIFRoZXkgc2hhcmUgdGhlXG4gIC8vIHNpbmdsZXRvbiB0b29sIHNlcnZpY2VzIHdpdGggUGxvdGx5LCBzbyB3ZSByZS1iaW5kIHRvIG91ciBob3N0cyBvbiBhY3RpdmF0ZS5cbiAgc2V0V2FuZE1vZGUoYWN0aXZlOiBib29sZWFuLCBvcHRpb25zPzogYW55KTogdm9pZCB7XG4gICAgaWYgKCFhY3RpdmUpIHtcbiAgICAgIHRoaXMud2FuZFRvb2wuc2V0TW9kZShmYWxzZSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIFRha2UgdGhlIHBvaW50ZXIgZnJvbSBPU0Qgc28gdGhlIGRyYWcgZHJhd3MgaW5zdGVhZCBvZiBwYW5uaW5nLiAoTmF2IGlzXG4gICAgLy8gcmUtZW5hYmxlZCB3aGVuIHRoZSByZWdpb24gb3ZlcmxheSByZXR1cm5zIHRvICdub25lJyBvbiB0b29sIHN3aXRjaC4pXG4gICAgdGhpcy52aWV3ZXI/LnNldE1vdXNlTmF2RW5hYmxlZChmYWxzZSk7XG4gICAgLy8gVGhlIHdhbmQgc2FtcGxlcyB0aGUgcmVuZGVyZWQgdmlld3BvcnQg4oCUIHJlYWQgYmFjayBsYXppbHkgb24gZmlyc3QgdXNlLlxuICAgIHRoaXMudmlld3BvcnRQaXhlbHMgPSBudWxsO1xuICAgIHRoaXMud2FuZFRvb2wuYmluZEhvc3QodGhpcy53YW5kSG9zdCk7XG4gICAgdGhpcy53YW5kVG9vbC5zZXRNb2RlKHRydWUsIChvcHRpb25zID8/IHt9KSBhcyBXYW5kT3B0aW9ucyk7XG4gIH1cbiAgc2V0V2FuZE9wdGlvbnMob3B0aW9uczogYW55KTogdm9pZCB7XG4gICAgdGhpcy53YW5kVG9vbC5zZXRPcHRpb25zKG9wdGlvbnMgYXMgV2FuZE9wdGlvbnMpO1xuICB9XG4gIGNsZWFyQWN0aXZlV2FuZFJlZ2lvbigpOiB2b2lkIHtcbiAgICB0aGlzLndhbmRUb29sLmNsZWFyQWN0aXZlUmVnaW9uKCk7XG4gIH1cbiAgc2V0QnJ1c2hNb2RlKGFjdGl2ZTogYm9vbGVhbiwgb3B0aW9ucz86IGFueSk6IHZvaWQge1xuICAgIGlmICghYWN0aXZlKSB7XG4gICAgICB0aGlzLmJydXNoVG9vbC5zZXRNb2RlKGZhbHNlKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gVGFrZSB0aGUgcG9pbnRlciBmcm9tIE9TRCBzbyB0aGUgZHJhZyBwYWludHMgaW5zdGVhZCBvZiBwYW5uaW5nLlxuICAgIHRoaXMudmlld2VyPy5zZXRNb3VzZU5hdkVuYWJsZWQoZmFsc2UpO1xuICAgIC8vIFRoZSBicnVzaCB3b3JrcyBpbiB0aGUgcmVuZGVyZWQtdmlld3BvcnQgY29vcmRpbmF0ZSBmcmFtZSDigJQgcmVhZCBpdCBiYWNrXG4gICAgLy8gbGF6aWx5IG9uIGZpcnN0IHVzZSwgbGlrZSB0aGUgd2FuZC4gSXQgc2hhcmVzIHRoZSB3YW5kJ3MgaG9zdC5cbiAgICB0aGlzLnZpZXdwb3J0UGl4ZWxzID0gbnVsbDtcbiAgICB0aGlzLmJydXNoVG9vbC5iaW5kSG9zdCh0aGlzLndhbmRIb3N0KTtcbiAgICB0aGlzLmJydXNoVG9vbC5zZXRNb2RlKHRydWUsIChvcHRpb25zID8/IHt9KSBhcyBCcnVzaE9wdGlvbnMpO1xuICB9XG4gIHNldEJydXNoT3B0aW9ucyhvcHRpb25zOiBhbnkpOiB2b2lkIHtcbiAgICB0aGlzLmJydXNoVG9vbC5zZXRTaXplKChvcHRpb25zIGFzIEJydXNoT3B0aW9ucyk/LnNpemUgPz8gMCk7XG4gIH1cbiAgc2V0VmVydGV4RXJhc2VyTW9kZShhY3RpdmU6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICBpZiAoYWN0aXZlKSB7XG4gICAgICB0aGlzLnZpZXdlcj8uc2V0TW91c2VOYXZFbmFibGVkKGZhbHNlKTsgLy8gdG9vbCB0YWtlcyB0aGUgcG9pbnRlclxuICAgICAgdGhpcy52aWV3cG9ydFBpeGVscyA9IG51bGw7XG4gICAgICB0aGlzLmVyYXNlclRvb2wuYmluZEhvc3QodGhpcy5lcmFzZXJIb3N0KTtcbiAgICB9XG4gICAgdGhpcy5lcmFzZXJUb29sLnNldE1vZGUoYWN0aXZlKTtcbiAgfVxuICBzZXRWZXJ0ZXhFcmFzZXJSYWRpdXMocmFkaXVzOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLmVyYXNlclRvb2wuc2V0UmFkaXVzKHJhZGl1cyk7XG4gIH1cbiAgc2V0Wm9vbVRvQm94TW9kZShhY3RpdmU6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICBpZiAoYWN0aXZlKSB7XG4gICAgICB0aGlzLnZpZXdlcj8uc2V0TW91c2VOYXZFbmFibGVkKGZhbHNlKTsgLy8gdGhlIGJveCBkcmFnIG11c3Qgbm90IHBhblxuICAgICAgdGhpcy56b29tVG9Cb3hUb29sLmJpbmRIb3N0KHRoaXMuem9vbUJveEhvc3QoKSk7XG4gICAgfVxuICAgIHRoaXMuem9vbVRvQm94VG9vbC5zZXRNb2RlKGFjdGl2ZSk7XG4gICAgaWYgKCFhY3RpdmUpIHRoaXMudmlld2VyPy5zZXRNb3VzZU5hdkVuYWJsZWQodHJ1ZSk7XG4gIH1cbiAgLyoqIEJveC1wcm9tcHRlZCBTQU06IHNlZ21lbnQgdGhlIGRyYXduIHJlY3RhbmdsZXMuIFRoZSBTQU0gdG9vbCByZXVzZXMgdGhlXG4gICAqICB3YW5kIGhvc3QgKHZpZXdwb3J0IHJlYWRiYWNrICsgY29vcmRpbmF0ZSB0cmFuc2Zvcm0gKyByZWdpb24gc3RvcmUpLiAqL1xuICBzZWdtZW50UmVjdGFuZ2xlcygpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIHRoaXMudmlld3BvcnRQaXhlbHMgPSBudWxsOyAvLyBzZWdtZW50IGFnYWluc3QgdGhlIGN1cnJlbnQgdmlld3BvcnQgcmVhZGJhY2tcbiAgICB0aGlzLnNhbVRvb2wuYmluZEhvc3QodGhpcy53YW5kSG9zdCk7XG4gICAgcmV0dXJuIHRoaXMuc2FtVG9vbC5zZWdtZW50Qm94ZXMoKTtcbiAgfVxuICBzZWdtZW50UmVjdGFuZ2xlc0NlbGxwb3NlKCk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgaWYgKCF0aGlzLmNlbGxTZWdtZW50ZXIpIHJldHVybiBQcm9taXNlLnJlc29sdmUoMCk7XG4gICAgdGhpcy52aWV3cG9ydFBpeGVscyA9IG51bGw7IC8vIGNyb3AgYWdhaW5zdCB0aGUgY3VycmVudCB2aWV3cG9ydCByZWFkYmFja1xuICAgIHRoaXMuY2VsbFNlZ21lbnRUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIHJldHVybiB0aGlzLmNlbGxTZWdtZW50VG9vbC5zZWdtZW50Qm94ZXModGhpcy5jZWxsU2VnbWVudGVyKTtcbiAgfVxuICBzZXRTYW1Nb2RlbChpZDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Ub29sLnNldE1vZGVsKGlkKTtcbiAgICB0aGlzLnNhbVBvaW50VG9vbC5zZXRNb2RlbChpZCk7XG4gIH1cbiAgc2V0U2FtUG9pbnRNb2RlKGFjdGl2ZTogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmIChhY3RpdmUpIHtcbiAgICAgIHRoaXMudmlld2VyPy5zZXRNb3VzZU5hdkVuYWJsZWQoZmFsc2UpOyAvLyBjbGlja3MgYWRkIHBvaW50cywgZG9uJ3QgcGFuXG4gICAgICB0aGlzLnZpZXdwb3J0UGl4ZWxzID0gbnVsbDtcbiAgICAgIHRoaXMuc2FtUG9pbnRUb29sLmJpbmRIb3N0KHRoaXMud2FuZEhvc3QpO1xuICAgIH1cbiAgICB0aGlzLnNhbVBvaW50VG9vbC5zZXRNb2RlKGFjdGl2ZSk7XG4gIH1cbiAgY29tbWl0U2FtUG9pbnRzKCk6IHZvaWQgeyB0aGlzLnNhbVBvaW50VG9vbC5jb21taXQoKTsgfVxuICBjbGVhclNhbVBvaW50cygpOiB2b2lkIHsgdGhpcy5zYW1Qb2ludFRvb2wuY2xlYXIoKTsgfVxuXG4gIC8qKiBCdWlsZCB0aGUgd2FuZC9lcmFzZXIgaG9zdCBvYmplY3RzIGJvdW5kIHRvIHRoaXMgYmFja2VuZC4gQm90aCB0b29scyBydW5cbiAgICogIG92ZXIgT1NEIHZpYSB0aGUgc2hhcmVkIGNvb3JkaW5hdGUgdHJhbnNmb3JtICsgYSB2aWV3cG9ydCBwaXhlbCByZWFkYmFjayxcbiAgICogIGFuZCByZWFkL3dyaXRlIHJlZ2lvbnMgZGlyZWN0bHkgb24gdGhlIHNoYXJlZCBSZWdpb25TdG9yZSAobmV1dHJhbCBtb2RlbCkuICovXG4gIHByaXZhdGUgYnVpbGRUb29sSG9zdHMoKTogdm9pZCB7XG4gICAgdGhpcy53YW5kSG9zdCA9IHtcbiAgICAgIGdldFJlZ2lvbnM6ICgpID0+IHRoaXMucmVnaW9uU3RvcmUuZ2V0UmVnaW9ucygpLFxuICAgICAgc2V0UmVnaW9uczogKHJlZ2lvbnMpID0+IHRoaXMucmVnaW9uU3RvcmUuc2V0UmVnaW9ucyhyZWdpb25zKSxcbiAgICAgIGdldENhY2hlZEltYWdlRGF0YTogKCkgPT4gdGhpcy5yZWFkYmFja1ZpZXdwb3J0KCksXG4gICAgICBnZXRBY3RpdmVGcmFtZUluZGV4OiAoKSA9PiB0aGlzLmN1cnJlbnRaLFxuICAgICAgZ2V0T3ZlcmxheUNvbnRhaW5lcjogKCkgPT4gdGhpcy5nZXRPdmVybGF5Q29udGFpbmVyKCksXG4gICAgICBnZXRDb29yZGluYXRlVHJhbnNmb3JtOiAoKSA9PiB0aGlzLmNvb3JkVHJhbnNmb3JtIGFzIElDb29yZGluYXRlVHJhbnNmb3JtLFxuICAgICAgZ2V0RmlsZU5hbWU6ICgpID0+IHRoaXMuY3VycmVudEZpbGVOYW1lLFxuICAgICAgZ2V0U2hhcGVDb2xvcjogKCkgPT4gdGhpcy5yZWdpb25TdG9yZS5nZXRTaGFwZUNvbG9yKCksXG4gICAgfTtcbiAgICB0aGlzLmVyYXNlckhvc3QgPSB7XG4gICAgICBnZXRSZWdpb25zOiAoKSA9PiB0aGlzLnJlZ2lvblN0b3JlLmdldFJlZ2lvbnMoKSxcbiAgICAgIHNldFJlZ2lvbnM6IChyZWdpb25zKSA9PiB0aGlzLnJlZ2lvblN0b3JlLnNldFJlZ2lvbnMocmVnaW9ucyksXG4gICAgICBpbnZhbGlkYXRlV2FuZFJlZ2lvbjogKCkgPT4gdGhpcy53YW5kVG9vbC5jbGVhckFjdGl2ZVJlZ2lvbigpLFxuICAgICAgZ2V0T3ZlcmxheUNvbnRhaW5lcjogKCkgPT4gdGhpcy5nZXRPdmVybGF5Q29udGFpbmVyKCksXG4gICAgICBnZXRDb29yZGluYXRlVHJhbnNmb3JtOiAoKSA9PiB0aGlzLmNvb3JkVHJhbnNmb3JtIGFzIElDb29yZGluYXRlVHJhbnNmb3JtLFxuICAgICAgZ2V0Q2FjaGVkSW1hZ2VSYXRpbzogKCkgPT4gdGhpcy5yZWFkYmFja1ZpZXdwb3J0KCk/LnJhdGlvc1swXSA/PyAxLFxuICAgIH07XG4gIH1cblxuICAvKiogVGhlIGVsZW1lbnQgdGhlIG9uLWNhbnZhcyB0b29sIG92ZXJsYXlzIGF0dGFjaCB0byAoT1NEIGlzIG1vdW50ZWQgaGVyZSkuICovXG4gIHByaXZhdGUgZ2V0T3ZlcmxheUNvbnRhaW5lcigpOiBIVE1MRWxlbWVudCB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLnBsb3REaXYgPyBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0aGlzLnBsb3REaXYpIDogbnVsbDtcbiAgfVxuXG4gIC8qKiBTaXplIHRoZSBuYXZpZ2F0b3IgZnJvbSB0aGUgQ1VSUkVOVCBjb250YWluZXIgKG5hdmlnYXRvclNpemVSYXRpbyBvZiB0aGVcbiAgICogIHZpZXdlciBlbGVtZW50KSBhbmQga2VlcCBpdCBwaW5uZWQgdG8gdGhlIGNvcm5lci4gT1NEIGNvbXB1dGVzIHRoZSBzaXplXG4gICAqICBvbmNlIGluIHRoZSBWaWV3ZXIgY29uc3RydWN0b3Ig4oCUIGJlZm9yZSB0aGUgaG9zdCBmbGV4IGxheW91dCBzZXR0bGVzIOKAlFxuICAgKiAgYW5kIG5ldmVyIGNvcnJlY3RzIGl0LCBsZWF2aW5nIGEgc3RhbGUtc2l6ZSBlbGVtZW50IHdob3NlIHZpc2libGUgbWluaW1hcFxuICAgKiAgZmxvYXRzIGFib3ZlIHRoZSBib3R0b20tcmlnaHQgY29ybmVyLiAqL1xuICBwcml2YXRlIHJlc2l6ZU5hdmlnYXRvcigpOiB2b2lkIHtcbiAgICBjb25zdCB2OiBhbnkgPSB0aGlzLnZpZXdlcjtcbiAgICBjb25zdCBuYXYgPSB2Py5uYXZpZ2F0b3I7XG4gICAgY29uc3QgZWw6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkID0gdj8uZWxlbWVudDtcbiAgICBpZiAoIW5hdj8uZWxlbWVudCB8fCAhZWw/LmNsaWVudFdpZHRoIHx8ICFlbD8uY2xpZW50SGVpZ2h0KSByZXR1cm47XG4gICAgY29uc3QgdyA9IE1hdGgucm91bmQoZWwuY2xpZW50V2lkdGggKiAwLjE2KTtcbiAgICBjb25zdCBoID0gTWF0aC5yb3VuZChlbC5jbGllbnRIZWlnaHQgKiAwLjE2KTtcbiAgICBpZiAobmF2LmVsZW1lbnQuc3R5bGUud2lkdGggIT09IGAke3d9cHhgIHx8IG5hdi5lbGVtZW50LnN0eWxlLmhlaWdodCAhPT0gYCR7aH1weGApIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIG5hdi5zZXRXaWR0aCh3KTtcbiAgICAgICAgbmF2LnNldEhlaWdodChoKTtcbiAgICAgIH0gY2F0Y2ggeyAvKiBuYXZpZ2F0b3IgdG9ybiBkb3duICovIH1cbiAgICB9XG4gICAgLy8gTm9ybWFsaXplIE9TRCdzIGNvbnRyb2wtY29ybmVyIHN0YWNrIHNvIHRoZSBtaW5pbWFwIHNpdHMgZmx1c2ggaW4gdGhlXG4gICAgLy8gY29ybmVyLCBpbnNldCAxMnB4IHRvIGxpbmUgdXAgd2l0aCB0aGUgc2NhbGUgYmFyOlxuICAgIC8vICAtIHRoZSBpbmxpbmUtYmxvY2sgd3JhcHBlciBjYXJyaWVzIGxpbmUtYm94IHN0cnV0cyBhbmQgY2FuIHJldGFpbiBzdGFsZVxuICAgIC8vICAgIHNpemluZyDihpIgbWFrZSBpdCBhIHRpZ2h0IGJsb2NrO1xuICAgIC8vICAtIGFueXRoaW5nIGVsc2UgT1NEIGxlZnQgaW4gdGhlIGNvcm5lciB3b3VsZCBzdGFjayBiZWxvdyB0aGUgbmF2aWdhdG9yXG4gICAgLy8gICAgYW5kIGZsb2F0IGl0IHVwIOKGkiBoaWRlIGl0O1xuICAgIC8vICAtIGluc2V0IHRoZSBjb3JuZXIgaXRzZWxmIChib3R0b20vcmlnaHQgMTJweCksIGtlZXBpbmcgdGhlIGVsZW1lbnQgaW5cbiAgICAvLyAgICBub3JtYWwgZmxvdy5cbiAgICBjb25zdCB3cmFwcGVyID0gbmF2LmVsZW1lbnQucGFyZW50RWxlbWVudCBhcyBIVE1MRWxlbWVudCB8IG51bGw7XG4gICAgY29uc3QgY29ybmVyID0gd3JhcHBlcj8ucGFyZW50RWxlbWVudCBhcyBIVE1MRWxlbWVudCB8IG51bGw7XG4gICAgaWYgKHdyYXBwZXIgJiYgY29ybmVyKSB7XG4gICAgICB3cmFwcGVyLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xuICAgICAgd3JhcHBlci5zdHlsZS5saW5lSGVpZ2h0ID0gJzAnO1xuICAgICAgd3JhcHBlci5zdHlsZS5mb250U2l6ZSA9ICcwJztcbiAgICAgIC8vIFRoZSB3cmFwcGVyIGtlZXBzIHRoZSBuYXZpZ2F0b3IncyBzdGFsZSBwcmUtc2V0dGxlIGhlaWdodCBhcyBhblxuICAgICAgLy8gZXhwbGljaXQgc2l6ZSAodGhlIG5hdiB3YXMgMC4xNnggdGhlIFVOU0VUVExFRCBjb250YWluZXIgYXQgY3JlYXRpb24pLFxuICAgICAgLy8gbGVhdmluZyBhbiBlbXB0eSBiYW5kIHVuZGVyIHRoZSByZXNpemVkIG1pbmltYXAg4oCUIG1lYXN1cmVkIGxpdmU6XG4gICAgICAvLyBuYXZIPTg3IGluc2lkZSB3cmFwSD0xNjcuIEZvcmNlIGl0IHRvIGh1ZyBpdHMgY29udGVudC5cbiAgICAgIHdyYXBwZXIuc3R5bGUuaGVpZ2h0ID0gJ2F1dG8nO1xuICAgICAgd3JhcHBlci5zdHlsZS53aWR0aCA9ICdhdXRvJztcbiAgICAgIGZvciAoY29uc3QgY2hpbGQgb2YgQXJyYXkuZnJvbShjb3JuZXIuY2hpbGRyZW4pIGFzIEhUTUxFbGVtZW50W10pIHtcbiAgICAgICAgaWYgKGNoaWxkICE9PSB3cmFwcGVyKSBjaGlsZC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgfVxuICAgICAgZm9yIChjb25zdCBjaGlsZCBvZiBBcnJheS5mcm9tKHdyYXBwZXIuY2hpbGRyZW4pIGFzIEhUTUxFbGVtZW50W10pIHtcbiAgICAgICAgaWYgKGNoaWxkICE9PSBuYXYuZWxlbWVudCkgY2hpbGQuc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgICAgIH1cbiAgICAgIGNvcm5lci5zdHlsZS5ib3R0b20gPSAnMTJweCc7XG4gICAgICBjb3JuZXIuc3R5bGUucmlnaHQgPSAnMTJweCc7XG4gICAgfVxuICAgIE9iamVjdC5hc3NpZ24obmF2LmVsZW1lbnQuc3R5bGUsIHsgcG9zaXRpb246ICdyZWxhdGl2ZScsIHRvcDogJycsIGxlZnQ6ICcnLCBib3R0b206ICcnLCByaWdodDogJycsIG1hcmdpbjogJzAnIH0pO1xuICAgIC8vIFRPRE8oZGVidWcpOiByZW1vdmUgYWZ0ZXIgdmVyaWZpY2F0aW9uXG4gICAgY29uc3QgbmVyID0gbmF2LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgY29uc3Qgd3IgPSB3cmFwcGVyPy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICBjb25zdCBjciA9IGNvcm5lcj8uZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgY29uc3QgcGVyID0gZWwuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgY29uc29sZS53YXJuKGBbdml6Om5hdl0gbmF2Qm90dG9tPSR7TWF0aC5yb3VuZChuZXIuYm90dG9tKX0gd3JhcEJvdHRvbT0ke01hdGgucm91bmQod3I/LmJvdHRvbSA/PyAtMSl9IGAgK1xuICAgICAgYGNvcm5lckJvdHRvbT0ke01hdGgucm91bmQoY3I/LmJvdHRvbSA/PyAtMSl9IHBsb3RCb3R0b209JHtNYXRoLnJvdW5kKHBlci5ib3R0b20pfSBgICtcbiAgICAgIGBuYXZIPSR7TWF0aC5yb3VuZChuZXIuaGVpZ2h0KX0gd3JhcEg9JHtNYXRoLnJvdW5kKHdyPy5oZWlnaHQgPz8gLTEpfSBjb3JuZXJIPSR7TWF0aC5yb3VuZChjcj8uaGVpZ2h0ID8/IC0xKX1gKTtcbiAgfVxuXG4gIC8qKiBGb3JjZSB0aGUgZG9ja2VkIHRvb2xiYXIgdG8gcmVwYWludCBhZnRlciBhbiBPU0Qgem9vbS4gQ2hyb21lIGxlYXZlcyBpdFxuICAgKiAgbGFpZC1vdXQtYnV0LXVucGFpbnRlZCAodGhlIGNhbnZhcydzIGNvbXBvc2l0aW5nIGNodXJuIHN0cmFuZHMgdGhlIHRvb2xiYXInc1xuICAgKiAgcmFzdGVyKS4gQSBzeW5jaHJvbm91cyBkaXNwbGF5IHRvZ2dsZSByZS1yYXN0ZXJzIGl0IHdpdGggbm8gdmlzaWJsZSBnb25lLWZyYW1lXG4gICAqICBhbmQgbm8gbGF5b3V0IHNoaWZ0LiBMb2NhdGVkIHZpYSB0aGUgRE9NIHNpbmNlIHRoaXMgc2VydmljZSBkb2Vzbid0IG93biB0aGVcbiAgICogIHRvb2xiYXI7IGEgbm8tb3Agd2hlbiB0aGVyZSdzIG5vIHRvb2xiYXIgKGUuZy4gZW1iZWRkZWQgd2l0aG91dCBvbmUpLiAqL1xuICBwcml2YXRlIG51ZGdlVG9vbGJhclJlcGFpbnQoKTogdm9pZCB7XG4gICAgY29uc3QgcGxvdEVsID0gdGhpcy5wbG90RGl2ID8gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2KSA6IG51bGw7XG4gICAgY29uc3QgZG9jayA9IHBsb3RFbD8uY2xvc2VzdCgndmlzdWFsaXphdGlvbicpPy5xdWVyeVNlbGVjdG9yPEhUTUxFbGVtZW50PignLnRvb2xiYXItZG9jaycpO1xuICAgIGlmICghZG9jaykgcmV0dXJuO1xuICAgIGNvbnN0IHByZXYgPSBkb2NrLnN0eWxlLmRpc3BsYXk7XG4gICAgZG9jay5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgIHZvaWQgZG9jay5vZmZzZXRIZWlnaHQ7IC8vIHJlZmxvdyBzbyB0aGUgdG9nZ2xlIHJlLXJhc3RlcnMgb24gdGhlIG5leHQgcGFpbnRcbiAgICBkb2NrLnN0eWxlLmRpc3BsYXkgPSBwcmV2O1xuICB9XG5cbiAgLyoqIEhvc3QgZm9yIHRoZSAoc2hhcmVkKSB6b29tLXRvLWJveCB0b29sOiBjb252ZXJ0IG92ZXJsYXkgcGl4ZWxzIHRvIGltYWdlXG4gICAqICBjb29yZHMgdmlhIHRoZSB2aWV3cG9ydCwgYW5kIGZpdCB0aGUgdmlld3BvcnQgdG8gdGhlIGNob3NlbiByZWN0YW5nbGUuICovXG4gIHByaXZhdGUgem9vbUJveEhvc3QoKTogWm9vbVRvQm94VG9vbEhvc3Qge1xuICAgIHJldHVybiB7XG4gICAgICBnZXRQbG90RGl2OiAoKSA9PiB0aGlzLnBsb3REaXYsXG4gICAgICBwaXhlbFRvRGF0YTogKHB4OiBudW1iZXIsIHB5OiBudW1iZXIpID0+IGVsZW1lbnRUb0ltYWdlKHRoaXMudmlld2VyLCBweCwgcHkpLFxuICAgICAgYXBwbHlab29tVG9Cb3g6IChjb29yZHM6IG51bWJlcltdKSA9PiB0aGlzLmFwcGx5Wm9vbVRvQm94KGNvb3JkcyksXG4gICAgfTtcbiAgfVxuXG4gIC8qKiBGaXQgdGhlIE9TRCB2aWV3cG9ydCB0byBhbiBpbWFnZS1zcGFjZSByZWN0YW5nbGUgKGNvb3JkcyBvcmRlcmVkXG4gICAqICBbeE1pbiwgeE1heCwgeU1heCwgeU1pbl0pLiAqL1xuICBwcml2YXRlIGFwcGx5Wm9vbVRvQm94KGNvb3JkaW5hdGVzOiBudW1iZXJbXSk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3ZXIgfHwgY29vcmRpbmF0ZXMubGVuZ3RoIDwgNCkgcmV0dXJuO1xuICAgIGNvbnN0IFthLCBiLCBjLCBkXSA9IGNvb3JkaW5hdGVzO1xuICAgIGNvbnN0IHggPSBNYXRoLm1pbihhLCBiKTtcbiAgICBjb25zdCB3ID0gTWF0aC5hYnMoYiAtIGEpO1xuICAgIGNvbnN0IHkgPSBNYXRoLm1pbihjLCBkKTtcbiAgICBjb25zdCBoID0gTWF0aC5hYnMoYyAtIGQpO1xuICAgIGlmICh3IDw9IDAgfHwgaCA8PSAwKSByZXR1cm47XG4gICAgY29uc3QgcmVjdCA9IGltYWdlUmVjdFRvVmlld3BvcnQodGhpcy52aWV3ZXIsIHgsIHksIHcsIGgpO1xuICAgIHRoaXMudmlld2VyLnZpZXdwb3J0LmZpdEJvdW5kcyhyZWN0LCBmYWxzZSk7XG4gIH1cblxuICAvKipcbiAgICogUmVhZCBiYWNrIHRoZSAqY3VycmVudGx5IHJlbmRlcmVkKiBPU0QgY2FudmFzIGFzIHRoZSB3YW5kJ3MgcGl4ZWwgbWF0cml4LlxuICAgKiBUaGUgbWF0cml4IGNvdmVycyBvbmx5IHRoZSB2aXNpYmxlIHZpZXdwb3J0IGF0IHNjcmVlbiByZXNvbHV0aW9uLCBzbyB3aGVuXG4gICAqIHRoZSB1c2VyIGlzIHpvb21lZCBpbnRvIGEgc3ViLXJlZ2lvbiB0aGUgd2FuZCBzYW1wbGVzIHRoYXQgcmVnaW9uJ3MgZGV0YWlsXG4gICAqIChyYXRoZXIgdGhhbiB0aGUgd2hvbGUgaW1hZ2UgYXQgcHJldmlldyByZXNvbHV0aW9uKS4gYG9yaWdpblgvb3JpZ2luWWAgYW5kXG4gICAqIGByYXRpb3NgIG1hcCBpbWFnZSBjb29yZHMgPC0+IHJlYWRiYWNrLXBpeGVsIGNvb3JkcyBmb3IgdGhlIHdhbmQnc1xuICAgKiBkYXRhL3JhdGlvL29yaWdpbiBtb2RlbC4gQ2FjaGVkIHVudGlsIHRoZSB2aWV3cG9ydCBjaGFuZ2VzLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkYmFja1ZpZXdwb3J0KCk6IENhY2hlZEltYWdlRGF0YSB8IG51bGwge1xuICAgIGlmICh0aGlzLnZpZXdwb3J0UGl4ZWxzKSByZXR1cm4gdGhpcy52aWV3cG9ydFBpeGVscztcbiAgICBjb25zdCB2aWV3ZXIgPSB0aGlzLnZpZXdlciBhcyBhbnk7XG4gICAgY29uc3QgY2FudmFzOiBIVE1MQ2FudmFzRWxlbWVudCB8IHVuZGVmaW5lZCA9IHZpZXdlcj8uZHJhd2VyPy5jYW52YXM7XG4gICAgY29uc3QgdnAgPSB2aWV3ZXI/LnZpZXdwb3J0O1xuICAgIGlmICghY2FudmFzIHx8ICFjYW52YXMud2lkdGggfHwgIWNhbnZhcy5oZWlnaHQgfHwgIXZwKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCBjdHggPSBjYW52YXMuZ2V0Q29udGV4dCgnMmQnLCB7IHdpbGxSZWFkRnJlcXVlbnRseTogdHJ1ZSB9KTtcbiAgICBpZiAoIWN0eCkgcmV0dXJuIG51bGw7XG5cbiAgICBjb25zdCB3ID0gY2FudmFzLndpZHRoOyAvLyBkZXZpY2UgcGl4ZWxzXG4gICAgY29uc3QgaCA9IGNhbnZhcy5oZWlnaHQ7XG4gICAgY29uc3QgZGF0YSA9IGN0eC5nZXRJbWFnZURhdGEoMCwgMCwgdywgaCkuZGF0YTsgLy8gUkdCQSwgcm93LW1ham9yXG4gICAgLy8gQnVpbGQgdGhlIFt5XVt4XSA9IFtyLGcsYl0gbWF0cml4IHRoZSB3YW5kIGV4cGVjdHMgZm9yIFJHQiBmcmFtZXMuXG4gICAgY29uc3QgbWF0cml4OiBudW1iZXJbXVtdW10gPSBuZXcgQXJyYXkoaCk7XG4gICAgZm9yIChsZXQgeSA9IDA7IHkgPCBoOyB5KyspIHtcbiAgICAgIGNvbnN0IHJvdzogbnVtYmVyW11bXSA9IG5ldyBBcnJheSh3KTtcbiAgICAgIGNvbnN0IGJhc2UgPSB5ICogdyAqIDQ7XG4gICAgICBmb3IgKGxldCB4ID0gMDsgeCA8IHc7IHgrKykge1xuICAgICAgICBjb25zdCBvID0gYmFzZSArIHggKiA0O1xuICAgICAgICByb3dbeF0gPSBbZGF0YVtvXSwgZGF0YVtvICsgMV0sIGRhdGFbbyArIDJdXTtcbiAgICAgIH1cbiAgICAgIG1hdHJpeFt5XSA9IHJvdztcbiAgICB9XG5cbiAgICAvLyBJbWFnZS1jb29yZCBzcGFuIHRoZSByZWFkYmFjayBjb3ZlcnMgKENTUyBweCBpbiwgaW1hZ2UgY29vcmRzIG91dCkuIFJvdXRlXG4gICAgLy8gdGhyb3VnaCB3b3JsZCBpdGVtIDAgKG9zZC1jb29yZHMpIHNvIGl0IHN0YXlzIGFjY3VyYXRlIOKAlCBhbmQgcXVpZXQg4oCUIHdoZW5cbiAgICAvLyB0aGUgd29ybGQgaG9sZHMgbXVsdGlwbGUgaW1hZ2VzIChwZXItY2hhbm5lbCBtdWx0aWNoYW5uZWwgbGF5ZXJzKS5cbiAgICBjb25zdCBlbFcgPSBjYW52YXMuY2xpZW50V2lkdGggfHwgdztcbiAgICBjb25zdCBlbEggPSBjYW52YXMuY2xpZW50SGVpZ2h0IHx8IGg7XG4gICAgY29uc3QgdGwgPSBlbGVtZW50VG9JbWFnZSh0aGlzLnZpZXdlciwgMCwgMCk7XG4gICAgY29uc3QgYnIgPSBlbGVtZW50VG9JbWFnZSh0aGlzLnZpZXdlciwgZWxXLCBlbEgpO1xuICAgIGNvbnN0IHJhdGlvWCA9IChici54IC0gdGwueCkgLyB3OyAvLyBpbWFnZSBweCBwZXIgcmVhZGJhY2sgcHhcbiAgICBjb25zdCByYXRpb1kgPSAoYnIueSAtIHRsLnkpIC8gaDtcblxuICAgIHRoaXMudmlld3BvcnRQaXhlbHMgPSB7XG4gICAgICBmcmFtZXM6IFttYXRyaXhdLFxuICAgICAgd2lkdGg6IHcsXG4gICAgICBoZWlnaHQ6IGgsXG4gICAgICByYXRpb3M6IFtyYXRpb1gsIHJhdGlvWV0sXG4gICAgICBpc0dyYXlzY2FsZTogZmFsc2UsIC8vIGNhbnZhcyByZWFkYmFjayBpcyBhbHdheXMgUkdCQVxuICAgICAgb3JpZ2luWDogdGwueCxcbiAgICAgIG9yaWdpblk6IHRsLnksXG4gICAgfTtcbiAgICByZXR1cm4gdGhpcy52aWV3cG9ydFBpeGVscztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvbG9yIGEgZ3JheXNjYWxlIHRpbGUgdGhyb3VnaCB0aGUgYWN0aXZlIGNvbG9ybWFwIExVVCwgdXNpbmcgT1NEJ3NcbiAgICogdGlsZS1pbnZhbGlkYXRlZCBwaXhlbCBwaXBlbGluZSAoT1NEIDUrLzYpLiBNYXBzIGVhY2ggcGl4ZWwncyBncmF5c2NhbGVcbiAgICogdmFsdWUgKHI9PWc9PWIpIHRvIHRoZSBMVVQncyBSR0IuIE5vLW9wIGZvciBSR0IgaW1hZ2VzIG9yIHVudGlsIHRoZSBMVVQgaXNcbiAgICogYnVpbHQuIEhhbmRsZXIgaXMgYXN5bmMg4oCUIE9TRCBhd2FpdHMgaXQgKHJhaXNlRXZlbnRBd2FpdGluZykuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHJlY29sb3JUaWxlKGV2ZW50OiBhbnkpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5pc011bHRpQ2hhbm5lbCkge1xuICAgICAgYXdhaXQgdGhpcy5yZWNvbG9yQ2hhbm5lbFRpbGUoZXZlbnQpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBncmF5ID0gdGhpcy5pc0dyYXlzY2FsZUltYWdlO1xuICAgIGlmIChncmF5KSB7XG4gICAgICBpZiAoIXRoaXMuY29sb3JMdXQpIHJldHVybjtcbiAgICB9IGVsc2UgaWYgKCF0aGlzLmRpc3BsYXkucmdiTmVlZHNSZWNvbG9yKCkpIHtcbiAgICAgIHJldHVybjsgLy8gUkdCIGF0IGRlZmF1bHQgKGFsbCB2aXNpYmxlLCBmdWxsIHdpbmRvdywgzrM9MSwgbm8gaW52ZXJ0KSDihpIgcGFzc3Rocm91Z2hcbiAgICB9XG5cbiAgICAvLyBSZWFkIHRoZSB0aWxlJ3MgcGl4ZWxzLiBQcmVmZXIgdGhlIHJlbmRlcmluZyBjb250ZXh0LCBidXQgc29tZSB0aWxlIGNhY2hlc1xuICAgIC8vIChhamF4IFBORyBibG9icykgY29udmVydCB0byBhbiAqZW1wdHkqIGNvbnRleHQyZCDigJQgcmVjb2xvcmluZyB0aGF0IHdvdWxkXG4gICAgLy8gYmxhbmsgdGhlIHRpbGUgKHdoaXRlIGNhbnZhcykuIFNvIGlmIHRoZSBjb250ZXh0IGhhcyBubyBvcGFxdWUgcGl4ZWxzLCBkcmF3XG4gICAgLy8gdGhlIHRpbGUncyBvd24gYml0bWFwL2ltYWdlIGFuZCByZWNvbG9yIHRoYXQgaW5zdGVhZC4gV2UgbmV2ZXIgd3JpdGUgYmFja1xuICAgIC8vIHVubGVzcyB3ZSBhY3R1YWxseSByZWNvbG9yZWQgc29tZXRoaW5nLCBzbyBhIHRpbGUgaXMgbmV2ZXIgYmxhbmtlZC5cbiAgICBsZXQgY3R4OiBDYW52YXNSZW5kZXJpbmdDb250ZXh0MkQgfCBudWxsID0gbnVsbDtcbiAgICB0cnkge1xuICAgICAgY3R4ID0gYXdhaXQgZXZlbnQuZ2V0RGF0YSgnY29udGV4dDJkJyk7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvKiB0cnkgYml0bWFwIGJlbG93ICovXG4gICAgfVxuICAgIGxldCBpbWc6IEltYWdlRGF0YSB8IG51bGwgPSBudWxsO1xuICAgIGlmIChjdHggJiYgY3R4LmNhbnZhcyAmJiBjdHguY2FudmFzLndpZHRoICYmIGN0eC5jYW52YXMuaGVpZ2h0KSB7XG4gICAgICB0cnkge1xuICAgICAgICBpbWcgPSBjdHguZ2V0SW1hZ2VEYXRhKDAsIDAsIGN0eC5jYW52YXMud2lkdGgsIGN0eC5jYW52YXMuaGVpZ2h0KTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICBpbWcgPSBudWxsO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoIWltZyB8fCAhdGhpcy5oYXNPcGFxdWUoaW1nLmRhdGEpKSB7XG4gICAgICAvLyBGYWxsIGJhY2sgdG8gdGhlIHRpbGUncyBkZWNvZGVkIGltYWdlL2JpdG1hcC5cbiAgICAgIGxldCBzcmM6IGFueSA9IG51bGw7XG4gICAgICB0cnkge1xuICAgICAgICBzcmMgPSBhd2FpdCBldmVudC5nZXREYXRhKCdpbWFnZUJpdG1hcCcpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8qIG5vbmUgKi9cbiAgICAgIH1cbiAgICAgIGlmICghc3JjIHx8ICFzcmMud2lkdGgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBzcmMgPSBhd2FpdCBldmVudC5nZXREYXRhKCdpbWFnZScpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAvKiBub25lICovXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICghc3JjIHx8ICFzcmMud2lkdGgpIHJldHVybjsgLy8gY2FuJ3QgZ2V0IHBpeGVscyDigJQgbGVhdmUgdGhlIHRpbGUgdW50b3VjaGVkXG4gICAgICBjb25zdCBjID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gICAgICBjLndpZHRoID0gc3JjLndpZHRoO1xuICAgICAgYy5oZWlnaHQgPSBzcmMuaGVpZ2h0O1xuICAgICAgY3R4ID0gYy5nZXRDb250ZXh0KCcyZCcsIHsgd2lsbFJlYWRGcmVxdWVudGx5OiB0cnVlIH0pO1xuICAgICAgaWYgKCFjdHgpIHJldHVybjtcbiAgICAgIGN0eC5kcmF3SW1hZ2Uoc3JjLCAwLCAwKTtcbiAgICAgIGltZyA9IGN0eC5nZXRJbWFnZURhdGEoMCwgMCwgYy53aWR0aCwgYy5oZWlnaHQpO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5kaXNwbGF5LmFwcGx5VG9SZ2JhKGltZy5kYXRhKSB8fCAhY3R4KSByZXR1cm47IC8vIG5vdGhpbmcgb3BhcXVlXG4gICAgY3R4LnB1dEltYWdlRGF0YShpbWcsIDAsIDApO1xuICAgIC8vIFRoZSB0aWxlJ3MgY2FjaGUgY2FuIGJlIGV2aWN0ZWQgYmV0d2VlbiB0aGUgYXdhaXRzIGFib3ZlIGFuZCBoZXJlIChzbGljZVxuICAgIC8vIGNoYW5nZSAvIGludmFsaWRhdGlvbiksIG1ha2luZyBzZXREYXRhIHRocm93IGEgRE9NRXhjZXB0aW9uIG9uIGEgZGVhZFxuICAgIC8vIGNhbnZhcy4gU3dhbGxvdyBpdCDigJQgdGhlIHRpbGUgaXMgZ29uZSwgc28gdGhlcmUncyBub3RoaW5nIHRvIHJlY29sb3IuXG4gICAgdHJ5IHsgYXdhaXQgZXZlbnQuc2V0RGF0YShjdHgsICdjb250ZXh0MmQnKTsgfSBjYXRjaCB7IC8qIHRpbGUgZXZpY3RlZCAqLyB9XG4gIH1cblxuICAvKipcbiAgICogVGludCBhIHNpbmdsZSBjaGFubmVsJ3MgdGlsZSBpbiBwbGFjZS4gRWFjaCBjaGFubmVsIGlzIGl0cyBvd24gT3BlblNlYWRyYWdvblxuICAgKiBUaWxlZEltYWdlIChhIHNpbmdsZS1iYW5kIGdyYXlzY2FsZSB0aWxlKTsgT1NEIGNvbXBvc2l0ZXMgdGhlIE4gaW1hZ2VzXG4gICAqIGFkZGl0aXZlbHkgKCdsaWdodGVyJykgaW4gaXRzIGRyYXdlciDigJQgc28gdGhpcyBqdXN0IG1hcHMgdGhlIHRpbGUncyBpbnRlbnNpdHlcbiAgICogdGhyb3VnaCB0aGUgY2hhbm5lbCdzIHdpbmRvdy9nYW1tYSBhbmQgaXRzIHBzZXVkby1jb2xvdXIuIFN5bmNocm9ub3VzIChub1xuICAgKiBjcm9zcy10aWxlIGZldGNoKSDihpIgbm8gcmFjZSwgbm8gc2VhbXMuIENoYW5uZWwgaXMgcGFyc2VkIGZyb20gdGhlIHRpbGUgVVJMLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByZWNvbG9yQ2hhbm5lbFRpbGUoZXZlbnQ6IGFueSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHRpbGUgPSBldmVudD8udGlsZTtcbiAgICBjb25zdCB1cmw6IHN0cmluZyA9ICh0aWxlICYmICh0eXBlb2YgdGlsZS5nZXRVcmwgPT09ICdmdW5jdGlvbicgPyB0aWxlLmdldFVybCgpIDogdGlsZS51cmwpKSB8fCAnJztcbiAgICBjb25zdCBtID0gL1s/Jl1jaGFubmVsPShcXGQrKS8uZXhlYyh1cmwpO1xuICAgIGNvbnN0IGNoID0gbSA/IHBhcnNlSW50KG1bMV0sIDEwKSA6IDA7XG4gICAgY29uc3Qgc3QgPSB0aGlzLmNoYW5uZWxTdGF0ZXNbY2hdO1xuXG4gICAgbGV0IGN0eDogQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEIHwgbnVsbCA9IG51bGw7XG4gICAgdHJ5IHsgY3R4ID0gYXdhaXQgZXZlbnQuZ2V0RGF0YSgnY29udGV4dDJkJyk7IH0gY2F0Y2ggeyAvKiBmYWxsYmFjayBiZWxvdyAqLyB9XG4gICAgbGV0IGltZzogSW1hZ2VEYXRhIHwgbnVsbCA9IG51bGw7XG4gICAgaWYgKGN0eCAmJiBjdHguY2FudmFzICYmIGN0eC5jYW52YXMud2lkdGggJiYgY3R4LmNhbnZhcy5oZWlnaHQpIHtcbiAgICAgIHRyeSB7IGltZyA9IGN0eC5nZXRJbWFnZURhdGEoMCwgMCwgY3R4LmNhbnZhcy53aWR0aCwgY3R4LmNhbnZhcy5oZWlnaHQpOyB9IGNhdGNoIHsgaW1nID0gbnVsbDsgfVxuICAgIH1cbiAgICBpZiAoIWltZyB8fCAhdGhpcy5oYXNPcGFxdWUoaW1nLmRhdGEpKSB7XG4gICAgICBsZXQgc3JjOiBhbnkgPSBudWxsO1xuICAgICAgdHJ5IHsgc3JjID0gYXdhaXQgZXZlbnQuZ2V0RGF0YSgnaW1hZ2VCaXRtYXAnKTsgfSBjYXRjaCB7IC8qIG5vbmUgKi8gfVxuICAgICAgaWYgKCFzcmMgfHwgIXNyYy53aWR0aCkgeyB0cnkgeyBzcmMgPSBhd2FpdCBldmVudC5nZXREYXRhKCdpbWFnZScpOyB9IGNhdGNoIHsgLyogbm9uZSAqLyB9IH1cbiAgICAgIGlmICghc3JjIHx8ICFzcmMud2lkdGgpIHJldHVybjtcbiAgICAgIGNvbnN0IGMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICAgIGMud2lkdGggPSBzcmMud2lkdGg7IGMuaGVpZ2h0ID0gc3JjLmhlaWdodDtcbiAgICAgIGN0eCA9IGMuZ2V0Q29udGV4dCgnMmQnLCB7IHdpbGxSZWFkRnJlcXVlbnRseTogdHJ1ZSB9KTtcbiAgICAgIGlmICghY3R4KSByZXR1cm47XG4gICAgICBjdHguZHJhd0ltYWdlKHNyYywgMCwgMCk7XG4gICAgICBpbWcgPSBjdHguZ2V0SW1hZ2VEYXRhKDAsIDAsIGMud2lkdGgsIGMuaGVpZ2h0KTtcbiAgICB9XG4gICAgaWYgKCFjdHgpIHJldHVybjtcblxuICAgIC8vIFByZWNvbXB1dGUgbHVtKDAuLjI1NSkg4oaSIHRpbnRlZCBSR0Igb25jZSwgdGhlbiBtYXAgZWFjaCBwaXhlbCBieSBsb29rdXAg4oCUXG4gICAgLy8gdGhlIGNoYW5uZWwgdGlsZSBpcyBzaW5nbGUtYmFuZCwgc28gdGhpcyB0dXJucyB+MjYyayBwZXItcGl4ZWxcbiAgICAvLyBjaGFubmVsSW50ZW5zaXR5KCkgKHdpdGggTWF0aC5wb3cgZm9yIGdhbW1hKSBjYWxscyBpbnRvIDI1NiDigJQgdGhlIGRpZmZlcmVuY2VcbiAgICAvLyBiZXR3ZWVuIGEgc25hcHB5IGFuZCBhIHNsdWdnaXNoIHNsaWRlciBvbiBhIDQtY2hhbm5lbCBzdGFjay5cbiAgICBjb25zdCB7IHI6IHJMLCBnOiBnTCwgYjogYkwgfSA9IHRoaXMuZGlzcGxheS5jaGFubmVsUmdiTHV0KHN0KTtcbiAgICBjb25zdCBkID0gaW1nLmRhdGE7XG4gICAgbGV0IGNoYW5nZWQgPSBmYWxzZTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGQubGVuZ3RoOyBpICs9IDQpIHtcbiAgICAgIGlmIChkW2kgKyAzXSA9PT0gMCkgY29udGludWU7XG4gICAgICBjb25zdCByID0gZFtpXSwgZyA9IGRbaSArIDFdLCBiID0gZFtpICsgMl07XG4gICAgICBjb25zdCBsdW0gPSByID49IGcgPyAociA+PSBiID8gciA6IGIpIDogZyA+PSBiID8gZyA6IGI7IC8vIHNpbmdsZS1iYW5kIOKGkiBtYXhcbiAgICAgIGRbaV0gPSByTFtsdW1dO1xuICAgICAgZFtpICsgMV0gPSBnTFtsdW1dO1xuICAgICAgZFtpICsgMl0gPSBiTFtsdW1dO1xuICAgICAgY2hhbmdlZCA9IHRydWU7XG4gICAgfVxuICAgIGlmICghY2hhbmdlZCkgcmV0dXJuO1xuICAgIGN0eC5wdXRJbWFnZURhdGEoaW1nLCAwLCAwKTtcbiAgICAvLyBTZWUgcmVjb2xvclRpbGU6IHRoZSB0aWxlIGNhY2hlIGNhbiBkaWUgYmV0d2VlbiB0aGUgYXdhaXRzIGFuZCBoZXJlLlxuICAgIHRyeSB7IGF3YWl0IGV2ZW50LnNldERhdGEoY3R4LCAnY29udGV4dDJkJyk7IH0gY2F0Y2ggeyAvKiB0aWxlIGV2aWN0ZWQgKi8gfVxuICB9XG5cbiAgLyoqIFBlci1jaGFubmVsIGhpc3RvZ3JhbSBmb3IgdGhlIENoYW5uZWxzICYgSGlzdG9ncmFtIHBhbmUsIGZyb20gdGhlIGN1cnJlbnRcbiAgICogIHNsaWNlJ3Mgc2FtcGxlZCB0aWxlcyAoZ3JheXNjYWxlIOKGkiBjaGFubmVsIDA7IFJHQiDihpIgUi9HL0IpLiBOdWxsIHVudGlsIHRoZVxuICAgKiAgYXN5bmMgc2FtcGxpbmcgcmVzb2x2ZXMgb3IgaWYgaXQgd2FzIHNraXBwZWQuICovXG4gIGdldEhpc3RvZ3JhbShjaGFubmVsSW5kZXg6IG51bWJlciwgX2JpbnM6IG51bWJlcik6IElIaXN0b2dyYW0gfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5zYW1wbGVyLmdldCh0aGlzLmN1cnJlbnRaLCBjaGFubmVsSW5kZXgpO1xuICB9XG5cbiAgLyoqIE5hdGl2ZSBiaXQgZGVwdGggb2YgYSBjaGFubmVsIGZyb20gdGhlIHRpbGUgZGVzY3JpcHRvciAoOCB3aGVuIHVua25vd24pLiAqL1xuICBwcml2YXRlIGNoYW5uZWxCaXREZXB0aChjaGFubmVsSW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuZGVzY3JpcHRvcj8uY2hhbm5lbEluZm8/LltjaGFubmVsSW5kZXhdPy5iaXREZXB0aCA/PyA4O1xuICB9XG5cbiAgLyoqXG4gICAqIEFzeW5jIGhpc3RvZ3JhbSBmb3IgdGhlIENoYW5uZWxzICYgSGlzdG9ncmFtIHBhbmUuIEZvciA+OC1iaXQgaW1hZ2VzIGl0XG4gICAqIGZldGNoZXMgdGhlICoqbmF0aXZlKiogZGlzdHJpYnV0aW9uIGZyb20gdGhlIHNlcnZlciBgL2hpc3RvZ3JhbWAgZW5kcG9pbnRcbiAgICogKHRoZSA4LWJpdCBjYW52YXMgdGlsZXMgY2FuJ3QgY2FycnkgMTYtYml0IHZhbHVlcykgYW5kIGNhY2hlcyBpdCBwZXJcbiAgICogc2xpY2UrY2hhbm5lbDsgZm9yIDgtYml0L1JHQiBpdCByZXR1cm5zIHRoZSBleGlzdGluZyBjbGllbnQtc2FtcGxlZCA4LWJpdFxuICAgKiBoaXN0b2dyYW0uIFJlYWQtb25seSDigJQgaXQgbmV2ZXIgdG91Y2hlcyB0aGUgdGlsZS9kaXNwbGF5IHBpcGVsaW5lLlxuICAgKi9cbiAgZ2V0SGlzdG9ncmFtJChjaGFubmVsSW5kZXg6IG51bWJlciwgYmluczogbnVtYmVyKTogT2JzZXJ2YWJsZTxJSGlzdG9ncmFtIHwgbnVsbD4ge1xuICAgIGlmICh0aGlzLmNoYW5uZWxCaXREZXB0aChjaGFubmVsSW5kZXgpIDw9IDggfHwgIXRoaXMuaW5mb0I2NCkge1xuICAgICAgcmV0dXJuIG9mKHRoaXMuZ2V0SGlzdG9ncmFtKGNoYW5uZWxJbmRleCwgYmlucykpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5zYW1wbGVyLm5hdGl2ZSQodGhpcy5pbmZvQjY0LCB0aGlzLmN1cnJlbnRaLCBjaGFubmVsSW5kZXgsIGJpbnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4cG9ydCB0aGUgdW5kZXJseWluZyBpbWFnZSBkYXRhIGFzIGEgZGF0YS1wcmVzZXJ2aW5nIG11bHRpLWJhbmQgVElGRiBhdFxuICAgKiBuYXRpdmUgYml0IGRlcHRoIHZpYSBgR0VUIC9leHBvcnQvdGlmZmAgKHRoZSBzZXJ2ZXIgcmVhZHMgdGhlIHJhdyAxNi1iaXRcbiAgICogcGxhbmVzIOKAlCB0aGUgY2xpZW50IG5ldmVyIHNlZXMgdGhlbSkuIFNlbmRzIG9ubHkgdGhlIHZpc2libGUgY2hhbm5lbHM7IHRoZVxuICAgKiBleGlzdGluZyA4LWJpdCBjb21wb3NpdGUgUE5HIGV4cG9ydCBpcyB1bmFmZmVjdGVkLlxuICAgKi9cbiAgYXN5bmMgZXhwb3J0RGF0YSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuaW5mb0I2NCkgcmV0dXJuO1xuICAgIGNvbnN0IHZpc2libGUgPSB0aGlzLmNoYW5uZWxTdGF0ZXMuZmlsdGVyKChjKSA9PiBjLnZpc2libGUpLm1hcCgoYykgPT4gYy5pbmRleCk7XG4gICAgY29uc3QgYWxsID0gdGhpcy5jaGFubmVsU3RhdGVzLmxlbmd0aDtcbiAgICAvLyBPbWl0IGBjaGFubmVsc2Agd2hlbiBldmVyeSBjaGFubmVsIGlzIHZpc2libGUgKHNlcnZlciBkZWZhdWx0ID0gYWxsKS5cbiAgICBjb25zdCBjaFBhcmFtID1cbiAgICAgIHZpc2libGUubGVuZ3RoICYmIHZpc2libGUubGVuZ3RoIDwgYWxsID8gYCZjaGFubmVscz0ke3Zpc2libGUuam9pbignLCcpfWAgOiAnJztcbiAgICBjb25zdCB1cmwgPSBgJHt0aGlzLmFwaX1leHBvcnQvdGlmZj9pbmZvPSR7dGhpcy5pbmZvQjY0fSZ6PSR7dGhpcy5jdXJyZW50Wn0ke2NoUGFyYW19YDtcbiAgICBjb25zdCBzdGVtID0gKHRoaXMuY3VycmVudEZpbGVOYW1lIHx8ICdpbWFnZScpLnJlcGxhY2UoL1xcLlteLl0rJC8sICcnKTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IGZpcnN0VmFsdWVGcm9tKFxuICAgICAgICB0aGlzLmh0dHBcbiAgICAgICAgICAuZ2V0KHVybCwgeyBvYnNlcnZlOiAncmVzcG9uc2UnLCByZXNwb25zZVR5cGU6ICdibG9iJyB9KVxuICAgICAgICAgIC5waXBlKHRpbWVvdXQoNjAwMDAwKSksIC8vIGxhcmdlIGV4cG9ydHMgc3RyZWFtIHNsb3dseTsgZ2VuZXJvdXMgZGVhZGxpbmVcbiAgICAgICk7XG4gICAgICBpZiAocmVzcC5zdGF0dXMgPT09IDIwMikge1xuICAgICAgICBjb25zb2xlLndhcm4oJ1tPU0RdIDE2LWJpdCBleHBvcnQ6IGZpbGUgc3RpbGwgY2FjaGluZyDigJQgdHJ5IGFnYWluIHNob3J0bHkuJyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGJsb2IgPSByZXNwLmJvZHk7XG4gICAgICBpZiAoYmxvYikgc2F2ZUFzKGJsb2IsIGAke3N0ZW19XzE2Yml0Lm9tZS50aWZgKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUud2FybignW09TRF0gMTYtYml0IFRJRkYgZXhwb3J0IGZhaWxlZCcsIGVycik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEV4cG9ydCB0aGUgY3VycmVudCBzbGljZSBhcyBhIHB1YmxpY2F0aW9uLXJlYWR5IFBORyBjb21wb3NpdGVkIHdpdGggdGhlXG4gICAqIGFjdGl2ZSBkaXNwbGF5IHNldHRpbmdzICh3aW5kb3cgLyBnYW1tYSAvIGNvbG9ybWFwIG9yIHBlci1jaGFubmVsIHBzZXVkby1cbiAgICogY29sb3VycyAvIGludmVydCkuIFBpY2tzIHRoZSBsYXJnZXN0IHB5cmFtaWQgbGV2ZWwgdW5kZXIgYSBwaXhlbCBjYXAgKHRoZVxuICAgKiBjb2Fyc2VyIG92ZXJ2aWV3IGZvciBodWdlIHdob2xlLXNsaWRlcyksIGZldGNoZXMgdGhhdCBsZXZlbCdzIHRpbGUgZ3JpZCxcbiAgICogc3RpdGNoZXMgaXQgaW50byBvbmUgY2FudmFzLCBydW5zIHRoZSBzaGFyZWQgZGlzcGxheSBwaXBlbGluZSwgYW5kIHNhdmVzIGl0LlxuICAgKi9cbiAgYXN5bmMgZXhwb3J0Q29tcG9zaXRlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGRlc2MgPSB0aGlzLmRlc2NyaXB0b3I7XG4gICAgY29uc3QgbGV2ZWxzID0gZGVzYz8ubGV2ZWxzID8/IFtdO1xuICAgIGlmICghZGVzYyB8fCAhdGhpcy5pbmZvQjY0IHx8ICFsZXZlbHMubGVuZ3RoKSByZXR1cm47XG4gICAgY29uc3QgQ0FQID0gMzJfMDAwXzAwMDsgLy8gfjMyIE1QIOKAlCBib3VuZHMgbWVtb3J5IGZvciB3aG9sZS1zbGlkZSBpbWFnZXNcbiAgICBsZXQgcmVzID0gbGV2ZWxzLmxlbmd0aCAtIDE7IC8vIGNvYXJzZXN0IGZhbGxiYWNrXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsZXZlbHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChsZXZlbHNbaV0ud2lkdGggKiBsZXZlbHNbaV0uaGVpZ2h0IDw9IENBUCkgeyByZXMgPSBpOyBicmVhazsgfVxuICAgIH1cbiAgICBjb25zdCBsdyA9IGxldmVsc1tyZXNdLndpZHRoO1xuICAgIGNvbnN0IGxoID0gbGV2ZWxzW3Jlc10uaGVpZ2h0O1xuICAgIGNvbnN0IHQgPSBkZXNjLnRpbGVTaXplO1xuICAgIGNvbnN0IGNvbHMgPSBNYXRoLm1heCgxLCBNYXRoLmNlaWwobHcgLyB0KSk7XG4gICAgY29uc3Qgcm93cyA9IE1hdGgubWF4KDEsIE1hdGguY2VpbChsaCAvIHQpKTtcbiAgICBjb25zdCBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICBjYW52YXMud2lkdGggPSBsdztcbiAgICBjYW52YXMuaGVpZ2h0ID0gbGg7XG4gICAgY29uc3QgY3R4ID0gY2FudmFzLmdldENvbnRleHQoJzJkJywgeyB3aWxsUmVhZEZyZXF1ZW50bHk6IHRydWUgfSk7XG4gICAgaWYgKCFjdHgpIHJldHVybjtcbiAgICBjb25zdCB6ID0gdGhpcy5jdXJyZW50WjtcbiAgICBjb25zdCBqb2JzOiBQcm9taXNlPHZvaWQ+W10gPSBbXTtcbiAgICBmb3IgKGxldCByb3cgPSAwOyByb3cgPCByb3dzOyByb3crKykge1xuICAgICAgZm9yIChsZXQgY29sID0gMDsgY29sIDwgY29sczsgY29sKyspIHtcbiAgICAgICAgY29uc3QgdXJsID0gYnVpbGRUaWxlVXJsKHRoaXMuYXBpLCB0aGlzLmluZm9CNjQsIHsgcmVzLCBjb2wsIHJvdywgeiwgdGlsZVNpemU6IHQgfSk7XG4gICAgICAgIGpvYnMucHVzaChcbiAgICAgICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgY29uc3QgYm1wID0gYXdhaXQgZmV0Y2hUaWxlQml0bWFwKHRoaXMuaHR0cCwgdXJsLCAzMDAwMCk7XG4gICAgICAgICAgICAgIGN0eC5kcmF3SW1hZ2UoYm1wLCBjb2wgKiB0LCByb3cgKiB0KTtcbiAgICAgICAgICAgICAgYm1wLmNsb3NlPy4oKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgICAvLyBTa2lwIGEgZmFpbGVkIHRpbGUg4oCUIHRoZSBleHBvcnRlZCBjb21wb3NpdGUgaGFzIGEgZ2FwIHRoZXJlLlxuICAgICAgICAgICAgICBjb25zb2xlLndhcm4oJ1t2aXo6ZXhwb3J0XSBjb21wb3NpdGUgdGlsZSBmZXRjaCBmYWlsZWQsIHNraXBwaW5nJywgdXJsLCBlcnIpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pKCksXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICAgIGF3YWl0IFByb21pc2UuYWxsKGpvYnMpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBpbWFnZURhdGEgPSBjdHguZ2V0SW1hZ2VEYXRhKDAsIDAsIGx3LCBsaCk7XG4gICAgICBpZiAodGhpcy5kaXNwbGF5LmFwcGx5VG9SZ2JhKGltYWdlRGF0YS5kYXRhKSkgY3R4LnB1dEltYWdlRGF0YShpbWFnZURhdGEsIDAsIDApO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8gS2VlcCB0aGUgdW4tcmVjb2xvcmVkIGNvbXBvc2l0ZSBpZiByZWFkYmFjayBmYWlscyDigJQgYnV0IHNheSB3aHkuXG4gICAgICBjb25zb2xlLndhcm4oJ1t2aXo6ZXhwb3J0XSBjb21wb3NpdGUgcmVjb2xvciByZWFkYmFjayBmYWlsZWQg4oCUIGV4cG9ydGluZyByYXcgdGlsZXMnLCBlcnIpO1xuICAgIH1cbiAgICBjb25zdCBzdGVtID0gKHRoaXMuY3VycmVudEZpbGVOYW1lIHx8ICdpbWFnZScpLnJlcGxhY2UoL1xcLlteLl0rJC8sICcnKTtcbiAgICBjYW52YXMudG9CbG9iKChibG9iKSA9PiB7XG4gICAgICBpZiAoYmxvYikgc2F2ZUFzKGJsb2IsIGAke3N0ZW19X2NvbXBvc2l0ZS5wbmdgKTtcbiAgICB9LCAnaW1hZ2UvcG5nJyk7XG4gIH1cblxuICAvKiogVHJ1ZSBpZiBhbnkgcGl4ZWwgaW4gdGhlIFJHQkEgYnVmZmVyIGlzIG5vbi10cmFuc3BhcmVudC4gKi9cbiAgcHJpdmF0ZSBoYXNPcGFxdWUoZDogVWludDhDbGFtcGVkQXJyYXkpOiBib29sZWFuIHtcbiAgICBmb3IgKGxldCBpID0gMzsgaSA8IGQubGVuZ3RoOyBpICs9IDQpIHtcbiAgICAgIGlmIChkW2ldICE9PSAwKSByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgLy8g4pSA4pSAIElEaXNwbGF5T3B0aW9ucyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgLy8gSW5oZXJpdGVkIGZyb20gQmFzZVN0b3JlVmlzdWFsaXplciDigJQgcHVyZSBkZWxlZ2F0aW9ucyB0byB0aGUgc2hhcmVkXG4gIC8vIFZpc3VhbGl6ZXJTdG9yZSwgc28gT1NEIGFuZCBQbG90bHkgc3RheSBpbiBsb2NrLXN0ZXAuIE9TRCBhcHBsaWVzIHRoZVxuICAvLyBjb2xvcm1hcCB0byBncmF5c2NhbGUgdGlsZXMgdmlhIHRoZSBMVVQgcmVhY3RpdmVseSAoc2VlIHRoZSBjb25zdHJ1Y3RvcidzXG4gIC8vIGNvbG9ybWFwIHN1YnNjcmlwdGlvbiksIG5vdCBpbiBzZXRDb2xvcm1hcC5cblxuICAvLyDilIDilIAgSVZpc3VhbGl6ZXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIHVuc3Vic2NyaWJlKCk6IHZvaWQge1xuICAgIHRoaXMuY29sb3JtYXBTdWI/LnVuc3Vic2NyaWJlKCk7XG4gICAgdGhpcy5jb2xvcm1hcFN1YiA9IG51bGw7XG4gICAgdGhpcy5kZXN0cm95Vmlld2VyKCk7XG4gIH1cbn1cbiJdfQ==
|