@jax-data-science/sci-image-visualizer 0.8.3 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (525) hide show
  1. package/README.md +201 -460
  2. package/esm2022/index.mjs +28 -15
  3. package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +163 -91
  4. package/esm2022/lib/contracts/capabilities.contract.mjs +4 -3
  5. package/esm2022/lib/contracts/cell-segmenter.contract.mjs +1 -1
  6. package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +5 -1
  7. package/esm2022/lib/contracts/color.mjs +60 -0
  8. package/esm2022/lib/contracts/colormap-lut.mjs +213 -57
  9. package/esm2022/lib/contracts/coordinate-transform.contract.mjs +1 -1
  10. package/esm2022/lib/contracts/display-types.mjs +11 -1
  11. package/esm2022/lib/contracts/instance-segmenter.contract.mjs +1 -1
  12. package/esm2022/lib/contracts/intensity.mjs +48 -5
  13. package/esm2022/lib/contracts/plot-type-contribution.contract.mjs +1 -1
  14. package/esm2022/lib/contracts/plot-type.mjs +134 -17
  15. package/esm2022/lib/contracts/ports/image-state.port.mjs +4 -1
  16. package/esm2022/lib/contracts/ports/preferences.port.mjs +5 -1
  17. package/esm2022/lib/contracts/ports/region-io.port.mjs +2 -1
  18. package/esm2022/lib/contracts/ports/spatial-data.port.mjs +5 -1
  19. package/esm2022/lib/contracts/ports/tile-access.port.mjs +2 -1
  20. package/esm2022/lib/contracts/region-editor-api.contract.mjs +1 -1
  21. package/esm2022/lib/contracts/region-overlay.contract.mjs +1 -1
  22. package/esm2022/lib/contracts/sam.contract.mjs +1 -1
  23. package/esm2022/lib/contracts/semantic-segmenter.contract.mjs +1 -1
  24. package/esm2022/lib/contracts/spatial-dataset.contract.mjs +3 -1
  25. package/esm2022/lib/contracts/toolbar-tool.contract.mjs +6 -4
  26. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  27. package/esm2022/lib/contracts/viz-config.mjs +2 -1
  28. package/esm2022/lib/geometry/contour.mjs +63 -0
  29. package/esm2022/lib/geometry/raster.mjs +98 -0
  30. package/esm2022/lib/geometry/ring.mjs +201 -0
  31. package/esm2022/lib/hex-color-picker/hex-color-picker.component.mjs +202 -102
  32. package/esm2022/lib/implementations/base-store-visualizer.mjs +201 -48
  33. package/esm2022/lib/implementations/napari-js/napari-axes-gizmo.mjs +65 -0
  34. package/esm2022/lib/implementations/napari-js/napari-display-state.mjs +136 -0
  35. package/esm2022/lib/implementations/napari-js/napari-helpers.mjs +199 -0
  36. package/esm2022/lib/implementations/napari-js/napari-image-2d-scene.mjs +602 -0
  37. package/esm2022/lib/implementations/napari-js/napari-loading-badge.mjs +3 -4
  38. package/esm2022/lib/implementations/napari-js/napari-loading-state.mjs +66 -0
  39. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +157 -591
  40. package/esm2022/lib/implementations/napari-js/napari-scene.mjs +2 -0
  41. package/esm2022/lib/implementations/napari-js/napari-spatial-2d-scene.mjs +614 -0
  42. package/esm2022/lib/implementations/napari-js/napari-spatial-3d-scene.mjs +769 -0
  43. package/esm2022/lib/implementations/napari-js/napari-spatial-encoding.mjs +199 -0
  44. package/esm2022/lib/implementations/napari-js/napari-spatial-hover.mjs +263 -0
  45. package/esm2022/lib/implementations/napari-js/napari-spatial-scene.mjs +159 -0
  46. package/esm2022/lib/implementations/napari-js/napari-spatial-tiles.mjs +106 -1408
  47. package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +2 -2
  48. package/esm2022/lib/implementations/napari-js/napari-surface-scene.mjs +271 -0
  49. package/esm2022/lib/implementations/napari-js/napari-tile-client.mjs +562 -0
  50. package/esm2022/lib/implementations/napari-js/napari-tool-bridge.mjs +344 -0
  51. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +358 -4218
  52. package/esm2022/lib/implementations/napari-js/napari-volume-scene.mjs +439 -0
  53. package/esm2022/lib/implementations/napari-js/napari-volume-z-handle.mjs +6 -6
  54. package/esm2022/lib/implementations/napari-js/region-overlay/overlay-projection.mjs +96 -0
  55. package/esm2022/lib/implementations/napari-js/region-overlay/region-edit.mjs +35 -0
  56. package/esm2022/lib/implementations/napari-js/region-overlay/region-svg-renderer.mjs +182 -0
  57. package/esm2022/lib/implementations/napari-js/spatial-tiles/categorical-lookup.mjs +30 -0
  58. package/esm2022/lib/implementations/napari-js/spatial-tiles/cell-layers.mjs +274 -0
  59. package/esm2022/lib/implementations/napari-js/spatial-tiles/density-layer.mjs +144 -0
  60. package/esm2022/lib/implementations/napari-js/spatial-tiles/layer-groups.mjs +165 -0
  61. package/esm2022/lib/implementations/napari-js/spatial-tiles/plan-context.mjs +78 -0
  62. package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-hover.mjs +182 -0
  63. package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-jobs.mjs +369 -0
  64. package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-layers.mjs +306 -0
  65. package/esm2022/lib/implementations/osd/display-pipeline.mjs +102 -40
  66. package/esm2022/lib/implementations/osd/histogram-sampler.mjs +56 -31
  67. package/esm2022/lib/implementations/osd/openseadragon-viewer-options.mjs +128 -0
  68. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +333 -1685
  69. package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +1 -1
  70. package/esm2022/lib/implementations/osd/osd-coords.mjs +5 -6
  71. package/esm2022/lib/implementations/osd/osd-export.mjs +122 -0
  72. package/esm2022/lib/implementations/osd/osd-lib.mjs +15 -1
  73. package/esm2022/lib/implementations/osd/osd-navigator-chrome.mjs +205 -0
  74. package/esm2022/lib/implementations/osd/osd-pixel-readback.mjs +68 -0
  75. package/esm2022/lib/implementations/osd/osd-region-overlay-draw.mjs +153 -0
  76. package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +185 -718
  77. package/esm2022/lib/implementations/osd/osd-scale-bar.mjs +52 -60
  78. package/esm2022/lib/implementations/osd/osd-tile-source.mjs +105 -0
  79. package/esm2022/lib/implementations/osd/osd-viewer-like.mjs +2 -0
  80. package/esm2022/lib/implementations/osd/osd-viewer-mount.mjs +44 -0
  81. package/esm2022/lib/implementations/osd/osd-viewport.mjs +184 -0
  82. package/esm2022/lib/implementations/osd/osd-zoom.mjs +7 -1
  83. package/esm2022/lib/implementations/osd/simple-source.mjs +395 -0
  84. package/esm2022/lib/implementations/osd/slice-cache.mjs +53 -48
  85. package/esm2022/lib/implementations/osd/tile-client.mjs +32 -11
  86. package/esm2022/lib/implementations/osd/tile-recolor.mjs +330 -0
  87. package/esm2022/lib/implementations/plotly/omics-trace-builders.mjs +161 -73
  88. package/esm2022/lib/implementations/plotly/plotly-coordinate-transform.mjs +1 -1
  89. package/esm2022/lib/implementations/plotly/plotly-image-loader.mjs +112 -0
  90. package/esm2022/lib/implementations/plotly/plotly-image-recolor.mjs +178 -0
  91. package/esm2022/lib/implementations/plotly/plotly-intensity-inset.mjs +33 -0
  92. package/esm2022/lib/implementations/plotly/plotly-isosurface-controls.mjs +83 -0
  93. package/esm2022/lib/implementations/plotly/plotly-layouts.mjs +121 -0
  94. package/esm2022/lib/implementations/plotly/plotly-readback.mjs +117 -0
  95. package/esm2022/lib/implementations/plotly/plotly-region-overlay.mjs +19 -10
  96. package/esm2022/lib/implementations/plotly/plotly-shape-projection.mjs +223 -0
  97. package/esm2022/lib/implementations/plotly/plotly-trace-builders.mjs +79 -19
  98. package/esm2022/lib/implementations/plotly/plotly-zoom-controller.mjs +223 -0
  99. package/esm2022/lib/implementations/plotly/plotly.service.mjs +439 -1655
  100. package/esm2022/lib/implementations/region-centroids.mjs +47 -0
  101. package/esm2022/lib/implementations/simple-slice-access.service.mjs +22 -12
  102. package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +172 -92
  103. package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +103 -22
  104. package/esm2022/lib/implementations/tile-server/index.mjs +6 -0
  105. package/esm2022/lib/implementations/tile-server/native-histogram.mjs +30 -0
  106. package/esm2022/lib/implementations/tile-server/poll-descriptor.mjs +65 -0
  107. package/esm2022/lib/implementations/tile-server/tile-protocol.mjs +42 -0
  108. package/esm2022/lib/implementations/tile-server/transport.mjs +135 -0
  109. package/esm2022/lib/intensity/intensity-profile.mjs +107 -0
  110. package/esm2022/lib/intensity/intensity-profile.service.mjs +223 -0
  111. package/esm2022/lib/intensity-inset/intensity-inset.component.mjs +133 -0
  112. package/esm2022/lib/models/bezier.mjs +1 -11
  113. package/esm2022/lib/models/class-preset.mjs +41 -3
  114. package/esm2022/lib/models/geometry.mjs +27 -164
  115. package/esm2022/lib/models/polygon-edit.mjs +254 -0
  116. package/esm2022/lib/models/polygon-factory.mjs +118 -0
  117. package/esm2022/lib/models/region-clone.mjs +68 -0
  118. package/esm2022/lib/models/region-geojson.mjs +373 -0
  119. package/esm2022/lib/models/region.mjs +34 -33
  120. package/esm2022/lib/models/shape.mjs +18 -16
  121. package/esm2022/lib/plot-mode/contribution-host.mjs +297 -0
  122. package/esm2022/lib/plot-mode/plot-mode-controller.mjs +9 -6
  123. package/esm2022/lib/plot-mode/plot-type-menu.mjs +85 -0
  124. package/esm2022/lib/plot-mode/tool-params-model.mjs +87 -0
  125. package/esm2022/lib/plot.utilities.mjs +296 -473
  126. package/esm2022/lib/processing/image-crop.mjs +1 -1
  127. package/esm2022/lib/processing/processing-image.mjs +28 -15
  128. package/esm2022/lib/provide-visualization.mjs +40 -14
  129. package/esm2022/lib/region-editor/classes-panel/classes-panel.component.mjs +99 -0
  130. package/esm2022/lib/region-editor/file-stem.mjs +13 -0
  131. package/esm2022/lib/region-editor/manage-classes-dialog/manage-classes-dialog.component.mjs +118 -0
  132. package/esm2022/lib/region-editor/mask-export.service.mjs +98 -0
  133. package/esm2022/lib/region-editor/mask-raster.mjs +11 -5
  134. package/esm2022/lib/region-editor/region-color-dialog/region-color-dialog.component.mjs +56 -0
  135. package/esm2022/lib/region-editor/region-editor-help/region-editor-help.component.mjs +28 -0
  136. package/esm2022/lib/region-editor/region-editor.component.mjs +402 -651
  137. package/esm2022/lib/region-editor/region-metrics.mjs +68 -0
  138. package/esm2022/lib/region-editor/region-persistence.service.mjs +134 -0
  139. package/esm2022/lib/region-editor/region-table/region-table.component.mjs +153 -0
  140. package/esm2022/lib/region-editor/save-mask-dialog/save-mask-dialog.component.mjs +91 -0
  141. package/esm2022/lib/region-editor/save-regions-dialog/save-regions-dialog.component.mjs +92 -0
  142. package/esm2022/lib/region-ops.service.mjs +28 -70
  143. package/esm2022/lib/region-overlay/region-geometry.mjs +360 -0
  144. package/esm2022/lib/region-overlay/region-hit-test.mjs +103 -0
  145. package/esm2022/lib/region-overlay/svg-region-renderer.mjs +228 -0
  146. package/esm2022/lib/render-orchestrator.mjs +14 -11
  147. package/esm2022/lib/render-session.mjs +207 -0
  148. package/esm2022/lib/routing-visualizer.service.mjs +459 -411
  149. package/esm2022/lib/spatial/density-raster.mjs +14 -0
  150. package/esm2022/lib/spatial/embedding-compute-coordinator.mjs +239 -0
  151. package/esm2022/lib/spatial/embedding-compute.mjs +43 -10
  152. package/esm2022/lib/spatial/gene-search.mjs +1 -1
  153. package/esm2022/lib/spatial/glyphs.mjs +97 -0
  154. package/esm2022/lib/spatial/lod.mjs +187 -0
  155. package/esm2022/lib/spatial/spatial-controls.facade.mjs +200 -0
  156. package/esm2022/lib/spatial/spatial-density.mjs +25 -16
  157. package/esm2022/lib/spatial/spatial-encoding.mjs +26 -82
  158. package/esm2022/lib/spatial/spatial-expression.mjs +43 -42
  159. package/esm2022/lib/spatial/spatial-framing.mjs +1 -1
  160. package/esm2022/lib/spatial/spatial-heatmap.mjs +6 -3
  161. package/esm2022/lib/spatial/spatial-hover.mjs +5 -33
  162. package/esm2022/lib/spatial/spatial-panel-model.mjs +282 -0
  163. package/esm2022/lib/spatial/spatial-sections.mjs +1 -1
  164. package/esm2022/lib/spatial/spatial-selection.mjs +33 -46
  165. package/esm2022/lib/spatial/spatial-tile-merge.mjs +219 -0
  166. package/esm2022/lib/spatial/spatial-tiles.mjs +12 -648
  167. package/esm2022/lib/spatial/spatial-volume-image.mjs +2 -4
  168. package/esm2022/lib/spatial/stats.mjs +71 -0
  169. package/esm2022/lib/spatial/transcript-grouping.mjs +307 -0
  170. package/esm2022/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.mjs +150 -0
  171. package/esm2022/lib/spatial-controls/spatial-charts/chart-data-model.mjs +309 -0
  172. package/esm2022/lib/spatial-controls/spatial-charts/chart-help.mjs +145 -0
  173. package/esm2022/lib/spatial-controls/spatial-charts/plotly-chart-host.mjs +158 -0
  174. package/esm2022/lib/spatial-controls/spatial-charts/spatial-chart-window/spatial-chart-window.component.mjs +47 -0
  175. package/esm2022/lib/spatial-controls/spatial-charts/spatial-charts.component.mjs +297 -802
  176. package/esm2022/lib/spatial-controls/spatial-charts/spatial-embedding-controls/spatial-embedding-controls.component.mjs +89 -0
  177. package/esm2022/lib/spatial-controls/spatial-charts/spatial-heatmap-controls/spatial-heatmap-controls.component.mjs +52 -0
  178. package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +143 -1238
  179. package/esm2022/lib/spatial-controls/spatial-gene-picker.mjs +153 -0
  180. package/esm2022/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.mjs +241 -0
  181. package/esm2022/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.mjs +193 -0
  182. package/esm2022/lib/spatial-controls/spatial-key/spatial-key.component.mjs +132 -0
  183. package/esm2022/lib/spatial-controls/spatial-key/spatial-key.model.mjs +67 -0
  184. package/esm2022/lib/spatial-controls/spatial-marker-genes-form/spatial-marker-genes-form.component.mjs +147 -0
  185. package/esm2022/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.mjs +300 -0
  186. package/esm2022/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.mjs +370 -0
  187. package/esm2022/lib/store/class-color.util.mjs +46 -3
  188. package/esm2022/lib/store/region-history.mjs +146 -0
  189. package/esm2022/lib/store/region-scope-cache.mjs +140 -0
  190. package/esm2022/lib/store/region-selection.mjs +82 -0
  191. package/esm2022/lib/store/region-store.service.mjs +279 -675
  192. package/esm2022/lib/store/spatial-selection.service.mjs +2 -2
  193. package/esm2022/lib/store/visualizer-store.service.mjs +49 -71
  194. package/esm2022/lib/toast-outlets.mjs +5 -4
  195. package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +92 -434
  196. package/esm2022/lib/toolbar/canvas-tools.mjs +20 -0
  197. package/esm2022/lib/toolbar/crop/slide-crop.mjs +26 -42
  198. package/esm2022/lib/toolbar/plot-type-selector/plot-type-selector.component.mjs +85 -0
  199. package/esm2022/lib/toolbar/region-tools/region-tool-buttons.mjs +74 -0
  200. package/esm2022/lib/toolbar/region-tools/region-tools.component.mjs +114 -0
  201. package/esm2022/lib/toolbar/segmentation/cell-segment-tool.service.mjs +74 -78
  202. package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +50 -8
  203. package/esm2022/lib/toolbar/segmentation/onnx-sam-session.mjs +61 -42
  204. package/esm2022/lib/toolbar/segmentation/pending-calls.mjs +47 -0
  205. package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +12 -4
  206. package/esm2022/lib/toolbar/segmentation/sam-onnx-core.mjs +70 -14
  207. package/esm2022/lib/toolbar/segmentation/sam-point-tool.service.mjs +135 -172
  208. package/esm2022/lib/toolbar/segmentation/sam-prompt.mjs +9 -90
  209. package/esm2022/lib/toolbar/segmentation/sam-session.service.mjs +183 -0
  210. package/esm2022/lib/toolbar/segmentation/sam-tool.service.mjs +95 -150
  211. package/esm2022/lib/toolbar/segmentation/segmentation-commit.mjs +12 -0
  212. package/esm2022/lib/toolbar/segmentation/segmentation-runner.mjs +108 -0
  213. package/esm2022/lib/toolbar/segmentation-tools/segmentation-tools.component.mjs +155 -0
  214. package/esm2022/lib/toolbar/stack-controls/stack-controls.component.mjs +81 -0
  215. package/esm2022/lib/toolbar/tool-kit/async-tool-status.mjs +37 -0
  216. package/esm2022/lib/toolbar/tool-kit/canvas-tool-manager.mjs +63 -0
  217. package/esm2022/lib/toolbar/tool-kit/canvas-tool.mjs +2 -0
  218. package/esm2022/lib/toolbar/tool-kit/frame-pixels.mjs +108 -0
  219. package/esm2022/lib/toolbar/tool-kit/mask-stroke-editor.mjs +306 -0
  220. package/esm2022/lib/toolbar/tool-kit/matrix-frame.mjs +61 -0
  221. package/esm2022/lib/toolbar/tool-kit/tool-overlay.mjs +140 -0
  222. package/esm2022/lib/toolbar/tool-kit/undo-gesture.mjs +24 -0
  223. package/esm2022/lib/toolbar/tool-slider/tool-slider.component.mjs +63 -0
  224. package/esm2022/lib/toolbar/toolbar-help-dialog/toolbar-help-dialog.component.mjs +50 -0
  225. package/esm2022/lib/toolbar/toolbar.component.mjs +68 -117
  226. package/esm2022/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.mjs +93 -115
  227. package/esm2022/lib/toolbar/view-controls/view-controls.component.mjs +107 -0
  228. package/esm2022/lib/toolbar/wand/wand-region-grow.mjs +321 -0
  229. package/esm2022/lib/toolbar/wand/wand-tool.service.mjs +121 -416
  230. package/esm2022/lib/toolbar/wand/wand.service.mjs +30 -894
  231. package/esm2022/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.mjs +48 -90
  232. package/esm2022/lib/util/mark-for-check.mjs +17 -0
  233. package/esm2022/lib/util/supersede.mjs +56 -0
  234. package/esm2022/lib/visualization.module.mjs +39 -141
  235. package/esm2022/lib/visualizer/floating-drag.directive.mjs +58 -0
  236. package/esm2022/lib/visualizer/region-actions.mjs +149 -0
  237. package/esm2022/lib/visualizer/region-load.mjs +60 -0
  238. package/esm2022/lib/visualizer/spatial-dataset-binder.mjs +163 -0
  239. package/esm2022/lib/visualizer/tool-modes.mjs +113 -0
  240. package/esm2022/lib/visualizer/viewer-shortcuts.mjs +167 -0
  241. package/esm2022/lib/visualizer/visualizer-context-menu.mjs +119 -0
  242. package/esm2022/lib/visualizer.component.mjs +480 -1902
  243. package/esm2022/lib/workers/spatial-math-ops.mjs +43 -0
  244. package/esm2022/lib/workers/spatial-math-worker.mjs +10 -0
  245. package/esm2022/lib/workers/spatial-math.mjs +190 -0
  246. package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-Csxlnq7I.mjs +41083 -0
  247. package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-Csxlnq7I.mjs.map +1 -0
  248. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-Bkujqg7m.mjs +238 -0
  249. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-Bkujqg7m.mjs.map +1 -0
  250. package/fesm2022/jax-data-science-sci-image-visualizer-spatial-math-worker-BzVFFqGS.mjs +12 -0
  251. package/fesm2022/jax-data-science-sci-image-visualizer-spatial-math-worker-BzVFFqGS.mjs.map +1 -0
  252. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +41 -34467
  253. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  254. package/fesm2022/mask.worker.js +36 -917
  255. package/fesm2022/onnx-sam.worker.js +52 -16
  256. package/fesm2022/spatial-math.worker.js +471 -0
  257. package/fesm2022/tsne.worker.js +32 -15
  258. package/index.d.ts +13 -10
  259. package/lib/channel-histogram/channel-histogram.component.d.ts +53 -43
  260. package/lib/contracts/capabilities.contract.d.ts +4 -2
  261. package/lib/contracts/cell-segmenter.contract.d.ts +1 -0
  262. package/lib/contracts/channel-histogram-api.contract.d.ts +4 -0
  263. package/lib/contracts/color.d.ts +27 -0
  264. package/lib/contracts/colormap-lut.d.ts +19 -4
  265. package/lib/contracts/coordinate-transform.contract.d.ts +3 -3
  266. package/lib/contracts/display-types.d.ts +21 -0
  267. package/lib/contracts/instance-segmenter.contract.d.ts +5 -0
  268. package/lib/contracts/intensity.d.ts +13 -2
  269. package/lib/contracts/plot-type.d.ts +3 -1
  270. package/lib/contracts/ports/image-state.port.d.ts +6 -2
  271. package/lib/contracts/ports/preferences.port.d.ts +4 -0
  272. package/lib/contracts/ports/region-io.port.d.ts +1 -0
  273. package/lib/contracts/ports/spatial-data.port.d.ts +4 -10
  274. package/lib/contracts/ports/tile-access.port.d.ts +1 -0
  275. package/lib/contracts/region-editor-api.contract.d.ts +1 -1
  276. package/lib/contracts/region-overlay.contract.d.ts +5 -0
  277. package/lib/contracts/sam.contract.d.ts +7 -0
  278. package/lib/contracts/semantic-segmenter.contract.d.ts +6 -0
  279. package/lib/contracts/spatial-dataset.contract.d.ts +19 -6
  280. package/lib/contracts/toolbar-tool.contract.d.ts +1 -0
  281. package/lib/contracts/visualizer.contract.d.ts +221 -54
  282. package/lib/contracts/viz-config.d.ts +1 -0
  283. package/lib/geometry/contour.d.ts +24 -0
  284. package/lib/geometry/raster.d.ts +42 -0
  285. package/lib/geometry/ring.d.ts +67 -0
  286. package/lib/hex-color-picker/hex-color-picker.component.d.ts +61 -27
  287. package/lib/implementations/base-store-visualizer.d.ts +61 -15
  288. package/lib/implementations/napari-js/napari-axes-gizmo.d.ts +36 -0
  289. package/lib/implementations/napari-js/napari-display-state.d.ts +76 -0
  290. package/lib/implementations/napari-js/napari-helpers.d.ts +129 -0
  291. package/lib/implementations/napari-js/napari-image-2d-scene.d.ts +162 -0
  292. package/lib/implementations/napari-js/napari-loading-badge.d.ts +0 -1
  293. package/lib/implementations/napari-js/napari-loading-state.d.ts +30 -0
  294. package/lib/implementations/napari-js/napari-region-overlay.d.ts +62 -70
  295. package/lib/implementations/napari-js/napari-scene.d.ts +93 -0
  296. package/lib/implementations/napari-js/napari-spatial-2d-scene.d.ts +163 -0
  297. package/lib/implementations/napari-js/napari-spatial-3d-scene.d.ts +212 -0
  298. package/lib/implementations/napari-js/napari-spatial-encoding.d.ts +114 -0
  299. package/lib/implementations/napari-js/napari-spatial-hover.d.ts +89 -0
  300. package/lib/implementations/napari-js/napari-spatial-scene.d.ts +118 -0
  301. package/lib/implementations/napari-js/napari-spatial-tiles.d.ts +56 -263
  302. package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +1 -1
  303. package/lib/implementations/napari-js/napari-surface-scene.d.ts +76 -0
  304. package/lib/implementations/napari-js/napari-tile-client.d.ts +214 -0
  305. package/lib/implementations/napari-js/napari-tool-bridge.d.ts +179 -0
  306. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +121 -883
  307. package/lib/implementations/napari-js/napari-volume-scene.d.ts +104 -0
  308. package/lib/implementations/napari-js/napari-volume-z-handle.d.ts +8 -12
  309. package/lib/implementations/napari-js/region-overlay/overlay-projection.d.ts +59 -0
  310. package/lib/implementations/napari-js/region-overlay/region-edit.d.ts +15 -0
  311. package/lib/implementations/napari-js/region-overlay/region-svg-renderer.d.ts +54 -0
  312. package/lib/implementations/napari-js/spatial-tiles/categorical-lookup.d.ts +26 -0
  313. package/lib/implementations/napari-js/spatial-tiles/cell-layers.d.ts +70 -0
  314. package/lib/implementations/napari-js/spatial-tiles/density-layer.d.ts +61 -0
  315. package/lib/implementations/napari-js/spatial-tiles/layer-groups.d.ts +67 -0
  316. package/lib/implementations/napari-js/spatial-tiles/plan-context.d.ts +38 -0
  317. package/lib/implementations/napari-js/spatial-tiles/transcript-hover.d.ts +71 -0
  318. package/lib/implementations/napari-js/spatial-tiles/transcript-jobs.d.ts +105 -0
  319. package/lib/implementations/napari-js/spatial-tiles/transcript-layers.d.ts +56 -0
  320. package/lib/implementations/osd/display-pipeline.d.ts +38 -9
  321. package/lib/implementations/osd/histogram-sampler.d.ts +22 -12
  322. package/lib/implementations/osd/openseadragon-viewer-options.d.ts +35 -0
  323. package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +78 -354
  324. package/lib/implementations/osd/osd-coordinate-transform.d.ts +2 -1
  325. package/lib/implementations/osd/osd-coords.d.ts +6 -4
  326. package/lib/implementations/osd/osd-export.d.ts +36 -0
  327. package/lib/implementations/osd/osd-lib.d.ts +7 -0
  328. package/lib/implementations/osd/osd-navigator-chrome.d.ts +69 -0
  329. package/lib/implementations/osd/osd-pixel-readback.d.ts +22 -0
  330. package/lib/implementations/osd/osd-region-overlay-draw.d.ts +37 -0
  331. package/lib/implementations/osd/osd-region-overlay.d.ts +36 -48
  332. package/lib/implementations/osd/osd-scale-bar.d.ts +8 -6
  333. package/lib/implementations/osd/osd-tile-source.d.ts +49 -0
  334. package/lib/implementations/osd/osd-viewer-like.d.ts +100 -0
  335. package/lib/implementations/osd/osd-viewer-mount.d.ts +14 -0
  336. package/lib/implementations/osd/osd-viewport.d.ts +77 -0
  337. package/lib/implementations/osd/osd-zoom.d.ts +6 -0
  338. package/lib/implementations/osd/simple-source.d.ts +161 -0
  339. package/lib/implementations/osd/slice-cache.d.ts +12 -3
  340. package/lib/implementations/osd/tile-client.d.ts +11 -14
  341. package/lib/implementations/osd/tile-recolor.d.ts +141 -0
  342. package/lib/implementations/plotly/omics-trace-builders.d.ts +32 -20
  343. package/lib/implementations/plotly/plotly-image-loader.d.ts +49 -0
  344. package/lib/implementations/plotly/plotly-image-recolor.d.ts +75 -0
  345. package/lib/implementations/plotly/plotly-intensity-inset.d.ts +8 -0
  346. package/lib/implementations/plotly/plotly-isosurface-controls.d.ts +38 -0
  347. package/lib/implementations/plotly/plotly-layouts.d.ts +188 -0
  348. package/lib/implementations/plotly/plotly-readback.d.ts +41 -0
  349. package/lib/implementations/plotly/plotly-region-overlay.d.ts +2 -2
  350. package/lib/implementations/plotly/plotly-shape-projection.d.ts +92 -0
  351. package/lib/implementations/plotly/plotly-trace-builders.d.ts +23 -12
  352. package/lib/implementations/plotly/plotly-zoom-controller.d.ts +86 -0
  353. package/lib/implementations/plotly/plotly.service.d.ts +136 -401
  354. package/lib/implementations/region-centroids.d.ts +14 -0
  355. package/lib/implementations/simple-slice-access.service.d.ts +7 -4
  356. package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +42 -18
  357. package/lib/implementations/spatial-data-http/spatial-wire.d.ts +34 -9
  358. package/lib/implementations/tile-server/index.d.ts +5 -0
  359. package/lib/implementations/tile-server/native-histogram.d.ts +25 -0
  360. package/lib/implementations/tile-server/poll-descriptor.d.ts +39 -0
  361. package/lib/implementations/tile-server/tile-protocol.d.ts +80 -0
  362. package/lib/implementations/tile-server/transport.d.ts +62 -0
  363. package/lib/intensity/intensity-profile.d.ts +66 -0
  364. package/lib/intensity/intensity-profile.service.d.ts +108 -0
  365. package/lib/intensity-inset/intensity-inset.component.d.ts +58 -0
  366. package/lib/models/bezier.d.ts +0 -5
  367. package/lib/models/class-preset.d.ts +18 -0
  368. package/lib/models/geometry.d.ts +14 -48
  369. package/lib/models/polygon-edit.d.ts +72 -0
  370. package/lib/models/polygon-factory.d.ts +40 -0
  371. package/lib/models/region-clone.d.ts +20 -0
  372. package/lib/models/region-geojson.d.ts +17 -0
  373. package/lib/models/region.d.ts +40 -4
  374. package/lib/models/shape.d.ts +8 -0
  375. package/lib/plot-mode/contribution-host.d.ts +125 -0
  376. package/lib/plot-mode/plot-type-menu.d.ts +60 -0
  377. package/lib/plot-mode/tool-params-model.d.ts +49 -0
  378. package/lib/plot.utilities.d.ts +19 -30
  379. package/lib/processing/processing-image.d.ts +5 -2
  380. package/lib/provide-visualization.d.ts +19 -2
  381. package/lib/region-editor/classes-panel/classes-panel.component.d.ts +56 -0
  382. package/lib/region-editor/file-stem.d.ts +7 -0
  383. package/lib/region-editor/manage-classes-dialog/manage-classes-dialog.component.d.ts +45 -0
  384. package/lib/region-editor/mask-export.service.d.ts +54 -0
  385. package/lib/region-editor/mask-raster.d.ts +2 -8
  386. package/lib/region-editor/region-color-dialog/region-color-dialog.component.d.ts +29 -0
  387. package/lib/region-editor/region-editor-help/region-editor-help.component.d.ts +11 -0
  388. package/lib/region-editor/region-editor.component.d.ts +166 -199
  389. package/lib/region-editor/region-metrics.d.ts +26 -0
  390. package/lib/region-editor/region-persistence.service.d.ts +69 -0
  391. package/lib/region-editor/region-table/region-table.component.d.ts +83 -0
  392. package/lib/region-editor/save-mask-dialog/save-mask-dialog.component.d.ts +36 -0
  393. package/lib/region-editor/save-regions-dialog/save-regions-dialog.component.d.ts +37 -0
  394. package/lib/region-ops.service.d.ts +1 -7
  395. package/lib/region-overlay/region-geometry.d.ts +157 -0
  396. package/lib/region-overlay/region-hit-test.d.ts +38 -0
  397. package/lib/region-overlay/svg-region-renderer.d.ts +110 -0
  398. package/lib/render-orchestrator.d.ts +6 -2
  399. package/lib/render-session.d.ts +75 -0
  400. package/lib/routing-visualizer.service.d.ts +110 -58
  401. package/lib/spatial/density-raster.d.ts +6 -0
  402. package/lib/spatial/embedding-compute-coordinator.d.ts +123 -0
  403. package/lib/spatial/embedding-compute.d.ts +21 -2
  404. package/lib/spatial/glyphs.d.ts +16 -0
  405. package/lib/spatial/lod.d.ts +94 -0
  406. package/lib/spatial/spatial-controls.facade.d.ts +64 -0
  407. package/lib/spatial/spatial-density.d.ts +15 -5
  408. package/lib/spatial/spatial-encoding.d.ts +11 -44
  409. package/lib/spatial/spatial-expression.d.ts +40 -22
  410. package/lib/spatial/spatial-framing.d.ts +1 -0
  411. package/lib/spatial/spatial-heatmap.d.ts +2 -0
  412. package/lib/spatial/spatial-hover.d.ts +4 -16
  413. package/lib/spatial/spatial-panel-model.d.ts +135 -0
  414. package/lib/spatial/spatial-selection.d.ts +21 -26
  415. package/lib/spatial/spatial-tile-merge.d.ts +41 -0
  416. package/lib/spatial/spatial-tiles.d.ts +9 -256
  417. package/lib/spatial/stats.d.ts +38 -0
  418. package/lib/spatial/transcript-grouping.d.ts +119 -0
  419. package/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.d.ts +64 -0
  420. package/lib/spatial-controls/spatial-charts/chart-data-model.d.ts +153 -0
  421. package/lib/spatial-controls/spatial-charts/chart-help.d.ts +69 -0
  422. package/lib/spatial-controls/spatial-charts/plotly-chart-host.d.ts +98 -0
  423. package/lib/spatial-controls/spatial-charts/spatial-chart-window/spatial-chart-window.component.d.ts +26 -0
  424. package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +101 -260
  425. package/lib/spatial-controls/spatial-charts/spatial-embedding-controls/spatial-embedding-controls.component.d.ts +43 -0
  426. package/lib/spatial-controls/spatial-charts/spatial-heatmap-controls/spatial-heatmap-controls.component.d.ts +27 -0
  427. package/lib/spatial-controls/spatial-controls.component.d.ts +68 -448
  428. package/lib/spatial-controls/spatial-gene-picker.d.ts +86 -0
  429. package/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.d.ts +102 -0
  430. package/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.d.ts +72 -0
  431. package/lib/spatial-controls/spatial-key/spatial-key.component.d.ts +66 -0
  432. package/lib/spatial-controls/spatial-key/spatial-key.model.d.ts +33 -0
  433. package/lib/spatial-controls/spatial-marker-genes-form/spatial-marker-genes-form.component.d.ts +64 -0
  434. package/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.d.ts +117 -0
  435. package/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.d.ts +142 -0
  436. package/lib/store/class-color.util.d.ts +35 -1
  437. package/lib/store/region-history.d.ts +66 -0
  438. package/lib/store/region-scope-cache.d.ts +63 -0
  439. package/lib/store/region-selection.d.ts +36 -0
  440. package/lib/store/region-store.service.d.ts +104 -112
  441. package/lib/store/visualizer-store.service.d.ts +22 -33
  442. package/lib/toast-outlets.d.ts +4 -3
  443. package/lib/toolbar/brush/brush-tool.service.d.ts +31 -98
  444. package/lib/toolbar/canvas-tools.d.ts +20 -0
  445. package/lib/toolbar/crop/slide-crop.d.ts +10 -4
  446. package/lib/toolbar/plot-type-selector/plot-type-selector.component.d.ts +38 -0
  447. package/lib/toolbar/region-tools/region-tool-buttons.d.ts +21 -0
  448. package/lib/toolbar/region-tools/region-tools.component.d.ts +60 -0
  449. package/lib/toolbar/segmentation/cell-segment-tool.service.d.ts +10 -14
  450. package/lib/toolbar/segmentation/cellpose-segmenter.service.d.ts +12 -2
  451. package/lib/toolbar/segmentation/onnx-sam-session.d.ts +22 -3
  452. package/lib/toolbar/segmentation/pending-calls.d.ts +31 -0
  453. package/lib/toolbar/segmentation/sam-model-registry.d.ts +9 -3
  454. package/lib/toolbar/segmentation/sam-onnx-core.d.ts +18 -5
  455. package/lib/toolbar/segmentation/sam-point-tool.service.d.ts +46 -32
  456. package/lib/toolbar/segmentation/sam-prompt.d.ts +7 -31
  457. package/lib/toolbar/segmentation/sam-session.service.d.ts +74 -0
  458. package/lib/toolbar/segmentation/sam-tool.service.d.ts +23 -30
  459. package/lib/toolbar/segmentation/segmentation-commit.d.ts +8 -0
  460. package/lib/toolbar/segmentation/segmentation-runner.d.ts +52 -0
  461. package/lib/toolbar/segmentation-tools/segmentation-tools.component.d.ts +87 -0
  462. package/lib/toolbar/stack-controls/stack-controls.component.d.ts +40 -0
  463. package/lib/toolbar/tool-kit/async-tool-status.d.ts +21 -0
  464. package/lib/toolbar/tool-kit/canvas-tool-manager.d.ts +36 -0
  465. package/lib/toolbar/tool-kit/canvas-tool.d.ts +86 -0
  466. package/lib/toolbar/tool-kit/frame-pixels.d.ts +45 -0
  467. package/lib/toolbar/tool-kit/mask-stroke-editor.d.ts +125 -0
  468. package/lib/toolbar/tool-kit/matrix-frame.d.ts +40 -0
  469. package/lib/toolbar/tool-kit/tool-overlay.d.ts +60 -0
  470. package/lib/toolbar/tool-kit/undo-gesture.d.ts +17 -0
  471. package/lib/toolbar/tool-slider/tool-slider.component.d.ts +33 -0
  472. package/lib/toolbar/toolbar-help-dialog/toolbar-help-dialog.component.d.ts +24 -0
  473. package/lib/toolbar/toolbar.component.d.ts +47 -83
  474. package/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.d.ts +33 -40
  475. package/lib/toolbar/view-controls/view-controls.component.d.ts +54 -0
  476. package/lib/toolbar/wand/wand-region-grow.d.ts +49 -0
  477. package/lib/toolbar/wand/wand-tool.service.d.ts +39 -124
  478. package/lib/toolbar/wand/wand.service.d.ts +22 -116
  479. package/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.d.ts +17 -37
  480. package/lib/util/mark-for-check.d.ts +11 -0
  481. package/lib/util/supersede.d.ts +53 -0
  482. package/lib/visualization.module.d.ts +18 -39
  483. package/lib/visualizer/floating-drag.directive.d.ts +30 -0
  484. package/lib/visualizer/region-actions.d.ts +54 -0
  485. package/lib/visualizer/region-load.d.ts +27 -0
  486. package/lib/visualizer/spatial-dataset-binder.d.ts +74 -0
  487. package/lib/visualizer/tool-modes.d.ts +49 -0
  488. package/lib/visualizer/viewer-shortcuts.d.ts +71 -0
  489. package/lib/visualizer/visualizer-context-menu.d.ts +55 -0
  490. package/lib/visualizer.component.d.ts +201 -447
  491. package/lib/workers/spatial-math-ops.d.ts +65 -0
  492. package/lib/workers/spatial-math-worker.d.ts +7 -0
  493. package/lib/workers/spatial-math.d.ts +74 -0
  494. package/package.json +12 -3
  495. package/src/lib/styles/_viz-icons-rules.scss +73 -0
  496. package/src/lib/styles/viz-icons.scss +14 -0
  497. package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +0 -97
  498. package/esm2022/lib/implementations/napari-js/napari-navigator.mjs +0 -195
  499. package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +0 -92
  500. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs +0 -329
  501. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs.map +0 -1
  502. package/lib/implementations/napari-js/napari-axes-labels.d.ts +0 -49
  503. package/lib/implementations/napari-js/napari-navigator.d.ts +0 -72
  504. package/lib/implementations/napari-js/napari-scale-bar.d.ts +0 -35
  505. package/src/lib/assets/cell-detection.svg +0 -37
  506. package/src/lib/assets/click.svg +0 -1
  507. package/src/lib/assets/colormap-luts.json +0 -1
  508. package/src/lib/assets/edit-selection-white.svg +0 -47
  509. package/src/lib/assets/icons/colormap-earth.png +0 -0
  510. package/src/lib/assets/icons/colormap-placeholder.png +0 -0
  511. package/src/lib/assets/icons/colormap-pubu.png +0 -0
  512. package/src/lib/assets/interactive-segmentation-cursor.svg +0 -1
  513. package/src/lib/assets/picture-segmentation.svg +0 -43
  514. package/src/lib/assets/test/download-379.svg +0 -1
  515. package/src/lib/assets/test/download-98.svg +0 -1
  516. package/src/lib/assets/test/json-1.svg +0 -1
  517. package/src/lib/assets/test/load-53.svg +0 -1
  518. package/src/lib/assets/test/mask-44.svg +0 -1
  519. package/src/lib/assets/test/mask-download-44.svg +0 -15
  520. package/src/lib/assets/test/mask-save-44.svg +0 -11
  521. package/src/lib/assets/test/overlay-1.svg +0 -1
  522. package/src/lib/assets/test/overlay-4.svg +0 -1
  523. package/src/lib/assets/test/region-26.svg +0 -1
  524. package/src/lib/assets/test/save-56.svg +0 -1
  525. package/src/lib/assets/test/upload-43.svg +0 -1
@@ -1,20 +1,23 @@
1
- import { Component, EventEmitter, Input, Output } from '@angular/core';
2
- import { PlotType, isBuiltinPlotType, isNapari3d, isNapariIsosurface, isNapariSurface, isNapariScatter, NAPARI_DECIMATE_OPTIONS, NAPARI_DEFAULT_DECIMATE, isSpatialOmics, isSpatialOmics3d } from '../contracts/plot-type';
1
+ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { ButtonModule } from 'primeng/button';
4
+ import { RippleModule } from 'primeng/ripple';
5
+ import { ToolbarModule } from 'primeng/toolbar';
6
+ import { TooltipModule } from 'primeng/tooltip';
7
+ import { PlotType, isBuiltinPlotType, isNapari3d, isNapariIsosurface, isNapariSurface, isNapariScatter, NAPARI_DEFAULT_DECIMATE, isSpatialOmics, isSpatialOmics3d, } from '../contracts/plot-type';
3
8
  import { ALL_TOOLBAR_TOOLS } from '../contracts/toolbar-config';
4
- import { MODEL_INFO } from './model-info';
9
+ import { ToolbarHelpDialogComponent } from './toolbar-help-dialog/toolbar-help-dialog.component';
10
+ import { PlotTypeSelectorComponent } from './plot-type-selector/plot-type-selector.component';
11
+ import { StackControlsComponent } from './stack-controls/stack-controls.component';
12
+ import { ViewControlsComponent } from './view-controls/view-controls.component';
13
+ import { RegionToolsComponent } from './region-tools/region-tools.component';
14
+ import { SegmentationToolsComponent } from './segmentation-tools/segmentation-tools.component';
5
15
  import * as i0 from "@angular/core";
6
16
  import * as i1 from "@angular/common";
7
- import * as i2 from "@angular/forms";
8
- import * as i3 from "primeng/toolbar";
9
- import * as i4 from "primeng/api";
10
- import * as i5 from "primeng/button";
11
- import * as i6 from "primeng/menu";
12
- import * as i7 from "primeng/tooltip";
13
- import * as i8 from "primeng/dropdown";
14
- import * as i9 from "primeng/slider";
15
- import * as i10 from "primeng/inputnumber";
16
- import * as i11 from "primeng/dialog";
17
- import * as i12 from "primeng/ripple";
17
+ import * as i2 from "primeng/button";
18
+ import * as i3 from "primeng/ripple";
19
+ import * as i4 from "primeng/toolbar";
20
+ import * as i5 from "primeng/tooltip";
18
21
  /**
19
22
  * Presentational toolbar for the plotting viewport.
20
23
  *
@@ -46,7 +49,9 @@ export class ToolbarComponent {
46
49
  /** Isosurface band as a 0–255 slider position (mapped onto the volume's real
47
50
  * intensity range by the renderer). Defaults to the full range. */
48
51
  this.isoRange = [0, 255];
52
+ /** The last slice index of a stack. */
49
53
  this.maxIndex = 0;
54
+ /** The current slice (0-based). */
50
55
  this.zIndex = 0;
51
56
  /** True for any 2D (non Surface-3D) plot type. */
52
57
  this.isHeatmap = true;
@@ -57,17 +62,21 @@ export class ToolbarComponent {
57
62
  * controls — flipping that would show 2D pan/zoom and hide the orbit controls.
58
63
  */
59
64
  this.is3dRegions = false;
65
+ /** The armed tool's mode (zoom, pan, a drawing tool, `samPoint`, …), or null. */
60
66
  this.activeDragMode = null;
67
+ /** The 3D camera mode (`turntable` / `orbit` / …). */
61
68
  this.activeSurface3dMode = 'turntable';
62
69
  /** Whether the napari 3D axes/scale gizmo is currently shown (drives the toggle's look). */
63
70
  this.axesVisible = true;
71
+ /** The napari surface wireframe is on. */
64
72
  this.wireframeActive = false;
65
73
  /** Active napari 3D decimate factor (1 = Full … 8 = ⅛; default ½). */
66
74
  this.resolutionScale = NAPARI_DEFAULT_DECIMATE;
67
- /** Decimate-factor options for the Resolution dropdown. */
68
- this.decimateOptions = NAPARI_DECIMATE_OPTIONS;
75
+ /** Magic-wand sensitivity. */
69
76
  this.wandSensitivity = 2.0;
77
+ /** Brush diameter in image pixels. */
70
78
  this.brushSize = 40;
79
+ /** Vertex-eraser radius in image pixels. */
71
80
  this.vertexEraserRadius = 20;
72
81
  /** Whether a region action is available to undo (jit-ui#85). Greys out the
73
82
  * Undo button when false (nothing done yet, or all steps already undone). */
@@ -75,7 +84,11 @@ export class ToolbarComponent {
75
84
  /** Whether an undone region action is available to redo (jit-ui#85). Greys
76
85
  * out the Redo button when false. */
77
86
  this.canRedo = false;
78
- this.stackOptions = [{ name: 'Single image', val: 'false' }, { name: 'Stack', val: 'true' }];
87
+ /** The single-image / stack choices of the Plotly stack toggle. */
88
+ this.stackOptions = [
89
+ { name: 'Single image', val: 'false' },
90
+ { name: 'Stack', val: 'true' },
91
+ ];
79
92
  /** Which toolbar groups to show. Defaults to the full toolbar; the host forwards
80
93
  * the consumer's choice (e.g. the pipeline shows only zoom + region tools). */
81
94
  this.tools = ALL_TOOLBAR_TOOLS;
@@ -84,6 +97,7 @@ export class ToolbarComponent {
84
97
  this.imageSmoothingEnabled = false;
85
98
  /** SAM model picker options + current selection (jit-ui#90 P1). */
86
99
  this.samModels = [];
100
+ /** The selected SAM model id. */
87
101
  this.samModelId = '';
88
102
  /**
89
103
  * Tools contributed through {@link TOOLBAR_TOOLS}, already filtered to the
@@ -97,6 +111,7 @@ export class ToolbarComponent {
97
111
  this.openDialogToolId = null;
98
112
  /** Active checkpoint per contributed tool, keyed by tool id. */
99
113
  this.toolModelIds = {};
114
+ /** A plot type or contributed mode was picked. */
100
115
  this.selectPlotType = new EventEmitter();
101
116
  /** Intensity (LINE) mode: add another colored line ROI + inset trace. */
102
117
  this.addProfileLine = new EventEmitter();
@@ -104,23 +119,37 @@ export class ToolbarComponent {
104
119
  this.toggleImageSmoothing = new EventEmitter();
105
120
  // PrimeNG slider/inputNumber change events carry optional values, mirrored by
106
121
  // the host handlers (which accept `| undefined`).
122
+ /** The isosurface band slider moved: the new `[low, high]` pair. */
107
123
  this.isoRangeChange = new EventEmitter();
108
124
  /** Open the Channels & Histogram dialog (brightness/contrast, colormap, …). */
109
125
  this.openChannelHistogram = new EventEmitter();
110
126
  /** Open the spatial-omics controls (colour-by, legend, point display). */
111
127
  this.openSpatialControls = new EventEmitter();
128
+ /** Open the Region Editor dialog. */
112
129
  this.openRegionEditor = new EventEmitter();
130
+ /** A single-image / stack choice was picked. */
113
131
  this.selectStackOption = new EventEmitter();
132
+ /** The live slice scrubber moved (fires while dragging). */
114
133
  this.zScrub = new EventEmitter();
134
+ /** The live slice scrubber was released on a slice. */
115
135
  this.zSlide = new EventEmitter();
136
+ /** A slice number was typed. */
116
137
  this.zIndexInput = new EventEmitter();
138
+ /** Re-plot at the typed slice. */
117
139
  this.reloadAndPlot = new EventEmitter();
140
+ /** Download the current view as PNG. */
118
141
  this.downloadImage = new EventEmitter();
142
+ /** Fit the image to the view. */
119
143
  this.autoscaleImage = new EventEmitter();
144
+ /** A zoom / pan / drawing tool button was clicked (its mode). */
120
145
  this.toggleDragMode = new EventEmitter();
146
+ /** Zoom in one step. */
121
147
  this.zoomIn = new EventEmitter();
148
+ /** Zoom out one step. */
122
149
  this.zoomOut = new EventEmitter();
150
+ /** A 3D camera mode button was clicked (its mode). */
123
151
  this.toggleSurface3dMode = new EventEmitter();
152
+ /** Reset the 3D camera. */
124
153
  this.resetSurfaceCamera = new EventEmitter();
125
154
  /** Toggle the napari 3D coordinate-axes / scale gizmo on/off. */
126
155
  this.toggleAxes = new EventEmitter();
@@ -128,6 +157,7 @@ export class ToolbarComponent {
128
157
  this.toggleWireframe = new EventEmitter();
129
158
  /** Change the napari 3D decimate factor (reloads at the new resolution). */
130
159
  this.selectResolution = new EventEmitter();
160
+ /** Delete the selected region. */
131
161
  this.deleteRegion = new EventEmitter();
132
162
  /** Undo the last region action (jit-ui#85). */
133
163
  this.undoRegion = new EventEmitter();
@@ -147,104 +177,19 @@ export class ToolbarComponent {
147
177
  this.openToolParams = new EventEmitter();
148
178
  /** SAM model picker (jit-ui#90 P1). */
149
179
  this.samModelChange = new EventEmitter();
180
+ /** The wand sensitivity slider moved. */
150
181
  this.wandSensitivityChange = new EventEmitter();
182
+ /** The brush size slider moved. */
151
183
  this.brushSizeChange = new EventEmitter();
184
+ /** The vertex-eraser radius slider moved. */
152
185
  this.vertexEraserRadiusChange = new EventEmitter();
153
- /** Sliders' bounds — toolbar UI constants (the values are host-owned). */
154
- this.wandSensitivityMin = 0.5;
155
- this.wandSensitivityMax = 10.0;
156
- this.wandSensitivityStep = 0.1;
157
- this.brushSizeMin = 5;
158
- this.brushSizeMax = 300;
159
- this.brushSizeStep = 5;
160
- this.vertexEraserRadiusMin = 5;
161
- this.vertexEraserRadiusMax = 300;
162
- this.vertexEraserRadiusStep = 5;
163
- this.isoValueMin = 0;
164
- this.isoValueMax = 255;
165
- this.isoValueStep = 1;
166
186
  this.displayHelpDialog = false;
167
- /** Model picker for the Segment button's dropdown menu. The active model
168
- * (`samModelId`) is marked with a check; selecting an item emits
169
- * `samModelChange` (jit-ui#90 P1).
170
- *
171
- * Held as a stable array (rebuilt only when `samModels`/`samModelId` change),
172
- * NOT a getter: a getter returns a fresh array with new `command` closures on
173
- * every change-detection tick, which makes the bound `p-menu` overlay rebuild
174
- * its DOM mid-interaction and swallow the click on a menu item.
175
- *
176
- * `tooltip` carries the model's description from {@link MODEL_INFO}; the
177
- * menus' shared item template turns it into the hover info icon. p-menu's own
178
- * rendering ignores the field (it reads `item.title`), so it is free to use. */
179
- this.samMenuItems = [];
180
- /**
181
- * Model menus for the contributed tools, keyed by tool id. Same stable-array
182
- * reasoning as {@link samMenuItems} — and it is why this is a map rebuilt in
183
- * `ngOnChanges` rather than a method called from the template, which would
184
- * hand `p-menu` a new array every tick.
185
- *
186
- * A contributed tool supplies its own per-model description, so unlike the
187
- * SAM menu these do not consult {@link MODEL_INFO}: this library has no
188
- * copy for checkpoints it does not know about.
189
- */
190
- this.toolMenuItems = {};
191
187
  }
192
188
  /** Whether the plot-type dropdown shows: for an image, or a spatial dataset with none. */
193
189
  get showPlotTypes() {
194
190
  return !!(this.imageInfo || this.hasSpatialDataset) && !!this.tools.specialTools;
195
191
  }
196
- /** Rebuild the SAM model menu when the model list or active selection
197
- * changes (keeps the array reference stable across other CD ticks). */
198
- ngOnChanges(changes) {
199
- if (changes['samModels'] || changes['samModelId']) {
200
- this.samMenuItems = this.samModels.map((m) => ({
201
- label: m.label,
202
- icon: m.id === this.samModelId ? 'pi pi-check' : 'pi pi-fw',
203
- tooltip: MODEL_INFO[m.id],
204
- command: () => this.samModelChange.emit(m.id),
205
- }));
206
- }
207
- if (changes['contributedTools'] || changes['toolModelIds']) {
208
- this.toolMenuItems = {};
209
- for (const tool of this.contributedTools) {
210
- const active = this.toolModelIds[tool.id] ?? tool.defaultModelId();
211
- this.toolMenuItems[tool.id] = tool.models().map((m) => ({
212
- label: m.label,
213
- icon: m.id === active ? 'pi pi-check' : 'pi pi-fw',
214
- tooltip: m.info,
215
- command: () => this.toolModelChange.emit({ toolId: tool.id, modelId: m.id }),
216
- }));
217
- }
218
- }
219
- }
220
- /**
221
- * Strip markup from a model description so it can be read aloud.
222
- *
223
- * The copy in MODEL_INFO and in a contribution's `ToolModelOption.info` is
224
- * written for a visual tooltip rendered with `[escape]="false"`, so it carries
225
- * `<b>` and `<br>`. Passed to `aria-label` verbatim a screen reader announces
226
- * the tags, so they are removed and `<br>` becomes a sentence break. Entities
227
- * used in that copy (`&nbsp;`, `&times;`, `&amp;`) are decoded for the same
228
- * reason.
229
- */
230
- plainText(html) {
231
- return (html ?? '')
232
- .replace(/<br\s*\/?>/gi, '. ')
233
- .replace(/<[^>]+>/g, '')
234
- .replace(/&nbsp;/g, ' ')
235
- .replace(/&times;/g, 'x')
236
- .replace(/&amp;/g, '&')
237
- .replace(/\s+/g, ' ')
238
- .trim();
239
- }
240
- /** Contributed tool names for the help dialog's prompted/no-prompt contrast,
241
- * e.g. "YOLO, Retinal layers". Empty when nothing is registered, which is
242
- * why the sentence that uses it is itself conditional. */
243
- get contributedToolNames() {
244
- return this.contributedTools.map((t) => t.label).join(', ');
245
- }
246
- /** Keeps each contributed tool's `p-menu` overlay alive across CD ticks —
247
- * re-creating it mid-interaction swallows the click on a menu item. */
192
+ /** trackBy for the dialog tools' buttons. */
248
193
  trackToolById(_index, tool) {
249
194
  return tool.id;
250
195
  }
@@ -266,16 +211,10 @@ export class ToolbarComponent {
266
211
  get isSpatialMode() {
267
212
  return isSpatialOmics(this.effectivePlotType) || isSpatialOmics3d(this.effectivePlotType);
268
213
  }
269
- /** A plot-type icon is a PrimeNG font glyph (e.g. `pi pi-image`) rather than an
270
- * SVG asset path — drives which element the selector item template renders. */
271
- isPiIcon(icon) {
272
- return !!icon && icon.startsWith('pi ');
273
- }
274
214
  /** Backends with a vertex-editing region overlay: OSD Image and napari-js WebGPU image
275
215
  * (jit-ui#102). Gates the polygon / add-vertex / delete-vertex / Bézier-convert tools. */
276
216
  get supportsRegionVertexTools() {
277
- return (this.effectivePlotType === PlotType.IMAGE ||
278
- this.effectivePlotType === PlotType.NAPARI_IMAGE);
217
+ return this.effectivePlotType === PlotType.IMAGE || this.effectivePlotType === PlotType.NAPARI_IMAGE;
279
218
  }
280
219
  /** Plot types that scrub a z-stack live (the renderer swaps the slice in place): the OSD Image
281
220
  * view, the napari-js WebGPU image, the napari-js surface — which rebuilds the height field
@@ -298,7 +237,7 @@ export class ToolbarComponent {
298
237
  }
299
238
  /** ISOSURFACE (Plotly or napari-js WebGPU, either resolution) shows the isovalue range slider. */
300
239
  get isIsosurfaceMode() {
301
- return (this.effectivePlotType === PlotType.ISOSURFACE || isNapariIsosurface(this.effectivePlotType));
240
+ return this.effectivePlotType === PlotType.ISOSURFACE || isNapariIsosurface(this.effectivePlotType);
302
241
  }
303
242
  /** The napari-js WebGPU surface — shows the wireframe toggle. */
304
243
  get isNapariSurfaceMode() {
@@ -317,11 +256,23 @@ export class ToolbarComponent {
317
256
  this.displayHelpDialog = true;
318
257
  }
319
258
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: ToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
320
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: ToolbarComponent, selector: "plotting-toolbar", inputs: { imageInfo: "imageInfo", hasSpatialDataset: "hasSpatialDataset", plotTypeOptions: "plotTypeOptions", selectedPlotType: "selectedPlotType", basePlotType: "basePlotType", isoRange: "isoRange", maxIndex: "maxIndex", zIndex: "zIndex", isHeatmap: "isHeatmap", is3dRegions: "is3dRegions", activeDragMode: "activeDragMode", activeSurface3dMode: "activeSurface3dMode", axesVisible: "axesVisible", wireframeActive: "wireframeActive", resolutionScale: "resolutionScale", wandSensitivity: "wandSensitivity", brushSize: "brushSize", vertexEraserRadius: "vertexEraserRadius", canUndo: "canUndo", canRedo: "canRedo", stackOptions: "stackOptions", tools: "tools", imageSmoothingEnabled: "imageSmoothingEnabled", samModels: "samModels", samModelId: "samModelId", contributedTools: "contributedTools", dialogTools: "dialogTools", openDialogToolId: "openDialogToolId", toolModelIds: "toolModelIds" }, outputs: { selectPlotType: "selectPlotType", addProfileLine: "addProfileLine", toggleImageSmoothing: "toggleImageSmoothing", isoRangeChange: "isoRangeChange", openChannelHistogram: "openChannelHistogram", openSpatialControls: "openSpatialControls", openRegionEditor: "openRegionEditor", selectStackOption: "selectStackOption", zScrub: "zScrub", zSlide: "zSlide", zIndexInput: "zIndexInput", reloadAndPlot: "reloadAndPlot", downloadImage: "downloadImage", autoscaleImage: "autoscaleImage", toggleDragMode: "toggleDragMode", zoomIn: "zoomIn", zoomOut: "zoomOut", toggleSurface3dMode: "toggleSurface3dMode", resetSurfaceCamera: "resetSurfaceCamera", toggleAxes: "toggleAxes", toggleWireframe: "toggleWireframe", selectResolution: "selectResolution", deleteRegion: "deleteRegion", undoRegion: "undoRegion", redoRegion: "redoRegion", segmentRegions: "segmentRegions", segmentCellpose: "segmentCellpose", runTool: "runTool", toggleDialogTool: "toggleDialogTool", toolModelChange: "toolModelChange", openToolParams: "openToolParams", samModelChange: "samModelChange", wandSensitivityChange: "wandSensitivityChange", brushSizeChange: "brushSizeChange", vertexEraserRadiusChange: "vertexEraserRadiusChange" }, usesOnChanges: true, ngImport: i0, template: "<p-toolbar>\n <div class=\"p-toolbar-group-start flex-wrap align-items-center gap-1\">\n\n <!-- Host-provided custom toolbar buttons, shown first (e.g. app-specific\n actions the visualization view itself doesn't own). -->\n <ng-content></ng-content>\n\n <!-- Contributed dialog tools (TOOLBAR_TOOLS, kind: 'dialog'): app-level actions\n like the host's own buttons above, so they sit right beside them. Image view\n only, the view their dialog draws over. Highlighted while the dialog is open. -->\n <ng-container *ngIf=\"isImageView\">\n <p-button *ngFor=\"let tool of dialogTools; trackBy: trackToolById\"\n size=\"small\" [text]=\"openDialogToolId !== tool.id\"\n [pTooltip]=\"tool.tooltip\" tooltipPosition=\"bottom\"\n [ariaLabel]=\"tool.label\"\n [attr.aria-pressed]=\"openDialogToolId === tool.id\"\n (onClick)=\"toggleDialogTool.emit(tool.id)\">\n <img *ngIf=\"tool.icon.src\" [src]=\"tool.icon.src\" style=\"width: 1rem\" [alt]=\"tool.label\"\n [class]=\"openDialogToolId === tool.id ? 'toolbar-icon-active' : 'toolbar-icon-dark'\" />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n </ng-container>\n\n <!-- Plot type: available for any image (grayscale or RGB), and for a spatial dataset\n that brings none (its spatial modes are still a choice). -->\n <ng-container *ngIf=\"showPlotTypes\">\n <span class=\"toolbar-separator\"></span>\n <p-dropdown [options]=\"plotTypeOptions\" optionLabel=\"label\" optionValue=\"type\"\n [(ngModel)]=\"selectedPlotType\" appendTo=\"body\"\n pTooltip=\"Select plot type\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '11rem'}\"\n styleClass=\"plotmode-dropdown\" panelStyleClass=\"plotmode-panel\"\n (onChange)=\"selectPlotType.emit($event.value)\">\n <ng-template let-opt pTemplate=\"selectedItem\">\n <span class=\"plotmode-item\" *ngIf=\"opt\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n <ng-template let-opt pTemplate=\"item\">\n <span class=\"plotmode-item\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n </p-dropdown>\n <ng-container *ngIf=\"isIsosurfaceMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Iso</span>\n <p-slider [(ngModel)]=\"isoRange\" [range]=\"true\"\n [min]=\"isoValueMin\" [max]=\"isoValueMax\" [step]=\"isoValueStep\"\n (onChange)=\"isoRangeChange.emit($event.values)\"\n pTooltip=\"Isosurface band (0\u2013255), mapped onto the volume's\n actual intensity range. Surfaces are drawn for voxels whose\n intensity falls in the selected band \u2014 narrow it to isolate\n features, widen it to see the whole structure.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ isoRange[0] }}\u2013{{ isoRange[1] }}</span>\n </ng-container>\n <!-- Intensity tools (Heatmap / Image mode): add another colored line ROI.\n Each added line gets the next bright palette colour and its own inset\n trace; select a line on the image and use the trash tool to remove it\n (and its trace). The lines are Region-based but excluded from the\n Region Editor and exports. -->\n <ng-container *ngIf=\"isIntensityCapable\">\n <span class=\"toolbar-separator\"></span>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Add an intensity line (new colour). Click a line to select it,\n then use the trash tool to remove it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"addProfileLine.emit()\">\n <img src=\"assets/plotting/spline-chart.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n </ng-container>\n <!-- Channels & Histogram: brightness/contrast, gamma, per-channel display,\n colormap/reverse/invert \u2014 opens the floating dialog. -->\n <p-button *ngIf=\"tools.specialTools\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Channels &amp; Histogram (brightness/contrast, colormap)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openChannelHistogram.emit()\">\n <img src=\"assets/plotting/invert.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Spatial-omics controls. Only meaningful in the spatial mode, which is\n itself only offered while a dataset is loaded \u2014 so this button appears\n exactly when there is something for it to control. -->\n <p-button *ngIf=\"tools.specialTools && isSpatialMode\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Spatial omics (colour by column or gene, legend, selection, distributions)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openSpatialControls.emit()\">\n <img src=\"assets/plotting/spatial-omics.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Smoothen toggle: bilinear smoothing vs nearest-neighbour raw pixels (Image\n view only \u2014 it's an OpenSeadragon drawer setting). Highlighted when smoothing on. -->\n <p-button *ngIf=\"tools.specialTools && isImageView\" size=\"small\"\n [text]=\"!imageSmoothingEnabled\"\n pTooltip=\"Smoothen (turn on image smoothing)\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleImageSmoothing.emit()\">\n <img src=\"assets/plotting/smooth.svg\" style=\"width: 1rem\"\n [class]=\"imageSmoothingEnabled ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"/>\n </p-button>\n\n <!-- Stack controls -->\n <ng-container *ngIf=\"imageInfo?.isStack\">\n <span class=\"toolbar-separator\"></span>\n <!-- Image view: scrub z-slices live (the renderer swaps slices in place). -->\n <ng-container *ngIf=\"showsLiveSliceScrubber\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <p-slider [(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onChange)=\"zScrub.emit($event.value)\" (onSlideEnd)=\"zSlide.emit($event.value)\"\n pTooltip=\"Slice through the stack (\u2190 / \u2192 keys)\" tooltipPosition=\"bottom\"\n [style]=\"{'width':'16rem'}\" styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text\">{{zIndex}} of {{maxIndex}}</span>\n </ng-container>\n <!-- Heatmap view: single-image (high-def zoom) vs stack (frame slider). Plotly-only \u2014\n hidden for napari, which has no single/stack toggle or per-frame fetch, and for the\n spatial-omics modes: a 3D omics dataset publishes its reference volume as a z-stack\n image, and these modes draw observations, not one of its slices. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode && !isSpatialMode\">\n <p-button icon=\"pi pi-image\" size=\"small\"\n [text]=\"imageInfo?.showStack\"\n pTooltip=\"Single image mode: enables high-def zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[0])\">\n </p-button>\n <p-button icon=\"pi pi-images\" size=\"small\"\n [text]=\"!imageInfo?.showStack\"\n pTooltip=\"Stack mode, enables slicing through image frames\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[1])\">\n </p-button>\n <ng-container *ngIf=\"!imageInfo?.showStack\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <div class=\"p-inputgroup\" style=\"width: auto\">\n <p-inputNumber styleClass=\"w-3rem toolbar-input\"\n\t\t\t\t\tpTooltip=\"View image in stack (&#x23CE;)\"\n\t\t\t\t\t[(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onInput)=\"zIndexInput.emit(zIndex)\" (keydown.enter)=\"reloadAndPlot.emit()\"/>\n <p-button icon=\"pi pi-refresh\" size=\"small\" [text]=\"true\" (onClick)=\"reloadAndPlot.emit()\"\n pTooltip=\"Plot this slice\" tooltipPosition=\"bottom\">\n </p-button>\n </div>\n <span class=\"flex align-items-center mx-1 toolbar-text\">of {{maxIndex}}</span>\n </ng-container>\n </ng-container>\n </ng-container>\n\n <!-- Image actions (special tools) -->\n <ng-container *ngIf=\"tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-download\" size=\"small\" [text]=\"true\"\n pTooltip=\"Download image as PNG\" tooltipPosition=\"bottom\"\n (onClick)=\"downloadImage.emit()\">\n </p-button>\n <p-button *ngIf=\"isHeatmap\" icon=\"pi pi-window-maximize\" size=\"small\" [text]=\"true\"\n pTooltip=\"Fit to view\" tooltipPosition=\"bottom\"\n (onClick)=\"autoscaleImage.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Zoom tools. Plotly rubber-band drag-zoom: on OpenSeadragon the box-zoom\n covers this and a bare drag pans natively, so it's hidden in image view. -->\n <ng-container *ngIf=\"isHeatmap && tools.zoomTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button *ngIf=\"!isImageView\" icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeDragMode !== 'zoom'\"\n pTooltip=\"Zoom drag\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoom')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'zoomToBox'\"\n pTooltip=\"Zoom box (<kbd>b</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoomToBox')\">\n <img src=\"assets/plotting/zoom-box.svg\"\n [class]=\"activeDragMode === 'zoomToBox' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeDragMode !== 'pan'\"\n pTooltip=\"Pan the image (<kbd>p</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-search-plus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom in (<kbd>+</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomIn.emit()\">\n </p-button>\n <p-button icon=\"pi pi-search-minus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom out (<kbd>-</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomOut.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Surface 3D controls -->\n <ng-container *ngIf=\"!isHeatmap\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'zoom'\"\n pTooltip=\"Zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('zoom')\">\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'pan'\"\n pTooltip=\"Pan\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-globe\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'orbit'\"\n pTooltip=\"Orbital rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('orbit')\">\n </p-button>\n <p-button icon=\"pi pi-sync\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'turntable'\"\n pTooltip=\"Turntable rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('turntable')\">\n </p-button>\n <p-button icon=\"pi pi-home\" size=\"small\" [text]=\"true\"\n pTooltip=\"Reset camera to default\" tooltipPosition=\"bottom\"\n (onClick)=\"resetSurfaceCamera.emit()\">\n </p-button>\n <!-- 3D coordinate-axes / scale gizmo toggle (napari-js WebGPU volume/isosurface only).\n `p-button-icon-only` gives the same square sizing as the neighbouring pi-icon buttons\n (the projected <img> doesn't trigger PrimeNG's automatic icon-only sizing). -->\n <p-button size=\"small\" *ngIf=\"isNapariMode\" styleClass=\"p-button-icon-only\"\n [text]=\"!axesVisible\"\n pTooltip=\"Toggle 3D axes / scale\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleAxes.emit()\">\n <img src=\"assets/plotting/3d-coordinates.svg\"\n [class]=\"axesVisible ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <!-- Surface wireframe toggle (napari-js WebGPU surface only). -->\n <p-button icon=\"pi pi-share-alt\" size=\"small\" *ngIf=\"isNapariSurfaceMode\"\n [text]=\"!wireframeActive\"\n pTooltip=\"Toggle wireframe\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleWireframe.emit()\">\n </p-button>\n <!-- Resolution (decimate factor) for the napari 3D types \u2014 reloads at coarser/finer sampling. -->\n <ng-container *ngIf=\"isNapari3dMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Res</span>\n <p-dropdown [options]=\"decimateOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"resolutionScale\" appendTo=\"body\"\n pTooltip=\"Resolution (decimate factor)\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '6rem'}\"\n (onChange)=\"selectResolution.emit($event.value)\">\n </p-dropdown>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"(isHeatmap || is3dRegions) && tools.regionTools\">\n <span class=\"toolbar-separator\"></span>\n <!-- Region Editor: opens the editor as a floating dialog (formerly the\n right-panel Regions tab). Sits with the region drawing tools. -->\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Open the Region Editor\" tooltipPosition=\"bottom\"\n (onClick)=\"openRegionEditor.emit()\">\n <img src=\"assets/plotting/region-editor.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <!-- Region drawing tools -->\n <p-button icon=\"pi pi-arrow-up-right\" size=\"small\"\n [text]=\"activeDragMode !== 'select'\"\n pTooltip=\"Selection mode (<kbd>s</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('select')\">\n </p-button>\n <p-button icon=\"pi pi-stop\" size=\"small\"\n [text]=\"activeDragMode !== 'drawrect'\"\n pTooltip=\"Draw a rectangular region (<kbd>r</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawrect')\">\n </p-button>\n <p-button *ngIf=\"!is3dRegions\" size=\"small\"\n [text]=\"activeDragMode !== 'drawopenpath'\"\n pTooltip=\"Draw a polyline (<kbd>l</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawopenpath')\">\n <img src=\"assets/plotting/polyline.svg\"\n [class]=\"activeDragMode === 'drawopenpath' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-pencil\" size=\"small\"\n [text]=\"activeDragMode !== 'drawclosedpath'\"\n pTooltip=\"Draw a freeform region (<kbd>f</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawclosedpath')\">\n </p-button>\n <!-- Brush: paint a region by dragging a disc of the given size; the Size\n slider appears while it's active. Hold Shift to erase (subtract). -->\n <!-- Brush and polyline are absent from a 3D cloud: the brush paints in IMAGE\n pixels and there is no raster to sample, and an open polyline is a\n profile tool, not a closed region a selection can test against. -->\n <p-button *ngIf=\"!is3dRegions\" size=\"small\"\n [text]=\"activeDragMode !== 'brush'\"\n pTooltip=\"Brush: click and drag to paint a region.\n <br>Hold <kbd>Shift</kbd> while painting to erase from a region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('brush')\">\n <img src=\"assets/plotting/paintbrush.svg\"\n [class]=\"activeDragMode === 'brush' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'brush'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Size</span>\n <p-slider [(ngModel)]=\"brushSize\"\n [min]=\"brushSizeMin\" [max]=\"brushSizeMax\" [step]=\"brushSizeStep\"\n (onChange)=\"brushSizeChange.emit($event.value)\"\n pTooltip=\"Brush diameter in image pixels.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ brushSize }}</span>\n </ng-container>\n\n <!-- Vertex editing \u2014 backends with a vertex-editing overlay (OSD + napari-js image). -->\n <ng-container *ngIf=\"supportsRegionVertexTools\">\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawpolygon'\"\n pTooltip=\"Polygon: click to place each vertex; click the first point to close\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawpolygon')\">\n <img src=\"assets/plotting/polygon-vertices.svg\"\n [class]=\"activeDragMode === 'drawpolygon' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'addpoint'\"\n pTooltip=\"Add a vertex: click an edge of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('addpoint')\">\n <img src=\"assets/plotting/vertex-add.svg\"\n [class]=\"activeDragMode === 'addpoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'deletepoint'\"\n pTooltip=\"Delete a vertex: click a vertex of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('deletepoint')\">\n <img src=\"assets/plotting/vertex-delete.svg\"\n [class]=\"activeDragMode === 'deletepoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'wand'\"\n pTooltip=\"Wand: click and drag to grow a region by similar pixel values (<kbd>w</kbd>).\n <br>Hold <kbd>Shift</kbd> while clicking or dragging on a region to erase pixels from it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('wand')\">\n <img src=\"assets/plotting/wand.svg\"\n [class]=\"activeDragMode === 'wand' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'wand'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Sens.</span>\n <p-slider [(ngModel)]=\"wandSensitivity\"\n [min]=\"wandSensitivityMin\" [max]=\"wandSensitivityMax\" [step]=\"wandSensitivityStep\"\n (onChange)=\"wandSensitivityChange.emit($event.value)\"\n pTooltip=\"Higher = stricter (smaller selection); lower = looser.\n Takes effect from the next click/drag tick.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ wandSensitivity.toFixed(1) }}</span>\n </ng-container>\n <p-button icon=\"pi pi-eraser\" size=\"small\"\n [text]=\"activeDragMode !== 'eraseVertex'\"\n pTooltip=\"Vertex eraser (<kbd>e</kbd>): click or drag to remove polygon vertices inside\n the cursor circle from any region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('eraseVertex')\">\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'eraseVertex'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Radius</span>\n <p-slider [(ngModel)]=\"vertexEraserRadius\"\n [min]=\"vertexEraserRadiusMin\" [max]=\"vertexEraserRadiusMax\" [step]=\"vertexEraserRadiusStep\"\n (onChange)=\"vertexEraserRadiusChange.emit($event.value)\"\n pTooltip=\"Eraser radius in image pixels.\n Vertices within this distance of the cursor are removed.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ vertexEraserRadius }}</span>\n </ng-container>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canUndo\"\n pTooltip=\"Undo the last region action (<kbd>Ctrl</kbd>+<kbd>Z</kbd>).\n Up to 10 actions can be undone.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"undoRegion.emit()\">\n <img src=\"assets/plotting/undo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canRedo\"\n pTooltip=\"Redo the last undone region action\n (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd>).\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"redoRegion.emit()\">\n <img src=\"assets/plotting/redo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-trash\" size=\"small\" [text]=\"true\"\n pTooltip=\"Delete selected region (<kbd>Del</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"deleteRegion.emit()\">\n </p-button>\n <!-- SAM (jit-ui#90): box-prompt segment, interactive point prompts, and a\n model picker. -->\n <span class=\"toolbar-separator\"></span>\n <!-- SAM controls grouped: box-prompt Segment, then the point-prompt tool,\n then the model-picker dropdown (the chevron) which governs BOTH tools.\n Falls back to Segment + point with no model picker when none are set. -->\n <span *ngIf=\"samModels.length; else segmentPlain\" class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/sam-box-prompt.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the SAM model (used by both segment tools)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"samModelMenu.toggle($event)\">\n </p-button>\n </span>\n <ng-template #segmentPlain>\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/sam-box-prompt.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n </span>\n </ng-template>\n <!-- Point-prompt tool \u2014 shared between the models / no-models layouts so it\n always sits just left of the model dropdown. -->\n <ng-template #samPointBtn>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'samPoint'\"\n pTooltip=\"SAM point prompts: click a fiber to segment it as a new region;\n Shift/Alt-click to refine (exclude) the current one.<br>Esc undoes it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('samPoint')\">\n <img src=\"assets/plotting/sam-point-prompt.svg\"\n [class]=\"activeDragMode === 'samPoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-template>\n <p-menu #samModelMenu styleClass=\"model-menu\" [model]=\"samMenuItems\" [popup]=\"true\" appendTo=\"body\">\n <ng-template pTemplate=\"item\" let-item>\n <ng-container [ngTemplateOutlet]=\"modelMenuItem\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n </ng-template>\n </p-menu>\n\n <!-- Shared item body for all three model pickers (SAM / YOLO / retinal):\n the standard check-or-blank icon and label, plus an info icon carrying\n the model's description as a hover tooltip. This replaced a single\n \"About the SAM models\" overlay panel \u2014 per-model text sits next to the\n model it describes, and YOLO/retinal get the same affordance.\n\n p-menu puts its click handler on the wrapping `.p-menuitem-content`,\n outside this template, so `command` still fires and the whole row stays\n clickable \u2014 which is also why the info icon has to stop propagation, or\n reading about a model would switch to it. -->\n <ng-template #modelMenuItem let-item>\n <a class=\"p-menuitem-link model-menu-item\">\n <span class=\"p-menuitem-icon\" [ngClass]=\"item.icon\"></span>\n <span class=\"p-menuitem-text\">{{ item.label }}</span>\n <!-- `bottom` like every other tooltip in this toolbar, and for room:\n these menus open under buttons that can sit near the right edge,\n where a `right` tooltip has a few hundred px and squeezes its text\n into a narrow column instead of using its max-width. -->\n <!-- A focusable <button>, not a bare <i>. As an <i> the description\n was reachable only by hovering a mouse: not tabbable, no\n accessible name, nothing for a screen reader to announce \u2014 so\n keyboard and AT users could pick a checkpoint but never read what\n it was. PrimeNG's tooltip shows on focus as well as hover, so\n making it a button restores it for free.\n\n `aria-describedby` is deliberately not used: the tooltip is\n rendered into `body` on demand and has no stable id to point at.\n The description is carried on `aria-label` instead, which is\n announced whether or not the tooltip is showing. It is stripped of\n markup first, since the copy contains `<b>`/`<br>` for the visual\n tooltip and those would otherwise be read out literally. -->\n <button *ngIf=\"item.tooltip\" type=\"button\"\n class=\"pi pi-info-circle model-info-icon\"\n [attr.aria-label]=\"'About ' + item.label + ': ' + plainText(item.tooltip)\"\n [pTooltip]=\"item.tooltip\" [escape]=\"false\" tooltipPosition=\"bottom\"\n appendTo=\"body\" tooltipStyleClass=\"model-info-tooltip\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.space)=\"$event.stopPropagation()\"></button>\n </a>\n </ng-template>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Auto-segment cells inside each drawn rectangle (cellpose-SAM)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentCellpose.emit()\">\n <img src=\"assets/plotting/cells.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Contributed no-prompt tools (TOOLBAR_TOOLS). Unlike the SAM and\n cellpose tools these take no prompt \u2014 they sweep the whole current\n view rather than being pointed at something \u2014 so there is nothing to\n draw first. Each renders the same split button: run, pick checkpoint,\n open parameters.\n\n Nothing here names a specific tool. The library ships none; a host\n registers them, which is what lets this package be published without\n the closed model dependencies. -->\n <ng-container *ngFor=\"let tool of contributedTools; trackBy: trackToolById\">\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.runTooltip\"\n tooltipPosition=\"bottom\"\n (onClick)=\"runTool.emit(tool.id)\">\n <!-- `toolbar-icon-dark` is hardcoded rather than bound, unlike the\n toggle tools: these fire a run and have no active state. -->\n <img *ngIf=\"tool.icon.src\" [src]=\"tool.icon.src\" class=\"toolbar-icon-dark\"\n style=\"width: 1rem\" [alt]=\"tool.label\" />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.modelTooltip || 'Pick the model'\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toolMenu.toggle($event)\">\n </p-button>\n <p-button *ngIf=\"tool.params.length\"\n icon=\"pi pi-sliders-h\" size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.paramsTooltip || 'Parameters'\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openToolParams.emit(tool.id)\">\n </p-button>\n </span>\n <p-menu #toolMenu styleClass=\"model-menu\" [model]=\"toolMenuItems[tool.id] || []\"\n [popup]=\"true\" appendTo=\"body\">\n <ng-template pTemplate=\"item\" let-item>\n <ng-container [ngTemplateOutlet]=\"modelMenuItem\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n </ng-template>\n </p-menu>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"tools.help\">\n <span class=\"toolbar-separator\"></span>\n\n <!-- Help -->\n <p-button icon=\"pi pi-question-circle\" size=\"small\" [text]=\"true\"\n pTooltip=\"Help\" tooltipPosition=\"bottom\"\n (onClick)=\"showHelp()\">\n </p-button>\n </ng-container>\n\n </div>\n</p-toolbar>\n<p-dialog header=\"Plotting help\" [(visible)]=\"displayHelpDialog\" [modal]=\"true\" [style]=\"{width: '50vw'}\"\n [maximizable]=\"true\" [draggable]=\"false\" [resizable]=\"false\">\n <p>Click a file in the left-hand tree to display it. By default it opens in the\n <b>Image</b> view \u2014 a natively tiled, deeply zoomable raster (OpenSeadragon) with a\n navigator minimap and a physical-units scale bar. <b>Click the image to zoom</b> toward\n the clicked point; scroll to zoom and drag to pan. When a stack is open, a slice slider\n (Image view) or image-mode toggle (other views) appears. Regions can also be edited in the\n <b>Region Editor</b>, opened with the\n <img src=\"assets/plotting/region-editor.svg\" width=\"14\" height=\"14\" alt=\"Region Editor\"/>\n toolbar button as a floating dialog. Drag the <i class=\"pi pi-bars\"></i> handle to detach\n this toolbar into a floating window.\n </p>\n\n <h3>Plot-type dropdown</h3>\n <p>\n The <b>plot-type dropdown</b> switches how the file is visualized:\n <b>Image</b>, <b>Heatmap</b>, <b>Contour</b>, <b>Surface</b>, <b>Volume</b>,\n <b>Isosurface</b>, and \u2014 when a spatial-omics dataset is open \u2014\n <b>Spatial omics</b> and <b>Spatial omics 3D</b>.\n </p>\n <p>\n <b>Only the modes the data can actually support are offered</b>, so the list changes\n with what is loaded rather than showing entries that would come up blank:\n </p>\n <ul>\n <li><b>Contour</b>, <b>Volume</b> and <b>Isosurface</b> need a grayscale image; the\n two 3D ones also need a z-stack.</li>\n <li><b>Spatial omics</b> needs a published dataset, and <b>Spatial omics 3D</b> needs\n one whose coordinates carry a z.</li>\n <li>The pixel modes (<b>Image</b>, <b>Heatmap</b>, <b>Surface</b>) are withheld for a\n dataset that brings no tissue image \u2014 seqFISH records coordinates and no section, so\n there is nothing for them to draw.</li>\n </ul>\n <ul>\n <li>\"<b>Isosurface band</b>\" slider (Isosurface): sets the intensity band the surfaces are\n drawn for, mapped onto the volume's real intensity range.</li>\n <li>\"<b>3D camera</b>\" (Surface / Scatter 3D / Isosurface): zoom <i class=\"pi pi-search\"></i>,\n pan <i class=\"pi pi-arrows-alt\"></i>, orbit <i class=\"pi pi-globe\"></i>,\n turntable <i class=\"pi pi-sync\"></i>, and reset camera <i class=\"pi pi-home\"></i>;\n scroll to zoom the 3D scene.</li>\n </ul>\n\n <h3>Image manipulation tools</h3>\n <ul>\n <li>\"<b>Region Editor</b>\" <img src=\"assets/plotting/region-editor.svg\" width=\"15\" height=\"15\">:\n opens the Region Editor as a floating, draggable dialog \u2014 a per-image table of every region\n (class, colour, area) with bulk select/label/colour/delete and GeoJSON import/export. It stays\n in sync with the image, so you can keep it open while drawing.</li>\n <li>\"<b>Intensity profile lines</b>\" <img src=\"assets/plotting/spline-chart.svg\" width=\"15\" height=\"15\">\n (<i>work in progress \u2014 not yet stable</i>): adds a coloured line ROI; a floating inset\n chart plots intensity along each line, updates live as you drag it, and re-samples at the\n current zoom resolution. Each added line gets its own colour; select a line and use the\n trash tool <i class=\"pi pi-trash\"></i> to remove it (and its trace).</li>\n <li>\"<b>Channels &amp; Histogram</b>\" <img src=\"assets/plotting/invert.svg\" width=\"15\" height=\"15\">:\n opens a floating dialog for brightness/contrast, gamma, and per-channel display, plus the\n <b>colormap / LUT</b> controls (grayscale only) \u2014 a <b>reverse</b> toggle and a colormap\n dropdown (default: inverted greys).</li>\n <li>\"<b>Smoothen</b>\" <img src=\"assets/plotting/smooth.svg\" width=\"15\" height=\"15\">\n (Image view only): the image opens showing <b>raw pixels</b> (nearest-neighbour), so\n zooming in renders crisp blocks for pixel inspection. Toggle this on for bilinear\n smoothing; the icon turns white while smoothing is active.</li>\n </ul>\n\n <h3>Download &amp; view tools</h3>\n <ul>\n <li>\"<b>Download</b>\" <i class=\"pi pi-download\"></i>: download the current image as a PNG.</li>\n <li>\"<b>Fit to view</b>\" / autoscale <i class=\"pi pi-window-maximize\"></i>: resets the image\n to its original dimensions. In stack mode it also resets to single-image mode.</li>\n </ul>\n\n <h3>Zoom tools</h3>\n <ul>\n <li>\"<b>Zoom box</b>\" <img src=\"assets/plotting/zoom-box.svg\" width=\"15\" height=\"15\">\n (<kbd>b</kbd>): drag a box to select an area to zoom into.</li>\n <li>\"<b>Pan</b>\" <i class=\"pi pi-arrows-alt\"></i> (<kbd>p</kbd>): drag to pan the image.</li>\n <li>\"<b>Zoom in</b>\" <i class=\"pi pi-search-plus\"></i> (<kbd>+</kbd>): zoom in one step.</li>\n <li>\"<b>Zoom out</b>\" <i class=\"pi pi-search-minus\"></i> (<kbd>-</kbd>): zoom out one step,\n stopping at the original image dimensions.</li>\n </ul>\n\n <h3>Region tools</h3>\n <p>By default no tool is selected (all greyed-out); selecting a tool highlights it. The\n vertex-editing tools (polygon, add/delete vertex, move, B\u00E9zier conversions) are available\n in the <b>Image</b> view only. On a <b>donut</b> (a region with a hole), the inner-ring\n vertices are editable too \u2014 drag, add, and delete them just like the outer ring.</p>\n <ul>\n <li>\"<b>Selection</b>\" <i class=\"pi pi-arrow-up-right\"></i> (<kbd>s</kbd>): a neutral mode\n that turns off whatever drawing tool was active (wand, vertex eraser, rectangle, etc.).\n Use it to step out of editing without picking another tool.</li>\n <li>\"<b>Rectangular region</b>\" <i class=\"pi pi-stop\"></i> (<kbd>r</kbd>): click and drag to\n create a rectangular region.</li>\n <li>\"<b>Polyline</b>\" <img src=\"assets/plotting/polyline.svg\" width=\"14\" height=\"14\">\n (<kbd>l</kbd>): draw an open (non-closed) polyline by clicking and dragging. Polylines can\n be edited and exported to GeoJSON as a <code>LineString</code> feature.</li>\n <li>\"<b>Freeform region</b>\" <i class=\"pi pi-pencil\"></i> (<kbd>f</kbd>): click and drag to\n draw a closed freeform polygon.</li>\n <li>\"<b>Brush</b>\" <img src=\"assets/plotting/paintbrush.svg\" width=\"15\" height=\"15\">: click\n and drag to paint a region with a circular brush \u2014 the painted stroke is the region.\n A <b>Size</b> slider (brush diameter, in image pixels) appears in the toolbar while the\n brush is active. Hold <kbd>Shift</kbd> while painting to <b>erase</b> (subtract) from a\n region instead. Painting over an existing region extends it; brushing across two regions\n merges them. Painting a closed ring (or shift-erasing an interior pocket) leaves the\n enclosed area empty \u2014 a <b>donut</b> (Image view).</li>\n <li>\"<b>Polygon</b>\" <img src=\"assets/plotting/polygon-vertices.svg\" width=\"15\" height=\"15\">:\n click to place each vertex; click the first point to close the polygon.</li>\n <li>\"<b>Add vertex</b>\" <img src=\"assets/plotting/vertex-add.svg\" width=\"15\" height=\"15\">:\n click an edge of the selected polygon to insert a new vertex.</li>\n <li>\"<b>Delete vertex</b>\" <img src=\"assets/plotting/vertex-delete.svg\" width=\"15\" height=\"15\">:\n click a vertex of the selected polygon to remove it.</li>\n <li>\"<b>Move region</b>\" <img src=\"assets/plotting/region-move.svg\" width=\"15\" height=\"15\">:\n drag to move the selected region.</li>\n <li>\"<b>To B\u00E9zier</b>\" <img src=\"assets/plotting/to-bezier.svg\" width=\"15\" height=\"15\"> /\n \"<b>To polygon</b>\" <img src=\"assets/plotting/to-polygon.svg\" width=\"15\" height=\"15\">:\n convert the selected region to a smooth B\u00E9zier curve, or back to a straight-edged polygon.</li>\n <li>\"<b>Wand</b>\" <img src=\"assets/plotting/wand.svg\" width=\"15\" height=\"15\"> (<kbd>w</kbd>):\n click to grow a region from pixels with similar values around the click point. Hold\n <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> for an exact-match flood fill; drag to keep extending the\n selection while moving; hold <kbd>Shift</kbd> while clicking or dragging on an existing\n region to <b>erase</b> pixels from it. Use the <b>Sens.</b> slider that appears to make the\n wand stricter (higher = smaller selection) or looser. Subsequent clicks keep extending the\n same region; clicking inside an existing region adopts it, and growing into another region\n merges them.</li>\n <li>\"<b>Vertex eraser</b>\" <i class=\"pi pi-eraser\"></i> (<kbd>e</kbd>): click or click-and-drag\n to remove polygon vertices that fall inside the cursor circle from any region, including a\n region's inner rings (donut holes). Use the <b>Radius</b> slider to set the eraser size in\n image pixels. Polygons reduced below 3 vertices (or polylines below 2) are deleted entirely;\n a hole reduced below 3 vertices is removed.</li>\n <li>\"<b>Undo</b>\" <img src=\"assets/plotting/undo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd>): reverts the\n last region action (draw, delete, edit, wand/brush stroke, segmentation, \u2026). Up to the last\n <b>10</b> actions can be undone; the button greys out when there's nothing left to undo.</li>\n <li>\"<b>Redo</b>\" <img src=\"assets/plotting/redo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> or <kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Y</kbd>):\n re-applies the most recently undone action. Performing a new region action clears what can be redone.</li>\n <li>\"<b>Delete region</b>\" <i class=\"pi pi-trash\"></i> (<kbd>Del</kbd>): deletes the selected\n region. This can also be done from the Region Editor.</li>\n </ul>\n\n <h3>Region actions (right-click the image)</h3>\n <p>Right-click a region \u2014 or the current multi-selection \u2014 to open the\n \"<b>Selected region(s)</b>\" group at the top of the context menu. These act immediately on\n the selection (they're not drawing modes) and are fully undo/redo-able:</p>\n <ul>\n <li>\"<b>Select all regions</b>\" <i class=\"pi pi-check-square\"></i>: select every region on\n the image (also in the Region Editor toolbar).</li>\n <li>\"<b>Merge / group</b>\": combine 2+ selected regions into one. If the pieces don't touch,\n the result is a single multi-part region (sparse \"bits\").</li>\n <li>\"<b>Ungroup</b>\": split a multi-part region back into one region per piece.</li>\n <li>\"<b>Inverse</b>\": replace the selection with everything <i>outside</i> it \u2014 the image\n rectangle with the selected regions punched out as holes. Works at any image size.</li>\n <li>\"<b>Simplify</b>\": reduce a region's vertex count (Douglas\u2013Peucker) by an altitude\n threshold in pixels \u2014 pick <b>Light</b> (1&nbsp;px), <b>Medium</b> (3&nbsp;px),\n <b>Strong</b> (8&nbsp;px), or <b>Custom\u2026</b> for a slider. Applies to the exterior and any\n holes (and every part of a multi-part region).</li>\n <li>The B\u00E9zier conversions and Delete also live in this group (the right-click equivalents of\n the toolbar / vertex tools above).</li>\n </ul>\n\n <h3>Segmentation &amp; detection tools</h3>\n <p>AI-assisted segmentation and detection that run entirely in the browser. The first run\n of any model downloads it (a progress toast is shown); it's then cached.</p>\n <p>Two shapes of tool: the <b>prompted</b> ones (Segment, Point prompts, Cellpose) act on\n rectangles you draw, and generated regions inherit the color of the rectangle they came\n from<span *ngIf=\"contributedTools.length\">; the <b>no-prompt</b> ones ({{ contributedToolNames }})\n sweep the whole current view, so there is nothing to draw first</span>.</p>\n <p>Each model dropdown <i class=\"pi pi-chevron-down\"></i> lists the available checkpoints with\n a <i class=\"pi pi-info-circle\"></i> beside each one \u2014 hover it for that model's size, speed,\n training domain and accuracy, so you can pick without leaving the toolbar.</p>\n <ul>\n <li>\"<b>Segment</b>\" <img src=\"assets/plotting/sam-box-prompt.svg\" width=\"15\" height=\"15\">\n (box prompt): draw one or more <b>Rectangular regions</b> around the objects of interest,\n then click Segment. Each rectangle is replaced by the SAM mask of the object inside it.</li>\n <li>\"<b>Point prompts</b>\" <img src=\"assets/plotting/sam-point-prompt.svg\" width=\"15\" height=\"15\">: click directly on an object to\n segment it as a <b>new</b> region. Each click starts a fresh object \u2014 clicking another\n object won't grow the previous one. <kbd>Shift</kbd>/<kbd>Alt</kbd>-click adds an\n <i>exclude</i> point that refines the current object; <kbd>Esc</kbd> undoes it,\n <kbd>Enter</kbd> commits it.</li>\n <li>\"<b>Model picker</b>\" <i class=\"pi pi-chevron-down\"></i>: the dropdown attached to the\n Segment button chooses the SAM model \u2014 <b>micro-sam ViT-T</b> (default; small, fast) or\n <b>micro-sam ViT-B</b> (larger, highest accuracy) for light microscopy, or\n <b>patho-sam ViT-B</b> for histopathology (H&amp;E / stained tissue). All are promptable\n Segment Anything models finetuned for biomedical images; they differ in the <i>image\n encoder</i> (the heavy once-per-image step) and the training domain \u2014 box and point\n prompting behave the same. The choice applies to <i>both</i> the box and point tools, and\n each tool loads its own encoder, so the first click after switching re-encodes once.</li>\n <li>\"<b>Cellpose</b>\" <img src=\"assets/plotting/cells.svg\" width=\"15\" height=\"15\">: draw rectangles, then click Cellpose to\n automatically segment every cell inside each rectangle (client-side cellpose-SAM) \u2014 one\n region per detected cell. No clicking on individual cells needed.</li>\n <!-- Contributed tools describe themselves. This library ships none, so an\n open build's help lists only the prompted tools above \u2014 the text no\n longer promises buttons that are not there. -->\n <ng-container *ngFor=\"let tool of contributedTools\">\n <li *ngIf=\"tool.help\" [innerHTML]=\"tool.help.body\"></li>\n </ng-container>\n <ng-container *ngFor=\"let tool of dialogTools\">\n <li *ngIf=\"tool.help\" [innerHTML]=\"tool.help.body\"></li>\n </ng-container>\n </ul>\n\n <h3>Spatial omics</h3>\n <p>\n In <b>Spatial omics</b> mode each observation \u2014 a cell or a Visium spot \u2014 is drawn at\n its own coordinates, over the tissue image where the dataset registers onto one. The\n <img src=\"assets/plotting/spatial-omics.svg\" width=\"14\" height=\"14\" alt=\"Spatial omics\"/>\n panel colours them by a <b>column</b> (a cell type, a cluster) or by a <b>gene</b>, and\n the two are exclusive so what drives the colours is never ambiguous.\n </p>\n <ul>\n <li>\"<b>Select from ROIs</b>\": turns the regions you have drawn into a selection of\n observations. Everything else then follows that selection.</li>\n <li>\"<b>Point size</b>\" / \"<b>Opacity</b>\": how the markers are drawn, not what is\n selected.</li>\n </ul>\n\n <h3>Distribution charts</h3>\n <p>\n The <b>Distribution</b> section of the spatial-omics panel charts whatever the map is\n coloured by, so the two can never disagree. Each tab answers a different question, and\n the <i class=\"pi pi-question-circle\"></i> beside them explains the one on screen \u2014\n including what it must <em>not</em> be read for.\n </p>\n <ul>\n <li>\"<b>Counts</b>\": how many observations per category. A category is a label, not a\n magnitude, so a frequency is the only distribution it has.</li>\n <li>\"<b>Histogram</b>\" / \"<b>Violin</b>\" / \"<b>Box</b>\": how a continuous value is\n distributed \u2014 over everything, or split by a grouping column.</li>\n <li>\"<b>Heatmap</b>\": mean expression of the genes you pick within each group.\n Z-scored per gene by default, so a colour means \"above or below this gene's own\n average\" rather than \"highly expressed\".</li>\n <li>\"<b>Embedding</b>\": a UMAP, PCA or t-SNE over the same cells. <b>Lasso it to\n select on the map</b> \u2014 both views read the same selection. Only PCA labels its axes\n with a percentage, because only its axes explain a measurable share of the variance;\n UMAP and t-SNE coordinates are arbitrary, so distance between distant clusters\n should not be read.</li>\n <li>\"<b>Detach</b>\": moves the charts into their own window so they can sit beside the\n map instead of inside the panel. Switching tabs keeps the window where it is.</li>\n </ul>\n\n <h3>Help</h3>\n <p>\"<b>Help</b>\" <i class=\"pi pi-question-circle\"></i>: opens this dialog.</p>\n\n <h3>Keyboard shortcuts</h3>\n <table class=\"shortcuts-table\">\n <tr><td><kbd>+</kbd> / <kbd>=</kbd></td><td>Zoom in</td></tr>\n <tr><td><kbd>-</kbd></td><td>Zoom out</td></tr>\n <tr><td><kbd>b</kbd></td><td>Toggle zoom box mode</td></tr>\n <tr><td><kbd>p</kbd></td><td>Toggle pan mode</td></tr>\n <tr><td><kbd>f</kbd></td><td>Toggle freeform region drawing</td></tr>\n <tr><td><kbd>l</kbd></td><td>Toggle polyline drawing</td></tr>\n <tr><td><kbd>r</kbd></td><td>Toggle rectangular region drawing</td></tr>\n <tr><td><kbd>w</kbd></td><td>Toggle wand tool</td></tr>\n <tr><td>Wand + <kbd>Shift</kbd></td><td>Erase pixels from the active region</td></tr>\n <tr><td>Wand + <kbd>Ctrl</kbd>/<kbd>Cmd</kbd></td><td>Exact-match flood fill (no smoothing)</td></tr>\n <tr><td><kbd>e</kbd></td><td>Toggle vertex eraser</td></tr>\n <tr><td><kbd>s</kbd></td><td>Selection mode (deactivates drawing tools)</td></tr>\n <tr><td>Point prompt + <kbd>Shift</kbd>/<kbd>Alt</kbd></td><td>Add an exclude point (refine the current object)</td></tr>\n <tr><td><kbd>Enter</kbd></td><td>Commit the current SAM point object</td></tr>\n <tr><td><kbd>Esc</kbd></td><td>Undo the current SAM point object</td></tr>\n <tr><td><kbd>Del</kbd> / <kbd>\u232B</kbd></td><td>Delete selected region</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd></td><td>Undo the last region action (up to 10)</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd></td><td>Redo the last undone region action</td></tr>\n </table>\n\n <ng-template pTemplate=\"footer\">\n <p-button icon=\"pi pi-check\" (click)=\"displayHelpDialog=false\" label=\"Ok\" styleClass=\"p-button-text\">\n </p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:block}:host ::ng-deep .p-toolbar{padding:.25rem .5rem}.toolbar-text{color:var(--primary-color);font-size:.875rem}::ng-deep .toolbar-input .p-inputtext{color:var(--primary-color);border-color:var(--primary-color)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-icon-active{filter:brightness(0) invert(1)}::ng-deep .plotmode-item{display:inline-flex;align-items:center;gap:.5rem;color:#000}::ng-deep .plotmode-item i{font-size:1rem;color:var(--primary-color)}::ng-deep .plotmode-item .plotmode-icon-img{width:1.1rem;height:1.1rem;object-fit:contain;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .p-menu.model-menu{width:auto;min-width:12.5rem}::ng-deep .model-menu-item{display:flex;align-items:center;gap:.5rem}::ng-deep .model-menu-item .p-menuitem-text{flex:1 1 auto;white-space:nowrap}::ng-deep .model-menu-item .model-info-icon{flex:0 0 auto;font-size:.875rem;opacity:.55;cursor:help;appearance:none;background:none;border:0;padding:0;margin:0;color:inherit;line-height:1}::ng-deep .model-menu-item .model-info-icon:hover{opacity:1}::ng-deep .model-menu-item .model-info-icon:focus-visible{opacity:1;outline:2px solid var(--primary-color, #2196f3);outline-offset:2px;border-radius:2px}::ng-deep .model-info-tooltip .p-tooltip-text{min-width:18rem;max-width:24rem;line-height:1.4}::ng-deep .wand-sensitivity-slider{width:8rem;align-self:center;margin:0 .25rem}.wand-sensitivity-value{min-width:2rem;text-align:right}span.colormap{width:50px;height:auto;display:inline-block}img.colormap{width:50px;height:auto}.colormap{background:no-repeat;background-size:100%;vertical-align:middle}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.shortcuts-table{border-collapse:collapse}.shortcuts-table td{padding:.25rem .75rem .25rem 0;vertical-align:middle}.shortcuts-table td:first-child{white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.Toolbar, selector: "p-toolbar", inputs: ["style", "styleClass", "ariaLabelledBy"] }, { kind: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i5.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: "component", type: i6.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "appendTo", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: i8.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i9.Slider, selector: "p-slider", inputs: ["animate", "disabled", "min", "max", "orientation", "step", "range", "style", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "component", type: i10.InputNumber, selector: "p-inputNumber", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "component", type: i11.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "visible", "style", "position"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i12.Ripple, selector: "[pRipple]" }] }); }
259
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: ToolbarComponent, isStandalone: true, selector: "plotting-toolbar", inputs: { imageInfo: "imageInfo", hasSpatialDataset: "hasSpatialDataset", plotTypeOptions: "plotTypeOptions", selectedPlotType: "selectedPlotType", basePlotType: "basePlotType", isoRange: "isoRange", maxIndex: "maxIndex", zIndex: "zIndex", isHeatmap: "isHeatmap", is3dRegions: "is3dRegions", activeDragMode: "activeDragMode", activeSurface3dMode: "activeSurface3dMode", axesVisible: "axesVisible", wireframeActive: "wireframeActive", resolutionScale: "resolutionScale", wandSensitivity: "wandSensitivity", brushSize: "brushSize", vertexEraserRadius: "vertexEraserRadius", canUndo: "canUndo", canRedo: "canRedo", stackOptions: "stackOptions", tools: "tools", imageSmoothingEnabled: "imageSmoothingEnabled", samModels: "samModels", samModelId: "samModelId", contributedTools: "contributedTools", dialogTools: "dialogTools", openDialogToolId: "openDialogToolId", toolModelIds: "toolModelIds" }, outputs: { selectPlotType: "selectPlotType", addProfileLine: "addProfileLine", toggleImageSmoothing: "toggleImageSmoothing", isoRangeChange: "isoRangeChange", openChannelHistogram: "openChannelHistogram", openSpatialControls: "openSpatialControls", openRegionEditor: "openRegionEditor", selectStackOption: "selectStackOption", zScrub: "zScrub", zSlide: "zSlide", zIndexInput: "zIndexInput", reloadAndPlot: "reloadAndPlot", downloadImage: "downloadImage", autoscaleImage: "autoscaleImage", toggleDragMode: "toggleDragMode", zoomIn: "zoomIn", zoomOut: "zoomOut", toggleSurface3dMode: "toggleSurface3dMode", resetSurfaceCamera: "resetSurfaceCamera", toggleAxes: "toggleAxes", toggleWireframe: "toggleWireframe", selectResolution: "selectResolution", deleteRegion: "deleteRegion", undoRegion: "undoRegion", redoRegion: "redoRegion", segmentRegions: "segmentRegions", segmentCellpose: "segmentCellpose", runTool: "runTool", toggleDialogTool: "toggleDialogTool", toolModelChange: "toolModelChange", openToolParams: "openToolParams", samModelChange: "samModelChange", wandSensitivityChange: "wandSensitivityChange", brushSizeChange: "brushSizeChange", vertexEraserRadiusChange: "vertexEraserRadiusChange" }, ngImport: i0, template: "<p-toolbar>\n <div class=\"p-toolbar-group-start flex-wrap align-items-center gap-1\">\n <!-- Host-provided custom toolbar buttons, shown first (e.g. app-specific\n actions the visualization view itself doesn't own). -->\n <ng-content></ng-content>\n\n <!-- Contributed dialog tools (TOOLBAR_TOOLS, kind: 'dialog'): app-level actions\n like the host's own buttons above, so they sit right beside them. Image view\n only, the view their dialog draws over. Highlighted while the dialog is open. -->\n <ng-container *ngIf=\"isImageView\">\n <p-button\n *ngFor=\"let tool of dialogTools; trackBy: trackToolById\"\n size=\"small\"\n [text]=\"openDialogToolId !== tool.id\"\n [pTooltip]=\"tool.tooltip\"\n tooltipPosition=\"bottom\"\n [ariaLabel]=\"tool.label\"\n [attr.aria-pressed]=\"openDialogToolId === tool.id\"\n (onClick)=\"toggleDialogTool.emit(tool.id)\"\n >\n <img\n *ngIf=\"tool.icon.src\"\n [src]=\"tool.icon.src\"\n style=\"width: 1rem\"\n [alt]=\"tool.label\"\n [class]=\"openDialogToolId === tool.id ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n </ng-container>\n\n <!-- Plot type: available for any image (grayscale or RGB), and for a spatial dataset\n that brings none (its spatial modes are still a choice). -->\n <toolbar-plot-type-selector\n *ngIf=\"showPlotTypes\"\n [options]=\"plotTypeOptions\"\n [selected]=\"selectedPlotType\"\n [isosurface]=\"isIsosurfaceMode\"\n [isoRange]=\"isoRange\"\n [intensityLines]=\"isIntensityCapable\"\n (selectPlotType)=\"selectPlotType.emit($event)\"\n (isoRangeChange)=\"isoRangeChange.emit($event)\"\n (addProfileLine)=\"addProfileLine.emit()\"\n >\n </toolbar-plot-type-selector>\n <!-- Channels & Histogram: brightness/contrast, gamma, per-channel display,\n colormap/reverse/invert \u2014 opens the floating dialog. -->\n <p-button\n *ngIf=\"tools.specialTools\"\n pRipple\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Channels &amp; Histogram\"\n pTooltip=\"Channels &amp; Histogram (brightness/contrast, colormap)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openChannelHistogram.emit()\"\n >\n <img src=\"assets/plotting/invert.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\" />\n </p-button>\n\n <!-- Spatial-omics controls. Only meaningful in the spatial mode, which is\n itself only offered while a dataset is loaded \u2014 so this button appears\n exactly when there is something for it to control. -->\n <p-button\n *ngIf=\"tools.specialTools && isSpatialMode\"\n pRipple\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Spatial omics controls\"\n pTooltip=\"Spatial omics (colour by column or gene, legend, selection, distributions)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openSpatialControls.emit()\"\n >\n <img src=\"assets/plotting/spatial-omics.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\" />\n </p-button>\n\n <!-- Smoothen toggle: bilinear smoothing vs nearest-neighbour raw pixels (Image\n view only \u2014 it's an OpenSeadragon drawer setting). Highlighted when smoothing on. -->\n <p-button\n *ngIf=\"tools.specialTools && isImageView\"\n size=\"small\"\n [text]=\"!imageSmoothingEnabled\"\n ariaLabel=\"Smoothen\"\n pTooltip=\"Smoothen (turn on image smoothing)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleImageSmoothing.emit()\"\n >\n <img\n src=\"assets/plotting/smooth.svg\"\n style=\"width: 1rem\"\n [class]=\"imageSmoothingEnabled ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n />\n </p-button>\n\n <!-- Stack controls -->\n <toolbar-stack-controls\n *ngIf=\"imageInfo?.isStack\"\n [liveScrubber]=\"showsLiveSliceScrubber\"\n [stackToggle]=\"!showsLiveSliceScrubber && !isNapariMode && !isSpatialMode\"\n [showStack]=\"!!imageInfo?.showStack\"\n [stackOptions]=\"stackOptions\"\n [zIndex]=\"zIndex\"\n [maxIndex]=\"maxIndex\"\n (zScrub)=\"zScrub.emit($event)\"\n (zSlide)=\"zSlide.emit($event)\"\n (selectStackOption)=\"selectStackOption.emit($event)\"\n (zIndexInput)=\"zIndexInput.emit($event)\"\n (reloadAndPlot)=\"reloadAndPlot.emit()\"\n >\n </toolbar-stack-controls>\n\n <!-- Image actions (special tools) -->\n <ng-container *ngIf=\"tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button\n icon=\"pi pi-download\"\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Download image as PNG\"\n pTooltip=\"Download image as PNG\"\n tooltipPosition=\"bottom\"\n (onClick)=\"downloadImage.emit()\"\n >\n </p-button>\n <p-button\n *ngIf=\"isHeatmap\"\n icon=\"pi pi-window-maximize\"\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Fit to view\"\n pTooltip=\"Fit to view\"\n tooltipPosition=\"bottom\"\n (onClick)=\"autoscaleImage.emit()\"\n >\n </p-button>\n </ng-container>\n\n <toolbar-view-controls\n [isHeatmap]=\"isHeatmap\"\n [zoomTools]=\"tools.zoomTools\"\n [isImageView]=\"isImageView\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [isNapariMode]=\"isNapariMode\"\n [isNapariSurfaceMode]=\"isNapariSurfaceMode\"\n [isNapari3dMode]=\"isNapari3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n (toggleDragMode)=\"toggleDragMode.emit($event)\"\n (zoomIn)=\"zoomIn.emit()\"\n (zoomOut)=\"zoomOut.emit()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode.emit($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera.emit()\"\n (toggleAxes)=\"toggleAxes.emit()\"\n (toggleWireframe)=\"toggleWireframe.emit()\"\n (selectResolution)=\"selectResolution.emit($event)\"\n >\n </toolbar-view-controls>\n\n <ng-container *ngIf=\"(isHeatmap || is3dRegions) && tools.regionTools\">\n <toolbar-region-tools\n [activeDragMode]=\"activeDragMode\"\n [is3dRegions]=\"is3dRegions\"\n [vertexTools]=\"supportsRegionVertexTools\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"canUndo\"\n [canRedo]=\"canRedo\"\n (openRegionEditor)=\"openRegionEditor.emit()\"\n (toggleDragMode)=\"toggleDragMode.emit($event)\"\n (undoRegion)=\"undoRegion.emit()\"\n (redoRegion)=\"redoRegion.emit()\"\n (deleteRegion)=\"deleteRegion.emit()\"\n (wandSensitivityChange)=\"wandSensitivityChange.emit($event)\"\n (brushSizeChange)=\"brushSizeChange.emit($event)\"\n (vertexEraserRadiusChange)=\"vertexEraserRadiusChange.emit($event)\"\n >\n </toolbar-region-tools>\n <toolbar-segmentation-tools\n [activeDragMode]=\"activeDragMode\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [toolModelIds]=\"toolModelIds\"\n (segmentRegions)=\"segmentRegions.emit()\"\n (segmentCellpose)=\"segmentCellpose.emit()\"\n (toggleDragMode)=\"toggleDragMode.emit($event)\"\n (samModelChange)=\"samModelChange.emit($event)\"\n (runTool)=\"runTool.emit($event)\"\n (toolModelChange)=\"toolModelChange.emit($event)\"\n (openToolParams)=\"openToolParams.emit($event)\"\n >\n </toolbar-segmentation-tools>\n </ng-container>\n\n <ng-container *ngIf=\"tools.help\">\n <span class=\"toolbar-separator\"></span>\n\n <!-- Help -->\n <p-button\n icon=\"pi pi-question-circle\"\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Help\"\n pTooltip=\"Help\"\n tooltipPosition=\"bottom\"\n (onClick)=\"showHelp()\"\n >\n </p-button>\n </ng-container>\n </div>\n</p-toolbar>\n<toolbar-help-dialog\n [(visible)]=\"displayHelpDialog\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n>\n</toolbar-help-dialog>\n", styles: [".toolbar-text{color:var(--primary-color);font-size:.875rem}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-icon-active{filter:brightness(0) invert(1)}.wand-sensitivity-value{min-width:2rem;text-align:right}:host{display:block}:host ::ng-deep .p-toolbar{padding:.25rem .5rem}::ng-deep .toolbar-input .p-inputtext{color:var(--primary-color);border-color:var(--primary-color)}::ng-deep .p-menu.model-menu{width:auto;min-width:12.5rem}::ng-deep .model-menu-item{display:flex;align-items:center;gap:.5rem}::ng-deep .model-menu-item .p-menuitem-text{flex:1 1 auto;white-space:nowrap}::ng-deep .model-menu-item .model-info-icon{flex:0 0 auto;font-size:.875rem;opacity:.55;cursor:help;appearance:none;background:none;border:0;padding:0;margin:0;color:inherit;line-height:1}::ng-deep .model-menu-item .model-info-icon:hover{opacity:1}::ng-deep .model-menu-item .model-info-icon:focus-visible{opacity:1;outline:2px solid var(--primary-color, #2196f3);outline-offset:2px;border-radius:2px}::ng-deep .model-info-tooltip .p-tooltip-text{min-width:18rem;max-width:24rem;line-height:1.4}::ng-deep .wand-sensitivity-slider{width:8rem;align-self:center;margin:0 .25rem}span.colormap{width:50px;height:auto;display:inline-block}img.colormap{width:50px;height:auto}.colormap{background:no-repeat;background-size:100%;vertical-align:middle}\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: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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: RippleModule }, { kind: "directive", type: i3.Ripple, selector: "[pRipple]" }, { kind: "ngmodule", type: ToolbarModule }, { kind: "component", type: i4.Toolbar, selector: "p-toolbar", inputs: ["style", "styleClass", "ariaLabelledBy"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: ToolbarHelpDialogComponent, selector: "toolbar-help-dialog", inputs: ["visible", "contributedTools", "dialogTools"], outputs: ["visibleChange"] }, { kind: "component", type: PlotTypeSelectorComponent, selector: "toolbar-plot-type-selector", inputs: ["options", "selected", "isosurface", "isoRange", "intensityLines"], outputs: ["selectPlotType", "isoRangeChange", "addProfileLine"] }, { kind: "component", type: StackControlsComponent, selector: "toolbar-stack-controls", inputs: ["liveScrubber", "stackToggle", "showStack", "stackOptions", "zIndex", "maxIndex"], outputs: ["zScrub", "zSlide", "selectStackOption", "zIndexInput", "reloadAndPlot"] }, { kind: "component", type: ViewControlsComponent, selector: "toolbar-view-controls", inputs: ["isHeatmap", "zoomTools", "isImageView", "activeDragMode", "activeSurface3dMode", "isNapariMode", "isNapariSurfaceMode", "isNapari3dMode", "axesVisible", "wireframeActive", "resolutionScale"], outputs: ["toggleDragMode", "zoomIn", "zoomOut", "toggleSurface3dMode", "resetSurfaceCamera", "toggleAxes", "toggleWireframe", "selectResolution"] }, { kind: "component", type: RegionToolsComponent, selector: "toolbar-region-tools", inputs: ["activeDragMode", "is3dRegions", "vertexTools", "wandSensitivity", "brushSize", "vertexEraserRadius", "canUndo", "canRedo"], outputs: ["openRegionEditor", "toggleDragMode", "undoRegion", "redoRegion", "deleteRegion", "wandSensitivityChange", "brushSizeChange", "vertexEraserRadiusChange"] }, { kind: "component", type: SegmentationToolsComponent, selector: "toolbar-segmentation-tools", inputs: ["activeDragMode", "samModels", "samModelId", "contributedTools", "toolModelIds"], outputs: ["segmentRegions", "segmentCellpose", "toggleDragMode", "samModelChange", "runTool", "toolModelChange", "openToolParams"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
321
260
  }
322
261
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: ToolbarComponent, decorators: [{
323
262
  type: Component,
324
- args: [{ selector: 'plotting-toolbar', template: "<p-toolbar>\n <div class=\"p-toolbar-group-start flex-wrap align-items-center gap-1\">\n\n <!-- Host-provided custom toolbar buttons, shown first (e.g. app-specific\n actions the visualization view itself doesn't own). -->\n <ng-content></ng-content>\n\n <!-- Contributed dialog tools (TOOLBAR_TOOLS, kind: 'dialog'): app-level actions\n like the host's own buttons above, so they sit right beside them. Image view\n only, the view their dialog draws over. Highlighted while the dialog is open. -->\n <ng-container *ngIf=\"isImageView\">\n <p-button *ngFor=\"let tool of dialogTools; trackBy: trackToolById\"\n size=\"small\" [text]=\"openDialogToolId !== tool.id\"\n [pTooltip]=\"tool.tooltip\" tooltipPosition=\"bottom\"\n [ariaLabel]=\"tool.label\"\n [attr.aria-pressed]=\"openDialogToolId === tool.id\"\n (onClick)=\"toggleDialogTool.emit(tool.id)\">\n <img *ngIf=\"tool.icon.src\" [src]=\"tool.icon.src\" style=\"width: 1rem\" [alt]=\"tool.label\"\n [class]=\"openDialogToolId === tool.id ? 'toolbar-icon-active' : 'toolbar-icon-dark'\" />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n </ng-container>\n\n <!-- Plot type: available for any image (grayscale or RGB), and for a spatial dataset\n that brings none (its spatial modes are still a choice). -->\n <ng-container *ngIf=\"showPlotTypes\">\n <span class=\"toolbar-separator\"></span>\n <p-dropdown [options]=\"plotTypeOptions\" optionLabel=\"label\" optionValue=\"type\"\n [(ngModel)]=\"selectedPlotType\" appendTo=\"body\"\n pTooltip=\"Select plot type\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '11rem'}\"\n styleClass=\"plotmode-dropdown\" panelStyleClass=\"plotmode-panel\"\n (onChange)=\"selectPlotType.emit($event.value)\">\n <ng-template let-opt pTemplate=\"selectedItem\">\n <span class=\"plotmode-item\" *ngIf=\"opt\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n <ng-template let-opt pTemplate=\"item\">\n <span class=\"plotmode-item\">\n <i *ngIf=\"isPiIcon(opt.icon)\" [class]=\"opt.icon\"></i>\n <img *ngIf=\"opt.icon && !isPiIcon(opt.icon)\" class=\"plotmode-icon-img\" [src]=\"opt.icon\" alt=\"\" />\n <span>{{ opt.label }}</span>\n </span>\n </ng-template>\n </p-dropdown>\n <ng-container *ngIf=\"isIsosurfaceMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Iso</span>\n <p-slider [(ngModel)]=\"isoRange\" [range]=\"true\"\n [min]=\"isoValueMin\" [max]=\"isoValueMax\" [step]=\"isoValueStep\"\n (onChange)=\"isoRangeChange.emit($event.values)\"\n pTooltip=\"Isosurface band (0\u2013255), mapped onto the volume's\n actual intensity range. Surfaces are drawn for voxels whose\n intensity falls in the selected band \u2014 narrow it to isolate\n features, widen it to see the whole structure.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ isoRange[0] }}\u2013{{ isoRange[1] }}</span>\n </ng-container>\n <!-- Intensity tools (Heatmap / Image mode): add another colored line ROI.\n Each added line gets the next bright palette colour and its own inset\n trace; select a line on the image and use the trash tool to remove it\n (and its trace). The lines are Region-based but excluded from the\n Region Editor and exports. -->\n <ng-container *ngIf=\"isIntensityCapable\">\n <span class=\"toolbar-separator\"></span>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Add an intensity line (new colour). Click a line to select it,\n then use the trash tool to remove it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"addProfileLine.emit()\">\n <img src=\"assets/plotting/spline-chart.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n </ng-container>\n <!-- Channels & Histogram: brightness/contrast, gamma, per-channel display,\n colormap/reverse/invert \u2014 opens the floating dialog. -->\n <p-button *ngIf=\"tools.specialTools\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Channels &amp; Histogram (brightness/contrast, colormap)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openChannelHistogram.emit()\">\n <img src=\"assets/plotting/invert.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Spatial-omics controls. Only meaningful in the spatial mode, which is\n itself only offered while a dataset is loaded \u2014 so this button appears\n exactly when there is something for it to control. -->\n <p-button *ngIf=\"tools.specialTools && isSpatialMode\" pRipple size=\"small\" [text]=\"true\"\n pTooltip=\"Spatial omics (colour by column or gene, legend, selection, distributions)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openSpatialControls.emit()\">\n <img src=\"assets/plotting/spatial-omics.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Smoothen toggle: bilinear smoothing vs nearest-neighbour raw pixels (Image\n view only \u2014 it's an OpenSeadragon drawer setting). Highlighted when smoothing on. -->\n <p-button *ngIf=\"tools.specialTools && isImageView\" size=\"small\"\n [text]=\"!imageSmoothingEnabled\"\n pTooltip=\"Smoothen (turn on image smoothing)\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleImageSmoothing.emit()\">\n <img src=\"assets/plotting/smooth.svg\" style=\"width: 1rem\"\n [class]=\"imageSmoothingEnabled ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"/>\n </p-button>\n\n <!-- Stack controls -->\n <ng-container *ngIf=\"imageInfo?.isStack\">\n <span class=\"toolbar-separator\"></span>\n <!-- Image view: scrub z-slices live (the renderer swaps slices in place). -->\n <ng-container *ngIf=\"showsLiveSliceScrubber\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <p-slider [(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onChange)=\"zScrub.emit($event.value)\" (onSlideEnd)=\"zSlide.emit($event.value)\"\n pTooltip=\"Slice through the stack (\u2190 / \u2192 keys)\" tooltipPosition=\"bottom\"\n [style]=\"{'width':'16rem'}\" styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text\">{{zIndex}} of {{maxIndex}}</span>\n </ng-container>\n <!-- Heatmap view: single-image (high-def zoom) vs stack (frame slider). Plotly-only \u2014\n hidden for napari, which has no single/stack toggle or per-frame fetch, and for the\n spatial-omics modes: a 3D omics dataset publishes its reference volume as a z-stack\n image, and these modes draw observations, not one of its slices. -->\n <ng-container *ngIf=\"!showsLiveSliceScrubber && !isNapariMode && !isSpatialMode\">\n <p-button icon=\"pi pi-image\" size=\"small\"\n [text]=\"imageInfo?.showStack\"\n pTooltip=\"Single image mode: enables high-def zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[0])\">\n </p-button>\n <p-button icon=\"pi pi-images\" size=\"small\"\n [text]=\"!imageInfo?.showStack\"\n pTooltip=\"Stack mode, enables slicing through image frames\" tooltipPosition=\"bottom\"\n (onClick)=\"selectStackOption.emit(stackOptions[1])\">\n </p-button>\n <ng-container *ngIf=\"!imageInfo?.showStack\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Slice</span>\n <div class=\"p-inputgroup\" style=\"width: auto\">\n <p-inputNumber styleClass=\"w-3rem toolbar-input\"\n\t\t\t\t\tpTooltip=\"View image in stack (&#x23CE;)\"\n\t\t\t\t\t[(ngModel)]=\"zIndex\" [min]=\"0\" [max]=\"maxIndex\"\n (onInput)=\"zIndexInput.emit(zIndex)\" (keydown.enter)=\"reloadAndPlot.emit()\"/>\n <p-button icon=\"pi pi-refresh\" size=\"small\" [text]=\"true\" (onClick)=\"reloadAndPlot.emit()\"\n pTooltip=\"Plot this slice\" tooltipPosition=\"bottom\">\n </p-button>\n </div>\n <span class=\"flex align-items-center mx-1 toolbar-text\">of {{maxIndex}}</span>\n </ng-container>\n </ng-container>\n </ng-container>\n\n <!-- Image actions (special tools) -->\n <ng-container *ngIf=\"tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-download\" size=\"small\" [text]=\"true\"\n pTooltip=\"Download image as PNG\" tooltipPosition=\"bottom\"\n (onClick)=\"downloadImage.emit()\">\n </p-button>\n <p-button *ngIf=\"isHeatmap\" icon=\"pi pi-window-maximize\" size=\"small\" [text]=\"true\"\n pTooltip=\"Fit to view\" tooltipPosition=\"bottom\"\n (onClick)=\"autoscaleImage.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Zoom tools. Plotly rubber-band drag-zoom: on OpenSeadragon the box-zoom\n covers this and a bare drag pans natively, so it's hidden in image view. -->\n <ng-container *ngIf=\"isHeatmap && tools.zoomTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button *ngIf=\"!isImageView\" icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeDragMode !== 'zoom'\"\n pTooltip=\"Zoom drag\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoom')\">\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'zoomToBox'\"\n pTooltip=\"Zoom box (<kbd>b</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('zoomToBox')\">\n <img src=\"assets/plotting/zoom-box.svg\"\n [class]=\"activeDragMode === 'zoomToBox' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeDragMode !== 'pan'\"\n pTooltip=\"Pan the image (<kbd>p</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-search-plus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom in (<kbd>+</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomIn.emit()\">\n </p-button>\n <p-button icon=\"pi pi-search-minus\" size=\"small\" [text]=\"true\"\n pTooltip=\"Zoom out (<kbd>-</kbd>)\" [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"zoomOut.emit()\">\n </p-button>\n </ng-container>\n\n <!-- Surface 3D controls -->\n <ng-container *ngIf=\"!isHeatmap\">\n <span class=\"toolbar-separator\"></span>\n <p-button icon=\"pi pi-search\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'zoom'\"\n pTooltip=\"Zoom\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('zoom')\">\n </p-button>\n <p-button icon=\"pi pi-arrows-alt\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'pan'\"\n pTooltip=\"Pan\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('pan')\">\n </p-button>\n <p-button icon=\"pi pi-globe\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'orbit'\"\n pTooltip=\"Orbital rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('orbit')\">\n </p-button>\n <p-button icon=\"pi pi-sync\" size=\"small\"\n [text]=\"activeSurface3dMode !== 'turntable'\"\n pTooltip=\"Turntable rotation\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleSurface3dMode.emit('turntable')\">\n </p-button>\n <p-button icon=\"pi pi-home\" size=\"small\" [text]=\"true\"\n pTooltip=\"Reset camera to default\" tooltipPosition=\"bottom\"\n (onClick)=\"resetSurfaceCamera.emit()\">\n </p-button>\n <!-- 3D coordinate-axes / scale gizmo toggle (napari-js WebGPU volume/isosurface only).\n `p-button-icon-only` gives the same square sizing as the neighbouring pi-icon buttons\n (the projected <img> doesn't trigger PrimeNG's automatic icon-only sizing). -->\n <p-button size=\"small\" *ngIf=\"isNapariMode\" styleClass=\"p-button-icon-only\"\n [text]=\"!axesVisible\"\n pTooltip=\"Toggle 3D axes / scale\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleAxes.emit()\">\n <img src=\"assets/plotting/3d-coordinates.svg\"\n [class]=\"axesVisible ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <!-- Surface wireframe toggle (napari-js WebGPU surface only). -->\n <p-button icon=\"pi pi-share-alt\" size=\"small\" *ngIf=\"isNapariSurfaceMode\"\n [text]=\"!wireframeActive\"\n pTooltip=\"Toggle wireframe\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleWireframe.emit()\">\n </p-button>\n <!-- Resolution (decimate factor) for the napari 3D types \u2014 reloads at coarser/finer sampling. -->\n <ng-container *ngIf=\"isNapari3dMode\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Res</span>\n <p-dropdown [options]=\"decimateOptions\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"resolutionScale\" appendTo=\"body\"\n pTooltip=\"Resolution (decimate factor)\" tooltipPosition=\"bottom\"\n [style]=\"{'min-width': '6rem'}\"\n (onChange)=\"selectResolution.emit($event.value)\">\n </p-dropdown>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"(isHeatmap || is3dRegions) && tools.regionTools\">\n <span class=\"toolbar-separator\"></span>\n <!-- Region Editor: opens the editor as a floating dialog (formerly the\n right-panel Regions tab). Sits with the region drawing tools. -->\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Open the Region Editor\" tooltipPosition=\"bottom\"\n (onClick)=\"openRegionEditor.emit()\">\n <img src=\"assets/plotting/region-editor.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <!-- Region drawing tools -->\n <p-button icon=\"pi pi-arrow-up-right\" size=\"small\"\n [text]=\"activeDragMode !== 'select'\"\n pTooltip=\"Selection mode (<kbd>s</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('select')\">\n </p-button>\n <p-button icon=\"pi pi-stop\" size=\"small\"\n [text]=\"activeDragMode !== 'drawrect'\"\n pTooltip=\"Draw a rectangular region (<kbd>r</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawrect')\">\n </p-button>\n <p-button *ngIf=\"!is3dRegions\" size=\"small\"\n [text]=\"activeDragMode !== 'drawopenpath'\"\n pTooltip=\"Draw a polyline (<kbd>l</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawopenpath')\">\n <img src=\"assets/plotting/polyline.svg\"\n [class]=\"activeDragMode === 'drawopenpath' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-pencil\" size=\"small\"\n [text]=\"activeDragMode !== 'drawclosedpath'\"\n pTooltip=\"Draw a freeform region (<kbd>f</kbd>)\" [escape]=\"false\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawclosedpath')\">\n </p-button>\n <!-- Brush: paint a region by dragging a disc of the given size; the Size\n slider appears while it's active. Hold Shift to erase (subtract). -->\n <!-- Brush and polyline are absent from a 3D cloud: the brush paints in IMAGE\n pixels and there is no raster to sample, and an open polyline is a\n profile tool, not a closed region a selection can test against. -->\n <p-button *ngIf=\"!is3dRegions\" size=\"small\"\n [text]=\"activeDragMode !== 'brush'\"\n pTooltip=\"Brush: click and drag to paint a region.\n <br>Hold <kbd>Shift</kbd> while painting to erase from a region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('brush')\">\n <img src=\"assets/plotting/paintbrush.svg\"\n [class]=\"activeDragMode === 'brush' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'brush'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Size</span>\n <p-slider [(ngModel)]=\"brushSize\"\n [min]=\"brushSizeMin\" [max]=\"brushSizeMax\" [step]=\"brushSizeStep\"\n (onChange)=\"brushSizeChange.emit($event.value)\"\n pTooltip=\"Brush diameter in image pixels.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ brushSize }}</span>\n </ng-container>\n\n <!-- Vertex editing \u2014 backends with a vertex-editing overlay (OSD + napari-js image). -->\n <ng-container *ngIf=\"supportsRegionVertexTools\">\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'drawpolygon'\"\n pTooltip=\"Polygon: click to place each vertex; click the first point to close\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('drawpolygon')\">\n <img src=\"assets/plotting/polygon-vertices.svg\"\n [class]=\"activeDragMode === 'drawpolygon' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'addpoint'\"\n pTooltip=\"Add a vertex: click an edge of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('addpoint')\">\n <img src=\"assets/plotting/vertex-add.svg\"\n [class]=\"activeDragMode === 'addpoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'deletepoint'\"\n pTooltip=\"Delete a vertex: click a vertex of the selected polygon\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('deletepoint')\">\n <img src=\"assets/plotting/vertex-delete.svg\"\n [class]=\"activeDragMode === 'deletepoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-container>\n\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'wand'\"\n pTooltip=\"Wand: click and drag to grow a region by similar pixel values (<kbd>w</kbd>).\n <br>Hold <kbd>Shift</kbd> while clicking or dragging on a region to erase pixels from it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('wand')\">\n <img src=\"assets/plotting/wand.svg\"\n [class]=\"activeDragMode === 'wand' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'wand'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Sens.</span>\n <p-slider [(ngModel)]=\"wandSensitivity\"\n [min]=\"wandSensitivityMin\" [max]=\"wandSensitivityMax\" [step]=\"wandSensitivityStep\"\n (onChange)=\"wandSensitivityChange.emit($event.value)\"\n pTooltip=\"Higher = stricter (smaller selection); lower = looser.\n Takes effect from the next click/drag tick.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ wandSensitivity.toFixed(1) }}</span>\n </ng-container>\n <p-button icon=\"pi pi-eraser\" size=\"small\"\n [text]=\"activeDragMode !== 'eraseVertex'\"\n pTooltip=\"Vertex eraser (<kbd>e</kbd>): click or drag to remove polygon vertices inside\n the cursor circle from any region.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('eraseVertex')\">\n </p-button>\n <ng-container *ngIf=\"activeDragMode === 'eraseVertex'\">\n <span class=\"flex align-items-center mx-1 toolbar-text\">Radius</span>\n <p-slider [(ngModel)]=\"vertexEraserRadius\"\n [min]=\"vertexEraserRadiusMin\" [max]=\"vertexEraserRadiusMax\" [step]=\"vertexEraserRadiusStep\"\n (onChange)=\"vertexEraserRadiusChange.emit($event.value)\"\n pTooltip=\"Eraser radius in image pixels.\n Vertices within this distance of the cursor are removed.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n styleClass=\"wand-sensitivity-slider\"></p-slider>\n <span class=\"flex align-items-center mx-1 toolbar-text wand-sensitivity-value\">\n {{ vertexEraserRadius }}</span>\n </ng-container>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canUndo\"\n pTooltip=\"Undo the last region action (<kbd>Ctrl</kbd>+<kbd>Z</kbd>).\n Up to 10 actions can be undone.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"undoRegion.emit()\">\n <img src=\"assets/plotting/undo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button size=\"small\" [text]=\"true\"\n [disabled]=\"!canRedo\"\n pTooltip=\"Redo the last undone region action\n (<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd>).\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"redoRegion.emit()\">\n <img src=\"assets/plotting/redo.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <p-button icon=\"pi pi-trash\" size=\"small\" [text]=\"true\"\n pTooltip=\"Delete selected region (<kbd>Del</kbd>)\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"deleteRegion.emit()\">\n </p-button>\n <!-- SAM (jit-ui#90): box-prompt segment, interactive point prompts, and a\n model picker. -->\n <span class=\"toolbar-separator\"></span>\n <!-- SAM controls grouped: box-prompt Segment, then the point-prompt tool,\n then the model-picker dropdown (the chevron) which governs BOTH tools.\n Falls back to Segment + point with no model picker when none are set. -->\n <span *ngIf=\"samModels.length; else segmentPlain\" class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/sam-box-prompt.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n pTooltip=\"Pick the SAM model (used by both segment tools)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"samModelMenu.toggle($event)\">\n </p-button>\n </span>\n <ng-template #segmentPlain>\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Segment the drawn rectangles with SAM (box prompt)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentRegions.emit()\">\n <img src=\"assets/plotting/sam-box-prompt.svg\"\n class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n <ng-container [ngTemplateOutlet]=\"samPointBtn\"></ng-container>\n </span>\n </ng-template>\n <!-- Point-prompt tool \u2014 shared between the models / no-models layouts so it\n always sits just left of the model dropdown. -->\n <ng-template #samPointBtn>\n <p-button size=\"small\"\n [text]=\"activeDragMode !== 'samPoint'\"\n pTooltip=\"SAM point prompts: click a fiber to segment it as a new region;\n Shift/Alt-click to refine (exclude) the current one.<br>Esc undoes it.\"\n [escape]=\"false\" tooltipPosition=\"bottom\"\n (onClick)=\"toggleDragMode.emit('samPoint')\">\n <img src=\"assets/plotting/sam-point-prompt.svg\"\n [class]=\"activeDragMode === 'samPoint' ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n style=\"width: 1rem\"/>\n </p-button>\n </ng-template>\n <p-menu #samModelMenu styleClass=\"model-menu\" [model]=\"samMenuItems\" [popup]=\"true\" appendTo=\"body\">\n <ng-template pTemplate=\"item\" let-item>\n <ng-container [ngTemplateOutlet]=\"modelMenuItem\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n </ng-template>\n </p-menu>\n\n <!-- Shared item body for all three model pickers (SAM / YOLO / retinal):\n the standard check-or-blank icon and label, plus an info icon carrying\n the model's description as a hover tooltip. This replaced a single\n \"About the SAM models\" overlay panel \u2014 per-model text sits next to the\n model it describes, and YOLO/retinal get the same affordance.\n\n p-menu puts its click handler on the wrapping `.p-menuitem-content`,\n outside this template, so `command` still fires and the whole row stays\n clickable \u2014 which is also why the info icon has to stop propagation, or\n reading about a model would switch to it. -->\n <ng-template #modelMenuItem let-item>\n <a class=\"p-menuitem-link model-menu-item\">\n <span class=\"p-menuitem-icon\" [ngClass]=\"item.icon\"></span>\n <span class=\"p-menuitem-text\">{{ item.label }}</span>\n <!-- `bottom` like every other tooltip in this toolbar, and for room:\n these menus open under buttons that can sit near the right edge,\n where a `right` tooltip has a few hundred px and squeezes its text\n into a narrow column instead of using its max-width. -->\n <!-- A focusable <button>, not a bare <i>. As an <i> the description\n was reachable only by hovering a mouse: not tabbable, no\n accessible name, nothing for a screen reader to announce \u2014 so\n keyboard and AT users could pick a checkpoint but never read what\n it was. PrimeNG's tooltip shows on focus as well as hover, so\n making it a button restores it for free.\n\n `aria-describedby` is deliberately not used: the tooltip is\n rendered into `body` on demand and has no stable id to point at.\n The description is carried on `aria-label` instead, which is\n announced whether or not the tooltip is showing. It is stripped of\n markup first, since the copy contains `<b>`/`<br>` for the visual\n tooltip and those would otherwise be read out literally. -->\n <button *ngIf=\"item.tooltip\" type=\"button\"\n class=\"pi pi-info-circle model-info-icon\"\n [attr.aria-label]=\"'About ' + item.label + ': ' + plainText(item.tooltip)\"\n [pTooltip]=\"item.tooltip\" [escape]=\"false\" tooltipPosition=\"bottom\"\n appendTo=\"body\" tooltipStyleClass=\"model-info-tooltip\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.space)=\"$event.stopPropagation()\"></button>\n </a>\n </ng-template>\n <p-button size=\"small\" [text]=\"true\"\n pTooltip=\"Auto-segment cells inside each drawn rectangle (cellpose-SAM)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"segmentCellpose.emit()\">\n <img src=\"assets/plotting/cells.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\"/>\n </p-button>\n\n <!-- Contributed no-prompt tools (TOOLBAR_TOOLS). Unlike the SAM and\n cellpose tools these take no prompt \u2014 they sweep the whole current\n view rather than being pointed at something \u2014 so there is nothing to\n draw first. Each renders the same split button: run, pick checkpoint,\n open parameters.\n\n Nothing here names a specific tool. The library ships none; a host\n registers them, which is what lets this package be published without\n the closed model dependencies. -->\n <ng-container *ngFor=\"let tool of contributedTools; trackBy: trackToolById\">\n <span class=\"p-buttonset segment-split\">\n <p-button size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.runTooltip\"\n tooltipPosition=\"bottom\"\n (onClick)=\"runTool.emit(tool.id)\">\n <!-- `toolbar-icon-dark` is hardcoded rather than bound, unlike the\n toggle tools: these fire a run and have no active state. -->\n <img *ngIf=\"tool.icon.src\" [src]=\"tool.icon.src\" class=\"toolbar-icon-dark\"\n style=\"width: 1rem\" [alt]=\"tool.label\" />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n <p-button icon=\"pi pi-chevron-down\" size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.modelTooltip || 'Pick the model'\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toolMenu.toggle($event)\">\n </p-button>\n <p-button *ngIf=\"tool.params.length\"\n icon=\"pi pi-sliders-h\" size=\"small\" [text]=\"true\"\n [pTooltip]=\"tool.paramsTooltip || 'Parameters'\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openToolParams.emit(tool.id)\">\n </p-button>\n </span>\n <p-menu #toolMenu styleClass=\"model-menu\" [model]=\"toolMenuItems[tool.id] || []\"\n [popup]=\"true\" appendTo=\"body\">\n <ng-template pTemplate=\"item\" let-item>\n <ng-container [ngTemplateOutlet]=\"modelMenuItem\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n </ng-template>\n </p-menu>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"tools.help\">\n <span class=\"toolbar-separator\"></span>\n\n <!-- Help -->\n <p-button icon=\"pi pi-question-circle\" size=\"small\" [text]=\"true\"\n pTooltip=\"Help\" tooltipPosition=\"bottom\"\n (onClick)=\"showHelp()\">\n </p-button>\n </ng-container>\n\n </div>\n</p-toolbar>\n<p-dialog header=\"Plotting help\" [(visible)]=\"displayHelpDialog\" [modal]=\"true\" [style]=\"{width: '50vw'}\"\n [maximizable]=\"true\" [draggable]=\"false\" [resizable]=\"false\">\n <p>Click a file in the left-hand tree to display it. By default it opens in the\n <b>Image</b> view \u2014 a natively tiled, deeply zoomable raster (OpenSeadragon) with a\n navigator minimap and a physical-units scale bar. <b>Click the image to zoom</b> toward\n the clicked point; scroll to zoom and drag to pan. When a stack is open, a slice slider\n (Image view) or image-mode toggle (other views) appears. Regions can also be edited in the\n <b>Region Editor</b>, opened with the\n <img src=\"assets/plotting/region-editor.svg\" width=\"14\" height=\"14\" alt=\"Region Editor\"/>\n toolbar button as a floating dialog. Drag the <i class=\"pi pi-bars\"></i> handle to detach\n this toolbar into a floating window.\n </p>\n\n <h3>Plot-type dropdown</h3>\n <p>\n The <b>plot-type dropdown</b> switches how the file is visualized:\n <b>Image</b>, <b>Heatmap</b>, <b>Contour</b>, <b>Surface</b>, <b>Volume</b>,\n <b>Isosurface</b>, and \u2014 when a spatial-omics dataset is open \u2014\n <b>Spatial omics</b> and <b>Spatial omics 3D</b>.\n </p>\n <p>\n <b>Only the modes the data can actually support are offered</b>, so the list changes\n with what is loaded rather than showing entries that would come up blank:\n </p>\n <ul>\n <li><b>Contour</b>, <b>Volume</b> and <b>Isosurface</b> need a grayscale image; the\n two 3D ones also need a z-stack.</li>\n <li><b>Spatial omics</b> needs a published dataset, and <b>Spatial omics 3D</b> needs\n one whose coordinates carry a z.</li>\n <li>The pixel modes (<b>Image</b>, <b>Heatmap</b>, <b>Surface</b>) are withheld for a\n dataset that brings no tissue image \u2014 seqFISH records coordinates and no section, so\n there is nothing for them to draw.</li>\n </ul>\n <ul>\n <li>\"<b>Isosurface band</b>\" slider (Isosurface): sets the intensity band the surfaces are\n drawn for, mapped onto the volume's real intensity range.</li>\n <li>\"<b>3D camera</b>\" (Surface / Scatter 3D / Isosurface): zoom <i class=\"pi pi-search\"></i>,\n pan <i class=\"pi pi-arrows-alt\"></i>, orbit <i class=\"pi pi-globe\"></i>,\n turntable <i class=\"pi pi-sync\"></i>, and reset camera <i class=\"pi pi-home\"></i>;\n scroll to zoom the 3D scene.</li>\n </ul>\n\n <h3>Image manipulation tools</h3>\n <ul>\n <li>\"<b>Region Editor</b>\" <img src=\"assets/plotting/region-editor.svg\" width=\"15\" height=\"15\">:\n opens the Region Editor as a floating, draggable dialog \u2014 a per-image table of every region\n (class, colour, area) with bulk select/label/colour/delete and GeoJSON import/export. It stays\n in sync with the image, so you can keep it open while drawing.</li>\n <li>\"<b>Intensity profile lines</b>\" <img src=\"assets/plotting/spline-chart.svg\" width=\"15\" height=\"15\">\n (<i>work in progress \u2014 not yet stable</i>): adds a coloured line ROI; a floating inset\n chart plots intensity along each line, updates live as you drag it, and re-samples at the\n current zoom resolution. Each added line gets its own colour; select a line and use the\n trash tool <i class=\"pi pi-trash\"></i> to remove it (and its trace).</li>\n <li>\"<b>Channels &amp; Histogram</b>\" <img src=\"assets/plotting/invert.svg\" width=\"15\" height=\"15\">:\n opens a floating dialog for brightness/contrast, gamma, and per-channel display, plus the\n <b>colormap / LUT</b> controls (grayscale only) \u2014 a <b>reverse</b> toggle and a colormap\n dropdown (default: inverted greys).</li>\n <li>\"<b>Smoothen</b>\" <img src=\"assets/plotting/smooth.svg\" width=\"15\" height=\"15\">\n (Image view only): the image opens showing <b>raw pixels</b> (nearest-neighbour), so\n zooming in renders crisp blocks for pixel inspection. Toggle this on for bilinear\n smoothing; the icon turns white while smoothing is active.</li>\n </ul>\n\n <h3>Download &amp; view tools</h3>\n <ul>\n <li>\"<b>Download</b>\" <i class=\"pi pi-download\"></i>: download the current image as a PNG.</li>\n <li>\"<b>Fit to view</b>\" / autoscale <i class=\"pi pi-window-maximize\"></i>: resets the image\n to its original dimensions. In stack mode it also resets to single-image mode.</li>\n </ul>\n\n <h3>Zoom tools</h3>\n <ul>\n <li>\"<b>Zoom box</b>\" <img src=\"assets/plotting/zoom-box.svg\" width=\"15\" height=\"15\">\n (<kbd>b</kbd>): drag a box to select an area to zoom into.</li>\n <li>\"<b>Pan</b>\" <i class=\"pi pi-arrows-alt\"></i> (<kbd>p</kbd>): drag to pan the image.</li>\n <li>\"<b>Zoom in</b>\" <i class=\"pi pi-search-plus\"></i> (<kbd>+</kbd>): zoom in one step.</li>\n <li>\"<b>Zoom out</b>\" <i class=\"pi pi-search-minus\"></i> (<kbd>-</kbd>): zoom out one step,\n stopping at the original image dimensions.</li>\n </ul>\n\n <h3>Region tools</h3>\n <p>By default no tool is selected (all greyed-out); selecting a tool highlights it. The\n vertex-editing tools (polygon, add/delete vertex, move, B\u00E9zier conversions) are available\n in the <b>Image</b> view only. On a <b>donut</b> (a region with a hole), the inner-ring\n vertices are editable too \u2014 drag, add, and delete them just like the outer ring.</p>\n <ul>\n <li>\"<b>Selection</b>\" <i class=\"pi pi-arrow-up-right\"></i> (<kbd>s</kbd>): a neutral mode\n that turns off whatever drawing tool was active (wand, vertex eraser, rectangle, etc.).\n Use it to step out of editing without picking another tool.</li>\n <li>\"<b>Rectangular region</b>\" <i class=\"pi pi-stop\"></i> (<kbd>r</kbd>): click and drag to\n create a rectangular region.</li>\n <li>\"<b>Polyline</b>\" <img src=\"assets/plotting/polyline.svg\" width=\"14\" height=\"14\">\n (<kbd>l</kbd>): draw an open (non-closed) polyline by clicking and dragging. Polylines can\n be edited and exported to GeoJSON as a <code>LineString</code> feature.</li>\n <li>\"<b>Freeform region</b>\" <i class=\"pi pi-pencil\"></i> (<kbd>f</kbd>): click and drag to\n draw a closed freeform polygon.</li>\n <li>\"<b>Brush</b>\" <img src=\"assets/plotting/paintbrush.svg\" width=\"15\" height=\"15\">: click\n and drag to paint a region with a circular brush \u2014 the painted stroke is the region.\n A <b>Size</b> slider (brush diameter, in image pixels) appears in the toolbar while the\n brush is active. Hold <kbd>Shift</kbd> while painting to <b>erase</b> (subtract) from a\n region instead. Painting over an existing region extends it; brushing across two regions\n merges them. Painting a closed ring (or shift-erasing an interior pocket) leaves the\n enclosed area empty \u2014 a <b>donut</b> (Image view).</li>\n <li>\"<b>Polygon</b>\" <img src=\"assets/plotting/polygon-vertices.svg\" width=\"15\" height=\"15\">:\n click to place each vertex; click the first point to close the polygon.</li>\n <li>\"<b>Add vertex</b>\" <img src=\"assets/plotting/vertex-add.svg\" width=\"15\" height=\"15\">:\n click an edge of the selected polygon to insert a new vertex.</li>\n <li>\"<b>Delete vertex</b>\" <img src=\"assets/plotting/vertex-delete.svg\" width=\"15\" height=\"15\">:\n click a vertex of the selected polygon to remove it.</li>\n <li>\"<b>Move region</b>\" <img src=\"assets/plotting/region-move.svg\" width=\"15\" height=\"15\">:\n drag to move the selected region.</li>\n <li>\"<b>To B\u00E9zier</b>\" <img src=\"assets/plotting/to-bezier.svg\" width=\"15\" height=\"15\"> /\n \"<b>To polygon</b>\" <img src=\"assets/plotting/to-polygon.svg\" width=\"15\" height=\"15\">:\n convert the selected region to a smooth B\u00E9zier curve, or back to a straight-edged polygon.</li>\n <li>\"<b>Wand</b>\" <img src=\"assets/plotting/wand.svg\" width=\"15\" height=\"15\"> (<kbd>w</kbd>):\n click to grow a region from pixels with similar values around the click point. Hold\n <kbd>Ctrl</kbd>/<kbd>Cmd</kbd> for an exact-match flood fill; drag to keep extending the\n selection while moving; hold <kbd>Shift</kbd> while clicking or dragging on an existing\n region to <b>erase</b> pixels from it. Use the <b>Sens.</b> slider that appears to make the\n wand stricter (higher = smaller selection) or looser. Subsequent clicks keep extending the\n same region; clicking inside an existing region adopts it, and growing into another region\n merges them.</li>\n <li>\"<b>Vertex eraser</b>\" <i class=\"pi pi-eraser\"></i> (<kbd>e</kbd>): click or click-and-drag\n to remove polygon vertices that fall inside the cursor circle from any region, including a\n region's inner rings (donut holes). Use the <b>Radius</b> slider to set the eraser size in\n image pixels. Polygons reduced below 3 vertices (or polylines below 2) are deleted entirely;\n a hole reduced below 3 vertices is removed.</li>\n <li>\"<b>Undo</b>\" <img src=\"assets/plotting/undo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd>): reverts the\n last region action (draw, delete, edit, wand/brush stroke, segmentation, \u2026). Up to the last\n <b>10</b> actions can be undone; the button greys out when there's nothing left to undo.</li>\n <li>\"<b>Redo</b>\" <img src=\"assets/plotting/redo.svg\" width=\"15\" height=\"15\">\n (<kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> or <kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Y</kbd>):\n re-applies the most recently undone action. Performing a new region action clears what can be redone.</li>\n <li>\"<b>Delete region</b>\" <i class=\"pi pi-trash\"></i> (<kbd>Del</kbd>): deletes the selected\n region. This can also be done from the Region Editor.</li>\n </ul>\n\n <h3>Region actions (right-click the image)</h3>\n <p>Right-click a region \u2014 or the current multi-selection \u2014 to open the\n \"<b>Selected region(s)</b>\" group at the top of the context menu. These act immediately on\n the selection (they're not drawing modes) and are fully undo/redo-able:</p>\n <ul>\n <li>\"<b>Select all regions</b>\" <i class=\"pi pi-check-square\"></i>: select every region on\n the image (also in the Region Editor toolbar).</li>\n <li>\"<b>Merge / group</b>\": combine 2+ selected regions into one. If the pieces don't touch,\n the result is a single multi-part region (sparse \"bits\").</li>\n <li>\"<b>Ungroup</b>\": split a multi-part region back into one region per piece.</li>\n <li>\"<b>Inverse</b>\": replace the selection with everything <i>outside</i> it \u2014 the image\n rectangle with the selected regions punched out as holes. Works at any image size.</li>\n <li>\"<b>Simplify</b>\": reduce a region's vertex count (Douglas\u2013Peucker) by an altitude\n threshold in pixels \u2014 pick <b>Light</b> (1&nbsp;px), <b>Medium</b> (3&nbsp;px),\n <b>Strong</b> (8&nbsp;px), or <b>Custom\u2026</b> for a slider. Applies to the exterior and any\n holes (and every part of a multi-part region).</li>\n <li>The B\u00E9zier conversions and Delete also live in this group (the right-click equivalents of\n the toolbar / vertex tools above).</li>\n </ul>\n\n <h3>Segmentation &amp; detection tools</h3>\n <p>AI-assisted segmentation and detection that run entirely in the browser. The first run\n of any model downloads it (a progress toast is shown); it's then cached.</p>\n <p>Two shapes of tool: the <b>prompted</b> ones (Segment, Point prompts, Cellpose) act on\n rectangles you draw, and generated regions inherit the color of the rectangle they came\n from<span *ngIf=\"contributedTools.length\">; the <b>no-prompt</b> ones ({{ contributedToolNames }})\n sweep the whole current view, so there is nothing to draw first</span>.</p>\n <p>Each model dropdown <i class=\"pi pi-chevron-down\"></i> lists the available checkpoints with\n a <i class=\"pi pi-info-circle\"></i> beside each one \u2014 hover it for that model's size, speed,\n training domain and accuracy, so you can pick without leaving the toolbar.</p>\n <ul>\n <li>\"<b>Segment</b>\" <img src=\"assets/plotting/sam-box-prompt.svg\" width=\"15\" height=\"15\">\n (box prompt): draw one or more <b>Rectangular regions</b> around the objects of interest,\n then click Segment. Each rectangle is replaced by the SAM mask of the object inside it.</li>\n <li>\"<b>Point prompts</b>\" <img src=\"assets/plotting/sam-point-prompt.svg\" width=\"15\" height=\"15\">: click directly on an object to\n segment it as a <b>new</b> region. Each click starts a fresh object \u2014 clicking another\n object won't grow the previous one. <kbd>Shift</kbd>/<kbd>Alt</kbd>-click adds an\n <i>exclude</i> point that refines the current object; <kbd>Esc</kbd> undoes it,\n <kbd>Enter</kbd> commits it.</li>\n <li>\"<b>Model picker</b>\" <i class=\"pi pi-chevron-down\"></i>: the dropdown attached to the\n Segment button chooses the SAM model \u2014 <b>micro-sam ViT-T</b> (default; small, fast) or\n <b>micro-sam ViT-B</b> (larger, highest accuracy) for light microscopy, or\n <b>patho-sam ViT-B</b> for histopathology (H&amp;E / stained tissue). All are promptable\n Segment Anything models finetuned for biomedical images; they differ in the <i>image\n encoder</i> (the heavy once-per-image step) and the training domain \u2014 box and point\n prompting behave the same. The choice applies to <i>both</i> the box and point tools, and\n each tool loads its own encoder, so the first click after switching re-encodes once.</li>\n <li>\"<b>Cellpose</b>\" <img src=\"assets/plotting/cells.svg\" width=\"15\" height=\"15\">: draw rectangles, then click Cellpose to\n automatically segment every cell inside each rectangle (client-side cellpose-SAM) \u2014 one\n region per detected cell. No clicking on individual cells needed.</li>\n <!-- Contributed tools describe themselves. This library ships none, so an\n open build's help lists only the prompted tools above \u2014 the text no\n longer promises buttons that are not there. -->\n <ng-container *ngFor=\"let tool of contributedTools\">\n <li *ngIf=\"tool.help\" [innerHTML]=\"tool.help.body\"></li>\n </ng-container>\n <ng-container *ngFor=\"let tool of dialogTools\">\n <li *ngIf=\"tool.help\" [innerHTML]=\"tool.help.body\"></li>\n </ng-container>\n </ul>\n\n <h3>Spatial omics</h3>\n <p>\n In <b>Spatial omics</b> mode each observation \u2014 a cell or a Visium spot \u2014 is drawn at\n its own coordinates, over the tissue image where the dataset registers onto one. The\n <img src=\"assets/plotting/spatial-omics.svg\" width=\"14\" height=\"14\" alt=\"Spatial omics\"/>\n panel colours them by a <b>column</b> (a cell type, a cluster) or by a <b>gene</b>, and\n the two are exclusive so what drives the colours is never ambiguous.\n </p>\n <ul>\n <li>\"<b>Select from ROIs</b>\": turns the regions you have drawn into a selection of\n observations. Everything else then follows that selection.</li>\n <li>\"<b>Point size</b>\" / \"<b>Opacity</b>\": how the markers are drawn, not what is\n selected.</li>\n </ul>\n\n <h3>Distribution charts</h3>\n <p>\n The <b>Distribution</b> section of the spatial-omics panel charts whatever the map is\n coloured by, so the two can never disagree. Each tab answers a different question, and\n the <i class=\"pi pi-question-circle\"></i> beside them explains the one on screen \u2014\n including what it must <em>not</em> be read for.\n </p>\n <ul>\n <li>\"<b>Counts</b>\": how many observations per category. A category is a label, not a\n magnitude, so a frequency is the only distribution it has.</li>\n <li>\"<b>Histogram</b>\" / \"<b>Violin</b>\" / \"<b>Box</b>\": how a continuous value is\n distributed \u2014 over everything, or split by a grouping column.</li>\n <li>\"<b>Heatmap</b>\": mean expression of the genes you pick within each group.\n Z-scored per gene by default, so a colour means \"above or below this gene's own\n average\" rather than \"highly expressed\".</li>\n <li>\"<b>Embedding</b>\": a UMAP, PCA or t-SNE over the same cells. <b>Lasso it to\n select on the map</b> \u2014 both views read the same selection. Only PCA labels its axes\n with a percentage, because only its axes explain a measurable share of the variance;\n UMAP and t-SNE coordinates are arbitrary, so distance between distant clusters\n should not be read.</li>\n <li>\"<b>Detach</b>\": moves the charts into their own window so they can sit beside the\n map instead of inside the panel. Switching tabs keeps the window where it is.</li>\n </ul>\n\n <h3>Help</h3>\n <p>\"<b>Help</b>\" <i class=\"pi pi-question-circle\"></i>: opens this dialog.</p>\n\n <h3>Keyboard shortcuts</h3>\n <table class=\"shortcuts-table\">\n <tr><td><kbd>+</kbd> / <kbd>=</kbd></td><td>Zoom in</td></tr>\n <tr><td><kbd>-</kbd></td><td>Zoom out</td></tr>\n <tr><td><kbd>b</kbd></td><td>Toggle zoom box mode</td></tr>\n <tr><td><kbd>p</kbd></td><td>Toggle pan mode</td></tr>\n <tr><td><kbd>f</kbd></td><td>Toggle freeform region drawing</td></tr>\n <tr><td><kbd>l</kbd></td><td>Toggle polyline drawing</td></tr>\n <tr><td><kbd>r</kbd></td><td>Toggle rectangular region drawing</td></tr>\n <tr><td><kbd>w</kbd></td><td>Toggle wand tool</td></tr>\n <tr><td>Wand + <kbd>Shift</kbd></td><td>Erase pixels from the active region</td></tr>\n <tr><td>Wand + <kbd>Ctrl</kbd>/<kbd>Cmd</kbd></td><td>Exact-match flood fill (no smoothing)</td></tr>\n <tr><td><kbd>e</kbd></td><td>Toggle vertex eraser</td></tr>\n <tr><td><kbd>s</kbd></td><td>Selection mode (deactivates drawing tools)</td></tr>\n <tr><td>Point prompt + <kbd>Shift</kbd>/<kbd>Alt</kbd></td><td>Add an exclude point (refine the current object)</td></tr>\n <tr><td><kbd>Enter</kbd></td><td>Commit the current SAM point object</td></tr>\n <tr><td><kbd>Esc</kbd></td><td>Undo the current SAM point object</td></tr>\n <tr><td><kbd>Del</kbd> / <kbd>\u232B</kbd></td><td>Delete selected region</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Z</kbd></td><td>Undo the last region action (up to 10)</td></tr>\n <tr><td><kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>Z</kbd> / <kbd>Ctrl</kbd>+<kbd>Y</kbd></td><td>Redo the last undone region action</td></tr>\n </table>\n\n <ng-template pTemplate=\"footer\">\n <p-button icon=\"pi pi-check\" (click)=\"displayHelpDialog=false\" label=\"Ok\" styleClass=\"p-button-text\">\n </p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:block}:host ::ng-deep .p-toolbar{padding:.25rem .5rem}.toolbar-text{color:var(--primary-color);font-size:.875rem}::ng-deep .toolbar-input .p-inputtext{color:var(--primary-color);border-color:var(--primary-color)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-icon-active{filter:brightness(0) invert(1)}::ng-deep .plotmode-item{display:inline-flex;align-items:center;gap:.5rem;color:#000}::ng-deep .plotmode-item i{font-size:1rem;color:var(--primary-color)}::ng-deep .plotmode-item .plotmode-icon-img{width:1.1rem;height:1.1rem;object-fit:contain;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .p-menu.model-menu{width:auto;min-width:12.5rem}::ng-deep .model-menu-item{display:flex;align-items:center;gap:.5rem}::ng-deep .model-menu-item .p-menuitem-text{flex:1 1 auto;white-space:nowrap}::ng-deep .model-menu-item .model-info-icon{flex:0 0 auto;font-size:.875rem;opacity:.55;cursor:help;appearance:none;background:none;border:0;padding:0;margin:0;color:inherit;line-height:1}::ng-deep .model-menu-item .model-info-icon:hover{opacity:1}::ng-deep .model-menu-item .model-info-icon:focus-visible{opacity:1;outline:2px solid var(--primary-color, #2196f3);outline-offset:2px;border-radius:2px}::ng-deep .model-info-tooltip .p-tooltip-text{min-width:18rem;max-width:24rem;line-height:1.4}::ng-deep .wand-sensitivity-slider{width:8rem;align-self:center;margin:0 .25rem}.wand-sensitivity-value{min-width:2rem;text-align:right}span.colormap{width:50px;height:auto;display:inline-block}img.colormap{width:50px;height:auto}.colormap{background:no-repeat;background-size:100%;vertical-align:middle}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.shortcuts-table{border-collapse:collapse}.shortcuts-table td{padding:.25rem .75rem .25rem 0;vertical-align:middle}.shortcuts-table td:first-child{white-space:nowrap}\n"] }]
263
+ args: [{ selector: 'plotting-toolbar', standalone: true, imports: [
264
+ CommonModule,
265
+ ButtonModule,
266
+ RippleModule,
267
+ ToolbarModule,
268
+ TooltipModule,
269
+ ToolbarHelpDialogComponent,
270
+ PlotTypeSelectorComponent,
271
+ StackControlsComponent,
272
+ ViewControlsComponent,
273
+ RegionToolsComponent,
274
+ SegmentationToolsComponent,
275
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p-toolbar>\n <div class=\"p-toolbar-group-start flex-wrap align-items-center gap-1\">\n <!-- Host-provided custom toolbar buttons, shown first (e.g. app-specific\n actions the visualization view itself doesn't own). -->\n <ng-content></ng-content>\n\n <!-- Contributed dialog tools (TOOLBAR_TOOLS, kind: 'dialog'): app-level actions\n like the host's own buttons above, so they sit right beside them. Image view\n only, the view their dialog draws over. Highlighted while the dialog is open. -->\n <ng-container *ngIf=\"isImageView\">\n <p-button\n *ngFor=\"let tool of dialogTools; trackBy: trackToolById\"\n size=\"small\"\n [text]=\"openDialogToolId !== tool.id\"\n [pTooltip]=\"tool.tooltip\"\n tooltipPosition=\"bottom\"\n [ariaLabel]=\"tool.label\"\n [attr.aria-pressed]=\"openDialogToolId === tool.id\"\n (onClick)=\"toggleDialogTool.emit(tool.id)\"\n >\n <img\n *ngIf=\"tool.icon.src\"\n [src]=\"tool.icon.src\"\n style=\"width: 1rem\"\n [alt]=\"tool.label\"\n [class]=\"openDialogToolId === tool.id ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n />\n <i *ngIf=\"tool.icon.pi\" class=\"pi {{ tool.icon.pi }}\"></i>\n </p-button>\n </ng-container>\n\n <!-- Plot type: available for any image (grayscale or RGB), and for a spatial dataset\n that brings none (its spatial modes are still a choice). -->\n <toolbar-plot-type-selector\n *ngIf=\"showPlotTypes\"\n [options]=\"plotTypeOptions\"\n [selected]=\"selectedPlotType\"\n [isosurface]=\"isIsosurfaceMode\"\n [isoRange]=\"isoRange\"\n [intensityLines]=\"isIntensityCapable\"\n (selectPlotType)=\"selectPlotType.emit($event)\"\n (isoRangeChange)=\"isoRangeChange.emit($event)\"\n (addProfileLine)=\"addProfileLine.emit()\"\n >\n </toolbar-plot-type-selector>\n <!-- Channels & Histogram: brightness/contrast, gamma, per-channel display,\n colormap/reverse/invert \u2014 opens the floating dialog. -->\n <p-button\n *ngIf=\"tools.specialTools\"\n pRipple\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Channels &amp; Histogram\"\n pTooltip=\"Channels &amp; Histogram (brightness/contrast, colormap)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openChannelHistogram.emit()\"\n >\n <img src=\"assets/plotting/invert.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\" />\n </p-button>\n\n <!-- Spatial-omics controls. Only meaningful in the spatial mode, which is\n itself only offered while a dataset is loaded \u2014 so this button appears\n exactly when there is something for it to control. -->\n <p-button\n *ngIf=\"tools.specialTools && isSpatialMode\"\n pRipple\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Spatial omics controls\"\n pTooltip=\"Spatial omics (colour by column or gene, legend, selection, distributions)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"openSpatialControls.emit()\"\n >\n <img src=\"assets/plotting/spatial-omics.svg\" class=\"toolbar-icon-dark\" style=\"width: 1rem\" />\n </p-button>\n\n <!-- Smoothen toggle: bilinear smoothing vs nearest-neighbour raw pixels (Image\n view only \u2014 it's an OpenSeadragon drawer setting). Highlighted when smoothing on. -->\n <p-button\n *ngIf=\"tools.specialTools && isImageView\"\n size=\"small\"\n [text]=\"!imageSmoothingEnabled\"\n ariaLabel=\"Smoothen\"\n pTooltip=\"Smoothen (turn on image smoothing)\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleImageSmoothing.emit()\"\n >\n <img\n src=\"assets/plotting/smooth.svg\"\n style=\"width: 1rem\"\n [class]=\"imageSmoothingEnabled ? 'toolbar-icon-active' : 'toolbar-icon-dark'\"\n />\n </p-button>\n\n <!-- Stack controls -->\n <toolbar-stack-controls\n *ngIf=\"imageInfo?.isStack\"\n [liveScrubber]=\"showsLiveSliceScrubber\"\n [stackToggle]=\"!showsLiveSliceScrubber && !isNapariMode && !isSpatialMode\"\n [showStack]=\"!!imageInfo?.showStack\"\n [stackOptions]=\"stackOptions\"\n [zIndex]=\"zIndex\"\n [maxIndex]=\"maxIndex\"\n (zScrub)=\"zScrub.emit($event)\"\n (zSlide)=\"zSlide.emit($event)\"\n (selectStackOption)=\"selectStackOption.emit($event)\"\n (zIndexInput)=\"zIndexInput.emit($event)\"\n (reloadAndPlot)=\"reloadAndPlot.emit()\"\n >\n </toolbar-stack-controls>\n\n <!-- Image actions (special tools) -->\n <ng-container *ngIf=\"tools.specialTools\">\n <span class=\"toolbar-separator\"></span>\n <p-button\n icon=\"pi pi-download\"\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Download image as PNG\"\n pTooltip=\"Download image as PNG\"\n tooltipPosition=\"bottom\"\n (onClick)=\"downloadImage.emit()\"\n >\n </p-button>\n <p-button\n *ngIf=\"isHeatmap\"\n icon=\"pi pi-window-maximize\"\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Fit to view\"\n pTooltip=\"Fit to view\"\n tooltipPosition=\"bottom\"\n (onClick)=\"autoscaleImage.emit()\"\n >\n </p-button>\n </ng-container>\n\n <toolbar-view-controls\n [isHeatmap]=\"isHeatmap\"\n [zoomTools]=\"tools.zoomTools\"\n [isImageView]=\"isImageView\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [isNapariMode]=\"isNapariMode\"\n [isNapariSurfaceMode]=\"isNapariSurfaceMode\"\n [isNapari3dMode]=\"isNapari3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n (toggleDragMode)=\"toggleDragMode.emit($event)\"\n (zoomIn)=\"zoomIn.emit()\"\n (zoomOut)=\"zoomOut.emit()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode.emit($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera.emit()\"\n (toggleAxes)=\"toggleAxes.emit()\"\n (toggleWireframe)=\"toggleWireframe.emit()\"\n (selectResolution)=\"selectResolution.emit($event)\"\n >\n </toolbar-view-controls>\n\n <ng-container *ngIf=\"(isHeatmap || is3dRegions) && tools.regionTools\">\n <toolbar-region-tools\n [activeDragMode]=\"activeDragMode\"\n [is3dRegions]=\"is3dRegions\"\n [vertexTools]=\"supportsRegionVertexTools\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"canUndo\"\n [canRedo]=\"canRedo\"\n (openRegionEditor)=\"openRegionEditor.emit()\"\n (toggleDragMode)=\"toggleDragMode.emit($event)\"\n (undoRegion)=\"undoRegion.emit()\"\n (redoRegion)=\"redoRegion.emit()\"\n (deleteRegion)=\"deleteRegion.emit()\"\n (wandSensitivityChange)=\"wandSensitivityChange.emit($event)\"\n (brushSizeChange)=\"brushSizeChange.emit($event)\"\n (vertexEraserRadiusChange)=\"vertexEraserRadiusChange.emit($event)\"\n >\n </toolbar-region-tools>\n <toolbar-segmentation-tools\n [activeDragMode]=\"activeDragMode\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [toolModelIds]=\"toolModelIds\"\n (segmentRegions)=\"segmentRegions.emit()\"\n (segmentCellpose)=\"segmentCellpose.emit()\"\n (toggleDragMode)=\"toggleDragMode.emit($event)\"\n (samModelChange)=\"samModelChange.emit($event)\"\n (runTool)=\"runTool.emit($event)\"\n (toolModelChange)=\"toolModelChange.emit($event)\"\n (openToolParams)=\"openToolParams.emit($event)\"\n >\n </toolbar-segmentation-tools>\n </ng-container>\n\n <ng-container *ngIf=\"tools.help\">\n <span class=\"toolbar-separator\"></span>\n\n <!-- Help -->\n <p-button\n icon=\"pi pi-question-circle\"\n size=\"small\"\n [text]=\"true\"\n ariaLabel=\"Help\"\n pTooltip=\"Help\"\n tooltipPosition=\"bottom\"\n (onClick)=\"showHelp()\"\n >\n </p-button>\n </ng-container>\n </div>\n</p-toolbar>\n<toolbar-help-dialog\n [(visible)]=\"displayHelpDialog\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n>\n</toolbar-help-dialog>\n", styles: [".toolbar-text{color:var(--primary-color);font-size:.875rem}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.toolbar-icon-dark{filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.toolbar-icon-active{filter:brightness(0) invert(1)}.wand-sensitivity-value{min-width:2rem;text-align:right}:host{display:block}:host ::ng-deep .p-toolbar{padding:.25rem .5rem}::ng-deep .toolbar-input .p-inputtext{color:var(--primary-color);border-color:var(--primary-color)}::ng-deep .p-menu.model-menu{width:auto;min-width:12.5rem}::ng-deep .model-menu-item{display:flex;align-items:center;gap:.5rem}::ng-deep .model-menu-item .p-menuitem-text{flex:1 1 auto;white-space:nowrap}::ng-deep .model-menu-item .model-info-icon{flex:0 0 auto;font-size:.875rem;opacity:.55;cursor:help;appearance:none;background:none;border:0;padding:0;margin:0;color:inherit;line-height:1}::ng-deep .model-menu-item .model-info-icon:hover{opacity:1}::ng-deep .model-menu-item .model-info-icon:focus-visible{opacity:1;outline:2px solid var(--primary-color, #2196f3);outline-offset:2px;border-radius:2px}::ng-deep .model-info-tooltip .p-tooltip-text{min-width:18rem;max-width:24rem;line-height:1.4}::ng-deep .wand-sensitivity-slider{width:8rem;align-self:center;margin:0 .25rem}span.colormap{width:50px;height:auto;display:inline-block}img.colormap{width:50px;height:auto}.colormap{background:no-repeat;background-size:100%;vertical-align:middle}\n"] }]
325
276
  }], propDecorators: { imageInfo: [{
326
277
  type: Input
327
278
  }], hasSpatialDataset: [{
@@ -451,4 +402,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
451
402
  }], vertexEraserRadiusChange: [{
452
403
  type: Output
453
404
  }] } });
454
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbGJhci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQWEsTUFBTSxFQUFpQixNQUFNLGVBQWUsQ0FBQztBQUlqRyxPQUFPLEVBQUUsUUFBUSxFQUFjLGlCQUFpQixFQUFFLFVBQVUsRUFBRSxrQkFBa0IsRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLHVCQUF1QixFQUFFLHVCQUF1QixFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3ZPLE9BQU8sRUFBeUIsaUJBQWlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUd2RixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sY0FBYyxDQUFDOzs7Ozs7Ozs7Ozs7OztBQUUxQzs7Ozs7Ozs7Ozs7R0FXRztBQVFILE1BQU0sT0FBTyxnQkFBZ0I7SUFQN0I7UUFVRSw0RkFBNEY7UUFDbkYsc0JBQWlCLEdBQUcsS0FBSyxDQUFDO1FBTW5DLDRFQUE0RTtRQUNuRSxvQkFBZSxHQUFxQixFQUFFLENBQUM7UUFDaEQsd0VBQXdFO1FBQy9ELHFCQUFnQixHQUFlLFFBQVEsQ0FBQyxLQUFLLENBQUM7UUFDdkQ7Ozs7OztXQU1HO1FBQ00saUJBQVksR0FBb0IsSUFBSSxDQUFDO1FBQzlDOzRFQUNvRTtRQUMzRCxhQUFRLEdBQWEsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDOUIsYUFBUSxHQUFHLENBQUMsQ0FBQztRQUNiLFdBQU0sR0FBRyxDQUFDLENBQUM7UUFDcEIsa0RBQWtEO1FBQ3pDLGNBQVMsR0FBRyxJQUFJLENBQUM7UUFDMUI7Ozs7O1dBS0c7UUFDTSxnQkFBVyxHQUFHLEtBQUssQ0FBQztRQUNwQixtQkFBYyxHQUFrQixJQUFJLENBQUM7UUFDckMsd0JBQW1CLEdBQUcsV0FBVyxDQUFDO1FBQzNDLDRGQUE0RjtRQUNuRixnQkFBVyxHQUFHLElBQUksQ0FBQztRQUNuQixvQkFBZSxHQUFHLEtBQUssQ0FBQztRQUNqQyxzRUFBc0U7UUFDN0Qsb0JBQWUsR0FBRyx1QkFBdUIsQ0FBQztRQUNuRCwyREFBMkQ7UUFDbEQsb0JBQWUsR0FBRyx1QkFBdUIsQ0FBQztRQUMxQyxvQkFBZSxHQUFHLEdBQUcsQ0FBQztRQUN0QixjQUFTLEdBQUcsRUFBRSxDQUFDO1FBQ2YsdUJBQWtCLEdBQUcsRUFBRSxDQUFDO1FBQ2pDO3NGQUM4RTtRQUNyRSxZQUFPLEdBQUcsS0FBSyxDQUFDO1FBQ3pCOzhDQUNzQztRQUM3QixZQUFPLEdBQUcsS0FBSyxDQUFDO1FBQ2hCLGlCQUFZLEdBQ25CLENBQUMsRUFBRSxJQUFJLEVBQUUsY0FBYyxFQUFFLEdBQUcsRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDM0U7d0ZBQ2dGO1FBQ3ZFLFVBQUssR0FBb0MsaUJBQWlCLENBQUM7UUFDcEU7MkVBQ21FO1FBQzFELDBCQUFxQixHQUFHLEtBQUssQ0FBQztRQUN2QyxtRUFBbUU7UUFDMUQsY0FBUyxHQUFvQyxFQUFFLENBQUM7UUFDaEQsZUFBVSxHQUFHLEVBQUUsQ0FBQztRQUN6Qjs7OztXQUlHO1FBQ00scUJBQWdCLEdBQThCLEVBQUUsQ0FBQztRQUMxRCxxRkFBcUY7UUFDNUUsZ0JBQVcsR0FBb0MsRUFBRSxDQUFDO1FBQzNELDJEQUEyRDtRQUNsRCxxQkFBZ0IsR0FBa0IsSUFBSSxDQUFDO1FBQ2hELGdFQUFnRTtRQUN2RCxpQkFBWSxHQUEyQixFQUFFLENBQUM7UUFFekMsbUJBQWMsR0FBRyxJQUFJLFlBQVksRUFBYyxDQUFDO1FBQzFELHlFQUF5RTtRQUMvRCxtQkFBYyxHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7UUFDcEQsNkVBQTZFO1FBQ25FLHlCQUFvQixHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7UUFDMUQsOEVBQThFO1FBQzlFLGtEQUFrRDtRQUN4QyxtQkFBYyxHQUFHLElBQUksWUFBWSxFQUF3QixDQUFDO1FBQ3BFLCtFQUErRTtRQUNyRSx5QkFBb0IsR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBQzFELDBFQUEwRTtRQUNoRSx3QkFBbUIsR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBQy9DLHFCQUFnQixHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7UUFDNUMsc0JBQWlCLEdBQUcsSUFBSSxZQUFZLEVBQWlDLENBQUM7UUFDdEUsV0FBTSxHQUFHLElBQUksWUFBWSxFQUFzQixDQUFDO1FBQ2hELFdBQU0sR0FBRyxJQUFJLFlBQVksRUFBc0IsQ0FBQztRQUNoRCxnQkFBVyxHQUFHLElBQUksWUFBWSxFQUFVLENBQUM7UUFDekMsa0JBQWEsR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBQ3pDLGtCQUFhLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztRQUN6QyxtQkFBYyxHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7UUFDMUMsbUJBQWMsR0FBRyxJQUFJLFlBQVksRUFBVSxDQUFDO1FBQzVDLFdBQU0sR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBQ2xDLFlBQU8sR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBQ25DLHdCQUFtQixHQUFHLElBQUksWUFBWSxFQUFVLENBQUM7UUFDakQsdUJBQWtCLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztRQUN4RCxpRUFBaUU7UUFDdkQsZUFBVSxHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7UUFDaEQsMERBQTBEO1FBQ2hELG9CQUFlLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztRQUNyRCw0RUFBNEU7UUFDbEUscUJBQWdCLEdBQUcsSUFBSSxZQUFZLEVBQVUsQ0FBQztRQUM5QyxpQkFBWSxHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7UUFDbEQsK0NBQStDO1FBQ3JDLGVBQVUsR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBQ2hELHNEQUFzRDtRQUM1QyxlQUFVLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztRQUNoRCw2RUFBNkU7UUFDbkUsbUJBQWMsR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBQ3BELDhFQUE4RTtRQUNwRSxvQkFBZSxHQUFHLElBQUksWUFBWSxFQUFRLENBQUM7UUFDckQsZ0VBQWdFO1FBQ3RELFlBQU8sR0FBRyxJQUFJLFlBQVksRUFBVSxDQUFDO1FBQy9DLGdGQUFnRjtRQUN0RSxxQkFBZ0IsR0FBRyxJQUFJLFlBQVksRUFBVSxDQUFDO1FBQ3hELDRDQUE0QztRQUNsQyxvQkFBZSxHQUFHLElBQUksWUFBWSxFQUF1QyxDQUFDO1FBQ3BGLDhEQUE4RDtRQUNwRCxtQkFBYyxHQUFHLElBQUksWUFBWSxFQUFVLENBQUM7UUFDdEQsdUNBQXVDO1FBQzdCLG1CQUFjLEdBQUcsSUFBSSxZQUFZLEVBQVUsQ0FBQztRQUM1QywwQkFBcUIsR0FBRyxJQUFJLFlBQVksRUFBc0IsQ0FBQztRQUMvRCxvQkFBZSxHQUFHLElBQUksWUFBWSxFQUFzQixDQUFDO1FBQ3pELDZCQUF3QixHQUFHLElBQUksWUFBWSxFQUFzQixDQUFDO1FBRTVFLDBFQUEwRTtRQUNqRSx1QkFBa0IsR0FBRyxHQUFHLENBQUM7UUFDekIsdUJBQWtCLEdBQUcsSUFBSSxDQUFDO1FBQzFCLHdCQUFtQixHQUFHLEdBQUcsQ0FBQztRQUMxQixpQkFBWSxHQUFHLENBQUMsQ0FBQztRQUNqQixpQkFBWSxHQUFHLEdBQUcsQ0FBQztRQUNuQixrQkFBYSxHQUFHLENBQUMsQ0FBQztRQUNsQiwwQkFBcUIsR0FBRyxDQUFDLENBQUM7UUFDMUIsMEJBQXFCLEdBQUcsR0FBRyxDQUFDO1FBQzVCLDJCQUFzQixHQUFHLENBQUMsQ0FBQztRQUMzQixnQkFBVyxHQUFHLENBQUMsQ0FBQztRQUNoQixnQkFBVyxHQUFHLEdBQUcsQ0FBQztRQUNsQixpQkFBWSxHQUFHLENBQUMsQ0FBQztRQUUxQixzQkFBaUIsR0FBRyxLQUFLLENBQUM7UUFFMUI7Ozs7Ozs7Ozs7O3lGQVdpRjtRQUNqRixpQkFBWSxHQUFlLEVBQUUsQ0FBQztRQUM5Qjs7Ozs7Ozs7O1dBU0c7UUFDSCxrQkFBYSxHQUErQixFQUFFLENBQUM7S0FtSmhEO0lBeFRDLDBGQUEwRjtJQUMxRixJQUFJLGFBQWE7UUFDZixPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLElBQUksSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDO0lBQ25GLENBQUM7SUFvS0Q7NEVBQ3dFO0lBQ3hFLFdBQVcsQ0FBQyxPQUFzQjtRQUNoQyxJQUFJLE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztZQUNsRCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO2dCQUM3QyxLQUFLLEVBQUUsQ0FBQyxDQUFDLEtBQUs7Z0JBQ2QsSUFBSSxFQUFFLENBQUMsQ0FBQyxFQUFFLEtBQUssSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxVQUFVO2dCQUMzRCxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQ3pCLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2FBQzlDLENBQUMsQ0FBQyxDQUFDO1FBQ04sQ0FBQztRQUNELElBQUksT0FBTyxDQUFDLGtCQUFrQixDQUFDLElBQUksT0FBTyxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUM7WUFDM0QsSUFBSSxDQUFDLGFBQWEsR0FBRyxFQUFFLENBQUM7WUFDeEIsS0FBSyxNQUFNLElBQUksSUFBSSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztnQkFDekMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUNuRSxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO29CQUN0RCxLQUFLLEVBQUUsQ0FBQyxDQUFDLEtBQUs7b0JBQ2QsSUFBSSxFQUFFLENBQUMsQ0FBQyxFQUFFLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLFVBQVU7b0JBQ2xELE9BQU8sRUFBRSxDQUFDLENBQUMsSUFBSTtvQkFDZixPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDO2lCQUM3RSxDQUFDLENBQUMsQ0FBQztZQUNOLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNILFNBQVMsQ0FBQyxJQUFZO1FBQ3BCLE9BQU8sQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDO2FBQ2hCLE9BQU8sQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDO2FBQzdCLE9BQU8sQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDO2FBQ3ZCLE9BQU8sQ0FBQyxTQUFTLEVBQUUsR0FBRyxDQUFDO2FBQ3ZCLE9BQU8sQ0FBQyxVQUFVLEVBQUUsR0FBRyxDQUFDO2FBQ3hCLE9BQU8sQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDO2FBQ3RCLE9BQU8sQ0FBQyxNQUFNLEVBQUUsR0FBRyxDQUFDO2FBQ3BCLElBQUksRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUVEOzsrREFFMkQ7SUFDM0QsSUFBSSxvQkFBb0I7UUFDdEIsT0FBTyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzlELENBQUM7SUFFRDs0RUFDd0U7SUFDeEUsYUFBYSxDQUFDLE1BQWMsRUFBRSxJQUFvQjtRQUNoRCxPQUFPLElBQUksQ0FBQyxFQUFFLENBQUM7SUFDakIsQ0FBQztJQUVELGlGQUFpRjtJQUNqRixJQUFJLGlCQUFpQjtRQUNuQixJQUFJLElBQUksQ0FBQyxZQUFZO1lBQUUsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDO1FBQ2hELE9BQU8saUJBQWlCLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQztJQUMzRixDQUFDO0lBRUQ7NkNBQ3lDO0lBQ3pDLElBQUksV0FBVztRQUNiLE9BQU8sSUFBSSxDQUFDLGlCQUFpQixLQUFLLFFBQVEsQ0FBQyxLQUFLLENBQUM7SUFDbkQsQ0FBQztJQUVEOzs7c0NBR2tDO0lBQ2xDLElBQUksYUFBYTtRQUNmLE9BQU8sY0FBYyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLGdCQUFnQixDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0lBQzVGLENBQUM7SUFFRDtvRkFDZ0Y7SUFDaEYsUUFBUSxDQUFDLElBQXdCO1FBQy9CLE9BQU8sQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzFDLENBQUM7SUFFRDsrRkFDMkY7SUFDM0YsSUFBSSx5QkFBeUI7UUFDM0IsT0FBTyxDQUNMLElBQUksQ0FBQyxpQkFBaUIsS0FBSyxRQUFRLENBQUMsS0FBSztZQUN6QyxJQUFJLENBQUMsaUJBQWlCLEtBQUssUUFBUSxDQUFDLFlBQVksQ0FDakQsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs4Q0FJMEM7SUFDMUMsSUFBSSxzQkFBc0I7UUFDeEIsT0FBTyxDQUNMLElBQUksQ0FBQyxpQkFBaUIsS0FBSyxRQUFRLENBQUMsS0FBSztZQUN6QyxJQUFJLENBQUMsaUJBQWlCLEtBQUssUUFBUSxDQUFDLFlBQVk7WUFDaEQsZUFBZSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQztZQUN2QyxlQUFlLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDO1lBQ3ZDLGNBQWMsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FDdkMsQ0FBQztJQUNKLENBQUM7SUFFRDtpRkFDNkU7SUFDN0UsSUFBSSxZQUFZO1FBQ2QsT0FBTyxDQUNMLElBQUksQ0FBQyxpQkFBaUIsS0FBSyxRQUFRLENBQUMsWUFBWTtZQUNoRCxlQUFlLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDO1lBQ3ZDLFVBQVUsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FDbkMsQ0FBQztJQUNKLENBQUM7SUFFRCxrR0FBa0c7SUFDbEcsSUFBSSxnQkFBZ0I7UUFDbEIsT0FBTyxDQUNMLElBQUksQ0FBQyxpQkFBaUIsS0FBSyxRQUFRLENBQUMsVUFBVSxJQUFJLGtCQUFrQixDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUM3RixDQUFDO0lBQ0osQ0FBQztJQUVELGlFQUFpRTtJQUNqRSxJQUFJLG1CQUFtQjtRQUNyQixPQUFPLGVBQWUsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUNqRCxDQUFDO0lBRUQsK0ZBQStGO0lBQy9GLElBQUksY0FBYztRQUNoQixPQUFPLFVBQVUsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQ7aUVBQzZEO0lBQzdELElBQUksa0JBQWtCO1FBQ3BCLE9BQU8sSUFBSSxDQUFDLGlCQUFpQixLQUFLLFFBQVEsQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFDLGlCQUFpQixLQUFLLFFBQVEsQ0FBQyxLQUFLLENBQUM7SUFDbEcsQ0FBQztJQUVELFFBQVE7UUFDTixJQUFJLENBQUMsaUJBQWlCLEdBQUcsSUFBSSxDQUFDO0lBQ2hDLENBQUM7OEdBN1RVLGdCQUFnQjtrR0FBaEIsZ0JBQWdCLDBuRUM3QjdCLGsybURBaTBCQTs7MkZEcHlCYSxnQkFBZ0I7a0JBUDVCLFNBQVM7K0JBR0Usa0JBQWtCOzhCQU1uQixTQUFTO3NCQUFqQixLQUFLO2dCQUVHLGlCQUFpQjtzQkFBekIsS0FBSztnQkFPRyxlQUFlO3NCQUF2QixLQUFLO2dCQUVHLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFRRyxZQUFZO3NCQUFwQixLQUFLO2dCQUdHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDRyxNQUFNO3NCQUFkLEtBQUs7Z0JBRUcsU0FBUztzQkFBakIsS0FBSztnQkFPRyxXQUFXO3NCQUFuQixLQUFLO2dCQUNHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBQ0csbUJBQW1CO3NCQUEzQixLQUFLO2dCQUVHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBQ0csZUFBZTtzQkFBdkIsS0FBSztnQkFFRyxlQUFlO3NCQUF2QixLQUFLO2dCQUdHLGVBQWU7c0JBQXZCLEtBQUs7Z0JBQ0csU0FBUztzQkFBakIsS0FBSztnQkFDRyxrQkFBa0I7c0JBQTFCLEtBQUs7Z0JBR0csT0FBTztzQkFBZixLQUFLO2dCQUdHLE9BQU87c0JBQWYsS0FBSztnQkFDRyxZQUFZO3NCQUFwQixLQUFLO2dCQUlHLEtBQUs7c0JBQWIsS0FBSztnQkFHRyxxQkFBcUI7c0JBQTdCLEtBQUs7Z0JBRUcsU0FBUztzQkFBakIsS0FBSztnQkFDRyxVQUFVO3NCQUFsQixLQUFLO2dCQU1HLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFFRyxXQUFXO3NCQUFuQixLQUFLO2dCQUVHLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFFRyxZQUFZO3NCQUFwQixLQUFLO2dCQUVJLGNBQWM7c0JBQXZCLE1BQU07Z0JBRUcsY0FBYztzQkFBdkIsTUFBTTtnQkFFRyxvQkFBb0I7c0JBQTdCLE1BQU07Z0JBR0csY0FBYztzQkFBdkIsTUFBTTtnQkFFRyxvQkFBb0I7c0JBQTdCLE1BQU07Z0JBRUcsbUJBQW1CO3NCQUE1QixNQUFNO2dCQUNHLGdCQUFnQjtzQkFBekIsTUFBTTtnQkFDRyxpQkFBaUI7c0JBQTFCLE1BQU07Z0JBQ0csTUFBTTtzQkFBZixNQUFNO2dCQUNHLE1BQU07c0JBQWYsTUFBTTtnQkFDRyxXQUFXO3NCQUFwQixNQUFNO2dCQUNHLGFBQWE7c0JBQXRCLE1BQU07Z0JBQ0csYUFBYTtzQkFBdEIsTUFBTTtnQkFDRyxjQUFjO3NCQUF2QixNQUFNO2dCQUNHLGNBQWM7c0JBQXZCLE1BQU07Z0JBQ0csTUFBTTtzQkFBZixNQUFNO2dCQUNHLE9BQU87c0JBQWhCLE1BQU07Z0JBQ0csbUJBQW1CO3NCQUE1QixNQUFNO2dCQUNHLGtCQUFrQjtzQkFBM0IsTUFBTTtnQkFFRyxVQUFVO3NCQUFuQixNQUFNO2dCQUVHLGVBQWU7c0JBQXhCLE1BQU07Z0JBRUcsZ0JBQWdCO3NCQUF6QixNQUFNO2dCQUNHLFlBQVk7c0JBQXJCLE1BQU07Z0JBRUcsVUFBVTtzQkFBbkIsTUFBTTtnQkFFRyxVQUFVO3NCQUFuQixNQUFNO2dCQUVHLGNBQWM7c0JBQXZCLE1BQU07Z0JBRUcsZUFBZTtzQkFBeEIsTUFBTTtnQkFFRyxPQUFPO3NCQUFoQixNQUFNO2dCQUVHLGdCQUFnQjtzQkFBekIsTUFBTTtnQkFFRyxlQUFlO3NCQUF4QixNQUFNO2dCQUVHLGNBQWM7c0JBQXZCLE1BQU07Z0JBRUcsY0FBYztzQkFBdkIsTUFBTTtnQkFDRyxxQkFBcUI7c0JBQTlCLE1BQU07Z0JBQ0csZUFBZTtzQkFBeEIsTUFBTTtnQkFDRyx3QkFBd0I7c0JBQWpDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIEV2ZW50RW1pdHRlciwgSW5wdXQsIE9uQ2hhbmdlcywgT3V0cHV0LCBTaW1wbGVDaGFuZ2VzIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBNZW51SXRlbSB9IGZyb20gJ3ByaW1lbmcvYXBpJztcblxuaW1wb3J0IHsgSUltYWdlSW5mbyB9IGZyb20gJy4uL2NvbnRyYWN0cy9pbWFnZS5jb250cmFjdCc7XG5pbXBvcnQgeyBQbG90VHlwZSwgUGxvdFR5cGVJZCwgaXNCdWlsdGluUGxvdFR5cGUsIGlzTmFwYXJpM2QsIGlzTmFwYXJpSXNvc3VyZmFjZSwgaXNOYXBhcmlTdXJmYWNlLCBpc05hcGFyaVNjYXR0ZXIsIE5BUEFSSV9ERUNJTUFURV9PUFRJT05TLCBOQVBBUklfREVGQVVMVF9ERUNJTUFURSwgaXNTcGF0aWFsT21pY3MsIGlzU3BhdGlhbE9taWNzM2QgfSBmcm9tICcuLi9jb250cmFjdHMvcGxvdC10eXBlJztcbmltcG9ydCB7IFRvb2xiYXJUb29sVmlzaWJpbGl0eSwgQUxMX1RPT0xCQVJfVE9PTFMgfSBmcm9tICcuLi9jb250cmFjdHMvdG9vbGJhci1jb25maWcnO1xuaW1wb3J0IHsgVG9vbGJhckRpYWxvZ1Rvb2xDb250cmlidXRpb24sIFRvb2xiYXJUb29sQ29udHJpYnV0aW9uIH0gZnJvbSAnLi4vY29udHJhY3RzL3Rvb2xiYXItdG9vbC5jb250cmFjdCc7XG5pbXBvcnQgeyBQbG90VHlwZU9wdGlvbiB9IGZyb20gJy4uL2NvbnRyYWN0cy9wbG90LXR5cGUtY29udHJpYnV0aW9uLmNvbnRyYWN0JztcbmltcG9ydCB7IE1PREVMX0lORk8gfSBmcm9tICcuL21vZGVsLWluZm8nO1xuXG4vKipcbiAqIFByZXNlbnRhdGlvbmFsIHRvb2xiYXIgZm9yIHRoZSBwbG90dGluZyB2aWV3cG9ydC5cbiAqXG4gKiBSZW5kZXJzIHRoZSBwbG90LXR5cGUgc2VsZWN0b3IsIGNvbG9ybWFwL0xVVCBjb250cm9scywgc3RhY2sgbmF2aWdhdGlvbixcbiAqIGltYWdlIGFjdGlvbnMgKGRvd25sb2FkIC8gYXV0b3NjYWxlIC8gcGlwZWxpbmUpLCB2aWV3cG9ydCB6b29tL3BhbiwgdGhlXG4gKiBTdXJmYWNlLTNEIGNhbWVyYSBjb250cm9scywgYW5kIHRoZSByZWdpb24tZHJhd2luZyB0b29scyDigJQgcGx1cyB0aGUgcGxvdHRpbmdcbiAqIGhlbHAgZGlhbG9nLiBJdCBvd25zIG5vIHJlbmRlcmluZyBvciB2aXN1YWxpemF0aW9uIHN0YXRlOiB0aGUgaG9zdFxuICogKFZpc3VhbGl6ZXJDb21wb25lbnQpIHN0YXlzIHRoZSBvcmNoZXN0cmF0b3IsIHBhc3Npbmcgc3RhdGUgaW4gdmlhIGBASW5wdXRgIGFuZFxuICogaGFuZGxpbmcgZXZlcnkgYWN0aW9uIHZpYSBgQE91dHB1dGAuIFRoZSBzYW1lIGFjdGlvbnMgYXJlIGFsc28gZHJpdmVuIGJ5IHRoZVxuICogZGlhZ3JhbSdzIHJpZ2h0LWNsaWNrIGNvbnRleHQgbWVudSBhbmQga2V5Ym9hcmQgc2hvcnRjdXRzLCBzbyBrZWVwaW5nIHRoZVxuICogaGFuZGxlcnMgaW4gdGhlIGhvc3QgYXZvaWRzIGR1cGxpY2F0aW5nIHRoYXQgbG9naWMuXG4gKi9cbkBDb21wb25lbnQoe1xuICAvLyBDYW5vbmljYWwgcHJlZml4ZWQgc2VsZWN0b3IgZmlyc3Q7IHRoZSB1bnByZWZpeGVkIG9yaWdpbmFsIGlzIGtlcHQgYXMgYW5cbiAgLy8gYWxpYXMgZm9yIG9uZSByZWxlYXNlIChwcmUtcHVibGljYXRpb24gYmFjay1jb21wYXQpLlxuICBzZWxlY3RvcjogJ3Bsb3R0aW5nLXRvb2xiYXInLFxuICB0ZW1wbGF0ZVVybDogJy4vdG9vbGJhci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3Rvb2xiYXIuY29tcG9uZW50LnNjc3MnXSxcbn0pXG5leHBvcnQgY2xhc3MgVG9vbGJhckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uQ2hhbmdlcyB7XG4gIC8qKiBDdXJyZW50IGltYWdlIChnYXRlcyB3aGljaCBjb250cm9sIGdyb3VwcyBhcmUgc2hvd24pLiAqL1xuICBASW5wdXQoKSBpbWFnZUluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQ7XG4gIC8qKiBBIHNwYXRpYWwgZGF0YXNldCBpcyBvbiBvZmZlcjogaXRzIHBsb3QgbW9kZXMgYXJlIGEgY2hvaWNlIGV2ZW4gd2l0aCBubyBpbWFnZSBsb2FkZWQuICovXG4gIEBJbnB1dCgpIGhhc1NwYXRpYWxEYXRhc2V0ID0gZmFsc2U7XG5cbiAgLyoqIFdoZXRoZXIgdGhlIHBsb3QtdHlwZSBkcm9wZG93biBzaG93czogZm9yIGFuIGltYWdlLCBvciBhIHNwYXRpYWwgZGF0YXNldCB3aXRoIG5vbmUuICovXG4gIGdldCBzaG93UGxvdFR5cGVzKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAhISh0aGlzLmltYWdlSW5mbyB8fCB0aGlzLmhhc1NwYXRpYWxEYXRhc2V0KSAmJiAhIXRoaXMudG9vbHMuc3BlY2lhbFRvb2xzO1xuICB9XG4gIC8qKiBQbG90IHR5cGVzIHRoZSBhY3RpdmUgYmFja2VuZCBhZHZlcnRpc2VzLCB0aGVuIGFueSBjb250cmlidXRlZCBtb2Rlcy4gKi9cbiAgQElucHV0KCkgcGxvdFR5cGVPcHRpb25zOiBQbG90VHlwZU9wdGlvbltdID0gW107XG4gIC8qKiBUaGUgc2VsZWN0b3IncyB2YWx1ZTogYSBidWlsdC1pbiB0eXBlIG9yIGEgY29udHJpYnV0ZWQgbW9kZSdzIGlkLiAqL1xuICBASW5wdXQoKSBzZWxlY3RlZFBsb3RUeXBlOiBQbG90VHlwZUlkID0gUGxvdFR5cGUuSU1BR0U7XG4gIC8qKlxuICAgKiBUaGUgYnVpbHQtaW4gdHlwZSB0aGF0IHJlbmRlcnMge0BsaW5rIHNlbGVjdGVkUGxvdFR5cGV9IOKAlCBpdHMgYGJhc2VUeXBlYFxuICAgKiBmb3IgYSBjb250cmlidXRlZCBtb2RlLiBFdmVyeSB0b29sL2NvbnRyb2wgZGVjaXNpb24gYmVsb3cgcmVhZHNcbiAgICoge0BsaW5rIGVmZmVjdGl2ZVBsb3RUeXBlfSwgc28gYSBjb250cmlidXRlZCBtb2RlIGdldHMgZXhhY3RseSBpdHMgYmFzZVxuICAgKiB0eXBlJ3MgdG9vbGJhci4gV2hlbiB1bmJvdW5kLCBhIGJ1aWx0LWluIHNlbGVjdGlvbiBpcyBpdHMgb3duIGJhc2UgYW5kXG4gICAqIGFueXRoaW5nIGVsc2UgaXMgdHJlYXRlZCBhcyB0aGUgZGVmYXVsdCBJbWFnZSB2aWV3LlxuICAgKi9cbiAgQElucHV0KCkgYmFzZVBsb3RUeXBlOiBQbG90VHlwZSB8IG51bGwgPSBudWxsO1xuICAvKiogSXNvc3VyZmFjZSBiYW5kIGFzIGEgMOKAkzI1NSBzbGlkZXIgcG9zaXRpb24gKG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUncyByZWFsXG4gICAqICBpbnRlbnNpdHkgcmFuZ2UgYnkgdGhlIHJlbmRlcmVyKS4gRGVmYXVsdHMgdG8gdGhlIGZ1bGwgcmFuZ2UuICovXG4gIEBJbnB1dCgpIGlzb1JhbmdlOiBudW1iZXJbXSA9IFswLCAyNTVdO1xuICBASW5wdXQoKSBtYXhJbmRleCA9IDA7XG4gIEBJbnB1dCgpIHpJbmRleCA9IDA7XG4gIC8qKiBUcnVlIGZvciBhbnkgMkQgKG5vbiBTdXJmYWNlLTNEKSBwbG90IHR5cGUuICovXG4gIEBJbnB1dCgpIGlzSGVhdG1hcCA9IHRydWU7XG4gIC8qKlxuICAgKiBSZWdpb24gdG9vbHMgYXBwbHkgZXZlbiB0aG91Z2ggdGhpcyBpcyBhIDNEIHZpZXcsIGJlY2F1c2UgdGhleSBkcmF3IGFcbiAgICogU0NSRUVOLVNQQUNFIGxhc3NvICh0aGUgM0Qgc3BhdGlhbCBjbG91ZCkuIFNlcGFyYXRlIGZyb20ge0BsaW5rIGlzSGVhdG1hcH0sXG4gICAqIHdoaWNoIG1lYW5zIFwiMkQgdmlld1wiIGFuZCBhbHNvIGRyaXZlcyB0aGUgem9vbSB0b29scyBhbmQgdGhlIDNEIGNhbWVyYVxuICAgKiBjb250cm9scyDigJQgZmxpcHBpbmcgdGhhdCB3b3VsZCBzaG93IDJEIHBhbi96b29tIGFuZCBoaWRlIHRoZSBvcmJpdCBjb250cm9scy5cbiAgICovXG4gIEBJbnB1dCgpIGlzM2RSZWdpb25zID0gZmFsc2U7XG4gIEBJbnB1dCgpIGFjdGl2ZURyYWdNb2RlOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgQElucHV0KCkgYWN0aXZlU3VyZmFjZTNkTW9kZSA9ICd0dXJudGFibGUnO1xuICAvKiogV2hldGhlciB0aGUgbmFwYXJpIDNEIGF4ZXMvc2NhbGUgZ2l6bW8gaXMgY3VycmVudGx5IHNob3duIChkcml2ZXMgdGhlIHRvZ2dsZSdzIGxvb2spLiAqL1xuICBASW5wdXQoKSBheGVzVmlzaWJsZSA9IHRydWU7XG4gIEBJbnB1dCgpIHdpcmVmcmFtZUFjdGl2ZSA9IGZhbHNlO1xuICAvKiogQWN0aXZlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKDEgPSBGdWxsIOKApiA4ID0g4oWbOyBkZWZhdWx0IMK9KS4gKi9cbiAgQElucHV0KCkgcmVzb2x1dGlvblNjYWxlID0gTkFQQVJJX0RFRkFVTFRfREVDSU1BVEU7XG4gIC8qKiBEZWNpbWF0ZS1mYWN0b3Igb3B0aW9ucyBmb3IgdGhlIFJlc29sdXRpb24gZHJvcGRvd24uICovXG4gIHJlYWRvbmx5IGRlY2ltYXRlT3B0aW9ucyA9IE5BUEFSSV9ERUNJTUFURV9PUFRJT05TO1xuICBASW5wdXQoKSB3YW5kU2Vuc2l0aXZpdHkgPSAyLjA7XG4gIEBJbnB1dCgpIGJydXNoU2l6ZSA9IDQwO1xuICBASW5wdXQoKSB2ZXJ0ZXhFcmFzZXJSYWRpdXMgPSAyMDtcbiAgLyoqIFdoZXRoZXIgYSByZWdpb24gYWN0aW9uIGlzIGF2YWlsYWJsZSB0byB1bmRvIChqaXQtdWkjODUpLiBHcmV5cyBvdXQgdGhlXG4gICAqICBVbmRvIGJ1dHRvbiB3aGVuIGZhbHNlIChub3RoaW5nIGRvbmUgeWV0LCBvciBhbGwgc3RlcHMgYWxyZWFkeSB1bmRvbmUpLiAqL1xuICBASW5wdXQoKSBjYW5VbmRvID0gZmFsc2U7XG4gIC8qKiBXaGV0aGVyIGFuIHVuZG9uZSByZWdpb24gYWN0aW9uIGlzIGF2YWlsYWJsZSB0byByZWRvIChqaXQtdWkjODUpLiBHcmV5c1xuICAgKiAgb3V0IHRoZSBSZWRvIGJ1dHRvbiB3aGVuIGZhbHNlLiAqL1xuICBASW5wdXQoKSBjYW5SZWRvID0gZmFsc2U7XG4gIEBJbnB1dCgpIHN0YWNrT3B0aW9uczogeyBuYW1lOiBzdHJpbmc7IHZhbDogc3RyaW5nIH1bXSA9XG4gICAgW3sgbmFtZTogJ1NpbmdsZSBpbWFnZScsIHZhbDogJ2ZhbHNlJyB9LCB7IG5hbWU6ICdTdGFjaycsIHZhbDogJ3RydWUnIH1dO1xuICAvKiogV2hpY2ggdG9vbGJhciBncm91cHMgdG8gc2hvdy4gRGVmYXVsdHMgdG8gdGhlIGZ1bGwgdG9vbGJhcjsgdGhlIGhvc3QgZm9yd2FyZHNcbiAgICogIHRoZSBjb25zdW1lcidzIGNob2ljZSAoZS5nLiB0aGUgcGlwZWxpbmUgc2hvd3Mgb25seSB6b29tICsgcmVnaW9uIHRvb2xzKS4gKi9cbiAgQElucHV0KCkgdG9vbHM6IFJlcXVpcmVkPFRvb2xiYXJUb29sVmlzaWJpbGl0eT4gPSBBTExfVE9PTEJBUl9UT09MUztcbiAgLyoqIEN1cnJlbnQgaW1hZ2Utc21vb3RoaW5nIHN0YXRlIChkcml2ZXMgdGhlIFNtb290aGVuIHRvZ2dsZSBidXR0b24gbG9vaykuXG4gICAqICBgZmFsc2VgID0gbmVhcmVzdC1uZWlnaGJvdXIgKGNyaXNwIHJhdyBwaXhlbHMpLCB0aGUgZGVmYXVsdC4gKi9cbiAgQElucHV0KCkgaW1hZ2VTbW9vdGhpbmdFbmFibGVkID0gZmFsc2U7XG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIG9wdGlvbnMgKyBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzkwIFAxKS4gKi9cbiAgQElucHV0KCkgc2FtTW9kZWxzOiB7IGlkOiBzdHJpbmc7IGxhYmVsOiBzdHJpbmcgfVtdID0gW107XG4gIEBJbnB1dCgpIHNhbU1vZGVsSWQgPSAnJztcbiAgLyoqXG4gICAqIFRvb2xzIGNvbnRyaWJ1dGVkIHRocm91Z2gge0BsaW5rIFRPT0xCQVJfVE9PTFN9LCBhbHJlYWR5IGZpbHRlcmVkIHRvIHRoZVxuICAgKiBvbmVzIHdpdGggbW9kZWxzIGFuZCBzb3J0ZWQuIFRoZSBob3N0IHJlc29sdmVzIHRoZW0gc28gaXQgY2FuIG93biB0aGVcbiAgICogcGVyLXRvb2wgcGFyYW1ldGVyIHN0YXRlOyB0aGlzIGNvbXBvbmVudCBvbmx5IGRyYXdzIHRoZW0uXG4gICAqL1xuICBASW5wdXQoKSBjb250cmlidXRlZFRvb2xzOiBUb29sYmFyVG9vbENvbnRyaWJ1dGlvbltdID0gW107XG4gIC8qKiBEaWFsb2cgdG9vbHMgKFRPT0xCQVJfVE9PTFMsIGtpbmQ6ICdkaWFsb2cnKSwgc29ydGVkOyBzaG93biBpbiB0aGUgSW1hZ2Ugdmlldy4gKi9cbiAgQElucHV0KCkgZGlhbG9nVG9vbHM6IFRvb2xiYXJEaWFsb2dUb29sQ29udHJpYnV0aW9uW10gPSBbXTtcbiAgLyoqIElkIG9mIHRoZSBkaWFsb2cgdG9vbCB3aG9zZSBkaWFsb2cgaXMgb3Blbiwgb3IgbnVsbC4gKi9cbiAgQElucHV0KCkgb3BlbkRpYWxvZ1Rvb2xJZDogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBBY3RpdmUgY2hlY2twb2ludCBwZXIgY29udHJpYnV0ZWQgdG9vbCwga2V5ZWQgYnkgdG9vbCBpZC4gKi9cbiAgQElucHV0KCkgdG9vbE1vZGVsSWRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgQE91dHB1dCgpIHNlbGVjdFBsb3RUeXBlID0gbmV3IEV2ZW50RW1pdHRlcjxQbG90VHlwZUlkPigpO1xuICAvKiogSW50ZW5zaXR5IChMSU5FKSBtb2RlOiBhZGQgYW5vdGhlciBjb2xvcmVkIGxpbmUgUk9JICsgaW5zZXQgdHJhY2UuICovXG4gIEBPdXRwdXQoKSBhZGRQcm9maWxlTGluZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFRvZ2dsZSBpbWFnZSBzbW9vdGhpbmcgKGJpbGluZWFyKSB2cyBuZWFyZXN0LW5laWdoYm91ciAoY3Jpc3AgcGl4ZWxzKS4gKi9cbiAgQE91dHB1dCgpIHRvZ2dsZUltYWdlU21vb3RoaW5nID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvLyBQcmltZU5HIHNsaWRlci9pbnB1dE51bWJlciBjaGFuZ2UgZXZlbnRzIGNhcnJ5IG9wdGlvbmFsIHZhbHVlcywgbWlycm9yZWQgYnlcbiAgLy8gdGhlIGhvc3QgaGFuZGxlcnMgKHdoaWNoIGFjY2VwdCBgfCB1bmRlZmluZWRgKS5cbiAgQE91dHB1dCgpIGlzb1JhbmdlQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXJbXSB8IHVuZGVmaW5lZD4oKTtcbiAgLyoqIE9wZW4gdGhlIENoYW5uZWxzICYgSGlzdG9ncmFtIGRpYWxvZyAoYnJpZ2h0bmVzcy9jb250cmFzdCwgY29sb3JtYXAsIOKApikuICovXG4gIEBPdXRwdXQoKSBvcGVuQ2hhbm5lbEhpc3RvZ3JhbSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIE9wZW4gdGhlIHNwYXRpYWwtb21pY3MgY29udHJvbHMgKGNvbG91ci1ieSwgbGVnZW5kLCBwb2ludCBkaXNwbGF5KS4gKi9cbiAgQE91dHB1dCgpIG9wZW5TcGF0aWFsQ29udHJvbHMgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSBvcGVuUmVnaW9uRWRpdG9yID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgc2VsZWN0U3RhY2tPcHRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHsgbmFtZTogc3RyaW5nOyB2YWw6IHN0cmluZyB9PigpO1xuICBAT3V0cHV0KCkgelNjcnViID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIEBPdXRwdXQoKSB6U2xpZGUgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcbiAgQE91dHB1dCgpIHpJbmRleElucHV0ID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG4gIEBPdXRwdXQoKSByZWxvYWRBbmRQbG90ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgZG93bmxvYWRJbWFnZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIGF1dG9zY2FsZUltYWdlID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICBAT3V0cHV0KCkgdG9nZ2xlRHJhZ01vZGUgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgQE91dHB1dCgpIHpvb21JbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgQE91dHB1dCgpIHpvb21PdXQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIEBPdXRwdXQoKSB0b2dnbGVTdXJmYWNlM2RNb2RlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIEBPdXRwdXQoKSByZXNldFN1cmZhY2VDYW1lcmEgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBUb2dnbGUgdGhlIG5hcGFyaSAzRCBjb29yZGluYXRlLWF4ZXMgLyBzY2FsZSBnaXptbyBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVBeGVzID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogVG9nZ2xlIHRoZSBuYXBhcmkgc3VyZmFjZSB3aXJlZnJhbWUgKGVkZ2VzKSBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVXaXJlZnJhbWUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBDaGFuZ2UgdGhlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKHJlbG9hZHMgYXQgdGhlIG5ldyByZXNvbHV0aW9uKS4gKi9cbiAgQE91dHB1dCgpIHNlbGVjdFJlc29sdXRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlcj4oKTtcbiAgQE91dHB1dCgpIGRlbGV0ZVJlZ2lvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFVuZG8gdGhlIGxhc3QgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gKi9cbiAgQE91dHB1dCgpIHVuZG9SZWdpb24gPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBSZWRvIHRoZSBsYXN0IHVuZG9uZSByZWdpb24gYWN0aW9uIChqaXQtdWkjODUpLiAqL1xuICBAT3V0cHV0KCkgcmVkb1JlZ2lvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biBib3gtcHJvbXB0ZWQgU0FNIHNlZ21lbnRhdGlvbiBvbiB0aGUgZHJhd24gcmVjdGFuZ2xlcyAoaml0LXVpIzkwKS4gKi9cbiAgQE91dHB1dCgpIHNlZ21lbnRSZWdpb25zID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogUnVuIGNlbGxwb3NlLVNBTSAoYXV0bykgaW5zaWRlIGVhY2ggZHJhd24gcmVjdGFuZ2xlJ3MgY3JvcCAoaml0LXVpIzkwKS4gKi9cbiAgQE91dHB1dCgpIHNlZ21lbnRDZWxscG9zZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJ1biBhIGNvbnRyaWJ1dGVkIHRvb2wgb3ZlciB0aGUgY3VycmVudCB2aWV3LCBieSB0b29sIGlkLiAqL1xuICBAT3V0cHV0KCkgcnVuVG9vbCA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuICAvKiogQSBkaWFsb2cgdG9vbCdzIGJ1dHRvbiB3YXMgY2xpY2tlZDogb3BlbiBpdHMgZGlhbG9nLCBvciBjbG9zZSBpdCBpZiBvcGVuLiAqL1xuICBAT3V0cHV0KCkgdG9nZ2xlRGlhbG9nVG9vbCA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuICAvKiogUGljayBhIGNvbnRyaWJ1dGVkIHRvb2wncyBjaGVja3BvaW50LiAqL1xuICBAT3V0cHV0KCkgdG9vbE1vZGVsQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjx7IHRvb2xJZDogc3RyaW5nOyBtb2RlbElkOiBzdHJpbmcgfT4oKTtcbiAgLyoqIE9wZW4gYSBjb250cmlidXRlZCB0b29sJ3MgcGFyYW1ldGVyIGRpYWxvZywgYnkgdG9vbCBpZC4gKi9cbiAgQE91dHB1dCgpIG9wZW5Ub29sUGFyYW1zID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIChqaXQtdWkjOTAgUDEpLiAqL1xuICBAT3V0cHV0KCkgc2FtTW9kZWxDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgQE91dHB1dCgpIHdhbmRTZW5zaXRpdml0eUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyIHwgdW5kZWZpbmVkPigpO1xuICBAT3V0cHV0KCkgYnJ1c2hTaXplQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIEBPdXRwdXQoKSB2ZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcblxuICAvKiogU2xpZGVycycgYm91bmRzIOKAlCB0b29sYmFyIFVJIGNvbnN0YW50cyAodGhlIHZhbHVlcyBhcmUgaG9zdC1vd25lZCkuICovXG4gIHJlYWRvbmx5IHdhbmRTZW5zaXRpdml0eU1pbiA9IDAuNTtcbiAgcmVhZG9ubHkgd2FuZFNlbnNpdGl2aXR5TWF4ID0gMTAuMDtcbiAgcmVhZG9ubHkgd2FuZFNlbnNpdGl2aXR5U3RlcCA9IDAuMTtcbiAgcmVhZG9ubHkgYnJ1c2hTaXplTWluID0gNTtcbiAgcmVhZG9ubHkgYnJ1c2hTaXplTWF4ID0gMzAwO1xuICByZWFkb25seSBicnVzaFNpemVTdGVwID0gNTtcbiAgcmVhZG9ubHkgdmVydGV4RXJhc2VyUmFkaXVzTWluID0gNTtcbiAgcmVhZG9ubHkgdmVydGV4RXJhc2VyUmFkaXVzTWF4ID0gMzAwO1xuICByZWFkb25seSB2ZXJ0ZXhFcmFzZXJSYWRpdXNTdGVwID0gNTtcbiAgcmVhZG9ubHkgaXNvVmFsdWVNaW4gPSAwO1xuICByZWFkb25seSBpc29WYWx1ZU1heCA9IDI1NTtcbiAgcmVhZG9ubHkgaXNvVmFsdWVTdGVwID0gMTtcblxuICBkaXNwbGF5SGVscERpYWxvZyA9IGZhbHNlO1xuXG4gIC8qKiBNb2RlbCBwaWNrZXIgZm9yIHRoZSBTZWdtZW50IGJ1dHRvbidzIGRyb3Bkb3duIG1lbnUuIFRoZSBhY3RpdmUgbW9kZWxcbiAgICogIChgc2FtTW9kZWxJZGApIGlzIG1hcmtlZCB3aXRoIGEgY2hlY2s7IHNlbGVjdGluZyBhbiBpdGVtIGVtaXRzXG4gICAqICBgc2FtTW9kZWxDaGFuZ2VgIChqaXQtdWkjOTAgUDEpLlxuICAgKlxuICAgKiAgSGVsZCBhcyBhIHN0YWJsZSBhcnJheSAocmVidWlsdCBvbmx5IHdoZW4gYHNhbU1vZGVsc2AvYHNhbU1vZGVsSWRgIGNoYW5nZSksXG4gICAqICBOT1QgYSBnZXR0ZXI6IGEgZ2V0dGVyIHJldHVybnMgYSBmcmVzaCBhcnJheSB3aXRoIG5ldyBgY29tbWFuZGAgY2xvc3VyZXMgb25cbiAgICogIGV2ZXJ5IGNoYW5nZS1kZXRlY3Rpb24gdGljaywgd2hpY2ggbWFrZXMgdGhlIGJvdW5kIGBwLW1lbnVgIG92ZXJsYXkgcmVidWlsZFxuICAgKiAgaXRzIERPTSBtaWQtaW50ZXJhY3Rpb24gYW5kIHN3YWxsb3cgdGhlIGNsaWNrIG9uIGEgbWVudSBpdGVtLlxuICAgKlxuICAgKiAgYHRvb2x0aXBgIGNhcnJpZXMgdGhlIG1vZGVsJ3MgZGVzY3JpcHRpb24gZnJvbSB7QGxpbmsgTU9ERUxfSU5GT307IHRoZVxuICAgKiAgbWVudXMnIHNoYXJlZCBpdGVtIHRlbXBsYXRlIHR1cm5zIGl0IGludG8gdGhlIGhvdmVyIGluZm8gaWNvbi4gcC1tZW51J3Mgb3duXG4gICAqICByZW5kZXJpbmcgaWdub3JlcyB0aGUgZmllbGQgKGl0IHJlYWRzIGBpdGVtLnRpdGxlYCksIHNvIGl0IGlzIGZyZWUgdG8gdXNlLiAqL1xuICBzYW1NZW51SXRlbXM6IE1lbnVJdGVtW10gPSBbXTtcbiAgLyoqXG4gICAqIE1vZGVsIG1lbnVzIGZvciB0aGUgY29udHJpYnV0ZWQgdG9vbHMsIGtleWVkIGJ5IHRvb2wgaWQuIFNhbWUgc3RhYmxlLWFycmF5XG4gICAqIHJlYXNvbmluZyBhcyB7QGxpbmsgc2FtTWVudUl0ZW1zfSDigJQgYW5kIGl0IGlzIHdoeSB0aGlzIGlzIGEgbWFwIHJlYnVpbHQgaW5cbiAgICogYG5nT25DaGFuZ2VzYCByYXRoZXIgdGhhbiBhIG1ldGhvZCBjYWxsZWQgZnJvbSB0aGUgdGVtcGxhdGUsIHdoaWNoIHdvdWxkXG4gICAqIGhhbmQgYHAtbWVudWAgYSBuZXcgYXJyYXkgZXZlcnkgdGljay5cbiAgICpcbiAgICogQSBjb250cmlidXRlZCB0b29sIHN1cHBsaWVzIGl0cyBvd24gcGVyLW1vZGVsIGRlc2NyaXB0aW9uLCBzbyB1bmxpa2UgdGhlXG4gICAqIFNBTSBtZW51IHRoZXNlIGRvIG5vdCBjb25zdWx0IHtAbGluayBNT0RFTF9JTkZPfTogdGhpcyBsaWJyYXJ5IGhhcyBub1xuICAgKiBjb3B5IGZvciBjaGVja3BvaW50cyBpdCBkb2VzIG5vdCBrbm93IGFib3V0LlxuICAgKi9cbiAgdG9vbE1lbnVJdGVtczogUmVjb3JkPHN0cmluZywgTWVudUl0ZW1bXT4gPSB7fTtcblxuICAvKiogUmVidWlsZCB0aGUgU0FNIG1vZGVsIG1lbnUgd2hlbiB0aGUgbW9kZWwgbGlzdCBvciBhY3RpdmUgc2VsZWN0aW9uXG4gICAqICBjaGFuZ2VzIChrZWVwcyB0aGUgYXJyYXkgcmVmZXJlbmNlIHN0YWJsZSBhY3Jvc3Mgb3RoZXIgQ0QgdGlja3MpLiAqL1xuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgaWYgKGNoYW5nZXNbJ3NhbU1vZGVscyddIHx8IGNoYW5nZXNbJ3NhbU1vZGVsSWQnXSkge1xuICAgICAgdGhpcy5zYW1NZW51SXRlbXMgPSB0aGlzLnNhbU1vZGVscy5tYXAoKG0pID0+ICh7XG4gICAgICAgIGxhYmVsOiBtLmxhYmVsLFxuICAgICAgICBpY29uOiBtLmlkID09PSB0aGlzLnNhbU1vZGVsSWQgPyAncGkgcGktY2hlY2snIDogJ3BpIHBpLWZ3JyxcbiAgICAgICAgdG9vbHRpcDogTU9ERUxfSU5GT1ttLmlkXSxcbiAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy5zYW1Nb2RlbENoYW5nZS5lbWl0KG0uaWQpLFxuICAgICAgfSkpO1xuICAgIH1cbiAgICBpZiAoY2hhbmdlc1snY29udHJpYnV0ZWRUb29scyddIHx8IGNoYW5nZXNbJ3Rvb2xNb2RlbElkcyddKSB7XG4gICAgICB0aGlzLnRvb2xNZW51SXRlbXMgPSB7fTtcbiAgICAgIGZvciAoY29uc3QgdG9vbCBvZiB0aGlzLmNvbnRyaWJ1dGVkVG9vbHMpIHtcbiAgICAgICAgY29uc3QgYWN0aXZlID0gdGhpcy50b29sTW9kZWxJZHNbdG9vbC5pZF0gPz8gdG9vbC5kZWZhdWx0TW9kZWxJZCgpO1xuICAgICAgICB0aGlzLnRvb2xNZW51SXRlbXNbdG9vbC5pZF0gPSB0b29sLm1vZGVscygpLm1hcCgobSkgPT4gKHtcbiAgICAgICAgICBsYWJlbDogbS5sYWJlbCxcbiAgICAgICAgICBpY29uOiBtLmlkID09PSBhY3RpdmUgPyAncGkgcGktY2hlY2snIDogJ3BpIHBpLWZ3JyxcbiAgICAgICAgICB0b29sdGlwOiBtLmluZm8sXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b29sTW9kZWxDaGFuZ2UuZW1pdCh7IHRvb2xJZDogdG9vbC5pZCwgbW9kZWxJZDogbS5pZCB9KSxcbiAgICAgICAgfSkpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTdHJpcCBtYXJrdXAgZnJvbSBhIG1vZGVsIGRlc2NyaXB0aW9uIHNvIGl0IGNhbiBiZSByZWFkIGFsb3VkLlxuICAgKlxuICAgKiBUaGUgY29weSBpbiBNT0RFTF9JTkZPIGFuZCBpbiBhIGNvbnRyaWJ1dGlvbidzIGBUb29sTW9kZWxPcHRpb24uaW5mb2AgaXNcbiAgICogd3JpdHRlbiBmb3IgYSB2aXN1YWwgdG9vbHRpcCByZW5kZXJlZCB3aXRoIGBbZXNjYXBlXT1cImZhbHNlXCJgLCBzbyBpdCBjYXJyaWVzXG4gICAqIGA8Yj5gIGFuZCBgPGJyPmAuIFBhc3NlZCB0byBgYXJpYS1sYWJlbGAgdmVyYmF0aW0gYSBzY3JlZW4gcmVhZGVyIGFubm91bmNlc1xuICAgKiB0aGUgdGFncywgc28gdGhleSBhcmUgcmVtb3ZlZCBhbmQgYDxicj5gIGJlY29tZXMgYSBzZW50ZW5jZSBicmVhay4gRW50aXRpZXNcbiAgICogdXNlZCBpbiB0aGF0IGNvcHkgKGAmbmJzcDtgLCBgJnRpbWVzO2AsIGAmYW1wO2ApIGFyZSBkZWNvZGVkIGZvciB0aGUgc2FtZVxuICAgKiByZWFzb24uXG4gICAqL1xuICBwbGFpblRleHQoaHRtbDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gKGh0bWwgPz8gJycpXG4gICAgICAucmVwbGFjZSgvPGJyXFxzKlxcLz8+L2dpLCAnLiAnKVxuICAgICAgLnJlcGxhY2UoLzxbXj5dKz4vZywgJycpXG4gICAgICAucmVwbGFjZSgvJm5ic3A7L2csICcgJylcbiAgICAgIC5yZXBsYWNlKC8mdGltZXM7L2csICd4JylcbiAgICAgIC5yZXBsYWNlKC8mYW1wOy9nLCAnJicpXG4gICAgICAucmVwbGFjZSgvXFxzKy9nLCAnICcpXG4gICAgICAudHJpbSgpO1xuICB9XG5cbiAgLyoqIENvbnRyaWJ1dGVkIHRvb2wgbmFtZXMgZm9yIHRoZSBoZWxwIGRpYWxvZydzIHByb21wdGVkL25vLXByb21wdCBjb250cmFzdCxcbiAgICogIGUuZy4gXCJZT0xPLCBSZXRpbmFsIGxheWVyc1wiLiBFbXB0eSB3aGVuIG5vdGhpbmcgaXMgcmVnaXN0ZXJlZCwgd2hpY2ggaXNcbiAgICogIHdoeSB0aGUgc2VudGVuY2UgdGhhdCB1c2VzIGl0IGlzIGl0c2VsZiBjb25kaXRpb25hbC4gKi9cbiAgZ2V0IGNvbnRyaWJ1dGVkVG9vbE5hbWVzKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuY29udHJpYnV0ZWRUb29scy5tYXAoKHQpID0+IHQubGFiZWwpLmpvaW4oJywgJyk7XG4gIH1cblxuICAvKiogS2VlcHMgZWFjaCBjb250cmlidXRlZCB0b29sJ3MgYHAtbWVudWAgb3ZlcmxheSBhbGl2ZSBhY3Jvc3MgQ0QgdGlja3Mg4oCUXG4gICAqICByZS1jcmVhdGluZyBpdCBtaWQtaW50ZXJhY3Rpb24gc3dhbGxvd3MgdGhlIGNsaWNrIG9uIGEgbWVudSBpdGVtLiAqL1xuICB0cmFja1Rvb2xCeUlkKF9pbmRleDogbnVtYmVyLCB0b29sOiB7IGlkOiBzdHJpbmcgfSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRvb2wuaWQ7XG4gIH1cblxuICAvKiogVGhlIGJ1aWx0LWluIHR5cGUgd2hvc2UgdG9vbHMgYW5kIGNvbnRyb2xzIGFwcGx5IHRvIHRoZSBjdXJyZW50IHNlbGVjdGlvbi4gKi9cbiAgZ2V0IGVmZmVjdGl2ZVBsb3RUeXBlKCk6IFBsb3RUeXBlIHtcbiAgICBpZiAodGhpcy5iYXNlUGxvdFR5cGUpIHJldHVybiB0aGlzLmJhc2VQbG90VHlwZTtcbiAgICByZXR1cm4gaXNCdWlsdGluUGxvdFR5cGUodGhpcy5zZWxlY3RlZFBsb3RUeXBlKSA/IHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA6IFBsb3RUeXBlLklNQUdFO1xuICB9XG5cbiAgLyoqIFRoZSBJbWFnZSBwbG90IHR5cGUgcmVuZGVycyBhcyBhIG5hdGl2ZWx5IHBhbi96b29tLWFibGUgcmFzdGVyLCBzbyB0aGVcbiAgICogIGdlbmVyaWMgem9vbS9wYW4gdG9vbHMgYXJlIGhpZGRlbi4gKi9cbiAgZ2V0IGlzSW1hZ2VWaWV3KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRTtcbiAgfVxuXG4gIC8qKiBFaXRoZXIgc3BhdGlhbC1vbWljcyBtb2RlIGlzIGFjdGl2ZSwgc28gaXRzIGNvbnRyb2xzIGFyZSB3b3J0aCBvZmZlcmluZy5cbiAgICogIFRoZSAzRCBjbG91ZCBuZWVkcyB0aGUgcGFuZWwgTU9SRSB0aGFuIHRoZSAyRCB2aWV3IGRvZXMsIG5vdCBsZXNzOiBjb2xvdXJpbmcsXG4gICAqICB0aGUgbGVnZW5kIGFuZCBjYXRlZ29yeSBzaG93L2hpZGUgYXJlIHRoZSBvbmx5IHdheSB0byBtYWtlIHNlbnNlIG9mIGFcbiAgICogIG1pbGxpb24gb3ZlcmxhcHBpbmcgcG9pbnRzLiAqL1xuICBnZXQgaXNTcGF0aWFsTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gaXNTcGF0aWFsT21pY3ModGhpcy5lZmZlY3RpdmVQbG90VHlwZSkgfHwgaXNTcGF0aWFsT21pY3MzZCh0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBBIHBsb3QtdHlwZSBpY29uIGlzIGEgUHJpbWVORyBmb250IGdseXBoIChlLmcuIGBwaSBwaS1pbWFnZWApIHJhdGhlciB0aGFuIGFuXG4gICAqICBTVkcgYXNzZXQgcGF0aCDigJQgZHJpdmVzIHdoaWNoIGVsZW1lbnQgdGhlIHNlbGVjdG9yIGl0ZW0gdGVtcGxhdGUgcmVuZGVycy4gKi9cbiAgaXNQaUljb24oaWNvbjogc3RyaW5nIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICEhaWNvbiAmJiBpY29uLnN0YXJ0c1dpdGgoJ3BpICcpO1xuICB9XG5cbiAgLyoqIEJhY2tlbmRzIHdpdGggYSB2ZXJ0ZXgtZWRpdGluZyByZWdpb24gb3ZlcmxheTogT1NEIEltYWdlIGFuZCBuYXBhcmktanMgV2ViR1BVIGltYWdlXG4gICAqICAoaml0LXVpIzEwMikuIEdhdGVzIHRoZSBwb2x5Z29uIC8gYWRkLXZlcnRleCAvIGRlbGV0ZS12ZXJ0ZXggLyBCw6l6aWVyLWNvbnZlcnQgdG9vbHMuICovXG4gIGdldCBzdXBwb3J0c1JlZ2lvblZlcnRleFRvb2xzKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRSB8fFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBQbG90IHR5cGVzIHRoYXQgc2NydWIgYSB6LXN0YWNrIGxpdmUgKHRoZSByZW5kZXJlciBzd2FwcyB0aGUgc2xpY2UgaW4gcGxhY2UpOiB0aGUgT1NEIEltYWdlXG4gICAqICB2aWV3LCB0aGUgbmFwYXJpLWpzIFdlYkdQVSBpbWFnZSwgdGhlIG5hcGFyaS1qcyBzdXJmYWNlIOKAlCB3aGljaCByZWJ1aWxkcyB0aGUgaGVpZ2h0IGZpZWxkXG4gICAqICBmcm9tIHRoZSBwaWNrZWQgc2xpY2UgKGppdC11aSMxMDIpIOKAlCBhbmQgdGhlIDJEIHNwYXRpYWwtb21pY3Mgdmlldywgd2hlcmUgYSAzRCBkYXRhc2V0J3NcbiAgICogIHJlZ2lzdGVyZWQgdm9sdW1lIGlzIHRoZSBpbWFnZSBhbmQgdGhlIHNsaWRlciBwaWNrcyB0aGUgc2VjdGlvbiB3aG9zZSBvYnNlcnZhdGlvbnMgYXJlXG4gICAqICBkcmF3bi4gRHJpdmVzIHRoZSBwZXItc2xpY2Ugc2xpZGVyLiAqL1xuICBnZXQgc2hvd3NMaXZlU2xpY2VTY3J1YmJlcigpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0UgfHxcbiAgICAgIHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLk5BUEFSSV9JTUFHRSB8fFxuICAgICAgaXNOYXBhcmlTdXJmYWNlKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpIHx8XG4gICAgICBpc05hcGFyaVNjYXR0ZXIodGhpcy5lZmZlY3RpdmVQbG90VHlwZSkgfHxcbiAgICAgIGlzU3BhdGlhbE9taWNzKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBBbnkgbmFwYXJpLWpzIFdlYkdQVSBwbG90IHR5cGUuIFRoZSBzaW5nbGUtaW1hZ2Uvc3RhY2sgdG9nZ2xlICsgc2xpY2UtbnVtYmVyIGZpZWxkIGFyZVxuICAgKiAgUGxvdGx5LW9ubHkgc3RhY2sgY29udHJvbHMsIHNvIHRoZXkncmUgaGlkZGVuIGZvciBuYXBhcmkgKGppdC11aSMxMDIpLiAqL1xuICBnZXQgaXNOYXBhcmlNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5OQVBBUklfSU1BR0UgfHxcbiAgICAgIGlzTmFwYXJpU2NhdHRlcih0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKSB8fFxuICAgICAgaXNOYXBhcmkzZCh0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKVxuICAgICk7XG4gIH1cblxuICAvKiogSVNPU1VSRkFDRSAoUGxvdGx5IG9yIG5hcGFyaS1qcyBXZWJHUFUsIGVpdGhlciByZXNvbHV0aW9uKSBzaG93cyB0aGUgaXNvdmFsdWUgcmFuZ2Ugc2xpZGVyLiAqL1xuICBnZXQgaXNJc29zdXJmYWNlTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSVNPU1VSRkFDRSB8fCBpc05hcGFyaUlzb3N1cmZhY2UodGhpcy5lZmZlY3RpdmVQbG90VHlwZSlcbiAgICApO1xuICB9XG5cbiAgLyoqIFRoZSBuYXBhcmktanMgV2ViR1BVIHN1cmZhY2Ug4oCUIHNob3dzIHRoZSB3aXJlZnJhbWUgdG9nZ2xlLiAqL1xuICBnZXQgaXNOYXBhcmlTdXJmYWNlTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gaXNOYXBhcmlTdXJmYWNlKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpO1xuICB9XG5cbiAgLyoqIEFueSBuYXBhcmktanMgV2ViR1BVIDNEIHR5cGUgKHZvbHVtZS9pc29zdXJmYWNlL3N1cmZhY2UpIOKAlCBzaG93cyB0aGUgUmVzb2x1dGlvbiBjb250cm9sLiAqL1xuICBnZXQgaXNOYXBhcmkzZE1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGlzTmFwYXJpM2QodGhpcy5lZmZlY3RpdmVQbG90VHlwZSk7XG4gIH1cblxuICAvKiogSW50ZW5zaXR5IHByb2ZpbGUgbGluZXMgYXJlIFJlZ2lvbi1iYXNlZCBhbmQgYXZhaWxhYmxlIGluIHRoZSBIZWF0bWFwIGFuZFxuICAgKiAgSW1hZ2UgcGxvdCB0eXBlcywgd2hpY2ggc2hvdyB0aGUgSW50ZW5zaXR5IHRvb2wgZ3JvdXAuICovXG4gIGdldCBpc0ludGVuc2l0eUNhcGFibGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLkhFQVRNQVAgfHwgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gIH1cblxuICBzaG93SGVscCgpOiB2b2lkIHtcbiAgICB0aGlzLmRpc3BsYXlIZWxwRGlhbG9nID0gdHJ1ZTtcbiAgfVxufVxuIiwiPHAtdG9vbGJhcj5cbiAgPGRpdiBjbGFzcz1cInAtdG9vbGJhci1ncm91cC1zdGFydCBmbGV4LXdyYXAgYWxpZ24taXRlbXMtY2VudGVyIGdhcC0xXCI+XG5cbiAgICA8IS0tIEhvc3QtcHJvdmlkZWQgY3VzdG9tIHRvb2xiYXIgYnV0dG9ucywgc2hvd24gZmlyc3QgKGUuZy4gYXBwLXNwZWNpZmljXG4gICAgICAgICBhY3Rpb25zIHRoZSB2aXN1YWxpemF0aW9uIHZpZXcgaXRzZWxmIGRvZXNuJ3Qgb3duKS4gLS0+XG4gICAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuXG4gICAgPCEtLSBDb250cmlidXRlZCBkaWFsb2cgdG9vbHMgKFRPT0xCQVJfVE9PTFMsIGtpbmQ6ICdkaWFsb2cnKTogYXBwLWxldmVsIGFjdGlvbnNcbiAgICAgICAgIGxpa2UgdGhlIGhvc3QncyBvd24gYnV0dG9ucyBhYm92ZSwgc28gdGhleSBzaXQgcmlnaHQgYmVzaWRlIHRoZW0uIEltYWdlIHZpZXdcbiAgICAgICAgIG9ubHksIHRoZSB2aWV3IHRoZWlyIGRpYWxvZyBkcmF3cyBvdmVyLiBIaWdobGlnaHRlZCB3aGlsZSB0aGUgZGlhbG9nIGlzIG9wZW4uIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc0ltYWdlVmlld1wiPlxuICAgICAgPHAtYnV0dG9uICpuZ0Zvcj1cImxldCB0b29sIG9mIGRpYWxvZ1Rvb2xzOyB0cmFja0J5OiB0cmFja1Rvb2xCeUlkXCJcbiAgICAgICAgICAgICAgICBzaXplPVwic21hbGxcIiBbdGV4dF09XCJvcGVuRGlhbG9nVG9vbElkICE9PSB0b29sLmlkXCJcbiAgICAgICAgICAgICAgICBbcFRvb2x0aXBdPVwidG9vbC50b29sdGlwXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICBbYXJpYUxhYmVsXT1cInRvb2wubGFiZWxcIlxuICAgICAgICAgICAgICAgIFthdHRyLmFyaWEtcHJlc3NlZF09XCJvcGVuRGlhbG9nVG9vbElkID09PSB0b29sLmlkXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEaWFsb2dUb29sLmVtaXQodG9vbC5pZClcIj5cbiAgICAgICAgPGltZyAqbmdJZj1cInRvb2wuaWNvbi5zcmNcIiBbc3JjXT1cInRvb2wuaWNvbi5zcmNcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIgW2FsdF09XCJ0b29sLmxhYmVsXCJcbiAgICAgICAgICAgICBbY2xhc3NdPVwib3BlbkRpYWxvZ1Rvb2xJZCA9PT0gdG9vbC5pZCA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIiAvPlxuICAgICAgICA8aSAqbmdJZj1cInRvb2wuaWNvbi5waVwiIGNsYXNzPVwicGkge3sgdG9vbC5pY29uLnBpIH19XCI+PC9pPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDwhLS0gUGxvdCB0eXBlOiBhdmFpbGFibGUgZm9yIGFueSBpbWFnZSAoZ3JheXNjYWxlIG9yIFJHQiksIGFuZCBmb3IgYSBzcGF0aWFsIGRhdGFzZXRcbiAgICAgICAgIHRoYXQgYnJpbmdzIG5vbmUgKGl0cyBzcGF0aWFsIG1vZGVzIGFyZSBzdGlsbCBhIGNob2ljZSkuIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJzaG93UGxvdFR5cGVzXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuICAgICAgPHAtZHJvcGRvd24gW29wdGlvbnNdPVwicGxvdFR5cGVPcHRpb25zXCIgb3B0aW9uTGFiZWw9XCJsYWJlbFwiIG9wdGlvblZhbHVlPVwidHlwZVwiXG4gICAgICAgICAgICAgICAgICBbKG5nTW9kZWwpXT1cInNlbGVjdGVkUGxvdFR5cGVcIiBhcHBlbmRUbz1cImJvZHlcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTZWxlY3QgcGxvdCB0eXBlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIFtzdHlsZV09XCJ7J21pbi13aWR0aCc6ICcxMXJlbSd9XCJcbiAgICAgICAgICAgICAgICAgIHN0eWxlQ2xhc3M9XCJwbG90bW9kZS1kcm9wZG93blwiIHBhbmVsU3R5bGVDbGFzcz1cInBsb3Rtb2RlLXBhbmVsXCJcbiAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJzZWxlY3RQbG90VHlwZS5lbWl0KCRldmVudC52YWx1ZSlcIj5cbiAgICAgICAgPG5nLXRlbXBsYXRlIGxldC1vcHQgcFRlbXBsYXRlPVwic2VsZWN0ZWRJdGVtXCI+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJwbG90bW9kZS1pdGVtXCIgKm5nSWY9XCJvcHRcIj5cbiAgICAgICAgICAgIDxpICpuZ0lmPVwiaXNQaUljb24ob3B0Lmljb24pXCIgW2NsYXNzXT1cIm9wdC5pY29uXCI+PC9pPlxuICAgICAgICAgICAgPGltZyAqbmdJZj1cIm9wdC5pY29uICYmICFpc1BpSWNvbihvcHQuaWNvbilcIiBjbGFzcz1cInBsb3Rtb2RlLWljb24taW1nXCIgW3NyY109XCJvcHQuaWNvblwiIGFsdD1cIlwiIC8+XG4gICAgICAgICAgICA8c3Bhbj57eyBvcHQubGFiZWwgfX08L3NwYW4+XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICA8bmctdGVtcGxhdGUgbGV0LW9wdCBwVGVtcGxhdGU9XCJpdGVtXCI+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJwbG90bW9kZS1pdGVtXCI+XG4gICAgICAgICAgICA8aSAqbmdJZj1cImlzUGlJY29uKG9wdC5pY29uKVwiIFtjbGFzc109XCJvcHQuaWNvblwiPjwvaT5cbiAgICAgICAgICAgIDxpbWcgKm5nSWY9XCJvcHQuaWNvbiAmJiAhaXNQaUljb24ob3B0Lmljb24pXCIgY2xhc3M9XCJwbG90bW9kZS1pY29uLWltZ1wiIFtzcmNdPVwib3B0Lmljb25cIiBhbHQ9XCJcIiAvPlxuICAgICAgICAgICAgPHNwYW4+e3sgb3B0LmxhYmVsIH19PC9zcGFuPlxuICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgIDwvcC1kcm9wZG93bj5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc0lzb3N1cmZhY2VNb2RlXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5Jc288L3NwYW4+XG4gICAgICAgIDxwLXNsaWRlciBbKG5nTW9kZWwpXT1cImlzb1JhbmdlXCIgW3JhbmdlXT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgW21pbl09XCJpc29WYWx1ZU1pblwiIFttYXhdPVwiaXNvVmFsdWVNYXhcIiBbc3RlcF09XCJpc29WYWx1ZVN0ZXBcIlxuICAgICAgICAgICAgICAgICAgKG9uQ2hhbmdlKT1cImlzb1JhbmdlQ2hhbmdlLmVtaXQoJGV2ZW50LnZhbHVlcylcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJJc29zdXJmYWNlIGJhbmQgKDDigJMyNTUpLCBtYXBwZWQgb250byB0aGUgdm9sdW1lJ3NcbiAgICAgICAgICAgICAgICAgIGFjdHVhbCBpbnRlbnNpdHkgcmFuZ2UuIFN1cmZhY2VzIGFyZSBkcmF3biBmb3Igdm94ZWxzIHdob3NlXG4gICAgICAgICAgICAgICAgICBpbnRlbnNpdHkgZmFsbHMgaW4gdGhlIHNlbGVjdGVkIGJhbmQg4oCUIG5hcnJvdyBpdCB0byBpc29sYXRlXG4gICAgICAgICAgICAgICAgICBmZWF0dXJlcywgd2lkZW4gaXQgdG8gc2VlIHRoZSB3aG9sZSBzdHJ1Y3R1cmUuXCJcbiAgICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgc3R5bGVDbGFzcz1cIndhbmQtc2Vuc2l0aXZpdHktc2xpZGVyXCI+PC9wLXNsaWRlcj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dCB3YW5kLXNlbnNpdGl2aXR5LXZhbHVlXCI+XG4gICAgICAgICAge3sgaXNvUmFuZ2VbMF0gfX3igJN7eyBpc29SYW5nZVsxXSB9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPCEtLSBJbnRlbnNpdHkgdG9vbHMgKEhlYXRtYXAgLyBJbWFnZSBtb2RlKTogYWRkIGFub3RoZXIgY29sb3JlZCBsaW5lIFJPSS5cbiAgICAgICAgICAgRWFjaCBhZGRlZCBsaW5lIGdldHMgdGhlIG5leHQgYnJpZ2h0IHBhbGV0dGUgY29sb3VyIGFuZCBpdHMgb3duIGluc2V0XG4gICAgICAgICAgIHRyYWNlOyBzZWxlY3QgYSBsaW5lIG9uIHRoZSBpbWFnZSBhbmQgdXNlIHRoZSB0cmFzaCB0b29sIHRvIHJlbW92ZSBpdFxuICAgICAgICAgICAoYW5kIGl0cyB0cmFjZSkuIFRoZSBsaW5lcyBhcmUgUmVnaW9uLWJhc2VkIGJ1dCBleGNsdWRlZCBmcm9tIHRoZVxuICAgICAgICAgICBSZWdpb24gRWRpdG9yIGFuZCBleHBvcnRzLiAtLT5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc0ludGVuc2l0eUNhcGFibGVcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJBZGQgYW4gaW50ZW5zaXR5IGxpbmUgKG5ldyBjb2xvdXIpLiBDbGljayBhIGxpbmUgdG8gc2VsZWN0IGl0LFxuICAgICAgICAgICAgICAgICAgdGhlbiB1c2UgdGhlIHRyYXNoIHRvb2wgdG8gcmVtb3ZlIGl0LlwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cImFkZFByb2ZpbGVMaW5lLmVtaXQoKVwiPlxuICAgICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NwbGluZS1jaGFydC5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwhLS0gQ2hhbm5lbHMgJiBIaXN0b2dyYW06IGJyaWdodG5lc3MvY29udHJhc3QsIGdhbW1hLCBwZXItY2hhbm5lbCBkaXNwbGF5LFxuICAgICAgICAgY29sb3JtYXAvcmV2ZXJzZS9pbnZlcnQg4oCUIG9wZW5zIHRoZSBmbG9hdGluZyBkaWFsb2cuIC0tPlxuICAgIDxwLWJ1dHRvbiAqbmdJZj1cInRvb2xzLnNwZWNpYWxUb29sc1wiIHBSaXBwbGUgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgIHBUb29sdGlwPVwiQ2hhbm5lbHMgJmFtcDsgSGlzdG9ncmFtIChicmlnaHRuZXNzL2NvbnRyYXN0LCBjb2xvcm1hcClcIlxuICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAob25DbGljayk9XCJvcGVuQ2hhbm5lbEhpc3RvZ3JhbS5lbWl0KClcIj5cbiAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL2ludmVydC5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICA8L3AtYnV0dG9uPlxuXG4gICAgPCEtLSBTcGF0aWFsLW9taWNzIGNvbnRyb2xzLiBPbmx5IG1lYW5pbmdmdWwgaW4gdGhlIHNwYXRpYWwgbW9kZSwgd2hpY2ggaXNcbiAgICAgICAgIGl0c2VsZiBvbmx5IG9mZmVyZWQgd2hpbGUgYSBkYXRhc2V0IGlzIGxvYWRlZCDigJQgc28gdGhpcyBidXR0b24gYXBwZWFyc1xuICAgICAgICAgZXhhY3RseSB3aGVuIHRoZXJlIGlzIHNvbWV0aGluZyBmb3IgaXQgdG8gY29udHJvbC4gLS0+XG4gICAgPHAtYnV0dG9uICpuZ0lmPVwidG9vbHMuc3BlY2lhbFRvb2xzICYmIGlzU3BhdGlhbE1vZGVcIiBwUmlwcGxlIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNwYXRpYWwgb21pY3MgKGNvbG91ciBieSBjb2x1bW4gb3IgZ2VuZSwgbGVnZW5kLCBzZWxlY3Rpb24sIGRpc3RyaWJ1dGlvbnMpXCJcbiAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwib3BlblNwYXRpYWxDb250cm9scy5lbWl0KClcIj5cbiAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NwYXRpYWwtb21pY3Muc3ZnXCIgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgPC9wLWJ1dHRvbj5cblxuICAgIDwhLS0gU21vb3RoZW4gdG9nZ2xlOiBiaWxpbmVhciBzbW9vdGhpbmcgdnMgbmVhcmVzdC1uZWlnaGJvdXIgcmF3IHBpeGVscyAoSW1hZ2VcbiAgICAgICAgIHZpZXcgb25seSDigJQgaXQncyBhbiBPcGVuU2VhZHJhZ29uIGRyYXdlciBzZXR0aW5nKS4gSGlnaGxpZ2h0ZWQgd2hlbiBzbW9vdGhpbmcgb24uIC0tPlxuICAgIDxwLWJ1dHRvbiAqbmdJZj1cInRvb2xzLnNwZWNpYWxUb29scyAmJiBpc0ltYWdlVmlld1wiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgIFt0ZXh0XT1cIiFpbWFnZVNtb290aGluZ0VuYWJsZWRcIlxuICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNtb290aGVuICh0dXJuIG9uIGltYWdlIHNtb290aGluZylcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVJbWFnZVNtb290aGluZy5lbWl0KClcIj5cbiAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3Ntb290aC5zdmdcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCJcbiAgICAgICAgICAgW2NsYXNzXT1cImltYWdlU21vb3RoaW5nRW5hYmxlZCA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIi8+XG4gICAgPC9wLWJ1dHRvbj5cblxuICAgIDwhLS0gU3RhY2sgY29udHJvbHMgLS0+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImltYWdlSW5mbz8uaXNTdGFja1wiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDwhLS0gSW1hZ2Ugdmlldzogc2NydWIgei1zbGljZXMgbGl2ZSAodGhlIHJlbmRlcmVyIHN3YXBzIHNsaWNlcyBpbiBwbGFjZSkuIC0tPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cInNob3dzTGl2ZVNsaWNlU2NydWJiZXJcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPlNsaWNlPC9zcGFuPlxuICAgICAgICA8cC1zbGlkZXIgWyhuZ01vZGVsKV09XCJ6SW5kZXhcIiBbbWluXT1cIjBcIiBbbWF4XT1cIm1heEluZGV4XCJcbiAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJ6U2NydWIuZW1pdCgkZXZlbnQudmFsdWUpXCIgKG9uU2xpZGVFbmQpPVwielNsaWRlLmVtaXQoJGV2ZW50LnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNsaWNlIHRocm91Z2ggdGhlIHN0YWNrICjihpAgLyDihpIga2V5cylcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgW3N0eWxlXT1cInsnd2lkdGgnOicxNnJlbSd9XCIgc3R5bGVDbGFzcz1cIndhbmQtc2Vuc2l0aXZpdHktc2xpZGVyXCI+PC9wLXNsaWRlcj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPnt7ekluZGV4fX0gb2Yge3ttYXhJbmRleH19PC9zcGFuPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICA8IS0tIEhlYXRtYXAgdmlldzogc2luZ2xlLWltYWdlIChoaWdoLWRlZiB6b29tKSB2cyBzdGFjayAoZnJhbWUgc2xpZGVyKS4gUGxvdGx5LW9ubHkg4oCUXG4gICAgICAgICAgIGhpZGRlbiBmb3IgbmFwYXJpLCB3aGljaCBoYXMgbm8gc2luZ2xlL3N0YWNrIHRvZ2dsZSBvciBwZXItZnJhbWUgZmV0Y2gsIGFuZCBmb3IgdGhlXG4gICAgICAgICAgIHNwYXRpYWwtb21pY3MgbW9kZXM6IGEgM0Qgb21pY3MgZGF0YXNldCBwdWJsaXNoZXMgaXRzIHJlZmVyZW5jZSB2b2x1bWUgYXMgYSB6LXN0YWNrXG4gICAgICAgICAgIGltYWdlLCBhbmQgdGhlc2UgbW9kZXMgZHJhdyBvYnNlcnZhdGlvbnMsIG5vdCBvbmUgb2YgaXRzIHNsaWNlcy4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiIXNob3dzTGl2ZVNsaWNlU2NydWJiZXIgJiYgIWlzTmFwYXJpTW9kZSAmJiAhaXNTcGF0aWFsTW9kZVwiPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1pbWFnZVwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiaW1hZ2VJbmZvPy5zaG93U3RhY2tcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU2luZ2xlIGltYWdlIG1vZGU6IGVuYWJsZXMgaGlnaC1kZWYgem9vbVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2VsZWN0U3RhY2tPcHRpb24uZW1pdChzdGFja09wdGlvbnNbMF0pXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1pbWFnZXNcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cIiFpbWFnZUluZm8/LnNob3dTdGFja1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTdGFjayBtb2RlLCBlbmFibGVzIHNsaWNpbmcgdGhyb3VnaCBpbWFnZSBmcmFtZXNcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNlbGVjdFN0YWNrT3B0aW9uLmVtaXQoc3RhY2tPcHRpb25zWzFdKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIhaW1hZ2VJbmZvPy5zaG93U3RhY2tcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBteC0xIHRvb2xiYXItdGV4dFwiPlNsaWNlPC9zcGFuPlxuICAgICAgICA8ZGl2IGNsYXNzPVwicC1pbnB1dGdyb3VwXCIgc3R5bGU9XCJ3aWR0aDogYXV0b1wiPlxuICAgICAgICAgIDxwLWlucHV0TnVtYmVyIHN0eWxlQ2xhc3M9XCJ3LTNyZW0gdG9vbGJhci1pbnB1dFwiXG5cdFx0XHRcdFx0cFRvb2x0aXA9XCJWaWV3IGltYWdlIGluIHN0YWNrICgmI3gyM0NFOylcIlxuXHRcdFx0XHRcdFsobmdNb2RlbCldPVwiekluZGV4XCIgW21pbl09XCIwXCIgW21heF09XCJtYXhJbmRleFwiXG4gICAgICAgICAgIChvbklucHV0KT1cInpJbmRleElucHV0LmVtaXQoekluZGV4KVwiIChrZXlkb3duLmVudGVyKT1cInJlbG9hZEFuZFBsb3QuZW1pdCgpXCIvPlxuICAgICAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktcmVmcmVzaFwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIiAob25DbGljayk9XCJyZWxvYWRBbmRQbG90LmVtaXQoKVwiXG4gICAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUGxvdCB0aGlzIHNsaWNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCI+XG4gICAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5vZiB7e21heEluZGV4fX08L3NwYW4+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8IS0tIEltYWdlIGFjdGlvbnMgKHNwZWNpYWwgdG9vbHMpIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJ0b29scy5zcGVjaWFsVG9vbHNcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWRvd25sb2FkXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEb3dubG9hZCBpbWFnZSBhcyBQTkdcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cImRvd25sb2FkSW1hZ2UuZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uICpuZ0lmPVwiaXNIZWF0bWFwXCIgaWNvbj1cInBpIHBpLXdpbmRvdy1tYXhpbWl6ZVwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRml0IHRvIHZpZXdcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cImF1dG9zY2FsZUltYWdlLmVtaXQoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDwhLS0gWm9vbSB0b29scy4gUGxvdGx5IHJ1YmJlci1iYW5kIGRyYWctem9vbTogb24gT3BlblNlYWRyYWdvbiB0aGUgYm94LXpvb21cbiAgICAgICAgIGNvdmVycyB0aGlzIGFuZCBhIGJhcmUgZHJhZyBwYW5zIG5hdGl2ZWx5LCBzbyBpdCdzIGhpZGRlbiBpbiBpbWFnZSB2aWV3LiAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNIZWF0bWFwICYmIHRvb2xzLnpvb21Ub29sc1wiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDxwLWJ1dHRvbiAqbmdJZj1cIiFpc0ltYWdlVmlld1wiIGljb249XCJwaSBwaS1zZWFyY2hcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnem9vbSdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiWm9vbSBkcmFnXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCd6b29tJylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ3pvb21Ub0JveCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiWm9vbSBib3ggKDxrYmQ+Yjwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCd6b29tVG9Cb3gnKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy96b29tLWJveC5zdmdcIlxuICAgICAgICAgICAgIFtjbGFzc109XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ3pvb21Ub0JveCcgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktYXJyb3dzLWFsdFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdwYW4nXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlBhbiB0aGUgaW1hZ2UgKDxrYmQ+cDwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdwYW4nKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktc2VhcmNoLXBsdXNcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlpvb20gaW4gKDxrYmQ+Kzwva2JkPilcIiBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ6b29tSW4uZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1zZWFyY2gtbWludXNcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlpvb20gb3V0ICg8a2JkPi08L2tiZD4pXCIgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiem9vbU91dC5lbWl0KClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8IS0tIFN1cmZhY2UgM0QgY29udHJvbHMgLS0+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFpc0hlYXRtYXBcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXNlYXJjaFwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlU3VyZmFjZTNkTW9kZSAhPT0gJ3pvb20nXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlpvb21cIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZVN1cmZhY2UzZE1vZGUuZW1pdCgnem9vbScpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1hcnJvd3MtYWx0XCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVTdXJmYWNlM2RNb2RlICE9PSAncGFuJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJQYW5cIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZVN1cmZhY2UzZE1vZGUuZW1pdCgncGFuJylcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWdsb2JlXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVTdXJmYWNlM2RNb2RlICE9PSAnb3JiaXQnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIk9yYml0YWwgcm90YXRpb25cIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZVN1cmZhY2UzZE1vZGUuZW1pdCgnb3JiaXQnKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktc3luY1wiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlU3VyZmFjZTNkTW9kZSAhPT0gJ3R1cm50YWJsZSdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiVHVybnRhYmxlIHJvdGF0aW9uXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVTdXJmYWNlM2RNb2RlLmVtaXQoJ3R1cm50YWJsZScpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1ob21lXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJSZXNldCBjYW1lcmEgdG8gZGVmYXVsdFwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwicmVzZXRTdXJmYWNlQ2FtZXJhLmVtaXQoKVwiPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwhLS0gM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gdG9nZ2xlIChuYXBhcmktanMgV2ViR1BVIHZvbHVtZS9pc29zdXJmYWNlIG9ubHkpLlxuICAgICAgICAgICBgcC1idXR0b24taWNvbi1vbmx5YCBnaXZlcyB0aGUgc2FtZSBzcXVhcmUgc2l6aW5nIGFzIHRoZSBuZWlnaGJvdXJpbmcgcGktaWNvbiBidXR0b25zXG4gICAgICAgICAgICh0aGUgcHJvamVjdGVkIDxpbWc+IGRvZXNuJ3QgdHJpZ2dlciBQcmltZU5HJ3MgYXV0b21hdGljIGljb24tb25seSBzaXppbmcpLiAtLT5cbiAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiAqbmdJZj1cImlzTmFwYXJpTW9kZVwiIHN0eWxlQ2xhc3M9XCJwLWJ1dHRvbi1pY29uLW9ubHlcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cIiFheGVzVmlzaWJsZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJUb2dnbGUgM0QgYXhlcyAvIHNjYWxlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVBeGVzLmVtaXQoKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy8zZC1jb29yZGluYXRlcy5zdmdcIlxuICAgICAgICAgICAgIFtjbGFzc109XCJheGVzVmlzaWJsZSA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPCEtLSBTdXJmYWNlIHdpcmVmcmFtZSB0b2dnbGUgKG5hcGFyaS1qcyBXZWJHUFUgc3VyZmFjZSBvbmx5KS4gLS0+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLXNoYXJlLWFsdFwiIHNpemU9XCJzbWFsbFwiICpuZ0lmPVwiaXNOYXBhcmlTdXJmYWNlTW9kZVwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiIXdpcmVmcmFtZUFjdGl2ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJUb2dnbGUgd2lyZWZyYW1lXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVXaXJlZnJhbWUuZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPCEtLSBSZXNvbHV0aW9uIChkZWNpbWF0ZSBmYWN0b3IpIGZvciB0aGUgbmFwYXJpIDNEIHR5cGVzIOKAlCByZWxvYWRzIGF0IGNvYXJzZXIvZmluZXIgc2FtcGxpbmcuIC0tPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImlzTmFwYXJpM2RNb2RlXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5SZXM8L3NwYW4+XG4gICAgICAgIDxwLWRyb3Bkb3duIFtvcHRpb25zXT1cImRlY2ltYXRlT3B0aW9uc1wiIG9wdGlvbkxhYmVsPVwibGFiZWxcIiBvcHRpb25WYWx1ZT1cInZhbHVlXCJcbiAgICAgICAgICAgICAgICAgICAgW25nTW9kZWxdPVwicmVzb2x1dGlvblNjYWxlXCIgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJSZXNvbHV0aW9uIChkZWNpbWF0ZSBmYWN0b3IpXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgICAgW3N0eWxlXT1cInsnbWluLXdpZHRoJzogJzZyZW0nfVwiXG4gICAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJzZWxlY3RSZXNvbHV0aW9uLmVtaXQoJGV2ZW50LnZhbHVlKVwiPlxuICAgICAgICA8L3AtZHJvcGRvd24+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIoaXNIZWF0bWFwIHx8IGlzM2RSZWdpb25zKSAmJiB0b29scy5yZWdpb25Ub29sc1wiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDwhLS0gUmVnaW9uIEVkaXRvcjogb3BlbnMgdGhlIGVkaXRvciBhcyBhIGZsb2F0aW5nIGRpYWxvZyAoZm9ybWVybHkgdGhlXG4gICAgICAgICAgIHJpZ2h0LXBhbmVsIFJlZ2lvbnMgdGFiKS4gU2l0cyB3aXRoIHRoZSByZWdpb24gZHJhd2luZyB0b29scy4gLS0+XG4gICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJPcGVuIHRoZSBSZWdpb24gRWRpdG9yXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJvcGVuUmVnaW9uRWRpdG9yLmVtaXQoKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9yZWdpb24tZWRpdG9yLnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwhLS0gUmVnaW9uIGRyYXdpbmcgdG9vbHMgLS0+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWFycm93LXVwLXJpZ2h0XCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ3NlbGVjdCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU2VsZWN0aW9uIG1vZGUgKDxrYmQ+czwva2JkPilcIlxuICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ3NlbGVjdCcpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1zdG9wXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2RyYXdyZWN0J1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEcmF3IGEgcmVjdGFuZ3VsYXIgcmVnaW9uICg8a2JkPnI8L2tiZD4pXCIgW2VzY2FwZV09XCJmYWxzZVwiXG4gICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdkcmF3cmVjdCcpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uICpuZ0lmPVwiIWlzM2RSZWdpb25zXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2RyYXdvcGVucGF0aCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRHJhdyBhIHBvbHlsaW5lICg8a2JkPmw8L2tiZD4pXCIgW2VzY2FwZV09XCJmYWxzZVwiXG4gICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdkcmF3b3BlbnBhdGgnKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9wb2x5bGluZS5zdmdcIlxuICAgICAgICAgICAgIFtjbGFzc109XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ2RyYXdvcGVucGF0aCcgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktcGVuY2lsXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ2RyYXdjbG9zZWRwYXRoJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJEcmF3IGEgZnJlZWZvcm0gcmVnaW9uICg8a2JkPmY8L2tiZD4pXCIgW2VzY2FwZV09XCJmYWxzZVwiXG4gICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdkcmF3Y2xvc2VkcGF0aCcpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPCEtLSBCcnVzaDogcGFpbnQgYSByZWdpb24gYnkgZHJhZ2dpbmcgYSBkaXNjIG9mIHRoZSBnaXZlbiBzaXplOyB0aGUgU2l6ZVxuICAgICAgICAgICBzbGlkZXIgYXBwZWFycyB3aGlsZSBpdCdzIGFjdGl2ZS4gSG9sZCBTaGlmdCB0byBlcmFzZSAoc3VidHJhY3QpLiAtLT5cbiAgICAgIDwhLS0gQnJ1c2ggYW5kIHBvbHlsaW5lIGFyZSBhYnNlbnQgZnJvbSBhIDNEIGNsb3VkOiB0aGUgYnJ1c2ggcGFpbnRzIGluIElNQUdFXG4gICAgICAgICAgICAgICAgIHBpeGVscyBhbmQgdGhlcmUgaXMgbm8gcmFzdGVyIHRvIHNhbXBsZSwgYW5kIGFuIG9wZW4gcG9seWxpbmUgaXMgYVxuICAgICAgICAgICAgICAgICBwcm9maWxlIHRvb2wsIG5vdCBhIGNsb3NlZCByZWdpb24gYSBzZWxlY3Rpb24gY2FuIHRlc3QgYWdhaW5zdC4gLS0+XG4gICAgICAgICAgICA8cC1idXR0b24gKm5nSWY9XCIhaXMzZFJlZ2lvbnNcIiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnYnJ1c2gnXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkJydXNoOiBjbGljayBhbmQgZHJhZyB0byBwYWludCBhIHJlZ2lvbi5cbiAgICAgICAgICAgICAgICA8YnI+SG9sZCA8a2JkPlNoaWZ0PC9rYmQ+IHdoaWxlIHBhaW50aW5nIHRvIGVyYXNlIGZyb20gYSByZWdpb24uXCJcbiAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdicnVzaCcpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3BhaW50YnJ1c2guc3ZnXCJcbiAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdicnVzaCcgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ2JydXNoJ1wiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+U2l6ZTwvc3Bhbj5cbiAgICAgICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwiYnJ1c2hTaXplXCJcbiAgICAgICAgICAgICAgICAgIFttaW5dPVwiYnJ1c2hTaXplTWluXCIgW21heF09XCJicnVzaFNpemVNYXhcIiBbc3RlcF09XCJicnVzaFNpemVTdGVwXCJcbiAgICAgICAgICAgICAgICAgIChvbkNoYW5nZSk9XCJicnVzaFNpemVDaGFuZ2UuZW1pdCgkZXZlbnQudmFsdWUpXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiQnJ1c2ggZGlhbWV0ZXIgaW4gaW1hZ2UgcGl4ZWxzLlwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIHN0eWxlQ2xhc3M9XCJ3YW5kLXNlbnNpdGl2aXR5LXNsaWRlclwiPjwvcC1zbGlkZXI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHQgd2FuZC1zZW5zaXRpdml0eS12YWx1ZVwiPlxuICAgICAgICAgIHt7IGJydXNoU2l6ZSB9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICA8IS0tIFZlcnRleCBlZGl0aW5nIOKAlCBiYWNrZW5kcyB3aXRoIGEgdmVydGV4LWVkaXRpbmcgb3ZlcmxheSAoT1NEICsgbmFwYXJpLWpzIGltYWdlKS4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwic3VwcG9ydHNSZWdpb25WZXJ0ZXhUb29sc1wiPlxuICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnZHJhd3BvbHlnb24nXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUG9seWdvbjogY2xpY2sgdG8gcGxhY2UgZWFjaCB2ZXJ0ZXg7IGNsaWNrIHRoZSBmaXJzdCBwb2ludCB0byBjbG9zZVwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2RyYXdwb2x5Z29uJylcIj5cbiAgICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9wb2x5Z29uLXZlcnRpY2VzLnN2Z1wiXG4gICAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdkcmF3cG9seWdvbicgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdhZGRwb2ludCdcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJBZGQgYSB2ZXJ0ZXg6IGNsaWNrIGFuIGVkZ2Ugb2YgdGhlIHNlbGVjdGVkIHBvbHlnb25cIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdhZGRwb2ludCcpXCI+XG4gICAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvdmVydGV4LWFkZC5zdmdcIlxuICAgICAgICAgICAgICAgW2NsYXNzXT1cImFjdGl2ZURyYWdNb2RlID09PSAnYWRkcG9pbnQnID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgICBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgICAgIFt0ZXh0XT1cImFjdGl2ZURyYWdNb2RlICE9PSAnZGVsZXRlcG9pbnQnXCJcbiAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRGVsZXRlIGEgdmVydGV4OiBjbGljayBhIHZlcnRleCBvZiB0aGUgc2VsZWN0ZWQgcG9seWdvblwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ2RlbGV0ZXBvaW50JylcIj5cbiAgICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy92ZXJ0ZXgtZGVsZXRlLnN2Z1wiXG4gICAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdkZWxldGVwb2ludCcgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICd3YW5kJ1wiXG4gICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJXYW5kOiBjbGljayBhbmQgZHJhZyB0byBncm93IGEgcmVnaW9uIGJ5IHNpbWlsYXIgcGl4ZWwgdmFsdWVzICg8a2JkPnc8L2tiZD4pLlxuICAgICAgICAgICAgICAgIDxicj5Ib2xkIDxrYmQ+U2hpZnQ8L2tiZD4gd2hpbGUgY2xpY2tpbmcgb3IgZHJhZ2dpbmcgb24gYSByZWdpb24gdG8gZXJhc2UgcGl4ZWxzIGZyb20gaXQuXCJcbiAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCd3YW5kJylcIj5cbiAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvd2FuZC5zdmdcIlxuICAgICAgICAgICAgIFtjbGFzc109XCJhY3RpdmVEcmFnTW9kZSA9PT0gJ3dhbmQnID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiYWN0aXZlRHJhZ01vZGUgPT09ICd3YW5kJ1wiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0XCI+U2Vucy48L3NwYW4+XG4gICAgICAgIDxwLXNsaWRlciBbKG5nTW9kZWwpXT1cIndhbmRTZW5zaXRpdml0eVwiXG4gICAgICAgICAgICAgICAgICBbbWluXT1cIndhbmRTZW5zaXRpdml0eU1pblwiIFttYXhdPVwid2FuZFNlbnNpdGl2aXR5TWF4XCIgW3N0ZXBdPVwid2FuZFNlbnNpdGl2aXR5U3RlcFwiXG4gICAgICAgICAgICAgICAgICAob25DaGFuZ2UpPVwid2FuZFNlbnNpdGl2aXR5Q2hhbmdlLmVtaXQoJGV2ZW50LnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkhpZ2hlciA9IHN0cmljdGVyIChzbWFsbGVyIHNlbGVjdGlvbik7IGxvd2VyID0gbG9vc2VyLlxuICAgICAgICAgICAgICAgICAgVGFrZXMgZWZmZWN0IGZyb20gdGhlIG5leHQgY2xpY2svZHJhZyB0aWNrLlwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIHN0eWxlQ2xhc3M9XCJ3YW5kLXNlbnNpdGl2aXR5LXNsaWRlclwiPjwvcC1zbGlkZXI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHQgd2FuZC1zZW5zaXRpdml0eS12YWx1ZVwiPlxuICAgICAgICAgIHt7IHdhbmRTZW5zaXRpdml0eS50b0ZpeGVkKDEpIH19PC9zcGFuPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICA8cC1idXR0b24gaWNvbj1cInBpIHBpLWVyYXNlclwiIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgW3RleHRdPVwiYWN0aXZlRHJhZ01vZGUgIT09ICdlcmFzZVZlcnRleCdcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiVmVydGV4IGVyYXNlciAoPGtiZD5lPC9rYmQ+KTogY2xpY2sgb3IgZHJhZyB0byByZW1vdmUgcG9seWdvbiB2ZXJ0aWNlcyBpbnNpZGVcbiAgICAgICAgICAgICAgICB0aGUgY3Vyc29yIGNpcmNsZSBmcm9tIGFueSByZWdpb24uXCJcbiAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ0b2dnbGVEcmFnTW9kZS5lbWl0KCdlcmFzZVZlcnRleCcpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImFjdGl2ZURyYWdNb2RlID09PSAnZXJhc2VWZXJ0ZXgnXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZmxleCBhbGlnbi1pdGVtcy1jZW50ZXIgbXgtMSB0b29sYmFyLXRleHRcIj5SYWRpdXM8L3NwYW4+XG4gICAgICAgIDxwLXNsaWRlciBbKG5nTW9kZWwpXT1cInZlcnRleEVyYXNlclJhZGl1c1wiXG4gICAgICAgICAgICAgICAgICBbbWluXT1cInZlcnRleEVyYXNlclJhZGl1c01pblwiIFttYXhdPVwidmVydGV4RXJhc2VyUmFkaXVzTWF4XCIgW3N0ZXBdPVwidmVydGV4RXJhc2VyUmFkaXVzU3RlcFwiXG4gICAgICAgICAgICAgICAgICAob25DaGFuZ2UpPVwidmVydGV4RXJhc2VyUmFkaXVzQ2hhbmdlLmVtaXQoJGV2ZW50LnZhbHVlKVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkVyYXNlciByYWRpdXMgaW4gaW1hZ2UgcGl4ZWxzLlxuICAgICAgICAgICAgICAgICAgVmVydGljZXMgd2l0aGluIHRoaXMgZGlzdGFuY2Ugb2YgdGhlIGN1cnNvciBhcmUgcmVtb3ZlZC5cIlxuICAgICAgICAgICAgICAgICAgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICBzdHlsZUNsYXNzPVwid2FuZC1zZW5zaXRpdml0eS1zbGlkZXJcIj48L3Atc2xpZGVyPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIG14LTEgdG9vbGJhci10ZXh0IHdhbmQtc2Vuc2l0aXZpdHktdmFsdWVcIj5cbiAgICAgICAgICB7eyB2ZXJ0ZXhFcmFzZXJSYWRpdXMgfX08L3NwYW4+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiIWNhblVuZG9cIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiVW5kbyB0aGUgbGFzdCByZWdpb24gYWN0aW9uICg8a2JkPkN0cmw8L2tiZD4rPGtiZD5aPC9rYmQ+KS5cbiAgICAgICAgICAgICAgICBVcCB0byAxMCBhY3Rpb25zIGNhbiBiZSB1bmRvbmUuXCJcbiAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJ1bmRvUmVnaW9uLmVtaXQoKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy91bmRvLnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiIWNhblJlZG9cIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiUmVkbyB0aGUgbGFzdCB1bmRvbmUgcmVnaW9uIGFjdGlvblxuICAgICAgICAgICAgICAgICg8a2JkPkN0cmw8L2tiZD4rPGtiZD5TaGlmdDwva2JkPis8a2JkPlo8L2tiZD4gLyA8a2JkPkN0cmw8L2tiZD4rPGtiZD5ZPC9rYmQ+KS5cIlxuICAgICAgICAgICAgICAgIFtlc2NhcGVdPVwiZmFsc2VcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInJlZG9SZWdpb24uZW1pdCgpXCI+XG4gICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3JlZG8uc3ZnXCIgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS10cmFzaFwiIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiRGVsZXRlIHNlbGVjdGVkIHJlZ2lvbiAoPGtiZD5EZWw8L2tiZD4pXCJcbiAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAob25DbGljayk9XCJkZWxldGVSZWdpb24uZW1pdCgpXCI+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPCEtLSBTQU0gKGppdC11aSM5MCk6IGJveC1wcm9tcHQgc2VnbWVudCwgaW50ZXJhY3RpdmUgcG9pbnQgcHJvbXB0cywgYW5kIGFcbiAgICAgICAgICAgbW9kZWwgcGlja2VyLiAtLT5cbiAgICAgIDxzcGFuIGNsYXNzPVwidG9vbGJhci1zZXBhcmF0b3JcIj48L3NwYW4+XG4gICAgICA8IS0tIFNBTSBjb250cm9scyBncm91cGVkOiBib3gtcHJvbXB0IFNlZ21lbnQsIHRoZW4gdGhlIHBvaW50LXByb21wdCB0b29sLFxuICAgICAgICAgICB0aGVuIHRoZSBtb2RlbC1waWNrZXIgZHJvcGRvd24gKHRoZSBjaGV2cm9uKSB3aGljaCBnb3Zlcm5zIEJPVEggdG9vbHMuXG4gICAgICAgICAgIEZhbGxzIGJhY2sgdG8gU2VnbWVudCArIHBvaW50IHdpdGggbm8gbW9kZWwgcGlja2VyIHdoZW4gbm9uZSBhcmUgc2V0LiAtLT5cbiAgICAgIDxzcGFuICpuZ0lmPVwic2FtTW9kZWxzLmxlbmd0aDsgZWxzZSBzZWdtZW50UGxhaW5cIiBjbGFzcz1cInAtYnV0dG9uc2V0IHNlZ21lbnQtc3BsaXRcIj5cbiAgICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICAgICAgICAgICAgcFRvb2x0aXA9XCJTZWdtZW50IHRoZSBkcmF3biByZWN0YW5nbGVzIHdpdGggU0FNIChib3ggcHJvbXB0KVwiXG4gICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2VnbWVudFJlZ2lvbnMuZW1pdCgpXCI+XG4gICAgICAgICAgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc2FtLWJveC1wcm9tcHQuc3ZnXCJcbiAgICAgICAgICAgICAgIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cInNhbVBvaW50QnRuXCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktY2hldnJvbi1kb3duXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlBpY2sgdGhlIFNBTSBtb2RlbCAodXNlZCBieSBib3RoIHNlZ21lbnQgdG9vbHMpXCJcbiAgICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAob25DbGljayk9XCJzYW1Nb2RlbE1lbnUudG9nZ2xlKCRldmVudClcIj5cbiAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgIDwvc3Bhbj5cbiAgICAgIDxuZy10ZW1wbGF0ZSAjc2VnbWVudFBsYWluPlxuICAgICAgICA8c3BhbiBjbGFzcz1cInAtYnV0dG9uc2V0IHNlZ21lbnQtc3BsaXRcIj5cbiAgICAgICAgICA8cC1idXR0b24gc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICAgIHBUb29sdGlwPVwiU2VnbWVudCB0aGUgZHJhd24gcmVjdGFuZ2xlcyB3aXRoIFNBTSAoYm94IHByb21wdClcIlxuICAgICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgICAob25DbGljayk9XCJzZWdtZW50UmVnaW9ucy5lbWl0KClcIj5cbiAgICAgICAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3NhbS1ib3gtcHJvbXB0LnN2Z1wiXG4gICAgICAgICAgICAgICAgIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIvPlxuICAgICAgICAgIDwvcC1idXR0b24+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJzYW1Qb2ludEJ0blwiPjwvbmctY29udGFpbmVyPlxuICAgICAgICA8L3NwYW4+XG4gICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgPCEtLSBQb2ludC1wcm9tcHQgdG9vbCDigJQgc2hhcmVkIGJldHdlZW4gdGhlIG1vZGVscyAvIG5vLW1vZGVscyBsYXlvdXRzIHNvIGl0XG4gICAgICAgICAgIGFsd2F5cyBzaXRzIGp1c3QgbGVmdCBvZiB0aGUgbW9kZWwgZHJvcGRvd24uIC0tPlxuICAgICAgPG5nLXRlbXBsYXRlICNzYW1Qb2ludEJ0bj5cbiAgICAgICAgPHAtYnV0dG9uIHNpemU9XCJzbWFsbFwiXG4gICAgICAgICAgICAgICAgICBbdGV4dF09XCJhY3RpdmVEcmFnTW9kZSAhPT0gJ3NhbVBvaW50J1wiXG4gICAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIlNBTSBwb2ludCBwcm9tcHRzOiBjbGljayBhIGZpYmVyIHRvIHNlZ21lbnQgaXQgYXMgYSBuZXcgcmVnaW9uO1xuICAgICAgICAgICAgICAgICAgU2hpZnQvQWx0LWNsaWNrIHRvIHJlZmluZSAoZXhjbHVkZSkgdGhlIGN1cnJlbnQgb25lLjxicj5Fc2MgdW5kb2VzIGl0LlwiXG4gICAgICAgICAgICAgICAgICBbZXNjYXBlXT1cImZhbHNlXCIgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJ3NhbVBvaW50JylcIj5cbiAgICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zYW0tcG9pbnQtcHJvbXB0LnN2Z1wiXG4gICAgICAgICAgICAgICBbY2xhc3NdPVwiYWN0aXZlRHJhZ01vZGUgPT09ICdzYW1Qb2ludCcgPyAndG9vbGJhci1pY29uLWFjdGl2ZScgOiAndG9vbGJhci1pY29uLWRhcmsnXCJcbiAgICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIi8+XG4gICAgICAgIDwvcC1idXR0b24+XG4gICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgPHAtbWVudSAjc2FtTW9kZWxNZW51IHN0eWxlQ2xhc3M9XCJtb2RlbC1tZW51XCIgW21vZGVsXT1cInNhbU1lbnVJdGVtc1wiIFtwb3B1cF09XCJ0cnVlXCIgYXBwZW5kVG89XCJib2R5XCI+XG4gICAgICAgIDxuZy10ZW1wbGF0ZSBwVGVtcGxhdGU9XCJpdGVtXCIgbGV0LWl0ZW0+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJtb2RlbE1lbnVJdGVtXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIFtuZ1RlbXBsYXRlT3V0bGV0Q29udGV4dF09XCJ7ICRpbXBsaWNpdDogaXRlbSB9XCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICA8L3AtbWVudT5cblxuICAgICAgPCEtLSBTaGFyZWQgaXRlbSBib2R5IGZvciBhbGwgdGhyZWUgbW9kZWwgcGlja2VycyAoU0FNIC8gWU9MTyAvIHJldGluYWwpOlxuICAgICAgICAgICB0aGUgc3RhbmRhcmQgY2hlY2stb3ItYmxhbmsgaWNvbiBhbmQgbGFiZWwsIHBsdXMgYW4gaW5mbyBpY29uIGNhcnJ5aW5nXG4gICAgICAgICAgIHRoZSBtb2RlbCdzIGRlc2NyaXB0aW9uIGFzIGEgaG92ZXIgdG9vbHRpcC4gVGhpcyByZXBsYWNlZCBhIHNpbmdsZVxuICAgICAgICAgICBcIkFib3V0IHRoZSBTQU0gbW9kZWxzXCIgb3ZlcmxheSBwYW5lbCDigJQgcGVyLW1vZGVsIHRleHQgc2l0cyBuZXh0IHRvIHRoZVxuICAgICAgICAgICBtb2RlbCBpdCBkZXNjcmliZXMsIGFuZCBZT0xPL3JldGluYWwgZ2V0IHRoZSBzYW1lIGFmZm9yZGFuY2UuXG5cbiAgICAgICAgICAgcC1tZW51IHB1dHMgaXRzIGNsaWNrIGhhbmRsZXIgb24gdGhlIHdyYXBwaW5nIGAucC1tZW51aXRlbS1jb250ZW50YCxcbiAgICAgICAgICAgb3V0c2lkZSB0aGlzIHRlbXBsYXRlLCBzbyBgY29tbWFuZGAgc3RpbGwgZmlyZXMgYW5kIHRoZSB3aG9sZSByb3cgc3RheXNcbiAgICAgICAgICAgY2xpY2thYmxlIOKAlCB3aGljaCBpcyBhbHNvIHdoeSB0aGUgaW5mbyBpY29uIGhhcyB0byBzdG9wIHByb3BhZ2F0aW9uLCBvclxuICAgICAgICAgICByZWFkaW5nIGFib3V0IGEgbW9kZWwgd291bGQgc3dpdGNoIHRvIGl0LiAtLT5cbiAgICAgIDxuZy10ZW1wbGF0ZSAjbW9kZWxNZW51SXRlbSBsZXQtaXRlbT5cbiAgICAgICAgPGEgY2xhc3M9XCJwLW1lbnVpdGVtLWxpbmsgbW9kZWwtbWVudS1pdGVtXCI+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJwLW1lbnVpdGVtLWljb25cIiBbbmdDbGFzc109XCJpdGVtLmljb25cIj48L3NwYW4+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJwLW1lbnVpdGVtLXRleHRcIj57eyBpdGVtLmxhYmVsIH19PC9zcGFuPlxuICAgICAgICAgIDwhLS0gYGJvdHRvbWAgbGlrZSBldmVyeSBvdGhlciB0b29sdGlwIGluIHRoaXMgdG9vbGJhciwgYW5kIGZvciByb29tOlxuICAgICAgICAgICAgICAgdGhlc2UgbWVudXMgb3BlbiB1bmRlciBidXR0b25zIHRoYXQgY2FuIHNpdCBuZWFyIHRoZSByaWdodCBlZGdlLFxuICAgICAgICAgICAgICAgd2hlcmUgYSBgcmlnaHRgIHRvb2x0aXAgaGFzIGEgZmV3IGh1bmRyZWQgcHggYW5kIHNxdWVlemVzIGl0cyB0ZXh0XG4gICAgICAgICAgICAgICBpbnRvIGEgbmFycm93IGNvbHVtbiBpbnN0ZWFkIG9mIHVzaW5nIGl0cyBtYXgtd2lkdGguIC0tPlxuICAgICAgICAgIDwhLS0gQSBmb2N1c2FibGUgPGJ1dHRvbj4sIG5vdCBhIGJhcmUgPGk+LiBBcyBhbiA8aT4gdGhlIGRlc2NyaXB0aW9uXG4gICAgICAgICAgICAgICB3YXMgcmVhY2hhYmxlIG9ubHkgYnkgaG92ZXJpbmcgYSBtb3VzZTogbm90IHRhYmJhYmxlLCBub1xuICAgICAgICAgICAgICAgYWNjZXNzaWJsZSBuYW1lLCBub3RoaW5nIGZvciBhIHNjcmVlbiByZWFkZXIgdG8gYW5ub3VuY2Ug4oCUIHNvXG4gICAgICAgICAgICAgICBrZXlib2FyZCBhbmQgQVQgdXNlcnMgY291bGQgcGljayBhIGNoZWNrcG9pbnQgYnV0IG5ldmVyIHJlYWQgd2hhdFxuICAgICAgICAgICAgICAgaXQgd2FzLiBQcmltZU5HJ3MgdG9vbHRpcCBzaG93cyBvbiBmb2N1cyBhcyB3ZWxsIGFzIGhvdmVyLCBzb1xuICAgICAgICAgICAgICAgbWFraW5nIGl0IGEgYnV0dG9uIHJlc3RvcmVzIGl0IGZvciBmcmVlLlxuXG4gICAgICAgICAgICAgICBgYXJpYS1kZXNjcmliZWRieWAgaXMgZGVsaWJlcmF0ZWx5IG5vdCB1c2VkOiB0aGUgdG9vbHRpcCBpc1xuICAgICAgICAgICAgICAgcmVuZGVyZWQgaW50byBgYm9keWAgb24gZGVtYW5kIGFuZCBoYXMgbm8gc3RhYmxlIGlkIHRvIHBvaW50IGF0LlxuICAgICAgICAgICAgICAgVGhlIGRlc2NyaXB0aW9uIGlzIGNhcnJpZWQgb24gYGFyaWEtbGFiZWxgIGluc3RlYWQsIHdoaWNoIGlzXG4gICAgICAgICAgICAgICBhbm5vdW5jZWQgd2hldGhlciBvciBub3QgdGhlIHRvb2x0aXAgaXMgc2hvd2luZy4gSXQgaXMgc3RyaXBwZWQgb2ZcbiAgICAgICAgICAgICAgIG1hcmt1cCBmaXJzdCwgc2luY2UgdGhlIGNvcHkgY29udGFpbnMgYDxiPmAvYDxicj5gIGZvciB0aGUgdmlzdWFsXG4gICAgICAgICAgICAgICB0b29sdGlwIGFuZCB0aG9zZSB3b3VsZCBvdGhlcndpc2UgYmUgcmVhZCBvdXQgbGl0ZXJhbGx5LiAtLT5cbiAgICAgICAgICA8YnV0dG9uICpuZ0lmPVwiaXRlbS50b29sdGlwXCIgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgICBjbGFzcz1cInBpIHBpLWluZm8tY2lyY2xlIG1vZGVsLWluZm8taWNvblwiXG4gICAgICAgICAgICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidBYm91dCAnICsgaXRlbS5sYWJlbCArICc6ICcgKyBwbGFpblRleHQoaXRlbS50b29sdGlwKVwiXG4gICAgICAgICAgICAgICAgICBbcFRvb2x0aXBdPVwiaXRlbS50b29sdGlwXCIgW2VzY2FwZV09XCJmYWxzZVwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICBhcHBlbmRUbz1cImJvZHlcIiB0b29sdGlwU3R5bGVDbGFzcz1cIm1vZGVsLWluZm8tdG9vbHRpcFwiXG4gICAgICAgICAgICAgICAgICAoY2xpY2spPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgICAgICAgICAgICAgIChrZXlkb3duLmVudGVyKT1cIiRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiXG4gICAgICAgICAgICAgICAgICAoa2V5ZG93bi5zcGFjZSk9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIj48L2J1dHRvbj5cbiAgICAgICAgPC9hPlxuICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkF1dG8tc2VnbWVudCBjZWxscyBpbnNpZGUgZWFjaCBkcmF3biByZWN0YW5nbGUgKGNlbGxwb3NlLVNBTSlcIlxuICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwic2VnbWVudENlbGxwb3NlLmVtaXQoKVwiPlxuICAgICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9jZWxscy5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiLz5cbiAgICAgIDwvcC1idXR0b24+XG5cbiAgICAgIDwhLS0gQ29udHJpYnV0ZWQgbm8tcHJvbXB0IHRvb2xzIChUT09MQkFSX1RPT0xTKS4gVW5saWtlIHRoZSBTQU0gYW5kXG4gICAgICAgICAgIGNlbGxwb3NlIHRvb2xzIHRoZXNlIHRha2Ugbm8gcHJvbXB0IOKAlCB0aGV5IHN3ZWVwIHRoZSB3aG9sZSBjdXJyZW50XG4gICAgICAgICAgIHZpZXcgcmF0aGVyIHRoYW4gYmVpbmcgcG9pbnRlZCBhdCBzb21ldGhpbmcg4oCUIHNvIHRoZXJlIGlzIG5vdGhpbmcgdG9cbiAgICAgICAgICAgZHJhdyBmaXJzdC4gRWFjaCByZW5kZXJzIHRoZSBzYW1lIHNwbGl0IGJ1dHRvbjogcnVuLCBwaWNrIGNoZWNrcG9pbnQsXG4gICAgICAgICAgIG9wZW4gcGFyYW1ldGVycy5cblxuICAgICAgICAgICBOb3RoaW5nIGhlcmUgbmFtZXMgYSBzcGVjaWZpYyB0b29sLiBUaGUgbGlicmFyeSBzaGlwcyBub25lOyBhIGhvc3RcbiAgICAgICAgICAgcmVnaXN0ZXJzIHRoZW0sIHdoaWNoIGlzIHdoYXQgbGV0cyB0aGlzIHBhY2thZ2UgYmUgcHVibGlzaGVkIHdpdGhvdXRcbiAgICAgICAgICAgdGhlIGNsb3NlZCBtb2RlbCBkZXBlbmRlbmNpZXMuIC0tPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdGb3I9XCJsZXQgdG9vbCBvZiBjb250cmlidXRlZFRvb2xzOyB0cmFja0J5OiB0cmFja1Rvb2xCeUlkXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwicC1idXR0b25zZXQgc2VnbWVudC1zcGxpdFwiPlxuICAgICAgICAgIDxwLWJ1dHRvbiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgICAgW3BUb29sdGlwXT1cInRvb2wucnVuVG9vbHRpcFwiXG4gICAgICAgICAgICAgICAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInJ1blRvb2wuZW1pdCh0b29sLmlkKVwiPlxuICAgICAgICAgICAgPCEtLSBgdG9vbGJhci1pY29uLWRhcmtgIGlzIGhhcmRjb2RlZCByYXRoZXIgdGhhbiBib3VuZCwgdW5saWtlIHRoZVxuICAgICAgICAgICAgICAgICB0b2dnbGUgdG9vbHM6IHRoZXNlIGZpcmUgYSBydW4gYW5kIGhhdmUgbm8gYWN0aXZlIHN0YXRlLiAtLT5cbiAgICAgICAgICAgIDxpbWcgKm5nSWY9XCJ0b29sLmljb24uc3JjXCIgW3NyY109XCJ0b29sLmljb24uc3JjXCIgY2xhc3M9XCJ0b29sYmFyLWljb24tZGFya1wiXG4gICAgICAgICAgICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIiBbYWx0XT1cInRvb2wubGFiZWxcIiAvPlxuICAgICAgICAgICAgPGkgKm5nSWY9XCJ0b29sLmljb24ucGlcIiBjbGFzcz1cInBpIHt7IHRvb2wuaWNvbi5waSB9fVwiPjwvaT5cbiAgICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgICAgIDxwLWJ1dHRvbiBpY29uPVwicGkgcGktY2hldnJvbi1kb3duXCIgc2l6ZT1cInNtYWxsXCIgW3RleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICAgIFtwVG9vbHRpcF09XCJ0b29sLm1vZGVsVG9vbHRpcCB8fCAnUGljayB0aGUgbW9kZWwnXCJcbiAgICAgICAgICAgICAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgICAgICAgICAgICAgKG9uQ2xpY2spPVwidG9vbE1lbnUudG9nZ2xlKCRldmVudClcIj5cbiAgICAgICAgICA8L3AtYnV0dG9uPlxuICAgICAgICAgIDxwLWJ1dHRvbiAqbmdJZj1cInRvb2wucGFyYW1zLmxlbmd0aFwiXG4gICAgICAgICAgICAgICAgICAgIGljb249XCJwaSBwaS1zbGlkZXJzLWhcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgICAgW3BUb29sdGlwXT1cInRvb2wucGFyYW1zVG9vbHRpcCB8fCAnUGFyYW1ldGVycydcIlxuICAgICAgICAgICAgICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgICAgICAob25DbGljayk9XCJvcGVuVG9vbFBhcmFtcy5lbWl0KHRvb2wuaWQpXCI+XG4gICAgICAgICAgPC9wLWJ1dHRvbj5cbiAgICAgICAgPC9zcGFuPlxuICAgICAgICA8cC1tZW51ICN0b29sTWVudSBzdHlsZUNsYXNzPVwibW9kZWwtbWVudVwiIFttb2RlbF09XCJ0b29sTWVudUl0ZW1zW3Rvb2wuaWRdIHx8IFtdXCJcbiAgICAgICAgICAgICAgICBbcG9wdXBdPVwidHJ1ZVwiIGFwcGVuZFRvPVwiYm9keVwiPlxuICAgICAgICAgIDxuZy10ZW1wbGF0ZSBwVGVtcGxhdGU9XCJpdGVtXCIgbGV0LWl0ZW0+XG4gICAgICAgICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIm1vZGVsTWVudUl0ZW1cIlxuICAgICAgICAgICAgICAgICAgICAgICAgICBbbmdUZW1wbGF0ZU91dGxldENvbnRleHRdPVwieyAkaW1wbGljaXQ6IGl0ZW0gfVwiPjwvbmctY29udGFpbmVyPlxuICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgIDwvcC1tZW51PlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwidG9vbHMuaGVscFwiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cblxuICAgICAgPCEtLSBIZWxwIC0tPlxuICAgICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1xdWVzdGlvbi1jaXJjbGVcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICBwVG9vbHRpcD1cIkhlbHBcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAgICAgICAgIChvbkNsaWNrKT1cInNob3dIZWxwKClcIj5cbiAgICAgIDwvcC1idXR0b24+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgPC9kaXY+XG48L3AtdG9vbGJhcj5cbjxwLWRpYWxvZyBoZWFkZXI9XCJQbG90dGluZyBoZWxwXCIgWyh2aXNpYmxlKV09XCJkaXNwbGF5SGVscERpYWxvZ1wiIFttb2RhbF09XCJ0cnVlXCIgW3N0eWxlXT1cInt3aWR0aDogJzUwdncnfVwiXG4gICAgICAgICAgW21heGltaXphYmxlXT1cInRydWVcIiBbZHJhZ2dhYmxlXT1cImZhbHNlXCIgW3Jlc2l6YWJsZV09XCJmYWxzZVwiPlxuICA8cD5DbGljayBhIGZpbGUgaW4gdGhlIGxlZnQtaGFuZCB0cmVlIHRvIGRpc3BsYXkgaXQuIEJ5IGRlZmF1bHQgaXQgb3BlbnMgaW4gdGhlXG4gICAgPGI+SW1hZ2U8L2I+IHZpZXcg4oCUIGEgbmF0aXZlbHkgdGlsZWQsIGRlZXBseSB6b29tYWJsZSByYXN0ZXIgKE9wZW5TZWFkcmFnb24pIHdpdGggYVxuICAgIG5hdmlnYXRvciBtaW5pbWFwIGFuZCBhIHBoeXNpY2FsLXVuaXRzIHNjYWxlIGJhci4gPGI+Q2xpY2sgdGhlIGltYWdlIHRvIHpvb208L2I+IHRvd2FyZFxuICAgIHRoZSBjbGlja2VkIHBvaW50OyBzY3JvbGwgdG8gem9vbSBhbmQgZHJhZyB0byBwYW4uIFdoZW4gYSBzdGFjayBpcyBvcGVuLCBhIHNsaWNlIHNsaWRlclxuICAgIChJbWFnZSB2aWV3KSBvciBpbWFnZS1tb2RlIHRvZ2dsZSAob3RoZXIgdmlld3MpIGFwcGVhcnMuIFJlZ2lvbnMgY2FuIGFsc28gYmUgZWRpdGVkIGluIHRoZVxuICAgIDxiPlJlZ2lvbiBFZGl0b3I8L2I+LCBvcGVuZWQgd2l0aCB0aGVcbiAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9yZWdpb24tZWRpdG9yLnN2Z1wiIHdpZHRoPVwiMTRcIiBoZWlnaHQ9XCIxNFwiIGFsdD1cIlJlZ2lvbiBFZGl0b3JcIi8+XG4gICAgdG9vbGJhciBidXR0b24gYXMgYSBmbG9hdGluZyBkaWFsb2cuIERyYWcgdGhlIDxpIGNsYXNzPVwicGkgcGktYmFyc1wiPjwvaT4gaGFuZGxlIHRvIGRldGFjaFxuICAgIHRoaXMgdG9vbGJhciBpbnRvIGEgZmxvYXRpbmcgd2luZG93LlxuICA8L3A+XG5cbiAgPGgzPlBsb3QtdHlwZSBkcm9wZG93bjwvaDM+XG4gIDxwPlxuICAgIFRoZSA8Yj5wbG90LXR5cGUgZHJvcGRvd248L2I+IHN3aXRjaGVzIGhvdyB0aGUgZmlsZSBpcyB2aXN1YWxpemVkOlxuICAgIDxiPkltYWdlPC9iPiwgPGI+SGVhdG1hcDwvYj4sIDxiPkNvbnRvdXI8L2I+LCA8Yj5TdXJmYWNlPC9iPiwgPGI+Vm9sdW1lPC9iPixcbiAgICA8Yj5Jc29zdXJmYWNlPC9iPiwgYW5kIOKAlCB3aGVuIGEgc3BhdGlhbC1vbWljcyBkYXRhc2V0IGlzIG9wZW4g4oCUXG4gICAgPGI+U3BhdGlhbCBvbWljczwvYj4gYW5kIDxiPlNwYXRpYWwgb21pY3MgM0Q8L2I+LlxuICA8L3A+XG4gIDxwPlxuICAgIDxiPk9ubHkgdGhlIG1vZGVzIHRoZSBkYXRhIGNhbiBhY3R1YWxseSBzdXBwb3J0IGFyZSBvZmZlcmVkPC9iPiwgc28gdGhlIGxpc3QgY2hhbmdlc1xuICAgIHdpdGggd2hhdCBpcyBsb2FkZWQgcmF0aGVyIHRoYW4gc2hvd2luZyBlbnRyaWVzIHRoYXQgd291bGQgY29tZSB1cCBibGFuazpcbiAgPC9wPlxuICA8dWw+XG4gICAgPGxpPjxiPkNvbnRvdXI8L2I+LCA8Yj5Wb2x1bWU8L2I+IGFuZCA8Yj5Jc29zdXJmYWNlPC9iPiBuZWVkIGEgZ3JheXNjYWxlIGltYWdlOyB0aGVcbiAgICAgIHR3byAzRCBvbmVzIGFsc28gbmVlZCBhIHotc3RhY2suPC9saT5cbiAgICA8bGk+PGI+U3BhdGlhbCBvbWljczwvYj4gbmVlZHMgYSBwdWJsaXNoZWQgZGF0YXNldCwgYW5kIDxiPlNwYXRpYWwgb21pY3MgM0Q8L2I+IG5lZWRzXG4gICAgICBvbmUgd2hvc2UgY29vcmRpbmF0ZXMgY2FycnkgYSB6LjwvbGk+XG4gICAgPGxpPlRoZSBwaXhlbCBtb2RlcyAoPGI+SW1hZ2U8L2I+LCA8Yj5IZWF0bWFwPC9iPiwgPGI+U3VyZmFjZTwvYj4pIGFyZSB3aXRoaGVsZCBmb3IgYVxuICAgICAgZGF0YXNldCB0aGF0IGJyaW5ncyBubyB0aXNzdWUgaW1hZ2Ug4oCUIHNlcUZJU0ggcmVjb3JkcyBjb29yZGluYXRlcyBhbmQgbm8gc2VjdGlvbiwgc29cbiAgICAgIHRoZXJlIGlzIG5vdGhpbmcgZm9yIHRoZW0gdG8gZHJhdy48L2xpPlxuICA8L3VsPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+SXNvc3VyZmFjZSBiYW5kPC9iPlwiIHNsaWRlciAoSXNvc3VyZmFjZSk6IHNldHMgdGhlIGludGVuc2l0eSBiYW5kIHRoZSBzdXJmYWNlcyBhcmVcbiAgICAgIGRyYXduIGZvciwgbWFwcGVkIG9udG8gdGhlIHZvbHVtZSdzIHJlYWwgaW50ZW5zaXR5IHJhbmdlLjwvbGk+XG4gICAgPGxpPlwiPGI+M0QgY2FtZXJhPC9iPlwiIChTdXJmYWNlIC8gU2NhdHRlciAzRCAvIElzb3N1cmZhY2UpOiB6b29tIDxpIGNsYXNzPVwicGkgcGktc2VhcmNoXCI+PC9pPixcbiAgICAgIHBhbiA8aSBjbGFzcz1cInBpIHBpLWFycm93cy1hbHRcIj48L2k+LCBvcmJpdCA8aSBjbGFzcz1cInBpIHBpLWdsb2JlXCI+PC9pPixcbiAgICAgIHR1cm50YWJsZSA8aSBjbGFzcz1cInBpIHBpLXN5bmNcIj48L2k+LCBhbmQgcmVzZXQgY2FtZXJhIDxpIGNsYXNzPVwicGkgcGktaG9tZVwiPjwvaT47XG4gICAgICBzY3JvbGwgdG8gem9vbSB0aGUgM0Qgc2NlbmUuPC9saT5cbiAgPC91bD5cblxuICA8aDM+SW1hZ2UgbWFuaXB1bGF0aW9uIHRvb2xzPC9oMz5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPlJlZ2lvbiBFZGl0b3I8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcmVnaW9uLWVkaXRvci5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46XG4gICAgICBvcGVucyB0aGUgUmVnaW9uIEVkaXRvciBhcyBhIGZsb2F0aW5nLCBkcmFnZ2FibGUgZGlhbG9nIOKAlCBhIHBlci1pbWFnZSB0YWJsZSBvZiBldmVyeSByZWdpb25cbiAgICAgIChjbGFzcywgY29sb3VyLCBhcmVhKSB3aXRoIGJ1bGsgc2VsZWN0L2xhYmVsL2NvbG91ci9kZWxldGUgYW5kIEdlb0pTT04gaW1wb3J0L2V4cG9ydC4gSXQgc3RheXNcbiAgICAgIGluIHN5bmMgd2l0aCB0aGUgaW1hZ2UsIHNvIHlvdSBjYW4ga2VlcCBpdCBvcGVuIHdoaWxlIGRyYXdpbmcuPC9saT5cbiAgICA8bGk+XCI8Yj5JbnRlbnNpdHkgcHJvZmlsZSBsaW5lczwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zcGxpbmUtY2hhcnQuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+XG4gICAgICAoPGk+d29yayBpbiBwcm9ncmVzcyDigJQgbm90IHlldCBzdGFibGU8L2k+KTogYWRkcyBhIGNvbG91cmVkIGxpbmUgUk9JOyBhIGZsb2F0aW5nIGluc2V0XG4gICAgICBjaGFydCBwbG90cyBpbnRlbnNpdHkgYWxvbmcgZWFjaCBsaW5lLCB1cGRhdGVzIGxpdmUgYXMgeW91IGRyYWcgaXQsIGFuZCByZS1zYW1wbGVzIGF0IHRoZVxuICAgICAgY3VycmVudCB6b29tIHJlc29sdXRpb24uIEVhY2ggYWRkZWQgbGluZSBnZXRzIGl0cyBvd24gY29sb3VyOyBzZWxlY3QgYSBsaW5lIGFuZCB1c2UgdGhlXG4gICAgICB0cmFzaCB0b29sIDxpIGNsYXNzPVwicGkgcGktdHJhc2hcIj48L2k+IHRvIHJlbW92ZSBpdCAoYW5kIGl0cyB0cmFjZSkuPC9saT5cbiAgICA8bGk+XCI8Yj5DaGFubmVscyAmYW1wOyBIaXN0b2dyYW08L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvaW52ZXJ0LnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjpcbiAgICAgIG9wZW5zIGEgZmxvYXRpbmcgZGlhbG9nIGZvciBicmlnaHRuZXNzL2NvbnRyYXN0LCBnYW1tYSwgYW5kIHBlci1jaGFubmVsIGRpc3BsYXksIHBsdXMgdGhlXG4gICAgICA8Yj5jb2xvcm1hcCAvIExVVDwvYj4gY29udHJvbHMgKGdyYXlzY2FsZSBvbmx5KSDigJQgYSA8Yj5yZXZlcnNlPC9iPiB0b2dnbGUgYW5kIGEgY29sb3JtYXBcbiAgICAgIGRyb3Bkb3duIChkZWZhdWx0OiBpbnZlcnRlZCBncmV5cykuPC9saT5cbiAgICA8bGk+XCI8Yj5TbW9vdGhlbjwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zbW9vdGguc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+XG4gICAgICAoSW1hZ2UgdmlldyBvbmx5KTogdGhlIGltYWdlIG9wZW5zIHNob3dpbmcgPGI+cmF3IHBpeGVsczwvYj4gKG5lYXJlc3QtbmVpZ2hib3VyKSwgc29cbiAgICAgIHpvb21pbmcgaW4gcmVuZGVycyBjcmlzcCBibG9ja3MgZm9yIHBpeGVsIGluc3BlY3Rpb24uIFRvZ2dsZSB0aGlzIG9uIGZvciBiaWxpbmVhclxuICAgICAgc21vb3RoaW5nOyB0aGUgaWNvbiB0dXJucyB3aGl0ZSB3aGlsZSBzbW9vdGhpbmcgaXMgYWN0aXZlLjwvbGk+XG4gIDwvdWw+XG5cbiAgPGgzPkRvd25sb2FkICZhbXA7IHZpZXcgdG9vbHM8L2gzPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+RG93bmxvYWQ8L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1kb3dubG9hZFwiPjwvaT46IGRvd25sb2FkIHRoZSBjdXJyZW50IGltYWdlIGFzIGEgUE5HLjwvbGk+XG4gICAgPGxpPlwiPGI+Rml0IHRvIHZpZXc8L2I+XCIgLyBhdXRvc2NhbGUgPGkgY2xhc3M9XCJwaSBwaS13aW5kb3ctbWF4aW1pemVcIj48L2k+OiByZXNldHMgdGhlIGltYWdlXG4gICAgICB0byBpdHMgb3JpZ2luYWwgZGltZW5zaW9ucy4gSW4gc3RhY2sgbW9kZSBpdCBhbHNvIHJlc2V0cyB0byBzaW5nbGUtaW1hZ2UgbW9kZS48L2xpPlxuICA8L3VsPlxuXG4gIDxoMz5ab29tIHRvb2xzPC9oMz5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPlpvb20gYm94PC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3pvb20tYm94LnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPlxuICAgICAgKDxrYmQ+Yjwva2JkPik6IGRyYWcgYSBib3ggdG8gc2VsZWN0IGFuIGFyZWEgdG8gem9vbSBpbnRvLjwvbGk+XG4gICAgPGxpPlwiPGI+UGFuPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktYXJyb3dzLWFsdFwiPjwvaT4gKDxrYmQ+cDwva2JkPik6IGRyYWcgdG8gcGFuIHRoZSBpbWFnZS48L2xpPlxuICAgIDxsaT5cIjxiPlpvb20gaW48L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1zZWFyY2gtcGx1c1wiPjwvaT4gKDxrYmQ+Kzwva2JkPik6IHpvb20gaW4gb25lIHN0ZXAuPC9saT5cbiAgICA8bGk+XCI8Yj5ab29tIG91dDwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXNlYXJjaC1taW51c1wiPjwvaT4gKDxrYmQ+LTwva2JkPik6IHpvb20gb3V0IG9uZSBzdGVwLFxuICAgICAgc3RvcHBpbmcgYXQgdGhlIG9yaWdpbmFsIGltYWdlIGRpbWVuc2lvbnMuPC9saT5cbiAgPC91bD5cblxuICA8aDM+UmVnaW9uIHRvb2xzPC9oMz5cbiAgPHA+QnkgZGVmYXVsdCBubyB0b29sIGlzIHNlbGVjdGVkIChhbGwgZ3JleWVkLW91dCk7IHNlbGVjdGluZyBhIHRvb2wgaGlnaGxpZ2h0cyBpdC4gVGhlXG4gICAgdmVydGV4LWVkaXRpbmcgdG9vbHMgKHBvbHlnb24sIGFkZC9kZWxldGUgdmVydGV4LCBtb3ZlLCBCw6l6aWVyIGNvbnZlcnNpb25zKSBhcmUgYXZhaWxhYmxlXG4gICAgaW4gdGhlIDxiPkltYWdlPC9iPiB2aWV3IG9ubHkuIE9uIGEgPGI+ZG9udXQ8L2I+IChhIHJlZ2lvbiB3aXRoIGEgaG9sZSksIHRoZSBpbm5lci1yaW5nXG4gICAgdmVydGljZXMgYXJlIGVkaXRhYmxlIHRvbyDigJQgZHJhZywgYWRkLCBhbmQgZGVsZXRlIHRoZW0ganVzdCBsaWtlIHRoZSBvdXRlciByaW5nLjwvcD5cbiAgPHVsPlxuICAgIDxsaT5cIjxiPlNlbGVjdGlvbjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLWFycm93LXVwLXJpZ2h0XCI+PC9pPiAoPGtiZD5zPC9rYmQ+KTogYSBuZXV0cmFsIG1vZGVcbiAgICAgIHRoYXQgdHVybnMgb2ZmIHdoYXRldmVyIGRyYXdpbmcgdG9vbCB3YXMgYWN0aXZlICh3YW5kLCB2ZXJ0ZXggZXJhc2VyLCByZWN0YW5nbGUsIGV0Yy4pLlxuICAgICAgVXNlIGl0IHRvIHN0ZXAgb3V0IG9mIGVkaXRpbmcgd2l0aG91dCBwaWNraW5nIGFub3RoZXIgdG9vbC48L2xpPlxuICAgIDxsaT5cIjxiPlJlY3Rhbmd1bGFyIHJlZ2lvbjwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXN0b3BcIj48L2k+ICg8a2JkPnI8L2tiZD4pOiBjbGljayBhbmQgZHJhZyB0b1xuICAgICAgY3JlYXRlIGEgcmVjdGFuZ3VsYXIgcmVnaW9uLjwvbGk+XG4gICAgPGxpPlwiPGI+UG9seWxpbmU8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcG9seWxpbmUuc3ZnXCIgd2lkdGg9XCIxNFwiIGhlaWdodD1cIjE0XCI+XG4gICAgICAoPGtiZD5sPC9rYmQ+KTogZHJhdyBhbiBvcGVuIChub24tY2xvc2VkKSBwb2x5bGluZSBieSBjbGlja2luZyBhbmQgZHJhZ2dpbmcuIFBvbHlsaW5lcyBjYW5cbiAgICAgIGJlIGVkaXRlZCBhbmQgZXhwb3J0ZWQgdG8gR2VvSlNPTiBhcyBhIDxjb2RlPkxpbmVTdHJpbmc8L2NvZGU+IGZlYXR1cmUuPC9saT5cbiAgICA8bGk+XCI8Yj5GcmVlZm9ybSByZWdpb248L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1wZW5jaWxcIj48L2k+ICg8a2JkPmY8L2tiZD4pOiBjbGljayBhbmQgZHJhZyB0b1xuICAgICAgZHJhdyBhIGNsb3NlZCBmcmVlZm9ybSBwb2x5Z29uLjwvbGk+XG4gICAgPGxpPlwiPGI+QnJ1c2g8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvcGFpbnRicnVzaC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46IGNsaWNrXG4gICAgICBhbmQgZHJhZyB0byBwYWludCBhIHJlZ2lvbiB3aXRoIGEgY2lyY3VsYXIgYnJ1c2gg4oCUIHRoZSBwYWludGVkIHN0cm9rZSBpcyB0aGUgcmVnaW9uLlxuICAgICAgQSA8Yj5TaXplPC9iPiBzbGlkZXIgKGJydXNoIGRpYW1ldGVyLCBpbiBpbWFnZSBwaXhlbHMpIGFwcGVhcnMgaW4gdGhlIHRvb2xiYXIgd2hpbGUgdGhlXG4gICAgICBicnVzaCBpcyBhY3RpdmUuIEhvbGQgPGtiZD5TaGlmdDwva2JkPiB3aGlsZSBwYWludGluZyB0byA8Yj5lcmFzZTwvYj4gKHN1YnRyYWN0KSBmcm9tIGFcbiAgICAgIHJlZ2lvbiBpbnN0ZWFkLiBQYWludGluZyBvdmVyIGFuIGV4aXN0aW5nIHJlZ2lvbiBleHRlbmRzIGl0OyBicnVzaGluZyBhY3Jvc3MgdHdvIHJlZ2lvbnNcbiAgICAgIG1lcmdlcyB0aGVtLiBQYWludGluZyBhIGNsb3NlZCByaW5nIChvciBzaGlmdC1lcmFzaW5nIGFuIGludGVyaW9yIHBvY2tldCkgbGVhdmVzIHRoZVxuICAgICAgZW5jbG9zZWQgYXJlYSBlbXB0eSDigJQgYSA8Yj5kb251dDwvYj4gKEltYWdlIHZpZXcpLjwvbGk+XG4gICAgPGxpPlwiPGI+UG9seWdvbjwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9wb2x5Z29uLXZlcnRpY2VzLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjpcbiAgICAgIGNsaWNrIHRvIHBsYWNlIGVhY2ggdmVydGV4OyBjbGljayB0aGUgZmlyc3QgcG9pbnQgdG8gY2xvc2UgdGhlIHBvbHlnb24uPC9saT5cbiAgICA8bGk+XCI8Yj5BZGQgdmVydGV4PC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3ZlcnRleC1hZGQuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OlxuICAgICAgY2xpY2sgYW4gZWRnZSBvZiB0aGUgc2VsZWN0ZWQgcG9seWdvbiB0byBpbnNlcnQgYSBuZXcgdmVydGV4LjwvbGk+XG4gICAgPGxpPlwiPGI+RGVsZXRlIHZlcnRleDwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy92ZXJ0ZXgtZGVsZXRlLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjpcbiAgICAgIGNsaWNrIGEgdmVydGV4IG9mIHRoZSBzZWxlY3RlZCBwb2x5Z29uIHRvIHJlbW92ZSBpdC48L2xpPlxuICAgIDxsaT5cIjxiPk1vdmUgcmVnaW9uPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3JlZ2lvbi1tb3ZlLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjpcbiAgICAgIGRyYWcgdG8gbW92ZSB0aGUgc2VsZWN0ZWQgcmVnaW9uLjwvbGk+XG4gICAgPGxpPlwiPGI+VG8gQsOpemllcjwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy90by1iZXppZXIuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+IC9cbiAgICAgIFwiPGI+VG8gcG9seWdvbjwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy90by1wb2x5Z29uLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPjpcbiAgICAgIGNvbnZlcnQgdGhlIHNlbGVjdGVkIHJlZ2lvbiB0byBhIHNtb290aCBCw6l6aWVyIGN1cnZlLCBvciBiYWNrIHRvIGEgc3RyYWlnaHQtZWRnZWQgcG9seWdvbi48L2xpPlxuICAgIDxsaT5cIjxiPldhbmQ8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvd2FuZC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj4gKDxrYmQ+dzwva2JkPik6XG4gICAgICBjbGljayB0byBncm93IGEgcmVnaW9uIGZyb20gcGl4ZWxzIHdpdGggc2ltaWxhciB2YWx1ZXMgYXJvdW5kIHRoZSBjbGljayBwb2ludC4gSG9sZFxuICAgICAgPGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+IGZvciBhbiBleGFjdC1tYXRjaCBmbG9vZCBmaWxsOyBkcmFnIHRvIGtlZXAgZXh0ZW5kaW5nIHRoZVxuICAgICAgc2VsZWN0aW9uIHdoaWxlIG1vdmluZzsgaG9sZCA8a2JkPlNoaWZ0PC9rYmQ+IHdoaWxlIGNsaWNraW5nIG9yIGRyYWdnaW5nIG9uIGFuIGV4aXN0aW5nXG4gICAgICByZWdpb24gdG8gPGI+ZXJhc2U8L2I+IHBpeGVscyBmcm9tIGl0LiBVc2UgdGhlIDxiPlNlbnMuPC9iPiBzbGlkZXIgdGhhdCBhcHBlYXJzIHRvIG1ha2UgdGhlXG4gICAgICB3YW5kIHN0cmljdGVyIChoaWdoZXIgPSBzbWFsbGVyIHNlbGVjdGlvbikgb3IgbG9vc2VyLiBTdWJzZXF1ZW50IGNsaWNrcyBrZWVwIGV4dGVuZGluZyB0aGVcbiAgICAgIHNhbWUgcmVnaW9uOyBjbGlja2luZyBpbnNpZGUgYW4gZXhpc3RpbmcgcmVnaW9uIGFkb3B0cyBpdCwgYW5kIGdyb3dpbmcgaW50byBhbm90aGVyIHJlZ2lvblxuICAgICAgbWVyZ2VzIHRoZW0uPC9saT5cbiAgICA8bGk+XCI8Yj5WZXJ0ZXggZXJhc2VyPC9iPlwiIDxpIGNsYXNzPVwicGkgcGktZXJhc2VyXCI+PC9pPiAoPGtiZD5lPC9rYmQ+KTogY2xpY2sgb3IgY2xpY2stYW5kLWRyYWdcbiAgICAgIHRvIHJlbW92ZSBwb2x5Z29uIHZlcnRpY2VzIHRoYXQgZmFsbCBpbnNpZGUgdGhlIGN1cnNvciBjaXJjbGUgZnJvbSBhbnkgcmVnaW9uLCBpbmNsdWRpbmcgYVxuICAgICAgcmVnaW9uJ3MgaW5uZXIgcmluZ3MgKGRvbnV0IGhvbGVzKS4gVXNlIHRoZSA8Yj5SYWRpdXM8L2I+IHNsaWRlciB0byBzZXQgdGhlIGVyYXNlciBzaXplIGluXG4gICAgICBpbWFnZSBwaXhlbHMuIFBvbHlnb25zIHJlZHVjZWQgYmVsb3cgMyB2ZXJ0aWNlcyAob3IgcG9seWxpbmVzIGJlbG93IDIpIGFyZSBkZWxldGVkIGVudGlyZWx5O1xuICAgICAgYSBob2xlIHJlZHVjZWQgYmVsb3cgMyB2ZXJ0aWNlcyBpcyByZW1vdmVkLjwvbGk+XG4gICAgPGxpPlwiPGI+VW5kbzwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy91bmRvLnN2Z1wiIHdpZHRoPVwiMTVcIiBoZWlnaHQ9XCIxNVwiPlxuICAgICAgKDxrYmQ+Q3RybDwva2JkPi88a2JkPkNtZDwva2JkPis8a2JkPlo8L2tiZD4pOiByZXZlcnRzIHRoZVxuICAgICAgbGFzdCByZWdpb24gYWN0aW9uIChkcmF3LCBkZWxldGUsIGVkaXQsIHdhbmQvYnJ1c2ggc3Ryb2tlLCBzZWdtZW50YXRpb24sIOKApikuIFVwIHRvIHRoZSBsYXN0XG4gICAgICA8Yj4xMDwvYj4gYWN0aW9ucyBjYW4gYmUgdW5kb25lOyB0aGUgYnV0dG9uIGdyZXlzIG91dCB3aGVuIHRoZXJlJ3Mgbm90aGluZyBsZWZ0IHRvIHVuZG8uPC9saT5cbiAgICA8bGk+XCI8Yj5SZWRvPC9iPlwiIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL3JlZG8uc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+XG4gICAgICAoPGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+KzxrYmQ+U2hpZnQ8L2tiZD4rPGtiZD5aPC9rYmQ+IG9yIDxrYmQ+Q3RybDwva2JkPi88a2JkPkNtZDwva2JkPis8a2JkPlk8L2tiZD4pOlxuICAgICAgcmUtYXBwbGllcyB0aGUgbW9zdCByZWNlbnRseSB1bmRvbmUgYWN0aW9uLiBQZXJmb3JtaW5nIGEgbmV3IHJlZ2lvbiBhY3Rpb24gY2xlYXJzIHdoYXQgY2FuIGJlIHJlZG9uZS48L2xpPlxuICAgIDxsaT5cIjxiPkRlbGV0ZSByZWdpb248L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS10cmFzaFwiPjwvaT4gKDxrYmQ+RGVsPC9rYmQ+KTogZGVsZXRlcyB0aGUgc2VsZWN0ZWRcbiAgICAgIHJlZ2lvbi4gVGhpcyBjYW4gYWxzbyBiZSBkb25lIGZyb20gdGhlIFJlZ2lvbiBFZGl0b3IuPC9saT5cbiAgPC91bD5cblxuICA8aDM+UmVnaW9uIGFjdGlvbnMgKHJpZ2h0LWNsaWNrIHRoZSBpbWFnZSk8L2gzPlxuICA8cD5SaWdodC1jbGljayBhIHJlZ2lvbiDigJQgb3IgdGhlIGN1cnJlbnQgbXVsdGktc2VsZWN0aW9uIOKAlCB0byBvcGVuIHRoZVxuICAgIFwiPGI+U2VsZWN0ZWQgcmVnaW9uKHMpPC9iPlwiIGdyb3VwIGF0IHRoZSB0b3Agb2YgdGhlIGNvbnRleHQgbWVudS4gVGhlc2UgYWN0IGltbWVkaWF0ZWx5IG9uXG4gICAgdGhlIHNlbGVjdGlvbiAodGhleSdyZSBub3QgZHJhd2luZyBtb2RlcykgYW5kIGFyZSBmdWxseSB1bmRvL3JlZG8tYWJsZTo8L3A+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5TZWxlY3QgYWxsIHJlZ2lvbnM8L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1jaGVjay1zcXVhcmVcIj48L2k+OiBzZWxlY3QgZXZlcnkgcmVnaW9uIG9uXG4gICAgICB0aGUgaW1hZ2UgKGFsc28gaW4gdGhlIFJlZ2lvbiBFZGl0b3IgdG9vbGJhcikuPC9saT5cbiAgICA8bGk+XCI8Yj5NZXJnZSAvIGdyb3VwPC9iPlwiOiBjb21iaW5lIDIrIHNlbGVjdGVkIHJlZ2lvbnMgaW50byBvbmUuIElmIHRoZSBwaWVjZXMgZG9uJ3QgdG91Y2gsXG4gICAgICB0aGUgcmVzdWx0IGlzIGEgc2luZ2xlIG11bHRpLXBhcnQgcmVnaW9uIChzcGFyc2UgXCJiaXRzXCIpLjwvbGk+XG4gICAgPGxpPlwiPGI+VW5ncm91cDwvYj5cIjogc3BsaXQgYSBtdWx0aS1wYXJ0IHJlZ2lvbiBiYWNrIGludG8gb25lIHJlZ2lvbiBwZXIgcGllY2UuPC9saT5cbiAgICA8bGk+XCI8Yj5JbnZlcnNlPC9iPlwiOiByZXBsYWNlIHRoZSBzZWxlY3Rpb24gd2l0aCBldmVyeXRoaW5nIDxpPm91dHNpZGU8L2k+IGl0IOKAlCB0aGUgaW1hZ2VcbiAgICAgIHJlY3RhbmdsZSB3aXRoIHRoZSBzZWxlY3RlZCByZWdpb25zIHB1bmNoZWQgb3V0IGFzIGhvbGVzLiBXb3JrcyBhdCBhbnkgaW1hZ2Ugc2l6ZS48L2xpPlxuICAgIDxsaT5cIjxiPlNpbXBsaWZ5PC9iPlwiOiByZWR1Y2UgYSByZWdpb24ncyB2ZXJ0ZXggY291bnQgKERvdWdsYXPigJNQZXVja2VyKSBieSBhbiBhbHRpdHVkZVxuICAgICAgdGhyZXNob2xkIGluIHBpeGVscyDigJQgcGljayA8Yj5MaWdodDwvYj4gKDEmbmJzcDtweCksIDxiPk1lZGl1bTwvYj4gKDMmbmJzcDtweCksXG4gICAgICA8Yj5TdHJvbmc8L2I+ICg4Jm5ic3A7cHgpLCBvciA8Yj5DdXN0b23igKY8L2I+IGZvciBhIHNsaWRlci4gQXBwbGllcyB0byB0aGUgZXh0ZXJpb3IgYW5kIGFueVxuICAgICAgaG9sZXMgKGFuZCBldmVyeSBwYXJ0IG9mIGEgbXVsdGktcGFydCByZWdpb24pLjwvbGk+XG4gICAgPGxpPlRoZSBCw6l6aWVyIGNvbnZlcnNpb25zIGFuZCBEZWxldGUgYWxzbyBsaXZlIGluIHRoaXMgZ3JvdXAgKHRoZSByaWdodC1jbGljayBlcXVpdmFsZW50cyBvZlxuICAgICAgdGhlIHRvb2xiYXIgLyB2ZXJ0ZXggdG9vbHMgYWJvdmUpLjwvbGk+XG4gIDwvdWw+XG5cbiAgPGgzPlNlZ21lbnRhdGlvbiAmYW1wOyBkZXRlY3Rpb24gdG9vbHM8L2gzPlxuICA8cD5BSS1hc3Npc3RlZCBzZWdtZW50YXRpb24gYW5kIGRldGVjdGlvbiB0aGF0IHJ1biBlbnRpcmVseSBpbiB0aGUgYnJvd3Nlci4gVGhlIGZpcnN0IHJ1blxuICAgIG9mIGFueSBtb2RlbCBkb3dubG9hZHMgaXQgKGEgcHJvZ3Jlc3MgdG9hc3QgaXMgc2hvd24pOyBpdCdzIHRoZW4gY2FjaGVkLjwvcD5cbiAgPHA+VHdvIHNoYXBlcyBvZiB0b29sOiB0aGUgPGI+cHJvbXB0ZWQ8L2I+IG9uZXMgKFNlZ21lbnQsIFBvaW50IHByb21wdHMsIENlbGxwb3NlKSBhY3Qgb25cbiAgICByZWN0YW5nbGVzIHlvdSBkcmF3LCBhbmQgZ2VuZXJhdGVkIHJlZ2lvbnMgaW5oZXJpdCB0aGUgY29sb3Igb2YgdGhlIHJlY3RhbmdsZSB0aGV5IGNhbWVcbiAgICBmcm9tPHNwYW4gKm5nSWY9XCJjb250cmlidXRlZFRvb2xzLmxlbmd0aFwiPjsgdGhlIDxiPm5vLXByb21wdDwvYj4gb25lcyAoe3sgY29udHJpYnV0ZWRUb29sTmFtZXMgfX0pXG4gICAgc3dlZXAgdGhlIHdob2xlIGN1cnJlbnQgdmlldywgc28gdGhlcmUgaXMgbm90aGluZyB0byBkcmF3IGZpcnN0PC9zcGFuPi48L3A+XG4gIDxwPkVhY2ggbW9kZWwgZHJvcGRvd24gPGkgY2xhc3M9XCJwaSBwaS1jaGV2cm9uLWRvd25cIj48L2k+IGxpc3RzIHRoZSBhdmFpbGFibGUgY2hlY2twb2ludHMgd2l0aFxuICAgIGEgPGkgY2xhc3M9XCJwaSBwaS1pbmZvLWNpcmNsZVwiPjwvaT4gYmVzaWRlIGVhY2ggb25lIOKAlCBob3ZlciBpdCBmb3IgdGhhdCBtb2RlbCdzIHNpemUsIHNwZWVkLFxuICAgIHRyYWluaW5nIGRvbWFpbiBhbmQgYWNjdXJhY3ksIHNvIHlvdSBjYW4gcGljayB3aXRob3V0IGxlYXZpbmcgdGhlIHRvb2xiYXIuPC9wPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+U2VnbWVudDwvYj5cIiA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zYW0tYm94LXByb21wdC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj5cbiAgICAgIChib3ggcHJvbXB0KTogZHJhdyBvbmUgb3IgbW9yZSA8Yj5SZWN0YW5ndWxhciByZWdpb25zPC9iPiBhcm91bmQgdGhlIG9iamVjdHMgb2YgaW50ZXJlc3QsXG4gICAgICB0aGVuIGNsaWNrIFNlZ21lbnQuIEVhY2ggcmVjdGFuZ2xlIGlzIHJlcGxhY2VkIGJ5IHRoZSBTQU0gbWFzayBvZiB0aGUgb2JqZWN0IGluc2lkZSBpdC48L2xpPlxuICAgIDxsaT5cIjxiPlBvaW50IHByb21wdHM8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc2FtLXBvaW50LXByb21wdC5zdmdcIiB3aWR0aD1cIjE1XCIgaGVpZ2h0PVwiMTVcIj46IGNsaWNrIGRpcmVjdGx5IG9uIGFuIG9iamVjdCB0b1xuICAgICAgc2VnbWVudCBpdCBhcyBhIDxiPm5ldzwvYj4gcmVnaW9uLiBFYWNoIGNsaWNrIHN0YXJ0cyBhIGZyZXNoIG9iamVjdCDigJQgY2xpY2tpbmcgYW5vdGhlclxuICAgICAgb2JqZWN0IHdvbid0IGdyb3cgdGhlIHByZXZpb3VzIG9uZS4gPGtiZD5TaGlmdDwva2JkPi88a2JkPkFsdDwva2JkPi1jbGljayBhZGRzIGFuXG4gICAgICA8aT5leGNsdWRlPC9pPiBwb2ludCB0aGF0IHJlZmluZXMgdGhlIGN1cnJlbnQgb2JqZWN0OyA8a2JkPkVzYzwva2JkPiB1bmRvZXMgaXQsXG4gICAgICA8a2JkPkVudGVyPC9rYmQ+IGNvbW1pdHMgaXQuPC9saT5cbiAgICA8bGk+XCI8Yj5Nb2RlbCBwaWNrZXI8L2I+XCIgPGkgY2xhc3M9XCJwaSBwaS1jaGV2cm9uLWRvd25cIj48L2k+OiB0aGUgZHJvcGRvd24gYXR0YWNoZWQgdG8gdGhlXG4gICAgICBTZWdtZW50IGJ1dHRvbiBjaG9vc2VzIHRoZSBTQU0gbW9kZWwg4oCUIDxiPm1pY3JvLXNhbSBWaVQtVDwvYj4gKGRlZmF1bHQ7IHNtYWxsLCBmYXN0KSBvclxuICAgICAgPGI+bWljcm8tc2FtIFZpVC1CPC9iPiAobGFyZ2VyLCBoaWdoZXN0IGFjY3VyYWN5KSBmb3IgbGlnaHQgbWljcm9zY29weSwgb3JcbiAgICAgIDxiPnBhdGhvLXNhbSBWaVQtQjwvYj4gZm9yIGhpc3RvcGF0aG9sb2d5IChIJmFtcDtFIC8gc3RhaW5lZCB0aXNzdWUpLiBBbGwgYXJlIHByb21wdGFibGVcbiAgICAgIFNlZ21lbnQgQW55dGhpbmcgbW9kZWxzIGZpbmV0dW5lZCBmb3IgYmlvbWVkaWNhbCBpbWFnZXM7IHRoZXkgZGlmZmVyIGluIHRoZSA8aT5pbWFnZVxuICAgICAgZW5jb2RlcjwvaT4gKHRoZSBoZWF2eSBvbmNlLXBlci1pbWFnZSBzdGVwKSBhbmQgdGhlIHRyYWluaW5nIGRvbWFpbiDigJQgYm94IGFuZCBwb2ludFxuICAgICAgcHJvbXB0aW5nIGJlaGF2ZSB0aGUgc2FtZS4gVGhlIGNob2ljZSBhcHBsaWVzIHRvIDxpPmJvdGg8L2k+IHRoZSBib3ggYW5kIHBvaW50IHRvb2xzLCBhbmRcbiAgICAgIGVhY2ggdG9vbCBsb2FkcyBpdHMgb3duIGVuY29kZXIsIHNvIHRoZSBmaXJzdCBjbGljayBhZnRlciBzd2l0Y2hpbmcgcmUtZW5jb2RlcyBvbmNlLjwvbGk+XG4gICAgPGxpPlwiPGI+Q2VsbHBvc2U8L2I+XCIgPGltZyBzcmM9XCJhc3NldHMvcGxvdHRpbmcvY2VsbHMuc3ZnXCIgd2lkdGg9XCIxNVwiIGhlaWdodD1cIjE1XCI+OiBkcmF3IHJlY3RhbmdsZXMsIHRoZW4gY2xpY2sgQ2VsbHBvc2UgdG9cbiAgICAgIGF1dG9tYXRpY2FsbHkgc2VnbWVudCBldmVyeSBjZWxsIGluc2lkZSBlYWNoIHJlY3RhbmdsZSAoY2xpZW50LXNpZGUgY2VsbHBvc2UtU0FNKSDigJQgb25lXG4gICAgICByZWdpb24gcGVyIGRldGVjdGVkIGNlbGwuIE5vIGNsaWNraW5nIG9uIGluZGl2aWR1YWwgY2VsbHMgbmVlZGVkLjwvbGk+XG4gICAgPCEtLSBDb250cmlidXRlZCB0b29scyBkZXNjcmliZSB0aGVtc2VsdmVzLiBUaGlzIGxpYnJhcnkgc2hpcHMgbm9uZSwgc28gYW5cbiAgICAgICAgIG9wZW4gYnVpbGQncyBoZWxwIGxpc3RzIG9ubHkgdGhlIHByb21wdGVkIHRvb2xzIGFib3ZlIOKAlCB0aGUgdGV4dCBub1xuICAgICAgICAgbG9uZ2VyIHByb21pc2VzIGJ1dHRvbnMgdGhhdCBhcmUgbm90IHRoZXJlLiAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCB0b29sIG9mIGNvbnRyaWJ1dGVkVG9vbHNcIj5cbiAgICAgIDxsaSAqbmdJZj1cInRvb2wuaGVscFwiIFtpbm5lckhUTUxdPVwidG9vbC5oZWxwLmJvZHlcIj48L2xpPlxuICAgIDwvbmctY29udGFpbmVyPlxuICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IHRvb2wgb2YgZGlhbG9nVG9vbHNcIj5cbiAgICAgIDxsaSAqbmdJZj1cInRvb2wuaGVscFwiIFtpbm5lckhUTUxdPVwidG9vbC5oZWxwLmJvZHlcIj48L2xpPlxuICAgIDwvbmctY29udGFpbmVyPlxuICA8L3VsPlxuXG4gIDxoMz5TcGF0aWFsIG9taWNzPC9oMz5cbiAgPHA+XG4gICAgSW4gPGI+U3BhdGlhbCBvbWljczwvYj4gbW9kZSBlYWNoIG9ic2VydmF0aW9uIOKAlCBhIGNlbGwgb3IgYSBWaXNpdW0gc3BvdCDigJQgaXMgZHJhd24gYXRcbiAgICBpdHMgb3duIGNvb3JkaW5hdGVzLCBvdmVyIHRoZSB0aXNzdWUgaW1hZ2Ugd2hlcmUgdGhlIGRhdGFzZXQgcmVnaXN0ZXJzIG9udG8gb25lLiBUaGVcbiAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zcGF0aWFsLW9taWNzLnN2Z1wiIHdpZHRoPVwiMTRcIiBoZWlnaHQ9XCIxNFwiIGFsdD1cIlNwYXRpYWwgb21pY3NcIi8+XG4gICAgcGFuZWwgY29sb3VycyB0aGVtIGJ5IGEgPGI+Y29sdW1uPC9iPiAoYSBjZWxsIHR5cGUsIGEgY2x1c3Rlcikgb3IgYnkgYSA8Yj5nZW5lPC9iPiwgYW5kXG4gICAgdGhlIHR3byBhcmUgZXhjbHVzaXZlIHNvIHdoYXQgZHJpdmVzIHRoZSBjb2xvdXJzIGlzIG5ldmVyIGFtYmlndW91cy5cbiAgPC9wPlxuICA8dWw+XG4gICAgPGxpPlwiPGI+U2VsZWN0IGZyb20gUk9JczwvYj5cIjogdHVybnMgdGhlIHJlZ2lvbnMgeW91IGhhdmUgZHJhd24gaW50byBhIHNlbGVjdGlvbiBvZlxuICAgICAgb2JzZXJ2YXRpb25zLiBFdmVyeXRoaW5nIGVsc2UgdGhlbiBmb2xsb3dzIHRoYXQgc2VsZWN0aW9uLjwvbGk+XG4gICAgPGxpPlwiPGI+UG9pbnQgc2l6ZTwvYj5cIiAvIFwiPGI+T3BhY2l0eTwvYj5cIjogaG93IHRoZSBtYXJrZXJzIGFyZSBkcmF3biwgbm90IHdoYXQgaXNcbiAgICAgIHNlbGVjdGVkLjwvbGk+XG4gIDwvdWw+XG5cbiAgPGgzPkRpc3RyaWJ1dGlvbiBjaGFydHM8L2gzPlxuICA8cD5cbiAgICBUaGUgPGI+RGlzdHJpYnV0aW9uPC9iPiBzZWN0aW9uIG9mIHRoZSBzcGF0aWFsLW9taWNzIHBhbmVsIGNoYXJ0cyB3aGF0ZXZlciB0aGUgbWFwIGlzXG4gICAgY29sb3VyZWQgYnksIHNvIHRoZSB0d28gY2FuIG5ldmVyIGRpc2FncmVlLiBFYWNoIHRhYiBhbnN3ZXJzIGEgZGlmZmVyZW50IHF1ZXN0aW9uLCBhbmRcbiAgICB0aGUgPGkgY2xhc3M9XCJwaSBwaS1xdWVzdGlvbi1jaXJjbGVcIj48L2k+IGJlc2lkZSB0aGVtIGV4cGxhaW5zIHRoZSBvbmUgb24gc2NyZWVuIOKAlFxuICAgIGluY2x1ZGluZyB3aGF0IGl0IG11c3QgPGVtPm5vdDwvZW0+IGJlIHJlYWQgZm9yLlxuICA8L3A+XG4gIDx1bD5cbiAgICA8bGk+XCI8Yj5Db3VudHM8L2I+XCI6IGhvdyBtYW55IG9ic2VydmF0aW9ucyBwZXIgY2F0ZWdvcnkuIEEgY2F0ZWdvcnkgaXMgYSBsYWJlbCwgbm90IGFcbiAgICAgIG1hZ25pdHVkZSwgc28gYSBmcmVxdWVuY3kgaXMgdGhlIG9ubHkgZGlzdHJpYnV0aW9uIGl0IGhhcy48L2xpPlxuICAgIDxsaT5cIjxiPkhpc3RvZ3JhbTwvYj5cIiAvIFwiPGI+VmlvbGluPC9iPlwiIC8gXCI8Yj5Cb3g8L2I+XCI6IGhvdyBhIGNvbnRpbnVvdXMgdmFsdWUgaXNcbiAgICAgIGRpc3RyaWJ1dGVkIOKAlCBvdmVyIGV2ZXJ5dGhpbmcsIG9yIHNwbGl0IGJ5IGEgZ3JvdXBpbmcgY29sdW1uLjwvbGk+XG4gICAgPGxpPlwiPGI+SGVhdG1hcDwvYj5cIjogbWVhbiBleHByZXNzaW9uIG9mIHRoZSBnZW5lcyB5b3UgcGljayB3aXRoaW4gZWFjaCBncm91cC5cbiAgICAgIFotc2NvcmVkIHBlciBnZW5lIGJ5IGRlZmF1bHQsIHNvIGEgY29sb3VyIG1lYW5zIFwiYWJvdmUgb3IgYmVsb3cgdGhpcyBnZW5lJ3Mgb3duXG4gICAgICBhdmVyYWdlXCIgcmF0aGVyIHRoYW4gXCJoaWdobHkgZXhwcmVzc2VkXCIuPC9saT5cbiAgICA8bGk+XCI8Yj5FbWJlZGRpbmc8L2I+XCI6IGEgVU1BUCwgUENBIG9yIHQtU05FIG92ZXIgdGhlIHNhbWUgY2VsbHMuIDxiPkxhc3NvIGl0IHRvXG4gICAgICBzZWxlY3Qgb24gdGhlIG1hcDwvYj4g4oCUIGJvdGggdmlld3MgcmVhZCB0aGUgc2FtZSBzZWxlY3Rpb24uIE9ubHkgUENBIGxhYmVscyBpdHMgYXhlc1xuICAgICAgd2l0aCBhIHBlcmNlbnRhZ2UsIGJlY2F1c2Ugb25seSBpdHMgYXhlcyBleHBsYWluIGEgbWVhc3VyYWJsZSBzaGFyZSBvZiB0aGUgdmFyaWFuY2U7XG4gICAgICBVTUFQIGFuZCB0LVNORSBjb29yZGluYXRlcyBhcmUgYXJiaXRyYXJ5LCBzbyBkaXN0YW5jZSBiZXR3ZWVuIGRpc3RhbnQgY2x1c3RlcnNcbiAgICAgIHNob3VsZCBub3QgYmUgcmVhZC48L2xpPlxuICAgIDxsaT5cIjxiPkRldGFjaDwvYj5cIjogbW92ZXMgdGhlIGNoYXJ0cyBpbnRvIHRoZWlyIG93biB3aW5kb3cgc28gdGhleSBjYW4gc2l0IGJlc2lkZSB0aGVcbiAgICAgIG1hcCBpbnN0ZWFkIG9mIGluc2lkZSB0aGUgcGFuZWwuIFN3aXRjaGluZyB0YWJzIGtlZXBzIHRoZSB3aW5kb3cgd2hlcmUgaXQgaXMuPC9saT5cbiAgPC91bD5cblxuICA8aDM+SGVscDwvaDM+XG4gIDxwPlwiPGI+SGVscDwvYj5cIiA8aSBjbGFzcz1cInBpIHBpLXF1ZXN0aW9uLWNpcmNsZVwiPjwvaT46IG9wZW5zIHRoaXMgZGlhbG9nLjwvcD5cblxuICA8aDM+S2V5Ym9hcmQgc2hvcnRjdXRzPC9oMz5cbiAgPHRhYmxlIGNsYXNzPVwic2hvcnRjdXRzLXRhYmxlXCI+XG4gICAgPHRyPjx0ZD48a2JkPis8L2tiZD4gLyA8a2JkPj08L2tiZD48L3RkPjx0ZD5ab29tIGluPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD4tPC9rYmQ+PC90ZD48dGQ+Wm9vbSBvdXQ8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPmI8L2tiZD48L3RkPjx0ZD5Ub2dnbGUgem9vbSBib3ggbW9kZTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+cDwva2JkPjwvdGQ+PHRkPlRvZ2dsZSBwYW4gbW9kZTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+Zjwva2JkPjwvdGQ+PHRkPlRvZ2dsZSBmcmVlZm9ybSByZWdpb24gZHJhd2luZzwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+bDwva2JkPjwvdGQ+PHRkPlRvZ2dsZSBwb2x5bGluZSBkcmF3aW5nPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5yPC9rYmQ+PC90ZD48dGQ+VG9nZ2xlIHJlY3Rhbmd1bGFyIHJlZ2lvbiBkcmF3aW5nPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD53PC9rYmQ+PC90ZD48dGQ+VG9nZ2xlIHdhbmQgdG9vbDwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPldhbmQgKyA8a2JkPlNoaWZ0PC9rYmQ+PC90ZD48dGQ+RXJhc2UgcGl4ZWxzIGZyb20gdGhlIGFjdGl2ZSByZWdpb248L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD5XYW5kICsgPGtiZD5DdHJsPC9rYmQ+LzxrYmQ+Q21kPC9rYmQ+PC90ZD48dGQ+RXhhY3QtbWF0Y2ggZmxvb2QgZmlsbCAobm8gc21vb3RoaW5nKTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+ZTwva2JkPjwvdGQ+PHRkPlRvZ2dsZSB2ZXJ0ZXggZXJhc2VyPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5zPC9rYmQ+PC90ZD48dGQ+U2VsZWN0aW9uIG1vZGUgKGRlYWN0aXZhdGVzIGRyYXdpbmcgdG9vbHMpPC90ZD48L3RyPlxuICAgIDx0cj48dGQ+UG9pbnQgcHJvbXB0ICsgPGtiZD5TaGlmdDwva2JkPi88a2JkPkFsdDwva2JkPjwvdGQ+PHRkPkFkZCBhbiBleGNsdWRlIHBvaW50IChyZWZpbmUgdGhlIGN1cnJlbnQgb2JqZWN0KTwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+RW50ZXI8L2tiZD48L3RkPjx0ZD5Db21taXQgdGhlIGN1cnJlbnQgU0FNIHBvaW50IG9iamVjdDwvdGQ+PC90cj5cbiAgICA8dHI+PHRkPjxrYmQ+RXNjPC9rYmQ+PC90ZD48dGQ+VW5kbyB0aGUgY3VycmVudCBTQU0gcG9pbnQgb2JqZWN0PC90ZD48L3RyPlxuICAgIDx0cj48dGQ+PGtiZD5EZWw8L2tiZD4gLyA8a2JkPuKMqzwva2JkPjwvdGQ+PHRkPkRlbGV0ZSBzZWxlY3RlZCByZWdpb248L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4rPGtiZD5aPC9rYmQ+PC90ZD48dGQ+VW5kbyB0aGUgbGFzdCByZWdpb24gYWN0aW9uICh1cCB0byAxMCk8L3RkPjwvdHI+XG4gICAgPHRyPjx0ZD48a2JkPkN0cmw8L2tiZD4vPGtiZD5DbWQ8L2tiZD4rPGtiZD5TaGlmdDwva2JkPis8a2JkPlo8L2tiZD4gLyA8a2JkPkN0cmw8L2tiZD4rPGtiZD5ZPC9rYmQ+PC90ZD48dGQ+UmVkbyB0aGUgbGFzdCB1bmRvbmUgcmVnaW9uIGFjdGlvbjwvdGQ+PC90cj5cbiAgPC90YWJsZT5cblxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPHAtYnV0dG9uIGljb249XCJwaSBwaS1jaGVja1wiIChjbGljayk9XCJkaXNwbGF5SGVscERpYWxvZz1mYWxzZVwiIGxhYmVsPVwiT2tcIiBzdHlsZUNsYXNzPVwicC1idXR0b24tdGV4dFwiPlxuICAgIDwvcC1idXR0b24+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtZGlhbG9nPlxuIl19
405
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbGJhci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL3Rvb2xiYXIvdG9vbGJhci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2hHLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDOUMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFHaEQsT0FBTyxFQUNMLFFBQVEsRUFFUixpQkFBaUIsRUFDakIsVUFBVSxFQUNWLGtCQUFrQixFQUNsQixlQUFlLEVBQ2YsZUFBZSxFQUNmLHVCQUF1QixFQUN2QixjQUFjLEVBQ2QsZ0JBQWdCLEdBQ2pCLE1BQU0sd0JBQXdCLENBQUM7QUFDaEMsT0FBTyxFQUF5QixpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBR3ZGLE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxNQUFNLHFEQUFxRCxDQUFDO0FBQ2pHLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLG1EQUFtRCxDQUFDO0FBQzlGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDJDQUEyQyxDQUFDO0FBQ25GLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFD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O3NCQUF0QixNQUFNO2dCQUVHLGFBQWE7c0JBQXRCLE1BQU07Z0JBRUcsY0FBYztzQkFBdkIsTUFBTTtnQkFFRyxjQUFjO3NCQUF2QixNQUFNO2dCQUVHLE1BQU07c0JBQWYsTUFBTTtnQkFFRyxPQUFPO3NCQUFoQixNQUFNO2dCQUVHLG1CQUFtQjtzQkFBNUIsTUFBTTtnQkFFRyxrQkFBa0I7c0JBQTNCLE1BQU07Z0JBRUcsVUFBVTtzQkFBbkIsTUFBTTtnQkFFRyxlQUFlO3NCQUF4QixNQUFNO2dCQUVHLGdCQUFnQjtzQkFBekIsTUFBTTtnQkFFRyxZQUFZO3NCQUFyQixNQUFNO2dCQUVHLFVBQVU7c0JBQW5CLE1BQU07Z0JBRUcsVUFBVTtzQkFBbkIsTUFBTTtnQkFFRyxjQUFjO3NCQUF2QixNQUFNO2dCQUVHLGVBQWU7c0JBQXhCLE1BQU07Z0JBRUcsT0FBTztzQkFBaEIsTUFBTTtnQkFFRyxnQkFBZ0I7c0JBQXpCLE1BQU07Z0JBRUcsZUFBZTtzQkFBeEIsTUFBTTtnQkFFRyxjQUFjO3NCQUF2QixNQUFNO2dCQUVHLGNBQWM7c0JBQXZCLE1BQU07Z0JBRUcscUJBQXFCO3NCQUE5QixNQUFNO2dCQUVHLGVBQWU7c0JBQXhCLE1BQU07Z0JBRUcsd0JBQXdCO3NCQUFqQyxNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgRXZlbnRFbWl0dGVyLCBJbnB1dCwgT3V0cHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQnV0dG9uTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9idXR0b24nO1xuaW1wb3J0IHsgUmlwcGxlTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9yaXBwbGUnO1xuaW1wb3J0IHsgVG9vbGJhck1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvdG9vbGJhcic7XG5pbXBvcnQgeyBUb29sdGlwTW9kdWxlIH0gZnJvbSAncHJpbWVuZy90b29sdGlwJztcblxuaW1wb3J0IHsgSUltYWdlSW5mbyB9IGZyb20gJy4uL2NvbnRyYWN0cy9pbWFnZS5jb250cmFjdCc7XG5pbXBvcnQge1xuICBQbG90VHlwZSxcbiAgUGxvdFR5cGVJZCxcbiAgaXNCdWlsdGluUGxvdFR5cGUsXG4gIGlzTmFwYXJpM2QsXG4gIGlzTmFwYXJpSXNvc3VyZmFjZSxcbiAgaXNOYXBhcmlTdXJmYWNlLFxuICBpc05hcGFyaVNjYXR0ZXIsXG4gIE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFLFxuICBpc1NwYXRpYWxPbWljcyxcbiAgaXNTcGF0aWFsT21pY3MzZCxcbn0gZnJvbSAnLi4vY29udHJhY3RzL3Bsb3QtdHlwZSc7XG5pbXBvcnQgeyBUb29sYmFyVG9vbFZpc2liaWxpdHksIEFMTF9UT09MQkFSX1RPT0xTIH0gZnJvbSAnLi4vY29udHJhY3RzL3Rvb2xiYXItY29uZmlnJztcbmltcG9ydCB7IFRvb2xiYXJEaWFsb2dUb29sQ29udHJpYnV0aW9uLCBUb29sYmFyVG9vbENvbnRyaWJ1dGlvbiB9IGZyb20gJy4uL2NvbnRyYWN0cy90b29sYmFyLXRvb2wuY29udHJhY3QnO1xuaW1wb3J0IHsgUGxvdFR5cGVPcHRpb24gfSBmcm9tICcuLi9jb250cmFjdHMvcGxvdC10eXBlLWNvbnRyaWJ1dGlvbi5jb250cmFjdCc7XG5pbXBvcnQgeyBUb29sYmFySGVscERpYWxvZ0NvbXBvbmVudCB9IGZyb20gJy4vdG9vbGJhci1oZWxwLWRpYWxvZy90b29sYmFyLWhlbHAtZGlhbG9nLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBQbG90VHlwZVNlbGVjdG9yQ29tcG9uZW50IH0gZnJvbSAnLi9wbG90LXR5cGUtc2VsZWN0b3IvcGxvdC10eXBlLXNlbGVjdG9yLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBTdGFja0NvbnRyb2xzQ29tcG9uZW50IH0gZnJvbSAnLi9zdGFjay1jb250cm9scy9zdGFjay1jb250cm9scy5jb21wb25lbnQnO1xuaW1wb3J0IHsgVmlld0NvbnRyb2xzQ29tcG9uZW50IH0gZnJvbSAnLi92aWV3LWNvbnRyb2xzL3ZpZXctY29udHJvbHMuY29tcG9uZW50JztcbmltcG9ydCB7IFJlZ2lvblRvb2xzQ29tcG9uZW50IH0gZnJvbSAnLi9yZWdpb24tdG9vbHMvcmVnaW9uLXRvb2xzLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBTZWdtZW50YXRpb25Ub29sc0NvbXBvbmVudCB9IGZyb20gJy4vc2VnbWVudGF0aW9uLXRvb2xzL3NlZ21lbnRhdGlvbi10b29scy5jb21wb25lbnQnO1xuXG4vKipcbiAqIFByZXNlbnRhdGlvbmFsIHRvb2xiYXIgZm9yIHRoZSBwbG90dGluZyB2aWV3cG9ydC5cbiAqXG4gKiBSZW5kZXJzIHRoZSBwbG90LXR5cGUgc2VsZWN0b3IsIGNvbG9ybWFwL0xVVCBjb250cm9scywgc3RhY2sgbmF2aWdhdGlvbixcbiAqIGltYWdlIGFjdGlvbnMgKGRvd25sb2FkIC8gYXV0b3NjYWxlIC8gcGlwZWxpbmUpLCB2aWV3cG9ydCB6b29tL3BhbiwgdGhlXG4gKiBTdXJmYWNlLTNEIGNhbWVyYSBjb250cm9scywgYW5kIHRoZSByZWdpb24tZHJhd2luZyB0b29scyDigJQgcGx1cyB0aGUgcGxvdHRpbmdcbiAqIGhlbHAgZGlhbG9nLiBJdCBvd25zIG5vIHJlbmRlcmluZyBvciB2aXN1YWxpemF0aW9uIHN0YXRlOiB0aGUgaG9zdFxuICogKFZpc3VhbGl6ZXJDb21wb25lbnQpIHN0YXlzIHRoZSBvcmNoZXN0cmF0b3IsIHBhc3Npbmcgc3RhdGUgaW4gdmlhIGBASW5wdXRgIGFuZFxuICogaGFuZGxpbmcgZXZlcnkgYWN0aW9uIHZpYSBgQE91dHB1dGAuIFRoZSBzYW1lIGFjdGlvbnMgYXJlIGFsc28gZHJpdmVuIGJ5IHRoZVxuICogZGlhZ3JhbSdzIHJpZ2h0LWNsaWNrIGNvbnRleHQgbWVudSBhbmQga2V5Ym9hcmQgc2hvcnRjdXRzLCBzbyBrZWVwaW5nIHRoZVxuICogaGFuZGxlcnMgaW4gdGhlIGhvc3QgYXZvaWRzIGR1cGxpY2F0aW5nIHRoYXQgbG9naWMuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3Bsb3R0aW5nLXRvb2xiYXInLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIEJ1dHRvbk1vZHVsZSxcbiAgICBSaXBwbGVNb2R1bGUsXG4gICAgVG9vbGJhck1vZHVsZSxcbiAgICBUb29sdGlwTW9kdWxlLFxuICAgIFRvb2xiYXJIZWxwRGlhbG9nQ29tcG9uZW50LFxuICAgIFBsb3RUeXBlU2VsZWN0b3JDb21wb25lbnQsXG4gICAgU3RhY2tDb250cm9sc0NvbXBvbmVudCxcbiAgICBWaWV3Q29udHJvbHNDb21wb25lbnQsXG4gICAgUmVnaW9uVG9vbHNDb21wb25lbnQsXG4gICAgU2VnbWVudGF0aW9uVG9vbHNDb21wb25lbnQsXG4gIF0sXG4gIHRlbXBsYXRlVXJsOiAnLi90b29sYmFyLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vdG9vbGJhci5jb21wb25lbnQuc2NzcyddLFxuICAvLyBQdXJlbHkgQElucHV0L0BPdXRwdXQ6IHJlLWNoZWNrIG9ubHkgd2hlbiBhbiBpbnB1dCBjaGFuZ2VzIG9yIHRoZSB0b29sYmFyXG4gIC8vIGl0c2VsZiBoYW5kbGVzIGFuIGV2ZW50LCBub3Qgb24gZXZlcnkgYXBwIHRpY2sgKGl0cyB+MTUgZ2V0dGVycyByYW4gZWFjaFxuICAvLyB0aW1lKS4gQ29udHJvbHMgYmluZCBbbmdNb2RlbF0gb25lLXdheSBhbmQgcmVwb3J0IHRocm91Z2ggdGhlIG91dHB1dHMsIHNvXG4gIC8vIHRoZSBob3N0IHN0YXlzIHRoZSBvd25lciBvZiBldmVyeSB2YWx1ZSAoUlQtMzUpLlxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgVG9vbGJhckNvbXBvbmVudCB7XG4gIC8qKiBDdXJyZW50IGltYWdlIChnYXRlcyB3aGljaCBjb250cm9sIGdyb3VwcyBhcmUgc2hvd24pLiAqL1xuICBASW5wdXQoKSBpbWFnZUluZm86IElJbWFnZUluZm8gfCB1bmRlZmluZWQ7XG4gIC8qKiBBIHNwYXRpYWwgZGF0YXNldCBpcyBvbiBvZmZlcjogaXRzIHBsb3QgbW9kZXMgYXJlIGEgY2hvaWNlIGV2ZW4gd2l0aCBubyBpbWFnZSBsb2FkZWQuICovXG4gIEBJbnB1dCgpIGhhc1NwYXRpYWxEYXRhc2V0ID0gZmFsc2U7XG5cbiAgLyoqIFdoZXRoZXIgdGhlIHBsb3QtdHlwZSBkcm9wZG93biBzaG93czogZm9yIGFuIGltYWdlLCBvciBhIHNwYXRpYWwgZGF0YXNldCB3aXRoIG5vbmUuICovXG4gIHByb3RlY3RlZCBnZXQgc2hvd1Bsb3RUeXBlcygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISEodGhpcy5pbWFnZUluZm8gfHwgdGhpcy5oYXNTcGF0aWFsRGF0YXNldCkgJiYgISF0aGlzLnRvb2xzLnNwZWNpYWxUb29scztcbiAgfVxuICAvKiogUGxvdCB0eXBlcyB0aGUgYWN0aXZlIGJhY2tlbmQgYWR2ZXJ0aXNlcywgdGhlbiBhbnkgY29udHJpYnV0ZWQgbW9kZXMuICovXG4gIEBJbnB1dCgpIHBsb3RUeXBlT3B0aW9uczogUGxvdFR5cGVPcHRpb25bXSA9IFtdO1xuICAvKiogVGhlIHNlbGVjdG9yJ3MgdmFsdWU6IGEgYnVpbHQtaW4gdHlwZSBvciBhIGNvbnRyaWJ1dGVkIG1vZGUncyBpZC4gKi9cbiAgQElucHV0KCkgc2VsZWN0ZWRQbG90VHlwZTogUGxvdFR5cGVJZCA9IFBsb3RUeXBlLklNQUdFO1xuICAvKipcbiAgICogVGhlIGJ1aWx0LWluIHR5cGUgdGhhdCByZW5kZXJzIHtAbGluayBzZWxlY3RlZFBsb3RUeXBlfSDigJQgaXRzIGBiYXNlVHlwZWBcbiAgICogZm9yIGEgY29udHJpYnV0ZWQgbW9kZS4gRXZlcnkgdG9vbC9jb250cm9sIGRlY2lzaW9uIGJlbG93IHJlYWRzXG4gICAqIHtAbGluayBlZmZlY3RpdmVQbG90VHlwZX0sIHNvIGEgY29udHJpYnV0ZWQgbW9kZSBnZXRzIGV4YWN0bHkgaXRzIGJhc2VcbiAgICogdHlwZSdzIHRvb2xiYXIuIFdoZW4gdW5ib3VuZCwgYSBidWlsdC1pbiBzZWxlY3Rpb24gaXMgaXRzIG93biBiYXNlIGFuZFxuICAgKiBhbnl0aGluZyBlbHNlIGlzIHRyZWF0ZWQgYXMgdGhlIGRlZmF1bHQgSW1hZ2Ugdmlldy5cbiAgICovXG4gIEBJbnB1dCgpIGJhc2VQbG90VHlwZTogUGxvdFR5cGUgfCBudWxsID0gbnVsbDtcbiAgLyoqIElzb3N1cmZhY2UgYmFuZCBhcyBhIDDigJMyNTUgc2xpZGVyIHBvc2l0aW9uIChtYXBwZWQgb250byB0aGUgdm9sdW1lJ3MgcmVhbFxuICAgKiAgaW50ZW5zaXR5IHJhbmdlIGJ5IHRoZSByZW5kZXJlcikuIERlZmF1bHRzIHRvIHRoZSBmdWxsIHJhbmdlLiAqL1xuICBASW5wdXQoKSBpc29SYW5nZTogbnVtYmVyW10gPSBbMCwgMjU1XTtcbiAgLyoqIFRoZSBsYXN0IHNsaWNlIGluZGV4IG9mIGEgc3RhY2suICovXG4gIEBJbnB1dCgpIG1heEluZGV4ID0gMDtcbiAgLyoqIFRoZSBjdXJyZW50IHNsaWNlICgwLWJhc2VkKS4gKi9cbiAgQElucHV0KCkgekluZGV4ID0gMDtcbiAgLyoqIFRydWUgZm9yIGFueSAyRCAobm9uIFN1cmZhY2UtM0QpIHBsb3QgdHlwZS4gKi9cbiAgQElucHV0KCkgaXNIZWF0bWFwID0gdHJ1ZTtcbiAgLyoqXG4gICAqIFJlZ2lvbiB0b29scyBhcHBseSBldmVuIHRob3VnaCB0aGlzIGlzIGEgM0QgdmlldywgYmVjYXVzZSB0aGV5IGRyYXcgYVxuICAgKiBTQ1JFRU4tU1BBQ0UgbGFzc28gKHRoZSAzRCBzcGF0aWFsIGNsb3VkKS4gU2VwYXJhdGUgZnJvbSB7QGxpbmsgaXNIZWF0bWFwfSxcbiAgICogd2hpY2ggbWVhbnMgXCIyRCB2aWV3XCIgYW5kIGFsc28gZHJpdmVzIHRoZSB6b29tIHRvb2xzIGFuZCB0aGUgM0QgY2FtZXJhXG4gICAqIGNvbnRyb2xzIOKAlCBmbGlwcGluZyB0aGF0IHdvdWxkIHNob3cgMkQgcGFuL3pvb20gYW5kIGhpZGUgdGhlIG9yYml0IGNvbnRyb2xzLlxuICAgKi9cbiAgQElucHV0KCkgaXMzZFJlZ2lvbnMgPSBmYWxzZTtcbiAgLyoqIFRoZSBhcm1lZCB0b29sJ3MgbW9kZSAoem9vbSwgcGFuLCBhIGRyYXdpbmcgdG9vbCwgYHNhbVBvaW50YCwg4oCmKSwgb3IgbnVsbC4gKi9cbiAgQElucHV0KCkgYWN0aXZlRHJhZ01vZGU6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAvKiogVGhlIDNEIGNhbWVyYSBtb2RlIChgdHVybnRhYmxlYCAvIGBvcmJpdGAgLyDigKYpLiAqL1xuICBASW5wdXQoKSBhY3RpdmVTdXJmYWNlM2RNb2RlID0gJ3R1cm50YWJsZSc7XG4gIC8qKiBXaGV0aGVyIHRoZSBuYXBhcmkgM0QgYXhlcy9zY2FsZSBnaXptbyBpcyBjdXJyZW50bHkgc2hvd24gKGRyaXZlcyB0aGUgdG9nZ2xlJ3MgbG9vaykuICovXG4gIEBJbnB1dCgpIGF4ZXNWaXNpYmxlID0gdHJ1ZTtcbiAgLyoqIFRoZSBuYXBhcmkgc3VyZmFjZSB3aXJlZnJhbWUgaXMgb24uICovXG4gIEBJbnB1dCgpIHdpcmVmcmFtZUFjdGl2ZSA9IGZhbHNlO1xuICAvKiogQWN0aXZlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKDEgPSBGdWxsIOKApiA4ID0g4oWbOyBkZWZhdWx0IMK9KS4gKi9cbiAgQElucHV0KCkgcmVzb2x1dGlvblNjYWxlID0gTkFQQVJJX0RFRkFVTFRfREVDSU1BVEU7XG4gIC8qKiBNYWdpYy13YW5kIHNlbnNpdGl2aXR5LiAqL1xuICBASW5wdXQoKSB3YW5kU2Vuc2l0aXZpdHkgPSAyLjA7XG4gIC8qKiBCcnVzaCBkaWFtZXRlciBpbiBpbWFnZSBwaXhlbHMuICovXG4gIEBJbnB1dCgpIGJydXNoU2l6ZSA9IDQwO1xuICAvKiogVmVydGV4LWVyYXNlciByYWRpdXMgaW4gaW1hZ2UgcGl4ZWxzLiAqL1xuICBASW5wdXQoKSB2ZXJ0ZXhFcmFzZXJSYWRpdXMgPSAyMDtcbiAgLyoqIFdoZXRoZXIgYSByZWdpb24gYWN0aW9uIGlzIGF2YWlsYWJsZSB0byB1bmRvIChqaXQtdWkjODUpLiBHcmV5cyBvdXQgdGhlXG4gICAqICBVbmRvIGJ1dHRvbiB3aGVuIGZhbHNlIChub3RoaW5nIGRvbmUgeWV0LCBvciBhbGwgc3RlcHMgYWxyZWFkeSB1bmRvbmUpLiAqL1xuICBASW5wdXQoKSBjYW5VbmRvID0gZmFsc2U7XG4gIC8qKiBXaGV0aGVyIGFuIHVuZG9uZSByZWdpb24gYWN0aW9uIGlzIGF2YWlsYWJsZSB0byByZWRvIChqaXQtdWkjODUpLiBHcmV5c1xuICAgKiAgb3V0IHRoZSBSZWRvIGJ1dHRvbiB3aGVuIGZhbHNlLiAqL1xuICBASW5wdXQoKSBjYW5SZWRvID0gZmFsc2U7XG4gIC8qKiBUaGUgc2luZ2xlLWltYWdlIC8gc3RhY2sgY2hvaWNlcyBvZiB0aGUgUGxvdGx5IHN0YWNrIHRvZ2dsZS4gKi9cbiAgQElucHV0KCkgc3RhY2tPcHRpb25zOiB7IG5hbWU6IHN0cmluZzsgdmFsOiBzdHJpbmcgfVtdID0gW1xuICAgIHsgbmFtZTogJ1NpbmdsZSBpbWFnZScsIHZhbDogJ2ZhbHNlJyB9LFxuICAgIHsgbmFtZTogJ1N0YWNrJywgdmFsOiAndHJ1ZScgfSxcbiAgXTtcbiAgLyoqIFdoaWNoIHRvb2xiYXIgZ3JvdXBzIHRvIHNob3cuIERlZmF1bHRzIHRvIHRoZSBmdWxsIHRvb2xiYXI7IHRoZSBob3N0IGZvcndhcmRzXG4gICAqICB0aGUgY29uc3VtZXIncyBjaG9pY2UgKGUuZy4gdGhlIHBpcGVsaW5lIHNob3dzIG9ubHkgem9vbSArIHJlZ2lvbiB0b29scykuICovXG4gIEBJbnB1dCgpIHRvb2xzOiBSZXF1aXJlZDxUb29sYmFyVG9vbFZpc2liaWxpdHk+ID0gQUxMX1RPT0xCQVJfVE9PTFM7XG4gIC8qKiBDdXJyZW50IGltYWdlLXNtb290aGluZyBzdGF0ZSAoZHJpdmVzIHRoZSBTbW9vdGhlbiB0b2dnbGUgYnV0dG9uIGxvb2spLlxuICAgKiAgYGZhbHNlYCA9IG5lYXJlc3QtbmVpZ2hib3VyIChjcmlzcCByYXcgcGl4ZWxzKSwgdGhlIGRlZmF1bHQuICovXG4gIEBJbnB1dCgpIGltYWdlU21vb3RoaW5nRW5hYmxlZCA9IGZhbHNlO1xuICAvKiogU0FNIG1vZGVsIHBpY2tlciBvcHRpb25zICsgY3VycmVudCBzZWxlY3Rpb24gKGppdC11aSM5MCBQMSkuICovXG4gIEBJbnB1dCgpIHNhbU1vZGVsczogeyBpZDogc3RyaW5nOyBsYWJlbDogc3RyaW5nIH1bXSA9IFtdO1xuICAvKiogVGhlIHNlbGVjdGVkIFNBTSBtb2RlbCBpZC4gKi9cbiAgQElucHV0KCkgc2FtTW9kZWxJZCA9ICcnO1xuICAvKipcbiAgICogVG9vbHMgY29udHJpYnV0ZWQgdGhyb3VnaCB7QGxpbmsgVE9PTEJBUl9UT09MU30sIGFscmVhZHkgZmlsdGVyZWQgdG8gdGhlXG4gICAqIG9uZXMgd2l0aCBtb2RlbHMgYW5kIHNvcnRlZC4gVGhlIGhvc3QgcmVzb2x2ZXMgdGhlbSBzbyBpdCBjYW4gb3duIHRoZVxuICAgKiBwZXItdG9vbCBwYXJhbWV0ZXIgc3RhdGU7IHRoaXMgY29tcG9uZW50IG9ubHkgZHJhd3MgdGhlbS5cbiAgICovXG4gIEBJbnB1dCgpIGNvbnRyaWJ1dGVkVG9vbHM6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uW10gPSBbXTtcbiAgLyoqIERpYWxvZyB0b29scyAoVE9PTEJBUl9UT09MUywga2luZDogJ2RpYWxvZycpLCBzb3J0ZWQ7IHNob3duIGluIHRoZSBJbWFnZSB2aWV3LiAqL1xuICBASW5wdXQoKSBkaWFsb2dUb29sczogVG9vbGJhckRpYWxvZ1Rvb2xDb250cmlidXRpb25bXSA9IFtdO1xuICAvKiogSWQgb2YgdGhlIGRpYWxvZyB0b29sIHdob3NlIGRpYWxvZyBpcyBvcGVuLCBvciBudWxsLiAqL1xuICBASW5wdXQoKSBvcGVuRGlhbG9nVG9vbElkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgLyoqIEFjdGl2ZSBjaGVja3BvaW50IHBlciBjb250cmlidXRlZCB0b29sLCBrZXllZCBieSB0b29sIGlkLiAqL1xuICBASW5wdXQoKSB0b29sTW9kZWxJZHM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICAvKiogQSBwbG90IHR5cGUgb3IgY29udHJpYnV0ZWQgbW9kZSB3YXMgcGlja2VkLiAqL1xuICBAT3V0cHV0KCkgc2VsZWN0UGxvdFR5cGUgPSBuZXcgRXZlbnRFbWl0dGVyPFBsb3RUeXBlSWQ+KCk7XG4gIC8qKiBJbnRlbnNpdHkgKExJTkUpIG1vZGU6IGFkZCBhbm90aGVyIGNvbG9yZWQgbGluZSBST0kgKyBpbnNldCB0cmFjZS4gKi9cbiAgQE91dHB1dCgpIGFkZFByb2ZpbGVMaW5lID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogVG9nZ2xlIGltYWdlIHNtb290aGluZyAoYmlsaW5lYXIpIHZzIG5lYXJlc3QtbmVpZ2hib3VyIChjcmlzcCBwaXhlbHMpLiAqL1xuICBAT3V0cHV0KCkgdG9nZ2xlSW1hZ2VTbW9vdGhpbmcgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8vIFByaW1lTkcgc2xpZGVyL2lucHV0TnVtYmVyIGNoYW5nZSBldmVudHMgY2Fycnkgb3B0aW9uYWwgdmFsdWVzLCBtaXJyb3JlZCBieVxuICAvLyB0aGUgaG9zdCBoYW5kbGVycyAod2hpY2ggYWNjZXB0IGB8IHVuZGVmaW5lZGApLlxuICAvKiogVGhlIGlzb3N1cmZhY2UgYmFuZCBzbGlkZXIgbW92ZWQ6IHRoZSBuZXcgYFtsb3csIGhpZ2hdYCBwYWlyLiAqL1xuICBAT3V0cHV0KCkgaXNvUmFuZ2VDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlcltdIHwgdW5kZWZpbmVkPigpO1xuICAvKiogT3BlbiB0aGUgQ2hhbm5lbHMgJiBIaXN0b2dyYW0gZGlhbG9nIChicmlnaHRuZXNzL2NvbnRyYXN0LCBjb2xvcm1hcCwg4oCmKS4gKi9cbiAgQE91dHB1dCgpIG9wZW5DaGFubmVsSGlzdG9ncmFtID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogT3BlbiB0aGUgc3BhdGlhbC1vbWljcyBjb250cm9scyAoY29sb3VyLWJ5LCBsZWdlbmQsIHBvaW50IGRpc3BsYXkpLiAqL1xuICBAT3V0cHV0KCkgb3BlblNwYXRpYWxDb250cm9scyA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIE9wZW4gdGhlIFJlZ2lvbiBFZGl0b3IgZGlhbG9nLiAqL1xuICBAT3V0cHV0KCkgb3BlblJlZ2lvbkVkaXRvciA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIEEgc2luZ2xlLWltYWdlIC8gc3RhY2sgY2hvaWNlIHdhcyBwaWNrZWQuICovXG4gIEBPdXRwdXQoKSBzZWxlY3RTdGFja09wdGlvbiA9IG5ldyBFdmVudEVtaXR0ZXI8eyBuYW1lOiBzdHJpbmc7IHZhbDogc3RyaW5nIH0+KCk7XG4gIC8qKiBUaGUgbGl2ZSBzbGljZSBzY3J1YmJlciBtb3ZlZCAoZmlyZXMgd2hpbGUgZHJhZ2dpbmcpLiAqL1xuICBAT3V0cHV0KCkgelNjcnViID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIC8qKiBUaGUgbGl2ZSBzbGljZSBzY3J1YmJlciB3YXMgcmVsZWFzZWQgb24gYSBzbGljZS4gKi9cbiAgQE91dHB1dCgpIHpTbGlkZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyIHwgdW5kZWZpbmVkPigpO1xuICAvKiogQSBzbGljZSBudW1iZXIgd2FzIHR5cGVkLiAqL1xuICBAT3V0cHV0KCkgekluZGV4SW5wdXQgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlcj4oKTtcbiAgLyoqIFJlLXBsb3QgYXQgdGhlIHR5cGVkIHNsaWNlLiAqL1xuICBAT3V0cHV0KCkgcmVsb2FkQW5kUGxvdCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIERvd25sb2FkIHRoZSBjdXJyZW50IHZpZXcgYXMgUE5HLiAqL1xuICBAT3V0cHV0KCkgZG93bmxvYWRJbWFnZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIEZpdCB0aGUgaW1hZ2UgdG8gdGhlIHZpZXcuICovXG4gIEBPdXRwdXQoKSBhdXRvc2NhbGVJbWFnZSA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIEEgem9vbSAvIHBhbiAvIGRyYXdpbmcgdG9vbCBidXR0b24gd2FzIGNsaWNrZWQgKGl0cyBtb2RlKS4gKi9cbiAgQE91dHB1dCgpIHRvZ2dsZURyYWdNb2RlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIC8qKiBab29tIGluIG9uZSBzdGVwLiAqL1xuICBAT3V0cHV0KCkgem9vbUluID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogWm9vbSBvdXQgb25lIHN0ZXAuICovXG4gIEBPdXRwdXQoKSB6b29tT3V0ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogQSAzRCBjYW1lcmEgbW9kZSBidXR0b24gd2FzIGNsaWNrZWQgKGl0cyBtb2RlKS4gKi9cbiAgQE91dHB1dCgpIHRvZ2dsZVN1cmZhY2UzZE1vZGUgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgLyoqIFJlc2V0IHRoZSAzRCBjYW1lcmEuICovXG4gIEBPdXRwdXQoKSByZXNldFN1cmZhY2VDYW1lcmEgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBUb2dnbGUgdGhlIG5hcGFyaSAzRCBjb29yZGluYXRlLWF4ZXMgLyBzY2FsZSBnaXptbyBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVBeGVzID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogVG9nZ2xlIHRoZSBuYXBhcmkgc3VyZmFjZSB3aXJlZnJhbWUgKGVkZ2VzKSBvbi9vZmYuICovXG4gIEBPdXRwdXQoKSB0b2dnbGVXaXJlZnJhbWUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBDaGFuZ2UgdGhlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKHJlbG9hZHMgYXQgdGhlIG5ldyByZXNvbHV0aW9uKS4gKi9cbiAgQE91dHB1dCgpIHNlbGVjdFJlc29sdXRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlcj4oKTtcbiAgLyoqIERlbGV0ZSB0aGUgc2VsZWN0ZWQgcmVnaW9uLiAqL1xuICBAT3V0cHV0KCkgZGVsZXRlUmVnaW9uID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogVW5kbyB0aGUgbGFzdCByZWdpb24gYWN0aW9uIChqaXQtdWkjODUpLiAqL1xuICBAT3V0cHV0KCkgdW5kb1JlZ2lvbiA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcbiAgLyoqIFJlZG8gdGhlIGxhc3QgdW5kb25lIHJlZ2lvbiBhY3Rpb24gKGppdC11aSM4NSkuICovXG4gIEBPdXRwdXQoKSByZWRvUmVnaW9uID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogUnVuIGJveC1wcm9tcHRlZCBTQU0gc2VnbWVudGF0aW9uIG9uIHRoZSBkcmF3biByZWN0YW5nbGVzIChqaXQtdWkjOTApLiAqL1xuICBAT3V0cHV0KCkgc2VnbWVudFJlZ2lvbnMgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBSdW4gY2VsbHBvc2UtU0FNIChhdXRvKSBpbnNpZGUgZWFjaCBkcmF3biByZWN0YW5nbGUncyBjcm9wIChqaXQtdWkjOTApLiAqL1xuICBAT3V0cHV0KCkgc2VnbWVudENlbGxwb3NlID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogUnVuIGEgY29udHJpYnV0ZWQgdG9vbCBvdmVyIHRoZSBjdXJyZW50IHZpZXcsIGJ5IHRvb2wgaWQuICovXG4gIEBPdXRwdXQoKSBydW5Ub29sID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIC8qKiBBIGRpYWxvZyB0b29sJ3MgYnV0dG9uIHdhcyBjbGlja2VkOiBvcGVuIGl0cyBkaWFsb2csIG9yIGNsb3NlIGl0IGlmIG9wZW4uICovXG4gIEBPdXRwdXQoKSB0b2dnbGVEaWFsb2dUb29sID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIC8qKiBQaWNrIGEgY29udHJpYnV0ZWQgdG9vbCdzIGNoZWNrcG9pbnQuICovXG4gIEBPdXRwdXQoKSB0b29sTW9kZWxDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPHsgdG9vbElkOiBzdHJpbmc7IG1vZGVsSWQ6IHN0cmluZyB9PigpO1xuICAvKiogT3BlbiBhIGNvbnRyaWJ1dGVkIHRvb2wncyBwYXJhbWV0ZXIgZGlhbG9nLCBieSB0b29sIGlkLiAqL1xuICBAT3V0cHV0KCkgb3BlblRvb2xQYXJhbXMgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgLyoqIFNBTSBtb2RlbCBwaWNrZXIgKGppdC11aSM5MCBQMSkuICovXG4gIEBPdXRwdXQoKSBzYW1Nb2RlbENoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuICAvKiogVGhlIHdhbmQgc2Vuc2l0aXZpdHkgc2xpZGVyIG1vdmVkLiAqL1xuICBAT3V0cHV0KCkgd2FuZFNlbnNpdGl2aXR5Q2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXIgfCB1bmRlZmluZWQ+KCk7XG4gIC8qKiBUaGUgYnJ1c2ggc2l6ZSBzbGlkZXIgbW92ZWQuICovXG4gIEBPdXRwdXQoKSBicnVzaFNpemVDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcbiAgLyoqIFRoZSB2ZXJ0ZXgtZXJhc2VyIHJhZGl1cyBzbGlkZXIgbW92ZWQuICovXG4gIEBPdXRwdXQoKSB2ZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IHVuZGVmaW5lZD4oKTtcblxuICBwcm90ZWN0ZWQgZGlzcGxheUhlbHBEaWFsb2cgPSBmYWxzZTtcblxuICAvKiogdHJhY2tCeSBmb3IgdGhlIGRpYWxvZyB0b29scycgYnV0dG9ucy4gKi9cbiAgcHJvdGVjdGVkIHRyYWNrVG9vbEJ5SWQoX2luZGV4OiBudW1iZXIsIHRvb2w6IHsgaWQ6IHN0cmluZyB9KTogc3RyaW5nIHtcbiAgICByZXR1cm4gdG9vbC5pZDtcbiAgfVxuXG4gIC8qKiBUaGUgYnVpbHQtaW4gdHlwZSB3aG9zZSB0b29scyBhbmQgY29udHJvbHMgYXBwbHkgdG8gdGhlIGN1cnJlbnQgc2VsZWN0aW9uLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGVmZmVjdGl2ZVBsb3RUeXBlKCk6IFBsb3RUeXBlIHtcbiAgICBpZiAodGhpcy5iYXNlUGxvdFR5cGUpIHJldHVybiB0aGlzLmJhc2VQbG90VHlwZTtcbiAgICByZXR1cm4gaXNCdWlsdGluUGxvdFR5cGUodGhpcy5zZWxlY3RlZFBsb3RUeXBlKSA/IHRoaXMuc2VsZWN0ZWRQbG90VHlwZSA6IFBsb3RUeXBlLklNQUdFO1xuICB9XG5cbiAgLyoqIFRoZSBJbWFnZSBwbG90IHR5cGUgcmVuZGVycyBhcyBhIG5hdGl2ZWx5IHBhbi96b29tLWFibGUgcmFzdGVyLCBzbyB0aGVcbiAgICogIGdlbmVyaWMgem9vbS9wYW4gdG9vbHMgYXJlIGhpZGRlbi4gKi9cbiAgcHJvdGVjdGVkIGdldCBpc0ltYWdlVmlldygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gIH1cblxuICAvKiogRWl0aGVyIHNwYXRpYWwtb21pY3MgbW9kZSBpcyBhY3RpdmUsIHNvIGl0cyBjb250cm9scyBhcmUgd29ydGggb2ZmZXJpbmcuXG4gICAqICBUaGUgM0QgY2xvdWQgbmVlZHMgdGhlIHBhbmVsIE1PUkUgdGhhbiB0aGUgMkQgdmlldyBkb2VzLCBub3QgbGVzczogY29sb3VyaW5nLFxuICAgKiAgdGhlIGxlZ2VuZCBhbmQgY2F0ZWdvcnkgc2hvdy9oaWRlIGFyZSB0aGUgb25seSB3YXkgdG8gbWFrZSBzZW5zZSBvZiBhXG4gICAqICBtaWxsaW9uIG92ZXJsYXBwaW5nIHBvaW50cy4gKi9cbiAgcHJvdGVjdGVkIGdldCBpc1NwYXRpYWxNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc1NwYXRpYWxPbWljcyh0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKSB8fCBpc1NwYXRpYWxPbWljczNkKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpO1xuICB9XG5cbiAgLyoqIEJhY2tlbmRzIHdpdGggYSB2ZXJ0ZXgtZWRpdGluZyByZWdpb24gb3ZlcmxheTogT1NEIEltYWdlIGFuZCBuYXBhcmktanMgV2ViR1BVIGltYWdlXG4gICAqICAoaml0LXVpIzEwMikuIEdhdGVzIHRoZSBwb2x5Z29uIC8gYWRkLXZlcnRleCAvIGRlbGV0ZS12ZXJ0ZXggLyBCw6l6aWVyLWNvbnZlcnQgdG9vbHMuICovXG4gIHByb3RlY3RlZCBnZXQgc3VwcG9ydHNSZWdpb25WZXJ0ZXhUb29scygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0UgfHwgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFO1xuICB9XG5cbiAgLyoqIFBsb3QgdHlwZXMgdGhhdCBzY3J1YiBhIHotc3RhY2sgbGl2ZSAodGhlIHJlbmRlcmVyIHN3YXBzIHRoZSBzbGljZSBpbiBwbGFjZSk6IHRoZSBPU0QgSW1hZ2VcbiAgICogIHZpZXcsIHRoZSBuYXBhcmktanMgV2ViR1BVIGltYWdlLCB0aGUgbmFwYXJpLWpzIHN1cmZhY2Ug4oCUIHdoaWNoIHJlYnVpbGRzIHRoZSBoZWlnaHQgZmllbGRcbiAgICogIGZyb20gdGhlIHBpY2tlZCBzbGljZSAoaml0LXVpIzEwMikg4oCUIGFuZCB0aGUgMkQgc3BhdGlhbC1vbWljcyB2aWV3LCB3aGVyZSBhIDNEIGRhdGFzZXQnc1xuICAgKiAgcmVnaXN0ZXJlZCB2b2x1bWUgaXMgdGhlIGltYWdlIGFuZCB0aGUgc2xpZGVyIHBpY2tzIHRoZSBzZWN0aW9uIHdob3NlIG9ic2VydmF0aW9ucyBhcmVcbiAgICogIGRyYXduLiBEcml2ZXMgdGhlIHBlci1zbGljZSBzbGlkZXIuICovXG4gIHByb3RlY3RlZCBnZXQgc2hvd3NMaXZlU2xpY2VTY3J1YmJlcigpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0UgfHxcbiAgICAgIHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLk5BUEFSSV9JTUFHRSB8fFxuICAgICAgaXNOYXBhcmlTdXJmYWNlKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpIHx8XG4gICAgICBpc05hcGFyaVNjYXR0ZXIodGhpcy5lZmZlY3RpdmVQbG90VHlwZSkgfHxcbiAgICAgIGlzU3BhdGlhbE9taWNzKHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUpXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBBbnkgbmFwYXJpLWpzIFdlYkdQVSBwbG90IHR5cGUuIFRoZSBzaW5nbGUtaW1hZ2Uvc3RhY2sgdG9nZ2xlICsgc2xpY2UtbnVtYmVyIGZpZWxkIGFyZVxuICAgKiAgUGxvdGx5LW9ubHkgc3RhY2sgY29udHJvbHMsIHNvIHRoZXkncmUgaGlkZGVuIGZvciBuYXBhcmkgKGppdC11aSMxMDIpLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGlzTmFwYXJpTW9kZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5lZmZlY3RpdmVQbG90VHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFIHx8XG4gICAgICBpc05hcGFyaVNjYXR0ZXIodGhpcy5lZmZlY3RpdmVQbG90VHlwZSkgfHxcbiAgICAgIGlzTmFwYXJpM2QodGhpcy5lZmZlY3RpdmVQbG90VHlwZSlcbiAgICApO1xuICB9XG5cbiAgLyoqIElTT1NVUkZBQ0UgKFBsb3RseSBvciBuYXBhcmktanMgV2ViR1BVLCBlaXRoZXIgcmVzb2x1dGlvbikgc2hvd3MgdGhlIGlzb3ZhbHVlIHJhbmdlIHNsaWRlci4gKi9cbiAgcHJvdGVjdGVkIGdldCBpc0lzb3N1cmZhY2VNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5JU09TVVJGQUNFIHx8IGlzTmFwYXJpSXNvc3VyZmFjZSh0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBUaGUgbmFwYXJpLWpzIFdlYkdQVSBzdXJmYWNlIOKAlCBzaG93cyB0aGUgd2lyZWZyYW1lIHRvZ2dsZS4gKi9cbiAgcHJvdGVjdGVkIGdldCBpc05hcGFyaVN1cmZhY2VNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc05hcGFyaVN1cmZhY2UodGhpcy5lZmZlY3RpdmVQbG90VHlwZSk7XG4gIH1cblxuICAvKiogQW55IG5hcGFyaS1qcyBXZWJHUFUgM0QgdHlwZSAodm9sdW1lL2lzb3N1cmZhY2Uvc3VyZmFjZSkg4oCUIHNob3dzIHRoZSBSZXNvbHV0aW9uIGNvbnRyb2wuICovXG4gIHByb3RlY3RlZCBnZXQgaXNOYXBhcmkzZE1vZGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGlzTmFwYXJpM2QodGhpcy5lZmZlY3RpdmVQbG90VHlwZSk7XG4gIH1cblxuICAvKiogSW50ZW5zaXR5IHByb2ZpbGUgbGluZXMgYXJlIFJlZ2lvbi1iYXNlZCBhbmQgYXZhaWxhYmxlIGluIHRoZSBIZWF0bWFwIGFuZFxuICAgKiAgSW1hZ2UgcGxvdCB0eXBlcywgd2hpY2ggc2hvdyB0aGUgSW50ZW5zaXR5IHRvb2wgZ3JvdXAuICovXG4gIHByb3RlY3RlZCBnZXQgaXNJbnRlbnNpdHlDYXBhYmxlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmVmZmVjdGl2ZVBsb3RUeXBlID09PSBQbG90VHlwZS5IRUFUTUFQIHx8IHRoaXMuZWZmZWN0aXZlUGxvdFR5cGUgPT09IFBsb3RUeXBlLklNQUdFO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNob3dIZWxwKCk6IHZvaWQge1xuICAgIHRoaXMuZGlzcGxheUhlbHBEaWFsb2cgPSB0cnVlO1xuICB9XG59XG4iLCI8cC10b29sYmFyPlxuICA8ZGl2IGNsYXNzPVwicC10b29sYmFyLWdyb3VwLXN0YXJ0IGZsZXgtd3JhcCBhbGlnbi1pdGVtcy1jZW50ZXIgZ2FwLTFcIj5cbiAgICA8IS0tIEhvc3QtcHJvdmlkZWQgY3VzdG9tIHRvb2xiYXIgYnV0dG9ucywgc2hvd24gZmlyc3QgKGUuZy4gYXBwLXNwZWNpZmljXG4gICAgICAgICBhY3Rpb25zIHRoZSB2aXN1YWxpemF0aW9uIHZpZXcgaXRzZWxmIGRvZXNuJ3Qgb3duKS4gLS0+XG4gICAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuXG4gICAgPCEtLSBDb250cmlidXRlZCBkaWFsb2cgdG9vbHMgKFRPT0xCQVJfVE9PTFMsIGtpbmQ6ICdkaWFsb2cnKTogYXBwLWxldmVsIGFjdGlvbnNcbiAgICAgICAgIGxpa2UgdGhlIGhvc3QncyBvd24gYnV0dG9ucyBhYm92ZSwgc28gdGhleSBzaXQgcmlnaHQgYmVzaWRlIHRoZW0uIEltYWdlIHZpZXdcbiAgICAgICAgIG9ubHksIHRoZSB2aWV3IHRoZWlyIGRpYWxvZyBkcmF3cyBvdmVyLiBIaWdobGlnaHRlZCB3aGlsZSB0aGUgZGlhbG9nIGlzIG9wZW4uIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJpc0ltYWdlVmlld1wiPlxuICAgICAgPHAtYnV0dG9uXG4gICAgICAgICpuZ0Zvcj1cImxldCB0b29sIG9mIGRpYWxvZ1Rvb2xzOyB0cmFja0J5OiB0cmFja1Rvb2xCeUlkXCJcbiAgICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgW3RleHRdPVwib3BlbkRpYWxvZ1Rvb2xJZCAhPT0gdG9vbC5pZFwiXG4gICAgICAgIFtwVG9vbHRpcF09XCJ0b29sLnRvb2x0aXBcIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICBbYXJpYUxhYmVsXT1cInRvb2wubGFiZWxcIlxuICAgICAgICBbYXR0ci5hcmlhLXByZXNzZWRdPVwib3BlbkRpYWxvZ1Rvb2xJZCA9PT0gdG9vbC5pZFwiXG4gICAgICAgIChvbkNsaWNrKT1cInRvZ2dsZURpYWxvZ1Rvb2wuZW1pdCh0b29sLmlkKVwiXG4gICAgICA+XG4gICAgICAgIDxpbWdcbiAgICAgICAgICAqbmdJZj1cInRvb2wuaWNvbi5zcmNcIlxuICAgICAgICAgIFtzcmNdPVwidG9vbC5pY29uLnNyY1wiXG4gICAgICAgICAgc3R5bGU9XCJ3aWR0aDogMXJlbVwiXG4gICAgICAgICAgW2FsdF09XCJ0b29sLmxhYmVsXCJcbiAgICAgICAgICBbY2xhc3NdPVwib3BlbkRpYWxvZ1Rvb2xJZCA9PT0gdG9vbC5pZCA/ICd0b29sYmFyLWljb24tYWN0aXZlJyA6ICd0b29sYmFyLWljb24tZGFyaydcIlxuICAgICAgICAvPlxuICAgICAgICA8aSAqbmdJZj1cInRvb2wuaWNvbi5waVwiIGNsYXNzPVwicGkge3sgdG9vbC5pY29uLnBpIH19XCI+PC9pPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDwhLS0gUGxvdCB0eXBlOiBhdmFpbGFibGUgZm9yIGFueSBpbWFnZSAoZ3JheXNjYWxlIG9yIFJHQiksIGFuZCBmb3IgYSBzcGF0aWFsIGRhdGFzZXRcbiAgICAgICAgIHRoYXQgYnJpbmdzIG5vbmUgKGl0cyBzcGF0aWFsIG1vZGVzIGFyZSBzdGlsbCBhIGNob2ljZSkuIC0tPlxuICAgIDx0b29sYmFyLXBsb3QtdHlwZS1zZWxlY3RvclxuICAgICAgKm5nSWY9XCJzaG93UGxvdFR5cGVzXCJcbiAgICAgIFtvcHRpb25zXT1cInBsb3RUeXBlT3B0aW9uc1wiXG4gICAgICBbc2VsZWN0ZWRdPVwic2VsZWN0ZWRQbG90VHlwZVwiXG4gICAgICBbaXNvc3VyZmFjZV09XCJpc0lzb3N1cmZhY2VNb2RlXCJcbiAgICAgIFtpc29SYW5nZV09XCJpc29SYW5nZVwiXG4gICAgICBbaW50ZW5zaXR5TGluZXNdPVwiaXNJbnRlbnNpdHlDYXBhYmxlXCJcbiAgICAgIChzZWxlY3RQbG90VHlwZSk9XCJzZWxlY3RQbG90VHlwZS5lbWl0KCRldmVudClcIlxuICAgICAgKGlzb1JhbmdlQ2hhbmdlKT1cImlzb1JhbmdlQ2hhbmdlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAoYWRkUHJvZmlsZUxpbmUpPVwiYWRkUHJvZmlsZUxpbmUuZW1pdCgpXCJcbiAgICA+XG4gICAgPC90b29sYmFyLXBsb3QtdHlwZS1zZWxlY3Rvcj5cbiAgICA8IS0tIENoYW5uZWxzICYgSGlzdG9ncmFtOiBicmlnaHRuZXNzL2NvbnRyYXN0LCBnYW1tYSwgcGVyLWNoYW5uZWwgZGlzcGxheSxcbiAgICAgICAgIGNvbG9ybWFwL3JldmVyc2UvaW52ZXJ0IOKAlCBvcGVucyB0aGUgZmxvYXRpbmcgZGlhbG9nLiAtLT5cbiAgICA8cC1idXR0b25cbiAgICAgICpuZ0lmPVwidG9vbHMuc3BlY2lhbFRvb2xzXCJcbiAgICAgIHBSaXBwbGVcbiAgICAgIHNpemU9XCJzbWFsbFwiXG4gICAgICBbdGV4dF09XCJ0cnVlXCJcbiAgICAgIGFyaWFMYWJlbD1cIkNoYW5uZWxzICZhbXA7IEhpc3RvZ3JhbVwiXG4gICAgICBwVG9vbHRpcD1cIkNoYW5uZWxzICZhbXA7IEhpc3RvZ3JhbSAoYnJpZ2h0bmVzcy9jb250cmFzdCwgY29sb3JtYXApXCJcbiAgICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gICAgICAob25DbGljayk9XCJvcGVuQ2hhbm5lbEhpc3RvZ3JhbS5lbWl0KClcIlxuICAgID5cbiAgICAgIDxpbWcgc3JjPVwiYXNzZXRzL3Bsb3R0aW5nL2ludmVydC5zdmdcIiBjbGFzcz1cInRvb2xiYXItaWNvbi1kYXJrXCIgc3R5bGU9XCJ3aWR0aDogMXJlbVwiIC8+XG4gICAgPC9wLWJ1dHRvbj5cblxuICAgIDwhLS0gU3BhdGlhbC1vbWljcyBjb250cm9scy4gT25seSBtZWFuaW5nZnVsIGluIHRoZSBzcGF0aWFsIG1vZGUsIHdoaWNoIGlzXG4gICAgICAgICBpdHNlbGYgb25seSBvZmZlcmVkIHdoaWxlIGEgZGF0YXNldCBpcyBsb2FkZWQg4oCUIHNvIHRoaXMgYnV0dG9uIGFwcGVhcnNcbiAgICAgICAgIGV4YWN0bHkgd2hlbiB0aGVyZSBpcyBzb21ldGhpbmcgZm9yIGl0IHRvIGNvbnRyb2wuIC0tPlxuICAgIDxwLWJ1dHRvblxuICAgICAgKm5nSWY9XCJ0b29scy5zcGVjaWFsVG9vbHMgJiYgaXNTcGF0aWFsTW9kZVwiXG4gICAgICBwUmlwcGxlXG4gICAgICBzaXplPVwic21hbGxcIlxuICAgICAgW3RleHRdPVwidHJ1ZVwiXG4gICAgICBhcmlhTGFiZWw9XCJTcGF0aWFsIG9taWNzIGNvbnRyb2xzXCJcbiAgICAgIHBUb29sdGlwPVwiU3BhdGlhbCBvbWljcyAoY29sb3VyIGJ5IGNvbHVtbiBvciBnZW5lLCBsZWdlbmQsIHNlbGVjdGlvbiwgZGlzdHJpYnV0aW9ucylcIlxuICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgIChvbkNsaWNrKT1cIm9wZW5TcGF0aWFsQ29udHJvbHMuZW1pdCgpXCJcbiAgICA+XG4gICAgICA8aW1nIHNyYz1cImFzc2V0cy9wbG90dGluZy9zcGF0aWFsLW9taWNzLnN2Z1wiIGNsYXNzPVwidG9vbGJhci1pY29uLWRhcmtcIiBzdHlsZT1cIndpZHRoOiAxcmVtXCIgLz5cbiAgICA8L3AtYnV0dG9uPlxuXG4gICAgPCEtLSBTbW9vdGhlbiB0b2dnbGU6IGJpbGluZWFyIHNtb290aGluZyB2cyBuZWFyZXN0LW5laWdoYm91ciByYXcgcGl4ZWxzIChJbWFnZVxuICAgICAgICAgdmlldyBvbmx5IOKAlCBpdCdzIGFuIE9wZW5TZWFkcmFnb24gZHJhd2VyIHNldHRpbmcpLiBIaWdobGlnaHRlZCB3aGVuIHNtb290aGluZyBvbi4gLS0+XG4gICAgPHAtYnV0dG9uXG4gICAgICAqbmdJZj1cInRvb2xzLnNwZWNpYWxUb29scyAmJiBpc0ltYWdlVmlld1wiXG4gICAgICBzaXplPVwic21hbGxcIlxuICAgICAgW3RleHRdPVwiIWltYWdlU21vb3RoaW5nRW5hYmxlZFwiXG4gICAgICBhcmlhTGFiZWw9XCJTbW9vdGhlblwiXG4gICAgICBwVG9vbHRpcD1cIlNtb290aGVuICh0dXJuIG9uIGltYWdlIHNtb290aGluZylcIlxuICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgIChvbkNsaWNrKT1cInRvZ2dsZUltYWdlU21vb3RoaW5nLmVtaXQoKVwiXG4gICAgPlxuICAgICAgPGltZ1xuICAgICAgICBzcmM9XCJhc3NldHMvcGxvdHRpbmcvc21vb3RoLnN2Z1wiXG4gICAgICAgIHN0eWxlPVwid2lkdGg6IDFyZW1cIlxuICAgICAgICBbY2xhc3NdPVwiaW1hZ2VTbW9vdGhpbmdFbmFibGVkID8gJ3Rvb2xiYXItaWNvbi1hY3RpdmUnIDogJ3Rvb2xiYXItaWNvbi1kYXJrJ1wiXG4gICAgICAvPlxuICAgIDwvcC1idXR0b24+XG5cbiAgICA8IS0tIFN0YWNrIGNvbnRyb2xzIC0tPlxuICAgIDx0b29sYmFyLXN0YWNrLWNvbnRyb2xzXG4gICAgICAqbmdJZj1cImltYWdlSW5mbz8uaXNTdGFja1wiXG4gICAgICBbbGl2ZVNjcnViYmVyXT1cInNob3dzTGl2ZVNsaWNlU2NydWJiZXJcIlxuICAgICAgW3N0YWNrVG9nZ2xlXT1cIiFzaG93c0xpdmVTbGljZVNjcnViYmVyICYmICFpc05hcGFyaU1vZGUgJiYgIWlzU3BhdGlhbE1vZGVcIlxuICAgICAgW3Nob3dTdGFja109XCIhIWltYWdlSW5mbz8uc2hvd1N0YWNrXCJcbiAgICAgIFtzdGFja09wdGlvbnNdPVwic3RhY2tPcHRpb25zXCJcbiAgICAgIFt6SW5kZXhdPVwiekluZGV4XCJcbiAgICAgIFttYXhJbmRleF09XCJtYXhJbmRleFwiXG4gICAgICAoelNjcnViKT1cInpTY3J1Yi5lbWl0KCRldmVudClcIlxuICAgICAgKHpTbGlkZSk9XCJ6U2xpZGUuZW1pdCgkZXZlbnQpXCJcbiAgICAgIChzZWxlY3RTdGFja09wdGlvbik9XCJzZWxlY3RTdGFja09wdGlvbi5lbWl0KCRldmVudClcIlxuICAgICAgKHpJbmRleElucHV0KT1cInpJbmRleElucHV0LmVtaXQoJGV2ZW50KVwiXG4gICAgICAocmVsb2FkQW5kUGxvdCk9XCJyZWxvYWRBbmRQbG90LmVtaXQoKVwiXG4gICAgPlxuICAgIDwvdG9vbGJhci1zdGFjay1jb250cm9scz5cblxuICAgIDwhLS0gSW1hZ2UgYWN0aW9ucyAoc3BlY2lhbCB0b29scykgLS0+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cInRvb2xzLnNwZWNpYWxUb29sc1wiPlxuICAgICAgPHNwYW4gY2xhc3M9XCJ0b29sYmFyLXNlcGFyYXRvclwiPjwvc3Bhbj5cbiAgICAgIDxwLWJ1dHRvblxuICAgICAgICBpY29uPVwicGkgcGktZG93bmxvYWRcIlxuICAgICAgICBzaXplPVwic21hbGxcIlxuICAgICAgICBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgYXJpYUxhYmVsPVwiRG93bmxvYWQgaW1hZ2UgYXMgUE5HXCJcbiAgICAgICAgcFRvb2x0aXA9XCJEb3dubG9hZCBpbWFnZSBhcyBQTkdcIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAob25DbGljayk9XCJkb3dubG9hZEltYWdlLmVtaXQoKVwiXG4gICAgICA+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgICAgPHAtYnV0dG9uXG4gICAgICAgICpuZ0lmPVwiaXNIZWF0bWFwXCJcbiAgICAgICAgaWNvbj1cInBpIHBpLXdpbmRvdy1tYXhpbWl6ZVwiXG4gICAgICAgIHNpemU9XCJzbWFsbFwiXG4gICAgICAgIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICBhcmlhTGFiZWw9XCJGaXQgdG8gdmlld1wiXG4gICAgICAgIHBUb29sdGlwPVwiRml0IHRvIHZpZXdcIlxuICAgICAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgICAgICAob25DbGljayk9XCJhdXRvc2NhbGVJbWFnZS5lbWl0KClcIlxuICAgICAgPlxuICAgICAgPC9wLWJ1dHRvbj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDx0b29sYmFyLXZpZXctY29udHJvbHNcbiAgICAgIFtpc0hlYXRtYXBdPVwiaXNIZWF0bWFwXCJcbiAgICAgIFt6b29tVG9vbHNdPVwidG9vbHMuem9vbVRvb2xzXCJcbiAgICAgIFtpc0ltYWdlVmlld109XCJpc0ltYWdlVmlld1wiXG4gICAgICBbYWN0aXZlRHJhZ01vZGVdPVwiYWN0aXZlRHJhZ01vZGVcIlxuICAgICAgW2FjdGl2ZVN1cmZhY2UzZE1vZGVdPVwiYWN0aXZlU3VyZmFjZTNkTW9kZVwiXG4gICAgICBbaXNOYXBhcmlNb2RlXT1cImlzTmFwYXJpTW9kZVwiXG4gICAgICBbaXNOYXBhcmlTdXJmYWNlTW9kZV09XCJpc05hcGFyaVN1cmZhY2VNb2RlXCJcbiAgICAgIFtpc05hcGFyaTNkTW9kZV09XCJpc05hcGFyaTNkTW9kZVwiXG4gICAgICBbYXhlc1Zpc2libGVdPVwiYXhlc1Zpc2libGVcIlxuICAgICAgW3dpcmVmcmFtZUFjdGl2ZV09XCJ3aXJlZnJhbWVBY3RpdmVcIlxuICAgICAgW3Jlc29sdXRpb25TY2FsZV09XCJyZXNvbHV0aW9uU2NhbGVcIlxuICAgICAgKHRvZ2dsZURyYWdNb2RlKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAoem9vbUluKT1cInpvb21Jbi5lbWl0KClcIlxuICAgICAgKHpvb21PdXQpPVwiem9vbU91dC5lbWl0KClcIlxuICAgICAgKHRvZ2dsZVN1cmZhY2UzZE1vZGUpPVwidG9nZ2xlU3VyZmFjZTNkTW9kZS5lbWl0KCRldmVudClcIlxuICAgICAgKHJlc2V0U3VyZmFjZUNhbWVyYSk9XCJyZXNldFN1cmZhY2VDYW1lcmEuZW1pdCgpXCJcbiAgICAgICh0b2dnbGVBeGVzKT1cInRvZ2dsZUF4ZXMuZW1pdCgpXCJcbiAgICAgICh0b2dnbGVXaXJlZnJhbWUpPVwidG9nZ2xlV2lyZWZyYW1lLmVtaXQoKVwiXG4gICAgICAoc2VsZWN0UmVzb2x1dGlvbik9XCJzZWxlY3RSZXNvbHV0aW9uLmVtaXQoJGV2ZW50KVwiXG4gICAgPlxuICAgIDwvdG9vbGJhci12aWV3LWNvbnRyb2xzPlxuXG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIihpc0hlYXRtYXAgfHwgaXMzZFJlZ2lvbnMpICYmIHRvb2xzLnJlZ2lvblRvb2xzXCI+XG4gICAgICA8dG9vbGJhci1yZWdpb24tdG9vbHNcbiAgICAgICAgW2FjdGl2ZURyYWdNb2RlXT1cImFjdGl2ZURyYWdNb2RlXCJcbiAgICAgICAgW2lzM2RSZWdpb25zXT1cImlzM2RSZWdpb25zXCJcbiAgICAgICAgW3ZlcnRleFRvb2xzXT1cInN1cHBvcnRzUmVnaW9uVmVydGV4VG9vbHNcIlxuICAgICAgICBbd2FuZFNlbnNpdGl2aXR5XT1cIndhbmRTZW5zaXRpdml0eVwiXG4gICAgICAgIFticnVzaFNpemVdPVwiYnJ1c2hTaXplXCJcbiAgICAgICAgW3ZlcnRleEVyYXNlclJhZGl1c109XCJ2ZXJ0ZXhFcmFzZXJSYWRpdXNcIlxuICAgICAgICBbY2FuVW5kb109XCJjYW5VbmRvXCJcbiAgICAgICAgW2NhblJlZG9dPVwiY2FuUmVkb1wiXG4gICAgICAgIChvcGVuUmVnaW9uRWRpdG9yKT1cIm9wZW5SZWdpb25FZGl0b3IuZW1pdCgpXCJcbiAgICAgICAgKHRvZ2dsZURyYWdNb2RlKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICh1bmRvUmVnaW9uKT1cInVuZG9SZWdpb24uZW1pdCgpXCJcbiAgICAgICAgKHJlZG9SZWdpb24pPVwicmVkb1JlZ2lvbi5lbWl0KClcIlxuICAgICAgICAoZGVsZXRlUmVnaW9uKT1cImRlbGV0ZVJlZ2lvbi5lbWl0KClcIlxuICAgICAgICAod2FuZFNlbnNpdGl2aXR5Q2hhbmdlKT1cIndhbmRTZW5zaXRpdml0eUNoYW5nZS5lbWl0KCRldmVudClcIlxuICAgICAgICAoYnJ1c2hTaXplQ2hhbmdlKT1cImJydXNoU2l6ZUNoYW5nZS5lbWl0KCRldmVudClcIlxuICAgICAgICAodmVydGV4RXJhc2VyUmFkaXVzQ2hhbmdlKT1cInZlcnRleEVyYXNlclJhZGl1c0NoYW5nZS5lbWl0KCRldmVudClcIlxuICAgICAgPlxuICAgICAgPC90b29sYmFyLXJlZ2lvbi10b29scz5cbiAgICAgIDx0b29sYmFyLXNlZ21lbnRhdGlvbi10b29sc1xuICAgICAgICBbYWN0aXZlRHJhZ01vZGVdPVwiYWN0aXZlRHJhZ01vZGVcIlxuICAgICAgICBbc2FtTW9kZWxzXT1cInNhbU1vZGVsc1wiXG4gICAgICAgIFtzYW1Nb2RlbElkXT1cInNhbU1vZGVsSWRcIlxuICAgICAgICBbY29udHJpYnV0ZWRUb29sc109XCJjb250cmlidXRlZFRvb2xzXCJcbiAgICAgICAgW3Rvb2xNb2RlbElkc109XCJ0b29sTW9kZWxJZHNcIlxuICAgICAgICAoc2VnbWVudFJlZ2lvbnMpPVwic2VnbWVudFJlZ2lvbnMuZW1pdCgpXCJcbiAgICAgICAgKHNlZ21lbnRDZWxscG9zZSk9XCJzZWdtZW50Q2VsbHBvc2UuZW1pdCgpXCJcbiAgICAgICAgKHRvZ2dsZURyYWdNb2RlKT1cInRvZ2dsZURyYWdNb2RlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgIChzYW1Nb2RlbENoYW5nZSk9XCJzYW1Nb2RlbENoYW5nZS5lbWl0KCRldmVudClcIlxuICAgICAgICAocnVuVG9vbCk9XCJydW5Ub29sLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgICh0b29sTW9kZWxDaGFuZ2UpPVwidG9vbE1vZGVsQ2hhbmdlLmVtaXQoJGV2ZW50KVwiXG4gICAgICAgIChvcGVuVG9vbFBhcmFtcyk9XCJvcGVuVG9vbFBhcmFtcy5lbWl0KCRldmVudClcIlxuICAgICAgPlxuICAgICAgPC90b29sYmFyLXNlZ21lbnRhdGlvbi10b29scz5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJ0b29scy5oZWxwXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRvb2xiYXItc2VwYXJhdG9yXCI+PC9zcGFuPlxuXG4gICAgICA8IS0tIEhlbHAgLS0+XG4gICAgICA8cC1idXR0b25cbiAgICAgICAgaWNvbj1cInBpIHBpLXF1ZXN0aW9uLWNpcmNsZVwiXG4gICAgICAgIHNpemU9XCJzbWFsbFwiXG4gICAgICAgIFt0ZXh0XT1cInRydWVcIlxuICAgICAgICBhcmlhTGFiZWw9XCJIZWxwXCJcbiAgICAgICAgcFRvb2x0aXA9XCJIZWxwXCJcbiAgICAgICAgdG9vbHRpcFBvc2l0aW9uPVwiYm90dG9tXCJcbiAgICAgICAgKG9uQ2xpY2spPVwic2hvd0hlbHAoKVwiXG4gICAgICA+XG4gICAgICA8L3AtYnV0dG9uPlxuICAgIDwvbmctY29udGFpbmVyPlxuICA8L2Rpdj5cbjwvcC10b29sYmFyPlxuPHRvb2xiYXItaGVscC1kaWFsb2dcbiAgWyh2aXNpYmxlKV09XCJkaXNwbGF5SGVscERpYWxvZ1wiXG4gIFtjb250cmlidXRlZFRvb2xzXT1cImNvbnRyaWJ1dGVkVG9vbHNcIlxuICBbZGlhbG9nVG9vbHNdPVwiZGlhbG9nVG9vbHNcIlxuPlxuPC90b29sYmFyLWhlbHAtZGlhbG9nPlxuIl19