@jax-data-science/sci-image-visualizer 0.8.2 → 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 +361 -4184
- 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 -34430
- 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 -878
- 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,153 @@
|
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
2
|
+
import { searchGeneNames } from '../spatial/gene-search';
|
|
3
|
+
import { geneOption } from '../spatial/spatial-panel-model';
|
|
4
|
+
import { Supersede } from '../util/supersede';
|
|
5
|
+
/**
|
|
6
|
+
* The gene list behind every gene dropdown of the spatial-omics panel — "Colour by gene",
|
|
7
|
+
* the cells' gene colouring, and the transcript genes. One model for all three: they share
|
|
8
|
+
* one list and one search, so each opens on the head of the full list rather than on
|
|
9
|
+
* whatever another was last filtered to, and a whole-transcriptome list is fetched once.
|
|
10
|
+
*
|
|
11
|
+
* Every gene the dataset inlined is held as plain strings and searched per keystroke. A
|
|
12
|
+
* whole-transcriptome dataset inlines ~18k names — cheap to keep, ruinous to hand a
|
|
13
|
+
* dropdown all at once — so the options are only ever the best few hundred of them.
|
|
14
|
+
*
|
|
15
|
+
* Plain class, no Angular: the owner passes `run`, which re-enters the zone, and the
|
|
16
|
+
* dropdowns read {@link state$} through the `async` pipe, so an OnPush panel re-renders
|
|
17
|
+
* when a search lands.
|
|
18
|
+
*/
|
|
19
|
+
export class GenePickerModel {
|
|
20
|
+
/** Most names fetched for the lazy list — well above any panel, whole-transcriptome included. */
|
|
21
|
+
static { this.LIST_MAX = 100_000; }
|
|
22
|
+
/** Names asked of the port per keystroke while the list is not resident. */
|
|
23
|
+
static { this.SEARCH_LIMIT = 50; }
|
|
24
|
+
/**
|
|
25
|
+
* @param controls the port, or null when none is bound.
|
|
26
|
+
* @param chosen the genes already chosen anywhere, kept among the options: a multi-select
|
|
27
|
+
* shows a chip only for a value it can find among its options.
|
|
28
|
+
* @param run where async results are applied — the owner's `NgZone.run`.
|
|
29
|
+
*/
|
|
30
|
+
constructor(controls, chosen, run = (fn) => fn()) {
|
|
31
|
+
this.controls = controls;
|
|
32
|
+
this.chosen = chosen;
|
|
33
|
+
this.run = run;
|
|
34
|
+
this.state$ = new BehaviorSubject({ options: [], remote: false, failed: false });
|
|
35
|
+
this.names = [];
|
|
36
|
+
this.listLoading = false;
|
|
37
|
+
/** Guards the typeahead: typing outruns the lookup. */
|
|
38
|
+
this.search = new Supersede();
|
|
39
|
+
/** Guards the whole-transcriptome list preload against a dataset switch. */
|
|
40
|
+
this.listLoad = new Supersede();
|
|
41
|
+
}
|
|
42
|
+
get options() {
|
|
43
|
+
return this.state$.value.options;
|
|
44
|
+
}
|
|
45
|
+
get remote() {
|
|
46
|
+
return this.state$.value.remote;
|
|
47
|
+
}
|
|
48
|
+
get failed() {
|
|
49
|
+
return this.state$.value.failed;
|
|
50
|
+
}
|
|
51
|
+
/** Genes resident in the list (the inlined names, or the fetched list once loaded). */
|
|
52
|
+
get residentCount() {
|
|
53
|
+
return this.names.length;
|
|
54
|
+
}
|
|
55
|
+
/** A new dataset: its own names (or none), and nothing in flight for the previous one. */
|
|
56
|
+
setDataset(dataset) {
|
|
57
|
+
// A gene search or list preload still in flight answers for the previous dataset.
|
|
58
|
+
this.search.cancel();
|
|
59
|
+
this.listLoad.cancel();
|
|
60
|
+
this.listLoading = false;
|
|
61
|
+
const names = dataset?.features?.names;
|
|
62
|
+
this.names = names ? [...names] : [];
|
|
63
|
+
// The head of the list, not all of it.
|
|
64
|
+
this.set({
|
|
65
|
+
options: searchGeneNames(this.names, '').map(geneOption),
|
|
66
|
+
remote: !!dataset?.features && !names,
|
|
67
|
+
failed: false,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A keystroke in a gene dropdown's filter box.
|
|
72
|
+
*
|
|
73
|
+
* With the names resident, search them here and materialise only the top matches: the
|
|
74
|
+
* dropdown's own filter then runs over those and agrees — they were chosen by the same
|
|
75
|
+
* query — so the control behaves as if it still held the whole list. Without them there
|
|
76
|
+
* is nothing to filter, so the query goes to the port and its answer BECOMES the option
|
|
77
|
+
* list — the same control, filtering one hop further away.
|
|
78
|
+
*/
|
|
79
|
+
async onFilter(query) {
|
|
80
|
+
if (!this.remote) {
|
|
81
|
+
this.set({ options: this.withChosen(searchGeneNames(this.names, query)), failed: false });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
this.set({ failed: false });
|
|
85
|
+
const controls = this.controls();
|
|
86
|
+
if (!controls)
|
|
87
|
+
return;
|
|
88
|
+
// A slow answer for an earlier query would replace the options for the text now in
|
|
89
|
+
// the box — including a failure, which would wrongly mark the current query as failed.
|
|
90
|
+
const current = this.search.next();
|
|
91
|
+
if (!query) {
|
|
92
|
+
this.set({ options: [] });
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const names = await controls.searchFeatures(query, GenePickerModel.SEARCH_LIMIT);
|
|
97
|
+
if (!current())
|
|
98
|
+
return;
|
|
99
|
+
this.run(() => this.set({ options: this.withChosen(names) }));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
if (!current())
|
|
103
|
+
return;
|
|
104
|
+
// A failed lookup must not wedge the control — show none and say so.
|
|
105
|
+
this.run(() => this.set({ options: this.withChosen([]), failed: true }));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A gene dropdown opened.
|
|
110
|
+
*
|
|
111
|
+
* A whole-transcriptome dataset does not inline its ~30k names; they are fetched here,
|
|
112
|
+
* once, on first open — the list is then resident and every keystroke filters locally,
|
|
113
|
+
* exactly as for a targeted panel. Until it arrives the dropdown falls back to asking the
|
|
114
|
+
* server per keystroke.
|
|
115
|
+
*/
|
|
116
|
+
async ensureList() {
|
|
117
|
+
const controls = this.controls();
|
|
118
|
+
if (this.remote && controls && !this.listLoading) {
|
|
119
|
+
this.listLoading = true;
|
|
120
|
+
// A switch to another remote-gene dataset mid-fetch would otherwise hand it this
|
|
121
|
+
// dataset's names as its own.
|
|
122
|
+
const current = this.listLoad.next();
|
|
123
|
+
try {
|
|
124
|
+
const names = await controls.searchFeatures('', GenePickerModel.LIST_MAX);
|
|
125
|
+
if (current() && names.length && this.remote) {
|
|
126
|
+
this.run(() => {
|
|
127
|
+
this.names = names;
|
|
128
|
+
this.set({ remote: false });
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Keep the per-keystroke search; the list is a convenience, not a requirement.
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
if (current())
|
|
137
|
+
this.listLoading = false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
this.run(() => this.set({
|
|
141
|
+
options: this.withChosen(this.remote ? [] : searchGeneNames(this.names, '')),
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
/** Options for `names`, plus the genes already chosen. */
|
|
145
|
+
withChosen(names) {
|
|
146
|
+
const seen = new Set(names);
|
|
147
|
+
return [...this.chosen().filter((g) => !seen.has(g)), ...names].map(geneOption);
|
|
148
|
+
}
|
|
149
|
+
set(patch) {
|
|
150
|
+
this.state$.next({ ...this.state$.value, ...patch });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC1nZW5lLXBpY2tlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvc3BhdGlhbC1jb250cm9scy9zcGF0aWFsLWdlbmUtcGlja2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFJdkMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3pELE9BQU8sRUFBZSxVQUFVLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN6RSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFtQjlDOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLE9BQU8sZUFBZTtJQUMxQixpR0FBaUc7YUFDakYsYUFBUSxHQUFHLE9BQU8sQUFBVixDQUFXO0lBQ25DLDRFQUE0RTthQUM1RCxpQkFBWSxHQUFHLEVBQUUsQUFBTCxDQUFNO0lBV2xDOzs7OztPQUtHO0lBQ0gsWUFDbUIsUUFBdUMsRUFDdkMsTUFBK0IsRUFDL0IsTUFBZ0MsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLEVBQUUsRUFBRTtRQUY1QyxhQUFRLEdBQVIsUUFBUSxDQUErQjtRQUN2QyxXQUFNLEdBQU4sTUFBTSxDQUF5QjtRQUMvQixRQUFHLEdBQUgsR0FBRyxDQUF5QztRQWxCdEQsV0FBTSxHQUFHLElBQUksZUFBZSxDQUFrQixFQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUU5RixVQUFLLEdBQWEsRUFBRSxDQUFDO1FBQ3JCLGdCQUFXLEdBQUcsS0FBSyxDQUFDO1FBQzVCLHVEQUF1RDtRQUN0QyxXQUFNLEdBQUcsSUFBSSxTQUFTLEVBQUUsQ0FBQztRQUMxQyw0RUFBNEU7UUFDM0QsYUFBUSxHQUFHLElBQUksU0FBUyxFQUFFLENBQUM7SUFZekMsQ0FB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Q1osSUFBSSxDQUFDLEdBQUcsQ0FBQztZQUNQLE9BQU8sRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7U0FDN0UsQ0FBQyxDQUNILENBQUM7SUFDSixDQUFDO0lBRUQsMERBQTBEO0lBQ2xELFVBQVUsQ0FBQyxLQUF3QjtRQUN6QyxNQUFNLElBQUksR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUM1QixPQUFPLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUNsRixDQUFDO0lBRU8sR0FBRyxDQUFDLEtBQStCO1FBQ3pDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxHQUFHLEtBQUssRUFBRSxDQUFDLENBQUM7SUFDdkQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgdHlwZSB7IElTcGF0aWFsQ29udHJvbHMgfSBmcm9tICcuLi9jb250cmFjdHMvdmlzdWFsaXplci5jb250cmFjdCc7XG5pbXBvcnQgdHlwZSB7IFNwYXRpYWxEYXRhc2V0IH0gZnJvbSAnLi4vY29udHJhY3RzL3NwYXRpYWwtZGF0YXNldC5jb250cmFjdCc7XG5pbXBvcnQgeyBzZWFyY2hHZW5lTmFtZXMgfSBmcm9tICcuLi9zcGF0aWFsL2dlbmUtc2VhcmNoJztcbmltcG9ydCB7IFBhbmVsT3B0aW9uLCBnZW5lT3B0aW9uIH0gZnJvbSAnLi4vc3BhdGlhbC9zcGF0aWFsLXBhbmVsLW1vZGVsJztcbmltcG9ydCB7IFN1cGVyc2VkZSB9IGZyb20gJy4uL3V0aWwvc3VwZXJzZWRlJztcblxuLyoqIFdoYXQgYSBnZW5lIGRyb3Bkb3duIHNob3dzOiBpdHMgb3B0aW9ucywgYW5kIHdoYXQgYW4gZW1wdHkgbGlzdCBtZWFucy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgR2VuZVBpY2tlclN0YXRlIHtcbiAgLyoqXG4gICAqIFRoZSBvcHRpb25zLiBUaGUgZnVsbCBuYW1lIGxpc3Qgd2hlbiB0aGUgZGF0YXNldCBpbmxpbmVzIGl0IChhIHRhcmdldGVkIHBhbmVsOiA4IGZvclxuICAgKiB0aGUgQUJDIGRlbW8sIDMwMOKAkzUsMDAwIGZvciBYZW5pdW0vQ29zTXgpIOKAlCBvciByYXRoZXIgaXRzIGJlc3QgZmV3IGh1bmRyZWQsIHNlZVxuICAgKiB7QGxpbmsgR2VuZVBpY2tlck1vZGVsfS4gQSB3aG9sZS10cmFuc2NyaXB0b21lIGRhdGFzZXQgZG9lcyBub3Qgc2hpcCBpdHMgfjMxayBuYW1lcywgc29cbiAgICogdGhlcmUgdGhlIGxpc3QgaXMgd2hhdCB0aGUgcG9ydCdzIGxhc3Qgc2VhcmNoIHJldHVybmVkIGFuZCBmaWx0ZXJpbmcgaXMgc2VydmVyLXNpZGUg4oCUXG4gICAqIHNhbWUgY29udHJvbCBlaXRoZXIgd2F5LlxuICAgKi9cbiAgb3B0aW9uczogUGFuZWxPcHRpb248c3RyaW5nPltdO1xuICAvKiogVHJ1ZSB3aGVuIHRoZSBvcHRpb25zIGNvbWUgZnJvbSB0aGUgcG9ydCBwZXIga2V5c3Ryb2tlIHJhdGhlciB0aGFuIGEgcmVzaWRlbnQgbGlzdCxcbiAgICogIHdoaWNoIGNoYW5nZXMgd2hhdCBhbiBlbXB0eSBsaXN0IG1lYW5zIChub3RoaW5nIG1hdGNoZWQgKnlldCopLiAqL1xuICByZW1vdGU6IGJvb2xlYW47XG4gIC8qKiBUaGUgbGFzdCByZW1vdGUgbG9va3VwIGZhaWxlZC4gKi9cbiAgZmFpbGVkOiBib29sZWFuO1xufVxuXG4vKipcbiAqIFRoZSBnZW5lIGxpc3QgYmVoaW5kIGV2ZXJ5IGdlbmUgZHJvcGRvd24gb2YgdGhlIHNwYXRpYWwtb21pY3MgcGFuZWwg4oCUIFwiQ29sb3VyIGJ5IGdlbmVcIixcbiAqIHRoZSBjZWxscycgZ2VuZSBjb2xvdXJpbmcsIGFuZCB0aGUgdHJhbnNjcmlwdCBnZW5lcy4gT25lIG1vZGVsIGZvciBhbGwgdGhyZWU6IHRoZXkgc2hhcmVcbiAqIG9uZSBsaXN0IGFuZCBvbmUgc2VhcmNoLCBzbyBlYWNoIG9wZW5zIG9uIHRoZSBoZWFkIG9mIHRoZSBmdWxsIGxpc3QgcmF0aGVyIHRoYW4gb25cbiAqIHdoYXRldmVyIGFub3RoZXIgd2FzIGxhc3QgZmlsdGVyZWQgdG8sIGFuZCBhIHdob2xlLXRyYW5zY3JpcHRvbWUgbGlzdCBpcyBmZXRjaGVkIG9uY2UuXG4gKlxuICogRXZlcnkgZ2VuZSB0aGUgZGF0YXNldCBpbmxpbmVkIGlzIGhlbGQgYXMgcGxhaW4gc3RyaW5ncyBhbmQgc2VhcmNoZWQgcGVyIGtleXN0cm9rZS4gQVxuICogd2hvbGUtdHJhbnNjcmlwdG9tZSBkYXRhc2V0IGlubGluZXMgfjE4ayBuYW1lcyDigJQgY2hlYXAgdG8ga2VlcCwgcnVpbm91cyB0byBoYW5kIGFcbiAqIGRyb3Bkb3duIGFsbCBhdCBvbmNlIOKAlCBzbyB0aGUgb3B0aW9ucyBhcmUgb25seSBldmVyIHRoZSBiZXN0IGZldyBodW5kcmVkIG9mIHRoZW0uXG4gKlxuICogUGxhaW4gY2xhc3MsIG5vIEFuZ3VsYXI6IHRoZSBvd25lciBwYXNzZXMgYHJ1bmAsIHdoaWNoIHJlLWVudGVycyB0aGUgem9uZSwgYW5kIHRoZVxuICogZHJvcGRvd25zIHJlYWQge0BsaW5rIHN0YXRlJH0gdGhyb3VnaCB0aGUgYGFzeW5jYCBwaXBlLCBzbyBhbiBPblB1c2ggcGFuZWwgcmUtcmVuZGVyc1xuICogd2hlbiBhIHNlYXJjaCBsYW5kcy5cbiAqL1xuZXhwb3J0IGNsYXNzIEdlbmVQaWNrZXJNb2RlbCB7XG4gIC8qKiBNb3N0IG5hbWVzIGZldGNoZWQgZm9yIHRoZSBsYXp5IGxpc3Qg4oCUIHdlbGwgYWJvdmUgYW55IHBhbmVsLCB3aG9sZS10cmFuc2NyaXB0b21lIGluY2x1ZGVkLiAqL1xuICBzdGF0aWMgcmVhZG9ubHkgTElTVF9NQVggPSAxMDBfMDAwO1xuICAvKiogTmFtZXMgYXNrZWQgb2YgdGhlIHBvcnQgcGVyIGtleXN0cm9rZSB3aGlsZSB0aGUgbGlzdCBpcyBub3QgcmVzaWRlbnQuICovXG4gIHN0YXRpYyByZWFkb25seSBTRUFSQ0hfTElNSVQgPSA1MDtcblxuICByZWFkb25seSBzdGF0ZSQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PEdlbmVQaWNrZXJTdGF0ZT4oeyBvcHRpb25zOiBbXSwgcmVtb3RlOiBmYWxzZSwgZmFpbGVkOiBmYWxzZSB9KTtcblxuICBwcml2YXRlIG5hbWVzOiBzdHJpbmdbXSA9IFtdO1xuICBwcml2YXRlIGxpc3RMb2FkaW5nID0gZmFsc2U7XG4gIC8qKiBHdWFyZHMgdGhlIHR5cGVhaGVhZDogdHlwaW5nIG91dHJ1bnMgdGhlIGxvb2t1cC4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBzZWFyY2ggPSBuZXcgU3VwZXJzZWRlKCk7XG4gIC8qKiBHdWFyZHMgdGhlIHdob2xlLXRyYW5zY3JpcHRvbWUgbGlzdCBwcmVsb2FkIGFnYWluc3QgYSBkYXRhc2V0IHN3aXRjaC4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBsaXN0TG9hZCA9IG5ldyBTdXBlcnNlZGUoKTtcblxuICAvKipcbiAgICogQHBhcmFtIGNvbnRyb2xzIHRoZSBwb3J0LCBvciBudWxsIHdoZW4gbm9uZSBpcyBib3VuZC5cbiAgICogQHBhcmFtIGNob3NlbiB0aGUgZ2VuZXMgYWxyZWFkeSBjaG9zZW4gYW55d2hlcmUsIGtlcHQgYW1vbmcgdGhlIG9wdGlvbnM6IGEgbXVsdGktc2VsZWN0XG4gICAqICAgc2hvd3MgYSBjaGlwIG9ubHkgZm9yIGEgdmFsdWUgaXQgY2FuIGZpbmQgYW1vbmcgaXRzIG9wdGlvbnMuXG4gICAqIEBwYXJhbSBydW4gd2hlcmUgYXN5bmMgcmVzdWx0cyBhcmUgYXBwbGllZCDigJQgdGhlIG93bmVyJ3MgYE5nWm9uZS5ydW5gLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBjb250cm9sczogKCkgPT4gSVNwYXRpYWxDb250cm9scyB8IG51bGwsXG4gICAgcHJpdmF0ZSByZWFkb25seSBjaG9zZW46ICgpID0+IHJlYWRvbmx5IHN0cmluZ1tdLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgcnVuOiAoZm46ICgpID0+IHZvaWQpID0+IHZvaWQgPSAoZm4pID0+IGZuKCksXG4gICkge31cblxuICBnZXQgb3B0aW9ucygpOiBQYW5lbE9wdGlvbjxzdHJpbmc+W10ge1xuICAgIHJldHVybiB0aGlzLnN0YXRlJC52YWx1ZS5vcHRpb25zO1xuICB9XG5cbiAgZ2V0IHJlbW90ZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zdGF0ZSQudmFsdWUucmVtb3RlO1xuICB9XG5cbiAgZ2V0IGZhaWxlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zdGF0ZSQudmFsdWUuZmFpbGVkO1xuICB9XG5cbiAgLyoqIEdlbmVzIHJlc2lkZW50IGluIHRoZSBsaXN0ICh0aGUgaW5saW5lZCBuYW1lcywgb3IgdGhlIGZldGNoZWQgbGlzdCBvbmNlIGxvYWRlZCkuICovXG4gIGdldCByZXNpZGVudENvdW50KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMubmFtZXMubGVuZ3RoO1xuICB9XG5cbiAgLyoqIEEgbmV3IGRhdGFzZXQ6IGl0cyBvd24gbmFtZXMgKG9yIG5vbmUpLCBhbmQgbm90aGluZyBpbiBmbGlnaHQgZm9yIHRoZSBwcmV2aW91cyBvbmUuICovXG4gIHNldERhdGFzZXQoZGF0YXNldDogU3BhdGlhbERhdGFzZXQgfCBudWxsKTogdm9pZCB7XG4gICAgLy8gQSBnZW5lIHNlYXJjaCBvciBsaXN0IHByZWxvYWQgc3RpbGwgaW4gZmxpZ2h0IGFuc3dlcnMgZm9yIHRoZSBwcmV2aW91cyBkYXRhc2V0LlxuICAgIHRoaXMuc2VhcmNoLmNhbmNlbCgpO1xuICAgIHRoaXMubGlzdExvYWQuY2FuY2VsKCk7XG4gICAgdGhpcy5saXN0TG9hZGluZyA9IGZhbHNlO1xuICAgIGNvbnN0IG5hbWVzID0gZGF0YXNldD8uZmVhdHVyZXM/Lm5hbWVzO1xuICAgIHRoaXMubmFtZXMgPSBuYW1lcyA/IFsuLi5uYW1lc10gOiBbXTtcbiAgICAvLyBUaGUgaGVhZCBvZiB0aGUgbGlzdCwgbm90IGFsbCBvZiBpdC5cbiAgICB0aGlzLnNldCh7XG4gICAgICBvcHRpb25zOiBzZWFyY2hHZW5lTmFtZXModGhpcy5uYW1lcywgJycpLm1hcChnZW5lT3B0aW9uKSxcbiAgICAgIHJlbW90ZTogISFkYXRhc2V0Py5mZWF0dXJlcyAmJiAhbmFtZXMsXG4gICAgICBmYWlsZWQ6IGZhbHNlLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEEga2V5c3Ryb2tlIGluIGEgZ2VuZSBkcm9wZG93bidzIGZpbHRlciBib3guXG4gICAqXG4gICAqIFdpdGggdGhlIG5hbWVzIHJlc2lkZW50LCBzZWFyY2ggdGhlbSBoZXJlIGFuZCBtYXRlcmlhbGlzZSBvbmx5IHRoZSB0b3AgbWF0Y2hlczogdGhlXG4gICAqIGRyb3Bkb3duJ3Mgb3duIGZpbHRlciB0aGVuIHJ1bnMgb3ZlciB0aG9zZSBhbmQgYWdyZWVzIOKAlCB0aGV5IHdlcmUgY2hvc2VuIGJ5IHRoZSBzYW1lXG4gICAqIHF1ZXJ5IOKAlCBzbyB0aGUgY29udHJvbCBiZWhhdmVzIGFzIGlmIGl0IHN0aWxsIGhlbGQgdGhlIHdob2xlIGxpc3QuIFdpdGhvdXQgdGhlbSB0aGVyZVxuICAgKiBpcyBub3RoaW5nIHRvIGZpbHRlciwgc28gdGhlIHF1ZXJ5IGdvZXMgdG8gdGhlIHBvcnQgYW5kIGl0cyBhbnN3ZXIgQkVDT01FUyB0aGUgb3B0aW9uXG4gICAqIGxpc3Qg4oCUIHRoZSBzYW1lIGNvbnRyb2wsIGZpbHRlcmluZyBvbmUgaG9wIGZ1cnRoZXIgYXdheS5cbiAgICovXG4gIGFzeW5jIG9uRmlsdGVyKHF1ZXJ5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMucmVtb3RlKSB7XG4gICAgICB0aGlzLnNldCh7IG9wdGlvbnM6IHRoaXMud2l0aENob3NlbihzZWFyY2hHZW5lTmFtZXModGhpcy5uYW1lcywgcXVlcnkpKSwgZmFpbGVkOiBmYWxzZSB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5zZXQoeyBmYWlsZWQ6IGZhbHNlIH0pO1xuICAgIGNvbnN0IGNvbnRyb2xzID0gdGhpcy5jb250cm9scygpO1xuICAgIGlmICghY29udHJvbHMpIHJldHVybjtcbiAgICAvLyBBIHNsb3cgYW5zd2VyIGZvciBhbiBlYXJsaWVyIHF1ZXJ5IHdvdWxkIHJlcGxhY2UgdGhlIG9wdGlvbnMgZm9yIHRoZSB0ZXh0IG5vdyBpblxuICAgIC8vIHRoZSBib3gg4oCUIGluY2x1ZGluZyBhIGZhaWx1cmUsIHdoaWNoIHdvdWxkIHdyb25nbHkgbWFyayB0aGUgY3VycmVudCBxdWVyeSBhcyBmYWlsZWQuXG4gICAgY29uc3QgY3VycmVudCA9IHRoaXMuc2VhcmNoLm5leHQoKTtcbiAgICBpZiAoIXF1ZXJ5KSB7XG4gICAgICB0aGlzLnNldCh7IG9wdGlvbnM6IFtdIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgY29uc3QgbmFtZXMgPSBhd2FpdCBjb250cm9scy5zZWFyY2hGZWF0dXJlcyhxdWVyeSwgR2VuZVBpY2tlck1vZGVsLlNFQVJDSF9MSU1JVCk7XG4gICAgICBpZiAoIWN1cnJlbnQoKSkgcmV0dXJuO1xuICAgICAgdGhpcy5ydW4oKCkgPT4gdGhpcy5zZXQoeyBvcHRpb25zOiB0aGlzLndpdGhDaG9zZW4obmFtZXMpIH0pKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIGlmICghY3VycmVudCgpKSByZXR1cm47XG4gICAgICAvLyBBIGZhaWxlZCBsb29rdXAgbXVzdCBub3Qgd2VkZ2UgdGhlIGNvbnRyb2wg4oCUIHNob3cgbm9uZSBhbmQgc2F5IHNvLlxuICAgICAgdGhpcy5ydW4oKCkgPT4gdGhpcy5zZXQoeyBvcHRpb25zOiB0aGlzLndpdGhDaG9zZW4oW10pLCBmYWlsZWQ6IHRydWUgfSkpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBIGdlbmUgZHJvcGRvd24gb3BlbmVkLlxuICAgKlxuICAgKiBBIHdob2xlLXRyYW5zY3JpcHRvbWUgZGF0YXNldCBkb2VzIG5vdCBpbmxpbmUgaXRzIH4zMGsgbmFtZXM7IHRoZXkgYXJlIGZldGNoZWQgaGVyZSxcbiAgICogb25jZSwgb24gZmlyc3Qgb3BlbiDigJQgdGhlIGxpc3QgaXMgdGhlbiByZXNpZGVudCBhbmQgZXZlcnkga2V5c3Ryb2tlIGZpbHRlcnMgbG9jYWxseSxcbiAgICogZXhhY3RseSBhcyBmb3IgYSB0YXJnZXRlZCBwYW5lbC4gVW50aWwgaXQgYXJyaXZlcyB0aGUgZHJvcGRvd24gZmFsbHMgYmFjayB0byBhc2tpbmcgdGhlXG4gICAqIHNlcnZlciBwZXIga2V5c3Ryb2tlLlxuICAgKi9cbiAgYXN5bmMgZW5zdXJlTGlzdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBjb250cm9scyA9IHRoaXMuY29udHJvbHMoKTtcbiAgICBpZiAodGhpcy5yZW1vdGUgJiYgY29udHJvbHMgJiYgIXRoaXMubGlzdExvYWRpbmcpIHtcbiAgICAgIHRoaXMubGlzdExvYWRpbmcgPSB0cnVlO1xuICAgICAgLy8gQSBzd2l0Y2ggdG8gYW5vdGhlciByZW1vdGUtZ2VuZSBkYXRhc2V0IG1pZC1mZXRjaCB3b3VsZCBvdGhlcndpc2UgaGFuZCBpdCB0aGlzXG4gICAgICAvLyBkYXRhc2V0J3MgbmFtZXMgYXMgaXRzIG93bi5cbiAgICAgIGNvbnN0IGN1cnJlbnQgPSB0aGlzLmxpc3RMb2FkLm5leHQoKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IG5hbWVzID0gYXdhaXQgY29udHJvbHMuc2VhcmNoRmVhdHVyZXMoJycsIEdlbmVQaWNrZXJNb2RlbC5MSVNUX01BWCk7XG4gICAgICAgIGlmIChjdXJyZW50KCkgJiYgbmFtZXMubGVuZ3RoICYmIHRoaXMucmVtb3RlKSB7XG4gICAgICAgICAgdGhpcy5ydW4oKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5uYW1lcyA9IG5hbWVzO1xuICAgICAgICAgICAgdGhpcy5zZXQoeyByZW1vdGU6IGZhbHNlIH0pO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gS2VlcCB0aGUgcGVyLWtleXN0cm9rZSBzZWFyY2g7IHRoZSBsaXN0IGlzIGEgY29udmVuaWVuY2UsIG5vdCBhIHJlcXVpcmVtZW50LlxuICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgaWYgKGN1cnJlbnQoKSkgdGhpcy5saXN0TG9hZGluZyA9IGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnJ1bigoKSA9PlxuICAgICAgdGhpcy5zZXQoe1xuICAgICAgICBvcHRpb25zOiB0aGlzLndpdGhDaG9zZW4odGhpcy5yZW1vdGUgPyBbXSA6IHNlYXJjaEdlbmVOYW1lcyh0aGlzLm5hbWVzLCAnJykpLFxuICAgICAgfSksXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBPcHRpb25zIGZvciBgbmFtZXNgLCBwbHVzIHRoZSBnZW5lcyBhbHJlYWR5IGNob3Nlbi4gKi9cbiAgcHJpdmF0ZSB3aXRoQ2hvc2VuKG5hbWVzOiByZWFkb25seSBzdHJpbmdbXSk6IFBhbmVsT3B0aW9uPHN0cmluZz5bXSB7XG4gICAgY29uc3Qgc2VlbiA9IG5ldyBTZXQobmFtZXMpO1xuICAgIHJldHVybiBbLi4udGhpcy5jaG9zZW4oKS5maWx0ZXIoKGcpID0+ICFzZWVuLmhhcyhnKSksIC4uLm5hbWVzXS5tYXAoZ2VuZU9wdGlvbik7XG4gIH1cblxuICBwcml2YXRlIHNldChwYXRjaDogUGFydGlhbDxHZW5lUGlja2VyU3RhdGU+KTogdm9pZCB7XG4gICAgdGhpcy5zdGF0ZSQubmV4dCh7IC4uLnRoaXMuc3RhdGUkLnZhbHVlLCAuLi5wYXRjaCB9KTtcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, NgZone, } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { SharedModule } from 'primeng/api';
|
|
4
|
+
import { ButtonModule } from 'primeng/button';
|
|
5
|
+
import { InputTextModule } from 'primeng/inputtext';
|
|
6
|
+
import { OverlayPanelModule } from 'primeng/overlaypanel';
|
|
7
|
+
import { TooltipModule } from 'primeng/tooltip';
|
|
8
|
+
import { DEFAULT_SPATIAL_VIEW } from '../../contracts/display-types';
|
|
9
|
+
import { parseCssColor, rgbToHex } from '../../contracts/color';
|
|
10
|
+
import { DEFAULT_CATEGORICAL_PALETTE } from '../../spatial/spatial-encoding';
|
|
11
|
+
import { cellTypeColumnFor, clusterColorMap, clusterOfGene, defaultGlyphFor } from '../../spatial/spatial-tiles';
|
|
12
|
+
import { GLYPH_OPTIONS, buildGeneTree, glyphPoints, toggleHidden, } from '../../spatial/spatial-panel-model';
|
|
13
|
+
import { Supersede } from '../../util/supersede';
|
|
14
|
+
import * as i0 from "@angular/core";
|
|
15
|
+
import * as i1 from "@angular/common";
|
|
16
|
+
import * as i2 from "primeng/api";
|
|
17
|
+
import * as i3 from "primeng/button";
|
|
18
|
+
import * as i4 from "primeng/inputtext";
|
|
19
|
+
import * as i5 from "primeng/overlaypanel";
|
|
20
|
+
import * as i6 from "primeng/tooltip";
|
|
21
|
+
/**
|
|
22
|
+
* The selected transcript genes as Xenium Explorer's tree: the gene groups, then the
|
|
23
|
+
* ungrouped genes, each row with an eye that hides it without deselecting it, its marker
|
|
24
|
+
* in its colour (opening one icon-and-colour picker per gene), and its transcript count in
|
|
25
|
+
* view.
|
|
26
|
+
*
|
|
27
|
+
* Writes only through `controls.setViewState`.
|
|
28
|
+
*/
|
|
29
|
+
export class SpatialGeneTreeComponent {
|
|
30
|
+
constructor(zone, cdr) {
|
|
31
|
+
this.zone = zone;
|
|
32
|
+
this.cdr = cdr;
|
|
33
|
+
/** The spatial controls this panel reads and writes through; null without a `SPATIAL_DATA_PORT`. */
|
|
34
|
+
this.controls = null;
|
|
35
|
+
/** The dataset on offer, or null. */
|
|
36
|
+
this.dataset = null;
|
|
37
|
+
/** The current spatial view state: what is drawn, and how. */
|
|
38
|
+
this.view = DEFAULT_SPATIAL_VIEW;
|
|
39
|
+
/** Transcripts of each selected gene in the current view, from the renderer. */
|
|
40
|
+
this.geneCounts = null;
|
|
41
|
+
/** Real genes in the panel — the tree's denominator. */
|
|
42
|
+
this.geneTotal = 0;
|
|
43
|
+
this.glyphOptions = GLYPH_OPTIONS;
|
|
44
|
+
/** Swatches in the picker: the categorical palette genes are coloured from by default. */
|
|
45
|
+
this.colorPresets = DEFAULT_CATEGORICAL_PALETTE.slice(0, 10).map((c) => c.toLowerCase());
|
|
46
|
+
/** The tree, rebuilt when the view changes — never per change-detection pass. */
|
|
47
|
+
this.geneTree = [];
|
|
48
|
+
/** Per gene: its eye, swatch, glyph and count — rebuilt with the inputs (and when the
|
|
49
|
+
* cells' group colours arrive), so the template does no per-row lookups per check. */
|
|
50
|
+
this.rows = new Map();
|
|
51
|
+
/** Per named group, and for the root: whether any of its genes is shown. */
|
|
52
|
+
this.groupShown = new Map();
|
|
53
|
+
this.rootShown = false;
|
|
54
|
+
this.collapsedGeneGroups = new Set();
|
|
55
|
+
this.geneTreeOpen = true;
|
|
56
|
+
/** The gene the open picker edits. */
|
|
57
|
+
this.styleGene = null;
|
|
58
|
+
/** Colours of the cells' groups, by name — what a cluster of the same name is drawn in. */
|
|
59
|
+
this.cellGroupColors = new Map();
|
|
60
|
+
/** The grouping {@link cellGroupColors} belongs to — a repeat request for it is a no-op. */
|
|
61
|
+
this.cellGroupColorsFor = undefined;
|
|
62
|
+
/** Latest wins among colour loads for {@link cellGroupColors}. */
|
|
63
|
+
this.cellGroupColorsLoad = new Supersede();
|
|
64
|
+
this.trackByNode = (_i, node) => node.name ?? '';
|
|
65
|
+
this.trackByLabelString = (_i, s) => s;
|
|
66
|
+
this.clusterColorMemo = null;
|
|
67
|
+
}
|
|
68
|
+
ngOnChanges(changes) {
|
|
69
|
+
if (changes['view'])
|
|
70
|
+
this.geneTree = buildGeneTree(this.view.transcriptGenes, this.view.transcriptGeneGroups);
|
|
71
|
+
// A same-named grouping of a new dataset is not the old one.
|
|
72
|
+
if (changes['dataset'])
|
|
73
|
+
this.cellGroupColorsFor = undefined;
|
|
74
|
+
this.rebuildRows();
|
|
75
|
+
void this.refreshCellGroupColors();
|
|
76
|
+
}
|
|
77
|
+
rebuildRows() {
|
|
78
|
+
const genes = this.view.transcriptGenes;
|
|
79
|
+
this.rows = new Map(genes.map((gene, slot) => [
|
|
80
|
+
gene,
|
|
81
|
+
{
|
|
82
|
+
shown: this.isGeneShown(gene),
|
|
83
|
+
swatch: this.geneSwatchOf(gene),
|
|
84
|
+
points: glyphPoints(this.glyphOf(gene, slot)),
|
|
85
|
+
count: this.geneCountOf(gene),
|
|
86
|
+
},
|
|
87
|
+
]));
|
|
88
|
+
this.groupShown = new Map(this.geneTree
|
|
89
|
+
.filter((node) => node.name)
|
|
90
|
+
.map((node) => [node.name, this.areGenesShown(node.genes)]));
|
|
91
|
+
this.rootShown = this.areGenesShown(genes);
|
|
92
|
+
}
|
|
93
|
+
toggleGeneGroup(name) {
|
|
94
|
+
const next = new Set(this.collapsedGeneGroups);
|
|
95
|
+
if (next.has(name))
|
|
96
|
+
next.delete(name);
|
|
97
|
+
else
|
|
98
|
+
next.add(name);
|
|
99
|
+
this.collapsedGeneGroups = next;
|
|
100
|
+
}
|
|
101
|
+
isGeneShown(gene) {
|
|
102
|
+
return !this.view.transcriptHiddenGenes.includes(gene);
|
|
103
|
+
}
|
|
104
|
+
areGenesShown(genes) {
|
|
105
|
+
return genes.some((g) => this.isGeneShown(g));
|
|
106
|
+
}
|
|
107
|
+
/** The eye toggle: hide or show genes without removing them from the selection. */
|
|
108
|
+
onGenesShown(genes, on) {
|
|
109
|
+
this.controls?.setViewState({
|
|
110
|
+
transcriptHiddenGenes: toggleHidden(this.view.transcriptHiddenGenes, genes, on),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/** The root eye while every gene is shown: back to the chosen list. */
|
|
114
|
+
onToggleAllGenes() {
|
|
115
|
+
if (!this.dataset?.transcriptBins)
|
|
116
|
+
return;
|
|
117
|
+
this.controls?.setViewState({ transcriptAllGenes: !this.view.transcriptAllGenes });
|
|
118
|
+
}
|
|
119
|
+
onGeneColor(gene, hex) {
|
|
120
|
+
this.controls?.setViewState({ transcriptGeneColors: { ...this.view.transcriptGeneColors, [gene]: hex } });
|
|
121
|
+
}
|
|
122
|
+
// ── per-gene icon and colour picker ──────────────────────────────────────
|
|
123
|
+
openGeneStyle(event, gene, panel) {
|
|
124
|
+
if (this.styleGene === gene) {
|
|
125
|
+
panel.toggle(event);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.styleGene = gene;
|
|
129
|
+
panel.hide();
|
|
130
|
+
// Re-open anchored on the row that was clicked, once the panel has closed.
|
|
131
|
+
setTimeout(() => panel.toggle(event));
|
|
132
|
+
}
|
|
133
|
+
/** The icon the open picker shows as chosen. */
|
|
134
|
+
get styleGlyph() {
|
|
135
|
+
const gene = this.styleGene;
|
|
136
|
+
return gene ? this.glyphOf(gene, Math.max(0, this.view.transcriptGenes.indexOf(gene))) : null;
|
|
137
|
+
}
|
|
138
|
+
glyphPoints(glyph) {
|
|
139
|
+
return glyphPoints(glyph);
|
|
140
|
+
}
|
|
141
|
+
glyphOf(gene, slot) {
|
|
142
|
+
return this.view.transcriptGlyphs[gene] ?? defaultGlyphFor(slot);
|
|
143
|
+
}
|
|
144
|
+
onGlyph(gene, glyph) {
|
|
145
|
+
this.controls?.setViewState({ transcriptGlyphs: { ...this.view.transcriptGlyphs, [gene]: glyph } });
|
|
146
|
+
}
|
|
147
|
+
/** A typed hex colour, accepted as `#rrggbb` or `rrggbb`; anything else is ignored. */
|
|
148
|
+
onGeneHex(gene, text) {
|
|
149
|
+
const t = text.trim();
|
|
150
|
+
const rgb = /^#?[0-9a-f]{6}$/i.test(t) ? parseCssColor(t) : null;
|
|
151
|
+
if (rgb)
|
|
152
|
+
this.onGeneColor(gene, rgbToHex(rgb));
|
|
153
|
+
}
|
|
154
|
+
/** Back to the gene's default icon and colour (by its position in the list). */
|
|
155
|
+
resetGeneStyle(gene) {
|
|
156
|
+
const colors = { ...this.view.transcriptGeneColors };
|
|
157
|
+
const glyphs = { ...this.view.transcriptGlyphs };
|
|
158
|
+
delete colors[gene];
|
|
159
|
+
delete glyphs[gene];
|
|
160
|
+
this.controls?.setViewState({ transcriptGeneColors: colors, transcriptGlyphs: glyphs });
|
|
161
|
+
}
|
|
162
|
+
// ── colours and counts ──────────────────────────────────────────────────
|
|
163
|
+
/** The colour gene `slot` is drawn in when transcripts are coloured by gene. */
|
|
164
|
+
geneColor(slot) {
|
|
165
|
+
const gene = this.view?.transcriptGenes[slot];
|
|
166
|
+
return ((gene && this.view.transcriptGeneColors[gene]) ||
|
|
167
|
+
DEFAULT_CATEGORICAL_PALETTE[slot % DEFAULT_CATEGORICAL_PALETTE.length]);
|
|
168
|
+
}
|
|
169
|
+
geneColorOf(gene) {
|
|
170
|
+
return this.geneColor(Math.max(0, this.view.transcriptGenes.indexOf(gene)));
|
|
171
|
+
}
|
|
172
|
+
/** In Cluster colouring, a gene's swatch is its cluster's colour, as its markers are. */
|
|
173
|
+
geneSwatchOf(gene) {
|
|
174
|
+
if (this.view.transcriptColorBy !== 'cluster')
|
|
175
|
+
return this.geneColorOf(gene);
|
|
176
|
+
return this.clusterColors().get(clusterOfGene(gene, this.view.transcriptGeneGroups)) ?? this.geneColorOf(gene);
|
|
177
|
+
}
|
|
178
|
+
geneCountOf(gene) {
|
|
179
|
+
return this.geneCounts ? (this.geneCounts[gene] ?? 0) : null;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The cluster colours, rebuilt only when what they come from changes. The template asks
|
|
183
|
+
* once per gene row per change-detection pass, and rebuilding per call made that O(G²).
|
|
184
|
+
*/
|
|
185
|
+
clusterColors() {
|
|
186
|
+
const { transcriptGenes: genes, transcriptGeneGroups: groups } = this.view;
|
|
187
|
+
const hit = this.clusterColorMemo;
|
|
188
|
+
if (hit && hit.genes === genes && hit.groups === groups && hit.cellColors === this.cellGroupColors) {
|
|
189
|
+
return hit.colors;
|
|
190
|
+
}
|
|
191
|
+
const colors = clusterColorMap(genes, groups, this.cellGroupColors, DEFAULT_CATEGORICAL_PALETTE);
|
|
192
|
+
this.clusterColorMemo = { genes, groups, cellColors: this.cellGroupColors, colors };
|
|
193
|
+
return colors;
|
|
194
|
+
}
|
|
195
|
+
async refreshCellGroupColors() {
|
|
196
|
+
const column = this.dataset ? cellTypeColumnFor(this.dataset, this.view) : null;
|
|
197
|
+
if (column === this.cellGroupColorsFor)
|
|
198
|
+
return;
|
|
199
|
+
this.cellGroupColorsFor = column;
|
|
200
|
+
const task = this.cellGroupColorsLoad.next();
|
|
201
|
+
const map = new Map();
|
|
202
|
+
const meta = column ? this.dataset?.columns.find((c) => c.name === column) : null;
|
|
203
|
+
if (meta && meta.kind === 'categorical' && this.controls) {
|
|
204
|
+
try {
|
|
205
|
+
const colors = await this.controls.categoryColors(column);
|
|
206
|
+
meta.categories.forEach((c, k) => {
|
|
207
|
+
if (colors[k])
|
|
208
|
+
map.set(c, colors[k]);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// No colours: palette colours, as the markers fall back to.
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// The cells' grouping changed while the colours loaded: the newer request applies its own.
|
|
216
|
+
if (!task.isCurrent())
|
|
217
|
+
return;
|
|
218
|
+
this.zone.run(() => {
|
|
219
|
+
this.cellGroupColors = map;
|
|
220
|
+
this.rebuildRows();
|
|
221
|
+
this.cdr.markForCheck();
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SpatialGeneTreeComponent, deps: [{ token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
225
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: SpatialGeneTreeComponent, isStandalone: true, selector: "spatial-gene-tree", inputs: { controls: "controls", dataset: "dataset", view: "view", geneCounts: "geneCounts", geneTotal: "geneTotal" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"sc-subhead\">Selected genes</div>\n<div class=\"sc-gene-tree\">\n <div class=\"sc-gene-row sc-gene-root\">\n <button\n type=\"button\"\n class=\"sc-eye\"\n (click)=\"view.transcriptAllGenes ? onToggleAllGenes() : onGenesShown(view.transcriptGenes, !rootShown)\"\n >\n <i\n class=\"pi\"\n [class.pi-eye]=\"view.transcriptAllGenes || rootShown\"\n [class.pi-eye-slash]=\"!view.transcriptAllGenes && !rootShown\"\n ></i>\n </button>\n <span class=\"sc-group-label\">Genes</span>\n <span class=\"sc-count\"\n >({{ (view.transcriptAllGenes ? geneTotal : view.transcriptGenes.length) | number }}/{{\n geneTotal | number\n }})</span\n >\n <button\n type=\"button\"\n class=\"sc-icon-btn\"\n (click)=\"geneTreeOpen = !geneTreeOpen\"\n *ngIf=\"!view.transcriptAllGenes && view.transcriptGenes.length\"\n >\n <i class=\"pi\" [class.pi-chevron-down]=\"geneTreeOpen\" [class.pi-chevron-right]=\"!geneTreeOpen\"></i>\n </button>\n </div>\n <ng-container *ngIf=\"geneTreeOpen && !view.transcriptAllGenes\">\n <ng-container *ngFor=\"let node of geneTree; trackBy: trackByNode\">\n <div class=\"sc-gene-row sc-gene-group\" *ngIf=\"node.name\">\n <button type=\"button\" class=\"sc-eye\" (click)=\"onGenesShown(node.genes, !groupShown.get(node.name))\">\n <i\n class=\"pi\"\n [class.pi-eye]=\"groupShown.get(node.name)\"\n [class.pi-eye-slash]=\"!groupShown.get(node.name)\"\n ></i>\n </button>\n <span class=\"sc-group-label\" [title]=\"node.name\">{{ node.name }}</span>\n <span class=\"sc-count\">({{ node.genes.length }})</span>\n <button type=\"button\" class=\"sc-icon-btn\" (click)=\"toggleGeneGroup(node.name)\">\n <i\n class=\"pi\"\n [class.pi-chevron-down]=\"!collapsedGeneGroups.has(node.name)\"\n [class.pi-chevron-right]=\"collapsedGeneGroups.has(node.name)\"\n ></i>\n </button>\n </div>\n <ng-container *ngIf=\"!node.name || !collapsedGeneGroups.has(node.name)\">\n <div\n class=\"sc-gene-row sc-gene-leaf\"\n [class.nested]=\"!!node.name\"\n *ngFor=\"let gene of node.genes; trackBy: trackByLabelString\"\n >\n <button type=\"button\" class=\"sc-eye\" (click)=\"onGenesShown([gene], !rows.get(gene)?.shown)\">\n <i class=\"pi\" [class.pi-eye]=\"rows.get(gene)?.shown\" [class.pi-eye-slash]=\"!rows.get(gene)?.shown\"></i>\n </button>\n <!-- The gene's marker, in its colour: its icon in Icons mode, a dot for circles.\n Clicking it opens the icon and colour picker (as Xenium Explorer does). -->\n <button\n type=\"button\"\n class=\"sc-gene-icon\"\n [style.color]=\"rows.get(gene)?.swatch\"\n [attr.aria-label]=\"'Icon and colour for ' + gene\"\n pTooltip=\"Icon and colour\"\n tooltipPosition=\"top\"\n (click)=\"openGeneStyle($event, gene, genePop)\"\n >\n <svg viewBox=\"-1.2 -1.2 2.4 2.4\" class=\"sc-glyph-svg\">\n <polygon\n *ngIf=\"view.transcriptMode === 'glyphs'; else dotIcon\"\n [attr.points]=\"rows.get(gene)?.points\"\n ></polygon>\n <ng-template #dotIcon><circle r=\"0.75\"></circle></ng-template>\n </svg>\n </button>\n <span class=\"sc-group-label\">{{ gene }}</span>\n <span\n class=\"sc-gene-count\"\n *ngIf=\"rows.get(gene)?.count as n; else zeroCount\"\n title=\"Transcripts in view\"\n >{{ n | number }}</span\n >\n <ng-template #zeroCount>\n <span class=\"sc-gene-count\" *ngIf=\"geneCounts\" title=\"Transcripts in view\">0</span>\n </ng-template>\n </div>\n </ng-container>\n </ng-container>\n </ng-container>\n <!-- One picker for every gene row, filled for the gene that opened it. -->\n <p-overlayPanel #genePop styleClass=\"sc-gene-style\" appendTo=\"body\">\n <ng-template pTemplate=\"content\">\n <div class=\"sc-style-pop\" *ngIf=\"styleGene as gene\">\n <div class=\"sc-style-title\">Transcript Icon</div>\n <div class=\"sc-style-icons\" role=\"radiogroup\" [attr.aria-label]=\"'Icon for ' + gene\">\n <button\n type=\"button\"\n *ngFor=\"let opt of glyphOptions\"\n class=\"sc-style-icon\"\n role=\"radio\"\n [attr.aria-checked]=\"opt.value === styleGlyph\"\n [class.active]=\"opt.value === styleGlyph\"\n [title]=\"opt.label\"\n (click)=\"onGlyph(gene, opt.value)\"\n >\n <svg viewBox=\"-1.2 -1.2 2.4 2.4\" class=\"sc-glyph-svg\">\n <polygon [attr.points]=\"opt.points\"></polygon>\n </svg>\n </button>\n </div>\n <small class=\"sc-hint\" *ngIf=\"view.transcriptMode !== 'glyphs'\">\n Icons show when the transcript point style is Icons.\n </small>\n <div class=\"sc-style-title\">Transcript Color</div>\n <div class=\"sc-style-color\">\n <label class=\"sc-style-swatch\" [style.background]=\"geneColorOf(gene)\" title=\"Pick a colour\">\n <input\n type=\"color\"\n [value]=\"geneColorOf(gene)\"\n (change)=\"onGeneColor(gene, $any($event.target).value)\"\n />\n </label>\n <input\n pInputText\n class=\"sc-style-hex\"\n [value]=\"geneColorOf(gene)\"\n maxlength=\"7\"\n [attr.aria-label]=\"'Hex colour for ' + gene\"\n (change)=\"onGeneHex(gene, $any($event.target).value)\"\n (keydown.enter)=\"onGeneHex(gene, $any($event.target).value)\"\n />\n </div>\n <div class=\"sc-style-title sc-style-sub\">Presets</div>\n <div class=\"sc-style-presets\">\n <button\n type=\"button\"\n *ngFor=\"let c of colorPresets\"\n class=\"sc-style-preset\"\n [style.background]=\"c\"\n [class.active]=\"c === geneColorOf(gene).toLowerCase()\"\n [title]=\"c\"\n [attr.aria-label]=\"'Colour ' + c\"\n (click)=\"onGeneColor(gene, c)\"\n ></button>\n </div>\n <p-button\n label=\"Reset to default\"\n icon=\"pi pi-refresh\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"sc-style-reset\"\n (onClick)=\"resetGeneStyle(gene)\"\n ></p-button>\n </div>\n </ng-template>\n </p-overlayPanel>\n <div class=\"sc-gene-footer\">\n {{ (view.transcriptAllGenes ? geneTotal : view.transcriptGenes.length) | number }}/{{ geneTotal | number }}\n genes selected\n </div>\n</div>\n", styles: [".sc-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d)}.sc-error{color:var(--red-600, #c62828)}.sc-section{display:flex;flex-direction:column;gap:.35rem}.sc-lbl{font-size:.78rem;color:var(--text-color-secondary, #6c757d);white-space:nowrap}.sc-row{display:flex;align-items:center;gap:.6rem}.sc-row p-slider,.sc-row p-dropdown,.sc-row p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row p-slider .p-slider{width:100%}:host ::ng-deep .sc-row p-dropdown .p-dropdown,:host ::ng-deep .sc-row p-treeSelect .p-treeselect{width:100%}.sc-row .sc-lbl{flex:0 0 calc(50% - .6rem);min-width:0;overflow:hidden;text-overflow:ellipsis}.sc-cmap-option{display:inline-flex;align-items:center;gap:.4rem}.sc-cmap-swatch{width:3.5rem;height:.7rem;border-radius:2px;display:block}.sc-val{flex:0 0 4rem;text-align:right;font-size:.78rem;font-variant-numeric:tabular-nums;white-space:nowrap}:host ::ng-deep .sc-full,:host ::ng-deep .sc-full .p-dropdown,:host ::ng-deep .sc-full .p-autocomplete,:host ::ng-deep .sc-full input{width:100%}.sc-colorbar{height:.75rem;border-radius:2px;border:1px solid rgba(0,0,0,.2)}.sc-colorbar-ends{display:flex;justify-content:space-between;font-size:.7rem;color:var(--text-color-secondary, #6c757d);margin-top:.2rem}.sc-panel{border:1px solid var(--surface-border, #dee2e6);border-radius:6px;margin-bottom:.5rem;overflow:hidden}.sc-panel-head{display:flex;align-items:center;gap:.6rem;padding:.5rem .7rem;cursor:pointer;-webkit-user-select:none;user-select:none;background:var(--surface-100, #f5f6f7)}.sc-panel-head .sc-panel-title{flex:1;font-weight:600}.sc-panel.open>.sc-panel-head{background:var(--primary-color, #2196f3);color:var(--primary-color-text, #fff)}.sc-panel-body{display:flex;flex-direction:column;gap:.45rem;padding:.6rem .75rem .75rem}.sc-subhead{display:flex;align-items:center;gap:.5rem;margin-top:.4rem;font-size:.72rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-color-secondary, #6c757d)}.sc-subhead:after{content:\"\";flex:1;border-top:1px solid var(--surface-border, #dee2e6)}:host ::ng-deep .sc-buttons .p-button{padding:.35rem .75rem;font-size:.85rem}.sc-row-picker p-dropdown,.sc-row-picker p-multiSelect,.sc-row-picker p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row-picker .sc-variant{width:6.5rem}:host ::ng-deep .sc-row-picker p-multiselect .p-multiselect{width:100%}:host ::ng-deep .sc-row-picker p-dropdown:has(.sc-variant){flex:0 0 6.5rem}:host ::ng-deep .sc-row-picker p-treeselect:has(.sc-palette){flex:0 0 3rem}:host ::ng-deep .sc-palette .p-treeselect-label{width:0;padding:0}.sc-icon-btn{flex:0 0 auto;border:0;background:transparent;cursor:pointer;padding:.25rem .35rem;font:inherit;font-size:1.05rem;color:var(--text-color, #333);border-radius:4px}.sc-icon-btn:hover{background:var(--surface-200, #e9ecef)}.sc-icon-btn.active{color:var(--primary-color, #2196f3);font-weight:700}.sc-icon-btn:disabled{opacity:.5;cursor:default}.sc-small-btn{margin-left:auto;border:1px solid var(--surface-border, #ced4da);background:var(--surface-0, #fff);border-radius:4px;padding:.2rem .6rem;font:inherit;font-size:.8rem;cursor:pointer}:host ::ng-deep .sc-group-heading{font-size:.7rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;opacity:.7}.sc-group-list,.sc-gene-tree{max-height:16rem;overflow-y:auto;border:1px solid var(--surface-border, #dee2e6);border-radius:4px;padding:.25rem .4rem}.sc-group-row,.sc-gene-row{display:flex;align-items:center;gap:.45rem;padding:.15rem 0;font-size:.88rem}.sc-group-row .sc-group-label,.sc-gene-row .sc-group-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-group-all,.sc-gene-root,.sc-gene-group{font-weight:600}.sc-group-all .sc-icon-btn,.sc-gene-root .sc-icon-btn,.sc-gene-group .sc-icon-btn{margin-left:auto;font-size:.85rem}.sc-count{flex:0 0 auto;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sc-glyph-option{display:inline-flex;align-items:center;gap:.4rem}.sc-glyph-svg{width:1rem;height:1rem;fill:currentColor}:host ::ng-deep .sc-glyph-pick .p-dropdown-label{padding:.2rem .4rem}.sc-color-input{width:2.5rem;height:1.8rem;border:1px solid var(--surface-border, #ced4da);border-radius:4px;padding:0;background:none}.sc-row-number p-slider,.sc-row-range p-slider{flex:1 1 0;min-width:0}:host ::ng-deep .sc-num{width:4.5rem;padding:.3rem .4rem;text-align:center}:host ::ng-deep .sc-row-number p-inputnumber,:host ::ng-deep .sc-row-range p-inputnumber{flex:0 0 auto}:host{display:contents}.sc-gene-group,.sc-gene-leaf{padding-left:1rem}.sc-gene-leaf.nested{padding-left:2rem}.sc-eye{flex:0 0 auto;border:0;background:transparent;cursor:pointer;padding:0;color:var(--text-color-secondary, #6c757d)}.sc-eye .pi-eye{color:var(--text-color, #333)}.sc-gene-footer{text-align:right;font-size:.78rem;padding:.2rem 0;color:var(--text-color-secondary, #6c757d)}.sc-gene-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1.4rem;height:1.4rem;padding:0;border:1px solid transparent;border-radius:4px;background:transparent;cursor:pointer}.sc-gene-icon:hover,.sc-gene-icon:focus-visible{border-color:var(--surface-border, rgba(0, 0, 0, .2))}::ng-deep .sc-gene-style .p-overlaypanel-content{padding:.75rem}.sc-style-pop{display:flex;flex-direction:column;gap:.5rem;width:15rem}.sc-style-title{font-weight:600;font-size:.9rem}.sc-style-sub{font-size:.75rem;text-transform:uppercase;opacity:.7}.sc-style-icons,.sc-style-presets{display:grid;grid-template-columns:repeat(5,1fr);gap:.35rem}.sc-style-icon{display:inline-flex;align-items:center;justify-content:center;height:1.9rem;border:1px solid var(--surface-border, rgba(0, 0, 0, .15));border-radius:4px;background:transparent;color:var(--text-color, inherit);cursor:pointer}.sc-style-icon .sc-glyph-svg{width:1.1rem;height:1.1rem}.sc-style-icon.active{border-color:var(--primary-color, #3b82f6);background:var(--highlight-bg, rgba(59, 130, 246, .12))}.sc-style-color{display:flex;gap:.5rem;align-items:center}.sc-style-swatch{position:relative;flex:0 0 3rem;height:2rem;border-radius:4px;border:1px solid rgba(0,0,0,.2);cursor:pointer}.sc-style-swatch input{position:absolute;inset:0;opacity:0;width:100%;height:100%;cursor:pointer}.sc-style-hex{flex:1 1 auto;min-width:0;font-family:monospace}.sc-style-preset{width:1.4rem;height:1.4rem;justify-self:center;border-radius:50%;border:2px solid transparent;cursor:pointer}.sc-style-preset.active{border-color:var(--text-color, #000)}.sc-gene-count{margin-left:auto;font-variant-numeric:tabular-nums;opacity:.8;padding-left:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "ngmodule", type: SharedModule }, { kind: "directive", type: i2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["variant"] }, { kind: "ngmodule", type: OverlayPanelModule }, { kind: "component", type: i5.OverlayPanel, selector: "p-overlayPanel", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "showCloseIcon", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
226
|
+
}
|
|
227
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: SpatialGeneTreeComponent, decorators: [{
|
|
228
|
+
type: Component,
|
|
229
|
+
args: [{ selector: 'spatial-gene-tree', standalone: true, imports: [CommonModule, SharedModule, ButtonModule, InputTextModule, OverlayPanelModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"sc-subhead\">Selected genes</div>\n<div class=\"sc-gene-tree\">\n <div class=\"sc-gene-row sc-gene-root\">\n <button\n type=\"button\"\n class=\"sc-eye\"\n (click)=\"view.transcriptAllGenes ? onToggleAllGenes() : onGenesShown(view.transcriptGenes, !rootShown)\"\n >\n <i\n class=\"pi\"\n [class.pi-eye]=\"view.transcriptAllGenes || rootShown\"\n [class.pi-eye-slash]=\"!view.transcriptAllGenes && !rootShown\"\n ></i>\n </button>\n <span class=\"sc-group-label\">Genes</span>\n <span class=\"sc-count\"\n >({{ (view.transcriptAllGenes ? geneTotal : view.transcriptGenes.length) | number }}/{{\n geneTotal | number\n }})</span\n >\n <button\n type=\"button\"\n class=\"sc-icon-btn\"\n (click)=\"geneTreeOpen = !geneTreeOpen\"\n *ngIf=\"!view.transcriptAllGenes && view.transcriptGenes.length\"\n >\n <i class=\"pi\" [class.pi-chevron-down]=\"geneTreeOpen\" [class.pi-chevron-right]=\"!geneTreeOpen\"></i>\n </button>\n </div>\n <ng-container *ngIf=\"geneTreeOpen && !view.transcriptAllGenes\">\n <ng-container *ngFor=\"let node of geneTree; trackBy: trackByNode\">\n <div class=\"sc-gene-row sc-gene-group\" *ngIf=\"node.name\">\n <button type=\"button\" class=\"sc-eye\" (click)=\"onGenesShown(node.genes, !groupShown.get(node.name))\">\n <i\n class=\"pi\"\n [class.pi-eye]=\"groupShown.get(node.name)\"\n [class.pi-eye-slash]=\"!groupShown.get(node.name)\"\n ></i>\n </button>\n <span class=\"sc-group-label\" [title]=\"node.name\">{{ node.name }}</span>\n <span class=\"sc-count\">({{ node.genes.length }})</span>\n <button type=\"button\" class=\"sc-icon-btn\" (click)=\"toggleGeneGroup(node.name)\">\n <i\n class=\"pi\"\n [class.pi-chevron-down]=\"!collapsedGeneGroups.has(node.name)\"\n [class.pi-chevron-right]=\"collapsedGeneGroups.has(node.name)\"\n ></i>\n </button>\n </div>\n <ng-container *ngIf=\"!node.name || !collapsedGeneGroups.has(node.name)\">\n <div\n class=\"sc-gene-row sc-gene-leaf\"\n [class.nested]=\"!!node.name\"\n *ngFor=\"let gene of node.genes; trackBy: trackByLabelString\"\n >\n <button type=\"button\" class=\"sc-eye\" (click)=\"onGenesShown([gene], !rows.get(gene)?.shown)\">\n <i class=\"pi\" [class.pi-eye]=\"rows.get(gene)?.shown\" [class.pi-eye-slash]=\"!rows.get(gene)?.shown\"></i>\n </button>\n <!-- The gene's marker, in its colour: its icon in Icons mode, a dot for circles.\n Clicking it opens the icon and colour picker (as Xenium Explorer does). -->\n <button\n type=\"button\"\n class=\"sc-gene-icon\"\n [style.color]=\"rows.get(gene)?.swatch\"\n [attr.aria-label]=\"'Icon and colour for ' + gene\"\n pTooltip=\"Icon and colour\"\n tooltipPosition=\"top\"\n (click)=\"openGeneStyle($event, gene, genePop)\"\n >\n <svg viewBox=\"-1.2 -1.2 2.4 2.4\" class=\"sc-glyph-svg\">\n <polygon\n *ngIf=\"view.transcriptMode === 'glyphs'; else dotIcon\"\n [attr.points]=\"rows.get(gene)?.points\"\n ></polygon>\n <ng-template #dotIcon><circle r=\"0.75\"></circle></ng-template>\n </svg>\n </button>\n <span class=\"sc-group-label\">{{ gene }}</span>\n <span\n class=\"sc-gene-count\"\n *ngIf=\"rows.get(gene)?.count as n; else zeroCount\"\n title=\"Transcripts in view\"\n >{{ n | number }}</span\n >\n <ng-template #zeroCount>\n <span class=\"sc-gene-count\" *ngIf=\"geneCounts\" title=\"Transcripts in view\">0</span>\n </ng-template>\n </div>\n </ng-container>\n </ng-container>\n </ng-container>\n <!-- One picker for every gene row, filled for the gene that opened it. -->\n <p-overlayPanel #genePop styleClass=\"sc-gene-style\" appendTo=\"body\">\n <ng-template pTemplate=\"content\">\n <div class=\"sc-style-pop\" *ngIf=\"styleGene as gene\">\n <div class=\"sc-style-title\">Transcript Icon</div>\n <div class=\"sc-style-icons\" role=\"radiogroup\" [attr.aria-label]=\"'Icon for ' + gene\">\n <button\n type=\"button\"\n *ngFor=\"let opt of glyphOptions\"\n class=\"sc-style-icon\"\n role=\"radio\"\n [attr.aria-checked]=\"opt.value === styleGlyph\"\n [class.active]=\"opt.value === styleGlyph\"\n [title]=\"opt.label\"\n (click)=\"onGlyph(gene, opt.value)\"\n >\n <svg viewBox=\"-1.2 -1.2 2.4 2.4\" class=\"sc-glyph-svg\">\n <polygon [attr.points]=\"opt.points\"></polygon>\n </svg>\n </button>\n </div>\n <small class=\"sc-hint\" *ngIf=\"view.transcriptMode !== 'glyphs'\">\n Icons show when the transcript point style is Icons.\n </small>\n <div class=\"sc-style-title\">Transcript Color</div>\n <div class=\"sc-style-color\">\n <label class=\"sc-style-swatch\" [style.background]=\"geneColorOf(gene)\" title=\"Pick a colour\">\n <input\n type=\"color\"\n [value]=\"geneColorOf(gene)\"\n (change)=\"onGeneColor(gene, $any($event.target).value)\"\n />\n </label>\n <input\n pInputText\n class=\"sc-style-hex\"\n [value]=\"geneColorOf(gene)\"\n maxlength=\"7\"\n [attr.aria-label]=\"'Hex colour for ' + gene\"\n (change)=\"onGeneHex(gene, $any($event.target).value)\"\n (keydown.enter)=\"onGeneHex(gene, $any($event.target).value)\"\n />\n </div>\n <div class=\"sc-style-title sc-style-sub\">Presets</div>\n <div class=\"sc-style-presets\">\n <button\n type=\"button\"\n *ngFor=\"let c of colorPresets\"\n class=\"sc-style-preset\"\n [style.background]=\"c\"\n [class.active]=\"c === geneColorOf(gene).toLowerCase()\"\n [title]=\"c\"\n [attr.aria-label]=\"'Colour ' + c\"\n (click)=\"onGeneColor(gene, c)\"\n ></button>\n </div>\n <p-button\n label=\"Reset to default\"\n icon=\"pi pi-refresh\"\n size=\"small\"\n [text]=\"true\"\n styleClass=\"sc-style-reset\"\n (onClick)=\"resetGeneStyle(gene)\"\n ></p-button>\n </div>\n </ng-template>\n </p-overlayPanel>\n <div class=\"sc-gene-footer\">\n {{ (view.transcriptAllGenes ? geneTotal : view.transcriptGenes.length) | number }}/{{ geneTotal | number }}\n genes selected\n </div>\n</div>\n", styles: [".sc-hint{font-size:.75rem;color:var(--text-color-secondary, #6c757d)}.sc-error{color:var(--red-600, #c62828)}.sc-section{display:flex;flex-direction:column;gap:.35rem}.sc-lbl{font-size:.78rem;color:var(--text-color-secondary, #6c757d);white-space:nowrap}.sc-row{display:flex;align-items:center;gap:.6rem}.sc-row p-slider,.sc-row p-dropdown,.sc-row p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row p-slider .p-slider{width:100%}:host ::ng-deep .sc-row p-dropdown .p-dropdown,:host ::ng-deep .sc-row p-treeSelect .p-treeselect{width:100%}.sc-row .sc-lbl{flex:0 0 calc(50% - .6rem);min-width:0;overflow:hidden;text-overflow:ellipsis}.sc-cmap-option{display:inline-flex;align-items:center;gap:.4rem}.sc-cmap-swatch{width:3.5rem;height:.7rem;border-radius:2px;display:block}.sc-val{flex:0 0 4rem;text-align:right;font-size:.78rem;font-variant-numeric:tabular-nums;white-space:nowrap}:host ::ng-deep .sc-full,:host ::ng-deep .sc-full .p-dropdown,:host ::ng-deep .sc-full .p-autocomplete,:host ::ng-deep .sc-full input{width:100%}.sc-colorbar{height:.75rem;border-radius:2px;border:1px solid rgba(0,0,0,.2)}.sc-colorbar-ends{display:flex;justify-content:space-between;font-size:.7rem;color:var(--text-color-secondary, #6c757d);margin-top:.2rem}.sc-panel{border:1px solid var(--surface-border, #dee2e6);border-radius:6px;margin-bottom:.5rem;overflow:hidden}.sc-panel-head{display:flex;align-items:center;gap:.6rem;padding:.5rem .7rem;cursor:pointer;-webkit-user-select:none;user-select:none;background:var(--surface-100, #f5f6f7)}.sc-panel-head .sc-panel-title{flex:1;font-weight:600}.sc-panel.open>.sc-panel-head{background:var(--primary-color, #2196f3);color:var(--primary-color-text, #fff)}.sc-panel-body{display:flex;flex-direction:column;gap:.45rem;padding:.6rem .75rem .75rem}.sc-subhead{display:flex;align-items:center;gap:.5rem;margin-top:.4rem;font-size:.72rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-color-secondary, #6c757d)}.sc-subhead:after{content:\"\";flex:1;border-top:1px solid var(--surface-border, #dee2e6)}:host ::ng-deep .sc-buttons .p-button{padding:.35rem .75rem;font-size:.85rem}.sc-row-picker p-dropdown,.sc-row-picker p-multiSelect,.sc-row-picker p-treeSelect{flex:1 1 0;min-width:0}:host ::ng-deep .sc-row-picker .sc-variant{width:6.5rem}:host ::ng-deep .sc-row-picker p-multiselect .p-multiselect{width:100%}:host ::ng-deep .sc-row-picker p-dropdown:has(.sc-variant){flex:0 0 6.5rem}:host ::ng-deep .sc-row-picker p-treeselect:has(.sc-palette){flex:0 0 3rem}:host ::ng-deep .sc-palette .p-treeselect-label{width:0;padding:0}.sc-icon-btn{flex:0 0 auto;border:0;background:transparent;cursor:pointer;padding:.25rem .35rem;font:inherit;font-size:1.05rem;color:var(--text-color, #333);border-radius:4px}.sc-icon-btn:hover{background:var(--surface-200, #e9ecef)}.sc-icon-btn.active{color:var(--primary-color, #2196f3);font-weight:700}.sc-icon-btn:disabled{opacity:.5;cursor:default}.sc-small-btn{margin-left:auto;border:1px solid var(--surface-border, #ced4da);background:var(--surface-0, #fff);border-radius:4px;padding:.2rem .6rem;font:inherit;font-size:.8rem;cursor:pointer}:host ::ng-deep .sc-group-heading{font-size:.7rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;opacity:.7}.sc-group-list,.sc-gene-tree{max-height:16rem;overflow-y:auto;border:1px solid var(--surface-border, #dee2e6);border-radius:4px;padding:.25rem .4rem}.sc-group-row,.sc-gene-row{display:flex;align-items:center;gap:.45rem;padding:.15rem 0;font-size:.88rem}.sc-group-row .sc-group-label,.sc-gene-row .sc-group-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-group-all,.sc-gene-root,.sc-gene-group{font-weight:600}.sc-group-all .sc-icon-btn,.sc-gene-root .sc-icon-btn,.sc-gene-group .sc-icon-btn{margin-left:auto;font-size:.85rem}.sc-count{flex:0 0 auto;font-size:.78rem;color:var(--text-color-secondary, #6c757d)}.sc-glyph-option{display:inline-flex;align-items:center;gap:.4rem}.sc-glyph-svg{width:1rem;height:1rem;fill:currentColor}:host ::ng-deep .sc-glyph-pick .p-dropdown-label{padding:.2rem .4rem}.sc-color-input{width:2.5rem;height:1.8rem;border:1px solid var(--surface-border, #ced4da);border-radius:4px;padding:0;background:none}.sc-row-number p-slider,.sc-row-range p-slider{flex:1 1 0;min-width:0}:host ::ng-deep .sc-num{width:4.5rem;padding:.3rem .4rem;text-align:center}:host ::ng-deep .sc-row-number p-inputnumber,:host ::ng-deep .sc-row-range p-inputnumber{flex:0 0 auto}:host{display:contents}.sc-gene-group,.sc-gene-leaf{padding-left:1rem}.sc-gene-leaf.nested{padding-left:2rem}.sc-eye{flex:0 0 auto;border:0;background:transparent;cursor:pointer;padding:0;color:var(--text-color-secondary, #6c757d)}.sc-eye .pi-eye{color:var(--text-color, #333)}.sc-gene-footer{text-align:right;font-size:.78rem;padding:.2rem 0;color:var(--text-color-secondary, #6c757d)}.sc-gene-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:1.4rem;height:1.4rem;padding:0;border:1px solid transparent;border-radius:4px;background:transparent;cursor:pointer}.sc-gene-icon:hover,.sc-gene-icon:focus-visible{border-color:var(--surface-border, rgba(0, 0, 0, .2))}::ng-deep .sc-gene-style .p-overlaypanel-content{padding:.75rem}.sc-style-pop{display:flex;flex-direction:column;gap:.5rem;width:15rem}.sc-style-title{font-weight:600;font-size:.9rem}.sc-style-sub{font-size:.75rem;text-transform:uppercase;opacity:.7}.sc-style-icons,.sc-style-presets{display:grid;grid-template-columns:repeat(5,1fr);gap:.35rem}.sc-style-icon{display:inline-flex;align-items:center;justify-content:center;height:1.9rem;border:1px solid var(--surface-border, rgba(0, 0, 0, .15));border-radius:4px;background:transparent;color:var(--text-color, inherit);cursor:pointer}.sc-style-icon .sc-glyph-svg{width:1.1rem;height:1.1rem}.sc-style-icon.active{border-color:var(--primary-color, #3b82f6);background:var(--highlight-bg, rgba(59, 130, 246, .12))}.sc-style-color{display:flex;gap:.5rem;align-items:center}.sc-style-swatch{position:relative;flex:0 0 3rem;height:2rem;border-radius:4px;border:1px solid rgba(0,0,0,.2);cursor:pointer}.sc-style-swatch input{position:absolute;inset:0;opacity:0;width:100%;height:100%;cursor:pointer}.sc-style-hex{flex:1 1 auto;min-width:0;font-family:monospace}.sc-style-preset{width:1.4rem;height:1.4rem;justify-self:center;border-radius:50%;border:2px solid transparent;cursor:pointer}.sc-style-preset.active{border-color:var(--text-color, #000)}.sc-gene-count{margin-left:auto;font-variant-numeric:tabular-nums;opacity:.8;padding-left:.5rem}\n"] }]
|
|
230
|
+
}], ctorParameters: () => [{ type: i0.NgZone }, { type: i0.ChangeDetectorRef }], propDecorators: { controls: [{
|
|
231
|
+
type: Input
|
|
232
|
+
}], dataset: [{
|
|
233
|
+
type: Input
|
|
234
|
+
}], view: [{
|
|
235
|
+
type: Input
|
|
236
|
+
}], geneCounts: [{
|
|
237
|
+
type: Input
|
|
238
|
+
}], geneTotal: [{
|
|
239
|
+
type: Input
|
|
240
|
+
}] } });
|
|
241
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhdGlhbC1nZW5lLXRyZWUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9zcGF0aWFsLWNvbnRyb2xzL3NwYXRpYWwtZ2VuZS10cmVlL3NwYXRpYWwtZ2VuZS10cmVlLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvc3BhdGlhbC1jb250cm9scy9zcGF0aWFsLWdlbmUtdHJlZS9zcGF0aWFsLWdlbmUtdHJlZS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLGlCQUFpQixFQUNqQixTQUFTLEVBQ1QsS0FBSyxFQUNMLE1BQU0sR0FHUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMzQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDOUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzFELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUloRCxPQUFPLEVBQUUsb0JBQW9CLEVBQXlDLE1BQU0sK0JBQStCLENBQUM7QUFDNUcsT0FBTyxFQUFFLGFBQWEsRUFBRSxRQUFRLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUM3RSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsZUFBZSxFQUFFLGFBQWEsRUFBRSxlQUFlLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNqSCxPQUFPLEVBQ0wsYUFBYSxFQUViLGFBQWEsRUFDYixXQUFXLEVBQ1gsWUFB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SSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUU7WUFDakIsSUFBSSxDQUFDLGVBQWUsR0FBRyxHQUFHLENBQUM7WUFDM0IsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ25CLElBQUksQ0FBQyxHQUFHLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDMUIsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDOzhHQTlOVSx3QkFBd0I7a0dBQXhCLHdCQUF3Qix3TkN6RHJDLDgwTkFtS0EsOCtNRC9HWSxZQUFZLHVUQUFFLFlBQVkscUlBQUUsWUFBWSw2WEFBRSxlQUFlLHdIQUFFLGtCQUFrQixrV0FBRSxhQUFhOzsyRkFLM0Ysd0JBQXdCO2tCQVJwQyxTQUFTOytCQUNFLG1CQUFtQixjQUNqQixJQUFJLFdBQ1AsQ0FBQyxZQUFZLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxlQUFlLEVBQUUsa0JBQWtCLEVBQUUsYUFBYSxDQUFDLG1CQUd0Rix1QkFBdUIsQ0FBQyxNQUFNOzJHQUl0QyxRQUFRO3NCQUFoQixLQUFLO2dCQUVHLE9BQU87c0JBQWYsS0FBSztnQkFFRyxJQUFJO3NCQUFaLEtBQUs7Z0JBRUcsVUFBVTtzQkFBbEIsS0FBSztnQkFFRyxTQUFTO3NCQUFqQixLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENoYW5nZURldGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIElucHV0LFxuICBOZ1pvbmUsXG4gIE9uQ2hhbmdlcyxcbiAgU2ltcGxlQ2hhbmdlcyxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgU2hhcmVkTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9hcGknO1xuaW1wb3J0IHsgQnV0dG9uTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9idXR0b24nO1xuaW1wb3J0IHsgSW5wdXRUZXh0TW9kdWxlIH0gZnJvbSAncHJpbWVuZy9pbnB1dHRleHQnO1xuaW1wb3J0IHsgT3ZlcmxheVBhbmVsTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9vdmVybGF5cGFuZWwnO1xuaW1wb3J0IHsgVG9vbHRpcE1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvdG9vbHRpcCc7XG5cbmltcG9ydCB0eXBlIHsgSVNwYXRpYWxDb250cm9scyB9IGZyb20gJy4uLy4uL2NvbnRyYWN0cy92aXN1YWxpemVyLmNvbnRyYWN0JztcbmltcG9ydCB0eXBlIHsgU3BhdGlhbERhdGFzZXQgfSBmcm9tICcuLi8uLi9jb250cmFjdHMvc3BhdGlhbC1kYXRhc2V0LmNvbnRyYWN0JztcbmltcG9ydCB7IERFRkFVTFRfU1BBVElBTF9WSUVXLCBTcGF0aWFsVmlld1N0YXRlLCBUcmFuc2NyaXB0R2x5cGhOYW1lIH0gZnJvbSAnLi4vLi4vY29udHJhY3RzL2Rpc3BsYXktdHlwZXMnO1xuaW1wb3J0IHsgcGFyc2VDc3NDb2xvciwgcmdiVG9IZXggfSBmcm9tICcuLi8uLi9jb250cmFjdHMvY29sb3InO1xuaW1wb3J0IHsgREVGQVVMVF9DQVRFR09SSUNBTF9QQUxFVFRFIH0gZnJvbSAnLi4vLi4vc3BhdGlhbC9zcGF0aWFsLWVuY29kaW5nJztcbmltcG9ydCB7IGNlbGxUeXBlQ29sdW1uRm9yLCBjbHVzdGVyQ29sb3JNYXAsIGNsdXN0ZXJPZkdlbmUsIGRlZmF1bHRHbHlwaEZvciB9IGZyb20gJy4uLy4uL3NwYXRpYWwvc3BhdGlhbC10aWxlcyc7XG5pbXBvcnQge1xuICBHTFlQSF9PUFRJT05TLFxuICBHZW5lVHJlZU5vZGUsXG4gIGJ1aWxkR2VuZVRyZWUsXG4gIGdseXBoUG9pbnRzLFxuICB0b2dnbGVIaWRkZW4sXG59IGZyb20gJy4uLy4uL3NwYXRpYWwvc3BhdGlhbC1wYW5lbC1tb2RlbCc7XG5pbXBvcnQgeyBTdXBlcnNlZGUgfSBmcm9tICcuLi8uLi91dGlsL3N1cGVyc2VkZSc7XG5cbi8qKiBPbmUgZ2VuZSByb3cgYXMgdGhlIHRlbXBsYXRlIGRyYXdzIGl0LCBkZXJpdmVkIG9uY2UgcGVyIGlucHV0IGNoYW5nZS4gKi9cbmludGVyZmFjZSBHZW5lUm93VmlldyB7XG4gIHNob3duOiBib29sZWFuO1xuICBzd2F0Y2g6IHN0cmluZztcbiAgLyoqIFRoZSByb3cncyBnbHlwaCBvdXRsaW5lIChkcmF3biBpbiBJY29ucyBtb2RlKS4gKi9cbiAgcG9pbnRzOiBzdHJpbmc7XG4gIC8qKiBUcmFuc2NyaXB0cyBpbiB2aWV3LCBvciBudWxsIGJlZm9yZSB0aGUgcmVuZGVyZXIgcmVwb3J0cyBhbnkuICovXG4gIGNvdW50OiBudW1iZXIgfCBudWxsO1xufVxuXG4vKipcbiAqIFRoZSBzZWxlY3RlZCB0cmFuc2NyaXB0IGdlbmVzIGFzIFhlbml1bSBFeHBsb3JlcidzIHRyZWU6IHRoZSBnZW5lIGdyb3VwcywgdGhlbiB0aGVcbiAqIHVuZ3JvdXBlZCBnZW5lcywgZWFjaCByb3cgd2l0aCBhbiBleWUgdGhhdCBoaWRlcyBpdCB3aXRob3V0IGRlc2VsZWN0aW5nIGl0LCBpdHMgbWFya2VyXG4gKiBpbiBpdHMgY29sb3VyIChvcGVuaW5nIG9uZSBpY29uLWFuZC1jb2xvdXIgcGlja2VyIHBlciBnZW5lKSwgYW5kIGl0cyB0cmFuc2NyaXB0IGNvdW50IGluXG4gKiB2aWV3LlxuICpcbiAqIFdyaXRlcyBvbmx5IHRocm91Z2ggYGNvbnRyb2xzLnNldFZpZXdTdGF0ZWAuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3NwYXRpYWwtZ2VuZS10cmVlJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZSwgU2hhcmVkTW9kdWxlLCBCdXR0b25Nb2R1bGUsIElucHV0VGV4dE1vZHVsZSwgT3ZlcmxheVBhbmVsTW9kdWxlLCBUb29sdGlwTW9kdWxlXSxcbiAgdGVtcGxhdGVVcmw6ICcuL3NwYXRpYWwtZ2VuZS10cmVlLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vc3BhdGlhbC1nZW5lLXRyZWUuY29tcG9uZW50LnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIFNwYXRpYWxHZW5lVHJlZUNvbXBvbmVudCBpbXBsZW1lbnRzIE9uQ2hhbmdlcyB7XG4gIC8qKiBUaGUgc3BhdGlhbCBjb250cm9scyB0aGlzIHBhbmVsIHJlYWRzIGFuZCB3cml0ZXMgdGhyb3VnaDsgbnVsbCB3aXRob3V0IGEgYFNQQVRJQUxfREFUQV9QT1JUYC4gKi9cbiAgQElucHV0KCkgY29udHJvbHM6IElTcGF0aWFsQ29udHJvbHMgfCBudWxsID0gbnVsbDtcbiAgLyoqIFRoZSBkYXRhc2V0IG9uIG9mZmVyLCBvciBudWxsLiAqL1xuICBASW5wdXQoKSBkYXRhc2V0OiBTcGF0aWFsRGF0YXNldCB8IG51bGwgPSBudWxsO1xuICAvKiogVGhlIGN1cnJlbnQgc3BhdGlhbCB2aWV3IHN0YXRlOiB3aGF0IGlzIGRyYXduLCBhbmQgaG93LiAqL1xuICBASW5wdXQoKSB2aWV3OiBTcGF0aWFsVmlld1N0YXRlID0gREVGQVVMVF9TUEFUSUFMX1ZJRVc7XG4gIC8qKiBUcmFuc2NyaXB0cyBvZiBlYWNoIHNlbGVjdGVkIGdlbmUgaW4gdGhlIGN1cnJlbnQgdmlldywgZnJvbSB0aGUgcmVuZGVyZXIuICovXG4gIEBJbnB1dCgpIGdlbmVDb3VudHM6IFJlY29yZDxzdHJpbmcsIG51bWJlcj4gfCBudWxsID0gbnVsbDtcbiAgLyoqIFJlYWwgZ2VuZXMgaW4gdGhlIHBhbmVsIOKAlCB0aGUgdHJlZSdzIGRlbm9taW5hdG9yLiAqL1xuICBASW5wdXQoKSBnZW5lVG90YWwgPSAwO1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBnbHlwaE9wdGlvbnMgPSBHTFlQSF9PUFRJT05TO1xuICAvKiogU3dhdGNoZXMgaW4gdGhlIHBpY2tlcjogdGhlIGNhdGVnb3JpY2FsIHBhbGV0dGUgZ2VuZXMgYXJlIGNvbG91cmVkIGZyb20gYnkgZGVmYXVsdC4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNvbG9yUHJlc2V0cyA9IERFRkFVTFRfQ0FURUdPUklDQUxfUEFMRVRURS5zbGljZSgwLCAxMCkubWFwKChjKSA9PiBjLnRvTG93ZXJDYXNlKCkpO1xuXG4gIC8qKiBUaGUgdHJlZSwgcmVidWlsdCB3aGVuIHRoZSB2aWV3IGNoYW5nZXMg4oCUIG5ldmVyIHBlciBjaGFuZ2UtZGV0ZWN0aW9uIHBhc3MuICovXG4gIHByb3RlY3RlZCBnZW5lVHJlZTogR2VuZVRyZWVOb2RlW10gPSBbXTtcbiAgLyoqIFBlciBnZW5lOiBpdHMgZXllLCBzd2F0Y2gsIGdseXBoIGFuZCBjb3VudCDigJQgcmVidWlsdCB3aXRoIHRoZSBpbnB1dHMgKGFuZCB3aGVuIHRoZVxuICAgKiAgY2VsbHMnIGdyb3VwIGNvbG91cnMgYXJyaXZlKSwgc28gdGhlIHRlbXBsYXRlIGRvZXMgbm8gcGVyLXJvdyBsb29rdXBzIHBlciBjaGVjay4gKi9cbiAgcHJvdGVjdGVkIHJvd3MgPSBuZXcgTWFwPHN0cmluZywgR2VuZVJvd1ZpZXc+KCk7XG4gIC8qKiBQZXIgbmFtZWQgZ3JvdXAsIGFuZCBmb3IgdGhlIHJvb3Q6IHdoZXRoZXIgYW55IG9mIGl0cyBnZW5lcyBpcyBzaG93bi4gKi9cbiAgcHJvdGVjdGVkIGdyb3VwU2hvd24gPSBuZXcgTWFwPHN0cmluZywgYm9vbGVhbj4oKTtcbiAgcHJvdGVjdGVkIHJvb3RTaG93biA9IGZhbHNlO1xuICBwcm90ZWN0ZWQgY29sbGFwc2VkR2VuZUdyb3VwcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICBwcm90ZWN0ZWQgZ2VuZVRyZWVPcGVuID0gdHJ1ZTtcbiAgLyoqIFRoZSBnZW5lIHRoZSBvcGVuIHBpY2tlciBlZGl0cy4gKi9cbiAgcHJvdGVjdGVkIHN0eWxlR2VuZTogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG5cbiAgLyoqIENvbG91cnMgb2YgdGhlIGNlbGxzJyBncm91cHMsIGJ5IG5hbWUg4oCUIHdoYXQgYSBjbHVzdGVyIG9mIHRoZSBzYW1lIG5hbWUgaXMgZHJhd24gaW4uICovXG4gIHByaXZhdGUgY2VsbEdyb3VwQ29sb3JzID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oKTtcbiAgLyoqIFRoZSBncm91cGluZyB7QGxpbmsgY2VsbEdyb3VwQ29sb3JzfSBiZWxvbmdzIHRvIOKAlCBhIHJlcGVhdCByZXF1ZXN0IGZvciBpdCBpcyBhIG5vLW9wLiAqL1xuICBwcml2YXRlIGNlbGxHcm91cENvbG9yc0Zvcjogc3RyaW5nIHwgbnVsbCB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcbiAgLyoqIExhdGVzdCB3aW5zIGFtb25nIGNvbG91ciBsb2FkcyBmb3Ige0BsaW5rIGNlbGxHcm91cENvbG9yc30uICovXG4gIHByaXZhdGUgcmVhZG9ubHkgY2VsbEdyb3VwQ29sb3JzTG9hZCA9IG5ldyBTdXBlcnNlZGUoKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IHpvbmU6IE5nWm9uZSxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNkcjogQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICkge31cblxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgaWYgKGNoYW5nZXNbJ3ZpZXcnXSkgdGhpcy5nZW5lVHJlZSA9IGJ1aWxkR2VuZVRyZWUodGhpcy52aWV3LnRyYW5zY3JpcHRHZW5lcywgdGhpcy52aWV3LnRyYW5zY3JpcHRHZW5lR3JvdXBzKTtcbiAgICAvLyBBIHNhbWUtbmFtZWQgZ3JvdXBpbmcgb2YgYSBuZXcgZGF0YXNldCBpcyBub3QgdGhlIG9sZCBvbmUuXG4gICAgaWYgKGNoYW5nZXNbJ2RhdGFzZXQnXSkgdGhpcy5jZWxsR3JvdXBDb2xvcnNGb3IgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5yZWJ1aWxkUm93cygpO1xuICAgIHZvaWQgdGhpcy5yZWZyZXNoQ2VsbEdyb3VwQ29sb3JzKCk7XG4gIH1cblxuICBwcml2YXRlIHJlYnVpbGRSb3dzKCk6IHZvaWQge1xuICAgIGNvbnN0IGdlbmVzID0gdGhpcy52aWV3LnRyYW5zY3JpcHRHZW5lcztcbiAgICB0aGlzLnJvd3MgPSBuZXcgTWFwKFxuICAgICAgZ2VuZXMubWFwKChnZW5lLCBzbG90KSA9PiBbXG4gICAgICAgIGdlbmUsXG4gICAgICAgIHtcbiAgICAgICAgICBzaG93bjogdGhpcy5pc0dlbmVTaG93bihnZW5lKSxcbiAgICAgICAgICBzd2F0Y2g6IHRoaXMuZ2VuZVN3YXRjaE9mKGdlbmUpLFxuICAgICAgICAgIHBvaW50czogZ2x5cGhQb2ludHModGhpcy5nbHlwaE9mKGdlbmUsIHNsb3QpKSxcbiAgICAgICAgICBjb3VudDogdGhpcy5nZW5lQ291bnRPZihnZW5lKSxcbiAgICAgICAgfSxcbiAgICAgIF0pLFxuICAgICk7XG4gICAgdGhpcy5ncm91cFNob3duID0gbmV3IE1hcChcbiAgICAgIHRoaXMuZ2VuZVRyZWVcbiAgICAgICAgLmZpbHRlcigobm9kZSkgPT4gbm9kZS5uYW1lKVxuICAgICAgICAubWFwKChub2RlKSA9PiBbbm9kZS5uYW1lIGFzIHN0cmluZywgdGhpcy5hcmVHZW5lc1Nob3duKG5vZGUuZ2VuZXMpXSksXG4gICAgKTtcbiAgICB0aGlzLnJvb3RTaG93biA9IHRoaXMuYXJlR2VuZXNTaG93bihnZW5lcyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgdG9nZ2xlR2VuZUdyb3VwKG5hbWU6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IG5leHQgPSBuZXcgU2V0KHRoaXMuY29sbGFwc2VkR2VuZUdyb3Vwcyk7XG4gICAgaWYgKG5leHQuaGFzKG5hbWUpKSBuZXh0LmRlbGV0ZShuYW1lKTtcbiAgICBlbHNlIG5leHQuYWRkKG5hbWUpO1xuICAgIHRoaXMuY29sbGFwc2VkR2VuZUdyb3VwcyA9IG5leHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgaXNHZW5lU2hvd24oZ2VuZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICF0aGlzLnZpZXcudHJhbnNjcmlwdEhpZGRlbkdlbmVzLmluY2x1ZGVzKGdlbmUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZUdlbmVzU2hvd24oZ2VuZXM6IHN0cmluZ1tdKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGdlbmVzLnNvbWUoKGcpID0+IHRoaXMuaXNHZW5lU2hvd24oZykpO1xuICB9XG5cbiAgLyoqIFRoZSBleWUgdG9nZ2xlOiBoaWRlIG9yIHNob3cgZ2VuZXMgd2l0aG91dCByZW1vdmluZyB0aGVtIGZyb20gdGhlIHNlbGVjdGlvbi4gKi9cbiAgcHJvdGVjdGVkIG9uR2VuZXNTaG93bihnZW5lczogc3RyaW5nW10sIG9uOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHtcbiAgICAgIHRyYW5zY3JpcHRIaWRkZW5HZW5lczogdG9nZ2xlSGlkZGVuKHRoaXMudmlldy50cmFuc2NyaXB0SGlkZGVuR2VuZXMsIGdlbmVzLCBvbiksXG4gICAgfSk7XG4gIH1cblxuICAvKiogVGhlIHJvb3QgZXllIHdoaWxlIGV2ZXJ5IGdlbmUgaXMgc2hvd246IGJhY2sgdG8gdGhlIGNob3NlbiBsaXN0LiAqL1xuICBwcm90ZWN0ZWQgb25Ub2dnbGVBbGxHZW5lcygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuZGF0YXNldD8udHJhbnNjcmlwdEJpbnMpIHJldHVybjtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyB0cmFuc2NyaXB0QWxsR2VuZXM6ICF0aGlzLnZpZXcudHJhbnNjcmlwdEFsbEdlbmVzIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG9uR2VuZUNvbG9yKGdlbmU6IHN0cmluZywgaGV4OiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyB0cmFuc2NyaXB0R2VuZUNvbG9yczogeyAuLi50aGlzLnZpZXcudHJhbnNjcmlwdEdlbmVDb2xvcnMsIFtnZW5lXTogaGV4IH0gfSk7XG4gIH1cblxuICAvLyDilIDilIAgcGVyLWdlbmUgaWNvbiBhbmQgY29sb3VyIHBpY2tlciDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICBwcm90ZWN0ZWQgb3BlbkdlbmVTdHlsZShldmVudDogRXZlbnQsIGdlbmU6IHN0cmluZywgcGFuZWw6IHsgdG9nZ2xlKGU6IEV2ZW50KTogdm9pZDsgaGlkZSgpOiB2b2lkIH0pOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zdHlsZUdlbmUgPT09IGdlbmUpIHtcbiAgICAgIHBhbmVsLnRvZ2dsZShldmVudCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuc3R5bGVHZW5lID0gZ2VuZTtcbiAgICBwYW5lbC5oaWRlKCk7XG4gICAgLy8gUmUtb3BlbiBhbmNob3JlZCBvbiB0aGUgcm93IHRoYXQgd2FzIGNsaWNrZWQsIG9uY2UgdGhlIHBhbmVsIGhhcyBjbG9zZWQuXG4gICAgc2V0VGltZW91dCgoKSA9PiBwYW5lbC50b2dnbGUoZXZlbnQpKTtcbiAgfVxuXG4gIC8qKiBUaGUgaWNvbiB0aGUgb3BlbiBwaWNrZXIgc2hvd3MgYXMgY2hvc2VuLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IHN0eWxlR2x5cGgoKTogVHJhbnNjcmlwdEdseXBoTmFtZSB8IG51bGwge1xuICAgIGNvbnN0IGdlbmUgPSB0aGlzLnN0eWxlR2VuZTtcbiAgICByZXR1cm4gZ2VuZSA/IHRoaXMuZ2x5cGhPZihnZW5lLCBNYXRoLm1heCgwLCB0aGlzLnZpZXcudHJhbnNjcmlwdEdlbmVzLmluZGV4T2YoZ2VuZSkpKSA6IG51bGw7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2x5cGhQb2ludHMoZ2x5cGg6IFRyYW5zY3JpcHRHbHlwaE5hbWUpOiBzdHJpbmcge1xuICAgIHJldHVybiBnbHlwaFBvaW50cyhnbHlwaCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2x5cGhPZihnZW5lOiBzdHJpbmcsIHNsb3Q6IG51bWJlcik6IFRyYW5zY3JpcHRHbHlwaE5hbWUge1xuICAgIHJldHVybiB0aGlzLnZpZXcudHJhbnNjcmlwdEdseXBoc1tnZW5lXSA/PyBkZWZhdWx0R2x5cGhGb3Ioc2xvdCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgb25HbHlwaChnZW5lOiBzdHJpbmcsIGdseXBoOiBUcmFuc2NyaXB0R2x5cGhOYW1lKTogdm9pZCB7XG4gICAgdGhpcy5jb250cm9scz8uc2V0Vmlld1N0YXRlKHsgdHJhbnNjcmlwdEdseXBoczogeyAuLi50aGlzLnZpZXcudHJhbnNjcmlwdEdseXBocywgW2dlbmVdOiBnbHlwaCB9IH0pO1xuICB9XG5cbiAgLyoqIEEgdHlwZWQgaGV4IGNvbG91ciwgYWNjZXB0ZWQgYXMgYCNycmdnYmJgIG9yIGBycmdnYmJgOyBhbnl0aGluZyBlbHNlIGlzIGlnbm9yZWQuICovXG4gIHByb3RlY3RlZCBvbkdlbmVIZXgoZ2VuZTogc3RyaW5nLCB0ZXh0OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCB0ID0gdGV4dC50cmltKCk7XG4gICAgY29uc3QgcmdiID0gL14jP1swLTlhLWZdezZ9JC9pLnRlc3QodCkgPyBwYXJzZUNzc0NvbG9yKHQpIDogbnVsbDtcbiAgICBpZiAocmdiKSB0aGlzLm9uR2VuZUNvbG9yKGdlbmUsIHJnYlRvSGV4KHJnYikpO1xuICB9XG5cbiAgLyoqIEJhY2sgdG8gdGhlIGdlbmUncyBkZWZhdWx0IGljb24gYW5kIGNvbG91ciAoYnkgaXRzIHBvc2l0aW9uIGluIHRoZSBsaXN0KS4gKi9cbiAgcHJvdGVjdGVkIHJlc2V0R2VuZVN0eWxlKGdlbmU6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IGNvbG9ycyA9IHsgLi4udGhpcy52aWV3LnRyYW5zY3JpcHRHZW5lQ29sb3JzIH07XG4gICAgY29uc3QgZ2x5cGhzID0geyAuLi50aGlzLnZpZXcudHJhbnNjcmlwdEdseXBocyB9O1xuICAgIGRlbGV0ZSBjb2xvcnNbZ2VuZV07XG4gICAgZGVsZXRlIGdseXBoc1tnZW5lXTtcbiAgICB0aGlzLmNvbnRyb2xzPy5zZXRWaWV3U3RhdGUoeyB0cmFuc2NyaXB0R2VuZUNvbG9yczogY29sb3JzLCB0cmFuc2NyaXB0R2x5cGhzOiBnbHlwaHMgfSk7XG4gIH1cblxuICAvLyDilIDilIAgY29sb3VycyBhbmQgY291bnRzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBUaGUgY29sb3VyIGdlbmUgYHNsb3RgIGlzIGRyYXduIGluIHdoZW4gdHJhbnNjcmlwdHMgYXJlIGNvbG91cmVkIGJ5IGdlbmUuICovXG4gIGdlbmVDb2xvcihzbG90OiBudW1iZXIpOiBzdHJpbmcge1xuICAgIGNvbnN0IGdlbmUgPSB0aGlzLnZpZXc/LnRyYW5zY3JpcHRHZW5lc1tzbG90XTtcbiAgICByZXR1cm4gKFxuICAgICAgKGdlbmUgJiYgdGhpcy52aWV3LnRyYW5zY3JpcHRHZW5lQ29sb3JzW2dlbmVdKSB8fFxuICAgICAgREVGQVVMVF9DQVRFR09SSUNBTF9QQUxFVFRFW3Nsb3QgJSBERUZBVUxUX0NBVEVHT1JJQ0FMX1BBTEVUVEUubGVuZ3RoXVxuICAgICk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2VuZUNvbG9yT2YoZ2VuZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5nZW5lQ29sb3IoTWF0aC5tYXgoMCwgdGhpcy52aWV3LnRyYW5zY3JpcHRHZW5lcy5pbmRleE9mKGdlbmUpKSk7XG4gIH1cblxuICAvKiogSW4gQ2x1c3RlciBjb2xvdXJpbmcsIGEgZ2VuZSdzIHN3YXRjaCBpcyBpdHMgY2x1c3RlcidzIGNvbG91ciwgYXMgaXRzIG1hcmtlcnMgYXJlLiAqL1xuICBwcm90ZWN0ZWQgZ2VuZVN3YXRjaE9mKGdlbmU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgaWYgKHRoaXMudmlldy50cmFuc2NyaXB0Q29sb3JCeSAhPT0gJ2NsdXN0ZXInKSByZXR1cm4gdGhpcy5nZW5lQ29sb3JPZihnZW5lKTtcbiAgICByZXR1cm4gdGhpcy5jbHVzdGVyQ29sb3JzKCkuZ2V0KGNsdXN0ZXJPZkdlbmUoZ2VuZSwgdGhpcy52aWV3LnRyYW5zY3JpcHRHZW5lR3JvdXBzKSkgPz8gdGhpcy5nZW5lQ29sb3JPZihnZW5lKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBnZW5lQ291bnRPZihnZW5lOiBzdHJpbmcpOiBudW1iZXIgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5nZW5lQ291bnRzID8gKHRoaXMuZ2VuZUNvdW50c1tnZW5lXSA/PyAwKSA6IG51bGw7XG4gIH1cblxuICBwcm90ZWN0ZWQgdHJhY2tCeU5vZGUgPSAoX2k6IG51bWJlciwgbm9kZTogeyBuYW1lOiBzdHJpbmcgfCBudWxsIH0pID0+IG5vZGUubmFtZSA/PyAnJztcbiAgcHJvdGVjdGVkIHRyYWNrQnlMYWJlbFN0cmluZyA9IChfaTogbnVtYmVyLCBzOiBzdHJpbmcpID0+IHM7XG5cbiAgLyoqXG4gICAqIFRoZSBjbHVzdGVyIGNvbG91cnMsIHJlYnVpbHQgb25seSB3aGVuIHdoYXQgdGhleSBjb21lIGZyb20gY2hhbmdlcy4gVGhlIHRlbXBsYXRlIGFza3NcbiAgICogb25jZSBwZXIgZ2VuZSByb3cgcGVyIGNoYW5nZS1kZXRlY3Rpb24gcGFzcywgYW5kIHJlYnVpbGRpbmcgcGVyIGNhbGwgbWFkZSB0aGF0IE8oR8KyKS5cbiAgICovXG4gIHByaXZhdGUgY2x1c3RlckNvbG9ycygpOiBNYXA8c3RyaW5nLCBzdHJpbmc+IHtcbiAgICBjb25zdCB7IHRyYW5zY3JpcHRHZW5lczogZ2VuZXMsIHRyYW5zY3JpcHRHZW5lR3JvdXBzOiBncm91cHMgfSA9IHRoaXMudmlldztcbiAgICBjb25zdCBoaXQgPSB0aGlzLmNsdXN0ZXJDb2xvck1lbW87XG4gICAgaWYgKGhpdCAmJiBoaXQuZ2VuZXMgPT09IGdlbmVzICYmIGhpdC5ncm91cHMgPT09IGdyb3VwcyAmJiBoaXQuY2VsbENvbG9ycyA9PT0gdGhpcy5jZWxsR3JvdXBDb2xvcnMpIHtcbiAgICAgIHJldHVybiBoaXQuY29sb3JzO1xuICAgIH1cbiAgICBjb25zdCBjb2xvcnMgPSBjbHVzdGVyQ29sb3JNYXAoZ2VuZXMsIGdyb3VwcywgdGhpcy5jZWxsR3JvdXBDb2xvcnMsIERFRkFVTFRfQ0FURUdPUklDQUxfUEFMRVRURSk7XG4gICAgdGhpcy5jbHVzdGVyQ29sb3JNZW1vID0geyBnZW5lcywgZ3JvdXBzLCBjZWxsQ29sb3JzOiB0aGlzLmNlbGxHcm91cENvbG9ycywgY29sb3JzIH07XG4gICAgcmV0dXJuIGNvbG9ycztcbiAgfVxuICBwcml2YXRlIGNsdXN0ZXJDb2xvck1lbW86IHtcbiAgICBnZW5lczogU3BhdGlhbFZpZXdTdGF0ZVsndHJhbnNjcmlwdEdlbmVzJ107XG4gICAgZ3JvdXBzOiBTcGF0aWFsVmlld1N0YXRlWyd0cmFuc2NyaXB0R2VuZUdyb3VwcyddO1xuICAgIGNlbGxDb2xvcnM6IE1hcDxzdHJpbmcsIHN0cmluZz47XG4gICAgY29sb3JzOiBNYXA8c3RyaW5nLCBzdHJpbmc+O1xuICB9IHwgbnVsbCA9IG51bGw7XG5cbiAgcHJpdmF0ZSBhc3luYyByZWZyZXNoQ2VsbEdyb3VwQ29sb3JzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGNvbHVtbiA9IHRoaXMuZGF0YXNldCA/IGNlbGxUeXBlQ29sdW1uRm9yKHRoaXMuZGF0YXNldCwgdGhpcy52aWV3KSA6IG51bGw7XG4gICAgaWYgKGNvbHVtbiA9PT0gdGhpcy5jZWxsR3JvdXBDb2xvcnNGb3IpIHJldHVybjtcbiAgICB0aGlzLmNlbGxHcm91cENvbG9yc0ZvciA9IGNvbHVtbjtcbiAgICBjb25zdCB0YXNrID0gdGhpcy5jZWxsR3JvdXBDb2xvcnNMb2FkLm5leHQoKTtcbiAgICBjb25zdCBtYXAgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuICAgIGNvbnN0IG1ldGEgPSBjb2x1bW4gPyB0aGlzLmRhdGFzZXQ/LmNvbHVtbnMuZmluZCgoYykgPT4gYy5uYW1lID09PSBjb2x1bW4pIDogbnVsbDtcbiAgICBpZiAobWV0YSAmJiBtZXRhLmtpbmQgPT09ICdjYXRlZ29yaWNhbCcgJiYgdGhpcy5jb250cm9scykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgY29sb3JzID0gYXdhaXQgdGhpcy5jb250cm9scy5jYXRlZ29yeUNvbG9ycyhjb2x1bW4hKTtcbiAgICAgICAgbWV0YS5jYXRlZ29yaWVzLmZvckVhY2goKGMsIGspID0+IHtcbiAgICAgICAgICBpZiAoY29sb3JzW2tdKSBtYXAuc2V0KGMsIGNvbG9yc1trXSk7XG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIE5vIGNvbG91cnM6IHBhbGV0dGUgY29sb3VycywgYXMgdGhlIG1hcmtlcnMgZmFsbCBiYWNrIHRvLlxuICAgICAgfVxuICAgIH1cbiAgICAvLyBUaGUgY2VsbHMnIGdyb3VwaW5nIGNoYW5nZWQgd2hpbGUgdGhlIGNvbG91cnMgbG9hZGVkOiB0aGUgbmV3ZXIgcmVxdWVzdCBhcHBsaWVzIGl0cyBvd24uXG4gICAgaWYgKCF0YXNrLmlzQ3VycmVudCgpKSByZXR1cm47XG4gICAgdGhpcy56b25lLnJ1bigoKSA9PiB7XG4gICAgICB0aGlzLmNlbGxHcm91cENvbG9ycyA9IG1hcDtcbiAgICAgIHRoaXMucmVidWlsZFJvd3MoKTtcbiAgICAgIHRoaXMuY2RyLm1hcmtGb3JDaGVjaygpO1xuICAgIH0pO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwic2Mtc3ViaGVhZFwiPlNlbGVjdGVkIGdlbmVzPC9kaXY+XG48ZGl2IGNsYXNzPVwic2MtZ2VuZS10cmVlXCI+XG4gIDxkaXYgY2xhc3M9XCJzYy1nZW5lLXJvdyBzYy1nZW5lLXJvb3RcIj5cbiAgICA8YnV0dG9uXG4gICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgIGNsYXNzPVwic2MtZXllXCJcbiAgICAgIChjbGljayk9XCJ2aWV3LnRyYW5zY3JpcHRBbGxHZW5lcyA/IG9uVG9nZ2xlQWxsR2VuZXMoKSA6IG9uR2VuZXNTaG93bih2aWV3LnRyYW5zY3JpcHRHZW5lcywgIXJvb3RTaG93bilcIlxuICAgID5cbiAgICAgIDxpXG4gICAgICAgIGNsYXNzPVwicGlcIlxuICAgICAgICBbY2xhc3MucGktZXllXT1cInZpZXcudHJhbnNjcmlwdEFsbEdlbmVzIHx8IHJvb3RTaG93blwiXG4gICAgICAgIFtjbGFzcy5waS1leWUtc2xhc2hdPVwiIXZpZXcudHJhbnNjcmlwdEFsbEdlbmVzICYmICFyb290U2hvd25cIlxuICAgICAgPjwvaT5cbiAgICA8L2J1dHRvbj5cbiAgICA8c3BhbiBjbGFzcz1cInNjLWdyb3VwLWxhYmVsXCI+R2VuZXM8L3NwYW4+XG4gICAgPHNwYW4gY2xhc3M9XCJzYy1jb3VudFwiXG4gICAgICA+KHt7ICh2aWV3LnRyYW5zY3JpcHRBbGxHZW5lcyA/IGdlbmVUb3RhbCA6IHZpZXcudHJhbnNjcmlwdEdlbmVzLmxlbmd0aCkgfCBudW1iZXIgfX0ve3tcbiAgICAgICAgZ2VuZVRvdGFsIHwgbnVtYmVyXG4gICAgICB9fSk8L3NwYW5cbiAgICA+XG4gICAgPGJ1dHRvblxuICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICBjbGFzcz1cInNjLWljb24tYnRuXCJcbiAgICAgIChjbGljayk9XCJnZW5lVHJlZU9wZW4gPSAhZ2VuZVRyZWVPcGVuXCJcbiAgICAgICpuZ0lmPVwiIXZpZXcudHJhbnNjcmlwdEFsbEdlbmVzICYmIHZpZXcudHJhbnNjcmlwdEdlbmVzLmxlbmd0aFwiXG4gICAgPlxuICAgICAgPGkgY2xhc3M9XCJwaVwiIFtjbGFzcy5waS1jaGV2cm9uLWRvd25dPVwiZ2VuZVRyZWVPcGVuXCIgW2NsYXNzLnBpLWNoZXZyb24tcmlnaHRdPVwiIWdlbmVUcmVlT3BlblwiPjwvaT5cbiAgICA8L2J1dHRvbj5cbiAgPC9kaXY+XG4gIDxuZy1jb250YWluZXIgKm5nSWY9XCJnZW5lVHJlZU9wZW4gJiYgIXZpZXcudHJhbnNjcmlwdEFsbEdlbmVzXCI+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdGb3I9XCJsZXQgbm9kZSBvZiBnZW5lVHJlZTsgdHJhY2tCeTogdHJhY2tCeU5vZGVcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJzYy1nZW5lLXJvdyBzYy1nZW5lLWdyb3VwXCIgKm5nSWY9XCJub2RlLm5hbWVcIj5cbiAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJzYy1leWVcIiAoY2xpY2spPVwib25HZW5lc1Nob3duKG5vZGUuZ2VuZXMsICFncm91cFNob3duLmdldChub2RlLm5hbWUpKVwiPlxuICAgICAgICAgIDxpXG4gICAgICAgICAgICBjbGFzcz1cInBpXCJcbiAgICAgICAgICAgIFtjbGFzcy5waS1leWVdPVwiZ3JvdXBTaG93bi5nZXQobm9kZS5uYW1lKVwiXG4gICAgICAgICAgICBbY2xhc3MucGktZXllLXNsYXNoXT1cIiFncm91cFNob3duLmdldChub2RlLm5hbWUpXCJcbiAgICAgICAgICA+PC9pPlxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJzYy1ncm91cC1sYWJlbFwiIFt0aXRsZV09XCJub2RlLm5hbWVcIj57eyBub2RlLm5hbWUgfX08L3NwYW4+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwic2MtY291bnRcIj4oe3sgbm9kZS5nZW5lcy5sZW5ndGggfX0pPC9zcGFuPlxuICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cInNjLWljb24tYnRuXCIgKGNsaWNrKT1cInRvZ2dsZUdlbmVHcm91cChub2RlLm5hbWUpXCI+XG4gICAgICAgICAgPGlcbiAgICAgICAgICAgIGNsYXNzPVwicGlcIlxuICAgICAgICAgICAgW2NsYXNzLnBpLWNoZXZyb24tZG93bl09XCIhY29sbGFwc2VkR2VuZUdyb3Vwcy5oYXMobm9kZS5uYW1lKVwiXG4gICAgICAgICAgICBbY2xhc3MucGktY2hldnJvbi1yaWdodF09XCJjb2xsYXBzZWRHZW5lR3JvdXBzLmhhcyhub2RlLm5hbWUpXCJcbiAgICAgICAgICA+PC9pPlxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIDwvZGl2PlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFub2RlLm5hbWUgfHwgIWNvbGxhcHNlZEdlbmVHcm91cHMuaGFzKG5vZGUubmFtZSlcIj5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIGNsYXNzPVwic2MtZ2VuZS1yb3cgc2MtZ2VuZS1sZWFmXCJcbiAgICAgICAgICBbY2xhc3MubmVzdGVkXT1cIiEhbm9kZS5uYW1lXCJcbiAgICAgICAgICAqbmdGb3I9XCJsZXQgZ2VuZSBvZiBub2RlLmdlbmVzOyB0cmFja0J5OiB0cmFja0J5TGFiZWxTdHJpbmdcIlxuICAgICAgICA+XG4gICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJzYy1leWVcIiAoY2xpY2spPVwib25HZW5lc1Nob3duKFtnZW5lXSwgIXJvd3MuZ2V0KGdlbmUpPy5zaG93bilcIj5cbiAgICAgICAgICAgIDxpIGNsYXNzPVwicGlcIiBbY2xhc3MucGktZXllXT1cInJvd3MuZ2V0KGdlbmUpPy5zaG93blwiIFtjbGFzcy5waS1leWUtc2xhc2hdPVwiIXJvd3MuZ2V0KGdlbmUpPy5zaG93blwiPjwvaT5cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICA8IS0tIFRoZSBnZW5lJ3MgbWFya2VyLCBpbiBpdHMgY29sb3VyOiBpdHMgaWNvbiBpbiBJY29ucyBtb2RlLCBhIGRvdCBmb3IgY2lyY2xlcy5cbiAgICAgICAgICAgICAgIENsaWNraW5nIGl0IG9wZW5zIHRoZSBpY29uIGFuZCBjb2xvdXIgcGlja2VyIChhcyBYZW5pdW0gRXhwbG9yZXIgZG9lcykuIC0tPlxuICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgY2xhc3M9XCJzYy1nZW5lLWljb25cIlxuICAgICAgICAgICAgW3N0eWxlLmNvbG9yXT1cInJvd3MuZ2V0KGdlbmUpPy5zd2F0Y2hcIlxuICAgICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCInSWNvbiBhbmQgY29sb3VyIGZvciAnICsgZ2VuZVwiXG4gICAgICAgICAgICBwVG9vbHRpcD1cIkljb24gYW5kIGNvbG91clwiXG4gICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJ0b3BcIlxuICAgICAgICAgICAgKGNsaWNrKT1cIm9wZW5HZW5lU3R5bGUoJGV2ZW50LCBnZW5lLCBnZW5lUG9wKVwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAgPHN2ZyB2aWV3Qm94PVwiLTEuMiAtMS4yIDIuNCAyLjRcIiBjbGFzcz1cInNjLWdseXBoLXN2Z1wiPlxuICAgICAgICAgICAgICA8cG9seWdvblxuICAgICAgICAgICAgICAgICpuZ0lmPVwidmlldy50cmFuc2NyaXB0TW9kZSA9PT0gJ2dseXBocyc7IGVsc2UgZG90SWNvblwiXG4gICAgICAgICAgICAgICAgW2F0dHIucG9pbnRzXT1cInJvd3MuZ2V0KGdlbmUpPy5wb2ludHNcIlxuICAgICAgICAgICAgICA+PC9wb2x5Z29uPlxuICAgICAgICAgICAgICA8bmctdGVtcGxhdGUgI2RvdEljb24+PGNpcmNsZSByPVwiMC43NVwiPjwvY2lyY2xlPjwvbmctdGVtcGxhdGU+XG4gICAgICAgICAgICA8L3N2Zz5cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInNjLWdyb3VwLWxhYmVsXCI+e3sgZ2VuZSB9fTwvc3Bhbj5cbiAgICAgICAgICA8c3BhblxuICAgICAgICAgICAgY2xhc3M9XCJzYy1nZW5lLWNvdW50XCJcbiAgICAgICAgICAgICpuZ0lmPVwicm93cy5nZXQoZ2VuZSk/LmNvdW50IGFzIG47IGVsc2UgemVyb0NvdW50XCJcbiAgICAgICAgICAgIHRpdGxlPVwiVHJhbnNjcmlwdHMgaW4gdmlld1wiXG4gICAgICAgICAgICA+e3sgbiB8IG51bWJlciB9fTwvc3BhblxuICAgICAgICAgID5cbiAgICAgICAgICA8bmctdGVtcGxhdGUgI3plcm9Db3VudD5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwic2MtZ2VuZS1jb3VudFwiICpuZ0lmPVwiZ2VuZUNvdW50c1wiIHRpdGxlPVwiVHJhbnNjcmlwdHMgaW4gdmlld1wiPjA8L3NwYW4+XG4gICAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8L25nLWNvbnRhaW5lcj5cbiAgPC9uZy1jb250YWluZXI+XG4gIDwhLS0gT25lIHBpY2tlciBmb3IgZXZlcnkgZ2VuZSByb3csIGZpbGxlZCBmb3IgdGhlIGdlbmUgdGhhdCBvcGVuZWQgaXQuIC0tPlxuICA8cC1vdmVybGF5UGFuZWwgI2dlbmVQb3Agc3R5bGVDbGFzcz1cInNjLWdlbmUtc3R5bGVcIiBhcHBlbmRUbz1cImJvZHlcIj5cbiAgICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiY29udGVudFwiPlxuICAgICAgPGRpdiBjbGFzcz1cInNjLXN0eWxlLXBvcFwiICpuZ0lmPVwic3R5bGVHZW5lIGFzIGdlbmVcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cInNjLXN0eWxlLXRpdGxlXCI+VHJhbnNjcmlwdCBJY29uPC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJzYy1zdHlsZS1pY29uc1wiIHJvbGU9XCJyYWRpb2dyb3VwXCIgW2F0dHIuYXJpYS1sYWJlbF09XCInSWNvbiBmb3IgJyArIGdlbmVcIj5cbiAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICpuZ0Zvcj1cImxldCBvcHQgb2YgZ2x5cGhPcHRpb25zXCJcbiAgICAgICAgICAgIGNsYXNzPVwic2Mtc3R5bGUtaWNvblwiXG4gICAgICAgICAgICByb2xlPVwicmFkaW9cIlxuICAgICAgICAgICAgW2F0dHIuYXJpYS1jaGVja2VkXT1cIm9wdC52YWx1ZSA9PT0gc3R5bGVHbHlwaFwiXG4gICAgICAgICAgICBbY2xhc3MuYWN0aXZlXT1cIm9wdC52YWx1ZSA9PT0gc3R5bGVHbHlwaFwiXG4gICAgICAgICAgICBbdGl0bGVdPVwib3B0LmxhYmVsXCJcbiAgICAgICAgICAgIChjbGljayk9XCJvbkdseXBoKGdlbmUsIG9wdC52YWx1ZSlcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxzdmcgdmlld0JveD1cIi0xLjIgLTEuMiAyLjQgMi40XCIgY2xhc3M9XCJzYy1nbHlwaC1zdmdcIj5cbiAgICAgICAgICAgICAgPHBvbHlnb24gW2F0dHIucG9pbnRzXT1cIm9wdC5wb2ludHNcIj48L3BvbHlnb24+XG4gICAgICAgICAgICA8L3N2Zz5cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxzbWFsbCBjbGFzcz1cInNjLWhpbnRcIiAqbmdJZj1cInZpZXcudHJhbnNjcmlwdE1vZGUgIT09ICdnbHlwaHMnXCI+XG4gICAgICAgICAgSWNvbnMgc2hvdyB3aGVuIHRoZSB0cmFuc2NyaXB0IHBvaW50IHN0eWxlIGlzIEljb25zLlxuICAgICAgICA8L3NtYWxsPlxuICAgICAgICA8ZGl2IGNsYXNzPVwic2Mtc3R5bGUtdGl0bGVcIj5UcmFuc2NyaXB0IENvbG9yPC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJzYy1zdHlsZS1jb2xvclwiPlxuICAgICAgICAgIDxsYWJlbCBjbGFzcz1cInNjLXN0eWxlLXN3YXRjaFwiIFtzdHlsZS5iYWNrZ3JvdW5kXT1cImdlbmVDb2xvck9mKGdlbmUpXCIgdGl0bGU9XCJQaWNrIGEgY29sb3VyXCI+XG4gICAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICAgdHlwZT1cImNvbG9yXCJcbiAgICAgICAgICAgICAgW3ZhbHVlXT1cImdlbmVDb2xvck9mKGdlbmUpXCJcbiAgICAgICAgICAgICAgKGNoYW5nZSk9XCJvbkdlbmVDb2xvcihnZW5lLCAkYW55KCRldmVudC50YXJnZXQpLnZhbHVlKVwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgIDwvbGFiZWw+XG4gICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICBwSW5wdXRUZXh0XG4gICAgICAgICAgICBjbGFzcz1cInNjLXN0eWxlLWhleFwiXG4gICAgICAgICAgICBbdmFsdWVdPVwiZ2VuZUNvbG9yT2YoZ2VuZSlcIlxuICAgICAgICAgICAgbWF4bGVuZ3RoPVwiN1wiXG4gICAgICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidIZXggY29sb3VyIGZvciAnICsgZ2VuZVwiXG4gICAgICAgICAgICAoY2hhbmdlKT1cIm9uR2VuZUhleChnZW5lLCAkYW55KCRldmVudC50YXJnZXQpLnZhbHVlKVwiXG4gICAgICAgICAgICAoa2V5ZG93bi5lbnRlcik9XCJvbkdlbmVIZXgoZ2VuZSwgJGFueSgkZXZlbnQudGFyZ2V0KS52YWx1ZSlcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8ZGl2IGNsYXNzPVwic2Mtc3R5bGUtdGl0bGUgc2Mtc3R5bGUtc3ViXCI+UHJlc2V0czwvZGl2PlxuICAgICAgICA8ZGl2IGNsYXNzPVwic2Mtc3R5bGUtcHJlc2V0c1wiPlxuICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgKm5nRm9yPVwibGV0IGMgb2YgY29sb3JQcmVzZXRzXCJcbiAgICAgICAgICAgIGNsYXNzPVwic2Mtc3R5bGUtcHJlc2V0XCJcbiAgICAgICAgICAgIFtzdHlsZS5iYWNrZ3JvdW5kXT1cImNcIlxuICAgICAgICAgICAgW2NsYXNzLmFjdGl2ZV09XCJjID09PSBnZW5lQ29sb3JPZihnZW5lKS50b0xvd2VyQ2FzZSgpXCJcbiAgICAgICAgICAgIFt0aXRsZV09XCJjXCJcbiAgICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ0NvbG91ciAnICsgY1wiXG4gICAgICAgICAgICAoY2xpY2spPVwib25HZW5lQ29sb3IoZ2VuZSwgYylcIlxuICAgICAgICAgID48L2J1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxwLWJ1dHRvblxuICAgICAgICAgIGxhYmVsPVwiUmVzZXQgdG8gZGVmYXVsdFwiXG4gICAgICAgICAgaWNvbj1cInBpIHBpLXJlZnJlc2hcIlxuICAgICAgICAgIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgc3R5bGVDbGFzcz1cInNjLXN0eWxlLXJlc2V0XCJcbiAgICAgICAgICAob25DbGljayk9XCJyZXNldEdlbmVTdHlsZShnZW5lKVwiXG4gICAgICAgID48L3AtYnV0dG9uPlxuICAgICAgPC9kaXY+XG4gICAgPC9uZy10ZW1wbGF0ZT5cbiAgPC9wLW92ZXJsYXlQYW5lbD5cbiAgPGRpdiBjbGFzcz1cInNjLWdlbmUtZm9vdGVyXCI+XG4gICAge3sgKHZpZXcudHJhbnNjcmlwdEFsbEdlbmVzID8gZ2VuZVRvdGFsIDogdmlldy50cmFuc2NyaXB0R2VuZXMubGVuZ3RoKSB8IG51bWJlciB9fS97eyBnZW5lVG90YWwgfCBudW1iZXIgfX1cbiAgICBnZW5lcyBzZWxlY3RlZFxuICA8L2Rpdj5cbjwvZGl2PlxuIl19
|