@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
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
import { from, of } from 'rxjs';
|
|
2
|
+
import { saveAs } from 'file-saver';
|
|
3
|
+
import { LruCache, assembleVolume, bitmapToScalar, chooseStitchLevel, decodeImageRGBA, readLevel, } from 'napari-js';
|
|
4
|
+
import { buildTileUrl, exportTiffFilename, exportTiffUrl, fetchJsonWithAuth, fetchWithAuth, isAbortError, nativeHistogram, pollDescriptor, tilesInfoUrl, } from '../tile-server';
|
|
5
|
+
import { DESCRIPTOR_POLL_INTERVAL_MS, DESCRIPTOR_TIMEOUT_MS, MAX_STITCH_TILES, MAX_TEXTURE_DIM, RGB_BAND_CACHE_TILES, STITCH_BUDGET_COEFF, TILE_FETCH_CONCURRENCY, TILE_SIZE, VOLUME_FETCH_CONCURRENCY, VOLUME_MAX_SLICE, create2dCanvas, splitRgbBands, stackDepth, } from './napari-helpers';
|
|
6
|
+
/** Decode an `ImageBitmap` to a single-channel uint8 luminance plane with napari-js's
|
|
7
|
+
* `bitmapToScalar` (server bands are grey, R=G=B, and decode exactly; a colour composite
|
|
8
|
+
* becomes its BT.601 luminance). `maxSide` caps the longest side (downscaling on the canvas
|
|
9
|
+
* draw) — used to keep pre-loaded surface slice planes small. Closes the bitmap. */
|
|
10
|
+
export function bitmapToLuminance(bmp, maxSide) {
|
|
11
|
+
const plane = bitmapToScalar(bmp, { maxSide, weights: 'bt601' });
|
|
12
|
+
bmp.close?.();
|
|
13
|
+
return plane;
|
|
14
|
+
}
|
|
15
|
+
/** A colour tile's R, G and B bands (closes a bitmap tile). */
|
|
16
|
+
function chunkToBands(chunk) {
|
|
17
|
+
if (typeof ImageBitmap !== 'undefined' && chunk.data instanceof ImageBitmap) {
|
|
18
|
+
const bmp = chunk.data;
|
|
19
|
+
try {
|
|
20
|
+
return splitRgbBands(decodeImageRGBA(bmp));
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
bmp.close?.();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return splitRgbBands({ data: chunk.data, width: chunk.width, height: chunk.height });
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The napari-js backend's client of the jit-service tile server (review Appendix B, step 2), on
|
|
30
|
+
* the shared `tile-server/` protocol: the `/tiles/info` descriptor (polled, cached per source,
|
|
31
|
+
* shared between concurrent callers), whole-slice reads from the pyramid (napari-js `readLevel`), the pyramidal
|
|
32
|
+
* {@link TiledSource}s the 2D image draws, volume assembly, surface planes, the native `/histogram`
|
|
33
|
+
* and the `/export/tiff` download.
|
|
34
|
+
*
|
|
35
|
+
* The image is always passed in — the client never reads which image the service has loaded —
|
|
36
|
+
* so a descriptor can only ever describe the image a caller asked about (NAPARI-SVC-2).
|
|
37
|
+
*
|
|
38
|
+
* Scene-scoped state (the poll in flight, the sources that answered "no", the native histograms)
|
|
39
|
+
* lives until {@link startScene}; the descriptor cache itself survives scenes, keyed by source.
|
|
40
|
+
*/
|
|
41
|
+
export class NapariTileClient {
|
|
42
|
+
constructor(tiles, simpleStack, api) {
|
|
43
|
+
this.tiles = tiles;
|
|
44
|
+
this.simpleStack = simpleStack;
|
|
45
|
+
this.api = api;
|
|
46
|
+
/** Cached `/tiles/info` pyramid descriptor + the infoB64 it was fetched for. */
|
|
47
|
+
this.descriptor = null;
|
|
48
|
+
this.descriptorKey = null;
|
|
49
|
+
/** The poll in flight, shared by every caller asking for the same `infoB64`. */
|
|
50
|
+
this.descriptorPoll = null;
|
|
51
|
+
/** `infoB64`s this scene's poll got no descriptor for (non-202 status, empty body, timeout).
|
|
52
|
+
* Scene-scoped, so a re-plot retries a slow or flaky server once. */
|
|
53
|
+
this.descriptorMisses = new Set();
|
|
54
|
+
/** Native-bit-depth histograms from `/histogram`, keyed `${z}|${channel}` (>8-bit images). */
|
|
55
|
+
this.nativeHistograms = new Map();
|
|
56
|
+
/** The current scene's lifetime: a poll started under it ends when it is aborted. */
|
|
57
|
+
this.scene = new AbortController().signal;
|
|
58
|
+
}
|
|
59
|
+
/** Whether `info` is a self-contained stack (`tiled:false`: each slice its own file, no
|
|
60
|
+
* server pyramid). */
|
|
61
|
+
isSimple(info) {
|
|
62
|
+
return this.simpleStack.isSimple(info);
|
|
63
|
+
}
|
|
64
|
+
/** A new scene: forget the scene-scoped state and poll under `signal` from now on. */
|
|
65
|
+
startScene(signal) {
|
|
66
|
+
this.scene = signal;
|
|
67
|
+
this.descriptorPoll = null;
|
|
68
|
+
this.descriptorMisses.clear();
|
|
69
|
+
this.nativeHistograms.clear();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Fetch + cache the server pyramid descriptor (`GET /tiles/info`): the REAL per-level tile grid,
|
|
73
|
+
* tile size, channel metadata and physical pixel size. The backend returns 202 while the source
|
|
74
|
+
* is still caching, so we poll ({@link pollDescriptor}). Cached per `infoB64`; returns null if it
|
|
75
|
+
* never becomes ready, or the server will not describe the source (callers fall back to a
|
|
76
|
+
* single-tile fetch). Read it back through {@link currentDescriptor}. This is the authoritative
|
|
77
|
+
* grid — guessing level dims from `trueImageSize` overshoots the real grid and the server 400s
|
|
78
|
+
* out-of-range tiles.
|
|
79
|
+
*/
|
|
80
|
+
ensureDescriptor(info) {
|
|
81
|
+
// Self-contained multi-slice stack (no tile server — e.g. a numbered image
|
|
82
|
+
// series assembled client-side, each slice a different file): there is no
|
|
83
|
+
// single server-tiled pyramid to describe. Returning null routes every
|
|
84
|
+
// caller (2D image, volume, surface) to their stitched/single-fetch
|
|
85
|
+
// fallback, which — via fetchImage's own SimpleSliceAccessService branch —
|
|
86
|
+
// correctly fetches each slice's own URL instead of one fixed file's tile
|
|
87
|
+
// pyramid.
|
|
88
|
+
if (this.simpleStack.isSimple(info))
|
|
89
|
+
return Promise.resolve(null);
|
|
90
|
+
const infoB64 = this.tiles.getSelectedInfoB64();
|
|
91
|
+
if (!infoB64)
|
|
92
|
+
return Promise.resolve(null);
|
|
93
|
+
if (this.descriptor && this.descriptorKey === infoB64)
|
|
94
|
+
return Promise.resolve(this.descriptor);
|
|
95
|
+
// A source that answered "no" (or never answered) this scene is not asked again: the render,
|
|
96
|
+
// the stitch fallback and every slice would otherwise each wait out their own poll.
|
|
97
|
+
if (this.descriptorMisses.has(infoB64))
|
|
98
|
+
return Promise.resolve(null);
|
|
99
|
+
// Concurrent callers (render, navigator, histogram) share one poll.
|
|
100
|
+
if (this.descriptorPoll?.key === infoB64)
|
|
101
|
+
return this.descriptorPoll.promise;
|
|
102
|
+
const scene = this.scene;
|
|
103
|
+
const promise = this.pollDescriptor(infoB64, scene).then((desc) => {
|
|
104
|
+
if (this.descriptorPoll?.promise === promise)
|
|
105
|
+
this.descriptorPoll = null;
|
|
106
|
+
if (desc) {
|
|
107
|
+
this.descriptor = desc;
|
|
108
|
+
this.descriptorKey = infoB64;
|
|
109
|
+
}
|
|
110
|
+
else if (!scene.aborted) {
|
|
111
|
+
this.descriptorMisses.add(infoB64);
|
|
112
|
+
}
|
|
113
|
+
return desc;
|
|
114
|
+
});
|
|
115
|
+
this.descriptorPoll = { key: infoB64, promise };
|
|
116
|
+
return promise;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The `/tiles/info` loop behind {@link ensureDescriptor}: the shared jit-service poll
|
|
120
|
+
* (`tile-server/pollDescriptor`). Re-polls only on 202 (the server is still caching the source);
|
|
121
|
+
* any other status means this server will not describe it, so the answer is null at once. Each
|
|
122
|
+
* request gives up after its own timeout — a hung request would otherwise never reach the
|
|
123
|
+
* deadline check — and the whole poll ends (null) as soon as `scene` is aborted.
|
|
124
|
+
*/
|
|
125
|
+
async pollDescriptor(infoB64, scene) {
|
|
126
|
+
try {
|
|
127
|
+
return await pollDescriptor(fetchJsonWithAuth(this.tiles), tilesInfoUrl(this.api, infoB64), {
|
|
128
|
+
deadlineMs: DESCRIPTOR_TIMEOUT_MS,
|
|
129
|
+
intervalMs: DESCRIPTOR_POLL_INTERVAL_MS,
|
|
130
|
+
signal: scene,
|
|
131
|
+
tag: '[napari-js]',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
if (isAbortError(err))
|
|
136
|
+
return null; // the scene was reset
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The pyramid descriptor of `info`, or null when it has none.
|
|
142
|
+
*
|
|
143
|
+
* The cache is keyed by `infoB64` and survives image switches and failed polls, so reading it
|
|
144
|
+
* directly would hand a `tiled:false` stack (or an image whose poll never answered) the
|
|
145
|
+
* PREVIOUS image's size, µm/pixel, tile size and bit depth. Every read site goes through here.
|
|
146
|
+
*/
|
|
147
|
+
currentDescriptor(info) {
|
|
148
|
+
if (!this.descriptor || this.simpleStack.isSimple(info))
|
|
149
|
+
return null;
|
|
150
|
+
return this.descriptorKey === this.tiles.getSelectedInfoB64() ? this.descriptor : null;
|
|
151
|
+
}
|
|
152
|
+
/** µm per pixel along x for `info`: the descriptor's, else the image metadata's; 0 when
|
|
153
|
+
* neither declares it. */
|
|
154
|
+
mppX(info) {
|
|
155
|
+
return this.currentDescriptor(info)?.mppX || info?.imageMeta?.[0]?.mppX || 0;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Slice `z` as ONE image, for a source with no pyramid to read: a `tiled:false` stack's own
|
|
159
|
+
* file for `z`, else (no descriptor) the server's single top-left tile — correct for
|
|
160
|
+
* small/simple images.
|
|
161
|
+
*
|
|
162
|
+
* A self-contained multi-slice stack's `z` indexes a completely different file's own preview
|
|
163
|
+
* URL, not an internal slice of one server-tiled file, so it is resolved and fetched via
|
|
164
|
+
* SimpleSliceAccessService (shared with OSD; see its docs for why this can't be a bare fetch())
|
|
165
|
+
* rather than as a /tile?info=...&z= request against whichever single file
|
|
166
|
+
* getSelectedInfoB64() points at (which would 400/mismatch for any z beyond that one file's own
|
|
167
|
+
* extent). `channel` selects a band: a serverless multichannel stack's own plane
|
|
168
|
+
* (`channelUrls[z][channel]`, the client-side analog of the server's per-channel
|
|
169
|
+
* `/tile?channel=c`), else the server's.
|
|
170
|
+
*/
|
|
171
|
+
async fetchImage(info, z, channel) {
|
|
172
|
+
if (this.simpleStack.isSimple(info)) {
|
|
173
|
+
const chUrls = info.channelUrls;
|
|
174
|
+
const url = channel != null && chUrls?.[z]?.[channel] != null
|
|
175
|
+
? chUrls[z][channel]
|
|
176
|
+
: this.simpleStack.urlFor(info, z);
|
|
177
|
+
if (!url)
|
|
178
|
+
throw new Error(`[napari-js] no URL for slice ${z}`);
|
|
179
|
+
return this.simpleStack.fetchAsBitmap(url);
|
|
180
|
+
}
|
|
181
|
+
const infoB64 = this.tiles.getSelectedInfoB64();
|
|
182
|
+
if (!infoB64)
|
|
183
|
+
throw new Error('[napari-js] no selected image info (getSelectedInfoB64 null)');
|
|
184
|
+
const url = buildTileUrl(this.api, infoB64, { res: 0, col: 0, row: 0, z, tileSize: TILE_SIZE, channel });
|
|
185
|
+
const resp = await fetchWithAuth(this.tiles, url);
|
|
186
|
+
if (!resp.ok)
|
|
187
|
+
throw new Error(`[napari-js] slice fetch failed: ${resp.status} (0/0 res 0)`);
|
|
188
|
+
return createImageBitmap(await resp.blob());
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Slice `z` as a single-channel uint8 luminance plane. With a pyramid, napari-js's `readLevel`
|
|
192
|
+
* reads it from the same {@link tiledSource} the 2D view draws: the finest level whose tile grid
|
|
193
|
+
* fits `maxTiles` and whose longest side fits the GPU texture limit (no finer than `maxSide`
|
|
194
|
+
* needs, when given), stitched with bounded concurrency and area-averaged down to `maxSide`.
|
|
195
|
+
* Without one, the single image of {@link fetchImage}. `channel` selects a band (real levels
|
|
196
|
+
* only); omit it for the composite, whose overview levels let a large image read a small level.
|
|
197
|
+
* `compositeFallback`: see {@link planeSource}. `signal` cancels the read (it rejects).
|
|
198
|
+
*/
|
|
199
|
+
async readPlane(info, z, channel, opts) {
|
|
200
|
+
// A tiled:false stack has no pyramid: fetch its slice in this tick, as the 2D render does.
|
|
201
|
+
const desc = this.simpleStack.isSimple(info) ? null : await this.ensureDescriptor(info);
|
|
202
|
+
if (!desc?.levels?.length)
|
|
203
|
+
return bitmapToLuminance(await this.fetchImage(info, z, channel), opts.maxSide);
|
|
204
|
+
const budget = {
|
|
205
|
+
maxTiles: opts.maxTiles,
|
|
206
|
+
maxSide: opts.maxSide,
|
|
207
|
+
maxTextureDim: MAX_TEXTURE_DIM,
|
|
208
|
+
};
|
|
209
|
+
const source = opts.compositeFallback
|
|
210
|
+
? this.planeSource(desc, channel, budget)
|
|
211
|
+
: this.tiledSource(desc, channel, 1);
|
|
212
|
+
const img = await readLevel(source, z, {
|
|
213
|
+
...budget,
|
|
214
|
+
concurrency: TILE_FETCH_CONCURRENCY,
|
|
215
|
+
signal: opts.signal,
|
|
216
|
+
});
|
|
217
|
+
if (!img.fits && opts.maxTiles >= MAX_STITCH_TILES) {
|
|
218
|
+
console.warn(`[napari-js] no level of slice ${z} fits the ${opts.maxTiles}-tile/${MAX_TEXTURE_DIM}px budget; ` +
|
|
219
|
+
`read the coarsest (level ${img.level}) and downscaled it to ${img.width}×${img.height}.`);
|
|
220
|
+
}
|
|
221
|
+
return { data: img.data, width: img.width, height: img.height };
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* The scalar source for one decimated plane of band `channel`. A band has tiles only at the
|
|
225
|
+
* (few, large) real Bio-Formats levels; when none of them fits the budget — e.g. the coarsest
|
|
226
|
+
* real level is still 14982×18670, ~1100 full-res tiles — stitching it per slice floods the
|
|
227
|
+
* server (504s) and stalls the load. The composite pyramid has small overview levels, so the
|
|
228
|
+
* plane is read from the composite instead (its luminance). Only for a caller that wants one
|
|
229
|
+
* decimated plane (the surface height): for a multichannel VOLUME every channel would then read
|
|
230
|
+
* the same composite and the channels would collapse into one, so volumes keep each band.
|
|
231
|
+
*/
|
|
232
|
+
planeSource(desc, channel, budget) {
|
|
233
|
+
const band = this.tiledSource(desc, channel, 1);
|
|
234
|
+
if (channel == null || desc.levels.length <= (desc.realLevels ?? desc.levels.length))
|
|
235
|
+
return band;
|
|
236
|
+
const own = chooseStitchLevel(band, budget);
|
|
237
|
+
if (own.fits)
|
|
238
|
+
return band;
|
|
239
|
+
const composite = this.tiledSource(desc, undefined, 1);
|
|
240
|
+
const alt = chooseStitchLevel(composite, budget);
|
|
241
|
+
if (!alt.fits && alt.cols * alt.rows >= own.cols * own.rows)
|
|
242
|
+
return band;
|
|
243
|
+
console.warn(`[napari-js] channel ${channel} has no pyramid level within the ${budget.maxTiles}-tile ` +
|
|
244
|
+
`budget; using the composite overview (${alt.width}×${alt.height}) for this plane.`);
|
|
245
|
+
return composite;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* A small picture of the whole slice for the navigator: the composite (or band `channel`) at the
|
|
249
|
+
* finest level a single tile holds, else the coarsest level, as an RGBA canvas. Without a
|
|
250
|
+
* pyramid, the single image of {@link fetchImage}.
|
|
251
|
+
*/
|
|
252
|
+
async overviewImage(info, z, channel) {
|
|
253
|
+
const desc = this.simpleStack.isSimple(info) ? null : await this.ensureDescriptor(info);
|
|
254
|
+
if (!desc?.levels?.length)
|
|
255
|
+
return this.fetchImage(info, z, channel);
|
|
256
|
+
const img = await readLevel(this.tiledSource(desc, channel, 4), z, {
|
|
257
|
+
maxTiles: 1,
|
|
258
|
+
maxTextureDim: MAX_TEXTURE_DIM,
|
|
259
|
+
concurrency: TILE_FETCH_CONCURRENCY,
|
|
260
|
+
});
|
|
261
|
+
const { canvas, ctx } = create2dCanvas(img.width, img.height, 'overview');
|
|
262
|
+
const pixels = ctx.createImageData(img.width, img.height);
|
|
263
|
+
pixels.data.set(img.data);
|
|
264
|
+
ctx.putImageData(pixels, 0, 0);
|
|
265
|
+
return canvas;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Slice `z`'s R, G and B bands, as three uint8 planes: the composite read as RGBA at the
|
|
269
|
+
* finest level within `maxTiles` (as {@link readPlane} picks it; 1 for a cheap overview
|
|
270
|
+
* sample), else the single image of {@link fetchImage}. The raw bands of an RGB image, for the
|
|
271
|
+
* per-band layers and their histograms.
|
|
272
|
+
*/
|
|
273
|
+
async readRgbBands(info, z, maxTiles = MAX_STITCH_TILES) {
|
|
274
|
+
const desc = this.simpleStack.isSimple(info) ? null : await this.ensureDescriptor(info);
|
|
275
|
+
if (!desc?.levels?.length) {
|
|
276
|
+
const bmp = await this.fetchImage(info, z);
|
|
277
|
+
return chunkToBands({ width: bmp.width, height: bmp.height, data: bmp });
|
|
278
|
+
}
|
|
279
|
+
const img = await readLevel(this.tiledSource(desc, undefined, 4), z, {
|
|
280
|
+
maxTiles,
|
|
281
|
+
maxTextureDim: MAX_TEXTURE_DIM,
|
|
282
|
+
concurrency: TILE_FETCH_CONCURRENCY,
|
|
283
|
+
});
|
|
284
|
+
return splitRgbBands({ data: img.data, width: img.width, height: img.height });
|
|
285
|
+
}
|
|
286
|
+
/** Fetch slice `z` and read it back as a single-channel uint8 plane (see {@link readPlane}),
|
|
287
|
+
* within `budgetTiles`: the 2D stitch fallback reads at the full budget, the histogram samples
|
|
288
|
+
* at 1 for a cheap overview tile. */
|
|
289
|
+
async fetchChannelData(info, z, channel, budgetTiles = MAX_STITCH_TILES) {
|
|
290
|
+
return this.readPlane(info, z, channel, { maxTiles: budgetTiles });
|
|
291
|
+
}
|
|
292
|
+
/** Tile budget to stitch a whole slice at ~`targetPx` resolution from the pyramid. Shared by the
|
|
293
|
+
* surface plane fetch and the volume assembly, so both cap their reads by the decimate factor. */
|
|
294
|
+
tileBudgetFor(info, targetPx) {
|
|
295
|
+
const tileSize = this.currentDescriptor(info)?.tileSize || TILE_SIZE;
|
|
296
|
+
return Math.min(MAX_STITCH_TILES, Math.max(1, Math.round((targetPx / tileSize) ** 2 * STITCH_BUDGET_COEFF)));
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Build a pyramidal TiledSource backed by the server `/tile` endpoint. `channel` selects a band
|
|
300
|
+
* (grayscale luminance, real levels only); omit it for the composite (RGBA, all levels).
|
|
301
|
+
*
|
|
302
|
+
* For the 2D view, `scene` is the signal of the render that asked for it, taken before that
|
|
303
|
+
* render's awaits, and `onTile` is told of each tile request and handed back the tile's end. A
|
|
304
|
+
* request issued once `scene` is aborted (a disposed source still fetching after a reset) is not
|
|
305
|
+
* reported, and one that settles after it does not end, so it can never count toward the next
|
|
306
|
+
* scene. A tile request's own `signal` (napari-js aborts a read it no longer needs) aborts the
|
|
307
|
+
* HTTP request.
|
|
308
|
+
*/
|
|
309
|
+
tiledSource(desc, channel, channels, scene, onTile) {
|
|
310
|
+
const infoB64 = this.tiles.getSelectedInfoB64() ?? '';
|
|
311
|
+
// Per-channel tiles exist only at REAL Bio-Formats levels; the composite exists at all levels.
|
|
312
|
+
const usable = channel == null ? desc.levels : desc.levels.slice(0, Math.max(1, desc.realLevels ?? desc.levels.length));
|
|
313
|
+
const levelScales = usable.map((l) => desc.width / Math.max(1, l.width)); // level-0 px per level px
|
|
314
|
+
const tileSize = desc.tileSize || TILE_SIZE;
|
|
315
|
+
const api = this.api;
|
|
316
|
+
return {
|
|
317
|
+
kind: 'tiled',
|
|
318
|
+
width: desc.width,
|
|
319
|
+
height: desc.height,
|
|
320
|
+
tileSize,
|
|
321
|
+
levels: usable.length,
|
|
322
|
+
levelScales,
|
|
323
|
+
depth: Math.max(1, desc.z || 1),
|
|
324
|
+
channels,
|
|
325
|
+
dtype: 'uint8',
|
|
326
|
+
fetchTile: async (key, signal) => {
|
|
327
|
+
const res = usable[key.level]?.res ?? key.level;
|
|
328
|
+
const url = buildTileUrl(api, infoB64, {
|
|
329
|
+
res,
|
|
330
|
+
col: key.col,
|
|
331
|
+
row: key.row,
|
|
332
|
+
z: key.z,
|
|
333
|
+
tileSize,
|
|
334
|
+
channel,
|
|
335
|
+
});
|
|
336
|
+
const end = !onTile || scene?.aborted ? null : onTile();
|
|
337
|
+
try {
|
|
338
|
+
const resp = await fetchWithAuth(this.tiles, url, signal ? { signal } : {});
|
|
339
|
+
if (!resp.ok) {
|
|
340
|
+
throw new Error(`[napari-js] tile ${key.level}/${key.col}/${key.row} → ${resp.status}`);
|
|
341
|
+
}
|
|
342
|
+
const bmp = await createImageBitmap(await resp.blob());
|
|
343
|
+
if (channels === 4)
|
|
344
|
+
return { width: bmp.width, height: bmp.height, data: bmp };
|
|
345
|
+
return bitmapToLuminance(bmp);
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
if (!scene?.aborted)
|
|
349
|
+
end?.();
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* The composite pyramid's R, G and B bands as three scalar {@link TiledSource}s — one per
|
|
356
|
+
* additive layer, so each band takes its own window, gamma, invert, tint and visibility — that
|
|
357
|
+
* share ONE `/tile` request per tile. The three layers ask for a tile in the same frame: the
|
|
358
|
+
* first fetches the RGBA tile and splits it, the other two take their band from the same
|
|
359
|
+
* promise, and the split tile is dropped once all three have it. A band that asks later (one
|
|
360
|
+
* shown again after being hidden) finds it in a small LRU, else fetches it again. `scene` and
|
|
361
|
+
* `onTile` as {@link tiledSource}: the loading badge counts the one request.
|
|
362
|
+
*/
|
|
363
|
+
rgbBandSources(desc, scene, onTile) {
|
|
364
|
+
const composite = this.tiledSource(desc, undefined, 4, scene, onTile);
|
|
365
|
+
const split = new LruCache(RGB_BAND_CACHE_TILES);
|
|
366
|
+
const band = (key, b) => {
|
|
367
|
+
const id = `${key.z}/${key.level}/${key.col}/${key.row}`;
|
|
368
|
+
let entry = split.get(id);
|
|
369
|
+
if (!entry) {
|
|
370
|
+
const bands = composite.fetchTile(key).then(chunkToBands);
|
|
371
|
+
const fresh = { bands, taken: new Set() };
|
|
372
|
+
// A failed tile is not remembered: the next ask fetches it again.
|
|
373
|
+
bands.catch(() => split.get(id) === fresh && split.delete(id));
|
|
374
|
+
split.set(id, (entry = fresh));
|
|
375
|
+
}
|
|
376
|
+
entry.taken.add(b);
|
|
377
|
+
if (entry.taken.size === 3)
|
|
378
|
+
split.delete(id);
|
|
379
|
+
return entry.bands.then((planes) => planes[b]);
|
|
380
|
+
};
|
|
381
|
+
return [0, 1, 2].map((b) => ({
|
|
382
|
+
...composite,
|
|
383
|
+
channels: 1,
|
|
384
|
+
fetchTile: (key) => band(key, b),
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Assemble a downsampled uint8 volume (luminance) of `info`'s stack with napari-js's
|
|
389
|
+
* `assembleVolume`: every `sliceStep`th slice, read from the band's (or composite's) pyramid at
|
|
390
|
+
* the level that `maxSlice` needs, within its tile budget, and area-averaged to `maxSlice` on
|
|
391
|
+
* the longest side. Slices are read with bounded concurrency (the connection pool stays full
|
|
392
|
+
* without flooding it on a deep stack), each reporting `progress` (0–100) as it lands so the host
|
|
393
|
+
* shows a determinate progress bar. Null when `signal` aborts (a Cancel or a new plot), which
|
|
394
|
+
* also aborts the tile requests in flight — a partial volume is never returned.
|
|
395
|
+
*/
|
|
396
|
+
async assembleVolume(info, opts, channel, hooks) {
|
|
397
|
+
const { signal } = hooks;
|
|
398
|
+
const fullDepth = stackDepth(info) || 1;
|
|
399
|
+
const step = Math.max(1, Math.floor(opts.sliceStep ?? 1));
|
|
400
|
+
const maxSlice = opts.maxSlice ?? VOLUME_MAX_SLICE;
|
|
401
|
+
// Source-slice indices sampled into the volume (every `step`th plane → low-res is faster).
|
|
402
|
+
const zIndices = [];
|
|
403
|
+
for (let z = 0; z < fullDepth; z += step)
|
|
404
|
+
zIndices.push(z);
|
|
405
|
+
hooks.progress(0);
|
|
406
|
+
try {
|
|
407
|
+
// `channel` selects a band (multichannel volume); omit for the grayscale composite. The
|
|
408
|
+
// caller owns the stackLoading flag (multichannel assembles channels in turn).
|
|
409
|
+
const source = await this.volumeSource(info, channel, zIndices[0]);
|
|
410
|
+
return await assembleVolume(source, zIndices, {
|
|
411
|
+
maxSide: maxSlice,
|
|
412
|
+
maxTiles: this.tileBudgetFor(info, maxSlice),
|
|
413
|
+
maxTextureDim: MAX_TEXTURE_DIM,
|
|
414
|
+
concurrency: VOLUME_FETCH_CONCURRENCY,
|
|
415
|
+
tileConcurrency: TILE_FETCH_CONCURRENCY,
|
|
416
|
+
onProgress: (done, total) => hooks.progress(Math.round((done / total) * 100)),
|
|
417
|
+
signal,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
catch (err) {
|
|
421
|
+
if (signal.aborted)
|
|
422
|
+
return null; // cancelled → don't render a partial volume
|
|
423
|
+
throw err;
|
|
424
|
+
}
|
|
425
|
+
finally {
|
|
426
|
+
hooks.progress(0);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* The scalar source a volume reads: the band's (or composite's) pyramid; without one, one whole
|
|
431
|
+
* image per slice ({@link fetchImage}) as a single-tile level sized by the first slice, which is
|
|
432
|
+
* fetched here and served again as that slice's tile.
|
|
433
|
+
*/
|
|
434
|
+
async volumeSource(info, channel, firstZ) {
|
|
435
|
+
const desc = this.simpleStack.isSimple(info) ? null : await this.ensureDescriptor(info);
|
|
436
|
+
if (desc?.levels?.length)
|
|
437
|
+
return this.tiledSource(desc, channel, 1);
|
|
438
|
+
let first = await this.fetchImage(info, firstZ, channel);
|
|
439
|
+
const source = {
|
|
440
|
+
kind: 'tiled',
|
|
441
|
+
width: first.width,
|
|
442
|
+
height: first.height,
|
|
443
|
+
tileSize: Math.max(1, first.width, first.height),
|
|
444
|
+
levels: 1,
|
|
445
|
+
depth: stackDepth(info) || 1,
|
|
446
|
+
channels: 1,
|
|
447
|
+
dtype: 'uint8',
|
|
448
|
+
fetchTile: async (key) => {
|
|
449
|
+
let bmp;
|
|
450
|
+
if (first && key.z === firstZ) {
|
|
451
|
+
bmp = first;
|
|
452
|
+
first = null;
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
bmp = await this.fetchImage(info, key.z, channel);
|
|
456
|
+
}
|
|
457
|
+
return { width: bmp.width, height: bmp.height, data: bmp };
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
return source;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Slice `z` as a single WHOLE-image luminance plane (decimated to `maxGrid`), for the surface.
|
|
464
|
+
* Prefers the server pyramid, read at the level the grid needs ({@link readPlane}, which may
|
|
465
|
+
* drop a band to the composite overview); without a descriptor, the app's complete per-slice
|
|
466
|
+
* image (`urls`/`smallUrls`, as the Plotly surface) — never a lone top-left tile, which would be
|
|
467
|
+
* a corner of the slice — and only as a last resort the single-tile fetch.
|
|
468
|
+
*/
|
|
469
|
+
async fetchPlane(info, z, channel, maxGrid) {
|
|
470
|
+
const desc = await this.ensureDescriptor(info);
|
|
471
|
+
// Preferred: the WHOLE slice from the server pyramid at a resolution driven by the decimate
|
|
472
|
+
// factor — a higher target grid reads a FINER pyramid level (more real detail). This is what
|
|
473
|
+
// makes "Full" actually higher-res than "½", not just a fixed preview. The surface is a single
|
|
474
|
+
// decimated plane, so the composite fallback is acceptable when the band has no small level.
|
|
475
|
+
if (desc?.levels?.length) {
|
|
476
|
+
return this.readPlane(info, z, channel, {
|
|
477
|
+
maxTiles: this.tileBudgetFor(info, maxGrid),
|
|
478
|
+
maxSide: maxGrid,
|
|
479
|
+
compositeFallback: true,
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
// Fallback (no pyramid): the app's COMPLETE per-slice image (urls[z], not the small blurry
|
|
483
|
+
// thumbnail) — whole slice, avoids a corner tile. Capped at the image's own resolution.
|
|
484
|
+
const url = info?.urls?.[z] ?? info?.smallUrls?.[z];
|
|
485
|
+
if (url) {
|
|
486
|
+
try {
|
|
487
|
+
const resp = await fetchWithAuth(this.tiles, url);
|
|
488
|
+
if (resp.ok)
|
|
489
|
+
return bitmapToLuminance(await createImageBitmap(await resp.blob()), maxGrid);
|
|
490
|
+
}
|
|
491
|
+
catch (err) {
|
|
492
|
+
console.warn(`[napari-js] surface url fetch failed for z=${z}`, err);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// Last resort (no pyramid and no complete image): the single image.
|
|
496
|
+
return bitmapToLuminance(await this.fetchImage(info, z, channel), maxGrid);
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Channel `channel`'s native-bit-depth histogram at slice `z` when `info` is a >8-bit image —
|
|
500
|
+
* the displayed pixels are 8-bit, so a client histogram would be clipped — cached per
|
|
501
|
+
* `(z, channel)`; null for an 8-bit channel, which the caller bins from the client data.
|
|
502
|
+
*/
|
|
503
|
+
nativeHistogram$(info, z, channel, bins) {
|
|
504
|
+
const bitDepth = this.currentDescriptor(info)?.channelInfo?.[channel]?.bitDepth ?? 8;
|
|
505
|
+
if (bitDepth <= 8)
|
|
506
|
+
return null;
|
|
507
|
+
const key = `${z}|${channel}`;
|
|
508
|
+
const cached = this.nativeHistograms.get(key);
|
|
509
|
+
if (cached)
|
|
510
|
+
return of(cached);
|
|
511
|
+
return from(this.fetchNativeHistogram(channel, bins, z, key));
|
|
512
|
+
}
|
|
513
|
+
/** Fetch + cache one channel's native-bit-depth histogram from `GET /histogram` (>8-bit),
|
|
514
|
+
* through the shared jit-service client — whose URL carries the per-app-load cache-buster
|
|
515
|
+
* this backend used to omit, so a reload showed the server's 24 h-cached histogram. */
|
|
516
|
+
async fetchNativeHistogram(channel, bins, z, key) {
|
|
517
|
+
const infoB64 = this.tiles.getSelectedInfoB64();
|
|
518
|
+
if (!infoB64)
|
|
519
|
+
return null;
|
|
520
|
+
try {
|
|
521
|
+
const out = await nativeHistogram(fetchJsonWithAuth(this.tiles), this.api, infoB64, {
|
|
522
|
+
z,
|
|
523
|
+
channel,
|
|
524
|
+
bins,
|
|
525
|
+
});
|
|
526
|
+
if (!out)
|
|
527
|
+
return null; // 202 (still caching) or transient → null; the pane retries
|
|
528
|
+
this.nativeHistograms.set(key, out);
|
|
529
|
+
return out;
|
|
530
|
+
}
|
|
531
|
+
catch (err) {
|
|
532
|
+
console.warn('[napari-js] native histogram fetch failed', err);
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
/** Native-bit-depth (16/32-bit) multi-band TIFF export of slice `z` via the server
|
|
537
|
+
* `/export/tiff` endpoint — the displayed PNG is an 8-bit figure, this preserves the true pixel
|
|
538
|
+
* values. Visible channels only (omitted when all are visible → server default). Mirrors the
|
|
539
|
+
* OSD backend. */
|
|
540
|
+
async exportTiff(z, states, fileName) {
|
|
541
|
+
const infoB64 = this.tiles.getSelectedInfoB64();
|
|
542
|
+
if (!infoB64)
|
|
543
|
+
return;
|
|
544
|
+
const visible = states.filter((c) => c.visible).map((c) => c.index);
|
|
545
|
+
const url = exportTiffUrl(this.api, infoB64, z, visible, states.length);
|
|
546
|
+
const saveName = exportTiffFilename(fileName);
|
|
547
|
+
try {
|
|
548
|
+
const resp = await fetchWithAuth(this.tiles, url);
|
|
549
|
+
if (resp.status === 202) {
|
|
550
|
+
console.warn('[napari-js] 16-bit export: file still caching — try again shortly.');
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (!resp.ok)
|
|
554
|
+
throw new Error(`status ${resp.status}`);
|
|
555
|
+
saveAs(await resp.blob(), saveName);
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
console.warn('[napari-js] 16-bit TIFF export failed', err);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmFwYXJpLXRpbGUtY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9pbXBsZW1lbnRhdGlvbnMvbmFwYXJpLWpzL25hcGFyaS10aWxlLWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQWMsSUFBSSxFQUFFLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUM1QyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQ3BDLE9BQU8sRUFDTCxRQUFRLEVBQ1IsY0FBYyxFQUNkLGNBQWMsRUFDZCxpQkFBaUIsRUFDakIsZUFBZSxFQUNmLFNBQVMsR0FDVixNQUFNLFdBQVcsQ0FBQztBQU9uQixPQUFPLEVBRUwsWUFBWSxFQUNaLGtCQUFrQixFQUNsQixhQUFhLEVBQ2IsaUJBQWlCLEVBQ2pCLGFBQWEsRUFDYixZQUFZLEVBQ1osZUFBZSxFQUNmLGNBQWMsRUFDZCxZQUFZLEdBQ2IsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQ0wsMkJBQTJCLEVBQzNCLHFCQUFxQixFQUVyQixnQkFBZ0IsRUFDaEIsZUFBZSxFQUNmLG9CQUFvQixFQUNwQixtQkFBbUIsRUFDbkIsc0JBQXNCLEVBQ3RCLFNBQVMsRUFDVCx3QkFBd0IsRUFDeEIsZ0JBQWdCLEVBQ2hCLGNBQWMsRUFDZCxhQUFhLEVBQ2IsVUFBVSxHQUNYLE1BQU0sa0JBQWtCLENBQUM7QUFhMUI7OztxRkFHcUY7QUFDckYsTUFBTSxVQUFVLGlCQUFpQixDQUFDLEdBQWdCLEVBQUUsT0FBZ0I7SUFDbEUsTUFBTSxLQUFLLEdBQUcsY0FBYyxDQUFDLEdBQUcsRUFBRSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNqRSxHQUFHLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQztJQUNkLE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQztBQUVELCtEQUErRDtBQUMvRCxTQUFTLFlBQVksQ0FBQyxLQUFpQjtJQUNyQyxJQUFJLE9BQU8sV0FBVyxLQUFLLFdBQVcsSUFBSSxLQUFLLENBQUMsSUFBSSxZQUFZLFdBQVcsRUFBRSxDQUFDO1FBQzVFLE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUM7UUFDdkIsSUFBSSxDQUFDO1lBQ0gsT0FBTyxhQUFhLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDN0MsQ0FBQztnQkFBUyxDQUFDO1lBQ1QsR0FBRyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDaEIsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLGFBQWEsQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsSUFBa0IsRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7QUFDckcsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILE1BQU0sT0FBTyxnQkFBZ0I7SUFjM0IsWUFDbUIsS0FBcUIsRUFDckIsV0FBcUMsRUFDckMsR0FBVztRQUZYLFVBQUssR0FBTCxLQUFLLENBQWdCO1FBQ3JCLGdCQUFXLEdBQVgsV0FBVyxDQUEwQjtRQUNyQyxRQUFHLEdBQUgsR0FBRyxDQUFRO1FBaEI5QixnRkFBZ0Y7UUFDeEUsZUFBVSxHQUEwQixJQUFJLENBQUM7UUFDekMsa0JBQWEsR0FBa0IsSUFBSSxDQUFDO1FBQzVDLGdGQUFnRjtRQUN4RSxtQkFBYyxHQUFvRSxJQUFJLENBQUM7UUFDL0Y7OEVBQ3NFO1FBQ3JELHFCQUFnQixHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7UUFDdEQsOEZBQThGO1FBQzdFLHFCQUFnQixHQUFHLElBQUksR0FBRyxFQUFzQixDQUFDO1FBQ2xFLHFGQUFxRjtRQUM3RSxVQUFLLEdBQWdCLElBQUksZUFBZSxFQUFFLENBQUMsTUFBTSxDQUFDO0lBTXZELENBQUM7SUFFSjsyQkFDdUI7SUFDdkIsUUFBUSxDQUFDLElBQTRCO1FBQ25DLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVELHNGQUFzRjtJQUN0RixVQUFVLENBQUMsTUFBbUI7UUFDNUIsSUFBSSxDQUFDLEtBQUssR0FBRyxNQUFNLENBQUM7UUFDcEIsSUFBSSxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUM7UUFDM0IsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEtBQUssRUFBRSxDQUFDO1FBQzlCLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNoQyxDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCxnQkFBZ0IsQ0FBQyxJQUE0QjtRQUMzQywyRUFBMkU7UUFDM0UsMEVBQTBFO1FBQzFFLHVFQUF1RTtRQUN2RSxvRUFBb0U7UUFDcEUsMkVBQTJFO1FBQzNFLDBFQUEwRTtRQUMxRSxXQUFXO1FBQ1gsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFBRSxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbEUsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQ2hELElBQUksQ0FBQyxPQUFPO1lBQUUsT0FBTyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzNDLElBQUksSUFBSSxDQUFDLFVBQVUsSUFBSSxJQUFJLENBQUMsYUFBYSxLQUFLLE9BQU87WUFBRSxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQy9GLDZGQUE2RjtRQUM3RixvRkFBb0Y7UUFDcEYsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQztZQUFFLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNyRSxvRUFBb0U7UUFDcEUsSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFLEdBQUcsS0FBSyxPQUFPO1lBQUUsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQztRQUM3RSxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDO1FBQ3pCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFO1lBQ2hFLElBQUksSUFBSSxDQUFDLGNBQWMsRUFBRSxPQUFPLEtBQUssT0FBTztnQkFBRSxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztZQUN6RSxJQUFJLElBQUksRUFBRSxDQUFDO2dCQUNULElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDO2dCQUN2QixJQUFJLENBQUMsYUFBYSxHQUFHLE9BQU8sQ0FBQztZQUMvQixDQUFDO2lCQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQzFCLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDckMsQ0FBQztZQUNELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQyxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsY0FBYyxHQUFHLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQztRQUNoRCxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ssS0FBSyxDQUFDLGNBQWMsQ0FBQyxPQUFlLEVBQUUsS0FBa0I7UUFDOUQsSUFBSSxDQUFDO1lBQ0gsT0FBTyxNQUFNLGNBQWMsQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLEVBQUU7Z0JBQzFGLFVBQVUsRUFBRSxxQkFBcUI7Z0JBQ2pDLFVBQVUsRUFBRSwyQkFBMkI7Z0JBQ3ZDLE1BQU0sRUFBRSxLQUFLO2dCQUNiLEdBQUcsRUFBRSxhQUFhO2FBQ25CLENBQUMsQ0FBQztRQUNMLENBQUM7UUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1lBQ2IsSUFBSSxZQUFZLENBQUMsR0FBRyxDQUFDO2dCQUFFLE9BQU8sSUFBSSxDQUFDLENBQUMsc0JBQXNCO1lBQzFELE1BQU0sR0FBRyxDQUFDO1FBQ1osQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxpQkFBaUIsQ0FBQyxJQUE0QjtRQUM1QyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFBRSxPQUFPLElBQUksQ0FBQztRQUNyRSxPQUFPLElBQUksQ0FBQyxhQUFhLEtBQUssSUFBSSxDQUFDLEtBQUssQ0FBQyxrQkFBa0IsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDekYsQ0FBQztJQUVEOytCQUMyQjtJQUMzQixJQUFJLENBQUMsSUFBNEI7UUFDL0IsT0FBTyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEVBQUUsSUFBSSxJQUFJLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxJQUFJLElBQUksQ0FBQyxDQUFDO0lBQy9FLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7OztPQWFHO0lBQ0gsS0FBSyxDQUFDLFVBQVUsQ0FBQyxJQUE0QixFQUFFLENBQVMsRUFBRSxPQUFnQjtRQUN4RSxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDcEMsTUFBTSxNQUFNLEdBQUksSUFBbUIsQ0FBQyxXQUFXLENBQUM7WUFDaEQsTUFBTSxHQUFHLEdBQ1AsT0FBTyxJQUFJLElBQUksSUFBSSxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLElBQUk7Z0JBQy9DLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDO2dCQUNwQixDQUFDLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsSUFBa0IsRUFBRSxDQUFDLENBQUMsQ0FBQztZQUNyRCxJQUFJLENBQUMsR0FBRztnQkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGdDQUFnQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQy9ELE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDN0MsQ0FBQztRQUNELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUNoRCxJQUFJLENBQUMsT0FBTztZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsOERBQThELENBQUMsQ0FBQztRQUM5RixNQUFNLEdBQUcsR0FBRyxZQUFZLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxPQUFPLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQ3pHLE1BQU0sSUFBSSxHQUFHLE1BQU0sYUFBYSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDbEQsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxtQ0FBbUMsSUFBSSxDQUFDLE1BQU0sY0FBYyxDQUFDLENBQUM7UUFDNUYsT0FBTyxpQkFBaUIsQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNILEtBQUssQ0FBQyxTQUFTLENBQ2IsSUFBNEIsRUFDNUIsQ0FBUyxFQUNULE9BQTJCLEVBQzNCLElBQStGO1FBRS9GLDJGQUEyRjtRQUMzRixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN4RixJQUFJLENBQUMsSUFBSSxFQUFFLE1BQU0sRUFBRSxNQUFNO1lBQUUsT0FBTyxpQkFBaUIsQ0FBQyxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLENBQUMsRUFBRSxPQUFPLENBQUMsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDM0csTUFBTSxNQUFNLEdBQWlCO1lBQzNCLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUTtZQUN2QixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU87WUFDckIsYUFBYSxFQUFFLGVBQWU7U0FDL0IsQ0FBQztRQUNGLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxpQkFBaUI7WUFDbkMsQ0FBQyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxNQUFNLENBQUM7WUFDekMsQ0FBQyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUN2QyxNQUFNLEdBQUcsR0FBRyxNQUFNLFNBQVMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFO1lBQ3JDLEdBQUcsTUFBTTtZQUNULFdBQVcsRUFBRSxzQkFBc0I7WUFDbkMsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNO1NBQ3BCLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxRQUFRLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztZQUNuRCxPQUFPLENBQUMsSUFBSSxDQUNWLGlDQUFpQyxDQUFDLGFBQWEsSUFBSSxDQUFDLFFBQVEsU0FBUyxlQUFlLGFBQWE7Z0JBQy9GLDRCQUE0QixHQUFHLENBQUMsS0FBSywwQkFBMEIsR0FBRyxDQUFDLEtBQUssSUFBSSxHQUFHLENBQUMsTUFBTSxHQUFHLENBQzVGLENBQUM7UUFDSixDQUFDO1FBQ0QsT0FBTyxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsSUFBa0IsRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU0sRUFBRSxDQUFDO0lBQ2hGLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNLLFdBQVcsQ0FBQyxJQUFvQixFQUFFLE9BQTJCLEVBQUUsTUFBb0I7UUFDekYsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ2hELElBQUksT0FBTyxJQUFJLElBQUksSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUM7WUFBRSxPQUFPLElBQUksQ0FBQztRQUNsRyxNQUFNLEdBQUcsR0FBRyxpQkFBaUIsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDNUMsSUFBSSxHQUFHLENBQUMsSUFBSTtZQUFFLE9BQU8sSUFBSSxDQUFDO1FBQzFCLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUN2RCxNQUFNLEdBQUcsR0FBRyxpQkFBaUIsQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDakQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLElBQUksR0FBRyxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsSUFBSSxJQUFJLEdBQUcsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDLElBQUk7WUFBRSxPQUFPLElBQUksQ0FBQztRQUN6RSxPQUFPLENBQUMsSUFBSSxDQUNWLHVCQUF1QixPQUFPLG9DQUFvQyxNQUFNLENBQUMsUUFBUSxRQUFRO1lBQ3ZGLHlDQUF5QyxHQUFHLENBQUMsS0FBSyxJQUFJLEdBQUcsQ0FBQyxNQUFNLG1CQUFtQixDQUN0RixDQUFDO1FBQ0YsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxLQUFLLENBQUMsYUFBYSxDQUFDLElBQTRCLEVBQUUsQ0FBUyxFQUFFLE9BQWdCO1FBQzNFLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3hGLElBQUksQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU07WUFBRSxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNwRSxNQUFNLEdBQUcsR0FBRyxNQUFNLFNBQVMsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFO1lBQ2pFLFFBQVEsRUFBRSxDQUFDO1lBQ1gsYUFBYSxFQUFFLGVBQWU7WUFDOUIsV0FBVyxFQUFFLHNCQUFzQjtTQUNwQyxDQUFDLENBQUM7UUFDSCxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxNQUFNLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDMUUsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMxRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBa0IsQ0FBQyxDQUFDO1FBQ3hDLEdBQUcsQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUMvQixPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxLQUFLLENBQUMsWUFBWSxDQUNoQixJQUE0QixFQUM1QixDQUFTLEVBQ1QsV0FBbUIsZ0JBQWdCO1FBRW5DLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3hGLElBQUksQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFDO1lBQzFCLE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDM0MsT0FBTyxZQUFZLENBQUMsRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUMzRSxDQUFDO1FBQ0QsTUFBTSxHQUFHLEdBQUcsTUFBTSxTQUFTLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRTtZQUNuRSxRQUFRO1lBQ1IsYUFBYSxFQUFFLGVBQWU7WUFDOUIsV0FBVyxFQUFFLHNCQUFzQjtTQUNwQyxDQUFDLENBQUM7UUFDSCxPQUFPLGFBQWEsQ0FBQyxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsSUFBa0IsRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDL0YsQ0FBQztJQUVEOzswQ0FFc0M7SUFDdEMsS0FBSyxDQUFDLGdCQUFnQixDQUNwQixJQUE0QixFQUM1QixDQUFTLEVBQ1QsT0FBZ0IsRUFDaEIsY0FBc0IsZ0JBQWdCO1FBRXRDLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFFRDt1R0FDbUc7SUFDbkcsYUFBYSxDQUFDLElBQTRCLEVBQUUsUUFBZ0I7UUFDMUQsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsSUFBSSxTQUFTLENBQUM7UUFDckUsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLGdCQUFnQixFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxHQUFHLG1CQUFtQixDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQy9HLENBQUM7SUFFRDs7Ozs7Ozs7OztPQVVHO0lBQ0gsV0FBVyxDQUNULElBQW9CLEVBQ3BCLE9BQTJCLEVBQzNCLFFBQWUsRUFDZixLQUFtQixFQUNuQixNQUF5QjtRQUV6QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGtCQUFrQixFQUFFLElBQUksRUFBRSxDQUFDO1FBQ3RELCtGQUErRjtRQUMvRixNQUFNLE1BQU0sR0FDVixPQUFPLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLFVBQVUsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFDM0csTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLDBCQUEwQjtRQUNwRyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsUUFBUSxJQUFJLFNBQVMsQ0FBQztRQUM1QyxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO1FBQ3JCLE9BQU87WUFDTCxJQUFJLEVBQUUsT0FBTztZQUNiLEtBQUssRUFBRSxJQUFJLENBQUMsS0FBSztZQUNqQixNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07WUFDbkIsUUFBUTtZQUNSLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTTtZQUNyQixXQUFXO1lBQ1gsS0FBSyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQy9CLFFBQVE7WUFDUixLQUFLLEVBQUUsT0FBTztZQUNkLFNBQVMsRUFBRSxLQUFLLEVBQUUsR0FBWSxFQUFFLE1BQW9CLEVBQXVCLEVBQUU7Z0JBQzNFLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUM7Z0JBQ2hELE1BQU0sR0FBRyxHQUFHLFlBQVksQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFO29CQUNyQyxHQUFHO29CQUNILEdBQUcsRUFBRSxHQUFHLENBQUMsR0FBRztvQkFDWixHQUFHLEVBQUUsR0FBRyxDQUFDLEdBQUc7b0JBQ1osQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO29CQUNSLFFBQVE7b0JBQ1IsT0FBTztpQkFDUixDQUFDLENBQUM7Z0JBQ0gsTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLElBQUksS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQztnQkFDeEQsSUFBSSxDQUFDO29CQUNILE1BQU0sSUFBSSxHQUFHLE1BQU0sYUFBYSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7b0JBQzVFLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUM7d0JBQ2IsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQkFBb0IsR0FBRyxDQUFDLEtBQUssSUFBSSxHQUFHLENBQUMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7b0JBQzFGLENBQUM7b0JBQ0QsTUFBTSxHQUFHLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO29CQUN2RCxJQUFJLFFBQVEsS0FBSyxDQUFDO3dCQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLENBQUM7b0JBQy9FLE9BQU8saUJBQWlCLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2hDLENBQUM7d0JBQVMsQ0FBQztvQkFDVCxJQUFJLENBQUMsS0FBSyxFQUFFLE9BQU87d0JBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztnQkFDL0IsQ0FBQztZQUNILENBQUM7U0FDRixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0gsY0FBYyxDQUFDLElBQW9CLEVBQUUsS0FBbUIsRUFBRSxNQUF5QjtRQUNqRixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQztRQUN0RSxNQUFNLEtBQUssR0FBRyxJQUFJLFFBQVEsQ0FBc0Qsb0JBQW9CLENBQUMsQ0FBQztRQUN0RyxNQUFNLElBQUksR0FBRyxDQUFDLEdBQVksRUFBRSxDQUFTLEVBQXVCLEVBQUU7WUFDNUQsTUFBTSxFQUFFLEdBQUcsR0FBRyxHQUFHLENBQUMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxLQUFLLElBQUksR0FBRyxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDekQsSUFBSSxLQUFLLEdBQUcsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUMxQixJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQ1gsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7Z0JBQzFELE1BQU0sS0FBSyxHQUFHLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxJQUFJLEdBQUcsRUFBVSxFQUFFLENBQUM7Z0JBQ2xELGtFQUFrRTtnQkFDbEUsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEtBQUssSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQy9ELEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUM7WUFDakMsQ0FBQztZQUNELEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ25CLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQztnQkFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQzdDLE9BQU8sS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2pELENBQUMsQ0FBQztRQUNGLE9BQU8sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBZSxFQUFFLENBQUMsQ0FBQztZQUN4QyxHQUFHLFNBQVM7WUFDWixRQUFRLEVBQUUsQ0FBQztZQUNYLFNBQVMsRUFBRSxDQUFDLEdBQVksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7U0FDMUMsQ0FBQyxDQUFDLENBQUM7SUFDTixDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCxLQUFLLENBQUMsY0FBYyxDQUNsQixJQUE0QixFQUM1QixJQUErQyxFQUMvQyxPQUEyQixFQUMzQixLQUErRDtRQUUvRCxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsS0FBSyxDQUFDO1FBQ3pCLE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDMUQsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsSUFBSSxnQkFBZ0IsQ0FBQztRQUNuRCwyRkFBMkY7UUFDM0YsTUFBTSxRQUFRLEdBQWEsRUFBRSxDQUFDO1FBQzlCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxTQUFTLEVBQUUsQ0FBQyxJQUFJLElBQUk7WUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRTNELEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbEIsSUFBSSxDQUFDO1lBQ0gsd0ZBQXdGO1lBQ3hGLCtFQUErRTtZQUMvRSxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNuRSxPQUFPLE1BQU0sY0FBYyxDQUFDLE1BQU0sRUFBRSxRQUFRLEVBQUU7Z0JBQzVDLE9BQU8sRUFBRSxRQUFRO2dCQUNqQixRQUFRLEVBQUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDO2dCQUM1QyxhQUFhLEVBQUUsZUFBZTtnQkFDOUIsV0FBVyxFQUFFLHdCQUF3QjtnQkFDckMsZUFBZSxFQUFFLHNCQUFzQjtnQkFDdkMsVUFBVSxFQUFFLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxHQUFHLEtBQUssQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNO2FBQ1AsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixJQUFJLE1BQU0sQ0FBQyxPQUFPO2dCQUFFLE9BQU8sSUFBSSxDQUFDLENBQUMsNENBQTRDO1lBQzdFLE1BQU0sR0FBRyxDQUFDO1FBQ1osQ0FBQztnQkFBUyxDQUFDO1lBQ1QsS0FBSyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNwQixDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxLQUFLLENBQUMsWUFBWSxDQUFDLElBQTRCLEVBQUUsT0FBMkIsRUFBRSxNQUFjO1FBQ2xHLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3hGLElBQUksSUFBSSxFQUFFLE1BQU0sRUFBRSxNQUFNO1lBQUUsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDcEUsSUFBSSxLQUFLLEdBQXVCLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzdFLE1BQU0sTUFBTSxHQUFnQjtZQUMxQixJQUFJLEVBQUUsT0FBTztZQUNiLEtBQUssRUFBRSxLQUFLLENBQUMsS0FBSztZQUNsQixNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07WUFDcEIsUUFBUSxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLE1BQU0sQ0FBQztZQUNoRCxNQUFNLEVBQUUsQ0FBQztZQUNULEtBQUssRUFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQztZQUM1QixRQUFRLEVBQUUsQ0FBQztZQUNYLEtBQUssRUFBRSxPQUFPO1lBQ2QsU0FBUyxFQUFFLEtBQUssRUFBRSxHQUFZLEVBQXVCLEVBQUU7Z0JBQ3JELElBQUksR0FBZ0IsQ0FBQztnQkFDckIsSUFBSSxLQUFLLElBQUksR0FBRyxDQUFDLENBQUMsS0FBSyxNQUFNLEVBQUUsQ0FBQztvQkFDOUIsR0FBRyxHQUFHLEtBQUssQ0FBQztvQkFDWixLQUFLLEdBQUcsSUFBSSxDQUFDO2dCQUNmLENBQUM7cUJBQU0sQ0FBQztvQkFDTixHQUFHLEdBQUcsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNwRCxDQUFDO2dCQUNELE9BQU8sRUFBRSxLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLENBQUM7WUFDN0QsQ0FBQztTQUNGLENBQUM7UUFDRixPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsS0FBSyxDQUFDLFVBQVUsQ0FDZCxJQUE0QixFQUM1QixDQUFTLEVBQ1QsT0FBMkIsRUFDM0IsT0FBZTtRQUVmLE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQy9DLDRGQUE0RjtRQUM1Riw2RkFBNkY7UUFDN0YsK0ZBQStGO1FBQy9GLDZGQUE2RjtRQUM3RixJQUFJLElBQUksRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLENBQUM7WUFDekIsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFO2dCQUN0QyxRQUFRLEVBQUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDO2dCQUMzQyxPQUFPLEVBQUUsT0FBTztnQkFDaEIsaUJBQWlCLEVBQUUsSUFBSTthQUN4QixDQUFDLENBQUM7UUFDTCxDQUFDO1FBRUQsMkZBQTJGO1FBQzNGLHdGQUF3RjtRQUN4RixNQUFNLEdBQUcsR0FBRyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3BELElBQUksR0FBRyxFQUFFLENBQUM7WUFDUixJQUFJLENBQUM7Z0JBQ0gsTUFBTSxJQUFJLEdBQUcsTUFBTSxhQUFhLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQztnQkFDbEQsSUFBSSxJQUFJLENBQUMsRUFBRTtvQkFBRSxPQUFPLGlCQUFpQixDQUFDLE1BQU0saUJBQWlCLENBQUMsTUFBTSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUM3RixDQUFDO1lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDYixPQUFPLENBQUMsSUFBSSxDQUFDLDhDQUE4QyxDQUFDLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUN2RSxDQUFDO1FBQ0gsQ0FBQztRQUVELG9FQUFvRTtRQUNwRSxPQUFPLGlCQUFpQixDQUFDLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzdFLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsZ0JBQWdCLENBQ2QsSUFBNEIsRUFDNUIsQ0FBUyxFQUNULE9BQWUsRUFDZixJQUFZO1FBRVosTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLFFBQVEsSUFBSSxDQUFDLENBQUM7UUFDckYsSUFBSSxRQUFRLElBQUksQ0FBQztZQUFFLE9BQU8sSUFBSSxDQUFDO1FBQy9CLE1BQU0sR0FBRyxHQUFHLEdBQUcsQ0FBQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1FBQzlCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDOUMsSUFBSSxNQUFNO1lBQUUsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDOUIsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVEOzs0RkFFd0Y7SUFDeEYsS0FBSyxDQUFDLG9CQUFvQixDQUFDLE9BQWUsRUFBRSxJQUFZLEVBQUUsQ0FBUyxFQUFFLEdBQVc7UUFDOUUsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQ2hELElBQUksQ0FBQyxPQUFPO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFDMUIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxHQUFHLEdBQUcsTUFBTSxlQUFlLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFO2dCQUNsRixDQUFDO2dCQUNELE9BQU87Z0JBQ1AsSUFBSTthQUNMLENBQUMsQ0FBQztZQUNILElBQUksQ0FBQyxHQUFHO2dCQUFFLE9BQU8sSUFBSSxDQUFDLENBQUMsNERBQTREO1lBQ25GLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ3BDLE9BQU8sR0FBRyxDQUFDO1FBQ2IsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixPQUFPLENBQUMsSUFBSSxDQUFDLDJDQUEyQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQy9ELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUM7SUFFRDs7O3VCQUdtQjtJQUNuQixLQUFLLENBQUMsVUFBVSxDQUFDLENBQVMsRUFBRSxNQUF1QixFQUFFLFFBQTRCO1FBQy9FLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUNoRCxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU87UUFDckIsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3BFLE1BQU0sR0FBRyxHQUFHLGFBQWEsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLE9BQU8sRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN4RSxNQUFNLFFBQVEsR0FBRyxrQkFBa0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksR0FBRyxNQUFNLGFBQWEsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ2xELElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxHQUFHLEVBQUUsQ0FBQztnQkFDeEIsT0FBTyxDQUFDLElBQUksQ0FBQyxvRUFBb0UsQ0FBQyxDQUFDO2dCQUNuRixPQUFPO1lBQ1QsQ0FBQztZQUNELElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRTtnQkFBRSxNQUFNLElBQUksS0FBSyxDQUFDLFVBQVUsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7WUFDdkQsTUFBTSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksRUFBRSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ3RDLENBQUM7UUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1lBQ2IsT0FBTyxDQUFDLElBQUksQ0FBQyx1Q0FBdUMsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUM3RCxDQUFDO0lBQ0gsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgT2JzZXJ2YWJsZSwgZnJvbSwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHNhdmVBcyB9IGZyb20gJ2ZpbGUtc2F2ZXInO1xuaW1wb3J0IHtcbiAgTHJ1Q2FjaGUsXG4gIGFzc2VtYmxlVm9sdW1lLFxuICBiaXRtYXBUb1NjYWxhcixcbiAgY2hvb3NlU3RpdGNoTGV2ZWwsXG4gIGRlY29kZUltYWdlUkdCQSxcbiAgcmVhZExldmVsLFxufSBmcm9tICduYXBhcmktanMnO1xuaW1wb3J0IHR5cGUgeyBQaXhlbENodW5rLCBTdGl0Y2hCdWRnZXQsIFRpbGVLZXksIFRpbGVkU291cmNlIH0gZnJvbSAnbmFwYXJpLWpzJztcblxuaW1wb3J0IHsgSUltYWdlSW5mbyB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9pbWFnZS5jb250cmFjdCc7XG5pbXBvcnQgeyBJQ2hhbm5lbFN0YXRlLCBJSGlzdG9ncmFtIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2NoYW5uZWwtaGlzdG9ncmFtLWFwaS5jb250cmFjdCc7XG5pbXBvcnQgeyBUaWxlQWNjZXNzUG9ydCB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy9wb3J0cy90aWxlLWFjY2Vzcy5wb3J0JztcbmltcG9ydCB7IFNpbXBsZVNsaWNlQWNjZXNzU2VydmljZSB9IGZyb20gJy4uL3NpbXBsZS1zbGljZS1hY2Nlc3Muc2VydmljZSc7XG5pbXBvcnQge1xuICBUaWxlRGVzY3JpcHRvcixcbiAgYnVpbGRUaWxlVXJsLFxuICBleHBvcnRUaWZmRmlsZW5hbWUsXG4gIGV4cG9ydFRpZmZVcmwsXG4gIGZldGNoSnNvbldpdGhBdXRoLFxuICBmZXRjaFdpdGhBdXRoLFxuICBpc0Fib3J0RXJyb3IsXG4gIG5hdGl2ZUhpc3RvZ3JhbSxcbiAgcG9sbERlc2NyaXB0b3IsXG4gIHRpbGVzSW5mb1VybCxcbn0gZnJvbSAnLi4vdGlsZS1zZXJ2ZXInO1xuaW1wb3J0IHtcbiAgREVTQ1JJUFRPUl9QT0xMX0lOVEVSVkFMX01TLFxuICBERVNDUklQVE9SX1RJTUVPVVRfTVMsXG4gIEx1bWFQbGFuZSxcbiAgTUFYX1NUSVRDSF9USUxFUyxcbiAgTUFYX1RFWFRVUkVfRElNLFxuICBSR0JfQkFORF9DQUNIRV9USUxFUyxcbiAgU1RJVENIX0JVREdFVF9DT0VGRixcbiAgVElMRV9GRVRDSF9DT05DVVJSRU5DWSxcbiAgVElMRV9TSVpFLFxuICBWT0xVTUVfRkVUQ0hfQ09OQ1VSUkVOQ1ksXG4gIFZPTFVNRV9NQVhfU0xJQ0UsXG4gIGNyZWF0ZTJkQ2FudmFzLFxuICBzcGxpdFJnYkJhbmRzLFxuICBzdGFja0RlcHRoLFxufSBmcm9tICcuL25hcGFyaS1oZWxwZXJzJztcblxuLyoqIFdoYXQgdGhlIG5hdmlnYXRvciBkcmF3czogYSB3aG9sZS1zbGljZSBwaWN0dXJlIChhIGNhbnZhcyByZWFkIGZyb20gdGhlIHB5cmFtaWQsIG9yIGEgYml0bWFwKS4gKi9cbmV4cG9ydCB0eXBlIE92ZXJ2aWV3SW1hZ2UgPSBJbWFnZUJpdG1hcCB8IEhUTUxDYW52YXNFbGVtZW50IHwgT2Zmc2NyZWVuQ2FudmFzO1xuXG4vKiogQW4gYXNzZW1ibGVkIHVpbnQ4IChsdW1pbmFuY2UpIHZvbHVtZSwgeC1mYXN0ZXN0IHRoZW4geSB0aGVuIHouICovXG5leHBvcnQgaW50ZXJmYWNlIEFzc2VtYmxlZFZvbHVtZSB7XG4gIGRhdGE6IFVpbnQ4QXJyYXk7XG4gIHdpZHRoOiBudW1iZXI7XG4gIGhlaWdodDogbnVtYmVyO1xuICBkZXB0aDogbnVtYmVyO1xufVxuXG4vKiogRGVjb2RlIGFuIGBJbWFnZUJpdG1hcGAgdG8gYSBzaW5nbGUtY2hhbm5lbCB1aW50OCBsdW1pbmFuY2UgcGxhbmUgd2l0aCBuYXBhcmktanMnc1xuICogIGBiaXRtYXBUb1NjYWxhcmAgKHNlcnZlciBiYW5kcyBhcmUgZ3JleSwgUj1HPUIsIGFuZCBkZWNvZGUgZXhhY3RseTsgYSBjb2xvdXIgY29tcG9zaXRlXG4gKiAgYmVjb21lcyBpdHMgQlQuNjAxIGx1bWluYW5jZSkuIGBtYXhTaWRlYCBjYXBzIHRoZSBsb25nZXN0IHNpZGUgKGRvd25zY2FsaW5nIG9uIHRoZSBjYW52YXNcbiAqICBkcmF3KSDigJQgdXNlZCB0byBrZWVwIHByZS1sb2FkZWQgc3VyZmFjZSBzbGljZSBwbGFuZXMgc21hbGwuIENsb3NlcyB0aGUgYml0bWFwLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJpdG1hcFRvTHVtaW5hbmNlKGJtcDogSW1hZ2VCaXRtYXAsIG1heFNpZGU/OiBudW1iZXIpOiBMdW1hUGxhbmUge1xuICBjb25zdCBwbGFuZSA9IGJpdG1hcFRvU2NhbGFyKGJtcCwgeyBtYXhTaWRlLCB3ZWlnaHRzOiAnYnQ2MDEnIH0pO1xuICBibXAuY2xvc2U/LigpO1xuICByZXR1cm4gcGxhbmU7XG59XG5cbi8qKiBBIGNvbG91ciB0aWxlJ3MgUiwgRyBhbmQgQiBiYW5kcyAoY2xvc2VzIGEgYml0bWFwIHRpbGUpLiAqL1xuZnVuY3Rpb24gY2h1bmtUb0JhbmRzKGNodW5rOiBQaXhlbENodW5rKTogTHVtYVBsYW5lW10ge1xuICBpZiAodHlwZW9mIEltYWdlQml0bWFwICE9PSAndW5kZWZpbmVkJyAmJiBjaHVuay5kYXRhIGluc3RhbmNlb2YgSW1hZ2VCaXRtYXApIHtcbiAgICBjb25zdCBibXAgPSBjaHVuay5kYXRhO1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gc3BsaXRSZ2JCYW5kcyhkZWNvZGVJbWFnZVJHQkEoYm1wKSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGJtcC5jbG9zZT8uKCk7XG4gICAgfVxuICB9XG4gIHJldHVybiBzcGxpdFJnYkJhbmRzKHsgZGF0YTogY2h1bmsuZGF0YSBhcyBVaW50OEFycmF5LCB3aWR0aDogY2h1bmsud2lkdGgsIGhlaWdodDogY2h1bmsuaGVpZ2h0IH0pO1xufVxuXG4vKipcbiAqIFRoZSBuYXBhcmktanMgYmFja2VuZCdzIGNsaWVudCBvZiB0aGUgaml0LXNlcnZpY2UgdGlsZSBzZXJ2ZXIgKHJldmlldyBBcHBlbmRpeCBCLCBzdGVwIDIpLCBvblxuICogdGhlIHNoYXJlZCBgdGlsZS1zZXJ2ZXIvYCBwcm90b2NvbDogdGhlIGAvdGlsZXMvaW5mb2AgZGVzY3JpcHRvciAocG9sbGVkLCBjYWNoZWQgcGVyIHNvdXJjZSxcbiAqIHNoYXJlZCBiZXR3ZWVuIGNvbmN1cnJlbnQgY2FsbGVycyksIHdob2xlLXNsaWNlIHJlYWRzIGZyb20gdGhlIHB5cmFtaWQgKG5hcGFyaS1qcyBgcmVhZExldmVsYCksIHRoZSBweXJhbWlkYWxcbiAqIHtAbGluayBUaWxlZFNvdXJjZX1zIHRoZSAyRCBpbWFnZSBkcmF3cywgdm9sdW1lIGFzc2VtYmx5LCBzdXJmYWNlIHBsYW5lcywgdGhlIG5hdGl2ZSBgL2hpc3RvZ3JhbWBcbiAqIGFuZCB0aGUgYC9leHBvcnQvdGlmZmAgZG93bmxvYWQuXG4gKlxuICogVGhlIGltYWdlIGlzIGFsd2F5cyBwYXNzZWQgaW4g4oCUIHRoZSBjbGllbnQgbmV2ZXIgcmVhZHMgd2hpY2ggaW1hZ2UgdGhlIHNlcnZpY2UgaGFzIGxvYWRlZCDigJRcbiAqIHNvIGEgZGVzY3JpcHRvciBjYW4gb25seSBldmVyIGRlc2NyaWJlIHRoZSBpbWFnZSBhIGNhbGxlciBhc2tlZCBhYm91dCAoTkFQQVJJLVNWQy0yKS5cbiAqXG4gKiBTY2VuZS1zY29wZWQgc3RhdGUgKHRoZSBwb2xsIGluIGZsaWdodCwgdGhlIHNvdXJjZXMgdGhhdCBhbnN3ZXJlZCBcIm5vXCIsIHRoZSBuYXRpdmUgaGlzdG9ncmFtcylcbiAqIGxpdmVzIHVudGlsIHtAbGluayBzdGFydFNjZW5lfTsgdGhlIGRlc2NyaXB0b3IgY2FjaGUgaXRzZWxmIHN1cnZpdmVzIHNjZW5lcywga2V5ZWQgYnkgc291cmNlLlxuICovXG5leHBvcnQgY2xhc3MgTmFwYXJpVGlsZUNsaWVudCB7XG4gIC8qKiBDYWNoZWQgYC90aWxlcy9pbmZvYCBweXJhbWlkIGRlc2NyaXB0b3IgKyB0aGUgaW5mb0I2NCBpdCB3YXMgZmV0Y2hlZCBmb3IuICovXG4gIHByaXZhdGUgZGVzY3JpcHRvcjogVGlsZURlc2NyaXB0b3IgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBkZXNjcmlwdG9yS2V5OiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgLyoqIFRoZSBwb2xsIGluIGZsaWdodCwgc2hhcmVkIGJ5IGV2ZXJ5IGNhbGxlciBhc2tpbmcgZm9yIHRoZSBzYW1lIGBpbmZvQjY0YC4gKi9cbiAgcHJpdmF0ZSBkZXNjcmlwdG9yUG9sbDogeyBrZXk6IHN0cmluZzsgcHJvbWlzZTogUHJvbWlzZTxUaWxlRGVzY3JpcHRvciB8IG51bGw+IH0gfCBudWxsID0gbnVsbDtcbiAgLyoqIGBpbmZvQjY0YHMgdGhpcyBzY2VuZSdzIHBvbGwgZ290IG5vIGRlc2NyaXB0b3IgZm9yIChub24tMjAyIHN0YXR1cywgZW1wdHkgYm9keSwgdGltZW91dCkuXG4gICAqICBTY2VuZS1zY29wZWQsIHNvIGEgcmUtcGxvdCByZXRyaWVzIGEgc2xvdyBvciBmbGFreSBzZXJ2ZXIgb25jZS4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBkZXNjcmlwdG9yTWlzc2VzID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gIC8qKiBOYXRpdmUtYml0LWRlcHRoIGhpc3RvZ3JhbXMgZnJvbSBgL2hpc3RvZ3JhbWAsIGtleWVkIGAke3p9fCR7Y2hhbm5lbH1gICg+OC1iaXQgaW1hZ2VzKS4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBuYXRpdmVIaXN0b2dyYW1zID0gbmV3IE1hcDxzdHJpbmcsIElIaXN0b2dyYW0+KCk7XG4gIC8qKiBUaGUgY3VycmVudCBzY2VuZSdzIGxpZmV0aW1lOiBhIHBvbGwgc3RhcnRlZCB1bmRlciBpdCBlbmRzIHdoZW4gaXQgaXMgYWJvcnRlZC4gKi9cbiAgcHJpdmF0ZSBzY2VuZTogQWJvcnRTaWduYWwgPSBuZXcgQWJvcnRDb250cm9sbGVyKCkuc2lnbmFsO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgdGlsZXM6IFRpbGVBY2Nlc3NQb3J0LFxuICAgIHByaXZhdGUgcmVhZG9ubHkgc2ltcGxlU3RhY2s6IFNpbXBsZVNsaWNlQWNjZXNzU2VydmljZSxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGFwaTogc3RyaW5nLFxuICApIHt9XG5cbiAgLyoqIFdoZXRoZXIgYGluZm9gIGlzIGEgc2VsZi1jb250YWluZWQgc3RhY2sgKGB0aWxlZDpmYWxzZWA6IGVhY2ggc2xpY2UgaXRzIG93biBmaWxlLCBub1xuICAgKiAgc2VydmVyIHB5cmFtaWQpLiAqL1xuICBpc1NpbXBsZShpbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuc2ltcGxlU3RhY2suaXNTaW1wbGUoaW5mbyk7XG4gIH1cblxuICAvKiogQSBuZXcgc2NlbmU6IGZvcmdldCB0aGUgc2NlbmUtc2NvcGVkIHN0YXRlIGFuZCBwb2xsIHVuZGVyIGBzaWduYWxgIGZyb20gbm93IG9uLiAqL1xuICBzdGFydFNjZW5lKHNpZ25hbDogQWJvcnRTaWduYWwpOiB2b2lkIHtcbiAgICB0aGlzLnNjZW5lID0gc2lnbmFsO1xuICAgIHRoaXMuZGVzY3JpcHRvclBvbGwgPSBudWxsO1xuICAgIHRoaXMuZGVzY3JpcHRvck1pc3Nlcy5jbGVhcigpO1xuICAgIHRoaXMubmF0aXZlSGlzdG9ncmFtcy5jbGVhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoICsgY2FjaGUgdGhlIHNlcnZlciBweXJhbWlkIGRlc2NyaXB0b3IgKGBHRVQgL3RpbGVzL2luZm9gKTogdGhlIFJFQUwgcGVyLWxldmVsIHRpbGUgZ3JpZCxcbiAgICogdGlsZSBzaXplLCBjaGFubmVsIG1ldGFkYXRhIGFuZCBwaHlzaWNhbCBwaXhlbCBzaXplLiBUaGUgYmFja2VuZCByZXR1cm5zIDIwMiB3aGlsZSB0aGUgc291cmNlXG4gICAqIGlzIHN0aWxsIGNhY2hpbmcsIHNvIHdlIHBvbGwgKHtAbGluayBwb2xsRGVzY3JpcHRvcn0pLiBDYWNoZWQgcGVyIGBpbmZvQjY0YDsgcmV0dXJucyBudWxsIGlmIGl0XG4gICAqIG5ldmVyIGJlY29tZXMgcmVhZHksIG9yIHRoZSBzZXJ2ZXIgd2lsbCBub3QgZGVzY3JpYmUgdGhlIHNvdXJjZSAoY2FsbGVycyBmYWxsIGJhY2sgdG8gYVxuICAgKiBzaW5nbGUtdGlsZSBmZXRjaCkuIFJlYWQgaXQgYmFjayB0aHJvdWdoIHtAbGluayBjdXJyZW50RGVzY3JpcHRvcn0uIFRoaXMgaXMgdGhlIGF1dGhvcml0YXRpdmVcbiAgICogZ3JpZCDigJQgZ3Vlc3NpbmcgbGV2ZWwgZGltcyBmcm9tIGB0cnVlSW1hZ2VTaXplYCBvdmVyc2hvb3RzIHRoZSByZWFsIGdyaWQgYW5kIHRoZSBzZXJ2ZXIgNDAwc1xuICAgKiBvdXQtb2YtcmFuZ2UgdGlsZXMuXG4gICAqL1xuICBlbnN1cmVEZXNjcmlwdG9yKGluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQpOiBQcm9taXNlPFRpbGVEZXNjcmlwdG9yIHwgbnVsbD4ge1xuICAgIC8vIFNlbGYtY29udGFpbmVkIG11bHRpLXNsaWNlIHN0YWNrIChubyB0aWxlIHNlcnZlciDigJQgZS5nLiBhIG51bWJlcmVkIGltYWdlXG4gICAgLy8gc2VyaWVzIGFzc2VtYmxlZCBjbGllbnQtc2lkZSwgZWFjaCBzbGljZSBhIGRpZmZlcmVudCBmaWxlKTogdGhlcmUgaXMgbm9cbiAgICAvLyBzaW5nbGUgc2VydmVyLXRpbGVkIHB5cmFtaWQgdG8gZGVzY3JpYmUuIFJldHVybmluZyBudWxsIHJvdXRlcyBldmVyeVxuICAgIC8vIGNhbGxlciAoMkQgaW1hZ2UsIHZvbHVtZSwgc3VyZmFjZSkgdG8gdGhlaXIgc3RpdGNoZWQvc2luZ2xlLWZldGNoXG4gICAgLy8gZmFsbGJhY2ssIHdoaWNoIOKAlCB2aWEgZmV0Y2hJbWFnZSdzIG93biBTaW1wbGVTbGljZUFjY2Vzc1NlcnZpY2UgYnJhbmNoIOKAlFxuICAgIC8vIGNvcnJlY3RseSBmZXRjaGVzIGVhY2ggc2xpY2UncyBvd24gVVJMIGluc3RlYWQgb2Ygb25lIGZpeGVkIGZpbGUncyB0aWxlXG4gICAgLy8gcHlyYW1pZC5cbiAgICBpZiAodGhpcy5zaW1wbGVTdGFjay5pc1NpbXBsZShpbmZvKSkgcmV0dXJuIFByb21pc2UucmVzb2x2ZShudWxsKTtcbiAgICBjb25zdCBpbmZvQjY0ID0gdGhpcy50aWxlcy5nZXRTZWxlY3RlZEluZm9CNjQoKTtcbiAgICBpZiAoIWluZm9CNjQpIHJldHVybiBQcm9taXNlLnJlc29sdmUobnVsbCk7XG4gICAgaWYgKHRoaXMuZGVzY3JpcHRvciAmJiB0aGlzLmRlc2NyaXB0b3JLZXkgPT09IGluZm9CNjQpIHJldHVybiBQcm9taXNlLnJlc29sdmUodGhpcy5kZXNjcmlwdG9yKTtcbiAgICAvLyBBIHNvdXJjZSB0aGF0IGFuc3dlcmVkIFwibm9cIiAob3IgbmV2ZXIgYW5zd2VyZWQpIHRoaXMgc2NlbmUgaXMgbm90IGFza2VkIGFnYWluOiB0aGUgcmVuZGVyLFxuICAgIC8vIHRoZSBzdGl0Y2ggZmFsbGJhY2sgYW5kIGV2ZXJ5IHNsaWNlIHdvdWxkIG90aGVyd2lzZSBlYWNoIHdhaXQgb3V0IHRoZWlyIG93biBwb2xsLlxuICAgIGlmICh0aGlzLmRlc2NyaXB0b3JNaXNzZXMuaGFzKGluZm9CNjQpKSByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKG51bGwpO1xuICAgIC8vIENvbmN1cnJlbnQgY2FsbGVycyAocmVuZGVyLCBuYXZpZ2F0b3IsIGhpc3RvZ3JhbSkgc2hhcmUgb25lIHBvbGwuXG4gICAgaWYgKHRoaXMuZGVzY3JpcHRvclBvbGw/LmtleSA9PT0gaW5mb0I2NCkgcmV0dXJuIHRoaXMuZGVzY3JpcHRvclBvbGwucHJvbWlzZTtcbiAgICBjb25zdCBzY2VuZSA9IHRoaXMuc2NlbmU7XG4gICAgY29uc3QgcHJvbWlzZSA9IHRoaXMucG9sbERlc2NyaXB0b3IoaW5mb0I2NCwgc2NlbmUpLnRoZW4oKGRlc2MpID0+IHtcbiAgICAgIGlmICh0aGlzLmRlc2NyaXB0b3JQb2xsPy5wcm9taXNlID09PSBwcm9taXNlKSB0aGlzLmRlc2NyaXB0b3JQb2xsID0gbnVsbDtcbiAgICAgIGlmIChkZXNjKSB7XG4gICAgICAgIHRoaXMuZGVzY3JpcHRvciA9IGRlc2M7XG4gICAgICAgIHRoaXMuZGVzY3JpcHRvcktleSA9IGluZm9CNjQ7XG4gICAgICB9IGVsc2UgaWYgKCFzY2VuZS5hYm9ydGVkKSB7XG4gICAgICAgIHRoaXMuZGVzY3JpcHRvck1pc3Nlcy5hZGQoaW5mb0I2NCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gZGVzYztcbiAgICB9KTtcbiAgICB0aGlzLmRlc2NyaXB0b3JQb2xsID0geyBrZXk6IGluZm9CNjQsIHByb21pc2UgfTtcbiAgICByZXR1cm4gcHJvbWlzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgYC90aWxlcy9pbmZvYCBsb29wIGJlaGluZCB7QGxpbmsgZW5zdXJlRGVzY3JpcHRvcn06IHRoZSBzaGFyZWQgaml0LXNlcnZpY2UgcG9sbFxuICAgKiAoYHRpbGUtc2VydmVyL3BvbGxEZXNjcmlwdG9yYCkuIFJlLXBvbGxzIG9ubHkgb24gMjAyICh0aGUgc2VydmVyIGlzIHN0aWxsIGNhY2hpbmcgdGhlIHNvdXJjZSk7XG4gICAqIGFueSBvdGhlciBzdGF0dXMgbWVhbnMgdGhpcyBzZXJ2ZXIgd2lsbCBub3QgZGVzY3JpYmUgaXQsIHNvIHRoZSBhbnN3ZXIgaXMgbnVsbCBhdCBvbmNlLiBFYWNoXG4gICAqIHJlcXVlc3QgZ2l2ZXMgdXAgYWZ0ZXIgaXRzIG93biB0aW1lb3V0IOKAlCBhIGh1bmcgcmVxdWVzdCB3b3VsZCBvdGhlcndpc2UgbmV2ZXIgcmVhY2ggdGhlXG4gICAqIGRlYWRsaW5lIGNoZWNrIOKAlCBhbmQgdGhlIHdob2xlIHBvbGwgZW5kcyAobnVsbCkgYXMgc29vbiBhcyBgc2NlbmVgIGlzIGFib3J0ZWQuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHBvbGxEZXNjcmlwdG9yKGluZm9CNjQ6IHN0cmluZywgc2NlbmU6IEFib3J0U2lnbmFsKTogUHJvbWlzZTxUaWxlRGVzY3JpcHRvciB8IG51bGw+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHBvbGxEZXNjcmlwdG9yKGZldGNoSnNvbldpdGhBdXRoKHRoaXMudGlsZXMpLCB0aWxlc0luZm9VcmwodGhpcy5hcGksIGluZm9CNjQpLCB7XG4gICAgICAgIGRlYWRsaW5lTXM6IERFU0NSSVBUT1JfVElNRU9VVF9NUyxcbiAgICAgICAgaW50ZXJ2YWxNczogREVTQ1JJUFRPUl9QT0xMX0lOVEVSVkFMX01TLFxuICAgICAgICBzaWduYWw6IHNjZW5lLFxuICAgICAgICB0YWc6ICdbbmFwYXJpLWpzXScsXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGlmIChpc0Fib3J0RXJyb3IoZXJyKSkgcmV0dXJuIG51bGw7IC8vIHRoZSBzY2VuZSB3YXMgcmVzZXRcbiAgICAgIHRocm93IGVycjtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGhlIHB5cmFtaWQgZGVzY3JpcHRvciBvZiBgaW5mb2AsIG9yIG51bGwgd2hlbiBpdCBoYXMgbm9uZS5cbiAgICpcbiAgICogVGhlIGNhY2hlIGlzIGtleWVkIGJ5IGBpbmZvQjY0YCBhbmQgc3Vydml2ZXMgaW1hZ2Ugc3dpdGNoZXMgYW5kIGZhaWxlZCBwb2xscywgc28gcmVhZGluZyBpdFxuICAgKiBkaXJlY3RseSB3b3VsZCBoYW5kIGEgYHRpbGVkOmZhbHNlYCBzdGFjayAob3IgYW4gaW1hZ2Ugd2hvc2UgcG9sbCBuZXZlciBhbnN3ZXJlZCkgdGhlXG4gICAqIFBSRVZJT1VTIGltYWdlJ3Mgc2l6ZSwgwrVtL3BpeGVsLCB0aWxlIHNpemUgYW5kIGJpdCBkZXB0aC4gRXZlcnkgcmVhZCBzaXRlIGdvZXMgdGhyb3VnaCBoZXJlLlxuICAgKi9cbiAgY3VycmVudERlc2NyaXB0b3IoaW5mbzogSUltYWdlSW5mbyB8IHVuZGVmaW5lZCk6IFRpbGVEZXNjcmlwdG9yIHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLmRlc2NyaXB0b3IgfHwgdGhpcy5zaW1wbGVTdGFjay5pc1NpbXBsZShpbmZvKSkgcmV0dXJuIG51bGw7XG4gICAgcmV0dXJuIHRoaXMuZGVzY3JpcHRvcktleSA9PT0gdGhpcy50aWxlcy5nZXRTZWxlY3RlZEluZm9CNjQoKSA/IHRoaXMuZGVzY3JpcHRvciA6IG51bGw7XG4gIH1cblxuICAvKiogwrVtIHBlciBwaXhlbCBhbG9uZyB4IGZvciBgaW5mb2A6IHRoZSBkZXNjcmlwdG9yJ3MsIGVsc2UgdGhlIGltYWdlIG1ldGFkYXRhJ3M7IDAgd2hlblxuICAgKiAgbmVpdGhlciBkZWNsYXJlcyBpdC4gKi9cbiAgbXBwWChpbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5jdXJyZW50RGVzY3JpcHRvcihpbmZvKT8ubXBwWCB8fCBpbmZvPy5pbWFnZU1ldGE/LlswXT8ubXBwWCB8fCAwO1xuICB9XG5cbiAgLyoqXG4gICAqIFNsaWNlIGB6YCBhcyBPTkUgaW1hZ2UsIGZvciBhIHNvdXJjZSB3aXRoIG5vIHB5cmFtaWQgdG8gcmVhZDogYSBgdGlsZWQ6ZmFsc2VgIHN0YWNrJ3Mgb3duXG4gICAqIGZpbGUgZm9yIGB6YCwgZWxzZSAobm8gZGVzY3JpcHRvcikgdGhlIHNlcnZlcidzIHNpbmdsZSB0b3AtbGVmdCB0aWxlIOKAlCBjb3JyZWN0IGZvclxuICAgKiBzbWFsbC9zaW1wbGUgaW1hZ2VzLlxuICAgKlxuICAgKiBBIHNlbGYtY29udGFpbmVkIG11bHRpLXNsaWNlIHN0YWNrJ3MgYHpgIGluZGV4ZXMgYSBjb21wbGV0ZWx5IGRpZmZlcmVudCBmaWxlJ3Mgb3duIHByZXZpZXdcbiAgICogVVJMLCBub3QgYW4gaW50ZXJuYWwgc2xpY2Ugb2Ygb25lIHNlcnZlci10aWxlZCBmaWxlLCBzbyBpdCBpcyByZXNvbHZlZCBhbmQgZmV0Y2hlZCB2aWFcbiAgICogU2ltcGxlU2xpY2VBY2Nlc3NTZXJ2aWNlIChzaGFyZWQgd2l0aCBPU0Q7IHNlZSBpdHMgZG9jcyBmb3Igd2h5IHRoaXMgY2FuJ3QgYmUgYSBiYXJlIGZldGNoKCkpXG4gICAqIHJhdGhlciB0aGFuIGFzIGEgL3RpbGU/aW5mbz0uLi4mej0gcmVxdWVzdCBhZ2FpbnN0IHdoaWNoZXZlciBzaW5nbGUgZmlsZVxuICAgKiBnZXRTZWxlY3RlZEluZm9CNjQoKSBwb2ludHMgYXQgKHdoaWNoIHdvdWxkIDQwMC9taXNtYXRjaCBmb3IgYW55IHogYmV5b25kIHRoYXQgb25lIGZpbGUncyBvd25cbiAgICogZXh0ZW50KS4gYGNoYW5uZWxgIHNlbGVjdHMgYSBiYW5kOiBhIHNlcnZlcmxlc3MgbXVsdGljaGFubmVsIHN0YWNrJ3Mgb3duIHBsYW5lXG4gICAqIChgY2hhbm5lbFVybHNbel1bY2hhbm5lbF1gLCB0aGUgY2xpZW50LXNpZGUgYW5hbG9nIG9mIHRoZSBzZXJ2ZXIncyBwZXItY2hhbm5lbFxuICAgKiBgL3RpbGU/Y2hhbm5lbD1jYCksIGVsc2UgdGhlIHNlcnZlcidzLlxuICAgKi9cbiAgYXN5bmMgZmV0Y2hJbWFnZShpbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkLCB6OiBudW1iZXIsIGNoYW5uZWw/OiBudW1iZXIpOiBQcm9taXNlPEltYWdlQml0bWFwPiB7XG4gICAgaWYgKHRoaXMuc2ltcGxlU3RhY2suaXNTaW1wbGUoaW5mbykpIHtcbiAgICAgIGNvbnN0IGNoVXJscyA9IChpbmZvIGFzIElJbWFnZUluZm8pLmNoYW5uZWxVcmxzO1xuICAgICAgY29uc3QgdXJsID1cbiAgICAgICAgY2hhbm5lbCAhPSBudWxsICYmIGNoVXJscz8uW3pdPy5bY2hhbm5lbF0gIT0gbnVsbFxuICAgICAgICAgID8gY2hVcmxzW3pdW2NoYW5uZWxdXG4gICAgICAgICAgOiB0aGlzLnNpbXBsZVN0YWNrLnVybEZvcihpbmZvIGFzIElJbWFnZUluZm8sIHopO1xuICAgICAgaWYgKCF1cmwpIHRocm93IG5ldyBFcnJvcihgW25hcGFyaS1qc10gbm8gVVJMIGZvciBzbGljZSAke3p9YCk7XG4gICAgICByZXR1cm4gdGhpcy5zaW1wbGVTdGFjay5mZXRjaEFzQml0bWFwKHVybCk7XG4gICAgfVxuICAgIGNvbnN0IGluZm9CNjQgPSB0aGlzLnRpbGVzLmdldFNlbGVjdGVkSW5mb0I2NCgpO1xuICAgIGlmICghaW5mb0I2NCkgdGhyb3cgbmV3IEVycm9yKCdbbmFwYXJpLWpzXSBubyBzZWxlY3RlZCBpbWFnZSBpbmZvIChnZXRTZWxlY3RlZEluZm9CNjQgbnVsbCknKTtcbiAgICBjb25zdCB1cmwgPSBidWlsZFRpbGVVcmwodGhpcy5hcGksIGluZm9CNjQsIHsgcmVzOiAwLCBjb2w6IDAsIHJvdzogMCwgeiwgdGlsZVNpemU6IFRJTEVfU0laRSwgY2hhbm5lbCB9KTtcbiAgICBjb25zdCByZXNwID0gYXdhaXQgZmV0Y2hXaXRoQXV0aCh0aGlzLnRpbGVzLCB1cmwpO1xuICAgIGlmICghcmVzcC5vaykgdGhyb3cgbmV3IEVycm9yKGBbbmFwYXJpLWpzXSBzbGljZSBmZXRjaCBmYWlsZWQ6ICR7cmVzcC5zdGF0dXN9ICgwLzAgcmVzIDApYCk7XG4gICAgcmV0dXJuIGNyZWF0ZUltYWdlQml0bWFwKGF3YWl0IHJlc3AuYmxvYigpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTbGljZSBgemAgYXMgYSBzaW5nbGUtY2hhbm5lbCB1aW50OCBsdW1pbmFuY2UgcGxhbmUuIFdpdGggYSBweXJhbWlkLCBuYXBhcmktanMncyBgcmVhZExldmVsYFxuICAgKiByZWFkcyBpdCBmcm9tIHRoZSBzYW1lIHtAbGluayB0aWxlZFNvdXJjZX0gdGhlIDJEIHZpZXcgZHJhd3M6IHRoZSBmaW5lc3QgbGV2ZWwgd2hvc2UgdGlsZSBncmlkXG4gICAqIGZpdHMgYG1heFRpbGVzYCBhbmQgd2hvc2UgbG9uZ2VzdCBzaWRlIGZpdHMgdGhlIEdQVSB0ZXh0dXJlIGxpbWl0IChubyBmaW5lciB0aGFuIGBtYXhTaWRlYFxuICAgKiBuZWVkcywgd2hlbiBnaXZlbiksIHN0aXRjaGVkIHdpdGggYm91bmRlZCBjb25jdXJyZW5jeSBhbmQgYXJlYS1hdmVyYWdlZCBkb3duIHRvIGBtYXhTaWRlYC5cbiAgICogV2l0aG91dCBvbmUsIHRoZSBzaW5nbGUgaW1hZ2Ugb2Yge0BsaW5rIGZldGNoSW1hZ2V9LiBgY2hhbm5lbGAgc2VsZWN0cyBhIGJhbmQgKHJlYWwgbGV2ZWxzXG4gICAqIG9ubHkpOyBvbWl0IGl0IGZvciB0aGUgY29tcG9zaXRlLCB3aG9zZSBvdmVydmlldyBsZXZlbHMgbGV0IGEgbGFyZ2UgaW1hZ2UgcmVhZCBhIHNtYWxsIGxldmVsLlxuICAgKiBgY29tcG9zaXRlRmFsbGJhY2tgOiBzZWUge0BsaW5rIHBsYW5lU291cmNlfS4gYHNpZ25hbGAgY2FuY2VscyB0aGUgcmVhZCAoaXQgcmVqZWN0cykuXG4gICAqL1xuICBhc3luYyByZWFkUGxhbmUoXG4gICAgaW5mbzogSUltYWdlSW5mbyB8IHVuZGVmaW5lZCxcbiAgICB6OiBudW1iZXIsXG4gICAgY2hhbm5lbDogbnVtYmVyIHwgdW5kZWZpbmVkLFxuICAgIG9wdHM6IHsgbWF4VGlsZXM6IG51bWJlcjsgbWF4U2lkZT86IG51bWJlcjsgY29tcG9zaXRlRmFsbGJhY2s/OiBib29sZWFuOyBzaWduYWw/OiBBYm9ydFNpZ25hbCB9LFxuICApOiBQcm9taXNlPEx1bWFQbGFuZT4ge1xuICAgIC8vIEEgdGlsZWQ6ZmFsc2Ugc3RhY2sgaGFzIG5vIHB5cmFtaWQ6IGZldGNoIGl0cyBzbGljZSBpbiB0aGlzIHRpY2ssIGFzIHRoZSAyRCByZW5kZXIgZG9lcy5cbiAgICBjb25zdCBkZXNjID0gdGhpcy5zaW1wbGVTdGFjay5pc1NpbXBsZShpbmZvKSA/IG51bGwgOiBhd2FpdCB0aGlzLmVuc3VyZURlc2NyaXB0b3IoaW5mbyk7XG4gICAgaWYgKCFkZXNjPy5sZXZlbHM/Lmxlbmd0aCkgcmV0dXJuIGJpdG1hcFRvTHVtaW5hbmNlKGF3YWl0IHRoaXMuZmV0Y2hJbWFnZShpbmZvLCB6LCBjaGFubmVsKSwgb3B0cy5tYXhTaWRlKTtcbiAgICBjb25zdCBidWRnZXQ6IFN0aXRjaEJ1ZGdldCA9IHtcbiAgICAgIG1heFRpbGVzOiBvcHRzLm1heFRpbGVzLFxuICAgICAgbWF4U2lkZTogb3B0cy5tYXhTaWRlLFxuICAgICAgbWF4VGV4dHVyZURpbTogTUFYX1RFWFRVUkVfRElNLFxuICAgIH07XG4gICAgY29uc3Qgc291cmNlID0gb3B0cy5jb21wb3NpdGVGYWxsYmFja1xuICAgICAgPyB0aGlzLnBsYW5lU291cmNlKGRlc2MsIGNoYW5uZWwsIGJ1ZGdldClcbiAgICAgIDogdGhpcy50aWxlZFNvdXJjZShkZXNjLCBjaGFubmVsLCAxKTtcbiAgICBjb25zdCBpbWcgPSBhd2FpdCByZWFkTGV2ZWwoc291cmNlLCB6LCB7XG4gICAgICAuLi5idWRnZXQsXG4gICAgICBjb25jdXJyZW5jeTogVElMRV9GRVRDSF9DT05DVVJSRU5DWSxcbiAgICAgIHNpZ25hbDogb3B0cy5zaWduYWwsXG4gICAgfSk7XG4gICAgaWYgKCFpbWcuZml0cyAmJiBvcHRzLm1heFRpbGVzID49IE1BWF9TVElUQ0hfVElMRVMpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgYFtuYXBhcmktanNdIG5vIGxldmVsIG9mIHNsaWNlICR7en0gZml0cyB0aGUgJHtvcHRzLm1heFRpbGVzfS10aWxlLyR7TUFYX1RFWFRVUkVfRElNfXB4IGJ1ZGdldDsgYCArXG4gICAgICAgICAgYHJlYWQgdGhlIGNvYXJzZXN0IChsZXZlbCAke2ltZy5sZXZlbH0pIGFuZCBkb3duc2NhbGVkIGl0IHRvICR7aW1nLndpZHRofcOXJHtpbWcuaGVpZ2h0fS5gLFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHsgZGF0YTogaW1nLmRhdGEgYXMgVWludDhBcnJheSwgd2lkdGg6IGltZy53aWR0aCwgaGVpZ2h0OiBpbWcuaGVpZ2h0IH07XG4gIH1cblxuICAvKipcbiAgICogVGhlIHNjYWxhciBzb3VyY2UgZm9yIG9uZSBkZWNpbWF0ZWQgcGxhbmUgb2YgYmFuZCBgY2hhbm5lbGAuIEEgYmFuZCBoYXMgdGlsZXMgb25seSBhdCB0aGVcbiAgICogKGZldywgbGFyZ2UpIHJlYWwgQmlvLUZvcm1hdHMgbGV2ZWxzOyB3aGVuIG5vbmUgb2YgdGhlbSBmaXRzIHRoZSBidWRnZXQg4oCUIGUuZy4gdGhlIGNvYXJzZXN0XG4gICAqIHJlYWwgbGV2ZWwgaXMgc3RpbGwgMTQ5ODLDlzE4NjcwLCB+MTEwMCBmdWxsLXJlcyB0aWxlcyDigJQgc3RpdGNoaW5nIGl0IHBlciBzbGljZSBmbG9vZHMgdGhlXG4gICAqIHNlcnZlciAoNTA0cykgYW5kIHN0YWxscyB0aGUgbG9hZC4gVGhlIGNvbXBvc2l0ZSBweXJhbWlkIGhhcyBzbWFsbCBvdmVydmlldyBsZXZlbHMsIHNvIHRoZVxuICAgKiBwbGFuZSBpcyByZWFkIGZyb20gdGhlIGNvbXBvc2l0ZSBpbnN0ZWFkIChpdHMgbHVtaW5hbmNlKS4gT25seSBmb3IgYSBjYWxsZXIgdGhhdCB3YW50cyBvbmVcbiAgICogZGVjaW1hdGVkIHBsYW5lICh0aGUgc3VyZmFjZSBoZWlnaHQpOiBmb3IgYSBtdWx0aWNoYW5uZWwgVk9MVU1FIGV2ZXJ5IGNoYW5uZWwgd291bGQgdGhlbiByZWFkXG4gICAqIHRoZSBzYW1lIGNvbXBvc2l0ZSBhbmQgdGhlIGNoYW5uZWxzIHdvdWxkIGNvbGxhcHNlIGludG8gb25lLCBzbyB2b2x1bWVzIGtlZXAgZWFjaCBiYW5kLlxuICAgKi9cbiAgcHJpdmF0ZSBwbGFuZVNvdXJjZShkZXNjOiBUaWxlRGVzY3JpcHRvciwgY2hhbm5lbDogbnVtYmVyIHwgdW5kZWZpbmVkLCBidWRnZXQ6IFN0aXRjaEJ1ZGdldCk6IFRpbGVkU291cmNlIHtcbiAgICBjb25zdCBiYW5kID0gdGhpcy50aWxlZFNvdXJjZShkZXNjLCBjaGFubmVsLCAxKTtcbiAgICBpZiAoY2hhbm5lbCA9PSBudWxsIHx8IGRlc2MubGV2ZWxzLmxlbmd0aCA8PSAoZGVzYy5yZWFsTGV2ZWxzID8/IGRlc2MubGV2ZWxzLmxlbmd0aCkpIHJldHVybiBiYW5kO1xuICAgIGNvbnN0IG93biA9IGNob29zZVN0aXRjaExldmVsKGJhbmQsIGJ1ZGdldCk7XG4gICAgaWYgKG93bi5maXRzKSByZXR1cm4gYmFuZDtcbiAgICBjb25zdCBjb21wb3NpdGUgPSB0aGlzLnRpbGVkU291cmNlKGRlc2MsIHVuZGVmaW5lZCwgMSk7XG4gICAgY29uc3QgYWx0ID0gY2hvb3NlU3RpdGNoTGV2ZWwoY29tcG9zaXRlLCBidWRnZXQpO1xuICAgIGlmICghYWx0LmZpdHMgJiYgYWx0LmNvbHMgKiBhbHQucm93cyA+PSBvd24uY29scyAqIG93bi5yb3dzKSByZXR1cm4gYmFuZDtcbiAgICBjb25zb2xlLndhcm4oXG4gICAgICBgW25hcGFyaS1qc10gY2hhbm5lbCAke2NoYW5uZWx9IGhhcyBubyBweXJhbWlkIGxldmVsIHdpdGhpbiB0aGUgJHtidWRnZXQubWF4VGlsZXN9LXRpbGUgYCArXG4gICAgICAgIGBidWRnZXQ7IHVzaW5nIHRoZSBjb21wb3NpdGUgb3ZlcnZpZXcgKCR7YWx0LndpZHRofcOXJHthbHQuaGVpZ2h0fSkgZm9yIHRoaXMgcGxhbmUuYCxcbiAgICApO1xuICAgIHJldHVybiBjb21wb3NpdGU7XG4gIH1cblxuICAvKipcbiAgICogQSBzbWFsbCBwaWN0dXJlIG9mIHRoZSB3aG9sZSBzbGljZSBmb3IgdGhlIG5hdmlnYXRvcjogdGhlIGNvbXBvc2l0ZSAob3IgYmFuZCBgY2hhbm5lbGApIGF0IHRoZVxuICAgKiBmaW5lc3QgbGV2ZWwgYSBzaW5nbGUgdGlsZSBob2xkcywgZWxzZSB0aGUgY29hcnNlc3QgbGV2ZWwsIGFzIGFuIFJHQkEgY2FudmFzLiBXaXRob3V0IGFcbiAgICogcHlyYW1pZCwgdGhlIHNpbmdsZSBpbWFnZSBvZiB7QGxpbmsgZmV0Y2hJbWFnZX0uXG4gICAqL1xuICBhc3luYyBvdmVydmlld0ltYWdlKGluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQsIHo6IG51bWJlciwgY2hhbm5lbD86IG51bWJlcik6IFByb21pc2U8T3ZlcnZpZXdJbWFnZT4ge1xuICAgIGNvbnN0IGRlc2MgPSB0aGlzLnNpbXBsZVN0YWNrLmlzU2ltcGxlKGluZm8pID8gbnVsbCA6IGF3YWl0IHRoaXMuZW5zdXJlRGVzY3JpcHRvcihpbmZvKTtcbiAgICBpZiAoIWRlc2M/LmxldmVscz8ubGVuZ3RoKSByZXR1cm4gdGhpcy5mZXRjaEltYWdlKGluZm8sIHosIGNoYW5uZWwpO1xuICAgIGNvbnN0IGltZyA9IGF3YWl0IHJlYWRMZXZlbCh0aGlzLnRpbGVkU291cmNlKGRlc2MsIGNoYW5uZWwsIDQpLCB6LCB7XG4gICAgICBtYXhUaWxlczogMSxcbiAgICAgIG1heFRleHR1cmVEaW06IE1BWF9URVhUVVJFX0RJTSxcbiAgICAgIGNvbmN1cnJlbmN5OiBUSUxFX0ZFVENIX0NPTkNVUlJFTkNZLFxuICAgIH0pO1xuICAgIGNvbnN0IHsgY2FudmFzLCBjdHggfSA9IGNyZWF0ZTJkQ2FudmFzKGltZy53aWR0aCwgaW1nLmhlaWdodCwgJ292ZXJ2aWV3Jyk7XG4gICAgY29uc3QgcGl4ZWxzID0gY3R4LmNyZWF0ZUltYWdlRGF0YShpbWcud2lkdGgsIGltZy5oZWlnaHQpO1xuICAgIHBpeGVscy5kYXRhLnNldChpbWcuZGF0YSBhcyBVaW50OEFycmF5KTtcbiAgICBjdHgucHV0SW1hZ2VEYXRhKHBpeGVscywgMCwgMCk7XG4gICAgcmV0dXJuIGNhbnZhcztcbiAgfVxuXG4gIC8qKlxuICAgKiBTbGljZSBgemAncyBSLCBHIGFuZCBCIGJhbmRzLCBhcyB0aHJlZSB1aW50OCBwbGFuZXM6IHRoZSBjb21wb3NpdGUgcmVhZCBhcyBSR0JBIGF0IHRoZVxuICAgKiBmaW5lc3QgbGV2ZWwgd2l0aGluIGBtYXhUaWxlc2AgKGFzIHtAbGluayByZWFkUGxhbmV9IHBpY2tzIGl0OyAxIGZvciBhIGNoZWFwIG92ZXJ2aWV3XG4gICAqIHNhbXBsZSksIGVsc2UgdGhlIHNpbmdsZSBpbWFnZSBvZiB7QGxpbmsgZmV0Y2hJbWFnZX0uIFRoZSByYXcgYmFuZHMgb2YgYW4gUkdCIGltYWdlLCBmb3IgdGhlXG4gICAqIHBlci1iYW5kIGxheWVycyBhbmQgdGhlaXIgaGlzdG9ncmFtcy5cbiAgICovXG4gIGFzeW5jIHJlYWRSZ2JCYW5kcyhcbiAgICBpbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkLFxuICAgIHo6IG51bWJlcixcbiAgICBtYXhUaWxlczogbnVtYmVyID0gTUFYX1NUSVRDSF9USUxFUyxcbiAgKTogUHJvbWlzZTxMdW1hUGxhbmVbXT4ge1xuICAgIGNvbnN0IGRlc2MgPSB0aGlzLnNpbXBsZVN0YWNrLmlzU2ltcGxlKGluZm8pID8gbnVsbCA6IGF3YWl0IHRoaXMuZW5zdXJlRGVzY3JpcHRvcihpbmZvKTtcbiAgICBpZiAoIWRlc2M/LmxldmVscz8ubGVuZ3RoKSB7XG4gICAgICBjb25zdCBibXAgPSBhd2FpdCB0aGlzLmZldGNoSW1hZ2UoaW5mbywgeik7XG4gICAgICByZXR1cm4gY2h1bmtUb0JhbmRzKHsgd2lkdGg6IGJtcC53aWR0aCwgaGVpZ2h0OiBibXAuaGVpZ2h0LCBkYXRhOiBibXAgfSk7XG4gICAgfVxuICAgIGNvbnN0IGltZyA9IGF3YWl0IHJlYWRMZXZlbCh0aGlzLnRpbGVkU291cmNlKGRlc2MsIHVuZGVmaW5lZCwgNCksIHosIHtcbiAgICAgIG1heFRpbGVzLFxuICAgICAgbWF4VGV4dHVyZURpbTogTUFYX1RFWFRVUkVfRElNLFxuICAgICAgY29uY3VycmVuY3k6IFRJTEVfRkVUQ0hfQ09OQ1VSUkVOQ1ksXG4gICAgfSk7XG4gICAgcmV0dXJuIHNwbGl0UmdiQmFuZHMoeyBkYXRhOiBpbWcuZGF0YSBhcyBVaW50OEFycmF5LCB3aWR0aDogaW1nLndpZHRoLCBoZWlnaHQ6IGltZy5oZWlnaHQgfSk7XG4gIH1cblxuICAvKiogRmV0Y2ggc2xpY2UgYHpgIGFuZCByZWFkIGl0IGJhY2sgYXMgYSBzaW5nbGUtY2hhbm5lbCB1aW50OCBwbGFuZSAoc2VlIHtAbGluayByZWFkUGxhbmV9KSxcbiAgICogIHdpdGhpbiBgYnVkZ2V0VGlsZXNgOiB0aGUgMkQgc3RpdGNoIGZhbGxiYWNrIHJlYWRzIGF0IHRoZSBmdWxsIGJ1ZGdldCwgdGhlIGhpc3RvZ3JhbSBzYW1wbGVzXG4gICAqICBhdCAxIGZvciBhIGNoZWFwIG92ZXJ2aWV3IHRpbGUuICovXG4gIGFzeW5jIGZldGNoQ2hhbm5lbERhdGEoXG4gICAgaW5mbzogSUltYWdlSW5mbyB8IHVuZGVmaW5lZCxcbiAgICB6OiBudW1iZXIsXG4gICAgY2hhbm5lbD86IG51bWJlcixcbiAgICBidWRnZXRUaWxlczogbnVtYmVyID0gTUFYX1NUSVRDSF9USUxFUyxcbiAgKTogUHJvbWlzZTxMdW1hUGxhbmU+IHtcbiAgICByZXR1cm4gdGhpcy5yZWFkUGxhbmUoaW5mbywgeiwgY2hhbm5lbCwgeyBtYXhUaWxlczogYnVkZ2V0VGlsZXMgfSk7XG4gIH1cblxuICAvKiogVGlsZSBidWRnZXQgdG8gc3RpdGNoIGEgd2hvbGUgc2xpY2UgYXQgfmB0YXJnZXRQeGAgcmVzb2x1dGlvbiBmcm9tIHRoZSBweXJhbWlkLiBTaGFyZWQgYnkgdGhlXG4gICAqICBzdXJmYWNlIHBsYW5lIGZldGNoIGFuZCB0aGUgdm9sdW1lIGFzc2VtYmx5LCBzbyBib3RoIGNhcCB0aGVpciByZWFkcyBieSB0aGUgZGVjaW1hdGUgZmFjdG9yLiAqL1xuICB0aWxlQnVkZ2V0Rm9yKGluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQsIHRhcmdldFB4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IHRpbGVTaXplID0gdGhpcy5jdXJyZW50RGVzY3JpcHRvcihpbmZvKT8udGlsZVNpemUgfHwgVElMRV9TSVpFO1xuICAgIHJldHVybiBNYXRoLm1pbihNQVhfU1RJVENIX1RJTEVTLCBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKCh0YXJnZXRQeCAvIHRpbGVTaXplKSAqKiAyICogU1RJVENIX0JVREdFVF9DT0VGRikpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZCBhIHB5cmFtaWRhbCBUaWxlZFNvdXJjZSBiYWNrZWQgYnkgdGhlIHNlcnZlciBgL3RpbGVgIGVuZHBvaW50LiBgY2hhbm5lbGAgc2VsZWN0cyBhIGJhbmRcbiAgICogKGdyYXlzY2FsZSBsdW1pbmFuY2UsIHJlYWwgbGV2ZWxzIG9ubHkpOyBvbWl0IGl0IGZvciB0aGUgY29tcG9zaXRlIChSR0JBLCBhbGwgbGV2ZWxzKS5cbiAgICpcbiAgICogRm9yIHRoZSAyRCB2aWV3LCBgc2NlbmVgIGlzIHRoZSBzaWduYWwgb2YgdGhlIHJlbmRlciB0aGF0IGFza2VkIGZvciBpdCwgdGFrZW4gYmVmb3JlIHRoYXRcbiAgICogcmVuZGVyJ3MgYXdhaXRzLCBhbmQgYG9uVGlsZWAgaXMgdG9sZCBvZiBlYWNoIHRpbGUgcmVxdWVzdCBhbmQgaGFuZGVkIGJhY2sgdGhlIHRpbGUncyBlbmQuIEFcbiAgICogcmVxdWVzdCBpc3N1ZWQgb25jZSBgc2NlbmVgIGlzIGFib3J0ZWQgKGEgZGlzcG9zZWQgc291cmNlIHN0aWxsIGZldGNoaW5nIGFmdGVyIGEgcmVzZXQpIGlzIG5vdFxuICAgKiByZXBvcnRlZCwgYW5kIG9uZSB0aGF0IHNldHRsZXMgYWZ0ZXIgaXQgZG9lcyBub3QgZW5kLCBzbyBpdCBjYW4gbmV2ZXIgY291bnQgdG93YXJkIHRoZSBuZXh0XG4gICAqIHNjZW5lLiBBIHRpbGUgcmVxdWVzdCdzIG93biBgc2lnbmFsYCAobmFwYXJpLWpzIGFib3J0cyBhIHJlYWQgaXQgbm8gbG9uZ2VyIG5lZWRzKSBhYm9ydHMgdGhlXG4gICAqIEhUVFAgcmVxdWVzdC5cbiAgICovXG4gIHRpbGVkU291cmNlKFxuICAgIGRlc2M6IFRpbGVEZXNjcmlwdG9yLFxuICAgIGNoYW5uZWw6IG51bWJlciB8IHVuZGVmaW5lZCxcbiAgICBjaGFubmVsczogMSB8IDQsXG4gICAgc2NlbmU/OiBBYm9ydFNpZ25hbCxcbiAgICBvblRpbGU/OiAoKSA9PiAoKSA9PiB2b2lkLFxuICApOiBUaWxlZFNvdXJjZSB7XG4gICAgY29uc3QgaW5mb0I2NCA9IHRoaXMudGlsZXMuZ2V0U2VsZWN0ZWRJbmZvQjY0KCkgPz8gJyc7XG4gICAgLy8gUGVyLWNoYW5uZWwgdGlsZXMgZXhpc3Qgb25seSBhdCBSRUFMIEJpby1Gb3JtYXRzIGxldmVsczsgdGhlIGNvbXBvc2l0ZSBleGlzdHMgYXQgYWxsIGxldmVscy5cbiAgICBjb25zdCB1c2FibGUgPVxuICAgICAgY2hhbm5lbCA9PSBudWxsID8gZGVzYy5sZXZlbHMgOiBkZXNjLmxldmVscy5zbGljZSgwLCBNYXRoLm1heCgxLCBkZXNjLnJlYWxMZXZlbHMgPz8gZGVzYy5sZXZlbHMubGVuZ3RoKSk7XG4gICAgY29uc3QgbGV2ZWxTY2FsZXMgPSB1c2FibGUubWFwKChsKSA9PiBkZXNjLndpZHRoIC8gTWF0aC5tYXgoMSwgbC53aWR0aCkpOyAvLyBsZXZlbC0wIHB4IHBlciBsZXZlbCBweFxuICAgIGNvbnN0IHRpbGVTaXplID0gZGVzYy50aWxlU2l6ZSB8fCBUSUxFX1NJWkU7XG4gICAgY29uc3QgYXBpID0gdGhpcy5hcGk7XG4gICAgcmV0dXJuIHtcbiAgICAgIGtpbmQ6ICd0aWxlZCcsXG4gICAgICB3aWR0aDogZGVzYy53aWR0aCxcbiAgICAgIGhlaWdodDogZGVzYy5oZWlnaHQsXG4gICAgICB0aWxlU2l6ZSxcbiAgICAgIGxldmVsczogdXNhYmxlLmxlbmd0aCxcbiAgICAgIGxldmVsU2NhbGVzLFxuICAgICAgZGVwdGg6IE1hdGgubWF4KDEsIGRlc2MueiB8fCAxKSxcbiAgICAgIGNoYW5uZWxzLFxuICAgICAgZHR5cGU6ICd1aW50OCcsXG4gICAgICBmZXRjaFRpbGU6IGFzeW5jIChrZXk6IFRpbGVLZXksIHNpZ25hbD86IEFib3J0U2lnbmFsKTogUHJvbWlzZTxQaXhlbENodW5rPiA9PiB7XG4gICAgICAgIGNvbnN0IHJlcyA9IHVzYWJsZVtrZXkubGV2ZWxdPy5yZXMgPz8ga2V5LmxldmVsO1xuICAgICAgICBjb25zdCB1cmwgPSBidWlsZFRpbGVVcmwoYXBpLCBpbmZvQjY0LCB7XG4gICAgICAgICAgcmVzLFxuICAgICAgICAgIGNvbDoga2V5LmNvbCxcbiAgICAgICAgICByb3c6IGtleS5yb3csXG4gICAgICAgICAgejoga2V5LnosXG4gICAgICAgICAgdGlsZVNpemUsXG4gICAgICAgICAgY2hhbm5lbCxcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IGVuZCA9ICFvblRpbGUgfHwgc2NlbmU/LmFib3J0ZWQgPyBudWxsIDogb25UaWxlKCk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IGZldGNoV2l0aEF1dGgodGhpcy50aWxlcywgdXJsLCBzaWduYWwgPyB7IHNpZ25hbCB9IDoge30pO1xuICAgICAgICAgIGlmICghcmVzcC5vaykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBbbmFwYXJpLWpzXSB0aWxlICR7a2V5LmxldmVsfS8ke2tleS5jb2x9LyR7a2V5LnJvd30g4oaSICR7cmVzcC5zdGF0dXN9YCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IGJtcCA9IGF3YWl0IGNyZWF0ZUltYWdlQml0bWFwKGF3YWl0IHJlc3AuYmxvYigpKTtcbiAgICAgICAgICBpZiAoY2hhbm5lbHMgPT09IDQpIHJldHVybiB7IHdpZHRoOiBibXAud2lkdGgsIGhlaWdodDogYm1wLmhlaWdodCwgZGF0YTogYm1wIH07XG4gICAgICAgICAgcmV0dXJuIGJpdG1hcFRvTHVtaW5hbmNlKGJtcCk7XG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgaWYgKCFzY2VuZT8uYWJvcnRlZCkgZW5kPy4oKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBjb21wb3NpdGUgcHlyYW1pZCdzIFIsIEcgYW5kIEIgYmFuZHMgYXMgdGhyZWUgc2NhbGFyIHtAbGluayBUaWxlZFNvdXJjZX1zIOKAlCBvbmUgcGVyXG4gICAqIGFkZGl0aXZlIGxheWVyLCBzbyBlYWNoIGJhbmQgdGFrZXMgaXRzIG93biB3aW5kb3csIGdhbW1hLCBpbnZlcnQsIHRpbnQgYW5kIHZpc2liaWxpdHkg4oCUIHRoYXRcbiAgICogc2hhcmUgT05FIGAvdGlsZWAgcmVxdWVzdCBwZXIgdGlsZS4gVGhlIHRocmVlIGxheWVycyBhc2sgZm9yIGEgdGlsZSBpbiB0aGUgc2FtZSBmcmFtZTogdGhlXG4gICAqIGZpcnN0IGZldGNoZXMgdGhlIFJHQkEgdGlsZSBhbmQgc3BsaXRzIGl0LCB0aGUgb3RoZXIgdHdvIHRha2UgdGhlaXIgYmFuZCBmcm9tIHRoZSBzYW1lXG4gICAqIHByb21pc2UsIGFuZCB0aGUgc3BsaXQgdGlsZSBpcyBkcm9wcGVkIG9uY2UgYWxsIHRocmVlIGhhdmUgaXQuIEEgYmFuZCB0aGF0IGFza3MgbGF0ZXIgKG9uZVxuICAgKiBzaG93biBhZ2FpbiBhZnRlciBiZWluZyBoaWRkZW4pIGZpbmRzIGl0IGluIGEgc21hbGwgTFJVLCBlbHNlIGZldGNoZXMgaXQgYWdhaW4uIGBzY2VuZWAgYW5kXG4gICAqIGBvblRpbGVgIGFzIHtAbGluayB0aWxlZFNvdXJjZX06IHRoZSBsb2FkaW5nIGJhZGdlIGNvdW50cyB0aGUgb25lIHJlcXVlc3QuXG4gICAqL1xuICByZ2JCYW5kU291cmNlcyhkZXNjOiBUaWxlRGVzY3JpcHRvciwgc2NlbmU/OiBBYm9ydFNpZ25hbCwgb25UaWxlPzogKCkgPT4gKCkgPT4gdm9pZCk6IFRpbGVkU291cmNlW10ge1xuICAgIGNvbnN0IGNvbXBvc2l0ZSA9IHRoaXMudGlsZWRTb3VyY2UoZGVzYywgdW5kZWZpbmVkLCA0LCBzY2VuZSwgb25UaWxlKTtcbiAgICBjb25zdCBzcGxpdCA9IG5ldyBMcnVDYWNoZTx7IGJhbmRzOiBQcm9taXNlPEx1bWFQbGFuZVtdPjsgdGFrZW46IFNldDxudW1iZXI+IH0+KFJHQl9CQU5EX0NBQ0hFX1RJTEVTKTtcbiAgICBjb25zdCBiYW5kID0gKGtleTogVGlsZUtleSwgYjogbnVtYmVyKTogUHJvbWlzZTxQaXhlbENodW5rPiA9PiB7XG4gICAgICBjb25zdCBpZCA9IGAke2tleS56fS8ke2tleS5sZXZlbH0vJHtrZXkuY29sfS8ke2tleS5yb3d9YDtcbiAgICAgIGxldCBlbnRyeSA9IHNwbGl0LmdldChpZCk7XG4gICAgICBpZiAoIWVudHJ5KSB7XG4gICAgICAgIGNvbnN0IGJhbmRzID0gY29tcG9zaXRlLmZldGNoVGlsZShrZXkpLnRoZW4oY2h1bmtUb0JhbmRzKTtcbiAgICAgICAgY29uc3QgZnJlc2ggPSB7IGJhbmRzLCB0YWtlbjogbmV3IFNldDxudW1iZXI+KCkgfTtcbiAgICAgICAgLy8gQSBmYWlsZWQgdGlsZSBpcyBub3QgcmVtZW1iZXJlZDogdGhlIG5leHQgYXNrIGZldGNoZXMgaXQgYWdhaW4uXG4gICAgICAgIGJhbmRzLmNhdGNoKCgpID0+IHNwbGl0LmdldChpZCkgPT09IGZyZXNoICYmIHNwbGl0LmRlbGV0ZShpZCkpO1xuICAgICAgICBzcGxpdC5zZXQoaWQsIChlbnRyeSA9IGZyZXNoKSk7XG4gICAgICB9XG4gICAgICBlbnRyeS50YWtlbi5hZGQoYik7XG4gICAgICBpZiAoZW50cnkudGFrZW4uc2l6ZSA9PT0gMykgc3BsaXQuZGVsZXRlKGlkKTtcbiAgICAgIHJldHVybiBlbnRyeS5iYW5kcy50aGVuKChwbGFuZXMpID0+IHBsYW5lc1tiXSk7XG4gICAgfTtcbiAgICByZXR1cm4gWzAsIDEsIDJdLm1hcCgoYik6IFRpbGVkU291cmNlID0+ICh7XG4gICAgICAuLi5jb21wb3NpdGUsXG4gICAgICBjaGFubmVsczogMSxcbiAgICAgIGZldGNoVGlsZTogKGtleTogVGlsZUtleSkgPT4gYmFuZChrZXksIGIpLFxuICAgIH0pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBc3NlbWJsZSBhIGRvd25zYW1wbGVkIHVpbnQ4IHZvbHVtZSAobHVtaW5hbmNlKSBvZiBgaW5mb2AncyBzdGFjayB3aXRoIG5hcGFyaS1qcydzXG4gICAqIGBhc3NlbWJsZVZvbHVtZWA6IGV2ZXJ5IGBzbGljZVN0ZXBgdGggc2xpY2UsIHJlYWQgZnJvbSB0aGUgYmFuZCdzIChvciBjb21wb3NpdGUncykgcHlyYW1pZCBhdFxuICAgKiB0aGUgbGV2ZWwgdGhhdCBgbWF4U2xpY2VgIG5lZWRzLCB3aXRoaW4gaXRzIHRpbGUgYnVkZ2V0LCBhbmQgYXJlYS1hdmVyYWdlZCB0byBgbWF4U2xpY2VgIG9uXG4gICAqIHRoZSBsb25nZXN0IHNpZGUuIFNsaWNlcyBhcmUgcmVhZCB3aXRoIGJvdW5kZWQgY29uY3VycmVuY3kgKHRoZSBjb25uZWN0aW9uIHBvb2wgc3RheXMgZnVsbFxuICAgKiB3aXRob3V0IGZsb29kaW5nIGl0IG9uIGEgZGVlcCBzdGFjayksIGVhY2ggcmVwb3J0aW5nIGBwcm9ncmVzc2AgKDDigJMxMDApIGFzIGl0IGxhbmRzIHNvIHRoZSBob3N0XG4gICAqIHNob3dzIGEgZGV0ZXJtaW5hdGUgcHJvZ3Jlc3MgYmFyLiBOdWxsIHdoZW4gYHNpZ25hbGAgYWJvcnRzIChhIENhbmNlbCBvciBhIG5ldyBwbG90KSwgd2hpY2hcbiAgICogYWxzbyBhYm9ydHMgdGhlIHRpbGUgcmVxdWVzdHMgaW4gZmxpZ2h0IOKAlCBhIHBhcnRpYWwgdm9sdW1lIGlzIG5ldmVyIHJldHVybmVkLlxuICAgKi9cbiAgYXN5bmMgYXNzZW1ibGVWb2x1bWUoXG4gICAgaW5mbzogSUltYWdlSW5mbyB8IHVuZGVmaW5lZCxcbiAgICBvcHRzOiB7IG1heFNsaWNlPzogbnVtYmVyOyBzbGljZVN0ZXA/OiBudW1iZXIgfSxcbiAgICBjaGFubmVsOiBudW1iZXIgfCB1bmRlZmluZWQsXG4gICAgaG9va3M6IHsgc2lnbmFsOiBBYm9ydFNpZ25hbDsgcHJvZ3Jlc3MocGVyY2VudDogbnVtYmVyKTogdm9pZCB9LFxuICApOiBQcm9taXNlPEFzc2VtYmxlZFZvbHVtZSB8IG51bGw+IHtcbiAgICBjb25zdCB7IHNpZ25hbCB9ID0gaG9va3M7XG4gICAgY29uc3QgZnVsbERlcHRoID0gc3RhY2tEZXB0aChpbmZvKSB8fCAxO1xuICAgIGNvbnN0IHN0ZXAgPSBNYXRoLm1heCgxLCBNYXRoLmZsb29yKG9wdHMuc2xpY2VTdGVwID8/IDEpKTtcbiAgICBjb25zdCBtYXhTbGljZSA9IG9wdHMubWF4U2xpY2UgPz8gVk9MVU1FX01BWF9TTElDRTtcbiAgICAvLyBTb3VyY2Utc2xpY2UgaW5kaWNlcyBzYW1wbGVkIGludG8gdGhlIHZvbHVtZSAoZXZlcnkgYHN0ZXBgdGggcGxhbmUg4oaSIGxvdy1yZXMgaXMgZmFzdGVyKS5cbiAgICBjb25zdCB6SW5kaWNlczogbnVtYmVyW10gPSBbXTtcbiAgICBmb3IgKGxldCB6ID0gMDsgeiA8IGZ1bGxEZXB0aDsgeiArPSBzdGVwKSB6SW5kaWNlcy5wdXNoKHopO1xuXG4gICAgaG9va3MucHJvZ3Jlc3MoMCk7XG4gICAgdHJ5IHtcbiAgICAgIC8vIGBjaGFubmVsYCBzZWxlY3RzIGEgYmFuZCAobXVsdGljaGFubmVsIHZvbHVtZSk7IG9taXQgZm9yIHRoZSBncmF5c2NhbGUgY29tcG9zaXRlLiBUaGVcbiAgICAgIC8vIGNhbGxlciBvd25zIHRoZSBzdGFja0xvYWRpbmcgZmxhZyAobXVsdGljaGFubmVsIGFzc2VtYmxlcyBjaGFubmVscyBpbiB0dXJuKS5cbiAgICAgIGNvbnN0IHNvdXJjZSA9IGF3YWl0IHRoaXMudm9sdW1lU291cmNlKGluZm8sIGNoYW5uZWwsIHpJbmRpY2VzWzBdKTtcbiAgICAgIHJldHVybiBhd2FpdCBhc3NlbWJsZVZvbHVtZShzb3VyY2UsIHpJbmRpY2VzLCB7XG4gICAgICAgIG1heFNpZGU6IG1heFNsaWNlLFxuICAgICAgICBtYXhUaWxlczogdGhpcy50aWxlQnVkZ2V0Rm9yKGluZm8sIG1heFNsaWNlKSxcbiAgICAgICAgbWF4VGV4dHVyZURpbTogTUFYX1RFWFRVUkVfRElNLFxuICAgICAgICBjb25jdXJyZW5jeTogVk9MVU1FX0ZFVENIX0NPTkNVUlJFTkNZLFxuICAgICAgICB0aWxlQ29uY3VycmVuY3k6IFRJTEVfRkVUQ0hfQ09OQ1VSUkVOQ1ksXG4gICAgICAgIG9uUHJvZ3Jlc3M6IChkb25lLCB0b3RhbCkgPT4gaG9va3MucHJvZ3Jlc3MoTWF0aC5yb3VuZCgoZG9uZSAvIHRvdGFsKSAqIDEwMCkpLFxuICAgICAgICBzaWduYWwsXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGlmIChzaWduYWwuYWJvcnRlZCkgcmV0dXJuIG51bGw7IC8vIGNhbmNlbGxlZCDihpIgZG9uJ3QgcmVuZGVyIGEgcGFydGlhbCB2b2x1bWVcbiAgICAgIHRocm93IGVycjtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgaG9va3MucHJvZ3Jlc3MoMCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBzY2FsYXIgc291cmNlIGEgdm9sdW1lIHJlYWRzOiB0aGUgYmFuZCdzIChvciBjb21wb3NpdGUncykgcHlyYW1pZDsgd2l0aG91dCBvbmUsIG9uZSB3aG9sZVxuICAgKiBpbWFnZSBwZXIgc2xpY2UgKHtAbGluayBmZXRjaEltYWdlfSkgYXMgYSBzaW5nbGUtdGlsZSBsZXZlbCBzaXplZCBieSB0aGUgZmlyc3Qgc2xpY2UsIHdoaWNoIGlzXG4gICAqIGZldGNoZWQgaGVyZSBhbmQgc2VydmVkIGFnYWluIGFzIHRoYXQgc2xpY2UncyB0aWxlLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB2b2x1bWVTb3VyY2UoaW5mbzogSUltYWdlSW5mbyB8IHVuZGVmaW5lZCwgY2hhbm5lbDogbnVtYmVyIHwgdW5kZWZpbmVkLCBmaXJzdFo6IG51bWJlcikge1xuICAgIGNvbnN0IGRlc2MgPSB0aGlzLnNpbXBsZVN0YWNrLmlzU2ltcGxlKGluZm8pID8gbnVsbCA6IGF3YWl0IHRoaXMuZW5zdXJlRGVzY3JpcHRvcihpbmZvKTtcbiAgICBpZiAoZGVzYz8ubGV2ZWxzPy5sZW5ndGgpIHJldHVybiB0aGlzLnRpbGVkU291cmNlKGRlc2MsIGNoYW5uZWwsIDEpO1xuICAgIGxldCBmaXJzdDogSW1hZ2VCaXRtYXAgfCBudWxsID0gYXdhaXQgdGhpcy5mZXRjaEltYWdlKGluZm8sIGZpcnN0WiwgY2hhbm5lbCk7XG4gICAgY29uc3Qgc291cmNlOiBUaWxlZFNvdXJjZSA9IHtcbiAgICAgIGtpbmQ6ICd0aWxlZCcsXG4gICAgICB3aWR0aDogZmlyc3Qud2lkdGgsXG4gICAgICBoZWlnaHQ6IGZpcnN0LmhlaWdodCxcbiAgICAgIHRpbGVTaXplOiBNYXRoLm1heCgxLCBmaXJzdC53aWR0aCwgZmlyc3QuaGVpZ2h0KSxcbiAgICAgIGxldmVsczogMSxcbiAgICAgIGRlcHRoOiBzdGFja0RlcHRoKGluZm8pIHx8IDEsXG4gICAgICBjaGFubmVsczogMSxcbiAgICAgIGR0eXBlOiAndWludDgnLFxuICAgICAgZmV0Y2hUaWxlOiBhc3luYyAoa2V5OiBUaWxlS2V5KTogUHJvbWlzZTxQaXhlbENodW5rPiA9PiB7XG4gICAgICAgIGxldCBibXA6IEltYWdlQml0bWFwO1xuICAgICAgICBpZiAoZmlyc3QgJiYga2V5LnogPT09IGZpcnN0Wikge1xuICAgICAgICAgIGJtcCA9IGZpcnN0O1xuICAgICAgICAgIGZpcnN0ID0gbnVsbDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBibXAgPSBhd2FpdCB0aGlzLmZldGNoSW1hZ2UoaW5mbywga2V5LnosIGNoYW5uZWwpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7IHdpZHRoOiBibXAud2lkdGgsIGhlaWdodDogYm1wLmhlaWdodCwgZGF0YTogYm1wIH07XG4gICAgICB9LFxuICAgIH07XG4gICAgcmV0dXJuIHNvdXJjZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTbGljZSBgemAgYXMgYSBzaW5nbGUgV0hPTEUtaW1hZ2UgbHVtaW5hbmNlIHBsYW5lIChkZWNpbWF0ZWQgdG8gYG1heEdyaWRgKSwgZm9yIHRoZSBzdXJmYWNlLlxuICAgKiBQcmVmZXJzIHRoZSBzZXJ2ZXIgcHlyYW1pZCwgcmVhZCBhdCB0aGUgbGV2ZWwgdGhlIGdyaWQgbmVlZHMgKHtAbGluayByZWFkUGxhbmV9LCB3aGljaCBtYXlcbiAgICogZHJvcCBhIGJhbmQgdG8gdGhlIGNvbXBvc2l0ZSBvdmVydmlldyk7IHdpdGhvdXQgYSBkZXNjcmlwdG9yLCB0aGUgYXBwJ3MgY29tcGxldGUgcGVyLXNsaWNlXG4gICAqIGltYWdlIChgdXJsc2AvYHNtYWxsVXJsc2AsIGFzIHRoZSBQbG90bHkgc3VyZmFjZSkg4oCUIG5ldmVyIGEgbG9uZSB0b3AtbGVmdCB0aWxlLCB3aGljaCB3b3VsZCBiZVxuICAgKiBhIGNvcm5lciBvZiB0aGUgc2xpY2Ug4oCUIGFuZCBvbmx5IGFzIGEgbGFzdCByZXNvcnQgdGhlIHNpbmdsZS10aWxlIGZldGNoLlxuICAgKi9cbiAgYXN5bmMgZmV0Y2hQbGFuZShcbiAgICBpbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkLFxuICAgIHo6IG51bWJlcixcbiAgICBjaGFubmVsOiBudW1iZXIgfCB1bmRlZmluZWQsXG4gICAgbWF4R3JpZDogbnVtYmVyLFxuICApOiBQcm9taXNlPEx1bWFQbGFuZT4ge1xuICAgIGNvbnN0IGRlc2MgPSBhd2FpdCB0aGlzLmVuc3VyZURlc2NyaXB0b3IoaW5mbyk7XG4gICAgLy8gUHJlZmVycmVkOiB0aGUgV0hPTEUgc2xpY2UgZnJvbSB0aGUgc2VydmVyIHB5cmFtaWQgYXQgYSByZXNvbHV0aW9uIGRyaXZlbiBieSB0aGUgZGVjaW1hdGVcbiAgICAvLyBmYWN0b3Ig4oCUIGEgaGlnaGVyIHRhcmdldCBncmlkIHJlYWRzIGEgRklORVIgcHlyYW1pZCBsZXZlbCAobW9yZSByZWFsIGRldGFpbCkuIFRoaXMgaXMgd2hhdFxuICAgIC8vIG1ha2VzIFwiRnVsbFwiIGFjdHVhbGx5IGhpZ2hlci1yZXMgdGhhbiBcIsK9XCIsIG5vdCBqdXN0IGEgZml4ZWQgcHJldmlldy4gVGhlIHN1cmZhY2UgaXMgYSBzaW5nbGVcbiAgICAvLyBkZWNpbWF0ZWQgcGxhbmUsIHNvIHRoZSBjb21wb3NpdGUgZmFsbGJhY2sgaXMgYWNjZXB0YWJsZSB3aGVuIHRoZSBiYW5kIGhhcyBubyBzbWFsbCBsZXZlbC5cbiAgICBpZiAoZGVzYz8ubGV2ZWxzPy5sZW5ndGgpIHtcbiAgICAgIHJldHVybiB0aGlzLnJlYWRQbGFuZShpbmZvLCB6LCBjaGFubmVsLCB7XG4gICAgICAgIG1heFRpbGVzOiB0aGlzLnRpbGVCdWRnZXRGb3IoaW5mbywgbWF4R3JpZCksXG4gICAgICAgIG1heFNpZGU6IG1heEdyaWQsXG4gICAgICAgIGNvbXBvc2l0ZUZhbGxiYWNrOiB0cnVlLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gRmFsbGJhY2sgKG5vIHB5cmFtaWQpOiB0aGUgYXBwJ3MgQ09NUExFVEUgcGVyLXNsaWNlIGltYWdlICh1cmxzW3pdLCBub3QgdGhlIHNtYWxsIGJsdXJyeVxuICAgIC8vIHRodW1ibmFpbCkg4oCUIHdob2xlIHNsaWNlLCBhdm9pZHMgYSBjb3JuZXIgdGlsZS4gQ2FwcGVkIGF0IHRoZSBpbWFnZSdzIG93biByZXNvbHV0aW9uLlxuICAgIGNvbnN0IHVybCA9IGluZm8/LnVybHM/Llt6XSA/PyBpbmZvPy5zbWFsbFVybHM/Llt6XTtcbiAgICBpZiAodXJsKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXNwID0gYXdhaXQgZmV0Y2hXaXRoQXV0aCh0aGlzLnRpbGVzLCB1cmwpO1xuICAgICAgICBpZiAocmVzcC5vaykgcmV0dXJuIGJpdG1hcFRvTHVtaW5hbmNlKGF3YWl0IGNyZWF0ZUltYWdlQml0bWFwKGF3YWl0IHJlc3AuYmxvYigpKSwgbWF4R3JpZCk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKGBbbmFwYXJpLWpzXSBzdXJmYWNlIHVybCBmZXRjaCBmYWlsZWQgZm9yIHo9JHt6fWAsIGVycik7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gTGFzdCByZXNvcnQgKG5vIHB5cmFtaWQgYW5kIG5vIGNvbXBsZXRlIGltYWdlKTogdGhlIHNpbmdsZSBpbWFnZS5cbiAgICByZXR1cm4gYml0bWFwVG9MdW1pbmFuY2UoYXdhaXQgdGhpcy5mZXRjaEltYWdlKGluZm8sIHosIGNoYW5uZWwpLCBtYXhHcmlkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGFubmVsIGBjaGFubmVsYCdzIG5hdGl2ZS1iaXQtZGVwdGggaGlzdG9ncmFtIGF0IHNsaWNlIGB6YCB3aGVuIGBpbmZvYCBpcyBhID44LWJpdCBpbWFnZSDigJRcbiAgICogdGhlIGRpc3BsYXllZCBwaXhlbHMgYXJlIDgtYml0LCBzbyBhIGNsaWVudCBoaXN0b2dyYW0gd291bGQgYmUgY2xpcHBlZCDigJQgY2FjaGVkIHBlclxuICAgKiBgKHosIGNoYW5uZWwpYDsgbnVsbCBmb3IgYW4gOC1iaXQgY2hhbm5lbCwgd2hpY2ggdGhlIGNhbGxlciBiaW5zIGZyb20gdGhlIGNsaWVudCBkYXRhLlxuICAgKi9cbiAgbmF0aXZlSGlzdG9ncmFtJChcbiAgICBpbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkLFxuICAgIHo6IG51bWJlcixcbiAgICBjaGFubmVsOiBudW1iZXIsXG4gICAgYmluczogbnVtYmVyLFxuICApOiBPYnNlcnZhYmxlPElIaXN0b2dyYW0gfCBudWxsPiB8IG51bGwge1xuICAgIGNvbnN0IGJpdERlcHRoID0gdGhpcy5jdXJyZW50RGVzY3JpcHRvcihpbmZvKT8uY2hhbm5lbEluZm8/LltjaGFubmVsXT8uYml0RGVwdGggPz8gODtcbiAgICBpZiAoYml0RGVwdGggPD0gOCkgcmV0dXJuIG51bGw7XG4gICAgY29uc3Qga2V5ID0gYCR7en18JHtjaGFubmVsfWA7XG4gICAgY29uc3QgY2FjaGVkID0gdGhpcy5uYXRpdmVIaXN0b2dyYW1zLmdldChrZXkpO1xuICAgIGlmIChjYWNoZWQpIHJldHVybiBvZihjYWNoZWQpO1xuICAgIHJldHVybiBmcm9tKHRoaXMuZmV0Y2hOYXRpdmVIaXN0b2dyYW0oY2hhbm5lbCwgYmlucywgeiwga2V5KSk7XG4gIH1cblxuICAvKiogRmV0Y2ggKyBjYWNoZSBvbmUgY2hhbm5lbCdzIG5hdGl2ZS1iaXQtZGVwdGggaGlzdG9ncmFtIGZyb20gYEdFVCAvaGlzdG9ncmFtYCAoPjgtYml0KSxcbiAgICogIHRocm91Z2ggdGhlIHNoYXJlZCBqaXQtc2VydmljZSBjbGllbnQg4oCUIHdob3NlIFVSTCBjYXJyaWVzIHRoZSBwZXItYXBwLWxvYWQgY2FjaGUtYnVzdGVyXG4gICAqICB0aGlzIGJhY2tlbmQgdXNlZCB0byBvbWl0LCBzbyBhIHJlbG9hZCBzaG93ZWQgdGhlIHNlcnZlcidzIDI0IGgtY2FjaGVkIGhpc3RvZ3JhbS4gKi9cbiAgYXN5bmMgZmV0Y2hOYXRpdmVIaXN0b2dyYW0oY2hhbm5lbDogbnVtYmVyLCBiaW5zOiBudW1iZXIsIHo6IG51bWJlciwga2V5OiBzdHJpbmcpOiBQcm9taXNlPElIaXN0b2dyYW0gfCBudWxsPiB7XG4gICAgY29uc3QgaW5mb0I2NCA9IHRoaXMudGlsZXMuZ2V0U2VsZWN0ZWRJbmZvQjY0KCk7XG4gICAgaWYgKCFpbmZvQjY0KSByZXR1cm4gbnVsbDtcbiAgICB0cnkge1xuICAgICAgY29uc3Qgb3V0ID0gYXdhaXQgbmF0aXZlSGlzdG9ncmFtKGZldGNoSnNvbldpdGhBdXRoKHRoaXMudGlsZXMpLCB0aGlzLmFwaSwgaW5mb0I2NCwge1xuICAgICAgICB6LFxuICAgICAgICBjaGFubmVsLFxuICAgICAgICBiaW5zLFxuICAgICAgfSk7XG4gICAgICBpZiAoIW91dCkgcmV0dXJuIG51bGw7IC8vIDIwMiAoc3RpbGwgY2FjaGluZykgb3IgdHJhbnNpZW50IOKGkiBudWxsOyB0aGUgcGFuZSByZXRyaWVzXG4gICAgICB0aGlzLm5hdGl2ZUhpc3RvZ3JhbXMuc2V0KGtleSwgb3V0KTtcbiAgICAgIHJldHVybiBvdXQ7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ1tuYXBhcmktanNdIG5hdGl2ZSBoaXN0b2dyYW0gZmV0Y2ggZmFpbGVkJywgZXJyKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBOYXRpdmUtYml0LWRlcHRoICgxNi8zMi1iaXQpIG11bHRpLWJhbmQgVElGRiBleHBvcnQgb2Ygc2xpY2UgYHpgIHZpYSB0aGUgc2VydmVyXG4gICAqICBgL2V4cG9ydC90aWZmYCBlbmRwb2ludCDigJQgdGhlIGRpc3BsYXllZCBQTkcgaXMgYW4gOC1iaXQgZmlndXJlLCB0aGlzIHByZXNlcnZlcyB0aGUgdHJ1ZSBwaXhlbFxuICAgKiAgdmFsdWVzLiBWaXNpYmxlIGNoYW5uZWxzIG9ubHkgKG9taXR0ZWQgd2hlbiBhbGwgYXJlIHZpc2libGUg4oaSIHNlcnZlciBkZWZhdWx0KS4gTWlycm9ycyB0aGVcbiAgICogIE9TRCBiYWNrZW5kLiAqL1xuICBhc3luYyBleHBvcnRUaWZmKHo6IG51bWJlciwgc3RhdGVzOiBJQ2hhbm5lbFN0YXRlW10sIGZpbGVOYW1lOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBpbmZvQjY0ID0gdGhpcy50aWxlcy5nZXRTZWxlY3RlZEluZm9CNjQoKTtcbiAgICBpZiAoIWluZm9CNjQpIHJldHVybjtcbiAgICBjb25zdCB2aXNpYmxlID0gc3RhdGVzLmZpbHRlcigoYykgPT4gYy52aXNpYmxlKS5tYXAoKGMpID0+IGMuaW5kZXgpO1xuICAgIGNvbnN0IHVybCA9IGV4cG9ydFRpZmZVcmwodGhpcy5hcGksIGluZm9CNjQsIHosIHZpc2libGUsIHN0YXRlcy5sZW5ndGgpO1xuICAgIGNvbnN0IHNhdmVOYW1lID0gZXhwb3J0VGlmZkZpbGVuYW1lKGZpbGVOYW1lKTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IGZldGNoV2l0aEF1dGgodGhpcy50aWxlcywgdXJsKTtcbiAgICAgIGlmIChyZXNwLnN0YXR1cyA9PT0gMjAyKSB7XG4gICAgICAgIGNvbnNvbGUud2FybignW25hcGFyaS1qc10gMTYtYml0IGV4cG9ydDogZmlsZSBzdGlsbCBjYWNoaW5nIOKAlCB0cnkgYWdhaW4gc2hvcnRseS4nKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKCFyZXNwLm9rKSB0aHJvdyBuZXcgRXJyb3IoYHN0YXR1cyAke3Jlc3Auc3RhdHVzfWApO1xuICAgICAgc2F2ZUFzKGF3YWl0IHJlc3AuYmxvYigpLCBzYXZlTmFtZSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ1tuYXBhcmktanNdIDE2LWJpdCBUSUZGIGV4cG9ydCBmYWlsZWQnLCBlcnIpO1xuICAgIH1cbiAgfVxufVxuIl19
|