@jax-data-science/sci-image-visualizer 0.8.3 → 0.9.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/README.md +201 -460
- package/esm2022/index.mjs +28 -15
- package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +163 -91
- package/esm2022/lib/contracts/capabilities.contract.mjs +4 -3
- package/esm2022/lib/contracts/cell-segmenter.contract.mjs +1 -1
- package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +5 -1
- package/esm2022/lib/contracts/color.mjs +60 -0
- package/esm2022/lib/contracts/colormap-lut.mjs +213 -57
- package/esm2022/lib/contracts/coordinate-transform.contract.mjs +1 -1
- package/esm2022/lib/contracts/display-types.mjs +11 -1
- package/esm2022/lib/contracts/instance-segmenter.contract.mjs +1 -1
- package/esm2022/lib/contracts/intensity.mjs +48 -5
- package/esm2022/lib/contracts/plot-type-contribution.contract.mjs +1 -1
- package/esm2022/lib/contracts/plot-type.mjs +134 -17
- package/esm2022/lib/contracts/ports/image-state.port.mjs +4 -1
- package/esm2022/lib/contracts/ports/preferences.port.mjs +5 -1
- package/esm2022/lib/contracts/ports/region-io.port.mjs +2 -1
- package/esm2022/lib/contracts/ports/spatial-data.port.mjs +5 -1
- package/esm2022/lib/contracts/ports/tile-access.port.mjs +2 -1
- package/esm2022/lib/contracts/region-editor-api.contract.mjs +1 -1
- package/esm2022/lib/contracts/region-overlay.contract.mjs +1 -1
- package/esm2022/lib/contracts/sam.contract.mjs +1 -1
- package/esm2022/lib/contracts/semantic-segmenter.contract.mjs +1 -1
- package/esm2022/lib/contracts/spatial-dataset.contract.mjs +3 -1
- package/esm2022/lib/contracts/toolbar-tool.contract.mjs +6 -4
- package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
- package/esm2022/lib/contracts/viz-config.mjs +2 -1
- package/esm2022/lib/geometry/contour.mjs +63 -0
- package/esm2022/lib/geometry/raster.mjs +98 -0
- package/esm2022/lib/geometry/ring.mjs +201 -0
- package/esm2022/lib/hex-color-picker/hex-color-picker.component.mjs +202 -102
- package/esm2022/lib/implementations/base-store-visualizer.mjs +201 -48
- package/esm2022/lib/implementations/napari-js/napari-axes-gizmo.mjs +65 -0
- package/esm2022/lib/implementations/napari-js/napari-display-state.mjs +136 -0
- package/esm2022/lib/implementations/napari-js/napari-helpers.mjs +199 -0
- package/esm2022/lib/implementations/napari-js/napari-image-2d-scene.mjs +602 -0
- package/esm2022/lib/implementations/napari-js/napari-loading-badge.mjs +3 -4
- package/esm2022/lib/implementations/napari-js/napari-loading-state.mjs +66 -0
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +157 -591
- package/esm2022/lib/implementations/napari-js/napari-scene.mjs +2 -0
- package/esm2022/lib/implementations/napari-js/napari-spatial-2d-scene.mjs +614 -0
- package/esm2022/lib/implementations/napari-js/napari-spatial-3d-scene.mjs +769 -0
- package/esm2022/lib/implementations/napari-js/napari-spatial-encoding.mjs +199 -0
- package/esm2022/lib/implementations/napari-js/napari-spatial-hover.mjs +263 -0
- package/esm2022/lib/implementations/napari-js/napari-spatial-scene.mjs +159 -0
- package/esm2022/lib/implementations/napari-js/napari-spatial-tiles.mjs +106 -1408
- package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +2 -2
- package/esm2022/lib/implementations/napari-js/napari-surface-scene.mjs +271 -0
- package/esm2022/lib/implementations/napari-js/napari-tile-client.mjs +562 -0
- package/esm2022/lib/implementations/napari-js/napari-tool-bridge.mjs +344 -0
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +358 -4218
- package/esm2022/lib/implementations/napari-js/napari-volume-scene.mjs +439 -0
- package/esm2022/lib/implementations/napari-js/napari-volume-z-handle.mjs +6 -6
- package/esm2022/lib/implementations/napari-js/region-overlay/overlay-projection.mjs +96 -0
- package/esm2022/lib/implementations/napari-js/region-overlay/region-edit.mjs +35 -0
- package/esm2022/lib/implementations/napari-js/region-overlay/region-svg-renderer.mjs +182 -0
- package/esm2022/lib/implementations/napari-js/spatial-tiles/categorical-lookup.mjs +30 -0
- package/esm2022/lib/implementations/napari-js/spatial-tiles/cell-layers.mjs +274 -0
- package/esm2022/lib/implementations/napari-js/spatial-tiles/density-layer.mjs +144 -0
- package/esm2022/lib/implementations/napari-js/spatial-tiles/layer-groups.mjs +165 -0
- package/esm2022/lib/implementations/napari-js/spatial-tiles/plan-context.mjs +78 -0
- package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-hover.mjs +182 -0
- package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-jobs.mjs +369 -0
- package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-layers.mjs +306 -0
- package/esm2022/lib/implementations/osd/display-pipeline.mjs +102 -40
- package/esm2022/lib/implementations/osd/histogram-sampler.mjs +56 -31
- package/esm2022/lib/implementations/osd/openseadragon-viewer-options.mjs +128 -0
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +333 -1685
- package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +1 -1
- package/esm2022/lib/implementations/osd/osd-coords.mjs +5 -6
- package/esm2022/lib/implementations/osd/osd-export.mjs +122 -0
- package/esm2022/lib/implementations/osd/osd-lib.mjs +15 -1
- package/esm2022/lib/implementations/osd/osd-navigator-chrome.mjs +205 -0
- package/esm2022/lib/implementations/osd/osd-pixel-readback.mjs +68 -0
- package/esm2022/lib/implementations/osd/osd-region-overlay-draw.mjs +153 -0
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +185 -718
- package/esm2022/lib/implementations/osd/osd-scale-bar.mjs +52 -60
- package/esm2022/lib/implementations/osd/osd-tile-source.mjs +105 -0
- package/esm2022/lib/implementations/osd/osd-viewer-like.mjs +2 -0
- package/esm2022/lib/implementations/osd/osd-viewer-mount.mjs +44 -0
- package/esm2022/lib/implementations/osd/osd-viewport.mjs +184 -0
- package/esm2022/lib/implementations/osd/osd-zoom.mjs +7 -1
- package/esm2022/lib/implementations/osd/simple-source.mjs +395 -0
- package/esm2022/lib/implementations/osd/slice-cache.mjs +53 -48
- package/esm2022/lib/implementations/osd/tile-client.mjs +32 -11
- package/esm2022/lib/implementations/osd/tile-recolor.mjs +330 -0
- package/esm2022/lib/implementations/plotly/omics-trace-builders.mjs +161 -73
- package/esm2022/lib/implementations/plotly/plotly-coordinate-transform.mjs +1 -1
- package/esm2022/lib/implementations/plotly/plotly-image-loader.mjs +112 -0
- package/esm2022/lib/implementations/plotly/plotly-image-recolor.mjs +178 -0
- package/esm2022/lib/implementations/plotly/plotly-intensity-inset.mjs +33 -0
- package/esm2022/lib/implementations/plotly/plotly-isosurface-controls.mjs +83 -0
- package/esm2022/lib/implementations/plotly/plotly-layouts.mjs +121 -0
- package/esm2022/lib/implementations/plotly/plotly-readback.mjs +117 -0
- package/esm2022/lib/implementations/plotly/plotly-region-overlay.mjs +19 -10
- package/esm2022/lib/implementations/plotly/plotly-shape-projection.mjs +223 -0
- package/esm2022/lib/implementations/plotly/plotly-trace-builders.mjs +79 -19
- package/esm2022/lib/implementations/plotly/plotly-zoom-controller.mjs +223 -0
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +439 -1655
- package/esm2022/lib/implementations/region-centroids.mjs +47 -0
- package/esm2022/lib/implementations/simple-slice-access.service.mjs +22 -12
- package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +172 -92
- package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +103 -22
- package/esm2022/lib/implementations/tile-server/index.mjs +6 -0
- package/esm2022/lib/implementations/tile-server/native-histogram.mjs +30 -0
- package/esm2022/lib/implementations/tile-server/poll-descriptor.mjs +65 -0
- package/esm2022/lib/implementations/tile-server/tile-protocol.mjs +42 -0
- package/esm2022/lib/implementations/tile-server/transport.mjs +135 -0
- package/esm2022/lib/intensity/intensity-profile.mjs +107 -0
- package/esm2022/lib/intensity/intensity-profile.service.mjs +223 -0
- package/esm2022/lib/intensity-inset/intensity-inset.component.mjs +133 -0
- package/esm2022/lib/models/bezier.mjs +1 -11
- package/esm2022/lib/models/class-preset.mjs +41 -3
- package/esm2022/lib/models/geometry.mjs +27 -164
- package/esm2022/lib/models/polygon-edit.mjs +254 -0
- package/esm2022/lib/models/polygon-factory.mjs +118 -0
- package/esm2022/lib/models/region-clone.mjs +68 -0
- package/esm2022/lib/models/region-geojson.mjs +373 -0
- package/esm2022/lib/models/region.mjs +34 -33
- package/esm2022/lib/models/shape.mjs +18 -16
- package/esm2022/lib/plot-mode/contribution-host.mjs +297 -0
- package/esm2022/lib/plot-mode/plot-mode-controller.mjs +9 -6
- package/esm2022/lib/plot-mode/plot-type-menu.mjs +85 -0
- package/esm2022/lib/plot-mode/tool-params-model.mjs +87 -0
- package/esm2022/lib/plot.utilities.mjs +296 -473
- package/esm2022/lib/processing/image-crop.mjs +1 -1
- package/esm2022/lib/processing/processing-image.mjs +28 -15
- package/esm2022/lib/provide-visualization.mjs +40 -14
- package/esm2022/lib/region-editor/classes-panel/classes-panel.component.mjs +99 -0
- package/esm2022/lib/region-editor/file-stem.mjs +13 -0
- package/esm2022/lib/region-editor/manage-classes-dialog/manage-classes-dialog.component.mjs +118 -0
- package/esm2022/lib/region-editor/mask-export.service.mjs +98 -0
- package/esm2022/lib/region-editor/mask-raster.mjs +11 -5
- package/esm2022/lib/region-editor/region-color-dialog/region-color-dialog.component.mjs +56 -0
- package/esm2022/lib/region-editor/region-editor-help/region-editor-help.component.mjs +28 -0
- package/esm2022/lib/region-editor/region-editor.component.mjs +402 -651
- package/esm2022/lib/region-editor/region-metrics.mjs +68 -0
- package/esm2022/lib/region-editor/region-persistence.service.mjs +134 -0
- package/esm2022/lib/region-editor/region-table/region-table.component.mjs +153 -0
- package/esm2022/lib/region-editor/save-mask-dialog/save-mask-dialog.component.mjs +91 -0
- package/esm2022/lib/region-editor/save-regions-dialog/save-regions-dialog.component.mjs +92 -0
- package/esm2022/lib/region-ops.service.mjs +28 -70
- package/esm2022/lib/region-overlay/region-geometry.mjs +360 -0
- package/esm2022/lib/region-overlay/region-hit-test.mjs +103 -0
- package/esm2022/lib/region-overlay/svg-region-renderer.mjs +228 -0
- package/esm2022/lib/render-orchestrator.mjs +14 -11
- package/esm2022/lib/render-session.mjs +207 -0
- package/esm2022/lib/routing-visualizer.service.mjs +459 -411
- package/esm2022/lib/spatial/density-raster.mjs +14 -0
- package/esm2022/lib/spatial/embedding-compute-coordinator.mjs +239 -0
- package/esm2022/lib/spatial/embedding-compute.mjs +43 -10
- package/esm2022/lib/spatial/gene-search.mjs +1 -1
- package/esm2022/lib/spatial/glyphs.mjs +97 -0
- package/esm2022/lib/spatial/lod.mjs +187 -0
- package/esm2022/lib/spatial/spatial-controls.facade.mjs +200 -0
- package/esm2022/lib/spatial/spatial-density.mjs +25 -16
- package/esm2022/lib/spatial/spatial-encoding.mjs +26 -82
- package/esm2022/lib/spatial/spatial-expression.mjs +43 -42
- package/esm2022/lib/spatial/spatial-framing.mjs +1 -1
- package/esm2022/lib/spatial/spatial-heatmap.mjs +6 -3
- package/esm2022/lib/spatial/spatial-hover.mjs +5 -33
- package/esm2022/lib/spatial/spatial-panel-model.mjs +282 -0
- package/esm2022/lib/spatial/spatial-sections.mjs +1 -1
- package/esm2022/lib/spatial/spatial-selection.mjs +33 -46
- package/esm2022/lib/spatial/spatial-tile-merge.mjs +219 -0
- package/esm2022/lib/spatial/spatial-tiles.mjs +12 -648
- package/esm2022/lib/spatial/spatial-volume-image.mjs +2 -4
- package/esm2022/lib/spatial/stats.mjs +71 -0
- package/esm2022/lib/spatial/transcript-grouping.mjs +307 -0
- package/esm2022/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.mjs +150 -0
- package/esm2022/lib/spatial-controls/spatial-charts/chart-data-model.mjs +309 -0
- package/esm2022/lib/spatial-controls/spatial-charts/chart-help.mjs +145 -0
- package/esm2022/lib/spatial-controls/spatial-charts/plotly-chart-host.mjs +158 -0
- package/esm2022/lib/spatial-controls/spatial-charts/spatial-chart-window/spatial-chart-window.component.mjs +47 -0
- package/esm2022/lib/spatial-controls/spatial-charts/spatial-charts.component.mjs +297 -802
- package/esm2022/lib/spatial-controls/spatial-charts/spatial-embedding-controls/spatial-embedding-controls.component.mjs +89 -0
- package/esm2022/lib/spatial-controls/spatial-charts/spatial-heatmap-controls/spatial-heatmap-controls.component.mjs +52 -0
- package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +143 -1238
- package/esm2022/lib/spatial-controls/spatial-gene-picker.mjs +153 -0
- package/esm2022/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.mjs +241 -0
- package/esm2022/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.mjs +193 -0
- package/esm2022/lib/spatial-controls/spatial-key/spatial-key.component.mjs +132 -0
- package/esm2022/lib/spatial-controls/spatial-key/spatial-key.model.mjs +67 -0
- package/esm2022/lib/spatial-controls/spatial-marker-genes-form/spatial-marker-genes-form.component.mjs +147 -0
- package/esm2022/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.mjs +300 -0
- package/esm2022/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.mjs +370 -0
- package/esm2022/lib/store/class-color.util.mjs +46 -3
- package/esm2022/lib/store/region-history.mjs +146 -0
- package/esm2022/lib/store/region-scope-cache.mjs +140 -0
- package/esm2022/lib/store/region-selection.mjs +82 -0
- package/esm2022/lib/store/region-store.service.mjs +279 -675
- package/esm2022/lib/store/spatial-selection.service.mjs +2 -2
- package/esm2022/lib/store/visualizer-store.service.mjs +49 -71
- package/esm2022/lib/toast-outlets.mjs +5 -4
- package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +92 -434
- package/esm2022/lib/toolbar/canvas-tools.mjs +20 -0
- package/esm2022/lib/toolbar/crop/slide-crop.mjs +26 -42
- package/esm2022/lib/toolbar/plot-type-selector/plot-type-selector.component.mjs +85 -0
- package/esm2022/lib/toolbar/region-tools/region-tool-buttons.mjs +74 -0
- package/esm2022/lib/toolbar/region-tools/region-tools.component.mjs +114 -0
- package/esm2022/lib/toolbar/segmentation/cell-segment-tool.service.mjs +74 -78
- package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +50 -8
- package/esm2022/lib/toolbar/segmentation/onnx-sam-session.mjs +61 -42
- package/esm2022/lib/toolbar/segmentation/pending-calls.mjs +47 -0
- package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +12 -4
- package/esm2022/lib/toolbar/segmentation/sam-onnx-core.mjs +70 -14
- package/esm2022/lib/toolbar/segmentation/sam-point-tool.service.mjs +135 -172
- package/esm2022/lib/toolbar/segmentation/sam-prompt.mjs +9 -90
- package/esm2022/lib/toolbar/segmentation/sam-session.service.mjs +183 -0
- package/esm2022/lib/toolbar/segmentation/sam-tool.service.mjs +95 -150
- package/esm2022/lib/toolbar/segmentation/segmentation-commit.mjs +12 -0
- package/esm2022/lib/toolbar/segmentation/segmentation-runner.mjs +108 -0
- package/esm2022/lib/toolbar/segmentation-tools/segmentation-tools.component.mjs +155 -0
- package/esm2022/lib/toolbar/stack-controls/stack-controls.component.mjs +81 -0
- package/esm2022/lib/toolbar/tool-kit/async-tool-status.mjs +37 -0
- package/esm2022/lib/toolbar/tool-kit/canvas-tool-manager.mjs +63 -0
- package/esm2022/lib/toolbar/tool-kit/canvas-tool.mjs +2 -0
- package/esm2022/lib/toolbar/tool-kit/frame-pixels.mjs +108 -0
- package/esm2022/lib/toolbar/tool-kit/mask-stroke-editor.mjs +306 -0
- package/esm2022/lib/toolbar/tool-kit/matrix-frame.mjs +61 -0
- package/esm2022/lib/toolbar/tool-kit/tool-overlay.mjs +140 -0
- package/esm2022/lib/toolbar/tool-kit/undo-gesture.mjs +24 -0
- package/esm2022/lib/toolbar/tool-slider/tool-slider.component.mjs +63 -0
- package/esm2022/lib/toolbar/toolbar-help-dialog/toolbar-help-dialog.component.mjs +50 -0
- package/esm2022/lib/toolbar/toolbar.component.mjs +68 -117
- package/esm2022/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.mjs +93 -115
- package/esm2022/lib/toolbar/view-controls/view-controls.component.mjs +107 -0
- package/esm2022/lib/toolbar/wand/wand-region-grow.mjs +321 -0
- package/esm2022/lib/toolbar/wand/wand-tool.service.mjs +121 -416
- package/esm2022/lib/toolbar/wand/wand.service.mjs +30 -894
- package/esm2022/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.mjs +48 -90
- package/esm2022/lib/util/mark-for-check.mjs +17 -0
- package/esm2022/lib/util/supersede.mjs +56 -0
- package/esm2022/lib/visualization.module.mjs +39 -141
- package/esm2022/lib/visualizer/floating-drag.directive.mjs +58 -0
- package/esm2022/lib/visualizer/region-actions.mjs +149 -0
- package/esm2022/lib/visualizer/region-load.mjs +60 -0
- package/esm2022/lib/visualizer/spatial-dataset-binder.mjs +163 -0
- package/esm2022/lib/visualizer/tool-modes.mjs +113 -0
- package/esm2022/lib/visualizer/viewer-shortcuts.mjs +167 -0
- package/esm2022/lib/visualizer/visualizer-context-menu.mjs +119 -0
- package/esm2022/lib/visualizer.component.mjs +480 -1902
- package/esm2022/lib/workers/spatial-math-ops.mjs +43 -0
- package/esm2022/lib/workers/spatial-math-worker.mjs +10 -0
- package/esm2022/lib/workers/spatial-math.mjs +190 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-Csxlnq7I.mjs +41083 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-Csxlnq7I.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-Bkujqg7m.mjs +238 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-Bkujqg7m.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-spatial-math-worker-BzVFFqGS.mjs +12 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-spatial-math-worker-BzVFFqGS.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +41 -34467
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/fesm2022/mask.worker.js +36 -917
- package/fesm2022/onnx-sam.worker.js +52 -16
- package/fesm2022/spatial-math.worker.js +471 -0
- package/fesm2022/tsne.worker.js +32 -15
- package/index.d.ts +13 -10
- package/lib/channel-histogram/channel-histogram.component.d.ts +53 -43
- package/lib/contracts/capabilities.contract.d.ts +4 -2
- package/lib/contracts/cell-segmenter.contract.d.ts +1 -0
- package/lib/contracts/channel-histogram-api.contract.d.ts +4 -0
- package/lib/contracts/color.d.ts +27 -0
- package/lib/contracts/colormap-lut.d.ts +19 -4
- package/lib/contracts/coordinate-transform.contract.d.ts +3 -3
- package/lib/contracts/display-types.d.ts +21 -0
- package/lib/contracts/instance-segmenter.contract.d.ts +5 -0
- package/lib/contracts/intensity.d.ts +13 -2
- package/lib/contracts/plot-type.d.ts +3 -1
- package/lib/contracts/ports/image-state.port.d.ts +6 -2
- package/lib/contracts/ports/preferences.port.d.ts +4 -0
- package/lib/contracts/ports/region-io.port.d.ts +1 -0
- package/lib/contracts/ports/spatial-data.port.d.ts +4 -10
- package/lib/contracts/ports/tile-access.port.d.ts +1 -0
- package/lib/contracts/region-editor-api.contract.d.ts +1 -1
- package/lib/contracts/region-overlay.contract.d.ts +5 -0
- package/lib/contracts/sam.contract.d.ts +7 -0
- package/lib/contracts/semantic-segmenter.contract.d.ts +6 -0
- package/lib/contracts/spatial-dataset.contract.d.ts +19 -6
- package/lib/contracts/toolbar-tool.contract.d.ts +1 -0
- package/lib/contracts/visualizer.contract.d.ts +221 -54
- package/lib/contracts/viz-config.d.ts +1 -0
- package/lib/geometry/contour.d.ts +24 -0
- package/lib/geometry/raster.d.ts +42 -0
- package/lib/geometry/ring.d.ts +67 -0
- package/lib/hex-color-picker/hex-color-picker.component.d.ts +61 -27
- package/lib/implementations/base-store-visualizer.d.ts +61 -15
- package/lib/implementations/napari-js/napari-axes-gizmo.d.ts +36 -0
- package/lib/implementations/napari-js/napari-display-state.d.ts +76 -0
- package/lib/implementations/napari-js/napari-helpers.d.ts +129 -0
- package/lib/implementations/napari-js/napari-image-2d-scene.d.ts +162 -0
- package/lib/implementations/napari-js/napari-loading-badge.d.ts +0 -1
- package/lib/implementations/napari-js/napari-loading-state.d.ts +30 -0
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +62 -70
- package/lib/implementations/napari-js/napari-scene.d.ts +93 -0
- package/lib/implementations/napari-js/napari-spatial-2d-scene.d.ts +163 -0
- package/lib/implementations/napari-js/napari-spatial-3d-scene.d.ts +212 -0
- package/lib/implementations/napari-js/napari-spatial-encoding.d.ts +114 -0
- package/lib/implementations/napari-js/napari-spatial-hover.d.ts +89 -0
- package/lib/implementations/napari-js/napari-spatial-scene.d.ts +118 -0
- package/lib/implementations/napari-js/napari-spatial-tiles.d.ts +56 -263
- package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +1 -1
- package/lib/implementations/napari-js/napari-surface-scene.d.ts +76 -0
- package/lib/implementations/napari-js/napari-tile-client.d.ts +214 -0
- package/lib/implementations/napari-js/napari-tool-bridge.d.ts +179 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +121 -883
- package/lib/implementations/napari-js/napari-volume-scene.d.ts +104 -0
- package/lib/implementations/napari-js/napari-volume-z-handle.d.ts +8 -12
- package/lib/implementations/napari-js/region-overlay/overlay-projection.d.ts +59 -0
- package/lib/implementations/napari-js/region-overlay/region-edit.d.ts +15 -0
- package/lib/implementations/napari-js/region-overlay/region-svg-renderer.d.ts +54 -0
- package/lib/implementations/napari-js/spatial-tiles/categorical-lookup.d.ts +26 -0
- package/lib/implementations/napari-js/spatial-tiles/cell-layers.d.ts +70 -0
- package/lib/implementations/napari-js/spatial-tiles/density-layer.d.ts +61 -0
- package/lib/implementations/napari-js/spatial-tiles/layer-groups.d.ts +67 -0
- package/lib/implementations/napari-js/spatial-tiles/plan-context.d.ts +38 -0
- package/lib/implementations/napari-js/spatial-tiles/transcript-hover.d.ts +71 -0
- package/lib/implementations/napari-js/spatial-tiles/transcript-jobs.d.ts +105 -0
- package/lib/implementations/napari-js/spatial-tiles/transcript-layers.d.ts +56 -0
- package/lib/implementations/osd/display-pipeline.d.ts +38 -9
- package/lib/implementations/osd/histogram-sampler.d.ts +22 -12
- package/lib/implementations/osd/openseadragon-viewer-options.d.ts +35 -0
- package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +78 -354
- package/lib/implementations/osd/osd-coordinate-transform.d.ts +2 -1
- package/lib/implementations/osd/osd-coords.d.ts +6 -4
- package/lib/implementations/osd/osd-export.d.ts +36 -0
- package/lib/implementations/osd/osd-lib.d.ts +7 -0
- package/lib/implementations/osd/osd-navigator-chrome.d.ts +69 -0
- package/lib/implementations/osd/osd-pixel-readback.d.ts +22 -0
- package/lib/implementations/osd/osd-region-overlay-draw.d.ts +37 -0
- package/lib/implementations/osd/osd-region-overlay.d.ts +36 -48
- package/lib/implementations/osd/osd-scale-bar.d.ts +8 -6
- package/lib/implementations/osd/osd-tile-source.d.ts +49 -0
- package/lib/implementations/osd/osd-viewer-like.d.ts +100 -0
- package/lib/implementations/osd/osd-viewer-mount.d.ts +14 -0
- package/lib/implementations/osd/osd-viewport.d.ts +77 -0
- package/lib/implementations/osd/osd-zoom.d.ts +6 -0
- package/lib/implementations/osd/simple-source.d.ts +161 -0
- package/lib/implementations/osd/slice-cache.d.ts +12 -3
- package/lib/implementations/osd/tile-client.d.ts +11 -14
- package/lib/implementations/osd/tile-recolor.d.ts +141 -0
- package/lib/implementations/plotly/omics-trace-builders.d.ts +32 -20
- package/lib/implementations/plotly/plotly-image-loader.d.ts +49 -0
- package/lib/implementations/plotly/plotly-image-recolor.d.ts +75 -0
- package/lib/implementations/plotly/plotly-intensity-inset.d.ts +8 -0
- package/lib/implementations/plotly/plotly-isosurface-controls.d.ts +38 -0
- package/lib/implementations/plotly/plotly-layouts.d.ts +188 -0
- package/lib/implementations/plotly/plotly-readback.d.ts +41 -0
- package/lib/implementations/plotly/plotly-region-overlay.d.ts +2 -2
- package/lib/implementations/plotly/plotly-shape-projection.d.ts +92 -0
- package/lib/implementations/plotly/plotly-trace-builders.d.ts +23 -12
- package/lib/implementations/plotly/plotly-zoom-controller.d.ts +86 -0
- package/lib/implementations/plotly/plotly.service.d.ts +136 -401
- package/lib/implementations/region-centroids.d.ts +14 -0
- package/lib/implementations/simple-slice-access.service.d.ts +7 -4
- package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +42 -18
- package/lib/implementations/spatial-data-http/spatial-wire.d.ts +34 -9
- package/lib/implementations/tile-server/index.d.ts +5 -0
- package/lib/implementations/tile-server/native-histogram.d.ts +25 -0
- package/lib/implementations/tile-server/poll-descriptor.d.ts +39 -0
- package/lib/implementations/tile-server/tile-protocol.d.ts +80 -0
- package/lib/implementations/tile-server/transport.d.ts +62 -0
- package/lib/intensity/intensity-profile.d.ts +66 -0
- package/lib/intensity/intensity-profile.service.d.ts +108 -0
- package/lib/intensity-inset/intensity-inset.component.d.ts +58 -0
- package/lib/models/bezier.d.ts +0 -5
- package/lib/models/class-preset.d.ts +18 -0
- package/lib/models/geometry.d.ts +14 -48
- package/lib/models/polygon-edit.d.ts +72 -0
- package/lib/models/polygon-factory.d.ts +40 -0
- package/lib/models/region-clone.d.ts +20 -0
- package/lib/models/region-geojson.d.ts +17 -0
- package/lib/models/region.d.ts +40 -4
- package/lib/models/shape.d.ts +8 -0
- package/lib/plot-mode/contribution-host.d.ts +125 -0
- package/lib/plot-mode/plot-type-menu.d.ts +60 -0
- package/lib/plot-mode/tool-params-model.d.ts +49 -0
- package/lib/plot.utilities.d.ts +19 -30
- package/lib/processing/processing-image.d.ts +5 -2
- package/lib/provide-visualization.d.ts +19 -2
- package/lib/region-editor/classes-panel/classes-panel.component.d.ts +56 -0
- package/lib/region-editor/file-stem.d.ts +7 -0
- package/lib/region-editor/manage-classes-dialog/manage-classes-dialog.component.d.ts +45 -0
- package/lib/region-editor/mask-export.service.d.ts +54 -0
- package/lib/region-editor/mask-raster.d.ts +2 -8
- package/lib/region-editor/region-color-dialog/region-color-dialog.component.d.ts +29 -0
- package/lib/region-editor/region-editor-help/region-editor-help.component.d.ts +11 -0
- package/lib/region-editor/region-editor.component.d.ts +166 -199
- package/lib/region-editor/region-metrics.d.ts +26 -0
- package/lib/region-editor/region-persistence.service.d.ts +69 -0
- package/lib/region-editor/region-table/region-table.component.d.ts +83 -0
- package/lib/region-editor/save-mask-dialog/save-mask-dialog.component.d.ts +36 -0
- package/lib/region-editor/save-regions-dialog/save-regions-dialog.component.d.ts +37 -0
- package/lib/region-ops.service.d.ts +1 -7
- package/lib/region-overlay/region-geometry.d.ts +157 -0
- package/lib/region-overlay/region-hit-test.d.ts +38 -0
- package/lib/region-overlay/svg-region-renderer.d.ts +110 -0
- package/lib/render-orchestrator.d.ts +6 -2
- package/lib/render-session.d.ts +75 -0
- package/lib/routing-visualizer.service.d.ts +110 -58
- package/lib/spatial/density-raster.d.ts +6 -0
- package/lib/spatial/embedding-compute-coordinator.d.ts +123 -0
- package/lib/spatial/embedding-compute.d.ts +21 -2
- package/lib/spatial/glyphs.d.ts +16 -0
- package/lib/spatial/lod.d.ts +94 -0
- package/lib/spatial/spatial-controls.facade.d.ts +64 -0
- package/lib/spatial/spatial-density.d.ts +15 -5
- package/lib/spatial/spatial-encoding.d.ts +11 -44
- package/lib/spatial/spatial-expression.d.ts +40 -22
- package/lib/spatial/spatial-framing.d.ts +1 -0
- package/lib/spatial/spatial-heatmap.d.ts +2 -0
- package/lib/spatial/spatial-hover.d.ts +4 -16
- package/lib/spatial/spatial-panel-model.d.ts +135 -0
- package/lib/spatial/spatial-selection.d.ts +21 -26
- package/lib/spatial/spatial-tile-merge.d.ts +41 -0
- package/lib/spatial/spatial-tiles.d.ts +9 -256
- package/lib/spatial/stats.d.ts +38 -0
- package/lib/spatial/transcript-grouping.d.ts +119 -0
- package/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.d.ts +64 -0
- package/lib/spatial-controls/spatial-charts/chart-data-model.d.ts +153 -0
- package/lib/spatial-controls/spatial-charts/chart-help.d.ts +69 -0
- package/lib/spatial-controls/spatial-charts/plotly-chart-host.d.ts +98 -0
- package/lib/spatial-controls/spatial-charts/spatial-chart-window/spatial-chart-window.component.d.ts +26 -0
- package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +101 -260
- package/lib/spatial-controls/spatial-charts/spatial-embedding-controls/spatial-embedding-controls.component.d.ts +43 -0
- package/lib/spatial-controls/spatial-charts/spatial-heatmap-controls/spatial-heatmap-controls.component.d.ts +27 -0
- package/lib/spatial-controls/spatial-controls.component.d.ts +68 -448
- package/lib/spatial-controls/spatial-gene-picker.d.ts +86 -0
- package/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.d.ts +102 -0
- package/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.d.ts +72 -0
- package/lib/spatial-controls/spatial-key/spatial-key.component.d.ts +66 -0
- package/lib/spatial-controls/spatial-key/spatial-key.model.d.ts +33 -0
- package/lib/spatial-controls/spatial-marker-genes-form/spatial-marker-genes-form.component.d.ts +64 -0
- package/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.d.ts +117 -0
- package/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.d.ts +142 -0
- package/lib/store/class-color.util.d.ts +35 -1
- package/lib/store/region-history.d.ts +66 -0
- package/lib/store/region-scope-cache.d.ts +63 -0
- package/lib/store/region-selection.d.ts +36 -0
- package/lib/store/region-store.service.d.ts +104 -112
- package/lib/store/visualizer-store.service.d.ts +22 -33
- package/lib/toast-outlets.d.ts +4 -3
- package/lib/toolbar/brush/brush-tool.service.d.ts +31 -98
- package/lib/toolbar/canvas-tools.d.ts +20 -0
- package/lib/toolbar/crop/slide-crop.d.ts +10 -4
- package/lib/toolbar/plot-type-selector/plot-type-selector.component.d.ts +38 -0
- package/lib/toolbar/region-tools/region-tool-buttons.d.ts +21 -0
- package/lib/toolbar/region-tools/region-tools.component.d.ts +60 -0
- package/lib/toolbar/segmentation/cell-segment-tool.service.d.ts +10 -14
- package/lib/toolbar/segmentation/cellpose-segmenter.service.d.ts +12 -2
- package/lib/toolbar/segmentation/onnx-sam-session.d.ts +22 -3
- package/lib/toolbar/segmentation/pending-calls.d.ts +31 -0
- package/lib/toolbar/segmentation/sam-model-registry.d.ts +9 -3
- package/lib/toolbar/segmentation/sam-onnx-core.d.ts +18 -5
- package/lib/toolbar/segmentation/sam-point-tool.service.d.ts +46 -32
- package/lib/toolbar/segmentation/sam-prompt.d.ts +7 -31
- package/lib/toolbar/segmentation/sam-session.service.d.ts +74 -0
- package/lib/toolbar/segmentation/sam-tool.service.d.ts +23 -30
- package/lib/toolbar/segmentation/segmentation-commit.d.ts +8 -0
- package/lib/toolbar/segmentation/segmentation-runner.d.ts +52 -0
- package/lib/toolbar/segmentation-tools/segmentation-tools.component.d.ts +87 -0
- package/lib/toolbar/stack-controls/stack-controls.component.d.ts +40 -0
- package/lib/toolbar/tool-kit/async-tool-status.d.ts +21 -0
- package/lib/toolbar/tool-kit/canvas-tool-manager.d.ts +36 -0
- package/lib/toolbar/tool-kit/canvas-tool.d.ts +86 -0
- package/lib/toolbar/tool-kit/frame-pixels.d.ts +45 -0
- package/lib/toolbar/tool-kit/mask-stroke-editor.d.ts +125 -0
- package/lib/toolbar/tool-kit/matrix-frame.d.ts +40 -0
- package/lib/toolbar/tool-kit/tool-overlay.d.ts +60 -0
- package/lib/toolbar/tool-kit/undo-gesture.d.ts +17 -0
- package/lib/toolbar/tool-slider/tool-slider.component.d.ts +33 -0
- package/lib/toolbar/toolbar-help-dialog/toolbar-help-dialog.component.d.ts +24 -0
- package/lib/toolbar/toolbar.component.d.ts +47 -83
- package/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.d.ts +33 -40
- package/lib/toolbar/view-controls/view-controls.component.d.ts +54 -0
- package/lib/toolbar/wand/wand-region-grow.d.ts +49 -0
- package/lib/toolbar/wand/wand-tool.service.d.ts +39 -124
- package/lib/toolbar/wand/wand.service.d.ts +22 -116
- package/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.d.ts +17 -37
- package/lib/util/mark-for-check.d.ts +11 -0
- package/lib/util/supersede.d.ts +53 -0
- package/lib/visualization.module.d.ts +18 -39
- package/lib/visualizer/floating-drag.directive.d.ts +30 -0
- package/lib/visualizer/region-actions.d.ts +54 -0
- package/lib/visualizer/region-load.d.ts +27 -0
- package/lib/visualizer/spatial-dataset-binder.d.ts +74 -0
- package/lib/visualizer/tool-modes.d.ts +49 -0
- package/lib/visualizer/viewer-shortcuts.d.ts +71 -0
- package/lib/visualizer/visualizer-context-menu.d.ts +55 -0
- package/lib/visualizer.component.d.ts +201 -447
- package/lib/workers/spatial-math-ops.d.ts +65 -0
- package/lib/workers/spatial-math-worker.d.ts +7 -0
- package/lib/workers/spatial-math.d.ts +74 -0
- package/package.json +12 -3
- package/src/lib/styles/_viz-icons-rules.scss +73 -0
- package/src/lib/styles/viz-icons.scss +14 -0
- package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +0 -97
- package/esm2022/lib/implementations/napari-js/napari-navigator.mjs +0 -195
- package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +0 -92
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs +0 -329
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs.map +0 -1
- package/lib/implementations/napari-js/napari-axes-labels.d.ts +0 -49
- package/lib/implementations/napari-js/napari-navigator.d.ts +0 -72
- package/lib/implementations/napari-js/napari-scale-bar.d.ts +0 -35
- package/src/lib/assets/cell-detection.svg +0 -37
- package/src/lib/assets/click.svg +0 -1
- package/src/lib/assets/colormap-luts.json +0 -1
- package/src/lib/assets/edit-selection-white.svg +0 -47
- package/src/lib/assets/icons/colormap-earth.png +0 -0
- package/src/lib/assets/icons/colormap-placeholder.png +0 -0
- package/src/lib/assets/icons/colormap-pubu.png +0 -0
- package/src/lib/assets/interactive-segmentation-cursor.svg +0 -1
- package/src/lib/assets/picture-segmentation.svg +0 -43
- package/src/lib/assets/test/download-379.svg +0 -1
- package/src/lib/assets/test/download-98.svg +0 -1
- package/src/lib/assets/test/json-1.svg +0 -1
- package/src/lib/assets/test/load-53.svg +0 -1
- package/src/lib/assets/test/mask-44.svg +0 -1
- package/src/lib/assets/test/mask-download-44.svg +0 -15
- package/src/lib/assets/test/mask-save-44.svg +0 -11
- package/src/lib/assets/test/overlay-1.svg +0 -1
- package/src/lib/assets/test/overlay-4.svg +0 -1
- package/src/lib/assets/test/region-26.svg +0 -1
- package/src/lib/assets/test/save-56.svg +0 -1
- package/src/lib/assets/test/upload-43.svg +0 -1
|
@@ -1,28 +1,52 @@
|
|
|
1
|
-
import { ChangeDetectorRef, Component,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, EventEmitter, Inject, Injector, Input, NgZone, Optional, Output, ViewChild, computed, signal, } from '@angular/core';
|
|
2
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
3
|
+
import { CommonModule } from '@angular/common';
|
|
4
|
+
import { FormsModule } from '@angular/forms';
|
|
5
|
+
import { Observable } from 'rxjs';
|
|
6
|
+
import { MessageService, SharedModule } from 'primeng/api';
|
|
7
|
+
import { ButtonModule } from 'primeng/button';
|
|
8
|
+
import { CheckboxModule } from 'primeng/checkbox';
|
|
9
|
+
import { ContextMenu, ContextMenuModule } from 'primeng/contextmenu';
|
|
10
|
+
import { DialogModule } from 'primeng/dialog';
|
|
11
|
+
import { DropdownModule } from 'primeng/dropdown';
|
|
12
|
+
import { InputNumberModule } from 'primeng/inputnumber';
|
|
13
|
+
import { ProgressBarModule } from 'primeng/progressbar';
|
|
14
|
+
import { SliderModule } from 'primeng/slider';
|
|
15
|
+
import { ToastModule } from 'primeng/toast';
|
|
16
|
+
import { TooltipModule } from 'primeng/tooltip';
|
|
6
17
|
import { IMAGE_STATE_PORT } from './contracts/ports/image-state.port';
|
|
7
|
-
import { Polygon, Rectangle, MultiPolygon } from './models/region';
|
|
8
18
|
import { RegionOpsService } from './region-ops.service';
|
|
9
19
|
import { VIZ_TOAST_KEY, VIZ_ALERT_TOAST_KEY } from './toast-outlets';
|
|
10
20
|
import { VisualizerStore } from './store/visualizer-store.service';
|
|
11
|
-
import {
|
|
21
|
+
import { SliceScrubber } from './render-orchestrator';
|
|
22
|
+
import { ImageRenderSession, errorMessage } from './render-session';
|
|
12
23
|
import { PlotType, getPlotTypeDescriptor, isBuiltinPlotType, isNapari3d, isSpatialOmics, isSpatialOmics3d, rendererOwnsWheel, NAPARI_DEFAULT_DECIMATE, } from './contracts/plot-type';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
24
|
+
import { PLOT_TYPE_CONTRIBUTIONS, } from './contracts/plot-type-contribution.contract';
|
|
25
|
+
import { computePlotTypeMenu, reconcilePlotType } from './plot-mode/plot-type-menu';
|
|
26
|
+
import { ToolParamsModel } from './plot-mode/tool-params-model';
|
|
27
|
+
import { ContributionHost } from './plot-mode/contribution-host';
|
|
16
28
|
import { VISUALIZER } from './contracts/visualizer.contract';
|
|
17
29
|
import { SAM_MODELS, getDefaultSamModelId, isSamModelReady } from './toolbar/segmentation/sam-model-registry';
|
|
18
30
|
import { SamToolService } from './toolbar/segmentation/sam-tool.service';
|
|
19
31
|
import { SamPointToolService } from './toolbar/segmentation/sam-point-tool.service';
|
|
20
32
|
import { CellSegmentToolService } from './toolbar/segmentation/cell-segment-tool.service';
|
|
21
|
-
import {
|
|
33
|
+
import { SegmentationRunner } from './toolbar/segmentation/segmentation-runner';
|
|
34
|
+
import { TOOLBAR_TOOLS, visibleToolContributions, } from './contracts/toolbar-tool.contract';
|
|
22
35
|
import { ALL_TOOLBAR_TOOLS } from './contracts/toolbar-config';
|
|
23
36
|
import { VIZ_CONFIG } from './contracts/viz-config';
|
|
24
37
|
import { SPATIAL_DATA_PORT } from './contracts/ports/spatial-data.port';
|
|
25
|
-
import {
|
|
38
|
+
import { applyImageRois } from './visualizer/region-load';
|
|
39
|
+
import { buildContextMenu } from './visualizer/visualizer-context-menu';
|
|
40
|
+
import { RegionActions } from './visualizer/region-actions';
|
|
41
|
+
import { ToolModes } from './visualizer/tool-modes';
|
|
42
|
+
import { ViewerShortcuts } from './visualizer/viewer-shortcuts';
|
|
43
|
+
import { FloatingDragDirective } from './visualizer/floating-drag.directive';
|
|
44
|
+
import { IntensityInsetComponent } from './intensity-inset/intensity-inset.component';
|
|
45
|
+
import { SpatialDatasetBinder } from './visualizer/spatial-dataset-binder';
|
|
46
|
+
import { ToolbarComponent } from './toolbar/toolbar.component';
|
|
47
|
+
import { RegionEditorComponent } from './region-editor/region-editor.component';
|
|
48
|
+
import { ChannelHistogramComponent } from './channel-histogram/channel-histogram.component';
|
|
49
|
+
import { SpatialControlsComponent } from './spatial-controls/spatial-controls.component';
|
|
26
50
|
import * as i0 from "@angular/core";
|
|
27
51
|
import * as i1 from "primeng/api";
|
|
28
52
|
import * as i2 from "./store/visualizer-store.service";
|
|
@@ -33,24 +57,31 @@ import * as i6 from "./region-ops.service";
|
|
|
33
57
|
import * as i7 from "@angular/common";
|
|
34
58
|
import * as i8 from "@angular/forms";
|
|
35
59
|
import * as i9 from "primeng/button";
|
|
36
|
-
import * as i10 from "primeng/
|
|
37
|
-
import * as i11 from "primeng/
|
|
38
|
-
import * as i12 from "primeng/
|
|
39
|
-
import * as i13 from "primeng/
|
|
40
|
-
import * as i14 from "primeng/
|
|
41
|
-
import * as i15 from "primeng/
|
|
60
|
+
import * as i10 from "primeng/checkbox";
|
|
61
|
+
import * as i11 from "primeng/contextmenu";
|
|
62
|
+
import * as i12 from "primeng/tooltip";
|
|
63
|
+
import * as i13 from "primeng/dialog";
|
|
64
|
+
import * as i14 from "primeng/dropdown";
|
|
65
|
+
import * as i15 from "primeng/inputnumber";
|
|
42
66
|
import * as i16 from "primeng/progressbar";
|
|
43
|
-
import * as i17 from "primeng/
|
|
44
|
-
import * as i18 from "primeng/
|
|
45
|
-
import * as i19 from "./toolbar/toolbar.component";
|
|
46
|
-
import * as i20 from "./region-editor/region-editor.component";
|
|
47
|
-
import * as i21 from "./channel-histogram/channel-histogram.component";
|
|
48
|
-
import * as i22 from "./spatial-controls/spatial-controls.component";
|
|
67
|
+
import * as i17 from "primeng/slider";
|
|
68
|
+
import * as i18 from "primeng/toast";
|
|
49
69
|
/** Per-instance plot-div id source. The mount element's id must be unique so two
|
|
50
70
|
* live viewers (e.g. the main diagram + a modal preview) don't collide on the
|
|
51
71
|
* same DOM id — `getElementById` would otherwise return whichever came first.
|
|
52
72
|
* Styling hangs off the `.viz-plot` class instead of the id. */
|
|
53
73
|
let plotInstanceSeq = 0;
|
|
74
|
+
/**
|
|
75
|
+
* The viewer (`<visualizer>`): the plot surface, its toolbar, the loading overlay and
|
|
76
|
+
* the dialogs it opens (Channels & Histogram, spatial-omics controls, Region Editor,
|
|
77
|
+
* contributed modes and tools). It orchestrates rendering through the `VISUALIZER`
|
|
78
|
+
* contract and reads the image from the host's `IMAGE_STATE_PORT`.
|
|
79
|
+
*
|
|
80
|
+
* OnPush. Template events re-render it; state that lands asynchronously goes through
|
|
81
|
+
* {@link changed} (or a signal: the loading overlay), and the collaborators that must
|
|
82
|
+
* render synchronously (the render session's overlay, a contributed panel's slot, the
|
|
83
|
+
* context menu before it opens) still call `detectChanges()`.
|
|
84
|
+
*/
|
|
54
85
|
export class VisualizerComponent {
|
|
55
86
|
/** Per-instance toast key. MessageService is a global singleton, so two live
|
|
56
87
|
* `<visualizer>` instances (main viewer + pipeline-dialog preview) sharing
|
|
@@ -91,11 +122,45 @@ export class VisualizerComponent {
|
|
|
91
122
|
get toolbarTools() {
|
|
92
123
|
return this._toolbarTools;
|
|
93
124
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
125
|
+
get activeDragMode() {
|
|
126
|
+
return this.tools.active;
|
|
127
|
+
}
|
|
128
|
+
set activeDragMode(mode) {
|
|
129
|
+
this.tools.active = mode;
|
|
130
|
+
}
|
|
131
|
+
get wandSensitivity() {
|
|
132
|
+
return this.tools.wandSensitivity;
|
|
133
|
+
}
|
|
134
|
+
set wandSensitivity(v) {
|
|
135
|
+
this.tools.wandSensitivity = v;
|
|
136
|
+
}
|
|
137
|
+
get brushSize() {
|
|
138
|
+
return this.tools.brushSize;
|
|
139
|
+
}
|
|
140
|
+
get vertexEraserRadius() {
|
|
141
|
+
return this.tools.vertexEraserRadius;
|
|
142
|
+
}
|
|
143
|
+
set vertexEraserRadius(v) {
|
|
144
|
+
this.tools.vertexEraserRadius = v;
|
|
145
|
+
}
|
|
146
|
+
/** Custom-threshold Simplify dialog visibility (see {@link RegionActions}). */
|
|
147
|
+
get displaySimplifyDialog() {
|
|
148
|
+
return this.regionActions.displaySimplifyDialog;
|
|
149
|
+
}
|
|
150
|
+
set displaySimplifyDialog(v) {
|
|
151
|
+
this.regionActions.displaySimplifyDialog = v;
|
|
152
|
+
}
|
|
153
|
+
/** Dialog tools registered through {@link TOOLBAR_TOOLS} (`kind: 'dialog'`). */
|
|
154
|
+
get dialogTools() {
|
|
155
|
+
return this.contributions.dialogTools;
|
|
156
|
+
}
|
|
157
|
+
/** The dialog tool the user has open (its session may be between renders). */
|
|
158
|
+
get openDialogToolId() {
|
|
159
|
+
return this.contributions.openDialogToolId;
|
|
160
|
+
}
|
|
161
|
+
/** The open dialog tool's live dialog, as the template renders it. */
|
|
162
|
+
get toolDialog() {
|
|
163
|
+
return this.contributions.toolDialog;
|
|
99
164
|
}
|
|
100
165
|
/**
|
|
101
166
|
* The built-in plot type being rendered. With no contributed mode active this
|
|
@@ -109,11 +174,17 @@ export class VisualizerComponent {
|
|
|
109
174
|
set selectedPlotType(type) {
|
|
110
175
|
this.selectedPlotTypeId = type;
|
|
111
176
|
}
|
|
177
|
+
/** Contributed plot modes and their single live session. */
|
|
178
|
+
get plotModes() {
|
|
179
|
+
return this.contributions.plotModes;
|
|
180
|
+
}
|
|
181
|
+
/** The live contributed mode's side panel; null while no contributed session is live. */
|
|
182
|
+
get plotModePanel() {
|
|
183
|
+
return this.contributions.plotModePanel;
|
|
184
|
+
}
|
|
112
185
|
/** Where a `mount` panel's host element is attached once the dialog renders. */
|
|
113
186
|
set plotModePanelSlot(ref) {
|
|
114
|
-
|
|
115
|
-
if (ref && host && host.parentElement !== ref.nativeElement)
|
|
116
|
-
ref.nativeElement.appendChild(host);
|
|
187
|
+
this.contributions.attachPanelSlot(ref?.nativeElement);
|
|
117
188
|
}
|
|
118
189
|
/** The built-in type actually rendered for {@link selectedPlotTypeId}: itself
|
|
119
190
|
* for a built-in type, `baseType` for a contributed mode. */
|
|
@@ -126,17 +197,20 @@ export class VisualizerComponent {
|
|
|
126
197
|
get isSpatial3dMode() {
|
|
127
198
|
return isSpatialOmics3d(this.basePlotType);
|
|
128
199
|
}
|
|
129
|
-
/** LINE plot type shows the image + draggable line ROI + intensity inset. */
|
|
130
|
-
get isProfileMode() {
|
|
131
|
-
return this.basePlotType === PlotType.LINE;
|
|
132
|
-
}
|
|
133
200
|
/** True for the Image plot type, which renders as a natively pan/zoom-able
|
|
134
201
|
* raster — so the backend-agnostic zoom/pan toolbar tools are hidden. The
|
|
135
202
|
* component drives this off the plot type, not the active backend. */
|
|
136
203
|
get isImageView() {
|
|
137
204
|
return this.basePlotType === PlotType.IMAGE;
|
|
138
205
|
}
|
|
139
|
-
|
|
206
|
+
/** Whether a spatial-omics dataset is published on `SPATIAL_DATA_PORT` — gates the
|
|
207
|
+
* spatial plot types; the toolbar offers the plot modes for an image-less one too. */
|
|
208
|
+
get hasSpatialDataset() {
|
|
209
|
+
return this.spatial.hasDataset;
|
|
210
|
+
}
|
|
211
|
+
constructor(
|
|
212
|
+
// The constructor parameters below are the injection points; none is public API.
|
|
213
|
+
state, plotService, messageService, ngZone, cdr, session, samTool, cellSegmentTool, samPointTool, regionOps, vizConfig,
|
|
140
214
|
// A constructor parameter rather than a field `inject()`: this component is
|
|
141
215
|
// also constructed directly with `new` in its own specs, which is outside
|
|
142
216
|
// an injection context and would throw NG0203 on a field initializer.
|
|
@@ -149,7 +223,12 @@ export class VisualizerComponent {
|
|
|
149
223
|
plotTypeContributions,
|
|
150
224
|
// Parent of a contributed panel component's injector. Optional only so the
|
|
151
225
|
// specs can construct this component with `new`.
|
|
152
|
-
injector
|
|
226
|
+
injector,
|
|
227
|
+
// The `<visualizer>` element: keyboard shortcuts are scoped to it (CORE-2).
|
|
228
|
+
hostRef,
|
|
229
|
+
// Ends the subscriptions (`takeUntilDestroyed`). A parameter, like the rest, so the
|
|
230
|
+
// specs' `new` can pass one; Angular always provides it.
|
|
231
|
+
destroyRef = undefined) {
|
|
153
232
|
this.state = state;
|
|
154
233
|
this.plotService = plotService;
|
|
155
234
|
this.messageService = messageService;
|
|
@@ -163,9 +242,13 @@ export class VisualizerComponent {
|
|
|
163
242
|
this.vizConfig = vizConfig;
|
|
164
243
|
this.spatialData = spatialData;
|
|
165
244
|
this.injector = injector;
|
|
245
|
+
this.hostRef = hostRef;
|
|
246
|
+
this.destroyRef = destroyRef;
|
|
166
247
|
this.vizToastKey = VIZ_TOAST_KEY;
|
|
167
248
|
this.vizAlertToastKey = VIZ_ALERT_TOAST_KEY;
|
|
249
|
+
/** Each new image: whether it is a z-stack. */
|
|
168
250
|
this.isStackEvent = new EventEmitter(false);
|
|
251
|
+
/** Each new image: whether it is single-channel (grayscale). */
|
|
169
252
|
this.isGrayscaleEvent = new EventEmitter(false);
|
|
170
253
|
this._toolbarTools = ALL_TOOLBAR_TOOLS;
|
|
171
254
|
/** Expose test-only plot modes in the selector (e.g. the napari `Image`
|
|
@@ -175,55 +258,43 @@ export class VisualizerComponent {
|
|
|
175
258
|
/** Image-smoothing state for the toolbar's Smoothen toggle (OSD only).
|
|
176
259
|
* Defaults to `false` so OSD shows raw pixels (nearest-neighbour). */
|
|
177
260
|
this.imageSmoothingEnabled = false;
|
|
178
|
-
|
|
179
|
-
this.
|
|
180
|
-
this.
|
|
181
|
-
|
|
182
|
-
this.
|
|
183
|
-
this.
|
|
184
|
-
this.
|
|
261
|
+
// ── The loading overlay, as signals: it is fed by the host's and the backends' streams
|
|
262
|
+
// and by the render session, all of which land outside this view's template events. ──
|
|
263
|
+
this.loadingMessage = signal('Loading image...');
|
|
264
|
+
/** The host is loading at zoom (the overlay message moves out of the way). */
|
|
265
|
+
this.zoom = signal(false);
|
|
266
|
+
this.stackLoading = signal(false);
|
|
267
|
+
this.imgLoading = signal(false);
|
|
268
|
+
this.loadingPercentage = signal(0);
|
|
185
269
|
// Non-null (0..100) while jit-service is copying the source file into the
|
|
186
270
|
// local cache PVC on first click. When set, the loading overlay swaps the
|
|
187
271
|
// spinner for a determinate progress bar with a "Caching image" message.
|
|
188
|
-
this.cacheProgress = null;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
this.viewReady = false;
|
|
204
|
-
/** An image-less dataset whose draw waits for the view: see plotSpatialWithoutImage. */
|
|
205
|
-
this.pendingSpatialDraw = null;
|
|
272
|
+
this.cacheProgress = signal(null);
|
|
273
|
+
/** A jit-service cache copy is actively in progress (determinate 0..99). Drives the cache
|
|
274
|
+
* progress bar independently of `imgLoading` so the bar can't be dropped — or masked by the
|
|
275
|
+
* "Sharpening preview..." overlay — mid-download if the loading flag flips early. */
|
|
276
|
+
this.isCaching = computed(() => {
|
|
277
|
+
const p = this.cacheProgress();
|
|
278
|
+
return p !== null && p < 100;
|
|
279
|
+
});
|
|
280
|
+
/** Emits once when this view is destroyed; the collaborators end their subscriptions on it. */
|
|
281
|
+
this.destroyed$ = new Observable((subscriber) => this.destroyRef.onDestroy(() => {
|
|
282
|
+
subscriber.next();
|
|
283
|
+
subscriber.complete();
|
|
284
|
+
}));
|
|
285
|
+
/** Host image-info objects whose one-shot `initialZIndex` was already applied. */
|
|
286
|
+
this.consumedSliceHints = new WeakSet();
|
|
206
287
|
this.zIndex = 0;
|
|
207
288
|
this.maxIndex = 0;
|
|
208
289
|
this.contextMenuItems = [];
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
this.
|
|
215
|
-
/**
|
|
216
|
-
this.
|
|
217
|
-
this.simplifyThreshold = 3;
|
|
218
|
-
/** Current region selection (array indices) — mirrored from the store so the
|
|
219
|
-
* context-menu region actions can read it synchronously (jit-ui#85). */
|
|
220
|
-
this.selectedIndices = [];
|
|
221
|
-
/** Wand sensitivity — higher = stricter (smaller selection). Matches QuPath default. */
|
|
222
|
-
this.wandSensitivity = 2.0;
|
|
223
|
-
/** Brush diameter in image-pixel coordinates (drives the painted disc size). */
|
|
224
|
-
this.brushSize = 40;
|
|
225
|
-
/** Class the brush paints while a plot mode armed it (PlotModeTools.armBrush); null = plain brush. */
|
|
226
|
-
this.brushClass = null;
|
|
290
|
+
/** The armed tool mode and the toolbar's tool settings. */
|
|
291
|
+
this.tools = new ToolModes(this.plotService, this.session, this.ngZone, (mode) => {
|
|
292
|
+
if (mode !== 'samPoint')
|
|
293
|
+
this.segmentation.hide();
|
|
294
|
+
}, // leaving point mode drops its toast
|
|
295
|
+
() => this.changed());
|
|
296
|
+
/** Region set-operations on the selection, and the store mirrors they read (jit-ui#85). */
|
|
297
|
+
this.regionActions = new RegionActions(this.plotService, this.regionOps, () => this.imageInfo?.trueImageSize, (m) => this.messageService.add({ key: this.resultToastKey, ...m }));
|
|
227
298
|
/** SAM model picker options + current selection (jit-ui#90 P1). Only models
|
|
228
299
|
* with a hosted ONNX pair (configured via setSamModelUrls at app init) are
|
|
229
300
|
* offered, so the picker can't select a model that can't run. */
|
|
@@ -236,58 +307,16 @@ export class VisualizerComponent {
|
|
|
236
307
|
* and help dialog simply omit that group.
|
|
237
308
|
*/
|
|
238
309
|
this.contributedTools = [];
|
|
239
|
-
/**
|
|
240
|
-
* Dialog tools registered through {@link TOOLBAR_TOOLS} (`kind: 'dialog'`),
|
|
241
|
-
* sorted. Their buttons sit with the host's own actions and show in the Image
|
|
242
|
-
* view only; each opens a floating dialog the tool fills.
|
|
243
|
-
*/
|
|
244
|
-
this.dialogTools = [];
|
|
245
|
-
/** The dialog tool the user has open (its session may be between renders). */
|
|
246
|
-
this.openDialogToolId = null;
|
|
247
|
-
/** The open dialog tool's live dialog, as the template renders it. */
|
|
248
|
-
this.toolDialog = null;
|
|
249
|
-
/** Active checkpoint per contributed tool. */
|
|
250
|
-
this.toolModelIds = {};
|
|
251
|
-
/**
|
|
252
|
-
* Parameter values per contributed tool, seeded from each tool's own defaults
|
|
253
|
-
* — which encode the scale and crowding its checkpoint was trained for.
|
|
254
|
-
*/
|
|
255
|
-
this.toolParams = {};
|
|
256
|
-
/** Which tool's parameter dialog is open, or null. */
|
|
257
|
-
this.openParamsToolId = null;
|
|
258
|
-
/** SAM download/segment toast state (bound by the `sam` p-toast template). */
|
|
259
|
-
this.samStatus = '';
|
|
260
|
-
this.samProgress = 0; // 0..100, encoder download
|
|
261
|
-
this.samDownloading = false;
|
|
262
|
-
this.samBusy = false; // any SAM work in flight (drives the indeterminate spinner)
|
|
263
|
-
/** Whether the shared `sam` toast is currently shown (avoids stacking it on
|
|
264
|
-
* every point click, which re-runs inference). */
|
|
265
|
-
this.samToastShown = false;
|
|
266
310
|
this.samToastKey = `sam-${VisualizerComponent.nextToastId++}`;
|
|
267
311
|
/**
|
|
268
|
-
* Outlet for the library's own result/error notices
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* to be emitted with no key, so they rendered only in hosts that happen to
|
|
273
|
-
* mount a bare `<p-toast>` (jit-ui does, in its file-tree component). Every
|
|
274
|
-
* other host — including this repo's own browser example — dropped them
|
|
275
|
-
* silently, which made a failed or no-op segmentation look like a dead button:
|
|
276
|
-
* the sticky progress toast is cleared in the same tick by hideSamToast, and
|
|
277
|
-
* the message explaining why never appeared anywhere.
|
|
278
|
-
*
|
|
279
|
-
* Keyed to this component instance so the library renders its own outlet and
|
|
280
|
-
* is not dependent on host markup. Deliberately NOT the sticky toast's key:
|
|
281
|
-
* {@link hideSamToast} clears that key in the `finally`, which would wipe the
|
|
282
|
-
* result the moment it was posted, and the sticky toast's custom template is
|
|
283
|
-
* built for the live status + progress bar.
|
|
312
|
+
* Outlet for the library's own result/error notices, keyed to this instance so they
|
|
313
|
+
* do not depend on the host mounting a keyless `<p-toast>` (most hosts don't, and a
|
|
314
|
+
* failed segmentation then looked like a dead button). Deliberately NOT the sticky
|
|
315
|
+
* toast's key: that is cleared when a run settles, which would wipe the result.
|
|
284
316
|
*/
|
|
285
317
|
this.resultToastKey = `${this.samToastKey}-result`;
|
|
286
|
-
/**
|
|
287
|
-
this.
|
|
288
|
-
/** Vertex eraser radius in image-pixel coordinates. */
|
|
289
|
-
this.vertexEraserRadius = 20;
|
|
290
|
-
this.reversescale = false;
|
|
318
|
+
/** Segmentation runs and their sticky progress toast. */
|
|
319
|
+
this.segmentation = new SegmentationRunner(this.messageService, this.samToastKey, this.resultToastKey, () => this.changed());
|
|
291
320
|
/** Channels & Histogram dialog visibility (opened from the toolbar). */
|
|
292
321
|
this.showChannelHistogram = false;
|
|
293
322
|
/** Spatial-omics controls dialog visibility (toolbar button). */
|
|
@@ -298,12 +327,30 @@ export class VisualizerComponent {
|
|
|
298
327
|
* element's current width, or — when no selector is configured / the element
|
|
299
328
|
* isn't found — to a quarter of the page (see openRegionEditor). */
|
|
300
329
|
this.regionEditorWidth = '25vw';
|
|
301
|
-
this.stackOptions = [
|
|
302
|
-
{ name: 'Single image', val: 'false' },
|
|
303
|
-
{ name: 'Stack', val: 'true' },
|
|
304
|
-
];
|
|
305
|
-
this.selectedStackOption = this.stackOptions[0];
|
|
306
330
|
this.plotDivName = `viz-plot-${plotInstanceSeq++}`;
|
|
331
|
+
/** The render pipeline: the newest image (or image-less dataset) always wins. */
|
|
332
|
+
this.render = new ImageRenderSession({
|
|
333
|
+
plotDivName: this.plotDivName,
|
|
334
|
+
visualizer: this.plotService,
|
|
335
|
+
zIndex: () => this.zIndex,
|
|
336
|
+
plotType: () => this.plotType,
|
|
337
|
+
isHeatmap: () => this.isHeatmap,
|
|
338
|
+
isCaching: () => this.isCaching(),
|
|
339
|
+
beforeReset: () => this.contributions.endSessions(),
|
|
340
|
+
prepare: (info) => this.layOutSlices(info),
|
|
341
|
+
releaseOverlay: (info) => {
|
|
342
|
+
if (info.isStack && info.showStack)
|
|
343
|
+
this.stackLoading.set(false);
|
|
344
|
+
this.state.setImageLoading(false);
|
|
345
|
+
},
|
|
346
|
+
landed: (info) => {
|
|
347
|
+
applyImageRois(info, this.plotService, this.zIndex);
|
|
348
|
+
this.contributions.activate();
|
|
349
|
+
},
|
|
350
|
+
setImageLoading: (loading) => this.state.setImageLoading(loading),
|
|
351
|
+
alert: (severity, summary, detail) => this.messageService.add({ key: this.vizAlertToastKey, severity, summary, detail }),
|
|
352
|
+
detectChanges: () => this.cdr.detectChanges(),
|
|
353
|
+
});
|
|
307
354
|
this.plotType = PlotType.IMAGE;
|
|
308
355
|
this.isHeatmap = true;
|
|
309
356
|
this.activeSurface3dMode = 'turntable';
|
|
@@ -327,52 +374,26 @@ export class VisualizerComponent {
|
|
|
327
374
|
* contributed mode behaves exactly like the built-in type it rides on.
|
|
328
375
|
*/
|
|
329
376
|
this.selectedPlotTypeId = PlotType.IMAGE;
|
|
330
|
-
/**
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
|
|
334
|
-
*/
|
|
335
|
-
this.plotModePanel = null;
|
|
336
|
-
this.destroying = false;
|
|
337
|
-
/** Floating intensity-profile inset (LINE mode). */
|
|
338
|
-
this.intensityInsetDiv = 'intensity-inset-plot';
|
|
339
|
-
this.profilePanelPos = { x: 20, y: 70 };
|
|
340
|
-
this.profilePanelDragging = false;
|
|
341
|
-
this.profilePanelStart = { mx: 0, my: 0, x: 0, y: 0 };
|
|
377
|
+
/** Div the floating intensity-profile inset is charted into. Per instance, like
|
|
378
|
+
* {@link plotDivName}: the backend resolves it by id, so a fixed id made two live
|
|
379
|
+
* viewers draw both insets into whichever div came first in the document. */
|
|
380
|
+
this.intensityInsetDiv = `${this.plotDivName}-inset`;
|
|
342
381
|
/** Toolbar docking: docked across the top by default; dragging its handle
|
|
343
382
|
* detaches it into a floating, movable window (frees the top row for the
|
|
344
383
|
* visualization). */
|
|
345
384
|
this.toolbarFloating = false;
|
|
346
385
|
this.toolbarPos = { x: 8, y: 8 };
|
|
347
|
-
|
|
348
|
-
this.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
386
|
+
/** Where a toolbar-handle drag starts from: grabbing the docked toolbar floats it. */
|
|
387
|
+
this.toolbarDragOrigin = () => {
|
|
388
|
+
if (!this.toolbarFloating) {
|
|
389
|
+
this.toolbarFloating = true;
|
|
390
|
+
this.toolbarPos = { x: 8, y: 8 };
|
|
391
|
+
}
|
|
392
|
+
return this.toolbarPos;
|
|
393
|
+
};
|
|
353
394
|
/** Isosurface band as a 0–255 slider position, mapped onto the volume's real
|
|
354
395
|
* intensity range by the renderer. Defaults to the full range. */
|
|
355
396
|
this.isoRange = [0, 255];
|
|
356
|
-
/** Whether a spatial-omics dataset is currently published on
|
|
357
|
-
* `SPATIAL_DATA_PORT` — gates the spatial plot types in the selector. */
|
|
358
|
-
/** Bound by the toolbar, which offers the plot modes for a dataset with no image too. */
|
|
359
|
-
this.hasSpatialDataset = false;
|
|
360
|
-
/** The spatial dataset on offer, for drawing one that brings no image (see reloadAndPlot). */
|
|
361
|
-
this.spatialDataset = null;
|
|
362
|
-
/** Whether that dataset's observations carry a z, gating the 3D spatial mode. */
|
|
363
|
-
this.hasSpatial3dDataset = false;
|
|
364
|
-
/** Whether it carries a registered volume. Change detection only: a volume
|
|
365
|
-
* appearing is what publishes it as the image, and the plot-type gates then
|
|
366
|
-
* see an ordinary grayscale stack. */
|
|
367
|
-
this.hasSpatialVolume = false;
|
|
368
|
-
/** Dataset identity + capability shape the last emission was handled at, so a
|
|
369
|
-
* switch between two datasets of the same shape is not mistaken for a repeat. */
|
|
370
|
-
this.spatialDatasetKey = null;
|
|
371
|
-
/** Dataset + geometry the currently published volume image was built from, so a
|
|
372
|
-
* re-emitted dataset doesn't re-fetch megabytes or reset the user's scrub. */
|
|
373
|
-
this.volumeImageKey = null;
|
|
374
|
-
/** Blob URLs backing that image — ours to revoke. */
|
|
375
|
-
this.volumeImageUrls = [];
|
|
376
397
|
/** Debounced z-slice scrubbing (see SliceScrubber — refactoring plan Step 7). */
|
|
377
398
|
this.scrubber = new SliceScrubber((z) => {
|
|
378
399
|
this.plotService.setZIndex(z);
|
|
@@ -381,296 +402,65 @@ export class VisualizerComponent {
|
|
|
381
402
|
// just records the slice, leaving single-plane regions untouched (jit-ui#93).
|
|
382
403
|
this.plotService.setDisplaySlice(z);
|
|
383
404
|
});
|
|
384
|
-
/**
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
/** The live dialog body's teardown, and a pending wait for its host to connect. */
|
|
391
|
-
this.toolDialogTeardown = null;
|
|
392
|
-
this.toolDialogFrame = null;
|
|
393
|
-
/** Toolbar tools for contributed modes. Calls can come from outside Angular, hence the zone. */
|
|
394
|
-
this.plotModeTools = {
|
|
395
|
-
armBrush: (brushClass) => this.ngZone.run(() => {
|
|
396
|
-
this.brushClass = { label: brushClass?.label, color: brushClass?.color };
|
|
397
|
-
if (this.activeDragMode === 'brush') {
|
|
398
|
-
this.plotService.setBrushOptions({ size: this.brushSize, ...this.brushClass });
|
|
399
|
-
}
|
|
400
|
-
else {
|
|
401
|
-
this.applyDragMode('brush');
|
|
402
|
-
}
|
|
403
|
-
this.cdr.markForCheck();
|
|
404
|
-
}),
|
|
405
|
-
disarm: () => this.ngZone.run(() => {
|
|
406
|
-
this.brushClass = null;
|
|
407
|
-
if (this.activeDragMode !== null)
|
|
408
|
-
this.applyDragMode(null);
|
|
409
|
-
this.cdr.markForCheck();
|
|
410
|
-
}),
|
|
411
|
-
activeTool$: this.session.getActiveTool$(),
|
|
405
|
+
/** What the host gets through `setDiagram` — see {@link VisualizerHandle}. */
|
|
406
|
+
this.hostHandle = {
|
|
407
|
+
visualizer: this.plotService,
|
|
408
|
+
plotService: this.plotService,
|
|
409
|
+
hasRegions: () => this.hasRegions(),
|
|
410
|
+
getRegionPolygons: () => this.getRegionPolygons(),
|
|
412
411
|
};
|
|
413
|
-
this.plotModes = new PlotModeController(plotTypeContributions, {
|
|
414
|
-
onActivated: (active) => this.showPlotModePanel(active),
|
|
415
|
-
onDeactivating: () => this.hidePlotModePanel(),
|
|
416
|
-
onFailed: (contribution) => this.fallBackFromPlotMode(contribution),
|
|
417
|
-
});
|
|
418
412
|
this.contributedTools = visibleToolContributions(toolContributions);
|
|
419
|
-
this.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
413
|
+
this.toolParams = new ToolParamsModel(this.contributedTools);
|
|
414
|
+
this.contributions = new ContributionHost(plotTypeContributions, toolContributions, {
|
|
415
|
+
visualizer: this.plotService,
|
|
416
|
+
zone: this.ngZone,
|
|
417
|
+
tools: this.tools.plotModeTools,
|
|
418
|
+
injector: this.injector,
|
|
419
|
+
imageInfo$: () => this.state.getImageInfo$(),
|
|
420
|
+
selectedId: () => this.selectedPlotTypeId,
|
|
421
|
+
renderedType: () => this.plotType,
|
|
422
|
+
selectBase: (base) => {
|
|
423
|
+
this.selectedPlotTypeId = base;
|
|
424
|
+
this.plotType = base;
|
|
425
|
+
},
|
|
426
|
+
notify: (m) => this.messageService.add({ key: this.resultToastKey, ...m }),
|
|
427
|
+
detectChanges: () => this.cdr.detectChanges(),
|
|
428
|
+
});
|
|
429
|
+
this.spatial = new SpatialDatasetBinder(this.spatialData, {
|
|
430
|
+
gatesChanged: () => this.computePlotTypeOptions(),
|
|
431
|
+
selectedId: () => this.selectedPlotTypeId,
|
|
432
|
+
basePlotType: () => this.basePlotType,
|
|
433
|
+
hasImage: () => !!this.imageInfo,
|
|
434
|
+
selectPlotType: (type) => this.onSelectPlotType(type),
|
|
435
|
+
replot: () => this.reloadAndPlot(),
|
|
436
|
+
publishImage: (info) => this.state.setImageInfo(info),
|
|
437
|
+
setImageLoading: (loading) => this.state.setImageLoading(loading),
|
|
438
|
+
settled: () => {
|
|
439
|
+
this.reconcileSelectedPlotType();
|
|
440
|
+
this.changed();
|
|
441
|
+
},
|
|
442
|
+
detectChanges: () => this.changed(),
|
|
432
443
|
});
|
|
433
|
-
this.colormapsOptions = plotService.getColormapOptions();
|
|
434
444
|
this.computePlotTypeOptions();
|
|
435
445
|
}
|
|
436
|
-
/**
|
|
437
|
-
*
|
|
438
|
-
* - Outside **test mode**, only the curated default set (descriptors with a
|
|
439
|
-
* `productionLabel`) is offered, shown under suffix-free names — Image,
|
|
440
|
-
* Heatmap, Contour and the napari Surface / Volume / Isosurface. Test mode
|
|
441
|
-
* exposes every backend's type under its full (backend-suffixed) label.
|
|
442
|
-
* - 3D types hidden when the backend can't render a 3D scene.
|
|
443
|
-
* - stack-only types (volume, isosurface) hidden unless the file is a stack —
|
|
444
|
-
* a volume needs multiple z-slices.
|
|
445
|
-
* - scalar-intensity types (contour, surface, isosurface) hidden for RGB
|
|
446
|
-
* images — they map a single intensity per pixel. Image and Heatmap render
|
|
447
|
-
* any image.
|
|
448
|
-
* - spatial-omics types hidden until a `SpatialDataset` is published on
|
|
449
|
-
* `SPATIAL_DATA_PORT` — the mode has nothing to draw without observations,
|
|
450
|
-
* exactly as a volume has nothing to draw without a stack.
|
|
451
|
-
*/
|
|
452
|
-
/**
|
|
453
|
-
* Track whether a spatial-omics dataset is available. A dataset appearing (or
|
|
454
|
-
* being cleared) changes which plot types make sense, so it drives the same
|
|
455
|
-
* recompute + reconcile that the image stream and the test-mode toggle do.
|
|
456
|
-
*
|
|
457
|
-
* No-op when the host provides no `SPATIAL_DATA_PORT` — the spatial types then
|
|
458
|
-
* stay hidden for the life of the component.
|
|
459
|
-
*/
|
|
446
|
+
/** Follow the spatial-omics dataset: a dataset appearing, switching or being cleared
|
|
447
|
+
* re-gates the selector exactly as the image stream and the test-mode toggle do. */
|
|
460
448
|
watchSpatialDataset() {
|
|
461
|
-
this.
|
|
462
|
-
this.spatialDataset = dataset ?? null;
|
|
463
|
-
const has = !!dataset;
|
|
464
|
-
// Only a dataset whose observations carry a z can be drawn as a cloud, so
|
|
465
|
-
// the 3D mode is gated on the coordinates, not merely on a dataset being
|
|
466
|
-
// present. Most spatial assays are one plane.
|
|
467
|
-
const has3d = !!dataset?.observations.z;
|
|
468
|
-
const hasVolume = !!dataset?.volume;
|
|
469
|
-
// The port publishes its current value on subscribe, so the initial `null`
|
|
470
|
-
// would otherwise recompute the selector for no change. Keyed by dataset
|
|
471
|
-
// IDENTITY as well as capability shape: two 3D datasets that both carry a
|
|
472
|
-
// volume have the same shape, and comparing only that skipped the switch —
|
|
473
|
-
// leaving the previous dataset's volume image on screen underneath the new
|
|
474
|
-
// one's observations.
|
|
475
|
-
const key = dataset
|
|
476
|
-
? `${dataset.id}|${has3d}|${hasVolume}|${dataset.volume
|
|
477
|
-
? `${dataset.volume.width}x${dataset.volume.height}x${dataset.volume.depth}` : ''}`
|
|
478
|
-
: null;
|
|
479
|
-
if (key === this.spatialDatasetKey)
|
|
480
|
-
return;
|
|
481
|
-
this.spatialDatasetKey = key;
|
|
482
|
-
this.hasSpatialDataset = has;
|
|
483
|
-
this.hasSpatial3dDataset = has3d;
|
|
484
|
-
// Whether the dataset says it registers onto a tissue image. Distinct from an
|
|
485
|
-
// image being LOADED: a registered dataset's image may still be on its way, and
|
|
486
|
-
// the pixel modes must not flicker out of the selector while it arrives.
|
|
487
|
-
// A registered dataset draws over a tissue image; a volume-backed one publishes its
|
|
488
|
-
// volume AS a grayscale z-stack image (`buildVolumeStackImage`). Either way pixels
|
|
489
|
-
// exist, so the pixel modes stay on offer — Volume and Isosurface are exactly how
|
|
490
|
-
// a 3D omics dataset is read.
|
|
491
|
-
this.spatialDatasetHasPixels = !!dataset?.imageRef || hasVolume;
|
|
492
|
-
this.hasSpatialVolume = hasVolume;
|
|
493
|
-
this.computePlotTypeOptions();
|
|
494
|
-
// A dataset with no reference image has nothing to draw observations OVER:
|
|
495
|
-
// a cloud registered into a common frame (the Allen CCF) has coordinates
|
|
496
|
-
// but no one section. What it does have is its registered VOLUME, which is
|
|
497
|
-
// a 3D image in one file — so make that the image and open on it, slice bar
|
|
498
|
-
// and all, rather than leaving the host on an Image view showing whatever
|
|
499
|
-
// slide was loaded before. Ordered after computePlotTypeOptions so the type
|
|
500
|
-
// is on offer before it is selected.
|
|
501
|
-
if (dataset && !dataset.imageRef && hasVolume) {
|
|
502
|
-
void this.showVolumeAsImage(dataset);
|
|
503
|
-
}
|
|
504
|
-
else {
|
|
505
|
-
// Anything else — a dataset with its own section image, a volume-less
|
|
506
|
-
// cloud, or none at all — means a published volume image is no longer what
|
|
507
|
-
// is on screen. Forget it, or coming BACK to the volume dataset would
|
|
508
|
-
// short-circuit on a matching key and leave the other dataset's slide up.
|
|
509
|
-
this.dropVolumeImage();
|
|
510
|
-
if (dataset && !dataset.imageRef) {
|
|
511
|
-
// No reference image and no volume to slice: the observations are the only
|
|
512
|
-
// thing there is to draw, so open on whichever spatial mode their
|
|
513
|
-
// coordinates support. Leaving the type alone strands the host on an Image
|
|
514
|
-
// view showing whatever slide was loaded BEFORE — the observations never
|
|
515
|
-
// appear, and the previous dataset's tissue does, which reads as this
|
|
516
|
-
// dataset failing to load.
|
|
517
|
-
//
|
|
518
|
-
// Gated on the coordinates, not on the dataset merely existing: a
|
|
519
|
-
// one-plane assay has no z and cannot be a cloud. That case only turned up
|
|
520
|
-
// with an image-less 2D dataset, which is why it went unhandled — before
|
|
521
|
-
// it, image-less meant 3D.
|
|
522
|
-
const target = has3d ? PlotType.SPATIAL_OMICS_3D : PlotType.SPATIAL_OMICS;
|
|
523
|
-
if (this.selectedPlotTypeId !== target)
|
|
524
|
-
this.onSelectPlotType(target);
|
|
525
|
-
// Same mode, another image-less dataset, nothing loaded: re-plot so its placeholder
|
|
526
|
-
// image info (and with it the regions' key) is this dataset's, not the last one's.
|
|
527
|
-
else if (!this.imageInfo)
|
|
528
|
-
this.reloadAndPlot();
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
// Clearing the dataset while a spatial mode is active leaves a type that is
|
|
532
|
-
// no longer offered — fall back to Image, as turning test mode off does.
|
|
533
|
-
this.reconcileSelectedPlotType();
|
|
534
|
-
this.cdr.detectChanges();
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
/**
|
|
538
|
-
* Publish a dataset's reference volume AS the image, and open the 2D Image view
|
|
539
|
-
* on it.
|
|
540
|
-
*
|
|
541
|
-
* The volume is a 3D image delivered in one file, so for a dataset that has no
|
|
542
|
-
* section image it is the honest thing to put on screen: the slice bar scrubs z,
|
|
543
|
-
* and the contrast window, colormaps and region tools all work because the
|
|
544
|
-
* volume genuinely is the image now. The 3D cloud stays one menu pick away.
|
|
545
|
-
*
|
|
546
|
-
* Keyed by dataset + geometry: the dataset stream re-emits on things like a
|
|
547
|
-
* colour-column change, and rebuilding then would re-fetch the voxels and throw
|
|
548
|
-
* the user back to the middle slice.
|
|
549
|
-
*/
|
|
550
|
-
async showVolumeAsImage(dataset) {
|
|
551
|
-
const meta = dataset.volume;
|
|
552
|
-
if (!meta || !this.spatialData?.getVolume)
|
|
553
|
-
return;
|
|
554
|
-
const key = `${dataset.id}:${meta.width}x${meta.height}x${meta.depth}`;
|
|
555
|
-
if (key === this.volumeImageKey)
|
|
556
|
-
return;
|
|
557
|
-
// Claim the key BEFORE awaiting: the stream can emit again while the voxels
|
|
558
|
-
// are in flight, and two builds of the same volume would race to publish.
|
|
559
|
-
this.volumeImageKey = key;
|
|
560
|
-
this.state.setImageLoading(true);
|
|
561
|
-
try {
|
|
562
|
-
const built = await buildVolumeStackImage(dataset, await this.spatialData.getVolume());
|
|
563
|
-
// A different dataset was selected while this one encoded — its image is the
|
|
564
|
-
// one that belongs on screen, so drop what we just built.
|
|
565
|
-
if (this.volumeImageKey !== key) {
|
|
566
|
-
built?.urls.forEach((u) => URL.revokeObjectURL(u));
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
if (!built) {
|
|
570
|
-
this.volumeImageKey = null;
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
// Pick the mode first so the image lands in the view that will show it,
|
|
574
|
-
// instead of rendering once into whatever mode the last dataset left active.
|
|
575
|
-
if (this.selectedPlotTypeId !== PlotType.IMAGE)
|
|
576
|
-
this.onSelectPlotType(PlotType.IMAGE);
|
|
577
|
-
this.revokeVolumeImageUrls();
|
|
578
|
-
this.volumeImageUrls = built.urls;
|
|
579
|
-
this.state.setImageInfo(built.info);
|
|
580
|
-
}
|
|
581
|
-
catch (err) {
|
|
582
|
-
// No volume served after all: the cloud is still renderable, so fall back to
|
|
583
|
-
// it rather than leaving the host on an Image view with nothing in it.
|
|
584
|
-
console.warn('[visualizer] reference volume unavailable — falling back to the 3D cloud', err);
|
|
585
|
-
this.volumeImageKey = null;
|
|
586
|
-
if (this.hasSpatial3dDataset && !isSpatialOmics3d(this.basePlotType)) {
|
|
587
|
-
this.onSelectPlotType(PlotType.SPATIAL_OMICS_3D);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
finally {
|
|
591
|
-
this.state.setImageLoading(false);
|
|
592
|
-
this.cdr.detectChanges();
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
/** Forget the published volume image so re-selecting the dataset rebuilds it.
|
|
596
|
-
* The URLs deliberately stay alive: the host may still be displaying that image
|
|
597
|
-
* at this instant, and revoking under it would break every later tile read. They
|
|
598
|
-
* are freed when the next volume replaces them, or on destroy. */
|
|
599
|
-
dropVolumeImage() {
|
|
600
|
-
this.volumeImageKey = null;
|
|
601
|
-
}
|
|
602
|
-
revokeVolumeImageUrls() {
|
|
603
|
-
this.volumeImageUrls.forEach((u) => URL.revokeObjectURL(u));
|
|
604
|
-
this.volumeImageUrls = [];
|
|
449
|
+
this.spatial.bind(this.destroyed$);
|
|
605
450
|
}
|
|
451
|
+
/** Recompute the selector's entries for the current image, dataset and test mode
|
|
452
|
+
* (see {@link computePlotTypeMenu}). */
|
|
606
453
|
computePlotTypeOptions() {
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
return false;
|
|
618
|
-
// Volume and Isosurface raymarch the IMAGE STACK, and nothing else: these
|
|
619
|
-
// two gates are about the loaded image, full stop. A 3D omics dataset
|
|
620
|
-
// reaches them because its registered volume is published AS a grayscale
|
|
621
|
-
// z-stack image (`buildVolumeStackImage`), not through a second voxel
|
|
622
|
-
// source hiding behind the same modes.
|
|
623
|
-
if (d.requiresStack && !isStack)
|
|
624
|
-
return false;
|
|
625
|
-
if (d.requiresGrayscale && !isGrayscale && !isMultichannel)
|
|
626
|
-
return false;
|
|
627
|
-
if (d.requiresSpatialData && !this.hasSpatialDataset)
|
|
628
|
-
return false;
|
|
629
|
-
if (d.requiresSpatial3d && !this.hasSpatial3dDataset)
|
|
630
|
-
return false;
|
|
631
|
-
// An image-sourced mode reads PIXELS. A spatial dataset that brings no tissue
|
|
632
|
-
// image (seqFISH records unitless coordinates, not a section) leaves nothing for
|
|
633
|
-
// one to draw, so offering Image / Heatmap / Surface there offers modes that can
|
|
634
|
-
// only come up blank — or worse, showing whatever slide was loaded before.
|
|
635
|
-
//
|
|
636
|
-
// Narrowed to "a dataset is up AND there is no image": with no dataset at all,
|
|
637
|
-
// Image stays on offer, because a host that has not loaded anything yet needs a
|
|
638
|
-
// default and an empty selector would be worse than a blank view.
|
|
639
|
-
if (d.source === 'image' && this.hasSpatialDataset
|
|
640
|
-
&& !this.spatialDatasetHasPixels)
|
|
641
|
-
return false;
|
|
642
|
-
return true;
|
|
643
|
-
};
|
|
644
|
-
// Outside test mode, only the curated set (those with a productionLabel)
|
|
645
|
-
// is offered; test mode exposes every backend's type.
|
|
646
|
-
const curated = (d) => this.testMode || !!d.productionLabel;
|
|
647
|
-
const builtIn = this.plotService.getPlotTypeDescriptors()
|
|
648
|
-
.filter((d) => curated(d) && passesGates(d));
|
|
649
|
-
// Contributed modes come after every built-in one, under the same label rule
|
|
650
|
-
// and their own stack/grayscale/spatial gates. A mode also needs whatever its base
|
|
651
|
-
// view needs (pixels, a 3D-capable backend), so the base type's gates apply
|
|
652
|
-
// too — a contributed Image-based mode goes wherever Image goes.
|
|
653
|
-
const contributed = [];
|
|
654
|
-
for (const d of this.plotModes.descriptors()) {
|
|
655
|
-
if (!curated(d))
|
|
656
|
-
continue;
|
|
657
|
-
if (d.requiresStack && !isStack)
|
|
658
|
-
continue;
|
|
659
|
-
if (d.requiresGrayscale && !isGrayscale && !isMultichannel)
|
|
660
|
-
continue;
|
|
661
|
-
if (d.requiresSpatialData && !this.hasSpatialDataset)
|
|
662
|
-
continue;
|
|
663
|
-
if (d.requiresSpatial3d && !this.hasSpatial3dDataset)
|
|
664
|
-
continue;
|
|
665
|
-
const base = getPlotTypeDescriptor(d.baseType);
|
|
666
|
-
if (base && passesGates(base))
|
|
667
|
-
contributed.push(contributedPlotTypeOption(d, base));
|
|
668
|
-
}
|
|
669
|
-
// Default selector shows the suffix-free productionLabel; test mode keeps
|
|
670
|
-
// the full backend-suffixed label so same-named modes stay distinguishable.
|
|
671
|
-
const labelled = (d) => (this.testMode ? d : { ...d, label: d.productionLabel });
|
|
672
|
-
this.plotTypeOptions = builtIn.map(labelled);
|
|
673
|
-
this.plotTypeMenu = [...this.plotTypeOptions, ...contributed.map(labelled)];
|
|
454
|
+
const { builtIn, menu } = computePlotTypeMenu({
|
|
455
|
+
descriptors: this.plotService.getPlotTypeDescriptors(),
|
|
456
|
+
contributions: this.plotModes.descriptors(),
|
|
457
|
+
caps: this.plotService.capabilities,
|
|
458
|
+
imageInfo: this.imageInfo,
|
|
459
|
+
spatial: this.spatial,
|
|
460
|
+
testMode: this.testMode,
|
|
461
|
+
});
|
|
462
|
+
this.plotTypeOptions = builtIn;
|
|
463
|
+
this.plotTypeMenu = menu;
|
|
674
464
|
}
|
|
675
465
|
ngOnChanges(changes) {
|
|
676
466
|
// The constructor computes the options with testMode still at its default;
|
|
@@ -693,8 +483,7 @@ export class VisualizerComponent {
|
|
|
693
483
|
onImageCleared() {
|
|
694
484
|
// The image a contributed mode was drawing over is gone. An open dialog
|
|
695
485
|
// tool stays open and restarts once the next image has plotted.
|
|
696
|
-
this.
|
|
697
|
-
this.toolDialogs.deactivate();
|
|
486
|
+
this.contributions.endSessions();
|
|
698
487
|
this.imageInfo = undefined;
|
|
699
488
|
this.loadedFileName = undefined;
|
|
700
489
|
this.computePlotTypeOptions();
|
|
@@ -707,17 +496,13 @@ export class VisualizerComponent {
|
|
|
707
496
|
// Nothing is left stranded: an image is cleared because a spatial dataset that
|
|
708
497
|
// brings none is being opened, and that dataset's own subscription selects the
|
|
709
498
|
// mode its coordinates support a moment later.
|
|
710
|
-
this.
|
|
499
|
+
this.changed();
|
|
711
500
|
}
|
|
501
|
+
/** If the active plot type is no longer in the offered options (e.g. test mode
|
|
502
|
+
* turned off while a test-only type was active, or a scalar type carried onto
|
|
503
|
+
* an RGB image), fall back to the default 2D Image view. */
|
|
712
504
|
reconcileSelectedPlotType() {
|
|
713
|
-
|
|
714
|
-
return;
|
|
715
|
-
// Image is the usual fallback, but it is not always ON OFFER: with no image loaded
|
|
716
|
-
// the pixel modes are gone, and falling back to one would select a mode the selector
|
|
717
|
-
// does not list and nothing can draw. Take the first type still offered instead.
|
|
718
|
-
const fallback = this.plotTypeMenu.some((d) => d.type === PlotType.IMAGE)
|
|
719
|
-
? PlotType.IMAGE
|
|
720
|
-
: this.plotTypeMenu[0]?.type;
|
|
505
|
+
const fallback = reconcilePlotType(this.plotTypeMenu, this.selectedPlotTypeId);
|
|
721
506
|
if (!fallback)
|
|
722
507
|
return;
|
|
723
508
|
// Leaving a contributed mode (its gates stopped passing, or its provider is
|
|
@@ -734,132 +519,43 @@ export class VisualizerComponent {
|
|
|
734
519
|
ngOnInit() {
|
|
735
520
|
// Join the live set; the oldest member renders the shared notice outlets.
|
|
736
521
|
VisualizerComponent.liveInstances.add(this);
|
|
737
|
-
this.state.setDiagram(this);
|
|
738
|
-
this.selectedColormap =
|
|
739
|
-
this.colormapsOptions[0].children.find((c) => c.label === 'Greys Inv') ??
|
|
740
|
-
this.colormapsOptions[0].children[0];
|
|
741
|
-
this.stackOptions = [
|
|
742
|
-
{ name: 'Single image', val: 'false' },
|
|
743
|
-
{ name: 'Stack', val: 'true' },
|
|
744
|
-
];
|
|
745
|
-
this.selectedStackOption = this.stackOptions[0];
|
|
522
|
+
this.state.setDiagram(this.hostHandle);
|
|
746
523
|
this.watchSpatialDataset();
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
this.selectedStackOption = { name: 'Single image', val: 'false' };
|
|
750
|
-
this.activeDragMode = null;
|
|
751
|
-
this.session.setActiveTool(null);
|
|
752
|
-
});
|
|
753
|
-
this.imageLoadingSubscription = this.state.isImageLoading$().subscribe((isImageLoading) => {
|
|
754
|
-
this.imgLoading = isImageLoading;
|
|
755
|
-
});
|
|
756
|
-
// Undo availability (jit-ui#85): the shared RegionStore emits whenever its
|
|
757
|
-
// history depth changes; greys out the toolbar Undo button accordingly.
|
|
758
|
-
this.plotService
|
|
759
|
-
.getCanUndo$()
|
|
760
|
-
.pipe(takeUntil(this.unsub))
|
|
761
|
-
.subscribe((canUndo) => {
|
|
762
|
-
this.canUndoRegion = canUndo;
|
|
763
|
-
this.cdr.detectChanges();
|
|
764
|
-
});
|
|
765
|
-
this.plotService
|
|
766
|
-
.getCanRedo$()
|
|
767
|
-
.pipe(takeUntil(this.unsub))
|
|
768
|
-
.subscribe((canRedo) => {
|
|
769
|
-
this.canRedoRegion = canRedo;
|
|
770
|
-
this.cdr.detectChanges();
|
|
771
|
-
});
|
|
772
|
-
// Mirror the region selection so the right-click action group (jit-ui#85)
|
|
773
|
-
// can read it synchronously when the menu is built.
|
|
524
|
+
// OSD and napari-js emit this from their own "fit to view": disarm the tool on
|
|
525
|
+
// the backend too, not only the toolbar's highlight.
|
|
774
526
|
this.plotService
|
|
775
|
-
.
|
|
776
|
-
.pipe(
|
|
777
|
-
.subscribe((
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
this.
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
}));
|
|
789
|
-
this.samPointSub.add(this.samPointTool.status$.subscribe((m) => {
|
|
790
|
-
this.samStatus = m;
|
|
791
|
-
this.cdr.detectChanges();
|
|
792
|
-
}));
|
|
793
|
-
this.samPointSub.add(this.samPointTool.busy$.subscribe((busy) => {
|
|
794
|
-
this.samBusy = busy;
|
|
795
|
-
if (busy)
|
|
796
|
-
this.showSamToast('SAM point segmentation');
|
|
797
|
-
this.cdr.detectChanges();
|
|
798
|
-
}));
|
|
799
|
-
this.plotService.getStackLoadingProgress().subscribe((loadingProgress) => {
|
|
800
|
-
this.loadingPercentage = loadingProgress;
|
|
801
|
-
});
|
|
802
|
-
this.plotService.isStackLoading().subscribe((stackLoading) => {
|
|
803
|
-
this.stackLoading = stackLoading;
|
|
527
|
+
.getAutoscaleEvent()
|
|
528
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
529
|
+
.subscribe(() => this.tools.apply(null));
|
|
530
|
+
// Loading/overlay UI mirrors (signals: they re-render the view themselves).
|
|
531
|
+
this.mirror(this.state.isImageLoading$(), (v) => this.imgLoading.set(v));
|
|
532
|
+
this.mirror(this.plotService.getStackLoadingProgress(), (v) => this.loadingPercentage.set(v));
|
|
533
|
+
this.mirror(this.plotService.isStackLoading(), (v) => this.stackLoading.set(v));
|
|
534
|
+
this.mirror(this.state.getImageLoadingMessage$(), (v) => this.loadingMessage.set(v));
|
|
535
|
+
this.mirror(this.state.getCacheProgress$(), (v) => this.cacheProgress.set(v));
|
|
536
|
+
this.mirror(this.state.isZoom$(), (v) => this.zoom.set(v));
|
|
537
|
+
this.mirror(this.state.getFilename$(), (v) => {
|
|
538
|
+
if (v)
|
|
539
|
+
this.fileName = v;
|
|
804
540
|
});
|
|
805
|
-
this.
|
|
541
|
+
this.regionActions.bind(this.destroyed$, () => this.changed());
|
|
542
|
+
this.segmentation.bindPointTool(this.samPointTool, this.destroyed$);
|
|
543
|
+
this.mirror(this.state.getPanelWidth$(), () => {
|
|
806
544
|
this.plotService.relayout();
|
|
807
|
-
// The intensity inset is a separate
|
|
808
|
-
|
|
809
|
-
// size and blanks out. Defer a tick so the panel layout has settled.
|
|
810
|
-
if (this.hasProfiles) {
|
|
811
|
-
setTimeout(() => this.renderIntensityInset(), 0);
|
|
812
|
-
}
|
|
813
|
-
});
|
|
814
|
-
this.imgLoadingMessageSubscription = this.state.getImageLoadingMessage$().subscribe((message) => {
|
|
815
|
-
this.loadingMessage = message;
|
|
545
|
+
// The intensity inset is a separate chart in a floating panel; reflow it too.
|
|
546
|
+
this.inset?.reflow();
|
|
816
547
|
});
|
|
817
548
|
this.state
|
|
818
|
-
.
|
|
819
|
-
.pipe(
|
|
820
|
-
.subscribe(
|
|
821
|
-
this.cacheProgress = progress;
|
|
822
|
-
});
|
|
823
|
-
this.isZoomSubscription = this.state.isZoom$().subscribe((zoom) => {
|
|
824
|
-
this.zoom = zoom;
|
|
825
|
-
});
|
|
826
|
-
this.filenameSubscription = this.state.getFilename$().subscribe((filename) => {
|
|
827
|
-
if (filename) {
|
|
828
|
-
this.fileName = filename;
|
|
829
|
-
}
|
|
830
|
-
});
|
|
831
|
-
this.plotService.getColormap().subscribe((colormap) => {
|
|
832
|
-
this.selectedColormap = colormap;
|
|
833
|
-
});
|
|
834
|
-
this.plotService.getReverseScale().subscribe((reversescale) => {
|
|
835
|
-
this.reversescale = reversescale;
|
|
836
|
-
});
|
|
837
|
-
this.intensityProfileSub = this.plotService.getIntensityProfile$().subscribe((profiles) => {
|
|
838
|
-
this.latestProfiles = profiles;
|
|
839
|
-
this.hasProfiles = profiles.length > 0;
|
|
840
|
-
// The panel div is behind *ngIf="hasProfiles". detectChanges() materializes
|
|
841
|
-
// it synchronously (the subscription may fire outside Angular's zone — e.g.
|
|
842
|
-
// from an OSD drag — so CD wouldn't run on its own). Then render on the next
|
|
843
|
-
// animation frame, AFTER the browser lays the panel out: a synchronous draw
|
|
844
|
-
// hits a zero-size container on first create and Plotly keeps that size, so
|
|
845
|
-
// the inset stays blank and live updates redraw into the same zero box.
|
|
846
|
-
this.cdr.detectChanges();
|
|
847
|
-
requestAnimationFrame(() => this.renderIntensityInset());
|
|
848
|
-
});
|
|
849
|
-
// When the OSD view settles at a new zoom/pan, re-sample the intensity lines
|
|
850
|
-
// from a crop of the visible region so the inset reflects the zoom-level
|
|
851
|
-
// resolution (Plotly's own high-def zoom updates the sampling cache inline).
|
|
852
|
-
this.viewportChangeSub = this.plotService.getViewportChange$().subscribe((roi) => {
|
|
853
|
-
if (this.hasProfiles && this.isImageView) {
|
|
854
|
-
this.plotService.refreshIntensitySamplingForRoi(roi.x, roi.y, roi.width, roi.height, this.zIndex);
|
|
855
|
-
}
|
|
856
|
-
});
|
|
857
|
-
this.previewSubscription = this.state.getImageInfo$().subscribe({
|
|
549
|
+
.getImageInfo$()
|
|
550
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
551
|
+
.subscribe({
|
|
858
552
|
next: (imgInfo) => {
|
|
859
553
|
if (!imgInfo) {
|
|
860
554
|
this.onImageCleared();
|
|
861
555
|
return;
|
|
862
556
|
}
|
|
557
|
+
// The selector, the stack controls and the inset all follow the image.
|
|
558
|
+
this.changed();
|
|
863
559
|
if (imgInfo) {
|
|
864
560
|
this.imageInfo = imgInfo;
|
|
865
561
|
// Stack-only plot types (isosurface, scatter3d) depend on whether this
|
|
@@ -868,8 +564,6 @@ export class VisualizerComponent {
|
|
|
868
564
|
// If the active plot type isn't valid for this image (e.g. a scalar
|
|
869
565
|
// type like Contour carried over to an RGB image), fall back to Image.
|
|
870
566
|
this.reconcileSelectedPlotType();
|
|
871
|
-
// reset stack options selection
|
|
872
|
-
this.selectedStackOption = imgInfo.showStack ? this.stackOptions[1] : this.stackOptions[0];
|
|
873
567
|
// Read from `imgInfo`, the value this emission carried, rather than from the
|
|
874
568
|
// field. Handling the empty emission means the field CAN be nulled part-way
|
|
875
569
|
// through this branch: `reconcileSelectedPlotType` may call `setPlotType`,
|
|
@@ -888,215 +582,14 @@ export class VisualizerComponent {
|
|
|
888
582
|
this.activeSurface3dMode = 'turntable';
|
|
889
583
|
}
|
|
890
584
|
this.loadedFileName = imgInfo.fileName;
|
|
891
|
-
this.plotService.setImageMeta(imgInfo.imageMeta);
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
// finished: identity above had already flipped to the new file, so the
|
|
896
|
-
// app reported image B as loaded while image A stayed on screen. Worse,
|
|
897
|
-
// a cold image holds the slot for its whole server-side cache fill, so
|
|
898
|
-
// every click for 1-2 minutes was discarded against a blank viewport
|
|
899
|
-
// and a "Loading image..." overlay that never resolved.
|
|
900
|
-
if (urls) {
|
|
901
|
-
const token = ++this.renderToken;
|
|
902
|
-
const isCurrent = () => this.renderToken === token;
|
|
903
|
-
if (this.running) {
|
|
904
|
-
// Stop the previous render's frame streaming (napari volume/surface
|
|
905
|
-
// preload keeps fetching otherwise) and clear its sharpen flag. Its
|
|
906
|
-
// callbacks are already inert via the token, so it can neither paint
|
|
907
|
-
// nor release the overlay this render now owns.
|
|
908
|
-
this.plotService.cancelLoading?.();
|
|
909
|
-
this.sharpening = false;
|
|
910
|
-
}
|
|
911
|
-
// image size — measure the plot div directly so the toolbar height is excluded
|
|
912
|
-
const plotDiv = document.getElementById(this.plotDivName);
|
|
913
|
-
const screenHeight = plotDiv?.offsetHeight || 500;
|
|
914
|
-
// A contributed mode's session is bound to the base view about to be
|
|
915
|
-
// torn down, so it ends here — before reset(), before anything new
|
|
916
|
-
// draws. If the mode is still selected once this render lands, a
|
|
917
|
-
// fresh session starts for it (see activateSelectedPlotMode).
|
|
918
|
-
this.plotModes.deactivate();
|
|
919
|
-
this.toolDialogs.deactivate();
|
|
920
|
-
this.plotService.reset();
|
|
921
|
-
this.stackLoading = imgInfo.isStack && imgInfo.showStack;
|
|
922
|
-
// set max index of stack — computed before updateZIndex so a
|
|
923
|
-
// fresh stack (shorter or longer than whatever was previously
|
|
924
|
-
// loaded) clamps against ITS bounds, not the stale ones.
|
|
925
|
-
// One URL per slice (0-indexed), so the last reachable index is
|
|
926
|
-
// length-1 — earlier `length-2` dropped the final slice.
|
|
927
|
-
this.maxIndex = urls.length > 1 ? urls.length - 1 : 0;
|
|
928
|
-
// One-shot hint: jump straight to a specific slice (e.g. the
|
|
929
|
-
// file the user actually clicked within a numbered series).
|
|
930
|
-
// Consumed immediately so redelivering the same ImageInfo later
|
|
931
|
-
// (a plot-type switch, reloadAndPlot) doesn't reset the user's
|
|
932
|
-
// current scrub position back to it.
|
|
933
|
-
if (imgInfo.initialZIndex !== undefined) {
|
|
934
|
-
this.zIndex = imgInfo.initialZIndex;
|
|
935
|
-
imgInfo.initialZIndex = undefined;
|
|
936
|
-
}
|
|
937
|
-
// make sure the zindex is within bounds
|
|
938
|
-
this.updateZIndex(urls);
|
|
939
|
-
this.running = true;
|
|
940
|
-
// Multi-tier rendering (small blurry tier first, then sharpen in
|
|
941
|
-
// place) — sequencing lives in RenderOrchestrator; this component
|
|
942
|
-
// supplies the phase render and owns the UI flags via callbacks.
|
|
943
|
-
// 3D plot types render single-pass: the in-place large pass doesn't
|
|
944
|
-
// rebuild a 3D gl-mesh isosurface, so the sharpen step blanked it.
|
|
945
|
-
const hasSmallTier = this.isHeatmap &&
|
|
946
|
-
(imgInfo.smallUrls?.length ?? 0) === urls.length &&
|
|
947
|
-
(imgInfo.smallUrls?.length ?? 0) > 0;
|
|
948
|
-
const smallImgInfo = hasSmallTier ? { ...imgInfo, urls: imgInfo.smallUrls } : null;
|
|
949
|
-
// Enter per-slice stack mode with the given slice→regions map and
|
|
950
|
-
// seed the "previous shapes" from the now-live slice. enterStackMode
|
|
951
|
-
// makes zIndex's slice live and resets undo (jit-ui#93).
|
|
952
|
-
const enterStack = (slices, layout) => {
|
|
953
|
-
this.plotService.enterStackMode(slices, this.zIndex, layout);
|
|
954
|
-
const shapes = this.plotService
|
|
955
|
-
.getRegions()
|
|
956
|
-
.map((region) => region.getShape(this.plotService.getShowShapeLabel()));
|
|
957
|
-
this.plotService.setPreviousShapes(shapes);
|
|
958
|
-
};
|
|
959
|
-
const applyRoi = () => {
|
|
960
|
-
// Folder stack: a stack of self-contained per-slice files
|
|
961
|
-
// (tiled === false; see loadSeriesAsStack). Each slice-file may
|
|
962
|
-
// carry its own sibling "<stem>.geojson" (roiJsonStrs[z]), but a
|
|
963
|
-
// fresh folder with none yet leaves roiJsonStrs undefined — key
|
|
964
|
-
// off `tiled === false`, NOT roiJsonStrs, so an unannotated
|
|
965
|
-
// folder stack still enters the per-slice-file layout (and saves
|
|
966
|
-
// back one geojson per slice-file) rather than falling through to
|
|
967
|
-
// the single-file combined path (jit-ui#93).
|
|
968
|
-
if (imgInfo.isStack && imgInfo.tiled === false) {
|
|
969
|
-
const perSlice = imgInfo.roiJsonStrs;
|
|
970
|
-
const sliceCount = imgInfo.urls?.length ?? perSlice?.length ?? 0;
|
|
971
|
-
const slices = new Map();
|
|
972
|
-
for (let z = 0; z < sliceCount; z++) {
|
|
973
|
-
const json = perSlice?.[z] ?? null;
|
|
974
|
-
slices.set(z, json ? this.plotService.importRegions(json) : []);
|
|
975
|
-
}
|
|
976
|
-
enterStack(slices, 'per-slice-file');
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
// Single-file z-stack: one sibling geojson holding every slice's
|
|
980
|
-
// regions indexed by QuPath's geometry.plane.z. Enter per-slice
|
|
981
|
-
// mode (saving back one combined z-indexed geojson) when the
|
|
982
|
-
// geojson actually carries slice indices, or when there's nothing
|
|
983
|
-
// yet to author against. A legacy geojson whose regions are all on
|
|
984
|
-
// the default plane stays global (shown on every slice) so
|
|
985
|
-
// existing single-plane annotations aren't confined to slice 0.
|
|
986
|
-
const roiJson = imgInfo.roiJsonStr;
|
|
987
|
-
if (imgInfo.isStack) {
|
|
988
|
-
const regions = roiJson ? this.plotService.importRegions(roiJson) : [];
|
|
989
|
-
const hasSliceInfo = regions.some((r) => (r.z ?? 0) !== 0);
|
|
990
|
-
if (!roiJson || hasSliceInfo) {
|
|
991
|
-
const slices = new Map();
|
|
992
|
-
for (const r of regions) {
|
|
993
|
-
const z = r.z ?? 0;
|
|
994
|
-
const bucket = slices.get(z);
|
|
995
|
-
if (bucket)
|
|
996
|
-
bucket.push(r);
|
|
997
|
-
else
|
|
998
|
-
slices.set(z, [r]);
|
|
999
|
-
}
|
|
1000
|
-
enterStack(slices, 'combined');
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
// Single-plane image (or a legacy global z-stack geojson): one
|
|
1005
|
-
// region set for the whole image.
|
|
1006
|
-
if (roiJson) {
|
|
1007
|
-
const regions = this.plotService.importRegions(roiJson);
|
|
1008
|
-
this.plotService.setRegions(regions);
|
|
1009
|
-
const shapes = regions.map((region) => region.getShape(this.plotService.getShowShapeLabel()));
|
|
1010
|
-
this.plotService.setPreviousShapes(shapes);
|
|
1011
|
-
}
|
|
1012
|
-
// Loading an image's saved ROIs is not a user edit — start the
|
|
1013
|
-
// undo history fresh so the first undo can't wipe them (jit-ui#85).
|
|
1014
|
-
this.plotService.resetUndoHistory();
|
|
1015
|
-
};
|
|
1016
|
-
let overlayReleased = false;
|
|
1017
|
-
const releaseOverlay = () => {
|
|
1018
|
-
if (overlayReleased)
|
|
1019
|
-
return;
|
|
1020
|
-
overlayReleased = true;
|
|
1021
|
-
if (imgInfo.isStack && imgInfo.showStack)
|
|
1022
|
-
this.stackLoading = false;
|
|
1023
|
-
this.state.setImageLoading(false);
|
|
1024
|
-
};
|
|
1025
|
-
new RenderOrchestrator({
|
|
1026
|
-
// inPlace=true updates the existing render instead of rebuilding
|
|
1027
|
-
// it, so the canvas doesn't blank during the small→large swap.
|
|
1028
|
-
renderPhase: (phaseInfo, inPlace) => {
|
|
1029
|
-
// Superseded BEFORE this phase started — don't even issue the load.
|
|
1030
|
-
// RenderOrchestrator calls renderPhase once per tier and retries the
|
|
1031
|
-
// sharpen pass on failure, so checking only after the load resolves
|
|
1032
|
-
// would let a preempted render keep fetching slices/tiles for an
|
|
1033
|
-
// image nobody is looking at.
|
|
1034
|
-
if (!isCurrent())
|
|
1035
|
-
return Promise.resolve(null);
|
|
1036
|
-
return this.plotService.load(phaseInfo, this.zIndex).then((loadedImage) => {
|
|
1037
|
-
// Preempted while this phase was loading — drop it on the floor.
|
|
1038
|
-
if (!isCurrent())
|
|
1039
|
-
return null;
|
|
1040
|
-
// Guard against a newer click reaching us mid-render.
|
|
1041
|
-
if (phaseInfo.fileName !== loadedImage.filename)
|
|
1042
|
-
return null;
|
|
1043
|
-
return this.plotService.plot(this.plotDivName, loadedImage, phaseInfo, screenHeight, this.plotType, inPlace);
|
|
1044
|
-
});
|
|
1045
|
-
},
|
|
1046
|
-
smallShown: () => {
|
|
1047
|
-
if (!isCurrent())
|
|
1048
|
-
return; // superseded by a newer image
|
|
1049
|
-
// If the file is still being cached/prepared server-side, keep the full
|
|
1050
|
-
// cache-progress overlay up instead of dropping to the translucent "Sharpening
|
|
1051
|
-
// preview..." spinner over a blank canvas (a large uncached image renders its
|
|
1052
|
-
// small preview before its tiles exist). finished() releases the overlay once
|
|
1053
|
-
// the real render lands.
|
|
1054
|
-
if (this.isCaching)
|
|
1055
|
-
return;
|
|
1056
|
-
// Small tier on screen — drop the full overlay but keep a translucent spinner so
|
|
1057
|
-
// the blurry render isn't mistaken for the final image.
|
|
1058
|
-
releaseOverlay();
|
|
1059
|
-
this.sharpening = true;
|
|
1060
|
-
},
|
|
1061
|
-
sharpenSettled: () => {
|
|
1062
|
-
if (!isCurrent())
|
|
1063
|
-
return; // superseded by a newer image
|
|
1064
|
-
this.sharpening = false;
|
|
1065
|
-
},
|
|
1066
|
-
finished: (_viaSmall, logTag) => {
|
|
1067
|
-
if (!isCurrent())
|
|
1068
|
-
return; // superseded by a newer image
|
|
1069
|
-
// Idempotent — releases now if smallShown deferred it (caching) or was skipped.
|
|
1070
|
-
releaseOverlay();
|
|
1071
|
-
this.running = false;
|
|
1072
|
-
applyRoi();
|
|
1073
|
-
this.activateSelectedPlotMode();
|
|
1074
|
-
this.activateOpenDialogTool();
|
|
1075
|
-
console.log(logTag);
|
|
1076
|
-
},
|
|
1077
|
-
sharpenFailed: (err) => {
|
|
1078
|
-
if (!isCurrent())
|
|
1079
|
-
return; // superseded by a newer image
|
|
1080
|
-
// The small tier stays on screen as the fallback — tell the
|
|
1081
|
-
// user the sharper version isn't coming.
|
|
1082
|
-
const msg = err?.error?.message || err?.message || err?.statusText || String(err);
|
|
1083
|
-
this.messageService.add({
|
|
1084
|
-
key: this.vizAlertToastKey,
|
|
1085
|
-
severity: 'warn',
|
|
1086
|
-
summary: 'Preview not sharpened',
|
|
1087
|
-
detail: `The full-resolution preview did not load (${msg}). The low-resolution preview is still shown. Try clicking the image again.`,
|
|
1088
|
-
});
|
|
1089
|
-
this.running = false;
|
|
1090
|
-
applyRoi();
|
|
1091
|
-
this.activateSelectedPlotMode();
|
|
1092
|
-
this.activateOpenDialogTool();
|
|
1093
|
-
},
|
|
1094
|
-
}).render(imgInfo, smallImgInfo);
|
|
1095
|
-
}
|
|
585
|
+
this.plotService.setImageMeta(imgInfo.imageMeta, imgInfo.fileName);
|
|
586
|
+
// A newer image always preempts an in-flight render (see ImageRenderSession).
|
|
587
|
+
if (imgInfo.urls)
|
|
588
|
+
this.render.render(imgInfo);
|
|
1096
589
|
}
|
|
1097
590
|
},
|
|
1098
591
|
error: (err) => {
|
|
1099
|
-
const msg =
|
|
592
|
+
const msg = errorMessage(err);
|
|
1100
593
|
console.error('Error occured when getting image info', err);
|
|
1101
594
|
this.messageService.add({
|
|
1102
595
|
key: this.vizAlertToastKey,
|
|
@@ -1106,323 +599,142 @@ export class VisualizerComponent {
|
|
|
1106
599
|
detail: `The following error occured while getting image info: ${msg}.
|
|
1107
600
|
Please try to open the image again through the file navigator.`,
|
|
1108
601
|
});
|
|
1109
|
-
this.stackLoading
|
|
602
|
+
this.stackLoading.set(false);
|
|
1110
603
|
this.state.setImageLoading(false);
|
|
1111
|
-
this.running = false;
|
|
604
|
+
this.render.running = false;
|
|
1112
605
|
this.plotService.reset();
|
|
1113
606
|
},
|
|
1114
607
|
});
|
|
1115
608
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
};
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
this.ngZone.run(() => this.redoRegion());
|
|
1157
|
-
return;
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
if (event.key === 'Delete' || event.key === 'Backspace' || event.key === 'd' || event.key === 'D') {
|
|
1161
|
-
this.ngZone.run(() => this.deleteRegion());
|
|
1162
|
-
}
|
|
1163
|
-
else if (event.key === '+' || event.key === '=') {
|
|
1164
|
-
this.ngZone.run(() => this.zoomIn());
|
|
1165
|
-
}
|
|
1166
|
-
else if (event.key === '-' || event.key === '_') {
|
|
1167
|
-
this.ngZone.run(() => this.zoomOut());
|
|
1168
|
-
}
|
|
1169
|
-
else if (event.key === 'p') {
|
|
1170
|
-
this.ngZone.run(() => this.toggleDragMode('pan'));
|
|
1171
|
-
}
|
|
1172
|
-
else if (event.key === 'b') {
|
|
1173
|
-
this.ngZone.run(() => this.toggleDragMode('zoomToBox'));
|
|
1174
|
-
}
|
|
1175
|
-
else if (event.key === 'r') {
|
|
1176
|
-
this.ngZone.run(() => this.toggleDragMode('drawrect'));
|
|
1177
|
-
}
|
|
1178
|
-
else if (event.key === 'f') {
|
|
1179
|
-
this.ngZone.run(() => this.toggleDragMode('drawclosedpath'));
|
|
1180
|
-
}
|
|
1181
|
-
else if (event.key === 'w') {
|
|
1182
|
-
this.ngZone.run(() => this.toggleDragMode('wand'));
|
|
1183
|
-
}
|
|
1184
|
-
else if (event.key === 'e') {
|
|
1185
|
-
this.ngZone.run(() => this.toggleDragMode('eraseVertex'));
|
|
1186
|
-
}
|
|
1187
|
-
else if (event.key === 's') {
|
|
1188
|
-
this.ngZone.run(() => this.toggleDragMode('select'));
|
|
1189
|
-
}
|
|
1190
|
-
else if (event.key === 'l') {
|
|
1191
|
-
this.ngZone.run(() => this.toggleDragMode('drawopenpath'));
|
|
1192
|
-
}
|
|
1193
|
-
};
|
|
1194
|
-
window.addEventListener('keydown', this.keydownListener);
|
|
1195
|
-
this.wheelListener = (event) => {
|
|
1196
|
-
const plotEl = document.getElementById(this.plotDivName);
|
|
1197
|
-
if (!plotEl?.contains(event.target))
|
|
1198
|
-
return;
|
|
1199
|
-
// Intercepting here fires a FIXED zoom step per wheel event, which is far
|
|
1200
|
-
// too sensitive for a renderer that reads the scroll delta — so anything
|
|
1201
|
-
// drawn by such a renderer keeps its own wheel. See `rendererOwnsWheel`.
|
|
1202
|
-
if (rendererOwnsWheel(this.basePlotType))
|
|
1203
|
-
return;
|
|
1204
|
-
// 3D plot types (surface, scatter3d, isosurface) render in a Plotly scene
|
|
1205
|
-
// that orbits/zooms natively on scroll. The 2D step-zoom doesn't apply and
|
|
1206
|
-
// would throw (no xaxis on a scene), so let Plotly handle the wheel.
|
|
1207
|
-
if (!this.isHeatmap)
|
|
1208
|
-
return;
|
|
1209
|
-
event.preventDefault();
|
|
1210
|
-
event.stopImmediatePropagation();
|
|
1211
|
-
if (event.deltaY < 0) {
|
|
1212
|
-
this.ngZone.run(() => this.zoomIn());
|
|
1213
|
-
}
|
|
1214
|
-
else if (event.deltaY > 0) {
|
|
1215
|
-
this.ngZone.run(() => this.zoomOut());
|
|
1216
|
-
}
|
|
1217
|
-
};
|
|
1218
|
-
window.addEventListener('wheel', this.wheelListener, { capture: true, passive: false });
|
|
1219
|
-
// Drag handling for the floating intensity-profile panel.
|
|
1220
|
-
this.profileDragMoveListener = (e) => {
|
|
1221
|
-
if (this.profilePanelDragging) {
|
|
1222
|
-
this.ngZone.run(() => {
|
|
1223
|
-
this.profilePanelPos = {
|
|
1224
|
-
x: this.profilePanelStart.x + (e.clientX - this.profilePanelStart.mx),
|
|
1225
|
-
y: this.profilePanelStart.y + (e.clientY - this.profilePanelStart.my),
|
|
1226
|
-
};
|
|
1227
|
-
});
|
|
1228
|
-
}
|
|
1229
|
-
else if (this.toolbarDragging) {
|
|
1230
|
-
this.ngZone.run(() => {
|
|
1231
|
-
this.toolbarPos = {
|
|
1232
|
-
x: this.toolbarStart.x + (e.clientX - this.toolbarStart.mx),
|
|
1233
|
-
y: this.toolbarStart.y + (e.clientY - this.toolbarStart.my),
|
|
1234
|
-
};
|
|
1235
|
-
});
|
|
609
|
+
/**
|
|
610
|
+
* Follow `source` until destroy. Host streams can emit outside the Angular zone (a
|
|
611
|
+
* serverless host clears its loading flag from an image decode or an OpenSeadragon
|
|
612
|
+
* callback); setting a signal there marks this OnPush view but schedules no change
|
|
613
|
+
* detection, so the update is applied inside the zone, as {@link changed} does.
|
|
614
|
+
*/
|
|
615
|
+
mirror(source, apply) {
|
|
616
|
+
source.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
|
|
617
|
+
if (NgZone.isInAngularZone())
|
|
618
|
+
apply(value);
|
|
619
|
+
else
|
|
620
|
+
this.ngZone.run(() => apply(value));
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* State this view shows changed outside one of its template events (a stream, a
|
|
625
|
+
* resolved promise, a host call): mark the OnPush view for check, re-entering the
|
|
626
|
+
* zone when it changed outside it so a change-detection pass follows.
|
|
627
|
+
*/
|
|
628
|
+
changed() {
|
|
629
|
+
if (NgZone.isInAngularZone())
|
|
630
|
+
this.cdr.markForCheck();
|
|
631
|
+
else
|
|
632
|
+
this.ngZone.run(() => this.cdr.markForCheck());
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Lay a new image out once the old view is gone: the stack flag, the slice bounds (one
|
|
636
|
+
* url per slice, so the last index is length-1), and the one-shot `initialZIndex` hint
|
|
637
|
+
* (e.g. the file the user clicked within a numbered series). The hint is consumed so
|
|
638
|
+
* re-delivering the same info (a plot-type switch, reloadAndPlot) does not reset the
|
|
639
|
+
* user's scrub; the host's object is never written to (a host store may freeze it) —
|
|
640
|
+
* the component keeps a copy without the hint, and remembers the host object.
|
|
641
|
+
*/
|
|
642
|
+
layOutSlices(info) {
|
|
643
|
+
this.stackLoading.set(info.isStack && info.showStack);
|
|
644
|
+
this.maxIndex = info.urls.length > 1 ? info.urls.length - 1 : 0;
|
|
645
|
+
if (info.initialZIndex !== undefined) {
|
|
646
|
+
if (!this.consumedSliceHints.has(info)) {
|
|
647
|
+
this.consumedSliceHints.add(info);
|
|
648
|
+
this.zIndex = info.initialZIndex;
|
|
1236
649
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
//
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
this.profileResizeListener = () => {
|
|
1248
|
-
if (!this.hasProfiles)
|
|
1249
|
-
return;
|
|
1250
|
-
requestAnimationFrame(() => this.renderIntensityInset());
|
|
1251
|
-
};
|
|
1252
|
-
window.addEventListener('resize', this.profileResizeListener);
|
|
650
|
+
this.imageInfo = { ...info, initialZIndex: undefined };
|
|
651
|
+
}
|
|
652
|
+
this.updateZIndex();
|
|
653
|
+
}
|
|
654
|
+
ngAfterViewInit() {
|
|
655
|
+
// Window listeners are registered OUTSIDE the Angular zone: zone-patched, every
|
|
656
|
+
// mousemove anywhere in the host app ran app-wide change detection. Each handler
|
|
657
|
+
// re-enters the zone (ngZone.run) only when it actually changes view state.
|
|
658
|
+
this.shortcuts = new ViewerShortcuts(this.shortcutHost(), this.ngZone);
|
|
659
|
+
this.shortcuts.attach();
|
|
1253
660
|
this.onViewReady();
|
|
1254
661
|
}
|
|
1255
662
|
/** The plot div exists now: run an image-less draw that arrived before it did. */
|
|
1256
663
|
onViewReady() {
|
|
1257
|
-
this.
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
void this.plotSpatialWithoutImage(current);
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
onProfilePanelDragStart(e) {
|
|
1269
|
-
this.profilePanelDragging = true;
|
|
1270
|
-
this.profilePanelStart = {
|
|
1271
|
-
mx: e.clientX,
|
|
1272
|
-
my: e.clientY,
|
|
1273
|
-
x: this.profilePanelPos.x,
|
|
1274
|
-
y: this.profilePanelPos.y,
|
|
664
|
+
this.render.viewIsReady(this.spatial.dataset, !!this.imageInfo);
|
|
665
|
+
}
|
|
666
|
+
/** What the keyboard, wheel and context-menu shortcuts act on. Their listeners live
|
|
667
|
+
* outside this view, so every action that changes what it shows marks it for check. */
|
|
668
|
+
shortcutHost() {
|
|
669
|
+
const marked = (act) => (...args) => {
|
|
670
|
+
act(...args);
|
|
671
|
+
this.changed();
|
|
1275
672
|
};
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
this.
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
673
|
+
return {
|
|
674
|
+
plotDivName: this.plotDivName,
|
|
675
|
+
hostElement: this.hostRef?.nativeElement ?? null,
|
|
676
|
+
activeDragMode: () => this.activeDragMode,
|
|
677
|
+
canStepSlice: () => !!this.imageInfo?.isStack && this.isImageView,
|
|
678
|
+
wheelZooms: () => !rendererOwnsWheel(this.basePlotType) && this.isHeatmap,
|
|
679
|
+
stepSlice: marked((delta) => this.stepSlice(delta)),
|
|
680
|
+
resolveSamPrompt: marked((commit) => {
|
|
681
|
+
if (commit)
|
|
682
|
+
this.plotService.commitSamPoints();
|
|
683
|
+
else
|
|
684
|
+
this.plotService.clearSamPoints();
|
|
685
|
+
this.segmentation.hide(); // prompt resolved → dismiss the status toast
|
|
686
|
+
}),
|
|
687
|
+
undo: marked(() => this.undoRegion()),
|
|
688
|
+
redo: marked(() => this.redoRegion()),
|
|
689
|
+
deleteRegion: marked(() => this.deleteRegion()),
|
|
690
|
+
zoomIn: marked(() => this.zoomIn()),
|
|
691
|
+
zoomOut: marked(() => this.zoomOut()),
|
|
692
|
+
toggleDragMode: marked((mode) => this.toggleDragMode(mode)),
|
|
693
|
+
openContextMenu: (event) => {
|
|
694
|
+
this.contextMenuItems = this.buildContextMenuItems();
|
|
695
|
+
this.cdr.detectChanges();
|
|
696
|
+
this.contextMenu.show(event);
|
|
697
|
+
},
|
|
1291
698
|
};
|
|
1292
|
-
e.preventDefault();
|
|
1293
699
|
}
|
|
1294
700
|
/** Snap the floating toolbar back to its docked position across the top. */
|
|
1295
701
|
dockToolbar() {
|
|
1296
702
|
this.toolbarFloating = false;
|
|
1297
703
|
}
|
|
1298
|
-
/** Render the intensity inset chart from the latest profile data. The actual
|
|
1299
|
-
* charting is owned by the visualizer service — the component just decides
|
|
1300
|
-
* when (profile mode + a fresh profile). */
|
|
1301
|
-
renderIntensityInset() {
|
|
1302
|
-
if (!this.hasProfiles)
|
|
1303
|
-
return;
|
|
1304
|
-
this.plotService.renderIntensityInset(this.intensityInsetDiv, this.latestProfiles);
|
|
1305
|
-
}
|
|
1306
704
|
/** Toolbar "Intensity" group: add another line ROI (next bright colour). */
|
|
1307
705
|
async addProfileLine() {
|
|
1308
|
-
|
|
1309
|
-
//
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
: { x: 20, y: 70 };
|
|
1315
|
-
}
|
|
1316
|
-
// Image (OSD) mode: Plotly never rendered, so it has no pixel cache / extent.
|
|
1317
|
-
// Load the current slice's frames for sampling + line placement first.
|
|
1318
|
-
if (this.isImageView && this.imageInfo) {
|
|
1319
|
-
await this.plotService.ensureIntensitySampling(this.imageInfo, this.zIndex);
|
|
1320
|
-
}
|
|
1321
|
-
const region = this.plotService.getIntensityControls()?.addProfileLine();
|
|
1322
|
-
// Auto-select the new line on the active backend (Plotly handles / OSD
|
|
1323
|
-
// highlight) so it's ready to move or delete immediately.
|
|
1324
|
-
if (region) {
|
|
1325
|
-
this.plotService.selectRegion(region);
|
|
1326
|
-
// OSD only draws a selected region's handles in an edit mode, so a line in
|
|
1327
|
-
// 'none' mode looks unselected. Switch to 'select' so the new line shows
|
|
1328
|
-
// its endpoint handles and can be dragged/deleted right away.
|
|
1329
|
-
if (this.isImageView && this.activeDragMode !== 'select') {
|
|
1330
|
-
this.toggleDragMode('select');
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
706
|
+
const region = await this.inset?.addProfileLine();
|
|
707
|
+
// OSD only draws a selected region's handles in an edit mode, so a line in
|
|
708
|
+
// 'none' mode looks unselected. Switch to 'select' so the new line shows its
|
|
709
|
+
// endpoint handles and can be dragged/deleted right away.
|
|
710
|
+
if (region && this.isImageView && this.activeDragMode !== 'select')
|
|
711
|
+
this.toggleDragMode('select');
|
|
1333
712
|
}
|
|
1334
713
|
ngOnDestroy() {
|
|
1335
714
|
// A contributed mode's session ends first, while the viewer it drew over
|
|
1336
715
|
// still exists. The panel goes with this view, so no change detection.
|
|
1337
|
-
this.
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
// Leave the live set so the next-oldest visualizer picks up the outlets.
|
|
716
|
+
this.contributions.destroy();
|
|
717
|
+
// Leave the live set so the next-oldest visualizer picks up the outlets — on its
|
|
718
|
+
// next check, which an OnPush sibling needs asking for.
|
|
1341
719
|
VisualizerComponent.liveInstances.delete(this);
|
|
1342
|
-
|
|
720
|
+
for (const other of VisualizerComponent.liveInstances)
|
|
721
|
+
other.changed();
|
|
722
|
+
this.state.setDiagram(null);
|
|
723
|
+
this.render.cancel();
|
|
724
|
+
this.spatial.dispose();
|
|
1343
725
|
this.scrubber.cancel();
|
|
1344
|
-
this.
|
|
1345
|
-
|
|
1346
|
-
window.removeEventListener('contextmenu', this.plotContextMenuListener, true);
|
|
1347
|
-
}
|
|
1348
|
-
if (this.keydownListener) {
|
|
1349
|
-
window.removeEventListener('keydown', this.keydownListener);
|
|
1350
|
-
}
|
|
1351
|
-
if (this.wheelListener) {
|
|
1352
|
-
window.removeEventListener('wheel', this.wheelListener, true);
|
|
1353
|
-
}
|
|
1354
|
-
if (this.profileDragMoveListener) {
|
|
1355
|
-
window.removeEventListener('mousemove', this.profileDragMoveListener);
|
|
1356
|
-
}
|
|
1357
|
-
if (this.profileDragUpListener) {
|
|
1358
|
-
window.removeEventListener('mouseup', this.profileDragUpListener);
|
|
1359
|
-
}
|
|
1360
|
-
if (this.profileResizeListener) {
|
|
1361
|
-
window.removeEventListener('resize', this.profileResizeListener);
|
|
1362
|
-
}
|
|
1363
|
-
if (this.intensityProfileSub) {
|
|
1364
|
-
this.intensityProfileSub.unsubscribe();
|
|
1365
|
-
}
|
|
1366
|
-
if (this.viewportChangeSub) {
|
|
1367
|
-
this.viewportChangeSub.unsubscribe();
|
|
1368
|
-
}
|
|
1369
|
-
this.unsub.next();
|
|
1370
|
-
this.unsub.complete();
|
|
1371
|
-
if (this.previewSubscription) {
|
|
1372
|
-
this.previewSubscription.unsubscribe();
|
|
1373
|
-
}
|
|
1374
|
-
if (this.spatialDatasetSubscription) {
|
|
1375
|
-
this.spatialDatasetSubscription.unsubscribe();
|
|
1376
|
-
}
|
|
1377
|
-
if (this.plotWidthSubscription) {
|
|
1378
|
-
this.plotWidthSubscription.unsubscribe();
|
|
1379
|
-
}
|
|
1380
|
-
if (this.imageLoadingSubscription) {
|
|
1381
|
-
this.imageLoadingSubscription.unsubscribe();
|
|
1382
|
-
}
|
|
1383
|
-
if (this.imgLoadingMessageSubscription) {
|
|
1384
|
-
this.imgLoadingMessageSubscription.unsubscribe();
|
|
1385
|
-
}
|
|
1386
|
-
if (this.isZoomSubscription) {
|
|
1387
|
-
this.isZoomSubscription.unsubscribe();
|
|
1388
|
-
}
|
|
1389
|
-
if (this.autoscaleSubscription) {
|
|
1390
|
-
this.autoscaleSubscription.unsubscribe();
|
|
1391
|
-
}
|
|
1392
|
-
if (this.filenameSubscription) {
|
|
1393
|
-
this.filenameSubscription.unsubscribe();
|
|
1394
|
-
}
|
|
1395
|
-
this.plotService.unsubscribe();
|
|
726
|
+
this.shortcuts?.detach();
|
|
727
|
+
this.plotService.detach();
|
|
1396
728
|
}
|
|
1397
729
|
hasRegions() {
|
|
1398
|
-
//
|
|
1399
|
-
//
|
|
1400
|
-
// region exists, and must not depend on a backend's wire format.
|
|
730
|
+
// The contract's framework-neutral region accessor: the component only needs to
|
|
731
|
+
// know whether any region exists, never a backend's wire format.
|
|
1401
732
|
return this.plotService.getRegions().length > 0;
|
|
1402
733
|
}
|
|
1403
|
-
/**
|
|
1404
|
-
* This method is called in mainFacade.configRequest() - DO NOT DELETE
|
|
1405
|
-
*/
|
|
734
|
+
/** The current regions as polygons (also on the host handle). */
|
|
1406
735
|
getRegionPolygons() {
|
|
1407
736
|
return this.plotService.getRegionPolygons();
|
|
1408
737
|
}
|
|
1409
|
-
/**
|
|
1410
|
-
* Select the color scale type: called by the color scale select button
|
|
1411
|
-
* @param event
|
|
1412
|
-
*/
|
|
1413
|
-
toggleReverseScale() {
|
|
1414
|
-
this.reversescale = !this.reversescale;
|
|
1415
|
-
this.plotService.setReverseScale(this.reversescale);
|
|
1416
|
-
}
|
|
1417
|
-
/**
|
|
1418
|
-
* Select a color map: called by the color map dropdown button
|
|
1419
|
-
* @param colormapNode
|
|
1420
|
-
*/
|
|
1421
|
-
selectColormap(colormapNode) {
|
|
1422
|
-
if (!colormapNode.children) {
|
|
1423
|
-
this.plotService.setColormap(colormapNode);
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
738
|
/** Open the Channels & Histogram dialog (toolbar button). */
|
|
1427
739
|
openChannelHistogram() {
|
|
1428
740
|
this.showChannelHistogram = true;
|
|
@@ -1446,15 +758,10 @@ export class VisualizerComponent {
|
|
|
1446
758
|
this.regionEditorWidth = width && width > 0 ? `${Math.round(width)}px` : '25vw';
|
|
1447
759
|
this.showRegionEditor = true;
|
|
1448
760
|
}
|
|
1449
|
-
/**
|
|
1450
|
-
* Called by the stack/single image dropdown button
|
|
1451
|
-
* @param showstack
|
|
1452
|
-
*/
|
|
761
|
+
/** The toolbar's Single image / Stack toggle. */
|
|
1453
762
|
selectStackOption(selectedStackOption) {
|
|
1454
763
|
const showstack = selectedStackOption.val === 'true';
|
|
1455
|
-
this.
|
|
1456
|
-
console.log('selected stack option' + JSON.stringify(this.selectedStackOption));
|
|
1457
|
-
this.stackLoading = showstack;
|
|
764
|
+
this.stackLoading.set(showstack);
|
|
1458
765
|
this.state.setImageLoading(!showstack);
|
|
1459
766
|
// Stack mode is always a 2D heatmap — reset surface mode if active
|
|
1460
767
|
if (!this.isHeatmap) {
|
|
@@ -1465,19 +772,14 @@ export class VisualizerComponent {
|
|
|
1465
772
|
}
|
|
1466
773
|
this.plotService.setShowStack(showstack);
|
|
1467
774
|
}
|
|
1468
|
-
|
|
775
|
+
/** Clamp the slice index into [0, maxIndex] and push it to the renderer. */
|
|
776
|
+
updateZIndex() {
|
|
1469
777
|
if (this.zIndex > this.maxIndex) {
|
|
1470
778
|
this.zIndex = this.maxIndex;
|
|
1471
779
|
}
|
|
1472
780
|
if (this.zIndex < 0) {
|
|
1473
781
|
this.zIndex = 0;
|
|
1474
782
|
}
|
|
1475
|
-
if (urls) {
|
|
1476
|
-
if (this.zIndex > urls?.length) {
|
|
1477
|
-
this.zIndex = 0;
|
|
1478
|
-
this.maxIndex = urls.length - 1;
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
783
|
this.plotService.setZIndex(this.zIndex);
|
|
1482
784
|
}
|
|
1483
785
|
/**
|
|
@@ -1490,11 +792,8 @@ export class VisualizerComponent {
|
|
|
1490
792
|
return;
|
|
1491
793
|
this.zIndex = z;
|
|
1492
794
|
this.scrubber.commit(z);
|
|
1493
|
-
// Keep the intensity inset
|
|
1494
|
-
|
|
1495
|
-
if (this.hasProfiles && this.isImageView && this.imageInfo) {
|
|
1496
|
-
this.plotService.ensureIntensitySampling(this.imageInfo, z);
|
|
1497
|
-
}
|
|
795
|
+
// Keep the intensity inset sampling the displayed slice (Image view).
|
|
796
|
+
this.inset?.sliceCommitted(z);
|
|
1498
797
|
}
|
|
1499
798
|
/**
|
|
1500
799
|
* Live (debounced) scrub while dragging the Image-view z-slider: update the
|
|
@@ -1507,27 +806,6 @@ export class VisualizerComponent {
|
|
|
1507
806
|
this.zIndex = z;
|
|
1508
807
|
this.scrubber.scrub(z);
|
|
1509
808
|
}
|
|
1510
|
-
/**
|
|
1511
|
-
* Keyboard stack navigation: ←/→ step the z-slice in the Image view, the same
|
|
1512
|
-
* way the slider does. Only active for a loaded stack in Image view, and
|
|
1513
|
-
* ignored while a form field is focused so typing isn't hijacked. Up/Down are
|
|
1514
|
-
* left to OpenSeadragon (panning).
|
|
1515
|
-
*/
|
|
1516
|
-
onKeydown(e) {
|
|
1517
|
-
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight')
|
|
1518
|
-
return;
|
|
1519
|
-
if (!this.imageInfo?.isStack || !this.isImageView)
|
|
1520
|
-
return;
|
|
1521
|
-
const t = e.target;
|
|
1522
|
-
if (t && (/^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName) || t.isContentEditable))
|
|
1523
|
-
return;
|
|
1524
|
-
// The slice slider handles arrows natively when focused (also a +1 step) —
|
|
1525
|
-
// skip here so we don't double-step it.
|
|
1526
|
-
if (t && (t.getAttribute('role') === 'slider' || t.closest('.p-slider')))
|
|
1527
|
-
return;
|
|
1528
|
-
e.preventDefault();
|
|
1529
|
-
this.stepSlice(e.key === 'ArrowRight' ? 1 : -1);
|
|
1530
|
-
}
|
|
1531
809
|
/** Move the displayed slice by `delta`, clamped to the stack bounds. */
|
|
1532
810
|
stepSlice(delta) {
|
|
1533
811
|
const next = Math.min(this.maxIndex, Math.max(0, this.zIndex + delta));
|
|
@@ -1545,80 +823,34 @@ export class VisualizerComponent {
|
|
|
1545
823
|
if (this.imageInfo) {
|
|
1546
824
|
this.state.setImageInfo(this.imageInfo);
|
|
1547
825
|
}
|
|
1548
|
-
else if (this.
|
|
826
|
+
else if (this.spatial.dataset && (isSpatialOmics(this.plotType) || isSpatialOmics3d(this.plotType))) {
|
|
1549
827
|
// A spatial dataset that brings no image, opened with no image loaded (a host's
|
|
1550
828
|
// first view): there is no image info to re-drive, so draw the spatial mode itself.
|
|
1551
|
-
void this.
|
|
829
|
+
void this.render.plotWithoutImage(this.spatial.dataset);
|
|
1552
830
|
}
|
|
1553
831
|
else {
|
|
1554
832
|
this.plotService.reloadAndPlot();
|
|
1555
833
|
}
|
|
1556
834
|
}
|
|
1557
|
-
/**
|
|
1558
|
-
* Draw a spatial mode with no image behind it: the observations alone, framed on their own
|
|
1559
|
-
* extent, as the seqFISH example shows them. The image info is a placeholder naming the
|
|
1560
|
-
* dataset, so regions drawn here are kept per dataset like any image's.
|
|
1561
|
-
*/
|
|
1562
|
-
async plotSpatialWithoutImage(dataset) {
|
|
1563
|
-
if (!this.viewReady) {
|
|
1564
|
-
// A host that creates the visualizer AFTER publishing the dataset (jit-ui opening a file
|
|
1565
|
-
// from its tree) has it replayed into ngOnInit, before the template and its plot div
|
|
1566
|
-
// exist; drawing now finds no target. Draw once the view is ready (onViewReady).
|
|
1567
|
-
this.pendingSpatialDraw = dataset;
|
|
1568
|
-
return;
|
|
1569
|
-
}
|
|
1570
|
-
// The same generation as the image pipeline: a newer image or dataset supersedes this
|
|
1571
|
-
// draw, and a superseded draw neither reports nor releases the newer one's loading state.
|
|
1572
|
-
const token = ++this.renderToken;
|
|
1573
|
-
const div = document.getElementById(this.plotDivName);
|
|
1574
|
-
const info = {
|
|
1575
|
-
isGrayscale: false, trueImageSize: [0, 0], urls: [], isStack: false, showStack: false,
|
|
1576
|
-
scaleRatio: true, fileName: `spatial:${dataset.id}`, imageMeta: [],
|
|
1577
|
-
};
|
|
1578
|
-
let failure = null;
|
|
1579
|
-
try {
|
|
1580
|
-
// A backend that cannot draw (no WebGPU, no plot target) resolves false rather than throwing.
|
|
1581
|
-
if (!(await this.plotService.plot(this.plotDivName, null, info, div?.offsetHeight || 500, this.plotType))) {
|
|
1582
|
-
failure = 'the renderer could not start';
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
catch (err) {
|
|
1586
|
-
failure = err;
|
|
1587
|
-
}
|
|
1588
|
-
if (token !== this.renderToken)
|
|
1589
|
-
return;
|
|
1590
|
-
if (failure) {
|
|
1591
|
-
console.warn('[visualizer] could not draw the spatial dataset', failure);
|
|
1592
|
-
const e = failure;
|
|
1593
|
-
this.messageService.add({
|
|
1594
|
-
key: this.vizAlertToastKey,
|
|
1595
|
-
severity: 'error',
|
|
1596
|
-
summary: 'Could not draw the dataset',
|
|
1597
|
-
// An image-less dataset has no tissue image to fall back on, so say so rather than
|
|
1598
|
-
// leave an empty canvas that looks finished.
|
|
1599
|
-
detail: `${dataset.name ?? dataset.id}: ${e?.message ?? String(failure)}.`,
|
|
1600
|
-
});
|
|
1601
|
-
}
|
|
1602
|
-
this.state.setImageLoading(false);
|
|
1603
|
-
this.cdr.detectChanges();
|
|
1604
|
-
}
|
|
1605
835
|
cancelLoading() {
|
|
1606
836
|
// Stop any in-flight frame streaming (napari-js volume/surface preload) so the fetch loops
|
|
1607
837
|
// actually abort — clearing the flag alone only routed to Plotly and left napari fetching.
|
|
1608
838
|
this.plotService.cancelLoading?.();
|
|
1609
|
-
|
|
1610
|
-
|
|
839
|
+
// The cancelled render is superseded, not just aborted: its aborted load must not
|
|
840
|
+
// come back as "Could not draw the image", nor apply ROIs for an image left unshown.
|
|
841
|
+
this.render.cancel();
|
|
842
|
+
this.stackLoading.set(false);
|
|
843
|
+
this.imgLoading.set(false);
|
|
1611
844
|
this.state.setImageLoading(false);
|
|
1612
845
|
this.zIndex = 0;
|
|
1613
846
|
this.plotService.setZIndex(this.zIndex);
|
|
1614
|
-
this.running = false;
|
|
1615
847
|
this.plotService.setStackLoading(false);
|
|
1616
848
|
}
|
|
1617
849
|
downloadImage() {
|
|
1618
850
|
this.plotService.downloadImage();
|
|
1619
851
|
}
|
|
1620
852
|
autoscaleImage() {
|
|
1621
|
-
this.plotService.
|
|
853
|
+
this.plotService.fitToView();
|
|
1622
854
|
}
|
|
1623
855
|
/** Toolbar pixel/smooth toggle: flip smoothing and apply to the active backend. */
|
|
1624
856
|
onToggleImageSmoothing() {
|
|
@@ -1629,53 +861,7 @@ export class VisualizerComponent {
|
|
|
1629
861
|
this.plotService.resetAxes();
|
|
1630
862
|
}
|
|
1631
863
|
toggleDragMode(mode) {
|
|
1632
|
-
|
|
1633
|
-
// brush class.
|
|
1634
|
-
this.brushClass = null;
|
|
1635
|
-
// Toggle off if the same mode is re-selected.
|
|
1636
|
-
this.applyDragMode(this.activeDragMode === mode ? null : mode);
|
|
1637
|
-
}
|
|
1638
|
-
/** Arm `mode` (or nothing) across every tool, keeping the toolbar in step. */
|
|
1639
|
-
applyDragMode(mode) {
|
|
1640
|
-
this.activeDragMode = mode;
|
|
1641
|
-
const active = this.activeDragMode;
|
|
1642
|
-
// Record the armed tool in the shared session store.
|
|
1643
|
-
this.session.setActiveTool(active);
|
|
1644
|
-
// Region draw/select run through the renderer's region overlay.
|
|
1645
|
-
this.plotService
|
|
1646
|
-
.getRegionOverlay()
|
|
1647
|
-
?.setMode(this.isRegionMode(active) ? active : 'none');
|
|
1648
|
-
// Viewport drag modes (pan/box-zoom). Region modes own the drag mode via
|
|
1649
|
-
// the overlay, so don't also set one here.
|
|
1650
|
-
if (!this.isRegionMode(active)) {
|
|
1651
|
-
const viewportDrag = active === 'pan' || active === 'zoom';
|
|
1652
|
-
this.plotService.setDragMode(viewportDrag ? active : false);
|
|
1653
|
-
}
|
|
1654
|
-
// On-canvas tool overlays.
|
|
1655
|
-
this.plotService.setZoomToBoxMode(active === 'zoomToBox');
|
|
1656
|
-
this.plotService.setWandMode(active === 'wand', { sensitivity: this.wandSensitivity });
|
|
1657
|
-
this.plotService.setBrushMode(active === 'brush', { size: this.brushSize, ...this.brushClass });
|
|
1658
|
-
this.plotService.setSamPointMode(active === 'samPoint');
|
|
1659
|
-
// Leaving point mode dismisses any lingering status toast.
|
|
1660
|
-
if (active !== 'samPoint')
|
|
1661
|
-
this.hideSamToast();
|
|
1662
|
-
this.plotService.setVertexEraserMode(active === 'eraseVertex');
|
|
1663
|
-
if (active === 'eraseVertex') {
|
|
1664
|
-
this.plotService.setVertexEraserRadius(this.vertexEraserRadius);
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
/** Region draw/select/edit modes routed through the region overlay. The
|
|
1668
|
-
* The vertex tools (drawpolygon/addpoint/deletepoint/move) are
|
|
1669
|
-
* handled by the OpenSeadragon overlay; Plotly's overlay maps them to no-op. */
|
|
1670
|
-
isRegionMode(mode) {
|
|
1671
|
-
return (mode === 'drawrect' ||
|
|
1672
|
-
mode === 'drawclosedpath' ||
|
|
1673
|
-
mode === 'drawopenpath' ||
|
|
1674
|
-
mode === 'select' ||
|
|
1675
|
-
mode === 'drawpolygon' ||
|
|
1676
|
-
mode === 'addpoint' ||
|
|
1677
|
-
mode === 'deletepoint' ||
|
|
1678
|
-
mode === 'move');
|
|
864
|
+
this.tools.toggle(mode);
|
|
1679
865
|
}
|
|
1680
866
|
/** Live-update the isosurface as the range slider moves. The control is only
|
|
1681
867
|
* available when the active backend renders isosurfaces (ISOSURFACE mode). */
|
|
@@ -1686,22 +872,13 @@ export class VisualizerComponent {
|
|
|
1686
872
|
this.plotService.getIsosurfaceControls()?.setIsoRange(values[0], values[1]);
|
|
1687
873
|
}
|
|
1688
874
|
onWandSensitivityChange(value) {
|
|
1689
|
-
|
|
1690
|
-
return;
|
|
1691
|
-
this.wandSensitivity = value;
|
|
1692
|
-
this.plotService.setWandOptions({ sensitivity: value });
|
|
875
|
+
this.tools.setWandSensitivity(value);
|
|
1693
876
|
}
|
|
1694
877
|
onBrushSizeChange(value) {
|
|
1695
|
-
|
|
1696
|
-
return;
|
|
1697
|
-
this.brushSize = value;
|
|
1698
|
-
this.plotService.setBrushOptions({ size: value });
|
|
878
|
+
this.tools.setBrushSize(value);
|
|
1699
879
|
}
|
|
1700
880
|
onVertexEraserRadiusChange(value) {
|
|
1701
|
-
|
|
1702
|
-
return;
|
|
1703
|
-
this.vertexEraserRadius = value;
|
|
1704
|
-
this.plotService.setVertexEraserRadius(value);
|
|
881
|
+
this.tools.setVertexEraserRadius(value);
|
|
1705
882
|
}
|
|
1706
883
|
zoomIn() {
|
|
1707
884
|
this.plotService.zoomIn();
|
|
@@ -1721,281 +898,61 @@ export class VisualizerComponent {
|
|
|
1721
898
|
this.plotService.redo();
|
|
1722
899
|
}
|
|
1723
900
|
// ── Region set-operations on the current selection (jit-ui#85) ──────────
|
|
1724
|
-
/** The currently-selected regions (live store instances). */
|
|
1725
|
-
get selectedRegions() {
|
|
1726
|
-
const all = this.plotService.getRegions();
|
|
1727
|
-
return this.selectedIndices
|
|
1728
|
-
.map((i) => all[i])
|
|
1729
|
-
.filter((r) => !!r);
|
|
1730
|
-
}
|
|
1731
|
-
/** Regions eligible for set-ops: closed areas (rect / closed polygon /
|
|
1732
|
-
* multi-polygon), excluding intensity-profile lines. */
|
|
1733
|
-
opEligible(regions) {
|
|
1734
|
-
return regions.filter((r) => r.kind !== 'profile' && (r.bounds instanceof Rectangle ||
|
|
1735
|
-
r.bounds instanceof MultiPolygon ||
|
|
1736
|
-
(r.bounds instanceof Polygon && r.bounds.closed !== false)));
|
|
1737
|
-
}
|
|
1738
|
-
/** Image pixel dimensions for the raster ops; falls back to the selection's
|
|
1739
|
-
* extent when the image size isn't known (keeps clamping sane). */
|
|
1740
|
-
opImageDims(regions) {
|
|
1741
|
-
let [w, h] = this.imageInfo?.trueImageSize ?? [0, 0];
|
|
1742
|
-
if (!(w > 0) || !(h > 0)) {
|
|
1743
|
-
let maxX = 0, maxY = 0;
|
|
1744
|
-
const scan = (xs, ys) => {
|
|
1745
|
-
for (const x of xs)
|
|
1746
|
-
maxX = Math.max(maxX, x);
|
|
1747
|
-
for (const y of ys)
|
|
1748
|
-
maxY = Math.max(maxY, y);
|
|
1749
|
-
};
|
|
1750
|
-
for (const r of regions) {
|
|
1751
|
-
const b = r.bounds;
|
|
1752
|
-
if (b instanceof Rectangle)
|
|
1753
|
-
scan([b.x + b.width], [b.y + b.height]);
|
|
1754
|
-
else if (b instanceof Polygon)
|
|
1755
|
-
scan(b.xpoints, b.ypoints);
|
|
1756
|
-
else if (b instanceof MultiPolygon)
|
|
1757
|
-
for (const p of b.polygons)
|
|
1758
|
-
scan(p.xpoints, p.ypoints);
|
|
1759
|
-
}
|
|
1760
|
-
w = Math.max(w, Math.ceil(maxX) + 2);
|
|
1761
|
-
h = Math.max(h, Math.ceil(maxY) + 2);
|
|
1762
|
-
}
|
|
1763
|
-
return { w, h };
|
|
1764
|
-
}
|
|
1765
|
-
/** True when ≥2 eligible regions are selected (Merge available). */
|
|
1766
|
-
get canMergeRegions() { return this.opEligible(this.selectedRegions).length >= 2; }
|
|
1767
|
-
/** True when any selected region is multi-part (Ungroup available). */
|
|
1768
|
-
get canUngroupRegions() { return this.selectedRegions.some((r) => this.regionOps.canUngroup(r)); }
|
|
1769
|
-
/** True when ≥1 eligible region is selected (Inverse / Simplify available). */
|
|
1770
|
-
get hasEligibleSelection() { return this.opEligible(this.selectedRegions).length >= 1; }
|
|
1771
|
-
/** Select every region on the image (excludes intensity-profile lines). */
|
|
1772
901
|
selectAllRegions() {
|
|
1773
|
-
|
|
1774
|
-
const indices = [];
|
|
1775
|
-
all.forEach((r, i) => { if (r.kind !== 'profile')
|
|
1776
|
-
indices.push(i); });
|
|
1777
|
-
this.plotService.setSelectedShapeIndices(indices);
|
|
902
|
+
this.regionActions.selectAll();
|
|
1778
903
|
}
|
|
1779
|
-
/** Merge the selected regions into a single (possibly multi-part) region. */
|
|
1780
904
|
mergeRegions() {
|
|
1781
|
-
|
|
1782
|
-
if (sel.length < 2)
|
|
1783
|
-
return;
|
|
1784
|
-
const { w, h } = this.opImageDims(sel);
|
|
1785
|
-
const merged = this.regionOps.merge(sel, w, h);
|
|
1786
|
-
if (merged)
|
|
1787
|
-
this.replaceRegions(sel, [merged]);
|
|
905
|
+
this.regionActions.merge();
|
|
1788
906
|
}
|
|
1789
|
-
/** Split each selected multi-part region into one region per part. */
|
|
1790
907
|
ungroupRegions() {
|
|
1791
|
-
|
|
1792
|
-
if (sel.length === 0)
|
|
1793
|
-
return;
|
|
1794
|
-
const results = [];
|
|
1795
|
-
for (const r of sel)
|
|
1796
|
-
results.push(...this.regionOps.ungroup(r));
|
|
1797
|
-
this.replaceRegions(sel, results);
|
|
908
|
+
this.regionActions.ungroup();
|
|
1798
909
|
}
|
|
1799
|
-
/** Replace the selection with its inverse inside the image rectangle. */
|
|
1800
910
|
inverseRegions() {
|
|
1801
|
-
|
|
1802
|
-
if (sel.length === 0)
|
|
1803
|
-
return;
|
|
1804
|
-
const { w, h } = this.opImageDims(sel);
|
|
1805
|
-
const inv = this.regionOps.inverse(sel, w, h);
|
|
1806
|
-
if (!inv) {
|
|
1807
|
-
this.messageService.add({
|
|
1808
|
-
key: this.resultToastKey,
|
|
1809
|
-
severity: 'warn', summary: 'Inverse',
|
|
1810
|
-
detail: 'Nothing to invert — select one or more closed regions first.',
|
|
1811
|
-
});
|
|
1812
|
-
return;
|
|
1813
|
-
}
|
|
1814
|
-
this.replaceRegions(sel, [inv]);
|
|
911
|
+
this.regionActions.inverse();
|
|
1815
912
|
}
|
|
1816
|
-
/** Douglas–Peucker simplify each selected region by `thresholdPx`. */
|
|
1817
913
|
simplifyRegions(thresholdPx) {
|
|
1818
|
-
|
|
1819
|
-
if (sel.length === 0)
|
|
1820
|
-
return;
|
|
1821
|
-
this.replaceRegions(sel, sel.map((r) => this.regionOps.simplify(r, thresholdPx)));
|
|
1822
|
-
this.displaySimplifyDialog = false;
|
|
914
|
+
this.regionActions.simplify(thresholdPx);
|
|
1823
915
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
/**
|
|
1827
|
-
* Commit a set-op: drop the `remove` regions, append `add`, and select the
|
|
1828
|
-
* results. Goes through setRegions so it's undo-tracked and re-rendered;
|
|
1829
|
-
* profile lines and unselected regions are preserved.
|
|
1830
|
-
*/
|
|
1831
|
-
replaceRegions(remove, add) {
|
|
1832
|
-
if (add.length === 0)
|
|
1833
|
-
return;
|
|
1834
|
-
const removeSet = new Set(remove);
|
|
1835
|
-
const kept = this.plotService.getRegions().filter((r) => !removeSet.has(r));
|
|
1836
|
-
this.plotService.setRegions([...kept, ...add]); // mints ids on `add`, records undo
|
|
1837
|
-
const stored = this.plotService.getRegions();
|
|
1838
|
-
const sel = add.map((r) => stored.indexOf(r)).filter((i) => i >= 0);
|
|
1839
|
-
this.plotService.setSelectedShapeIndices(sel);
|
|
916
|
+
openSimplifyDialog() {
|
|
917
|
+
this.regionActions.openSimplifyDialog();
|
|
1840
918
|
}
|
|
1841
919
|
/** Box-prompted SAM segmentation of the drawn rectangles (jit-ui#90). A sticky
|
|
1842
920
|
* `sam` toast shows live status + a download progress bar (first run pulls the
|
|
1843
921
|
* encoder, ~170 MB); it stays open until the run finishes (bar hits 100%). */
|
|
1844
922
|
async segmentRegions() {
|
|
1845
|
-
await this.
|
|
923
|
+
await this.segmentation.run('SAM', this.samTool, () => this.plotService.segmentRectangles());
|
|
1846
924
|
}
|
|
1847
925
|
/** Auto-segment cells inside each drawn rectangle with cellpose-SAM, client-side
|
|
1848
926
|
* (jit-ui#90). Each box is cropped (browser slide-crop) then run through the
|
|
1849
927
|
* cellpose-js model; the same sticky `sam` toast + progress bar is reused. */
|
|
1850
928
|
async segmentCellpose() {
|
|
1851
|
-
await this.
|
|
929
|
+
await this.segmentation.run('Cellpose', this.cellSegmentTool, () => this.plotService.segmentRectanglesCellpose());
|
|
1852
930
|
}
|
|
1853
|
-
|
|
1854
|
-
* Parameters currently bound by the generic parameter dialog, for whichever
|
|
1855
|
-
* tool is open. Seeded lazily: a tool's defaults are only asked for when the
|
|
1856
|
-
* user first touches it, so registering a tool costs nothing until used.
|
|
1857
|
-
*/
|
|
931
|
+
// ── contributed tool parameters (see ToolParamsModel) ─────────────────
|
|
1858
932
|
paramsFor(toolId) {
|
|
1859
|
-
|
|
1860
|
-
if (!tool)
|
|
1861
|
-
return {};
|
|
1862
|
-
if (!this.toolParams[toolId]) {
|
|
1863
|
-
// Through `seedParams`, not `defaultParams` directly: the active
|
|
1864
|
-
// checkpoint's own `ToolModelOption.defaults` have to be merged on top.
|
|
1865
|
-
// Calling `defaultParams` here bypassed them, so the very first run of a
|
|
1866
|
-
// tool used the tool's baseline thresholds and tiling rather than the
|
|
1867
|
-
// model's — and it only corrected itself once the user switched
|
|
1868
|
-
// checkpoints or hit Reset. Every entry point now goes through one path.
|
|
1869
|
-
this.toolParams[toolId] = this.seedParams(tool, this.modelIdFor(tool));
|
|
1870
|
-
}
|
|
1871
|
-
return this.toolParams[toolId];
|
|
933
|
+
return this.toolParams.paramsFor(toolId);
|
|
1872
934
|
}
|
|
1873
|
-
/** Active checkpoint for a tool, falling back to the tool's own default. */
|
|
1874
|
-
modelIdFor(tool) {
|
|
1875
|
-
return this.toolModelIds[tool.id] ?? tool.defaultModelId();
|
|
1876
|
-
}
|
|
1877
|
-
/** Switching checkpoint re-seeds the parameters, since the defaults belong to
|
|
1878
|
-
* the model rather than to the tool. */
|
|
1879
935
|
onToolModelChange(e) {
|
|
1880
|
-
|
|
1881
|
-
if (!tool)
|
|
1882
|
-
return;
|
|
1883
|
-
// Reassigned rather than mutated so the toolbar's ngOnChanges sees a new
|
|
1884
|
-
// reference and rebuilds that tool's model menu with the check mark moved.
|
|
1885
|
-
this.toolModelIds = { ...this.toolModelIds, [e.toolId]: e.modelId };
|
|
1886
|
-
tool.onModelChange?.(e.modelId);
|
|
1887
|
-
this.toolParams[e.toolId] = this.seedParams(tool, e.modelId);
|
|
1888
|
-
}
|
|
1889
|
-
/** A tool's defaults for a checkpoint, with that checkpoint's own overrides
|
|
1890
|
-
* applied on top — per-model defaults beat per-tool ones. */
|
|
1891
|
-
seedParams(tool, modelId) {
|
|
1892
|
-
const model = tool.models().find((m) => m.id === modelId);
|
|
1893
|
-
return { ...tool.defaultParams(modelId), ...(model?.defaults ?? {}) };
|
|
1894
|
-
}
|
|
1895
|
-
/**
|
|
1896
|
-
* Narrow a param spec for the template.
|
|
1897
|
-
*
|
|
1898
|
-
* `*ngSwitchCase` does not narrow a discriminated union in Angular's template
|
|
1899
|
-
* type checker the way a `switch` does in TypeScript, so the template asks for
|
|
1900
|
-
* the variant it has already matched on.
|
|
1901
|
-
*/
|
|
1902
|
-
numberParam(p) {
|
|
1903
|
-
return p;
|
|
1904
|
-
}
|
|
1905
|
-
selectParam(p) {
|
|
1906
|
-
return p;
|
|
936
|
+
this.toolParams.setModel(e.toolId, e.modelId);
|
|
1907
937
|
}
|
|
1908
938
|
openToolParams(toolId) {
|
|
1909
|
-
this.
|
|
1910
|
-
this.openParamsToolId = toolId;
|
|
939
|
+
this.toolParams.open(toolId);
|
|
1911
940
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
get openParamsTool() {
|
|
1915
|
-
return this.contributedTools.find((t) => t.id === this.openParamsToolId);
|
|
941
|
+
closeToolParams() {
|
|
942
|
+
this.toolParams.close();
|
|
1916
943
|
}
|
|
1917
944
|
resetToolParams(toolId) {
|
|
1918
|
-
|
|
1919
|
-
if (tool)
|
|
1920
|
-
this.toolParams[toolId] = this.seedParams(tool, this.modelIdFor(tool));
|
|
945
|
+
this.toolParams.reset(toolId);
|
|
1921
946
|
}
|
|
1922
947
|
/** Run a contributed tool over the current view. No prompt: these sweep the
|
|
1923
948
|
* whole view rather than being pointed at something. Reuses the shared
|
|
1924
949
|
* segmentation toast so progress reads the same as the SAM/cellpose tools. */
|
|
1925
950
|
async runTool(toolId) {
|
|
1926
|
-
const tool = this.
|
|
951
|
+
const tool = this.toolParams.find(toolId);
|
|
1927
952
|
if (!tool)
|
|
1928
953
|
return;
|
|
1929
|
-
const params =
|
|
1930
|
-
await this.
|
|
1931
|
-
}
|
|
1932
|
-
/** Shared driver for the box-prompt segment tools: wires the tool's status +
|
|
1933
|
-
* download progress into the sticky `sam` toast, runs `op`, and reports the
|
|
1934
|
-
* region count. Keeps the toast open until the run settles (bar hits 100%). */
|
|
1935
|
-
async runSegmentWithToast(label,
|
|
1936
|
-
// Observables rather than BehaviorSubjects: a contributed tool implements
|
|
1937
|
-
// ToolProgress, and requiring a concrete subject would force every plugin
|
|
1938
|
-
// to expose its internals just to drive this toast.
|
|
1939
|
-
tool, op) {
|
|
1940
|
-
this.samStatus = 'Starting…';
|
|
1941
|
-
this.samProgress = 0;
|
|
1942
|
-
this.samDownloading = false;
|
|
1943
|
-
this.samBusy = true;
|
|
1944
|
-
const psub = tool.progress$.subscribe((f) => {
|
|
1945
|
-
this.samDownloading = f >= 0 && f < 1;
|
|
1946
|
-
if (f >= 0)
|
|
1947
|
-
this.samProgress = Math.min(100, Math.round(f * 100));
|
|
1948
|
-
this.cdr.detectChanges();
|
|
1949
|
-
});
|
|
1950
|
-
// Tracked here rather than read off the subject at the end: `tool` is only
|
|
1951
|
-
// an Observable pair now, so there is no `.value` to sample once the run
|
|
1952
|
-
// settles. The last non-empty status is what the result toast reports.
|
|
1953
|
-
let lastStatus = '';
|
|
1954
|
-
const ssub = tool.status$.subscribe((m) => {
|
|
1955
|
-
if (m) {
|
|
1956
|
-
lastStatus = m;
|
|
1957
|
-
this.samStatus = m;
|
|
1958
|
-
this.cdr.detectChanges();
|
|
1959
|
-
}
|
|
1960
|
-
});
|
|
1961
|
-
this.showSamToast(label);
|
|
1962
|
-
try {
|
|
1963
|
-
const n = await op();
|
|
1964
|
-
this.messageService.add({
|
|
1965
|
-
key: this.resultToastKey,
|
|
1966
|
-
severity: n > 0 ? 'success' : 'warn',
|
|
1967
|
-
summary: label,
|
|
1968
|
-
detail: lastStatus || (n > 0 ? `Added ${n} region(s).` : 'No regions added.'),
|
|
1969
|
-
});
|
|
1970
|
-
}
|
|
1971
|
-
catch (e) {
|
|
1972
|
-
this.messageService.add({
|
|
1973
|
-
key: this.resultToastKey,
|
|
1974
|
-
severity: 'error', summary: `${label} failed`, detail: String(e),
|
|
1975
|
-
});
|
|
1976
|
-
}
|
|
1977
|
-
finally {
|
|
1978
|
-
psub.unsubscribe();
|
|
1979
|
-
ssub.unsubscribe();
|
|
1980
|
-
this.hideSamToast();
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
/** Show the shared sticky `sam` toast once (idempotent — re-adding would stack
|
|
1984
|
-
* a new toast on every point click). */
|
|
1985
|
-
showSamToast(summary) {
|
|
1986
|
-
if (this.samToastShown)
|
|
1987
|
-
return;
|
|
1988
|
-
this.samToastShown = true;
|
|
1989
|
-
this.messageService.add({ key: this.samToastKey, sticky: true, severity: 'info', summary });
|
|
1990
|
-
}
|
|
1991
|
-
/** Dismiss the shared `sam` toast and reset its progress/spinner state. */
|
|
1992
|
-
hideSamToast() {
|
|
1993
|
-
this.samToastShown = false;
|
|
1994
|
-
this.samBusy = false;
|
|
1995
|
-
this.samDownloading = false;
|
|
1996
|
-
this.samProgress = 0;
|
|
1997
|
-
this.messageService.clear(this.samToastKey);
|
|
1998
|
-
this.cdr.detectChanges();
|
|
954
|
+
const params = this.toolParams.runParams(tool);
|
|
955
|
+
await this.segmentation.run(tool.label, tool.progress, () => tool.run(this.plotService, params));
|
|
1999
956
|
}
|
|
2000
957
|
/** Pick the SAM model the segment tools use (jit-ui#90 P1). */
|
|
2001
958
|
onSamModelChange(id) {
|
|
@@ -2010,192 +967,28 @@ export class VisualizerComponent {
|
|
|
2010
967
|
toPolygonRegion() {
|
|
2011
968
|
this.plotService.getRegionOverlay()?.setSelectedBezier(false);
|
|
2012
969
|
}
|
|
2013
|
-
/**
|
|
2014
|
-
* Immediate region actions on the current selection (jit-ui#85): merge,
|
|
2015
|
-
* ungroup, inverse, simplify, the Bézier conversions, and delete — the
|
|
2016
|
-
* one-shot geometry transforms, grouped apart from the tool/mode toggles and
|
|
2017
|
-
* shown only when something is selected. Each item is gated on what the
|
|
2018
|
-
* selection supports.
|
|
2019
|
-
*/
|
|
2020
|
-
buildRegionActionItems() {
|
|
2021
|
-
const all = this.plotService.getRegions();
|
|
2022
|
-
const selectable = all.filter((r) => r.kind !== 'profile');
|
|
2023
|
-
if (selectable.length === 0)
|
|
2024
|
-
return [];
|
|
2025
|
-
const items = [];
|
|
2026
|
-
// Select all — available whenever the image has regions, regardless of the
|
|
2027
|
-
// current selection.
|
|
2028
|
-
items.push({
|
|
2029
|
-
label: 'Select all regions', icon: 'pi pi-check-square',
|
|
2030
|
-
command: () => this.selectAllRegions(),
|
|
2031
|
-
});
|
|
2032
|
-
const selAll = this.selectedRegions;
|
|
2033
|
-
if (selAll.length === 0)
|
|
2034
|
-
return items;
|
|
2035
|
-
const eligible = this.opEligible(selAll);
|
|
2036
|
-
if (eligible.length >= 2) {
|
|
2037
|
-
items.push({ label: 'Merge / group', icon: 'pi pi-link', command: () => this.mergeRegions() });
|
|
2038
|
-
}
|
|
2039
|
-
if (selAll.some((r) => this.regionOps.canUngroup(r))) {
|
|
2040
|
-
items.push({ label: 'Ungroup', icon: 'pi pi-sitemap', command: () => this.ungroupRegions() });
|
|
2041
|
-
}
|
|
2042
|
-
if (eligible.length >= 1) {
|
|
2043
|
-
items.push({ label: 'Inverse', icon: 'pi pi-clone', command: () => this.inverseRegions() }, {
|
|
2044
|
-
label: 'Simplify', icon: 'pi pi-chart-line',
|
|
2045
|
-
items: [
|
|
2046
|
-
{ label: 'Light (1 px)', command: () => this.simplifyRegions(1) },
|
|
2047
|
-
{ label: 'Medium (3 px)', command: () => this.simplifyRegions(3) },
|
|
2048
|
-
{ label: 'Strong (8 px)', command: () => this.simplifyRegions(8) },
|
|
2049
|
-
{ separator: true },
|
|
2050
|
-
{ label: 'Custom…', command: () => this.openSimplifyDialog() },
|
|
2051
|
-
],
|
|
2052
|
-
});
|
|
2053
|
-
}
|
|
2054
|
-
// Bézier conversions are vertex-level edits — Image (OpenSeadragon) view
|
|
2055
|
-
// only — gated on the selection's current form.
|
|
2056
|
-
if (this.basePlotType === PlotType.IMAGE) {
|
|
2057
|
-
const hasStraight = selAll.some((r) => (r.bounds instanceof Polygon && !r.bounds.bezier) || r.bounds instanceof Rectangle);
|
|
2058
|
-
const hasBezier = selAll.some((r) => r.bounds instanceof Polygon && r.bounds.bezier);
|
|
2059
|
-
if (hasStraight) {
|
|
2060
|
-
items.push({ label: 'Convert to Bézier', icon: 'to-bezier-icon', command: () => this.toBezierRegion() });
|
|
2061
|
-
}
|
|
2062
|
-
if (hasBezier) {
|
|
2063
|
-
items.push({ label: 'Convert to polygon', icon: 'to-polygon-icon', command: () => this.toPolygonRegion() });
|
|
2064
|
-
}
|
|
2065
|
-
}
|
|
2066
|
-
items.push({ label: 'Delete region', icon: 'pi pi-trash', command: () => this.deleteRegion() });
|
|
2067
|
-
return items;
|
|
2068
|
-
}
|
|
970
|
+
/** The right-click menu for the current view, armed mode and selection. */
|
|
2069
971
|
buildContextMenuItems() {
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
// OpenSeadragon-backed Image view (use 'Zoom to box' there instead).
|
|
2083
|
-
if (!this.isImageView) {
|
|
2084
|
-
items.push({
|
|
2085
|
-
label: 'Zoom selection',
|
|
2086
|
-
icon: 'pi pi-search',
|
|
2087
|
-
styleClass: active === 'zoom' ? activeClass : '',
|
|
2088
|
-
command: () => this.toggleDragMode('zoom'),
|
|
2089
|
-
});
|
|
2090
|
-
}
|
|
2091
|
-
items.push({
|
|
2092
|
-
label: 'Zoom to box',
|
|
2093
|
-
icon: 'zoom-box-off-icon',
|
|
2094
|
-
styleClass: active === 'zoomToBox' ? activeClass : '',
|
|
2095
|
-
command: () => this.toggleDragMode('zoomToBox'),
|
|
2096
|
-
});
|
|
2097
|
-
}
|
|
2098
|
-
if (this.isHeatmap) {
|
|
2099
|
-
items.push({
|
|
2100
|
-
label: 'Pan',
|
|
2101
|
-
icon: 'pi pi-arrows-alt',
|
|
2102
|
-
styleClass: active === 'pan' ? activeClass : '',
|
|
2103
|
-
command: () => this.toggleDragMode('pan'),
|
|
2104
|
-
});
|
|
2105
|
-
}
|
|
2106
|
-
else {
|
|
2107
|
-
const s3d = this.activeSurface3dMode;
|
|
2108
|
-
items.push({
|
|
2109
|
-
label: 'Zoom',
|
|
2110
|
-
icon: 'pi pi-search',
|
|
2111
|
-
styleClass: s3d === 'zoom' ? activeClass : '',
|
|
2112
|
-
command: () => this.toggleSurface3dMode('zoom'),
|
|
2113
|
-
}, {
|
|
2114
|
-
label: 'Pan',
|
|
2115
|
-
icon: 'pi pi-arrows-alt',
|
|
2116
|
-
styleClass: s3d === 'pan' ? activeClass : '',
|
|
2117
|
-
command: () => this.toggleSurface3dMode('pan'),
|
|
2118
|
-
}, {
|
|
2119
|
-
label: 'Orbital rotation',
|
|
2120
|
-
icon: 'pi pi-globe',
|
|
2121
|
-
styleClass: s3d === 'orbit' ? activeClass : '',
|
|
2122
|
-
command: () => this.toggleSurface3dMode('orbit'),
|
|
2123
|
-
}, {
|
|
2124
|
-
label: 'Turntable rotation',
|
|
2125
|
-
icon: 'pi pi-sync',
|
|
2126
|
-
styleClass: s3d === 'turntable' ? activeClass : '',
|
|
2127
|
-
command: () => this.toggleSurface3dMode('turntable'),
|
|
2128
|
-
}, { separator: true }, { label: 'Reset camera', icon: 'pi pi-home', command: () => this.resetSurfaceCamera() });
|
|
2129
|
-
}
|
|
2130
|
-
if (this.isHeatmap) {
|
|
2131
|
-
items.push({ label: 'Zoom in', icon: 'pi pi-search-plus', command: () => this.zoomIn() }, { label: 'Zoom out', icon: 'pi pi-search-minus', command: () => this.zoomOut() }, { separator: true }, {
|
|
2132
|
-
label: 'Select',
|
|
2133
|
-
icon: 'pi pi-arrow-up-right',
|
|
2134
|
-
styleClass: active === 'select' ? activeClass : '',
|
|
2135
|
-
command: () => this.toggleDragMode('select'),
|
|
2136
|
-
}, {
|
|
2137
|
-
label: 'Freeform',
|
|
2138
|
-
icon: 'pi pi-pencil',
|
|
2139
|
-
styleClass: active === 'drawclosedpath' ? activeClass : '',
|
|
2140
|
-
command: () => this.toggleDragMode('drawclosedpath'),
|
|
2141
|
-
}, {
|
|
2142
|
-
label: 'Brush',
|
|
2143
|
-
icon: 'brush-icon',
|
|
2144
|
-
styleClass: active === 'brush' ? activeClass : '',
|
|
2145
|
-
command: () => this.toggleDragMode('brush'),
|
|
2146
|
-
}, {
|
|
2147
|
-
label: 'Polyline',
|
|
2148
|
-
icon: 'polyline-icon',
|
|
2149
|
-
styleClass: active === 'drawopenpath' ? activeClass : '',
|
|
2150
|
-
command: () => this.toggleDragMode('drawopenpath'),
|
|
2151
|
-
}, {
|
|
2152
|
-
label: 'Rectangle',
|
|
2153
|
-
icon: 'pi pi-stop',
|
|
2154
|
-
styleClass: active === 'drawrect' ? activeClass : '',
|
|
2155
|
-
command: () => this.toggleDragMode('drawrect'),
|
|
2156
|
-
}, {
|
|
2157
|
-
label: 'Wand',
|
|
2158
|
-
icon: 'wand-icon',
|
|
2159
|
-
styleClass: active === 'wand' ? activeClass : '',
|
|
2160
|
-
command: () => this.toggleDragMode('wand'),
|
|
2161
|
-
}, {
|
|
2162
|
-
label: 'Vertex eraser',
|
|
2163
|
-
icon: 'pi pi-eraser',
|
|
2164
|
-
styleClass: active === 'eraseVertex' ? activeClass : '',
|
|
2165
|
-
command: () => this.toggleDragMode('eraseVertex'),
|
|
2166
|
-
});
|
|
2167
|
-
// Vertex editing runs on the OpenSeadragon overlay, which
|
|
2168
|
-
// backs the Image plot type. Hidden for other 2D types (Plotly), where
|
|
2169
|
-
// these modes are no-ops.
|
|
2170
|
-
if (this.basePlotType === PlotType.IMAGE) {
|
|
2171
|
-
items.push({
|
|
2172
|
-
label: 'Polygon (click vertices)',
|
|
2173
|
-
icon: 'polygon-vertices-icon',
|
|
2174
|
-
styleClass: active === 'drawpolygon' ? activeClass : '',
|
|
2175
|
-
command: () => this.toggleDragMode('drawpolygon'),
|
|
2176
|
-
}, {
|
|
2177
|
-
label: 'Add vertex',
|
|
2178
|
-
icon: 'vertex-add-icon',
|
|
2179
|
-
styleClass: active === 'addpoint' ? activeClass : '',
|
|
2180
|
-
command: () => this.toggleDragMode('addpoint'),
|
|
2181
|
-
}, {
|
|
2182
|
-
label: 'Delete vertex',
|
|
2183
|
-
icon: 'vertex-delete-icon',
|
|
2184
|
-
styleClass: active === 'deletepoint' ? activeClass : '',
|
|
2185
|
-
command: () => this.toggleDragMode('deletepoint'),
|
|
2186
|
-
});
|
|
2187
|
-
}
|
|
2188
|
-
// Bézier conversions + Delete are immediate selection *actions*, now in the
|
|
2189
|
-
// "Selected region(s)" group at the top (see buildRegionActionItems).
|
|
2190
|
-
}
|
|
2191
|
-
return items;
|
|
972
|
+
return buildContextMenu(this.contextMenuState(), this);
|
|
973
|
+
}
|
|
974
|
+
contextMenuState() {
|
|
975
|
+
return {
|
|
976
|
+
isHeatmap: this.isHeatmap,
|
|
977
|
+
basePlotType: this.basePlotType,
|
|
978
|
+
activeDragMode: this.activeDragMode,
|
|
979
|
+
activeSurface3dMode: this.activeSurface3dMode,
|
|
980
|
+
regions: this.plotService.getRegions(),
|
|
981
|
+
selectedIndices: this.regionActions.selectedIndices,
|
|
982
|
+
canUngroup: (r) => this.regionOps.canUngroup(r),
|
|
983
|
+
};
|
|
2192
984
|
}
|
|
985
|
+
/** 3D scene interaction mode (orbit / turntable / pan / zoom); no-op on a 2D-only backend. */
|
|
2193
986
|
toggleSurface3dMode(mode) {
|
|
2194
987
|
this.activeSurface3dMode = mode;
|
|
2195
|
-
this.plotService.setSurfaceDragMode(mode);
|
|
988
|
+
this.plotService.getSurface3dControls()?.setSurfaceDragMode(mode);
|
|
2196
989
|
}
|
|
2197
990
|
resetSurfaceCamera() {
|
|
2198
|
-
this.plotService.resetSurfaceCamera();
|
|
991
|
+
this.plotService.getSurface3dControls()?.resetSurfaceCamera();
|
|
2199
992
|
}
|
|
2200
993
|
/** Toggle the napari 3D coordinate-axes / scale gizmo (volume/isosurface). No-op on backends
|
|
2201
994
|
* that don't render one (the control method is optional). */
|
|
@@ -2224,6 +1017,9 @@ export class VisualizerComponent {
|
|
|
2224
1017
|
* moving to a 3D type, then re-plots.
|
|
2225
1018
|
*/
|
|
2226
1019
|
onSelectPlotType(selected) {
|
|
1020
|
+
// Also called by the host directly, outside this view's events: the selector, the
|
|
1021
|
+
// toolbar's tool groups and the dialogs all follow the type.
|
|
1022
|
+
this.changed();
|
|
2227
1023
|
// A contributed id whose provider is gone (e.g. restored from a previous
|
|
2228
1024
|
// session) has nothing to activate — take the default Image view instead.
|
|
2229
1025
|
if (!isBuiltinPlotType(selected) && !this.plotModes.find(selected)) {
|
|
@@ -2243,7 +1039,7 @@ export class VisualizerComponent {
|
|
|
2243
1039
|
// A dialog tool draws over the Image view: leaving it closes the dialog. Staying
|
|
2244
1040
|
// on it, the session ends here and restarts once the re-plot lands.
|
|
2245
1041
|
if (type === PlotType.IMAGE)
|
|
2246
|
-
this.toolDialogs.deactivate();
|
|
1042
|
+
this.contributions.toolDialogs.deactivate();
|
|
2247
1043
|
else
|
|
2248
1044
|
this.closeDialogTool();
|
|
2249
1045
|
this.plotType = type;
|
|
@@ -2257,7 +1053,7 @@ export class VisualizerComponent {
|
|
|
2257
1053
|
// Switching plot type cancels the active tool — not every type supports the
|
|
2258
1054
|
// same tools (line/scatter charts and 3D scenes have no box zoom or region
|
|
2259
1055
|
// drawing), so leaving a tool armed would misbehave on the new plot.
|
|
2260
|
-
this.
|
|
1056
|
+
this.tools.deactivate();
|
|
2261
1057
|
if (is3d) {
|
|
2262
1058
|
this.activeSurface3dMode = 'turntable';
|
|
2263
1059
|
}
|
|
@@ -2274,275 +1070,55 @@ export class VisualizerComponent {
|
|
|
2274
1070
|
// Plotly's stack-loading flag (keeps its frame-fetch loop alive) and
|
|
2275
1071
|
// re-emit the image info with showStack on so the pipeline reloads on
|
|
2276
1072
|
// Plotly regardless of which backend was on screen.
|
|
2277
|
-
this.
|
|
2278
|
-
this.stackLoading = true;
|
|
1073
|
+
this.stackLoading.set(true);
|
|
2279
1074
|
this.plotService.setStackLoading(true);
|
|
2280
1075
|
if (this.imageInfo) {
|
|
2281
|
-
|
|
2282
|
-
this.state.setImageInfo(this.imageInfo);
|
|
1076
|
+
// A copy: the image info may be the host's own (possibly frozen) object.
|
|
1077
|
+
this.state.setImageInfo({ ...this.imageInfo, showStack: true });
|
|
2283
1078
|
}
|
|
2284
1079
|
return;
|
|
2285
1080
|
}
|
|
2286
1081
|
this.reloadAndPlot();
|
|
2287
1082
|
}
|
|
2288
|
-
// ── contributed plot modes ─────────────────────────────────────────────
|
|
2289
|
-
/**
|
|
2290
|
-
* Start the selected contributed mode's session, once its base view has
|
|
2291
|
-
* plotted. No-op for a built-in type, or when the base on screen is not the
|
|
2292
|
-
* one the mode rides on. A backend that cannot provide a viewport for it
|
|
2293
|
-
* (e.g. OSD fell back to Plotly) counts as a failed activation.
|
|
2294
|
-
*/
|
|
2295
|
-
activateSelectedPlotMode() {
|
|
2296
|
-
const contribution = this.plotModes.find(this.selectedPlotTypeId);
|
|
2297
|
-
if (!contribution || this.plotType !== contribution.descriptor.baseType)
|
|
2298
|
-
return;
|
|
2299
|
-
const viewport = this.plotService.getPlotModeViewport?.() ?? null;
|
|
2300
|
-
if (!viewport) {
|
|
2301
|
-
console.error(`[visualizer] plot-type contribution '${contribution.descriptor.type}': the backend `
|
|
2302
|
-
+ 'on screen provides no viewport to draw over — falling back.');
|
|
2303
|
-
this.fallBackFromPlotMode(contribution);
|
|
2304
|
-
return;
|
|
2305
|
-
}
|
|
2306
|
-
const ctx = {
|
|
2307
|
-
visualizer: this.plotService,
|
|
2308
|
-
viewport,
|
|
2309
|
-
imageInfo$: this.state.getImageInfo$(),
|
|
2310
|
-
tools: this.plotModeTools,
|
|
2311
|
-
};
|
|
2312
|
-
void this.plotModes.activate(contribution, ctx);
|
|
2313
|
-
}
|
|
2314
|
-
/** Render the live mode's panel. Activation can resolve outside Angular (an
|
|
2315
|
-
* OSD callback, a contribution's own promise), hence the zone re-entry. */
|
|
2316
|
-
showPlotModePanel(active) {
|
|
2317
|
-
const panel = active.contribution.panel;
|
|
2318
|
-
if (!panel)
|
|
2319
|
-
return;
|
|
2320
|
-
this.ngZone.run(() => {
|
|
2321
|
-
if ('component' in panel && panel.component) {
|
|
2322
|
-
this.plotModePanel = {
|
|
2323
|
-
title: panel.title,
|
|
2324
|
-
component: panel.component,
|
|
2325
|
-
injector: Injector.create({
|
|
2326
|
-
providers: [
|
|
2327
|
-
{ provide: PLOT_MODE_CONTEXT, useValue: active.ctx },
|
|
2328
|
-
{ provide: PLOT_MODE_SESSION, useValue: active.session },
|
|
2329
|
-
],
|
|
2330
|
-
parent: this.injector,
|
|
2331
|
-
}),
|
|
2332
|
-
host: null,
|
|
2333
|
-
};
|
|
2334
|
-
}
|
|
2335
|
-
else if (active.panelHost) {
|
|
2336
|
-
this.plotModePanel = { title: panel.title, component: null, injector: null, host: active.panelHost };
|
|
2337
|
-
}
|
|
2338
|
-
else {
|
|
2339
|
-
return;
|
|
2340
|
-
}
|
|
2341
|
-
// A component panel is created and first rendered right here; if that throws,
|
|
2342
|
-
// the mode is not usable — end it and fall back rather than leave it selected.
|
|
2343
|
-
const err = this.detectChangesSafely();
|
|
2344
|
-
if (err !== null)
|
|
2345
|
-
this.plotModes.failActive(err);
|
|
2346
|
-
});
|
|
2347
|
-
}
|
|
2348
|
-
/** Drop the panel (destroying a component panel) before the session ends. */
|
|
2349
|
-
hidePlotModePanel() {
|
|
2350
|
-
if (!this.plotModePanel)
|
|
2351
|
-
return;
|
|
2352
|
-
this.plotModePanel = null;
|
|
2353
|
-
this.detectChangesSafely();
|
|
2354
|
-
}
|
|
2355
|
-
/**
|
|
2356
|
-
* A contributed mode could not start. Its base type is already on screen —
|
|
2357
|
-
* the base plotted first — so just select it: nothing needs to re-plot.
|
|
2358
|
-
*/
|
|
2359
|
-
fallBackFromPlotMode(contribution) {
|
|
2360
|
-
if (this.destroying || this.selectedPlotTypeId !== contribution.descriptor.type)
|
|
2361
|
-
return;
|
|
2362
|
-
this.ngZone.run(() => {
|
|
2363
|
-
const base = contribution.descriptor.baseType;
|
|
2364
|
-
this.selectedPlotTypeId = base;
|
|
2365
|
-
this.plotType = base;
|
|
2366
|
-
this.messageService.add({
|
|
2367
|
-
key: this.resultToastKey,
|
|
2368
|
-
severity: 'warn',
|
|
2369
|
-
summary: `${contribution.descriptor.productionLabel ?? contribution.descriptor.label} is unavailable`,
|
|
2370
|
-
detail: 'The plot mode could not start, so the plain image is shown instead. See the browser console for details.',
|
|
2371
|
-
});
|
|
2372
|
-
this.detectChangesSafely();
|
|
2373
|
-
});
|
|
2374
|
-
}
|
|
2375
|
-
/** Run change detection; returns what it threw (logged), or null. */
|
|
2376
|
-
detectChangesSafely() {
|
|
2377
|
-
if (this.destroying)
|
|
2378
|
-
return null;
|
|
2379
|
-
try {
|
|
2380
|
-
this.cdr.detectChanges();
|
|
2381
|
-
return null;
|
|
2382
|
-
}
|
|
2383
|
-
catch (err) {
|
|
2384
|
-
console.error('[visualizer] change detection failed while updating a contributed plot mode panel.', err);
|
|
2385
|
-
return err ?? new Error('change detection failed');
|
|
2386
|
-
}
|
|
2387
|
-
}
|
|
2388
1083
|
// ── dialog tools (TOOLBAR_TOOLS, kind: 'dialog') ─────────────────────────
|
|
2389
1084
|
/** The toolbar button: open the tool's dialog, or close it if it is open. */
|
|
2390
1085
|
toggleDialogTool(id) {
|
|
2391
|
-
|
|
2392
|
-
this.closeDialogTool();
|
|
2393
|
-
return;
|
|
2394
|
-
}
|
|
2395
|
-
this.closeDialogTool();
|
|
2396
|
-
if (!this.dialogTools.some((t) => t.id === id))
|
|
2397
|
-
return;
|
|
2398
|
-
this.openDialogToolId = id;
|
|
2399
|
-
this.toolDialogs.clearCleanupFailures(); // an explicit open gets a fresh attempt
|
|
2400
|
-
this.activateOpenDialogTool();
|
|
1086
|
+
this.contributions.toggleDialogTool(id);
|
|
2401
1087
|
}
|
|
2402
1088
|
/** Close the open dialog tool: its body is torn down, then its session ends. */
|
|
2403
1089
|
closeDialogTool() {
|
|
2404
|
-
this.
|
|
2405
|
-
this.toolDialogs.deactivate();
|
|
2406
|
-
}
|
|
2407
|
-
/** Start (or restart, after a re-render) the open dialog tool's session. */
|
|
2408
|
-
activateOpenDialogTool() {
|
|
2409
|
-
const id = this.openDialogToolId;
|
|
2410
|
-
const contribution = id ? this.toolDialogs.find(id) : undefined;
|
|
2411
|
-
if (!contribution || this.plotType !== PlotType.IMAGE)
|
|
2412
|
-
return;
|
|
2413
|
-
if (this.toolDialogs.current?.contribution === contribution || this.toolDialogs.pending)
|
|
2414
|
-
return;
|
|
2415
|
-
const viewport = this.plotService.getPlotModeViewport?.() ?? null;
|
|
2416
|
-
if (!viewport) {
|
|
2417
|
-
console.error(`[visualizer] dialog tool '${id}': the backend on screen provides no viewport to draw over.`);
|
|
2418
|
-
this.dialogToolFailed(id);
|
|
2419
|
-
return;
|
|
2420
|
-
}
|
|
2421
|
-
const ctx = {
|
|
2422
|
-
visualizer: this.plotService,
|
|
2423
|
-
viewport,
|
|
2424
|
-
imageInfo$: this.state.getImageInfo$(),
|
|
2425
|
-
tools: this.plotModeTools,
|
|
2426
|
-
};
|
|
2427
|
-
void this.toolDialogs.activate(contribution, ctx);
|
|
2428
|
-
}
|
|
2429
|
-
/**
|
|
2430
|
-
* The session is live: render the dialog, then mount the tool's body once the host
|
|
2431
|
-
* element is in the document, so `mount()` can measure it and start widgets that need
|
|
2432
|
-
* a connected element.
|
|
2433
|
-
*/
|
|
2434
|
-
showToolDialog(active) {
|
|
2435
|
-
const tool = this.dialogTools.find((t) => t.id === active.contribution.descriptor.type);
|
|
2436
|
-
if (!tool)
|
|
2437
|
-
return;
|
|
2438
|
-
const host = document.createElement('div');
|
|
2439
|
-
host.className = 'tool-dialog-body';
|
|
2440
|
-
this.ngZone.run(() => {
|
|
2441
|
-
this.toolDialog = {
|
|
2442
|
-
title: tool.dialog?.title ?? tool.label,
|
|
2443
|
-
width: tool.dialog?.width ?? '22rem',
|
|
2444
|
-
host,
|
|
2445
|
-
};
|
|
2446
|
-
const err = this.detectChangesSafely();
|
|
2447
|
-
if (err !== null) {
|
|
2448
|
-
this.toolDialogs.failActive(err);
|
|
2449
|
-
return;
|
|
2450
|
-
}
|
|
2451
|
-
this.mountToolDialogBody(tool, active, host, 0);
|
|
2452
|
-
});
|
|
2453
|
-
}
|
|
2454
|
-
/** How many animation frames to wait for the dialog to attach the body's host. */
|
|
2455
|
-
static { this.TOOL_DIALOG_ATTACH_FRAMES = 60; }
|
|
2456
|
-
/**
|
|
2457
|
-
* Mount `tool`'s body into `host` once it is connected (the dialog may attach it a
|
|
2458
|
-
* frame later, when it moves itself to `<body>`). If it never connects within
|
|
2459
|
-
* {@link TOOL_DIALOG_ATTACH_FRAMES} it is mounted anyway, with a warning, rather than
|
|
2460
|
-
* leaving an empty dialog. A throwing `mount()` fails the session like a failed start.
|
|
2461
|
-
*/
|
|
2462
|
-
mountToolDialogBody(tool, active, host, frames) {
|
|
2463
|
-
this.toolDialogFrame = null;
|
|
2464
|
-
// The session ended (or was replaced) while waiting: nothing to mount into.
|
|
2465
|
-
if (this.toolDialogs.current !== active || this.toolDialog?.host !== host)
|
|
2466
|
-
return;
|
|
2467
|
-
if (!host.isConnected) {
|
|
2468
|
-
if (frames < VisualizerComponent.TOOL_DIALOG_ATTACH_FRAMES) {
|
|
2469
|
-
this.toolDialogFrame = requestAnimationFrame(() => this.mountToolDialogBody(tool, active, host, frames + 1));
|
|
2470
|
-
return;
|
|
2471
|
-
}
|
|
2472
|
-
console.warn(`[visualizer] dialog tool '${tool.id}': the dialog body never attached; mounting it detached.`);
|
|
2473
|
-
}
|
|
2474
|
-
try {
|
|
2475
|
-
const teardown = tool.mount(host, active.ctx, active.session);
|
|
2476
|
-
this.toolDialogTeardown = typeof teardown === 'function' ? teardown : null;
|
|
2477
|
-
}
|
|
2478
|
-
catch (err) {
|
|
2479
|
-
console.error(`[visualizer] dialog tool '${tool.id}' mount() threw.`, err);
|
|
2480
|
-
this.toolDialogs.failActive(err);
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
/** The session is about to end: tear the body down first, then drop the dialog. */
|
|
2484
|
-
hideToolDialog() {
|
|
2485
|
-
if (this.toolDialogFrame !== null)
|
|
2486
|
-
cancelAnimationFrame(this.toolDialogFrame);
|
|
2487
|
-
this.toolDialogFrame = null;
|
|
2488
|
-
const teardown = this.toolDialogTeardown;
|
|
2489
|
-
this.toolDialogTeardown = null;
|
|
2490
|
-
if (teardown) {
|
|
2491
|
-
try {
|
|
2492
|
-
teardown();
|
|
2493
|
-
}
|
|
2494
|
-
catch (err) {
|
|
2495
|
-
console.error('[visualizer] dialog tool body teardown threw.', err);
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
if (!this.toolDialog)
|
|
2499
|
-
return;
|
|
2500
|
-
this.toolDialog.host.remove();
|
|
2501
|
-
this.toolDialog = null;
|
|
2502
|
-
if (!this.destroying)
|
|
2503
|
-
this.detectChangesSafely();
|
|
2504
|
-
}
|
|
2505
|
-
/** A dialog tool could not start: close it and say so. */
|
|
2506
|
-
dialogToolFailed(id) {
|
|
2507
|
-
if (this.destroying || this.openDialogToolId !== id)
|
|
2508
|
-
return;
|
|
2509
|
-
this.ngZone.run(() => {
|
|
2510
|
-
this.openDialogToolId = null;
|
|
2511
|
-
const tool = this.dialogTools.find((t) => t.id === id);
|
|
2512
|
-
this.messageService.add({
|
|
2513
|
-
key: this.resultToastKey,
|
|
2514
|
-
severity: 'warn',
|
|
2515
|
-
summary: `${tool?.label ?? id} is unavailable`,
|
|
2516
|
-
detail: 'The tool could not start. See the browser console for details.',
|
|
2517
|
-
});
|
|
2518
|
-
this.detectChangesSafely();
|
|
2519
|
-
});
|
|
1090
|
+
this.contributions.closeDialogTool();
|
|
2520
1091
|
}
|
|
2521
1092
|
/** Where the open dialog tool's host element is attached once its dialog renders. */
|
|
2522
1093
|
set toolDialogSlot(ref) {
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
}
|
|
2527
|
-
/** Deactivate whatever tool is armed and clear every tool mode. */
|
|
2528
|
-
deactivateActiveTool() {
|
|
2529
|
-
this.brushClass = null;
|
|
2530
|
-
this.activeDragMode = null;
|
|
2531
|
-
this.session.setActiveTool(null);
|
|
2532
|
-
this.plotService.getRegionOverlay()?.setMode('none');
|
|
2533
|
-
this.plotService.setDragMode(false);
|
|
2534
|
-
this.plotService.setZoomToBoxMode(false);
|
|
2535
|
-
this.plotService.setWandMode(false);
|
|
2536
|
-
this.plotService.setBrushMode(false);
|
|
2537
|
-
this.plotService.setSamPointMode(false);
|
|
2538
|
-
this.plotService.setVertexEraserMode(false);
|
|
2539
|
-
}
|
|
2540
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: VisualizerComponent, deps: [{ token: IMAGE_STATE_PORT }, { token: VISUALIZER }, { token: i1.MessageService }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i2.VisualizerStore }, { token: i3.SamToolService }, { token: i4.CellSegmentToolService }, { token: i5.SamPointToolService }, { token: i6.RegionOpsService }, { token: VIZ_CONFIG, optional: true }, { token: TOOLBAR_TOOLS, optional: true }, { token: SPATIAL_DATA_PORT, optional: true }, { token: PLOT_TYPE_CONTRIBUTIONS, optional: true }, { token: i0.Injector, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2541
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: VisualizerComponent, selector: "visualizer", inputs: { toolbarTools: "toolbarTools", testMode: "testMode" }, outputs: { isStackEvent: "isStackEvent", isGrayscaleEvent: "isGrayscaleEvent" }, host: { listeners: { "window:keydown": "onKeydown($event)" } }, viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["cm"], descendants: true }, { propertyName: "plotModePanelSlot", first: true, predicate: ["plotModePanelSlot"], descendants: true }, { propertyName: "toolDialogSlot", first: true, predicate: ["toolDialogSlot"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ samStatus || message.detail }}</div>\n <p-progressBar *ngIf=\"samDownloading\" [value]=\"samProgress\" styleClass=\"mt-2 sam-progress\"></p-progressBar>\n <p-progressBar *ngIf=\"samBusy && !samDownloading\" mode=\"indeterminate\" styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Result/error notices from the library itself (segmentation outcome, region\n validation). Rendered here so they do not depend on the host mounting a\n keyless <p-toast> \u2014 without this the messages went nowhere in any host but\n jit-ui, and a failed run looked like a button that did nothing. -->\n<p-toast [key]=\"resultToastKey\" position=\"bottom-right\"></p-toast>\n\n<!-- Shared outlets for notices raised where there is no per-instance key to use:\n the root-provided PlotlyService and the region-editor dialog. Rendered by a\n single visualizer (ownsSharedToasts) so a message from the root service is\n not shown once per live viewer. -->\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizToastKey\"></p-toast>\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizAlertToastKey\" position=\"center\"></p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div class=\"toolbar-dock\" [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\">\n <div class=\"toolbar-drag-handle\" (mousedown)=\"onToolbarDragStart($event)\"\n pTooltip=\"Drag to float the toolbar\" tooltipPosition=\"bottom\">\n <i class=\"pi pi-bars\"></i>\n </div>\n<plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [hasSpatialDataset]=\"hasSpatialDataset\"\n [plotTypeOptions]=\"plotTypeMenu\"\n [selectedPlotType]=\"selectedPlotTypeId\"\n [basePlotType]=\"basePlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [is3dRegions]=\"isSpatial3dMode\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n [openDialogToolId]=\"openDialogToolId\"\n (toggleDialogTool)=\"toggleDialogTool($event)\"\n [toolModelIds]=\"toolModelIds\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"canUndoRegion\"\n [canRedo]=\"canRedoRegion\"\n [stackOptions]=\"stackOptions\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openSpatialControls)=\"openSpatialControls()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (runTool)=\"runTool($event)\"\n (toolModelChange)=\"onToolModelChange($event)\"\n (openToolParams)=\"openToolParams($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\">\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n</plotting-toolbar>\n <p-button *ngIf=\"toolbarFloating\" class=\"toolbar-dock-btn\" icon=\"pi pi-thumbtack\" size=\"small\" [text]=\"true\"\n pTooltip=\"Dock to top\" tooltipPosition=\"bottom\" (onClick)=\"dockToolbar()\">\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading || stackLoading || isCaching\" id=\"overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading; else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching; else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress\"></p-progressBar>\n <span class=\"text-loading\">{{loadingMessage}}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading && !zoom\" class=\"text-loading\">{{loadingMessage}}</span>\n <span *ngIf=\"imgLoading && zoom\" class=\"zoom-text-loading\">{{loadingMessage}}</span>\n </ng-template>\n </ng-template>\n <p-button *ngIf=\"stackLoading || imgLoading\" class=\"cancel-loading \" label=\"Cancel\"\n (onClick)=\"cancelLoading()\">\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"sharpening && !imgLoading && !stackLoading && !isCaching\" id=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n\n</div>\n\n<!-- Floating, draggable intensity-profile inset. Shown in LINE mode; the chart\n updates live as the line ROI on the image is moved. Kept OUTSIDE #plot and\n positioned fixed so it floats over the whole page (not clipped to the\n diagram/canvas area). -->\n<div *ngIf=\"hasProfiles\" class=\"intensity-panel\"\n [style.left.px]=\"profilePanelPos.x\" [style.top.px]=\"profilePanelPos.y\">\n <div class=\"intensity-panel-header\" (mousedown)=\"onProfilePanelDragStart($event)\">\n <span>Intensity profile</span>\n <i class=\"pi pi-arrows-alt\"></i>\n </div>\n <div [id]=\"intensityInsetDiv\" class=\"intensity-panel-plot\"></div>\n</div>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Spatial-omics controls. Renders its own empty state when no dataset (or no\n SPATIAL_DATA_PORT) is present, so it is always safe to host. -->\n<spatial-controls [(visible)]=\"showSpatialControls\" [is3d]=\"isSpatial3dMode\" ></spatial-controls>\n\n<!-- Side panel of the active contributed plot mode (PLOT_TYPE_CONTRIBUTIONS),\n in the same right-hand area as the spatial controls and the Region Editor.\n Exists only while the mode's session is live; there is no close button,\n because leaving the mode is what ends it. A component panel gets\n PLOT_MODE_CONTEXT / PLOT_MODE_SESSION from its injector; a `mount` panel's\n host element is attached to the slot below (see plotModePanelSlot). -->\n<p-dialog *ngIf=\"plotModePanel as panel\" [header]=\"panel.title\" [visible]=\"true\"\n [modal]=\"false\" [closable]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n appendTo=\"body\" position=\"right\" styleClass=\"plot-mode-panel\"\n [style]=\"{ width: '22rem', maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <ng-container *ngIf=\"panel.component\">\n <ng-container *ngComponentOutlet=\"panel.component; injector: panel.injector ?? undefined\"></ng-container>\n </ng-container>\n <div *ngIf=\"panel.host\" #plotModePanelSlot class=\"plot-mode-panel-slot\"></div>\n</p-dialog>\n\n<!-- An open dialog tool (TOOLBAR_TOOLS, kind: 'dialog'). Non-modal so the user can\n keep working on the image, e.g. painting with the brush the tool armed. The\n tool renders its body into a host element attached to the slot below (see\n toolDialogSlot); closing the dialog ends the tool's session. -->\n<p-dialog *ngIf=\"toolDialog as dlg\" [header]=\"dlg.title\" [visible]=\"true\"\n (visibleChange)=\"$event || closeDialogTool()\"\n [modal]=\"false\" [closable]=\"true\" [resizable]=\"true\" [draggable]=\"true\"\n appendTo=\"body\" position=\"right\" styleClass=\"tool-dialog\"\n [style]=\"{ width: dlg.width, maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <div #toolDialogSlot class=\"tool-dialog-slot\"></div>\n</p-dialog>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog header=\"Region Editor\" [(visible)]=\"showRegionEditor\"\n [modal]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n [maximizable]=\"true\" appendTo=\"body\" position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog header=\"Simplify region(s)\" [(visible)]=\"displaySimplifyDialog\" [modal]=\"true\"\n [style]=\"{width: '22rem'}\" [draggable]=\"false\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-2\">\n <span>Altitude threshold: <b>{{ simplifyThreshold }} px</b></span>\n <p-slider [(ngModel)]=\"simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"simplifyRegions(simplifyThreshold)\"></p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Parameters for whichever contributed tool is open. One dialog for all of\n them: a tool declares its fields (ToolParamSpec) and this renders them, so\n the toolbar can lay every tool out the same way and a plugin needs no\n PrimeNG of its own. Defaults come from the active checkpoint, since they\n encode what it was trained for. -->\n<p-dialog *ngIf=\"openParamsTool as tool\"\n [header]=\"tool.label + ' parameters'\"\n [visible]=\"true\" (visibleChange)=\"openParamsToolId = null\"\n [modal]=\"true\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\" appendTo=\"body\">\n <div class=\"grid formgrid p-fluid\">\n <ng-container *ngFor=\"let p of tool.params\">\n <div class=\"field\" [ngClass]=\"p.type === 'boolean' ? 'col-12' : 'col-6'\">\n <ng-container [ngSwitch]=\"p.type\">\n\n <ng-container *ngSwitchCase=\"'number'\">\n <label class=\"text-sm\" [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n <p-inputNumber [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n [min]=\"numberParam(p).min\" [max]=\"numberParam(p).max\"\n [step]=\"numberParam(p).step\"\n [minFractionDigits]=\"numberParam(p).fractionDigits\"\n [showButtons]=\"true\"></p-inputNumber>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'select'\">\n <label class=\"text-sm\" [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n <p-dropdown [options]=\"selectParam(p).options\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n appendTo=\"body\"></p-dropdown>\n </ng-container>\n\n <!-- Checkbox label sits beside the box and may run to a sentence, so\n it does not get the stacked label the other types use. -->\n <div *ngSwitchCase=\"'boolean'\" class=\"flex align-items-center gap-2\">\n <p-checkbox [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n [binary]=\"true\" [inputId]=\"tool.id + '-' + p.id\"></p-checkbox>\n <label [for]=\"tool.id + '-' + p.id\" class=\"text-sm mb-0\"\n [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n </div>\n\n </ng-container>\n </div>\n </ng-container>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Reset to model defaults\" [text]=\"true\" size=\"small\"\n (onClick)=\"resetToolParams(tool.id)\"></p-button>\n <p-button label=\"Close\" size=\"small\" (onClick)=\"openParamsToolId = null\"></p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}#overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}#sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}#sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}#sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.intensity-panel{position:fixed;z-index:1100;width:280px;height:180px;background:#191919e6;border:1px solid var(--primary-color, #3399ff);border-radius:6px;box-shadow:0 4px 14px #00000073;display:flex;flex-direction:column;overflow:hidden}.intensity-panel-header{display:flex;align-items:center;justify-content:space-between;padding:.25rem .5rem;cursor:move;-webkit-user-select:none;user-select:none;background:#ffffff14;color:#eee;font-size:.75rem;font-weight:600}.intensity-panel-header i{opacity:.6;font-size:.7rem}.intensity-panel-plot{flex:1 1 auto;width:100%}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"], dependencies: [{ kind: "directive", type: i7.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i7.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: i7.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i7.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i7.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i9.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i10.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: i11.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i12.Slider, selector: "p-slider", inputs: ["animate", "disabled", "min", "max", "orientation", "step", "range", "style", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "component", type: i13.InputNumber, selector: "p-inputNumber", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "component", type: i14.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "visible", "style", "position"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "component", type: i15.ContextMenu, selector: "p-contextMenu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "appendTo", "autoZIndex", "baseZIndex", "id", "ariaLabel", "ariaLabelledBy", "pressDelay"], outputs: ["onShow", "onHide"] }, { kind: "component", type: i16.ProgressBar, selector: "p-progressBar", inputs: ["value", "showValue", "styleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: i17.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "style", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "component", type: i18.Checkbox, selector: "p-checkbox", inputs: ["value", "name", "disabled", "binary", "label", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "styleClass", "labelStyleClass", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i19.ToolbarComponent, selector: "plotting-toolbar", inputs: ["imageInfo", "hasSpatialDataset", "plotTypeOptions", "selectedPlotType", "basePlotType", "isoRange", "maxIndex", "zIndex", "isHeatmap", "is3dRegions", "activeDragMode", "activeSurface3dMode", "axesVisible", "wireframeActive", "resolutionScale", "wandSensitivity", "brushSize", "vertexEraserRadius", "canUndo", "canRedo", "stackOptions", "tools", "imageSmoothingEnabled", "samModels", "samModelId", "contributedTools", "dialogTools", "openDialogToolId", "toolModelIds"], outputs: ["selectPlotType", "addProfileLine", "toggleImageSmoothing", "isoRangeChange", "openChannelHistogram", "openSpatialControls", "openRegionEditor", "selectStackOption", "zScrub", "zSlide", "zIndexInput", "reloadAndPlot", "downloadImage", "autoscaleImage", "toggleDragMode", "zoomIn", "zoomOut", "toggleSurface3dMode", "resetSurfaceCamera", "toggleAxes", "toggleWireframe", "selectResolution", "deleteRegion", "undoRegion", "redoRegion", "segmentRegions", "segmentCellpose", "runTool", "toggleDialogTool", "toolModelChange", "openToolParams", "samModelChange", "wandSensitivityChange", "brushSizeChange", "vertexEraserRadiusChange"] }, { kind: "component", type: i20.RegionEditorComponent, selector: "region-editor" }, { kind: "component", type: i21.ChannelHistogramComponent, selector: "channel-histogram", inputs: ["visible"], outputs: ["visibleChange"] }, { kind: "component", type: i22.SpatialControlsComponent, selector: "spatial-controls", inputs: ["visible", "is3d"], outputs: ["visibleChange"] }] }); }
|
|
1094
|
+
this.contributions.attachToolDialogSlot(ref?.nativeElement);
|
|
1095
|
+
}
|
|
1096
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: VisualizerComponent, deps: [{ token: IMAGE_STATE_PORT }, { token: VISUALIZER }, { token: i1.MessageService }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i2.VisualizerStore }, { token: i3.SamToolService }, { token: i4.CellSegmentToolService }, { token: i5.SamPointToolService }, { token: i6.RegionOpsService }, { token: VIZ_CONFIG, optional: true }, { token: TOOLBAR_TOOLS, optional: true }, { token: SPATIAL_DATA_PORT, optional: true }, { token: PLOT_TYPE_CONTRIBUTIONS, optional: true }, { token: i0.Injector, optional: true }, { token: i0.ElementRef, optional: true }, { token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1097
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: VisualizerComponent, isStandalone: true, selector: "visualizer", inputs: { toolbarTools: "toolbarTools", testMode: "testMode" }, outputs: { isStackEvent: "isStackEvent", isGrayscaleEvent: "isGrayscaleEvent" }, viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["cm"], descendants: true }, { propertyName: "plotModePanelSlot", first: true, predicate: ["plotModePanelSlot"], descendants: true }, { propertyName: "inset", first: true, predicate: IntensityInsetComponent, descendants: true, static: true }, { propertyName: "toolDialogSlot", first: true, predicate: ["toolDialogSlot"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ segmentation.status || message.detail }}</div>\n <p-progressBar\n *ngIf=\"segmentation.downloading\"\n [value]=\"segmentation.progress\"\n styleClass=\"mt-2 sam-progress\"\n ></p-progressBar>\n <p-progressBar\n *ngIf=\"segmentation.busy && !segmentation.downloading\"\n mode=\"indeterminate\"\n styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"\n ></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Result/error notices from the library itself (segmentation outcome, region\n validation). Rendered here so they do not depend on the host mounting a\n keyless <p-toast> \u2014 without this the messages went nowhere in any host but\n jit-ui, and a failed run looked like a button that did nothing. -->\n<p-toast [key]=\"resultToastKey\" position=\"bottom-right\"></p-toast>\n\n<!-- Shared outlets for notices raised where there is no per-instance key to use:\n the root-provided PlotlyService and the region-editor dialog. Rendered by a\n single visualizer (ownsSharedToasts) so a message from the root service is\n not shown once per live viewer. -->\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizToastKey\"></p-toast>\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizAlertToastKey\" position=\"center\"></p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div\n class=\"toolbar-dock\"\n [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\"\n>\n <div\n class=\"toolbar-drag-handle\"\n [vizFloatingDrag]=\"toolbarDragOrigin\"\n (vizFloatingDragMove)=\"toolbarPos = $event\"\n pTooltip=\"Drag to float the toolbar\"\n tooltipPosition=\"bottom\"\n >\n <i class=\"pi pi-bars\"></i>\n </div>\n <plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [hasSpatialDataset]=\"hasSpatialDataset\"\n [plotTypeOptions]=\"plotTypeMenu\"\n [selectedPlotType]=\"selectedPlotTypeId\"\n [basePlotType]=\"basePlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [is3dRegions]=\"isSpatial3dMode\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n [openDialogToolId]=\"openDialogToolId\"\n (toggleDialogTool)=\"toggleDialogTool($event)\"\n [toolModelIds]=\"toolParams.toolModelIds\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"regionActions.canUndo\"\n [canRedo]=\"regionActions.canRedo\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openSpatialControls)=\"openSpatialControls()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (runTool)=\"runTool($event)\"\n (toolModelChange)=\"onToolModelChange($event)\"\n (openToolParams)=\"openToolParams($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\"\n >\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n </plotting-toolbar>\n <p-button\n *ngIf=\"toolbarFloating\"\n class=\"toolbar-dock-btn\"\n icon=\"pi pi-thumbtack\"\n size=\"small\"\n [text]=\"true\"\n pTooltip=\"Dock to top\"\n tooltipPosition=\"bottom\"\n (onClick)=\"dockToolbar()\"\n >\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading() || stackLoading() || isCaching()\" class=\"loading-overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading(); else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage()\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching(); else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress()\"></p-progressBar>\n <span class=\"text-loading\">{{ loadingMessage() }}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading()\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading() && !zoom()\" class=\"text-loading\">{{ loadingMessage() }}</span>\n <span *ngIf=\"imgLoading() && zoom()\" class=\"zoom-text-loading\">{{ loadingMessage() }}</span>\n </ng-template>\n </ng-template>\n <p-button\n *ngIf=\"stackLoading() || imgLoading()\"\n class=\"cancel-loading\"\n label=\"Cancel\"\n (onClick)=\"cancelLoading()\"\n >\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"render.sharpening && !imgLoading() && !stackLoading() && !isCaching()\" class=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n</div>\n\n<!-- Floating, draggable intensity-profile inset, shown while any intensity line\n exists. Kept OUTSIDE the plot div and fixed to the viewport, so it floats over\n the whole page rather than being clipped to the canvas. -->\n<viz-intensity-inset\n [divId]=\"intensityInsetDiv\"\n [plotDivName]=\"plotDivName\"\n [zIndex]=\"zIndex\"\n [imageView]=\"isImageView\"\n [imageInfo]=\"imageInfo\"\n></viz-intensity-inset>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Spatial-omics controls. Renders its own empty state when no dataset (or no\n SPATIAL_DATA_PORT) is present, so it is always safe to host. -->\n<spatial-controls [(visible)]=\"showSpatialControls\" [is3d]=\"isSpatial3dMode\"></spatial-controls>\n\n<!-- Side panel of the active contributed plot mode (PLOT_TYPE_CONTRIBUTIONS),\n in the same right-hand area as the spatial controls and the Region Editor.\n Exists only while the mode's session is live; there is no close button,\n because leaving the mode is what ends it. A component panel gets\n PLOT_MODE_CONTEXT / PLOT_MODE_SESSION from its injector; a `mount` panel's\n host element is attached to the slot below (see plotModePanelSlot). -->\n<p-dialog\n *ngIf=\"plotModePanel as panel\"\n [header]=\"panel.title\"\n [visible]=\"true\"\n [modal]=\"false\"\n [closable]=\"false\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n styleClass=\"plot-mode-panel\"\n [style]=\"{ width: '22rem', maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <ng-container *ngIf=\"panel.component\">\n <ng-container *ngComponentOutlet=\"panel.component; injector: panel.injector ?? undefined\"></ng-container>\n </ng-container>\n <div *ngIf=\"panel.host\" #plotModePanelSlot class=\"plot-mode-panel-slot\"></div>\n</p-dialog>\n\n<!-- An open dialog tool (TOOLBAR_TOOLS, kind: 'dialog'). Non-modal so the user can\n keep working on the image, e.g. painting with the brush the tool armed. The\n tool renders its body into a host element attached to the slot below (see\n toolDialogSlot); closing the dialog ends the tool's session. -->\n<p-dialog\n *ngIf=\"toolDialog as dlg\"\n [header]=\"dlg.title\"\n [visible]=\"true\"\n (visibleChange)=\"$event || closeDialogTool()\"\n [modal]=\"false\"\n [closable]=\"true\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n styleClass=\"tool-dialog\"\n [style]=\"{ width: dlg.width, maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <div #toolDialogSlot class=\"tool-dialog-slot\"></div>\n</p-dialog>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog\n header=\"Region Editor\"\n [(visible)]=\"showRegionEditor\"\n [modal]=\"false\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n [maximizable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog\n header=\"Simplify region(s)\"\n [(visible)]=\"displaySimplifyDialog\"\n [modal]=\"true\"\n [style]=\"{ width: '22rem' }\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n>\n <div class=\"flex flex-column gap-2\">\n <span\n >Altitude threshold: <b>{{ regionActions.simplifyThreshold }} px</b></span\n >\n <p-slider [(ngModel)]=\"regionActions.simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button\n label=\"Apply\"\n icon=\"pi pi-check\"\n (onClick)=\"simplifyRegions(regionActions.simplifyThreshold)\"\n ></p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Parameters for whichever contributed tool is open. One dialog for all of\n them: a tool declares its fields (ToolParamSpec) and this renders them, so\n the toolbar can lay every tool out the same way and a plugin needs no\n PrimeNG of its own. Defaults come from the active checkpoint, since they\n encode what it was trained for. -->\n<p-dialog\n *ngIf=\"toolParams.openParams as open\"\n [header]=\"open.tool.label + ' parameters'\"\n [visible]=\"true\"\n (visibleChange)=\"closeToolParams()\"\n [modal]=\"true\"\n [style]=\"{ width: '30rem' }\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n appendTo=\"body\"\n>\n <div class=\"grid formgrid p-fluid\">\n <ng-container *ngFor=\"let f of open.fields\">\n <div class=\"field\" [ngClass]=\"f.spec.type === 'boolean' ? 'col-12' : 'col-6'\">\n <ng-container *ngIf=\"f.number as n\">\n <label class=\"text-sm\" [pTooltip]=\"n.tooltip || ''\">{{ n.label }}</label>\n <p-inputNumber\n [ngModel]=\"open.values[n.id]\"\n (ngModelChange)=\"open.values[n.id] = $event\"\n [min]=\"n.min\"\n [max]=\"n.max\"\n [step]=\"n.step\"\n [minFractionDigits]=\"n.fractionDigits\"\n [showButtons]=\"true\"\n ></p-inputNumber>\n </ng-container>\n\n <ng-container *ngIf=\"f.select as sel\">\n <label class=\"text-sm\" [pTooltip]=\"sel.tooltip || ''\">{{ sel.label }}</label>\n <p-dropdown\n [options]=\"sel.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"open.values[sel.id]\"\n (ngModelChange)=\"open.values[sel.id] = $event\"\n appendTo=\"body\"\n ></p-dropdown>\n </ng-container>\n\n <!-- Checkbox label sits beside the box and may run to a sentence, so\n it does not get the stacked label the other types use. -->\n <div *ngIf=\"f.spec.type === 'boolean'\" class=\"flex align-items-center gap-2\">\n <p-checkbox\n [ngModel]=\"open.values[f.spec.id]\"\n (ngModelChange)=\"open.values[f.spec.id] = $event\"\n [binary]=\"true\"\n [inputId]=\"open.tool.id + '-' + f.spec.id\"\n ></p-checkbox>\n <label [for]=\"open.tool.id + '-' + f.spec.id\" class=\"text-sm mb-0\" [pTooltip]=\"f.spec.tooltip || ''\">{{\n f.spec.label\n }}</label>\n </div>\n </div>\n </ng-container>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button\n label=\"Reset to model defaults\"\n [text]=\"true\"\n size=\"small\"\n (onClick)=\"resetToolParams(open.tool.id)\"\n ></p-button>\n <p-button label=\"Close\" size=\"small\" (onClick)=\"closeToolParams()\"></p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}.sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}.sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}.sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i7.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i7.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: i7.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SharedModule }, { kind: "directive", type: i1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i9.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i10.Checkbox, selector: "p-checkbox", inputs: ["value", "name", "disabled", "binary", "label", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "styleClass", "labelStyleClass", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i11.ContextMenu, selector: "p-contextMenu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "appendTo", "autoZIndex", "baseZIndex", "id", "ariaLabel", "ariaLabelledBy", "pressDelay"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i12.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i13.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "visible", "style", "position"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i14.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i15.InputNumber, selector: "p-inputNumber", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i16.ProgressBar, selector: "p-progressBar", inputs: ["value", "showValue", "styleClass", "style", "unit", "mode", "color"] }, { kind: "ngmodule", type: SliderModule }, { kind: "component", type: i17.Slider, selector: "p-slider", inputs: ["animate", "disabled", "min", "max", "orientation", "step", "range", "style", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i18.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "style", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: FloatingDragDirective, selector: "[vizFloatingDrag]", inputs: ["vizFloatingDrag"], outputs: ["vizFloatingDragMove"] }, { kind: "component", type: ToolbarComponent, selector: "plotting-toolbar", inputs: ["imageInfo", "hasSpatialDataset", "plotTypeOptions", "selectedPlotType", "basePlotType", "isoRange", "maxIndex", "zIndex", "isHeatmap", "is3dRegions", "activeDragMode", "activeSurface3dMode", "axesVisible", "wireframeActive", "resolutionScale", "wandSensitivity", "brushSize", "vertexEraserRadius", "canUndo", "canRedo", "stackOptions", "tools", "imageSmoothingEnabled", "samModels", "samModelId", "contributedTools", "dialogTools", "openDialogToolId", "toolModelIds"], outputs: ["selectPlotType", "addProfileLine", "toggleImageSmoothing", "isoRangeChange", "openChannelHistogram", "openSpatialControls", "openRegionEditor", "selectStackOption", "zScrub", "zSlide", "zIndexInput", "reloadAndPlot", "downloadImage", "autoscaleImage", "toggleDragMode", "zoomIn", "zoomOut", "toggleSurface3dMode", "resetSurfaceCamera", "toggleAxes", "toggleWireframe", "selectResolution", "deleteRegion", "undoRegion", "redoRegion", "segmentRegions", "segmentCellpose", "runTool", "toggleDialogTool", "toolModelChange", "openToolParams", "samModelChange", "wandSensitivityChange", "brushSizeChange", "vertexEraserRadiusChange"] }, { kind: "component", type: IntensityInsetComponent, selector: "viz-intensity-inset", inputs: ["divId", "plotDivName", "zIndex", "imageView", "imageInfo"] }, { kind: "component", type: ChannelHistogramComponent, selector: "channel-histogram", inputs: ["visible"], outputs: ["visibleChange"] }, { kind: "component", type: SpatialControlsComponent, selector: "spatial-controls", inputs: ["visible", "is3d"], outputs: ["visibleChange"] }, { kind: "component", type: RegionEditorComponent, selector: "region-editor" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2542
1098
|
}
|
|
2543
1099
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: VisualizerComponent, decorators: [{
|
|
2544
1100
|
type: Component,
|
|
2545
|
-
args: [{ selector: 'visualizer', template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ samStatus || message.detail }}</div>\n <p-progressBar *ngIf=\"samDownloading\" [value]=\"samProgress\" styleClass=\"mt-2 sam-progress\"></p-progressBar>\n <p-progressBar *ngIf=\"samBusy && !samDownloading\" mode=\"indeterminate\" styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Result/error notices from the library itself (segmentation outcome, region\n validation). Rendered here so they do not depend on the host mounting a\n keyless <p-toast> \u2014 without this the messages went nowhere in any host but\n jit-ui, and a failed run looked like a button that did nothing. -->\n<p-toast [key]=\"resultToastKey\" position=\"bottom-right\"></p-toast>\n\n<!-- Shared outlets for notices raised where there is no per-instance key to use:\n the root-provided PlotlyService and the region-editor dialog. Rendered by a\n single visualizer (ownsSharedToasts) so a message from the root service is\n not shown once per live viewer. -->\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizToastKey\"></p-toast>\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizAlertToastKey\" position=\"center\"></p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div class=\"toolbar-dock\" [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\">\n <div class=\"toolbar-drag-handle\" (mousedown)=\"onToolbarDragStart($event)\"\n pTooltip=\"Drag to float the toolbar\" tooltipPosition=\"bottom\">\n <i class=\"pi pi-bars\"></i>\n </div>\n<plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [hasSpatialDataset]=\"hasSpatialDataset\"\n [plotTypeOptions]=\"plotTypeMenu\"\n [selectedPlotType]=\"selectedPlotTypeId\"\n [basePlotType]=\"basePlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [is3dRegions]=\"isSpatial3dMode\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n [openDialogToolId]=\"openDialogToolId\"\n (toggleDialogTool)=\"toggleDialogTool($event)\"\n [toolModelIds]=\"toolModelIds\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"canUndoRegion\"\n [canRedo]=\"canRedoRegion\"\n [stackOptions]=\"stackOptions\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openSpatialControls)=\"openSpatialControls()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (runTool)=\"runTool($event)\"\n (toolModelChange)=\"onToolModelChange($event)\"\n (openToolParams)=\"openToolParams($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\">\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n</plotting-toolbar>\n <p-button *ngIf=\"toolbarFloating\" class=\"toolbar-dock-btn\" icon=\"pi pi-thumbtack\" size=\"small\" [text]=\"true\"\n pTooltip=\"Dock to top\" tooltipPosition=\"bottom\" (onClick)=\"dockToolbar()\">\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading || stackLoading || isCaching\" id=\"overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading; else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching; else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress\"></p-progressBar>\n <span class=\"text-loading\">{{loadingMessage}}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading && !zoom\" class=\"text-loading\">{{loadingMessage}}</span>\n <span *ngIf=\"imgLoading && zoom\" class=\"zoom-text-loading\">{{loadingMessage}}</span>\n </ng-template>\n </ng-template>\n <p-button *ngIf=\"stackLoading || imgLoading\" class=\"cancel-loading \" label=\"Cancel\"\n (onClick)=\"cancelLoading()\">\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"sharpening && !imgLoading && !stackLoading && !isCaching\" id=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n\n</div>\n\n<!-- Floating, draggable intensity-profile inset. Shown in LINE mode; the chart\n updates live as the line ROI on the image is moved. Kept OUTSIDE #plot and\n positioned fixed so it floats over the whole page (not clipped to the\n diagram/canvas area). -->\n<div *ngIf=\"hasProfiles\" class=\"intensity-panel\"\n [style.left.px]=\"profilePanelPos.x\" [style.top.px]=\"profilePanelPos.y\">\n <div class=\"intensity-panel-header\" (mousedown)=\"onProfilePanelDragStart($event)\">\n <span>Intensity profile</span>\n <i class=\"pi pi-arrows-alt\"></i>\n </div>\n <div [id]=\"intensityInsetDiv\" class=\"intensity-panel-plot\"></div>\n</div>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Spatial-omics controls. Renders its own empty state when no dataset (or no\n SPATIAL_DATA_PORT) is present, so it is always safe to host. -->\n<spatial-controls [(visible)]=\"showSpatialControls\" [is3d]=\"isSpatial3dMode\" ></spatial-controls>\n\n<!-- Side panel of the active contributed plot mode (PLOT_TYPE_CONTRIBUTIONS),\n in the same right-hand area as the spatial controls and the Region Editor.\n Exists only while the mode's session is live; there is no close button,\n because leaving the mode is what ends it. A component panel gets\n PLOT_MODE_CONTEXT / PLOT_MODE_SESSION from its injector; a `mount` panel's\n host element is attached to the slot below (see plotModePanelSlot). -->\n<p-dialog *ngIf=\"plotModePanel as panel\" [header]=\"panel.title\" [visible]=\"true\"\n [modal]=\"false\" [closable]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n appendTo=\"body\" position=\"right\" styleClass=\"plot-mode-panel\"\n [style]=\"{ width: '22rem', maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <ng-container *ngIf=\"panel.component\">\n <ng-container *ngComponentOutlet=\"panel.component; injector: panel.injector ?? undefined\"></ng-container>\n </ng-container>\n <div *ngIf=\"panel.host\" #plotModePanelSlot class=\"plot-mode-panel-slot\"></div>\n</p-dialog>\n\n<!-- An open dialog tool (TOOLBAR_TOOLS, kind: 'dialog'). Non-modal so the user can\n keep working on the image, e.g. painting with the brush the tool armed. The\n tool renders its body into a host element attached to the slot below (see\n toolDialogSlot); closing the dialog ends the tool's session. -->\n<p-dialog *ngIf=\"toolDialog as dlg\" [header]=\"dlg.title\" [visible]=\"true\"\n (visibleChange)=\"$event || closeDialogTool()\"\n [modal]=\"false\" [closable]=\"true\" [resizable]=\"true\" [draggable]=\"true\"\n appendTo=\"body\" position=\"right\" styleClass=\"tool-dialog\"\n [style]=\"{ width: dlg.width, maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <div #toolDialogSlot class=\"tool-dialog-slot\"></div>\n</p-dialog>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog header=\"Region Editor\" [(visible)]=\"showRegionEditor\"\n [modal]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n [maximizable]=\"true\" appendTo=\"body\" position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog header=\"Simplify region(s)\" [(visible)]=\"displaySimplifyDialog\" [modal]=\"true\"\n [style]=\"{width: '22rem'}\" [draggable]=\"false\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-2\">\n <span>Altitude threshold: <b>{{ simplifyThreshold }} px</b></span>\n <p-slider [(ngModel)]=\"simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"simplifyRegions(simplifyThreshold)\"></p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Parameters for whichever contributed tool is open. One dialog for all of\n them: a tool declares its fields (ToolParamSpec) and this renders them, so\n the toolbar can lay every tool out the same way and a plugin needs no\n PrimeNG of its own. Defaults come from the active checkpoint, since they\n encode what it was trained for. -->\n<p-dialog *ngIf=\"openParamsTool as tool\"\n [header]=\"tool.label + ' parameters'\"\n [visible]=\"true\" (visibleChange)=\"openParamsToolId = null\"\n [modal]=\"true\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\" appendTo=\"body\">\n <div class=\"grid formgrid p-fluid\">\n <ng-container *ngFor=\"let p of tool.params\">\n <div class=\"field\" [ngClass]=\"p.type === 'boolean' ? 'col-12' : 'col-6'\">\n <ng-container [ngSwitch]=\"p.type\">\n\n <ng-container *ngSwitchCase=\"'number'\">\n <label class=\"text-sm\" [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n <p-inputNumber [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n [min]=\"numberParam(p).min\" [max]=\"numberParam(p).max\"\n [step]=\"numberParam(p).step\"\n [minFractionDigits]=\"numberParam(p).fractionDigits\"\n [showButtons]=\"true\"></p-inputNumber>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'select'\">\n <label class=\"text-sm\" [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n <p-dropdown [options]=\"selectParam(p).options\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n appendTo=\"body\"></p-dropdown>\n </ng-container>\n\n <!-- Checkbox label sits beside the box and may run to a sentence, so\n it does not get the stacked label the other types use. -->\n <div *ngSwitchCase=\"'boolean'\" class=\"flex align-items-center gap-2\">\n <p-checkbox [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n [binary]=\"true\" [inputId]=\"tool.id + '-' + p.id\"></p-checkbox>\n <label [for]=\"tool.id + '-' + p.id\" class=\"text-sm mb-0\"\n [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n </div>\n\n </ng-container>\n </div>\n </ng-container>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Reset to model defaults\" [text]=\"true\" size=\"small\"\n (onClick)=\"resetToolParams(tool.id)\"></p-button>\n <p-button label=\"Close\" size=\"small\" (onClick)=\"openParamsToolId = null\"></p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}#overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}#sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}#sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}#sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.intensity-panel{position:fixed;z-index:1100;width:280px;height:180px;background:#191919e6;border:1px solid var(--primary-color, #3399ff);border-radius:6px;box-shadow:0 4px 14px #00000073;display:flex;flex-direction:column;overflow:hidden}.intensity-panel-header{display:flex;align-items:center;justify-content:space-between;padding:.25rem .5rem;cursor:move;-webkit-user-select:none;user-select:none;background:#ffffff14;color:#eee;font-size:.75rem;font-weight:600}.intensity-panel-header i{opacity:.6;font-size:.7rem}.intensity-panel-plot{flex:1 1 auto;width:100%}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"] }]
|
|
1101
|
+
args: [{ selector: 'visualizer', standalone: true, imports: [
|
|
1102
|
+
CommonModule,
|
|
1103
|
+
FormsModule,
|
|
1104
|
+
SharedModule,
|
|
1105
|
+
ButtonModule,
|
|
1106
|
+
CheckboxModule,
|
|
1107
|
+
ContextMenuModule,
|
|
1108
|
+
DialogModule,
|
|
1109
|
+
DropdownModule,
|
|
1110
|
+
InputNumberModule,
|
|
1111
|
+
ProgressBarModule,
|
|
1112
|
+
SliderModule,
|
|
1113
|
+
ToastModule,
|
|
1114
|
+
TooltipModule,
|
|
1115
|
+
FloatingDragDirective,
|
|
1116
|
+
ToolbarComponent,
|
|
1117
|
+
IntensityInsetComponent,
|
|
1118
|
+
ChannelHistogramComponent,
|
|
1119
|
+
SpatialControlsComponent,
|
|
1120
|
+
RegionEditorComponent,
|
|
1121
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ segmentation.status || message.detail }}</div>\n <p-progressBar\n *ngIf=\"segmentation.downloading\"\n [value]=\"segmentation.progress\"\n styleClass=\"mt-2 sam-progress\"\n ></p-progressBar>\n <p-progressBar\n *ngIf=\"segmentation.busy && !segmentation.downloading\"\n mode=\"indeterminate\"\n styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"\n ></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Result/error notices from the library itself (segmentation outcome, region\n validation). Rendered here so they do not depend on the host mounting a\n keyless <p-toast> \u2014 without this the messages went nowhere in any host but\n jit-ui, and a failed run looked like a button that did nothing. -->\n<p-toast [key]=\"resultToastKey\" position=\"bottom-right\"></p-toast>\n\n<!-- Shared outlets for notices raised where there is no per-instance key to use:\n the root-provided PlotlyService and the region-editor dialog. Rendered by a\n single visualizer (ownsSharedToasts) so a message from the root service is\n not shown once per live viewer. -->\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizToastKey\"></p-toast>\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizAlertToastKey\" position=\"center\"></p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div\n class=\"toolbar-dock\"\n [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\"\n>\n <div\n class=\"toolbar-drag-handle\"\n [vizFloatingDrag]=\"toolbarDragOrigin\"\n (vizFloatingDragMove)=\"toolbarPos = $event\"\n pTooltip=\"Drag to float the toolbar\"\n tooltipPosition=\"bottom\"\n >\n <i class=\"pi pi-bars\"></i>\n </div>\n <plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [hasSpatialDataset]=\"hasSpatialDataset\"\n [plotTypeOptions]=\"plotTypeMenu\"\n [selectedPlotType]=\"selectedPlotTypeId\"\n [basePlotType]=\"basePlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [is3dRegions]=\"isSpatial3dMode\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n [openDialogToolId]=\"openDialogToolId\"\n (toggleDialogTool)=\"toggleDialogTool($event)\"\n [toolModelIds]=\"toolParams.toolModelIds\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"regionActions.canUndo\"\n [canRedo]=\"regionActions.canRedo\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openSpatialControls)=\"openSpatialControls()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (runTool)=\"runTool($event)\"\n (toolModelChange)=\"onToolModelChange($event)\"\n (openToolParams)=\"openToolParams($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\"\n >\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n </plotting-toolbar>\n <p-button\n *ngIf=\"toolbarFloating\"\n class=\"toolbar-dock-btn\"\n icon=\"pi pi-thumbtack\"\n size=\"small\"\n [text]=\"true\"\n pTooltip=\"Dock to top\"\n tooltipPosition=\"bottom\"\n (onClick)=\"dockToolbar()\"\n >\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading() || stackLoading() || isCaching()\" class=\"loading-overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading(); else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage()\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching(); else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress()\"></p-progressBar>\n <span class=\"text-loading\">{{ loadingMessage() }}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading()\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading() && !zoom()\" class=\"text-loading\">{{ loadingMessage() }}</span>\n <span *ngIf=\"imgLoading() && zoom()\" class=\"zoom-text-loading\">{{ loadingMessage() }}</span>\n </ng-template>\n </ng-template>\n <p-button\n *ngIf=\"stackLoading() || imgLoading()\"\n class=\"cancel-loading\"\n label=\"Cancel\"\n (onClick)=\"cancelLoading()\"\n >\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"render.sharpening && !imgLoading() && !stackLoading() && !isCaching()\" class=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n</div>\n\n<!-- Floating, draggable intensity-profile inset, shown while any intensity line\n exists. Kept OUTSIDE the plot div and fixed to the viewport, so it floats over\n the whole page rather than being clipped to the canvas. -->\n<viz-intensity-inset\n [divId]=\"intensityInsetDiv\"\n [plotDivName]=\"plotDivName\"\n [zIndex]=\"zIndex\"\n [imageView]=\"isImageView\"\n [imageInfo]=\"imageInfo\"\n></viz-intensity-inset>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Spatial-omics controls. Renders its own empty state when no dataset (or no\n SPATIAL_DATA_PORT) is present, so it is always safe to host. -->\n<spatial-controls [(visible)]=\"showSpatialControls\" [is3d]=\"isSpatial3dMode\"></spatial-controls>\n\n<!-- Side panel of the active contributed plot mode (PLOT_TYPE_CONTRIBUTIONS),\n in the same right-hand area as the spatial controls and the Region Editor.\n Exists only while the mode's session is live; there is no close button,\n because leaving the mode is what ends it. A component panel gets\n PLOT_MODE_CONTEXT / PLOT_MODE_SESSION from its injector; a `mount` panel's\n host element is attached to the slot below (see plotModePanelSlot). -->\n<p-dialog\n *ngIf=\"plotModePanel as panel\"\n [header]=\"panel.title\"\n [visible]=\"true\"\n [modal]=\"false\"\n [closable]=\"false\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n styleClass=\"plot-mode-panel\"\n [style]=\"{ width: '22rem', maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <ng-container *ngIf=\"panel.component\">\n <ng-container *ngComponentOutlet=\"panel.component; injector: panel.injector ?? undefined\"></ng-container>\n </ng-container>\n <div *ngIf=\"panel.host\" #plotModePanelSlot class=\"plot-mode-panel-slot\"></div>\n</p-dialog>\n\n<!-- An open dialog tool (TOOLBAR_TOOLS, kind: 'dialog'). Non-modal so the user can\n keep working on the image, e.g. painting with the brush the tool armed. The\n tool renders its body into a host element attached to the slot below (see\n toolDialogSlot); closing the dialog ends the tool's session. -->\n<p-dialog\n *ngIf=\"toolDialog as dlg\"\n [header]=\"dlg.title\"\n [visible]=\"true\"\n (visibleChange)=\"$event || closeDialogTool()\"\n [modal]=\"false\"\n [closable]=\"true\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n styleClass=\"tool-dialog\"\n [style]=\"{ width: dlg.width, maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <div #toolDialogSlot class=\"tool-dialog-slot\"></div>\n</p-dialog>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog\n header=\"Region Editor\"\n [(visible)]=\"showRegionEditor\"\n [modal]=\"false\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n [maximizable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog\n header=\"Simplify region(s)\"\n [(visible)]=\"displaySimplifyDialog\"\n [modal]=\"true\"\n [style]=\"{ width: '22rem' }\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n>\n <div class=\"flex flex-column gap-2\">\n <span\n >Altitude threshold: <b>{{ regionActions.simplifyThreshold }} px</b></span\n >\n <p-slider [(ngModel)]=\"regionActions.simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button\n label=\"Apply\"\n icon=\"pi pi-check\"\n (onClick)=\"simplifyRegions(regionActions.simplifyThreshold)\"\n ></p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Parameters for whichever contributed tool is open. One dialog for all of\n them: a tool declares its fields (ToolParamSpec) and this renders them, so\n the toolbar can lay every tool out the same way and a plugin needs no\n PrimeNG of its own. Defaults come from the active checkpoint, since they\n encode what it was trained for. -->\n<p-dialog\n *ngIf=\"toolParams.openParams as open\"\n [header]=\"open.tool.label + ' parameters'\"\n [visible]=\"true\"\n (visibleChange)=\"closeToolParams()\"\n [modal]=\"true\"\n [style]=\"{ width: '30rem' }\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n appendTo=\"body\"\n>\n <div class=\"grid formgrid p-fluid\">\n <ng-container *ngFor=\"let f of open.fields\">\n <div class=\"field\" [ngClass]=\"f.spec.type === 'boolean' ? 'col-12' : 'col-6'\">\n <ng-container *ngIf=\"f.number as n\">\n <label class=\"text-sm\" [pTooltip]=\"n.tooltip || ''\">{{ n.label }}</label>\n <p-inputNumber\n [ngModel]=\"open.values[n.id]\"\n (ngModelChange)=\"open.values[n.id] = $event\"\n [min]=\"n.min\"\n [max]=\"n.max\"\n [step]=\"n.step\"\n [minFractionDigits]=\"n.fractionDigits\"\n [showButtons]=\"true\"\n ></p-inputNumber>\n </ng-container>\n\n <ng-container *ngIf=\"f.select as sel\">\n <label class=\"text-sm\" [pTooltip]=\"sel.tooltip || ''\">{{ sel.label }}</label>\n <p-dropdown\n [options]=\"sel.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"open.values[sel.id]\"\n (ngModelChange)=\"open.values[sel.id] = $event\"\n appendTo=\"body\"\n ></p-dropdown>\n </ng-container>\n\n <!-- Checkbox label sits beside the box and may run to a sentence, so\n it does not get the stacked label the other types use. -->\n <div *ngIf=\"f.spec.type === 'boolean'\" class=\"flex align-items-center gap-2\">\n <p-checkbox\n [ngModel]=\"open.values[f.spec.id]\"\n (ngModelChange)=\"open.values[f.spec.id] = $event\"\n [binary]=\"true\"\n [inputId]=\"open.tool.id + '-' + f.spec.id\"\n ></p-checkbox>\n <label [for]=\"open.tool.id + '-' + f.spec.id\" class=\"text-sm mb-0\" [pTooltip]=\"f.spec.tooltip || ''\">{{\n f.spec.label\n }}</label>\n </div>\n </div>\n </ng-container>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button\n label=\"Reset to model defaults\"\n [text]=\"true\"\n size=\"small\"\n (onClick)=\"resetToolParams(open.tool.id)\"\n ></p-button>\n <p-button label=\"Close\" size=\"small\" (onClick)=\"closeToolParams()\"></p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}.sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}.sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}.sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"] }]
|
|
2546
1122
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
2547
1123
|
type: Inject,
|
|
2548
1124
|
args: [IMAGE_STATE_PORT]
|
|
@@ -2571,7 +1147,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
|
|
|
2571
1147
|
args: [PLOT_TYPE_CONTRIBUTIONS]
|
|
2572
1148
|
}] }, { type: i0.Injector, decorators: [{
|
|
2573
1149
|
type: Optional
|
|
2574
|
-
}] }
|
|
1150
|
+
}] }, { type: i0.ElementRef, decorators: [{
|
|
1151
|
+
type: Optional
|
|
1152
|
+
}] }, { type: i0.DestroyRef }], propDecorators: { isStackEvent: [{
|
|
2575
1153
|
type: Output
|
|
2576
1154
|
}], isGrayscaleEvent: [{
|
|
2577
1155
|
type: Output
|
|
@@ -2585,11 +1163,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
|
|
|
2585
1163
|
}], plotModePanelSlot: [{
|
|
2586
1164
|
type: ViewChild,
|
|
2587
1165
|
args: ['plotModePanelSlot']
|
|
2588
|
-
}],
|
|
2589
|
-
type:
|
|
2590
|
-
args: [
|
|
1166
|
+
}], inset: [{
|
|
1167
|
+
type: ViewChild,
|
|
1168
|
+
args: [IntensityInsetComponent, { static: true }]
|
|
2591
1169
|
}], toolDialogSlot: [{
|
|
2592
1170
|
type: ViewChild,
|
|
2593
1171
|
args: ['toolDialogSlot']
|
|
2594
1172
|
}] } });
|
|
2595
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlzdWFsaXplci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3Zpc3VhbGl6ZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vc3JjL2xpYi92aXN1YWxpemVyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxTQUFTLEVBQTZCLFlBQVksRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFnQyxRQUFRLEVBQUUsTUFBTSxFQUF1QixTQUFTLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFck8sT0FBTyxFQUErQixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUUzQyxPQUFPLEVBQVksY0FBYyxFQUFZLE1BQU0sYUFBYSxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUVsRCxPQUFPLEVBQWtCLGdCQUFnQixFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDdEYsT0FBTyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFVLE1BQU0saUJBQWlCLENBQUM7QUFDM0UsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3JFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUNuRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsYUFBYSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDMUUsT0FBTyxFQUNMLFFBQVEsRUFHUixxQkFBcUIsRUFDckIsaUJBQWlCLEVBQ2pCLFVBQVUsRUFDVixjQUFjLEVBQ2QsZ0JBQWdCLEVBQ2hC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LENBQUMsbUJBQW1CLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDOUMsQ0FBQzs4R0FwakZVLG1CQUFtQixrQkFtWHBCLGdCQUFnQixhQUNoQixVQUFVLHdRQVNFLFVBQVUsNkJBSVYsYUFBYSw2QkFHYixpQkFBaUIsNkJBR2pCLHVCQUF1QjtrR0F2WWxDLG1CQUFtQix1a0JDNUVoQywwbGZBaVNBOzsyRkRyTmEsbUJBQW1CO2tCQVAvQixTQUFTOytCQUdFLFlBQVk7OzBCQXVYbkIsTUFBTTsyQkFBQyxnQkFBZ0I7OzBCQUN2QixNQUFNOzJCQUFDLFVBQVU7OzBCQVNqQixRQUFROzswQkFBSSxNQUFNOzJCQUFDLFVBQVU7OzBCQUk3QixRQUFROzswQkFBSSxNQUFNOzJCQUFDLGFBQWE7OzBCQUdoQyxRQUFROzswQkFBSSxNQUFNOzJCQUFDLGlCQUFpQjs7MEJBR3BDLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsdUJBQXVCOzswQkFHMUMsUUFBUTt5Q0FyV1gsWUFBWTtzQkFEWCxNQUFNO2dCQUdQLGdCQUFnQjtzQkFEZixNQUFNO2dCQU1ILFlBQVk7c0JBRGYsS0FBSztnQkFjTixRQUFRO3NCQURQLEtBQUs7Z0JBaURXLFdBQVc7c0JBQTNCLFNBQVM7dUJBQUMsSUFBSTtnQkFzS1gsaUJBQWlCO3NCQURwQixTQUFTO3VCQUFDLG1CQUFtQjtnQkFvdEM5QixTQUFTO3NCQURSLFlBQVk7dUJBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxRQUFRLENBQUM7Z0JBK2lDdEMsY0FBYztzQkFEakIsU0FBUzt1QkFBQyxnQkFBZ0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFuZ2VEZXRlY3RvclJlZiwgQ29tcG9uZW50LCBBZnRlclZpZXdJbml0LCBFbGVtZW50UmVmLCBFdmVudEVtaXR0ZXIsIEhvc3RMaXN0ZW5lciwgSW5qZWN0LCBJbmplY3RvciwgSW5wdXQsIE5nWm9uZSwgT25DaGFuZ2VzLCBPbkRlc3Ryb3ksIE9uSW5pdCwgT3B0aW9uYWwsIE91dHB1dCwgU2ltcGxlQ2hhbmdlcywgVHlwZSwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgT2JzZXJ2YWJsZSwgU3ViamVjdCwgU3Vic2NyaXB0aW9uIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyB0YWtlVW50aWwgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5cbmltcG9ydCB7IE1lbnVJdGVtLCBNZXNzYWdlU2VydmljZSwgVHJlZU5vZGUgfSBmcm9tICdwcmltZW5nL2FwaSc7XG5pbXBvcnQgeyBDb250ZXh0TWVudSB9IGZyb20gJ3ByaW1lbmcvY29udGV4dG1lbnUnO1xuaW1wb3J0IHsgSUltYWdlSW5mbyB9IGZyb20gJy4vY29udHJhY3RzL2ltYWdlLmNvbnRyYWN0JztcbmltcG9ydCB7IEltYWdlU3RhdGVQb3J0LCBJTUFHRV9TVEFURV9QT1JUIH0gZnJvbSAnLi9jb250cmFjdHMvcG9ydHMvaW1hZ2Utc3RhdGUucG9ydCc7XG5pbXBvcnQgeyBQb2x5Z29uLCBSZWN0YW5nbGUsIE11bHRpUG9seWdvbiwgUmVnaW9uIH0gZnJvbSAnLi9tb2RlbHMvcmVnaW9uJztcbmltcG9ydCB7IFJlZ2lvbk9wc1NlcnZpY2UgfSBmcm9tICcuL3JlZ2lvbi1vcHMuc2VydmljZSc7XG5pbXBvcnQgeyBWSVpfVE9BU1RfS0VZLCBWSVpfQUxFUlRfVE9BU1RfS0VZIH0gZnJvbSAnLi90b2FzdC1vdXRsZXRzJztcbmltcG9ydCB7IFZpc3VhbGl6ZXJTdG9yZSB9IGZyb20gJy4vc3RvcmUvdmlzdWFsaXplci1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IFJlbmRlck9yY2hlc3RyYXRvciwgU2xpY2VTY3J1YmJlciB9IGZyb20gJy4vcmVuZGVyLW9yY2hlc3RyYXRvcic7XG5pbXBvcnQge1xuICBQbG90VHlwZSxcbiAgUGxvdFR5cGVEZXNjcmlwdG9yLFxuICBQbG90VHlwZUlkLFxuICBnZXRQbG90VHlwZURlc2NyaXB0b3IsXG4gIGlzQnVpbHRpblBsb3RUeXBlLFxuICBpc05hcGFyaTNkLFxuICBpc1NwYXRpYWxPbWljcyxcbiAgaXNTcGF0aWFsT21pY3MzZCxcbiAgcmVuZGVyZXJPd25zV2hlZWwsXG4gIE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFLFxufSBmcm9tICcuL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuaW1wb3J0IHtcbiAgUExPVF9NT0RFX0NPTlRFWFQsXG4gIFBMT1RfTU9ERV9TRVNTSU9OLFxuICBQTE9UX1RZUEVfQ09OVFJJQlVUSU9OUyxcbiAgUGxvdE1vZGVCcnVzaENsYXNzLFxuICBQbG90TW9kZUNvbnRleHQsXG4gIFBsb3RNb2RlVG9vbHMsXG4gIFBsb3RUeXBlQ29udHJpYnV0aW9uLFxuICBQbG90VHlwZU9wdGlvbixcbiAgY29udHJpYnV0ZWRQbG90VHlwZU9wdGlvbixcbn0gZnJvbSAnLi9jb250cmFjdHMvcGxvdC10eXBlLWNvbnRyaWJ1dGlvbi5jb250cmFjdCc7XG5pbXBvcnQgeyBBY3RpdmVQbG90TW9kZSwgUGxvdE1vZGVDb250cm9sbGVyIH0gZnJvbSAnLi9wbG90LW1vZGUvcGxvdC1tb2RlLWNvbnRyb2xsZXInO1xuaW1wb3J0IHsgVmlld2VyRmVhdHVyZSB9IGZyb20gJy4vY29udHJhY3RzL2NhcGFiaWxpdGllcy5jb250cmFjdCc7XG5pbXBvcnQgeyBJbnRlbnNpdHlQcm9maWxlLCBJVmlzdWFsaXplciwgVklTVUFMSVpFUiB9IGZyb20gJy4vY29udHJhY3RzL3Zpc3VhbGl6ZXIuY29udHJhY3QnO1xuaW1wb3J0IHsgU0FNX01PREVMUywgZ2V0RGVmYXVsdFNhbU1vZGVsSWQsIGlzU2FtTW9kZWxSZWFkeSB9IGZyb20gJy4vdG9vbGJhci9zZWdtZW50YXRpb24vc2FtLW1vZGVsLXJlZ2lzdHJ5JztcbmltcG9ydCB7IFNhbVRvb2xTZXJ2aWNlIH0gZnJvbSAnLi90b29sYmFyL3NlZ21lbnRhdGlvbi9zYW0tdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IFNhbVBvaW50VG9vbFNlcnZpY2UgfSBmcm9tICcuL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS1wb2ludC10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ2VsbFNlZ21lbnRUb29sU2VydmljZSB9IGZyb20gJy4vdG9vbGJhci9zZWdtZW50YXRpb24vY2VsbC1zZWdtZW50LXRvb2wuc2VydmljZSc7XG5pbXBvcnQge1xuICBUT09MQkFSX1RPT0xTLFxuICBOdW1iZXJQYXJhbVNwZWMsXG4gIFNlbGVjdFBhcmFtU3BlYyxcbiAgVG9vbFBhcmFtU3BlYyxcbiAgVG9vbGJhckNvbnRyaWJ1dGlvbixcbiAgVG9vbGJhckRpYWxvZ1Rvb2xDb250cmlidXRpb24sXG4gIFRvb2xiYXJUb29sQ29udHJpYnV0aW9uLFxuICBUb29sRGlhbG9nQ29udGV4dCxcbiAgZGlhbG9nVG9vbENvbnRyaWJ1dGlvbnMsXG4gIHZpc2libGVUb29sQ29udHJpYnV0aW9ucyxcbn0gZnJvbSAnLi9jb250cmFjdHMvdG9vbGJhci10b29sLmNvbnRyYWN0JztcbmltcG9ydCB7IFJlZ2lvblRvb2xNb2RlIH0gZnJvbSAnLi9jb250cmFjdHMvcmVnaW9uLW92ZXJsYXkuY29udHJhY3QnO1xuaW1wb3J0IHsgVG9vbGJhclRvb2xWaXNpYmlsaXR5LCBBTExfVE9PTEJBUl9UT09MUyB9IGZyb20gJy4vY29udHJhY3RzL3Rvb2xiYXItY29uZmlnJztcbmltcG9ydCB7IFZJWl9DT05GSUcsIFZpekNvbmZpZyB9IGZyb20gJy4vY29udHJhY3RzL3Zpei1jb25maWcnO1xuaW1wb3J0IHsgU1BBVElBTF9EQVRBX1BPUlQsIFNwYXRpYWxEYXRhUG9ydCB9IGZyb20gJy4vY29udHJhY3RzL3BvcnRzL3NwYXRpYWwtZGF0YS5wb3J0JztcbmltcG9ydCB7IFNwYXRpYWxEYXRhc2V0IH0gZnJvbSAnLi9jb250cmFjdHMvc3BhdGlhbC1kYXRhc2V0LmNvbnRyYWN0JztcbmltcG9ydCB7IGJ1aWxkVm9sdW1lU3RhY2tJbWFnZSB9IGZyb20gJy4vc3BhdGlhbC9zcGF0aWFsLXZvbHVtZS1pbWFnZSc7XG5cbi8qKiBQZXItaW5zdGFuY2UgcGxvdC1kaXYgaWQgc291cmNlLiBUaGUgbW91bnQgZWxlbWVudCdzIGlkIG11c3QgYmUgdW5pcXVlIHNvIHR3b1xuICogIGxpdmUgdmlld2VycyAoZS5nLiB0aGUgbWFpbiBkaWFncmFtICsgYSBtb2RhbCBwcmV2aWV3KSBkb24ndCBjb2xsaWRlIG9uIHRoZVxuICogIHNhbWUgRE9NIGlkIOKAlCBgZ2V0RWxlbWVudEJ5SWRgIHdvdWxkIG90aGVyd2lzZSByZXR1cm4gd2hpY2hldmVyIGNhbWUgZmlyc3QuXG4gKiAgU3R5bGluZyBoYW5ncyBvZmYgdGhlIGAudml6LXBsb3RgIGNsYXNzIGluc3RlYWQgb2YgdGhlIGlkLiAqL1xubGV0IHBsb3RJbnN0YW5jZVNlcSA9IDA7XG5cbkBDb21wb25lbnQoe1xuICAvLyBDYW5vbmljYWwgcHJlZml4ZWQgc2VsZWN0b3IgZmlyc3Q7IHRoZSB1bnByZWZpeGVkIG9yaWdpbmFsIGlzIGtlcHQgYXMgYW5cbiAgLy8gYWxpYXMgZm9yIG9uZSByZWxlYXNlIChwcmUtcHVibGljYXRpb24gYmFjay1jb21wYXQpLlxuICBzZWxlY3RvcjogJ3Zpc3VhbGl6ZXInLFxuICB0ZW1wbGF0ZVVybDogJy4vdmlzdWFsaXplci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3Zpc3VhbGl6ZXIuY29tcG9uZW50LnNjc3MnXSxcbn0pXG5leHBvcnQgY2xhc3MgVmlzdWFsaXplckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzLCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xuICAvKiogUGVyLWluc3RhbmNlIHRvYXN0IGtleS4gTWVzc2FnZVNlcnZpY2UgaXMgYSBnbG9iYWwgc2luZ2xldG9uLCBzbyB0d28gbGl2ZVxuICAgKiAgYDx2aXN1YWxpemVyPmAgaW5zdGFuY2VzIChtYWluIHZpZXdlciArIHBpcGVsaW5lLWRpYWxvZyBwcmV2aWV3KSBzaGFyaW5nXG4gICAqICBvbmUga2V5IHdvdWxkIGVhY2ggcmVuZGVyIHRoZSBzYW1lIG1lc3NhZ2Ug4oCUIGEgZHVwbGljYXRlIHRvYXN0LiBBIHVuaXF1ZVxuICAgKiAga2V5IHBlciBpbnN0YW5jZSBzY29wZXMgdGhlIHRvYXN0IHRvIHRoZSBjb21wb25lbnQgdGhhdCByYWlzZWQgaXQuICovXG4gIHByaXZhdGUgc3RhdGljIG5leHRUb2FzdElkID0gMDtcblxuICAvKipcbiAgICogVGhlIGxpYnJhcnkgYWxzbyByYWlzZXMgbm90aWNlcyBmcm9tIHBsYWNlcyB0aGF0IGhhdmUgbm8gaW5zdGFuY2Ugb2YgdGhlaXJcbiAgICogb3duIHRvIGtleSBhZ2FpbnN0OiB7QGxpbmsgUGxvdGx5U2VydmljZX0gaXMgYHByb3ZpZGVkSW46ICdyb290J2AsIGFuZCB0aGVcbiAgICogcmVnaW9uIGVkaXRvciBpcyBhIGNoaWxkIGRpYWxvZy4gVGhvc2UgdXNlIHRoZSBmaXhlZFxuICAgKiB7QGxpbmsgVklaX1RPQVNUX0tFWX0gLyB7QGxpbmsgVklaX0FMRVJUX1RPQVNUX0tFWX0gb3V0bGV0cy5cbiAgICpcbiAgICogRm9yIHRoZSBzYW1lIHJlYXNvbiB0aGUgcGVyLWluc3RhbmNlIGtleSBhYm92ZSBleGlzdHMsIGV4YWN0bHkgT05FIG1vdW50ZWRcbiAgICogdmlzdWFsaXplciBtYXkgcmVuZGVyIHRob3NlIHNoYXJlZCBvdXRsZXRzIOKAlCBvdGhlcndpc2UgYSBtZXNzYWdlIGZyb20gdGhlXG4gICAqIHJvb3Qgc2VydmljZSB3b3VsZCBiZSBzaG93biBvbmNlIHBlciBsaXZlIHZpZXdlciAoaml0LXVpIHJ1bnMgYSBtYWluIHZpZXdcbiAgICogYW5kIGEgcGlwZWxpbmUtcHJldmlldyBhdCB0aGUgc2FtZSB0aW1lKS4gVGhlIGZpcnN0IHRvIGluaXQgY2xhaW1zIHRoZW0gYW5kXG4gICAqIHJlbGVhc2VzIG9uIGRlc3Ryb3ksIHNvIHRoZSBzdXJ2aXZpbmcgdmlld2VyIHBpY2tzIHRoZW0gdXAuXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBsaXZlSW5zdGFuY2VzID0gbmV3IFNldDxWaXN1YWxpemVyQ29tcG9uZW50PigpO1xuICByZWFkb25seSB2aXpUb2FzdEtleSA9IFZJWl9UT0FTVF9LRVk7XG4gIHJlYWRvbmx5IHZpekFsZXJ0VG9hc3RLZXkgPSBWSVpfQUxFUlRfVE9BU1RfS0VZO1xuICAvKipcbiAgICogV2hldGhlciB0aGlzIGluc3RhbmNlIHJlbmRlcnMgdGhlIHNoYXJlZCBvdXRsZXRzLiBUaGUgb3duZXIgaXMgc2ltcGx5IHRoZVxuICAgKiBvbGRlc3QgbGl2ZSB2aXN1YWxpemVyIOKAlCBhIGBTZXRgIGl0ZXJhdGVzIGluIGluc2VydGlvbiBvcmRlciwgc28gd2hlbiB0aGVcbiAgICogb3duZXIgaXMgZGVzdHJveWVkIHRoZSBuZXh0IG9uZSB0YWtlcyBvdmVyIG9uIGl0cyBmb2xsb3dpbmcgY2hhbmdlLWRldGVjdGlvblxuICAgKiBwYXNzLiBUcmFja2luZyBhIHNldCByYXRoZXIgdGhhbiBhIHNpbmdsZSBvd25lciBtYXR0ZXJzOiB3aXRoIGEgbG9uZVxuICAgKiBgb3duZXJgIHJlZmVyZW5jZSwgdGVhcmluZyBkb3duIHRoZSBvd25lciB3aGlsZSBhIHNpYmxpbmcgd2FzIGFscmVhZHlcbiAgICogaW5pdGlhbGlzZWQgd291bGQgbGVhdmUgbm9ib2R5IHJlbmRlcmluZyB0aGUgb3V0bGV0cywgYW5kIGV2ZXJ5IG5vdGljZSBmcm9tXG4gICAqIHRoZSByb290IHNlcnZpY2Ugd291bGQgc2lsZW50bHkgdmFuaXNoIGFnYWluLlxuICAgKi9cbiAgZ2V0IG93bnNTaGFyZWRUb2FzdHMoKTogYm9vbGVhbiB7XG4gICAgY29uc3Qgb2xkZXN0ID0gVmlzdWFsaXplckNvbXBvbmVudC5saXZlSW5zdGFuY2VzLnZhbHVlcygpLm5leHQoKTtcbiAgICByZXR1cm4gIW9sZGVzdC5kb25lICYmIG9sZGVzdC52YWx1ZSA9PT0gdGhpcztcbiAgfVxuXG4gIEBPdXRwdXQoKVxuICBpc1N0YWNrRXZlbnQgPSBuZXcgRXZlbnRFbWl0dGVyKGZhbHNlKTtcbiAgQE91dHB1dCgpXG4gIGlzR3JheXNjYWxlRXZlbnQgPSBuZXcgRXZlbnRFbWl0dGVyKGZhbHNlKTtcblxuICAvKiogV2hpY2ggdG9vbGJhciBncm91cHMgdG8gc2hvdy4gQSBwYXJ0aWFsIG92ZXJyaWRlIG1lcmdlcyBvdmVyIHRoZSBmdWxsIHNldCxcbiAgICogIHNvIGB7IHNwZWNpYWxUb29sczogZmFsc2UgfWAgaGlkZXMgb25seSB0aGF0IGdyb3VwLiBEZWZhdWx0cyB0byBldmVyeXRoaW5nLiAqL1xuICBASW5wdXQoKVxuICBzZXQgdG9vbGJhclRvb2xzKHY6IFRvb2xiYXJUb29sVmlzaWJpbGl0eSB8IHVuZGVmaW5lZCkge1xuICAgIHRoaXMuX3Rvb2xiYXJUb29scyA9IHsgLi4uQUxMX1RPT0xCQVJfVE9PTFMsIC4uLih2ID8/IHt9KSB9O1xuICB9XG5cbiAgZ2V0IHRvb2xiYXJUb29scygpOiBSZXF1aXJlZDxUb29sYmFyVG9vbFZpc2liaWxpdHk+IHtcbiAgICByZXR1cm4gdGhpcy5fdG9vbGJhclRvb2xzO1xuICB9XG4gIHByaXZhdGUgX3Rvb2xiYXJUb29sczogUmVxdWlyZWQ8VG9vbGJhclRvb2xWaXNpYmlsaXR5PiA9IEFMTF9UT09MQkFSX1RPT0xTO1xuXG4gIC8qKiBFeHBvc2UgdGVzdC1vbmx5IHBsb3QgbW9kZXMgaW4gdGhlIHNlbGVjdG9yIChlLmcuIHRoZSBuYXBhcmkgYEltYWdlYFxuICAgKiAgbW9kZSwgb3RoZXJ3aXNlIGhpZGRlbikuIFRoZSBob3N0IGFwcCBzZXRzIHRoaXMgZnJvbSBhIGA/dGVzdD0xYCBVUkxcbiAgICogIHRvZ2dsZS4gT2ZmIGJ5IGRlZmF1bHQsIHNvIHByb2R1Y3Rpb24gdXNlcnMgbmV2ZXIgc2VlIHRoZW0uICovXG4gIEBJbnB1dCgpXG4gIHRlc3RNb2RlID0gZmFsc2U7XG5cbiAgLyoqIEltYWdlLXNtb290aGluZyBzdGF0ZSBmb3IgdGhlIHRvb2xiYXIncyBTbW9vdGhlbiB0b2dnbGUgKE9TRCBvbmx5KS5cbiAgICogIERlZmF1bHRzIHRvIGBmYWxzZWAgc28gT1NEIHNob3dzIHJhdyBwaXhlbHMgKG5lYXJlc3QtbmVpZ2hib3VyKS4gKi9cbiAgaW1hZ2VTbW9vdGhpbmdFbmFibGVkID0gZmFsc2U7XG4gIGxvYWRpbmdNZXNzYWdlID0gJ0xvYWRpbmcgaW1hZ2UuLi4nO1xuICB6b29tID0gZmFsc2U7XG4gIGZpbGVOYW1lOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIHByaXZhdGUgbG9hZGVkRmlsZU5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkO1xuXG4gIHByaXZhdGUgdW5zdWIgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICBwcml2YXRlIHByZXZpZXdTdWJzY3JpcHRpb24gPSBuZXcgU3Vic2NyaXB0aW9uKCk7XG5cbiAgcHVibGljIHN0YWNrTG9hZGluZyA9IGZhbHNlO1xuICBwdWJsaWMgaW1nTG9hZGluZyA9IGZhbHNlO1xuICBwdWJsaWMgbG9hZGluZ1BlcmNlbnRhZ2UgPSAwO1xuICAvLyBOb24tbnVsbCAoMC4uMTAwKSB3aGlsZSBqaXQtc2VydmljZSBpcyBjb3B5aW5nIHRoZSBzb3VyY2UgZmlsZSBpbnRvIHRoZVxuICAvLyBsb2NhbCBjYWNoZSBQVkMgb24gZmlyc3QgY2xpY2suIFdoZW4gc2V0LCB0aGUgbG9hZGluZyBvdmVybGF5IHN3YXBzIHRoZVxuICAvLyBzcGlubmVyIGZvciBhIGRldGVybWluYXRlIHByb2dyZXNzIGJhciB3aXRoIGEgXCJDYWNoaW5nIGltYWdlXCIgbWVzc2FnZS5cbiAgcHVibGljIGNhY2hlUHJvZ3Jlc3M6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICAvLyBUcnVlIGJldHdlZW4gdGhlIHNtYWxsLXRpZXIgcmVuZGVyIGxhbmRpbmcgYW5kIHRoZSBsYXJnZS10aWVyIHJlbmRlclxuICAvLyBsYW5kaW5nIGluIHRoZSBtdWx0aS10aWVyIHJlbmRlcmluZyBwYXRoLiBUaGUgdGVtcGxhdGUgdXNlcyB0aGlzIHRvXG4gIC8vIG92ZXJsYXkgYSB0cmFuc2x1Y2VudCBzcGlubmVyIG9uIHRvcCBvZiB0aGUgYmx1cnJ5IHNtYWxsLXRpZXIgaW1hZ2Ugc29cbiAgLy8gdGhlIHVzZXIgZG9lc24ndCBtaXN0YWtlIGl0IGZvciB0aGUgZmluYWwgcHJldmlldy5cbiAgcHVibGljIHNoYXJwZW5pbmcgPSBmYWxzZTtcbiAgLyoqIEEgaml0LXNlcnZpY2UgY2FjaGUgY29weSBpcyBhY3RpdmVseSBpbiBwcm9ncmVzcyAoZGV0ZXJtaW5hdGUgMC4uOTkpLiBEcml2ZXMgdGhlIGNhY2hlXG4gICAqICBwcm9ncmVzcyBiYXIgaW5kZXBlbmRlbnRseSBvZiBgaW1nTG9hZGluZ2Agc28gdGhlIGJhciBjYW4ndCBiZSBkcm9wcGVkIOKAlCBvciBtYXNrZWQgYnkgdGhlXG4gICAqICBcIlNoYXJwZW5pbmcgcHJldmlldy4uLlwiIG92ZXJsYXkg4oCUIG1pZC1kb3dubG9hZCBpZiB0aGUgbG9hZGluZyBmbGFnIGZsaXBzIGVhcmx5LiAqL1xuICBnZXQgaXNDYWNoaW5nKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNhY2hlUHJvZ3Jlc3MgIT09IG51bGwgJiYgdGhpcy5jYWNoZVByb2dyZXNzIDwgMTAwO1xuICB9XG4gIHByaXZhdGUgcnVubmluZyA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBNb25vdG9uaWMgcmVuZGVyIGdlbmVyYXRpb24uIEJ1bXBlZCB3aGVuIGEgcmVuZGVyIHN0YXJ0cywgc28gYSByZW5kZXIgdGhhdFxuICAgKiBoYXMgYmVlbiBzdXBlcnNlZGVkIGJ5IGEgbmV3ZXIgaW1hZ2UgY2FuIGJlIHJlY29nbmlzZWQgYW5kIG1hZGUgaW5lcnQ6IGl0c1xuICAgKiBjYWxsYmFja3MgcmV0dXJuIGVhcmx5IGluc3RlYWQgb2YgcGFpbnRpbmcsIGNsZWFyaW5nIGBydW5uaW5nYCwgcmVsZWFzaW5nXG4gICAqIHRoZSBuZXdlciByZW5kZXIncyBvdmVybGF5LCBvciBhcHBseWluZyBpdHMgUk9Jcy5cbiAgICovXG4gIHByaXZhdGUgcmVuZGVyVG9rZW4gPSAwO1xuICAvKiogU2V0IG9uY2UgdGhlIHRlbXBsYXRlLCBhbmQgd2l0aCBpdCB0aGUgcGxvdCBkaXYsIGV4aXN0cyAoc2VlIG5nQWZ0ZXJWaWV3SW5pdCkuICovXG4gIHByaXZhdGUgdmlld1JlYWR5ID0gZmFsc2U7XG4gIC8qKiBBbiBpbWFnZS1sZXNzIGRhdGFzZXQgd2hvc2UgZHJhdyB3YWl0cyBmb3IgdGhlIHZpZXc6IHNlZSBwbG90U3BhdGlhbFdpdGhvdXRJbWFnZS4gKi9cbiAgcHJpdmF0ZSBwZW5kaW5nU3BhdGlhbERyYXc6IFNwYXRpYWxEYXRhc2V0IHwgbnVsbCA9IG51bGw7XG4gIHB1YmxpYyB6SW5kZXggPSAwO1xuICBwdWJsaWMgbWF4SW5kZXggPSAwO1xuXG4gIEBWaWV3Q2hpbGQoJ2NtJykgY29udGV4dE1lbnUhOiBDb250ZXh0TWVudTtcbiAgY29udGV4dE1lbnVJdGVtczogTWVudUl0ZW1bXSA9IFtdO1xuXG4gIGFjdGl2ZURyYWdNb2RlOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuICAvKiogV2hldGhlciBhIHJlZ2lvbiBhY3Rpb24gaXMgYXZhaWxhYmxlIHRvIHVuZG8gKGppdC11aSM4NSkuIE1pcnJvcnMgdGhlXG4gICAqICBzaGFyZWQgUmVnaW9uU3RvcmUncyBoaXN0b3J5IGRlcHRoOyBkcml2ZXMgdGhlIHRvb2xiYXIgVW5kbyBidXR0b24uICovXG4gIGNhblVuZG9SZWdpb24gPSBmYWxzZTtcbiAgLyoqIFdoZXRoZXIgYW4gdW5kb25lIHJlZ2lvbiBhY3Rpb24gaXMgYXZhaWxhYmxlIHRvIHJlZG8gKGppdC11aSM4NSkuICovXG4gIGNhblJlZG9SZWdpb24gPSBmYWxzZTtcblxuICAvKiogQ3VzdG9tLXRocmVzaG9sZCBTaW1wbGlmeSBkaWFsb2cgKGppdC11aSM4NSkuICovXG4gIGRpc3BsYXlTaW1wbGlmeURpYWxvZyA9IGZhbHNlO1xuICBzaW1wbGlmeVRocmVzaG9sZCA9IDM7XG4gIC8qKiBDdXJyZW50IHJlZ2lvbiBzZWxlY3Rpb24gKGFycmF5IGluZGljZXMpIOKAlCBtaXJyb3JlZCBmcm9tIHRoZSBzdG9yZSBzbyB0aGVcbiAgICogIGNvbnRleHQtbWVudSByZWdpb24gYWN0aW9ucyBjYW4gcmVhZCBpdCBzeW5jaHJvbm91c2x5IChqaXQtdWkjODUpLiAqL1xuICBwcml2YXRlIHNlbGVjdGVkSW5kaWNlczogbnVtYmVyW10gPSBbXTtcblxuICAvKiogV2FuZCBzZW5zaXRpdml0eSDigJQgaGlnaGVyID0gc3RyaWN0ZXIgKHNtYWxsZXIgc2VsZWN0aW9uKS4gTWF0Y2hlcyBRdVBhdGggZGVmYXVsdC4gKi9cbiAgd2FuZFNlbnNpdGl2aXR5ID0gMi4wO1xuICAvKiogQnJ1c2ggZGlhbWV0ZXIgaW4gaW1hZ2UtcGl4ZWwgY29vcmRpbmF0ZXMgKGRyaXZlcyB0aGUgcGFpbnRlZCBkaXNjIHNpemUpLiAqL1xuICBicnVzaFNpemUgPSA0MDtcbiAgLyoqIENsYXNzIHRoZSBicnVzaCBwYWludHMgd2hpbGUgYSBwbG90IG1vZGUgYXJtZWQgaXQgKFBsb3RNb2RlVG9vbHMuYXJtQnJ1c2gpOyBudWxsID0gcGxhaW4gYnJ1c2guICovXG4gIHByaXZhdGUgYnJ1c2hDbGFzczogUGxvdE1vZGVCcnVzaENsYXNzIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIG9wdGlvbnMgKyBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzkwIFAxKS4gT25seSBtb2RlbHNcbiAgICogIHdpdGggYSBob3N0ZWQgT05OWCBwYWlyIChjb25maWd1cmVkIHZpYSBzZXRTYW1Nb2RlbFVybHMgYXQgYXBwIGluaXQpIGFyZVxuICAgKiAgb2ZmZXJlZCwgc28gdGhlIHBpY2tlciBjYW4ndCBzZWxlY3QgYSBtb2RlbCB0aGF0IGNhbid0IHJ1bi4gKi9cbiAgc2FtTW9kZWxzID0gU0FNX01PREVMUy5maWx0ZXIoaXNTYW1Nb2RlbFJlYWR5KS5tYXAoKG0pID0+ICh7IGlkOiBtLmlkLCBsYWJlbDogbS5sYWJlbCB9KSk7XG4gIHNhbU1vZGVsSWQgPSBnZXREZWZhdWx0U2FtTW9kZWxJZCgpO1xuXG4gIC8qKlxuICAgKiBOby1wcm9tcHQgdG9vbHMgcmVnaXN0ZXJlZCB0aHJvdWdoIHtAbGluayBUT09MQkFSX1RPT0xTfSwgZmlsdGVyZWQgdG8gdGhvc2VcbiAgICogd2l0aCBjb25maWd1cmVkIG1vZGVscyBhbmQgc29ydGVkLiBUaGlzIGxpYnJhcnkgcmVnaXN0ZXJzIG5vbmUg4oCUIGEgaG9zdFxuICAgKiBzdXBwbGllcyB0aGVtIOKAlCBzbyBhbiBvcGVuIGJ1aWxkIGhhcyBhbiBlbXB0eSBhcnJheSBoZXJlIGFuZCB0aGUgdG9vbGJhclxuICAgKiBhbmQgaGVscCBkaWFsb2cgc2ltcGx5IG9taXQgdGhhdCBncm91cC5cbiAgICovXG4gIGNvbnRyaWJ1dGVkVG9vbHM6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uW10gPSBbXTtcbiAgLyoqXG4gICAqIERpYWxvZyB0b29scyByZWdpc3RlcmVkIHRocm91Z2gge0BsaW5rIFRPT0xCQVJfVE9PTFN9IChga2luZDogJ2RpYWxvZydgKSxcbiAgICogc29ydGVkLiBUaGVpciBidXR0b25zIHNpdCB3aXRoIHRoZSBob3N0J3Mgb3duIGFjdGlvbnMgYW5kIHNob3cgaW4gdGhlIEltYWdlXG4gICAqIHZpZXcgb25seTsgZWFjaCBvcGVucyBhIGZsb2F0aW5nIGRpYWxvZyB0aGUgdG9vbCBmaWxscy5cbiAgICovXG4gIGRpYWxvZ1Rvb2xzOiBUb29sYmFyRGlhbG9nVG9vbENvbnRyaWJ1dGlvbltdID0gW107XG4gIC8qKiBUaGUgZGlhbG9nIHRvb2wgdGhlIHVzZXIgaGFzIG9wZW4gKGl0cyBzZXNzaW9uIG1heSBiZSBiZXR3ZWVuIHJlbmRlcnMpLiAqL1xuICBvcGVuRGlhbG9nVG9vbElkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgLyoqIFRoZSBvcGVuIGRpYWxvZyB0b29sJ3MgbGl2ZSBkaWFsb2csIGFzIHRoZSB0ZW1wbGF0ZSByZW5kZXJzIGl0LiAqL1xuICB0b29sRGlhbG9nOiB7IHRpdGxlOiBzdHJpbmc7IHdpZHRoOiBzdHJpbmc7IGhvc3Q6IEhUTUxFbGVtZW50IH0gfCBudWxsID0gbnVsbDtcbiAgLyoqIERpYWxvZy10b29sIHNlc3Npb25zOiB0aGUgc2FtZSBsaWZlY3ljbGUgYW5kIGlzb2xhdGlvbiBhcyBjb250cmlidXRlZCBwbG90IG1vZGVzLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHRvb2xEaWFsb2dzOiBQbG90TW9kZUNvbnRyb2xsZXI7XG4gIC8qKiBBY3RpdmUgY2hlY2twb2ludCBwZXIgY29udHJpYnV0ZWQgdG9vbC4gKi9cbiAgdG9vbE1vZGVsSWRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gIC8qKlxuICAgKiBQYXJhbWV0ZXIgdmFsdWVzIHBlciBjb250cmlidXRlZCB0b29sLCBzZWVkZWQgZnJvbSBlYWNoIHRvb2wncyBvd24gZGVmYXVsdHNcbiAgICog4oCUIHdoaWNoIGVuY29kZSB0aGUgc2NhbGUgYW5kIGNyb3dkaW5nIGl0cyBjaGVja3BvaW50IHdhcyB0cmFpbmVkIGZvci5cbiAgICovXG4gIHRvb2xQYXJhbXM6IFJlY29yZDxzdHJpbmcsIFJlY29yZDxzdHJpbmcsIHVua25vd24+PiA9IHt9O1xuICAvKiogV2hpY2ggdG9vbCdzIHBhcmFtZXRlciBkaWFsb2cgaXMgb3Blbiwgb3IgbnVsbC4gKi9cbiAgb3BlblBhcmFtc1Rvb2xJZDogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBTQU0gZG93bmxvYWQvc2VnbWVudCB0b2FzdCBzdGF0ZSAoYm91bmQgYnkgdGhlIGBzYW1gIHAtdG9hc3QgdGVtcGxhdGUpLiAqL1xuICBzYW1TdGF0dXMgPSAnJztcbiAgc2FtUHJvZ3Jlc3MgPSAwOyAvLyAwLi4xMDAsIGVuY29kZXIgZG93bmxvYWRcbiAgc2FtRG93bmxvYWRpbmcgPSBmYWxzZTtcbiAgc2FtQnVzeSA9IGZhbHNlOyAvLyBhbnkgU0FNIHdvcmsgaW4gZmxpZ2h0IChkcml2ZXMgdGhlIGluZGV0ZXJtaW5hdGUgc3Bpbm5lcilcbiAgLyoqIFdoZXRoZXIgdGhlIHNoYXJlZCBgc2FtYCB0b2FzdCBpcyBjdXJyZW50bHkgc2hvd24gKGF2b2lkcyBzdGFja2luZyBpdCBvblxuICAgKiAgZXZlcnkgcG9pbnQgY2xpY2ssIHdoaWNoIHJlLXJ1bnMgaW5mZXJlbmNlKS4gKi9cbiAgcHJpdmF0ZSBzYW1Ub2FzdFNob3duID0gZmFsc2U7XG5cbiAgcmVhZG9ubHkgc2FtVG9hc3RLZXkgPSBgc2FtLSR7VmlzdWFsaXplckNvbXBvbmVudC5uZXh0VG9hc3RJZCsrfWA7XG4gIC8qKlxuICAgKiBPdXRsZXQgZm9yIHRoZSBsaWJyYXJ5J3Mgb3duIHJlc3VsdC9lcnJvciBub3RpY2VzLlxuICAgKlxuICAgKiBQcmltZU5HIHJvdXRlcyBhIG1lc3NhZ2UgdG8gYSBgPHAtdG9hc3Q+YCBvbmx5IHdoZW4gdGhlIGtleXMgbWF0Y2gg4oCUIGFcbiAgICoga2V5bGVzcyBtZXNzYWdlIHJlYWNoZXMgYSBrZXlsZXNzIHRvYXN0IGFuZCBub3RoaW5nIGVsc2UuIFRoZXNlIG5vdGljZXMgdXNlZFxuICAgKiB0byBiZSBlbWl0dGVkIHdpdGggbm8ga2V5LCBzbyB0aGV5IHJlbmRlcmVkIG9ubHkgaW4gaG9zdHMgdGhhdCBoYXBwZW4gdG9cbiAgICogbW91bnQgYSBiYXJlIGA8cC10b2FzdD5gIChqaXQtdWkgZG9lcywgaW4gaXRzIGZpbGUtdHJlZSBjb21wb25lbnQpLiBFdmVyeVxuICAgKiBvdGhlciBob3N0IOKAlCBpbmNsdWRpbmcgdGhpcyByZXBvJ3Mgb3duIGJyb3dzZXIgZXhhbXBsZSDigJQgZHJvcHBlZCB0aGVtXG4gICAqIHNpbGVudGx5LCB3aGljaCBtYWRlIGEgZmFpbGVkIG9yIG5vLW9wIHNlZ21lbnRhdGlvbiBsb29rIGxpa2UgYSBkZWFkIGJ1dHRvbjpcbiAgICogdGhlIHN0aWNreSBwcm9ncmVzcyB0b2FzdCBpcyBjbGVhcmVkIGluIHRoZSBzYW1lIHRpY2sgYnkgaGlkZVNhbVRvYXN0LCBhbmRcbiAgICogdGhlIG1lc3NhZ2UgZXhwbGFpbmluZyB3aHkgbmV2ZXIgYXBwZWFyZWQgYW55d2hlcmUuXG4gICAqXG4gICAqIEtleWVkIHRvIHRoaXMgY29tcG9uZW50IGluc3RhbmNlIHNvIHRoZSBsaWJyYXJ5IHJlbmRlcnMgaXRzIG93biBvdXRsZXQgYW5kXG4gICAqIGlzIG5vdCBkZXBlbmRlbnQgb24gaG9zdCBtYXJrdXAuIERlbGliZXJhdGVseSBOT1QgdGhlIHN0aWNreSB0b2FzdCdzIGtleTpcbiAgICoge0BsaW5rIGhpZGVTYW1Ub2FzdH0gY2xlYXJzIHRoYXQga2V5IGluIHRoZSBgZmluYWxseWAsIHdoaWNoIHdvdWxkIHdpcGUgdGhlXG4gICAqIHJlc3VsdCB0aGUgbW9tZW50IGl0IHdhcyBwb3N0ZWQsIGFuZCB0aGUgc3RpY2t5IHRvYXN0J3MgY3VzdG9tIHRlbXBsYXRlIGlzXG4gICAqIGJ1aWx0IGZvciB0aGUgbGl2ZSBzdGF0dXMgKyBwcm9ncmVzcyBiYXIuXG4gICAqL1xuICByZWFkb25seSByZXN1bHRUb2FzdEtleSA9IGAke3RoaXMuc2FtVG9hc3RLZXl9LXJlc3VsdGA7XG4gIC8qKiBTdWJzY3JpcHRpb25zIHRvIHRoZSBwb2ludCB0b29sJ3MgbGl2ZSBmZWVkcyAoc3RhdHVzL2J1c3kvZG93bmxvYWQpLiAqL1xuICBwcml2YXRlIHNhbVBvaW50U3ViID0gbmV3IFN1YnNjcmlwdGlvbigpO1xuICAvKiogVmVydGV4IGVyYXNlciByYWRpdXMgaW4gaW1hZ2UtcGl4ZWwgY29vcmRpbmF0ZXMuICovXG4gIHZlcnRleEVyYXNlclJhZGl1cyA9IDIwO1xuXG4gIGNvbG9ybWFwc09wdGlvbnMhOiBhbnk7XG4gIHJldmVyc2VzY2FsZSA9IGZhbHNlO1xuICBzZWxlY3RlZENvbG9ybWFwITogYW55O1xuICAvKiogQ2hhbm5lbHMgJiBIaXN0b2dyYW0gZGlhbG9nIHZpc2liaWxpdHkgKG9wZW5lZCBmcm9tIHRoZSB0b29sYmFyKS4gKi9cbiAgc2hvd0NoYW5uZWxIaXN0b2dyYW0gPSBmYWxzZTtcbiAgLyoqIFNwYXRpYWwtb21pY3MgY29udHJvbHMgZGlhbG9nIHZpc2liaWxpdHkgKHRvb2xiYXIgYnV0dG9uKS4gKi9cbiAgc2hvd1NwYXRpYWxDb250cm9scyA9IGZhbHNlO1xuICAvKiogUmVnaW9uIEVkaXRvciBkaWFsb2cgdmlzaWJpbGl0eSAob3BlbmVkIGZyb20gdGhlIHRvb2xiYXIpLiAqL1xuICBzaG93UmVnaW9uRWRpdG9yID0gZmFsc2U7XG4gIC8qKiBSZWdpb24gRWRpdG9yIGRpYWxvZyB3aWR0aC4gT24gb3BlbiBpdCBpcyBzZXQgdG8gdGhlIGNvbmZpZ3VyZWQgaG9zdFxuICAgKiAgZWxlbWVudCdzIGN1cnJlbnQgd2lkdGgsIG9yIOKAlCB3aGVuIG5vIHNlbGVjdG9yIGlzIGNvbmZpZ3VyZWQgLyB0aGUgZWxlbWVudFxuICAgKiAgaXNuJ3QgZm91bmQg4oCUIHRvIGEgcXVhcnRlciBvZiB0aGUgcGFnZSAoc2VlIG9wZW5SZWdpb25FZGl0b3IpLiAqL1xuICByZWdpb25FZGl0b3JXaWR0aCA9ICcyNXZ3JztcbiAgc3RhY2tPcHRpb25zID0gW1xuICAgIHsgbmFtZTogJ1NpbmdsZSBpbWFnZScsIHZhbDogJ2ZhbHNlJyB9LFxuICAgIHsgbmFtZTogJ1N0YWNrJywgdmFsOiAndHJ1ZScgfSxcbiAgXTtcbiAgc2VsZWN0ZWRTdGFja09wdGlvbjogeyBuYW1lOiBzdHJpbmc7IHZhbDogc3RyaW5nIH0gfCB1bmRlZmluZWQgPSB0aGlzLnN0YWNrT3B0aW9uc1swXTtcbiAgcmVhZG9ubHkgcGxvdERpdk5hbWUgPSBgdml6LXBsb3QtJHtwbG90SW5zdGFuY2VTZXErK31gO1xuICBwbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuICBpc0hlYXRtYXAgPSB0cnVlO1xuICBhY3RpdmVTdXJmYWNlM2RNb2RlID0gJ3R1cm50YWJsZSc7XG4gIC8qKiBXaGV0aGVyIHRoZSBuYXBhcmkgM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gaXMgc2hvd24gKHZvbHVtZS9pc29zdXJmYWNlKS4gKi9cbiAgYXhlc1Zpc2libGUgPSB0cnVlO1xuICB3aXJlZnJhbWVBY3RpdmUgPSBmYWxzZTtcbiAgLyoqIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKDEgPSBGdWxsIOKApiA4ID0g4oWbOyBkZWZhdWx0IMK9KTsgY2hhbmdpbmcgaXQgcmUtcGxvdHMuICovXG4gIHJlc29sdXRpb25TY2FsZSA9IE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFO1xuXG4gIC8qKiBUaGUgc2VsZWN0b3IncyBlbnRyaWVzOiB0aGUgYnVpbHQtaW4gcGxvdCB0eXBlcyB0aGUgYWN0aXZlIGJhY2tlbmRcbiAgICogIGFkdmVydGlzZXMgKDNEIGdhdGVkIGJ5IGNhcGFiaWxpdHkpLCB0aGVuIGFueSBjb250cmlidXRlZCBtb2Rlc1xuICAgKiAgKHtAbGluayBQTE9UX1RZUEVfQ09OVFJJQlVUSU9OU30pLiAqL1xuICBwbG90VHlwZU1lbnU6IFBsb3RUeXBlT3B0aW9uW10gPSBbXTtcbiAgLyoqXG4gICAqIFRoZSBidWlsdC1pbiBwbG90IHR5cGVzIG9uIG9mZmVyIOKAlCB7QGxpbmsgcGxvdFR5cGVNZW51fSB3aXRob3V0IGNvbnRyaWJ1dGVkXG4gICAqIG1vZGVzLiBLZXB0IHdpdGggaXRzIG9yaWdpbmFsIHR5cGUgc28gZXhpc3RpbmcgcmVhZGVycyBhcmUgdW5hZmZlY3RlZC5cbiAgICovXG4gIHBsb3RUeXBlT3B0aW9uczogUGxvdFR5cGVEZXNjcmlwdG9yW10gPSBbXTtcbiAgLyoqXG4gICAqIFdoYXQgdGhlIHNlbGVjdG9yIHNob3dzOiBhIGJ1aWx0LWluIHR5cGUgb3IgYSBjb250cmlidXRlZCBtb2RlJ3MgaWQuIEV2ZXJ5XG4gICAqIHJlbmRlcmluZyBhbmQgdG9vbCBkZWNpc2lvbiBnb2VzIHRocm91Z2gge0BsaW5rIGJhc2VQbG90VHlwZX0gaW5zdGVhZCwgc28gYVxuICAgKiBjb250cmlidXRlZCBtb2RlIGJlaGF2ZXMgZXhhY3RseSBsaWtlIHRoZSBidWlsdC1pbiB0eXBlIGl0IHJpZGVzIG9uLlxuICAgKi9cbiAgc2VsZWN0ZWRQbG90VHlwZUlkOiBQbG90VHlwZUlkID0gUGxvdFR5cGUuSU1BR0U7XG5cbiAgLyoqXG4gICAqIFRoZSBidWlsdC1pbiBwbG90IHR5cGUgYmVpbmcgcmVuZGVyZWQuIFdpdGggbm8gY29udHJpYnV0ZWQgbW9kZSBhY3RpdmUgdGhpc1xuICAgKiBpcyB0aGUgc2VsZWN0aW9uIGl0c2VsZjsgZHVyaW5nIG9uZSBpdCBpcyB0aGUgbW9kZSdzIGBiYXNlVHlwZWAuIEtlcHQgd2l0aCBpdHNcbiAgICogb3JpZ2luYWwgYFBsb3RUeXBlYCB0eXBlIGZvciBleGlzdGluZyByZWFkZXJzIOKAlCB1c2Uge0BsaW5rIHNlbGVjdGVkUGxvdFR5cGVJZH1cbiAgICogZm9yIHRoZSBzZWxlY3RvcidzIGlkLiBBc3NpZ25pbmcgc2VsZWN0cyB0aGF0IGJ1aWx0LWluIHR5cGUuXG4gICAqL1xuICBnZXQgc2VsZWN0ZWRQbG90VHlwZSgpOiBQbG90VHlwZSB7XG4gICAgcmV0dXJuIHRoaXMuYmFzZVBsb3RUeXBlO1xuICB9XG4gIHNldCBzZWxlY3RlZFBsb3RUeXBlKHR5cGU6IFBsb3RUeXBlKSB7XG4gICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlSWQgPSB0eXBlO1xuICB9XG5cbiAgLyoqIENvbnRyaWJ1dGVkIHBsb3QgbW9kZXMgYW5kIHRoZWlyIHNpbmdsZSBsaXZlIHNlc3Npb24uICovXG4gIHJlYWRvbmx5IHBsb3RNb2RlczogUGxvdE1vZGVDb250cm9sbGVyO1xuICAvKipcbiAgICogVGhlIGxpdmUgY29udHJpYnV0ZWQgbW9kZSdzIHNpZGUgcGFuZWwsIGFzIHRoZSB0ZW1wbGF0ZSByZW5kZXJzIGl0OiBhblxuICAgKiBBbmd1bGFyIGNvbXBvbmVudCB3aXRoIGl0cyBvd24gaW5qZWN0b3IsIG9yIGEgcGxhaW4gaG9zdCBlbGVtZW50IGEgYG1vdW50YFxuICAgKiBwYW5lbCByZW5kZXJlZCBpbnRvLiBOdWxsIHdoaWxlIG5vIGNvbnRyaWJ1dGVkIHNlc3Npb24gaXMgbGl2ZS5cbiAgICovXG4gIHBsb3RNb2RlUGFuZWw6IHtcbiAgICB0aXRsZTogc3RyaW5nO1xuICAgIGNvbXBvbmVudDogVHlwZTx1bmtub3duPiB8IG51bGw7XG4gICAgaW5qZWN0b3I6IEluamVjdG9yIHwgbnVsbDtcbiAgICBob3N0OiBIVE1MRWxlbWVudCB8IG51bGw7XG4gIH0gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBkZXN0cm95aW5nID0gZmFsc2U7XG5cbiAgLyoqIFdoZXJlIGEgYG1vdW50YCBwYW5lbCdzIGhvc3QgZWxlbWVudCBpcyBhdHRhY2hlZCBvbmNlIHRoZSBkaWFsb2cgcmVuZGVycy4gKi9cbiAgQFZpZXdDaGlsZCgncGxvdE1vZGVQYW5lbFNsb3QnKVxuICBzZXQgcGxvdE1vZGVQYW5lbFNsb3QocmVmOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PiB8IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IGhvc3QgPSB0aGlzLnBsb3RNb2RlUGFuZWw/Lmhvc3Q7XG4gICAgaWYgKHJlZiAmJiBob3N0ICYmIGhvc3QucGFyZW50RWxlbWVudCAhPT0gcmVmLm5hdGl2ZUVsZW1lbnQpIHJlZi5uYXRpdmVFbGVtZW50LmFwcGVuZENoaWxkKGhvc3QpO1xuICB9XG5cbiAgLyoqIFRoZSBidWlsdC1pbiB0eXBlIGFjdHVhbGx5IHJlbmRlcmVkIGZvciB7QGxpbmsgc2VsZWN0ZWRQbG90VHlwZUlkfTogaXRzZWxmXG4gICAqICBmb3IgYSBidWlsdC1pbiB0eXBlLCBgYmFzZVR5cGVgIGZvciBhIGNvbnRyaWJ1dGVkIG1vZGUuICovXG4gIGdldCBiYXNlUGxvdFR5cGUoKTogUGxvdFR5cGUge1xuICAgIHJldHVybiB0aGlzLnBsb3RNb2Rlcy5iYXNlVHlwZU9mKHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkKTtcbiAgfVxuXG4gIC8qKiBGbG9hdGluZyBpbnRlbnNpdHktcHJvZmlsZSBpbnNldCAoTElORSBtb2RlKS4gKi9cbiAgcmVhZG9ubHkgaW50ZW5zaXR5SW5zZXREaXYgPSAnaW50ZW5zaXR5LWluc2V0LXBsb3QnO1xuICBwcm9maWxlUGFuZWxQb3MgPSB7IHg6IDIwLCB5OiA3MCB9O1xuICBwcml2YXRlIHByb2ZpbGVQYW5lbERyYWdnaW5nID0gZmFsc2U7XG4gIHByaXZhdGUgcHJvZmlsZVBhbmVsU3RhcnQgPSB7IG14OiAwLCBteTogMCwgeDogMCwgeTogMCB9O1xuXG4gIC8qKiBUb29sYmFyIGRvY2tpbmc6IGRvY2tlZCBhY3Jvc3MgdGhlIHRvcCBieSBkZWZhdWx0OyBkcmFnZ2luZyBpdHMgaGFuZGxlXG4gICAqICBkZXRhY2hlcyBpdCBpbnRvIGEgZmxvYXRpbmcsIG1vdmFibGUgd2luZG93IChmcmVlcyB0aGUgdG9wIHJvdyBmb3IgdGhlXG4gICAqICB2aXN1YWxpemF0aW9uKS4gKi9cbiAgdG9vbGJhckZsb2F0aW5nID0gZmFsc2U7XG4gIHRvb2xiYXJQb3MgPSB7IHg6IDgsIHk6IDggfTtcbiAgcHJpdmF0ZSB0b29sYmFyRHJhZ2dpbmcgPSBmYWxzZTtcbiAgcHJpdmF0ZSB0b29sYmFyU3RhcnQgPSB7IG14OiAwLCBteTogMCwgeDogMCwgeTogMCB9O1xuICBwcml2YXRlIGxhdGVzdFByb2ZpbGVzOiBJbnRlbnNpdHlQcm9maWxlW10gPSBbXTtcbiAgLyoqIERyaXZlcyB0aGUgaW50ZW5zaXR5IGluc2V0IHBhbmVsJ3MgdmlzaWJpbGl0eSDigJQgdHJ1ZSB3aGVuZXZlciBhbnlcbiAgICogIGludGVuc2l0eS1wcm9maWxlIGxpbmUgZXhpc3RzIChpbmRlcGVuZGVudCBvZiB0aGUgY3VycmVudCBwbG90IHR5cGUpLiAqL1xuICBoYXNQcm9maWxlcyA9IGZhbHNlO1xuICBwcml2YXRlIGludGVuc2l0eVByb2ZpbGVTdWI/OiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgdmlld3BvcnRDaGFuZ2VTdWI/OiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgcHJvZmlsZURyYWdNb3ZlTGlzdGVuZXI/OiAoZTogTW91c2VFdmVudCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSBwcm9maWxlRHJhZ1VwTGlzdGVuZXI/OiAoKSA9PiB2b2lkO1xuICBwcml2YXRlIHByb2ZpbGVSZXNpemVMaXN0ZW5lcj86ICgpID0+IHZvaWQ7XG5cbiAgLyoqIFRydWUgd2hpbGUgdGhlIDNEIHNwYXRpYWwgY2xvdWQgaXMgdGhlIGFjdGl2ZSBtb2RlLiBUaGUgY29udHJvbHMgZHJvcCB0aGUgUk9JXG4gICAqICBzZWxlY3Rpb24gdGhlcmU6IHJlZ2lvbiB0b29scyBhcmUgc2NyZWVuLXNwYWNlLCBhbmQgYWdhaW5zdCBhbiBvcmJpdGluZ1xuICAgKiAgY2FtZXJhIGEgZHJhd24gcmVjdGFuZ2xlIGhhcyBubyBmaXhlZCBtZWFuaW5nIGluIHRoZSBkYXRhLiAqL1xuICBnZXQgaXNTcGF0aWFsM2RNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc1NwYXRpYWxPbWljczNkKHRoaXMuYmFzZVBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBMSU5FIHBsb3QgdHlwZSBzaG93cyB0aGUgaW1hZ2UgKyBkcmFnZ2FibGUgbGluZSBST0kgKyBpbnRlbnNpdHkgaW5zZXQuICovXG4gIGdldCBpc1Byb2ZpbGVNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmJhc2VQbG90VHlwZSA9PT0gUGxvdFR5cGUuTElORTtcbiAgfVxuXG4gIC8qKiBUcnVlIGZvciB0aGUgSW1hZ2UgcGxvdCB0eXBlLCB3aGljaCByZW5kZXJzIGFzIGEgbmF0aXZlbHkgcGFuL3pvb20tYWJsZVxuICAgKiAgcmFzdGVyIOKAlCBzbyB0aGUgYmFja2VuZC1hZ25vc3RpYyB6b29tL3BhbiB0b29sYmFyIHRvb2xzIGFyZSBoaWRkZW4uIFRoZVxuICAgKiAgY29tcG9uZW50IGRyaXZlcyB0aGlzIG9mZiB0aGUgcGxvdCB0eXBlLCBub3QgdGhlIGFjdGl2ZSBiYWNrZW5kLiAqL1xuICBnZXQgaXNJbWFnZVZpZXcoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuYmFzZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRTtcbiAgfVxuXG4gIC8qKiBJc29zdXJmYWNlIGJhbmQgYXMgYSAw4oCTMjU1IHNsaWRlciBwb3NpdGlvbiwgbWFwcGVkIG9udG8gdGhlIHZvbHVtZSdzIHJlYWxcbiAgICogIGludGVuc2l0eSByYW5nZSBieSB0aGUgcmVuZGVyZXIuIERlZmF1bHRzIHRvIHRoZSBmdWxsIHJhbmdlLiAqL1xuICBpc29SYW5nZTogbnVtYmVyW10gPSBbMCwgMjU1XTtcblxuICAvKiogV2hldGhlciBhIHNwYXRpYWwtb21pY3MgZGF0YXNldCBpcyBjdXJyZW50bHkgcHVibGlzaGVkIG9uXG4gICAqICBgU1BBVElBTF9EQVRBX1BPUlRgIOKAlCBnYXRlcyB0aGUgc3BhdGlhbCBwbG90IHR5cGVzIGluIHRoZSBzZWxlY3Rvci4gKi9cbiAgLyoqIEJvdW5kIGJ5IHRoZSB0b29sYmFyLCB3aGljaCBvZmZlcnMgdGhlIHBsb3QgbW9kZXMgZm9yIGEgZGF0YXNldCB3aXRoIG5vIGltYWdlIHRvby4gKi9cbiAgaGFzU3BhdGlhbERhdGFzZXQgPSBmYWxzZTtcbiAgLyoqIFRoZSBzcGF0aWFsIGRhdGFzZXQgb24gb2ZmZXIsIGZvciBkcmF3aW5nIG9uZSB0aGF0IGJyaW5ncyBubyBpbWFnZSAoc2VlIHJlbG9hZEFuZFBsb3QpLiAqL1xuICBwcml2YXRlIHNwYXRpYWxEYXRhc2V0OiBTcGF0aWFsRGF0YXNldCB8IG51bGwgPSBudWxsO1xuICAvKiogV2hldGhlciB0aGF0IGRhdGFzZXQncyBvYnNlcnZhdGlvbnMgY2FycnkgYSB6LCBnYXRpbmcgdGhlIDNEIHNwYXRpYWwgbW9kZS4gKi9cbiAgcHJpdmF0ZSBoYXNTcGF0aWFsM2REYXRhc2V0ID0gZmFsc2U7XG4gIC8qKiBXaGV0aGVyIGl0IGNhcnJpZXMgYSByZWdpc3RlcmVkIHZvbHVtZS4gQ2hhbmdlIGRldGVjdGlvbiBvbmx5OiBhIHZvbHVtZVxuICAgKiAgYXBwZWFyaW5nIGlzIHdoYXQgcHVibGlzaGVzIGl0IGFzIHRoZSBpbWFnZSwgYW5kIHRoZSBwbG90LXR5cGUgZ2F0ZXMgdGhlblxuICAgKiAgc2VlIGFuIG9yZGluYXJ5IGdyYXlzY2FsZSBzdGFjay4gKi9cbiAgcHJpdmF0ZSBoYXNTcGF0aWFsVm9sdW1lID0gZmFsc2U7XG4gIC8qKiBEYXRhc2V0IGlkZW50aXR5ICsgY2FwYWJpbGl0eSBzaGFwZSB0aGUgbGFzdCBlbWlzc2lvbiB3YXMgaGFuZGxlZCBhdCwgc28gYVxuICAgKiAgc3dpdGNoIGJldHdlZW4gdHdvIGRhdGFzZXRzIG9mIHRoZSBzYW1lIHNoYXBlIGlzIG5vdCBtaXN0YWtlbiBmb3IgYSByZXBlYXQuICovXG4gIHByaXZhdGUgc3BhdGlhbERhdGFzZXRLZXk6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAvKiogRGF0YXNldCArIGdlb21ldHJ5IHRoZSBjdXJyZW50bHkgcHVibGlzaGVkIHZvbHVtZSBpbWFnZSB3YXMgYnVpbHQgZnJvbSwgc28gYVxuICAgKiAgcmUtZW1pdHRlZCBkYXRhc2V0IGRvZXNuJ3QgcmUtZmV0Y2ggbWVnYWJ5dGVzIG9yIHJlc2V0IHRoZSB1c2VyJ3Mgc2NydWIuICovXG4gIHByaXZhdGUgdm9sdW1lSW1hZ2VLZXk6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAvKiogQmxvYiBVUkxzIGJhY2tpbmcgdGhhdCBpbWFnZSDigJQgb3VycyB0byByZXZva2UuICovXG4gIHByaXZhdGUgdm9sdW1lSW1hZ2VVcmxzOiBzdHJpbmdbXSA9IFtdO1xuICBwcml2YXRlIHNwYXRpYWxEYXRhc2V0U3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuXG4gIHBsb3RXaWR0aFN1YnNjcmlwdGlvbj86IFN1YnNjcmlwdGlvbjtcbiAgaW1hZ2VMb2FkaW5nU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuICBpbWdMb2FkaW5nTWVzc2FnZVN1YnNjcmlwdGlvbj86IFN1YnNjcmlwdGlvbjtcbiAgaXNab29tU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuICBmaWxlbmFtZVN1YnNjcmlwdGlvbj86IFN1YnNjcmlwdGlvbjtcbiAgYXV0b3NjYWxlU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuXG4gIHByaXZhdGUgcGxvdENvbnRleHRNZW51TGlzdGVuZXI/OiAoZTogTW91c2VFdmVudCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSBrZXlkb3duTGlzdGVuZXI/OiAoZTogS2V5Ym9hcmRFdmVudCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSB3aGVlbExpc3RlbmVyPzogKGU6IFdoZWVsRXZlbnQpID0+IHZvaWQ7XG5cbiAgLyoqIERlYm91bmNlZCB6LXNsaWNlIHNjcnViYmluZyAoc2VlIFNsaWNlU2NydWJiZXIg4oCUIHJlZmFjdG9yaW5nIHBsYW4gU3RlcCA3KS4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBzY3J1YmJlciA9IG5ldyBTbGljZVNjcnViYmVyKCh6KSA9PiB7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRaSW5kZXgoeik7XG4gICAgLy8gSW4gYSBwZXItc2xpY2Ugei1zdGFjayBzZXNzaW9uIHRoZSBzdG9yZSBzd2FwcyB0aGUgbGl2ZSByZWdpb24gc2V0IHRvIHRoaXNcbiAgICAvLyBzbGljZSAocHJlc2VydmluZyBlZGl0cyBtYWRlIG9uIHRoZSBwcmV2aW91cyBvbmUpOyBvdXRzaWRlIHN0YWNrIG1vZGUgdGhpc1xuICAgIC8vIGp1c3QgcmVjb3JkcyB0aGUgc2xpY2UsIGxlYXZpbmcgc2luZ2xlLXBsYW5lIHJlZ2lvbnMgdW50b3VjaGVkIChqaXQtdWkjOTMpLlxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0RGlzcGxheVNsaWNlKHopO1xuICB9KTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBASW5qZWN0KElNQUdFX1NUQVRFX1BPUlQpIHByaXZhdGUgc3RhdGU6IEltYWdlU3RhdGVQb3J0LFxuICAgIEBJbmplY3QoVklTVUFMSVpFUikgcHVibGljIHBsb3RTZXJ2aWNlOiBJVmlzdWFsaXplcixcbiAgICBwdWJsaWMgbWVzc2FnZVNlcnZpY2U6IE1lc3NhZ2VTZXJ2aWNlLFxuICAgIHByaXZhdGUgbmdab25lOiBOZ1pvbmUsXG4gICAgcHJpdmF0ZSBjZHI6IENoYW5nZURldGVjdG9yUmVmLFxuICAgIHByaXZhdGUgc2Vzc2lvbjogVmlzdWFsaXplclN0b3JlLFxuICAgIHByaXZhdGUgc2FtVG9vbDogU2FtVG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBjZWxsU2VnbWVudFRvb2w6IENlbGxTZWdtZW50VG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBzYW1Qb2ludFRvb2w6IFNhbVBvaW50VG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSByZWdpb25PcHM6IFJlZ2lvbk9wc1NlcnZpY2UsXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChWSVpfQ09ORklHKSBwcml2YXRlIHZpekNvbmZpZz86IFZpekNvbmZpZyxcbiAgICAvLyBBIGNvbnN0cnVjdG9yIHBhcmFtZXRlciByYXRoZXIgdGhhbiBhIGZpZWxkIGBpbmplY3QoKWA6IHRoaXMgY29tcG9uZW50IGlzXG4gICAgLy8gYWxzbyBjb25zdHJ1Y3RlZCBkaXJlY3RseSB3aXRoIGBuZXdgIGluIGl0cyBvd24gc3BlY3MsIHdoaWNoIGlzIG91dHNpZGVcbiAgICAvLyBhbiBpbmplY3Rpb24gY29udGV4dCBhbmQgd291bGQgdGhyb3cgTkcwMjAzIG9uIGEgZmllbGQgaW5pdGlhbGl6ZXIuXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChUT09MQkFSX1RPT0xTKSB0b29sQ29udHJpYnV0aW9ucz86IHJlYWRvbmx5IFRvb2xiYXJDb250cmlidXRpb25bXSxcbiAgICAvLyBPcHRpb25hbCBsaWtlIHRoZSByZXN0OiBhIGhvc3QgdGhhdCBzaG93cyBvbmx5IGltYWdlcyBuZXZlciBwcm92aWRlcyBpdCxcbiAgICAvLyBhbmQgdGhlIHNwYXRpYWwgcGxvdCB0eXBlcyBzaW1wbHkgc3RheSBoaWRkZW4uXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChTUEFUSUFMX0RBVEFfUE9SVCkgcHJpdmF0ZSBzcGF0aWFsRGF0YT86IFNwYXRpYWxEYXRhUG9ydCxcbiAgICAvLyBDb250cmlidXRlZCBwbG90IG1vZGVzLiBMaWtlIFRPT0xCQVJfVE9PTFMgdGhlIHRva2VuIGhhcyBubyBmYWN0b3J5LCBzbyBhXG4gICAgLy8gaG9zdCB0aGF0IHByb3ZpZGVzIG5vbmUgZ2V0cyBudWxsIGhlcmUgYW5kIHRoZSBzZWxlY3RvciBpcyB1bmNoYW5nZWQuXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChQTE9UX1RZUEVfQ09OVFJJQlVUSU9OUykgcGxvdFR5cGVDb250cmlidXRpb25zPzogcmVhZG9ubHkgUGxvdFR5cGVDb250cmlidXRpb25bXSxcbiAgICAvLyBQYXJlbnQgb2YgYSBjb250cmlidXRlZCBwYW5lbCBjb21wb25lbnQncyBpbmplY3Rvci4gT3B0aW9uYWwgb25seSBzbyB0aGVcbiAgICAvLyBzcGVjcyBjYW4gY29uc3RydWN0IHRoaXMgY29tcG9uZW50IHdpdGggYG5ld2AuXG4gICAgQE9wdGlvbmFsKCkgcHJpdmF0ZSBpbmplY3Rvcj86IEluamVjdG9yLFxuICApIHtcbiAgICB0aGlzLnBsb3RNb2RlcyA9IG5ldyBQbG90TW9kZUNvbnRyb2xsZXIocGxvdFR5cGVDb250cmlidXRpb25zLCB7XG4gICAgICBvbkFjdGl2YXRlZDogKGFjdGl2ZSkgPT4gdGhpcy5zaG93UGxvdE1vZGVQYW5lbChhY3RpdmUpLFxuICAgICAgb25EZWFjdGl2YXRpbmc6ICgpID0+IHRoaXMuaGlkZVBsb3RNb2RlUGFuZWwoKSxcbiAgICAgIG9uRmFpbGVkOiAoY29udHJpYnV0aW9uKSA9PiB0aGlzLmZhbGxCYWNrRnJvbVBsb3RNb2RlKGNvbnRyaWJ1dGlvbiksXG4gICAgfSk7XG4gICAgdGhpcy5jb250cmlidXRlZFRvb2xzID0gdmlzaWJsZVRvb2xDb250cmlidXRpb25zKHRvb2xDb250cmlidXRpb25zKTtcbiAgICB0aGlzLmRpYWxvZ1Rvb2xzID0gZGlhbG9nVG9vbENvbnRyaWJ1dGlvbnModG9vbENvbnRyaWJ1dGlvbnMpO1xuICAgIC8vIEEgZGlhbG9nIHRvb2wncyBzZXNzaW9uIGlzIHJ1biBieSB0aGUgcGxvdC1tb2RlIGxpZmVjeWNsZSwgYWRhcHRlZDogaXRzIFwiYmFzZVxuICAgIC8vIHR5cGVcIiBpcyB0aGUgSW1hZ2UgdmlldyBpdCBkcmF3cyBvdmVyLiBJdHMgYm9keSBpcyBOT1QgYSBjb250cm9sbGVyIHBhbmVsOiB0aGVcbiAgICAvLyBjb250cm9sbGVyIHdvdWxkIG1vdW50IGludG8gYSBkZXRhY2hlZCBob3N0LCBhbmQgYSBib2R5IG11c3QgYmUgYWJsZSB0byBtZWFzdXJlXG4gICAgLy8gaXRzZWxmLiBUaGUgZGlhbG9nIGlzIHJlbmRlcmVkIGZpcnN0IGFuZCB0aGUgYm9keSBtb3VudGVkIG9uY2UgaXRzIGhvc3QgaXMgaW4gdGhlXG4gICAgLy8gZG9jdW1lbnQgKHNob3dUb29sRGlhbG9nKS5cbiAgICB0aGlzLnRvb2xEaWFsb2dzID0gbmV3IFBsb3RNb2RlQ29udHJvbGxlcihcbiAgICAgIHRoaXMuZGlhbG9nVG9vbHMubWFwKCh0KTogUGxvdFR5cGVDb250cmlidXRpb24gPT4gKHtcbiAgICAgICAgZGVzY3JpcHRvcjogeyB0eXBlOiB0LmlkLCBsYWJlbDogdC5sYWJlbCwgZGltZW5zaW9uczogJzJkJywgYmFzZVR5cGU6IFBsb3RUeXBlLklNQUdFIH0sXG4gICAgICAgIGFjdGl2YXRlOiAoY3R4KSA9PiB0LmFjdGl2YXRlKGN0eCBhcyBUb29sRGlhbG9nQ29udGV4dCksXG4gICAgICB9KSksXG4gICAgICB7XG4gICAgICAgIG9uQWN0aXZhdGVkOiAoYWN0aXZlKSA9PiB0aGlzLnNob3dUb29sRGlhbG9nKGFjdGl2ZSksXG4gICAgICAgIG9uRGVhY3RpdmF0aW5nOiAoKSA9PiB0aGlzLmhpZGVUb29sRGlhbG9nKCksXG4gICAgICAgIG9uRmFpbGVkOiAoY29udHJpYnV0aW9uKSA9PiB0aGlzLmRpYWxvZ1Rvb2xGYWlsZWQoY29udHJpYnV0aW9uLmRlc2NyaXB0b3IudHlwZSksXG4gICAgICB9LFxuICAgICk7XG4gICAgdGhpcy5jb2xvcm1hcHNPcHRpb25zID0gcGxvdFNlcnZpY2UuZ2V0Q29sb3JtYXBPcHRpb25zKCk7XG4gICAgdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCk7XG4gIH1cblxuICAvKipcbiAgICogUGxvdCB0eXBlcyBvZmZlcmVkIGluIHRoZSBzZWxlY3RvciBmb3IgdGhlIGN1cnJlbnQgaW1hZ2U6XG4gICAqICAtIE91dHNpZGUgKip0ZXN0IG1vZGUqKiwgb25seSB0aGUgY3VyYXRlZCBkZWZhdWx0IHNldCAoZGVzY3JpcHRvcnMgd2l0aCBhXG4gICAqICAgIGBwcm9kdWN0aW9uTGFiZWxgKSBpcyBvZmZlcmVkLCBzaG93biB1bmRlciBzdWZmaXgtZnJlZSBuYW1lcyDigJQgSW1hZ2UsXG4gICAqICAgIEhlYXRtYXAsIENvbnRvdXIgYW5kIHRoZSBuYXBhcmkgU3VyZmFjZSAvIFZvbHVtZSAvIElzb3N1cmZhY2UuIFRlc3QgbW9kZVxuICAgKiAgICBleHBvc2VzIGV2ZXJ5IGJhY2tlbmQncyB0eXBlIHVuZGVyIGl0cyBmdWxsIChiYWNrZW5kLXN1ZmZpeGVkKSBsYWJlbC5cbiAgICogIC0gM0QgdHlwZXMgaGlkZGVuIHdoZW4gdGhlIGJhY2tlbmQgY2FuJ3QgcmVuZGVyIGEgM0Qgc2NlbmUuXG4gICAqICAtIHN0YWNrLW9ubHkgdHlwZXMgKHZvbHVtZSwgaXNvc3VyZmFjZSkgaGlkZGVuIHVubGVzcyB0aGUgZmlsZSBpcyBhIHN0YWNrIOKAlFxuICAgKiAgICBhIHZvbHVtZSBuZWVkcyBtdWx0aXBsZSB6LXNsaWNlcy5cbiAgICogIC0gc2NhbGFyLWludGVuc2l0eSB0eXBlcyAoY29udG91ciwgc3VyZmFjZSwgaXNvc3VyZmFjZSkgaGlkZGVuIGZvciBSR0JcbiAgICogICAgaW1hZ2VzIOKAlCB0aGV5IG1hcCBhIHNpbmdsZSBpbnRlbnNpdHkgcGVyIHBpeGVsLiBJbWFnZSBhbmQgSGVhdG1hcCByZW5kZXJcbiAgICogICAgYW55IGltYWdlLlxuICAgKiAgLSBzcGF0aWFsLW9taWNzIHR5cGVzIGhpZGRlbiB1bnRpbCBhIGBTcGF0aWFsRGF0YXNldGAgaXMgcHVibGlzaGVkIG9uXG4gICAqICAgIGBTUEFUSUFMX0RBVEFfUE9SVGAg4oCUIHRoZSBtb2RlIGhhcyBub3RoaW5nIHRvIGRyYXcgd2l0aG91dCBvYnNlcnZhdGlvbnMsXG4gICAqICAgIGV4YWN0bHkgYXMgYSB2b2x1bWUgaGFzIG5vdGhpbmcgdG8gZHJhdyB3aXRob3V0IGEgc3RhY2suXG4gICAqL1xuICAvKipcbiAgICogVHJhY2sgd2hldGhlciBhIHNwYXRpYWwtb21pY3MgZGF0YXNldCBpcyBhdmFpbGFibGUuIEEgZGF0YXNldCBhcHBlYXJpbmcgKG9yXG4gICAqIGJlaW5nIGNsZWFyZWQpIGNoYW5nZXMgd2hpY2ggcGxvdCB0eXBlcyBtYWtlIHNlbnNlLCBzbyBpdCBkcml2ZXMgdGhlIHNhbWVcbiAgICogcmVjb21wdXRlICsgcmVjb25jaWxlIHRoYXQgdGhlIGltYWdlIHN0cmVhbSBhbmQgdGhlIHRlc3QtbW9kZSB0b2dnbGUgZG8uXG4gICAqXG4gICAqIE5vLW9wIHdoZW4gdGhlIGhvc3QgcHJvdmlkZXMgbm8gYFNQQVRJQUxfREFUQV9QT1JUYCDigJQgdGhlIHNwYXRpYWwgdHlwZXMgdGhlblxuICAgKiBzdGF5IGhpZGRlbiBmb3IgdGhlIGxpZmUgb2YgdGhlIGNvbXBvbmVudC5cbiAgICovXG4gIHByaXZhdGUgd2F0Y2hTcGF0aWFsRGF0YXNldCgpOiB2b2lkIHtcbiAgICB0aGlzLnNwYXRpYWxEYXRhc2V0U3Vic2NyaXB0aW9uID0gdGhpcy5zcGF0aWFsRGF0YT8uZ2V0RGF0YXNldCQoKS5zdWJzY3JpYmUoKGRhdGFzZXQpID0+IHtcbiAgICAgIHRoaXMuc3BhdGlhbERhdGFzZXQgPSBkYXRhc2V0ID8/IG51bGw7XG4gICAgICBjb25zdCBoYXMgPSAhIWRhdGFzZXQ7XG4gICAgICAvLyBPbmx5IGEgZGF0YXNldCB3aG9zZSBvYnNlcnZhdGlvbnMgY2FycnkgYSB6IGNhbiBiZSBkcmF3biBhcyBhIGNsb3VkLCBzb1xuICAgICAgLy8gdGhlIDNEIG1vZGUgaXMgZ2F0ZWQgb24gdGhlIGNvb3JkaW5hdGVzLCBub3QgbWVyZWx5IG9uIGEgZGF0YXNldCBiZWluZ1xuICAgICAgLy8gcHJlc2VudC4gTW9zdCBzcGF0aWFsIGFzc2F5cyBhcmUgb25lIHBsYW5lLlxuICAgICAgY29uc3QgaGFzM2QgPSAhIWRhdGFzZXQ/Lm9ic2VydmF0aW9ucy56O1xuICAgICAgY29uc3QgaGFzVm9sdW1lID0gISFkYXRhc2V0Py52b2x1bWU7XG4gICAgICAvLyBUaGUgcG9ydCBwdWJsaXNoZXMgaXRzIGN1cnJlbnQgdmFsdWUgb24gc3Vic2NyaWJlLCBzbyB0aGUgaW5pdGlhbCBgbnVsbGBcbiAgICAgIC8vIHdvdWxkIG90aGVyd2lzZSByZWNvbXB1dGUgdGhlIHNlbGVjdG9yIGZvciBubyBjaGFuZ2UuIEtleWVkIGJ5IGRhdGFzZXRcbiAgICAgIC8vIElERU5USVRZIGFzIHdlbGwgYXMgY2FwYWJpbGl0eSBzaGFwZTogdHdvIDNEIGRhdGFzZXRzIHRoYXQgYm90aCBjYXJyeSBhXG4gICAgICAvLyB2b2x1bWUgaGF2ZSB0aGUgc2FtZSBzaGFwZSwgYW5kIGNvbXBhcmluZyBvbmx5IHRoYXQgc2tpcHBlZCB0aGUgc3dpdGNoIOKAlFxuICAgICAgLy8gbGVhdmluZyB0aGUgcHJldmlvdXMgZGF0YXNldCdzIHZvbHVtZSBpbWFnZSBvbiBzY3JlZW4gdW5kZXJuZWF0aCB0aGUgbmV3XG4gICAgICAvLyBvbmUncyBvYnNlcnZhdGlvbnMuXG4gICAgICBjb25zdCBrZXkgPSBkYXRhc2V0XG4gICAgICAgID8gYCR7ZGF0YXNldC5pZH18JHtoYXMzZH18JHtoYXNWb2x1bWV9fCR7ZGF0YXNldC52b2x1bWVcbiAgICAgICAgICA/IGAke2RhdGFzZXQudm9sdW1lLndpZHRofXgke2RhdGFzZXQudm9sdW1lLmhlaWdodH14JHtkYXRhc2V0LnZvbHVtZS5kZXB0aH1gIDogJyd9YFxuICAgICAgICA6IG51bGw7XG4gICAgICBpZiAoa2V5ID09PSB0aGlzLnNwYXRpYWxEYXRhc2V0S2V5KSByZXR1cm47XG4gICAgICB0aGlzLnNwYXRpYWxEYXRhc2V0S2V5ID0ga2V5O1xuICAgICAgdGhpcy5oYXNTcGF0aWFsRGF0YXNldCA9IGhhcztcbiAgICAgIHRoaXMuaGFzU3BhdGlhbDNkRGF0YXNldCA9IGhhczNkO1xuICAgICAgLy8gV2hldGhlciB0aGUgZGF0YXNldCBzYXlzIGl0IHJlZ2lzdGVycyBvbnRvIGEgdGlzc3VlIGltYWdlLiBEaXN0aW5jdCBmcm9tIGFuXG4gICAgICAvLyBpbWFnZSBiZWluZyBMT0FERUQ6IGEgcmVnaXN0ZXJlZCBkYXRhc2V0J3MgaW1hZ2UgbWF5IHN0aWxsIGJlIG9uIGl0cyB3YXksIGFuZFxuICAgICAgLy8gdGhlIHBpeGVsIG1vZGVzIG11c3Qgbm90IGZsaWNrZXIgb3V0IG9mIHRoZSBzZWxlY3RvciB3aGlsZSBpdCBhcnJpdmVzLlxuICAgICAgLy8gQSByZWdpc3RlcmVkIGRhdGFzZXQgZHJhd3Mgb3ZlciBhIHRpc3N1ZSBpbWFnZTsgYSB2b2x1bWUtYmFja2VkIG9uZSBwdWJsaXNoZXMgaXRzXG4gICAgICAvLyB2b2x1bWUgQVMgYSBncmF5c2NhbGUgei1zdGFjayBpbWFnZSAoYGJ1aWxkVm9sdW1lU3RhY2tJbWFnZWApLiBFaXRoZXIgd2F5IHBpeGVsc1xuICAgICAgLy8gZXhpc3QsIHNvIHRoZSBwaXhlbCBtb2RlcyBzdGF5IG9uIG9mZmVyIOKAlCBWb2x1bWUgYW5kIElzb3N1cmZhY2UgYXJlIGV4YWN0bHkgaG93XG4gICAgICAvLyBhIDNEIG9taWNzIGRhdGFzZXQgaXMgcmVhZC5cbiAgICAgIHRoaXMuc3BhdGlhbERhdGFzZXRIYXNQaXhlbHMgPSAhIWRhdGFzZXQ/LmltYWdlUmVmIHx8IGhhc1ZvbHVtZTtcbiAgICAgIHRoaXMuaGFzU3BhdGlhbFZvbHVtZSA9IGhhc1ZvbHVtZTtcbiAgICAgIHRoaXMuY29tcHV0ZVBsb3RUeXBlT3B0aW9ucygpO1xuICAgICAgLy8gQSBkYXRhc2V0IHdpdGggbm8gcmVmZXJlbmNlIGltYWdlIGhhcyBub3RoaW5nIHRvIGRyYXcgb2JzZXJ2YXRpb25zIE9WRVI6XG4gICAgICAvLyBhIGNsb3VkIHJlZ2lzdGVyZWQgaW50byBhIGNvbW1vbiBmcmFtZSAodGhlIEFsbGVuIENDRikgaGFzIGNvb3JkaW5hdGVzXG4gICAgICAvLyBidXQgbm8gb25lIHNlY3Rpb24uIFdoYXQgaXQgZG9lcyBoYXZlIGlzIGl0cyByZWdpc3RlcmVkIFZPTFVNRSwgd2hpY2ggaXNcbiAgICAgIC8vIGEgM0QgaW1hZ2UgaW4gb25lIGZpbGUg4oCUIHNvIG1ha2UgdGhhdCB0aGUgaW1hZ2UgYW5kIG9wZW4gb24gaXQsIHNsaWNlIGJhclxuICAgICAgLy8gYW5kIGFsbCwgcmF0aGVyIHRoYW4gbGVhdmluZyB0aGUgaG9zdCBvbiBhbiBJbWFnZSB2aWV3IHNob3dpbmcgd2hhdGV2ZXJcbiAgICAgIC8vIHNsaWRlIHdhcyBsb2FkZWQgYmVmb3JlLiBPcmRlcmVkIGFmdGVyIGNvbXB1dGVQbG90VHlwZU9wdGlvbnMgc28gdGhlIHR5cGVcbiAgICAgIC8vIGlzIG9uIG9mZmVyIGJlZm9yZSBpdCBpcyBzZWxlY3RlZC5cbiAgICAgIGlmIChkYXRhc2V0ICYmICFkYXRhc2V0LmltYWdlUmVmICYmIGhhc1ZvbHVtZSkge1xuICAgICAgICB2b2lkIHRoaXMuc2hvd1ZvbHVtZUFzSW1hZ2UoZGF0YXNldCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBBbnl0aGluZyBlbHNlIOKAlCBhIGRhdGFzZXQgd2l0aCBpdHMgb3duIHNlY3Rpb24gaW1hZ2UsIGEgdm9sdW1lLWxlc3NcbiAgICAgICAgLy8gY2xvdWQsIG9yIG5vbmUgYXQgYWxsIOKAlCBtZWFucyBhIHB1Ymxpc2hlZCB2b2x1bWUgaW1hZ2UgaXMgbm8gbG9uZ2VyIHdoYXRcbiAgICAgICAgLy8gaXMgb24gc2NyZWVuLiBGb3JnZXQgaXQsIG9yIGNvbWluZyBCQUNLIHRvIHRoZSB2b2x1bWUgZGF0YXNldCB3b3VsZFxuICAgICAgICAvLyBzaG9ydC1jaXJjdWl0IG9uIGEgbWF0Y2hpbmcga2V5IGFuZCBsZWF2ZSB0aGUgb3RoZXIgZGF0YXNldCdzIHNsaWRlIHVwLlxuICAgICAgICB0aGlzLmRyb3BWb2x1bWVJbWFnZSgpO1xuICAgICAgICBpZiAoZGF0YXNldCAmJiAhZGF0YXNldC5pbWFnZVJlZikge1xuICAgICAgICAgIC8vIE5vIHJlZmVyZW5jZSBpbWFnZSBhbmQgbm8gdm9sdW1lIHRvIHNsaWNlOiB0aGUgb2JzZXJ2YXRpb25zIGFyZSB0aGUgb25seVxuICAgICAgICAgIC8vIHRoaW5nIHRoZXJlIGlzIHRvIGRyYXcsIHNvIG9wZW4gb24gd2hpY2hldmVyIHNwYXRpYWwgbW9kZSB0aGVpclxuICAgICAgICAgIC8vIGNvb3JkaW5hdGVzIHN1cHBvcnQuIExlYXZpbmcgdGhlIHR5cGUgYWxvbmUgc3RyYW5kcyB0aGUgaG9zdCBvbiBhbiBJbWFnZVxuICAgICAgICAgIC8vIHZpZXcgc2hvd2luZyB3aGF0ZXZlciBzbGlkZSB3YXMgbG9hZGVkIEJFRk9SRSDigJQgdGhlIG9ic2VydmF0aW9ucyBuZXZlclxuICAgICAgICAgIC8vIGFwcGVhciwgYW5kIHRoZSBwcmV2aW91cyBkYXRhc2V0J3MgdGlzc3VlIGRvZXMsIHdoaWNoIHJlYWRzIGFzIHRoaXNcbiAgICAgICAgICAvLyBkYXRhc2V0IGZhaWxpbmcgdG8gbG9hZC5cbiAgICAgICAgICAvL1xuICAgICAgICAgIC8vIEdhdGVkIG9uIHRoZSBjb29yZGluYXRlcywgbm90IG9uIHRoZSBkYXRhc2V0IG1lcmVseSBleGlzdGluZzogYVxuICAgICAgICAgIC8vIG9uZS1wbGFuZSBhc3NheSBoYXMgbm8geiBhbmQgY2Fubm90IGJlIGEgY2xvdWQuIFRoYXQgY2FzZSBvbmx5IHR1cm5lZCB1cFxuICAgICAgICAgIC8vIHdpdGggYW4gaW1hZ2UtbGVzcyAyRCBkYXRhc2V0LCB3aGljaCBpcyB3aHkgaXQgd2VudCB1bmhhbmRsZWQg4oCUIGJlZm9yZVxuICAgICAgICAgIC8vIGl0LCBpbWFnZS1sZXNzIG1lYW50IDNELlxuICAgICAgICAgIGNvbnN0IHRhcmdldCA9IGhhczNkID8gUGxvdFR5cGUuU1BBVElBTF9PTUlDU18zRCA6IFBsb3RUeXBlLlNQQVRJQUxfT01JQ1M7XG4gICAgICAgICAgaWYgKHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkICE9PSB0YXJnZXQpIHRoaXMub25TZWxlY3RQbG90VHlwZSh0YXJnZXQpO1xuICAgICAgICAgIC8vIFNhbWUgbW9kZSwgYW5vdGhlciBpbWFnZS1sZXNzIGRhdGFzZXQsIG5vdGhpbmcgbG9hZGVkOiByZS1wbG90IHNvIGl0cyBwbGFjZWhvbGRlclxuICAgICAgICAgIC8vIGltYWdlIGluZm8gKGFuZCB3aXRoIGl0IHRoZSByZWdpb25zJyBrZXkpIGlzIHRoaXMgZGF0YXNldCdzLCBub3QgdGhlIGxhc3Qgb25lJ3MuXG4gICAgICAgICAgZWxzZSBpZiAoIXRoaXMuaW1hZ2VJbmZvKSB0aGlzLnJlbG9hZEFuZFBsb3QoKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy8gQ2xlYXJpbmcgdGhlIGRhdGFzZXQgd2hpbGUgYSBzcGF0aWFsIG1vZGUgaXMgYWN0aXZlIGxlYXZlcyBhIHR5cGUgdGhhdCBpc1xuICAgICAgLy8gbm8gbG9uZ2VyIG9mZmVyZWQg4oCUIGZhbGwgYmFjayB0byBJbWFnZSwgYXMgdHVybmluZyB0ZXN0IG1vZGUgb2ZmIGRvZXMuXG4gICAgICB0aGlzLnJlY29uY2lsZVNlbGVjdGVkUGxvdFR5cGUoKTtcbiAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQdWJsaXNoIGEgZGF0YXNldCdzIHJlZmVyZW5jZSB2b2x1bWUgQVMgdGhlIGltYWdlLCBhbmQgb3BlbiB0aGUgMkQgSW1hZ2Ugdmlld1xuICAgKiBvbiBpdC5cbiAgICpcbiAgICogVGhlIHZvbHVtZSBpcyBhIDNEIGltYWdlIGRlbGl2ZXJlZCBpbiBvbmUgZmlsZSwgc28gZm9yIGEgZGF0YXNldCB0aGF0IGhhcyBub1xuICAgKiBzZWN0aW9uIGltYWdlIGl0IGlzIHRoZSBob25lc3QgdGhpbmcgdG8gcHV0IG9uIHNjcmVlbjogdGhlIHNsaWNlIGJhciBzY3J1YnMgeixcbiAgICogYW5kIHRoZSBjb250cmFzdCB3aW5kb3csIGNvbG9ybWFwcyBhbmQgcmVnaW9uIHRvb2xzIGFsbCB3b3JrIGJlY2F1c2UgdGhlXG4gICAqIHZvbHVtZSBnZW51aW5lbHkgaXMgdGhlIGltYWdlIG5vdy4gVGhlIDNEIGNsb3VkIHN0YXlzIG9uZSBtZW51IHBpY2sgYXdheS5cbiAgICpcbiAgICogS2V5ZWQgYnkgZGF0YXNldCArIGdlb21ldHJ5OiB0aGUgZGF0YXNldCBzdHJlYW0gcmUtZW1pdHMgb24gdGhpbmdzIGxpa2UgYVxuICAgKiBjb2xvdXItY29sdW1uIGNoYW5nZSwgYW5kIHJlYnVpbGRpbmcgdGhlbiB3b3VsZCByZS1mZXRjaCB0aGUgdm94ZWxzIGFuZCB0aHJvd1xuICAgKiB0aGUgdXNlciBiYWNrIHRvIHRoZSBtaWRkbGUgc2xpY2UuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHNob3dWb2x1bWVBc0ltYWdlKGRhdGFzZXQ6IFNwYXRpYWxEYXRhc2V0KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbWV0YSA9IGRhdGFzZXQudm9sdW1lO1xuICAgIGlmICghbWV0YSB8fCAhdGhpcy5zcGF0aWFsRGF0YT8uZ2V0Vm9sdW1lKSByZXR1cm47XG4gICAgY29uc3Qga2V5ID0gYCR7ZGF0YXNldC5pZH06JHttZXRhLndpZHRofXgke21ldGEuaGVpZ2h0fXgke21ldGEuZGVwdGh9YDtcbiAgICBpZiAoa2V5ID09PSB0aGlzLnZvbHVtZUltYWdlS2V5KSByZXR1cm47XG4gICAgLy8gQ2xhaW0gdGhlIGtleSBCRUZPUkUgYXdhaXRpbmc6IHRoZSBzdHJlYW0gY2FuIGVtaXQgYWdhaW4gd2hpbGUgdGhlIHZveGVsc1xuICAgIC8vIGFyZSBpbiBmbGlnaHQsIGFuZCB0d28gYnVpbGRzIG9mIHRoZSBzYW1lIHZvbHVtZSB3b3VsZCByYWNlIHRvIHB1Ymxpc2guXG4gICAgdGhpcy52b2x1bWVJbWFnZUtleSA9IGtleTtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyh0cnVlKTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgYnVpbHQgPSBhd2FpdCBidWlsZFZvbHVtZVN0YWNrSW1hZ2UoZGF0YXNldCwgYXdhaXQgdGhpcy5zcGF0aWFsRGF0YS5nZXRWb2x1bWUoKSk7XG4gICAgICAvLyBBIGRpZmZlcmVudCBkYXRhc2V0IHdhcyBzZWxlY3RlZCB3aGlsZSB0aGlzIG9uZSBlbmNvZGVkIOKAlCBpdHMgaW1hZ2UgaXMgdGhlXG4gICAgICAvLyBvbmUgdGhhdCBiZWxvbmdzIG9uIHNjcmVlbiwgc28gZHJvcCB3aGF0IHdlIGp1c3QgYnVpbHQuXG4gICAgICBpZiAodGhpcy52b2x1bWVJbWFnZUtleSAhPT0ga2V5KSB7XG4gICAgICAgIGJ1aWx0Py51cmxzLmZvckVhY2goKHUpID0+IFVSTC5yZXZva2VPYmplY3RVUkwodSkpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAoIWJ1aWx0KSB7XG4gICAgICAgIHRoaXMudm9sdW1lSW1hZ2VLZXkgPSBudWxsO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBQaWNrIHRoZSBtb2RlIGZpcnN0IHNvIHRoZSBpbWFnZSBsYW5kcyBpbiB0aGUgdmlldyB0aGF0IHdpbGwgc2hvdyBpdCxcbiAgICAgIC8vIGluc3RlYWQgb2YgcmVuZGVyaW5nIG9uY2UgaW50byB3aGF0ZXZlciBtb2RlIHRoZSBsYXN0IGRhdGFzZXQgbGVmdCBhY3RpdmUuXG4gICAgICBpZiAodGhpcy5zZWxlY3RlZFBsb3RUeXBlSWQgIT09IFBsb3RUeXBlLklNQUdFKSB0aGlzLm9uU2VsZWN0UGxvdFR5cGUoUGxvdFR5cGUuSU1BR0UpO1xuICAgICAgdGhpcy5yZXZva2VWb2x1bWVJbWFnZVVybHMoKTtcbiAgICAgIHRoaXMudm9sdW1lSW1hZ2VVcmxzID0gYnVpbHQudXJscztcbiAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKGJ1aWx0LmluZm8pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8gTm8gdm9sdW1lIHNlcnZlZCBhZnRlciBhbGw6IHRoZSBjbG91ZCBpcyBzdGlsbCByZW5kZXJhYmxlLCBzbyBmYWxsIGJhY2sgdG9cbiAgICAgIC8vIGl0IHJhdGhlciB0aGFuIGxlYXZpbmcgdGhlIGhvc3Qgb24gYW4gSW1hZ2UgdmlldyB3aXRoIG5vdGhpbmcgaW4gaXQuXG4gICAgICBjb25zb2xlLndhcm4oJ1t2aXN1YWxpemVyXSByZWZlcmVuY2Ugdm9sdW1lIHVuYXZhaWxhYmxlIOKAlCBmYWxsaW5nIGJhY2sgdG8gdGhlIDNEIGNsb3VkJywgZXJyKTtcbiAgICAgIHRoaXMudm9sdW1lSW1hZ2VLZXkgPSBudWxsO1xuICAgICAgaWYgKHRoaXMuaGFzU3BhdGlhbDNkRGF0YXNldCAmJiAhaXNTcGF0aWFsT21pY3MzZCh0aGlzLmJhc2VQbG90VHlwZSkpIHtcbiAgICAgICAgdGhpcy5vblNlbGVjdFBsb3RUeXBlKFBsb3RUeXBlLlNQQVRJQUxfT01JQ1NfM0QpO1xuICAgICAgfVxuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyhmYWxzZSk7XG4gICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIEZvcmdldCB0aGUgcHVibGlzaGVkIHZvbHVtZSBpbWFnZSBzbyByZS1zZWxlY3RpbmcgdGhlIGRhdGFzZXQgcmVidWlsZHMgaXQuXG4gICAqICBUaGUgVVJMcyBkZWxpYmVyYXRlbHkgc3RheSBhbGl2ZTogdGhlIGhvc3QgbWF5IHN0aWxsIGJlIGRpc3BsYXlpbmcgdGhhdCBpbWFnZVxuICAgKiAgYXQgdGhpcyBpbnN0YW50LCBhbmQgcmV2b2tpbmcgdW5kZXIgaXQgd291bGQgYnJlYWsgZXZlcnkgbGF0ZXIgdGlsZSByZWFkLiBUaGV5XG4gICAqICBhcmUgZnJlZWQgd2hlbiB0aGUgbmV4dCB2b2x1bWUgcmVwbGFjZXMgdGhlbSwgb3Igb24gZGVzdHJveS4gKi9cbiAgcHJpdmF0ZSBkcm9wVm9sdW1lSW1hZ2UoKTogdm9pZCB7XG4gICAgdGhpcy52b2x1bWVJbWFnZUtleSA9IG51bGw7XG4gIH1cblxuICBwcml2YXRlIHJldm9rZVZvbHVtZUltYWdlVXJscygpOiB2b2lkIHtcbiAgICB0aGlzLnZvbHVtZUltYWdlVXJscy5mb3JFYWNoKCh1KSA9PiBVUkwucmV2b2tlT2JqZWN0VVJMKHUpKTtcbiAgICB0aGlzLnZvbHVtZUltYWdlVXJscyA9IFtdO1xuICB9XG5cbiAgcHJpdmF0ZSBjb21wdXRlUGxvdFR5cGVPcHRpb25zKCkge1xuICAgIGNvbnN0IGNhcHMgPSB0aGlzLnBsb3RTZXJ2aWNlLmNhcGFiaWxpdGllcztcbiAgICBjb25zdCBpc1N0YWNrID0gISF0aGlzLmltYWdlSW5mbz8uaXNTdGFjaztcbiAgICBjb25zdCBpc0dyYXlzY2FsZSA9ICEhdGhpcy5pbWFnZUluZm8/LmlzR3JheXNjYWxlO1xuICAgIC8vIEEgbXVsdGljaGFubmVsIGltYWdlJ3MgYmFuZHMgYXJlIGVhY2ggc2luZ2xlLWJhbmQgKHNjYWxhciksIHNvIHRoZSBzY2FsYXJcbiAgICAvLyBwbG90IG1vZGVzIChIZWF0bWFwIC8gU3VyZmFjZSAvIFZvbHVtZSAvIElzb3N1cmZhY2UpIGFwcGx5IHBlciBjaGFubmVsIGV2ZW5cbiAgICAvLyB0aG91Z2ggdGhlIFJHQiBjb21wb3NpdGUgaXNuJ3QgZ3JheXNjYWxlIOKAlCBkb24ndCBnYXRlIHRoZW0gb3V0LlxuICAgIGNvbnN0IG0wID0gdGhpcy5pbWFnZUluZm8/LmltYWdlTWV0YT8uWzBdO1xuICAgIGNvbnN0IGlzTXVsdGljaGFubmVsID0gKG0wPy5jaGFubmVsQ291bnQgPz8gMSkgPiAxICYmIChtMD8ucmdiQ2hhbm5lbHMgPz8gMSkgPCAzO1xuICAgIGNvbnN0IHBhc3Nlc0dhdGVzID0gKGQ6IFBsb3RUeXBlRGVzY3JpcHRvcik6IGJvb2xlYW4gPT4ge1xuICAgICAgaWYgKGQuZGltZW5zaW9ucyA9PT0gJzNkJyAmJiAhY2Fwcy5oYXMoVmlld2VyRmVhdHVyZS5TdXJmYWNlM0QpKSByZXR1cm4gZmFsc2U7XG4gICAgICAvLyBWb2x1bWUgYW5kIElzb3N1cmZhY2UgcmF5bWFyY2ggdGhlIElNQUdFIFNUQUNLLCBhbmQgbm90aGluZyBlbHNlOiB0aGVzZVxuICAgICAgLy8gdHdvIGdhdGVzIGFyZSBhYm91dCB0aGUgbG9hZGVkIGltYWdlLCBmdWxsIHN0b3AuIEEgM0Qgb21pY3MgZGF0YXNldFxuICAgICAgLy8gcmVhY2hlcyB0aGVtIGJlY2F1c2UgaXRzIHJlZ2lzdGVyZWQgdm9sdW1lIGlzIHB1Ymxpc2hlZCBBUyBhIGdyYXlzY2FsZVxuICAgICAgLy8gei1zdGFjayBpbWFnZSAoYGJ1aWxkVm9sdW1lU3RhY2tJbWFnZWApLCBub3QgdGhyb3VnaCBhIHNlY29uZCB2b3hlbFxuICAgICAgLy8gc291cmNlIGhpZGluZyBiZWhpbmQgdGhlIHNhbWUgbW9kZXMuXG4gICAgICBpZiAoZC5yZXF1aXJlc1N0YWNrICYmICFpc1N0YWNrKSByZXR1cm4gZmFsc2U7XG4gICAgICBpZiAoZC5yZXF1aXJlc0dyYXlzY2FsZSAmJiAhaXNHcmF5c2NhbGUgJiYgIWlzTXVsdGljaGFubmVsKSByZXR1cm4gZmFsc2U7XG4gICAgICBpZiAoZC5yZXF1aXJlc1NwYXRpYWxEYXRhICYmICF0aGlzLmhhc1NwYXRpYWxEYXRhc2V0KSByZXR1cm4gZmFsc2U7XG4gICAgICBpZiAoZC5yZXF1aXJlc1NwYXRpYWwzZCAmJiAhdGhpcy5oYXNTcGF0aWFsM2REYXRhc2V0KSByZXR1cm4gZmFsc2U7XG4gICAgICAvLyBBbiBpbWFnZS1zb3VyY2VkIG1vZGUgcmVhZHMgUElYRUxTLiBBIHNwYXRpYWwgZGF0YXNldCB0aGF0IGJyaW5ncyBubyB0aXNzdWVcbiAgICAgIC8vIGltYWdlIChzZXFGSVNIIHJlY29yZHMgdW5pdGxlc3MgY29vcmRpbmF0ZXMsIG5vdCBhIHNlY3Rpb24pIGxlYXZlcyBub3RoaW5nIGZvclxuICAgICAgLy8gb25lIHRvIGRyYXcsIHNvIG9mZmVyaW5nIEltYWdlIC8gSGVhdG1hcCAvIFN1cmZhY2UgdGhlcmUgb2ZmZXJzIG1vZGVzIHRoYXQgY2FuXG4gICAgICAvLyBvbmx5IGNvbWUgdXAgYmxhbmsg4oCUIG9yIHdvcnNlLCBzaG93aW5nIHdoYXRldmVyIHNsaWRlIHdhcyBsb2FkZWQgYmVmb3JlLlxuICAgICAgLy9cbiAgICAgIC8vIE5hcnJvd2VkIHRvIFwiYSBkYXRhc2V0IGlzIHVwIEFORCB0aGVyZSBpcyBubyBpbWFnZVwiOiB3aXRoIG5vIGRhdGFzZXQgYXQgYWxsLFxuICAgICAgLy8gSW1hZ2Ugc3RheXMgb24gb2ZmZXIsIGJlY2F1c2UgYSBob3N0IHRoYXQgaGFzIG5vdCBsb2FkZWQgYW55dGhpbmcgeWV0IG5lZWRzIGFcbiAgICAgIC8vIGRlZmF1bHQgYW5kIGFuIGVtcHR5IHNlbGVjdG9yIHdvdWxkIGJlIHdvcnNlIHRoYW4gYSBibGFuayB2aWV3LlxuICAgICAgaWYgKGQuc291cmNlID09PSAnaW1hZ2UnICYmIHRoaXMuaGFzU3BhdGlhbERhdGFzZXRcbiAgICAgICAgJiYgIXRoaXMuc3BhdGlhbERhdGFzZXRIYXNQaXhlbHMpIHJldHVybiBmYWxzZTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH07XG4gICAgLy8gT3V0c2lkZSB0ZXN0IG1vZGUsIG9ubHkgdGhlIGN1cmF0ZWQgc2V0ICh0aG9zZSB3aXRoIGEgcHJvZHVjdGlvbkxhYmVsKVxuICAgIC8vIGlzIG9mZmVyZWQ7IHRlc3QgbW9kZSBleHBvc2VzIGV2ZXJ5IGJhY2tlbmQncyB0eXBlLlxuICAgIGNvbnN0IGN1cmF0ZWQgPSAoZDogeyBwcm9kdWN0aW9uTGFiZWw/OiBzdHJpbmcgfSk6IGJvb2xlYW4gPT4gdGhpcy50ZXN0TW9kZSB8fCAhIWQucHJvZHVjdGlvbkxhYmVsO1xuICAgIGNvbnN0IGJ1aWx0SW4gPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFBsb3RUeXBlRGVzY3JpcHRvcnMoKVxuICAgICAgLmZpbHRlcigoZCkgPT4gY3VyYXRlZChkKSAmJiBwYXNzZXNHYXRlcyhkKSk7XG4gICAgLy8gQ29udHJpYnV0ZWQgbW9kZXMgY29tZSBhZnRlciBldmVyeSBidWlsdC1pbiBvbmUsIHVuZGVyIHRoZSBzYW1lIGxhYmVsIHJ1bGVcbiAgICAvLyBhbmQgdGhlaXIgb3duIHN0YWNrL2dyYXlzY2FsZS9zcGF0aWFsIGdhdGVzLiBBIG1vZGUgYWxzbyBuZWVkcyB3aGF0ZXZlciBpdHMgYmFzZVxuICAgIC8vIHZpZXcgbmVlZHMgKHBpeGVscywgYSAzRC1jYXBhYmxlIGJhY2tlbmQpLCBzbyB0aGUgYmFzZSB0eXBlJ3MgZ2F0ZXMgYXBwbHlcbiAgICAvLyB0b28g4oCUIGEgY29udHJpYnV0ZWQgSW1hZ2UtYmFzZWQgbW9kZSBnb2VzIHdoZXJldmVyIEltYWdlIGdvZXMuXG4gICAgY29uc3QgY29udHJpYnV0ZWQ6IFBsb3RUeXBlT3B0aW9uW10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IGQgb2YgdGhpcy5wbG90TW9kZXMuZGVzY3JpcHRvcnMoKSkge1xuICAgICAgaWYgKCFjdXJhdGVkKGQpKSBjb250aW51ZTtcbiAgICAgIGlmIChkLnJlcXVpcmVzU3RhY2sgJiYgIWlzU3RhY2spIGNvbnRpbnVlO1xuICAgICAgaWYgKGQucmVxdWlyZXNHcmF5c2NhbGUgJiYgIWlzR3JheXNjYWxlICYmICFpc011bHRpY2hhbm5lbCkgY29udGludWU7XG4gICAgICBpZiAoZC5yZXF1aXJlc1NwYXRpYWxEYXRhICYmICF0aGlzLmhhc1NwYXRpYWxEYXRhc2V0KSBjb250aW51ZTtcbiAgICAgIGlmIChkLnJlcXVpcmVzU3BhdGlhbDNkICYmICF0aGlzLmhhc1NwYXRpYWwzZERhdGFzZXQpIGNvbnRpbnVlO1xuICAgICAgY29uc3QgYmFzZSA9IGdldFBsb3RUeXBlRGVzY3JpcHRvcihkLmJhc2VUeXBlKTtcbiAgICAgIGlmIChiYXNlICYmIHBhc3Nlc0dhdGVzKGJhc2UpKSBjb250cmlidXRlZC5wdXNoKGNvbnRyaWJ1dGVkUGxvdFR5cGVPcHRpb24oZCwgYmFzZSkpO1xuICAgIH1cbiAgICAvLyBEZWZhdWx0IHNlbGVjdG9yIHNob3dzIHRoZSBzdWZmaXgtZnJlZSBwcm9kdWN0aW9uTGFiZWw7IHRlc3QgbW9kZSBrZWVwc1xuICAgIC8vIHRoZSBmdWxsIGJhY2tlbmQtc3VmZml4ZWQgbGFiZWwgc28gc2FtZS1uYW1lZCBtb2RlcyBzdGF5IGRpc3Rpbmd1aXNoYWJsZS5cbiAgICBjb25zdCBsYWJlbGxlZCA9IDxUIGV4dGVuZHMgeyBsYWJlbDogc3RyaW5nOyBwcm9kdWN0aW9uTGFiZWw/OiBzdHJpbmcgfT4oZDogVCk6IFQgPT5cbiAgICAgICh0aGlzLnRlc3RNb2RlID8gZCA6IHsgLi4uZCwgbGFiZWw6IGQucHJvZHVjdGlvbkxhYmVsISB9KTtcbiAgICB0aGlzLnBsb3RUeXBlT3B0aW9ucyA9IGJ1aWx0SW4ubWFwKGxhYmVsbGVkKTtcbiAgICB0aGlzLnBsb3RUeXBlTWVudSA9IFsuLi50aGlzLnBsb3RUeXBlT3B0aW9ucywgLi4uY29udHJpYnV0ZWQubWFwKGxhYmVsbGVkKV07XG4gIH1cblxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgLy8gVGhlIGNvbnN0cnVjdG9yIGNvbXB1dGVzIHRoZSBvcHRpb25zIHdpdGggdGVzdE1vZGUgc3RpbGwgYXQgaXRzIGRlZmF1bHQ7XG4gICAgLy8gcmVjb21wdXRlIG9uY2UgdGhlIGhvc3QgYmluZHMgaXQgKGFuZCBvbiBhbnkgbGF0ZXIgY2hhbmdlKSBzbyB0aGUgc2VsZWN0b3JcbiAgICAvLyByZWZsZWN0cyB0ZXN0IG1vZGUgaW1tZWRpYXRlbHksIHdpdGhvdXQgd2FpdGluZyBmb3IgYW4gaW1hZ2UgdG8gKHJlKWxvYWQuXG4gICAgaWYgKGNoYW5nZXNbJ3Rlc3RNb2RlJ10pIHtcbiAgICAgIHRoaXMuY29tcHV0ZVBsb3RUeXBlT3B0aW9ucygpO1xuICAgICAgLy8gSWYgdGVzdCBtb2RlIGlzIHR1cm5lZCBvZmYgd2hpbGUgYSB0ZXN0LW9ubHkgdHlwZSAoZS5nLiBOQVBBUklfSU1BR0UpIGlzXG4gICAgICAvLyBhY3RpdmUsIHRoYXQgdHlwZSBpcyBubyBsb25nZXIgb2ZmZXJlZCDigJQgZmFsbCBiYWNrIHRvIEltYWdlLlxuICAgICAgdGhpcy5yZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIElmIHRoZSBhY3RpdmUgcGxvdCB0eXBlIGlzIG5vIGxvbmdlciBpbiB0aGUgb2ZmZXJlZCBvcHRpb25zIChlLmcuIHRlc3QgbW9kZVxuICAgKiAgdHVybmVkIG9mZiB3aGlsZSBhIHRlc3Qtb25seSB0eXBlIHdhcyBhY3RpdmUsIG9yIGEgc2NhbGFyIHR5cGUgY2FycmllZCBvbnRvXG4gICAqICBhbiBSR0IgaW1hZ2UpLCBmYWxsIGJhY2sgdG8gdGhlIGRlZmF1bHQgMkQgSW1hZ2Ugdmlldy4gKi9cbiAgLyoqIFdoZXRoZXIgdGhlIGxpdmUgc3BhdGlhbCBkYXRhc2V0IGJyaW5ncyBwaXhlbHMgb2YgaXRzIG93biDigJQgYSB0aXNzdWUgaW1hZ2UgaXRcbiAgICogIHJlZ2lzdGVycyBvbnRvLCBvciBhIHZvbHVtZSB0aGF0IGlzIHB1Ymxpc2hlZCBhcyBhIHotc3RhY2sgaW1hZ2UuICovXG4gIHByaXZhdGUgc3BhdGlhbERhdGFzZXRIYXNQaXhlbHMgPSBmYWxzZTtcblxuICAvKipcbiAgICogVGhlIGhvc3QgcHVibGlzaGVkIE5PIGltYWdlIOKAlCBhIHNwYXRpYWwgZGF0YXNldCB0aGF0IGJyaW5ncyBub25lIGRvZXMgdGhpcy5cbiAgICpcbiAgICogV2l0aG91dCBoYW5kbGluZyB0aGUgZW1wdHkgZW1pc3Npb24sIGBpbWFnZUluZm9gIGtlZXBzIHRoZSBMQVNUIGltYWdlJ3MgbWV0YWRhdGE6IHRoZVxuICAgKiBzZWxlY3RvciBnb2VzIG9uIG9mZmVyaW5nIHBpeGVsIG1vZGVzIGZvciBwaXhlbHMgdGhhdCBhcmUgZ29uZSwgYW5kIGV2ZXJ5XG4gICAqIGBpc1N0YWNrYC9gaXNHcmF5c2NhbGVgIGRlY2lzaW9uIGlzIG1hZGUgYWdhaW5zdCBhIGZpbGUgdGhhdCBpcyBubyBsb25nZXIgbG9hZGVkLlxuICAgKi9cbiAgcHJpdmF0ZSBvbkltYWdlQ2xlYXJlZCgpOiB2b2lkIHtcbiAgICAvLyBUaGUgaW1hZ2UgYSBjb250cmlidXRlZCBtb2RlIHdhcyBkcmF3aW5nIG92ZXIgaXMgZ29uZS4gQW4gb3BlbiBkaWFsb2dcbiAgICAvLyB0b29sIHN0YXlzIG9wZW4gYW5kIHJlc3RhcnRzIG9uY2UgdGhlIG5leHQgaW1hZ2UgaGFzIHBsb3R0ZWQuXG4gICAgdGhpcy5wbG90TW9kZXMuZGVhY3RpdmF0ZSgpO1xuICAgIHRoaXMudG9vbERpYWxvZ3MuZGVhY3RpdmF0ZSgpO1xuICAgIHRoaXMuaW1hZ2VJbmZvID0gdW5kZWZpbmVkO1xuICAgIHRoaXMubG9hZGVkRmlsZU5hbWUgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCk7XG4gICAgLy8gRGVsaWJlcmF0ZWx5IE5PVCByZWNvbmNpbGluZyB0aGUgc2VsZWN0ZWQgdHlwZSBoZXJlLiBSZWNvbmNpbGluZyBjYWxsc1xuICAgIC8vIGBzZXRQbG90VHlwZWAsIHdoaWNoIGRyaXZlcyBhIHJlLXBsb3Qg4oCUIGFuZCB0aGVyZSBpcyBub3RoaW5nIHRvIHBsb3QsIHNvIHRoZVxuICAgIC8vIFBsb3RseSBiYWNrZW5kIHJlYWQgaXRzIG93biBub3ctdW5zZXQgYGltYWdlSW5mb2AgYW5kIHRocmV3XG4gICAgLy8gKFwiY2FuJ3QgYWNjZXNzIHByb3BlcnR5IGlzR3JheXNjYWxlXCIpLCBhYm9ydGluZyB0aGUgbG9hZCB3aXRoIG5vIG9ic2VydmF0aW9uc1xuICAgIC8vIGRyYXduLiBJZ25vcmluZyB0aGUgZW1wdHkgZW1pc3Npb24gZW50aXJlbHkgaXMgd2hhdCB1c2VkIHRvIGF2b2lkIHRoYXQuXG4gICAgLy9cbiAgICAvLyBOb3RoaW5nIGlzIGxlZnQgc3RyYW5kZWQ6IGFuIGltYWdlIGlzIGNsZWFyZWQgYmVjYXVzZSBhIHNwYXRpYWwgZGF0YXNldCB0aGF0XG4gICAgLy8gYnJpbmdzIG5vbmUgaXMgYmVpbmcgb3BlbmVkLCBhbmQgdGhhdCBkYXRhc2V0J3Mgb3duIHN1YnNjcmlwdGlvbiBzZWxlY3RzIHRoZVxuICAgIC8vIG1vZGUgaXRzIGNvb3JkaW5hdGVzIHN1cHBvcnQgYSBtb21lbnQgbGF0ZXIuXG4gICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICB9XG5cbiAgcHJpdmF0ZSByZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnBsb3RUeXBlTWVudS5zb21lKChkKSA9PiBkLnR5cGUgPT09IHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkKSkgcmV0dXJuO1xuICAgIC8vIEltYWdlIGlzIHRoZSB1c3VhbCBmYWxsYmFjaywgYnV0IGl0IGlzIG5vdCBhbHdheXMgT04gT0ZGRVI6IHdpdGggbm8gaW1hZ2UgbG9hZGVkXG4gICAgLy8gdGhlIHBpeGVsIG1vZGVzIGFyZSBnb25lLCBhbmQgZmFsbGluZyBiYWNrIHRvIG9uZSB3b3VsZCBzZWxlY3QgYSBtb2RlIHRoZSBzZWxlY3RvclxuICAgIC8vIGRvZXMgbm90IGxpc3QgYW5kIG5vdGhpbmcgY2FuIGRyYXcuIFRha2UgdGhlIGZpcnN0IHR5cGUgc3RpbGwgb2ZmZXJlZCBpbnN0ZWFkLlxuICAgIGNvbnN0IGZhbGxiYWNrID0gdGhpcy5wbG90VHlwZU1lbnUuc29tZSgoZCkgPT4gZC50eXBlID09PSBQbG90VHlwZS5JTUFHRSlcbiAgICAgID8gUGxvdFR5cGUuSU1BR0VcbiAgICAgIDogdGhpcy5wbG90VHlwZU1lbnVbMF0/LnR5cGU7XG4gICAgaWYgKCFmYWxsYmFjaykgcmV0dXJuO1xuICAgIC8vIExlYXZpbmcgYSBjb250cmlidXRlZCBtb2RlIChpdHMgZ2F0ZXMgc3RvcHBlZCBwYXNzaW5nLCBvciBpdHMgcHJvdmlkZXIgaXNcbiAgICAvLyBnb25lKTogaXRzIHNlc3Npb24gZW5kcyBiZWZvcmUgYW55dGhpbmcgZWxzZSBkcmF3cy5cbiAgICB0aGlzLnBsb3RNb2Rlcy5kZWFjdGl2YXRlKCk7XG4gICAgY29uc3QgYmFzZSA9IHRoaXMucGxvdE1vZGVzLmJhc2VUeXBlT2YoZmFsbGJhY2spO1xuICAgIGlmIChiYXNlICE9PSBQbG90VHlwZS5JTUFHRSkgdGhpcy5jbG9zZURpYWxvZ1Rvb2woKTtcbiAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCA9IGZhbGxiYWNrO1xuICAgIHRoaXMucGxvdFR5cGUgPSBiYXNlO1xuICAgIHRoaXMuaXNIZWF0bWFwID0gYmFzZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRQbG90VHlwZShiYXNlKTtcbiAgfVxuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIC8vIEpvaW4gdGhlIGxpdmUgc2V0OyB0aGUgb2xkZXN0IG1lbWJlciByZW5kZXJzIHRoZSBzaGFyZWQgbm90aWNlIG91dGxldHMuXG4gICAgVmlzdWFsaXplckNvbXBvbmVudC5saXZlSW5zdGFuY2VzLmFkZCh0aGlzKTtcbiAgICB0aGlzLnN0YXRlLnNldERpYWdyYW0odGhpcyk7XG4gICAgdGhpcy5zZWxlY3RlZENvbG9ybWFwID1cbiAgICAgIHRoaXMuY29sb3JtYXBzT3B0aW9uc1swXS5jaGlsZHJlbi5maW5kKChjOiBhbnkpID0+IGMubGFiZWwgPT09ICdHcmV5cyBJbnYnKSA/P1xuICAgICAgdGhpcy5jb2xvcm1hcHNPcHRpb25zWzBdLmNoaWxkcmVuWzBdO1xuICAgIHRoaXMuc3RhY2tPcHRpb25zID0gW1xuICAgICAgeyBuYW1lOiAnU2luZ2xlIGltYWdlJywgdmFsOiAnZmFsc2UnIH0sXG4gICAgICB7IG5hbWU6ICdTdGFjaycsIHZhbDogJ3RydWUnIH0sXG4gICAgXTtcbiAgICB0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24gPSB0aGlzLnN0YWNrT3B0aW9uc1swXTtcbiAgICB0aGlzLndhdGNoU3BhdGlhbERhdGFzZXQoKTtcbiAgICB0aGlzLmF1dG9zY2FsZVN1YnNjcmlwdGlvbiA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0QXV0b3NjYWxlRXZlbnQoKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgLy8gcmVzZXQgdGhlIGltYWdlIG1vZGUgdG8gc2luZ2xlIGltYWdlXG4gICAgICB0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24gPSB7IG5hbWU6ICdTaW5nbGUgaW1hZ2UnLCB2YWw6ICdmYWxzZScgfTtcbiAgICAgIHRoaXMuYWN0aXZlRHJhZ01vZGUgPSBudWxsO1xuICAgICAgdGhpcy5zZXNzaW9uLnNldEFjdGl2ZVRvb2wobnVsbCk7XG4gICAgfSk7XG4gICAgdGhpcy5pbWFnZUxvYWRpbmdTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmlzSW1hZ2VMb2FkaW5nJCgpLnN1YnNjcmliZSgoaXNJbWFnZUxvYWRpbmcpID0+IHtcbiAgICAgIHRoaXMuaW1nTG9hZGluZyA9IGlzSW1hZ2VMb2FkaW5nO1xuICAgIH0pO1xuICAgIC8vIFVuZG8gYXZhaWxhYmlsaXR5IChqaXQtdWkjODUpOiB0aGUgc2hhcmVkIFJlZ2lvblN0b3JlIGVtaXRzIHdoZW5ldmVyIGl0c1xuICAgIC8vIGhpc3RvcnkgZGVwdGggY2hhbmdlczsgZ3JleXMgb3V0IHRoZSB0b29sYmFyIFVuZG8gYnV0dG9uIGFjY29yZGluZ2x5LlxuICAgIHRoaXMucGxvdFNlcnZpY2VcbiAgICAgIC5nZXRDYW5VbmRvJCgpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy51bnN1YikpXG4gICAgICAuc3Vic2NyaWJlKChjYW5VbmRvKSA9PiB7XG4gICAgICAgIHRoaXMuY2FuVW5kb1JlZ2lvbiA9IGNhblVuZG87XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pO1xuICAgIHRoaXMucGxvdFNlcnZpY2VcbiAgICAgIC5nZXRDYW5SZWRvJCgpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy51bnN1YikpXG4gICAgICAuc3Vic2NyaWJlKChjYW5SZWRvKSA9PiB7XG4gICAgICAgIHRoaXMuY2FuUmVkb1JlZ2lvbiA9IGNhblJlZG87XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pO1xuICAgIC8vIE1pcnJvciB0aGUgcmVnaW9uIHNlbGVjdGlvbiBzbyB0aGUgcmlnaHQtY2xpY2sgYWN0aW9uIGdyb3VwIChqaXQtdWkjODUpXG4gICAgLy8gY2FuIHJlYWQgaXQgc3luY2hyb25vdXNseSB3aGVuIHRoZSBtZW51IGlzIGJ1aWx0LlxuICAgIHRoaXMucGxvdFNlcnZpY2VcbiAgICAgIC5nZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyQoKVxuICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMudW5zdWIpKVxuICAgICAgLnN1YnNjcmliZSgoaW5kaWNlcykgPT4ge1xuICAgICAgICB0aGlzLnNlbGVjdGVkSW5kaWNlcyA9IGluZGljZXMgfHwgW107XG4gICAgICB9KTtcbiAgICAvLyBJbnRlcmFjdGl2ZSBwb2ludC1wcm9tcHQgc2VnbWVudGF0aW9uIHJ1bnMgaW5zaWRlIHRoZSByZW5kZXJlciBvbiBlYWNoXG4gICAgLy8gY2xpY2s7IHN1cmZhY2UgaXRzIGxpdmUgc3RhdHVzICsgZG93bmxvYWQgcHJvZ3Jlc3MgaW4gdGhlIHNoYXJlZCBgc2FtYFxuICAgIC8vIHRvYXN0IHNvIHRoZSB1c2VyIHNlZXMgaXQgd29ya2luZyAodGhlIGZpcnN0IGNsaWNrIHB1bGxzIHRoZSBlbmNvZGVyKS5cbiAgICB0aGlzLnNhbVBvaW50U3ViLmFkZChcbiAgICAgIHRoaXMuc2FtUG9pbnRUb29sLnByb2dyZXNzJC5zdWJzY3JpYmUoKGYpID0+IHtcbiAgICAgICAgdGhpcy5zYW1Eb3dubG9hZGluZyA9IGYgPj0gMCAmJiBmIDwgMTtcbiAgICAgICAgaWYgKGYgPj0gMCkgdGhpcy5zYW1Qcm9ncmVzcyA9IE1hdGgubWluKDEwMCwgTWF0aC5yb3VuZChmICogMTAwKSk7XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pLFxuICAgICk7XG4gICAgdGhpcy5zYW1Qb2ludFN1Yi5hZGQoXG4gICAgICB0aGlzLnNhbVBvaW50VG9vbC5zdGF0dXMkLnN1YnNjcmliZSgobSkgPT4ge1xuICAgICAgICB0aGlzLnNhbVN0YXR1cyA9IG07XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pLFxuICAgICk7XG4gICAgdGhpcy5zYW1Qb2ludFN1Yi5hZGQoXG4gICAgICB0aGlzLnNhbVBvaW50VG9vbC5idXN5JC5zdWJzY3JpYmUoKGJ1c3kpID0+IHtcbiAgICAgICAgdGhpcy5zYW1CdXN5ID0gYnVzeTtcbiAgICAgICAgaWYgKGJ1c3kpIHRoaXMuc2hvd1NhbVRvYXN0KCdTQU0gcG9pbnQgc2VnbWVudGF0aW9uJyk7XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pLFxuICAgICk7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRTdGFja0xvYWRpbmdQcm9ncmVzcygpLnN1YnNjcmliZSgobG9hZGluZ1Byb2dyZXNzKSA9PiB7XG4gICAgICB0aGlzLmxvYWRpbmdQZXJjZW50YWdlID0gbG9hZGluZ1Byb2dyZXNzO1xuICAgIH0pO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuaXNTdGFja0xvYWRpbmcoKS5zdWJzY3JpYmUoKHN0YWNrTG9hZGluZykgPT4ge1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmcgPSBzdGFja0xvYWRpbmc7XG4gICAgfSk7XG4gICAgdGhpcy5wbG90V2lkdGhTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmdldFBhbmVsV2lkdGgkKCkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVsYXlvdXQoKTtcbiAgICAgIC8vIFRoZSBpbnRlbnNpdHkgaW5zZXQgaXMgYSBzZXBhcmF0ZSBQbG90bHkgY2hhcnQgaW4gYSBmbG9hdGluZyBwYW5lbDsgcmVmbG93XG4gICAgICAvLyBpdCB0byBpdHMgY3VycmVudCBzaXplIHdoZW4gdGhlIGNhbnZhcyByZXNpemVzLCBlbHNlIGl0IGtlZXBzIHRoZSBzdGFsZVxuICAgICAgLy8gc2l6ZSBhbmQgYmxhbmtzIG91dC4gRGVmZXIgYSB0aWNrIHNvIHRoZSBwYW5lbCBsYXlvdXQgaGFzIHNldHRsZWQuXG4gICAgICBpZiAodGhpcy5oYXNQcm9maWxlcykge1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMucmVuZGVySW50ZW5zaXR5SW5zZXQoKSwgMCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5pbWdMb2FkaW5nTWVzc2FnZVN1YnNjcmlwdGlvbiA9IHRoaXMuc3RhdGUuZ2V0SW1hZ2VMb2FkaW5nTWVzc2FnZSQoKS5zdWJzY3JpYmUoKG1lc3NhZ2UpID0+IHtcbiAgICAgIHRoaXMubG9hZGluZ01lc3NhZ2UgPSBtZXNzYWdlO1xuICAgIH0pO1xuICAgIHRoaXMuc3RhdGVcbiAgICAgIC5nZXRDYWNoZVByb2dyZXNzJCgpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy51bnN1YikpXG4gICAgICAuc3Vic2NyaWJlKChwcm9ncmVzcykgPT4ge1xuICAgICAgICB0aGlzLmNhY2hlUHJvZ3Jlc3MgPSBwcm9ncmVzcztcbiAgICAgIH0pO1xuICAgIHRoaXMuaXNab29tU3Vic2NyaXB0aW9uID0gdGhpcy5zdGF0ZS5pc1pvb20kKCkuc3Vic2NyaWJlKCh6b29tKSA9PiB7XG4gICAgICB0aGlzLnpvb20gPSB6b29tO1xuICAgIH0pO1xuICAgIHRoaXMuZmlsZW5hbWVTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmdldEZpbGVuYW1lJCgpLnN1YnNjcmliZSgoZmlsZW5hbWUpID0+IHtcbiAgICAgIGlmIChmaWxlbmFtZSkge1xuICAgICAgICB0aGlzLmZpbGVOYW1lID0gZmlsZW5hbWU7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRDb2xvcm1hcCgpLnN1YnNjcmliZSgoY29sb3JtYXApID0+IHtcbiAgICAgIHRoaXMuc2VsZWN0ZWRDb2xvcm1hcCA9IGNvbG9ybWFwO1xuICAgIH0pO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0UmV2ZXJzZVNjYWxlKCkuc3Vic2NyaWJlKChyZXZlcnNlc2NhbGUpID0+IHtcbiAgICAgIHRoaXMucmV2ZXJzZXNjYWxlID0gcmV2ZXJzZXNjYWxlO1xuICAgIH0pO1xuICAgIHRoaXMuaW50ZW5zaXR5UHJvZmlsZVN1YiA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0SW50ZW5zaXR5UHJvZmlsZSQoKS5zdWJzY3JpYmUoKHByb2ZpbGVzKSA9PiB7XG4gICAgICB0aGlzLmxhdGVzdFByb2ZpbGVzID0gcHJvZmlsZXM7XG4gICAgICB0aGlzLmhhc1Byb2ZpbGVzID0gcHJvZmlsZXMubGVuZ3RoID4gMDtcbiAgICAgIC8vIFRoZSBwYW5lbCBkaXYgaXMgYmVoaW5kICpuZ0lmPVwiaGFzUHJvZmlsZXNcIi4gZGV0ZWN0Q2hhbmdlcygpIG1hdGVyaWFsaXplc1xuICAgICAgLy8gaXQgc3luY2hyb25vdXNseSAodGhlIHN1YnNjcmlwdGlvbiBtYXkgZmlyZSBvdXRzaWRlIEFuZ3VsYXIncyB6b25lIOKAlCBlLmcuXG4gICAgICAvLyBmcm9tIGFuIE9TRCBkcmFnIOKAlCBzbyBDRCB3b3VsZG4ndCBydW4gb24gaXRzIG93bikuIFRoZW4gcmVuZGVyIG9uIHRoZSBuZXh0XG4gICAgICAvLyBhbmltYXRpb24gZnJhbWUsIEFGVEVSIHRoZSBicm93c2VyIGxheXMgdGhlIHBhbmVsIG91dDogYSBzeW5jaHJvbm91cyBkcmF3XG4gICAgICAvLyBoaXRzIGEgemVyby1zaXplIGNvbnRhaW5lciBvbiBmaXJzdCBjcmVhdGUgYW5kIFBsb3RseSBrZWVwcyB0aGF0IHNpemUsIHNvXG4gICAgICAvLyB0aGUgaW5zZXQgc3RheXMgYmxhbmsgYW5kIGxpdmUgdXBkYXRlcyByZWRyYXcgaW50byB0aGUgc2FtZSB6ZXJvIGJveC5cbiAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiB0aGlzLnJlbmRlckludGVuc2l0eUluc2V0KCkpO1xuICAgIH0pO1xuICAgIC8vIFdoZW4gdGhlIE9TRCB2aWV3IHNldHRsZXMgYXQgYSBuZXcgem9vbS9wYW4sIHJlLXNhbXBsZSB0aGUgaW50ZW5zaXR5IGxpbmVzXG4gICAgLy8gZnJvbSBhIGNyb3Agb2YgdGhlIHZpc2libGUgcmVnaW9uIHNvIHRoZSBpbnNldCByZWZsZWN0cyB0aGUgem9vbS1sZXZlbFxuICAgIC8vIHJlc29sdXRpb24gKFBsb3RseSdzIG93biBoaWdoLWRlZiB6b29tIHVwZGF0ZXMgdGhlIHNhbXBsaW5nIGNhY2hlIGlubGluZSkuXG4gICAgdGhpcy52aWV3cG9ydENoYW5nZVN1YiA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0Vmlld3BvcnRDaGFuZ2UkKCkuc3Vic2NyaWJlKChyb2kpID0+IHtcbiAgICAgIGlmICh0aGlzLmhhc1Byb2ZpbGVzICYmIHRoaXMuaXNJbWFnZVZpZXcpIHtcbiAgICAgICAgdGhpcy5wbG90U2VydmljZS5yZWZyZXNoSW50ZW5zaXR5U2FtcGxpbmdGb3JSb2kocm9pLngsIHJvaS55LCByb2kud2lkdGgsIHJvaS5oZWlnaHQsIHRoaXMuekluZGV4KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLnByZXZpZXdTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmdldEltYWdlSW5mbyQoKS5zdWJzY3JpYmUoe1xuICAgICAgbmV4dDogKGltZ0luZm8pID0+IHtcbiAgICAgICAgaWYgKCFpbWdJbmZvKSB7XG4gICAgICAgICAgdGhpcy5vbkltYWdlQ2xlYXJlZCgpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoaW1nSW5mbykge1xuICAgICAgICAgIHRoaXMuaW1hZ2VJbmZvID0gaW1nSW5mbztcbiAgICAgICAgICAvLyBTdGFjay1vbmx5IHBsb3QgdHlwZXMgKGlzb3N1cmZhY2UsIHNjYXR0ZXIzZCkgZGVwZW5kIG9uIHdoZXRoZXIgdGhpc1xuICAgICAgICAgIC8vIGZpbGUgaXMgYSBzdGFjayDigJQgcmVjb21wdXRlIHRoZSBzZWxlY3RvciBvcHRpb25zLlxuICAgICAgICAgIHRoaXMuY29tcHV0ZVBsb3RUeXBlT3B0aW9ucygpO1xuICAgICAgICAgIC8vIElmIHRoZSBhY3RpdmUgcGxvdCB0eXBlIGlzbid0IHZhbGlkIGZvciB0aGlzIGltYWdlIChlLmcuIGEgc2NhbGFyXG4gICAgICAgICAgLy8gdHlwZSBsaWtlIENvbnRvdXIgY2FycmllZCBvdmVyIHRvIGFuIFJHQiBpbWFnZSksIGZhbGwgYmFjayB0byBJbWFnZS5cbiAgICAgICAgICB0aGlzLnJlY29uY2lsZVNlbGVjdGVkUGxvdFR5cGUoKTtcbiAgICAgICAgICAvLyByZXNldCBzdGFjayBvcHRpb25zIHNlbGVjdGlvblxuICAgICAgICAgIHRoaXMuc2VsZWN0ZWRTdGFja09wdGlvbiA9IGltZ0luZm8uc2hvd1N0YWNrID8gdGhpcy5zdGFja09wdGlvbnNbMV0gOiB0aGlzLnN0YWNrT3B0aW9uc1swXTtcblxuICAgICAgICAgIC8vIFJlYWQgZnJvbSBgaW1nSW5mb2AsIHRoZSB2YWx1ZSB0aGlzIGVtaXNzaW9uIGNhcnJpZWQsIHJhdGhlciB0aGFuIGZyb20gdGhlXG4gICAgICAgICAgLy8gZmllbGQuIEhhbmRsaW5nIHRoZSBlbXB0eSBlbWlzc2lvbiBtZWFucyB0aGUgZmllbGQgQ0FOIGJlIG51bGxlZCBwYXJ0LXdheVxuICAgICAgICAgIC8vIHRocm91Z2ggdGhpcyBicmFuY2g6IGByZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlYCBtYXkgY2FsbCBgc2V0UGxvdFR5cGVgLFxuICAgICAgICAgIC8vIHdoaWNoIGNhbiBtYWtlIHRoZSBob3N0IHB1Ymxpc2ggYSBuZXcgaW1hZ2Ugc3RhdGUsIHJlLWVudGVyaW5nIHRoaXMgdmVyeVxuICAgICAgICAgIC8vIHN1YnNjcmlwdGlvbi4gUmVhZGluZyB0aGUgZmllbGQgdGhlbiB0aHJldyBvbiBgaXNHcmF5c2NhbGVgIGFuZCBhYm9ydGVkIHRoZVxuICAgICAgICAgIC8vIGhhbmRsZXIsIHNvIHRoZSBvYnNlcnZhdGlvbnMgd2VyZSBuZXZlciBkcmF3bi5cbiAgICAgICAgICB0aGlzLmlzR3JheXNjYWxlRXZlbnQuZW1pdChpbWdJbmZvLmlzR3JheXNjYWxlKTtcbiAgICAgICAgICB0aGlzLmlzU3RhY2tFdmVudC5lbWl0KGltZ0luZm8uaXNTdGFjayk7XG4gICAgICAgICAgLy8gUmVzZXQgdG8gdGhlIGRlZmF1bHQgMkQgSW1hZ2UgdmlldyB3aGVuIGEgZGlmZmVyZW50IGltYWdlIGlzXG4gICAgICAgICAgLy8gc2VsZWN0ZWQgd2hpbGUgYSAzRCB0eXBlIGlzIGFjdGl2ZS5cbiAgICAgICAgICBpZiAoIXRoaXMuaXNIZWF0bWFwICYmIGltZ0luZm8uZmlsZU5hbWUgIT09IHRoaXMubG9hZGVkRmlsZU5hbWUpIHtcbiAgICAgICAgICAgIHRoaXMuaXNIZWF0bWFwID0gdHJ1ZTtcbiAgICAgICAgICAgIHRoaXMucGxvdFR5cGUgPSBQbG90VHlwZS5JTUFHRTtcbiAgICAgICAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gUGxvdFR5cGUuSU1BR0U7XG4gICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFBsb3RUeXBlKHRoaXMucGxvdFR5cGUpO1xuICAgICAgICAgICAgdGhpcy5hY3RpdmVTdXJmYWNlM2RNb2RlID0gJ3R1cm50YWJsZSc7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRoaXMubG9hZGVkRmlsZU5hbWUgPSBpbWdJbmZvLmZpbGVOYW1lO1xuICAgICAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0SW1hZ2VNZXRhKGltZ0luZm8uaW1hZ2VNZXRhKTtcbiAgICAgICAgICBjb25zdCB1cmxzID0gaW1nSW5mby51cmxzO1xuICAgICAgICAgIC8vIEEgbmV3ZXIgaW1hZ2UgQUxXQVlTIHByZWVtcHRzIGFuIGluLWZsaWdodCByZW5kZXIuIFRoaXMgd2FzXG4gICAgICAgICAgLy8gYGlmICghdGhpcy5ydW5uaW5nKWAsIHdoaWNoIERST1BQRUQgdGhlIG5ldyBpbWFnZSB3aGlsZSB0aGUgb2xkIG9uZVxuICAgICAgICAgIC8vIGZpbmlzaGVkOiBpZGVudGl0eSBhYm92ZSBoYWQgYWxyZWFkeSBmbGlwcGVkIHRvIHRoZSBuZXcgZmlsZSwgc28gdGhlXG4gICAgICAgICAgLy8gYXBwIHJlcG9ydGVkIGltYWdlIEIgYXMgbG9hZGVkIHdoaWxlIGltYWdlIEEgc3RheWVkIG9uIHNjcmVlbi4gV29yc2UsXG4gICAgICAgICAgLy8gYSBjb2xkIGltYWdlIGhvbGRzIHRoZSBzbG90IGZvciBpdHMgd2hvbGUgc2VydmVyLXNpZGUgY2FjaGUgZmlsbCwgc29cbiAgICAgICAgICAvLyBldmVyeSBjbGljayBmb3IgMS0yIG1pbnV0ZXMgd2FzIGRpc2NhcmRlZCBhZ2FpbnN0IGEgYmxhbmsgdmlld3BvcnRcbiAgICAgICAgICAvLyBhbmQgYSBcIkxvYWRpbmcgaW1hZ2UuLi5cIiBvdmVybGF5IHRoYXQgbmV2ZXIgcmVzb2x2ZWQuXG4gICAgICAgICAgaWYgKHVybHMpIHtcbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gKyt0aGlzLnJlbmRlclRva2VuO1xuICAgICAgICAgICAgY29uc3QgaXNDdXJyZW50ID0gKCkgPT4gdGhpcy5yZW5kZXJUb2tlbiA9PT0gdG9rZW47XG4gICAgICAgICAgICBpZiAodGhpcy5ydW5uaW5nKSB7XG4gICAgICAgICAgICAgIC8vIFN0b3AgdGhlIHByZXZpb3VzIHJlbmRlcidzIGZyYW1lIHN0cmVhbWluZyAobmFwYXJpIHZvbHVtZS9zdXJmYWNlXG4gICAgICAgICAgICAgIC8vIHByZWxvYWQga2VlcHMgZmV0Y2hpbmcgb3RoZXJ3aXNlKSBhbmQgY2xlYXIgaXRzIHNoYXJwZW4gZmxhZy4gSXRzXG4gICAgICAgICAgICAgIC8vIGNhbGxiYWNrcyBhcmUgYWxyZWFkeSBpbmVydCB2aWEgdGhlIHRva2VuLCBzbyBpdCBjYW4gbmVpdGhlciBwYWludFxuICAgICAgICAgICAgICAvLyBub3IgcmVsZWFzZSB0aGUgb3ZlcmxheSB0aGlzIHJlbmRlciBub3cgb3ducy5cbiAgICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5jYW5jZWxMb2FkaW5nPy4oKTtcbiAgICAgICAgICAgICAgdGhpcy5zaGFycGVuaW5nID0gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBpbWFnZSBzaXplIOKAlCBtZWFzdXJlIHRoZSBwbG90IGRpdiBkaXJlY3RseSBzbyB0aGUgdG9vbGJhciBoZWlnaHQgaXMgZXhjbHVkZWRcbiAgICAgICAgICAgIGNvbnN0IHBsb3REaXY6IEhUTUxFbGVtZW50IHwgbnVsbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdk5hbWUpO1xuICAgICAgICAgICAgY29uc3Qgc2NyZWVuSGVpZ2h0ID0gcGxvdERpdj8ub2Zmc2V0SGVpZ2h0IHx8IDUwMDtcbiAgICAgICAgICAgIC8vIEEgY29udHJpYnV0ZWQgbW9kZSdzIHNlc3Npb24gaXMgYm91bmQgdG8gdGhlIGJhc2UgdmlldyBhYm91dCB0byBiZVxuICAgICAgICAgICAgLy8gdG9ybiBkb3duLCBzbyBpdCBlbmRzIGhlcmUg4oCUIGJlZm9yZSByZXNldCgpLCBiZWZvcmUgYW55dGhpbmcgbmV3XG4gICAgICAgICAgICAvLyBkcmF3cy4gSWYgdGhlIG1vZGUgaXMgc3RpbGwgc2VsZWN0ZWQgb25jZSB0aGlzIHJlbmRlciBsYW5kcywgYVxuICAgICAgICAgICAgLy8gZnJlc2ggc2Vzc2lvbiBzdGFydHMgZm9yIGl0IChzZWUgYWN0aXZhdGVTZWxlY3RlZFBsb3RNb2RlKS5cbiAgICAgICAgICAgIHRoaXMucGxvdE1vZGVzLmRlYWN0aXZhdGUoKTtcbiAgICAgICAgICAgIHRoaXMudG9vbERpYWxvZ3MuZGVhY3RpdmF0ZSgpO1xuICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5yZXNldCgpO1xuICAgICAgICAgICAgdGhpcy5zdGFja0xvYWRpbmcgPSBpbWdJbmZvLmlzU3RhY2sgJiYgaW1nSW5mby5zaG93U3RhY2s7XG4gICAgICAgICAgICAvLyBzZXQgbWF4IGluZGV4IG9mIHN0YWNrIOKAlCBjb21wdXRlZCBiZWZvcmUgdXBkYXRlWkluZGV4IHNvIGFcbiAgICAgICAgICAgIC8vIGZyZXNoIHN0YWNrIChzaG9ydGVyIG9yIGxvbmdlciB0aGFuIHdoYXRldmVyIHdhcyBwcmV2aW91c2x5XG4gICAgICAgICAgICAvLyBsb2FkZWQpIGNsYW1wcyBhZ2FpbnN0IElUUyBib3VuZHMsIG5vdCB0aGUgc3RhbGUgb25lcy5cbiAgICAgICAgICAgIC8vIE9uZSBVUkwgcGVyIHNsaWNlICgwLWluZGV4ZWQpLCBzbyB0aGUgbGFzdCByZWFjaGFibGUgaW5kZXggaXNcbiAgICAgICAgICAgIC8vIGxlbmd0aC0xIOKAlCBlYXJsaWVyIGBsZW5ndGgtMmAgZHJvcHBlZCB0aGUgZmluYWwgc2xpY2UuXG4gICAgICAgICAgICB0aGlzLm1heEluZGV4ID0gdXJscy5sZW5ndGggPiAxID8gdXJscy5sZW5ndGggLSAxIDogMDtcbiAgICAgICAgICAgIC8vIE9uZS1zaG90IGhpbnQ6IGp1bXAgc3RyYWlnaHQgdG8gYSBzcGVjaWZpYyBzbGljZSAoZS5nLiB0aGVcbiAgICAgICAgICAgIC8vIGZpbGUgdGhlIHVzZXIgYWN0dWFsbHkgY2xpY2tlZCB3aXRoaW4gYSBudW1iZXJlZCBzZXJpZXMpLlxuICAgICAgICAgICAgLy8gQ29uc3VtZWQgaW1tZWRpYXRlbHkgc28gcmVkZWxpdmVyaW5nIHRoZSBzYW1lIEltYWdlSW5mbyBsYXRlclxuICAgICAgICAgICAgLy8gKGEgcGxvdC10eXBlIHN3aXRjaCwgcmVsb2FkQW5kUGxvdCkgZG9lc24ndCByZXNldCB0aGUgdXNlcidzXG4gICAgICAgICAgICAvLyBjdXJyZW50IHNjcnViIHBvc2l0aW9uIGJhY2sgdG8gaXQuXG4gICAgICAgICAgICBpZiAoaW1nSW5mby5pbml0aWFsWkluZGV4ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgdGhpcy56SW5kZXggPSBpbWdJbmZvLmluaXRpYWxaSW5kZXg7XG4gICAgICAgICAgICAgIGltZ0luZm8uaW5pdGlhbFpJbmRleCA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIG1ha2Ugc3VyZSB0aGUgemluZGV4IGlzIHdpdGhpbiBib3VuZHNcbiAgICAgICAgICAgIHRoaXMudXBkYXRlWkluZGV4KHVybHMpO1xuICAgICAgICAgICAgdGhpcy5ydW5uaW5nID0gdHJ1ZTtcbiAgICAgICAgICAgIC8vIE11bHRpLXRpZXIgcmVuZGVyaW5nIChzbWFsbCBibHVycnkgdGllciBmaXJzdCwgdGhlbiBzaGFycGVuIGluXG4gICAgICAgICAgICAvLyBwbGFjZSkg4oCUIHNlcXVlbmNpbmcgbGl2ZXMgaW4gUmVuZGVyT3JjaGVzdHJhdG9yOyB0aGlzIGNvbXBvbmVudFxuICAgICAgICAgICAgLy8gc3VwcGxpZXMgdGhlIHBoYXNlIHJlbmRlciBhbmQgb3ducyB0aGUgVUkgZmxhZ3MgdmlhIGNhbGxiYWNrcy5cbiAgICAgICAgICAgIC8vIDNEIHBsb3QgdHlwZXMgcmVuZGVyIHNpbmdsZS1wYXNzOiB0aGUgaW4tcGxhY2UgbGFyZ2UgcGFzcyBkb2Vzbid0XG4gICAgICAgICAgICAvLyByZWJ1aWxkIGEgM0QgZ2wtbWVzaCBpc29zdXJmYWNlLCBzbyB0aGUgc2hhcnBlbiBzdGVwIGJsYW5rZWQgaXQuXG4gICAgICAgICAgICBjb25zdCBoYXNTbWFsbFRpZXIgPVxuICAgICAgICAgICAgICB0aGlzLmlzSGVhdG1hcCAmJlxuICAgICAgICAgICAgICAoaW1nSW5mby5zbWFsbFVybHM/Lmxlbmd0aCA/PyAwKSA9PT0gdXJscy5sZW5ndGggJiZcbiAgICAgICAgICAgICAgKGltZ0luZm8uc21hbGxVcmxzPy5sZW5ndGggPz8gMCkgPiAwO1xuICAgICAgICAgICAgY29uc3Qgc21hbGxJbWdJbmZvID0gaGFzU21hbGxUaWVyID8geyAuLi5pbWdJbmZvLCB1cmxzOiBpbWdJbmZvLnNtYWxsVXJscyBhcyBzdHJpbmdbXSB9IDogbnVsbDtcblxuICAgICAgICAgICAgLy8gRW50ZXIgcGVyLXNsaWNlIHN0YWNrIG1vZGUgd2l0aCB0aGUgZ2l2ZW4gc2xpY2XihpJyZWdpb25zIG1hcCBhbmRcbiAgICAgICAgICAgIC8vIHNlZWQgdGhlIFwicHJldmlvdXMgc2hhcGVzXCIgZnJvbSB0aGUgbm93LWxpdmUgc2xpY2UuIGVudGVyU3RhY2tNb2RlXG4gICAgICAgICAgICAvLyBtYWtlcyB6SW5kZXgncyBzbGljZSBsaXZlIGFuZCByZXNldHMgdW5kbyAoaml0LXVpIzkzKS5cbiAgICAgICAgICAgIGNvbnN0IGVudGVyU3RhY2sgPSAoXG4gICAgICAgICAgICAgIHNsaWNlczogTWFwPG51bWJlciwgUmVnaW9uW10+LFxuICAgICAgICAgICAgICBsYXlvdXQ6ICdjb21iaW5lZCcgfCAncGVyLXNsaWNlLWZpbGUnLFxuICAgICAgICAgICAgKSA9PiB7XG4gICAgICAgICAgICAgIHRoaXMucGxvdFNlcnZpY2UuZW50ZXJTdGFja01vZGUoc2xpY2VzLCB0aGlzLnpJbmRleCwgbGF5b3V0KTtcbiAgICAgICAgICAgICAgY29uc3Qgc2hhcGVzID0gdGhpcy5wbG90U2VydmljZVxuICAgICAgICAgICAgICAgIC5nZXRSZWdpb25zKClcbiAgICAgICAgICAgICAgICAubWFwKChyZWdpb24pID0+IHJlZ2lvbi5nZXRTaGFwZSh0aGlzLnBsb3RTZXJ2aWNlLmdldFNob3dTaGFwZUxhYmVsKCkpKTtcbiAgICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRQcmV2aW91c1NoYXBlcyhzaGFwZXMpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgY29uc3QgYXBwbHlSb2kgPSAoKSA9PiB7XG4gICAgICAgICAgICAgIC8vIEZvbGRlciBzdGFjazogYSBzdGFjayBvZiBzZWxmLWNvbnRhaW5lZCBwZXItc2xpY2UgZmlsZXNcbiAgICAgICAgICAgICAgLy8gKHRpbGVkID09PSBmYWxzZTsgc2VlIGxvYWRTZXJpZXNBc1N0YWNrKS4gRWFjaCBzbGljZS1maWxlIG1heVxuICAgICAgICAgICAgICAvLyBjYXJyeSBpdHMgb3duIHNpYmxpbmcgXCI8c3RlbT4uZ2VvanNvblwiIChyb2lKc29uU3Ryc1t6XSksIGJ1dCBhXG4gICAgICAgICAgICAgIC8vIGZyZXNoIGZvbGRlciB3aXRoIG5vbmUgeWV0IGxlYXZlcyByb2lKc29uU3RycyB1bmRlZmluZWQg4oCUIGtleVxuICAgICAgICAgICAgICAvLyBvZmYgYHRpbGVkID09PSBmYWxzZWAsIE5PVCByb2lKc29uU3Rycywgc28gYW4gdW5hbm5vdGF0ZWRcbiAgICAgICAgICAgICAgLy8gZm9sZGVyIHN0YWNrIHN0aWxsIGVudGVycyB0aGUgcGVyLXNsaWNlLWZpbGUgbGF5b3V0IChhbmQgc2F2ZXNcbiAgICAgICAgICAgICAgLy8gYmFjayBvbmUgZ2VvanNvbiBwZXIgc2xpY2UtZmlsZSkgcmF0aGVyIHRoYW4gZmFsbGluZyB0aHJvdWdoIHRvXG4gICAgICAgICAgICAgIC8vIHRoZSBzaW5nbGUtZmlsZSBjb21iaW5lZCBwYXRoIChqaXQtdWkjOTMpLlxuICAgICAgICAgICAgICBpZiAoaW1nSW5mby5pc1N0YWNrICYmIGltZ0luZm8udGlsZWQgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGVyU2xpY2UgPSBpbWdJbmZvLnJvaUpzb25TdHJzO1xuICAgICAgICAgICAgICAgIGNvbnN0IHNsaWNlQ291bnQgPSBpbWdJbmZvLnVybHM/Lmxlbmd0aCA/PyBwZXJTbGljZT8ubGVuZ3RoID8/IDA7XG4gICAgICAgICAgICAgICAgY29uc3Qgc2xpY2VzID0gbmV3IE1hcDxudW1iZXIsIFJlZ2lvbltdPigpO1xuICAgICAgICAgICAgICAgIGZvciAobGV0IHogPSAwOyB6IDwgc2xpY2VDb3VudDsgeisrKSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCBqc29uID0gcGVyU2xpY2U/Llt6XSA/PyBudWxsO1xuICAgICAgICAgICAgICAgICAgc2xpY2VzLnNldCh6LCBqc29uID8gdGhpcy5wbG90U2VydmljZS5pbXBvcnRSZWdpb25zKGpzb24pIDogW10pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbnRlclN0YWNrKHNsaWNlcywgJ3Blci1zbGljZS1maWxlJyk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFNpbmdsZS1maWxlIHotc3RhY2s6IG9uZSBzaWJsaW5nIGdlb2pzb24gaG9sZGluZyBldmVyeSBzbGljZSdzXG4gICAgICAgICAgICAgIC8vIHJlZ2lvbnMgaW5kZXhlZCBieSBRdVBhdGgncyBnZW9tZXRyeS5wbGFuZS56LiBFbnRlciBwZXItc2xpY2VcbiAgICAgICAgICAgICAgLy8gbW9kZSAoc2F2aW5nIGJhY2sgb25lIGNvbWJpbmVkIHotaW5kZXhlZCBnZW9qc29uKSB3aGVuIHRoZVxuICAgICAgICAgICAgICAvLyBnZW9qc29uIGFjdHVhbGx5IGNhcnJpZXMgc2xpY2UgaW5kaWNlcywgb3Igd2hlbiB0aGVyZSdzIG5vdGhpbmdcbiAgICAgICAgICAgICAgLy8geWV0IHRvIGF1dGhvciBhZ2FpbnN0LiBBIGxlZ2FjeSBnZW9qc29uIHdob3NlIHJlZ2lvbnMgYXJlIGFsbCBvblxuICAgICAgICAgICAgICAvLyB0aGUgZGVmYXVsdCBwbGFuZSBzdGF5cyBnbG9iYWwgKHNob3duIG9uIGV2ZXJ5IHNsaWNlKSBzb1xuICAgICAgICAgICAgICAvLyBleGlzdGluZyBzaW5nbGUtcGxhbmUgYW5ub3RhdGlvbnMgYXJlbid0IGNvbmZpbmVkIHRvIHNsaWNlIDAuXG4gICAgICAgICAgICAgIGNvbnN0IHJvaUpzb24gPSBpbWdJbmZvLnJvaUpzb25TdHI7XG4gICAgICAgICAgICAgIGlmIChpbWdJbmZvLmlzU3RhY2spIHtcbiAgICAgICAgICAgICAgICBjb25zdCByZWdpb25zID0gcm9pSnNvbiA/IHRoaXMucGxvdFNlcnZpY2UuaW1wb3J0UmVnaW9ucyhyb2lKc29uKSA6IFtdO1xuICAgICAgICAgICAgICAgIGNvbnN0IGhhc1NsaWNlSW5mbyA9IHJlZ2lvbnMuc29tZSgocikgPT4gKHIueiA/PyAwKSAhPT0gMCk7XG4gICAgICAgICAgICAgICAgaWYgKCFyb2lKc29uIHx8IGhhc1NsaWNlSW5mbykge1xuICAgICAgICAgICAgICAgICAgY29uc3Qgc2xpY2VzID0gbmV3IE1hcDxudW1iZXIsIFJlZ2lvbltdPigpO1xuICAgICAgICAgICAgICAgICAgZm9yIChjb25zdCByIG9mIHJlZ2lvbnMpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgeiA9IHIueiA/PyAwO1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBidWNrZXQgPSBzbGljZXMuZ2V0KHopO1xuICAgICAgICAgICAgICAgICAgICBpZiAoYnVja2V0KSBidWNrZXQucHVzaChyKTtcbiAgICAgICAgICAgICAgICAgICAgZWxzZSBzbGljZXMuc2V0KHosIFtyXSk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBlbnRlclN0YWNrKHNsaWNlcywgJ2NvbWJpbmVkJyk7XG4gICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFNpbmdsZS1wbGFuZSBpbWFnZSAob3IgYSBsZWdhY3kgZ2xvYmFsIHotc3RhY2sgZ2VvanNvbik6IG9uZVxuICAgICAgICAgICAgICAvLyByZWdpb24gc2V0IGZvciB0aGUgd2hvbGUgaW1hZ2UuXG4gICAgICAgICAgICAgIGlmIChyb2lKc29uKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVnaW9ucyA9IHRoaXMucGxvdFNlcnZpY2UuaW1wb3J0UmVnaW9ucyhyb2lKc29uKTtcbiAgICAgICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFJlZ2lvbnMocmVnaW9ucyk7XG4gICAgICAgICAgICAgICAgY29uc3Qgc2hhcGVzID0gcmVnaW9ucy5tYXAoKHJlZ2lvbikgPT5cbiAgICAgICAgICAgICAgICAgIHJlZ2lvbi5nZXRTaGFwZSh0aGlzLnBsb3RTZXJ2aWNlLmdldFNob3dTaGFwZUxhYmVsKCkpLFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRQcmV2aW91c1NoYXBlcyhzaGFwZXMpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIExvYWRpbmcgYW4gaW1hZ2UncyBzYXZlZCBST0lzIGlzIG5vdCBhIHVzZXIgZWRpdCDigJQgc3RhcnQgdGhlXG4gICAgICAgICAgICAgIC8vIHVuZG8gaGlzdG9yeSBmcmVzaCBzbyB0aGUgZmlyc3QgdW5kbyBjYW4ndCB3aXBlIHRoZW0gKGppdC11aSM4NSkuXG4gICAgICAgICAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVzZXRVbmRvSGlzdG9yeSgpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGxldCBvdmVybGF5UmVsZWFzZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIGNvbnN0IHJlbGVhc2VPdmVybGF5ID0gKCkgPT4ge1xuICAgICAgICAgICAgICBpZiAob3ZlcmxheVJlbGVhc2VkKSByZXR1cm47XG4gICAgICAgICAgICAgIG92ZXJsYXlSZWxlYXNlZCA9IHRydWU7XG4gICAgICAgICAgICAgIGlmIChpbWdJbmZvLmlzU3RhY2sgJiYgaW1nSW5mby5zaG93U3RhY2spIHRoaXMuc3RhY2tMb2FkaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VMb2FkaW5nKGZhbHNlKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIG5ldyBSZW5kZXJPcmNoZXN0cmF0b3Ioe1xuICAgICAgICAgICAgICAvLyBpblBsYWNlPXRydWUgdXBkYXRlcyB0aGUgZXhpc3RpbmcgcmVuZGVyIGluc3RlYWQgb2YgcmVidWlsZGluZ1xuICAgICAgICAgICAgICAvLyBpdCwgc28gdGhlIGNhbnZhcyBkb2Vzbid0IGJsYW5rIGR1cmluZyB0aGUgc21hbGzihpJsYXJnZSBzd2FwLlxuICAgICAgICAgICAgICByZW5kZXJQaGFzZTogKHBoYXNlSW5mbywgaW5QbGFjZSkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIFN1cGVyc2VkZWQgQkVGT1JFIHRoaXMgcGhhc2Ugc3RhcnRlZCDigJQgZG9uJ3QgZXZlbiBpc3N1ZSB0aGUgbG9hZC5cbiAgICAgICAgICAgICAgICAvLyBSZW5kZXJPcmNoZXN0cmF0b3IgY2FsbHMgcmVuZGVyUGhhc2Ugb25jZSBwZXIgdGllciBhbmQgcmV0cmllcyB0aGVcbiAgICAgICAgICAgICAgICAvLyBzaGFycGVuIHBhc3Mgb24gZmFpbHVyZSwgc28gY2hlY2tpbmcgb25seSBhZnRlciB0aGUgbG9hZCByZXNvbHZlc1xuICAgICAgICAgICAgICAgIC8vIHdvdWxkIGxldCBhIHByZWVtcHRlZCByZW5kZXIga2VlcCBmZXRjaGluZyBzbGljZXMvdGlsZXMgZm9yIGFuXG4gICAgICAgICAgICAgICAgLy8gaW1hZ2Ugbm9ib2R5IGlzIGxvb2tpbmcgYXQuXG4gICAgICAgICAgICAgICAgaWYgKCFpc0N1cnJlbnQoKSkgcmV0dXJuIFByb21pc2UucmVzb2x2ZShudWxsKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5wbG90U2VydmljZS5sb2FkKHBoYXNlSW5mbywgdGhpcy56SW5kZXgpLnRoZW4oKGxvYWRlZEltYWdlKSA9PiB7XG4gICAgICAgICAgICAgICAgICAvLyBQcmVlbXB0ZWQgd2hpbGUgdGhpcyBwaGFzZSB3YXMgbG9hZGluZyDigJQgZHJvcCBpdCBvbiB0aGUgZmxvb3IuXG4gICAgICAgICAgICAgICAgICBpZiAoIWlzQ3VycmVudCgpKSByZXR1cm4gbnVsbDtcbiAgICAgICAgICAgICAgICAgIC8vIEd1YXJkIGFnYWluc3QgYSBuZXdlciBjbGljayByZWFjaGluZyB1cyBtaWQtcmVuZGVyLlxuICAgICAgICAgICAgICAgICAgaWYgKHBoYXNlSW5mby5maWxlTmFtZSAhPT0gbG9hZGVkSW1hZ2UuZmlsZW5hbWUpIHJldHVybiBudWxsO1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMucGxvdFNlcnZpY2UucGxvdChcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wbG90RGl2TmFtZSxcbiAgICAgICAgICAgICAgICAgICAgbG9hZGVkSW1hZ2UsXG4gICAgICAgICAgICAgICAgICAgIHBoYXNlSW5mbyxcbiAgICAgICAgICAgICAgICAgICAgc2NyZWVuSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICB0aGlzLnBsb3RUeXBlLFxuICAgICAgICAgICAgICAgICAgICBpblBsYWNlLFxuICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgc21hbGxTaG93bjogKCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghaXNDdXJyZW50KCkpIHJldHVybjsgLy8gc3VwZXJzZWRlZCBieSBhIG5ld2VyIGltYWdlXG4gICAgICAgICAgICAgICAgLy8gSWYgdGhlIGZpbGUgaXMgc3RpbGwgYmVpbmcgY2FjaGVkL3ByZXBhcmVkIHNlcnZlci1zaWRlLCBrZWVwIHRoZSBmdWxsXG4gICAgICAgICAgICAgICAgLy8gY2FjaGUtcHJvZ3Jlc3Mgb3ZlcmxheSB1cCBpbnN0ZWFkIG9mIGRyb3BwaW5nIHRvIHRoZSB0cmFuc2x1Y2VudCBcIlNoYXJwZW5pbmdcbiAgICAgICAgICAgICAgICAvLyBwcmV2aWV3Li4uXCIgc3Bpbm5lciBvdmVyIGEgYmxhbmsgY2FudmFzIChhIGxhcmdlIHVuY2FjaGVkIGltYWdlIHJlbmRlcnMgaXRzXG4gICAgICAgICAgICAgICAgLy8gc21hbGwgcHJldmlldyBiZWZvcmUgaXRzIHRpbGVzIGV4aXN0KS4gZmluaXNoZWQoKSByZWxlYXNlcyB0aGUgb3ZlcmxheSBvbmNlXG4gICAgICAgICAgICAgICAgLy8gdGhlIHJlYWwgcmVuZGVyIGxhbmRzLlxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmlzQ2FjaGluZykgcmV0dXJuO1xuICAgICAgICAgICAgICAgIC8vIFNtYWxsIHRpZXIgb24gc2NyZWVuIOKAlCBkcm9wIHRoZSBmdWxsIG92ZXJsYXkgYnV0IGtlZXAgYSB0cmFuc2x1Y2VudCBzcGlubmVyIHNvXG4gICAgICAgICAgICAgICAgLy8gdGhlIGJsdXJyeSByZW5kZXIgaXNuJ3QgbWlzdGFrZW4gZm9yIHRoZSBmaW5hbCBpbWFnZS5cbiAgICAgICAgICAgICAgICByZWxlYXNlT3ZlcmxheSgpO1xuICAgICAgICAgICAgICAgIHRoaXMuc2hhcnBlbmluZyA9IHRydWU7XG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIHNoYXJwZW5TZXR0bGVkOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKCFpc0N1cnJlbnQoKSkgcmV0dXJuOyAvLyBzdXBlcnNlZGVkIGJ5IGEgbmV3ZXIgaW1hZ2VcbiAgICAgICAgICAgICAgICB0aGlzLnNoYXJwZW5pbmcgPSBmYWxzZTtcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgZmluaXNoZWQ6IChfdmlhU21hbGwsIGxvZ1RhZykgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghaXNDdXJyZW50KCkpIHJldHVybjsgLy8gc3VwZXJzZWRlZCBieSBhIG5ld2VyIGltYWdlXG4gICAgICAgICAgICAgICAgLy8gSWRlbXBvdGVudCDigJQgcmVsZWFzZXMgbm93IGlmIHNtYWxsU2hvd24gZGVmZXJyZWQgaXQgKGNhY2hpbmcpIG9yIHdhcyBza2lwcGVkLlxuICAgICAgICAgICAgICAgIHJlbGVhc2VPdmVybGF5KCk7XG4gICAgICAgICAgICAgICAgdGhpcy5ydW5uaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgYXBwbHlSb2koKTtcbiAgICAgICAgICAgICAgICB0aGlzLmFjdGl2YXRlU2VsZWN0ZWRQbG90TW9kZSgpO1xuICAgICAgICAgICAgICAgIHRoaXMuYWN0aXZhdGVPcGVuRGlhbG9nVG9vbCgpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGxvZ1RhZyk7XG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIHNoYXJwZW5GYWlsZWQ6IChlcnI6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghaXNDdXJyZW50KCkpIHJldHVybjsgLy8gc3VwZXJzZWRlZCBieSBhIG5ld2VyIGltYWdlXG4gICAgICAgICAgICAgICAgLy8gVGhlIHNtYWxsIHRpZXIgc3RheXMgb24gc2NyZWVuIGFzIHRoZSBmYWxsYmFjayDigJQgdGVsbCB0aGVcbiAgICAgICAgICAgICAgICAvLyB1c2VyIHRoZSBzaGFycGVyIHZlcnNpb24gaXNuJ3QgY29taW5nLlxuICAgICAgICAgICAgICAgIGNvbnN0IG1zZyA9IGVycj8uZXJyb3I/Lm1lc3NhZ2UgfHwgZXJyPy5tZXNzYWdlIHx8IGVycj8uc3RhdHVzVGV4dCB8fCBTdHJpbmcoZXJyKTtcbiAgICAgICAgICAgICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgICAgICAgICAgICBrZXk6IHRoaXMudml6QWxlcnRUb2FzdEtleSxcbiAgICAgICAgICAgICAgICAgIHNldmVyaXR5OiAnd2FybicsXG4gICAgICAgICAgICAgICAgICBzdW1tYXJ5OiAnUHJldmlldyBub3Qgc2hhcnBlbmVkJyxcbiAgICAgICAgICAgICAgICAgIGRldGFpbDogYFRoZSBmdWxsLXJlc29sdXRpb24gcHJldmlldyBkaWQgbm90IGxvYWQgKCR7bXNnfSkuIFRoZSBsb3ctcmVzb2x1dGlvbiBwcmV2aWV3IGlzIHN0aWxsIHNob3duLiBUcnkgY2xpY2tpbmcgdGhlIGltYWdlIGFnYWluLmAsXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5ydW5uaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgYXBwbHlSb2koKTtcbiAgICAgICAgICAgICAgICB0aGlzLmFjdGl2YXRlU2VsZWN0ZWRQbG90TW9kZSgpO1xuICAgICAgICAgICAgICAgIHRoaXMuYWN0aXZhdGVPcGVuRGlhbG9nVG9vbCgpO1xuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSkucmVuZGVyKGltZ0luZm8sIHNtYWxsSW1nSW5mbyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgZXJyb3I6IChlcnIpID0+IHtcbiAgICAgICAgY29uc3QgbXNnID0gZXJyPy5lcnJvcj8ubWVzc2FnZSB8fCBlcnI/Lm1lc3NhZ2UgfHwgZXJyPy5zdGF0dXNUZXh0IHx8IFN0cmluZyhlcnIpO1xuICAgICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBvY2N1cmVkIHdoZW4gZ2V0dGluZyBpbWFnZSBpbmZvJywgZXJyKTtcbiAgICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICAgIGtleTogdGhpcy52aXpBbGVydFRvYXN0S2V5LFxuICAgICAgICAgIHN0aWNreTogdHJ1ZSxcbiAgICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgICBzdW1tYXJ5OiAnQW4gZXJyb3Igb2NjdXJlZCcsXG4gICAgICAgICAgZGV0YWlsOiBgVGhlIGZvbGxvd2luZyBlcnJvciBvY2N1cmVkIHdoaWxlIGdldHRpbmcgaW1hZ2UgaW5mbzogJHttc2d9LlxuICAgICAgICAgICAgICAgICAgIFBsZWFzZSB0cnkgdG8gb3BlbiB0aGUgaW1hZ2UgYWdhaW4gdGhyb3VnaCB0aGUgZmlsZSBuYXZpZ2F0b3IuYCxcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuc3RhY2tMb2FkaW5nID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VMb2FkaW5nKGZhbHNlKTtcbiAgICAgICAgdGhpcy5ydW5uaW5nID0gZmFsc2U7XG4gICAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVzZXQoKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICBuZ0FmdGVyVmlld0luaXQoKSB7XG4gICAgdGhpcy5wbG90Q29udGV4dE1lbnVMaXN0ZW5lciA9IChldmVudDogTW91c2VFdmVudCkgPT4ge1xuICAgICAgY29uc3QgcGxvdEVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2TmFtZSk7XG4gICAgICBpZiAoIXBsb3RFbD8uY29udGFpbnMoZXZlbnQudGFyZ2V0IGFzIE5vZGUpKSByZXR1cm47XG4gICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgZXZlbnQuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCk7XG4gICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICB0aGlzLmNvbnRleHRNZW51SXRlbXMgPSB0aGlzLmJ1aWxkQ29udGV4dE1lbnVJdGVtcygpO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICAgIHRoaXMuY29udGV4dE1lbnUuc2hvdyhldmVudCk7XG4gICAgICB9KTtcbiAgICB9O1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdjb250ZXh0bWVudScsIHRoaXMucGxvdENvbnRleHRNZW51TGlzdGVuZXIsIHRydWUpO1xuXG4gICAgdGhpcy5rZXlkb3duTGlzdGVuZXIgPSAoZXZlbnQ6IEtleWJvYXJkRXZlbnQpID0+IHtcbiAgICAgIGNvbnN0IHRhcmdldCA9IGV2ZW50LnRhcmdldCBhcyBIVE1MRWxlbWVudDtcbiAgICAgIGlmICh0YXJnZXQudGFnTmFtZSA9PT0gJ0lOUFVUJyB8fCB0YXJnZXQudGFnTmFtZSA9PT0gJ1RFWFRBUkVBJyB8fCB0YXJnZXQuaXNDb250ZW50RWRpdGFibGUpIHJldHVybjtcbiAgICAgIC8vIFNBTSBwb2ludCBtb2RlOiBFbnRlciBjb21taXRzIHRoZSBvYmplY3QsIEVzYyBjbGVhcnMgdGhlIHByb21wdC5cbiAgICAgIGlmICh0aGlzLmFjdGl2ZURyYWdNb2RlID09PSAnc2FtUG9pbnQnICYmIChldmVudC5rZXkgPT09ICdFbnRlcicgfHwgZXZlbnQua2V5ID09PSAnRXNjYXBlJykpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgICAgICBpZiAoZXZlbnQua2V5ID09PSAnRW50ZXInKSB0aGlzLnBsb3RTZXJ2aWNlLmNvbW1pdFNhbVBvaW50cygpO1xuICAgICAgICAgIGVsc2UgdGhpcy5wbG90U2VydmljZS5jbGVhclNhbVBvaW50cygpO1xuICAgICAgICAgIHRoaXMuaGlkZVNhbVRvYXN0KCk7IC8vIHByb21wdCByZXNvbHZlZCDihpIgZGlzbWlzcyB0aGUgc3RhdHVzIHRvYXN0XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBSZWdpb24gaGlzdG9yeSAoaml0LXVpIzg1KTogQ3RybC9DbWQrWiB1bmRvZXM7IEN0cmwvQ21kK1NoaWZ0K1ogb3JcbiAgICAgIC8vIEN0cmwvQ21kK1kgcmVkb2VzLlxuICAgICAgaWYgKGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSkge1xuICAgICAgICBjb25zdCBrID0gZXZlbnQua2V5LnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGlmIChrID09PSAneicgJiYgIWV2ZW50LnNoaWZ0S2V5KSB7XG4gICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy51bmRvUmVnaW9uKCkpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoKGsgPT09ICd6JyAmJiBldmVudC5zaGlmdEtleSkgfHwgayA9PT0gJ3knKSB7XG4gICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy5yZWRvUmVnaW9uKCkpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKGV2ZW50LmtleSA9PT0gJ0RlbGV0ZScgfHwgZXZlbnQua2V5ID09PSAnQmFja3NwYWNlJyB8fCBldmVudC5rZXkgPT09ICdkJyB8fCBldmVudC5rZXkgPT09ICdEJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy5kZWxldGVSZWdpb24oKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJysnIHx8IGV2ZW50LmtleSA9PT0gJz0nKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnpvb21JbigpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnLScgfHwgZXZlbnQua2V5ID09PSAnXycpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMuem9vbU91dCgpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAncCcpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3BhbicpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnYicpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3pvb21Ub0JveCcpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAncicpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2RyYXdyZWN0JykpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICdmJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd2Nsb3NlZHBhdGgnKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ3cnKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCd3YW5kJykpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICdlJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZXJhc2VWZXJ0ZXgnKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ3MnKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdzZWxlY3QnKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ2wnKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdkcmF3b3BlbnBhdGgnKSk7XG4gICAgICB9XG4gICAgfTtcbiAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigna2V5ZG93bicsIHRoaXMua2V5ZG93bkxpc3RlbmVyKTtcblxuICAgIHRoaXMud2hlZWxMaXN0ZW5lciA9IChldmVudDogV2hlZWxFdmVudCkgPT4ge1xuICAgICAgY29uc3QgcGxvdEVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2TmFtZSk7XG4gICAgICBpZiAoIXBsb3RFbD8uY29udGFpbnMoZXZlbnQudGFyZ2V0IGFzIE5vZGUpKSByZXR1cm47XG4gICAgICAvLyBJbnRlcmNlcHRpbmcgaGVyZSBmaXJlcyBhIEZJWEVEIHpvb20gc3RlcCBwZXIgd2hlZWwgZXZlbnQsIHdoaWNoIGlzIGZhclxuICAgICAgLy8gdG9vIHNlbnNpdGl2ZSBmb3IgYSByZW5kZXJlciB0aGF0IHJlYWRzIHRoZSBzY3JvbGwgZGVsdGEg4oCUIHNvIGFueXRoaW5nXG4gICAgICAvLyBkcmF3biBieSBzdWNoIGEgcmVuZGVyZXIga2VlcHMgaXRzIG93biB3aGVlbC4gU2VlIGByZW5kZXJlck93bnNXaGVlbGAuXG4gICAgICBpZiAocmVuZGVyZXJPd25zV2hlZWwodGhpcy5iYXNlUGxvdFR5cGUpKSByZXR1cm47XG4gICAgICAvLyAzRCBwbG90IHR5cGVzIChzdXJmYWNlLCBzY2F0dGVyM2QsIGlzb3N1cmZhY2UpIHJlbmRlciBpbiBhIFBsb3RseSBzY2VuZVxuICAgICAgLy8gdGhhdCBvcmJpdHMvem9vbXMgbmF0aXZlbHkgb24gc2Nyb2xsLiBUaGUgMkQgc3RlcC16b29tIGRvZXNuJ3QgYXBwbHkgYW5kXG4gICAgICAvLyB3b3VsZCB0aHJvdyAobm8geGF4aXMgb24gYSBzY2VuZSksIHNvIGxldCBQbG90bHkgaGFuZGxlIHRoZSB3aGVlbC5cbiAgICAgIGlmICghdGhpcy5pc0hlYXRtYXApIHJldHVybjtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICBldmVudC5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcbiAgICAgIGlmIChldmVudC5kZWx0YVkgPCAwKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnpvb21JbigpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQuZGVsdGFZID4gMCkge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy56b29tT3V0KCkpO1xuICAgICAgfVxuICAgIH07XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3doZWVsJywgdGhpcy53aGVlbExpc3RlbmVyLCB7IGNhcHR1cmU6IHRydWUsIHBhc3NpdmU6IGZhbHNlIH0pO1xuXG4gICAgLy8gRHJhZyBoYW5kbGluZyBmb3IgdGhlIGZsb2F0aW5nIGludGVuc2l0eS1wcm9maWxlIHBhbmVsLlxuICAgIHRoaXMucHJvZmlsZURyYWdNb3ZlTGlzdGVuZXIgPSAoZTogTW91c2VFdmVudCkgPT4ge1xuICAgICAgaWYgKHRoaXMucHJvZmlsZVBhbmVsRHJhZ2dpbmcpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgICAgICB0aGlzLnByb2ZpbGVQYW5lbFBvcyA9IHtcbiAgICAgICAgICAgIHg6IHRoaXMucHJvZmlsZVBhbmVsU3RhcnQueCArIChlLmNsaWVudFggLSB0aGlzLnByb2ZpbGVQYW5lbFN0YXJ0Lm14KSxcbiAgICAgICAgICAgIHk6IHRoaXMucHJvZmlsZVBhbmVsU3RhcnQueSArIChlLmNsaWVudFkgLSB0aGlzLnByb2ZpbGVQYW5lbFN0YXJ0Lm15KSxcbiAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy50b29sYmFyRHJhZ2dpbmcpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgICAgICB0aGlzLnRvb2xiYXJQb3MgPSB7XG4gICAgICAgICAgICB4OiB0aGlzLnRvb2xiYXJTdGFydC54ICsgKGUuY2xpZW50WCAtIHRoaXMudG9vbGJhclN0YXJ0Lm14KSxcbiAgICAgICAgICAgIHk6IHRoaXMudG9vbGJhclN0YXJ0LnkgKyAoZS5jbGllbnRZIC0gdGhpcy50b29sYmFyU3RhcnQubXkpLFxuICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5wcm9maWxlRHJhZ1VwTGlzdGVuZXIgPSAoKSA9PiB7XG4gICAgICB0aGlzLnByb2ZpbGVQYW5lbERyYWdnaW5nID0gZmFsc2U7XG4gICAgICB0aGlzLnRvb2xiYXJEcmFnZ2luZyA9IGZhbHNlO1xuICAgIH07XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIHRoaXMucHJvZmlsZURyYWdNb3ZlTGlzdGVuZXIpO1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgdGhpcy5wcm9maWxlRHJhZ1VwTGlzdGVuZXIpO1xuXG4gICAgLy8gT24gYW55IHdpbmRvdyByZXNpemUsIHJlZmxvdyB0aGUgaW50ZW5zaXR5IGluc2V0IHRvIGl0cyAoZml4ZWQpIHBhbmVsIHNpemVcbiAgICAvLyBvbiB0aGUgbmV4dCBmcmFtZSwgb25jZSBsYXlvdXQgaGFzIHNldHRsZWQuIFdpdGhvdXQgdGhpcyB0aGUgaW5zZXQgY2FuIGJlXG4gICAgLy8gbGVmdCBhdCBhIHN0YWxlL3plcm8gc2l6ZSBieSBhIG1pZC1yZWZsb3cgcmVzaXplIGFuZCBzdG9wIHNob3dpbmcuXG4gICAgdGhpcy5wcm9maWxlUmVzaXplTGlzdGVuZXIgPSAoKSA9PiB7XG4gICAgICBpZiAoIXRoaXMuaGFzUHJvZmlsZXMpIHJldHVybjtcbiAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiB0aGlzLnJlbmRlckludGVuc2l0eUluc2V0KCkpO1xuICAgIH07XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsIHRoaXMucHJvZmlsZVJlc2l6ZUxpc3RlbmVyKTtcbiAgICB0aGlzLm9uVmlld1JlYWR5KCk7XG4gIH1cblxuICAvKiogVGhlIHBsb3QgZGl2IGV4aXN0cyBub3c6IHJ1biBhbiBpbWFnZS1sZXNzIGRyYXcgdGhhdCBhcnJpdmVkIGJlZm9yZSBpdCBkaWQuICovXG4gIHByaXZhdGUgb25WaWV3UmVhZHkoKTogdm9pZCB7XG4gICAgdGhpcy52aWV3UmVhZHkgPSB0cnVlO1xuICAgIGNvbnN0IHBlbmRpbmcgPSB0aGlzLnBlbmRpbmdTcGF0aWFsRHJhdztcbiAgICB0aGlzLnBlbmRpbmdTcGF0aWFsRHJhdyA9IG51bGw7XG4gICAgLy8gT25seSBpZiBpdCBpcyBzdGlsbCB0aGUgZGF0YXNldCBvbiBvZmZlciBhbmQgbm8gaW1hZ2UgaGFzIGFycml2ZWQgbWVhbndoaWxlLiBCeSBpZCwgbm90IGJ5XG4gICAgLy8gb2JqZWN0OiB0aGUgcG9ydCBtYXkgcmUtZW1pdCB0aGUgc2FtZSBkYXRhc2V0IGFzIGEgbmV3IG9iamVjdCAoYSBjb2xvdXItY29sdW1uIGNoYW5nZSBkb2VzKSxcbiAgICAvLyBhbmQgdGhlbiB0aGUgY3VycmVudCBvYmplY3QgaXMgdGhlIG9uZSB0byBkcmF3LlxuICAgIGNvbnN0IGN1cnJlbnQgPSB0aGlzLnNwYXRpYWxEYXRhc2V0O1xuICAgIGlmIChwZW5kaW5nICYmIGN1cnJlbnQgJiYgY3VycmVudC5pZCA9PT0gcGVuZGluZy5pZCAmJiAhdGhpcy5pbWFnZUluZm8pIHtcbiAgICAgIHZvaWQgdGhpcy5wbG90U3BhdGlhbFdpdGhvdXRJbWFnZShjdXJyZW50KTtcbiAgICB9XG4gIH1cblxuICBvblByb2ZpbGVQYW5lbERyYWdTdGFydChlOiBNb3VzZUV2ZW50KSB7XG4gICAgdGhpcy5wcm9maWxlUGFuZWxEcmFnZ2luZyA9IHRydWU7XG4gICAgdGhpcy5wcm9maWxlUGFuZWxTdGFydCA9IHtcbiAgICAgIG14OiBlLmNsaWVudFgsXG4gICAgICBteTogZS5jbGllbnRZLFxuICAgICAgeDogdGhpcy5wcm9maWxlUGFuZWxQb3MueCxcbiAgICAgIHk6IHRoaXMucHJvZmlsZVBhbmVsUG9zLnksXG4gICAgfTtcbiAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gIH1cblxuICAvKiogR3JhYiB0aGUgdG9vbGJhciBoYW5kbGU6IGRldGFjaCBpdCBpbnRvIGEgZmxvYXRpbmcgd2luZG93IChpZiBzdGlsbCBkb2NrZWQpXG4gICAqICBhbmQgc3RhcnQgZHJhZ2dpbmcuICovXG4gIG9uVG9vbGJhckRyYWdTdGFydChlOiBNb3VzZUV2ZW50KSB7XG4gICAgaWYgKCF0aGlzLnRvb2xiYXJGbG9hdGluZykge1xuICAgICAgdGhpcy50b29sYmFyRmxvYXRpbmcgPSB0cnVlO1xuICAgICAgdGhpcy50b29sYmFyUG9zID0geyB4OiA4LCB5OiA4IH07XG4gICAgfVxuICAgIHRoaXMudG9vbGJhckRyYWdnaW5nID0gdHJ1ZTtcbiAgICB0aGlzLnRvb2xiYXJTdGFydCA9IHtcbiAgICAgIG14OiBlLmNsaWVudFgsXG4gICAgICBteTogZS5jbGllbnRZLFxuICAgICAgeDogdGhpcy50b29sYmFyUG9zLngsXG4gICAgICB5OiB0aGlzLnRvb2xiYXJQb3MueSxcbiAgICB9O1xuICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgfVxuXG4gIC8qKiBTbmFwIHRoZSBmbG9hdGluZyB0b29sYmFyIGJhY2sgdG8gaXRzIGRvY2tlZCBwb3NpdGlvbiBhY3Jvc3MgdGhlIHRvcC4gKi9cbiAgZG9ja1Rvb2xiYXIoKSB7XG4gICAgdGhpcy50b29sYmFyRmxvYXRpbmcgPSBmYWxzZTtcbiAgfVxuXG4gIC8qKiBSZW5kZXIgdGhlIGludGVuc2l0eSBpbnNldCBjaGFydCBmcm9tIHRoZSBsYXRlc3QgcHJvZmlsZSBkYXRhLiBUaGUgYWN0dWFsXG4gICAqICBjaGFydGluZyBpcyBvd25lZCBieSB0aGUgdmlzdWFsaXplciBzZXJ2aWNlIOKAlCB0aGUgY29tcG9uZW50IGp1c3QgZGVjaWRlc1xuICAgKiAgd2hlbiAocHJvZmlsZSBtb2RlICsgYSBmcmVzaCBwcm9maWxlKS4gKi9cbiAgcHJpdmF0ZSByZW5kZXJJbnRlbnNpdHlJbnNldCgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaGFzUHJvZmlsZXMpIHJldHVybjtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlbmRlckludGVuc2l0eUluc2V0KHRoaXMuaW50ZW5zaXR5SW5zZXREaXYsIHRoaXMubGF0ZXN0UHJvZmlsZXMpO1xuICB9XG5cbiAgLyoqIFRvb2xiYXIgXCJJbnRlbnNpdHlcIiBncm91cDogYWRkIGFub3RoZXIgbGluZSBST0kgKG5leHQgYnJpZ2h0IGNvbG91cikuICovXG4gIGFzeW5jIGFkZFByb2ZpbGVMaW5lKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIFBhcmsgdGhlIGZsb2F0aW5nIGluc2V0IG5lYXIgdGhlIHBsb3QncyB0b3AtcmlnaHQgd2hlbiB0aGUgZmlyc3QgbGluZSBpc1xuICAgIC8vIGFkZGVkIChpdCdzIHBvc2l0aW9uOmZpeGVkLCBzbyB1c2Ugdmlld3BvcnQgY29vcmRzIGZyb20gdGhlIHBsb3QgcmVjdCkuXG4gICAgaWYgKCF0aGlzLmhhc1Byb2ZpbGVzKSB7XG4gICAgICBjb25zdCByZWN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2TmFtZSk/LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgdGhpcy5wcm9maWxlUGFuZWxQb3MgPSByZWN0XG4gICAgICAgID8geyB4OiBNYXRoLm1heCgxMCwgcmVjdC5yaWdodCAtIDMwMCksIHk6IHJlY3QudG9wICsgMTAgfVxuICAgICAgICA6IHsgeDogMjAsIHk6IDcwIH07XG4gICAgfVxuICAgIC8vIEltYWdlIChPU0QpIG1vZGU6IFBsb3RseSBuZXZlciByZW5kZXJlZCwgc28gaXQgaGFzIG5vIHBpeGVsIGNhY2hlIC8gZXh0ZW50LlxuICAgIC8vIExvYWQgdGhlIGN1cnJlbnQgc2xpY2UncyBmcmFtZXMgZm9yIHNhbXBsaW5nICsgbGluZSBwbGFjZW1lbnQgZmlyc3QuXG4gICAgaWYgKHRoaXMuaXNJbWFnZVZpZXcgJiYgdGhpcy5pbWFnZUluZm8pIHtcbiAgICAgIGF3YWl0IHRoaXMucGxvdFNlcnZpY2UuZW5zdXJlSW50ZW5zaXR5U2FtcGxpbmcodGhpcy5pbWFnZUluZm8sIHRoaXMuekluZGV4KTtcbiAgICB9XG4gICAgY29uc3QgcmVnaW9uID0gdGhpcy5wbG90U2VydmljZS5nZXRJbnRlbnNpdHlDb250cm9scygpPy5hZGRQcm9maWxlTGluZSgpO1xuICAgIC8vIEF1dG8tc2VsZWN0IHRoZSBuZXcgbGluZSBvbiB0aGUgYWN0aXZlIGJhY2tlbmQgKFBsb3RseSBoYW5kbGVzIC8gT1NEXG4gICAgLy8gaGlnaGxpZ2h0KSBzbyBpdCdzIHJlYWR5IHRvIG1vdmUgb3IgZGVsZXRlIGltbWVkaWF0ZWx5LlxuICAgIGlmIChyZWdpb24pIHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2VsZWN0UmVnaW9uKHJlZ2lvbik7XG4gICAgICAvLyBPU0Qgb25seSBkcmF3cyBhIHNlbGVjdGVkIHJlZ2lvbidzIGhhbmRsZXMgaW4gYW4gZWRpdCBtb2RlLCBzbyBhIGxpbmUgaW5cbiAgICAgIC8vICdub25lJyBtb2RlIGxvb2tzIHVuc2VsZWN0ZWQuIFN3aXRjaCB0byAnc2VsZWN0JyBzbyB0aGUgbmV3IGxpbmUgc2hvd3NcbiAgICAgIC8vIGl0cyBlbmRwb2ludCBoYW5kbGVzIGFuZCBjYW4gYmUgZHJhZ2dlZC9kZWxldGVkIHJpZ2h0IGF3YXkuXG4gICAgICBpZiAodGhpcy5pc0ltYWdlVmlldyAmJiB0aGlzLmFjdGl2ZURyYWdNb2RlICE9PSAnc2VsZWN0Jykge1xuICAgICAgICB0aGlzLnRvZ2dsZURyYWdNb2RlKCdzZWxlY3QnKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICAvLyBBIGNvbnRyaWJ1dGVkIG1vZGUncyBzZXNzaW9uIGVuZHMgZmlyc3QsIHdoaWxlIHRoZSB2aWV3ZXIgaXQgZHJldyBvdmVyXG4gICAgLy8gc3RpbGwgZXhpc3RzLiBUaGUgcGFuZWwgZ29lcyB3aXRoIHRoaXMgdmlldywgc28gbm8gY2hhbmdlIGRldGVjdGlvbi5cbiAgICB0aGlzLmRlc3Ryb3lpbmcgPSB0cnVlO1xuICAgIHRoaXMucGxvdE1vZGVzLmRlYWN0aXZhdGUoKTtcbiAgICB0aGlzLnRvb2xEaWFsb2dzLmRlYWN0aXZhdGUoKTtcbiAgICAvLyBMZWF2ZSB0aGUgbGl2ZSBzZXQgc28gdGhlIG5leHQtb2xkZXN0IHZpc3VhbGl6ZXIgcGlja3MgdXAgdGhlIG91dGxldHMuXG4gICAgVmlzdWFsaXplckNvbXBvbmVudC5saXZlSW5zdGFuY2VzLmRlbGV0ZSh0aGlzKTtcbiAgICB0aGlzLnJldm9rZVZvbHVtZUltYWdlVXJscygpO1xuICAgIHRoaXMuc2NydWJiZXIuY2FuY2VsKCk7XG4gICAgdGhpcy5zYW1Qb2ludFN1Yi51bnN1YnNjcmliZSgpO1xuICAgIGlmICh0aGlzLnBsb3RDb250ZXh0TWVudUxpc3RlbmVyKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcignY29udGV4dG1lbnUnLCB0aGlzLnBsb3RDb250ZXh0TWVudUxpc3RlbmVyLCB0cnVlKTtcbiAgICB9XG4gICAgaWYgKHRoaXMua2V5ZG93bkxpc3RlbmVyKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcigna2V5ZG93bicsIHRoaXMua2V5ZG93bkxpc3RlbmVyKTtcbiAgICB9XG4gICAgaWYgKHRoaXMud2hlZWxMaXN0ZW5lcikge1xuICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3doZWVsJywgdGhpcy53aGVlbExpc3RlbmVyLCB0cnVlKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucHJvZmlsZURyYWdNb3ZlTGlzdGVuZXIpIHtcbiAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCB0aGlzLnByb2ZpbGVEcmFnTW92ZUxpc3RlbmVyKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucHJvZmlsZURyYWdVcExpc3RlbmVyKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsIHRoaXMucHJvZmlsZURyYWdVcExpc3RlbmVyKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucHJvZmlsZVJlc2l6ZUxpc3RlbmVyKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcigncmVzaXplJywgdGhpcy5wcm9maWxlUmVzaXplTGlzdGVuZXIpO1xuICAgIH1cbiAgICBpZiAodGhpcy5pbnRlbnNpdHlQcm9maWxlU3ViKSB7XG4gICAgICB0aGlzLmludGVuc2l0eVByb2ZpbGVTdWIudW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMudmlld3BvcnRDaGFuZ2VTdWIpIHtcbiAgICAgIHRoaXMudmlld3BvcnRDaGFuZ2VTdWIudW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgdGhpcy51bnN1Yi5uZXh0KCk7XG4gICAgdGhpcy51bnN1Yi5jb21wbGV0ZSgpO1xuICAgIGlmICh0aGlzLnByZXZpZXdTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMucHJldmlld1N1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5zcGF0aWFsRGF0YXNldFN1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5zcGF0aWFsRGF0YXNldFN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5wbG90V2lkdGhTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMucGxvdFdpZHRoU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmltYWdlTG9hZGluZ1N1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5pbWFnZUxvYWRpbmdTdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaW1nTG9hZGluZ01lc3NhZ2VTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMuaW1nTG9hZGluZ01lc3NhZ2VTdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaXNab29tU3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLmlzWm9vbVN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5hdXRvc2NhbGVTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMuYXV0b3NjYWxlU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmZpbGVuYW1lU3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLmZpbGVuYW1lU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIHRoaXMucGxvdFNlcnZpY2UudW5zdWJzY3JpYmUoKTtcbiAgfVxuXG4gIHB1YmxpYyBoYXNSZWdpb25zKCk6IGJvb2xlYW4ge1xuICAgIC8vIFVzZSB0aGUgY29udHJhY3QncyBmcmFtZXdvcmstbmV1dHJhbCByZWdpb24gYWNjZXNzb3IsIG5vdCB0aGUgcmF3XG4gICAgLy8gUGxvdGx5LXNoYXBlZCBnZXRTaGFwZXMoKSDigJQgdGhlIGNvbXBvbmVudCBvbmx5IG5lZWRzIHRvIGtub3cgd2hldGhlciBhbnlcbiAgICAvLyByZWdpb24gZXhpc3RzLCBhbmQgbXVzdCBub3QgZGVwZW5kIG9uIGEgYmFja2VuZCdzIHdpcmUgZm9ybWF0LlxuICAgIHJldHVybiB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKS5sZW5ndGggPiAwO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoaXMgbWV0aG9kIGlzIGNhbGxlZCBpbiBtYWluRmFjYWRlLmNvbmZpZ1JlcXVlc3QoKSAtIERPIE5PVCBERUxFVEVcbiAgICovXG4gIHB1YmxpYyBnZXRSZWdpb25Qb2x5Z29ucygpOiBQb2x5Z29uW10ge1xuICAgIHJldHVybiB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvblBvbHlnb25zKCk7XG4gIH1cblxuICAvKipcbiAgICogU2VsZWN0IHRoZSBjb2xvciBzY2FsZSB0eXBlOiBjYWxsZWQgYnkgdGhlIGNvbG9yIHNjYWxlIHNlbGVjdCBidXR0b25cbiAgICogQHBhcmFtIGV2ZW50XG4gICAqL1xuICB0b2dnbGVSZXZlcnNlU2NhbGUoKSB7XG4gICAgdGhpcy5yZXZlcnNlc2NhbGUgPSAhdGhpcy5yZXZlcnNlc2NhbGU7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRSZXZlcnNlU2NhbGUodGhpcy5yZXZlcnNlc2NhbGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlbGVjdCBhIGNvbG9yIG1hcDogY2FsbGVkIGJ5IHRoZSBjb2xvciBtYXAgZHJvcGRvd24gYnV0dG9uXG4gICAqIEBwYXJhbSBjb2xvcm1hcE5vZGVcbiAgICovXG4gIHNlbGVjdENvbG9ybWFwKGNvbG9ybWFwTm9kZTogVHJlZU5vZGUpIHtcbiAgICBpZiAoIWNvbG9ybWFwTm9kZS5jaGlsZHJlbikge1xuICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRDb2xvcm1hcChjb2xvcm1hcE5vZGUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBPcGVuIHRoZSBDaGFubmVscyAmIEhpc3RvZ3JhbSBkaWFsb2cgKHRvb2xiYXIgYnV0dG9uKS4gKi9cbiAgb3BlbkNoYW5uZWxIaXN0b2dyYW0oKSB7XG4gICAgdGhpcy5zaG93Q2hhbm5lbEhpc3RvZ3JhbSA9IHRydWU7XG4gIH1cblxuICAvKiogVG9vbGJhciDihpIgb3BlbiB0aGUgc3BhdGlhbC1vbWljcyBjb250cm9scy4gKi9cbiAgb3BlblNwYXRpYWxDb250cm9scygpOiB2b2lkIHtcbiAgICB0aGlzLnNob3dTcGF0aWFsQ29udHJvbHMgPSB0cnVlO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIFJlZ2lvbiBFZGl0b3IgZGlhbG9nICh0b29sYmFyIGJ1dHRvbikuIFRoZSBlZGl0b3Igc3RheXMgbGlua2VkIHRvXG4gICAqICB0aGlzIHBsb3R0aW5nIGluc3RhbmNlIHRocm91Z2ggdGhlIHJvb3QgUmVnaW9uU3RvcmUgc2luZ2xldG9uLCBzbyBpdCB3b3Jrc1xuICAgKiAgdGhlIHNhbWUgYXMgdGhlIGZvcm1lciByaWdodC1wYW5lbCBSZWdpb25zIHRhYi4gKi9cbiAgb3BlblJlZ2lvbkVkaXRvcigpIHtcbiAgICAvLyBTaXplIHRoZSBkaWFsb2cgdG8gbWF0Y2ggdGhlIGhvc3QncyBjb25maWd1cmVkIGVsZW1lbnQgKGUuZy4gdGhlIGFwcCdzXG4gICAgLy8gcmlnaHQgcGFuZWwpIGF0IG9wZW4gdGltZS4gTWVhc3VyZWQgb25jZSDigJQgaXQgaW50ZW50aW9uYWxseSBkb2VzIG5vdFxuICAgIC8vIHRyYWNrIGxhdGVyIGJyb3dzZXIvc3BsaXQgcmVzaXplcy4gRmFsbHMgYmFjayB0byBhIHF1YXJ0ZXIgb2YgdGhlIHBhZ2VcbiAgICAvLyB3aGVuIG5vIHNlbGVjdG9yIGlzIGNvbmZpZ3VyZWQgb3IgdGhlIGVsZW1lbnQgaXNuJ3QgcHJlc2VudC5cbiAgICBjb25zdCBzZWxlY3RvciA9IHRoaXMudml6Q29uZmlnPy5yZWdpb25FZGl0b3JXaWR0aFNlbGVjdG9yO1xuICAgIGNvbnN0IHdpZHRoID0gc2VsZWN0b3JcbiAgICAgID8gZG9jdW1lbnQucXVlcnlTZWxlY3RvcjxIVE1MRWxlbWVudD4oc2VsZWN0b3IpPy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS53aWR0aFxuICAgICAgOiB1bmRlZmluZWQ7XG4gICAgdGhpcy5yZWdpb25FZGl0b3JXaWR0aCA9IHdpZHRoICYmIHdpZHRoID4gMCA/IGAke01hdGgucm91bmQod2lkdGgpfXB4YCA6ICcyNXZ3JztcbiAgICB0aGlzLnNob3dSZWdpb25FZGl0b3IgPSB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGxlZCBieSB0aGUgc3RhY2svc2luZ2xlIGltYWdlIGRyb3Bkb3duIGJ1dHRvblxuICAgKiBAcGFyYW0gc2hvd3N0YWNrXG4gICAqL1xuICBzZWxlY3RTdGFja09wdGlvbihzZWxlY3RlZFN0YWNrT3B0aW9uOiBhbnkpIHtcbiAgICBjb25zdCBzaG93c3RhY2sgPSBzZWxlY3RlZFN0YWNrT3B0aW9uLnZhbCA9PT0gJ3RydWUnO1xuICAgIHRoaXMuc2VsZWN0ZWRTdGFja09wdGlvbiA9IHNlbGVjdGVkU3RhY2tPcHRpb247XG4gICAgY29uc29sZS5sb2coJ3NlbGVjdGVkIHN0YWNrIG9wdGlvbicgKyBKU09OLnN0cmluZ2lmeSh0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24pKTtcbiAgICB0aGlzLnN0YWNrTG9hZGluZyA9IHNob3dzdGFjaztcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyghc2hvd3N0YWNrKTtcbiAgICAvLyBTdGFjayBtb2RlIGlzIGFsd2F5cyBhIDJEIGhlYXRtYXAg4oCUIHJlc2V0IHN1cmZhY2UgbW9kZSBpZiBhY3RpdmVcbiAgICBpZiAoIXRoaXMuaXNIZWF0bWFwKSB7XG4gICAgICB0aGlzLmlzSGVhdG1hcCA9IHRydWU7XG4gICAgICB0aGlzLnBsb3RUeXBlID0gUGxvdFR5cGUuSEVBVE1BUDtcbiAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gUGxvdFR5cGUuSEVBVE1BUDtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UGxvdFR5cGUodGhpcy5wbG90VHlwZSk7XG4gICAgfVxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U2hvd1N0YWNrKHNob3dzdGFjayk7XG4gIH1cblxuICB1cGRhdGVaSW5kZXgodXJscz86IHN0cmluZ1tdKSB7XG4gICAgaWYgKHRoaXMuekluZGV4ID4gdGhpcy5tYXhJbmRleCkge1xuICAgICAgdGhpcy56SW5kZXggPSB0aGlzLm1heEluZGV4O1xuICAgIH1cbiAgICBpZiAodGhpcy56SW5kZXggPCAwKSB7XG4gICAgICB0aGlzLnpJbmRleCA9IDA7XG4gICAgfVxuICAgIGlmICh1cmxzKSB7XG4gICAgICBpZiAodGhpcy56SW5kZXggPiB1cmxzPy5sZW5ndGgpIHtcbiAgICAgICAgdGhpcy56SW5kZXggPSAwO1xuICAgICAgICB0aGlzLm1heEluZGV4ID0gdXJscy5sZW5ndGggLSAxO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFpJbmRleCh0aGlzLnpJbmRleCk7XG4gIH1cblxuICAvKipcbiAgICogTGl2ZSB6LXNsaWNlIHNjcnViIGZvciB0aGUgSW1hZ2UgdmlldzogdGhlIHJlbmRlcmVyIHN3YXBzIHRoZSBkaXNwbGF5ZWRcbiAgICogc2xpY2UgaW4gcGxhY2UgKG5vIHJlLW1vdW50KSwgc28gZHJhZ2dpbmcgdGhlIHNsaWRlciBzdGVwcyB0aHJvdWdoIHRoZVxuICAgKiBzdGFjayB0aGUgc2FtZSB3YXkgdGhlIGhlYXRtYXAgZnJhbWUgc2xpZGVyIGRvZXMuXG4gICAqL1xuICBvblpTbGlkZSh6OiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoeiA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy56SW5kZXggPSB6O1xuICAgIHRoaXMuc2NydWJiZXIuY29tbWl0KHopO1xuICAgIC8vIEtlZXAgdGhlIGludGVuc2l0eSBpbnNldCBpbiBzeW5jIHdpdGggdGhlIGRpc3BsYXllZCBzbGljZSAoSW1hZ2UvT1NEIG1vZGUsXG4gICAgLy8gd2hlcmUgdGhlIHByb2ZpbGUgc2FtcGxlciBpcyBmZWQgZnJvbSB0aGUgbG9hZGVkIHByZXZpZXcgZnJhbWVzKS5cbiAgICBpZiAodGhpcy5oYXNQcm9maWxlcyAmJiB0aGlzLmlzSW1hZ2VWaWV3ICYmIHRoaXMuaW1hZ2VJbmZvKSB7XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLmVuc3VyZUludGVuc2l0eVNhbXBsaW5nKHRoaXMuaW1hZ2VJbmZvLCB6KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTGl2ZSAoZGVib3VuY2VkKSBzY3J1YiB3aGlsZSBkcmFnZ2luZyB0aGUgSW1hZ2UtdmlldyB6LXNsaWRlcjogdXBkYXRlIHRoZVxuICAgKiBzbGljZSBhcyB0aGUgdXNlciBkcmFncywgYnV0IGNvYWxlc2NlIHJhcGlkIGNoYW5nZXMgc28gd2UgZG9uJ3QgZmlyZSBhXG4gICAqIHNsaWNlIHN3YXAgb24gZXZlcnkgcGl4ZWwuIFRoZSBmaW5hbCB2YWx1ZSBhbHNvIGxhbmRzIHZpYSBvblpTbGlkZSAob25TbGlkZUVuZCkuXG4gICAqL1xuICBvblpTY3J1Yih6OiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoeiA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy56SW5kZXggPSB6O1xuICAgIHRoaXMuc2NydWJiZXIuc2NydWIoeik7XG4gIH1cblxuICAvKipcbiAgICogS2V5Ym9hcmQgc3RhY2sgbmF2aWdhdGlvbjog4oaQL+KGkiBzdGVwIHRoZSB6LXNsaWNlIGluIHRoZSBJbWFnZSB2aWV3LCB0aGUgc2FtZVxuICAgKiB3YXkgdGhlIHNsaWRlciBkb2VzLiBPbmx5IGFjdGl2ZSBmb3IgYSBsb2FkZWQgc3RhY2sgaW4gSW1hZ2UgdmlldywgYW5kXG4gICAqIGlnbm9yZWQgd2hpbGUgYSBmb3JtIGZpZWxkIGlzIGZvY3VzZWQgc28gdHlwaW5nIGlzbid0IGhpamFja2VkLiBVcC9Eb3duIGFyZVxuICAgKiBsZWZ0IHRvIE9wZW5TZWFkcmFnb24gKHBhbm5pbmcpLlxuICAgKi9cbiAgQEhvc3RMaXN0ZW5lcignd2luZG93OmtleWRvd24nLCBbJyRldmVudCddKVxuICBvbktleWRvd24oZTogS2V5Ym9hcmRFdmVudCk6IHZvaWQge1xuICAgIGlmIChlLmtleSAhPT0gJ0Fycm93TGVmdCcgJiYgZS5rZXkgIT09ICdBcnJvd1JpZ2h0JykgcmV0dXJuO1xuICAgIGlmICghdGhpcy5pbWFnZUluZm8/LmlzU3RhY2sgfHwgIXRoaXMuaXNJbWFnZVZpZXcpIHJldHVybjtcbiAgICBjb25zdCB0ID0gZS50YXJnZXQgYXMgSFRNTEVsZW1lbnQgfCBudWxsO1xuICAgIGlmICh0ICYmICgvXihJTlBVVHxURVhUQVJFQXxTRUxFQ1QpJC8udGVzdCh0LnRhZ05hbWUpIHx8IHQuaXNDb250ZW50RWRpdGFibGUpKSByZXR1cm47XG4gICAgLy8gVGhlIHNsaWNlIHNsaWRlciBoYW5kbGVzIGFycm93cyBuYXRpdmVseSB3aGVuIGZvY3VzZWQgKGFsc28gYSArMSBzdGVwKSDigJRcbiAgICAvLyBza2lwIGhlcmUgc28gd2UgZG9uJ3QgZG91YmxlLXN0ZXAgaXQuXG4gICAgaWYgKHQgJiYgKHQuZ2V0QXR0cmlidXRlKCdyb2xlJykgPT09ICdzbGlkZXInIHx8IHQuY2xvc2VzdCgnLnAtc2xpZGVyJykpKSByZXR1cm47XG4gICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIHRoaXMuc3RlcFNsaWNlKGUua2V5ID09PSAnQXJyb3dSaWdodCcgPyAxIDogLTEpO1xuICB9XG5cbiAgLyoqIE1vdmUgdGhlIGRpc3BsYXllZCBzbGljZSBieSBgZGVsdGFgLCBjbGFtcGVkIHRvIHRoZSBzdGFjayBib3VuZHMuICovXG4gIHN0ZXBTbGljZShkZWx0YTogbnVtYmVyKTogdm9pZCB7XG4gICAgY29uc3QgbmV4dCA9IE1hdGgubWluKHRoaXMubWF4SW5kZXgsIE1hdGgubWF4KDAsIHRoaXMuekluZGV4ICsgZGVsdGEpKTtcbiAgICBpZiAobmV4dCA9PT0gdGhpcy56SW5kZXgpIHJldHVybjtcbiAgICB0aGlzLm9uWlNsaWRlKG5leHQpO1xuICB9XG5cbiAgcmVsb2FkQW5kUGxvdCgpIHtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyh0cnVlKTtcbiAgICAvLyBSZS1kcml2ZSB0aGUgcmVuZGVyIHBpcGVsaW5lIGZyb20gdGhlIGN1cnJlbnQgaW1hZ2UgaW5mbyAodGhlIHNvdXJjZSBvZlxuICAgIC8vIHRydXRoLCB3aXRoIHJlYWwgdXJscy9maWxlTmFtZSkuIERvbid0IHVzZSBwbG90U2VydmljZS5yZWxvYWRBbmRQbG90KCk6XG4gICAgLy8gdGhhdCByZWJ1aWxkcyB0aGUgaW5mbyBmcm9tIHRoZSByZW5kZXJlcidzIGludGVybmFsIHN0YXRlLCB3aGljaCBjYW4gYmVcbiAgICAvLyBzdGFsZSAoZS5nLiBhZnRlciBzd2l0Y2hpbmcgcGxvdCB0eXBlIEltYWdlIC0+IEhlYXRtYXApIGFuZCByZS1lbWl0XG4gICAgLy8gZW1wdHkvb2xkIHVybHMsIGZhaWxpbmcgdG8gaGFuZCB0aGUgZGl2IG92ZXIgdG8gdGhlIG5ldyByZW5kZXJlci5cbiAgICBpZiAodGhpcy5pbWFnZUluZm8pIHtcbiAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKHRoaXMuaW1hZ2VJbmZvKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuc3BhdGlhbERhdGFzZXQgJiYgKGlzU3BhdGlhbE9taWNzKHRoaXMucGxvdFR5cGUpIHx8IGlzU3BhdGlhbE9taWNzM2QodGhpcy5wbG90VHlwZSkpKSB7XG4gICAgICAvLyBBIHNwYXRpYWwgZGF0YXNldCB0aGF0IGJyaW5ncyBubyBpbWFnZSwgb3BlbmVkIHdpdGggbm8gaW1hZ2UgbG9hZGVkIChhIGhvc3Qnc1xuICAgICAgLy8gZmlyc3Qgdmlldyk6IHRoZXJlIGlzIG5vIGltYWdlIGluZm8gdG8gcmUtZHJpdmUsIHNvIGRyYXcgdGhlIHNwYXRpYWwgbW9kZSBpdHNlbGYuXG4gICAgICB2b2lkIHRoaXMucGxvdFNwYXRpYWxXaXRob3V0SW1hZ2UodGhpcy5zcGF0aWFsRGF0YXNldCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVsb2FkQW5kUGxvdCgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEcmF3IGEgc3BhdGlhbCBtb2RlIHdpdGggbm8gaW1hZ2UgYmVoaW5kIGl0OiB0aGUgb2JzZXJ2YXRpb25zIGFsb25lLCBmcmFtZWQgb24gdGhlaXIgb3duXG4gICAqIGV4dGVudCwgYXMgdGhlIHNlcUZJU0ggZXhhbXBsZSBzaG93cyB0aGVtLiBUaGUgaW1hZ2UgaW5mbyBpcyBhIHBsYWNlaG9sZGVyIG5hbWluZyB0aGVcbiAgICogZGF0YXNldCwgc28gcmVnaW9ucyBkcmF3biBoZXJlIGFyZSBrZXB0IHBlciBkYXRhc2V0IGxpa2UgYW55IGltYWdlJ3MuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHBsb3RTcGF0aWFsV2l0aG91dEltYWdlKGRhdGFzZXQ6IFNwYXRpYWxEYXRhc2V0KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0aGlzLnZpZXdSZWFkeSkge1xuICAgICAgLy8gQSBob3N0IHRoYXQgY3JlYXRlcyB0aGUgdmlzdWFsaXplciBBRlRFUiBwdWJsaXNoaW5nIHRoZSBkYXRhc2V0IChqaXQtdWkgb3BlbmluZyBhIGZpbGVcbiAgICAgIC8vIGZyb20gaXRzIHRyZWUpIGhhcyBpdCByZXBsYXllZCBpbnRvIG5nT25Jbml0LCBiZWZvcmUgdGhlIHRlbXBsYXRlIGFuZCBpdHMgcGxvdCBkaXZcbiAgICAgIC8vIGV4aXN0OyBkcmF3aW5nIG5vdyBmaW5kcyBubyB0YXJnZXQuIERyYXcgb25jZSB0aGUgdmlldyBpcyByZWFkeSAob25WaWV3UmVhZHkpLlxuICAgICAgdGhpcy5wZW5kaW5nU3BhdGlhbERyYXcgPSBkYXRhc2V0O1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBUaGUgc2FtZSBnZW5lcmF0aW9uIGFzIHRoZSBpbWFnZSBwaXBlbGluZTogYSBuZXdlciBpbWFnZSBvciBkYXRhc2V0IHN1cGVyc2VkZXMgdGhpc1xuICAgIC8vIGRyYXcsIGFuZCBhIHN1cGVyc2VkZWQgZHJhdyBuZWl0aGVyIHJlcG9ydHMgbm9yIHJlbGVhc2VzIHRoZSBuZXdlciBvbmUncyBsb2FkaW5nIHN0YXRlLlxuICAgIGNvbnN0IHRva2VuID0gKyt0aGlzLnJlbmRlclRva2VuO1xuICAgIGNvbnN0IGRpdiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdk5hbWUpO1xuICAgIGNvbnN0IGluZm86IElJbWFnZUluZm8gPSB7XG4gICAgICBpc0dyYXlzY2FsZTogZmFsc2UsIHRydWVJbWFnZVNpemU6IFswLCAwXSwgdXJsczogW10sIGlzU3RhY2s6IGZhbHNlLCBzaG93U3RhY2s6IGZhbHNlLFxuICAgICAgc2NhbGVSYXRpbzogdHJ1ZSwgZmlsZU5hbWU6IGBzcGF0aWFsOiR7ZGF0YXNldC5pZH1gLCBpbWFnZU1ldGE6IFtdLFxuICAgIH07XG4gICAgbGV0IGZhaWx1cmU6IHVua25vd24gPSBudWxsO1xuICAgIHRyeSB7XG4gICAgICAvLyBBIGJhY2tlbmQgdGhhdCBjYW5ub3QgZHJhdyAobm8gV2ViR1BVLCBubyBwbG90IHRhcmdldCkgcmVzb2x2ZXMgZmFsc2UgcmF0aGVyIHRoYW4gdGhyb3dpbmcuXG4gICAgICBpZiAoIShhd2FpdCB0aGlzLnBsb3RTZXJ2aWNlLnBsb3QodGhpcy5wbG90RGl2TmFtZSwgbnVsbCwgaW5mbywgZGl2Py5vZmZzZXRIZWlnaHQgfHwgNTAwLCB0aGlzLnBsb3RUeXBlKSkpIHtcbiAgICAgICAgZmFpbHVyZSA9ICd0aGUgcmVuZGVyZXIgY291bGQgbm90IHN0YXJ0JztcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGZhaWx1cmUgPSBlcnI7XG4gICAgfVxuICAgIGlmICh0b2tlbiAhPT0gdGhpcy5yZW5kZXJUb2tlbikgcmV0dXJuO1xuICAgIGlmIChmYWlsdXJlKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ1t2aXN1YWxpemVyXSBjb3VsZCBub3QgZHJhdyB0aGUgc3BhdGlhbCBkYXRhc2V0JywgZmFpbHVyZSk7XG4gICAgICBjb25zdCBlID0gZmFpbHVyZSBhcyB7IG1lc3NhZ2U/OiBzdHJpbmcgfTtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiB0aGlzLnZpekFsZXJ0VG9hc3RLZXksXG4gICAgICAgIHNldmVyaXR5OiAnZXJyb3InLFxuICAgICAgICBzdW1tYXJ5OiAnQ291bGQgbm90IGRyYXcgdGhlIGRhdGFzZXQnLFxuICAgICAgICAvLyBBbiBpbWFnZS1sZXNzIGRhdGFzZXQgaGFzIG5vIHRpc3N1ZSBpbWFnZSB0byBmYWxsIGJhY2sgb24sIHNvIHNheSBzbyByYXRoZXIgdGhhblxuICAgICAgICAvLyBsZWF2ZSBhbiBlbXB0eSBjYW52YXMgdGhhdCBsb29rcyBmaW5pc2hlZC5cbiAgICAgICAgZGV0YWlsOiBgJHtkYXRhc2V0Lm5hbWUgPz8gZGF0YXNldC5pZH06ICR7ZT8ubWVzc2FnZSA/PyBTdHJpbmcoZmFpbHVyZSl9LmAsXG4gICAgICB9KTtcbiAgICB9XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgfVxuXG4gIGNhbmNlbExvYWRpbmcoKSB7XG4gICAgLy8gU3RvcCBhbnkgaW4tZmxpZ2h0IGZyYW1lIHN0cmVhbWluZyAobmFwYXJpLWpzIHZvbHVtZS9zdXJmYWNlIHByZWxvYWQpIHNvIHRoZSBmZXRjaCBsb29wc1xuICAgIC8vIGFjdHVhbGx5IGFib3J0IOKAlCBjbGVhcmluZyB0aGUgZmxhZyBhbG9uZSBvbmx5IHJvdXRlZCB0byBQbG90bHkgYW5kIGxlZnQgbmFwYXJpIGZldGNoaW5nLlxuICAgIHRoaXMucGxvdFNlcnZpY2UuY2FuY2VsTG9hZGluZz8uKCk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmcgPSBmYWxzZTtcbiAgICB0aGlzLmltZ0xvYWRpbmcgPSBmYWxzZTtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyhmYWxzZSk7XG4gICAgdGhpcy56SW5kZXggPSAwO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0WkluZGV4KHRoaXMuekluZGV4KTtcbiAgICB0aGlzLnJ1bm5pbmcgPSBmYWxzZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFN0YWNrTG9hZGluZyhmYWxzZSk7XG4gIH1cblxuICBkb3dubG9hZEltYWdlKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZG93bmxvYWRJbWFnZSgpO1xuICB9XG5cbiAgYXV0b3NjYWxlSW1hZ2UoKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5hdXRvc2NhbGUoKTtcbiAgfVxuXG4gIC8qKiBUb29sYmFyIHBpeGVsL3Ntb290aCB0b2dnbGU6IGZsaXAgc21vb3RoaW5nIGFuZCBhcHBseSB0byB0aGUgYWN0aXZlIGJhY2tlbmQuICovXG4gIG9uVG9nZ2xlSW1hZ2VTbW9vdGhpbmcoKTogdm9pZCB7XG4gICAgdGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQgPSAhdGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQ7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRJbWFnZVNtb290aGluZ0VuYWJsZWQodGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQpO1xuICB9XG5cbiAgcmVzZXRBeGVzKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UucmVzZXRBeGVzKCk7XG4gIH1cblxuICB0b2dnbGVEcmFnTW9kZShtb2RlOiBzdHJpbmcpIHtcbiAgICAvLyBBIHRvb2wgcGlja2VkIGJ5IHRoZSB1c2VyIGlzIGFsd2F5cyB0aGUgcGxhaW4gdG9vbCwgbmV2ZXIgYSBwbG90IG1vZGUnc1xuICAgIC8vIGJydXNoIGNsYXNzLlxuICAgIHRoaXMuYnJ1c2hDbGFzcyA9IG51bGw7XG4gICAgLy8gVG9nZ2xlIG9mZiBpZiB0aGUgc2FtZSBtb2RlIGlzIHJlLXNlbGVjdGVkLlxuICAgIHRoaXMuYXBwbHlEcmFnTW9kZSh0aGlzLmFjdGl2ZURyYWdNb2RlID09PSBtb2RlID8gbnVsbCA6IG1vZGUpO1xuICB9XG5cbiAgLyoqIEFybSBgbW9kZWAgKG9yIG5vdGhpbmcpIGFjcm9zcyBldmVyeSB0b29sLCBrZWVwaW5nIHRoZSB0b29sYmFyIGluIHN0ZXAuICovXG4gIHByaXZhdGUgYXBwbHlEcmFnTW9kZShtb2RlOiBzdHJpbmcgfCBudWxsKSB7XG4gICAgdGhpcy5hY3RpdmVEcmFnTW9kZSA9IG1vZGU7XG4gICAgY29uc3QgYWN0aXZlID0gdGhpcy5hY3RpdmVEcmFnTW9kZTtcbiAgICAvLyBSZWNvcmQgdGhlIGFybWVkIHRvb2wgaW4gdGhlIHNoYXJlZCBzZXNzaW9uIHN0b3JlLlxuICAgIHRoaXMuc2Vzc2lvbi5zZXRBY3RpdmVUb29sKGFjdGl2ZSk7XG5cbiAgICAvLyBSZWdpb24gZHJhdy9zZWxlY3QgcnVuIHRocm91Z2ggdGhlIHJlbmRlcmVyJ3MgcmVnaW9uIG92ZXJsYXkuXG4gICAgdGhpcy5wbG90U2VydmljZVxuICAgICAgLmdldFJlZ2lvbk92ZXJsYXkoKVxuICAgICAgPy5zZXRNb2RlKHRoaXMuaXNSZWdpb25Nb2RlKGFjdGl2ZSkgPyAoYWN0aXZlIGFzIFJlZ2lvblRvb2xNb2RlKSA6ICdub25lJyk7XG5cbiAgICAvLyBWaWV3cG9ydCBkcmFnIG1vZGVzIChwYW4vYm94LXpvb20pLiBSZWdpb24gbW9kZXMgb3duIHRoZSBkcmFnIG1vZGUgdmlhXG4gICAgLy8gdGhlIG92ZXJsYXksIHNvIGRvbid0IGFsc28gc2V0IG9uZSBoZXJlLlxuICAgIGlmICghdGhpcy5pc1JlZ2lvbk1vZGUoYWN0aXZlKSkge1xuICAgICAgY29uc3Qgdmlld3BvcnREcmFnID0gYWN0aXZlID09PSAncGFuJyB8fCBhY3RpdmUgPT09ICd6b29tJztcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0RHJhZ01vZGUodmlld3BvcnREcmFnID8gYWN0aXZlIDogZmFsc2UpO1xuICAgIH1cblxuICAgIC8vIE9uLWNhbnZhcyB0b29sIG92ZXJsYXlzLlxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0Wm9vbVRvQm94TW9kZShhY3RpdmUgPT09ICd6b29tVG9Cb3gnKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFdhbmRNb2RlKGFjdGl2ZSA9PT0gJ3dhbmQnLCB7IHNlbnNpdGl2aXR5OiB0aGlzLndhbmRTZW5zaXRpdml0eSB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldEJydXNoTW9kZShhY3RpdmUgPT09ICdicnVzaCcsIHsgc2l6ZTogdGhpcy5icnVzaFNpemUsIC4uLnRoaXMuYnJ1c2hDbGFzcyB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNhbVBvaW50TW9kZShhY3RpdmUgPT09ICdzYW1Qb2ludCcpO1xuICAgIC8vIExlYXZpbmcgcG9pbnQgbW9kZSBkaXNtaXNzZXMgYW55IGxpbmdlcmluZyBzdGF0dXMgdG9hc3QuXG4gICAgaWYgKGFjdGl2ZSAhPT0gJ3NhbVBvaW50JykgdGhpcy5oaWRlU2FtVG9hc3QoKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFZlcnRleEVyYXNlck1vZGUoYWN0aXZlID09PSAnZXJhc2VWZXJ0ZXgnKTtcbiAgICBpZiAoYWN0aXZlID09PSAnZXJhc2VWZXJ0ZXgnKSB7XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFZlcnRleEVyYXNlclJhZGl1cyh0aGlzLnZlcnRleEVyYXNlclJhZGl1cyk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFJlZ2lvbiBkcmF3L3NlbGVjdC9lZGl0IG1vZGVzIHJvdXRlZCB0aHJvdWdoIHRoZSByZWdpb24gb3ZlcmxheS4gVGhlXG4gICAqICBUaGUgdmVydGV4IHRvb2xzIChkcmF3cG9seWdvbi9hZGRwb2ludC9kZWxldGVwb2ludC9tb3ZlKSBhcmVcbiAgICogIGhhbmRsZWQgYnkgdGhlIE9wZW5TZWFkcmFnb24gb3ZlcmxheTsgUGxvdGx5J3Mgb3ZlcmxheSBtYXBzIHRoZW0gdG8gbm8tb3AuICovXG4gIHByaXZhdGUgaXNSZWdpb25Nb2RlKG1vZGU6IHN0cmluZyB8IG51bGwpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgbW9kZSA9PT0gJ2RyYXdyZWN0JyB8fFxuICAgICAgbW9kZSA9PT0gJ2RyYXdjbG9zZWRwYXRoJyB8fFxuICAgICAgbW9kZSA9PT0gJ2RyYXdvcGVucGF0aCcgfHxcbiAgICAgIG1vZGUgPT09ICdzZWxlY3QnIHx8XG4gICAgICBtb2RlID09PSAnZHJhd3BvbHlnb24nIHx8XG4gICAgICBtb2RlID09PSAnYWRkcG9pbnQnIHx8XG4gICAgICBtb2RlID09PSAnZGVsZXRlcG9pbnQnIHx8XG4gICAgICBtb2RlID09PSAnbW92ZSdcbiAgICApO1xuICB9XG5cbiAgLyoqIExpdmUtdXBkYXRlIHRoZSBpc29zdXJmYWNlIGFzIHRoZSByYW5nZSBzbGlkZXIgbW92ZXMuIFRoZSBjb250cm9sIGlzIG9ubHlcbiAgICogIGF2YWlsYWJsZSB3aGVuIHRoZSBhY3RpdmUgYmFja2VuZCByZW5kZXJzIGlzb3N1cmZhY2VzIChJU09TVVJGQUNFIG1vZGUpLiAqL1xuICBvbklzb1JhbmdlQ2hhbmdlKHZhbHVlczogbnVtYmVyW10gfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoIXZhbHVlcyB8fCB2YWx1ZXMubGVuZ3RoIDwgMikgcmV0dXJuO1xuICAgIHRoaXMuaXNvUmFuZ2UgPSB2YWx1ZXM7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRJc29zdXJmYWNlQ29udHJvbHMoKT8uc2V0SXNvUmFuZ2UodmFsdWVzWzBdLCB2YWx1ZXNbMV0pO1xuICB9XG5cbiAgb25XYW5kU2Vuc2l0aXZpdHlDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8ICFOdW1iZXIuaXNGaW5pdGUodmFsdWUpKSByZXR1cm47XG4gICAgdGhpcy53YW5kU2Vuc2l0aXZpdHkgPSB2YWx1ZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFdhbmRPcHRpb25zKHsgc2Vuc2l0aXZpdHk6IHZhbHVlIH0pO1xuICB9XG5cbiAgb25CcnVzaFNpemVDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8ICFOdW1iZXIuaXNGaW5pdGUodmFsdWUpKSByZXR1cm47XG4gICAgdGhpcy5icnVzaFNpemUgPSB2YWx1ZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldEJydXNoT3B0aW9ucyh7IHNpemU6IHZhbHVlIH0pO1xuICB9XG5cbiAgb25WZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8ICFOdW1iZXIuaXNGaW5pdGUodmFsdWUpKSByZXR1cm47XG4gICAgdGhpcy52ZXJ0ZXhFcmFzZXJSYWRpdXMgPSB2YWx1ZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFZlcnRleEVyYXNlclJhZGl1cyh2YWx1ZSk7XG4gIH1cblxuICB6b29tSW4oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS56b29tSW4oKTtcbiAgfVxuXG4gIHpvb21PdXQoKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS56b29tT3V0KCk7XG4gIH1cblxuICBkZWxldGVSZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5kZWxldGVBY3RpdmVTaGFwZSgpO1xuICB9XG5cbiAgLyoqIFVuZG8gdGhlIG1vc3QgcmVjZW50IHJlZ2lvbiBhY3Rpb24gKGppdC11aSM4NSkuIFVwIHRvIDEwIHN0ZXBzIGJhY2suICovXG4gIHVuZG9SZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS51bmRvKCk7XG4gIH1cblxuICAvKiogUmVkbyB0aGUgbW9zdCByZWNlbnRseSB1bmRvbmUgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gKi9cbiAgcmVkb1JlZ2lvbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlZG8oKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBSZWdpb24gc2V0LW9wZXJhdGlvbnMgb24gdGhlIGN1cnJlbnQgc2VsZWN0aW9uIChqaXQtdWkjODUpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBUaGUgY3VycmVudGx5LXNlbGVjdGVkIHJlZ2lvbnMgKGxpdmUgc3RvcmUgaW5zdGFuY2VzKS4gKi9cbiAgcHJpdmF0ZSBnZXQgc2VsZWN0ZWRSZWdpb25zKCk6IFJlZ2lvbltdIHtcbiAgICBjb25zdCBhbGwgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKTtcbiAgICByZXR1cm4gdGhpcy5zZWxlY3RlZEluZGljZXNcbiAgICAgIC5tYXAoKGkpID0+IGFsbFtpXSlcbiAgICAgIC5maWx0ZXIoKHIpOiByIGlzIFJlZ2lvbiA9PiAhIXIpO1xuICB9XG5cbiAgLyoqIFJlZ2lvbnMgZWxpZ2libGUgZm9yIHNldC1vcHM6IGNsb3NlZCBhcmVhcyAocmVjdCAvIGNsb3NlZCBwb2x5Z29uIC9cbiAgICogIG11bHRpLXBvbHlnb24pLCBleGNsdWRpbmcgaW50ZW5zaXR5LXByb2ZpbGUgbGluZXMuICovXG4gIHByaXZhdGUgb3BFbGlnaWJsZShyZWdpb25zOiBSZWdpb25bXSk6IFJlZ2lvbltdIHtcbiAgICByZXR1cm4gcmVnaW9ucy5maWx0ZXIoKHIpID0+IHIua2luZCAhPT0gJ3Byb2ZpbGUnICYmIChcbiAgICAgIHIuYm91bmRzIGluc3RhbmNlb2YgUmVjdGFuZ2xlIHx8XG4gICAgICByLmJvdW5kcyBpbnN0YW5jZW9mIE11bHRpUG9seWdvbiB8fFxuICAgICAgKHIuYm91bmRzIGluc3RhbmNlb2YgUG9seWdvbiAmJiByLmJvdW5kcy5jbG9zZWQgIT09IGZhbHNlKVxuICAgICkpO1xuICB9XG5cbiAgLyoqIEltYWdlIHBpeGVsIGRpbWVuc2lvbnMgZm9yIHRoZSByYXN0ZXIgb3BzOyBmYWxscyBiYWNrIHRvIHRoZSBzZWxlY3Rpb24nc1xuICAgKiAgZXh0ZW50IHdoZW4gdGhlIGltYWdlIHNpemUgaXNuJ3Qga25vd24gKGtlZXBzIGNsYW1waW5nIHNhbmUpLiAqL1xuICBwcml2YXRlIG9wSW1hZ2VEaW1zKHJlZ2lvbnM6IFJlZ2lvbltdKTogeyB3OiBudW1iZXI7IGg6IG51bWJlciB9IHtcbiAgICBsZXQgW3csIGhdID0gdGhpcy5pbWFnZUluZm8/LnRydWVJbWFnZVNpemUgPz8gWzAsIDBdO1xuICAgIGlmICghKHcgPiAwKSB8fCAhKGggPiAwKSkge1xuICAgICAgbGV0IG1heFggPSAwLCBtYXhZID0gMDtcbiAgICAgIGNvbnN0IHNjYW4gPSAoeHM6IG51bWJlcltdLCB5czogbnVtYmVyW10pID0+IHtcbiAgICAgICAgZm9yIChjb25zdCB4IG9mIHhzKSBtYXhYID0gTWF0aC5tYXgobWF4WCwgeCk7XG4gICAgICAgIGZvciAoY29uc3QgeSBvZiB5cykgbWF4WSA9IE1hdGgubWF4KG1heFksIHkpO1xuICAgICAgfTtcbiAgICAgIGZvciAoY29uc3QgciBvZiByZWdpb25zKSB7XG4gICAgICAgIGNvbnN0IGIgPSByLmJvdW5kcztcbiAgICAgICAgaWYgKGIgaW5zdGFuY2VvZiBSZWN0YW5nbGUpIHNjYW4oW2IueCArIGIud2lkdGhdLCBbYi55ICsgYi5oZWlnaHRdKTtcbiAgICAgICAgZWxzZSBpZiAoYiBpbnN0YW5jZW9mIFBvbHlnb24pIHNjYW4oYi54cG9pbnRzLCBiLnlwb2ludHMpO1xuICAgICAgICBlbHNlIGlmIChiIGluc3RhbmNlb2YgTXVsdGlQb2x5Z29uKSBmb3IgKGNvbnN0IHAgb2YgYi5wb2x5Z29ucykgc2NhbihwLnhwb2ludHMsIHAueXBvaW50cyk7XG4gICAgICB9XG4gICAgICB3ID0gTWF0aC5tYXgodywgTWF0aC5jZWlsKG1heFgpICsgMik7XG4gICAgICBoID0gTWF0aC5tYXgoaCwgTWF0aC5jZWlsKG1heFkpICsgMik7XG4gICAgfVxuICAgIHJldHVybiB7IHcsIGggfTtcbiAgfVxuXG4gIC8qKiBUcnVlIHdoZW4g4omlMiBlbGlnaWJsZSByZWdpb25zIGFyZSBzZWxlY3RlZCAoTWVyZ2UgYXZhaWxhYmxlKS4gKi9cbiAgZ2V0IGNhbk1lcmdlUmVnaW9ucygpOiBib29sZWFuIHsgcmV0dXJuIHRoaXMub3BFbGlnaWJsZSh0aGlzLnNlbGVjdGVkUmVnaW9ucykubGVuZ3RoID49IDI7IH1cbiAgLyoqIFRydWUgd2hlbiBhbnkgc2VsZWN0ZWQgcmVnaW9uIGlzIG11bHRpLXBhcnQgKFVuZ3JvdXAgYXZhaWxhYmxlKS4gKi9cbiAgZ2V0IGNhblVuZ3JvdXBSZWdpb25zKCk6IGJvb2xlYW4geyByZXR1cm4gdGhpcy5zZWxlY3RlZFJlZ2lvbnMuc29tZSgocikgPT4gdGhpcy5yZWdpb25PcHMuY2FuVW5ncm91cChyKSk7IH1cbiAgLyoqIFRydWUgd2hlbiDiiaUxIGVsaWdpYmxlIHJlZ2lvbiBpcyBzZWxlY3RlZCAoSW52ZXJzZSAvIFNpbXBsaWZ5IGF2YWlsYWJsZSkuICovXG4gIGdldCBoYXNFbGlnaWJsZVNlbGVjdGlvbigpOiBib29sZWFuIHsgcmV0dXJuIHRoaXMub3BFbGlnaWJsZSh0aGlzLnNlbGVjdGVkUmVnaW9ucykubGVuZ3RoID49IDE7IH1cblxuICAvKiogU2VsZWN0IGV2ZXJ5IHJlZ2lvbiBvbiB0aGUgaW1hZ2UgKGV4Y2x1ZGVzIGludGVuc2l0eS1wcm9maWxlIGxpbmVzKS4gKi9cbiAgc2VsZWN0QWxsUmVnaW9ucygpOiB2b2lkIHtcbiAgICBjb25zdCBhbGwgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKTtcbiAgICBjb25zdCBpbmRpY2VzOiBudW1iZXJbXSA9IFtdO1xuICAgIGFsbC5mb3JFYWNoKChyLCBpKSA9PiB7IGlmIChyLmtpbmQgIT09ICdwcm9maWxlJykgaW5kaWNlcy5wdXNoKGkpOyB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNlbGVjdGVkU2hhcGVJbmRpY2VzKGluZGljZXMpO1xuICB9XG5cbiAgLyoqIE1lcmdlIHRoZSBzZWxlY3RlZCByZWdpb25zIGludG8gYSBzaW5nbGUgKHBvc3NpYmx5IG11bHRpLXBhcnQpIHJlZ2lvbi4gKi9cbiAgbWVyZ2VSZWdpb25zKCk6IHZvaWQge1xuICAgIGNvbnN0IHNlbCA9IHRoaXMub3BFbGlnaWJsZSh0aGlzLnNlbGVjdGVkUmVnaW9ucyk7XG4gICAgaWYgKHNlbC5sZW5ndGggPCAyKSByZXR1cm47XG4gICAgY29uc3QgeyB3LCBoIH0gPSB0aGlzLm9wSW1hZ2VEaW1zKHNlbCk7XG4gICAgY29uc3QgbWVyZ2VkID0gdGhpcy5yZWdpb25PcHMubWVyZ2Uoc2VsLCB3LCBoKTtcbiAgICBpZiAobWVyZ2VkKSB0aGlzLnJlcGxhY2VSZWdpb25zKHNlbCwgW21lcmdlZF0pO1xuICB9XG5cbiAgLyoqIFNwbGl0IGVhY2ggc2VsZWN0ZWQgbXVsdGktcGFydCByZWdpb24gaW50byBvbmUgcmVnaW9uIHBlciBwYXJ0LiAqL1xuICB1bmdyb3VwUmVnaW9ucygpOiB2b2lkIHtcbiAgICBjb25zdCBzZWwgPSB0aGlzLnNlbGVjdGVkUmVnaW9ucy5maWx0ZXIoKHIpID0+IHRoaXMucmVnaW9uT3BzLmNhblVuZ3JvdXAocikpO1xuICAgIGlmIChzZWwubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgY29uc3QgcmVzdWx0czogUmVnaW9uW10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IHIgb2Ygc2VsKSByZXN1bHRzLnB1c2goLi4udGhpcy5yZWdpb25PcHMudW5ncm91cChyKSk7XG4gICAgdGhpcy5yZXBsYWNlUmVnaW9ucyhzZWwsIHJlc3VsdHMpO1xuICB9XG5cbiAgLyoqIFJlcGxhY2UgdGhlIHNlbGVjdGlvbiB3aXRoIGl0cyBpbnZlcnNlIGluc2lkZSB0aGUgaW1hZ2UgcmVjdGFuZ2xlLiAqL1xuICBpbnZlcnNlUmVnaW9ucygpOiB2b2lkIHtcbiAgICBjb25zdCBzZWwgPSB0aGlzLm9wRWxpZ2libGUodGhpcy5zZWxlY3RlZFJlZ2lvbnMpO1xuICAgIGlmIChzZWwubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgY29uc3QgeyB3LCBoIH0gPSB0aGlzLm9wSW1hZ2VEaW1zKHNlbCk7XG4gICAgY29uc3QgaW52ID0gdGhpcy5yZWdpb25PcHMuaW52ZXJzZShzZWwsIHcsIGgpO1xuICAgIGlmICghaW52KSB7XG4gICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgIGtleTogdGhpcy5yZXN1bHRUb2FzdEtleSxcbiAgICAgICAgc2V2ZXJpdHk6ICd3YXJuJywgc3VtbWFyeTogJ0ludmVyc2UnLFxuICAgICAgICBkZXRhaWw6ICdOb3RoaW5nIHRvIGludmVydCDigJQgc2VsZWN0IG9uZSBvciBtb3JlIGNsb3NlZCByZWdpb25zIGZpcnN0LicsXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5yZXBsYWNlUmVnaW9ucyhzZWwsIFtpbnZdKTtcbiAgfVxuXG4gIC8qKiBEb3VnbGFz4oCTUGV1Y2tlciBzaW1wbGlmeSBlYWNoIHNlbGVjdGVkIHJlZ2lvbiBieSBgdGhyZXNob2xkUHhgLiAqL1xuICBzaW1wbGlmeVJlZ2lvbnModGhyZXNob2xkUHg6IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IHNlbCA9IHRoaXMub3BFbGlnaWJsZSh0aGlzLnNlbGVjdGVkUmVnaW9ucyk7XG4gICAgaWYgKHNlbC5sZW5ndGggPT09IDApIHJldHVybjtcbiAgICB0aGlzLnJlcGxhY2VSZWdpb25zKHNlbCwgc2VsLm1hcCgocikgPT4gdGhpcy5yZWdpb25PcHMuc2ltcGxpZnkociwgdGhyZXNob2xkUHgpKSk7XG4gICAgdGhpcy5kaXNwbGF5U2ltcGxpZnlEaWFsb2cgPSBmYWxzZTtcbiAgfVxuXG4gIC8qKiBPcGVuIHRoZSBjdXN0b20tdGhyZXNob2xkIHNpbXBsaWZ5IGRpYWxvZy4gKi9cbiAgb3BlblNpbXBsaWZ5RGlhbG9nKCk6IHZvaWQgeyB0aGlzLmRpc3BsYXlTaW1wbGlmeURpYWxvZyA9IHRydWU7IH1cblxuICAvKipcbiAgICogQ29tbWl0IGEgc2V0LW9wOiBkcm9wIHRoZSBgcmVtb3ZlYCByZWdpb25zLCBhcHBlbmQgYGFkZGAsIGFuZCBzZWxlY3QgdGhlXG4gICAqIHJlc3VsdHMuIEdvZXMgdGhyb3VnaCBzZXRSZWdpb25zIHNvIGl0J3MgdW5kby10cmFja2VkIGFuZCByZS1yZW5kZXJlZDtcbiAgICogcHJvZmlsZSBsaW5lcyBhbmQgdW5zZWxlY3RlZCByZWdpb25zIGFyZSBwcmVzZXJ2ZWQuXG4gICAqL1xuICBwcml2YXRlIHJlcGxhY2VSZWdpb25zKHJlbW92ZTogUmVnaW9uW10sIGFkZDogUmVnaW9uW10pOiB2b2lkIHtcbiAgICBpZiAoYWRkLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuICAgIGNvbnN0IHJlbW92ZVNldCA9IG5ldyBTZXQocmVtb3ZlKTtcbiAgICBjb25zdCBrZXB0ID0gdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25zKCkuZmlsdGVyKChyKSA9PiAhcmVtb3ZlU2V0LmhhcyhyKSk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRSZWdpb25zKFsuLi5rZXB0LCAuLi5hZGRdKTsgLy8gbWludHMgaWRzIG9uIGBhZGRgLCByZWNvcmRzIHVuZG9cbiAgICBjb25zdCBzdG9yZWQgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKTtcbiAgICBjb25zdCBzZWwgPSBhZGQubWFwKChyKSA9PiBzdG9yZWQuaW5kZXhPZihyKSkuZmlsdGVyKChpKSA9PiBpID49IDApO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U2VsZWN0ZWRTaGFwZUluZGljZXMoc2VsKTtcbiAgfVxuXG4gIC8qKiBCb3gtcHJvbXB0ZWQgU0FNIHNlZ21lbnRhdGlvbiBvZiB0aGUgZHJhd24gcmVjdGFuZ2xlcyAoaml0LXVpIzkwKS4gQSBzdGlja3lcbiAgICogIGBzYW1gIHRvYXN0IHNob3dzIGxpdmUgc3RhdHVzICsgYSBkb3dubG9hZCBwcm9ncmVzcyBiYXIgKGZpcnN0IHJ1biBwdWxscyB0aGVcbiAgICogIGVuY29kZXIsIH4xNzAgTUIpOyBpdCBzdGF5cyBvcGVuIHVudGlsIHRoZSBydW4gZmluaXNoZXMgKGJhciBoaXRzIDEwMCUpLiAqL1xuICBhc3luYyBzZWdtZW50UmVnaW9ucygpIHtcbiAgICBhd2FpdCB0aGlzLnJ1blNlZ21lbnRXaXRoVG9hc3QoJ1NBTScsIHRoaXMuc2FtVG9vbCwgKCkgPT4gdGhpcy5wbG90U2VydmljZS5zZWdtZW50UmVjdGFuZ2xlcygpKTtcbiAgfVxuXG4gIC8qKiBBdXRvLXNlZ21lbnQgY2VsbHMgaW5zaWRlIGVhY2ggZHJhd24gcmVjdGFuZ2xlIHdpdGggY2VsbHBvc2UtU0FNLCBjbGllbnQtc2lkZVxuICAgKiAgKGppdC11aSM5MCkuIEVhY2ggYm94IGlzIGNyb3BwZWQgKGJyb3dzZXIgc2xpZGUtY3JvcCkgdGhlbiBydW4gdGhyb3VnaCB0aGVcbiAgICogIGNlbGxwb3NlLWpzIG1vZGVsOyB0aGUgc2FtZSBzdGlja3kgYHNhbWAgdG9hc3QgKyBwcm9ncmVzcyBiYXIgaXMgcmV1c2VkLiAqL1xuICBhc3luYyBzZWdtZW50Q2VsbHBvc2UoKSB7XG4gICAgYXdhaXQgdGhpcy5ydW5TZWdtZW50V2l0aFRvYXN0KCdDZWxscG9zZScsIHRoaXMuY2VsbFNlZ21lbnRUb29sLCAoKSA9PlxuICAgICAgdGhpcy5wbG90U2VydmljZS5zZWdtZW50UmVjdGFuZ2xlc0NlbGxwb3NlKCksXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQYXJhbWV0ZXJzIGN1cnJlbnRseSBib3VuZCBieSB0aGUgZ2VuZXJpYyBwYXJhbWV0ZXIgZGlhbG9nLCBmb3Igd2hpY2hldmVyXG4gICAqIHRvb2wgaXMgb3Blbi4gU2VlZGVkIGxhemlseTogYSB0b29sJ3MgZGVmYXVsdHMgYXJlIG9ubHkgYXNrZWQgZm9yIHdoZW4gdGhlXG4gICAqIHVzZXIgZmlyc3QgdG91Y2hlcyBpdCwgc28gcmVnaXN0ZXJpbmcgYSB0b29sIGNvc3RzIG5vdGhpbmcgdW50aWwgdXNlZC5cbiAgICovXG4gIHBhcmFtc0Zvcih0b29sSWQ6IHN0cmluZyk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICBjb25zdCB0b29sID0gdGhpcy5jb250cmlidXRlZFRvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IHRvb2xJZCk7XG4gICAgaWYgKCF0b29sKSByZXR1cm4ge307XG4gICAgaWYgKCF0aGlzLnRvb2xQYXJhbXNbdG9vbElkXSkge1xuICAgICAgLy8gVGhyb3VnaCBgc2VlZFBhcmFtc2AsIG5vdCBgZGVmYXVsdFBhcmFtc2AgZGlyZWN0bHk6IHRoZSBhY3RpdmVcbiAgICAgIC8vIGNoZWNrcG9pbnQncyBvd24gYFRvb2xNb2RlbE9wdGlvbi5kZWZhdWx0c2AgaGF2ZSB0byBiZSBtZXJnZWQgb24gdG9wLlxuICAgICAgLy8gQ2FsbGluZyBgZGVmYXVsdFBhcmFtc2AgaGVyZSBieXBhc3NlZCB0aGVtLCBzbyB0aGUgdmVyeSBmaXJzdCBydW4gb2YgYVxuICAgICAgLy8gdG9vbCB1c2VkIHRoZSB0b29sJ3MgYmFzZWxpbmUgdGhyZXNob2xkcyBhbmQgdGlsaW5nIHJhdGhlciB0aGFuIHRoZVxuICAgICAgLy8gbW9kZWwncyDigJQgYW5kIGl0IG9ubHkgY29ycmVjdGVkIGl0c2VsZiBvbmNlIHRoZSB1c2VyIHN3aXRjaGVkXG4gICAgICAvLyBjaGVja3BvaW50cyBvciBoaXQgUmVzZXQuIEV2ZXJ5IGVudHJ5IHBvaW50IG5vdyBnb2VzIHRocm91Z2ggb25lIHBhdGguXG4gICAgICB0aGlzLnRvb2xQYXJhbXNbdG9vbElkXSA9IHRoaXMuc2VlZFBhcmFtcyh0b29sLCB0aGlzLm1vZGVsSWRGb3IodG9vbCkpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy50b29sUGFyYW1zW3Rvb2xJZF0hO1xuICB9XG5cbiAgLyoqIEFjdGl2ZSBjaGVja3BvaW50IGZvciBhIHRvb2wsIGZhbGxpbmcgYmFjayB0byB0aGUgdG9vbCdzIG93biBkZWZhdWx0LiAqL1xuICBtb2RlbElkRm9yKHRvb2w6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy50b29sTW9kZWxJZHNbdG9vbC5pZF0gPz8gdG9vbC5kZWZhdWx0TW9kZWxJZCgpO1xuICB9XG5cbiAgLyoqIFN3aXRjaGluZyBjaGVja3BvaW50IHJlLXNlZWRzIHRoZSBwYXJhbWV0ZXJzLCBzaW5jZSB0aGUgZGVmYXVsdHMgYmVsb25nIHRvXG4gICAqICB0aGUgbW9kZWwgcmF0aGVyIHRoYW4gdG8gdGhlIHRvb2wuICovXG4gIG9uVG9vbE1vZGVsQ2hhbmdlKGU6IHsgdG9vbElkOiBzdHJpbmc7IG1vZGVsSWQ6IHN0cmluZyB9KTogdm9pZCB7XG4gICAgY29uc3QgdG9vbCA9IHRoaXMuY29udHJpYnV0ZWRUb29scy5maW5kKCh0KSA9PiB0LmlkID09PSBlLnRvb2xJZCk7XG4gICAgaWYgKCF0b29sKSByZXR1cm47XG4gICAgLy8gUmVhc3NpZ25lZCByYXRoZXIgdGhhbiBtdXRhdGVkIHNvIHRoZSB0b29sYmFyJ3MgbmdPbkNoYW5nZXMgc2VlcyBhIG5ld1xuICAgIC8vIHJlZmVyZW5jZSBhbmQgcmVidWlsZHMgdGhhdCB0b29sJ3MgbW9kZWwgbWVudSB3aXRoIHRoZSBjaGVjayBtYXJrIG1vdmVkLlxuICAgIHRoaXMudG9vbE1vZGVsSWRzID0geyAuLi50aGlzLnRvb2xNb2RlbElkcywgW2UudG9vbElkXTogZS5tb2RlbElkIH07XG4gICAgdG9vbC5vbk1vZGVsQ2hhbmdlPy4oZS5tb2RlbElkKTtcbiAgICB0aGlzLnRvb2xQYXJhbXNbZS50b29sSWRdID0gdGhpcy5zZWVkUGFyYW1zKHRvb2wsIGUubW9kZWxJZCk7XG4gIH1cblxuICAvKiogQSB0b29sJ3MgZGVmYXVsdHMgZm9yIGEgY2hlY2twb2ludCwgd2l0aCB0aGF0IGNoZWNrcG9pbnQncyBvd24gb3ZlcnJpZGVzXG4gICAqICBhcHBsaWVkIG9uIHRvcCDigJQgcGVyLW1vZGVsIGRlZmF1bHRzIGJlYXQgcGVyLXRvb2wgb25lcy4gKi9cbiAgcHJpdmF0ZSBzZWVkUGFyYW1zKHRvb2w6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uLCBtb2RlbElkOiBzdHJpbmcpOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gICAgY29uc3QgbW9kZWwgPSB0b29sLm1vZGVscygpLmZpbmQoKG0pID0+IG0uaWQgPT09IG1vZGVsSWQpO1xuICAgIHJldHVybiB7IC4uLnRvb2wuZGVmYXVsdFBhcmFtcyhtb2RlbElkKSwgLi4uKG1vZGVsPy5kZWZhdWx0cyA/PyB7fSkgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBOYXJyb3cgYSBwYXJhbSBzcGVjIGZvciB0aGUgdGVtcGxhdGUuXG4gICAqXG4gICAqIGAqbmdTd2l0Y2hDYXNlYCBkb2VzIG5vdCBuYXJyb3cgYSBkaXNjcmltaW5hdGVkIHVuaW9uIGluIEFuZ3VsYXIncyB0ZW1wbGF0ZVxuICAgKiB0eXBlIGNoZWNrZXIgdGhlIHdheSBhIGBzd2l0Y2hgIGRvZXMgaW4gVHlwZVNjcmlwdCwgc28gdGhlIHRlbXBsYXRlIGFza3MgZm9yXG4gICAqIHRoZSB2YXJpYW50IGl0IGhhcyBhbHJlYWR5IG1hdGNoZWQgb24uXG4gICAqL1xuICBudW1iZXJQYXJhbShwOiBUb29sUGFyYW1TcGVjKTogTnVtYmVyUGFyYW1TcGVjIHtcbiAgICByZXR1cm4gcCBhcyBOdW1iZXJQYXJhbVNwZWM7XG4gIH1cblxuICBzZWxlY3RQYXJhbShwOiBUb29sUGFyYW1TcGVjKTogU2VsZWN0UGFyYW1TcGVjIHtcbiAgICByZXR1cm4gcCBhcyBTZWxlY3RQYXJhbVNwZWM7XG4gIH1cblxuICBvcGVuVG9vbFBhcmFtcyh0b29sSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMucGFyYW1zRm9yKHRvb2xJZCk7IC8vIHNlZWQgYmVmb3JlIHRoZSBkaWFsb2cgYmluZHMgdG8gaXRcbiAgICB0aGlzLm9wZW5QYXJhbXNUb29sSWQgPSB0b29sSWQ7XG4gIH1cblxuICAvKiogVGhlIHRvb2wgd2hvc2UgcGFyYW1ldGVyIGRpYWxvZyBpcyBvcGVuLCBmb3IgdGhlIGdlbmVyaWMgZGlhbG9nJ3MgaGVhZGVyXG4gICAqICBhbmQgZmllbGQgbGlzdC4gKi9cbiAgZ2V0IG9wZW5QYXJhbXNUb29sKCk6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5jb250cmlidXRlZFRvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IHRoaXMub3BlblBhcmFtc1Rvb2xJZCk7XG4gIH1cblxuICByZXNldFRvb2xQYXJhbXModG9vbElkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCB0b29sID0gdGhpcy5jb250cmlidXRlZFRvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IHRvb2xJZCk7XG4gICAgaWYgKHRvb2wpIHRoaXMudG9vbFBhcmFtc1t0b29sSWRdID0gdGhpcy5zZWVkUGFyYW1zKHRvb2wsIHRoaXMubW9kZWxJZEZvcih0b29sKSk7XG4gIH1cblxuICAvKiogUnVuIGEgY29udHJpYnV0ZWQgdG9vbCBvdmVyIHRoZSBjdXJyZW50IHZpZXcuIE5vIHByb21wdDogdGhlc2Ugc3dlZXAgdGhlXG4gICAqICB3aG9sZSB2aWV3IHJhdGhlciB0aGFuIGJlaW5nIHBvaW50ZWQgYXQgc29tZXRoaW5nLiBSZXVzZXMgdGhlIHNoYXJlZFxuICAgKiAgc2VnbWVudGF0aW9uIHRvYXN0IHNvIHByb2dyZXNzIHJlYWRzIHRoZSBzYW1lIGFzIHRoZSBTQU0vY2VsbHBvc2UgdG9vbHMuICovXG4gIGFzeW5jIHJ1blRvb2wodG9vbElkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB0b29sID0gdGhpcy5jb250cmlidXRlZFRvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IHRvb2xJZCk7XG4gICAgaWYgKCF0b29sKSByZXR1cm47XG4gICAgY29uc3QgcGFyYW1zID0geyAuLi50aGlzLnBhcmFtc0Zvcih0b29sSWQpLCBtb2RlbElkOiB0aGlzLm1vZGVsSWRGb3IodG9vbCkgfTtcbiAgICBhd2FpdCB0aGlzLnJ1blNlZ21lbnRXaXRoVG9hc3QodG9vbC5sYWJlbCwgdG9vbC5wcm9ncmVzcywgKCkgPT5cbiAgICAgIHRvb2wucnVuKHRoaXMucGxvdFNlcnZpY2UsIHBhcmFtcyksXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBTaGFyZWQgZHJpdmVyIGZvciB0aGUgYm94LXByb21wdCBzZWdtZW50IHRvb2xzOiB3aXJlcyB0aGUgdG9vbCdzIHN0YXR1cyArXG4gICAqICBkb3dubG9hZCBwcm9ncmVzcyBpbnRvIHRoZSBzdGlja3kgYHNhbWAgdG9hc3QsIHJ1bnMgYG9wYCwgYW5kIHJlcG9ydHMgdGhlXG4gICAqICByZWdpb24gY291bnQuIEtlZXBzIHRoZSB0b2FzdCBvcGVuIHVudGlsIHRoZSBydW4gc2V0dGxlcyAoYmFyIGhpdHMgMTAwJSkuICovXG4gIHByaXZhdGUgYXN5bmMgcnVuU2VnbWVudFdpdGhUb2FzdChcbiAgICBsYWJlbDogc3RyaW5nLFxuICAgIC8vIE9ic2VydmFibGVzIHJhdGhlciB0aGFuIEJlaGF2aW9yU3ViamVjdHM6IGEgY29udHJpYnV0ZWQgdG9vbCBpbXBsZW1lbnRzXG4gICAgLy8gVG9vbFByb2dyZXNzLCBhbmQgcmVxdWlyaW5nIGEgY29uY3JldGUgc3ViamVjdCB3b3VsZCBmb3JjZSBldmVyeSBwbHVnaW5cbiAgICAvLyB0byBleHBvc2UgaXRzIGludGVybmFscyBqdXN0IHRvIGRyaXZlIHRoaXMgdG9hc3QuXG4gICAgdG9vbDogeyBzdGF0dXMkOiBPYnNlcnZhYmxlPHN0cmluZz47IHByb2dyZXNzJDogT2JzZXJ2YWJsZTxudW1iZXI+IH0sXG4gICAgb3A6ICgpID0+IFByb21pc2U8bnVtYmVyPixcbiAgKSB7XG4gICAgdGhpcy5zYW1TdGF0dXMgPSAnU3RhcnRpbmfigKYnO1xuICAgIHRoaXMuc2FtUHJvZ3Jlc3MgPSAwO1xuICAgIHRoaXMuc2FtRG93bmxvYWRpbmcgPSBmYWxzZTtcbiAgICB0aGlzLnNhbUJ1c3kgPSB0cnVlO1xuICAgIGNvbnN0IHBzdWIgPSB0b29sLnByb2dyZXNzJC5zdWJzY3JpYmUoKGYpID0+IHtcbiAgICAgIHRoaXMuc2FtRG93bmxvYWRpbmcgPSBmID49IDAgJiYgZiA8IDE7XG4gICAgICBpZiAoZiA+PSAwKSB0aGlzLnNhbVByb2dyZXNzID0gTWF0aC5taW4oMTAwLCBNYXRoLnJvdW5kKGYgKiAxMDApKTtcbiAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICB9KTtcbiAgICAvLyBUcmFja2VkIGhlcmUgcmF0aGVyIHRoYW4gcmVhZCBvZmYgdGhlIHN1YmplY3QgYXQgdGhlIGVuZDogYHRvb2xgIGlzIG9ubHlcbiAgICAvLyBhbiBPYnNlcnZhYmxlIHBhaXIgbm93LCBzbyB0aGVyZSBpcyBubyBgLnZhbHVlYCB0byBzYW1wbGUgb25jZSB0aGUgcnVuXG4gICAgLy8gc2V0dGxlcy4gVGhlIGxhc3Qgbm9uLWVtcHR5IHN0YXR1cyBpcyB3aGF0IHRoZSByZXN1bHQgdG9hc3QgcmVwb3J0cy5cbiAgICBsZXQgbGFzdFN0YXR1cyA9ICcnO1xuICAgIGNvbnN0IHNzdWIgPSB0b29sLnN0YXR1cyQuc3Vic2NyaWJlKChtKSA9PiB7XG4gICAgICBpZiAobSkge1xuICAgICAgICBsYXN0U3RhdHVzID0gbTtcbiAgICAgICAgdGhpcy5zYW1TdGF0dXMgPSBtO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5zaG93U2FtVG9hc3QobGFiZWwpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBuID0gYXdhaXQgb3AoKTtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiB0aGlzLnJlc3VsdFRvYXN0S2V5LFxuICAgICAgICBzZXZlcml0eTogbiA+IDAgPyAnc3VjY2VzcycgOiAnd2FybicsXG4gICAgICAgIHN1bW1hcnk6IGxhYmVsLFxuICAgICAgICBkZXRhaWw6IGxhc3RTdGF0dXMgfHwgKG4gPiAwID8gYEFkZGVkICR7bn0gcmVnaW9uKHMpLmAgOiAnTm8gcmVnaW9ucyBhZGRlZC4nKSxcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiB0aGlzLnJlc3VsdFRvYXN0S2V5LFxuICAgICAgICBzZXZlcml0eTogJ2Vycm9yJywgc3VtbWFyeTogYCR7bGFiZWx9IGZhaWxlZGAsIGRldGFpbDogU3RyaW5nKGUpLFxuICAgICAgfSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHBzdWIudW5zdWJzY3JpYmUoKTtcbiAgICAgIHNzdWIudW5zdWJzY3JpYmUoKTtcbiAgICAgIHRoaXMuaGlkZVNhbVRvYXN0KCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFNob3cgdGhlIHNoYXJlZCBzdGlja3kgYHNhbWAgdG9hc3Qgb25jZSAoaWRlbXBvdGVudCDigJQgcmUtYWRkaW5nIHdvdWxkIHN0YWNrXG4gICAqICBhIG5ldyB0b2FzdCBvbiBldmVyeSBwb2ludCBjbGljaykuICovXG4gIHByaXZhdGUgc2hvd1NhbVRvYXN0KHN1bW1hcnk6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNhbVRvYXN0U2hvd24pIHJldHVybjtcbiAgICB0aGlzLnNhbVRvYXN0U2hvd24gPSB0cnVlO1xuICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHsga2V5OiB0aGlzLnNhbVRvYXN0S2V5LCBzdGlja3k6IHRydWUsIHNldmVyaXR5OiAnaW5mbycsIHN1bW1hcnkgfSk7XG4gIH1cblxuICAvKiogRGlzbWlzcyB0aGUgc2hhcmVkIGBzYW1gIHRvYXN0IGFuZCByZXNldCBpdHMgcHJvZ3Jlc3Mvc3Bpbm5lciBzdGF0ZS4gKi9cbiAgcHJpdmF0ZSBoaWRlU2FtVG9hc3QoKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Ub2FzdFNob3duID0gZmFsc2U7XG4gICAgdGhpcy5zYW1CdXN5ID0gZmFsc2U7XG4gICAgdGhpcy5zYW1Eb3dubG9hZGluZyA9IGZhbHNlO1xuICAgIHRoaXMuc2FtUHJvZ3Jlc3MgPSAwO1xuICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuY2xlYXIodGhpcy5zYW1Ub2FzdEtleSk7XG4gICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICB9XG5cbiAgLyoqIFBpY2sgdGhlIFNBTSBtb2RlbCB0aGUgc2VnbWVudCB0b29scyB1c2UgKGppdC11aSM5MCBQMSkuICovXG4gIG9uU2FtTW9kZWxDaGFuZ2UoaWQ6IHN0cmluZykge1xuICAgIHRoaXMuc2FtTW9kZWxJZCA9IGlkO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U2FtTW9kZWwoaWQpO1xuICB9XG5cbiAgLyoqIENvbnZlcnQgdGhlIHNlbGVjdGVkIHJlZ2lvbiB0byBhIHNtb290aCBiZXppZXIgY3VydmUuICovXG4gIHRvQmV6aWVyUmVnaW9uKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0UmVnaW9uT3ZlcmxheSgpPy5zZXRTZWxlY3RlZEJlemllcih0cnVlKTtcbiAgfVxuXG4gIC8qKiBDb252ZXJ0IHRoZSBzZWxlY3RlZCByZWdpb24gYmFjayB0byBhIHN0cmFpZ2h0LWVkZ2VkIHBvbHlnb24uICovXG4gIHRvUG9seWdvblJlZ2lvbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbk92ZXJsYXkoKT8uc2V0U2VsZWN0ZWRCZXppZXIoZmFsc2UpO1xuICB9XG5cbiAgLyoqXG4gICAqIEltbWVkaWF0ZSByZWdpb24gYWN0aW9ucyBvbiB0aGUgY3VycmVudCBzZWxlY3Rpb24gKGppdC11aSM4NSk6IG1lcmdlLFxuICAgKiB1bmdyb3VwLCBpbnZlcnNlLCBzaW1wbGlmeSwgdGhlIELDqXppZXIgY29udmVyc2lvbnMsIGFuZCBkZWxldGUg4oCUIHRoZVxuICAgKiBvbmUtc2hvdCBnZW9tZXRyeSB0cmFuc2Zvcm1zLCBncm91cGVkIGFwYXJ0IGZyb20gdGhlIHRvb2wvbW9kZSB0b2dnbGVzIGFuZFxuICAgKiBzaG93biBvbmx5IHdoZW4gc29tZXRoaW5nIGlzIHNlbGVjdGVkLiBFYWNoIGl0ZW0gaXMgZ2F0ZWQgb24gd2hhdCB0aGVcbiAgICogc2VsZWN0aW9uIHN1cHBvcnRzLlxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFJlZ2lvbkFjdGlvbkl0ZW1zKCk6IE1lbnVJdGVtW10ge1xuICAgIGNvbnN0IGFsbCA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0UmVnaW9ucygpO1xuICAgIGNvbnN0IHNlbGVjdGFibGUgPSBhbGwuZmlsdGVyKChyKSA9PiByLmtpbmQgIT09ICdwcm9maWxlJyk7XG4gICAgaWYgKHNlbGVjdGFibGUubGVuZ3RoID09PSAwKSByZXR1cm4gW107XG4gICAgY29uc3QgaXRlbXM6IE1lbnVJdGVtW10gPSBbXTtcblxuICAgIC8vIFNlbGVjdCBhbGwg4oCUIGF2YWlsYWJsZSB3aGVuZXZlciB0aGUgaW1hZ2UgaGFzIHJlZ2lvbnMsIHJlZ2FyZGxlc3Mgb2YgdGhlXG4gICAgLy8gY3VycmVudCBzZWxlY3Rpb24uXG4gICAgaXRlbXMucHVzaCh7XG4gICAgICBsYWJlbDogJ1NlbGVjdCBhbGwgcmVnaW9ucycsIGljb246ICdwaSBwaS1jaGVjay1zcXVhcmUnLFxuICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy5zZWxlY3RBbGxSZWdpb25zKCksXG4gICAgfSk7XG5cbiAgICBjb25zdCBzZWxBbGwgPSB0aGlzLnNlbGVjdGVkUmVnaW9ucztcbiAgICBpZiAoc2VsQWxsLmxlbmd0aCA9PT0gMCkgcmV0dXJuIGl0ZW1zO1xuICAgIGNvbnN0IGVsaWdpYmxlID0gdGhpcy5vcEVsaWdpYmxlKHNlbEFsbCk7XG5cbiAgICBpZiAoZWxpZ2libGUubGVuZ3RoID49IDIpIHtcbiAgICAgIGl0ZW1zLnB1c2goeyBsYWJlbDogJ01lcmdlIC8gZ3JvdXAnLCBpY29uOiAncGkgcGktbGluaycsIGNvbW1hbmQ6ICgpID0+IHRoaXMubWVyZ2VSZWdpb25zKCkgfSk7XG4gICAgfVxuICAgIGlmIChzZWxBbGwuc29tZSgocikgPT4gdGhpcy5yZWdpb25PcHMuY2FuVW5ncm91cChyKSkpIHtcbiAgICAgIGl0ZW1zLnB1c2goeyBsYWJlbDogJ1VuZ3JvdXAnLCBpY29uOiAncGkgcGktc2l0ZW1hcCcsIGNvbW1hbmQ6ICgpID0+IHRoaXMudW5ncm91cFJlZ2lvbnMoKSB9KTtcbiAgICB9XG4gICAgaWYgKGVsaWdpYmxlLmxlbmd0aCA+PSAxKSB7XG4gICAgICBpdGVtcy5wdXNoKFxuICAgICAgICB7IGxhYmVsOiAnSW52ZXJzZScsIGljb246ICdwaSBwaS1jbG9uZScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuaW52ZXJzZVJlZ2lvbnMoKSB9LFxuICAgICAgICB7XG4gICAgICAgICAgbGFiZWw6ICdTaW1wbGlmeScsIGljb246ICdwaSBwaS1jaGFydC1saW5lJyxcbiAgICAgICAgICBpdGVtczogW1xuICAgICAgICAgICAgeyBsYWJlbDogJ0xpZ2h0ICgxIHB4KScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2ltcGxpZnlSZWdpb25zKDEpIH0sXG4gICAgICAgICAgICB7IGxhYmVsOiAnTWVkaXVtICgzIHB4KScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2ltcGxpZnlSZWdpb25zKDMpIH0sXG4gICAgICAgICAgICB7IGxhYmVsOiAnU3Ryb25nICg4IHB4KScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2ltcGxpZnlSZWdpb25zKDgpIH0sXG4gICAgICAgICAgICB7IHNlcGFyYXRvcjogdHJ1ZSB9LFxuICAgICAgICAgICAgeyBsYWJlbDogJ0N1c3RvbeKApicsIGNvbW1hbmQ6ICgpID0+IHRoaXMub3BlblNpbXBsaWZ5RGlhbG9nKCkgfSxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgKTtcbiAgICB9XG4gICAgLy8gQsOpemllciBjb252ZXJzaW9ucyBhcmUgdmVydGV4LWxldmVsIGVkaXRzIOKAlCBJbWFnZSAoT3BlblNlYWRyYWdvbikgdmlld1xuICAgIC8vIG9ubHkg4oCUIGdhdGVkIG9uIHRoZSBzZWxlY3Rpb24ncyBjdXJyZW50IGZvcm0uXG4gICAgaWYgKHRoaXMuYmFzZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRSkge1xuICAgICAgY29uc3QgaGFzU3RyYWlnaHQgPSBzZWxBbGwuc29tZSgocikgPT5cbiAgICAgICAgKHIuYm91bmRzIGluc3RhbmNlb2YgUG9seWdvbiAmJiAhci5ib3VuZHMuYmV6aWVyKSB8fCByLmJvdW5kcyBpbnN0YW5jZW9mIFJlY3RhbmdsZSk7XG4gICAgICBjb25zdCBoYXNCZXppZXIgPSBzZWxBbGwuc29tZSgocikgPT4gci5ib3VuZHMgaW5zdGFuY2VvZiBQb2x5Z29uICYmIHIuYm91bmRzLmJlemllcik7XG4gICAgICBpZiAoaGFzU3RyYWlnaHQpIHtcbiAgICAgICAgaXRlbXMucHVzaCh7IGxhYmVsOiAnQ29udmVydCB0byBCw6l6aWVyJywgaWNvbjogJ3RvLWJlemllci1pY29uJywgY29tbWFuZDogKCkgPT4gdGhpcy50b0JlemllclJlZ2lvbigpIH0pO1xuICAgICAgfVxuICAgICAgaWYgKGhhc0Jlemllcikge1xuICAgICAgICBpdGVtcy5wdXNoKHsgbGFiZWw6ICdDb252ZXJ0IHRvIHBvbHlnb24nLCBpY29uOiAndG8tcG9seWdvbi1pY29uJywgY29tbWFuZDogKCkgPT4gdGhpcy50b1BvbHlnb25SZWdpb24oKSB9KTtcbiAgICAgIH1cbiAgICB9XG4gICAgaXRlbXMucHVzaCh7IGxhYmVsOiAnRGVsZXRlIHJlZ2lvbicsIGljb246ICdwaSBwaS10cmFzaCcsIGNvbW1hbmQ6ICgpID0+IHRoaXMuZGVsZXRlUmVnaW9uKCkgfSk7XG4gICAgcmV0dXJuIGl0ZW1zO1xuICB9XG5cbiAgcHJpdmF0ZSBidWlsZENvbnRleHRNZW51SXRlbXMoKTogTWVudUl0ZW1bXSB7XG4gICAgY29uc3QgYWN0aXZlID0gdGhpcy5hY3RpdmVEcmFnTW9kZTtcbiAgICBjb25zdCBhY3RpdmVDbGFzcyA9ICdjb250ZXh0LW1lbnUtYWN0aXZlJztcbiAgICBjb25zdCBpdGVtczogTWVudUl0ZW1bXSA9IFtdO1xuICAgIC8vIFNlbGVjdGVkLXJlZ2lvbiBhY3Rpb25zIGxlYWQgdGhlIG1lbnUgKHNlcGFyYXRlZCBmcm9tIHRoZSB0b29sIHRvZ2dsZXMpLlxuICAgIGlmICh0aGlzLmlzSGVhdG1hcCkge1xuICAgICAgY29uc3QgYWN0aW9ucyA9IHRoaXMuYnVpbGRSZWdpb25BY3Rpb25JdGVtcygpO1xuICAgICAgaWYgKGFjdGlvbnMubGVuZ3RoKSBpdGVtcy5wdXNoKC4uLmFjdGlvbnMsIHsgc2VwYXJhdG9yOiB0cnVlIH0pO1xuICAgIH1cbiAgICBpZiAodGhpcy5pc0hlYXRtYXApIHtcbiAgICAgIGl0ZW1zLnB1c2goXG4gICAgICAgIHsgbGFiZWw6ICdBdXRvc2NhbGUnLCBpY29uOiAncGkgcGktd2luZG93LW1heGltaXplJywgY29tbWFuZDogKCkgPT4gdGhpcy5hdXRvc2NhbGVJbWFnZSgpIH0sXG4gICAgICAgIHsgc2VwYXJhdG9yOiB0cnVlIH0sXG4gICAgICApO1xuICAgICAgLy8gJ1pvb20gc2VsZWN0aW9uJyBpcyBQbG90bHkncyBydWJiZXItYmFuZCB6b29tOyBpdCBkb2Vzbid0IGFwcGx5IHRvIHRoZVxuICAgICAgLy8gT3BlblNlYWRyYWdvbi1iYWNrZWQgSW1hZ2UgdmlldyAodXNlICdab29tIHRvIGJveCcgdGhlcmUgaW5zdGVhZCkuXG4gICAgICBpZiAoIXRoaXMuaXNJbWFnZVZpZXcpIHtcbiAgICAgICAgaXRlbXMucHVzaCh7XG4gICAgICAgICAgbGFiZWw6ICdab29tIHNlbGVjdGlvbicsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLXNlYXJjaCcsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnem9vbScgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3pvb20nKSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICBpdGVtcy5wdXNoKHtcbiAgICAgICAgbGFiZWw6ICdab29tIHRvIGJveCcsXG4gICAgICAgIGljb246ICd6b29tLWJveC1vZmYtaWNvbicsXG4gICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ3pvb21Ub0JveCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCd6b29tVG9Cb3gnKSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICBpZiAodGhpcy5pc0hlYXRtYXApIHtcbiAgICAgIGl0ZW1zLnB1c2goe1xuICAgICAgICBsYWJlbDogJ1BhbicsXG4gICAgICAgIGljb246ICdwaSBwaS1hcnJvd3MtYWx0JyxcbiAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAncGFuJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3BhbicpLFxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHMzZCA9IHRoaXMuYWN0aXZlU3VyZmFjZTNkTW9kZTtcbiAgICAgIGl0ZW1zLnB1c2goXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1pvb20nLFxuICAgICAgICAgIGljb246ICdwaSBwaS1zZWFyY2gnLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IHMzZCA9PT0gJ3pvb20nID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZVN1cmZhY2UzZE1vZGUoJ3pvb20nKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnUGFuJyxcbiAgICAgICAgICBpY29uOiAncGkgcGktYXJyb3dzLWFsdCcsXG4gICAgICAgICAgc3R5bGVDbGFzczogczNkID09PSAncGFuJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVTdXJmYWNlM2RNb2RlKCdwYW4nKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnT3JiaXRhbCByb3RhdGlvbicsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLWdsb2JlJyxcbiAgICAgICAgICBzdHlsZUNsYXNzOiBzM2QgPT09ICdvcmJpdCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlU3VyZmFjZTNkTW9kZSgnb3JiaXQnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnVHVybnRhYmxlIHJvdGF0aW9uJyxcbiAgICAgICAgICBpY29uOiAncGkgcGktc3luYycsXG4gICAgICAgICAgc3R5bGVDbGFzczogczNkID09PSAndHVybnRhYmxlJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVTdXJmYWNlM2RNb2RlKCd0dXJudGFibGUnKSxcbiAgICAgICAgfSxcbiAgICAgICAgeyBzZXBhcmF0b3I6IHRydWUgfSxcbiAgICAgICAgeyBsYWJlbDogJ1Jlc2V0IGNhbWVyYScsIGljb246ICdwaSBwaS1ob21lJywgY29tbWFuZDogKCkgPT4gdGhpcy5yZXNldFN1cmZhY2VDYW1lcmEoKSB9LFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaXNIZWF0bWFwKSB7XG4gICAgICBpdGVtcy5wdXNoKFxuICAgICAgICB7IGxhYmVsOiAnWm9vbSBpbicsIGljb246ICdwaSBwaS1zZWFyY2gtcGx1cycsIGNvbW1hbmQ6ICgpID0+IHRoaXMuem9vbUluKCkgfSxcbiAgICAgICAgeyBsYWJlbDogJ1pvb20gb3V0JywgaWNvbjogJ3BpIHBpLXNlYXJjaC1taW51cycsIGNvbW1hbmQ6ICgpID0+IHRoaXMuem9vbU91dCgpIH0sXG4gICAgICAgIHsgc2VwYXJhdG9yOiB0cnVlIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1NlbGVjdCcsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLWFycm93LXVwLXJpZ2h0JyxcbiAgICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICdzZWxlY3QnID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdzZWxlY3QnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnRnJlZWZvcm0nLFxuICAgICAgICAgIGljb246ICdwaSBwaS1wZW5jaWwnLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ2RyYXdjbG9zZWRwYXRoJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd2Nsb3NlZHBhdGgnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnQnJ1c2gnLFxuICAgICAgICAgIGljb246ICdicnVzaC1pY29uJyxcbiAgICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICdicnVzaCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2JydXNoJyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1BvbHlsaW5lJyxcbiAgICAgICAgICBpY29uOiAncG9seWxpbmUtaWNvbicsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnZHJhd29wZW5wYXRoJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd29wZW5wYXRoJyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1JlY3RhbmdsZScsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLXN0b3AnLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ2RyYXdyZWN0JyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd3JlY3QnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnV2FuZCcsXG4gICAgICAgICAgaWNvbjogJ3dhbmQtaWNvbicsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnd2FuZCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3dhbmQnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnVmVydGV4IGVyYXNlcicsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLWVyYXNlcicsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnZXJhc2VWZXJ0ZXgnID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdlcmFzZVZlcnRleCcpLFxuICAgICAgICB9LFxuICAgICAgKTtcbiAgICAgIC8vIFZlcnRleCBlZGl0aW5nIHJ1bnMgb24gdGhlIE9wZW5TZWFkcmFnb24gb3ZlcmxheSwgd2hpY2hcbiAgICAgIC8vIGJhY2tzIHRoZSBJbWFnZSBwbG90IHR5cGUuIEhpZGRlbiBmb3Igb3RoZXIgMkQgdHlwZXMgKFBsb3RseSksIHdoZXJlXG4gICAgICAvLyB0aGVzZSBtb2RlcyBhcmUgbm8tb3BzLlxuICAgICAgaWYgKHRoaXMuYmFzZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRSkge1xuICAgICAgICBpdGVtcy5wdXNoKFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiAnUG9seWdvbiAoY2xpY2sgdmVydGljZXMpJyxcbiAgICAgICAgICAgIGljb246ICdwb2x5Z29uLXZlcnRpY2VzLWljb24nLFxuICAgICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnZHJhd3BvbHlnb24nID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2RyYXdwb2x5Z29uJyksXG4gICAgICAgICAgfSxcbiAgICAgICAgICB7XG4gICAgICAgICAgICBsYWJlbDogJ0FkZCB2ZXJ0ZXgnLFxuICAgICAgICAgICAgaWNvbjogJ3ZlcnRleC1hZGQtaWNvbicsXG4gICAgICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICdhZGRwb2ludCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnYWRkcG9pbnQnKSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiAnRGVsZXRlIHZlcnRleCcsXG4gICAgICAgICAgICBpY29uOiAndmVydGV4LWRlbGV0ZS1pY29uJyxcbiAgICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ2RlbGV0ZXBvaW50JyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdkZWxldGVwb2ludCcpLFxuICAgICAgICAgIH0sXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICAvLyBCw6l6aWVyIGNvbnZlcnNpb25zICsgRGVsZXRlIGFyZSBpbW1lZGlhdGUgc2VsZWN0aW9uICphY3Rpb25zKiwgbm93IGluIHRoZVxuICAgICAgLy8gXCJTZWxlY3RlZCByZWdpb24ocylcIiBncm91cCBhdCB0aGUgdG9wIChzZWUgYnVpbGRSZWdpb25BY3Rpb25JdGVtcykuXG4gICAgfVxuICAgIHJldHVybiBpdGVtcztcbiAgfVxuXG4gIHRvZ2dsZVN1cmZhY2UzZE1vZGUobW9kZTogc3RyaW5nKSB7XG4gICAgdGhpcy5hY3RpdmVTdXJmYWNlM2RNb2RlID0gbW9kZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFN1cmZhY2VEcmFnTW9kZShtb2RlKTtcbiAgfVxuXG4gIHJlc2V0U3VyZmFjZUNhbWVyYSgpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlc2V0U3VyZmFjZUNhbWVyYSgpO1xuICB9XG5cbiAgLyoqIFRvZ2dsZSB0aGUgbmFwYXJpIDNEIGNvb3JkaW5hdGUtYXhlcyAvIHNjYWxlIGdpem1vICh2b2x1bWUvaXNvc3VyZmFjZSkuIE5vLW9wIG9uIGJhY2tlbmRzXG4gICAqICB0aGF0IGRvbid0IHJlbmRlciBvbmUgKHRoZSBjb250cm9sIG1ldGhvZCBpcyBvcHRpb25hbCkuICovXG4gIHRvZ2dsZUF4ZXMoKSB7XG4gICAgdGhpcy5heGVzVmlzaWJsZSA9ICF0aGlzLmF4ZXNWaXNpYmxlO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0U3VyZmFjZTNkQ29udHJvbHMoKT8uc2V0QXhlc1Zpc2libGU/Lih0aGlzLmF4ZXNWaXNpYmxlKTtcbiAgfVxuXG4gIC8qKiBUb2dnbGUgdGhlIG5hcGFyaSBzdXJmYWNlIHdpcmVmcmFtZSAoZWRnZXMgdnMgZmlsbGVkKS4gTm8tb3Agb24gYmFja2VuZHMgd2l0aG91dCBhIHN1cmZhY2UuICovXG4gIHRvZ2dsZVdpcmVmcmFtZSgpIHtcbiAgICB0aGlzLndpcmVmcmFtZUFjdGl2ZSA9ICF0aGlzLndpcmVmcmFtZUFjdGl2ZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFN1cmZhY2UzZENvbnRyb2xzKCk/LnNldFdpcmVmcmFtZT8uKHRoaXMud2lyZWZyYW1lQWN0aXZlKTtcbiAgfVxuXG4gIC8qKiBDaGFuZ2UgdGhlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IuIERlY2ltYXRpb24gY2hhbmdlcyB0aGUgZmV0Y2hlZC9hc3NlbWJsZWQgZGF0YSwgc28gdGhpc1xuICAgKiAgcmUtcGxvdHMgdGhlIGN1cnJlbnQgM0QgdHlwZSBhdCB0aGUgbmV3IHJlc29sdXRpb24gKHVubGlrZSBheGVzL3dpcmVmcmFtZSwgd2hpY2ggYXJlIGxpdmUpLiAqL1xuICBzZWxlY3RSZXNvbHV0aW9uKHNjYWxlOiBudW1iZXIpIHtcbiAgICBpZiAoc2NhbGUgPT09IHRoaXMucmVzb2x1dGlvblNjYWxlKSByZXR1cm47XG4gICAgdGhpcy5yZXNvbHV0aW9uU2NhbGUgPSBzY2FsZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFJlc29sdXRpb25TY2FsZT8uKHNjYWxlKTtcbiAgICB0aGlzLnJlbG9hZEFuZFBsb3QoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTd2l0Y2ggdGhlIGFjdGl2ZSBwbG90IHR5cGUgKGhlYXRtYXAsIHN1cmZhY2UsIGNvbnRvdXIsIHNjYXR0ZXIsIGxpbmUsXG4gICAqIHNjYXR0ZXIzZCwgaXNvc3VyZmFjZSkuIEtlZXBzIGBpc0hlYXRtYXBgIGFzIHRoZSAyRC12cy0zRCBmbGFnIHRoZSByZXN0IG9mXG4gICAqIHRoZSB0b29sYmFyL2NvbnRleHQtbWVudSBsb2dpYyByZWxpZXMgb24sIGRlYWN0aXZhdGVzIDJELW9ubHkgdG9vbHMgd2hlblxuICAgKiBtb3ZpbmcgdG8gYSAzRCB0eXBlLCB0aGVuIHJlLXBsb3RzLlxuICAgKi9cbiAgb25TZWxlY3RQbG90VHlwZShzZWxlY3RlZDogUGxvdFR5cGVJZCkge1xuICAgIC8vIEEgY29udHJpYnV0ZWQgaWQgd2hvc2UgcHJvdmlkZXIgaXMgZ29uZSAoZS5nLiByZXN0b3JlZCBmcm9tIGEgcHJldmlvdXNcbiAgICAvLyBzZXNzaW9uKSBoYXMgbm90aGluZyB0byBhY3RpdmF0ZSDigJQgdGFrZSB0aGUgZGVmYXVsdCBJbWFnZSB2aWV3IGluc3RlYWQuXG4gICAgaWYgKCFpc0J1aWx0aW5QbG90VHlwZShzZWxlY3RlZCkgJiYgIXRoaXMucGxvdE1vZGVzLmZpbmQoc2VsZWN0ZWQpKSB7XG4gICAgICBjb25zb2xlLndhcm4oYFt2aXN1YWxpemVyXSB1bmtub3duIHBsb3QgdHlwZSAnJHtzZWxlY3RlZH0nIOKAlCBzaG93aW5nICcke1Bsb3RUeXBlLklNQUdFfScgaW5zdGVhZC5gKTtcbiAgICAgIHNlbGVjdGVkID0gUGxvdFR5cGUuSU1BR0U7XG4gICAgfVxuICAgIC8vIEFuIGV4cGxpY2l0IGNob2ljZSByZXRyaWVzIGEgbW9kZSB3aG9zZSBlYXJsaWVyIGNsZWFudXAgZmFpbGVkOyBvbmx5XG4gICAgLy8gYXV0b21hdGljIHJlLWFjdGl2YXRpb25zIChyZS1yZW5kZXIsIGltYWdlIHN3aXRjaCkgZmFsbCBiYWNrIGZvciB0aGF0LlxuICAgIHRoaXMucGxvdE1vZGVzLmNsZWFyQ2xlYW51cEZhaWx1cmVzKCk7XG4gICAgLy8gV2hhdGV2ZXIgd2FzIHNlbGVjdGVkIGJlZm9yZSwgYSBjb250cmlidXRlZCBzZXNzaW9uIGVuZHMgYmVmb3JlIHRoZSBuZXh0XG4gICAgLy8gbW9kZSBkcmF3cy4gKFJlLXNlbGVjdGluZyB0aGUgc2FtZSBjb250cmlidXRlZCBtb2RlIHJlLXBsb3RzLCBzbyBpdCBnZXRzIGFcbiAgICAvLyBmcmVzaCBzZXNzaW9uIHRvby4pXG4gICAgdGhpcy5wbG90TW9kZXMuZGVhY3RpdmF0ZSgpO1xuICAgIC8vIEV2ZXJ5dGhpbmcgYmVsb3cgZGVjaWRlcyBob3cgdG8gUkVOREVSLCBzbyBpdCB3b3JrcyBvbiB0aGUgYnVpbHQtaW4gdHlwZVxuICAgIC8vIHRoYXQgZHJhd3MgYHNlbGVjdGVkYDogYSBjb250cmlidXRlZCBtb2RlIGJlaGF2ZXMgZXhhY3RseSBsaWtlIGl0cyBiYXNlLlxuICAgIGNvbnN0IHR5cGUgPSB0aGlzLnBsb3RNb2Rlcy5iYXNlVHlwZU9mKHNlbGVjdGVkKTtcbiAgICAvLyBBIGRpYWxvZyB0b29sIGRyYXdzIG92ZXIgdGhlIEltYWdlIHZpZXc6IGxlYXZpbmcgaXQgY2xvc2VzIHRoZSBkaWFsb2cuIFN0YXlpbmdcbiAgICAvLyBvbiBpdCwgdGhlIHNlc3Npb24gZW5kcyBoZXJlIGFuZCByZXN0YXJ0cyBvbmNlIHRoZSByZS1wbG90IGxhbmRzLlxuICAgIGlmICh0eXBlID09PSBQbG90VHlwZS5JTUFHRSkgdGhpcy50b29sRGlhbG9ncy5kZWFjdGl2YXRlKCk7XG4gICAgZWxzZSB0aGlzLmNsb3NlRGlhbG9nVG9vbCgpO1xuICAgIHRoaXMucGxvdFR5cGUgPSB0eXBlO1xuICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gc2VsZWN0ZWQ7XG4gICAgY29uc3QgZGVzY3JpcHRvciA9IGlzQnVpbHRpblBsb3RUeXBlKHNlbGVjdGVkKVxuICAgICAgPyB0aGlzLnBsb3RUeXBlTWVudS5maW5kKChkKSA9PiBkLnR5cGUgPT09IHR5cGUpXG4gICAgICA6IGdldFBsb3RUeXBlRGVzY3JpcHRvcih0eXBlKTtcbiAgICBjb25zdCBpczNkID0gZGVzY3JpcHRvcj8uZGltZW5zaW9ucyA9PT0gJzNkJztcbiAgICB0aGlzLmlzSGVhdG1hcCA9ICFpczNkO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UGxvdFR5cGUodHlwZSk7XG4gICAgLy8gU3dpdGNoaW5nIHBsb3QgdHlwZSBjYW5jZWxzIHRoZSBhY3RpdmUgdG9vbCDigJQgbm90IGV2ZXJ5IHR5cGUgc3VwcG9ydHMgdGhlXG4gICAgLy8gc2FtZSB0b29scyAobGluZS9zY2F0dGVyIGNoYXJ0cyBhbmQgM0Qgc2NlbmVzIGhhdmUgbm8gYm94IHpvb20gb3IgcmVnaW9uXG4gICAgLy8gZHJhd2luZyksIHNvIGxlYXZpbmcgYSB0b29sIGFybWVkIHdvdWxkIG1pc2JlaGF2ZSBvbiB0aGUgbmV3IHBsb3QuXG4gICAgdGhpcy5kZWFjdGl2YXRlQWN0aXZlVG9vbCgpO1xuICAgIGlmIChpczNkKSB7XG4gICAgICB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUgPSAndHVybnRhYmxlJztcbiAgICB9XG4gICAgLy8gbmFwYXJpLWpzIHZvbHVtZS9pc29zdXJmYWNlIGFzc2VtYmxlIHRoZWlyIG93biAzRCB0ZXh0dXJlIGZyb20gdGhlIHNsaWNlIGVuZHBvaW50LCBzb1xuICAgIC8vIHRoZXkgdGFrZSB0aGUgbm9ybWFsIHJlLXBsb3QgcGF0aCDigJQgTk9UIFBsb3RseSdzIHN0YWNrLWZyYW1lIGxvYWRlciAod2hvc2UgXCJMb2FkaW5nXG4gICAgLy8gZnJhbWVzXCIgb3ZlcmxheSB3b3VsZCBuZXZlciBjbGVhciwgc2luY2UgUGxvdGx5IGlzbid0IHRoZSBhY3RpdmUgYmFja2VuZCkgKGppdC11aSMxMDIpLlxuICAgIGNvbnN0IGlzTmFwYXJpID0gdHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFIHx8IGlzTmFwYXJpM2QodHlwZSk7XG4gICAgaWYgKGRlc2NyaXB0b3I/LnJlcXVpcmVzU3RhY2sgJiYgIWlzTmFwYXJpKSB7XG4gICAgICAvLyBWb2x1bWUgdHlwZXMgKGlzb3N1cmZhY2UsIHNjYXR0ZXIzZCkgbmVlZCB0aGUgd2hvbGUgei1zdGFjayBsb2FkZWQgYXMgYVxuICAgICAgLy8gM0QgYXJyYXksIGFuZCB0aGV5IGFsd2F5cyByZW5kZXIgb24gUGxvdGx5LiBEcml2ZSB0aGUgc3RhY2sgcmVsb2FkXG4gICAgICAvLyBkaXJlY3RseSB0aHJvdWdoIHRoZSBpbWFnZS1pbmZvIHN0cmVhbSByYXRoZXIgdGhhbiB0aGUgYWN0aXZlIHJlbmRlcmVyJ3NcbiAgICAgIC8vIHNldFNob3dTdGFjazogd2hlbiBPcGVuU2VhZHJhZ29uIG93bnMgdGhlIEltYWdlIHZpZXcgaXRzIHNldFNob3dTdGFjayBpc1xuICAgICAgLy8gYSBuby1vcCwgc28gdGhlIHJlbG9hZCBuZXZlciBmaXJlZCBhbmQgdGhlIGxvYWRlciBzcHVuIGZvcmV2ZXIuIFdlIGFybVxuICAgICAgLy8gUGxvdGx5J3Mgc3RhY2stbG9hZGluZyBmbGFnIChrZWVwcyBpdHMgZnJhbWUtZmV0Y2ggbG9vcCBhbGl2ZSkgYW5kXG4gICAgICAvLyByZS1lbWl0IHRoZSBpbWFnZSBpbmZvIHdpdGggc2hvd1N0YWNrIG9uIHNvIHRoZSBwaXBlbGluZSByZWxvYWRzIG9uXG4gICAgICAvLyBQbG90bHkgcmVnYXJkbGVzcyBvZiB3aGljaCBiYWNrZW5kIHdhcyBvbiBzY3JlZW4uXG4gICAgICB0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24gPSB0aGlzLnN0YWNrT3B0aW9uc1sxXTtcbiAgICAgIHRoaXMuc3RhY2tMb2FkaW5nID0gdHJ1ZTtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U3RhY2tMb2FkaW5nKHRydWUpO1xuICAgICAgaWYgKHRoaXMuaW1hZ2VJbmZvKSB7XG4gICAgICAgIHRoaXMuaW1hZ2VJbmZvLnNob3dTdGFjayA9IHRydWU7XG4gICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKHRoaXMuaW1hZ2VJbmZvKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5yZWxvYWRBbmRQbG90KCk7XG4gIH1cblxuICAvLyDilIDilIAgY29udHJpYnV0ZWQgcGxvdCBtb2RlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKipcbiAgICogU3RhcnQgdGhlIHNlbGVjdGVkIGNvbnRyaWJ1dGVkIG1vZGUncyBzZXNzaW9uLCBvbmNlIGl0cyBiYXNlIHZpZXcgaGFzXG4gICAqIHBsb3R0ZWQuIE5vLW9wIGZvciBhIGJ1aWx0LWluIHR5cGUsIG9yIHdoZW4gdGhlIGJhc2Ugb24gc2NyZWVuIGlzIG5vdCB0aGVcbiAgICogb25lIHRoZSBtb2RlIHJpZGVzIG9uLiBBIGJhY2tlbmQgdGhhdCBjYW5ub3QgcHJvdmlkZSBhIHZpZXdwb3J0IGZvciBpdFxuICAgKiAoZS5nLiBPU0QgZmVsbCBiYWNrIHRvIFBsb3RseSkgY291bnRzIGFzIGEgZmFpbGVkIGFjdGl2YXRpb24uXG4gICAqL1xuICBwcml2YXRlIGFjdGl2YXRlU2VsZWN0ZWRQbG90TW9kZSgpOiB2b2lkIHtcbiAgICBjb25zdCBjb250cmlidXRpb24gPSB0aGlzLnBsb3RNb2Rlcy5maW5kKHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkKTtcbiAgICBpZiAoIWNvbnRyaWJ1dGlvbiB8fCB0aGlzLnBsb3RUeXBlICE9PSBjb250cmlidXRpb24uZGVzY3JpcHRvci5iYXNlVHlwZSkgcmV0dXJuO1xuICAgIGNvbnN0IHZpZXdwb3J0ID0gdGhpcy5wbG90U2VydmljZS5nZXRQbG90TW9kZVZpZXdwb3J0Py4oKSA/PyBudWxsO1xuICAgIGlmICghdmlld3BvcnQpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYFt2aXN1YWxpemVyXSBwbG90LXR5cGUgY29udHJpYnV0aW9uICcke2NvbnRyaWJ1dGlvbi5kZXNjcmlwdG9yLnR5cGV9JzogdGhlIGJhY2tlbmQgYFxuICAgICAgICArICdvbiBzY3JlZW4gcHJvdmlkZXMgbm8gdmlld3BvcnQgdG8gZHJhdyBvdmVyIOKAlCBmYWxsaW5nIGJhY2suJyk7XG4gICAgICB0aGlzLmZhbGxCYWNrRnJvbVBsb3RNb2RlKGNvbnRyaWJ1dGlvbik7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGN0eDogUGxvdE1vZGVDb250ZXh0ID0ge1xuICAgICAgdmlzdWFsaXplcjogdGhpcy5wbG90U2VydmljZSxcbiAgICAgIHZpZXdwb3J0LFxuICAgICAgaW1hZ2VJbmZvJDogdGhpcy5zdGF0ZS5nZXRJbWFnZUluZm8kKCksXG4gICAgICB0b29sczogdGhpcy5wbG90TW9kZVRvb2xzLFxuICAgIH07XG4gICAgdm9pZCB0aGlzLnBsb3RNb2Rlcy5hY3RpdmF0ZShjb250cmlidXRpb24sIGN0eCk7XG4gIH1cblxuICAvKiogUmVuZGVyIHRoZSBsaXZlIG1vZGUncyBwYW5lbC4gQWN0aXZhdGlvbiBjYW4gcmVzb2x2ZSBvdXRzaWRlIEFuZ3VsYXIgKGFuXG4gICAqICBPU0QgY2FsbGJhY2ssIGEgY29udHJpYnV0aW9uJ3Mgb3duIHByb21pc2UpLCBoZW5jZSB0aGUgem9uZSByZS1lbnRyeS4gKi9cbiAgcHJpdmF0ZSBzaG93UGxvdE1vZGVQYW5lbChhY3RpdmU6IEFjdGl2ZVBsb3RNb2RlKTogdm9pZCB7XG4gICAgY29uc3QgcGFuZWwgPSBhY3RpdmUuY29udHJpYnV0aW9uLnBhbmVsO1xuICAgIGlmICghcGFuZWwpIHJldHVybjtcbiAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgaWYgKCdjb21wb25lbnQnIGluIHBhbmVsICYmIHBhbmVsLmNvbXBvbmVudCkge1xuICAgICAgICB0aGlzLnBsb3RNb2RlUGFuZWwgPSB7XG4gICAgICAgICAgdGl0bGU6IHBhbmVsLnRpdGxlLFxuICAgICAgICAgIGNvbXBvbmVudDogcGFuZWwuY29tcG9uZW50LFxuICAgICAgICAgIGluamVjdG9yOiBJbmplY3Rvci5jcmVhdGUoe1xuICAgICAgICAgICAgcHJvdmlkZXJzOiBbXG4gICAgICAgICAgICAgIHsgcHJvdmlkZTogUExPVF9NT0RFX0NPTlRFWFQsIHVzZVZhbHVlOiBhY3RpdmUuY3R4IH0sXG4gICAgICAgICAgICAgIHsgcHJvdmlkZTogUExPVF9NT0RFX1NFU1NJT04sIHVzZVZhbHVlOiBhY3RpdmUuc2Vzc2lvbiB9LFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICAgIHBhcmVudDogdGhpcy5pbmplY3RvcixcbiAgICAgICAgICB9KSxcbiAgICAgICAgICBob3N0OiBudWxsLFxuICAgICAgICB9O1xuICAgICAgfSBlbHNlIGlmIChhY3RpdmUucGFuZWxIb3N0KSB7XG4gICAgICAgIHRoaXMucGxvdE1vZGVQYW5lbCA9IHsgdGl0bGU6IHBhbmVsLnRpdGxlLCBjb21wb25lbnQ6IG51bGwsIGluamVjdG9yOiBudWxsLCBob3N0OiBhY3RpdmUucGFuZWxIb3N0IH07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBBIGNvbXBvbmVudCBwYW5lbCBpcyBjcmVhdGVkIGFuZCBmaXJzdCByZW5kZXJlZCByaWdodCBoZXJlOyBpZiB0aGF0IHRocm93cyxcbiAgICAgIC8vIHRoZSBtb2RlIGlzIG5vdCB1c2FibGUg4oCUIGVuZCBpdCBhbmQgZmFsbCBiYWNrIHJhdGhlciB0aGFuIGxlYXZlIGl0IHNlbGVjdGVkLlxuICAgICAgY29uc3QgZXJyID0gdGhpcy5kZXRlY3RDaGFuZ2VzU2FmZWx5KCk7XG4gICAgICBpZiAoZXJyICE9PSBudWxsKSB0aGlzLnBsb3RNb2Rlcy5mYWlsQWN0aXZlKGVycik7XG4gICAgfSk7XG4gIH1cblxuICAvKiogRHJvcCB0aGUgcGFuZWwgKGRlc3Ryb3lpbmcgYSBjb21wb25lbnQgcGFuZWwpIGJlZm9yZSB0aGUgc2Vzc2lvbiBlbmRzLiAqL1xuICBwcml2YXRlIGhpZGVQbG90TW9kZVBhbmVsKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5wbG90TW9kZVBhbmVsKSByZXR1cm47XG4gICAgdGhpcy5wbG90TW9kZVBhbmVsID0gbnVsbDtcbiAgICB0aGlzLmRldGVjdENoYW5nZXNTYWZlbHkoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBIGNvbnRyaWJ1dGVkIG1vZGUgY291bGQgbm90IHN0YXJ0LiBJdHMgYmFzZSB0eXBlIGlzIGFscmVhZHkgb24gc2NyZWVuIOKAlFxuICAgKiB0aGUgYmFzZSBwbG90dGVkIGZpcnN0IOKAlCBzbyBqdXN0IHNlbGVjdCBpdDogbm90aGluZyBuZWVkcyB0byByZS1wbG90LlxuICAgKi9cbiAgcHJpdmF0ZSBmYWxsQmFja0Zyb21QbG90TW9kZShjb250cmlidXRpb246IFBsb3RUeXBlQ29udHJpYnV0aW9uKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuZGVzdHJveWluZyB8fCB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCAhPT0gY29udHJpYnV0aW9uLmRlc2NyaXB0b3IudHlwZSkgcmV0dXJuO1xuICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICBjb25zdCBiYXNlID0gY29udHJpYnV0aW9uLmRlc2NyaXB0b3IuYmFzZVR5cGU7XG4gICAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCA9IGJhc2U7XG4gICAgICB0aGlzLnBsb3RUeXBlID0gYmFzZTtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiB0aGlzLnJlc3VsdFRvYXN0S2V5LFxuICAgICAgICBzZXZlcml0eTogJ3dhcm4nLFxuICAgICAgICBzdW1tYXJ5OiBgJHtjb250cmlidXRpb24uZGVzY3JpcHRvci5wcm9kdWN0aW9uTGFiZWwgPz8gY29udHJpYnV0aW9uLmRlc2NyaXB0b3IubGFiZWx9IGlzIHVuYXZhaWxhYmxlYCxcbiAgICAgICAgZGV0YWlsOiAnVGhlIHBsb3QgbW9kZSBjb3VsZCBub3Qgc3RhcnQsIHNvIHRoZSBwbGFpbiBpbWFnZSBpcyBzaG93biBpbnN0ZWFkLiBTZWUgdGhlIGJyb3dzZXIgY29uc29sZSBmb3IgZGV0YWlscy4nLFxuICAgICAgfSk7XG4gICAgICB0aGlzLmRldGVjdENoYW5nZXNTYWZlbHkoKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBSdW4gY2hhbmdlIGRldGVjdGlvbjsgcmV0dXJucyB3aGF0IGl0IHRocmV3IChsb2dnZWQpLCBvciBudWxsLiAqL1xuICBwcml2YXRlIGRldGVjdENoYW5nZXNTYWZlbHkoKTogdW5rbm93biB8IG51bGwge1xuICAgIGlmICh0aGlzLmRlc3Ryb3lpbmcpIHJldHVybiBudWxsO1xuICAgIHRyeSB7XG4gICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ1t2aXN1YWxpemVyXSBjaGFuZ2UgZGV0ZWN0aW9uIGZhaWxlZCB3aGlsZSB1cGRhdGluZyBhIGNvbnRyaWJ1dGVkIHBsb3QgbW9kZSBwYW5lbC4nLCBlcnIpO1xuICAgICAgcmV0dXJuIGVyciA/PyBuZXcgRXJyb3IoJ2NoYW5nZSBkZXRlY3Rpb24gZmFpbGVkJyk7XG4gICAgfVxuICB9XG5cbiAgLy8g4pSA4pSAIGRpYWxvZyB0b29scyAoVE9PTEJBUl9UT09MUywga2luZDogJ2RpYWxvZycpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBUaGUgdG9vbGJhciBidXR0b246IG9wZW4gdGhlIHRvb2wncyBkaWFsb2csIG9yIGNsb3NlIGl0IGlmIGl0IGlzIG9wZW4uICovXG4gIHRvZ2dsZURpYWxvZ1Rvb2woaWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLm9wZW5EaWFsb2dUb29sSWQgPT09IGlkKSB7XG4gICAgICB0aGlzLmNsb3NlRGlhbG9nVG9vbCgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmNsb3NlRGlhbG9nVG9vbCgpO1xuICAgIGlmICghdGhpcy5kaWFsb2dUb29scy5zb21lKCh0KSA9PiB0LmlkID09PSBpZCkpIHJldHVybjtcbiAgICB0aGlzLm9wZW5EaWFsb2dUb29sSWQgPSBpZDtcbiAgICB0aGlzLnRvb2xEaWFsb2dzLmNsZWFyQ2xlYW51cEZhaWx1cmVzKCk7IC8vIGFuIGV4cGxpY2l0IG9wZW4gZ2V0cyBhIGZyZXNoIGF0dGVtcHRcbiAgICB0aGlzLmFjdGl2YXRlT3BlbkRpYWxvZ1Rvb2woKTtcbiAgfVxuXG4gIC8qKiBDbG9zZSB0aGUgb3BlbiBkaWFsb2cgdG9vbDogaXRzIGJvZHkgaXMgdG9ybiBkb3duLCB0aGVuIGl0cyBzZXNzaW9uIGVuZHMuICovXG4gIGNsb3NlRGlhbG9nVG9vbCgpOiB2b2lkIHtcbiAgICB0aGlzLm9wZW5EaWFsb2dUb29sSWQgPSBudWxsO1xuICAgIHRoaXMudG9vbERpYWxvZ3MuZGVhY3RpdmF0ZSgpO1xuICB9XG5cbiAgLyoqIFN0YXJ0IChvciByZXN0YXJ0LCBhZnRlciBhIHJlLXJlbmRlcikgdGhlIG9wZW4gZGlhbG9nIHRvb2wncyBzZXNzaW9uLiAqL1xuICBwcml2YXRlIGFjdGl2YXRlT3BlbkRpYWxvZ1Rvb2woKTogdm9pZCB7XG4gICAgY29uc3QgaWQgPSB0aGlzLm9wZW5EaWFsb2dUb29sSWQ7XG4gICAgY29uc3QgY29udHJpYnV0aW9uID0gaWQgPyB0aGlzLnRvb2xEaWFsb2dzLmZpbmQoaWQpIDogdW5kZWZpbmVkO1xuICAgIGlmICghY29udHJpYnV0aW9uIHx8IHRoaXMucGxvdFR5cGUgIT09IFBsb3RUeXBlLklNQUdFKSByZXR1cm47XG4gICAgaWYgKHRoaXMudG9vbERpYWxvZ3MuY3VycmVudD8uY29udHJpYnV0aW9uID09PSBjb250cmlidXRpb24gfHwgdGhpcy50b29sRGlhbG9ncy5wZW5kaW5nKSByZXR1cm47XG4gICAgY29uc3Qgdmlld3BvcnQgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFBsb3RNb2RlVmlld3BvcnQ/LigpID8/IG51bGw7XG4gICAgaWYgKCF2aWV3cG9ydCkge1xuICAgICAgY29uc29sZS5lcnJvcihgW3Zpc3VhbGl6ZXJdIGRpYWxvZyB0b29sICcke2lkfSc6IHRoZSBiYWNrZW5kIG9uIHNjcmVlbiBwcm92aWRlcyBubyB2aWV3cG9ydCB0byBkcmF3IG92ZXIuYCk7XG4gICAgICB0aGlzLmRpYWxvZ1Rvb2xGYWlsZWQoaWQhKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgY3R4OiBUb29sRGlhbG9nQ29udGV4dCA9IHtcbiAgICAgIHZpc3VhbGl6ZXI6IHRoaXMucGxvdFNlcnZpY2UsXG4gICAgICB2aWV3cG9ydCxcbiAgICAgIGltYWdlSW5mbyQ6IHRoaXMuc3RhdGUuZ2V0SW1hZ2VJbmZvJCgpLFxuICAgICAgdG9vbHM6IHRoaXMucGxvdE1vZGVUb29scyxcbiAgICB9O1xuICAgIHZvaWQgdGhpcy50b29sRGlhbG9ncy5hY3RpdmF0ZShjb250cmlidXRpb24sIGN0eCk7XG4gIH1cblxuICAvKiogVGhlIGxpdmUgZGlhbG9nIGJvZHkncyB0ZWFyZG93biwgYW5kIGEgcGVuZGluZyB3YWl0IGZvciBpdHMgaG9zdCB0byBjb25uZWN0LiAqL1xuICBwcml2YXRlIHRvb2xEaWFsb2dUZWFyZG93bjogKCgpID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgdG9vbERpYWxvZ0ZyYW1lOiBudW1iZXIgfCBudWxsID0gbnVsbDtcblxuICAvKipcbiAgICogVGhlIHNlc3Npb24gaXMgbGl2ZTogcmVuZGVyIHRoZSBkaWFsb2csIHRoZW4gbW91bnQgdGhlIHRvb2wncyBib2R5IG9uY2UgdGhlIGhvc3RcbiAgICogZWxlbWVudCBpcyBpbiB0aGUgZG9jdW1lbnQsIHNvIGBtb3VudCgpYCBjYW4gbWVhc3VyZSBpdCBhbmQgc3RhcnQgd2lkZ2V0cyB0aGF0IG5lZWRcbiAgICogYSBjb25uZWN0ZWQgZWxlbWVudC5cbiAgICovXG4gIHByaXZhdGUgc2hvd1Rvb2xEaWFsb2coYWN0aXZlOiBBY3RpdmVQbG90TW9kZSk6IHZvaWQge1xuICAgIGNvbnN0IHRvb2wgPSB0aGlzLmRpYWxvZ1Rvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IGFjdGl2ZS5jb250cmlidXRpb24uZGVzY3JpcHRvci50eXBlKTtcbiAgICBpZiAoIXRvb2wpIHJldHVybjtcbiAgICBjb25zdCBob3N0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgaG9zdC5jbGFzc05hbWUgPSAndG9vbC1kaWFsb2ctYm9keSc7XG4gICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgIHRoaXMudG9vbERpYWxvZyA9IHtcbiAgICAgICAgdGl0bGU6IHRvb2wuZGlhbG9nPy50aXRsZSA/PyB0b29sLmxhYmVsLFxuICAgICAgICB3aWR0aDogdG9vbC5kaWFsb2c/LndpZHRoID8/ICcyMnJlbScsXG4gICAgICAgIGhvc3QsXG4gICAgICB9O1xuICAgICAgY29uc3QgZXJyID0gdGhpcy5kZXRlY3RDaGFuZ2VzU2FmZWx5KCk7XG4gICAgICBpZiAoZXJyICE9PSBudWxsKSB7XG4gICAgICAgIHRoaXMudG9vbERpYWxvZ3MuZmFpbEFjdGl2ZShlcnIpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLm1vdW50VG9vbERpYWxvZ0JvZHkodG9vbCwgYWN0aXZlLCBob3N0LCAwKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBIb3cgbWFueSBhbmltYXRpb24gZnJhbWVzIHRvIHdhaXQgZm9yIHRoZSBkaWFsb2cgdG8gYXR0YWNoIHRoZSBib2R5J3MgaG9zdC4gKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgVE9PTF9ESUFMT0dfQVRUQUNIX0ZSQU1FUyA9IDYwO1xuXG4gIC8qKlxuICAgKiBNb3VudCBgdG9vbGAncyBib2R5IGludG8gYGhvc3RgIG9uY2UgaXQgaXMgY29ubmVjdGVkICh0aGUgZGlhbG9nIG1heSBhdHRhY2ggaXQgYVxuICAgKiBmcmFtZSBsYXRlciwgd2hlbiBpdCBtb3ZlcyBpdHNlbGYgdG8gYDxib2R5PmApLiBJZiBpdCBuZXZlciBjb25uZWN0cyB3aXRoaW5cbiAgICoge0BsaW5rIFRPT0xfRElBTE9HX0FUVEFDSF9GUkFNRVN9IGl0IGlzIG1vdW50ZWQgYW55d2F5LCB3aXRoIGEgd2FybmluZywgcmF0aGVyIHRoYW5cbiAgICogbGVhdmluZyBhbiBlbXB0eSBkaWFsb2cuIEEgdGhyb3dpbmcgYG1vdW50KClgIGZhaWxzIHRoZSBzZXNzaW9uIGxpa2UgYSBmYWlsZWQgc3RhcnQuXG4gICAqL1xuICBwcml2YXRlIG1vdW50VG9vbERpYWxvZ0JvZHkodG9vbDogVG9vbGJhckRpYWxvZ1Rvb2xDb250cmlidXRpb24sIGFjdGl2ZTogQWN0aXZlUGxvdE1vZGUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3N0OiBIVE1MRWxlbWVudCwgZnJhbWVzOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnRvb2xEaWFsb2dGcmFtZSA9IG51bGw7XG4gICAgLy8gVGhlIHNlc3Npb24gZW5kZWQgKG9yIHdhcyByZXBsYWNlZCkgd2hpbGUgd2FpdGluZzogbm90aGluZyB0byBtb3VudCBpbnRvLlxuICAgIGlmICh0aGlzLnRvb2xEaWFsb2dzLmN1cnJlbnQgIT09IGFjdGl2ZSB8fCB0aGlzLnRvb2xEaWFsb2c/Lmhvc3QgIT09IGhvc3QpIHJldHVybjtcbiAgICBpZiAoIWhvc3QuaXNDb25uZWN0ZWQpIHtcbiAgICAgIGlmIChmcmFtZXMgPCBWaXN1YWxpemVyQ29tcG9uZW50LlRPT0xfRElBTE9HX0FUVEFDSF9GUkFNRVMpIHtcbiAgICAgICAgdGhpcy50b29sRGlhbG9nRnJhbWUgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoXG4gICAgICAgICAgKCkgPT4gdGhpcy5tb3VudFRvb2xEaWFsb2dCb2R5KHRvb2wsIGFjdGl2ZSwgaG9zdCwgZnJhbWVzICsgMSkpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zb2xlLndhcm4oYFt2aXN1YWxpemVyXSBkaWFsb2cgdG9vbCAnJHt0b29sLmlkfSc6IHRoZSBkaWFsb2cgYm9keSBuZXZlciBhdHRhY2hlZDsgbW91bnRpbmcgaXQgZGV0YWNoZWQuYCk7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBjb25zdCB0ZWFyZG93biA9IHRvb2wubW91bnQoaG9zdCwgYWN0aXZlLmN0eCBhcyBUb29sRGlhbG9nQ29udGV4dCwgYWN0aXZlLnNlc3Npb24pO1xuICAgICAgdGhpcy50b29sRGlhbG9nVGVhcmRvd24gPSB0eXBlb2YgdGVhcmRvd24gPT09ICdmdW5jdGlvbicgPyB0ZWFyZG93biA6IG51bGw7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGBbdmlzdWFsaXplcl0gZGlhbG9nIHRvb2wgJyR7dG9vbC5pZH0nIG1vdW50KCkgdGhyZXcuYCwgZXJyKTtcbiAgICAgIHRoaXMudG9vbERpYWxvZ3MuZmFpbEFjdGl2ZShlcnIpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBUaGUgc2Vzc2lvbiBpcyBhYm91dCB0byBlbmQ6IHRlYXIgdGhlIGJvZHkgZG93biBmaXJzdCwgdGhlbiBkcm9wIHRoZSBkaWFsb2cuICovXG4gIHByaXZhdGUgaGlkZVRvb2xEaWFsb2coKTogdm9pZCB7XG4gICAgaWYgKHRoaXMudG9vbERpYWxvZ0ZyYW1lICE9PSBudWxsKSBjYW5jZWxBbmltYXRpb25GcmFtZSh0aGlzLnRvb2xEaWFsb2dGcmFtZSk7XG4gICAgdGhpcy50b29sRGlhbG9nRnJhbWUgPSBudWxsO1xuICAgIGNvbnN0IHRlYXJkb3duID0gdGhpcy50b29sRGlhbG9nVGVhcmRvd247XG4gICAgdGhpcy50b29sRGlhbG9nVGVhcmRvd24gPSBudWxsO1xuICAgIGlmICh0ZWFyZG93bikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgdGVhcmRvd24oKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdbdmlzdWFsaXplcl0gZGlhbG9nIHRvb2wgYm9keSB0ZWFyZG93biB0aHJldy4nLCBlcnIpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoIXRoaXMudG9vbERpYWxvZykgcmV0dXJuO1xuICAgIHRoaXMudG9vbERpYWxvZy5ob3N0LnJlbW92ZSgpO1xuICAgIHRoaXMudG9vbERpYWxvZyA9IG51bGw7XG4gICAgaWYgKCF0aGlzLmRlc3Ryb3lpbmcpIHRoaXMuZGV0ZWN0Q2hhbmdlc1NhZmVseSgpO1xuICB9XG5cbiAgLyoqIEEgZGlhbG9nIHRvb2wgY291bGQgbm90IHN0YXJ0OiBjbG9zZSBpdCBhbmQgc2F5IHNvLiAqL1xuICBwcml2YXRlIGRpYWxvZ1Rvb2xGYWlsZWQoaWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLmRlc3Ryb3lpbmcgfHwgdGhpcy5vcGVuRGlhbG9nVG9vbElkICE9PSBpZCkgcmV0dXJuO1xuICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICB0aGlzLm9wZW5EaWFsb2dUb29sSWQgPSBudWxsO1xuICAgICAgY29uc3QgdG9vbCA9IHRoaXMuZGlhbG9nVG9vbHMuZmluZCgodCkgPT4gdC5pZCA9PT0gaWQpO1xuICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICBrZXk6IHRoaXMucmVzdWx0VG9hc3RLZXksXG4gICAgICAgIHNldmVyaXR5OiAnd2FybicsXG4gICAgICAgIHN1bW1hcnk6IGAke3Rvb2w/LmxhYmVsID8/IGlkfSBpcyB1bmF2YWlsYWJsZWAsXG4gICAgICAgIGRldGFpbDogJ1RoZSB0b29sIGNvdWxkIG5vdCBzdGFydC4gU2VlIHRoZSBicm93c2VyIGNvbnNvbGUgZm9yIGRldGFpbHMuJyxcbiAgICAgIH0pO1xuICAgICAgdGhpcy5kZXRlY3RDaGFuZ2VzU2FmZWx5KCk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogV2hlcmUgdGhlIG9wZW4gZGlhbG9nIHRvb2wncyBob3N0IGVsZW1lbnQgaXMgYXR0YWNoZWQgb25jZSBpdHMgZGlhbG9nIHJlbmRlcnMuICovXG4gIEBWaWV3Q2hpbGQoJ3Rvb2xEaWFsb2dTbG90JylcbiAgc2V0IHRvb2xEaWFsb2dTbG90KHJlZjogRWxlbWVudFJlZjxIVE1MRWxlbWVudD4gfCB1bmRlZmluZWQpIHtcbiAgICBjb25zdCBob3N0ID0gdGhpcy50b29sRGlhbG9nPy5ob3N0O1xuICAgIGlmIChyZWYgJiYgaG9zdCAmJiBob3N0LnBhcmVudEVsZW1lbnQgIT09IHJlZi5uYXRpdmVFbGVtZW50KSByZWYubmF0aXZlRWxlbWVudC5hcHBlbmRDaGlsZChob3N0KTtcbiAgfVxuXG4gIC8qKiBUb29sYmFyIHRvb2xzIGZvciBjb250cmlidXRlZCBtb2Rlcy4gQ2FsbHMgY2FuIGNvbWUgZnJvbSBvdXRzaWRlIEFuZ3VsYXIsIGhlbmNlIHRoZSB6b25lLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHBsb3RNb2RlVG9vbHM6IFBsb3RNb2RlVG9vbHMgPSB7XG4gICAgYXJtQnJ1c2g6IChicnVzaENsYXNzPzogUGxvdE1vZGVCcnVzaENsYXNzKSA9PiB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgdGhpcy5icnVzaENsYXNzID0geyBsYWJlbDogYnJ1c2hDbGFzcz8ubGFiZWwsIGNvbG9yOiBicnVzaENsYXNzPy5jb2xvciB9O1xuICAgICAgaWYgKHRoaXMuYWN0aXZlRHJhZ01vZGUgPT09ICdicnVzaCcpIHtcbiAgICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRCcnVzaE9wdGlvbnMoeyBzaXplOiB0aGlzLmJydXNoU2l6ZSwgLi4udGhpcy5icnVzaENsYXNzIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5hcHBseURyYWdNb2RlKCdicnVzaCcpO1xuICAgICAgfVxuICAgICAgdGhpcy5jZHIubWFya0ZvckNoZWNrKCk7XG4gICAgfSksXG4gICAgZGlzYXJtOiAoKSA9PiB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgdGhpcy5icnVzaENsYXNzID0gbnVsbDtcbiAgICAgIGlmICh0aGlzLmFjdGl2ZURyYWdNb2RlICE9PSBudWxsKSB0aGlzLmFwcGx5RHJhZ01vZGUobnVsbCk7XG4gICAgICB0aGlzLmNkci5tYXJrRm9yQ2hlY2soKTtcbiAgICB9KSxcbiAgICBhY3RpdmVUb29sJDogdGhpcy5zZXNzaW9uLmdldEFjdGl2ZVRvb2wkKCksXG4gIH07XG5cbiAgLyoqIERlYWN0aXZhdGUgd2hhdGV2ZXIgdG9vbCBpcyBhcm1lZCBhbmQgY2xlYXIgZXZlcnkgdG9vbCBtb2RlLiAqL1xuICBwcml2YXRlIGRlYWN0aXZhdGVBY3RpdmVUb29sKCkge1xuICAgIHRoaXMuYnJ1c2hDbGFzcyA9IG51bGw7XG4gICAgdGhpcy5hY3RpdmVEcmFnTW9kZSA9IG51bGw7XG4gICAgdGhpcy5zZXNzaW9uLnNldEFjdGl2ZVRvb2wobnVsbCk7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25PdmVybGF5KCk/LnNldE1vZGUoJ25vbmUnKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldERyYWdNb2RlKGZhbHNlKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFpvb21Ub0JveE1vZGUoZmFsc2UpO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0V2FuZE1vZGUoZmFsc2UpO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0QnJ1c2hNb2RlKGZhbHNlKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNhbVBvaW50TW9kZShmYWxzZSk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRWZXJ0ZXhFcmFzZXJNb2RlKGZhbHNlKTtcbiAgfVxufVxuXG4iLCI8IS0tIFNBTSB0b2FzdDogbGl2ZSBzdGF0dXMgKyBlbmNvZGVyLWRvd25sb2FkIHByb2dyZXNzIGJhciAoaml0LXVpIzkwKS4gU3RpY2t5XG4gICAgIHdoaWxlIGEgc2VnbWVudCBydW5zOyBzdGF5cyBvcGVuIHVudGlsIHRoZSBiYXIgcmVhY2hlcyAxMDAlIGFuZCB0aGUgcnVuIGVuZHMuIC0tPlxuPHAtdG9hc3QgW2tleV09XCJzYW1Ub2FzdEtleVwiIHBvc2l0aW9uPVwiYm90dG9tLXJpZ2h0XCI+XG4gIDxuZy10ZW1wbGF0ZSBsZXQtbWVzc2FnZSBwVGVtcGxhdGU9XCJtZXNzYWdlXCI+XG4gICAgPGRpdiBzdHlsZT1cImZsZXg6IDE7IG1pbi13aWR0aDogMTZyZW1cIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJmb250LWJvbGQgbWItMVwiPnt7IG1lc3NhZ2Uuc3VtbWFyeSB9fTwvZGl2PlxuICAgICAgPGRpdj57eyBzYW1TdGF0dXMgfHwgbWVzc2FnZS5kZXRhaWwgfX08L2Rpdj5cbiAgICAgIDxwLXByb2dyZXNzQmFyICpuZ0lmPVwic2FtRG93bmxvYWRpbmdcIiBbdmFsdWVdPVwic2FtUHJvZ3Jlc3NcIiBzdHlsZUNsYXNzPVwibXQtMiBzYW0tcHJvZ3Jlc3NcIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgICA8cC1wcm9ncmVzc0JhciAqbmdJZj1cInNhbUJ1c3kgJiYgIXNhbURvd25sb2FkaW5nXCIgbW9kZT1cImluZGV0ZXJtaW5hdGVcIiBzdHlsZUNsYXNzPVwibXQtMlwiXG4gICAgICAgICAgICAgICAgICAgICBbc3R5bGVdPVwieyBoZWlnaHQ6ICc0cHgnIH1cIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgPC9kaXY+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtdG9hc3Q+XG5cbjwhLS0gUmVzdWx0L2Vycm9yIG5vdGljZXMgZnJvbSB0aGUgbGlicmFyeSBpdHNlbGYgKHNlZ21lbnRhdGlvbiBvdXRjb21lLCByZWdpb25cbiAgICAgdmFsaWRhdGlvbikuIFJlbmRlcmVkIGhlcmUgc28gdGhleSBkbyBub3QgZGVwZW5kIG9uIHRoZSBob3N0IG1vdW50aW5nIGFcbiAgICAga2V5bGVzcyA8cC10b2FzdD4g4oCUIHdpdGhvdXQgdGhpcyB0aGUgbWVzc2FnZXMgd2VudCBub3doZXJlIGluIGFueSBob3N0IGJ1dFxuICAgICBqaXQtdWksIGFuZCBhIGZhaWxlZCBydW4gbG9va2VkIGxpa2UgYSBidXR0b24gdGhhdCBkaWQgbm90aGluZy4gLS0+XG48cC10b2FzdCBba2V5XT1cInJlc3VsdFRvYXN0S2V5XCIgcG9zaXRpb249XCJib3R0b20tcmlnaHRcIj48L3AtdG9hc3Q+XG5cbjwhLS0gU2hhcmVkIG91dGxldHMgZm9yIG5vdGljZXMgcmFpc2VkIHdoZXJlIHRoZXJlIGlzIG5vIHBlci1pbnN0YW5jZSBrZXkgdG8gdXNlOlxuICAgICB0aGUgcm9vdC1wcm92aWRlZCBQbG90bHlTZXJ2aWNlIGFuZCB0aGUgcmVnaW9uLWVkaXRvciBkaWFsb2cuIFJlbmRlcmVkIGJ5IGFcbiAgICAgc2luZ2xlIHZpc3VhbGl6ZXIgKG93bnNTaGFyZWRUb2FzdHMpIHNvIGEgbWVzc2FnZSBmcm9tIHRoZSByb290IHNlcnZpY2UgaXNcbiAgICAgbm90IHNob3duIG9uY2UgcGVyIGxpdmUgdmlld2VyLiAtLT5cbjxwLXRvYXN0ICpuZ0lmPVwib3duc1NoYXJlZFRvYXN0c1wiIFtrZXldPVwidml6VG9hc3RLZXlcIj48L3AtdG9hc3Q+XG48cC10b2FzdCAqbmdJZj1cIm93bnNTaGFyZWRUb2FzdHNcIiBba2V5XT1cInZpekFsZXJ0VG9hc3RLZXlcIiBwb3NpdGlvbj1cImNlbnRlclwiPjwvcC10b2FzdD5cblxuPCEtLSBUb29sYmFyIGRvY2s6IGRvY2tlZCBhY3Jvc3MgdGhlIHRvcCBieSBkZWZhdWx0OyBkcmFnIHRoZSBoYW5kbGUgdG8gZGV0YWNoXG4gICAgIGl0IGludG8gYSBmbG9hdGluZywgbW92YWJsZSB3aW5kb3cgKGZyZWVzIHRoZSB0b3Agcm93IGZvciB0aGUgaW1hZ2UpLiAtLT5cbjxkaXYgY2xhc3M9XCJ0b29sYmFyLWRvY2tcIiBbY2xhc3MuZmxvYXRpbmddPVwidG9vbGJhckZsb2F0aW5nXCJcbiAgICAgW3N0eWxlLmxlZnQucHhdPVwidG9vbGJhckZsb2F0aW5nID8gdG9vbGJhclBvcy54IDogbnVsbFwiXG4gICAgIFtzdHlsZS50b3AucHhdPVwidG9vbGJhckZsb2F0aW5nID8gdG9vbGJhclBvcy55IDogbnVsbFwiPlxuICA8ZGl2IGNsYXNzPVwidG9vbGJhci1kcmFnLWhhbmRsZVwiIChtb3VzZWRvd24pPVwib25Ub29sYmFyRHJhZ1N0YXJ0KCRldmVudClcIlxuICAgICAgIHBUb29sdGlwPVwiRHJhZyB0byBmbG9hdCB0aGUgdG9vbGJhclwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiPlxuICAgIDxpIGNsYXNzPVwicGkgcGktYmFyc1wiPjwvaT5cbiAgPC9kaXY+XG48cGxvdHRpbmctdG9vbGJhclxuICBbaW1hZ2VJbmZvXT1cImltYWdlSW5mb1wiXG4gIFtoYXNTcGF0aWFsRGF0YXNldF09XCJoYXNTcGF0aWFsRGF0YXNldFwiXG4gIFtwbG90VHlwZU9wdGlvbnNdPVwicGxvdFR5cGVNZW51XCJcbiAgW3NlbGVjdGVkUGxvdFR5cGVdPVwic2VsZWN0ZWRQbG90VHlwZUlkXCJcbiAgW2Jhc2VQbG90VHlwZV09XCJiYXNlUGxvdFR5cGVcIlxuICBbaXNvUmFuZ2VdPVwiaXNvUmFuZ2VcIlxuICBbbWF4SW5kZXhdPVwibWF4SW5kZXhcIlxuICBbekluZGV4XT1cInpJbmRleFwiXG4gIFtpc0hlYXRtYXBdPVwiaXNIZWF0bWFwXCJcbiAgW2lzM2RSZWdpb25zXT1cImlzU3BhdGlhbDNkTW9kZVwiXG4gIFthY3RpdmVEcmFnTW9kZV09XCJhY3RpdmVEcmFnTW9kZVwiXG4gIFthY3RpdmVTdXJmYWNlM2RNb2RlXT1cImFjdGl2ZVN1cmZhY2UzZE1vZGVcIlxuICBbYXhlc1Zpc2libGVdPVwiYXhlc1Zpc2libGVcIlxuICBbd2lyZWZyYW1lQWN0aXZlXT1cIndpcmVmcmFtZUFjdGl2ZVwiXG4gIFtyZXNvbHV0aW9uU2NhbGVdPVwicmVzb2x1dGlvblNjYWxlXCJcbiAgW3dhbmRTZW5zaXRpdml0eV09XCJ3YW5kU2Vuc2l0aXZpdHlcIlxuICBbYnJ1c2hTaXplXT1cImJydXNoU2l6ZVwiXG4gIFtzYW1Nb2RlbHNdPVwic2FtTW9kZWxzXCJcbiAgW3NhbU1vZGVsSWRdPVwic2FtTW9kZWxJZFwiXG4gIFtjb250cmlidXRlZFRvb2xzXT1cImNvbnRyaWJ1dGVkVG9vbHNcIlxuICBbZGlhbG9nVG9vbHNdPVwiZGlhbG9nVG9vbHNcIlxuICBbb3BlbkRpYWxvZ1Rvb2xJZF09XCJvcGVuRGlhbG9nVG9vbElkXCJcbiAgKHRvZ2dsZURpYWxvZ1Rvb2wpPVwidG9nZ2xlRGlhbG9nVG9vbCgkZXZlbnQpXCJcbiAgW3Rvb2xNb2RlbElkc109XCJ0b29sTW9kZWxJZHNcIlxuICBbdmVydGV4RXJhc2VyUmFkaXVzXT1cInZlcnRleEVyYXNlclJhZGl1c1wiXG4gIFtjYW5VbmRvXT1cImNhblVuZG9SZWdpb25cIlxuICBbY2FuUmVkb109XCJjYW5SZWRvUmVnaW9uXCJcbiAgW3N0YWNrT3B0aW9uc109XCJzdGFja09wdGlvbnNcIlxuICBbdG9vbHNdPVwidG9vbGJhclRvb2xzXCJcbiAgW2ltYWdlU21vb3RoaW5nRW5hYmxlZF09XCJpbWFnZVNtb290aGluZ0VuYWJsZWRcIlxuICAoc2VsZWN0UGxvdFR5cGUpPVwib25TZWxlY3RQbG90VHlwZSgkZXZlbnQpXCJcbiAgKHRvZ2dsZUltYWdlU21vb3RoaW5nKT1cIm9uVG9nZ2xlSW1hZ2VTbW9vdGhpbmcoKVwiXG4gIChhZGRQcm9maWxlTGluZSk9XCJhZGRQcm9maWxlTGluZSgpXCJcbiAgKGlzb1JhbmdlQ2hhbmdlKT1cIm9uSXNvUmFuZ2VDaGFuZ2UoJGV2ZW50KVwiXG4gIChvcGVuQ2hhbm5lbEhpc3RvZ3JhbSk9XCJvcGVuQ2hhbm5lbEhpc3RvZ3JhbSgpXCJcbiAgKG9wZW5TcGF0aWFsQ29udHJvbHMpPVwib3BlblNwYXRpYWxDb250cm9scygpXCJcbiAgKG9wZW5SZWdpb25FZGl0b3IpPVwib3BlblJlZ2lvbkVkaXRvcigpXCJcbiAgKHNlbGVjdFN0YWNrT3B0aW9uKT1cInNlbGVjdFN0YWNrT3B0aW9uKCRldmVudClcIlxuICAoelNjcnViKT1cIm9uWlNjcnViKCRldmVudClcIlxuICAoelNsaWRlKT1cIm9uWlNsaWRlKCRldmVudClcIlxuICAoekluZGV4SW5wdXQpPVwiekluZGV4ID0gJGV2ZW50OyB1cGRhdGVaSW5kZXgoKVwiXG4gIChyZWxvYWRBbmRQbG90KT1cInJlbG9hZEFuZFBsb3QoKVwiXG4gIChkb3dubG9hZEltYWdlKT1cImRvd25sb2FkSW1hZ2UoKVwiXG4gIChhdXRvc2NhbGVJbWFnZSk9XCJhdXRvc2NhbGVJbWFnZSgpXCJcbiAgKHRvZ2dsZURyYWdNb2RlKT1cInRvZ2dsZURyYWdNb2RlKCRldmVudClcIlxuICAoem9vbUluKT1cInpvb21JbigpXCJcbiAgKHpvb21PdXQpPVwiem9vbU91dCgpXCJcbiAgKHRvZ2dsZVN1cmZhY2UzZE1vZGUpPVwidG9nZ2xlU3VyZmFjZTNkTW9kZSgkZXZlbnQpXCJcbiAgKHJlc2V0U3VyZmFjZUNhbWVyYSk9XCJyZXNldFN1cmZhY2VDYW1lcmEoKVwiXG4gICh0b2dnbGVBeGVzKT1cInRvZ2dsZUF4ZXMoKVwiXG4gICh0b2dnbGVXaXJlZnJhbWUpPVwidG9nZ2xlV2lyZWZyYW1lKClcIlxuICAoc2VsZWN0UmVzb2x1dGlvbik9XCJzZWxlY3RSZXNvbHV0aW9uKCRldmVudClcIlxuICAoZGVsZXRlUmVnaW9uKT1cImRlbGV0ZVJlZ2lvbigpXCJcbiAgKHVuZG9SZWdpb24pPVwidW5kb1JlZ2lvbigpXCJcbiAgKHJlZG9SZWdpb24pPVwicmVkb1JlZ2lvbigpXCJcbiAgKHNlZ21lbnRSZWdpb25zKT1cInNlZ21lbnRSZWdpb25zKClcIlxuICAoc2VnbWVudENlbGxwb3NlKT1cInNlZ21lbnRDZWxscG9zZSgpXCJcbiAgKHNhbU1vZGVsQ2hhbmdlKT1cIm9uU2FtTW9kZWxDaGFuZ2UoJGV2ZW50KVwiXG4gIChydW5Ub29sKT1cInJ1blRvb2woJGV2ZW50KVwiXG4gICh0b29sTW9kZWxDaGFuZ2UpPVwib25Ub29sTW9kZWxDaGFuZ2UoJGV2ZW50KVwiXG4gIChvcGVuVG9vbFBhcmFtcyk9XCJvcGVuVG9vbFBhcmFtcygkZXZlbnQpXCJcbiAgKHdhbmRTZW5zaXRpdml0eUNoYW5nZSk9XCJvbldhbmRTZW5zaXRpdml0eUNoYW5nZSgkZXZlbnQpXCJcbiAgKGJydXNoU2l6ZUNoYW5nZSk9XCJvbkJydXNoU2l6ZUNoYW5nZSgkZXZlbnQpXCJcbiAgKHZlcnRleEVyYXNlclJhZGl1c0NoYW5nZSk9XCJvblZlcnRleEVyYXNlclJhZGl1c0NoYW5nZSgkZXZlbnQpXCI+XG4gIDwhLS0gSG9zdC1wcm9qZWN0ZWQgY3VzdG9tIHRvb2xiYXIgYnV0dG9ucywgZm9yd2FyZGVkIGludG8gdGhlIHRvb2xiYXIuIC0tPlxuICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG48L3Bsb3R0aW5nLXRvb2xiYXI+XG4gIDxwLWJ1dHRvbiAqbmdJZj1cInRvb2xiYXJGbG9hdGluZ1wiIGNsYXNzPVwidG9vbGJhci1kb2NrLWJ0blwiIGljb249XCJwaSBwaS10aHVtYnRhY2tcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgIHBUb29sdGlwPVwiRG9jayB0byB0b3BcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIiAob25DbGljayk9XCJkb2NrVG9vbGJhcigpXCI+XG4gIDwvcC1idXR0b24+XG48L2Rpdj5cblxuPHAtY29udGV4dE1lbnUgI2NtIFttb2RlbF09XCJjb250ZXh0TWVudUl0ZW1zXCIgYXBwZW5kVG89XCJib2R5XCI+PC9wLWNvbnRleHRNZW51PlxuPCEtLSBObyBgaC1mdWxsYCBoZXJlOiBQcmltZUZsZXgncyBoZWlnaHQ6MTAwJSAhaW1wb3J0YW50IG1hZGUgI3Bsb3QgYXMgdGFsbCBhc1xuICAgICB0aGUgd2hvbGUgaG9zdCwgc28gdW5kZXIgdGhlIGRvY2tlZCB0b29sYmFyIGl0cyBib3R0b20gdG9vbGJhci1oZWlnaHQgc3RyaXBcbiAgICAgKGluY2wuIHRoZSBPU0Qgc2NhbGUgYmFyIGF0IGJvdHRvbS1sZWZ0KSB3YXMgY2xpcHBlZCBieSB0aGUgaG9zdCdzXG4gICAgIG92ZXJmbG93OmhpZGRlbi4gVGhlIGNvbXBvbmVudCBzdHlsZXNoZWV0IHNpemVzIGl0ICgudml6LXBsb3QgeyBmbGV4OiAxIH0pLiAtLT5cbjxkaXYgY2xhc3M9XCJ3LWZ1bGwgcmVsYXRpdmUgdml6LXBsb3RcIiBbaWRdPVwicGxvdERpdk5hbWVcIj5cbiAgPGRpdiAqbmdJZj1cImltZ0xvYWRpbmcgfHwgc3RhY2tMb2FkaW5nIHx8IGlzQ2FjaGluZ1wiIGlkPVwib3ZlcmxheVwiPlxuICAgIDwhLS0gRGV0ZXJtaW5hdGUgc3RhY2svdm9sdW1lIHByb2dyZXNzIHRha2VzIHByZWNlZGVuY2U6IHdoaWxlIGl0J3MgYWN0aXZlIHNob3cgT05MWSB0aGVcbiAgICAgICAgIHByb2dyZXNzIGJhciArIFwiTG9hZGluZyBmcmFtZXMuLi5cIiwgbm90IHRoZSBnZW5lcmljIHNwaW5uZXIvXCJMb2FkaW5nIGltYWdlLi4uXCIgb24gdG9wIG9mXG4gICAgICAgICBpdCAoYm90aCB3ZXJlIHJlbmRlcmluZyBhdCBvbmNlIGZvciBuYXBhcmkgdm9sdW1lL2lzb3N1cmZhY2UgbG9hZHMpLiAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwic3RhY2tMb2FkaW5nOyBlbHNlIGltZ0xvYWRpbmdUcGxcIj5cbiAgICAgIDxwLXByb2dyZXNzQmFyIGNsYXNzPVwicHJvZ3Jlc3NcIiBbdmFsdWVdPVwibG9hZGluZ1BlcmNlbnRhZ2VcIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRleHQtbG9hZGluZ1wiPkxvYWRpbmcgZnJhbWVzLi4uPC9zcGFuPlxuICAgIDwvbmctY29udGFpbmVyPlxuICAgIDxuZy10ZW1wbGF0ZSAjaW1nTG9hZGluZ1RwbD5cbiAgICAgIDwhLS0gQ2FjaGUtY29weSBwcm9ncmVzcyBpcyBzaG93biB3aGVuZXZlciBhIGNhY2hlIGRvd25sb2FkIGlzIGFjdGl2ZSAoaXNDYWNoaW5nKSwgaW5kZXBlbmRlbnRcbiAgICAgICAgICAgb2YgYGltZ0xvYWRpbmdgLCBzbyBpdCdzIG5ldmVyIGRyb3BwZWQvbWFza2VkIG1pZC1kb3dubG9hZC4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNDYWNoaW5nOyBlbHNlIHNwaW5uZXJMb2FkaW5nXCI+XG4gICAgICAgIDwhLS0gU3RhZ2UtYXdhcmUgbWVzc2FnZSBjb21lcyBmcm9tIENhY2hlUHJvZ3Jlc3NTZXJ2aWNlIHVwZGF0aW5nXG4gICAgICAgICAgICAgaW1hZ2VMb2FkaW5nTWVzc2FnZSQgKFwiQ2FjaGluZyBpbWFnZS4uLlwiIGR1cmluZyB0aGUgR0NTIGRvd25sb2FkLFxuICAgICAgICAgICAgIHRoZW4gXCJHZW5lcmF0aW5nIHByZXZpZXcuLi5cIiBkdXJpbmcgcGVyLXNsaWNlIHRodW1ibmFpbCBnZW4pLiBPbmNlIHRoZVxuICAgICAgICAgICAgIGRvd25sb2FkIGhpdHMgMTAwJSB0aGUgcmVtYWluaW5nIHdvcmsgKHNlcnZlciBwcmV2aWV3L3RpbGUgZ2VuZXJhdGlvbilcbiAgICAgICAgICAgICBoYXMgbm8gZGV0ZXJtaW5hdGUgcHJvZ3Jlc3MsIHNvIGZhbGwgdGhyb3VnaCB0byB0aGUgc3Bpbm5lciB0ZW1wbGF0ZVxuICAgICAgICAgICAgIHJhdGhlciB0aGFuIHBhcmtpbmcgYSBmdWxsIGJhciB1bmRlciBcIkxvYWRpbmcgaW1hZ2UuLi5cIi4gLS0+XG4gICAgICAgIDxwLXByb2dyZXNzQmFyIGNsYXNzPVwicHJvZ3Jlc3NcIiBbdmFsdWVdPVwiY2FjaGVQcm9ncmVzc1wiPjwvcC1wcm9ncmVzc0Jhcj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LWxvYWRpbmdcIj57e2xvYWRpbmdNZXNzYWdlfX08L3NwYW4+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgIDxuZy10ZW1wbGF0ZSAjc3Bpbm5lckxvYWRpbmc+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJpbWdMb2FkaW5nXCIgY2xhc3M9XCJzcGlubmVyXCI+PC9kaXY+XG4gICAgICAgIDxzcGFuICpuZ0lmPVwiaW1nTG9hZGluZyAmJiAhem9vbVwiIGNsYXNzPVwidGV4dC1sb2FkaW5nXCI+e3tsb2FkaW5nTWVzc2FnZX19PC9zcGFuPlxuICAgICAgICA8c3BhbiAqbmdJZj1cImltZ0xvYWRpbmcgJiYgem9vbVwiIGNsYXNzPVwiem9vbS10ZXh0LWxvYWRpbmdcIj57e2xvYWRpbmdNZXNzYWdlfX08L3NwYW4+XG4gICAgICA8L25nLXRlbXBsYXRlPlxuICAgIDwvbmctdGVtcGxhdGU+XG4gICAgPHAtYnV0dG9uICpuZ0lmPVwic3RhY2tMb2FkaW5nIHx8IGltZ0xvYWRpbmdcIiBjbGFzcz1cImNhbmNlbC1sb2FkaW5nIFwiIGxhYmVsPVwiQ2FuY2VsXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiY2FuY2VsTG9hZGluZygpXCI+XG4gICAgPC9wLWJ1dHRvbj5cbiAgPC9kaXY+XG4gIDwhLS0gVHJhbnNsdWNlbnQgXCJzaGFycGVuaW5nXCIgb3ZlcmxheSBzaG93biBhZnRlciB0aGUgc21hbGwtdGllciBwcmV2aWV3IGlzXG4gICAgICAgb24gc2NyZWVuIHdoaWxlIHRoZSBsYXJnZS10aWVyIHJlbmRlciBpcyBzdGlsbCBsb2FkaW5nIGluIHRoZVxuICAgICAgIGJhY2tncm91bmQuIExldHMgdGhlIHVzZXIgc2VlIHRoZSBibHVycnkgaW1hZ2UgdW5kZXJuZWF0aCBidXQgbWFrZXNcbiAgICAgICBpdCBjbGVhciBzb21ldGhpbmcgaXMgc3RpbGwgbG9hZGluZyBzbyB0aGV5IGRvbid0IG1pc3Rha2UgdGhlXG4gICAgICAgbG93LXJlcyByZW5kZXIgZm9yIHRoZSBmaW5hbCBpbWFnZS4gLS0+XG4gIDxkaXYgKm5nSWY9XCJzaGFycGVuaW5nICYmICFpbWdMb2FkaW5nICYmICFzdGFja0xvYWRpbmcgJiYgIWlzQ2FjaGluZ1wiIGlkPVwic2hhcnBlbmluZy1vdmVybGF5XCI+XG4gICAgPGRpdiBjbGFzcz1cInNwaW5uZXJcIj48L2Rpdj5cbiAgICA8c3BhbiBjbGFzcz1cInRleHQtbG9hZGluZ1wiPlNoYXJwZW5pbmcgcHJldmlldy4uLjwvc3Bhbj5cbiAgPC9kaXY+XG5cbjwvZGl2PlxuXG48IS0tIEZsb2F0aW5nLCBkcmFnZ2FibGUgaW50ZW5zaXR5LXByb2ZpbGUgaW5zZXQuIFNob3duIGluIExJTkUgbW9kZTsgdGhlIGNoYXJ0XG4gICAgIHVwZGF0ZXMgbGl2ZSBhcyB0aGUgbGluZSBST0kgb24gdGhlIGltYWdlIGlzIG1vdmVkLiBLZXB0IE9VVFNJREUgI3Bsb3QgYW5kXG4gICAgIHBvc2l0aW9uZWQgZml4ZWQgc28gaXQgZmxvYXRzIG92ZXIgdGhlIHdob2xlIHBhZ2UgKG5vdCBjbGlwcGVkIHRvIHRoZVxuICAgICBkaWFncmFtL2NhbnZhcyBhcmVhKS4gLS0+XG48ZGl2ICpuZ0lmPVwiaGFzUHJvZmlsZXNcIiBjbGFzcz1cImludGVuc2l0eS1wYW5lbFwiXG4gICAgIFtzdHlsZS5sZWZ0LnB4XT1cInByb2ZpbGVQYW5lbFBvcy54XCIgW3N0eWxlLnRvcC5weF09XCJwcm9maWxlUGFuZWxQb3MueVwiPlxuICA8ZGl2IGNsYXNzPVwiaW50ZW5zaXR5LXBhbmVsLWhlYWRlclwiIChtb3VzZWRvd24pPVwib25Qcm9maWxlUGFuZWxEcmFnU3RhcnQoJGV2ZW50KVwiPlxuICAgIDxzcGFuPkludGVuc2l0eSBwcm9maWxlPC9zcGFuPlxuICAgIDxpIGNsYXNzPVwicGkgcGktYXJyb3dzLWFsdFwiPjwvaT5cbiAgPC9kaXY+XG4gIDxkaXYgW2lkXT1cImludGVuc2l0eUluc2V0RGl2XCIgY2xhc3M9XCJpbnRlbnNpdHktcGFuZWwtcGxvdFwiPjwvZGl2PlxuPC9kaXY+XG5cbjwhLS0gQ2hhbm5lbHMgJiBIaXN0b2dyYW06IG5vbi1tb2RhbCBmbG9hdGluZyBkaWFsb2cgb3BlbmVkIGZyb20gdGhlIHRvb2xiYXIuIC0tPlxuPGNoYW5uZWwtaGlzdG9ncmFtIFsodmlzaWJsZSldPVwic2hvd0NoYW5uZWxIaXN0b2dyYW1cIj48L2NoYW5uZWwtaGlzdG9ncmFtPlxuXG48IS0tIFNwYXRpYWwtb21pY3MgY29udHJvbHMuIFJlbmRlcnMgaXRzIG93biBlbXB0eSBzdGF0ZSB3aGVuIG5vIGRhdGFzZXQgKG9yIG5vXG4gICAgIFNQQVRJQUxfREFUQV9QT1JUKSBpcyBwcmVzZW50LCBzbyBpdCBpcyBhbHdheXMgc2FmZSB0byBob3N0LiAtLT5cbjxzcGF0aWFsLWNvbnRyb2xzIFsodmlzaWJsZSldPVwic2hvd1NwYXRpYWxDb250cm9sc1wiIFtpczNkXT1cImlzU3BhdGlhbDNkTW9kZVwiID48L3NwYXRpYWwtY29udHJvbHM+XG5cbjwhLS0gU2lkZSBwYW5lbCBvZiB0aGUgYWN0aXZlIGNvbnRyaWJ1dGVkIHBsb3QgbW9kZSAoUExPVF9UWVBFX0NPTlRSSUJVVElPTlMpLFxuICAgICBpbiB0aGUgc2FtZSByaWdodC1oYW5kIGFyZWEgYXMgdGhlIHNwYXRpYWwgY29udHJvbHMgYW5kIHRoZSBSZWdpb24gRWRpdG9yLlxuICAgICBFeGlzdHMgb25seSB3aGlsZSB0aGUgbW9kZSdzIHNlc3Npb24gaXMgbGl2ZTsgdGhlcmUgaXMgbm8gY2xvc2UgYnV0dG9uLFxuICAgICBiZWNhdXNlIGxlYXZpbmcgdGhlIG1vZGUgaXMgd2hhdCBlbmRzIGl0LiBBIGNvbXBvbmVudCBwYW5lbCBnZXRzXG4gICAgIFBMT1RfTU9ERV9DT05URVhUIC8gUExPVF9NT0RFX1NFU1NJT04gZnJvbSBpdHMgaW5qZWN0b3I7IGEgYG1vdW50YCBwYW5lbCdzXG4gICAgIGhvc3QgZWxlbWVudCBpcyBhdHRhY2hlZCB0byB0aGUgc2xvdCBiZWxvdyAoc2VlIHBsb3RNb2RlUGFuZWxTbG90KS4gLS0+XG48cC1kaWFsb2cgKm5nSWY9XCJwbG90TW9kZVBhbmVsIGFzIHBhbmVsXCIgW2hlYWRlcl09XCJwYW5lbC50aXRsZVwiIFt2aXNpYmxlXT1cInRydWVcIlxuICAgICAgICAgIFttb2RhbF09XCJmYWxzZVwiIFtjbG9zYWJsZV09XCJmYWxzZVwiIFtyZXNpemFibGVdPVwidHJ1ZVwiIFtkcmFnZ2FibGVdPVwidHJ1ZVwiXG4gICAgICAgICAgYXBwZW5kVG89XCJib2R5XCIgcG9zaXRpb249XCJyaWdodFwiIHN0eWxlQ2xhc3M9XCJwbG90LW1vZGUtcGFuZWxcIlxuICAgICAgICAgIFtzdHlsZV09XCJ7IHdpZHRoOiAnMjJyZW0nLCBtYXhIZWlnaHQ6ICc4NXZoJyB9XCJcbiAgICAgICAgICBbY29udGVudFN0eWxlXT1cInsgb3ZlcmZsb3c6ICdhdXRvJyB9XCI+XG4gIDxuZy1jb250YWluZXIgKm5nSWY9XCJwYW5lbC5jb21wb25lbnRcIj5cbiAgICA8bmctY29udGFpbmVyICpuZ0NvbXBvbmVudE91dGxldD1cInBhbmVsLmNvbXBvbmVudDsgaW5qZWN0b3I6IHBhbmVsLmluamVjdG9yID8/IHVuZGVmaW5lZFwiPjwvbmctY29udGFpbmVyPlxuICA8L25nLWNvbnRhaW5lcj5cbiAgPGRpdiAqbmdJZj1cInBhbmVsLmhvc3RcIiAjcGxvdE1vZGVQYW5lbFNsb3QgY2xhc3M9XCJwbG90LW1vZGUtcGFuZWwtc2xvdFwiPjwvZGl2PlxuPC9wLWRpYWxvZz5cblxuPCEtLSBBbiBvcGVuIGRpYWxvZyB0b29sIChUT09MQkFSX1RPT0xTLCBraW5kOiAnZGlhbG9nJykuIE5vbi1tb2RhbCBzbyB0aGUgdXNlciBjYW5cbiAgICAga2VlcCB3b3JraW5nIG9uIHRoZSBpbWFnZSwgZS5nLiBwYWludGluZyB3aXRoIHRoZSBicnVzaCB0aGUgdG9vbCBhcm1lZC4gVGhlXG4gICAgIHRvb2wgcmVuZGVycyBpdHMgYm9keSBpbnRvIGEgaG9zdCBlbGVtZW50IGF0dGFjaGVkIHRvIHRoZSBzbG90IGJlbG93IChzZWVcbiAgICAgdG9vbERpYWxvZ1Nsb3QpOyBjbG9zaW5nIHRoZSBkaWFsb2cgZW5kcyB0aGUgdG9vbCdzIHNlc3Npb24uIC0tPlxuPHAtZGlhbG9nICpuZ0lmPVwidG9vbERpYWxvZyBhcyBkbGdcIiBbaGVhZGVyXT1cImRsZy50aXRsZVwiIFt2aXNpYmxlXT1cInRydWVcIlxuICAgICAgICAgICh2aXNpYmxlQ2hhbmdlKT1cIiRldmVudCB8fCBjbG9zZURpYWxvZ1Rvb2woKVwiXG4gICAgICAgICAgW21vZGFsXT1cImZhbHNlXCIgW2Nsb3NhYmxlXT1cInRydWVcIiBbcmVzaXphYmxlXT1cInRydWVcIiBbZHJhZ2dhYmxlXT1cInRydWVcIlxuICAgICAgICAgIGFwcGVuZFRvPVwiYm9keVwiIHBvc2l0aW9uPVwicmlnaHRcIiBzdHlsZUNsYXNzPVwidG9vbC1kaWFsb2dcIlxuICAgICAgICAgIFtzdHlsZV09XCJ7IHdpZHRoOiBkbGcud2lkdGgsIG1heEhlaWdodDogJzg1dmgnIH1cIlxuICAgICAgICAgIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIj5cbiAgPGRpdiAjdG9vbERpYWxvZ1Nsb3QgY2xhc3M9XCJ0b29sLWRpYWxvZy1zbG90XCI+PC9kaXY+XG48L3AtZGlhbG9nPlxuXG48IS0tIFJlZ2lvbiBFZGl0b3I6IG5vbi1tb2RhbCwgZHJhZ2dhYmxlLCByZXNpemFibGUgZmxvYXRpbmcgZGlhbG9nIG9wZW5lZCBmcm9tXG4gICAgIHRoZSB0b29sYmFyIChmb3JtZXJseSB0aGUgcmlnaHQtcGFuZWwgUmVnaW9ucyB0YWIpLiBNb2RlbGVzcyBzbyByZWdpb25zIGNhblxuICAgICBzdGlsbCBiZSBkcmF3bi9zZWxlY3RlZCBvbiB0aGUgcGxvdCB3aGlsZSBpdCdzIG9wZW47IHN0YXlzIGxpbmtlZCB0byB0aGlzXG4gICAgIHBsb3R0aW5nIGluc3RhbmNlIHZpYSB0aGUgcm9vdCBSZWdpb25TdG9yZSBzaW5nbGV0b24uIE9wZW5zIG92ZXIgdGhlIHJpZ2h0XG4gICAgIHNpZGUgb2YgdGhlIHZpZXcuIC0tPlxuPHAtZGlhbG9nIGhlYWRlcj1cIlJlZ2lvbiBFZGl0b3JcIiBbKHZpc2libGUpXT1cInNob3dSZWdpb25FZGl0b3JcIlxuICAgICAgICAgIFttb2RhbF09XCJmYWxzZVwiIFtyZXNpemFibGVdPVwidHJ1ZVwiIFtkcmFnZ2FibGVdPVwidHJ1ZVwiXG4gICAgICAgICAgW21heGltaXphYmxlXT1cInRydWVcIiBhcHBlbmRUbz1cImJvZHlcIiBwb3NpdGlvbj1cInJpZ2h0XCJcbiAgICAgICAgICBbc3R5bGVdPVwieyB3aWR0aDogcmVnaW9uRWRpdG9yV2lkdGgsIGhlaWdodDogJzg1dmgnIH1cIlxuICAgICAgICAgIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIj5cbiAgPHJlZ2lvbi1lZGl0b3I+PC9yZWdpb24tZWRpdG9yPlxuPC9wLWRpYWxvZz5cblxuPCEtLSBTaW1wbGlmeSAoRG91Z2xhc+KAk1BldWNrZXIpOiBjdXN0b20gcHgtdGhyZXNob2xkIHBpY2tlciBmb3IgdGhlIHNlbGVjdGVkXG4gICAgIHJlZ2lvbihzKSwgb3BlbmVkIGZyb20gdGhlIHJpZ2h0LWNsaWNrIFwiU2ltcGxpZnkg4pa4IEN1c3RvbeKAplwiIGFjdGlvbi4gLS0+XG48cC1kaWFsb2cgaGVhZGVyPVwiU2ltcGxpZnkgcmVnaW9uKHMpXCIgWyh2aXNpYmxlKV09XCJkaXNwbGF5U2ltcGxpZnlEaWFsb2dcIiBbbW9kYWxdPVwidHJ1ZVwiXG4gICAgICAgICAgW3N0eWxlXT1cInt3aWR0aDogJzIycmVtJ31cIiBbZHJhZ2dhYmxlXT1cImZhbHNlXCIgW3Jlc2l6YWJsZV09XCJmYWxzZVwiPlxuICA8ZGl2IGNsYXNzPVwiZmxleCBmbGV4LWNvbHVtbiBnYXAtMlwiPlxuICAgIDxzcGFuPkFsdGl0dWRlIHRocmVzaG9sZDogPGI+e3sgc2ltcGxpZnlUaHJlc2hvbGQgfX0gcHg8L2I+PC9zcGFuPlxuICAgIDxwLXNsaWRlciBbKG5nTW9kZWwpXT1cInNpbXBsaWZ5VGhyZXNob2xkXCIgW21pbl09XCIxXCIgW21heF09XCIyMFwiIFtzdGVwXT1cIjFcIj48L3Atc2xpZGVyPlxuICAgIDxzbWFsbD5WZXJ0aWNlcyB3aXRoaW4gdGhpcyBkaXN0YW5jZSBvZiB0aGUgbGluZSBiZXR3ZWVuIHRoZWlyIG5laWdoYm91cnMgYXJlIHJlbW92ZWQuPC9zbWFsbD5cbiAgPC9kaXY+XG4gIDxuZy10ZW1wbGF0ZSBwVGVtcGxhdGU9XCJmb290ZXJcIj5cbiAgICA8cC1idXR0b24gbGFiZWw9XCJDYW5jZWxcIiBbdGV4dF09XCJ0cnVlXCIgKG9uQ2xpY2spPVwiZGlzcGxheVNpbXBsaWZ5RGlhbG9nID0gZmFsc2VcIj48L3AtYnV0dG9uPlxuICAgIDxwLWJ1dHRvbiBsYWJlbD1cIkFwcGx5XCIgaWNvbj1cInBpIHBpLWNoZWNrXCIgKG9uQ2xpY2spPVwic2ltcGxpZnlSZWdpb25zKHNpbXBsaWZ5VGhyZXNob2xkKVwiPjwvcC1idXR0b24+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtZGlhbG9nPlxuXG48IS0tIFBhcmFtZXRlcnMgZm9yIHdoaWNoZXZlciBjb250cmlidXRlZCB0b29sIGlzIG9wZW4uIE9uZSBkaWFsb2cgZm9yIGFsbCBvZlxuICAgICB0aGVtOiBhIHRvb2wgZGVjbGFyZXMgaXRzIGZpZWxkcyAoVG9vbFBhcmFtU3BlYykgYW5kIHRoaXMgcmVuZGVycyB0aGVtLCBzb1xuICAgICB0aGUgdG9vbGJhciBjYW4gbGF5IGV2ZXJ5IHRvb2wgb3V0IHRoZSBzYW1lIHdheSBhbmQgYSBwbHVnaW4gbmVlZHMgbm9cbiAgICAgUHJpbWVORyBvZiBpdHMgb3duLiBEZWZhdWx0cyBjb21lIGZyb20gdGhlIGFjdGl2ZSBjaGVja3BvaW50LCBzaW5jZSB0aGV5XG4gICAgIGVuY29kZSB3aGF0IGl0IHdhcyB0cmFpbmVkIGZvci4gLS0+XG48cC1kaWFsb2cgKm5nSWY9XCJvcGVuUGFyYW1zVG9vbCBhcyB0b29sXCJcbiAgICAgICAgICBbaGVhZGVyXT1cInRvb2wubGFiZWwgKyAnIHBhcmFtZXRlcnMnXCJcbiAgICAgICAgICBbdmlzaWJsZV09XCJ0cnVlXCIgKHZpc2libGVDaGFuZ2UpPVwib3BlblBhcmFtc1Rvb2xJZCA9IG51bGxcIlxuICAgICAgICAgIFttb2RhbF09XCJ0cnVlXCJcbiAgICAgICAgICBbc3R5bGVdPVwie3dpZHRoOiAnMzByZW0nfVwiIFtkcmFnZ2FibGVdPVwiZmFsc2VcIiBbcmVzaXphYmxlXT1cImZhbHNlXCIgYXBwZW5kVG89XCJib2R5XCI+XG4gIDxkaXYgY2xhc3M9XCJncmlkIGZvcm1ncmlkIHAtZmx1aWRcIj5cbiAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCBwIG9mIHRvb2wucGFyYW1zXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiZmllbGRcIiBbbmdDbGFzc109XCJwLnR5cGUgPT09ICdib29sZWFuJyA/ICdjb2wtMTInIDogJ2NvbC02J1wiPlxuICAgICAgICA8bmctY29udGFpbmVyIFtuZ1N3aXRjaF09XCJwLnR5cGVcIj5cblxuICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nU3dpdGNoQ2FzZT1cIidudW1iZXInXCI+XG4gICAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJ0ZXh0LXNtXCIgW3BUb29sdGlwXT1cInAudG9vbHRpcCB8fCAnJ1wiPnt7IHAubGFiZWwgfX08L2xhYmVsPlxuICAgICAgICAgICAgPHAtaW5wdXROdW1iZXIgW25nTW9kZWxdPVwicGFyYW1zRm9yKHRvb2wuaWQpW3AuaWRdXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInBhcmFtc0Zvcih0b29sLmlkKVtwLmlkXSA9ICRldmVudFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICBbbWluXT1cIm51bWJlclBhcmFtKHApLm1pblwiIFttYXhdPVwibnVtYmVyUGFyYW0ocCkubWF4XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIFtzdGVwXT1cIm51bWJlclBhcmFtKHApLnN0ZXBcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgW21pbkZyYWN0aW9uRGlnaXRzXT1cIm51bWJlclBhcmFtKHApLmZyYWN0aW9uRGlnaXRzXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIFtzaG93QnV0dG9uc109XCJ0cnVlXCI+PC9wLWlucHV0TnVtYmVyPlxuICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdTd2l0Y2hDYXNlPVwiJ3NlbGVjdCdcIj5cbiAgICAgICAgICAgIDxsYWJlbCBjbGFzcz1cInRleHQtc21cIiBbcFRvb2x0aXBdPVwicC50b29sdGlwIHx8ICcnXCI+e3sgcC5sYWJlbCB9fTwvbGFiZWw+XG4gICAgICAgICAgICA8cC1kcm9wZG93biBbb3B0aW9uc109XCJzZWxlY3RQYXJhbShwKS5vcHRpb25zXCIgb3B0aW9uTGFiZWw9XCJsYWJlbFwiIG9wdGlvblZhbHVlPVwidmFsdWVcIlxuICAgICAgICAgICAgICAgICAgICAgICAgW25nTW9kZWxdPVwicGFyYW1zRm9yKHRvb2wuaWQpW3AuaWRdXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInBhcmFtc0Zvcih0b29sLmlkKVtwLmlkXSA9ICRldmVudFwiXG4gICAgICAgICAgICAgICAgICAgICAgICBhcHBlbmRUbz1cImJvZHlcIj48L3AtZHJvcGRvd24+XG4gICAgICAgICAgPC9uZy1jb250YWluZXI+XG5cbiAgICAgICAgICA8IS0tIENoZWNrYm94IGxhYmVsIHNpdHMgYmVzaWRlIHRoZSBib3ggYW5kIG1heSBydW4gdG8gYSBzZW50ZW5jZSwgc29cbiAgICAgICAgICAgICAgIGl0IGRvZXMgbm90IGdldCB0aGUgc3RhY2tlZCBsYWJlbCB0aGUgb3RoZXIgdHlwZXMgdXNlLiAtLT5cbiAgICAgICAgICA8ZGl2ICpuZ1N3aXRjaENhc2U9XCInYm9vbGVhbidcIiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIGdhcC0yXCI+XG4gICAgICAgICAgICA8cC1jaGVja2JveCBbbmdNb2RlbF09XCJwYXJhbXNGb3IodG9vbC5pZClbcC5pZF1cIlxuICAgICAgICAgICAgICAgICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwicGFyYW1zRm9yKHRvb2wuaWQpW3AuaWRdID0gJGV2ZW50XCJcbiAgICAgICAgICAgICAgICAgICAgICAgIFtiaW5hcnldPVwidHJ1ZVwiIFtpbnB1dElkXT1cInRvb2wuaWQgKyAnLScgKyBwLmlkXCI+PC9wLWNoZWNrYm94PlxuICAgICAgICAgICAgPGxhYmVsIFtmb3JdPVwidG9vbC5pZCArICctJyArIHAuaWRcIiBjbGFzcz1cInRleHQtc20gbWItMFwiXG4gICAgICAgICAgICAgICAgICAgW3BUb29sdGlwXT1cInAudG9vbHRpcCB8fCAnJ1wiPnt7IHAubGFiZWwgfX08L2xhYmVsPlxuICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPC9kaXY+XG4gICAgPC9uZy1jb250YWluZXI+XG4gIDwvZGl2PlxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPHAtYnV0dG9uIGxhYmVsPVwiUmVzZXQgdG8gbW9kZWwgZGVmYXVsdHNcIiBbdGV4dF09XCJ0cnVlXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwicmVzZXRUb29sUGFyYW1zKHRvb2wuaWQpXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gbGFiZWw9XCJDbG9zZVwiIHNpemU9XCJzbWFsbFwiIChvbkNsaWNrKT1cIm9wZW5QYXJhbXNUb29sSWQgPSBudWxsXCI+PC9wLWJ1dHRvbj5cbiAgPC9uZy10ZW1wbGF0ZT5cbjwvcC1kaWFsb2c+XG4iXX0=
|
|
1173
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlzdWFsaXplci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3Zpc3VhbGl6ZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vc3JjL2xpYi92aXN1YWxpemVyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFFTCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxVQUFVLEVBQ1YsVUFBVSxFQUNWLFlBQVksRUFDWixNQUFNLEVBQ04sUUFBUSxFQUNSLEtBQUssRUFDTCxNQUFNLEVBSU4sUUFBUSxFQUNSLE1BQU0sRUFFTixTQUFTLEVBQ1QsUUFBUSxFQUNSLE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTdDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFbEMsT0FBTyxFQUFZLGNBQWMsRUFBRSxZQUFZLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDckUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsV0FBVyxFQUFFLGlCQUFpQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDckUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxx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QixNQUFNOzJCQUFDLFVBQVU7OzBCQVNqQixRQUFROzswQkFBSSxNQUFNOzJCQUFDLFVBQVU7OzBCQUk3QixRQUFROzswQkFBSSxNQUFNOzJCQUFDLGFBQWE7OzBCQUdoQyxRQUFROzswQkFBSSxNQUFNOzJCQUFDLGlCQUFpQjs7MEJBR3BDLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsdUJBQXVCOzswQkFHMUMsUUFBUTs7MEJBRVIsUUFBUTtrRUFoVlgsWUFBWTtzQkFEWCxNQUFNO2dCQUlQLGdCQUFnQjtzQkFEZixNQUFNO2dCQU1ILFlBQVk7c0JBRGYsS0FBSztnQkFjTixRQUFRO3NCQURQLEtBQUs7Z0JBMENXLFdBQVc7c0JBQTNCLFNBQVM7dUJBQUMsSUFBSTtnQkFrTFgsaUJBQWlCO3NCQURwQixTQUFTO3VCQUFDLG1CQUFtQjtnQkE4QndCLEtBQUs7c0JBQTFELFNBQVM7dUJBQUMsdUJBQXVCLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO2dCQW15QmhELGNBQWM7c0JBRGpCLFNBQVM7dUJBQUMsZ0JBQWdCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENoYW5nZURldGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIERlc3Ryb3lSZWYsXG4gIEVsZW1lbnRSZWYsXG4gIEV2ZW50RW1pdHRlcixcbiAgSW5qZWN0LFxuICBJbmplY3RvcixcbiAgSW5wdXQsXG4gIE5nWm9uZSxcbiAgT25DaGFuZ2VzLFxuICBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgT3B0aW9uYWwsXG4gIE91dHB1dCxcbiAgU2ltcGxlQ2hhbmdlcyxcbiAgVmlld0NoaWxkLFxuICBjb21wdXRlZCxcbiAgc2lnbmFsLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IHRha2VVbnRpbERlc3Ryb3llZCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUvcnhqcy1pbnRlcm9wJztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcblxuaW1wb3J0IHsgT2JzZXJ2YWJsZSB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgeyBNZW51SXRlbSwgTWVzc2FnZVNlcnZpY2UsIFNoYXJlZE1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvYXBpJztcbmltcG9ydCB7IEJ1dHRvbk1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvYnV0dG9uJztcbmltcG9ydCB7IENoZWNrYm94TW9kdWxlIH0gZnJvbSAncHJpbWVuZy9jaGVja2JveCc7XG5pbXBvcnQgeyBDb250ZXh0TWVudSwgQ29udGV4dE1lbnVNb2R1bGUgfSBmcm9tICdwcmltZW5nL2NvbnRleHRtZW51JztcbmltcG9ydCB7IERpYWxvZ01vZHVsZSB9IGZyb20gJ3ByaW1lbmcvZGlhbG9nJztcbmltcG9ydCB7IERyb3Bkb3duTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9kcm9wZG93bic7XG5pbXBvcnQgeyBJbnB1dE51bWJlck1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvaW5wdXRudW1iZXInO1xuaW1wb3J0IHsgUHJvZ3Jlc3NCYXJNb2R1bGUgfSBmcm9tICdwcmltZW5nL3Byb2dyZXNzYmFyJztcbmltcG9ydCB7IFNsaWRlck1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvc2xpZGVyJztcbmltcG9ydCB7IFRvYXN0TW9kdWxlIH0gZnJvbSAncHJpbWVuZy90b2FzdCc7XG5pbXBvcnQgeyBUb29sdGlwTW9kdWxlIH0gZnJvbSAncHJpbWVuZy90b29sdGlwJztcbmltcG9ydCB7IElJbWFnZUluZm8gfSBmcm9tICcuL2NvbnRyYWN0cy9pbWFnZS5jb250cmFjdCc7XG5pbXBvcnQgeyBJbWFnZVN0YXRlUG9ydCwgSU1BR0VfU1RBVEVfUE9SVCB9IGZyb20gJy4vY29udHJhY3RzL3BvcnRzL2ltYWdlLXN0YXRlLnBvcnQnO1xuaW1wb3J0IHsgUG9seWdvbiB9IGZyb20gJy4vbW9kZWxzL3JlZ2lvbic7XG5pbXBvcnQgeyBSZWdpb25PcHNTZXJ2aWNlIH0gZnJvbSAnLi9yZWdpb24tb3BzLnNlcnZpY2UnO1xuaW1wb3J0IHsgVklaX1RPQVNUX0tFWSwgVklaX0FMRVJUX1RPQVNUX0tFWSB9IGZyb20gJy4vdG9hc3Qtb3V0bGV0cyc7XG5pbXBvcnQgeyBWaXN1YWxpemVyU3RvcmUgfSBmcm9tICcuL3N0b3JlL3Zpc3VhbGl6ZXItc3RvcmUuc2VydmljZSc7XG5pbXBvcnQgeyBTbGljZVNjcnViYmVyIH0gZnJvbSAnLi9yZW5kZXItb3JjaGVzdHJhdG9yJztcbmltcG9ydCB7IEltYWdlUmVuZGVyU2Vzc2lvbiwgZXJyb3JNZXNzYWdlIH0gZnJvbSAnLi9yZW5kZXItc2Vzc2lvbic7XG5pbXBvcnQge1xuICBQbG90VHlwZSxcbiAgUGxvdFR5cGVEZXNjcmlwdG9yLFxuICBQbG90VHlwZUlkLFxuICBnZXRQbG90VHlwZURlc2NyaXB0b3IsXG4gIGlzQnVpbHRpblBsb3RUeXBlLFxuICBpc05hcGFyaTNkLFxuICBpc1NwYXRpYWxPbWljcyxcbiAgaXNTcGF0aWFsT21pY3MzZCxcbiAgcmVuZGVyZXJPd25zV2hlZWwsXG4gIE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFLFxufSBmcm9tICcuL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuaW1wb3J0IHtcbiAgUExPVF9UWVBFX0NPTlRSSUJVVElPTlMsXG4gIFBsb3RUeXBlQ29udHJpYnV0aW9uLFxuICBQbG90VHlwZU9wdGlvbixcbn0gZnJvbSAnLi9jb250cmFjdHMvcGxvdC10eXBlLWNvbnRyaWJ1dGlvbi5jb250cmFjdCc7XG5pbXBvcnQgeyBQbG90TW9kZUNvbnRyb2xsZXIgfSBmcm9tICcuL3Bsb3QtbW9kZS9wbG90LW1vZGUtY29udHJvbGxlcic7XG5pbXBvcnQgeyBjb21wdXRlUGxvdFR5cGVNZW51LCByZWNvbmNpbGVQbG90VHlwZSB9IGZyb20gJy4vcGxvdC1tb2RlL3Bsb3QtdHlwZS1tZW51JztcbmltcG9ydCB7IFRvb2xQYXJhbXNNb2RlbCB9IGZyb20gJy4vcGxvdC1tb2RlL3Rvb2wtcGFyYW1zLW1vZGVsJztcbmltcG9ydCB7IENvbnRyaWJ1dGlvbkhvc3QsIFBsb3RNb2RlUGFuZWxWaWV3LCBUb29sRGlhbG9nVmlldyB9IGZyb20gJy4vcGxvdC1tb2RlL2NvbnRyaWJ1dGlvbi1ob3N0JztcbmltcG9ydCB7IElWaXN1YWxpemVyLCBWSVNVQUxJWkVSLCBWaXN1YWxpemVySGFuZGxlIH0gZnJvbSAnLi9jb250cmFjdHMvdmlzdWFsaXplci5jb250cmFjdCc7XG5pbXBvcnQgeyBTQU1fTU9ERUxTLCBnZXREZWZhdWx0U2FtTW9kZWxJZCwgaXNTYW1Nb2RlbFJlYWR5IH0gZnJvbSAnLi90b29sYmFyL3NlZ21lbnRhdGlvbi9zYW0tbW9kZWwtcmVnaXN0cnknO1xuaW1wb3J0IHsgU2FtVG9vbFNlcnZpY2UgfSBmcm9tICcuL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgU2FtUG9pbnRUb29sU2VydmljZSB9IGZyb20gJy4vdG9vbGJhci9zZWdtZW50YXRpb24vc2FtLXBvaW50LXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBDZWxsU2VnbWVudFRvb2xTZXJ2aWNlIH0gZnJvbSAnLi90b29sYmFyL3NlZ21lbnRhdGlvbi9jZWxsLXNlZ21lbnQtdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IFNlZ21lbnRhdGlvblJ1bm5lciB9IGZyb20gJy4vdG9vbGJhci9zZWdtZW50YXRpb24vc2VnbWVudGF0aW9uLXJ1bm5lcic7XG5pbXBvcnQge1xuICBUT09MQkFSX1RPT0xTLFxuICBUb29sYmFyQ29udHJpYnV0aW9uLFxuICBUb29sYmFyRGlhbG9nVG9vbENvbnRyaWJ1dGlvbixcbiAgVG9vbGJhclRvb2xDb250cmlidXRpb24sXG4gIHZpc2libGVUb29sQ29udHJpYnV0aW9ucyxcbn0gZnJvbSAnLi9jb250cmFjdHMvdG9vbGJhci10b29sLmNvbnRyYWN0JztcbmltcG9ydCB7IFRvb2xiYXJUb29sVmlzaWJpbGl0eSwgQUxMX1RPT0xCQVJfVE9PTFMgfSBmcm9tICcuL2NvbnRyYWN0cy90b29sYmFyLWNvbmZpZyc7XG5pbXBvcnQgeyBWSVpfQ09ORklHLCBWaXpDb25maWcgfSBmcm9tICcuL2NvbnRyYWN0cy92aXotY29uZmlnJztcbmltcG9ydCB7IFNQQVRJQUxfREFUQV9QT1JULCBTcGF0aWFsRGF0YVBvcnQgfSBmcm9tICcuL2NvbnRyYWN0cy9wb3J0cy9zcGF0aWFsLWRhdGEucG9ydCc7XG5pbXBvcnQgeyBhcHBseUltYWdlUm9pcyB9IGZyb20gJy4vdmlzdWFsaXplci9yZWdpb24tbG9hZCc7XG5pbXBvcnQgeyBDb250ZXh0TWVudUFjdGlvbnMsIENvbnRleHRNZW51U3RhdGUsIGJ1aWxkQ29udGV4dE1lbnUgfSBmcm9tICcuL3Zpc3VhbGl6ZXIvdmlzdWFsaXplci1jb250ZXh0LW1lbnUnO1xuaW1wb3J0IHsgUmVnaW9uQWN0aW9ucyB9IGZyb20gJy4vdmlzdWFsaXplci9yZWdpb24tYWN0aW9ucyc7XG5pbXBvcnQgeyBUb29sTW9kZXMgfSBmcm9tICcuL3Zpc3VhbGl6ZXIvdG9vbC1tb2Rlcyc7XG5pbXBvcnQgeyBTaG9ydGN1dEhvc3QsIFZpZXdlclNob3J0Y3V0cyB9IGZyb20gJy4vdmlzdWFsaXplci92aWV3ZXItc2hvcnRjdXRzJztcbmltcG9ydCB7IEZsb2F0aW5nRHJhZ0RpcmVjdGl2ZSwgRmxvYXRpbmdQb3MgfSBmcm9tICcuL3Zpc3VhbGl6ZXIvZmxvYXRpbmctZHJhZy5kaXJlY3RpdmUnO1xuaW1wb3J0IHsgSW50ZW5zaXR5SW5zZXRDb21wb25lbnQgfSBmcm9tICcuL2ludGVuc2l0eS1pbnNldC9pbnRlbnNpdHktaW5zZXQuY29tcG9uZW50JztcbmltcG9ydCB7IFNwYXRpYWxEYXRhc2V0QmluZGVyIH0gZnJvbSAnLi92aXN1YWxpemVyL3NwYXRpYWwtZGF0YXNldC1iaW5kZXInO1xuaW1wb3J0IHsgVG9vbGJhckNvbXBvbmVudCB9IGZyb20gJy4vdG9vbGJhci90b29sYmFyLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBSZWdpb25FZGl0b3JDb21wb25lbnQgfSBmcm9tICcuL3JlZ2lvbi1lZGl0b3IvcmVnaW9uLWVkaXRvci5jb21wb25lbnQnO1xuaW1wb3J0IHsgQ2hhbm5lbEhpc3RvZ3JhbUNvbXBvbmVudCB9IGZyb20gJy4vY2hhbm5lbC1oaXN0b2dyYW0vY2hhbm5lbC1oaXN0b2dyYW0uY29tcG9uZW50JztcbmltcG9ydCB7IFNwYXRpYWxDb250cm9sc0NvbXBvbmVudCB9IGZyb20gJy4vc3BhdGlhbC1jb250cm9scy9zcGF0aWFsLWNvbnRyb2xzLmNvbXBvbmVudCc7XG5cbi8qKiBQZXItaW5zdGFuY2UgcGxvdC1kaXYgaWQgc291cmNlLiBUaGUgbW91bnQgZWxlbWVudCdzIGlkIG11c3QgYmUgdW5pcXVlIHNvIHR3b1xuICogIGxpdmUgdmlld2VycyAoZS5nLiB0aGUgbWFpbiBkaWFncmFtICsgYSBtb2RhbCBwcmV2aWV3KSBkb24ndCBjb2xsaWRlIG9uIHRoZVxuICogIHNhbWUgRE9NIGlkIOKAlCBgZ2V0RWxlbWVudEJ5SWRgIHdvdWxkIG90aGVyd2lzZSByZXR1cm4gd2hpY2hldmVyIGNhbWUgZmlyc3QuXG4gKiAgU3R5bGluZyBoYW5ncyBvZmYgdGhlIGAudml6LXBsb3RgIGNsYXNzIGluc3RlYWQgb2YgdGhlIGlkLiAqL1xubGV0IHBsb3RJbnN0YW5jZVNlcSA9IDA7XG5cbi8qKlxuICogVGhlIHZpZXdlciAoYDx2aXN1YWxpemVyPmApOiB0aGUgcGxvdCBzdXJmYWNlLCBpdHMgdG9vbGJhciwgdGhlIGxvYWRpbmcgb3ZlcmxheSBhbmRcbiAqIHRoZSBkaWFsb2dzIGl0IG9wZW5zIChDaGFubmVscyAmIEhpc3RvZ3JhbSwgc3BhdGlhbC1vbWljcyBjb250cm9scywgUmVnaW9uIEVkaXRvcixcbiAqIGNvbnRyaWJ1dGVkIG1vZGVzIGFuZCB0b29scykuIEl0IG9yY2hlc3RyYXRlcyByZW5kZXJpbmcgdGhyb3VnaCB0aGUgYFZJU1VBTElaRVJgXG4gKiBjb250cmFjdCBhbmQgcmVhZHMgdGhlIGltYWdlIGZyb20gdGhlIGhvc3QncyBgSU1BR0VfU1RBVEVfUE9SVGAuXG4gKlxuICogT25QdXNoLiBUZW1wbGF0ZSBldmVudHMgcmUtcmVuZGVyIGl0OyBzdGF0ZSB0aGF0IGxhbmRzIGFzeW5jaHJvbm91c2x5IGdvZXMgdGhyb3VnaFxuICoge0BsaW5rIGNoYW5nZWR9IChvciBhIHNpZ25hbDogdGhlIGxvYWRpbmcgb3ZlcmxheSksIGFuZCB0aGUgY29sbGFib3JhdG9ycyB0aGF0IG11c3RcbiAqIHJlbmRlciBzeW5jaHJvbm91c2x5ICh0aGUgcmVuZGVyIHNlc3Npb24ncyBvdmVybGF5LCBhIGNvbnRyaWJ1dGVkIHBhbmVsJ3Mgc2xvdCwgdGhlXG4gKiBjb250ZXh0IG1lbnUgYmVmb3JlIGl0IG9wZW5zKSBzdGlsbCBjYWxsIGBkZXRlY3RDaGFuZ2VzKClgLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICd2aXN1YWxpemVyJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIENvbW1vbk1vZHVsZSxcbiAgICBGb3Jtc01vZHVsZSxcbiAgICBTaGFyZWRNb2R1bGUsXG4gICAgQnV0dG9uTW9kdWxlLFxuICAgIENoZWNrYm94TW9kdWxlLFxuICAgIENvbnRleHRNZW51TW9kdWxlLFxuICAgIERpYWxvZ01vZHVsZSxcbiAgICBEcm9wZG93bk1vZHVsZSxcbiAgICBJbnB1dE51bWJlck1vZHVsZSxcbiAgICBQcm9ncmVzc0Jhck1vZHVsZSxcbiAgICBTbGlkZXJNb2R1bGUsXG4gICAgVG9hc3RNb2R1bGUsXG4gICAgVG9vbHRpcE1vZHVsZSxcbiAgICBGbG9hdGluZ0RyYWdEaXJlY3RpdmUsXG4gICAgVG9vbGJhckNvbXBvbmVudCxcbiAgICBJbnRlbnNpdHlJbnNldENvbXBvbmVudCxcbiAgICBDaGFubmVsSGlzdG9ncmFtQ29tcG9uZW50LFxuICAgIFNwYXRpYWxDb250cm9sc0NvbXBvbmVudCxcbiAgICBSZWdpb25FZGl0b3JDb21wb25lbnQsXG4gIF0sXG4gIHRlbXBsYXRlVXJsOiAnLi92aXN1YWxpemVyLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vdmlzdWFsaXplci5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgVmlzdWFsaXplckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzLCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3ksIENvbnRleHRNZW51QWN0aW9ucyB7XG4gIC8qKiBQZXItaW5zdGFuY2UgdG9hc3Qga2V5LiBNZXNzYWdlU2VydmljZSBpcyBhIGdsb2JhbCBzaW5nbGV0b24sIHNvIHR3byBsaXZlXG4gICAqICBgPHZpc3VhbGl6ZXI+YCBpbnN0YW5jZXMgKG1haW4gdmlld2VyICsgcGlwZWxpbmUtZGlhbG9nIHByZXZpZXcpIHNoYXJpbmdcbiAgICogIG9uZSBrZXkgd291bGQgZWFjaCByZW5kZXIgdGhlIHNhbWUgbWVzc2FnZSDigJQgYSBkdXBsaWNhdGUgdG9hc3QuIEEgdW5pcXVlXG4gICAqICBrZXkgcGVyIGluc3RhbmNlIHNjb3BlcyB0aGUgdG9hc3QgdG8gdGhlIGNvbXBvbmVudCB0aGF0IHJhaXNlZCBpdC4gKi9cbiAgcHJpdmF0ZSBzdGF0aWMgbmV4dFRvYXN0SWQgPSAwO1xuXG4gIC8qKlxuICAgKiBUaGUgbGlicmFyeSBhbHNvIHJhaXNlcyBub3RpY2VzIGZyb20gcGxhY2VzIHRoYXQgaGF2ZSBubyBpbnN0YW5jZSBvZiB0aGVpclxuICAgKiBvd24gdG8ga2V5IGFnYWluc3Q6IHtAbGluayBQbG90bHlTZXJ2aWNlfSBpcyBgcHJvdmlkZWRJbjogJ3Jvb3QnYCwgYW5kIHRoZVxuICAgKiByZWdpb24gZWRpdG9yIGlzIGEgY2hpbGQgZGlhbG9nLiBUaG9zZSB1c2UgdGhlIGZpeGVkXG4gICAqIHtAbGluayBWSVpfVE9BU1RfS0VZfSAvIHtAbGluayBWSVpfQUxFUlRfVE9BU1RfS0VZfSBvdXRsZXRzLlxuICAgKlxuICAgKiBGb3IgdGhlIHNhbWUgcmVhc29uIHRoZSBwZXItaW5zdGFuY2Uga2V5IGFib3ZlIGV4aXN0cywgZXhhY3RseSBPTkUgbW91bnRlZFxuICAgKiB2aXN1YWxpemVyIG1heSByZW5kZXIgdGhvc2Ugc2hhcmVkIG91dGxldHMg4oCUIG90aGVyd2lzZSBhIG1lc3NhZ2UgZnJvbSB0aGVcbiAgICogcm9vdCBzZXJ2aWNlIHdvdWxkIGJlIHNob3duIG9uY2UgcGVyIGxpdmUgdmlld2VyIChqaXQtdWkgcnVucyBhIG1haW4gdmlld1xuICAgKiBhbmQgYSBwaXBlbGluZS1wcmV2aWV3IGF0IHRoZSBzYW1lIHRpbWUpLiBUaGUgZmlyc3QgdG8gaW5pdCBjbGFpbXMgdGhlbSBhbmRcbiAgICogcmVsZWFzZXMgb24gZGVzdHJveSwgc28gdGhlIHN1cnZpdmluZyB2aWV3ZXIgcGlja3MgdGhlbSB1cC5cbiAgICovXG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IGxpdmVJbnN0YW5jZXMgPSBuZXcgU2V0PFZpc3VhbGl6ZXJDb21wb25lbnQ+KCk7XG4gIHByb3RlY3RlZCByZWFkb25seSB2aXpUb2FzdEtleSA9IFZJWl9UT0FTVF9LRVk7XG4gIHByb3RlY3RlZCByZWFkb25seSB2aXpBbGVydFRvYXN0S2V5ID0gVklaX0FMRVJUX1RPQVNUX0tFWTtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhpcyBpbnN0YW5jZSByZW5kZXJzIHRoZSBzaGFyZWQgb3V0bGV0cy4gVGhlIG93bmVyIGlzIHNpbXBseSB0aGVcbiAgICogb2xkZXN0IGxpdmUgdmlzdWFsaXplciDigJQgYSBgU2V0YCBpdGVyYXRlcyBpbiBpbnNlcnRpb24gb3JkZXIsIHNvIHdoZW4gdGhlXG4gICAqIG93bmVyIGlzIGRlc3Ryb3llZCB0aGUgbmV4dCBvbmUgdGFrZXMgb3ZlciBvbiBpdHMgZm9sbG93aW5nIGNoYW5nZS1kZXRlY3Rpb25cbiAgICogcGFzcy4gVHJhY2tpbmcgYSBzZXQgcmF0aGVyIHRoYW4gYSBzaW5nbGUgb3duZXIgbWF0dGVyczogd2l0aCBhIGxvbmVcbiAgICogYG93bmVyYCByZWZlcmVuY2UsIHRlYXJpbmcgZG93biB0aGUgb3duZXIgd2hpbGUgYSBzaWJsaW5nIHdhcyBhbHJlYWR5XG4gICAqIGluaXRpYWxpc2VkIHdvdWxkIGxlYXZlIG5vYm9keSByZW5kZXJpbmcgdGhlIG91dGxldHMsIGFuZCBldmVyeSBub3RpY2UgZnJvbVxuICAgKiB0aGUgcm9vdCBzZXJ2aWNlIHdvdWxkIHNpbGVudGx5IHZhbmlzaCBhZ2Fpbi5cbiAgICovXG4gIHByb3RlY3RlZCBnZXQgb3duc1NoYXJlZFRvYXN0cygpOiBib29sZWFuIHtcbiAgICBjb25zdCBvbGRlc3QgPSBWaXN1YWxpemVyQ29tcG9uZW50LmxpdmVJbnN0YW5jZXMudmFsdWVzKCkubmV4dCgpO1xuICAgIHJldHVybiAhb2xkZXN0LmRvbmUgJiYgb2xkZXN0LnZhbHVlID09PSB0aGlzO1xuICB9XG5cbiAgLyoqIEVhY2ggbmV3IGltYWdlOiB3aGV0aGVyIGl0IGlzIGEgei1zdGFjay4gKi9cbiAgQE91dHB1dCgpXG4gIGlzU3RhY2tFdmVudCA9IG5ldyBFdmVudEVtaXR0ZXIoZmFsc2UpO1xuICAvKiogRWFjaCBuZXcgaW1hZ2U6IHdoZXRoZXIgaXQgaXMgc2luZ2xlLWNoYW5uZWwgKGdyYXlzY2FsZSkuICovXG4gIEBPdXRwdXQoKVxuICBpc0dyYXlzY2FsZUV2ZW50ID0gbmV3IEV2ZW50RW1pdHRlcihmYWxzZSk7XG5cbiAgLyoqIFdoaWNoIHRvb2xiYXIgZ3JvdXBzIHRvIHNob3cuIEEgcGFydGlhbCBvdmVycmlkZSBtZXJnZXMgb3ZlciB0aGUgZnVsbCBzZXQsXG4gICAqICBzbyBgeyBzcGVjaWFsVG9vbHM6IGZhbHNlIH1gIGhpZGVzIG9ubHkgdGhhdCBncm91cC4gRGVmYXVsdHMgdG8gZXZlcnl0aGluZy4gKi9cbiAgQElucHV0KClcbiAgc2V0IHRvb2xiYXJUb29scyh2OiBUb29sYmFyVG9vbFZpc2liaWxpdHkgfCB1bmRlZmluZWQpIHtcbiAgICB0aGlzLl90b29sYmFyVG9vbHMgPSB7IC4uLkFMTF9UT09MQkFSX1RPT0xTLCAuLi4odiA/PyB7fSkgfTtcbiAgfVxuXG4gIGdldCB0b29sYmFyVG9vbHMoKTogUmVxdWlyZWQ8VG9vbGJhclRvb2xWaXNpYmlsaXR5PiB7XG4gICAgcmV0dXJuIHRoaXMuX3Rvb2xiYXJUb29scztcbiAgfVxuICBwcml2YXRlIF90b29sYmFyVG9vbHM6IFJlcXVpcmVkPFRvb2xiYXJUb29sVmlzaWJpbGl0eT4gPSBBTExfVE9PTEJBUl9UT09MUztcblxuICAvKiogRXhwb3NlIHRlc3Qtb25seSBwbG90IG1vZGVzIGluIHRoZSBzZWxlY3RvciAoZS5nLiB0aGUgbmFwYXJpIGBJbWFnZWBcbiAgICogIG1vZGUsIG90aGVyd2lzZSBoaWRkZW4pLiBUaGUgaG9zdCBhcHAgc2V0cyB0aGlzIGZyb20gYSBgP3Rlc3Q9MWAgVVJMXG4gICAqICB0b2dnbGUuIE9mZiBieSBkZWZhdWx0LCBzbyBwcm9kdWN0aW9uIHVzZXJzIG5ldmVyIHNlZSB0aGVtLiAqL1xuICBASW5wdXQoKVxuICB0ZXN0TW9kZSA9IGZhbHNlO1xuXG4gIC8qKiBJbWFnZS1zbW9vdGhpbmcgc3RhdGUgZm9yIHRoZSB0b29sYmFyJ3MgU21vb3RoZW4gdG9nZ2xlIChPU0Qgb25seSkuXG4gICAqICBEZWZhdWx0cyB0byBgZmFsc2VgIHNvIE9TRCBzaG93cyByYXcgcGl4ZWxzIChuZWFyZXN0LW5laWdoYm91cikuICovXG4gIHByb3RlY3RlZCBpbWFnZVNtb290aGluZ0VuYWJsZWQgPSBmYWxzZTtcbiAgZmlsZU5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSBsb2FkZWRGaWxlTmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBwcm90ZWN0ZWQgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkO1xuXG4gIC8vIOKUgOKUgCBUaGUgbG9hZGluZyBvdmVybGF5LCBhcyBzaWduYWxzOiBpdCBpcyBmZWQgYnkgdGhlIGhvc3QncyBhbmQgdGhlIGJhY2tlbmRzJyBzdHJlYW1zXG4gIC8vIGFuZCBieSB0aGUgcmVuZGVyIHNlc3Npb24sIGFsbCBvZiB3aGljaCBsYW5kIG91dHNpZGUgdGhpcyB2aWV3J3MgdGVtcGxhdGUgZXZlbnRzLiDilIDilIBcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGxvYWRpbmdNZXNzYWdlID0gc2lnbmFsKCdMb2FkaW5nIGltYWdlLi4uJyk7XG4gIC8qKiBUaGUgaG9zdCBpcyBsb2FkaW5nIGF0IHpvb20gKHRoZSBvdmVybGF5IG1lc3NhZ2UgbW92ZXMgb3V0IG9mIHRoZSB3YXkpLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgem9vbSA9IHNpZ25hbChmYWxzZSk7XG4gIHByb3RlY3RlZCByZWFkb25seSBzdGFja0xvYWRpbmcgPSBzaWduYWwoZmFsc2UpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaW1nTG9hZGluZyA9IHNpZ25hbChmYWxzZSk7XG4gIHByb3RlY3RlZCByZWFkb25seSBsb2FkaW5nUGVyY2VudGFnZSA9IHNpZ25hbCgwKTtcbiAgLy8gTm9uLW51bGwgKDAuLjEwMCkgd2hpbGUgaml0LXNlcnZpY2UgaXMgY29weWluZyB0aGUgc291cmNlIGZpbGUgaW50byB0aGVcbiAgLy8gbG9jYWwgY2FjaGUgUFZDIG9uIGZpcnN0IGNsaWNrLiBXaGVuIHNldCwgdGhlIGxvYWRpbmcgb3ZlcmxheSBzd2FwcyB0aGVcbiAgLy8gc3Bpbm5lciBmb3IgYSBkZXRlcm1pbmF0ZSBwcm9ncmVzcyBiYXIgd2l0aCBhIFwiQ2FjaGluZyBpbWFnZVwiIG1lc3NhZ2UuXG4gIHByb3RlY3RlZCByZWFkb25seSBjYWNoZVByb2dyZXNzID0gc2lnbmFsPG51bWJlciB8IG51bGw+KG51bGwpO1xuICAvKiogQSBqaXQtc2VydmljZSBjYWNoZSBjb3B5IGlzIGFjdGl2ZWx5IGluIHByb2dyZXNzIChkZXRlcm1pbmF0ZSAwLi45OSkuIERyaXZlcyB0aGUgY2FjaGVcbiAgICogIHByb2dyZXNzIGJhciBpbmRlcGVuZGVudGx5IG9mIGBpbWdMb2FkaW5nYCBzbyB0aGUgYmFyIGNhbid0IGJlIGRyb3BwZWQg4oCUIG9yIG1hc2tlZCBieSB0aGVcbiAgICogIFwiU2hhcnBlbmluZyBwcmV2aWV3Li4uXCIgb3ZlcmxheSDigJQgbWlkLWRvd25sb2FkIGlmIHRoZSBsb2FkaW5nIGZsYWcgZmxpcHMgZWFybHkuICovXG4gIHByb3RlY3RlZCByZWFkb25seSBpc0NhY2hpbmcgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgY29uc3QgcCA9IHRoaXMuY2FjaGVQcm9ncmVzcygpO1xuICAgIHJldHVybiBwICE9PSBudWxsICYmIHAgPCAxMDA7XG4gIH0pO1xuXG4gIC8qKiBFbWl0cyBvbmNlIHdoZW4gdGhpcyB2aWV3IGlzIGRlc3Ryb3llZDsgdGhlIGNvbGxhYm9yYXRvcnMgZW5kIHRoZWlyIHN1YnNjcmlwdGlvbnMgb24gaXQuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgZGVzdHJveWVkJCA9IG5ldyBPYnNlcnZhYmxlPHZvaWQ+KChzdWJzY3JpYmVyKSA9PlxuICAgIHRoaXMuZGVzdHJveVJlZi5vbkRlc3Ryb3koKCkgPT4ge1xuICAgICAgc3Vic2NyaWJlci5uZXh0KCk7XG4gICAgICBzdWJzY3JpYmVyLmNvbXBsZXRlKCk7XG4gICAgfSksXG4gICk7XG4gIC8qKiBIb3N0IGltYWdlLWluZm8gb2JqZWN0cyB3aG9zZSBvbmUtc2hvdCBgaW5pdGlhbFpJbmRleGAgd2FzIGFscmVhZHkgYXBwbGllZC4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBjb25zdW1lZFNsaWNlSGludHMgPSBuZXcgV2Vha1NldDxJSW1hZ2VJbmZvPigpO1xuICBwcm90ZWN0ZWQgekluZGV4ID0gMDtcbiAgcHJvdGVjdGVkIG1heEluZGV4ID0gMDtcblxuICBAVmlld0NoaWxkKCdjbScpIGNvbnRleHRNZW51ITogQ29udGV4dE1lbnU7XG4gIHByb3RlY3RlZCBjb250ZXh0TWVudUl0ZW1zOiBNZW51SXRlbVtdID0gW107XG5cbiAgLyoqIFRoZSBhcm1lZCB0b29sIG1vZGUgYW5kIHRoZSB0b29sYmFyJ3MgdG9vbCBzZXR0aW5ncy4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHRvb2xzID0gbmV3IFRvb2xNb2RlcyhcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLFxuICAgIHRoaXMuc2Vzc2lvbixcbiAgICB0aGlzLm5nWm9uZSxcbiAgICAobW9kZSkgPT4ge1xuICAgICAgaWYgKG1vZGUgIT09ICdzYW1Qb2ludCcpIHRoaXMuc2VnbWVudGF0aW9uLmhpZGUoKTtcbiAgICB9LCAvLyBsZWF2aW5nIHBvaW50IG1vZGUgZHJvcHMgaXRzIHRvYXN0XG4gICAgKCkgPT4gdGhpcy5jaGFuZ2VkKCksXG4gICk7XG4gIHByb3RlY3RlZCBnZXQgYWN0aXZlRHJhZ01vZGUoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMudG9vbHMuYWN0aXZlO1xuICB9XG4gIHByb3RlY3RlZCBzZXQgYWN0aXZlRHJhZ01vZGUobW9kZTogc3RyaW5nIHwgbnVsbCkge1xuICAgIHRoaXMudG9vbHMuYWN0aXZlID0gbW9kZTtcbiAgfVxuICBwcm90ZWN0ZWQgZ2V0IHdhbmRTZW5zaXRpdml0eSgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnRvb2xzLndhbmRTZW5zaXRpdml0eTtcbiAgfVxuICBwcm90ZWN0ZWQgc2V0IHdhbmRTZW5zaXRpdml0eSh2OiBudW1iZXIpIHtcbiAgICB0aGlzLnRvb2xzLndhbmRTZW5zaXRpdml0eSA9IHY7XG4gIH1cbiAgcHJvdGVjdGVkIGdldCBicnVzaFNpemUoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy50b29scy5icnVzaFNpemU7XG4gIH1cbiAgcHJvdGVjdGVkIGdldCB2ZXJ0ZXhFcmFzZXJSYWRpdXMoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy50b29scy52ZXJ0ZXhFcmFzZXJSYWRpdXM7XG4gIH1cbiAgcHJvdGVjdGVkIHNldCB2ZXJ0ZXhFcmFzZXJSYWRpdXModjogbnVtYmVyKSB7XG4gICAgdGhpcy50b29scy52ZXJ0ZXhFcmFzZXJSYWRpdXMgPSB2O1xuICB9XG5cbiAgLyoqIFJlZ2lvbiBzZXQtb3BlcmF0aW9ucyBvbiB0aGUgc2VsZWN0aW9uLCBhbmQgdGhlIHN0b3JlIG1pcnJvcnMgdGhleSByZWFkIChqaXQtdWkjODUpLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcmVnaW9uQWN0aW9ucyA9IG5ldyBSZWdpb25BY3Rpb25zKFxuICAgIHRoaXMucGxvdFNlcnZpY2UsXG4gICAgdGhpcy5yZWdpb25PcHMsXG4gICAgKCkgPT4gdGhpcy5pbWFnZUluZm8/LnRydWVJbWFnZVNpemUsXG4gICAgKG0pID0+IHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHsga2V5OiB0aGlzLnJlc3VsdFRvYXN0S2V5LCAuLi5tIH0pLFxuICApO1xuICAvKiogQ3VzdG9tLXRocmVzaG9sZCBTaW1wbGlmeSBkaWFsb2cgdmlzaWJpbGl0eSAoc2VlIHtAbGluayBSZWdpb25BY3Rpb25zfSkuICovXG4gIHByb3RlY3RlZCBnZXQgZGlzcGxheVNpbXBsaWZ5RGlhbG9nKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnJlZ2lvbkFjdGlvbnMuZGlzcGxheVNpbXBsaWZ5RGlhbG9nO1xuICB9XG4gIHByb3RlY3RlZCBzZXQgZGlzcGxheVNpbXBsaWZ5RGlhbG9nKHY6IGJvb2xlYW4pIHtcbiAgICB0aGlzLnJlZ2lvbkFjdGlvbnMuZGlzcGxheVNpbXBsaWZ5RGlhbG9nID0gdjtcbiAgfVxuXG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIG9wdGlvbnMgKyBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzkwIFAxKS4gT25seSBtb2RlbHNcbiAgICogIHdpdGggYSBob3N0ZWQgT05OWCBwYWlyIChjb25maWd1cmVkIHZpYSBzZXRTYW1Nb2RlbFVybHMgYXQgYXBwIGluaXQpIGFyZVxuICAgKiAgb2ZmZXJlZCwgc28gdGhlIHBpY2tlciBjYW4ndCBzZWxlY3QgYSBtb2RlbCB0aGF0IGNhbid0IHJ1bi4gKi9cbiAgcHJvdGVjdGVkIHNhbU1vZGVscyA9IFNBTV9NT0RFTFMuZmlsdGVyKGlzU2FtTW9kZWxSZWFkeSkubWFwKChtKSA9PiAoeyBpZDogbS5pZCwgbGFiZWw6IG0ubGFiZWwgfSkpO1xuICBwcm90ZWN0ZWQgc2FtTW9kZWxJZCA9IGdldERlZmF1bHRTYW1Nb2RlbElkKCk7XG5cbiAgLyoqXG4gICAqIE5vLXByb21wdCB0b29scyByZWdpc3RlcmVkIHRocm91Z2gge0BsaW5rIFRPT0xCQVJfVE9PTFN9LCBmaWx0ZXJlZCB0byB0aG9zZVxuICAgKiB3aXRoIGNvbmZpZ3VyZWQgbW9kZWxzIGFuZCBzb3J0ZWQuIFRoaXMgbGlicmFyeSByZWdpc3RlcnMgbm9uZSDigJQgYSBob3N0XG4gICAqIHN1cHBsaWVzIHRoZW0g4oCUIHNvIGFuIG9wZW4gYnVpbGQgaGFzIGFuIGVtcHR5IGFycmF5IGhlcmUgYW5kIHRoZSB0b29sYmFyXG4gICAqIGFuZCBoZWxwIGRpYWxvZyBzaW1wbHkgb21pdCB0aGF0IGdyb3VwLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnRyaWJ1dGVkVG9vbHM6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uW10gPSBbXTtcbiAgLyoqIENoZWNrcG9pbnRzIGFuZCBwYXJhbWV0ZXIgdmFsdWVzIG9mIHtAbGluayBjb250cmlidXRlZFRvb2xzfSwgYW5kIHRoZWlyIGRpYWxvZy4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHRvb2xQYXJhbXM6IFRvb2xQYXJhbXNNb2RlbDtcbiAgLyoqIENvbnRyaWJ1dGVkIHBsb3QgbW9kZXMgYW5kIGRpYWxvZyB0b29scywgYW5kIHRoZWlyIGxpdmUgc2Vzc2lvbnMuICovXG4gIHJlYWRvbmx5IGNvbnRyaWJ1dGlvbnM6IENvbnRyaWJ1dGlvbkhvc3Q7XG4gIC8qKiBEaWFsb2cgdG9vbHMgcmVnaXN0ZXJlZCB0aHJvdWdoIHtAbGluayBUT09MQkFSX1RPT0xTfSAoYGtpbmQ6ICdkaWFsb2cnYCkuICovXG4gIHByb3RlY3RlZCBnZXQgZGlhbG9nVG9vbHMoKTogVG9vbGJhckRpYWxvZ1Rvb2xDb250cmlidXRpb25bXSB7XG4gICAgcmV0dXJuIHRoaXMuY29udHJpYnV0aW9ucy5kaWFsb2dUb29scztcbiAgfVxuICAvKiogVGhlIGRpYWxvZyB0b29sIHRoZSB1c2VyIGhhcyBvcGVuIChpdHMgc2Vzc2lvbiBtYXkgYmUgYmV0d2VlbiByZW5kZXJzKS4gKi9cbiAgcHJvdGVjdGVkIGdldCBvcGVuRGlhbG9nVG9vbElkKCk6IHN0cmluZyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNvbnRyaWJ1dGlvbnMub3BlbkRpYWxvZ1Rvb2xJZDtcbiAgfVxuICAvKiogVGhlIG9wZW4gZGlhbG9nIHRvb2wncyBsaXZlIGRpYWxvZywgYXMgdGhlIHRlbXBsYXRlIHJlbmRlcnMgaXQuICovXG4gIHByb3RlY3RlZCBnZXQgdG9vbERpYWxvZygpOiBUb29sRGlhbG9nVmlldyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNvbnRyaWJ1dGlvbnMudG9vbERpYWxvZztcbiAgfVxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc2FtVG9hc3RLZXkgPSBgc2FtLSR7VmlzdWFsaXplckNvbXBvbmVudC5uZXh0VG9hc3RJZCsrfWA7XG4gIC8qKlxuICAgKiBPdXRsZXQgZm9yIHRoZSBsaWJyYXJ5J3Mgb3duIHJlc3VsdC9lcnJvciBub3RpY2VzLCBrZXllZCB0byB0aGlzIGluc3RhbmNlIHNvIHRoZXlcbiAgICogZG8gbm90IGRlcGVuZCBvbiB0aGUgaG9zdCBtb3VudGluZyBhIGtleWxlc3MgYDxwLXRvYXN0PmAgKG1vc3QgaG9zdHMgZG9uJ3QsIGFuZCBhXG4gICAqIGZhaWxlZCBzZWdtZW50YXRpb24gdGhlbiBsb29rZWQgbGlrZSBhIGRlYWQgYnV0dG9uKS4gRGVsaWJlcmF0ZWx5IE5PVCB0aGUgc3RpY2t5XG4gICAqIHRvYXN0J3Mga2V5OiB0aGF0IGlzIGNsZWFyZWQgd2hlbiBhIHJ1biBzZXR0bGVzLCB3aGljaCB3b3VsZCB3aXBlIHRoZSByZXN1bHQuXG4gICAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcmVzdWx0VG9hc3RLZXkgPSBgJHt0aGlzLnNhbVRvYXN0S2V5fS1yZXN1bHRgO1xuICAvKiogU2VnbWVudGF0aW9uIHJ1bnMgYW5kIHRoZWlyIHN0aWNreSBwcm9ncmVzcyB0b2FzdC4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHNlZ21lbnRhdGlvbiA9IG5ldyBTZWdtZW50YXRpb25SdW5uZXIoXG4gICAgdGhpcy5tZXNzYWdlU2VydmljZSxcbiAgICB0aGlzLnNhbVRvYXN0S2V5LFxuICAgIHRoaXMucmVzdWx0VG9hc3RLZXksXG4gICAgKCkgPT4gdGhpcy5jaGFuZ2VkKCksXG4gICk7XG5cbiAgLyoqIENoYW5uZWxzICYgSGlzdG9ncmFtIGRpYWxvZyB2aXNpYmlsaXR5IChvcGVuZWQgZnJvbSB0aGUgdG9vbGJhcikuICovXG4gIHByb3RlY3RlZCBzaG93Q2hhbm5lbEhpc3RvZ3JhbSA9IGZhbHNlO1xuICAvKiogU3BhdGlhbC1vbWljcyBjb250cm9scyBkaWFsb2cgdmlzaWJpbGl0eSAodG9vbGJhciBidXR0b24pLiAqL1xuICBwcm90ZWN0ZWQgc2hvd1NwYXRpYWxDb250cm9scyA9IGZhbHNlO1xuICAvKiogUmVnaW9uIEVkaXRvciBkaWFsb2cgdmlzaWJpbGl0eSAob3BlbmVkIGZyb20gdGhlIHRvb2xiYXIpLiAqL1xuICBwcm90ZWN0ZWQgc2hvd1JlZ2lvbkVkaXRvciA9IGZhbHNlO1xuICAvKiogUmVnaW9uIEVkaXRvciBkaWFsb2cgd2lkdGguIE9uIG9wZW4gaXQgaXMgc2V0IHRvIHRoZSBjb25maWd1cmVkIGhvc3RcbiAgICogIGVsZW1lbnQncyBjdXJyZW50IHdpZHRoLCBvciDigJQgd2hlbiBubyBzZWxlY3RvciBpcyBjb25maWd1cmVkIC8gdGhlIGVsZW1lbnRcbiAgICogIGlzbid0IGZvdW5kIOKAlCB0byBhIHF1YXJ0ZXIgb2YgdGhlIHBhZ2UgKHNlZSBvcGVuUmVnaW9uRWRpdG9yKS4gKi9cbiAgcHJvdGVjdGVkIHJlZ2lvbkVkaXRvcldpZHRoID0gJzI1dncnO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcGxvdERpdk5hbWUgPSBgdml6LXBsb3QtJHtwbG90SW5zdGFuY2VTZXErK31gO1xuICAvKiogVGhlIHJlbmRlciBwaXBlbGluZTogdGhlIG5ld2VzdCBpbWFnZSAob3IgaW1hZ2UtbGVzcyBkYXRhc2V0KSBhbHdheXMgd2lucy4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHJlbmRlcjogSW1hZ2VSZW5kZXJTZXNzaW9uID0gbmV3IEltYWdlUmVuZGVyU2Vzc2lvbih7XG4gICAgcGxvdERpdk5hbWU6IHRoaXMucGxvdERpdk5hbWUsXG4gICAgdmlzdWFsaXplcjogdGhpcy5wbG90U2VydmljZSxcbiAgICB6SW5kZXg6ICgpID0+IHRoaXMuekluZGV4LFxuICAgIHBsb3RUeXBlOiAoKSA9PiB0aGlzLnBsb3RUeXBlLFxuICAgIGlzSGVhdG1hcDogKCkgPT4gdGhpcy5pc0hlYXRtYXAsXG4gICAgaXNDYWNoaW5nOiAoKSA9PiB0aGlzLmlzQ2FjaGluZygpLFxuICAgIGJlZm9yZVJlc2V0OiAoKSA9PiB0aGlzLmNvbnRyaWJ1dGlvbnMuZW5kU2Vzc2lvbnMoKSxcbiAgICBwcmVwYXJlOiAoaW5mbykgPT4gdGhpcy5sYXlPdXRTbGljZXMoaW5mbyksXG4gICAgcmVsZWFzZU92ZXJsYXk6IChpbmZvKSA9PiB7XG4gICAgICBpZiAoaW5mby5pc1N0YWNrICYmIGluZm8uc2hvd1N0YWNrKSB0aGlzLnN0YWNrTG9hZGluZy5zZXQoZmFsc2UpO1xuICAgICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgIH0sXG4gICAgbGFuZGVkOiAoaW5mbykgPT4ge1xuICAgICAgYXBwbHlJbWFnZVJvaXMoaW5mbywgdGhpcy5wbG90U2VydmljZSwgdGhpcy56SW5kZXgpO1xuICAgICAgdGhpcy5jb250cmlidXRpb25zLmFjdGl2YXRlKCk7XG4gICAgfSxcbiAgICBzZXRJbWFnZUxvYWRpbmc6IChsb2FkaW5nKSA9PiB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyhsb2FkaW5nKSxcbiAgICBhbGVydDogKHNldmVyaXR5LCBzdW1tYXJ5LCBkZXRhaWwpID0+XG4gICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7IGtleTogdGhpcy52aXpBbGVydFRvYXN0S2V5LCBzZXZlcml0eSwgc3VtbWFyeSwgZGV0YWlsIH0pLFxuICAgIGRldGVjdENoYW5nZXM6ICgpID0+IHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKSxcbiAgfSk7XG4gIHBsb3RUeXBlID0gUGxvdFR5cGUuSU1BR0U7XG4gIHByb3RlY3RlZCBpc0hlYXRtYXAgPSB0cnVlO1xuICBwcm90ZWN0ZWQgYWN0aXZlU3VyZmFjZTNkTW9kZSA9ICd0dXJudGFibGUnO1xuICAvKiogV2hldGhlciB0aGUgbmFwYXJpIDNEIGNvb3JkaW5hdGUtYXhlcyAvIHNjYWxlIGdpem1vIGlzIHNob3duICh2b2x1bWUvaXNvc3VyZmFjZSkuICovXG4gIHByb3RlY3RlZCBheGVzVmlzaWJsZSA9IHRydWU7XG4gIHByb3RlY3RlZCB3aXJlZnJhbWVBY3RpdmUgPSBmYWxzZTtcbiAgLyoqIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKDEgPSBGdWxsIOKApiA4ID0g4oWbOyBkZWZhdWx0IMK9KTsgY2hhbmdpbmcgaXQgcmUtcGxvdHMuICovXG4gIHByb3RlY3RlZCByZXNvbHV0aW9uU2NhbGUgPSBOQVBBUklfREVGQVVMVF9ERUNJTUFURTtcblxuICAvKiogVGhlIHNlbGVjdG9yJ3MgZW50cmllczogdGhlIGJ1aWx0LWluIHBsb3QgdHlwZXMgdGhlIGFjdGl2ZSBiYWNrZW5kXG4gICAqICBhZHZlcnRpc2VzICgzRCBnYXRlZCBieSBjYXBhYmlsaXR5KSwgdGhlbiBhbnkgY29udHJpYnV0ZWQgbW9kZXNcbiAgICogICh7QGxpbmsgUExPVF9UWVBFX0NPTlRSSUJVVElPTlN9KS4gKi9cbiAgcHJvdGVjdGVkIHBsb3RUeXBlTWVudTogUGxvdFR5cGVPcHRpb25bXSA9IFtdO1xuICAvKipcbiAgICogVGhlIGJ1aWx0LWluIHBsb3QgdHlwZXMgb24gb2ZmZXIg4oCUIHtAbGluayBwbG90VHlwZU1lbnV9IHdpdGhvdXQgY29udHJpYnV0ZWRcbiAgICogbW9kZXMuIEtlcHQgd2l0aCBpdHMgb3JpZ2luYWwgdHlwZSBzbyBleGlzdGluZyByZWFkZXJzIGFyZSB1bmFmZmVjdGVkLlxuICAgKi9cbiAgcHJvdGVjdGVkIHBsb3RUeXBlT3B0aW9uczogUGxvdFR5cGVEZXNjcmlwdG9yW10gPSBbXTtcbiAgLyoqXG4gICAqIFdoYXQgdGhlIHNlbGVjdG9yIHNob3dzOiBhIGJ1aWx0LWluIHR5cGUgb3IgYSBjb250cmlidXRlZCBtb2RlJ3MgaWQuIEV2ZXJ5XG4gICAqIHJlbmRlcmluZyBhbmQgdG9vbCBkZWNpc2lvbiBnb2VzIHRocm91Z2gge0BsaW5rIGJhc2VQbG90VHlwZX0gaW5zdGVhZCwgc28gYVxuICAgKiBjb250cmlidXRlZCBtb2RlIGJlaGF2ZXMgZXhhY3RseSBsaWtlIHRoZSBidWlsdC1pbiB0eXBlIGl0IHJpZGVzIG9uLlxuICAgKi9cbiAgcHJvdGVjdGVkIHNlbGVjdGVkUGxvdFR5cGVJZDogUGxvdFR5cGVJZCA9IFBsb3RUeXBlLklNQUdFO1xuXG4gIC8qKlxuICAgKiBUaGUgYnVpbHQtaW4gcGxvdCB0eXBlIGJlaW5nIHJlbmRlcmVkLiBXaXRoIG5vIGNvbnRyaWJ1dGVkIG1vZGUgYWN0aXZlIHRoaXNcbiAgICogaXMgdGhlIHNlbGVjdGlvbiBpdHNlbGY7IGR1cmluZyBvbmUgaXQgaXMgdGhlIG1vZGUncyBgYmFzZVR5cGVgLiBLZXB0IHdpdGggaXRzXG4gICAqIG9yaWdpbmFsIGBQbG90VHlwZWAgdHlwZSBmb3IgZXhpc3RpbmcgcmVhZGVycyDigJQgdXNlIHtAbGluayBzZWxlY3RlZFBsb3RUeXBlSWR9XG4gICAqIGZvciB0aGUgc2VsZWN0b3IncyBpZC4gQXNzaWduaW5nIHNlbGVjdHMgdGhhdCBidWlsdC1pbiB0eXBlLlxuICAgKi9cbiAgcHJvdGVjdGVkIGdldCBzZWxlY3RlZFBsb3RUeXBlKCk6IFBsb3RUeXBlIHtcbiAgICByZXR1cm4gdGhpcy5iYXNlUGxvdFR5cGU7XG4gIH1cbiAgcHJvdGVjdGVkIHNldCBzZWxlY3RlZFBsb3RUeXBlKHR5cGU6IFBsb3RUeXBlKSB7XG4gICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlSWQgPSB0eXBlO1xuICB9XG5cbiAgLyoqIENvbnRyaWJ1dGVkIHBsb3QgbW9kZXMgYW5kIHRoZWlyIHNpbmdsZSBsaXZlIHNlc3Npb24uICovXG4gIGdldCBwbG90TW9kZXMoKTogUGxvdE1vZGVDb250cm9sbGVyIHtcbiAgICByZXR1cm4gdGhpcy5jb250cmlidXRpb25zLnBsb3RNb2RlcztcbiAgfVxuICAvKiogVGhlIGxpdmUgY29udHJpYnV0ZWQgbW9kZSdzIHNpZGUgcGFuZWw7IG51bGwgd2hpbGUgbm8gY29udHJpYnV0ZWQgc2Vzc2lvbiBpcyBsaXZlLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IHBsb3RNb2RlUGFuZWwoKTogUGxvdE1vZGVQYW5lbFZpZXcgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5jb250cmlidXRpb25zLnBsb3RNb2RlUGFuZWw7XG4gIH1cblxuICAvKiogV2hlcmUgYSBgbW91bnRgIHBhbmVsJ3MgaG9zdCBlbGVtZW50IGlzIGF0dGFjaGVkIG9uY2UgdGhlIGRpYWxvZyByZW5kZXJzLiAqL1xuICBAVmlld0NoaWxkKCdwbG90TW9kZVBhbmVsU2xvdCcpXG4gIHNldCBwbG90TW9kZVBhbmVsU2xvdChyZWY6IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+IHwgdW5kZWZpbmVkKSB7XG4gICAgdGhpcy5jb250cmlidXRpb25zLmF0dGFjaFBhbmVsU2xvdChyZWY/Lm5hdGl2ZUVsZW1lbnQpO1xuICB9XG5cbiAgLyoqIFRoZSBidWlsdC1pbiB0eXBlIGFjdHVhbGx5IHJlbmRlcmVkIGZvciB7QGxpbmsgc2VsZWN0ZWRQbG90VHlwZUlkfTogaXRzZWxmXG4gICAqICBmb3IgYSBidWlsdC1pbiB0eXBlLCBgYmFzZVR5cGVgIGZvciBhIGNvbnRyaWJ1dGVkIG1vZGUuICovXG4gIHByb3RlY3RlZCBnZXQgYmFzZVBsb3RUeXBlKCk6IFBsb3RUeXBlIHtcbiAgICByZXR1cm4gdGhpcy5wbG90TW9kZXMuYmFzZVR5cGVPZih0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCk7XG4gIH1cblxuICAvKiogRGl2IHRoZSBmbG9hdGluZyBpbnRlbnNpdHktcHJvZmlsZSBpbnNldCBpcyBjaGFydGVkIGludG8uIFBlciBpbnN0YW5jZSwgbGlrZVxuICAgKiAge0BsaW5rIHBsb3REaXZOYW1lfTogdGhlIGJhY2tlbmQgcmVzb2x2ZXMgaXQgYnkgaWQsIHNvIGEgZml4ZWQgaWQgbWFkZSB0d28gbGl2ZVxuICAgKiAgdmlld2VycyBkcmF3IGJvdGggaW5zZXRzIGludG8gd2hpY2hldmVyIGRpdiBjYW1lIGZpcnN0IGluIHRoZSBkb2N1bWVudC4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGludGVuc2l0eUluc2V0RGl2ID0gYCR7dGhpcy5wbG90RGl2TmFtZX0taW5zZXRgO1xuXG4gIC8qKiBUb29sYmFyIGRvY2tpbmc6IGRvY2tlZCBhY3Jvc3MgdGhlIHRvcCBieSBkZWZhdWx0OyBkcmFnZ2luZyBpdHMgaGFuZGxlXG4gICAqICBkZXRhY2hlcyBpdCBpbnRvIGEgZmxvYXRpbmcsIG1vdmFibGUgd2luZG93IChmcmVlcyB0aGUgdG9wIHJvdyBmb3IgdGhlXG4gICAqICB2aXN1YWxpemF0aW9uKS4gKi9cbiAgcHJvdGVjdGVkIHRvb2xiYXJGbG9hdGluZyA9IGZhbHNlO1xuICBwcm90ZWN0ZWQgdG9vbGJhclBvcyA9IHsgeDogOCwgeTogOCB9O1xuICAvKiogV2hlcmUgYSB0b29sYmFyLWhhbmRsZSBkcmFnIHN0YXJ0cyBmcm9tOiBncmFiYmluZyB0aGUgZG9ja2VkIHRvb2xiYXIgZmxvYXRzIGl0LiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgdG9vbGJhckRyYWdPcmlnaW4gPSAoKTogRmxvYXRpbmdQb3MgPT4ge1xuICAgIGlmICghdGhpcy50b29sYmFyRmxvYXRpbmcpIHtcbiAgICAgIHRoaXMudG9vbGJhckZsb2F0aW5nID0gdHJ1ZTtcbiAgICAgIHRoaXMudG9vbGJhclBvcyA9IHsgeDogOCwgeTogOCB9O1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy50b29sYmFyUG9zO1xuICB9O1xuICAvKiogVGhlIGZsb2F0aW5nIGludGVuc2l0eS1wcm9maWxlIGluc2V0IChhbHdheXMgbW91bnRlZDsgc2hvd3MgaXRzZWxmIHdoaWxlIGxpbmVzIGV4aXN0KS4gKi9cbiAgQFZpZXdDaGlsZChJbnRlbnNpdHlJbnNldENvbXBvbmVudCwgeyBzdGF0aWM6IHRydWUgfSkgaW5zZXQ/OiBJbnRlbnNpdHlJbnNldENvbXBvbmVudDtcblxuICAvKiogVHJ1ZSB3aGlsZSB0aGUgM0Qgc3BhdGlhbCBjbG91ZCBpcyB0aGUgYWN0aXZlIG1vZGUuIFRoZSBjb250cm9scyBkcm9wIHRoZSBST0lcbiAgICogIHNlbGVjdGlvbiB0aGVyZTogcmVnaW9uIHRvb2xzIGFyZSBzY3JlZW4tc3BhY2UsIGFuZCBhZ2FpbnN0IGFuIG9yYml0aW5nXG4gICAqICBjYW1lcmEgYSBkcmF3biByZWN0YW5nbGUgaGFzIG5vIGZpeGVkIG1lYW5pbmcgaW4gdGhlIGRhdGEuICovXG4gIHByb3RlY3RlZCBnZXQgaXNTcGF0aWFsM2RNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc1NwYXRpYWxPbWljczNkKHRoaXMuYmFzZVBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBUcnVlIGZvciB0aGUgSW1hZ2UgcGxvdCB0eXBlLCB3aGljaCByZW5kZXJzIGFzIGEgbmF0aXZlbHkgcGFuL3pvb20tYWJsZVxuICAgKiAgcmFzdGVyIOKAlCBzbyB0aGUgYmFja2VuZC1hZ25vc3RpYyB6b29tL3BhbiB0b29sYmFyIHRvb2xzIGFyZSBoaWRkZW4uIFRoZVxuICAgKiAgY29tcG9uZW50IGRyaXZlcyB0aGlzIG9mZiB0aGUgcGxvdCB0eXBlLCBub3QgdGhlIGFjdGl2ZSBiYWNrZW5kLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGlzSW1hZ2VWaWV3KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmJhc2VQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gIH1cblxuICAvKiogSXNvc3VyZmFjZSBiYW5kIGFzIGEgMOKAkzI1NSBzbGlkZXIgcG9zaXRpb24sIG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUncyByZWFsXG4gICAqICBpbnRlbnNpdHkgcmFuZ2UgYnkgdGhlIHJlbmRlcmVyLiBEZWZhdWx0cyB0byB0aGUgZnVsbCByYW5nZS4gKi9cbiAgcHJvdGVjdGVkIGlzb1JhbmdlOiBudW1iZXJbXSA9IFswLCAyNTVdO1xuXG4gIC8qKiBUaGUgc3BhdGlhbC1vbWljcyBkYXRhc2V0IG9uIG9mZmVyIGFuZCB0aGUgZ2F0ZXMgaXQgcHV0cyBvbiB0aGUgc2VsZWN0b3IuICovXG4gIHByb3RlY3RlZCByZWFkb25seSBzcGF0aWFsOiBTcGF0aWFsRGF0YXNldEJpbmRlcjtcbiAgLyoqIFdoZXRoZXIgYSBzcGF0aWFsLW9taWNzIGRhdGFzZXQgaXMgcHVibGlzaGVkIG9uIGBTUEFUSUFMX0RBVEFfUE9SVGAg4oCUIGdhdGVzIHRoZVxuICAgKiAgc3BhdGlhbCBwbG90IHR5cGVzOyB0aGUgdG9vbGJhciBvZmZlcnMgdGhlIHBsb3QgbW9kZXMgZm9yIGFuIGltYWdlLWxlc3Mgb25lIHRvby4gKi9cbiAgcHJvdGVjdGVkIGdldCBoYXNTcGF0aWFsRGF0YXNldCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zcGF0aWFsLmhhc0RhdGFzZXQ7XG4gIH1cblxuICAvKiogS2V5Ym9hcmQsIHdoZWVsIGFuZCBjb250ZXh0LW1lbnUgaGFuZGxpbmc7IGNyZWF0ZWQgb25jZSB0aGUgdmlldyBleGlzdHMuICovXG4gIHByaXZhdGUgc2hvcnRjdXRzPzogVmlld2VyU2hvcnRjdXRzO1xuXG4gIC8qKiBEZWJvdW5jZWQgei1zbGljZSBzY3J1YmJpbmcgKHNlZSBTbGljZVNjcnViYmVyIOKAlCByZWZhY3RvcmluZyBwbGFuIFN0ZXAgNykuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgc2NydWJiZXIgPSBuZXcgU2xpY2VTY3J1YmJlcigoeikgPT4ge1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0WkluZGV4KHopO1xuICAgIC8vIEluIGEgcGVyLXNsaWNlIHotc3RhY2sgc2Vzc2lvbiB0aGUgc3RvcmUgc3dhcHMgdGhlIGxpdmUgcmVnaW9uIHNldCB0byB0aGlzXG4gICAgLy8gc2xpY2UgKHByZXNlcnZpbmcgZWRpdHMgbWFkZSBvbiB0aGUgcHJldmlvdXMgb25lKTsgb3V0c2lkZSBzdGFjayBtb2RlIHRoaXNcbiAgICAvLyBqdXN0IHJlY29yZHMgdGhlIHNsaWNlLCBsZWF2aW5nIHNpbmdsZS1wbGFuZSByZWdpb25zIHVudG91Y2hlZCAoaml0LXVpIzkzKS5cbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldERpc3BsYXlTbGljZSh6KTtcbiAgfSk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgLy8gVGhlIGNvbnN0cnVjdG9yIHBhcmFtZXRlcnMgYmVsb3cgYXJlIHRoZSBpbmplY3Rpb24gcG9pbnRzOyBub25lIGlzIHB1YmxpYyBBUEkuXG4gICAgQEluamVjdChJTUFHRV9TVEFURV9QT1JUKSBwcml2YXRlIHN0YXRlOiBJbWFnZVN0YXRlUG9ydCxcbiAgICBASW5qZWN0KFZJU1VBTElaRVIpIHB1YmxpYyBwbG90U2VydmljZTogSVZpc3VhbGl6ZXIsXG4gICAgcHVibGljIG1lc3NhZ2VTZXJ2aWNlOiBNZXNzYWdlU2VydmljZSxcbiAgICBwcml2YXRlIG5nWm9uZTogTmdab25lLFxuICAgIHByaXZhdGUgY2RyOiBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICBwcml2YXRlIHNlc3Npb246IFZpc3VhbGl6ZXJTdG9yZSxcbiAgICBwcml2YXRlIHNhbVRvb2w6IFNhbVRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgY2VsbFNlZ21lbnRUb29sOiBDZWxsU2VnbWVudFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgc2FtUG9pbnRUb29sOiBTYW1Qb2ludFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVnaW9uT3BzOiBSZWdpb25PcHNTZXJ2aWNlLFxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoVklaX0NPTkZJRykgcHJpdmF0ZSB2aXpDb25maWc/OiBWaXpDb25maWcsXG4gICAgLy8gQSBjb25zdHJ1Y3RvciBwYXJhbWV0ZXIgcmF0aGVyIHRoYW4gYSBmaWVsZCBgaW5qZWN0KClgOiB0aGlzIGNvbXBvbmVudCBpc1xuICAgIC8vIGFsc28gY29uc3RydWN0ZWQgZGlyZWN0bHkgd2l0aCBgbmV3YCBpbiBpdHMgb3duIHNwZWNzLCB3aGljaCBpcyBvdXRzaWRlXG4gICAgLy8gYW4gaW5qZWN0aW9uIGNvbnRleHQgYW5kIHdvdWxkIHRocm93IE5HMDIwMyBvbiBhIGZpZWxkIGluaXRpYWxpemVyLlxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoVE9PTEJBUl9UT09MUykgdG9vbENvbnRyaWJ1dGlvbnM/OiByZWFkb25seSBUb29sYmFyQ29udHJpYnV0aW9uW10sXG4gICAgLy8gT3B0aW9uYWwgbGlrZSB0aGUgcmVzdDogYSBob3N0IHRoYXQgc2hvd3Mgb25seSBpbWFnZXMgbmV2ZXIgcHJvdmlkZXMgaXQsXG4gICAgLy8gYW5kIHRoZSBzcGF0aWFsIHBsb3QgdHlwZXMgc2ltcGx5IHN0YXkgaGlkZGVuLlxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoU1BBVElBTF9EQVRBX1BPUlQpIHByaXZhdGUgc3BhdGlhbERhdGE/OiBTcGF0aWFsRGF0YVBvcnQsXG4gICAgLy8gQ29udHJpYnV0ZWQgcGxvdCBtb2Rlcy4gTGlrZSBUT09MQkFSX1RPT0xTIHRoZSB0b2tlbiBoYXMgbm8gZmFjdG9yeSwgc28gYVxuICAgIC8vIGhvc3QgdGhhdCBwcm92aWRlcyBub25lIGdldHMgbnVsbCBoZXJlIGFuZCB0aGUgc2VsZWN0b3IgaXMgdW5jaGFuZ2VkLlxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoUExPVF9UWVBFX0NPTlRSSUJVVElPTlMpIHBsb3RUeXBlQ29udHJpYnV0aW9ucz86IHJlYWRvbmx5IFBsb3RUeXBlQ29udHJpYnV0aW9uW10sXG4gICAgLy8gUGFyZW50IG9mIGEgY29udHJpYnV0ZWQgcGFuZWwgY29tcG9uZW50J3MgaW5qZWN0b3IuIE9wdGlvbmFsIG9ubHkgc28gdGhlXG4gICAgLy8gc3BlY3MgY2FuIGNvbnN0cnVjdCB0aGlzIGNvbXBvbmVudCB3aXRoIGBuZXdgLlxuICAgIEBPcHRpb25hbCgpIHByaXZhdGUgaW5qZWN0b3I/OiBJbmplY3RvcixcbiAgICAvLyBUaGUgYDx2aXN1YWxpemVyPmAgZWxlbWVudDoga2V5Ym9hcmQgc2hvcnRjdXRzIGFyZSBzY29wZWQgdG8gaXQgKENPUkUtMikuXG4gICAgQE9wdGlvbmFsKCkgcHJpdmF0ZSBob3N0UmVmPzogRWxlbWVudFJlZjxIVE1MRWxlbWVudD4sXG4gICAgLy8gRW5kcyB0aGUgc3Vic2NyaXB0aW9ucyAoYHRha2VVbnRpbERlc3Ryb3llZGApLiBBIHBhcmFtZXRlciwgbGlrZSB0aGUgcmVzdCwgc28gdGhlXG4gICAgLy8gc3BlY3MnIGBuZXdgIGNhbiBwYXNzIG9uZTsgQW5ndWxhciBhbHdheXMgcHJvdmlkZXMgaXQuXG4gICAgcHJpdmF0ZSByZWFkb25seSBkZXN0cm95UmVmOiBEZXN0cm95UmVmID0gdW5kZWZpbmVkIGFzIHVua25vd24gYXMgRGVzdHJveVJlZixcbiAgKSB7XG4gICAgdGhpcy5jb250cmlidXRlZFRvb2xzID0gdmlzaWJsZVRvb2xDb250cmlidXRpb25zKHRvb2xDb250cmlidXRpb25zKTtcbiAgICB0aGlzLnRvb2xQYXJhbXMgPSBuZXcgVG9vbFBhcmFtc01vZGVsKHRoaXMuY29udHJpYnV0ZWRUb29scyk7XG4gICAgdGhpcy5jb250cmlidXRpb25zID0gbmV3IENvbnRyaWJ1dGlvbkhvc3QocGxvdFR5cGVDb250cmlidXRpb25zLCB0b29sQ29udHJpYnV0aW9ucywge1xuICAgICAgdmlzdWFsaXplcjogdGhpcy5wbG90U2VydmljZSxcbiAgICAgIHpvbmU6IHRoaXMubmdab25lLFxuICAgICAgdG9vbHM6IHRoaXMudG9vbHMucGxvdE1vZGVUb29scyxcbiAgICAgIGluamVjdG9yOiB0aGlzLmluamVjdG9yLFxuICAgICAgaW1hZ2VJbmZvJDogKCkgPT4gdGhpcy5zdGF0ZS5nZXRJbWFnZUluZm8kKCksXG4gICAgICBzZWxlY3RlZElkOiAoKSA9PiB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCxcbiAgICAgIHJlbmRlcmVkVHlwZTogKCkgPT4gdGhpcy5wbG90VHlwZSxcbiAgICAgIHNlbGVjdEJhc2U6IChiYXNlKSA9PiB7XG4gICAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gYmFzZTtcbiAgICAgICAgdGhpcy5wbG90VHlwZSA9IGJhc2U7XG4gICAgICB9LFxuICAgICAgbm90aWZ5OiAobSkgPT4gdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoeyBrZXk6IHRoaXMucmVzdWx0VG9hc3RLZXksIC4uLm0gfSksXG4gICAgICBkZXRlY3RDaGFuZ2VzOiAoKSA9PiB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCksXG4gICAgfSk7XG4gICAgdGhpcy5zcGF0aWFsID0gbmV3IFNwYXRpYWxEYXRhc2V0QmluZGVyKHRoaXMuc3BhdGlhbERhdGEsIHtcbiAgICAgIGdhdGVzQ2hhbmdlZDogKCkgPT4gdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCksXG4gICAgICBzZWxlY3RlZElkOiAoKSA9PiB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCxcbiAgICAgIGJhc2VQbG90VHlwZTogKCkgPT4gdGhpcy5iYXNlUGxvdFR5cGUsXG4gICAgICBoYXNJbWFnZTogKCkgPT4gISF0aGlzLmltYWdlSW5mbyxcbiAgICAgIHNlbGVjdFBsb3RUeXBlOiAodHlwZSkgPT4gdGhpcy5vblNlbGVjdFBsb3RUeXBlKHR5cGUpLFxuICAgICAgcmVwbG90OiAoKSA9PiB0aGlzLnJlbG9hZEFuZFBsb3QoKSxcbiAgICAgIHB1Ymxpc2hJbWFnZTogKGluZm8pID0+IHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKGluZm8pLFxuICAgICAgc2V0SW1hZ2VMb2FkaW5nOiAobG9hZGluZykgPT4gdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcobG9hZGluZyksXG4gICAgICBzZXR0bGVkOiAoKSA9PiB7XG4gICAgICAgIHRoaXMucmVjb25jaWxlU2VsZWN0ZWRQbG90VHlwZSgpO1xuICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgIH0sXG4gICAgICBkZXRlY3RDaGFuZ2VzOiAoKSA9PiB0aGlzLmNoYW5nZWQoKSxcbiAgICB9KTtcbiAgICB0aGlzLmNvbXB1dGVQbG90VHlwZU9wdGlvbnMoKTtcbiAgfVxuXG4gIC8qKiBGb2xsb3cgdGhlIHNwYXRpYWwtb21pY3MgZGF0YXNldDogYSBkYXRhc2V0IGFwcGVhcmluZywgc3dpdGNoaW5nIG9yIGJlaW5nIGNsZWFyZWRcbiAgICogIHJlLWdhdGVzIHRoZSBzZWxlY3RvciBleGFjdGx5IGFzIHRoZSBpbWFnZSBzdHJlYW0gYW5kIHRoZSB0ZXN0LW1vZGUgdG9nZ2xlIGRvLiAqL1xuICBwcml2YXRlIHdhdGNoU3BhdGlhbERhdGFzZXQoKTogdm9pZCB7XG4gICAgdGhpcy5zcGF0aWFsLmJpbmQodGhpcy5kZXN0cm95ZWQkKTtcbiAgfVxuXG4gIC8qKiBSZWNvbXB1dGUgdGhlIHNlbGVjdG9yJ3MgZW50cmllcyBmb3IgdGhlIGN1cnJlbnQgaW1hZ2UsIGRhdGFzZXQgYW5kIHRlc3QgbW9kZVxuICAgKiAgKHNlZSB7QGxpbmsgY29tcHV0ZVBsb3RUeXBlTWVudX0pLiAqL1xuICBwcml2YXRlIGNvbXB1dGVQbG90VHlwZU9wdGlvbnMoKSB7XG4gICAgY29uc3QgeyBidWlsdEluLCBtZW51IH0gPSBjb21wdXRlUGxvdFR5cGVNZW51KHtcbiAgICAgIGRlc2NyaXB0b3JzOiB0aGlzLnBsb3RTZXJ2aWNlLmdldFBsb3RUeXBlRGVzY3JpcHRvcnMoKSxcbiAgICAgIGNvbnRyaWJ1dGlvbnM6IHRoaXMucGxvdE1vZGVzLmRlc2NyaXB0b3JzKCksXG4gICAgICBjYXBzOiB0aGlzLnBsb3RTZXJ2aWNlLmNhcGFiaWxpdGllcyxcbiAgICAgIGltYWdlSW5mbzogdGhpcy5pbWFnZUluZm8sXG4gICAgICBzcGF0aWFsOiB0aGlzLnNwYXRpYWwsXG4gICAgICB0ZXN0TW9kZTogdGhpcy50ZXN0TW9kZSxcbiAgICB9KTtcbiAgICB0aGlzLnBsb3RUeXBlT3B0aW9ucyA9IGJ1aWx0SW47XG4gICAgdGhpcy5wbG90VHlwZU1lbnUgPSBtZW51O1xuICB9XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIC8vIFRoZSBjb25zdHJ1Y3RvciBjb21wdXRlcyB0aGUgb3B0aW9ucyB3aXRoIHRlc3RNb2RlIHN0aWxsIGF0IGl0cyBkZWZhdWx0O1xuICAgIC8vIHJlY29tcHV0ZSBvbmNlIHRoZSBob3N0IGJpbmRzIGl0IChhbmQgb24gYW55IGxhdGVyIGNoYW5nZSkgc28gdGhlIHNlbGVjdG9yXG4gICAgLy8gcmVmbGVjdHMgdGVzdCBtb2RlIGltbWVkaWF0ZWx5LCB3aXRob3V0IHdhaXRpbmcgZm9yIGFuIGltYWdlIHRvIChyZSlsb2FkLlxuICAgIGlmIChjaGFuZ2VzWyd0ZXN0TW9kZSddKSB7XG4gICAgICB0aGlzLmNvbXB1dGVQbG90VHlwZU9wdGlvbnMoKTtcbiAgICAgIC8vIElmIHRlc3QgbW9kZSBpcyB0dXJuZWQgb2ZmIHdoaWxlIGEgdGVzdC1vbmx5IHR5cGUgKGUuZy4gTkFQQVJJX0lNQUdFKSBpc1xuICAgICAgLy8gYWN0aXZlLCB0aGF0IHR5cGUgaXMgbm8gbG9uZ2VyIG9mZmVyZWQg4oCUIGZhbGwgYmFjayB0byBJbWFnZS5cbiAgICAgIHRoaXMucmVjb25jaWxlU2VsZWN0ZWRQbG90VHlwZSgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgaG9zdCBwdWJsaXNoZWQgTk8gaW1hZ2Ug4oCUIGEgc3BhdGlhbCBkYXRhc2V0IHRoYXQgYnJpbmdzIG5vbmUgZG9lcyB0aGlzLlxuICAgKlxuICAgKiBXaXRob3V0IGhhbmRsaW5nIHRoZSBlbXB0eSBlbWlzc2lvbiwgYGltYWdlSW5mb2Aga2VlcHMgdGhlIExBU1QgaW1hZ2UncyBtZXRhZGF0YTogdGhlXG4gICAqIHNlbGVjdG9yIGdvZXMgb24gb2ZmZXJpbmcgcGl4ZWwgbW9kZXMgZm9yIHBpeGVscyB0aGF0IGFyZSBnb25lLCBhbmQgZXZlcnlcbiAgICogYGlzU3RhY2tgL2Bpc0dyYXlzY2FsZWAgZGVjaXNpb24gaXMgbWFkZSBhZ2FpbnN0IGEgZmlsZSB0aGF0IGlzIG5vIGxvbmdlciBsb2FkZWQuXG4gICAqL1xuICBwcml2YXRlIG9uSW1hZ2VDbGVhcmVkKCk6IHZvaWQge1xuICAgIC8vIFRoZSBpbWFnZSBhIGNvbnRyaWJ1dGVkIG1vZGUgd2FzIGRyYXdpbmcgb3ZlciBpcyBnb25lLiBBbiBvcGVuIGRpYWxvZ1xuICAgIC8vIHRvb2wgc3RheXMgb3BlbiBhbmQgcmVzdGFydHMgb25jZSB0aGUgbmV4dCBpbWFnZSBoYXMgcGxvdHRlZC5cbiAgICB0aGlzLmNvbnRyaWJ1dGlvbnMuZW5kU2Vzc2lvbnMoKTtcbiAgICB0aGlzLmltYWdlSW5mbyA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLmxvYWRlZEZpbGVOYW1lID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuY29tcHV0ZVBsb3RUeXBlT3B0aW9ucygpO1xuICAgIC8vIERlbGliZXJhdGVseSBOT1QgcmVjb25jaWxpbmcgdGhlIHNlbGVjdGVkIHR5cGUgaGVyZS4gUmVjb25jaWxpbmcgY2FsbHNcbiAgICAvLyBgc2V0UGxvdFR5cGVgLCB3aGljaCBkcml2ZXMgYSByZS1wbG90IOKAlCBhbmQgdGhlcmUgaXMgbm90aGluZyB0byBwbG90LCBzbyB0aGVcbiAgICAvLyBQbG90bHkgYmFja2VuZCByZWFkIGl0cyBvd24gbm93LXVuc2V0IGBpbWFnZUluZm9gIGFuZCB0aHJld1xuICAgIC8vIChcImNhbid0IGFjY2VzcyBwcm9wZXJ0eSBpc0dyYXlzY2FsZVwiKSwgYWJvcnRpbmcgdGhlIGxvYWQgd2l0aCBubyBvYnNlcnZhdGlvbnNcbiAgICAvLyBkcmF3bi4gSWdub3JpbmcgdGhlIGVtcHR5IGVtaXNzaW9uIGVudGlyZWx5IGlzIHdoYXQgdXNlZCB0byBhdm9pZCB0aGF0LlxuICAgIC8vXG4gICAgLy8gTm90aGluZyBpcyBsZWZ0IHN0cmFuZGVkOiBhbiBpbWFnZSBpcyBjbGVhcmVkIGJlY2F1c2UgYSBzcGF0aWFsIGRhdGFzZXQgdGhhdFxuICAgIC8vIGJyaW5ncyBub25lIGlzIGJlaW5nIG9wZW5lZCwgYW5kIHRoYXQgZGF0YXNldCdzIG93biBzdWJzY3JpcHRpb24gc2VsZWN0cyB0aGVcbiAgICAvLyBtb2RlIGl0cyBjb29yZGluYXRlcyBzdXBwb3J0IGEgbW9tZW50IGxhdGVyLlxuICAgIHRoaXMuY2hhbmdlZCgpO1xuICB9XG5cbiAgLyoqIElmIHRoZSBhY3RpdmUgcGxvdCB0eXBlIGlzIG5vIGxvbmdlciBpbiB0aGUgb2ZmZXJlZCBvcHRpb25zIChlLmcuIHRlc3QgbW9kZVxuICAgKiAgdHVybmVkIG9mZiB3aGlsZSBhIHRlc3Qtb25seSB0eXBlIHdhcyBhY3RpdmUsIG9yIGEgc2NhbGFyIHR5cGUgY2FycmllZCBvbnRvXG4gICAqICBhbiBSR0IgaW1hZ2UpLCBmYWxsIGJhY2sgdG8gdGhlIGRlZmF1bHQgMkQgSW1hZ2Ugdmlldy4gKi9cbiAgcHJpdmF0ZSByZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlKCk6IHZvaWQge1xuICAgIGNvbnN0IGZhbGxiYWNrID0gcmVjb25jaWxlUGxvdFR5cGUodGhpcy5wbG90VHlwZU1lbnUsIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkKTtcbiAgICBpZiAoIWZhbGxiYWNrKSByZXR1cm47XG4gICAgLy8gTGVhdmluZyBhIGNvbnRyaWJ1dGVkIG1vZGUgKGl0cyBnYXRlcyBzdG9wcGVkIHBhc3NpbmcsIG9yIGl0cyBwcm92aWRlciBpc1xuICAgIC8vIGdvbmUpOiBpdHMgc2Vzc2lvbiBlbmRzIGJlZm9yZSBhbnl0aGluZyBlbHNlIGRyYXdzLlxuICAgIHRoaXMucGxvdE1vZGVzLmRlYWN0aXZhdGUoKTtcbiAgICBjb25zdCBiYXNlID0gdGhpcy5wbG90TW9kZXMuYmFzZVR5cGVPZihmYWxsYmFjayk7XG4gICAgaWYgKGJhc2UgIT09IFBsb3RUeXBlLklNQUdFKSB0aGlzLmNsb3NlRGlhbG9nVG9vbCgpO1xuICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gZmFsbGJhY2s7XG4gICAgdGhpcy5wbG90VHlwZSA9IGJhc2U7XG4gICAgdGhpcy5pc0hlYXRtYXAgPSBiYXNlID09PSBQbG90VHlwZS5JTUFHRTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFBsb3RUeXBlKGJhc2UpO1xuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgLy8gSm9pbiB0aGUgbGl2ZSBzZXQ7IHRoZSBvbGRlc3QgbWVtYmVyIHJlbmRlcnMgdGhlIHNoYXJlZCBub3RpY2Ugb3V0bGV0cy5cbiAgICBWaXN1YWxpemVyQ29tcG9uZW50LmxpdmVJbnN0YW5jZXMuYWRkKHRoaXMpO1xuICAgIHRoaXMuc3RhdGUuc2V0RGlhZ3JhbSh0aGlzLmhvc3RIYW5kbGUpO1xuICAgIHRoaXMud2F0Y2hTcGF0aWFsRGF0YXNldCgpO1xuICAgIC8vIE9TRCBhbmQgbmFwYXJpLWpzIGVtaXQgdGhpcyBmcm9tIHRoZWlyIG93biBcImZpdCB0byB2aWV3XCI6IGRpc2FybSB0aGUgdG9vbCBvblxuICAgIC8vIHRoZSBiYWNrZW5kIHRvbywgbm90IG9ubHkgdGhlIHRvb2xiYXIncyBoaWdobGlnaHQuXG4gICAgdGhpcy5wbG90U2VydmljZVxuICAgICAgLmdldEF1dG9zY2FsZUV2ZW50KClcbiAgICAgIC5waXBlKHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLmRlc3Ryb3lSZWYpKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLnRvb2xzLmFwcGx5KG51bGwpKTtcbiAgICAvLyBMb2FkaW5nL292ZXJsYXkgVUkgbWlycm9ycyAoc2lnbmFsczogdGhleSByZS1yZW5kZXIgdGhlIHZpZXcgdGhlbXNlbHZlcykuXG4gICAgdGhpcy5taXJyb3IodGhpcy5zdGF0ZS5pc0ltYWdlTG9hZGluZyQoKSwgKHYpID0+IHRoaXMuaW1nTG9hZGluZy5zZXQodikpO1xuICAgIHRoaXMubWlycm9yKHRoaXMucGxvdFNlcnZpY2UuZ2V0U3RhY2tMb2FkaW5nUHJvZ3Jlc3MoKSwgKHYpID0+IHRoaXMubG9hZGluZ1BlcmNlbnRhZ2Uuc2V0KHYpKTtcbiAgICB0aGlzLm1pcnJvcih0aGlzLnBsb3RTZXJ2aWNlLmlzU3RhY2tMb2FkaW5nKCksICh2KSA9PiB0aGlzLnN0YWNrTG9hZGluZy5zZXQodikpO1xuICAgIHRoaXMubWlycm9yKHRoaXMuc3RhdGUuZ2V0SW1hZ2VMb2FkaW5nTWVzc2FnZSQoKSwgKHYpID0+IHRoaXMubG9hZGluZ01lc3NhZ2Uuc2V0KHYpKTtcbiAgICB0aGlzLm1pcnJvcih0aGlzLnN0YXRlLmdldENhY2hlUHJvZ3Jlc3MkKCksICh2KSA9PiB0aGlzLmNhY2hlUHJvZ3Jlc3Muc2V0KHYpKTtcbiAgICB0aGlzLm1pcnJvcih0aGlzLnN0YXRlLmlzWm9vbSQoKSwgKHYpID0+IHRoaXMuem9vbS5zZXQodikpO1xuICAgIHRoaXMubWlycm9yKHRoaXMuc3RhdGUuZ2V0RmlsZW5hbWUkKCksICh2KSA9PiB7XG4gICAgICBpZiAodikgdGhpcy5maWxlTmFtZSA9IHY7XG4gICAgfSk7XG4gICAgdGhpcy5yZWdpb25BY3Rpb25zLmJpbmQodGhpcy5kZXN0cm95ZWQkLCAoKSA9PiB0aGlzLmNoYW5nZWQoKSk7XG4gICAgdGhpcy5zZWdtZW50YXRpb24uYmluZFBvaW50VG9vbCh0aGlzLnNhbVBvaW50VG9vbCwgdGhpcy5kZXN0cm95ZWQkKTtcbiAgICB0aGlzLm1pcnJvcih0aGlzLnN0YXRlLmdldFBhbmVsV2lkdGgkKCksICgpID0+IHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVsYXlvdXQoKTtcbiAgICAgIC8vIFRoZSBpbnRlbnNpdHkgaW5zZXQgaXMgYSBzZXBhcmF0ZSBjaGFydCBpbiBhIGZsb2F0aW5nIHBhbmVsOyByZWZsb3cgaXQgdG9vLlxuICAgICAgdGhpcy5pbnNldD8ucmVmbG93KCk7XG4gICAgfSk7XG4gICAgdGhpcy5zdGF0ZVxuICAgICAgLmdldEltYWdlSW5mbyQoKVxuICAgICAgLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgbmV4dDogKGltZ0luZm8pID0+IHtcbiAgICAgICAgICBpZiAoIWltZ0luZm8pIHtcbiAgICAgICAgICAgIHRoaXMub25JbWFnZUNsZWFyZWQoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gVGhlIHNlbGVjdG9yLCB0aGUgc3RhY2sgY29udHJvbHMgYW5kIHRoZSBpbnNldCBhbGwgZm9sbG93IHRoZSBpbWFnZS5cbiAgICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgICAgICBpZiAoaW1nSW5mbykge1xuICAgICAgICAgICAgdGhpcy5pbWFnZUluZm8gPSBpbWdJbmZvO1xuICAgICAgICAgICAgLy8gU3RhY2stb25seSBwbG90IHR5cGVzIChpc29zdXJmYWNlLCBzY2F0dGVyM2QpIGRlcGVuZCBvbiB3aGV0aGVyIHRoaXNcbiAgICAgICAgICAgIC8vIGZpbGUgaXMgYSBzdGFjayDigJQgcmVjb21wdXRlIHRoZSBzZWxlY3RvciBvcHRpb25zLlxuICAgICAgICAgICAgdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCk7XG4gICAgICAgICAgICAvLyBJZiB0aGUgYWN0aXZlIHBsb3QgdHlwZSBpc24ndCB2YWxpZCBmb3IgdGhpcyBpbWFnZSAoZS5nLiBhIHNjYWxhclxuICAgICAgICAgICAgLy8gdHlwZSBsaWtlIENvbnRvdXIgY2FycmllZCBvdmVyIHRvIGFuIFJHQiBpbWFnZSksIGZhbGwgYmFjayB0byBJbWFnZS5cbiAgICAgICAgICAgIHRoaXMucmVjb25jaWxlU2VsZWN0ZWRQbG90VHlwZSgpO1xuICAgICAgICAgICAgLy8gUmVhZCBmcm9tIGBpbWdJbmZvYCwgdGhlIHZhbHVlIHRoaXMgZW1pc3Npb24gY2FycmllZCwgcmF0aGVyIHRoYW4gZnJvbSB0aGVcbiAgICAgICAgICAgIC8vIGZpZWxkLiBIYW5kbGluZyB0aGUgZW1wdHkgZW1pc3Npb24gbWVhbnMgdGhlIGZpZWxkIENBTiBiZSBudWxsZWQgcGFydC13YXlcbiAgICAgICAgICAgIC8vIHRocm91Z2ggdGhpcyBicmFuY2g6IGByZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlYCBtYXkgY2FsbCBgc2V0UGxvdFR5cGVgLFxuICAgICAgICAgICAgLy8gd2hpY2ggY2FuIG1ha2UgdGhlIGhvc3QgcHVibGlzaCBhIG5ldyBpbWFnZSBzdGF0ZSwgcmUtZW50ZXJpbmcgdGhpcyB2ZXJ5XG4gICAgICAgICAgICAvLyBzdWJzY3JpcHRpb24uIFJlYWRpbmcgdGhlIGZpZWxkIHRoZW4gdGhyZXcgb24gYGlzR3JheXNjYWxlYCBhbmQgYWJvcnRlZCB0aGVcbiAgICAgICAgICAgIC8vIGhhbmRsZXIsIHNvIHRoZSBvYnNlcnZhdGlvbnMgd2VyZSBuZXZlciBkcmF3bi5cbiAgICAgICAgICAgIHRoaXMuaXNHcmF5c2NhbGVFdmVudC5lbWl0KGltZ0luZm8uaXNHcmF5c2NhbGUpO1xuICAgICAgICAgICAgdGhpcy5pc1N0YWNrRXZlbnQuZW1pdChpbWdJbmZvLmlzU3RhY2spO1xuICAgICAgICAgICAgLy8gUmVzZXQgdG8gdGhlIGRlZmF1bHQgMkQgSW1hZ2UgdmlldyB3aGVuIGEgZGlmZmVyZW50IGltYWdlIGlzXG4gICAgICAgICAgICAvLyBzZWxlY3RlZCB3aGlsZSBhIDNEIHR5cGUgaXMgYWN0aXZlLlxuICAgICAgICAgICAgaWYgKCF0aGlzLmlzSGVhdG1hcCAmJiBpbWdJbmZvLmZpbGVOYW1lICE9PSB0aGlzLmxvYWRlZEZpbGVOYW1lKSB7XG4gICAgICAgICAgICAgIHRoaXMuaXNIZWF0bWFwID0gdHJ1ZTtcbiAgICAgICAgICAgICAgdGhpcy5wbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuICAgICAgICAgICAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCA9IFBsb3RUeXBlLklNQUdFO1xuICAgICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFBsb3RUeXBlKHRoaXMucGxvdFR5cGUpO1xuICAgICAgICAgICAgICB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUgPSAndHVybnRhYmxlJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMubG9hZGVkRmlsZU5hbWUgPSBpbWdJbmZvLmZpbGVOYW1lO1xuICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRJbWFnZU1ldGEoaW1nSW5mby5pbWFnZU1ldGEsIGltZ0luZm8uZmlsZU5hbWUpO1xuICAgICAgICAgICAgLy8gQSBuZXdlciBpbWFnZSBhbHdheXMgcHJlZW1wdHMgYW4gaW4tZmxpZ2h0IHJlbmRlciAoc2VlIEltYWdlUmVuZGVyU2Vzc2lvbikuXG4gICAgICAgICAgICBpZiAoaW1nSW5mby51cmxzKSB0aGlzLnJlbmRlci5yZW5kZXIoaW1nSW5mbyk7XG4gICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBlcnJvcjogKGVycjogdW5rbm93bikgPT4ge1xuICAgICAgICAgIGNvbnN0IG1zZyA9IGVycm9yTWVzc2FnZShlcnIpO1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIG9jY3VyZWQgd2hlbiBnZXR0aW5nIGltYWdlIGluZm8nLCBlcnIpO1xuICAgICAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAgICAgIGtleTogdGhpcy52aXpBbGVydFRvYXN0S2V5LFxuICAgICAgICAgICAgc3RpY2t5OiB0cnVlLFxuICAgICAgICAgICAgc2V2ZXJpdHk6ICdlcnJvcicsXG4gICAgICAgICAgICBzdW1tYXJ5OiAnQW4gZXJyb3Igb2NjdXJlZCcsXG4gICAgICAgICAgICBkZXRhaWw6IGBUaGUgZm9sbG93aW5nIGVycm9yIG9jY3VyZWQgd2hpbGUgZ2V0dGluZyBpbWFnZSBpbmZvOiAke21zZ30uXG4gICAgICAgICAgICAgICAgICAgUGxlYXNlIHRyeSB0byBvcGVuIHRoZSBpbWFnZSBhZ2FpbiB0aHJvdWdoIHRoZSBmaWxlIG5hdmlnYXRvci5gLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHRoaXMuc3RhY2tMb2FkaW5nLnNldChmYWxzZSk7XG4gICAgICAgICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgICAgICAgIHRoaXMucmVuZGVyLnJ1bm5pbmcgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlc2V0KCk7XG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGb2xsb3cgYHNvdXJjZWAgdW50aWwgZGVzdHJveS4gSG9zdCBzdHJlYW1zIGNhbiBlbWl0IG91dHNpZGUgdGhlIEFuZ3VsYXIgem9uZSAoYVxuICAgKiBzZXJ2ZXJsZXNzIGhvc3QgY2xlYXJzIGl0cyBsb2FkaW5nIGZsYWcgZnJvbSBhbiBpbWFnZSBkZWNvZGUgb3IgYW4gT3BlblNlYWRyYWdvblxuICAgKiBjYWxsYmFjayk7IHNldHRpbmcgYSBzaWduYWwgdGhlcmUgbWFya3MgdGhpcyBPblB1c2ggdmlldyBidXQgc2NoZWR1bGVzIG5vIGNoYW5nZVxuICAgKiBkZXRlY3Rpb24sIHNvIHRoZSB1cGRhdGUgaXMgYXBwbGllZCBpbnNpZGUgdGhlIHpvbmUsIGFzIHtAbGluayBjaGFuZ2VkfSBkb2VzLlxuICAgKi9cbiAgcHJpdmF0ZSBtaXJyb3I8VD4oc291cmNlOiBPYnNlcnZhYmxlPFQ+LCBhcHBseTogKHZhbHVlOiBUKSA9PiB2b2lkKTogdm9pZCB7XG4gICAgc291cmNlLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuZGVzdHJveVJlZikpLnN1YnNjcmliZSgodmFsdWUpID0+IHtcbiAgICAgIGlmIChOZ1pvbmUuaXNJbkFuZ3VsYXJab25lKCkpIGFwcGx5KHZhbHVlKTtcbiAgICAgIGVsc2UgdGhpcy5uZ1pvbmUucnVuKCgpID0+IGFwcGx5KHZhbHVlKSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogU3RhdGUgdGhpcyB2aWV3IHNob3dzIGNoYW5nZWQgb3V0c2lkZSBvbmUgb2YgaXRzIHRlbXBsYXRlIGV2ZW50cyAoYSBzdHJlYW0sIGFcbiAgICogcmVzb2x2ZWQgcHJvbWlzZSwgYSBob3N0IGNhbGwpOiBtYXJrIHRoZSBPblB1c2ggdmlldyBmb3IgY2hlY2ssIHJlLWVudGVyaW5nIHRoZVxuICAgKiB6b25lIHdoZW4gaXQgY2hhbmdlZCBvdXRzaWRlIGl0IHNvIGEgY2hhbmdlLWRldGVjdGlvbiBwYXNzIGZvbGxvd3MuXG4gICAqL1xuICBwcml2YXRlIGNoYW5nZWQoKTogdm9pZCB7XG4gICAgaWYgKE5nWm9uZS5pc0luQW5ndWxhclpvbmUoKSkgdGhpcy5jZHIubWFya0ZvckNoZWNrKCk7XG4gICAgZWxzZSB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy5jZHIubWFya0ZvckNoZWNrKCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIExheSBhIG5ldyBpbWFnZSBvdXQgb25jZSB0aGUgb2xkIHZpZXcgaXMgZ29uZTogdGhlIHN0YWNrIGZsYWcsIHRoZSBzbGljZSBib3VuZHMgKG9uZVxuICAgKiB1cmwgcGVyIHNsaWNlLCBzbyB0aGUgbGFzdCBpbmRleCBpcyBsZW5ndGgtMSksIGFuZCB0aGUgb25lLXNob3QgYGluaXRpYWxaSW5kZXhgIGhpbnRcbiAgICogKGUuZy4gdGhlIGZpbGUgdGhlIHVzZXIgY2xpY2tlZCB3aXRoaW4gYSBudW1iZXJlZCBzZXJpZXMpLiBUaGUgaGludCBpcyBjb25zdW1lZCBzb1xuICAgKiByZS1kZWxpdmVyaW5nIHRoZSBzYW1lIGluZm8gKGEgcGxvdC10eXBlIHN3aXRjaCwgcmVsb2FkQW5kUGxvdCkgZG9lcyBub3QgcmVzZXQgdGhlXG4gICAqIHVzZXIncyBzY3J1YjsgdGhlIGhvc3QncyBvYmplY3QgaXMgbmV2ZXIgd3JpdHRlbiB0byAoYSBob3N0IHN0b3JlIG1heSBmcmVlemUgaXQpIOKAlFxuICAgKiB0aGUgY29tcG9uZW50IGtlZXBzIGEgY29weSB3aXRob3V0IHRoZSBoaW50LCBhbmQgcmVtZW1iZXJzIHRoZSBob3N0IG9iamVjdC5cbiAgICovXG4gIHByaXZhdGUgbGF5T3V0U2xpY2VzKGluZm86IElJbWFnZUluZm8pOiB2b2lkIHtcbiAgICB0aGlzLnN0YWNrTG9hZGluZy5zZXQoaW5mby5pc1N0YWNrICYmIGluZm8uc2hvd1N0YWNrKTtcbiAgICB0aGlzLm1heEluZGV4ID0gaW5mby51cmxzLmxlbmd0aCA+IDEgPyBpbmZvLnVybHMubGVuZ3RoIC0gMSA6IDA7XG4gICAgaWYgKGluZm8uaW5pdGlhbFpJbmRleCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBpZiAoIXRoaXMuY29uc3VtZWRTbGljZUhpbnRzLmhhcyhpbmZvKSkge1xuICAgICAgICB0aGlzLmNvbnN1bWVkU2xpY2VIaW50cy5hZGQoaW5mbyk7XG4gICAgICAgIHRoaXMuekluZGV4ID0gaW5mby5pbml0aWFsWkluZGV4O1xuICAgICAgfVxuICAgICAgdGhpcy5pbWFnZUluZm8gPSB7IC4uLmluZm8sIGluaXRpYWxaSW5kZXg6IHVuZGVmaW5lZCB9O1xuICAgIH1cbiAgICB0aGlzLnVwZGF0ZVpJbmRleCgpO1xuICB9XG5cbiAgbmdBZnRlclZpZXdJbml0KCkge1xuICAgIC8vIFdpbmRvdyBsaXN0ZW5lcnMgYXJlIHJlZ2lzdGVyZWQgT1VUU0lERSB0aGUgQW5ndWxhciB6b25lOiB6b25lLXBhdGNoZWQsIGV2ZXJ5XG4gICAgLy8gbW91c2Vtb3ZlIGFueXdoZXJlIGluIHRoZSBob3N0IGFwcCByYW4gYXBwLXdpZGUgY2hhbmdlIGRldGVjdGlvbi4gRWFjaCBoYW5kbGVyXG4gICAgLy8gcmUtZW50ZXJzIHRoZSB6b25lIChuZ1pvbmUucnVuKSBvbmx5IHdoZW4gaXQgYWN0dWFsbHkgY2hhbmdlcyB2aWV3IHN0YXRlLlxuICAgIHRoaXMuc2hvcnRjdXRzID0gbmV3IFZpZXdlclNob3J0Y3V0cyh0aGlzLnNob3J0Y3V0SG9zdCgpLCB0aGlzLm5nWm9uZSk7XG4gICAgdGhpcy5zaG9ydGN1dHMuYXR0YWNoKCk7XG4gICAgdGhpcy5vblZpZXdSZWFkeSgpO1xuICB9XG5cbiAgLyoqIFRoZSBwbG90IGRpdiBleGlzdHMgbm93OiBydW4gYW4gaW1hZ2UtbGVzcyBkcmF3IHRoYXQgYXJyaXZlZCBiZWZvcmUgaXQgZGlkLiAqL1xuICBwcml2YXRlIG9uVmlld1JlYWR5KCk6IHZvaWQge1xuICAgIHRoaXMucmVuZGVyLnZpZXdJc1JlYWR5KHRoaXMuc3BhdGlhbC5kYXRhc2V0LCAhIXRoaXMuaW1hZ2VJbmZvKTtcbiAgfVxuXG4gIC8qKiBXaGF0IHRoZSBrZXlib2FyZCwgd2hlZWwgYW5kIGNvbnRleHQtbWVudSBzaG9ydGN1dHMgYWN0IG9uLiBUaGVpciBsaXN0ZW5lcnMgbGl2ZVxuICAgKiAgb3V0c2lkZSB0aGlzIHZpZXcsIHNvIGV2ZXJ5IGFjdGlvbiB0aGF0IGNoYW5nZXMgd2hhdCBpdCBzaG93cyBtYXJrcyBpdCBmb3IgY2hlY2suICovXG4gIHByaXZhdGUgc2hvcnRjdXRIb3N0KCk6IFNob3J0Y3V0SG9zdCB7XG4gICAgY29uc3QgbWFya2VkID1cbiAgICAgIDxBIGV4dGVuZHMgdW5rbm93bltdPihhY3Q6ICguLi5hcmdzOiBBKSA9PiB2b2lkKSA9PlxuICAgICAgKC4uLmFyZ3M6IEEpOiB2b2lkID0+IHtcbiAgICAgICAgYWN0KC4uLmFyZ3MpO1xuICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgIH07XG4gICAgcmV0dXJuIHtcbiAgICAgIHBsb3REaXZOYW1lOiB0aGlzLnBsb3REaXZOYW1lLFxuICAgICAgaG9zdEVsZW1lbnQ6IHRoaXMuaG9zdFJlZj8ubmF0aXZlRWxlbWVudCA/PyBudWxsLFxuICAgICAgYWN0aXZlRHJhZ01vZGU6ICgpID0+IHRoaXMuYWN0aXZlRHJhZ01vZGUsXG4gICAgICBjYW5TdGVwU2xpY2U6ICgpID0+ICEhdGhpcy5pbWFnZUluZm8/LmlzU3RhY2sgJiYgdGhpcy5pc0ltYWdlVmlldyxcbiAgICAgIHdoZWVsWm9vbXM6ICgpID0+ICFyZW5kZXJlck93bnNXaGVlbCh0aGlzLmJhc2VQbG90VHlwZSkgJiYgdGhpcy5pc0hlYXRtYXAsXG4gICAgICBzdGVwU2xpY2U6IG1hcmtlZCgoZGVsdGE6IG51bWJlcikgPT4gdGhpcy5zdGVwU2xpY2UoZGVsdGEpKSxcbiAgICAgIHJlc29sdmVTYW1Qcm9tcHQ6IG1hcmtlZCgoY29tbWl0OiBib29sZWFuKSA9PiB7XG4gICAgICAgIGlmIChjb21taXQpIHRoaXMucGxvdFNlcnZpY2UuY29tbWl0U2FtUG9pbnRzKCk7XG4gICAgICAgIGVsc2UgdGhpcy5wbG90U2VydmljZS5jbGVhclNhbVBvaW50cygpO1xuICAgICAgICB0aGlzLnNlZ21lbnRhdGlvbi5oaWRlKCk7IC8vIHByb21wdCByZXNvbHZlZCDihpIgZGlzbWlzcyB0aGUgc3RhdHVzIHRvYXN0XG4gICAgICB9KSxcbiAgICAgIHVuZG86IG1hcmtlZCgoKSA9PiB0aGlzLnVuZG9SZWdpb24oKSksXG4gICAgICByZWRvOiBtYXJrZWQoKCkgPT4gdGhpcy5yZWRvUmVnaW9uKCkpLFxuICAgICAgZGVsZXRlUmVnaW9uOiBtYXJrZWQoKCkgPT4gdGhpcy5kZWxldGVSZWdpb24oKSksXG4gICAgICB6b29tSW46IG1hcmtlZCgoKSA9PiB0aGlzLnpvb21JbigpKSxcbiAgICAgIHpvb21PdXQ6IG1hcmtlZCgoKSA9PiB0aGlzLnpvb21PdXQoKSksXG4gICAgICB0b2dnbGVEcmFnTW9kZTogbWFya2VkKChtb2RlOiBzdHJpbmcpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUobW9kZSkpLFxuICAgICAgb3BlbkNvbnRleHRNZW51OiAoZXZlbnQpID0+IHtcbiAgICAgICAgdGhpcy5jb250ZXh0TWVudUl0ZW1zID0gdGhpcy5idWlsZENvbnRleHRNZW51SXRlbXMoKTtcbiAgICAgICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgICB0aGlzLmNvbnRleHRNZW51LnNob3coZXZlbnQpO1xuICAgICAgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFNuYXAgdGhlIGZsb2F0aW5nIHRvb2xiYXIgYmFjayB0byBpdHMgZG9ja2VkIHBvc2l0aW9uIGFjcm9zcyB0aGUgdG9wLiAqL1xuICBwcm90ZWN0ZWQgZG9ja1Rvb2xiYXIoKSB7XG4gICAgdGhpcy50b29sYmFyRmxvYXRpbmcgPSBmYWxzZTtcbiAgfVxuXG4gIC8qKiBUb29sYmFyIFwiSW50ZW5zaXR5XCIgZ3JvdXA6IGFkZCBhbm90aGVyIGxpbmUgUk9JIChuZXh0IGJyaWdodCBjb2xvdXIpLiAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgYWRkUHJvZmlsZUxpbmUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcmVnaW9uID0gYXdhaXQgdGhpcy5pbnNldD8uYWRkUHJvZmlsZUxpbmUoKTtcbiAgICAvLyBPU0Qgb25seSBkcmF3cyBhIHNlbGVjdGVkIHJlZ2lvbidzIGhhbmRsZXMgaW4gYW4gZWRpdCBtb2RlLCBzbyBhIGxpbmUgaW5cbiAgICAvLyAnbm9uZScgbW9kZSBsb29rcyB1bnNlbGVjdGVkLiBTd2l0Y2ggdG8gJ3NlbGVjdCcgc28gdGhlIG5ldyBsaW5lIHNob3dzIGl0c1xuICAgIC8vIGVuZHBvaW50IGhhbmRsZXMgYW5kIGNhbiBiZSBkcmFnZ2VkL2RlbGV0ZWQgcmlnaHQgYXdheS5cbiAgICBpZiAocmVnaW9uICYmIHRoaXMuaXNJbWFnZVZpZXcgJiYgdGhpcy5hY3RpdmVEcmFnTW9kZSAhPT0gJ3NlbGVjdCcpIHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3NlbGVjdCcpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgLy8gQSBjb250cmlidXRlZCBtb2RlJ3Mgc2Vzc2lvbiBlbmRzIGZpcnN0LCB3aGlsZSB0aGUgdmlld2VyIGl0IGRyZXcgb3ZlclxuICAgIC8vIHN0aWxsIGV4aXN0cy4gVGhlIHBhbmVsIGdvZXMgd2l0aCB0aGlzIHZpZXcsIHNvIG5vIGNoYW5nZSBkZXRlY3Rpb24uXG4gICAgdGhpcy5jb250cmlidXRpb25zLmRlc3Ryb3koKTtcbiAgICAvLyBMZWF2ZSB0aGUgbGl2ZSBzZXQgc28gdGhlIG5leHQtb2xkZXN0IHZpc3VhbGl6ZXIgcGlja3MgdXAgdGhlIG91dGxldHMg4oCUIG9uIGl0c1xuICAgIC8vIG5leHQgY2hlY2ssIHdoaWNoIGFuIE9uUHVzaCBzaWJsaW5nIG5lZWRzIGFza2luZyBmb3IuXG4gICAgVmlzdWFsaXplckNvbXBvbmVudC5saXZlSW5zdGFuY2VzLmRlbGV0ZSh0aGlzKTtcbiAgICBmb3IgKGNvbnN0IG90aGVyIG9mIFZpc3VhbGl6ZXJDb21wb25lbnQubGl2ZUluc3RhbmNlcykgb3RoZXIuY2hhbmdlZCgpO1xuICAgIHRoaXMuc3RhdGUuc2V0RGlhZ3JhbShudWxsKTtcbiAgICB0aGlzLnJlbmRlci5jYW5jZWwoKTtcbiAgICB0aGlzLnNwYXRpYWwuZGlzcG9zZSgpO1xuICAgIHRoaXMuc2NydWJiZXIuY2FuY2VsKCk7XG4gICAgdGhpcy5zaG9ydGN1dHM/LmRldGFjaCgpO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZGV0YWNoKCk7XG4gIH1cblxuICAvKiogV2hhdCB0aGUgaG9zdCBnZXRzIHRocm91Z2ggYHNldERpYWdyYW1gIOKAlCBzZWUge0BsaW5rIFZpc3VhbGl6ZXJIYW5kbGV9LiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGhvc3RIYW5kbGU6IFZpc3VhbGl6ZXJIYW5kbGUgPSB7XG4gICAgdmlzdWFsaXplcjogdGhpcy5wbG90U2VydmljZSxcbiAgICBwbG90U2VydmljZTogdGhpcy5wbG90U2VydmljZSxcbiAgICBoYXNSZWdpb25zOiAoKSA9PiB0aGlzLmhhc1JlZ2lvbnMoKSxcbiAgICBnZXRSZWdpb25Qb2x5Z29uczogKCkgPT4gdGhpcy5nZXRSZWdpb25Qb2x5Z29ucygpLFxuICB9O1xuXG4gIHB1YmxpYyBoYXNSZWdpb25zKCk6IGJvb2xlYW4ge1xuICAgIC8vIFRoZSBjb250cmFjdCdzIGZyYW1ld29yay1uZXV0cmFsIHJlZ2lvbiBhY2Nlc3NvcjogdGhlIGNvbXBvbmVudCBvbmx5IG5lZWRzIHRvXG4gICAgLy8ga25vdyB3aGV0aGVyIGFueSByZWdpb24gZXhpc3RzLCBuZXZlciBhIGJhY2tlbmQncyB3aXJlIGZvcm1hdC5cbiAgICByZXR1cm4gdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25zKCkubGVuZ3RoID4gMDtcbiAgfVxuXG4gIC8qKiBUaGUgY3VycmVudCByZWdpb25zIGFzIHBvbHlnb25zIChhbHNvIG9uIHRoZSBob3N0IGhhbmRsZSkuICovXG4gIHB1YmxpYyBnZXRSZWdpb25Qb2x5Z29ucygpOiBQb2x5Z29uW10ge1xuICAgIHJldHVybiB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvblBvbHlnb25zKCk7XG4gIH1cblxuICAvKiogT3BlbiB0aGUgQ2hhbm5lbHMgJiBIaXN0b2dyYW0gZGlhbG9nICh0b29sYmFyIGJ1dHRvbikuICovXG4gIHByb3RlY3RlZCBvcGVuQ2hhbm5lbEhpc3RvZ3JhbSgpIHtcbiAgICB0aGlzLnNob3dDaGFubmVsSGlzdG9ncmFtID0gdHJ1ZTtcbiAgfVxuXG4gIC8qKiBUb29sYmFyIOKGkiBvcGVuIHRoZSBzcGF0aWFsLW9taWNzIGNvbnRyb2xzLiAqL1xuICBwcm90ZWN0ZWQgb3BlblNwYXRpYWxDb250cm9scygpOiB2b2lkIHtcbiAgICB0aGlzLnNob3dTcGF0aWFsQ29udHJvbHMgPSB0cnVlO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIFJlZ2lvbiBFZGl0b3IgZGlhbG9nICh0b29sYmFyIGJ1dHRvbikuIFRoZSBlZGl0b3Igc3RheXMgbGlua2VkIHRvXG4gICAqICB0aGlzIHBsb3R0aW5nIGluc3RhbmNlIHRocm91Z2ggdGhlIHJvb3QgUmVnaW9uU3RvcmUgc2luZ2xldG9uLCBzbyBpdCB3b3Jrc1xuICAgKiAgdGhlIHNhbWUgYXMgdGhlIGZvcm1lciByaWdodC1wYW5lbCBSZWdpb25zIHRhYi4gKi9cbiAgcHJvdGVjdGVkIG9wZW5SZWdpb25FZGl0b3IoKSB7XG4gICAgLy8gU2l6ZSB0aGUgZGlhbG9nIHRvIG1hdGNoIHRoZSBob3N0J3MgY29uZmlndXJlZCBlbGVtZW50IChlLmcuIHRoZSBhcHAnc1xuICAgIC8vIHJpZ2h0IHBhbmVsKSBhdCBvcGVuIHRpbWUuIE1lYXN1cmVkIG9uY2Ug4oCUIGl0IGludGVudGlvbmFsbHkgZG9lcyBub3RcbiAgICAvLyB0cmFjayBsYXRlciBicm93c2VyL3NwbGl0IHJlc2l6ZXMuIEZhbGxzIGJhY2sgdG8gYSBxdWFydGVyIG9mIHRoZSBwYWdlXG4gICAgLy8gd2hlbiBubyBzZWxlY3RvciBpcyBjb25maWd1cmVkIG9yIHRoZSBlbGVtZW50IGlzbid0IHByZXNlbnQuXG4gICAgY29uc3Qgc2VsZWN0b3IgPSB0aGlzLnZpekNvbmZpZz8ucmVnaW9uRWRpdG9yV2lkdGhTZWxlY3RvcjtcbiAgICBjb25zdCB3aWR0aCA9IHNlbGVjdG9yXG4gICAgICA/IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3I8SFRNTEVsZW1lbnQ+KHNlbGVjdG9yKT8uZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkud2lkdGhcbiAgICAgIDogdW5kZWZpbmVkO1xuICAgIHRoaXMucmVnaW9uRWRpdG9yV2lkdGggPSB3aWR0aCAmJiB3aWR0aCA+IDAgPyBgJHtNYXRoLnJvdW5kKHdpZHRoKX1weGAgOiAnMjV2dyc7XG4gICAgdGhpcy5zaG93UmVnaW9uRWRpdG9yID0gdHJ1ZTtcbiAgfVxuXG4gIC8qKiBUaGUgdG9vbGJhcidzIFNpbmdsZSBpbWFnZSAvIFN0YWNrIHRvZ2dsZS4gKi9cbiAgcHJvdGVjdGVkIHNlbGVjdFN0YWNrT3B0aW9uKHNlbGVjdGVkU3RhY2tPcHRpb246IHsgbmFtZTogc3RyaW5nOyB2YWw6IHN0cmluZyB9KSB7XG4gICAgY29uc3Qgc2hvd3N0YWNrID0gc2VsZWN0ZWRTdGFja09wdGlvbi52YWwgPT09ICd0cnVlJztcbiAgICB0aGlzLnN0YWNrTG9hZGluZy5zZXQoc2hvd3N0YWNrKTtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyghc2hvd3N0YWNrKTtcbiAgICAvLyBTdGFjayBtb2RlIGlzIGFsd2F5cyBhIDJEIGhlYXRtYXAg4oCUIHJlc2V0IHN1cmZhY2UgbW9kZSBpZiBhY3RpdmVcbiAgICBpZiAoIXRoaXMuaXNIZWF0bWFwKSB7XG4gICAgICB0aGlzLmlzSGVhdG1hcCA9IHRydWU7XG4gICAgICB0aGlzLnBsb3RUeXBlID0gUGxvdFR5cGUuSEVBVE1BUDtcbiAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gUGxvdFR5cGUuSEVBVE1BUDtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UGxvdFR5cGUodGhpcy5wbG90VHlwZSk7XG4gICAgfVxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U2hvd1N0YWNrKHNob3dzdGFjayk7XG4gIH1cblxuICAvKiogQ2xhbXAgdGhlIHNsaWNlIGluZGV4IGludG8gWzAsIG1heEluZGV4XSBhbmQgcHVzaCBpdCB0byB0aGUgcmVuZGVyZXIuICovXG4gIHByb3RlY3RlZCB1cGRhdGVaSW5kZXgoKSB7XG4gICAgaWYgKHRoaXMuekluZGV4ID4gdGhpcy5tYXhJbmRleCkge1xuICAgICAgdGhpcy56SW5kZXggPSB0aGlzLm1heEluZGV4O1xuICAgIH1cbiAgICBpZiAodGhpcy56SW5kZXggPCAwKSB7XG4gICAgICB0aGlzLnpJbmRleCA9IDA7XG4gICAgfVxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0WkluZGV4KHRoaXMuekluZGV4KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMaXZlIHotc2xpY2Ugc2NydWIgZm9yIHRoZSBJbWFnZSB2aWV3OiB0aGUgcmVuZGVyZXIgc3dhcHMgdGhlIGRpc3BsYXllZFxuICAgKiBzbGljZSBpbiBwbGFjZSAobm8gcmUtbW91bnQpLCBzbyBkcmFnZ2luZyB0aGUgc2xpZGVyIHN0ZXBzIHRocm91Z2ggdGhlXG4gICAqIHN0YWNrIHRoZSBzYW1lIHdheSB0aGUgaGVhdG1hcCBmcmFtZSBzbGlkZXIgZG9lcy5cbiAgICovXG4gIHByb3RlY3RlZCBvblpTbGlkZSh6OiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoeiA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy56SW5kZXggPSB6O1xuICAgIHRoaXMuc2NydWJiZXIuY29tbWl0KHopO1xuICAgIC8vIEtlZXAgdGhlIGludGVuc2l0eSBpbnNldCBzYW1wbGluZyB0aGUgZGlzcGxheWVkIHNsaWNlIChJbWFnZSB2aWV3KS5cbiAgICB0aGlzLmluc2V0Py5zbGljZUNvbW1pdHRlZCh6KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMaXZlIChkZWJvdW5jZWQpIHNjcnViIHdoaWxlIGRyYWdnaW5nIHRoZSBJbWFnZS12aWV3IHotc2xpZGVyOiB1cGRhdGUgdGhlXG4gICAqIHNsaWNlIGFzIHRoZSB1c2VyIGRyYWdzLCBidXQgY29hbGVzY2UgcmFwaWQgY2hhbmdlcyBzbyB3ZSBkb24ndCBmaXJlIGFcbiAgICogc2xpY2Ugc3dhcCBvbiBldmVyeSBwaXhlbC4gVGhlIGZpbmFsIHZhbHVlIGFsc28gbGFuZHMgdmlhIG9uWlNsaWRlIChvblNsaWRlRW5kKS5cbiAgICovXG4gIHByb3RlY3RlZCBvblpTY3J1Yih6OiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoeiA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy56SW5kZXggPSB6O1xuICAgIHRoaXMuc2NydWJiZXIuc2NydWIoeik7XG4gIH1cblxuICAvKiogTW92ZSB0aGUgZGlzcGxheWVkIHNsaWNlIGJ5IGBkZWx0YWAsIGNsYW1wZWQgdG8gdGhlIHN0YWNrIGJvdW5kcy4gKi9cbiAgc3RlcFNsaWNlKGRlbHRhOiBudW1iZXIpOiB2b2lkIHtcbiAgICBjb25zdCBuZXh0ID0gTWF0aC5taW4odGhpcy5tYXhJbmRleCwgTWF0aC5tYXgoMCwgdGhpcy56SW5kZXggKyBkZWx0YSkpO1xuICAgIGlmIChuZXh0ID09PSB0aGlzLnpJbmRleCkgcmV0dXJuO1xuICAgIHRoaXMub25aU2xpZGUobmV4dCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVsb2FkQW5kUGxvdCgpIHtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyh0cnVlKTtcbiAgICAvLyBSZS1kcml2ZSB0aGUgcmVuZGVyIHBpcGVsaW5lIGZyb20gdGhlIGN1cnJlbnQgaW1hZ2UgaW5mbyAodGhlIHNvdXJjZSBvZlxuICAgIC8vIHRydXRoLCB3aXRoIHJlYWwgdXJscy9maWxlTmFtZSkuIERvbid0IHVzZSBwbG90U2VydmljZS5yZWxvYWRBbmRQbG90KCk6XG4gICAgLy8gdGhhdCByZWJ1aWxkcyB0aGUgaW5mbyBmcm9tIHRoZSByZW5kZXJlcidzIGludGVybmFsIHN0YXRlLCB3aGljaCBjYW4gYmVcbiAgICAvLyBzdGFsZSAoZS5nLiBhZnRlciBzd2l0Y2hpbmcgcGxvdCB0eXBlIEltYWdlIC0+IEhlYXRtYXApIGFuZCByZS1lbWl0XG4gICAgLy8gZW1wdHkvb2xkIHVybHMsIGZhaWxpbmcgdG8gaGFuZCB0aGUgZGl2IG92ZXIgdG8gdGhlIG5ldyByZW5kZXJlci5cbiAgICBpZiAodGhpcy5pbWFnZUluZm8pIHtcbiAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKHRoaXMuaW1hZ2VJbmZvKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuc3BhdGlhbC5kYXRhc2V0ICYmIChpc1NwYXRpYWxPbWljcyh0aGlzLnBsb3RUeXBlKSB8fCBpc1NwYXRpYWxPbWljczNkKHRoaXMucGxvdFR5cGUpKSkge1xuICAgICAgLy8gQSBzcGF0aWFsIGRhdGFzZXQgdGhhdCBicmluZ3Mgbm8gaW1hZ2UsIG9wZW5lZCB3aXRoIG5vIGltYWdlIGxvYWRlZCAoYSBob3N0J3NcbiAgICAgIC8vIGZpcnN0IHZpZXcpOiB0aGVyZSBpcyBubyBpbWFnZSBpbmZvIHRvIHJlLWRyaXZlLCBzbyBkcmF3IHRoZSBzcGF0aWFsIG1vZGUgaXRzZWxmLlxuICAgICAgdm9pZCB0aGlzLnJlbmRlci5wbG90V2l0aG91dEltYWdlKHRoaXMuc3BhdGlhbC5kYXRhc2V0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5wbG90U2VydmljZS5yZWxvYWRBbmRQbG90KCk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGNhbmNlbExvYWRpbmcoKSB7XG4gICAgLy8gU3RvcCBhbnkgaW4tZmxpZ2h0IGZyYW1lIHN0cmVhbWluZyAobmFwYXJpLWpzIHZvbHVtZS9zdXJmYWNlIHByZWxvYWQpIHNvIHRoZSBmZXRjaCBsb29wc1xuICAgIC8vIGFjdHVhbGx5IGFib3J0IOKAlCBjbGVhcmluZyB0aGUgZmxhZyBhbG9uZSBvbmx5IHJvdXRlZCB0byBQbG90bHkgYW5kIGxlZnQgbmFwYXJpIGZldGNoaW5nLlxuICAgIHRoaXMucGxvdFNlcnZpY2UuY2FuY2VsTG9hZGluZz8uKCk7XG4gICAgLy8gVGhlIGNhbmNlbGxlZCByZW5kZXIgaXMgc3VwZXJzZWRlZCwgbm90IGp1c3QgYWJvcnRlZDogaXRzIGFib3J0ZWQgbG9hZCBtdXN0IG5vdFxuICAgIC8vIGNvbWUgYmFjayBhcyBcIkNvdWxkIG5vdCBkcmF3IHRoZSBpbWFnZVwiLCBub3IgYXBwbHkgUk9JcyBmb3IgYW4gaW1hZ2UgbGVmdCB1bnNob3duLlxuICAgIHRoaXMucmVuZGVyLmNhbmNlbCgpO1xuICAgIHRoaXMuc3RhY2tMb2FkaW5nLnNldChmYWxzZSk7XG4gICAgdGhpcy5pbWdMb2FkaW5nLnNldChmYWxzZSk7XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgIHRoaXMuekluZGV4ID0gMDtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFpJbmRleCh0aGlzLnpJbmRleCk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRTdGFja0xvYWRpbmcoZmFsc2UpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRvd25sb2FkSW1hZ2UoKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5kb3dubG9hZEltYWdlKCk7XG4gIH1cblxuICBhdXRvc2NhbGVJbWFnZSgpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmZpdFRvVmlldygpO1xuICB9XG5cbiAgLyoqIFRvb2xiYXIgcGl4ZWwvc21vb3RoIHRvZ2dsZTogZmxpcCBzbW9vdGhpbmcgYW5kIGFwcGx5IHRvIHRoZSBhY3RpdmUgYmFja2VuZC4gKi9cbiAgcHJvdGVjdGVkIG9uVG9nZ2xlSW1hZ2VTbW9vdGhpbmcoKTogdm9pZCB7XG4gICAgdGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQgPSAhdGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQ7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRJbWFnZVNtb290aGluZ0VuYWJsZWQodGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQpO1xuICB9XG5cbiAgcmVzZXRBeGVzKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UucmVzZXRBeGVzKCk7XG4gIH1cblxuICB0b2dnbGVEcmFnTW9kZShtb2RlOiBzdHJpbmcpIHtcbiAgICB0aGlzLnRvb2xzLnRvZ2dsZShtb2RlKTtcbiAgfVxuXG4gIC8qKiBMaXZlLXVwZGF0ZSB0aGUgaXNvc3VyZmFjZSBhcyB0aGUgcmFuZ2Ugc2xpZGVyIG1vdmVzLiBUaGUgY29udHJvbCBpcyBvbmx5XG4gICAqICBhdmFpbGFibGUgd2hlbiB0aGUgYWN0aXZlIGJhY2tlbmQgcmVuZGVycyBpc29zdXJmYWNlcyAoSVNPU1VSRkFDRSBtb2RlKS4gKi9cbiAgcHJvdGVjdGVkIG9uSXNvUmFuZ2VDaGFuZ2UodmFsdWVzOiBudW1iZXJbXSB8IHVuZGVmaW5lZCkge1xuICAgIGlmICghdmFsdWVzIHx8IHZhbHVlcy5sZW5ndGggPCAyKSByZXR1cm47XG4gICAgdGhpcy5pc29SYW5nZSA9IHZhbHVlcztcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldElzb3N1cmZhY2VDb250cm9scygpPy5zZXRJc29SYW5nZSh2YWx1ZXNbMF0sIHZhbHVlc1sxXSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgb25XYW5kU2Vuc2l0aXZpdHlDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIHRoaXMudG9vbHMuc2V0V2FuZFNlbnNpdGl2aXR5KHZhbHVlKTtcbiAgfVxuICBwcm90ZWN0ZWQgb25CcnVzaFNpemVDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIHRoaXMudG9vbHMuc2V0QnJ1c2hTaXplKHZhbHVlKTtcbiAgfVxuICBwcm90ZWN0ZWQgb25WZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIHRoaXMudG9vbHMuc2V0VmVydGV4RXJhc2VyUmFkaXVzKHZhbHVlKTtcbiAgfVxuXG4gIHpvb21JbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnpvb21JbigpO1xuICB9XG5cbiAgem9vbU91dCgpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnpvb21PdXQoKTtcbiAgfVxuXG4gIGRlbGV0ZVJlZ2lvbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmRlbGV0ZUFjdGl2ZVNoYXBlKCk7XG4gIH1cblxuICAvKiogVW5kbyB0aGUgbW9zdCByZWNlbnQgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gVXAgdG8gMTAgc3RlcHMgYmFjay4gKi9cbiAgcHJvdGVjdGVkIHVuZG9SZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS51bmRvKCk7XG4gIH1cblxuICAvKiogUmVkbyB0aGUgbW9zdCByZWNlbnRseSB1bmRvbmUgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gKi9cbiAgcHJvdGVjdGVkIHJlZG9SZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5yZWRvKCk7XG4gIH1cblxuICAvLyDilIDilIAgUmVnaW9uIHNldC1vcGVyYXRpb25zIG9uIHRoZSBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzg1KSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgc2VsZWN0QWxsUmVnaW9ucygpOiB2b2lkIHtcbiAgICB0aGlzLnJlZ2lvbkFjdGlvbnMuc2VsZWN0QWxsKCk7XG4gIH1cbiAgbWVyZ2VSZWdpb25zKCk6IHZvaWQge1xuICAgIHRoaXMucmVnaW9uQWN0aW9ucy5tZXJnZSgpO1xuICB9XG4gIHVuZ3JvdXBSZWdpb25zKCk6IHZvaWQge1xuICAgIHRoaXMucmVnaW9uQWN0aW9ucy51bmdyb3VwKCk7XG4gIH1cbiAgaW52ZXJzZVJlZ2lvbnMoKTogdm9pZCB7XG4gICAgdGhpcy5yZWdpb25BY3Rpb25zLmludmVyc2UoKTtcbiAgfVxuICBzaW1wbGlmeVJlZ2lvbnModGhyZXNob2xkUHg6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMucmVnaW9uQWN0aW9ucy5zaW1wbGlmeSh0aHJlc2hvbGRQeCk7XG4gIH1cbiAgb3BlblNpbXBsaWZ5RGlhbG9nKCk6IHZvaWQge1xuICAgIHRoaXMucmVnaW9uQWN0aW9ucy5vcGVuU2ltcGxpZnlEaWFsb2coKTtcbiAgfVxuXG4gIC8qKiBCb3gtcHJvbXB0ZWQgU0FNIHNlZ21lbnRhdGlvbiBvZiB0aGUgZHJhd24gcmVjdGFuZ2xlcyAoaml0LXVpIzkwKS4gQSBzdGlja3lcbiAgICogIGBzYW1gIHRvYXN0IHNob3dzIGxpdmUgc3RhdHVzICsgYSBkb3dubG9hZCBwcm9ncmVzcyBiYXIgKGZpcnN0IHJ1biBwdWxscyB0aGVcbiAgICogIGVuY29kZXIsIH4xNzAgTUIpOyBpdCBzdGF5cyBvcGVuIHVudGlsIHRoZSBydW4gZmluaXNoZXMgKGJhciBoaXRzIDEwMCUpLiAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgc2VnbWVudFJlZ2lvbnMoKSB7XG4gICAgYXdhaXQgdGhpcy5zZWdtZW50YXRpb24ucnVuKCdTQU0nLCB0aGlzLnNhbVRvb2wsICgpID0+IHRoaXMucGxvdFNlcnZpY2Uuc2VnbWVudFJlY3RhbmdsZXMoKSk7XG4gIH1cblxuICAvKiogQXV0by1zZWdtZW50IGNlbGxzIGluc2lkZSBlYWNoIGRyYXduIHJlY3RhbmdsZSB3aXRoIGNlbGxwb3NlLVNBTSwgY2xpZW50LXNpZGVcbiAgICogIChqaXQtdWkjOTApLiBFYWNoIGJveCBpcyBjcm9wcGVkIChicm93c2VyIHNsaWRlLWNyb3ApIHRoZW4gcnVuIHRocm91Z2ggdGhlXG4gICAqICBjZWxscG9zZS1qcyBtb2RlbDsgdGhlIHNhbWUgc3RpY2t5IGBzYW1gIHRvYXN0ICsgcHJvZ3Jlc3MgYmFyIGlzIHJldXNlZC4gKi9cbiAgcHJvdGVjdGVkIGFzeW5jIHNlZ21lbnRDZWxscG9zZSgpIHtcbiAgICBhd2FpdCB0aGlzLnNlZ21lbnRhdGlvbi5ydW4oJ0NlbGxwb3NlJywgdGhpcy5jZWxsU2VnbWVudFRvb2wsICgpID0+XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNlZ21lbnRSZWN0YW5nbGVzQ2VsbHBvc2UoKSxcbiAgICApO1xuICB9XG5cbiAgLy8g4pSA4pSAIGNvbnRyaWJ1dGVkIHRvb2wgcGFyYW1ldGVycyAoc2VlIFRvb2xQYXJhbXNNb2RlbCkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIHBhcmFtc0Zvcih0b29sSWQ6IHN0cmluZyk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICByZXR1cm4gdGhpcy50b29sUGFyYW1zLnBhcmFtc0Zvcih0b29sSWQpO1xuICB9XG4gIHByb3RlY3RlZCBvblRvb2xNb2RlbENoYW5nZShlOiB7IHRvb2xJZDogc3RyaW5nOyBtb2RlbElkOiBzdHJpbmcgfSk6IHZvaWQge1xuICAgIHRoaXMudG9vbFBhcmFtcy5zZXRNb2RlbChlLnRvb2xJZCwgZS5tb2RlbElkKTtcbiAgfVxuICBwcm90ZWN0ZWQgb3BlblRvb2xQYXJhbXModG9vbElkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnRvb2xQYXJhbXMub3Blbih0b29sSWQpO1xuICB9XG4gIHByb3RlY3RlZCBjbG9zZVRvb2xQYXJhbXMoKTogdm9pZCB7XG4gICAgdGhpcy50b29sUGFyYW1zLmNsb3NlKCk7XG4gIH1cbiAgcHJvdGVjdGVkIHJlc2V0VG9vbFBhcmFtcyh0b29sSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMudG9vbFBhcmFtcy5yZXNldCh0b29sSWQpO1xuICB9XG5cbiAgLyoqIFJ1biBhIGNvbnRyaWJ1dGVkIHRvb2wgb3ZlciB0aGUgY3VycmVudCB2aWV3LiBObyBwcm9tcHQ6IHRoZXNlIHN3ZWVwIHRoZVxuICAgKiAgd2hvbGUgdmlldyByYXRoZXIgdGhhbiBiZWluZyBwb2ludGVkIGF0IHNvbWV0aGluZy4gUmV1c2VzIHRoZSBzaGFyZWRcbiAgICogIHNlZ21lbnRhdGlvbiB0b2FzdCBzbyBwcm9ncmVzcyByZWFkcyB0aGUgc2FtZSBhcyB0aGUgU0FNL2NlbGxwb3NlIHRvb2xzLiAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgcnVuVG9vbCh0b29sSWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHRvb2wgPSB0aGlzLnRvb2xQYXJhbXMuZmluZCh0b29sSWQpO1xuICAgIGlmICghdG9vbCkgcmV0dXJuO1xuICAgIGNvbnN0IHBhcmFtcyA9IHRoaXMudG9vbFBhcmFtcy5ydW5QYXJhbXModG9vbCk7XG4gICAgYXdhaXQgdGhpcy5zZWdtZW50YXRpb24ucnVuKHRvb2wubGFiZWwsIHRvb2wucHJvZ3Jlc3MsICgpID0+IHRvb2wucnVuKHRoaXMucGxvdFNlcnZpY2UsIHBhcmFtcykpO1xuICB9XG5cbiAgLyoqIFBpY2sgdGhlIFNBTSBtb2RlbCB0aGUgc2VnbWVudCB0b29scyB1c2UgKGppdC11aSM5MCBQMSkuICovXG4gIHByb3RlY3RlZCBvblNhbU1vZGVsQ2hhbmdlKGlkOiBzdHJpbmcpIHtcbiAgICB0aGlzLnNhbU1vZGVsSWQgPSBpZDtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNhbU1vZGVsKGlkKTtcbiAgfVxuXG4gIC8qKiBDb252ZXJ0IHRoZSBzZWxlY3RlZCByZWdpb24gdG8gYSBzbW9vdGggYmV6aWVyIGN1cnZlLiAqL1xuICB0b0JlemllclJlZ2lvbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbk92ZXJsYXkoKT8uc2V0U2VsZWN0ZWRCZXppZXIodHJ1ZSk7XG4gIH1cblxuICAvKiogQ29udmVydCB0aGUgc2VsZWN0ZWQgcmVnaW9uIGJhY2sgdG8gYSBzdHJhaWdodC1lZGdlZCBwb2x5Z29uLiAqL1xuICB0b1BvbHlnb25SZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25PdmVybGF5KCk/LnNldFNlbGVjdGVkQmV6aWVyKGZhbHNlKTtcbiAgfVxuXG4gIC8qKiBUaGUgcmlnaHQtY2xpY2sgbWVudSBmb3IgdGhlIGN1cnJlbnQgdmlldywgYXJtZWQgbW9kZSBhbmQgc2VsZWN0aW9uLiAqL1xuICBwcml2YXRlIGJ1aWxkQ29udGV4dE1lbnVJdGVtcygpOiBNZW51SXRlbVtdIHtcbiAgICByZXR1cm4gYnVpbGRDb250ZXh0TWVudSh0aGlzLmNvbnRleHRNZW51U3RhdGUoKSwgdGhpcyk7XG4gIH1cblxuICBwcml2YXRlIGNvbnRleHRNZW51U3RhdGUoKTogQ29udGV4dE1lbnVTdGF0ZSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGlzSGVhdG1hcDogdGhpcy5pc0hlYXRtYXAsXG4gICAgICBiYXNlUGxvdFR5cGU6IHRoaXMuYmFzZVBsb3RUeXBlLFxuICAgICAgYWN0aXZlRHJhZ01vZGU6IHRoaXMuYWN0aXZlRHJhZ01vZGUsXG4gICAgICBhY3RpdmVTdXJmYWNlM2RNb2RlOiB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUsXG4gICAgICByZWdpb25zOiB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKSxcbiAgICAgIHNlbGVjdGVkSW5kaWNlczogdGhpcy5yZWdpb25BY3Rpb25zLnNlbGVjdGVkSW5kaWNlcyxcbiAgICAgIGNhblVuZ3JvdXA6IChyKSA9PiB0aGlzLnJlZ2lvbk9wcy5jYW5Vbmdyb3VwKHIpLFxuICAgIH07XG4gIH1cblxuICAvKiogM0Qgc2NlbmUgaW50ZXJhY3Rpb24gbW9kZSAob3JiaXQgLyB0dXJudGFibGUgLyBwYW4gLyB6b29tKTsgbm8tb3Agb24gYSAyRC1vbmx5IGJhY2tlbmQuICovXG4gIHRvZ2dsZVN1cmZhY2UzZE1vZGUobW9kZTogc3RyaW5nKSB7XG4gICAgdGhpcy5hY3RpdmVTdXJmYWNlM2RNb2RlID0gbW9kZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFN1cmZhY2UzZENvbnRyb2xzKCk/LnNldFN1cmZhY2VEcmFnTW9kZShtb2RlKTtcbiAgfVxuXG4gIHJlc2V0U3VyZmFjZUNhbWVyYSgpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFN1cmZhY2UzZENvbnRyb2xzKCk/LnJlc2V0U3VyZmFjZUNhbWVyYSgpO1xuICB9XG5cbiAgLyoqIFRvZ2dsZSB0aGUgbmFwYXJpIDNEIGNvb3JkaW5hdGUtYXhlcyAvIHNjYWxlIGdpem1vICh2b2x1bWUvaXNvc3VyZmFjZSkuIE5vLW9wIG9uIGJhY2tlbmRzXG4gICAqICB0aGF0IGRvbid0IHJlbmRlciBvbmUgKHRoZSBjb250cm9sIG1ldGhvZCBpcyBvcHRpb25hbCkuICovXG4gIHByb3RlY3RlZCB0b2dnbGVBeGVzKCkge1xuICAgIHRoaXMuYXhlc1Zpc2libGUgPSAhdGhpcy5heGVzVmlzaWJsZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFN1cmZhY2UzZENvbnRyb2xzKCk/LnNldEF4ZXNWaXNpYmxlPy4odGhpcy5heGVzVmlzaWJsZSk7XG4gIH1cblxuICAvKiogVG9nZ2xlIHRoZSBuYXBhcmkgc3VyZmFjZSB3aXJlZnJhbWUgKGVkZ2VzIHZzIGZpbGxlZCkuIE5vLW9wIG9uIGJhY2tlbmRzIHdpdGhvdXQgYSBzdXJmYWNlLiAqL1xuICBwcm90ZWN0ZWQgdG9nZ2xlV2lyZWZyYW1lKCkge1xuICAgIHRoaXMud2lyZWZyYW1lQWN0aXZlID0gIXRoaXMud2lyZWZyYW1lQWN0aXZlO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0U3VyZmFjZTNkQ29udHJvbHMoKT8uc2V0V2lyZWZyYW1lPy4odGhpcy53aXJlZnJhbWVBY3RpdmUpO1xuICB9XG5cbiAgLyoqIENoYW5nZSB0aGUgbmFwYXJpIDNEIGRlY2ltYXRlIGZhY3Rvci4gRGVjaW1hdGlvbiBjaGFuZ2VzIHRoZSBmZXRjaGVkL2Fzc2VtYmxlZCBkYXRhLCBzbyB0aGlzXG4gICAqICByZS1wbG90cyB0aGUgY3VycmVudCAzRCB0eXBlIGF0IHRoZSBuZXcgcmVzb2x1dGlvbiAodW5saWtlIGF4ZXMvd2lyZWZyYW1lLCB3aGljaCBhcmUgbGl2ZSkuICovXG4gIHByb3RlY3RlZCBzZWxlY3RSZXNvbHV0aW9uKHNjYWxlOiBudW1iZXIpIHtcbiAgICBpZiAoc2NhbGUgPT09IHRoaXMucmVzb2x1dGlvblNjYWxlKSByZXR1cm47XG4gICAgdGhpcy5yZXNvbHV0aW9uU2NhbGUgPSBzY2FsZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFJlc29sdXRpb25TY2FsZT8uKHNjYWxlKTtcbiAgICB0aGlzLnJlbG9hZEFuZFBsb3QoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTd2l0Y2ggdGhlIGFjdGl2ZSBwbG90IHR5cGUgKGhlYXRtYXAsIHN1cmZhY2UsIGNvbnRvdXIsIHNjYXR0ZXIsIGxpbmUsXG4gICAqIHNjYXR0ZXIzZCwgaXNvc3VyZmFjZSkuIEtlZXBzIGBpc0hlYXRtYXBgIGFzIHRoZSAyRC12cy0zRCBmbGFnIHRoZSByZXN0IG9mXG4gICAqIHRoZSB0b29sYmFyL2NvbnRleHQtbWVudSBsb2dpYyByZWxpZXMgb24sIGRlYWN0aXZhdGVzIDJELW9ubHkgdG9vbHMgd2hlblxuICAgKiBtb3ZpbmcgdG8gYSAzRCB0eXBlLCB0aGVuIHJlLXBsb3RzLlxuICAgKi9cbiAgb25TZWxlY3RQbG90VHlwZShzZWxlY3RlZDogUGxvdFR5cGVJZCkge1xuICAgIC8vIEFsc28gY2FsbGVkIGJ5IHRoZSBob3N0IGRpcmVjdGx5LCBvdXRzaWRlIHRoaXMgdmlldydzIGV2ZW50czogdGhlIHNlbGVjdG9yLCB0aGVcbiAgICAvLyB0b29sYmFyJ3MgdG9vbCBncm91cHMgYW5kIHRoZSBkaWFsb2dzIGFsbCBmb2xsb3cgdGhlIHR5cGUuXG4gICAgdGhpcy5jaGFuZ2VkKCk7XG4gICAgLy8gQSBjb250cmlidXRlZCBpZCB3aG9zZSBwcm92aWRlciBpcyBnb25lIChlLmcuIHJlc3RvcmVkIGZyb20gYSBwcmV2aW91c1xuICAgIC8vIHNlc3Npb24pIGhhcyBub3RoaW5nIHRvIGFjdGl2YXRlIOKAlCB0YWtlIHRoZSBkZWZhdWx0IEltYWdlIHZpZXcgaW5zdGVhZC5cbiAgICBpZiAoIWlzQnVpbHRpblBsb3RUeXBlKHNlbGVjdGVkKSAmJiAhdGhpcy5wbG90TW9kZXMuZmluZChzZWxlY3RlZCkpIHtcbiAgICAgIGNvbnNvbGUud2FybihgW3Zpc3VhbGl6ZXJdIHVua25vd24gcGxvdCB0eXBlICcke3NlbGVjdGVkfScg4oCUIHNob3dpbmcgJyR7UGxvdFR5cGUuSU1BR0V9JyBpbnN0ZWFkLmApO1xuICAgICAgc2VsZWN0ZWQgPSBQbG90VHlwZS5JTUFHRTtcbiAgICB9XG4gICAgLy8gQW4gZXhwbGljaXQgY2hvaWNlIHJldHJpZXMgYSBtb2RlIHdob3NlIGVhcmxpZXIgY2xlYW51cCBmYWlsZWQ7IG9ubHlcbiAgICAvLyBhdXRvbWF0aWMgcmUtYWN0aXZhdGlvbnMgKHJlLXJlbmRlciwgaW1hZ2Ugc3dpdGNoKSBmYWxsIGJhY2sgZm9yIHRoYXQuXG4gICAgdGhpcy5wbG90TW9kZXMuY2xlYXJDbGVhbnVwRmFpbHVyZXMoKTtcbiAgICAvLyBXaGF0ZXZlciB3YXMgc2VsZWN0ZWQgYmVmb3JlLCBhIGNvbnRyaWJ1dGVkIHNlc3Npb24gZW5kcyBiZWZvcmUgdGhlIG5leHRcbiAgICAvLyBtb2RlIGRyYXdzLiAoUmUtc2VsZWN0aW5nIHRoZSBzYW1lIGNvbnRyaWJ1dGVkIG1vZGUgcmUtcGxvdHMsIHNvIGl0IGdldHMgYVxuICAgIC8vIGZyZXNoIHNlc3Npb24gdG9vLilcbiAgICB0aGlzLnBsb3RNb2Rlcy5kZWFjdGl2YXRlKCk7XG4gICAgLy8gRXZlcnl0aGluZyBiZWxvdyBkZWNpZGVzIGhvdyB0byBSRU5ERVIsIHNvIGl0IHdvcmtzIG9uIHRoZSBidWlsdC1pbiB0eXBlXG4gICAgLy8gdGhhdCBkcmF3cyBgc2VsZWN0ZWRgOiBhIGNvbnRyaWJ1dGVkIG1vZGUgYmVoYXZlcyBleGFjdGx5IGxpa2UgaXRzIGJhc2UuXG4gICAgY29uc3QgdHlwZSA9IHRoaXMucGxvdE1vZGVzLmJhc2VUeXBlT2Yoc2VsZWN0ZWQpO1xuICAgIC8vIEEgZGlhbG9nIHRvb2wgZHJhd3Mgb3ZlciB0aGUgSW1hZ2UgdmlldzogbGVhdmluZyBpdCBjbG9zZXMgdGhlIGRpYWxvZy4gU3RheWluZ1xuICAgIC8vIG9uIGl0LCB0aGUgc2Vzc2lvbiBlbmRzIGhlcmUgYW5kIHJlc3RhcnRzIG9uY2UgdGhlIHJlLXBsb3QgbGFuZHMuXG4gICAgaWYgKHR5cGUgPT09IFBsb3RUeXBlLklNQUdFKSB0aGlzLmNvbnRyaWJ1dGlvbnMudG9vbERpYWxvZ3MuZGVhY3RpdmF0ZSgpO1xuICAgIGVsc2UgdGhpcy5jbG9zZURpYWxvZ1Rvb2woKTtcbiAgICB0aGlzLnBsb3RUeXBlID0gdHlwZTtcbiAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCA9IHNlbGVjdGVkO1xuICAgIGNvbnN0IGRlc2NyaXB0b3IgPSBpc0J1aWx0aW5QbG90VHlwZShzZWxlY3RlZClcbiAgICAgID8gdGhpcy5wbG90VHlwZU1lbnUuZmluZCgoZCkgPT4gZC50eXBlID09PSB0eXBlKVxuICAgICAgOiBnZXRQbG90VHlwZURlc2NyaXB0b3IodHlwZSk7XG4gICAgY29uc3QgaXMzZCA9IGRlc2NyaXB0b3I/LmRpbWVuc2lvbnMgPT09ICczZCc7XG4gICAgdGhpcy5pc0hlYXRtYXAgPSAhaXMzZDtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFBsb3RUeXBlKHR5cGUpO1xuICAgIC8vIFN3aXRjaGluZyBwbG90IHR5cGUgY2FuY2VscyB0aGUgYWN0aXZlIHRvb2wg4oCUIG5vdCBldmVyeSB0eXBlIHN1cHBvcnRzIHRoZVxuICAgIC8vIHNhbWUgdG9vbHMgKGxpbmUvc2NhdHRlciBjaGFydHMgYW5kIDNEIHNjZW5lcyBoYXZlIG5vIGJveCB6b29tIG9yIHJlZ2lvblxuICAgIC8vIGRyYXdpbmcpLCBzbyBsZWF2aW5nIGEgdG9vbCBhcm1lZCB3b3VsZCBtaXNiZWhhdmUgb24gdGhlIG5ldyBwbG90LlxuICAgIHRoaXMudG9vbHMuZGVhY3RpdmF0ZSgpO1xuICAgIGlmIChpczNkKSB7XG4gICAgICB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUgPSAndHVybnRhYmxlJztcbiAgICB9XG4gICAgLy8gbmFwYXJpLWpzIHZvbHVtZS9pc29zdXJmYWNlIGFzc2VtYmxlIHRoZWlyIG93biAzRCB0ZXh0dXJlIGZyb20gdGhlIHNsaWNlIGVuZHBvaW50LCBzb1xuICAgIC8vIHRoZXkgdGFrZSB0aGUgbm9ybWFsIHJlLXBsb3QgcGF0aCDigJQgTk9UIFBsb3RseSdzIHN0YWNrLWZyYW1lIGxvYWRlciAod2hvc2UgXCJMb2FkaW5nXG4gICAgLy8gZnJhbWVzXCIgb3ZlcmxheSB3b3VsZCBuZXZlciBjbGVhciwgc2luY2UgUGxvdGx5IGlzbid0IHRoZSBhY3RpdmUgYmFja2VuZCkgKGppdC11aSMxMDIpLlxuICAgIGNvbnN0IGlzTmFwYXJpID0gdHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFIHx8IGlzTmFwYXJpM2QodHlwZSk7XG4gICAgaWYgKGRlc2NyaXB0b3I/LnJlcXVpcmVzU3RhY2sgJiYgIWlzTmFwYXJpKSB7XG4gICAgICAvLyBWb2x1bWUgdHlwZXMgKGlzb3N1cmZhY2UsIHNjYXR0ZXIzZCkgbmVlZCB0aGUgd2hvbGUgei1zdGFjayBsb2FkZWQgYXMgYVxuICAgICAgLy8gM0QgYXJyYXksIGFuZCB0aGV5IGFsd2F5cyByZW5kZXIgb24gUGxvdGx5LiBEcml2ZSB0aGUgc3RhY2sgcmVsb2FkXG4gICAgICAvLyBkaXJlY3RseSB0aHJvdWdoIHRoZSBpbWFnZS1pbmZvIHN0cmVhbSByYXRoZXIgdGhhbiB0aGUgYWN0aXZlIHJlbmRlcmVyJ3NcbiAgICAgIC8vIHNldFNob3dTdGFjazogd2hlbiBPcGVuU2VhZHJhZ29uIG93bnMgdGhlIEltYWdlIHZpZXcgaXRzIHNldFNob3dTdGFjayBpc1xuICAgICAgLy8gYSBuby1vcCwgc28gdGhlIHJlbG9hZCBuZXZlciBmaXJlZCBhbmQgdGhlIGxvYWRlciBzcHVuIGZvcmV2ZXIuIFdlIGFybVxuICAgICAgLy8gUGxvdGx5J3Mgc3RhY2stbG9hZGluZyBmbGFnIChrZWVwcyBpdHMgZnJhbWUtZmV0Y2ggbG9vcCBhbGl2ZSkgYW5kXG4gICAgICAvLyByZS1lbWl0IHRoZSBpbWFnZSBpbmZvIHdpdGggc2hvd1N0YWNrIG9uIHNvIHRoZSBwaXBlbGluZSByZWxvYWRzIG9uXG4gICAgICAvLyBQbG90bHkgcmVnYXJkbGVzcyBvZiB3aGljaCBiYWNrZW5kIHdhcyBvbiBzY3JlZW4uXG4gICAgICB0aGlzLnN0YWNrTG9hZGluZy5zZXQodHJ1ZSk7XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFN0YWNrTG9hZGluZyh0cnVlKTtcbiAgICAgIGlmICh0aGlzLmltYWdlSW5mbykge1xuICAgICAgICAvLyBBIGNvcHk6IHRoZSBpbWFnZSBpbmZvIG1heSBiZSB0aGUgaG9zdCdzIG93biAocG9zc2libHkgZnJvemVuKSBvYmplY3QuXG4gICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKHsgLi4udGhpcy5pbWFnZUluZm8sIHNob3dTdGFjazogdHJ1ZSB9KTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5yZWxvYWRBbmRQbG90KCk7XG4gIH1cblxuICAvLyDilIDilIAgZGlhbG9nIHRvb2xzIChUT09MQkFSX1RPT0xTLCBraW5kOiAnZGlhbG9nJykg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIFRoZSB0b29sYmFyIGJ1dHRvbjogb3BlbiB0aGUgdG9vbCdzIGRpYWxvZywgb3IgY2xvc2UgaXQgaWYgaXQgaXMgb3Blbi4gKi9cbiAgcHJvdGVjdGVkIHRvZ2dsZURpYWxvZ1Rvb2woaWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuY29udHJpYnV0aW9ucy50b2dnbGVEaWFsb2dUb29sKGlkKTtcbiAgfVxuXG4gIC8qKiBDbG9zZSB0aGUgb3BlbiBkaWFsb2cgdG9vbDogaXRzIGJvZHkgaXMgdG9ybiBkb3duLCB0aGVuIGl0cyBzZXNzaW9uIGVuZHMuICovXG4gIHByb3RlY3RlZCBjbG9zZURpYWxvZ1Rvb2woKTogdm9pZCB7XG4gICAgdGhpcy5jb250cmlidXRpb25zLmNsb3NlRGlhbG9nVG9vbCgpO1xuICB9XG5cbiAgLyoqIFdoZXJlIHRoZSBvcGVuIGRpYWxvZyB0b29sJ3MgaG9zdCBlbGVtZW50IGlzIGF0dGFjaGVkIG9uY2UgaXRzIGRpYWxvZyByZW5kZXJzLiAqL1xuICBAVmlld0NoaWxkKCd0b29sRGlhbG9nU2xvdCcpXG4gIHNldCB0b29sRGlhbG9nU2xvdChyZWY6IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+IHwgdW5kZWZpbmVkKSB7XG4gICAgdGhpcy5jb250cmlidXRpb25zLmF0dGFjaFRvb2xEaWFsb2dTbG90KHJlZj8ubmF0aXZlRWxlbWVudCk7XG4gIH1cbn1cbiIsIjwhLS0gU0FNIHRvYXN0OiBsaXZlIHN0YXR1cyArIGVuY29kZXItZG93bmxvYWQgcHJvZ3Jlc3MgYmFyIChqaXQtdWkjOTApLiBTdGlja3lcbiAgICAgd2hpbGUgYSBzZWdtZW50IHJ1bnM7IHN0YXlzIG9wZW4gdW50aWwgdGhlIGJhciByZWFjaGVzIDEwMCUgYW5kIHRoZSBydW4gZW5kcy4gLS0+XG48cC10b2FzdCBba2V5XT1cInNhbVRvYXN0S2V5XCIgcG9zaXRpb249XCJib3R0b20tcmlnaHRcIj5cbiAgPG5nLXRlbXBsYXRlIGxldC1tZXNzYWdlIHBUZW1wbGF0ZT1cIm1lc3NhZ2VcIj5cbiAgICA8ZGl2IHN0eWxlPVwiZmxleDogMTsgbWluLXdpZHRoOiAxNnJlbVwiPlxuICAgICAgPGRpdiBjbGFzcz1cImZvbnQtYm9sZCBtYi0xXCI+e3sgbWVzc2FnZS5zdW1tYXJ5IH19PC9kaXY+XG4gICAgICA8ZGl2Pnt7IHNlZ21lbnRhdGlvbi5zdGF0dXMgfHwgbWVzc2FnZS5kZXRhaWwgfX08L2Rpdj5cbiAgICAgIDxwLXByb2dyZXNzQmFyXG4gICAgICAgICpuZ0lmPVwic2VnbWVudGF0aW9uLmRvd25sb2FkaW5nXCJcbiAgICAgICAgW3ZhbHVlXT1cInNlZ21lbnRhdGlvbi5wcm9ncmVzc1wiXG4gICAgICAgIHN0eWxlQ2xhc3M9XCJtdC0yIHNhbS1wcm9ncmVzc1wiXG4gICAgICA+PC9wLXByb2dyZXNzQmFyPlxuICAgICAgPHAtcHJvZ3Jlc3NCYXJcbiAgICAgICAgKm5nSWY9XCJzZWdtZW50YXRpb24uYnVzeSAmJiAhc2VnbWVudGF0aW9uLmRvd25sb2FkaW5nXCJcbiAgICAgICAgbW9kZT1cImluZGV0ZXJtaW5hdGVcIlxuICAgICAgICBzdHlsZUNsYXNzPVwibXQtMlwiXG4gICAgICAgIFtzdHlsZV09XCJ7IGhlaWdodDogJzRweCcgfVwiXG4gICAgICA+PC9wLXByb2dyZXNzQmFyPlxuICAgIDwvZGl2PlxuICA8L25nLXRlbXBsYXRlPlxuPC9wLXRvYXN0PlxuXG48IS0tIFJlc3VsdC9lcnJvciBub3RpY2VzIGZyb20gdGhlIGxpYnJhcnkgaXRzZWxmIChzZWdtZW50YXRpb24gb3V0Y29tZSwgcmVnaW9uXG4gICAgIHZhbGlkYXRpb24pLiBSZW5kZXJlZCBoZXJlIHNvIHRoZXkgZG8gbm90IGRlcGVuZCBvbiB0aGUgaG9zdCBtb3VudGluZyBhXG4gICAgIGtleWxlc3MgPHAtdG9hc3Q+IOKAlCB3aXRob3V0IHRoaXMgdGhlIG1lc3NhZ2VzIHdlbnQgbm93aGVyZSBpbiBhbnkgaG9zdCBidXRcbiAgICAgaml0LXVpLCBhbmQgYSBmYWlsZWQgcnVuIGxvb2tlZCBsaWtlIGEgYnV0dG9uIHRoYXQgZGlkIG5vdGhpbmcuIC0tPlxuPHAtdG9hc3QgW2tleV09XCJyZXN1bHRUb2FzdEtleVwiIHBvc2l0aW9uPVwiYm90dG9tLXJpZ2h0XCI+PC9wLXRvYXN0PlxuXG48IS0tIFNoYXJlZCBvdXRsZXRzIGZvciBub3RpY2VzIHJhaXNlZCB3aGVyZSB0aGVyZSBpcyBubyBwZXItaW5zdGFuY2Uga2V5IHRvIHVzZTpcbiAgICAgdGhlIHJvb3QtcHJvdmlkZWQgUGxvdGx5U2VydmljZSBhbmQgdGhlIHJlZ2lvbi1lZGl0b3IgZGlhbG9nLiBSZW5kZXJlZCBieSBhXG4gICAgIHNpbmdsZSB2aXN1YWxpemVyIChvd25zU2hhcmVkVG9hc3RzKSBzbyBhIG1lc3NhZ2UgZnJvbSB0aGUgcm9vdCBzZXJ2aWNlIGlzXG4gICAgIG5vdCBzaG93biBvbmNlIHBlciBsaXZlIHZpZXdlci4gLS0+XG48cC10b2FzdCAqbmdJZj1cIm93bnNTaGFyZWRUb2FzdHNcIiBba2V5XT1cInZpelRvYXN0S2V5XCI+PC9wLXRvYXN0PlxuPHAtdG9hc3QgKm5nSWY9XCJvd25zU2hhcmVkVG9hc3RzXCIgW2tleV09XCJ2aXpBbGVydFRvYXN0S2V5XCIgcG9zaXRpb249XCJjZW50ZXJcIj48L3AtdG9hc3Q+XG5cbjwhLS0gVG9vbGJhciBkb2NrOiBkb2NrZWQgYWNyb3NzIHRoZSB0b3AgYnkgZGVmYXVsdDsgZHJhZyB0aGUgaGFuZGxlIHRvIGRldGFjaFxuICAgICBpdCBpbnRvIGEgZmxvYXRpbmcsIG1vdmFibGUgd2luZG93IChmcmVlcyB0aGUgdG9wIHJvdyBmb3IgdGhlIGltYWdlKS4gLS0+XG48ZGl2XG4gIGNsYXNzPVwidG9vbGJhci1kb2NrXCJcbiAgW2NsYXNzLmZsb2F0aW5nXT1cInRvb2xiYXJGbG9hdGluZ1wiXG4gIFtzdHlsZS5sZWZ0LnB4XT1cInRvb2xiYXJGbG9hdGluZyA/IHRvb2xiYXJQb3MueCA6IG51bGxcIlxuICBbc3R5bGUudG9wLnB4XT1cInRvb2xiYXJGbG9hdGluZyA/IHRvb2xiYXJQb3MueSA6IG51bGxcIlxuPlxuICA8ZGl2XG4gICAgY2xhc3M9XCJ0b29sYmFyLWRyYWctaGFuZGxlXCJcbiAgICBbdml6RmxvYXRpbmdEcmFnXT1cInRvb2xiYXJEcmFnT3JpZ2luXCJcbiAgICAodml6RmxvYXRpbmdEcmFnTW92ZSk9XCJ0b29sYmFyUG9zID0gJGV2ZW50XCJcbiAgICBwVG9vbHRpcD1cIkRyYWcgdG8gZmxvYXQgdGhlIHRvb2xiYXJcIlxuICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gID5cbiAgICA8aSBjbGFzcz1cInBpIHBpLWJhcnNcIj48L2k+XG4gIDwvZGl2PlxuICA8cGxvdHRpbmctdG9vbGJhclxuICAgIFtpbWFnZUluZm9dPVwiaW1hZ2VJbmZvXCJcbiAgICBbaGFzU3BhdGlhbERhdGFzZXRdPVwiaGFzU3BhdGlhbERhdGFzZXRcIlxuICAgIFtwbG90VHlwZU9wdGlvbnNdPVwicGxvdFR5cGVNZW51XCJcbiAgICBbc2VsZWN0ZWRQbG90VHlwZV09XCJzZWxlY3RlZFBsb3RUeXBlSWRcIlxuICAgIFtiYXNlUGxvdFR5cGVdPVwiYmFzZVBsb3RUeXBlXCJcbiAgICBbaXNvUmFuZ2VdPVwiaXNvUmFuZ2VcIlxuICAgIFttYXhJbmRleF09XCJtYXhJbmRleFwiXG4gICAgW3pJbmRleF09XCJ6SW5kZXhcIlxuICAgIFtpc0hlYXRtYXBdPVwiaXNIZWF0bWFwXCJcbiAgICBbaXMzZFJlZ2lvbnNdPVwiaXNTcGF0aWFsM2RNb2RlXCJcbiAgICBbYWN0aXZlRHJhZ01vZGVdPVwiYWN0aXZlRHJhZ01vZGVcIlxuICAgIFthY3RpdmVTdXJmYWNlM2RNb2RlXT1cImFjdGl2ZVN1cmZhY2UzZE1vZGVcIlxuICAgIFtheGVzVmlzaWJsZV09XCJheGVzVmlzaWJsZVwiXG4gICAgW3dpcmVmcmFtZUFjdGl2ZV09XCJ3aXJlZnJhbWVBY3RpdmVcIlxuICAgIFtyZXNvbHV0aW9uU2NhbGVdPVwicmVzb2x1dGlvblNjYWxlXCJcbiAgICBbd2FuZFNlbnNpdGl2aXR5XT1cIndhbmRTZW5zaXRpdml0eVwiXG4gICAgW2JydXNoU2l6ZV09XCJicnVzaFNpemVcIlxuICAgIFtzYW1Nb2RlbHNdPVwic2FtTW9kZWxzXCJcbiAgICBbc2FtTW9kZWxJZF09XCJzYW1Nb2RlbElkXCJcbiAgICBbY29udHJpYnV0ZWRUb29sc109XCJjb250cmlidXRlZFRvb2xzXCJcbiAgICBbZGlhbG9nVG9vbHNdPVwiZGlhbG9nVG9vbHNcIlxuICAgIFtvcGVuRGlhbG9nVG9vbElkXT1cIm9wZW5EaWFsb2dUb29sSWRcIlxuICAgICh0b2dnbGVEaWFsb2dUb29sKT1cInRvZ2dsZURpYWxvZ1Rvb2woJGV2ZW50KVwiXG4gICAgW3Rvb2xNb2RlbElkc109XCJ0b29sUGFyYW1zLnRvb2xNb2RlbElkc1wiXG4gICAgW3ZlcnRleEVyYXNlclJhZGl1c109XCJ2ZXJ0ZXhFcmFzZXJSYWRpdXNcIlxuICAgIFtjYW5VbmRvXT1cInJlZ2lvbkFjdGlvbnMuY2FuVW5kb1wiXG4gICAgW2NhblJlZG9dPVwicmVnaW9uQWN0aW9ucy5jYW5SZWRvXCJcbiAgICBbdG9vbHNdPVwidG9vbGJhclRvb2xzXCJcbiAgICBbaW1hZ2VTbW9vdGhpbmdFbmFibGVkXT1cImltYWdlU21vb3RoaW5nRW5hYmxlZFwiXG4gICAgKHNlbGVjdFBsb3RUeXBlKT1cIm9uU2VsZWN0UGxvdFR5cGUoJGV2ZW50KVwiXG4gICAgKHRvZ2dsZUltYWdlU21vb3RoaW5nKT1cIm9uVG9nZ2xlSW1hZ2VTbW9vdGhpbmcoKVwiXG4gICAgKGFkZFByb2ZpbGVMaW5lKT1cImFkZFByb2ZpbGVMaW5lKClcIlxuICAgIChpc29SYW5nZUNoYW5nZSk9XCJvbklzb1JhbmdlQ2hhbmdlKCRldmVudClcIlxuICAgIChvcGVuQ2hhbm5lbEhpc3RvZ3JhbSk9XCJvcGVuQ2hhbm5lbEhpc3RvZ3JhbSgpXCJcbiAgICAob3BlblNwYXRpYWxDb250cm9scyk9XCJvcGVuU3BhdGlhbENvbnRyb2xzKClcIlxuICAgIChvcGVuUmVnaW9uRWRpdG9yKT1cIm9wZW5SZWdpb25FZGl0b3IoKVwiXG4gICAgKHNlbGVjdFN0YWNrT3B0aW9uKT1cInNlbGVjdFN0YWNrT3B0aW9uKCRldmVudClcIlxuICAgICh6U2NydWIpPVwib25aU2NydWIoJGV2ZW50KVwiXG4gICAgKHpTbGlkZSk9XCJvblpTbGlkZSgkZXZlbnQpXCJcbiAgICAoekluZGV4SW5wdXQpPVwiekluZGV4ID0gJGV2ZW50OyB1cGRhdGVaSW5kZXgoKVwiXG4gICAgKHJlbG9hZEFuZFBsb3QpPVwicmVsb2FkQW5kUGxvdCgpXCJcbiAgICAoZG93bmxvYWRJbWFnZSk9XCJkb3dubG9hZEltYWdlKClcIlxuICAgIChhdXRvc2NhbGVJbWFnZSk9XCJhdXRvc2NhbGVJbWFnZSgpXCJcbiAgICAodG9nZ2xlRHJhZ01vZGUpPVwidG9nZ2xlRHJhZ01vZGUoJGV2ZW50KVwiXG4gICAgKHpvb21Jbik9XCJ6b29tSW4oKVwiXG4gICAgKHpvb21PdXQpPVwiem9vbU91dCgpXCJcbiAgICAodG9nZ2xlU3VyZmFjZTNkTW9kZSk9XCJ0b2dnbGVTdXJmYWNlM2RNb2RlKCRldmVudClcIlxuICAgIChyZXNldFN1cmZhY2VDYW1lcmEpPVwicmVzZXRTdXJmYWNlQ2FtZXJhKClcIlxuICAgICh0b2dnbGVBeGVzKT1cInRvZ2dsZUF4ZXMoKVwiXG4gICAgKHRvZ2dsZVdpcmVmcmFtZSk9XCJ0b2dnbGVXaXJlZnJhbWUoKVwiXG4gICAgKHNlbGVjdFJlc29sdXRpb24pPVwic2VsZWN0UmVzb2x1dGlvbigkZXZlbnQpXCJcbiAgICAoZGVsZXRlUmVnaW9uKT1cImRlbGV0ZVJlZ2lvbigpXCJcbiAgICAodW5kb1JlZ2lvbik9XCJ1bmRvUmVnaW9uKClcIlxuICAgIChyZWRvUmVnaW9uKT1cInJlZG9SZWdpb24oKVwiXG4gICAgKHNlZ21lbnRSZWdpb25zKT1cInNlZ21lbnRSZWdpb25zKClcIlxuICAgIChzZWdtZW50Q2VsbHBvc2UpPVwic2VnbWVudENlbGxwb3NlKClcIlxuICAgIChzYW1Nb2RlbENoYW5nZSk9XCJvblNhbU1vZGVsQ2hhbmdlKCRldmVudClcIlxuICAgIChydW5Ub29sKT1cInJ1blRvb2woJGV2ZW50KVwiXG4gICAgKHRvb2xNb2RlbENoYW5nZSk9XCJvblRvb2xNb2RlbENoYW5nZSgkZXZlbnQpXCJcbiAgICAob3BlblRvb2xQYXJhbXMpPVwib3BlblRvb2xQYXJhbXMoJGV2ZW50KVwiXG4gICAgKHdhbmRTZW5zaXRpdml0eUNoYW5nZSk9XCJvbldhbmRTZW5zaXRpdml0eUNoYW5nZSgkZXZlbnQpXCJcbiAgICAoYnJ1c2hTaXplQ2hhbmdlKT1cIm9uQnJ1c2hTaXplQ2hhbmdlKCRldmVudClcIlxuICAgICh2ZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UpPVwib25WZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UoJGV2ZW50KVwiXG4gID5cbiAgICA8IS0tIEhvc3QtcHJvamVjdGVkIGN1c3RvbSB0b29sYmFyIGJ1dHRvbnMsIGZvcndhcmRlZCBpbnRvIHRoZSB0b29sYmFyLiAtLT5cbiAgICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG4gIDwvcGxvdHRpbmctdG9vbGJhcj5cbiAgPHAtYnV0dG9uXG4gICAgKm5nSWY9XCJ0b29sYmFyRmxvYXRpbmdcIlxuICAgIGNsYXNzPVwidG9vbGJhci1kb2NrLWJ0blwiXG4gICAgaWNvbj1cInBpIHBpLXRodW1idGFja1wiXG4gICAgc2l6ZT1cInNtYWxsXCJcbiAgICBbdGV4dF09XCJ0cnVlXCJcbiAgICBwVG9vbHRpcD1cIkRvY2sgdG8gdG9wXCJcbiAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgIChvbkNsaWNrKT1cImRvY2tUb29sYmFyKClcIlxuICA+XG4gIDwvcC1idXR0b24+XG48L2Rpdj5cblxuPHAtY29udGV4dE1lbnUgI2NtIFttb2RlbF09XCJjb250ZXh0TWVudUl0ZW1zXCIgYXBwZW5kVG89XCJib2R5XCI+PC9wLWNvbnRleHRNZW51PlxuPCEtLSBObyBgaC1mdWxsYCBoZXJlOiBQcmltZUZsZXgncyBoZWlnaHQ6MTAwJSAhaW1wb3J0YW50IG1hZGUgI3Bsb3QgYXMgdGFsbCBhc1xuICAgICB0aGUgd2hvbGUgaG9zdCwgc28gdW5kZXIgdGhlIGRvY2tlZCB0b29sYmFyIGl0cyBib3R0b20gdG9vbGJhci1oZWlnaHQgc3RyaXBcbiAgICAgKGluY2wuIHRoZSBPU0Qgc2NhbGUgYmFyIGF0IGJvdHRvbS1sZWZ0KSB3YXMgY2xpcHBlZCBieSB0aGUgaG9zdCdzXG4gICAgIG92ZXJmbG93OmhpZGRlbi4gVGhlIGNvbXBvbmVudCBzdHlsZXNoZWV0IHNpemVzIGl0ICgudml6LXBsb3QgeyBmbGV4OiAxIH0pLiAtLT5cbjxkaXYgY2xhc3M9XCJ3LWZ1bGwgcmVsYXRpdmUgdml6LXBsb3RcIiBbaWRdPVwicGxvdERpdk5hbWVcIj5cbiAgPGRpdiAqbmdJZj1cImltZ0xvYWRpbmcoKSB8fCBzdGFja0xvYWRpbmcoKSB8fCBpc0NhY2hpbmcoKVwiIGNsYXNzPVwibG9hZGluZy1vdmVybGF5XCI+XG4gICAgPCEtLSBEZXRlcm1pbmF0ZSBzdGFjay92b2x1bWUgcHJvZ3Jlc3MgdGFrZXMgcHJlY2VkZW5jZTogd2hpbGUgaXQncyBhY3RpdmUgc2hvdyBPTkxZIHRoZVxuICAgICAgICAgcHJvZ3Jlc3MgYmFyICsgXCJMb2FkaW5nIGZyYW1lcy4uLlwiLCBub3QgdGhlIGdlbmVyaWMgc3Bpbm5lci9cIkxvYWRpbmcgaW1hZ2UuLi5cIiBvbiB0b3Agb2ZcbiAgICAgICAgIGl0IChib3RoIHdlcmUgcmVuZGVyaW5nIGF0IG9uY2UgZm9yIG5hcGFyaSB2b2x1bWUvaXNvc3VyZmFjZSBsb2FkcykuIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJzdGFja0xvYWRpbmcoKTsgZWxzZSBpbWdMb2FkaW5nVHBsXCI+XG4gICAgICA8cC1wcm9ncmVzc0JhciBjbGFzcz1cInByb2dyZXNzXCIgW3ZhbHVlXT1cImxvYWRpbmdQZXJjZW50YWdlKClcIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRleHQtbG9hZGluZ1wiPkxvYWRpbmcgZnJhbWVzLi4uPC9zcGFuPlxuICAgIDwvbmctY29udGFpbmVyPlxuICAgIDxuZy10ZW1wbGF0ZSAjaW1nTG9hZGluZ1RwbD5cbiAgICAgIDwhLS0gQ2FjaGUtY29weSBwcm9ncmVzcyBpcyBzaG93biB3aGVuZXZlciBhIGNhY2hlIGRvd25sb2FkIGlzIGFjdGl2ZSAoaXNDYWNoaW5nKSwgaW5kZXBlbmRlbnRcbiAgICAgICAgICAgb2YgYGltZ0xvYWRpbmdgLCBzbyBpdCdzIG5ldmVyIGRyb3BwZWQvbWFza2VkIG1pZC1kb3dubG9hZC4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNDYWNoaW5nKCk7IGVsc2Ugc3Bpbm5lckxvYWRpbmdcIj5cbiAgICAgICAgPCEtLSBTdGFnZS1hd2FyZSBtZXNzYWdlIGNvbWVzIGZyb20gQ2FjaGVQcm9ncmVzc1NlcnZpY2UgdXBkYXRpbmdcbiAgICAgICAgICAgICBpbWFnZUxvYWRpbmdNZXNzYWdlJCAoXCJDYWNoaW5nIGltYWdlLi4uXCIgZHVyaW5nIHRoZSBHQ1MgZG93bmxvYWQsXG4gICAgICAgICAgICAgdGhlbiBcIkdlbmVyYXRpbmcgcHJldmlldy4uLlwiIGR1cmluZyBwZXItc2xpY2UgdGh1bWJuYWlsIGdlbikuIE9uY2UgdGhlXG4gICAgICAgICAgICAgZG93bmxvYWQgaGl0cyAxMDAlIHRoZSByZW1haW5pbmcgd29yayAoc2VydmVyIHByZXZpZXcvdGlsZSBnZW5lcmF0aW9uKVxuICAgICAgICAgICAgIGhhcyBubyBkZXRlcm1pbmF0ZSBwcm9ncmVzcywgc28gZmFsbCB0aHJvdWdoIHRvIHRoZSBzcGlubmVyIHRlbXBsYXRlXG4gICAgICAgICAgICAgcmF0aGVyIHRoYW4gcGFya2luZyBhIGZ1bGwgYmFyIHVuZGVyIFwiTG9hZGluZyBpbWFnZS4uLlwiLiAtLT5cbiAgICAgICAgPHAtcHJvZ3Jlc3NCYXIgY2xhc3M9XCJwcm9ncmVzc1wiIFt2YWx1ZV09XCJjYWNoZVByb2dyZXNzKClcIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1sb2FkaW5nXCI+e3sgbG9hZGluZ01lc3NhZ2UoKSB9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPG5nLXRlbXBsYXRlICNzcGlubmVyTG9hZGluZz5cbiAgICAgICAgPGRpdiAqbmdJZj1cImltZ0xvYWRpbmcoKVwiIGNsYXNzPVwic3Bpbm5lclwiPjwvZGl2PlxuICAgICAgICA8c3BhbiAqbmdJZj1cImltZ0xvYWRpbmcoKSAmJiAhem9vbSgpXCIgY2xhc3M9XCJ0ZXh0LWxvYWRpbmdcIj57eyBsb2FkaW5nTWVzc2FnZSgpIH19PC9zcGFuPlxuICAgICAgICA8c3BhbiAqbmdJZj1cImltZ0xvYWRpbmcoKSAmJiB6b29tKClcIiBjbGFzcz1cInpvb20tdGV4dC1sb2FkaW5nXCI+e3sgbG9hZGluZ01lc3NhZ2UoKSB9fTwvc3Bhbj5cbiAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgPC9uZy10ZW1wbGF0ZT5cbiAgICA8cC1idXR0b25cbiAgICAgICpuZ0lmPVwic3RhY2tMb2FkaW5nKCkgfHwgaW1nTG9hZGluZygpXCJcbiAgICAgIGNsYXNzPVwiY2FuY2VsLWxvYWRpbmdcIlxuICAgICAgbGFiZWw9XCJDYW5jZWxcIlxuICAgICAgKG9uQ2xpY2spPVwiY2FuY2VsTG9hZGluZygpXCJcbiAgICA+XG4gICAgPC9wLWJ1dHRvbj5cbiAgPC9kaXY+XG4gIDwhLS0gVHJhbnNsdWNlbnQgXCJzaGFycGVuaW5nXCIgb3ZlcmxheSBzaG93biBhZnRlciB0aGUgc21hbGwtdGllciBwcmV2aWV3IGlzXG4gICAgICAgb24gc2NyZWVuIHdoaWxlIHRoZSBsYXJnZS10aWVyIHJlbmRlciBpcyBzdGlsbCBsb2FkaW5nIGluIHRoZVxuICAgICAgIGJhY2tncm91bmQuIExldHMgdGhlIHVzZXIgc2VlIHRoZSBibHVycnkgaW1hZ2UgdW5kZXJuZWF0aCBidXQgbWFrZXNcbiAgICAgICBpdCBjbGVhciBzb21ldGhpbmcgaXMgc3RpbGwgbG9hZGluZyBzbyB0aGV5IGRvbid0IG1pc3Rha2UgdGhlXG4gICAgICAgbG93LXJlcyByZW5kZXIgZm9yIHRoZSBmaW5hbCBpbWFnZS4gLS0+XG4gIDxkaXYgKm5nSWY9XCJyZW5kZXIuc2hhcnBlbmluZyAmJiAhaW1nTG9hZGluZygpICYmICFzdGFja0xvYWRpbmcoKSAmJiAhaXNDYWNoaW5nKClcIiBjbGFzcz1cInNoYXJwZW5pbmctb3ZlcmxheVwiPlxuICAgIDxkaXYgY2xhc3M9XCJzcGlubmVyXCI+PC9kaXY+XG4gICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LWxvYWRpbmdcIj5TaGFycGVuaW5nIHByZXZpZXcuLi48L3NwYW4+XG4gIDwvZGl2PlxuPC9kaXY+XG5cbjwhLS0gRmxvYXRpbmcsIGRyYWdnYWJsZSBpbnRlbnNpdHktcHJvZmlsZSBpbnNldCwgc2hvd24gd2hpbGUgYW55IGludGVuc2l0eSBsaW5lXG4gICAgIGV4aXN0cy4gS2VwdCBPVVRTSURFIHRoZSBwbG90IGRpdiBhbmQgZml4ZWQgdG8gdGhlIHZpZXdwb3J0LCBzbyBpdCBmbG9hdHMgb3ZlclxuICAgICB0aGUgd2hvbGUgcGFnZSByYXRoZXIgdGhhbiBiZWluZyBjbGlwcGVkIHRvIHRoZSBjYW52YXMuIC0tPlxuPHZpei1pbnRlbnNpdHktaW5zZXRcbiAgW2RpdklkXT1cImludGVuc2l0eUluc2V0RGl2XCJcbiAgW3Bsb3REaXZOYW1lXT1cInBsb3REaXZOYW1lXCJcbiAgW3pJbmRleF09XCJ6SW5kZXhcIlxuICBbaW1hZ2VWaWV3XT1cImlzSW1hZ2VWaWV3XCJcbiAgW2ltYWdlSW5mb109XCJpbWFnZUluZm9cIlxuPjwvdml6LWludGVuc2l0eS1pbnNldD5cblxuPCEtLSBDaGFubmVscyAmIEhpc3RvZ3JhbTogbm9uLW1vZGFsIGZsb2F0aW5nIGRpYWxvZyBvcGVuZWQgZnJvbSB0aGUgdG9vbGJhci4gLS0+XG48Y2hhbm5lbC1oaXN0b2dyYW0gWyh2aXNpYmxlKV09XCJzaG93Q2hhbm5lbEhpc3RvZ3JhbVwiPjwvY2hhbm5lbC1oaXN0b2dyYW0+XG5cbjwhLS0gU3BhdGlhbC1vbWljcyBjb250cm9scy4gUmVuZGVycyBpdHMgb3duIGVtcHR5IHN0YXRlIHdoZW4gbm8gZGF0YXNldCAob3Igbm9cbiAgICAgU1BBVElBTF9EQVRBX1BPUlQpIGlzIHByZXNlbnQsIHNvIGl0IGlzIGFsd2F5cyBzYWZlIHRvIGhvc3QuIC0tPlxuPHNwYXRpYWwtY29udHJvbHMgWyh2aXNpYmxlKV09XCJzaG93U3BhdGlhbENvbnRyb2xzXCIgW2lzM2RdPVwiaXNTcGF0aWFsM2RNb2RlXCI+PC9zcGF0aWFsLWNvbnRyb2xzPlxuXG48IS0tIFNpZGUgcGFuZWwgb2YgdGhlIGFjdGl2ZSBjb250cmlidXRlZCBwbG90IG1vZGUgKFBMT1RfVFlQRV9DT05UUklCVVRJT05TKSxcbiAgICAgaW4gdGhlIHNhbWUgcmlnaHQtaGFuZCBhcmVhIGFzIHRoZSBzcGF0aWFsIGNvbnRyb2xzIGFuZCB0aGUgUmVnaW9uIEVkaXRvci5cbiAgICAgRXhpc3RzIG9ubHkgd2hpbGUgdGhlIG1vZGUncyBzZXNzaW9uIGlzIGxpdmU7IHRoZXJlIGlzIG5vIGNsb3NlIGJ1dHRvbixcbiAgICAgYmVjYXVzZSBsZWF2aW5nIHRoZSBtb2RlIGlzIHdoYXQgZW5kcyBpdC4gQSBjb21wb25lbnQgcGFuZWwgZ2V0c1xuICAgICBQTE9UX01PREVfQ09OVEVYVCAvIFBMT1RfTU9ERV9TRVNTSU9OIGZyb20gaXRzIGluamVjdG9yOyBhIGBtb3VudGAgcGFuZWwnc1xuICAgICBob3N0IGVsZW1lbnQgaXMgYXR0YWNoZWQgdG8gdGhlIHNsb3QgYmVsb3cgKHNlZSBwbG90TW9kZVBhbmVsU2xvdCkuIC0tPlxuPHAtZGlhbG9nXG4gICpuZ0lmPVwicGxvdE1vZGVQYW5lbCBhcyBwYW5lbFwiXG4gIFtoZWFkZXJdPVwicGFuZWwudGl0bGVcIlxuICBbdmlzaWJsZV09XCJ0cnVlXCJcbiAgW21vZGFsXT1cImZhbHNlXCJcbiAgW2Nsb3NhYmxlXT1cImZhbHNlXCJcbiAgW3Jlc2l6YWJsZV09XCJ0cnVlXCJcbiAgW2RyYWdnYWJsZV09XCJ0cnVlXCJcbiAgYXBwZW5kVG89XCJib2R5XCJcbiAgcG9zaXRpb249XCJyaWdodFwiXG4gIHN0eWxlQ2xhc3M9XCJwbG90LW1vZGUtcGFuZWxcIlxuICBbc3R5bGVdPVwieyB3aWR0aDogJzIycmVtJywgbWF4SGVpZ2h0OiAnODV2aCcgfVwiXG4gIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIlxuPlxuICA8bmctY29udGFpbmVyICpuZ0lmPVwicGFuZWwuY29tcG9uZW50XCI+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdDb21wb25lbnRPdXRsZXQ9XCJwYW5lbC5jb21wb25lbnQ7IGluamVjdG9yOiBwYW5lbC5pbmplY3RvciA/PyB1bmRlZmluZWRcIj48L25nLWNvbnRhaW5lcj5cbiAgPC9uZy1jb250YWluZXI+XG4gIDxkaXYgKm5nSWY9XCJwYW5lbC5ob3N0XCIgI3Bsb3RNb2RlUGFuZWxTbG90IGNsYXNzPVwicGxvdC1tb2RlLXBhbmVsLXNsb3RcIj48L2Rpdj5cbjwvcC1kaWFsb2c+XG5cbjwhLS0gQW4gb3BlbiBkaWFsb2cgdG9vbCAoVE9PTEJBUl9UT09MUywga2luZDogJ2RpYWxvZycpLiBOb24tbW9kYWwgc28gdGhlIHVzZXIgY2FuXG4gICAgIGtlZXAgd29ya2luZyBvbiB0aGUgaW1hZ2UsIGUuZy4gcGFpbnRpbmcgd2l0aCB0aGUgYnJ1c2ggdGhlIHRvb2wgYXJtZWQuIFRoZVxuICAgICB0b29sIHJlbmRlcnMgaXRzIGJvZHkgaW50byBhIGhvc3QgZWxlbWVudCBhdHRhY2hlZCB0byB0aGUgc2xvdCBiZWxvdyAoc2VlXG4gICAgIHRvb2xEaWFsb2dTbG90KTsgY2xvc2luZyB0aGUgZGlhbG9nIGVuZHMgdGhlIHRvb2wncyBzZXNzaW9uLiAtLT5cbjxwLWRpYWxvZ1xuICAqbmdJZj1cInRvb2xEaWFsb2cgYXMgZGxnXCJcbiAgW2hlYWRlcl09XCJkbGcudGl0bGVcIlxuICBbdmlzaWJsZV09XCJ0cnVlXCJcbiAgKHZpc2libGVDaGFuZ2UpPVwiJGV2ZW50IHx8IGNsb3NlRGlhbG9nVG9vbCgpXCJcbiAgW21vZGFsXT1cImZhbHNlXCJcbiAgW2Nsb3NhYmxlXT1cInRydWVcIlxuICBbcmVzaXphYmxlXT1cInRydWVcIlxuICBbZHJhZ2dhYmxlXT1cInRydWVcIlxuICBhcHBlbmRUbz1cImJvZHlcIlxuICBwb3NpdGlvbj1cInJpZ2h0XCJcbiAgc3R5bGVDbGFzcz1cInRvb2wtZGlhbG9nXCJcbiAgW3N0eWxlXT1cInsgd2lkdGg6IGRsZy53aWR0aCwgbWF4SGVpZ2h0OiAnODV2aCcgfVwiXG4gIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIlxuPlxuICA8ZGl2ICN0b29sRGlhbG9nU2xvdCBjbGFzcz1cInRvb2wtZGlhbG9nLXNsb3RcIj48L2Rpdj5cbjwvcC1kaWFsb2c+XG5cbjwhLS0gUmVnaW9uIEVkaXRvcjogbm9uLW1vZGFsLCBkcmFnZ2FibGUsIHJlc2l6YWJsZSBmbG9hdGluZyBkaWFsb2cgb3BlbmVkIGZyb21cbiAgICAgdGhlIHRvb2xiYXIgKGZvcm1lcmx5IHRoZSByaWdodC1wYW5lbCBSZWdpb25zIHRhYikuIE1vZGVsZXNzIHNvIHJlZ2lvbnMgY2FuXG4gICAgIHN0aWxsIGJlIGRyYXduL3NlbGVjdGVkIG9uIHRoZSBwbG90IHdoaWxlIGl0J3Mgb3Blbjsgc3RheXMgbGlua2VkIHRvIHRoaXNcbiAgICAgcGxvdHRpbmcgaW5zdGFuY2UgdmlhIHRoZSByb290IFJlZ2lvblN0b3JlIHNpbmdsZXRvbi4gT3BlbnMgb3ZlciB0aGUgcmlnaHRcbiAgICAgc2lkZSBvZiB0aGUgdmlldy4gLS0+XG48cC1kaWFsb2dcbiAgaGVhZGVyPVwiUmVnaW9uIEVkaXRvclwiXG4gIFsodmlzaWJsZSldPVwic2hvd1JlZ2lvbkVkaXRvclwiXG4gIFttb2RhbF09XCJmYWxzZVwiXG4gIFtyZXNpemFibGVdPVwidHJ1ZVwiXG4gIFtkcmFnZ2FibGVdPVwidHJ1ZVwiXG4gIFttYXhpbWl6YWJsZV09XCJ0cnVlXCJcbiAgYXBwZW5kVG89XCJib2R5XCJcbiAgcG9zaXRpb249XCJyaWdodFwiXG4gIFtzdHlsZV09XCJ7IHdpZHRoOiByZWdpb25FZGl0b3JXaWR0aCwgaGVpZ2h0OiAnODV2aCcgfVwiXG4gIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIlxuPlxuICA8cmVnaW9uLWVkaXRvcj48L3JlZ2lvbi1lZGl0b3I+XG48L3AtZGlhbG9nPlxuXG48IS0tIFNpbXBsaWZ5IChEb3VnbGFz4oCTUGV1Y2tlcik6IGN1c3RvbSBweC10aHJlc2hvbGQgcGlja2VyIGZvciB0aGUgc2VsZWN0ZWRcbiAgICAgcmVnaW9uKHMpLCBvcGVuZWQgZnJvbSB0aGUgcmlnaHQtY2xpY2sgXCJTaW1wbGlmeSDilrggQ3VzdG9t4oCmXCIgYWN0aW9uLiAtLT5cbjxwLWRpYWxvZ1xuICBoZWFkZXI9XCJTaW1wbGlmeSByZWdpb24ocylcIlxuICBbKHZpc2libGUpXT1cImRpc3BsYXlTaW1wbGlmeURpYWxvZ1wiXG4gIFttb2RhbF09XCJ0cnVlXCJcbiAgW3N0eWxlXT1cInsgd2lkdGg6ICcyMnJlbScgfVwiXG4gIFtkcmFnZ2FibGVdPVwiZmFsc2VcIlxuICBbcmVzaXphYmxlXT1cImZhbHNlXCJcbj5cbiAgPGRpdiBjbGFzcz1cImZsZXggZmxleC1jb2x1bW4gZ2FwLTJcIj5cbiAgICA8c3BhblxuICAgICAgPkFsdGl0dWRlIHRocmVzaG9sZDogPGI+e3sgcmVnaW9uQWN0aW9ucy5zaW1wbGlmeVRocmVzaG9sZCB9fSBweDwvYj48L3NwYW5cbiAgICA+XG4gICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwicmVnaW9uQWN0aW9ucy5zaW1wbGlmeVRocmVzaG9sZFwiIFttaW5dPVwiMVwiIFttYXhdPVwiMjBcIiBbc3RlcF09XCIxXCI+PC9wLXNsaWRlcj5cbiAgICA8c21hbGw+VmVydGljZXMgd2l0aGluIHRoaXMgZGlzdGFuY2Ugb2YgdGhlIGxpbmUgYmV0d2VlbiB0aGVpciBuZWlnaGJvdXJzIGFyZSByZW1vdmVkLjwvc21hbGw+XG4gIDwvZGl2PlxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPHAtYnV0dG9uIGxhYmVsPVwiQ2FuY2VsXCIgW3RleHRdPVwidHJ1ZVwiIChvbkNsaWNrKT1cImRpc3BsYXlTaW1wbGlmeURpYWxvZyA9IGZhbHNlXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b25cbiAgICAgIGxhYmVsPVwiQXBwbHlcIlxuICAgICAgaWNvbj1cInBpIHBpLWNoZWNrXCJcbiAgICAgIChvbkNsaWNrKT1cInNpbXBsaWZ5UmVnaW9ucyhyZWdpb25BY3Rpb25zLnNpbXBsaWZ5VGhyZXNob2xkKVwiXG4gICAgPjwvcC1idXR0b24+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtZGlhbG9nPlxuXG48IS0tIFBhcmFtZXRlcnMgZm9yIHdoaWNoZXZlciBjb250cmlidXRlZCB0b29sIGlzIG9wZW4uIE9uZSBkaWFsb2cgZm9yIGFsbCBvZlxuICAgICB0aGVtOiBhIHRvb2wgZGVjbGFyZXMgaXRzIGZpZWxkcyAoVG9vbFBhcmFtU3BlYykgYW5kIHRoaXMgcmVuZGVycyB0aGVtLCBzb1xuICAgICB0aGUgdG9vbGJhciBjYW4gbGF5IGV2ZXJ5IHRvb2wgb3V0IHRoZSBzYW1lIHdheSBhbmQgYSBwbHVnaW4gbmVlZHMgbm9cbiAgICAgUHJpbWVORyBvZiBpdHMgb3duLiBEZWZhdWx0cyBjb21lIGZyb20gdGhlIGFjdGl2ZSBjaGVja3BvaW50LCBzaW5jZSB0aGV5XG4gICAgIGVuY29kZSB3aGF0IGl0IHdhcyB0cmFpbmVkIGZvci4gLS0+XG48cC1kaWFsb2dcbiAgKm5nSWY9XCJ0b29sUGFyYW1zLm9wZW5QYXJhbXMgYXMgb3BlblwiXG4gIFtoZWFkZXJdPVwib3Blbi50b29sLmxhYmVsICsgJyBwYXJhbWV0ZXJzJ1wiXG4gIFt2aXNpYmxlXT1cInRydWVcIlxuICAodmlzaWJsZUNoYW5nZSk9XCJjbG9zZVRvb2xQYXJhbXMoKVwiXG4gIFttb2RhbF09XCJ0cnVlXCJcbiAgW3N0eWxlXT1cInsgd2lkdGg6ICczMHJlbScgfVwiXG4gIFtkcmFnZ2FibGVdPVwiZmFsc2VcIlxuICBbcmVzaXphYmxlXT1cImZhbHNlXCJcbiAgYXBwZW5kVG89XCJib2R5XCJcbj5cbiAgPGRpdiBjbGFzcz1cImdyaWQgZm9ybWdyaWQgcC1mbHVpZFwiPlxuICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IGYgb2Ygb3Blbi5maWVsZHNcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJmaWVsZFwiIFtuZ0NsYXNzXT1cImYuc3BlYy50eXBlID09PSAnYm9vbGVhbicgPyAnY29sLTEyJyA6ICdjb2wtNidcIj5cbiAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImYubnVtYmVyIGFzIG5cIj5cbiAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJ0ZXh0LXNtXCIgW3BUb29sdGlwXT1cIm4udG9vbHRpcCB8fCAnJ1wiPnt7IG4ubGFiZWwgfX08L2xhYmVsPlxuICAgICAgICAgIDxwLWlucHV0TnVtYmVyXG4gICAgICAgICAgICBbbmdNb2RlbF09XCJvcGVuLnZhbHVlc1tuLmlkXVwiXG4gICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJvcGVuLnZhbHVlc1tuLmlkXSA9ICRldmVudFwiXG4gICAgICAgICAgICBbbWluXT1cIm4ubWluXCJcbiAgICAgICAgICAgIFttYXhdPVwibi5tYXhcIlxuICAgICAgICAgICAgW3N0ZXBdPVwibi5zdGVwXCJcbiAgICAgICAgICAgIFttaW5GcmFjdGlvbkRpZ2l0c109XCJuLmZyYWN0aW9uRGlnaXRzXCJcbiAgICAgICAgICAgIFtzaG93QnV0dG9uc109XCJ0cnVlXCJcbiAgICAgICAgICA+PC9wLWlucHV0TnVtYmVyPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiZi5zZWxlY3QgYXMgc2VsXCI+XG4gICAgICAgICAgPGxhYmVsIGNsYXNzPVwidGV4dC1zbVwiIFtwVG9vbHRpcF09XCJzZWwudG9vbHRpcCB8fCAnJ1wiPnt7IHNlbC5sYWJlbCB9fTwvbGFiZWw+XG4gICAgICAgICAgPHAtZHJvcGRvd25cbiAgICAgICAgICAgIFtvcHRpb25zXT1cInNlbC5vcHRpb25zXCJcbiAgICAgICAgICAgIG9wdGlvbkxhYmVsPVwibGFiZWxcIlxuICAgICAgICAgICAgb3B0aW9uVmFsdWU9XCJ2YWx1ZVwiXG4gICAgICAgICAgICBbbmdNb2RlbF09XCJvcGVuLnZhbHVlc1tzZWwuaWRdXCJcbiAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cIm9wZW4udmFsdWVzW3NlbC5pZF0gPSAkZXZlbnRcIlxuICAgICAgICAgICAgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgICA+PC9wLWRyb3Bkb3duPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgICA8IS0tIENoZWNrYm94IGxhYmVsIHNpdHMgYmVzaWRlIHRoZSBib3ggYW5kIG1heSBydW4gdG8gYSBzZW50ZW5jZSwgc29cbiAgICAgICAgICAgICBpdCBkb2VzIG5vdCBnZXQgdGhlIHN0YWNrZWQgbGFiZWwgdGhlIG90aGVyIHR5cGVzIHVzZS4gLS0+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJmLnNwZWMudHlwZSA9PT0gJ2Jvb2xlYW4nXCIgY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBnYXAtMlwiPlxuICAgICAgICAgIDxwLWNoZWNrYm94XG4gICAgICAgICAgICBbbmdNb2RlbF09XCJvcGVuLnZhbHVlc1tmLnNwZWMuaWRdXCJcbiAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cIm9wZW4udmFsdWVzW2Yuc3BlYy5pZF0gPSAkZXZlbnRcIlxuICAgICAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgICAgIFtpbnB1dElkXT1cIm9wZW4udG9vbC5pZCArICctJyArIGYuc3BlYy5pZFwiXG4gICAgICAgICAgPjwvcC1jaGVja2JveD5cbiAgICAgICAgICA8bGFiZWwgW2Zvcl09XCJvcGVuLnRvb2wuaWQgKyAnLScgKyBmLnNwZWMuaWRcIiBjbGFzcz1cInRleHQtc20gbWItMFwiIFtwVG9vbHRpcF09XCJmLnNwZWMudG9vbHRpcCB8fCAnJ1wiPnt7XG4gICAgICAgICAgICBmLnNwZWMubGFiZWxcbiAgICAgICAgICB9fTwvbGFiZWw+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9uZy1jb250YWluZXI+XG4gIDwvZGl2PlxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPHAtYnV0dG9uXG4gICAgICBsYWJlbD1cIlJlc2V0IHRvIG1vZGVsIGRlZmF1bHRzXCJcbiAgICAgIFt0ZXh0XT1cInRydWVcIlxuICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgIChvbkNsaWNrKT1cInJlc2V0VG9vbFBhcmFtcyhvcGVuLnRvb2wuaWQpXCJcbiAgICA+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gbGFiZWw9XCJDbG9zZVwiIHNpemU9XCJzbWFsbFwiIChvbkNsaWNrKT1cImNsb3NlVG9vbFBhcmFtcygpXCI+PC9wLWJ1dHRvbj5cbiAgPC9uZy10ZW1wbGF0ZT5cbjwvcC1kaWFsb2c+XG4iXX0=
|