@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,28 +1,52 @@
1
- import { ChangeDetectorRef, Component, EventEmitter, HostListener, Inject, Injector, Input, NgZone, Optional, Output, ViewChild } from '@angular/core';
2
- import { Subject, Subscription } from 'rxjs';
3
- import { takeUntil } from 'rxjs/operators';
4
- import { MessageService } from 'primeng/api';
5
- import { ContextMenu } from 'primeng/contextmenu';
1
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, EventEmitter, Inject, Injector, Input, NgZone, Optional, Output, ViewChild, computed, signal, } from '@angular/core';
2
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
+ import { CommonModule } from '@angular/common';
4
+ import { FormsModule } from '@angular/forms';
5
+ import { Observable } from 'rxjs';
6
+ import { MessageService, SharedModule } from 'primeng/api';
7
+ import { ButtonModule } from 'primeng/button';
8
+ import { CheckboxModule } from 'primeng/checkbox';
9
+ import { ContextMenu, ContextMenuModule } from 'primeng/contextmenu';
10
+ import { DialogModule } from 'primeng/dialog';
11
+ import { DropdownModule } from 'primeng/dropdown';
12
+ import { InputNumberModule } from 'primeng/inputnumber';
13
+ import { ProgressBarModule } from 'primeng/progressbar';
14
+ import { SliderModule } from 'primeng/slider';
15
+ import { ToastModule } from 'primeng/toast';
16
+ import { TooltipModule } from 'primeng/tooltip';
6
17
  import { IMAGE_STATE_PORT } from './contracts/ports/image-state.port';
7
- import { Polygon, Rectangle, MultiPolygon } from './models/region';
8
18
  import { RegionOpsService } from './region-ops.service';
9
19
  import { VIZ_TOAST_KEY, VIZ_ALERT_TOAST_KEY } from './toast-outlets';
10
20
  import { VisualizerStore } from './store/visualizer-store.service';
11
- import { RenderOrchestrator, SliceScrubber } from './render-orchestrator';
21
+ import { SliceScrubber } from './render-orchestrator';
22
+ import { ImageRenderSession, errorMessage } from './render-session';
12
23
  import { PlotType, getPlotTypeDescriptor, isBuiltinPlotType, isNapari3d, isSpatialOmics, isSpatialOmics3d, rendererOwnsWheel, NAPARI_DEFAULT_DECIMATE, } from './contracts/plot-type';
13
- import { PLOT_MODE_CONTEXT, PLOT_MODE_SESSION, PLOT_TYPE_CONTRIBUTIONS, contributedPlotTypeOption, } from './contracts/plot-type-contribution.contract';
14
- import { PlotModeController } from './plot-mode/plot-mode-controller';
15
- import { ViewerFeature } from './contracts/capabilities.contract';
24
+ import { PLOT_TYPE_CONTRIBUTIONS, } from './contracts/plot-type-contribution.contract';
25
+ import { computePlotTypeMenu, reconcilePlotType } from './plot-mode/plot-type-menu';
26
+ import { ToolParamsModel } from './plot-mode/tool-params-model';
27
+ import { ContributionHost } from './plot-mode/contribution-host';
16
28
  import { VISUALIZER } from './contracts/visualizer.contract';
17
29
  import { SAM_MODELS, getDefaultSamModelId, isSamModelReady } from './toolbar/segmentation/sam-model-registry';
18
30
  import { SamToolService } from './toolbar/segmentation/sam-tool.service';
19
31
  import { SamPointToolService } from './toolbar/segmentation/sam-point-tool.service';
20
32
  import { CellSegmentToolService } from './toolbar/segmentation/cell-segment-tool.service';
21
- import { TOOLBAR_TOOLS, dialogToolContributions, visibleToolContributions, } from './contracts/toolbar-tool.contract';
33
+ import { SegmentationRunner } from './toolbar/segmentation/segmentation-runner';
34
+ import { TOOLBAR_TOOLS, visibleToolContributions, } from './contracts/toolbar-tool.contract';
22
35
  import { ALL_TOOLBAR_TOOLS } from './contracts/toolbar-config';
23
36
  import { VIZ_CONFIG } from './contracts/viz-config';
24
37
  import { SPATIAL_DATA_PORT } from './contracts/ports/spatial-data.port';
25
- import { buildVolumeStackImage } from './spatial/spatial-volume-image';
38
+ import { applyImageRois } from './visualizer/region-load';
39
+ import { buildContextMenu } from './visualizer/visualizer-context-menu';
40
+ import { RegionActions } from './visualizer/region-actions';
41
+ import { ToolModes } from './visualizer/tool-modes';
42
+ import { ViewerShortcuts } from './visualizer/viewer-shortcuts';
43
+ import { FloatingDragDirective } from './visualizer/floating-drag.directive';
44
+ import { IntensityInsetComponent } from './intensity-inset/intensity-inset.component';
45
+ import { SpatialDatasetBinder } from './visualizer/spatial-dataset-binder';
46
+ import { ToolbarComponent } from './toolbar/toolbar.component';
47
+ import { RegionEditorComponent } from './region-editor/region-editor.component';
48
+ import { ChannelHistogramComponent } from './channel-histogram/channel-histogram.component';
49
+ import { SpatialControlsComponent } from './spatial-controls/spatial-controls.component';
26
50
  import * as i0 from "@angular/core";
27
51
  import * as i1 from "primeng/api";
28
52
  import * as i2 from "./store/visualizer-store.service";
@@ -33,24 +57,31 @@ import * as i6 from "./region-ops.service";
33
57
  import * as i7 from "@angular/common";
34
58
  import * as i8 from "@angular/forms";
35
59
  import * as i9 from "primeng/button";
36
- import * as i10 from "primeng/tooltip";
37
- import * as i11 from "primeng/dropdown";
38
- import * as i12 from "primeng/slider";
39
- import * as i13 from "primeng/inputnumber";
40
- import * as i14 from "primeng/dialog";
41
- import * as i15 from "primeng/contextmenu";
60
+ import * as i10 from "primeng/checkbox";
61
+ import * as i11 from "primeng/contextmenu";
62
+ import * as i12 from "primeng/tooltip";
63
+ import * as i13 from "primeng/dialog";
64
+ import * as i14 from "primeng/dropdown";
65
+ import * as i15 from "primeng/inputnumber";
42
66
  import * as i16 from "primeng/progressbar";
43
- import * as i17 from "primeng/toast";
44
- import * as i18 from "primeng/checkbox";
45
- import * as i19 from "./toolbar/toolbar.component";
46
- import * as i20 from "./region-editor/region-editor.component";
47
- import * as i21 from "./channel-histogram/channel-histogram.component";
48
- import * as i22 from "./spatial-controls/spatial-controls.component";
67
+ import * as i17 from "primeng/slider";
68
+ import * as i18 from "primeng/toast";
49
69
  /** Per-instance plot-div id source. The mount element's id must be unique so two
50
70
  * live viewers (e.g. the main diagram + a modal preview) don't collide on the
51
71
  * same DOM id — `getElementById` would otherwise return whichever came first.
52
72
  * Styling hangs off the `.viz-plot` class instead of the id. */
53
73
  let plotInstanceSeq = 0;
74
+ /**
75
+ * The viewer (`<visualizer>`): the plot surface, its toolbar, the loading overlay and
76
+ * the dialogs it opens (Channels & Histogram, spatial-omics controls, Region Editor,
77
+ * contributed modes and tools). It orchestrates rendering through the `VISUALIZER`
78
+ * contract and reads the image from the host's `IMAGE_STATE_PORT`.
79
+ *
80
+ * OnPush. Template events re-render it; state that lands asynchronously goes through
81
+ * {@link changed} (or a signal: the loading overlay), and the collaborators that must
82
+ * render synchronously (the render session's overlay, a contributed panel's slot, the
83
+ * context menu before it opens) still call `detectChanges()`.
84
+ */
54
85
  export class VisualizerComponent {
55
86
  /** Per-instance toast key. MessageService is a global singleton, so two live
56
87
  * `<visualizer>` instances (main viewer + pipeline-dialog preview) sharing
@@ -91,11 +122,45 @@ export class VisualizerComponent {
91
122
  get toolbarTools() {
92
123
  return this._toolbarTools;
93
124
  }
94
- /** A jit-service cache copy is actively in progress (determinate 0..99). Drives the cache
95
- * progress bar independently of `imgLoading` so the bar can't be dropped — or masked by the
96
- * "Sharpening preview..." overlay — mid-download if the loading flag flips early. */
97
- get isCaching() {
98
- return this.cacheProgress !== null && this.cacheProgress < 100;
125
+ get activeDragMode() {
126
+ return this.tools.active;
127
+ }
128
+ set activeDragMode(mode) {
129
+ this.tools.active = mode;
130
+ }
131
+ get wandSensitivity() {
132
+ return this.tools.wandSensitivity;
133
+ }
134
+ set wandSensitivity(v) {
135
+ this.tools.wandSensitivity = v;
136
+ }
137
+ get brushSize() {
138
+ return this.tools.brushSize;
139
+ }
140
+ get vertexEraserRadius() {
141
+ return this.tools.vertexEraserRadius;
142
+ }
143
+ set vertexEraserRadius(v) {
144
+ this.tools.vertexEraserRadius = v;
145
+ }
146
+ /** Custom-threshold Simplify dialog visibility (see {@link RegionActions}). */
147
+ get displaySimplifyDialog() {
148
+ return this.regionActions.displaySimplifyDialog;
149
+ }
150
+ set displaySimplifyDialog(v) {
151
+ this.regionActions.displaySimplifyDialog = v;
152
+ }
153
+ /** Dialog tools registered through {@link TOOLBAR_TOOLS} (`kind: 'dialog'`). */
154
+ get dialogTools() {
155
+ return this.contributions.dialogTools;
156
+ }
157
+ /** The dialog tool the user has open (its session may be between renders). */
158
+ get openDialogToolId() {
159
+ return this.contributions.openDialogToolId;
160
+ }
161
+ /** The open dialog tool's live dialog, as the template renders it. */
162
+ get toolDialog() {
163
+ return this.contributions.toolDialog;
99
164
  }
100
165
  /**
101
166
  * The built-in plot type being rendered. With no contributed mode active this
@@ -109,11 +174,17 @@ export class VisualizerComponent {
109
174
  set selectedPlotType(type) {
110
175
  this.selectedPlotTypeId = type;
111
176
  }
177
+ /** Contributed plot modes and their single live session. */
178
+ get plotModes() {
179
+ return this.contributions.plotModes;
180
+ }
181
+ /** The live contributed mode's side panel; null while no contributed session is live. */
182
+ get plotModePanel() {
183
+ return this.contributions.plotModePanel;
184
+ }
112
185
  /** Where a `mount` panel's host element is attached once the dialog renders. */
113
186
  set plotModePanelSlot(ref) {
114
- const host = this.plotModePanel?.host;
115
- if (ref && host && host.parentElement !== ref.nativeElement)
116
- ref.nativeElement.appendChild(host);
187
+ this.contributions.attachPanelSlot(ref?.nativeElement);
117
188
  }
118
189
  /** The built-in type actually rendered for {@link selectedPlotTypeId}: itself
119
190
  * for a built-in type, `baseType` for a contributed mode. */
@@ -126,17 +197,20 @@ export class VisualizerComponent {
126
197
  get isSpatial3dMode() {
127
198
  return isSpatialOmics3d(this.basePlotType);
128
199
  }
129
- /** LINE plot type shows the image + draggable line ROI + intensity inset. */
130
- get isProfileMode() {
131
- return this.basePlotType === PlotType.LINE;
132
- }
133
200
  /** True for the Image plot type, which renders as a natively pan/zoom-able
134
201
  * raster — so the backend-agnostic zoom/pan toolbar tools are hidden. The
135
202
  * component drives this off the plot type, not the active backend. */
136
203
  get isImageView() {
137
204
  return this.basePlotType === PlotType.IMAGE;
138
205
  }
139
- constructor(state, plotService, messageService, ngZone, cdr, session, samTool, cellSegmentTool, samPointTool, regionOps, vizConfig,
206
+ /** Whether a spatial-omics dataset is published on `SPATIAL_DATA_PORT` — gates the
207
+ * spatial plot types; the toolbar offers the plot modes for an image-less one too. */
208
+ get hasSpatialDataset() {
209
+ return this.spatial.hasDataset;
210
+ }
211
+ constructor(
212
+ // The constructor parameters below are the injection points; none is public API.
213
+ state, plotService, messageService, ngZone, cdr, session, samTool, cellSegmentTool, samPointTool, regionOps, vizConfig,
140
214
  // A constructor parameter rather than a field `inject()`: this component is
141
215
  // also constructed directly with `new` in its own specs, which is outside
142
216
  // an injection context and would throw NG0203 on a field initializer.
@@ -149,7 +223,12 @@ export class VisualizerComponent {
149
223
  plotTypeContributions,
150
224
  // Parent of a contributed panel component's injector. Optional only so the
151
225
  // specs can construct this component with `new`.
152
- injector) {
226
+ injector,
227
+ // The `<visualizer>` element: keyboard shortcuts are scoped to it (CORE-2).
228
+ hostRef,
229
+ // Ends the subscriptions (`takeUntilDestroyed`). A parameter, like the rest, so the
230
+ // specs' `new` can pass one; Angular always provides it.
231
+ destroyRef = undefined) {
153
232
  this.state = state;
154
233
  this.plotService = plotService;
155
234
  this.messageService = messageService;
@@ -163,9 +242,13 @@ export class VisualizerComponent {
163
242
  this.vizConfig = vizConfig;
164
243
  this.spatialData = spatialData;
165
244
  this.injector = injector;
245
+ this.hostRef = hostRef;
246
+ this.destroyRef = destroyRef;
166
247
  this.vizToastKey = VIZ_TOAST_KEY;
167
248
  this.vizAlertToastKey = VIZ_ALERT_TOAST_KEY;
249
+ /** Each new image: whether it is a z-stack. */
168
250
  this.isStackEvent = new EventEmitter(false);
251
+ /** Each new image: whether it is single-channel (grayscale). */
169
252
  this.isGrayscaleEvent = new EventEmitter(false);
170
253
  this._toolbarTools = ALL_TOOLBAR_TOOLS;
171
254
  /** Expose test-only plot modes in the selector (e.g. the napari `Image`
@@ -175,55 +258,43 @@ export class VisualizerComponent {
175
258
  /** Image-smoothing state for the toolbar's Smoothen toggle (OSD only).
176
259
  * Defaults to `false` so OSD shows raw pixels (nearest-neighbour). */
177
260
  this.imageSmoothingEnabled = false;
178
- this.loadingMessage = 'Loading image...';
179
- this.zoom = false;
180
- this.unsub = new Subject();
181
- this.previewSubscription = new Subscription();
182
- this.stackLoading = false;
183
- this.imgLoading = false;
184
- this.loadingPercentage = 0;
261
+ // ── The loading overlay, as signals: it is fed by the host's and the backends' streams
262
+ // and by the render session, all of which land outside this view's template events. ──
263
+ this.loadingMessage = signal('Loading image...');
264
+ /** The host is loading at zoom (the overlay message moves out of the way). */
265
+ this.zoom = signal(false);
266
+ this.stackLoading = signal(false);
267
+ this.imgLoading = signal(false);
268
+ this.loadingPercentage = signal(0);
185
269
  // Non-null (0..100) while jit-service is copying the source file into the
186
270
  // local cache PVC on first click. When set, the loading overlay swaps the
187
271
  // spinner for a determinate progress bar with a "Caching image" message.
188
- this.cacheProgress = null;
189
- // True between the small-tier render landing and the large-tier render
190
- // landing in the multi-tier rendering path. The template uses this to
191
- // overlay a translucent spinner on top of the blurry small-tier image so
192
- // the user doesn't mistake it for the final preview.
193
- this.sharpening = false;
194
- this.running = false;
195
- /**
196
- * Monotonic render generation. Bumped when a render starts, so a render that
197
- * has been superseded by a newer image can be recognised and made inert: its
198
- * callbacks return early instead of painting, clearing `running`, releasing
199
- * the newer render's overlay, or applying its ROIs.
200
- */
201
- this.renderToken = 0;
202
- /** Set once the template, and with it the plot div, exists (see ngAfterViewInit). */
203
- this.viewReady = false;
204
- /** An image-less dataset whose draw waits for the view: see plotSpatialWithoutImage. */
205
- this.pendingSpatialDraw = null;
272
+ this.cacheProgress = signal(null);
273
+ /** A jit-service cache copy is actively in progress (determinate 0..99). Drives the cache
274
+ * progress bar independently of `imgLoading` so the bar can't be dropped — or masked by the
275
+ * "Sharpening preview..." overlay — mid-download if the loading flag flips early. */
276
+ this.isCaching = computed(() => {
277
+ const p = this.cacheProgress();
278
+ return p !== null && p < 100;
279
+ });
280
+ /** Emits once when this view is destroyed; the collaborators end their subscriptions on it. */
281
+ this.destroyed$ = new Observable((subscriber) => this.destroyRef.onDestroy(() => {
282
+ subscriber.next();
283
+ subscriber.complete();
284
+ }));
285
+ /** Host image-info objects whose one-shot `initialZIndex` was already applied. */
286
+ this.consumedSliceHints = new WeakSet();
206
287
  this.zIndex = 0;
207
288
  this.maxIndex = 0;
208
289
  this.contextMenuItems = [];
209
- this.activeDragMode = null;
210
- /** Whether a region action is available to undo (jit-ui#85). Mirrors the
211
- * shared RegionStore's history depth; drives the toolbar Undo button. */
212
- this.canUndoRegion = false;
213
- /** Whether an undone region action is available to redo (jit-ui#85). */
214
- this.canRedoRegion = false;
215
- /** Custom-threshold Simplify dialog (jit-ui#85). */
216
- this.displaySimplifyDialog = false;
217
- this.simplifyThreshold = 3;
218
- /** Current region selection (array indices) — mirrored from the store so the
219
- * context-menu region actions can read it synchronously (jit-ui#85). */
220
- this.selectedIndices = [];
221
- /** Wand sensitivity — higher = stricter (smaller selection). Matches QuPath default. */
222
- this.wandSensitivity = 2.0;
223
- /** Brush diameter in image-pixel coordinates (drives the painted disc size). */
224
- this.brushSize = 40;
225
- /** Class the brush paints while a plot mode armed it (PlotModeTools.armBrush); null = plain brush. */
226
- this.brushClass = null;
290
+ /** The armed tool mode and the toolbar's tool settings. */
291
+ this.tools = new ToolModes(this.plotService, this.session, this.ngZone, (mode) => {
292
+ if (mode !== 'samPoint')
293
+ this.segmentation.hide();
294
+ }, // leaving point mode drops its toast
295
+ () => this.changed());
296
+ /** Region set-operations on the selection, and the store mirrors they read (jit-ui#85). */
297
+ this.regionActions = new RegionActions(this.plotService, this.regionOps, () => this.imageInfo?.trueImageSize, (m) => this.messageService.add({ key: this.resultToastKey, ...m }));
227
298
  /** SAM model picker options + current selection (jit-ui#90 P1). Only models
228
299
  * with a hosted ONNX pair (configured via setSamModelUrls at app init) are
229
300
  * offered, so the picker can't select a model that can't run. */
@@ -236,58 +307,16 @@ export class VisualizerComponent {
236
307
  * and help dialog simply omit that group.
237
308
  */
238
309
  this.contributedTools = [];
239
- /**
240
- * Dialog tools registered through {@link TOOLBAR_TOOLS} (`kind: 'dialog'`),
241
- * sorted. Their buttons sit with the host's own actions and show in the Image
242
- * view only; each opens a floating dialog the tool fills.
243
- */
244
- this.dialogTools = [];
245
- /** The dialog tool the user has open (its session may be between renders). */
246
- this.openDialogToolId = null;
247
- /** The open dialog tool's live dialog, as the template renders it. */
248
- this.toolDialog = null;
249
- /** Active checkpoint per contributed tool. */
250
- this.toolModelIds = {};
251
- /**
252
- * Parameter values per contributed tool, seeded from each tool's own defaults
253
- * — which encode the scale and crowding its checkpoint was trained for.
254
- */
255
- this.toolParams = {};
256
- /** Which tool's parameter dialog is open, or null. */
257
- this.openParamsToolId = null;
258
- /** SAM download/segment toast state (bound by the `sam` p-toast template). */
259
- this.samStatus = '';
260
- this.samProgress = 0; // 0..100, encoder download
261
- this.samDownloading = false;
262
- this.samBusy = false; // any SAM work in flight (drives the indeterminate spinner)
263
- /** Whether the shared `sam` toast is currently shown (avoids stacking it on
264
- * every point click, which re-runs inference). */
265
- this.samToastShown = false;
266
310
  this.samToastKey = `sam-${VisualizerComponent.nextToastId++}`;
267
311
  /**
268
- * Outlet for the library's own result/error notices.
269
- *
270
- * PrimeNG routes a message to a `<p-toast>` only when the keys match — a
271
- * keyless message reaches a keyless toast and nothing else. These notices used
272
- * to be emitted with no key, so they rendered only in hosts that happen to
273
- * mount a bare `<p-toast>` (jit-ui does, in its file-tree component). Every
274
- * other host — including this repo's own browser example — dropped them
275
- * silently, which made a failed or no-op segmentation look like a dead button:
276
- * the sticky progress toast is cleared in the same tick by hideSamToast, and
277
- * the message explaining why never appeared anywhere.
278
- *
279
- * Keyed to this component instance so the library renders its own outlet and
280
- * is not dependent on host markup. Deliberately NOT the sticky toast's key:
281
- * {@link hideSamToast} clears that key in the `finally`, which would wipe the
282
- * result the moment it was posted, and the sticky toast's custom template is
283
- * built for the live status + progress bar.
312
+ * Outlet for the library's own result/error notices, keyed to this instance so they
313
+ * do not depend on the host mounting a keyless `<p-toast>` (most hosts don't, and a
314
+ * failed segmentation then looked like a dead button). Deliberately NOT the sticky
315
+ * toast's key: that is cleared when a run settles, which would wipe the result.
284
316
  */
285
317
  this.resultToastKey = `${this.samToastKey}-result`;
286
- /** Subscriptions to the point tool's live feeds (status/busy/download). */
287
- this.samPointSub = new Subscription();
288
- /** Vertex eraser radius in image-pixel coordinates. */
289
- this.vertexEraserRadius = 20;
290
- this.reversescale = false;
318
+ /** Segmentation runs and their sticky progress toast. */
319
+ this.segmentation = new SegmentationRunner(this.messageService, this.samToastKey, this.resultToastKey, () => this.changed());
291
320
  /** Channels & Histogram dialog visibility (opened from the toolbar). */
292
321
  this.showChannelHistogram = false;
293
322
  /** Spatial-omics controls dialog visibility (toolbar button). */
@@ -298,12 +327,30 @@ export class VisualizerComponent {
298
327
  * element's current width, or — when no selector is configured / the element
299
328
  * isn't found — to a quarter of the page (see openRegionEditor). */
300
329
  this.regionEditorWidth = '25vw';
301
- this.stackOptions = [
302
- { name: 'Single image', val: 'false' },
303
- { name: 'Stack', val: 'true' },
304
- ];
305
- this.selectedStackOption = this.stackOptions[0];
306
330
  this.plotDivName = `viz-plot-${plotInstanceSeq++}`;
331
+ /** The render pipeline: the newest image (or image-less dataset) always wins. */
332
+ this.render = new ImageRenderSession({
333
+ plotDivName: this.plotDivName,
334
+ visualizer: this.plotService,
335
+ zIndex: () => this.zIndex,
336
+ plotType: () => this.plotType,
337
+ isHeatmap: () => this.isHeatmap,
338
+ isCaching: () => this.isCaching(),
339
+ beforeReset: () => this.contributions.endSessions(),
340
+ prepare: (info) => this.layOutSlices(info),
341
+ releaseOverlay: (info) => {
342
+ if (info.isStack && info.showStack)
343
+ this.stackLoading.set(false);
344
+ this.state.setImageLoading(false);
345
+ },
346
+ landed: (info) => {
347
+ applyImageRois(info, this.plotService, this.zIndex);
348
+ this.contributions.activate();
349
+ },
350
+ setImageLoading: (loading) => this.state.setImageLoading(loading),
351
+ alert: (severity, summary, detail) => this.messageService.add({ key: this.vizAlertToastKey, severity, summary, detail }),
352
+ detectChanges: () => this.cdr.detectChanges(),
353
+ });
307
354
  this.plotType = PlotType.IMAGE;
308
355
  this.isHeatmap = true;
309
356
  this.activeSurface3dMode = 'turntable';
@@ -327,52 +374,26 @@ export class VisualizerComponent {
327
374
  * contributed mode behaves exactly like the built-in type it rides on.
328
375
  */
329
376
  this.selectedPlotTypeId = PlotType.IMAGE;
330
- /**
331
- * The live contributed mode's side panel, as the template renders it: an
332
- * Angular component with its own injector, or a plain host element a `mount`
333
- * panel rendered into. Null while no contributed session is live.
334
- */
335
- this.plotModePanel = null;
336
- this.destroying = false;
337
- /** Floating intensity-profile inset (LINE mode). */
338
- this.intensityInsetDiv = 'intensity-inset-plot';
339
- this.profilePanelPos = { x: 20, y: 70 };
340
- this.profilePanelDragging = false;
341
- this.profilePanelStart = { mx: 0, my: 0, x: 0, y: 0 };
377
+ /** Div the floating intensity-profile inset is charted into. Per instance, like
378
+ * {@link plotDivName}: the backend resolves it by id, so a fixed id made two live
379
+ * viewers draw both insets into whichever div came first in the document. */
380
+ this.intensityInsetDiv = `${this.plotDivName}-inset`;
342
381
  /** Toolbar docking: docked across the top by default; dragging its handle
343
382
  * detaches it into a floating, movable window (frees the top row for the
344
383
  * visualization). */
345
384
  this.toolbarFloating = false;
346
385
  this.toolbarPos = { x: 8, y: 8 };
347
- this.toolbarDragging = false;
348
- this.toolbarStart = { mx: 0, my: 0, x: 0, y: 0 };
349
- this.latestProfiles = [];
350
- /** Drives the intensity inset panel's visibility — true whenever any
351
- * intensity-profile line exists (independent of the current plot type). */
352
- this.hasProfiles = false;
386
+ /** Where a toolbar-handle drag starts from: grabbing the docked toolbar floats it. */
387
+ this.toolbarDragOrigin = () => {
388
+ if (!this.toolbarFloating) {
389
+ this.toolbarFloating = true;
390
+ this.toolbarPos = { x: 8, y: 8 };
391
+ }
392
+ return this.toolbarPos;
393
+ };
353
394
  /** Isosurface band as a 0–255 slider position, mapped onto the volume's real
354
395
  * intensity range by the renderer. Defaults to the full range. */
355
396
  this.isoRange = [0, 255];
356
- /** Whether a spatial-omics dataset is currently published on
357
- * `SPATIAL_DATA_PORT` — gates the spatial plot types in the selector. */
358
- /** Bound by the toolbar, which offers the plot modes for a dataset with no image too. */
359
- this.hasSpatialDataset = false;
360
- /** The spatial dataset on offer, for drawing one that brings no image (see reloadAndPlot). */
361
- this.spatialDataset = null;
362
- /** Whether that dataset's observations carry a z, gating the 3D spatial mode. */
363
- this.hasSpatial3dDataset = false;
364
- /** Whether it carries a registered volume. Change detection only: a volume
365
- * appearing is what publishes it as the image, and the plot-type gates then
366
- * see an ordinary grayscale stack. */
367
- this.hasSpatialVolume = false;
368
- /** Dataset identity + capability shape the last emission was handled at, so a
369
- * switch between two datasets of the same shape is not mistaken for a repeat. */
370
- this.spatialDatasetKey = null;
371
- /** Dataset + geometry the currently published volume image was built from, so a
372
- * re-emitted dataset doesn't re-fetch megabytes or reset the user's scrub. */
373
- this.volumeImageKey = null;
374
- /** Blob URLs backing that image — ours to revoke. */
375
- this.volumeImageUrls = [];
376
397
  /** Debounced z-slice scrubbing (see SliceScrubber — refactoring plan Step 7). */
377
398
  this.scrubber = new SliceScrubber((z) => {
378
399
  this.plotService.setZIndex(z);
@@ -381,296 +402,65 @@ export class VisualizerComponent {
381
402
  // just records the slice, leaving single-plane regions untouched (jit-ui#93).
382
403
  this.plotService.setDisplaySlice(z);
383
404
  });
384
- /** If the active plot type is no longer in the offered options (e.g. test mode
385
- * turned off while a test-only type was active, or a scalar type carried onto
386
- * an RGB image), fall back to the default 2D Image view. */
387
- /** Whether the live spatial dataset brings pixels of its own — a tissue image it
388
- * registers onto, or a volume that is published as a z-stack image. */
389
- this.spatialDatasetHasPixels = false;
390
- /** The live dialog body's teardown, and a pending wait for its host to connect. */
391
- this.toolDialogTeardown = null;
392
- this.toolDialogFrame = null;
393
- /** Toolbar tools for contributed modes. Calls can come from outside Angular, hence the zone. */
394
- this.plotModeTools = {
395
- armBrush: (brushClass) => this.ngZone.run(() => {
396
- this.brushClass = { label: brushClass?.label, color: brushClass?.color };
397
- if (this.activeDragMode === 'brush') {
398
- this.plotService.setBrushOptions({ size: this.brushSize, ...this.brushClass });
399
- }
400
- else {
401
- this.applyDragMode('brush');
402
- }
403
- this.cdr.markForCheck();
404
- }),
405
- disarm: () => this.ngZone.run(() => {
406
- this.brushClass = null;
407
- if (this.activeDragMode !== null)
408
- this.applyDragMode(null);
409
- this.cdr.markForCheck();
410
- }),
411
- activeTool$: this.session.getActiveTool$(),
405
+ /** What the host gets through `setDiagram` — see {@link VisualizerHandle}. */
406
+ this.hostHandle = {
407
+ visualizer: this.plotService,
408
+ plotService: this.plotService,
409
+ hasRegions: () => this.hasRegions(),
410
+ getRegionPolygons: () => this.getRegionPolygons(),
412
411
  };
413
- this.plotModes = new PlotModeController(plotTypeContributions, {
414
- onActivated: (active) => this.showPlotModePanel(active),
415
- onDeactivating: () => this.hidePlotModePanel(),
416
- onFailed: (contribution) => this.fallBackFromPlotMode(contribution),
417
- });
418
412
  this.contributedTools = visibleToolContributions(toolContributions);
419
- this.dialogTools = dialogToolContributions(toolContributions);
420
- // A dialog tool's session is run by the plot-mode lifecycle, adapted: its "base
421
- // type" is the Image view it draws over. Its body is NOT a controller panel: the
422
- // controller would mount into a detached host, and a body must be able to measure
423
- // itself. The dialog is rendered first and the body mounted once its host is in the
424
- // document (showToolDialog).
425
- this.toolDialogs = new PlotModeController(this.dialogTools.map((t) => ({
426
- descriptor: { type: t.id, label: t.label, dimensions: '2d', baseType: PlotType.IMAGE },
427
- activate: (ctx) => t.activate(ctx),
428
- })), {
429
- onActivated: (active) => this.showToolDialog(active),
430
- onDeactivating: () => this.hideToolDialog(),
431
- onFailed: (contribution) => this.dialogToolFailed(contribution.descriptor.type),
413
+ this.toolParams = new ToolParamsModel(this.contributedTools);
414
+ this.contributions = new ContributionHost(plotTypeContributions, toolContributions, {
415
+ visualizer: this.plotService,
416
+ zone: this.ngZone,
417
+ tools: this.tools.plotModeTools,
418
+ injector: this.injector,
419
+ imageInfo$: () => this.state.getImageInfo$(),
420
+ selectedId: () => this.selectedPlotTypeId,
421
+ renderedType: () => this.plotType,
422
+ selectBase: (base) => {
423
+ this.selectedPlotTypeId = base;
424
+ this.plotType = base;
425
+ },
426
+ notify: (m) => this.messageService.add({ key: this.resultToastKey, ...m }),
427
+ detectChanges: () => this.cdr.detectChanges(),
428
+ });
429
+ this.spatial = new SpatialDatasetBinder(this.spatialData, {
430
+ gatesChanged: () => this.computePlotTypeOptions(),
431
+ selectedId: () => this.selectedPlotTypeId,
432
+ basePlotType: () => this.basePlotType,
433
+ hasImage: () => !!this.imageInfo,
434
+ selectPlotType: (type) => this.onSelectPlotType(type),
435
+ replot: () => this.reloadAndPlot(),
436
+ publishImage: (info) => this.state.setImageInfo(info),
437
+ setImageLoading: (loading) => this.state.setImageLoading(loading),
438
+ settled: () => {
439
+ this.reconcileSelectedPlotType();
440
+ this.changed();
441
+ },
442
+ detectChanges: () => this.changed(),
432
443
  });
433
- this.colormapsOptions = plotService.getColormapOptions();
434
444
  this.computePlotTypeOptions();
435
445
  }
436
- /**
437
- * Plot types offered in the selector for the current image:
438
- * - Outside **test mode**, only the curated default set (descriptors with a
439
- * `productionLabel`) is offered, shown under suffix-free names — Image,
440
- * Heatmap, Contour and the napari Surface / Volume / Isosurface. Test mode
441
- * exposes every backend's type under its full (backend-suffixed) label.
442
- * - 3D types hidden when the backend can't render a 3D scene.
443
- * - stack-only types (volume, isosurface) hidden unless the file is a stack —
444
- * a volume needs multiple z-slices.
445
- * - scalar-intensity types (contour, surface, isosurface) hidden for RGB
446
- * images — they map a single intensity per pixel. Image and Heatmap render
447
- * any image.
448
- * - spatial-omics types hidden until a `SpatialDataset` is published on
449
- * `SPATIAL_DATA_PORT` — the mode has nothing to draw without observations,
450
- * exactly as a volume has nothing to draw without a stack.
451
- */
452
- /**
453
- * Track whether a spatial-omics dataset is available. A dataset appearing (or
454
- * being cleared) changes which plot types make sense, so it drives the same
455
- * recompute + reconcile that the image stream and the test-mode toggle do.
456
- *
457
- * No-op when the host provides no `SPATIAL_DATA_PORT` — the spatial types then
458
- * stay hidden for the life of the component.
459
- */
446
+ /** Follow the spatial-omics dataset: a dataset appearing, switching or being cleared
447
+ * re-gates the selector exactly as the image stream and the test-mode toggle do. */
460
448
  watchSpatialDataset() {
461
- this.spatialDatasetSubscription = this.spatialData?.getDataset$().subscribe((dataset) => {
462
- this.spatialDataset = dataset ?? null;
463
- const has = !!dataset;
464
- // Only a dataset whose observations carry a z can be drawn as a cloud, so
465
- // the 3D mode is gated on the coordinates, not merely on a dataset being
466
- // present. Most spatial assays are one plane.
467
- const has3d = !!dataset?.observations.z;
468
- const hasVolume = !!dataset?.volume;
469
- // The port publishes its current value on subscribe, so the initial `null`
470
- // would otherwise recompute the selector for no change. Keyed by dataset
471
- // IDENTITY as well as capability shape: two 3D datasets that both carry a
472
- // volume have the same shape, and comparing only that skipped the switch —
473
- // leaving the previous dataset's volume image on screen underneath the new
474
- // one's observations.
475
- const key = dataset
476
- ? `${dataset.id}|${has3d}|${hasVolume}|${dataset.volume
477
- ? `${dataset.volume.width}x${dataset.volume.height}x${dataset.volume.depth}` : ''}`
478
- : null;
479
- if (key === this.spatialDatasetKey)
480
- return;
481
- this.spatialDatasetKey = key;
482
- this.hasSpatialDataset = has;
483
- this.hasSpatial3dDataset = has3d;
484
- // Whether the dataset says it registers onto a tissue image. Distinct from an
485
- // image being LOADED: a registered dataset's image may still be on its way, and
486
- // the pixel modes must not flicker out of the selector while it arrives.
487
- // A registered dataset draws over a tissue image; a volume-backed one publishes its
488
- // volume AS a grayscale z-stack image (`buildVolumeStackImage`). Either way pixels
489
- // exist, so the pixel modes stay on offer — Volume and Isosurface are exactly how
490
- // a 3D omics dataset is read.
491
- this.spatialDatasetHasPixels = !!dataset?.imageRef || hasVolume;
492
- this.hasSpatialVolume = hasVolume;
493
- this.computePlotTypeOptions();
494
- // A dataset with no reference image has nothing to draw observations OVER:
495
- // a cloud registered into a common frame (the Allen CCF) has coordinates
496
- // but no one section. What it does have is its registered VOLUME, which is
497
- // a 3D image in one file — so make that the image and open on it, slice bar
498
- // and all, rather than leaving the host on an Image view showing whatever
499
- // slide was loaded before. Ordered after computePlotTypeOptions so the type
500
- // is on offer before it is selected.
501
- if (dataset && !dataset.imageRef && hasVolume) {
502
- void this.showVolumeAsImage(dataset);
503
- }
504
- else {
505
- // Anything else — a dataset with its own section image, a volume-less
506
- // cloud, or none at all — means a published volume image is no longer what
507
- // is on screen. Forget it, or coming BACK to the volume dataset would
508
- // short-circuit on a matching key and leave the other dataset's slide up.
509
- this.dropVolumeImage();
510
- if (dataset && !dataset.imageRef) {
511
- // No reference image and no volume to slice: the observations are the only
512
- // thing there is to draw, so open on whichever spatial mode their
513
- // coordinates support. Leaving the type alone strands the host on an Image
514
- // view showing whatever slide was loaded BEFORE — the observations never
515
- // appear, and the previous dataset's tissue does, which reads as this
516
- // dataset failing to load.
517
- //
518
- // Gated on the coordinates, not on the dataset merely existing: a
519
- // one-plane assay has no z and cannot be a cloud. That case only turned up
520
- // with an image-less 2D dataset, which is why it went unhandled — before
521
- // it, image-less meant 3D.
522
- const target = has3d ? PlotType.SPATIAL_OMICS_3D : PlotType.SPATIAL_OMICS;
523
- if (this.selectedPlotTypeId !== target)
524
- this.onSelectPlotType(target);
525
- // Same mode, another image-less dataset, nothing loaded: re-plot so its placeholder
526
- // image info (and with it the regions' key) is this dataset's, not the last one's.
527
- else if (!this.imageInfo)
528
- this.reloadAndPlot();
529
- }
530
- }
531
- // Clearing the dataset while a spatial mode is active leaves a type that is
532
- // no longer offered — fall back to Image, as turning test mode off does.
533
- this.reconcileSelectedPlotType();
534
- this.cdr.detectChanges();
535
- });
536
- }
537
- /**
538
- * Publish a dataset's reference volume AS the image, and open the 2D Image view
539
- * on it.
540
- *
541
- * The volume is a 3D image delivered in one file, so for a dataset that has no
542
- * section image it is the honest thing to put on screen: the slice bar scrubs z,
543
- * and the contrast window, colormaps and region tools all work because the
544
- * volume genuinely is the image now. The 3D cloud stays one menu pick away.
545
- *
546
- * Keyed by dataset + geometry: the dataset stream re-emits on things like a
547
- * colour-column change, and rebuilding then would re-fetch the voxels and throw
548
- * the user back to the middle slice.
549
- */
550
- async showVolumeAsImage(dataset) {
551
- const meta = dataset.volume;
552
- if (!meta || !this.spatialData?.getVolume)
553
- return;
554
- const key = `${dataset.id}:${meta.width}x${meta.height}x${meta.depth}`;
555
- if (key === this.volumeImageKey)
556
- return;
557
- // Claim the key BEFORE awaiting: the stream can emit again while the voxels
558
- // are in flight, and two builds of the same volume would race to publish.
559
- this.volumeImageKey = key;
560
- this.state.setImageLoading(true);
561
- try {
562
- const built = await buildVolumeStackImage(dataset, await this.spatialData.getVolume());
563
- // A different dataset was selected while this one encoded — its image is the
564
- // one that belongs on screen, so drop what we just built.
565
- if (this.volumeImageKey !== key) {
566
- built?.urls.forEach((u) => URL.revokeObjectURL(u));
567
- return;
568
- }
569
- if (!built) {
570
- this.volumeImageKey = null;
571
- return;
572
- }
573
- // Pick the mode first so the image lands in the view that will show it,
574
- // instead of rendering once into whatever mode the last dataset left active.
575
- if (this.selectedPlotTypeId !== PlotType.IMAGE)
576
- this.onSelectPlotType(PlotType.IMAGE);
577
- this.revokeVolumeImageUrls();
578
- this.volumeImageUrls = built.urls;
579
- this.state.setImageInfo(built.info);
580
- }
581
- catch (err) {
582
- // No volume served after all: the cloud is still renderable, so fall back to
583
- // it rather than leaving the host on an Image view with nothing in it.
584
- console.warn('[visualizer] reference volume unavailable — falling back to the 3D cloud', err);
585
- this.volumeImageKey = null;
586
- if (this.hasSpatial3dDataset && !isSpatialOmics3d(this.basePlotType)) {
587
- this.onSelectPlotType(PlotType.SPATIAL_OMICS_3D);
588
- }
589
- }
590
- finally {
591
- this.state.setImageLoading(false);
592
- this.cdr.detectChanges();
593
- }
594
- }
595
- /** Forget the published volume image so re-selecting the dataset rebuilds it.
596
- * The URLs deliberately stay alive: the host may still be displaying that image
597
- * at this instant, and revoking under it would break every later tile read. They
598
- * are freed when the next volume replaces them, or on destroy. */
599
- dropVolumeImage() {
600
- this.volumeImageKey = null;
601
- }
602
- revokeVolumeImageUrls() {
603
- this.volumeImageUrls.forEach((u) => URL.revokeObjectURL(u));
604
- this.volumeImageUrls = [];
449
+ this.spatial.bind(this.destroyed$);
605
450
  }
451
+ /** Recompute the selector's entries for the current image, dataset and test mode
452
+ * (see {@link computePlotTypeMenu}). */
606
453
  computePlotTypeOptions() {
607
- const caps = this.plotService.capabilities;
608
- const isStack = !!this.imageInfo?.isStack;
609
- const isGrayscale = !!this.imageInfo?.isGrayscale;
610
- // A multichannel image's bands are each single-band (scalar), so the scalar
611
- // plot modes (Heatmap / Surface / Volume / Isosurface) apply per channel even
612
- // though the RGB composite isn't grayscale — don't gate them out.
613
- const m0 = this.imageInfo?.imageMeta?.[0];
614
- const isMultichannel = (m0?.channelCount ?? 1) > 1 && (m0?.rgbChannels ?? 1) < 3;
615
- const passesGates = (d) => {
616
- if (d.dimensions === '3d' && !caps.has(ViewerFeature.Surface3D))
617
- return false;
618
- // Volume and Isosurface raymarch the IMAGE STACK, and nothing else: these
619
- // two gates are about the loaded image, full stop. A 3D omics dataset
620
- // reaches them because its registered volume is published AS a grayscale
621
- // z-stack image (`buildVolumeStackImage`), not through a second voxel
622
- // source hiding behind the same modes.
623
- if (d.requiresStack && !isStack)
624
- return false;
625
- if (d.requiresGrayscale && !isGrayscale && !isMultichannel)
626
- return false;
627
- if (d.requiresSpatialData && !this.hasSpatialDataset)
628
- return false;
629
- if (d.requiresSpatial3d && !this.hasSpatial3dDataset)
630
- return false;
631
- // An image-sourced mode reads PIXELS. A spatial dataset that brings no tissue
632
- // image (seqFISH records unitless coordinates, not a section) leaves nothing for
633
- // one to draw, so offering Image / Heatmap / Surface there offers modes that can
634
- // only come up blank — or worse, showing whatever slide was loaded before.
635
- //
636
- // Narrowed to "a dataset is up AND there is no image": with no dataset at all,
637
- // Image stays on offer, because a host that has not loaded anything yet needs a
638
- // default and an empty selector would be worse than a blank view.
639
- if (d.source === 'image' && this.hasSpatialDataset
640
- && !this.spatialDatasetHasPixels)
641
- return false;
642
- return true;
643
- };
644
- // Outside test mode, only the curated set (those with a productionLabel)
645
- // is offered; test mode exposes every backend's type.
646
- const curated = (d) => this.testMode || !!d.productionLabel;
647
- const builtIn = this.plotService.getPlotTypeDescriptors()
648
- .filter((d) => curated(d) && passesGates(d));
649
- // Contributed modes come after every built-in one, under the same label rule
650
- // and their own stack/grayscale/spatial gates. A mode also needs whatever its base
651
- // view needs (pixels, a 3D-capable backend), so the base type's gates apply
652
- // too — a contributed Image-based mode goes wherever Image goes.
653
- const contributed = [];
654
- for (const d of this.plotModes.descriptors()) {
655
- if (!curated(d))
656
- continue;
657
- if (d.requiresStack && !isStack)
658
- continue;
659
- if (d.requiresGrayscale && !isGrayscale && !isMultichannel)
660
- continue;
661
- if (d.requiresSpatialData && !this.hasSpatialDataset)
662
- continue;
663
- if (d.requiresSpatial3d && !this.hasSpatial3dDataset)
664
- continue;
665
- const base = getPlotTypeDescriptor(d.baseType);
666
- if (base && passesGates(base))
667
- contributed.push(contributedPlotTypeOption(d, base));
668
- }
669
- // Default selector shows the suffix-free productionLabel; test mode keeps
670
- // the full backend-suffixed label so same-named modes stay distinguishable.
671
- const labelled = (d) => (this.testMode ? d : { ...d, label: d.productionLabel });
672
- this.plotTypeOptions = builtIn.map(labelled);
673
- this.plotTypeMenu = [...this.plotTypeOptions, ...contributed.map(labelled)];
454
+ const { builtIn, menu } = computePlotTypeMenu({
455
+ descriptors: this.plotService.getPlotTypeDescriptors(),
456
+ contributions: this.plotModes.descriptors(),
457
+ caps: this.plotService.capabilities,
458
+ imageInfo: this.imageInfo,
459
+ spatial: this.spatial,
460
+ testMode: this.testMode,
461
+ });
462
+ this.plotTypeOptions = builtIn;
463
+ this.plotTypeMenu = menu;
674
464
  }
675
465
  ngOnChanges(changes) {
676
466
  // The constructor computes the options with testMode still at its default;
@@ -693,8 +483,7 @@ export class VisualizerComponent {
693
483
  onImageCleared() {
694
484
  // The image a contributed mode was drawing over is gone. An open dialog
695
485
  // tool stays open and restarts once the next image has plotted.
696
- this.plotModes.deactivate();
697
- this.toolDialogs.deactivate();
486
+ this.contributions.endSessions();
698
487
  this.imageInfo = undefined;
699
488
  this.loadedFileName = undefined;
700
489
  this.computePlotTypeOptions();
@@ -707,17 +496,13 @@ export class VisualizerComponent {
707
496
  // Nothing is left stranded: an image is cleared because a spatial dataset that
708
497
  // brings none is being opened, and that dataset's own subscription selects the
709
498
  // mode its coordinates support a moment later.
710
- this.cdr.detectChanges();
499
+ this.changed();
711
500
  }
501
+ /** If the active plot type is no longer in the offered options (e.g. test mode
502
+ * turned off while a test-only type was active, or a scalar type carried onto
503
+ * an RGB image), fall back to the default 2D Image view. */
712
504
  reconcileSelectedPlotType() {
713
- if (this.plotTypeMenu.some((d) => d.type === this.selectedPlotTypeId))
714
- return;
715
- // Image is the usual fallback, but it is not always ON OFFER: with no image loaded
716
- // the pixel modes are gone, and falling back to one would select a mode the selector
717
- // does not list and nothing can draw. Take the first type still offered instead.
718
- const fallback = this.plotTypeMenu.some((d) => d.type === PlotType.IMAGE)
719
- ? PlotType.IMAGE
720
- : this.plotTypeMenu[0]?.type;
505
+ const fallback = reconcilePlotType(this.plotTypeMenu, this.selectedPlotTypeId);
721
506
  if (!fallback)
722
507
  return;
723
508
  // Leaving a contributed mode (its gates stopped passing, or its provider is
@@ -734,132 +519,43 @@ export class VisualizerComponent {
734
519
  ngOnInit() {
735
520
  // Join the live set; the oldest member renders the shared notice outlets.
736
521
  VisualizerComponent.liveInstances.add(this);
737
- this.state.setDiagram(this);
738
- this.selectedColormap =
739
- this.colormapsOptions[0].children.find((c) => c.label === 'Greys Inv') ??
740
- this.colormapsOptions[0].children[0];
741
- this.stackOptions = [
742
- { name: 'Single image', val: 'false' },
743
- { name: 'Stack', val: 'true' },
744
- ];
745
- this.selectedStackOption = this.stackOptions[0];
522
+ this.state.setDiagram(this.hostHandle);
746
523
  this.watchSpatialDataset();
747
- this.autoscaleSubscription = this.plotService.getAutoscaleEvent().subscribe(() => {
748
- // reset the image mode to single image
749
- this.selectedStackOption = { name: 'Single image', val: 'false' };
750
- this.activeDragMode = null;
751
- this.session.setActiveTool(null);
752
- });
753
- this.imageLoadingSubscription = this.state.isImageLoading$().subscribe((isImageLoading) => {
754
- this.imgLoading = isImageLoading;
755
- });
756
- // Undo availability (jit-ui#85): the shared RegionStore emits whenever its
757
- // history depth changes; greys out the toolbar Undo button accordingly.
758
- this.plotService
759
- .getCanUndo$()
760
- .pipe(takeUntil(this.unsub))
761
- .subscribe((canUndo) => {
762
- this.canUndoRegion = canUndo;
763
- this.cdr.detectChanges();
764
- });
765
- this.plotService
766
- .getCanRedo$()
767
- .pipe(takeUntil(this.unsub))
768
- .subscribe((canRedo) => {
769
- this.canRedoRegion = canRedo;
770
- this.cdr.detectChanges();
771
- });
772
- // Mirror the region selection so the right-click action group (jit-ui#85)
773
- // can read it synchronously when the menu is built.
524
+ // OSD and napari-js emit this from their own "fit to view": disarm the tool on
525
+ // the backend too, not only the toolbar's highlight.
774
526
  this.plotService
775
- .getSelectedShapeIndices$()
776
- .pipe(takeUntil(this.unsub))
777
- .subscribe((indices) => {
778
- this.selectedIndices = indices || [];
779
- });
780
- // Interactive point-prompt segmentation runs inside the renderer on each
781
- // click; surface its live status + download progress in the shared `sam`
782
- // toast so the user sees it working (the first click pulls the encoder).
783
- this.samPointSub.add(this.samPointTool.progress$.subscribe((f) => {
784
- this.samDownloading = f >= 0 && f < 1;
785
- if (f >= 0)
786
- this.samProgress = Math.min(100, Math.round(f * 100));
787
- this.cdr.detectChanges();
788
- }));
789
- this.samPointSub.add(this.samPointTool.status$.subscribe((m) => {
790
- this.samStatus = m;
791
- this.cdr.detectChanges();
792
- }));
793
- this.samPointSub.add(this.samPointTool.busy$.subscribe((busy) => {
794
- this.samBusy = busy;
795
- if (busy)
796
- this.showSamToast('SAM point segmentation');
797
- this.cdr.detectChanges();
798
- }));
799
- this.plotService.getStackLoadingProgress().subscribe((loadingProgress) => {
800
- this.loadingPercentage = loadingProgress;
801
- });
802
- this.plotService.isStackLoading().subscribe((stackLoading) => {
803
- this.stackLoading = stackLoading;
527
+ .getAutoscaleEvent()
528
+ .pipe(takeUntilDestroyed(this.destroyRef))
529
+ .subscribe(() => this.tools.apply(null));
530
+ // Loading/overlay UI mirrors (signals: they re-render the view themselves).
531
+ this.mirror(this.state.isImageLoading$(), (v) => this.imgLoading.set(v));
532
+ this.mirror(this.plotService.getStackLoadingProgress(), (v) => this.loadingPercentage.set(v));
533
+ this.mirror(this.plotService.isStackLoading(), (v) => this.stackLoading.set(v));
534
+ this.mirror(this.state.getImageLoadingMessage$(), (v) => this.loadingMessage.set(v));
535
+ this.mirror(this.state.getCacheProgress$(), (v) => this.cacheProgress.set(v));
536
+ this.mirror(this.state.isZoom$(), (v) => this.zoom.set(v));
537
+ this.mirror(this.state.getFilename$(), (v) => {
538
+ if (v)
539
+ this.fileName = v;
804
540
  });
805
- this.plotWidthSubscription = this.state.getPanelWidth$().subscribe(() => {
541
+ this.regionActions.bind(this.destroyed$, () => this.changed());
542
+ this.segmentation.bindPointTool(this.samPointTool, this.destroyed$);
543
+ this.mirror(this.state.getPanelWidth$(), () => {
806
544
  this.plotService.relayout();
807
- // The intensity inset is a separate Plotly chart in a floating panel; reflow
808
- // it to its current size when the canvas resizes, else it keeps the stale
809
- // size and blanks out. Defer a tick so the panel layout has settled.
810
- if (this.hasProfiles) {
811
- setTimeout(() => this.renderIntensityInset(), 0);
812
- }
813
- });
814
- this.imgLoadingMessageSubscription = this.state.getImageLoadingMessage$().subscribe((message) => {
815
- this.loadingMessage = message;
545
+ // The intensity inset is a separate chart in a floating panel; reflow it too.
546
+ this.inset?.reflow();
816
547
  });
817
548
  this.state
818
- .getCacheProgress$()
819
- .pipe(takeUntil(this.unsub))
820
- .subscribe((progress) => {
821
- this.cacheProgress = progress;
822
- });
823
- this.isZoomSubscription = this.state.isZoom$().subscribe((zoom) => {
824
- this.zoom = zoom;
825
- });
826
- this.filenameSubscription = this.state.getFilename$().subscribe((filename) => {
827
- if (filename) {
828
- this.fileName = filename;
829
- }
830
- });
831
- this.plotService.getColormap().subscribe((colormap) => {
832
- this.selectedColormap = colormap;
833
- });
834
- this.plotService.getReverseScale().subscribe((reversescale) => {
835
- this.reversescale = reversescale;
836
- });
837
- this.intensityProfileSub = this.plotService.getIntensityProfile$().subscribe((profiles) => {
838
- this.latestProfiles = profiles;
839
- this.hasProfiles = profiles.length > 0;
840
- // The panel div is behind *ngIf="hasProfiles". detectChanges() materializes
841
- // it synchronously (the subscription may fire outside Angular's zone — e.g.
842
- // from an OSD drag — so CD wouldn't run on its own). Then render on the next
843
- // animation frame, AFTER the browser lays the panel out: a synchronous draw
844
- // hits a zero-size container on first create and Plotly keeps that size, so
845
- // the inset stays blank and live updates redraw into the same zero box.
846
- this.cdr.detectChanges();
847
- requestAnimationFrame(() => this.renderIntensityInset());
848
- });
849
- // When the OSD view settles at a new zoom/pan, re-sample the intensity lines
850
- // from a crop of the visible region so the inset reflects the zoom-level
851
- // resolution (Plotly's own high-def zoom updates the sampling cache inline).
852
- this.viewportChangeSub = this.plotService.getViewportChange$().subscribe((roi) => {
853
- if (this.hasProfiles && this.isImageView) {
854
- this.plotService.refreshIntensitySamplingForRoi(roi.x, roi.y, roi.width, roi.height, this.zIndex);
855
- }
856
- });
857
- this.previewSubscription = this.state.getImageInfo$().subscribe({
549
+ .getImageInfo$()
550
+ .pipe(takeUntilDestroyed(this.destroyRef))
551
+ .subscribe({
858
552
  next: (imgInfo) => {
859
553
  if (!imgInfo) {
860
554
  this.onImageCleared();
861
555
  return;
862
556
  }
557
+ // The selector, the stack controls and the inset all follow the image.
558
+ this.changed();
863
559
  if (imgInfo) {
864
560
  this.imageInfo = imgInfo;
865
561
  // Stack-only plot types (isosurface, scatter3d) depend on whether this
@@ -868,8 +564,6 @@ export class VisualizerComponent {
868
564
  // If the active plot type isn't valid for this image (e.g. a scalar
869
565
  // type like Contour carried over to an RGB image), fall back to Image.
870
566
  this.reconcileSelectedPlotType();
871
- // reset stack options selection
872
- this.selectedStackOption = imgInfo.showStack ? this.stackOptions[1] : this.stackOptions[0];
873
567
  // Read from `imgInfo`, the value this emission carried, rather than from the
874
568
  // field. Handling the empty emission means the field CAN be nulled part-way
875
569
  // through this branch: `reconcileSelectedPlotType` may call `setPlotType`,
@@ -888,215 +582,14 @@ export class VisualizerComponent {
888
582
  this.activeSurface3dMode = 'turntable';
889
583
  }
890
584
  this.loadedFileName = imgInfo.fileName;
891
- this.plotService.setImageMeta(imgInfo.imageMeta);
892
- const urls = imgInfo.urls;
893
- // A newer image ALWAYS preempts an in-flight render. This was
894
- // `if (!this.running)`, which DROPPED the new image while the old one
895
- // finished: identity above had already flipped to the new file, so the
896
- // app reported image B as loaded while image A stayed on screen. Worse,
897
- // a cold image holds the slot for its whole server-side cache fill, so
898
- // every click for 1-2 minutes was discarded against a blank viewport
899
- // and a "Loading image..." overlay that never resolved.
900
- if (urls) {
901
- const token = ++this.renderToken;
902
- const isCurrent = () => this.renderToken === token;
903
- if (this.running) {
904
- // Stop the previous render's frame streaming (napari volume/surface
905
- // preload keeps fetching otherwise) and clear its sharpen flag. Its
906
- // callbacks are already inert via the token, so it can neither paint
907
- // nor release the overlay this render now owns.
908
- this.plotService.cancelLoading?.();
909
- this.sharpening = false;
910
- }
911
- // image size — measure the plot div directly so the toolbar height is excluded
912
- const plotDiv = document.getElementById(this.plotDivName);
913
- const screenHeight = plotDiv?.offsetHeight || 500;
914
- // A contributed mode's session is bound to the base view about to be
915
- // torn down, so it ends here — before reset(), before anything new
916
- // draws. If the mode is still selected once this render lands, a
917
- // fresh session starts for it (see activateSelectedPlotMode).
918
- this.plotModes.deactivate();
919
- this.toolDialogs.deactivate();
920
- this.plotService.reset();
921
- this.stackLoading = imgInfo.isStack && imgInfo.showStack;
922
- // set max index of stack — computed before updateZIndex so a
923
- // fresh stack (shorter or longer than whatever was previously
924
- // loaded) clamps against ITS bounds, not the stale ones.
925
- // One URL per slice (0-indexed), so the last reachable index is
926
- // length-1 — earlier `length-2` dropped the final slice.
927
- this.maxIndex = urls.length > 1 ? urls.length - 1 : 0;
928
- // One-shot hint: jump straight to a specific slice (e.g. the
929
- // file the user actually clicked within a numbered series).
930
- // Consumed immediately so redelivering the same ImageInfo later
931
- // (a plot-type switch, reloadAndPlot) doesn't reset the user's
932
- // current scrub position back to it.
933
- if (imgInfo.initialZIndex !== undefined) {
934
- this.zIndex = imgInfo.initialZIndex;
935
- imgInfo.initialZIndex = undefined;
936
- }
937
- // make sure the zindex is within bounds
938
- this.updateZIndex(urls);
939
- this.running = true;
940
- // Multi-tier rendering (small blurry tier first, then sharpen in
941
- // place) — sequencing lives in RenderOrchestrator; this component
942
- // supplies the phase render and owns the UI flags via callbacks.
943
- // 3D plot types render single-pass: the in-place large pass doesn't
944
- // rebuild a 3D gl-mesh isosurface, so the sharpen step blanked it.
945
- const hasSmallTier = this.isHeatmap &&
946
- (imgInfo.smallUrls?.length ?? 0) === urls.length &&
947
- (imgInfo.smallUrls?.length ?? 0) > 0;
948
- const smallImgInfo = hasSmallTier ? { ...imgInfo, urls: imgInfo.smallUrls } : null;
949
- // Enter per-slice stack mode with the given slice→regions map and
950
- // seed the "previous shapes" from the now-live slice. enterStackMode
951
- // makes zIndex's slice live and resets undo (jit-ui#93).
952
- const enterStack = (slices, layout) => {
953
- this.plotService.enterStackMode(slices, this.zIndex, layout);
954
- const shapes = this.plotService
955
- .getRegions()
956
- .map((region) => region.getShape(this.plotService.getShowShapeLabel()));
957
- this.plotService.setPreviousShapes(shapes);
958
- };
959
- const applyRoi = () => {
960
- // Folder stack: a stack of self-contained per-slice files
961
- // (tiled === false; see loadSeriesAsStack). Each slice-file may
962
- // carry its own sibling "<stem>.geojson" (roiJsonStrs[z]), but a
963
- // fresh folder with none yet leaves roiJsonStrs undefined — key
964
- // off `tiled === false`, NOT roiJsonStrs, so an unannotated
965
- // folder stack still enters the per-slice-file layout (and saves
966
- // back one geojson per slice-file) rather than falling through to
967
- // the single-file combined path (jit-ui#93).
968
- if (imgInfo.isStack && imgInfo.tiled === false) {
969
- const perSlice = imgInfo.roiJsonStrs;
970
- const sliceCount = imgInfo.urls?.length ?? perSlice?.length ?? 0;
971
- const slices = new Map();
972
- for (let z = 0; z < sliceCount; z++) {
973
- const json = perSlice?.[z] ?? null;
974
- slices.set(z, json ? this.plotService.importRegions(json) : []);
975
- }
976
- enterStack(slices, 'per-slice-file');
977
- return;
978
- }
979
- // Single-file z-stack: one sibling geojson holding every slice's
980
- // regions indexed by QuPath's geometry.plane.z. Enter per-slice
981
- // mode (saving back one combined z-indexed geojson) when the
982
- // geojson actually carries slice indices, or when there's nothing
983
- // yet to author against. A legacy geojson whose regions are all on
984
- // the default plane stays global (shown on every slice) so
985
- // existing single-plane annotations aren't confined to slice 0.
986
- const roiJson = imgInfo.roiJsonStr;
987
- if (imgInfo.isStack) {
988
- const regions = roiJson ? this.plotService.importRegions(roiJson) : [];
989
- const hasSliceInfo = regions.some((r) => (r.z ?? 0) !== 0);
990
- if (!roiJson || hasSliceInfo) {
991
- const slices = new Map();
992
- for (const r of regions) {
993
- const z = r.z ?? 0;
994
- const bucket = slices.get(z);
995
- if (bucket)
996
- bucket.push(r);
997
- else
998
- slices.set(z, [r]);
999
- }
1000
- enterStack(slices, 'combined');
1001
- return;
1002
- }
1003
- }
1004
- // Single-plane image (or a legacy global z-stack geojson): one
1005
- // region set for the whole image.
1006
- if (roiJson) {
1007
- const regions = this.plotService.importRegions(roiJson);
1008
- this.plotService.setRegions(regions);
1009
- const shapes = regions.map((region) => region.getShape(this.plotService.getShowShapeLabel()));
1010
- this.plotService.setPreviousShapes(shapes);
1011
- }
1012
- // Loading an image's saved ROIs is not a user edit — start the
1013
- // undo history fresh so the first undo can't wipe them (jit-ui#85).
1014
- this.plotService.resetUndoHistory();
1015
- };
1016
- let overlayReleased = false;
1017
- const releaseOverlay = () => {
1018
- if (overlayReleased)
1019
- return;
1020
- overlayReleased = true;
1021
- if (imgInfo.isStack && imgInfo.showStack)
1022
- this.stackLoading = false;
1023
- this.state.setImageLoading(false);
1024
- };
1025
- new RenderOrchestrator({
1026
- // inPlace=true updates the existing render instead of rebuilding
1027
- // it, so the canvas doesn't blank during the small→large swap.
1028
- renderPhase: (phaseInfo, inPlace) => {
1029
- // Superseded BEFORE this phase started — don't even issue the load.
1030
- // RenderOrchestrator calls renderPhase once per tier and retries the
1031
- // sharpen pass on failure, so checking only after the load resolves
1032
- // would let a preempted render keep fetching slices/tiles for an
1033
- // image nobody is looking at.
1034
- if (!isCurrent())
1035
- return Promise.resolve(null);
1036
- return this.plotService.load(phaseInfo, this.zIndex).then((loadedImage) => {
1037
- // Preempted while this phase was loading — drop it on the floor.
1038
- if (!isCurrent())
1039
- return null;
1040
- // Guard against a newer click reaching us mid-render.
1041
- if (phaseInfo.fileName !== loadedImage.filename)
1042
- return null;
1043
- return this.plotService.plot(this.plotDivName, loadedImage, phaseInfo, screenHeight, this.plotType, inPlace);
1044
- });
1045
- },
1046
- smallShown: () => {
1047
- if (!isCurrent())
1048
- return; // superseded by a newer image
1049
- // If the file is still being cached/prepared server-side, keep the full
1050
- // cache-progress overlay up instead of dropping to the translucent "Sharpening
1051
- // preview..." spinner over a blank canvas (a large uncached image renders its
1052
- // small preview before its tiles exist). finished() releases the overlay once
1053
- // the real render lands.
1054
- if (this.isCaching)
1055
- return;
1056
- // Small tier on screen — drop the full overlay but keep a translucent spinner so
1057
- // the blurry render isn't mistaken for the final image.
1058
- releaseOverlay();
1059
- this.sharpening = true;
1060
- },
1061
- sharpenSettled: () => {
1062
- if (!isCurrent())
1063
- return; // superseded by a newer image
1064
- this.sharpening = false;
1065
- },
1066
- finished: (_viaSmall, logTag) => {
1067
- if (!isCurrent())
1068
- return; // superseded by a newer image
1069
- // Idempotent — releases now if smallShown deferred it (caching) or was skipped.
1070
- releaseOverlay();
1071
- this.running = false;
1072
- applyRoi();
1073
- this.activateSelectedPlotMode();
1074
- this.activateOpenDialogTool();
1075
- console.log(logTag);
1076
- },
1077
- sharpenFailed: (err) => {
1078
- if (!isCurrent())
1079
- return; // superseded by a newer image
1080
- // The small tier stays on screen as the fallback — tell the
1081
- // user the sharper version isn't coming.
1082
- const msg = err?.error?.message || err?.message || err?.statusText || String(err);
1083
- this.messageService.add({
1084
- key: this.vizAlertToastKey,
1085
- severity: 'warn',
1086
- summary: 'Preview not sharpened',
1087
- detail: `The full-resolution preview did not load (${msg}). The low-resolution preview is still shown. Try clicking the image again.`,
1088
- });
1089
- this.running = false;
1090
- applyRoi();
1091
- this.activateSelectedPlotMode();
1092
- this.activateOpenDialogTool();
1093
- },
1094
- }).render(imgInfo, smallImgInfo);
1095
- }
585
+ this.plotService.setImageMeta(imgInfo.imageMeta, imgInfo.fileName);
586
+ // A newer image always preempts an in-flight render (see ImageRenderSession).
587
+ if (imgInfo.urls)
588
+ this.render.render(imgInfo);
1096
589
  }
1097
590
  },
1098
591
  error: (err) => {
1099
- const msg = err?.error?.message || err?.message || err?.statusText || String(err);
592
+ const msg = errorMessage(err);
1100
593
  console.error('Error occured when getting image info', err);
1101
594
  this.messageService.add({
1102
595
  key: this.vizAlertToastKey,
@@ -1106,323 +599,142 @@ export class VisualizerComponent {
1106
599
  detail: `The following error occured while getting image info: ${msg}.
1107
600
  Please try to open the image again through the file navigator.`,
1108
601
  });
1109
- this.stackLoading = false;
602
+ this.stackLoading.set(false);
1110
603
  this.state.setImageLoading(false);
1111
- this.running = false;
604
+ this.render.running = false;
1112
605
  this.plotService.reset();
1113
606
  },
1114
607
  });
1115
608
  }
1116
- ngAfterViewInit() {
1117
- this.plotContextMenuListener = (event) => {
1118
- const plotEl = document.getElementById(this.plotDivName);
1119
- if (!plotEl?.contains(event.target))
1120
- return;
1121
- event.preventDefault();
1122
- event.stopImmediatePropagation();
1123
- this.ngZone.run(() => {
1124
- this.contextMenuItems = this.buildContextMenuItems();
1125
- this.cdr.detectChanges();
1126
- this.contextMenu.show(event);
1127
- });
1128
- };
1129
- window.addEventListener('contextmenu', this.plotContextMenuListener, true);
1130
- this.keydownListener = (event) => {
1131
- const target = event.target;
1132
- if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)
1133
- return;
1134
- // SAM point mode: Enter commits the object, Esc clears the prompt.
1135
- if (this.activeDragMode === 'samPoint' && (event.key === 'Enter' || event.key === 'Escape')) {
1136
- this.ngZone.run(() => {
1137
- if (event.key === 'Enter')
1138
- this.plotService.commitSamPoints();
1139
- else
1140
- this.plotService.clearSamPoints();
1141
- this.hideSamToast(); // prompt resolved → dismiss the status toast
1142
- });
1143
- return;
1144
- }
1145
- // Region history (jit-ui#85): Ctrl/Cmd+Z undoes; Ctrl/Cmd+Shift+Z or
1146
- // Ctrl/Cmd+Y redoes.
1147
- if (event.ctrlKey || event.metaKey) {
1148
- const k = event.key.toLowerCase();
1149
- if (k === 'z' && !event.shiftKey) {
1150
- event.preventDefault();
1151
- this.ngZone.run(() => this.undoRegion());
1152
- return;
1153
- }
1154
- if ((k === 'z' && event.shiftKey) || k === 'y') {
1155
- event.preventDefault();
1156
- this.ngZone.run(() => this.redoRegion());
1157
- return;
1158
- }
1159
- }
1160
- if (event.key === 'Delete' || event.key === 'Backspace' || event.key === 'd' || event.key === 'D') {
1161
- this.ngZone.run(() => this.deleteRegion());
1162
- }
1163
- else if (event.key === '+' || event.key === '=') {
1164
- this.ngZone.run(() => this.zoomIn());
1165
- }
1166
- else if (event.key === '-' || event.key === '_') {
1167
- this.ngZone.run(() => this.zoomOut());
1168
- }
1169
- else if (event.key === 'p') {
1170
- this.ngZone.run(() => this.toggleDragMode('pan'));
1171
- }
1172
- else if (event.key === 'b') {
1173
- this.ngZone.run(() => this.toggleDragMode('zoomToBox'));
1174
- }
1175
- else if (event.key === 'r') {
1176
- this.ngZone.run(() => this.toggleDragMode('drawrect'));
1177
- }
1178
- else if (event.key === 'f') {
1179
- this.ngZone.run(() => this.toggleDragMode('drawclosedpath'));
1180
- }
1181
- else if (event.key === 'w') {
1182
- this.ngZone.run(() => this.toggleDragMode('wand'));
1183
- }
1184
- else if (event.key === 'e') {
1185
- this.ngZone.run(() => this.toggleDragMode('eraseVertex'));
1186
- }
1187
- else if (event.key === 's') {
1188
- this.ngZone.run(() => this.toggleDragMode('select'));
1189
- }
1190
- else if (event.key === 'l') {
1191
- this.ngZone.run(() => this.toggleDragMode('drawopenpath'));
1192
- }
1193
- };
1194
- window.addEventListener('keydown', this.keydownListener);
1195
- this.wheelListener = (event) => {
1196
- const plotEl = document.getElementById(this.plotDivName);
1197
- if (!plotEl?.contains(event.target))
1198
- return;
1199
- // Intercepting here fires a FIXED zoom step per wheel event, which is far
1200
- // too sensitive for a renderer that reads the scroll delta — so anything
1201
- // drawn by such a renderer keeps its own wheel. See `rendererOwnsWheel`.
1202
- if (rendererOwnsWheel(this.basePlotType))
1203
- return;
1204
- // 3D plot types (surface, scatter3d, isosurface) render in a Plotly scene
1205
- // that orbits/zooms natively on scroll. The 2D step-zoom doesn't apply and
1206
- // would throw (no xaxis on a scene), so let Plotly handle the wheel.
1207
- if (!this.isHeatmap)
1208
- return;
1209
- event.preventDefault();
1210
- event.stopImmediatePropagation();
1211
- if (event.deltaY < 0) {
1212
- this.ngZone.run(() => this.zoomIn());
1213
- }
1214
- else if (event.deltaY > 0) {
1215
- this.ngZone.run(() => this.zoomOut());
1216
- }
1217
- };
1218
- window.addEventListener('wheel', this.wheelListener, { capture: true, passive: false });
1219
- // Drag handling for the floating intensity-profile panel.
1220
- this.profileDragMoveListener = (e) => {
1221
- if (this.profilePanelDragging) {
1222
- this.ngZone.run(() => {
1223
- this.profilePanelPos = {
1224
- x: this.profilePanelStart.x + (e.clientX - this.profilePanelStart.mx),
1225
- y: this.profilePanelStart.y + (e.clientY - this.profilePanelStart.my),
1226
- };
1227
- });
1228
- }
1229
- else if (this.toolbarDragging) {
1230
- this.ngZone.run(() => {
1231
- this.toolbarPos = {
1232
- x: this.toolbarStart.x + (e.clientX - this.toolbarStart.mx),
1233
- y: this.toolbarStart.y + (e.clientY - this.toolbarStart.my),
1234
- };
1235
- });
609
+ /**
610
+ * Follow `source` until destroy. Host streams can emit outside the Angular zone (a
611
+ * serverless host clears its loading flag from an image decode or an OpenSeadragon
612
+ * callback); setting a signal there marks this OnPush view but schedules no change
613
+ * detection, so the update is applied inside the zone, as {@link changed} does.
614
+ */
615
+ mirror(source, apply) {
616
+ source.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
617
+ if (NgZone.isInAngularZone())
618
+ apply(value);
619
+ else
620
+ this.ngZone.run(() => apply(value));
621
+ });
622
+ }
623
+ /**
624
+ * State this view shows changed outside one of its template events (a stream, a
625
+ * resolved promise, a host call): mark the OnPush view for check, re-entering the
626
+ * zone when it changed outside it so a change-detection pass follows.
627
+ */
628
+ changed() {
629
+ if (NgZone.isInAngularZone())
630
+ this.cdr.markForCheck();
631
+ else
632
+ this.ngZone.run(() => this.cdr.markForCheck());
633
+ }
634
+ /**
635
+ * Lay a new image out once the old view is gone: the stack flag, the slice bounds (one
636
+ * url per slice, so the last index is length-1), and the one-shot `initialZIndex` hint
637
+ * (e.g. the file the user clicked within a numbered series). The hint is consumed so
638
+ * re-delivering the same info (a plot-type switch, reloadAndPlot) does not reset the
639
+ * user's scrub; the host's object is never written to (a host store may freeze it) —
640
+ * the component keeps a copy without the hint, and remembers the host object.
641
+ */
642
+ layOutSlices(info) {
643
+ this.stackLoading.set(info.isStack && info.showStack);
644
+ this.maxIndex = info.urls.length > 1 ? info.urls.length - 1 : 0;
645
+ if (info.initialZIndex !== undefined) {
646
+ if (!this.consumedSliceHints.has(info)) {
647
+ this.consumedSliceHints.add(info);
648
+ this.zIndex = info.initialZIndex;
1236
649
  }
1237
- };
1238
- this.profileDragUpListener = () => {
1239
- this.profilePanelDragging = false;
1240
- this.toolbarDragging = false;
1241
- };
1242
- window.addEventListener('mousemove', this.profileDragMoveListener);
1243
- window.addEventListener('mouseup', this.profileDragUpListener);
1244
- // On any window resize, reflow the intensity inset to its (fixed) panel size
1245
- // on the next frame, once layout has settled. Without this the inset can be
1246
- // left at a stale/zero size by a mid-reflow resize and stop showing.
1247
- this.profileResizeListener = () => {
1248
- if (!this.hasProfiles)
1249
- return;
1250
- requestAnimationFrame(() => this.renderIntensityInset());
1251
- };
1252
- window.addEventListener('resize', this.profileResizeListener);
650
+ this.imageInfo = { ...info, initialZIndex: undefined };
651
+ }
652
+ this.updateZIndex();
653
+ }
654
+ ngAfterViewInit() {
655
+ // Window listeners are registered OUTSIDE the Angular zone: zone-patched, every
656
+ // mousemove anywhere in the host app ran app-wide change detection. Each handler
657
+ // re-enters the zone (ngZone.run) only when it actually changes view state.
658
+ this.shortcuts = new ViewerShortcuts(this.shortcutHost(), this.ngZone);
659
+ this.shortcuts.attach();
1253
660
  this.onViewReady();
1254
661
  }
1255
662
  /** The plot div exists now: run an image-less draw that arrived before it did. */
1256
663
  onViewReady() {
1257
- this.viewReady = true;
1258
- const pending = this.pendingSpatialDraw;
1259
- this.pendingSpatialDraw = null;
1260
- // Only if it is still the dataset on offer and no image has arrived meanwhile. By id, not by
1261
- // object: the port may re-emit the same dataset as a new object (a colour-column change does),
1262
- // and then the current object is the one to draw.
1263
- const current = this.spatialDataset;
1264
- if (pending && current && current.id === pending.id && !this.imageInfo) {
1265
- void this.plotSpatialWithoutImage(current);
1266
- }
1267
- }
1268
- onProfilePanelDragStart(e) {
1269
- this.profilePanelDragging = true;
1270
- this.profilePanelStart = {
1271
- mx: e.clientX,
1272
- my: e.clientY,
1273
- x: this.profilePanelPos.x,
1274
- y: this.profilePanelPos.y,
664
+ this.render.viewIsReady(this.spatial.dataset, !!this.imageInfo);
665
+ }
666
+ /** What the keyboard, wheel and context-menu shortcuts act on. Their listeners live
667
+ * outside this view, so every action that changes what it shows marks it for check. */
668
+ shortcutHost() {
669
+ const marked = (act) => (...args) => {
670
+ act(...args);
671
+ this.changed();
1275
672
  };
1276
- e.preventDefault();
1277
- }
1278
- /** Grab the toolbar handle: detach it into a floating window (if still docked)
1279
- * and start dragging. */
1280
- onToolbarDragStart(e) {
1281
- if (!this.toolbarFloating) {
1282
- this.toolbarFloating = true;
1283
- this.toolbarPos = { x: 8, y: 8 };
1284
- }
1285
- this.toolbarDragging = true;
1286
- this.toolbarStart = {
1287
- mx: e.clientX,
1288
- my: e.clientY,
1289
- x: this.toolbarPos.x,
1290
- y: this.toolbarPos.y,
673
+ return {
674
+ plotDivName: this.plotDivName,
675
+ hostElement: this.hostRef?.nativeElement ?? null,
676
+ activeDragMode: () => this.activeDragMode,
677
+ canStepSlice: () => !!this.imageInfo?.isStack && this.isImageView,
678
+ wheelZooms: () => !rendererOwnsWheel(this.basePlotType) && this.isHeatmap,
679
+ stepSlice: marked((delta) => this.stepSlice(delta)),
680
+ resolveSamPrompt: marked((commit) => {
681
+ if (commit)
682
+ this.plotService.commitSamPoints();
683
+ else
684
+ this.plotService.clearSamPoints();
685
+ this.segmentation.hide(); // prompt resolved → dismiss the status toast
686
+ }),
687
+ undo: marked(() => this.undoRegion()),
688
+ redo: marked(() => this.redoRegion()),
689
+ deleteRegion: marked(() => this.deleteRegion()),
690
+ zoomIn: marked(() => this.zoomIn()),
691
+ zoomOut: marked(() => this.zoomOut()),
692
+ toggleDragMode: marked((mode) => this.toggleDragMode(mode)),
693
+ openContextMenu: (event) => {
694
+ this.contextMenuItems = this.buildContextMenuItems();
695
+ this.cdr.detectChanges();
696
+ this.contextMenu.show(event);
697
+ },
1291
698
  };
1292
- e.preventDefault();
1293
699
  }
1294
700
  /** Snap the floating toolbar back to its docked position across the top. */
1295
701
  dockToolbar() {
1296
702
  this.toolbarFloating = false;
1297
703
  }
1298
- /** Render the intensity inset chart from the latest profile data. The actual
1299
- * charting is owned by the visualizer service — the component just decides
1300
- * when (profile mode + a fresh profile). */
1301
- renderIntensityInset() {
1302
- if (!this.hasProfiles)
1303
- return;
1304
- this.plotService.renderIntensityInset(this.intensityInsetDiv, this.latestProfiles);
1305
- }
1306
704
  /** Toolbar "Intensity" group: add another line ROI (next bright colour). */
1307
705
  async addProfileLine() {
1308
- // Park the floating inset near the plot's top-right when the first line is
1309
- // added (it's position:fixed, so use viewport coords from the plot rect).
1310
- if (!this.hasProfiles) {
1311
- const rect = document.getElementById(this.plotDivName)?.getBoundingClientRect();
1312
- this.profilePanelPos = rect
1313
- ? { x: Math.max(10, rect.right - 300), y: rect.top + 10 }
1314
- : { x: 20, y: 70 };
1315
- }
1316
- // Image (OSD) mode: Plotly never rendered, so it has no pixel cache / extent.
1317
- // Load the current slice's frames for sampling + line placement first.
1318
- if (this.isImageView && this.imageInfo) {
1319
- await this.plotService.ensureIntensitySampling(this.imageInfo, this.zIndex);
1320
- }
1321
- const region = this.plotService.getIntensityControls()?.addProfileLine();
1322
- // Auto-select the new line on the active backend (Plotly handles / OSD
1323
- // highlight) so it's ready to move or delete immediately.
1324
- if (region) {
1325
- this.plotService.selectRegion(region);
1326
- // OSD only draws a selected region's handles in an edit mode, so a line in
1327
- // 'none' mode looks unselected. Switch to 'select' so the new line shows
1328
- // its endpoint handles and can be dragged/deleted right away.
1329
- if (this.isImageView && this.activeDragMode !== 'select') {
1330
- this.toggleDragMode('select');
1331
- }
1332
- }
706
+ const region = await this.inset?.addProfileLine();
707
+ // OSD only draws a selected region's handles in an edit mode, so a line in
708
+ // 'none' mode looks unselected. Switch to 'select' so the new line shows its
709
+ // endpoint handles and can be dragged/deleted right away.
710
+ if (region && this.isImageView && this.activeDragMode !== 'select')
711
+ this.toggleDragMode('select');
1333
712
  }
1334
713
  ngOnDestroy() {
1335
714
  // A contributed mode's session ends first, while the viewer it drew over
1336
715
  // still exists. The panel goes with this view, so no change detection.
1337
- this.destroying = true;
1338
- this.plotModes.deactivate();
1339
- this.toolDialogs.deactivate();
1340
- // Leave the live set so the next-oldest visualizer picks up the outlets.
716
+ this.contributions.destroy();
717
+ // Leave the live set so the next-oldest visualizer picks up the outlets — on its
718
+ // next check, which an OnPush sibling needs asking for.
1341
719
  VisualizerComponent.liveInstances.delete(this);
1342
- this.revokeVolumeImageUrls();
720
+ for (const other of VisualizerComponent.liveInstances)
721
+ other.changed();
722
+ this.state.setDiagram(null);
723
+ this.render.cancel();
724
+ this.spatial.dispose();
1343
725
  this.scrubber.cancel();
1344
- this.samPointSub.unsubscribe();
1345
- if (this.plotContextMenuListener) {
1346
- window.removeEventListener('contextmenu', this.plotContextMenuListener, true);
1347
- }
1348
- if (this.keydownListener) {
1349
- window.removeEventListener('keydown', this.keydownListener);
1350
- }
1351
- if (this.wheelListener) {
1352
- window.removeEventListener('wheel', this.wheelListener, true);
1353
- }
1354
- if (this.profileDragMoveListener) {
1355
- window.removeEventListener('mousemove', this.profileDragMoveListener);
1356
- }
1357
- if (this.profileDragUpListener) {
1358
- window.removeEventListener('mouseup', this.profileDragUpListener);
1359
- }
1360
- if (this.profileResizeListener) {
1361
- window.removeEventListener('resize', this.profileResizeListener);
1362
- }
1363
- if (this.intensityProfileSub) {
1364
- this.intensityProfileSub.unsubscribe();
1365
- }
1366
- if (this.viewportChangeSub) {
1367
- this.viewportChangeSub.unsubscribe();
1368
- }
1369
- this.unsub.next();
1370
- this.unsub.complete();
1371
- if (this.previewSubscription) {
1372
- this.previewSubscription.unsubscribe();
1373
- }
1374
- if (this.spatialDatasetSubscription) {
1375
- this.spatialDatasetSubscription.unsubscribe();
1376
- }
1377
- if (this.plotWidthSubscription) {
1378
- this.plotWidthSubscription.unsubscribe();
1379
- }
1380
- if (this.imageLoadingSubscription) {
1381
- this.imageLoadingSubscription.unsubscribe();
1382
- }
1383
- if (this.imgLoadingMessageSubscription) {
1384
- this.imgLoadingMessageSubscription.unsubscribe();
1385
- }
1386
- if (this.isZoomSubscription) {
1387
- this.isZoomSubscription.unsubscribe();
1388
- }
1389
- if (this.autoscaleSubscription) {
1390
- this.autoscaleSubscription.unsubscribe();
1391
- }
1392
- if (this.filenameSubscription) {
1393
- this.filenameSubscription.unsubscribe();
1394
- }
1395
- this.plotService.unsubscribe();
726
+ this.shortcuts?.detach();
727
+ this.plotService.detach();
1396
728
  }
1397
729
  hasRegions() {
1398
- // Use the contract's framework-neutral region accessor, not the raw
1399
- // Plotly-shaped getShapes() — the component only needs to know whether any
1400
- // region exists, and must not depend on a backend's wire format.
730
+ // The contract's framework-neutral region accessor: the component only needs to
731
+ // know whether any region exists, never a backend's wire format.
1401
732
  return this.plotService.getRegions().length > 0;
1402
733
  }
1403
- /**
1404
- * This method is called in mainFacade.configRequest() - DO NOT DELETE
1405
- */
734
+ /** The current regions as polygons (also on the host handle). */
1406
735
  getRegionPolygons() {
1407
736
  return this.plotService.getRegionPolygons();
1408
737
  }
1409
- /**
1410
- * Select the color scale type: called by the color scale select button
1411
- * @param event
1412
- */
1413
- toggleReverseScale() {
1414
- this.reversescale = !this.reversescale;
1415
- this.plotService.setReverseScale(this.reversescale);
1416
- }
1417
- /**
1418
- * Select a color map: called by the color map dropdown button
1419
- * @param colormapNode
1420
- */
1421
- selectColormap(colormapNode) {
1422
- if (!colormapNode.children) {
1423
- this.plotService.setColormap(colormapNode);
1424
- }
1425
- }
1426
738
  /** Open the Channels & Histogram dialog (toolbar button). */
1427
739
  openChannelHistogram() {
1428
740
  this.showChannelHistogram = true;
@@ -1446,15 +758,10 @@ export class VisualizerComponent {
1446
758
  this.regionEditorWidth = width && width > 0 ? `${Math.round(width)}px` : '25vw';
1447
759
  this.showRegionEditor = true;
1448
760
  }
1449
- /**
1450
- * Called by the stack/single image dropdown button
1451
- * @param showstack
1452
- */
761
+ /** The toolbar's Single image / Stack toggle. */
1453
762
  selectStackOption(selectedStackOption) {
1454
763
  const showstack = selectedStackOption.val === 'true';
1455
- this.selectedStackOption = selectedStackOption;
1456
- console.log('selected stack option' + JSON.stringify(this.selectedStackOption));
1457
- this.stackLoading = showstack;
764
+ this.stackLoading.set(showstack);
1458
765
  this.state.setImageLoading(!showstack);
1459
766
  // Stack mode is always a 2D heatmap — reset surface mode if active
1460
767
  if (!this.isHeatmap) {
@@ -1465,19 +772,14 @@ export class VisualizerComponent {
1465
772
  }
1466
773
  this.plotService.setShowStack(showstack);
1467
774
  }
1468
- updateZIndex(urls) {
775
+ /** Clamp the slice index into [0, maxIndex] and push it to the renderer. */
776
+ updateZIndex() {
1469
777
  if (this.zIndex > this.maxIndex) {
1470
778
  this.zIndex = this.maxIndex;
1471
779
  }
1472
780
  if (this.zIndex < 0) {
1473
781
  this.zIndex = 0;
1474
782
  }
1475
- if (urls) {
1476
- if (this.zIndex > urls?.length) {
1477
- this.zIndex = 0;
1478
- this.maxIndex = urls.length - 1;
1479
- }
1480
- }
1481
783
  this.plotService.setZIndex(this.zIndex);
1482
784
  }
1483
785
  /**
@@ -1490,11 +792,8 @@ export class VisualizerComponent {
1490
792
  return;
1491
793
  this.zIndex = z;
1492
794
  this.scrubber.commit(z);
1493
- // Keep the intensity inset in sync with the displayed slice (Image/OSD mode,
1494
- // where the profile sampler is fed from the loaded preview frames).
1495
- if (this.hasProfiles && this.isImageView && this.imageInfo) {
1496
- this.plotService.ensureIntensitySampling(this.imageInfo, z);
1497
- }
795
+ // Keep the intensity inset sampling the displayed slice (Image view).
796
+ this.inset?.sliceCommitted(z);
1498
797
  }
1499
798
  /**
1500
799
  * Live (debounced) scrub while dragging the Image-view z-slider: update the
@@ -1507,27 +806,6 @@ export class VisualizerComponent {
1507
806
  this.zIndex = z;
1508
807
  this.scrubber.scrub(z);
1509
808
  }
1510
- /**
1511
- * Keyboard stack navigation: ←/→ step the z-slice in the Image view, the same
1512
- * way the slider does. Only active for a loaded stack in Image view, and
1513
- * ignored while a form field is focused so typing isn't hijacked. Up/Down are
1514
- * left to OpenSeadragon (panning).
1515
- */
1516
- onKeydown(e) {
1517
- if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight')
1518
- return;
1519
- if (!this.imageInfo?.isStack || !this.isImageView)
1520
- return;
1521
- const t = e.target;
1522
- if (t && (/^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName) || t.isContentEditable))
1523
- return;
1524
- // The slice slider handles arrows natively when focused (also a +1 step) —
1525
- // skip here so we don't double-step it.
1526
- if (t && (t.getAttribute('role') === 'slider' || t.closest('.p-slider')))
1527
- return;
1528
- e.preventDefault();
1529
- this.stepSlice(e.key === 'ArrowRight' ? 1 : -1);
1530
- }
1531
809
  /** Move the displayed slice by `delta`, clamped to the stack bounds. */
1532
810
  stepSlice(delta) {
1533
811
  const next = Math.min(this.maxIndex, Math.max(0, this.zIndex + delta));
@@ -1545,80 +823,34 @@ export class VisualizerComponent {
1545
823
  if (this.imageInfo) {
1546
824
  this.state.setImageInfo(this.imageInfo);
1547
825
  }
1548
- else if (this.spatialDataset && (isSpatialOmics(this.plotType) || isSpatialOmics3d(this.plotType))) {
826
+ else if (this.spatial.dataset && (isSpatialOmics(this.plotType) || isSpatialOmics3d(this.plotType))) {
1549
827
  // A spatial dataset that brings no image, opened with no image loaded (a host's
1550
828
  // first view): there is no image info to re-drive, so draw the spatial mode itself.
1551
- void this.plotSpatialWithoutImage(this.spatialDataset);
829
+ void this.render.plotWithoutImage(this.spatial.dataset);
1552
830
  }
1553
831
  else {
1554
832
  this.plotService.reloadAndPlot();
1555
833
  }
1556
834
  }
1557
- /**
1558
- * Draw a spatial mode with no image behind it: the observations alone, framed on their own
1559
- * extent, as the seqFISH example shows them. The image info is a placeholder naming the
1560
- * dataset, so regions drawn here are kept per dataset like any image's.
1561
- */
1562
- async plotSpatialWithoutImage(dataset) {
1563
- if (!this.viewReady) {
1564
- // A host that creates the visualizer AFTER publishing the dataset (jit-ui opening a file
1565
- // from its tree) has it replayed into ngOnInit, before the template and its plot div
1566
- // exist; drawing now finds no target. Draw once the view is ready (onViewReady).
1567
- this.pendingSpatialDraw = dataset;
1568
- return;
1569
- }
1570
- // The same generation as the image pipeline: a newer image or dataset supersedes this
1571
- // draw, and a superseded draw neither reports nor releases the newer one's loading state.
1572
- const token = ++this.renderToken;
1573
- const div = document.getElementById(this.plotDivName);
1574
- const info = {
1575
- isGrayscale: false, trueImageSize: [0, 0], urls: [], isStack: false, showStack: false,
1576
- scaleRatio: true, fileName: `spatial:${dataset.id}`, imageMeta: [],
1577
- };
1578
- let failure = null;
1579
- try {
1580
- // A backend that cannot draw (no WebGPU, no plot target) resolves false rather than throwing.
1581
- if (!(await this.plotService.plot(this.plotDivName, null, info, div?.offsetHeight || 500, this.plotType))) {
1582
- failure = 'the renderer could not start';
1583
- }
1584
- }
1585
- catch (err) {
1586
- failure = err;
1587
- }
1588
- if (token !== this.renderToken)
1589
- return;
1590
- if (failure) {
1591
- console.warn('[visualizer] could not draw the spatial dataset', failure);
1592
- const e = failure;
1593
- this.messageService.add({
1594
- key: this.vizAlertToastKey,
1595
- severity: 'error',
1596
- summary: 'Could not draw the dataset',
1597
- // An image-less dataset has no tissue image to fall back on, so say so rather than
1598
- // leave an empty canvas that looks finished.
1599
- detail: `${dataset.name ?? dataset.id}: ${e?.message ?? String(failure)}.`,
1600
- });
1601
- }
1602
- this.state.setImageLoading(false);
1603
- this.cdr.detectChanges();
1604
- }
1605
835
  cancelLoading() {
1606
836
  // Stop any in-flight frame streaming (napari-js volume/surface preload) so the fetch loops
1607
837
  // actually abort — clearing the flag alone only routed to Plotly and left napari fetching.
1608
838
  this.plotService.cancelLoading?.();
1609
- this.stackLoading = false;
1610
- this.imgLoading = false;
839
+ // The cancelled render is superseded, not just aborted: its aborted load must not
840
+ // come back as "Could not draw the image", nor apply ROIs for an image left unshown.
841
+ this.render.cancel();
842
+ this.stackLoading.set(false);
843
+ this.imgLoading.set(false);
1611
844
  this.state.setImageLoading(false);
1612
845
  this.zIndex = 0;
1613
846
  this.plotService.setZIndex(this.zIndex);
1614
- this.running = false;
1615
847
  this.plotService.setStackLoading(false);
1616
848
  }
1617
849
  downloadImage() {
1618
850
  this.plotService.downloadImage();
1619
851
  }
1620
852
  autoscaleImage() {
1621
- this.plotService.autoscale();
853
+ this.plotService.fitToView();
1622
854
  }
1623
855
  /** Toolbar pixel/smooth toggle: flip smoothing and apply to the active backend. */
1624
856
  onToggleImageSmoothing() {
@@ -1629,53 +861,7 @@ export class VisualizerComponent {
1629
861
  this.plotService.resetAxes();
1630
862
  }
1631
863
  toggleDragMode(mode) {
1632
- // A tool picked by the user is always the plain tool, never a plot mode's
1633
- // brush class.
1634
- this.brushClass = null;
1635
- // Toggle off if the same mode is re-selected.
1636
- this.applyDragMode(this.activeDragMode === mode ? null : mode);
1637
- }
1638
- /** Arm `mode` (or nothing) across every tool, keeping the toolbar in step. */
1639
- applyDragMode(mode) {
1640
- this.activeDragMode = mode;
1641
- const active = this.activeDragMode;
1642
- // Record the armed tool in the shared session store.
1643
- this.session.setActiveTool(active);
1644
- // Region draw/select run through the renderer's region overlay.
1645
- this.plotService
1646
- .getRegionOverlay()
1647
- ?.setMode(this.isRegionMode(active) ? active : 'none');
1648
- // Viewport drag modes (pan/box-zoom). Region modes own the drag mode via
1649
- // the overlay, so don't also set one here.
1650
- if (!this.isRegionMode(active)) {
1651
- const viewportDrag = active === 'pan' || active === 'zoom';
1652
- this.plotService.setDragMode(viewportDrag ? active : false);
1653
- }
1654
- // On-canvas tool overlays.
1655
- this.plotService.setZoomToBoxMode(active === 'zoomToBox');
1656
- this.plotService.setWandMode(active === 'wand', { sensitivity: this.wandSensitivity });
1657
- this.plotService.setBrushMode(active === 'brush', { size: this.brushSize, ...this.brushClass });
1658
- this.plotService.setSamPointMode(active === 'samPoint');
1659
- // Leaving point mode dismisses any lingering status toast.
1660
- if (active !== 'samPoint')
1661
- this.hideSamToast();
1662
- this.plotService.setVertexEraserMode(active === 'eraseVertex');
1663
- if (active === 'eraseVertex') {
1664
- this.plotService.setVertexEraserRadius(this.vertexEraserRadius);
1665
- }
1666
- }
1667
- /** Region draw/select/edit modes routed through the region overlay. The
1668
- * The vertex tools (drawpolygon/addpoint/deletepoint/move) are
1669
- * handled by the OpenSeadragon overlay; Plotly's overlay maps them to no-op. */
1670
- isRegionMode(mode) {
1671
- return (mode === 'drawrect' ||
1672
- mode === 'drawclosedpath' ||
1673
- mode === 'drawopenpath' ||
1674
- mode === 'select' ||
1675
- mode === 'drawpolygon' ||
1676
- mode === 'addpoint' ||
1677
- mode === 'deletepoint' ||
1678
- mode === 'move');
864
+ this.tools.toggle(mode);
1679
865
  }
1680
866
  /** Live-update the isosurface as the range slider moves. The control is only
1681
867
  * available when the active backend renders isosurfaces (ISOSURFACE mode). */
@@ -1686,22 +872,13 @@ export class VisualizerComponent {
1686
872
  this.plotService.getIsosurfaceControls()?.setIsoRange(values[0], values[1]);
1687
873
  }
1688
874
  onWandSensitivityChange(value) {
1689
- if (value === undefined || !Number.isFinite(value))
1690
- return;
1691
- this.wandSensitivity = value;
1692
- this.plotService.setWandOptions({ sensitivity: value });
875
+ this.tools.setWandSensitivity(value);
1693
876
  }
1694
877
  onBrushSizeChange(value) {
1695
- if (value === undefined || !Number.isFinite(value))
1696
- return;
1697
- this.brushSize = value;
1698
- this.plotService.setBrushOptions({ size: value });
878
+ this.tools.setBrushSize(value);
1699
879
  }
1700
880
  onVertexEraserRadiusChange(value) {
1701
- if (value === undefined || !Number.isFinite(value))
1702
- return;
1703
- this.vertexEraserRadius = value;
1704
- this.plotService.setVertexEraserRadius(value);
881
+ this.tools.setVertexEraserRadius(value);
1705
882
  }
1706
883
  zoomIn() {
1707
884
  this.plotService.zoomIn();
@@ -1721,281 +898,61 @@ export class VisualizerComponent {
1721
898
  this.plotService.redo();
1722
899
  }
1723
900
  // ── Region set-operations on the current selection (jit-ui#85) ──────────
1724
- /** The currently-selected regions (live store instances). */
1725
- get selectedRegions() {
1726
- const all = this.plotService.getRegions();
1727
- return this.selectedIndices
1728
- .map((i) => all[i])
1729
- .filter((r) => !!r);
1730
- }
1731
- /** Regions eligible for set-ops: closed areas (rect / closed polygon /
1732
- * multi-polygon), excluding intensity-profile lines. */
1733
- opEligible(regions) {
1734
- return regions.filter((r) => r.kind !== 'profile' && (r.bounds instanceof Rectangle ||
1735
- r.bounds instanceof MultiPolygon ||
1736
- (r.bounds instanceof Polygon && r.bounds.closed !== false)));
1737
- }
1738
- /** Image pixel dimensions for the raster ops; falls back to the selection's
1739
- * extent when the image size isn't known (keeps clamping sane). */
1740
- opImageDims(regions) {
1741
- let [w, h] = this.imageInfo?.trueImageSize ?? [0, 0];
1742
- if (!(w > 0) || !(h > 0)) {
1743
- let maxX = 0, maxY = 0;
1744
- const scan = (xs, ys) => {
1745
- for (const x of xs)
1746
- maxX = Math.max(maxX, x);
1747
- for (const y of ys)
1748
- maxY = Math.max(maxY, y);
1749
- };
1750
- for (const r of regions) {
1751
- const b = r.bounds;
1752
- if (b instanceof Rectangle)
1753
- scan([b.x + b.width], [b.y + b.height]);
1754
- else if (b instanceof Polygon)
1755
- scan(b.xpoints, b.ypoints);
1756
- else if (b instanceof MultiPolygon)
1757
- for (const p of b.polygons)
1758
- scan(p.xpoints, p.ypoints);
1759
- }
1760
- w = Math.max(w, Math.ceil(maxX) + 2);
1761
- h = Math.max(h, Math.ceil(maxY) + 2);
1762
- }
1763
- return { w, h };
1764
- }
1765
- /** True when ≥2 eligible regions are selected (Merge available). */
1766
- get canMergeRegions() { return this.opEligible(this.selectedRegions).length >= 2; }
1767
- /** True when any selected region is multi-part (Ungroup available). */
1768
- get canUngroupRegions() { return this.selectedRegions.some((r) => this.regionOps.canUngroup(r)); }
1769
- /** True when ≥1 eligible region is selected (Inverse / Simplify available). */
1770
- get hasEligibleSelection() { return this.opEligible(this.selectedRegions).length >= 1; }
1771
- /** Select every region on the image (excludes intensity-profile lines). */
1772
901
  selectAllRegions() {
1773
- const all = this.plotService.getRegions();
1774
- const indices = [];
1775
- all.forEach((r, i) => { if (r.kind !== 'profile')
1776
- indices.push(i); });
1777
- this.plotService.setSelectedShapeIndices(indices);
902
+ this.regionActions.selectAll();
1778
903
  }
1779
- /** Merge the selected regions into a single (possibly multi-part) region. */
1780
904
  mergeRegions() {
1781
- const sel = this.opEligible(this.selectedRegions);
1782
- if (sel.length < 2)
1783
- return;
1784
- const { w, h } = this.opImageDims(sel);
1785
- const merged = this.regionOps.merge(sel, w, h);
1786
- if (merged)
1787
- this.replaceRegions(sel, [merged]);
905
+ this.regionActions.merge();
1788
906
  }
1789
- /** Split each selected multi-part region into one region per part. */
1790
907
  ungroupRegions() {
1791
- const sel = this.selectedRegions.filter((r) => this.regionOps.canUngroup(r));
1792
- if (sel.length === 0)
1793
- return;
1794
- const results = [];
1795
- for (const r of sel)
1796
- results.push(...this.regionOps.ungroup(r));
1797
- this.replaceRegions(sel, results);
908
+ this.regionActions.ungroup();
1798
909
  }
1799
- /** Replace the selection with its inverse inside the image rectangle. */
1800
910
  inverseRegions() {
1801
- const sel = this.opEligible(this.selectedRegions);
1802
- if (sel.length === 0)
1803
- return;
1804
- const { w, h } = this.opImageDims(sel);
1805
- const inv = this.regionOps.inverse(sel, w, h);
1806
- if (!inv) {
1807
- this.messageService.add({
1808
- key: this.resultToastKey,
1809
- severity: 'warn', summary: 'Inverse',
1810
- detail: 'Nothing to invert — select one or more closed regions first.',
1811
- });
1812
- return;
1813
- }
1814
- this.replaceRegions(sel, [inv]);
911
+ this.regionActions.inverse();
1815
912
  }
1816
- /** Douglas–Peucker simplify each selected region by `thresholdPx`. */
1817
913
  simplifyRegions(thresholdPx) {
1818
- const sel = this.opEligible(this.selectedRegions);
1819
- if (sel.length === 0)
1820
- return;
1821
- this.replaceRegions(sel, sel.map((r) => this.regionOps.simplify(r, thresholdPx)));
1822
- this.displaySimplifyDialog = false;
914
+ this.regionActions.simplify(thresholdPx);
1823
915
  }
1824
- /** Open the custom-threshold simplify dialog. */
1825
- openSimplifyDialog() { this.displaySimplifyDialog = true; }
1826
- /**
1827
- * Commit a set-op: drop the `remove` regions, append `add`, and select the
1828
- * results. Goes through setRegions so it's undo-tracked and re-rendered;
1829
- * profile lines and unselected regions are preserved.
1830
- */
1831
- replaceRegions(remove, add) {
1832
- if (add.length === 0)
1833
- return;
1834
- const removeSet = new Set(remove);
1835
- const kept = this.plotService.getRegions().filter((r) => !removeSet.has(r));
1836
- this.plotService.setRegions([...kept, ...add]); // mints ids on `add`, records undo
1837
- const stored = this.plotService.getRegions();
1838
- const sel = add.map((r) => stored.indexOf(r)).filter((i) => i >= 0);
1839
- this.plotService.setSelectedShapeIndices(sel);
916
+ openSimplifyDialog() {
917
+ this.regionActions.openSimplifyDialog();
1840
918
  }
1841
919
  /** Box-prompted SAM segmentation of the drawn rectangles (jit-ui#90). A sticky
1842
920
  * `sam` toast shows live status + a download progress bar (first run pulls the
1843
921
  * encoder, ~170 MB); it stays open until the run finishes (bar hits 100%). */
1844
922
  async segmentRegions() {
1845
- await this.runSegmentWithToast('SAM', this.samTool, () => this.plotService.segmentRectangles());
923
+ await this.segmentation.run('SAM', this.samTool, () => this.plotService.segmentRectangles());
1846
924
  }
1847
925
  /** Auto-segment cells inside each drawn rectangle with cellpose-SAM, client-side
1848
926
  * (jit-ui#90). Each box is cropped (browser slide-crop) then run through the
1849
927
  * cellpose-js model; the same sticky `sam` toast + progress bar is reused. */
1850
928
  async segmentCellpose() {
1851
- await this.runSegmentWithToast('Cellpose', this.cellSegmentTool, () => this.plotService.segmentRectanglesCellpose());
929
+ await this.segmentation.run('Cellpose', this.cellSegmentTool, () => this.plotService.segmentRectanglesCellpose());
1852
930
  }
1853
- /**
1854
- * Parameters currently bound by the generic parameter dialog, for whichever
1855
- * tool is open. Seeded lazily: a tool's defaults are only asked for when the
1856
- * user first touches it, so registering a tool costs nothing until used.
1857
- */
931
+ // ── contributed tool parameters (see ToolParamsModel) ─────────────────
1858
932
  paramsFor(toolId) {
1859
- const tool = this.contributedTools.find((t) => t.id === toolId);
1860
- if (!tool)
1861
- return {};
1862
- if (!this.toolParams[toolId]) {
1863
- // Through `seedParams`, not `defaultParams` directly: the active
1864
- // checkpoint's own `ToolModelOption.defaults` have to be merged on top.
1865
- // Calling `defaultParams` here bypassed them, so the very first run of a
1866
- // tool used the tool's baseline thresholds and tiling rather than the
1867
- // model's — and it only corrected itself once the user switched
1868
- // checkpoints or hit Reset. Every entry point now goes through one path.
1869
- this.toolParams[toolId] = this.seedParams(tool, this.modelIdFor(tool));
1870
- }
1871
- return this.toolParams[toolId];
933
+ return this.toolParams.paramsFor(toolId);
1872
934
  }
1873
- /** Active checkpoint for a tool, falling back to the tool's own default. */
1874
- modelIdFor(tool) {
1875
- return this.toolModelIds[tool.id] ?? tool.defaultModelId();
1876
- }
1877
- /** Switching checkpoint re-seeds the parameters, since the defaults belong to
1878
- * the model rather than to the tool. */
1879
935
  onToolModelChange(e) {
1880
- const tool = this.contributedTools.find((t) => t.id === e.toolId);
1881
- if (!tool)
1882
- return;
1883
- // Reassigned rather than mutated so the toolbar's ngOnChanges sees a new
1884
- // reference and rebuilds that tool's model menu with the check mark moved.
1885
- this.toolModelIds = { ...this.toolModelIds, [e.toolId]: e.modelId };
1886
- tool.onModelChange?.(e.modelId);
1887
- this.toolParams[e.toolId] = this.seedParams(tool, e.modelId);
1888
- }
1889
- /** A tool's defaults for a checkpoint, with that checkpoint's own overrides
1890
- * applied on top — per-model defaults beat per-tool ones. */
1891
- seedParams(tool, modelId) {
1892
- const model = tool.models().find((m) => m.id === modelId);
1893
- return { ...tool.defaultParams(modelId), ...(model?.defaults ?? {}) };
1894
- }
1895
- /**
1896
- * Narrow a param spec for the template.
1897
- *
1898
- * `*ngSwitchCase` does not narrow a discriminated union in Angular's template
1899
- * type checker the way a `switch` does in TypeScript, so the template asks for
1900
- * the variant it has already matched on.
1901
- */
1902
- numberParam(p) {
1903
- return p;
1904
- }
1905
- selectParam(p) {
1906
- return p;
936
+ this.toolParams.setModel(e.toolId, e.modelId);
1907
937
  }
1908
938
  openToolParams(toolId) {
1909
- this.paramsFor(toolId); // seed before the dialog binds to it
1910
- this.openParamsToolId = toolId;
939
+ this.toolParams.open(toolId);
1911
940
  }
1912
- /** The tool whose parameter dialog is open, for the generic dialog's header
1913
- * and field list. */
1914
- get openParamsTool() {
1915
- return this.contributedTools.find((t) => t.id === this.openParamsToolId);
941
+ closeToolParams() {
942
+ this.toolParams.close();
1916
943
  }
1917
944
  resetToolParams(toolId) {
1918
- const tool = this.contributedTools.find((t) => t.id === toolId);
1919
- if (tool)
1920
- this.toolParams[toolId] = this.seedParams(tool, this.modelIdFor(tool));
945
+ this.toolParams.reset(toolId);
1921
946
  }
1922
947
  /** Run a contributed tool over the current view. No prompt: these sweep the
1923
948
  * whole view rather than being pointed at something. Reuses the shared
1924
949
  * segmentation toast so progress reads the same as the SAM/cellpose tools. */
1925
950
  async runTool(toolId) {
1926
- const tool = this.contributedTools.find((t) => t.id === toolId);
951
+ const tool = this.toolParams.find(toolId);
1927
952
  if (!tool)
1928
953
  return;
1929
- const params = { ...this.paramsFor(toolId), modelId: this.modelIdFor(tool) };
1930
- await this.runSegmentWithToast(tool.label, tool.progress, () => tool.run(this.plotService, params));
1931
- }
1932
- /** Shared driver for the box-prompt segment tools: wires the tool's status +
1933
- * download progress into the sticky `sam` toast, runs `op`, and reports the
1934
- * region count. Keeps the toast open until the run settles (bar hits 100%). */
1935
- async runSegmentWithToast(label,
1936
- // Observables rather than BehaviorSubjects: a contributed tool implements
1937
- // ToolProgress, and requiring a concrete subject would force every plugin
1938
- // to expose its internals just to drive this toast.
1939
- tool, op) {
1940
- this.samStatus = 'Starting…';
1941
- this.samProgress = 0;
1942
- this.samDownloading = false;
1943
- this.samBusy = true;
1944
- const psub = tool.progress$.subscribe((f) => {
1945
- this.samDownloading = f >= 0 && f < 1;
1946
- if (f >= 0)
1947
- this.samProgress = Math.min(100, Math.round(f * 100));
1948
- this.cdr.detectChanges();
1949
- });
1950
- // Tracked here rather than read off the subject at the end: `tool` is only
1951
- // an Observable pair now, so there is no `.value` to sample once the run
1952
- // settles. The last non-empty status is what the result toast reports.
1953
- let lastStatus = '';
1954
- const ssub = tool.status$.subscribe((m) => {
1955
- if (m) {
1956
- lastStatus = m;
1957
- this.samStatus = m;
1958
- this.cdr.detectChanges();
1959
- }
1960
- });
1961
- this.showSamToast(label);
1962
- try {
1963
- const n = await op();
1964
- this.messageService.add({
1965
- key: this.resultToastKey,
1966
- severity: n > 0 ? 'success' : 'warn',
1967
- summary: label,
1968
- detail: lastStatus || (n > 0 ? `Added ${n} region(s).` : 'No regions added.'),
1969
- });
1970
- }
1971
- catch (e) {
1972
- this.messageService.add({
1973
- key: this.resultToastKey,
1974
- severity: 'error', summary: `${label} failed`, detail: String(e),
1975
- });
1976
- }
1977
- finally {
1978
- psub.unsubscribe();
1979
- ssub.unsubscribe();
1980
- this.hideSamToast();
1981
- }
1982
- }
1983
- /** Show the shared sticky `sam` toast once (idempotent — re-adding would stack
1984
- * a new toast on every point click). */
1985
- showSamToast(summary) {
1986
- if (this.samToastShown)
1987
- return;
1988
- this.samToastShown = true;
1989
- this.messageService.add({ key: this.samToastKey, sticky: true, severity: 'info', summary });
1990
- }
1991
- /** Dismiss the shared `sam` toast and reset its progress/spinner state. */
1992
- hideSamToast() {
1993
- this.samToastShown = false;
1994
- this.samBusy = false;
1995
- this.samDownloading = false;
1996
- this.samProgress = 0;
1997
- this.messageService.clear(this.samToastKey);
1998
- this.cdr.detectChanges();
954
+ const params = this.toolParams.runParams(tool);
955
+ await this.segmentation.run(tool.label, tool.progress, () => tool.run(this.plotService, params));
1999
956
  }
2000
957
  /** Pick the SAM model the segment tools use (jit-ui#90 P1). */
2001
958
  onSamModelChange(id) {
@@ -2010,192 +967,28 @@ export class VisualizerComponent {
2010
967
  toPolygonRegion() {
2011
968
  this.plotService.getRegionOverlay()?.setSelectedBezier(false);
2012
969
  }
2013
- /**
2014
- * Immediate region actions on the current selection (jit-ui#85): merge,
2015
- * ungroup, inverse, simplify, the Bézier conversions, and delete — the
2016
- * one-shot geometry transforms, grouped apart from the tool/mode toggles and
2017
- * shown only when something is selected. Each item is gated on what the
2018
- * selection supports.
2019
- */
2020
- buildRegionActionItems() {
2021
- const all = this.plotService.getRegions();
2022
- const selectable = all.filter((r) => r.kind !== 'profile');
2023
- if (selectable.length === 0)
2024
- return [];
2025
- const items = [];
2026
- // Select all — available whenever the image has regions, regardless of the
2027
- // current selection.
2028
- items.push({
2029
- label: 'Select all regions', icon: 'pi pi-check-square',
2030
- command: () => this.selectAllRegions(),
2031
- });
2032
- const selAll = this.selectedRegions;
2033
- if (selAll.length === 0)
2034
- return items;
2035
- const eligible = this.opEligible(selAll);
2036
- if (eligible.length >= 2) {
2037
- items.push({ label: 'Merge / group', icon: 'pi pi-link', command: () => this.mergeRegions() });
2038
- }
2039
- if (selAll.some((r) => this.regionOps.canUngroup(r))) {
2040
- items.push({ label: 'Ungroup', icon: 'pi pi-sitemap', command: () => this.ungroupRegions() });
2041
- }
2042
- if (eligible.length >= 1) {
2043
- items.push({ label: 'Inverse', icon: 'pi pi-clone', command: () => this.inverseRegions() }, {
2044
- label: 'Simplify', icon: 'pi pi-chart-line',
2045
- items: [
2046
- { label: 'Light (1 px)', command: () => this.simplifyRegions(1) },
2047
- { label: 'Medium (3 px)', command: () => this.simplifyRegions(3) },
2048
- { label: 'Strong (8 px)', command: () => this.simplifyRegions(8) },
2049
- { separator: true },
2050
- { label: 'Custom…', command: () => this.openSimplifyDialog() },
2051
- ],
2052
- });
2053
- }
2054
- // Bézier conversions are vertex-level edits — Image (OpenSeadragon) view
2055
- // only — gated on the selection's current form.
2056
- if (this.basePlotType === PlotType.IMAGE) {
2057
- const hasStraight = selAll.some((r) => (r.bounds instanceof Polygon && !r.bounds.bezier) || r.bounds instanceof Rectangle);
2058
- const hasBezier = selAll.some((r) => r.bounds instanceof Polygon && r.bounds.bezier);
2059
- if (hasStraight) {
2060
- items.push({ label: 'Convert to Bézier', icon: 'to-bezier-icon', command: () => this.toBezierRegion() });
2061
- }
2062
- if (hasBezier) {
2063
- items.push({ label: 'Convert to polygon', icon: 'to-polygon-icon', command: () => this.toPolygonRegion() });
2064
- }
2065
- }
2066
- items.push({ label: 'Delete region', icon: 'pi pi-trash', command: () => this.deleteRegion() });
2067
- return items;
2068
- }
970
+ /** The right-click menu for the current view, armed mode and selection. */
2069
971
  buildContextMenuItems() {
2070
- const active = this.activeDragMode;
2071
- const activeClass = 'context-menu-active';
2072
- const items = [];
2073
- // Selected-region actions lead the menu (separated from the tool toggles).
2074
- if (this.isHeatmap) {
2075
- const actions = this.buildRegionActionItems();
2076
- if (actions.length)
2077
- items.push(...actions, { separator: true });
2078
- }
2079
- if (this.isHeatmap) {
2080
- items.push({ label: 'Autoscale', icon: 'pi pi-window-maximize', command: () => this.autoscaleImage() }, { separator: true });
2081
- // 'Zoom selection' is Plotly's rubber-band zoom; it doesn't apply to the
2082
- // OpenSeadragon-backed Image view (use 'Zoom to box' there instead).
2083
- if (!this.isImageView) {
2084
- items.push({
2085
- label: 'Zoom selection',
2086
- icon: 'pi pi-search',
2087
- styleClass: active === 'zoom' ? activeClass : '',
2088
- command: () => this.toggleDragMode('zoom'),
2089
- });
2090
- }
2091
- items.push({
2092
- label: 'Zoom to box',
2093
- icon: 'zoom-box-off-icon',
2094
- styleClass: active === 'zoomToBox' ? activeClass : '',
2095
- command: () => this.toggleDragMode('zoomToBox'),
2096
- });
2097
- }
2098
- if (this.isHeatmap) {
2099
- items.push({
2100
- label: 'Pan',
2101
- icon: 'pi pi-arrows-alt',
2102
- styleClass: active === 'pan' ? activeClass : '',
2103
- command: () => this.toggleDragMode('pan'),
2104
- });
2105
- }
2106
- else {
2107
- const s3d = this.activeSurface3dMode;
2108
- items.push({
2109
- label: 'Zoom',
2110
- icon: 'pi pi-search',
2111
- styleClass: s3d === 'zoom' ? activeClass : '',
2112
- command: () => this.toggleSurface3dMode('zoom'),
2113
- }, {
2114
- label: 'Pan',
2115
- icon: 'pi pi-arrows-alt',
2116
- styleClass: s3d === 'pan' ? activeClass : '',
2117
- command: () => this.toggleSurface3dMode('pan'),
2118
- }, {
2119
- label: 'Orbital rotation',
2120
- icon: 'pi pi-globe',
2121
- styleClass: s3d === 'orbit' ? activeClass : '',
2122
- command: () => this.toggleSurface3dMode('orbit'),
2123
- }, {
2124
- label: 'Turntable rotation',
2125
- icon: 'pi pi-sync',
2126
- styleClass: s3d === 'turntable' ? activeClass : '',
2127
- command: () => this.toggleSurface3dMode('turntable'),
2128
- }, { separator: true }, { label: 'Reset camera', icon: 'pi pi-home', command: () => this.resetSurfaceCamera() });
2129
- }
2130
- if (this.isHeatmap) {
2131
- items.push({ label: 'Zoom in', icon: 'pi pi-search-plus', command: () => this.zoomIn() }, { label: 'Zoom out', icon: 'pi pi-search-minus', command: () => this.zoomOut() }, { separator: true }, {
2132
- label: 'Select',
2133
- icon: 'pi pi-arrow-up-right',
2134
- styleClass: active === 'select' ? activeClass : '',
2135
- command: () => this.toggleDragMode('select'),
2136
- }, {
2137
- label: 'Freeform',
2138
- icon: 'pi pi-pencil',
2139
- styleClass: active === 'drawclosedpath' ? activeClass : '',
2140
- command: () => this.toggleDragMode('drawclosedpath'),
2141
- }, {
2142
- label: 'Brush',
2143
- icon: 'brush-icon',
2144
- styleClass: active === 'brush' ? activeClass : '',
2145
- command: () => this.toggleDragMode('brush'),
2146
- }, {
2147
- label: 'Polyline',
2148
- icon: 'polyline-icon',
2149
- styleClass: active === 'drawopenpath' ? activeClass : '',
2150
- command: () => this.toggleDragMode('drawopenpath'),
2151
- }, {
2152
- label: 'Rectangle',
2153
- icon: 'pi pi-stop',
2154
- styleClass: active === 'drawrect' ? activeClass : '',
2155
- command: () => this.toggleDragMode('drawrect'),
2156
- }, {
2157
- label: 'Wand',
2158
- icon: 'wand-icon',
2159
- styleClass: active === 'wand' ? activeClass : '',
2160
- command: () => this.toggleDragMode('wand'),
2161
- }, {
2162
- label: 'Vertex eraser',
2163
- icon: 'pi pi-eraser',
2164
- styleClass: active === 'eraseVertex' ? activeClass : '',
2165
- command: () => this.toggleDragMode('eraseVertex'),
2166
- });
2167
- // Vertex editing runs on the OpenSeadragon overlay, which
2168
- // backs the Image plot type. Hidden for other 2D types (Plotly), where
2169
- // these modes are no-ops.
2170
- if (this.basePlotType === PlotType.IMAGE) {
2171
- items.push({
2172
- label: 'Polygon (click vertices)',
2173
- icon: 'polygon-vertices-icon',
2174
- styleClass: active === 'drawpolygon' ? activeClass : '',
2175
- command: () => this.toggleDragMode('drawpolygon'),
2176
- }, {
2177
- label: 'Add vertex',
2178
- icon: 'vertex-add-icon',
2179
- styleClass: active === 'addpoint' ? activeClass : '',
2180
- command: () => this.toggleDragMode('addpoint'),
2181
- }, {
2182
- label: 'Delete vertex',
2183
- icon: 'vertex-delete-icon',
2184
- styleClass: active === 'deletepoint' ? activeClass : '',
2185
- command: () => this.toggleDragMode('deletepoint'),
2186
- });
2187
- }
2188
- // Bézier conversions + Delete are immediate selection *actions*, now in the
2189
- // "Selected region(s)" group at the top (see buildRegionActionItems).
2190
- }
2191
- return items;
972
+ return buildContextMenu(this.contextMenuState(), this);
973
+ }
974
+ contextMenuState() {
975
+ return {
976
+ isHeatmap: this.isHeatmap,
977
+ basePlotType: this.basePlotType,
978
+ activeDragMode: this.activeDragMode,
979
+ activeSurface3dMode: this.activeSurface3dMode,
980
+ regions: this.plotService.getRegions(),
981
+ selectedIndices: this.regionActions.selectedIndices,
982
+ canUngroup: (r) => this.regionOps.canUngroup(r),
983
+ };
2192
984
  }
985
+ /** 3D scene interaction mode (orbit / turntable / pan / zoom); no-op on a 2D-only backend. */
2193
986
  toggleSurface3dMode(mode) {
2194
987
  this.activeSurface3dMode = mode;
2195
- this.plotService.setSurfaceDragMode(mode);
988
+ this.plotService.getSurface3dControls()?.setSurfaceDragMode(mode);
2196
989
  }
2197
990
  resetSurfaceCamera() {
2198
- this.plotService.resetSurfaceCamera();
991
+ this.plotService.getSurface3dControls()?.resetSurfaceCamera();
2199
992
  }
2200
993
  /** Toggle the napari 3D coordinate-axes / scale gizmo (volume/isosurface). No-op on backends
2201
994
  * that don't render one (the control method is optional). */
@@ -2224,6 +1017,9 @@ export class VisualizerComponent {
2224
1017
  * moving to a 3D type, then re-plots.
2225
1018
  */
2226
1019
  onSelectPlotType(selected) {
1020
+ // Also called by the host directly, outside this view's events: the selector, the
1021
+ // toolbar's tool groups and the dialogs all follow the type.
1022
+ this.changed();
2227
1023
  // A contributed id whose provider is gone (e.g. restored from a previous
2228
1024
  // session) has nothing to activate — take the default Image view instead.
2229
1025
  if (!isBuiltinPlotType(selected) && !this.plotModes.find(selected)) {
@@ -2243,7 +1039,7 @@ export class VisualizerComponent {
2243
1039
  // A dialog tool draws over the Image view: leaving it closes the dialog. Staying
2244
1040
  // on it, the session ends here and restarts once the re-plot lands.
2245
1041
  if (type === PlotType.IMAGE)
2246
- this.toolDialogs.deactivate();
1042
+ this.contributions.toolDialogs.deactivate();
2247
1043
  else
2248
1044
  this.closeDialogTool();
2249
1045
  this.plotType = type;
@@ -2257,7 +1053,7 @@ export class VisualizerComponent {
2257
1053
  // Switching plot type cancels the active tool — not every type supports the
2258
1054
  // same tools (line/scatter charts and 3D scenes have no box zoom or region
2259
1055
  // drawing), so leaving a tool armed would misbehave on the new plot.
2260
- this.deactivateActiveTool();
1056
+ this.tools.deactivate();
2261
1057
  if (is3d) {
2262
1058
  this.activeSurface3dMode = 'turntable';
2263
1059
  }
@@ -2274,275 +1070,55 @@ export class VisualizerComponent {
2274
1070
  // Plotly's stack-loading flag (keeps its frame-fetch loop alive) and
2275
1071
  // re-emit the image info with showStack on so the pipeline reloads on
2276
1072
  // Plotly regardless of which backend was on screen.
2277
- this.selectedStackOption = this.stackOptions[1];
2278
- this.stackLoading = true;
1073
+ this.stackLoading.set(true);
2279
1074
  this.plotService.setStackLoading(true);
2280
1075
  if (this.imageInfo) {
2281
- this.imageInfo.showStack = true;
2282
- this.state.setImageInfo(this.imageInfo);
1076
+ // A copy: the image info may be the host's own (possibly frozen) object.
1077
+ this.state.setImageInfo({ ...this.imageInfo, showStack: true });
2283
1078
  }
2284
1079
  return;
2285
1080
  }
2286
1081
  this.reloadAndPlot();
2287
1082
  }
2288
- // ── contributed plot modes ─────────────────────────────────────────────
2289
- /**
2290
- * Start the selected contributed mode's session, once its base view has
2291
- * plotted. No-op for a built-in type, or when the base on screen is not the
2292
- * one the mode rides on. A backend that cannot provide a viewport for it
2293
- * (e.g. OSD fell back to Plotly) counts as a failed activation.
2294
- */
2295
- activateSelectedPlotMode() {
2296
- const contribution = this.plotModes.find(this.selectedPlotTypeId);
2297
- if (!contribution || this.plotType !== contribution.descriptor.baseType)
2298
- return;
2299
- const viewport = this.plotService.getPlotModeViewport?.() ?? null;
2300
- if (!viewport) {
2301
- console.error(`[visualizer] plot-type contribution '${contribution.descriptor.type}': the backend `
2302
- + 'on screen provides no viewport to draw over — falling back.');
2303
- this.fallBackFromPlotMode(contribution);
2304
- return;
2305
- }
2306
- const ctx = {
2307
- visualizer: this.plotService,
2308
- viewport,
2309
- imageInfo$: this.state.getImageInfo$(),
2310
- tools: this.plotModeTools,
2311
- };
2312
- void this.plotModes.activate(contribution, ctx);
2313
- }
2314
- /** Render the live mode's panel. Activation can resolve outside Angular (an
2315
- * OSD callback, a contribution's own promise), hence the zone re-entry. */
2316
- showPlotModePanel(active) {
2317
- const panel = active.contribution.panel;
2318
- if (!panel)
2319
- return;
2320
- this.ngZone.run(() => {
2321
- if ('component' in panel && panel.component) {
2322
- this.plotModePanel = {
2323
- title: panel.title,
2324
- component: panel.component,
2325
- injector: Injector.create({
2326
- providers: [
2327
- { provide: PLOT_MODE_CONTEXT, useValue: active.ctx },
2328
- { provide: PLOT_MODE_SESSION, useValue: active.session },
2329
- ],
2330
- parent: this.injector,
2331
- }),
2332
- host: null,
2333
- };
2334
- }
2335
- else if (active.panelHost) {
2336
- this.plotModePanel = { title: panel.title, component: null, injector: null, host: active.panelHost };
2337
- }
2338
- else {
2339
- return;
2340
- }
2341
- // A component panel is created and first rendered right here; if that throws,
2342
- // the mode is not usable — end it and fall back rather than leave it selected.
2343
- const err = this.detectChangesSafely();
2344
- if (err !== null)
2345
- this.plotModes.failActive(err);
2346
- });
2347
- }
2348
- /** Drop the panel (destroying a component panel) before the session ends. */
2349
- hidePlotModePanel() {
2350
- if (!this.plotModePanel)
2351
- return;
2352
- this.plotModePanel = null;
2353
- this.detectChangesSafely();
2354
- }
2355
- /**
2356
- * A contributed mode could not start. Its base type is already on screen —
2357
- * the base plotted first — so just select it: nothing needs to re-plot.
2358
- */
2359
- fallBackFromPlotMode(contribution) {
2360
- if (this.destroying || this.selectedPlotTypeId !== contribution.descriptor.type)
2361
- return;
2362
- this.ngZone.run(() => {
2363
- const base = contribution.descriptor.baseType;
2364
- this.selectedPlotTypeId = base;
2365
- this.plotType = base;
2366
- this.messageService.add({
2367
- key: this.resultToastKey,
2368
- severity: 'warn',
2369
- summary: `${contribution.descriptor.productionLabel ?? contribution.descriptor.label} is unavailable`,
2370
- detail: 'The plot mode could not start, so the plain image is shown instead. See the browser console for details.',
2371
- });
2372
- this.detectChangesSafely();
2373
- });
2374
- }
2375
- /** Run change detection; returns what it threw (logged), or null. */
2376
- detectChangesSafely() {
2377
- if (this.destroying)
2378
- return null;
2379
- try {
2380
- this.cdr.detectChanges();
2381
- return null;
2382
- }
2383
- catch (err) {
2384
- console.error('[visualizer] change detection failed while updating a contributed plot mode panel.', err);
2385
- return err ?? new Error('change detection failed');
2386
- }
2387
- }
2388
1083
  // ── dialog tools (TOOLBAR_TOOLS, kind: 'dialog') ─────────────────────────
2389
1084
  /** The toolbar button: open the tool's dialog, or close it if it is open. */
2390
1085
  toggleDialogTool(id) {
2391
- if (this.openDialogToolId === id) {
2392
- this.closeDialogTool();
2393
- return;
2394
- }
2395
- this.closeDialogTool();
2396
- if (!this.dialogTools.some((t) => t.id === id))
2397
- return;
2398
- this.openDialogToolId = id;
2399
- this.toolDialogs.clearCleanupFailures(); // an explicit open gets a fresh attempt
2400
- this.activateOpenDialogTool();
1086
+ this.contributions.toggleDialogTool(id);
2401
1087
  }
2402
1088
  /** Close the open dialog tool: its body is torn down, then its session ends. */
2403
1089
  closeDialogTool() {
2404
- this.openDialogToolId = null;
2405
- this.toolDialogs.deactivate();
2406
- }
2407
- /** Start (or restart, after a re-render) the open dialog tool's session. */
2408
- activateOpenDialogTool() {
2409
- const id = this.openDialogToolId;
2410
- const contribution = id ? this.toolDialogs.find(id) : undefined;
2411
- if (!contribution || this.plotType !== PlotType.IMAGE)
2412
- return;
2413
- if (this.toolDialogs.current?.contribution === contribution || this.toolDialogs.pending)
2414
- return;
2415
- const viewport = this.plotService.getPlotModeViewport?.() ?? null;
2416
- if (!viewport) {
2417
- console.error(`[visualizer] dialog tool '${id}': the backend on screen provides no viewport to draw over.`);
2418
- this.dialogToolFailed(id);
2419
- return;
2420
- }
2421
- const ctx = {
2422
- visualizer: this.plotService,
2423
- viewport,
2424
- imageInfo$: this.state.getImageInfo$(),
2425
- tools: this.plotModeTools,
2426
- };
2427
- void this.toolDialogs.activate(contribution, ctx);
2428
- }
2429
- /**
2430
- * The session is live: render the dialog, then mount the tool's body once the host
2431
- * element is in the document, so `mount()` can measure it and start widgets that need
2432
- * a connected element.
2433
- */
2434
- showToolDialog(active) {
2435
- const tool = this.dialogTools.find((t) => t.id === active.contribution.descriptor.type);
2436
- if (!tool)
2437
- return;
2438
- const host = document.createElement('div');
2439
- host.className = 'tool-dialog-body';
2440
- this.ngZone.run(() => {
2441
- this.toolDialog = {
2442
- title: tool.dialog?.title ?? tool.label,
2443
- width: tool.dialog?.width ?? '22rem',
2444
- host,
2445
- };
2446
- const err = this.detectChangesSafely();
2447
- if (err !== null) {
2448
- this.toolDialogs.failActive(err);
2449
- return;
2450
- }
2451
- this.mountToolDialogBody(tool, active, host, 0);
2452
- });
2453
- }
2454
- /** How many animation frames to wait for the dialog to attach the body's host. */
2455
- static { this.TOOL_DIALOG_ATTACH_FRAMES = 60; }
2456
- /**
2457
- * Mount `tool`'s body into `host` once it is connected (the dialog may attach it a
2458
- * frame later, when it moves itself to `<body>`). If it never connects within
2459
- * {@link TOOL_DIALOG_ATTACH_FRAMES} it is mounted anyway, with a warning, rather than
2460
- * leaving an empty dialog. A throwing `mount()` fails the session like a failed start.
2461
- */
2462
- mountToolDialogBody(tool, active, host, frames) {
2463
- this.toolDialogFrame = null;
2464
- // The session ended (or was replaced) while waiting: nothing to mount into.
2465
- if (this.toolDialogs.current !== active || this.toolDialog?.host !== host)
2466
- return;
2467
- if (!host.isConnected) {
2468
- if (frames < VisualizerComponent.TOOL_DIALOG_ATTACH_FRAMES) {
2469
- this.toolDialogFrame = requestAnimationFrame(() => this.mountToolDialogBody(tool, active, host, frames + 1));
2470
- return;
2471
- }
2472
- console.warn(`[visualizer] dialog tool '${tool.id}': the dialog body never attached; mounting it detached.`);
2473
- }
2474
- try {
2475
- const teardown = tool.mount(host, active.ctx, active.session);
2476
- this.toolDialogTeardown = typeof teardown === 'function' ? teardown : null;
2477
- }
2478
- catch (err) {
2479
- console.error(`[visualizer] dialog tool '${tool.id}' mount() threw.`, err);
2480
- this.toolDialogs.failActive(err);
2481
- }
2482
- }
2483
- /** The session is about to end: tear the body down first, then drop the dialog. */
2484
- hideToolDialog() {
2485
- if (this.toolDialogFrame !== null)
2486
- cancelAnimationFrame(this.toolDialogFrame);
2487
- this.toolDialogFrame = null;
2488
- const teardown = this.toolDialogTeardown;
2489
- this.toolDialogTeardown = null;
2490
- if (teardown) {
2491
- try {
2492
- teardown();
2493
- }
2494
- catch (err) {
2495
- console.error('[visualizer] dialog tool body teardown threw.', err);
2496
- }
2497
- }
2498
- if (!this.toolDialog)
2499
- return;
2500
- this.toolDialog.host.remove();
2501
- this.toolDialog = null;
2502
- if (!this.destroying)
2503
- this.detectChangesSafely();
2504
- }
2505
- /** A dialog tool could not start: close it and say so. */
2506
- dialogToolFailed(id) {
2507
- if (this.destroying || this.openDialogToolId !== id)
2508
- return;
2509
- this.ngZone.run(() => {
2510
- this.openDialogToolId = null;
2511
- const tool = this.dialogTools.find((t) => t.id === id);
2512
- this.messageService.add({
2513
- key: this.resultToastKey,
2514
- severity: 'warn',
2515
- summary: `${tool?.label ?? id} is unavailable`,
2516
- detail: 'The tool could not start. See the browser console for details.',
2517
- });
2518
- this.detectChangesSafely();
2519
- });
1090
+ this.contributions.closeDialogTool();
2520
1091
  }
2521
1092
  /** Where the open dialog tool's host element is attached once its dialog renders. */
2522
1093
  set toolDialogSlot(ref) {
2523
- const host = this.toolDialog?.host;
2524
- if (ref && host && host.parentElement !== ref.nativeElement)
2525
- ref.nativeElement.appendChild(host);
2526
- }
2527
- /** Deactivate whatever tool is armed and clear every tool mode. */
2528
- deactivateActiveTool() {
2529
- this.brushClass = null;
2530
- this.activeDragMode = null;
2531
- this.session.setActiveTool(null);
2532
- this.plotService.getRegionOverlay()?.setMode('none');
2533
- this.plotService.setDragMode(false);
2534
- this.plotService.setZoomToBoxMode(false);
2535
- this.plotService.setWandMode(false);
2536
- this.plotService.setBrushMode(false);
2537
- this.plotService.setSamPointMode(false);
2538
- this.plotService.setVertexEraserMode(false);
2539
- }
2540
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: VisualizerComponent, deps: [{ token: IMAGE_STATE_PORT }, { token: VISUALIZER }, { token: i1.MessageService }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i2.VisualizerStore }, { token: i3.SamToolService }, { token: i4.CellSegmentToolService }, { token: i5.SamPointToolService }, { token: i6.RegionOpsService }, { token: VIZ_CONFIG, optional: true }, { token: TOOLBAR_TOOLS, optional: true }, { token: SPATIAL_DATA_PORT, optional: true }, { token: PLOT_TYPE_CONTRIBUTIONS, optional: true }, { token: i0.Injector, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
2541
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: VisualizerComponent, selector: "visualizer", inputs: { toolbarTools: "toolbarTools", testMode: "testMode" }, outputs: { isStackEvent: "isStackEvent", isGrayscaleEvent: "isGrayscaleEvent" }, host: { listeners: { "window:keydown": "onKeydown($event)" } }, viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["cm"], descendants: true }, { propertyName: "plotModePanelSlot", first: true, predicate: ["plotModePanelSlot"], descendants: true }, { propertyName: "toolDialogSlot", first: true, predicate: ["toolDialogSlot"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ samStatus || message.detail }}</div>\n <p-progressBar *ngIf=\"samDownloading\" [value]=\"samProgress\" styleClass=\"mt-2 sam-progress\"></p-progressBar>\n <p-progressBar *ngIf=\"samBusy && !samDownloading\" mode=\"indeterminate\" styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Result/error notices from the library itself (segmentation outcome, region\n validation). Rendered here so they do not depend on the host mounting a\n keyless <p-toast> \u2014 without this the messages went nowhere in any host but\n jit-ui, and a failed run looked like a button that did nothing. -->\n<p-toast [key]=\"resultToastKey\" position=\"bottom-right\"></p-toast>\n\n<!-- Shared outlets for notices raised where there is no per-instance key to use:\n the root-provided PlotlyService and the region-editor dialog. Rendered by a\n single visualizer (ownsSharedToasts) so a message from the root service is\n not shown once per live viewer. -->\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizToastKey\"></p-toast>\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizAlertToastKey\" position=\"center\"></p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div class=\"toolbar-dock\" [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\">\n <div class=\"toolbar-drag-handle\" (mousedown)=\"onToolbarDragStart($event)\"\n pTooltip=\"Drag to float the toolbar\" tooltipPosition=\"bottom\">\n <i class=\"pi pi-bars\"></i>\n </div>\n<plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [hasSpatialDataset]=\"hasSpatialDataset\"\n [plotTypeOptions]=\"plotTypeMenu\"\n [selectedPlotType]=\"selectedPlotTypeId\"\n [basePlotType]=\"basePlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [is3dRegions]=\"isSpatial3dMode\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n [openDialogToolId]=\"openDialogToolId\"\n (toggleDialogTool)=\"toggleDialogTool($event)\"\n [toolModelIds]=\"toolModelIds\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"canUndoRegion\"\n [canRedo]=\"canRedoRegion\"\n [stackOptions]=\"stackOptions\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openSpatialControls)=\"openSpatialControls()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (runTool)=\"runTool($event)\"\n (toolModelChange)=\"onToolModelChange($event)\"\n (openToolParams)=\"openToolParams($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\">\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n</plotting-toolbar>\n <p-button *ngIf=\"toolbarFloating\" class=\"toolbar-dock-btn\" icon=\"pi pi-thumbtack\" size=\"small\" [text]=\"true\"\n pTooltip=\"Dock to top\" tooltipPosition=\"bottom\" (onClick)=\"dockToolbar()\">\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading || stackLoading || isCaching\" id=\"overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading; else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching; else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress\"></p-progressBar>\n <span class=\"text-loading\">{{loadingMessage}}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading && !zoom\" class=\"text-loading\">{{loadingMessage}}</span>\n <span *ngIf=\"imgLoading && zoom\" class=\"zoom-text-loading\">{{loadingMessage}}</span>\n </ng-template>\n </ng-template>\n <p-button *ngIf=\"stackLoading || imgLoading\" class=\"cancel-loading \" label=\"Cancel\"\n (onClick)=\"cancelLoading()\">\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"sharpening && !imgLoading && !stackLoading && !isCaching\" id=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n\n</div>\n\n<!-- Floating, draggable intensity-profile inset. Shown in LINE mode; the chart\n updates live as the line ROI on the image is moved. Kept OUTSIDE #plot and\n positioned fixed so it floats over the whole page (not clipped to the\n diagram/canvas area). -->\n<div *ngIf=\"hasProfiles\" class=\"intensity-panel\"\n [style.left.px]=\"profilePanelPos.x\" [style.top.px]=\"profilePanelPos.y\">\n <div class=\"intensity-panel-header\" (mousedown)=\"onProfilePanelDragStart($event)\">\n <span>Intensity profile</span>\n <i class=\"pi pi-arrows-alt\"></i>\n </div>\n <div [id]=\"intensityInsetDiv\" class=\"intensity-panel-plot\"></div>\n</div>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Spatial-omics controls. Renders its own empty state when no dataset (or no\n SPATIAL_DATA_PORT) is present, so it is always safe to host. -->\n<spatial-controls [(visible)]=\"showSpatialControls\" [is3d]=\"isSpatial3dMode\" ></spatial-controls>\n\n<!-- Side panel of the active contributed plot mode (PLOT_TYPE_CONTRIBUTIONS),\n in the same right-hand area as the spatial controls and the Region Editor.\n Exists only while the mode's session is live; there is no close button,\n because leaving the mode is what ends it. A component panel gets\n PLOT_MODE_CONTEXT / PLOT_MODE_SESSION from its injector; a `mount` panel's\n host element is attached to the slot below (see plotModePanelSlot). -->\n<p-dialog *ngIf=\"plotModePanel as panel\" [header]=\"panel.title\" [visible]=\"true\"\n [modal]=\"false\" [closable]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n appendTo=\"body\" position=\"right\" styleClass=\"plot-mode-panel\"\n [style]=\"{ width: '22rem', maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <ng-container *ngIf=\"panel.component\">\n <ng-container *ngComponentOutlet=\"panel.component; injector: panel.injector ?? undefined\"></ng-container>\n </ng-container>\n <div *ngIf=\"panel.host\" #plotModePanelSlot class=\"plot-mode-panel-slot\"></div>\n</p-dialog>\n\n<!-- An open dialog tool (TOOLBAR_TOOLS, kind: 'dialog'). Non-modal so the user can\n keep working on the image, e.g. painting with the brush the tool armed. The\n tool renders its body into a host element attached to the slot below (see\n toolDialogSlot); closing the dialog ends the tool's session. -->\n<p-dialog *ngIf=\"toolDialog as dlg\" [header]=\"dlg.title\" [visible]=\"true\"\n (visibleChange)=\"$event || closeDialogTool()\"\n [modal]=\"false\" [closable]=\"true\" [resizable]=\"true\" [draggable]=\"true\"\n appendTo=\"body\" position=\"right\" styleClass=\"tool-dialog\"\n [style]=\"{ width: dlg.width, maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <div #toolDialogSlot class=\"tool-dialog-slot\"></div>\n</p-dialog>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog header=\"Region Editor\" [(visible)]=\"showRegionEditor\"\n [modal]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n [maximizable]=\"true\" appendTo=\"body\" position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog header=\"Simplify region(s)\" [(visible)]=\"displaySimplifyDialog\" [modal]=\"true\"\n [style]=\"{width: '22rem'}\" [draggable]=\"false\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-2\">\n <span>Altitude threshold: <b>{{ simplifyThreshold }} px</b></span>\n <p-slider [(ngModel)]=\"simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"simplifyRegions(simplifyThreshold)\"></p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Parameters for whichever contributed tool is open. One dialog for all of\n them: a tool declares its fields (ToolParamSpec) and this renders them, so\n the toolbar can lay every tool out the same way and a plugin needs no\n PrimeNG of its own. Defaults come from the active checkpoint, since they\n encode what it was trained for. -->\n<p-dialog *ngIf=\"openParamsTool as tool\"\n [header]=\"tool.label + ' parameters'\"\n [visible]=\"true\" (visibleChange)=\"openParamsToolId = null\"\n [modal]=\"true\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\" appendTo=\"body\">\n <div class=\"grid formgrid p-fluid\">\n <ng-container *ngFor=\"let p of tool.params\">\n <div class=\"field\" [ngClass]=\"p.type === 'boolean' ? 'col-12' : 'col-6'\">\n <ng-container [ngSwitch]=\"p.type\">\n\n <ng-container *ngSwitchCase=\"'number'\">\n <label class=\"text-sm\" [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n <p-inputNumber [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n [min]=\"numberParam(p).min\" [max]=\"numberParam(p).max\"\n [step]=\"numberParam(p).step\"\n [minFractionDigits]=\"numberParam(p).fractionDigits\"\n [showButtons]=\"true\"></p-inputNumber>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'select'\">\n <label class=\"text-sm\" [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n <p-dropdown [options]=\"selectParam(p).options\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n appendTo=\"body\"></p-dropdown>\n </ng-container>\n\n <!-- Checkbox label sits beside the box and may run to a sentence, so\n it does not get the stacked label the other types use. -->\n <div *ngSwitchCase=\"'boolean'\" class=\"flex align-items-center gap-2\">\n <p-checkbox [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n [binary]=\"true\" [inputId]=\"tool.id + '-' + p.id\"></p-checkbox>\n <label [for]=\"tool.id + '-' + p.id\" class=\"text-sm mb-0\"\n [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n </div>\n\n </ng-container>\n </div>\n </ng-container>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Reset to model defaults\" [text]=\"true\" size=\"small\"\n (onClick)=\"resetToolParams(tool.id)\"></p-button>\n <p-button label=\"Close\" size=\"small\" (onClick)=\"openParamsToolId = null\"></p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}#overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}#sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}#sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}#sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.intensity-panel{position:fixed;z-index:1100;width:280px;height:180px;background:#191919e6;border:1px solid var(--primary-color, #3399ff);border-radius:6px;box-shadow:0 4px 14px #00000073;display:flex;flex-direction:column;overflow:hidden}.intensity-panel-header{display:flex;align-items:center;justify-content:space-between;padding:.25rem .5rem;cursor:move;-webkit-user-select:none;user-select:none;background:#ffffff14;color:#eee;font-size:.75rem;font-weight:600}.intensity-panel-header i{opacity:.6;font-size:.7rem}.intensity-panel-plot{flex:1 1 auto;width:100%}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"], dependencies: [{ kind: "directive", type: i7.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i7.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: i7.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i7.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i7.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i9.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i10.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: i11.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i12.Slider, selector: "p-slider", inputs: ["animate", "disabled", "min", "max", "orientation", "step", "range", "style", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "component", type: i13.InputNumber, selector: "p-inputNumber", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "component", type: i14.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "visible", "style", "position"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "component", type: i15.ContextMenu, selector: "p-contextMenu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "appendTo", "autoZIndex", "baseZIndex", "id", "ariaLabel", "ariaLabelledBy", "pressDelay"], outputs: ["onShow", "onHide"] }, { kind: "component", type: i16.ProgressBar, selector: "p-progressBar", inputs: ["value", "showValue", "styleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: i17.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "style", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "component", type: i18.Checkbox, selector: "p-checkbox", inputs: ["value", "name", "disabled", "binary", "label", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "styleClass", "labelStyleClass", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i19.ToolbarComponent, selector: "plotting-toolbar", inputs: ["imageInfo", "hasSpatialDataset", "plotTypeOptions", "selectedPlotType", "basePlotType", "isoRange", "maxIndex", "zIndex", "isHeatmap", "is3dRegions", "activeDragMode", "activeSurface3dMode", "axesVisible", "wireframeActive", "resolutionScale", "wandSensitivity", "brushSize", "vertexEraserRadius", "canUndo", "canRedo", "stackOptions", "tools", "imageSmoothingEnabled", "samModels", "samModelId", "contributedTools", "dialogTools", "openDialogToolId", "toolModelIds"], outputs: ["selectPlotType", "addProfileLine", "toggleImageSmoothing", "isoRangeChange", "openChannelHistogram", "openSpatialControls", "openRegionEditor", "selectStackOption", "zScrub", "zSlide", "zIndexInput", "reloadAndPlot", "downloadImage", "autoscaleImage", "toggleDragMode", "zoomIn", "zoomOut", "toggleSurface3dMode", "resetSurfaceCamera", "toggleAxes", "toggleWireframe", "selectResolution", "deleteRegion", "undoRegion", "redoRegion", "segmentRegions", "segmentCellpose", "runTool", "toggleDialogTool", "toolModelChange", "openToolParams", "samModelChange", "wandSensitivityChange", "brushSizeChange", "vertexEraserRadiusChange"] }, { kind: "component", type: i20.RegionEditorComponent, selector: "region-editor" }, { kind: "component", type: i21.ChannelHistogramComponent, selector: "channel-histogram", inputs: ["visible"], outputs: ["visibleChange"] }, { kind: "component", type: i22.SpatialControlsComponent, selector: "spatial-controls", inputs: ["visible", "is3d"], outputs: ["visibleChange"] }] }); }
1094
+ this.contributions.attachToolDialogSlot(ref?.nativeElement);
1095
+ }
1096
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: VisualizerComponent, deps: [{ token: IMAGE_STATE_PORT }, { token: VISUALIZER }, { token: i1.MessageService }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i2.VisualizerStore }, { token: i3.SamToolService }, { token: i4.CellSegmentToolService }, { token: i5.SamPointToolService }, { token: i6.RegionOpsService }, { token: VIZ_CONFIG, optional: true }, { token: TOOLBAR_TOOLS, optional: true }, { token: SPATIAL_DATA_PORT, optional: true }, { token: PLOT_TYPE_CONTRIBUTIONS, optional: true }, { token: i0.Injector, optional: true }, { token: i0.ElementRef, optional: true }, { token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component }); }
1097
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: VisualizerComponent, isStandalone: true, selector: "visualizer", inputs: { toolbarTools: "toolbarTools", testMode: "testMode" }, outputs: { isStackEvent: "isStackEvent", isGrayscaleEvent: "isGrayscaleEvent" }, viewQueries: [{ propertyName: "contextMenu", first: true, predicate: ["cm"], descendants: true }, { propertyName: "plotModePanelSlot", first: true, predicate: ["plotModePanelSlot"], descendants: true }, { propertyName: "inset", first: true, predicate: IntensityInsetComponent, descendants: true, static: true }, { propertyName: "toolDialogSlot", first: true, predicate: ["toolDialogSlot"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ segmentation.status || message.detail }}</div>\n <p-progressBar\n *ngIf=\"segmentation.downloading\"\n [value]=\"segmentation.progress\"\n styleClass=\"mt-2 sam-progress\"\n ></p-progressBar>\n <p-progressBar\n *ngIf=\"segmentation.busy && !segmentation.downloading\"\n mode=\"indeterminate\"\n styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"\n ></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Result/error notices from the library itself (segmentation outcome, region\n validation). Rendered here so they do not depend on the host mounting a\n keyless <p-toast> \u2014 without this the messages went nowhere in any host but\n jit-ui, and a failed run looked like a button that did nothing. -->\n<p-toast [key]=\"resultToastKey\" position=\"bottom-right\"></p-toast>\n\n<!-- Shared outlets for notices raised where there is no per-instance key to use:\n the root-provided PlotlyService and the region-editor dialog. Rendered by a\n single visualizer (ownsSharedToasts) so a message from the root service is\n not shown once per live viewer. -->\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizToastKey\"></p-toast>\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizAlertToastKey\" position=\"center\"></p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div\n class=\"toolbar-dock\"\n [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\"\n>\n <div\n class=\"toolbar-drag-handle\"\n [vizFloatingDrag]=\"toolbarDragOrigin\"\n (vizFloatingDragMove)=\"toolbarPos = $event\"\n pTooltip=\"Drag to float the toolbar\"\n tooltipPosition=\"bottom\"\n >\n <i class=\"pi pi-bars\"></i>\n </div>\n <plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [hasSpatialDataset]=\"hasSpatialDataset\"\n [plotTypeOptions]=\"plotTypeMenu\"\n [selectedPlotType]=\"selectedPlotTypeId\"\n [basePlotType]=\"basePlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [is3dRegions]=\"isSpatial3dMode\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n [openDialogToolId]=\"openDialogToolId\"\n (toggleDialogTool)=\"toggleDialogTool($event)\"\n [toolModelIds]=\"toolParams.toolModelIds\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"regionActions.canUndo\"\n [canRedo]=\"regionActions.canRedo\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openSpatialControls)=\"openSpatialControls()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (runTool)=\"runTool($event)\"\n (toolModelChange)=\"onToolModelChange($event)\"\n (openToolParams)=\"openToolParams($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\"\n >\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n </plotting-toolbar>\n <p-button\n *ngIf=\"toolbarFloating\"\n class=\"toolbar-dock-btn\"\n icon=\"pi pi-thumbtack\"\n size=\"small\"\n [text]=\"true\"\n pTooltip=\"Dock to top\"\n tooltipPosition=\"bottom\"\n (onClick)=\"dockToolbar()\"\n >\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading() || stackLoading() || isCaching()\" class=\"loading-overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading(); else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage()\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching(); else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress()\"></p-progressBar>\n <span class=\"text-loading\">{{ loadingMessage() }}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading()\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading() && !zoom()\" class=\"text-loading\">{{ loadingMessage() }}</span>\n <span *ngIf=\"imgLoading() && zoom()\" class=\"zoom-text-loading\">{{ loadingMessage() }}</span>\n </ng-template>\n </ng-template>\n <p-button\n *ngIf=\"stackLoading() || imgLoading()\"\n class=\"cancel-loading\"\n label=\"Cancel\"\n (onClick)=\"cancelLoading()\"\n >\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"render.sharpening && !imgLoading() && !stackLoading() && !isCaching()\" class=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n</div>\n\n<!-- Floating, draggable intensity-profile inset, shown while any intensity line\n exists. Kept OUTSIDE the plot div and fixed to the viewport, so it floats over\n the whole page rather than being clipped to the canvas. -->\n<viz-intensity-inset\n [divId]=\"intensityInsetDiv\"\n [plotDivName]=\"plotDivName\"\n [zIndex]=\"zIndex\"\n [imageView]=\"isImageView\"\n [imageInfo]=\"imageInfo\"\n></viz-intensity-inset>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Spatial-omics controls. Renders its own empty state when no dataset (or no\n SPATIAL_DATA_PORT) is present, so it is always safe to host. -->\n<spatial-controls [(visible)]=\"showSpatialControls\" [is3d]=\"isSpatial3dMode\"></spatial-controls>\n\n<!-- Side panel of the active contributed plot mode (PLOT_TYPE_CONTRIBUTIONS),\n in the same right-hand area as the spatial controls and the Region Editor.\n Exists only while the mode's session is live; there is no close button,\n because leaving the mode is what ends it. A component panel gets\n PLOT_MODE_CONTEXT / PLOT_MODE_SESSION from its injector; a `mount` panel's\n host element is attached to the slot below (see plotModePanelSlot). -->\n<p-dialog\n *ngIf=\"plotModePanel as panel\"\n [header]=\"panel.title\"\n [visible]=\"true\"\n [modal]=\"false\"\n [closable]=\"false\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n styleClass=\"plot-mode-panel\"\n [style]=\"{ width: '22rem', maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <ng-container *ngIf=\"panel.component\">\n <ng-container *ngComponentOutlet=\"panel.component; injector: panel.injector ?? undefined\"></ng-container>\n </ng-container>\n <div *ngIf=\"panel.host\" #plotModePanelSlot class=\"plot-mode-panel-slot\"></div>\n</p-dialog>\n\n<!-- An open dialog tool (TOOLBAR_TOOLS, kind: 'dialog'). Non-modal so the user can\n keep working on the image, e.g. painting with the brush the tool armed. The\n tool renders its body into a host element attached to the slot below (see\n toolDialogSlot); closing the dialog ends the tool's session. -->\n<p-dialog\n *ngIf=\"toolDialog as dlg\"\n [header]=\"dlg.title\"\n [visible]=\"true\"\n (visibleChange)=\"$event || closeDialogTool()\"\n [modal]=\"false\"\n [closable]=\"true\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n styleClass=\"tool-dialog\"\n [style]=\"{ width: dlg.width, maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <div #toolDialogSlot class=\"tool-dialog-slot\"></div>\n</p-dialog>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog\n header=\"Region Editor\"\n [(visible)]=\"showRegionEditor\"\n [modal]=\"false\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n [maximizable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog\n header=\"Simplify region(s)\"\n [(visible)]=\"displaySimplifyDialog\"\n [modal]=\"true\"\n [style]=\"{ width: '22rem' }\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n>\n <div class=\"flex flex-column gap-2\">\n <span\n >Altitude threshold: <b>{{ regionActions.simplifyThreshold }} px</b></span\n >\n <p-slider [(ngModel)]=\"regionActions.simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button\n label=\"Apply\"\n icon=\"pi pi-check\"\n (onClick)=\"simplifyRegions(regionActions.simplifyThreshold)\"\n ></p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Parameters for whichever contributed tool is open. One dialog for all of\n them: a tool declares its fields (ToolParamSpec) and this renders them, so\n the toolbar can lay every tool out the same way and a plugin needs no\n PrimeNG of its own. Defaults come from the active checkpoint, since they\n encode what it was trained for. -->\n<p-dialog\n *ngIf=\"toolParams.openParams as open\"\n [header]=\"open.tool.label + ' parameters'\"\n [visible]=\"true\"\n (visibleChange)=\"closeToolParams()\"\n [modal]=\"true\"\n [style]=\"{ width: '30rem' }\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n appendTo=\"body\"\n>\n <div class=\"grid formgrid p-fluid\">\n <ng-container *ngFor=\"let f of open.fields\">\n <div class=\"field\" [ngClass]=\"f.spec.type === 'boolean' ? 'col-12' : 'col-6'\">\n <ng-container *ngIf=\"f.number as n\">\n <label class=\"text-sm\" [pTooltip]=\"n.tooltip || ''\">{{ n.label }}</label>\n <p-inputNumber\n [ngModel]=\"open.values[n.id]\"\n (ngModelChange)=\"open.values[n.id] = $event\"\n [min]=\"n.min\"\n [max]=\"n.max\"\n [step]=\"n.step\"\n [minFractionDigits]=\"n.fractionDigits\"\n [showButtons]=\"true\"\n ></p-inputNumber>\n </ng-container>\n\n <ng-container *ngIf=\"f.select as sel\">\n <label class=\"text-sm\" [pTooltip]=\"sel.tooltip || ''\">{{ sel.label }}</label>\n <p-dropdown\n [options]=\"sel.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"open.values[sel.id]\"\n (ngModelChange)=\"open.values[sel.id] = $event\"\n appendTo=\"body\"\n ></p-dropdown>\n </ng-container>\n\n <!-- Checkbox label sits beside the box and may run to a sentence, so\n it does not get the stacked label the other types use. -->\n <div *ngIf=\"f.spec.type === 'boolean'\" class=\"flex align-items-center gap-2\">\n <p-checkbox\n [ngModel]=\"open.values[f.spec.id]\"\n (ngModelChange)=\"open.values[f.spec.id] = $event\"\n [binary]=\"true\"\n [inputId]=\"open.tool.id + '-' + f.spec.id\"\n ></p-checkbox>\n <label [for]=\"open.tool.id + '-' + f.spec.id\" class=\"text-sm mb-0\" [pTooltip]=\"f.spec.tooltip || ''\">{{\n f.spec.label\n }}</label>\n </div>\n </div>\n </ng-container>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button\n label=\"Reset to model defaults\"\n [text]=\"true\"\n size=\"small\"\n (onClick)=\"resetToolParams(open.tool.id)\"\n ></p-button>\n <p-button label=\"Close\" size=\"small\" (onClick)=\"closeToolParams()\"></p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}.sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}.sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}.sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i7.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i7.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: i7.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SharedModule }, { kind: "directive", type: i1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i9.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "style", "styleClass", "badgeClass", "ariaLabel", "autofocus"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i10.Checkbox, selector: "p-checkbox", inputs: ["value", "name", "disabled", "binary", "label", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "styleClass", "labelStyleClass", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i11.ContextMenu, selector: "p-contextMenu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "appendTo", "autoZIndex", "baseZIndex", "id", "ariaLabel", "ariaLabelledBy", "pressDelay"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i12.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i13.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "visible", "style", "position"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i14.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "autoShowPanelOnPrintableCharacterKeyDown", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i15.InputNumber, selector: "p-inputNumber", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "style", "placeholder", "size", "maxlength", "tabindex", "title", "ariaLabelledBy", "ariaLabel", "ariaRequired", "name", "required", "autocomplete", "min", "max", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "step", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "variant", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus", "disabled"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i16.ProgressBar, selector: "p-progressBar", inputs: ["value", "showValue", "styleClass", "style", "unit", "mode", "color"] }, { kind: "ngmodule", type: SliderModule }, { kind: "component", type: i17.Slider, selector: "p-slider", inputs: ["animate", "disabled", "min", "max", "orientation", "step", "range", "style", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i18.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "style", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: FloatingDragDirective, selector: "[vizFloatingDrag]", inputs: ["vizFloatingDrag"], outputs: ["vizFloatingDragMove"] }, { kind: "component", type: ToolbarComponent, selector: "plotting-toolbar", inputs: ["imageInfo", "hasSpatialDataset", "plotTypeOptions", "selectedPlotType", "basePlotType", "isoRange", "maxIndex", "zIndex", "isHeatmap", "is3dRegions", "activeDragMode", "activeSurface3dMode", "axesVisible", "wireframeActive", "resolutionScale", "wandSensitivity", "brushSize", "vertexEraserRadius", "canUndo", "canRedo", "stackOptions", "tools", "imageSmoothingEnabled", "samModels", "samModelId", "contributedTools", "dialogTools", "openDialogToolId", "toolModelIds"], outputs: ["selectPlotType", "addProfileLine", "toggleImageSmoothing", "isoRangeChange", "openChannelHistogram", "openSpatialControls", "openRegionEditor", "selectStackOption", "zScrub", "zSlide", "zIndexInput", "reloadAndPlot", "downloadImage", "autoscaleImage", "toggleDragMode", "zoomIn", "zoomOut", "toggleSurface3dMode", "resetSurfaceCamera", "toggleAxes", "toggleWireframe", "selectResolution", "deleteRegion", "undoRegion", "redoRegion", "segmentRegions", "segmentCellpose", "runTool", "toggleDialogTool", "toolModelChange", "openToolParams", "samModelChange", "wandSensitivityChange", "brushSizeChange", "vertexEraserRadiusChange"] }, { kind: "component", type: IntensityInsetComponent, selector: "viz-intensity-inset", inputs: ["divId", "plotDivName", "zIndex", "imageView", "imageInfo"] }, { kind: "component", type: ChannelHistogramComponent, selector: "channel-histogram", inputs: ["visible"], outputs: ["visibleChange"] }, { kind: "component", type: SpatialControlsComponent, selector: "spatial-controls", inputs: ["visible", "is3d"], outputs: ["visibleChange"] }, { kind: "component", type: RegionEditorComponent, selector: "region-editor" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2542
1098
  }
2543
1099
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: VisualizerComponent, decorators: [{
2544
1100
  type: Component,
2545
- args: [{ selector: 'visualizer', template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ samStatus || message.detail }}</div>\n <p-progressBar *ngIf=\"samDownloading\" [value]=\"samProgress\" styleClass=\"mt-2 sam-progress\"></p-progressBar>\n <p-progressBar *ngIf=\"samBusy && !samDownloading\" mode=\"indeterminate\" styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Result/error notices from the library itself (segmentation outcome, region\n validation). Rendered here so they do not depend on the host mounting a\n keyless <p-toast> \u2014 without this the messages went nowhere in any host but\n jit-ui, and a failed run looked like a button that did nothing. -->\n<p-toast [key]=\"resultToastKey\" position=\"bottom-right\"></p-toast>\n\n<!-- Shared outlets for notices raised where there is no per-instance key to use:\n the root-provided PlotlyService and the region-editor dialog. Rendered by a\n single visualizer (ownsSharedToasts) so a message from the root service is\n not shown once per live viewer. -->\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizToastKey\"></p-toast>\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizAlertToastKey\" position=\"center\"></p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div class=\"toolbar-dock\" [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\">\n <div class=\"toolbar-drag-handle\" (mousedown)=\"onToolbarDragStart($event)\"\n pTooltip=\"Drag to float the toolbar\" tooltipPosition=\"bottom\">\n <i class=\"pi pi-bars\"></i>\n </div>\n<plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [hasSpatialDataset]=\"hasSpatialDataset\"\n [plotTypeOptions]=\"plotTypeMenu\"\n [selectedPlotType]=\"selectedPlotTypeId\"\n [basePlotType]=\"basePlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [is3dRegions]=\"isSpatial3dMode\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n [openDialogToolId]=\"openDialogToolId\"\n (toggleDialogTool)=\"toggleDialogTool($event)\"\n [toolModelIds]=\"toolModelIds\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"canUndoRegion\"\n [canRedo]=\"canRedoRegion\"\n [stackOptions]=\"stackOptions\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openSpatialControls)=\"openSpatialControls()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (runTool)=\"runTool($event)\"\n (toolModelChange)=\"onToolModelChange($event)\"\n (openToolParams)=\"openToolParams($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\">\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n</plotting-toolbar>\n <p-button *ngIf=\"toolbarFloating\" class=\"toolbar-dock-btn\" icon=\"pi pi-thumbtack\" size=\"small\" [text]=\"true\"\n pTooltip=\"Dock to top\" tooltipPosition=\"bottom\" (onClick)=\"dockToolbar()\">\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading || stackLoading || isCaching\" id=\"overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading; else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching; else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress\"></p-progressBar>\n <span class=\"text-loading\">{{loadingMessage}}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading && !zoom\" class=\"text-loading\">{{loadingMessage}}</span>\n <span *ngIf=\"imgLoading && zoom\" class=\"zoom-text-loading\">{{loadingMessage}}</span>\n </ng-template>\n </ng-template>\n <p-button *ngIf=\"stackLoading || imgLoading\" class=\"cancel-loading \" label=\"Cancel\"\n (onClick)=\"cancelLoading()\">\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"sharpening && !imgLoading && !stackLoading && !isCaching\" id=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n\n</div>\n\n<!-- Floating, draggable intensity-profile inset. Shown in LINE mode; the chart\n updates live as the line ROI on the image is moved. Kept OUTSIDE #plot and\n positioned fixed so it floats over the whole page (not clipped to the\n diagram/canvas area). -->\n<div *ngIf=\"hasProfiles\" class=\"intensity-panel\"\n [style.left.px]=\"profilePanelPos.x\" [style.top.px]=\"profilePanelPos.y\">\n <div class=\"intensity-panel-header\" (mousedown)=\"onProfilePanelDragStart($event)\">\n <span>Intensity profile</span>\n <i class=\"pi pi-arrows-alt\"></i>\n </div>\n <div [id]=\"intensityInsetDiv\" class=\"intensity-panel-plot\"></div>\n</div>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Spatial-omics controls. Renders its own empty state when no dataset (or no\n SPATIAL_DATA_PORT) is present, so it is always safe to host. -->\n<spatial-controls [(visible)]=\"showSpatialControls\" [is3d]=\"isSpatial3dMode\" ></spatial-controls>\n\n<!-- Side panel of the active contributed plot mode (PLOT_TYPE_CONTRIBUTIONS),\n in the same right-hand area as the spatial controls and the Region Editor.\n Exists only while the mode's session is live; there is no close button,\n because leaving the mode is what ends it. A component panel gets\n PLOT_MODE_CONTEXT / PLOT_MODE_SESSION from its injector; a `mount` panel's\n host element is attached to the slot below (see plotModePanelSlot). -->\n<p-dialog *ngIf=\"plotModePanel as panel\" [header]=\"panel.title\" [visible]=\"true\"\n [modal]=\"false\" [closable]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n appendTo=\"body\" position=\"right\" styleClass=\"plot-mode-panel\"\n [style]=\"{ width: '22rem', maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <ng-container *ngIf=\"panel.component\">\n <ng-container *ngComponentOutlet=\"panel.component; injector: panel.injector ?? undefined\"></ng-container>\n </ng-container>\n <div *ngIf=\"panel.host\" #plotModePanelSlot class=\"plot-mode-panel-slot\"></div>\n</p-dialog>\n\n<!-- An open dialog tool (TOOLBAR_TOOLS, kind: 'dialog'). Non-modal so the user can\n keep working on the image, e.g. painting with the brush the tool armed. The\n tool renders its body into a host element attached to the slot below (see\n toolDialogSlot); closing the dialog ends the tool's session. -->\n<p-dialog *ngIf=\"toolDialog as dlg\" [header]=\"dlg.title\" [visible]=\"true\"\n (visibleChange)=\"$event || closeDialogTool()\"\n [modal]=\"false\" [closable]=\"true\" [resizable]=\"true\" [draggable]=\"true\"\n appendTo=\"body\" position=\"right\" styleClass=\"tool-dialog\"\n [style]=\"{ width: dlg.width, maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <div #toolDialogSlot class=\"tool-dialog-slot\"></div>\n</p-dialog>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog header=\"Region Editor\" [(visible)]=\"showRegionEditor\"\n [modal]=\"false\" [resizable]=\"true\" [draggable]=\"true\"\n [maximizable]=\"true\" appendTo=\"body\" position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\">\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog header=\"Simplify region(s)\" [(visible)]=\"displaySimplifyDialog\" [modal]=\"true\"\n [style]=\"{width: '22rem'}\" [draggable]=\"false\" [resizable]=\"false\">\n <div class=\"flex flex-column gap-2\">\n <span>Altitude threshold: <b>{{ simplifyThreshold }} px</b></span>\n <p-slider [(ngModel)]=\"simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button label=\"Apply\" icon=\"pi pi-check\" (onClick)=\"simplifyRegions(simplifyThreshold)\"></p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Parameters for whichever contributed tool is open. One dialog for all of\n them: a tool declares its fields (ToolParamSpec) and this renders them, so\n the toolbar can lay every tool out the same way and a plugin needs no\n PrimeNG of its own. Defaults come from the active checkpoint, since they\n encode what it was trained for. -->\n<p-dialog *ngIf=\"openParamsTool as tool\"\n [header]=\"tool.label + ' parameters'\"\n [visible]=\"true\" (visibleChange)=\"openParamsToolId = null\"\n [modal]=\"true\"\n [style]=\"{width: '30rem'}\" [draggable]=\"false\" [resizable]=\"false\" appendTo=\"body\">\n <div class=\"grid formgrid p-fluid\">\n <ng-container *ngFor=\"let p of tool.params\">\n <div class=\"field\" [ngClass]=\"p.type === 'boolean' ? 'col-12' : 'col-6'\">\n <ng-container [ngSwitch]=\"p.type\">\n\n <ng-container *ngSwitchCase=\"'number'\">\n <label class=\"text-sm\" [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n <p-inputNumber [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n [min]=\"numberParam(p).min\" [max]=\"numberParam(p).max\"\n [step]=\"numberParam(p).step\"\n [minFractionDigits]=\"numberParam(p).fractionDigits\"\n [showButtons]=\"true\"></p-inputNumber>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'select'\">\n <label class=\"text-sm\" [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n <p-dropdown [options]=\"selectParam(p).options\" optionLabel=\"label\" optionValue=\"value\"\n [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n appendTo=\"body\"></p-dropdown>\n </ng-container>\n\n <!-- Checkbox label sits beside the box and may run to a sentence, so\n it does not get the stacked label the other types use. -->\n <div *ngSwitchCase=\"'boolean'\" class=\"flex align-items-center gap-2\">\n <p-checkbox [ngModel]=\"paramsFor(tool.id)[p.id]\"\n (ngModelChange)=\"paramsFor(tool.id)[p.id] = $event\"\n [binary]=\"true\" [inputId]=\"tool.id + '-' + p.id\"></p-checkbox>\n <label [for]=\"tool.id + '-' + p.id\" class=\"text-sm mb-0\"\n [pTooltip]=\"p.tooltip || ''\">{{ p.label }}</label>\n </div>\n\n </ng-container>\n </div>\n </ng-container>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Reset to model defaults\" [text]=\"true\" size=\"small\"\n (onClick)=\"resetToolParams(tool.id)\"></p-button>\n <p-button label=\"Close\" size=\"small\" (onClick)=\"openParamsToolId = null\"></p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}#overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}#sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}#sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}#sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}.intensity-panel{position:fixed;z-index:1100;width:280px;height:180px;background:#191919e6;border:1px solid var(--primary-color, #3399ff);border-radius:6px;box-shadow:0 4px 14px #00000073;display:flex;flex-direction:column;overflow:hidden}.intensity-panel-header{display:flex;align-items:center;justify-content:space-between;padding:.25rem .5rem;cursor:move;-webkit-user-select:none;user-select:none;background:#ffffff14;color:#eee;font-size:.75rem;font-weight:600}.intensity-panel-header i{opacity:.6;font-size:.7rem}.intensity-panel-plot{flex:1 1 auto;width:100%}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"] }]
1101
+ args: [{ selector: 'visualizer', standalone: true, imports: [
1102
+ CommonModule,
1103
+ FormsModule,
1104
+ SharedModule,
1105
+ ButtonModule,
1106
+ CheckboxModule,
1107
+ ContextMenuModule,
1108
+ DialogModule,
1109
+ DropdownModule,
1110
+ InputNumberModule,
1111
+ ProgressBarModule,
1112
+ SliderModule,
1113
+ ToastModule,
1114
+ TooltipModule,
1115
+ FloatingDragDirective,
1116
+ ToolbarComponent,
1117
+ IntensityInsetComponent,
1118
+ ChannelHistogramComponent,
1119
+ SpatialControlsComponent,
1120
+ RegionEditorComponent,
1121
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- SAM toast: live status + encoder-download progress bar (jit-ui#90). Sticky\n while a segment runs; stays open until the bar reaches 100% and the run ends. -->\n<p-toast [key]=\"samToastKey\" position=\"bottom-right\">\n <ng-template let-message pTemplate=\"message\">\n <div style=\"flex: 1; min-width: 16rem\">\n <div class=\"font-bold mb-1\">{{ message.summary }}</div>\n <div>{{ segmentation.status || message.detail }}</div>\n <p-progressBar\n *ngIf=\"segmentation.downloading\"\n [value]=\"segmentation.progress\"\n styleClass=\"mt-2 sam-progress\"\n ></p-progressBar>\n <p-progressBar\n *ngIf=\"segmentation.busy && !segmentation.downloading\"\n mode=\"indeterminate\"\n styleClass=\"mt-2\"\n [style]=\"{ height: '4px' }\"\n ></p-progressBar>\n </div>\n </ng-template>\n</p-toast>\n\n<!-- Result/error notices from the library itself (segmentation outcome, region\n validation). Rendered here so they do not depend on the host mounting a\n keyless <p-toast> \u2014 without this the messages went nowhere in any host but\n jit-ui, and a failed run looked like a button that did nothing. -->\n<p-toast [key]=\"resultToastKey\" position=\"bottom-right\"></p-toast>\n\n<!-- Shared outlets for notices raised where there is no per-instance key to use:\n the root-provided PlotlyService and the region-editor dialog. Rendered by a\n single visualizer (ownsSharedToasts) so a message from the root service is\n not shown once per live viewer. -->\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizToastKey\"></p-toast>\n<p-toast *ngIf=\"ownsSharedToasts\" [key]=\"vizAlertToastKey\" position=\"center\"></p-toast>\n\n<!-- Toolbar dock: docked across the top by default; drag the handle to detach\n it into a floating, movable window (frees the top row for the image). -->\n<div\n class=\"toolbar-dock\"\n [class.floating]=\"toolbarFloating\"\n [style.left.px]=\"toolbarFloating ? toolbarPos.x : null\"\n [style.top.px]=\"toolbarFloating ? toolbarPos.y : null\"\n>\n <div\n class=\"toolbar-drag-handle\"\n [vizFloatingDrag]=\"toolbarDragOrigin\"\n (vizFloatingDragMove)=\"toolbarPos = $event\"\n pTooltip=\"Drag to float the toolbar\"\n tooltipPosition=\"bottom\"\n >\n <i class=\"pi pi-bars\"></i>\n </div>\n <plotting-toolbar\n [imageInfo]=\"imageInfo\"\n [hasSpatialDataset]=\"hasSpatialDataset\"\n [plotTypeOptions]=\"plotTypeMenu\"\n [selectedPlotType]=\"selectedPlotTypeId\"\n [basePlotType]=\"basePlotType\"\n [isoRange]=\"isoRange\"\n [maxIndex]=\"maxIndex\"\n [zIndex]=\"zIndex\"\n [isHeatmap]=\"isHeatmap\"\n [is3dRegions]=\"isSpatial3dMode\"\n [activeDragMode]=\"activeDragMode\"\n [activeSurface3dMode]=\"activeSurface3dMode\"\n [axesVisible]=\"axesVisible\"\n [wireframeActive]=\"wireframeActive\"\n [resolutionScale]=\"resolutionScale\"\n [wandSensitivity]=\"wandSensitivity\"\n [brushSize]=\"brushSize\"\n [samModels]=\"samModels\"\n [samModelId]=\"samModelId\"\n [contributedTools]=\"contributedTools\"\n [dialogTools]=\"dialogTools\"\n [openDialogToolId]=\"openDialogToolId\"\n (toggleDialogTool)=\"toggleDialogTool($event)\"\n [toolModelIds]=\"toolParams.toolModelIds\"\n [vertexEraserRadius]=\"vertexEraserRadius\"\n [canUndo]=\"regionActions.canUndo\"\n [canRedo]=\"regionActions.canRedo\"\n [tools]=\"toolbarTools\"\n [imageSmoothingEnabled]=\"imageSmoothingEnabled\"\n (selectPlotType)=\"onSelectPlotType($event)\"\n (toggleImageSmoothing)=\"onToggleImageSmoothing()\"\n (addProfileLine)=\"addProfileLine()\"\n (isoRangeChange)=\"onIsoRangeChange($event)\"\n (openChannelHistogram)=\"openChannelHistogram()\"\n (openSpatialControls)=\"openSpatialControls()\"\n (openRegionEditor)=\"openRegionEditor()\"\n (selectStackOption)=\"selectStackOption($event)\"\n (zScrub)=\"onZScrub($event)\"\n (zSlide)=\"onZSlide($event)\"\n (zIndexInput)=\"zIndex = $event; updateZIndex()\"\n (reloadAndPlot)=\"reloadAndPlot()\"\n (downloadImage)=\"downloadImage()\"\n (autoscaleImage)=\"autoscaleImage()\"\n (toggleDragMode)=\"toggleDragMode($event)\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (toggleSurface3dMode)=\"toggleSurface3dMode($event)\"\n (resetSurfaceCamera)=\"resetSurfaceCamera()\"\n (toggleAxes)=\"toggleAxes()\"\n (toggleWireframe)=\"toggleWireframe()\"\n (selectResolution)=\"selectResolution($event)\"\n (deleteRegion)=\"deleteRegion()\"\n (undoRegion)=\"undoRegion()\"\n (redoRegion)=\"redoRegion()\"\n (segmentRegions)=\"segmentRegions()\"\n (segmentCellpose)=\"segmentCellpose()\"\n (samModelChange)=\"onSamModelChange($event)\"\n (runTool)=\"runTool($event)\"\n (toolModelChange)=\"onToolModelChange($event)\"\n (openToolParams)=\"openToolParams($event)\"\n (wandSensitivityChange)=\"onWandSensitivityChange($event)\"\n (brushSizeChange)=\"onBrushSizeChange($event)\"\n (vertexEraserRadiusChange)=\"onVertexEraserRadiusChange($event)\"\n >\n <!-- Host-projected custom toolbar buttons, forwarded into the toolbar. -->\n <ng-content></ng-content>\n </plotting-toolbar>\n <p-button\n *ngIf=\"toolbarFloating\"\n class=\"toolbar-dock-btn\"\n icon=\"pi pi-thumbtack\"\n size=\"small\"\n [text]=\"true\"\n pTooltip=\"Dock to top\"\n tooltipPosition=\"bottom\"\n (onClick)=\"dockToolbar()\"\n >\n </p-button>\n</div>\n\n<p-contextMenu #cm [model]=\"contextMenuItems\" appendTo=\"body\"></p-contextMenu>\n<!-- No `h-full` here: PrimeFlex's height:100% !important made #plot as tall as\n the whole host, so under the docked toolbar its bottom toolbar-height strip\n (incl. the OSD scale bar at bottom-left) was clipped by the host's\n overflow:hidden. The component stylesheet sizes it (.viz-plot { flex: 1 }). -->\n<div class=\"w-full relative viz-plot\" [id]=\"plotDivName\">\n <div *ngIf=\"imgLoading() || stackLoading() || isCaching()\" class=\"loading-overlay\">\n <!-- Determinate stack/volume progress takes precedence: while it's active show ONLY the\n progress bar + \"Loading frames...\", not the generic spinner/\"Loading image...\" on top of\n it (both were rendering at once for napari volume/isosurface loads). -->\n <ng-container *ngIf=\"stackLoading(); else imgLoadingTpl\">\n <p-progressBar class=\"progress\" [value]=\"loadingPercentage()\"></p-progressBar>\n <span class=\"text-loading\">Loading frames...</span>\n </ng-container>\n <ng-template #imgLoadingTpl>\n <!-- Cache-copy progress is shown whenever a cache download is active (isCaching), independent\n of `imgLoading`, so it's never dropped/masked mid-download. -->\n <ng-container *ngIf=\"isCaching(); else spinnerLoading\">\n <!-- Stage-aware message comes from CacheProgressService updating\n imageLoadingMessage$ (\"Caching image...\" during the GCS download,\n then \"Generating preview...\" during per-slice thumbnail gen). Once the\n download hits 100% the remaining work (server preview/tile generation)\n has no determinate progress, so fall through to the spinner template\n rather than parking a full bar under \"Loading image...\". -->\n <p-progressBar class=\"progress\" [value]=\"cacheProgress()\"></p-progressBar>\n <span class=\"text-loading\">{{ loadingMessage() }}</span>\n </ng-container>\n <ng-template #spinnerLoading>\n <div *ngIf=\"imgLoading()\" class=\"spinner\"></div>\n <span *ngIf=\"imgLoading() && !zoom()\" class=\"text-loading\">{{ loadingMessage() }}</span>\n <span *ngIf=\"imgLoading() && zoom()\" class=\"zoom-text-loading\">{{ loadingMessage() }}</span>\n </ng-template>\n </ng-template>\n <p-button\n *ngIf=\"stackLoading() || imgLoading()\"\n class=\"cancel-loading\"\n label=\"Cancel\"\n (onClick)=\"cancelLoading()\"\n >\n </p-button>\n </div>\n <!-- Translucent \"sharpening\" overlay shown after the small-tier preview is\n on screen while the large-tier render is still loading in the\n background. Lets the user see the blurry image underneath but makes\n it clear something is still loading so they don't mistake the\n low-res render for the final image. -->\n <div *ngIf=\"render.sharpening && !imgLoading() && !stackLoading() && !isCaching()\" class=\"sharpening-overlay\">\n <div class=\"spinner\"></div>\n <span class=\"text-loading\">Sharpening preview...</span>\n </div>\n</div>\n\n<!-- Floating, draggable intensity-profile inset, shown while any intensity line\n exists. Kept OUTSIDE the plot div and fixed to the viewport, so it floats over\n the whole page rather than being clipped to the canvas. -->\n<viz-intensity-inset\n [divId]=\"intensityInsetDiv\"\n [plotDivName]=\"plotDivName\"\n [zIndex]=\"zIndex\"\n [imageView]=\"isImageView\"\n [imageInfo]=\"imageInfo\"\n></viz-intensity-inset>\n\n<!-- Channels & Histogram: non-modal floating dialog opened from the toolbar. -->\n<channel-histogram [(visible)]=\"showChannelHistogram\"></channel-histogram>\n\n<!-- Spatial-omics controls. Renders its own empty state when no dataset (or no\n SPATIAL_DATA_PORT) is present, so it is always safe to host. -->\n<spatial-controls [(visible)]=\"showSpatialControls\" [is3d]=\"isSpatial3dMode\"></spatial-controls>\n\n<!-- Side panel of the active contributed plot mode (PLOT_TYPE_CONTRIBUTIONS),\n in the same right-hand area as the spatial controls and the Region Editor.\n Exists only while the mode's session is live; there is no close button,\n because leaving the mode is what ends it. A component panel gets\n PLOT_MODE_CONTEXT / PLOT_MODE_SESSION from its injector; a `mount` panel's\n host element is attached to the slot below (see plotModePanelSlot). -->\n<p-dialog\n *ngIf=\"plotModePanel as panel\"\n [header]=\"panel.title\"\n [visible]=\"true\"\n [modal]=\"false\"\n [closable]=\"false\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n styleClass=\"plot-mode-panel\"\n [style]=\"{ width: '22rem', maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <ng-container *ngIf=\"panel.component\">\n <ng-container *ngComponentOutlet=\"panel.component; injector: panel.injector ?? undefined\"></ng-container>\n </ng-container>\n <div *ngIf=\"panel.host\" #plotModePanelSlot class=\"plot-mode-panel-slot\"></div>\n</p-dialog>\n\n<!-- An open dialog tool (TOOLBAR_TOOLS, kind: 'dialog'). Non-modal so the user can\n keep working on the image, e.g. painting with the brush the tool armed. The\n tool renders its body into a host element attached to the slot below (see\n toolDialogSlot); closing the dialog ends the tool's session. -->\n<p-dialog\n *ngIf=\"toolDialog as dlg\"\n [header]=\"dlg.title\"\n [visible]=\"true\"\n (visibleChange)=\"$event || closeDialogTool()\"\n [modal]=\"false\"\n [closable]=\"true\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n styleClass=\"tool-dialog\"\n [style]=\"{ width: dlg.width, maxHeight: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <div #toolDialogSlot class=\"tool-dialog-slot\"></div>\n</p-dialog>\n\n<!-- Region Editor: non-modal, draggable, resizable floating dialog opened from\n the toolbar (formerly the right-panel Regions tab). Modeless so regions can\n still be drawn/selected on the plot while it's open; stays linked to this\n plotting instance via the root RegionStore singleton. Opens over the right\n side of the view. -->\n<p-dialog\n header=\"Region Editor\"\n [(visible)]=\"showRegionEditor\"\n [modal]=\"false\"\n [resizable]=\"true\"\n [draggable]=\"true\"\n [maximizable]=\"true\"\n appendTo=\"body\"\n position=\"right\"\n [style]=\"{ width: regionEditorWidth, height: '85vh' }\"\n [contentStyle]=\"{ overflow: 'auto' }\"\n>\n <region-editor></region-editor>\n</p-dialog>\n\n<!-- Simplify (Douglas\u2013Peucker): custom px-threshold picker for the selected\n region(s), opened from the right-click \"Simplify \u25B8 Custom\u2026\" action. -->\n<p-dialog\n header=\"Simplify region(s)\"\n [(visible)]=\"displaySimplifyDialog\"\n [modal]=\"true\"\n [style]=\"{ width: '22rem' }\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n>\n <div class=\"flex flex-column gap-2\">\n <span\n >Altitude threshold: <b>{{ regionActions.simplifyThreshold }} px</b></span\n >\n <p-slider [(ngModel)]=\"regionActions.simplifyThreshold\" [min]=\"1\" [max]=\"20\" [step]=\"1\"></p-slider>\n <small>Vertices within this distance of the line between their neighbours are removed.</small>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Cancel\" [text]=\"true\" (onClick)=\"displaySimplifyDialog = false\"></p-button>\n <p-button\n label=\"Apply\"\n icon=\"pi pi-check\"\n (onClick)=\"simplifyRegions(regionActions.simplifyThreshold)\"\n ></p-button>\n </ng-template>\n</p-dialog>\n\n<!-- Parameters for whichever contributed tool is open. One dialog for all of\n them: a tool declares its fields (ToolParamSpec) and this renders them, so\n the toolbar can lay every tool out the same way and a plugin needs no\n PrimeNG of its own. Defaults come from the active checkpoint, since they\n encode what it was trained for. -->\n<p-dialog\n *ngIf=\"toolParams.openParams as open\"\n [header]=\"open.tool.label + ' parameters'\"\n [visible]=\"true\"\n (visibleChange)=\"closeToolParams()\"\n [modal]=\"true\"\n [style]=\"{ width: '30rem' }\"\n [draggable]=\"false\"\n [resizable]=\"false\"\n appendTo=\"body\"\n>\n <div class=\"grid formgrid p-fluid\">\n <ng-container *ngFor=\"let f of open.fields\">\n <div class=\"field\" [ngClass]=\"f.spec.type === 'boolean' ? 'col-12' : 'col-6'\">\n <ng-container *ngIf=\"f.number as n\">\n <label class=\"text-sm\" [pTooltip]=\"n.tooltip || ''\">{{ n.label }}</label>\n <p-inputNumber\n [ngModel]=\"open.values[n.id]\"\n (ngModelChange)=\"open.values[n.id] = $event\"\n [min]=\"n.min\"\n [max]=\"n.max\"\n [step]=\"n.step\"\n [minFractionDigits]=\"n.fractionDigits\"\n [showButtons]=\"true\"\n ></p-inputNumber>\n </ng-container>\n\n <ng-container *ngIf=\"f.select as sel\">\n <label class=\"text-sm\" [pTooltip]=\"sel.tooltip || ''\">{{ sel.label }}</label>\n <p-dropdown\n [options]=\"sel.options\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [ngModel]=\"open.values[sel.id]\"\n (ngModelChange)=\"open.values[sel.id] = $event\"\n appendTo=\"body\"\n ></p-dropdown>\n </ng-container>\n\n <!-- Checkbox label sits beside the box and may run to a sentence, so\n it does not get the stacked label the other types use. -->\n <div *ngIf=\"f.spec.type === 'boolean'\" class=\"flex align-items-center gap-2\">\n <p-checkbox\n [ngModel]=\"open.values[f.spec.id]\"\n (ngModelChange)=\"open.values[f.spec.id] = $event\"\n [binary]=\"true\"\n [inputId]=\"open.tool.id + '-' + f.spec.id\"\n ></p-checkbox>\n <label [for]=\"open.tool.id + '-' + f.spec.id\" class=\"text-sm mb-0\" [pTooltip]=\"f.spec.tooltip || ''\">{{\n f.spec.label\n }}</label>\n </div>\n </div>\n </ng-container>\n </div>\n <ng-template pTemplate=\"footer\">\n <p-button\n label=\"Reset to model defaults\"\n [text]=\"true\"\n size=\"small\"\n (onClick)=\"resetToolParams(open.tool.id)\"\n ></p-button>\n <p-button label=\"Close\" size=\"small\" (onClick)=\"closeToolParams()\"></p-button>\n </ng-template>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%;overflow:hidden}.viz-plot{flex:1;min-height:0;height:auto!important}.toolbar-dock{display:flex;align-items:stretch;width:100%}.toolbar-dock plotting-toolbar{flex:1;min-width:0}.toolbar-dock.floating{position:fixed;z-index:1000;width:auto;max-width:calc(100% - 16px);background:var(--surface-card, #ffffff);border:1px solid var(--surface-border, #cccccc);border-radius:6px;box-shadow:0 4px 16px #0000004d}.toolbar-dock.floating plotting-toolbar{flex:0 1 auto}.toolbar-dock.floating .toolbar-drag-handle{border-radius:6px 0 0 6px}.toolbar-drag-handle{display:flex;align-items:center;justify-content:center;padding:0 .4rem;cursor:move;color:var(--primary-color);background:var(--surface-100, rgba(0, 0, 0, .04));border-right:1px solid var(--surface-border, #cccccc);-webkit-user-select:none;user-select:none}.toolbar-dock-btn{align-self:center}:host ::ng-deep .navigator{margin-bottom:80px!important;margin-right:12px!important}kbd{background:#e8e8e8;border:1px solid #bbb;border-radius:3px;padding:1px 5px;font-family:monospace;font-size:.85em}.toolbar-separator{border-left:1px solid var(--surface-border);height:1.5rem;margin:0 .25rem;align-self:center}.progress{position:absolute;width:50%;left:25%;top:55%}.text-loading{position:absolute;margin:0 auto;left:45%;top:60%;font-weight:700}.zoom-text-loading{position:absolute;margin:0 auto;left:35%;top:60%;font-weight:700;display:inline-block;width:350px}.cancel-loading{position:absolute;margin:0 auto;left:46%;top:70%}.spinner{position:absolute;left:45%;top:45%;height:100px;width:100px;margin:0 auto;-webkit-animation:rotation .6s infinite linear;-moz-animation:rotation .6s infinite linear;-o-animation:rotation .6s infinite linear;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgb(82,107,178);border-radius:100%}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff80;z-index:1000}.sharpening-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffff2e;pointer-events:none;z-index:1000}.sharpening-overlay .spinner{height:60px;width:60px;border-width:4px;left:calc(50% - 30px);top:calc(50% - 60px)}.sharpening-overlay .text-loading{position:absolute;left:0;right:0;width:auto;text-align:center;top:calc(50% + 35px);font-size:.9rem;color:#526bb2}@-webkit-keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(359deg)}}@-moz-keyframes rotation{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(359deg)}}@-o-keyframes rotation{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(359deg)}}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}::ng-deep .context-menu-active>.p-menuitem-content{background-color:var(--primary-color)!important}::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-icon,::ng-deep .context-menu-active>.p-menuitem-content .p-menuitem-text{color:#fff!important}::ng-deep .context-menu-active>.p-menuitem-content .zoom-box-off-icon,::ng-deep .context-menu-active>.p-menuitem-content .wand-icon,::ng-deep .context-menu-active>.p-menuitem-content .polyline-icon,::ng-deep .context-menu-active>.p-menuitem-content .polygon-vertices-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-add-icon,::ng-deep .context-menu-active>.p-menuitem-content .vertex-delete-icon,::ng-deep .context-menu-active>.p-menuitem-content .region-move-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-bezier-icon,::ng-deep .context-menu-active>.p-menuitem-content .to-polygon-icon{filter:brightness(0) invert(1)}::ng-deep .zoom-box-off-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" fill=\"black\" fill-rule=\"evenodd\">%0A <!-- Rectangle outline -->%0A <rect x=\"2\" y=\"2\" width=\"56\" height=\"56\"/>%0A <rect x=\"9\" y=\"9\" width=\"42\" height=\"42\"/>%0A <!-- Magnifying glass ring -->%0A <circle cx=\"70\" cy=\"70\" r=\"23\"/>%0A <circle cx=\"70\" cy=\"70\" r=\"14\"/>%0A <!-- Handle -->%0A <polygon points=\"81,81 97,97 92,100 77,85\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .wand-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<svg%0A viewBox=\"0 0 100 100\"%0A fill=\"black\"%0A fill-rule=\"evenodd\"%0A version=\"1.1\"%0A id=\"svg4\"%0A sodipodi:docname=\"wand.svg\"%0A inkscape:version=\"1.4.3 (0d15f75, 2025-12-25)\"%0A xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"%0A xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"%0A xmlns=\"http://www.w3.org/2000/svg\"%0A xmlns:svg=\"http://www.w3.org/2000/svg\">%0A <defs%0A id=\"defs4\" />%0A <sodipodi:namedview%0A id=\"namedview4\"%0A pagecolor=\"%23ffffff\"%0A bordercolor=\"%23000000\"%0A borderopacity=\"0.25\"%0A inkscape:showpageshadow=\"2\"%0A inkscape:pageopacity=\"0.0\"%0A inkscape:pagecheckerboard=\"0\"%0A inkscape:deskcolor=\"%23d1d1d1\"%0A inkscape:zoom=\"8.74\"%0A inkscape:cx=\"50\"%0A inkscape:cy=\"49.942792\"%0A inkscape:window-width=\"1024\"%0A inkscape:window-height=\"1186\"%0A inkscape:window-x=\"3560\"%0A inkscape:window-y=\"131\"%0A inkscape:window-maximized=\"0\"%0A inkscape:current-layer=\"svg4\" />%0A <!-- Wand handle: thin diagonal stick from lower-left to upper-right -->%0A <polygon%0A points=\"12,92 78,26 74,22 8,88 \"%0A id=\"polygon1\"%0A transform=\"matrix(1.0375192,0.05799445,0.25179496,0.91871864,-17.85351,5.6289793)\" />%0A <!-- 4-pointed star at the wand tip -->%0A <polygon%0A points=\"62,34 76,30 80,6 84,30 98,34 84,38 80,62 76,38 \"%0A id=\"polygon2\"%0A transform=\"matrix(1.3082889,0,0,1.0245178,-38.907958,-1.4056881)\" />%0A <!-- Small sparkle to the upper-left -->%0A <polygon%0A points=\"46,30 36,32 34,42 32,32 22,30 32,28 34,18 36,28 \"%0A id=\"polygon3\"%0A transform=\"translate(-9.610984,-6.7505721)\" />%0A <!-- Small sparkle to the lower-right -->%0A <polygon%0A points=\"62,80 70,82 62,84 60,92 58,84 50,82 58,80 60,72 \"%0A id=\"polygon4\"%0A transform=\"translate(0,-1.6018307)\" />%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .brush-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d=\"M512.5 74.3L291.1 222C262 241.4 243.5 272.9 240.5 307.3C302.8 320.1 351.9 369.2 364.8 431.6C399.3 428.6 430.7 410.1 450.1 381L597.7 159.5C604.4 149.4 608 137.6 608 125.4C608 91.5 580.5 64 546.6 64C534.5 64 522.6 67.6 512.5 74.3zM320 464C320 402.1 269.9 352 208 352C146.1 352 96 402.1 96 464C96 467.9 96.2 471.8 96.6 475.6C98.4 493.1 86.4 512 68.8 512L64 512C46.3 512 32 526.3 32 544C32 561.7 46.3 576 64 576L208 576C269.9 576 320 525.9 320 464z\"/></svg>') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polyline-icon{background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\">%0A <polyline points=\"1,14 5,3 10,11 15,2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .polygon-vertices-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Polygon (click-to-place) tool icon: a traced polyline with vertex dots. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.4740709,0,0,1.4740709,-6.0268793,-6.8086826)\">%0A <polyline points=\"23.877,16.627 14.657,10.767 5.25,21.551 12.757,29.397\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\"%0A stroke-linejoin=\"round\" stroke-linecap=\"round\"/>%0A <circle cx=\"23.877\" cy=\"16.627\" r=\"1.9\"/>%0A <circle cx=\"14.657\" cy=\"10.767\" r=\"1.9\"/>%0A <circle cx=\"5.25\" cy=\"21.551\" r=\"1.9\"/>%0A <circle cx=\"12.757\" cy=\"29.397\" r=\"1.9\"/>%0A </g>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-add-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Add-vertex tool icon: a region blob with three corner vertices and a plus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 9.65,29.0 H 5.85 v 5.3 H 0.55 v 3.4 H 5.85 v 5.3 H 9.65 V 37.7 H 14.95 V 34.3 H 9.65 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .vertex-delete-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Delete-vertex tool icon: a region blob with three corner vertices and a minus glyph. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <g transform=\"matrix(1.5222969,0,0,1.5222969,-8.2143505,-7.6146399)\">%0A <path d=\"m 9.35834,12.33267 c 1.78,-2.53 4.93,-3.56 7.86,-3.98 3.98,-0.72 8.33,0.08 11.52,2.67 2.62,1.98 4.71,5.1 4.53,8.49 -0.29,2.27 -2.5,3.81 -4.66,4.01 -2.75,0.43 -5.54,0.07 -8.28,-0.26 -3.99,-0.68 -8.05,-2.16 -10.93,-5.12 -1.32,-1.63 -1.39,-4.17 -0.04,-5.81 z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"1.6\" stroke-miterlimit=\"10\"/>%0A <circle cx=\"8.294\" cy=\"15.248\" r=\"2.63\"/>%0A <circle cx=\"19.781\" cy=\"8.287\" r=\"2.63\"/>%0A <circle cx=\"25.044\" cy=\"23.748\" r=\"2.63\"/>%0A </g>%0A <path d=\"M 0.55,34.3 v 3.4 H 14.95 V 34.3 Z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .region-move-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Region-move tool icon: a region blob with a drag arrow. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <path d=\"m 17.115582,8.9648664 c 1.707597,-3.3276806 4.729468,-4.6824281 7.540287,-5.2348498 3.827703,-0.947008 7.991169,0.1052238 11.051414,3.5118228 2.523022,2.604273 4.518416,6.7079726 4.345738,11.1668036 -0.278204,2.985704 -2.398311,5.011251 -4.470451,5.274308 -2.638142,0.565574 -5.314655,0.09206 -7.943203,-0.341976 C 23.811664,22.446579 19.916808,20.499952 17.153955,16.606696 15.89724,14.462775 15.820494,11.12194 17.115585,8.9648664 Z\"%0A fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.1\" stroke-miterlimit=\"10\"/>%0A <path d=\"m 26.741429,13.966963 6.746922,16.786269 3.229398,-4.060758 3.583857,3.583857 2.761195,-2.761196 -3.583856,-3.571852 4.060758,-3.2354 z\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-bezier-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-bezier tool icon: a smooth curve through two anchor points with control handles. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- smooth cubic curve between the two anchors -->%0A <path d=\"M 7,34 C 7,16 37,28 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\"/>%0A <!-- control handles -->%0A <line x1=\"7\" y1=\"34\" x2=\"7\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <line x1=\"37\" y1=\"10\" x2=\"37\" y2=\"22\" stroke=\"%23000000\" stroke-width=\"1.4\"/>%0A <circle cx=\"7\" cy=\"22\" r=\"2.2\"/>%0A <circle cx=\"37\" cy=\"22\" r=\"2.2\"/>%0A <!-- anchor points -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}::ng-deep .to-polygon-icon{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>%0A<!-- Convert-to-polygon tool icon: endpoints joined by straight segments through a vertex. -->%0A<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 44 44\" width=\"44\" height=\"44\" fill=\"%23000000\">%0A <!-- straight-segment path through the vertices -->%0A <polyline points=\"7,34 22,12 37,10\" fill=\"none\" stroke=\"%23000000\" stroke-width=\"2.4\"%0A stroke-linecap=\"round\" stroke-linejoin=\"round\"/>%0A <!-- vertices -->%0A <rect x=\"4\" y=\"31\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"19\" y=\"9\" width=\"6\" height=\"6\" rx=\"1\"/>%0A <rect x=\"34\" y=\"7\" width=\"6\" height=\"6\" rx=\"1\"/>%0A</svg>%0A') no-repeat center center;background-size:contain;width:1.25rem;height:1.25rem;display:inline-block;filter:brightness(0) invert(47%) sepia(98%) saturate(567%) hue-rotate(181deg) brightness(103%) contrast(98%)}:host ::ng-deep .sam-progress .p-progressbar-value{transition:none}\n"] }]
2546
1122
  }], ctorParameters: () => [{ type: undefined, decorators: [{
2547
1123
  type: Inject,
2548
1124
  args: [IMAGE_STATE_PORT]
@@ -2571,7 +1147,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
2571
1147
  args: [PLOT_TYPE_CONTRIBUTIONS]
2572
1148
  }] }, { type: i0.Injector, decorators: [{
2573
1149
  type: Optional
2574
- }] }], propDecorators: { isStackEvent: [{
1150
+ }] }, { type: i0.ElementRef, decorators: [{
1151
+ type: Optional
1152
+ }] }, { type: i0.DestroyRef }], propDecorators: { isStackEvent: [{
2575
1153
  type: Output
2576
1154
  }], isGrayscaleEvent: [{
2577
1155
  type: Output
@@ -2585,11 +1163,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
2585
1163
  }], plotModePanelSlot: [{
2586
1164
  type: ViewChild,
2587
1165
  args: ['plotModePanelSlot']
2588
- }], onKeydown: [{
2589
- type: HostListener,
2590
- args: ['window:keydown', ['$event']]
1166
+ }], inset: [{
1167
+ type: ViewChild,
1168
+ args: [IntensityInsetComponent, { static: true }]
2591
1169
  }], toolDialogSlot: [{
2592
1170
  type: ViewChild,
2593
1171
  args: ['toolDialogSlot']
2594
1172
  }] } });
2595
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlzdWFsaXplci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3Zpc3VhbGl6ZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vc3JjL2xpYi92aXN1YWxpemVyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxTQUFTLEVBQTZCLFlBQVksRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFnQyxRQUFRLEVBQUUsTUFBTSxFQUF1QixTQUFTLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFck8sT0FBTyxFQUErQixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUUzQyxPQUFPLEVBQVksY0FBYyxFQUFZLE1BQU0sYUFBYSxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUVsRCxPQUFPLEVBQWtCLGdCQUFnQixFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDdEYsT0FBTyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFVLE1BQU0saUJBQWlCLENBQUM7QUFDM0UsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3JFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUNuRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsYUFBYSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDMUUsT0FBTyxFQUNMLFFBQVEsRUFHUixxQkFBcUIsRUFDckIsaUJBQWlCLEVBQ2pCLFVBQVUsRUFDVixjQUFjLEVBQ2QsZ0JBQWdCLEVBQ2hC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LENBQUMsbUJBQW1CLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDOUMsQ0FBQzs4R0FwakZVLG1CQUFtQixrQkFtWHBCLGdCQUFnQixhQUNoQixVQUFVLHdRQVNFLFVBQVUsNkJBSVYsYUFBYSw2QkFHYixpQkFBaUIsNkJBR2pCLHVCQUF1QjtrR0F2WWxDLG1CQUFtQix1a0JDNUVoQywwbGZBaVNBOzsyRkRyTmEsbUJBQW1CO2tCQVAvQixTQUFTOytCQUdFLFlBQVk7OzBCQXVYbkIsTUFBTTsyQkFBQyxnQkFBZ0I7OzBCQUN2QixNQUFNOzJCQUFDLFVBQVU7OzBCQVNqQixRQUFROzswQkFBSSxNQUFNOzJCQUFDLFVBQVU7OzBCQUk3QixRQUFROzswQkFBSSxNQUFNOzJCQUFDLGFBQWE7OzBCQUdoQyxRQUFROzswQkFBSSxNQUFNOzJCQUFDLGlCQUFpQjs7MEJBR3BDLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsdUJBQXVCOzswQkFHMUMsUUFBUTt5Q0FyV1gsWUFBWTtzQkFEWCxNQUFNO2dCQUdQLGdCQUFnQjtzQkFEZixNQUFNO2dCQU1ILFlBQVk7c0JBRGYsS0FBSztnQkFjTixRQUFRO3NCQURQLEtBQUs7Z0JBaURXLFdBQVc7c0JBQTNCLFNBQVM7dUJBQUMsSUFBSTtnQkFzS1gsaUJBQWlCO3NCQURwQixTQUFTO3VCQUFDLG1CQUFtQjtnQkFvdEM5QixTQUFTO3NCQURSLFlBQVk7dUJBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxRQUFRLENBQUM7Z0JBK2lDdEMsY0FBYztzQkFEakIsU0FBUzt1QkFBQyxnQkFBZ0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFuZ2VEZXRlY3RvclJlZiwgQ29tcG9uZW50LCBBZnRlclZpZXdJbml0LCBFbGVtZW50UmVmLCBFdmVudEVtaXR0ZXIsIEhvc3RMaXN0ZW5lciwgSW5qZWN0LCBJbmplY3RvciwgSW5wdXQsIE5nWm9uZSwgT25DaGFuZ2VzLCBPbkRlc3Ryb3ksIE9uSW5pdCwgT3B0aW9uYWwsIE91dHB1dCwgU2ltcGxlQ2hhbmdlcywgVHlwZSwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgT2JzZXJ2YWJsZSwgU3ViamVjdCwgU3Vic2NyaXB0aW9uIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyB0YWtlVW50aWwgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5cbmltcG9ydCB7IE1lbnVJdGVtLCBNZXNzYWdlU2VydmljZSwgVHJlZU5vZGUgfSBmcm9tICdwcmltZW5nL2FwaSc7XG5pbXBvcnQgeyBDb250ZXh0TWVudSB9IGZyb20gJ3ByaW1lbmcvY29udGV4dG1lbnUnO1xuaW1wb3J0IHsgSUltYWdlSW5mbyB9IGZyb20gJy4vY29udHJhY3RzL2ltYWdlLmNvbnRyYWN0JztcbmltcG9ydCB7IEltYWdlU3RhdGVQb3J0LCBJTUFHRV9TVEFURV9QT1JUIH0gZnJvbSAnLi9jb250cmFjdHMvcG9ydHMvaW1hZ2Utc3RhdGUucG9ydCc7XG5pbXBvcnQgeyBQb2x5Z29uLCBSZWN0YW5nbGUsIE11bHRpUG9seWdvbiwgUmVnaW9uIH0gZnJvbSAnLi9tb2RlbHMvcmVnaW9uJztcbmltcG9ydCB7IFJlZ2lvbk9wc1NlcnZpY2UgfSBmcm9tICcuL3JlZ2lvbi1vcHMuc2VydmljZSc7XG5pbXBvcnQgeyBWSVpfVE9BU1RfS0VZLCBWSVpfQUxFUlRfVE9BU1RfS0VZIH0gZnJvbSAnLi90b2FzdC1vdXRsZXRzJztcbmltcG9ydCB7IFZpc3VhbGl6ZXJTdG9yZSB9IGZyb20gJy4vc3RvcmUvdmlzdWFsaXplci1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IFJlbmRlck9yY2hlc3RyYXRvciwgU2xpY2VTY3J1YmJlciB9IGZyb20gJy4vcmVuZGVyLW9yY2hlc3RyYXRvcic7XG5pbXBvcnQge1xuICBQbG90VHlwZSxcbiAgUGxvdFR5cGVEZXNjcmlwdG9yLFxuICBQbG90VHlwZUlkLFxuICBnZXRQbG90VHlwZURlc2NyaXB0b3IsXG4gIGlzQnVpbHRpblBsb3RUeXBlLFxuICBpc05hcGFyaTNkLFxuICBpc1NwYXRpYWxPbWljcyxcbiAgaXNTcGF0aWFsT21pY3MzZCxcbiAgcmVuZGVyZXJPd25zV2hlZWwsXG4gIE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFLFxufSBmcm9tICcuL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuaW1wb3J0IHtcbiAgUExPVF9NT0RFX0NPTlRFWFQsXG4gIFBMT1RfTU9ERV9TRVNTSU9OLFxuICBQTE9UX1RZUEVfQ09OVFJJQlVUSU9OUyxcbiAgUGxvdE1vZGVCcnVzaENsYXNzLFxuICBQbG90TW9kZUNvbnRleHQsXG4gIFBsb3RNb2RlVG9vbHMsXG4gIFBsb3RUeXBlQ29udHJpYnV0aW9uLFxuICBQbG90VHlwZU9wdGlvbixcbiAgY29udHJpYnV0ZWRQbG90VHlwZU9wdGlvbixcbn0gZnJvbSAnLi9jb250cmFjdHMvcGxvdC10eXBlLWNvbnRyaWJ1dGlvbi5jb250cmFjdCc7XG5pbXBvcnQgeyBBY3RpdmVQbG90TW9kZSwgUGxvdE1vZGVDb250cm9sbGVyIH0gZnJvbSAnLi9wbG90LW1vZGUvcGxvdC1tb2RlLWNvbnRyb2xsZXInO1xuaW1wb3J0IHsgVmlld2VyRmVhdHVyZSB9IGZyb20gJy4vY29udHJhY3RzL2NhcGFiaWxpdGllcy5jb250cmFjdCc7XG5pbXBvcnQgeyBJbnRlbnNpdHlQcm9maWxlLCBJVmlzdWFsaXplciwgVklTVUFMSVpFUiB9IGZyb20gJy4vY29udHJhY3RzL3Zpc3VhbGl6ZXIuY29udHJhY3QnO1xuaW1wb3J0IHsgU0FNX01PREVMUywgZ2V0RGVmYXVsdFNhbU1vZGVsSWQsIGlzU2FtTW9kZWxSZWFkeSB9IGZyb20gJy4vdG9vbGJhci9zZWdtZW50YXRpb24vc2FtLW1vZGVsLXJlZ2lzdHJ5JztcbmltcG9ydCB7IFNhbVRvb2xTZXJ2aWNlIH0gZnJvbSAnLi90b29sYmFyL3NlZ21lbnRhdGlvbi9zYW0tdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IFNhbVBvaW50VG9vbFNlcnZpY2UgfSBmcm9tICcuL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS1wb2ludC10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgQ2VsbFNlZ21lbnRUb29sU2VydmljZSB9IGZyb20gJy4vdG9vbGJhci9zZWdtZW50YXRpb24vY2VsbC1zZWdtZW50LXRvb2wuc2VydmljZSc7XG5pbXBvcnQge1xuICBUT09MQkFSX1RPT0xTLFxuICBOdW1iZXJQYXJhbVNwZWMsXG4gIFNlbGVjdFBhcmFtU3BlYyxcbiAgVG9vbFBhcmFtU3BlYyxcbiAgVG9vbGJhckNvbnRyaWJ1dGlvbixcbiAgVG9vbGJhckRpYWxvZ1Rvb2xDb250cmlidXRpb24sXG4gIFRvb2xiYXJUb29sQ29udHJpYnV0aW9uLFxuICBUb29sRGlhbG9nQ29udGV4dCxcbiAgZGlhbG9nVG9vbENvbnRyaWJ1dGlvbnMsXG4gIHZpc2libGVUb29sQ29udHJpYnV0aW9ucyxcbn0gZnJvbSAnLi9jb250cmFjdHMvdG9vbGJhci10b29sLmNvbnRyYWN0JztcbmltcG9ydCB7IFJlZ2lvblRvb2xNb2RlIH0gZnJvbSAnLi9jb250cmFjdHMvcmVnaW9uLW92ZXJsYXkuY29udHJhY3QnO1xuaW1wb3J0IHsgVG9vbGJhclRvb2xWaXNpYmlsaXR5LCBBTExfVE9PTEJBUl9UT09MUyB9IGZyb20gJy4vY29udHJhY3RzL3Rvb2xiYXItY29uZmlnJztcbmltcG9ydCB7IFZJWl9DT05GSUcsIFZpekNvbmZpZyB9IGZyb20gJy4vY29udHJhY3RzL3Zpei1jb25maWcnO1xuaW1wb3J0IHsgU1BBVElBTF9EQVRBX1BPUlQsIFNwYXRpYWxEYXRhUG9ydCB9IGZyb20gJy4vY29udHJhY3RzL3BvcnRzL3NwYXRpYWwtZGF0YS5wb3J0JztcbmltcG9ydCB7IFNwYXRpYWxEYXRhc2V0IH0gZnJvbSAnLi9jb250cmFjdHMvc3BhdGlhbC1kYXRhc2V0LmNvbnRyYWN0JztcbmltcG9ydCB7IGJ1aWxkVm9sdW1lU3RhY2tJbWFnZSB9IGZyb20gJy4vc3BhdGlhbC9zcGF0aWFsLXZvbHVtZS1pbWFnZSc7XG5cbi8qKiBQZXItaW5zdGFuY2UgcGxvdC1kaXYgaWQgc291cmNlLiBUaGUgbW91bnQgZWxlbWVudCdzIGlkIG11c3QgYmUgdW5pcXVlIHNvIHR3b1xuICogIGxpdmUgdmlld2VycyAoZS5nLiB0aGUgbWFpbiBkaWFncmFtICsgYSBtb2RhbCBwcmV2aWV3KSBkb24ndCBjb2xsaWRlIG9uIHRoZVxuICogIHNhbWUgRE9NIGlkIOKAlCBgZ2V0RWxlbWVudEJ5SWRgIHdvdWxkIG90aGVyd2lzZSByZXR1cm4gd2hpY2hldmVyIGNhbWUgZmlyc3QuXG4gKiAgU3R5bGluZyBoYW5ncyBvZmYgdGhlIGAudml6LXBsb3RgIGNsYXNzIGluc3RlYWQgb2YgdGhlIGlkLiAqL1xubGV0IHBsb3RJbnN0YW5jZVNlcSA9IDA7XG5cbkBDb21wb25lbnQoe1xuICAvLyBDYW5vbmljYWwgcHJlZml4ZWQgc2VsZWN0b3IgZmlyc3Q7IHRoZSB1bnByZWZpeGVkIG9yaWdpbmFsIGlzIGtlcHQgYXMgYW5cbiAgLy8gYWxpYXMgZm9yIG9uZSByZWxlYXNlIChwcmUtcHVibGljYXRpb24gYmFjay1jb21wYXQpLlxuICBzZWxlY3RvcjogJ3Zpc3VhbGl6ZXInLFxuICB0ZW1wbGF0ZVVybDogJy4vdmlzdWFsaXplci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3Zpc3VhbGl6ZXIuY29tcG9uZW50LnNjc3MnXSxcbn0pXG5leHBvcnQgY2xhc3MgVmlzdWFsaXplckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzLCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xuICAvKiogUGVyLWluc3RhbmNlIHRvYXN0IGtleS4gTWVzc2FnZVNlcnZpY2UgaXMgYSBnbG9iYWwgc2luZ2xldG9uLCBzbyB0d28gbGl2ZVxuICAgKiAgYDx2aXN1YWxpemVyPmAgaW5zdGFuY2VzIChtYWluIHZpZXdlciArIHBpcGVsaW5lLWRpYWxvZyBwcmV2aWV3KSBzaGFyaW5nXG4gICAqICBvbmUga2V5IHdvdWxkIGVhY2ggcmVuZGVyIHRoZSBzYW1lIG1lc3NhZ2Ug4oCUIGEgZHVwbGljYXRlIHRvYXN0LiBBIHVuaXF1ZVxuICAgKiAga2V5IHBlciBpbnN0YW5jZSBzY29wZXMgdGhlIHRvYXN0IHRvIHRoZSBjb21wb25lbnQgdGhhdCByYWlzZWQgaXQuICovXG4gIHByaXZhdGUgc3RhdGljIG5leHRUb2FzdElkID0gMDtcblxuICAvKipcbiAgICogVGhlIGxpYnJhcnkgYWxzbyByYWlzZXMgbm90aWNlcyBmcm9tIHBsYWNlcyB0aGF0IGhhdmUgbm8gaW5zdGFuY2Ugb2YgdGhlaXJcbiAgICogb3duIHRvIGtleSBhZ2FpbnN0OiB7QGxpbmsgUGxvdGx5U2VydmljZX0gaXMgYHByb3ZpZGVkSW46ICdyb290J2AsIGFuZCB0aGVcbiAgICogcmVnaW9uIGVkaXRvciBpcyBhIGNoaWxkIGRpYWxvZy4gVGhvc2UgdXNlIHRoZSBmaXhlZFxuICAgKiB7QGxpbmsgVklaX1RPQVNUX0tFWX0gLyB7QGxpbmsgVklaX0FMRVJUX1RPQVNUX0tFWX0gb3V0bGV0cy5cbiAgICpcbiAgICogRm9yIHRoZSBzYW1lIHJlYXNvbiB0aGUgcGVyLWluc3RhbmNlIGtleSBhYm92ZSBleGlzdHMsIGV4YWN0bHkgT05FIG1vdW50ZWRcbiAgICogdmlzdWFsaXplciBtYXkgcmVuZGVyIHRob3NlIHNoYXJlZCBvdXRsZXRzIOKAlCBvdGhlcndpc2UgYSBtZXNzYWdlIGZyb20gdGhlXG4gICAqIHJvb3Qgc2VydmljZSB3b3VsZCBiZSBzaG93biBvbmNlIHBlciBsaXZlIHZpZXdlciAoaml0LXVpIHJ1bnMgYSBtYWluIHZpZXdcbiAgICogYW5kIGEgcGlwZWxpbmUtcHJldmlldyBhdCB0aGUgc2FtZSB0aW1lKS4gVGhlIGZpcnN0IHRvIGluaXQgY2xhaW1zIHRoZW0gYW5kXG4gICAqIHJlbGVhc2VzIG9uIGRlc3Ryb3ksIHNvIHRoZSBzdXJ2aXZpbmcgdmlld2VyIHBpY2tzIHRoZW0gdXAuXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBsaXZlSW5zdGFuY2VzID0gbmV3IFNldDxWaXN1YWxpemVyQ29tcG9uZW50PigpO1xuICByZWFkb25seSB2aXpUb2FzdEtleSA9IFZJWl9UT0FTVF9LRVk7XG4gIHJlYWRvbmx5IHZpekFsZXJ0VG9hc3RLZXkgPSBWSVpfQUxFUlRfVE9BU1RfS0VZO1xuICAvKipcbiAgICogV2hldGhlciB0aGlzIGluc3RhbmNlIHJlbmRlcnMgdGhlIHNoYXJlZCBvdXRsZXRzLiBUaGUgb3duZXIgaXMgc2ltcGx5IHRoZVxuICAgKiBvbGRlc3QgbGl2ZSB2aXN1YWxpemVyIOKAlCBhIGBTZXRgIGl0ZXJhdGVzIGluIGluc2VydGlvbiBvcmRlciwgc28gd2hlbiB0aGVcbiAgICogb3duZXIgaXMgZGVzdHJveWVkIHRoZSBuZXh0IG9uZSB0YWtlcyBvdmVyIG9uIGl0cyBmb2xsb3dpbmcgY2hhbmdlLWRldGVjdGlvblxuICAgKiBwYXNzLiBUcmFja2luZyBhIHNldCByYXRoZXIgdGhhbiBhIHNpbmdsZSBvd25lciBtYXR0ZXJzOiB3aXRoIGEgbG9uZVxuICAgKiBgb3duZXJgIHJlZmVyZW5jZSwgdGVhcmluZyBkb3duIHRoZSBvd25lciB3aGlsZSBhIHNpYmxpbmcgd2FzIGFscmVhZHlcbiAgICogaW5pdGlhbGlzZWQgd291bGQgbGVhdmUgbm9ib2R5IHJlbmRlcmluZyB0aGUgb3V0bGV0cywgYW5kIGV2ZXJ5IG5vdGljZSBmcm9tXG4gICAqIHRoZSByb290IHNlcnZpY2Ugd291bGQgc2lsZW50bHkgdmFuaXNoIGFnYWluLlxuICAgKi9cbiAgZ2V0IG93bnNTaGFyZWRUb2FzdHMoKTogYm9vbGVhbiB7XG4gICAgY29uc3Qgb2xkZXN0ID0gVmlzdWFsaXplckNvbXBvbmVudC5saXZlSW5zdGFuY2VzLnZhbHVlcygpLm5leHQoKTtcbiAgICByZXR1cm4gIW9sZGVzdC5kb25lICYmIG9sZGVzdC52YWx1ZSA9PT0gdGhpcztcbiAgfVxuXG4gIEBPdXRwdXQoKVxuICBpc1N0YWNrRXZlbnQgPSBuZXcgRXZlbnRFbWl0dGVyKGZhbHNlKTtcbiAgQE91dHB1dCgpXG4gIGlzR3JheXNjYWxlRXZlbnQgPSBuZXcgRXZlbnRFbWl0dGVyKGZhbHNlKTtcblxuICAvKiogV2hpY2ggdG9vbGJhciBncm91cHMgdG8gc2hvdy4gQSBwYXJ0aWFsIG92ZXJyaWRlIG1lcmdlcyBvdmVyIHRoZSBmdWxsIHNldCxcbiAgICogIHNvIGB7IHNwZWNpYWxUb29sczogZmFsc2UgfWAgaGlkZXMgb25seSB0aGF0IGdyb3VwLiBEZWZhdWx0cyB0byBldmVyeXRoaW5nLiAqL1xuICBASW5wdXQoKVxuICBzZXQgdG9vbGJhclRvb2xzKHY6IFRvb2xiYXJUb29sVmlzaWJpbGl0eSB8IHVuZGVmaW5lZCkge1xuICAgIHRoaXMuX3Rvb2xiYXJUb29scyA9IHsgLi4uQUxMX1RPT0xCQVJfVE9PTFMsIC4uLih2ID8/IHt9KSB9O1xuICB9XG5cbiAgZ2V0IHRvb2xiYXJUb29scygpOiBSZXF1aXJlZDxUb29sYmFyVG9vbFZpc2liaWxpdHk+IHtcbiAgICByZXR1cm4gdGhpcy5fdG9vbGJhclRvb2xzO1xuICB9XG4gIHByaXZhdGUgX3Rvb2xiYXJUb29sczogUmVxdWlyZWQ8VG9vbGJhclRvb2xWaXNpYmlsaXR5PiA9IEFMTF9UT09MQkFSX1RPT0xTO1xuXG4gIC8qKiBFeHBvc2UgdGVzdC1vbmx5IHBsb3QgbW9kZXMgaW4gdGhlIHNlbGVjdG9yIChlLmcuIHRoZSBuYXBhcmkgYEltYWdlYFxuICAgKiAgbW9kZSwgb3RoZXJ3aXNlIGhpZGRlbikuIFRoZSBob3N0IGFwcCBzZXRzIHRoaXMgZnJvbSBhIGA/dGVzdD0xYCBVUkxcbiAgICogIHRvZ2dsZS4gT2ZmIGJ5IGRlZmF1bHQsIHNvIHByb2R1Y3Rpb24gdXNlcnMgbmV2ZXIgc2VlIHRoZW0uICovXG4gIEBJbnB1dCgpXG4gIHRlc3RNb2RlID0gZmFsc2U7XG5cbiAgLyoqIEltYWdlLXNtb290aGluZyBzdGF0ZSBmb3IgdGhlIHRvb2xiYXIncyBTbW9vdGhlbiB0b2dnbGUgKE9TRCBvbmx5KS5cbiAgICogIERlZmF1bHRzIHRvIGBmYWxzZWAgc28gT1NEIHNob3dzIHJhdyBwaXhlbHMgKG5lYXJlc3QtbmVpZ2hib3VyKS4gKi9cbiAgaW1hZ2VTbW9vdGhpbmdFbmFibGVkID0gZmFsc2U7XG4gIGxvYWRpbmdNZXNzYWdlID0gJ0xvYWRpbmcgaW1hZ2UuLi4nO1xuICB6b29tID0gZmFsc2U7XG4gIGZpbGVOYW1lOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIHByaXZhdGUgbG9hZGVkRmlsZU5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkO1xuXG4gIHByaXZhdGUgdW5zdWIgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICBwcml2YXRlIHByZXZpZXdTdWJzY3JpcHRpb24gPSBuZXcgU3Vic2NyaXB0aW9uKCk7XG5cbiAgcHVibGljIHN0YWNrTG9hZGluZyA9IGZhbHNlO1xuICBwdWJsaWMgaW1nTG9hZGluZyA9IGZhbHNlO1xuICBwdWJsaWMgbG9hZGluZ1BlcmNlbnRhZ2UgPSAwO1xuICAvLyBOb24tbnVsbCAoMC4uMTAwKSB3aGlsZSBqaXQtc2VydmljZSBpcyBjb3B5aW5nIHRoZSBzb3VyY2UgZmlsZSBpbnRvIHRoZVxuICAvLyBsb2NhbCBjYWNoZSBQVkMgb24gZmlyc3QgY2xpY2suIFdoZW4gc2V0LCB0aGUgbG9hZGluZyBvdmVybGF5IHN3YXBzIHRoZVxuICAvLyBzcGlubmVyIGZvciBhIGRldGVybWluYXRlIHByb2dyZXNzIGJhciB3aXRoIGEgXCJDYWNoaW5nIGltYWdlXCIgbWVzc2FnZS5cbiAgcHVibGljIGNhY2hlUHJvZ3Jlc3M6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICAvLyBUcnVlIGJldHdlZW4gdGhlIHNtYWxsLXRpZXIgcmVuZGVyIGxhbmRpbmcgYW5kIHRoZSBsYXJnZS10aWVyIHJlbmRlclxuICAvLyBsYW5kaW5nIGluIHRoZSBtdWx0aS10aWVyIHJlbmRlcmluZyBwYXRoLiBUaGUgdGVtcGxhdGUgdXNlcyB0aGlzIHRvXG4gIC8vIG92ZXJsYXkgYSB0cmFuc2x1Y2VudCBzcGlubmVyIG9uIHRvcCBvZiB0aGUgYmx1cnJ5IHNtYWxsLXRpZXIgaW1hZ2Ugc29cbiAgLy8gdGhlIHVzZXIgZG9lc24ndCBtaXN0YWtlIGl0IGZvciB0aGUgZmluYWwgcHJldmlldy5cbiAgcHVibGljIHNoYXJwZW5pbmcgPSBmYWxzZTtcbiAgLyoqIEEgaml0LXNlcnZpY2UgY2FjaGUgY29weSBpcyBhY3RpdmVseSBpbiBwcm9ncmVzcyAoZGV0ZXJtaW5hdGUgMC4uOTkpLiBEcml2ZXMgdGhlIGNhY2hlXG4gICAqICBwcm9ncmVzcyBiYXIgaW5kZXBlbmRlbnRseSBvZiBgaW1nTG9hZGluZ2Agc28gdGhlIGJhciBjYW4ndCBiZSBkcm9wcGVkIOKAlCBvciBtYXNrZWQgYnkgdGhlXG4gICAqICBcIlNoYXJwZW5pbmcgcHJldmlldy4uLlwiIG92ZXJsYXkg4oCUIG1pZC1kb3dubG9hZCBpZiB0aGUgbG9hZGluZyBmbGFnIGZsaXBzIGVhcmx5LiAqL1xuICBnZXQgaXNDYWNoaW5nKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNhY2hlUHJvZ3Jlc3MgIT09IG51bGwgJiYgdGhpcy5jYWNoZVByb2dyZXNzIDwgMTAwO1xuICB9XG4gIHByaXZhdGUgcnVubmluZyA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBNb25vdG9uaWMgcmVuZGVyIGdlbmVyYXRpb24uIEJ1bXBlZCB3aGVuIGEgcmVuZGVyIHN0YXJ0cywgc28gYSByZW5kZXIgdGhhdFxuICAgKiBoYXMgYmVlbiBzdXBlcnNlZGVkIGJ5IGEgbmV3ZXIgaW1hZ2UgY2FuIGJlIHJlY29nbmlzZWQgYW5kIG1hZGUgaW5lcnQ6IGl0c1xuICAgKiBjYWxsYmFja3MgcmV0dXJuIGVhcmx5IGluc3RlYWQgb2YgcGFpbnRpbmcsIGNsZWFyaW5nIGBydW5uaW5nYCwgcmVsZWFzaW5nXG4gICAqIHRoZSBuZXdlciByZW5kZXIncyBvdmVybGF5LCBvciBhcHBseWluZyBpdHMgUk9Jcy5cbiAgICovXG4gIHByaXZhdGUgcmVuZGVyVG9rZW4gPSAwO1xuICAvKiogU2V0IG9uY2UgdGhlIHRlbXBsYXRlLCBhbmQgd2l0aCBpdCB0aGUgcGxvdCBkaXYsIGV4aXN0cyAoc2VlIG5nQWZ0ZXJWaWV3SW5pdCkuICovXG4gIHByaXZhdGUgdmlld1JlYWR5ID0gZmFsc2U7XG4gIC8qKiBBbiBpbWFnZS1sZXNzIGRhdGFzZXQgd2hvc2UgZHJhdyB3YWl0cyBmb3IgdGhlIHZpZXc6IHNlZSBwbG90U3BhdGlhbFdpdGhvdXRJbWFnZS4gKi9cbiAgcHJpdmF0ZSBwZW5kaW5nU3BhdGlhbERyYXc6IFNwYXRpYWxEYXRhc2V0IHwgbnVsbCA9IG51bGw7XG4gIHB1YmxpYyB6SW5kZXggPSAwO1xuICBwdWJsaWMgbWF4SW5kZXggPSAwO1xuXG4gIEBWaWV3Q2hpbGQoJ2NtJykgY29udGV4dE1lbnUhOiBDb250ZXh0TWVudTtcbiAgY29udGV4dE1lbnVJdGVtczogTWVudUl0ZW1bXSA9IFtdO1xuXG4gIGFjdGl2ZURyYWdNb2RlOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuICAvKiogV2hldGhlciBhIHJlZ2lvbiBhY3Rpb24gaXMgYXZhaWxhYmxlIHRvIHVuZG8gKGppdC11aSM4NSkuIE1pcnJvcnMgdGhlXG4gICAqICBzaGFyZWQgUmVnaW9uU3RvcmUncyBoaXN0b3J5IGRlcHRoOyBkcml2ZXMgdGhlIHRvb2xiYXIgVW5kbyBidXR0b24uICovXG4gIGNhblVuZG9SZWdpb24gPSBmYWxzZTtcbiAgLyoqIFdoZXRoZXIgYW4gdW5kb25lIHJlZ2lvbiBhY3Rpb24gaXMgYXZhaWxhYmxlIHRvIHJlZG8gKGppdC11aSM4NSkuICovXG4gIGNhblJlZG9SZWdpb24gPSBmYWxzZTtcblxuICAvKiogQ3VzdG9tLXRocmVzaG9sZCBTaW1wbGlmeSBkaWFsb2cgKGppdC11aSM4NSkuICovXG4gIGRpc3BsYXlTaW1wbGlmeURpYWxvZyA9IGZhbHNlO1xuICBzaW1wbGlmeVRocmVzaG9sZCA9IDM7XG4gIC8qKiBDdXJyZW50IHJlZ2lvbiBzZWxlY3Rpb24gKGFycmF5IGluZGljZXMpIOKAlCBtaXJyb3JlZCBmcm9tIHRoZSBzdG9yZSBzbyB0aGVcbiAgICogIGNvbnRleHQtbWVudSByZWdpb24gYWN0aW9ucyBjYW4gcmVhZCBpdCBzeW5jaHJvbm91c2x5IChqaXQtdWkjODUpLiAqL1xuICBwcml2YXRlIHNlbGVjdGVkSW5kaWNlczogbnVtYmVyW10gPSBbXTtcblxuICAvKiogV2FuZCBzZW5zaXRpdml0eSDigJQgaGlnaGVyID0gc3RyaWN0ZXIgKHNtYWxsZXIgc2VsZWN0aW9uKS4gTWF0Y2hlcyBRdVBhdGggZGVmYXVsdC4gKi9cbiAgd2FuZFNlbnNpdGl2aXR5ID0gMi4wO1xuICAvKiogQnJ1c2ggZGlhbWV0ZXIgaW4gaW1hZ2UtcGl4ZWwgY29vcmRpbmF0ZXMgKGRyaXZlcyB0aGUgcGFpbnRlZCBkaXNjIHNpemUpLiAqL1xuICBicnVzaFNpemUgPSA0MDtcbiAgLyoqIENsYXNzIHRoZSBicnVzaCBwYWludHMgd2hpbGUgYSBwbG90IG1vZGUgYXJtZWQgaXQgKFBsb3RNb2RlVG9vbHMuYXJtQnJ1c2gpOyBudWxsID0gcGxhaW4gYnJ1c2guICovXG4gIHByaXZhdGUgYnJ1c2hDbGFzczogUGxvdE1vZGVCcnVzaENsYXNzIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIG9wdGlvbnMgKyBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzkwIFAxKS4gT25seSBtb2RlbHNcbiAgICogIHdpdGggYSBob3N0ZWQgT05OWCBwYWlyIChjb25maWd1cmVkIHZpYSBzZXRTYW1Nb2RlbFVybHMgYXQgYXBwIGluaXQpIGFyZVxuICAgKiAgb2ZmZXJlZCwgc28gdGhlIHBpY2tlciBjYW4ndCBzZWxlY3QgYSBtb2RlbCB0aGF0IGNhbid0IHJ1bi4gKi9cbiAgc2FtTW9kZWxzID0gU0FNX01PREVMUy5maWx0ZXIoaXNTYW1Nb2RlbFJlYWR5KS5tYXAoKG0pID0+ICh7IGlkOiBtLmlkLCBsYWJlbDogbS5sYWJlbCB9KSk7XG4gIHNhbU1vZGVsSWQgPSBnZXREZWZhdWx0U2FtTW9kZWxJZCgpO1xuXG4gIC8qKlxuICAgKiBOby1wcm9tcHQgdG9vbHMgcmVnaXN0ZXJlZCB0aHJvdWdoIHtAbGluayBUT09MQkFSX1RPT0xTfSwgZmlsdGVyZWQgdG8gdGhvc2VcbiAgICogd2l0aCBjb25maWd1cmVkIG1vZGVscyBhbmQgc29ydGVkLiBUaGlzIGxpYnJhcnkgcmVnaXN0ZXJzIG5vbmUg4oCUIGEgaG9zdFxuICAgKiBzdXBwbGllcyB0aGVtIOKAlCBzbyBhbiBvcGVuIGJ1aWxkIGhhcyBhbiBlbXB0eSBhcnJheSBoZXJlIGFuZCB0aGUgdG9vbGJhclxuICAgKiBhbmQgaGVscCBkaWFsb2cgc2ltcGx5IG9taXQgdGhhdCBncm91cC5cbiAgICovXG4gIGNvbnRyaWJ1dGVkVG9vbHM6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uW10gPSBbXTtcbiAgLyoqXG4gICAqIERpYWxvZyB0b29scyByZWdpc3RlcmVkIHRocm91Z2gge0BsaW5rIFRPT0xCQVJfVE9PTFN9IChga2luZDogJ2RpYWxvZydgKSxcbiAgICogc29ydGVkLiBUaGVpciBidXR0b25zIHNpdCB3aXRoIHRoZSBob3N0J3Mgb3duIGFjdGlvbnMgYW5kIHNob3cgaW4gdGhlIEltYWdlXG4gICAqIHZpZXcgb25seTsgZWFjaCBvcGVucyBhIGZsb2F0aW5nIGRpYWxvZyB0aGUgdG9vbCBmaWxscy5cbiAgICovXG4gIGRpYWxvZ1Rvb2xzOiBUb29sYmFyRGlhbG9nVG9vbENvbnRyaWJ1dGlvbltdID0gW107XG4gIC8qKiBUaGUgZGlhbG9nIHRvb2wgdGhlIHVzZXIgaGFzIG9wZW4gKGl0cyBzZXNzaW9uIG1heSBiZSBiZXR3ZWVuIHJlbmRlcnMpLiAqL1xuICBvcGVuRGlhbG9nVG9vbElkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgLyoqIFRoZSBvcGVuIGRpYWxvZyB0b29sJ3MgbGl2ZSBkaWFsb2csIGFzIHRoZSB0ZW1wbGF0ZSByZW5kZXJzIGl0LiAqL1xuICB0b29sRGlhbG9nOiB7IHRpdGxlOiBzdHJpbmc7IHdpZHRoOiBzdHJpbmc7IGhvc3Q6IEhUTUxFbGVtZW50IH0gfCBudWxsID0gbnVsbDtcbiAgLyoqIERpYWxvZy10b29sIHNlc3Npb25zOiB0aGUgc2FtZSBsaWZlY3ljbGUgYW5kIGlzb2xhdGlvbiBhcyBjb250cmlidXRlZCBwbG90IG1vZGVzLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHRvb2xEaWFsb2dzOiBQbG90TW9kZUNvbnRyb2xsZXI7XG4gIC8qKiBBY3RpdmUgY2hlY2twb2ludCBwZXIgY29udHJpYnV0ZWQgdG9vbC4gKi9cbiAgdG9vbE1vZGVsSWRzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gIC8qKlxuICAgKiBQYXJhbWV0ZXIgdmFsdWVzIHBlciBjb250cmlidXRlZCB0b29sLCBzZWVkZWQgZnJvbSBlYWNoIHRvb2wncyBvd24gZGVmYXVsdHNcbiAgICog4oCUIHdoaWNoIGVuY29kZSB0aGUgc2NhbGUgYW5kIGNyb3dkaW5nIGl0cyBjaGVja3BvaW50IHdhcyB0cmFpbmVkIGZvci5cbiAgICovXG4gIHRvb2xQYXJhbXM6IFJlY29yZDxzdHJpbmcsIFJlY29yZDxzdHJpbmcsIHVua25vd24+PiA9IHt9O1xuICAvKiogV2hpY2ggdG9vbCdzIHBhcmFtZXRlciBkaWFsb2cgaXMgb3Blbiwgb3IgbnVsbC4gKi9cbiAgb3BlblBhcmFtc1Rvb2xJZDogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBTQU0gZG93bmxvYWQvc2VnbWVudCB0b2FzdCBzdGF0ZSAoYm91bmQgYnkgdGhlIGBzYW1gIHAtdG9hc3QgdGVtcGxhdGUpLiAqL1xuICBzYW1TdGF0dXMgPSAnJztcbiAgc2FtUHJvZ3Jlc3MgPSAwOyAvLyAwLi4xMDAsIGVuY29kZXIgZG93bmxvYWRcbiAgc2FtRG93bmxvYWRpbmcgPSBmYWxzZTtcbiAgc2FtQnVzeSA9IGZhbHNlOyAvLyBhbnkgU0FNIHdvcmsgaW4gZmxpZ2h0IChkcml2ZXMgdGhlIGluZGV0ZXJtaW5hdGUgc3Bpbm5lcilcbiAgLyoqIFdoZXRoZXIgdGhlIHNoYXJlZCBgc2FtYCB0b2FzdCBpcyBjdXJyZW50bHkgc2hvd24gKGF2b2lkcyBzdGFja2luZyBpdCBvblxuICAgKiAgZXZlcnkgcG9pbnQgY2xpY2ssIHdoaWNoIHJlLXJ1bnMgaW5mZXJlbmNlKS4gKi9cbiAgcHJpdmF0ZSBzYW1Ub2FzdFNob3duID0gZmFsc2U7XG5cbiAgcmVhZG9ubHkgc2FtVG9hc3RLZXkgPSBgc2FtLSR7VmlzdWFsaXplckNvbXBvbmVudC5uZXh0VG9hc3RJZCsrfWA7XG4gIC8qKlxuICAgKiBPdXRsZXQgZm9yIHRoZSBsaWJyYXJ5J3Mgb3duIHJlc3VsdC9lcnJvciBub3RpY2VzLlxuICAgKlxuICAgKiBQcmltZU5HIHJvdXRlcyBhIG1lc3NhZ2UgdG8gYSBgPHAtdG9hc3Q+YCBvbmx5IHdoZW4gdGhlIGtleXMgbWF0Y2gg4oCUIGFcbiAgICoga2V5bGVzcyBtZXNzYWdlIHJlYWNoZXMgYSBrZXlsZXNzIHRvYXN0IGFuZCBub3RoaW5nIGVsc2UuIFRoZXNlIG5vdGljZXMgdXNlZFxuICAgKiB0byBiZSBlbWl0dGVkIHdpdGggbm8ga2V5LCBzbyB0aGV5IHJlbmRlcmVkIG9ubHkgaW4gaG9zdHMgdGhhdCBoYXBwZW4gdG9cbiAgICogbW91bnQgYSBiYXJlIGA8cC10b2FzdD5gIChqaXQtdWkgZG9lcywgaW4gaXRzIGZpbGUtdHJlZSBjb21wb25lbnQpLiBFdmVyeVxuICAgKiBvdGhlciBob3N0IOKAlCBpbmNsdWRpbmcgdGhpcyByZXBvJ3Mgb3duIGJyb3dzZXIgZXhhbXBsZSDigJQgZHJvcHBlZCB0aGVtXG4gICAqIHNpbGVudGx5LCB3aGljaCBtYWRlIGEgZmFpbGVkIG9yIG5vLW9wIHNlZ21lbnRhdGlvbiBsb29rIGxpa2UgYSBkZWFkIGJ1dHRvbjpcbiAgICogdGhlIHN0aWNreSBwcm9ncmVzcyB0b2FzdCBpcyBjbGVhcmVkIGluIHRoZSBzYW1lIHRpY2sgYnkgaGlkZVNhbVRvYXN0LCBhbmRcbiAgICogdGhlIG1lc3NhZ2UgZXhwbGFpbmluZyB3aHkgbmV2ZXIgYXBwZWFyZWQgYW55d2hlcmUuXG4gICAqXG4gICAqIEtleWVkIHRvIHRoaXMgY29tcG9uZW50IGluc3RhbmNlIHNvIHRoZSBsaWJyYXJ5IHJlbmRlcnMgaXRzIG93biBvdXRsZXQgYW5kXG4gICAqIGlzIG5vdCBkZXBlbmRlbnQgb24gaG9zdCBtYXJrdXAuIERlbGliZXJhdGVseSBOT1QgdGhlIHN0aWNreSB0b2FzdCdzIGtleTpcbiAgICoge0BsaW5rIGhpZGVTYW1Ub2FzdH0gY2xlYXJzIHRoYXQga2V5IGluIHRoZSBgZmluYWxseWAsIHdoaWNoIHdvdWxkIHdpcGUgdGhlXG4gICAqIHJlc3VsdCB0aGUgbW9tZW50IGl0IHdhcyBwb3N0ZWQsIGFuZCB0aGUgc3RpY2t5IHRvYXN0J3MgY3VzdG9tIHRlbXBsYXRlIGlzXG4gICAqIGJ1aWx0IGZvciB0aGUgbGl2ZSBzdGF0dXMgKyBwcm9ncmVzcyBiYXIuXG4gICAqL1xuICByZWFkb25seSByZXN1bHRUb2FzdEtleSA9IGAke3RoaXMuc2FtVG9hc3RLZXl9LXJlc3VsdGA7XG4gIC8qKiBTdWJzY3JpcHRpb25zIHRvIHRoZSBwb2ludCB0b29sJ3MgbGl2ZSBmZWVkcyAoc3RhdHVzL2J1c3kvZG93bmxvYWQpLiAqL1xuICBwcml2YXRlIHNhbVBvaW50U3ViID0gbmV3IFN1YnNjcmlwdGlvbigpO1xuICAvKiogVmVydGV4IGVyYXNlciByYWRpdXMgaW4gaW1hZ2UtcGl4ZWwgY29vcmRpbmF0ZXMuICovXG4gIHZlcnRleEVyYXNlclJhZGl1cyA9IDIwO1xuXG4gIGNvbG9ybWFwc09wdGlvbnMhOiBhbnk7XG4gIHJldmVyc2VzY2FsZSA9IGZhbHNlO1xuICBzZWxlY3RlZENvbG9ybWFwITogYW55O1xuICAvKiogQ2hhbm5lbHMgJiBIaXN0b2dyYW0gZGlhbG9nIHZpc2liaWxpdHkgKG9wZW5lZCBmcm9tIHRoZSB0b29sYmFyKS4gKi9cbiAgc2hvd0NoYW5uZWxIaXN0b2dyYW0gPSBmYWxzZTtcbiAgLyoqIFNwYXRpYWwtb21pY3MgY29udHJvbHMgZGlhbG9nIHZpc2liaWxpdHkgKHRvb2xiYXIgYnV0dG9uKS4gKi9cbiAgc2hvd1NwYXRpYWxDb250cm9scyA9IGZhbHNlO1xuICAvKiogUmVnaW9uIEVkaXRvciBkaWFsb2cgdmlzaWJpbGl0eSAob3BlbmVkIGZyb20gdGhlIHRvb2xiYXIpLiAqL1xuICBzaG93UmVnaW9uRWRpdG9yID0gZmFsc2U7XG4gIC8qKiBSZWdpb24gRWRpdG9yIGRpYWxvZyB3aWR0aC4gT24gb3BlbiBpdCBpcyBzZXQgdG8gdGhlIGNvbmZpZ3VyZWQgaG9zdFxuICAgKiAgZWxlbWVudCdzIGN1cnJlbnQgd2lkdGgsIG9yIOKAlCB3aGVuIG5vIHNlbGVjdG9yIGlzIGNvbmZpZ3VyZWQgLyB0aGUgZWxlbWVudFxuICAgKiAgaXNuJ3QgZm91bmQg4oCUIHRvIGEgcXVhcnRlciBvZiB0aGUgcGFnZSAoc2VlIG9wZW5SZWdpb25FZGl0b3IpLiAqL1xuICByZWdpb25FZGl0b3JXaWR0aCA9ICcyNXZ3JztcbiAgc3RhY2tPcHRpb25zID0gW1xuICAgIHsgbmFtZTogJ1NpbmdsZSBpbWFnZScsIHZhbDogJ2ZhbHNlJyB9LFxuICAgIHsgbmFtZTogJ1N0YWNrJywgdmFsOiAndHJ1ZScgfSxcbiAgXTtcbiAgc2VsZWN0ZWRTdGFja09wdGlvbjogeyBuYW1lOiBzdHJpbmc7IHZhbDogc3RyaW5nIH0gfCB1bmRlZmluZWQgPSB0aGlzLnN0YWNrT3B0aW9uc1swXTtcbiAgcmVhZG9ubHkgcGxvdERpdk5hbWUgPSBgdml6LXBsb3QtJHtwbG90SW5zdGFuY2VTZXErK31gO1xuICBwbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuICBpc0hlYXRtYXAgPSB0cnVlO1xuICBhY3RpdmVTdXJmYWNlM2RNb2RlID0gJ3R1cm50YWJsZSc7XG4gIC8qKiBXaGV0aGVyIHRoZSBuYXBhcmkgM0QgY29vcmRpbmF0ZS1heGVzIC8gc2NhbGUgZ2l6bW8gaXMgc2hvd24gKHZvbHVtZS9pc29zdXJmYWNlKS4gKi9cbiAgYXhlc1Zpc2libGUgPSB0cnVlO1xuICB3aXJlZnJhbWVBY3RpdmUgPSBmYWxzZTtcbiAgLyoqIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKDEgPSBGdWxsIOKApiA4ID0g4oWbOyBkZWZhdWx0IMK9KTsgY2hhbmdpbmcgaXQgcmUtcGxvdHMuICovXG4gIHJlc29sdXRpb25TY2FsZSA9IE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFO1xuXG4gIC8qKiBUaGUgc2VsZWN0b3IncyBlbnRyaWVzOiB0aGUgYnVpbHQtaW4gcGxvdCB0eXBlcyB0aGUgYWN0aXZlIGJhY2tlbmRcbiAgICogIGFkdmVydGlzZXMgKDNEIGdhdGVkIGJ5IGNhcGFiaWxpdHkpLCB0aGVuIGFueSBjb250cmlidXRlZCBtb2Rlc1xuICAgKiAgKHtAbGluayBQTE9UX1RZUEVfQ09OVFJJQlVUSU9OU30pLiAqL1xuICBwbG90VHlwZU1lbnU6IFBsb3RUeXBlT3B0aW9uW10gPSBbXTtcbiAgLyoqXG4gICAqIFRoZSBidWlsdC1pbiBwbG90IHR5cGVzIG9uIG9mZmVyIOKAlCB7QGxpbmsgcGxvdFR5cGVNZW51fSB3aXRob3V0IGNvbnRyaWJ1dGVkXG4gICAqIG1vZGVzLiBLZXB0IHdpdGggaXRzIG9yaWdpbmFsIHR5cGUgc28gZXhpc3RpbmcgcmVhZGVycyBhcmUgdW5hZmZlY3RlZC5cbiAgICovXG4gIHBsb3RUeXBlT3B0aW9uczogUGxvdFR5cGVEZXNjcmlwdG9yW10gPSBbXTtcbiAgLyoqXG4gICAqIFdoYXQgdGhlIHNlbGVjdG9yIHNob3dzOiBhIGJ1aWx0LWluIHR5cGUgb3IgYSBjb250cmlidXRlZCBtb2RlJ3MgaWQuIEV2ZXJ5XG4gICAqIHJlbmRlcmluZyBhbmQgdG9vbCBkZWNpc2lvbiBnb2VzIHRocm91Z2gge0BsaW5rIGJhc2VQbG90VHlwZX0gaW5zdGVhZCwgc28gYVxuICAgKiBjb250cmlidXRlZCBtb2RlIGJlaGF2ZXMgZXhhY3RseSBsaWtlIHRoZSBidWlsdC1pbiB0eXBlIGl0IHJpZGVzIG9uLlxuICAgKi9cbiAgc2VsZWN0ZWRQbG90VHlwZUlkOiBQbG90VHlwZUlkID0gUGxvdFR5cGUuSU1BR0U7XG5cbiAgLyoqXG4gICAqIFRoZSBidWlsdC1pbiBwbG90IHR5cGUgYmVpbmcgcmVuZGVyZWQuIFdpdGggbm8gY29udHJpYnV0ZWQgbW9kZSBhY3RpdmUgdGhpc1xuICAgKiBpcyB0aGUgc2VsZWN0aW9uIGl0c2VsZjsgZHVyaW5nIG9uZSBpdCBpcyB0aGUgbW9kZSdzIGBiYXNlVHlwZWAuIEtlcHQgd2l0aCBpdHNcbiAgICogb3JpZ2luYWwgYFBsb3RUeXBlYCB0eXBlIGZvciBleGlzdGluZyByZWFkZXJzIOKAlCB1c2Uge0BsaW5rIHNlbGVjdGVkUGxvdFR5cGVJZH1cbiAgICogZm9yIHRoZSBzZWxlY3RvcidzIGlkLiBBc3NpZ25pbmcgc2VsZWN0cyB0aGF0IGJ1aWx0LWluIHR5cGUuXG4gICAqL1xuICBnZXQgc2VsZWN0ZWRQbG90VHlwZSgpOiBQbG90VHlwZSB7XG4gICAgcmV0dXJuIHRoaXMuYmFzZVBsb3RUeXBlO1xuICB9XG4gIHNldCBzZWxlY3RlZFBsb3RUeXBlKHR5cGU6IFBsb3RUeXBlKSB7XG4gICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlSWQgPSB0eXBlO1xuICB9XG5cbiAgLyoqIENvbnRyaWJ1dGVkIHBsb3QgbW9kZXMgYW5kIHRoZWlyIHNpbmdsZSBsaXZlIHNlc3Npb24uICovXG4gIHJlYWRvbmx5IHBsb3RNb2RlczogUGxvdE1vZGVDb250cm9sbGVyO1xuICAvKipcbiAgICogVGhlIGxpdmUgY29udHJpYnV0ZWQgbW9kZSdzIHNpZGUgcGFuZWwsIGFzIHRoZSB0ZW1wbGF0ZSByZW5kZXJzIGl0OiBhblxuICAgKiBBbmd1bGFyIGNvbXBvbmVudCB3aXRoIGl0cyBvd24gaW5qZWN0b3IsIG9yIGEgcGxhaW4gaG9zdCBlbGVtZW50IGEgYG1vdW50YFxuICAgKiBwYW5lbCByZW5kZXJlZCBpbnRvLiBOdWxsIHdoaWxlIG5vIGNvbnRyaWJ1dGVkIHNlc3Npb24gaXMgbGl2ZS5cbiAgICovXG4gIHBsb3RNb2RlUGFuZWw6IHtcbiAgICB0aXRsZTogc3RyaW5nO1xuICAgIGNvbXBvbmVudDogVHlwZTx1bmtub3duPiB8IG51bGw7XG4gICAgaW5qZWN0b3I6IEluamVjdG9yIHwgbnVsbDtcbiAgICBob3N0OiBIVE1MRWxlbWVudCB8IG51bGw7XG4gIH0gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBkZXN0cm95aW5nID0gZmFsc2U7XG5cbiAgLyoqIFdoZXJlIGEgYG1vdW50YCBwYW5lbCdzIGhvc3QgZWxlbWVudCBpcyBhdHRhY2hlZCBvbmNlIHRoZSBkaWFsb2cgcmVuZGVycy4gKi9cbiAgQFZpZXdDaGlsZCgncGxvdE1vZGVQYW5lbFNsb3QnKVxuICBzZXQgcGxvdE1vZGVQYW5lbFNsb3QocmVmOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PiB8IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IGhvc3QgPSB0aGlzLnBsb3RNb2RlUGFuZWw/Lmhvc3Q7XG4gICAgaWYgKHJlZiAmJiBob3N0ICYmIGhvc3QucGFyZW50RWxlbWVudCAhPT0gcmVmLm5hdGl2ZUVsZW1lbnQpIHJlZi5uYXRpdmVFbGVtZW50LmFwcGVuZENoaWxkKGhvc3QpO1xuICB9XG5cbiAgLyoqIFRoZSBidWlsdC1pbiB0eXBlIGFjdHVhbGx5IHJlbmRlcmVkIGZvciB7QGxpbmsgc2VsZWN0ZWRQbG90VHlwZUlkfTogaXRzZWxmXG4gICAqICBmb3IgYSBidWlsdC1pbiB0eXBlLCBgYmFzZVR5cGVgIGZvciBhIGNvbnRyaWJ1dGVkIG1vZGUuICovXG4gIGdldCBiYXNlUGxvdFR5cGUoKTogUGxvdFR5cGUge1xuICAgIHJldHVybiB0aGlzLnBsb3RNb2Rlcy5iYXNlVHlwZU9mKHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkKTtcbiAgfVxuXG4gIC8qKiBGbG9hdGluZyBpbnRlbnNpdHktcHJvZmlsZSBpbnNldCAoTElORSBtb2RlKS4gKi9cbiAgcmVhZG9ubHkgaW50ZW5zaXR5SW5zZXREaXYgPSAnaW50ZW5zaXR5LWluc2V0LXBsb3QnO1xuICBwcm9maWxlUGFuZWxQb3MgPSB7IHg6IDIwLCB5OiA3MCB9O1xuICBwcml2YXRlIHByb2ZpbGVQYW5lbERyYWdnaW5nID0gZmFsc2U7XG4gIHByaXZhdGUgcHJvZmlsZVBhbmVsU3RhcnQgPSB7IG14OiAwLCBteTogMCwgeDogMCwgeTogMCB9O1xuXG4gIC8qKiBUb29sYmFyIGRvY2tpbmc6IGRvY2tlZCBhY3Jvc3MgdGhlIHRvcCBieSBkZWZhdWx0OyBkcmFnZ2luZyBpdHMgaGFuZGxlXG4gICAqICBkZXRhY2hlcyBpdCBpbnRvIGEgZmxvYXRpbmcsIG1vdmFibGUgd2luZG93IChmcmVlcyB0aGUgdG9wIHJvdyBmb3IgdGhlXG4gICAqICB2aXN1YWxpemF0aW9uKS4gKi9cbiAgdG9vbGJhckZsb2F0aW5nID0gZmFsc2U7XG4gIHRvb2xiYXJQb3MgPSB7IHg6IDgsIHk6IDggfTtcbiAgcHJpdmF0ZSB0b29sYmFyRHJhZ2dpbmcgPSBmYWxzZTtcbiAgcHJpdmF0ZSB0b29sYmFyU3RhcnQgPSB7IG14OiAwLCBteTogMCwgeDogMCwgeTogMCB9O1xuICBwcml2YXRlIGxhdGVzdFByb2ZpbGVzOiBJbnRlbnNpdHlQcm9maWxlW10gPSBbXTtcbiAgLyoqIERyaXZlcyB0aGUgaW50ZW5zaXR5IGluc2V0IHBhbmVsJ3MgdmlzaWJpbGl0eSDigJQgdHJ1ZSB3aGVuZXZlciBhbnlcbiAgICogIGludGVuc2l0eS1wcm9maWxlIGxpbmUgZXhpc3RzIChpbmRlcGVuZGVudCBvZiB0aGUgY3VycmVudCBwbG90IHR5cGUpLiAqL1xuICBoYXNQcm9maWxlcyA9IGZhbHNlO1xuICBwcml2YXRlIGludGVuc2l0eVByb2ZpbGVTdWI/OiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgdmlld3BvcnRDaGFuZ2VTdWI/OiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgcHJvZmlsZURyYWdNb3ZlTGlzdGVuZXI/OiAoZTogTW91c2VFdmVudCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSBwcm9maWxlRHJhZ1VwTGlzdGVuZXI/OiAoKSA9PiB2b2lkO1xuICBwcml2YXRlIHByb2ZpbGVSZXNpemVMaXN0ZW5lcj86ICgpID0+IHZvaWQ7XG5cbiAgLyoqIFRydWUgd2hpbGUgdGhlIDNEIHNwYXRpYWwgY2xvdWQgaXMgdGhlIGFjdGl2ZSBtb2RlLiBUaGUgY29udHJvbHMgZHJvcCB0aGUgUk9JXG4gICAqICBzZWxlY3Rpb24gdGhlcmU6IHJlZ2lvbiB0b29scyBhcmUgc2NyZWVuLXNwYWNlLCBhbmQgYWdhaW5zdCBhbiBvcmJpdGluZ1xuICAgKiAgY2FtZXJhIGEgZHJhd24gcmVjdGFuZ2xlIGhhcyBubyBmaXhlZCBtZWFuaW5nIGluIHRoZSBkYXRhLiAqL1xuICBnZXQgaXNTcGF0aWFsM2RNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc1NwYXRpYWxPbWljczNkKHRoaXMuYmFzZVBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBMSU5FIHBsb3QgdHlwZSBzaG93cyB0aGUgaW1hZ2UgKyBkcmFnZ2FibGUgbGluZSBST0kgKyBpbnRlbnNpdHkgaW5zZXQuICovXG4gIGdldCBpc1Byb2ZpbGVNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmJhc2VQbG90VHlwZSA9PT0gUGxvdFR5cGUuTElORTtcbiAgfVxuXG4gIC8qKiBUcnVlIGZvciB0aGUgSW1hZ2UgcGxvdCB0eXBlLCB3aGljaCByZW5kZXJzIGFzIGEgbmF0aXZlbHkgcGFuL3pvb20tYWJsZVxuICAgKiAgcmFzdGVyIOKAlCBzbyB0aGUgYmFja2VuZC1hZ25vc3RpYyB6b29tL3BhbiB0b29sYmFyIHRvb2xzIGFyZSBoaWRkZW4uIFRoZVxuICAgKiAgY29tcG9uZW50IGRyaXZlcyB0aGlzIG9mZiB0aGUgcGxvdCB0eXBlLCBub3QgdGhlIGFjdGl2ZSBiYWNrZW5kLiAqL1xuICBnZXQgaXNJbWFnZVZpZXcoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuYmFzZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRTtcbiAgfVxuXG4gIC8qKiBJc29zdXJmYWNlIGJhbmQgYXMgYSAw4oCTMjU1IHNsaWRlciBwb3NpdGlvbiwgbWFwcGVkIG9udG8gdGhlIHZvbHVtZSdzIHJlYWxcbiAgICogIGludGVuc2l0eSByYW5nZSBieSB0aGUgcmVuZGVyZXIuIERlZmF1bHRzIHRvIHRoZSBmdWxsIHJhbmdlLiAqL1xuICBpc29SYW5nZTogbnVtYmVyW10gPSBbMCwgMjU1XTtcblxuICAvKiogV2hldGhlciBhIHNwYXRpYWwtb21pY3MgZGF0YXNldCBpcyBjdXJyZW50bHkgcHVibGlzaGVkIG9uXG4gICAqICBgU1BBVElBTF9EQVRBX1BPUlRgIOKAlCBnYXRlcyB0aGUgc3BhdGlhbCBwbG90IHR5cGVzIGluIHRoZSBzZWxlY3Rvci4gKi9cbiAgLyoqIEJvdW5kIGJ5IHRoZSB0b29sYmFyLCB3aGljaCBvZmZlcnMgdGhlIHBsb3QgbW9kZXMgZm9yIGEgZGF0YXNldCB3aXRoIG5vIGltYWdlIHRvby4gKi9cbiAgaGFzU3BhdGlhbERhdGFzZXQgPSBmYWxzZTtcbiAgLyoqIFRoZSBzcGF0aWFsIGRhdGFzZXQgb24gb2ZmZXIsIGZvciBkcmF3aW5nIG9uZSB0aGF0IGJyaW5ncyBubyBpbWFnZSAoc2VlIHJlbG9hZEFuZFBsb3QpLiAqL1xuICBwcml2YXRlIHNwYXRpYWxEYXRhc2V0OiBTcGF0aWFsRGF0YXNldCB8IG51bGwgPSBudWxsO1xuICAvKiogV2hldGhlciB0aGF0IGRhdGFzZXQncyBvYnNlcnZhdGlvbnMgY2FycnkgYSB6LCBnYXRpbmcgdGhlIDNEIHNwYXRpYWwgbW9kZS4gKi9cbiAgcHJpdmF0ZSBoYXNTcGF0aWFsM2REYXRhc2V0ID0gZmFsc2U7XG4gIC8qKiBXaGV0aGVyIGl0IGNhcnJpZXMgYSByZWdpc3RlcmVkIHZvbHVtZS4gQ2hhbmdlIGRldGVjdGlvbiBvbmx5OiBhIHZvbHVtZVxuICAgKiAgYXBwZWFyaW5nIGlzIHdoYXQgcHVibGlzaGVzIGl0IGFzIHRoZSBpbWFnZSwgYW5kIHRoZSBwbG90LXR5cGUgZ2F0ZXMgdGhlblxuICAgKiAgc2VlIGFuIG9yZGluYXJ5IGdyYXlzY2FsZSBzdGFjay4gKi9cbiAgcHJpdmF0ZSBoYXNTcGF0aWFsVm9sdW1lID0gZmFsc2U7XG4gIC8qKiBEYXRhc2V0IGlkZW50aXR5ICsgY2FwYWJpbGl0eSBzaGFwZSB0aGUgbGFzdCBlbWlzc2lvbiB3YXMgaGFuZGxlZCBhdCwgc28gYVxuICAgKiAgc3dpdGNoIGJldHdlZW4gdHdvIGRhdGFzZXRzIG9mIHRoZSBzYW1lIHNoYXBlIGlzIG5vdCBtaXN0YWtlbiBmb3IgYSByZXBlYXQuICovXG4gIHByaXZhdGUgc3BhdGlhbERhdGFzZXRLZXk6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAvKiogRGF0YXNldCArIGdlb21ldHJ5IHRoZSBjdXJyZW50bHkgcHVibGlzaGVkIHZvbHVtZSBpbWFnZSB3YXMgYnVpbHQgZnJvbSwgc28gYVxuICAgKiAgcmUtZW1pdHRlZCBkYXRhc2V0IGRvZXNuJ3QgcmUtZmV0Y2ggbWVnYWJ5dGVzIG9yIHJlc2V0IHRoZSB1c2VyJ3Mgc2NydWIuICovXG4gIHByaXZhdGUgdm9sdW1lSW1hZ2VLZXk6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAvKiogQmxvYiBVUkxzIGJhY2tpbmcgdGhhdCBpbWFnZSDigJQgb3VycyB0byByZXZva2UuICovXG4gIHByaXZhdGUgdm9sdW1lSW1hZ2VVcmxzOiBzdHJpbmdbXSA9IFtdO1xuICBwcml2YXRlIHNwYXRpYWxEYXRhc2V0U3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuXG4gIHBsb3RXaWR0aFN1YnNjcmlwdGlvbj86IFN1YnNjcmlwdGlvbjtcbiAgaW1hZ2VMb2FkaW5nU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuICBpbWdMb2FkaW5nTWVzc2FnZVN1YnNjcmlwdGlvbj86IFN1YnNjcmlwdGlvbjtcbiAgaXNab29tU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuICBmaWxlbmFtZVN1YnNjcmlwdGlvbj86IFN1YnNjcmlwdGlvbjtcbiAgYXV0b3NjYWxlU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xuXG4gIHByaXZhdGUgcGxvdENvbnRleHRNZW51TGlzdGVuZXI/OiAoZTogTW91c2VFdmVudCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSBrZXlkb3duTGlzdGVuZXI/OiAoZTogS2V5Ym9hcmRFdmVudCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSB3aGVlbExpc3RlbmVyPzogKGU6IFdoZWVsRXZlbnQpID0+IHZvaWQ7XG5cbiAgLyoqIERlYm91bmNlZCB6LXNsaWNlIHNjcnViYmluZyAoc2VlIFNsaWNlU2NydWJiZXIg4oCUIHJlZmFjdG9yaW5nIHBsYW4gU3RlcCA3KS4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBzY3J1YmJlciA9IG5ldyBTbGljZVNjcnViYmVyKCh6KSA9PiB7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRaSW5kZXgoeik7XG4gICAgLy8gSW4gYSBwZXItc2xpY2Ugei1zdGFjayBzZXNzaW9uIHRoZSBzdG9yZSBzd2FwcyB0aGUgbGl2ZSByZWdpb24gc2V0IHRvIHRoaXNcbiAgICAvLyBzbGljZSAocHJlc2VydmluZyBlZGl0cyBtYWRlIG9uIHRoZSBwcmV2aW91cyBvbmUpOyBvdXRzaWRlIHN0YWNrIG1vZGUgdGhpc1xuICAgIC8vIGp1c3QgcmVjb3JkcyB0aGUgc2xpY2UsIGxlYXZpbmcgc2luZ2xlLXBsYW5lIHJlZ2lvbnMgdW50b3VjaGVkIChqaXQtdWkjOTMpLlxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0RGlzcGxheVNsaWNlKHopO1xuICB9KTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBASW5qZWN0KElNQUdFX1NUQVRFX1BPUlQpIHByaXZhdGUgc3RhdGU6IEltYWdlU3RhdGVQb3J0LFxuICAgIEBJbmplY3QoVklTVUFMSVpFUikgcHVibGljIHBsb3RTZXJ2aWNlOiBJVmlzdWFsaXplcixcbiAgICBwdWJsaWMgbWVzc2FnZVNlcnZpY2U6IE1lc3NhZ2VTZXJ2aWNlLFxuICAgIHByaXZhdGUgbmdab25lOiBOZ1pvbmUsXG4gICAgcHJpdmF0ZSBjZHI6IENoYW5nZURldGVjdG9yUmVmLFxuICAgIHByaXZhdGUgc2Vzc2lvbjogVmlzdWFsaXplclN0b3JlLFxuICAgIHByaXZhdGUgc2FtVG9vbDogU2FtVG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBjZWxsU2VnbWVudFRvb2w6IENlbGxTZWdtZW50VG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBzYW1Qb2ludFRvb2w6IFNhbVBvaW50VG9vbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSByZWdpb25PcHM6IFJlZ2lvbk9wc1NlcnZpY2UsXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChWSVpfQ09ORklHKSBwcml2YXRlIHZpekNvbmZpZz86IFZpekNvbmZpZyxcbiAgICAvLyBBIGNvbnN0cnVjdG9yIHBhcmFtZXRlciByYXRoZXIgdGhhbiBhIGZpZWxkIGBpbmplY3QoKWA6IHRoaXMgY29tcG9uZW50IGlzXG4gICAgLy8gYWxzbyBjb25zdHJ1Y3RlZCBkaXJlY3RseSB3aXRoIGBuZXdgIGluIGl0cyBvd24gc3BlY3MsIHdoaWNoIGlzIG91dHNpZGVcbiAgICAvLyBhbiBpbmplY3Rpb24gY29udGV4dCBhbmQgd291bGQgdGhyb3cgTkcwMjAzIG9uIGEgZmllbGQgaW5pdGlhbGl6ZXIuXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChUT09MQkFSX1RPT0xTKSB0b29sQ29udHJpYnV0aW9ucz86IHJlYWRvbmx5IFRvb2xiYXJDb250cmlidXRpb25bXSxcbiAgICAvLyBPcHRpb25hbCBsaWtlIHRoZSByZXN0OiBhIGhvc3QgdGhhdCBzaG93cyBvbmx5IGltYWdlcyBuZXZlciBwcm92aWRlcyBpdCxcbiAgICAvLyBhbmQgdGhlIHNwYXRpYWwgcGxvdCB0eXBlcyBzaW1wbHkgc3RheSBoaWRkZW4uXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChTUEFUSUFMX0RBVEFfUE9SVCkgcHJpdmF0ZSBzcGF0aWFsRGF0YT86IFNwYXRpYWxEYXRhUG9ydCxcbiAgICAvLyBDb250cmlidXRlZCBwbG90IG1vZGVzLiBMaWtlIFRPT0xCQVJfVE9PTFMgdGhlIHRva2VuIGhhcyBubyBmYWN0b3J5LCBzbyBhXG4gICAgLy8gaG9zdCB0aGF0IHByb3ZpZGVzIG5vbmUgZ2V0cyBudWxsIGhlcmUgYW5kIHRoZSBzZWxlY3RvciBpcyB1bmNoYW5nZWQuXG4gICAgQE9wdGlvbmFsKCkgQEluamVjdChQTE9UX1RZUEVfQ09OVFJJQlVUSU9OUykgcGxvdFR5cGVDb250cmlidXRpb25zPzogcmVhZG9ubHkgUGxvdFR5cGVDb250cmlidXRpb25bXSxcbiAgICAvLyBQYXJlbnQgb2YgYSBjb250cmlidXRlZCBwYW5lbCBjb21wb25lbnQncyBpbmplY3Rvci4gT3B0aW9uYWwgb25seSBzbyB0aGVcbiAgICAvLyBzcGVjcyBjYW4gY29uc3RydWN0IHRoaXMgY29tcG9uZW50IHdpdGggYG5ld2AuXG4gICAgQE9wdGlvbmFsKCkgcHJpdmF0ZSBpbmplY3Rvcj86IEluamVjdG9yLFxuICApIHtcbiAgICB0aGlzLnBsb3RNb2RlcyA9IG5ldyBQbG90TW9kZUNvbnRyb2xsZXIocGxvdFR5cGVDb250cmlidXRpb25zLCB7XG4gICAgICBvbkFjdGl2YXRlZDogKGFjdGl2ZSkgPT4gdGhpcy5zaG93UGxvdE1vZGVQYW5lbChhY3RpdmUpLFxuICAgICAgb25EZWFjdGl2YXRpbmc6ICgpID0+IHRoaXMuaGlkZVBsb3RNb2RlUGFuZWwoKSxcbiAgICAgIG9uRmFpbGVkOiAoY29udHJpYnV0aW9uKSA9PiB0aGlzLmZhbGxCYWNrRnJvbVBsb3RNb2RlKGNvbnRyaWJ1dGlvbiksXG4gICAgfSk7XG4gICAgdGhpcy5jb250cmlidXRlZFRvb2xzID0gdmlzaWJsZVRvb2xDb250cmlidXRpb25zKHRvb2xDb250cmlidXRpb25zKTtcbiAgICB0aGlzLmRpYWxvZ1Rvb2xzID0gZGlhbG9nVG9vbENvbnRyaWJ1dGlvbnModG9vbENvbnRyaWJ1dGlvbnMpO1xuICAgIC8vIEEgZGlhbG9nIHRvb2wncyBzZXNzaW9uIGlzIHJ1biBieSB0aGUgcGxvdC1tb2RlIGxpZmVjeWNsZSwgYWRhcHRlZDogaXRzIFwiYmFzZVxuICAgIC8vIHR5cGVcIiBpcyB0aGUgSW1hZ2UgdmlldyBpdCBkcmF3cyBvdmVyLiBJdHMgYm9keSBpcyBOT1QgYSBjb250cm9sbGVyIHBhbmVsOiB0aGVcbiAgICAvLyBjb250cm9sbGVyIHdvdWxkIG1vdW50IGludG8gYSBkZXRhY2hlZCBob3N0LCBhbmQgYSBib2R5IG11c3QgYmUgYWJsZSB0byBtZWFzdXJlXG4gICAgLy8gaXRzZWxmLiBUaGUgZGlhbG9nIGlzIHJlbmRlcmVkIGZpcnN0IGFuZCB0aGUgYm9keSBtb3VudGVkIG9uY2UgaXRzIGhvc3QgaXMgaW4gdGhlXG4gICAgLy8gZG9jdW1lbnQgKHNob3dUb29sRGlhbG9nKS5cbiAgICB0aGlzLnRvb2xEaWFsb2dzID0gbmV3IFBsb3RNb2RlQ29udHJvbGxlcihcbiAgICAgIHRoaXMuZGlhbG9nVG9vbHMubWFwKCh0KTogUGxvdFR5cGVDb250cmlidXRpb24gPT4gKHtcbiAgICAgICAgZGVzY3JpcHRvcjogeyB0eXBlOiB0LmlkLCBsYWJlbDogdC5sYWJlbCwgZGltZW5zaW9uczogJzJkJywgYmFzZVR5cGU6IFBsb3RUeXBlLklNQUdFIH0sXG4gICAgICAgIGFjdGl2YXRlOiAoY3R4KSA9PiB0LmFjdGl2YXRlKGN0eCBhcyBUb29sRGlhbG9nQ29udGV4dCksXG4gICAgICB9KSksXG4gICAgICB7XG4gICAgICAgIG9uQWN0aXZhdGVkOiAoYWN0aXZlKSA9PiB0aGlzLnNob3dUb29sRGlhbG9nKGFjdGl2ZSksXG4gICAgICAgIG9uRGVhY3RpdmF0aW5nOiAoKSA9PiB0aGlzLmhpZGVUb29sRGlhbG9nKCksXG4gICAgICAgIG9uRmFpbGVkOiAoY29udHJpYnV0aW9uKSA9PiB0aGlzLmRpYWxvZ1Rvb2xGYWlsZWQoY29udHJpYnV0aW9uLmRlc2NyaXB0b3IudHlwZSksXG4gICAgICB9LFxuICAgICk7XG4gICAgdGhpcy5jb2xvcm1hcHNPcHRpb25zID0gcGxvdFNlcnZpY2UuZ2V0Q29sb3JtYXBPcHRpb25zKCk7XG4gICAgdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCk7XG4gIH1cblxuICAvKipcbiAgICogUGxvdCB0eXBlcyBvZmZlcmVkIGluIHRoZSBzZWxlY3RvciBmb3IgdGhlIGN1cnJlbnQgaW1hZ2U6XG4gICAqICAtIE91dHNpZGUgKip0ZXN0IG1vZGUqKiwgb25seSB0aGUgY3VyYXRlZCBkZWZhdWx0IHNldCAoZGVzY3JpcHRvcnMgd2l0aCBhXG4gICAqICAgIGBwcm9kdWN0aW9uTGFiZWxgKSBpcyBvZmZlcmVkLCBzaG93biB1bmRlciBzdWZmaXgtZnJlZSBuYW1lcyDigJQgSW1hZ2UsXG4gICAqICAgIEhlYXRtYXAsIENvbnRvdXIgYW5kIHRoZSBuYXBhcmkgU3VyZmFjZSAvIFZvbHVtZSAvIElzb3N1cmZhY2UuIFRlc3QgbW9kZVxuICAgKiAgICBleHBvc2VzIGV2ZXJ5IGJhY2tlbmQncyB0eXBlIHVuZGVyIGl0cyBmdWxsIChiYWNrZW5kLXN1ZmZpeGVkKSBsYWJlbC5cbiAgICogIC0gM0QgdHlwZXMgaGlkZGVuIHdoZW4gdGhlIGJhY2tlbmQgY2FuJ3QgcmVuZGVyIGEgM0Qgc2NlbmUuXG4gICAqICAtIHN0YWNrLW9ubHkgdHlwZXMgKHZvbHVtZSwgaXNvc3VyZmFjZSkgaGlkZGVuIHVubGVzcyB0aGUgZmlsZSBpcyBhIHN0YWNrIOKAlFxuICAgKiAgICBhIHZvbHVtZSBuZWVkcyBtdWx0aXBsZSB6LXNsaWNlcy5cbiAgICogIC0gc2NhbGFyLWludGVuc2l0eSB0eXBlcyAoY29udG91ciwgc3VyZmFjZSwgaXNvc3VyZmFjZSkgaGlkZGVuIGZvciBSR0JcbiAgICogICAgaW1hZ2VzIOKAlCB0aGV5IG1hcCBhIHNpbmdsZSBpbnRlbnNpdHkgcGVyIHBpeGVsLiBJbWFnZSBhbmQgSGVhdG1hcCByZW5kZXJcbiAgICogICAgYW55IGltYWdlLlxuICAgKiAgLSBzcGF0aWFsLW9taWNzIHR5cGVzIGhpZGRlbiB1bnRpbCBhIGBTcGF0aWFsRGF0YXNldGAgaXMgcHVibGlzaGVkIG9uXG4gICAqICAgIGBTUEFUSUFMX0RBVEFfUE9SVGAg4oCUIHRoZSBtb2RlIGhhcyBub3RoaW5nIHRvIGRyYXcgd2l0aG91dCBvYnNlcnZhdGlvbnMsXG4gICAqICAgIGV4YWN0bHkgYXMgYSB2b2x1bWUgaGFzIG5vdGhpbmcgdG8gZHJhdyB3aXRob3V0IGEgc3RhY2suXG4gICAqL1xuICAvKipcbiAgICogVHJhY2sgd2hldGhlciBhIHNwYXRpYWwtb21pY3MgZGF0YXNldCBpcyBhdmFpbGFibGUuIEEgZGF0YXNldCBhcHBlYXJpbmcgKG9yXG4gICAqIGJlaW5nIGNsZWFyZWQpIGNoYW5nZXMgd2hpY2ggcGxvdCB0eXBlcyBtYWtlIHNlbnNlLCBzbyBpdCBkcml2ZXMgdGhlIHNhbWVcbiAgICogcmVjb21wdXRlICsgcmVjb25jaWxlIHRoYXQgdGhlIGltYWdlIHN0cmVhbSBhbmQgdGhlIHRlc3QtbW9kZSB0b2dnbGUgZG8uXG4gICAqXG4gICAqIE5vLW9wIHdoZW4gdGhlIGhvc3QgcHJvdmlkZXMgbm8gYFNQQVRJQUxfREFUQV9QT1JUYCDigJQgdGhlIHNwYXRpYWwgdHlwZXMgdGhlblxuICAgKiBzdGF5IGhpZGRlbiBmb3IgdGhlIGxpZmUgb2YgdGhlIGNvbXBvbmVudC5cbiAgICovXG4gIHByaXZhdGUgd2F0Y2hTcGF0aWFsRGF0YXNldCgpOiB2b2lkIHtcbiAgICB0aGlzLnNwYXRpYWxEYXRhc2V0U3Vic2NyaXB0aW9uID0gdGhpcy5zcGF0aWFsRGF0YT8uZ2V0RGF0YXNldCQoKS5zdWJzY3JpYmUoKGRhdGFzZXQpID0+IHtcbiAgICAgIHRoaXMuc3BhdGlhbERhdGFzZXQgPSBkYXRhc2V0ID8/IG51bGw7XG4gICAgICBjb25zdCBoYXMgPSAhIWRhdGFzZXQ7XG4gICAgICAvLyBPbmx5IGEgZGF0YXNldCB3aG9zZSBvYnNlcnZhdGlvbnMgY2FycnkgYSB6IGNhbiBiZSBkcmF3biBhcyBhIGNsb3VkLCBzb1xuICAgICAgLy8gdGhlIDNEIG1vZGUgaXMgZ2F0ZWQgb24gdGhlIGNvb3JkaW5hdGVzLCBub3QgbWVyZWx5IG9uIGEgZGF0YXNldCBiZWluZ1xuICAgICAgLy8gcHJlc2VudC4gTW9zdCBzcGF0aWFsIGFzc2F5cyBhcmUgb25lIHBsYW5lLlxuICAgICAgY29uc3QgaGFzM2QgPSAhIWRhdGFzZXQ/Lm9ic2VydmF0aW9ucy56O1xuICAgICAgY29uc3QgaGFzVm9sdW1lID0gISFkYXRhc2V0Py52b2x1bWU7XG4gICAgICAvLyBUaGUgcG9ydCBwdWJsaXNoZXMgaXRzIGN1cnJlbnQgdmFsdWUgb24gc3Vic2NyaWJlLCBzbyB0aGUgaW5pdGlhbCBgbnVsbGBcbiAgICAgIC8vIHdvdWxkIG90aGVyd2lzZSByZWNvbXB1dGUgdGhlIHNlbGVjdG9yIGZvciBubyBjaGFuZ2UuIEtleWVkIGJ5IGRhdGFzZXRcbiAgICAgIC8vIElERU5USVRZIGFzIHdlbGwgYXMgY2FwYWJpbGl0eSBzaGFwZTogdHdvIDNEIGRhdGFzZXRzIHRoYXQgYm90aCBjYXJyeSBhXG4gICAgICAvLyB2b2x1bWUgaGF2ZSB0aGUgc2FtZSBzaGFwZSwgYW5kIGNvbXBhcmluZyBvbmx5IHRoYXQgc2tpcHBlZCB0aGUgc3dpdGNoIOKAlFxuICAgICAgLy8gbGVhdmluZyB0aGUgcHJldmlvdXMgZGF0YXNldCdzIHZvbHVtZSBpbWFnZSBvbiBzY3JlZW4gdW5kZXJuZWF0aCB0aGUgbmV3XG4gICAgICAvLyBvbmUncyBvYnNlcnZhdGlvbnMuXG4gICAgICBjb25zdCBrZXkgPSBkYXRhc2V0XG4gICAgICAgID8gYCR7ZGF0YXNldC5pZH18JHtoYXMzZH18JHtoYXNWb2x1bWV9fCR7ZGF0YXNldC52b2x1bWVcbiAgICAgICAgICA/IGAke2RhdGFzZXQudm9sdW1lLndpZHRofXgke2RhdGFzZXQudm9sdW1lLmhlaWdodH14JHtkYXRhc2V0LnZvbHVtZS5kZXB0aH1gIDogJyd9YFxuICAgICAgICA6IG51bGw7XG4gICAgICBpZiAoa2V5ID09PSB0aGlzLnNwYXRpYWxEYXRhc2V0S2V5KSByZXR1cm47XG4gICAgICB0aGlzLnNwYXRpYWxEYXRhc2V0S2V5ID0ga2V5O1xuICAgICAgdGhpcy5oYXNTcGF0aWFsRGF0YXNldCA9IGhhcztcbiAgICAgIHRoaXMuaGFzU3BhdGlhbDNkRGF0YXNldCA9IGhhczNkO1xuICAgICAgLy8gV2hldGhlciB0aGUgZGF0YXNldCBzYXlzIGl0IHJlZ2lzdGVycyBvbnRvIGEgdGlzc3VlIGltYWdlLiBEaXN0aW5jdCBmcm9tIGFuXG4gICAgICAvLyBpbWFnZSBiZWluZyBMT0FERUQ6IGEgcmVnaXN0ZXJlZCBkYXRhc2V0J3MgaW1hZ2UgbWF5IHN0aWxsIGJlIG9uIGl0cyB3YXksIGFuZFxuICAgICAgLy8gdGhlIHBpeGVsIG1vZGVzIG11c3Qgbm90IGZsaWNrZXIgb3V0IG9mIHRoZSBzZWxlY3RvciB3aGlsZSBpdCBhcnJpdmVzLlxuICAgICAgLy8gQSByZWdpc3RlcmVkIGRhdGFzZXQgZHJhd3Mgb3ZlciBhIHRpc3N1ZSBpbWFnZTsgYSB2b2x1bWUtYmFja2VkIG9uZSBwdWJsaXNoZXMgaXRzXG4gICAgICAvLyB2b2x1bWUgQVMgYSBncmF5c2NhbGUgei1zdGFjayBpbWFnZSAoYGJ1aWxkVm9sdW1lU3RhY2tJbWFnZWApLiBFaXRoZXIgd2F5IHBpeGVsc1xuICAgICAgLy8gZXhpc3QsIHNvIHRoZSBwaXhlbCBtb2RlcyBzdGF5IG9uIG9mZmVyIOKAlCBWb2x1bWUgYW5kIElzb3N1cmZhY2UgYXJlIGV4YWN0bHkgaG93XG4gICAgICAvLyBhIDNEIG9taWNzIGRhdGFzZXQgaXMgcmVhZC5cbiAgICAgIHRoaXMuc3BhdGlhbERhdGFzZXRIYXNQaXhlbHMgPSAhIWRhdGFzZXQ/LmltYWdlUmVmIHx8IGhhc1ZvbHVtZTtcbiAgICAgIHRoaXMuaGFzU3BhdGlhbFZvbHVtZSA9IGhhc1ZvbHVtZTtcbiAgICAgIHRoaXMuY29tcHV0ZVBsb3RUeXBlT3B0aW9ucygpO1xuICAgICAgLy8gQSBkYXRhc2V0IHdpdGggbm8gcmVmZXJlbmNlIGltYWdlIGhhcyBub3RoaW5nIHRvIGRyYXcgb2JzZXJ2YXRpb25zIE9WRVI6XG4gICAgICAvLyBhIGNsb3VkIHJlZ2lzdGVyZWQgaW50byBhIGNvbW1vbiBmcmFtZSAodGhlIEFsbGVuIENDRikgaGFzIGNvb3JkaW5hdGVzXG4gICAgICAvLyBidXQgbm8gb25lIHNlY3Rpb24uIFdoYXQgaXQgZG9lcyBoYXZlIGlzIGl0cyByZWdpc3RlcmVkIFZPTFVNRSwgd2hpY2ggaXNcbiAgICAgIC8vIGEgM0QgaW1hZ2UgaW4gb25lIGZpbGUg4oCUIHNvIG1ha2UgdGhhdCB0aGUgaW1hZ2UgYW5kIG9wZW4gb24gaXQsIHNsaWNlIGJhclxuICAgICAgLy8gYW5kIGFsbCwgcmF0aGVyIHRoYW4gbGVhdmluZyB0aGUgaG9zdCBvbiBhbiBJbWFnZSB2aWV3IHNob3dpbmcgd2hhdGV2ZXJcbiAgICAgIC8vIHNsaWRlIHdhcyBsb2FkZWQgYmVmb3JlLiBPcmRlcmVkIGFmdGVyIGNvbXB1dGVQbG90VHlwZU9wdGlvbnMgc28gdGhlIHR5cGVcbiAgICAgIC8vIGlzIG9uIG9mZmVyIGJlZm9yZSBpdCBpcyBzZWxlY3RlZC5cbiAgICAgIGlmIChkYXRhc2V0ICYmICFkYXRhc2V0LmltYWdlUmVmICYmIGhhc1ZvbHVtZSkge1xuICAgICAgICB2b2lkIHRoaXMuc2hvd1ZvbHVtZUFzSW1hZ2UoZGF0YXNldCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBBbnl0aGluZyBlbHNlIOKAlCBhIGRhdGFzZXQgd2l0aCBpdHMgb3duIHNlY3Rpb24gaW1hZ2UsIGEgdm9sdW1lLWxlc3NcbiAgICAgICAgLy8gY2xvdWQsIG9yIG5vbmUgYXQgYWxsIOKAlCBtZWFucyBhIHB1Ymxpc2hlZCB2b2x1bWUgaW1hZ2UgaXMgbm8gbG9uZ2VyIHdoYXRcbiAgICAgICAgLy8gaXMgb24gc2NyZWVuLiBGb3JnZXQgaXQsIG9yIGNvbWluZyBCQUNLIHRvIHRoZSB2b2x1bWUgZGF0YXNldCB3b3VsZFxuICAgICAgICAvLyBzaG9ydC1jaXJjdWl0IG9uIGEgbWF0Y2hpbmcga2V5IGFuZCBsZWF2ZSB0aGUgb3RoZXIgZGF0YXNldCdzIHNsaWRlIHVwLlxuICAgICAgICB0aGlzLmRyb3BWb2x1bWVJbWFnZSgpO1xuICAgICAgICBpZiAoZGF0YXNldCAmJiAhZGF0YXNldC5pbWFnZVJlZikge1xuICAgICAgICAgIC8vIE5vIHJlZmVyZW5jZSBpbWFnZSBhbmQgbm8gdm9sdW1lIHRvIHNsaWNlOiB0aGUgb2JzZXJ2YXRpb25zIGFyZSB0aGUgb25seVxuICAgICAgICAgIC8vIHRoaW5nIHRoZXJlIGlzIHRvIGRyYXcsIHNvIG9wZW4gb24gd2hpY2hldmVyIHNwYXRpYWwgbW9kZSB0aGVpclxuICAgICAgICAgIC8vIGNvb3JkaW5hdGVzIHN1cHBvcnQuIExlYXZpbmcgdGhlIHR5cGUgYWxvbmUgc3RyYW5kcyB0aGUgaG9zdCBvbiBhbiBJbWFnZVxuICAgICAgICAgIC8vIHZpZXcgc2hvd2luZyB3aGF0ZXZlciBzbGlkZSB3YXMgbG9hZGVkIEJFRk9SRSDigJQgdGhlIG9ic2VydmF0aW9ucyBuZXZlclxuICAgICAgICAgIC8vIGFwcGVhciwgYW5kIHRoZSBwcmV2aW91cyBkYXRhc2V0J3MgdGlzc3VlIGRvZXMsIHdoaWNoIHJlYWRzIGFzIHRoaXNcbiAgICAgICAgICAvLyBkYXRhc2V0IGZhaWxpbmcgdG8gbG9hZC5cbiAgICAgICAgICAvL1xuICAgICAgICAgIC8vIEdhdGVkIG9uIHRoZSBjb29yZGluYXRlcywgbm90IG9uIHRoZSBkYXRhc2V0IG1lcmVseSBleGlzdGluZzogYVxuICAgICAgICAgIC8vIG9uZS1wbGFuZSBhc3NheSBoYXMgbm8geiBhbmQgY2Fubm90IGJlIGEgY2xvdWQuIFRoYXQgY2FzZSBvbmx5IHR1cm5lZCB1cFxuICAgICAgICAgIC8vIHdpdGggYW4gaW1hZ2UtbGVzcyAyRCBkYXRhc2V0LCB3aGljaCBpcyB3aHkgaXQgd2VudCB1bmhhbmRsZWQg4oCUIGJlZm9yZVxuICAgICAgICAgIC8vIGl0LCBpbWFnZS1sZXNzIG1lYW50IDNELlxuICAgICAgICAgIGNvbnN0IHRhcmdldCA9IGhhczNkID8gUGxvdFR5cGUuU1BBVElBTF9PTUlDU18zRCA6IFBsb3RUeXBlLlNQQVRJQUxfT01JQ1M7XG4gICAgICAgICAgaWYgKHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkICE9PSB0YXJnZXQpIHRoaXMub25TZWxlY3RQbG90VHlwZSh0YXJnZXQpO1xuICAgICAgICAgIC8vIFNhbWUgbW9kZSwgYW5vdGhlciBpbWFnZS1sZXNzIGRhdGFzZXQsIG5vdGhpbmcgbG9hZGVkOiByZS1wbG90IHNvIGl0cyBwbGFjZWhvbGRlclxuICAgICAgICAgIC8vIGltYWdlIGluZm8gKGFuZCB3aXRoIGl0IHRoZSByZWdpb25zJyBrZXkpIGlzIHRoaXMgZGF0YXNldCdzLCBub3QgdGhlIGxhc3Qgb25lJ3MuXG4gICAgICAgICAgZWxzZSBpZiAoIXRoaXMuaW1hZ2VJbmZvKSB0aGlzLnJlbG9hZEFuZFBsb3QoKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy8gQ2xlYXJpbmcgdGhlIGRhdGFzZXQgd2hpbGUgYSBzcGF0aWFsIG1vZGUgaXMgYWN0aXZlIGxlYXZlcyBhIHR5cGUgdGhhdCBpc1xuICAgICAgLy8gbm8gbG9uZ2VyIG9mZmVyZWQg4oCUIGZhbGwgYmFjayB0byBJbWFnZSwgYXMgdHVybmluZyB0ZXN0IG1vZGUgb2ZmIGRvZXMuXG4gICAgICB0aGlzLnJlY29uY2lsZVNlbGVjdGVkUGxvdFR5cGUoKTtcbiAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQdWJsaXNoIGEgZGF0YXNldCdzIHJlZmVyZW5jZSB2b2x1bWUgQVMgdGhlIGltYWdlLCBhbmQgb3BlbiB0aGUgMkQgSW1hZ2Ugdmlld1xuICAgKiBvbiBpdC5cbiAgICpcbiAgICogVGhlIHZvbHVtZSBpcyBhIDNEIGltYWdlIGRlbGl2ZXJlZCBpbiBvbmUgZmlsZSwgc28gZm9yIGEgZGF0YXNldCB0aGF0IGhhcyBub1xuICAgKiBzZWN0aW9uIGltYWdlIGl0IGlzIHRoZSBob25lc3QgdGhpbmcgdG8gcHV0IG9uIHNjcmVlbjogdGhlIHNsaWNlIGJhciBzY3J1YnMgeixcbiAgICogYW5kIHRoZSBjb250cmFzdCB3aW5kb3csIGNvbG9ybWFwcyBhbmQgcmVnaW9uIHRvb2xzIGFsbCB3b3JrIGJlY2F1c2UgdGhlXG4gICAqIHZvbHVtZSBnZW51aW5lbHkgaXMgdGhlIGltYWdlIG5vdy4gVGhlIDNEIGNsb3VkIHN0YXlzIG9uZSBtZW51IHBpY2sgYXdheS5cbiAgICpcbiAgICogS2V5ZWQgYnkgZGF0YXNldCArIGdlb21ldHJ5OiB0aGUgZGF0YXNldCBzdHJlYW0gcmUtZW1pdHMgb24gdGhpbmdzIGxpa2UgYVxuICAgKiBjb2xvdXItY29sdW1uIGNoYW5nZSwgYW5kIHJlYnVpbGRpbmcgdGhlbiB3b3VsZCByZS1mZXRjaCB0aGUgdm94ZWxzIGFuZCB0aHJvd1xuICAgKiB0aGUgdXNlciBiYWNrIHRvIHRoZSBtaWRkbGUgc2xpY2UuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHNob3dWb2x1bWVBc0ltYWdlKGRhdGFzZXQ6IFNwYXRpYWxEYXRhc2V0KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbWV0YSA9IGRhdGFzZXQudm9sdW1lO1xuICAgIGlmICghbWV0YSB8fCAhdGhpcy5zcGF0aWFsRGF0YT8uZ2V0Vm9sdW1lKSByZXR1cm47XG4gICAgY29uc3Qga2V5ID0gYCR7ZGF0YXNldC5pZH06JHttZXRhLndpZHRofXgke21ldGEuaGVpZ2h0fXgke21ldGEuZGVwdGh9YDtcbiAgICBpZiAoa2V5ID09PSB0aGlzLnZvbHVtZUltYWdlS2V5KSByZXR1cm47XG4gICAgLy8gQ2xhaW0gdGhlIGtleSBCRUZPUkUgYXdhaXRpbmc6IHRoZSBzdHJlYW0gY2FuIGVtaXQgYWdhaW4gd2hpbGUgdGhlIHZveGVsc1xuICAgIC8vIGFyZSBpbiBmbGlnaHQsIGFuZCB0d28gYnVpbGRzIG9mIHRoZSBzYW1lIHZvbHVtZSB3b3VsZCByYWNlIHRvIHB1Ymxpc2guXG4gICAgdGhpcy52b2x1bWVJbWFnZUtleSA9IGtleTtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyh0cnVlKTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgYnVpbHQgPSBhd2FpdCBidWlsZFZvbHVtZVN0YWNrSW1hZ2UoZGF0YXNldCwgYXdhaXQgdGhpcy5zcGF0aWFsRGF0YS5nZXRWb2x1bWUoKSk7XG4gICAgICAvLyBBIGRpZmZlcmVudCBkYXRhc2V0IHdhcyBzZWxlY3RlZCB3aGlsZSB0aGlzIG9uZSBlbmNvZGVkIOKAlCBpdHMgaW1hZ2UgaXMgdGhlXG4gICAgICAvLyBvbmUgdGhhdCBiZWxvbmdzIG9uIHNjcmVlbiwgc28gZHJvcCB3aGF0IHdlIGp1c3QgYnVpbHQuXG4gICAgICBpZiAodGhpcy52b2x1bWVJbWFnZUtleSAhPT0ga2V5KSB7XG4gICAgICAgIGJ1aWx0Py51cmxzLmZvckVhY2goKHUpID0+IFVSTC5yZXZva2VPYmplY3RVUkwodSkpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAoIWJ1aWx0KSB7XG4gICAgICAgIHRoaXMudm9sdW1lSW1hZ2VLZXkgPSBudWxsO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBQaWNrIHRoZSBtb2RlIGZpcnN0IHNvIHRoZSBpbWFnZSBsYW5kcyBpbiB0aGUgdmlldyB0aGF0IHdpbGwgc2hvdyBpdCxcbiAgICAgIC8vIGluc3RlYWQgb2YgcmVuZGVyaW5nIG9uY2UgaW50byB3aGF0ZXZlciBtb2RlIHRoZSBsYXN0IGRhdGFzZXQgbGVmdCBhY3RpdmUuXG4gICAgICBpZiAodGhpcy5zZWxlY3RlZFBsb3RUeXBlSWQgIT09IFBsb3RUeXBlLklNQUdFKSB0aGlzLm9uU2VsZWN0UGxvdFR5cGUoUGxvdFR5cGUuSU1BR0UpO1xuICAgICAgdGhpcy5yZXZva2VWb2x1bWVJbWFnZVVybHMoKTtcbiAgICAgIHRoaXMudm9sdW1lSW1hZ2VVcmxzID0gYnVpbHQudXJscztcbiAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKGJ1aWx0LmluZm8pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8gTm8gdm9sdW1lIHNlcnZlZCBhZnRlciBhbGw6IHRoZSBjbG91ZCBpcyBzdGlsbCByZW5kZXJhYmxlLCBzbyBmYWxsIGJhY2sgdG9cbiAgICAgIC8vIGl0IHJhdGhlciB0aGFuIGxlYXZpbmcgdGhlIGhvc3Qgb24gYW4gSW1hZ2UgdmlldyB3aXRoIG5vdGhpbmcgaW4gaXQuXG4gICAgICBjb25zb2xlLndhcm4oJ1t2aXN1YWxpemVyXSByZWZlcmVuY2Ugdm9sdW1lIHVuYXZhaWxhYmxlIOKAlCBmYWxsaW5nIGJhY2sgdG8gdGhlIDNEIGNsb3VkJywgZXJyKTtcbiAgICAgIHRoaXMudm9sdW1lSW1hZ2VLZXkgPSBudWxsO1xuICAgICAgaWYgKHRoaXMuaGFzU3BhdGlhbDNkRGF0YXNldCAmJiAhaXNTcGF0aWFsT21pY3MzZCh0aGlzLmJhc2VQbG90VHlwZSkpIHtcbiAgICAgICAgdGhpcy5vblNlbGVjdFBsb3RUeXBlKFBsb3RUeXBlLlNQQVRJQUxfT01JQ1NfM0QpO1xuICAgICAgfVxuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyhmYWxzZSk7XG4gICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIEZvcmdldCB0aGUgcHVibGlzaGVkIHZvbHVtZSBpbWFnZSBzbyByZS1zZWxlY3RpbmcgdGhlIGRhdGFzZXQgcmVidWlsZHMgaXQuXG4gICAqICBUaGUgVVJMcyBkZWxpYmVyYXRlbHkgc3RheSBhbGl2ZTogdGhlIGhvc3QgbWF5IHN0aWxsIGJlIGRpc3BsYXlpbmcgdGhhdCBpbWFnZVxuICAgKiAgYXQgdGhpcyBpbnN0YW50LCBhbmQgcmV2b2tpbmcgdW5kZXIgaXQgd291bGQgYnJlYWsgZXZlcnkgbGF0ZXIgdGlsZSByZWFkLiBUaGV5XG4gICAqICBhcmUgZnJlZWQgd2hlbiB0aGUgbmV4dCB2b2x1bWUgcmVwbGFjZXMgdGhlbSwgb3Igb24gZGVzdHJveS4gKi9cbiAgcHJpdmF0ZSBkcm9wVm9sdW1lSW1hZ2UoKTogdm9pZCB7XG4gICAgdGhpcy52b2x1bWVJbWFnZUtleSA9IG51bGw7XG4gIH1cblxuICBwcml2YXRlIHJldm9rZVZvbHVtZUltYWdlVXJscygpOiB2b2lkIHtcbiAgICB0aGlzLnZvbHVtZUltYWdlVXJscy5mb3JFYWNoKCh1KSA9PiBVUkwucmV2b2tlT2JqZWN0VVJMKHUpKTtcbiAgICB0aGlzLnZvbHVtZUltYWdlVXJscyA9IFtdO1xuICB9XG5cbiAgcHJpdmF0ZSBjb21wdXRlUGxvdFR5cGVPcHRpb25zKCkge1xuICAgIGNvbnN0IGNhcHMgPSB0aGlzLnBsb3RTZXJ2aWNlLmNhcGFiaWxpdGllcztcbiAgICBjb25zdCBpc1N0YWNrID0gISF0aGlzLmltYWdlSW5mbz8uaXNTdGFjaztcbiAgICBjb25zdCBpc0dyYXlzY2FsZSA9ICEhdGhpcy5pbWFnZUluZm8/LmlzR3JheXNjYWxlO1xuICAgIC8vIEEgbXVsdGljaGFubmVsIGltYWdlJ3MgYmFuZHMgYXJlIGVhY2ggc2luZ2xlLWJhbmQgKHNjYWxhciksIHNvIHRoZSBzY2FsYXJcbiAgICAvLyBwbG90IG1vZGVzIChIZWF0bWFwIC8gU3VyZmFjZSAvIFZvbHVtZSAvIElzb3N1cmZhY2UpIGFwcGx5IHBlciBjaGFubmVsIGV2ZW5cbiAgICAvLyB0aG91Z2ggdGhlIFJHQiBjb21wb3NpdGUgaXNuJ3QgZ3JheXNjYWxlIOKAlCBkb24ndCBnYXRlIHRoZW0gb3V0LlxuICAgIGNvbnN0IG0wID0gdGhpcy5pbWFnZUluZm8/LmltYWdlTWV0YT8uWzBdO1xuICAgIGNvbnN0IGlzTXVsdGljaGFubmVsID0gKG0wPy5jaGFubmVsQ291bnQgPz8gMSkgPiAxICYmIChtMD8ucmdiQ2hhbm5lbHMgPz8gMSkgPCAzO1xuICAgIGNvbnN0IHBhc3Nlc0dhdGVzID0gKGQ6IFBsb3RUeXBlRGVzY3JpcHRvcik6IGJvb2xlYW4gPT4ge1xuICAgICAgaWYgKGQuZGltZW5zaW9ucyA9PT0gJzNkJyAmJiAhY2Fwcy5oYXMoVmlld2VyRmVhdHVyZS5TdXJmYWNlM0QpKSByZXR1cm4gZmFsc2U7XG4gICAgICAvLyBWb2x1bWUgYW5kIElzb3N1cmZhY2UgcmF5bWFyY2ggdGhlIElNQUdFIFNUQUNLLCBhbmQgbm90aGluZyBlbHNlOiB0aGVzZVxuICAgICAgLy8gdHdvIGdhdGVzIGFyZSBhYm91dCB0aGUgbG9hZGVkIGltYWdlLCBmdWxsIHN0b3AuIEEgM0Qgb21pY3MgZGF0YXNldFxuICAgICAgLy8gcmVhY2hlcyB0aGVtIGJlY2F1c2UgaXRzIHJlZ2lzdGVyZWQgdm9sdW1lIGlzIHB1Ymxpc2hlZCBBUyBhIGdyYXlzY2FsZVxuICAgICAgLy8gei1zdGFjayBpbWFnZSAoYGJ1aWxkVm9sdW1lU3RhY2tJbWFnZWApLCBub3QgdGhyb3VnaCBhIHNlY29uZCB2b3hlbFxuICAgICAgLy8gc291cmNlIGhpZGluZyBiZWhpbmQgdGhlIHNhbWUgbW9kZXMuXG4gICAgICBpZiAoZC5yZXF1aXJlc1N0YWNrICYmICFpc1N0YWNrKSByZXR1cm4gZmFsc2U7XG4gICAgICBpZiAoZC5yZXF1aXJlc0dyYXlzY2FsZSAmJiAhaXNHcmF5c2NhbGUgJiYgIWlzTXVsdGljaGFubmVsKSByZXR1cm4gZmFsc2U7XG4gICAgICBpZiAoZC5yZXF1aXJlc1NwYXRpYWxEYXRhICYmICF0aGlzLmhhc1NwYXRpYWxEYXRhc2V0KSByZXR1cm4gZmFsc2U7XG4gICAgICBpZiAoZC5yZXF1aXJlc1NwYXRpYWwzZCAmJiAhdGhpcy5oYXNTcGF0aWFsM2REYXRhc2V0KSByZXR1cm4gZmFsc2U7XG4gICAgICAvLyBBbiBpbWFnZS1zb3VyY2VkIG1vZGUgcmVhZHMgUElYRUxTLiBBIHNwYXRpYWwgZGF0YXNldCB0aGF0IGJyaW5ncyBubyB0aXNzdWVcbiAgICAgIC8vIGltYWdlIChzZXFGSVNIIHJlY29yZHMgdW5pdGxlc3MgY29vcmRpbmF0ZXMsIG5vdCBhIHNlY3Rpb24pIGxlYXZlcyBub3RoaW5nIGZvclxuICAgICAgLy8gb25lIHRvIGRyYXcsIHNvIG9mZmVyaW5nIEltYWdlIC8gSGVhdG1hcCAvIFN1cmZhY2UgdGhlcmUgb2ZmZXJzIG1vZGVzIHRoYXQgY2FuXG4gICAgICAvLyBvbmx5IGNvbWUgdXAgYmxhbmsg4oCUIG9yIHdvcnNlLCBzaG93aW5nIHdoYXRldmVyIHNsaWRlIHdhcyBsb2FkZWQgYmVmb3JlLlxuICAgICAgLy9cbiAgICAgIC8vIE5hcnJvd2VkIHRvIFwiYSBkYXRhc2V0IGlzIHVwIEFORCB0aGVyZSBpcyBubyBpbWFnZVwiOiB3aXRoIG5vIGRhdGFzZXQgYXQgYWxsLFxuICAgICAgLy8gSW1hZ2Ugc3RheXMgb24gb2ZmZXIsIGJlY2F1c2UgYSBob3N0IHRoYXQgaGFzIG5vdCBsb2FkZWQgYW55dGhpbmcgeWV0IG5lZWRzIGFcbiAgICAgIC8vIGRlZmF1bHQgYW5kIGFuIGVtcHR5IHNlbGVjdG9yIHdvdWxkIGJlIHdvcnNlIHRoYW4gYSBibGFuayB2aWV3LlxuICAgICAgaWYgKGQuc291cmNlID09PSAnaW1hZ2UnICYmIHRoaXMuaGFzU3BhdGlhbERhdGFzZXRcbiAgICAgICAgJiYgIXRoaXMuc3BhdGlhbERhdGFzZXRIYXNQaXhlbHMpIHJldHVybiBmYWxzZTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH07XG4gICAgLy8gT3V0c2lkZSB0ZXN0IG1vZGUsIG9ubHkgdGhlIGN1cmF0ZWQgc2V0ICh0aG9zZSB3aXRoIGEgcHJvZHVjdGlvbkxhYmVsKVxuICAgIC8vIGlzIG9mZmVyZWQ7IHRlc3QgbW9kZSBleHBvc2VzIGV2ZXJ5IGJhY2tlbmQncyB0eXBlLlxuICAgIGNvbnN0IGN1cmF0ZWQgPSAoZDogeyBwcm9kdWN0aW9uTGFiZWw/OiBzdHJpbmcgfSk6IGJvb2xlYW4gPT4gdGhpcy50ZXN0TW9kZSB8fCAhIWQucHJvZHVjdGlvbkxhYmVsO1xuICAgIGNvbnN0IGJ1aWx0SW4gPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFBsb3RUeXBlRGVzY3JpcHRvcnMoKVxuICAgICAgLmZpbHRlcigoZCkgPT4gY3VyYXRlZChkKSAmJiBwYXNzZXNHYXRlcyhkKSk7XG4gICAgLy8gQ29udHJpYnV0ZWQgbW9kZXMgY29tZSBhZnRlciBldmVyeSBidWlsdC1pbiBvbmUsIHVuZGVyIHRoZSBzYW1lIGxhYmVsIHJ1bGVcbiAgICAvLyBhbmQgdGhlaXIgb3duIHN0YWNrL2dyYXlzY2FsZS9zcGF0aWFsIGdhdGVzLiBBIG1vZGUgYWxzbyBuZWVkcyB3aGF0ZXZlciBpdHMgYmFzZVxuICAgIC8vIHZpZXcgbmVlZHMgKHBpeGVscywgYSAzRC1jYXBhYmxlIGJhY2tlbmQpLCBzbyB0aGUgYmFzZSB0eXBlJ3MgZ2F0ZXMgYXBwbHlcbiAgICAvLyB0b28g4oCUIGEgY29udHJpYnV0ZWQgSW1hZ2UtYmFzZWQgbW9kZSBnb2VzIHdoZXJldmVyIEltYWdlIGdvZXMuXG4gICAgY29uc3QgY29udHJpYnV0ZWQ6IFBsb3RUeXBlT3B0aW9uW10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IGQgb2YgdGhpcy5wbG90TW9kZXMuZGVzY3JpcHRvcnMoKSkge1xuICAgICAgaWYgKCFjdXJhdGVkKGQpKSBjb250aW51ZTtcbiAgICAgIGlmIChkLnJlcXVpcmVzU3RhY2sgJiYgIWlzU3RhY2spIGNvbnRpbnVlO1xuICAgICAgaWYgKGQucmVxdWlyZXNHcmF5c2NhbGUgJiYgIWlzR3JheXNjYWxlICYmICFpc011bHRpY2hhbm5lbCkgY29udGludWU7XG4gICAgICBpZiAoZC5yZXF1aXJlc1NwYXRpYWxEYXRhICYmICF0aGlzLmhhc1NwYXRpYWxEYXRhc2V0KSBjb250aW51ZTtcbiAgICAgIGlmIChkLnJlcXVpcmVzU3BhdGlhbDNkICYmICF0aGlzLmhhc1NwYXRpYWwzZERhdGFzZXQpIGNvbnRpbnVlO1xuICAgICAgY29uc3QgYmFzZSA9IGdldFBsb3RUeXBlRGVzY3JpcHRvcihkLmJhc2VUeXBlKTtcbiAgICAgIGlmIChiYXNlICYmIHBhc3Nlc0dhdGVzKGJhc2UpKSBjb250cmlidXRlZC5wdXNoKGNvbnRyaWJ1dGVkUGxvdFR5cGVPcHRpb24oZCwgYmFzZSkpO1xuICAgIH1cbiAgICAvLyBEZWZhdWx0IHNlbGVjdG9yIHNob3dzIHRoZSBzdWZmaXgtZnJlZSBwcm9kdWN0aW9uTGFiZWw7IHRlc3QgbW9kZSBrZWVwc1xuICAgIC8vIHRoZSBmdWxsIGJhY2tlbmQtc3VmZml4ZWQgbGFiZWwgc28gc2FtZS1uYW1lZCBtb2RlcyBzdGF5IGRpc3Rpbmd1aXNoYWJsZS5cbiAgICBjb25zdCBsYWJlbGxlZCA9IDxUIGV4dGVuZHMgeyBsYWJlbDogc3RyaW5nOyBwcm9kdWN0aW9uTGFiZWw/OiBzdHJpbmcgfT4oZDogVCk6IFQgPT5cbiAgICAgICh0aGlzLnRlc3RNb2RlID8gZCA6IHsgLi4uZCwgbGFiZWw6IGQucHJvZHVjdGlvbkxhYmVsISB9KTtcbiAgICB0aGlzLnBsb3RUeXBlT3B0aW9ucyA9IGJ1aWx0SW4ubWFwKGxhYmVsbGVkKTtcbiAgICB0aGlzLnBsb3RUeXBlTWVudSA9IFsuLi50aGlzLnBsb3RUeXBlT3B0aW9ucywgLi4uY29udHJpYnV0ZWQubWFwKGxhYmVsbGVkKV07XG4gIH1cblxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgLy8gVGhlIGNvbnN0cnVjdG9yIGNvbXB1dGVzIHRoZSBvcHRpb25zIHdpdGggdGVzdE1vZGUgc3RpbGwgYXQgaXRzIGRlZmF1bHQ7XG4gICAgLy8gcmVjb21wdXRlIG9uY2UgdGhlIGhvc3QgYmluZHMgaXQgKGFuZCBvbiBhbnkgbGF0ZXIgY2hhbmdlKSBzbyB0aGUgc2VsZWN0b3JcbiAgICAvLyByZWZsZWN0cyB0ZXN0IG1vZGUgaW1tZWRpYXRlbHksIHdpdGhvdXQgd2FpdGluZyBmb3IgYW4gaW1hZ2UgdG8gKHJlKWxvYWQuXG4gICAgaWYgKGNoYW5nZXNbJ3Rlc3RNb2RlJ10pIHtcbiAgICAgIHRoaXMuY29tcHV0ZVBsb3RUeXBlT3B0aW9ucygpO1xuICAgICAgLy8gSWYgdGVzdCBtb2RlIGlzIHR1cm5lZCBvZmYgd2hpbGUgYSB0ZXN0LW9ubHkgdHlwZSAoZS5nLiBOQVBBUklfSU1BR0UpIGlzXG4gICAgICAvLyBhY3RpdmUsIHRoYXQgdHlwZSBpcyBubyBsb25nZXIgb2ZmZXJlZCDigJQgZmFsbCBiYWNrIHRvIEltYWdlLlxuICAgICAgdGhpcy5yZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIElmIHRoZSBhY3RpdmUgcGxvdCB0eXBlIGlzIG5vIGxvbmdlciBpbiB0aGUgb2ZmZXJlZCBvcHRpb25zIChlLmcuIHRlc3QgbW9kZVxuICAgKiAgdHVybmVkIG9mZiB3aGlsZSBhIHRlc3Qtb25seSB0eXBlIHdhcyBhY3RpdmUsIG9yIGEgc2NhbGFyIHR5cGUgY2FycmllZCBvbnRvXG4gICAqICBhbiBSR0IgaW1hZ2UpLCBmYWxsIGJhY2sgdG8gdGhlIGRlZmF1bHQgMkQgSW1hZ2Ugdmlldy4gKi9cbiAgLyoqIFdoZXRoZXIgdGhlIGxpdmUgc3BhdGlhbCBkYXRhc2V0IGJyaW5ncyBwaXhlbHMgb2YgaXRzIG93biDigJQgYSB0aXNzdWUgaW1hZ2UgaXRcbiAgICogIHJlZ2lzdGVycyBvbnRvLCBvciBhIHZvbHVtZSB0aGF0IGlzIHB1Ymxpc2hlZCBhcyBhIHotc3RhY2sgaW1hZ2UuICovXG4gIHByaXZhdGUgc3BhdGlhbERhdGFzZXRIYXNQaXhlbHMgPSBmYWxzZTtcblxuICAvKipcbiAgICogVGhlIGhvc3QgcHVibGlzaGVkIE5PIGltYWdlIOKAlCBhIHNwYXRpYWwgZGF0YXNldCB0aGF0IGJyaW5ncyBub25lIGRvZXMgdGhpcy5cbiAgICpcbiAgICogV2l0aG91dCBoYW5kbGluZyB0aGUgZW1wdHkgZW1pc3Npb24sIGBpbWFnZUluZm9gIGtlZXBzIHRoZSBMQVNUIGltYWdlJ3MgbWV0YWRhdGE6IHRoZVxuICAgKiBzZWxlY3RvciBnb2VzIG9uIG9mZmVyaW5nIHBpeGVsIG1vZGVzIGZvciBwaXhlbHMgdGhhdCBhcmUgZ29uZSwgYW5kIGV2ZXJ5XG4gICAqIGBpc1N0YWNrYC9gaXNHcmF5c2NhbGVgIGRlY2lzaW9uIGlzIG1hZGUgYWdhaW5zdCBhIGZpbGUgdGhhdCBpcyBubyBsb25nZXIgbG9hZGVkLlxuICAgKi9cbiAgcHJpdmF0ZSBvbkltYWdlQ2xlYXJlZCgpOiB2b2lkIHtcbiAgICAvLyBUaGUgaW1hZ2UgYSBjb250cmlidXRlZCBtb2RlIHdhcyBkcmF3aW5nIG92ZXIgaXMgZ29uZS4gQW4gb3BlbiBkaWFsb2dcbiAgICAvLyB0b29sIHN0YXlzIG9wZW4gYW5kIHJlc3RhcnRzIG9uY2UgdGhlIG5leHQgaW1hZ2UgaGFzIHBsb3R0ZWQuXG4gICAgdGhpcy5wbG90TW9kZXMuZGVhY3RpdmF0ZSgpO1xuICAgIHRoaXMudG9vbERpYWxvZ3MuZGVhY3RpdmF0ZSgpO1xuICAgIHRoaXMuaW1hZ2VJbmZvID0gdW5kZWZpbmVkO1xuICAgIHRoaXMubG9hZGVkRmlsZU5hbWUgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCk7XG4gICAgLy8gRGVsaWJlcmF0ZWx5IE5PVCByZWNvbmNpbGluZyB0aGUgc2VsZWN0ZWQgdHlwZSBoZXJlLiBSZWNvbmNpbGluZyBjYWxsc1xuICAgIC8vIGBzZXRQbG90VHlwZWAsIHdoaWNoIGRyaXZlcyBhIHJlLXBsb3Qg4oCUIGFuZCB0aGVyZSBpcyBub3RoaW5nIHRvIHBsb3QsIHNvIHRoZVxuICAgIC8vIFBsb3RseSBiYWNrZW5kIHJlYWQgaXRzIG93biBub3ctdW5zZXQgYGltYWdlSW5mb2AgYW5kIHRocmV3XG4gICAgLy8gKFwiY2FuJ3QgYWNjZXNzIHByb3BlcnR5IGlzR3JheXNjYWxlXCIpLCBhYm9ydGluZyB0aGUgbG9hZCB3aXRoIG5vIG9ic2VydmF0aW9uc1xuICAgIC8vIGRyYXduLiBJZ25vcmluZyB0aGUgZW1wdHkgZW1pc3Npb24gZW50aXJlbHkgaXMgd2hhdCB1c2VkIHRvIGF2b2lkIHRoYXQuXG4gICAgLy9cbiAgICAvLyBOb3RoaW5nIGlzIGxlZnQgc3RyYW5kZWQ6IGFuIGltYWdlIGlzIGNsZWFyZWQgYmVjYXVzZSBhIHNwYXRpYWwgZGF0YXNldCB0aGF0XG4gICAgLy8gYnJpbmdzIG5vbmUgaXMgYmVpbmcgb3BlbmVkLCBhbmQgdGhhdCBkYXRhc2V0J3Mgb3duIHN1YnNjcmlwdGlvbiBzZWxlY3RzIHRoZVxuICAgIC8vIG1vZGUgaXRzIGNvb3JkaW5hdGVzIHN1cHBvcnQgYSBtb21lbnQgbGF0ZXIuXG4gICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICB9XG5cbiAgcHJpdmF0ZSByZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnBsb3RUeXBlTWVudS5zb21lKChkKSA9PiBkLnR5cGUgPT09IHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkKSkgcmV0dXJuO1xuICAgIC8vIEltYWdlIGlzIHRoZSB1c3VhbCBmYWxsYmFjaywgYnV0IGl0IGlzIG5vdCBhbHdheXMgT04gT0ZGRVI6IHdpdGggbm8gaW1hZ2UgbG9hZGVkXG4gICAgLy8gdGhlIHBpeGVsIG1vZGVzIGFyZSBnb25lLCBhbmQgZmFsbGluZyBiYWNrIHRvIG9uZSB3b3VsZCBzZWxlY3QgYSBtb2RlIHRoZSBzZWxlY3RvclxuICAgIC8vIGRvZXMgbm90IGxpc3QgYW5kIG5vdGhpbmcgY2FuIGRyYXcuIFRha2UgdGhlIGZpcnN0IHR5cGUgc3RpbGwgb2ZmZXJlZCBpbnN0ZWFkLlxuICAgIGNvbnN0IGZhbGxiYWNrID0gdGhpcy5wbG90VHlwZU1lbnUuc29tZSgoZCkgPT4gZC50eXBlID09PSBQbG90VHlwZS5JTUFHRSlcbiAgICAgID8gUGxvdFR5cGUuSU1BR0VcbiAgICAgIDogdGhpcy5wbG90VHlwZU1lbnVbMF0/LnR5cGU7XG4gICAgaWYgKCFmYWxsYmFjaykgcmV0dXJuO1xuICAgIC8vIExlYXZpbmcgYSBjb250cmlidXRlZCBtb2RlIChpdHMgZ2F0ZXMgc3RvcHBlZCBwYXNzaW5nLCBvciBpdHMgcHJvdmlkZXIgaXNcbiAgICAvLyBnb25lKTogaXRzIHNlc3Npb24gZW5kcyBiZWZvcmUgYW55dGhpbmcgZWxzZSBkcmF3cy5cbiAgICB0aGlzLnBsb3RNb2Rlcy5kZWFjdGl2YXRlKCk7XG4gICAgY29uc3QgYmFzZSA9IHRoaXMucGxvdE1vZGVzLmJhc2VUeXBlT2YoZmFsbGJhY2spO1xuICAgIGlmIChiYXNlICE9PSBQbG90VHlwZS5JTUFHRSkgdGhpcy5jbG9zZURpYWxvZ1Rvb2woKTtcbiAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCA9IGZhbGxiYWNrO1xuICAgIHRoaXMucGxvdFR5cGUgPSBiYXNlO1xuICAgIHRoaXMuaXNIZWF0bWFwID0gYmFzZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRQbG90VHlwZShiYXNlKTtcbiAgfVxuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIC8vIEpvaW4gdGhlIGxpdmUgc2V0OyB0aGUgb2xkZXN0IG1lbWJlciByZW5kZXJzIHRoZSBzaGFyZWQgbm90aWNlIG91dGxldHMuXG4gICAgVmlzdWFsaXplckNvbXBvbmVudC5saXZlSW5zdGFuY2VzLmFkZCh0aGlzKTtcbiAgICB0aGlzLnN0YXRlLnNldERpYWdyYW0odGhpcyk7XG4gICAgdGhpcy5zZWxlY3RlZENvbG9ybWFwID1cbiAgICAgIHRoaXMuY29sb3JtYXBzT3B0aW9uc1swXS5jaGlsZHJlbi5maW5kKChjOiBhbnkpID0+IGMubGFiZWwgPT09ICdHcmV5cyBJbnYnKSA/P1xuICAgICAgdGhpcy5jb2xvcm1hcHNPcHRpb25zWzBdLmNoaWxkcmVuWzBdO1xuICAgIHRoaXMuc3RhY2tPcHRpb25zID0gW1xuICAgICAgeyBuYW1lOiAnU2luZ2xlIGltYWdlJywgdmFsOiAnZmFsc2UnIH0sXG4gICAgICB7IG5hbWU6ICdTdGFjaycsIHZhbDogJ3RydWUnIH0sXG4gICAgXTtcbiAgICB0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24gPSB0aGlzLnN0YWNrT3B0aW9uc1swXTtcbiAgICB0aGlzLndhdGNoU3BhdGlhbERhdGFzZXQoKTtcbiAgICB0aGlzLmF1dG9zY2FsZVN1YnNjcmlwdGlvbiA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0QXV0b3NjYWxlRXZlbnQoKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgLy8gcmVzZXQgdGhlIGltYWdlIG1vZGUgdG8gc2luZ2xlIGltYWdlXG4gICAgICB0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24gPSB7IG5hbWU6ICdTaW5nbGUgaW1hZ2UnLCB2YWw6ICdmYWxzZScgfTtcbiAgICAgIHRoaXMuYWN0aXZlRHJhZ01vZGUgPSBudWxsO1xuICAgICAgdGhpcy5zZXNzaW9uLnNldEFjdGl2ZVRvb2wobnVsbCk7XG4gICAgfSk7XG4gICAgdGhpcy5pbWFnZUxvYWRpbmdTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmlzSW1hZ2VMb2FkaW5nJCgpLnN1YnNjcmliZSgoaXNJbWFnZUxvYWRpbmcpID0+IHtcbiAgICAgIHRoaXMuaW1nTG9hZGluZyA9IGlzSW1hZ2VMb2FkaW5nO1xuICAgIH0pO1xuICAgIC8vIFVuZG8gYXZhaWxhYmlsaXR5IChqaXQtdWkjODUpOiB0aGUgc2hhcmVkIFJlZ2lvblN0b3JlIGVtaXRzIHdoZW5ldmVyIGl0c1xuICAgIC8vIGhpc3RvcnkgZGVwdGggY2hhbmdlczsgZ3JleXMgb3V0IHRoZSB0b29sYmFyIFVuZG8gYnV0dG9uIGFjY29yZGluZ2x5LlxuICAgIHRoaXMucGxvdFNlcnZpY2VcbiAgICAgIC5nZXRDYW5VbmRvJCgpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy51bnN1YikpXG4gICAgICAuc3Vic2NyaWJlKChjYW5VbmRvKSA9PiB7XG4gICAgICAgIHRoaXMuY2FuVW5kb1JlZ2lvbiA9IGNhblVuZG87XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pO1xuICAgIHRoaXMucGxvdFNlcnZpY2VcbiAgICAgIC5nZXRDYW5SZWRvJCgpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy51bnN1YikpXG4gICAgICAuc3Vic2NyaWJlKChjYW5SZWRvKSA9PiB7XG4gICAgICAgIHRoaXMuY2FuUmVkb1JlZ2lvbiA9IGNhblJlZG87XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pO1xuICAgIC8vIE1pcnJvciB0aGUgcmVnaW9uIHNlbGVjdGlvbiBzbyB0aGUgcmlnaHQtY2xpY2sgYWN0aW9uIGdyb3VwIChqaXQtdWkjODUpXG4gICAgLy8gY2FuIHJlYWQgaXQgc3luY2hyb25vdXNseSB3aGVuIHRoZSBtZW51IGlzIGJ1aWx0LlxuICAgIHRoaXMucGxvdFNlcnZpY2VcbiAgICAgIC5nZXRTZWxlY3RlZFNoYXBlSW5kaWNlcyQoKVxuICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMudW5zdWIpKVxuICAgICAgLnN1YnNjcmliZSgoaW5kaWNlcykgPT4ge1xuICAgICAgICB0aGlzLnNlbGVjdGVkSW5kaWNlcyA9IGluZGljZXMgfHwgW107XG4gICAgICB9KTtcbiAgICAvLyBJbnRlcmFjdGl2ZSBwb2ludC1wcm9tcHQgc2VnbWVudGF0aW9uIHJ1bnMgaW5zaWRlIHRoZSByZW5kZXJlciBvbiBlYWNoXG4gICAgLy8gY2xpY2s7IHN1cmZhY2UgaXRzIGxpdmUgc3RhdHVzICsgZG93bmxvYWQgcHJvZ3Jlc3MgaW4gdGhlIHNoYXJlZCBgc2FtYFxuICAgIC8vIHRvYXN0IHNvIHRoZSB1c2VyIHNlZXMgaXQgd29ya2luZyAodGhlIGZpcnN0IGNsaWNrIHB1bGxzIHRoZSBlbmNvZGVyKS5cbiAgICB0aGlzLnNhbVBvaW50U3ViLmFkZChcbiAgICAgIHRoaXMuc2FtUG9pbnRUb29sLnByb2dyZXNzJC5zdWJzY3JpYmUoKGYpID0+IHtcbiAgICAgICAgdGhpcy5zYW1Eb3dubG9hZGluZyA9IGYgPj0gMCAmJiBmIDwgMTtcbiAgICAgICAgaWYgKGYgPj0gMCkgdGhpcy5zYW1Qcm9ncmVzcyA9IE1hdGgubWluKDEwMCwgTWF0aC5yb3VuZChmICogMTAwKSk7XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pLFxuICAgICk7XG4gICAgdGhpcy5zYW1Qb2ludFN1Yi5hZGQoXG4gICAgICB0aGlzLnNhbVBvaW50VG9vbC5zdGF0dXMkLnN1YnNjcmliZSgobSkgPT4ge1xuICAgICAgICB0aGlzLnNhbVN0YXR1cyA9IG07XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pLFxuICAgICk7XG4gICAgdGhpcy5zYW1Qb2ludFN1Yi5hZGQoXG4gICAgICB0aGlzLnNhbVBvaW50VG9vbC5idXN5JC5zdWJzY3JpYmUoKGJ1c3kpID0+IHtcbiAgICAgICAgdGhpcy5zYW1CdXN5ID0gYnVzeTtcbiAgICAgICAgaWYgKGJ1c3kpIHRoaXMuc2hvd1NhbVRvYXN0KCdTQU0gcG9pbnQgc2VnbWVudGF0aW9uJyk7XG4gICAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIH0pLFxuICAgICk7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRTdGFja0xvYWRpbmdQcm9ncmVzcygpLnN1YnNjcmliZSgobG9hZGluZ1Byb2dyZXNzKSA9PiB7XG4gICAgICB0aGlzLmxvYWRpbmdQZXJjZW50YWdlID0gbG9hZGluZ1Byb2dyZXNzO1xuICAgIH0pO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuaXNTdGFja0xvYWRpbmcoKS5zdWJzY3JpYmUoKHN0YWNrTG9hZGluZykgPT4ge1xuICAgICAgdGhpcy5zdGFja0xvYWRpbmcgPSBzdGFja0xvYWRpbmc7XG4gICAgfSk7XG4gICAgdGhpcy5wbG90V2lkdGhTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmdldFBhbmVsV2lkdGgkKCkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVsYXlvdXQoKTtcbiAgICAgIC8vIFRoZSBpbnRlbnNpdHkgaW5zZXQgaXMgYSBzZXBhcmF0ZSBQbG90bHkgY2hhcnQgaW4gYSBmbG9hdGluZyBwYW5lbDsgcmVmbG93XG4gICAgICAvLyBpdCB0byBpdHMgY3VycmVudCBzaXplIHdoZW4gdGhlIGNhbnZhcyByZXNpemVzLCBlbHNlIGl0IGtlZXBzIHRoZSBzdGFsZVxuICAgICAgLy8gc2l6ZSBhbmQgYmxhbmtzIG91dC4gRGVmZXIgYSB0aWNrIHNvIHRoZSBwYW5lbCBsYXlvdXQgaGFzIHNldHRsZWQuXG4gICAgICBpZiAodGhpcy5oYXNQcm9maWxlcykge1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMucmVuZGVySW50ZW5zaXR5SW5zZXQoKSwgMCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5pbWdMb2FkaW5nTWVzc2FnZVN1YnNjcmlwdGlvbiA9IHRoaXMuc3RhdGUuZ2V0SW1hZ2VMb2FkaW5nTWVzc2FnZSQoKS5zdWJzY3JpYmUoKG1lc3NhZ2UpID0+IHtcbiAgICAgIHRoaXMubG9hZGluZ01lc3NhZ2UgPSBtZXNzYWdlO1xuICAgIH0pO1xuICAgIHRoaXMuc3RhdGVcbiAgICAgIC5nZXRDYWNoZVByb2dyZXNzJCgpXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy51bnN1YikpXG4gICAgICAuc3Vic2NyaWJlKChwcm9ncmVzcykgPT4ge1xuICAgICAgICB0aGlzLmNhY2hlUHJvZ3Jlc3MgPSBwcm9ncmVzcztcbiAgICAgIH0pO1xuICAgIHRoaXMuaXNab29tU3Vic2NyaXB0aW9uID0gdGhpcy5zdGF0ZS5pc1pvb20kKCkuc3Vic2NyaWJlKCh6b29tKSA9PiB7XG4gICAgICB0aGlzLnpvb20gPSB6b29tO1xuICAgIH0pO1xuICAgIHRoaXMuZmlsZW5hbWVTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmdldEZpbGVuYW1lJCgpLnN1YnNjcmliZSgoZmlsZW5hbWUpID0+IHtcbiAgICAgIGlmIChmaWxlbmFtZSkge1xuICAgICAgICB0aGlzLmZpbGVOYW1lID0gZmlsZW5hbWU7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRDb2xvcm1hcCgpLnN1YnNjcmliZSgoY29sb3JtYXApID0+IHtcbiAgICAgIHRoaXMuc2VsZWN0ZWRDb2xvcm1hcCA9IGNvbG9ybWFwO1xuICAgIH0pO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0UmV2ZXJzZVNjYWxlKCkuc3Vic2NyaWJlKChyZXZlcnNlc2NhbGUpID0+IHtcbiAgICAgIHRoaXMucmV2ZXJzZXNjYWxlID0gcmV2ZXJzZXNjYWxlO1xuICAgIH0pO1xuICAgIHRoaXMuaW50ZW5zaXR5UHJvZmlsZVN1YiA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0SW50ZW5zaXR5UHJvZmlsZSQoKS5zdWJzY3JpYmUoKHByb2ZpbGVzKSA9PiB7XG4gICAgICB0aGlzLmxhdGVzdFByb2ZpbGVzID0gcHJvZmlsZXM7XG4gICAgICB0aGlzLmhhc1Byb2ZpbGVzID0gcHJvZmlsZXMubGVuZ3RoID4gMDtcbiAgICAgIC8vIFRoZSBwYW5lbCBkaXYgaXMgYmVoaW5kICpuZ0lmPVwiaGFzUHJvZmlsZXNcIi4gZGV0ZWN0Q2hhbmdlcygpIG1hdGVyaWFsaXplc1xuICAgICAgLy8gaXQgc3luY2hyb25vdXNseSAodGhlIHN1YnNjcmlwdGlvbiBtYXkgZmlyZSBvdXRzaWRlIEFuZ3VsYXIncyB6b25lIOKAlCBlLmcuXG4gICAgICAvLyBmcm9tIGFuIE9TRCBkcmFnIOKAlCBzbyBDRCB3b3VsZG4ndCBydW4gb24gaXRzIG93bikuIFRoZW4gcmVuZGVyIG9uIHRoZSBuZXh0XG4gICAgICAvLyBhbmltYXRpb24gZnJhbWUsIEFGVEVSIHRoZSBicm93c2VyIGxheXMgdGhlIHBhbmVsIG91dDogYSBzeW5jaHJvbm91cyBkcmF3XG4gICAgICAvLyBoaXRzIGEgemVyby1zaXplIGNvbnRhaW5lciBvbiBmaXJzdCBjcmVhdGUgYW5kIFBsb3RseSBrZWVwcyB0aGF0IHNpemUsIHNvXG4gICAgICAvLyB0aGUgaW5zZXQgc3RheXMgYmxhbmsgYW5kIGxpdmUgdXBkYXRlcyByZWRyYXcgaW50byB0aGUgc2FtZSB6ZXJvIGJveC5cbiAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiB0aGlzLnJlbmRlckludGVuc2l0eUluc2V0KCkpO1xuICAgIH0pO1xuICAgIC8vIFdoZW4gdGhlIE9TRCB2aWV3IHNldHRsZXMgYXQgYSBuZXcgem9vbS9wYW4sIHJlLXNhbXBsZSB0aGUgaW50ZW5zaXR5IGxpbmVzXG4gICAgLy8gZnJvbSBhIGNyb3Agb2YgdGhlIHZpc2libGUgcmVnaW9uIHNvIHRoZSBpbnNldCByZWZsZWN0cyB0aGUgem9vbS1sZXZlbFxuICAgIC8vIHJlc29sdXRpb24gKFBsb3RseSdzIG93biBoaWdoLWRlZiB6b29tIHVwZGF0ZXMgdGhlIHNhbXBsaW5nIGNhY2hlIGlubGluZSkuXG4gICAgdGhpcy52aWV3cG9ydENoYW5nZVN1YiA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0Vmlld3BvcnRDaGFuZ2UkKCkuc3Vic2NyaWJlKChyb2kpID0+IHtcbiAgICAgIGlmICh0aGlzLmhhc1Byb2ZpbGVzICYmIHRoaXMuaXNJbWFnZVZpZXcpIHtcbiAgICAgICAgdGhpcy5wbG90U2VydmljZS5yZWZyZXNoSW50ZW5zaXR5U2FtcGxpbmdGb3JSb2kocm9pLngsIHJvaS55LCByb2kud2lkdGgsIHJvaS5oZWlnaHQsIHRoaXMuekluZGV4KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLnByZXZpZXdTdWJzY3JpcHRpb24gPSB0aGlzLnN0YXRlLmdldEltYWdlSW5mbyQoKS5zdWJzY3JpYmUoe1xuICAgICAgbmV4dDogKGltZ0luZm8pID0+IHtcbiAgICAgICAgaWYgKCFpbWdJbmZvKSB7XG4gICAgICAgICAgdGhpcy5vbkltYWdlQ2xlYXJlZCgpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoaW1nSW5mbykge1xuICAgICAgICAgIHRoaXMuaW1hZ2VJbmZvID0gaW1nSW5mbztcbiAgICAgICAgICAvLyBTdGFjay1vbmx5IHBsb3QgdHlwZXMgKGlzb3N1cmZhY2UsIHNjYXR0ZXIzZCkgZGVwZW5kIG9uIHdoZXRoZXIgdGhpc1xuICAgICAgICAgIC8vIGZpbGUgaXMgYSBzdGFjayDigJQgcmVjb21wdXRlIHRoZSBzZWxlY3RvciBvcHRpb25zLlxuICAgICAgICAgIHRoaXMuY29tcHV0ZVBsb3RUeXBlT3B0aW9ucygpO1xuICAgICAgICAgIC8vIElmIHRoZSBhY3RpdmUgcGxvdCB0eXBlIGlzbid0IHZhbGlkIGZvciB0aGlzIGltYWdlIChlLmcuIGEgc2NhbGFyXG4gICAgICAgICAgLy8gdHlwZSBsaWtlIENvbnRvdXIgY2FycmllZCBvdmVyIHRvIGFuIFJHQiBpbWFnZSksIGZhbGwgYmFjayB0byBJbWFnZS5cbiAgICAgICAgICB0aGlzLnJlY29uY2lsZVNlbGVjdGVkUGxvdFR5cGUoKTtcbiAgICAgICAgICAvLyByZXNldCBzdGFjayBvcHRpb25zIHNlbGVjdGlvblxuICAgICAgICAgIHRoaXMuc2VsZWN0ZWRTdGFja09wdGlvbiA9IGltZ0luZm8uc2hvd1N0YWNrID8gdGhpcy5zdGFja09wdGlvbnNbMV0gOiB0aGlzLnN0YWNrT3B0aW9uc1swXTtcblxuICAgICAgICAgIC8vIFJlYWQgZnJvbSBgaW1nSW5mb2AsIHRoZSB2YWx1ZSB0aGlzIGVtaXNzaW9uIGNhcnJpZWQsIHJhdGhlciB0aGFuIGZyb20gdGhlXG4gICAgICAgICAgLy8gZmllbGQuIEhhbmRsaW5nIHRoZSBlbXB0eSBlbWlzc2lvbiBtZWFucyB0aGUgZmllbGQgQ0FOIGJlIG51bGxlZCBwYXJ0LXdheVxuICAgICAgICAgIC8vIHRocm91Z2ggdGhpcyBicmFuY2g6IGByZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlYCBtYXkgY2FsbCBgc2V0UGxvdFR5cGVgLFxuICAgICAgICAgIC8vIHdoaWNoIGNhbiBtYWtlIHRoZSBob3N0IHB1Ymxpc2ggYSBuZXcgaW1hZ2Ugc3RhdGUsIHJlLWVudGVyaW5nIHRoaXMgdmVyeVxuICAgICAgICAgIC8vIHN1YnNjcmlwdGlvbi4gUmVhZGluZyB0aGUgZmllbGQgdGhlbiB0aHJldyBvbiBgaXNHcmF5c2NhbGVgIGFuZCBhYm9ydGVkIHRoZVxuICAgICAgICAgIC8vIGhhbmRsZXIsIHNvIHRoZSBvYnNlcnZhdGlvbnMgd2VyZSBuZXZlciBkcmF3bi5cbiAgICAgICAgICB0aGlzLmlzR3JheXNjYWxlRXZlbnQuZW1pdChpbWdJbmZvLmlzR3JheXNjYWxlKTtcbiAgICAgICAgICB0aGlzLmlzU3RhY2tFdmVudC5lbWl0KGltZ0luZm8uaXNTdGFjayk7XG4gICAgICAgICAgLy8gUmVzZXQgdG8gdGhlIGRlZmF1bHQgMkQgSW1hZ2UgdmlldyB3aGVuIGEgZGlmZmVyZW50IGltYWdlIGlzXG4gICAgICAgICAgLy8gc2VsZWN0ZWQgd2hpbGUgYSAzRCB0eXBlIGlzIGFjdGl2ZS5cbiAgICAgICAgICBpZiAoIXRoaXMuaXNIZWF0bWFwICYmIGltZ0luZm8uZmlsZU5hbWUgIT09IHRoaXMubG9hZGVkRmlsZU5hbWUpIHtcbiAgICAgICAgICAgIHRoaXMuaXNIZWF0bWFwID0gdHJ1ZTtcbiAgICAgICAgICAgIHRoaXMucGxvdFR5cGUgPSBQbG90VHlwZS5JTUFHRTtcbiAgICAgICAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gUGxvdFR5cGUuSU1BR0U7XG4gICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFBsb3RUeXBlKHRoaXMucGxvdFR5cGUpO1xuICAgICAgICAgICAgdGhpcy5hY3RpdmVTdXJmYWNlM2RNb2RlID0gJ3R1cm50YWJsZSc7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRoaXMubG9hZGVkRmlsZU5hbWUgPSBpbWdJbmZvLmZpbGVOYW1lO1xuICAgICAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0SW1hZ2VNZXRhKGltZ0luZm8uaW1hZ2VNZXRhKTtcbiAgICAgICAgICBjb25zdCB1cmxzID0gaW1nSW5mby51cmxzO1xuICAgICAgICAgIC8vIEEgbmV3ZXIgaW1hZ2UgQUxXQVlTIHByZWVtcHRzIGFuIGluLWZsaWdodCByZW5kZXIuIFRoaXMgd2FzXG4gICAgICAgICAgLy8gYGlmICghdGhpcy5ydW5uaW5nKWAsIHdoaWNoIERST1BQRUQgdGhlIG5ldyBpbWFnZSB3aGlsZSB0aGUgb2xkIG9uZVxuICAgICAgICAgIC8vIGZpbmlzaGVkOiBpZGVudGl0eSBhYm92ZSBoYWQgYWxyZWFkeSBmbGlwcGVkIHRvIHRoZSBuZXcgZmlsZSwgc28gdGhlXG4gICAgICAgICAgLy8gYXBwIHJlcG9ydGVkIGltYWdlIEIgYXMgbG9hZGVkIHdoaWxlIGltYWdlIEEgc3RheWVkIG9uIHNjcmVlbi4gV29yc2UsXG4gICAgICAgICAgLy8gYSBjb2xkIGltYWdlIGhvbGRzIHRoZSBzbG90IGZvciBpdHMgd2hvbGUgc2VydmVyLXNpZGUgY2FjaGUgZmlsbCwgc29cbiAgICAgICAgICAvLyBldmVyeSBjbGljayBmb3IgMS0yIG1pbnV0ZXMgd2FzIGRpc2NhcmRlZCBhZ2FpbnN0IGEgYmxhbmsgdmlld3BvcnRcbiAgICAgICAgICAvLyBhbmQgYSBcIkxvYWRpbmcgaW1hZ2UuLi5cIiBvdmVybGF5IHRoYXQgbmV2ZXIgcmVzb2x2ZWQuXG4gICAgICAgICAgaWYgKHVybHMpIHtcbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gKyt0aGlzLnJlbmRlclRva2VuO1xuICAgICAgICAgICAgY29uc3QgaXNDdXJyZW50ID0gKCkgPT4gdGhpcy5yZW5kZXJUb2tlbiA9PT0gdG9rZW47XG4gICAgICAgICAgICBpZiAodGhpcy5ydW5uaW5nKSB7XG4gICAgICAgICAgICAgIC8vIFN0b3AgdGhlIHByZXZpb3VzIHJlbmRlcidzIGZyYW1lIHN0cmVhbWluZyAobmFwYXJpIHZvbHVtZS9zdXJmYWNlXG4gICAgICAgICAgICAgIC8vIHByZWxvYWQga2VlcHMgZmV0Y2hpbmcgb3RoZXJ3aXNlKSBhbmQgY2xlYXIgaXRzIHNoYXJwZW4gZmxhZy4gSXRzXG4gICAgICAgICAgICAgIC8vIGNhbGxiYWNrcyBhcmUgYWxyZWFkeSBpbmVydCB2aWEgdGhlIHRva2VuLCBzbyBpdCBjYW4gbmVpdGhlciBwYWludFxuICAgICAgICAgICAgICAvLyBub3IgcmVsZWFzZSB0aGUgb3ZlcmxheSB0aGlzIHJlbmRlciBub3cgb3ducy5cbiAgICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5jYW5jZWxMb2FkaW5nPy4oKTtcbiAgICAgICAgICAgICAgdGhpcy5zaGFycGVuaW5nID0gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBpbWFnZSBzaXplIOKAlCBtZWFzdXJlIHRoZSBwbG90IGRpdiBkaXJlY3RseSBzbyB0aGUgdG9vbGJhciBoZWlnaHQgaXMgZXhjbHVkZWRcbiAgICAgICAgICAgIGNvbnN0IHBsb3REaXY6IEhUTUxFbGVtZW50IHwgbnVsbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdk5hbWUpO1xuICAgICAgICAgICAgY29uc3Qgc2NyZWVuSGVpZ2h0ID0gcGxvdERpdj8ub2Zmc2V0SGVpZ2h0IHx8IDUwMDtcbiAgICAgICAgICAgIC8vIEEgY29udHJpYnV0ZWQgbW9kZSdzIHNlc3Npb24gaXMgYm91bmQgdG8gdGhlIGJhc2UgdmlldyBhYm91dCB0byBiZVxuICAgICAgICAgICAgLy8gdG9ybiBkb3duLCBzbyBpdCBlbmRzIGhlcmUg4oCUIGJlZm9yZSByZXNldCgpLCBiZWZvcmUgYW55dGhpbmcgbmV3XG4gICAgICAgICAgICAvLyBkcmF3cy4gSWYgdGhlIG1vZGUgaXMgc3RpbGwgc2VsZWN0ZWQgb25jZSB0aGlzIHJlbmRlciBsYW5kcywgYVxuICAgICAgICAgICAgLy8gZnJlc2ggc2Vzc2lvbiBzdGFydHMgZm9yIGl0IChzZWUgYWN0aXZhdGVTZWxlY3RlZFBsb3RNb2RlKS5cbiAgICAgICAgICAgIHRoaXMucGxvdE1vZGVzLmRlYWN0aXZhdGUoKTtcbiAgICAgICAgICAgIHRoaXMudG9vbERpYWxvZ3MuZGVhY3RpdmF0ZSgpO1xuICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5yZXNldCgpO1xuICAgICAgICAgICAgdGhpcy5zdGFja0xvYWRpbmcgPSBpbWdJbmZvLmlzU3RhY2sgJiYgaW1nSW5mby5zaG93U3RhY2s7XG4gICAgICAgICAgICAvLyBzZXQgbWF4IGluZGV4IG9mIHN0YWNrIOKAlCBjb21wdXRlZCBiZWZvcmUgdXBkYXRlWkluZGV4IHNvIGFcbiAgICAgICAgICAgIC8vIGZyZXNoIHN0YWNrIChzaG9ydGVyIG9yIGxvbmdlciB0aGFuIHdoYXRldmVyIHdhcyBwcmV2aW91c2x5XG4gICAgICAgICAgICAvLyBsb2FkZWQpIGNsYW1wcyBhZ2FpbnN0IElUUyBib3VuZHMsIG5vdCB0aGUgc3RhbGUgb25lcy5cbiAgICAgICAgICAgIC8vIE9uZSBVUkwgcGVyIHNsaWNlICgwLWluZGV4ZWQpLCBzbyB0aGUgbGFzdCByZWFjaGFibGUgaW5kZXggaXNcbiAgICAgICAgICAgIC8vIGxlbmd0aC0xIOKAlCBlYXJsaWVyIGBsZW5ndGgtMmAgZHJvcHBlZCB0aGUgZmluYWwgc2xpY2UuXG4gICAgICAgICAgICB0aGlzLm1heEluZGV4ID0gdXJscy5sZW5ndGggPiAxID8gdXJscy5sZW5ndGggLSAxIDogMDtcbiAgICAgICAgICAgIC8vIE9uZS1zaG90IGhpbnQ6IGp1bXAgc3RyYWlnaHQgdG8gYSBzcGVjaWZpYyBzbGljZSAoZS5nLiB0aGVcbiAgICAgICAgICAgIC8vIGZpbGUgdGhlIHVzZXIgYWN0dWFsbHkgY2xpY2tlZCB3aXRoaW4gYSBudW1iZXJlZCBzZXJpZXMpLlxuICAgICAgICAgICAgLy8gQ29uc3VtZWQgaW1tZWRpYXRlbHkgc28gcmVkZWxpdmVyaW5nIHRoZSBzYW1lIEltYWdlSW5mbyBsYXRlclxuICAgICAgICAgICAgLy8gKGEgcGxvdC10eXBlIHN3aXRjaCwgcmVsb2FkQW5kUGxvdCkgZG9lc24ndCByZXNldCB0aGUgdXNlcidzXG4gICAgICAgICAgICAvLyBjdXJyZW50IHNjcnViIHBvc2l0aW9uIGJhY2sgdG8gaXQuXG4gICAgICAgICAgICBpZiAoaW1nSW5mby5pbml0aWFsWkluZGV4ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgdGhpcy56SW5kZXggPSBpbWdJbmZvLmluaXRpYWxaSW5kZXg7XG4gICAgICAgICAgICAgIGltZ0luZm8uaW5pdGlhbFpJbmRleCA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIG1ha2Ugc3VyZSB0aGUgemluZGV4IGlzIHdpdGhpbiBib3VuZHNcbiAgICAgICAgICAgIHRoaXMudXBkYXRlWkluZGV4KHVybHMpO1xuICAgICAgICAgICAgdGhpcy5ydW5uaW5nID0gdHJ1ZTtcbiAgICAgICAgICAgIC8vIE11bHRpLXRpZXIgcmVuZGVyaW5nIChzbWFsbCBibHVycnkgdGllciBmaXJzdCwgdGhlbiBzaGFycGVuIGluXG4gICAgICAgICAgICAvLyBwbGFjZSkg4oCUIHNlcXVlbmNpbmcgbGl2ZXMgaW4gUmVuZGVyT3JjaGVzdHJhdG9yOyB0aGlzIGNvbXBvbmVudFxuICAgICAgICAgICAgLy8gc3VwcGxpZXMgdGhlIHBoYXNlIHJlbmRlciBhbmQgb3ducyB0aGUgVUkgZmxhZ3MgdmlhIGNhbGxiYWNrcy5cbiAgICAgICAgICAgIC8vIDNEIHBsb3QgdHlwZXMgcmVuZGVyIHNpbmdsZS1wYXNzOiB0aGUgaW4tcGxhY2UgbGFyZ2UgcGFzcyBkb2Vzbid0XG4gICAgICAgICAgICAvLyByZWJ1aWxkIGEgM0QgZ2wtbWVzaCBpc29zdXJmYWNlLCBzbyB0aGUgc2hhcnBlbiBzdGVwIGJsYW5rZWQgaXQuXG4gICAgICAgICAgICBjb25zdCBoYXNTbWFsbFRpZXIgPVxuICAgICAgICAgICAgICB0aGlzLmlzSGVhdG1hcCAmJlxuICAgICAgICAgICAgICAoaW1nSW5mby5zbWFsbFVybHM/Lmxlbmd0aCA/PyAwKSA9PT0gdXJscy5sZW5ndGggJiZcbiAgICAgICAgICAgICAgKGltZ0luZm8uc21hbGxVcmxzPy5sZW5ndGggPz8gMCkgPiAwO1xuICAgICAgICAgICAgY29uc3Qgc21hbGxJbWdJbmZvID0gaGFzU21hbGxUaWVyID8geyAuLi5pbWdJbmZvLCB1cmxzOiBpbWdJbmZvLnNtYWxsVXJscyBhcyBzdHJpbmdbXSB9IDogbnVsbDtcblxuICAgICAgICAgICAgLy8gRW50ZXIgcGVyLXNsaWNlIHN0YWNrIG1vZGUgd2l0aCB0aGUgZ2l2ZW4gc2xpY2XihpJyZWdpb25zIG1hcCBhbmRcbiAgICAgICAgICAgIC8vIHNlZWQgdGhlIFwicHJldmlvdXMgc2hhcGVzXCIgZnJvbSB0aGUgbm93LWxpdmUgc2xpY2UuIGVudGVyU3RhY2tNb2RlXG4gICAgICAgICAgICAvLyBtYWtlcyB6SW5kZXgncyBzbGljZSBsaXZlIGFuZCByZXNldHMgdW5kbyAoaml0LXVpIzkzKS5cbiAgICAgICAgICAgIGNvbnN0IGVudGVyU3RhY2sgPSAoXG4gICAgICAgICAgICAgIHNsaWNlczogTWFwPG51bWJlciwgUmVnaW9uW10+LFxuICAgICAgICAgICAgICBsYXlvdXQ6ICdjb21iaW5lZCcgfCAncGVyLXNsaWNlLWZpbGUnLFxuICAgICAgICAgICAgKSA9PiB7XG4gICAgICAgICAgICAgIHRoaXMucGxvdFNlcnZpY2UuZW50ZXJTdGFja01vZGUoc2xpY2VzLCB0aGlzLnpJbmRleCwgbGF5b3V0KTtcbiAgICAgICAgICAgICAgY29uc3Qgc2hhcGVzID0gdGhpcy5wbG90U2VydmljZVxuICAgICAgICAgICAgICAgIC5nZXRSZWdpb25zKClcbiAgICAgICAgICAgICAgICAubWFwKChyZWdpb24pID0+IHJlZ2lvbi5nZXRTaGFwZSh0aGlzLnBsb3RTZXJ2aWNlLmdldFNob3dTaGFwZUxhYmVsKCkpKTtcbiAgICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRQcmV2aW91c1NoYXBlcyhzaGFwZXMpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgY29uc3QgYXBwbHlSb2kgPSAoKSA9PiB7XG4gICAgICAgICAgICAgIC8vIEZvbGRlciBzdGFjazogYSBzdGFjayBvZiBzZWxmLWNvbnRhaW5lZCBwZXItc2xpY2UgZmlsZXNcbiAgICAgICAgICAgICAgLy8gKHRpbGVkID09PSBmYWxzZTsgc2VlIGxvYWRTZXJpZXNBc1N0YWNrKS4gRWFjaCBzbGljZS1maWxlIG1heVxuICAgICAgICAgICAgICAvLyBjYXJyeSBpdHMgb3duIHNpYmxpbmcgXCI8c3RlbT4uZ2VvanNvblwiIChyb2lKc29uU3Ryc1t6XSksIGJ1dCBhXG4gICAgICAgICAgICAgIC8vIGZyZXNoIGZvbGRlciB3aXRoIG5vbmUgeWV0IGxlYXZlcyByb2lKc29uU3RycyB1bmRlZmluZWQg4oCUIGtleVxuICAgICAgICAgICAgICAvLyBvZmYgYHRpbGVkID09PSBmYWxzZWAsIE5PVCByb2lKc29uU3Rycywgc28gYW4gdW5hbm5vdGF0ZWRcbiAgICAgICAgICAgICAgLy8gZm9sZGVyIHN0YWNrIHN0aWxsIGVudGVycyB0aGUgcGVyLXNsaWNlLWZpbGUgbGF5b3V0IChhbmQgc2F2ZXNcbiAgICAgICAgICAgICAgLy8gYmFjayBvbmUgZ2VvanNvbiBwZXIgc2xpY2UtZmlsZSkgcmF0aGVyIHRoYW4gZmFsbGluZyB0aHJvdWdoIHRvXG4gICAgICAgICAgICAgIC8vIHRoZSBzaW5nbGUtZmlsZSBjb21iaW5lZCBwYXRoIChqaXQtdWkjOTMpLlxuICAgICAgICAgICAgICBpZiAoaW1nSW5mby5pc1N0YWNrICYmIGltZ0luZm8udGlsZWQgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGVyU2xpY2UgPSBpbWdJbmZvLnJvaUpzb25TdHJzO1xuICAgICAgICAgICAgICAgIGNvbnN0IHNsaWNlQ291bnQgPSBpbWdJbmZvLnVybHM/Lmxlbmd0aCA/PyBwZXJTbGljZT8ubGVuZ3RoID8/IDA7XG4gICAgICAgICAgICAgICAgY29uc3Qgc2xpY2VzID0gbmV3IE1hcDxudW1iZXIsIFJlZ2lvbltdPigpO1xuICAgICAgICAgICAgICAgIGZvciAobGV0IHogPSAwOyB6IDwgc2xpY2VDb3VudDsgeisrKSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCBqc29uID0gcGVyU2xpY2U/Llt6XSA/PyBudWxsO1xuICAgICAgICAgICAgICAgICAgc2xpY2VzLnNldCh6LCBqc29uID8gdGhpcy5wbG90U2VydmljZS5pbXBvcnRSZWdpb25zKGpzb24pIDogW10pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbnRlclN0YWNrKHNsaWNlcywgJ3Blci1zbGljZS1maWxlJyk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFNpbmdsZS1maWxlIHotc3RhY2s6IG9uZSBzaWJsaW5nIGdlb2pzb24gaG9sZGluZyBldmVyeSBzbGljZSdzXG4gICAgICAgICAgICAgIC8vIHJlZ2lvbnMgaW5kZXhlZCBieSBRdVBhdGgncyBnZW9tZXRyeS5wbGFuZS56LiBFbnRlciBwZXItc2xpY2VcbiAgICAgICAgICAgICAgLy8gbW9kZSAoc2F2aW5nIGJhY2sgb25lIGNvbWJpbmVkIHotaW5kZXhlZCBnZW9qc29uKSB3aGVuIHRoZVxuICAgICAgICAgICAgICAvLyBnZW9qc29uIGFjdHVhbGx5IGNhcnJpZXMgc2xpY2UgaW5kaWNlcywgb3Igd2hlbiB0aGVyZSdzIG5vdGhpbmdcbiAgICAgICAgICAgICAgLy8geWV0IHRvIGF1dGhvciBhZ2FpbnN0LiBBIGxlZ2FjeSBnZW9qc29uIHdob3NlIHJlZ2lvbnMgYXJlIGFsbCBvblxuICAgICAgICAgICAgICAvLyB0aGUgZGVmYXVsdCBwbGFuZSBzdGF5cyBnbG9iYWwgKHNob3duIG9uIGV2ZXJ5IHNsaWNlKSBzb1xuICAgICAgICAgICAgICAvLyBleGlzdGluZyBzaW5nbGUtcGxhbmUgYW5ub3RhdGlvbnMgYXJlbid0IGNvbmZpbmVkIHRvIHNsaWNlIDAuXG4gICAgICAgICAgICAgIGNvbnN0IHJvaUpzb24gPSBpbWdJbmZvLnJvaUpzb25TdHI7XG4gICAgICAgICAgICAgIGlmIChpbWdJbmZvLmlzU3RhY2spIHtcbiAgICAgICAgICAgICAgICBjb25zdCByZWdpb25zID0gcm9pSnNvbiA/IHRoaXMucGxvdFNlcnZpY2UuaW1wb3J0UmVnaW9ucyhyb2lKc29uKSA6IFtdO1xuICAgICAgICAgICAgICAgIGNvbnN0IGhhc1NsaWNlSW5mbyA9IHJlZ2lvbnMuc29tZSgocikgPT4gKHIueiA/PyAwKSAhPT0gMCk7XG4gICAgICAgICAgICAgICAgaWYgKCFyb2lKc29uIHx8IGhhc1NsaWNlSW5mbykge1xuICAgICAgICAgICAgICAgICAgY29uc3Qgc2xpY2VzID0gbmV3IE1hcDxudW1iZXIsIFJlZ2lvbltdPigpO1xuICAgICAgICAgICAgICAgICAgZm9yIChjb25zdCByIG9mIHJlZ2lvbnMpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgeiA9IHIueiA/PyAwO1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBidWNrZXQgPSBzbGljZXMuZ2V0KHopO1xuICAgICAgICAgICAgICAgICAgICBpZiAoYnVja2V0KSBidWNrZXQucHVzaChyKTtcbiAgICAgICAgICAgICAgICAgICAgZWxzZSBzbGljZXMuc2V0KHosIFtyXSk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBlbnRlclN0YWNrKHNsaWNlcywgJ2NvbWJpbmVkJyk7XG4gICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFNpbmdsZS1wbGFuZSBpbWFnZSAob3IgYSBsZWdhY3kgZ2xvYmFsIHotc3RhY2sgZ2VvanNvbik6IG9uZVxuICAgICAgICAgICAgICAvLyByZWdpb24gc2V0IGZvciB0aGUgd2hvbGUgaW1hZ2UuXG4gICAgICAgICAgICAgIGlmIChyb2lKc29uKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVnaW9ucyA9IHRoaXMucGxvdFNlcnZpY2UuaW1wb3J0UmVnaW9ucyhyb2lKc29uKTtcbiAgICAgICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFJlZ2lvbnMocmVnaW9ucyk7XG4gICAgICAgICAgICAgICAgY29uc3Qgc2hhcGVzID0gcmVnaW9ucy5tYXAoKHJlZ2lvbikgPT5cbiAgICAgICAgICAgICAgICAgIHJlZ2lvbi5nZXRTaGFwZSh0aGlzLnBsb3RTZXJ2aWNlLmdldFNob3dTaGFwZUxhYmVsKCkpLFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRQcmV2aW91c1NoYXBlcyhzaGFwZXMpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIExvYWRpbmcgYW4gaW1hZ2UncyBzYXZlZCBST0lzIGlzIG5vdCBhIHVzZXIgZWRpdCDigJQgc3RhcnQgdGhlXG4gICAgICAgICAgICAgIC8vIHVuZG8gaGlzdG9yeSBmcmVzaCBzbyB0aGUgZmlyc3QgdW5kbyBjYW4ndCB3aXBlIHRoZW0gKGppdC11aSM4NSkuXG4gICAgICAgICAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVzZXRVbmRvSGlzdG9yeSgpO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGxldCBvdmVybGF5UmVsZWFzZWQgPSBmYWxzZTtcbiAgICAgICAgICAgIGNvbnN0IHJlbGVhc2VPdmVybGF5ID0gKCkgPT4ge1xuICAgICAgICAgICAgICBpZiAob3ZlcmxheVJlbGVhc2VkKSByZXR1cm47XG4gICAgICAgICAgICAgIG92ZXJsYXlSZWxlYXNlZCA9IHRydWU7XG4gICAgICAgICAgICAgIGlmIChpbWdJbmZvLmlzU3RhY2sgJiYgaW1nSW5mby5zaG93U3RhY2spIHRoaXMuc3RhY2tMb2FkaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VMb2FkaW5nKGZhbHNlKTtcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIG5ldyBSZW5kZXJPcmNoZXN0cmF0b3Ioe1xuICAgICAgICAgICAgICAvLyBpblBsYWNlPXRydWUgdXBkYXRlcyB0aGUgZXhpc3RpbmcgcmVuZGVyIGluc3RlYWQgb2YgcmVidWlsZGluZ1xuICAgICAgICAgICAgICAvLyBpdCwgc28gdGhlIGNhbnZhcyBkb2Vzbid0IGJsYW5rIGR1cmluZyB0aGUgc21hbGzihpJsYXJnZSBzd2FwLlxuICAgICAgICAgICAgICByZW5kZXJQaGFzZTogKHBoYXNlSW5mbywgaW5QbGFjZSkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIFN1cGVyc2VkZWQgQkVGT1JFIHRoaXMgcGhhc2Ugc3RhcnRlZCDigJQgZG9uJ3QgZXZlbiBpc3N1ZSB0aGUgbG9hZC5cbiAgICAgICAgICAgICAgICAvLyBSZW5kZXJPcmNoZXN0cmF0b3IgY2FsbHMgcmVuZGVyUGhhc2Ugb25jZSBwZXIgdGllciBhbmQgcmV0cmllcyB0aGVcbiAgICAgICAgICAgICAgICAvLyBzaGFycGVuIHBhc3Mgb24gZmFpbHVyZSwgc28gY2hlY2tpbmcgb25seSBhZnRlciB0aGUgbG9hZCByZXNvbHZlc1xuICAgICAgICAgICAgICAgIC8vIHdvdWxkIGxldCBhIHByZWVtcHRlZCByZW5kZXIga2VlcCBmZXRjaGluZyBzbGljZXMvdGlsZXMgZm9yIGFuXG4gICAgICAgICAgICAgICAgLy8gaW1hZ2Ugbm9ib2R5IGlzIGxvb2tpbmcgYXQuXG4gICAgICAgICAgICAgICAgaWYgKCFpc0N1cnJlbnQoKSkgcmV0dXJuIFByb21pc2UucmVzb2x2ZShudWxsKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5wbG90U2VydmljZS5sb2FkKHBoYXNlSW5mbywgdGhpcy56SW5kZXgpLnRoZW4oKGxvYWRlZEltYWdlKSA9PiB7XG4gICAgICAgICAgICAgICAgICAvLyBQcmVlbXB0ZWQgd2hpbGUgdGhpcyBwaGFzZSB3YXMgbG9hZGluZyDigJQgZHJvcCBpdCBvbiB0aGUgZmxvb3IuXG4gICAgICAgICAgICAgICAgICBpZiAoIWlzQ3VycmVudCgpKSByZXR1cm4gbnVsbDtcbiAgICAgICAgICAgICAgICAgIC8vIEd1YXJkIGFnYWluc3QgYSBuZXdlciBjbGljayByZWFjaGluZyB1cyBtaWQtcmVuZGVyLlxuICAgICAgICAgICAgICAgICAgaWYgKHBoYXNlSW5mby5maWxlTmFtZSAhPT0gbG9hZGVkSW1hZ2UuZmlsZW5hbWUpIHJldHVybiBudWxsO1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMucGxvdFNlcnZpY2UucGxvdChcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wbG90RGl2TmFtZSxcbiAgICAgICAgICAgICAgICAgICAgbG9hZGVkSW1hZ2UsXG4gICAgICAgICAgICAgICAgICAgIHBoYXNlSW5mbyxcbiAgICAgICAgICAgICAgICAgICAgc2NyZWVuSGVpZ2h0LFxuICAgICAgICAgICAgICAgICAgICB0aGlzLnBsb3RUeXBlLFxuICAgICAgICAgICAgICAgICAgICBpblBsYWNlLFxuICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgc21hbGxTaG93bjogKCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghaXNDdXJyZW50KCkpIHJldHVybjsgLy8gc3VwZXJzZWRlZCBieSBhIG5ld2VyIGltYWdlXG4gICAgICAgICAgICAgICAgLy8gSWYgdGhlIGZpbGUgaXMgc3RpbGwgYmVpbmcgY2FjaGVkL3ByZXBhcmVkIHNlcnZlci1zaWRlLCBrZWVwIHRoZSBmdWxsXG4gICAgICAgICAgICAgICAgLy8gY2FjaGUtcHJvZ3Jlc3Mgb3ZlcmxheSB1cCBpbnN0ZWFkIG9mIGRyb3BwaW5nIHRvIHRoZSB0cmFuc2x1Y2VudCBcIlNoYXJwZW5pbmdcbiAgICAgICAgICAgICAgICAvLyBwcmV2aWV3Li4uXCIgc3Bpbm5lciBvdmVyIGEgYmxhbmsgY2FudmFzIChhIGxhcmdlIHVuY2FjaGVkIGltYWdlIHJlbmRlcnMgaXRzXG4gICAgICAgICAgICAgICAgLy8gc21hbGwgcHJldmlldyBiZWZvcmUgaXRzIHRpbGVzIGV4aXN0KS4gZmluaXNoZWQoKSByZWxlYXNlcyB0aGUgb3ZlcmxheSBvbmNlXG4gICAgICAgICAgICAgICAgLy8gdGhlIHJlYWwgcmVuZGVyIGxhbmRzLlxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmlzQ2FjaGluZykgcmV0dXJuO1xuICAgICAgICAgICAgICAgIC8vIFNtYWxsIHRpZXIgb24gc2NyZWVuIOKAlCBkcm9wIHRoZSBmdWxsIG92ZXJsYXkgYnV0IGtlZXAgYSB0cmFuc2x1Y2VudCBzcGlubmVyIHNvXG4gICAgICAgICAgICAgICAgLy8gdGhlIGJsdXJyeSByZW5kZXIgaXNuJ3QgbWlzdGFrZW4gZm9yIHRoZSBmaW5hbCBpbWFnZS5cbiAgICAgICAgICAgICAgICByZWxlYXNlT3ZlcmxheSgpO1xuICAgICAgICAgICAgICAgIHRoaXMuc2hhcnBlbmluZyA9IHRydWU7XG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIHNoYXJwZW5TZXR0bGVkOiAoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKCFpc0N1cnJlbnQoKSkgcmV0dXJuOyAvLyBzdXBlcnNlZGVkIGJ5IGEgbmV3ZXIgaW1hZ2VcbiAgICAgICAgICAgICAgICB0aGlzLnNoYXJwZW5pbmcgPSBmYWxzZTtcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgZmluaXNoZWQ6IChfdmlhU21hbGwsIGxvZ1RhZykgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghaXNDdXJyZW50KCkpIHJldHVybjsgLy8gc3VwZXJzZWRlZCBieSBhIG5ld2VyIGltYWdlXG4gICAgICAgICAgICAgICAgLy8gSWRlbXBvdGVudCDigJQgcmVsZWFzZXMgbm93IGlmIHNtYWxsU2hvd24gZGVmZXJyZWQgaXQgKGNhY2hpbmcpIG9yIHdhcyBza2lwcGVkLlxuICAgICAgICAgICAgICAgIHJlbGVhc2VPdmVybGF5KCk7XG4gICAgICAgICAgICAgICAgdGhpcy5ydW5uaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgYXBwbHlSb2koKTtcbiAgICAgICAgICAgICAgICB0aGlzLmFjdGl2YXRlU2VsZWN0ZWRQbG90TW9kZSgpO1xuICAgICAgICAgICAgICAgIHRoaXMuYWN0aXZhdGVPcGVuRGlhbG9nVG9vbCgpO1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGxvZ1RhZyk7XG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIHNoYXJwZW5GYWlsZWQ6IChlcnI6IGFueSkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghaXNDdXJyZW50KCkpIHJldHVybjsgLy8gc3VwZXJzZWRlZCBieSBhIG5ld2VyIGltYWdlXG4gICAgICAgICAgICAgICAgLy8gVGhlIHNtYWxsIHRpZXIgc3RheXMgb24gc2NyZWVuIGFzIHRoZSBmYWxsYmFjayDigJQgdGVsbCB0aGVcbiAgICAgICAgICAgICAgICAvLyB1c2VyIHRoZSBzaGFycGVyIHZlcnNpb24gaXNuJ3QgY29taW5nLlxuICAgICAgICAgICAgICAgIGNvbnN0IG1zZyA9IGVycj8uZXJyb3I/Lm1lc3NhZ2UgfHwgZXJyPy5tZXNzYWdlIHx8IGVycj8uc3RhdHVzVGV4dCB8fCBTdHJpbmcoZXJyKTtcbiAgICAgICAgICAgICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgICAgICAgICAgICBrZXk6IHRoaXMudml6QWxlcnRUb2FzdEtleSxcbiAgICAgICAgICAgICAgICAgIHNldmVyaXR5OiAnd2FybicsXG4gICAgICAgICAgICAgICAgICBzdW1tYXJ5OiAnUHJldmlldyBub3Qgc2hhcnBlbmVkJyxcbiAgICAgICAgICAgICAgICAgIGRldGFpbDogYFRoZSBmdWxsLXJlc29sdXRpb24gcHJldmlldyBkaWQgbm90IGxvYWQgKCR7bXNnfSkuIFRoZSBsb3ctcmVzb2x1dGlvbiBwcmV2aWV3IGlzIHN0aWxsIHNob3duLiBUcnkgY2xpY2tpbmcgdGhlIGltYWdlIGFnYWluLmAsXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5ydW5uaW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgYXBwbHlSb2koKTtcbiAgICAgICAgICAgICAgICB0aGlzLmFjdGl2YXRlU2VsZWN0ZWRQbG90TW9kZSgpO1xuICAgICAgICAgICAgICAgIHRoaXMuYWN0aXZhdGVPcGVuRGlhbG9nVG9vbCgpO1xuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSkucmVuZGVyKGltZ0luZm8sIHNtYWxsSW1nSW5mbyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgZXJyb3I6IChlcnIpID0+IHtcbiAgICAgICAgY29uc3QgbXNnID0gZXJyPy5lcnJvcj8ubWVzc2FnZSB8fCBlcnI/Lm1lc3NhZ2UgfHwgZXJyPy5zdGF0dXNUZXh0IHx8IFN0cmluZyhlcnIpO1xuICAgICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBvY2N1cmVkIHdoZW4gZ2V0dGluZyBpbWFnZSBpbmZvJywgZXJyKTtcbiAgICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICAgIGtleTogdGhpcy52aXpBbGVydFRvYXN0S2V5LFxuICAgICAgICAgIHN0aWNreTogdHJ1ZSxcbiAgICAgICAgICBzZXZlcml0eTogJ2Vycm9yJyxcbiAgICAgICAgICBzdW1tYXJ5OiAnQW4gZXJyb3Igb2NjdXJlZCcsXG4gICAgICAgICAgZGV0YWlsOiBgVGhlIGZvbGxvd2luZyBlcnJvciBvY2N1cmVkIHdoaWxlIGdldHRpbmcgaW1hZ2UgaW5mbzogJHttc2d9LlxuICAgICAgICAgICAgICAgICAgIFBsZWFzZSB0cnkgdG8gb3BlbiB0aGUgaW1hZ2UgYWdhaW4gdGhyb3VnaCB0aGUgZmlsZSBuYXZpZ2F0b3IuYCxcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuc3RhY2tMb2FkaW5nID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VMb2FkaW5nKGZhbHNlKTtcbiAgICAgICAgdGhpcy5ydW5uaW5nID0gZmFsc2U7XG4gICAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVzZXQoKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICBuZ0FmdGVyVmlld0luaXQoKSB7XG4gICAgdGhpcy5wbG90Q29udGV4dE1lbnVMaXN0ZW5lciA9IChldmVudDogTW91c2VFdmVudCkgPT4ge1xuICAgICAgY29uc3QgcGxvdEVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2TmFtZSk7XG4gICAgICBpZiAoIXBsb3RFbD8uY29udGFpbnMoZXZlbnQudGFyZ2V0IGFzIE5vZGUpKSByZXR1cm47XG4gICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgZXZlbnQuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCk7XG4gICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICB0aGlzLmNvbnRleHRNZW51SXRlbXMgPSB0aGlzLmJ1aWxkQ29udGV4dE1lbnVJdGVtcygpO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICAgIHRoaXMuY29udGV4dE1lbnUuc2hvdyhldmVudCk7XG4gICAgICB9KTtcbiAgICB9O1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdjb250ZXh0bWVudScsIHRoaXMucGxvdENvbnRleHRNZW51TGlzdGVuZXIsIHRydWUpO1xuXG4gICAgdGhpcy5rZXlkb3duTGlzdGVuZXIgPSAoZXZlbnQ6IEtleWJvYXJkRXZlbnQpID0+IHtcbiAgICAgIGNvbnN0IHRhcmdldCA9IGV2ZW50LnRhcmdldCBhcyBIVE1MRWxlbWVudDtcbiAgICAgIGlmICh0YXJnZXQudGFnTmFtZSA9PT0gJ0lOUFVUJyB8fCB0YXJnZXQudGFnTmFtZSA9PT0gJ1RFWFRBUkVBJyB8fCB0YXJnZXQuaXNDb250ZW50RWRpdGFibGUpIHJldHVybjtcbiAgICAgIC8vIFNBTSBwb2ludCBtb2RlOiBFbnRlciBjb21taXRzIHRoZSBvYmplY3QsIEVzYyBjbGVhcnMgdGhlIHByb21wdC5cbiAgICAgIGlmICh0aGlzLmFjdGl2ZURyYWdNb2RlID09PSAnc2FtUG9pbnQnICYmIChldmVudC5rZXkgPT09ICdFbnRlcicgfHwgZXZlbnQua2V5ID09PSAnRXNjYXBlJykpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgICAgICBpZiAoZXZlbnQua2V5ID09PSAnRW50ZXInKSB0aGlzLnBsb3RTZXJ2aWNlLmNvbW1pdFNhbVBvaW50cygpO1xuICAgICAgICAgIGVsc2UgdGhpcy5wbG90U2VydmljZS5jbGVhclNhbVBvaW50cygpO1xuICAgICAgICAgIHRoaXMuaGlkZVNhbVRvYXN0KCk7IC8vIHByb21wdCByZXNvbHZlZCDihpIgZGlzbWlzcyB0aGUgc3RhdHVzIHRvYXN0XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBSZWdpb24gaGlzdG9yeSAoaml0LXVpIzg1KTogQ3RybC9DbWQrWiB1bmRvZXM7IEN0cmwvQ21kK1NoaWZ0K1ogb3JcbiAgICAgIC8vIEN0cmwvQ21kK1kgcmVkb2VzLlxuICAgICAgaWYgKGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSkge1xuICAgICAgICBjb25zdCBrID0gZXZlbnQua2V5LnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGlmIChrID09PSAneicgJiYgIWV2ZW50LnNoaWZ0S2V5KSB7XG4gICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy51bmRvUmVnaW9uKCkpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoKGsgPT09ICd6JyAmJiBldmVudC5zaGlmdEtleSkgfHwgayA9PT0gJ3knKSB7XG4gICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy5yZWRvUmVnaW9uKCkpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKGV2ZW50LmtleSA9PT0gJ0RlbGV0ZScgfHwgZXZlbnQua2V5ID09PSAnQmFja3NwYWNlJyB8fCBldmVudC5rZXkgPT09ICdkJyB8fCBldmVudC5rZXkgPT09ICdEJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy5kZWxldGVSZWdpb24oKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJysnIHx8IGV2ZW50LmtleSA9PT0gJz0nKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnpvb21JbigpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnLScgfHwgZXZlbnQua2V5ID09PSAnXycpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMuem9vbU91dCgpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAncCcpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3BhbicpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAnYicpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3pvb21Ub0JveCcpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQua2V5ID09PSAncicpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2RyYXdyZWN0JykpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICdmJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd2Nsb3NlZHBhdGgnKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ3cnKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCd3YW5kJykpO1xuICAgICAgfSBlbHNlIGlmIChldmVudC5rZXkgPT09ICdlJykge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZXJhc2VWZXJ0ZXgnKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ3MnKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdzZWxlY3QnKSk7XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ2wnKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdkcmF3b3BlbnBhdGgnKSk7XG4gICAgICB9XG4gICAgfTtcbiAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigna2V5ZG93bicsIHRoaXMua2V5ZG93bkxpc3RlbmVyKTtcblxuICAgIHRoaXMud2hlZWxMaXN0ZW5lciA9IChldmVudDogV2hlZWxFdmVudCkgPT4ge1xuICAgICAgY29uc3QgcGxvdEVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2TmFtZSk7XG4gICAgICBpZiAoIXBsb3RFbD8uY29udGFpbnMoZXZlbnQudGFyZ2V0IGFzIE5vZGUpKSByZXR1cm47XG4gICAgICAvLyBJbnRlcmNlcHRpbmcgaGVyZSBmaXJlcyBhIEZJWEVEIHpvb20gc3RlcCBwZXIgd2hlZWwgZXZlbnQsIHdoaWNoIGlzIGZhclxuICAgICAgLy8gdG9vIHNlbnNpdGl2ZSBmb3IgYSByZW5kZXJlciB0aGF0IHJlYWRzIHRoZSBzY3JvbGwgZGVsdGEg4oCUIHNvIGFueXRoaW5nXG4gICAgICAvLyBkcmF3biBieSBzdWNoIGEgcmVuZGVyZXIga2VlcHMgaXRzIG93biB3aGVlbC4gU2VlIGByZW5kZXJlck93bnNXaGVlbGAuXG4gICAgICBpZiAocmVuZGVyZXJPd25zV2hlZWwodGhpcy5iYXNlUGxvdFR5cGUpKSByZXR1cm47XG4gICAgICAvLyAzRCBwbG90IHR5cGVzIChzdXJmYWNlLCBzY2F0dGVyM2QsIGlzb3N1cmZhY2UpIHJlbmRlciBpbiBhIFBsb3RseSBzY2VuZVxuICAgICAgLy8gdGhhdCBvcmJpdHMvem9vbXMgbmF0aXZlbHkgb24gc2Nyb2xsLiBUaGUgMkQgc3RlcC16b29tIGRvZXNuJ3QgYXBwbHkgYW5kXG4gICAgICAvLyB3b3VsZCB0aHJvdyAobm8geGF4aXMgb24gYSBzY2VuZSksIHNvIGxldCBQbG90bHkgaGFuZGxlIHRoZSB3aGVlbC5cbiAgICAgIGlmICghdGhpcy5pc0hlYXRtYXApIHJldHVybjtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICBldmVudC5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcbiAgICAgIGlmIChldmVudC5kZWx0YVkgPCAwKSB7XG4gICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLnpvb21JbigpKTtcbiAgICAgIH0gZWxzZSBpZiAoZXZlbnQuZGVsdGFZID4gMCkge1xuICAgICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy56b29tT3V0KCkpO1xuICAgICAgfVxuICAgIH07XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3doZWVsJywgdGhpcy53aGVlbExpc3RlbmVyLCB7IGNhcHR1cmU6IHRydWUsIHBhc3NpdmU6IGZhbHNlIH0pO1xuXG4gICAgLy8gRHJhZyBoYW5kbGluZyBmb3IgdGhlIGZsb2F0aW5nIGludGVuc2l0eS1wcm9maWxlIHBhbmVsLlxuICAgIHRoaXMucHJvZmlsZURyYWdNb3ZlTGlzdGVuZXIgPSAoZTogTW91c2VFdmVudCkgPT4ge1xuICAgICAgaWYgKHRoaXMucHJvZmlsZVBhbmVsRHJhZ2dpbmcpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgICAgICB0aGlzLnByb2ZpbGVQYW5lbFBvcyA9IHtcbiAgICAgICAgICAgIHg6IHRoaXMucHJvZmlsZVBhbmVsU3RhcnQueCArIChlLmNsaWVudFggLSB0aGlzLnByb2ZpbGVQYW5lbFN0YXJ0Lm14KSxcbiAgICAgICAgICAgIHk6IHRoaXMucHJvZmlsZVBhbmVsU3RhcnQueSArIChlLmNsaWVudFkgLSB0aGlzLnByb2ZpbGVQYW5lbFN0YXJ0Lm15KSxcbiAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy50b29sYmFyRHJhZ2dpbmcpIHtcbiAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgICAgICB0aGlzLnRvb2xiYXJQb3MgPSB7XG4gICAgICAgICAgICB4OiB0aGlzLnRvb2xiYXJTdGFydC54ICsgKGUuY2xpZW50WCAtIHRoaXMudG9vbGJhclN0YXJ0Lm14KSxcbiAgICAgICAgICAgIHk6IHRoaXMudG9vbGJhclN0YXJ0LnkgKyAoZS5jbGllbnRZIC0gdGhpcy50b29sYmFyU3RhcnQubXkpLFxuICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5wcm9maWxlRHJhZ1VwTGlzdGVuZXIgPSAoKSA9PiB7XG4gICAgICB0aGlzLnByb2ZpbGVQYW5lbERyYWdnaW5nID0gZmFsc2U7XG4gICAgICB0aGlzLnRvb2xiYXJEcmFnZ2luZyA9IGZhbHNlO1xuICAgIH07XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIHRoaXMucHJvZmlsZURyYWdNb3ZlTGlzdGVuZXIpO1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgdGhpcy5wcm9maWxlRHJhZ1VwTGlzdGVuZXIpO1xuXG4gICAgLy8gT24gYW55IHdpbmRvdyByZXNpemUsIHJlZmxvdyB0aGUgaW50ZW5zaXR5IGluc2V0IHRvIGl0cyAoZml4ZWQpIHBhbmVsIHNpemVcbiAgICAvLyBvbiB0aGUgbmV4dCBmcmFtZSwgb25jZSBsYXlvdXQgaGFzIHNldHRsZWQuIFdpdGhvdXQgdGhpcyB0aGUgaW5zZXQgY2FuIGJlXG4gICAgLy8gbGVmdCBhdCBhIHN0YWxlL3plcm8gc2l6ZSBieSBhIG1pZC1yZWZsb3cgcmVzaXplIGFuZCBzdG9wIHNob3dpbmcuXG4gICAgdGhpcy5wcm9maWxlUmVzaXplTGlzdGVuZXIgPSAoKSA9PiB7XG4gICAgICBpZiAoIXRoaXMuaGFzUHJvZmlsZXMpIHJldHVybjtcbiAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiB0aGlzLnJlbmRlckludGVuc2l0eUluc2V0KCkpO1xuICAgIH07XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsIHRoaXMucHJvZmlsZVJlc2l6ZUxpc3RlbmVyKTtcbiAgICB0aGlzLm9uVmlld1JlYWR5KCk7XG4gIH1cblxuICAvKiogVGhlIHBsb3QgZGl2IGV4aXN0cyBub3c6IHJ1biBhbiBpbWFnZS1sZXNzIGRyYXcgdGhhdCBhcnJpdmVkIGJlZm9yZSBpdCBkaWQuICovXG4gIHByaXZhdGUgb25WaWV3UmVhZHkoKTogdm9pZCB7XG4gICAgdGhpcy52aWV3UmVhZHkgPSB0cnVlO1xuICAgIGNvbnN0IHBlbmRpbmcgPSB0aGlzLnBlbmRpbmdTcGF0aWFsRHJhdztcbiAgICB0aGlzLnBlbmRpbmdTcGF0aWFsRHJhdyA9IG51bGw7XG4gICAgLy8gT25seSBpZiBpdCBpcyBzdGlsbCB0aGUgZGF0YXNldCBvbiBvZmZlciBhbmQgbm8gaW1hZ2UgaGFzIGFycml2ZWQgbWVhbndoaWxlLiBCeSBpZCwgbm90IGJ5XG4gICAgLy8gb2JqZWN0OiB0aGUgcG9ydCBtYXkgcmUtZW1pdCB0aGUgc2FtZSBkYXRhc2V0IGFzIGEgbmV3IG9iamVjdCAoYSBjb2xvdXItY29sdW1uIGNoYW5nZSBkb2VzKSxcbiAgICAvLyBhbmQgdGhlbiB0aGUgY3VycmVudCBvYmplY3QgaXMgdGhlIG9uZSB0byBkcmF3LlxuICAgIGNvbnN0IGN1cnJlbnQgPSB0aGlzLnNwYXRpYWxEYXRhc2V0O1xuICAgIGlmIChwZW5kaW5nICYmIGN1cnJlbnQgJiYgY3VycmVudC5pZCA9PT0gcGVuZGluZy5pZCAmJiAhdGhpcy5pbWFnZUluZm8pIHtcbiAgICAgIHZvaWQgdGhpcy5wbG90U3BhdGlhbFdpdGhvdXRJbWFnZShjdXJyZW50KTtcbiAgICB9XG4gIH1cblxuICBvblByb2ZpbGVQYW5lbERyYWdTdGFydChlOiBNb3VzZUV2ZW50KSB7XG4gICAgdGhpcy5wcm9maWxlUGFuZWxEcmFnZ2luZyA9IHRydWU7XG4gICAgdGhpcy5wcm9maWxlUGFuZWxTdGFydCA9IHtcbiAgICAgIG14OiBlLmNsaWVudFgsXG4gICAgICBteTogZS5jbGllbnRZLFxuICAgICAgeDogdGhpcy5wcm9maWxlUGFuZWxQb3MueCxcbiAgICAgIHk6IHRoaXMucHJvZmlsZVBhbmVsUG9zLnksXG4gICAgfTtcbiAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gIH1cblxuICAvKiogR3JhYiB0aGUgdG9vbGJhciBoYW5kbGU6IGRldGFjaCBpdCBpbnRvIGEgZmxvYXRpbmcgd2luZG93IChpZiBzdGlsbCBkb2NrZWQpXG4gICAqICBhbmQgc3RhcnQgZHJhZ2dpbmcuICovXG4gIG9uVG9vbGJhckRyYWdTdGFydChlOiBNb3VzZUV2ZW50KSB7XG4gICAgaWYgKCF0aGlzLnRvb2xiYXJGbG9hdGluZykge1xuICAgICAgdGhpcy50b29sYmFyRmxvYXRpbmcgPSB0cnVlO1xuICAgICAgdGhpcy50b29sYmFyUG9zID0geyB4OiA4LCB5OiA4IH07XG4gICAgfVxuICAgIHRoaXMudG9vbGJhckRyYWdnaW5nID0gdHJ1ZTtcbiAgICB0aGlzLnRvb2xiYXJTdGFydCA9IHtcbiAgICAgIG14OiBlLmNsaWVudFgsXG4gICAgICBteTogZS5jbGllbnRZLFxuICAgICAgeDogdGhpcy50b29sYmFyUG9zLngsXG4gICAgICB5OiB0aGlzLnRvb2xiYXJQb3MueSxcbiAgICB9O1xuICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgfVxuXG4gIC8qKiBTbmFwIHRoZSBmbG9hdGluZyB0b29sYmFyIGJhY2sgdG8gaXRzIGRvY2tlZCBwb3NpdGlvbiBhY3Jvc3MgdGhlIHRvcC4gKi9cbiAgZG9ja1Rvb2xiYXIoKSB7XG4gICAgdGhpcy50b29sYmFyRmxvYXRpbmcgPSBmYWxzZTtcbiAgfVxuXG4gIC8qKiBSZW5kZXIgdGhlIGludGVuc2l0eSBpbnNldCBjaGFydCBmcm9tIHRoZSBsYXRlc3QgcHJvZmlsZSBkYXRhLiBUaGUgYWN0dWFsXG4gICAqICBjaGFydGluZyBpcyBvd25lZCBieSB0aGUgdmlzdWFsaXplciBzZXJ2aWNlIOKAlCB0aGUgY29tcG9uZW50IGp1c3QgZGVjaWRlc1xuICAgKiAgd2hlbiAocHJvZmlsZSBtb2RlICsgYSBmcmVzaCBwcm9maWxlKS4gKi9cbiAgcHJpdmF0ZSByZW5kZXJJbnRlbnNpdHlJbnNldCgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaGFzUHJvZmlsZXMpIHJldHVybjtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlbmRlckludGVuc2l0eUluc2V0KHRoaXMuaW50ZW5zaXR5SW5zZXREaXYsIHRoaXMubGF0ZXN0UHJvZmlsZXMpO1xuICB9XG5cbiAgLyoqIFRvb2xiYXIgXCJJbnRlbnNpdHlcIiBncm91cDogYWRkIGFub3RoZXIgbGluZSBST0kgKG5leHQgYnJpZ2h0IGNvbG91cikuICovXG4gIGFzeW5jIGFkZFByb2ZpbGVMaW5lKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIFBhcmsgdGhlIGZsb2F0aW5nIGluc2V0IG5lYXIgdGhlIHBsb3QncyB0b3AtcmlnaHQgd2hlbiB0aGUgZmlyc3QgbGluZSBpc1xuICAgIC8vIGFkZGVkIChpdCdzIHBvc2l0aW9uOmZpeGVkLCBzbyB1c2Ugdmlld3BvcnQgY29vcmRzIGZyb20gdGhlIHBsb3QgcmVjdCkuXG4gICAgaWYgKCF0aGlzLmhhc1Byb2ZpbGVzKSB7XG4gICAgICBjb25zdCByZWN0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodGhpcy5wbG90RGl2TmFtZSk/LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgdGhpcy5wcm9maWxlUGFuZWxQb3MgPSByZWN0XG4gICAgICAgID8geyB4OiBNYXRoLm1heCgxMCwgcmVjdC5yaWdodCAtIDMwMCksIHk6IHJlY3QudG9wICsgMTAgfVxuICAgICAgICA6IHsgeDogMjAsIHk6IDcwIH07XG4gICAgfVxuICAgIC8vIEltYWdlIChPU0QpIG1vZGU6IFBsb3RseSBuZXZlciByZW5kZXJlZCwgc28gaXQgaGFzIG5vIHBpeGVsIGNhY2hlIC8gZXh0ZW50LlxuICAgIC8vIExvYWQgdGhlIGN1cnJlbnQgc2xpY2UncyBmcmFtZXMgZm9yIHNhbXBsaW5nICsgbGluZSBwbGFjZW1lbnQgZmlyc3QuXG4gICAgaWYgKHRoaXMuaXNJbWFnZVZpZXcgJiYgdGhpcy5pbWFnZUluZm8pIHtcbiAgICAgIGF3YWl0IHRoaXMucGxvdFNlcnZpY2UuZW5zdXJlSW50ZW5zaXR5U2FtcGxpbmcodGhpcy5pbWFnZUluZm8sIHRoaXMuekluZGV4KTtcbiAgICB9XG4gICAgY29uc3QgcmVnaW9uID0gdGhpcy5wbG90U2VydmljZS5nZXRJbnRlbnNpdHlDb250cm9scygpPy5hZGRQcm9maWxlTGluZSgpO1xuICAgIC8vIEF1dG8tc2VsZWN0IHRoZSBuZXcgbGluZSBvbiB0aGUgYWN0aXZlIGJhY2tlbmQgKFBsb3RseSBoYW5kbGVzIC8gT1NEXG4gICAgLy8gaGlnaGxpZ2h0KSBzbyBpdCdzIHJlYWR5IHRvIG1vdmUgb3IgZGVsZXRlIGltbWVkaWF0ZWx5LlxuICAgIGlmIChyZWdpb24pIHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2VsZWN0UmVnaW9uKHJlZ2lvbik7XG4gICAgICAvLyBPU0Qgb25seSBkcmF3cyBhIHNlbGVjdGVkIHJlZ2lvbidzIGhhbmRsZXMgaW4gYW4gZWRpdCBtb2RlLCBzbyBhIGxpbmUgaW5cbiAgICAgIC8vICdub25lJyBtb2RlIGxvb2tzIHVuc2VsZWN0ZWQuIFN3aXRjaCB0byAnc2VsZWN0JyBzbyB0aGUgbmV3IGxpbmUgc2hvd3NcbiAgICAgIC8vIGl0cyBlbmRwb2ludCBoYW5kbGVzIGFuZCBjYW4gYmUgZHJhZ2dlZC9kZWxldGVkIHJpZ2h0IGF3YXkuXG4gICAgICBpZiAodGhpcy5pc0ltYWdlVmlldyAmJiB0aGlzLmFjdGl2ZURyYWdNb2RlICE9PSAnc2VsZWN0Jykge1xuICAgICAgICB0aGlzLnRvZ2dsZURyYWdNb2RlKCdzZWxlY3QnKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICAvLyBBIGNvbnRyaWJ1dGVkIG1vZGUncyBzZXNzaW9uIGVuZHMgZmlyc3QsIHdoaWxlIHRoZSB2aWV3ZXIgaXQgZHJldyBvdmVyXG4gICAgLy8gc3RpbGwgZXhpc3RzLiBUaGUgcGFuZWwgZ29lcyB3aXRoIHRoaXMgdmlldywgc28gbm8gY2hhbmdlIGRldGVjdGlvbi5cbiAgICB0aGlzLmRlc3Ryb3lpbmcgPSB0cnVlO1xuICAgIHRoaXMucGxvdE1vZGVzLmRlYWN0aXZhdGUoKTtcbiAgICB0aGlzLnRvb2xEaWFsb2dzLmRlYWN0aXZhdGUoKTtcbiAgICAvLyBMZWF2ZSB0aGUgbGl2ZSBzZXQgc28gdGhlIG5leHQtb2xkZXN0IHZpc3VhbGl6ZXIgcGlja3MgdXAgdGhlIG91dGxldHMuXG4gICAgVmlzdWFsaXplckNvbXBvbmVudC5saXZlSW5zdGFuY2VzLmRlbGV0ZSh0aGlzKTtcbiAgICB0aGlzLnJldm9rZVZvbHVtZUltYWdlVXJscygpO1xuICAgIHRoaXMuc2NydWJiZXIuY2FuY2VsKCk7XG4gICAgdGhpcy5zYW1Qb2ludFN1Yi51bnN1YnNjcmliZSgpO1xuICAgIGlmICh0aGlzLnBsb3RDb250ZXh0TWVudUxpc3RlbmVyKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcignY29udGV4dG1lbnUnLCB0aGlzLnBsb3RDb250ZXh0TWVudUxpc3RlbmVyLCB0cnVlKTtcbiAgICB9XG4gICAgaWYgKHRoaXMua2V5ZG93bkxpc3RlbmVyKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcigna2V5ZG93bicsIHRoaXMua2V5ZG93bkxpc3RlbmVyKTtcbiAgICB9XG4gICAgaWYgKHRoaXMud2hlZWxMaXN0ZW5lcikge1xuICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3doZWVsJywgdGhpcy53aGVlbExpc3RlbmVyLCB0cnVlKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucHJvZmlsZURyYWdNb3ZlTGlzdGVuZXIpIHtcbiAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCB0aGlzLnByb2ZpbGVEcmFnTW92ZUxpc3RlbmVyKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucHJvZmlsZURyYWdVcExpc3RlbmVyKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsIHRoaXMucHJvZmlsZURyYWdVcExpc3RlbmVyKTtcbiAgICB9XG4gICAgaWYgKHRoaXMucHJvZmlsZVJlc2l6ZUxpc3RlbmVyKSB7XG4gICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcigncmVzaXplJywgdGhpcy5wcm9maWxlUmVzaXplTGlzdGVuZXIpO1xuICAgIH1cbiAgICBpZiAodGhpcy5pbnRlbnNpdHlQcm9maWxlU3ViKSB7XG4gICAgICB0aGlzLmludGVuc2l0eVByb2ZpbGVTdWIudW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMudmlld3BvcnRDaGFuZ2VTdWIpIHtcbiAgICAgIHRoaXMudmlld3BvcnRDaGFuZ2VTdWIudW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgdGhpcy51bnN1Yi5uZXh0KCk7XG4gICAgdGhpcy51bnN1Yi5jb21wbGV0ZSgpO1xuICAgIGlmICh0aGlzLnByZXZpZXdTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMucHJldmlld1N1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5zcGF0aWFsRGF0YXNldFN1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5zcGF0aWFsRGF0YXNldFN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5wbG90V2lkdGhTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMucGxvdFdpZHRoU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmltYWdlTG9hZGluZ1N1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5pbWFnZUxvYWRpbmdTdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaW1nTG9hZGluZ01lc3NhZ2VTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMuaW1nTG9hZGluZ01lc3NhZ2VTdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaXNab29tU3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLmlzWm9vbVN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5hdXRvc2NhbGVTdWJzY3JpcHRpb24pIHtcbiAgICAgIHRoaXMuYXV0b3NjYWxlU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmZpbGVuYW1lU3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLmZpbGVuYW1lU3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICAgIHRoaXMucGxvdFNlcnZpY2UudW5zdWJzY3JpYmUoKTtcbiAgfVxuXG4gIHB1YmxpYyBoYXNSZWdpb25zKCk6IGJvb2xlYW4ge1xuICAgIC8vIFVzZSB0aGUgY29udHJhY3QncyBmcmFtZXdvcmstbmV1dHJhbCByZWdpb24gYWNjZXNzb3IsIG5vdCB0aGUgcmF3XG4gICAgLy8gUGxvdGx5LXNoYXBlZCBnZXRTaGFwZXMoKSDigJQgdGhlIGNvbXBvbmVudCBvbmx5IG5lZWRzIHRvIGtub3cgd2hldGhlciBhbnlcbiAgICAvLyByZWdpb24gZXhpc3RzLCBhbmQgbXVzdCBub3QgZGVwZW5kIG9uIGEgYmFja2VuZCdzIHdpcmUgZm9ybWF0LlxuICAgIHJldHVybiB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKS5sZW5ndGggPiAwO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoaXMgbWV0aG9kIGlzIGNhbGxlZCBpbiBtYWluRmFjYWRlLmNvbmZpZ1JlcXVlc3QoKSAtIERPIE5PVCBERUxFVEVcbiAgICovXG4gIHB1YmxpYyBnZXRSZWdpb25Qb2x5Z29ucygpOiBQb2x5Z29uW10ge1xuICAgIHJldHVybiB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvblBvbHlnb25zKCk7XG4gIH1cblxuICAvKipcbiAgICogU2VsZWN0IHRoZSBjb2xvciBzY2FsZSB0eXBlOiBjYWxsZWQgYnkgdGhlIGNvbG9yIHNjYWxlIHNlbGVjdCBidXR0b25cbiAgICogQHBhcmFtIGV2ZW50XG4gICAqL1xuICB0b2dnbGVSZXZlcnNlU2NhbGUoKSB7XG4gICAgdGhpcy5yZXZlcnNlc2NhbGUgPSAhdGhpcy5yZXZlcnNlc2NhbGU7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRSZXZlcnNlU2NhbGUodGhpcy5yZXZlcnNlc2NhbGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlbGVjdCBhIGNvbG9yIG1hcDogY2FsbGVkIGJ5IHRoZSBjb2xvciBtYXAgZHJvcGRvd24gYnV0dG9uXG4gICAqIEBwYXJhbSBjb2xvcm1hcE5vZGVcbiAgICovXG4gIHNlbGVjdENvbG9ybWFwKGNvbG9ybWFwTm9kZTogVHJlZU5vZGUpIHtcbiAgICBpZiAoIWNvbG9ybWFwTm9kZS5jaGlsZHJlbikge1xuICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRDb2xvcm1hcChjb2xvcm1hcE5vZGUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBPcGVuIHRoZSBDaGFubmVscyAmIEhpc3RvZ3JhbSBkaWFsb2cgKHRvb2xiYXIgYnV0dG9uKS4gKi9cbiAgb3BlbkNoYW5uZWxIaXN0b2dyYW0oKSB7XG4gICAgdGhpcy5zaG93Q2hhbm5lbEhpc3RvZ3JhbSA9IHRydWU7XG4gIH1cblxuICAvKiogVG9vbGJhciDihpIgb3BlbiB0aGUgc3BhdGlhbC1vbWljcyBjb250cm9scy4gKi9cbiAgb3BlblNwYXRpYWxDb250cm9scygpOiB2b2lkIHtcbiAgICB0aGlzLnNob3dTcGF0aWFsQ29udHJvbHMgPSB0cnVlO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIFJlZ2lvbiBFZGl0b3IgZGlhbG9nICh0b29sYmFyIGJ1dHRvbikuIFRoZSBlZGl0b3Igc3RheXMgbGlua2VkIHRvXG4gICAqICB0aGlzIHBsb3R0aW5nIGluc3RhbmNlIHRocm91Z2ggdGhlIHJvb3QgUmVnaW9uU3RvcmUgc2luZ2xldG9uLCBzbyBpdCB3b3Jrc1xuICAgKiAgdGhlIHNhbWUgYXMgdGhlIGZvcm1lciByaWdodC1wYW5lbCBSZWdpb25zIHRhYi4gKi9cbiAgb3BlblJlZ2lvbkVkaXRvcigpIHtcbiAgICAvLyBTaXplIHRoZSBkaWFsb2cgdG8gbWF0Y2ggdGhlIGhvc3QncyBjb25maWd1cmVkIGVsZW1lbnQgKGUuZy4gdGhlIGFwcCdzXG4gICAgLy8gcmlnaHQgcGFuZWwpIGF0IG9wZW4gdGltZS4gTWVhc3VyZWQgb25jZSDigJQgaXQgaW50ZW50aW9uYWxseSBkb2VzIG5vdFxuICAgIC8vIHRyYWNrIGxhdGVyIGJyb3dzZXIvc3BsaXQgcmVzaXplcy4gRmFsbHMgYmFjayB0byBhIHF1YXJ0ZXIgb2YgdGhlIHBhZ2VcbiAgICAvLyB3aGVuIG5vIHNlbGVjdG9yIGlzIGNvbmZpZ3VyZWQgb3IgdGhlIGVsZW1lbnQgaXNuJ3QgcHJlc2VudC5cbiAgICBjb25zdCBzZWxlY3RvciA9IHRoaXMudml6Q29uZmlnPy5yZWdpb25FZGl0b3JXaWR0aFNlbGVjdG9yO1xuICAgIGNvbnN0IHdpZHRoID0gc2VsZWN0b3JcbiAgICAgID8gZG9jdW1lbnQucXVlcnlTZWxlY3RvcjxIVE1MRWxlbWVudD4oc2VsZWN0b3IpPy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS53aWR0aFxuICAgICAgOiB1bmRlZmluZWQ7XG4gICAgdGhpcy5yZWdpb25FZGl0b3JXaWR0aCA9IHdpZHRoICYmIHdpZHRoID4gMCA/IGAke01hdGgucm91bmQod2lkdGgpfXB4YCA6ICcyNXZ3JztcbiAgICB0aGlzLnNob3dSZWdpb25FZGl0b3IgPSB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGxlZCBieSB0aGUgc3RhY2svc2luZ2xlIGltYWdlIGRyb3Bkb3duIGJ1dHRvblxuICAgKiBAcGFyYW0gc2hvd3N0YWNrXG4gICAqL1xuICBzZWxlY3RTdGFja09wdGlvbihzZWxlY3RlZFN0YWNrT3B0aW9uOiBhbnkpIHtcbiAgICBjb25zdCBzaG93c3RhY2sgPSBzZWxlY3RlZFN0YWNrT3B0aW9uLnZhbCA9PT0gJ3RydWUnO1xuICAgIHRoaXMuc2VsZWN0ZWRTdGFja09wdGlvbiA9IHNlbGVjdGVkU3RhY2tPcHRpb247XG4gICAgY29uc29sZS5sb2coJ3NlbGVjdGVkIHN0YWNrIG9wdGlvbicgKyBKU09OLnN0cmluZ2lmeSh0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24pKTtcbiAgICB0aGlzLnN0YWNrTG9hZGluZyA9IHNob3dzdGFjaztcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyghc2hvd3N0YWNrKTtcbiAgICAvLyBTdGFjayBtb2RlIGlzIGFsd2F5cyBhIDJEIGhlYXRtYXAg4oCUIHJlc2V0IHN1cmZhY2UgbW9kZSBpZiBhY3RpdmVcbiAgICBpZiAoIXRoaXMuaXNIZWF0bWFwKSB7XG4gICAgICB0aGlzLmlzSGVhdG1hcCA9IHRydWU7XG4gICAgICB0aGlzLnBsb3RUeXBlID0gUGxvdFR5cGUuSEVBVE1BUDtcbiAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gUGxvdFR5cGUuSEVBVE1BUDtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UGxvdFR5cGUodGhpcy5wbG90VHlwZSk7XG4gICAgfVxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U2hvd1N0YWNrKHNob3dzdGFjayk7XG4gIH1cblxuICB1cGRhdGVaSW5kZXgodXJscz86IHN0cmluZ1tdKSB7XG4gICAgaWYgKHRoaXMuekluZGV4ID4gdGhpcy5tYXhJbmRleCkge1xuICAgICAgdGhpcy56SW5kZXggPSB0aGlzLm1heEluZGV4O1xuICAgIH1cbiAgICBpZiAodGhpcy56SW5kZXggPCAwKSB7XG4gICAgICB0aGlzLnpJbmRleCA9IDA7XG4gICAgfVxuICAgIGlmICh1cmxzKSB7XG4gICAgICBpZiAodGhpcy56SW5kZXggPiB1cmxzPy5sZW5ndGgpIHtcbiAgICAgICAgdGhpcy56SW5kZXggPSAwO1xuICAgICAgICB0aGlzLm1heEluZGV4ID0gdXJscy5sZW5ndGggLSAxO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFpJbmRleCh0aGlzLnpJbmRleCk7XG4gIH1cblxuICAvKipcbiAgICogTGl2ZSB6LXNsaWNlIHNjcnViIGZvciB0aGUgSW1hZ2UgdmlldzogdGhlIHJlbmRlcmVyIHN3YXBzIHRoZSBkaXNwbGF5ZWRcbiAgICogc2xpY2UgaW4gcGxhY2UgKG5vIHJlLW1vdW50KSwgc28gZHJhZ2dpbmcgdGhlIHNsaWRlciBzdGVwcyB0aHJvdWdoIHRoZVxuICAgKiBzdGFjayB0aGUgc2FtZSB3YXkgdGhlIGhlYXRtYXAgZnJhbWUgc2xpZGVyIGRvZXMuXG4gICAqL1xuICBvblpTbGlkZSh6OiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoeiA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy56SW5kZXggPSB6O1xuICAgIHRoaXMuc2NydWJiZXIuY29tbWl0KHopO1xuICAgIC8vIEtlZXAgdGhlIGludGVuc2l0eSBpbnNldCBpbiBzeW5jIHdpdGggdGhlIGRpc3BsYXllZCBzbGljZSAoSW1hZ2UvT1NEIG1vZGUsXG4gICAgLy8gd2hlcmUgdGhlIHByb2ZpbGUgc2FtcGxlciBpcyBmZWQgZnJvbSB0aGUgbG9hZGVkIHByZXZpZXcgZnJhbWVzKS5cbiAgICBpZiAodGhpcy5oYXNQcm9maWxlcyAmJiB0aGlzLmlzSW1hZ2VWaWV3ICYmIHRoaXMuaW1hZ2VJbmZvKSB7XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLmVuc3VyZUludGVuc2l0eVNhbXBsaW5nKHRoaXMuaW1hZ2VJbmZvLCB6KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTGl2ZSAoZGVib3VuY2VkKSBzY3J1YiB3aGlsZSBkcmFnZ2luZyB0aGUgSW1hZ2UtdmlldyB6LXNsaWRlcjogdXBkYXRlIHRoZVxuICAgKiBzbGljZSBhcyB0aGUgdXNlciBkcmFncywgYnV0IGNvYWxlc2NlIHJhcGlkIGNoYW5nZXMgc28gd2UgZG9uJ3QgZmlyZSBhXG4gICAqIHNsaWNlIHN3YXAgb24gZXZlcnkgcGl4ZWwuIFRoZSBmaW5hbCB2YWx1ZSBhbHNvIGxhbmRzIHZpYSBvblpTbGlkZSAob25TbGlkZUVuZCkuXG4gICAqL1xuICBvblpTY3J1Yih6OiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoeiA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy56SW5kZXggPSB6O1xuICAgIHRoaXMuc2NydWJiZXIuc2NydWIoeik7XG4gIH1cblxuICAvKipcbiAgICogS2V5Ym9hcmQgc3RhY2sgbmF2aWdhdGlvbjog4oaQL+KGkiBzdGVwIHRoZSB6LXNsaWNlIGluIHRoZSBJbWFnZSB2aWV3LCB0aGUgc2FtZVxuICAgKiB3YXkgdGhlIHNsaWRlciBkb2VzLiBPbmx5IGFjdGl2ZSBmb3IgYSBsb2FkZWQgc3RhY2sgaW4gSW1hZ2UgdmlldywgYW5kXG4gICAqIGlnbm9yZWQgd2hpbGUgYSBmb3JtIGZpZWxkIGlzIGZvY3VzZWQgc28gdHlwaW5nIGlzbid0IGhpamFja2VkLiBVcC9Eb3duIGFyZVxuICAgKiBsZWZ0IHRvIE9wZW5TZWFkcmFnb24gKHBhbm5pbmcpLlxuICAgKi9cbiAgQEhvc3RMaXN0ZW5lcignd2luZG93OmtleWRvd24nLCBbJyRldmVudCddKVxuICBvbktleWRvd24oZTogS2V5Ym9hcmRFdmVudCk6IHZvaWQge1xuICAgIGlmIChlLmtleSAhPT0gJ0Fycm93TGVmdCcgJiYgZS5rZXkgIT09ICdBcnJvd1JpZ2h0JykgcmV0dXJuO1xuICAgIGlmICghdGhpcy5pbWFnZUluZm8/LmlzU3RhY2sgfHwgIXRoaXMuaXNJbWFnZVZpZXcpIHJldHVybjtcbiAgICBjb25zdCB0ID0gZS50YXJnZXQgYXMgSFRNTEVsZW1lbnQgfCBudWxsO1xuICAgIGlmICh0ICYmICgvXihJTlBVVHxURVhUQVJFQXxTRUxFQ1QpJC8udGVzdCh0LnRhZ05hbWUpIHx8IHQuaXNDb250ZW50RWRpdGFibGUpKSByZXR1cm47XG4gICAgLy8gVGhlIHNsaWNlIHNsaWRlciBoYW5kbGVzIGFycm93cyBuYXRpdmVseSB3aGVuIGZvY3VzZWQgKGFsc28gYSArMSBzdGVwKSDigJRcbiAgICAvLyBza2lwIGhlcmUgc28gd2UgZG9uJ3QgZG91YmxlLXN0ZXAgaXQuXG4gICAgaWYgKHQgJiYgKHQuZ2V0QXR0cmlidXRlKCdyb2xlJykgPT09ICdzbGlkZXInIHx8IHQuY2xvc2VzdCgnLnAtc2xpZGVyJykpKSByZXR1cm47XG4gICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIHRoaXMuc3RlcFNsaWNlKGUua2V5ID09PSAnQXJyb3dSaWdodCcgPyAxIDogLTEpO1xuICB9XG5cbiAgLyoqIE1vdmUgdGhlIGRpc3BsYXllZCBzbGljZSBieSBgZGVsdGFgLCBjbGFtcGVkIHRvIHRoZSBzdGFjayBib3VuZHMuICovXG4gIHN0ZXBTbGljZShkZWx0YTogbnVtYmVyKTogdm9pZCB7XG4gICAgY29uc3QgbmV4dCA9IE1hdGgubWluKHRoaXMubWF4SW5kZXgsIE1hdGgubWF4KDAsIHRoaXMuekluZGV4ICsgZGVsdGEpKTtcbiAgICBpZiAobmV4dCA9PT0gdGhpcy56SW5kZXgpIHJldHVybjtcbiAgICB0aGlzLm9uWlNsaWRlKG5leHQpO1xuICB9XG5cbiAgcmVsb2FkQW5kUGxvdCgpIHtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyh0cnVlKTtcbiAgICAvLyBSZS1kcml2ZSB0aGUgcmVuZGVyIHBpcGVsaW5lIGZyb20gdGhlIGN1cnJlbnQgaW1hZ2UgaW5mbyAodGhlIHNvdXJjZSBvZlxuICAgIC8vIHRydXRoLCB3aXRoIHJlYWwgdXJscy9maWxlTmFtZSkuIERvbid0IHVzZSBwbG90U2VydmljZS5yZWxvYWRBbmRQbG90KCk6XG4gICAgLy8gdGhhdCByZWJ1aWxkcyB0aGUgaW5mbyBmcm9tIHRoZSByZW5kZXJlcidzIGludGVybmFsIHN0YXRlLCB3aGljaCBjYW4gYmVcbiAgICAvLyBzdGFsZSAoZS5nLiBhZnRlciBzd2l0Y2hpbmcgcGxvdCB0eXBlIEltYWdlIC0+IEhlYXRtYXApIGFuZCByZS1lbWl0XG4gICAgLy8gZW1wdHkvb2xkIHVybHMsIGZhaWxpbmcgdG8gaGFuZCB0aGUgZGl2IG92ZXIgdG8gdGhlIG5ldyByZW5kZXJlci5cbiAgICBpZiAodGhpcy5pbWFnZUluZm8pIHtcbiAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKHRoaXMuaW1hZ2VJbmZvKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuc3BhdGlhbERhdGFzZXQgJiYgKGlzU3BhdGlhbE9taWNzKHRoaXMucGxvdFR5cGUpIHx8IGlzU3BhdGlhbE9taWNzM2QodGhpcy5wbG90VHlwZSkpKSB7XG4gICAgICAvLyBBIHNwYXRpYWwgZGF0YXNldCB0aGF0IGJyaW5ncyBubyBpbWFnZSwgb3BlbmVkIHdpdGggbm8gaW1hZ2UgbG9hZGVkIChhIGhvc3Qnc1xuICAgICAgLy8gZmlyc3Qgdmlldyk6IHRoZXJlIGlzIG5vIGltYWdlIGluZm8gdG8gcmUtZHJpdmUsIHNvIGRyYXcgdGhlIHNwYXRpYWwgbW9kZSBpdHNlbGYuXG4gICAgICB2b2lkIHRoaXMucGxvdFNwYXRpYWxXaXRob3V0SW1hZ2UodGhpcy5zcGF0aWFsRGF0YXNldCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVsb2FkQW5kUGxvdCgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEcmF3IGEgc3BhdGlhbCBtb2RlIHdpdGggbm8gaW1hZ2UgYmVoaW5kIGl0OiB0aGUgb2JzZXJ2YXRpb25zIGFsb25lLCBmcmFtZWQgb24gdGhlaXIgb3duXG4gICAqIGV4dGVudCwgYXMgdGhlIHNlcUZJU0ggZXhhbXBsZSBzaG93cyB0aGVtLiBUaGUgaW1hZ2UgaW5mbyBpcyBhIHBsYWNlaG9sZGVyIG5hbWluZyB0aGVcbiAgICogZGF0YXNldCwgc28gcmVnaW9ucyBkcmF3biBoZXJlIGFyZSBrZXB0IHBlciBkYXRhc2V0IGxpa2UgYW55IGltYWdlJ3MuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHBsb3RTcGF0aWFsV2l0aG91dEltYWdlKGRhdGFzZXQ6IFNwYXRpYWxEYXRhc2V0KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0aGlzLnZpZXdSZWFkeSkge1xuICAgICAgLy8gQSBob3N0IHRoYXQgY3JlYXRlcyB0aGUgdmlzdWFsaXplciBBRlRFUiBwdWJsaXNoaW5nIHRoZSBkYXRhc2V0IChqaXQtdWkgb3BlbmluZyBhIGZpbGVcbiAgICAgIC8vIGZyb20gaXRzIHRyZWUpIGhhcyBpdCByZXBsYXllZCBpbnRvIG5nT25Jbml0LCBiZWZvcmUgdGhlIHRlbXBsYXRlIGFuZCBpdHMgcGxvdCBkaXZcbiAgICAgIC8vIGV4aXN0OyBkcmF3aW5nIG5vdyBmaW5kcyBubyB0YXJnZXQuIERyYXcgb25jZSB0aGUgdmlldyBpcyByZWFkeSAob25WaWV3UmVhZHkpLlxuICAgICAgdGhpcy5wZW5kaW5nU3BhdGlhbERyYXcgPSBkYXRhc2V0O1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBUaGUgc2FtZSBnZW5lcmF0aW9uIGFzIHRoZSBpbWFnZSBwaXBlbGluZTogYSBuZXdlciBpbWFnZSBvciBkYXRhc2V0IHN1cGVyc2VkZXMgdGhpc1xuICAgIC8vIGRyYXcsIGFuZCBhIHN1cGVyc2VkZWQgZHJhdyBuZWl0aGVyIHJlcG9ydHMgbm9yIHJlbGVhc2VzIHRoZSBuZXdlciBvbmUncyBsb2FkaW5nIHN0YXRlLlxuICAgIGNvbnN0IHRva2VuID0gKyt0aGlzLnJlbmRlclRva2VuO1xuICAgIGNvbnN0IGRpdiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHRoaXMucGxvdERpdk5hbWUpO1xuICAgIGNvbnN0IGluZm86IElJbWFnZUluZm8gPSB7XG4gICAgICBpc0dyYXlzY2FsZTogZmFsc2UsIHRydWVJbWFnZVNpemU6IFswLCAwXSwgdXJsczogW10sIGlzU3RhY2s6IGZhbHNlLCBzaG93U3RhY2s6IGZhbHNlLFxuICAgICAgc2NhbGVSYXRpbzogdHJ1ZSwgZmlsZU5hbWU6IGBzcGF0aWFsOiR7ZGF0YXNldC5pZH1gLCBpbWFnZU1ldGE6IFtdLFxuICAgIH07XG4gICAgbGV0IGZhaWx1cmU6IHVua25vd24gPSBudWxsO1xuICAgIHRyeSB7XG4gICAgICAvLyBBIGJhY2tlbmQgdGhhdCBjYW5ub3QgZHJhdyAobm8gV2ViR1BVLCBubyBwbG90IHRhcmdldCkgcmVzb2x2ZXMgZmFsc2UgcmF0aGVyIHRoYW4gdGhyb3dpbmcuXG4gICAgICBpZiAoIShhd2FpdCB0aGlzLnBsb3RTZXJ2aWNlLnBsb3QodGhpcy5wbG90RGl2TmFtZSwgbnVsbCwgaW5mbywgZGl2Py5vZmZzZXRIZWlnaHQgfHwgNTAwLCB0aGlzLnBsb3RUeXBlKSkpIHtcbiAgICAgICAgZmFpbHVyZSA9ICd0aGUgcmVuZGVyZXIgY291bGQgbm90IHN0YXJ0JztcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGZhaWx1cmUgPSBlcnI7XG4gICAgfVxuICAgIGlmICh0b2tlbiAhPT0gdGhpcy5yZW5kZXJUb2tlbikgcmV0dXJuO1xuICAgIGlmIChmYWlsdXJlKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ1t2aXN1YWxpemVyXSBjb3VsZCBub3QgZHJhdyB0aGUgc3BhdGlhbCBkYXRhc2V0JywgZmFpbHVyZSk7XG4gICAgICBjb25zdCBlID0gZmFpbHVyZSBhcyB7IG1lc3NhZ2U/OiBzdHJpbmcgfTtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiB0aGlzLnZpekFsZXJ0VG9hc3RLZXksXG4gICAgICAgIHNldmVyaXR5OiAnZXJyb3InLFxuICAgICAgICBzdW1tYXJ5OiAnQ291bGQgbm90IGRyYXcgdGhlIGRhdGFzZXQnLFxuICAgICAgICAvLyBBbiBpbWFnZS1sZXNzIGRhdGFzZXQgaGFzIG5vIHRpc3N1ZSBpbWFnZSB0byBmYWxsIGJhY2sgb24sIHNvIHNheSBzbyByYXRoZXIgdGhhblxuICAgICAgICAvLyBsZWF2ZSBhbiBlbXB0eSBjYW52YXMgdGhhdCBsb29rcyBmaW5pc2hlZC5cbiAgICAgICAgZGV0YWlsOiBgJHtkYXRhc2V0Lm5hbWUgPz8gZGF0YXNldC5pZH06ICR7ZT8ubWVzc2FnZSA/PyBTdHJpbmcoZmFpbHVyZSl9LmAsXG4gICAgICB9KTtcbiAgICB9XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgfVxuXG4gIGNhbmNlbExvYWRpbmcoKSB7XG4gICAgLy8gU3RvcCBhbnkgaW4tZmxpZ2h0IGZyYW1lIHN0cmVhbWluZyAobmFwYXJpLWpzIHZvbHVtZS9zdXJmYWNlIHByZWxvYWQpIHNvIHRoZSBmZXRjaCBsb29wc1xuICAgIC8vIGFjdHVhbGx5IGFib3J0IOKAlCBjbGVhcmluZyB0aGUgZmxhZyBhbG9uZSBvbmx5IHJvdXRlZCB0byBQbG90bHkgYW5kIGxlZnQgbmFwYXJpIGZldGNoaW5nLlxuICAgIHRoaXMucGxvdFNlcnZpY2UuY2FuY2VsTG9hZGluZz8uKCk7XG4gICAgdGhpcy5zdGFja0xvYWRpbmcgPSBmYWxzZTtcbiAgICB0aGlzLmltZ0xvYWRpbmcgPSBmYWxzZTtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyhmYWxzZSk7XG4gICAgdGhpcy56SW5kZXggPSAwO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0WkluZGV4KHRoaXMuekluZGV4KTtcbiAgICB0aGlzLnJ1bm5pbmcgPSBmYWxzZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFN0YWNrTG9hZGluZyhmYWxzZSk7XG4gIH1cblxuICBkb3dubG9hZEltYWdlKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZG93bmxvYWRJbWFnZSgpO1xuICB9XG5cbiAgYXV0b3NjYWxlSW1hZ2UoKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5hdXRvc2NhbGUoKTtcbiAgfVxuXG4gIC8qKiBUb29sYmFyIHBpeGVsL3Ntb290aCB0b2dnbGU6IGZsaXAgc21vb3RoaW5nIGFuZCBhcHBseSB0byB0aGUgYWN0aXZlIGJhY2tlbmQuICovXG4gIG9uVG9nZ2xlSW1hZ2VTbW9vdGhpbmcoKTogdm9pZCB7XG4gICAgdGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQgPSAhdGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQ7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRJbWFnZVNtb290aGluZ0VuYWJsZWQodGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQpO1xuICB9XG5cbiAgcmVzZXRBeGVzKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UucmVzZXRBeGVzKCk7XG4gIH1cblxuICB0b2dnbGVEcmFnTW9kZShtb2RlOiBzdHJpbmcpIHtcbiAgICAvLyBBIHRvb2wgcGlja2VkIGJ5IHRoZSB1c2VyIGlzIGFsd2F5cyB0aGUgcGxhaW4gdG9vbCwgbmV2ZXIgYSBwbG90IG1vZGUnc1xuICAgIC8vIGJydXNoIGNsYXNzLlxuICAgIHRoaXMuYnJ1c2hDbGFzcyA9IG51bGw7XG4gICAgLy8gVG9nZ2xlIG9mZiBpZiB0aGUgc2FtZSBtb2RlIGlzIHJlLXNlbGVjdGVkLlxuICAgIHRoaXMuYXBwbHlEcmFnTW9kZSh0aGlzLmFjdGl2ZURyYWdNb2RlID09PSBtb2RlID8gbnVsbCA6IG1vZGUpO1xuICB9XG5cbiAgLyoqIEFybSBgbW9kZWAgKG9yIG5vdGhpbmcpIGFjcm9zcyBldmVyeSB0b29sLCBrZWVwaW5nIHRoZSB0b29sYmFyIGluIHN0ZXAuICovXG4gIHByaXZhdGUgYXBwbHlEcmFnTW9kZShtb2RlOiBzdHJpbmcgfCBudWxsKSB7XG4gICAgdGhpcy5hY3RpdmVEcmFnTW9kZSA9IG1vZGU7XG4gICAgY29uc3QgYWN0aXZlID0gdGhpcy5hY3RpdmVEcmFnTW9kZTtcbiAgICAvLyBSZWNvcmQgdGhlIGFybWVkIHRvb2wgaW4gdGhlIHNoYXJlZCBzZXNzaW9uIHN0b3JlLlxuICAgIHRoaXMuc2Vzc2lvbi5zZXRBY3RpdmVUb29sKGFjdGl2ZSk7XG5cbiAgICAvLyBSZWdpb24gZHJhdy9zZWxlY3QgcnVuIHRocm91Z2ggdGhlIHJlbmRlcmVyJ3MgcmVnaW9uIG92ZXJsYXkuXG4gICAgdGhpcy5wbG90U2VydmljZVxuICAgICAgLmdldFJlZ2lvbk92ZXJsYXkoKVxuICAgICAgPy5zZXRNb2RlKHRoaXMuaXNSZWdpb25Nb2RlKGFjdGl2ZSkgPyAoYWN0aXZlIGFzIFJlZ2lvblRvb2xNb2RlKSA6ICdub25lJyk7XG5cbiAgICAvLyBWaWV3cG9ydCBkcmFnIG1vZGVzIChwYW4vYm94LXpvb20pLiBSZWdpb24gbW9kZXMgb3duIHRoZSBkcmFnIG1vZGUgdmlhXG4gICAgLy8gdGhlIG92ZXJsYXksIHNvIGRvbid0IGFsc28gc2V0IG9uZSBoZXJlLlxuICAgIGlmICghdGhpcy5pc1JlZ2lvbk1vZGUoYWN0aXZlKSkge1xuICAgICAgY29uc3Qgdmlld3BvcnREcmFnID0gYWN0aXZlID09PSAncGFuJyB8fCBhY3RpdmUgPT09ICd6b29tJztcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0RHJhZ01vZGUodmlld3BvcnREcmFnID8gYWN0aXZlIDogZmFsc2UpO1xuICAgIH1cblxuICAgIC8vIE9uLWNhbnZhcyB0b29sIG92ZXJsYXlzLlxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0Wm9vbVRvQm94TW9kZShhY3RpdmUgPT09ICd6b29tVG9Cb3gnKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFdhbmRNb2RlKGFjdGl2ZSA9PT0gJ3dhbmQnLCB7IHNlbnNpdGl2aXR5OiB0aGlzLndhbmRTZW5zaXRpdml0eSB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldEJydXNoTW9kZShhY3RpdmUgPT09ICdicnVzaCcsIHsgc2l6ZTogdGhpcy5icnVzaFNpemUsIC4uLnRoaXMuYnJ1c2hDbGFzcyB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNhbVBvaW50TW9kZShhY3RpdmUgPT09ICdzYW1Qb2ludCcpO1xuICAgIC8vIExlYXZpbmcgcG9pbnQgbW9kZSBkaXNtaXNzZXMgYW55IGxpbmdlcmluZyBzdGF0dXMgdG9hc3QuXG4gICAgaWYgKGFjdGl2ZSAhPT0gJ3NhbVBvaW50JykgdGhpcy5oaWRlU2FtVG9hc3QoKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFZlcnRleEVyYXNlck1vZGUoYWN0aXZlID09PSAnZXJhc2VWZXJ0ZXgnKTtcbiAgICBpZiAoYWN0aXZlID09PSAnZXJhc2VWZXJ0ZXgnKSB7XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFZlcnRleEVyYXNlclJhZGl1cyh0aGlzLnZlcnRleEVyYXNlclJhZGl1cyk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFJlZ2lvbiBkcmF3L3NlbGVjdC9lZGl0IG1vZGVzIHJvdXRlZCB0aHJvdWdoIHRoZSByZWdpb24gb3ZlcmxheS4gVGhlXG4gICAqICBUaGUgdmVydGV4IHRvb2xzIChkcmF3cG9seWdvbi9hZGRwb2ludC9kZWxldGVwb2ludC9tb3ZlKSBhcmVcbiAgICogIGhhbmRsZWQgYnkgdGhlIE9wZW5TZWFkcmFnb24gb3ZlcmxheTsgUGxvdGx5J3Mgb3ZlcmxheSBtYXBzIHRoZW0gdG8gbm8tb3AuICovXG4gIHByaXZhdGUgaXNSZWdpb25Nb2RlKG1vZGU6IHN0cmluZyB8IG51bGwpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgbW9kZSA9PT0gJ2RyYXdyZWN0JyB8fFxuICAgICAgbW9kZSA9PT0gJ2RyYXdjbG9zZWRwYXRoJyB8fFxuICAgICAgbW9kZSA9PT0gJ2RyYXdvcGVucGF0aCcgfHxcbiAgICAgIG1vZGUgPT09ICdzZWxlY3QnIHx8XG4gICAgICBtb2RlID09PSAnZHJhd3BvbHlnb24nIHx8XG4gICAgICBtb2RlID09PSAnYWRkcG9pbnQnIHx8XG4gICAgICBtb2RlID09PSAnZGVsZXRlcG9pbnQnIHx8XG4gICAgICBtb2RlID09PSAnbW92ZSdcbiAgICApO1xuICB9XG5cbiAgLyoqIExpdmUtdXBkYXRlIHRoZSBpc29zdXJmYWNlIGFzIHRoZSByYW5nZSBzbGlkZXIgbW92ZXMuIFRoZSBjb250cm9sIGlzIG9ubHlcbiAgICogIGF2YWlsYWJsZSB3aGVuIHRoZSBhY3RpdmUgYmFja2VuZCByZW5kZXJzIGlzb3N1cmZhY2VzIChJU09TVVJGQUNFIG1vZGUpLiAqL1xuICBvbklzb1JhbmdlQ2hhbmdlKHZhbHVlczogbnVtYmVyW10gfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoIXZhbHVlcyB8fCB2YWx1ZXMubGVuZ3RoIDwgMikgcmV0dXJuO1xuICAgIHRoaXMuaXNvUmFuZ2UgPSB2YWx1ZXM7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRJc29zdXJmYWNlQ29udHJvbHMoKT8uc2V0SXNvUmFuZ2UodmFsdWVzWzBdLCB2YWx1ZXNbMV0pO1xuICB9XG5cbiAgb25XYW5kU2Vuc2l0aXZpdHlDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8ICFOdW1iZXIuaXNGaW5pdGUodmFsdWUpKSByZXR1cm47XG4gICAgdGhpcy53YW5kU2Vuc2l0aXZpdHkgPSB2YWx1ZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFdhbmRPcHRpb25zKHsgc2Vuc2l0aXZpdHk6IHZhbHVlIH0pO1xuICB9XG5cbiAgb25CcnVzaFNpemVDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8ICFOdW1iZXIuaXNGaW5pdGUodmFsdWUpKSByZXR1cm47XG4gICAgdGhpcy5icnVzaFNpemUgPSB2YWx1ZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldEJydXNoT3B0aW9ucyh7IHNpemU6IHZhbHVlIH0pO1xuICB9XG5cbiAgb25WZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8ICFOdW1iZXIuaXNGaW5pdGUodmFsdWUpKSByZXR1cm47XG4gICAgdGhpcy52ZXJ0ZXhFcmFzZXJSYWRpdXMgPSB2YWx1ZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFZlcnRleEVyYXNlclJhZGl1cyh2YWx1ZSk7XG4gIH1cblxuICB6b29tSW4oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS56b29tSW4oKTtcbiAgfVxuXG4gIHpvb21PdXQoKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS56b29tT3V0KCk7XG4gIH1cblxuICBkZWxldGVSZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5kZWxldGVBY3RpdmVTaGFwZSgpO1xuICB9XG5cbiAgLyoqIFVuZG8gdGhlIG1vc3QgcmVjZW50IHJlZ2lvbiBhY3Rpb24gKGppdC11aSM4NSkuIFVwIHRvIDEwIHN0ZXBzIGJhY2suICovXG4gIHVuZG9SZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS51bmRvKCk7XG4gIH1cblxuICAvKiogUmVkbyB0aGUgbW9zdCByZWNlbnRseSB1bmRvbmUgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gKi9cbiAgcmVkb1JlZ2lvbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlZG8oKTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBSZWdpb24gc2V0LW9wZXJhdGlvbnMgb24gdGhlIGN1cnJlbnQgc2VsZWN0aW9uIChqaXQtdWkjODUpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBUaGUgY3VycmVudGx5LXNlbGVjdGVkIHJlZ2lvbnMgKGxpdmUgc3RvcmUgaW5zdGFuY2VzKS4gKi9cbiAgcHJpdmF0ZSBnZXQgc2VsZWN0ZWRSZWdpb25zKCk6IFJlZ2lvbltdIHtcbiAgICBjb25zdCBhbGwgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKTtcbiAgICByZXR1cm4gdGhpcy5zZWxlY3RlZEluZGljZXNcbiAgICAgIC5tYXAoKGkpID0+IGFsbFtpXSlcbiAgICAgIC5maWx0ZXIoKHIpOiByIGlzIFJlZ2lvbiA9PiAhIXIpO1xuICB9XG5cbiAgLyoqIFJlZ2lvbnMgZWxpZ2libGUgZm9yIHNldC1vcHM6IGNsb3NlZCBhcmVhcyAocmVjdCAvIGNsb3NlZCBwb2x5Z29uIC9cbiAgICogIG11bHRpLXBvbHlnb24pLCBleGNsdWRpbmcgaW50ZW5zaXR5LXByb2ZpbGUgbGluZXMuICovXG4gIHByaXZhdGUgb3BFbGlnaWJsZShyZWdpb25zOiBSZWdpb25bXSk6IFJlZ2lvbltdIHtcbiAgICByZXR1cm4gcmVnaW9ucy5maWx0ZXIoKHIpID0+IHIua2luZCAhPT0gJ3Byb2ZpbGUnICYmIChcbiAgICAgIHIuYm91bmRzIGluc3RhbmNlb2YgUmVjdGFuZ2xlIHx8XG4gICAgICByLmJvdW5kcyBpbnN0YW5jZW9mIE11bHRpUG9seWdvbiB8fFxuICAgICAgKHIuYm91bmRzIGluc3RhbmNlb2YgUG9seWdvbiAmJiByLmJvdW5kcy5jbG9zZWQgIT09IGZhbHNlKVxuICAgICkpO1xuICB9XG5cbiAgLyoqIEltYWdlIHBpeGVsIGRpbWVuc2lvbnMgZm9yIHRoZSByYXN0ZXIgb3BzOyBmYWxscyBiYWNrIHRvIHRoZSBzZWxlY3Rpb24nc1xuICAgKiAgZXh0ZW50IHdoZW4gdGhlIGltYWdlIHNpemUgaXNuJ3Qga25vd24gKGtlZXBzIGNsYW1waW5nIHNhbmUpLiAqL1xuICBwcml2YXRlIG9wSW1hZ2VEaW1zKHJlZ2lvbnM6IFJlZ2lvbltdKTogeyB3OiBudW1iZXI7IGg6IG51bWJlciB9IHtcbiAgICBsZXQgW3csIGhdID0gdGhpcy5pbWFnZUluZm8/LnRydWVJbWFnZVNpemUgPz8gWzAsIDBdO1xuICAgIGlmICghKHcgPiAwKSB8fCAhKGggPiAwKSkge1xuICAgICAgbGV0IG1heFggPSAwLCBtYXhZID0gMDtcbiAgICAgIGNvbnN0IHNjYW4gPSAoeHM6IG51bWJlcltdLCB5czogbnVtYmVyW10pID0+IHtcbiAgICAgICAgZm9yIChjb25zdCB4IG9mIHhzKSBtYXhYID0gTWF0aC5tYXgobWF4WCwgeCk7XG4gICAgICAgIGZvciAoY29uc3QgeSBvZiB5cykgbWF4WSA9IE1hdGgubWF4KG1heFksIHkpO1xuICAgICAgfTtcbiAgICAgIGZvciAoY29uc3QgciBvZiByZWdpb25zKSB7XG4gICAgICAgIGNvbnN0IGIgPSByLmJvdW5kcztcbiAgICAgICAgaWYgKGIgaW5zdGFuY2VvZiBSZWN0YW5nbGUpIHNjYW4oW2IueCArIGIud2lkdGhdLCBbYi55ICsgYi5oZWlnaHRdKTtcbiAgICAgICAgZWxzZSBpZiAoYiBpbnN0YW5jZW9mIFBvbHlnb24pIHNjYW4oYi54cG9pbnRzLCBiLnlwb2ludHMpO1xuICAgICAgICBlbHNlIGlmIChiIGluc3RhbmNlb2YgTXVsdGlQb2x5Z29uKSBmb3IgKGNvbnN0IHAgb2YgYi5wb2x5Z29ucykgc2NhbihwLnhwb2ludHMsIHAueXBvaW50cyk7XG4gICAgICB9XG4gICAgICB3ID0gTWF0aC5tYXgodywgTWF0aC5jZWlsKG1heFgpICsgMik7XG4gICAgICBoID0gTWF0aC5tYXgoaCwgTWF0aC5jZWlsKG1heFkpICsgMik7XG4gICAgfVxuICAgIHJldHVybiB7IHcsIGggfTtcbiAgfVxuXG4gIC8qKiBUcnVlIHdoZW4g4omlMiBlbGlnaWJsZSByZWdpb25zIGFyZSBzZWxlY3RlZCAoTWVyZ2UgYXZhaWxhYmxlKS4gKi9cbiAgZ2V0IGNhbk1lcmdlUmVnaW9ucygpOiBib29sZWFuIHsgcmV0dXJuIHRoaXMub3BFbGlnaWJsZSh0aGlzLnNlbGVjdGVkUmVnaW9ucykubGVuZ3RoID49IDI7IH1cbiAgLyoqIFRydWUgd2hlbiBhbnkgc2VsZWN0ZWQgcmVnaW9uIGlzIG11bHRpLXBhcnQgKFVuZ3JvdXAgYXZhaWxhYmxlKS4gKi9cbiAgZ2V0IGNhblVuZ3JvdXBSZWdpb25zKCk6IGJvb2xlYW4geyByZXR1cm4gdGhpcy5zZWxlY3RlZFJlZ2lvbnMuc29tZSgocikgPT4gdGhpcy5yZWdpb25PcHMuY2FuVW5ncm91cChyKSk7IH1cbiAgLyoqIFRydWUgd2hlbiDiiaUxIGVsaWdpYmxlIHJlZ2lvbiBpcyBzZWxlY3RlZCAoSW52ZXJzZSAvIFNpbXBsaWZ5IGF2YWlsYWJsZSkuICovXG4gIGdldCBoYXNFbGlnaWJsZVNlbGVjdGlvbigpOiBib29sZWFuIHsgcmV0dXJuIHRoaXMub3BFbGlnaWJsZSh0aGlzLnNlbGVjdGVkUmVnaW9ucykubGVuZ3RoID49IDE7IH1cblxuICAvKiogU2VsZWN0IGV2ZXJ5IHJlZ2lvbiBvbiB0aGUgaW1hZ2UgKGV4Y2x1ZGVzIGludGVuc2l0eS1wcm9maWxlIGxpbmVzKS4gKi9cbiAgc2VsZWN0QWxsUmVnaW9ucygpOiB2b2lkIHtcbiAgICBjb25zdCBhbGwgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKTtcbiAgICBjb25zdCBpbmRpY2VzOiBudW1iZXJbXSA9IFtdO1xuICAgIGFsbC5mb3JFYWNoKChyLCBpKSA9PiB7IGlmIChyLmtpbmQgIT09ICdwcm9maWxlJykgaW5kaWNlcy5wdXNoKGkpOyB9KTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNlbGVjdGVkU2hhcGVJbmRpY2VzKGluZGljZXMpO1xuICB9XG5cbiAgLyoqIE1lcmdlIHRoZSBzZWxlY3RlZCByZWdpb25zIGludG8gYSBzaW5nbGUgKHBvc3NpYmx5IG11bHRpLXBhcnQpIHJlZ2lvbi4gKi9cbiAgbWVyZ2VSZWdpb25zKCk6IHZvaWQge1xuICAgIGNvbnN0IHNlbCA9IHRoaXMub3BFbGlnaWJsZSh0aGlzLnNlbGVjdGVkUmVnaW9ucyk7XG4gICAgaWYgKHNlbC5sZW5ndGggPCAyKSByZXR1cm47XG4gICAgY29uc3QgeyB3LCBoIH0gPSB0aGlzLm9wSW1hZ2VEaW1zKHNlbCk7XG4gICAgY29uc3QgbWVyZ2VkID0gdGhpcy5yZWdpb25PcHMubWVyZ2Uoc2VsLCB3LCBoKTtcbiAgICBpZiAobWVyZ2VkKSB0aGlzLnJlcGxhY2VSZWdpb25zKHNlbCwgW21lcmdlZF0pO1xuICB9XG5cbiAgLyoqIFNwbGl0IGVhY2ggc2VsZWN0ZWQgbXVsdGktcGFydCByZWdpb24gaW50byBvbmUgcmVnaW9uIHBlciBwYXJ0LiAqL1xuICB1bmdyb3VwUmVnaW9ucygpOiB2b2lkIHtcbiAgICBjb25zdCBzZWwgPSB0aGlzLnNlbGVjdGVkUmVnaW9ucy5maWx0ZXIoKHIpID0+IHRoaXMucmVnaW9uT3BzLmNhblVuZ3JvdXAocikpO1xuICAgIGlmIChzZWwubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgY29uc3QgcmVzdWx0czogUmVnaW9uW10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IHIgb2Ygc2VsKSByZXN1bHRzLnB1c2goLi4udGhpcy5yZWdpb25PcHMudW5ncm91cChyKSk7XG4gICAgdGhpcy5yZXBsYWNlUmVnaW9ucyhzZWwsIHJlc3VsdHMpO1xuICB9XG5cbiAgLyoqIFJlcGxhY2UgdGhlIHNlbGVjdGlvbiB3aXRoIGl0cyBpbnZlcnNlIGluc2lkZSB0aGUgaW1hZ2UgcmVjdGFuZ2xlLiAqL1xuICBpbnZlcnNlUmVnaW9ucygpOiB2b2lkIHtcbiAgICBjb25zdCBzZWwgPSB0aGlzLm9wRWxpZ2libGUodGhpcy5zZWxlY3RlZFJlZ2lvbnMpO1xuICAgIGlmIChzZWwubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgY29uc3QgeyB3LCBoIH0gPSB0aGlzLm9wSW1hZ2VEaW1zKHNlbCk7XG4gICAgY29uc3QgaW52ID0gdGhpcy5yZWdpb25PcHMuaW52ZXJzZShzZWwsIHcsIGgpO1xuICAgIGlmICghaW52KSB7XG4gICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7XG4gICAgICAgIGtleTogdGhpcy5yZXN1bHRUb2FzdEtleSxcbiAgICAgICAgc2V2ZXJpdHk6ICd3YXJuJywgc3VtbWFyeTogJ0ludmVyc2UnLFxuICAgICAgICBkZXRhaWw6ICdOb3RoaW5nIHRvIGludmVydCDigJQgc2VsZWN0IG9uZSBvciBtb3JlIGNsb3NlZCByZWdpb25zIGZpcnN0LicsXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5yZXBsYWNlUmVnaW9ucyhzZWwsIFtpbnZdKTtcbiAgfVxuXG4gIC8qKiBEb3VnbGFz4oCTUGV1Y2tlciBzaW1wbGlmeSBlYWNoIHNlbGVjdGVkIHJlZ2lvbiBieSBgdGhyZXNob2xkUHhgLiAqL1xuICBzaW1wbGlmeVJlZ2lvbnModGhyZXNob2xkUHg6IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IHNlbCA9IHRoaXMub3BFbGlnaWJsZSh0aGlzLnNlbGVjdGVkUmVnaW9ucyk7XG4gICAgaWYgKHNlbC5sZW5ndGggPT09IDApIHJldHVybjtcbiAgICB0aGlzLnJlcGxhY2VSZWdpb25zKHNlbCwgc2VsLm1hcCgocikgPT4gdGhpcy5yZWdpb25PcHMuc2ltcGxpZnkociwgdGhyZXNob2xkUHgpKSk7XG4gICAgdGhpcy5kaXNwbGF5U2ltcGxpZnlEaWFsb2cgPSBmYWxzZTtcbiAgfVxuXG4gIC8qKiBPcGVuIHRoZSBjdXN0b20tdGhyZXNob2xkIHNpbXBsaWZ5IGRpYWxvZy4gKi9cbiAgb3BlblNpbXBsaWZ5RGlhbG9nKCk6IHZvaWQgeyB0aGlzLmRpc3BsYXlTaW1wbGlmeURpYWxvZyA9IHRydWU7IH1cblxuICAvKipcbiAgICogQ29tbWl0IGEgc2V0LW9wOiBkcm9wIHRoZSBgcmVtb3ZlYCByZWdpb25zLCBhcHBlbmQgYGFkZGAsIGFuZCBzZWxlY3QgdGhlXG4gICAqIHJlc3VsdHMuIEdvZXMgdGhyb3VnaCBzZXRSZWdpb25zIHNvIGl0J3MgdW5kby10cmFja2VkIGFuZCByZS1yZW5kZXJlZDtcbiAgICogcHJvZmlsZSBsaW5lcyBhbmQgdW5zZWxlY3RlZCByZWdpb25zIGFyZSBwcmVzZXJ2ZWQuXG4gICAqL1xuICBwcml2YXRlIHJlcGxhY2VSZWdpb25zKHJlbW92ZTogUmVnaW9uW10sIGFkZDogUmVnaW9uW10pOiB2b2lkIHtcbiAgICBpZiAoYWRkLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuICAgIGNvbnN0IHJlbW92ZVNldCA9IG5ldyBTZXQocmVtb3ZlKTtcbiAgICBjb25zdCBrZXB0ID0gdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25zKCkuZmlsdGVyKChyKSA9PiAhcmVtb3ZlU2V0LmhhcyhyKSk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRSZWdpb25zKFsuLi5rZXB0LCAuLi5hZGRdKTsgLy8gbWludHMgaWRzIG9uIGBhZGRgLCByZWNvcmRzIHVuZG9cbiAgICBjb25zdCBzdG9yZWQgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKTtcbiAgICBjb25zdCBzZWwgPSBhZGQubWFwKChyKSA9PiBzdG9yZWQuaW5kZXhPZihyKSkuZmlsdGVyKChpKSA9PiBpID49IDApO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U2VsZWN0ZWRTaGFwZUluZGljZXMoc2VsKTtcbiAgfVxuXG4gIC8qKiBCb3gtcHJvbXB0ZWQgU0FNIHNlZ21lbnRhdGlvbiBvZiB0aGUgZHJhd24gcmVjdGFuZ2xlcyAoaml0LXVpIzkwKS4gQSBzdGlja3lcbiAgICogIGBzYW1gIHRvYXN0IHNob3dzIGxpdmUgc3RhdHVzICsgYSBkb3dubG9hZCBwcm9ncmVzcyBiYXIgKGZpcnN0IHJ1biBwdWxscyB0aGVcbiAgICogIGVuY29kZXIsIH4xNzAgTUIpOyBpdCBzdGF5cyBvcGVuIHVudGlsIHRoZSBydW4gZmluaXNoZXMgKGJhciBoaXRzIDEwMCUpLiAqL1xuICBhc3luYyBzZWdtZW50UmVnaW9ucygpIHtcbiAgICBhd2FpdCB0aGlzLnJ1blNlZ21lbnRXaXRoVG9hc3QoJ1NBTScsIHRoaXMuc2FtVG9vbCwgKCkgPT4gdGhpcy5wbG90U2VydmljZS5zZWdtZW50UmVjdGFuZ2xlcygpKTtcbiAgfVxuXG4gIC8qKiBBdXRvLXNlZ21lbnQgY2VsbHMgaW5zaWRlIGVhY2ggZHJhd24gcmVjdGFuZ2xlIHdpdGggY2VsbHBvc2UtU0FNLCBjbGllbnQtc2lkZVxuICAgKiAgKGppdC11aSM5MCkuIEVhY2ggYm94IGlzIGNyb3BwZWQgKGJyb3dzZXIgc2xpZGUtY3JvcCkgdGhlbiBydW4gdGhyb3VnaCB0aGVcbiAgICogIGNlbGxwb3NlLWpzIG1vZGVsOyB0aGUgc2FtZSBzdGlja3kgYHNhbWAgdG9hc3QgKyBwcm9ncmVzcyBiYXIgaXMgcmV1c2VkLiAqL1xuICBhc3luYyBzZWdtZW50Q2VsbHBvc2UoKSB7XG4gICAgYXdhaXQgdGhpcy5ydW5TZWdtZW50V2l0aFRvYXN0KCdDZWxscG9zZScsIHRoaXMuY2VsbFNlZ21lbnRUb29sLCAoKSA9PlxuICAgICAgdGhpcy5wbG90U2VydmljZS5zZWdtZW50UmVjdGFuZ2xlc0NlbGxwb3NlKCksXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQYXJhbWV0ZXJzIGN1cnJlbnRseSBib3VuZCBieSB0aGUgZ2VuZXJpYyBwYXJhbWV0ZXIgZGlhbG9nLCBmb3Igd2hpY2hldmVyXG4gICAqIHRvb2wgaXMgb3Blbi4gU2VlZGVkIGxhemlseTogYSB0b29sJ3MgZGVmYXVsdHMgYXJlIG9ubHkgYXNrZWQgZm9yIHdoZW4gdGhlXG4gICAqIHVzZXIgZmlyc3QgdG91Y2hlcyBpdCwgc28gcmVnaXN0ZXJpbmcgYSB0b29sIGNvc3RzIG5vdGhpbmcgdW50aWwgdXNlZC5cbiAgICovXG4gIHBhcmFtc0Zvcih0b29sSWQ6IHN0cmluZyk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICBjb25zdCB0b29sID0gdGhpcy5jb250cmlidXRlZFRvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IHRvb2xJZCk7XG4gICAgaWYgKCF0b29sKSByZXR1cm4ge307XG4gICAgaWYgKCF0aGlzLnRvb2xQYXJhbXNbdG9vbElkXSkge1xuICAgICAgLy8gVGhyb3VnaCBgc2VlZFBhcmFtc2AsIG5vdCBgZGVmYXVsdFBhcmFtc2AgZGlyZWN0bHk6IHRoZSBhY3RpdmVcbiAgICAgIC8vIGNoZWNrcG9pbnQncyBvd24gYFRvb2xNb2RlbE9wdGlvbi5kZWZhdWx0c2AgaGF2ZSB0byBiZSBtZXJnZWQgb24gdG9wLlxuICAgICAgLy8gQ2FsbGluZyBgZGVmYXVsdFBhcmFtc2AgaGVyZSBieXBhc3NlZCB0aGVtLCBzbyB0aGUgdmVyeSBmaXJzdCBydW4gb2YgYVxuICAgICAgLy8gdG9vbCB1c2VkIHRoZSB0b29sJ3MgYmFzZWxpbmUgdGhyZXNob2xkcyBhbmQgdGlsaW5nIHJhdGhlciB0aGFuIHRoZVxuICAgICAgLy8gbW9kZWwncyDigJQgYW5kIGl0IG9ubHkgY29ycmVjdGVkIGl0c2VsZiBvbmNlIHRoZSB1c2VyIHN3aXRjaGVkXG4gICAgICAvLyBjaGVja3BvaW50cyBvciBoaXQgUmVzZXQuIEV2ZXJ5IGVudHJ5IHBvaW50IG5vdyBnb2VzIHRocm91Z2ggb25lIHBhdGguXG4gICAgICB0aGlzLnRvb2xQYXJhbXNbdG9vbElkXSA9IHRoaXMuc2VlZFBhcmFtcyh0b29sLCB0aGlzLm1vZGVsSWRGb3IodG9vbCkpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy50b29sUGFyYW1zW3Rvb2xJZF0hO1xuICB9XG5cbiAgLyoqIEFjdGl2ZSBjaGVja3BvaW50IGZvciBhIHRvb2wsIGZhbGxpbmcgYmFjayB0byB0aGUgdG9vbCdzIG93biBkZWZhdWx0LiAqL1xuICBtb2RlbElkRm9yKHRvb2w6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy50b29sTW9kZWxJZHNbdG9vbC5pZF0gPz8gdG9vbC5kZWZhdWx0TW9kZWxJZCgpO1xuICB9XG5cbiAgLyoqIFN3aXRjaGluZyBjaGVja3BvaW50IHJlLXNlZWRzIHRoZSBwYXJhbWV0ZXJzLCBzaW5jZSB0aGUgZGVmYXVsdHMgYmVsb25nIHRvXG4gICAqICB0aGUgbW9kZWwgcmF0aGVyIHRoYW4gdG8gdGhlIHRvb2wuICovXG4gIG9uVG9vbE1vZGVsQ2hhbmdlKGU6IHsgdG9vbElkOiBzdHJpbmc7IG1vZGVsSWQ6IHN0cmluZyB9KTogdm9pZCB7XG4gICAgY29uc3QgdG9vbCA9IHRoaXMuY29udHJpYnV0ZWRUb29scy5maW5kKCh0KSA9PiB0LmlkID09PSBlLnRvb2xJZCk7XG4gICAgaWYgKCF0b29sKSByZXR1cm47XG4gICAgLy8gUmVhc3NpZ25lZCByYXRoZXIgdGhhbiBtdXRhdGVkIHNvIHRoZSB0b29sYmFyJ3MgbmdPbkNoYW5nZXMgc2VlcyBhIG5ld1xuICAgIC8vIHJlZmVyZW5jZSBhbmQgcmVidWlsZHMgdGhhdCB0b29sJ3MgbW9kZWwgbWVudSB3aXRoIHRoZSBjaGVjayBtYXJrIG1vdmVkLlxuICAgIHRoaXMudG9vbE1vZGVsSWRzID0geyAuLi50aGlzLnRvb2xNb2RlbElkcywgW2UudG9vbElkXTogZS5tb2RlbElkIH07XG4gICAgdG9vbC5vbk1vZGVsQ2hhbmdlPy4oZS5tb2RlbElkKTtcbiAgICB0aGlzLnRvb2xQYXJhbXNbZS50b29sSWRdID0gdGhpcy5zZWVkUGFyYW1zKHRvb2wsIGUubW9kZWxJZCk7XG4gIH1cblxuICAvKiogQSB0b29sJ3MgZGVmYXVsdHMgZm9yIGEgY2hlY2twb2ludCwgd2l0aCB0aGF0IGNoZWNrcG9pbnQncyBvd24gb3ZlcnJpZGVzXG4gICAqICBhcHBsaWVkIG9uIHRvcCDigJQgcGVyLW1vZGVsIGRlZmF1bHRzIGJlYXQgcGVyLXRvb2wgb25lcy4gKi9cbiAgcHJpdmF0ZSBzZWVkUGFyYW1zKHRvb2w6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uLCBtb2RlbElkOiBzdHJpbmcpOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gICAgY29uc3QgbW9kZWwgPSB0b29sLm1vZGVscygpLmZpbmQoKG0pID0+IG0uaWQgPT09IG1vZGVsSWQpO1xuICAgIHJldHVybiB7IC4uLnRvb2wuZGVmYXVsdFBhcmFtcyhtb2RlbElkKSwgLi4uKG1vZGVsPy5kZWZhdWx0cyA/PyB7fSkgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBOYXJyb3cgYSBwYXJhbSBzcGVjIGZvciB0aGUgdGVtcGxhdGUuXG4gICAqXG4gICAqIGAqbmdTd2l0Y2hDYXNlYCBkb2VzIG5vdCBuYXJyb3cgYSBkaXNjcmltaW5hdGVkIHVuaW9uIGluIEFuZ3VsYXIncyB0ZW1wbGF0ZVxuICAgKiB0eXBlIGNoZWNrZXIgdGhlIHdheSBhIGBzd2l0Y2hgIGRvZXMgaW4gVHlwZVNjcmlwdCwgc28gdGhlIHRlbXBsYXRlIGFza3MgZm9yXG4gICAqIHRoZSB2YXJpYW50IGl0IGhhcyBhbHJlYWR5IG1hdGNoZWQgb24uXG4gICAqL1xuICBudW1iZXJQYXJhbShwOiBUb29sUGFyYW1TcGVjKTogTnVtYmVyUGFyYW1TcGVjIHtcbiAgICByZXR1cm4gcCBhcyBOdW1iZXJQYXJhbVNwZWM7XG4gIH1cblxuICBzZWxlY3RQYXJhbShwOiBUb29sUGFyYW1TcGVjKTogU2VsZWN0UGFyYW1TcGVjIHtcbiAgICByZXR1cm4gcCBhcyBTZWxlY3RQYXJhbVNwZWM7XG4gIH1cblxuICBvcGVuVG9vbFBhcmFtcyh0b29sSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMucGFyYW1zRm9yKHRvb2xJZCk7IC8vIHNlZWQgYmVmb3JlIHRoZSBkaWFsb2cgYmluZHMgdG8gaXRcbiAgICB0aGlzLm9wZW5QYXJhbXNUb29sSWQgPSB0b29sSWQ7XG4gIH1cblxuICAvKiogVGhlIHRvb2wgd2hvc2UgcGFyYW1ldGVyIGRpYWxvZyBpcyBvcGVuLCBmb3IgdGhlIGdlbmVyaWMgZGlhbG9nJ3MgaGVhZGVyXG4gICAqICBhbmQgZmllbGQgbGlzdC4gKi9cbiAgZ2V0IG9wZW5QYXJhbXNUb29sKCk6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5jb250cmlidXRlZFRvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IHRoaXMub3BlblBhcmFtc1Rvb2xJZCk7XG4gIH1cblxuICByZXNldFRvb2xQYXJhbXModG9vbElkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCB0b29sID0gdGhpcy5jb250cmlidXRlZFRvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IHRvb2xJZCk7XG4gICAgaWYgKHRvb2wpIHRoaXMudG9vbFBhcmFtc1t0b29sSWRdID0gdGhpcy5zZWVkUGFyYW1zKHRvb2wsIHRoaXMubW9kZWxJZEZvcih0b29sKSk7XG4gIH1cblxuICAvKiogUnVuIGEgY29udHJpYnV0ZWQgdG9vbCBvdmVyIHRoZSBjdXJyZW50IHZpZXcuIE5vIHByb21wdDogdGhlc2Ugc3dlZXAgdGhlXG4gICAqICB3aG9sZSB2aWV3IHJhdGhlciB0aGFuIGJlaW5nIHBvaW50ZWQgYXQgc29tZXRoaW5nLiBSZXVzZXMgdGhlIHNoYXJlZFxuICAgKiAgc2VnbWVudGF0aW9uIHRvYXN0IHNvIHByb2dyZXNzIHJlYWRzIHRoZSBzYW1lIGFzIHRoZSBTQU0vY2VsbHBvc2UgdG9vbHMuICovXG4gIGFzeW5jIHJ1blRvb2wodG9vbElkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB0b29sID0gdGhpcy5jb250cmlidXRlZFRvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IHRvb2xJZCk7XG4gICAgaWYgKCF0b29sKSByZXR1cm47XG4gICAgY29uc3QgcGFyYW1zID0geyAuLi50aGlzLnBhcmFtc0Zvcih0b29sSWQpLCBtb2RlbElkOiB0aGlzLm1vZGVsSWRGb3IodG9vbCkgfTtcbiAgICBhd2FpdCB0aGlzLnJ1blNlZ21lbnRXaXRoVG9hc3QodG9vbC5sYWJlbCwgdG9vbC5wcm9ncmVzcywgKCkgPT5cbiAgICAgIHRvb2wucnVuKHRoaXMucGxvdFNlcnZpY2UsIHBhcmFtcyksXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBTaGFyZWQgZHJpdmVyIGZvciB0aGUgYm94LXByb21wdCBzZWdtZW50IHRvb2xzOiB3aXJlcyB0aGUgdG9vbCdzIHN0YXR1cyArXG4gICAqICBkb3dubG9hZCBwcm9ncmVzcyBpbnRvIHRoZSBzdGlja3kgYHNhbWAgdG9hc3QsIHJ1bnMgYG9wYCwgYW5kIHJlcG9ydHMgdGhlXG4gICAqICByZWdpb24gY291bnQuIEtlZXBzIHRoZSB0b2FzdCBvcGVuIHVudGlsIHRoZSBydW4gc2V0dGxlcyAoYmFyIGhpdHMgMTAwJSkuICovXG4gIHByaXZhdGUgYXN5bmMgcnVuU2VnbWVudFdpdGhUb2FzdChcbiAgICBsYWJlbDogc3RyaW5nLFxuICAgIC8vIE9ic2VydmFibGVzIHJhdGhlciB0aGFuIEJlaGF2aW9yU3ViamVjdHM6IGEgY29udHJpYnV0ZWQgdG9vbCBpbXBsZW1lbnRzXG4gICAgLy8gVG9vbFByb2dyZXNzLCBhbmQgcmVxdWlyaW5nIGEgY29uY3JldGUgc3ViamVjdCB3b3VsZCBmb3JjZSBldmVyeSBwbHVnaW5cbiAgICAvLyB0byBleHBvc2UgaXRzIGludGVybmFscyBqdXN0IHRvIGRyaXZlIHRoaXMgdG9hc3QuXG4gICAgdG9vbDogeyBzdGF0dXMkOiBPYnNlcnZhYmxlPHN0cmluZz47IHByb2dyZXNzJDogT2JzZXJ2YWJsZTxudW1iZXI+IH0sXG4gICAgb3A6ICgpID0+IFByb21pc2U8bnVtYmVyPixcbiAgKSB7XG4gICAgdGhpcy5zYW1TdGF0dXMgPSAnU3RhcnRpbmfigKYnO1xuICAgIHRoaXMuc2FtUHJvZ3Jlc3MgPSAwO1xuICAgIHRoaXMuc2FtRG93bmxvYWRpbmcgPSBmYWxzZTtcbiAgICB0aGlzLnNhbUJ1c3kgPSB0cnVlO1xuICAgIGNvbnN0IHBzdWIgPSB0b29sLnByb2dyZXNzJC5zdWJzY3JpYmUoKGYpID0+IHtcbiAgICAgIHRoaXMuc2FtRG93bmxvYWRpbmcgPSBmID49IDAgJiYgZiA8IDE7XG4gICAgICBpZiAoZiA+PSAwKSB0aGlzLnNhbVByb2dyZXNzID0gTWF0aC5taW4oMTAwLCBNYXRoLnJvdW5kKGYgKiAxMDApKTtcbiAgICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgICB9KTtcbiAgICAvLyBUcmFja2VkIGhlcmUgcmF0aGVyIHRoYW4gcmVhZCBvZmYgdGhlIHN1YmplY3QgYXQgdGhlIGVuZDogYHRvb2xgIGlzIG9ubHlcbiAgICAvLyBhbiBPYnNlcnZhYmxlIHBhaXIgbm93LCBzbyB0aGVyZSBpcyBubyBgLnZhbHVlYCB0byBzYW1wbGUgb25jZSB0aGUgcnVuXG4gICAgLy8gc2V0dGxlcy4gVGhlIGxhc3Qgbm9uLWVtcHR5IHN0YXR1cyBpcyB3aGF0IHRoZSByZXN1bHQgdG9hc3QgcmVwb3J0cy5cbiAgICBsZXQgbGFzdFN0YXR1cyA9ICcnO1xuICAgIGNvbnN0IHNzdWIgPSB0b29sLnN0YXR1cyQuc3Vic2NyaWJlKChtKSA9PiB7XG4gICAgICBpZiAobSkge1xuICAgICAgICBsYXN0U3RhdHVzID0gbTtcbiAgICAgICAgdGhpcy5zYW1TdGF0dXMgPSBtO1xuICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5zaG93U2FtVG9hc3QobGFiZWwpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBuID0gYXdhaXQgb3AoKTtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiB0aGlzLnJlc3VsdFRvYXN0S2V5LFxuICAgICAgICBzZXZlcml0eTogbiA+IDAgPyAnc3VjY2VzcycgOiAnd2FybicsXG4gICAgICAgIHN1bW1hcnk6IGxhYmVsLFxuICAgICAgICBkZXRhaWw6IGxhc3RTdGF0dXMgfHwgKG4gPiAwID8gYEFkZGVkICR7bn0gcmVnaW9uKHMpLmAgOiAnTm8gcmVnaW9ucyBhZGRlZC4nKSxcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiB0aGlzLnJlc3VsdFRvYXN0S2V5LFxuICAgICAgICBzZXZlcml0eTogJ2Vycm9yJywgc3VtbWFyeTogYCR7bGFiZWx9IGZhaWxlZGAsIGRldGFpbDogU3RyaW5nKGUpLFxuICAgICAgfSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHBzdWIudW5zdWJzY3JpYmUoKTtcbiAgICAgIHNzdWIudW5zdWJzY3JpYmUoKTtcbiAgICAgIHRoaXMuaGlkZVNhbVRvYXN0KCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFNob3cgdGhlIHNoYXJlZCBzdGlja3kgYHNhbWAgdG9hc3Qgb25jZSAoaWRlbXBvdGVudCDigJQgcmUtYWRkaW5nIHdvdWxkIHN0YWNrXG4gICAqICBhIG5ldyB0b2FzdCBvbiBldmVyeSBwb2ludCBjbGljaykuICovXG4gIHByaXZhdGUgc2hvd1NhbVRvYXN0KHN1bW1hcnk6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNhbVRvYXN0U2hvd24pIHJldHVybjtcbiAgICB0aGlzLnNhbVRvYXN0U2hvd24gPSB0cnVlO1xuICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHsga2V5OiB0aGlzLnNhbVRvYXN0S2V5LCBzdGlja3k6IHRydWUsIHNldmVyaXR5OiAnaW5mbycsIHN1bW1hcnkgfSk7XG4gIH1cblxuICAvKiogRGlzbWlzcyB0aGUgc2hhcmVkIGBzYW1gIHRvYXN0IGFuZCByZXNldCBpdHMgcHJvZ3Jlc3Mvc3Bpbm5lciBzdGF0ZS4gKi9cbiAgcHJpdmF0ZSBoaWRlU2FtVG9hc3QoKTogdm9pZCB7XG4gICAgdGhpcy5zYW1Ub2FzdFNob3duID0gZmFsc2U7XG4gICAgdGhpcy5zYW1CdXN5ID0gZmFsc2U7XG4gICAgdGhpcy5zYW1Eb3dubG9hZGluZyA9IGZhbHNlO1xuICAgIHRoaXMuc2FtUHJvZ3Jlc3MgPSAwO1xuICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuY2xlYXIodGhpcy5zYW1Ub2FzdEtleSk7XG4gICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICB9XG5cbiAgLyoqIFBpY2sgdGhlIFNBTSBtb2RlbCB0aGUgc2VnbWVudCB0b29scyB1c2UgKGppdC11aSM5MCBQMSkuICovXG4gIG9uU2FtTW9kZWxDaGFuZ2UoaWQ6IHN0cmluZykge1xuICAgIHRoaXMuc2FtTW9kZWxJZCA9IGlkO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U2FtTW9kZWwoaWQpO1xuICB9XG5cbiAgLyoqIENvbnZlcnQgdGhlIHNlbGVjdGVkIHJlZ2lvbiB0byBhIHNtb290aCBiZXppZXIgY3VydmUuICovXG4gIHRvQmV6aWVyUmVnaW9uKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0UmVnaW9uT3ZlcmxheSgpPy5zZXRTZWxlY3RlZEJlemllcih0cnVlKTtcbiAgfVxuXG4gIC8qKiBDb252ZXJ0IHRoZSBzZWxlY3RlZCByZWdpb24gYmFjayB0byBhIHN0cmFpZ2h0LWVkZ2VkIHBvbHlnb24uICovXG4gIHRvUG9seWdvblJlZ2lvbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbk92ZXJsYXkoKT8uc2V0U2VsZWN0ZWRCZXppZXIoZmFsc2UpO1xuICB9XG5cbiAgLyoqXG4gICAqIEltbWVkaWF0ZSByZWdpb24gYWN0aW9ucyBvbiB0aGUgY3VycmVudCBzZWxlY3Rpb24gKGppdC11aSM4NSk6IG1lcmdlLFxuICAgKiB1bmdyb3VwLCBpbnZlcnNlLCBzaW1wbGlmeSwgdGhlIELDqXppZXIgY29udmVyc2lvbnMsIGFuZCBkZWxldGUg4oCUIHRoZVxuICAgKiBvbmUtc2hvdCBnZW9tZXRyeSB0cmFuc2Zvcm1zLCBncm91cGVkIGFwYXJ0IGZyb20gdGhlIHRvb2wvbW9kZSB0b2dnbGVzIGFuZFxuICAgKiBzaG93biBvbmx5IHdoZW4gc29tZXRoaW5nIGlzIHNlbGVjdGVkLiBFYWNoIGl0ZW0gaXMgZ2F0ZWQgb24gd2hhdCB0aGVcbiAgICogc2VsZWN0aW9uIHN1cHBvcnRzLlxuICAgKi9cbiAgcHJpdmF0ZSBidWlsZFJlZ2lvbkFjdGlvbkl0ZW1zKCk6IE1lbnVJdGVtW10ge1xuICAgIGNvbnN0IGFsbCA9IHRoaXMucGxvdFNlcnZpY2UuZ2V0UmVnaW9ucygpO1xuICAgIGNvbnN0IHNlbGVjdGFibGUgPSBhbGwuZmlsdGVyKChyKSA9PiByLmtpbmQgIT09ICdwcm9maWxlJyk7XG4gICAgaWYgKHNlbGVjdGFibGUubGVuZ3RoID09PSAwKSByZXR1cm4gW107XG4gICAgY29uc3QgaXRlbXM6IE1lbnVJdGVtW10gPSBbXTtcblxuICAgIC8vIFNlbGVjdCBhbGwg4oCUIGF2YWlsYWJsZSB3aGVuZXZlciB0aGUgaW1hZ2UgaGFzIHJlZ2lvbnMsIHJlZ2FyZGxlc3Mgb2YgdGhlXG4gICAgLy8gY3VycmVudCBzZWxlY3Rpb24uXG4gICAgaXRlbXMucHVzaCh7XG4gICAgICBsYWJlbDogJ1NlbGVjdCBhbGwgcmVnaW9ucycsIGljb246ICdwaSBwaS1jaGVjay1zcXVhcmUnLFxuICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy5zZWxlY3RBbGxSZWdpb25zKCksXG4gICAgfSk7XG5cbiAgICBjb25zdCBzZWxBbGwgPSB0aGlzLnNlbGVjdGVkUmVnaW9ucztcbiAgICBpZiAoc2VsQWxsLmxlbmd0aCA9PT0gMCkgcmV0dXJuIGl0ZW1zO1xuICAgIGNvbnN0IGVsaWdpYmxlID0gdGhpcy5vcEVsaWdpYmxlKHNlbEFsbCk7XG5cbiAgICBpZiAoZWxpZ2libGUubGVuZ3RoID49IDIpIHtcbiAgICAgIGl0ZW1zLnB1c2goeyBsYWJlbDogJ01lcmdlIC8gZ3JvdXAnLCBpY29uOiAncGkgcGktbGluaycsIGNvbW1hbmQ6ICgpID0+IHRoaXMubWVyZ2VSZWdpb25zKCkgfSk7XG4gICAgfVxuICAgIGlmIChzZWxBbGwuc29tZSgocikgPT4gdGhpcy5yZWdpb25PcHMuY2FuVW5ncm91cChyKSkpIHtcbiAgICAgIGl0ZW1zLnB1c2goeyBsYWJlbDogJ1VuZ3JvdXAnLCBpY29uOiAncGkgcGktc2l0ZW1hcCcsIGNvbW1hbmQ6ICgpID0+IHRoaXMudW5ncm91cFJlZ2lvbnMoKSB9KTtcbiAgICB9XG4gICAgaWYgKGVsaWdpYmxlLmxlbmd0aCA+PSAxKSB7XG4gICAgICBpdGVtcy5wdXNoKFxuICAgICAgICB7IGxhYmVsOiAnSW52ZXJzZScsIGljb246ICdwaSBwaS1jbG9uZScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuaW52ZXJzZVJlZ2lvbnMoKSB9LFxuICAgICAgICB7XG4gICAgICAgICAgbGFiZWw6ICdTaW1wbGlmeScsIGljb246ICdwaSBwaS1jaGFydC1saW5lJyxcbiAgICAgICAgICBpdGVtczogW1xuICAgICAgICAgICAgeyBsYWJlbDogJ0xpZ2h0ICgxIHB4KScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2ltcGxpZnlSZWdpb25zKDEpIH0sXG4gICAgICAgICAgICB7IGxhYmVsOiAnTWVkaXVtICgzIHB4KScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2ltcGxpZnlSZWdpb25zKDMpIH0sXG4gICAgICAgICAgICB7IGxhYmVsOiAnU3Ryb25nICg4IHB4KScsIGNvbW1hbmQ6ICgpID0+IHRoaXMuc2ltcGxpZnlSZWdpb25zKDgpIH0sXG4gICAgICAgICAgICB7IHNlcGFyYXRvcjogdHJ1ZSB9LFxuICAgICAgICAgICAgeyBsYWJlbDogJ0N1c3RvbeKApicsIGNvbW1hbmQ6ICgpID0+IHRoaXMub3BlblNpbXBsaWZ5RGlhbG9nKCkgfSxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgKTtcbiAgICB9XG4gICAgLy8gQsOpemllciBjb252ZXJzaW9ucyBhcmUgdmVydGV4LWxldmVsIGVkaXRzIOKAlCBJbWFnZSAoT3BlblNlYWRyYWdvbikgdmlld1xuICAgIC8vIG9ubHkg4oCUIGdhdGVkIG9uIHRoZSBzZWxlY3Rpb24ncyBjdXJyZW50IGZvcm0uXG4gICAgaWYgKHRoaXMuYmFzZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRSkge1xuICAgICAgY29uc3QgaGFzU3RyYWlnaHQgPSBzZWxBbGwuc29tZSgocikgPT5cbiAgICAgICAgKHIuYm91bmRzIGluc3RhbmNlb2YgUG9seWdvbiAmJiAhci5ib3VuZHMuYmV6aWVyKSB8fCByLmJvdW5kcyBpbnN0YW5jZW9mIFJlY3RhbmdsZSk7XG4gICAgICBjb25zdCBoYXNCZXppZXIgPSBzZWxBbGwuc29tZSgocikgPT4gci5ib3VuZHMgaW5zdGFuY2VvZiBQb2x5Z29uICYmIHIuYm91bmRzLmJlemllcik7XG4gICAgICBpZiAoaGFzU3RyYWlnaHQpIHtcbiAgICAgICAgaXRlbXMucHVzaCh7IGxhYmVsOiAnQ29udmVydCB0byBCw6l6aWVyJywgaWNvbjogJ3RvLWJlemllci1pY29uJywgY29tbWFuZDogKCkgPT4gdGhpcy50b0JlemllclJlZ2lvbigpIH0pO1xuICAgICAgfVxuICAgICAgaWYgKGhhc0Jlemllcikge1xuICAgICAgICBpdGVtcy5wdXNoKHsgbGFiZWw6ICdDb252ZXJ0IHRvIHBvbHlnb24nLCBpY29uOiAndG8tcG9seWdvbi1pY29uJywgY29tbWFuZDogKCkgPT4gdGhpcy50b1BvbHlnb25SZWdpb24oKSB9KTtcbiAgICAgIH1cbiAgICB9XG4gICAgaXRlbXMucHVzaCh7IGxhYmVsOiAnRGVsZXRlIHJlZ2lvbicsIGljb246ICdwaSBwaS10cmFzaCcsIGNvbW1hbmQ6ICgpID0+IHRoaXMuZGVsZXRlUmVnaW9uKCkgfSk7XG4gICAgcmV0dXJuIGl0ZW1zO1xuICB9XG5cbiAgcHJpdmF0ZSBidWlsZENvbnRleHRNZW51SXRlbXMoKTogTWVudUl0ZW1bXSB7XG4gICAgY29uc3QgYWN0aXZlID0gdGhpcy5hY3RpdmVEcmFnTW9kZTtcbiAgICBjb25zdCBhY3RpdmVDbGFzcyA9ICdjb250ZXh0LW1lbnUtYWN0aXZlJztcbiAgICBjb25zdCBpdGVtczogTWVudUl0ZW1bXSA9IFtdO1xuICAgIC8vIFNlbGVjdGVkLXJlZ2lvbiBhY3Rpb25zIGxlYWQgdGhlIG1lbnUgKHNlcGFyYXRlZCBmcm9tIHRoZSB0b29sIHRvZ2dsZXMpLlxuICAgIGlmICh0aGlzLmlzSGVhdG1hcCkge1xuICAgICAgY29uc3QgYWN0aW9ucyA9IHRoaXMuYnVpbGRSZWdpb25BY3Rpb25JdGVtcygpO1xuICAgICAgaWYgKGFjdGlvbnMubGVuZ3RoKSBpdGVtcy5wdXNoKC4uLmFjdGlvbnMsIHsgc2VwYXJhdG9yOiB0cnVlIH0pO1xuICAgIH1cbiAgICBpZiAodGhpcy5pc0hlYXRtYXApIHtcbiAgICAgIGl0ZW1zLnB1c2goXG4gICAgICAgIHsgbGFiZWw6ICdBdXRvc2NhbGUnLCBpY29uOiAncGkgcGktd2luZG93LW1heGltaXplJywgY29tbWFuZDogKCkgPT4gdGhpcy5hdXRvc2NhbGVJbWFnZSgpIH0sXG4gICAgICAgIHsgc2VwYXJhdG9yOiB0cnVlIH0sXG4gICAgICApO1xuICAgICAgLy8gJ1pvb20gc2VsZWN0aW9uJyBpcyBQbG90bHkncyBydWJiZXItYmFuZCB6b29tOyBpdCBkb2Vzbid0IGFwcGx5IHRvIHRoZVxuICAgICAgLy8gT3BlblNlYWRyYWdvbi1iYWNrZWQgSW1hZ2UgdmlldyAodXNlICdab29tIHRvIGJveCcgdGhlcmUgaW5zdGVhZCkuXG4gICAgICBpZiAoIXRoaXMuaXNJbWFnZVZpZXcpIHtcbiAgICAgICAgaXRlbXMucHVzaCh7XG4gICAgICAgICAgbGFiZWw6ICdab29tIHNlbGVjdGlvbicsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLXNlYXJjaCcsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnem9vbScgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3pvb20nKSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICBpdGVtcy5wdXNoKHtcbiAgICAgICAgbGFiZWw6ICdab29tIHRvIGJveCcsXG4gICAgICAgIGljb246ICd6b29tLWJveC1vZmYtaWNvbicsXG4gICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ3pvb21Ub0JveCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCd6b29tVG9Cb3gnKSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICBpZiAodGhpcy5pc0hlYXRtYXApIHtcbiAgICAgIGl0ZW1zLnB1c2goe1xuICAgICAgICBsYWJlbDogJ1BhbicsXG4gICAgICAgIGljb246ICdwaSBwaS1hcnJvd3MtYWx0JyxcbiAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAncGFuJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3BhbicpLFxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHMzZCA9IHRoaXMuYWN0aXZlU3VyZmFjZTNkTW9kZTtcbiAgICAgIGl0ZW1zLnB1c2goXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1pvb20nLFxuICAgICAgICAgIGljb246ICdwaSBwaS1zZWFyY2gnLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IHMzZCA9PT0gJ3pvb20nID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZVN1cmZhY2UzZE1vZGUoJ3pvb20nKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnUGFuJyxcbiAgICAgICAgICBpY29uOiAncGkgcGktYXJyb3dzLWFsdCcsXG4gICAgICAgICAgc3R5bGVDbGFzczogczNkID09PSAncGFuJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVTdXJmYWNlM2RNb2RlKCdwYW4nKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnT3JiaXRhbCByb3RhdGlvbicsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLWdsb2JlJyxcbiAgICAgICAgICBzdHlsZUNsYXNzOiBzM2QgPT09ICdvcmJpdCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlU3VyZmFjZTNkTW9kZSgnb3JiaXQnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnVHVybnRhYmxlIHJvdGF0aW9uJyxcbiAgICAgICAgICBpY29uOiAncGkgcGktc3luYycsXG4gICAgICAgICAgc3R5bGVDbGFzczogczNkID09PSAndHVybnRhYmxlJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVTdXJmYWNlM2RNb2RlKCd0dXJudGFibGUnKSxcbiAgICAgICAgfSxcbiAgICAgICAgeyBzZXBhcmF0b3I6IHRydWUgfSxcbiAgICAgICAgeyBsYWJlbDogJ1Jlc2V0IGNhbWVyYScsIGljb246ICdwaSBwaS1ob21lJywgY29tbWFuZDogKCkgPT4gdGhpcy5yZXNldFN1cmZhY2VDYW1lcmEoKSB9LFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaXNIZWF0bWFwKSB7XG4gICAgICBpdGVtcy5wdXNoKFxuICAgICAgICB7IGxhYmVsOiAnWm9vbSBpbicsIGljb246ICdwaSBwaS1zZWFyY2gtcGx1cycsIGNvbW1hbmQ6ICgpID0+IHRoaXMuem9vbUluKCkgfSxcbiAgICAgICAgeyBsYWJlbDogJ1pvb20gb3V0JywgaWNvbjogJ3BpIHBpLXNlYXJjaC1taW51cycsIGNvbW1hbmQ6ICgpID0+IHRoaXMuem9vbU91dCgpIH0sXG4gICAgICAgIHsgc2VwYXJhdG9yOiB0cnVlIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1NlbGVjdCcsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLWFycm93LXVwLXJpZ2h0JyxcbiAgICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICdzZWxlY3QnID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdzZWxlY3QnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnRnJlZWZvcm0nLFxuICAgICAgICAgIGljb246ICdwaSBwaS1wZW5jaWwnLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ2RyYXdjbG9zZWRwYXRoJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd2Nsb3NlZHBhdGgnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnQnJ1c2gnLFxuICAgICAgICAgIGljb246ICdicnVzaC1pY29uJyxcbiAgICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICdicnVzaCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2JydXNoJyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1BvbHlsaW5lJyxcbiAgICAgICAgICBpY29uOiAncG9seWxpbmUtaWNvbicsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnZHJhd29wZW5wYXRoJyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd29wZW5wYXRoJyksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1JlY3RhbmdsZScsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLXN0b3AnLFxuICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ2RyYXdyZWN0JyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnZHJhd3JlY3QnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnV2FuZCcsXG4gICAgICAgICAgaWNvbjogJ3dhbmQtaWNvbicsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnd2FuZCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3dhbmQnKSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnVmVydGV4IGVyYXNlcicsXG4gICAgICAgICAgaWNvbjogJ3BpIHBpLWVyYXNlcicsXG4gICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnZXJhc2VWZXJ0ZXgnID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdlcmFzZVZlcnRleCcpLFxuICAgICAgICB9LFxuICAgICAgKTtcbiAgICAgIC8vIFZlcnRleCBlZGl0aW5nIHJ1bnMgb24gdGhlIE9wZW5TZWFkcmFnb24gb3ZlcmxheSwgd2hpY2hcbiAgICAgIC8vIGJhY2tzIHRoZSBJbWFnZSBwbG90IHR5cGUuIEhpZGRlbiBmb3Igb3RoZXIgMkQgdHlwZXMgKFBsb3RseSksIHdoZXJlXG4gICAgICAvLyB0aGVzZSBtb2RlcyBhcmUgbm8tb3BzLlxuICAgICAgaWYgKHRoaXMuYmFzZVBsb3RUeXBlID09PSBQbG90VHlwZS5JTUFHRSkge1xuICAgICAgICBpdGVtcy5wdXNoKFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiAnUG9seWdvbiAoY2xpY2sgdmVydGljZXMpJyxcbiAgICAgICAgICAgIGljb246ICdwb2x5Z29uLXZlcnRpY2VzLWljb24nLFxuICAgICAgICAgICAgc3R5bGVDbGFzczogYWN0aXZlID09PSAnZHJhd3BvbHlnb24nID8gYWN0aXZlQ2xhc3MgOiAnJyxcbiAgICAgICAgICAgIGNvbW1hbmQ6ICgpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUoJ2RyYXdwb2x5Z29uJyksXG4gICAgICAgICAgfSxcbiAgICAgICAgICB7XG4gICAgICAgICAgICBsYWJlbDogJ0FkZCB2ZXJ0ZXgnLFxuICAgICAgICAgICAgaWNvbjogJ3ZlcnRleC1hZGQtaWNvbicsXG4gICAgICAgICAgICBzdHlsZUNsYXNzOiBhY3RpdmUgPT09ICdhZGRwb2ludCcgPyBhY3RpdmVDbGFzcyA6ICcnLFxuICAgICAgICAgICAgY29tbWFuZDogKCkgPT4gdGhpcy50b2dnbGVEcmFnTW9kZSgnYWRkcG9pbnQnKSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiAnRGVsZXRlIHZlcnRleCcsXG4gICAgICAgICAgICBpY29uOiAndmVydGV4LWRlbGV0ZS1pY29uJyxcbiAgICAgICAgICAgIHN0eWxlQ2xhc3M6IGFjdGl2ZSA9PT0gJ2RlbGV0ZXBvaW50JyA/IGFjdGl2ZUNsYXNzIDogJycsXG4gICAgICAgICAgICBjb21tYW5kOiAoKSA9PiB0aGlzLnRvZ2dsZURyYWdNb2RlKCdkZWxldGVwb2ludCcpLFxuICAgICAgICAgIH0sXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICAvLyBCw6l6aWVyIGNvbnZlcnNpb25zICsgRGVsZXRlIGFyZSBpbW1lZGlhdGUgc2VsZWN0aW9uICphY3Rpb25zKiwgbm93IGluIHRoZVxuICAgICAgLy8gXCJTZWxlY3RlZCByZWdpb24ocylcIiBncm91cCBhdCB0aGUgdG9wIChzZWUgYnVpbGRSZWdpb25BY3Rpb25JdGVtcykuXG4gICAgfVxuICAgIHJldHVybiBpdGVtcztcbiAgfVxuXG4gIHRvZ2dsZVN1cmZhY2UzZE1vZGUobW9kZTogc3RyaW5nKSB7XG4gICAgdGhpcy5hY3RpdmVTdXJmYWNlM2RNb2RlID0gbW9kZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFN1cmZhY2VEcmFnTW9kZShtb2RlKTtcbiAgfVxuXG4gIHJlc2V0U3VyZmFjZUNhbWVyYSgpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlc2V0U3VyZmFjZUNhbWVyYSgpO1xuICB9XG5cbiAgLyoqIFRvZ2dsZSB0aGUgbmFwYXJpIDNEIGNvb3JkaW5hdGUtYXhlcyAvIHNjYWxlIGdpem1vICh2b2x1bWUvaXNvc3VyZmFjZSkuIE5vLW9wIG9uIGJhY2tlbmRzXG4gICAqICB0aGF0IGRvbid0IHJlbmRlciBvbmUgKHRoZSBjb250cm9sIG1ldGhvZCBpcyBvcHRpb25hbCkuICovXG4gIHRvZ2dsZUF4ZXMoKSB7XG4gICAgdGhpcy5heGVzVmlzaWJsZSA9ICF0aGlzLmF4ZXNWaXNpYmxlO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0U3VyZmFjZTNkQ29udHJvbHMoKT8uc2V0QXhlc1Zpc2libGU/Lih0aGlzLmF4ZXNWaXNpYmxlKTtcbiAgfVxuXG4gIC8qKiBUb2dnbGUgdGhlIG5hcGFyaSBzdXJmYWNlIHdpcmVmcmFtZSAoZWRnZXMgdnMgZmlsbGVkKS4gTm8tb3Agb24gYmFja2VuZHMgd2l0aG91dCBhIHN1cmZhY2UuICovXG4gIHRvZ2dsZVdpcmVmcmFtZSgpIHtcbiAgICB0aGlzLndpcmVmcmFtZUFjdGl2ZSA9ICF0aGlzLndpcmVmcmFtZUFjdGl2ZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFN1cmZhY2UzZENvbnRyb2xzKCk/LnNldFdpcmVmcmFtZT8uKHRoaXMud2lyZWZyYW1lQWN0aXZlKTtcbiAgfVxuXG4gIC8qKiBDaGFuZ2UgdGhlIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IuIERlY2ltYXRpb24gY2hhbmdlcyB0aGUgZmV0Y2hlZC9hc3NlbWJsZWQgZGF0YSwgc28gdGhpc1xuICAgKiAgcmUtcGxvdHMgdGhlIGN1cnJlbnQgM0QgdHlwZSBhdCB0aGUgbmV3IHJlc29sdXRpb24gKHVubGlrZSBheGVzL3dpcmVmcmFtZSwgd2hpY2ggYXJlIGxpdmUpLiAqL1xuICBzZWxlY3RSZXNvbHV0aW9uKHNjYWxlOiBudW1iZXIpIHtcbiAgICBpZiAoc2NhbGUgPT09IHRoaXMucmVzb2x1dGlvblNjYWxlKSByZXR1cm47XG4gICAgdGhpcy5yZXNvbHV0aW9uU2NhbGUgPSBzY2FsZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFJlc29sdXRpb25TY2FsZT8uKHNjYWxlKTtcbiAgICB0aGlzLnJlbG9hZEFuZFBsb3QoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTd2l0Y2ggdGhlIGFjdGl2ZSBwbG90IHR5cGUgKGhlYXRtYXAsIHN1cmZhY2UsIGNvbnRvdXIsIHNjYXR0ZXIsIGxpbmUsXG4gICAqIHNjYXR0ZXIzZCwgaXNvc3VyZmFjZSkuIEtlZXBzIGBpc0hlYXRtYXBgIGFzIHRoZSAyRC12cy0zRCBmbGFnIHRoZSByZXN0IG9mXG4gICAqIHRoZSB0b29sYmFyL2NvbnRleHQtbWVudSBsb2dpYyByZWxpZXMgb24sIGRlYWN0aXZhdGVzIDJELW9ubHkgdG9vbHMgd2hlblxuICAgKiBtb3ZpbmcgdG8gYSAzRCB0eXBlLCB0aGVuIHJlLXBsb3RzLlxuICAgKi9cbiAgb25TZWxlY3RQbG90VHlwZShzZWxlY3RlZDogUGxvdFR5cGVJZCkge1xuICAgIC8vIEEgY29udHJpYnV0ZWQgaWQgd2hvc2UgcHJvdmlkZXIgaXMgZ29uZSAoZS5nLiByZXN0b3JlZCBmcm9tIGEgcHJldmlvdXNcbiAgICAvLyBzZXNzaW9uKSBoYXMgbm90aGluZyB0byBhY3RpdmF0ZSDigJQgdGFrZSB0aGUgZGVmYXVsdCBJbWFnZSB2aWV3IGluc3RlYWQuXG4gICAgaWYgKCFpc0J1aWx0aW5QbG90VHlwZShzZWxlY3RlZCkgJiYgIXRoaXMucGxvdE1vZGVzLmZpbmQoc2VsZWN0ZWQpKSB7XG4gICAgICBjb25zb2xlLndhcm4oYFt2aXN1YWxpemVyXSB1bmtub3duIHBsb3QgdHlwZSAnJHtzZWxlY3RlZH0nIOKAlCBzaG93aW5nICcke1Bsb3RUeXBlLklNQUdFfScgaW5zdGVhZC5gKTtcbiAgICAgIHNlbGVjdGVkID0gUGxvdFR5cGUuSU1BR0U7XG4gICAgfVxuICAgIC8vIEFuIGV4cGxpY2l0IGNob2ljZSByZXRyaWVzIGEgbW9kZSB3aG9zZSBlYXJsaWVyIGNsZWFudXAgZmFpbGVkOyBvbmx5XG4gICAgLy8gYXV0b21hdGljIHJlLWFjdGl2YXRpb25zIChyZS1yZW5kZXIsIGltYWdlIHN3aXRjaCkgZmFsbCBiYWNrIGZvciB0aGF0LlxuICAgIHRoaXMucGxvdE1vZGVzLmNsZWFyQ2xlYW51cEZhaWx1cmVzKCk7XG4gICAgLy8gV2hhdGV2ZXIgd2FzIHNlbGVjdGVkIGJlZm9yZSwgYSBjb250cmlidXRlZCBzZXNzaW9uIGVuZHMgYmVmb3JlIHRoZSBuZXh0XG4gICAgLy8gbW9kZSBkcmF3cy4gKFJlLXNlbGVjdGluZyB0aGUgc2FtZSBjb250cmlidXRlZCBtb2RlIHJlLXBsb3RzLCBzbyBpdCBnZXRzIGFcbiAgICAvLyBmcmVzaCBzZXNzaW9uIHRvby4pXG4gICAgdGhpcy5wbG90TW9kZXMuZGVhY3RpdmF0ZSgpO1xuICAgIC8vIEV2ZXJ5dGhpbmcgYmVsb3cgZGVjaWRlcyBob3cgdG8gUkVOREVSLCBzbyBpdCB3b3JrcyBvbiB0aGUgYnVpbHQtaW4gdHlwZVxuICAgIC8vIHRoYXQgZHJhd3MgYHNlbGVjdGVkYDogYSBjb250cmlidXRlZCBtb2RlIGJlaGF2ZXMgZXhhY3RseSBsaWtlIGl0cyBiYXNlLlxuICAgIGNvbnN0IHR5cGUgPSB0aGlzLnBsb3RNb2Rlcy5iYXNlVHlwZU9mKHNlbGVjdGVkKTtcbiAgICAvLyBBIGRpYWxvZyB0b29sIGRyYXdzIG92ZXIgdGhlIEltYWdlIHZpZXc6IGxlYXZpbmcgaXQgY2xvc2VzIHRoZSBkaWFsb2cuIFN0YXlpbmdcbiAgICAvLyBvbiBpdCwgdGhlIHNlc3Npb24gZW5kcyBoZXJlIGFuZCByZXN0YXJ0cyBvbmNlIHRoZSByZS1wbG90IGxhbmRzLlxuICAgIGlmICh0eXBlID09PSBQbG90VHlwZS5JTUFHRSkgdGhpcy50b29sRGlhbG9ncy5kZWFjdGl2YXRlKCk7XG4gICAgZWxzZSB0aGlzLmNsb3NlRGlhbG9nVG9vbCgpO1xuICAgIHRoaXMucGxvdFR5cGUgPSB0eXBlO1xuICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gc2VsZWN0ZWQ7XG4gICAgY29uc3QgZGVzY3JpcHRvciA9IGlzQnVpbHRpblBsb3RUeXBlKHNlbGVjdGVkKVxuICAgICAgPyB0aGlzLnBsb3RUeXBlTWVudS5maW5kKChkKSA9PiBkLnR5cGUgPT09IHR5cGUpXG4gICAgICA6IGdldFBsb3RUeXBlRGVzY3JpcHRvcih0eXBlKTtcbiAgICBjb25zdCBpczNkID0gZGVzY3JpcHRvcj8uZGltZW5zaW9ucyA9PT0gJzNkJztcbiAgICB0aGlzLmlzSGVhdG1hcCA9ICFpczNkO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UGxvdFR5cGUodHlwZSk7XG4gICAgLy8gU3dpdGNoaW5nIHBsb3QgdHlwZSBjYW5jZWxzIHRoZSBhY3RpdmUgdG9vbCDigJQgbm90IGV2ZXJ5IHR5cGUgc3VwcG9ydHMgdGhlXG4gICAgLy8gc2FtZSB0b29scyAobGluZS9zY2F0dGVyIGNoYXJ0cyBhbmQgM0Qgc2NlbmVzIGhhdmUgbm8gYm94IHpvb20gb3IgcmVnaW9uXG4gICAgLy8gZHJhd2luZyksIHNvIGxlYXZpbmcgYSB0b29sIGFybWVkIHdvdWxkIG1pc2JlaGF2ZSBvbiB0aGUgbmV3IHBsb3QuXG4gICAgdGhpcy5kZWFjdGl2YXRlQWN0aXZlVG9vbCgpO1xuICAgIGlmIChpczNkKSB7XG4gICAgICB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUgPSAndHVybnRhYmxlJztcbiAgICB9XG4gICAgLy8gbmFwYXJpLWpzIHZvbHVtZS9pc29zdXJmYWNlIGFzc2VtYmxlIHRoZWlyIG93biAzRCB0ZXh0dXJlIGZyb20gdGhlIHNsaWNlIGVuZHBvaW50LCBzb1xuICAgIC8vIHRoZXkgdGFrZSB0aGUgbm9ybWFsIHJlLXBsb3QgcGF0aCDigJQgTk9UIFBsb3RseSdzIHN0YWNrLWZyYW1lIGxvYWRlciAod2hvc2UgXCJMb2FkaW5nXG4gICAgLy8gZnJhbWVzXCIgb3ZlcmxheSB3b3VsZCBuZXZlciBjbGVhciwgc2luY2UgUGxvdGx5IGlzbid0IHRoZSBhY3RpdmUgYmFja2VuZCkgKGppdC11aSMxMDIpLlxuICAgIGNvbnN0IGlzTmFwYXJpID0gdHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFIHx8IGlzTmFwYXJpM2QodHlwZSk7XG4gICAgaWYgKGRlc2NyaXB0b3I/LnJlcXVpcmVzU3RhY2sgJiYgIWlzTmFwYXJpKSB7XG4gICAgICAvLyBWb2x1bWUgdHlwZXMgKGlzb3N1cmZhY2UsIHNjYXR0ZXIzZCkgbmVlZCB0aGUgd2hvbGUgei1zdGFjayBsb2FkZWQgYXMgYVxuICAgICAgLy8gM0QgYXJyYXksIGFuZCB0aGV5IGFsd2F5cyByZW5kZXIgb24gUGxvdGx5LiBEcml2ZSB0aGUgc3RhY2sgcmVsb2FkXG4gICAgICAvLyBkaXJlY3RseSB0aHJvdWdoIHRoZSBpbWFnZS1pbmZvIHN0cmVhbSByYXRoZXIgdGhhbiB0aGUgYWN0aXZlIHJlbmRlcmVyJ3NcbiAgICAgIC8vIHNldFNob3dTdGFjazogd2hlbiBPcGVuU2VhZHJhZ29uIG93bnMgdGhlIEltYWdlIHZpZXcgaXRzIHNldFNob3dTdGFjayBpc1xuICAgICAgLy8gYSBuby1vcCwgc28gdGhlIHJlbG9hZCBuZXZlciBmaXJlZCBhbmQgdGhlIGxvYWRlciBzcHVuIGZvcmV2ZXIuIFdlIGFybVxuICAgICAgLy8gUGxvdGx5J3Mgc3RhY2stbG9hZGluZyBmbGFnIChrZWVwcyBpdHMgZnJhbWUtZmV0Y2ggbG9vcCBhbGl2ZSkgYW5kXG4gICAgICAvLyByZS1lbWl0IHRoZSBpbWFnZSBpbmZvIHdpdGggc2hvd1N0YWNrIG9uIHNvIHRoZSBwaXBlbGluZSByZWxvYWRzIG9uXG4gICAgICAvLyBQbG90bHkgcmVnYXJkbGVzcyBvZiB3aGljaCBiYWNrZW5kIHdhcyBvbiBzY3JlZW4uXG4gICAgICB0aGlzLnNlbGVjdGVkU3RhY2tPcHRpb24gPSB0aGlzLnN0YWNrT3B0aW9uc1sxXTtcbiAgICAgIHRoaXMuc3RhY2tMb2FkaW5nID0gdHJ1ZTtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U3RhY2tMb2FkaW5nKHRydWUpO1xuICAgICAgaWYgKHRoaXMuaW1hZ2VJbmZvKSB7XG4gICAgICAgIHRoaXMuaW1hZ2VJbmZvLnNob3dTdGFjayA9IHRydWU7XG4gICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKHRoaXMuaW1hZ2VJbmZvKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5yZWxvYWRBbmRQbG90KCk7XG4gIH1cblxuICAvLyDilIDilIAgY29udHJpYnV0ZWQgcGxvdCBtb2RlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKipcbiAgICogU3RhcnQgdGhlIHNlbGVjdGVkIGNvbnRyaWJ1dGVkIG1vZGUncyBzZXNzaW9uLCBvbmNlIGl0cyBiYXNlIHZpZXcgaGFzXG4gICAqIHBsb3R0ZWQuIE5vLW9wIGZvciBhIGJ1aWx0LWluIHR5cGUsIG9yIHdoZW4gdGhlIGJhc2Ugb24gc2NyZWVuIGlzIG5vdCB0aGVcbiAgICogb25lIHRoZSBtb2RlIHJpZGVzIG9uLiBBIGJhY2tlbmQgdGhhdCBjYW5ub3QgcHJvdmlkZSBhIHZpZXdwb3J0IGZvciBpdFxuICAgKiAoZS5nLiBPU0QgZmVsbCBiYWNrIHRvIFBsb3RseSkgY291bnRzIGFzIGEgZmFpbGVkIGFjdGl2YXRpb24uXG4gICAqL1xuICBwcml2YXRlIGFjdGl2YXRlU2VsZWN0ZWRQbG90TW9kZSgpOiB2b2lkIHtcbiAgICBjb25zdCBjb250cmlidXRpb24gPSB0aGlzLnBsb3RNb2Rlcy5maW5kKHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkKTtcbiAgICBpZiAoIWNvbnRyaWJ1dGlvbiB8fCB0aGlzLnBsb3RUeXBlICE9PSBjb250cmlidXRpb24uZGVzY3JpcHRvci5iYXNlVHlwZSkgcmV0dXJuO1xuICAgIGNvbnN0IHZpZXdwb3J0ID0gdGhpcy5wbG90U2VydmljZS5nZXRQbG90TW9kZVZpZXdwb3J0Py4oKSA/PyBudWxsO1xuICAgIGlmICghdmlld3BvcnQpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYFt2aXN1YWxpemVyXSBwbG90LXR5cGUgY29udHJpYnV0aW9uICcke2NvbnRyaWJ1dGlvbi5kZXNjcmlwdG9yLnR5cGV9JzogdGhlIGJhY2tlbmQgYFxuICAgICAgICArICdvbiBzY3JlZW4gcHJvdmlkZXMgbm8gdmlld3BvcnQgdG8gZHJhdyBvdmVyIOKAlCBmYWxsaW5nIGJhY2suJyk7XG4gICAgICB0aGlzLmZhbGxCYWNrRnJvbVBsb3RNb2RlKGNvbnRyaWJ1dGlvbik7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGN0eDogUGxvdE1vZGVDb250ZXh0ID0ge1xuICAgICAgdmlzdWFsaXplcjogdGhpcy5wbG90U2VydmljZSxcbiAgICAgIHZpZXdwb3J0LFxuICAgICAgaW1hZ2VJbmZvJDogdGhpcy5zdGF0ZS5nZXRJbWFnZUluZm8kKCksXG4gICAgICB0b29sczogdGhpcy5wbG90TW9kZVRvb2xzLFxuICAgIH07XG4gICAgdm9pZCB0aGlzLnBsb3RNb2Rlcy5hY3RpdmF0ZShjb250cmlidXRpb24sIGN0eCk7XG4gIH1cblxuICAvKiogUmVuZGVyIHRoZSBsaXZlIG1vZGUncyBwYW5lbC4gQWN0aXZhdGlvbiBjYW4gcmVzb2x2ZSBvdXRzaWRlIEFuZ3VsYXIgKGFuXG4gICAqICBPU0QgY2FsbGJhY2ssIGEgY29udHJpYnV0aW9uJ3Mgb3duIHByb21pc2UpLCBoZW5jZSB0aGUgem9uZSByZS1lbnRyeS4gKi9cbiAgcHJpdmF0ZSBzaG93UGxvdE1vZGVQYW5lbChhY3RpdmU6IEFjdGl2ZVBsb3RNb2RlKTogdm9pZCB7XG4gICAgY29uc3QgcGFuZWwgPSBhY3RpdmUuY29udHJpYnV0aW9uLnBhbmVsO1xuICAgIGlmICghcGFuZWwpIHJldHVybjtcbiAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgaWYgKCdjb21wb25lbnQnIGluIHBhbmVsICYmIHBhbmVsLmNvbXBvbmVudCkge1xuICAgICAgICB0aGlzLnBsb3RNb2RlUGFuZWwgPSB7XG4gICAgICAgICAgdGl0bGU6IHBhbmVsLnRpdGxlLFxuICAgICAgICAgIGNvbXBvbmVudDogcGFuZWwuY29tcG9uZW50LFxuICAgICAgICAgIGluamVjdG9yOiBJbmplY3Rvci5jcmVhdGUoe1xuICAgICAgICAgICAgcHJvdmlkZXJzOiBbXG4gICAgICAgICAgICAgIHsgcHJvdmlkZTogUExPVF9NT0RFX0NPTlRFWFQsIHVzZVZhbHVlOiBhY3RpdmUuY3R4IH0sXG4gICAgICAgICAgICAgIHsgcHJvdmlkZTogUExPVF9NT0RFX1NFU1NJT04sIHVzZVZhbHVlOiBhY3RpdmUuc2Vzc2lvbiB9LFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICAgIHBhcmVudDogdGhpcy5pbmplY3RvcixcbiAgICAgICAgICB9KSxcbiAgICAgICAgICBob3N0OiBudWxsLFxuICAgICAgICB9O1xuICAgICAgfSBlbHNlIGlmIChhY3RpdmUucGFuZWxIb3N0KSB7XG4gICAgICAgIHRoaXMucGxvdE1vZGVQYW5lbCA9IHsgdGl0bGU6IHBhbmVsLnRpdGxlLCBjb21wb25lbnQ6IG51bGwsIGluamVjdG9yOiBudWxsLCBob3N0OiBhY3RpdmUucGFuZWxIb3N0IH07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBBIGNvbXBvbmVudCBwYW5lbCBpcyBjcmVhdGVkIGFuZCBmaXJzdCByZW5kZXJlZCByaWdodCBoZXJlOyBpZiB0aGF0IHRocm93cyxcbiAgICAgIC8vIHRoZSBtb2RlIGlzIG5vdCB1c2FibGUg4oCUIGVuZCBpdCBhbmQgZmFsbCBiYWNrIHJhdGhlciB0aGFuIGxlYXZlIGl0IHNlbGVjdGVkLlxuICAgICAgY29uc3QgZXJyID0gdGhpcy5kZXRlY3RDaGFuZ2VzU2FmZWx5KCk7XG4gICAgICBpZiAoZXJyICE9PSBudWxsKSB0aGlzLnBsb3RNb2Rlcy5mYWlsQWN0aXZlKGVycik7XG4gICAgfSk7XG4gIH1cblxuICAvKiogRHJvcCB0aGUgcGFuZWwgKGRlc3Ryb3lpbmcgYSBjb21wb25lbnQgcGFuZWwpIGJlZm9yZSB0aGUgc2Vzc2lvbiBlbmRzLiAqL1xuICBwcml2YXRlIGhpZGVQbG90TW9kZVBhbmVsKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5wbG90TW9kZVBhbmVsKSByZXR1cm47XG4gICAgdGhpcy5wbG90TW9kZVBhbmVsID0gbnVsbDtcbiAgICB0aGlzLmRldGVjdENoYW5nZXNTYWZlbHkoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBIGNvbnRyaWJ1dGVkIG1vZGUgY291bGQgbm90IHN0YXJ0LiBJdHMgYmFzZSB0eXBlIGlzIGFscmVhZHkgb24gc2NyZWVuIOKAlFxuICAgKiB0aGUgYmFzZSBwbG90dGVkIGZpcnN0IOKAlCBzbyBqdXN0IHNlbGVjdCBpdDogbm90aGluZyBuZWVkcyB0byByZS1wbG90LlxuICAgKi9cbiAgcHJpdmF0ZSBmYWxsQmFja0Zyb21QbG90TW9kZShjb250cmlidXRpb246IFBsb3RUeXBlQ29udHJpYnV0aW9uKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuZGVzdHJveWluZyB8fCB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCAhPT0gY29udHJpYnV0aW9uLmRlc2NyaXB0b3IudHlwZSkgcmV0dXJuO1xuICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICBjb25zdCBiYXNlID0gY29udHJpYnV0aW9uLmRlc2NyaXB0b3IuYmFzZVR5cGU7XG4gICAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCA9IGJhc2U7XG4gICAgICB0aGlzLnBsb3RUeXBlID0gYmFzZTtcbiAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAga2V5OiB0aGlzLnJlc3VsdFRvYXN0S2V5LFxuICAgICAgICBzZXZlcml0eTogJ3dhcm4nLFxuICAgICAgICBzdW1tYXJ5OiBgJHtjb250cmlidXRpb24uZGVzY3JpcHRvci5wcm9kdWN0aW9uTGFiZWwgPz8gY29udHJpYnV0aW9uLmRlc2NyaXB0b3IubGFiZWx9IGlzIHVuYXZhaWxhYmxlYCxcbiAgICAgICAgZGV0YWlsOiAnVGhlIHBsb3QgbW9kZSBjb3VsZCBub3Qgc3RhcnQsIHNvIHRoZSBwbGFpbiBpbWFnZSBpcyBzaG93biBpbnN0ZWFkLiBTZWUgdGhlIGJyb3dzZXIgY29uc29sZSBmb3IgZGV0YWlscy4nLFxuICAgICAgfSk7XG4gICAgICB0aGlzLmRldGVjdENoYW5nZXNTYWZlbHkoKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBSdW4gY2hhbmdlIGRldGVjdGlvbjsgcmV0dXJucyB3aGF0IGl0IHRocmV3IChsb2dnZWQpLCBvciBudWxsLiAqL1xuICBwcml2YXRlIGRldGVjdENoYW5nZXNTYWZlbHkoKTogdW5rbm93biB8IG51bGwge1xuICAgIGlmICh0aGlzLmRlc3Ryb3lpbmcpIHJldHVybiBudWxsO1xuICAgIHRyeSB7XG4gICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ1t2aXN1YWxpemVyXSBjaGFuZ2UgZGV0ZWN0aW9uIGZhaWxlZCB3aGlsZSB1cGRhdGluZyBhIGNvbnRyaWJ1dGVkIHBsb3QgbW9kZSBwYW5lbC4nLCBlcnIpO1xuICAgICAgcmV0dXJuIGVyciA/PyBuZXcgRXJyb3IoJ2NoYW5nZSBkZXRlY3Rpb24gZmFpbGVkJyk7XG4gICAgfVxuICB9XG5cbiAgLy8g4pSA4pSAIGRpYWxvZyB0b29scyAoVE9PTEJBUl9UT09MUywga2luZDogJ2RpYWxvZycpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBUaGUgdG9vbGJhciBidXR0b246IG9wZW4gdGhlIHRvb2wncyBkaWFsb2csIG9yIGNsb3NlIGl0IGlmIGl0IGlzIG9wZW4uICovXG4gIHRvZ2dsZURpYWxvZ1Rvb2woaWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLm9wZW5EaWFsb2dUb29sSWQgPT09IGlkKSB7XG4gICAgICB0aGlzLmNsb3NlRGlhbG9nVG9vbCgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmNsb3NlRGlhbG9nVG9vbCgpO1xuICAgIGlmICghdGhpcy5kaWFsb2dUb29scy5zb21lKCh0KSA9PiB0LmlkID09PSBpZCkpIHJldHVybjtcbiAgICB0aGlzLm9wZW5EaWFsb2dUb29sSWQgPSBpZDtcbiAgICB0aGlzLnRvb2xEaWFsb2dzLmNsZWFyQ2xlYW51cEZhaWx1cmVzKCk7IC8vIGFuIGV4cGxpY2l0IG9wZW4gZ2V0cyBhIGZyZXNoIGF0dGVtcHRcbiAgICB0aGlzLmFjdGl2YXRlT3BlbkRpYWxvZ1Rvb2woKTtcbiAgfVxuXG4gIC8qKiBDbG9zZSB0aGUgb3BlbiBkaWFsb2cgdG9vbDogaXRzIGJvZHkgaXMgdG9ybiBkb3duLCB0aGVuIGl0cyBzZXNzaW9uIGVuZHMuICovXG4gIGNsb3NlRGlhbG9nVG9vbCgpOiB2b2lkIHtcbiAgICB0aGlzLm9wZW5EaWFsb2dUb29sSWQgPSBudWxsO1xuICAgIHRoaXMudG9vbERpYWxvZ3MuZGVhY3RpdmF0ZSgpO1xuICB9XG5cbiAgLyoqIFN0YXJ0IChvciByZXN0YXJ0LCBhZnRlciBhIHJlLXJlbmRlcikgdGhlIG9wZW4gZGlhbG9nIHRvb2wncyBzZXNzaW9uLiAqL1xuICBwcml2YXRlIGFjdGl2YXRlT3BlbkRpYWxvZ1Rvb2woKTogdm9pZCB7XG4gICAgY29uc3QgaWQgPSB0aGlzLm9wZW5EaWFsb2dUb29sSWQ7XG4gICAgY29uc3QgY29udHJpYnV0aW9uID0gaWQgPyB0aGlzLnRvb2xEaWFsb2dzLmZpbmQoaWQpIDogdW5kZWZpbmVkO1xuICAgIGlmICghY29udHJpYnV0aW9uIHx8IHRoaXMucGxvdFR5cGUgIT09IFBsb3RUeXBlLklNQUdFKSByZXR1cm47XG4gICAgaWYgKHRoaXMudG9vbERpYWxvZ3MuY3VycmVudD8uY29udHJpYnV0aW9uID09PSBjb250cmlidXRpb24gfHwgdGhpcy50b29sRGlhbG9ncy5wZW5kaW5nKSByZXR1cm47XG4gICAgY29uc3Qgdmlld3BvcnQgPSB0aGlzLnBsb3RTZXJ2aWNlLmdldFBsb3RNb2RlVmlld3BvcnQ/LigpID8/IG51bGw7XG4gICAgaWYgKCF2aWV3cG9ydCkge1xuICAgICAgY29uc29sZS5lcnJvcihgW3Zpc3VhbGl6ZXJdIGRpYWxvZyB0b29sICcke2lkfSc6IHRoZSBiYWNrZW5kIG9uIHNjcmVlbiBwcm92aWRlcyBubyB2aWV3cG9ydCB0byBkcmF3IG92ZXIuYCk7XG4gICAgICB0aGlzLmRpYWxvZ1Rvb2xGYWlsZWQoaWQhKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgY3R4OiBUb29sRGlhbG9nQ29udGV4dCA9IHtcbiAgICAgIHZpc3VhbGl6ZXI6IHRoaXMucGxvdFNlcnZpY2UsXG4gICAgICB2aWV3cG9ydCxcbiAgICAgIGltYWdlSW5mbyQ6IHRoaXMuc3RhdGUuZ2V0SW1hZ2VJbmZvJCgpLFxuICAgICAgdG9vbHM6IHRoaXMucGxvdE1vZGVUb29scyxcbiAgICB9O1xuICAgIHZvaWQgdGhpcy50b29sRGlhbG9ncy5hY3RpdmF0ZShjb250cmlidXRpb24sIGN0eCk7XG4gIH1cblxuICAvKiogVGhlIGxpdmUgZGlhbG9nIGJvZHkncyB0ZWFyZG93biwgYW5kIGEgcGVuZGluZyB3YWl0IGZvciBpdHMgaG9zdCB0byBjb25uZWN0LiAqL1xuICBwcml2YXRlIHRvb2xEaWFsb2dUZWFyZG93bjogKCgpID0+IHZvaWQpIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgdG9vbERpYWxvZ0ZyYW1lOiBudW1iZXIgfCBudWxsID0gbnVsbDtcblxuICAvKipcbiAgICogVGhlIHNlc3Npb24gaXMgbGl2ZTogcmVuZGVyIHRoZSBkaWFsb2csIHRoZW4gbW91bnQgdGhlIHRvb2wncyBib2R5IG9uY2UgdGhlIGhvc3RcbiAgICogZWxlbWVudCBpcyBpbiB0aGUgZG9jdW1lbnQsIHNvIGBtb3VudCgpYCBjYW4gbWVhc3VyZSBpdCBhbmQgc3RhcnQgd2lkZ2V0cyB0aGF0IG5lZWRcbiAgICogYSBjb25uZWN0ZWQgZWxlbWVudC5cbiAgICovXG4gIHByaXZhdGUgc2hvd1Rvb2xEaWFsb2coYWN0aXZlOiBBY3RpdmVQbG90TW9kZSk6IHZvaWQge1xuICAgIGNvbnN0IHRvb2wgPSB0aGlzLmRpYWxvZ1Rvb2xzLmZpbmQoKHQpID0+IHQuaWQgPT09IGFjdGl2ZS5jb250cmlidXRpb24uZGVzY3JpcHRvci50eXBlKTtcbiAgICBpZiAoIXRvb2wpIHJldHVybjtcbiAgICBjb25zdCBob3N0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgaG9zdC5jbGFzc05hbWUgPSAndG9vbC1kaWFsb2ctYm9keSc7XG4gICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgIHRoaXMudG9vbERpYWxvZyA9IHtcbiAgICAgICAgdGl0bGU6IHRvb2wuZGlhbG9nPy50aXRsZSA/PyB0b29sLmxhYmVsLFxuICAgICAgICB3aWR0aDogdG9vbC5kaWFsb2c/LndpZHRoID8/ICcyMnJlbScsXG4gICAgICAgIGhvc3QsXG4gICAgICB9O1xuICAgICAgY29uc3QgZXJyID0gdGhpcy5kZXRlY3RDaGFuZ2VzU2FmZWx5KCk7XG4gICAgICBpZiAoZXJyICE9PSBudWxsKSB7XG4gICAgICAgIHRoaXMudG9vbERpYWxvZ3MuZmFpbEFjdGl2ZShlcnIpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLm1vdW50VG9vbERpYWxvZ0JvZHkodG9vbCwgYWN0aXZlLCBob3N0LCAwKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBIb3cgbWFueSBhbmltYXRpb24gZnJhbWVzIHRvIHdhaXQgZm9yIHRoZSBkaWFsb2cgdG8gYXR0YWNoIHRoZSBib2R5J3MgaG9zdC4gKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgVE9PTF9ESUFMT0dfQVRUQUNIX0ZSQU1FUyA9IDYwO1xuXG4gIC8qKlxuICAgKiBNb3VudCBgdG9vbGAncyBib2R5IGludG8gYGhvc3RgIG9uY2UgaXQgaXMgY29ubmVjdGVkICh0aGUgZGlhbG9nIG1heSBhdHRhY2ggaXQgYVxuICAgKiBmcmFtZSBsYXRlciwgd2hlbiBpdCBtb3ZlcyBpdHNlbGYgdG8gYDxib2R5PmApLiBJZiBpdCBuZXZlciBjb25uZWN0cyB3aXRoaW5cbiAgICoge0BsaW5rIFRPT0xfRElBTE9HX0FUVEFDSF9GUkFNRVN9IGl0IGlzIG1vdW50ZWQgYW55d2F5LCB3aXRoIGEgd2FybmluZywgcmF0aGVyIHRoYW5cbiAgICogbGVhdmluZyBhbiBlbXB0eSBkaWFsb2cuIEEgdGhyb3dpbmcgYG1vdW50KClgIGZhaWxzIHRoZSBzZXNzaW9uIGxpa2UgYSBmYWlsZWQgc3RhcnQuXG4gICAqL1xuICBwcml2YXRlIG1vdW50VG9vbERpYWxvZ0JvZHkodG9vbDogVG9vbGJhckRpYWxvZ1Rvb2xDb250cmlidXRpb24sIGFjdGl2ZTogQWN0aXZlUGxvdE1vZGUsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBob3N0OiBIVE1MRWxlbWVudCwgZnJhbWVzOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnRvb2xEaWFsb2dGcmFtZSA9IG51bGw7XG4gICAgLy8gVGhlIHNlc3Npb24gZW5kZWQgKG9yIHdhcyByZXBsYWNlZCkgd2hpbGUgd2FpdGluZzogbm90aGluZyB0byBtb3VudCBpbnRvLlxuICAgIGlmICh0aGlzLnRvb2xEaWFsb2dzLmN1cnJlbnQgIT09IGFjdGl2ZSB8fCB0aGlzLnRvb2xEaWFsb2c/Lmhvc3QgIT09IGhvc3QpIHJldHVybjtcbiAgICBpZiAoIWhvc3QuaXNDb25uZWN0ZWQpIHtcbiAgICAgIGlmIChmcmFtZXMgPCBWaXN1YWxpemVyQ29tcG9uZW50LlRPT0xfRElBTE9HX0FUVEFDSF9GUkFNRVMpIHtcbiAgICAgICAgdGhpcy50b29sRGlhbG9nRnJhbWUgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoXG4gICAgICAgICAgKCkgPT4gdGhpcy5tb3VudFRvb2xEaWFsb2dCb2R5KHRvb2wsIGFjdGl2ZSwgaG9zdCwgZnJhbWVzICsgMSkpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zb2xlLndhcm4oYFt2aXN1YWxpemVyXSBkaWFsb2cgdG9vbCAnJHt0b29sLmlkfSc6IHRoZSBkaWFsb2cgYm9keSBuZXZlciBhdHRhY2hlZDsgbW91bnRpbmcgaXQgZGV0YWNoZWQuYCk7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBjb25zdCB0ZWFyZG93biA9IHRvb2wubW91bnQoaG9zdCwgYWN0aXZlLmN0eCBhcyBUb29sRGlhbG9nQ29udGV4dCwgYWN0aXZlLnNlc3Npb24pO1xuICAgICAgdGhpcy50b29sRGlhbG9nVGVhcmRvd24gPSB0eXBlb2YgdGVhcmRvd24gPT09ICdmdW5jdGlvbicgPyB0ZWFyZG93biA6IG51bGw7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGBbdmlzdWFsaXplcl0gZGlhbG9nIHRvb2wgJyR7dG9vbC5pZH0nIG1vdW50KCkgdGhyZXcuYCwgZXJyKTtcbiAgICAgIHRoaXMudG9vbERpYWxvZ3MuZmFpbEFjdGl2ZShlcnIpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBUaGUgc2Vzc2lvbiBpcyBhYm91dCB0byBlbmQ6IHRlYXIgdGhlIGJvZHkgZG93biBmaXJzdCwgdGhlbiBkcm9wIHRoZSBkaWFsb2cuICovXG4gIHByaXZhdGUgaGlkZVRvb2xEaWFsb2coKTogdm9pZCB7XG4gICAgaWYgKHRoaXMudG9vbERpYWxvZ0ZyYW1lICE9PSBudWxsKSBjYW5jZWxBbmltYXRpb25GcmFtZSh0aGlzLnRvb2xEaWFsb2dGcmFtZSk7XG4gICAgdGhpcy50b29sRGlhbG9nRnJhbWUgPSBudWxsO1xuICAgIGNvbnN0IHRlYXJkb3duID0gdGhpcy50b29sRGlhbG9nVGVhcmRvd247XG4gICAgdGhpcy50b29sRGlhbG9nVGVhcmRvd24gPSBudWxsO1xuICAgIGlmICh0ZWFyZG93bikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgdGVhcmRvd24oKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdbdmlzdWFsaXplcl0gZGlhbG9nIHRvb2wgYm9keSB0ZWFyZG93biB0aHJldy4nLCBlcnIpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoIXRoaXMudG9vbERpYWxvZykgcmV0dXJuO1xuICAgIHRoaXMudG9vbERpYWxvZy5ob3N0LnJlbW92ZSgpO1xuICAgIHRoaXMudG9vbERpYWxvZyA9IG51bGw7XG4gICAgaWYgKCF0aGlzLmRlc3Ryb3lpbmcpIHRoaXMuZGV0ZWN0Q2hhbmdlc1NhZmVseSgpO1xuICB9XG5cbiAgLyoqIEEgZGlhbG9nIHRvb2wgY291bGQgbm90IHN0YXJ0OiBjbG9zZSBpdCBhbmQgc2F5IHNvLiAqL1xuICBwcml2YXRlIGRpYWxvZ1Rvb2xGYWlsZWQoaWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLmRlc3Ryb3lpbmcgfHwgdGhpcy5vcGVuRGlhbG9nVG9vbElkICE9PSBpZCkgcmV0dXJuO1xuICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICB0aGlzLm9wZW5EaWFsb2dUb29sSWQgPSBudWxsO1xuICAgICAgY29uc3QgdG9vbCA9IHRoaXMuZGlhbG9nVG9vbHMuZmluZCgodCkgPT4gdC5pZCA9PT0gaWQpO1xuICAgICAgdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoe1xuICAgICAgICBrZXk6IHRoaXMucmVzdWx0VG9hc3RLZXksXG4gICAgICAgIHNldmVyaXR5OiAnd2FybicsXG4gICAgICAgIHN1bW1hcnk6IGAke3Rvb2w/LmxhYmVsID8/IGlkfSBpcyB1bmF2YWlsYWJsZWAsXG4gICAgICAgIGRldGFpbDogJ1RoZSB0b29sIGNvdWxkIG5vdCBzdGFydC4gU2VlIHRoZSBicm93c2VyIGNvbnNvbGUgZm9yIGRldGFpbHMuJyxcbiAgICAgIH0pO1xuICAgICAgdGhpcy5kZXRlY3RDaGFuZ2VzU2FmZWx5KCk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogV2hlcmUgdGhlIG9wZW4gZGlhbG9nIHRvb2wncyBob3N0IGVsZW1lbnQgaXMgYXR0YWNoZWQgb25jZSBpdHMgZGlhbG9nIHJlbmRlcnMuICovXG4gIEBWaWV3Q2hpbGQoJ3Rvb2xEaWFsb2dTbG90JylcbiAgc2V0IHRvb2xEaWFsb2dTbG90KHJlZjogRWxlbWVudFJlZjxIVE1MRWxlbWVudD4gfCB1bmRlZmluZWQpIHtcbiAgICBjb25zdCBob3N0ID0gdGhpcy50b29sRGlhbG9nPy5ob3N0O1xuICAgIGlmIChyZWYgJiYgaG9zdCAmJiBob3N0LnBhcmVudEVsZW1lbnQgIT09IHJlZi5uYXRpdmVFbGVtZW50KSByZWYubmF0aXZlRWxlbWVudC5hcHBlbmRDaGlsZChob3N0KTtcbiAgfVxuXG4gIC8qKiBUb29sYmFyIHRvb2xzIGZvciBjb250cmlidXRlZCBtb2Rlcy4gQ2FsbHMgY2FuIGNvbWUgZnJvbSBvdXRzaWRlIEFuZ3VsYXIsIGhlbmNlIHRoZSB6b25lLiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IHBsb3RNb2RlVG9vbHM6IFBsb3RNb2RlVG9vbHMgPSB7XG4gICAgYXJtQnJ1c2g6IChicnVzaENsYXNzPzogUGxvdE1vZGVCcnVzaENsYXNzKSA9PiB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgdGhpcy5icnVzaENsYXNzID0geyBsYWJlbDogYnJ1c2hDbGFzcz8ubGFiZWwsIGNvbG9yOiBicnVzaENsYXNzPy5jb2xvciB9O1xuICAgICAgaWYgKHRoaXMuYWN0aXZlRHJhZ01vZGUgPT09ICdicnVzaCcpIHtcbiAgICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRCcnVzaE9wdGlvbnMoeyBzaXplOiB0aGlzLmJydXNoU2l6ZSwgLi4udGhpcy5icnVzaENsYXNzIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5hcHBseURyYWdNb2RlKCdicnVzaCcpO1xuICAgICAgfVxuICAgICAgdGhpcy5jZHIubWFya0ZvckNoZWNrKCk7XG4gICAgfSksXG4gICAgZGlzYXJtOiAoKSA9PiB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgdGhpcy5icnVzaENsYXNzID0gbnVsbDtcbiAgICAgIGlmICh0aGlzLmFjdGl2ZURyYWdNb2RlICE9PSBudWxsKSB0aGlzLmFwcGx5RHJhZ01vZGUobnVsbCk7XG4gICAgICB0aGlzLmNkci5tYXJrRm9yQ2hlY2soKTtcbiAgICB9KSxcbiAgICBhY3RpdmVUb29sJDogdGhpcy5zZXNzaW9uLmdldEFjdGl2ZVRvb2wkKCksXG4gIH07XG5cbiAgLyoqIERlYWN0aXZhdGUgd2hhdGV2ZXIgdG9vbCBpcyBhcm1lZCBhbmQgY2xlYXIgZXZlcnkgdG9vbCBtb2RlLiAqL1xuICBwcml2YXRlIGRlYWN0aXZhdGVBY3RpdmVUb29sKCkge1xuICAgIHRoaXMuYnJ1c2hDbGFzcyA9IG51bGw7XG4gICAgdGhpcy5hY3RpdmVEcmFnTW9kZSA9IG51bGw7XG4gICAgdGhpcy5zZXNzaW9uLnNldEFjdGl2ZVRvb2wobnVsbCk7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25PdmVybGF5KCk/LnNldE1vZGUoJ25vbmUnKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldERyYWdNb2RlKGZhbHNlKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFpvb21Ub0JveE1vZGUoZmFsc2UpO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0V2FuZE1vZGUoZmFsc2UpO1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0QnJ1c2hNb2RlKGZhbHNlKTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNhbVBvaW50TW9kZShmYWxzZSk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRWZXJ0ZXhFcmFzZXJNb2RlKGZhbHNlKTtcbiAgfVxufVxuXG4iLCI8IS0tIFNBTSB0b2FzdDogbGl2ZSBzdGF0dXMgKyBlbmNvZGVyLWRvd25sb2FkIHByb2dyZXNzIGJhciAoaml0LXVpIzkwKS4gU3RpY2t5XG4gICAgIHdoaWxlIGEgc2VnbWVudCBydW5zOyBzdGF5cyBvcGVuIHVudGlsIHRoZSBiYXIgcmVhY2hlcyAxMDAlIGFuZCB0aGUgcnVuIGVuZHMuIC0tPlxuPHAtdG9hc3QgW2tleV09XCJzYW1Ub2FzdEtleVwiIHBvc2l0aW9uPVwiYm90dG9tLXJpZ2h0XCI+XG4gIDxuZy10ZW1wbGF0ZSBsZXQtbWVzc2FnZSBwVGVtcGxhdGU9XCJtZXNzYWdlXCI+XG4gICAgPGRpdiBzdHlsZT1cImZsZXg6IDE7IG1pbi13aWR0aDogMTZyZW1cIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJmb250LWJvbGQgbWItMVwiPnt7IG1lc3NhZ2Uuc3VtbWFyeSB9fTwvZGl2PlxuICAgICAgPGRpdj57eyBzYW1TdGF0dXMgfHwgbWVzc2FnZS5kZXRhaWwgfX08L2Rpdj5cbiAgICAgIDxwLXByb2dyZXNzQmFyICpuZ0lmPVwic2FtRG93bmxvYWRpbmdcIiBbdmFsdWVdPVwic2FtUHJvZ3Jlc3NcIiBzdHlsZUNsYXNzPVwibXQtMiBzYW0tcHJvZ3Jlc3NcIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgICA8cC1wcm9ncmVzc0JhciAqbmdJZj1cInNhbUJ1c3kgJiYgIXNhbURvd25sb2FkaW5nXCIgbW9kZT1cImluZGV0ZXJtaW5hdGVcIiBzdHlsZUNsYXNzPVwibXQtMlwiXG4gICAgICAgICAgICAgICAgICAgICBbc3R5bGVdPVwieyBoZWlnaHQ6ICc0cHgnIH1cIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgPC9kaXY+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtdG9hc3Q+XG5cbjwhLS0gUmVzdWx0L2Vycm9yIG5vdGljZXMgZnJvbSB0aGUgbGlicmFyeSBpdHNlbGYgKHNlZ21lbnRhdGlvbiBvdXRjb21lLCByZWdpb25cbiAgICAgdmFsaWRhdGlvbikuIFJlbmRlcmVkIGhlcmUgc28gdGhleSBkbyBub3QgZGVwZW5kIG9uIHRoZSBob3N0IG1vdW50aW5nIGFcbiAgICAga2V5bGVzcyA8cC10b2FzdD4g4oCUIHdpdGhvdXQgdGhpcyB0aGUgbWVzc2FnZXMgd2VudCBub3doZXJlIGluIGFueSBob3N0IGJ1dFxuICAgICBqaXQtdWksIGFuZCBhIGZhaWxlZCBydW4gbG9va2VkIGxpa2UgYSBidXR0b24gdGhhdCBkaWQgbm90aGluZy4gLS0+XG48cC10b2FzdCBba2V5XT1cInJlc3VsdFRvYXN0S2V5XCIgcG9zaXRpb249XCJib3R0b20tcmlnaHRcIj48L3AtdG9hc3Q+XG5cbjwhLS0gU2hhcmVkIG91dGxldHMgZm9yIG5vdGljZXMgcmFpc2VkIHdoZXJlIHRoZXJlIGlzIG5vIHBlci1pbnN0YW5jZSBrZXkgdG8gdXNlOlxuICAgICB0aGUgcm9vdC1wcm92aWRlZCBQbG90bHlTZXJ2aWNlIGFuZCB0aGUgcmVnaW9uLWVkaXRvciBkaWFsb2cuIFJlbmRlcmVkIGJ5IGFcbiAgICAgc2luZ2xlIHZpc3VhbGl6ZXIgKG93bnNTaGFyZWRUb2FzdHMpIHNvIGEgbWVzc2FnZSBmcm9tIHRoZSByb290IHNlcnZpY2UgaXNcbiAgICAgbm90IHNob3duIG9uY2UgcGVyIGxpdmUgdmlld2VyLiAtLT5cbjxwLXRvYXN0ICpuZ0lmPVwib3duc1NoYXJlZFRvYXN0c1wiIFtrZXldPVwidml6VG9hc3RLZXlcIj48L3AtdG9hc3Q+XG48cC10b2FzdCAqbmdJZj1cIm93bnNTaGFyZWRUb2FzdHNcIiBba2V5XT1cInZpekFsZXJ0VG9hc3RLZXlcIiBwb3NpdGlvbj1cImNlbnRlclwiPjwvcC10b2FzdD5cblxuPCEtLSBUb29sYmFyIGRvY2s6IGRvY2tlZCBhY3Jvc3MgdGhlIHRvcCBieSBkZWZhdWx0OyBkcmFnIHRoZSBoYW5kbGUgdG8gZGV0YWNoXG4gICAgIGl0IGludG8gYSBmbG9hdGluZywgbW92YWJsZSB3aW5kb3cgKGZyZWVzIHRoZSB0b3Agcm93IGZvciB0aGUgaW1hZ2UpLiAtLT5cbjxkaXYgY2xhc3M9XCJ0b29sYmFyLWRvY2tcIiBbY2xhc3MuZmxvYXRpbmddPVwidG9vbGJhckZsb2F0aW5nXCJcbiAgICAgW3N0eWxlLmxlZnQucHhdPVwidG9vbGJhckZsb2F0aW5nID8gdG9vbGJhclBvcy54IDogbnVsbFwiXG4gICAgIFtzdHlsZS50b3AucHhdPVwidG9vbGJhckZsb2F0aW5nID8gdG9vbGJhclBvcy55IDogbnVsbFwiPlxuICA8ZGl2IGNsYXNzPVwidG9vbGJhci1kcmFnLWhhbmRsZVwiIChtb3VzZWRvd24pPVwib25Ub29sYmFyRHJhZ1N0YXJ0KCRldmVudClcIlxuICAgICAgIHBUb29sdGlwPVwiRHJhZyB0byBmbG9hdCB0aGUgdG9vbGJhclwiIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiPlxuICAgIDxpIGNsYXNzPVwicGkgcGktYmFyc1wiPjwvaT5cbiAgPC9kaXY+XG48cGxvdHRpbmctdG9vbGJhclxuICBbaW1hZ2VJbmZvXT1cImltYWdlSW5mb1wiXG4gIFtoYXNTcGF0aWFsRGF0YXNldF09XCJoYXNTcGF0aWFsRGF0YXNldFwiXG4gIFtwbG90VHlwZU9wdGlvbnNdPVwicGxvdFR5cGVNZW51XCJcbiAgW3NlbGVjdGVkUGxvdFR5cGVdPVwic2VsZWN0ZWRQbG90VHlwZUlkXCJcbiAgW2Jhc2VQbG90VHlwZV09XCJiYXNlUGxvdFR5cGVcIlxuICBbaXNvUmFuZ2VdPVwiaXNvUmFuZ2VcIlxuICBbbWF4SW5kZXhdPVwibWF4SW5kZXhcIlxuICBbekluZGV4XT1cInpJbmRleFwiXG4gIFtpc0hlYXRtYXBdPVwiaXNIZWF0bWFwXCJcbiAgW2lzM2RSZWdpb25zXT1cImlzU3BhdGlhbDNkTW9kZVwiXG4gIFthY3RpdmVEcmFnTW9kZV09XCJhY3RpdmVEcmFnTW9kZVwiXG4gIFthY3RpdmVTdXJmYWNlM2RNb2RlXT1cImFjdGl2ZVN1cmZhY2UzZE1vZGVcIlxuICBbYXhlc1Zpc2libGVdPVwiYXhlc1Zpc2libGVcIlxuICBbd2lyZWZyYW1lQWN0aXZlXT1cIndpcmVmcmFtZUFjdGl2ZVwiXG4gIFtyZXNvbHV0aW9uU2NhbGVdPVwicmVzb2x1dGlvblNjYWxlXCJcbiAgW3dhbmRTZW5zaXRpdml0eV09XCJ3YW5kU2Vuc2l0aXZpdHlcIlxuICBbYnJ1c2hTaXplXT1cImJydXNoU2l6ZVwiXG4gIFtzYW1Nb2RlbHNdPVwic2FtTW9kZWxzXCJcbiAgW3NhbU1vZGVsSWRdPVwic2FtTW9kZWxJZFwiXG4gIFtjb250cmlidXRlZFRvb2xzXT1cImNvbnRyaWJ1dGVkVG9vbHNcIlxuICBbZGlhbG9nVG9vbHNdPVwiZGlhbG9nVG9vbHNcIlxuICBbb3BlbkRpYWxvZ1Rvb2xJZF09XCJvcGVuRGlhbG9nVG9vbElkXCJcbiAgKHRvZ2dsZURpYWxvZ1Rvb2wpPVwidG9nZ2xlRGlhbG9nVG9vbCgkZXZlbnQpXCJcbiAgW3Rvb2xNb2RlbElkc109XCJ0b29sTW9kZWxJZHNcIlxuICBbdmVydGV4RXJhc2VyUmFkaXVzXT1cInZlcnRleEVyYXNlclJhZGl1c1wiXG4gIFtjYW5VbmRvXT1cImNhblVuZG9SZWdpb25cIlxuICBbY2FuUmVkb109XCJjYW5SZWRvUmVnaW9uXCJcbiAgW3N0YWNrT3B0aW9uc109XCJzdGFja09wdGlvbnNcIlxuICBbdG9vbHNdPVwidG9vbGJhclRvb2xzXCJcbiAgW2ltYWdlU21vb3RoaW5nRW5hYmxlZF09XCJpbWFnZVNtb290aGluZ0VuYWJsZWRcIlxuICAoc2VsZWN0UGxvdFR5cGUpPVwib25TZWxlY3RQbG90VHlwZSgkZXZlbnQpXCJcbiAgKHRvZ2dsZUltYWdlU21vb3RoaW5nKT1cIm9uVG9nZ2xlSW1hZ2VTbW9vdGhpbmcoKVwiXG4gIChhZGRQcm9maWxlTGluZSk9XCJhZGRQcm9maWxlTGluZSgpXCJcbiAgKGlzb1JhbmdlQ2hhbmdlKT1cIm9uSXNvUmFuZ2VDaGFuZ2UoJGV2ZW50KVwiXG4gIChvcGVuQ2hhbm5lbEhpc3RvZ3JhbSk9XCJvcGVuQ2hhbm5lbEhpc3RvZ3JhbSgpXCJcbiAgKG9wZW5TcGF0aWFsQ29udHJvbHMpPVwib3BlblNwYXRpYWxDb250cm9scygpXCJcbiAgKG9wZW5SZWdpb25FZGl0b3IpPVwib3BlblJlZ2lvbkVkaXRvcigpXCJcbiAgKHNlbGVjdFN0YWNrT3B0aW9uKT1cInNlbGVjdFN0YWNrT3B0aW9uKCRldmVudClcIlxuICAoelNjcnViKT1cIm9uWlNjcnViKCRldmVudClcIlxuICAoelNsaWRlKT1cIm9uWlNsaWRlKCRldmVudClcIlxuICAoekluZGV4SW5wdXQpPVwiekluZGV4ID0gJGV2ZW50OyB1cGRhdGVaSW5kZXgoKVwiXG4gIChyZWxvYWRBbmRQbG90KT1cInJlbG9hZEFuZFBsb3QoKVwiXG4gIChkb3dubG9hZEltYWdlKT1cImRvd25sb2FkSW1hZ2UoKVwiXG4gIChhdXRvc2NhbGVJbWFnZSk9XCJhdXRvc2NhbGVJbWFnZSgpXCJcbiAgKHRvZ2dsZURyYWdNb2RlKT1cInRvZ2dsZURyYWdNb2RlKCRldmVudClcIlxuICAoem9vbUluKT1cInpvb21JbigpXCJcbiAgKHpvb21PdXQpPVwiem9vbU91dCgpXCJcbiAgKHRvZ2dsZVN1cmZhY2UzZE1vZGUpPVwidG9nZ2xlU3VyZmFjZTNkTW9kZSgkZXZlbnQpXCJcbiAgKHJlc2V0U3VyZmFjZUNhbWVyYSk9XCJyZXNldFN1cmZhY2VDYW1lcmEoKVwiXG4gICh0b2dnbGVBeGVzKT1cInRvZ2dsZUF4ZXMoKVwiXG4gICh0b2dnbGVXaXJlZnJhbWUpPVwidG9nZ2xlV2lyZWZyYW1lKClcIlxuICAoc2VsZWN0UmVzb2x1dGlvbik9XCJzZWxlY3RSZXNvbHV0aW9uKCRldmVudClcIlxuICAoZGVsZXRlUmVnaW9uKT1cImRlbGV0ZVJlZ2lvbigpXCJcbiAgKHVuZG9SZWdpb24pPVwidW5kb1JlZ2lvbigpXCJcbiAgKHJlZG9SZWdpb24pPVwicmVkb1JlZ2lvbigpXCJcbiAgKHNlZ21lbnRSZWdpb25zKT1cInNlZ21lbnRSZWdpb25zKClcIlxuICAoc2VnbWVudENlbGxwb3NlKT1cInNlZ21lbnRDZWxscG9zZSgpXCJcbiAgKHNhbU1vZGVsQ2hhbmdlKT1cIm9uU2FtTW9kZWxDaGFuZ2UoJGV2ZW50KVwiXG4gIChydW5Ub29sKT1cInJ1blRvb2woJGV2ZW50KVwiXG4gICh0b29sTW9kZWxDaGFuZ2UpPVwib25Ub29sTW9kZWxDaGFuZ2UoJGV2ZW50KVwiXG4gIChvcGVuVG9vbFBhcmFtcyk9XCJvcGVuVG9vbFBhcmFtcygkZXZlbnQpXCJcbiAgKHdhbmRTZW5zaXRpdml0eUNoYW5nZSk9XCJvbldhbmRTZW5zaXRpdml0eUNoYW5nZSgkZXZlbnQpXCJcbiAgKGJydXNoU2l6ZUNoYW5nZSk9XCJvbkJydXNoU2l6ZUNoYW5nZSgkZXZlbnQpXCJcbiAgKHZlcnRleEVyYXNlclJhZGl1c0NoYW5nZSk9XCJvblZlcnRleEVyYXNlclJhZGl1c0NoYW5nZSgkZXZlbnQpXCI+XG4gIDwhLS0gSG9zdC1wcm9qZWN0ZWQgY3VzdG9tIHRvb2xiYXIgYnV0dG9ucywgZm9yd2FyZGVkIGludG8gdGhlIHRvb2xiYXIuIC0tPlxuICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG48L3Bsb3R0aW5nLXRvb2xiYXI+XG4gIDxwLWJ1dHRvbiAqbmdJZj1cInRvb2xiYXJGbG9hdGluZ1wiIGNsYXNzPVwidG9vbGJhci1kb2NrLWJ0blwiIGljb249XCJwaSBwaS10aHVtYnRhY2tcIiBzaXplPVwic21hbGxcIiBbdGV4dF09XCJ0cnVlXCJcbiAgICAgICAgICAgIHBUb29sdGlwPVwiRG9jayB0byB0b3BcIiB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIiAob25DbGljayk9XCJkb2NrVG9vbGJhcigpXCI+XG4gIDwvcC1idXR0b24+XG48L2Rpdj5cblxuPHAtY29udGV4dE1lbnUgI2NtIFttb2RlbF09XCJjb250ZXh0TWVudUl0ZW1zXCIgYXBwZW5kVG89XCJib2R5XCI+PC9wLWNvbnRleHRNZW51PlxuPCEtLSBObyBgaC1mdWxsYCBoZXJlOiBQcmltZUZsZXgncyBoZWlnaHQ6MTAwJSAhaW1wb3J0YW50IG1hZGUgI3Bsb3QgYXMgdGFsbCBhc1xuICAgICB0aGUgd2hvbGUgaG9zdCwgc28gdW5kZXIgdGhlIGRvY2tlZCB0b29sYmFyIGl0cyBib3R0b20gdG9vbGJhci1oZWlnaHQgc3RyaXBcbiAgICAgKGluY2wuIHRoZSBPU0Qgc2NhbGUgYmFyIGF0IGJvdHRvbS1sZWZ0KSB3YXMgY2xpcHBlZCBieSB0aGUgaG9zdCdzXG4gICAgIG92ZXJmbG93OmhpZGRlbi4gVGhlIGNvbXBvbmVudCBzdHlsZXNoZWV0IHNpemVzIGl0ICgudml6LXBsb3QgeyBmbGV4OiAxIH0pLiAtLT5cbjxkaXYgY2xhc3M9XCJ3LWZ1bGwgcmVsYXRpdmUgdml6LXBsb3RcIiBbaWRdPVwicGxvdERpdk5hbWVcIj5cbiAgPGRpdiAqbmdJZj1cImltZ0xvYWRpbmcgfHwgc3RhY2tMb2FkaW5nIHx8IGlzQ2FjaGluZ1wiIGlkPVwib3ZlcmxheVwiPlxuICAgIDwhLS0gRGV0ZXJtaW5hdGUgc3RhY2svdm9sdW1lIHByb2dyZXNzIHRha2VzIHByZWNlZGVuY2U6IHdoaWxlIGl0J3MgYWN0aXZlIHNob3cgT05MWSB0aGVcbiAgICAgICAgIHByb2dyZXNzIGJhciArIFwiTG9hZGluZyBmcmFtZXMuLi5cIiwgbm90IHRoZSBnZW5lcmljIHNwaW5uZXIvXCJMb2FkaW5nIGltYWdlLi4uXCIgb24gdG9wIG9mXG4gICAgICAgICBpdCAoYm90aCB3ZXJlIHJlbmRlcmluZyBhdCBvbmNlIGZvciBuYXBhcmkgdm9sdW1lL2lzb3N1cmZhY2UgbG9hZHMpLiAtLT5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwic3RhY2tMb2FkaW5nOyBlbHNlIGltZ0xvYWRpbmdUcGxcIj5cbiAgICAgIDxwLXByb2dyZXNzQmFyIGNsYXNzPVwicHJvZ3Jlc3NcIiBbdmFsdWVdPVwibG9hZGluZ1BlcmNlbnRhZ2VcIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRleHQtbG9hZGluZ1wiPkxvYWRpbmcgZnJhbWVzLi4uPC9zcGFuPlxuICAgIDwvbmctY29udGFpbmVyPlxuICAgIDxuZy10ZW1wbGF0ZSAjaW1nTG9hZGluZ1RwbD5cbiAgICAgIDwhLS0gQ2FjaGUtY29weSBwcm9ncmVzcyBpcyBzaG93biB3aGVuZXZlciBhIGNhY2hlIGRvd25sb2FkIGlzIGFjdGl2ZSAoaXNDYWNoaW5nKSwgaW5kZXBlbmRlbnRcbiAgICAgICAgICAgb2YgYGltZ0xvYWRpbmdgLCBzbyBpdCdzIG5ldmVyIGRyb3BwZWQvbWFza2VkIG1pZC1kb3dubG9hZC4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNDYWNoaW5nOyBlbHNlIHNwaW5uZXJMb2FkaW5nXCI+XG4gICAgICAgIDwhLS0gU3RhZ2UtYXdhcmUgbWVzc2FnZSBjb21lcyBmcm9tIENhY2hlUHJvZ3Jlc3NTZXJ2aWNlIHVwZGF0aW5nXG4gICAgICAgICAgICAgaW1hZ2VMb2FkaW5nTWVzc2FnZSQgKFwiQ2FjaGluZyBpbWFnZS4uLlwiIGR1cmluZyB0aGUgR0NTIGRvd25sb2FkLFxuICAgICAgICAgICAgIHRoZW4gXCJHZW5lcmF0aW5nIHByZXZpZXcuLi5cIiBkdXJpbmcgcGVyLXNsaWNlIHRodW1ibmFpbCBnZW4pLiBPbmNlIHRoZVxuICAgICAgICAgICAgIGRvd25sb2FkIGhpdHMgMTAwJSB0aGUgcmVtYWluaW5nIHdvcmsgKHNlcnZlciBwcmV2aWV3L3RpbGUgZ2VuZXJhdGlvbilcbiAgICAgICAgICAgICBoYXMgbm8gZGV0ZXJtaW5hdGUgcHJvZ3Jlc3MsIHNvIGZhbGwgdGhyb3VnaCB0byB0aGUgc3Bpbm5lciB0ZW1wbGF0ZVxuICAgICAgICAgICAgIHJhdGhlciB0aGFuIHBhcmtpbmcgYSBmdWxsIGJhciB1bmRlciBcIkxvYWRpbmcgaW1hZ2UuLi5cIi4gLS0+XG4gICAgICAgIDxwLXByb2dyZXNzQmFyIGNsYXNzPVwicHJvZ3Jlc3NcIiBbdmFsdWVdPVwiY2FjaGVQcm9ncmVzc1wiPjwvcC1wcm9ncmVzc0Jhcj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LWxvYWRpbmdcIj57e2xvYWRpbmdNZXNzYWdlfX08L3NwYW4+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgIDxuZy10ZW1wbGF0ZSAjc3Bpbm5lckxvYWRpbmc+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJpbWdMb2FkaW5nXCIgY2xhc3M9XCJzcGlubmVyXCI+PC9kaXY+XG4gICAgICAgIDxzcGFuICpuZ0lmPVwiaW1nTG9hZGluZyAmJiAhem9vbVwiIGNsYXNzPVwidGV4dC1sb2FkaW5nXCI+e3tsb2FkaW5nTWVzc2FnZX19PC9zcGFuPlxuICAgICAgICA8c3BhbiAqbmdJZj1cImltZ0xvYWRpbmcgJiYgem9vbVwiIGNsYXNzPVwiem9vbS10ZXh0LWxvYWRpbmdcIj57e2xvYWRpbmdNZXNzYWdlfX08L3NwYW4+XG4gICAgICA8L25nLXRlbXBsYXRlPlxuICAgIDwvbmctdGVtcGxhdGU+XG4gICAgPHAtYnV0dG9uICpuZ0lmPVwic3RhY2tMb2FkaW5nIHx8IGltZ0xvYWRpbmdcIiBjbGFzcz1cImNhbmNlbC1sb2FkaW5nIFwiIGxhYmVsPVwiQ2FuY2VsXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwiY2FuY2VsTG9hZGluZygpXCI+XG4gICAgPC9wLWJ1dHRvbj5cbiAgPC9kaXY+XG4gIDwhLS0gVHJhbnNsdWNlbnQgXCJzaGFycGVuaW5nXCIgb3ZlcmxheSBzaG93biBhZnRlciB0aGUgc21hbGwtdGllciBwcmV2aWV3IGlzXG4gICAgICAgb24gc2NyZWVuIHdoaWxlIHRoZSBsYXJnZS10aWVyIHJlbmRlciBpcyBzdGlsbCBsb2FkaW5nIGluIHRoZVxuICAgICAgIGJhY2tncm91bmQuIExldHMgdGhlIHVzZXIgc2VlIHRoZSBibHVycnkgaW1hZ2UgdW5kZXJuZWF0aCBidXQgbWFrZXNcbiAgICAgICBpdCBjbGVhciBzb21ldGhpbmcgaXMgc3RpbGwgbG9hZGluZyBzbyB0aGV5IGRvbid0IG1pc3Rha2UgdGhlXG4gICAgICAgbG93LXJlcyByZW5kZXIgZm9yIHRoZSBmaW5hbCBpbWFnZS4gLS0+XG4gIDxkaXYgKm5nSWY9XCJzaGFycGVuaW5nICYmICFpbWdMb2FkaW5nICYmICFzdGFja0xvYWRpbmcgJiYgIWlzQ2FjaGluZ1wiIGlkPVwic2hhcnBlbmluZy1vdmVybGF5XCI+XG4gICAgPGRpdiBjbGFzcz1cInNwaW5uZXJcIj48L2Rpdj5cbiAgICA8c3BhbiBjbGFzcz1cInRleHQtbG9hZGluZ1wiPlNoYXJwZW5pbmcgcHJldmlldy4uLjwvc3Bhbj5cbiAgPC9kaXY+XG5cbjwvZGl2PlxuXG48IS0tIEZsb2F0aW5nLCBkcmFnZ2FibGUgaW50ZW5zaXR5LXByb2ZpbGUgaW5zZXQuIFNob3duIGluIExJTkUgbW9kZTsgdGhlIGNoYXJ0XG4gICAgIHVwZGF0ZXMgbGl2ZSBhcyB0aGUgbGluZSBST0kgb24gdGhlIGltYWdlIGlzIG1vdmVkLiBLZXB0IE9VVFNJREUgI3Bsb3QgYW5kXG4gICAgIHBvc2l0aW9uZWQgZml4ZWQgc28gaXQgZmxvYXRzIG92ZXIgdGhlIHdob2xlIHBhZ2UgKG5vdCBjbGlwcGVkIHRvIHRoZVxuICAgICBkaWFncmFtL2NhbnZhcyBhcmVhKS4gLS0+XG48ZGl2ICpuZ0lmPVwiaGFzUHJvZmlsZXNcIiBjbGFzcz1cImludGVuc2l0eS1wYW5lbFwiXG4gICAgIFtzdHlsZS5sZWZ0LnB4XT1cInByb2ZpbGVQYW5lbFBvcy54XCIgW3N0eWxlLnRvcC5weF09XCJwcm9maWxlUGFuZWxQb3MueVwiPlxuICA8ZGl2IGNsYXNzPVwiaW50ZW5zaXR5LXBhbmVsLWhlYWRlclwiIChtb3VzZWRvd24pPVwib25Qcm9maWxlUGFuZWxEcmFnU3RhcnQoJGV2ZW50KVwiPlxuICAgIDxzcGFuPkludGVuc2l0eSBwcm9maWxlPC9zcGFuPlxuICAgIDxpIGNsYXNzPVwicGkgcGktYXJyb3dzLWFsdFwiPjwvaT5cbiAgPC9kaXY+XG4gIDxkaXYgW2lkXT1cImludGVuc2l0eUluc2V0RGl2XCIgY2xhc3M9XCJpbnRlbnNpdHktcGFuZWwtcGxvdFwiPjwvZGl2PlxuPC9kaXY+XG5cbjwhLS0gQ2hhbm5lbHMgJiBIaXN0b2dyYW06IG5vbi1tb2RhbCBmbG9hdGluZyBkaWFsb2cgb3BlbmVkIGZyb20gdGhlIHRvb2xiYXIuIC0tPlxuPGNoYW5uZWwtaGlzdG9ncmFtIFsodmlzaWJsZSldPVwic2hvd0NoYW5uZWxIaXN0b2dyYW1cIj48L2NoYW5uZWwtaGlzdG9ncmFtPlxuXG48IS0tIFNwYXRpYWwtb21pY3MgY29udHJvbHMuIFJlbmRlcnMgaXRzIG93biBlbXB0eSBzdGF0ZSB3aGVuIG5vIGRhdGFzZXQgKG9yIG5vXG4gICAgIFNQQVRJQUxfREFUQV9QT1JUKSBpcyBwcmVzZW50LCBzbyBpdCBpcyBhbHdheXMgc2FmZSB0byBob3N0LiAtLT5cbjxzcGF0aWFsLWNvbnRyb2xzIFsodmlzaWJsZSldPVwic2hvd1NwYXRpYWxDb250cm9sc1wiIFtpczNkXT1cImlzU3BhdGlhbDNkTW9kZVwiID48L3NwYXRpYWwtY29udHJvbHM+XG5cbjwhLS0gU2lkZSBwYW5lbCBvZiB0aGUgYWN0aXZlIGNvbnRyaWJ1dGVkIHBsb3QgbW9kZSAoUExPVF9UWVBFX0NPTlRSSUJVVElPTlMpLFxuICAgICBpbiB0aGUgc2FtZSByaWdodC1oYW5kIGFyZWEgYXMgdGhlIHNwYXRpYWwgY29udHJvbHMgYW5kIHRoZSBSZWdpb24gRWRpdG9yLlxuICAgICBFeGlzdHMgb25seSB3aGlsZSB0aGUgbW9kZSdzIHNlc3Npb24gaXMgbGl2ZTsgdGhlcmUgaXMgbm8gY2xvc2UgYnV0dG9uLFxuICAgICBiZWNhdXNlIGxlYXZpbmcgdGhlIG1vZGUgaXMgd2hhdCBlbmRzIGl0LiBBIGNvbXBvbmVudCBwYW5lbCBnZXRzXG4gICAgIFBMT1RfTU9ERV9DT05URVhUIC8gUExPVF9NT0RFX1NFU1NJT04gZnJvbSBpdHMgaW5qZWN0b3I7IGEgYG1vdW50YCBwYW5lbCdzXG4gICAgIGhvc3QgZWxlbWVudCBpcyBhdHRhY2hlZCB0byB0aGUgc2xvdCBiZWxvdyAoc2VlIHBsb3RNb2RlUGFuZWxTbG90KS4gLS0+XG48cC1kaWFsb2cgKm5nSWY9XCJwbG90TW9kZVBhbmVsIGFzIHBhbmVsXCIgW2hlYWRlcl09XCJwYW5lbC50aXRsZVwiIFt2aXNpYmxlXT1cInRydWVcIlxuICAgICAgICAgIFttb2RhbF09XCJmYWxzZVwiIFtjbG9zYWJsZV09XCJmYWxzZVwiIFtyZXNpemFibGVdPVwidHJ1ZVwiIFtkcmFnZ2FibGVdPVwidHJ1ZVwiXG4gICAgICAgICAgYXBwZW5kVG89XCJib2R5XCIgcG9zaXRpb249XCJyaWdodFwiIHN0eWxlQ2xhc3M9XCJwbG90LW1vZGUtcGFuZWxcIlxuICAgICAgICAgIFtzdHlsZV09XCJ7IHdpZHRoOiAnMjJyZW0nLCBtYXhIZWlnaHQ6ICc4NXZoJyB9XCJcbiAgICAgICAgICBbY29udGVudFN0eWxlXT1cInsgb3ZlcmZsb3c6ICdhdXRvJyB9XCI+XG4gIDxuZy1jb250YWluZXIgKm5nSWY9XCJwYW5lbC5jb21wb25lbnRcIj5cbiAgICA8bmctY29udGFpbmVyICpuZ0NvbXBvbmVudE91dGxldD1cInBhbmVsLmNvbXBvbmVudDsgaW5qZWN0b3I6IHBhbmVsLmluamVjdG9yID8/IHVuZGVmaW5lZFwiPjwvbmctY29udGFpbmVyPlxuICA8L25nLWNvbnRhaW5lcj5cbiAgPGRpdiAqbmdJZj1cInBhbmVsLmhvc3RcIiAjcGxvdE1vZGVQYW5lbFNsb3QgY2xhc3M9XCJwbG90LW1vZGUtcGFuZWwtc2xvdFwiPjwvZGl2PlxuPC9wLWRpYWxvZz5cblxuPCEtLSBBbiBvcGVuIGRpYWxvZyB0b29sIChUT09MQkFSX1RPT0xTLCBraW5kOiAnZGlhbG9nJykuIE5vbi1tb2RhbCBzbyB0aGUgdXNlciBjYW5cbiAgICAga2VlcCB3b3JraW5nIG9uIHRoZSBpbWFnZSwgZS5nLiBwYWludGluZyB3aXRoIHRoZSBicnVzaCB0aGUgdG9vbCBhcm1lZC4gVGhlXG4gICAgIHRvb2wgcmVuZGVycyBpdHMgYm9keSBpbnRvIGEgaG9zdCBlbGVtZW50IGF0dGFjaGVkIHRvIHRoZSBzbG90IGJlbG93IChzZWVcbiAgICAgdG9vbERpYWxvZ1Nsb3QpOyBjbG9zaW5nIHRoZSBkaWFsb2cgZW5kcyB0aGUgdG9vbCdzIHNlc3Npb24uIC0tPlxuPHAtZGlhbG9nICpuZ0lmPVwidG9vbERpYWxvZyBhcyBkbGdcIiBbaGVhZGVyXT1cImRsZy50aXRsZVwiIFt2aXNpYmxlXT1cInRydWVcIlxuICAgICAgICAgICh2aXNpYmxlQ2hhbmdlKT1cIiRldmVudCB8fCBjbG9zZURpYWxvZ1Rvb2woKVwiXG4gICAgICAgICAgW21vZGFsXT1cImZhbHNlXCIgW2Nsb3NhYmxlXT1cInRydWVcIiBbcmVzaXphYmxlXT1cInRydWVcIiBbZHJhZ2dhYmxlXT1cInRydWVcIlxuICAgICAgICAgIGFwcGVuZFRvPVwiYm9keVwiIHBvc2l0aW9uPVwicmlnaHRcIiBzdHlsZUNsYXNzPVwidG9vbC1kaWFsb2dcIlxuICAgICAgICAgIFtzdHlsZV09XCJ7IHdpZHRoOiBkbGcud2lkdGgsIG1heEhlaWdodDogJzg1dmgnIH1cIlxuICAgICAgICAgIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIj5cbiAgPGRpdiAjdG9vbERpYWxvZ1Nsb3QgY2xhc3M9XCJ0b29sLWRpYWxvZy1zbG90XCI+PC9kaXY+XG48L3AtZGlhbG9nPlxuXG48IS0tIFJlZ2lvbiBFZGl0b3I6IG5vbi1tb2RhbCwgZHJhZ2dhYmxlLCByZXNpemFibGUgZmxvYXRpbmcgZGlhbG9nIG9wZW5lZCBmcm9tXG4gICAgIHRoZSB0b29sYmFyIChmb3JtZXJseSB0aGUgcmlnaHQtcGFuZWwgUmVnaW9ucyB0YWIpLiBNb2RlbGVzcyBzbyByZWdpb25zIGNhblxuICAgICBzdGlsbCBiZSBkcmF3bi9zZWxlY3RlZCBvbiB0aGUgcGxvdCB3aGlsZSBpdCdzIG9wZW47IHN0YXlzIGxpbmtlZCB0byB0aGlzXG4gICAgIHBsb3R0aW5nIGluc3RhbmNlIHZpYSB0aGUgcm9vdCBSZWdpb25TdG9yZSBzaW5nbGV0b24uIE9wZW5zIG92ZXIgdGhlIHJpZ2h0XG4gICAgIHNpZGUgb2YgdGhlIHZpZXcuIC0tPlxuPHAtZGlhbG9nIGhlYWRlcj1cIlJlZ2lvbiBFZGl0b3JcIiBbKHZpc2libGUpXT1cInNob3dSZWdpb25FZGl0b3JcIlxuICAgICAgICAgIFttb2RhbF09XCJmYWxzZVwiIFtyZXNpemFibGVdPVwidHJ1ZVwiIFtkcmFnZ2FibGVdPVwidHJ1ZVwiXG4gICAgICAgICAgW21heGltaXphYmxlXT1cInRydWVcIiBhcHBlbmRUbz1cImJvZHlcIiBwb3NpdGlvbj1cInJpZ2h0XCJcbiAgICAgICAgICBbc3R5bGVdPVwieyB3aWR0aDogcmVnaW9uRWRpdG9yV2lkdGgsIGhlaWdodDogJzg1dmgnIH1cIlxuICAgICAgICAgIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIj5cbiAgPHJlZ2lvbi1lZGl0b3I+PC9yZWdpb24tZWRpdG9yPlxuPC9wLWRpYWxvZz5cblxuPCEtLSBTaW1wbGlmeSAoRG91Z2xhc+KAk1BldWNrZXIpOiBjdXN0b20gcHgtdGhyZXNob2xkIHBpY2tlciBmb3IgdGhlIHNlbGVjdGVkXG4gICAgIHJlZ2lvbihzKSwgb3BlbmVkIGZyb20gdGhlIHJpZ2h0LWNsaWNrIFwiU2ltcGxpZnkg4pa4IEN1c3RvbeKAplwiIGFjdGlvbi4gLS0+XG48cC1kaWFsb2cgaGVhZGVyPVwiU2ltcGxpZnkgcmVnaW9uKHMpXCIgWyh2aXNpYmxlKV09XCJkaXNwbGF5U2ltcGxpZnlEaWFsb2dcIiBbbW9kYWxdPVwidHJ1ZVwiXG4gICAgICAgICAgW3N0eWxlXT1cInt3aWR0aDogJzIycmVtJ31cIiBbZHJhZ2dhYmxlXT1cImZhbHNlXCIgW3Jlc2l6YWJsZV09XCJmYWxzZVwiPlxuICA8ZGl2IGNsYXNzPVwiZmxleCBmbGV4LWNvbHVtbiBnYXAtMlwiPlxuICAgIDxzcGFuPkFsdGl0dWRlIHRocmVzaG9sZDogPGI+e3sgc2ltcGxpZnlUaHJlc2hvbGQgfX0gcHg8L2I+PC9zcGFuPlxuICAgIDxwLXNsaWRlciBbKG5nTW9kZWwpXT1cInNpbXBsaWZ5VGhyZXNob2xkXCIgW21pbl09XCIxXCIgW21heF09XCIyMFwiIFtzdGVwXT1cIjFcIj48L3Atc2xpZGVyPlxuICAgIDxzbWFsbD5WZXJ0aWNlcyB3aXRoaW4gdGhpcyBkaXN0YW5jZSBvZiB0aGUgbGluZSBiZXR3ZWVuIHRoZWlyIG5laWdoYm91cnMgYXJlIHJlbW92ZWQuPC9zbWFsbD5cbiAgPC9kaXY+XG4gIDxuZy10ZW1wbGF0ZSBwVGVtcGxhdGU9XCJmb290ZXJcIj5cbiAgICA8cC1idXR0b24gbGFiZWw9XCJDYW5jZWxcIiBbdGV4dF09XCJ0cnVlXCIgKG9uQ2xpY2spPVwiZGlzcGxheVNpbXBsaWZ5RGlhbG9nID0gZmFsc2VcIj48L3AtYnV0dG9uPlxuICAgIDxwLWJ1dHRvbiBsYWJlbD1cIkFwcGx5XCIgaWNvbj1cInBpIHBpLWNoZWNrXCIgKG9uQ2xpY2spPVwic2ltcGxpZnlSZWdpb25zKHNpbXBsaWZ5VGhyZXNob2xkKVwiPjwvcC1idXR0b24+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtZGlhbG9nPlxuXG48IS0tIFBhcmFtZXRlcnMgZm9yIHdoaWNoZXZlciBjb250cmlidXRlZCB0b29sIGlzIG9wZW4uIE9uZSBkaWFsb2cgZm9yIGFsbCBvZlxuICAgICB0aGVtOiBhIHRvb2wgZGVjbGFyZXMgaXRzIGZpZWxkcyAoVG9vbFBhcmFtU3BlYykgYW5kIHRoaXMgcmVuZGVycyB0aGVtLCBzb1xuICAgICB0aGUgdG9vbGJhciBjYW4gbGF5IGV2ZXJ5IHRvb2wgb3V0IHRoZSBzYW1lIHdheSBhbmQgYSBwbHVnaW4gbmVlZHMgbm9cbiAgICAgUHJpbWVORyBvZiBpdHMgb3duLiBEZWZhdWx0cyBjb21lIGZyb20gdGhlIGFjdGl2ZSBjaGVja3BvaW50LCBzaW5jZSB0aGV5XG4gICAgIGVuY29kZSB3aGF0IGl0IHdhcyB0cmFpbmVkIGZvci4gLS0+XG48cC1kaWFsb2cgKm5nSWY9XCJvcGVuUGFyYW1zVG9vbCBhcyB0b29sXCJcbiAgICAgICAgICBbaGVhZGVyXT1cInRvb2wubGFiZWwgKyAnIHBhcmFtZXRlcnMnXCJcbiAgICAgICAgICBbdmlzaWJsZV09XCJ0cnVlXCIgKHZpc2libGVDaGFuZ2UpPVwib3BlblBhcmFtc1Rvb2xJZCA9IG51bGxcIlxuICAgICAgICAgIFttb2RhbF09XCJ0cnVlXCJcbiAgICAgICAgICBbc3R5bGVdPVwie3dpZHRoOiAnMzByZW0nfVwiIFtkcmFnZ2FibGVdPVwiZmFsc2VcIiBbcmVzaXphYmxlXT1cImZhbHNlXCIgYXBwZW5kVG89XCJib2R5XCI+XG4gIDxkaXYgY2xhc3M9XCJncmlkIGZvcm1ncmlkIHAtZmx1aWRcIj5cbiAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCBwIG9mIHRvb2wucGFyYW1zXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiZmllbGRcIiBbbmdDbGFzc109XCJwLnR5cGUgPT09ICdib29sZWFuJyA/ICdjb2wtMTInIDogJ2NvbC02J1wiPlxuICAgICAgICA8bmctY29udGFpbmVyIFtuZ1N3aXRjaF09XCJwLnR5cGVcIj5cblxuICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nU3dpdGNoQ2FzZT1cIidudW1iZXInXCI+XG4gICAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJ0ZXh0LXNtXCIgW3BUb29sdGlwXT1cInAudG9vbHRpcCB8fCAnJ1wiPnt7IHAubGFiZWwgfX08L2xhYmVsPlxuICAgICAgICAgICAgPHAtaW5wdXROdW1iZXIgW25nTW9kZWxdPVwicGFyYW1zRm9yKHRvb2wuaWQpW3AuaWRdXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInBhcmFtc0Zvcih0b29sLmlkKVtwLmlkXSA9ICRldmVudFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICBbbWluXT1cIm51bWJlclBhcmFtKHApLm1pblwiIFttYXhdPVwibnVtYmVyUGFyYW0ocCkubWF4XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIFtzdGVwXT1cIm51bWJlclBhcmFtKHApLnN0ZXBcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgW21pbkZyYWN0aW9uRGlnaXRzXT1cIm51bWJlclBhcmFtKHApLmZyYWN0aW9uRGlnaXRzXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgIFtzaG93QnV0dG9uc109XCJ0cnVlXCI+PC9wLWlucHV0TnVtYmVyPlxuICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdTd2l0Y2hDYXNlPVwiJ3NlbGVjdCdcIj5cbiAgICAgICAgICAgIDxsYWJlbCBjbGFzcz1cInRleHQtc21cIiBbcFRvb2x0aXBdPVwicC50b29sdGlwIHx8ICcnXCI+e3sgcC5sYWJlbCB9fTwvbGFiZWw+XG4gICAgICAgICAgICA8cC1kcm9wZG93biBbb3B0aW9uc109XCJzZWxlY3RQYXJhbShwKS5vcHRpb25zXCIgb3B0aW9uTGFiZWw9XCJsYWJlbFwiIG9wdGlvblZhbHVlPVwidmFsdWVcIlxuICAgICAgICAgICAgICAgICAgICAgICAgW25nTW9kZWxdPVwicGFyYW1zRm9yKHRvb2wuaWQpW3AuaWRdXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInBhcmFtc0Zvcih0b29sLmlkKVtwLmlkXSA9ICRldmVudFwiXG4gICAgICAgICAgICAgICAgICAgICAgICBhcHBlbmRUbz1cImJvZHlcIj48L3AtZHJvcGRvd24+XG4gICAgICAgICAgPC9uZy1jb250YWluZXI+XG5cbiAgICAgICAgICA8IS0tIENoZWNrYm94IGxhYmVsIHNpdHMgYmVzaWRlIHRoZSBib3ggYW5kIG1heSBydW4gdG8gYSBzZW50ZW5jZSwgc29cbiAgICAgICAgICAgICAgIGl0IGRvZXMgbm90IGdldCB0aGUgc3RhY2tlZCBsYWJlbCB0aGUgb3RoZXIgdHlwZXMgdXNlLiAtLT5cbiAgICAgICAgICA8ZGl2ICpuZ1N3aXRjaENhc2U9XCInYm9vbGVhbidcIiBjbGFzcz1cImZsZXggYWxpZ24taXRlbXMtY2VudGVyIGdhcC0yXCI+XG4gICAgICAgICAgICA8cC1jaGVja2JveCBbbmdNb2RlbF09XCJwYXJhbXNGb3IodG9vbC5pZClbcC5pZF1cIlxuICAgICAgICAgICAgICAgICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwicGFyYW1zRm9yKHRvb2wuaWQpW3AuaWRdID0gJGV2ZW50XCJcbiAgICAgICAgICAgICAgICAgICAgICAgIFtiaW5hcnldPVwidHJ1ZVwiIFtpbnB1dElkXT1cInRvb2wuaWQgKyAnLScgKyBwLmlkXCI+PC9wLWNoZWNrYm94PlxuICAgICAgICAgICAgPGxhYmVsIFtmb3JdPVwidG9vbC5pZCArICctJyArIHAuaWRcIiBjbGFzcz1cInRleHQtc20gbWItMFwiXG4gICAgICAgICAgICAgICAgICAgW3BUb29sdGlwXT1cInAudG9vbHRpcCB8fCAnJ1wiPnt7IHAubGFiZWwgfX08L2xhYmVsPlxuICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPC9kaXY+XG4gICAgPC9uZy1jb250YWluZXI+XG4gIDwvZGl2PlxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPHAtYnV0dG9uIGxhYmVsPVwiUmVzZXQgdG8gbW9kZWwgZGVmYXVsdHNcIiBbdGV4dF09XCJ0cnVlXCIgc2l6ZT1cInNtYWxsXCJcbiAgICAgICAgICAgICAgKG9uQ2xpY2spPVwicmVzZXRUb29sUGFyYW1zKHRvb2wuaWQpXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gbGFiZWw9XCJDbG9zZVwiIHNpemU9XCJzbWFsbFwiIChvbkNsaWNrKT1cIm9wZW5QYXJhbXNUb29sSWQgPSBudWxsXCI+PC9wLWJ1dHRvbj5cbiAgPC9uZy10ZW1wbGF0ZT5cbjwvcC1kaWFsb2c+XG4iXX0=
1173
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlzdWFsaXplci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3Zpc3VhbGl6ZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vc3JjL2xpYi92aXN1YWxpemVyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFFTCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxVQUFVLEVBQ1YsVUFBVSxFQUNWLFlBQVksRUFDWixNQUFNLEVBQ04sUUFBUSxFQUNSLEtBQUssRUFDTCxNQUFNLEVBSU4sUUFBUSxFQUNSLE1BQU0sRUFFTixTQUFTLEVBQ1QsUUFBUSxFQUNSLE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTdDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFbEMsT0FBTyxFQUFZLGNBQWMsRUFBRSxZQUFZLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDckUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsV0FBVyxFQUFFLGlCQUFpQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDckUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxx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QixNQUFNOzJCQUFDLFVBQVU7OzBCQVNqQixRQUFROzswQkFBSSxNQUFNOzJCQUFDLFVBQVU7OzBCQUk3QixRQUFROzswQkFBSSxNQUFNOzJCQUFDLGFBQWE7OzBCQUdoQyxRQUFROzswQkFBSSxNQUFNOzJCQUFDLGlCQUFpQjs7MEJBR3BDLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsdUJBQXVCOzswQkFHMUMsUUFBUTs7MEJBRVIsUUFBUTtrRUFoVlgsWUFBWTtzQkFEWCxNQUFNO2dCQUlQLGdCQUFnQjtzQkFEZixNQUFNO2dCQU1ILFlBQVk7c0JBRGYsS0FBSztnQkFjTixRQUFRO3NCQURQLEtBQUs7Z0JBMENXLFdBQVc7c0JBQTNCLFNBQVM7dUJBQUMsSUFBSTtnQkFrTFgsaUJBQWlCO3NCQURwQixTQUFTO3VCQUFDLG1CQUFtQjtnQkE4QndCLEtBQUs7c0JBQTFELFNBQVM7dUJBQUMsdUJBQXVCLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO2dCQW15QmhELGNBQWM7c0JBRGpCLFNBQVM7dUJBQUMsZ0JBQWdCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENoYW5nZURldGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIERlc3Ryb3lSZWYsXG4gIEVsZW1lbnRSZWYsXG4gIEV2ZW50RW1pdHRlcixcbiAgSW5qZWN0LFxuICBJbmplY3RvcixcbiAgSW5wdXQsXG4gIE5nWm9uZSxcbiAgT25DaGFuZ2VzLFxuICBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgT3B0aW9uYWwsXG4gIE91dHB1dCxcbiAgU2ltcGxlQ2hhbmdlcyxcbiAgVmlld0NoaWxkLFxuICBjb21wdXRlZCxcbiAgc2lnbmFsLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IHRha2VVbnRpbERlc3Ryb3llZCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUvcnhqcy1pbnRlcm9wJztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcblxuaW1wb3J0IHsgT2JzZXJ2YWJsZSB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgeyBNZW51SXRlbSwgTWVzc2FnZVNlcnZpY2UsIFNoYXJlZE1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvYXBpJztcbmltcG9ydCB7IEJ1dHRvbk1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvYnV0dG9uJztcbmltcG9ydCB7IENoZWNrYm94TW9kdWxlIH0gZnJvbSAncHJpbWVuZy9jaGVja2JveCc7XG5pbXBvcnQgeyBDb250ZXh0TWVudSwgQ29udGV4dE1lbnVNb2R1bGUgfSBmcm9tICdwcmltZW5nL2NvbnRleHRtZW51JztcbmltcG9ydCB7IERpYWxvZ01vZHVsZSB9IGZyb20gJ3ByaW1lbmcvZGlhbG9nJztcbmltcG9ydCB7IERyb3Bkb3duTW9kdWxlIH0gZnJvbSAncHJpbWVuZy9kcm9wZG93bic7XG5pbXBvcnQgeyBJbnB1dE51bWJlck1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvaW5wdXRudW1iZXInO1xuaW1wb3J0IHsgUHJvZ3Jlc3NCYXJNb2R1bGUgfSBmcm9tICdwcmltZW5nL3Byb2dyZXNzYmFyJztcbmltcG9ydCB7IFNsaWRlck1vZHVsZSB9IGZyb20gJ3ByaW1lbmcvc2xpZGVyJztcbmltcG9ydCB7IFRvYXN0TW9kdWxlIH0gZnJvbSAncHJpbWVuZy90b2FzdCc7XG5pbXBvcnQgeyBUb29sdGlwTW9kdWxlIH0gZnJvbSAncHJpbWVuZy90b29sdGlwJztcbmltcG9ydCB7IElJbWFnZUluZm8gfSBmcm9tICcuL2NvbnRyYWN0cy9pbWFnZS5jb250cmFjdCc7XG5pbXBvcnQgeyBJbWFnZVN0YXRlUG9ydCwgSU1BR0VfU1RBVEVfUE9SVCB9IGZyb20gJy4vY29udHJhY3RzL3BvcnRzL2ltYWdlLXN0YXRlLnBvcnQnO1xuaW1wb3J0IHsgUG9seWdvbiB9IGZyb20gJy4vbW9kZWxzL3JlZ2lvbic7XG5pbXBvcnQgeyBSZWdpb25PcHNTZXJ2aWNlIH0gZnJvbSAnLi9yZWdpb24tb3BzLnNlcnZpY2UnO1xuaW1wb3J0IHsgVklaX1RPQVNUX0tFWSwgVklaX0FMRVJUX1RPQVNUX0tFWSB9IGZyb20gJy4vdG9hc3Qtb3V0bGV0cyc7XG5pbXBvcnQgeyBWaXN1YWxpemVyU3RvcmUgfSBmcm9tICcuL3N0b3JlL3Zpc3VhbGl6ZXItc3RvcmUuc2VydmljZSc7XG5pbXBvcnQgeyBTbGljZVNjcnViYmVyIH0gZnJvbSAnLi9yZW5kZXItb3JjaGVzdHJhdG9yJztcbmltcG9ydCB7IEltYWdlUmVuZGVyU2Vzc2lvbiwgZXJyb3JNZXNzYWdlIH0gZnJvbSAnLi9yZW5kZXItc2Vzc2lvbic7XG5pbXBvcnQge1xuICBQbG90VHlwZSxcbiAgUGxvdFR5cGVEZXNjcmlwdG9yLFxuICBQbG90VHlwZUlkLFxuICBnZXRQbG90VHlwZURlc2NyaXB0b3IsXG4gIGlzQnVpbHRpblBsb3RUeXBlLFxuICBpc05hcGFyaTNkLFxuICBpc1NwYXRpYWxPbWljcyxcbiAgaXNTcGF0aWFsT21pY3MzZCxcbiAgcmVuZGVyZXJPd25zV2hlZWwsXG4gIE5BUEFSSV9ERUZBVUxUX0RFQ0lNQVRFLFxufSBmcm9tICcuL2NvbnRyYWN0cy9wbG90LXR5cGUnO1xuaW1wb3J0IHtcbiAgUExPVF9UWVBFX0NPTlRSSUJVVElPTlMsXG4gIFBsb3RUeXBlQ29udHJpYnV0aW9uLFxuICBQbG90VHlwZU9wdGlvbixcbn0gZnJvbSAnLi9jb250cmFjdHMvcGxvdC10eXBlLWNvbnRyaWJ1dGlvbi5jb250cmFjdCc7XG5pbXBvcnQgeyBQbG90TW9kZUNvbnRyb2xsZXIgfSBmcm9tICcuL3Bsb3QtbW9kZS9wbG90LW1vZGUtY29udHJvbGxlcic7XG5pbXBvcnQgeyBjb21wdXRlUGxvdFR5cGVNZW51LCByZWNvbmNpbGVQbG90VHlwZSB9IGZyb20gJy4vcGxvdC1tb2RlL3Bsb3QtdHlwZS1tZW51JztcbmltcG9ydCB7IFRvb2xQYXJhbXNNb2RlbCB9IGZyb20gJy4vcGxvdC1tb2RlL3Rvb2wtcGFyYW1zLW1vZGVsJztcbmltcG9ydCB7IENvbnRyaWJ1dGlvbkhvc3QsIFBsb3RNb2RlUGFuZWxWaWV3LCBUb29sRGlhbG9nVmlldyB9IGZyb20gJy4vcGxvdC1tb2RlL2NvbnRyaWJ1dGlvbi1ob3N0JztcbmltcG9ydCB7IElWaXN1YWxpemVyLCBWSVNVQUxJWkVSLCBWaXN1YWxpemVySGFuZGxlIH0gZnJvbSAnLi9jb250cmFjdHMvdmlzdWFsaXplci5jb250cmFjdCc7XG5pbXBvcnQgeyBTQU1fTU9ERUxTLCBnZXREZWZhdWx0U2FtTW9kZWxJZCwgaXNTYW1Nb2RlbFJlYWR5IH0gZnJvbSAnLi90b29sYmFyL3NlZ21lbnRhdGlvbi9zYW0tbW9kZWwtcmVnaXN0cnknO1xuaW1wb3J0IHsgU2FtVG9vbFNlcnZpY2UgfSBmcm9tICcuL3Rvb2xiYXIvc2VnbWVudGF0aW9uL3NhbS10b29sLnNlcnZpY2UnO1xuaW1wb3J0IHsgU2FtUG9pbnRUb29sU2VydmljZSB9IGZyb20gJy4vdG9vbGJhci9zZWdtZW50YXRpb24vc2FtLXBvaW50LXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBDZWxsU2VnbWVudFRvb2xTZXJ2aWNlIH0gZnJvbSAnLi90b29sYmFyL3NlZ21lbnRhdGlvbi9jZWxsLXNlZ21lbnQtdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IFNlZ21lbnRhdGlvblJ1bm5lciB9IGZyb20gJy4vdG9vbGJhci9zZWdtZW50YXRpb24vc2VnbWVudGF0aW9uLXJ1bm5lcic7XG5pbXBvcnQge1xuICBUT09MQkFSX1RPT0xTLFxuICBUb29sYmFyQ29udHJpYnV0aW9uLFxuICBUb29sYmFyRGlhbG9nVG9vbENvbnRyaWJ1dGlvbixcbiAgVG9vbGJhclRvb2xDb250cmlidXRpb24sXG4gIHZpc2libGVUb29sQ29udHJpYnV0aW9ucyxcbn0gZnJvbSAnLi9jb250cmFjdHMvdG9vbGJhci10b29sLmNvbnRyYWN0JztcbmltcG9ydCB7IFRvb2xiYXJUb29sVmlzaWJpbGl0eSwgQUxMX1RPT0xCQVJfVE9PTFMgfSBmcm9tICcuL2NvbnRyYWN0cy90b29sYmFyLWNvbmZpZyc7XG5pbXBvcnQgeyBWSVpfQ09ORklHLCBWaXpDb25maWcgfSBmcm9tICcuL2NvbnRyYWN0cy92aXotY29uZmlnJztcbmltcG9ydCB7IFNQQVRJQUxfREFUQV9QT1JULCBTcGF0aWFsRGF0YVBvcnQgfSBmcm9tICcuL2NvbnRyYWN0cy9wb3J0cy9zcGF0aWFsLWRhdGEucG9ydCc7XG5pbXBvcnQgeyBhcHBseUltYWdlUm9pcyB9IGZyb20gJy4vdmlzdWFsaXplci9yZWdpb24tbG9hZCc7XG5pbXBvcnQgeyBDb250ZXh0TWVudUFjdGlvbnMsIENvbnRleHRNZW51U3RhdGUsIGJ1aWxkQ29udGV4dE1lbnUgfSBmcm9tICcuL3Zpc3VhbGl6ZXIvdmlzdWFsaXplci1jb250ZXh0LW1lbnUnO1xuaW1wb3J0IHsgUmVnaW9uQWN0aW9ucyB9IGZyb20gJy4vdmlzdWFsaXplci9yZWdpb24tYWN0aW9ucyc7XG5pbXBvcnQgeyBUb29sTW9kZXMgfSBmcm9tICcuL3Zpc3VhbGl6ZXIvdG9vbC1tb2Rlcyc7XG5pbXBvcnQgeyBTaG9ydGN1dEhvc3QsIFZpZXdlclNob3J0Y3V0cyB9IGZyb20gJy4vdmlzdWFsaXplci92aWV3ZXItc2hvcnRjdXRzJztcbmltcG9ydCB7IEZsb2F0aW5nRHJhZ0RpcmVjdGl2ZSwgRmxvYXRpbmdQb3MgfSBmcm9tICcuL3Zpc3VhbGl6ZXIvZmxvYXRpbmctZHJhZy5kaXJlY3RpdmUnO1xuaW1wb3J0IHsgSW50ZW5zaXR5SW5zZXRDb21wb25lbnQgfSBmcm9tICcuL2ludGVuc2l0eS1pbnNldC9pbnRlbnNpdHktaW5zZXQuY29tcG9uZW50JztcbmltcG9ydCB7IFNwYXRpYWxEYXRhc2V0QmluZGVyIH0gZnJvbSAnLi92aXN1YWxpemVyL3NwYXRpYWwtZGF0YXNldC1iaW5kZXInO1xuaW1wb3J0IHsgVG9vbGJhckNvbXBvbmVudCB9IGZyb20gJy4vdG9vbGJhci90b29sYmFyLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBSZWdpb25FZGl0b3JDb21wb25lbnQgfSBmcm9tICcuL3JlZ2lvbi1lZGl0b3IvcmVnaW9uLWVkaXRvci5jb21wb25lbnQnO1xuaW1wb3J0IHsgQ2hhbm5lbEhpc3RvZ3JhbUNvbXBvbmVudCB9IGZyb20gJy4vY2hhbm5lbC1oaXN0b2dyYW0vY2hhbm5lbC1oaXN0b2dyYW0uY29tcG9uZW50JztcbmltcG9ydCB7IFNwYXRpYWxDb250cm9sc0NvbXBvbmVudCB9IGZyb20gJy4vc3BhdGlhbC1jb250cm9scy9zcGF0aWFsLWNvbnRyb2xzLmNvbXBvbmVudCc7XG5cbi8qKiBQZXItaW5zdGFuY2UgcGxvdC1kaXYgaWQgc291cmNlLiBUaGUgbW91bnQgZWxlbWVudCdzIGlkIG11c3QgYmUgdW5pcXVlIHNvIHR3b1xuICogIGxpdmUgdmlld2VycyAoZS5nLiB0aGUgbWFpbiBkaWFncmFtICsgYSBtb2RhbCBwcmV2aWV3KSBkb24ndCBjb2xsaWRlIG9uIHRoZVxuICogIHNhbWUgRE9NIGlkIOKAlCBgZ2V0RWxlbWVudEJ5SWRgIHdvdWxkIG90aGVyd2lzZSByZXR1cm4gd2hpY2hldmVyIGNhbWUgZmlyc3QuXG4gKiAgU3R5bGluZyBoYW5ncyBvZmYgdGhlIGAudml6LXBsb3RgIGNsYXNzIGluc3RlYWQgb2YgdGhlIGlkLiAqL1xubGV0IHBsb3RJbnN0YW5jZVNlcSA9IDA7XG5cbi8qKlxuICogVGhlIHZpZXdlciAoYDx2aXN1YWxpemVyPmApOiB0aGUgcGxvdCBzdXJmYWNlLCBpdHMgdG9vbGJhciwgdGhlIGxvYWRpbmcgb3ZlcmxheSBhbmRcbiAqIHRoZSBkaWFsb2dzIGl0IG9wZW5zIChDaGFubmVscyAmIEhpc3RvZ3JhbSwgc3BhdGlhbC1vbWljcyBjb250cm9scywgUmVnaW9uIEVkaXRvcixcbiAqIGNvbnRyaWJ1dGVkIG1vZGVzIGFuZCB0b29scykuIEl0IG9yY2hlc3RyYXRlcyByZW5kZXJpbmcgdGhyb3VnaCB0aGUgYFZJU1VBTElaRVJgXG4gKiBjb250cmFjdCBhbmQgcmVhZHMgdGhlIGltYWdlIGZyb20gdGhlIGhvc3QncyBgSU1BR0VfU1RBVEVfUE9SVGAuXG4gKlxuICogT25QdXNoLiBUZW1wbGF0ZSBldmVudHMgcmUtcmVuZGVyIGl0OyBzdGF0ZSB0aGF0IGxhbmRzIGFzeW5jaHJvbm91c2x5IGdvZXMgdGhyb3VnaFxuICoge0BsaW5rIGNoYW5nZWR9IChvciBhIHNpZ25hbDogdGhlIGxvYWRpbmcgb3ZlcmxheSksIGFuZCB0aGUgY29sbGFib3JhdG9ycyB0aGF0IG11c3RcbiAqIHJlbmRlciBzeW5jaHJvbm91c2x5ICh0aGUgcmVuZGVyIHNlc3Npb24ncyBvdmVybGF5LCBhIGNvbnRyaWJ1dGVkIHBhbmVsJ3Mgc2xvdCwgdGhlXG4gKiBjb250ZXh0IG1lbnUgYmVmb3JlIGl0IG9wZW5zKSBzdGlsbCBjYWxsIGBkZXRlY3RDaGFuZ2VzKClgLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICd2aXN1YWxpemVyJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIENvbW1vbk1vZHVsZSxcbiAgICBGb3Jtc01vZHVsZSxcbiAgICBTaGFyZWRNb2R1bGUsXG4gICAgQnV0dG9uTW9kdWxlLFxuICAgIENoZWNrYm94TW9kdWxlLFxuICAgIENvbnRleHRNZW51TW9kdWxlLFxuICAgIERpYWxvZ01vZHVsZSxcbiAgICBEcm9wZG93bk1vZHVsZSxcbiAgICBJbnB1dE51bWJlck1vZHVsZSxcbiAgICBQcm9ncmVzc0Jhck1vZHVsZSxcbiAgICBTbGlkZXJNb2R1bGUsXG4gICAgVG9hc3RNb2R1bGUsXG4gICAgVG9vbHRpcE1vZHVsZSxcbiAgICBGbG9hdGluZ0RyYWdEaXJlY3RpdmUsXG4gICAgVG9vbGJhckNvbXBvbmVudCxcbiAgICBJbnRlbnNpdHlJbnNldENvbXBvbmVudCxcbiAgICBDaGFubmVsSGlzdG9ncmFtQ29tcG9uZW50LFxuICAgIFNwYXRpYWxDb250cm9sc0NvbXBvbmVudCxcbiAgICBSZWdpb25FZGl0b3JDb21wb25lbnQsXG4gIF0sXG4gIHRlbXBsYXRlVXJsOiAnLi92aXN1YWxpemVyLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vdmlzdWFsaXplci5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgVmlzdWFsaXplckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzLCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3ksIENvbnRleHRNZW51QWN0aW9ucyB7XG4gIC8qKiBQZXItaW5zdGFuY2UgdG9hc3Qga2V5LiBNZXNzYWdlU2VydmljZSBpcyBhIGdsb2JhbCBzaW5nbGV0b24sIHNvIHR3byBsaXZlXG4gICAqICBgPHZpc3VhbGl6ZXI+YCBpbnN0YW5jZXMgKG1haW4gdmlld2VyICsgcGlwZWxpbmUtZGlhbG9nIHByZXZpZXcpIHNoYXJpbmdcbiAgICogIG9uZSBrZXkgd291bGQgZWFjaCByZW5kZXIgdGhlIHNhbWUgbWVzc2FnZSDigJQgYSBkdXBsaWNhdGUgdG9hc3QuIEEgdW5pcXVlXG4gICAqICBrZXkgcGVyIGluc3RhbmNlIHNjb3BlcyB0aGUgdG9hc3QgdG8gdGhlIGNvbXBvbmVudCB0aGF0IHJhaXNlZCBpdC4gKi9cbiAgcHJpdmF0ZSBzdGF0aWMgbmV4dFRvYXN0SWQgPSAwO1xuXG4gIC8qKlxuICAgKiBUaGUgbGlicmFyeSBhbHNvIHJhaXNlcyBub3RpY2VzIGZyb20gcGxhY2VzIHRoYXQgaGF2ZSBubyBpbnN0YW5jZSBvZiB0aGVpclxuICAgKiBvd24gdG8ga2V5IGFnYWluc3Q6IHtAbGluayBQbG90bHlTZXJ2aWNlfSBpcyBgcHJvdmlkZWRJbjogJ3Jvb3QnYCwgYW5kIHRoZVxuICAgKiByZWdpb24gZWRpdG9yIGlzIGEgY2hpbGQgZGlhbG9nLiBUaG9zZSB1c2UgdGhlIGZpeGVkXG4gICAqIHtAbGluayBWSVpfVE9BU1RfS0VZfSAvIHtAbGluayBWSVpfQUxFUlRfVE9BU1RfS0VZfSBvdXRsZXRzLlxuICAgKlxuICAgKiBGb3IgdGhlIHNhbWUgcmVhc29uIHRoZSBwZXItaW5zdGFuY2Uga2V5IGFib3ZlIGV4aXN0cywgZXhhY3RseSBPTkUgbW91bnRlZFxuICAgKiB2aXN1YWxpemVyIG1heSByZW5kZXIgdGhvc2Ugc2hhcmVkIG91dGxldHMg4oCUIG90aGVyd2lzZSBhIG1lc3NhZ2UgZnJvbSB0aGVcbiAgICogcm9vdCBzZXJ2aWNlIHdvdWxkIGJlIHNob3duIG9uY2UgcGVyIGxpdmUgdmlld2VyIChqaXQtdWkgcnVucyBhIG1haW4gdmlld1xuICAgKiBhbmQgYSBwaXBlbGluZS1wcmV2aWV3IGF0IHRoZSBzYW1lIHRpbWUpLiBUaGUgZmlyc3QgdG8gaW5pdCBjbGFpbXMgdGhlbSBhbmRcbiAgICogcmVsZWFzZXMgb24gZGVzdHJveSwgc28gdGhlIHN1cnZpdmluZyB2aWV3ZXIgcGlja3MgdGhlbSB1cC5cbiAgICovXG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IGxpdmVJbnN0YW5jZXMgPSBuZXcgU2V0PFZpc3VhbGl6ZXJDb21wb25lbnQ+KCk7XG4gIHByb3RlY3RlZCByZWFkb25seSB2aXpUb2FzdEtleSA9IFZJWl9UT0FTVF9LRVk7XG4gIHByb3RlY3RlZCByZWFkb25seSB2aXpBbGVydFRvYXN0S2V5ID0gVklaX0FMRVJUX1RPQVNUX0tFWTtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhpcyBpbnN0YW5jZSByZW5kZXJzIHRoZSBzaGFyZWQgb3V0bGV0cy4gVGhlIG93bmVyIGlzIHNpbXBseSB0aGVcbiAgICogb2xkZXN0IGxpdmUgdmlzdWFsaXplciDigJQgYSBgU2V0YCBpdGVyYXRlcyBpbiBpbnNlcnRpb24gb3JkZXIsIHNvIHdoZW4gdGhlXG4gICAqIG93bmVyIGlzIGRlc3Ryb3llZCB0aGUgbmV4dCBvbmUgdGFrZXMgb3ZlciBvbiBpdHMgZm9sbG93aW5nIGNoYW5nZS1kZXRlY3Rpb25cbiAgICogcGFzcy4gVHJhY2tpbmcgYSBzZXQgcmF0aGVyIHRoYW4gYSBzaW5nbGUgb3duZXIgbWF0dGVyczogd2l0aCBhIGxvbmVcbiAgICogYG93bmVyYCByZWZlcmVuY2UsIHRlYXJpbmcgZG93biB0aGUgb3duZXIgd2hpbGUgYSBzaWJsaW5nIHdhcyBhbHJlYWR5XG4gICAqIGluaXRpYWxpc2VkIHdvdWxkIGxlYXZlIG5vYm9keSByZW5kZXJpbmcgdGhlIG91dGxldHMsIGFuZCBldmVyeSBub3RpY2UgZnJvbVxuICAgKiB0aGUgcm9vdCBzZXJ2aWNlIHdvdWxkIHNpbGVudGx5IHZhbmlzaCBhZ2Fpbi5cbiAgICovXG4gIHByb3RlY3RlZCBnZXQgb3duc1NoYXJlZFRvYXN0cygpOiBib29sZWFuIHtcbiAgICBjb25zdCBvbGRlc3QgPSBWaXN1YWxpemVyQ29tcG9uZW50LmxpdmVJbnN0YW5jZXMudmFsdWVzKCkubmV4dCgpO1xuICAgIHJldHVybiAhb2xkZXN0LmRvbmUgJiYgb2xkZXN0LnZhbHVlID09PSB0aGlzO1xuICB9XG5cbiAgLyoqIEVhY2ggbmV3IGltYWdlOiB3aGV0aGVyIGl0IGlzIGEgei1zdGFjay4gKi9cbiAgQE91dHB1dCgpXG4gIGlzU3RhY2tFdmVudCA9IG5ldyBFdmVudEVtaXR0ZXIoZmFsc2UpO1xuICAvKiogRWFjaCBuZXcgaW1hZ2U6IHdoZXRoZXIgaXQgaXMgc2luZ2xlLWNoYW5uZWwgKGdyYXlzY2FsZSkuICovXG4gIEBPdXRwdXQoKVxuICBpc0dyYXlzY2FsZUV2ZW50ID0gbmV3IEV2ZW50RW1pdHRlcihmYWxzZSk7XG5cbiAgLyoqIFdoaWNoIHRvb2xiYXIgZ3JvdXBzIHRvIHNob3cuIEEgcGFydGlhbCBvdmVycmlkZSBtZXJnZXMgb3ZlciB0aGUgZnVsbCBzZXQsXG4gICAqICBzbyBgeyBzcGVjaWFsVG9vbHM6IGZhbHNlIH1gIGhpZGVzIG9ubHkgdGhhdCBncm91cC4gRGVmYXVsdHMgdG8gZXZlcnl0aGluZy4gKi9cbiAgQElucHV0KClcbiAgc2V0IHRvb2xiYXJUb29scyh2OiBUb29sYmFyVG9vbFZpc2liaWxpdHkgfCB1bmRlZmluZWQpIHtcbiAgICB0aGlzLl90b29sYmFyVG9vbHMgPSB7IC4uLkFMTF9UT09MQkFSX1RPT0xTLCAuLi4odiA/PyB7fSkgfTtcbiAgfVxuXG4gIGdldCB0b29sYmFyVG9vbHMoKTogUmVxdWlyZWQ8VG9vbGJhclRvb2xWaXNpYmlsaXR5PiB7XG4gICAgcmV0dXJuIHRoaXMuX3Rvb2xiYXJUb29scztcbiAgfVxuICBwcml2YXRlIF90b29sYmFyVG9vbHM6IFJlcXVpcmVkPFRvb2xiYXJUb29sVmlzaWJpbGl0eT4gPSBBTExfVE9PTEJBUl9UT09MUztcblxuICAvKiogRXhwb3NlIHRlc3Qtb25seSBwbG90IG1vZGVzIGluIHRoZSBzZWxlY3RvciAoZS5nLiB0aGUgbmFwYXJpIGBJbWFnZWBcbiAgICogIG1vZGUsIG90aGVyd2lzZSBoaWRkZW4pLiBUaGUgaG9zdCBhcHAgc2V0cyB0aGlzIGZyb20gYSBgP3Rlc3Q9MWAgVVJMXG4gICAqICB0b2dnbGUuIE9mZiBieSBkZWZhdWx0LCBzbyBwcm9kdWN0aW9uIHVzZXJzIG5ldmVyIHNlZSB0aGVtLiAqL1xuICBASW5wdXQoKVxuICB0ZXN0TW9kZSA9IGZhbHNlO1xuXG4gIC8qKiBJbWFnZS1zbW9vdGhpbmcgc3RhdGUgZm9yIHRoZSB0b29sYmFyJ3MgU21vb3RoZW4gdG9nZ2xlIChPU0Qgb25seSkuXG4gICAqICBEZWZhdWx0cyB0byBgZmFsc2VgIHNvIE9TRCBzaG93cyByYXcgcGl4ZWxzIChuZWFyZXN0LW5laWdoYm91cikuICovXG4gIHByb3RlY3RlZCBpbWFnZVNtb290aGluZ0VuYWJsZWQgPSBmYWxzZTtcbiAgZmlsZU5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSBsb2FkZWRGaWxlTmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBwcm90ZWN0ZWQgaW1hZ2VJbmZvOiBJSW1hZ2VJbmZvIHwgdW5kZWZpbmVkO1xuXG4gIC8vIOKUgOKUgCBUaGUgbG9hZGluZyBvdmVybGF5LCBhcyBzaWduYWxzOiBpdCBpcyBmZWQgYnkgdGhlIGhvc3QncyBhbmQgdGhlIGJhY2tlbmRzJyBzdHJlYW1zXG4gIC8vIGFuZCBieSB0aGUgcmVuZGVyIHNlc3Npb24sIGFsbCBvZiB3aGljaCBsYW5kIG91dHNpZGUgdGhpcyB2aWV3J3MgdGVtcGxhdGUgZXZlbnRzLiDilIDilIBcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGxvYWRpbmdNZXNzYWdlID0gc2lnbmFsKCdMb2FkaW5nIGltYWdlLi4uJyk7XG4gIC8qKiBUaGUgaG9zdCBpcyBsb2FkaW5nIGF0IHpvb20gKHRoZSBvdmVybGF5IG1lc3NhZ2UgbW92ZXMgb3V0IG9mIHRoZSB3YXkpLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgem9vbSA9IHNpZ25hbChmYWxzZSk7XG4gIHByb3RlY3RlZCByZWFkb25seSBzdGFja0xvYWRpbmcgPSBzaWduYWwoZmFsc2UpO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgaW1nTG9hZGluZyA9IHNpZ25hbChmYWxzZSk7XG4gIHByb3RlY3RlZCByZWFkb25seSBsb2FkaW5nUGVyY2VudGFnZSA9IHNpZ25hbCgwKTtcbiAgLy8gTm9uLW51bGwgKDAuLjEwMCkgd2hpbGUgaml0LXNlcnZpY2UgaXMgY29weWluZyB0aGUgc291cmNlIGZpbGUgaW50byB0aGVcbiAgLy8gbG9jYWwgY2FjaGUgUFZDIG9uIGZpcnN0IGNsaWNrLiBXaGVuIHNldCwgdGhlIGxvYWRpbmcgb3ZlcmxheSBzd2FwcyB0aGVcbiAgLy8gc3Bpbm5lciBmb3IgYSBkZXRlcm1pbmF0ZSBwcm9ncmVzcyBiYXIgd2l0aCBhIFwiQ2FjaGluZyBpbWFnZVwiIG1lc3NhZ2UuXG4gIHByb3RlY3RlZCByZWFkb25seSBjYWNoZVByb2dyZXNzID0gc2lnbmFsPG51bWJlciB8IG51bGw+KG51bGwpO1xuICAvKiogQSBqaXQtc2VydmljZSBjYWNoZSBjb3B5IGlzIGFjdGl2ZWx5IGluIHByb2dyZXNzIChkZXRlcm1pbmF0ZSAwLi45OSkuIERyaXZlcyB0aGUgY2FjaGVcbiAgICogIHByb2dyZXNzIGJhciBpbmRlcGVuZGVudGx5IG9mIGBpbWdMb2FkaW5nYCBzbyB0aGUgYmFyIGNhbid0IGJlIGRyb3BwZWQg4oCUIG9yIG1hc2tlZCBieSB0aGVcbiAgICogIFwiU2hhcnBlbmluZyBwcmV2aWV3Li4uXCIgb3ZlcmxheSDigJQgbWlkLWRvd25sb2FkIGlmIHRoZSBsb2FkaW5nIGZsYWcgZmxpcHMgZWFybHkuICovXG4gIHByb3RlY3RlZCByZWFkb25seSBpc0NhY2hpbmcgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgY29uc3QgcCA9IHRoaXMuY2FjaGVQcm9ncmVzcygpO1xuICAgIHJldHVybiBwICE9PSBudWxsICYmIHAgPCAxMDA7XG4gIH0pO1xuXG4gIC8qKiBFbWl0cyBvbmNlIHdoZW4gdGhpcyB2aWV3IGlzIGRlc3Ryb3llZDsgdGhlIGNvbGxhYm9yYXRvcnMgZW5kIHRoZWlyIHN1YnNjcmlwdGlvbnMgb24gaXQuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgZGVzdHJveWVkJCA9IG5ldyBPYnNlcnZhYmxlPHZvaWQ+KChzdWJzY3JpYmVyKSA9PlxuICAgIHRoaXMuZGVzdHJveVJlZi5vbkRlc3Ryb3koKCkgPT4ge1xuICAgICAgc3Vic2NyaWJlci5uZXh0KCk7XG4gICAgICBzdWJzY3JpYmVyLmNvbXBsZXRlKCk7XG4gICAgfSksXG4gICk7XG4gIC8qKiBIb3N0IGltYWdlLWluZm8gb2JqZWN0cyB3aG9zZSBvbmUtc2hvdCBgaW5pdGlhbFpJbmRleGAgd2FzIGFscmVhZHkgYXBwbGllZC4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBjb25zdW1lZFNsaWNlSGludHMgPSBuZXcgV2Vha1NldDxJSW1hZ2VJbmZvPigpO1xuICBwcm90ZWN0ZWQgekluZGV4ID0gMDtcbiAgcHJvdGVjdGVkIG1heEluZGV4ID0gMDtcblxuICBAVmlld0NoaWxkKCdjbScpIGNvbnRleHRNZW51ITogQ29udGV4dE1lbnU7XG4gIHByb3RlY3RlZCBjb250ZXh0TWVudUl0ZW1zOiBNZW51SXRlbVtdID0gW107XG5cbiAgLyoqIFRoZSBhcm1lZCB0b29sIG1vZGUgYW5kIHRoZSB0b29sYmFyJ3MgdG9vbCBzZXR0aW5ncy4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHRvb2xzID0gbmV3IFRvb2xNb2RlcyhcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLFxuICAgIHRoaXMuc2Vzc2lvbixcbiAgICB0aGlzLm5nWm9uZSxcbiAgICAobW9kZSkgPT4ge1xuICAgICAgaWYgKG1vZGUgIT09ICdzYW1Qb2ludCcpIHRoaXMuc2VnbWVudGF0aW9uLmhpZGUoKTtcbiAgICB9LCAvLyBsZWF2aW5nIHBvaW50IG1vZGUgZHJvcHMgaXRzIHRvYXN0XG4gICAgKCkgPT4gdGhpcy5jaGFuZ2VkKCksXG4gICk7XG4gIHByb3RlY3RlZCBnZXQgYWN0aXZlRHJhZ01vZGUoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMudG9vbHMuYWN0aXZlO1xuICB9XG4gIHByb3RlY3RlZCBzZXQgYWN0aXZlRHJhZ01vZGUobW9kZTogc3RyaW5nIHwgbnVsbCkge1xuICAgIHRoaXMudG9vbHMuYWN0aXZlID0gbW9kZTtcbiAgfVxuICBwcm90ZWN0ZWQgZ2V0IHdhbmRTZW5zaXRpdml0eSgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnRvb2xzLndhbmRTZW5zaXRpdml0eTtcbiAgfVxuICBwcm90ZWN0ZWQgc2V0IHdhbmRTZW5zaXRpdml0eSh2OiBudW1iZXIpIHtcbiAgICB0aGlzLnRvb2xzLndhbmRTZW5zaXRpdml0eSA9IHY7XG4gIH1cbiAgcHJvdGVjdGVkIGdldCBicnVzaFNpemUoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy50b29scy5icnVzaFNpemU7XG4gIH1cbiAgcHJvdGVjdGVkIGdldCB2ZXJ0ZXhFcmFzZXJSYWRpdXMoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy50b29scy52ZXJ0ZXhFcmFzZXJSYWRpdXM7XG4gIH1cbiAgcHJvdGVjdGVkIHNldCB2ZXJ0ZXhFcmFzZXJSYWRpdXModjogbnVtYmVyKSB7XG4gICAgdGhpcy50b29scy52ZXJ0ZXhFcmFzZXJSYWRpdXMgPSB2O1xuICB9XG5cbiAgLyoqIFJlZ2lvbiBzZXQtb3BlcmF0aW9ucyBvbiB0aGUgc2VsZWN0aW9uLCBhbmQgdGhlIHN0b3JlIG1pcnJvcnMgdGhleSByZWFkIChqaXQtdWkjODUpLiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcmVnaW9uQWN0aW9ucyA9IG5ldyBSZWdpb25BY3Rpb25zKFxuICAgIHRoaXMucGxvdFNlcnZpY2UsXG4gICAgdGhpcy5yZWdpb25PcHMsXG4gICAgKCkgPT4gdGhpcy5pbWFnZUluZm8/LnRydWVJbWFnZVNpemUsXG4gICAgKG0pID0+IHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHsga2V5OiB0aGlzLnJlc3VsdFRvYXN0S2V5LCAuLi5tIH0pLFxuICApO1xuICAvKiogQ3VzdG9tLXRocmVzaG9sZCBTaW1wbGlmeSBkaWFsb2cgdmlzaWJpbGl0eSAoc2VlIHtAbGluayBSZWdpb25BY3Rpb25zfSkuICovXG4gIHByb3RlY3RlZCBnZXQgZGlzcGxheVNpbXBsaWZ5RGlhbG9nKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnJlZ2lvbkFjdGlvbnMuZGlzcGxheVNpbXBsaWZ5RGlhbG9nO1xuICB9XG4gIHByb3RlY3RlZCBzZXQgZGlzcGxheVNpbXBsaWZ5RGlhbG9nKHY6IGJvb2xlYW4pIHtcbiAgICB0aGlzLnJlZ2lvbkFjdGlvbnMuZGlzcGxheVNpbXBsaWZ5RGlhbG9nID0gdjtcbiAgfVxuXG4gIC8qKiBTQU0gbW9kZWwgcGlja2VyIG9wdGlvbnMgKyBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzkwIFAxKS4gT25seSBtb2RlbHNcbiAgICogIHdpdGggYSBob3N0ZWQgT05OWCBwYWlyIChjb25maWd1cmVkIHZpYSBzZXRTYW1Nb2RlbFVybHMgYXQgYXBwIGluaXQpIGFyZVxuICAgKiAgb2ZmZXJlZCwgc28gdGhlIHBpY2tlciBjYW4ndCBzZWxlY3QgYSBtb2RlbCB0aGF0IGNhbid0IHJ1bi4gKi9cbiAgcHJvdGVjdGVkIHNhbU1vZGVscyA9IFNBTV9NT0RFTFMuZmlsdGVyKGlzU2FtTW9kZWxSZWFkeSkubWFwKChtKSA9PiAoeyBpZDogbS5pZCwgbGFiZWw6IG0ubGFiZWwgfSkpO1xuICBwcm90ZWN0ZWQgc2FtTW9kZWxJZCA9IGdldERlZmF1bHRTYW1Nb2RlbElkKCk7XG5cbiAgLyoqXG4gICAqIE5vLXByb21wdCB0b29scyByZWdpc3RlcmVkIHRocm91Z2gge0BsaW5rIFRPT0xCQVJfVE9PTFN9LCBmaWx0ZXJlZCB0byB0aG9zZVxuICAgKiB3aXRoIGNvbmZpZ3VyZWQgbW9kZWxzIGFuZCBzb3J0ZWQuIFRoaXMgbGlicmFyeSByZWdpc3RlcnMgbm9uZSDigJQgYSBob3N0XG4gICAqIHN1cHBsaWVzIHRoZW0g4oCUIHNvIGFuIG9wZW4gYnVpbGQgaGFzIGFuIGVtcHR5IGFycmF5IGhlcmUgYW5kIHRoZSB0b29sYmFyXG4gICAqIGFuZCBoZWxwIGRpYWxvZyBzaW1wbHkgb21pdCB0aGF0IGdyb3VwLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnRyaWJ1dGVkVG9vbHM6IFRvb2xiYXJUb29sQ29udHJpYnV0aW9uW10gPSBbXTtcbiAgLyoqIENoZWNrcG9pbnRzIGFuZCBwYXJhbWV0ZXIgdmFsdWVzIG9mIHtAbGluayBjb250cmlidXRlZFRvb2xzfSwgYW5kIHRoZWlyIGRpYWxvZy4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHRvb2xQYXJhbXM6IFRvb2xQYXJhbXNNb2RlbDtcbiAgLyoqIENvbnRyaWJ1dGVkIHBsb3QgbW9kZXMgYW5kIGRpYWxvZyB0b29scywgYW5kIHRoZWlyIGxpdmUgc2Vzc2lvbnMuICovXG4gIHJlYWRvbmx5IGNvbnRyaWJ1dGlvbnM6IENvbnRyaWJ1dGlvbkhvc3Q7XG4gIC8qKiBEaWFsb2cgdG9vbHMgcmVnaXN0ZXJlZCB0aHJvdWdoIHtAbGluayBUT09MQkFSX1RPT0xTfSAoYGtpbmQ6ICdkaWFsb2cnYCkuICovXG4gIHByb3RlY3RlZCBnZXQgZGlhbG9nVG9vbHMoKTogVG9vbGJhckRpYWxvZ1Rvb2xDb250cmlidXRpb25bXSB7XG4gICAgcmV0dXJuIHRoaXMuY29udHJpYnV0aW9ucy5kaWFsb2dUb29scztcbiAgfVxuICAvKiogVGhlIGRpYWxvZyB0b29sIHRoZSB1c2VyIGhhcyBvcGVuIChpdHMgc2Vzc2lvbiBtYXkgYmUgYmV0d2VlbiByZW5kZXJzKS4gKi9cbiAgcHJvdGVjdGVkIGdldCBvcGVuRGlhbG9nVG9vbElkKCk6IHN0cmluZyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNvbnRyaWJ1dGlvbnMub3BlbkRpYWxvZ1Rvb2xJZDtcbiAgfVxuICAvKiogVGhlIG9wZW4gZGlhbG9nIHRvb2wncyBsaXZlIGRpYWxvZywgYXMgdGhlIHRlbXBsYXRlIHJlbmRlcnMgaXQuICovXG4gIHByb3RlY3RlZCBnZXQgdG9vbERpYWxvZygpOiBUb29sRGlhbG9nVmlldyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNvbnRyaWJ1dGlvbnMudG9vbERpYWxvZztcbiAgfVxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc2FtVG9hc3RLZXkgPSBgc2FtLSR7VmlzdWFsaXplckNvbXBvbmVudC5uZXh0VG9hc3RJZCsrfWA7XG4gIC8qKlxuICAgKiBPdXRsZXQgZm9yIHRoZSBsaWJyYXJ5J3Mgb3duIHJlc3VsdC9lcnJvciBub3RpY2VzLCBrZXllZCB0byB0aGlzIGluc3RhbmNlIHNvIHRoZXlcbiAgICogZG8gbm90IGRlcGVuZCBvbiB0aGUgaG9zdCBtb3VudGluZyBhIGtleWxlc3MgYDxwLXRvYXN0PmAgKG1vc3QgaG9zdHMgZG9uJ3QsIGFuZCBhXG4gICAqIGZhaWxlZCBzZWdtZW50YXRpb24gdGhlbiBsb29rZWQgbGlrZSBhIGRlYWQgYnV0dG9uKS4gRGVsaWJlcmF0ZWx5IE5PVCB0aGUgc3RpY2t5XG4gICAqIHRvYXN0J3Mga2V5OiB0aGF0IGlzIGNsZWFyZWQgd2hlbiBhIHJ1biBzZXR0bGVzLCB3aGljaCB3b3VsZCB3aXBlIHRoZSByZXN1bHQuXG4gICAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcmVzdWx0VG9hc3RLZXkgPSBgJHt0aGlzLnNhbVRvYXN0S2V5fS1yZXN1bHRgO1xuICAvKiogU2VnbWVudGF0aW9uIHJ1bnMgYW5kIHRoZWlyIHN0aWNreSBwcm9ncmVzcyB0b2FzdC4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHNlZ21lbnRhdGlvbiA9IG5ldyBTZWdtZW50YXRpb25SdW5uZXIoXG4gICAgdGhpcy5tZXNzYWdlU2VydmljZSxcbiAgICB0aGlzLnNhbVRvYXN0S2V5LFxuICAgIHRoaXMucmVzdWx0VG9hc3RLZXksXG4gICAgKCkgPT4gdGhpcy5jaGFuZ2VkKCksXG4gICk7XG5cbiAgLyoqIENoYW5uZWxzICYgSGlzdG9ncmFtIGRpYWxvZyB2aXNpYmlsaXR5IChvcGVuZWQgZnJvbSB0aGUgdG9vbGJhcikuICovXG4gIHByb3RlY3RlZCBzaG93Q2hhbm5lbEhpc3RvZ3JhbSA9IGZhbHNlO1xuICAvKiogU3BhdGlhbC1vbWljcyBjb250cm9scyBkaWFsb2cgdmlzaWJpbGl0eSAodG9vbGJhciBidXR0b24pLiAqL1xuICBwcm90ZWN0ZWQgc2hvd1NwYXRpYWxDb250cm9scyA9IGZhbHNlO1xuICAvKiogUmVnaW9uIEVkaXRvciBkaWFsb2cgdmlzaWJpbGl0eSAob3BlbmVkIGZyb20gdGhlIHRvb2xiYXIpLiAqL1xuICBwcm90ZWN0ZWQgc2hvd1JlZ2lvbkVkaXRvciA9IGZhbHNlO1xuICAvKiogUmVnaW9uIEVkaXRvciBkaWFsb2cgd2lkdGguIE9uIG9wZW4gaXQgaXMgc2V0IHRvIHRoZSBjb25maWd1cmVkIGhvc3RcbiAgICogIGVsZW1lbnQncyBjdXJyZW50IHdpZHRoLCBvciDigJQgd2hlbiBubyBzZWxlY3RvciBpcyBjb25maWd1cmVkIC8gdGhlIGVsZW1lbnRcbiAgICogIGlzbid0IGZvdW5kIOKAlCB0byBhIHF1YXJ0ZXIgb2YgdGhlIHBhZ2UgKHNlZSBvcGVuUmVnaW9uRWRpdG9yKS4gKi9cbiAgcHJvdGVjdGVkIHJlZ2lvbkVkaXRvcldpZHRoID0gJzI1dncnO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgcGxvdERpdk5hbWUgPSBgdml6LXBsb3QtJHtwbG90SW5zdGFuY2VTZXErK31gO1xuICAvKiogVGhlIHJlbmRlciBwaXBlbGluZTogdGhlIG5ld2VzdCBpbWFnZSAob3IgaW1hZ2UtbGVzcyBkYXRhc2V0KSBhbHdheXMgd2lucy4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHJlbmRlcjogSW1hZ2VSZW5kZXJTZXNzaW9uID0gbmV3IEltYWdlUmVuZGVyU2Vzc2lvbih7XG4gICAgcGxvdERpdk5hbWU6IHRoaXMucGxvdERpdk5hbWUsXG4gICAgdmlzdWFsaXplcjogdGhpcy5wbG90U2VydmljZSxcbiAgICB6SW5kZXg6ICgpID0+IHRoaXMuekluZGV4LFxuICAgIHBsb3RUeXBlOiAoKSA9PiB0aGlzLnBsb3RUeXBlLFxuICAgIGlzSGVhdG1hcDogKCkgPT4gdGhpcy5pc0hlYXRtYXAsXG4gICAgaXNDYWNoaW5nOiAoKSA9PiB0aGlzLmlzQ2FjaGluZygpLFxuICAgIGJlZm9yZVJlc2V0OiAoKSA9PiB0aGlzLmNvbnRyaWJ1dGlvbnMuZW5kU2Vzc2lvbnMoKSxcbiAgICBwcmVwYXJlOiAoaW5mbykgPT4gdGhpcy5sYXlPdXRTbGljZXMoaW5mbyksXG4gICAgcmVsZWFzZU92ZXJsYXk6IChpbmZvKSA9PiB7XG4gICAgICBpZiAoaW5mby5pc1N0YWNrICYmIGluZm8uc2hvd1N0YWNrKSB0aGlzLnN0YWNrTG9hZGluZy5zZXQoZmFsc2UpO1xuICAgICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgIH0sXG4gICAgbGFuZGVkOiAoaW5mbykgPT4ge1xuICAgICAgYXBwbHlJbWFnZVJvaXMoaW5mbywgdGhpcy5wbG90U2VydmljZSwgdGhpcy56SW5kZXgpO1xuICAgICAgdGhpcy5jb250cmlidXRpb25zLmFjdGl2YXRlKCk7XG4gICAgfSxcbiAgICBzZXRJbWFnZUxvYWRpbmc6IChsb2FkaW5nKSA9PiB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyhsb2FkaW5nKSxcbiAgICBhbGVydDogKHNldmVyaXR5LCBzdW1tYXJ5LCBkZXRhaWwpID0+XG4gICAgICB0aGlzLm1lc3NhZ2VTZXJ2aWNlLmFkZCh7IGtleTogdGhpcy52aXpBbGVydFRvYXN0S2V5LCBzZXZlcml0eSwgc3VtbWFyeSwgZGV0YWlsIH0pLFxuICAgIGRldGVjdENoYW5nZXM6ICgpID0+IHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKSxcbiAgfSk7XG4gIHBsb3RUeXBlID0gUGxvdFR5cGUuSU1BR0U7XG4gIHByb3RlY3RlZCBpc0hlYXRtYXAgPSB0cnVlO1xuICBwcm90ZWN0ZWQgYWN0aXZlU3VyZmFjZTNkTW9kZSA9ICd0dXJudGFibGUnO1xuICAvKiogV2hldGhlciB0aGUgbmFwYXJpIDNEIGNvb3JkaW5hdGUtYXhlcyAvIHNjYWxlIGdpem1vIGlzIHNob3duICh2b2x1bWUvaXNvc3VyZmFjZSkuICovXG4gIHByb3RlY3RlZCBheGVzVmlzaWJsZSA9IHRydWU7XG4gIHByb3RlY3RlZCB3aXJlZnJhbWVBY3RpdmUgPSBmYWxzZTtcbiAgLyoqIG5hcGFyaSAzRCBkZWNpbWF0ZSBmYWN0b3IgKDEgPSBGdWxsIOKApiA4ID0g4oWbOyBkZWZhdWx0IMK9KTsgY2hhbmdpbmcgaXQgcmUtcGxvdHMuICovXG4gIHByb3RlY3RlZCByZXNvbHV0aW9uU2NhbGUgPSBOQVBBUklfREVGQVVMVF9ERUNJTUFURTtcblxuICAvKiogVGhlIHNlbGVjdG9yJ3MgZW50cmllczogdGhlIGJ1aWx0LWluIHBsb3QgdHlwZXMgdGhlIGFjdGl2ZSBiYWNrZW5kXG4gICAqICBhZHZlcnRpc2VzICgzRCBnYXRlZCBieSBjYXBhYmlsaXR5KSwgdGhlbiBhbnkgY29udHJpYnV0ZWQgbW9kZXNcbiAgICogICh7QGxpbmsgUExPVF9UWVBFX0NPTlRSSUJVVElPTlN9KS4gKi9cbiAgcHJvdGVjdGVkIHBsb3RUeXBlTWVudTogUGxvdFR5cGVPcHRpb25bXSA9IFtdO1xuICAvKipcbiAgICogVGhlIGJ1aWx0LWluIHBsb3QgdHlwZXMgb24gb2ZmZXIg4oCUIHtAbGluayBwbG90VHlwZU1lbnV9IHdpdGhvdXQgY29udHJpYnV0ZWRcbiAgICogbW9kZXMuIEtlcHQgd2l0aCBpdHMgb3JpZ2luYWwgdHlwZSBzbyBleGlzdGluZyByZWFkZXJzIGFyZSB1bmFmZmVjdGVkLlxuICAgKi9cbiAgcHJvdGVjdGVkIHBsb3RUeXBlT3B0aW9uczogUGxvdFR5cGVEZXNjcmlwdG9yW10gPSBbXTtcbiAgLyoqXG4gICAqIFdoYXQgdGhlIHNlbGVjdG9yIHNob3dzOiBhIGJ1aWx0LWluIHR5cGUgb3IgYSBjb250cmlidXRlZCBtb2RlJ3MgaWQuIEV2ZXJ5XG4gICAqIHJlbmRlcmluZyBhbmQgdG9vbCBkZWNpc2lvbiBnb2VzIHRocm91Z2gge0BsaW5rIGJhc2VQbG90VHlwZX0gaW5zdGVhZCwgc28gYVxuICAgKiBjb250cmlidXRlZCBtb2RlIGJlaGF2ZXMgZXhhY3RseSBsaWtlIHRoZSBidWlsdC1pbiB0eXBlIGl0IHJpZGVzIG9uLlxuICAgKi9cbiAgcHJvdGVjdGVkIHNlbGVjdGVkUGxvdFR5cGVJZDogUGxvdFR5cGVJZCA9IFBsb3RUeXBlLklNQUdFO1xuXG4gIC8qKlxuICAgKiBUaGUgYnVpbHQtaW4gcGxvdCB0eXBlIGJlaW5nIHJlbmRlcmVkLiBXaXRoIG5vIGNvbnRyaWJ1dGVkIG1vZGUgYWN0aXZlIHRoaXNcbiAgICogaXMgdGhlIHNlbGVjdGlvbiBpdHNlbGY7IGR1cmluZyBvbmUgaXQgaXMgdGhlIG1vZGUncyBgYmFzZVR5cGVgLiBLZXB0IHdpdGggaXRzXG4gICAqIG9yaWdpbmFsIGBQbG90VHlwZWAgdHlwZSBmb3IgZXhpc3RpbmcgcmVhZGVycyDigJQgdXNlIHtAbGluayBzZWxlY3RlZFBsb3RUeXBlSWR9XG4gICAqIGZvciB0aGUgc2VsZWN0b3IncyBpZC4gQXNzaWduaW5nIHNlbGVjdHMgdGhhdCBidWlsdC1pbiB0eXBlLlxuICAgKi9cbiAgcHJvdGVjdGVkIGdldCBzZWxlY3RlZFBsb3RUeXBlKCk6IFBsb3RUeXBlIHtcbiAgICByZXR1cm4gdGhpcy5iYXNlUGxvdFR5cGU7XG4gIH1cbiAgcHJvdGVjdGVkIHNldCBzZWxlY3RlZFBsb3RUeXBlKHR5cGU6IFBsb3RUeXBlKSB7XG4gICAgdGhpcy5zZWxlY3RlZFBsb3RUeXBlSWQgPSB0eXBlO1xuICB9XG5cbiAgLyoqIENvbnRyaWJ1dGVkIHBsb3QgbW9kZXMgYW5kIHRoZWlyIHNpbmdsZSBsaXZlIHNlc3Npb24uICovXG4gIGdldCBwbG90TW9kZXMoKTogUGxvdE1vZGVDb250cm9sbGVyIHtcbiAgICByZXR1cm4gdGhpcy5jb250cmlidXRpb25zLnBsb3RNb2RlcztcbiAgfVxuICAvKiogVGhlIGxpdmUgY29udHJpYnV0ZWQgbW9kZSdzIHNpZGUgcGFuZWw7IG51bGwgd2hpbGUgbm8gY29udHJpYnV0ZWQgc2Vzc2lvbiBpcyBsaXZlLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IHBsb3RNb2RlUGFuZWwoKTogUGxvdE1vZGVQYW5lbFZpZXcgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5jb250cmlidXRpb25zLnBsb3RNb2RlUGFuZWw7XG4gIH1cblxuICAvKiogV2hlcmUgYSBgbW91bnRgIHBhbmVsJ3MgaG9zdCBlbGVtZW50IGlzIGF0dGFjaGVkIG9uY2UgdGhlIGRpYWxvZyByZW5kZXJzLiAqL1xuICBAVmlld0NoaWxkKCdwbG90TW9kZVBhbmVsU2xvdCcpXG4gIHNldCBwbG90TW9kZVBhbmVsU2xvdChyZWY6IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+IHwgdW5kZWZpbmVkKSB7XG4gICAgdGhpcy5jb250cmlidXRpb25zLmF0dGFjaFBhbmVsU2xvdChyZWY/Lm5hdGl2ZUVsZW1lbnQpO1xuICB9XG5cbiAgLyoqIFRoZSBidWlsdC1pbiB0eXBlIGFjdHVhbGx5IHJlbmRlcmVkIGZvciB7QGxpbmsgc2VsZWN0ZWRQbG90VHlwZUlkfTogaXRzZWxmXG4gICAqICBmb3IgYSBidWlsdC1pbiB0eXBlLCBgYmFzZVR5cGVgIGZvciBhIGNvbnRyaWJ1dGVkIG1vZGUuICovXG4gIHByb3RlY3RlZCBnZXQgYmFzZVBsb3RUeXBlKCk6IFBsb3RUeXBlIHtcbiAgICByZXR1cm4gdGhpcy5wbG90TW9kZXMuYmFzZVR5cGVPZih0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCk7XG4gIH1cblxuICAvKiogRGl2IHRoZSBmbG9hdGluZyBpbnRlbnNpdHktcHJvZmlsZSBpbnNldCBpcyBjaGFydGVkIGludG8uIFBlciBpbnN0YW5jZSwgbGlrZVxuICAgKiAge0BsaW5rIHBsb3REaXZOYW1lfTogdGhlIGJhY2tlbmQgcmVzb2x2ZXMgaXQgYnkgaWQsIHNvIGEgZml4ZWQgaWQgbWFkZSB0d28gbGl2ZVxuICAgKiAgdmlld2VycyBkcmF3IGJvdGggaW5zZXRzIGludG8gd2hpY2hldmVyIGRpdiBjYW1lIGZpcnN0IGluIHRoZSBkb2N1bWVudC4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGludGVuc2l0eUluc2V0RGl2ID0gYCR7dGhpcy5wbG90RGl2TmFtZX0taW5zZXRgO1xuXG4gIC8qKiBUb29sYmFyIGRvY2tpbmc6IGRvY2tlZCBhY3Jvc3MgdGhlIHRvcCBieSBkZWZhdWx0OyBkcmFnZ2luZyBpdHMgaGFuZGxlXG4gICAqICBkZXRhY2hlcyBpdCBpbnRvIGEgZmxvYXRpbmcsIG1vdmFibGUgd2luZG93IChmcmVlcyB0aGUgdG9wIHJvdyBmb3IgdGhlXG4gICAqICB2aXN1YWxpemF0aW9uKS4gKi9cbiAgcHJvdGVjdGVkIHRvb2xiYXJGbG9hdGluZyA9IGZhbHNlO1xuICBwcm90ZWN0ZWQgdG9vbGJhclBvcyA9IHsgeDogOCwgeTogOCB9O1xuICAvKiogV2hlcmUgYSB0b29sYmFyLWhhbmRsZSBkcmFnIHN0YXJ0cyBmcm9tOiBncmFiYmluZyB0aGUgZG9ja2VkIHRvb2xiYXIgZmxvYXRzIGl0LiAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgdG9vbGJhckRyYWdPcmlnaW4gPSAoKTogRmxvYXRpbmdQb3MgPT4ge1xuICAgIGlmICghdGhpcy50b29sYmFyRmxvYXRpbmcpIHtcbiAgICAgIHRoaXMudG9vbGJhckZsb2F0aW5nID0gdHJ1ZTtcbiAgICAgIHRoaXMudG9vbGJhclBvcyA9IHsgeDogOCwgeTogOCB9O1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy50b29sYmFyUG9zO1xuICB9O1xuICAvKiogVGhlIGZsb2F0aW5nIGludGVuc2l0eS1wcm9maWxlIGluc2V0IChhbHdheXMgbW91bnRlZDsgc2hvd3MgaXRzZWxmIHdoaWxlIGxpbmVzIGV4aXN0KS4gKi9cbiAgQFZpZXdDaGlsZChJbnRlbnNpdHlJbnNldENvbXBvbmVudCwgeyBzdGF0aWM6IHRydWUgfSkgaW5zZXQ/OiBJbnRlbnNpdHlJbnNldENvbXBvbmVudDtcblxuICAvKiogVHJ1ZSB3aGlsZSB0aGUgM0Qgc3BhdGlhbCBjbG91ZCBpcyB0aGUgYWN0aXZlIG1vZGUuIFRoZSBjb250cm9scyBkcm9wIHRoZSBST0lcbiAgICogIHNlbGVjdGlvbiB0aGVyZTogcmVnaW9uIHRvb2xzIGFyZSBzY3JlZW4tc3BhY2UsIGFuZCBhZ2FpbnN0IGFuIG9yYml0aW5nXG4gICAqICBjYW1lcmEgYSBkcmF3biByZWN0YW5nbGUgaGFzIG5vIGZpeGVkIG1lYW5pbmcgaW4gdGhlIGRhdGEuICovXG4gIHByb3RlY3RlZCBnZXQgaXNTcGF0aWFsM2RNb2RlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc1NwYXRpYWxPbWljczNkKHRoaXMuYmFzZVBsb3RUeXBlKTtcbiAgfVxuXG4gIC8qKiBUcnVlIGZvciB0aGUgSW1hZ2UgcGxvdCB0eXBlLCB3aGljaCByZW5kZXJzIGFzIGEgbmF0aXZlbHkgcGFuL3pvb20tYWJsZVxuICAgKiAgcmFzdGVyIOKAlCBzbyB0aGUgYmFja2VuZC1hZ25vc3RpYyB6b29tL3BhbiB0b29sYmFyIHRvb2xzIGFyZSBoaWRkZW4uIFRoZVxuICAgKiAgY29tcG9uZW50IGRyaXZlcyB0aGlzIG9mZiB0aGUgcGxvdCB0eXBlLCBub3QgdGhlIGFjdGl2ZSBiYWNrZW5kLiAqL1xuICBwcm90ZWN0ZWQgZ2V0IGlzSW1hZ2VWaWV3KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmJhc2VQbG90VHlwZSA9PT0gUGxvdFR5cGUuSU1BR0U7XG4gIH1cblxuICAvKiogSXNvc3VyZmFjZSBiYW5kIGFzIGEgMOKAkzI1NSBzbGlkZXIgcG9zaXRpb24sIG1hcHBlZCBvbnRvIHRoZSB2b2x1bWUncyByZWFsXG4gICAqICBpbnRlbnNpdHkgcmFuZ2UgYnkgdGhlIHJlbmRlcmVyLiBEZWZhdWx0cyB0byB0aGUgZnVsbCByYW5nZS4gKi9cbiAgcHJvdGVjdGVkIGlzb1JhbmdlOiBudW1iZXJbXSA9IFswLCAyNTVdO1xuXG4gIC8qKiBUaGUgc3BhdGlhbC1vbWljcyBkYXRhc2V0IG9uIG9mZmVyIGFuZCB0aGUgZ2F0ZXMgaXQgcHV0cyBvbiB0aGUgc2VsZWN0b3IuICovXG4gIHByb3RlY3RlZCByZWFkb25seSBzcGF0aWFsOiBTcGF0aWFsRGF0YXNldEJpbmRlcjtcbiAgLyoqIFdoZXRoZXIgYSBzcGF0aWFsLW9taWNzIGRhdGFzZXQgaXMgcHVibGlzaGVkIG9uIGBTUEFUSUFMX0RBVEFfUE9SVGAg4oCUIGdhdGVzIHRoZVxuICAgKiAgc3BhdGlhbCBwbG90IHR5cGVzOyB0aGUgdG9vbGJhciBvZmZlcnMgdGhlIHBsb3QgbW9kZXMgZm9yIGFuIGltYWdlLWxlc3Mgb25lIHRvby4gKi9cbiAgcHJvdGVjdGVkIGdldCBoYXNTcGF0aWFsRGF0YXNldCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zcGF0aWFsLmhhc0RhdGFzZXQ7XG4gIH1cblxuICAvKiogS2V5Ym9hcmQsIHdoZWVsIGFuZCBjb250ZXh0LW1lbnUgaGFuZGxpbmc7IGNyZWF0ZWQgb25jZSB0aGUgdmlldyBleGlzdHMuICovXG4gIHByaXZhdGUgc2hvcnRjdXRzPzogVmlld2VyU2hvcnRjdXRzO1xuXG4gIC8qKiBEZWJvdW5jZWQgei1zbGljZSBzY3J1YmJpbmcgKHNlZSBTbGljZVNjcnViYmVyIOKAlCByZWZhY3RvcmluZyBwbGFuIFN0ZXAgNykuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgc2NydWJiZXIgPSBuZXcgU2xpY2VTY3J1YmJlcigoeikgPT4ge1xuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0WkluZGV4KHopO1xuICAgIC8vIEluIGEgcGVyLXNsaWNlIHotc3RhY2sgc2Vzc2lvbiB0aGUgc3RvcmUgc3dhcHMgdGhlIGxpdmUgcmVnaW9uIHNldCB0byB0aGlzXG4gICAgLy8gc2xpY2UgKHByZXNlcnZpbmcgZWRpdHMgbWFkZSBvbiB0aGUgcHJldmlvdXMgb25lKTsgb3V0c2lkZSBzdGFjayBtb2RlIHRoaXNcbiAgICAvLyBqdXN0IHJlY29yZHMgdGhlIHNsaWNlLCBsZWF2aW5nIHNpbmdsZS1wbGFuZSByZWdpb25zIHVudG91Y2hlZCAoaml0LXVpIzkzKS5cbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldERpc3BsYXlTbGljZSh6KTtcbiAgfSk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgLy8gVGhlIGNvbnN0cnVjdG9yIHBhcmFtZXRlcnMgYmVsb3cgYXJlIHRoZSBpbmplY3Rpb24gcG9pbnRzOyBub25lIGlzIHB1YmxpYyBBUEkuXG4gICAgQEluamVjdChJTUFHRV9TVEFURV9QT1JUKSBwcml2YXRlIHN0YXRlOiBJbWFnZVN0YXRlUG9ydCxcbiAgICBASW5qZWN0KFZJU1VBTElaRVIpIHB1YmxpYyBwbG90U2VydmljZTogSVZpc3VhbGl6ZXIsXG4gICAgcHVibGljIG1lc3NhZ2VTZXJ2aWNlOiBNZXNzYWdlU2VydmljZSxcbiAgICBwcml2YXRlIG5nWm9uZTogTmdab25lLFxuICAgIHByaXZhdGUgY2RyOiBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICBwcml2YXRlIHNlc3Npb246IFZpc3VhbGl6ZXJTdG9yZSxcbiAgICBwcml2YXRlIHNhbVRvb2w6IFNhbVRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgY2VsbFNlZ21lbnRUb29sOiBDZWxsU2VnbWVudFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgc2FtUG9pbnRUb29sOiBTYW1Qb2ludFRvb2xTZXJ2aWNlLFxuICAgIHByaXZhdGUgcmVnaW9uT3BzOiBSZWdpb25PcHNTZXJ2aWNlLFxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoVklaX0NPTkZJRykgcHJpdmF0ZSB2aXpDb25maWc/OiBWaXpDb25maWcsXG4gICAgLy8gQSBjb25zdHJ1Y3RvciBwYXJhbWV0ZXIgcmF0aGVyIHRoYW4gYSBmaWVsZCBgaW5qZWN0KClgOiB0aGlzIGNvbXBvbmVudCBpc1xuICAgIC8vIGFsc28gY29uc3RydWN0ZWQgZGlyZWN0bHkgd2l0aCBgbmV3YCBpbiBpdHMgb3duIHNwZWNzLCB3aGljaCBpcyBvdXRzaWRlXG4gICAgLy8gYW4gaW5qZWN0aW9uIGNvbnRleHQgYW5kIHdvdWxkIHRocm93IE5HMDIwMyBvbiBhIGZpZWxkIGluaXRpYWxpemVyLlxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoVE9PTEJBUl9UT09MUykgdG9vbENvbnRyaWJ1dGlvbnM/OiByZWFkb25seSBUb29sYmFyQ29udHJpYnV0aW9uW10sXG4gICAgLy8gT3B0aW9uYWwgbGlrZSB0aGUgcmVzdDogYSBob3N0IHRoYXQgc2hvd3Mgb25seSBpbWFnZXMgbmV2ZXIgcHJvdmlkZXMgaXQsXG4gICAgLy8gYW5kIHRoZSBzcGF0aWFsIHBsb3QgdHlwZXMgc2ltcGx5IHN0YXkgaGlkZGVuLlxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoU1BBVElBTF9EQVRBX1BPUlQpIHByaXZhdGUgc3BhdGlhbERhdGE/OiBTcGF0aWFsRGF0YVBvcnQsXG4gICAgLy8gQ29udHJpYnV0ZWQgcGxvdCBtb2Rlcy4gTGlrZSBUT09MQkFSX1RPT0xTIHRoZSB0b2tlbiBoYXMgbm8gZmFjdG9yeSwgc28gYVxuICAgIC8vIGhvc3QgdGhhdCBwcm92aWRlcyBub25lIGdldHMgbnVsbCBoZXJlIGFuZCB0aGUgc2VsZWN0b3IgaXMgdW5jaGFuZ2VkLlxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoUExPVF9UWVBFX0NPTlRSSUJVVElPTlMpIHBsb3RUeXBlQ29udHJpYnV0aW9ucz86IHJlYWRvbmx5IFBsb3RUeXBlQ29udHJpYnV0aW9uW10sXG4gICAgLy8gUGFyZW50IG9mIGEgY29udHJpYnV0ZWQgcGFuZWwgY29tcG9uZW50J3MgaW5qZWN0b3IuIE9wdGlvbmFsIG9ubHkgc28gdGhlXG4gICAgLy8gc3BlY3MgY2FuIGNvbnN0cnVjdCB0aGlzIGNvbXBvbmVudCB3aXRoIGBuZXdgLlxuICAgIEBPcHRpb25hbCgpIHByaXZhdGUgaW5qZWN0b3I/OiBJbmplY3RvcixcbiAgICAvLyBUaGUgYDx2aXN1YWxpemVyPmAgZWxlbWVudDoga2V5Ym9hcmQgc2hvcnRjdXRzIGFyZSBzY29wZWQgdG8gaXQgKENPUkUtMikuXG4gICAgQE9wdGlvbmFsKCkgcHJpdmF0ZSBob3N0UmVmPzogRWxlbWVudFJlZjxIVE1MRWxlbWVudD4sXG4gICAgLy8gRW5kcyB0aGUgc3Vic2NyaXB0aW9ucyAoYHRha2VVbnRpbERlc3Ryb3llZGApLiBBIHBhcmFtZXRlciwgbGlrZSB0aGUgcmVzdCwgc28gdGhlXG4gICAgLy8gc3BlY3MnIGBuZXdgIGNhbiBwYXNzIG9uZTsgQW5ndWxhciBhbHdheXMgcHJvdmlkZXMgaXQuXG4gICAgcHJpdmF0ZSByZWFkb25seSBkZXN0cm95UmVmOiBEZXN0cm95UmVmID0gdW5kZWZpbmVkIGFzIHVua25vd24gYXMgRGVzdHJveVJlZixcbiAgKSB7XG4gICAgdGhpcy5jb250cmlidXRlZFRvb2xzID0gdmlzaWJsZVRvb2xDb250cmlidXRpb25zKHRvb2xDb250cmlidXRpb25zKTtcbiAgICB0aGlzLnRvb2xQYXJhbXMgPSBuZXcgVG9vbFBhcmFtc01vZGVsKHRoaXMuY29udHJpYnV0ZWRUb29scyk7XG4gICAgdGhpcy5jb250cmlidXRpb25zID0gbmV3IENvbnRyaWJ1dGlvbkhvc3QocGxvdFR5cGVDb250cmlidXRpb25zLCB0b29sQ29udHJpYnV0aW9ucywge1xuICAgICAgdmlzdWFsaXplcjogdGhpcy5wbG90U2VydmljZSxcbiAgICAgIHpvbmU6IHRoaXMubmdab25lLFxuICAgICAgdG9vbHM6IHRoaXMudG9vbHMucGxvdE1vZGVUb29scyxcbiAgICAgIGluamVjdG9yOiB0aGlzLmluamVjdG9yLFxuICAgICAgaW1hZ2VJbmZvJDogKCkgPT4gdGhpcy5zdGF0ZS5nZXRJbWFnZUluZm8kKCksXG4gICAgICBzZWxlY3RlZElkOiAoKSA9PiB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCxcbiAgICAgIHJlbmRlcmVkVHlwZTogKCkgPT4gdGhpcy5wbG90VHlwZSxcbiAgICAgIHNlbGVjdEJhc2U6IChiYXNlKSA9PiB7XG4gICAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gYmFzZTtcbiAgICAgICAgdGhpcy5wbG90VHlwZSA9IGJhc2U7XG4gICAgICB9LFxuICAgICAgbm90aWZ5OiAobSkgPT4gdGhpcy5tZXNzYWdlU2VydmljZS5hZGQoeyBrZXk6IHRoaXMucmVzdWx0VG9hc3RLZXksIC4uLm0gfSksXG4gICAgICBkZXRlY3RDaGFuZ2VzOiAoKSA9PiB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCksXG4gICAgfSk7XG4gICAgdGhpcy5zcGF0aWFsID0gbmV3IFNwYXRpYWxEYXRhc2V0QmluZGVyKHRoaXMuc3BhdGlhbERhdGEsIHtcbiAgICAgIGdhdGVzQ2hhbmdlZDogKCkgPT4gdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCksXG4gICAgICBzZWxlY3RlZElkOiAoKSA9PiB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCxcbiAgICAgIGJhc2VQbG90VHlwZTogKCkgPT4gdGhpcy5iYXNlUGxvdFR5cGUsXG4gICAgICBoYXNJbWFnZTogKCkgPT4gISF0aGlzLmltYWdlSW5mbyxcbiAgICAgIHNlbGVjdFBsb3RUeXBlOiAodHlwZSkgPT4gdGhpcy5vblNlbGVjdFBsb3RUeXBlKHR5cGUpLFxuICAgICAgcmVwbG90OiAoKSA9PiB0aGlzLnJlbG9hZEFuZFBsb3QoKSxcbiAgICAgIHB1Ymxpc2hJbWFnZTogKGluZm8pID0+IHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKGluZm8pLFxuICAgICAgc2V0SW1hZ2VMb2FkaW5nOiAobG9hZGluZykgPT4gdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcobG9hZGluZyksXG4gICAgICBzZXR0bGVkOiAoKSA9PiB7XG4gICAgICAgIHRoaXMucmVjb25jaWxlU2VsZWN0ZWRQbG90VHlwZSgpO1xuICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgIH0sXG4gICAgICBkZXRlY3RDaGFuZ2VzOiAoKSA9PiB0aGlzLmNoYW5nZWQoKSxcbiAgICB9KTtcbiAgICB0aGlzLmNvbXB1dGVQbG90VHlwZU9wdGlvbnMoKTtcbiAgfVxuXG4gIC8qKiBGb2xsb3cgdGhlIHNwYXRpYWwtb21pY3MgZGF0YXNldDogYSBkYXRhc2V0IGFwcGVhcmluZywgc3dpdGNoaW5nIG9yIGJlaW5nIGNsZWFyZWRcbiAgICogIHJlLWdhdGVzIHRoZSBzZWxlY3RvciBleGFjdGx5IGFzIHRoZSBpbWFnZSBzdHJlYW0gYW5kIHRoZSB0ZXN0LW1vZGUgdG9nZ2xlIGRvLiAqL1xuICBwcml2YXRlIHdhdGNoU3BhdGlhbERhdGFzZXQoKTogdm9pZCB7XG4gICAgdGhpcy5zcGF0aWFsLmJpbmQodGhpcy5kZXN0cm95ZWQkKTtcbiAgfVxuXG4gIC8qKiBSZWNvbXB1dGUgdGhlIHNlbGVjdG9yJ3MgZW50cmllcyBmb3IgdGhlIGN1cnJlbnQgaW1hZ2UsIGRhdGFzZXQgYW5kIHRlc3QgbW9kZVxuICAgKiAgKHNlZSB7QGxpbmsgY29tcHV0ZVBsb3RUeXBlTWVudX0pLiAqL1xuICBwcml2YXRlIGNvbXB1dGVQbG90VHlwZU9wdGlvbnMoKSB7XG4gICAgY29uc3QgeyBidWlsdEluLCBtZW51IH0gPSBjb21wdXRlUGxvdFR5cGVNZW51KHtcbiAgICAgIGRlc2NyaXB0b3JzOiB0aGlzLnBsb3RTZXJ2aWNlLmdldFBsb3RUeXBlRGVzY3JpcHRvcnMoKSxcbiAgICAgIGNvbnRyaWJ1dGlvbnM6IHRoaXMucGxvdE1vZGVzLmRlc2NyaXB0b3JzKCksXG4gICAgICBjYXBzOiB0aGlzLnBsb3RTZXJ2aWNlLmNhcGFiaWxpdGllcyxcbiAgICAgIGltYWdlSW5mbzogdGhpcy5pbWFnZUluZm8sXG4gICAgICBzcGF0aWFsOiB0aGlzLnNwYXRpYWwsXG4gICAgICB0ZXN0TW9kZTogdGhpcy50ZXN0TW9kZSxcbiAgICB9KTtcbiAgICB0aGlzLnBsb3RUeXBlT3B0aW9ucyA9IGJ1aWx0SW47XG4gICAgdGhpcy5wbG90VHlwZU1lbnUgPSBtZW51O1xuICB9XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIC8vIFRoZSBjb25zdHJ1Y3RvciBjb21wdXRlcyB0aGUgb3B0aW9ucyB3aXRoIHRlc3RNb2RlIHN0aWxsIGF0IGl0cyBkZWZhdWx0O1xuICAgIC8vIHJlY29tcHV0ZSBvbmNlIHRoZSBob3N0IGJpbmRzIGl0IChhbmQgb24gYW55IGxhdGVyIGNoYW5nZSkgc28gdGhlIHNlbGVjdG9yXG4gICAgLy8gcmVmbGVjdHMgdGVzdCBtb2RlIGltbWVkaWF0ZWx5LCB3aXRob3V0IHdhaXRpbmcgZm9yIGFuIGltYWdlIHRvIChyZSlsb2FkLlxuICAgIGlmIChjaGFuZ2VzWyd0ZXN0TW9kZSddKSB7XG4gICAgICB0aGlzLmNvbXB1dGVQbG90VHlwZU9wdGlvbnMoKTtcbiAgICAgIC8vIElmIHRlc3QgbW9kZSBpcyB0dXJuZWQgb2ZmIHdoaWxlIGEgdGVzdC1vbmx5IHR5cGUgKGUuZy4gTkFQQVJJX0lNQUdFKSBpc1xuICAgICAgLy8gYWN0aXZlLCB0aGF0IHR5cGUgaXMgbm8gbG9uZ2VyIG9mZmVyZWQg4oCUIGZhbGwgYmFjayB0byBJbWFnZS5cbiAgICAgIHRoaXMucmVjb25jaWxlU2VsZWN0ZWRQbG90VHlwZSgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgaG9zdCBwdWJsaXNoZWQgTk8gaW1hZ2Ug4oCUIGEgc3BhdGlhbCBkYXRhc2V0IHRoYXQgYnJpbmdzIG5vbmUgZG9lcyB0aGlzLlxuICAgKlxuICAgKiBXaXRob3V0IGhhbmRsaW5nIHRoZSBlbXB0eSBlbWlzc2lvbiwgYGltYWdlSW5mb2Aga2VlcHMgdGhlIExBU1QgaW1hZ2UncyBtZXRhZGF0YTogdGhlXG4gICAqIHNlbGVjdG9yIGdvZXMgb24gb2ZmZXJpbmcgcGl4ZWwgbW9kZXMgZm9yIHBpeGVscyB0aGF0IGFyZSBnb25lLCBhbmQgZXZlcnlcbiAgICogYGlzU3RhY2tgL2Bpc0dyYXlzY2FsZWAgZGVjaXNpb24gaXMgbWFkZSBhZ2FpbnN0IGEgZmlsZSB0aGF0IGlzIG5vIGxvbmdlciBsb2FkZWQuXG4gICAqL1xuICBwcml2YXRlIG9uSW1hZ2VDbGVhcmVkKCk6IHZvaWQge1xuICAgIC8vIFRoZSBpbWFnZSBhIGNvbnRyaWJ1dGVkIG1vZGUgd2FzIGRyYXdpbmcgb3ZlciBpcyBnb25lLiBBbiBvcGVuIGRpYWxvZ1xuICAgIC8vIHRvb2wgc3RheXMgb3BlbiBhbmQgcmVzdGFydHMgb25jZSB0aGUgbmV4dCBpbWFnZSBoYXMgcGxvdHRlZC5cbiAgICB0aGlzLmNvbnRyaWJ1dGlvbnMuZW5kU2Vzc2lvbnMoKTtcbiAgICB0aGlzLmltYWdlSW5mbyA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLmxvYWRlZEZpbGVOYW1lID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuY29tcHV0ZVBsb3RUeXBlT3B0aW9ucygpO1xuICAgIC8vIERlbGliZXJhdGVseSBOT1QgcmVjb25jaWxpbmcgdGhlIHNlbGVjdGVkIHR5cGUgaGVyZS4gUmVjb25jaWxpbmcgY2FsbHNcbiAgICAvLyBgc2V0UGxvdFR5cGVgLCB3aGljaCBkcml2ZXMgYSByZS1wbG90IOKAlCBhbmQgdGhlcmUgaXMgbm90aGluZyB0byBwbG90LCBzbyB0aGVcbiAgICAvLyBQbG90bHkgYmFja2VuZCByZWFkIGl0cyBvd24gbm93LXVuc2V0IGBpbWFnZUluZm9gIGFuZCB0aHJld1xuICAgIC8vIChcImNhbid0IGFjY2VzcyBwcm9wZXJ0eSBpc0dyYXlzY2FsZVwiKSwgYWJvcnRpbmcgdGhlIGxvYWQgd2l0aCBubyBvYnNlcnZhdGlvbnNcbiAgICAvLyBkcmF3bi4gSWdub3JpbmcgdGhlIGVtcHR5IGVtaXNzaW9uIGVudGlyZWx5IGlzIHdoYXQgdXNlZCB0byBhdm9pZCB0aGF0LlxuICAgIC8vXG4gICAgLy8gTm90aGluZyBpcyBsZWZ0IHN0cmFuZGVkOiBhbiBpbWFnZSBpcyBjbGVhcmVkIGJlY2F1c2UgYSBzcGF0aWFsIGRhdGFzZXQgdGhhdFxuICAgIC8vIGJyaW5ncyBub25lIGlzIGJlaW5nIG9wZW5lZCwgYW5kIHRoYXQgZGF0YXNldCdzIG93biBzdWJzY3JpcHRpb24gc2VsZWN0cyB0aGVcbiAgICAvLyBtb2RlIGl0cyBjb29yZGluYXRlcyBzdXBwb3J0IGEgbW9tZW50IGxhdGVyLlxuICAgIHRoaXMuY2hhbmdlZCgpO1xuICB9XG5cbiAgLyoqIElmIHRoZSBhY3RpdmUgcGxvdCB0eXBlIGlzIG5vIGxvbmdlciBpbiB0aGUgb2ZmZXJlZCBvcHRpb25zIChlLmcuIHRlc3QgbW9kZVxuICAgKiAgdHVybmVkIG9mZiB3aGlsZSBhIHRlc3Qtb25seSB0eXBlIHdhcyBhY3RpdmUsIG9yIGEgc2NhbGFyIHR5cGUgY2FycmllZCBvbnRvXG4gICAqICBhbiBSR0IgaW1hZ2UpLCBmYWxsIGJhY2sgdG8gdGhlIGRlZmF1bHQgMkQgSW1hZ2Ugdmlldy4gKi9cbiAgcHJpdmF0ZSByZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlKCk6IHZvaWQge1xuICAgIGNvbnN0IGZhbGxiYWNrID0gcmVjb25jaWxlUGxvdFR5cGUodGhpcy5wbG90VHlwZU1lbnUsIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkKTtcbiAgICBpZiAoIWZhbGxiYWNrKSByZXR1cm47XG4gICAgLy8gTGVhdmluZyBhIGNvbnRyaWJ1dGVkIG1vZGUgKGl0cyBnYXRlcyBzdG9wcGVkIHBhc3NpbmcsIG9yIGl0cyBwcm92aWRlciBpc1xuICAgIC8vIGdvbmUpOiBpdHMgc2Vzc2lvbiBlbmRzIGJlZm9yZSBhbnl0aGluZyBlbHNlIGRyYXdzLlxuICAgIHRoaXMucGxvdE1vZGVzLmRlYWN0aXZhdGUoKTtcbiAgICBjb25zdCBiYXNlID0gdGhpcy5wbG90TW9kZXMuYmFzZVR5cGVPZihmYWxsYmFjayk7XG4gICAgaWYgKGJhc2UgIT09IFBsb3RUeXBlLklNQUdFKSB0aGlzLmNsb3NlRGlhbG9nVG9vbCgpO1xuICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gZmFsbGJhY2s7XG4gICAgdGhpcy5wbG90VHlwZSA9IGJhc2U7XG4gICAgdGhpcy5pc0hlYXRtYXAgPSBiYXNlID09PSBQbG90VHlwZS5JTUFHRTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFBsb3RUeXBlKGJhc2UpO1xuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgLy8gSm9pbiB0aGUgbGl2ZSBzZXQ7IHRoZSBvbGRlc3QgbWVtYmVyIHJlbmRlcnMgdGhlIHNoYXJlZCBub3RpY2Ugb3V0bGV0cy5cbiAgICBWaXN1YWxpemVyQ29tcG9uZW50LmxpdmVJbnN0YW5jZXMuYWRkKHRoaXMpO1xuICAgIHRoaXMuc3RhdGUuc2V0RGlhZ3JhbSh0aGlzLmhvc3RIYW5kbGUpO1xuICAgIHRoaXMud2F0Y2hTcGF0aWFsRGF0YXNldCgpO1xuICAgIC8vIE9TRCBhbmQgbmFwYXJpLWpzIGVtaXQgdGhpcyBmcm9tIHRoZWlyIG93biBcImZpdCB0byB2aWV3XCI6IGRpc2FybSB0aGUgdG9vbCBvblxuICAgIC8vIHRoZSBiYWNrZW5kIHRvbywgbm90IG9ubHkgdGhlIHRvb2xiYXIncyBoaWdobGlnaHQuXG4gICAgdGhpcy5wbG90U2VydmljZVxuICAgICAgLmdldEF1dG9zY2FsZUV2ZW50KClcbiAgICAgIC5waXBlKHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLmRlc3Ryb3lSZWYpKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLnRvb2xzLmFwcGx5KG51bGwpKTtcbiAgICAvLyBMb2FkaW5nL292ZXJsYXkgVUkgbWlycm9ycyAoc2lnbmFsczogdGhleSByZS1yZW5kZXIgdGhlIHZpZXcgdGhlbXNlbHZlcykuXG4gICAgdGhpcy5taXJyb3IodGhpcy5zdGF0ZS5pc0ltYWdlTG9hZGluZyQoKSwgKHYpID0+IHRoaXMuaW1nTG9hZGluZy5zZXQodikpO1xuICAgIHRoaXMubWlycm9yKHRoaXMucGxvdFNlcnZpY2UuZ2V0U3RhY2tMb2FkaW5nUHJvZ3Jlc3MoKSwgKHYpID0+IHRoaXMubG9hZGluZ1BlcmNlbnRhZ2Uuc2V0KHYpKTtcbiAgICB0aGlzLm1pcnJvcih0aGlzLnBsb3RTZXJ2aWNlLmlzU3RhY2tMb2FkaW5nKCksICh2KSA9PiB0aGlzLnN0YWNrTG9hZGluZy5zZXQodikpO1xuICAgIHRoaXMubWlycm9yKHRoaXMuc3RhdGUuZ2V0SW1hZ2VMb2FkaW5nTWVzc2FnZSQoKSwgKHYpID0+IHRoaXMubG9hZGluZ01lc3NhZ2Uuc2V0KHYpKTtcbiAgICB0aGlzLm1pcnJvcih0aGlzLnN0YXRlLmdldENhY2hlUHJvZ3Jlc3MkKCksICh2KSA9PiB0aGlzLmNhY2hlUHJvZ3Jlc3Muc2V0KHYpKTtcbiAgICB0aGlzLm1pcnJvcih0aGlzLnN0YXRlLmlzWm9vbSQoKSwgKHYpID0+IHRoaXMuem9vbS5zZXQodikpO1xuICAgIHRoaXMubWlycm9yKHRoaXMuc3RhdGUuZ2V0RmlsZW5hbWUkKCksICh2KSA9PiB7XG4gICAgICBpZiAodikgdGhpcy5maWxlTmFtZSA9IHY7XG4gICAgfSk7XG4gICAgdGhpcy5yZWdpb25BY3Rpb25zLmJpbmQodGhpcy5kZXN0cm95ZWQkLCAoKSA9PiB0aGlzLmNoYW5nZWQoKSk7XG4gICAgdGhpcy5zZWdtZW50YXRpb24uYmluZFBvaW50VG9vbCh0aGlzLnNhbVBvaW50VG9vbCwgdGhpcy5kZXN0cm95ZWQkKTtcbiAgICB0aGlzLm1pcnJvcih0aGlzLnN0YXRlLmdldFBhbmVsV2lkdGgkKCksICgpID0+IHtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2UucmVsYXlvdXQoKTtcbiAgICAgIC8vIFRoZSBpbnRlbnNpdHkgaW5zZXQgaXMgYSBzZXBhcmF0ZSBjaGFydCBpbiBhIGZsb2F0aW5nIHBhbmVsOyByZWZsb3cgaXQgdG9vLlxuICAgICAgdGhpcy5pbnNldD8ucmVmbG93KCk7XG4gICAgfSk7XG4gICAgdGhpcy5zdGF0ZVxuICAgICAgLmdldEltYWdlSW5mbyQoKVxuICAgICAgLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuZGVzdHJveVJlZikpXG4gICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgbmV4dDogKGltZ0luZm8pID0+IHtcbiAgICAgICAgICBpZiAoIWltZ0luZm8pIHtcbiAgICAgICAgICAgIHRoaXMub25JbWFnZUNsZWFyZWQoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gVGhlIHNlbGVjdG9yLCB0aGUgc3RhY2sgY29udHJvbHMgYW5kIHRoZSBpbnNldCBhbGwgZm9sbG93IHRoZSBpbWFnZS5cbiAgICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgICAgICBpZiAoaW1nSW5mbykge1xuICAgICAgICAgICAgdGhpcy5pbWFnZUluZm8gPSBpbWdJbmZvO1xuICAgICAgICAgICAgLy8gU3RhY2stb25seSBwbG90IHR5cGVzIChpc29zdXJmYWNlLCBzY2F0dGVyM2QpIGRlcGVuZCBvbiB3aGV0aGVyIHRoaXNcbiAgICAgICAgICAgIC8vIGZpbGUgaXMgYSBzdGFjayDigJQgcmVjb21wdXRlIHRoZSBzZWxlY3RvciBvcHRpb25zLlxuICAgICAgICAgICAgdGhpcy5jb21wdXRlUGxvdFR5cGVPcHRpb25zKCk7XG4gICAgICAgICAgICAvLyBJZiB0aGUgYWN0aXZlIHBsb3QgdHlwZSBpc24ndCB2YWxpZCBmb3IgdGhpcyBpbWFnZSAoZS5nLiBhIHNjYWxhclxuICAgICAgICAgICAgLy8gdHlwZSBsaWtlIENvbnRvdXIgY2FycmllZCBvdmVyIHRvIGFuIFJHQiBpbWFnZSksIGZhbGwgYmFjayB0byBJbWFnZS5cbiAgICAgICAgICAgIHRoaXMucmVjb25jaWxlU2VsZWN0ZWRQbG90VHlwZSgpO1xuICAgICAgICAgICAgLy8gUmVhZCBmcm9tIGBpbWdJbmZvYCwgdGhlIHZhbHVlIHRoaXMgZW1pc3Npb24gY2FycmllZCwgcmF0aGVyIHRoYW4gZnJvbSB0aGVcbiAgICAgICAgICAgIC8vIGZpZWxkLiBIYW5kbGluZyB0aGUgZW1wdHkgZW1pc3Npb24gbWVhbnMgdGhlIGZpZWxkIENBTiBiZSBudWxsZWQgcGFydC13YXlcbiAgICAgICAgICAgIC8vIHRocm91Z2ggdGhpcyBicmFuY2g6IGByZWNvbmNpbGVTZWxlY3RlZFBsb3RUeXBlYCBtYXkgY2FsbCBgc2V0UGxvdFR5cGVgLFxuICAgICAgICAgICAgLy8gd2hpY2ggY2FuIG1ha2UgdGhlIGhvc3QgcHVibGlzaCBhIG5ldyBpbWFnZSBzdGF0ZSwgcmUtZW50ZXJpbmcgdGhpcyB2ZXJ5XG4gICAgICAgICAgICAvLyBzdWJzY3JpcHRpb24uIFJlYWRpbmcgdGhlIGZpZWxkIHRoZW4gdGhyZXcgb24gYGlzR3JheXNjYWxlYCBhbmQgYWJvcnRlZCB0aGVcbiAgICAgICAgICAgIC8vIGhhbmRsZXIsIHNvIHRoZSBvYnNlcnZhdGlvbnMgd2VyZSBuZXZlciBkcmF3bi5cbiAgICAgICAgICAgIHRoaXMuaXNHcmF5c2NhbGVFdmVudC5lbWl0KGltZ0luZm8uaXNHcmF5c2NhbGUpO1xuICAgICAgICAgICAgdGhpcy5pc1N0YWNrRXZlbnQuZW1pdChpbWdJbmZvLmlzU3RhY2spO1xuICAgICAgICAgICAgLy8gUmVzZXQgdG8gdGhlIGRlZmF1bHQgMkQgSW1hZ2UgdmlldyB3aGVuIGEgZGlmZmVyZW50IGltYWdlIGlzXG4gICAgICAgICAgICAvLyBzZWxlY3RlZCB3aGlsZSBhIDNEIHR5cGUgaXMgYWN0aXZlLlxuICAgICAgICAgICAgaWYgKCF0aGlzLmlzSGVhdG1hcCAmJiBpbWdJbmZvLmZpbGVOYW1lICE9PSB0aGlzLmxvYWRlZEZpbGVOYW1lKSB7XG4gICAgICAgICAgICAgIHRoaXMuaXNIZWF0bWFwID0gdHJ1ZTtcbiAgICAgICAgICAgICAgdGhpcy5wbG90VHlwZSA9IFBsb3RUeXBlLklNQUdFO1xuICAgICAgICAgICAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCA9IFBsb3RUeXBlLklNQUdFO1xuICAgICAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFBsb3RUeXBlKHRoaXMucGxvdFR5cGUpO1xuICAgICAgICAgICAgICB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUgPSAndHVybnRhYmxlJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMubG9hZGVkRmlsZU5hbWUgPSBpbWdJbmZvLmZpbGVOYW1lO1xuICAgICAgICAgICAgdGhpcy5wbG90U2VydmljZS5zZXRJbWFnZU1ldGEoaW1nSW5mby5pbWFnZU1ldGEsIGltZ0luZm8uZmlsZU5hbWUpO1xuICAgICAgICAgICAgLy8gQSBuZXdlciBpbWFnZSBhbHdheXMgcHJlZW1wdHMgYW4gaW4tZmxpZ2h0IHJlbmRlciAoc2VlIEltYWdlUmVuZGVyU2Vzc2lvbikuXG4gICAgICAgICAgICBpZiAoaW1nSW5mby51cmxzKSB0aGlzLnJlbmRlci5yZW5kZXIoaW1nSW5mbyk7XG4gICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBlcnJvcjogKGVycjogdW5rbm93bikgPT4ge1xuICAgICAgICAgIGNvbnN0IG1zZyA9IGVycm9yTWVzc2FnZShlcnIpO1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIG9jY3VyZWQgd2hlbiBnZXR0aW5nIGltYWdlIGluZm8nLCBlcnIpO1xuICAgICAgICAgIHRoaXMubWVzc2FnZVNlcnZpY2UuYWRkKHtcbiAgICAgICAgICAgIGtleTogdGhpcy52aXpBbGVydFRvYXN0S2V5LFxuICAgICAgICAgICAgc3RpY2t5OiB0cnVlLFxuICAgICAgICAgICAgc2V2ZXJpdHk6ICdlcnJvcicsXG4gICAgICAgICAgICBzdW1tYXJ5OiAnQW4gZXJyb3Igb2NjdXJlZCcsXG4gICAgICAgICAgICBkZXRhaWw6IGBUaGUgZm9sbG93aW5nIGVycm9yIG9jY3VyZWQgd2hpbGUgZ2V0dGluZyBpbWFnZSBpbmZvOiAke21zZ30uXG4gICAgICAgICAgICAgICAgICAgUGxlYXNlIHRyeSB0byBvcGVuIHRoZSBpbWFnZSBhZ2FpbiB0aHJvdWdoIHRoZSBmaWxlIG5hdmlnYXRvci5gLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHRoaXMuc3RhY2tMb2FkaW5nLnNldChmYWxzZSk7XG4gICAgICAgICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgICAgICAgIHRoaXMucmVuZGVyLnJ1bm5pbmcgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLnBsb3RTZXJ2aWNlLnJlc2V0KCk7XG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGb2xsb3cgYHNvdXJjZWAgdW50aWwgZGVzdHJveS4gSG9zdCBzdHJlYW1zIGNhbiBlbWl0IG91dHNpZGUgdGhlIEFuZ3VsYXIgem9uZSAoYVxuICAgKiBzZXJ2ZXJsZXNzIGhvc3QgY2xlYXJzIGl0cyBsb2FkaW5nIGZsYWcgZnJvbSBhbiBpbWFnZSBkZWNvZGUgb3IgYW4gT3BlblNlYWRyYWdvblxuICAgKiBjYWxsYmFjayk7IHNldHRpbmcgYSBzaWduYWwgdGhlcmUgbWFya3MgdGhpcyBPblB1c2ggdmlldyBidXQgc2NoZWR1bGVzIG5vIGNoYW5nZVxuICAgKiBkZXRlY3Rpb24sIHNvIHRoZSB1cGRhdGUgaXMgYXBwbGllZCBpbnNpZGUgdGhlIHpvbmUsIGFzIHtAbGluayBjaGFuZ2VkfSBkb2VzLlxuICAgKi9cbiAgcHJpdmF0ZSBtaXJyb3I8VD4oc291cmNlOiBPYnNlcnZhYmxlPFQ+LCBhcHBseTogKHZhbHVlOiBUKSA9PiB2b2lkKTogdm9pZCB7XG4gICAgc291cmNlLnBpcGUodGFrZVVudGlsRGVzdHJveWVkKHRoaXMuZGVzdHJveVJlZikpLnN1YnNjcmliZSgodmFsdWUpID0+IHtcbiAgICAgIGlmIChOZ1pvbmUuaXNJbkFuZ3VsYXJab25lKCkpIGFwcGx5KHZhbHVlKTtcbiAgICAgIGVsc2UgdGhpcy5uZ1pvbmUucnVuKCgpID0+IGFwcGx5KHZhbHVlKSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogU3RhdGUgdGhpcyB2aWV3IHNob3dzIGNoYW5nZWQgb3V0c2lkZSBvbmUgb2YgaXRzIHRlbXBsYXRlIGV2ZW50cyAoYSBzdHJlYW0sIGFcbiAgICogcmVzb2x2ZWQgcHJvbWlzZSwgYSBob3N0IGNhbGwpOiBtYXJrIHRoZSBPblB1c2ggdmlldyBmb3IgY2hlY2ssIHJlLWVudGVyaW5nIHRoZVxuICAgKiB6b25lIHdoZW4gaXQgY2hhbmdlZCBvdXRzaWRlIGl0IHNvIGEgY2hhbmdlLWRldGVjdGlvbiBwYXNzIGZvbGxvd3MuXG4gICAqL1xuICBwcml2YXRlIGNoYW5nZWQoKTogdm9pZCB7XG4gICAgaWYgKE5nWm9uZS5pc0luQW5ndWxhclpvbmUoKSkgdGhpcy5jZHIubWFya0ZvckNoZWNrKCk7XG4gICAgZWxzZSB0aGlzLm5nWm9uZS5ydW4oKCkgPT4gdGhpcy5jZHIubWFya0ZvckNoZWNrKCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIExheSBhIG5ldyBpbWFnZSBvdXQgb25jZSB0aGUgb2xkIHZpZXcgaXMgZ29uZTogdGhlIHN0YWNrIGZsYWcsIHRoZSBzbGljZSBib3VuZHMgKG9uZVxuICAgKiB1cmwgcGVyIHNsaWNlLCBzbyB0aGUgbGFzdCBpbmRleCBpcyBsZW5ndGgtMSksIGFuZCB0aGUgb25lLXNob3QgYGluaXRpYWxaSW5kZXhgIGhpbnRcbiAgICogKGUuZy4gdGhlIGZpbGUgdGhlIHVzZXIgY2xpY2tlZCB3aXRoaW4gYSBudW1iZXJlZCBzZXJpZXMpLiBUaGUgaGludCBpcyBjb25zdW1lZCBzb1xuICAgKiByZS1kZWxpdmVyaW5nIHRoZSBzYW1lIGluZm8gKGEgcGxvdC10eXBlIHN3aXRjaCwgcmVsb2FkQW5kUGxvdCkgZG9lcyBub3QgcmVzZXQgdGhlXG4gICAqIHVzZXIncyBzY3J1YjsgdGhlIGhvc3QncyBvYmplY3QgaXMgbmV2ZXIgd3JpdHRlbiB0byAoYSBob3N0IHN0b3JlIG1heSBmcmVlemUgaXQpIOKAlFxuICAgKiB0aGUgY29tcG9uZW50IGtlZXBzIGEgY29weSB3aXRob3V0IHRoZSBoaW50LCBhbmQgcmVtZW1iZXJzIHRoZSBob3N0IG9iamVjdC5cbiAgICovXG4gIHByaXZhdGUgbGF5T3V0U2xpY2VzKGluZm86IElJbWFnZUluZm8pOiB2b2lkIHtcbiAgICB0aGlzLnN0YWNrTG9hZGluZy5zZXQoaW5mby5pc1N0YWNrICYmIGluZm8uc2hvd1N0YWNrKTtcbiAgICB0aGlzLm1heEluZGV4ID0gaW5mby51cmxzLmxlbmd0aCA+IDEgPyBpbmZvLnVybHMubGVuZ3RoIC0gMSA6IDA7XG4gICAgaWYgKGluZm8uaW5pdGlhbFpJbmRleCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBpZiAoIXRoaXMuY29uc3VtZWRTbGljZUhpbnRzLmhhcyhpbmZvKSkge1xuICAgICAgICB0aGlzLmNvbnN1bWVkU2xpY2VIaW50cy5hZGQoaW5mbyk7XG4gICAgICAgIHRoaXMuekluZGV4ID0gaW5mby5pbml0aWFsWkluZGV4O1xuICAgICAgfVxuICAgICAgdGhpcy5pbWFnZUluZm8gPSB7IC4uLmluZm8sIGluaXRpYWxaSW5kZXg6IHVuZGVmaW5lZCB9O1xuICAgIH1cbiAgICB0aGlzLnVwZGF0ZVpJbmRleCgpO1xuICB9XG5cbiAgbmdBZnRlclZpZXdJbml0KCkge1xuICAgIC8vIFdpbmRvdyBsaXN0ZW5lcnMgYXJlIHJlZ2lzdGVyZWQgT1VUU0lERSB0aGUgQW5ndWxhciB6b25lOiB6b25lLXBhdGNoZWQsIGV2ZXJ5XG4gICAgLy8gbW91c2Vtb3ZlIGFueXdoZXJlIGluIHRoZSBob3N0IGFwcCByYW4gYXBwLXdpZGUgY2hhbmdlIGRldGVjdGlvbi4gRWFjaCBoYW5kbGVyXG4gICAgLy8gcmUtZW50ZXJzIHRoZSB6b25lIChuZ1pvbmUucnVuKSBvbmx5IHdoZW4gaXQgYWN0dWFsbHkgY2hhbmdlcyB2aWV3IHN0YXRlLlxuICAgIHRoaXMuc2hvcnRjdXRzID0gbmV3IFZpZXdlclNob3J0Y3V0cyh0aGlzLnNob3J0Y3V0SG9zdCgpLCB0aGlzLm5nWm9uZSk7XG4gICAgdGhpcy5zaG9ydGN1dHMuYXR0YWNoKCk7XG4gICAgdGhpcy5vblZpZXdSZWFkeSgpO1xuICB9XG5cbiAgLyoqIFRoZSBwbG90IGRpdiBleGlzdHMgbm93OiBydW4gYW4gaW1hZ2UtbGVzcyBkcmF3IHRoYXQgYXJyaXZlZCBiZWZvcmUgaXQgZGlkLiAqL1xuICBwcml2YXRlIG9uVmlld1JlYWR5KCk6IHZvaWQge1xuICAgIHRoaXMucmVuZGVyLnZpZXdJc1JlYWR5KHRoaXMuc3BhdGlhbC5kYXRhc2V0LCAhIXRoaXMuaW1hZ2VJbmZvKTtcbiAgfVxuXG4gIC8qKiBXaGF0IHRoZSBrZXlib2FyZCwgd2hlZWwgYW5kIGNvbnRleHQtbWVudSBzaG9ydGN1dHMgYWN0IG9uLiBUaGVpciBsaXN0ZW5lcnMgbGl2ZVxuICAgKiAgb3V0c2lkZSB0aGlzIHZpZXcsIHNvIGV2ZXJ5IGFjdGlvbiB0aGF0IGNoYW5nZXMgd2hhdCBpdCBzaG93cyBtYXJrcyBpdCBmb3IgY2hlY2suICovXG4gIHByaXZhdGUgc2hvcnRjdXRIb3N0KCk6IFNob3J0Y3V0SG9zdCB7XG4gICAgY29uc3QgbWFya2VkID1cbiAgICAgIDxBIGV4dGVuZHMgdW5rbm93bltdPihhY3Q6ICguLi5hcmdzOiBBKSA9PiB2b2lkKSA9PlxuICAgICAgKC4uLmFyZ3M6IEEpOiB2b2lkID0+IHtcbiAgICAgICAgYWN0KC4uLmFyZ3MpO1xuICAgICAgICB0aGlzLmNoYW5nZWQoKTtcbiAgICAgIH07XG4gICAgcmV0dXJuIHtcbiAgICAgIHBsb3REaXZOYW1lOiB0aGlzLnBsb3REaXZOYW1lLFxuICAgICAgaG9zdEVsZW1lbnQ6IHRoaXMuaG9zdFJlZj8ubmF0aXZlRWxlbWVudCA/PyBudWxsLFxuICAgICAgYWN0aXZlRHJhZ01vZGU6ICgpID0+IHRoaXMuYWN0aXZlRHJhZ01vZGUsXG4gICAgICBjYW5TdGVwU2xpY2U6ICgpID0+ICEhdGhpcy5pbWFnZUluZm8/LmlzU3RhY2sgJiYgdGhpcy5pc0ltYWdlVmlldyxcbiAgICAgIHdoZWVsWm9vbXM6ICgpID0+ICFyZW5kZXJlck93bnNXaGVlbCh0aGlzLmJhc2VQbG90VHlwZSkgJiYgdGhpcy5pc0hlYXRtYXAsXG4gICAgICBzdGVwU2xpY2U6IG1hcmtlZCgoZGVsdGE6IG51bWJlcikgPT4gdGhpcy5zdGVwU2xpY2UoZGVsdGEpKSxcbiAgICAgIHJlc29sdmVTYW1Qcm9tcHQ6IG1hcmtlZCgoY29tbWl0OiBib29sZWFuKSA9PiB7XG4gICAgICAgIGlmIChjb21taXQpIHRoaXMucGxvdFNlcnZpY2UuY29tbWl0U2FtUG9pbnRzKCk7XG4gICAgICAgIGVsc2UgdGhpcy5wbG90U2VydmljZS5jbGVhclNhbVBvaW50cygpO1xuICAgICAgICB0aGlzLnNlZ21lbnRhdGlvbi5oaWRlKCk7IC8vIHByb21wdCByZXNvbHZlZCDihpIgZGlzbWlzcyB0aGUgc3RhdHVzIHRvYXN0XG4gICAgICB9KSxcbiAgICAgIHVuZG86IG1hcmtlZCgoKSA9PiB0aGlzLnVuZG9SZWdpb24oKSksXG4gICAgICByZWRvOiBtYXJrZWQoKCkgPT4gdGhpcy5yZWRvUmVnaW9uKCkpLFxuICAgICAgZGVsZXRlUmVnaW9uOiBtYXJrZWQoKCkgPT4gdGhpcy5kZWxldGVSZWdpb24oKSksXG4gICAgICB6b29tSW46IG1hcmtlZCgoKSA9PiB0aGlzLnpvb21JbigpKSxcbiAgICAgIHpvb21PdXQ6IG1hcmtlZCgoKSA9PiB0aGlzLnpvb21PdXQoKSksXG4gICAgICB0b2dnbGVEcmFnTW9kZTogbWFya2VkKChtb2RlOiBzdHJpbmcpID0+IHRoaXMudG9nZ2xlRHJhZ01vZGUobW9kZSkpLFxuICAgICAgb3BlbkNvbnRleHRNZW51OiAoZXZlbnQpID0+IHtcbiAgICAgICAgdGhpcy5jb250ZXh0TWVudUl0ZW1zID0gdGhpcy5idWlsZENvbnRleHRNZW51SXRlbXMoKTtcbiAgICAgICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgICB0aGlzLmNvbnRleHRNZW51LnNob3coZXZlbnQpO1xuICAgICAgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFNuYXAgdGhlIGZsb2F0aW5nIHRvb2xiYXIgYmFjayB0byBpdHMgZG9ja2VkIHBvc2l0aW9uIGFjcm9zcyB0aGUgdG9wLiAqL1xuICBwcm90ZWN0ZWQgZG9ja1Rvb2xiYXIoKSB7XG4gICAgdGhpcy50b29sYmFyRmxvYXRpbmcgPSBmYWxzZTtcbiAgfVxuXG4gIC8qKiBUb29sYmFyIFwiSW50ZW5zaXR5XCIgZ3JvdXA6IGFkZCBhbm90aGVyIGxpbmUgUk9JIChuZXh0IGJyaWdodCBjb2xvdXIpLiAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgYWRkUHJvZmlsZUxpbmUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcmVnaW9uID0gYXdhaXQgdGhpcy5pbnNldD8uYWRkUHJvZmlsZUxpbmUoKTtcbiAgICAvLyBPU0Qgb25seSBkcmF3cyBhIHNlbGVjdGVkIHJlZ2lvbidzIGhhbmRsZXMgaW4gYW4gZWRpdCBtb2RlLCBzbyBhIGxpbmUgaW5cbiAgICAvLyAnbm9uZScgbW9kZSBsb29rcyB1bnNlbGVjdGVkLiBTd2l0Y2ggdG8gJ3NlbGVjdCcgc28gdGhlIG5ldyBsaW5lIHNob3dzIGl0c1xuICAgIC8vIGVuZHBvaW50IGhhbmRsZXMgYW5kIGNhbiBiZSBkcmFnZ2VkL2RlbGV0ZWQgcmlnaHQgYXdheS5cbiAgICBpZiAocmVnaW9uICYmIHRoaXMuaXNJbWFnZVZpZXcgJiYgdGhpcy5hY3RpdmVEcmFnTW9kZSAhPT0gJ3NlbGVjdCcpIHRoaXMudG9nZ2xlRHJhZ01vZGUoJ3NlbGVjdCcpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgLy8gQSBjb250cmlidXRlZCBtb2RlJ3Mgc2Vzc2lvbiBlbmRzIGZpcnN0LCB3aGlsZSB0aGUgdmlld2VyIGl0IGRyZXcgb3ZlclxuICAgIC8vIHN0aWxsIGV4aXN0cy4gVGhlIHBhbmVsIGdvZXMgd2l0aCB0aGlzIHZpZXcsIHNvIG5vIGNoYW5nZSBkZXRlY3Rpb24uXG4gICAgdGhpcy5jb250cmlidXRpb25zLmRlc3Ryb3koKTtcbiAgICAvLyBMZWF2ZSB0aGUgbGl2ZSBzZXQgc28gdGhlIG5leHQtb2xkZXN0IHZpc3VhbGl6ZXIgcGlja3MgdXAgdGhlIG91dGxldHMg4oCUIG9uIGl0c1xuICAgIC8vIG5leHQgY2hlY2ssIHdoaWNoIGFuIE9uUHVzaCBzaWJsaW5nIG5lZWRzIGFza2luZyBmb3IuXG4gICAgVmlzdWFsaXplckNvbXBvbmVudC5saXZlSW5zdGFuY2VzLmRlbGV0ZSh0aGlzKTtcbiAgICBmb3IgKGNvbnN0IG90aGVyIG9mIFZpc3VhbGl6ZXJDb21wb25lbnQubGl2ZUluc3RhbmNlcykgb3RoZXIuY2hhbmdlZCgpO1xuICAgIHRoaXMuc3RhdGUuc2V0RGlhZ3JhbShudWxsKTtcbiAgICB0aGlzLnJlbmRlci5jYW5jZWwoKTtcbiAgICB0aGlzLnNwYXRpYWwuZGlzcG9zZSgpO1xuICAgIHRoaXMuc2NydWJiZXIuY2FuY2VsKCk7XG4gICAgdGhpcy5zaG9ydGN1dHM/LmRldGFjaCgpO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZGV0YWNoKCk7XG4gIH1cblxuICAvKiogV2hhdCB0aGUgaG9zdCBnZXRzIHRocm91Z2ggYHNldERpYWdyYW1gIOKAlCBzZWUge0BsaW5rIFZpc3VhbGl6ZXJIYW5kbGV9LiAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGhvc3RIYW5kbGU6IFZpc3VhbGl6ZXJIYW5kbGUgPSB7XG4gICAgdmlzdWFsaXplcjogdGhpcy5wbG90U2VydmljZSxcbiAgICBwbG90U2VydmljZTogdGhpcy5wbG90U2VydmljZSxcbiAgICBoYXNSZWdpb25zOiAoKSA9PiB0aGlzLmhhc1JlZ2lvbnMoKSxcbiAgICBnZXRSZWdpb25Qb2x5Z29uczogKCkgPT4gdGhpcy5nZXRSZWdpb25Qb2x5Z29ucygpLFxuICB9O1xuXG4gIHB1YmxpYyBoYXNSZWdpb25zKCk6IGJvb2xlYW4ge1xuICAgIC8vIFRoZSBjb250cmFjdCdzIGZyYW1ld29yay1uZXV0cmFsIHJlZ2lvbiBhY2Nlc3NvcjogdGhlIGNvbXBvbmVudCBvbmx5IG5lZWRzIHRvXG4gICAgLy8ga25vdyB3aGV0aGVyIGFueSByZWdpb24gZXhpc3RzLCBuZXZlciBhIGJhY2tlbmQncyB3aXJlIGZvcm1hdC5cbiAgICByZXR1cm4gdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25zKCkubGVuZ3RoID4gMDtcbiAgfVxuXG4gIC8qKiBUaGUgY3VycmVudCByZWdpb25zIGFzIHBvbHlnb25zIChhbHNvIG9uIHRoZSBob3N0IGhhbmRsZSkuICovXG4gIHB1YmxpYyBnZXRSZWdpb25Qb2x5Z29ucygpOiBQb2x5Z29uW10ge1xuICAgIHJldHVybiB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvblBvbHlnb25zKCk7XG4gIH1cblxuICAvKiogT3BlbiB0aGUgQ2hhbm5lbHMgJiBIaXN0b2dyYW0gZGlhbG9nICh0b29sYmFyIGJ1dHRvbikuICovXG4gIHByb3RlY3RlZCBvcGVuQ2hhbm5lbEhpc3RvZ3JhbSgpIHtcbiAgICB0aGlzLnNob3dDaGFubmVsSGlzdG9ncmFtID0gdHJ1ZTtcbiAgfVxuXG4gIC8qKiBUb29sYmFyIOKGkiBvcGVuIHRoZSBzcGF0aWFsLW9taWNzIGNvbnRyb2xzLiAqL1xuICBwcm90ZWN0ZWQgb3BlblNwYXRpYWxDb250cm9scygpOiB2b2lkIHtcbiAgICB0aGlzLnNob3dTcGF0aWFsQ29udHJvbHMgPSB0cnVlO1xuICB9XG5cbiAgLyoqIE9wZW4gdGhlIFJlZ2lvbiBFZGl0b3IgZGlhbG9nICh0b29sYmFyIGJ1dHRvbikuIFRoZSBlZGl0b3Igc3RheXMgbGlua2VkIHRvXG4gICAqICB0aGlzIHBsb3R0aW5nIGluc3RhbmNlIHRocm91Z2ggdGhlIHJvb3QgUmVnaW9uU3RvcmUgc2luZ2xldG9uLCBzbyBpdCB3b3Jrc1xuICAgKiAgdGhlIHNhbWUgYXMgdGhlIGZvcm1lciByaWdodC1wYW5lbCBSZWdpb25zIHRhYi4gKi9cbiAgcHJvdGVjdGVkIG9wZW5SZWdpb25FZGl0b3IoKSB7XG4gICAgLy8gU2l6ZSB0aGUgZGlhbG9nIHRvIG1hdGNoIHRoZSBob3N0J3MgY29uZmlndXJlZCBlbGVtZW50IChlLmcuIHRoZSBhcHAnc1xuICAgIC8vIHJpZ2h0IHBhbmVsKSBhdCBvcGVuIHRpbWUuIE1lYXN1cmVkIG9uY2Ug4oCUIGl0IGludGVudGlvbmFsbHkgZG9lcyBub3RcbiAgICAvLyB0cmFjayBsYXRlciBicm93c2VyL3NwbGl0IHJlc2l6ZXMuIEZhbGxzIGJhY2sgdG8gYSBxdWFydGVyIG9mIHRoZSBwYWdlXG4gICAgLy8gd2hlbiBubyBzZWxlY3RvciBpcyBjb25maWd1cmVkIG9yIHRoZSBlbGVtZW50IGlzbid0IHByZXNlbnQuXG4gICAgY29uc3Qgc2VsZWN0b3IgPSB0aGlzLnZpekNvbmZpZz8ucmVnaW9uRWRpdG9yV2lkdGhTZWxlY3RvcjtcbiAgICBjb25zdCB3aWR0aCA9IHNlbGVjdG9yXG4gICAgICA/IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3I8SFRNTEVsZW1lbnQ+KHNlbGVjdG9yKT8uZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkud2lkdGhcbiAgICAgIDogdW5kZWZpbmVkO1xuICAgIHRoaXMucmVnaW9uRWRpdG9yV2lkdGggPSB3aWR0aCAmJiB3aWR0aCA+IDAgPyBgJHtNYXRoLnJvdW5kKHdpZHRoKX1weGAgOiAnMjV2dyc7XG4gICAgdGhpcy5zaG93UmVnaW9uRWRpdG9yID0gdHJ1ZTtcbiAgfVxuXG4gIC8qKiBUaGUgdG9vbGJhcidzIFNpbmdsZSBpbWFnZSAvIFN0YWNrIHRvZ2dsZS4gKi9cbiAgcHJvdGVjdGVkIHNlbGVjdFN0YWNrT3B0aW9uKHNlbGVjdGVkU3RhY2tPcHRpb246IHsgbmFtZTogc3RyaW5nOyB2YWw6IHN0cmluZyB9KSB7XG4gICAgY29uc3Qgc2hvd3N0YWNrID0gc2VsZWN0ZWRTdGFja09wdGlvbi52YWwgPT09ICd0cnVlJztcbiAgICB0aGlzLnN0YWNrTG9hZGluZy5zZXQoc2hvd3N0YWNrKTtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyghc2hvd3N0YWNrKTtcbiAgICAvLyBTdGFjayBtb2RlIGlzIGFsd2F5cyBhIDJEIGhlYXRtYXAg4oCUIHJlc2V0IHN1cmZhY2UgbW9kZSBpZiBhY3RpdmVcbiAgICBpZiAoIXRoaXMuaXNIZWF0bWFwKSB7XG4gICAgICB0aGlzLmlzSGVhdG1hcCA9IHRydWU7XG4gICAgICB0aGlzLnBsb3RUeXBlID0gUGxvdFR5cGUuSEVBVE1BUDtcbiAgICAgIHRoaXMuc2VsZWN0ZWRQbG90VHlwZUlkID0gUGxvdFR5cGUuSEVBVE1BUDtcbiAgICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0UGxvdFR5cGUodGhpcy5wbG90VHlwZSk7XG4gICAgfVxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0U2hvd1N0YWNrKHNob3dzdGFjayk7XG4gIH1cblxuICAvKiogQ2xhbXAgdGhlIHNsaWNlIGluZGV4IGludG8gWzAsIG1heEluZGV4XSBhbmQgcHVzaCBpdCB0byB0aGUgcmVuZGVyZXIuICovXG4gIHByb3RlY3RlZCB1cGRhdGVaSW5kZXgoKSB7XG4gICAgaWYgKHRoaXMuekluZGV4ID4gdGhpcy5tYXhJbmRleCkge1xuICAgICAgdGhpcy56SW5kZXggPSB0aGlzLm1heEluZGV4O1xuICAgIH1cbiAgICBpZiAodGhpcy56SW5kZXggPCAwKSB7XG4gICAgICB0aGlzLnpJbmRleCA9IDA7XG4gICAgfVxuICAgIHRoaXMucGxvdFNlcnZpY2Uuc2V0WkluZGV4KHRoaXMuekluZGV4KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMaXZlIHotc2xpY2Ugc2NydWIgZm9yIHRoZSBJbWFnZSB2aWV3OiB0aGUgcmVuZGVyZXIgc3dhcHMgdGhlIGRpc3BsYXllZFxuICAgKiBzbGljZSBpbiBwbGFjZSAobm8gcmUtbW91bnQpLCBzbyBkcmFnZ2luZyB0aGUgc2xpZGVyIHN0ZXBzIHRocm91Z2ggdGhlXG4gICAqIHN0YWNrIHRoZSBzYW1lIHdheSB0aGUgaGVhdG1hcCBmcmFtZSBzbGlkZXIgZG9lcy5cbiAgICovXG4gIHByb3RlY3RlZCBvblpTbGlkZSh6OiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoeiA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy56SW5kZXggPSB6O1xuICAgIHRoaXMuc2NydWJiZXIuY29tbWl0KHopO1xuICAgIC8vIEtlZXAgdGhlIGludGVuc2l0eSBpbnNldCBzYW1wbGluZyB0aGUgZGlzcGxheWVkIHNsaWNlIChJbWFnZSB2aWV3KS5cbiAgICB0aGlzLmluc2V0Py5zbGljZUNvbW1pdHRlZCh6KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMaXZlIChkZWJvdW5jZWQpIHNjcnViIHdoaWxlIGRyYWdnaW5nIHRoZSBJbWFnZS12aWV3IHotc2xpZGVyOiB1cGRhdGUgdGhlXG4gICAqIHNsaWNlIGFzIHRoZSB1c2VyIGRyYWdzLCBidXQgY29hbGVzY2UgcmFwaWQgY2hhbmdlcyBzbyB3ZSBkb24ndCBmaXJlIGFcbiAgICogc2xpY2Ugc3dhcCBvbiBldmVyeSBwaXhlbC4gVGhlIGZpbmFsIHZhbHVlIGFsc28gbGFuZHMgdmlhIG9uWlNsaWRlIChvblNsaWRlRW5kKS5cbiAgICovXG4gIHByb3RlY3RlZCBvblpTY3J1Yih6OiBudW1iZXIgfCB1bmRlZmluZWQpIHtcbiAgICBpZiAoeiA9PT0gdW5kZWZpbmVkKSByZXR1cm47XG4gICAgdGhpcy56SW5kZXggPSB6O1xuICAgIHRoaXMuc2NydWJiZXIuc2NydWIoeik7XG4gIH1cblxuICAvKiogTW92ZSB0aGUgZGlzcGxheWVkIHNsaWNlIGJ5IGBkZWx0YWAsIGNsYW1wZWQgdG8gdGhlIHN0YWNrIGJvdW5kcy4gKi9cbiAgc3RlcFNsaWNlKGRlbHRhOiBudW1iZXIpOiB2b2lkIHtcbiAgICBjb25zdCBuZXh0ID0gTWF0aC5taW4odGhpcy5tYXhJbmRleCwgTWF0aC5tYXgoMCwgdGhpcy56SW5kZXggKyBkZWx0YSkpO1xuICAgIGlmIChuZXh0ID09PSB0aGlzLnpJbmRleCkgcmV0dXJuO1xuICAgIHRoaXMub25aU2xpZGUobmV4dCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVsb2FkQW5kUGxvdCgpIHtcbiAgICB0aGlzLnN0YXRlLnNldEltYWdlTG9hZGluZyh0cnVlKTtcbiAgICAvLyBSZS1kcml2ZSB0aGUgcmVuZGVyIHBpcGVsaW5lIGZyb20gdGhlIGN1cnJlbnQgaW1hZ2UgaW5mbyAodGhlIHNvdXJjZSBvZlxuICAgIC8vIHRydXRoLCB3aXRoIHJlYWwgdXJscy9maWxlTmFtZSkuIERvbid0IHVzZSBwbG90U2VydmljZS5yZWxvYWRBbmRQbG90KCk6XG4gICAgLy8gdGhhdCByZWJ1aWxkcyB0aGUgaW5mbyBmcm9tIHRoZSByZW5kZXJlcidzIGludGVybmFsIHN0YXRlLCB3aGljaCBjYW4gYmVcbiAgICAvLyBzdGFsZSAoZS5nLiBhZnRlciBzd2l0Y2hpbmcgcGxvdCB0eXBlIEltYWdlIC0+IEhlYXRtYXApIGFuZCByZS1lbWl0XG4gICAgLy8gZW1wdHkvb2xkIHVybHMsIGZhaWxpbmcgdG8gaGFuZCB0aGUgZGl2IG92ZXIgdG8gdGhlIG5ldyByZW5kZXJlci5cbiAgICBpZiAodGhpcy5pbWFnZUluZm8pIHtcbiAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKHRoaXMuaW1hZ2VJbmZvKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuc3BhdGlhbC5kYXRhc2V0ICYmIChpc1NwYXRpYWxPbWljcyh0aGlzLnBsb3RUeXBlKSB8fCBpc1NwYXRpYWxPbWljczNkKHRoaXMucGxvdFR5cGUpKSkge1xuICAgICAgLy8gQSBzcGF0aWFsIGRhdGFzZXQgdGhhdCBicmluZ3Mgbm8gaW1hZ2UsIG9wZW5lZCB3aXRoIG5vIGltYWdlIGxvYWRlZCAoYSBob3N0J3NcbiAgICAgIC8vIGZpcnN0IHZpZXcpOiB0aGVyZSBpcyBubyBpbWFnZSBpbmZvIHRvIHJlLWRyaXZlLCBzbyBkcmF3IHRoZSBzcGF0aWFsIG1vZGUgaXRzZWxmLlxuICAgICAgdm9pZCB0aGlzLnJlbmRlci5wbG90V2l0aG91dEltYWdlKHRoaXMuc3BhdGlhbC5kYXRhc2V0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5wbG90U2VydmljZS5yZWxvYWRBbmRQbG90KCk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGNhbmNlbExvYWRpbmcoKSB7XG4gICAgLy8gU3RvcCBhbnkgaW4tZmxpZ2h0IGZyYW1lIHN0cmVhbWluZyAobmFwYXJpLWpzIHZvbHVtZS9zdXJmYWNlIHByZWxvYWQpIHNvIHRoZSBmZXRjaCBsb29wc1xuICAgIC8vIGFjdHVhbGx5IGFib3J0IOKAlCBjbGVhcmluZyB0aGUgZmxhZyBhbG9uZSBvbmx5IHJvdXRlZCB0byBQbG90bHkgYW5kIGxlZnQgbmFwYXJpIGZldGNoaW5nLlxuICAgIHRoaXMucGxvdFNlcnZpY2UuY2FuY2VsTG9hZGluZz8uKCk7XG4gICAgLy8gVGhlIGNhbmNlbGxlZCByZW5kZXIgaXMgc3VwZXJzZWRlZCwgbm90IGp1c3QgYWJvcnRlZDogaXRzIGFib3J0ZWQgbG9hZCBtdXN0IG5vdFxuICAgIC8vIGNvbWUgYmFjayBhcyBcIkNvdWxkIG5vdCBkcmF3IHRoZSBpbWFnZVwiLCBub3IgYXBwbHkgUk9JcyBmb3IgYW4gaW1hZ2UgbGVmdCB1bnNob3duLlxuICAgIHRoaXMucmVuZGVyLmNhbmNlbCgpO1xuICAgIHRoaXMuc3RhY2tMb2FkaW5nLnNldChmYWxzZSk7XG4gICAgdGhpcy5pbWdMb2FkaW5nLnNldChmYWxzZSk7XG4gICAgdGhpcy5zdGF0ZS5zZXRJbWFnZUxvYWRpbmcoZmFsc2UpO1xuICAgIHRoaXMuekluZGV4ID0gMDtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFpJbmRleCh0aGlzLnpJbmRleCk7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRTdGFja0xvYWRpbmcoZmFsc2UpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRvd25sb2FkSW1hZ2UoKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5kb3dubG9hZEltYWdlKCk7XG4gIH1cblxuICBhdXRvc2NhbGVJbWFnZSgpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmZpdFRvVmlldygpO1xuICB9XG5cbiAgLyoqIFRvb2xiYXIgcGl4ZWwvc21vb3RoIHRvZ2dsZTogZmxpcCBzbW9vdGhpbmcgYW5kIGFwcGx5IHRvIHRoZSBhY3RpdmUgYmFja2VuZC4gKi9cbiAgcHJvdGVjdGVkIG9uVG9nZ2xlSW1hZ2VTbW9vdGhpbmcoKTogdm9pZCB7XG4gICAgdGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQgPSAhdGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQ7XG4gICAgdGhpcy5wbG90U2VydmljZS5zZXRJbWFnZVNtb290aGluZ0VuYWJsZWQodGhpcy5pbWFnZVNtb290aGluZ0VuYWJsZWQpO1xuICB9XG5cbiAgcmVzZXRBeGVzKCkge1xuICAgIHRoaXMucGxvdFNlcnZpY2UucmVzZXRBeGVzKCk7XG4gIH1cblxuICB0b2dnbGVEcmFnTW9kZShtb2RlOiBzdHJpbmcpIHtcbiAgICB0aGlzLnRvb2xzLnRvZ2dsZShtb2RlKTtcbiAgfVxuXG4gIC8qKiBMaXZlLXVwZGF0ZSB0aGUgaXNvc3VyZmFjZSBhcyB0aGUgcmFuZ2Ugc2xpZGVyIG1vdmVzLiBUaGUgY29udHJvbCBpcyBvbmx5XG4gICAqICBhdmFpbGFibGUgd2hlbiB0aGUgYWN0aXZlIGJhY2tlbmQgcmVuZGVycyBpc29zdXJmYWNlcyAoSVNPU1VSRkFDRSBtb2RlKS4gKi9cbiAgcHJvdGVjdGVkIG9uSXNvUmFuZ2VDaGFuZ2UodmFsdWVzOiBudW1iZXJbXSB8IHVuZGVmaW5lZCkge1xuICAgIGlmICghdmFsdWVzIHx8IHZhbHVlcy5sZW5ndGggPCAyKSByZXR1cm47XG4gICAgdGhpcy5pc29SYW5nZSA9IHZhbHVlcztcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldElzb3N1cmZhY2VDb250cm9scygpPy5zZXRJc29SYW5nZSh2YWx1ZXNbMF0sIHZhbHVlc1sxXSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgb25XYW5kU2Vuc2l0aXZpdHlDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIHRoaXMudG9vbHMuc2V0V2FuZFNlbnNpdGl2aXR5KHZhbHVlKTtcbiAgfVxuICBwcm90ZWN0ZWQgb25CcnVzaFNpemVDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIHRoaXMudG9vbHMuc2V0QnJ1c2hTaXplKHZhbHVlKTtcbiAgfVxuICBwcm90ZWN0ZWQgb25WZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgIHRoaXMudG9vbHMuc2V0VmVydGV4RXJhc2VyUmFkaXVzKHZhbHVlKTtcbiAgfVxuXG4gIHpvb21JbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnpvb21JbigpO1xuICB9XG5cbiAgem9vbU91dCgpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnpvb21PdXQoKTtcbiAgfVxuXG4gIGRlbGV0ZVJlZ2lvbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmRlbGV0ZUFjdGl2ZVNoYXBlKCk7XG4gIH1cblxuICAvKiogVW5kbyB0aGUgbW9zdCByZWNlbnQgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gVXAgdG8gMTAgc3RlcHMgYmFjay4gKi9cbiAgcHJvdGVjdGVkIHVuZG9SZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS51bmRvKCk7XG4gIH1cblxuICAvKiogUmVkbyB0aGUgbW9zdCByZWNlbnRseSB1bmRvbmUgcmVnaW9uIGFjdGlvbiAoaml0LXVpIzg1KS4gKi9cbiAgcHJvdGVjdGVkIHJlZG9SZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5yZWRvKCk7XG4gIH1cblxuICAvLyDilIDilIAgUmVnaW9uIHNldC1vcGVyYXRpb25zIG9uIHRoZSBjdXJyZW50IHNlbGVjdGlvbiAoaml0LXVpIzg1KSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgc2VsZWN0QWxsUmVnaW9ucygpOiB2b2lkIHtcbiAgICB0aGlzLnJlZ2lvbkFjdGlvbnMuc2VsZWN0QWxsKCk7XG4gIH1cbiAgbWVyZ2VSZWdpb25zKCk6IHZvaWQge1xuICAgIHRoaXMucmVnaW9uQWN0aW9ucy5tZXJnZSgpO1xuICB9XG4gIHVuZ3JvdXBSZWdpb25zKCk6IHZvaWQge1xuICAgIHRoaXMucmVnaW9uQWN0aW9ucy51bmdyb3VwKCk7XG4gIH1cbiAgaW52ZXJzZVJlZ2lvbnMoKTogdm9pZCB7XG4gICAgdGhpcy5yZWdpb25BY3Rpb25zLmludmVyc2UoKTtcbiAgfVxuICBzaW1wbGlmeVJlZ2lvbnModGhyZXNob2xkUHg6IG51bWJlcik6IHZvaWQge1xuICAgIHRoaXMucmVnaW9uQWN0aW9ucy5zaW1wbGlmeSh0aHJlc2hvbGRQeCk7XG4gIH1cbiAgb3BlblNpbXBsaWZ5RGlhbG9nKCk6IHZvaWQge1xuICAgIHRoaXMucmVnaW9uQWN0aW9ucy5vcGVuU2ltcGxpZnlEaWFsb2coKTtcbiAgfVxuXG4gIC8qKiBCb3gtcHJvbXB0ZWQgU0FNIHNlZ21lbnRhdGlvbiBvZiB0aGUgZHJhd24gcmVjdGFuZ2xlcyAoaml0LXVpIzkwKS4gQSBzdGlja3lcbiAgICogIGBzYW1gIHRvYXN0IHNob3dzIGxpdmUgc3RhdHVzICsgYSBkb3dubG9hZCBwcm9ncmVzcyBiYXIgKGZpcnN0IHJ1biBwdWxscyB0aGVcbiAgICogIGVuY29kZXIsIH4xNzAgTUIpOyBpdCBzdGF5cyBvcGVuIHVudGlsIHRoZSBydW4gZmluaXNoZXMgKGJhciBoaXRzIDEwMCUpLiAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgc2VnbWVudFJlZ2lvbnMoKSB7XG4gICAgYXdhaXQgdGhpcy5zZWdtZW50YXRpb24ucnVuKCdTQU0nLCB0aGlzLnNhbVRvb2wsICgpID0+IHRoaXMucGxvdFNlcnZpY2Uuc2VnbWVudFJlY3RhbmdsZXMoKSk7XG4gIH1cblxuICAvKiogQXV0by1zZWdtZW50IGNlbGxzIGluc2lkZSBlYWNoIGRyYXduIHJlY3RhbmdsZSB3aXRoIGNlbGxwb3NlLVNBTSwgY2xpZW50LXNpZGVcbiAgICogIChqaXQtdWkjOTApLiBFYWNoIGJveCBpcyBjcm9wcGVkIChicm93c2VyIHNsaWRlLWNyb3ApIHRoZW4gcnVuIHRocm91Z2ggdGhlXG4gICAqICBjZWxscG9zZS1qcyBtb2RlbDsgdGhlIHNhbWUgc3RpY2t5IGBzYW1gIHRvYXN0ICsgcHJvZ3Jlc3MgYmFyIGlzIHJldXNlZC4gKi9cbiAgcHJvdGVjdGVkIGFzeW5jIHNlZ21lbnRDZWxscG9zZSgpIHtcbiAgICBhd2FpdCB0aGlzLnNlZ21lbnRhdGlvbi5ydW4oJ0NlbGxwb3NlJywgdGhpcy5jZWxsU2VnbWVudFRvb2wsICgpID0+XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNlZ21lbnRSZWN0YW5nbGVzQ2VsbHBvc2UoKSxcbiAgICApO1xuICB9XG5cbiAgLy8g4pSA4pSAIGNvbnRyaWJ1dGVkIHRvb2wgcGFyYW1ldGVycyAoc2VlIFRvb2xQYXJhbXNNb2RlbCkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIHBhcmFtc0Zvcih0b29sSWQ6IHN0cmluZyk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICByZXR1cm4gdGhpcy50b29sUGFyYW1zLnBhcmFtc0Zvcih0b29sSWQpO1xuICB9XG4gIHByb3RlY3RlZCBvblRvb2xNb2RlbENoYW5nZShlOiB7IHRvb2xJZDogc3RyaW5nOyBtb2RlbElkOiBzdHJpbmcgfSk6IHZvaWQge1xuICAgIHRoaXMudG9vbFBhcmFtcy5zZXRNb2RlbChlLnRvb2xJZCwgZS5tb2RlbElkKTtcbiAgfVxuICBwcm90ZWN0ZWQgb3BlblRvb2xQYXJhbXModG9vbElkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnRvb2xQYXJhbXMub3Blbih0b29sSWQpO1xuICB9XG4gIHByb3RlY3RlZCBjbG9zZVRvb2xQYXJhbXMoKTogdm9pZCB7XG4gICAgdGhpcy50b29sUGFyYW1zLmNsb3NlKCk7XG4gIH1cbiAgcHJvdGVjdGVkIHJlc2V0VG9vbFBhcmFtcyh0b29sSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMudG9vbFBhcmFtcy5yZXNldCh0b29sSWQpO1xuICB9XG5cbiAgLyoqIFJ1biBhIGNvbnRyaWJ1dGVkIHRvb2wgb3ZlciB0aGUgY3VycmVudCB2aWV3LiBObyBwcm9tcHQ6IHRoZXNlIHN3ZWVwIHRoZVxuICAgKiAgd2hvbGUgdmlldyByYXRoZXIgdGhhbiBiZWluZyBwb2ludGVkIGF0IHNvbWV0aGluZy4gUmV1c2VzIHRoZSBzaGFyZWRcbiAgICogIHNlZ21lbnRhdGlvbiB0b2FzdCBzbyBwcm9ncmVzcyByZWFkcyB0aGUgc2FtZSBhcyB0aGUgU0FNL2NlbGxwb3NlIHRvb2xzLiAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgcnVuVG9vbCh0b29sSWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHRvb2wgPSB0aGlzLnRvb2xQYXJhbXMuZmluZCh0b29sSWQpO1xuICAgIGlmICghdG9vbCkgcmV0dXJuO1xuICAgIGNvbnN0IHBhcmFtcyA9IHRoaXMudG9vbFBhcmFtcy5ydW5QYXJhbXModG9vbCk7XG4gICAgYXdhaXQgdGhpcy5zZWdtZW50YXRpb24ucnVuKHRvb2wubGFiZWwsIHRvb2wucHJvZ3Jlc3MsICgpID0+IHRvb2wucnVuKHRoaXMucGxvdFNlcnZpY2UsIHBhcmFtcykpO1xuICB9XG5cbiAgLyoqIFBpY2sgdGhlIFNBTSBtb2RlbCB0aGUgc2VnbWVudCB0b29scyB1c2UgKGppdC11aSM5MCBQMSkuICovXG4gIHByb3RlY3RlZCBvblNhbU1vZGVsQ2hhbmdlKGlkOiBzdHJpbmcpIHtcbiAgICB0aGlzLnNhbU1vZGVsSWQgPSBpZDtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFNhbU1vZGVsKGlkKTtcbiAgfVxuXG4gIC8qKiBDb252ZXJ0IHRoZSBzZWxlY3RlZCByZWdpb24gdG8gYSBzbW9vdGggYmV6aWVyIGN1cnZlLiAqL1xuICB0b0JlemllclJlZ2lvbigpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbk92ZXJsYXkoKT8uc2V0U2VsZWN0ZWRCZXppZXIodHJ1ZSk7XG4gIH1cblxuICAvKiogQ29udmVydCB0aGUgc2VsZWN0ZWQgcmVnaW9uIGJhY2sgdG8gYSBzdHJhaWdodC1lZGdlZCBwb2x5Z29uLiAqL1xuICB0b1BvbHlnb25SZWdpb24oKSB7XG4gICAgdGhpcy5wbG90U2VydmljZS5nZXRSZWdpb25PdmVybGF5KCk/LnNldFNlbGVjdGVkQmV6aWVyKGZhbHNlKTtcbiAgfVxuXG4gIC8qKiBUaGUgcmlnaHQtY2xpY2sgbWVudSBmb3IgdGhlIGN1cnJlbnQgdmlldywgYXJtZWQgbW9kZSBhbmQgc2VsZWN0aW9uLiAqL1xuICBwcml2YXRlIGJ1aWxkQ29udGV4dE1lbnVJdGVtcygpOiBNZW51SXRlbVtdIHtcbiAgICByZXR1cm4gYnVpbGRDb250ZXh0TWVudSh0aGlzLmNvbnRleHRNZW51U3RhdGUoKSwgdGhpcyk7XG4gIH1cblxuICBwcml2YXRlIGNvbnRleHRNZW51U3RhdGUoKTogQ29udGV4dE1lbnVTdGF0ZSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGlzSGVhdG1hcDogdGhpcy5pc0hlYXRtYXAsXG4gICAgICBiYXNlUGxvdFR5cGU6IHRoaXMuYmFzZVBsb3RUeXBlLFxuICAgICAgYWN0aXZlRHJhZ01vZGU6IHRoaXMuYWN0aXZlRHJhZ01vZGUsXG4gICAgICBhY3RpdmVTdXJmYWNlM2RNb2RlOiB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUsXG4gICAgICByZWdpb25zOiB0aGlzLnBsb3RTZXJ2aWNlLmdldFJlZ2lvbnMoKSxcbiAgICAgIHNlbGVjdGVkSW5kaWNlczogdGhpcy5yZWdpb25BY3Rpb25zLnNlbGVjdGVkSW5kaWNlcyxcbiAgICAgIGNhblVuZ3JvdXA6IChyKSA9PiB0aGlzLnJlZ2lvbk9wcy5jYW5Vbmdyb3VwKHIpLFxuICAgIH07XG4gIH1cblxuICAvKiogM0Qgc2NlbmUgaW50ZXJhY3Rpb24gbW9kZSAob3JiaXQgLyB0dXJudGFibGUgLyBwYW4gLyB6b29tKTsgbm8tb3Agb24gYSAyRC1vbmx5IGJhY2tlbmQuICovXG4gIHRvZ2dsZVN1cmZhY2UzZE1vZGUobW9kZTogc3RyaW5nKSB7XG4gICAgdGhpcy5hY3RpdmVTdXJmYWNlM2RNb2RlID0gbW9kZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFN1cmZhY2UzZENvbnRyb2xzKCk/LnNldFN1cmZhY2VEcmFnTW9kZShtb2RlKTtcbiAgfVxuXG4gIHJlc2V0U3VyZmFjZUNhbWVyYSgpIHtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFN1cmZhY2UzZENvbnRyb2xzKCk/LnJlc2V0U3VyZmFjZUNhbWVyYSgpO1xuICB9XG5cbiAgLyoqIFRvZ2dsZSB0aGUgbmFwYXJpIDNEIGNvb3JkaW5hdGUtYXhlcyAvIHNjYWxlIGdpem1vICh2b2x1bWUvaXNvc3VyZmFjZSkuIE5vLW9wIG9uIGJhY2tlbmRzXG4gICAqICB0aGF0IGRvbid0IHJlbmRlciBvbmUgKHRoZSBjb250cm9sIG1ldGhvZCBpcyBvcHRpb25hbCkuICovXG4gIHByb3RlY3RlZCB0b2dnbGVBeGVzKCkge1xuICAgIHRoaXMuYXhlc1Zpc2libGUgPSAhdGhpcy5heGVzVmlzaWJsZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLmdldFN1cmZhY2UzZENvbnRyb2xzKCk/LnNldEF4ZXNWaXNpYmxlPy4odGhpcy5heGVzVmlzaWJsZSk7XG4gIH1cblxuICAvKiogVG9nZ2xlIHRoZSBuYXBhcmkgc3VyZmFjZSB3aXJlZnJhbWUgKGVkZ2VzIHZzIGZpbGxlZCkuIE5vLW9wIG9uIGJhY2tlbmRzIHdpdGhvdXQgYSBzdXJmYWNlLiAqL1xuICBwcm90ZWN0ZWQgdG9nZ2xlV2lyZWZyYW1lKCkge1xuICAgIHRoaXMud2lyZWZyYW1lQWN0aXZlID0gIXRoaXMud2lyZWZyYW1lQWN0aXZlO1xuICAgIHRoaXMucGxvdFNlcnZpY2UuZ2V0U3VyZmFjZTNkQ29udHJvbHMoKT8uc2V0V2lyZWZyYW1lPy4odGhpcy53aXJlZnJhbWVBY3RpdmUpO1xuICB9XG5cbiAgLyoqIENoYW5nZSB0aGUgbmFwYXJpIDNEIGRlY2ltYXRlIGZhY3Rvci4gRGVjaW1hdGlvbiBjaGFuZ2VzIHRoZSBmZXRjaGVkL2Fzc2VtYmxlZCBkYXRhLCBzbyB0aGlzXG4gICAqICByZS1wbG90cyB0aGUgY3VycmVudCAzRCB0eXBlIGF0IHRoZSBuZXcgcmVzb2x1dGlvbiAodW5saWtlIGF4ZXMvd2lyZWZyYW1lLCB3aGljaCBhcmUgbGl2ZSkuICovXG4gIHByb3RlY3RlZCBzZWxlY3RSZXNvbHV0aW9uKHNjYWxlOiBudW1iZXIpIHtcbiAgICBpZiAoc2NhbGUgPT09IHRoaXMucmVzb2x1dGlvblNjYWxlKSByZXR1cm47XG4gICAgdGhpcy5yZXNvbHV0aW9uU2NhbGUgPSBzY2FsZTtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFJlc29sdXRpb25TY2FsZT8uKHNjYWxlKTtcbiAgICB0aGlzLnJlbG9hZEFuZFBsb3QoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTd2l0Y2ggdGhlIGFjdGl2ZSBwbG90IHR5cGUgKGhlYXRtYXAsIHN1cmZhY2UsIGNvbnRvdXIsIHNjYXR0ZXIsIGxpbmUsXG4gICAqIHNjYXR0ZXIzZCwgaXNvc3VyZmFjZSkuIEtlZXBzIGBpc0hlYXRtYXBgIGFzIHRoZSAyRC12cy0zRCBmbGFnIHRoZSByZXN0IG9mXG4gICAqIHRoZSB0b29sYmFyL2NvbnRleHQtbWVudSBsb2dpYyByZWxpZXMgb24sIGRlYWN0aXZhdGVzIDJELW9ubHkgdG9vbHMgd2hlblxuICAgKiBtb3ZpbmcgdG8gYSAzRCB0eXBlLCB0aGVuIHJlLXBsb3RzLlxuICAgKi9cbiAgb25TZWxlY3RQbG90VHlwZShzZWxlY3RlZDogUGxvdFR5cGVJZCkge1xuICAgIC8vIEFsc28gY2FsbGVkIGJ5IHRoZSBob3N0IGRpcmVjdGx5LCBvdXRzaWRlIHRoaXMgdmlldydzIGV2ZW50czogdGhlIHNlbGVjdG9yLCB0aGVcbiAgICAvLyB0b29sYmFyJ3MgdG9vbCBncm91cHMgYW5kIHRoZSBkaWFsb2dzIGFsbCBmb2xsb3cgdGhlIHR5cGUuXG4gICAgdGhpcy5jaGFuZ2VkKCk7XG4gICAgLy8gQSBjb250cmlidXRlZCBpZCB3aG9zZSBwcm92aWRlciBpcyBnb25lIChlLmcuIHJlc3RvcmVkIGZyb20gYSBwcmV2aW91c1xuICAgIC8vIHNlc3Npb24pIGhhcyBub3RoaW5nIHRvIGFjdGl2YXRlIOKAlCB0YWtlIHRoZSBkZWZhdWx0IEltYWdlIHZpZXcgaW5zdGVhZC5cbiAgICBpZiAoIWlzQnVpbHRpblBsb3RUeXBlKHNlbGVjdGVkKSAmJiAhdGhpcy5wbG90TW9kZXMuZmluZChzZWxlY3RlZCkpIHtcbiAgICAgIGNvbnNvbGUud2FybihgW3Zpc3VhbGl6ZXJdIHVua25vd24gcGxvdCB0eXBlICcke3NlbGVjdGVkfScg4oCUIHNob3dpbmcgJyR7UGxvdFR5cGUuSU1BR0V9JyBpbnN0ZWFkLmApO1xuICAgICAgc2VsZWN0ZWQgPSBQbG90VHlwZS5JTUFHRTtcbiAgICB9XG4gICAgLy8gQW4gZXhwbGljaXQgY2hvaWNlIHJldHJpZXMgYSBtb2RlIHdob3NlIGVhcmxpZXIgY2xlYW51cCBmYWlsZWQ7IG9ubHlcbiAgICAvLyBhdXRvbWF0aWMgcmUtYWN0aXZhdGlvbnMgKHJlLXJlbmRlciwgaW1hZ2Ugc3dpdGNoKSBmYWxsIGJhY2sgZm9yIHRoYXQuXG4gICAgdGhpcy5wbG90TW9kZXMuY2xlYXJDbGVhbnVwRmFpbHVyZXMoKTtcbiAgICAvLyBXaGF0ZXZlciB3YXMgc2VsZWN0ZWQgYmVmb3JlLCBhIGNvbnRyaWJ1dGVkIHNlc3Npb24gZW5kcyBiZWZvcmUgdGhlIG5leHRcbiAgICAvLyBtb2RlIGRyYXdzLiAoUmUtc2VsZWN0aW5nIHRoZSBzYW1lIGNvbnRyaWJ1dGVkIG1vZGUgcmUtcGxvdHMsIHNvIGl0IGdldHMgYVxuICAgIC8vIGZyZXNoIHNlc3Npb24gdG9vLilcbiAgICB0aGlzLnBsb3RNb2Rlcy5kZWFjdGl2YXRlKCk7XG4gICAgLy8gRXZlcnl0aGluZyBiZWxvdyBkZWNpZGVzIGhvdyB0byBSRU5ERVIsIHNvIGl0IHdvcmtzIG9uIHRoZSBidWlsdC1pbiB0eXBlXG4gICAgLy8gdGhhdCBkcmF3cyBgc2VsZWN0ZWRgOiBhIGNvbnRyaWJ1dGVkIG1vZGUgYmVoYXZlcyBleGFjdGx5IGxpa2UgaXRzIGJhc2UuXG4gICAgY29uc3QgdHlwZSA9IHRoaXMucGxvdE1vZGVzLmJhc2VUeXBlT2Yoc2VsZWN0ZWQpO1xuICAgIC8vIEEgZGlhbG9nIHRvb2wgZHJhd3Mgb3ZlciB0aGUgSW1hZ2UgdmlldzogbGVhdmluZyBpdCBjbG9zZXMgdGhlIGRpYWxvZy4gU3RheWluZ1xuICAgIC8vIG9uIGl0LCB0aGUgc2Vzc2lvbiBlbmRzIGhlcmUgYW5kIHJlc3RhcnRzIG9uY2UgdGhlIHJlLXBsb3QgbGFuZHMuXG4gICAgaWYgKHR5cGUgPT09IFBsb3RUeXBlLklNQUdFKSB0aGlzLmNvbnRyaWJ1dGlvbnMudG9vbERpYWxvZ3MuZGVhY3RpdmF0ZSgpO1xuICAgIGVsc2UgdGhpcy5jbG9zZURpYWxvZ1Rvb2woKTtcbiAgICB0aGlzLnBsb3RUeXBlID0gdHlwZTtcbiAgICB0aGlzLnNlbGVjdGVkUGxvdFR5cGVJZCA9IHNlbGVjdGVkO1xuICAgIGNvbnN0IGRlc2NyaXB0b3IgPSBpc0J1aWx0aW5QbG90VHlwZShzZWxlY3RlZClcbiAgICAgID8gdGhpcy5wbG90VHlwZU1lbnUuZmluZCgoZCkgPT4gZC50eXBlID09PSB0eXBlKVxuICAgICAgOiBnZXRQbG90VHlwZURlc2NyaXB0b3IodHlwZSk7XG4gICAgY29uc3QgaXMzZCA9IGRlc2NyaXB0b3I/LmRpbWVuc2lvbnMgPT09ICczZCc7XG4gICAgdGhpcy5pc0hlYXRtYXAgPSAhaXMzZDtcbiAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFBsb3RUeXBlKHR5cGUpO1xuICAgIC8vIFN3aXRjaGluZyBwbG90IHR5cGUgY2FuY2VscyB0aGUgYWN0aXZlIHRvb2wg4oCUIG5vdCBldmVyeSB0eXBlIHN1cHBvcnRzIHRoZVxuICAgIC8vIHNhbWUgdG9vbHMgKGxpbmUvc2NhdHRlciBjaGFydHMgYW5kIDNEIHNjZW5lcyBoYXZlIG5vIGJveCB6b29tIG9yIHJlZ2lvblxuICAgIC8vIGRyYXdpbmcpLCBzbyBsZWF2aW5nIGEgdG9vbCBhcm1lZCB3b3VsZCBtaXNiZWhhdmUgb24gdGhlIG5ldyBwbG90LlxuICAgIHRoaXMudG9vbHMuZGVhY3RpdmF0ZSgpO1xuICAgIGlmIChpczNkKSB7XG4gICAgICB0aGlzLmFjdGl2ZVN1cmZhY2UzZE1vZGUgPSAndHVybnRhYmxlJztcbiAgICB9XG4gICAgLy8gbmFwYXJpLWpzIHZvbHVtZS9pc29zdXJmYWNlIGFzc2VtYmxlIHRoZWlyIG93biAzRCB0ZXh0dXJlIGZyb20gdGhlIHNsaWNlIGVuZHBvaW50LCBzb1xuICAgIC8vIHRoZXkgdGFrZSB0aGUgbm9ybWFsIHJlLXBsb3QgcGF0aCDigJQgTk9UIFBsb3RseSdzIHN0YWNrLWZyYW1lIGxvYWRlciAod2hvc2UgXCJMb2FkaW5nXG4gICAgLy8gZnJhbWVzXCIgb3ZlcmxheSB3b3VsZCBuZXZlciBjbGVhciwgc2luY2UgUGxvdGx5IGlzbid0IHRoZSBhY3RpdmUgYmFja2VuZCkgKGppdC11aSMxMDIpLlxuICAgIGNvbnN0IGlzTmFwYXJpID0gdHlwZSA9PT0gUGxvdFR5cGUuTkFQQVJJX0lNQUdFIHx8IGlzTmFwYXJpM2QodHlwZSk7XG4gICAgaWYgKGRlc2NyaXB0b3I/LnJlcXVpcmVzU3RhY2sgJiYgIWlzTmFwYXJpKSB7XG4gICAgICAvLyBWb2x1bWUgdHlwZXMgKGlzb3N1cmZhY2UsIHNjYXR0ZXIzZCkgbmVlZCB0aGUgd2hvbGUgei1zdGFjayBsb2FkZWQgYXMgYVxuICAgICAgLy8gM0QgYXJyYXksIGFuZCB0aGV5IGFsd2F5cyByZW5kZXIgb24gUGxvdGx5LiBEcml2ZSB0aGUgc3RhY2sgcmVsb2FkXG4gICAgICAvLyBkaXJlY3RseSB0aHJvdWdoIHRoZSBpbWFnZS1pbmZvIHN0cmVhbSByYXRoZXIgdGhhbiB0aGUgYWN0aXZlIHJlbmRlcmVyJ3NcbiAgICAgIC8vIHNldFNob3dTdGFjazogd2hlbiBPcGVuU2VhZHJhZ29uIG93bnMgdGhlIEltYWdlIHZpZXcgaXRzIHNldFNob3dTdGFjayBpc1xuICAgICAgLy8gYSBuby1vcCwgc28gdGhlIHJlbG9hZCBuZXZlciBmaXJlZCBhbmQgdGhlIGxvYWRlciBzcHVuIGZvcmV2ZXIuIFdlIGFybVxuICAgICAgLy8gUGxvdGx5J3Mgc3RhY2stbG9hZGluZyBmbGFnIChrZWVwcyBpdHMgZnJhbWUtZmV0Y2ggbG9vcCBhbGl2ZSkgYW5kXG4gICAgICAvLyByZS1lbWl0IHRoZSBpbWFnZSBpbmZvIHdpdGggc2hvd1N0YWNrIG9uIHNvIHRoZSBwaXBlbGluZSByZWxvYWRzIG9uXG4gICAgICAvLyBQbG90bHkgcmVnYXJkbGVzcyBvZiB3aGljaCBiYWNrZW5kIHdhcyBvbiBzY3JlZW4uXG4gICAgICB0aGlzLnN0YWNrTG9hZGluZy5zZXQodHJ1ZSk7XG4gICAgICB0aGlzLnBsb3RTZXJ2aWNlLnNldFN0YWNrTG9hZGluZyh0cnVlKTtcbiAgICAgIGlmICh0aGlzLmltYWdlSW5mbykge1xuICAgICAgICAvLyBBIGNvcHk6IHRoZSBpbWFnZSBpbmZvIG1heSBiZSB0aGUgaG9zdCdzIG93biAocG9zc2libHkgZnJvemVuKSBvYmplY3QuXG4gICAgICAgIHRoaXMuc3RhdGUuc2V0SW1hZ2VJbmZvKHsgLi4udGhpcy5pbWFnZUluZm8sIHNob3dTdGFjazogdHJ1ZSB9KTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5yZWxvYWRBbmRQbG90KCk7XG4gIH1cblxuICAvLyDilIDilIAgZGlhbG9nIHRvb2xzIChUT09MQkFSX1RPT0xTLCBraW5kOiAnZGlhbG9nJykg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIFRoZSB0b29sYmFyIGJ1dHRvbjogb3BlbiB0aGUgdG9vbCdzIGRpYWxvZywgb3IgY2xvc2UgaXQgaWYgaXQgaXMgb3Blbi4gKi9cbiAgcHJvdGVjdGVkIHRvZ2dsZURpYWxvZ1Rvb2woaWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuY29udHJpYnV0aW9ucy50b2dnbGVEaWFsb2dUb29sKGlkKTtcbiAgfVxuXG4gIC8qKiBDbG9zZSB0aGUgb3BlbiBkaWFsb2cgdG9vbDogaXRzIGJvZHkgaXMgdG9ybiBkb3duLCB0aGVuIGl0cyBzZXNzaW9uIGVuZHMuICovXG4gIHByb3RlY3RlZCBjbG9zZURpYWxvZ1Rvb2woKTogdm9pZCB7XG4gICAgdGhpcy5jb250cmlidXRpb25zLmNsb3NlRGlhbG9nVG9vbCgpO1xuICB9XG5cbiAgLyoqIFdoZXJlIHRoZSBvcGVuIGRpYWxvZyB0b29sJ3MgaG9zdCBlbGVtZW50IGlzIGF0dGFjaGVkIG9uY2UgaXRzIGRpYWxvZyByZW5kZXJzLiAqL1xuICBAVmlld0NoaWxkKCd0b29sRGlhbG9nU2xvdCcpXG4gIHNldCB0b29sRGlhbG9nU2xvdChyZWY6IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+IHwgdW5kZWZpbmVkKSB7XG4gICAgdGhpcy5jb250cmlidXRpb25zLmF0dGFjaFRvb2xEaWFsb2dTbG90KHJlZj8ubmF0aXZlRWxlbWVudCk7XG4gIH1cbn1cbiIsIjwhLS0gU0FNIHRvYXN0OiBsaXZlIHN0YXR1cyArIGVuY29kZXItZG93bmxvYWQgcHJvZ3Jlc3MgYmFyIChqaXQtdWkjOTApLiBTdGlja3lcbiAgICAgd2hpbGUgYSBzZWdtZW50IHJ1bnM7IHN0YXlzIG9wZW4gdW50aWwgdGhlIGJhciByZWFjaGVzIDEwMCUgYW5kIHRoZSBydW4gZW5kcy4gLS0+XG48cC10b2FzdCBba2V5XT1cInNhbVRvYXN0S2V5XCIgcG9zaXRpb249XCJib3R0b20tcmlnaHRcIj5cbiAgPG5nLXRlbXBsYXRlIGxldC1tZXNzYWdlIHBUZW1wbGF0ZT1cIm1lc3NhZ2VcIj5cbiAgICA8ZGl2IHN0eWxlPVwiZmxleDogMTsgbWluLXdpZHRoOiAxNnJlbVwiPlxuICAgICAgPGRpdiBjbGFzcz1cImZvbnQtYm9sZCBtYi0xXCI+e3sgbWVzc2FnZS5zdW1tYXJ5IH19PC9kaXY+XG4gICAgICA8ZGl2Pnt7IHNlZ21lbnRhdGlvbi5zdGF0dXMgfHwgbWVzc2FnZS5kZXRhaWwgfX08L2Rpdj5cbiAgICAgIDxwLXByb2dyZXNzQmFyXG4gICAgICAgICpuZ0lmPVwic2VnbWVudGF0aW9uLmRvd25sb2FkaW5nXCJcbiAgICAgICAgW3ZhbHVlXT1cInNlZ21lbnRhdGlvbi5wcm9ncmVzc1wiXG4gICAgICAgIHN0eWxlQ2xhc3M9XCJtdC0yIHNhbS1wcm9ncmVzc1wiXG4gICAgICA+PC9wLXByb2dyZXNzQmFyPlxuICAgICAgPHAtcHJvZ3Jlc3NCYXJcbiAgICAgICAgKm5nSWY9XCJzZWdtZW50YXRpb24uYnVzeSAmJiAhc2VnbWVudGF0aW9uLmRvd25sb2FkaW5nXCJcbiAgICAgICAgbW9kZT1cImluZGV0ZXJtaW5hdGVcIlxuICAgICAgICBzdHlsZUNsYXNzPVwibXQtMlwiXG4gICAgICAgIFtzdHlsZV09XCJ7IGhlaWdodDogJzRweCcgfVwiXG4gICAgICA+PC9wLXByb2dyZXNzQmFyPlxuICAgIDwvZGl2PlxuICA8L25nLXRlbXBsYXRlPlxuPC9wLXRvYXN0PlxuXG48IS0tIFJlc3VsdC9lcnJvciBub3RpY2VzIGZyb20gdGhlIGxpYnJhcnkgaXRzZWxmIChzZWdtZW50YXRpb24gb3V0Y29tZSwgcmVnaW9uXG4gICAgIHZhbGlkYXRpb24pLiBSZW5kZXJlZCBoZXJlIHNvIHRoZXkgZG8gbm90IGRlcGVuZCBvbiB0aGUgaG9zdCBtb3VudGluZyBhXG4gICAgIGtleWxlc3MgPHAtdG9hc3Q+IOKAlCB3aXRob3V0IHRoaXMgdGhlIG1lc3NhZ2VzIHdlbnQgbm93aGVyZSBpbiBhbnkgaG9zdCBidXRcbiAgICAgaml0LXVpLCBhbmQgYSBmYWlsZWQgcnVuIGxvb2tlZCBsaWtlIGEgYnV0dG9uIHRoYXQgZGlkIG5vdGhpbmcuIC0tPlxuPHAtdG9hc3QgW2tleV09XCJyZXN1bHRUb2FzdEtleVwiIHBvc2l0aW9uPVwiYm90dG9tLXJpZ2h0XCI+PC9wLXRvYXN0PlxuXG48IS0tIFNoYXJlZCBvdXRsZXRzIGZvciBub3RpY2VzIHJhaXNlZCB3aGVyZSB0aGVyZSBpcyBubyBwZXItaW5zdGFuY2Uga2V5IHRvIHVzZTpcbiAgICAgdGhlIHJvb3QtcHJvdmlkZWQgUGxvdGx5U2VydmljZSBhbmQgdGhlIHJlZ2lvbi1lZGl0b3IgZGlhbG9nLiBSZW5kZXJlZCBieSBhXG4gICAgIHNpbmdsZSB2aXN1YWxpemVyIChvd25zU2hhcmVkVG9hc3RzKSBzbyBhIG1lc3NhZ2UgZnJvbSB0aGUgcm9vdCBzZXJ2aWNlIGlzXG4gICAgIG5vdCBzaG93biBvbmNlIHBlciBsaXZlIHZpZXdlci4gLS0+XG48cC10b2FzdCAqbmdJZj1cIm93bnNTaGFyZWRUb2FzdHNcIiBba2V5XT1cInZpelRvYXN0S2V5XCI+PC9wLXRvYXN0PlxuPHAtdG9hc3QgKm5nSWY9XCJvd25zU2hhcmVkVG9hc3RzXCIgW2tleV09XCJ2aXpBbGVydFRvYXN0S2V5XCIgcG9zaXRpb249XCJjZW50ZXJcIj48L3AtdG9hc3Q+XG5cbjwhLS0gVG9vbGJhciBkb2NrOiBkb2NrZWQgYWNyb3NzIHRoZSB0b3AgYnkgZGVmYXVsdDsgZHJhZyB0aGUgaGFuZGxlIHRvIGRldGFjaFxuICAgICBpdCBpbnRvIGEgZmxvYXRpbmcsIG1vdmFibGUgd2luZG93IChmcmVlcyB0aGUgdG9wIHJvdyBmb3IgdGhlIGltYWdlKS4gLS0+XG48ZGl2XG4gIGNsYXNzPVwidG9vbGJhci1kb2NrXCJcbiAgW2NsYXNzLmZsb2F0aW5nXT1cInRvb2xiYXJGbG9hdGluZ1wiXG4gIFtzdHlsZS5sZWZ0LnB4XT1cInRvb2xiYXJGbG9hdGluZyA/IHRvb2xiYXJQb3MueCA6IG51bGxcIlxuICBbc3R5bGUudG9wLnB4XT1cInRvb2xiYXJGbG9hdGluZyA/IHRvb2xiYXJQb3MueSA6IG51bGxcIlxuPlxuICA8ZGl2XG4gICAgY2xhc3M9XCJ0b29sYmFyLWRyYWctaGFuZGxlXCJcbiAgICBbdml6RmxvYXRpbmdEcmFnXT1cInRvb2xiYXJEcmFnT3JpZ2luXCJcbiAgICAodml6RmxvYXRpbmdEcmFnTW92ZSk9XCJ0b29sYmFyUG9zID0gJGV2ZW50XCJcbiAgICBwVG9vbHRpcD1cIkRyYWcgdG8gZmxvYXQgdGhlIHRvb2xiYXJcIlxuICAgIHRvb2x0aXBQb3NpdGlvbj1cImJvdHRvbVwiXG4gID5cbiAgICA8aSBjbGFzcz1cInBpIHBpLWJhcnNcIj48L2k+XG4gIDwvZGl2PlxuICA8cGxvdHRpbmctdG9vbGJhclxuICAgIFtpbWFnZUluZm9dPVwiaW1hZ2VJbmZvXCJcbiAgICBbaGFzU3BhdGlhbERhdGFzZXRdPVwiaGFzU3BhdGlhbERhdGFzZXRcIlxuICAgIFtwbG90VHlwZU9wdGlvbnNdPVwicGxvdFR5cGVNZW51XCJcbiAgICBbc2VsZWN0ZWRQbG90VHlwZV09XCJzZWxlY3RlZFBsb3RUeXBlSWRcIlxuICAgIFtiYXNlUGxvdFR5cGVdPVwiYmFzZVBsb3RUeXBlXCJcbiAgICBbaXNvUmFuZ2VdPVwiaXNvUmFuZ2VcIlxuICAgIFttYXhJbmRleF09XCJtYXhJbmRleFwiXG4gICAgW3pJbmRleF09XCJ6SW5kZXhcIlxuICAgIFtpc0hlYXRtYXBdPVwiaXNIZWF0bWFwXCJcbiAgICBbaXMzZFJlZ2lvbnNdPVwiaXNTcGF0aWFsM2RNb2RlXCJcbiAgICBbYWN0aXZlRHJhZ01vZGVdPVwiYWN0aXZlRHJhZ01vZGVcIlxuICAgIFthY3RpdmVTdXJmYWNlM2RNb2RlXT1cImFjdGl2ZVN1cmZhY2UzZE1vZGVcIlxuICAgIFtheGVzVmlzaWJsZV09XCJheGVzVmlzaWJsZVwiXG4gICAgW3dpcmVmcmFtZUFjdGl2ZV09XCJ3aXJlZnJhbWVBY3RpdmVcIlxuICAgIFtyZXNvbHV0aW9uU2NhbGVdPVwicmVzb2x1dGlvblNjYWxlXCJcbiAgICBbd2FuZFNlbnNpdGl2aXR5XT1cIndhbmRTZW5zaXRpdml0eVwiXG4gICAgW2JydXNoU2l6ZV09XCJicnVzaFNpemVcIlxuICAgIFtzYW1Nb2RlbHNdPVwic2FtTW9kZWxzXCJcbiAgICBbc2FtTW9kZWxJZF09XCJzYW1Nb2RlbElkXCJcbiAgICBbY29udHJpYnV0ZWRUb29sc109XCJjb250cmlidXRlZFRvb2xzXCJcbiAgICBbZGlhbG9nVG9vbHNdPVwiZGlhbG9nVG9vbHNcIlxuICAgIFtvcGVuRGlhbG9nVG9vbElkXT1cIm9wZW5EaWFsb2dUb29sSWRcIlxuICAgICh0b2dnbGVEaWFsb2dUb29sKT1cInRvZ2dsZURpYWxvZ1Rvb2woJGV2ZW50KVwiXG4gICAgW3Rvb2xNb2RlbElkc109XCJ0b29sUGFyYW1zLnRvb2xNb2RlbElkc1wiXG4gICAgW3ZlcnRleEVyYXNlclJhZGl1c109XCJ2ZXJ0ZXhFcmFzZXJSYWRpdXNcIlxuICAgIFtjYW5VbmRvXT1cInJlZ2lvbkFjdGlvbnMuY2FuVW5kb1wiXG4gICAgW2NhblJlZG9dPVwicmVnaW9uQWN0aW9ucy5jYW5SZWRvXCJcbiAgICBbdG9vbHNdPVwidG9vbGJhclRvb2xzXCJcbiAgICBbaW1hZ2VTbW9vdGhpbmdFbmFibGVkXT1cImltYWdlU21vb3RoaW5nRW5hYmxlZFwiXG4gICAgKHNlbGVjdFBsb3RUeXBlKT1cIm9uU2VsZWN0UGxvdFR5cGUoJGV2ZW50KVwiXG4gICAgKHRvZ2dsZUltYWdlU21vb3RoaW5nKT1cIm9uVG9nZ2xlSW1hZ2VTbW9vdGhpbmcoKVwiXG4gICAgKGFkZFByb2ZpbGVMaW5lKT1cImFkZFByb2ZpbGVMaW5lKClcIlxuICAgIChpc29SYW5nZUNoYW5nZSk9XCJvbklzb1JhbmdlQ2hhbmdlKCRldmVudClcIlxuICAgIChvcGVuQ2hhbm5lbEhpc3RvZ3JhbSk9XCJvcGVuQ2hhbm5lbEhpc3RvZ3JhbSgpXCJcbiAgICAob3BlblNwYXRpYWxDb250cm9scyk9XCJvcGVuU3BhdGlhbENvbnRyb2xzKClcIlxuICAgIChvcGVuUmVnaW9uRWRpdG9yKT1cIm9wZW5SZWdpb25FZGl0b3IoKVwiXG4gICAgKHNlbGVjdFN0YWNrT3B0aW9uKT1cInNlbGVjdFN0YWNrT3B0aW9uKCRldmVudClcIlxuICAgICh6U2NydWIpPVwib25aU2NydWIoJGV2ZW50KVwiXG4gICAgKHpTbGlkZSk9XCJvblpTbGlkZSgkZXZlbnQpXCJcbiAgICAoekluZGV4SW5wdXQpPVwiekluZGV4ID0gJGV2ZW50OyB1cGRhdGVaSW5kZXgoKVwiXG4gICAgKHJlbG9hZEFuZFBsb3QpPVwicmVsb2FkQW5kUGxvdCgpXCJcbiAgICAoZG93bmxvYWRJbWFnZSk9XCJkb3dubG9hZEltYWdlKClcIlxuICAgIChhdXRvc2NhbGVJbWFnZSk9XCJhdXRvc2NhbGVJbWFnZSgpXCJcbiAgICAodG9nZ2xlRHJhZ01vZGUpPVwidG9nZ2xlRHJhZ01vZGUoJGV2ZW50KVwiXG4gICAgKHpvb21Jbik9XCJ6b29tSW4oKVwiXG4gICAgKHpvb21PdXQpPVwiem9vbU91dCgpXCJcbiAgICAodG9nZ2xlU3VyZmFjZTNkTW9kZSk9XCJ0b2dnbGVTdXJmYWNlM2RNb2RlKCRldmVudClcIlxuICAgIChyZXNldFN1cmZhY2VDYW1lcmEpPVwicmVzZXRTdXJmYWNlQ2FtZXJhKClcIlxuICAgICh0b2dnbGVBeGVzKT1cInRvZ2dsZUF4ZXMoKVwiXG4gICAgKHRvZ2dsZVdpcmVmcmFtZSk9XCJ0b2dnbGVXaXJlZnJhbWUoKVwiXG4gICAgKHNlbGVjdFJlc29sdXRpb24pPVwic2VsZWN0UmVzb2x1dGlvbigkZXZlbnQpXCJcbiAgICAoZGVsZXRlUmVnaW9uKT1cImRlbGV0ZVJlZ2lvbigpXCJcbiAgICAodW5kb1JlZ2lvbik9XCJ1bmRvUmVnaW9uKClcIlxuICAgIChyZWRvUmVnaW9uKT1cInJlZG9SZWdpb24oKVwiXG4gICAgKHNlZ21lbnRSZWdpb25zKT1cInNlZ21lbnRSZWdpb25zKClcIlxuICAgIChzZWdtZW50Q2VsbHBvc2UpPVwic2VnbWVudENlbGxwb3NlKClcIlxuICAgIChzYW1Nb2RlbENoYW5nZSk9XCJvblNhbU1vZGVsQ2hhbmdlKCRldmVudClcIlxuICAgIChydW5Ub29sKT1cInJ1blRvb2woJGV2ZW50KVwiXG4gICAgKHRvb2xNb2RlbENoYW5nZSk9XCJvblRvb2xNb2RlbENoYW5nZSgkZXZlbnQpXCJcbiAgICAob3BlblRvb2xQYXJhbXMpPVwib3BlblRvb2xQYXJhbXMoJGV2ZW50KVwiXG4gICAgKHdhbmRTZW5zaXRpdml0eUNoYW5nZSk9XCJvbldhbmRTZW5zaXRpdml0eUNoYW5nZSgkZXZlbnQpXCJcbiAgICAoYnJ1c2hTaXplQ2hhbmdlKT1cIm9uQnJ1c2hTaXplQ2hhbmdlKCRldmVudClcIlxuICAgICh2ZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UpPVwib25WZXJ0ZXhFcmFzZXJSYWRpdXNDaGFuZ2UoJGV2ZW50KVwiXG4gID5cbiAgICA8IS0tIEhvc3QtcHJvamVjdGVkIGN1c3RvbSB0b29sYmFyIGJ1dHRvbnMsIGZvcndhcmRlZCBpbnRvIHRoZSB0b29sYmFyLiAtLT5cbiAgICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG4gIDwvcGxvdHRpbmctdG9vbGJhcj5cbiAgPHAtYnV0dG9uXG4gICAgKm5nSWY9XCJ0b29sYmFyRmxvYXRpbmdcIlxuICAgIGNsYXNzPVwidG9vbGJhci1kb2NrLWJ0blwiXG4gICAgaWNvbj1cInBpIHBpLXRodW1idGFja1wiXG4gICAgc2l6ZT1cInNtYWxsXCJcbiAgICBbdGV4dF09XCJ0cnVlXCJcbiAgICBwVG9vbHRpcD1cIkRvY2sgdG8gdG9wXCJcbiAgICB0b29sdGlwUG9zaXRpb249XCJib3R0b21cIlxuICAgIChvbkNsaWNrKT1cImRvY2tUb29sYmFyKClcIlxuICA+XG4gIDwvcC1idXR0b24+XG48L2Rpdj5cblxuPHAtY29udGV4dE1lbnUgI2NtIFttb2RlbF09XCJjb250ZXh0TWVudUl0ZW1zXCIgYXBwZW5kVG89XCJib2R5XCI+PC9wLWNvbnRleHRNZW51PlxuPCEtLSBObyBgaC1mdWxsYCBoZXJlOiBQcmltZUZsZXgncyBoZWlnaHQ6MTAwJSAhaW1wb3J0YW50IG1hZGUgI3Bsb3QgYXMgdGFsbCBhc1xuICAgICB0aGUgd2hvbGUgaG9zdCwgc28gdW5kZXIgdGhlIGRvY2tlZCB0b29sYmFyIGl0cyBib3R0b20gdG9vbGJhci1oZWlnaHQgc3RyaXBcbiAgICAgKGluY2wuIHRoZSBPU0Qgc2NhbGUgYmFyIGF0IGJvdHRvbS1sZWZ0KSB3YXMgY2xpcHBlZCBieSB0aGUgaG9zdCdzXG4gICAgIG92ZXJmbG93OmhpZGRlbi4gVGhlIGNvbXBvbmVudCBzdHlsZXNoZWV0IHNpemVzIGl0ICgudml6LXBsb3QgeyBmbGV4OiAxIH0pLiAtLT5cbjxkaXYgY2xhc3M9XCJ3LWZ1bGwgcmVsYXRpdmUgdml6LXBsb3RcIiBbaWRdPVwicGxvdERpdk5hbWVcIj5cbiAgPGRpdiAqbmdJZj1cImltZ0xvYWRpbmcoKSB8fCBzdGFja0xvYWRpbmcoKSB8fCBpc0NhY2hpbmcoKVwiIGNsYXNzPVwibG9hZGluZy1vdmVybGF5XCI+XG4gICAgPCEtLSBEZXRlcm1pbmF0ZSBzdGFjay92b2x1bWUgcHJvZ3Jlc3MgdGFrZXMgcHJlY2VkZW5jZTogd2hpbGUgaXQncyBhY3RpdmUgc2hvdyBPTkxZIHRoZVxuICAgICAgICAgcHJvZ3Jlc3MgYmFyICsgXCJMb2FkaW5nIGZyYW1lcy4uLlwiLCBub3QgdGhlIGdlbmVyaWMgc3Bpbm5lci9cIkxvYWRpbmcgaW1hZ2UuLi5cIiBvbiB0b3Agb2ZcbiAgICAgICAgIGl0IChib3RoIHdlcmUgcmVuZGVyaW5nIGF0IG9uY2UgZm9yIG5hcGFyaSB2b2x1bWUvaXNvc3VyZmFjZSBsb2FkcykuIC0tPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJzdGFja0xvYWRpbmcoKTsgZWxzZSBpbWdMb2FkaW5nVHBsXCI+XG4gICAgICA8cC1wcm9ncmVzc0JhciBjbGFzcz1cInByb2dyZXNzXCIgW3ZhbHVlXT1cImxvYWRpbmdQZXJjZW50YWdlKClcIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgICA8c3BhbiBjbGFzcz1cInRleHQtbG9hZGluZ1wiPkxvYWRpbmcgZnJhbWVzLi4uPC9zcGFuPlxuICAgIDwvbmctY29udGFpbmVyPlxuICAgIDxuZy10ZW1wbGF0ZSAjaW1nTG9hZGluZ1RwbD5cbiAgICAgIDwhLS0gQ2FjaGUtY29weSBwcm9ncmVzcyBpcyBzaG93biB3aGVuZXZlciBhIGNhY2hlIGRvd25sb2FkIGlzIGFjdGl2ZSAoaXNDYWNoaW5nKSwgaW5kZXBlbmRlbnRcbiAgICAgICAgICAgb2YgYGltZ0xvYWRpbmdgLCBzbyBpdCdzIG5ldmVyIGRyb3BwZWQvbWFza2VkIG1pZC1kb3dubG9hZC4gLS0+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaXNDYWNoaW5nKCk7IGVsc2Ugc3Bpbm5lckxvYWRpbmdcIj5cbiAgICAgICAgPCEtLSBTdGFnZS1hd2FyZSBtZXNzYWdlIGNvbWVzIGZyb20gQ2FjaGVQcm9ncmVzc1NlcnZpY2UgdXBkYXRpbmdcbiAgICAgICAgICAgICBpbWFnZUxvYWRpbmdNZXNzYWdlJCAoXCJDYWNoaW5nIGltYWdlLi4uXCIgZHVyaW5nIHRoZSBHQ1MgZG93bmxvYWQsXG4gICAgICAgICAgICAgdGhlbiBcIkdlbmVyYXRpbmcgcHJldmlldy4uLlwiIGR1cmluZyBwZXItc2xpY2UgdGh1bWJuYWlsIGdlbikuIE9uY2UgdGhlXG4gICAgICAgICAgICAgZG93bmxvYWQgaGl0cyAxMDAlIHRoZSByZW1haW5pbmcgd29yayAoc2VydmVyIHByZXZpZXcvdGlsZSBnZW5lcmF0aW9uKVxuICAgICAgICAgICAgIGhhcyBubyBkZXRlcm1pbmF0ZSBwcm9ncmVzcywgc28gZmFsbCB0aHJvdWdoIHRvIHRoZSBzcGlubmVyIHRlbXBsYXRlXG4gICAgICAgICAgICAgcmF0aGVyIHRoYW4gcGFya2luZyBhIGZ1bGwgYmFyIHVuZGVyIFwiTG9hZGluZyBpbWFnZS4uLlwiLiAtLT5cbiAgICAgICAgPHAtcHJvZ3Jlc3NCYXIgY2xhc3M9XCJwcm9ncmVzc1wiIFt2YWx1ZV09XCJjYWNoZVByb2dyZXNzKClcIj48L3AtcHJvZ3Jlc3NCYXI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dC1sb2FkaW5nXCI+e3sgbG9hZGluZ01lc3NhZ2UoKSB9fTwvc3Bhbj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPG5nLXRlbXBsYXRlICNzcGlubmVyTG9hZGluZz5cbiAgICAgICAgPGRpdiAqbmdJZj1cImltZ0xvYWRpbmcoKVwiIGNsYXNzPVwic3Bpbm5lclwiPjwvZGl2PlxuICAgICAgICA8c3BhbiAqbmdJZj1cImltZ0xvYWRpbmcoKSAmJiAhem9vbSgpXCIgY2xhc3M9XCJ0ZXh0LWxvYWRpbmdcIj57eyBsb2FkaW5nTWVzc2FnZSgpIH19PC9zcGFuPlxuICAgICAgICA8c3BhbiAqbmdJZj1cImltZ0xvYWRpbmcoKSAmJiB6b29tKClcIiBjbGFzcz1cInpvb20tdGV4dC1sb2FkaW5nXCI+e3sgbG9hZGluZ01lc3NhZ2UoKSB9fTwvc3Bhbj5cbiAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgPC9uZy10ZW1wbGF0ZT5cbiAgICA8cC1idXR0b25cbiAgICAgICpuZ0lmPVwic3RhY2tMb2FkaW5nKCkgfHwgaW1nTG9hZGluZygpXCJcbiAgICAgIGNsYXNzPVwiY2FuY2VsLWxvYWRpbmdcIlxuICAgICAgbGFiZWw9XCJDYW5jZWxcIlxuICAgICAgKG9uQ2xpY2spPVwiY2FuY2VsTG9hZGluZygpXCJcbiAgICA+XG4gICAgPC9wLWJ1dHRvbj5cbiAgPC9kaXY+XG4gIDwhLS0gVHJhbnNsdWNlbnQgXCJzaGFycGVuaW5nXCIgb3ZlcmxheSBzaG93biBhZnRlciB0aGUgc21hbGwtdGllciBwcmV2aWV3IGlzXG4gICAgICAgb24gc2NyZWVuIHdoaWxlIHRoZSBsYXJnZS10aWVyIHJlbmRlciBpcyBzdGlsbCBsb2FkaW5nIGluIHRoZVxuICAgICAgIGJhY2tncm91bmQuIExldHMgdGhlIHVzZXIgc2VlIHRoZSBibHVycnkgaW1hZ2UgdW5kZXJuZWF0aCBidXQgbWFrZXNcbiAgICAgICBpdCBjbGVhciBzb21ldGhpbmcgaXMgc3RpbGwgbG9hZGluZyBzbyB0aGV5IGRvbid0IG1pc3Rha2UgdGhlXG4gICAgICAgbG93LXJlcyByZW5kZXIgZm9yIHRoZSBmaW5hbCBpbWFnZS4gLS0+XG4gIDxkaXYgKm5nSWY9XCJyZW5kZXIuc2hhcnBlbmluZyAmJiAhaW1nTG9hZGluZygpICYmICFzdGFja0xvYWRpbmcoKSAmJiAhaXNDYWNoaW5nKClcIiBjbGFzcz1cInNoYXJwZW5pbmctb3ZlcmxheVwiPlxuICAgIDxkaXYgY2xhc3M9XCJzcGlubmVyXCI+PC9kaXY+XG4gICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LWxvYWRpbmdcIj5TaGFycGVuaW5nIHByZXZpZXcuLi48L3NwYW4+XG4gIDwvZGl2PlxuPC9kaXY+XG5cbjwhLS0gRmxvYXRpbmcsIGRyYWdnYWJsZSBpbnRlbnNpdHktcHJvZmlsZSBpbnNldCwgc2hvd24gd2hpbGUgYW55IGludGVuc2l0eSBsaW5lXG4gICAgIGV4aXN0cy4gS2VwdCBPVVRTSURFIHRoZSBwbG90IGRpdiBhbmQgZml4ZWQgdG8gdGhlIHZpZXdwb3J0LCBzbyBpdCBmbG9hdHMgb3ZlclxuICAgICB0aGUgd2hvbGUgcGFnZSByYXRoZXIgdGhhbiBiZWluZyBjbGlwcGVkIHRvIHRoZSBjYW52YXMuIC0tPlxuPHZpei1pbnRlbnNpdHktaW5zZXRcbiAgW2RpdklkXT1cImludGVuc2l0eUluc2V0RGl2XCJcbiAgW3Bsb3REaXZOYW1lXT1cInBsb3REaXZOYW1lXCJcbiAgW3pJbmRleF09XCJ6SW5kZXhcIlxuICBbaW1hZ2VWaWV3XT1cImlzSW1hZ2VWaWV3XCJcbiAgW2ltYWdlSW5mb109XCJpbWFnZUluZm9cIlxuPjwvdml6LWludGVuc2l0eS1pbnNldD5cblxuPCEtLSBDaGFubmVscyAmIEhpc3RvZ3JhbTogbm9uLW1vZGFsIGZsb2F0aW5nIGRpYWxvZyBvcGVuZWQgZnJvbSB0aGUgdG9vbGJhci4gLS0+XG48Y2hhbm5lbC1oaXN0b2dyYW0gWyh2aXNpYmxlKV09XCJzaG93Q2hhbm5lbEhpc3RvZ3JhbVwiPjwvY2hhbm5lbC1oaXN0b2dyYW0+XG5cbjwhLS0gU3BhdGlhbC1vbWljcyBjb250cm9scy4gUmVuZGVycyBpdHMgb3duIGVtcHR5IHN0YXRlIHdoZW4gbm8gZGF0YXNldCAob3Igbm9cbiAgICAgU1BBVElBTF9EQVRBX1BPUlQpIGlzIHByZXNlbnQsIHNvIGl0IGlzIGFsd2F5cyBzYWZlIHRvIGhvc3QuIC0tPlxuPHNwYXRpYWwtY29udHJvbHMgWyh2aXNpYmxlKV09XCJzaG93U3BhdGlhbENvbnRyb2xzXCIgW2lzM2RdPVwiaXNTcGF0aWFsM2RNb2RlXCI+PC9zcGF0aWFsLWNvbnRyb2xzPlxuXG48IS0tIFNpZGUgcGFuZWwgb2YgdGhlIGFjdGl2ZSBjb250cmlidXRlZCBwbG90IG1vZGUgKFBMT1RfVFlQRV9DT05UUklCVVRJT05TKSxcbiAgICAgaW4gdGhlIHNhbWUgcmlnaHQtaGFuZCBhcmVhIGFzIHRoZSBzcGF0aWFsIGNvbnRyb2xzIGFuZCB0aGUgUmVnaW9uIEVkaXRvci5cbiAgICAgRXhpc3RzIG9ubHkgd2hpbGUgdGhlIG1vZGUncyBzZXNzaW9uIGlzIGxpdmU7IHRoZXJlIGlzIG5vIGNsb3NlIGJ1dHRvbixcbiAgICAgYmVjYXVzZSBsZWF2aW5nIHRoZSBtb2RlIGlzIHdoYXQgZW5kcyBpdC4gQSBjb21wb25lbnQgcGFuZWwgZ2V0c1xuICAgICBQTE9UX01PREVfQ09OVEVYVCAvIFBMT1RfTU9ERV9TRVNTSU9OIGZyb20gaXRzIGluamVjdG9yOyBhIGBtb3VudGAgcGFuZWwnc1xuICAgICBob3N0IGVsZW1lbnQgaXMgYXR0YWNoZWQgdG8gdGhlIHNsb3QgYmVsb3cgKHNlZSBwbG90TW9kZVBhbmVsU2xvdCkuIC0tPlxuPHAtZGlhbG9nXG4gICpuZ0lmPVwicGxvdE1vZGVQYW5lbCBhcyBwYW5lbFwiXG4gIFtoZWFkZXJdPVwicGFuZWwudGl0bGVcIlxuICBbdmlzaWJsZV09XCJ0cnVlXCJcbiAgW21vZGFsXT1cImZhbHNlXCJcbiAgW2Nsb3NhYmxlXT1cImZhbHNlXCJcbiAgW3Jlc2l6YWJsZV09XCJ0cnVlXCJcbiAgW2RyYWdnYWJsZV09XCJ0cnVlXCJcbiAgYXBwZW5kVG89XCJib2R5XCJcbiAgcG9zaXRpb249XCJyaWdodFwiXG4gIHN0eWxlQ2xhc3M9XCJwbG90LW1vZGUtcGFuZWxcIlxuICBbc3R5bGVdPVwieyB3aWR0aDogJzIycmVtJywgbWF4SGVpZ2h0OiAnODV2aCcgfVwiXG4gIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIlxuPlxuICA8bmctY29udGFpbmVyICpuZ0lmPVwicGFuZWwuY29tcG9uZW50XCI+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdDb21wb25lbnRPdXRsZXQ9XCJwYW5lbC5jb21wb25lbnQ7IGluamVjdG9yOiBwYW5lbC5pbmplY3RvciA/PyB1bmRlZmluZWRcIj48L25nLWNvbnRhaW5lcj5cbiAgPC9uZy1jb250YWluZXI+XG4gIDxkaXYgKm5nSWY9XCJwYW5lbC5ob3N0XCIgI3Bsb3RNb2RlUGFuZWxTbG90IGNsYXNzPVwicGxvdC1tb2RlLXBhbmVsLXNsb3RcIj48L2Rpdj5cbjwvcC1kaWFsb2c+XG5cbjwhLS0gQW4gb3BlbiBkaWFsb2cgdG9vbCAoVE9PTEJBUl9UT09MUywga2luZDogJ2RpYWxvZycpLiBOb24tbW9kYWwgc28gdGhlIHVzZXIgY2FuXG4gICAgIGtlZXAgd29ya2luZyBvbiB0aGUgaW1hZ2UsIGUuZy4gcGFpbnRpbmcgd2l0aCB0aGUgYnJ1c2ggdGhlIHRvb2wgYXJtZWQuIFRoZVxuICAgICB0b29sIHJlbmRlcnMgaXRzIGJvZHkgaW50byBhIGhvc3QgZWxlbWVudCBhdHRhY2hlZCB0byB0aGUgc2xvdCBiZWxvdyAoc2VlXG4gICAgIHRvb2xEaWFsb2dTbG90KTsgY2xvc2luZyB0aGUgZGlhbG9nIGVuZHMgdGhlIHRvb2wncyBzZXNzaW9uLiAtLT5cbjxwLWRpYWxvZ1xuICAqbmdJZj1cInRvb2xEaWFsb2cgYXMgZGxnXCJcbiAgW2hlYWRlcl09XCJkbGcudGl0bGVcIlxuICBbdmlzaWJsZV09XCJ0cnVlXCJcbiAgKHZpc2libGVDaGFuZ2UpPVwiJGV2ZW50IHx8IGNsb3NlRGlhbG9nVG9vbCgpXCJcbiAgW21vZGFsXT1cImZhbHNlXCJcbiAgW2Nsb3NhYmxlXT1cInRydWVcIlxuICBbcmVzaXphYmxlXT1cInRydWVcIlxuICBbZHJhZ2dhYmxlXT1cInRydWVcIlxuICBhcHBlbmRUbz1cImJvZHlcIlxuICBwb3NpdGlvbj1cInJpZ2h0XCJcbiAgc3R5bGVDbGFzcz1cInRvb2wtZGlhbG9nXCJcbiAgW3N0eWxlXT1cInsgd2lkdGg6IGRsZy53aWR0aCwgbWF4SGVpZ2h0OiAnODV2aCcgfVwiXG4gIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIlxuPlxuICA8ZGl2ICN0b29sRGlhbG9nU2xvdCBjbGFzcz1cInRvb2wtZGlhbG9nLXNsb3RcIj48L2Rpdj5cbjwvcC1kaWFsb2c+XG5cbjwhLS0gUmVnaW9uIEVkaXRvcjogbm9uLW1vZGFsLCBkcmFnZ2FibGUsIHJlc2l6YWJsZSBmbG9hdGluZyBkaWFsb2cgb3BlbmVkIGZyb21cbiAgICAgdGhlIHRvb2xiYXIgKGZvcm1lcmx5IHRoZSByaWdodC1wYW5lbCBSZWdpb25zIHRhYikuIE1vZGVsZXNzIHNvIHJlZ2lvbnMgY2FuXG4gICAgIHN0aWxsIGJlIGRyYXduL3NlbGVjdGVkIG9uIHRoZSBwbG90IHdoaWxlIGl0J3Mgb3Blbjsgc3RheXMgbGlua2VkIHRvIHRoaXNcbiAgICAgcGxvdHRpbmcgaW5zdGFuY2UgdmlhIHRoZSByb290IFJlZ2lvblN0b3JlIHNpbmdsZXRvbi4gT3BlbnMgb3ZlciB0aGUgcmlnaHRcbiAgICAgc2lkZSBvZiB0aGUgdmlldy4gLS0+XG48cC1kaWFsb2dcbiAgaGVhZGVyPVwiUmVnaW9uIEVkaXRvclwiXG4gIFsodmlzaWJsZSldPVwic2hvd1JlZ2lvbkVkaXRvclwiXG4gIFttb2RhbF09XCJmYWxzZVwiXG4gIFtyZXNpemFibGVdPVwidHJ1ZVwiXG4gIFtkcmFnZ2FibGVdPVwidHJ1ZVwiXG4gIFttYXhpbWl6YWJsZV09XCJ0cnVlXCJcbiAgYXBwZW5kVG89XCJib2R5XCJcbiAgcG9zaXRpb249XCJyaWdodFwiXG4gIFtzdHlsZV09XCJ7IHdpZHRoOiByZWdpb25FZGl0b3JXaWR0aCwgaGVpZ2h0OiAnODV2aCcgfVwiXG4gIFtjb250ZW50U3R5bGVdPVwieyBvdmVyZmxvdzogJ2F1dG8nIH1cIlxuPlxuICA8cmVnaW9uLWVkaXRvcj48L3JlZ2lvbi1lZGl0b3I+XG48L3AtZGlhbG9nPlxuXG48IS0tIFNpbXBsaWZ5IChEb3VnbGFz4oCTUGV1Y2tlcik6IGN1c3RvbSBweC10aHJlc2hvbGQgcGlja2VyIGZvciB0aGUgc2VsZWN0ZWRcbiAgICAgcmVnaW9uKHMpLCBvcGVuZWQgZnJvbSB0aGUgcmlnaHQtY2xpY2sgXCJTaW1wbGlmeSDilrggQ3VzdG9t4oCmXCIgYWN0aW9uLiAtLT5cbjxwLWRpYWxvZ1xuICBoZWFkZXI9XCJTaW1wbGlmeSByZWdpb24ocylcIlxuICBbKHZpc2libGUpXT1cImRpc3BsYXlTaW1wbGlmeURpYWxvZ1wiXG4gIFttb2RhbF09XCJ0cnVlXCJcbiAgW3N0eWxlXT1cInsgd2lkdGg6ICcyMnJlbScgfVwiXG4gIFtkcmFnZ2FibGVdPVwiZmFsc2VcIlxuICBbcmVzaXphYmxlXT1cImZhbHNlXCJcbj5cbiAgPGRpdiBjbGFzcz1cImZsZXggZmxleC1jb2x1bW4gZ2FwLTJcIj5cbiAgICA8c3BhblxuICAgICAgPkFsdGl0dWRlIHRocmVzaG9sZDogPGI+e3sgcmVnaW9uQWN0aW9ucy5zaW1wbGlmeVRocmVzaG9sZCB9fSBweDwvYj48L3NwYW5cbiAgICA+XG4gICAgPHAtc2xpZGVyIFsobmdNb2RlbCldPVwicmVnaW9uQWN0aW9ucy5zaW1wbGlmeVRocmVzaG9sZFwiIFttaW5dPVwiMVwiIFttYXhdPVwiMjBcIiBbc3RlcF09XCIxXCI+PC9wLXNsaWRlcj5cbiAgICA8c21hbGw+VmVydGljZXMgd2l0aGluIHRoaXMgZGlzdGFuY2Ugb2YgdGhlIGxpbmUgYmV0d2VlbiB0aGVpciBuZWlnaGJvdXJzIGFyZSByZW1vdmVkLjwvc21hbGw+XG4gIDwvZGl2PlxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPHAtYnV0dG9uIGxhYmVsPVwiQ2FuY2VsXCIgW3RleHRdPVwidHJ1ZVwiIChvbkNsaWNrKT1cImRpc3BsYXlTaW1wbGlmeURpYWxvZyA9IGZhbHNlXCI+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b25cbiAgICAgIGxhYmVsPVwiQXBwbHlcIlxuICAgICAgaWNvbj1cInBpIHBpLWNoZWNrXCJcbiAgICAgIChvbkNsaWNrKT1cInNpbXBsaWZ5UmVnaW9ucyhyZWdpb25BY3Rpb25zLnNpbXBsaWZ5VGhyZXNob2xkKVwiXG4gICAgPjwvcC1idXR0b24+XG4gIDwvbmctdGVtcGxhdGU+XG48L3AtZGlhbG9nPlxuXG48IS0tIFBhcmFtZXRlcnMgZm9yIHdoaWNoZXZlciBjb250cmlidXRlZCB0b29sIGlzIG9wZW4uIE9uZSBkaWFsb2cgZm9yIGFsbCBvZlxuICAgICB0aGVtOiBhIHRvb2wgZGVjbGFyZXMgaXRzIGZpZWxkcyAoVG9vbFBhcmFtU3BlYykgYW5kIHRoaXMgcmVuZGVycyB0aGVtLCBzb1xuICAgICB0aGUgdG9vbGJhciBjYW4gbGF5IGV2ZXJ5IHRvb2wgb3V0IHRoZSBzYW1lIHdheSBhbmQgYSBwbHVnaW4gbmVlZHMgbm9cbiAgICAgUHJpbWVORyBvZiBpdHMgb3duLiBEZWZhdWx0cyBjb21lIGZyb20gdGhlIGFjdGl2ZSBjaGVja3BvaW50LCBzaW5jZSB0aGV5XG4gICAgIGVuY29kZSB3aGF0IGl0IHdhcyB0cmFpbmVkIGZvci4gLS0+XG48cC1kaWFsb2dcbiAgKm5nSWY9XCJ0b29sUGFyYW1zLm9wZW5QYXJhbXMgYXMgb3BlblwiXG4gIFtoZWFkZXJdPVwib3Blbi50b29sLmxhYmVsICsgJyBwYXJhbWV0ZXJzJ1wiXG4gIFt2aXNpYmxlXT1cInRydWVcIlxuICAodmlzaWJsZUNoYW5nZSk9XCJjbG9zZVRvb2xQYXJhbXMoKVwiXG4gIFttb2RhbF09XCJ0cnVlXCJcbiAgW3N0eWxlXT1cInsgd2lkdGg6ICczMHJlbScgfVwiXG4gIFtkcmFnZ2FibGVdPVwiZmFsc2VcIlxuICBbcmVzaXphYmxlXT1cImZhbHNlXCJcbiAgYXBwZW5kVG89XCJib2R5XCJcbj5cbiAgPGRpdiBjbGFzcz1cImdyaWQgZm9ybWdyaWQgcC1mbHVpZFwiPlxuICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IGYgb2Ygb3Blbi5maWVsZHNcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJmaWVsZFwiIFtuZ0NsYXNzXT1cImYuc3BlYy50eXBlID09PSAnYm9vbGVhbicgPyAnY29sLTEyJyA6ICdjb2wtNidcIj5cbiAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImYubnVtYmVyIGFzIG5cIj5cbiAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJ0ZXh0LXNtXCIgW3BUb29sdGlwXT1cIm4udG9vbHRpcCB8fCAnJ1wiPnt7IG4ubGFiZWwgfX08L2xhYmVsPlxuICAgICAgICAgIDxwLWlucHV0TnVtYmVyXG4gICAgICAgICAgICBbbmdNb2RlbF09XCJvcGVuLnZhbHVlc1tuLmlkXVwiXG4gICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJvcGVuLnZhbHVlc1tuLmlkXSA9ICRldmVudFwiXG4gICAgICAgICAgICBbbWluXT1cIm4ubWluXCJcbiAgICAgICAgICAgIFttYXhdPVwibi5tYXhcIlxuICAgICAgICAgICAgW3N0ZXBdPVwibi5zdGVwXCJcbiAgICAgICAgICAgIFttaW5GcmFjdGlvbkRpZ2l0c109XCJuLmZyYWN0aW9uRGlnaXRzXCJcbiAgICAgICAgICAgIFtzaG93QnV0dG9uc109XCJ0cnVlXCJcbiAgICAgICAgICA+PC9wLWlucHV0TnVtYmVyPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiZi5zZWxlY3QgYXMgc2VsXCI+XG4gICAgICAgICAgPGxhYmVsIGNsYXNzPVwidGV4dC1zbVwiIFtwVG9vbHRpcF09XCJzZWwudG9vbHRpcCB8fCAnJ1wiPnt7IHNlbC5sYWJlbCB9fTwvbGFiZWw+XG4gICAgICAgICAgPHAtZHJvcGRvd25cbiAgICAgICAgICAgIFtvcHRpb25zXT1cInNlbC5vcHRpb25zXCJcbiAgICAgICAgICAgIG9wdGlvbkxhYmVsPVwibGFiZWxcIlxuICAgICAgICAgICAgb3B0aW9uVmFsdWU9XCJ2YWx1ZVwiXG4gICAgICAgICAgICBbbmdNb2RlbF09XCJvcGVuLnZhbHVlc1tzZWwuaWRdXCJcbiAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cIm9wZW4udmFsdWVzW3NlbC5pZF0gPSAkZXZlbnRcIlxuICAgICAgICAgICAgYXBwZW5kVG89XCJib2R5XCJcbiAgICAgICAgICA+PC9wLWRyb3Bkb3duPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgICA8IS0tIENoZWNrYm94IGxhYmVsIHNpdHMgYmVzaWRlIHRoZSBib3ggYW5kIG1heSBydW4gdG8gYSBzZW50ZW5jZSwgc29cbiAgICAgICAgICAgICBpdCBkb2VzIG5vdCBnZXQgdGhlIHN0YWNrZWQgbGFiZWwgdGhlIG90aGVyIHR5cGVzIHVzZS4gLS0+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJmLnNwZWMudHlwZSA9PT0gJ2Jvb2xlYW4nXCIgY2xhc3M9XCJmbGV4IGFsaWduLWl0ZW1zLWNlbnRlciBnYXAtMlwiPlxuICAgICAgICAgIDxwLWNoZWNrYm94XG4gICAgICAgICAgICBbbmdNb2RlbF09XCJvcGVuLnZhbHVlc1tmLnNwZWMuaWRdXCJcbiAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cIm9wZW4udmFsdWVzW2Yuc3BlYy5pZF0gPSAkZXZlbnRcIlxuICAgICAgICAgICAgW2JpbmFyeV09XCJ0cnVlXCJcbiAgICAgICAgICAgIFtpbnB1dElkXT1cIm9wZW4udG9vbC5pZCArICctJyArIGYuc3BlYy5pZFwiXG4gICAgICAgICAgPjwvcC1jaGVja2JveD5cbiAgICAgICAgICA8bGFiZWwgW2Zvcl09XCJvcGVuLnRvb2wuaWQgKyAnLScgKyBmLnNwZWMuaWRcIiBjbGFzcz1cInRleHQtc20gbWItMFwiIFtwVG9vbHRpcF09XCJmLnNwZWMudG9vbHRpcCB8fCAnJ1wiPnt7XG4gICAgICAgICAgICBmLnNwZWMubGFiZWxcbiAgICAgICAgICB9fTwvbGFiZWw+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9uZy1jb250YWluZXI+XG4gIDwvZGl2PlxuICA8bmctdGVtcGxhdGUgcFRlbXBsYXRlPVwiZm9vdGVyXCI+XG4gICAgPHAtYnV0dG9uXG4gICAgICBsYWJlbD1cIlJlc2V0IHRvIG1vZGVsIGRlZmF1bHRzXCJcbiAgICAgIFt0ZXh0XT1cInRydWVcIlxuICAgICAgc2l6ZT1cInNtYWxsXCJcbiAgICAgIChvbkNsaWNrKT1cInJlc2V0VG9vbFBhcmFtcyhvcGVuLnRvb2wuaWQpXCJcbiAgICA+PC9wLWJ1dHRvbj5cbiAgICA8cC1idXR0b24gbGFiZWw9XCJDbG9zZVwiIHNpemU9XCJzbWFsbFwiIChvbkNsaWNrKT1cImNsb3NlVG9vbFBhcmFtcygpXCI+PC9wLWJ1dHRvbj5cbiAgPC9uZy10ZW1wbGF0ZT5cbjwvcC1kaWFsb2c+XG4iXX0=